blob: b2562e955acd50b74eb6fee1f1716e4fbedbf463 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
#include "arrow/util/logging_internal.h"
#include "gandiva/precompiled/decimal_ops.h"
extern "C" {
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "./types.h"
// Expand the inner fn for types that support extended math.
#define ENUMERIC_TYPES_UNARY(INNER, OUT_TYPE) \
INNER(int32, OUT_TYPE) \
INNER(uint32, OUT_TYPE) \
INNER(int64, OUT_TYPE) \
INNER(uint64, OUT_TYPE) \
INNER(float32, OUT_TYPE) \
INNER(float64, OUT_TYPE)
// Cubic root
#define CBRT(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE cbrt_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_float64>(cbrtl(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(CBRT, float64)
// Exponent
#define EXP(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE exp_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_float64>(expl(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(EXP, float64)
// log
#define LOG(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE log_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_float64>(logl(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(LOG, float64)
// log base 10
#define LOG10(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE log10_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_float64>(log10l(static_cast<long double>(in))); \
}
#define LOGL(VALUE) static_cast<gdv_float64>(logl(static_cast<long double>(VALUE)))
ENUMERIC_TYPES_UNARY(LOG10, float64)
FORCE_INLINE
void set_error_for_logbase(int64_t execution_context, double base) {
const char* prefix = "divide by zero error with log of base";
int size = static_cast<int>(strlen(prefix)) + 64;
char* error = reinterpret_cast<char*>(malloc(size));
snprintf(error, size, "%s %f", prefix, base);
gdv_fn_context_set_error_msg(execution_context, error);
free(static_cast<char*>(error));
}
// log with base
#define LOG_WITH_BASE(IN_TYPE1, IN_TYPE2, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE log_##IN_TYPE1##_##IN_TYPE2(gdv_int64 context, gdv_##IN_TYPE1 base, \
gdv_##IN_TYPE2 value) { \
gdv_##OUT_TYPE log_of_base = LOGL(base); \
if (log_of_base == 0) { \
set_error_for_logbase(context, static_cast<gdv_float64>(base)); \
return 0; \
} \
return LOGL(value) / log_of_base; \
}
LOG_WITH_BASE(int32, int32, float64)
LOG_WITH_BASE(uint32, uint32, float64)
LOG_WITH_BASE(int64, int64, float64)
LOG_WITH_BASE(uint64, uint64, float64)
LOG_WITH_BASE(float32, float32, float64)
LOG_WITH_BASE(float64, float64, float64)
// Sin
#define SIN(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE sin_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(sin(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(SIN, float64)
// Asin
#define ASIN(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE asin_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(asin(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(ASIN, float64)
// Cos
#define COS(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE cos_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(cos(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(COS, float64)
// Acos
#define ACOS(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE acos_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(acos(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(ACOS, float64)
// Tan
#define TAN(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE tan_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(tan(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(TAN, float64)
// Atan
#define ATAN(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE atan_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(atan(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(ATAN, float64)
// Sinh
#define SINH(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE sinh_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(sinh(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(SINH, float64)
// Cosh
#define COSH(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE cosh_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(cosh(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(COSH, float64)
// Tanh
#define TANH(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE tanh_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(tanh(static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(TANH, float64)
// Atan2
#define ATAN2(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE atan2_##IN_TYPE##_##IN_TYPE(gdv_##IN_TYPE in1, gdv_##IN_TYPE in2) { \
return static_cast<gdv_##OUT_TYPE>( \
atan2(static_cast<long double>(in1), static_cast<long double>(in2))); \
}
ENUMERIC_TYPES_UNARY(ATAN2, float64)
// Cot
#define COT(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE cot_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(tan(M_PI / 2 - static_cast<long double>(in))); \
}
ENUMERIC_TYPES_UNARY(COT, float64)
// Radians
#define RADIANS(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE radians_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(static_cast<long double>(in) * M_PI / 180.0); \
}
ENUMERIC_TYPES_UNARY(RADIANS, float64)
// Degrees
#define DEGREES(IN_TYPE, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE degrees_##IN_TYPE(gdv_##IN_TYPE in) { \
return static_cast<gdv_##OUT_TYPE>(static_cast<long double>(in) * 180.0 / M_PI); \
}
ENUMERIC_TYPES_UNARY(DEGREES, float64)
// power
#define POWER(IN_TYPE1, IN_TYPE2, OUT_TYPE) \
FORCE_INLINE \
gdv_##OUT_TYPE power_##IN_TYPE1##_##IN_TYPE2(gdv_##IN_TYPE1 in1, gdv_##IN_TYPE2 in2) { \
return static_cast<gdv_float64>(powl(in1, in2)); \
}
POWER(float64, float64, float64)
FORCE_INLINE
gdv_int32 round_int32(gdv_int32 num) { return num; }
FORCE_INLINE
gdv_int64 round_int64(gdv_int64 num) { return num; }
// Lookup table to make the factorial function to execute faster
// It is used because the range of values for the function is limited to [0!-20!]
static const int64_t kFactorialLookupTable[] = {1,
1,
2,
6,
24,
120,
720,
5040,
40320,
362880,
3628800,
39916800,
479001600,
6227020800,
87178291200,
1307674368000,
20922789888000,
355687428096000,
6402373705728000,
121645100408832000,
2432902008176640000};
#define FACTORIAL(IN_TYPE) \
FORCE_INLINE \
gdv_int64 factorial_##IN_TYPE(gdv_int64 ctx, gdv_##IN_TYPE value) { \
if (value < 0) { \
gdv_fn_context_set_error_msg(ctx, "Factorial of negative number not exist!"); \
return 0; \
} \
/* For numbers greater than 20 causes an overflow. */ \
if (value > 20) { \
gdv_fn_context_set_error_msg(ctx, "Numbers greater than 20 cause overflow!"); \
return 0; \
} \
\
return kFactorialLookupTable[static_cast<int32_t>(value)]; \
}
FACTORIAL(int32)
FACTORIAL(int64)
// rounds the number to the nearest integer
#define ROUND_DECIMAL(TYPE) \
FORCE_INLINE \
gdv_##TYPE round_##TYPE(gdv_##TYPE num) { \
return static_cast<gdv_##TYPE>(trunc(num + ((num >= 0) ? 0.5 : -0.5))); \
}
ROUND_DECIMAL(float32)
ROUND_DECIMAL(float64)
// rounds the number to the nearest integer
FORCE_INLINE
gdv_float64 bround_float64(gdv_float64 num) {
gdv_float64 round_num = round(num);
gdv_float64 diff_num = round_num - num;
if ((diff_num != 0.5) && (diff_num != -0.5)) {
return round_num;
}
if (fmod(round_num, 2.0) == 0.0) {
return round_num;
}
return num - diff_num;
}
// rounds the number to the given scale
#define ROUND_DECIMAL_TO_SCALE(TYPE) \
FORCE_INLINE \
gdv_##TYPE round_##TYPE##_int32(gdv_##TYPE number, gdv_int32 out_scale) { \
gdv_float64 scale_multiplier = get_scale_multiplier(out_scale); \
return static_cast<gdv_##TYPE>( \
trunc(number * scale_multiplier + ((number >= 0) ? 0.5 : -0.5)) / \
scale_multiplier); \
}
ROUND_DECIMAL_TO_SCALE(float32)
ROUND_DECIMAL_TO_SCALE(float64)
FORCE_INLINE
gdv_int32 round_int32_int32(gdv_int32 number, gdv_int32 precision) {
// for integers, there is nothing following the decimal point,
// so round() always returns the same number if precision >= 0
if (precision >= 0) {
return number;
}
gdv_int32 abs_precision = -precision;
// This is to ensure that there is no overflow while calculating 10^precision, 9 is
// the smallest N for which 10^N does not fit into 32 bits, so we can safely return 0
if (abs_precision > 9) {
return 0;
}
gdv_int32 num_sign = (number > 0) ? 1 : -1;
gdv_int32 abs_number = number * num_sign;
gdv_int32 power_of_10 = static_cast<gdv_int32>(get_power_of_10(abs_precision));
gdv_int32 remainder = abs_number % power_of_10;
abs_number -= remainder;
// if the fractional part of the quotient >= 0.5, round to next higher integer
if (remainder >= power_of_10 / 2) {
abs_number += power_of_10;
}
return abs_number * num_sign;
}
FORCE_INLINE
gdv_int64 round_int64_int32(gdv_int64 number, gdv_int32 precision) {
// for long integers, there is nothing following the decimal point,
// so round() always returns the same number if precision >= 0
if (precision >= 0) {
return number;
}
gdv_int32 abs_precision = -precision;
// This is to ensure that there is no overflow while calculating 10^precision, 19 is
// the smallest N for which 10^N does not fit into 64 bits, so we can safely return 0
if (abs_precision > 18) {
return 0;
}
gdv_int32 num_sign = (number > 0) ? 1 : -1;
gdv_int64 abs_number = number * num_sign;
gdv_int64 power_of_10 = get_power_of_10(abs_precision);
gdv_int64 remainder = abs_number % power_of_10;
abs_number -= remainder;
// if the fractional part of the quotient >= 0.5, round to next higher integer
if (remainder >= power_of_10 / 2) {
abs_number += power_of_10;
}
return abs_number * num_sign;
}
FORCE_INLINE
gdv_int64 get_power_of_10(gdv_int32 exp) {
DCHECK_GE(exp, 0);
DCHECK_LE(exp, 18);
static const gdv_int64 power_of_10[] = {1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000};
return power_of_10[exp];
}
FORCE_INLINE
gdv_int64 truncate_int64_int32(gdv_int64 in, gdv_int32 out_scale) {
bool overflow = false;
arrow::BasicDecimal128 decimal = gandiva::decimalops::FromInt64(in, 38, 0, &overflow);
arrow::BasicDecimal128 decimal_with_outscale =
gandiva::decimalops::Truncate(gandiva::BasicDecimalScalar128(decimal, 38, 0), 38,
out_scale, out_scale, &overflow);
if (out_scale < 0) {
out_scale = 0;
}
return gandiva::decimalops::ToInt64(
gandiva::BasicDecimalScalar128(decimal_with_outscale, 38, out_scale), &overflow);
}
FORCE_INLINE
gdv_float64 get_scale_multiplier(gdv_int32 scale) {
static const gdv_float64 values[] = {1.0,
10.0,
100.0,
1000.0,
10000.0,
100000.0,
1000000.0,
10000000.0,
100000000.0,
1000000000.0,
10000000000.0,
100000000000.0,
1000000000000.0,
10000000000000.0,
100000000000000.0,
1000000000000000.0,
10000000000000000.0,
100000000000000000.0,
1000000000000000000.0,
10000000000000000000.0};
if (scale >= 0 && scale < 20) {
return values[scale];
}
return power_float64_float64(10.0, scale);
}
// returns the binary representation of a given integer (e.g. 928 -> 1110100000)
#define BIN_INTEGER(IN_TYPE) \
FORCE_INLINE \
const char* bin_##IN_TYPE(int64_t context, gdv_##IN_TYPE value, int32_t* out_len) { \
*out_len = 0; \
int32_t len = 8 * sizeof(value); \
char* ret = reinterpret_cast<char*>(gdv_fn_context_arena_malloc(context, len)); \
if (ret == nullptr) { \
gdv_fn_context_set_error_msg(context, "Could not allocate memory for output"); \
return ""; \
} \
/* handle case when value is zero */ \
if (value == 0) { \
*out_len = 1; \
ret[0] = '0'; \
return ret; \
} \
/* generate binary representation iteratively */ \
gdv_u##IN_TYPE i; \
int8_t count = 0; \
bool first = false; /* flag for not printing left zeros in positive numbers */ \
for (i = static_cast<gdv_u##IN_TYPE>(1) << (len - 1); i > 0; i = i / 2) { \
if ((value & i) != 0) { \
ret[count] = '1'; \
if (!first) first = true; \
} else { \
if (!first) continue; \
ret[count] = '0'; \
} \
count += 1; \
} \
*out_len = count; \
return ret; \
}
BIN_INTEGER(int32)
BIN_INTEGER(int64)
#undef BIN_INTEGER
} // extern "C"