ClickHouse/dbms/include/DB/Functions/NumberTraits.h
2017-04-01 11:35:09 +03:00

676 lines
30 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <boost/mpl/bool.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/greater.hpp>
#include <boost/mpl/min_max.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/comparison.hpp>
#include <DB/Core/Types.h>
#include <tuple>
#include <type_traits>
namespace DB
{
/** Позволяет получить тип результата применения функций +, -, *, /, %, div (целочисленное деление).
* Правила отличаются от используемых в C++.
*/
namespace NumberTraits
{
using Unsigned = boost::mpl::false_ ;
using Signed = boost::mpl::true_ ;
using Integer = boost::mpl::false_ ;
using Floating = boost::mpl::true_ ;
using HasNull = boost::mpl::true_;
using HasNoNull = boost::mpl::false_;
using Bits0 = boost::mpl::int_<0> ;
using Bits8 = boost::mpl::int_<8> ;
using Bits16 = boost::mpl::int_<16> ;
using Bits32 = boost::mpl::int_<32> ;
using Bits64 = boost::mpl::int_<64> ;
using BitsTooMany = boost::mpl::int_<1024>;
struct Error {};
template <typename T, typename Nullability>
struct AddNullability;
template <typename T>
struct AddNullability<T, HasNull>
{
using Type = Nullable<T>;
};
template <typename T>
struct AddNullability<T, HasNoNull>
{
using Type = T;
};
template <typename T> struct Next;
template <> struct Next<Bits0> { using Type = Bits0; };
template <> struct Next<Bits8> { using Type = Bits16; };
template <> struct Next<Bits16> { using Type = Bits32; };
template <> struct Next<Bits32> { using Type = Bits64; };
template <> struct Next<Bits64> { using Type = Bits64; };
template <typename T> struct ExactNext { using Type = typename Next<T>::Type; };
template <> struct ExactNext<Bits64> { using Type = BitsTooMany; };
template <typename T> struct Traits;
template <typename T>
struct Traits<Nullable<T>>
{
using Sign = typename Traits<T>::Sign;
using Floatness = typename Traits<T>::Floatness;
using Bits = typename Traits<T>::Bits;
using Nullity = HasNull;
};
template <> struct Traits<void> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits0 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<Null> : Traits<Nullable<void>> {};
template <> struct Traits<UInt8> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits8 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<UInt16> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits16 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<UInt32> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits32 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<UInt64> { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits64 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<Int8> { typedef Signed Sign; typedef Integer Floatness; typedef Bits8 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<Int16> { typedef Signed Sign; typedef Integer Floatness; typedef Bits16 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<Int32> { typedef Signed Sign; typedef Integer Floatness; typedef Bits32 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<Int64> { typedef Signed Sign; typedef Integer Floatness; typedef Bits64 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<Float32> { typedef Signed Sign; typedef Floating Floatness; typedef Bits32 Bits; typedef HasNoNull Nullity; };
template <> struct Traits<Float64> { typedef Signed Sign; typedef Floating Floatness; typedef Bits64 Bits; typedef HasNoNull Nullity; };
template <typename Sign, typename Floatness, typename Bits, typename Nullity> struct Construct;
template <typename Sign, typename Floatness, typename Bits>
struct Construct<Sign, Floatness, Bits, HasNull>
{
using Type = Nullable<typename Construct<Sign, Floatness, Bits, HasNoNull>::Type>;
};
template <> struct Construct<Unsigned, Integer, Bits0, HasNull> { using Type = Null; };
template <> struct Construct<Unsigned, Floating, Bits0, HasNull> { using Type = Null; };
template <> struct Construct<Signed, Integer, Bits0, HasNull> { using Type = Null; };
template <> struct Construct<Signed, Floating, Bits0, HasNull> { using Type = Null; };
template <typename Sign, typename Floatness>
struct Construct<Sign, Floatness, BitsTooMany, HasNull>
{
using Type = Error;
};
template <typename Sign, typename Floatness>
struct Construct<Sign, Floatness, BitsTooMany, HasNoNull>
{
using Type = Error;
};
template <> struct Construct<Unsigned, Integer, Bits0, HasNoNull> { using Type = void; };
template <> struct Construct<Unsigned, Floating, Bits0, HasNoNull> { using Type = void; };
template <> struct Construct<Signed, Integer, Bits0, HasNoNull> { using Type = void; };
template <> struct Construct<Signed, Floating, Bits0, HasNoNull> { using Type = void; };
template <> struct Construct<Unsigned, Integer, Bits8, HasNoNull> { using Type = UInt8 ; };
template <> struct Construct<Unsigned, Integer, Bits16, HasNoNull> { using Type = UInt16 ; };
template <> struct Construct<Unsigned, Integer, Bits32, HasNoNull> { using Type = UInt32 ; };
template <> struct Construct<Unsigned, Integer, Bits64, HasNoNull> { using Type = UInt64 ; };
template <> struct Construct<Unsigned, Floating, Bits8, HasNoNull> { using Type = Float32 ; };
template <> struct Construct<Unsigned, Floating, Bits16, HasNoNull> { using Type = Float32 ; };
template <> struct Construct<Unsigned, Floating, Bits32, HasNoNull> { using Type = Float32 ; };
template <> struct Construct<Unsigned, Floating, Bits64, HasNoNull> { using Type = Float64 ; };
template <> struct Construct<Signed, Integer, Bits8, HasNoNull> { using Type = Int8 ; };
template <> struct Construct<Signed, Integer, Bits16, HasNoNull> { using Type = Int16 ; };
template <> struct Construct<Signed, Integer, Bits32, HasNoNull> { using Type = Int32 ; };
template <> struct Construct<Signed, Integer, Bits64, HasNoNull> { using Type = Int64 ; };
template <> struct Construct<Signed, Floating, Bits8, HasNoNull> { using Type = Float32 ; };
template <> struct Construct<Signed, Floating, Bits16, HasNoNull> { using Type = Float32 ; };
template <> struct Construct<Signed, Floating, Bits32, HasNoNull> { using Type = Float32 ; };
template <> struct Construct<Signed, Floating, Bits64, HasNoNull> { using Type = Float64 ; };
template <typename T>
inline bool isErrorType()
{
return false;
}
template <>
inline bool isErrorType<Error>()
{
return true;
}
/// Returns the type A augmented with nullity = nullity(A) | nullity(B)
template <typename A, typename B>
struct UpdateNullity
{
using Type = typename Construct<
typename Traits<A>::Sign,
typename Traits<A>::Floatness,
typename Traits<A>::Bits,
typename boost::mpl::or_<typename Traits<A>::Nullity, typename Traits<B>::Nullity>::type
>::Type;
};
/** Результат сложения или умножения вычисляется по следующим правилам:
* - если один из аргументов с плавающей запятой, то результат - с плавающей запятой, иначе - целый;
* - если одно из аргументов со знаком, то результат - со знаком, иначе - без знака;
* - результат содержит больше бит (не только значащих), чем максимум в аргументах
* (например, UInt8 + Int32 = Int64).
*/
template <typename A, typename B> struct ResultOfAdditionMultiplication
{
typedef typename Construct<
typename boost::mpl::or_<typename Traits<A>::Sign, typename Traits<B>::Sign>::type,
typename boost::mpl::or_<typename Traits<A>::Floatness, typename Traits<B>::Floatness>::type,
typename Next<typename boost::mpl::max<typename Traits<A>::Bits, typename Traits<B>::Bits>::type>::Type,
typename boost::mpl::or_<typename Traits<A>::Nullity, typename Traits<B>::Nullity>::type>::Type Type;
};
template <typename A, typename B> struct ResultOfSubtraction
{
typedef typename Construct<
Signed,
typename boost::mpl::or_<typename Traits<A>::Floatness, typename Traits<B>::Floatness>::type,
typename Next<typename boost::mpl::max<typename Traits<A>::Bits, typename Traits<B>::Bits>::type>::Type,
typename boost::mpl::or_<typename Traits<A>::Nullity, typename Traits<B>::Nullity>::type>::Type Type;
};
/** При делении всегда получается число с плавающей запятой.
*/
template <typename A, typename B> struct ResultOfFloatingPointDivision
{
using Type = Float64;
};
/** При целочисленном делении получается число, битность которого равна делимому.
*/
template <typename A, typename B> struct ResultOfIntegerDivision
{
typedef typename Construct<
typename boost::mpl::or_<typename Traits<A>::Sign, typename Traits<B>::Sign>::type,
Integer,
typename Traits<A>::Bits,
typename boost::mpl::or_<typename Traits<A>::Nullity, typename Traits<B>::Nullity>::type>::Type Type;
};
/** При взятии остатка получается число, битность которого равна делителю.
*/
template <typename A, typename B> struct ResultOfModulo
{
typedef typename Construct<
typename boost::mpl::or_<typename Traits<A>::Sign, typename Traits<B>::Sign>::type,
Integer,
typename Traits<B>::Bits,
typename boost::mpl::or_<typename Traits<A>::Nullity, typename Traits<B>::Nullity>::type>::Type Type;
};
template <typename A> struct ResultOfNegate
{
typedef typename Construct<
Signed,
typename Traits<A>::Floatness,
typename boost::mpl::if_<
typename Traits<A>::Sign,
typename Traits<A>::Bits,
typename Next<typename Traits<A>::Bits>::Type>::type,
typename Traits<A>::Nullity>::Type Type;
};
template <typename A> struct ResultOfAbs
{
typedef typename Construct<
Unsigned,
typename Traits<A>::Floatness,
typename Traits <A>::Bits,
typename Traits<A>::Nullity>::Type Type;
};
/** При побитовых операциях получается целое число, битность которого равна максимальной из битностей аргументов.
*/
template <typename A, typename B> struct ResultOfBit
{
typedef typename Construct<
typename boost::mpl::or_<typename Traits<A>::Sign, typename Traits<B>::Sign>::type,
Integer,
typename boost::mpl::max<
typename boost::mpl::if_<
typename Traits<A>::Floatness,
Bits64,
typename Traits<A>::Bits>::type,
typename boost::mpl::if_<
typename Traits<B>::Floatness,
Bits64,
typename Traits<B>::Bits>::type>::type,
typename boost::mpl::or_<typename Traits<A>::Nullity, typename Traits<B>::Nullity>::type>::Type Type;
};
template <typename A> struct ResultOfBitNot
{
typedef typename Construct<
typename Traits<A>::Sign,
Integer,
typename Traits<A>::Bits,
typename Traits<A>::Nullity>::Type Type;
};
/** Приведение типов для функции if:
* 1) void, Type -> Type
* 2) UInt<x>, UInt<y> -> UInt<max(x,y)>
* 3) Int<x>, Int<y> -> Int<max(x,y)>
* 4) Float<x>, Float<y> -> Float<max(x, y)>
* 5) UInt<x>, Int<y> -> Int<max(x*2, y)>
* 6) Float<x>, [U]Int<y> -> Float<max(x, y*2)>
* 7) UInt64 , Int<x> -> Error
* 8) Float<x>, [U]Int64 -> Error
*/
template <typename A, typename B>
struct ResultOfIf
{
typedef
/// 1)
typename boost::mpl::if_<
typename boost::mpl::equal_to<typename Traits<A>::Bits, Bits0>::type,
typename UpdateNullity<B, A>::Type,
typename boost::mpl::if_<
typename boost::mpl::equal_to<typename Traits<B>::Bits, Bits0>::type,
typename UpdateNullity<A, B>::Type,
/// 4) and 6)
typename boost::mpl::if_<
typename boost::mpl::or_<
typename Traits<A>::Floatness,
typename Traits<B>::Floatness>::type,
typename Construct<
Signed,
Floating,
typename boost::mpl::max< /// Этот максимум нужен только потому что if_ всегда вычисляет все аргументы.
typename boost::mpl::max<
typename boost::mpl::if_<
typename Traits<A>::Floatness,
typename Traits<A>::Bits,
typename ExactNext<typename Traits<A>::Bits>::Type>::type,
typename boost::mpl::if_<
typename Traits<B>::Floatness,
typename Traits<B>::Bits,
typename ExactNext<typename Traits<B>::Bits>::Type>::type>::type,
Bits32>::type,
typename boost::mpl::or_<typename Traits<A>::Nullity, typename Traits<B>::Nullity>::type>::Type,
/// 2) and 3)
typename boost::mpl::if_<
typename boost::mpl::equal_to<
typename Traits<A>::Sign,
typename Traits<B>::Sign>::type,
typename boost::mpl::if_<
typename boost::mpl::less<
typename Traits<A>::Bits,
typename Traits<B>::Bits>::type,
typename UpdateNullity<B, A>::Type,
typename UpdateNullity<A, B>::Type>::type,
/// 5)
typename Construct<
Signed,
Integer,
typename boost::mpl::max<
typename boost::mpl::if_<
typename Traits<A>::Sign,
typename Traits<A>::Bits,
typename ExactNext<typename Traits<A>::Bits>::Type>::type,
typename boost::mpl::if_<
typename Traits<B>::Sign,
typename Traits<B>::Bits,
typename ExactNext<typename Traits<B>::Bits>::Type>::type>::type,
typename boost::mpl::or_<typename Traits<A>::Nullity, typename Traits<B>::Nullity>::type
>::Type>::type>::type>::type>::type Type;
};
/** Перед применением оператора % и побитовых операций, операнды приводятся к целым числам. */
template <typename A> struct ToInteger
{
typedef typename Construct<
typename Traits<A>::Sign,
Integer,
typename boost::mpl::if_<
typename Traits<A>::Floatness,
Bits64,
typename Traits<A>::Bits>::type,
typename Traits<A>::Nullity
>::Type Type;
};
// CLICKHOUSE-29. The same depth, different signs
// NOTE: This case is applied for 64-bit integers only (for backward compability), but colud be used for any-bit integers
template <typename A, typename B>
using LeastGreatestSpecialCase = std::integral_constant<bool, std::is_integral<A>::value && std::is_integral<B>::value
&& (8 == sizeof(A) && sizeof(A) == sizeof(B))
&& (std::is_signed<A>::value ^ std::is_signed<B>::value)>;
template <typename A, typename B>
using ResultOfLeast = std::conditional_t<LeastGreatestSpecialCase<A, B>::value,
typename Construct<Signed, Integer, typename Traits<A>::Bits, HasNoNull>::Type,
typename ResultOfIf<A, B>::Type>;
template <typename A, typename B>
using ResultOfGreatest = std::conditional_t<LeastGreatestSpecialCase<A, B>::value,
typename Construct<Unsigned, Integer, typename Traits<A>::Bits, HasNoNull>::Type,
typename ResultOfIf<A, B>::Type>;
/// Notes on type composition.
///
/// I. Problem statement.
///
/// Type composition with ResultOfIf is not associative. Example:
/// (Int8 x UInt32) x Float32 = Int64 x Float32 = Error;
/// Int8 x (UInt32 x Float32) = Int8 x Float64 = Float64.
/// In order to sort out this issue, we design a slightly improved version
/// of ResultOfIf.
///
/// II. A more rigorous approach to ResultOfIf.
///
/// First we represent the set of types:
/// T = {Void,Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float32,Float64}
/// as a poset P with the partial order being such that for any t1,t2 ∈ T,
/// t1 < t2 if and only if the domain of values of t1 is included in the domain
/// of values of t2.
///
/// For each type t ∈ T, we define C(t) as the set of chains of the poset P whose
/// unique minimal element is T.
///
/// Now for any two types t1,t2 ∈ T, we define the poset C(t1,t2) as the intersection
/// C(t1) ∩ C(t2).
///
/// Denote K(t1,t2) as the unique antichain of C(t1,t2) in which each element minimally
/// represents both t1 and t2. It is important to keep in mind that t1 and t2 are
/// *not* comparable.
///
/// For the most part, K(t1,t2) coincides with the result of the application of
/// ResultOfIf to t1 and t2. Nevertheless, for some particular combinations of t1
/// and t2, the map K returns one of the following two antichains: {Int32,Float32},
/// {Int64,Float64}.
///
/// From these observations, we conclude that the type system T and the composition
/// law ResultOfIf are not powerful enough to represent all the combinations of
/// elements of T. That is the reason why ResultOfIf is not associative.
///
/// III. Extending ResultOfIf.
///
/// Let's embed T into a larger set E of "enriched types" such that:
/// 1. E ⊂ TxT;
/// 2. for each t ∈ T, (T,Void) ∈ E.
/// 3. (Int32,Float32) ∈ E
/// 4. (Int64,Float64) ∈ E.
///
/// E represents the image A of the map K, a set of antichains, as a set of types.
///
/// Consider the canonical injection ψ : T x T ----> E x E and the natural bijection
/// φ : A ----> E.
/// Then there exists a unique map K' : E x E ----> E, that makes the diagram below
/// commutative:
///
/// K
/// T x T ----> A
/// | |
/// | ψ | φ
/// ↓ L ↓
/// E x E ----> E
///
/// L is exactly the same map as K, the sole difference being that L takes as
/// parameters extended types that map to ordinary ones.
///
/// Finally we extend the map L. To this end, we complete the type composition table
/// with the new types (Int32,Float32) and (Int64,Float64) appearing on either
/// the left-hand side or the right-hand side. This extended map is called
/// TypeProduct in the implementation. TypeProduct is both commutative and associative.
///
/// IV. Usage.
///
/// When we need to compose ordinary types, the following is to be performed:
/// 1. embed each type into its counterpart in E with EmbedType;
/// 2. compose the resulting enriched types with TypeProduct;
/// 3. return the first component of the result with ToOrdinaryType, which means
/// that, given an extended type e = (p,q) ∈ E, we return the ordinary type p ∈ T.
///
/// The result is the type we are looking for.
///
/// V. Example.
///
/// Suppose we need to compose, as in the problem statement, the types Int8,
/// UInt32, and Float32. The corresponding embedded types are:
/// (Int8,Void), (UInt32,Void), (Float32,Void).
///
/// By computing (Int8 x UInt32) x Float32, we get:
///
/// TypeProduct(TypeProduct((Int8,Void),(UInt32,Void)), (Float32,Void))
/// = TypeProduct((Int64,Float64), (Float32,Void))
/// = (Float64,void)
/// Thus, (Int8 x UInt32) x Float32 = Float64.
///
/// By computing Int8 x (UInt32 x Float32), we get:
///
/// TypeProduct((Int8,Void), TypeProduct((UInt32,Void), (Float32,Void)))
/// = TypeProduct((Int8,Void), (Float64,Void))
/// = (Float64,void)
/// Thus, Int8 x (UInt32 x Float32) = Float64.
///
namespace Enriched
{
/// Definitions of enriched types.
template <typename Nullity> using Void = std::tuple<void, void, Nullity>;
template <typename Nullity> using Int8 = std::tuple<DB::Int8, void, Nullity>;
template <typename Nullity> using Int16 = std::tuple<DB::Int16, void, Nullity>;
template <typename Nullity> using Int32 = std::tuple<DB::Int32, void, Nullity>;
template <typename Nullity> using Int64 = std::tuple<DB::Int64, void, Nullity>;
template <typename Nullity> using UInt8 = std::tuple<DB::UInt8, void, Nullity>;
template <typename Nullity> using UInt16 = std::tuple<DB::UInt16, void, Nullity>;
template <typename Nullity> using UInt32 = std::tuple<DB::UInt32, void, Nullity>;
template <typename Nullity> using UInt64 = std::tuple<DB::UInt64, void, Nullity>;
template <typename Nullity> using Float32 = std::tuple<DB::Float32, void, Nullity>;
template <typename Nullity> using Float64 = std::tuple<DB::Float64, void, Nullity>;
template <typename Nullity> using IntFloat32 = std::tuple<DB::Int32, DB::Float32, Nullity>;
template <typename Nullity> using IntFloat64 = std::tuple<DB::Int64, DB::Float64, Nullity>;
}
/// Embed an ordinary type into the corresponding enriched type.
template <typename T>
struct EmbedType;
template <> struct EmbedType<Error> { using Type = Error; };
template <> struct EmbedType<void> { using Type = Enriched::Void<HasNoNull>; };
template <> struct EmbedType<Int8> { using Type = Enriched::Int8<HasNoNull>; };
template <> struct EmbedType<Int16> { using Type = Enriched::Int16<HasNoNull>; };
template <> struct EmbedType<Int32> { using Type = Enriched::Int32<HasNoNull>; };
template <> struct EmbedType<Int64> { using Type = Enriched::Int64<HasNoNull>; };
template <> struct EmbedType<UInt8> { using Type = Enriched::UInt8<HasNoNull>; };
template <> struct EmbedType<UInt16> { using Type = Enriched::UInt16<HasNoNull>; };
template <> struct EmbedType<UInt32> { using Type = Enriched::UInt32<HasNoNull>; };
template <> struct EmbedType<UInt64> { using Type = Enriched::UInt64<HasNoNull>; };
template <> struct EmbedType<Float32> { using Type = Enriched::Float32<HasNoNull>; };
template <> struct EmbedType<Float64> { using Type = Enriched::Float64<HasNoNull>; };
template <> struct EmbedType<Null> { using Type = Enriched::Void<HasNull>; };
template <> struct EmbedType<Nullable<Int8> > { using Type = Enriched::Int8<HasNull>; };
template <> struct EmbedType<Nullable<Int16> > { using Type = Enriched::Int16<HasNull>; };
template <> struct EmbedType<Nullable<Int32> > { using Type = Enriched::Int32<HasNull>; };
template <> struct EmbedType<Nullable<Int64> > { using Type = Enriched::Int64<HasNull>; };
template <> struct EmbedType<Nullable<UInt8> > { using Type = Enriched::UInt8<HasNull>; };
template <> struct EmbedType<Nullable<UInt16> > { using Type = Enriched::UInt16<HasNull>; };
template <> struct EmbedType<Nullable<UInt32> > { using Type = Enriched::UInt32<HasNull>; };
template <> struct EmbedType<Nullable<UInt64> > { using Type = Enriched::UInt64<HasNull>; };
template <> struct EmbedType<Nullable<Float32> > { using Type = Enriched::Float32<HasNull>; };
template <> struct EmbedType<Nullable<Float64> > { using Type = Enriched::Float64<HasNull>; };
/// Get an ordinary type from an enriched type.
template <typename TType>
struct ToOrdinaryType
{
using Type = typename std::conditional<
std::is_same<typename std::tuple_element<2, TType>::type, HasNoNull>::value,
typename std::tuple_element<0, TType>::type,
Nullable<typename std::tuple_element<0, TType>::type>
>::type;
};
/// Get an ordinary type from an enriched type.
/// Error case.
template <>
struct ToOrdinaryType<Error>
{
using Type = Error;
};
namespace
{
/// The following helper functions and structures are used for the TypeProduct implementation.
/// Check if two types are equal up to nullity.
template <typename T1, typename T2>
constexpr bool areSimilarTypes()
{
return std::is_same<
typename std::tuple_element<0, T1>::type,
typename std::tuple_element<0, T2>::type
>::value &&
std::is_same<
typename std::tuple_element<1, T1>::type,
typename std::tuple_element<1, T2>::type
>::value;
}
/// Check if a pair of types {A,B} equals a pair of types {A1,B1} up to nullity.
template <typename A, typename B, template <typename> class A1, template <typename> class B1>
constexpr bool areSimilarPairs()
{
/// NOTE: the use of HasNoNull here is a trick. It has no meaning.
return (areSimilarTypes<A, A1<HasNoNull>>() && areSimilarTypes<B, B1<HasNoNull>>()) ||
(areSimilarTypes<A, B1<HasNoNull>>() && areSimilarTypes<B, A1<HasNoNull>>());
}
/// Check if a pair of enriched types {A,B} that have straight mappings to ordinary
/// types must be processed in a special way.
template <typename A, typename B>
constexpr bool isExceptionalPair()
{
return areSimilarPairs<A, B, Enriched::Int8, Enriched::UInt16>() ||
areSimilarPairs<A, B, Enriched::Int8, Enriched::UInt32>() ||
areSimilarPairs<A, B, Enriched::Int16, Enriched::UInt16>() ||
areSimilarPairs<A, B, Enriched::Int16, Enriched::UInt32>() ||
areSimilarPairs<A, B, Enriched::Int32, Enriched::UInt32>();
}
/// Check if a pair of enriched types {A,B} is ordinary. Here "ordinary" means
/// that both types map to ordinary types and that they are not exceptional as
/// defined in the function above.
template <typename A, typename B>
constexpr bool isOrdinaryPair()
{
return std::is_same<typename std::tuple_element<1, A>::type, void>::value &&
std::is_same<typename std::tuple_element<1, B>::type, void>::value &&
!isExceptionalPair<A, B>();
}
/// Returns nullity(A) | nullity(B).
template <typename A, typename B>
struct CombinedNullity
{
private:
using NullityA = typename Traits<typename ToOrdinaryType<A>::Type>::Nullity;
using NullityB = typename Traits<typename ToOrdinaryType<B>::Type>::Nullity;
public:
using Type = typename boost::mpl::or_<NullityA, NullityB>::type;
};
}
/// Compute the product of two enriched numeric types.
/// This statement catches all the incorrect combinations.
template <typename T1, typename T2, typename Enable = void>
struct TypeProduct
{
using Type = Error;
};
/// Compute the product of two enriched numeric types.
/// Case when both these types are ordinary in the meaning defined above.
template <typename A, typename B>
struct TypeProduct<A, B, typename std::enable_if<isOrdinaryPair<A, B>()>::type>
{
private:
using Result = typename ResultOfIf<
typename ToOrdinaryType<A>::Type,
typename ToOrdinaryType<B>::Type
>::Type;
public:
using Type = typename EmbedType<Result>::Type;
};
/// Compute the product of two enriched numeric types.
/// Case when a source type or the resulting type does not map to any ordinary type.
#define DEFINE_TYPE_PRODUCT_RULE(T1, T2, T3) \
template <typename A, typename B> \
struct TypeProduct< \
A, \
B, \
typename std::enable_if< \
!isOrdinaryPair<A, B>() && \
areSimilarPairs<A, B, T1, T2>() \
>::type> \
{ \
using Type = typename T3<typename CombinedNullity<A, B>::Type>; \
}
DEFINE_TYPE_PRODUCT_RULE(Enriched::Int8, Enriched::UInt16, Enriched::IntFloat32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::Int8, Enriched::UInt32, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::Int16, Enriched::UInt16, Enriched::IntFloat32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::Int16, Enriched::UInt32, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::Int32, Enriched::UInt32, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::Int8, Enriched::IntFloat32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::Int16, Enriched::IntFloat32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::Int32, Enriched::Int32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::Int64, Enriched::Int64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::Float32, Enriched::Float32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::Float64, Enriched::Float64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::UInt8, Enriched::IntFloat32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::UInt16, Enriched::IntFloat32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::UInt32, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::IntFloat32, Enriched::IntFloat32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::IntFloat64, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::Int8, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::Int16, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::Int32, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::Int64, Enriched::Int64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::Float32, Enriched::Float64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::Float64, Enriched::Float64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::UInt8, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::UInt16, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::UInt32, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::IntFloat64, Enriched::IntFloat64);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat32, Enriched::Void, Enriched::IntFloat32);
DEFINE_TYPE_PRODUCT_RULE(Enriched::IntFloat64, Enriched::Void, Enriched::IntFloat64);
#undef DEFINE_TYPE_PRODUCT_RULE
}
}