diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_uint.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_uint.hpp index f2b079972..dd0e02ec3 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_uint.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/big_uint.hpp @@ -12,6 +12,7 @@ #include "nil/crypto3/multiprecision/detail/big_uint/limits.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/detail/big_uint/ops/gcd_inverse.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/detail/big_uint/ops/jacobi.hpp" // IWYU pragma: export +#include "nil/crypto3/multiprecision/detail/big_uint/ops/pow.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/detail/big_uint/ops/powm.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/detail/big_uint/ops/ressol.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/detail/big_uint/ops/wnaf.hpp" // IWYU pragma: export diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/big_uint_impl.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/big_uint_impl.hpp index 8b22423e4..cc0ca4105 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/big_uint_impl.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/big_uint_impl.hpp @@ -42,6 +42,7 @@ #include "nil/crypto3/multiprecision/detail/big_uint/type_traits.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/detail/config.hpp" #include "nil/crypto3/multiprecision/detail/endian.hpp" +#include "nil/crypto3/multiprecision/detail/integer_utils.hpp" namespace nil::crypto3::multiprecision { /** @@ -143,13 +144,12 @@ namespace nil::crypto3::multiprecision { this->normalize(); } - template && std::is_unsigned_v, int> = 0> + template, int> = 0> constexpr void do_assign_integral(const T& a) { - do_assign_integral_unchecked(a); + do_assign_integral_unchecked(detail::unsigned_or_throw(a)); if constexpr (sizeof(T) * CHAR_BIT > Bits) { - if (big_uint(a).compare(*this) != 0) { - throw std::range_error("big_uint: out of range"); + if (compare(a) != 0) { + throw std::range_error("big_uint: overflow"); } } } @@ -171,7 +171,7 @@ namespace nil::crypto3::multiprecision { do_assign_unchecked(other); if constexpr (Bits2 > Bits) { if (other.compare(*this) != 0) { - throw std::range_error("big_uint: out of range"); + throw std::range_error("big_uint: overflow"); } } } @@ -185,17 +185,9 @@ namespace nil::crypto3::multiprecision { constexpr big_uint(const char* str) { *this = str; } constexpr big_uint(const std::string& str) { *this = str; } - template && std::is_signed_v, int> = 0> - constexpr big_uint(T val) { - if (val < 0) { - throw std::range_error("big_uint: assignment from negative integer"); - } - do_assign_integral(static_cast>(val)); - } - - template && std::is_unsigned_v, int> = 0> - constexpr big_uint(T val) { - do_assign_integral(val); + template, int> = 0> + constexpr big_uint(T a) { + do_assign_integral(a); } // TODO(ioxid): make this explicit for the case when Bits2 > Bits @@ -224,20 +216,9 @@ namespace nil::crypto3::multiprecision { return *this; } - template && std::is_signed_v, int> = 0> - constexpr big_uint& operator=(T val) { - if (val < 0) { - throw std::range_error("big_uint: assignment from negative integer"); - } - do_assign_integral(static_cast>(val)); - return *this; - } - - template && std::is_unsigned_v, int> = 0> - constexpr big_uint& operator=(T val) noexcept { - do_assign_integral(val); + template, int> = 0> + constexpr big_uint& operator=(T a) { + do_assign_integral(a); return *this; } @@ -269,26 +250,23 @@ namespace nil::crypto3::multiprecision { // String conversion - constexpr std::string str(std::ios_base::fmtflags flags = std::ios_base::hex | - std::ios_base::showbase | - std::ios_base::uppercase) const { - if (flags & std::ios_base::dec) { - // TODO(ioxid): this is inefficient - std::string result; - auto copy = *this; - while (!copy.is_zero()) { - result += static_cast(static_cast(copy % 10u) + '0'); - copy /= 10u; - } - std::reverse(result.begin(), result.end()); - if (result.empty()) { - result += '0'; - } - return result; + private: + constexpr std::string decimal_str() const { + // TODO(ioxid): this is inefficient + std::string result; + auto copy = *this; + while (!copy.is_zero()) { + result += static_cast(static_cast(copy % 10u) + '0'); + copy /= 10u; } - if (!(flags & std::ios_base::hex)) { - throw std::invalid_argument("big_uint: unsupported format flags"); + std::reverse(result.begin(), result.end()); + if (result.empty()) { + result += '0'; } + return result; + } + + constexpr std::string hex_str() const { std::string result; result.reserve(used_limbs() * limb_bits / 4); bool found_first = false; @@ -313,15 +291,29 @@ namespace nil::crypto3::multiprecision { BOOST_ASSERT(ec == std::errc{}); } } + if (result.empty()) { + result += '0'; + } + return result; + } + + public: + constexpr std::string str(std::ios_base::fmtflags flags = std::ios_base::hex | + std::ios_base::showbase | + std::ios_base::uppercase) const { + if (flags & std::ios_base::dec) { + return decimal_str(); + } + if (!(flags & std::ios_base::hex)) { + throw std::invalid_argument("big_uint: unsupported format flags"); + } + auto result = hex_str(); if (flags & std::ios_base::uppercase) { for (std::size_t i = 0; i < result.size(); ++i) { result[i] = static_cast(std::toupper(static_cast(result[i]))); } } - if (result.size() == 0) { - result += '0'; - } if (flags & std::ios_base::showbase) { result = "0x" + result; } @@ -341,24 +333,37 @@ namespace nil::crypto3::multiprecision { std::is_unsigned_v, int> = 0> explicit constexpr operator T() const { + T result; if constexpr (sizeof(T) <= sizeof(limb_type)) { - return static_cast(this->limbs()[0]); + result = static_cast(this->limbs()[0]); } else { + static_assert(sizeof(T) % sizeof(limb_type) == 0); constexpr std::size_t n = std::min(sizeof(T) / sizeof(limb_type), static_limb_count); - T result = 0; + result = 0; for (std::size_t i = 0; i < n; ++i) { result <<= limb_bits; result |= limbs()[n - i - 1]; } - return result; } + if constexpr (sizeof(T) * CHAR_BIT < Bits) { + if (compare(result) != 0) { + throw std::overflow_error("big_uint: overflow"); + } + } + return result; } template && std::is_signed_v, int> = 0> explicit constexpr operator T() const { - return static_cast(static_cast>(*this)); + T result = static_cast(static_cast>(*this)); + if constexpr (sizeof(T) * CHAR_BIT <= Bits) { + if (compare(result) != 0) { + throw std::overflow_error("big_uint: overflow"); + } + } + return result; } explicit constexpr operator bool() const { return !is_zero(); } @@ -420,8 +425,8 @@ namespace nil::crypto3::multiprecision { #define NIL_CO3_MP_BIG_UINT_IMPL_COMPARISON_OPERATOR(OP_) \ template, int> = 0> \ - constexpr bool operator OP_(const T& o) const noexcept { \ - return compare(o) OP_ 0; \ + friend constexpr bool operator OP_(const big_uint& a, const T& b) noexcept { \ + return a.compare(b) OP_ 0; \ } \ \ template, int> = 0> \ @@ -735,18 +740,18 @@ namespace nil::crypto3::multiprecision { } template - NIL_CO3_MP_FORCEINLINE constexpr void bitwise_and(const big_uint& o) noexcept { - bitwise_op(o, std::bit_and()); + NIL_CO3_MP_FORCEINLINE constexpr void bitwise_and(const big_uint& other) noexcept { + bitwise_op(other, std::bit_and()); } template - NIL_CO3_MP_FORCEINLINE constexpr void bitwise_or(const big_uint& o) noexcept { - bitwise_op(o, std::bit_or()); + NIL_CO3_MP_FORCEINLINE constexpr void bitwise_or(const big_uint& other) noexcept { + bitwise_op(other, std::bit_or()); } template - NIL_CO3_MP_FORCEINLINE constexpr void bitwise_xor(const big_uint& o) noexcept { - bitwise_op(o, std::bit_xor()); + NIL_CO3_MP_FORCEINLINE constexpr void bitwise_xor(const big_uint& other) noexcept { + bitwise_op(other, std::bit_xor()); } // @@ -762,10 +767,10 @@ namespace nil::crypto3::multiprecision { NIL_CO3_MP_FORCEINLINE constexpr void bitwise_xor(limb_type l) noexcept { limbs()[0] ^= l; } - NIL_CO3_MP_FORCEINLINE constexpr void complement(const big_uint& o) noexcept { - std::size_t os = o.limb_count(); + NIL_CO3_MP_FORCEINLINE constexpr void complement(const big_uint& other) noexcept { + std::size_t os = other.limb_count(); for (std::size_t i = 0; i < os; ++i) { - limbs()[i] = ~o.limbs()[i]; + limbs()[i] = ~other.limbs()[i]; } normalize(); } @@ -925,10 +930,13 @@ namespace nil::crypto3::multiprecision { } // Shifting left throws away upper Bits. - constexpr big_uint& operator<<=(double_limb_type s) noexcept { - if (!s) { + template + constexpr big_uint& operator<<=(T s_original) { + static_assert(std::is_integral_v); + if (!s_original) { return *this; } + auto s = detail::unsigned_or_throw(s_original); #if NIL_CO3_MP_ENDIAN_LITTLE_BYTE && defined(NIL_CO3_MP_USE_LIMB_SHIFT) constexpr limb_type limb_shift_mask = limb_bits - 1; @@ -962,16 +970,20 @@ namespace nil::crypto3::multiprecision { return *this; } - constexpr big_uint operator<<(double_limb_type s) const noexcept { + template + constexpr big_uint operator<<(T s) const { big_uint result = *this; result <<= s; return result; } - constexpr big_uint& operator>>=(double_limb_type s) noexcept { - if (!s) { + template + constexpr big_uint& operator>>=(T s_original) { + static_assert(std::is_integral_v); + if (!s_original) { return *this; } + auto s = detail::unsigned_or_throw(s_original); #if NIL_CO3_MP_ENDIAN_LITTLE_BYTE && defined(NIL_CO3_MP_USE_LIMB_SHIFT) constexpr limb_type limb_shift_mask = limb_bits - 1; @@ -1004,7 +1016,8 @@ namespace nil::crypto3::multiprecision { return *this; } - constexpr big_uint operator>>(double_limb_type s) const noexcept { + template + constexpr big_uint operator>>(T s) const { big_uint result = *this; result >>= s; return result; @@ -1058,7 +1071,7 @@ namespace nil::crypto3::multiprecision { return static_cast(limbs()[offset] & mask); } - constexpr void bit_set(std::size_t index) { + constexpr big_uint& bit_set(std::size_t index) { if (index >= Bits) { throw std::invalid_argument("fixed precision overflow"); } @@ -1066,9 +1079,10 @@ namespace nil::crypto3::multiprecision { std::size_t shift = index % limb_bits; limb_type mask = limb_type(1u) << shift; limbs()[offset] |= mask; + return *this; } - constexpr void bit_unset(std::size_t index) { + constexpr big_uint& bit_unset(std::size_t index) { if (index >= Bits) { throw std::invalid_argument("fixed precision overflow"); } @@ -1076,9 +1090,10 @@ namespace nil::crypto3::multiprecision { std::size_t shift = index % limb_bits; limb_type mask = limb_type(1u) << shift; limbs()[offset] &= ~mask; + return *this; } - constexpr void bit_flip(std::size_t index) { + constexpr big_uint& bit_flip(std::size_t index) { if (index >= Bits) { throw std::invalid_argument("fixed precision overflow"); } @@ -1086,6 +1101,7 @@ namespace nil::crypto3::multiprecision { std::size_t shift = index % limb_bits; limb_type mask = limb_type(1u) << shift; limbs()[offset] ^= mask; + return *this; } // Import / export @@ -1238,21 +1254,28 @@ namespace nil::crypto3::multiprecision { // Hash - friend constexpr std::size_t hash_value(const big_uint& val) noexcept { + friend constexpr std::size_t hash_value(const big_uint& a) noexcept { std::size_t result = 0; - for (std::size_t i = 0; i < val.limb_count(); ++i) { - boost::hash_combine(result, val.limbs()[i]); + for (std::size_t i = 0; i < a.limb_count(); ++i) { + boost::hash_combine(result, a.limbs()[i]); } return result; } // IO - friend constexpr std::ostream& operator<<(std::ostream& os, const big_uint& value) { - os << value.str(os.flags()); + friend constexpr std::ostream& operator<<(std::ostream& os, const big_uint& a) { + os << a.str(os.flags()); return os; } + friend constexpr std::istream& operator>>(std::istream& is, big_uint& a) { + std::string s; + is >> s; + a = s; + return is; + } + private: // Data @@ -1329,6 +1352,21 @@ namespace nil::crypto3::multiprecision { return a.bit_test(index); } + template + constexpr big_uint& bit_set(big_uint& a, std::size_t index) { + return a.bit_set(index); + } + + template + constexpr big_uint& bit_unset(big_uint& a, std::size_t index) { + return a.bit_unset(index); + } + + template + constexpr big_uint& bit_flip(big_uint& a, std::size_t index) { + return a.bit_flip(index); + } + template constexpr bool is_zero(const big_uint& a) { return a.is_zero(); diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/ops/pow.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/ops/pow.hpp new file mode 100644 index 000000000..31d406eb1 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/ops/pow.hpp @@ -0,0 +1,47 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2019-2021 Aleksei Moskvin +// Copyright (c) 2020 Mikhail Komarov +// Copyright (c) 2020 Ilias Khairullin +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +// IWYU pragma: private; include "nil/crypto3/multiprecision/big_uint.hpp" + +#include + +#include "nil/crypto3/multiprecision/detail/big_uint/big_uint_impl.hpp" +#include "nil/crypto3/multiprecision/detail/integer_ops_base.hpp" + +namespace nil::crypto3::multiprecision { + template> && + detail::is_integral_v>, + int> = 0> + constexpr std::decay_t pow(T1 b, T2 e) { + if (is_zero(e)) { + return 1u; + } + + T1 res = 1u; + + while (true) { + bool lsb = bit_test(e, 0u); + e >>= 1u; + if (lsb) { + res *= b; + if (is_zero(e)) { + break; + } + } + b *= b; + } + + return res; + } +} // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/ops/powm.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/ops/powm.hpp index 497200b64..20a9a868a 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/ops/powm.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/big_uint/ops/powm.hpp @@ -13,7 +13,6 @@ // IWYU pragma: private; include "nil/crypto3/multiprecision/big_uint.hpp" -#include #include #include "nil/crypto3/multiprecision/detail/big_mod/big_mod_impl.hpp" @@ -21,11 +20,15 @@ #include "nil/crypto3/multiprecision/detail/big_uint/big_uint_impl.hpp" namespace nil::crypto3::multiprecision { - template> && - detail::is_integral_v>, + detail::is_integral_v> && + detail::is_integral_v>, int> = 0> - constexpr big_uint powm(T1 &&b, T2 &&e, const big_uint &m) { - return pow(big_mod_rt(std::forward(b), m), std::forward(e)).base(); + constexpr std::decay_t powm(T1 &&b, T2 &&e, T3 &&m) { + using big_mod_t = big_mod_rt< + std::decay_t::Bits>; + return static_cast>( + pow(big_mod_t(std::forward(b), std::forward(m)), std::forward(e)).base()); } } // namespace nil::crypto3::multiprecision diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_base.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_base.hpp index 4350eb45c..58bc308e3 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_base.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_base.hpp @@ -32,6 +32,27 @@ namespace nil::crypto3::multiprecision { return detail::as_big_uint(detail::unsigned_or_throw(a)).bit_test(index); } + template, int> = 0> + constexpr T &bit_set(T &a, std::size_t index) { + // TODO(ioxid): optimize + a = detail::as_big_uint(detail::unsigned_or_throw(a)).bit_set(index); + return a; + } + + template, int> = 0> + constexpr T &bit_unset(T &a, std::size_t index) { + // TODO(ioxid): optimize + a = detail::as_big_uint(detail::unsigned_or_throw(a)).bit_unset(index); + return a; + } + + template, int> = 0> + constexpr T &bit_flip(T &a, std::size_t index) { + // TODO(ioxid): optimize + a = detail::as_big_uint(detail::unsigned_or_throw(a)).bit_flip(index); + return a; + } + template, int> = 0> constexpr bool is_zero(T a) { return a == 0; diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_pow.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_pow.hpp new file mode 100644 index 000000000..2ba1d08f2 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_pow.hpp @@ -0,0 +1,11 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#pragma once + +#include "nil/crypto3/multiprecision/detail/big_uint/ops/pow.hpp" // IWYU pragma: export diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_powm.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_powm.hpp index 529c5d44c..2dec67fec 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_powm.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/integer_ops_powm.hpp @@ -8,21 +8,5 @@ #pragma once -#include -#include - -#include "nil/crypto3/multiprecision/detail/big_uint/ops/powm.hpp" - -namespace nil::crypto3::multiprecision { - template>::is_integer && - std::numeric_limits>::is_integer && - std::is_integral_v, - int> = 0> - constexpr T3 powm(T1 &&b, T2 &&e, T3 m) { - // TODO(ioxid): optimize - return static_cast( - nil::crypto3::multiprecision::powm(std::forward(b), std::forward(e), - detail::as_big_uint(detail::unsigned_or_throw(m)))); - } -} // namespace nil::crypto3::multiprecision +// Reusing big_uint's implementation. TODO(ioxid): optimize for builtin types +#include "nil/crypto3/multiprecision/detail/big_uint/ops/powm.hpp" // IWYU pragma: export diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/integer.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/integer.hpp index c8bc9046e..ae697092d 100644 --- a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/integer.hpp +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/integer.hpp @@ -9,4 +9,5 @@ #pragma once #include "nil/crypto3/multiprecision/detail/integer_ops_base.hpp" // IWYU pragma: export +#include "nil/crypto3/multiprecision/detail/integer_ops_pow.hpp" // IWYU pragma: export #include "nil/crypto3/multiprecision/detail/integer_ops_powm.hpp" // IWYU pragma: export diff --git a/crypto3/libs/multiprecision/test/CMakeLists.txt b/crypto3/libs/multiprecision/test/CMakeLists.txt index 67f16d3fe..03d66d8e0 100644 --- a/crypto3/libs/multiprecision/test/CMakeLists.txt +++ b/crypto3/libs/multiprecision/test/CMakeLists.txt @@ -46,6 +46,7 @@ set(MULTIPRECISION_TESTS_NAMES "big_mod_randomized" "big_uint_comparison_randomized" "big_uint" + "boost_arithmetic_test" "inverse" "jacobi" "miller_rabin" diff --git a/crypto3/libs/multiprecision/test/big_uint.cpp b/crypto3/libs/multiprecision/test/big_uint.cpp index 05f9010be..aef6be253 100644 --- a/crypto3/libs/multiprecision/test/big_uint.cpp +++ b/crypto3/libs/multiprecision/test/big_uint.cpp @@ -254,8 +254,35 @@ using int_types = std::tuple>; +using unsigned_builtin_types = + std::tuple; + using signed_types = std::tuple; +BOOST_AUTO_TEST_SUITE(assignment) + +BOOST_AUTO_TEST_CASE_TEMPLATE(assignment_signed, T, signed_types) { + BOOST_CHECK_EQUAL(big_uint<7>(static_cast(2)), 2_big_uint7); + BOOST_CHECK_THROW(big_uint<7>(static_cast(-1)), std::range_error); + BOOST_CHECK_THROW(big_uint<7>(static_cast(128)), std::range_error); + BOOST_CHECK_THROW(big_uint<7>(static_cast(129)), std::range_error); + big_uint<7> n; + BOOST_CHECK_EQUAL(n = static_cast(2), 2_big_uint7); + BOOST_CHECK_THROW(n = static_cast(-1), std::range_error); + BOOST_CHECK_THROW(n = static_cast(128), std::range_error); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(assignment_unsigned, T, unsigned_builtin_types) { + BOOST_CHECK_EQUAL(big_uint<7>(static_cast(2)), 2_big_uint7); + BOOST_CHECK_THROW(big_uint<7>(static_cast(128)), std::range_error); + BOOST_CHECK_THROW(big_uint<7>(static_cast(129)), std::range_error); + big_uint<7> n; + BOOST_CHECK_EQUAL(n = static_cast(2), 2_big_uint7); + BOOST_CHECK_THROW(n = static_cast(128), std::range_error); +} + +BOOST_AUTO_TEST_SUITE_END() + BOOST_AUTO_TEST_SUITE(checked_operations) BOOST_AUTO_TEST_CASE_TEMPLATE(addition_positive, T, int_types) { @@ -387,6 +414,12 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(multiplication_negative, T, signed_types) { BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_CASE(add_assign_with_carry_test) { + auto n = 122_big_uint7; + BOOST_CHECK_EQUAL(add_assign_with_carry(n, 4_big_uint7), false); + BOOST_CHECK_EQUAL(add_assign_with_carry(n, 4_big_uint7), true); +} + BOOST_AUTO_TEST_SUITE(bit_operations) BOOST_AUTO_TEST_CASE_TEMPLATE(and_positive, T, int_types) { @@ -430,7 +463,7 @@ BOOST_AUTO_TEST_CASE(shift_left) { BOOST_CHECK_EQUAL(21_big_uint7 << 5, 32_big_uint7); BOOST_CHECK_EQUAL(21_big_uint7 << 7, 0_big_uint7); BOOST_CHECK_EQUAL(21_big_uint7 << 0, 21_big_uint7); - BOOST_CHECK_EQUAL(21_big_uint7 << -1, 0_big_uint7); + BOOST_CHECK_THROW(21_big_uint7 << -1, std::range_error); } BOOST_AUTO_TEST_CASE(shift_right) { @@ -439,7 +472,7 @@ BOOST_AUTO_TEST_CASE(shift_right) { BOOST_CHECK_EQUAL(21_big_uint7 >> 5, 0_big_uint7); BOOST_CHECK_EQUAL(21_big_uint7 >> 7, 0_big_uint7); BOOST_CHECK_EQUAL(21_big_uint7 >> 0, 21_big_uint7); - BOOST_CHECK_EQUAL(21_big_uint7 >> -1, 0_big_uint7); + BOOST_CHECK_THROW(21_big_uint7 >> -1, std::range_error); } BOOST_AUTO_TEST_CASE(bit_set) { @@ -492,3 +525,10 @@ BOOST_AUTO_TEST_CASE(lsb) { } BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_CASE(powm_test) { + BOOST_CHECK_EQUAL(powm(2_big_uint7, 4_big_uint7, 5_big_uint7), 1_big_uint7); + BOOST_CHECK_EQUAL(powm(2_big_uint7, 4_big_uint7, 5), 1); + BOOST_CHECK_EQUAL(powm(2_big_uint7, 4, 5_big_uint7), 1_big_uint7); + BOOST_CHECK_EQUAL(powm(2, 4, 5), 1); +} diff --git a/crypto3/libs/multiprecision/test/boost_arithmetic_test.cpp b/crypto3/libs/multiprecision/test/boost_arithmetic_test.cpp new file mode 100644 index 000000000..17d884a25 --- /dev/null +++ b/crypto3/libs/multiprecision/test/boost_arithmetic_test.cpp @@ -0,0 +1,2034 @@ +//---------------------------------------------------------------------------// +// Copyright (c) 2012 John Maddock +// Copyright (c) 2024 Andrey Nefedov +// +// Distributed under the Boost Software License, Version 1.0 +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +//---------------------------------------------------------------------------// + +#define BOOST_TEST_MODULE boost_arithmetic_test + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +template +struct is_twos_complement_integer : public std::integral_constant {}; + +template +struct is_checked_cpp_int : public std::integral_constant {}; + +template +void test_comparisons(val_t, val_t, const std::integral_constant&) {} + +int normalize_compare_result(int r) { return r > 0 ? 1 : r < 0 ? -1 : 0; } + +template +void test_comparisons(val_t a, val_t b, const std::integral_constant&) { + big_uint_t r1(a); + big_uint_t r2(b); + big_uint_t z(1); + + int cr = a < b ? -1 : a > b ? 1 : 0; + + BOOST_CHECK_EQUAL(r1 == r2, a == b); + BOOST_CHECK_EQUAL(r1 != r2, a != b); + BOOST_CHECK_EQUAL(r1 <= r2, a <= b); + BOOST_CHECK_EQUAL(r1 < r2, a < b); + BOOST_CHECK_EQUAL(r1 >= r2, a >= b); + BOOST_CHECK_EQUAL(r1 > r2, a > b); + + BOOST_CHECK_EQUAL(r1 == b, a == b); + BOOST_CHECK_EQUAL(r1 != b, a != b); + BOOST_CHECK_EQUAL(r1 <= b, a <= b); + BOOST_CHECK_EQUAL(r1 < b, a < b); + BOOST_CHECK_EQUAL(r1 >= b, a >= b); + BOOST_CHECK_EQUAL(r1 > b, a > b); + + BOOST_CHECK_EQUAL(a == r2, a == b); + BOOST_CHECK_EQUAL(a != r2, a != b); + BOOST_CHECK_EQUAL(a <= r2, a <= b); + BOOST_CHECK_EQUAL(a < r2, a < b); + BOOST_CHECK_EQUAL(a >= r2, a >= b); + BOOST_CHECK_EQUAL(a > r2, a > b); + + BOOST_CHECK_EQUAL(r1 * z == r2, a == b); + BOOST_CHECK_EQUAL(r1 * z != r2, a != b); + BOOST_CHECK_EQUAL(r1 * z <= r2, a <= b); + BOOST_CHECK_EQUAL(r1 * z < r2, a < b); + BOOST_CHECK_EQUAL(r1 * z >= r2, a >= b); + BOOST_CHECK_EQUAL(r1 * z > r2, a > b); + + BOOST_CHECK_EQUAL(r1 == r2 * z, a == b); + BOOST_CHECK_EQUAL(r1 != r2 * z, a != b); + BOOST_CHECK_EQUAL(r1 <= r2 * z, a <= b); + BOOST_CHECK_EQUAL(r1 < r2 * z, a < b); + BOOST_CHECK_EQUAL(r1 >= r2 * z, a >= b); + BOOST_CHECK_EQUAL(r1 > r2 * z, a > b); + + BOOST_CHECK_EQUAL(r1 * z == r2 * z, a == b); + BOOST_CHECK_EQUAL(r1 * z != r2 * z, a != b); + BOOST_CHECK_EQUAL(r1 * z <= r2 * z, a <= b); + BOOST_CHECK_EQUAL(r1 * z < r2 * z, a < b); + BOOST_CHECK_EQUAL(r1 * z >= r2 * z, a >= b); + BOOST_CHECK_EQUAL(r1 * z > r2 * z, a > b); + + BOOST_CHECK_EQUAL(r1 * z == b, a == b); + BOOST_CHECK_EQUAL(r1 * z != b, a != b); + BOOST_CHECK_EQUAL(r1 * z <= b, a <= b); + BOOST_CHECK_EQUAL(r1 * z < b, a < b); + BOOST_CHECK_EQUAL(r1 * z >= b, a >= b); + BOOST_CHECK_EQUAL(r1 * z > b, a > b); + + BOOST_CHECK_EQUAL(a == r2 * z, a == b); + BOOST_CHECK_EQUAL(a != r2 * z, a != b); + BOOST_CHECK_EQUAL(a <= r2 * z, a <= b); + BOOST_CHECK_EQUAL(a < r2 * z, a < b); + BOOST_CHECK_EQUAL(a >= r2 * z, a >= b); + BOOST_CHECK_EQUAL(a > r2 * z, a > b); + + BOOST_CHECK_EQUAL(normalize_compare_result(r1.compare(r2)), cr); + BOOST_CHECK_EQUAL(normalize_compare_result(r2.compare(r1)), -cr); + BOOST_CHECK_EQUAL(normalize_compare_result(r1.compare(b)), cr); + BOOST_CHECK_EQUAL(normalize_compare_result(r2.compare(a)), -cr); +} + +template +void test_conditional(big_uint_t v, Exp e) { + // + // Verify that Exp is usable in Boolean contexts, and has the same value as v: + // + if (e) { + BOOST_CHECK(v); + } else { + BOOST_CHECK(!v); + } + if (!e) { + BOOST_CHECK(!v); + } else { + BOOST_CHECK(v); + } +} + +template +void test_complement(big_uint_t a, big_uint_t b, big_uint_t c, + const std::integral_constant&) { + int i = 1020304; + int j = 56789123; + int sign_mask = ~0; + if (std::numeric_limits::is_signed) { + BOOST_CHECK_EQUAL(~a, (~i & sign_mask)); + c = a & ~b; + BOOST_CHECK_EQUAL(c, (i & (~j & sign_mask))); + c = ~(a | b); + BOOST_CHECK_EQUAL(c, (~(i | j) & sign_mask)); + } else { + BOOST_CHECK_EQUAL((~a & a), 0); + } +} + +template +void test_complement(big_uint_t, big_uint_t, big_uint_t, + const std::integral_constant&) {} + +template +void test_signed_integer_ops(const std::integral_constant&) { + big_uint_t a(20); + big_uint_t b(7); + big_uint_t c(5); + BOOST_CHECK_EQUAL(-a % c, 0); + BOOST_CHECK_EQUAL(-a % b, -20 % 7); + BOOST_CHECK_EQUAL(-a % -b, -20 % -7); + BOOST_CHECK_EQUAL(a % -b, 20 % -7); + BOOST_CHECK_EQUAL(-a % 7, -20 % 7); + BOOST_CHECK_EQUAL(-a % -7, -20 % -7); + BOOST_CHECK_EQUAL(a % -7, 20 % -7); + BOOST_CHECK_EQUAL(-a % 7u, -20 % 7); + BOOST_CHECK_EQUAL(-a % a, 0); + BOOST_CHECK_EQUAL(-a % 5, 0); + BOOST_CHECK_EQUAL(-a % -5, 0); + BOOST_CHECK_EQUAL(a % -5, 0); + + b = -b; + BOOST_CHECK_EQUAL(a % b, 20 % -7); + a = -a; + BOOST_CHECK_EQUAL(a % b, -20 % -7); + BOOST_CHECK_EQUAL(a % -7, -20 % -7); + b = 7; + BOOST_CHECK_EQUAL(a % b, -20 % 7); + BOOST_CHECK_EQUAL(a % 7, -20 % 7); + BOOST_CHECK_EQUAL(a % 7u, -20 % 7); + + a = 20; + a %= b; + BOOST_CHECK_EQUAL(a, 20 % 7); + a = -20; + a %= b; + BOOST_CHECK_EQUAL(a, -20 % 7); + a = 20; + a %= -b; + BOOST_CHECK_EQUAL(a, 20 % -7); + a = -20; + a %= -b; + BOOST_CHECK_EQUAL(a, -20 % -7); + a = 5; + a %= b - a; + BOOST_CHECK_EQUAL(a, 5 % (7 - 5)); + a = -20; + a %= 7; + BOOST_CHECK_EQUAL(a, -20 % 7); + a = 20; + a %= -7; + BOOST_CHECK_EQUAL(a, 20 % -7); + a = -20; + a %= -7; + BOOST_CHECK_EQUAL(a, -20 % -7); +#ifndef BOOST_NO_LONG_LONG + a = -20; + a %= 7uLL; + BOOST_CHECK_EQUAL(a, -20 % 7); + a = 20; + a %= -7LL; + BOOST_CHECK_EQUAL(a, 20 % -7); + a = -20; + a %= -7LL; + BOOST_CHECK_EQUAL(a, -20 % -7); +#endif + a = 400; + b = 45; + // TODO(ioxid): implement lcm + // BOOST_CHECK_EQUAL(gcd(a, -45), boost::integer::gcd(400, 45)); + // BOOST_CHECK_EQUAL(lcm(a, -45), boost::integer::lcm(400, 45)); + // BOOST_CHECK_EQUAL(gcd(-400, b), boost::integer::gcd(400, 45)); + // BOOST_CHECK_EQUAL(lcm(-400, b), boost::integer::lcm(400, 45)); + a = -20; + BOOST_CHECK_EQUAL(abs(a), 20); + BOOST_CHECK_EQUAL(abs(-a), 20); + BOOST_CHECK_EQUAL(abs(+a), 20); + a = 20; + BOOST_CHECK_EQUAL(abs(a), 20); + BOOST_CHECK_EQUAL(abs(-a), 20); + BOOST_CHECK_EQUAL(abs(+a), 20); + a = -400; + b = 45; + // TODO(ioxid): implement lcm + // BOOST_CHECK_EQUAL(gcd(a, b), boost::integer::gcd(-400, 45)); + // BOOST_CHECK_EQUAL(lcm(a, b), boost::integer::lcm(-400, 45)); + // BOOST_CHECK_EQUAL(gcd(a, 45), boost::integer::gcd(-400, 45)); + // BOOST_CHECK_EQUAL(lcm(a, 45), boost::integer::lcm(-400, 45)); + // BOOST_CHECK_EQUAL(gcd(-400, b), boost::integer::gcd(-400, 45)); + // BOOST_CHECK_EQUAL(lcm(-400, b), boost::integer::lcm(-400, 45)); + big_uint_t r; + divide_qr(a, b, c, r); + BOOST_CHECK_EQUAL(c, a / b); + BOOST_CHECK_EQUAL(r, a % b); + BOOST_CHECK_EQUAL(integer_modulus(a, 57), abs(a % 57)); + b = -57; + divide_qr(a, b, c, r); + BOOST_CHECK_EQUAL(c, a / b); + BOOST_CHECK_EQUAL(r, a % b); + BOOST_CHECK_EQUAL(integer_modulus(a, -57), abs(a % -57)); + a = 458; + divide_qr(a, b, c, r); + BOOST_CHECK_EQUAL(c, a / b); + BOOST_CHECK_EQUAL(r, a % b); + BOOST_CHECK_EQUAL(integer_modulus(a, -57), abs(a % -57)); +#ifndef TEST_CHECKED_INT + if (is_checked_cpp_int::value) { + a = -1; +#ifndef BOOST_NO_EXCEPTIONS + BOOST_CHECK_THROW(a << 2, std::range_error); + BOOST_CHECK_THROW(a >> 2, std::range_error); + BOOST_CHECK_THROW(a <<= 2, std::range_error); + BOOST_CHECK_THROW(a >>= 2, std::range_error); +#endif + } else { + a = -1; + BOOST_CHECK_EQUAL(a << 10, -1024); + a = -23; + BOOST_CHECK_EQUAL(a << 10, -23552); + a = -23456; + BOOST_CHECK_EQUAL(a >> 10, -23); + a = -3; + BOOST_CHECK_EQUAL(a >> 10, -1); + } +#endif +} +template +void test_signed_integer_ops(const std::integral_constant&) {} + +template +inline big_uint_t negate_if_signed(big_uint_t r, const std::integral_constant&) { + return -r; +} +template +inline big_uint_t negate_if_signed(big_uint_t r, const std::integral_constant&) { + return r; +} + +template +void test_integer_overflow() { + if (std::numeric_limits::digits > std::numeric_limits::digits) { + big_uint_t m((std::numeric_limits::max)()); + Int r; + ++m; + if (is_checked_cpp_int::value) { + BOOST_CHECK_THROW((void)static_cast(m), std::overflow_error); + } else if (std::is_signed::value) { + r = static_cast(m); + BOOST_CHECK_EQUAL(r, (std::numeric_limits::max)()); + } else { + r = static_cast(m); + BOOST_CHECK_EQUAL(r, 0); + } + // Again with much larger value: + m = 1u; + m <<= (std::min)(std::numeric_limits::digits - 1, 1000); + if (is_checked_cpp_int::value) { + BOOST_CHECK_THROW((void)static_cast(m), std::overflow_error); + } else if (std::is_signed::value && std::is_integral::value) { + r = static_cast(m); + BOOST_CHECK_EQUAL(r, (std::numeric_limits::max)()); + } else { + r = static_cast(m); + BOOST_CHECK_EQUAL(r, 0); + } + + if (std::numeric_limits::is_signed && (std::is_signed::value)) { + m = (std::numeric_limits::min)(); + --m; + if (is_checked_cpp_int::value) { + BOOST_CHECK_THROW((void)static_cast(m), std::overflow_error); + } else { + r = static_cast(m); + BOOST_CHECK_EQUAL(r, (std::numeric_limits::min)()); + } + // Again with much larger value: + m = 2u; + m = pow(m, (std::min)(std::numeric_limits::digits - 1, 1000)); + ++m; + m = negate_if_signed( + m, std::integral_constant::is_signed>()); + if (is_checked_cpp_int::value) { + BOOST_CHECK_THROW((void)static_cast(m), std::overflow_error); + } else { + r = static_cast(m); + BOOST_CHECK_EQUAL(r, (std::numeric_limits::min)()); + } + } else if (std::numeric_limits::is_signed && !std::is_signed::value) { + // signed to unsigned converison with overflow, it's really not clear what should happen + // here! + m = (std::numeric_limits::max)(); + ++m; + m = negate_if_signed( + m, std::integral_constant::is_signed>()); + BOOST_CHECK_THROW((void)static_cast(m), std::range_error); + // Again with much larger value: + m = 2u; + m = pow(m, (std::min)(std::numeric_limits::digits - 1, 1000)); + m = negate_if_signed( + m, std::integral_constant::is_signed>()); + BOOST_CHECK_THROW((void)static_cast(m), std::range_error); + } + } +} + +template +void test_integer_round_trip() { + if (std::numeric_limits::digits >= std::numeric_limits::digits) { + big_uint_t m((std::numeric_limits::max)()); + Int r = static_cast(m); + BOOST_CHECK_EQUAL(m, r); + if (std::numeric_limits::is_signed && + (std::numeric_limits::digits > std::numeric_limits::digits)) { + m = (std::numeric_limits::min)(); + r = static_cast(m); + BOOST_CHECK_EQUAL(m, r); + } + } + test_integer_overflow(); +} + +template +void test_integer_ops() { // NOLINT + test_signed_integer_ops( + std::integral_constant::is_signed>()); + + big_uint_t a(20); + big_uint_t b(7); + big_uint_t c(5); + BOOST_CHECK_EQUAL(a % b, 20 % 7); + BOOST_CHECK_EQUAL(a % 7, 20 % 7); + BOOST_CHECK_EQUAL(a % 7u, 20 % 7); + BOOST_CHECK_EQUAL(a % a, 0); + BOOST_CHECK_EQUAL(a % c, 0); + BOOST_CHECK_EQUAL(a % 5, 0); + a = a % (b + 0); + BOOST_CHECK_EQUAL(a, 20 % 7); + a = 20; + c = (a + 2) % (a - 1); + BOOST_CHECK_EQUAL(c, 22 % 19); + c = 5; + a = b % (a - 15); + BOOST_CHECK_EQUAL(a, 7 % 5); + a = 20; + + a = 20; + a %= 7; + BOOST_CHECK_EQUAL(a, 20 % 7); +#ifndef BOOST_NO_LONG_LONG + a = 20; + a %= 7uLL; + BOOST_CHECK_EQUAL(a, 20 % 7); +#endif + a = 20; + ++a; + BOOST_CHECK_EQUAL(a, 21); + --a; + BOOST_CHECK_EQUAL(a, 20); + BOOST_CHECK_EQUAL(a++, 20); + BOOST_CHECK_EQUAL(a, 21); + BOOST_CHECK_EQUAL(a--, 21); + BOOST_CHECK_EQUAL(a, 20); + a = 2000; + a <<= 20; + BOOST_CHECK_EQUAL(a, 2000L << 20); + a >>= 20; + BOOST_CHECK_EQUAL(a, 2000); + a <<= 20u; + BOOST_CHECK_EQUAL(a, 2000L << 20); + a >>= 20u; + BOOST_CHECK_EQUAL(a, 2000); +#ifndef BOOST_NO_EXCEPTIONS + BOOST_CHECK_THROW(a <<= -20, std::range_error); + BOOST_CHECK_THROW((void)(a >>= -20), std::range_error); + BOOST_CHECK_THROW((void)big_uint_t(a << -20), std::range_error); + BOOST_CHECK_THROW((void)big_uint_t(a >> -20), std::range_error); +#endif +#ifndef BOOST_NO_LONG_LONG + if (sizeof(long long) > sizeof(std::size_t)) { + // extreme values should trigger an exception: +#ifndef BOOST_NO_EXCEPTIONS + BOOST_CHECK_THROW(a >>= (1uLL << (sizeof(long long) * CHAR_BIT - 2)), std::range_error); + BOOST_CHECK_THROW(a <<= (1uLL << (sizeof(long long) * CHAR_BIT - 2)), std::range_error); + BOOST_CHECK_THROW(a >>= -(1LL << (sizeof(long long) * CHAR_BIT - 2)), std::range_error); + BOOST_CHECK_THROW(a <<= -(1LL << (sizeof(long long) * CHAR_BIT - 2)), std::range_error); + BOOST_CHECK_THROW(a >>= (1LL << (sizeof(long long) * CHAR_BIT - 2)), std::range_error); + BOOST_CHECK_THROW(a <<= (1LL << (sizeof(long long) * CHAR_BIT - 2)), std::range_error); +#endif + // Unless they fit within range: + a = 2000L; + a <<= 20uLL; + BOOST_CHECK_EQUAL(a, (2000L << 20)); + a = 2000; + a <<= 20LL; + BOOST_CHECK_EQUAL(a, (2000L << 20)); + +#ifndef BOOST_NO_EXCEPTIONS + BOOST_CHECK_THROW(big_uint_t(a >> (1uLL << (sizeof(long long) * CHAR_BIT - 2))), + std::range_error); + BOOST_CHECK_THROW(big_uint_t(a <<= (1uLL << (sizeof(long long) * CHAR_BIT - 2))), + std::range_error); + BOOST_CHECK_THROW(big_uint_t(a >>= -(1LL << (sizeof(long long) * CHAR_BIT - 2))), + std::range_error); + BOOST_CHECK_THROW(big_uint_t(a <<= -(1LL << (sizeof(long long) * CHAR_BIT - 2))), + std::range_error); + BOOST_CHECK_THROW(big_uint_t(a >>= (1LL << (sizeof(long long) * CHAR_BIT - 2))), + std::range_error); + BOOST_CHECK_THROW(big_uint_t(a <<= (1LL << (sizeof(long long) * CHAR_BIT - 2))), + std::range_error); +#endif + // Unless they fit within range: + a = 2000L; + BOOST_CHECK_EQUAL(big_uint_t(a << 20uLL), (2000L << 20)); + a = 2000; + BOOST_CHECK_EQUAL(big_uint_t(a << 20LL), (2000L << 20)); + } +#endif + a = 20; + b = a << 20; + BOOST_CHECK_EQUAL(b, (20 << 20)); + b = a >> 2; + BOOST_CHECK_EQUAL(b, (20 >> 2)); + b = (a + 2) << 10; + BOOST_CHECK_EQUAL(b, (22 << 10)); + b = (a + 3) >> 3; + BOOST_CHECK_EQUAL(b, (23 >> 3)); + // + // Bit fiddling: + // + int i = 1020304; + int j = 56789123; + int k = 4523187; + a = i; + b = j; + c = a; + c &= b; + BOOST_CHECK_EQUAL(c, (i & j)); + c = a; + c &= j; + BOOST_CHECK_EQUAL(c, (i & j)); + c = a; + c &= a + b; + BOOST_CHECK_EQUAL(c, (i & (i + j))); + BOOST_CHECK_EQUAL((a & b), (i & j)); + c = k; + a = a & (b + k); + BOOST_CHECK_EQUAL(a, (i & (j + k))); + a = i; + a = (b + k) & a; + BOOST_CHECK_EQUAL(a, (i & (j + k))); + a = i; + c = a & b & k; + BOOST_CHECK_EQUAL(c, (i & j & k)); + c = a; + c &= (c + b); + BOOST_CHECK_EQUAL(c, (i & (i + j))); + c = a & (b | 1); + BOOST_CHECK_EQUAL(c, (i & (j | 1))); + + test_complement(a, b, c, typename is_twos_complement_integer::type()); + + a = i; + b = j; + c = a; + c |= b; + BOOST_CHECK_EQUAL(c, (i | j)); + c = a; + c |= j; + BOOST_CHECK_EQUAL(c, (i | j)); + c = a; + c |= a + b; + BOOST_CHECK_EQUAL(c, (i | (i + j))); + BOOST_CHECK_EQUAL((a | b), (i | j)); + c = k; + a = a | (b + k); + BOOST_CHECK_EQUAL(a, (i | (j + k))); + a = i; + a = (b + k) | a; + BOOST_CHECK_EQUAL(a, (i | (j + k))); + a = i; + c = a | b | k; + BOOST_CHECK_EQUAL(c, (i | j | k)); + c = a; + c |= (c + b); + BOOST_CHECK_EQUAL(c, (i | (i + j))); + c = a | (b | 1); + BOOST_CHECK_EQUAL(c, (i | (j | 1))); + + a = i; + b = j; + c = a; + c ^= b; + BOOST_CHECK_EQUAL(c, (i ^ j)); + c = a; + c ^= j; + BOOST_CHECK_EQUAL(c, (i ^ j)); + c = a; + c ^= a + b; + BOOST_CHECK_EQUAL(c, (i ^ (i + j))); + BOOST_CHECK_EQUAL((a ^ b), (i ^ j)); + c = k; + a = a ^ (b + k); + BOOST_CHECK_EQUAL(a, (i ^ (j + k))); + a = i; + a = (b + k) ^ a; + BOOST_CHECK_EQUAL(a, (i ^ (j + k))); + a = i; + c = a ^ b ^ k; + BOOST_CHECK_EQUAL(c, (i ^ j ^ k)); + c = a; + c ^= (c + b); + BOOST_CHECK_EQUAL(c, (i ^ (i + j))); + c = a ^ (b | 1); + BOOST_CHECK_EQUAL(c, (i ^ (j | 1))); + + a = i; + b = j; + c = k; + // + // Non-member functions: + // + a = 400; + b = 45; + // TODO(ioxid): implement lcm + // BOOST_CHECK_EQUAL(gcd(a, b), boost::integer::gcd(400, 45)); + // BOOST_CHECK_EQUAL(lcm(a, b), boost::integer::lcm(400, 45)); + // BOOST_CHECK_EQUAL(gcd(a, 45), boost::integer::gcd(400, 45)); + // BOOST_CHECK_EQUAL(lcm(a, 45), boost::integer::lcm(400, 45)); + // BOOST_CHECK_EQUAL(gcd(a, 45u), boost::integer::gcd(400, 45)); + // BOOST_CHECK_EQUAL(lcm(a, 45u), boost::integer::lcm(400, 45)); + // BOOST_CHECK_EQUAL(gcd(400, b), boost::integer::gcd(400, 45)); + // BOOST_CHECK_EQUAL(lcm(400, b), boost::integer::lcm(400, 45)); + // BOOST_CHECK_EQUAL(gcd(400u, b), boost::integer::gcd(400, 45)); + // BOOST_CHECK_EQUAL(lcm(400u, b), boost::integer::lcm(400, 45)); + + if (std::numeric_limits::is_bounded) { + // Fixed precision integer: + a = (std::numeric_limits::max)() - 1; + b = (std::numeric_limits::max)() / 35; + big_uint_t div = gcd(a, b); + BOOST_CHECK_EQUAL(a % div, 0); + BOOST_CHECK_EQUAL(b % div, 0); + } + + // + // Conditionals involving 2 arg functions: + // + test_conditional(big_uint_t(gcd(a, b)), gcd(a, b)); + + big_uint_t r; + divide_qr(a, b, c, r); + BOOST_CHECK_EQUAL(c, a / b); + BOOST_CHECK_EQUAL(r, a % b); + divide_qr(a + 0, b, c, r); + BOOST_CHECK_EQUAL(c, a / b); + BOOST_CHECK_EQUAL(r, a % b); + divide_qr(a, b + 0, c, r); + BOOST_CHECK_EQUAL(c, a / b); + BOOST_CHECK_EQUAL(r, a % b); + divide_qr(a + 0, b + 0, c, r); + BOOST_CHECK_EQUAL(c, a / b); + BOOST_CHECK_EQUAL(r, a % b); + // BOOST_CHECK_EQUAL(integer_modulus(a, 57), a % 57); + for (i = 0; i < 20; ++i) { + if (std::numeric_limits::is_specialized && + (!std::numeric_limits::is_bounded || + (i * 17 < std::numeric_limits::digits))) { + BOOST_CHECK_EQUAL(lsb(big_uint_t(1) << (i * 17)), static_cast(i * 17)); + BOOST_CHECK_EQUAL(msb(big_uint_t(1) << (i * 17)), static_cast(i * 17)); + BOOST_CHECK(bit_test(big_uint_t(1) << (i * 17), i * 17)); + BOOST_CHECK(!bit_test(big_uint_t(1) << (i * 17), i * 17 + 1)); + if (i) { + BOOST_CHECK(!bit_test(big_uint_t(1) << (i * 17), i * 17 - 1)); + } + big_uint_t zero(0); + BOOST_CHECK(bit_test(bit_set(zero, i * 17), i * 17)); + zero = 0; + BOOST_CHECK_EQUAL(bit_flip(zero, i * 17), big_uint_t(1) << i * 17); + zero = big_uint_t(1) << i * 17; + BOOST_CHECK_EQUAL(bit_flip(zero, i * 17), 0); + zero = big_uint_t(1) << i * 17; + BOOST_CHECK_EQUAL(bit_unset(zero, i * 17), 0); + } + } + // + // pow, powm: + // + BOOST_CHECK_EQUAL(pow(big_uint_t(3), 4u), 81); + BOOST_CHECK_EQUAL(pow(big_uint_t(3) + big_uint_t(0), 4u), 81); + BOOST_CHECK_EQUAL(powm(big_uint_t(3), big_uint_t(4), big_uint_t(13)), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3), big_uint_t(4), 13), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3), big_uint_t(4), big_uint_t(13) + 0), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3), big_uint_t(4) + 0, big_uint_t(13)), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3), big_uint_t(4) + 0, 13), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3), big_uint_t(4) + 0, big_uint_t(13) + 0), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3), 4 + 0, big_uint_t(13)), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3), 4 + 0, 13), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3), 4 + 0, big_uint_t(13) + 0), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3) + 0, big_uint_t(4), big_uint_t(13)), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3) + 0, big_uint_t(4), 13), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3) + 0, big_uint_t(4), big_uint_t(13) + 0), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3) + 0, big_uint_t(4) + 0, big_uint_t(13)), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3) + 0, big_uint_t(4) + 0, 13), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3) + 0, big_uint_t(4) + 0, big_uint_t(13) + 0), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3) + 0, 4 + 0, big_uint_t(13)), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3) + 0, 4 + 0, 13), 81 % 13); + BOOST_CHECK_EQUAL(powm(big_uint_t(3) + 0, 4 + 0, big_uint_t(13) + 0), 81 % 13); + // + // Conditionals involving 3 arg functions: + // + test_conditional(big_uint_t(powm(big_uint_t(3), big_uint_t(4), big_uint_t(13))), + powm(big_uint_t(3), big_uint_t(4), big_uint_t(13))); + +#ifndef BOOST_NO_EXCEPTIONS + // + // Things that are expected errors: + // + BOOST_CHECK_THROW(big_uint_t("3.14"), std::invalid_argument); + BOOST_CHECK_THROW(big_uint_t("3L"), std::invalid_argument); + BOOST_CHECK_THROW((void)big_uint_t(big_uint_t(20) / 0u), std::overflow_error); +#endif + // + // Extra tests added for full coverage: + // + a = 20; + b = 7; + c = 20 % b; + BOOST_CHECK_EQUAL(c, (20 % 7)); + c = 20 % (b + 0); + BOOST_CHECK_EQUAL(c, (20 % 7)); + c = a & 10; + BOOST_CHECK_EQUAL(c, (20 & 10)); + c = 10 & a; + BOOST_CHECK_EQUAL(c, (20 & 10)); + c = (a + 0) & (b + 0); + BOOST_CHECK_EQUAL(c, (20 & 7)); + c = 10 & (a + 0); + BOOST_CHECK_EQUAL(c, (20 & 10)); + c = 10 | a; + BOOST_CHECK_EQUAL(c, (20 | 10)); + c = (a + 0) | (b + 0); + BOOST_CHECK(c == (20 | 7)); + c = 20 | (b + 0); + BOOST_CHECK_EQUAL(c, (20 | 7)); + c = a ^ 7; + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + c = 20 ^ b; + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + c = (a + 0) ^ (b + 0); + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + c = 20 ^ (b + 0); + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + // + // Rval_tue ref tests: + // + c = big_uint_t(20) % b; + BOOST_CHECK_EQUAL(c, (20 % 7)); + c = a % big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 % 7)); + c = big_uint_t(20) % big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 % 7)); + c = big_uint_t(20) % 7; + BOOST_CHECK_EQUAL(c, (20 % 7)); + c = 20 % big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 % 7)); + c = big_uint_t(20) % (b * 1); + BOOST_CHECK_EQUAL(c, (20 % 7)); + c = (a * 1 + 0) % big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 % 7)); + c = big_uint_t(20) >> 2; + BOOST_CHECK_EQUAL(c, (20 >> 2)); + c = big_uint_t(20) & b; + BOOST_CHECK_EQUAL(c, (20 & 7)); + c = a & big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 & 7)); + c = big_uint_t(20) & big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 & 7)); + c = big_uint_t(20) & 7; + BOOST_CHECK_EQUAL(c, (20 & 7)); + c = 20 & big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 & 7)); + c = big_uint_t(20) & (b * 1 + 0); + BOOST_CHECK_EQUAL(c, (20 & 7)); + c = (a * 1 + 0) & big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 & 7)); + c = big_uint_t(20) | b; + BOOST_CHECK_EQUAL(c, (20 | 7)); + c = a | big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 | 7)); + c = big_uint_t(20) | big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 | 7)); + c = big_uint_t(20) | 7; + BOOST_CHECK_EQUAL(c, (20 | 7)); + c = 20 | big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 | 7)); + c = big_uint_t(20) | (b * 1 + 0); + BOOST_CHECK_EQUAL(c, (20 | 7)); + c = (a * 1 + 0) | big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 | 7)); + c = big_uint_t(20) ^ b; + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + c = a ^ big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + c = big_uint_t(20) ^ big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + c = big_uint_t(20) ^ 7; + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + c = 20 ^ big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + c = big_uint_t(20) ^ (b * 1 + 0); + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + c = (a * 1 + 0) ^ big_uint_t(7); + BOOST_CHECK_EQUAL(c, (20 ^ 7)); + + // + // Round tripping of built in integers: + // + test_integer_round_trip(); + test_integer_round_trip(); + test_integer_round_trip(); + test_integer_round_trip(); + test_integer_round_trip(); + test_integer_round_trip(); +#ifndef BOOST_NO_LONG_LONG + test_integer_round_trip(); + test_integer_round_trip(); +#endif +} + +template +struct lexical_cast_target_type { + typedef typename std::conditional< + std::is_signed::value && std::is_integral::value, std::intmax_t, + typename std::conditional::value, std::uintmax_t, T>::type>::type type; +}; + +template +void test_negative_mixed_minmax(std::integral_constant const&) { + if (!std::numeric_limits::is_bounded || + (std::numeric_limits::digits >= std::numeric_limits::digits)) { + big_uint_t mx1((std::numeric_limits::max)() - 1); + ++mx1; + big_uint_t mx2((std::numeric_limits::max)()); + BOOST_CHECK_EQUAL(mx1, mx2); + mx1 = (std::numeric_limits::max)() - 1; + ++mx1; + mx2 = (std::numeric_limits::max)(); + BOOST_CHECK_EQUAL(mx1, mx2); + + if (!std::numeric_limits::is_bounded || + (std::numeric_limits::digits > std::numeric_limits::digits)) { + big_uint_t mx3((std::numeric_limits::min)() + 1); + --mx3; + big_uint_t mx4((std::numeric_limits::min)()); + BOOST_CHECK_EQUAL(mx3, mx4); + mx3 = (std::numeric_limits::min)() + 1; + --mx3; + mx4 = (std::numeric_limits::min)(); + BOOST_CHECK_EQUAL(mx3, mx4); + } + } +} +template +void test_negative_mixed_minmax(std::integral_constant const&) {} + +template +void test_negative_mixed_numeric_limits(std::integral_constant const&) { + typedef typename lexical_cast_target_type::type target_type; +#if defined(TEST_MPFR) + num_t tol = 10 * std::numeric_limits::epsilon(); +#else + num_t tol = 0; +#endif + static const int left_shift = std::numeric_limits::digits - 1; + num_t n1 = + -static_cast(1uLL << ((left_shift < 63) && (left_shift > 0) ? left_shift : 10)); + num_t n2 = -1; + num_t n3 = 0; + num_t n4 = -20; + std::ios_base::fmtflags f = std::is_floating_point::value ? std::ios_base::scientific + : std::ios_base::fmtflags(0); + int digits_to_print = + std::is_floating_point::value && std::numeric_limits::is_specialized + ? std::numeric_limits::digits10 + 5 + : 0; + if (std::numeric_limits::digits <= std::numeric_limits::digits) { + BOOST_CHECK_CLOSE( + n1, checked_lexical_cast(big_uint_t(n1).str(digits_to_print, f)), tol); + } + BOOST_CHECK_CLOSE(n2, checked_lexical_cast(big_uint_t(n2).str(digits_to_print, f)), + 0); + BOOST_CHECK_CLOSE(n3, checked_lexical_cast(big_uint_t(n3).str(digits_to_print, f)), + 0); + BOOST_CHECK_CLOSE(n4, checked_lexical_cast(big_uint_t(n4).str(digits_to_print, f)), + 0); +} + +template +void test_negative_mixed_numeric_limits(std::integral_constant const&) {} + +template +void test_negative_mixed(std::integral_constant const&) { + typedef typename std::conditional< + std::is_convertible::value, + typename std::conditional::value && (sizeof(num_t) < sizeof(int)), + int, num_t>::type, + big_uint_t>::type cast_type; + typedef typename std::conditional::value, num_t, + big_uint_t>::type simple_cast_type; + std::cout << "Testing mixed arithmetic with type: " << typeid(big_uint_t).name() << " and " + << typeid(num_t).name() << std::endl; + static const int left_shift = std::numeric_limits::digits - 1; + num_t n1 = + -static_cast(1uLL << ((left_shift < 63) && (left_shift > 0) ? left_shift : 10)); + num_t n2 = -1; + num_t n3 = 0; + num_t n4 = -20; + num_t n5 = -8; + + test_comparisons(n1, n2, std::is_convertible()); + test_comparisons(n1, n3, std::is_convertible()); + test_comparisons(n3, n1, std::is_convertible()); + test_comparisons(n2, n1, std::is_convertible()); + test_comparisons(n1, n1, std::is_convertible()); + test_comparisons(n3, n3, std::is_convertible()); + + // Default construct: + BOOST_CHECK_EQUAL(big_uint_t(n1), static_cast(n1)); + BOOST_CHECK_EQUAL(big_uint_t(n2), static_cast(n2)); + BOOST_CHECK_EQUAL(big_uint_t(n3), static_cast(n3)); + BOOST_CHECK_EQUAL(big_uint_t(n4), static_cast(n4)); + BOOST_CHECK_EQUAL(static_cast(n1), big_uint_t(n1)); + BOOST_CHECK_EQUAL(static_cast(n2), big_uint_t(n2)); + BOOST_CHECK_EQUAL(static_cast(n3), big_uint_t(n3)); + BOOST_CHECK_EQUAL(static_cast(n4), big_uint_t(n4)); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n1)), n1); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n2)), n2); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n3)), n3); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n4)), n4); + // Conversions when source is an expression template: + BOOST_CHECK_EQUAL(static_cast((big_uint_t(n1) + 0)), n1); + BOOST_CHECK_EQUAL(static_cast((big_uint_t(n2) + 0)), n2); + BOOST_CHECK_EQUAL(static_cast((big_uint_t(n3) + 0)), n3); + BOOST_CHECK_EQUAL(static_cast((big_uint_t(n4) + 0)), n4); + test_negative_mixed_numeric_limits( + std::integral_constant::is_specialized>()); + // Assignment: + big_uint_t r(0); + BOOST_CHECK(r != static_cast(n1)); + r = static_cast(n1); + BOOST_CHECK_EQUAL(r, static_cast(n1)); + r = static_cast(n2); + BOOST_CHECK_EQUAL(r, static_cast(n2)); + r = static_cast(n3); + BOOST_CHECK_EQUAL(r, static_cast(n3)); + r = static_cast(n4); + BOOST_CHECK_EQUAL(r, static_cast(n4)); + // Addition: + r = static_cast(n2); + BOOST_CHECK_EQUAL(r + static_cast(n4), static_cast(n2 + n4)); + BOOST_CHECK_EQUAL(big_uint_t(r + static_cast(n4)), + static_cast(n2 + n4)); + r += static_cast(n4); + BOOST_CHECK_EQUAL(r, static_cast(n2 + n4)); + // subtraction: + r = static_cast(n4); + BOOST_CHECK_EQUAL(r - static_cast(n5), static_cast(n4 - n5)); + BOOST_CHECK_EQUAL(big_uint_t(r - static_cast(n5)), + static_cast(n4 - n5)); + r -= static_cast(n5); + BOOST_CHECK_EQUAL(r, static_cast(n4 - n5)); + // Multiplication: + r = static_cast(n2); + BOOST_CHECK_EQUAL(r * static_cast(n4), static_cast(n2 * n4)); + BOOST_CHECK_EQUAL(big_uint_t(r * static_cast(n4)), + static_cast(n2 * n4)); + r *= static_cast(n4); + BOOST_CHECK_EQUAL(r, static_cast(n2 * n4)); + // Division: + r = static_cast(n1); + BOOST_CHECK_EQUAL(r / static_cast(n5), static_cast(n1 / n5)); + BOOST_CHECK_EQUAL(big_uint_t(r / static_cast(n5)), + static_cast(n1 / n5)); + r /= static_cast(n5); + BOOST_CHECK_EQUAL(r, static_cast(n1 / n5)); + // + // Extra cases for full coverage: + // + r = big_uint_t(n4) + static_cast(n5); + BOOST_CHECK_EQUAL(r, static_cast(n4 + n5)); + r = static_cast(n4) + big_uint_t(n5); + BOOST_CHECK_EQUAL(r, static_cast(n4 + n5)); + r = big_uint_t(n4) - static_cast(n5); + BOOST_CHECK_EQUAL(r, static_cast(n4 - n5)); + r = static_cast(n4) - big_uint_t(n5); + BOOST_CHECK_EQUAL(r, static_cast(n4 - n5)); + r = static_cast(n4) * big_uint_t(n5); + BOOST_CHECK_EQUAL(r, static_cast(n4 * n5)); + r = static_cast(num_t(4) * n4) / big_uint_t(4); + BOOST_CHECK_EQUAL(r, static_cast(n4)); + + big_uint_t a, b, c; + a = 20; + b = 30; + c = -a + b; + BOOST_CHECK_EQUAL(c, 10); + c = b + -a; + BOOST_CHECK_EQUAL(c, 10); + n4 = 30; + c = -a + static_cast(n4); + BOOST_CHECK_EQUAL(c, 10); + c = static_cast(n4) + -a; + BOOST_CHECK_EQUAL(c, 10); + c = -a + -b; + BOOST_CHECK_EQUAL(c, -50); + n4 = 4; + c = -(a + b) + static_cast(n4); + BOOST_CHECK_EQUAL(c, -50 + 4); + n4 = 50; + c = (a + b) - static_cast(n4); + BOOST_CHECK_EQUAL(c, 0); + c = (a + b) - static_cast(n4); + BOOST_CHECK_EQUAL(c, 0); + c = a - -(b + static_cast(n4)); + BOOST_CHECK_EQUAL(c, 20 - -(30 + 50)); + c = -(b + static_cast(n4)) - a; + BOOST_CHECK_EQUAL(c, -(30 + 50) - 20); + c = a - -b; + BOOST_CHECK_EQUAL(c, 50); + c = -a - b; + BOOST_CHECK_EQUAL(c, -50); + c = -a - static_cast(n4); + BOOST_CHECK_EQUAL(c, -20 - 50); + c = static_cast(n4) - -a; + BOOST_CHECK_EQUAL(c, 50 + 20); + c = -(a + b) - big_uint_t(n4); + BOOST_CHECK_EQUAL(c, -(20 + 30) - 50); + c = static_cast(n4) - (a + b); + BOOST_CHECK_EQUAL(c, 0); + c = (a + b) * static_cast(n4); + BOOST_CHECK_EQUAL(c, 50 * 50); + c = static_cast(n4) * (a + b); + BOOST_CHECK_EQUAL(c, 50 * 50); + c = a * -(b + static_cast(n4)); + BOOST_CHECK_EQUAL(c, 20 * -(30 + 50)); + c = -(b + static_cast(n4)) * a; + BOOST_CHECK_EQUAL(c, 20 * -(30 + 50)); + c = a * -b; + BOOST_CHECK_EQUAL(c, 20 * -30); + c = -a * b; + BOOST_CHECK_EQUAL(c, 20 * -30); + c = -a * static_cast(n4); + BOOST_CHECK_EQUAL(c, -20 * 50); + c = static_cast(n4) * -a; + BOOST_CHECK_EQUAL(c, -20 * 50); + c = -(a + b) + a; + BOOST_CHECK(-50 + 20); + c = static_cast(n4) - (a + b); + BOOST_CHECK_EQUAL(c, 0); + big_uint_t d = 10; + c = (a + b) / d; + BOOST_CHECK_EQUAL(c, 5); + c = (a + b) / (d + 0); + BOOST_CHECK_EQUAL(c, 5); + c = (a + b) / static_cast(n4); + BOOST_CHECK_EQUAL(c, 1); + c = static_cast(n4) / (a + b); + BOOST_CHECK_EQUAL(c, 1); + d = 50; + c = d / -(a + b); + BOOST_CHECK_EQUAL(c, -1); + c = -(a + b) / d; + BOOST_CHECK_EQUAL(c, -1); + d = 2; + c = a / -d; + BOOST_CHECK_EQUAL(c, 20 / -2); + c = -a / d; + BOOST_CHECK_EQUAL(c, 20 / -2); + d = 50; + c = -d / static_cast(n4); + BOOST_CHECK_EQUAL(c, -1); + c = static_cast(n4) / -d; + BOOST_CHECK_EQUAL(c, -1); + c = static_cast(n4) + a; + BOOST_CHECK_EQUAL(c, 70); + c = static_cast(n4) - a; + BOOST_CHECK_EQUAL(c, 30); + c = static_cast(n4) * a; + BOOST_CHECK_EQUAL(c, 50 * 20); + + n1 = -2; + n2 = -3; + n3 = -4; + a = static_cast(n1); + b = static_cast(n2); + c = static_cast(n3); + d = a + b * c; + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = static_cast(n1) + b * c; + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = a + static_cast(n2) * c; + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = a + b * static_cast(n3); + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = static_cast(n1) + static_cast(n2) * c; + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = static_cast(n1) + b * static_cast(n3); + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + a += static_cast(n2) * c; + BOOST_CHECK_EQUAL(a, -2 + -3 * -4); + a = static_cast(n1); + a += b * static_cast(n3); + BOOST_CHECK_EQUAL(a, -2 + -3 * -4); + a = static_cast(n1); + + d = b * c + a; + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = b * c + static_cast(n1); + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = static_cast(n2) * c + a; + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = b * static_cast(n3) + a; + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = static_cast(n2) * c + static_cast(n1); + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + d = b * static_cast(n3) + static_cast(n1); + BOOST_CHECK_EQUAL(d, -2 + -3 * -4); + + a = -20; + d = a - b * c; + BOOST_CHECK_EQUAL(d, -20 - -3 * -4); + n1 = -20; + d = static_cast(n1) - b * c; + BOOST_CHECK_EQUAL(d, -20 - -3 * -4); + d = a - static_cast(n2) * c; + BOOST_CHECK_EQUAL(d, -20 - -3 * -4); + d = a - b * static_cast(n3); + BOOST_CHECK_EQUAL(d, -20 - -3 * -4); + d = static_cast(n1) - static_cast(n2) * c; + BOOST_CHECK_EQUAL(d, -20 - -3 * -4); + d = static_cast(n1) - b * static_cast(n3); + BOOST_CHECK_EQUAL(d, -20 - -3 * -4); + a -= static_cast(n2) * c; + BOOST_CHECK_EQUAL(a, -20 - -3 * -4); + a = static_cast(n1); + a -= b * static_cast(n3); + BOOST_CHECK_EQUAL(a, -20 - -3 * -4); + + a = -2; + d = b * c - a; + BOOST_CHECK_EQUAL(d, -3 * -4 - -2); + n1 = -2; + d = b * c - static_cast(n1); + BOOST_CHECK_EQUAL(d, -3 * -4 - -2); + d = static_cast(n2) * c - a; + BOOST_CHECK_EQUAL(d, -3 * -4 - -2); + d = b * static_cast(n3) - a; + BOOST_CHECK_EQUAL(d, -3 * -4 - -2); + d = static_cast(n2) * c - static_cast(n1); + BOOST_CHECK_EQUAL(d, -3 * -4 - -2); + d = b * static_cast(n3) - static_cast(n1); + BOOST_CHECK_EQUAL(d, -3 * -4 - -2); + // + // Conversion from min and max values: + // + test_negative_mixed_minmax( + std::integral_constant < bool, + std::numeric_limits::is_integer&& std::numeric_limits::is_integer > ()); + // + // Rval_tue ref overloads: + // + a = 2; + n1 = 3; + d = -a + static_cast(n1); + BOOST_CHECK_EQUAL(d, 1); + d = static_cast(n1) + -a; + BOOST_CHECK_EQUAL(d, 1); + d = -a - static_cast(n1); + BOOST_CHECK_EQUAL(d, -5); + d = static_cast(n1) - -a; + BOOST_CHECK_EQUAL(d, 5); + d = -a * static_cast(n1); + BOOST_CHECK_EQUAL(d, -6); + d = static_cast(n1) * -a; + BOOST_CHECK_EQUAL(d, -6); + n1 = 4; + d = -static_cast(n1) / a; + BOOST_CHECK_EQUAL(d, -2); + d = static_cast(n1) / -a; + BOOST_CHECK_EQUAL(d, -2); +} + +template +void test_negative_mixed(std::integral_constant const&) {} + +template +void test_mixed(const std::integral_constant&) {} + +template +inline big_uint_t negate_value(const big_uint_t& val, const std::integral_constant&) { + return -val; +} +template +inline big_uint_t negate_value(const big_uint_t& val, const std::integral_constant&) { + return val; +} + +template +void test_mixed_numeric_limits(const std::integral_constant&) { + typedef typename lexical_cast_target_type::type target_type; +#if defined(TEST_MPFR) + num_t tol = 10 * std::numeric_limits::epsilon(); +#else + num_t tol = 0; +#endif + + big_uint_t d; + + static const int left_shift = std::numeric_limits::digits - 1; + num_t n1 = + static_cast(1uLL << ((left_shift < 63) && (left_shift > 0) ? left_shift : 10)); + num_t n2 = 1; + num_t n3 = 0; + num_t n4 = 20; + + std::ios_base::fmtflags f = std::is_floating_point::value ? std::ios_base::scientific + : std::ios_base::fmtflags(0); + int digits_to_print = + std::is_floating_point::value && std::numeric_limits::is_specialized + ? std::numeric_limits::digits10 + 5 + : 0; + // if (std::numeric_limits::digits <= std::numeric_limits::digits) { + // BOOST_CHECK_CLOSE(n1, + // checked_lexical_cast(big_uint_t(n1).str(digits_to_print, f)), + // tol); + // } + // BOOST_CHECK_CLOSE(n2, checked_lexical_cast(big_uint_t(n2).str(digits_to_print, + // f)), 0); BOOST_CHECK_CLOSE(n3, + // checked_lexical_cast(big_uint_t(n3).str(digits_to_print, f)), 0); + // BOOST_CHECK_CLOSE(n4, checked_lexical_cast(big_uint_t(n4).str(digits_to_print, + // f)), 0); +} +template +void test_mixed_numeric_limits(const std::integral_constant&) {} + +template +void test_mixed(const std::integral_constant&) { + typedef typename std::conditional< + std::is_convertible::value, + typename std::conditional::value && (sizeof(num_t) < sizeof(int)), + int, num_t>::type, + big_uint_t>::type cast_type; + typedef typename std::conditional::value, num_t, + big_uint_t>::type simple_cast_type; + + if (std::numeric_limits::is_specialized && + std::numeric_limits::is_bounded && + std::numeric_limits::digits < std::numeric_limits::digits) { + return; + } + + std::cout << "Testing mixed arithmetic with type: " << typeid(big_uint_t).name() << " and " + << typeid(num_t).name() << std::endl; + static const int left_shift = std::numeric_limits::digits - 1; + num_t n1 = + static_cast(1uLL << ((left_shift < 63) && (left_shift > 0) ? left_shift : 10)); + num_t n2 = 1; + num_t n3 = 0; + num_t n4 = 20; + num_t n5 = 8; + + test_comparisons(n1, n2, std::is_convertible()); + test_comparisons(n1, n3, std::is_convertible()); + test_comparisons(n1, n1, std::is_convertible()); + test_comparisons(n3, n1, std::is_convertible()); + test_comparisons(n2, n1, std::is_convertible()); + test_comparisons(n3, n3, std::is_convertible()); + + // Default construct: + BOOST_CHECK_EQUAL(big_uint_t(n1), static_cast(n1)); + BOOST_CHECK_EQUAL(big_uint_t(n2), static_cast(n2)); + BOOST_CHECK_EQUAL(big_uint_t(n3), static_cast(n3)); + BOOST_CHECK_EQUAL(big_uint_t(n4), static_cast(n4)); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n1)), n1); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n2)), n2); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n3)), n3); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n4)), n4); + // Again with expression templates: + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n1) + 0), n1); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n2) + 0), n2); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n3) + 0), n3); + BOOST_CHECK_EQUAL(static_cast(big_uint_t(n4) + 0), n4); + BOOST_CHECK_EQUAL(static_cast(n1), big_uint_t(n1)); + BOOST_CHECK_EQUAL(static_cast(n2), big_uint_t(n2)); + BOOST_CHECK_EQUAL(static_cast(n3), big_uint_t(n3)); + BOOST_CHECK_EQUAL(static_cast(n4), big_uint_t(n4)); + // Assignment: + big_uint_t r(0); + BOOST_CHECK(r != static_cast(n1)); + r = static_cast(n1); + BOOST_CHECK_EQUAL(r, static_cast(n1)); + r = static_cast(n2); + BOOST_CHECK_EQUAL(r, static_cast(n2)); + r = static_cast(n3); + BOOST_CHECK_EQUAL(r, static_cast(n3)); + r = static_cast(n4); + BOOST_CHECK_EQUAL(r, static_cast(n4)); + // Addition: + r = static_cast(n2); + BOOST_CHECK_EQUAL(r + static_cast(n4), static_cast(n2 + n4)); + BOOST_CHECK_EQUAL(big_uint_t(r + static_cast(n4)), + static_cast(n2 + n4)); + r += static_cast(n4); + BOOST_CHECK_EQUAL(r, static_cast(n2 + n4)); + // subtraction: + r = static_cast(n4); + BOOST_CHECK_EQUAL(r - static_cast(n5), static_cast(n4 - n5)); + BOOST_CHECK_EQUAL(big_uint_t(r - static_cast(n5)), + static_cast(n4 - n5)); + r -= static_cast(n5); + BOOST_CHECK_EQUAL(r, static_cast(n4 - n5)); + // Multiplication: + r = static_cast(n2); + BOOST_CHECK_EQUAL(r * static_cast(n4), static_cast(n2 * n4)); + BOOST_CHECK_EQUAL(big_uint_t(r * static_cast(n4)), + static_cast(n2 * n4)); + r *= static_cast(n4); + BOOST_CHECK_EQUAL(r, static_cast(n2 * n4)); + // Division: + r = static_cast(n1); + BOOST_CHECK_EQUAL(r / static_cast(n5), static_cast(n1 / n5)); + BOOST_CHECK_EQUAL(big_uint_t(r / static_cast(n5)), + static_cast(n1 / n5)); + r /= static_cast(n5); + BOOST_CHECK_EQUAL(r, static_cast(n1 / n5)); + // + // special cases for full coverage: + // + r = static_cast(n5) + big_uint_t(n4); + BOOST_CHECK_EQUAL(r, static_cast(n4 + n5)); + r = static_cast(n4) - big_uint_t(n5); + BOOST_CHECK_EQUAL(r, static_cast(n4 - n5)); + r = static_cast(n4) * big_uint_t(n5); + BOOST_CHECK_EQUAL(r, static_cast(n4 * n5)); + r = static_cast(num_t(4) * n4) / big_uint_t(4); + BOOST_CHECK_EQUAL(r, static_cast(n4)); + + typedef std::integral_constant::is_specialized || + std::numeric_limits::is_signed) && + (!std::numeric_limits::is_specialized || + std::numeric_limits::is_signed)> + signed_tag; + + test_negative_mixed(signed_tag()); + + n1 = 2; + n2 = 3; + n3 = 4; + big_uint_t a(n1), b(n2), c(n3), d; + d = a + b * c; + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = static_cast(n1) + b * c; + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = a + static_cast(n2) * c; + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = a + b * static_cast(n3); + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = static_cast(n1) + static_cast(n2) * c; + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = static_cast(n1) + b * static_cast(n3); + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + a += static_cast(n2) * c; + BOOST_CHECK_EQUAL(a, 2 + 3 * 4); + a = static_cast(n1); + a += b * static_cast(n3); + BOOST_CHECK_EQUAL(a, 2 + 3 * 4); + a = static_cast(n1); + + d = b * c + a; + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = b * c + static_cast(n1); + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = static_cast(n2) * c + a; + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = b * static_cast(n3) + a; + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = static_cast(n2) * c + static_cast(n1); + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + d = b * static_cast(n3) + static_cast(n1); + BOOST_CHECK_EQUAL(d, 2 + 3 * 4); + + a = 20; + d = a - b * c; + BOOST_CHECK_EQUAL(d, 20 - 3 * 4); + n1 = 20; + d = static_cast(n1) - b * c; + BOOST_CHECK_EQUAL(d, 20 - 3 * 4); + d = a - static_cast(n2) * c; + BOOST_CHECK_EQUAL(d, 20 - 3 * 4); + d = a - b * static_cast(n3); + BOOST_CHECK_EQUAL(d, 20 - 3 * 4); + d = static_cast(n1) - static_cast(n2) * c; + BOOST_CHECK_EQUAL(d, 20 - 3 * 4); + d = static_cast(n1) - b * static_cast(n3); + BOOST_CHECK_EQUAL(d, 20 - 3 * 4); + a -= static_cast(n2) * c; + BOOST_CHECK_EQUAL(a, 20 - 3 * 4); + a = static_cast(n1); + a -= b * static_cast(n3); + BOOST_CHECK_EQUAL(a, 20 - 3 * 4); + + a = 2; + d = b * c - a; + BOOST_CHECK_EQUAL(d, 3 * 4 - 2); + n1 = 2; + d = b * c - static_cast(n1); + BOOST_CHECK_EQUAL(d, 3 * 4 - 2); + d = static_cast(n2) * c - a; + BOOST_CHECK_EQUAL(d, 3 * 4 - 2); + d = b * static_cast(n3) - a; + BOOST_CHECK_EQUAL(d, 3 * 4 - a); + d = static_cast(n2) * c - static_cast(n1); + BOOST_CHECK_EQUAL(d, 3 * 4 - 2); + d = b * static_cast(n3) - static_cast(n1); + BOOST_CHECK_EQUAL(d, 3 * 4 - 2); + + test_mixed_numeric_limits( + std::integral_constant::is_specialized>()); +} + +template +void test_members(big_uint_t) { + // + // Test sign and zero functions: + // + big_uint_t a = 20; + big_uint_t b = 30; + // BOOST_CHECK(a.sign() > 0); + BOOST_CHECK(!a.is_zero()); + // if (std::numeric_limits::is_signed) { + // a = -20; + // BOOST_CHECK(a.sign() < 0); + // BOOST_CHECK(!a.is_zero()); + // } + a = 0; + // BOOST_CHECK_EQUAL(a.sign(), 0); + BOOST_CHECK(a.is_zero()); + + // a = 20; + // b = 30; + // a.swap(b); + // BOOST_CHECK_EQUAL(a, 30); + // BOOST_CHECK_EQUAL(b, 20); +} + +template +void test_signed_ops(const std::integral_constant&) { + big_uint_t a(8); + big_uint_t b(64); + big_uint_t c(500); + big_uint_t d(1024); + big_uint_t ac; + BOOST_CHECK_EQUAL(-a, -8); + ac = a; + ac = ac - b; + BOOST_CHECK_EQUAL(ac, 8 - 64); + ac = a; + ac -= a + b; + BOOST_CHECK_EQUAL(ac, -64); + ac = a; + ac -= b - a; + BOOST_CHECK_EQUAL(ac, 16 - 64); + ac = -a; + BOOST_CHECK_EQUAL(ac, -8); + ac = a; + ac -= -a; + BOOST_CHECK_EQUAL(ac, 16); + ac = a; + ac += -a; + BOOST_CHECK_EQUAL(ac, 0); + ac = b; + ac /= -a; + BOOST_CHECK_EQUAL(ac, -8); + ac = a; + ac *= -a; + BOOST_CHECK_EQUAL(ac, -64); + ac = a + -b; + BOOST_CHECK_EQUAL(ac, 8 - 64); + ac = -a + b; + BOOST_CHECK_EQUAL(ac, -8 + 64); + ac = -a + -b; + BOOST_CHECK_EQUAL(ac, -72); + ac = a + -+-b; // lots of unary operators!! + BOOST_CHECK_EQUAL(ac, 72); + test_conditional(big_uint_t(-a), -a); + + // + // Rval_tue ref tests: + // + a = 3; + b = 4; + c = big_uint_t(20) + -(a + b); + BOOST_CHECK_EQUAL(c, 13); + c = big_uint_t(20) + -a; + BOOST_CHECK_EQUAL(c, 17); + c = -a + big_uint_t(20); + BOOST_CHECK_EQUAL(c, 17); + c = -a + b; + BOOST_CHECK_EQUAL(c, 1); + c = b + -a; + BOOST_CHECK_EQUAL(c, 1); + a = 2; + b = 3; + c = big_uint_t(10) - a; + BOOST_CHECK_EQUAL(c, 8); + c = a - big_uint_t(2); + BOOST_CHECK_EQUAL(c, 0); + c = big_uint_t(3) - big_uint_t(2); + BOOST_CHECK_EQUAL(c, 1); + a = 20; + c = a - (a + b); + BOOST_CHECK_EQUAL(c, -3); + a = 2; + c = (a * b) - (a + b); + BOOST_CHECK_EQUAL(c, 1); + c = big_uint_t(20) - -(a + b); + BOOST_CHECK_EQUAL(c, 25); + c = big_uint_t(20) - (-a); + BOOST_CHECK_EQUAL(c, 22); + c = (-b) - big_uint_t(-5); + BOOST_CHECK_EQUAL(c, 2); + c = (-b) - a; + BOOST_CHECK_EQUAL(c, -5); + c = b - (-a); + BOOST_CHECK_EQUAL(c, 5); + c = big_uint_t(3) * -(a + b); + BOOST_CHECK_EQUAL(c, -15); + c = -(a + b) * big_uint_t(3); + BOOST_CHECK_EQUAL(c, -15); + c = big_uint_t(2) * -a; + BOOST_CHECK_EQUAL(c, -4); + c = -a * big_uint_t(2); + BOOST_CHECK_EQUAL(c, -4); + c = -a * b; + BOOST_CHECK_EQUAL(c, -6); + a = 2; + b = 4; + c = big_uint_t(4) / -a; + BOOST_CHECK_EQUAL(c, -2); + c = -b / big_uint_t(2); + BOOST_CHECK_EQUAL(c, -2); + c = big_uint_t(4) / -(2 * a); + BOOST_CHECK_EQUAL(c, -1); + c = b / -(2 * a); + BOOST_CHECK_EQUAL(c, -1); + c = -(2 * a) / big_uint_t(2); + BOOST_CHECK_EQUAL(c, -2); +} +template +void test_signed_ops(const std::integral_constant&) {} + +template +void test_basic_conditionals(big_uint_t a, big_uint_t b) { + if (a) { + BOOST_ERROR("Unexpected non-zero result"); + } + if (!a) { + } else { + BOOST_ERROR("Unexpected zero result"); + } + b = 2; + if (!b) { + BOOST_ERROR("Unexpected zero result"); + } + if (b) { + } else { + BOOST_ERROR("Unexpected non-zero result"); + } + if (a && b) { + BOOST_ERROR("Unexpected zero result"); + } + if (!(a || b)) { + BOOST_ERROR("Unexpected zero result"); + } + if (a + b) { + } else { + BOOST_ERROR("Unexpected zero result"); + } + if (b - 2) { + BOOST_ERROR("Unexpected non-zero result"); + } +} + +template +void test_relationals(T a, T b) { + BOOST_CHECK_EQUAL((a == b), false); + BOOST_CHECK_EQUAL((a != b), true); + BOOST_CHECK_EQUAL((a <= b), true); + BOOST_CHECK_EQUAL((a < b), true); + BOOST_CHECK_EQUAL((a >= b), false); + BOOST_CHECK_EQUAL((a > b), false); + + BOOST_CHECK_EQUAL((a + b == b), false); + BOOST_CHECK_EQUAL((a + b != b), true); + BOOST_CHECK_EQUAL((a + b >= b), true); + BOOST_CHECK_EQUAL((a + b > b), true); + BOOST_CHECK_EQUAL((a + b <= b), false); + BOOST_CHECK_EQUAL((a + b < b), false); + + BOOST_CHECK_EQUAL((a == b + a), false); + BOOST_CHECK_EQUAL((a != b + a), true); + BOOST_CHECK_EQUAL((a <= b + a), true); + BOOST_CHECK_EQUAL((a < b + a), true); + BOOST_CHECK_EQUAL((a >= b + a), false); + BOOST_CHECK_EQUAL((a > b + a), false); + + BOOST_CHECK_EQUAL((a + b == b + a), true); + BOOST_CHECK_EQUAL((a + b != b + a), false); + BOOST_CHECK_EQUAL((a + b <= b + a), true); + BOOST_CHECK_EQUAL((a + b < b + a), false); + BOOST_CHECK_EQUAL((a + b >= b + a), true); + BOOST_CHECK_EQUAL((a + b > b + a), false); + + BOOST_CHECK_EQUAL((8 == b + a), false); + BOOST_CHECK_EQUAL((8 != b + a), true); + BOOST_CHECK_EQUAL((8 <= b + a), true); + BOOST_CHECK_EQUAL((8 < b + a), true); + BOOST_CHECK_EQUAL((8 >= b + a), false); + BOOST_CHECK_EQUAL((8 > b + a), false); + BOOST_CHECK_EQUAL((800 == b + a), false); + BOOST_CHECK_EQUAL((800 != b + a), true); + BOOST_CHECK_EQUAL((800 >= b + a), true); + BOOST_CHECK_EQUAL((800 > b + a), true); + BOOST_CHECK_EQUAL((800 <= b + a), false); + BOOST_CHECK_EQUAL((800 < b + a), false); + BOOST_CHECK_EQUAL((72 == b + a), true); + BOOST_CHECK_EQUAL((72 != b + a), false); + BOOST_CHECK_EQUAL((72 <= b + a), true); + BOOST_CHECK_EQUAL((72 < b + a), false); + BOOST_CHECK_EQUAL((72 >= b + a), true); + BOOST_CHECK_EQUAL((72 > b + a), false); + + BOOST_CHECK_EQUAL((b + a == 8), false); + BOOST_CHECK_EQUAL((b + a != 8), true); + BOOST_CHECK_EQUAL((b + a >= 8), true); + BOOST_CHECK_EQUAL((b + a > 8), true); + BOOST_CHECK_EQUAL((b + a <= 8), false); + BOOST_CHECK_EQUAL((b + a < 8), false); + BOOST_CHECK_EQUAL((b + a == 800), false); + BOOST_CHECK_EQUAL((b + a != 800), true); + BOOST_CHECK_EQUAL((b + a <= 800), true); + BOOST_CHECK_EQUAL((b + a < 800), true); + BOOST_CHECK_EQUAL((b + a >= 800), false); + BOOST_CHECK_EQUAL((b + a > 800), false); + BOOST_CHECK_EQUAL((b + a == 72), true); + BOOST_CHECK_EQUAL((b + a != 72), false); + BOOST_CHECK_EQUAL((b + a >= 72), true); + BOOST_CHECK_EQUAL((b + a > 72), false); + BOOST_CHECK_EQUAL((b + a <= 72), true); + BOOST_CHECK_EQUAL((b + a < 72), false); + + T c; + // + // min and max overloads: + // +#if !defined(min) && !defined(max) + // using std::max; + // using std::min; + // This works, but still causes complaints from inspect.exe, so use brackets to prevent + // macrosubstitution, and to explicitly specify type T seems necessary, for reasons unclear. + a = 2; + b = 5; + c = 6; + BOOST_CHECK_EQUAL((std::min)(a, b), a); + BOOST_CHECK_EQUAL((std::min)(b, a), a); + BOOST_CHECK_EQUAL((std::max)(a, b), b); + BOOST_CHECK_EQUAL((std::max)(b, a), b); + BOOST_CHECK_EQUAL((std::min)(a, b + c), a); + BOOST_CHECK_EQUAL((std::min)(b + c, a), a); + BOOST_CHECK_EQUAL((std::min)(a, c - b), 1); + BOOST_CHECK_EQUAL((std::min)(c - b, a), 1); + BOOST_CHECK_EQUAL((std::max)(a, b + c), 11); + BOOST_CHECK_EQUAL((std::max)(b + c, a), 11); + BOOST_CHECK_EQUAL((std::max)(a, c - b), a); + BOOST_CHECK_EQUAL((std::max)(c - b, a), a); + BOOST_CHECK_EQUAL((std::min)(a + b, b + c), 7); + BOOST_CHECK_EQUAL((std::min)(b + c, a + b), 7); + BOOST_CHECK_EQUAL((std::max)(a + b, b + c), 11); + BOOST_CHECK_EQUAL((std::max)(b + c, a + b), 11); + BOOST_CHECK_EQUAL((std::min)(a + b, c - a), 4); + BOOST_CHECK_EQUAL((std::min)(c - a, a + b), 4); + BOOST_CHECK_EQUAL((std::max)(a + b, c - a), 7); + BOOST_CHECK_EQUAL((std::max)(c - a, a + b), 7); + + long l1(2), l2(3); + long l3 = (std::min)(l1, l2) + (std::max)(l1, l2) + (std::max)(l1, l2) + + (std::min)(l1, l2); + BOOST_CHECK_EQUAL(l3, 10); + +#endif +} + +template +const T& self(const T& a) { + return a; // NOLINT +} + +template +void test() { +#if !defined(NO_MIXED_OPS) && !defined(SLOW_COMPILER) + std::integral_constant tag; + test_mixed(tag); + test_mixed(tag); + test_mixed(tag); + test_mixed(tag); + test_mixed(tag); + test_mixed(tag); + test_mixed(tag); + test_mixed(tag); + test_mixed(tag); +#ifdef BOOST_HAS_LONG_LONG + test_mixed(tag); + test_mixed(tag); +#endif + +#endif +#ifndef MIXED_OPS_ONLY + // + // Integer only functions: + // + test_integer_ops(); + // + // Test basic arithmetic: + // + big_uint_t a(8); + big_uint_t b(64); + big_uint_t c(500); + big_uint_t d(1024); + BOOST_CHECK_EQUAL(a + b, 72); + a += b; + BOOST_CHECK_EQUAL(a, 72); + BOOST_CHECK_EQUAL(a - b, 8); + a -= b; + BOOST_CHECK_EQUAL(a, 8); + BOOST_CHECK_EQUAL(a * b, 8 * 64L); + a *= b; + BOOST_CHECK_EQUAL(a, 8 * 64L); + BOOST_CHECK_EQUAL(a / b, 8); + a /= b; + BOOST_CHECK_EQUAL(a, 8); + big_uint_t ac(a); + BOOST_CHECK_EQUAL(ac, a); + ac = a * c; + BOOST_CHECK_EQUAL(ac, 8 * 500L); + ac = 8 * 500L; + ac = ac + b + c; + BOOST_CHECK_EQUAL(ac, 8 * 500L + 64 + 500); + ac = a; + ac = b + c + ac; + BOOST_CHECK_EQUAL(ac, 8 + 64 + 500); + ac = ac - b + c; + BOOST_CHECK_EQUAL(ac, 8 + 64 + 500 - 64 + 500); + ac = a; + ac = b + c - ac; + BOOST_CHECK_EQUAL(ac, -8 + 64 + 500); + ac = a; + ac = ac * b; + BOOST_CHECK_EQUAL(ac, 8 * 64); + ac = a; + ac *= b * ac; + BOOST_CHECK_EQUAL(ac, 8 * 8 * 64); + ac = b; + ac = ac / a; + BOOST_CHECK_EQUAL(ac, 64 / 8); + ac = b; + ac /= ac / a; + BOOST_CHECK_EQUAL(ac, 64 / (64 / 8)); + ac = a; + ac = b + ac * a; + BOOST_CHECK_EQUAL(ac, 64 * 2); + ac = a; + ac = b - ac * a; + BOOST_CHECK_EQUAL(ac, 0); + ac = a; + ac = b * (ac + a); + BOOST_CHECK_EQUAL(ac, 64 * (16)); + ac = a; + ac = b / (ac * 1); + BOOST_CHECK_EQUAL(ac, 64 / 8); + ac = a; + ac = ac + b; + BOOST_CHECK_EQUAL(ac, 8 + 64); + ac = a; + ac = a + ac; + BOOST_CHECK_EQUAL(ac, 16); + ac = a; + ac = a - ac; + BOOST_CHECK_EQUAL(ac, 0); + ac = a; + ac += a + b; + BOOST_CHECK_EQUAL(ac, 80); + ac = a; + ac += b + a; + BOOST_CHECK_EQUAL(ac, 80); + ac = +a; + BOOST_CHECK_EQUAL(ac, 8); + ac = 8; + ac = a * ac; + BOOST_CHECK_EQUAL(ac, 8 * 8); + ac = a; + ac = a; + ac += +a; + BOOST_CHECK_EQUAL(ac, 16); + ac = a; + ac += b - a; + BOOST_CHECK_EQUAL(ac, 8 + 64 - 8); + ac = a; + ac += b * c; + BOOST_CHECK_EQUAL(ac, 8 + 64 * 500); + ac = a; + ac = a; + ac -= +a; + BOOST_CHECK_EQUAL(ac, 0); + ac = a; + if (std::numeric_limits::is_signed || + is_twos_complement_integer::value) { + ac = a; + ac -= c - b; + BOOST_CHECK_EQUAL(ac, 8 - (500 - 64)); + ac = a; + ac -= b * c; + BOOST_CHECK_EQUAL(ac, 8 - 500 * 64); + } + ac = a; + ac += ac * b; + BOOST_CHECK_EQUAL(ac, 8 + 8 * 64); + if (std::numeric_limits::is_signed || + is_twos_complement_integer::value) { + ac = a; + ac -= ac * b; + BOOST_CHECK_EQUAL(ac, 8 - 8 * 64); + } + ac = a * 8; + ac *= +a; + BOOST_CHECK_EQUAL(ac, 64 * 8); + ac = a; + ac *= b * c; + BOOST_CHECK_EQUAL(ac, 8 * 64 * 500); + ac = a; + ac *= b / a; + BOOST_CHECK_EQUAL(ac, 8 * 64 / 8); + ac = a; + ac *= b + c; + BOOST_CHECK_EQUAL(ac, 8 * (64 + 500)); + ac = b; + ac /= +a; + BOOST_CHECK_EQUAL(ac, 8); + ac = b; + ac /= b / a; + BOOST_CHECK_EQUAL(ac, 64 / (64 / 8)); + ac = b; + ac /= a + big_uint_t(0); + BOOST_CHECK_EQUAL(ac, 8); + // + // simple tests with immediate values, these calls can be optimised in many backends: + // + ac = a + b; + BOOST_CHECK_EQUAL(ac, 72); + ac = a + +b; + BOOST_CHECK_EQUAL(ac, 72); + ac = +a + b; + BOOST_CHECK_EQUAL(ac, 72); + ac = +a + +b; + BOOST_CHECK_EQUAL(ac, 72); + ac = a; + ac = b / ac; + BOOST_CHECK_EQUAL(ac, b / a); + // + // Comparisons: + // + test_relationals(a, b); + test_members(a); + // + // Use in Boolean context: + // + a = 0; + b = 2; + test_basic_conditionals(a, b); + // + // Test iostreams: + // + std::stringstream ss; + a = 20; + b = 2; + ss << a; + ss >> c; + BOOST_CHECK_EQUAL(a, c); + ss.clear(); + ss << a + b; + ss >> c; + BOOST_CHECK_EQUAL(c, 22); + BOOST_CHECK_EQUAL(c, a + b); + // + // More cases for complete code coverage: + // + a = 20; + b = 30; + using std::swap; + swap(a, b); + BOOST_CHECK_EQUAL(a, 30); + BOOST_CHECK_EQUAL(b, 20); + a = 20; + b = 30; + std::swap(a, b); + BOOST_CHECK_EQUAL(a, 30); + BOOST_CHECK_EQUAL(b, 20); + a = 20; + b = 30; + a = a + b * 2; + BOOST_CHECK_EQUAL(a, 20 + 30 * 2); + a = 100; + a = a - b * 2; + BOOST_CHECK_EQUAL(a, 100 - 30 * 2); + a = 20; + a = a * (b + 2); + BOOST_CHECK_EQUAL(a, 20 * (32)); + a = 20; + a = (b + 2) * a; + BOOST_CHECK_EQUAL(a, 20 * (32)); + a = 90; + b = 2; + a = a / (b + 0); + BOOST_CHECK_EQUAL(a, 45); + a = 20; + b = 30; + c = (a * b) + 22; + BOOST_CHECK_EQUAL(c, 20 * 30 + 22); + c = 22 + (a * b); + BOOST_CHECK_EQUAL(c, 20 * 30 + 22); + c = 10; + ac = a + b * c; + BOOST_CHECK_EQUAL(ac, 20 + 30 * 10); + ac = b * c + a; + BOOST_CHECK_EQUAL(ac, 20 + 30 * 10); + a = a + b * c; + BOOST_CHECK_EQUAL(a, 20 + 30 * 10); + a = 20; + b = a + b * c; + BOOST_CHECK_EQUAL(b, 20 + 30 * 10); + b = 30; + c = a + b * c; + BOOST_CHECK_EQUAL(c, 20 + 30 * 10); + c = 10; + c = a + b / c; + BOOST_CHECK_EQUAL(c, 20 + 30 / 10); + // + // Additional tests for rvalue ref overloads: + // + a = 3; + b = 4; + c = big_uint_t(2) + a; + BOOST_CHECK_EQUAL(c, 5); + c = a + big_uint_t(2); + BOOST_CHECK_EQUAL(c, 5); + c = big_uint_t(3) + big_uint_t(2); + BOOST_CHECK_EQUAL(c, 5); + c = big_uint_t(2) + (a + b); + BOOST_CHECK_EQUAL(c, 9); + c = (a + b) + big_uint_t(2); + BOOST_CHECK_EQUAL(c, 9); + c = (a + b) + (a + b); + BOOST_CHECK_EQUAL(c, 14); + c = a * big_uint_t(4); + BOOST_CHECK_EQUAL(c, 12); + c = big_uint_t(3) * big_uint_t(4); + BOOST_CHECK_EQUAL(c, 12); + c = (a + b) * (a + b); + BOOST_CHECK_EQUAL(c, 49); + a = 2; + c = b / big_uint_t(2); + BOOST_CHECK_EQUAL(c, 2); + c = big_uint_t(4) / a; + BOOST_CHECK_EQUAL(c, 2); + c = big_uint_t(4) / big_uint_t(2); + BOOST_CHECK_EQUAL(c, 2); + // + // Test conditionals: + // + a = 20; + test_conditional(a, +a); + test_conditional(a, (a + 0)); + + test_signed_ops( + std::integral_constant::is_signed>()); + // + // Test hashing: + // + boost::hash hasher; + std::size_t s = hasher(a); + BOOST_CHECK_NE(s, 0); + std::hash hasher2; + s = hasher2(a); + BOOST_CHECK_NE(s, 0); + + // + // Test move: + // + big_uint_t m(static_cast(a)); + BOOST_CHECK_EQUAL(m, 20); + // Move from already moved from object: + big_uint_t m2(static_cast(a)); + // assign from moved from object + // (may result in "a" being left in valid state as implementation artifact): + c = static_cast(a); + // assignment to moved-from objects: + c = static_cast(m); + BOOST_CHECK_EQUAL(c, 20); + m2 = c; + BOOST_CHECK_EQUAL(c, 20); + // Destructor of "a" checks destruction of moved-from-object... + big_uint_t m3(static_cast(a)); +#ifndef BOOST_MP_NOT_TESTING_NUMBER + // + // string and string_view: + // + { + std::string s1("2"); + big_uint_t x(s1); + BOOST_CHECK_EQUAL(x, 2); + s1 = "3"; + // x.assign(s1); + x = s1; + BOOST_CHECK_EQUAL(x, 3); +#ifndef BOOST_NO_CXX17_HDR_STRING_VIEW + s1 = "20"; + std::string_view v(s1.c_str(), 1); + big_uint_t y(v); + BOOST_CHECK_EQUAL(y, 2); + std::string_view v2(s1.c_str(), 2); + // y.assign(v2); + y = v2; + BOOST_CHECK_EQUAL(y, 20); +#endif + } +#endif + // + // Bug cases, self assignment first: + // + a = 20; + a = self(a); + BOOST_CHECK_EQUAL(a, 20); + + a = 2; + a = a * a * a; + BOOST_CHECK_EQUAL(a, 8); + a = 2; + a = a + a + a; + BOOST_CHECK_EQUAL(a, 6); + a = 2; + a = a - a + a; // NOLINT + BOOST_CHECK_EQUAL(a, 2); + a = 2; + a = a + a - a; + BOOST_CHECK_EQUAL(a, 2); + a = 2; + a = a * a - a; + BOOST_CHECK_EQUAL(a, 2); + a = 2; + a = a + a * a; + BOOST_CHECK_EQUAL(a, 6); + a = 2; + a = (a + a) * a; + BOOST_CHECK_EQUAL(a, 8); +#endif +} + +BOOST_AUTO_TEST_CASE(boost_arithmetic_big_uint_test) { + test>(); + test>(); + test>(); + test>(); + test>(); + test>(); + test>(); +}