From f2cbda004df71183d6ef2582d71d995e6028c55b Mon Sep 17 00:00:00 2001 From: wjr <1966336874@qq.com> Date: Fri, 16 Aug 2024 06:19:21 +0800 Subject: [PATCH] update --- include/wjr/assert.hpp | 4 +- include/wjr/biginteger/biginteger.hpp | 12 +- include/wjr/container/generic/bplus_tree.hpp | 16 +- include/wjr/crtp/class_base.hpp | 8 +- include/wjr/expected.hpp | 4 +- include/wjr/format/charconv-impl.hpp | 5 +- include/wjr/format/dragonbox.hpp | 3797 +++++++++++++++++ include/wjr/format/fastfloat.hpp | 16 +- include/wjr/json/json.hpp | 2 +- include/wjr/math/bit.hpp | 1 + include/wjr/math/div.hpp | 6 +- include/wjr/math/rotate.hpp | 34 + include/wjr/math/sub.hpp | 8 +- include/wjr/math/uint128_t.hpp | 22 + .../wjr/preprocessor/compiler/attribute.hpp | 18 +- .../wjr/x86/container/generic/bplus_tree.hpp | 20 +- include/wjr/x86/math/find.hpp | 8 +- include/wjr/x86/math/large-compare-impl.hpp | 8 +- include/wjr/x86/math/large-find-impl.hpp | 16 +- include/wjr/x86/math/not.hpp | 2 +- include/wjr/x86/math/shift.hpp | 4 +- src/wjr/format/dragonbox.cpp | 469 ++ src/wjr/json/lexer.cpp | 2 +- src/wjr/math/div.cpp | 6 +- src/wjr/math/mul.cpp | 18 +- src/wjr/x86/json/lexer.cpp | 2 +- src/wjr/x86/json/string.cpp | 4 +- 27 files changed, 4424 insertions(+), 88 deletions(-) create mode 100644 include/wjr/format/dragonbox.hpp create mode 100644 include/wjr/math/rotate.hpp create mode 100644 src/wjr/format/dragonbox.cpp diff --git a/include/wjr/assert.hpp b/include/wjr/assert.hpp index 3d7602fe..02357492 100644 --- a/include/wjr/assert.hpp +++ b/include/wjr/assert.hpp @@ -77,12 +77,12 @@ inline void __assert_handler(const char *expr, const char *file, const char *fun ::wjr::__assert_handler(#expr, WJR_FILE, WJR_CURRENT_FUNCTION, WJR_LINE, \ ##__VA_ARGS__); \ } \ - } while (0) + } while (false) // do nothing #define WJR_ASSERT_UNCHECK_I(expr, ...) \ do { \ - } while (0) + } while (false) // level = [0, 2] // The higher the level, the less likely it is to be detected diff --git a/include/wjr/biginteger/biginteger.hpp b/include/wjr/biginteger/biginteger.hpp index ebf32ea5..75963ae1 100644 --- a/include/wjr/biginteger/biginteger.hpp +++ b/include/wjr/biginteger/biginteger.hpp @@ -1605,7 +1605,7 @@ from_chars_result __from_chars_impl(const char *first, const char dssize = __fasts_conditional_negate(sign, dssize); dst->set_ssize(dssize); return {first, std::errc{}}; - } while (0); + } while (false); dst->clear(); return {__first, std::errc::invalid_argument}; @@ -2065,7 +2065,7 @@ void __addsubmul_impl(basic_biginteger *dst, const biginteger_data *lhs, uint } new_dusize = normalize(dp, new_dusize); - } while (0); + } while (false); } dst->set_ssize(__fasts_conditional_negate(dssize < 0, new_dusize)); @@ -3040,7 +3040,7 @@ void __cfdiv_r_2exp_impl(basic_biginteger *rem, const biginteger_data *num, rem->set_ssize(0); return; - } while (0); + } while (false); rem->reserve(offset + 1); rp = rem->data(); @@ -3082,7 +3082,7 @@ void __urandom_bit_impl(basic_biginteger *dst, uint32_t size, for (uint64_t i = 0; i < dusize; ++i) { dp[i] = engine(); } - } while (0); + } while (false); if (size != 0) { dp[dusize] = engine() >> (64 - size); @@ -3112,7 +3112,7 @@ void __urandom_exact_bit_impl(basic_biginteger *dst, uint32_t size, for (uint64_t i = 0; i < dusize; ++i) { dp[i] = engine(); } - } while (0); + } while (false); do { uint64_t high = (uint64_t)(1) << size; @@ -3122,7 +3122,7 @@ void __urandom_exact_bit_impl(basic_biginteger *dst, uint32_t size, } dp[dusize] = high; - } while (0); + } while (false); dst->set_ssize(dssize); } diff --git a/include/wjr/container/generic/bplus_tree.hpp b/include/wjr/container/generic/bplus_tree.hpp index 9a0e129d..e0221611 100644 --- a/include/wjr/container/generic/bplus_tree.hpp +++ b/include/wjr/container/generic/bplus_tree.hpp @@ -938,7 +938,7 @@ class basic_bplus_tree { } leaf = static_cast(node); - } while (0); + } while (false); unsigned int pos = iter.get_pos(); unsigned int cur_size = -leaf->m_size; @@ -1047,7 +1047,7 @@ class basic_bplus_tree { NOT_LEFTMOST_AT_INNER: pos = __search(current->as_inner(), cur_size, key, comp); - } while (0); + } while (false); current = current->as_inner()->m_sons[pos]; cur_size = current->m_size; @@ -1123,7 +1123,7 @@ class basic_bplus_tree { return B; \ } \ } \ - } while (0) + } while (false) #define WJR_REGISTER_BLPUS_SEARCH_4(A, B, C, D, E) \ do { \ if constexpr (E > Max) { \ @@ -1158,7 +1158,7 @@ class basic_bplus_tree { return D; \ } \ } \ - } while (0) + } while (false) WJR_REGISTER_BLPUS_SEARCH_2(1, 2, 3); WJR_REGISTER_BLPUS_SEARCH_2(3, 4, 5); @@ -1242,7 +1242,7 @@ class basic_bplus_tree { return tmp->m_size; } } - } while (0); + } while (false); do { if (pos != 0) { @@ -1263,7 +1263,7 @@ class basic_bplus_tree { } lhs = nullptr; - } while (0); + } while (false); return size; } @@ -1438,7 +1438,7 @@ class basic_bplus_tree { } return; - } while (0); + } while (false); lhs->m_size = merge_size; _Alty_traits::deallocate(__get_allocator(), (uint8_t *)rhs, @@ -1617,7 +1617,7 @@ class basic_bplus_tree { current->as_inner()->m_keys[tmp_pos - 1] = rhs->__get_key(0); return iterator(leaf, pos); - } while (0); + } while (false); lhs->m_size = -(merge_size - 1); remove_uninit(rhs); diff --git a/include/wjr/crtp/class_base.hpp b/include/wjr/crtp/class_base.hpp index 0a1b3c64..be03f8ad 100644 --- a/include/wjr/crtp/class_base.hpp +++ b/include/wjr/crtp/class_base.hpp @@ -239,7 +239,7 @@ struct control_copy_ctor_base : Mybase { using Mybase ::Mybase; control_copy_ctor_base() = default; constexpr control_copy_ctor_base(const control_copy_ctor_base &other) noexcept( - noexcept(Mybase::__copy_construct(static_cast(other)))) + noexcept(std::declval().__copy_construct(std::declval()))) : Mybase(enable_default_constructor) { Mybase::__copy_construct(static_cast(other)); } @@ -258,7 +258,7 @@ struct control_move_ctor_base : Mybase { control_move_ctor_base() = default; control_move_ctor_base(const control_move_ctor_base &) = default; constexpr control_move_ctor_base(control_move_ctor_base &&other) noexcept( - noexcept(Mybase::__move_construct(static_cast(other)))) + noexcept(std::declval().__move_construct(std::declval()))) : Mybase(enable_default_constructor) { Mybase::__move_construct(static_cast(other)); } @@ -278,7 +278,7 @@ struct control_copy_assign_base : Mybase { control_copy_assign_base(control_copy_assign_base &&) = default; constexpr control_copy_assign_base & operator=(const control_copy_assign_base &other) noexcept( - noexcept(Mybase::__copy_assign(static_cast(other)))) { + noexcept(std::declval().__copy_assign(std::declval()))) { Mybase::__copy_assign(static_cast(other)); return *this; } @@ -298,7 +298,7 @@ struct control_move_assign_base : Mybase { control_move_assign_base &operator=(const control_move_assign_base &) = default; constexpr control_move_assign_base & operator=(control_move_assign_base &&other) noexcept( - noexcept(Mybase::__move_assign(static_cast(other)))) { + noexcept(std::declval().__move_assign(std::declval()))) { Mybase::__move_assign(static_cast(other)); return *this; } diff --git a/include/wjr/expected.hpp b/include/wjr/expected.hpp index 4ae41ebf..cffc2a87 100644 --- a/include/wjr/expected.hpp +++ b/include/wjr/expected.hpp @@ -1760,7 +1760,7 @@ using compressed_expected = expected>; if (auto exp = (__VA_ARGS__); WJR_UNLIKELY(!exp)) { \ return ::wjr::unexpected(std::move(exp).error()); \ } \ - } while (0) + } while (false) #define WJR_EXPECTED_INIT(NAME, ...) \ auto NAME = (__VA_ARGS__); \ @@ -1775,7 +1775,7 @@ using compressed_expected = expected>; } else { \ VAR = *std::move(exp); \ } \ - } while (0) + } while (false) } // namespace wjr diff --git a/include/wjr/format/charconv-impl.hpp b/include/wjr/format/charconv-impl.hpp index 1cacaa7c..03bf7fdb 100644 --- a/include/wjr/format/charconv-impl.hpp +++ b/include/wjr/format/charconv-impl.hpp @@ -43,13 +43,12 @@ class char_converter_t { template WJR_CONST static constexpr uint8_t to(uint8_t x) noexcept { if constexpr (Base == 0) { - WJR_ASSERT_L3(x < 36); + WJR_ASSERT_ASSUME_L3(x < 36); } else { - WJR_ASSERT_L3(x < Base); + WJR_ASSERT_ASSUME_L3(x < Base); } if constexpr (Base == 0 || Base > 10) { - if (WJR_BUILTIN_CONSTANT_P_TRUE(x < 10)) { return x + '0'; } diff --git a/include/wjr/format/dragonbox.hpp b/include/wjr/format/dragonbox.hpp new file mode 100644 index 00000000..937bc27e --- /dev/null +++ b/include/wjr/format/dragonbox.hpp @@ -0,0 +1,3797 @@ +#ifndef WJR_FORMAT_DRAGONBOX_HPP__ +#define WJR_FORMAT_DRAGONBOX_HPP__ + +// Copyright 2020-2024 Junekey Jeon +// +// The contents of this file may be used under the terms of +// the Apache License v2.0 with LLVM Exceptions. +// +// (See accompanying file LICENSE-Apache or copy at +// https://llvm.org/foundation/relicensing/LICENSE.txt) +// +// Alternatively, the contents of this file may be used under the terms of +// the Boost Software License, Version 1.0. +// (See accompanying file LICENSE-Boost or copy at +// https://www.boost.org/LICENSE_1_0.txt) +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. + +#include + +// Attribute for storing static data into a dedicated place, e.g. flash memory. Every +// ODR-used static data declaration will be decorated with this macro. The users may +// define this macro, before including the library headers, into whatever they want. +#ifndef WJR_STATIC_DATA_SECTION +#define WJR_STATIC_DATA_SECTION +#endif + +namespace wjr { +namespace dragonbox { + +//////////////////////////////////////////////////////////////////////////////////////// +// Some basic features for encoding/decoding IEEE-754 formats. +//////////////////////////////////////////////////////////////////////////////////////// +namespace detail { +template +struct physical_bits { + static constexpr size_t value = + sizeof(T) * std::numeric_limits::digits; +}; +template +struct value_bits { + static constexpr size_t value = std::numeric_limits< + typename std::enable_if::value, T>::type>::digits; +}; +} // namespace detail + +// These classes expose encoding specs of IEEE-754-like floating-point formats. +// Currently available formats are IEEE-754 binary32 & IEEE-754 binary64. + +struct ieee754_binary32 { + static constexpr int total_bits = 32; + static constexpr int significand_bits = 23; + static constexpr int exponent_bits = 8; + static constexpr int min_exponent = -126; + static constexpr int max_exponent = 127; + static constexpr int exponent_bias = -127; + static constexpr int decimal_significand_digits = 9; + static constexpr int decimal_exponent_digits = 2; +}; +struct ieee754_binary64 { + static constexpr int total_bits = 64; + static constexpr int significand_bits = 52; + static constexpr int exponent_bits = 11; + static constexpr int min_exponent = -1022; + static constexpr int max_exponent = 1023; + static constexpr int exponent_bias = -1023; + static constexpr int decimal_significand_digits = 17; + static constexpr int decimal_exponent_digits = 3; +}; + +// A floating-point format traits class defines ways to interpret a bit pattern of given +// size as an encoding of floating-point number. This is an implementation of such a +// traits class, supporting ways to interpret IEEE-754 binary floating-point numbers. +template +struct ieee754_binary_traits { + // CarrierUInt needs to have enough size to hold the entire contents of floating-point + // numbers. The actual bits are assumed to be aligned to the LSB, and every other bits + // are assumed to be zeroed. + static_assert(detail::value_bits::value >= Format::total_bits, + "wjr::dragonbox: insufficient number of bits"); + static_assert(std::is_unsigned::value, ""); + + // ExponentUInt needs to be large enough to hold (unsigned) exponent bits as well as + // the (signed) actual exponent. + // TODO: static overflow guard against intermediate computations. + static_assert(detail::value_bits::value >= Format::exponent_bits + 1, + "wjr::dragonbox: insufficient number of bits"); + static_assert(std::is_signed::value, ""); + + using format = Format; + using carrier_uint = CarrierUInt; + static constexpr int carrier_bits = int(detail::value_bits::value); + using exponent_int = ExponentInt; + + // Extract exponent bits from a bit pattern. + // The result must be aligned to the LSB so that there is no additional zero paddings + // on the right. This function does not do bias adjustment. + static constexpr exponent_int extract_exponent_bits(carrier_uint u) noexcept { + return exponent_int((u >> format::significand_bits) & + ((exponent_int(1) << format::exponent_bits) - 1)); + } + + // Extract significand bits from a bit pattern. + // The result must be aligned to the LSB so that there is no additional zero paddings + // on the right. The result does not contain the implicit bit. + static constexpr carrier_uint extract_significand_bits(carrier_uint u) noexcept { + return carrier_uint(u & ((carrier_uint(1) << format::significand_bits) - 1u)); + } + + // Remove the exponent bits and extract significand bits together with the sign bit. + static constexpr carrier_uint remove_exponent_bits(carrier_uint u) noexcept { + return carrier_uint(u & ~(((carrier_uint(1) << format::exponent_bits) - 1u) + << format::significand_bits)); + } + + // Shift the obtained signed significand bits to the left by 1 to remove the sign bit. + static constexpr carrier_uint remove_sign_bit_and_shift(carrier_uint u) noexcept { + return carrier_uint( + (carrier_uint(u) << 1) & + ((((carrier_uint(1) << (Format::total_bits - 1)) - 1u) << 1) | 1u)); + } + + // Obtain the actual value of the binary exponent from the extracted exponent bits. + static constexpr exponent_int binary_exponent(exponent_int exponent_bits) noexcept { + return exponent_int(exponent_bits == 0 ? format::min_exponent + : exponent_bits + format::exponent_bias); + } + + // Obtain the actual value of the binary significand from the extracted significand + // bits and exponent bits. + static constexpr carrier_uint + binary_significand(carrier_uint significand_bits, + exponent_int exponent_bits) noexcept { + return carrier_uint( + exponent_bits == 0 + ? significand_bits + : (significand_bits | (carrier_uint(1) << format::significand_bits))); + } + + /* Various boolean observer functions */ + + static constexpr bool is_nonzero(carrier_uint u) noexcept { + return (u & + ((carrier_uint(1) << (format::significand_bits + format::exponent_bits)) - + 1u)) != 0; + } + static constexpr bool is_positive(carrier_uint u) noexcept { + return u < + (carrier_uint(1) << (format::significand_bits + format::exponent_bits)); + } + static constexpr bool is_negative(carrier_uint u) noexcept { return !is_positive(u); } + static constexpr bool is_finite(exponent_int exponent_bits) noexcept { + return exponent_bits != ((exponent_int(1) << format::exponent_bits) - 1); + } + static constexpr bool has_all_zero_significand_bits(carrier_uint u) noexcept { + return ((u << 1) & + ((((carrier_uint(1) << (Format::total_bits - 1)) - 1u) << 1) | 1u)) == 0; + } + static constexpr bool has_even_significand_bits(carrier_uint u) noexcept { + return u % 2 == 0; + } +}; + +// Convert between bit patterns stored in carrier_uint and instances of an actual +// floating-point type. Depending on format and carrier_uint, this operation might not +// be possible for some specific bit patterns. However, the contract is that u always +// denotes a valid bit pattern, so the functions here are assumed to be noexcept. +// Users might specialize this class to change the behavior for certain types. +// The default provided by the library is to treat the given floating-point type Float as +// either IEEE-754 binary32 or IEEE-754 binary64, depending on the bitwise size of Float. +template +struct default_float_bit_carrier_conversion_traits { + // Guards against types that have different internal representations than IEEE-754 + // binary32/64. I don't know if there is a truly reliable way of detecting IEEE-754 + // binary formats. I just did my best here. Note that in some cases + // numeric_limits::is_iec559 may report false even if the internal + // representation is IEEE-754 compatible. In such a case, the user can specialize this + // traits template and remove this static sanity check in order to make Dragonbox work + // for Float. + static_assert(std::numeric_limits::is_iec559 && + std::numeric_limits::radix == 2 && + (detail::physical_bits::value == 32 || + detail::physical_bits::value == 64), + "wjr::dragonbox: Float may not be of IEEE-754 binary32/binary64"); + + // Specifies the unsigned integer type to hold bitwise value of Float. + using carrier_uint = + typename std::conditional::value == 32, uint32_t, + uint64_t>::type; + + // Specifies the floating-point format. + using format = typename std::conditional::value == 32, + ieee754_binary32, ieee754_binary64>::type; + + // Converts the floating-point type into the bit-carrier unsigned integer type. + static WJR_CONSTEXPR20 carrier_uint float_to_carrier(Float x) noexcept { + return bit_cast(x); + } + + // Converts the bit-carrier unsigned integer type into the floating-point type. + static WJR_CONSTEXPR20 Float carrier_to_float(carrier_uint x) noexcept { + return bit_cast(x); + } +}; + +// Convenient wrappers for floating-point traits classes. +// In order to reduce the argument passing overhead, these classes should be as simple as +// possible (e.g., no inheritance, no private non-static data member, etc.; this is an +// unfortunate fact about common ABI convention). + +template +struct signed_significand_bits { + using format_traits = FormatTraits; + using carrier_uint = typename format_traits::carrier_uint; + + carrier_uint u; + + signed_significand_bits() = default; + constexpr explicit signed_significand_bits(carrier_uint bit_pattern) noexcept + : u{bit_pattern} {} + + // Shift the obtained signed significand bits to the left by 1 to remove the sign bit. + constexpr carrier_uint remove_sign_bit_and_shift() const noexcept { + return format_traits::remove_sign_bit_and_shift(u); + } + + constexpr bool is_positive() const noexcept { return format_traits::is_positive(u); } + constexpr bool is_negative() const noexcept { return format_traits::is_negative(u); } + constexpr bool has_all_zero_significand_bits() const noexcept { + return format_traits::has_all_zero_significand_bits(u); + } + constexpr bool has_even_significand_bits() const noexcept { + return format_traits::has_even_significand_bits(u); + } +}; + +template +struct float_bits { + using format_traits = FormatTraits; + using carrier_uint = typename format_traits::carrier_uint; + using exponent_int = typename format_traits::exponent_int; + + carrier_uint u; + + float_bits() = default; + constexpr explicit float_bits(carrier_uint bit_pattern) noexcept : u{bit_pattern} {} + + // Extract exponent bits from a bit pattern. + // The result must be aligned to the LSB so that there is no additional zero paddings + // on the right. This function does not do bias adjustment. + constexpr exponent_int extract_exponent_bits() const noexcept { + return format_traits::extract_exponent_bits(u); + } + + // Extract significand bits from a bit pattern. + // The result must be aligned to the LSB so that there is no additional zero paddings + // on the right. The result does not contain the implicit bit. + constexpr carrier_uint extract_significand_bits() const noexcept { + return format_traits::extract_significand_bits(u); + } + + // Remove the exponent bits and extract significand bits together with the sign bit. + constexpr signed_significand_bits + remove_exponent_bits() const noexcept { + return signed_significand_bits( + format_traits::remove_exponent_bits(u)); + } + + // Obtain the actual value of the binary exponent from the extracted exponent bits. + static constexpr exponent_int binary_exponent(exponent_int exponent_bits) noexcept { + return format_traits::binary_exponent(exponent_bits); + } + constexpr exponent_int binary_exponent() const noexcept { + return binary_exponent(extract_exponent_bits()); + } + + // Obtain the actual value of the binary exponent from the extracted significand bits + // and exponent bits. + static constexpr carrier_uint + binary_significand(carrier_uint significand_bits, + exponent_int exponent_bits) noexcept { + return format_traits::binary_significand(significand_bits, exponent_bits); + } + constexpr carrier_uint binary_significand() const noexcept { + return binary_significand(extract_significand_bits(), extract_exponent_bits()); + } + + constexpr bool is_nonzero() const noexcept { return format_traits::is_nonzero(u); } + constexpr bool is_positive() const noexcept { return format_traits::is_positive(u); } + constexpr bool is_negative() const noexcept { return format_traits::is_negative(u); } + constexpr bool is_finite(exponent_int exponent_bits) const noexcept { + return format_traits::is_finite(exponent_bits); + } + constexpr bool is_finite() const noexcept { + return format_traits::is_finite(extract_exponent_bits()); + } + constexpr bool has_even_significand_bits() const noexcept { + return format_traits::has_even_significand_bits(u); + } +}; + +template , + class FormatTraits = ieee754_binary_traits< + typename ConversionTraits::format, typename ConversionTraits::carrier_uint>> +WJR_CONSTEXPR20 float_bits make_float_bits(Float x) noexcept { + return float_bits(ConversionTraits::float_to_carrier(x)); +} + +namespace detail { +//////////////////////////////////////////////////////////////////////////////////////// +// Utilities for wide unsigned integer arithmetic. +//////////////////////////////////////////////////////////////////////////////////////// + +namespace wuint { + +inline constexpr uint64_t umul64(uint32_t x, uint32_t y) noexcept { + return x * static_cast(y); +} + +// Get 128-bit result of multiplication of two 64-bit unsigned integers. +WJR_SAFEBUFFERS WJR_INTRINSIC_CONSTEXPR20 uint128_t umul128(uint64_t x, + uint64_t y) noexcept { + return mul64x64to128(x, y); +} + +// Get high half of the 128-bit result of multiplication of two 64-bit unsigned +// integers. +WJR_SAFEBUFFERS WJR_INTRINSIC_CONSTEXPR20 uint64_t umul128_upper64(uint64_t x, + uint64_t y) noexcept { + return mulhi(x, y); +} + +// Get upper 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit +// unsigned integer. +WJR_SAFEBUFFERS WJR_INTRINSIC_CONSTEXPR20 uint128_t +umul192_upper128(uint64_t x, uint128_t y) noexcept { + auto r = umul128(x, y.high); + r += umul128_upper64(x, y.low); + return r; +} + +// Get upper 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit +// unsigned integer. +WJR_INTRINSIC_CONSTEXPR20 uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { + return umul128_upper64(uint64_t(x) << 32, y); +} + +// Get lower 128-bits of multiplication of a 64-bit unsigned integer and a 128-bit +// unsigned integer. +WJR_SAFEBUFFERS WJR_INTRINSIC_CONSTEXPR20 uint128_t +umul192_lower128(uint64_t x, uint128_t y) noexcept { + auto const high = x * y.high; + auto const high_low = umul128(x, y.low); + return {(high + high_low.high) & UINT64_C(0xffffffffffffffff), high_low.low}; +} + +// Get lower 64-bits of multiplication of a 32-bit unsigned integer and a 64-bit +// unsigned integer. +constexpr uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { + return (x * y) & UINT64_C(0xffffffffffffffff); +} +} // namespace wuint + +//////////////////////////////////////////////////////////////////////////////////////// +// Some simple utilities for constexpr computation. +//////////////////////////////////////////////////////////////////////////////////////// + +template +constexpr Int compute_power(Int a) noexcept { + static_assert(k >= 0, ""); + Int p = 1; + for (int i = 0; i < k; ++i) { + p *= a; + } + return p; +} + +template +constexpr int count_factors(UInt n) noexcept { + static_assert(a > 1, ""); + int c = 0; + while (n % a == 0) { + n /= a; + ++c; + } + return c; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Utilities for fast/constexpr log computation. +//////////////////////////////////////////////////////////////////////////////////////// + +namespace log { +static_assert((std::int_fast32_t(-1) >> 1) == std::int_fast32_t(-1) && + (std::int_fast16_t(-1) >> 1) == std::int_fast16_t(-1), + "wjr::dragonbox: right-shift for signed integers must be arithmetic"); + +// For constexpr computation. +// Returns -1 when n = 0. +template +constexpr int floor_log2(UInt n) noexcept { + int count = -1; + while (n != 0) { + ++count; + n >>= 1; + } + return count; +} + +template