/************************************************************************/
/*                                                                      */
/*    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 vector_promote.h

    \brief numeric and promote traits for vector data types

    to make both vspline::simd_type and Vc::SimdArray 'fit into' vigra,
    we define vigra::NumericTraits and vigra::PromoteTraits for them.
    Since the required definitions are the same, we use this common header,
    which is included once with VECTOR_TYPE defined to vspline::simd_type
    and, if USE_VC is defined, once to Vc::SimdArray.
    The second definition which calling code has to provide is XEL_TYPE,
    which is the class template of a data type similar to vigra::TinyVector,
    defining a small array of equally-typed elements. This is used to
    generate the appropriate traits for such small arrays of SIMD data
    types. here's an example of how the code in this header may be used:

    #define XEL_TYPE vigra::TinyVector
    #include <Vc/Vc>
    #define VECTOR_TYPE Vc::SimdArray

    #include "vspline/opt/vector_promote.h"

    #undef VECTOR_TYPE
    #undef XEL_TYPE

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

#include <vigra/numerictraits.hxx>
#include <vigra/tinyvector.hxx>

namespace vigra
{

#ifndef VECTOR_PROMOTE_COMMON

// we start out with a bit of 'infrastructure' code:

template < typename T >
struct vrpromote
{
  typedef float promote ;
} ;

template<>
struct vrpromote < double >
{
  typedef double promote ;
} ;

template<>
struct vrpromote < long double >
{
  typedef long double promote ;
} ;

struct Error_differently_sized_vectors_not_interoperable { } ;

#define VECTOR_PROMOTE_COMMON
#endif

// abbreviated notation

#define VECTOR VECTOR_TYPE < T , S >

/// numeric traits for VECTOR_TYPE

template < typename T , std::size_t S >
struct NumericTraits < VECTOR >
{
  typedef VECTOR Type ;
  
  typedef typename vigra::NumericTraits<T>::Promote TPromote ;
  typedef typename vigra::NumericTraits<T>::UnsignedPromote TUnsignedPromote ;
  typedef typename vigra::NumericTraits<T>::RealPromote TRealPromote ;
  typedef typename vrpromote<T>::promote vrpromote_t ;

  typedef VECTOR_TYPE < TPromote , S > Promote ;
  typedef VECTOR_TYPE < TUnsignedPromote , S > UnsignedPromote ;
  typedef VECTOR_TYPE < vrpromote_t , S > RealPromote ;
  
  // have to see if this works out right:
  typedef std::complex < RealPromote > ComplexPromote;
  typedef T ValueType ;
  
  typedef typename vigra::NumericTraits<T>::isIntegral isIntegral ;
  typedef VigraFalseType isScalar ;
  typedef typename vigra::NumericTraits<T>::isSigned isSigned ;
  typedef VigraFalseType isOrdered ;
  typedef VigraFalseType isComplex ;
  
  static Type zero() { return Type::Zero() ; }
  static Type one() { return Type::One() ; }
  static Type nonZero() { return Type::One() ; }
  
  static Promote toPromote(Type v) { return v ; }
  static RealPromote toRealPromote(Type v) { return v ; }
  static Type fromPromote(Promote v) { return Type(v) ; }
  static Type fromRealPromote(RealPromote v) { return Type(v) ; }
} ;

// mixed operation of T and a vector of T promotes to the vector

template < class T , std::size_t S >
struct PromoteTraits < VECTOR , T >
{
  typedef VECTOR Promote ;
} ;

template < class T , std::size_t S >
struct PromoteTraits < T , VECTOR >
{
  typedef VECTOR Promote ;
} ;

// promotion from two equally sized vectors with different T is possible

template < class T1 , class T2 , std::size_t S >
struct PromoteTraits < VECTOR_TYPE < T1 , S > ,
                       VECTOR_TYPE < T2 , S > >
{
  // the template definition ensures that both vectors have common SIZE,
  // so we only need to fix the common elementary type

  typedef VECTOR_TYPE
          < typename PromoteTraits < T1 , T2 > :: Promote ,
            S 
          > Promote ;
} ;

// but differently sized vectors are not interoperable

template < class T1 , class T2 ,
           std::size_t S1 , std::size_t S2 >
struct PromoteTraits < VECTOR_TYPE < T1 , S1 > ,
                       VECTOR_TYPE < T2 , S2 > >
: public Error_differently_sized_vectors_not_interoperable
{ } ;

// TinyVectors of vectors can be combined with single vectors,
// and TinyVectors of T and T.

template < class T , std::size_t S , int N >
struct PromoteTraits < VECTOR ,
                       XEL_TYPE < VECTOR , N > >
{
  typedef XEL_TYPE < VECTOR , N > Promote ;
} ;

template < class T , std::size_t S , int N >
struct PromoteTraits < T ,
                       XEL_TYPE < VECTOR , N > >
{
  typedef XEL_TYPE < VECTOR , N > Promote ;
} ;

template < class T , std::size_t S , int N >
struct PromoteTraits < XEL_TYPE < T , N > ,
                       XEL_TYPE < VECTOR , N > >
{
  typedef XEL_TYPE < VECTOR , N > Promote ;
} ;

template < class T , std::size_t S , int N >
struct PromoteTraits < XEL_TYPE < VECTOR , N > ,
                       VECTOR >
{
  typedef XEL_TYPE < VECTOR , N > Promote ;
} ;

template < class T , std::size_t S , int N >
struct PromoteTraits < XEL_TYPE < VECTOR , N > ,
                       T >
{
  typedef XEL_TYPE < VECTOR , N > Promote ;
} ;

template < class T , std::size_t S , int N >
struct PromoteTraits < XEL_TYPE < VECTOR , N > ,
                       XEL_TYPE < T , N > >
{
  typedef XEL_TYPE < VECTOR , N > Promote ;
} ;

} ;

#undef VECTOR
