From 999f81175711c574eb730dc4c58f56735979733d Mon Sep 17 00:00:00 2001 From: Kohei Morita Date: Sun, 19 Mar 2023 23:45:37 +0900 Subject: [PATCH 1/4] fix bit functions to fit cpp20 --- atcoder/convolution.hpp | 16 ++++----- atcoder/internal_bit.hpp | 30 ++++++++-------- atcoder/lazysegtree.hpp | 4 +-- atcoder/segtree.hpp | 4 +-- test/unittest/bit_test.cpp | 73 +++++++++++++++++++++++--------------- 5 files changed, 73 insertions(+), 54 deletions(-) diff --git a/atcoder/convolution.hpp b/atcoder/convolution.hpp index 280ccc5..7b27f81 100644 --- a/atcoder/convolution.hpp +++ b/atcoder/convolution.hpp @@ -18,7 +18,7 @@ template , internal::is_static_modint_t* = nullptr> struct fft_info { - static constexpr int rank2 = bsf_constexpr(mint::mod() - 1); + static constexpr int rank2 = countr_zero_constexpr(mint::mod() - 1); std::array root; // root[i]^(2^i) == 1 std::array iroot; // root[i] * iroot[i] == 1 @@ -60,7 +60,7 @@ struct fft_info { template * = nullptr> void butterfly(std::vector& a) { int n = int(a.size()); - int h = internal::ceil_pow2(n); + int h = internal::countr_zero((unsigned int)n); static const fft_info info; @@ -78,7 +78,7 @@ void butterfly(std::vector& a) { a[i + offset + p] = l - r; } if (s + 1 != (1 << len)) - rot *= info.rate2[bsf(~(unsigned int)(s))]; + rot *= info.rate2[countr_zero(~(unsigned int)(s))]; } len++; } else { @@ -104,7 +104,7 @@ void butterfly(std::vector& a) { a[i + offset + 3 * p] = a0 + na2 + (mod2 - a1na3imag); } if (s + 1 != (1 << len)) - rot *= info.rate3[bsf(~(unsigned int)(s))]; + rot *= info.rate3[countr_zero(~(unsigned int)(s))]; } len += 2; } @@ -114,7 +114,7 @@ void butterfly(std::vector& a) { template * = nullptr> void butterfly_inv(std::vector& a) { int n = int(a.size()); - int h = internal::ceil_pow2(n); + int h = internal::countr_zero((unsigned int)n); static const fft_info info; @@ -135,7 +135,7 @@ void butterfly_inv(std::vector& a) { ; } if (s + 1 != (1 << (len - 1))) - irot *= info.irate2[bsf(~(unsigned int)(s))]; + irot *= info.irate2[countr_zero(~(unsigned int)(s))]; } len--; } else { @@ -167,7 +167,7 @@ void butterfly_inv(std::vector& a) { irot3.val(); } if (s + 1 != (1 << (len - 2))) - irot *= info.irate3[bsf(~(unsigned int)(s))]; + irot *= info.irate3[countr_zero(~(unsigned int)(s))]; } len -= 2; } @@ -198,7 +198,7 @@ std::vector convolution_naive(const std::vector& a, template * = nullptr> std::vector convolution_fft(std::vector a, std::vector b) { int n = int(a.size()), m = int(b.size()); - int z = 1 << internal::ceil_pow2(n + m - 1); + int z = (int)internal::bit_ceil((unsigned int)(n + m - 1)); assert(mint::mod() % z == 1); a.resize(z); internal::butterfly(a); diff --git a/atcoder/internal_bit.hpp b/atcoder/internal_bit.hpp index ada311a..05f2b54 100644 --- a/atcoder/internal_bit.hpp +++ b/atcoder/internal_bit.hpp @@ -5,29 +5,23 @@ #include #endif +#if __cplusplus >= 202002L +#include +#endif + namespace atcoder { namespace internal { -// @param n `0 <= n` -// @return minimum non-negative `x` s.t. `n <= 2**x` -int ceil_pow2(int n) { - int x = 0; - while ((1U << x) < (unsigned int)(n)) x++; +unsigned int bit_ceil(unsigned int n) { + unsigned int x = 1; + while (x < (unsigned int)(n)) x *= 2; return x; } // @param n `1 <= n` // @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` -constexpr int bsf_constexpr(unsigned int n) { - int x = 0; - while (!(n & (1 << x))) x++; - return x; -} - -// @param n `1 <= n` -// @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` -int bsf(unsigned int n) { +int countr_zero(unsigned int n) { #ifdef _MSC_VER unsigned long index; _BitScanForward(&index, n); @@ -37,6 +31,14 @@ int bsf(unsigned int n) { #endif } +// @param n `1 <= n` +// @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` +constexpr int countr_zero_constexpr(unsigned int n) { + int x = 0; + while (!(n & (1 << x))) x++; + return x; +} + } // namespace internal } // namespace atcoder diff --git a/atcoder/lazysegtree.hpp b/atcoder/lazysegtree.hpp index f4c5e4f..2119d82 100644 --- a/atcoder/lazysegtree.hpp +++ b/atcoder/lazysegtree.hpp @@ -22,8 +22,8 @@ struct lazy_segtree { lazy_segtree() : lazy_segtree(0) {} explicit lazy_segtree(int n) : lazy_segtree(std::vector(n, e())) {} explicit lazy_segtree(const std::vector& v) : _n(int(v.size())) { - log = internal::ceil_pow2(_n); - size = 1 << log; + size = (int)internal::bit_ceil((unsigned int)(_n)); + log = internal::countr_zero((unsigned int)size); d = std::vector(2 * size, e()); lz = std::vector(size, id()); for (int i = 0; i < _n; i++) d[size + i] = v[i]; diff --git a/atcoder/segtree.hpp b/atcoder/segtree.hpp index d07018b..c8bfef6 100644 --- a/atcoder/segtree.hpp +++ b/atcoder/segtree.hpp @@ -14,8 +14,8 @@ template struct segtree { segtree() : segtree(0) {} explicit segtree(int n) : segtree(std::vector(n, e())) {} explicit segtree(const std::vector& v) : _n(int(v.size())) { - log = internal::ceil_pow2(_n); - size = 1 << log; + size = (int)internal::bit_ceil((unsigned int)(_n)); + log = internal::countr_zero((unsigned int)size); d = std::vector(2 * size, e()); for (int i = 0; i < _n; i++) d[size + i] = v[i]; for (int i = size - 1; i >= 1; i--) { diff --git a/test/unittest/bit_test.cpp b/test/unittest/bit_test.cpp index 18e38f4..5df162e 100644 --- a/test/unittest/bit_test.cpp +++ b/test/unittest/bit_test.cpp @@ -8,34 +8,51 @@ using namespace atcoder; using ll = long long; using ull = unsigned long long; -TEST(BitTest, CeilPow2) { - ASSERT_EQ(0, internal::ceil_pow2(0)); - ASSERT_EQ(0, internal::ceil_pow2(1)); - ASSERT_EQ(1, internal::ceil_pow2(2)); - ASSERT_EQ(2, internal::ceil_pow2(3)); - ASSERT_EQ(2, internal::ceil_pow2(4)); - ASSERT_EQ(3, internal::ceil_pow2(5)); - ASSERT_EQ(3, internal::ceil_pow2(6)); - ASSERT_EQ(3, internal::ceil_pow2(7)); - ASSERT_EQ(3, internal::ceil_pow2(8)); - ASSERT_EQ(4, internal::ceil_pow2(9)); - ASSERT_EQ(30, internal::ceil_pow2(1 << 30)); - ASSERT_EQ(31, internal::ceil_pow2((1 << 30) + 1)); - ASSERT_EQ(31, internal::ceil_pow2(std::numeric_limits::max())); +TEST(BitTest, BitCeil) { + ASSERT_EQ(1, internal::bit_ceil(0U)); + ASSERT_EQ(1, internal::bit_ceil(1U)); + ASSERT_EQ(2, internal::bit_ceil(2U)); + ASSERT_EQ(4, internal::bit_ceil(3U)); + ASSERT_EQ(4, internal::bit_ceil(4U)); + ASSERT_EQ(8, internal::bit_ceil(5U)); + ASSERT_EQ(8, internal::bit_ceil(6U)); + ASSERT_EQ(8, internal::bit_ceil(7U)); + ASSERT_EQ(8, internal::bit_ceil(8U)); + ASSERT_EQ(16, internal::bit_ceil(9U)); + ASSERT_EQ(1U<<30, internal::bit_ceil(1U << 30)); + ASSERT_EQ(1U<<31, internal::bit_ceil((1U << 30) + 1)); + ASSERT_EQ(1U<<31, internal::bit_ceil((1U << 31) - 1)); } -TEST(BitTest, BSF) { - ASSERT_EQ(0, internal::bsf(1)); - ASSERT_EQ(1, internal::bsf(2)); - ASSERT_EQ(0, internal::bsf(3)); - ASSERT_EQ(2, internal::bsf(4)); - ASSERT_EQ(0, internal::bsf(5)); - ASSERT_EQ(1, internal::bsf(6)); - ASSERT_EQ(0, internal::bsf(7)); - ASSERT_EQ(3, internal::bsf(8)); - ASSERT_EQ(0, internal::bsf(9)); - ASSERT_EQ(30, internal::bsf(1U << 30)); - ASSERT_EQ(0, internal::bsf((1U << 31) - 1)); - ASSERT_EQ(31, internal::bsf(1U << 31)); - ASSERT_EQ(0, internal::bsf(std::numeric_limits::max())); +TEST(BitTest, CountrZero) { + ASSERT_EQ(0, internal::countr_zero(1U)); + ASSERT_EQ(1, internal::countr_zero(2U)); + ASSERT_EQ(0, internal::countr_zero(3U)); + ASSERT_EQ(2, internal::countr_zero(4U)); + ASSERT_EQ(0, internal::countr_zero(5U)); + ASSERT_EQ(1, internal::countr_zero(6U)); + ASSERT_EQ(0, internal::countr_zero(7U)); + ASSERT_EQ(3, internal::countr_zero(8U)); + ASSERT_EQ(0, internal::countr_zero(9U)); + ASSERT_EQ(30, internal::countr_zero(1U << 30)); + ASSERT_EQ(0, internal::countr_zero((1U << 31) - 1)); + ASSERT_EQ(31, internal::countr_zero(1U << 31)); + ASSERT_EQ(0, internal::countr_zero(std::numeric_limits::max())); +} + +TEST(BitTest, CountrZeroConstexpr) { + ASSERT_EQ(0, internal::countr_zero_constexpr(1U)); + ASSERT_EQ(1, internal::countr_zero_constexpr(2U)); + ASSERT_EQ(0, internal::countr_zero_constexpr(3U)); + ASSERT_EQ(2, internal::countr_zero_constexpr(4U)); + ASSERT_EQ(0, internal::countr_zero_constexpr(5U)); + ASSERT_EQ(1, internal::countr_zero_constexpr(6U)); + ASSERT_EQ(0, internal::countr_zero_constexpr(7U)); + ASSERT_EQ(3, internal::countr_zero_constexpr(8U)); + ASSERT_EQ(0, internal::countr_zero_constexpr(9U)); + ASSERT_EQ(30, internal::countr_zero_constexpr(1U << 30)); + ASSERT_EQ(0, internal::countr_zero_constexpr((1U << 31) - 1)); + ASSERT_EQ(31, internal::countr_zero_constexpr(1U << 31)); + ASSERT_EQ(0, + internal::countr_zero_constexpr(std::numeric_limits::max())); } From 8e59e285f0a6fa7ef3409925005b57a06f469523 Mon Sep 17 00:00:00 2001 From: Kohei Morita Date: Sun, 19 Mar 2023 23:46:23 +0900 Subject: [PATCH 2/4] remove unused header --- atcoder/internal_bit.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/atcoder/internal_bit.hpp b/atcoder/internal_bit.hpp index 05f2b54..541b455 100644 --- a/atcoder/internal_bit.hpp +++ b/atcoder/internal_bit.hpp @@ -5,10 +5,6 @@ #include #endif -#if __cplusplus >= 202002L -#include -#endif - namespace atcoder { namespace internal { From 4121285442683f59a21ce116b005fe527d9dd7d8 Mon Sep 17 00:00:00 2001 From: Kohei Morita Date: Sun, 19 Mar 2023 23:49:01 +0900 Subject: [PATCH 3/4] fix comments --- atcoder/internal_bit.hpp | 5 +++-- test/unittest/bit_test.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/atcoder/internal_bit.hpp b/atcoder/internal_bit.hpp index 541b455..a081dd8 100644 --- a/atcoder/internal_bit.hpp +++ b/atcoder/internal_bit.hpp @@ -9,6 +9,7 @@ namespace atcoder { namespace internal { +// @return same with std::bit::bit_ceil unsigned int bit_ceil(unsigned int n) { unsigned int x = 1; while (x < (unsigned int)(n)) x *= 2; @@ -16,7 +17,7 @@ unsigned int bit_ceil(unsigned int n) { } // @param n `1 <= n` -// @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` +// @return same with std::bit::countr_zero int countr_zero(unsigned int n) { #ifdef _MSC_VER unsigned long index; @@ -28,7 +29,7 @@ int countr_zero(unsigned int n) { } // @param n `1 <= n` -// @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0` +// @return same with std::bit::countr_zero constexpr int countr_zero_constexpr(unsigned int n) { int x = 0; while (!(n & (1 << x))) x++; diff --git a/test/unittest/bit_test.cpp b/test/unittest/bit_test.cpp index 5df162e..0de6918 100644 --- a/test/unittest/bit_test.cpp +++ b/test/unittest/bit_test.cpp @@ -19,9 +19,10 @@ TEST(BitTest, BitCeil) { ASSERT_EQ(8, internal::bit_ceil(7U)); ASSERT_EQ(8, internal::bit_ceil(8U)); ASSERT_EQ(16, internal::bit_ceil(9U)); - ASSERT_EQ(1U<<30, internal::bit_ceil(1U << 30)); - ASSERT_EQ(1U<<31, internal::bit_ceil((1U << 30) + 1)); - ASSERT_EQ(1U<<31, internal::bit_ceil((1U << 31) - 1)); + ASSERT_EQ(1U << 30, internal::bit_ceil(1U << 30)); + ASSERT_EQ(1U << 31, internal::bit_ceil((1U << 30) + 1)); + ASSERT_EQ(1U << 31, internal::bit_ceil((1U << 31) - 1)); + ASSERT_EQ(1U << 31, internal::bit_ceil(1U << 31)); } TEST(BitTest, CountrZero) { From 67a98978e90e4609dcb269beff7be2dc0654b040 Mon Sep 17 00:00:00 2001 From: Kohei Morita Date: Sun, 19 Mar 2023 23:51:43 +0900 Subject: [PATCH 4/4] replace bit_ceil --- atcoder/internal_bit.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/atcoder/internal_bit.hpp b/atcoder/internal_bit.hpp index a081dd8..3b2126d 100644 --- a/atcoder/internal_bit.hpp +++ b/atcoder/internal_bit.hpp @@ -5,10 +5,20 @@ #include #endif +#if __cplusplus >= 202002L +#include +#endif + namespace atcoder { namespace internal { +#if __cplusplus >= 202002L + +using std::bit_ceil; + +#else + // @return same with std::bit::bit_ceil unsigned int bit_ceil(unsigned int n) { unsigned int x = 1; @@ -16,6 +26,8 @@ unsigned int bit_ceil(unsigned int n) { return x; } +#endif + // @param n `1 <= n` // @return same with std::bit::countr_zero int countr_zero(unsigned int n) {