#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 { struct NameLCM { static constexpr auto name = "lcm"; }; template struct LCMImpl : public GCDLCMImpl, NameLCM> { using ResultType = typename GCDLCMImpl, NameLCM>::ResultType; static ResultType applyImpl(A a, B b) { using Int = typename NumberTraits::ToInteger::Type; using Unsigned = make_unsigned_t; /** 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). */ Unsigned val1 = abs(a) / boost::integer::gcd(Int(a), Int(b)); // NOLINT(clang-analyzer-core.UndefinedBinaryOperatorResult) Unsigned val2 = abs(b); /// Overflow in implementation specific way. return ResultType(val1 * val2); } }; using FunctionLCM = BinaryArithmeticOverloadResolver; } REGISTER_FUNCTION(LCM) { factory.registerFunction(); } }