|
| 1 | +// Copyright 2026 Matt Borland |
| 2 | +// Distributed under the Boost Software License, Version 1.0. |
| 3 | +// https://www.boost.org/LICENSE_1_0.txt |
| 4 | + |
| 5 | +#if defined(__GNUC__) && __GNUC__ == 7 && defined(__i386__) |
| 6 | + |
| 7 | +// 32-bit GCC-7 fails with: "error: constexpr loop iteration count exceeds limit of 262144" |
| 8 | + |
| 9 | +int main() { return 0; } |
| 10 | + |
| 11 | +#else |
| 12 | + |
| 13 | +#ifndef BOOST_INT128_BUILD_MODULE |
| 14 | + |
| 15 | +#include <boost/int128.hpp> |
| 16 | + |
| 17 | +#else |
| 18 | + |
| 19 | +import boost.int128; |
| 20 | + |
| 21 | +#endif |
| 22 | + |
| 23 | +#include <boost/core/lightweight_test.hpp> |
| 24 | +#include <cstdint> |
| 25 | +#include <limits> |
| 26 | + |
| 27 | +using namespace boost::int128; |
| 28 | + |
| 29 | +namespace { |
| 30 | + |
| 31 | +// Naive reference implementation used to cross-check the squaring loop. |
| 32 | +template <typename T> |
| 33 | +constexpr T ipow_ref(T base, std::uint64_t exp) noexcept |
| 34 | +{ |
| 35 | + T result {1}; |
| 36 | + |
| 37 | + for (std::uint64_t i {0}; i < exp; ++i) |
| 38 | + { |
| 39 | + result *= base; |
| 40 | + } |
| 41 | + |
| 42 | + return result; |
| 43 | +} |
| 44 | + |
| 45 | +} // namespace |
| 46 | + |
| 47 | +void test_uint128_ipow_basic() |
| 48 | +{ |
| 49 | + BOOST_TEST_EQ(ipow(uint128_t{0}, 0U), uint128_t{1}); |
| 50 | + BOOST_TEST_EQ(ipow(uint128_t{1}, 0U), uint128_t{1}); |
| 51 | + BOOST_TEST_EQ(ipow(uint128_t{42}, 0U), uint128_t{1}); |
| 52 | + BOOST_TEST_EQ(ipow(uint128_t{0}, 1U), uint128_t{0}); |
| 53 | + BOOST_TEST_EQ(ipow(uint128_t{0}, 5U), uint128_t{0}); |
| 54 | + BOOST_TEST_EQ(ipow(uint128_t{1}, 1000U), uint128_t{1}); |
| 55 | + BOOST_TEST_EQ(ipow(uint128_t{42}, 1U), uint128_t{42}); |
| 56 | + |
| 57 | + BOOST_TEST_EQ(ipow(uint128_t{2}, 0U), uint128_t{1}); |
| 58 | + BOOST_TEST_EQ(ipow(uint128_t{2}, 1U), uint128_t{2}); |
| 59 | + BOOST_TEST_EQ(ipow(uint128_t{2}, 2U), uint128_t{4}); |
| 60 | + BOOST_TEST_EQ(ipow(uint128_t{2}, 10U), uint128_t{1024}); |
| 61 | + BOOST_TEST_EQ(ipow(uint128_t{3}, 5U), uint128_t{243}); |
| 62 | + BOOST_TEST_EQ(ipow(uint128_t{10}, 9U), uint128_t{UINT64_C(1000000000)}); |
| 63 | + BOOST_TEST_EQ(ipow(uint128_t{10}, 18U), uint128_t{UINT64_C(1000000000000000000)}); |
| 64 | +} |
| 65 | + |
| 66 | +void test_uint128_ipow_power_of_two() |
| 67 | +{ |
| 68 | + // 2^k fills bit k, so we can hit every bit position up to 127. |
| 69 | + for (std::uint64_t k {0}; k < 64; ++k) |
| 70 | + { |
| 71 | + const uint128_t expected {static_cast<std::uint64_t>(1) << k}; |
| 72 | + BOOST_TEST_EQ(ipow(uint128_t{2}, k), expected); |
| 73 | + } |
| 74 | + |
| 75 | + for (std::uint64_t k {64}; k < 128; ++k) |
| 76 | + { |
| 77 | + const uint128_t expected {static_cast<std::uint64_t>(1) << (k - 64), 0U}; |
| 78 | + BOOST_TEST_EQ(ipow(uint128_t{2}, k), expected); |
| 79 | + } |
| 80 | + |
| 81 | + // 2^128 wraps to 0 in uint128 arithmetic. |
| 82 | + BOOST_TEST_EQ(ipow(uint128_t{2}, 128U), uint128_t{0}); |
| 83 | + BOOST_TEST_EQ(ipow(uint128_t{2}, 200U), uint128_t{0}); |
| 84 | +} |
| 85 | + |
| 86 | +void test_uint128_ipow_large() |
| 87 | +{ |
| 88 | + // 10^38 is the largest power of 10 that fits in 128 bits. |
| 89 | + // 10^38 = 100000000000000000000000000000000000000. |
| 90 | + const uint128_t ten_pow_38 {UINT64_C(0x4B3B4CA85A86C47A), UINT64_C(0x098A224000000000)}; |
| 91 | + BOOST_TEST_EQ(ipow(uint128_t{10}, 38U), ten_pow_38); |
| 92 | + |
| 93 | + // Cross-check a range of bases against the naive reference for small |
| 94 | + // exponents where the result is hand-verifiable through repeated mul. |
| 95 | + for (std::uint64_t base {2}; base < 8; ++base) |
| 96 | + { |
| 97 | + for (std::uint64_t exp {0}; exp < 12; ++exp) |
| 98 | + { |
| 99 | + BOOST_TEST_EQ(ipow(uint128_t{base}, exp), ipow_ref(uint128_t{base}, exp)); |
| 100 | + } |
| 101 | + } |
| 102 | +} |
| 103 | + |
| 104 | +void test_uint128_ipow_wrap() |
| 105 | +{ |
| 106 | + // Squaring 2^64 yields 2^128 which wraps to 0. |
| 107 | + const uint128_t two_pow_64 {1U, 0U}; |
| 108 | + BOOST_TEST_EQ(ipow(two_pow_64, 2U), uint128_t{0}); |
| 109 | + |
| 110 | + // (2^64 - 1)^2 mod 2^128 = 2^128 - 2^65 + 1, which has a known bit pattern. |
| 111 | + const uint128_t u64_max {(std::numeric_limits<std::uint64_t>::max)()}; |
| 112 | + const uint128_t expected {UINT64_C(0xFFFFFFFFFFFFFFFE), 1U}; |
| 113 | + BOOST_TEST_EQ(ipow(u64_max, 2U), expected); |
| 114 | + |
| 115 | + // Anything to a sufficiently large power eventually wraps to 0 if the base |
| 116 | + // shares a factor of 2 with 2^128. |
| 117 | + BOOST_TEST_EQ(ipow(uint128_t{4}, 64U), uint128_t{0}); |
| 118 | + BOOST_TEST_EQ(ipow(uint128_t{6}, 200U), uint128_t{0}); |
| 119 | +} |
| 120 | + |
| 121 | +void test_uint128_ipow_identities() |
| 122 | +{ |
| 123 | + // a^(b+c) == a^b * a^c (under wrap modulo 2^128). |
| 124 | + const uint128_t a {UINT64_C(0xDEADBEEF)}; |
| 125 | + BOOST_TEST_EQ(ipow(a, 7U), ipow(a, 3U) * ipow(a, 4U)); |
| 126 | + BOOST_TEST_EQ(ipow(a, 20U), ipow(a, 13U) * ipow(a, 7U)); |
| 127 | + |
| 128 | + // (a*b)^e == a^e * b^e. |
| 129 | + const uint128_t aa {7}; |
| 130 | + const uint128_t bb {11}; |
| 131 | + BOOST_TEST_EQ(ipow(aa * bb, 6U), ipow(aa, 6U) * ipow(bb, 6U)); |
| 132 | + |
| 133 | + // (a^b)^c == a^(b*c). |
| 134 | + BOOST_TEST_EQ(ipow(ipow(uint128_t{3}, 4U), 5U), ipow(uint128_t{3}, 4U * 5U)); |
| 135 | +} |
| 136 | + |
| 137 | +void test_int128_ipow_basic() |
| 138 | +{ |
| 139 | + BOOST_TEST_EQ(ipow(int128_t{0}, 0U), int128_t{1}); |
| 140 | + BOOST_TEST_EQ(ipow(int128_t{1}, 0U), int128_t{1}); |
| 141 | + BOOST_TEST_EQ(ipow(int128_t{-1}, 0U), int128_t{1}); |
| 142 | + BOOST_TEST_EQ(ipow(int128_t{0}, 5U), int128_t{0}); |
| 143 | + BOOST_TEST_EQ(ipow(int128_t{42}, 1U), int128_t{42}); |
| 144 | + |
| 145 | + BOOST_TEST_EQ(ipow(int128_t{2}, 10U), int128_t{1024}); |
| 146 | + BOOST_TEST_EQ(ipow(int128_t{3}, 5U), int128_t{243}); |
| 147 | + BOOST_TEST_EQ(ipow(int128_t{10}, 18U), int128_t{INT64_C(1000000000000000000)}); |
| 148 | +} |
| 149 | + |
| 150 | +void test_int128_ipow_negative_base() |
| 151 | +{ |
| 152 | + // Even exponents are non-negative, odd exponents preserve the sign. |
| 153 | + BOOST_TEST_EQ(ipow(int128_t{-2}, 0U), int128_t{1}); |
| 154 | + BOOST_TEST_EQ(ipow(int128_t{-2}, 1U), int128_t{-2}); |
| 155 | + BOOST_TEST_EQ(ipow(int128_t{-2}, 2U), int128_t{4}); |
| 156 | + BOOST_TEST_EQ(ipow(int128_t{-2}, 3U), int128_t{-8}); |
| 157 | + BOOST_TEST_EQ(ipow(int128_t{-2}, 10U), int128_t{1024}); |
| 158 | + BOOST_TEST_EQ(ipow(int128_t{-3}, 5U), int128_t{-243}); |
| 159 | + |
| 160 | + BOOST_TEST_EQ(ipow(int128_t{-1}, 100U), int128_t{1}); |
| 161 | + BOOST_TEST_EQ(ipow(int128_t{-1}, 101U), int128_t{-1}); |
| 162 | + |
| 163 | + BOOST_TEST_EQ(ipow(int128_t{-10}, 18U), int128_t{INT64_C(1000000000000000000)}); |
| 164 | + BOOST_TEST_EQ(ipow(int128_t{-10}, 17U), int128_t{INT64_C(-100000000000000000)}); |
| 165 | +} |
| 166 | + |
| 167 | +void test_int128_ipow_large() |
| 168 | +{ |
| 169 | + // 10^38 still fits in int128_t (signed max is roughly 1.7e38). |
| 170 | + const int128_t ten_pow_38 {static_cast<int128_t>(uint128_t{UINT64_C(0x4B3B4CA85A86C47A), UINT64_C(0x098A224000000000)})}; |
| 171 | + BOOST_TEST_EQ(ipow(int128_t{10}, 38U), ten_pow_38); |
| 172 | + BOOST_TEST_EQ(ipow(int128_t{-10}, 38U), ten_pow_38); |
| 173 | + |
| 174 | + // Cross-check small bases against the naive reference. |
| 175 | + for (std::int64_t base {-7}; base < 8; ++base) |
| 176 | + { |
| 177 | + for (std::uint64_t exp {0}; exp < 12; ++exp) |
| 178 | + { |
| 179 | + BOOST_TEST_EQ(ipow(int128_t{base}, exp), ipow_ref(int128_t{base}, exp)); |
| 180 | + } |
| 181 | + } |
| 182 | +} |
| 183 | + |
| 184 | +void test_int128_ipow_identities() |
| 185 | +{ |
| 186 | + const int128_t a {12345}; |
| 187 | + BOOST_TEST_EQ(ipow(a, 7U), ipow(a, 3U) * ipow(a, 4U)); |
| 188 | + BOOST_TEST_EQ(ipow(ipow(int128_t{3}, 4U), 5U), ipow(int128_t{3}, 4U * 5U)); |
| 189 | + |
| 190 | + // Sign behaves multiplicatively. |
| 191 | + BOOST_TEST_EQ(ipow(int128_t{-7}, 3U) * ipow(int128_t{-7}, 4U), ipow(int128_t{-7}, 7U)); |
| 192 | +} |
| 193 | + |
| 194 | +#ifdef _MSC_VER |
| 195 | +# pragma warning(push) |
| 196 | +# pragma warning(disable : 4307) // integral constant overflow |
| 197 | +# pragma warning(disable : 4308) // negative integral constant converted to unsigned type |
| 198 | +#endif |
| 199 | + |
| 200 | +void test_constexpr_ipow() |
| 201 | +{ |
| 202 | + constexpr uint128_t r1 {ipow(uint128_t{2}, 10U)}; |
| 203 | + static_assert(r1 == uint128_t{1024}, "ipow constexpr uint128 small case"); |
| 204 | + |
| 205 | + constexpr uint128_t r2 {ipow(uint128_t{10}, 18U)}; |
| 206 | + static_assert(r2 == uint128_t{UINT64_C(1000000000000000000)}, "ipow constexpr uint128 18 digits"); |
| 207 | + |
| 208 | + constexpr int128_t r3 {ipow(int128_t{-3}, 5U)}; |
| 209 | + static_assert(r3 == int128_t{-243}, "ipow constexpr int128 negative base odd exp"); |
| 210 | + |
| 211 | + constexpr int128_t r4 {ipow(int128_t{-3}, 4U)}; |
| 212 | + static_assert(r4 == int128_t{81}, "ipow constexpr int128 negative base even exp"); |
| 213 | + |
| 214 | + constexpr uint128_t r5 {ipow(uint128_t{2}, 128U)}; |
| 215 | + static_assert(r5 == uint128_t{0}, "ipow constexpr uint128 wrap to zero"); |
| 216 | +} |
| 217 | + |
| 218 | +#ifdef _MSC_VER |
| 219 | +# pragma warning(pop) |
| 220 | +#endif |
| 221 | + |
| 222 | +int main() |
| 223 | +{ |
| 224 | + test_uint128_ipow_basic(); |
| 225 | + test_uint128_ipow_power_of_two(); |
| 226 | + test_uint128_ipow_large(); |
| 227 | + test_uint128_ipow_wrap(); |
| 228 | + test_uint128_ipow_identities(); |
| 229 | + test_int128_ipow_basic(); |
| 230 | + test_int128_ipow_negative_base(); |
| 231 | + test_int128_ipow_large(); |
| 232 | + test_int128_ipow_identities(); |
| 233 | + test_constexpr_ipow(); |
| 234 | + |
| 235 | + return boost::report_errors(); |
| 236 | +} |
| 237 | + |
| 238 | +#endif |
0 commit comments