#pragma once #include #include #include #include #include #include #include #include #include #include #include #include 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 struct AddNullability; template struct AddNullability { using Type = Nullable; }; template struct AddNullability { using Type = T; }; template struct Next; template <> struct Next { using Type = Bits0; }; template <> struct Next { using Type = Bits16; }; template <> struct Next { using Type = Bits32; }; template <> struct Next { using Type = Bits64; }; template <> struct Next { using Type = Bits64; }; template struct ExactNext { using Type = typename Next::Type; }; template <> struct ExactNext { using Type = BitsTooMany; }; template struct Traits; template struct Traits> { using Sign = typename Traits::Sign; using Floatness = typename Traits::Floatness; using Bits = typename Traits::Bits; using Nullity = HasNull; }; template <> struct Traits { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits0 Bits; typedef HasNoNull Nullity; }; template <> struct Traits : Traits> {}; template <> struct Traits { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits8 Bits; typedef HasNoNull Nullity; }; template <> struct Traits { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits16 Bits; typedef HasNoNull Nullity; }; template <> struct Traits { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits32 Bits; typedef HasNoNull Nullity; }; template <> struct Traits { typedef Unsigned Sign; typedef Integer Floatness; typedef Bits64 Bits; typedef HasNoNull Nullity; }; template <> struct Traits { typedef Signed Sign; typedef Integer Floatness; typedef Bits8 Bits; typedef HasNoNull Nullity; }; template <> struct Traits { typedef Signed Sign; typedef Integer Floatness; typedef Bits16 Bits; typedef HasNoNull Nullity; }; template <> struct Traits { typedef Signed Sign; typedef Integer Floatness; typedef Bits32 Bits; typedef HasNoNull Nullity; }; template <> struct Traits { typedef Signed Sign; typedef Integer Floatness; typedef Bits64 Bits; typedef HasNoNull Nullity; }; template <> struct Traits { typedef Signed Sign; typedef Floating Floatness; typedef Bits32 Bits; typedef HasNoNull Nullity; }; template <> struct Traits { typedef Signed Sign; typedef Floating Floatness; typedef Bits64 Bits; typedef HasNoNull Nullity; }; template struct Construct; template struct Construct { using Type = Nullable::Type>; }; template <> struct Construct { using Type = Null; }; template <> struct Construct { using Type = Null; }; template <> struct Construct { using Type = Null; }; template <> struct Construct { using Type = Null; }; template struct Construct { using Type = Error; }; template struct Construct { using Type = Error; }; template <> struct Construct { using Type = void; }; template <> struct Construct { using Type = void; }; template <> struct Construct { using Type = void; }; template <> struct Construct { using Type = void; }; template <> struct Construct { using Type = UInt8 ; }; template <> struct Construct { using Type = UInt16 ; }; template <> struct Construct { using Type = UInt32 ; }; template <> struct Construct { using Type = UInt64 ; }; template <> struct Construct { using Type = Float32 ; }; template <> struct Construct { using Type = Float32 ; }; template <> struct Construct { using Type = Float32 ; }; template <> struct Construct { using Type = Float64 ; }; template <> struct Construct { using Type = Int8 ; }; template <> struct Construct { using Type = Int16 ; }; template <> struct Construct { using Type = Int32 ; }; template <> struct Construct { using Type = Int64 ; }; template <> struct Construct { using Type = Float32 ; }; template <> struct Construct { using Type = Float32 ; }; template <> struct Construct { using Type = Float32 ; }; template <> struct Construct { using Type = Float64 ; }; template inline bool isErrorType() { return false; } template <> inline bool isErrorType() { return true; } /// Returns the type A augmented with nullity = nullity(A) | nullity(B) template struct UpdateNullity { using Type = typename Construct< typename Traits::Sign, typename Traits::Floatness, typename Traits::Bits, typename boost::mpl::or_::Nullity, typename Traits::Nullity>::type >::Type; }; /** Результат сложения или умножения вычисляется по следующим правилам: * - если один из аргументов с плавающей запятой, то результат - с плавающей запятой, иначе - целый; * - если одно из аргументов со знаком, то результат - со знаком, иначе - без знака; * - результат содержит больше бит (не только значащих), чем максимум в аргументах * (например, UInt8 + Int32 = Int64). */ template struct ResultOfAdditionMultiplication { typedef typename Construct< typename boost::mpl::or_::Sign, typename Traits::Sign>::type, typename boost::mpl::or_::Floatness, typename Traits::Floatness>::type, typename Next::Bits, typename Traits::Bits>::type>::Type, typename boost::mpl::or_::Nullity, typename Traits::Nullity>::type>::Type Type; }; template struct ResultOfSubtraction { typedef typename Construct< Signed, typename boost::mpl::or_::Floatness, typename Traits::Floatness>::type, typename Next::Bits, typename Traits::Bits>::type>::Type, typename boost::mpl::or_::Nullity, typename Traits::Nullity>::type>::Type Type; }; /** При делении всегда получается число с плавающей запятой. */ template struct ResultOfFloatingPointDivision { using Type = Float64; }; /** При целочисленном делении получается число, битность которого равна делимому. */ template struct ResultOfIntegerDivision { typedef typename Construct< typename boost::mpl::or_::Sign, typename Traits::Sign>::type, typename boost::mpl::or_::Floatness, typename Traits::Floatness>::type, typename Traits::Bits, typename boost::mpl::or_::Nullity, typename Traits::Nullity>::type>::Type Type; }; /** При взятии остатка получается число, битность которого равна делителю. */ template struct ResultOfModulo { typedef typename Construct< typename boost::mpl::or_::Sign, typename Traits::Sign>::type, Integer, typename Traits::Bits, typename boost::mpl::or_::Nullity, typename Traits::Nullity>::type>::Type Type; }; template struct ResultOfNegate { typedef typename Construct< Signed, typename Traits::Floatness, typename boost::mpl::if_< typename Traits::Sign, typename Traits::Bits, typename Next::Bits>::Type>::type, typename Traits::Nullity>::Type Type; }; template struct ResultOfAbs { typedef typename Construct< Unsigned, typename Traits::Floatness, typename Traits ::Bits, typename Traits::Nullity>::Type Type; }; /** При побитовых операциях получается целое число, битность которого равна максимальной из битностей аргументов. */ template struct ResultOfBit { typedef typename Construct< typename boost::mpl::or_::Sign, typename Traits::Sign>::type, Integer, typename boost::mpl::max< typename boost::mpl::if_< typename Traits::Floatness, Bits64, typename Traits::Bits>::type, typename boost::mpl::if_< typename Traits::Floatness, Bits64, typename Traits::Bits>::type>::type, typename boost::mpl::or_::Nullity, typename Traits::Nullity>::type>::Type Type; }; template struct ResultOfBitNot { typedef typename Construct< typename Traits::Sign, Integer, typename Traits::Bits, typename Traits::Nullity>::Type Type; }; /** Приведение типов для функции if: * 1) void, Type -> Type * 2) UInt, UInt -> UInt * 3) Int, Int -> Int * 4) Float, Float -> Float * 5) UInt, Int -> Int * 6) Float, [U]Int -> Float * 7) UInt64 , Int -> Error * 8) Float, [U]Int64 -> Error */ template struct ResultOfIf { typedef /// 1) typename boost::mpl::if_< typename boost::mpl::equal_to::Bits, Bits0>::type, typename UpdateNullity::Type, typename boost::mpl::if_< typename boost::mpl::equal_to::Bits, Bits0>::type, typename UpdateNullity::Type, /// 4) and 6) typename boost::mpl::if_< typename boost::mpl::or_< typename Traits::Floatness, typename Traits::Floatness>::type, typename Construct< Signed, Floating, typename boost::mpl::max< /// Этот максимум нужен только потому что if_ всегда вычисляет все аргументы. typename boost::mpl::max< typename boost::mpl::if_< typename Traits::Floatness, typename Traits::Bits, typename ExactNext::Bits>::Type>::type, typename boost::mpl::if_< typename Traits::Floatness, typename Traits::Bits, typename ExactNext::Bits>::Type>::type>::type, Bits32>::type, typename boost::mpl::or_::Nullity, typename Traits::Nullity>::type>::Type, /// 2) and 3) typename boost::mpl::if_< typename boost::mpl::equal_to< typename Traits::Sign, typename Traits::Sign>::type, typename boost::mpl::if_< typename boost::mpl::less< typename Traits::Bits, typename Traits::Bits>::type, typename UpdateNullity::Type, typename UpdateNullity::Type>::type, /// 5) typename Construct< Signed, Integer, typename boost::mpl::max< typename boost::mpl::if_< typename Traits::Sign, typename Traits::Bits, typename ExactNext::Bits>::Type>::type, typename boost::mpl::if_< typename Traits::Sign, typename Traits::Bits, typename ExactNext::Bits>::Type>::type>::type, typename boost::mpl::or_::Nullity, typename Traits::Nullity>::type >::Type>::type>::type>::type>::type Type; }; /** Перед применением оператора % и побитовых операций, операнды приводятся к целым числам. */ template struct ToInteger { typedef typename Construct< typename Traits::Sign, Integer, typename boost::mpl::if_< typename Traits::Floatness, Bits64, typename Traits::Bits>::type, typename Traits::Nullity >::Type 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 using Void = std::tuple; template using Int8 = std::tuple; template using Int16 = std::tuple; template using Int32 = std::tuple; template using Int64 = std::tuple; template using UInt8 = std::tuple; template using UInt16 = std::tuple; template using UInt32 = std::tuple; template using UInt64 = std::tuple; template using Float32 = std::tuple; template using Float64 = std::tuple; template using IntFloat32 = std::tuple; template using IntFloat64 = std::tuple; } /// Embed an ordinary type into the corresponding enriched type. template struct EmbedType; template <> struct EmbedType { using Type = Error; }; template <> struct EmbedType { using Type = Enriched::Void; }; template <> struct EmbedType { using Type = Enriched::Int8; }; template <> struct EmbedType { using Type = Enriched::Int16; }; template <> struct EmbedType { using Type = Enriched::Int32; }; template <> struct EmbedType { using Type = Enriched::Int64; }; template <> struct EmbedType { using Type = Enriched::UInt8; }; template <> struct EmbedType { using Type = Enriched::UInt16; }; template <> struct EmbedType { using Type = Enriched::UInt32; }; template <> struct EmbedType { using Type = Enriched::UInt64; }; template <> struct EmbedType { using Type = Enriched::Float32; }; template <> struct EmbedType { using Type = Enriched::Float64; }; template <> struct EmbedType { using Type = Enriched::Void; }; template <> struct EmbedType > { using Type = Enriched::Int8; }; template <> struct EmbedType > { using Type = Enriched::Int16; }; template <> struct EmbedType > { using Type = Enriched::Int32; }; template <> struct EmbedType > { using Type = Enriched::Int64; }; template <> struct EmbedType > { using Type = Enriched::UInt8; }; template <> struct EmbedType > { using Type = Enriched::UInt16; }; template <> struct EmbedType > { using Type = Enriched::UInt32; }; template <> struct EmbedType > { using Type = Enriched::UInt64; }; template <> struct EmbedType > { using Type = Enriched::Float32; }; template <> struct EmbedType > { using Type = Enriched::Float64; }; /// Get an ordinary type from an enriched type. template struct ToOrdinaryType { using Type = typename std::conditional< std::is_same::type, HasNoNull>::value, typename std::tuple_element<0, TType>::type, Nullable::type> >::type; }; /// Get an ordinary type from an enriched type. /// Error case. template <> struct ToOrdinaryType { 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 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 class A1, template class B1> constexpr bool areSimilarPairs() { /// NOTE: the use of HasNoNull here is a trick. It has no meaning. return (areSimilarTypes>() && areSimilarTypes>()) || (areSimilarTypes>() && areSimilarTypes>()); } /// Check if a pair of enriched types {A,B} that have straight mappings to ordinary /// types must be processed in a special way. template constexpr bool isExceptionalPair() { return areSimilarPairs() || areSimilarPairs() || areSimilarPairs() || areSimilarPairs() || areSimilarPairs(); } /// 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 constexpr bool isOrdinaryPair() { return std::is_same::type, void>::value && std::is_same::type, void>::value && !isExceptionalPair(); } /// Returns nullity(A) | nullity(B). template struct CombinedNullity { private: using NullityA = typename Traits::Type>::Nullity; using NullityB = typename Traits::Type>::Nullity; public: using Type = typename boost::mpl::or_::type; }; } /// Compute the product of two enriched numeric types. /// This statement catches all the incorrect combinations. template 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 struct TypeProduct()>::type> { private: using Result = typename ResultOfIf< typename ToOrdinaryType::Type, typename ToOrdinaryType::Type >::Type; public: using Type = typename EmbedType::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 \ struct TypeProduct< \ A, \ B, \ typename std::enable_if< \ !isOrdinaryPair() && \ areSimilarPairs() \ >::type> \ { \ using Type = typename T3::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); #undef DEFINE_TYPE_PRODUCT_RULE } }