// 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. #include #include #include #include #include #include #include "contrib/libs/apache/arrow_next/cpp/src/arrow/compare.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/compute/api_scalar.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/compute/cast.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/compute/kernels/base_arithmetic_internal.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/compute/kernels/codegen_internal.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/compute/kernels/common_internal.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/compute/kernels/util_internal.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/type.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/type_fwd.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/type_traits.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/decimal.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/int_util_overflow.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/util/macros.h" #include "contrib/libs/apache/arrow_next/cpp/src/arrow/visit_scalar_inline.h" namespace arrow20 { using internal::AddWithOverflow; using internal::DivideWithOverflow; using internal::MultiplyWithOverflow; using internal::NegateWithOverflow; using internal::SubtractWithOverflow; namespace compute { namespace internal { using applicator::ScalarBinary; using applicator::ScalarBinaryEqualTypes; using applicator::ScalarBinaryNotNull; using applicator::ScalarBinaryNotNullEqualTypes; using applicator::ScalarUnary; using applicator::ScalarUnaryNotNull; using applicator::ScalarUnaryNotNullStateful; namespace { // Bitwise operations struct BitWiseNot { template static T Call(KernelContext*, Arg arg, Status*) { return ~arg; } }; struct BitWiseAnd { template static T Call(KernelContext*, Arg0 lhs, Arg1 rhs, Status*) { return lhs & rhs; } }; struct BitWiseOr { template static T Call(KernelContext*, Arg0 lhs, Arg1 rhs, Status*) { return lhs | rhs; } }; struct BitWiseXor { template static T Call(KernelContext*, Arg0 lhs, Arg1 rhs, Status*) { return lhs ^ rhs; } }; struct ShiftLeft { template static T Call(KernelContext*, Arg0 lhs, Arg1 rhs, Status*) { using Unsigned = typename std::make_unsigned::type; static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(rhs < 0 || rhs >= std::numeric_limits::digits)) { return lhs; } return static_cast(static_cast(lhs) << static_cast(rhs)); } }; // See SEI CERT C Coding Standard rule INT34-C struct ShiftLeftChecked { template static enable_if_unsigned_integer_value Call(KernelContext*, Arg0 lhs, Arg1 rhs, Status* st) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(rhs < 0 || rhs >= std::numeric_limits::digits)) { *st = Status::Invalid("shift amount must be >= 0 and less than precision of type"); return lhs; } return lhs << rhs; } template static enable_if_signed_integer_value Call(KernelContext*, Arg0 lhs, Arg1 rhs, Status* st) { using Unsigned = typename std::make_unsigned::type; static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(rhs < 0 || rhs >= std::numeric_limits::digits)) { *st = Status::Invalid("shift amount must be >= 0 and less than precision of type"); return lhs; } // In C/C++ left shift of a negative number is undefined (C++11 standard 5.8.2) // Mimic Java/etc. and treat left shift as based on two's complement representation // Assumes two's complement machine return static_cast(static_cast(lhs) << static_cast(rhs)); } }; struct ShiftRight { template static T Call(KernelContext*, Arg0 lhs, Arg1 rhs, Status*) { static_assert(std::is_same::value, ""); // Logical right shift when Arg0 is unsigned // Arithmetic otherwise (this is implementation-defined but GCC and MSVC document this // as arithmetic right shift) // https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation // https://docs.microsoft.com/en-us/cpp/cpp/left-shift-and-right-shift-operators-input-and-output?view=msvc-160 // Clang doesn't document their behavior. if (ARROW_PREDICT_FALSE(rhs < 0 || rhs >= std::numeric_limits::digits)) { return lhs; } return lhs >> rhs; } }; struct ShiftRightChecked { template static T Call(KernelContext*, Arg0 lhs, Arg1 rhs, Status* st) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(rhs < 0 || rhs >= std::numeric_limits::digits)) { *st = Status::Invalid("shift amount must be >= 0 and less than precision of type"); return lhs; } return lhs >> rhs; } }; struct Sin { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); return std::sin(val); } }; struct SinChecked { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status* st) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(std::isinf(val))) { *st = Status::Invalid("domain error"); return val; } return std::sin(val); } }; struct Sinh { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); return std::sinh(val); } }; struct Cos { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); return std::cos(val); } }; struct CosChecked { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status* st) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(std::isinf(val))) { *st = Status::Invalid("domain error"); return val; } return std::cos(val); } }; struct Cosh { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); return std::cosh(val); } }; struct Tan { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); return std::tan(val); } }; struct TanChecked { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status* st) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(std::isinf(val))) { *st = Status::Invalid("domain error"); return val; } // Cannot raise range errors (overflow) since PI/2 is not exactly representable return std::tan(val); } }; struct Tanh { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); return std::tanh(val); } }; struct Asin { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(val < -1.0 || val > 1.0)) { return std::numeric_limits::quiet_NaN(); } return std::asin(val); } }; struct AsinChecked { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status* st) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(val < -1.0 || val > 1.0)) { *st = Status::Invalid("domain error"); return val; } return std::asin(val); } }; struct Asinh { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); return std::asinh(val); } }; struct Acos { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE((val < -1.0 || val > 1.0))) { return std::numeric_limits::quiet_NaN(); } return std::acos(val); } }; struct AcosChecked { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status* st) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE((val < -1.0 || val > 1.0))) { *st = Status::Invalid("domain error"); return val; } return std::acos(val); } }; struct Acosh { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(val < 1.0)) { return std::numeric_limits::quiet_NaN(); } return std::acosh(val); } }; struct AcoshChecked { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status* st) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE(val < 1.0)) { *st = Status::Invalid("domain error"); return val; } return std::acosh(val); } }; struct Atan { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); return std::atan(val); } }; struct Atanh { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status*) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE((val < -1.0 || val > 1.0))) { // N.B. This predicate does *not* match the predicate in AtanhChecked. In // GH-44630 it was decided that the checked version should error when asked // for +/- 1 as an input and the unchecked version should return +/- oo return std::numeric_limits::quiet_NaN(); } return std::atanh(val); } }; struct AtanhChecked { template static enable_if_floating_value Call(KernelContext*, Arg0 val, Status* st) { static_assert(std::is_same::value, ""); if (ARROW_PREDICT_FALSE((val <= -1.0 || val >= 1.0))) { // N.B. This predicate does *not* match the predicate in Atanh. In GH-44630 it was // decided that the checked version should error when asked for +/- 1 as an input // and the unchecked version should return +/- oo *st = Status::Invalid("domain error"); return val; } return std::atanh(val); } }; struct Atan2 { template static enable_if_floating_value Call(KernelContext*, Arg0 y, Arg1 x, Status*) { static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); return std::atan2(y, x); } }; struct LogNatural { template static enable_if_floating_value Call(KernelContext*, Arg arg, Status*) { static_assert(std::is_same::value, ""); if (arg == 0.0) { return -std::numeric_limits::infinity(); } else if (arg < 0.0) { return std::numeric_limits::quiet_NaN(); } return std::log(arg); } }; struct LogNaturalChecked { template static enable_if_floating_value Call(KernelContext*, Arg arg, Status* st) { static_assert(std::is_same::value, ""); if (arg == 0.0) { *st = Status::Invalid("logarithm of zero"); return arg; } else if (arg < 0.0) { *st = Status::Invalid("logarithm of negative number"); return arg; } return std::log(arg); } }; struct Log10 { template static enable_if_floating_value Call(KernelContext*, Arg arg, Status*) { static_assert(std::is_same::value, ""); if (arg == 0.0) { return -std::numeric_limits::infinity(); } else if (arg < 0.0) { return std::numeric_limits::quiet_NaN(); } return std::log10(arg); } }; struct Log10Checked { template static enable_if_floating_value Call(KernelContext*, Arg arg, Status* st) { static_assert(std::is_same::value, ""); if (arg == 0) { *st = Status::Invalid("logarithm of zero"); return arg; } else if (arg < 0) { *st = Status::Invalid("logarithm of negative number"); return arg; } return std::log10(arg); } }; struct Log2 { template static enable_if_floating_value Call(KernelContext*, Arg arg, Status*) { static_assert(std::is_same::value, ""); if (arg == 0.0) { return -std::numeric_limits::infinity(); } else if (arg < 0.0) { return std::numeric_limits::quiet_NaN(); } return std::log2(arg); } }; struct Log2Checked { template static enable_if_floating_value Call(KernelContext*, Arg arg, Status* st) { static_assert(std::is_same::value, ""); if (arg == 0.0) { *st = Status::Invalid("logarithm of zero"); return arg; } else if (arg < 0.0) { *st = Status::Invalid("logarithm of negative number"); return arg; } return std::log2(arg); } }; struct Log1p { template static enable_if_floating_value Call(KernelContext*, Arg arg, Status*) { static_assert(std::is_same::value, ""); if (arg == -1) { return -std::numeric_limits::infinity(); } else if (arg < -1) { return std::numeric_limits::quiet_NaN(); } return std::log1p(arg); } }; struct Log1pChecked { template static enable_if_floating_value Call(KernelContext*, Arg arg, Status* st) { static_assert(std::is_same::value, ""); if (arg == -1) { *st = Status::Invalid("logarithm of zero"); return arg; } else if (arg < -1) { *st = Status::Invalid("logarithm of negative number"); return arg; } return std::log1p(arg); } }; struct Logb { template static enable_if_floating_value Call(KernelContext*, Arg0 x, Arg1 base, Status*) { static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); if (x == 0.0) { if (base == 0.0 || base < 0.0) { return std::numeric_limits::quiet_NaN(); } else { return -std::numeric_limits::infinity(); } } else if (x < 0.0) { return std::numeric_limits::quiet_NaN(); } return std::log(x) / std::log(base); } }; struct LogbChecked { template static enable_if_floating_value Call(KernelContext*, Arg0 x, Arg1 base, Status* st) { static_assert(std::is_same::value, ""); static_assert(std::is_same::value, ""); if (x == 0.0 || base == 0.0) { *st = Status::Invalid("logarithm of zero"); return x; } else if (x < 0.0 || base < 0.0) { *st = Status::Invalid("logarithm of negative number"); return x; } return std::log(x) / std::log(base); } }; // Generate a kernel given a bitwise arithmetic functor. Assumes the // functor treats all integer types of equal width identically template