2012-12-24 08:16:25 +00:00
|
|
|
#pragma once
|
|
|
|
|
2017-07-21 06:35:58 +00:00
|
|
|
#include <Functions/FunctionHelpers.h>
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <IO/WriteHelpers.h>
|
2018-11-30 11:15:58 +00:00
|
|
|
#include <DataTypes/getLeastSupertype.h>
|
|
|
|
#include <DataTypes/DataTypeArray.h>
|
2018-12-27 00:10:38 +00:00
|
|
|
#include <DataTypes/DataTypesNumber.h>
|
2018-12-27 00:11:42 +00:00
|
|
|
#include <DataTypes/DataTypesDecimal.h>
|
2018-12-27 00:09:39 +00:00
|
|
|
#include <Columns/ColumnVector.h>
|
2018-11-30 15:57:06 +00:00
|
|
|
#include <Interpreters/castColumn.h>
|
2019-03-13 13:58:04 +00:00
|
|
|
#include "IFunction.h"
|
2018-12-27 00:02:11 +00:00
|
|
|
#include <Common/intExp.h>
|
2015-05-14 12:08:27 +00:00
|
|
|
#include <cmath>
|
|
|
|
#include <type_traits>
|
|
|
|
#include <array>
|
2017-09-16 22:12:24 +00:00
|
|
|
#include <ext/bit_cast.h>
|
2018-11-30 15:57:06 +00:00
|
|
|
#include <algorithm>
|
2018-11-29 00:32:52 +00:00
|
|
|
|
2019-01-04 12:10:00 +00:00
|
|
|
#ifdef __SSE4_1__
|
2017-04-01 07:20:54 +00:00
|
|
|
#include <smmintrin.h>
|
2016-01-16 00:45:19 +00:00
|
|
|
#endif
|
|
|
|
|
2017-09-16 22:12:24 +00:00
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
namespace DB
|
|
|
|
{
|
2014-11-12 17:23:26 +00:00
|
|
|
|
2017-06-13 02:06:53 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
2018-11-26 16:20:40 +00:00
|
|
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
2018-11-21 10:10:22 +00:00
|
|
|
extern const int ILLEGAL_COLUMN;
|
2018-11-26 16:20:40 +00:00
|
|
|
extern const int LOGICAL_ERROR;
|
2018-12-19 01:29:40 +00:00
|
|
|
extern const int BAD_ARGUMENTS;
|
2017-06-13 02:06:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-27 15:45:25 +00:00
|
|
|
/** Rounding Functions:
|
2017-09-17 17:58:30 +00:00
|
|
|
* round(x, N) - rounding to nearest (N = 0 by default). Use banker's rounding for floating point numbers.
|
|
|
|
* floor(x, N) is the largest number <= x (N = 0 by default).
|
|
|
|
* ceil(x, N) is the smallest number >= x (N = 0 by default).
|
|
|
|
* trunc(x, N) - is the largest by absolute value number that is not greater than x by absolute value (N = 0 by default).
|
2017-04-01 07:20:54 +00:00
|
|
|
*
|
2017-09-17 17:58:30 +00:00
|
|
|
* The value of the parameter N (scale):
|
2017-05-27 15:45:25 +00:00
|
|
|
* - N > 0: round to the number with N decimal places after the decimal point
|
|
|
|
* - N < 0: round to an integer with N zero characters
|
|
|
|
* - N = 0: round to an integer
|
2017-09-17 17:58:30 +00:00
|
|
|
*
|
|
|
|
* Type of the result is the type of argument.
|
|
|
|
* For integer arguments, when passing negative scale, overflow can occur.
|
|
|
|
* In that case, the behavior is implementation specific.
|
2017-04-01 07:20:54 +00:00
|
|
|
*/
|
2017-03-09 03:34:09 +00:00
|
|
|
|
|
|
|
|
2017-05-27 15:45:25 +00:00
|
|
|
/** This parameter controls the behavior of the rounding functions.
|
2017-09-16 16:38:27 +00:00
|
|
|
*/
|
|
|
|
enum class ScaleMode
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
Positive, // round to a number with N decimal places after the decimal point
|
|
|
|
Negative, // round to an integer with N zero characters
|
|
|
|
Zero, // round to an integer
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-19 13:23:13 +00:00
|
|
|
|
2017-09-16 16:38:27 +00:00
|
|
|
enum class RoundingMode
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2019-01-04 12:10:00 +00:00
|
|
|
#ifdef __SSE4_1__
|
2017-09-16 16:38:27 +00:00
|
|
|
Round = _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC,
|
|
|
|
Floor = _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC,
|
|
|
|
Ceil = _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC,
|
|
|
|
Trunc = _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC,
|
|
|
|
#else
|
|
|
|
Round = 8, /// Values are correspond to above just in case.
|
|
|
|
Floor = 9,
|
|
|
|
Ceil = 10,
|
|
|
|
Trunc = 11,
|
|
|
|
#endif
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-27 22:59:46 +00:00
|
|
|
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 19:31:20 +00:00
|
|
|
/** Rounding functions for integer values.
|
2017-09-16 16:38:27 +00:00
|
|
|
*/
|
|
|
|
template <typename T, RoundingMode rounding_mode, ScaleMode scale_mode>
|
|
|
|
struct IntegerRoundingComputation
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
static const size_t data_count = 1;
|
|
|
|
|
|
|
|
static size_t prepare(size_t scale)
|
|
|
|
{
|
|
|
|
return scale;
|
|
|
|
}
|
|
|
|
|
2018-12-24 00:47:11 +00:00
|
|
|
static ALWAYS_INLINE T computeImpl(T x, T scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
switch (rounding_mode)
|
|
|
|
{
|
|
|
|
case RoundingMode::Trunc:
|
2017-09-16 19:31:20 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
return x / scale * scale;
|
2017-09-16 19:31:20 +00:00
|
|
|
}
|
2017-09-16 16:38:27 +00:00
|
|
|
case RoundingMode::Floor:
|
2017-09-16 19:31:20 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
if (x < 0)
|
|
|
|
x -= scale - 1;
|
|
|
|
return x / scale * scale;
|
2017-09-16 19:31:20 +00:00
|
|
|
}
|
2017-09-16 16:38:27 +00:00
|
|
|
case RoundingMode::Ceil:
|
2017-09-16 19:31:20 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
if (x >= 0)
|
|
|
|
x += scale - 1;
|
|
|
|
return x / scale * scale;
|
2017-09-16 19:31:20 +00:00
|
|
|
}
|
2017-09-16 16:38:27 +00:00
|
|
|
case RoundingMode::Round:
|
2017-09-16 19:31:20 +00:00
|
|
|
{
|
|
|
|
bool negative = x < 0;
|
|
|
|
if (negative)
|
|
|
|
x = -x;
|
|
|
|
x = (x + scale / 2) / scale * scale;
|
|
|
|
if (negative)
|
|
|
|
x = -x;
|
|
|
|
return x;
|
|
|
|
}
|
2017-09-16 16:38:27 +00:00
|
|
|
}
|
2019-04-02 09:43:53 +00:00
|
|
|
|
|
|
|
__builtin_unreachable();
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
2018-12-24 00:47:11 +00:00
|
|
|
static ALWAYS_INLINE T compute(T x, T scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 16:38:27 +00:00
|
|
|
switch (scale_mode)
|
|
|
|
{
|
|
|
|
case ScaleMode::Zero:
|
|
|
|
return x;
|
|
|
|
case ScaleMode::Positive:
|
|
|
|
return x;
|
|
|
|
case ScaleMode::Negative:
|
2017-09-16 19:31:20 +00:00
|
|
|
return computeImpl(x, scale);
|
2017-09-16 16:38:27 +00:00
|
|
|
}
|
2019-04-02 09:43:53 +00:00
|
|
|
|
|
|
|
__builtin_unreachable();
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-09-16 18:36:16 +00:00
|
|
|
|
2017-09-16 19:31:20 +00:00
|
|
|
static ALWAYS_INLINE void compute(const T * __restrict in, size_t scale, T * __restrict out)
|
2017-09-16 18:36:16 +00:00
|
|
|
{
|
2018-12-25 17:52:28 +00:00
|
|
|
if (sizeof(T) <= sizeof(scale) && scale > size_t(std::numeric_limits<T>::max()))
|
2018-12-24 00:47:11 +00:00
|
|
|
*out = 0;
|
|
|
|
else
|
|
|
|
*out = compute(*in, scale);
|
2017-09-16 18:36:16 +00:00
|
|
|
}
|
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-27 22:59:46 +00:00
|
|
|
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2019-01-04 12:10:00 +00:00
|
|
|
#ifdef __SSE4_1__
|
2017-09-16 18:36:16 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
template <typename T>
|
|
|
|
class BaseFloatRoundingComputation;
|
|
|
|
|
|
|
|
template <>
|
|
|
|
class BaseFloatRoundingComputation<Float32>
|
|
|
|
{
|
|
|
|
public:
|
2017-09-16 18:36:16 +00:00
|
|
|
using ScalarType = Float32;
|
|
|
|
using VectorType = __m128;
|
2017-04-01 07:20:54 +00:00
|
|
|
static const size_t data_count = 4;
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType load(const ScalarType * in) { return _mm_loadu_ps(in); }
|
|
|
|
static VectorType load1(const ScalarType in) { return _mm_load1_ps(&in); }
|
|
|
|
static void store(ScalarType * out, VectorType val) { _mm_storeu_ps(out, val);}
|
|
|
|
static VectorType multiply(VectorType val, VectorType scale) { return _mm_mul_ps(val, scale); }
|
|
|
|
static VectorType divide(VectorType val, VectorType scale) { return _mm_div_ps(val, scale); }
|
|
|
|
template <RoundingMode mode> static VectorType apply(VectorType val) { return _mm_round_ps(val, int(mode)); }
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType prepare(size_t scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
return load1(scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-06-03 17:01:30 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
template <>
|
|
|
|
class BaseFloatRoundingComputation<Float64>
|
|
|
|
{
|
|
|
|
public:
|
2017-09-16 18:36:16 +00:00
|
|
|
using ScalarType = Float64;
|
|
|
|
using VectorType = __m128d;
|
2017-04-01 07:20:54 +00:00
|
|
|
static const size_t data_count = 2;
|
2015-05-19 13:23:13 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType load(const ScalarType * in) { return _mm_loadu_pd(in); }
|
|
|
|
static VectorType load1(const ScalarType in) { return _mm_load1_pd(&in); }
|
|
|
|
static void store(ScalarType * out, VectorType val) { _mm_storeu_pd(out, val);}
|
|
|
|
static VectorType multiply(VectorType val, VectorType scale) { return _mm_mul_pd(val, scale); }
|
|
|
|
static VectorType divide(VectorType val, VectorType scale) { return _mm_div_pd(val, scale); }
|
|
|
|
template <RoundingMode mode> static VectorType apply(VectorType val) { return _mm_round_pd(val, int(mode)); }
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType prepare(size_t scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
return load1(scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
#else
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
/// Implementation for ARM. Not vectorized.
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
inline float roundWithMode(float x, RoundingMode mode)
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
switch (mode)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
case RoundingMode::Round: return roundf(x);
|
|
|
|
case RoundingMode::Floor: return floorf(x);
|
|
|
|
case RoundingMode::Ceil: return ceilf(x);
|
|
|
|
case RoundingMode::Trunc: return truncf(x);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2019-02-08 22:09:32 +00:00
|
|
|
|
|
|
|
__builtin_unreachable();
|
2017-09-16 18:36:16 +00:00
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
inline double roundWithMode(double x, RoundingMode mode)
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
switch (mode)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
case RoundingMode::Round: return round(x);
|
|
|
|
case RoundingMode::Floor: return floor(x);
|
|
|
|
case RoundingMode::Ceil: return ceil(x);
|
|
|
|
case RoundingMode::Trunc: return trunc(x);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2019-02-08 22:09:32 +00:00
|
|
|
|
|
|
|
__builtin_unreachable();
|
2017-09-16 18:36:16 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
class BaseFloatRoundingComputation
|
|
|
|
{
|
|
|
|
public:
|
2017-09-16 18:36:16 +00:00
|
|
|
using ScalarType = T;
|
|
|
|
using VectorType = T;
|
2017-04-01 07:20:54 +00:00
|
|
|
static const size_t data_count = 1;
|
2017-03-09 03:34:09 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType load(const ScalarType * in) { return *in; }
|
|
|
|
static VectorType load1(const ScalarType in) { return in; }
|
|
|
|
static VectorType store(ScalarType * out, ScalarType val) { return *out = val;}
|
|
|
|
static VectorType multiply(VectorType val, VectorType scale) { return val * scale; }
|
|
|
|
static VectorType divide(VectorType val, VectorType scale) { return val / scale; }
|
|
|
|
template <RoundingMode mode> static VectorType apply(VectorType val) { return roundWithMode(val, mode); }
|
2016-01-16 00:45:19 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
static VectorType prepare(size_t scale)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
return load1(scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2016-01-16 00:45:19 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
/** Implementation of low-level round-off functions for floating-point values.
|
2017-09-16 16:38:27 +00:00
|
|
|
*/
|
|
|
|
template <typename T, RoundingMode rounding_mode, ScaleMode scale_mode>
|
2017-09-16 18:36:16 +00:00
|
|
|
class FloatRoundingComputation : public BaseFloatRoundingComputation<T>
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
using Base = BaseFloatRoundingComputation<T>;
|
2016-01-16 00:45:19 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
public:
|
2017-09-16 18:36:16 +00:00
|
|
|
static inline void compute(const T * __restrict in, const typename Base::VectorType & scale, T * __restrict out)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
auto val = Base::load(in);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
if (scale_mode == ScaleMode::Positive)
|
|
|
|
val = Base::multiply(val, scale);
|
|
|
|
else if (scale_mode == ScaleMode::Negative)
|
|
|
|
val = Base::divide(val, scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
val = Base::template apply<rounding_mode>(val);
|
|
|
|
|
|
|
|
if (scale_mode == ScaleMode::Positive)
|
|
|
|
val = Base::divide(val, scale);
|
|
|
|
else if (scale_mode == ScaleMode::Negative)
|
|
|
|
val = Base::multiply(val, scale);
|
|
|
|
|
|
|
|
Base::store(out, val);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-19 13:23:13 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
|
|
|
|
/** Implementing high-level rounding functions.
|
2017-09-16 16:38:27 +00:00
|
|
|
*/
|
|
|
|
template <typename T, RoundingMode rounding_mode, ScaleMode scale_mode>
|
2017-09-16 20:17:19 +00:00
|
|
|
struct FloatRoundingImpl
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
|
|
|
private:
|
2017-09-16 20:17:19 +00:00
|
|
|
using Op = FloatRoundingComputation<T, rounding_mode, scale_mode>;
|
2017-04-01 07:20:54 +00:00
|
|
|
using Data = std::array<T, Op::data_count>;
|
2015-05-19 13:23:13 +00:00
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
public:
|
2017-12-15 21:32:25 +00:00
|
|
|
static NO_INLINE void apply(const PaddedPODArray<T> & in, size_t scale, typename ColumnVector<T>::Container & out)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
auto mm_scale = Op::prepare(scale);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
const size_t data_count = std::tuple_size<Data>();
|
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
const T* end_in = in.data() + in.size();
|
|
|
|
const T* limit = in.data() + in.size() / data_count * data_count;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
const T* __restrict p_in = in.data();
|
|
|
|
T* __restrict p_out = out.data();
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
while (p_in < limit)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
|
|
|
Op::compute(p_in, mm_scale, p_out);
|
2017-09-16 18:36:16 +00:00
|
|
|
p_in += data_count;
|
2017-04-01 07:20:54 +00:00
|
|
|
p_out += data_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p_in < end_in)
|
|
|
|
{
|
2017-09-16 18:36:16 +00:00
|
|
|
Data tmp_src{{}};
|
|
|
|
Data tmp_dst;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
size_t tail_size_bytes = (end_in - p_in) * sizeof(*p_in);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
memcpy(&tmp_src, p_in, tail_size_bytes);
|
|
|
|
Op::compute(reinterpret_cast<T *>(&tmp_src), mm_scale, reinterpret_cast<T *>(&tmp_dst));
|
|
|
|
memcpy(p_out, &tmp_dst, tail_size_bytes);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-14 12:08:27 +00:00
|
|
|
|
2017-09-16 20:17:19 +00:00
|
|
|
template <typename T, RoundingMode rounding_mode, ScaleMode scale_mode>
|
|
|
|
struct IntegerRoundingImpl
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
using Op = IntegerRoundingComputation<T, rounding_mode, scale_mode>;
|
|
|
|
|
|
|
|
public:
|
|
|
|
template <size_t scale>
|
2017-12-15 21:32:25 +00:00
|
|
|
static NO_INLINE void applyImpl(const PaddedPODArray<T> & in, typename ColumnVector<T>::Container & out)
|
2017-09-16 20:17:19 +00:00
|
|
|
{
|
2018-12-25 17:45:02 +00:00
|
|
|
const T * end_in = in.data() + in.size();
|
2017-09-16 20:17:19 +00:00
|
|
|
|
2018-12-25 17:45:02 +00:00
|
|
|
const T * __restrict p_in = in.data();
|
|
|
|
T * __restrict p_out = out.data();
|
2017-09-16 20:17:19 +00:00
|
|
|
|
|
|
|
while (p_in < end_in)
|
|
|
|
{
|
|
|
|
Op::compute(p_in, scale, p_out);
|
|
|
|
++p_in;
|
|
|
|
++p_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-15 21:32:25 +00:00
|
|
|
static NO_INLINE void apply(const PaddedPODArray<T> & in, size_t scale, typename ColumnVector<T>::Container & out)
|
2017-09-16 20:17:19 +00:00
|
|
|
{
|
|
|
|
/// Manual function cloning for compiler to generate integer division by constant.
|
|
|
|
switch (scale)
|
|
|
|
{
|
|
|
|
case 1ULL: return applyImpl<1ULL>(in, out);
|
|
|
|
case 10ULL: return applyImpl<10ULL>(in, out);
|
|
|
|
case 100ULL: return applyImpl<100ULL>(in, out);
|
|
|
|
case 1000ULL: return applyImpl<1000ULL>(in, out);
|
|
|
|
case 10000ULL: return applyImpl<10000ULL>(in, out);
|
|
|
|
case 100000ULL: return applyImpl<100000ULL>(in, out);
|
|
|
|
case 1000000ULL: return applyImpl<1000000ULL>(in, out);
|
|
|
|
case 10000000ULL: return applyImpl<10000000ULL>(in, out);
|
|
|
|
case 100000000ULL: return applyImpl<100000000ULL>(in, out);
|
|
|
|
case 1000000000ULL: return applyImpl<1000000000ULL>(in, out);
|
|
|
|
case 10000000000ULL: return applyImpl<10000000000ULL>(in, out);
|
|
|
|
case 100000000000ULL: return applyImpl<100000000000ULL>(in, out);
|
|
|
|
case 1000000000000ULL: return applyImpl<1000000000000ULL>(in, out);
|
|
|
|
case 10000000000000ULL: return applyImpl<10000000000000ULL>(in, out);
|
|
|
|
case 100000000000000ULL: return applyImpl<100000000000000ULL>(in, out);
|
|
|
|
case 1000000000000000ULL: return applyImpl<1000000000000000ULL>(in, out);
|
|
|
|
case 10000000000000000ULL: return applyImpl<10000000000000000ULL>(in, out);
|
|
|
|
case 100000000000000000ULL: return applyImpl<100000000000000000ULL>(in, out);
|
|
|
|
case 1000000000000000000ULL: return applyImpl<1000000000000000000ULL>(in, out);
|
|
|
|
case 10000000000000000000ULL: return applyImpl<10000000000000000000ULL>(in, out);
|
|
|
|
default:
|
|
|
|
throw Exception("Logical error: unexpected 'scale' parameter passed to function IntegerRoundingComputation::compute",
|
|
|
|
ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-03-09 03:34:09 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
template <typename T, RoundingMode rounding_mode>
|
2018-09-10 13:52:18 +00:00
|
|
|
class DecimalRounding
|
2017-03-09 03:34:09 +00:00
|
|
|
{
|
2018-09-10 14:11:30 +00:00
|
|
|
using NativeType = typename T::NativeType;
|
2018-09-10 13:52:18 +00:00
|
|
|
using Op = IntegerRoundingComputation<NativeType, rounding_mode, ScaleMode::Negative>;
|
|
|
|
using Container = typename ColumnDecimal<T>::Container;
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
public:
|
|
|
|
static NO_INLINE void apply(const Container & in, Container & out, Int64 scale_arg)
|
|
|
|
{
|
|
|
|
scale_arg = in.getScale() - scale_arg;
|
|
|
|
if (scale_arg > 0)
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-09-10 14:11:30 +00:00
|
|
|
size_t scale = intExp10(scale_arg);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
const NativeType * __restrict p_in = reinterpret_cast<const NativeType *>(in.data());
|
|
|
|
const NativeType * end_in = reinterpret_cast<const NativeType *>(in.data()) + in.size();
|
|
|
|
NativeType * __restrict p_out = reinterpret_cast<NativeType *>(out.data());
|
2017-09-17 17:58:30 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
while (p_in < end_in)
|
|
|
|
{
|
|
|
|
Op::compute(p_in, scale, p_out);
|
|
|
|
++p_in;
|
|
|
|
++p_out;
|
|
|
|
}
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-09-10 13:52:18 +00:00
|
|
|
else
|
|
|
|
memcpy(out.data(), in.data(), in.size() * sizeof(T));
|
|
|
|
}
|
|
|
|
};
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
|
|
|
|
/** Select the appropriate processing algorithm depending on the scale.
|
|
|
|
*/
|
|
|
|
template <typename T, RoundingMode rounding_mode>
|
|
|
|
class Dispatcher
|
|
|
|
{
|
|
|
|
template <ScaleMode scale_mode>
|
|
|
|
using FunctionRoundingImpl = std::conditional_t<std::is_floating_point_v<T>,
|
|
|
|
FloatRoundingImpl<T, rounding_mode, scale_mode>,
|
|
|
|
IntegerRoundingImpl<T, rounding_mode, scale_mode>>;
|
|
|
|
|
|
|
|
static void apply(Block & block, const ColumnVector<T> * col, Int64 scale_arg, size_t result)
|
|
|
|
{
|
2017-12-14 01:43:19 +00:00
|
|
|
auto col_res = ColumnVector<T>::create();
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2017-12-15 21:32:25 +00:00
|
|
|
typename ColumnVector<T>::Container & vec_res = col_res->getData();
|
2017-04-01 07:20:54 +00:00
|
|
|
vec_res.resize(col->getData().size());
|
2015-05-25 13:35:04 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
if (!vec_res.empty())
|
2017-12-16 05:21:04 +00:00
|
|
|
{
|
2018-09-10 13:52:18 +00:00
|
|
|
if (scale_arg == 0)
|
|
|
|
{
|
|
|
|
size_t scale = 1;
|
|
|
|
FunctionRoundingImpl<ScaleMode::Zero>::apply(col->getData(), scale, vec_res);
|
|
|
|
}
|
|
|
|
else if (scale_arg > 0)
|
|
|
|
{
|
2018-09-10 14:13:03 +00:00
|
|
|
size_t scale = intExp10(scale_arg);
|
2018-09-10 13:52:18 +00:00
|
|
|
FunctionRoundingImpl<ScaleMode::Positive>::apply(col->getData(), scale, vec_res);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-09-10 14:13:03 +00:00
|
|
|
size_t scale = intExp10(-scale_arg);
|
2018-09-10 13:52:18 +00:00
|
|
|
FunctionRoundingImpl<ScaleMode::Negative>::apply(col->getData(), scale, vec_res);
|
|
|
|
}
|
2017-12-16 05:21:04 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
block.getByPosition(result).column = std::move(col_res);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void apply(Block & block, const ColumnDecimal<T> * col, Int64 scale_arg, size_t result)
|
|
|
|
{
|
|
|
|
const typename ColumnDecimal<T>::Container & vec_src = col->getData();
|
|
|
|
|
|
|
|
auto col_res = ColumnDecimal<T>::create(vec_src.size(), vec_src.getScale());
|
|
|
|
auto & vec_res = col_res->getData();
|
|
|
|
|
|
|
|
if (!vec_res.empty())
|
|
|
|
DecimalRounding<T, rounding_mode>::apply(col->getData(), vec_res, scale_arg);
|
2017-12-16 05:21:04 +00:00
|
|
|
|
|
|
|
block.getByPosition(result).column = std::move(col_res);
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2018-09-10 13:52:18 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
static void apply(Block & block, const IColumn * column, Int64 scale_arg, size_t result)
|
|
|
|
{
|
|
|
|
if constexpr (IsNumber<T>)
|
|
|
|
apply(block, checkAndGetColumn<ColumnVector<T>>(column), scale_arg, result);
|
|
|
|
else if constexpr (IsDecimalNumber<T>)
|
|
|
|
apply(block, checkAndGetColumn<ColumnDecimal<T>>(column), scale_arg, result);
|
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
|
|
|
|
2017-05-27 15:45:25 +00:00
|
|
|
/** A template for functions that round the value of an input parameter of type
|
2018-09-10 13:52:18 +00:00
|
|
|
* (U)Int8/16/32/64, Float32/64 or Decimal32/64/128, and accept an additional optional parameter (default is 0).
|
2017-09-16 18:36:16 +00:00
|
|
|
*/
|
2017-09-16 16:38:27 +00:00
|
|
|
template <typename Name, RoundingMode rounding_mode>
|
2017-03-09 03:34:09 +00:00
|
|
|
class FunctionRounding : public IFunction
|
|
|
|
{
|
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
static constexpr auto name = Name::name;
|
2017-12-02 02:47:12 +00:00
|
|
|
static FunctionPtr create(const Context &) { return std::make_shared<FunctionRounding>(); }
|
2017-03-09 03:34:09 +00:00
|
|
|
|
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
String getName() const override
|
|
|
|
{
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isVariadic() const override { return true; }
|
|
|
|
size_t getNumberOfArguments() const override { return 0; }
|
|
|
|
|
2017-05-27 15:45:25 +00:00
|
|
|
/// Get result types by argument types. If the function does not apply to these arguments, throw an exception.
|
2017-04-01 07:20:54 +00:00
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
|
|
|
{
|
|
|
|
if ((arguments.size() < 1) || (arguments.size() > 2))
|
|
|
|
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
|
|
|
+ toString(arguments.size()) + ", should be 1 or 2.",
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
for (const auto & type : arguments)
|
2019-05-24 12:11:03 +00:00
|
|
|
if (!isNumber(type))
|
2017-09-16 18:36:16 +00:00
|
|
|
throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
2017-04-01 07:20:54 +00:00
|
|
|
|
|
|
|
return arguments[0];
|
|
|
|
}
|
|
|
|
|
2018-09-10 13:52:18 +00:00
|
|
|
static Int64 getScaleArg(Block & block, const ColumnNumbers & arguments)
|
|
|
|
{
|
|
|
|
if (arguments.size() == 2)
|
|
|
|
{
|
|
|
|
const IColumn & scale_column = *block.getByPosition(arguments[1]).column;
|
|
|
|
if (!scale_column.isColumnConst())
|
|
|
|
throw Exception("Scale argument for rounding functions must be constant.", ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
|
|
|
Field scale_field = static_cast<const ColumnConst &>(scale_column).getField();
|
|
|
|
if (scale_field.getType() != Field::Types::UInt64
|
|
|
|
&& scale_field.getType() != Field::Types::Int64)
|
|
|
|
throw Exception("Scale argument for rounding functions must have integer type.", ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
|
|
|
return scale_field.get<Int64>();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-23 08:40:43 +00:00
|
|
|
bool useDefaultImplementationForConstants() const override { return true; }
|
|
|
|
ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; }
|
|
|
|
|
2018-04-24 07:16:39 +00:00
|
|
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-09-10 13:52:18 +00:00
|
|
|
const ColumnWithTypeAndName & column = block.getByPosition(arguments[0]);
|
|
|
|
Int64 scale_arg = getScaleArg(block, arguments);
|
|
|
|
|
|
|
|
auto call = [&](const auto & types) -> bool
|
|
|
|
{
|
|
|
|
using Types = std::decay_t<decltype(types)>;
|
|
|
|
using DataType = typename Types::LeftType;
|
|
|
|
|
|
|
|
if constexpr (IsDataTypeNumber<DataType> || IsDataTypeDecimal<DataType>)
|
|
|
|
{
|
|
|
|
using FieldType = typename DataType::FieldType;
|
|
|
|
Dispatcher<FieldType, rounding_mode>::apply(block, column.column.get(), scale_arg, result);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!callOnIndexAndDataType<void>(column.type->getTypeId(), call))
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
2018-09-10 13:52:18 +00:00
|
|
|
throw Exception("Illegal column " + column.name + " of argument of function " + getName(),
|
2017-04-01 07:20:54 +00:00
|
|
|
ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasInformationAboutMonotonicity() const override
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-02 02:47:12 +00:00
|
|
|
Monotonicity getMonotonicityForRange(const IDataType &, const Field &, const Field &) const override
|
2017-04-01 07:20:54 +00:00
|
|
|
{
|
PKCondition: infer index use with pk subexpression
By default only constraints explicitly matching
primary key expression (or expression wrapped in
a monotonic function) are eligible for part and
range selection. So for example, if index is:
(toStartOfHour(dt), UserID)
Then a query such as this resorts to full scan:
SELECT count() FROM t WHERE dt = now()
Intuitively, only parts with toStartOfHour(now())
could be selected, but it is less trivial to prove.
The primary key currently can be wrapped in a chain
of monotonic functions, so following would work:
toStartOfHour(dt) = toStartOfHour(now()) AND dt = now()
It must be however explicitly stated, if we wanted
to infer that we’d have to know the inverse function,
and prove that the inverse function is monotonic
on given interval. This is not practical as
there is no inverse function that for example undos
rounding, it isn’t strictly monotonic.
There are however functions that don’t transform
output range and preserve monotonicity on the
complete input range, such as rounding or casts
to a same or wider numeric type. This eliminates
the need to find inverse function, as no check for monotonicity over arbitrary interval is needed,
and thus makes this optimisation possible.
2017-07-06 05:39:05 +00:00
|
|
|
return { true, true, true };
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
2017-03-09 03:34:09 +00:00
|
|
|
};
|
2015-05-14 12:08:27 +00:00
|
|
|
|
2017-09-16 18:36:16 +00:00
|
|
|
|
2018-12-19 01:29:40 +00:00
|
|
|
/** Rounds down to a number within explicitly specified array.
|
|
|
|
* If the value is less than the minimal bound - returns the minimal bound.
|
|
|
|
*/
|
2018-12-03 11:28:22 +00:00
|
|
|
class FunctionRoundDown : public IFunction
|
|
|
|
{
|
2018-11-21 10:10:22 +00:00
|
|
|
public:
|
|
|
|
static constexpr auto name = "roundDown";
|
2018-11-30 15:57:06 +00:00
|
|
|
static FunctionPtr create(const Context & context) { return std::make_shared<FunctionRoundDown>(context); }
|
|
|
|
FunctionRoundDown(const Context & context) : context(context) {}
|
2018-11-21 10:10:22 +00:00
|
|
|
|
|
|
|
public:
|
2018-12-03 11:28:22 +00:00
|
|
|
String getName() const override { return name; }
|
2018-11-21 10:10:22 +00:00
|
|
|
|
|
|
|
bool isVariadic() const override { return false; }
|
|
|
|
size_t getNumberOfArguments() const override { return 2; }
|
2018-12-19 01:29:40 +00:00
|
|
|
bool useDefaultImplementationForConstants() const override { return true; }
|
2018-11-21 10:10:22 +00:00
|
|
|
ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; }
|
|
|
|
|
2018-12-03 11:28:22 +00:00
|
|
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
|
|
|
{
|
2018-11-30 11:15:58 +00:00
|
|
|
const DataTypePtr & type_x = arguments[0];
|
|
|
|
|
2019-05-24 12:11:03 +00:00
|
|
|
if (!isNumber(type_x))
|
2018-11-30 11:15:58 +00:00
|
|
|
throw Exception{"Unsupported type " + type_x->getName()
|
|
|
|
+ " of first argument of function " + getName()
|
|
|
|
+ ", must be numeric type.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
|
|
|
|
|
|
|
const DataTypeArray * type_arr = checkAndGetDataType<DataTypeArray>(arguments[1].get());
|
|
|
|
|
|
|
|
if (!type_arr)
|
|
|
|
throw Exception{"Second argument of function " + getName()
|
|
|
|
+ ", must be array of boundaries to round to.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
|
|
|
|
|
|
|
const auto type_arr_nested = type_arr->getNestedType();
|
|
|
|
|
2019-05-24 12:11:03 +00:00
|
|
|
if (!isNumber(type_arr_nested))
|
2018-11-30 11:15:58 +00:00
|
|
|
{
|
|
|
|
throw Exception{"Elements of array of second argument of function " + getName()
|
|
|
|
+ " must be numeric type.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
|
|
|
}
|
|
|
|
return getLeastSupertype({type_x, type_arr_nested});
|
2018-11-29 00:32:52 +00:00
|
|
|
}
|
2018-11-21 10:10:22 +00:00
|
|
|
|
2018-12-19 01:29:40 +00:00
|
|
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t) override
|
2018-12-03 11:28:22 +00:00
|
|
|
{
|
2018-11-30 15:57:06 +00:00
|
|
|
auto in_column = block.getByPosition(arguments[0]).column;
|
|
|
|
const auto & in_type = block.getByPosition(arguments[0]).type;
|
2018-11-30 11:15:58 +00:00
|
|
|
|
2018-12-19 02:43:09 +00:00
|
|
|
auto array_column = block.getByPosition(arguments[1]).column;
|
|
|
|
const auto & array_type = block.getByPosition(arguments[1]).type;
|
|
|
|
|
2018-11-30 15:57:06 +00:00
|
|
|
const auto & return_type = block.getByPosition(result).type;
|
|
|
|
auto column_result = return_type->createColumn();
|
2018-11-29 00:32:52 +00:00
|
|
|
auto out = column_result.get();
|
|
|
|
|
2018-11-30 15:57:06 +00:00
|
|
|
if (!in_type->equals(*return_type))
|
|
|
|
in_column = castColumn(block.getByPosition(arguments[0]), return_type, context);
|
2018-12-19 02:43:09 +00:00
|
|
|
|
|
|
|
if (!array_type->equals(*return_type))
|
|
|
|
array_column = castColumn(block.getByPosition(arguments[1]), std::make_shared<DataTypeArray>(return_type), context);
|
2018-11-30 15:57:06 +00:00
|
|
|
|
|
|
|
const auto in = in_column.get();
|
2018-12-19 02:43:09 +00:00
|
|
|
auto boundaries = typeid_cast<const ColumnConst &>(*array_column).getValue<Array>();
|
2018-12-19 01:29:40 +00:00
|
|
|
size_t num_boundaries = boundaries.size();
|
|
|
|
if (!num_boundaries)
|
|
|
|
throw Exception("Empty array is illegal for boundaries in " + getName() + " function", ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
|
2018-11-30 11:15:58 +00:00
|
|
|
if (!executeNum<UInt8>(in, out, boundaries)
|
|
|
|
&& !executeNum<UInt16>(in, out, boundaries)
|
|
|
|
&& !executeNum<UInt32>(in, out, boundaries)
|
|
|
|
&& !executeNum<UInt64>(in, out, boundaries)
|
|
|
|
&& !executeNum<Int8>(in, out, boundaries)
|
|
|
|
&& !executeNum<Int16>(in, out, boundaries)
|
|
|
|
&& !executeNum<Int32>(in, out, boundaries)
|
|
|
|
&& !executeNum<Int64>(in, out, boundaries)
|
|
|
|
&& !executeNum<Float32>(in, out, boundaries)
|
2018-12-19 02:11:31 +00:00
|
|
|
&& !executeNum<Float64>(in, out, boundaries)
|
|
|
|
&& !executeDecimal<Decimal32>(in, out, boundaries)
|
|
|
|
&& !executeDecimal<Decimal64>(in, out, boundaries)
|
|
|
|
&& !executeDecimal<Decimal128>(in, out, boundaries))
|
2018-11-30 11:15:58 +00:00
|
|
|
{
|
|
|
|
throw Exception{"Illegal column " + in->getName() + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN};
|
|
|
|
}
|
2018-11-29 00:32:52 +00:00
|
|
|
|
|
|
|
block.getByPosition(result).column = std::move(column_result);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template <typename T>
|
2018-12-03 11:28:22 +00:00
|
|
|
bool executeNum(const IColumn * in_untyped, IColumn * out_untyped, const Array & boundaries)
|
|
|
|
{
|
2018-11-30 11:15:58 +00:00
|
|
|
const auto in = checkAndGetColumn<ColumnVector<T>>(in_untyped);
|
|
|
|
auto out = typeid_cast<ColumnVector<T> *>(out_untyped);
|
|
|
|
if (!in || !out)
|
|
|
|
return false;
|
2018-12-19 02:11:31 +00:00
|
|
|
|
2018-11-30 11:15:58 +00:00
|
|
|
executeImplNumToNum(in->getData(), out->getData(), boundaries);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2018-12-19 02:11:31 +00:00
|
|
|
bool executeDecimal(const IColumn * in_untyped, IColumn * out_untyped, const Array & boundaries)
|
|
|
|
{
|
|
|
|
const auto in = checkAndGetColumn<ColumnDecimal<T>>(in_untyped);
|
|
|
|
auto out = typeid_cast<ColumnDecimal<T> *>(out_untyped);
|
|
|
|
if (!in || !out)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
executeImplNumToNum(in->getData(), out->getData(), boundaries);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Container>
|
|
|
|
void executeImplNumToNum(const Container & src, Container & dst, const Array & boundaries)
|
2018-12-03 11:28:22 +00:00
|
|
|
{
|
2018-12-19 02:11:31 +00:00
|
|
|
using ValueType = typename Container::value_type;
|
|
|
|
std::vector<ValueType> boundary_values(boundaries.size());
|
2018-12-03 11:28:22 +00:00
|
|
|
for (size_t i = 0; i < boundaries.size(); ++i)
|
2018-12-19 02:11:31 +00:00
|
|
|
boundary_values[i] = boundaries[i].get<ValueType>();
|
2018-12-19 01:29:40 +00:00
|
|
|
|
|
|
|
std::sort(boundary_values.begin(), boundary_values.end());
|
|
|
|
boundary_values.erase(std::unique(boundary_values.begin(), boundary_values.end()), boundary_values.end());
|
2018-11-30 15:57:06 +00:00
|
|
|
|
2018-11-21 10:10:22 +00:00
|
|
|
size_t size = src.size();
|
|
|
|
dst.resize(size);
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
{
|
2018-12-19 01:29:40 +00:00
|
|
|
auto it = std::upper_bound(boundary_values.begin(), boundary_values.end(), src[i]);
|
|
|
|
if (it == boundary_values.end())
|
2018-11-21 10:10:22 +00:00
|
|
|
{
|
2018-12-19 01:29:40 +00:00
|
|
|
dst[i] = boundary_values.back();
|
2018-11-21 10:10:22 +00:00
|
|
|
}
|
2018-12-19 01:29:40 +00:00
|
|
|
else if (it == boundary_values.begin())
|
2018-11-30 11:15:58 +00:00
|
|
|
{
|
2018-12-19 01:29:40 +00:00
|
|
|
dst[i] = boundary_values.front();
|
2018-11-21 10:10:22 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-11-30 15:57:06 +00:00
|
|
|
dst[i] = *(it - 1);
|
2018-11-21 10:10:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-30 15:57:06 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
const Context & context;
|
2018-11-21 10:10:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-07-30 19:47:32 +00:00
|
|
|
struct NameRound { static constexpr auto name = "round"; };
|
|
|
|
struct NameCeil { static constexpr auto name = "ceil"; };
|
|
|
|
struct NameFloor { static constexpr auto name = "floor"; };
|
2017-09-16 16:38:27 +00:00
|
|
|
struct NameTrunc { static constexpr auto name = "trunc"; };
|
2014-11-12 17:23:26 +00:00
|
|
|
|
2017-09-16 16:38:27 +00:00
|
|
|
using FunctionRound = FunctionRounding<NameRound, RoundingMode::Round>;
|
|
|
|
using FunctionFloor = FunctionRounding<NameFloor, RoundingMode::Floor>;
|
|
|
|
using FunctionCeil = FunctionRounding<NameCeil, RoundingMode::Ceil>;
|
|
|
|
using FunctionTrunc = FunctionRounding<NameTrunc, RoundingMode::Trunc>;
|
2015-11-29 08:06:29 +00:00
|
|
|
|
2012-12-24 08:16:25 +00:00
|
|
|
}
|