diff --git a/config/Jamfile.v2 b/config/Jamfile.v2 index 0daf721b9..c81c34025 100644 --- a/config/Jamfile.v2 +++ b/config/Jamfile.v2 @@ -76,6 +76,7 @@ exe has_eigen : has_eigen.cpp ; exe has_f2c : has_f2c.cpp f2c ; obj has_is_constant_evaluated : has_is_constant_evaluated.cpp ; obj has_constexpr_limits : has_constexpr_limits_cmd.cpp : -fconstexpr-ops-limit=268435456 ; +obj has_constexpr_dynamic_memory : has_constexpr_dynamic_memory.cpp : -std=c++2a ; obj has_big_obj : has_big_obj.cpp : -Wa,-mbig-obj ; obj is_ci_sanitizer_run : is_ci_sanitizer_run.cpp ; @@ -90,6 +91,7 @@ explicit has_mpc ; explicit has_eigen ; explicit has_is_constant_evaluated ; explicit has_constexpr_limits ; +explicit has_constexpr_dynamic_memory ; explicit has_big_obj ; explicit has_f2c ; explicit is_ci_sanitizer_run ; diff --git a/config/has_constexpr_dynamic_memory.cpp b/config/has_constexpr_dynamic_memory.cpp new file mode 100644 index 000000000..081857560 --- /dev/null +++ b/config/has_constexpr_dynamic_memory.cpp @@ -0,0 +1,25 @@ +// Copyright John Maddock 2025. +// Use, modification and distribution are subject to 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) + +#include + + +#if !defined(__cpp_constexpr_dynamic_alloc) +# error "__cpp_constexpr_dynamic_alloc is not defined" + +#elif __cpp_constexpr_dynamic_alloc < 201907 +# error "__cpp_constexpr_dynamic_alloc is defined with value < 201907" + +#elif !defined(__cpp_lib_constexpr_dynamic_alloc) +# error "__cpp_lib_constexpr_dynamic_alloc is not defined" + +#elif __cpp_lib_constexpr_dynamic_alloc < 201907 +# error "__cpp_lib_constexpr_dynamic_alloc is defined with value < 201907" +#endif + +int main() +{ + return 0; +} diff --git a/include/boost/multiprecision/cpp_int.hpp b/include/boost/multiprecision/cpp_int.hpp index efcf50cad..94f7c63ec 100644 --- a/include/boost/multiprecision/cpp_int.hpp +++ b/include/boost/multiprecision/cpp_int.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,91 @@ namespace detail { { static constexpr std::size_t value = (Value1 > Value2) ? Value1 : Value2; }; + + template + static BOOST_MP_CXX14_CONSTEXPR DestT* constexpr_copy(DestT* dest, const SrcT* src, std::size_t n) + { + bool use_loop = !std::is_same::value; + +# ifndef BOOST_MP_NO_CONSTEXPR_DETECTION + use_loop = use_loop || BOOST_MP_IS_CONST_EVALUATED(n); +# endif + + if (use_loop) + { + for (std::size_t i = 0; i < n; ++i) + dest[i] = src[i]; + } + else + { + std::memcpy(dest, src, n * sizeof(DestT)); + } + + return dest + n; + } + + template + static BOOST_MP_CXX14_CONSTEXPR DestT* constexpr_zero_trivial(DestT* dest, std::size_t n) + { +# ifndef BOOST_MP_NO_CONSTEXPR_DETECTION + if (BOOST_MP_IS_CONST_EVALUATED(n)) + { + for (std::size_t i = 0; i < n; ++i) + dest[i] = 0; + } + else +# endif + { + std::memset(static_cast(dest), 0, n * sizeof(DestT)); + } + + return dest + n; + } + + template + static BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR + typename std::allocator_traits::pointer constexpr_allocate_default_constructible(Allocator& alloc, std::size_t const n) + { + typename std::allocator_traits::pointer ptr = alloc.allocate(n); + +# if !defined(BOOST_MP_NO_CONSTEXPR_DETECTION) && defined(BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC) + if (BOOST_MP_IS_CONST_EVALUATED(n)) + for (std::size_t i = 0; i < n; ++i) + std::construct_at(ptr + i); +# endif + + return ptr; + } + + template + static BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR + void constexpr_deallocate_trivially_destructible(Allocator& alloc, typename std::allocator_traits::pointer data, std::size_t const n) + { +# if !defined(BOOST_MP_NO_CONSTEXPR_DETECTION) && defined(BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC) + if (BOOST_MP_IS_CONST_EVALUATED(n)) + for (std::size_t i = 0; i < n; ++i) + std::destroy_at(data + i); +# endif + + alloc.deallocate(data, n); + } + + static BOOST_MP_CXX14_CONSTEXPR std::size_t constexpr_strlen(const char* str) + { +# ifndef BOOST_MP_NO_CONSTEXPR_DETECTION + if (BOOST_MP_IS_CONST_EVALUATED(str)) { + const char* end = str; + + while (*end != '\0') { + end++; + } + + return end - str; + } +# endif + + return std::strlen(str); + } } // Namespace detail template @@ -210,37 +296,45 @@ struct cpp_int_base(0u); - static constexpr limb_type sign_bit_mask = static_cast(1u) << (limb_bits - 1); - static constexpr std::size_t internal_limb_count = - MinBits - ? (MinBits / limb_bits + ((MinBits % limb_bits) ? 1 : 0)) - : (sizeof(limb_data) / sizeof(limb_type)) > 1 ? (sizeof(limb_data) / sizeof(limb_type)) : 2; + static constexpr std::size_t limb_bits = sizeof(limb_type) * CHAR_BIT; + static constexpr limb_type max_limb_value = ~static_cast(0u); + static constexpr limb_type sign_bit_mask = static_cast(1u) << (limb_bits - 1); + + private: + static constexpr std::size_t min_bits_limb_capacity = (MinBits / limb_bits + ((MinBits % limb_bits) ? 1 : 0)); + static constexpr std::size_t limb_data_limb_capacity = sizeof(limb_data) / sizeof(limb_type); + static constexpr std::size_t double_limb_type_limb_capacity = sizeof(double_limb_type) / sizeof(limb_type); + + public: + static constexpr std::size_t internal_limb_count = (std::max)( + min_bits_limb_capacity, (std::max)(limb_data_limb_capacity, double_limb_type_limb_capacity) + ); + private: union data_type { limb_data ld; limb_type la[internal_limb_count]; - limb_type first; - double_limb_type double_first; - constexpr data_type() noexcept : first(0) {} - constexpr data_type(limb_type i) noexcept : first(i) {} - constexpr data_type(signed_limb_type i) noexcept : first(static_cast(boost::multiprecision::detail::unsigned_abs(i))) {} + constexpr data_type() noexcept : la{0} {} + constexpr data_type(limb_type i) noexcept : la{i} {} + constexpr data_type(signed_limb_type i) noexcept : data_type(static_cast(boost::multiprecision::detail::unsigned_abs(i))) {} #if BOOST_MP_ENDIAN_LITTLE_BYTE - constexpr data_type(double_limb_type i) noexcept : double_first(i) - {} - constexpr data_type(signed_double_limb_type i) noexcept : double_first(static_cast(boost::multiprecision::detail::unsigned_abs(i))) {} + constexpr data_type(double_limb_type i) noexcept : la{} + { + for (std::size_t limb_idx = 0; limb_idx < double_limb_type_limb_capacity; ++limb_idx, i >>= limb_bits) + la[limb_idx] = static_cast(i & max_limb_value); + } + constexpr data_type(signed_double_limb_type i) noexcept : data_type(static_cast(boost::multiprecision::detail::unsigned_abs(i))) {} #endif #if !defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX) && !(defined(BOOST_MSVC) && (BOOST_MSVC < 1900)) - constexpr data_type(limb_type* limbs, std::size_t len) noexcept : ld{ len, limbs } + constexpr data_type(limb_type* limbs, std::size_t len) noexcept : ld{len, limbs} {} #else constexpr data_type(limb_type* limbs, std::size_t len) noexcept { ld.capacity = len; - ld.data = limbs; + ld.data = limbs; } #endif }; @@ -290,33 +384,33 @@ struct cpp_int_base::get(); } + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR allocator_type& allocator() noexcept { return boost::multiprecision::detail::empty_value::get(); } public: - scoped_shared_storage(const allocator_type& a, std::size_t len) + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR scoped_shared_storage(const allocator_type& a, std::size_t len) : boost::multiprecision::detail::empty_value(boost::multiprecision::detail::empty_init_t(), a), capacity(len), allocated(0), is_alias(false) { - data = allocator().allocate(len); + data = detail::constexpr_allocate_default_constructible(allocator(), len); } - scoped_shared_storage(const cpp_int_base& i, std::size_t len) + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR scoped_shared_storage(const cpp_int_base& i, std::size_t len) : boost::multiprecision::detail::empty_value(boost::multiprecision::detail::empty_init_t(), i.allocator()), capacity(len), allocated(0), is_alias(false) { - data = allocator().allocate(len); + data = detail::constexpr_allocate_default_constructible(allocator(), len); } - scoped_shared_storage(limb_type* limbs, std::size_t n) : data(limbs), capacity(n), allocated(0), is_alias(true) {} - ~scoped_shared_storage() + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR scoped_shared_storage(limb_type* limbs, std::size_t n) : data(limbs), capacity(n), allocated(0), is_alias(true) {} + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR ~scoped_shared_storage() { if(!is_alias) - allocator().deallocate(data, capacity); + detail::constexpr_deallocate_trivially_destructible(allocator(), data, capacity); } - limb_type* allocate(std::size_t n) noexcept + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR limb_type* allocate(std::size_t n) noexcept { limb_type* result = data + allocated; allocated += n; BOOST_MP_ASSERT(allocated <= capacity); - return result; + return result; } - void deallocate(std::size_t n) + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR void deallocate(std::size_t n) { BOOST_MP_ASSERT(n <= allocated); allocated -= n; @@ -337,7 +431,7 @@ struct cpp_int_base(o)), m_limbs(o.m_limbs), m_sign(o.m_sign), m_internal(o.m_internal), m_alias(o.m_alias) { if (m_internal) { - std::memcpy(limbs(), o.limbs(), o.size() * sizeof(limbs()[0])); + detail::constexpr_copy(limbs(), o.limbs(), o.size()); } else { @@ -427,67 +521,73 @@ struct cpp_int_base(this) = static_cast(o); - m_limbs = o.m_limbs; - m_sign = o.m_sign; - m_internal = o.m_internal; - m_alias = o.m_alias; - if (m_internal) - { - std::memcpy(limbs(), o.limbs(), o.size() * sizeof(limbs()[0])); - } - else + if (static_cast(this) != static_cast(&o)) { - m_data.ld = o.m_data.ld; - o.m_limbs = 0; - o.m_internal = true; + if (!m_internal && !m_alias) + detail::constexpr_deallocate_trivially_destructible(allocator(), m_data.ld.data, m_data.ld.capacity); + *static_cast(this) = static_cast(o); + m_limbs = o.m_limbs; + m_sign = o.m_sign; + m_internal = o.m_internal; + m_alias = o.m_alias; + if (m_internal) + { + detail::constexpr_copy(limbs(), o.limbs(), o.size()); + } + else + { + m_data.ld = o.m_data.ld; + o.m_limbs = 0; + o.m_internal = true; + } } return *this; } template - cpp_int_base& operator=(cpp_int_base&& o) noexcept + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR cpp_int_base& operator=(cpp_int_base&& o) noexcept { - if(o.m_internal) + if (static_cast(this) != static_cast(&o)) { - m_sign = o.m_sign; - this->resize(o.size(), o.size()); - std::memcpy(this->limbs(), o.limbs(), o.size() * sizeof(*(o.limbs()))); - return *this; + if(o.m_internal) + { + m_sign = o.m_sign; + this->resize(o.size(), o.size()); + detail::constexpr_copy(this->limbs(), o.limbs(), o.size()); + return *this; + } + if (!m_internal && !m_alias) + detail::constexpr_deallocate_trivially_destructible(allocator(), m_data.ld.data, m_data.ld.capacity); + *static_cast(this) = static_cast::base_type&&>(o); + m_limbs = o.m_limbs; + m_sign = o.m_sign; + m_internal = o.m_internal; + m_alias = o.m_alias; + m_data.ld.capacity = o.m_data.ld.capacity; + m_data.ld.data = o.limbs(); + o.m_limbs = 0; + o.m_internal = true; } - if (!m_internal && !m_alias) - allocator().deallocate(m_data.ld.data, m_data.ld.capacity); - *static_cast(this) = static_cast::base_type&&>(o); - m_limbs = o.m_limbs; - m_sign = o.m_sign; - m_internal = o.m_internal; - m_alias = o.m_alias; - m_data.ld.capacity = o.m_data.ld.capacity; - m_data.ld.data = o.limbs(); - o.m_limbs = 0; - o.m_internal = true; return *this; } - BOOST_MP_FORCEINLINE ~cpp_int_base() noexcept + BOOST_MP_FORCEINLINE BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR ~cpp_int_base() noexcept { if (!m_internal && !m_alias) - allocator().deallocate(limbs(), capacity()); + detail::constexpr_deallocate_trivially_destructible(allocator(), limbs(), capacity()); } - void assign(const cpp_int_base& o) + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR void assign(const cpp_int_base& o) { - if (this != &o) + if (static_cast(this) != static_cast(&o)) { static_cast(*this) = static_cast(o); m_limbs = 0; resize(o.size(), o.size()); - std::memcpy(limbs(), o.limbs(), o.size() * sizeof(limbs()[0])); + detail::constexpr_copy(limbs(), o.limbs(), o.size()); m_sign = o.m_sign; } } - BOOST_MP_FORCEINLINE void negate() noexcept + BOOST_MP_FORCEINLINE BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR void negate() noexcept { m_sign = !m_sign; // Check for zero value: @@ -497,11 +597,11 @@ struct cpp_int_base - void check_in_range(const A&) noexcept {} + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR void check_in_range(const A&) noexcept {} }; template @@ -678,18 +778,10 @@ struct cpp_int_base void BOOST_MP_CXX14_CONSTEXPR assign(const cpp_int_base& o) noexcept { - if (this != &o) + if (static_cast(this) != static_cast(&o)) { m_limbs = o.m_limbs; -#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION - if (BOOST_MP_IS_CONST_EVALUATED(m_limbs)) - { - for (std::size_t i = 0; i < m_limbs; ++i) - limbs()[i] = o.limbs()[i]; - } - else -#endif - std::memcpy(limbs(), o.limbs(), o.size() * sizeof(o.limbs()[0])); + detail::constexpr_copy(limbs(), o.limbs(), o.size()); m_sign = o.m_sign; } } @@ -872,18 +964,10 @@ struct cpp_int_base BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR void assign(const cpp_int_base& o) noexcept { - if (this != &o) + if (static_cast(this) != static_cast(&o)) { m_limbs = o.m_limbs; -#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION - if (BOOST_MP_IS_CONST_EVALUATED(m_limbs)) - { - for (std::size_t i = 0; i < m_limbs; ++i) - limbs()[i] = o.limbs()[i]; - } - else -#endif - std::memcpy(limbs(), o.limbs(), o.size() * sizeof(limbs()[0])); + detail::constexpr_copy(limbs(), o.limbs(), o.size()); } } @@ -1437,26 +1521,17 @@ struct cpp_int_backend { // regular non-trivial to non-trivial assign: this->resize(other.size(), other.size()); - -#if !defined(BOOST_MP_HAS_IS_CONSTANT_EVALUATED) && !defined(BOOST_MP_HAS_BUILTIN_IS_CONSTANT_EVALUATED) && !defined(BOOST_NO_CXX14_CONSTEXPR) std::size_t count = (std::min)(other.size(), this->size()); + + // gcc-6 tests start failing without this check +# if !defined(BOOST_MP_HAS_IS_CONSTANT_EVALUATED) && !defined(BOOST_MP_HAS_BUILTIN_IS_CONSTANT_EVALUATED) && !defined(BOOST_NO_CXX14_CONSTEXPR) for (std::size_t i = 0; i < count; ++i) this->limbs()[i] = other.limbs()[i]; -#else -#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION - if (BOOST_MP_IS_CONST_EVALUATED(other.size())) - { - std::size_t count = (std::min)(other.size(), this->size()); - for (std::size_t i = 0; i < count; ++i) - this->limbs()[i] = other.limbs()[i]; - } - else -#endif - { - static_assert(sizeof(other.limbs()[0]) == sizeof(this->limbs()[0]), "This method requires equal limb sizes"); - std::memcpy(this->limbs(), other.limbs(), (std::min)(other.size() * sizeof(other.limbs()[0]), this->size() * sizeof(this->limbs()[0]))); - } -#endif + +# else + detail::constexpr_copy(this->limbs(), other.limbs(), count); +# endif + this->sign(other.sign()); this->normalize(); } @@ -1698,9 +1773,9 @@ struct cpp_int_backend } private: - void do_assign_string(const char* s, const std::integral_constant&) + BOOST_MP_CXX14_CONSTEXPR void do_assign_string(const char* s, const std::integral_constant&) { - std::size_t n = s ? std::strlen(s) : 0; + std::size_t n = s ? detail::constexpr_strlen(s) : 0; *this = 0; unsigned radix = 10; bool isneg = false; @@ -1726,7 +1801,7 @@ struct cpp_int_backend } if (n) { - unsigned val; + unsigned val = 0; while (*s) { if (*s >= '0' && *s <= '9') @@ -1749,11 +1824,11 @@ struct cpp_int_backend if (isneg) this->negate(); } - void do_assign_string(const char* s, const std::integral_constant&) + BOOST_MP_CXX14_CONSTEXPR void do_assign_string(const char* s, const std::integral_constant&) { using default_ops::eval_add; using default_ops::eval_multiply; - std::size_t n = s ? std::strlen(s) : 0; + std::size_t n = s ? detail::constexpr_strlen(s) : 0; *this = static_cast(0u); unsigned radix = 10; bool isneg = false; @@ -1789,16 +1864,16 @@ struct cpp_int_backend { while (*s == '0') ++s; - std::size_t bitcount = 4 * std::strlen(s); - limb_type val; - std::size_t limb, shift; + std::size_t bitcount = 4 * detail::constexpr_strlen(s); + limb_type val = 0; + std::size_t limb = 0, shift = 0; if (bitcount > 4) bitcount -= 4; else bitcount = 0; std::size_t newsize = bitcount / (sizeof(limb_type) * CHAR_BIT) + 1; result.resize(static_cast(newsize), static_cast(newsize)); // will throw if this is a checked integer that cannot be resized - std::memset(result.limbs(), 0, result.size() * sizeof(limb_type)); + detail::constexpr_zero_trivial(result.limbs(), result.size()); while (*s) { if (*s >= '0' && *s <= '9') @@ -1831,16 +1906,16 @@ struct cpp_int_backend { while (*s == '0') ++s; - std::size_t bitcount = 3 * std::strlen(s); - limb_type val; - std::size_t limb, shift; + std::size_t bitcount = 3 * detail::constexpr_strlen(s); + limb_type val = 0; + std::size_t limb = 0, shift = 0; if (bitcount > 3) bitcount -= 3; else bitcount = 0; std::size_t newsize = bitcount / (sizeof(limb_type) * CHAR_BIT) + 1; result.resize(static_cast(newsize), static_cast(newsize)); // will throw if this is a checked integer that cannot be resized - std::memset(result.limbs(), 0, result.size() * sizeof(limb_type)); + detail::constexpr_zero_trivial(result.limbs(), result.size()); while (*s) { if (*s >= '0' && *s <= '7') @@ -1892,7 +1967,7 @@ struct cpp_int_backend limb_type block = 0; for (unsigned i = 0; i < digits_per_block_10; ++i) { - limb_type val; + limb_type val = 0; if (*s >= '0' && *s <= '9') val = static_cast(*s - '0'); else @@ -1922,7 +1997,7 @@ struct cpp_int_backend } public: - cpp_int_backend& operator=(const char* s) + BOOST_MP_CXX14_CONSTEXPR cpp_int_backend& operator=(const char* s) { do_assign_string(s, trivial_tag()); return *this; @@ -2157,7 +2232,7 @@ struct cpp_int_backend if (newsize) { this->resize(newsize, newsize); // May throw - std::memset(this->limbs(), 0, this->size()); + detail::constexpr_zero_trivial(this->limbs(), this->size()); typename Container::const_iterator i(c.begin()), j(c.end()); std::size_t byte_location = static_cast(c.size() - 1); while (i != j) diff --git a/include/boost/multiprecision/cpp_int/cpp_int_config.hpp b/include/boost/multiprecision/cpp_int/cpp_int_config.hpp index 58bcce72b..23447aa61 100644 --- a/include/boost/multiprecision/cpp_int/cpp_int_config.hpp +++ b/include/boost/multiprecision/cpp_int/cpp_int_config.hpp @@ -129,7 +129,7 @@ using signed_double_limb_type = detail::largest_signed_type<64>::type ; constexpr limb_type max_block_10 = 1000000000; constexpr limb_type digits_per_block_10 = 9; -inline limb_type block_multiplier(std::size_t count) +inline BOOST_MP_CXX14_CONSTEXPR limb_type block_multiplier(std::size_t count) { constexpr limb_type values[digits_per_block_10] = {10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; BOOST_MP_ASSERT(count < digits_per_block_10); diff --git a/include/boost/multiprecision/cpp_int/limits.hpp b/include/boost/multiprecision/cpp_int/limits.hpp index fa29cc48b..52d23e2f1 100644 --- a/include/boost/multiprecision/cpp_int/limits.hpp +++ b/include/boost/multiprecision/cpp_int/limits.hpp @@ -24,25 +24,28 @@ inline BOOST_CXX14_CONSTEXPR_IF_DETECTION boost::multiprecision::number&, const std::integral_constant&, const std::integral_constant&) { // Bounded, signed, and no allocator. - using result_type = boost::multiprecision::number, ExpressionTemplates> ; + using result_type = boost::multiprecision::number, ExpressionTemplates>; using ui_type = boost::multiprecision::number, ExpressionTemplates>; #ifdef BOOST_MP_NO_CONSTEXPR_DETECTION static #else constexpr #endif - const result_type val = -result_type(~ui_type(0)); + const result_type val = -result_type(~ui_type(0)); return val; } template -inline boost::multiprecision::number, ExpressionTemplates> +inline BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION boost::multiprecision::number, ExpressionTemplates> get_min(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Bounded, signed, and an allocator (can't be constexpr). - using result_type = boost::multiprecision::number, ExpressionTemplates> ; + // Bounded, signed, and an allocator (can be constexpr if allocations are constexpr). + using result_type = boost::multiprecision::number, ExpressionTemplates>; using ui_type = boost::multiprecision::number, ExpressionTemplates>; - static const result_type val = -result_type(~ui_type(0)); +#ifdef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + static +#endif + const result_type val = -result_type(~ui_type(0)); return val; } @@ -50,7 +53,7 @@ template , ExpressionTemplates> get_min(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Bounded, unsigned, no allocator (can be constexpr): + // Bounded, unsigned, no allocator (can always be constexpr): #ifdef BOOST_MP_NO_CONSTEXPR_DETECTION static #else @@ -61,30 +64,39 @@ get_min(const std::integral_constant&, const std::integral_constant< } template -inline boost::multiprecision::number, ExpressionTemplates> +inline BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION boost::multiprecision::number, ExpressionTemplates> get_min(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Bounded and std::size_t with allocator (no constexpr): - static const boost::multiprecision::number, ExpressionTemplates> val(0u); + // Bounded and std::size_t with allocator (can be constexpr if allocations are constexpr): +#ifdef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + static +#endif + const boost::multiprecision::number, ExpressionTemplates> val(0u); return val; } template -inline boost::multiprecision::number, ExpressionTemplates> +inline BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION boost::multiprecision::number, ExpressionTemplates> get_min(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Unbounded and signed, never constexpr because there must be an allocator. + // Unbounded and signed, can be constexpr if allocations are constexpr. // There is no minimum value, just return 0: - static const boost::multiprecision::number, ExpressionTemplates> val(0u); +#ifdef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + static +#endif + const boost::multiprecision::number, ExpressionTemplates> val(0u); return val; } template -inline boost::multiprecision::number, ExpressionTemplates> +inline BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION boost::multiprecision::number, ExpressionTemplates> get_min(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Unbound and unsigned, never constexpr because there must be an allocator. - static const boost::multiprecision::number, ExpressionTemplates> val(0u); + // Unbound and unsigned, can be constexpr if allocations are constexpr. +#ifdef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + static +#endif + const boost::multiprecision::number, ExpressionTemplates> val(0u); return val; } @@ -92,26 +104,29 @@ template , ExpressionTemplates> get_max(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Bounded and signed, no allocator, can be constexpr. - using result_type = boost::multiprecision::number, ExpressionTemplates> ; + // Bounded and signed, no allocator, can always be constexpr. + using result_type = boost::multiprecision::number, ExpressionTemplates>; using ui_type = boost::multiprecision::number, ExpressionTemplates>; #ifdef BOOST_MP_NO_CONSTEXPR_DETECTION static #else constexpr #endif - const result_type val = ~ui_type(0); + const result_type val = ~ui_type(0); return val; } template -inline boost::multiprecision::number, ExpressionTemplates> +inline BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION boost::multiprecision::number, ExpressionTemplates> get_max(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Bounded and signed, has an allocator, never constexpr. - using result_type = boost::multiprecision::number, ExpressionTemplates> ; + // Bounded and signed, has an allocator, can be constexpr if allocations are constexpr. + using result_type = boost::multiprecision::number, ExpressionTemplates>; using ui_type = boost::multiprecision::number, ExpressionTemplates>; - static const result_type val = ~ui_type(0); +#ifdef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + static +#endif + const result_type val = ~ui_type(0); return val; } @@ -119,45 +134,54 @@ template , ExpressionTemplates> get_max(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Bound and unsigned, no allocator so can be constexpr: - using result_type = boost::multiprecision::number, ExpressionTemplates> ; + // Bounded and unsigned, no allocator so can always be constexpr: + using result_type = boost::multiprecision::number, ExpressionTemplates>; using ui_type = boost::multiprecision::number, ExpressionTemplates>; #ifdef BOOST_MP_NO_CONSTEXPR_DETECTION static #else constexpr #endif - const result_type val = ~ui_type(0); + const result_type val = ~ui_type(0); return val; } template -inline boost::multiprecision::number, ExpressionTemplates> +inline BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION boost::multiprecision::number, ExpressionTemplates> get_max(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Bound and unsigned, has an allocator so can never be constexpr: - using result_type = boost::multiprecision::number, ExpressionTemplates> ; + // Bounded and unsigned, has an allocator, can be constexpr if allocations are constexpr: + using result_type = boost::multiprecision::number, ExpressionTemplates>; using ui_type = boost::multiprecision::number, ExpressionTemplates>; - static const result_type val = ~ui_type(0); +#ifdef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + static +#endif + const result_type val = ~ui_type(0); return val; } template -inline boost::multiprecision::number, ExpressionTemplates> +inline BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION boost::multiprecision::number, ExpressionTemplates> get_max(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Unbounded and signed. + // Unbounded and signed, can be constexpr if allocations are constexpr. // There is no maximum value, just return 0: - static const boost::multiprecision::number, ExpressionTemplates> val(0u); +#ifdef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + static +#endif + const boost::multiprecision::number, ExpressionTemplates> val(0u); return val; } template -inline boost::multiprecision::number, ExpressionTemplates> +inline BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION boost::multiprecision::number, ExpressionTemplates> get_max(const std::integral_constant&, const std::integral_constant&, const std::integral_constant&) { - // Unbound and unsigned: - static const boost::multiprecision::number, ExpressionTemplates> val(0u); + // Unbound and unsigned, can be constexpr if allocations are constexpr: +#ifdef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + static +#endif + const boost::multiprecision::number, ExpressionTemplates> val(0u); return val; } @@ -167,7 +191,7 @@ template , ExpressionTemplates> > { using backend_type = boost::multiprecision::cpp_int_backend; - using number_type = boost::multiprecision::number ; + using number_type = boost::multiprecision::number; public: static constexpr bool is_specialized = true; diff --git a/include/boost/multiprecision/detail/empty_value.hpp b/include/boost/multiprecision/detail/empty_value.hpp index 401e5eca8..19e5538a5 100644 --- a/include/boost/multiprecision/detail/empty_value.hpp +++ b/include/boost/multiprecision/detail/empty_value.hpp @@ -25,7 +25,7 @@ namespace boost { namespace multiprecision { namespace detail { template -struct use_empty_value_base +struct use_empty_value_base { #if defined(BOOST_DETAIL_EMPTY_VALUE_BASE) static constexpr bool value = __is_empty(T) && !__is_final(T); @@ -38,7 +38,7 @@ struct empty_init_t {}; namespace empty_impl { -template ::value> class empty_value { @@ -48,15 +48,16 @@ class empty_value public: using type = T; - empty_value() = default; - explicit empty_value(boost::multiprecision::detail::empty_init_t) : value_ {} {} + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR empty_value() = default; + explicit BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR empty_value(boost::multiprecision::detail::empty_init_t) + : value_ {} {} template - empty_value(boost::multiprecision::detail::empty_init_t, U&& value, Args&&... args) : - value_ {std::forward(value), std::forward(args)...} {} + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR empty_value(boost::multiprecision::detail::empty_init_t, U&& value, Args&&... args) + : value_ {std::forward(value), std::forward(args)...} {} - const T& get() const noexcept { return value_; } - T& get() noexcept { return value_; } + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR const T& get() const noexcept { return value_; } + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR T& get() noexcept { return value_; } }; template @@ -65,15 +66,16 @@ class empty_value : T public: using type = T; - empty_value() = default; - explicit empty_value(boost::multiprecision::detail::empty_init_t) : T{} {} + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR empty_value() = default; + explicit BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR empty_value(boost::multiprecision::detail::empty_init_t) + : T{} {} template - empty_value(boost::multiprecision::detail::empty_init_t, U&& value, Args&&... args) : - T{std::forward(value), std::forward(args)...} {} + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR empty_value(boost::multiprecision::detail::empty_init_t, U&& value, Args&&... args) + : T{std::forward(value), std::forward(args)...} {} - const T& get() const noexcept { return *this; } - T& get() noexcept { return *this; } + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR const T& get() const noexcept { return *this; } + BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR T& get() noexcept { return *this; } }; } // Namespace empty impl diff --git a/include/boost/multiprecision/detail/number_base.hpp b/include/boost/multiprecision/detail/number_base.hpp index 99017c92e..ea48dff0d 100644 --- a/include/boost/multiprecision/detail/number_base.hpp +++ b/include/boost/multiprecision/detail/number_base.hpp @@ -102,6 +102,33 @@ # define BOOST_CXX14_CONSTEXPR_IF_DETECTION constexpr #endif +#if defined(__cpp_constexpr_dynamic_alloc) && __cpp_constexpr_dynamic_alloc >= 201907L \ + && defined(__cpp_lib_constexpr_dynamic_alloc) && __cpp_lib_constexpr_dynamic_alloc >= 201907L +# undef BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC +# define BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC + +# undef BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR +# define BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR constexpr + +#else +# undef BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC +# define BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR +#endif + +#if !defined(BOOST_MP_NO_CONSTEXPR_DETECTION) && defined(BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC) +# undef BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION +# define BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION constexpr + +# undef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + +#else +# undef BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION +# define BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION + +# undef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION +# define BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION +#endif + #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable : 6326) diff --git a/include/boost/multiprecision/detail/standalone_config.hpp b/include/boost/multiprecision/detail/standalone_config.hpp index 95377fd25..32ca0af70 100644 --- a/include/boost/multiprecision/detail/standalone_config.hpp +++ b/include/boost/multiprecision/detail/standalone_config.hpp @@ -158,4 +158,32 @@ namespace boost { namespace multiprecision { # endif #endif +#if defined(__cpp_constexpr_dynamic_alloc) && __cpp_constexpr_dynamic_alloc >= 201907L \ + && defined(__cpp_lib_constexpr_dynamic_alloc) && __cpp_lib_constexpr_dynamic_alloc >= 201907L +# undef BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC +# define BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC + +# undef BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR +# define BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR constexpr + +#else +# undef BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC +# define BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR +#endif + +#if !defined(BOOST_MP_NO_CONSTEXPR_DETECTION) && defined(BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC) +# undef BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION +# define BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION constexpr + +# undef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION + +#else +# undef BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION +# define BOOST_MP_CXX20_DYNAMIC_ALLOC_CONSTEXPR_IF_DETECTION + +# undef BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION +# define BOOST_MP_NO_CXX20_DYNAMIC_ALLOC_CONSTEXPR_DETECTION +#endif + + #endif // BOOST_MP_STANDALONE_CONFIG_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6338807cb..80400882c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1029,6 +1029,8 @@ test-suite misc : [ run constexpr_test_cpp_int_5.cpp : : : [ requires cxx14_constexpr cxx17_if_constexpr ] [ check-target-builds ../config//has_is_constant_evaluated : : no ] ] [ run constexpr_test_cpp_int_6.cpp : : : [ requires cxx14_constexpr cxx17_if_constexpr ] [ check-target-builds ../config//has_is_constant_evaluated : msvc:-constexpr:steps10000000 clang:-fconstexpr-steps=268435456 : no ] [ check-target-builds ../config//has_constexpr_limits : -fconstexpr-ops-limit=268435456 ] ] [ run constexpr_test_cpp_int_7.cpp : : : [ requires cxx14_constexpr cxx17_if_constexpr ] [ check-target-builds ../config//has_is_constant_evaluated : msvc:-constexpr:steps10000000 clang:-fconstexpr-steps=268435456 : no ] ] + [ run constexpr_test_dynamic_cpp_int_1.cpp : : : [ requires cxx14_constexpr cxx17_if_constexpr ] [ check-target-builds ../config//has_constexpr_dynamic_memory : msvc:-constexpr\:steps10000000 clang:-fconstexpr-steps=268435456 -std=c++2a : no ] ] + [ run constexpr_test_dynamic_cpp_int_2.cpp : : : [ requires cxx14_constexpr cxx17_if_constexpr ] [ check-target-builds ../config//has_constexpr_dynamic_memory : msvc:-constexpr\:steps10000000 clang:-fconstexpr-steps=268435456 -std=c++2a : no ] ] [ compile test_nothrow_cpp_int.cpp ] [ compile test_nothrow_cpp_rational.cpp ] @@ -1297,7 +1299,8 @@ test-suite standalone : [ run standalone_constexpr_test_cpp_int.cpp : : : [ requires cxx14_constexpr cxx17_if_constexpr ] [ check-target-builds ../config//has_is_constant_evaluated : : no ] ] [ compile standalone_constexpr_test_float128.cpp : [ requires cxx14_constexpr cxx17_if_constexpr ] [ check-target-builds ../config//has_float128 : quadmath : no ] ] - + [ run standalone_constexpr_test_dynamic_cpp_int.cpp : : : [ requires cxx14_constexpr cxx17_if_constexpr ] [ check-target-builds ../config//has_constexpr_dynamic_memory : msvc:-constexpr\:steps10000000 clang:-fconstexpr-steps=268435456 -std=c++2a : no ] ] + [ run standalone_test_arithmetic_complex128.cpp : : : [ check-target-builds ../config//has_float128 : quadmath ] ] [ run standalone_test_arithmetic_cpp_bin_float.cpp no_eh_support : : : msvc:-bigobj [ check-target-builds ../config//has_float128 : quadmath ] ] [ run standalone_test_arithmetic_cpp_dec_float.cpp no_eh_support : : : msvc:-bigobj [ check-target-builds ../config//has_float128 : quadmath ] ] diff --git a/test/constexpr_test_dynamic_cpp_int.hpp b/test/constexpr_test_dynamic_cpp_int.hpp new file mode 100644 index 000000000..752951f53 --- /dev/null +++ b/test/constexpr_test_dynamic_cpp_int.hpp @@ -0,0 +1,494 @@ +// (C) Copyright John Maddock 2025. +// Use, modification and distribution are subject to 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) + +#include +#include +#include "boost/multiprecision/cpp_int.hpp" +#include "test.hpp" + + +#if !defined(BOOST_MP_NO_CONSTEXPR_DETECTION) \ + && !defined(DISABLE_TESTS) \ + && defined(BOOST_MP_HAS_CONSTEXPR_DYNAMIC_ALLOC) + +// some operations fail with "Member call on variable whose lifetime has ended" +// when using boost::multiprecision::et_on +typedef boost::multiprecision::number< + boost::multiprecision::cpp_int_backend<0, 32>, + boost::multiprecision::et_off +> int_backend_32; + +typedef boost::multiprecision::number< + boost::multiprecision::cpp_int_backend<0, 64>, + boost::multiprecision::et_off +> int_backend_64; + +typedef boost::multiprecision::number< + boost::multiprecision::cpp_int_backend<0, 128>, + boost::multiprecision::et_off +> int_backend_128; + +typedef boost::multiprecision::number< + boost::multiprecision::cpp_int_backend<0, 256>, + boost::multiprecision::et_off +> int_backend_256; + +typedef boost::multiprecision::number< + boost::multiprecision::cpp_int_backend<0, 512>, + boost::multiprecision::et_off +> int_backend_512; + +typedef boost::multiprecision::number< + boost::multiprecision::cpp_int_backend<>, + boost::multiprecision::et_off +> int_backend_inf; + +typedef boost::multiprecision::number< + boost::multiprecision::cpp_int_backend<0, 32>, + boost::multiprecision::et_on +> int_backend_32_et; + +typedef boost::multiprecision::number< + boost::multiprecision::cpp_int_backend<>, + boost::multiprecision::et_on +> int_backend_inf_et; + +template +constexpr T factorial(T n) +{ + T result = 1; + + for (T k = 2; k <= n; ++k) + result *= k; + + return result; +} + +template +constexpr T fibonacci(T n) +{ + T a = 0; + T b = 1; + + for (T k = 0; k < n; ++k) + { + T c = a + b; + a = std::move(b); + b = std::move(c); + } + + return a; +} + +inline int run_constexpr_cpp_int_tests() +{ + constexpr std::int32_t i32_min = (std::numeric_limits::min)(); + constexpr std::uint32_t u32_max = (std::numeric_limits::max)(); + + { + // construction from integral + static_assert(int_backend_32(u32_max) == u32_max); + static_assert(int_backend_64(u32_max) == u32_max); + static_assert(int_backend_128(u32_max) == u32_max); + static_assert(int_backend_256(u32_max) == u32_max); + static_assert(int_backend_512(u32_max) == u32_max); + static_assert(int_backend_inf(u32_max) == u32_max); + + static_assert(int_backend_32(i32_min) == i32_min); + static_assert(int_backend_64(i32_min) == i32_min); + static_assert(int_backend_128(i32_min) == i32_min); + static_assert(int_backend_256(i32_min) == i32_min); + static_assert(int_backend_512(i32_min) == i32_min); + static_assert(int_backend_inf(i32_min) == i32_min); + } + { + // construction from floating point +# if defined(__cpp_lib_constexpr_cmath) && __cpp_lib_constexpr_cmath >= 202306L + static_assert(int_backend_32(12345.12345) == 12345); + static_assert(int_backend_64(12345.12345) == 12345); + static_assert(int_backend_128(12345.12345) == 12345); + static_assert(int_backend_256(12345.12345) == 12345); + static_assert(int_backend_512(12345.12345) == 12345); + + static_assert(int_backend_32(-12345.12345) == -12345); + static_assert(int_backend_64(-12345.12345) == -12345); + static_assert(int_backend_128(-12345.12345) == -12345); + static_assert(int_backend_256(-12345.12345) == -12345); + static_assert(int_backend_512(-12345.12345) == -12345); +# endif + } + { + // construction from string + static_assert(int_backend_32("12345678") == 12345678); + static_assert(int_backend_32("-12345678") == -12345678); + static_assert(int_backend_32("0xb0057") == 720983); + static_assert(int_backend_32("-0xb0057") == -720983); + static_assert(int_backend_32("0777") == 511); + static_assert(int_backend_32("-0777") == -511); + + static_assert(int_backend_64("12345678") == 12345678); + static_assert(int_backend_64("-12345678") == -12345678); + static_assert(int_backend_64("0xb0057") == 720983); + static_assert(int_backend_64("-0xb0057") == -720983); + static_assert(int_backend_64("0777") == 511); + static_assert(int_backend_64("-0777") == -511); + + static_assert(int_backend_128("12345678") == 12345678); + static_assert(int_backend_128("-12345678") == -12345678); + static_assert(int_backend_128("0xb0057") == 720983); + static_assert(int_backend_128("-0xb0057") == -720983); + static_assert(int_backend_128("0777") == 511); + static_assert(int_backend_128("-0777") == -511); + + static_assert(int_backend_256("12345678") == 12345678); + static_assert(int_backend_256("-12345678") == -12345678); + static_assert(int_backend_256("0xb0057") == 720983); + static_assert(int_backend_256("-0xb0057") == -720983); + static_assert(int_backend_256("0777") == 511); + static_assert(int_backend_256("-0777") == -511); + + static_assert(int_backend_512("12345678") == 12345678); + static_assert(int_backend_512("-12345678") == -12345678); + static_assert(int_backend_512("0xb0057") == 720983); + static_assert(int_backend_512("-0xb0057") == -720983); + static_assert(int_backend_512("0777") == 511); + static_assert(int_backend_512("-0777") == -511); + + static_assert(int_backend_inf("12345678") == 12345678); + static_assert(int_backend_inf("-12345678") == -12345678); + static_assert(int_backend_inf("0xb0057") == 720983); + static_assert(int_backend_inf("-0xb0057") == -720983); + static_assert(int_backend_inf("0777") == 511); + static_assert(int_backend_inf("-0777") == -511); + + static_assert(int_backend_inf_et("12345678") == 12345678); + static_assert(int_backend_inf_et("-12345678") == -12345678); + static_assert(int_backend_inf_et("0xb0057") == 720983); + static_assert(int_backend_inf_et("-0xb0057") == -720983); + static_assert(int_backend_inf_et("0777") == 511); + static_assert(int_backend_inf_et("-0777") == -511); + } + { + // construction from cpp_int + static_assert(int_backend_32(int_backend_32(12345678)) == 12345678); + static_assert(int_backend_32(int_backend_64(12345678)) == 12345678); + static_assert(int_backend_32(int_backend_128(12345678)) == 12345678); + static_assert(int_backend_32(int_backend_256(12345678)) == 12345678); + static_assert(int_backend_32(int_backend_512(12345678)) == 12345678); + static_assert(int_backend_32(int_backend_inf(12345678)) == 12345678); + static_assert(int_backend_32(int_backend_inf_et(12345678)) == 12345678); + static_assert(int_backend_32(boost::multiprecision::uint1024_t(12345678)) == 12345678); + static_assert(int_backend_32(boost::multiprecision::int1024_t(12345678)) == 12345678); + static_assert(int_backend_32(boost::multiprecision::int1024_t(-12345678)) == -12345678); + + static_assert(int_backend_64(int_backend_32(12345678)) == 12345678); + static_assert(int_backend_64(int_backend_64(12345678)) == 12345678); + static_assert(int_backend_64(int_backend_128(12345678)) == 12345678); + static_assert(int_backend_64(int_backend_256(12345678)) == 12345678); + static_assert(int_backend_64(int_backend_512(12345678)) == 12345678); + static_assert(int_backend_64(int_backend_inf(12345678)) == 12345678); + static_assert(int_backend_64(int_backend_inf_et(12345678)) == 12345678); + static_assert(int_backend_64(boost::multiprecision::uint1024_t(12345678)) == 12345678); + static_assert(int_backend_64(boost::multiprecision::int1024_t(12345678)) == 12345678); + static_assert(int_backend_64(boost::multiprecision::int1024_t(-12345678)) == -12345678); + + static_assert(int_backend_128(int_backend_32(12345678)) == 12345678); + static_assert(int_backend_128(int_backend_64(12345678)) == 12345678); + static_assert(int_backend_128(int_backend_128(12345678)) == 12345678); + static_assert(int_backend_128(int_backend_256(12345678)) == 12345678); + static_assert(int_backend_128(int_backend_512(12345678)) == 12345678); + static_assert(int_backend_128(int_backend_inf(12345678)) == 12345678); + static_assert(int_backend_128(int_backend_inf_et(12345678)) == 12345678); + static_assert(int_backend_128(boost::multiprecision::uint1024_t(12345678)) == 12345678); + static_assert(int_backend_128(boost::multiprecision::int1024_t(12345678)) == 12345678); + static_assert(int_backend_128(boost::multiprecision::int1024_t(-12345678)) == -12345678); + + static_assert(int_backend_256(int_backend_32(12345678)) == 12345678); + static_assert(int_backend_256(int_backend_64(12345678)) == 12345678); + static_assert(int_backend_256(int_backend_128(12345678)) == 12345678); + static_assert(int_backend_256(int_backend_256(12345678)) == 12345678); + static_assert(int_backend_256(int_backend_512(12345678)) == 12345678); + static_assert(int_backend_256(int_backend_inf(12345678)) == 12345678); + static_assert(int_backend_256(int_backend_inf_et(12345678)) == 12345678); + static_assert(int_backend_256(boost::multiprecision::uint1024_t(12345678)) == 12345678); + static_assert(int_backend_256(boost::multiprecision::int1024_t(12345678)) == 12345678); + static_assert(int_backend_256(boost::multiprecision::int1024_t(-12345678)) == -12345678); + + static_assert(int_backend_512(int_backend_32(12345678)) == 12345678); + static_assert(int_backend_512(int_backend_64(12345678)) == 12345678); + static_assert(int_backend_512(int_backend_128(12345678)) == 12345678); + static_assert(int_backend_512(int_backend_256(12345678)) == 12345678); + static_assert(int_backend_512(int_backend_512(12345678)) == 12345678); + static_assert(int_backend_512(int_backend_inf(12345678)) == 12345678); + static_assert(int_backend_512(int_backend_inf_et(12345678)) == 12345678); + static_assert(int_backend_512(boost::multiprecision::uint1024_t(12345678)) == 12345678); + static_assert(int_backend_512(boost::multiprecision::int1024_t(12345678)) == 12345678); + static_assert(int_backend_512(boost::multiprecision::int1024_t(-12345678)) == -12345678); + + static_assert(int_backend_inf(int_backend_32(12345678)) == 12345678); + static_assert(int_backend_inf(int_backend_64(12345678)) == 12345678); + static_assert(int_backend_inf(int_backend_128(12345678)) == 12345678); + static_assert(int_backend_inf(int_backend_256(12345678)) == 12345678); + static_assert(int_backend_inf(int_backend_inf(12345678)) == 12345678); + static_assert(int_backend_inf(int_backend_inf(12345678)) == 12345678); + static_assert(int_backend_inf(int_backend_inf_et(12345678)) == 12345678); + static_assert(int_backend_inf(boost::multiprecision::uint1024_t(12345678)) == 12345678); + static_assert(int_backend_inf(boost::multiprecision::int1024_t(12345678)) == 12345678); + static_assert(int_backend_inf(boost::multiprecision::int1024_t(-12345678)) == -12345678); + + static_assert(int_backend_inf_et(int_backend_32(12345678)) == 12345678); + static_assert(int_backend_inf_et(int_backend_64(12345678)) == 12345678); + static_assert(int_backend_inf_et(int_backend_128(12345678)) == 12345678); + static_assert(int_backend_inf_et(int_backend_256(12345678)) == 12345678); + static_assert(int_backend_inf_et(int_backend_inf(12345678)) == 12345678); + static_assert(int_backend_inf_et(int_backend_inf(12345678)) == 12345678); + static_assert(int_backend_inf_et(int_backend_inf_et(12345678)) == 12345678); + static_assert(int_backend_inf_et(boost::multiprecision::uint1024_t(12345678)) == 12345678); + static_assert(int_backend_inf_et(boost::multiprecision::int1024_t(12345678)) == 12345678); + static_assert(int_backend_inf_et(boost::multiprecision::int1024_t(-12345678)) == -12345678); + } + { + // cast to integral + static_assert(static_cast(int_backend_32(12345678)) == 12345678U); + static_assert(static_cast(int_backend_64(12345678)) == 12345678U); + static_assert(static_cast(int_backend_128(12345678)) == 12345678U); + static_assert(static_cast(int_backend_256(12345678)) == 12345678U); + static_assert(static_cast(int_backend_512(12345678)) == 12345678U); + static_assert(static_cast(int_backend_inf(12345678)) == 12345678U); + static_assert(static_cast(int_backend_inf_et(12345678)) == 12345678U); + + static_assert(static_cast(int_backend_32(12345678)) == 12345678); + static_assert(static_cast(int_backend_64(12345678)) == 12345678); + static_assert(static_cast(int_backend_128(12345678)) == 12345678); + static_assert(static_cast(int_backend_256(12345678)) == 12345678); + static_assert(static_cast(int_backend_512(12345678)) == 12345678); + static_assert(static_cast(int_backend_inf(12345678)) == 12345678); + static_assert(static_cast(int_backend_inf_et(12345678)) == 12345678); + + static_assert(static_cast(int_backend_32(-12345678)) == -12345678); + static_assert(static_cast(int_backend_64(-12345678)) == -12345678); + static_assert(static_cast(int_backend_128(-12345678)) == -12345678); + static_assert(static_cast(int_backend_256(-12345678)) == -12345678); + static_assert(static_cast(int_backend_512(-12345678)) == -12345678); + static_assert(static_cast(int_backend_inf(-12345678)) == -12345678); + static_assert(static_cast(int_backend_inf_et(-12345678)) == -12345678); + } + { +# if defined(__cpp_lib_constexpr_cmath) && __cpp_lib_constexpr_cmath >= 202306L + // cast to floating point only works for values that fit on a limb_type + static_assert(static_cast(int_backend_32(12345678)) == 12345678.0); + static_assert(static_cast(int_backend_64(12345678)) == 12345678.0); + static_assert(static_cast(int_backend_128(12345678)) == 12345678.0); + static_assert(static_cast(int_backend_256(12345678)) == 12345678.0); + static_assert(static_cast(int_backend_512(12345678)) == 12345678.0); + static_assert(static_cast(int_backend_inf(12345678)) == 12345678.0); + + static_assert(static_cast(int_backend_32(-12345678)) == -12345678.0); + static_assert(static_cast(int_backend_64(-12345678)) == -12345678.0); + static_assert(static_cast(int_backend_128(-12345678)) == -12345678.0); + static_assert(static_cast(int_backend_256(-12345678)) == -12345678.0); + static_assert(static_cast(int_backend_512(-12345678)) == -12345678.0); + static_assert(static_cast(int_backend_inf(-12345678)) == -12345678.0); + + static_assert(static_cast(int_backend_128("295147905179352825856")) == 295147905179352825856.0); + static_assert(static_cast(int_backend_256("295147905179352825856")) == 295147905179352825856.0); + static_assert(static_cast(int_backend_512("295147905179352825856")) == 295147905179352825856.0); + static_assert(static_cast(int_backend_inf("295147905179352825856")) == 295147905179352825856.0); +# endif + } + { + // bit functions + static_assert(boost::multiprecision::msb(int_backend_inf(1) << 500) == 500); + static_assert(boost::multiprecision::lsb(int_backend_inf(1) << 500) == 500); + static_assert(boost::multiprecision::bit_test(int_backend_inf(1) << 500, 500)); + + static_assert(boost::multiprecision::msb(int_backend_inf_et(1) << 500) == 500); + static_assert(boost::multiprecision::lsb(int_backend_inf_et(1) << 500) == 500); + static_assert(boost::multiprecision::bit_test(int_backend_inf_et(1) << 500, 500)); + + static_assert([] () constexpr noexcept -> int_backend_inf { + int_backend_inf a = 0; + return boost::multiprecision::bit_set(a, 500); + }() == (int_backend_inf(1) << 500)); + + static_assert([] () constexpr noexcept -> int_backend_inf { + int_backend_inf a = int_backend_inf(1) << 500; + return boost::multiprecision::bit_set(a, 500); + }() == (int_backend_inf(1) << 500)); + + static_assert([] () constexpr noexcept -> int_backend_inf { + int_backend_inf a = 0; + return boost::multiprecision::bit_unset(a, 500); + }() == 0); + + static_assert([] () constexpr noexcept -> int_backend_inf { + int_backend_inf a = int_backend_inf(1) << 500; + return boost::multiprecision::bit_unset(a, 500); + }() == 0); + + static_assert([] () constexpr noexcept -> int_backend_inf { + int_backend_inf a = 0; + return boost::multiprecision::bit_flip(a, 500); + }() == (int_backend_inf(1) << 500)); + + static_assert([] () constexpr noexcept -> int_backend_inf { + int_backend_inf a = int_backend_inf(1) << 500; + return boost::multiprecision::bit_flip(a, 500); + }() == 0); + } + { + // math operations + static_assert([] () constexpr noexcept { + int_backend_inf const a((std::numeric_limits::max)()); + int_backend_inf const b = -(a * a * a * a * a * a * a * a * a) + (a * a) - (a * a * a); + int_backend_inf q; + int_backend_inf r; + + boost::multiprecision::divide_qr(b, int_backend_inf(5), q, r); + return ((q + r) + 1) * -3; + }() == int_backend_inf( + "298393941220594703994056741294897077225167327209541862571731534281447031079324380613832" + )); + } + { + // boost integral functions + static_assert( + boost::multiprecision::powm( + int_backend_inf(123), 95, int_backend_inf("347524627690950719232228793293134097314") + ) == int_backend_inf("139046631754840920912974963240540036501") + ); + + static_assert( + boost::multiprecision::gcd( + int_backend_inf("19045691428043967582979989657743215704243597051686049733150514095808"), + int_backend_inf("13707112918842075981093200133901249713363763549144813434262669433146") + ) == 2 + ); + + static_assert( + boost::multiprecision::sqrt( + int_backend_inf("19045691428043967582979989657743215704243597051686049733150514095808") + ) == int_backend_inf("4364136962567051192681890285117644") + ); + + static_assert( + boost::multiprecision::abs( + int_backend_inf("19045691428043967582979989657743215704243597051686049733150514095808") + ) == int_backend_inf("19045691428043967582979989657743215704243597051686049733150514095808") + ); + + static_assert( + boost::multiprecision::abs( + int_backend_inf("-19045691428043967582979989657743215704243597051686049733150514095808") + ) == int_backend_inf("19045691428043967582979989657743215704243597051686049733150514095808") + ); + + static_assert( + boost::multiprecision::integer_modulus( + int_backend_inf("19045691428043967582979989657743215704243597051686049733150514095808"), + u32_max + ) == 2377187843 + ); + } + { + // handwritten functions + static_assert( + factorial(int_backend_512(50)) + == int_backend_512("30414093201713378043612608166064768844377641568960512000000000000") + ); + + static_assert( + factorial(int_backend_inf(50)) + == int_backend_inf("30414093201713378043612608166064768844377641568960512000000000000") + ); + + static_assert( + factorial(int_backend_inf_et(50)) + == int_backend_inf_et("30414093201713378043612608166064768844377641568960512000000000000") + ); + + static_assert(factorial(int_backend_32(12)) == 479001600); + static_assert(factorial(int_backend_32_et(12)) == 479001600); + + static_assert( + fibonacci(int_backend_512(200)) + == int_backend_512("280571172992510140037611932413038677189525") + ); + + static_assert( + fibonacci(int_backend_inf(200)) + == int_backend_inf("280571172992510140037611932413038677189525") + ); + + static_assert( + fibonacci(int_backend_inf_et(200)) + == int_backend_inf_et("280571172992510140037611932413038677189525") + ); + + static_assert(fibonacci(int_backend_32(47)) == 2971215073); + static_assert(fibonacci(int_backend_32_et(47)) == 2971215073); + } + { + // limits + static_assert((std::numeric_limits::min)() == -int_backend_32(u32_max)); + static_assert((std::numeric_limits::max)() == u32_max); + + static_assert((std::numeric_limits::min)() == -int_backend_32_et(u32_max)); + static_assert((std::numeric_limits::max)() == u32_max); + + // limits + static_assert( + (std::numeric_limits::min)() + == int_backend_inf( + "-13407807929942597099574024998205846127479365820592393377723561443721764030073546" + "976801874298166903427690031858186486050853753882811946569946433649006084095" + ) + ); + + static_assert( + (std::numeric_limits::min)() + == int_backend_inf( + "-0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ) + ); + + static_assert( + (std::numeric_limits::min)() + == int_backend_inf( + "-03777777777777777777777777777777777777777777777777777777777777777777777777777777777777" + "77777777777777777777777777777777777777777777777777777777777777777777777777777777777777" + ) + ); + + static_assert( + (std::numeric_limits::max)() + == int_backend_inf( + "13407807929942597099574024998205846127479365820592393377723561443721764030073546" + "976801874298166903427690031858186486050853753882811946569946433649006084095" + ) + ); + + static_assert( + (std::numeric_limits::max)() + == int_backend_inf( + "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ) + ); + + static_assert( + (std::numeric_limits::max)() + == int_backend_inf( + "03777777777777777777777777777777777777777777777777777777777777777777777777777777777777" + "77777777777777777777777777777777777777777777777777777777777777777777777777777777777777" + ) + ); + } + return boost::report_errors(); +} +#else +inline int run_constexpr_cpp_int_tests(){} +#endif diff --git a/test/constexpr_test_dynamic_cpp_int_1.cpp b/test/constexpr_test_dynamic_cpp_int_1.cpp new file mode 100644 index 000000000..41222baa7 --- /dev/null +++ b/test/constexpr_test_dynamic_cpp_int_1.cpp @@ -0,0 +1,14 @@ +// (C) Copyright John Maddock 2025. +// Use, modification and distribution are subject to 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) + +#include +#undef BOOST_HAS_INT128 + +#include "constexpr_test_dynamic_cpp_int.hpp" + + +int main(){ + return run_constexpr_cpp_int_tests(); +} diff --git a/test/constexpr_test_dynamic_cpp_int_2.cpp b/test/constexpr_test_dynamic_cpp_int_2.cpp new file mode 100644 index 000000000..64f52d064 --- /dev/null +++ b/test/constexpr_test_dynamic_cpp_int_2.cpp @@ -0,0 +1,18 @@ +// (C) Copyright John Maddock 2025. +// Use, modification and distribution are subject to 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) + +#include + +#include "constexpr_test_dynamic_cpp_int.hpp" + + +int main(){ +# if defined(BOOST_HAS_INT128) // BOOST_HAS_INT128 unset is already tested + return run_constexpr_cpp_int_tests(); + +# else + return 0; +# endif +} diff --git a/test/standalone_constexpr_test_dynamic_cpp_int.cpp b/test/standalone_constexpr_test_dynamic_cpp_int.cpp new file mode 100644 index 000000000..a537b5318 --- /dev/null +++ b/test/standalone_constexpr_test_dynamic_cpp_int.cpp @@ -0,0 +1,13 @@ +// (C) Copyright John Maddock 2025. +// Use, modification and distribution are subject to 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_MP_STANDALONE + +#include "constexpr_test_dynamic_cpp_int.hpp" + + +int main(){ + return run_constexpr_cpp_int_tests(); +}