2011-08-09 15:57:33 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <DB/DataTypes/DataTypesNumberFixed.h>
|
2014-09-08 10:46:30 +00:00
|
|
|
|
#include <DB/DataTypes/DataTypeDate.h>
|
|
|
|
|
#include <DB/DataTypes/DataTypeDateTime.h>
|
2011-08-09 15:57:33 +00:00
|
|
|
|
#include <DB/Functions/IFunction.h>
|
|
|
|
|
#include <DB/Functions/NumberTraits.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
2011-08-09 17:24:17 +00:00
|
|
|
|
/** Арифметические функции: +, -, *, /, %,
|
2012-12-11 20:32:08 +00:00
|
|
|
|
* intDiv (целочисленное деление), унарный минус.
|
2012-12-20 20:01:18 +00:00
|
|
|
|
* Битовые функции: |, &, ^, ~.
|
2011-08-09 15:57:33 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template<typename A, typename B, typename Op, typename ResultType_ = typename Op::ResultType>
|
2014-08-17 02:13:40 +00:00
|
|
|
|
struct BinaryOperationImplBase
|
2011-08-09 15:57:33 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
typedef ResultType_ ResultType;
|
2012-12-20 20:01:18 +00:00
|
|
|
|
|
2013-12-08 02:29:40 +00:00
|
|
|
|
static void vector_vector(const PODArray<A> & a, const PODArray<B> & b, PODArray<ResultType> & c)
|
2011-08-09 15:57:33 +00:00
|
|
|
|
{
|
|
|
|
|
size_t size = a.size();
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2014-09-08 10:46:30 +00:00
|
|
|
|
c[i] = Op::template apply<ResultType>(a[i], b[i]);
|
2011-08-09 15:57:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-08 02:29:40 +00:00
|
|
|
|
static void vector_constant(const PODArray<A> & a, B b, PODArray<ResultType> & c)
|
2011-08-09 15:57:33 +00:00
|
|
|
|
{
|
|
|
|
|
size_t size = a.size();
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2014-09-08 10:46:30 +00:00
|
|
|
|
c[i] = Op::template apply<ResultType>(a[i], b);
|
2011-08-09 15:57:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-08 02:29:40 +00:00
|
|
|
|
static void constant_vector(A a, const PODArray<B> & b, PODArray<ResultType> & c)
|
2011-08-20 23:56:07 +00:00
|
|
|
|
{
|
|
|
|
|
size_t size = b.size();
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2014-09-08 10:46:30 +00:00
|
|
|
|
c[i] = Op::template apply<ResultType>(a, b[i]);
|
2011-08-20 23:56:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-08-09 15:57:33 +00:00
|
|
|
|
static void constant_constant(A a, B b, ResultType & c)
|
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
c = Op::template apply<ResultType>(a, b);
|
2011-08-09 15:57:33 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template<typename A, typename B, typename Op, typename ResultType = typename Op::ResultType>
|
|
|
|
|
struct BinaryOperationImpl : BinaryOperationImplBase<A, B, Op, ResultType>
|
2014-08-17 02:13:40 +00:00
|
|
|
|
{
|
|
|
|
|
};
|
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
|
2012-12-20 20:01:18 +00:00
|
|
|
|
template<typename A, typename Op>
|
|
|
|
|
struct UnaryOperationImpl
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
2012-12-20 20:01:18 +00:00
|
|
|
|
typedef typename Op::ResultType ResultType;
|
2011-08-09 17:24:17 +00:00
|
|
|
|
|
2013-12-08 02:29:40 +00:00
|
|
|
|
static void vector(const PODArray<A> & a, PODArray<ResultType> & c)
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
|
|
|
|
size_t size = a.size();
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
2012-12-20 20:01:18 +00:00
|
|
|
|
c[i] = Op::apply(a[i]);
|
2011-08-09 17:24:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-20 20:01:18 +00:00
|
|
|
|
static void constant(A a, ResultType & c)
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
2012-12-20 20:01:18 +00:00
|
|
|
|
c = Op::apply(a);
|
2011-08-09 17:24:17 +00:00
|
|
|
|
}
|
2012-12-20 20:01:18 +00:00
|
|
|
|
};
|
2011-08-09 17:24:17 +00:00
|
|
|
|
|
2011-08-20 23:56:07 +00:00
|
|
|
|
|
2012-12-20 20:01:18 +00:00
|
|
|
|
template<typename A, typename B>
|
|
|
|
|
struct PlusImpl
|
|
|
|
|
{
|
|
|
|
|
typedef typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type ResultType;
|
2014-06-26 00:58:14 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
2012-12-20 20:01:18 +00:00
|
|
|
|
/// Далее везде, static_cast - чтобы не было неправильного результата в выражениях вида Int64 c = UInt32(a) * Int32(-1).
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a) + b;
|
2011-08-09 17:24:17 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
|
2011-08-09 17:24:17 +00:00
|
|
|
|
template<typename A, typename B>
|
2012-12-20 20:01:18 +00:00
|
|
|
|
struct MultiplyImpl
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
2012-12-20 20:01:18 +00:00
|
|
|
|
typedef typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type ResultType;
|
2011-08-09 17:24:17 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a) * b;
|
2011-08-09 17:24:17 +00:00
|
|
|
|
}
|
2012-12-20 20:01:18 +00:00
|
|
|
|
};
|
2011-08-09 17:24:17 +00:00
|
|
|
|
|
2012-12-20 20:01:18 +00:00
|
|
|
|
template<typename A, typename B>
|
|
|
|
|
struct MinusImpl
|
|
|
|
|
{
|
|
|
|
|
typedef typename NumberTraits::ResultOfSubtraction<A, B>::Type ResultType;
|
2011-08-20 23:56:07 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a) - b;
|
2011-08-09 17:24:17 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename A, typename B>
|
|
|
|
|
struct DivideFloatingImpl
|
|
|
|
|
{
|
|
|
|
|
typedef typename NumberTraits::ResultOfFloatingPointDivision<A, B>::Type ResultType;
|
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a) / b;
|
2011-08-09 17:24:17 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2012-12-18 20:07:26 +00:00
|
|
|
|
|
2013-06-07 22:12:58 +00:00
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
|
|
|
|
|
2013-02-16 21:23:55 +00:00
|
|
|
|
template <typename A, typename B>
|
|
|
|
|
inline void throwIfDivisionLeadsToFPE(A a, B b)
|
2012-12-18 20:07:26 +00:00
|
|
|
|
{
|
2013-02-16 21:23:55 +00:00
|
|
|
|
/// Возможно, лучше вместо проверок использовать siglongjmp?
|
2014-06-26 00:58:14 +00:00
|
|
|
|
|
2013-02-16 21:23:55 +00:00
|
|
|
|
if (unlikely(b == 0))
|
|
|
|
|
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
|
|
|
|
|
|
|
|
|
|
/// http://avva.livejournal.com/2548306.html
|
2014-01-08 16:33:28 +00:00
|
|
|
|
if (unlikely(std::is_signed<A>::value && std::is_signed<B>::value && a == std::numeric_limits<A>::min() && b == -1))
|
2013-02-16 21:23:55 +00:00
|
|
|
|
throw Exception("Division of minimal signed number by minus one", ErrorCodes::ILLEGAL_DIVISION);
|
2013-06-07 22:12:58 +00:00
|
|
|
|
}
|
2013-06-07 17:32:12 +00:00
|
|
|
|
|
2014-11-21 14:28:39 +00:00
|
|
|
|
template <typename A, typename B>
|
|
|
|
|
inline bool divisionLeadsToFPE(A a, B b)
|
|
|
|
|
{
|
|
|
|
|
/// Возможно, лучше вместо проверок использовать siglongjmp?
|
|
|
|
|
|
|
|
|
|
if (unlikely(b == 0))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
/// http://avva.livejournal.com/2548306.html
|
|
|
|
|
if (unlikely(std::is_signed<A>::value && std::is_signed<B>::value && a == std::numeric_limits<A>::min() && b == -1))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-07 17:32:12 +00:00
|
|
|
|
#pragma GCC diagnostic pop
|
2013-06-07 22:12:58 +00:00
|
|
|
|
|
2012-12-18 20:07:26 +00:00
|
|
|
|
|
2011-08-09 17:24:17 +00:00
|
|
|
|
template<typename A, typename B>
|
|
|
|
|
struct DivideIntegralImpl
|
|
|
|
|
{
|
|
|
|
|
typedef typename NumberTraits::ResultOfIntegerDivision<A, B>::Type ResultType;
|
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
2013-02-16 21:23:55 +00:00
|
|
|
|
throwIfDivisionLeadsToFPE(a, b);
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a) / b;
|
2011-08-09 17:24:17 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-21 14:28:39 +00:00
|
|
|
|
template<typename A, typename B>
|
|
|
|
|
struct DivideIntegralOrZeroImpl
|
|
|
|
|
{
|
|
|
|
|
typedef typename NumberTraits::ResultOfIntegerDivision<A, B>::Type ResultType;
|
|
|
|
|
|
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
|
|
|
|
{
|
|
|
|
|
return unlikely(divisionLeadsToFPE(a, b)) ? 0 : static_cast<Result>(a) / b;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2011-08-09 17:24:17 +00:00
|
|
|
|
template<typename A, typename B>
|
|
|
|
|
struct ModuloImpl
|
|
|
|
|
{
|
|
|
|
|
typedef typename NumberTraits::ResultOfModulo<A, B>::Type ResultType;
|
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2011-08-09 17:24:17 +00:00
|
|
|
|
{
|
2013-02-16 21:23:55 +00:00
|
|
|
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<A>::Type(b));
|
2012-12-20 20:01:18 +00:00
|
|
|
|
return typename NumberTraits::ToInteger<A>::Type(a)
|
2011-08-21 03:41:37 +00:00
|
|
|
|
% typename NumberTraits::ToInteger<A>::Type(b);
|
2011-08-09 17:24:17 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2012-12-20 13:41:55 +00:00
|
|
|
|
template<typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
|
struct BitAndImpl
|
2012-12-20 13:41:55 +00:00
|
|
|
|
{
|
2013-12-17 16:06:52 +00:00
|
|
|
|
typedef typename NumberTraits::ResultOfBit<A, B>::Type ResultType;
|
2014-06-26 00:58:14 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2012-12-20 13:41:55 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a)
|
|
|
|
|
& static_cast<Result>(b);
|
2012-12-20 13:41:55 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
|
struct BitOrImpl
|
2012-12-20 13:41:55 +00:00
|
|
|
|
{
|
2013-12-17 16:06:52 +00:00
|
|
|
|
typedef typename NumberTraits::ResultOfBit<A, B>::Type ResultType;
|
2014-06-26 00:58:14 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2012-12-20 13:41:55 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a)
|
|
|
|
|
| static_cast<Result>(b);
|
2012-12-20 13:41:55 +00:00
|
|
|
|
}
|
2012-12-20 20:01:18 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
|
struct BitXorImpl
|
2012-12-20 20:01:18 +00:00
|
|
|
|
{
|
2013-12-17 16:06:52 +00:00
|
|
|
|
typedef typename NumberTraits::ResultOfBit<A, B>::Type ResultType;
|
2012-12-20 20:01:18 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2012-12-20 13:41:55 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a)
|
|
|
|
|
^ static_cast<Result>(b);
|
2012-12-20 13:41:55 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2013-12-17 13:49:21 +00:00
|
|
|
|
template<typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
|
struct BitShiftLeftImpl
|
2013-12-17 13:49:21 +00:00
|
|
|
|
{
|
2013-12-17 16:06:52 +00:00
|
|
|
|
typedef typename NumberTraits::ResultOfBit<A, B>::Type ResultType;
|
2013-12-17 13:49:21 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2013-12-17 13:49:21 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a)
|
|
|
|
|
<< static_cast<Result>(b);
|
2013-12-17 13:49:21 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<typename A, typename B>
|
2013-12-17 16:06:52 +00:00
|
|
|
|
struct BitShiftRightImpl
|
2013-12-17 13:49:21 +00:00
|
|
|
|
{
|
2013-12-17 16:06:52 +00:00
|
|
|
|
typedef typename NumberTraits::ResultOfBit<A, B>::Type ResultType;
|
2013-12-17 13:49:21 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename Result = ResultType>
|
|
|
|
|
static inline Result apply(A a, B b)
|
2013-12-17 13:49:21 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return static_cast<Result>(a)
|
|
|
|
|
>> static_cast<Result>(b);
|
2013-12-17 13:49:21 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2011-09-05 00:32:22 +00:00
|
|
|
|
template<typename A>
|
|
|
|
|
struct NegateImpl
|
|
|
|
|
{
|
|
|
|
|
typedef typename NumberTraits::ResultOfNegate<A>::Type ResultType;
|
|
|
|
|
|
2012-12-20 20:01:18 +00:00
|
|
|
|
static inline ResultType apply(A a)
|
2011-09-05 00:32:22 +00:00
|
|
|
|
{
|
2014-08-09 02:29:03 +00:00
|
|
|
|
return -static_cast<ResultType>(a);
|
2011-09-05 00:32:22 +00:00
|
|
|
|
}
|
2012-12-20 20:01:18 +00:00
|
|
|
|
};
|
2011-09-05 00:32:22 +00:00
|
|
|
|
|
2012-12-20 20:01:18 +00:00
|
|
|
|
template<typename A>
|
2013-12-17 16:06:52 +00:00
|
|
|
|
struct BitNotImpl
|
2012-12-20 20:01:18 +00:00
|
|
|
|
{
|
2013-12-17 16:06:52 +00:00
|
|
|
|
typedef typename NumberTraits::ResultOfBitNot<A>::Type ResultType;
|
2012-12-20 20:01:18 +00:00
|
|
|
|
|
|
|
|
|
static inline ResultType apply(A a)
|
2011-09-05 00:32:22 +00:00
|
|
|
|
{
|
2012-12-20 20:01:18 +00:00
|
|
|
|
return ~static_cast<ResultType>(a);
|
2011-09-05 00:32:22 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2011-08-09 15:57:33 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
/// this one is just for convenience
|
|
|
|
|
template <bool B, typename T1, typename T2> using If = typename std::conditional<B, T1, T2>::type;
|
|
|
|
|
/// these ones for better semantics
|
|
|
|
|
template <typename T> using Then = T;
|
|
|
|
|
template <typename T> using Else = T;
|
|
|
|
|
|
|
|
|
|
/// Used to indicate undefined operation
|
2014-09-08 12:22:36 +00:00
|
|
|
|
struct InvalidType;
|
2014-09-08 10:46:30 +00:00
|
|
|
|
|
|
|
|
|
template <typename DataType> struct IsIntegral { static constexpr auto value = false; };
|
|
|
|
|
template <> struct IsIntegral<DataTypeUInt8> { static constexpr auto value = true; };
|
|
|
|
|
template <> struct IsIntegral<DataTypeUInt16> { static constexpr auto value = true; };
|
|
|
|
|
template <> struct IsIntegral<DataTypeUInt32> { static constexpr auto value = true; };
|
|
|
|
|
template <> struct IsIntegral<DataTypeUInt64> { static constexpr auto value = true; };
|
|
|
|
|
template <> struct IsIntegral<DataTypeInt8> { static constexpr auto value = true; };
|
|
|
|
|
template <> struct IsIntegral<DataTypeInt16> { static constexpr auto value = true; };
|
|
|
|
|
template <> struct IsIntegral<DataTypeInt32> { static constexpr auto value = true; };
|
|
|
|
|
template <> struct IsIntegral<DataTypeInt64> { static constexpr auto value = true; };
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
template <typename DataType> struct IsFloating { static constexpr auto value = false; };
|
|
|
|
|
template <> struct IsFloating<DataTypeFloat32> { static constexpr auto value = true; };
|
|
|
|
|
template <> struct IsFloating<DataTypeFloat64> { static constexpr auto value = true; };
|
|
|
|
|
|
|
|
|
|
template <typename DataType> struct IsNumeric
|
|
|
|
|
{
|
|
|
|
|
static constexpr auto value = IsIntegral<DataType>::value || IsFloating<DataType>::value;
|
|
|
|
|
};
|
|
|
|
|
|
2014-09-17 20:18:52 +00:00
|
|
|
|
template <typename DataType> struct IsDateOrDateTime { static constexpr auto value = false; };
|
|
|
|
|
template <> struct IsDateOrDateTime<DataTypeDate> { static constexpr auto value = true; };
|
|
|
|
|
template <> struct IsDateOrDateTime<DataTypeDateTime> { static constexpr auto value = true; };
|
2014-09-08 10:46:30 +00:00
|
|
|
|
|
|
|
|
|
/** Returns appropriate result type for binary operator on dates:
|
|
|
|
|
* Date + Integral -> Date
|
|
|
|
|
* Integral + Date -> Date
|
2014-09-08 12:22:36 +00:00
|
|
|
|
* Date - Date -> Int32
|
2014-09-08 10:46:30 +00:00
|
|
|
|
* Date - Integral -> Date
|
|
|
|
|
* All other operations are not defined and return InvalidType, operations on
|
|
|
|
|
* distinct date types are also undefined (e.g. DataTypeDate - DataTypeDateTime) */
|
|
|
|
|
template <template <typename, typename> class Operation, typename LeftDataType, typename RightDataType>
|
|
|
|
|
struct DateBinaryOperationTraits
|
|
|
|
|
{
|
|
|
|
|
using T0 = typename LeftDataType::FieldType;
|
|
|
|
|
using T1 = typename RightDataType::FieldType;
|
|
|
|
|
using Op = Operation<T0, T1>;
|
|
|
|
|
|
|
|
|
|
using ResultDataType =
|
|
|
|
|
If<std::is_same<Op, PlusImpl<T0, T1>>::value,
|
|
|
|
|
Then<
|
2014-09-17 20:18:52 +00:00
|
|
|
|
If<IsDateOrDateTime<LeftDataType>::value && IsIntegral<RightDataType>::value,
|
2014-09-08 10:46:30 +00:00
|
|
|
|
Then<LeftDataType>,
|
|
|
|
|
Else<
|
2014-09-17 20:18:52 +00:00
|
|
|
|
If<IsIntegral<LeftDataType>::value && IsDateOrDateTime<RightDataType>::value,
|
2014-09-08 10:46:30 +00:00
|
|
|
|
Then<RightDataType>,
|
|
|
|
|
Else<InvalidType>
|
|
|
|
|
>
|
|
|
|
|
>
|
|
|
|
|
>
|
|
|
|
|
>,
|
|
|
|
|
Else<
|
|
|
|
|
If<std::is_same<Op, MinusImpl<T0, T1>>::value,
|
|
|
|
|
Then<
|
2014-09-17 20:18:52 +00:00
|
|
|
|
If<IsDateOrDateTime<LeftDataType>::value,
|
2014-09-08 10:46:30 +00:00
|
|
|
|
Then<
|
|
|
|
|
If<std::is_same<LeftDataType, RightDataType>::value,
|
|
|
|
|
Then<DataTypeInt32>,
|
|
|
|
|
Else<
|
|
|
|
|
If<IsIntegral<RightDataType>::value,
|
|
|
|
|
Then<LeftDataType>,
|
|
|
|
|
Else<InvalidType>
|
|
|
|
|
>
|
|
|
|
|
>
|
|
|
|
|
>
|
|
|
|
|
>,
|
|
|
|
|
Else<InvalidType>
|
|
|
|
|
>
|
|
|
|
|
>,
|
|
|
|
|
Else<InvalidType>
|
|
|
|
|
>
|
|
|
|
|
>
|
|
|
|
|
>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Decides among date and numeric operations
|
|
|
|
|
template <template <typename, typename> class Operation, typename LeftDataType, typename RightDataType>
|
|
|
|
|
struct BinaryOperationTraits
|
|
|
|
|
{
|
|
|
|
|
using ResultDataType =
|
2014-09-17 20:18:52 +00:00
|
|
|
|
If<IsDateOrDateTime<LeftDataType>::value || IsDateOrDateTime<RightDataType>::value,
|
2014-09-08 10:46:30 +00:00
|
|
|
|
Then<
|
|
|
|
|
typename DateBinaryOperationTraits<
|
|
|
|
|
Operation, LeftDataType, RightDataType
|
|
|
|
|
>::ResultDataType
|
|
|
|
|
>,
|
|
|
|
|
Else<
|
|
|
|
|
typename DataTypeFromFieldType<
|
|
|
|
|
typename Operation<
|
|
|
|
|
typename LeftDataType::FieldType,
|
|
|
|
|
typename RightDataType::FieldType
|
|
|
|
|
>::ResultType
|
|
|
|
|
>::Type
|
|
|
|
|
>
|
|
|
|
|
>;
|
|
|
|
|
};
|
|
|
|
|
|
2012-12-20 20:01:18 +00:00
|
|
|
|
|
|
|
|
|
template <template <typename, typename> class Op, typename Name>
|
2011-08-09 15:57:33 +00:00
|
|
|
|
class FunctionBinaryArithmetic : public IFunction
|
|
|
|
|
{
|
2014-11-12 17:23:26 +00:00
|
|
|
|
public:
|
|
|
|
|
static constexpr auto name = Name::name;
|
|
|
|
|
static IFunction * create(const Context & context) { return new FunctionBinaryArithmetic; }
|
|
|
|
|
|
2011-08-12 20:39:42 +00:00
|
|
|
|
private:
|
2014-09-08 12:22:36 +00:00
|
|
|
|
/// Overload for InvalidType
|
|
|
|
|
template <typename ResultDataType,
|
|
|
|
|
typename std::enable_if<std::is_same<ResultDataType, InvalidType>::value>::type * = nullptr>
|
|
|
|
|
bool checkRightTypeImpl(DataTypePtr & type_res) const
|
2014-09-08 10:46:30 +00:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
/// Overload for well-defined operations
|
|
|
|
|
template <typename ResultDataType,
|
|
|
|
|
typename std::enable_if<!std::is_same<ResultDataType, InvalidType>::value>::type * = nullptr>
|
|
|
|
|
bool checkRightTypeImpl(DataTypePtr & type_res) const
|
2014-09-08 10:46:30 +00:00
|
|
|
|
{
|
|
|
|
|
type_res = new ResultDataType;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename LeftDataType, typename RightDataType>
|
2011-09-24 20:32:41 +00:00
|
|
|
|
bool checkRightType(const DataTypes & arguments, DataTypePtr & type_res) const
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
|
|
|
|
|
|
|
|
|
if (typeid_cast<const RightDataType *>(&*arguments[1]))
|
2014-09-08 12:22:36 +00:00
|
|
|
|
return checkRightTypeImpl<ResultDataType>(type_res);
|
2014-09-08 10:46:30 +00:00
|
|
|
|
|
2011-08-12 20:39:42 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T0>
|
2011-09-24 20:32:41 +00:00
|
|
|
|
bool checkLeftType(const DataTypes & arguments, DataTypePtr & type_res) const
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
2014-06-26 00:58:14 +00:00
|
|
|
|
if (typeid_cast<const T0 *>(&*arguments[0]))
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
if ( checkRightType<T0, DataTypeDate>(arguments, type_res)
|
|
|
|
|
|| checkRightType<T0, DataTypeDateTime>(arguments, type_res)
|
|
|
|
|
|| checkRightType<T0, DataTypeUInt8>(arguments, type_res)
|
2011-09-24 20:32:41 +00:00
|
|
|
|
|| checkRightType<T0, DataTypeUInt16>(arguments, type_res)
|
|
|
|
|
|| checkRightType<T0, DataTypeUInt32>(arguments, type_res)
|
|
|
|
|
|| checkRightType<T0, DataTypeUInt64>(arguments, type_res)
|
|
|
|
|
|| checkRightType<T0, DataTypeInt8>(arguments, type_res)
|
|
|
|
|
|| checkRightType<T0, DataTypeInt16>(arguments, type_res)
|
|
|
|
|
|| checkRightType<T0, DataTypeInt32>(arguments, type_res)
|
|
|
|
|
|| checkRightType<T0, DataTypeInt64>(arguments, type_res)
|
|
|
|
|
|| checkRightType<T0, DataTypeFloat32>(arguments, type_res)
|
2012-12-14 20:24:26 +00:00
|
|
|
|
|| checkRightType<T0, DataTypeFloat64>(arguments, type_res))
|
2011-08-12 20:39:42 +00:00
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
throw Exception("Illegal type " + arguments[1]->getName() + " of second argument of function " + getName(),
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
/// Overload for date operations
|
|
|
|
|
template <typename LeftDataType, typename RightDataType, typename ColumnType,
|
2014-09-17 20:18:52 +00:00
|
|
|
|
typename std::enable_if<IsDateOrDateTime<LeftDataType>::value || IsDateOrDateTime<RightDataType>::value>::type * = nullptr>
|
2014-09-08 12:22:36 +00:00
|
|
|
|
bool executeRightType(Block & block, const ColumnNumbers & arguments, const size_t result, const ColumnType * col_left)
|
|
|
|
|
{
|
|
|
|
|
if (!typeid_cast<const RightDataType *>(block.getByPosition(arguments[1]).type.get()))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
using ResultDataType = typename DateBinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
|
|
|
|
|
|
|
|
|
return executeRightTypeDispatch<LeftDataType, RightDataType, ResultDataType>(
|
|
|
|
|
block, arguments, result, col_left);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Overload for numeric operations
|
|
|
|
|
template <typename LeftDataType, typename RightDataType, typename ColumnType,
|
|
|
|
|
typename T0 = typename LeftDataType::FieldType, typename T1 = typename RightDataType::FieldType,
|
|
|
|
|
typename std::enable_if<IsNumeric<LeftDataType>::value && IsNumeric<RightDataType>::value>::type * = nullptr>
|
|
|
|
|
bool executeRightType(Block & block, const ColumnNumbers & arguments, const size_t result, const ColumnType * col_left)
|
|
|
|
|
{
|
|
|
|
|
return executeRightTypeImpl<T0, T1>(block, arguments, result, col_left);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Overload for InvalidType
|
|
|
|
|
template <typename LeftDataType, typename RightDataType, typename ResultDataType, typename ColumnType,
|
|
|
|
|
typename std::enable_if<std::is_same<ResultDataType, InvalidType>::value>::type * = nullptr>
|
|
|
|
|
bool executeRightTypeDispatch(Block & block, const ColumnNumbers & arguments, const size_t result,
|
|
|
|
|
const ColumnType * col_left)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Overload for well-defined operations
|
|
|
|
|
template <typename LeftDataType, typename RightDataType, typename ResultDataType, typename ColumnType,
|
|
|
|
|
typename std::enable_if<!std::is_same<ResultDataType, InvalidType>::value>::type * = nullptr>
|
|
|
|
|
bool executeRightTypeDispatch(Block & block, const ColumnNumbers & arguments, const size_t result,
|
|
|
|
|
const ColumnType * col_left)
|
|
|
|
|
{
|
|
|
|
|
using T0 = typename LeftDataType::FieldType;
|
|
|
|
|
using T1 = typename RightDataType::FieldType;
|
|
|
|
|
using ResultType = typename ResultDataType::FieldType;
|
|
|
|
|
|
|
|
|
|
return executeRightTypeImpl<T0, T1, ResultType>(block, arguments, result, col_left);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// ColumnVector overload
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename T0, typename T1, typename ResultType = typename Op<T0, T1>::ResultType>
|
2014-09-08 12:22:36 +00:00
|
|
|
|
bool executeRightTypeImpl(Block & block, const ColumnNumbers & arguments, size_t result, const ColumnVector<T0> * col_left)
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
if (auto col_right = typeid_cast<ColumnVector<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
auto col_res = new ColumnVector<ResultType>;
|
2011-09-24 20:32:41 +00:00
|
|
|
|
block.getByPosition(result).column = col_res;
|
2011-08-12 20:39:42 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
auto & vec_res = col_res->getData();
|
2011-08-12 20:39:42 +00:00
|
|
|
|
vec_res.resize(col_left->getData().size());
|
2014-09-08 10:46:30 +00:00
|
|
|
|
BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>::vector_vector(col_left->getData(), col_right->getData(), vec_res);
|
2011-08-12 20:39:42 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2014-09-08 10:46:30 +00:00
|
|
|
|
else if (auto col_right = typeid_cast<ColumnConst<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
auto col_res = new ColumnVector<ResultType>;
|
2011-09-24 20:32:41 +00:00
|
|
|
|
block.getByPosition(result).column = col_res;
|
2011-08-12 20:39:42 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
auto & vec_res = col_res->getData();
|
2011-08-12 20:39:42 +00:00
|
|
|
|
vec_res.resize(col_left->getData().size());
|
2014-09-08 10:46:30 +00:00
|
|
|
|
BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>::vector_constant(col_left->getData(), col_right->getData(), vec_res);
|
2011-08-12 20:39:42 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2014-06-26 00:58:14 +00:00
|
|
|
|
|
2011-08-12 20:39:42 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
/// ColumnConst overload
|
2014-09-08 10:46:30 +00:00
|
|
|
|
template <typename T0, typename T1, typename ResultType = typename Op<T0, T1>::ResultType>
|
2014-09-08 12:22:36 +00:00
|
|
|
|
bool executeRightTypeImpl(Block & block, const ColumnNumbers & arguments, size_t result, const ColumnConst<T0> * col_left)
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
if (auto col_right = typeid_cast<ColumnVector<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
2014-09-08 10:46:30 +00:00
|
|
|
|
auto col_res = new ColumnVector<ResultType>;
|
2011-09-24 20:32:41 +00:00
|
|
|
|
block.getByPosition(result).column = col_res;
|
2011-08-12 20:39:42 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
auto & vec_res = col_res->getData();
|
2011-08-12 20:39:42 +00:00
|
|
|
|
vec_res.resize(col_left->size());
|
2014-09-08 10:46:30 +00:00
|
|
|
|
BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>::constant_vector(col_left->getData(), col_right->getData(), vec_res);
|
2011-08-12 20:39:42 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2014-09-08 10:46:30 +00:00
|
|
|
|
else if (auto col_right = typeid_cast<ColumnConst<T1> *>(block.getByPosition(arguments[1]).column.get()))
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
|
|
|
|
ResultType res = 0;
|
2014-09-08 10:46:30 +00:00
|
|
|
|
BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>::constant_constant(col_left->getData(), col_right->getData(), res);
|
2014-06-26 00:58:14 +00:00
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
auto col_res = new ColumnConst<ResultType>(col_left->size(), res);
|
2011-09-24 20:32:41 +00:00
|
|
|
|
block.getByPosition(result).column = col_res;
|
2011-08-12 20:39:42 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
template <typename LeftDataType,
|
2014-09-17 20:18:52 +00:00
|
|
|
|
typename std::enable_if<IsDateOrDateTime<LeftDataType>::value>::type * = nullptr>
|
2014-09-08 12:22:36 +00:00
|
|
|
|
bool executeLeftType(Block & block, const ColumnNumbers & arguments, const size_t result)
|
2011-08-12 20:39:42 +00:00
|
|
|
|
{
|
2014-09-08 12:22:36 +00:00
|
|
|
|
if (!typeid_cast<const LeftDataType *>(block.getByPosition(arguments[0]).type.get()))
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
return executeLeftTypeDispatch<LeftDataType>(block, arguments, result);
|
2014-09-08 10:46:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
template <typename LeftDataType,
|
|
|
|
|
typename std::enable_if<IsNumeric<LeftDataType>::value>::type * = nullptr>
|
|
|
|
|
bool executeLeftType(Block & block, const ColumnNumbers & arguments, const size_t result)
|
2014-09-08 10:46:30 +00:00
|
|
|
|
{
|
2014-09-08 12:22:36 +00:00
|
|
|
|
return executeLeftTypeDispatch<LeftDataType>(block, arguments, result);
|
2014-09-08 10:46:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
template <typename LeftDataType>
|
|
|
|
|
bool executeLeftTypeDispatch(Block & block, const ColumnNumbers & arguments, const size_t result)
|
2014-09-08 10:46:30 +00:00
|
|
|
|
{
|
|
|
|
|
using T0 = typename LeftDataType::FieldType;
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
if ( executeLeftTypeImpl<LeftDataType, ColumnVector<T0>>(block, arguments, result)
|
|
|
|
|
|| executeLeftTypeImpl<LeftDataType, ColumnConst<T0>>(block, arguments, result))
|
|
|
|
|
return true;
|
2014-09-08 10:46:30 +00:00
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
return false;
|
2014-09-08 10:46:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 12:22:36 +00:00
|
|
|
|
template <typename LeftDataType, typename ColumnType>
|
|
|
|
|
bool executeLeftTypeImpl(Block & block, const ColumnNumbers & arguments, const size_t result)
|
2014-09-08 10:46:30 +00:00
|
|
|
|
{
|
2014-09-08 12:22:36 +00:00
|
|
|
|
if (auto col_left = typeid_cast<ColumnType *>(block.getByPosition(arguments[0]).column.get()))
|
2014-09-08 10:46:30 +00:00
|
|
|
|
{
|
2014-09-08 12:22:36 +00:00
|
|
|
|
if ( executeRightType<LeftDataType, DataTypeDate>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeDateTime>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeUInt8>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeUInt16>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeUInt32>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeUInt64>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeInt8>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeInt16>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeInt32>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeInt64>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeFloat32>(block, arguments, result, col_left)
|
|
|
|
|
|| executeRightType<LeftDataType, DataTypeFloat64>(block, arguments, result, col_left))
|
2014-09-08 10:46:30 +00:00
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName()
|
|
|
|
|
+ " of second argument of function " + getName(),
|
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-09 15:57:33 +00:00
|
|
|
|
public:
|
2011-08-20 23:56:07 +00:00
|
|
|
|
/// Получить имя функции.
|
2011-08-13 21:05:18 +00:00
|
|
|
|
String getName() const
|
2011-08-09 15:57:33 +00:00
|
|
|
|
{
|
2014-11-12 17:23:26 +00:00
|
|
|
|
return name;
|
2011-08-09 15:57:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Получить типы результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение.
|
2011-09-24 20:32:41 +00:00
|
|
|
|
DataTypePtr getReturnType(const DataTypes & arguments) const
|
2011-08-09 15:57:33 +00:00
|
|
|
|
{
|
|
|
|
|
if (arguments.size() != 2)
|
|
|
|
|
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
2013-06-21 20:34:19 +00:00
|
|
|
|
+ toString(arguments.size()) + ", should be 2.",
|
2011-08-09 15:57:33 +00:00
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
|
|
|
|
|
2011-09-24 20:32:41 +00:00
|
|
|
|
DataTypePtr type_res;
|
|
|
|
|
|
2014-09-08 10:46:30 +00:00
|
|
|
|
if (!( checkLeftType<DataTypeDate>(arguments, type_res)
|
|
|
|
|
|| checkLeftType<DataTypeDateTime>(arguments, type_res)
|
|
|
|
|
|| checkLeftType<DataTypeUInt8>(arguments, type_res)
|
2011-09-24 20:32:41 +00:00
|
|
|
|
|| checkLeftType<DataTypeUInt16>(arguments, type_res)
|
|
|
|
|
|| checkLeftType<DataTypeUInt32>(arguments, type_res)
|
|
|
|
|
|| checkLeftType<DataTypeUInt64>(arguments, type_res)
|
|
|
|
|
|| checkLeftType<DataTypeInt8>(arguments, type_res)
|
|
|
|
|
|| checkLeftType<DataTypeInt16>(arguments, type_res)
|
|
|
|
|
|| checkLeftType<DataTypeInt32>(arguments, type_res)
|
|
|
|
|
|| checkLeftType<DataTypeInt64>(arguments, type_res)
|
|
|
|
|
|| checkLeftType<DataTypeFloat32>(arguments, type_res)
|
2012-12-14 20:24:26 +00:00
|
|
|
|
|| checkLeftType<DataTypeFloat64>(arguments, type_res)))
|
2011-08-09 15:57:33 +00:00
|
|
|
|
throw Exception("Illegal type " + arguments[0]->getName() + " of first argument of function " + getName(),
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
|
2011-09-24 20:32:41 +00:00
|
|
|
|
return type_res;
|
2011-08-09 15:57:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Выполнить функцию над блоком.
|
2011-09-24 20:32:41 +00:00
|
|
|
|
void execute(Block & block, const ColumnNumbers & arguments, size_t result)
|
2011-08-09 15:57:33 +00:00
|
|
|
|
{
|
2014-09-08 12:22:36 +00:00
|
|
|
|
if (!( executeLeftType<DataTypeDate>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeDateTime>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeUInt8>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeUInt16>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeUInt32>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeUInt64>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeInt8>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeInt16>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeInt32>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeInt64>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeFloat32>(block, arguments, result)
|
|
|
|
|
|| executeLeftType<DataTypeFloat64>(block, arguments, result)))
|
2011-08-28 00:31:30 +00:00
|
|
|
|
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
|
|
|
|
|
+ " of first argument of function " + getName(),
|
2011-08-09 15:57:33 +00:00
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2012-12-20 20:01:18 +00:00
|
|
|
|
template <template <typename> class Op, typename Name>
|
2011-09-05 00:32:22 +00:00
|
|
|
|
class FunctionUnaryArithmetic : public IFunction
|
|
|
|
|
{
|
2014-11-12 17:23:26 +00:00
|
|
|
|
public:
|
|
|
|
|
static constexpr auto name = Name::name;
|
|
|
|
|
static IFunction * create(const Context & context) { return new FunctionUnaryArithmetic; }
|
|
|
|
|
|
2011-09-05 00:32:22 +00:00
|
|
|
|
private:
|
|
|
|
|
template <typename T0>
|
2011-09-25 05:29:13 +00:00
|
|
|
|
bool checkType(const DataTypes & arguments, DataTypePtr & result) const
|
2011-09-05 00:32:22 +00:00
|
|
|
|
{
|
2014-06-26 00:58:14 +00:00
|
|
|
|
if (typeid_cast<const T0 *>(&*arguments[0]))
|
2011-09-05 00:32:22 +00:00
|
|
|
|
{
|
2011-09-24 20:32:41 +00:00
|
|
|
|
result = new typename DataTypeFromFieldType<
|
2012-12-20 20:01:18 +00:00
|
|
|
|
typename Op<typename T0::FieldType>::ResultType>::Type;
|
2011-09-05 00:32:22 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename T0>
|
2011-09-24 20:32:41 +00:00
|
|
|
|
bool executeType(Block & block, const ColumnNumbers & arguments, size_t result)
|
2011-09-05 00:32:22 +00:00
|
|
|
|
{
|
2014-06-26 00:58:14 +00:00
|
|
|
|
if (ColumnVector<T0> * col = typeid_cast<ColumnVector<T0> *>(&*block.getByPosition(arguments[0]).column))
|
2011-09-05 00:32:22 +00:00
|
|
|
|
{
|
2012-12-20 20:01:18 +00:00
|
|
|
|
typedef typename Op<T0>::ResultType ResultType;
|
2011-09-05 00:32:22 +00:00
|
|
|
|
|
|
|
|
|
ColumnVector<ResultType> * col_res = new ColumnVector<ResultType>;
|
2011-09-24 20:32:41 +00:00
|
|
|
|
block.getByPosition(result).column = col_res;
|
2011-09-05 00:32:22 +00:00
|
|
|
|
|
|
|
|
|
typename ColumnVector<ResultType>::Container_t & vec_res = col_res->getData();
|
|
|
|
|
vec_res.resize(col->getData().size());
|
2012-12-20 20:01:18 +00:00
|
|
|
|
UnaryOperationImpl<T0, Op<T0> >::vector(col->getData(), vec_res);
|
2011-09-05 00:32:22 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2014-06-26 00:58:14 +00:00
|
|
|
|
else if (ColumnConst<T0> * col = typeid_cast<ColumnConst<T0> *>(&*block.getByPosition(arguments[0]).column))
|
2011-09-05 00:32:22 +00:00
|
|
|
|
{
|
2012-12-20 20:01:18 +00:00
|
|
|
|
typedef typename Op<T0>::ResultType ResultType;
|
2011-09-05 00:32:22 +00:00
|
|
|
|
|
|
|
|
|
ResultType res = 0;
|
2012-12-20 20:01:18 +00:00
|
|
|
|
UnaryOperationImpl<T0, Op<T0> >::constant(col->getData(), res);
|
2011-09-05 00:32:22 +00:00
|
|
|
|
|
|
|
|
|
ColumnConst<ResultType> * col_res = new ColumnConst<ResultType>(col->size(), res);
|
2011-09-24 20:32:41 +00:00
|
|
|
|
block.getByPosition(result).column = col_res;
|
2011-09-05 00:32:22 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
/// Получить имя функции.
|
|
|
|
|
String getName() const
|
|
|
|
|
{
|
2014-11-12 17:23:26 +00:00
|
|
|
|
return name;
|
2011-09-05 00:32:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Получить типы результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение.
|
2011-09-24 20:32:41 +00:00
|
|
|
|
DataTypePtr getReturnType(const DataTypes & arguments) const
|
2011-09-05 00:32:22 +00:00
|
|
|
|
{
|
|
|
|
|
if (arguments.size() != 1)
|
|
|
|
|
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
2013-06-21 20:34:19 +00:00
|
|
|
|
+ toString(arguments.size()) + ", should be 1.",
|
2011-09-05 00:32:22 +00:00
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
|
|
|
|
|
2011-09-24 20:32:41 +00:00
|
|
|
|
DataTypePtr result;
|
|
|
|
|
|
|
|
|
|
if (!( checkType<DataTypeUInt8>(arguments, result)
|
|
|
|
|
|| checkType<DataTypeUInt16>(arguments, result)
|
|
|
|
|
|| checkType<DataTypeUInt32>(arguments, result)
|
|
|
|
|
|| checkType<DataTypeUInt64>(arguments, result)
|
|
|
|
|
|| checkType<DataTypeInt8>(arguments, result)
|
|
|
|
|
|| checkType<DataTypeInt16>(arguments, result)
|
|
|
|
|
|| checkType<DataTypeInt32>(arguments, result)
|
|
|
|
|
|| checkType<DataTypeInt64>(arguments, result)
|
|
|
|
|
|| checkType<DataTypeFloat32>(arguments, result)
|
2012-12-14 20:24:26 +00:00
|
|
|
|
|| checkType<DataTypeFloat64>(arguments, result)))
|
2011-09-05 00:32:22 +00:00
|
|
|
|
throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
|
2011-09-24 20:32:41 +00:00
|
|
|
|
return result;
|
2011-09-05 00:32:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Выполнить функцию над блоком.
|
2011-09-24 20:32:41 +00:00
|
|
|
|
void execute(Block & block, const ColumnNumbers & arguments, size_t result)
|
2011-09-05 00:32:22 +00:00
|
|
|
|
{
|
|
|
|
|
if (!( executeType<UInt8>(block, arguments, result)
|
|
|
|
|
|| executeType<UInt16>(block, arguments, result)
|
|
|
|
|
|| executeType<UInt32>(block, arguments, result)
|
|
|
|
|
|| executeType<UInt64>(block, arguments, result)
|
|
|
|
|
|| executeType<Int8>(block, arguments, result)
|
|
|
|
|
|| executeType<Int16>(block, arguments, result)
|
|
|
|
|
|| executeType<Int32>(block, arguments, result)
|
|
|
|
|
|| executeType<Int64>(block, arguments, result)
|
|
|
|
|
|| executeType<Float32>(block, arguments, result)
|
|
|
|
|
|| executeType<Float64>(block, arguments, result)))
|
|
|
|
|
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
|
|
|
|
|
+ " of argument of function " + getName(),
|
|
|
|
|
ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2014-11-12 17:23:26 +00:00
|
|
|
|
struct NamePlus { static constexpr auto name = "plus"; };
|
|
|
|
|
struct NameMinus { static constexpr auto name = "minus"; };
|
|
|
|
|
struct NameMultiply { static constexpr auto name = "multiply"; };
|
|
|
|
|
struct NameDivideFloating { static constexpr auto name = "divide"; };
|
|
|
|
|
struct NameDivideIntegral { static constexpr auto name = "intDiv"; };
|
2014-11-21 14:28:39 +00:00
|
|
|
|
struct NameDivideIntegralOrZero { static constexpr auto name = "intDivOrZero"; };
|
2014-11-12 17:23:26 +00:00
|
|
|
|
struct NameModulo { static constexpr auto name = "modulo"; };
|
|
|
|
|
struct NameNegate { static constexpr auto name = "negate"; };
|
|
|
|
|
struct NameBitAnd { static constexpr auto name = "bitAnd"; };
|
|
|
|
|
struct NameBitOr { static constexpr auto name = "bitOr"; };
|
|
|
|
|
struct NameBitXor { static constexpr auto name = "bitXor"; };
|
|
|
|
|
struct NameBitNot { static constexpr auto name = "bitNot"; };
|
|
|
|
|
struct NameBitShiftLeft { static constexpr auto name = "bitShiftLeft"; };
|
|
|
|
|
struct NameBitShiftRight { static constexpr auto name = "bitShiftRight"; };
|
2013-12-17 13:49:21 +00:00
|
|
|
|
|
|
|
|
|
typedef FunctionBinaryArithmetic<PlusImpl, NamePlus> FunctionPlus;
|
|
|
|
|
typedef FunctionBinaryArithmetic<MinusImpl, NameMinus> FunctionMinus;
|
|
|
|
|
typedef FunctionBinaryArithmetic<MultiplyImpl, NameMultiply> FunctionMultiply;
|
|
|
|
|
typedef FunctionBinaryArithmetic<DivideFloatingImpl, NameDivideFloating> FunctionDivideFloating;
|
|
|
|
|
typedef FunctionBinaryArithmetic<DivideIntegralImpl, NameDivideIntegral> FunctionDivideIntegral;
|
2014-11-21 14:28:39 +00:00
|
|
|
|
typedef FunctionBinaryArithmetic<DivideIntegralOrZeroImpl, NameDivideIntegralOrZero> FunctionDivideIntegralOrZero;
|
2013-12-17 13:49:21 +00:00
|
|
|
|
typedef FunctionBinaryArithmetic<ModuloImpl, NameModulo> FunctionModulo;
|
|
|
|
|
typedef FunctionUnaryArithmetic<NegateImpl, NameNegate> FunctionNegate;
|
2013-12-17 16:06:52 +00:00
|
|
|
|
typedef FunctionBinaryArithmetic<BitAndImpl, NameBitAnd> FunctionBitAnd;
|
|
|
|
|
typedef FunctionBinaryArithmetic<BitOrImpl, NameBitOr> FunctionBitOr;
|
|
|
|
|
typedef FunctionBinaryArithmetic<BitXorImpl, NameBitXor> FunctionBitXor;
|
|
|
|
|
typedef FunctionUnaryArithmetic<BitNotImpl, NameBitNot> FunctionBitNot;
|
|
|
|
|
typedef FunctionBinaryArithmetic<BitShiftLeftImpl, NameBitShiftLeft> FunctionBitShiftLeft;
|
|
|
|
|
typedef FunctionBinaryArithmetic<BitShiftRightImpl, NameBitShiftRight> FunctionBitShiftRight;
|
2012-12-20 20:01:18 +00:00
|
|
|
|
|
2011-08-09 15:57:33 +00:00
|
|
|
|
|
|
|
|
|
|
2014-08-17 02:13:40 +00:00
|
|
|
|
/// Оптимизации для целочисленного деления на константу.
|
|
|
|
|
|
|
|
|
|
#define LIBDIVIDE_USE_SSE2 1
|
|
|
|
|
#include <libdivide.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename A, typename B>
|
|
|
|
|
struct DivideIntegralByConstantImpl
|
|
|
|
|
: BinaryOperationImplBase<A, B, DivideIntegralImpl<A, B>>
|
|
|
|
|
{
|
|
|
|
|
typedef typename DivideIntegralImpl<A, B>::ResultType ResultType;
|
|
|
|
|
|
|
|
|
|
static void vector_constant(const PODArray<A> & a, B b, PODArray<ResultType> & c)
|
|
|
|
|
{
|
|
|
|
|
if (unlikely(b == 0))
|
|
|
|
|
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
|
|
|
|
|
|
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
|
|
|
|
|
|
|
|
|
if (unlikely(std::is_signed<B>::value && b == -1))
|
|
|
|
|
{
|
|
|
|
|
size_t size = a.size();
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
|
c[i] = -c[i];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
|
|
|
|
|
libdivide::divider<A> divider(b);
|
|
|
|
|
|
|
|
|
|
size_t size = a.size();
|
|
|
|
|
const A * a_pos = &a[0];
|
|
|
|
|
const A * a_end = a_pos + size;
|
|
|
|
|
ResultType * c_pos = &c[0];
|
|
|
|
|
static constexpr size_t values_per_sse_register = 16 / sizeof(A);
|
|
|
|
|
const A * a_end_sse = a_pos + size / values_per_sse_register * values_per_sse_register;
|
|
|
|
|
|
|
|
|
|
while (a_pos < a_end_sse)
|
|
|
|
|
{
|
|
|
|
|
_mm_storeu_si128(reinterpret_cast<__m128i *>(c_pos),
|
|
|
|
|
_mm_loadu_si128(reinterpret_cast<const __m128i *>(a_pos)) / divider);
|
|
|
|
|
|
|
|
|
|
a_pos += values_per_sse_register;
|
|
|
|
|
c_pos += values_per_sse_register;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (a_pos < a_end)
|
|
|
|
|
{
|
|
|
|
|
*c_pos = *a_pos / divider;
|
|
|
|
|
++a_pos;
|
|
|
|
|
++c_pos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename A, typename B>
|
|
|
|
|
struct ModuloByConstantImpl
|
|
|
|
|
: BinaryOperationImplBase<A, B, ModuloImpl<A, B>>
|
|
|
|
|
{
|
|
|
|
|
typedef typename ModuloImpl<A, B>::ResultType ResultType;
|
|
|
|
|
|
|
|
|
|
static void vector_constant(const PODArray<A> & a, B b, PODArray<ResultType> & c)
|
|
|
|
|
{
|
|
|
|
|
if (unlikely(b == 0))
|
|
|
|
|
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
|
|
|
|
|
|
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
|
|
|
|
|
|
|
|
|
if (unlikely((std::is_signed<B>::value && b == -1) || b == 1))
|
|
|
|
|
{
|
|
|
|
|
size_t size = a.size();
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
|
c[i] = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
|
|
|
|
|
libdivide::divider<A> divider(b);
|
|
|
|
|
|
|
|
|
|
/// Тут не удалось сделать так, чтобы SSE вариант из libdivide давал преимущество.
|
|
|
|
|
size_t size = a.size();
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
|
c[i] = a[i] - (a[i] / divider) * b; /// NOTE: возможно, не сохраняется семантика деления с остатком отрицательных чисел.
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Прописаны специализации для деления чисел типа UInt64 и UInt32 на числа той же знаковости.
|
|
|
|
|
* Можно дополнить до всех возможных комбинаций, но потребуется больше кода.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt8, DivideIntegralImpl<UInt64, UInt8>> : DivideIntegralByConstantImpl<UInt64, UInt8> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt16, DivideIntegralImpl<UInt64, UInt16>> : DivideIntegralByConstantImpl<UInt64, UInt16> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt32, DivideIntegralImpl<UInt64, UInt32>> : DivideIntegralByConstantImpl<UInt64, UInt32> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt64, DivideIntegralImpl<UInt64, UInt64>> : DivideIntegralByConstantImpl<UInt64, UInt64> {};
|
|
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt8, DivideIntegralImpl<UInt32, UInt8>> : DivideIntegralByConstantImpl<UInt32, UInt8> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt16, DivideIntegralImpl<UInt32, UInt16>> : DivideIntegralByConstantImpl<UInt32, UInt16> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt32, DivideIntegralImpl<UInt32, UInt32>> : DivideIntegralByConstantImpl<UInt32, UInt32> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt64, DivideIntegralImpl<UInt32, UInt64>> : DivideIntegralByConstantImpl<UInt32, UInt64> {};
|
|
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int8, DivideIntegralImpl<Int64, Int8>> : DivideIntegralByConstantImpl<Int64, Int8> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int16, DivideIntegralImpl<Int64, Int16>> : DivideIntegralByConstantImpl<Int64, Int16> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int32, DivideIntegralImpl<Int64, Int32>> : DivideIntegralByConstantImpl<Int64, Int32> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int64, DivideIntegralImpl<Int64, Int64>> : DivideIntegralByConstantImpl<Int64, Int64> {};
|
|
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int8, DivideIntegralImpl<Int32, Int8>> : DivideIntegralByConstantImpl<Int32, Int8> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int16, DivideIntegralImpl<Int32, Int16>> : DivideIntegralByConstantImpl<Int32, Int16> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int32, DivideIntegralImpl<Int32, Int32>> : DivideIntegralByConstantImpl<Int32, Int32> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int64, DivideIntegralImpl<Int32, Int64>> : DivideIntegralByConstantImpl<Int32, Int64> {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt8, ModuloImpl<UInt64, UInt8>> : ModuloByConstantImpl<UInt64, UInt8> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt16, ModuloImpl<UInt64, UInt16>> : ModuloByConstantImpl<UInt64, UInt16> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt32, ModuloImpl<UInt64, UInt32>> : ModuloByConstantImpl<UInt64, UInt32> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt64, UInt64, ModuloImpl<UInt64, UInt64>> : ModuloByConstantImpl<UInt64, UInt64> {};
|
|
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt8, ModuloImpl<UInt32, UInt8>> : ModuloByConstantImpl<UInt32, UInt8> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt16, ModuloImpl<UInt32, UInt16>> : ModuloByConstantImpl<UInt32, UInt16> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt32, ModuloImpl<UInt32, UInt32>> : ModuloByConstantImpl<UInt32, UInt32> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<UInt32, UInt64, ModuloImpl<UInt32, UInt64>> : ModuloByConstantImpl<UInt32, UInt64> {};
|
|
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int8, ModuloImpl<Int64, Int8>> : ModuloByConstantImpl<Int64, Int8> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int16, ModuloImpl<Int64, Int16>> : ModuloByConstantImpl<Int64, Int16> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int32, ModuloImpl<Int64, Int32>> : ModuloByConstantImpl<Int64, Int32> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int64, Int64, ModuloImpl<Int64, Int64>> : ModuloByConstantImpl<Int64, Int64> {};
|
|
|
|
|
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int8, ModuloImpl<Int32, Int8>> : ModuloByConstantImpl<Int32, Int8> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int16, ModuloImpl<Int32, Int16>> : ModuloByConstantImpl<Int32, Int16> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int32, ModuloImpl<Int32, Int32>> : ModuloByConstantImpl<Int32, Int32> {};
|
|
|
|
|
template <> struct BinaryOperationImpl<Int32, Int64, ModuloImpl<Int32, Int64>> : ModuloByConstantImpl<Int32, Int64> {};
|
|
|
|
|
|
2011-08-09 15:57:33 +00:00
|
|
|
|
}
|