ClickHouse/dbms/include/DB/Functions/FunctionsArithmetic.h

536 lines
18 KiB
C
Raw Normal View History

2011-08-09 15:57:33 +00:00
#pragma once
#include <Poco/NumberFormatter.h>
#include <DB/DataTypes/DataTypesNumberFixed.h>
#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 (целочисленное деление), унарный минус.
* Битовые функции: |, &, ^, ~.
2011-08-09 15:57:33 +00:00
*/
template<typename A, typename B, typename Op>
struct BinaryOperationImpl
2011-08-09 15:57:33 +00:00
{
typedef typename Op::ResultType ResultType;
2011-08-09 15:57:33 +00:00
static void vector_vector(const std::vector<A> & a, const std::vector<B> & b, std::vector<ResultType> & c)
{
size_t size = a.size();
for (size_t i = 0; i < size; ++i)
c[i] = Op::apply(a[i], b[i]);
2011-08-09 15:57:33 +00:00
}
static void vector_constant(const std::vector<A> & a, B b, std::vector<ResultType> & c)
{
size_t size = a.size();
for (size_t i = 0; i < size; ++i)
c[i] = Op::apply(a[i], b);
2011-08-09 15:57:33 +00:00
}
2011-08-20 23:56:07 +00:00
static void constant_vector(A a, const std::vector<B> & b, std::vector<ResultType> & c)
{
size_t size = b.size();
for (size_t i = 0; i < size; ++i)
c[i] = Op::apply(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)
{
c = Op::apply(a, b);
2011-08-09 15:57:33 +00:00
}
};
template<typename A, typename Op>
struct UnaryOperationImpl
2011-08-09 17:24:17 +00:00
{
typedef typename Op::ResultType ResultType;
2011-08-09 17:24:17 +00:00
static void vector(const std::vector<A> & a, std::vector<ResultType> & c)
2011-08-09 17:24:17 +00:00
{
size_t size = a.size();
for (size_t i = 0; i < size; ++i)
c[i] = Op::apply(a[i]);
2011-08-09 17:24:17 +00:00
}
static void constant(A a, ResultType & c)
2011-08-09 17:24:17 +00:00
{
c = Op::apply(a);
2011-08-09 17:24:17 +00:00
}
};
2011-08-09 17:24:17 +00:00
2011-08-20 23:56:07 +00:00
template<typename A, typename B>
struct PlusImpl
{
typedef typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type ResultType;
static inline ResultType apply(A a, B b)
2011-08-09 17:24:17 +00:00
{
/// Далее везде, static_cast - чтобы не было неправильного результата в выражениях вида Int64 c = UInt32(a) * Int32(-1).
return static_cast<ResultType>(a) + b;
2011-08-09 17:24:17 +00:00
}
};
template<typename A, typename B>
struct MultiplyImpl
2011-08-09 17:24:17 +00:00
{
typedef typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type ResultType;
2011-08-09 17:24:17 +00:00
static inline ResultType apply(A a, B b)
2011-08-09 17:24:17 +00:00
{
return static_cast<ResultType>(a) * b;
2011-08-09 17:24:17 +00:00
}
};
2011-08-09 17:24:17 +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
static inline ResultType apply(A a, B b)
2011-08-09 17:24:17 +00:00
{
return static_cast<ResultType>(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;
static inline ResultType apply(A a, B b)
2011-08-09 17:24:17 +00:00
{
return static_cast<ResultType>(a) / b;
2011-08-09 17:24:17 +00:00
}
};
template <typename T>
inline void throwIfZero(T x)
{
if (unlikely(x == 0))
throw Exception("Division by zero", ErrorCodes::DIVISION_BY_ZERO);
}
2011-08-09 17:24:17 +00:00
template<typename A, typename B>
struct DivideIntegralImpl
{
typedef typename NumberTraits::ResultOfIntegerDivision<A, B>::Type ResultType;
static inline ResultType apply(A a, B b)
2011-08-09 17:24:17 +00:00
{
throwIfZero(b);
return static_cast<ResultType>(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;
static inline ResultType apply(A a, B b)
2011-08-09 17:24:17 +00:00
{
throwIfZero(typename NumberTraits::ToInteger<A>::Type(b));
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
}
};
template<typename A, typename B>
struct BitwiseAndImpl
{
typedef typename NumberTraits::ResultOfBitwise<A, B>::Type ResultType;
static inline ResultType apply(A a, B b)
{
return static_cast<ResultType>(a)
& static_cast<ResultType>(b);
}
};
template<typename A, typename B>
struct BitwiseOrImpl
{
typedef typename NumberTraits::ResultOfBitwise<A, B>::Type ResultType;
static inline ResultType apply(A a, B b)
{
return static_cast<ResultType>(a)
| static_cast<ResultType>(b);
}
};
template<typename A, typename B>
struct BitwiseXorImpl
{
typedef typename NumberTraits::ResultOfBitwise<A, B>::Type ResultType;
static inline ResultType apply(A a, B b)
{
return static_cast<ResultType>(a)
^ static_cast<ResultType>(b);
}
};
2011-09-05 00:32:22 +00:00
template<typename A>
struct NegateImpl
{
typedef typename NumberTraits::ResultOfNegate<A>::Type ResultType;
static inline ResultType apply(A a)
2011-09-05 00:32:22 +00:00
{
return -a;
2011-09-05 00:32:22 +00:00
}
};
2011-09-05 00:32:22 +00:00
template<typename A>
struct BitwiseNotImpl
{
typedef typename NumberTraits::ResultOfBitwiseNot<A>::Type ResultType;
static inline ResultType apply(A a)
2011-09-05 00:32:22 +00:00
{
return ~static_cast<ResultType>(a);
2011-09-05 00:32:22 +00:00
}
};
2011-08-09 15:57:33 +00:00
template <template <typename, typename> class Op, typename Name>
2011-08-09 15:57:33 +00:00
class FunctionBinaryArithmetic : public IFunction
{
2011-08-12 20:39:42 +00:00
private:
template <typename T0, typename T1>
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
{
2011-08-15 00:55:43 +00:00
if (dynamic_cast<const T1 *>(&*arguments[1]))
2011-08-12 20:39:42 +00:00
{
2011-09-24 20:32:41 +00:00
type_res = new typename DataTypeFromFieldType<
typename Op<typename T0::FieldType, typename T1::FieldType>::ResultType>::Type;
2011-08-12 20:39:42 +00:00
return true;
}
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
{
2011-08-15 00:55:43 +00:00
if (dynamic_cast<const T0 *>(&*arguments[0]))
2011-08-12 20:39:42 +00:00
{
2011-09-24 20:32:41 +00:00
if ( checkRightType<T0, DataTypeUInt8>(arguments, type_res)
|| 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)
|| 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;
}
template <typename T0, typename T1>
2011-09-24 20:32:41 +00:00
bool executeRightType(Block & block, const ColumnNumbers & arguments, size_t result, const ColumnVector<T0> * col_left)
2011-08-12 20:39:42 +00:00
{
if (ColumnVector<T1> * col_right = dynamic_cast<ColumnVector<T1> *>(&*block.getByPosition(arguments[1]).column))
{
typedef typename Op<T0, T1>::ResultType ResultType;
2011-08-12 20:39:42 +00:00
ColumnVector<ResultType> * 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
typename ColumnVector<ResultType>::Container_t & vec_res = col_res->getData();
vec_res.resize(col_left->getData().size());
BinaryOperationImpl<T0, T1, Op<T0, T1> >::vector_vector(col_left->getData(), col_right->getData(), vec_res);
2011-08-12 20:39:42 +00:00
return true;
}
else if (ColumnConst<T1> * col_right = dynamic_cast<ColumnConst<T1> *>(&*block.getByPosition(arguments[1]).column))
{
typedef typename Op<T0, T1>::ResultType ResultType;
2011-08-12 20:39:42 +00:00
ColumnVector<ResultType> * 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
typename ColumnVector<ResultType>::Container_t & vec_res = col_res->getData();
vec_res.resize(col_left->getData().size());
BinaryOperationImpl<T0, T1, Op<T0, T1> >::vector_constant(col_left->getData(), col_right->getData(), vec_res);
2011-08-12 20:39:42 +00:00
return true;
}
return false;
}
template <typename T0, typename T1>
2011-09-24 20:32:41 +00:00
bool executeConstRightType(Block & block, const ColumnNumbers & arguments, size_t result, const ColumnConst<T0> * col_left)
2011-08-12 20:39:42 +00:00
{
if (ColumnVector<T1> * col_right = dynamic_cast<ColumnVector<T1> *>(&*block.getByPosition(arguments[1]).column))
{
typedef typename Op<T0, T1>::ResultType ResultType;
2011-08-12 20:39:42 +00:00
ColumnVector<ResultType> * 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
typename ColumnVector<ResultType>::Container_t & vec_res = col_res->getData();
vec_res.resize(col_left->size());
BinaryOperationImpl<T0, T1, Op<T0, T1> >::constant_vector(col_left->getData(), col_right->getData(), vec_res);
2011-08-12 20:39:42 +00:00
return true;
}
else if (ColumnConst<T1> * col_right = dynamic_cast<ColumnConst<T1> *>(&*block.getByPosition(arguments[1]).column))
{
typedef typename Op<T0, T1>::ResultType ResultType;
2011-08-12 20:39:42 +00:00
ResultType res = 0;
BinaryOperationImpl<T0, T1, Op<T0, T1> >::constant_constant(col_left->getData(), col_right->getData(), res);
2011-08-12 20:39:42 +00:00
ColumnConst<ResultType> * 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;
}
template <typename T0>
2011-09-24 20:32:41 +00:00
bool executeLeftType(Block & block, const ColumnNumbers & arguments, size_t result)
2011-08-12 20:39:42 +00:00
{
if (ColumnVector<T0> * col_left = dynamic_cast<ColumnVector<T0> *>(&*block.getByPosition(arguments[0]).column))
{
if ( executeRightType<T0, UInt8>(block, arguments, result, col_left)
|| executeRightType<T0, UInt16>(block, arguments, result, col_left)
|| executeRightType<T0, UInt32>(block, arguments, result, col_left)
|| executeRightType<T0, UInt64>(block, arguments, result, col_left)
|| executeRightType<T0, Int8>(block, arguments, result, col_left)
|| executeRightType<T0, Int16>(block, arguments, result, col_left)
|| executeRightType<T0, Int32>(block, arguments, result, col_left)
|| executeRightType<T0, Int64>(block, arguments, result, col_left)
|| executeRightType<T0, Float32>(block, arguments, result, col_left)
|| executeRightType<T0, Float64>(block, arguments, result, col_left))
return true;
else
2011-08-28 00:31:30 +00:00
throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName()
+ " of second argument of function " + getName(),
2011-08-12 20:39:42 +00:00
ErrorCodes::ILLEGAL_COLUMN);
}
else if (ColumnConst<T0> * col_left = dynamic_cast<ColumnConst<T0> *>(&*block.getByPosition(arguments[0]).column))
{
if ( executeConstRightType<T0, UInt8>(block, arguments, result, col_left)
|| executeConstRightType<T0, UInt16>(block, arguments, result, col_left)
|| executeConstRightType<T0, UInt32>(block, arguments, result, col_left)
|| executeConstRightType<T0, UInt64>(block, arguments, result, col_left)
|| executeConstRightType<T0, Int8>(block, arguments, result, col_left)
|| executeConstRightType<T0, Int16>(block, arguments, result, col_left)
|| executeConstRightType<T0, Int32>(block, arguments, result, col_left)
|| executeConstRightType<T0, Int64>(block, arguments, result, col_left)
|| executeConstRightType<T0, Float32>(block, arguments, result, col_left)
|| executeConstRightType<T0, Float64>(block, arguments, result, col_left))
return true;
else
2011-08-28 00:31:30 +00:00
throw Exception("Illegal column " + block.getByPosition(arguments[1]).column->getName()
+ " of second argument of function " + getName(),
2011-08-12 20:39:42 +00:00
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
{
2011-08-13 21:05:18 +00:00
return Name::get();
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 "
+ Poco::NumberFormatter::format(arguments.size()) + ", should be 2.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
2011-09-24 20:32:41 +00:00
DataTypePtr type_res;
if (!( checkLeftType<DataTypeUInt8>(arguments, type_res)
|| 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)
|| 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
{
2011-08-12 20:39:42 +00:00
if (!( executeLeftType<UInt8>(block, arguments, result)
|| executeLeftType<UInt16>(block, arguments, result)
|| executeLeftType<UInt32>(block, arguments, result)
|| executeLeftType<UInt64>(block, arguments, result)
|| executeLeftType<Int8>(block, arguments, result)
|| executeLeftType<Int16>(block, arguments, result)
|| executeLeftType<Int32>(block, arguments, result)
|| executeLeftType<Int64>(block, arguments, result)
|| executeLeftType<Float32>(block, arguments, result)
|| executeLeftType<Float64>(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);
}
};
template <template <typename> class Op, typename Name>
2011-09-05 00:32:22 +00:00
class FunctionUnaryArithmetic : public IFunction
{
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
{
if (dynamic_cast<const T0 *>(&*arguments[0]))
{
2011-09-24 20:32:41 +00:00
result = new typename DataTypeFromFieldType<
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
{
if (ColumnVector<T0> * col = dynamic_cast<ColumnVector<T0> *>(&*block.getByPosition(arguments[0]).column))
{
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());
UnaryOperationImpl<T0, Op<T0> >::vector(col->getData(), vec_res);
2011-09-05 00:32:22 +00:00
return true;
}
else if (ColumnConst<T0> * col = dynamic_cast<ColumnConst<T0> *>(&*block.getByPosition(arguments[0]).column))
{
typedef typename Op<T0>::ResultType ResultType;
2011-09-05 00:32:22 +00:00
ResultType res = 0;
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
{
return Name::get();
}
/// Получить типы результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение.
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 "
+ Poco::NumberFormatter::format(arguments.size()) + ", should be 1.",
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)
|| 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);
}
};
2011-08-09 17:24:17 +00:00
struct NamePlus { static const char * get() { return "plus"; } };
struct NameMinus { static const char * get() { return "minus"; } };
struct NameMultiply { static const char * get() { return "multiply"; } };
struct NameDivideFloating { static const char * get() { return "divide"; } };
2012-12-11 20:32:08 +00:00
struct NameDivideIntegral { static const char * get() { return "intDiv"; } };
2011-08-09 17:24:17 +00:00
struct NameModulo { static const char * get() { return "modulo"; } };
struct NameNegate { static const char * get() { return "negate"; } };
struct NameBitwiseAnd { static const char * get() { return "bitwiseAnd"; } };
struct NameBitwiseOr { static const char * get() { return "bitwiseOr"; } };
struct NameBitwiseXor { static const char * get() { return "bitwiseXor"; } };
struct NameBitwiseNot { static const char * get() { return "bitwiseNot"; } };
2011-08-09 15:57:33 +00:00
typedef FunctionBinaryArithmetic<PlusImpl, NamePlus> FunctionPlus;
2011-08-09 17:24:17 +00:00
typedef FunctionBinaryArithmetic<MinusImpl, NameMinus> FunctionMinus;
typedef FunctionBinaryArithmetic<MultiplyImpl, NameMultiply> FunctionMultiply;
2011-08-09 17:24:17 +00:00
typedef FunctionBinaryArithmetic<DivideFloatingImpl, NameDivideFloating> FunctionDivideFloating;
typedef FunctionBinaryArithmetic<DivideIntegralImpl, NameDivideIntegral> FunctionDivideIntegral;
typedef FunctionBinaryArithmetic<ModuloImpl, NameModulo> FunctionModulo;
2011-09-05 00:32:22 +00:00
typedef FunctionUnaryArithmetic<NegateImpl, NameNegate> FunctionNegate;
typedef FunctionBinaryArithmetic<BitwiseAndImpl, NameBitwiseAnd> FunctionBitwiseAnd;
typedef FunctionBinaryArithmetic<BitwiseOrImpl, NameBitwiseOr> FunctionBitwiseOr;
typedef FunctionBinaryArithmetic<BitwiseXorImpl, NameBitwiseXor> FunctionBitwiseXor;
typedef FunctionUnaryArithmetic<BitwiseNotImpl, NameBitwiseNot> FunctionBitwiseNot;
2011-08-09 15:57:33 +00:00
}