#include #include #include #include #include namespace { template constexpr T abs(T value) noexcept { if constexpr (std::is_signed_v) { if (value >= 0 || value == std::numeric_limits::min()) return value; return -value; } else return value; } } namespace DB { namespace ErrorCodes { extern const int NOT_IMPLEMENTED; } template struct LCMImpl { using ResultType = typename NumberTraits::ResultOfAdditionMultiplication::Type; static const constexpr bool allow_fixed_string = false; template static inline std::enable_if_t || is_big_int_v, Result> apply([[maybe_unused]] A a, [[maybe_unused]] B b) { throw Exception("LCM is not implemented for big integers", ErrorCodes::NOT_IMPLEMENTED); } template static inline std::enable_if_t && !is_big_int_v, Result> apply([[maybe_unused]] A a, [[maybe_unused]] B b) { throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger::Type(a), typename NumberTraits::ToInteger::Type(b)); throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger::Type(b), typename NumberTraits::ToInteger::Type(a)); /** It's tempting to use std::lcm function. * But it has undefined behaviour on overflow. * And assert in debug build. * We need some well defined behaviour instead * (example: throw an exception or overflow in implementation specific way). */ using Int = typename NumberTraits::ToInteger::Type; using Unsigned = make_unsigned_t; Unsigned val1 = abs(a) / std::gcd(Int(a), Int(b)); Unsigned val2 = abs(b); /// Overflow in implementation specific way. return Result(val1 * val2); } #if USE_EMBEDDED_COMPILER static constexpr bool compilable = false; /// exceptions (and a non-trivial algorithm) #endif }; struct NameLCM { static constexpr auto name = "lcm"; }; using FunctionLCM = FunctionBinaryArithmetic; void registerFunctionLCM(FunctionFactory & factory) { factory.registerFunction(); } }