From 45aea48e181e6a065ffb0d4e87c68601fcb42bae Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 05:05:35 +0300 Subject: [PATCH 01/17] fix bug in integers_128_bit.hpp --- number_theory/integers_128_bit.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/number_theory/integers_128_bit.hpp b/number_theory/integers_128_bit.hpp index 440596f..0a1dcca 100644 --- a/number_theory/integers_128_bit.hpp +++ b/number_theory/integers_128_bit.hpp @@ -29,7 +29,9 @@ typedef std::_Signed128 int128_t; #else -#ifdef defined(__clang__) || defined(__GNUC__) +#if defined(INTEGERS_128_BIT_HPP_WARN_IF_UNSUPPORED) && INTEGERS_128_BIT_HPP_WARN_IF_UNSUPPORED + +#if defined(__clang__) || defined(__GNUC__) // cppcheck-suppress [preprocessorErrorDirective] #warning "Unsupported compiler, typedef 128-bit integer specific for your compiler" #elif defined(_MSC_VER) @@ -37,6 +39,8 @@ typedef std::_Signed128 int128_t; #pragma message WARN("your warning message here") #endif +#endif + #define HAS_INT128_TYPEDEF 0 #endif From c60c11daa752401e9b9c48cab68c6868295d0716 Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 05:05:49 +0300 Subject: [PATCH 02/17] update math_functions.hpp --- number_theory/math_functions.hpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/number_theory/math_functions.hpp b/number_theory/math_functions.hpp index 51e54dc..9ccb851 100644 --- a/number_theory/math_functions.hpp +++ b/number_theory/math_functions.hpp @@ -83,7 +83,7 @@ template #else template #endif -[[nodiscard]] ATTRIBUTE_CONST constexpr T bin_pow(T n, uint64_t p) noexcept(noexcept(n *= n)) { +[[nodiscard]] ATTRIBUTE_CONST constexpr T bin_pow(T n, std::size_t p) noexcept(noexcept(n *= n)) { T res(1); while (true) { if (p & 1) { @@ -107,10 +107,11 @@ template #else template #endif -[[nodiscard]] ATTRIBUTE_CONST constexpr T bin_pow(T n, int64_t p) noexcept(noexcept(n *= n) && - noexcept(1 / n)) { +[[nodiscard]] ATTRIBUTE_CONST constexpr T bin_pow(T n, + std::ptrdiff_t p) noexcept(noexcept(n *= n) && + noexcept(1 / n)) { const bool not_inverse = p >= 0; - uint64_t p_u = p >= 0 ? static_cast(p) : -static_cast(p); + std::size_t p_u = p >= 0 ? static_cast(p) : -static_cast(p); T res(1); while (true) { if (p_u & 1) { @@ -1262,12 +1263,14 @@ bool is_power_of_two(char) = delete; ATTRIBUTE_CONST constexpr uint64_t nearest_greater_equal_power_of_two(uint32_t n) noexcept { const auto shift = 32 - static_cast(::math_functions::countl_zero(n | 1)) - ((n & (n - 1)) == 0); + ATTRIBUTE_ASSUME(shift <= 32); return uint64_t{1} << shift; } [[nodiscard]] ATTRIBUTE_CONST constexpr uint64_t nearest_greater_power_of_two(uint32_t n) noexcept { const auto shift = 32 - static_cast(::math_functions::countl_zero(n)); + ATTRIBUTE_ASSUME(shift <= 32); return uint64_t{1} << shift; } From 032fa860ce7339e2dff128c3eea25b94c48965c0 Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 05:06:18 +0300 Subject: [PATCH 03/17] update longint and warnings for type limits in tests/CMakeLists.txt --- number_theory/longint.hpp | 1285 ++++++++++++++++++++----------------- tests/CMakeLists.txt | 1 + 2 files changed, 700 insertions(+), 586 deletions(-) diff --git a/number_theory/longint.hpp b/number_theory/longint.hpp index a267d7c..816ea0c 100644 --- a/number_theory/longint.hpp +++ b/number_theory/longint.hpp @@ -24,14 +24,6 @@ #endif #include "math_functions.hpp" -// https://en.cppreference.com/w/cpp/numeric/bit_cast -#if CONFIG_HAS_AT_LEAST_CXX_20 && defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L -#include -#define LONG_INT_USE_BIT_CAST 1 -#else -#define LONG_INT_USE_BIT_CAST 0 -#endif - #if !defined(__GNUG__) // cppcheck-suppress [preprocessorErrorDirective] #error "Current implementation works only with GCC" @@ -303,7 +295,8 @@ struct longint { static constexpr auto kFFTFloatRoundError = std::numeric_limits::round_error(); - using dec_digit_t = uint32_t; + using dec_digit_t = uint32_t; + using double_dec_digit_t = uint32_t; static constexpr uint32_t kDecimalBase = kStrConvBase; static constexpr uint32_t kFFTDecimalBase = 1'000; @@ -312,7 +305,7 @@ struct longint { longint(const longint& other) : nums_(nullptr), size_(other.size_), capacity_(other.capacity_) { if (capacity_ > 0) { nums_ = allocate(capacity_); - std::copy_n(other.nums_, usize32(), nums_); + std::copy_n(other.nums_, usize(), nums_); } } longint& operator=(const longint& other) { @@ -427,25 +420,26 @@ struct longint { static_assert(kMaxSSize > 0); constexpr auto kMaxUSize = std::numeric_limits::max(); return static_cast( - std::min({static_cast(kMaxSSize), static_cast(kMaxUSize), + std::min({static_cast(kMaxSSize), static_cast(kMaxUSize / 2), std::numeric_limits::max() / sizeof(digit_t)})); } [[nodiscard]] ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr ssize_type size() const noexcept { - const auto value = size_; - static_assert(static_cast(max_size()) == static_cast(max_size())); +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtype-limits" +#endif + const ssize_type value = size_; if (value > static_cast(max_size())) { CONFIG_UNREACHABLE(); } - +#if defined(__clang__) +#pragma clang diagnostic pop +#endif return value; } [[nodiscard]] - ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr std::size_t usize() const noexcept { - return usize32(); - } - [[nodiscard]] - ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr size_type usize32() const noexcept { + ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr size_type usize() const noexcept { const auto value = math_functions::uabs(size()); if (value > max_size()) { CONFIG_UNREACHABLE(); @@ -549,7 +543,7 @@ struct longint { longint& pow(std::size_t p) { longint res = uint32_t{1}; - reserve((std::max(usize32(), size_type{1}) - 1) * p); + reserve((std::max(usize(), size_type{1}) - 1) * p); while (true) { if (p & 1) { res *= *this; @@ -563,79 +557,48 @@ struct longint { return *this = std::move(res); } void SquareThisTo(longint& other) const { - const std::size_t usize_value = usize32(); - if (unlikely(usize_value == 0)) { + const size_type nums_size = usize(); + if (unlikely(nums_size == 0)) { other.set_zero(); return; } - const uint32_t* nums_ptr = nums_; - const std::size_t prod_size = usize_value + usize_value; + const digit_t* nums_ptr = nums_; + static_assert(max_size() + max_size() > max_size()); + const size_type prod_size = check_size(nums_size * 2); if (prod_size <= 16) { - other.capacity_ = static_cast(prod_size); - uint32_t* ans = allocate(prod_size); - std::fill_n(ans, prod_size, uint32_t{0}); - for (std::size_t j = 0; j < usize_value; j++) { + other.capacity_ = prod_size; + digit_t* ans = allocate(prod_size); + std::fill_n(ans, prod_size, digit_t{0}); + for (std::size_t j = 0; j < nums_size; j++) { const double_digit_t b_j = nums_ptr[j]; double_digit_t carry = 0; - for (std::size_t i = 0; i < usize_value; i++) { + for (std::size_t i = 0; i < nums_size; i++) { const double_digit_t a_i = nums_ptr[i]; - const double_digit_t res = - a_i * b_j + static_cast(ans[j + i]) + carry; - ans[j + i] = static_cast(res); - carry = res >> 32; + const double_digit_t res = a_i * b_j + double_digit_t{ans[j + i]} + carry; + ans[j + i] = static_cast(res); + carry = res >> kNumsBits; } - ans[j + usize_value] = static_cast(carry); + ans[j + nums_size] = static_cast(carry); } deallocate(other.nums_); other.nums_ = ans; } else { - const auto checked_prod_size = check_size(prod_size); - ATTRIBUTE_ASSUME(prod_size == checked_prod_size); - std::size_t n = - 2 * math_functions::nearest_greater_equal_power_of_two(checked_prod_size); - const bool high_precision = n > kFFTPrecisionBorder; - n <<= high_precision; + const auto [n, need_high_precision] = LongIntFFT::compute_fft_product_params(prod_size); // Allocate n complex numbers for p1 and n complex numbers for p2 const std::unique_ptr p1( allocate_complex_array_for_unique_ptr(2 * n)); - fft::complex* p = p1.get(); - if (likely(!high_precision)) { - for (std::size_t i = 0; i < usize_value; i++) { - uint32_t value = nums_ptr[i]; - *p = fft::complex(value & 0xFFFF, value & 0xFFFF); - p++; - value >>= 16; - *p = fft::complex(value, value); - p++; - } - } else { - for (std::size_t i = 0; i < usize_value; i++) { - uint32_t value = nums_ptr[i]; - *p = fft::complex(static_cast(value), static_cast(value)); - p++; - value >>= 8; - *p = fft::complex(static_cast(value), static_cast(value)); - p++; - value >>= 8; - *p = fft::complex(static_cast(value), static_cast(value)); - p++; - value >>= 8; - *p = fft::complex(value, value); - p++; - } - } - std::memset(static_cast(p), 0, - (n - (prod_size << high_precision)) * sizeof(fft::complex)); - - other.reserveUninitializedWithoutCopy(checked_prod_size); + LongIntFFT::convert_longint_nums_to_fft_poly(nums_ptr, nums_size, p1.get(), n, + need_high_precision); + other.reserveUninitializedWithoutCopy(prod_size); fft::complex* p2 = p1.get() + n; fft::forward_backward_fft(p1.get(), p2, n); - convert_fft_poly_to_longint_nums(high_precision, p2, other.nums_, checked_prod_size); + LongIntFFT::convert_fft_poly_to_longint_nums(need_high_precision, p2, other.nums_, + prod_size); } - other.size_ = static_cast(prod_size); + other.size_ = static_cast(prod_size); other.pop_leading_zeros(); } longint& SquareInplace() { @@ -646,8 +609,8 @@ struct longint { return nums_[pos]; } longint& operator*=(const longint& other) { - std::size_t k = usize32(); - std::size_t m = other.usize32(); + size_type k = usize(); + size_type m = other.usize(); const digit_t* k_ptr = nums_; const digit_t* m_ptr = other.nums_; @@ -661,99 +624,30 @@ struct longint { return *this; } - const std::size_t prod_size = m + k; - const auto checked_prod_size = check_size(prod_size); + static_assert(max_size() + max_size() > max_size()); + const size_type prod_size = check_size(m + k); if (m <= 16 || m * k <= 1024) { - capacity_ = checked_prod_size; digit_t* ans = allocate(prod_size); std::fill_n(ans, prod_size, digit_t{0}); - for (std::size_t j = 0; j < m; j++) { - const double_digit_t b_j = m_ptr[j]; - double_digit_t carry = 0; - for (std::size_t i = 0; i < k; i++) { - const double_digit_t a_i = k_ptr[i]; - const double_digit_t res = - a_i * b_j + static_cast(ans[j + i]) + carry; - ans[j + i] = static_cast(res); - carry = res / kNumsBase; - } - - ans[j + k] = static_cast(carry); - } - + LongIntNaive::multiply_and_store_to(m_ptr, m, k_ptr, k, ans); deallocate(nums_); - nums_ = ans; + nums_ = ans; + capacity_ = prod_size; } else { - std::size_t n = - 2 * math_functions::nearest_greater_equal_power_of_two(checked_prod_size); - const bool high_precision = n > kFFTPrecisionBorder; - n <<= high_precision; + const auto [n, need_high_precision] = LongIntFFT::compute_fft_product_params(prod_size); // Allocate n complex numbers for p1 and n complex numbers for p2 const std::unique_ptr p1( allocate_complex_array_for_unique_ptr(2 * n)); - fft::complex* p = p1.get(); - if (likely(!high_precision)) { - for (std::size_t i = 0; i < m; i++) { - const digit_t m_value = m_ptr[i]; - const digit_t k_value = k_ptr[i]; - *p = fft::complex(m_value & 0xFFFF, k_value & 0xFFFF); - p++; - *p = fft::complex(m_value >> 16, k_value >> 16); - p++; - } - for (std::size_t i = m; i < k; i++) { - const digit_t k_value = k_ptr[i]; - *p = fft::complex(0, k_value & 0xFFFF); - p++; - *p = fft::complex(0, k_value >> 16); - p++; - } - } else { - for (std::size_t i = 0; i < m; i++) { - uint32_t m_value = m_ptr[i]; - uint32_t k_value = k_ptr[i]; - *p = fft::complex(static_cast(m_value), static_cast(k_value)); - p++; - m_value >>= 8; - k_value >>= 8; - *p = fft::complex(static_cast(m_value), static_cast(k_value)); - p++; - m_value >>= 8; - k_value >>= 8; - *p = fft::complex(static_cast(m_value), static_cast(k_value)); - p++; - m_value >>= 8; - k_value >>= 8; - *p = fft::complex(static_cast(m_value), static_cast(k_value)); - p++; - } - for (std::size_t i = m; i < k; i++) { - uint32_t k_value = k_ptr[i]; - *p = fft::complex(0, static_cast(k_value)); - p++; - k_value >>= 8; - *p = fft::complex(0, static_cast(k_value)); - p++; - k_value >>= 8; - *p = fft::complex(0, static_cast(k_value)); - p++; - k_value >>= 8; - *p = fft::complex(0, static_cast(k_value)); - p++; - } - } - std::memset(static_cast(p), 0, - (n - ((2 * k) << high_precision)) * sizeof(fft::complex)); - - reserveUninitializedWithoutCopy(checked_prod_size); + LongIntFFT::convert_longint_nums_to_fft_poly(m_ptr, m, k_ptr, k, p1.get(), n, + need_high_precision); + reserveUninitializedWithoutCopy(prod_size); fft::complex* const p2 = p1.get() + n; fft::forward_backward_fft(p1.get(), p2, n); - convert_fft_poly_to_longint_nums(high_precision, p2, nums_, checked_prod_size); + LongIntFFT::convert_fft_poly_to_longint_nums(need_high_precision, p2, nums_, prod_size); } - const std::int32_t sign_product = size_ ^ other.size_; - size_ = - static_cast(sign_product >= 0 ? checked_prod_size : -checked_prod_size); + const ssize_type sign_product = size_ ^ other.size_; + set_ssize_from_size_and_sign(prod_size, /* sign = */ sign_product); pop_leading_zeros(); return *this; } @@ -774,10 +668,9 @@ struct longint { } longint& operator+=(const longint& other) { const bool find_sum = (size_ ^ other.size_) >= 0; - const size_type usize2 = other.usize32(); + const size_type usize2 = other.usize(); static_assert(max_size() + 1 > max_size()); - const size_type usize1 = - set_size_at_least(std::max(usize32(), usize2) + (find_sum ? 1 : 0)); + const size_type usize1 = set_size_at_least(std::max(usize(), usize2) + (find_sum ? 1 : 0)); ATTRIBUTE_ASSUME(usize1 >= usize2); if (find_sum) { ATTRIBUTE_ASSUME(usize1 > usize2); @@ -830,12 +723,11 @@ struct longint { return iszero(); } - if (this->sign() != ::math_functions::sign(n)) { + if (this->sign() != math_functions::sign(n)) { return false; } - /* Do not use std::abs in order to make operator== constexpr */ - const uint64_t n_abs = ::math_functions::uabs(n); + const uint64_t n_abs = math_functions::uabs(n); static_assert(kNumsBits == 32); switch (size_) { case 0: @@ -914,11 +806,11 @@ struct longint { return iszero(); } - if (this->sign() != ::math_functions::sign(n)) { + if (this->sign() != math_functions::sign(n)) { return false; } - const uint128_t n_abs = ::math_functions::uabs(n); + const uint128_t n_abs = math_functions::uabs(n); static_assert(kNumsBits == 32); switch (size_) { case 0: @@ -955,10 +847,10 @@ struct longint { return size_ <=> other.size_; } - const std::size_t usize_value = usize(); - const digit_t* const r_end = nums_ - 1; - const digit_t* r_nums = r_end + usize_value; - const digit_t* r_other_nums = other.nums_ - 1 + usize_value; + const size_type usize_value = usize(); + const digit_t* const r_end = nums_ - 1; + const digit_t* r_nums = r_end + usize_value; + const digit_t* r_other_nums = other.nums_ - 1 + usize_value; for (; r_nums != r_end; r_nums--, r_other_nums--) { if (*r_nums != *r_other_nums) { return static_cast(*r_nums) * sign() <=> @@ -974,7 +866,7 @@ struct longint { return size_ < other.size_; } - std::size_t usize_value = usize(); + const size_type usize_value = usize(); const uint32_t* r_end = nums_ - 1; const uint32_t* r_nums = r_end + usize_value; const uint32_t* r_other_nums = other.nums_ - 1 + usize_value; @@ -1056,10 +948,10 @@ struct longint { return *this; } - double_digit_t carry = 0; - const double_digit_t b_0 = x; - const size_type u32size = usize32(); - for (digit_t *nums_it = nums_, *nums_it_end = nums_it + u32size; nums_it != nums_it_end; + double_digit_t carry = 0; + const double_digit_t b_0 = x; + const size_type usize_value = usize(); + for (digit_t *nums_it = nums_, *nums_it_end = nums_it + usize_value; nums_it != nums_it_end; ++nums_it) { const double_digit_t res = *nums_it * b_0 + carry; *nums_it = static_cast(res); @@ -1068,12 +960,12 @@ struct longint { // x != 0 => sign won't change and there will be no leading zeros if (carry != 0) { - if (unlikely(u32size == capacity_)) { + if (unlikely(usize_value == capacity_)) { growCapacity(); } - assert(u32size < capacity_); - nums_[u32size] = static_cast(carry); + assert(usize_value < capacity_); + nums_[usize_value] = static_cast(carry); size_ += sign(); } @@ -1106,7 +998,9 @@ struct longint { /// @note Behaviour is undefined if n equals 0 /// @param n /// @return *this mod n - [[nodiscard]] + ATTRIBUTE_NODISCARD_WITH_MESSAGE( + "use longint::operator/=(uint32_t) if you don't need the result of " + "longint::divmod(uint32_t)") ATTRIBUTE_ALWAYS_INLINE constexpr uint32_t divmod(const uint32_t n) noexcept { if ((config::is_constant_evaluated() || config::is_gcc_constant_p(n)) && (n & (n - 1)) == 0) { @@ -1130,19 +1024,19 @@ struct longint { return *this; } - size_type usize_value = usize32(); + size_type usize_value = usize(); const size_type uints_move = shift / kNumsBits; if (uints_move >= usize_value) { set_zero(); return *this; } - if (uints_move != 0) { + if (uints_move > 0) { usize_value -= uints_move; set_ssize_from_size(usize_value); - uint32_t* copy_dst_start = nums_; - const uint32_t* copy_src_start = copy_dst_start + uints_move; - const uint32_t* copy_src_end = copy_src_start + usize_value; + digit_t* copy_dst_start = nums_; + const digit_t* copy_src_start = copy_dst_start + uints_move; + const digit_t* copy_src_end = copy_src_start + usize_value; std::copy(copy_src_start, copy_src_end, copy_dst_start); } @@ -1170,7 +1064,7 @@ struct longint { return *this; } - size_type usize_value = usize32(); + size_type usize_value = usize(); if (usize_value == 0) { return *this; } @@ -1209,7 +1103,9 @@ struct longint { return *this; } - inline void set_string(std::string_view s); + void set_string(std::string_view s) { + this->set_str_impl(reinterpret_cast(s.data()), s.size()); + } [[nodiscard]] ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr bool fits_in_uint32() const noexcept { @@ -1232,7 +1128,7 @@ struct longint { } [[nodiscard]] ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr bool fits_in_uint64() const noexcept { - return static_cast(size()) <= 2; + return static_cast(size()) <= 2; } [[nodiscard]] ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr std::uint64_t to_uint64() const noexcept { @@ -1281,13 +1177,13 @@ struct longint { } [[fallthrough]]; case 4: - value |= static_cast(nums_[3]) << 96; + value |= uint128_t{nums_[3]} << 96; [[fallthrough]]; case 3: - value |= static_cast(nums_[2]) << 64; + value |= uint128_t{nums_[2]} << 64; [[fallthrough]]; case 2: - value |= static_cast(nums_[1]) << 32; + value |= std::uint64_t{nums_[1]} << 32; [[fallthrough]]; case 1: value |= nums_[0]; @@ -1319,8 +1215,9 @@ struct longint { ans.push_back('-'); } - const std::uint32_t usize32_value = usize32(); - switch (usize32_value) { + static_assert(sizeof(digit_t) == sizeof(uint32_t)); + const size_type usize_value = usize(); + switch (usize_value) { case 0: ans = "0"; return; @@ -1328,27 +1225,30 @@ struct longint { ans += std::to_string(nums_[0]); return; case 2: - ans += std::to_string((static_cast(nums_[1]) << kNumsBits) | nums_[0]); + ans += std::to_string((uint64_t{nums_[1]} << kNumsBits) | nums_[0]); return; default: break; } - std::uint64_t n = math_functions::nearest_greater_equal_power_of_two(usize32_value); - ensureBinBasePowsCapacity(math_functions::log2_floor(static_cast(n))); + static_assert(math_functions::nearest_greater_equal_power_of_two(max_size()) <= + std::numeric_limits::max()); + const std::size_t n = static_cast( + math_functions::nearest_greater_equal_power_of_two(usize_value)); + ensureBinBasePowsCapacity(math_functions::log2_floor(n)); digit_t* const knums = allocate(n); - std::fill_n(std::copy_n(nums_, usize32_value, knums), n - usize32_value, digit_t{0}); + std::fill_n(std::copy_n(nums_, usize_value, knums), n - usize_value, digit_t{0}); const Decimal result = convertBinBase(knums, n); deallocate(knums); assert(result.size_ >= 3); const std::size_t full_blocks = result.size_ - 1; - uint32_t last_a_i = result.digits_[full_blocks]; + dec_digit_t last_a_i = result.digits_[full_blocks]; const std::size_t string_size = full_blocks * kStrConvBaseDigits + math_functions::base_10_len(last_a_i); ans.resize(ans.size() + string_size); auto* ptr = reinterpret_cast(std::addressof(ans[ans.size() - 1])); for (std::size_t i = 0; i < full_blocks; i++) { - uint32_t a_i = result.digits_[i]; + dec_digit_t a_i = result.digits_[i]; for (std::size_t j = kStrConvBaseDigits; j > 0; j--) { *ptr = static_cast('0' + a_i % 10); a_i /= 10; @@ -1378,7 +1278,7 @@ struct longint { } void reserve(const std::size_t requested_capacity) { - const auto checked_capacity = check_size(requested_capacity); + const size_type checked_capacity = check_size(requested_capacity); if (checked_capacity > capacity_) { digit_t* new_nums = allocate(checked_capacity); if (nums_) { @@ -1395,8 +1295,10 @@ struct longint { } struct Decimal { + using dec_size_type = std::size_t; + dec_digit_t* digits_ = nullptr; - std::size_t size_ = 0; + dec_size_type size_ = 0; constexpr Decimal() noexcept = default; explicit Decimal(uint32_t n) : digits_(allocate(2)) { @@ -1461,12 +1363,12 @@ struct longint { } Decimal& operator+=(const Decimal& other) { - uint64_t carry = 0; - const std::size_t m = std::min(this->size_, other.size_); - uint32_t* p = this->digits_; + double_dec_digit_t carry = 0; + const std::size_t m = std::min(this->size_, other.size_); + dec_digit_t* p = this->digits_; for (std::size_t i = 0; i < m; i++) { - const uint64_t res = - static_cast(p[i]) + static_cast(other.digits_[i]) + carry; + const double_dec_digit_t res = + double_dec_digit_t{p[i]} + double_dec_digit_t{other.digits_[i]} + carry; p[i] = static_cast(res % kDecimalBase); carry = res / kDecimalBase; } @@ -1483,7 +1385,7 @@ struct longint { p = this->digits_; const std::size_t this_size = size_; for (std::size_t i = m; carry != 0 && i < this_size; i++) { - const auto res = static_cast(p[i]) + carry; + const auto res = double_dec_digit_t{p[i]} + carry; p[i] = static_cast(res % kDecimalBase); carry = res / kDecimalBase; } @@ -1503,8 +1405,8 @@ struct longint { } Decimal& operator*=(const Decimal& other) { - std::size_t k = size_; - std::size_t m = other.size_; + dec_size_type k = size_; + dec_size_type m = other.size_; const dec_digit_t* k_ptr = digits_; const dec_digit_t* m_ptr = other.digits_; @@ -1518,12 +1420,11 @@ struct longint { return *this; } ATTRIBUTE_ASSUME(1 <= m && m <= k); - const std::size_t new_size = m + k; + const dec_size_type new_size = m + k; if (m <= 16 || m * k <= 1024) { - naive_multiply_and_store_to(m_ptr, m, k_ptr, k, *this); + DecNaive::multiply_and_store_to(m_ptr, m, k_ptr, k, *this); } else { - const DecFFTProduct prod(m_ptr, m, k_ptr, k); - prod.multiply_and_store_to(*this); + DecFFT::multiply_and_store_to(m_ptr, m, k_ptr, k, *this); } size_ = new_size; @@ -1539,71 +1440,189 @@ struct longint { } if (digits_size <= 16 || digits_size * digits_size <= 1024) { - naive_multiply_and_store_to(digits_, digits_size, other); + DecNaive::square_and_store_to(digits_, digits_size, other); } else { - const DecFFTProduct prod(digits_, digits_size); - prod.multiply_and_store_to(other); + DecFFT::square_and_store_to(digits_, digits_size, other); } other.pop_leading_zeros(); } - ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) - ATTRIBUTE_NONNULL_ALL_ARGS - static void naive_multiply_and_store_to(const dec_digit_t digits[], std::size_t digits_size, - Decimal& product_result) { - naive_multiply_and_store_to(digits, digits_size, digits, digits_size, product_result); + [[nodiscard]] + ATTRIBUTE_PURE constexpr bool operator==(uint32_t n) const noexcept { + switch (size_) { + case 0: + return n == 0; + case 1: + return digits_[0] == n; + case 2: + return static_cast(digits_[1]) * kDecimalBase + digits_[0] == n; + default: + return false; + } } - ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) - ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) - ATTRIBUTE_NONNULL_ALL_ARGS - static void naive_multiply_and_store_to(const dec_digit_t m_digits[], std::size_t m, - const dec_digit_t k_digits[], std::size_t k, - Decimal& product_result) { - const std::size_t new_size = m + k; - dec_digit_t* ans = allocate(new_size); - std::fill_n(ans, new_size, dec_digit_t{0}); - for (std::size_t j = 0; j < m; j++) { - const uint64_t b_j = m_digits[j]; - uint64_t carry = 0; - for (std::size_t i = 0; i < k; i++) { - uint64_t a_i = k_digits[i]; - uint64_t res = a_i * b_j + static_cast(ans[j + i]) + carry; - ans[j + i] = static_cast(res % kDecimalBase); - carry = res / kDecimalBase; + + [[nodiscard]] + ATTRIBUTE_PURE constexpr bool operator!=(uint32_t n) const noexcept { + return !(*this == n); + } + + [[nodiscard]] + ATTRIBUTE_PURE constexpr bool operator==(uint64_t n) const noexcept { + switch (size_) { + case 0: + return n == 0; + case 1: + return digits_[0] == n; + case 2: + return static_cast(digits_[1]) * kDecimalBase + digits_[0] == n; + case 3: { + constexpr uint64_t kDecimalBase2 = + static_cast(kDecimalBase) * kDecimalBase; + const uint64_t hi = digits_[2]; + if (hi > 18) { + return false; + } + + const uint64_t low_mid_m = + static_cast(digits_[1]) * kDecimalBase + digits_[0]; + if (hi == 18) { + return n >= 18 * kDecimalBase2 && n - 18 * kDecimalBase2 == low_mid_m; + } + const uint64_t m = static_cast(hi) * kDecimalBase2 + low_mid_m; + return m == n; } - ans[j + k] = static_cast(carry % kDecimalBase); + default: + return false; + } + } + + [[nodiscard]] + ATTRIBUTE_PURE constexpr bool operator!=(uint64_t n) const noexcept { + return !(*this == n); + } + + [[nodiscard]] + ATTRIBUTE_PURE constexpr bool operator==(const Decimal& other) const noexcept { + return size_ == other.size_ && std::equal(digits_, digits_ + size_, other.digits_); + } + + constexpr void set_zero() noexcept { + size_ = 0; + } + constexpr void pop_leading_zeros() noexcept { + std::size_t usize_value = size_; + while (usize_value > 0 && digits_[usize_value - 1] == 0) { + usize_value--; } - deallocate(product_result.digits_); - product_result.digits_ = ans; - product_result.size_ = new_size; + size_ = usize_value; } - class [[nodiscard]] DecFFTProduct final { - public: + ~Decimal() { + deallocate(digits_); + } + + private: + struct DecNaive final { + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + ATTRIBUTE_NONNULL_ALL_ARGS + ATTRIBUTE_ALWAYS_INLINE + static void square_and_store_to(const dec_digit_t digits[], + const dec_size_type digits_size, + Decimal& product_result) { + DecNaive::multiply_and_store_to(digits, digits_size, digits, digits_size, + product_result); + } + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) + ATTRIBUTE_NONNULL_ALL_ARGS + static void multiply_and_store_to(const dec_digit_t m_digits[], + const dec_size_type m_size, + const dec_digit_t k_digits[], + const dec_size_type k_size, Decimal& product_result) { + ATTRIBUTE_ASSUME(m_size <= k_size); + const dec_size_type new_size = m_size + k_size; + dec_digit_t* ans = allocate(new_size); + std::fill_n(ans, new_size, dec_digit_t{0}); + DecNaive::multiply_and_store_to_impl(m_digits, m_size, k_digits, k_size, ans); + deallocate(product_result.digits_); + product_result.digits_ = ans; + product_result.size_ = new_size; + } + + private: + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) + ATTRIBUTE_ACCESS(read_write, 5) + ATTRIBUTE_NONNULL_ALL_ARGS + static void multiply_and_store_to_impl(const dec_digit_t m_digits[], + const dec_size_type m_size, + const dec_digit_t k_digits[], + const dec_size_type k_size, + dec_digit_t ans[]) noexcept { + double_dec_digit_t* ans_store_ptr = ans; + for (dec_size_type j = 0; j < m_size; ans_store_ptr++, j++) { + const double_dec_digit_t b_j = m_digits[j]; + double_dec_digit_t carry = 0; + for (dec_size_type i = 0; i < k_size; i++) { + const double_dec_digit_t a_i = k_digits[i]; + const double_dec_digit_t res = + a_i * b_j + double_dec_digit_t{ans_store_ptr[i]} + carry; + ans_store_ptr[i] = static_cast(res % kDecimalBase); + carry = res / kDecimalBase; + } + ans_store_ptr[k_size] = static_cast(carry % kDecimalBase); + } + } + }; + + struct DecFFT final { + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) + ATTRIBUTE_NONNULL_ALL_ARGS + ATTRIBUTE_ALWAYS_INLINE + static void multiply_and_store_to(const dec_digit_t m_digits[], const dec_size_type m, + const dec_digit_t k_digits[], const dec_size_type k, + Decimal& product_result) { + const DecFFT prod(m_digits, m, k_digits, k); + prod.multiply_and_store_to_impl(product_result); + } + + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + ATTRIBUTE_NONNULL_ALL_ARGS + ATTRIBUTE_ALWAYS_INLINE + static void square_and_store_to(const dec_digit_t digits[], + const dec_size_type digits_size, + Decimal& product_result) { + const DecFFT prod(digits, digits_size); + prod.multiply_and_store_to_impl(product_result); + } + + private: ATTRIBUTE_SIZED_ACCESS(read_only, 2, 3) ATTRIBUTE_SIZED_ACCESS(read_only, 4, 5) ATTRIBUTE_NONNULL_ALL_ARGS - DecFFTProduct(const dec_digit_t m_digits[], std::size_t m, const dec_digit_t k_digits[], - std::size_t k) - : product_size_(check_size(m + k)), + DecFFT(const dec_digit_t m_digits[], const dec_size_type m, + const dec_digit_t k_digits[], const dec_size_type k) + : product_size_(check_size_for_fft(m + k)), poly_size_(polys_size(product_size_)), poly_(create_and_fill_polynomials(m_digits, m, k_digits, k, poly_size_)) {} ATTRIBUTE_SIZED_ACCESS(read_only, 2, 3) ATTRIBUTE_NONNULL_ALL_ARGS - DecFFTProduct(const dec_digit_t digits[], std::size_t digits_size) - : product_size_(check_size(digits_size + digits_size)), + DecFFT(const dec_digit_t digits[], const dec_size_type digits_size) + : product_size_(check_size_for_fft(digits_size + digits_size)), poly_size_(polys_size(product_size_)), poly_(create_and_fill_polynomials(digits, digits_size, poly_size_)) {} - DecFFTProduct(const DecFFTProduct&) = delete; - DecFFTProduct(DecFFTProduct&&) = delete; - DecFFTProduct& operator=(const DecFFTProduct&) = delete; - DecFFTProduct& operator=(DecFFTProduct&&) = delete; - ~DecFFTProduct() { + DecFFT(const DecFFT&) = delete; + DecFFT(DecFFT&&) = delete; + DecFFT& operator=(const DecFFT&) = delete; + DecFFT& operator=(DecFFT&&) = delete; + ~DecFFT() { ::operator delete(static_cast(poly_)); } - void multiply_and_store_to(Decimal& product_result) const noexcept { + + void multiply_and_store_to_impl(Decimal& product_result) const { if (product_size_ > product_result.size_) { static_assert(sizeof(dec_digit_t) == sizeof(digit_t)); deallocate(product_result.digits_); @@ -1615,62 +1634,86 @@ struct longint { product_result.size_ = product_size_; } - private: - // clang-format off + static constexpr std::size_t kComplexNumsPerOneDecNum = 3; + static_assert(math_functions::bin_pow(kFFTDecimalBase, kComplexNumsPerOneDecNum) == + kDecimalBase, + ""); + static constexpr dec_size_type kMaxDecFFTSize = + std::numeric_limits::max() / kComplexNumsPerOneDecNum / 2; + + ATTRIBUTE_ALWAYS_INLINE + static dec_size_type check_size_for_fft(const dec_size_type value) { + if (unlikely(value > kMaxDecFFTSize)) { + throw_size_error(__FILE__, __LINE__, FUNCTION_MACRO, value, kMaxDecFFTSize); + } - static_assert(kFFTDecimalBase * kFFTDecimalBase * kFFTDecimalBase == kDecimalBase, ""); + return value; + } + + // clang-format off [[nodiscard]] ATTRIBUTE_CONST ATTRIBUTE_ALWAYS_INLINE - static constexpr std::size_t polys_size(std::size_t size) noexcept { - return math_functions::nearest_greater_equal_power_of_two( - 3 * static_cast(size)); + static constexpr dec_size_type polys_size(const dec_size_type size_value) noexcept { + ATTRIBUTE_ASSUME(size_value <= kMaxDecFFTSize); + static_assert(3 * kMaxDecFFTSize > kMaxDecFFTSize); + static_assert( + 3 * kMaxDecFFTSize < + math_functions::nearest_greater_equal_power_of_two(3 * kMaxDecFFTSize)); + static_assert(math_functions::nearest_greater_equal_power_of_two( + 3 * kMaxDecFFTSize) <= std::numeric_limits::max()); + return math_functions::nearest_greater_equal_power_of_two(3 * size_value); } + [[nodiscard]] ATTRIBUTE_RETURNS_NONNULL ATTRIBUTE_ALWAYS_INLINE - static fft::complex* allocate_polynomials(std::size_t size) { + static fft::complex* allocate_polynomials(const dec_size_type size_value) { // Allocate n for the first polynomial // and n for the second one - return static_cast(::operator new(size * 2 * sizeof(fft::complex))); + return static_cast(::operator new(size_value * 2 * sizeof(fft::complex))); } ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) ATTRIBUTE_SIZED_ACCESS(write_only, 6, 5) ATTRIBUTE_NONNULL_ALL_ARGS - static void fill_polynomial(const dec_digit_t m_digits[], std::size_t m, - const dec_digit_t k_digits[], std::size_t k, std::size_t n, + static void fill_polynomial(const dec_digit_t m_digits[], + const dec_size_type m, + const dec_digit_t k_digits[], + const dec_size_type k, + const dec_size_type n, fft::complex* RESTRICT_QUALIFIER p) noexcept { - for (std::size_t i = 0; i < m; i++) { + static_assert(kComplexNumsPerOneDecNum == 3); + for (dec_size_type i = 0; i < m; i++) { dec_digit_t m_value = m_digits[i]; dec_digit_t k_value = k_digits[i]; - uint32_t r1 = m_value % kFFTDecimalBase; + dec_digit_t r1 = m_value % kFFTDecimalBase; m_value /= kFFTDecimalBase; - uint32_t r2 = k_value % kFFTDecimalBase; + dec_digit_t r2 = k_value % kFFTDecimalBase; k_value /= kFFTDecimalBase; - *p = fft::complex(r1, r2); + *p = fft::complex{static_cast(r1), static_cast(r2)}; p++; r1 = m_value % kFFTDecimalBase; m_value /= kFFTDecimalBase; r2 = k_value % kFFTDecimalBase; k_value /= kFFTDecimalBase; - *p = fft::complex(r1, r2); + *p = fft::complex{static_cast(r1), static_cast(r2)}; p++; - *p = fft::complex(m_value, k_value); + *p = fft::complex{static_cast(m_value), static_cast(k_value)}; p++; } - for (std::size_t i = m; i < k; i++) { + for (dec_size_type i = m; i < k; i++) { dec_digit_t k_value = k_digits[i]; - uint32_t r2 = k_value % kFFTDecimalBase; + dec_digit_t r2 = k_value % kFFTDecimalBase; k_value /= kFFTDecimalBase; - *p = fft::complex(0, r2); + *p = fft::complex{0.0, static_cast(r2)}; p++; r2 = k_value % kFFTDecimalBase; k_value /= kFFTDecimalBase; - *p = fft::complex(0, r2); + *p = fft::complex{0.0, static_cast(r2)}; p++; - *p = fft::complex(0, k_value); + *p = fft::complex{0.0, static_cast(k_value)}; p++; } @@ -1679,23 +1722,24 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) ATTRIBUTE_SIZED_ACCESS(write_only, 4, 3) ATTRIBUTE_NONNULL_ALL_ARGS - static void fill_polynomial(const dec_digit_t digits[], std::size_t digits_size, - std::size_t n, fft::complex* RESTRICT_QUALIFIER p) noexcept { - for (std::size_t i = 0; i < digits_size; i++) { - uint32_t value = digits[i]; - uint32_t r1 = value % kFFTDecimalBase; + static void fill_polynomial(const dec_digit_t* RESTRICT_QUALIFIER digits, const dec_size_type digits_size, + const dec_size_type n, fft::complex* RESTRICT_QUALIFIER p) noexcept { + static_assert(kComplexNumsPerOneDecNum == 3); + for (dec_size_type i = 0; i < digits_size; i++) { + dec_digit_t value = digits[i]; + dec_digit_t r1 = value % kFFTDecimalBase; value /= kFFTDecimalBase; - *p = fft::complex(r1, r1); + *p = fft::complex{static_cast(r1), static_cast(r1)}; p++; r1 = value % kFFTDecimalBase; value /= kFFTDecimalBase; - *p = fft::complex(r1, r1); + *p = fft::complex{static_cast(r1), static_cast(r1)}; p++; - *p = fft::complex(value, value); + *p = fft::complex{static_cast(value), static_cast(value)}; p++; } - std::memset(static_cast(p), 0, (n - 3 * digits_size) * sizeof(fft::complex)); + std::memset(static_cast(p), 0, (n - kComplexNumsPerOneDecNum * digits_size) * sizeof(fft::complex)); } [[nodiscard]] ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) @@ -1704,9 +1748,9 @@ struct longint { ATTRIBUTE_RETURNS_NONNULL ATTRIBUTE_ALWAYS_INLINE static fft::complex* create_and_fill_polynomials(const dec_digit_t m_digits[], - std::size_t m, + const dec_size_type m, const dec_digit_t k_digits[], - std::size_t k, std::size_t n) { + const dec_size_type k, const dec_size_type n) { fft::complex* const polys = allocate_polynomials(n); assert(polys != nullptr); fill_polynomial(m_digits, m, k_digits, k, n, polys); @@ -1718,8 +1762,8 @@ struct longint { ATTRIBUTE_RETURNS_NONNULL ATTRIBUTE_ALWAYS_INLINE static fft::complex* create_and_fill_polynomials(const dec_digit_t digits[], - std::size_t digits_size, - std::size_t n) { + const dec_size_type digits_size, + const dec_size_type n) { fft::complex* const polys = allocate_polynomials(n); assert(polys != nullptr); fill_polynomial(digits, digits_size, n, polys); @@ -1728,20 +1772,20 @@ struct longint { ATTRIBUTE_ACCESS(read_only, 1) ATTRIBUTE_SIZED_ACCESS(write_only, 2, 3) ATTRIBUTE_NONNULL_ALL_ARGS - static void convert_fft_poly_to_decimal_digits(const fft::complex poly[], - dec_digit_t digits[], - std::size_t digits_size) noexcept { - static_assert(sizeof(digit_t) == sizeof(uint32_t)); - uint64_t carry = 0; + static void convert_fft_poly_to_decimal_digits(const fft::complex* RESTRICT_QUALIFIER poly, + dec_digit_t* RESTRICT_QUALIFIER digits, + const dec_size_type digits_size) noexcept { + static_assert(sizeof(dec_digit_t) == sizeof(uint32_t)); + double_dec_digit_t carry = 0; const dec_digit_t* const digits_end = digits + digits_size; do { - uint64_t res = carry; - res += static_cast(poly->real() + kFFTFloatRoundError); + double_dec_digit_t res = carry; + res += static_cast(poly->real() + kFFTFloatRoundError); poly++; res += - static_cast(poly->real() + kFFTFloatRoundError) * kFFTDecimalBase; + static_cast(poly->real() + kFFTFloatRoundError) * kFFTDecimalBase; poly++; - res += static_cast(poly->real() + kFFTFloatRoundError) * + res += static_cast(poly->real() + kFFTFloatRoundError) * (kFFTDecimalBase * kFFTDecimalBase); poly++; *digits = static_cast(res % kDecimalBase); @@ -1767,7 +1811,7 @@ struct longint { [[nodiscard]] ATTRIBUTE_PURE ATTRIBUTE_ALWAYS_INLINE - constexpr std::size_t poly_size() const noexcept { + constexpr dec_size_type poly_size() const noexcept { const auto value = poly_size_; ATTRIBUTE_ASSUME(math_functions::is_power_of_two(value)); return value; @@ -1775,95 +1819,19 @@ struct longint { // clang-format on - std::size_t const product_size_; - std::size_t const poly_size_; + dec_size_type const product_size_; + dec_size_type const poly_size_; fft::complex* const poly_; }; - [[nodiscard]] - ATTRIBUTE_PURE constexpr bool operator==(uint32_t n) const noexcept { - switch (size_) { - case 0: - return n == 0; - case 1: - return digits_[0] == n; - case 2: - return static_cast(digits_[1]) * kDecimalBase + digits_[0] == n; - default: - return false; - } - } - - [[nodiscard]] - ATTRIBUTE_PURE constexpr bool operator!=(uint32_t n) const noexcept { - return !(*this == n); - } - - [[nodiscard]] - ATTRIBUTE_PURE constexpr bool operator==(uint64_t n) const noexcept { - switch (size_) { - case 0: - return n == 0; - case 1: - return digits_[0] == n; - case 2: - return static_cast(digits_[1]) * kDecimalBase + digits_[0] == n; - case 3: { - constexpr uint64_t kDecimalBase2 = - static_cast(kDecimalBase) * kDecimalBase; - const uint64_t hi = digits_[2]; - if (hi > 18) { - return false; - } - - const uint64_t low_mid_m = - static_cast(digits_[1]) * kDecimalBase + digits_[0]; - if (hi == 18) { - return n >= 18 * kDecimalBase2 && n - 18 * kDecimalBase2 == low_mid_m; - } - const uint64_t m = static_cast(hi) * kDecimalBase2 + low_mid_m; - return m == n; - } - default: - return false; - } - } - - [[nodiscard]] - ATTRIBUTE_PURE constexpr bool operator!=(uint64_t n) const noexcept { - return !(*this == n); - } - - [[nodiscard]] - ATTRIBUTE_PURE constexpr bool operator==(const Decimal& other) const noexcept { - return size_ == other.size_ && std::equal(digits_, digits_ + size_, other.digits_); - } - - constexpr void set_zero() noexcept { - size_ = 0; - } - constexpr void pop_leading_zeros() noexcept { - std::size_t usize_value = size_; - while (usize_value > 0 && digits_[usize_value - 1] == 0) { - usize_value--; - } - - size_ = usize_value; - } - - ~Decimal() { - deallocate(digits_); - } - - private: - constexpr void assign_uint32_unchecked(uint32_t n) noexcept { + constexpr void assign_uint32_unchecked(const uint32_t n) noexcept { const dec_digit_t low = n % kDecimalBase; digits_[0] = low; const dec_digit_t hi = n / kDecimalBase; digits_[1] = hi; size_ = hi != 0 ? 2 : low != 0; } - constexpr void assign_uint64_unchecked(uint64_t n) noexcept { + constexpr void assign_uint64_unchecked(const uint64_t n) noexcept { const auto low = static_cast(n % kDecimalBase); const uint64_t t = n / kDecimalBase; const auto mid = static_cast(t % kDecimalBase); @@ -1878,7 +1846,226 @@ struct longint { private: static constexpr bool kUseCustomLongIntAllocator = false; + struct LongIntNaive final { + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) + ATTRIBUTE_ACCESS(read_write, 5) + static constexpr void multiply_and_store_to(const digit_t m_ptr[], const size_type m, + const digit_t k_ptr[], const size_type k, + digit_t* const ans) noexcept { + ATTRIBUTE_ASSUME(m <= k); + digit_t* ans_store_ptr = ans; + for (size_type j = 0; j < m; ans_store_ptr++, j++) { + const double_digit_t b_j = m_ptr[j]; + double_digit_t carry = 0; + for (std::size_t i = 0; i < k; i++) { + const double_digit_t a_i = k_ptr[i]; + const double_digit_t res = a_i * b_j + double_digit_t{ans_store_ptr[i]} + carry; + ans_store_ptr[i] = static_cast(res); + carry = res / kNumsBase; + } + + ans_store_ptr[k] = static_cast(carry); + } + } + }; + + struct LongIntFFT final { + ATTRIBUTE_NODISCARD_WITH_MESSAGE("impl error in fft longint product") + ATTRIBUTE_ALWAYS_INLINE + ATTRIBUTE_CONST + static constexpr std::pair compute_fft_product_params( + size_type product_size) noexcept { + std::size_t n = 2 * math_functions::nearest_greater_equal_power_of_two(product_size); + const bool need_high_precision = n > kFFTPrecisionBorder; + n <<= need_high_precision; + ATTRIBUTE_ASSUME(math_functions::is_power_of_two(n)); + return {n, need_high_precision}; + } + + ATTRIBUTE_NONNULL_ALL_ARGS + ATTRIBUTE_ACCESS(read_only, 2) + ATTRIBUTE_SIZED_ACCESS(write_only, 3, 4) + static void convert_fft_poly_to_longint_nums(const bool is_high_precision, + const fft::complex* RESTRICT_QUALIFIER poly, + digit_t* RESTRICT_QUALIFIER nums, + const size_type nums_size) { + static_assert(sizeof(digit_t) == sizeof(uint32_t)); + double_digit_t carry = 0; + const digit_t* const nums_end = nums + nums_size; + if (likely(!is_high_precision)) { + do { + double_digit_t res = carry; + res += static_cast(poly->real() + kFFTFloatRoundError); + poly++; + res += static_cast(poly->real() + kFFTFloatRoundError) << 16; + poly++; + *nums = static_cast(res); + carry = res / kNumsBase; + nums++; + } while (nums != nums_end); + } else { + do { + double_digit_t res = carry; + res += static_cast(poly->real() + kFFTFloatRoundError); + poly++; + res += static_cast(poly->real() + kFFTFloatRoundError) << 8; + poly++; + res += static_cast(poly->real() + kFFTFloatRoundError) << 16; + poly++; + res += static_cast(poly->real() + kFFTFloatRoundError) << 24; + poly++; + *nums = static_cast(res); + carry = res / kNumsBase; + nums++; + } while (nums != nums_end); + } + assert(carry == 0); + } + + ATTRIBUTE_NONNULL_ALL_ARGS + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) + ATTRIBUTE_SIZED_ACCESS(write_only, 5, 6) + static void convert_longint_nums_to_fft_poly(const digit_t m_ptr[], const size_type m, + const digit_t k_ptr[], const size_type k, + fft::complex* p, const std::size_t n, + bool need_high_precision) noexcept { + ATTRIBUTE_ASSUME(0 < m); + ATTRIBUTE_ASSUME(m <= k); + ATTRIBUTE_ASSUME(m <= max_size()); + ATTRIBUTE_ASSUME(k <= max_size()); + ATTRIBUTE_ASSUME(m + k <= max_size()); + ATTRIBUTE_ASSUME(m + k <= n); + ATTRIBUTE_ASSUME(need_high_precision || n <= kFFTPrecisionBorder); + ATTRIBUTE_ASSUME(!need_high_precision || n > kFFTPrecisionBorder * 2); + ATTRIBUTE_ASSUME(math_functions::is_power_of_two(n)); + + static_assert(kNumsBits == 32); + if (likely(!need_high_precision)) { + for (size_type i = 0; i < m; i++) { + digit_t m_value = m_ptr[i]; + digit_t k_value = k_ptr[i]; + + *p = fft::complex{static_cast(static_cast(m_value)), + static_cast(static_cast(k_value))}; + p++; + m_value >>= 16; + k_value >>= 16; + *p = fft::complex{static_cast(static_cast(m_value)), + static_cast(static_cast(k_value))}; + p++; + } + for (size_type i = m; i < k; i++) { + const digit_t k_value = k_ptr[i]; + + *p = fft::complex{0.0, static_cast(k_value & 0xFFFF)}; + p++; + *p = fft::complex{0.0, static_cast(k_value >> 16)}; + p++; + } + } else { + for (std::size_t i = 0; i < m; i++) { + digit_t m_value = m_ptr[i]; + digit_t k_value = k_ptr[i]; + + *p = fft::complex{static_cast(static_cast(m_value)), + static_cast(static_cast(k_value))}; + p++; + m_value >>= 8; + k_value >>= 8; + *p = fft::complex{static_cast(static_cast(m_value)), + static_cast(static_cast(k_value))}; + p++; + m_value >>= 8; + k_value >>= 8; + *p = fft::complex{static_cast(static_cast(m_value)), + static_cast(static_cast(k_value))}; + p++; + m_value >>= 8; + k_value >>= 8; + *p = fft::complex{static_cast(static_cast(m_value)), + static_cast(static_cast(k_value))}; + p++; + } + for (std::size_t i = m; i < k; i++) { + digit_t k_value = k_ptr[i]; + + *p = fft::complex{0.0, static_cast(static_cast(k_value))}; + p++; + k_value >>= 8; + *p = fft::complex{0.0, static_cast(static_cast(k_value))}; + p++; + k_value >>= 8; + *p = fft::complex{0.0, static_cast(static_cast(k_value))}; + p++; + k_value >>= 8; + *p = fft::complex{0.0, static_cast(static_cast(k_value))}; + p++; + } + } + const size_type complex_nums_filled = (2 * k) << need_high_precision; + std::memset(static_cast(p), 0, (n - complex_nums_filled) * sizeof(fft::complex)); + } + + ATTRIBUTE_NONNULL_ALL_ARGS + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + ATTRIBUTE_SIZED_ACCESS(write_only, 3, 4) + static void convert_longint_nums_to_fft_poly(const digit_t nums_ptr[], + const size_type nums_size, fft::complex* p, + const std::size_t n, + bool need_high_precision) noexcept { + ATTRIBUTE_ASSUME(0 < nums_size); + ATTRIBUTE_ASSUME(nums_size <= max_size()); + ATTRIBUTE_ASSUME(nums_size * 2 <= n); + ATTRIBUTE_ASSUME(need_high_precision || n <= kFFTPrecisionBorder); + ATTRIBUTE_ASSUME(!need_high_precision || n > kFFTPrecisionBorder * 2); + ATTRIBUTE_ASSUME(math_functions::is_power_of_two(n)); + + static_assert(kNumsBits == 32); + if (likely(!need_high_precision)) { + for (size_type i = 0; i < nums_size; i++) { + digit_t value = nums_ptr[i]; + + *p = fft::complex{static_cast(static_cast(value)), + static_cast(static_cast(value))}; + p++; + value >>= 16; + *p = fft::complex{static_cast(static_cast(value)), + static_cast(static_cast(value))}; + p++; + } + } else { + for (std::size_t i = 0; i < nums_size; i++) { + digit_t value = nums_ptr[i]; + + *p = fft::complex{static_cast(static_cast(value)), + static_cast(static_cast(value))}; + p++; + value >>= 8; + *p = fft::complex{static_cast(static_cast(value)), + static_cast(static_cast(value))}; + p++; + value >>= 8; + *p = fft::complex{static_cast(static_cast(value)), + static_cast(static_cast(value))}; + p++; + value >>= 8; + *p = fft::complex{static_cast(static_cast(value)), + static_cast(static_cast(value))}; + p++; + } + } + const size_type complex_nums_filled = (2 * nums_size) << need_high_precision; + std::memset(static_cast(p), 0, (n - complex_nums_filled) * sizeof(fft::complex)); + } + }; + + ATTRIBUTE_SIZED_ACCESS(read_only, 2, 3) + inline void set_str_impl(const unsigned char* str, const std::size_t str_size); + [[nodiscard]] constexpr uint32_t divmod_impl(const uint32_t n) noexcept { + static_assert(sizeof(digit_t) == sizeof(uint32_t)); double_digit_t carry = 0; for (digit_t *const nums_iter_rend = nums_ - 1, *nums_riter = nums_iter_rend + usize(); nums_riter != nums_iter_rend; --nums_riter) { @@ -1898,15 +2085,15 @@ struct longint { /** * See Hackers Delight 9-2. */ - const std::size_t m = usize(); - const std::size_t n = other.usize(); + const size_type m = usize(); + const size_type n = other.usize(); if (m < n) { rem = std::move(*this); this->set_zero(); return; } - const std::int32_t sign_product = size() ^ other.size(); + const ssize_type sign_product = size() ^ other.size(); switch (n) { case 0: /* Quite return when dividing by zero. */ @@ -1919,15 +2106,15 @@ struct longint { break; } - rem.reserveUninitializedWithoutCopy(static_cast(n)); - rem.size_ = static_cast(n); + rem.reserveUninitializedWithoutCopy(n); + rem.size_ = static_cast(n); // Normilize by shifting v left just enough so that // its high-order bit i on, and shift u left the // same amount. We may have to append a high-order // digit on the dividend; we do that unconditionally (un size = m + -> 1 <-). - digit_t* const vn_and_un = allocate(n + m + 1); + digit_t* const vn_and_un = allocate(std::size_t{n + m} + 1); digit_t* const vn = vn_and_un; digit_t* const un = vn_and_un + n; @@ -1953,8 +2140,7 @@ struct longint { longint::divmod_unnormalize_remainder(rem.nums_, un, n, n + 1, s); deallocate(vn_and_un); rem.pop_leading_zeros(); - auto signed_size = static_cast(m - n + 1); - size_ = sign_product >= 0 ? signed_size : -signed_size; + set_ssize_from_size_and_sign(m - n + 1, /* sign = */ sign_product); pop_leading_zeros(); } @@ -1964,13 +2150,13 @@ struct longint { ATTRIBUTE_NONNULL_ALL_ARGS ATTRIBUTE_ALWAYS_INLINE static constexpr void divmod_impl_unchecked(digit_t* RESTRICT_QUALIFIER const un, - const std::size_t un_size, + const size_type un_size, const digit_t* RESTRICT_QUALIFIER const vn, - const std::size_t vn_size, + const size_type vn_size, digit_t* RESTRICT_QUALIFIER const quot) noexcept { ATTRIBUTE_ASSUME(vn_size >= 2); ATTRIBUTE_ASSUME(un_size > vn_size); - for (std::size_t j = un_size - vn_size - 1; static_cast(j) >= 0; j--) { + for (size_type j = un_size - vn_size - 1; static_cast(j) >= 0; j--) { // Compute estimate qhat of q[j]. const double_digit_t cur = (double_digit_t{un[j + vn_size]} << kNumsBits) | un[j + vn_size - 1]; @@ -2016,11 +2202,11 @@ struct longint { ATTRIBUTE_ALWAYS_INLINE static constexpr double_digit_t divmod_mult_sub(digit_t* RESTRICT_QUALIFIER const un, const digit_t* RESTRICT_QUALIFIER const vn, - const std::size_t vn_size, + const size_type vn_size, const double_digit_t qhat) noexcept { ATTRIBUTE_ASSUME(vn_size >= 2); double_digit_t carry = 0; - for (std::size_t i = 0; i < vn_size; i++) { + for (size_type i = 0; i < vn_size; i++) { double_digit_t p = qhat * vn[i]; double_digit_t t = double_digit_t{un[i]} - carry - static_cast(p % kNumsBase); un[i] = static_cast(t % kNumsBase); @@ -2037,10 +2223,10 @@ struct longint { ATTRIBUTE_ALWAYS_INLINE static constexpr void divmod_add_back(digit_t* RESTRICT_QUALIFIER const un, const digit_t* RESTRICT_QUALIFIER const vn, - const std::size_t vn_size) noexcept { + const size_type vn_size) noexcept { ATTRIBUTE_ASSUME(vn_size >= 2); double_digit_t carry = 0; - for (std::size_t i = 0; i < vn_size; i++) { + for (size_type i = 0; i < vn_size; i++) { double_digit_t t = double_digit_t{un[i]} + double_digit_t{vn[i]} + carry; un[i] = static_cast(t % kNumsBase); carry = t / kNumsBase; @@ -2052,11 +2238,11 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(read_only, 2, 3) ATTRIBUTE_NONNULL_ALL_ARGS ATTRIBUTE_ALWAYS_INLINE - static constexpr void divmod_normalize_vn(digit_t vn[], const digit_t v[], std::size_t n, + static constexpr void divmod_normalize_vn(digit_t vn[], const digit_t v[], size_type n, std::uint32_t s) noexcept { ATTRIBUTE_ASSUME(n > 1); ATTRIBUTE_ASSUME(s < 32); - for (std::size_t i = n - 1; i > 0; i--) { + for (size_type i = n - 1; i > 0; i--) { vn[i] = (v[i] << s) | static_cast(double_digit_t{v[i - 1]} >> (kNumsBits - s)); } vn[0] = v[0] << s; @@ -2066,14 +2252,14 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(read_only, 2, 3) ATTRIBUTE_NONNULL_ALL_ARGS ATTRIBUTE_ALWAYS_INLINE - static constexpr void divmod_normalize_un(digit_t un[], const digit_t u[], std::size_t m, - ATTRIBUTE_MAYBE_UNUSED std::size_t m_plus_one, + static constexpr void divmod_normalize_un(digit_t un[], const digit_t u[], size_type m, + ATTRIBUTE_MAYBE_UNUSED size_type m_plus_one, std::uint32_t s) noexcept { ATTRIBUTE_ASSUME(m > 1); ATTRIBUTE_ASSUME(s < 32); ATTRIBUTE_ASSUME(m + 1 == m_plus_one); un[m] = static_cast(double_digit_t{u[m - 1]} >> (kNumsBits - s)); - for (std::size_t i = m - 1; i > 0; i--) { + for (size_type i = m - 1; i > 0; i--) { un[i] = (u[i] << s) | static_cast(double_digit_t{u[i - 1]} >> (kNumsBits - s)); } un[0] = u[0] << s; @@ -2083,24 +2269,32 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(read_only, 2, 4) ATTRIBUTE_NONNULL_ALL_ARGS ATTRIBUTE_ALWAYS_INLINE - static constexpr void divmod_unnormalize_remainder( - digit_t rem[], const digit_t un[], std::size_t n, - ATTRIBUTE_MAYBE_UNUSED std::size_t n_plus_one, std::uint32_t s) noexcept { + static constexpr void divmod_unnormalize_remainder(digit_t rem[], const digit_t un[], + size_type n, + ATTRIBUTE_MAYBE_UNUSED size_type n_plus_one, + std::uint32_t s) noexcept { ATTRIBUTE_ASSUME(n > 1); ATTRIBUTE_ASSUME(s < 32); ATTRIBUTE_ASSUME(n + 1 == n_plus_one); - for (std::size_t i = 0; i < n; i++) { + for (size_type i = 0; i < n; i++) { rem[i] = (un[i] >> s) | static_cast(double_digit_t{un[i + 1]} << (kNumsBits - s)); } } + ATTRIBUTE_ALWAYS_INLINE constexpr void set_ssize_from_size(size_type size) noexcept { - size_ = static_cast(size_ >= 0 ? size : -size); + this->set_ssize_from_size_and_sign(size, /* sign = */ size_); + } + + ATTRIBUTE_ALWAYS_INLINE + constexpr void set_ssize_from_size_and_sign(size_type size, ssize_type sign) noexcept { + static_assert(sizeof(ssize_type) == sizeof(size_type)); + size_ = static_cast(sign >= 0 ? size : -size); } constexpr void pop_leading_zeros() noexcept { - auto usize_value = usize32(); + auto usize_value = usize(); while (usize_value > 0 && nums_[usize_value - 1] == 0) { usize_value--; } @@ -2108,7 +2302,7 @@ struct longint { set_ssize_from_size(usize_value); } - void reserveUninitializedWithoutCopy(const uint32_t capacity) { + void reserveUninitializedWithoutCopy(const size_type capacity) { if (capacity > capacity_) { deallocate(nums_); nums_ = allocate(capacity); @@ -2117,41 +2311,6 @@ struct longint { size_ = 0; } - static void convert_fft_poly_to_longint_nums(bool is_high_precision, fft::complex* poly, - digit_t* nums, std::size_t nums_size) { - static_assert(sizeof(digit_t) == sizeof(uint32_t)); - double_digit_t carry = 0; - const digit_t* nums_end = nums + nums_size; - if (likely(!is_high_precision)) { - do { - double_digit_t res = carry; - res += static_cast(poly->real() + kFFTFloatRoundError); - poly++; - res += static_cast(poly->real() + kFFTFloatRoundError) << 16; - poly++; - *nums = static_cast(res); - carry = res / kNumsBase; - nums++; - } while (nums != nums_end); - } else { - do { - double_digit_t res = carry; - res += static_cast(poly->real() + kFFTFloatRoundError); - poly++; - res += static_cast(poly->real() + kFFTFloatRoundError) << 8; - poly++; - res += static_cast(poly->real() + kFFTFloatRoundError) << 16; - poly++; - res += static_cast(poly->real() + kFFTFloatRoundError) << 24; - poly++; - *nums = static_cast(res); - carry = res / kNumsBase; - nums++; - } while (nums != nums_end); - } - assert(carry == 0); - } - static std::vector create_initial_conv_bin_base_pows() { std::vector pows; auto local_copy{longint::kNumsBase}; @@ -2164,104 +2323,61 @@ struct longint { friend struct longint_detail::longint_static_storage; - static void convertDecBaseMultAdd(digit_t conv_digits[], std::size_t half_len, - const longint* conv_base_pow, digit_t mult_add_buffer[], + ATTRIBUTE_ALWAYS_INLINE + ATTRIBUTE_SIZED_ACCESS(read_write, 1, 2) + ATTRIBUTE_SIZED_ACCESS(read_write, 4, 2) + ATTRIBUTE_ACCESS(read_write, 5) + static void convertDecBaseMultAdd(digit_t conv_digits[], const size_type conv_len, + const longint& conv_base_pow, digit_t mult_add_buffer[], fft::complex fft_poly_buffer[]) { - const digit_t* num_hi = conv_digits + half_len; - std::size_t m = static_cast(conv_base_pow->size_); - assert(m > 0 && m <= half_len); - const uint32_t* m_ptr = conv_base_pow->nums_; - std::size_t prod_size = m + half_len; - std::fill_n(mult_add_buffer, half_len + half_len, digit_t{0}); - if (half_len <= 32) { - for (std::size_t j = 0; j < m; j++) { - const double_digit_t b_j = m_ptr[j]; - double_digit_t carry = 0; - for (std::size_t i = 0; i < half_len; i++) { - const double_digit_t a_i = num_hi[i]; - const double_digit_t res = - a_i * b_j + double_digit_t{mult_add_buffer[j + i]} + carry; - mult_add_buffer[j + i] = static_cast(res); - carry = res >> kNumsBits; - } + ATTRIBUTE_ASSUME(0 < conv_base_pow.size_); + const size_type m_size = conv_base_pow.usize(); + const digit_t* const m_ptr = conv_base_pow.nums_; + assert(0 < m_size && m_size <= conv_len / 2); + convertDecBaseMultAddImpl(conv_digits, conv_len, m_ptr, m_size, mult_add_buffer, + fft_poly_buffer); + } - mult_add_buffer[j + half_len] = static_cast(carry); - } + ATTRIBUTE_SIZED_ACCESS(read_write, 1, 2) + ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) + ATTRIBUTE_SIZED_ACCESS(read_write, 5, 2) + ATTRIBUTE_ACCESS(read_write, 6) + static void convertDecBaseMultAddImpl(digit_t conv_digits[], const size_type conv_len, + const digit_t m_ptr[], const size_type m_size, + digit_t mult_add_buffer[], + fft::complex fft_poly_buffer[]) { + const size_type half_conv_len = conv_len / 2; + ATTRIBUTE_ASSUME(0 < m_size); + ATTRIBUTE_ASSUME(m_size <= half_conv_len); + ATTRIBUTE_ASSUME(math_functions::is_power_of_two(half_conv_len)); + ATTRIBUTE_ASSUME(conv_len <= max_size()); + const digit_t* num_hi = conv_digits + half_conv_len; + static_assert(max_size() + max_size() > max_size()); + const size_type prod_size = m_size + half_conv_len; + std::fill_n(mult_add_buffer, conv_len, digit_t{0}); + if (half_conv_len <= 32) { + LongIntNaive::multiply_and_store_to(m_ptr, m_size, num_hi, half_conv_len, + mult_add_buffer); } else { - std::size_t n = 2 * math_functions::nearest_greater_equal_power_of_two( - static_cast(prod_size)); - const bool high_precision = n > kFFTPrecisionBorder; - n <<= high_precision; - fft::complex* p1 = fft_poly_buffer; - fft::complex* p = p1; - if (likely(!high_precision)) { - for (std::size_t i = 0; i < m; i++) { - digit_t m_value = m_ptr[i]; - digit_t k_value = num_hi[i]; - *p = fft::complex(m_value & 0xFFFF, k_value & 0xFFFF); - p++; - *p = fft::complex(m_value >> 16, k_value >> 16); - p++; - } - for (std::size_t i = m; i < half_len; i++) { - digit_t k_value = num_hi[i]; - *p = fft::complex(0, k_value & 0xFFFF); - p++; - *p = fft::complex(0, k_value >> 16); - p++; - } - } else { - for (std::size_t i = 0; i < m; i++) { - uint32_t m_value = m_ptr[i]; - uint32_t k_value = num_hi[i]; - *p = fft::complex(static_cast(m_value), static_cast(k_value)); - p++; - m_value >>= 8; - k_value >>= 8; - *p = fft::complex(static_cast(m_value), static_cast(k_value)); - p++; - m_value >>= 8; - k_value >>= 8; - *p = fft::complex(static_cast(m_value), static_cast(k_value)); - p++; - m_value >>= 8; - k_value >>= 8; - *p = fft::complex(m_value, k_value); - p++; - } - for (std::size_t i = m; i < half_len; i++) { - uint32_t k_value = num_hi[i]; - *p = fft::complex(0, static_cast(k_value)); - p++; - k_value >>= 8; - *p = fft::complex(0, static_cast(k_value)); - p++; - k_value >>= 8; - *p = fft::complex(0, static_cast(k_value)); - p++; - k_value >>= 8; - *p = fft::complex(0, k_value); - p++; - } - } - - std::memset(static_cast(p), 0, - (n - ((2 * half_len) << high_precision)) * sizeof(fft::complex)); - + const auto [n, need_high_precision] = LongIntFFT::compute_fft_product_params(prod_size); + fft::complex* const p1 = fft_poly_buffer; + LongIntFFT::convert_longint_nums_to_fft_poly(m_ptr, m_size, num_hi, half_conv_len, p1, + n, need_high_precision); fft::complex* const p2 = p1 + n; fft::forward_backward_fft(p1, p2, n); - convert_fft_poly_to_longint_nums(high_precision, p2, mult_add_buffer, prod_size); + LongIntFFT::convert_fft_poly_to_longint_nums(need_high_precision, p2, mult_add_buffer, + prod_size); } // Now mult_add_buffer == num_hi * CONV_BASE^half_len double_digit_t carry = 0; - for (std::size_t i = half_len; i > 0; i--, conv_digits++, mult_add_buffer++) { + for (std::size_t i = half_conv_len; i > 0; i--, conv_digits++, mult_add_buffer++) { const double_digit_t res = double_digit_t{*conv_digits} + double_digit_t{*mult_add_buffer} + carry; *conv_digits = static_cast(res); carry = res >> kNumsBits; } - for (std::size_t i = half_len; i > 0; i--, conv_digits++, mult_add_buffer++) { + for (std::size_t i = half_conv_len; i > 0; i--, conv_digits++, mult_add_buffer++) { const double_digit_t res = double_digit_t{*mult_add_buffer} + carry; *conv_digits = static_cast(res); carry = res >> kNumsBits; @@ -2269,8 +2385,8 @@ struct longint { assert(carry == 0); } - static Decimal convertBinBase(const digit_t* nums, std::size_t size) { - assert(math_functions::is_power_of_two(size)); + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + static Decimal convertBinBaseImpl(const digit_t nums[], const std::size_t size) { ATTRIBUTE_ASSUME(math_functions::is_power_of_two(size)); switch (size) { case 0: @@ -2285,13 +2401,21 @@ struct longint { Decimal low_dec = convertBinBase(nums, size / 2); Decimal high_dec = convertBinBase(nums + size / 2, size / 2); - const std::size_t idx = math_functions::log2_floor(static_cast(size)) - 1; + const std::size_t idx = math_functions::log2_floor(std::uint64_t{size}) - 1; assert(idx < conv_bin_base_pows.size()); high_dec *= conv_bin_base_pows[idx]; high_dec += low_dec; return high_dec; } + ATTRIBUTE_NODISCARD_WITH_MESSAGE("impl error: must use result of convertBinBase") + ATTRIBUTE_ALWAYS_INLINE + ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) + static Decimal convertBinBase(const digit_t nums[], const std::size_t size) { + assert(math_functions::is_power_of_two(size)); + return convertBinBaseImpl(nums, size); + } + static void ensureBinBasePowsCapacity(std::size_t pows_size) { std::size_t i = conv_bin_base_pows.size(); if (i >= pows_size) { @@ -2308,18 +2432,8 @@ struct longint { reserve(static_cast(capacity_) * 2 | (capacity_ == 0)); } - ATTRIBUTE_NOINLINE ATTRIBUTE_COLD std::size_t growSizeByOne() { - std::size_t usize_value = usize(); - if (unlikely(usize_value == capacity_)) { - growCapacity(); - } - - size_ += sign(); - return usize_value + 1; - } - size_type set_size_at_least(size_type new_size) { - size_type cur_size = usize32(); + size_type cur_size = usize(); if (new_size <= cur_size) { return cur_size; } @@ -2347,8 +2461,8 @@ struct longint { } constexpr void assign_i32_unchecked(int32_t n) noexcept { static_assert(kNumsBits >= 32); - size_ = ::math_functions::sign(n); - nums_[0] = ::math_functions::uabs(n); + size_ = math_functions::sign(n); + nums_[0] = math_functions::uabs(n); } void allocate_default_capacity_64() { nums_ = allocate(kDefaultLINumsCapacity64); @@ -2369,8 +2483,8 @@ struct longint { nums_[1] = static_cast(n); } constexpr void assign_i64_unchecked(int64_t n) noexcept { - const std::int32_t sgn = ::math_functions::sign(n); - this->assign_u64_unchecked(::math_functions::uabs(n)); + const std::int32_t sgn = math_functions::sign(n); + this->assign_u64_unchecked(math_functions::uabs(n)); size_ *= sgn; } #if defined(INTEGERS_128_BIT_HPP) @@ -2398,8 +2512,8 @@ struct longint { nums_[3] = static_cast(n); } constexpr void assign_i128_unchecked(int128_t n) noexcept { - const std::int32_t sgn = ::math_functions::sign(n); - assign_u128_unchecked(::math_functions::uabs(n)); + const std::int32_t sgn = math_functions::sign(n); + assign_u128_unchecked(math_functions::uabs(n)); size_ *= sgn; } #endif @@ -2490,23 +2604,25 @@ struct longint { } while (it != end_iter); if (carry != 0) { - std::size_t usize_value = usize(); - if (unlikely(usize_value == capacity_)) { + const size_type usize_value = usize(); + assert(usize_value <= capacity_); + if (unlikely(usize_value >= capacity_)) { growCapacity(); + assert(usize_value < capacity_); } - assert(usize_value < capacity_); nums_[usize_value] = static_cast(carry); size_ += sign(); } } constexpr void nonZeroSizeSubUInt(uint32_t n) { - std::size_t usize_value = usize(); - uint32_t* nums_iter = nums_; - uint32_t low_num = nums_iter[0]; + static_assert(sizeof(digit_t) == sizeof(uint32_t)); + const size_type usize_value = usize(); + digit_t* nums_iter = nums_; + digit_t low_num = nums_iter[0]; if (usize_value != 1) { - uint32_t res = low_num - n; + digit_t res = low_num - n; bool carry = res > low_num; nums_iter[0] = res; if (carry) { @@ -2552,26 +2668,27 @@ struct longint { const auto size_bytes = size * sizeof(fft::complex); return static_cast(::operator new(size_bytes)); } - ATTRIBUTE_ALWAYS_INLINE static std::uint32_t check_size(const std::size_t value) { + ATTRIBUTE_ALWAYS_INLINE static size_type check_size(const std::size_t value) { if (unlikely(value > max_size())) { - throw_size_error(value, __FILE__, __LINE__, FUNCTION_MACRO); + throw_size_error(__FILE__, __LINE__, FUNCTION_MACRO, value, max_size()); } - static_assert(max_size() <= std::numeric_limits::max(), - "implementation error"); - const auto checked_value = static_cast(value); + const size_type checked_value = static_cast(value); if (checked_value > max_size()) { CONFIG_UNREACHABLE(); } ATTRIBUTE_ASSUME(checked_value == value); return checked_value; } - [[noreturn]] ATTRIBUTE_NOINLINE ATTRIBUTE_COLD static void throw_size_error( - std::size_t new_size, const char* file_name, std::uint32_t line, - const char* function_name) { + [[noreturn]] + ATTRIBUTE_NOINLINE ATTRIBUTE_COLD static void throw_size_error(const char* file_name, + std::uint32_t line, + const char* function_name, + std::size_t new_size_value, + std::size_t max_size_value) { char message[1024] = {}; const int bytes_written = std::snprintf( - message, std::size(message), "%s:%u: size error at %s: %zu > %u = max_size()", - file_name, line, function_name, new_size, max_size()); + message, std::size(message), "%s:%u: size error at %s: %zu > %zu (max size)", file_name, + line, function_name, new_size_value, max_size_value); if (unlikely(bytes_written < 0)) { constexpr const char kFallbackMessage[] = "size error at "; constexpr auto kPrefixSize = std::size(kFallbackMessage) - 1; @@ -2591,8 +2708,8 @@ struct longint { /** * size_ < 0 <=> sign = -1; size_ == 0 <=> sign = 0; size > 0 <=> sign = 1 */ - std::int32_t size_ = 0; - std::uint32_t capacity_ = 0; + ssize_type size_ = 0; + size_type capacity_ = 0; }; namespace longint_detail { @@ -2626,14 +2743,9 @@ struct longint_static_storage final { } // namespace longint_detail -inline void longint::set_string(std::string_view s) { -#if LONG_INT_USE_BIT_CAST - const auto* str_iter = std::bit_cast(s.begin()); - const auto* str_end = std::bit_cast(s.end()); -#else - const auto* str_iter = reinterpret_cast(s.begin()); - const auto* str_end = reinterpret_cast(s.end()); -#endif +inline void longint::set_str_impl(const unsigned char* str, const std::size_t str_size) { + const unsigned char* str_iter = str; + const unsigned char* str_end = str + str_size; std::int32_t sgn = 1; constexpr auto is_digit = [](const std::uint32_t chr) constexpr noexcept { @@ -2650,6 +2762,7 @@ inline void longint::set_string(std::string_view s) { const auto digits_count = static_cast(str_end - str_iter); if (digits_count <= 19) { + static_assert(sizeof(double_digit_t) == sizeof(uint64_t)); double_digit_t num = 0; for (; str_iter != str_end; ++str_iter) { num = num * 10 + double_digit_t{*str_iter} - '0'; @@ -2664,11 +2777,10 @@ inline void longint::set_string(std::string_view s) { const std::size_t str_conv_digits_size = (digits_count + kStrConvBaseDigits - 1) / kStrConvBaseDigits; - const std::size_t aligned_str_conv_digits_size = - math_functions::nearest_greater_equal_power_of_two( - static_cast(str_conv_digits_size)); - const auto checked_aligned_str_conv_digits_size = check_size(aligned_str_conv_digits_size); - reserveUninitializedWithoutCopy(checked_aligned_str_conv_digits_size); + const size_type aligned_str_conv_digits_size = check_size( + math_functions::nearest_greater_equal_power_of_two(uint64_t{str_conv_digits_size})); + ATTRIBUTE_ASSUME(str_conv_digits_size <= aligned_str_conv_digits_size); + reserveUninitializedWithoutCopy(aligned_str_conv_digits_size); digit_t* str_conv_digits = nums_; { @@ -2708,35 +2820,36 @@ inline void longint::set_string(std::string_view s) { } while (str_iter != str_end); } - std::size_t m = 2 * aligned_str_conv_digits_size; + static_assert(max_size() * 2 > max_size()); + std::size_t m = aligned_str_conv_digits_size * 2; if (m > kFFTPrecisionBorder) { m *= 2; } longint_detail::longint_static_storage::ensureDecBasePowsCapacity( - math_functions::log2_floor(checked_aligned_str_conv_digits_size)); + math_functions::log2_floor(aligned_str_conv_digits_size)); // Allocate m complex numbers for p1 and m complex numbers for p2 std::size_t max_fft_poly_length = 2 * m; - auto* const mult_add_buffer = - static_cast(operator new(aligned_str_conv_digits_size * sizeof(digit_t) + - max_fft_poly_length * sizeof(fft::complex))); + auto* const mult_add_buffer = static_cast(operator new( + std::size_t{aligned_str_conv_digits_size} * sizeof(digit_t) + + max_fft_poly_length * sizeof(fft::complex))); auto* const fft_poly_buffer = reinterpret_cast(mult_add_buffer + aligned_str_conv_digits_size); const longint* conv_dec_base_pows_iter = longint_detail::longint_static_storage::conv_dec_base_pows.data(); - for (std::size_t half_len = 1; half_len != aligned_str_conv_digits_size; - half_len *= 2, ++conv_dec_base_pows_iter) { - for (std::size_t pos = 0; pos != aligned_str_conv_digits_size; pos += 2 * half_len) { - convertDecBaseMultAdd(str_conv_digits + pos, half_len, conv_dec_base_pows_iter, + static_assert(max_size() * 2 > max_size()); + for (size_type conv_len = 2; conv_len <= aligned_str_conv_digits_size; + conv_len *= 2, ++conv_dec_base_pows_iter) { + ATTRIBUTE_ASSUME(math_functions::is_power_of_two(conv_len)); + for (size_type pos = 0; pos < aligned_str_conv_digits_size; pos += conv_len) { + convertDecBaseMultAdd(str_conv_digits + pos, conv_len, *conv_dec_base_pows_iter, mult_add_buffer, fft_poly_buffer); } } operator delete(mult_add_buffer); - std::size_t usize_value = aligned_str_conv_digits_size; + size_type usize_value = aligned_str_conv_digits_size; while (usize_value > 0 && nums_[usize_value - 1] == 0) { usize_value--; } - size_ = sgn * static_cast(usize_value); + set_ssize_from_size_and_sign(usize_value, sgn); } - -#undef LONG_INT_USE_BIT_CAST diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index acf071f..98acadd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -131,6 +131,7 @@ function(configure_gcc_or_clang_gcc_options) -Wswitch-default -Wswitch-enum -Wdeprecated + -Wtype-limits -Werror -pedantic-errors ) From 1e9a753c03b915f93a86b08a74543c675357d4f0 Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 12:26:55 +0300 Subject: [PATCH 04/17] update math_functions.hpp --- number_theory/math_functions.hpp | 271 +++++++++++--------------- number_theory/test_math_functions.cpp | 18 +- 2 files changed, 124 insertions(+), 165 deletions(-) diff --git a/number_theory/math_functions.hpp b/number_theory/math_functions.hpp index 9ccb851..66e233e 100644 --- a/number_theory/math_functions.hpp +++ b/number_theory/math_functions.hpp @@ -111,18 +111,9 @@ template std::ptrdiff_t p) noexcept(noexcept(n *= n) && noexcept(1 / n)) { const bool not_inverse = p >= 0; - std::size_t p_u = p >= 0 ? static_cast(p) : -static_cast(p); - T res(1); - while (true) { - if (p_u & 1) { - res *= n; - } - p_u >>= 1; - if (p_u == 0) { - return not_inverse ? res : 1 / res; - } - n *= n; - } + const std::size_t p_u = p >= 0 ? static_cast(p) : -static_cast(p); + const T res = ::math_functions::bin_pow(std::move(n), p_u); + return not_inverse ? res : 1 / res; } /// @brief Calculate (n ^ p) % mod @@ -132,8 +123,8 @@ template /// @return (n ^ p) % mod [[nodiscard]] ATTRIBUTE_CONST constexpr uint32_t bin_pow_mod(uint32_t n, uint64_t p, uint32_t mod) noexcept { - uint64_t res = mod != 1; - uint64_t widen_n = n; + std::uint64_t res = mod != 1; + std::uint64_t widen_n = n; while (true) { if (p % 2 != 0) { ATTRIBUTE_ASSUME(widen_n < (1ull << 32)); @@ -141,7 +132,7 @@ template } p /= 2; if (p == 0) { - return static_cast(res); + return static_cast(res); } ATTRIBUTE_ASSUME(widen_n < (1ull << 32)); widen_n = (widen_n * widen_n) % mod; @@ -158,16 +149,16 @@ template /// @return (n ^ p) % mod [[nodiscard]] ATTRIBUTE_CONST I128_CONSTEXPR uint64_t bin_pow_mod(uint64_t n, uint64_t p, uint64_t mod) noexcept { - uint64_t res = mod != 1; + std::uint64_t res = mod != 1; while (true) { if (p & 1) { - res = static_cast((uint128_t{res} * n) % mod); + res = static_cast((uint128_t{res} * n) % mod); } p >>= 1; if (p == 0) { return res; } - n = static_cast((uint128_t{n} * n) % mod); + n = static_cast((uint128_t{n} * n) % mod); } } @@ -188,8 +179,8 @@ ATTRIBUTE_CONST I128_CONSTEXPR uint64_t bin_pow_mod(uint64_t n, uint64_t p, uint /** * See Hackers Delight Chapter 11. */ - for (uint32_t m = 0x40000000; m != 0; m >>= 2) { - uint32_t b = y | m; + for (std::uint32_t m = 0x40000000; m != 0; m >>= 2) { + std::uint32_t b = y | m; y >>= 1; if (n >= b) { n -= b; @@ -217,8 +208,8 @@ ATTRIBUTE_CONST I128_CONSTEXPR uint64_t bin_pow_mod(uint64_t n, uint64_t p, uint /** * See Hackers Delight Chapter 11. */ - uint64_t l = 1; - uint64_t r = std::min((n >> 5) + 8, uint64_t(0xFFFFFFFFull)); + std::uint64_t l = 1; + std::uint64_t r = std::min((n >> 5) + 8, std::uint64_t{0xFFFFFFFFull}); do { ATTRIBUTE_ASSUME(l <= r); ATTRIBUTE_ASSUME((r >> 32) == 0); @@ -245,13 +236,13 @@ ATTRIBUTE_CONST I128_CONSTEXPR uint64_t bin_pow_mod(uint64_t n, uint64_t p, uint /** * See Hackers Delight Chapter 11. */ - uint64_t l = 0; + std::uint64_t l = 0; uint128_t r_approx = (n >> 6) + 16; - uint64_t r = + std::uint64_t r = r_approx > 0xFFFFFFFFFFFFFFFFull ? uint64_t(0xFFFFFFFFFFFFFFFFull) : uint64_t(r_approx); do { // m = (l + r + 1) / 2 - uint64_t m = (l / 2) + (r / 2) + ((r % 2) | (l % 2)); + std::uint64_t m = (l / 2) + (r / 2) + ((r % 2) | (l % 2)); if (n >= uint128_t(m) * m) { l = m; } else { @@ -290,10 +281,10 @@ ATTRIBUTE_CONST I128_CONSTEXPR uint64_t bin_pow_mod(uint64_t n, uint64_t p, uint [[maybe_unused]] const auto n_original_value = n; #endif - uint32_t y = 0; - for (int32_t s = 30; s >= 0; s -= 3) { + std::uint32_t y = 0; + for (std::int32_t s = 30; s >= 0; s -= 3) { y *= 2; - uint32_t b = (3 * y * (y + 1) | 1) << s; + std::uint32_t b = (3 * y * (y + 1) | 1) << s; if (n >= b) { n -= b; y++; @@ -315,7 +306,7 @@ ATTRIBUTE_CONST I128_CONSTEXPR uint64_t bin_pow_mod(uint64_t n, uint64_t p, uint /// @param[in] n /// @return ⌊n^(1/3)⌋ [[nodiscard]] ATTRIBUTE_CONST constexpr uint32_t icbrt(uint64_t n) noexcept { - uint64_t y = 0; + std::uint64_t y = 0; if (n >= 0x1000000000000000ull) { if (n >= 0x8000000000000000ull) { n -= 0x8000000000000000ull; @@ -391,7 +382,7 @@ ATTRIBUTE_CONST constexpr IsPerfectSquareResult is_perfect_square(uint case 1: case 4: case 9: { - const uint32_t root = ::math_functions::isqrt(n); + const std::uint32_t root = ::math_functions::isqrt(n); const bool is_perf_square = root * root == n; return {is_perf_square, is_perf_square ? root : 0}; } @@ -422,8 +413,8 @@ ATTRIBUTE_CONST constexpr IsPerfectSquareResult is_perfect_square(uint case 1: case 4: case 9: { - const uint32_t root = ::math_functions::isqrt(n); - const bool is_perf_square = uint64_t{root} * root == n; + const std::uint32_t root = ::math_functions::isqrt(n); + const bool is_perf_square = std::uint64_t{root} * root == n; return {is_perf_square, is_perf_square ? root : 0}; } default: @@ -456,7 +447,7 @@ ATTRIBUTE_CONST I128_CONSTEXPR case 1: case 4: case 9: { - uint64_t root = ::math_functions::isqrt(n); + const std::uint64_t root = ::math_functions::isqrt(n); const bool is_perf_square = uint128_t{root} * root == n; return {is_perf_square, is_perf_square ? root : 0}; } @@ -501,7 +492,7 @@ ATTRIBUTE_CONST I128_CONSTEXPR /** * See Knuth's algorithm in Hackers Delight 7.4 */ - uint64_t t = 0; + std::uint64_t t = 0; n = (n << 31) | (n >> 33); // I.e., shlr(x, 31). t = (n ^ (n >> 20)) & 0x00000FFF800007FFULL; n = (t | (t << 20)) ^ n; @@ -522,7 +513,7 @@ ATTRIBUTE_CONST I128_CONSTEXPR /// @return 128-bit number whose bits are reversed bits of the @a `n`. [[nodiscard]] ATTRIBUTE_CONST I128_CONSTEXPR uint128_t bit_reverse(uint128_t n) noexcept { uint128_t m = ~uint128_t{0}; - for (uint32_t s = sizeof(uint128_t) * CHAR_BIT; s >>= 1;) { + for (std::uint32_t s = sizeof(uint128_t) * CHAR_BIT; s >>= 1;) { m ^= m << s; n = ((n >> s) & m) | ((n << s) & ~m); } @@ -536,10 +527,10 @@ template requires requires(Functor f, uint64_t mask) { f(uint64_t{mask}); } #endif ATTRIBUTE_ALWAYS_INLINE constexpr void visit_all_submasks(uint64_t mask, Functor visiter) noexcept( - std::is_nothrow_invocable_v) { - uint64_t s = mask; + std::is_nothrow_invocable_v) { + std::uint64_t s = mask; do { - visiter(uint64_t{s}); + visiter(std::uint64_t{s}); s = (s - 1) & mask; } while (s != 0); } @@ -548,7 +539,7 @@ int32_t sign(bool x) = delete; int32_t sign(char x) = delete; [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(signed char x) noexcept { - return int32_t(x > 0) - int32_t(x < 0); + return std::int32_t(x > 0) - std::int32_t(x < 0); } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(unsigned char x) noexcept { @@ -556,7 +547,7 @@ int32_t sign(char x) = delete; } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(short x) noexcept { - return int32_t(x > 0) - int32_t(x < 0); + return std::int32_t(x > 0) - std::int32_t(x < 0); } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(unsigned short x) noexcept { @@ -564,7 +555,7 @@ int32_t sign(char x) = delete; } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(int x) noexcept { - return int32_t(x > 0) - int32_t(x < 0); + return std::int32_t(x > 0) - std::int32_t(x < 0); } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(unsigned x) noexcept { @@ -572,7 +563,7 @@ int32_t sign(char x) = delete; } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(long x) noexcept { - return int32_t(x > 0) - int32_t(x < 0); + return std::int32_t(x > 0) - std::int32_t(x < 0); } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(unsigned long x) noexcept { @@ -580,7 +571,7 @@ int32_t sign(char x) = delete; } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(long long x) noexcept { - return int32_t(x > 0) - int32_t(x < 0); + return std::int32_t(x > 0) - std::int32_t(x < 0); } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t sign(unsigned long long x) noexcept { @@ -590,7 +581,7 @@ int32_t sign(char x) = delete; #if defined(INTEGERS_128_BIT_HPP) [[nodiscard]] ATTRIBUTE_CONST I128_CONSTEXPR int32_t sign(int128_t x) noexcept { - const auto sign_bit = static_cast(uint128_t(x) >> 127); + const auto sign_bit = static_cast(static_cast(x) >> 127); return int32_t(x != 0) - int32_t(2 * sign_bit); } @@ -735,8 +726,8 @@ unsigned char uabs(char n) = delete; #if defined(INTEGERS_128_BIT_HPP) [[nodiscard]] ATTRIBUTE_CONST I128_CONSTEXPR uint128_t uabs(int128_t n) noexcept { - uint128_t t = uint128_t(n >> 127); - return (uint128_t(n) ^ t) - t; + const uint128_t t = static_cast(n >> 127); + return (static_cast(n) ^ t) - t; } [[nodiscard]] ATTRIBUTE_CONST I128_CONSTEXPR uint128_t uabs(uint128_t n) noexcept { @@ -783,11 +774,11 @@ concept unsigned_integral = ::math_functions::detail::helper_ns::unsigned_integr 0x000000000000FF00ull, 0x00000000000000F0ull, 0x000000000000000Cull, 0x0000000000000002ull}; - uint32_t y = 0; - uint32_t j = 32; + std::uint32_t y = 0; + std::uint32_t j = 32; - for (size_t i = 0; i != 6; ++i) { - uint32_t k = (((n & t[i]) == 0) ? 0 : j); + for (std::size_t i = 0; i != 6; ++i) { + std::uint32_t k = (((n & t[i]) == 0) ? 0 : j); y += k; n >>= k; j >>= 1; @@ -832,7 +823,7 @@ concept unsigned_integral = ::math_functions::detail::helper_ns::unsigned_integr if (unlikely(n == 0)) { return 32; } - uint32_t m = 1; + std::uint32_t m = 1; if ((n >> 16) == 0) { m += 16; n <<= 16; @@ -861,7 +852,7 @@ concept unsigned_integral = ::math_functions::detail::helper_ns::unsigned_integr if (unlikely(n == 0)) { return 64; } - uint32_t m = 1; + std::uint32_t m = 1; if ((n >> 32) == 0) { m += 32; n <<= 32; @@ -882,7 +873,7 @@ concept unsigned_integral = ::math_functions::detail::helper_ns::unsigned_integr m += 2; n <<= 2; } - m -= uint32_t(n >> 63); + m -= static_cast(n >> 63); ATTRIBUTE_ASSUME(m <= 63); return m; } @@ -894,7 +885,7 @@ concept unsigned_integral = ::math_functions::detail::helper_ns::unsigned_integr if (unlikely(n == 0)) { return 32; } - uint32_t m = 1; + std::uint32_t m = 1; if ((n & 0x0000FFFFu) == 0) { m += 16; n >>= 16; @@ -917,7 +908,7 @@ concept unsigned_integral = ::math_functions::detail::helper_ns::unsigned_integr } [[nodiscard]] ATTRIBUTE_CONST constexpr uint32_t tz_count_64_software(uint64_t n) noexcept { - uint32_t m = 0; + std::uint32_t m = 0; for (n = ~n & (n - 1); n != 0; n >>= 1) { m++; } @@ -967,18 +958,18 @@ concept unsigned_integral = ::math_functions::detail::helper_ns::unsigned_integr x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F); x = x + (x >> 8); x = x + (x >> 16); - return static_cast(x & 0x0000007F) - 32; + return static_cast(x & 0x0000007F) - 32; } [[nodiscard]] ATTRIBUTE_CONST constexpr int32_t pop_cmp(uint32_t x, uint32_t y) noexcept { /** * See Hackers Delight Chapter 5. */ - uint32_t n = x & ~y; // Clear bits where - uint32_t m = y & ~x; // both bits are 1 + std::uint32_t n = x & ~y; // Clear bits where + std::uint32_t m = y & ~x; // both bits are 1 while (true) { if (n == 0) - return static_cast(m | -m); + return static_cast(m | -m); if (m == 0) return 1; n &= n - 1; // Clear one bit @@ -1002,7 +993,7 @@ template #if defined(INTEGERS_128_BIT_HPP) if constexpr (std::is_same_v) { - uint64_t low = static_cast(n); + const std::uint64_t low = static_cast(n); if (low != 0) { #if CONFIG_HAS_AT_LEAST_CXX_20 return std::countr_zero(low); @@ -1013,7 +1004,7 @@ template #endif } - uint64_t high = static_cast(n >> 64); + const std::uint64_t high = static_cast(n >> 64); ATTRIBUTE_ASSUME(high != 0); #if CONFIG_HAS_AT_LEAST_CXX_20 int32_t high_trailing_zeros_count = std::countr_zero(high); @@ -1063,7 +1054,7 @@ template #if CONFIG_HAS_CONCEPTS requires ::math_functions::detail::unsigned_integral #endif -[[nodiscard]] ATTRIBUTE_CONST constexpr int32_t countl_zero(T n) noexcept { +[[nodiscard]] ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_CONST constexpr int32_t countl_zero(T n) noexcept { static_assert(::math_functions::detail::is_unsigned_v, "Unsigned integral type expected"); if (unlikely(n == 0)) { @@ -1072,7 +1063,7 @@ template #if defined(INTEGERS_128_BIT_HPP) if constexpr (std::is_same_v) { - uint64_t high = static_cast(n >> 64); + const std::uint64_t high = static_cast(n >> 64); if (high != 0) { // Avoid recursive call to countl_zero #if CONFIG_HAS_AT_LEAST_CXX_20 @@ -1084,7 +1075,7 @@ template #endif } - uint64_t low = static_cast(n); + const std::uint64_t low = static_cast(n); ATTRIBUTE_ASSUME(low != 0); // Avoid recursive call to countl_zero #if CONFIG_HAS_AT_LEAST_CXX_20 @@ -1136,8 +1127,8 @@ template if constexpr (std::is_same_v) { // Reason: cppcheck can not deduce that n is uint128_t here // cppcheck-suppress [shiftTooManyBits] - uint64_t high = static_cast(n >> 64); - uint64_t low = static_cast(n); + const std::uint64_t high = static_cast(n >> 64); + const std::uint64_t low = static_cast(n); #if CONFIG_HAS_AT_LEAST_CXX_20 return std::popcount(high) + std::popcount(low); #elif defined(__GNUG__) @@ -1195,7 +1186,7 @@ template // https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 // t gets x's least significant 0 bits set to 1 - uint32_t t = x | (x - 1); + const std::uint32_t t = x | (x - 1); // Next set to 1 the most significant bit to change, // set to 0 the least significant ones, and add the necessary 1 bits. return (t + 1) | static_cast(std::uint64_t{(~t & -~t) - 1} >> @@ -1259,51 +1250,37 @@ bool is_power_of_two(char) = delete; #endif +template [[nodiscard]] -ATTRIBUTE_CONST constexpr uint64_t nearest_greater_equal_power_of_two(uint32_t n) noexcept { - const auto shift = 32 - static_cast(::math_functions::countl_zero(n | 1)) - - ((n & (n - 1)) == 0); - ATTRIBUTE_ASSUME(shift <= 32); - return uint64_t{1} << shift; -} - -[[nodiscard]] -ATTRIBUTE_CONST constexpr uint64_t nearest_greater_power_of_two(uint32_t n) noexcept { - const auto shift = 32 - static_cast(::math_functions::countl_zero(n)); - ATTRIBUTE_ASSUME(shift <= 32); - return uint64_t{1} << shift; -} - -[[nodiscard]] -ATTRIBUTE_CONST constexpr uint64_t nearest_greater_equal_power_of_two(uint64_t n) noexcept { - const auto shift = 64 - static_cast(::math_functions::countl_zero(n | 1)) - - ((n & (n - 1)) == 0); - return uint64_t{1} << shift; -} +ATTRIBUTE_CONST constexpr auto nearest_greater_equal_power_of_two(const UIntType n) noexcept { + static_assert( + ::math_functions::detail::is_unsigned_v && sizeof(UIntType) >= sizeof(unsigned), + "unsigned integral type (at least unsigned int) is expected"); -[[nodiscard]] -ATTRIBUTE_CONST constexpr uint64_t nearest_greater_power_of_two(uint64_t n) noexcept { - const auto shift = 64 - static_cast(::math_functions::countl_zero(n)); - return uint64_t{1} << shift; + using ShiftType = std::int32_t; + const ShiftType shift = ShiftType{sizeof(n) * CHAR_BIT} - + ShiftType{::math_functions::countl_zero(n | 1)} - + ShiftType{(n & (n - 1)) == 0}; + using RetType = typename std::conditional_t<(sizeof(UIntType) > sizeof(std::uint32_t)), + UIntType, std::uint64_t>; + return RetType{1} << shift; } -#if defined(INTEGERS_128_BIT_HPP) - +template [[nodiscard]] -ATTRIBUTE_CONST I128_CONSTEXPR uint128_t nearest_greater_equal_power_of_two(uint128_t n) noexcept { - const auto shift = 128 - static_cast(::math_functions::countl_zero(n | 1)) - - ((n & (n - 1)) == 0); - return uint128_t{1} << shift; -} +ATTRIBUTE_CONST constexpr auto nearest_greater_power_of_two(const UIntType n) noexcept { + static_assert( + ::math_functions::detail::is_unsigned_v && sizeof(UIntType) >= sizeof(unsigned), + "unsigned integral type (at least unsigned int) is expected"); -[[nodiscard]] -ATTRIBUTE_CONST I128_CONSTEXPR uint128_t nearest_greater_power_of_two(uint128_t n) noexcept { - const auto shift = 128 - static_cast(::math_functions::countl_zero(n)); - return uint128_t{1} << shift; + using ShiftType = std::int32_t; + const ShiftType shift = + ShiftType{sizeof(n) * CHAR_BIT} - ShiftType{::math_functions::countl_zero(n)}; + using RetType = typename std::conditional_t<(sizeof(UIntType) > sizeof(std::uint32_t)), + UIntType, std::uint64_t>; + return RetType{1} << shift; } -#endif - /// @brief If @a n != 0, return number that is power of 2 and /// whose only bit is the lowest bit set in the @a n /// Otherwise, return 0 @@ -1378,75 +1355,57 @@ template } } -/// @brief For n > 0 returns ⌊log_2(n)⌋. For n = 0 returns (uint32_t)-1 -/// @param[in] n -/// @return -[[nodiscard]] ATTRIBUTE_CONST constexpr uint32_t log2_floor(uint32_t n) noexcept { - return 31 - static_cast(::math_functions::countl_zero(n)); -} - /// @brief For n > 0 returns ⌈log_2(n)⌉. For n = 0 returns (uint32_t)-1 +/// @tparam UIntType unsigned integral type (at least unsigned int in size) /// @param[in] n /// @return -[[nodiscard]] ATTRIBUTE_CONST constexpr uint32_t log2_ceil(uint32_t n) noexcept { - return ::math_functions::log2_floor(n) + ((n & (n - 1)) != 0); -} - -/// @brief For n > 0 returns ⌊log_2(n)⌋. For n = 0 returns (uint32_t)-1 -/// @param[in] n -/// @return -[[nodiscard]] ATTRIBUTE_CONST constexpr uint32_t log2_floor(uint64_t n) noexcept { - return 63 - static_cast(::math_functions::countl_zero(n)); -} - -/// @brief For n > 0 returns ⌈log_2(n)⌉. For n = 0 returns (uint32_t)-1 -/// @param[in] n -/// @return -[[nodiscard]] ATTRIBUTE_CONST constexpr uint32_t log2_ceil(uint64_t n) noexcept { - return ::math_functions::log2_floor(n) + ((n & (n - 1)) != 0); -} +template +#if CONFIG_HAS_CONCEPTS + requires ::math_functions::detail::unsigned_integral +#endif +[[nodiscard]] +ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_CONST constexpr uint32_t log2_floor(const UIntType n) noexcept { + static_assert( + ::math_functions::detail::is_unsigned_v && sizeof(UIntType) >= sizeof(unsigned), + "unsigned integral type (at least unsigned int) is expected"); #if defined(INTEGERS_128_BIT_HPP) -/// @brief For n > 0 returns ⌊log_2(n)⌋. For n = 0 returns (uint32_t)-1 -/// @param[in] n -/// @return -[[nodiscard]] ATTRIBUTE_CONST I128_CONSTEXPR uint32_t log2_floor(uint128_t n) noexcept { - const auto hi = static_cast(n >> 64); - return hi != 0 ? (127 - static_cast(::math_functions::countl_zero(hi))) - : (::math_functions::log2_floor(static_cast(n))); + if constexpr (std::is_same_v) { + const auto hi = static_cast(n >> 64); + return hi != 0 ? (127 - static_cast(::math_functions::countl_zero(hi))) + : (::math_functions::log2_floor(static_cast(n))); + } else +#endif + { + return std::uint32_t{sizeof(n) * CHAR_BIT - 1} - + static_cast(::math_functions::countl_zero(n)); + } } /// @brief For n > 0 returns ⌈log_2(n)⌉. For n = 0 returns (uint32_t)-1 +/// @tparam UIntType unsigned integral type (at least unsigned int in size) /// @param[in] n /// @return -[[nodiscard]] ATTRIBUTE_CONST I128_CONSTEXPR uint32_t log2_ceil(uint128_t n) noexcept { +template +#if CONFIG_HAS_CONCEPTS + requires ::math_functions::detail::unsigned_integral +#endif +[[nodiscard]] +ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_CONST constexpr uint32_t log2_ceil(const UIntType n) noexcept { return ::math_functions::log2_floor(n) + ((n & (n - 1)) != 0); } +template +#if CONFIG_HAS_CONCEPTS + requires ::math_functions::detail::unsigned_integral #endif - -[[nodiscard]] ATTRIBUTE_CONST constexpr uint32_t base_2_len(uint32_t n) noexcept { - // " | 1" operation does not affect answer for all - // numbers except n = 0. For n = 0 answer is 1. - return ::math_functions::log2_floor(n | 1) + 1; -} - -[[nodiscard]] ATTRIBUTE_CONST constexpr uint32_t base_2_len(uint64_t n) noexcept { - // " | 1" operation does not affect answer for all - // numbers except n = 0. For n = 0 answer is 1. - return ::math_functions::log2_floor(n | 1) + 1; -} - -#if defined(INTEGERS_128_BIT_HPP) - -[[nodiscard]] ATTRIBUTE_CONST I128_CONSTEXPR uint32_t base_2_len(uint128_t n) noexcept { +[[nodiscard]] +ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_CONST constexpr uint32_t base_2_len(const UIntType n) noexcept { // " | 1" operation does not affect answer for all // numbers except n = 0. For n = 0 answer is 1. return ::math_functions::log2_floor(n | 1) + 1; } -#endif - namespace detail { ATTRIBUTE_CONST constexpr uint32_t log10_floor_compile_time_impl(uint32_t n) noexcept { @@ -1698,8 +1657,8 @@ struct SumSinCos { /// + cos(alpha + (n - 1) beta) /// ) template -#if CONFIG_HAS_AT_LEAST_CXX_20 - requires std::is_floating_point_v +#if CONFIG_HAS_CONCEPTS + requires std::floating_point #endif [[nodiscard]] ATTRIBUTE_CONST SumSinCos sum_of_sines_and_cosines(FloatType alpha, FloatType beta, diff --git a/number_theory/test_math_functions.cpp b/number_theory/test_math_functions.cpp index 2a71931..5a6dcde 100644 --- a/number_theory/test_math_functions.cpp +++ b/number_theory/test_math_functions.cpp @@ -239,9 +239,9 @@ void test_sin_cos_sum_generic() noexcept { log_tests_started(); constexpr uint32_t kMaxN = 1e2; - constexpr int64_t k = 5; + constexpr int32_t k = 5; constexpr uint32_t angle_scale = 10; - constexpr double angle_start = bin_pow(double(angle_scale), -k); + constexpr double angle_start = bin_pow(double(angle_scale), -ptrdiff_t{k}); constexpr FloatType kSumEps = []() constexpr noexcept -> FloatType { if constexpr (std::is_same_v) { @@ -747,8 +747,8 @@ void test_inv_mod_m() { void test_powers_sum() noexcept { log_tests_started(); - constexpr uint64_t kMaxM = 6; - for (uint64_t m = 0; m <= kMaxM; m++) { + constexpr size_t kMaxM = 6; + for (size_t m = 0; m <= kMaxM; m++) { static_assert(kMaxM <= 6); const auto max_n = [m]() noexcept -> std::uint32_t { switch (m) { @@ -771,15 +771,15 @@ void test_powers_sum() noexcept { assert(false); std::terminate(); }(); - constexpr std::uint32_t kOffset = 50; + constexpr uint32_t kOffset = 50; assert(max_n >= kOffset); - const auto start_n = max_n - kOffset; - uint64_t s = 0; - for (uint64_t i = 1; i < start_n; i++) { + const uint32_t start_n = max_n - kOffset; + uint64_t s = 0; + for (size_t i = 1; i < start_n; i++) { s += math_functions::bin_pow(i, m); } for (uint32_t n = start_n; n <= max_n; n++) { - s += math_functions::bin_pow(uint64_t(n), m); + s += math_functions::bin_pow(uint64_t{n}, m); switch (m) { case 0: assert(math_functions::powers_sum_u64<0>(n) == s); From d44bb6b40063941b2fe7bc566c8dbcb86adfbb8c Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 12:27:11 +0300 Subject: [PATCH 05/17] update longint.hpp --- number_theory/longint.hpp | 71 +++++++++++--------- number_theory/test_long_int.cpp | 111 +++++++++++++++++--------------- 2 files changed, 99 insertions(+), 83 deletions(-) diff --git a/number_theory/longint.hpp b/number_theory/longint.hpp index 816ea0c..c2056aa 100644 --- a/number_theory/longint.hpp +++ b/number_theory/longint.hpp @@ -295,9 +295,6 @@ struct longint { static constexpr auto kFFTFloatRoundError = std::numeric_limits::round_error(); - using dec_digit_t = uint32_t; - using double_dec_digit_t = uint32_t; - static constexpr uint32_t kDecimalBase = kStrConvBase; static constexpr uint32_t kFFTDecimalBase = 1'000; @@ -1164,7 +1161,7 @@ struct longint { #if defined(INTEGERS_128_BIT_HPP) [[nodiscard]] ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr bool fits_in_uint128() const noexcept { - return static_cast(size()) <= 4; + return static_cast(size()) <= 4; } [[nodiscard]] ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr uint128_t to_uint128() const noexcept { @@ -1225,7 +1222,7 @@ struct longint { ans += std::to_string(nums_[0]); return; case 2: - ans += std::to_string((uint64_t{nums_[1]} << kNumsBits) | nums_[0]); + ans += std::to_string((double_digit_t{nums_[1]} << kNumsBits) | nums_[0]); return; default: break; @@ -1233,22 +1230,21 @@ struct longint { static_assert(math_functions::nearest_greater_equal_power_of_two(max_size()) <= std::numeric_limits::max()); - const std::size_t n = static_cast( - math_functions::nearest_greater_equal_power_of_two(usize_value)); + const size_t n = size_t{math_functions::nearest_greater_equal_power_of_two(usize_value)}; ensureBinBasePowsCapacity(math_functions::log2_floor(n)); digit_t* const knums = allocate(n); std::fill_n(std::copy_n(nums_, usize_value, knums), n - usize_value, digit_t{0}); const Decimal result = convertBinBase(knums, n); deallocate(knums); assert(result.size_ >= 3); - const std::size_t full_blocks = result.size_ - 1; - dec_digit_t last_a_i = result.digits_[full_blocks]; + const typename Decimal::dec_size_type full_blocks = result.size_ - 1; + typename Decimal::dec_digit_t last_a_i = result.digits_[full_blocks]; const std::size_t string_size = full_blocks * kStrConvBaseDigits + math_functions::base_10_len(last_a_i); ans.resize(ans.size() + string_size); auto* ptr = reinterpret_cast(std::addressof(ans[ans.size() - 1])); for (std::size_t i = 0; i < full_blocks; i++) { - dec_digit_t a_i = result.digits_[i]; + typename Decimal::dec_digit_t a_i = result.digits_[i]; for (std::size_t j = kStrConvBaseDigits; j > 0; j--) { *ptr = static_cast('0' + a_i % 10); a_i /= 10; @@ -1295,7 +1291,9 @@ struct longint { } struct Decimal { - using dec_size_type = std::size_t; + using dec_digit_t = uint32_t; + using double_dec_digit_t = uint64_t; + using dec_size_type = std::size_t; dec_digit_t* digits_ = nullptr; dec_size_type size_ = 0; @@ -1433,7 +1431,7 @@ struct longint { } void SquareThisTo(Decimal& other) const { - const std::size_t digits_size = size_; + const dec_size_type digits_size = size_; if (unlikely(digits_size == 0)) { other.set_zero(); return; @@ -1450,6 +1448,7 @@ struct longint { [[nodiscard]] ATTRIBUTE_PURE constexpr bool operator==(uint32_t n) const noexcept { + static_assert(sizeof(dec_digit_t) == sizeof(uint32_t)); switch (size_) { case 0: return n == 0; @@ -1469,6 +1468,7 @@ struct longint { [[nodiscard]] ATTRIBUTE_PURE constexpr bool operator==(uint64_t n) const noexcept { + static_assert(sizeof(dec_digit_t) == sizeof(uint32_t)); switch (size_) { case 0: return n == 0; @@ -1511,7 +1511,7 @@ struct longint { size_ = 0; } constexpr void pop_leading_zeros() noexcept { - std::size_t usize_value = size_; + dec_size_type usize_value = size_; while (usize_value > 0 && digits_[usize_value - 1] == 0) { usize_value--; } @@ -1543,7 +1543,7 @@ struct longint { const dec_size_type k_size, Decimal& product_result) { ATTRIBUTE_ASSUME(m_size <= k_size); const dec_size_type new_size = m_size + k_size; - dec_digit_t* ans = allocate(new_size); + dec_digit_t* const ans = allocate(new_size); std::fill_n(ans, new_size, dec_digit_t{0}); DecNaive::multiply_and_store_to_impl(m_digits, m_size, k_digits, k_size, ans); deallocate(product_result.digits_); @@ -1561,7 +1561,7 @@ struct longint { const dec_digit_t k_digits[], const dec_size_type k_size, dec_digit_t ans[]) noexcept { - double_dec_digit_t* ans_store_ptr = ans; + dec_digit_t* ans_store_ptr = ans; for (dec_size_type j = 0; j < m_size; ans_store_ptr++, j++) { const double_dec_digit_t b_j = m_digits[j]; double_dec_digit_t carry = 0; @@ -1858,7 +1858,7 @@ struct longint { for (size_type j = 0; j < m; ans_store_ptr++, j++) { const double_digit_t b_j = m_ptr[j]; double_digit_t carry = 0; - for (std::size_t i = 0; i < k; i++) { + for (size_type i = 0; i < k; i++) { const double_digit_t a_i = k_ptr[i]; const double_digit_t res = a_i * b_j + double_digit_t{ans_store_ptr[i]} + carry; ans_store_ptr[i] = static_cast(res); @@ -1871,12 +1871,17 @@ struct longint { }; struct LongIntFFT final { + using poly_size_type = std::size_t; + ATTRIBUTE_NODISCARD_WITH_MESSAGE("impl error in fft longint product") ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_CONST - static constexpr std::pair compute_fft_product_params( + static constexpr std::pair compute_fft_product_params( size_type product_size) noexcept { - std::size_t n = 2 * math_functions::nearest_greater_equal_power_of_two(product_size); + static_assert(max_size() * 2 > max_size()); + static_assert(2 * math_functions::nearest_greater_equal_power_of_two(max_size()) >= + 2 * max_size()); + poly_size_type n = 2 * math_functions::nearest_greater_equal_power_of_two(product_size); const bool need_high_precision = n > kFFTPrecisionBorder; n <<= need_high_precision; ATTRIBUTE_ASSUME(math_functions::is_power_of_two(n)); @@ -1929,7 +1934,7 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(write_only, 5, 6) static void convert_longint_nums_to_fft_poly(const digit_t m_ptr[], const size_type m, const digit_t k_ptr[], const size_type k, - fft::complex* p, const std::size_t n, + fft::complex* p, const poly_size_type n, bool need_high_precision) noexcept { ATTRIBUTE_ASSUME(0 < m); ATTRIBUTE_ASSUME(m <= k); @@ -2013,7 +2018,7 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(write_only, 3, 4) static void convert_longint_nums_to_fft_poly(const digit_t nums_ptr[], const size_type nums_size, fft::complex* p, - const std::size_t n, + const poly_size_type n, bool need_high_precision) noexcept { ATTRIBUTE_ASSUME(0 < nums_size); ATTRIBUTE_ASSUME(nums_size <= max_size()); @@ -2036,7 +2041,7 @@ struct longint { p++; } } else { - for (std::size_t i = 0; i < nums_size; i++) { + for (size_type i = 0; i < nums_size; i++) { digit_t value = nums_ptr[i]; *p = fft::complex{static_cast(static_cast(value)), @@ -2114,6 +2119,7 @@ struct longint { // same amount. We may have to append a high-order // digit on the dividend; we do that unconditionally (un size = m + -> 1 <-). + static_assert(max_size() + max_size() > max_size()); digit_t* const vn_and_un = allocate(std::size_t{n + m} + 1); digit_t* const vn = vn_and_un; digit_t* const un = vn_and_un + n; @@ -2371,13 +2377,13 @@ struct longint { // Now mult_add_buffer == num_hi * CONV_BASE^half_len double_digit_t carry = 0; - for (std::size_t i = half_conv_len; i > 0; i--, conv_digits++, mult_add_buffer++) { + for (size_type i = half_conv_len; i > 0; i--, conv_digits++, mult_add_buffer++) { const double_digit_t res = double_digit_t{*conv_digits} + double_digit_t{*mult_add_buffer} + carry; *conv_digits = static_cast(res); carry = res >> kNumsBits; } - for (std::size_t i = half_conv_len; i > 0; i--, conv_digits++, mult_add_buffer++) { + for (size_type i = half_conv_len; i > 0; i--, conv_digits++, mult_add_buffer++) { const double_digit_t res = double_digit_t{*mult_add_buffer} + carry; *conv_digits = static_cast(res); carry = res >> kNumsBits; @@ -2398,10 +2404,10 @@ struct longint { break; } - Decimal low_dec = convertBinBase(nums, size / 2); - Decimal high_dec = convertBinBase(nums + size / 2, size / 2); + Decimal low_dec = convertBinBaseImpl(nums, size / 2); + Decimal high_dec = convertBinBaseImpl(nums + size / 2, size / 2); - const std::size_t idx = math_functions::log2_floor(std::uint64_t{size}) - 1; + const std::size_t idx = math_functions::log2_floor(size) - 1; assert(idx < conv_bin_base_pows.size()); high_dec *= conv_bin_base_pows[idx]; high_dec += low_dec; @@ -2521,9 +2527,9 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(read_write, 1, 2) ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) ATTRIBUTE_NONNULL_ALL_ARGS - static constexpr void longint_add_with_free_space(digit_t lhs[], size_type lhs_size, + static constexpr void longint_add_with_free_space(digit_t lhs[], const size_type lhs_size, const digit_t rhs[], - size_type rhs_size) noexcept { + const size_type rhs_size) noexcept { ATTRIBUTE_ASSUME(lhs_size > rhs_size); double_digit_t carry = 0; const digit_t* const lhs_end = lhs + lhs_size; @@ -2545,9 +2551,9 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(read_write, 1, 2) ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) ATTRIBUTE_NONNULL_ALL_ARGS - static constexpr bool longint_subtract_with_free_space(digit_t lhs[], size_type lhs_size, + static constexpr bool longint_subtract_with_free_space(digit_t lhs[], const size_type lhs_size, const digit_t rhs[], - size_type rhs_size) noexcept { + const size_type rhs_size) noexcept { ATTRIBUTE_ASSUME(lhs_size >= rhs_size); bool overflowed = longint_subtract_with_carry(lhs, lhs_size, rhs, rhs_size); if (overflowed) { @@ -2562,9 +2568,9 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(read_write, 1, 2) ATTRIBUTE_SIZED_ACCESS(read_only, 3, 4) ATTRIBUTE_NONNULL_ALL_ARGS - static constexpr bool longint_subtract_with_carry(digit_t lhs[], size_type lhs_size, + static constexpr bool longint_subtract_with_carry(digit_t lhs[], const size_type lhs_size, const digit_t rhs[], - size_type rhs_size) noexcept { + const size_type rhs_size) noexcept { ATTRIBUTE_ASSUME(lhs_size >= rhs_size); const digit_t* const lhs_end = lhs + lhs_size; const digit_t* const rhs_end = rhs + rhs_size; @@ -2590,6 +2596,7 @@ struct longint { } void nonZeroSizeAddUInt(uint32_t n) { + static_assert(sizeof(digit_t) >= sizeof(uint32_t)); digit_t* it = begin(); const digit_t* end_iter = end(); double_digit_t carry = n; diff --git a/number_theory/test_long_int.cpp b/number_theory/test_long_int.cpp index bc9658c..a00ea01 100644 --- a/number_theory/test_long_int.cpp +++ b/number_theory/test_long_int.cpp @@ -147,8 +147,10 @@ void TestOperatorEqualsInt() { n = static_cast(-1); assert(n.sign() == 1); assert(n.size() == 4); - assert(n.begin()[0] == uint32_t(-1) && n.begin()[1] == uint32_t(-1) && - n.begin()[2] == uint32_t(-1) && n.begin()[3] == uint32_t(-1)); + assert(n.begin()[0] == std::numeric_limits::max() && + n.begin()[1] == std::numeric_limits::max() && + n.begin()[2] == std::numeric_limits::max() && + n.begin()[3] == std::numeric_limits::max()); AssertInvariants(n); for (uint64_t i = uint64_t(-1) - K; i != 0; i++) { @@ -535,7 +537,7 @@ void TestLongIntSquare() { AssertInvariants(n); } - for (uint32_t i = uint32_t(-1) - K; i != 0; i++) { + for (uint32_t i = std::numeric_limits::max() - K; i != 0; i++) { n = i; n.SquareInplace(); assert(n == uint64_t(i) * i); @@ -640,8 +642,8 @@ void TestUIntMult() { } } - for (uint32_t i = uint32_t(-1) - K; i != 0; i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + for (uint32_t i = std::numeric_limits::max() - K; i != 0; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; n *= uint32_t(j); assert(n == uint64_t(i) * uint64_t(j)); @@ -650,7 +652,7 @@ void TestUIntMult() { } for (uint64_t i = uint64_t(-1) - K; i != 0; i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; n *= j; assert(n == uint128_t(i) * j); @@ -685,8 +687,8 @@ void TestUIntAddAndSub() { } } - for (uint32_t i = uint32_t(-1) - K; i != 0; i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + for (uint32_t i = std::numeric_limits::max() - K; i != 0; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; assert(n == uint64_t(i)); n += j; @@ -704,18 +706,19 @@ void TestUIntAddAndSub() { } } - for (uint64_t i = uint64_t(-1) - K; i != 0; i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + for (uint64_t i = std::numeric_limits::max() - K; i != 0; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; assert(n == i); n += j; - assert(n == uint128_t(i) + uint128_t(j)); + assert(n == uint128_t{i} + uint128_t{j}); AssertInvariants(n); } } - for (uint128_t i = uint64_t(-1); i != uint128_t(uint64_t(-1)) + 2 * K; i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + const uint64_t kStartPos1 = std::numeric_limits::max(); + for (uint128_t i = kStartPos1; i != uint128_t{kStartPos1} + 2 * K; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; assert(n == i); n += j; @@ -724,9 +727,9 @@ void TestUIntAddAndSub() { } } - const uint128_t h = uint128_t(-1) / 2; - for (uint128_t i = h - 2 * K; i != h; i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + const uint128_t kStartPos2 = uint128_t(-1) / 2; + for (uint128_t i = kStartPos2 - 2 * K; i != kStartPos2; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; n += j; assert(n == i + j); @@ -734,56 +737,59 @@ void TestUIntAddAndSub() { } } - for (int32_t i = int32_t(1u << 31); i != int32_t(K) + int32_t(1u << 31); i++) { + const int32_t kStartPos3 = std::numeric_limits::min(); + for (int32_t i = kStartPos3; i != static_cast(K) + kStartPos3; i++) { for (uint32_t j = 0; j <= K; j++) { n = i; n += j; - assert(n == i + int32_t(j)); + assert(n == i + static_cast(j)); AssertInvariants(n); } } - for (int32_t i = int32_t(1u << 31); i != int32_t(K) + int32_t(1u << 31); i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + for (int32_t i = kStartPos3; i != int32_t(K) + kStartPos3; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; n += j; - assert(n == i + int64_t(j)); + assert(n == i + int64_t{j}); AssertInvariants(n); } } - for (int64_t i = -int64_t(1ll << 62); i != int64_t(K) - int64_t(1ll << 62); i++) { + const int64_t kStartPos4 = -int64_t{1ll << 62}; + for (int64_t i = kStartPos4; i != int64_t{K} + kStartPos4; i++) { for (uint32_t j = 0; j <= K; j++) { n = i; n += j; - assert(n == i + int32_t(j)); + assert(n == i + static_cast(j)); AssertInvariants(n); } } - for (int64_t i = -int64_t(1ll << 62); i != int64_t(K) - int64_t(1ll << 62); i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + for (int64_t i = kStartPos4; i != int64_t{K} + kStartPos4; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; n += j; - assert(n == i + int64_t(j)); + assert(n == i + int64_t{j}); AssertInvariants(n); } } - for (int64_t i = int64_t(1ull << 63); i != K + int64_t(1ull << 63); i++) { + const int64_t kStartPos5 = std::numeric_limits::min(); + for (int64_t i = kStartPos5; i != int64_t{K} + kStartPos5; i++) { for (uint32_t j = 0; j <= K; j++) { n = i; n += j; - assert(n == i + int32_t(j)); + assert(n == i + static_cast(j)); AssertInvariants(n); } } - for (int64_t i = int64_t(1ull << 63); i != K + int64_t(1ull << 63); i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + for (int64_t i = kStartPos5; i != int64_t{K} + kStartPos5; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; n += j; - assert(n == i + int64_t(j)); + assert(n == i + int64_t{j}); AssertInvariants(n); } } @@ -823,8 +829,8 @@ void TestInt32Div() { n /= uint32_t{1} << 31; assert(n == i >> 31); } - for (uint32_t i = uint32_t(-1) - K; i != 0; i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + for (uint32_t i = std::numeric_limits::max() - K; i != 0; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; n /= j; assert(n == i / j); @@ -872,8 +878,8 @@ void TestLongIntAddAndSub() { } } - for (uint32_t i = uint32_t(-1) - K; i != 0; i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + for (uint32_t i = std::numeric_limits::max() - K; i != 0; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; m = j; n += m; @@ -886,8 +892,10 @@ void TestLongIntAddAndSub() { } } - for (uint64_t i = uint64_t(-1) - uint32_t(-1) - K; i != uint64_t(-1) - uint32_t(-1); i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + const auto kStartPos1 = + std::numeric_limits::max() - std::numeric_limits::max(); + for (uint64_t i = kStartPos1 - K; i != kStartPos1; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; m = j; n += m; @@ -900,8 +908,9 @@ void TestLongIntAddAndSub() { } } - for (uint128_t i = uint64_t(-1); i != uint128_t(uint64_t(-1)) + 2 * K; i++) { - for (uint32_t j = uint32_t(-1) - K; j != 0; j++) { + const auto kStartPos2 = std::numeric_limits::max(); + for (uint128_t i = kStartPos2; i != uint128_t{kStartPos2} + 2 * K; i++) { + for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; m = j; n += m; @@ -914,9 +923,9 @@ void TestLongIntAddAndSub() { } } - const uint128_t h = uint128_t(-1) / 2; - for (uint128_t i = h - 2 * K; i < h; i++) { - for (uint128_t j = h - 2 * K; j < h; j++) { + const uint128_t kStartPos3 = uint128_t(-1) / 2; + for (uint128_t i = kStartPos3 - 2 * K; i < kStartPos3; i++) { + for (uint128_t j = kStartPos3 - 2 * K; j < kStartPos3; j++) { n = i; m = j; n += m; @@ -1028,7 +1037,7 @@ void TestSetString() { nums_count = 256 / (sizeof(uint32_t) * 8); assert(n.size() == int32_t(nums_count)); for (std::size_t i = 0; i < nums_count; i++) { - assert(n[i] == uint32_t(-1)); + assert(n[i] == std::numeric_limits::max()); } AssertInvariants(n); @@ -1040,7 +1049,7 @@ void TestSetString() { nums_count = 512 / (sizeof(uint32_t) * 8); assert(n.size() == int32_t(nums_count)); for (std::size_t i = 0; i < nums_count; i++) { - assert(n[i] == uint32_t(-1)); + assert(n[i] == std::numeric_limits::max()); } AssertInvariants(n); } @@ -1353,8 +1362,8 @@ void TestDecimal() { } } - for (uint32_t i = uint32_t(-1) - kC; i != 0; i++) { - for (uint32_t j = uint32_t(-1) - kC; j != 0; j++) { + for (uint32_t i = std::numeric_limits::max() - kC; i != 0; i++) { + for (uint32_t j = std::numeric_limits::max() - kC; j != 0; j++) { d1 = i; d2 = j; d1 += d2; @@ -1395,12 +1404,12 @@ void TestDecimal() { } } - for (uint32_t i = uint32_t(-1) - kC; i != 0; i++) { - for (uint32_t j = uint32_t(-1) - kC; j != 0; j++) { + for (uint32_t i = std::numeric_limits::max() - kC; i != 0; i++) { + for (uint32_t j = std::numeric_limits::max() - kC; j != 0; j++) { d1 = i; d2 = j; d1 *= d2; - assert(d1 == uint64_t(i) * j); + assert(d1 == uint64_t{i} * j); } } @@ -1454,7 +1463,7 @@ void TestDecimal() { assert(d1 == i * i); } - for (uint32_t i = uint32_t(-1) - kC; i != 0; i++) { + for (uint32_t i = std::numeric_limits::max() - kC; i != 0; i++) { d1 = i; d1.SquareThisTo(d1); assert(d1 == uint64_t(i) * i); @@ -1583,7 +1592,7 @@ void TestToIntTypes() { for (uint32_t i = 0; i <= kC; i++) { test(i); } - for (uint32_t i = uint32_t(-1) - kC; i != 0; i++) { + for (uint32_t i = std::numeric_limits::max() - kC; i != 0; i++) { test(i); } for (uint64_t i = 0; i <= kC; i++) { From 88799e3579c639b763c571084cc6ec7ec0eef5b4 Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 12:58:56 +0300 Subject: [PATCH 06/17] update longint --- number_theory/longint.hpp | 57 +++++++++++++++++++-------------- number_theory/test_long_int.cpp | 12 ++----- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/number_theory/longint.hpp b/number_theory/longint.hpp index c2056aa..c297fa3 100644 --- a/number_theory/longint.hpp +++ b/number_theory/longint.hpp @@ -667,7 +667,9 @@ struct longint { const bool find_sum = (size_ ^ other.size_) >= 0; const size_type usize2 = other.usize(); static_assert(max_size() + 1 > max_size()); - const size_type usize1 = set_size_at_least(std::max(usize(), usize2) + (find_sum ? 1 : 0)); + const size_type min_size_boundary = std::max(usize(), usize2) + (find_sum ? 1 : 0); + ATTRIBUTE_ASSUME(usize() < min_size_boundary); + const size_type usize1 = set_size_at_least(min_size_boundary); ATTRIBUTE_ASSUME(usize1 >= usize2); if (find_sum) { ATTRIBUTE_ASSUME(usize1 > usize2); @@ -957,11 +959,12 @@ struct longint { // x != 0 => sign won't change and there will be no leading zeros if (carry != 0) { - if (unlikely(usize_value == capacity_)) { + assert(usize_value <= capacity_); + if (unlikely(usize_value >= capacity_)) { growCapacity(); + assert(usize_value < capacity_); } - assert(usize_value < capacity_); nums_[usize_value] = static_cast(carry); size_ += sign(); } @@ -1243,9 +1246,9 @@ struct longint { full_blocks * kStrConvBaseDigits + math_functions::base_10_len(last_a_i); ans.resize(ans.size() + string_size); auto* ptr = reinterpret_cast(std::addressof(ans[ans.size() - 1])); - for (std::size_t i = 0; i < full_blocks; i++) { + for (typename Decimal::dec_size_type i = 0; i < full_blocks; i++) { typename Decimal::dec_digit_t a_i = result.digits_[i]; - for (std::size_t j = kStrConvBaseDigits; j > 0; j--) { + for (auto j = kStrConvBaseDigits; j > 0; j--) { *ptr = static_cast('0' + a_i % 10); a_i /= 10; ptr--; @@ -1362,9 +1365,9 @@ struct longint { Decimal& operator+=(const Decimal& other) { double_dec_digit_t carry = 0; - const std::size_t m = std::min(this->size_, other.size_); + const dec_size_type m = std::min(this->size_, other.size_); dec_digit_t* p = this->digits_; - for (std::size_t i = 0; i < m; i++) { + for (dec_size_type i = 0; i < m; i++) { const double_dec_digit_t res = double_dec_digit_t{p[i]} + double_dec_digit_t{other.digits_[i]} + carry; p[i] = static_cast(res % kDecimalBase); @@ -1380,9 +1383,9 @@ struct longint { size_ = other.size_; } - p = this->digits_; - const std::size_t this_size = size_; - for (std::size_t i = m; carry != 0 && i < this_size; i++) { + p = this->digits_; + const dec_size_type this_size = size_; + for (dec_size_type i = m; carry != 0 && i < this_size; i++) { const auto res = double_dec_digit_t{p[i]} + carry; p[i] = static_cast(res % kDecimalBase); carry = res / kDecimalBase; @@ -1391,12 +1394,13 @@ struct longint { if (carry == 0) { pop_leading_zeros(); } else { - dec_digit_t* new_digits = allocate((this_size + 1 + (this_size == 0))); - pointer new_digits_copy_end = std::copy_n(digits_, this_size, new_digits); - *new_digits_copy_end = static_cast(carry); + dec_digit_t* const new_digits = allocate((this_size + 1 + (this_size == 0))); + dec_digit_t* const new_digits_copy_end = + std::copy_n(digits_, this_size, new_digits); + *new_digits_copy_end = static_cast(carry); deallocate(this->digits_); this->digits_ = new_digits; - size_ = this_size + 1; + this->size_ = this_size + 1; } return *this; @@ -1970,7 +1974,7 @@ struct longint { p++; } } else { - for (std::size_t i = 0; i < m; i++) { + for (size_type i = 0; i < m; i++) { digit_t m_value = m_ptr[i]; digit_t k_value = k_ptr[i]; @@ -1993,7 +1997,7 @@ struct longint { static_cast(static_cast(k_value))}; p++; } - for (std::size_t i = m; i < k; i++) { + for (size_type i = m; i < k; i++) { digit_t k_value = k_ptr[i]; *p = fft::complex{0.0, static_cast(static_cast(k_value))}; @@ -2074,12 +2078,11 @@ struct longint { double_digit_t carry = 0; for (digit_t *const nums_iter_rend = nums_ - 1, *nums_riter = nums_iter_rend + usize(); nums_riter != nums_iter_rend; --nums_riter) { - const double_digit_t cur = - (carry << kNumsBits) | static_cast(*nums_riter); - const double_digit_t q = cur / n; - const double_digit_t r = cur - q * n; - *nums_riter = static_cast(q); - carry = r; + const double_digit_t cur = (carry << kNumsBits) | double_digit_t{*nums_riter}; + const double_digit_t q = cur / n; + const double_digit_t r = cur - q * n; + *nums_riter = static_cast(q); + carry = r; } pop_leading_zeros(); @@ -2407,7 +2410,7 @@ struct longint { Decimal low_dec = convertBinBaseImpl(nums, size / 2); Decimal high_dec = convertBinBaseImpl(nums + size / 2, size / 2); - const std::size_t idx = math_functions::log2_floor(size) - 1; + const uint32_t idx = math_functions::log2_floor(size) - 1; assert(idx < conv_bin_base_pows.size()); high_dec *= conv_bin_base_pows[idx]; high_dec += low_dec; @@ -2435,9 +2438,15 @@ struct longint { } ATTRIBUTE_NOINLINE ATTRIBUTE_COLD void growCapacity() { - reserve(static_cast(capacity_) * 2 | (capacity_ == 0)); + const size_type current_capacity = capacity(); + static_assert(max_size() * 2 > max_size()); + const size_type new_capacity = (current_capacity * 2) | (current_capacity == 0); + ATTRIBUTE_ASSUME(capacity_ < new_capacity); + reserve(new_capacity); } + ATTRIBUTE_NODISCARD_WITH_MESSAGE("impl error") + ATTRIBUTE_ALWAYS_INLINE size_type set_size_at_least(size_type new_size) { size_type cur_size = usize(); if (new_size <= cur_size) { diff --git a/number_theory/test_long_int.cpp b/number_theory/test_long_int.cpp index a00ea01..ffc7389 100644 --- a/number_theory/test_long_int.cpp +++ b/number_theory/test_long_int.cpp @@ -671,18 +671,16 @@ void TestUIntAddAndSub() { for (uint32_t i = 0; i <= K; i++) { for (uint32_t j = 0; j <= K; j++) { n = i; - assert(n == i); n += j; assert(n == i + j); AssertInvariants(n); n = i; - assert(n == i); n -= j; if (unlikely(i == j)) { assert(!n); assert(n.iszero()); } - assert(n == int64_t(i) - int64_t(j)); + assert(n == int64_t{i} - int64_t{j}); AssertInvariants(n); } } @@ -690,18 +688,16 @@ void TestUIntAddAndSub() { for (uint32_t i = std::numeric_limits::max() - K; i != 0; i++) { for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; - assert(n == uint64_t(i)); n += j; - assert(n == uint64_t(i) + uint64_t(j)); + assert(n == uint64_t{i} + uint64_t{j}); AssertInvariants(n); n = i; - assert(n == i); n -= j; if (unlikely(i == j)) { assert(!n); assert(n.iszero()); } - assert(n == int64_t(i) - int64_t(j)); + assert(n == int64_t{i} - int64_t{j}); AssertInvariants(n); } } @@ -709,7 +705,6 @@ void TestUIntAddAndSub() { for (uint64_t i = std::numeric_limits::max() - K; i != 0; i++) { for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; - assert(n == i); n += j; assert(n == uint128_t{i} + uint128_t{j}); AssertInvariants(n); @@ -720,7 +715,6 @@ void TestUIntAddAndSub() { for (uint128_t i = kStartPos1; i != uint128_t{kStartPos1} + 2 * K; i++) { for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; - assert(n == i); n += j; assert(n == i + j); AssertInvariants(n); From 23c9bffe304e4f206dd1446048e7e7657b9b6575 Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 13:07:32 +0300 Subject: [PATCH 07/17] update actrie --- tf_idf_actrie/actrie.hpp | 29 +++++++++++++++------------- tf_idf_actrie/test_actrie.cpp | 6 ++++++ tf_idf_actrie/test_tf_idf_actrie.cpp | 8 ++++---- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/tf_idf_actrie/actrie.hpp b/tf_idf_actrie/actrie.hpp index fd3ab88..fb6a979 100644 --- a/tf_idf_actrie/actrie.hpp +++ b/tf_idf_actrie/actrie.hpp @@ -127,7 +127,7 @@ class [[nodiscard]] ACTrie { find_callback(text.substr(l, pattern_size), l); } - for (auto terminal_node_index = node.compressed_suffix_link; + for (StoredNodeIndex terminal_node_index = node.compressed_suffix_link; terminal_node_index != kRootNodeIndex; terminal_node_index = nodes_[terminal_node_index].compressed_suffix_link) { if (!std::is_constant_evaluated()) { @@ -225,7 +225,7 @@ class [[nodiscard]] ACTrie { } // Jump up through compressed suffix links - for (uint32_t terminal_node_index = node.compressed_suffix_link; + for (StoredNodeIndex terminal_node_index = node.compressed_suffix_link; terminal_node_index != kRootNodeIndex; terminal_node_index = nodes_[terminal_node_index].compressed_suffix_link) { if (!std::is_constant_evaluated()) { @@ -288,14 +288,14 @@ class [[nodiscard]] ACTrie { return nodes[current_node_index].IsTerminal(); } - [[nodiscard]] static constexpr bool IsInAlphabet(Symbol symbol) noexcept { - return static_cast(symbol) - kAlphabetStart <= kAlphabetEnd - kAlphabetStart; + [[nodiscard]] static constexpr bool IsInAlphabet(const Symbol symbol) noexcept { + return SymbolToUInt(symbol) - kAlphabetStart <= kAlphabetEnd - kAlphabetStart; } - [[nodiscard]] static constexpr bool IsInAlphabet(char symbol) noexcept { + [[nodiscard]] static constexpr bool IsInAlphabet(const char symbol) noexcept { return IsInAlphabet(CharToSymbol(symbol)); } - [[nodiscard]] static constexpr size_type SymbolToIndex(Symbol symbol) noexcept { - std::int32_t symbol_as_int = symbol; + [[nodiscard]] static constexpr size_type SymbolToIndex(const Symbol symbol) noexcept { + std::uint32_t symbol_as_int = SymbolToUInt(symbol); if constexpr (kIsCaseInsensetive) { // We don't use std::tolower because we know that all // chars are < 128. On the other side, std::tolower makes @@ -304,22 +304,25 @@ class [[nodiscard]] ACTrie { symbol_as_int = ToLowerImpl(symbol_as_int); } - return static_cast(static_cast(symbol_as_int)) - kAlphabetStart; + return size_type{symbol_as_int} - kAlphabetStart; } - [[nodiscard]] static constexpr size_type CharToIndex(char chr) noexcept { + [[nodiscard]] static constexpr std::uint32_t SymbolToUInt(const Symbol symbol) noexcept { + return std::uint32_t{symbol}; + } + [[nodiscard]] static constexpr size_type CharToIndex(const char chr) noexcept { return SymbolToIndex(CharToSymbol(chr)); } - [[nodiscard]] static constexpr Symbol CharToSymbol(char chr) noexcept { + [[nodiscard]] static constexpr Symbol CharToSymbol(const char chr) noexcept { static_assert(std::is_same_v); return static_cast(chr); } private: - [[nodiscard]] static constexpr std::int32_t ToLowerImpl(std::int32_t c) noexcept { + [[nodiscard]] static constexpr std::uint32_t ToLowerImpl(const std::uint32_t c) noexcept { return c | (IsUpperImpl(c) * ('a' - 'A')); } - [[nodiscard]] static constexpr bool IsUpperImpl(std::int32_t c) noexcept { - return static_cast(c) - 'A' <= 'Z' - 'A'; + [[nodiscard]] static constexpr bool IsUpperImpl(const std::uint32_t c) noexcept { + return c - 'A' <= 'Z' - 'A'; } protected: diff --git a/tf_idf_actrie/test_actrie.cpp b/tf_idf_actrie/test_actrie.cpp index a4b5049..0330ffc 100644 --- a/tf_idf_actrie/test_actrie.cpp +++ b/tf_idf_actrie/test_actrie.cpp @@ -5,6 +5,12 @@ #include #include +#ifdef NDEBUG +#error("Can't test properly with NDEBUG macro defined (macro won't be undefined manually)") +#endif + +#include + #include "actrie.hpp" namespace { diff --git a/tf_idf_actrie/test_tf_idf_actrie.cpp b/tf_idf_actrie/test_tf_idf_actrie.cpp index 896505e..5e3b1db 100644 --- a/tf_idf_actrie/test_tf_idf_actrie.cpp +++ b/tf_idf_actrie/test_tf_idf_actrie.cpp @@ -1,3 +1,7 @@ +#ifdef NDEBUG +#error("Can't test properly with NDEBUG macro defined (macro won't be undefined manually)") +#endif + #include #include #include @@ -9,10 +13,6 @@ #include "search_lib.hpp" -#ifdef NDEBUG -#error("Can't test properly with NDEBUG macro defined (macro won't be undefined manually)") -#endif - namespace { using namespace std::literals::string_view_literals; From 00f91f663a542370ae25fdc3da5402e27bf6fefb Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 22:31:54 +0300 Subject: [PATCH 08/17] update longint.hpp and add flag for longint asserts to the CMakeLists.txt --- number_theory/longint.hpp | 79 ++++++++++++++++++++++++++++++++++----- tests/CMakeLists.txt | 3 +- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/number_theory/longint.hpp b/number_theory/longint.hpp index c297fa3..df17998 100644 --- a/number_theory/longint.hpp +++ b/number_theory/longint.hpp @@ -29,6 +29,12 @@ #error "Current implementation works only with GCC" #endif +#if defined(ENABLE_LONGINT_DEBUG_ASSERTS) && ENABLE_LONGINT_DEBUG_ASSERTS +#define LONGINT_DEBUG_ASSERT(expr) assert(expr) +#else +#define LONGINT_DEBUG_ASSERT(expr) +#endif + namespace longint_allocator { // #define DEBUG_LI_ALLOC_PRINTING 1 @@ -424,7 +430,7 @@ struct longint { ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_PURE constexpr ssize_type size() const noexcept { #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wtype-limits" +#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" #endif const ssize_type value = size_; if (value > static_cast(max_size())) { @@ -667,11 +673,11 @@ struct longint { const bool find_sum = (size_ ^ other.size_) >= 0; const size_type usize2 = other.usize(); static_assert(max_size() + 1 > max_size()); - const size_type min_size_boundary = std::max(usize(), usize2) + (find_sum ? 1 : 0); - ATTRIBUTE_ASSUME(usize() < min_size_boundary); - const size_type usize1 = set_size_at_least(min_size_boundary); + const size_type usize1 = set_size_at_least(std::max(usize(), usize2) + (find_sum ? 1 : 0)); + LONGINT_DEBUG_ASSERT(usize1 >= usize2); ATTRIBUTE_ASSUME(usize1 >= usize2); if (find_sum) { + LONGINT_DEBUG_ASSERT(usize1 > usize2); ATTRIBUTE_ASSUME(usize1 > usize2); longint_add_with_free_space(nums_, usize1, other.nums_, usize2); } else { @@ -1231,8 +1237,15 @@ struct longint { break; } +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" +#endif static_assert(math_functions::nearest_greater_equal_power_of_two(max_size()) <= std::numeric_limits::max()); +#if defined(__clang__) +#pragma clang diagnostic pop +#endif const size_t n = size_t{math_functions::nearest_greater_equal_power_of_two(usize_value)}; ensureBinBasePowsCapacity(math_functions::log2_floor(n)); digit_t* const knums = allocate(n); @@ -1421,6 +1434,7 @@ struct longint { set_zero(); return *this; } + LONGINT_DEBUG_ASSERT(1 <= m && m <= k); ATTRIBUTE_ASSUME(1 <= m && m <= k); const dec_size_type new_size = m + k; if (m <= 16 || m * k <= 1024) { @@ -1545,6 +1559,7 @@ struct longint { const dec_size_type m_size, const dec_digit_t k_digits[], const dec_size_type k_size, Decimal& product_result) { + LONGINT_DEBUG_ASSERT(m_size <= k_size); ATTRIBUTE_ASSUME(m_size <= k_size); const dec_size_type new_size = m_size + k_size; dec_digit_t* const ans = allocate(new_size); @@ -1660,6 +1675,7 @@ struct longint { ATTRIBUTE_CONST ATTRIBUTE_ALWAYS_INLINE static constexpr dec_size_type polys_size(const dec_size_type size_value) noexcept { + LONGINT_DEBUG_ASSERT(size_value <= kMaxDecFFTSize); ATTRIBUTE_ASSUME(size_value <= kMaxDecFFTSize); static_assert(3 * kMaxDecFFTSize > kMaxDecFFTSize); static_assert( @@ -1817,6 +1833,7 @@ struct longint { ATTRIBUTE_ALWAYS_INLINE constexpr dec_size_type poly_size() const noexcept { const auto value = poly_size_; + LONGINT_DEBUG_ASSERT(math_functions::is_power_of_two(value)); ATTRIBUTE_ASSUME(math_functions::is_power_of_two(value)); return value; } @@ -1857,6 +1874,7 @@ struct longint { static constexpr void multiply_and_store_to(const digit_t m_ptr[], const size_type m, const digit_t k_ptr[], const size_type k, digit_t* const ans) noexcept { + LONGINT_DEBUG_ASSERT(m <= k); ATTRIBUTE_ASSUME(m <= k); digit_t* ans_store_ptr = ans; for (size_type j = 0; j < m; ans_store_ptr++, j++) { @@ -1888,6 +1906,7 @@ struct longint { poly_size_type n = 2 * math_functions::nearest_greater_equal_power_of_two(product_size); const bool need_high_precision = n > kFFTPrecisionBorder; n <<= need_high_precision; + LONGINT_DEBUG_ASSERT(math_functions::is_power_of_two(n)); ATTRIBUTE_ASSUME(math_functions::is_power_of_two(n)); return {n, need_high_precision}; } @@ -1940,14 +1959,23 @@ struct longint { const digit_t k_ptr[], const size_type k, fft::complex* p, const poly_size_type n, bool need_high_precision) noexcept { + LONGINT_DEBUG_ASSERT(0 < m); ATTRIBUTE_ASSUME(0 < m); + LONGINT_DEBUG_ASSERT(m <= k); ATTRIBUTE_ASSUME(m <= k); + LONGINT_DEBUG_ASSERT(m <= max_size()); ATTRIBUTE_ASSUME(m <= max_size()); + LONGINT_DEBUG_ASSERT(k <= max_size()); ATTRIBUTE_ASSUME(k <= max_size()); + LONGINT_DEBUG_ASSERT(m + k <= max_size()); ATTRIBUTE_ASSUME(m + k <= max_size()); + LONGINT_DEBUG_ASSERT(m + k <= n); ATTRIBUTE_ASSUME(m + k <= n); + LONGINT_DEBUG_ASSERT(need_high_precision || n <= kFFTPrecisionBorder); ATTRIBUTE_ASSUME(need_high_precision || n <= kFFTPrecisionBorder); + LONGINT_DEBUG_ASSERT(!need_high_precision || n > kFFTPrecisionBorder * 2); ATTRIBUTE_ASSUME(!need_high_precision || n > kFFTPrecisionBorder * 2); + LONGINT_DEBUG_ASSERT(math_functions::is_power_of_two(n)); ATTRIBUTE_ASSUME(math_functions::is_power_of_two(n)); static_assert(kNumsBits == 32); @@ -2024,11 +2052,17 @@ struct longint { const size_type nums_size, fft::complex* p, const poly_size_type n, bool need_high_precision) noexcept { + LONGINT_DEBUG_ASSERT(0 < nums_size); ATTRIBUTE_ASSUME(0 < nums_size); + LONGINT_DEBUG_ASSERT(nums_size <= max_size()); ATTRIBUTE_ASSUME(nums_size <= max_size()); + LONGINT_DEBUG_ASSERT(nums_size * 2 <= n); ATTRIBUTE_ASSUME(nums_size * 2 <= n); + LONGINT_DEBUG_ASSERT(need_high_precision || n <= kFFTPrecisionBorder); ATTRIBUTE_ASSUME(need_high_precision || n <= kFFTPrecisionBorder); + LONGINT_DEBUG_ASSERT(!need_high_precision || n > kFFTPrecisionBorder * 2); ATTRIBUTE_ASSUME(!need_high_precision || n > kFFTPrecisionBorder * 2); + LONGINT_DEBUG_ASSERT(math_functions::is_power_of_two(n)); ATTRIBUTE_ASSUME(math_functions::is_power_of_two(n)); static_assert(kNumsBits == 32); @@ -2130,13 +2164,13 @@ struct longint { const digit_t* const u = nums_; const digit_t* const v = other.nums_; const digit_t last_v_num = v[n - 1]; - assert(last_v_num > 0); + LONGINT_DEBUG_ASSERT(last_v_num > 0); ATTRIBUTE_ASSUME(last_v_num > 0); static_assert(kNumsBits == 32); // 0 <= s < kNumsBits const auto s = static_cast(math_functions::countl_zero(last_v_num)); longint::divmod_normalize_vn(vn, v, n, s); - assert(vn[n - 1] >= digit_t{1} << (kNumsBits - 1)); + LONGINT_DEBUG_ASSERT(vn[n - 1] >= digit_t{1} << (kNumsBits - 1)); ATTRIBUTE_ASSUME(vn[n - 1] >= digit_t{1} << (kNumsBits - 1)); longint::divmod_normalize_un(un, u, m, m + 1, s); longint::divmod_impl_unchecked( @@ -2163,26 +2197,32 @@ struct longint { const digit_t* RESTRICT_QUALIFIER const vn, const size_type vn_size, digit_t* RESTRICT_QUALIFIER const quot) noexcept { + LONGINT_DEBUG_ASSERT(vn_size >= 2); ATTRIBUTE_ASSUME(vn_size >= 2); + LONGINT_DEBUG_ASSERT(un_size > vn_size); ATTRIBUTE_ASSUME(un_size > vn_size); for (size_type j = un_size - vn_size - 1; static_cast(j) >= 0; j--) { // Compute estimate qhat of q[j]. const double_digit_t cur = (double_digit_t{un[j + vn_size]} << kNumsBits) | un[j + vn_size - 1]; const digit_t last_vn = vn[vn_size - 1]; + LONGINT_DEBUG_ASSERT(last_vn >= digit_t{1} << (kNumsBits - 1)); ATTRIBUTE_ASSUME(last_vn >= digit_t{1} << (kNumsBits - 1)); double_digit_t qhat = cur / last_vn; double_digit_t rhat = cur % last_vn; + LONGINT_DEBUG_ASSERT(qhat * last_vn + rhat == cur); ATTRIBUTE_ASSUME(qhat * last_vn + rhat == cur); while (qhat >= kNumsBase || qhat * vn[vn_size - 2] > kNumsBase * rhat + un[j + vn_size - 2]) { qhat--; rhat += last_vn; + LONGINT_DEBUG_ASSERT(qhat * last_vn + rhat == cur); ATTRIBUTE_ASSUME(qhat * last_vn + rhat == cur); if (rhat >= kNumsBase) { break; } } + LONGINT_DEBUG_ASSERT(qhat * last_vn + rhat == cur); ATTRIBUTE_ASSUME(qhat * last_vn + rhat == cur); // Multiply and subtract double_digit_t t = divmod_mult_sub(un + j, vn, vn_size, qhat); @@ -2213,6 +2253,7 @@ struct longint { const digit_t* RESTRICT_QUALIFIER const vn, const size_type vn_size, const double_digit_t qhat) noexcept { + LONGINT_DEBUG_ASSERT(vn_size >= 2); ATTRIBUTE_ASSUME(vn_size >= 2); double_digit_t carry = 0; for (size_type i = 0; i < vn_size; i++) { @@ -2233,6 +2274,7 @@ struct longint { static constexpr void divmod_add_back(digit_t* RESTRICT_QUALIFIER const un, const digit_t* RESTRICT_QUALIFIER const vn, const size_type vn_size) noexcept { + LONGINT_DEBUG_ASSERT(vn_size >= 2); ATTRIBUTE_ASSUME(vn_size >= 2); double_digit_t carry = 0; for (size_type i = 0; i < vn_size; i++) { @@ -2249,7 +2291,9 @@ struct longint { ATTRIBUTE_ALWAYS_INLINE static constexpr void divmod_normalize_vn(digit_t vn[], const digit_t v[], size_type n, std::uint32_t s) noexcept { + LONGINT_DEBUG_ASSERT(n > 1); ATTRIBUTE_ASSUME(n > 1); + LONGINT_DEBUG_ASSERT(s < 32); ATTRIBUTE_ASSUME(s < 32); for (size_type i = n - 1; i > 0; i--) { vn[i] = (v[i] << s) | static_cast(double_digit_t{v[i - 1]} >> (kNumsBits - s)); @@ -2264,8 +2308,11 @@ struct longint { static constexpr void divmod_normalize_un(digit_t un[], const digit_t u[], size_type m, ATTRIBUTE_MAYBE_UNUSED size_type m_plus_one, std::uint32_t s) noexcept { + LONGINT_DEBUG_ASSERT(m > 1); ATTRIBUTE_ASSUME(m > 1); + LONGINT_DEBUG_ASSERT(s < 32); ATTRIBUTE_ASSUME(s < 32); + LONGINT_DEBUG_ASSERT(m + 1 == m_plus_one); ATTRIBUTE_ASSUME(m + 1 == m_plus_one); un[m] = static_cast(double_digit_t{u[m - 1]} >> (kNumsBits - s)); for (size_type i = m - 1; i > 0; i--) { @@ -2282,8 +2329,11 @@ struct longint { size_type n, ATTRIBUTE_MAYBE_UNUSED size_type n_plus_one, std::uint32_t s) noexcept { + LONGINT_DEBUG_ASSERT(n > 1); ATTRIBUTE_ASSUME(n > 1); + LONGINT_DEBUG_ASSERT(s < 32); ATTRIBUTE_ASSUME(s < 32); + LONGINT_DEBUG_ASSERT(n + 1 == n_plus_one); ATTRIBUTE_ASSUME(n + 1 == n_plus_one); for (size_type i = 0; i < n; i++) { rem[i] = @@ -2339,6 +2389,7 @@ struct longint { static void convertDecBaseMultAdd(digit_t conv_digits[], const size_type conv_len, const longint& conv_base_pow, digit_t mult_add_buffer[], fft::complex fft_poly_buffer[]) { + LONGINT_DEBUG_ASSERT(0 < conv_base_pow.size_); ATTRIBUTE_ASSUME(0 < conv_base_pow.size_); const size_type m_size = conv_base_pow.usize(); const digit_t* const m_ptr = conv_base_pow.nums_; @@ -2356,9 +2407,13 @@ struct longint { digit_t mult_add_buffer[], fft::complex fft_poly_buffer[]) { const size_type half_conv_len = conv_len / 2; + LONGINT_DEBUG_ASSERT(0 < m_size); ATTRIBUTE_ASSUME(0 < m_size); + LONGINT_DEBUG_ASSERT(m_size <= half_conv_len); ATTRIBUTE_ASSUME(m_size <= half_conv_len); + LONGINT_DEBUG_ASSERT(math_functions::is_power_of_two(half_conv_len)); ATTRIBUTE_ASSUME(math_functions::is_power_of_two(half_conv_len)); + LONGINT_DEBUG_ASSERT(conv_len <= max_size()); ATTRIBUTE_ASSUME(conv_len <= max_size()); const digit_t* num_hi = conv_digits + half_conv_len; static_assert(max_size() + max_size() > max_size()); @@ -2396,6 +2451,7 @@ struct longint { ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) static Decimal convertBinBaseImpl(const digit_t nums[], const std::size_t size) { + LONGINT_DEBUG_ASSERT(math_functions::is_power_of_two(size)); ATTRIBUTE_ASSUME(math_functions::is_power_of_two(size)); switch (size) { case 0: @@ -2411,7 +2467,7 @@ struct longint { Decimal high_dec = convertBinBaseImpl(nums + size / 2, size / 2); const uint32_t idx = math_functions::log2_floor(size) - 1; - assert(idx < conv_bin_base_pows.size()); + LONGINT_DEBUG_ASSERT(idx < conv_bin_base_pows.size()); high_dec *= conv_bin_base_pows[idx]; high_dec += low_dec; return high_dec; @@ -2421,7 +2477,7 @@ struct longint { ATTRIBUTE_ALWAYS_INLINE ATTRIBUTE_SIZED_ACCESS(read_only, 1, 2) static Decimal convertBinBase(const digit_t nums[], const std::size_t size) { - assert(math_functions::is_power_of_two(size)); + LONGINT_DEBUG_ASSERT(math_functions::is_power_of_two(size)); return convertBinBaseImpl(nums, size); } @@ -2441,12 +2497,12 @@ struct longint { const size_type current_capacity = capacity(); static_assert(max_size() * 2 > max_size()); const size_type new_capacity = (current_capacity * 2) | (current_capacity == 0); + LONGINT_DEBUG_ASSERT(capacity_ < new_capacity); ATTRIBUTE_ASSUME(capacity_ < new_capacity); reserve(new_capacity); } ATTRIBUTE_NODISCARD_WITH_MESSAGE("impl error") - ATTRIBUTE_ALWAYS_INLINE size_type set_size_at_least(size_type new_size) { size_type cur_size = usize(); if (new_size <= cur_size) { @@ -2539,6 +2595,7 @@ struct longint { static constexpr void longint_add_with_free_space(digit_t lhs[], const size_type lhs_size, const digit_t rhs[], const size_type rhs_size) noexcept { + LONGINT_DEBUG_ASSERT(lhs_size > rhs_size); ATTRIBUTE_ASSUME(lhs_size > rhs_size); double_digit_t carry = 0; const digit_t* const lhs_end = lhs + lhs_size; @@ -2563,6 +2620,7 @@ struct longint { static constexpr bool longint_subtract_with_free_space(digit_t lhs[], const size_type lhs_size, const digit_t rhs[], const size_type rhs_size) noexcept { + LONGINT_DEBUG_ASSERT(lhs_size >= rhs_size); ATTRIBUTE_ASSUME(lhs_size >= rhs_size); bool overflowed = longint_subtract_with_carry(lhs, lhs_size, rhs, rhs_size); if (overflowed) { @@ -2580,6 +2638,7 @@ struct longint { static constexpr bool longint_subtract_with_carry(digit_t lhs[], const size_type lhs_size, const digit_t rhs[], const size_type rhs_size) noexcept { + LONGINT_DEBUG_ASSERT(lhs_size >= rhs_size); ATTRIBUTE_ASSUME(lhs_size >= rhs_size); const digit_t* const lhs_end = lhs + lhs_size; const digit_t* const rhs_end = rhs + rhs_size; @@ -2795,6 +2854,7 @@ inline void longint::set_str_impl(const unsigned char* str, const std::size_t st (digits_count + kStrConvBaseDigits - 1) / kStrConvBaseDigits; const size_type aligned_str_conv_digits_size = check_size( math_functions::nearest_greater_equal_power_of_two(uint64_t{str_conv_digits_size})); + LONGINT_DEBUG_ASSERT(str_conv_digits_size <= aligned_str_conv_digits_size); ATTRIBUTE_ASSUME(str_conv_digits_size <= aligned_str_conv_digits_size); reserveUninitializedWithoutCopy(aligned_str_conv_digits_size); digit_t* str_conv_digits = nums_; @@ -2855,6 +2915,7 @@ inline void longint::set_str_impl(const unsigned char* str, const std::size_t st static_assert(max_size() * 2 > max_size()); for (size_type conv_len = 2; conv_len <= aligned_str_conv_digits_size; conv_len *= 2, ++conv_dec_base_pows_iter) { + LONGINT_DEBUG_ASSERT(math_functions::is_power_of_two(conv_len)); ATTRIBUTE_ASSUME(math_functions::is_power_of_two(conv_len)); for (size_type pos = 0; pos < aligned_str_conv_digits_size; pos += conv_len) { convertDecBaseMultAdd(str_conv_digits + pos, conv_len, *conv_dec_base_pows_iter, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 98acadd..6dd7881 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -191,7 +191,8 @@ function(configure_gcc_or_clang_gcc_options) -U_GLIBCXX_USE_DEPRECATED) set(LOCAL_FN_TEST_COMPILE_DEFINITIONS ${LOCAL_FN_TEST_COMPILE_DEFINITIONS} - _GLIBCXX_SANITIZE_VECTOR) + _GLIBCXX_SANITIZE_VECTOR + ENABLE_LONGINT_DEBUG_ASSERTS=1) if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) # In gcc with version < 10.0 these checks break `constexpr`-tivity of some std:: functions set(LOCAL_FN_TEST_COMPILE_DEFINITIONS From fd27f2fb3b7548d44ac62832e2319da82bc73bab Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 23:00:35 +0300 Subject: [PATCH 09/17] update macos workflows --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 5917a54..6ab2eb3 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - macos_versions: [13, 14] + macos_versions: [12, 13, 14, 15] steps: - uses: actions/checkout@v4 From f721971bb0613244e193cd4894699db8a623a5ec Mon Sep 17 00:00:00 2001 From: i80287 Date: Thu, 24 Oct 2024 23:00:46 +0300 Subject: [PATCH 10/17] update test running scripts --- tests/prepare_stage_for_tests.sh | 11 +++++++++++ tests/run_clang_tests.sh | 22 +++++++++++----------- tests/run_codechecker.sh | 17 +++++++++-------- tests/run_gcc_mingw_32_tests.sh | 21 ++++++++++----------- tests/run_gcc_mingw_64_tests.sh | 21 ++++++++++----------- tests/run_gcc_tests.sh | 22 +++++++++++----------- tests/run_macos_clang_tests.sh | 22 +++++++++++----------- 7 files changed, 73 insertions(+), 63 deletions(-) create mode 100644 tests/prepare_stage_for_tests.sh diff --git a/tests/prepare_stage_for_tests.sh b/tests/prepare_stage_for_tests.sh new file mode 100644 index 0000000..f2b9a7c --- /dev/null +++ b/tests/prepare_stage_for_tests.sh @@ -0,0 +1,11 @@ +#!/bin/env bash + +function prepare_tests_data_and_cd_to_build_dir() { + build_dir="$1" + if [ -d "$build_dir" ]; then rm -r "$build_dir"; fi + mkdir -p "$build_dir" + cp ../number_theory/u64-primes.txt "./$build_dir/u64-primes.txt" + cp ../number_theory/u128-primes.txt "./$build_dir/u128-primes.txt" + cp ../tf_idf_actrie/Anglo_Saxon_Chronicle.txt "./$build_dir/Anglo_Saxon_Chronicle.txt" + cd "./$build_dir" || return 1 +} diff --git a/tests/run_clang_tests.sh b/tests/run_clang_tests.sh index 100e862..75348e8 100755 --- a/tests/run_clang_tests.sh +++ b/tests/run_clang_tests.sh @@ -1,16 +1,16 @@ -#!/bin/bash - -build_dir=cmake-build-tests-clang +#!/bin/env bash set -e -if [ -d "$build_dir" ]; then rm -r "$build_dir"; fi -mkdir -p "$build_dir" -cp ../number_theory/u64-primes.txt ./$build_dir/u64-primes.txt -cp ../number_theory/u128-primes.txt ./$build_dir/u128-primes.txt -cp ../tf_idf_actrie/Anglo_Saxon_Chronicle.txt ./$build_dir/Anglo_Saxon_Chronicle.txt -cd ./$build_dir +build_dir=cmake-build-tests-clang + +. ./prepare_stage_for_tests.sh +prepare_tests_data_and_cd_to_build_dir "$build_dir" -cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -S .. -B . && - make all --jobs "$(nproc)" && +cmake -G "Unix Makefiles" \ + -D CMAKE_BUILD_TYPE=RelWithDebInfo \ + -D CMAKE_C_COMPILER=clang \ + -D CMAKE_CXX_COMPILER=clang++ \ + -S .. -B . && + cmake --build . --parallel "$(nproc)" && env CTEST_OUTPUT_ON_FAILURE=1 make test diff --git a/tests/run_codechecker.sh b/tests/run_codechecker.sh index d8dba68..35457a1 100755 --- a/tests/run_codechecker.sh +++ b/tests/run_codechecker.sh @@ -1,10 +1,10 @@ -#!/bin/bash +#!/bin/env bash + +set -e build_dir=cmake-build-codechecker cmake_build_dir=cmake-build-dir -set -e - mkdir -p "$build_dir" cp ./.clang-tidy ./$build_dir cd ./$build_dir || exit 1 @@ -16,13 +16,14 @@ for cc_and_cxx in clang,clang++ gcc,g++ i686-w64-mingw32-gcc-posix,i686-w64-ming mkdir -p "$cmake_build_dir" set -- $cc_and_cxx echo "cc = $1, c++ = $2" - cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_C_COMPILER="$1" \ - -DCMAKE_CXX_COMPILER="$2" \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ + + cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo \ + -D CMAKE_C_COMPILER="$1" \ + -D CMAKE_CXX_COMPILER="$2" \ + -D CMAKE_EXPORT_COMPILE_COMMANDS=1 \ -S .. \ -B $cmake_build_dir - cd ./$cmake_build_dir && make all --jobs "$(nproc)" && cd .. + cmake --build ./$cmake_build_dir --parallel "$(nproc)" CodeChecker analyze \ ./$cmake_build_dir/compile_commands.json \ --analyzer-config cppcheck:addons=../cppcheck/addons/misc.py \ diff --git a/tests/run_gcc_mingw_32_tests.sh b/tests/run_gcc_mingw_32_tests.sh index ba8112f..2e7d105 100755 --- a/tests/run_gcc_mingw_32_tests.sh +++ b/tests/run_gcc_mingw_32_tests.sh @@ -1,18 +1,17 @@ -#!/bin/bash - -build_dir=cmake-build-tests-gcc-mingw-w32 +#!/bin/env bash set -e -if [ -d "$build_dir" ]; then rm -r "$build_dir"; fi -mkdir -p "$build_dir" -cp ../number_theory/u64-primes.txt ./$build_dir/u64-primes.txt -cp ../number_theory/u128-primes.txt ./$build_dir/u128-primes.txt -cp ../tf_idf_actrie/Anglo_Saxon_Chronicle.txt ./$build_dir/Anglo_Saxon_Chronicle.txt -cd ./$build_dir +build_dir=cmake-build-tests-gcc-mingw-w32 + +. ./prepare_stage_for_tests.sh +prepare_tests_data_and_cd_to_build_dir "$build_dir" -cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=i686-w64-mingw32-gcc-posix -DCMAKE_CXX_COMPILER=i686-w64-mingw32-g++-posix -S .. -B . && - make all --jobs "$(nproc)" +cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo \ + -D CMAKE_C_COMPILER=i686-w64-mingw32-gcc-posix \ + -D CMAKE_CXX_COMPILER=i686-w64-mingw32-g++-posix \ + -S .. -B . && + cmake --build . --parallel "$(nproc)" shopt -s nullglob for test_executable in *.exe; do diff --git a/tests/run_gcc_mingw_64_tests.sh b/tests/run_gcc_mingw_64_tests.sh index de9ca7c..9dfadac 100755 --- a/tests/run_gcc_mingw_64_tests.sh +++ b/tests/run_gcc_mingw_64_tests.sh @@ -1,18 +1,17 @@ -#!/bin/bash - -build_dir=cmake-build-tests-gcc-mingw-w64 +#!/bin/env bash set -e -if [ -d "$build_dir" ]; then rm -r "$build_dir"; fi -mkdir -p "$build_dir" -cp ../number_theory/u64-primes.txt ./$build_dir/u64-primes.txt -cp ../number_theory/u128-primes.txt ./$build_dir/u128-primes.txt -cp ../tf_idf_actrie/Anglo_Saxon_Chronicle.txt ./$build_dir/Anglo_Saxon_Chronicle.txt -cd ./$build_dir +build_dir=cmake-build-tests-gcc-mingw-w64 + +. ./prepare_stage_for_tests.sh +prepare_tests_data_and_cd_to_build_dir "$build_dir" -cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc-posix -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++-posix -S .. -B . && - make all --jobs "$(nproc)" +cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo \ + -D CMAKE_C_COMPILER=x86_64-w64-mingw32-gcc-posix \ + -D CMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++-posix \ + -S .. -B . && + cmake --build . --parallel "$(nproc)" shopt -s nullglob for test_executable in *.exe; do diff --git a/tests/run_gcc_tests.sh b/tests/run_gcc_tests.sh index 489db2e..de066a6 100755 --- a/tests/run_gcc_tests.sh +++ b/tests/run_gcc_tests.sh @@ -1,16 +1,16 @@ -#!/bin/bash - -build_dir=cmake-build-tests-gcc +#!/bin/env bash set -e -if [ -d "$build_dir" ]; then rm -r "$build_dir"; fi -mkdir -p "$build_dir" -cp ../number_theory/u64-primes.txt ./$build_dir/u64-primes.txt -cp ../number_theory/u128-primes.txt ./$build_dir/u128-primes.txt -cp ../tf_idf_actrie/Anglo_Saxon_Chronicle.txt ./$build_dir/Anglo_Saxon_Chronicle.txt -cd ./$build_dir +build_dir=cmake-build-tests-gcc + +. ./prepare_stage_for_tests.sh +prepare_tests_data_and_cd_to_build_dir "$build_dir" -cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -S .. -B . && - make all --jobs "$(nproc)" && +cmake -G "Unix Makefiles" \ + -D CMAKE_BUILD_TYPE=RelWithDebInfo \ + -D CMAKE_C_COMPILER=gcc \ + -D CMAKE_CXX_COMPILER=g++ \ + -S .. -B . && + cmake --build . --parallel "$(nproc)" && env CTEST_OUTPUT_ON_FAILURE=1 make test diff --git a/tests/run_macos_clang_tests.sh b/tests/run_macos_clang_tests.sh index 92434b6..d403d2b 100644 --- a/tests/run_macos_clang_tests.sh +++ b/tests/run_macos_clang_tests.sh @@ -1,16 +1,16 @@ -#!/bin/bash - -build_dir=cmake-build-tests-macos-clang +#!/bin/env bash set -e -if [ -d "$build_dir" ]; then rm -r "$build_dir"; fi -mkdir -p "$build_dir" -cp ../number_theory/u64-primes.txt ./$build_dir/u64-primes.txt -cp ../number_theory/u128-primes.txt ./$build_dir/u128-primes.txt -cp ../tf_idf_actrie/Anglo_Saxon_Chronicle.txt ./$build_dir/Anglo_Saxon_Chronicle.txt -cd ./$build_dir +build_dir=cmake-build-tests-macos-clang + +. ./prepare_stage_for_tests.sh +prepare_tests_data_and_cd_to_build_dir "$build_dir" -cmake -D CMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -S .. -B . && - make all --jobs "$(sysctl -n hw.logicalcpu)" && +cmake -G "Unix Makefiles" \ + -D CMAKE_BUILD_TYPE=RelWithDebInfo \ + -D CMAKE_C_COMPILER=clang \ + -D CMAKE_CXX_COMPILER=clang++ \ + -S .. -B . && + cmake --build . --parallel "$(sysctl -n hw.logicalcpu)" && env CTEST_OUTPUT_ON_FAILURE=1 make test From c51928b556f72acc64d9a87244746a9a63368e00 Mon Sep 17 00:00:00 2001 From: i80287 Date: Fri, 25 Oct 2024 00:02:09 +0300 Subject: [PATCH 11/17] update windows tests running scripts --- tests/run_windows_clang_cl_tests.bat | 2 +- tests/run_windows_msvc_tests.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run_windows_clang_cl_tests.bat b/tests/run_windows_clang_cl_tests.bat index 0978450..ecf9fc3 100644 --- a/tests/run_windows_clang_cl_tests.bat +++ b/tests/run_windows_clang_cl_tests.bat @@ -13,7 +13,7 @@ if %errorlevel% neq 0 exit /b %errorlevel% cmake -G "Visual Studio 17 2022" -A x64 -T ClangCL -S .. -B . if %errorlevel% neq 0 exit /b %errorlevel% -msbuild tests.sln -property:Configuration=RelWithDebInfo -noWarn:D9025 -maxcpucount:%NUMBER_OF_PROCESSORS% +msbuild tests.sln -property:Configuration=RelWithDebInfo -noWarn:D9025 -warnaserror -maxcpucount:%NUMBER_OF_PROCESSORS% if %errorlevel% neq 0 exit /b %errorlevel% msbuild RUN_TESTS.vcxproj -property:Configuration=RelWithDebInfo diff --git a/tests/run_windows_msvc_tests.bat b/tests/run_windows_msvc_tests.bat index 283fcae..d515594 100644 --- a/tests/run_windows_msvc_tests.bat +++ b/tests/run_windows_msvc_tests.bat @@ -13,7 +13,7 @@ if %errorlevel% neq 0 exit /b %errorlevel% cmake -G "Visual Studio 17 2022" -A x64 -S .. -B . if %errorlevel% neq 0 exit /b %errorlevel% -msbuild tests.sln -property:Configuration=RelWithDebInfo -noWarn:D9025 -maxcpucount:%NUMBER_OF_PROCESSORS% +msbuild tests.sln -property:Configuration=RelWithDebInfo -noWarn:D9025 -warnaserror -maxcpucount:%NUMBER_OF_PROCESSORS% if %errorlevel% neq 0 exit /b %errorlevel% msbuild RUN_TESTS.vcxproj -property:Configuration=RelWithDebInfo From f27d9bd9899cd05b75e19741a4284ea5a67374e9 Mon Sep 17 00:00:00 2001 From: i80287 Date: Fri, 25 Oct 2024 00:02:26 +0300 Subject: [PATCH 12/17] update github workflows configs --- .github/workflows/fedora.yml | 13 +++++-------- .github/workflows/macos.yml | 4 +--- .github/workflows/ubuntu.yml | 22 +++++++--------------- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/.github/workflows/fedora.yml b/.github/workflows/fedora.yml index 885f929..9d39dd5 100644 --- a/.github/workflows/fedora.yml +++ b/.github/workflows/fedora.yml @@ -3,11 +3,11 @@ name: Fedora CI on: [push, workflow_dispatch] permissions: - contents: read + contents: read env: - UBSAN_OPTIONS: print_stacktrace=1 - ASAN_OPTIONS: print_stats=1:atexit=1:detect_odr_violation=2 + UBSAN_OPTIONS: print_stacktrace=1 + ASAN_OPTIONS: print_stats=1:atexit=1:detect_odr_violation=2 jobs: @@ -36,8 +36,7 @@ jobs: - name: Run tests run: | cd ./tests - chmod +x ./run_gcc_tests.sh - ./run_gcc_tests.sh + chmod +x run_gcc_tests.sh && ./run_gcc_tests.sh fedora-cmake-clang: name: test-fedora-clang @@ -63,6 +62,4 @@ jobs: - name: Run tests run: | - cd ./tests - chmod +x ./run_clang_tests.sh - ./run_clang_tests.sh + cd ./tests && chmod +x run_clang_tests.sh && ./run_clang_tests.sh diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 6ab2eb3..a8fe3b0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -30,6 +30,4 @@ jobs: - name: Run tests run: | - cd ./tests - chmod +x run_macos_clang_tests.sh - ./run_macos_clang_tests.sh + cd ./tests && chmod +x run_macos_clang_tests.sh && ./run_macos_clang_tests.sh diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3674516..f453ed1 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -3,11 +3,11 @@ name: Ubuntu CI on: [push, workflow_dispatch] permissions: - contents: read + contents: read env: - UBSAN_OPTIONS: print_stacktrace=1 - ASAN_OPTIONS: print_stats=1:atexit=1:detect_odr_violation=2 + UBSAN_OPTIONS: print_stacktrace=1 + ASAN_OPTIONS: print_stats=1:atexit=1:detect_odr_violation=2 jobs: @@ -34,9 +34,7 @@ jobs: - name: Run tests run: | - cd ./tests - chmod +x ./run_gcc_tests.sh - ./run_gcc_tests.sh + cd ./tests && chmod +x run_gcc_tests.sh && ./run_gcc_tests.sh ubuntu-cmake-clang: name: test-clang-on-ubuntu-${{ matrix.ubuntu_version }} @@ -61,9 +59,7 @@ jobs: - name: Run tests run: | - cd ./tests - chmod +x ./run_clang_tests.sh - ./run_clang_tests.sh + cd ./tests && chmod +x run_clang_tests.sh && ./run_clang_tests.sh ubuntu-cmake-gcc-mingw-w64-i686-posix: name: test-gcc-mingw-w64-i686-posix-on-ubuntu-${{ matrix.ubuntu_version }} @@ -93,9 +89,7 @@ jobs: - name: Run tests run: | - cd ./tests - chmod +x ./run_gcc_mingw_32_tests.sh - ./run_gcc_mingw_32_tests.sh + cd ./tests && chmod +x run_gcc_mingw_32_tests.sh && ./run_gcc_mingw_32_tests.sh ubuntu-cmake-gcc-mingw-w64-x86_64-posix: name: test-gcc-mingw-w64-x86_64-posix-on-ubuntu-${{ matrix.ubuntu_version }} @@ -125,6 +119,4 @@ jobs: - name: Run tests run: | - cd ./tests - chmod +x ./run_gcc_mingw_64_tests.sh - ./run_gcc_mingw_64_tests.sh + cd ./tests && chmod +x run_gcc_mingw_64_tests.sh && ./run_gcc_mingw_64_tests.sh From 12e7ef946147ba6275d5be25e854b5d89a025330 Mon Sep 17 00:00:00 2001 From: i80287 Date: Fri, 25 Oct 2024 00:10:54 +0300 Subject: [PATCH 13/17] remove macos 12 from the macos.yml workflow --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index a8fe3b0..a29d7b4 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - macos_versions: [12, 13, 14, 15] + macos_versions: [13, 14, 15] steps: - uses: actions/checkout@v4 From 6fe1ff3fb2e996cde7e0a71e85ac3e7e8dfc4f71 Mon Sep 17 00:00:00 2001 From: i80287 Date: Fri, 25 Oct 2024 00:14:39 +0300 Subject: [PATCH 14/17] update shebang in the tests running scripts --- tests/prepare_stage_for_tests.sh | 2 +- tests/run_clang_tests.sh | 2 +- tests/run_codechecker.sh | 2 +- tests/run_gcc_mingw_32_tests.sh | 2 +- tests/run_gcc_mingw_64_tests.sh | 2 +- tests/run_gcc_tests.sh | 2 +- tests/run_macos_clang_tests.sh | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/prepare_stage_for_tests.sh b/tests/prepare_stage_for_tests.sh index f2b9a7c..8766af3 100644 --- a/tests/prepare_stage_for_tests.sh +++ b/tests/prepare_stage_for_tests.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash function prepare_tests_data_and_cd_to_build_dir() { build_dir="$1" diff --git a/tests/run_clang_tests.sh b/tests/run_clang_tests.sh index 75348e8..09d8ca2 100755 --- a/tests/run_clang_tests.sh +++ b/tests/run_clang_tests.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash set -e diff --git a/tests/run_codechecker.sh b/tests/run_codechecker.sh index 35457a1..d7ecb04 100755 --- a/tests/run_codechecker.sh +++ b/tests/run_codechecker.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash set -e diff --git a/tests/run_gcc_mingw_32_tests.sh b/tests/run_gcc_mingw_32_tests.sh index 2e7d105..28c8450 100755 --- a/tests/run_gcc_mingw_32_tests.sh +++ b/tests/run_gcc_mingw_32_tests.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash set -e diff --git a/tests/run_gcc_mingw_64_tests.sh b/tests/run_gcc_mingw_64_tests.sh index 9dfadac..18aaf49 100755 --- a/tests/run_gcc_mingw_64_tests.sh +++ b/tests/run_gcc_mingw_64_tests.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash set -e diff --git a/tests/run_gcc_tests.sh b/tests/run_gcc_tests.sh index de066a6..e6d6f4d 100755 --- a/tests/run_gcc_tests.sh +++ b/tests/run_gcc_tests.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash set -e diff --git a/tests/run_macos_clang_tests.sh b/tests/run_macos_clang_tests.sh index d403d2b..301b179 100644 --- a/tests/run_macos_clang_tests.sh +++ b/tests/run_macos_clang_tests.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash set -e From c924abfcf807c5df4dddfbd0b171ba26a04eea91 Mon Sep 17 00:00:00 2001 From: i80287 Date: Fri, 25 Oct 2024 01:09:36 +0300 Subject: [PATCH 15/17] update number_theory/fibonacci_num.hpp --- number_theory/fibonacci_num.hpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/number_theory/fibonacci_num.hpp b/number_theory/fibonacci_num.hpp index f40e441..36eeba3 100644 --- a/number_theory/fibonacci_num.hpp +++ b/number_theory/fibonacci_num.hpp @@ -12,8 +12,8 @@ namespace detail { ATTRIBUTE_ACCESS(read_write, 1) ATTRIBUTE_ACCESS(read_only, 2) -constexpr void matrix_mul(uint64_t (&m1)[2][2], const uint64_t (&m2)[2][2]) noexcept { - const uint64_t tmp[2][2] = { +constexpr void matrix_mul(std::uint64_t (&m1)[2][2], const std::uint64_t (&m2)[2][2]) noexcept { + const std::uint64_t tmp[2][2] = { {m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0], m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1]}, {m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0], m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1]}, }; @@ -27,9 +27,9 @@ constexpr void matrix_mul(uint64_t (&m1)[2][2], const uint64_t (&m2)[2][2]) noex struct fibs_pair { /// @brief F_{n - 1} - uint64_t fib_n_1; + std::uint64_t fib_n_1; /// @brief F_n - uint64_t fib_n; + std::uint64_t fib_n; }; /// @brief Returns pair (F_{n - 1}, F_n), where F_n @@ -37,12 +37,13 @@ struct fibs_pair { /// Here we suppose that F_{-1} = 0, F_0 = 1, F_1 = 1. /// @param n /// @return (F_{n - 1}, F_n) -ATTRIBUTE_CONST constexpr fibs_pair fibonacci_nums(uint32_t n) noexcept { - uint64_t p[2][2] = { +[[nodiscard]] +ATTRIBUTE_CONST constexpr fibs_pair fibonacci_nums(std::uint32_t n) noexcept { + std::uint64_t p[2][2] = { {0, 1}, {1, 1}, }; - uint64_t fibmatrix[2][2] = { + std::uint64_t fibmatrix[2][2] = { {1, 0}, {0, 1}, }; @@ -67,7 +68,8 @@ ATTRIBUTE_CONST constexpr fibs_pair fibonacci_nums(uint32_t n) noexcept { /// Here we suppose that F_{-1} = 0, F_0 = 1, F_1 = 1. /// @param n /// @return F_n -ATTRIBUTE_CONST constexpr uint64_t fibonacci_num(uint32_t n) noexcept { +[[nodiscard]] +ATTRIBUTE_CONST constexpr std::uint64_t fibonacci_num(std::uint32_t n) noexcept { return fibonacci_nums(n).fib_n; } @@ -83,7 +85,7 @@ ATTRIBUTE_CONST constexpr uint64_t fibonacci_num(uint32_t n) noexcept { /// fibonacci_num(91) == 7540113804746346429 /// fibonacci_num(92) == 12200160415121876738 /// fibonacci_num(93) == 1293530146158671551 <- overflow -inline constexpr uint32_t kMaxFibNonOverflowU64 = 92; +inline constexpr std::uint32_t kMaxFibNonOverflowU64 = 92; #if defined(INTEGERS_128_BIT_HPP) @@ -92,7 +94,7 @@ namespace detail { ATTRIBUTE_ACCESS(read_write, 1) ATTRIBUTE_ACCESS(read_only, 2) -I128_CONSTEXPR void matrix_mul(uint128_t m1[2][2], const uint128_t m2[2][2]) noexcept { +I128_CONSTEXPR void matrix_mul(uint128_t (&m1)[2][2], const uint128_t (&m2)[2][2]) noexcept { const uint128_t tmp[2][2] = { {m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0], m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1]}, {m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0], m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1]}, @@ -117,7 +119,8 @@ struct fibs_pair_u128 { /// Here we suppose that F_{-1} = 0, F_0 = 1, F_1 = 1. /// @param n /// @return (F_{n - 1}, F_n) -ATTRIBUTE_CONST I128_CONSTEXPR fibs_pair_u128 fibonacci_nums_u128(uint32_t n) noexcept { +[[nodiscard]] +ATTRIBUTE_CONST I128_CONSTEXPR fibs_pair_u128 fibonacci_nums_u128(std::uint32_t n) noexcept { uint128_t p[2][2] = { {0, 1}, {1, 1}, @@ -147,7 +150,8 @@ ATTRIBUTE_CONST I128_CONSTEXPR fibs_pair_u128 fibonacci_nums_u128(uint32_t n) no /// Here we suppose that F_{-1} = 0, F_0 = 1, F_1 = 1. /// @param n /// @return F_n -ATTRIBUTE_CONST I128_CONSTEXPR uint128_t fibonacci_num_u128(uint32_t n) noexcept { +[[nodiscard]] +ATTRIBUTE_CONST I128_CONSTEXPR uint128_t fibonacci_num_u128(std::uint32_t n) noexcept { return fibonacci_nums_u128(n).fib_n; } @@ -165,7 +169,7 @@ ATTRIBUTE_CONST I128_CONSTEXPR uint128_t fibonacci_num_u128(uint32_t n) noexcept /// fibonacci_num_u128(186) == 198239973509362327032045173661212819077 /// ^ /// overflow -inline constexpr uint32_t kMaxFibNonOverflowU128 = 185; +inline constexpr std::uint32_t kMaxFibNonOverflowU128 = 185; #endif // INTEGERS_128_BIT_HPP From 9c0db12c896c6f33ea953d53b4655e64e6ff7e09 Mon Sep 17 00:00:00 2001 From: i80287 Date: Fri, 25 Oct 2024 01:29:34 +0300 Subject: [PATCH 16/17] update test_long_int.cpp --- number_theory/test_long_int.cpp | 109 ++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 32 deletions(-) diff --git a/number_theory/test_long_int.cpp b/number_theory/test_long_int.cpp index ffc7389..7c66165 100644 --- a/number_theory/test_long_int.cpp +++ b/number_theory/test_long_int.cpp @@ -50,8 +50,10 @@ void TestOperatorEqualsInt() { assert(n.sign() == 0); assert(n.size() == 0); assert(n == 0); + assert(n.iszero()); + assert(!n); AssertInvariants(n); - for (int32_t i = 1; i <= K; i++) { + for (int32_t i = 0; i <= K; i++) { n = i; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == uint32_t(i)); @@ -72,8 +74,10 @@ void TestOperatorEqualsInt() { assert(n.sign() == 0); assert(n.size() == 0); assert(n == 0u); + assert(n.iszero()); + assert(!n); AssertInvariants(n); - for (uint32_t i = 1; i <= K; i++) { + for (uint32_t i = 0; i <= K; i++) { n = i; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == i); @@ -86,8 +90,10 @@ void TestOperatorEqualsInt() { assert(n.sign() == 0); assert(n.size() == 0); assert(n == int64_t{0}); + assert(n.iszero()); + assert(!n); AssertInvariants(n); - for (int64_t i = 1; i <= K; i++) { + for (int64_t i = 0; i <= K; i++) { n = i; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == uint32_t(i)); @@ -108,13 +114,15 @@ void TestOperatorEqualsInt() { assert(n.sign() == 0); assert(n.size() == 0); assert(n == uint64_t{0}); + assert(n.iszero()); + assert(!n); AssertInvariants(n); - for (uint64_t i = 1; i < K; i++) { + for (uint64_t i = 0; i < K; i++) { n = i; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == i); assert(n == i); - assert(n != -int128_t{i}); + assert(n != static_cast(-i)); AssertInvariants(n); } @@ -122,8 +130,10 @@ void TestOperatorEqualsInt() { assert(n.sign() == 0); assert(n.size() == 0); assert(n == int128_t{0}); + assert(n.iszero()); + assert(!n); AssertInvariants(n); - for (int64_t i = 1; i <= K; i++) { + for (int64_t i = 0; i <= K; i++) { n = int128_t{i}; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == uint32_t(i)); @@ -143,6 +153,8 @@ void TestOperatorEqualsInt() { n = uint128_t{0}; assert(n.sign() == 0); assert(n.size() == 0); + assert(n.iszero()); + assert(!n); AssertInvariants(n); n = static_cast(-1); assert(n.sign() == 1); @@ -176,11 +188,10 @@ void TestLongIntMult() { longint n1; longint n2; constexpr uint64_t K = 5000; - for (uint32_t i = 1; i <= K; i++) { - for (uint32_t j = 1; j <= K; j++) { + for (uint32_t i = 0; i <= K; i++) { + for (uint32_t j = 0; j <= K; j++) { n1 = i; n2 = j; - assert((n1 < n2) == (i < j)); n1 *= n2; assert(n1 == i * j); AssertInvariants(n1); @@ -188,13 +199,12 @@ void TestLongIntMult() { } } - for (uint64_t i = uint64_t(-1) - K; i != 0; i++) { - for (uint64_t j = uint64_t(-1) - K; j != 0; j++) { - n1 = uint64_t(i); - n2 = uint64_t(j); - assert((n1 < n2) == (i < j)); + for (uint64_t i = std::numeric_limits::max() - K; i != 0; i++) { + for (uint64_t j = std::numeric_limits::max() - K; j != 0; j++) { + n1 = i; + n2 = j; n1 *= n2; - assert(n1 == (uint128_t(i) * uint128_t(j))); + assert(n1 == uint128_t{i} * uint128_t{j}); AssertInvariants(n1); AssertInvariants(n2); } @@ -503,7 +513,7 @@ void TestLongIntMult() { namespace chrono = std::chrono; - constexpr std::size_t k = std::size_t(1e6); + constexpr std::size_t k = static_cast(1e6); std::string s1(k, '9'); n1.set_string(s1); { @@ -540,25 +550,26 @@ void TestLongIntSquare() { for (uint32_t i = std::numeric_limits::max() - K; i != 0; i++) { n = i; n.SquareInplace(); - assert(n == uint64_t(i) * i); + assert(n == uint64_t{i} * i); AssertInvariants(n); } - for (uint64_t i = uint64_t(-1) - K; i != 0; i++) { + for (uint64_t i = std::numeric_limits::max() - K; i != 0; i++) { n = i; n.SquareInplace(); - assert(n == uint128_t(i) * i); + assert(n == uint128_t{i} * i); AssertInvariants(n); } for (uint32_t p = 32; p <= 96; p += 32) { - n = uint128_t(1) << p; + n = uint128_t{1} << p; n.SquareInplace(); - assert(n.size() == int32_t((p + p) / 32 + 1)); - for (std::size_t i = 0; i < (p + p) / 32; i++) { + const uint32_t size = (p + p) / longint::kNumsBits + 1; + assert(n.size() == static_cast(size)); + for (std::size_t i = 0; i < size - 1; i++) { assert(n[i] == 0); } - assert(n[(p + p) / 32] == 1); + assert(n[size - 1] == 1); AssertInvariants(n); } @@ -633,7 +644,8 @@ void TestUIntMult() { } } - for (uint128_t i = (uint128_t(-1) / K) - K; i != (uint128_t(-1) / K); i++) { + constexpr uint128_t kStartPos1 = uint128_t(-1) / K; + for (uint128_t i = kStartPos1 - K; i != kStartPos1; i++) { for (uint32_t j = 0; j < K; j++) { n = i; n *= j; @@ -645,17 +657,17 @@ void TestUIntMult() { for (uint32_t i = std::numeric_limits::max() - K; i != 0; i++) { for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; - n *= uint32_t(j); - assert(n == uint64_t(i) * uint64_t(j)); + n *= j; + assert(n == uint64_t{i} * uint64_t{j}); AssertInvariants(n); } } - for (uint64_t i = uint64_t(-1) - K; i != 0; i++) { + for (uint64_t i = std::numeric_limits::max() - K; i != 0; i++) { for (uint32_t j = std::numeric_limits::max() - K; j != 0; j++) { n = i; n *= j; - assert(n == uint128_t(i) * j); + assert(n == uint128_t{i} * j); AssertInvariants(n); } } @@ -667,7 +679,7 @@ void TestUIntAddAndSub() { longint n(longint::Reserve(4)); constexpr uint32_t K = 4000; n -= std::numeric_limits::max(); - assert(n == -int64_t(std::numeric_limits::max())); + assert(n == -int64_t{std::numeric_limits::max()}); for (uint32_t i = 0; i <= K; i++) { for (uint32_t j = 0; j <= K; j++) { n = i; @@ -859,7 +871,7 @@ void TestLongIntAddAndSub() { longint m(longint::Reserve(4)); constexpr uint32_t K = 5000; for (uint32_t i = 0; i <= K; i++) { - for (uint32_t j = 1; j <= K; j++) { + for (uint32_t j = 0; j <= K; j++) { n = i; m = j; n += m; @@ -1217,7 +1229,7 @@ void TestBitShifts() { for (uint32_t shift = 0; shift <= 127; shift++) { m = n; m >>= (255 - shift); - assert(m == uint128_t(1) << shift); + assert(m == uint128_t{1} << shift); AssertInvariants(m); } @@ -1272,6 +1284,39 @@ void TestBitShifts() { assert(n == m); AssertInvariants(n); AssertInvariants(m); + n = 1u; + n <<= 8000; + m.set_string( + "173766203193809456599982445949435627061939786100117250547173286503262376022458008465094333" + "630120854338003194362163007597987225472483598640843335685441710193966274131338557192586399" + "006789292714554767500194796127964596906605976605873665859580600161998556511368530960400907" + "199253450604168622770350228527124626728538626805418833470107651091641919900725415994689920" + "112219170907023561354484047025713734651608777544579846111001059482132180956689444108315785" + "401642188044178788629853592228467331730519810763559577944882016286493908631503101121166109" + "571682295769470379514531105239965209245314082665518579335511291525230373316486697786532335" + "206274149240813489201828773854353041855598709390675430960381072270432383913542702130202430" + "186637321862331068861776780211082856984506050024895394320139435868484643843368002496089956" + "046419964019877586845530207748994394501505588146979082629871366088121763790555364513243984" + "244004147636040219136443410377798011608722717131323621700159335786445601947601694025107888" + "293017058178562647175461026384343438874861406516767158373279032321096262126551620255666605" + "185789463207944391905756886829667520553014724372245300878786091700563444079107099009003380" + "230356461989260377273986023281444076082783406824471703499844642915587790146384758051663547" + "775336021829171033411043796977042190519657861762804226147480755555085278062866268677842432" + "851421790544407006581148631979148571299417963950579210719961422405768071335213324842709316" + "205032078384168750091017964584060285240107161561019930505687950233196051962261970932008838" + "279760834318101044311710769457048672103958655016388894770892065267451228938951370237422841" + "366052736174160431593023473217066764172949768821843606479073866252864377064398085101223216" + "558344281956767163876579889759124956035672317578122141070933058555310274598884089982879647" + "974020264495921703064439532898207943134374576254840272047075633856749514044298135927611328" + "433323640657533550512376900773273703275329924651465759145114579174356770593439987135755889" + "403613364529029604049868233807295134382284730745937309910703657676103447124097631074153287" + "120040247837143656624045055614076111832245239612708339272798262887437416818440064925049838" + "443370805645609424314780108030016683461562597569371539974003402697903023830108053034645133" + "078208043917492087248958344081026378788915528519967248989338592027124423914083391771884524" + "464968645052058218151010508471258285907685355807229880747677634789376"); + assert(n == m); + AssertInvariants(n); + AssertInvariants(m); auto test_compile_time_bit_shifts = [&n, &m](uint64_t i) { n = i; @@ -1329,7 +1374,7 @@ void TestBitShifts() { AssertInvariants(n); AssertInvariants(m); }; - for (uint64_t i = 1; i < k; i++) { + for (uint64_t i = 0; i < k; i++) { test_compile_time_bit_shifts(i); } for (uint64_t i = std::numeric_limits::max() - k; static_cast(i) != 0; From f1b787ab9263e102e692f990fae482da8a068a00 Mon Sep 17 00:00:00 2001 From: i80287 Date: Fri, 25 Oct 2024 02:38:55 +0300 Subject: [PATCH 17/17] update number_theory/test_long_int.cpp --- number_theory/test_long_int.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/number_theory/test_long_int.cpp b/number_theory/test_long_int.cpp index 7c66165..cbe1bc2 100644 --- a/number_theory/test_long_int.cpp +++ b/number_theory/test_long_int.cpp @@ -53,7 +53,7 @@ void TestOperatorEqualsInt() { assert(n.iszero()); assert(!n); AssertInvariants(n); - for (int32_t i = 0; i <= K; i++) { + for (int32_t i = 1; i <= K; i++) { n = i; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == uint32_t(i)); @@ -77,7 +77,7 @@ void TestOperatorEqualsInt() { assert(n.iszero()); assert(!n); AssertInvariants(n); - for (uint32_t i = 0; i <= K; i++) { + for (uint32_t i = 1; i <= K; i++) { n = i; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == i); @@ -93,7 +93,7 @@ void TestOperatorEqualsInt() { assert(n.iszero()); assert(!n); AssertInvariants(n); - for (int64_t i = 0; i <= K; i++) { + for (int64_t i = 1; i <= K; i++) { n = i; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == uint32_t(i)); @@ -117,7 +117,7 @@ void TestOperatorEqualsInt() { assert(n.iszero()); assert(!n); AssertInvariants(n); - for (uint64_t i = 0; i < K; i++) { + for (uint64_t i = 1; i < K; i++) { n = i; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == i); @@ -133,7 +133,7 @@ void TestOperatorEqualsInt() { assert(n.iszero()); assert(!n); AssertInvariants(n); - for (int64_t i = 0; i <= K; i++) { + for (int64_t i = 1; i <= K; i++) { n = int128_t{i}; assert(n.sign() == 1); assert(n.size() == 1 && n.begin()[0] == uint32_t(i)); @@ -165,7 +165,7 @@ void TestOperatorEqualsInt() { n.begin()[3] == std::numeric_limits::max()); AssertInvariants(n); - for (uint64_t i = uint64_t(-1) - K; i != 0; i++) { + for (uint64_t i = std::numeric_limits::max() - K; i != 0; i++) { n = i; assert(n == i); n = uint128_t(i);