/************************************************************************/
/*                                                                      */
/*    vspline - a set of generic tools for creation and evaluation      */
/*              of uniform b-splines                                    */
/*                                                                      */
/*            Copyright 2015 - 2023 by Kay F. Jahnke                    */
/*                                                                      */
/*    The git repository for this software is at                        */
/*                                                                      */
/*    https://bitbucket.org/kfj/vspline                                 */
/*                                                                      */
/*    Please direct questions, bug reports, and contributions to        */
/*                                                                      */
/*    kfjahnke+vspline@gmail.com                                        */
/*                                                                      */
/*    Permission is hereby granted, free of charge, to any person       */
/*    obtaining a copy of this software and associated documentation    */
/*    files (the "Software"), to deal in the Software without           */
/*    restriction, including without limitation the rights to use,      */
/*    copy, modify, merge, publish, distribute, sublicense, and/or      */
/*    sell copies of the Software, and to permit persons to whom the    */
/*    Software is furnished to do so, subject to the following          */
/*    conditions:                                                       */
/*                                                                      */
/*    The above copyright notice and this permission notice shall be    */
/*    included in all copies or substantial portions of the             */
/*    Software.                                                         */
/*                                                                      */
/*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
/*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
/*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
/*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
/*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
/*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
/*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
/*    OTHER DEALINGS IN THE SOFTWARE.                                   */
/*                                                                      */
/************************************************************************/

/*! \file simd_type_utils.h

    \brief some additional functions processing vspline::simd_type
           and Vc::Simdarray:
    
    -vspline::min
    -vspline::max
    -shuffle

    This header is optional and won't be automatically pulled in when
    including other vspline headers.
**/

#ifndef VSPLINE_SIMD_TYPE_UTILS_H
#define VSPLINE_SIMD_TYPE_UTILS_H

#include <vector>

namespace vspline
{
// element-wise minimum and maximum of two simd_type, or a simd_type
// and it's value_type. First we delegate vspline::min and ::max to
// std::min and std::max for fundamentals:

template < typename T >
typename std::enable_if < std::is_fundamental < T > :: value , T > :: type
min ( T lhs , T rhs )
{
  return std::min ( lhs , rhs ) ;
}

template < typename T >
typename std::enable_if < std::is_fundamental < T > :: value , T > :: type
max ( T lhs , T rhs )
{
  return std::max ( lhs , rhs ) ;
}

template < typename T , std::size_t N >
simd_type < T , N > min ( simd_type < T , N > lhs ,
                          simd_type < T , N > rhs )
{
  return ( lhs ( rhs < lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
simd_type < T , N > min ( simd_type < T , N > lhs ,
                          T rhs )
{
  return ( lhs ( rhs < lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
simd_type < T , N > min ( T lhs ,
                          simd_type < T , N > rhs )
{
  return ( rhs ( rhs > lhs ) = lhs ) ;
}

template < typename T , std::size_t N >
simd_type < T , N > max ( simd_type < T , N > lhs ,
                          simd_type < T , N > rhs )
{
  return ( lhs ( rhs > lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
simd_type < T , N > max ( simd_type < T , N > lhs ,
                          T rhs )
{
  return ( lhs ( rhs > lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
simd_type < T , N > max ( T lhs ,
                          simd_type < T , N > rhs )
{
  return ( rhs ( rhs < lhs ) = lhs ) ;
}

#ifdef USE_VC

template < typename T , std::size_t N >
vc_simd_type < T , N > min ( vc_simd_type < T , N > lhs ,
                             vc_simd_type < T , N > rhs )
{
  return ( lhs ( rhs < lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
vc_simd_type < T , N > min ( vc_simd_type < T , N > lhs ,
                             T rhs )
{
  return ( lhs ( rhs < lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
vc_simd_type < T , N > min ( T lhs ,
                             vc_simd_type < T , N > rhs )
{
  return ( rhs ( rhs > lhs ) = lhs ) ;
}

template < typename T , std::size_t N >
vc_simd_type < T , N > max ( vc_simd_type < T , N > lhs ,
                             vc_simd_type < T , N > rhs )
{
  return ( lhs ( rhs > lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
vc_simd_type < T , N > max ( vc_simd_type < T , N > lhs ,
                             T rhs )
{
  return ( lhs ( rhs > lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
vc_simd_type < T , N > max ( T lhs ,
                             vc_simd_type < T , N > rhs )
{
  return ( rhs ( rhs < lhs ) = lhs ) ;
}

#elif defined USE_HWY

template < typename T , std::size_t N >
hwy_simd_type < T , N > min ( hwy_simd_type < T , N > lhs ,
                              hwy_simd_type < T , N > rhs )
{
  return ( lhs ( rhs < lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
hwy_simd_type < T , N > min ( hwy_simd_type < T , N > lhs ,
                              T rhs )
{
  return ( lhs ( rhs < lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
hwy_simd_type < T , N > min ( T lhs ,
                              hwy_simd_type < T , N > rhs )
{
  return ( rhs ( rhs > lhs ) = lhs ) ;
}

template < typename T , std::size_t N >
hwy_simd_type < T , N > max ( hwy_simd_type < T , N > lhs ,
                              hwy_simd_type < T , N > rhs )
{
  return ( lhs ( rhs > lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
hwy_simd_type < T , N > max ( hwy_simd_type < T , N > lhs ,
                              T rhs )
{
  return ( lhs ( rhs > lhs ) = rhs ) ;
}

template < typename T , std::size_t N >
hwy_simd_type < T , N > max ( T lhs ,
                              hwy_simd_type < T , N > rhs )
{
  return ( rhs ( rhs < lhs ) = lhs ) ;
}

#endif

// shuffle is modelled after hardware shuffle instructions, which
// access a concatenation of two registers (or parts of registers)
// with indexes pertaining to the concatenated sequence.

template < typename T , std::size_t N >
simd_type < T , N > shuffle ( simd_type < T , N > lhs ,
                              simd_type < T , N > rhs ,
                              typename simd_type < T , N >
                                         :: index_type ix )
{
  // ixl holds all indexes pertaining to lhs, with those indexes
  // from ix pertaining to rhs set to N-1

  auto ixl = vspline::min ( ix , int ( N - 1 ) ) ;

  // ixr holds all indexes pertaining to rhs, with those indexes
  // from ix pertaining to lhs set to zero

  auto ixr = vspline::max ( 0 , ix - N ) ;

  // initially, the result picks from lhs, then overwrites those
  // elements with values picked from rhs where ix is N or greater.

  auto result = lhs [ ixl ] ;
  result ( ix >= N ) = rhs [ ixr ] ;
  return result ;
}

// shuffle several vectors. This should be used for shuffles with more
// than two vectors - for two vectors it's slightly less efficient than
// the function above

template < typename T , std::size_t N >
simd_type < T , N > shuffle ( std::vector < simd_type < T , N > > args ,
                              typename simd_type < T , N > :: index_type ix )                              
{
  auto count = args.size() ;
  auto ixl = vspline::min ( ix , int ( N - 1 ) ) ;
  auto result = args [ 0 ] [ ixl ] ;
  if ( count > 1 )
  {
    for ( int i = 1 ; i <= count ; i++ )
    {
      auto ixr = vspline::max ( 0 , ix - i * N ) ;
      ixr = min ( ixr , int ( N - 1 ) ) ;
      result ( ix >= i * N ) = args [ i ] [ ixr ] ;
    }
  }
  return result ;
}

} ; // end of namespace vspline

#endif // #define VSPLINE_SIMD_TYPE_UTILS_H
