/************************************************************************/
/*                                                                      */
/*    vspline - a set of generic tools for creation and evaluation      */
/*              of uniform b-splines                                    */
/*                                                                      */
/*            Copyright 2015 - 2020 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.

    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
