/////////////////////////////////////////////////////////////// // Copyright 2012 John Maddock. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt // // Comparison operators for cpp_int_backend: // #ifndef BOOST_MP_CPP_INT_ADD_HPP #define BOOST_MP_CPP_INT_ADD_HPP #include #include namespace boost { namespace multiprecision { namespace backends { // // As above, but for adding a single limb to a non-trivial cpp_int: // template inline BOOST_MP_CXX14_CONSTEXPR void add_unsigned(CppInt1& result, const CppInt2& a, const limb_type& o) noexcept(is_non_throwing_cpp_int::value) { // Addition using modular arithmetic. // Nothing fancy, just let uintmax_t take the strain: if (&result != &a) result.resize(a.size(), a.size()); double_limb_type carry = o; typename CppInt1::limb_pointer pr = result.limbs(); typename CppInt2::const_limb_pointer pa = a.limbs(); unsigned i = 0; // Addition with carry until we either run out of digits or carry is zero: for (; carry && (i < result.size()); ++i) { carry += static_cast(pa[i]); #ifdef __MSVC_RUNTIME_CHECKS pr[i] = static_cast(carry & ~static_cast(0)); #else pr[i] = static_cast(carry); #endif carry >>= CppInt1::limb_bits; } // Just copy any remaining digits: if (&a != &result) { std_constexpr::copy(pa + i, pa + a.size(), pr + i); } if (carry) { // We overflowed, need to add one more limb: unsigned x = result.size(); result.resize(x + 1, x + 1); if (result.size() > x) result.limbs()[x] = static_cast(carry); } result.normalize(); result.sign(a.sign()); } // // And again to subtract a single limb: // template inline BOOST_MP_CXX14_CONSTEXPR void subtract_unsigned(CppInt1& result, const CppInt2& a, const limb_type& b) noexcept(is_non_throwing_cpp_int::value) { // Subtract one limb. // Nothing fancy, just let uintmax_t take the strain: constexpr double_limb_type borrow = static_cast(CppInt1::max_limb_value) + 1; result.resize(a.size(), a.size()); typename CppInt1::limb_pointer pr = result.limbs(); typename CppInt2::const_limb_pointer pa = a.limbs(); if (*pa >= b) { *pr = *pa - b; if (&result != &a) { std_constexpr::copy(pa + 1, pa + a.size(), pr + 1); result.sign(a.sign()); } else if ((result.size() == 1) && (*pr == 0)) { result.sign(false); // zero is unsigned. } } else if (result.size() == 1) { *pr = b - *pa; result.sign(!a.sign()); } else { *pr = static_cast((borrow + *pa) - b); unsigned i = 1; while (!pa[i]) { pr[i] = CppInt1::max_limb_value; ++i; } pr[i] = pa[i] - 1; if (&result != &a) { ++i; std_constexpr::copy(pa + i, pa + a.size(), pr + i); } result.normalize(); result.sign(a.sign()); } } // // Now the actual functions called by the front end, all of which forward to one of the above: // template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value && !is_trivial_cpp_int >::value>::type eval_add( cpp_int_backend& result, const cpp_int_backend& o) noexcept((is_non_throwing_cpp_int >::value)) { eval_add(result, result, o); } template inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value && !is_trivial_cpp_int >::value && !is_trivial_cpp_int >::value>::type eval_add( cpp_int_backend& result, const cpp_int_backend& a, const cpp_int_backend& b) noexcept((is_non_throwing_cpp_int >::value)) { if (a.sign() != b.sign()) { subtract_unsigned(result, a, b); return; } add_unsigned(result, a, b); } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value>::type eval_add(cpp_int_backend& result, const limb_type& o) noexcept((is_non_throwing_cpp_int >::value)) { if (result.sign()) { subtract_unsigned(result, result, o); } else add_unsigned(result, result, o); } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value && !is_trivial_cpp_int >::value>::type eval_add( cpp_int_backend& result, const cpp_int_backend& a, const limb_type& o) noexcept((is_non_throwing_cpp_int >::value)) { if (a.sign()) { subtract_unsigned(result, a, o); } else add_unsigned(result, a, o); } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value>::type eval_add( cpp_int_backend& result, const signed_limb_type& o) noexcept((is_non_throwing_cpp_int >::value)) { if (o < 0) eval_subtract(result, static_cast(boost::multiprecision::detail::unsigned_abs(o))); else if (o > 0) eval_add(result, static_cast(o)); } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value && !is_trivial_cpp_int >::value>::type eval_add( cpp_int_backend& result, const cpp_int_backend& a, const signed_limb_type& o) noexcept((is_non_throwing_cpp_int >::value)) { if (o < 0) eval_subtract(result, a, static_cast(boost::multiprecision::detail::unsigned_abs(o))); else if (o > 0) eval_add(result, a, static_cast(o)); else if (&result != &a) result = a; } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value>::type eval_subtract( cpp_int_backend& result, const limb_type& o) noexcept((is_non_throwing_cpp_int >::value)) { if (result.sign()) { add_unsigned(result, result, o); } else subtract_unsigned(result, result, o); } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value && !is_trivial_cpp_int >::value>::type eval_subtract( cpp_int_backend& result, const cpp_int_backend& a, const limb_type& o) noexcept((is_non_throwing_cpp_int >::value)) { if (a.sign()) { add_unsigned(result, a, o); } else { subtract_unsigned(result, a, o); } } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value>::type eval_subtract( cpp_int_backend& result, const signed_limb_type& o) noexcept((is_non_throwing_cpp_int >::value)) { if (o) { if (o < 0) eval_add(result, static_cast(boost::multiprecision::detail::unsigned_abs(o))); else eval_subtract(result, static_cast(o)); } } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value && !is_trivial_cpp_int >::value>::type eval_subtract( cpp_int_backend& result, const cpp_int_backend& a, const signed_limb_type& o) noexcept((is_non_throwing_cpp_int >::value)) { if (o) { if (o < 0) eval_add(result, a, static_cast(boost::multiprecision::detail::unsigned_abs(o))); else eval_subtract(result, a, static_cast(o)); } else if (&result != &a) result = a; } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value>::type eval_increment(cpp_int_backend& result) noexcept((is_non_throwing_cpp_int >::value)) { constexpr const limb_type one = 1; if (!result.sign() && (result.limbs()[0] < cpp_int_backend::max_limb_value)) ++result.limbs()[0]; else if (result.sign() && result.limbs()[0]) { --result.limbs()[0]; if (!result.limbs()[0] && (result.size() == 1)) result.sign(false); } else eval_add(result, one); } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value>::type eval_decrement(cpp_int_backend& result) noexcept((is_non_throwing_cpp_int >::value)) { constexpr const limb_type one = 1; if (!result.sign() && result.limbs()[0]) --result.limbs()[0]; else if (result.sign() && (result.limbs()[0] < cpp_int_backend::max_limb_value)) ++result.limbs()[0]; else eval_subtract(result, one); } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value && !is_trivial_cpp_int >::value>::type eval_subtract( cpp_int_backend& result, const cpp_int_backend& o) noexcept((is_non_throwing_cpp_int >::value)) { eval_subtract(result, result, o); } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if >::value && !is_trivial_cpp_int >::value && !is_trivial_cpp_int >::value>::type eval_subtract( cpp_int_backend& result, const cpp_int_backend& a, const cpp_int_backend& b) noexcept((is_non_throwing_cpp_int >::value)) { if (a.sign() != b.sign()) { add_unsigned(result, a, b); return; } subtract_unsigned(result, a, b); } // // Simple addition and subtraction routine for trivial cpp_int's come last: // // One of the arguments is signed: // template inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if< is_trivial_cpp_int >::value && is_trivial_cpp_int >::value && (is_signed_number >::value || is_signed_number >::value)>::type eval_add( cpp_int_backend& result, const cpp_int_backend& o) noexcept((is_non_throwing_cpp_int >::value)) { if (result.sign() != o.sign()) { if (*o.limbs() > *result.limbs()) { *result.limbs() = detail::checked_subtract(*o.limbs(), *result.limbs(), typename cpp_int_backend::checked_type()); result.negate(); } else *result.limbs() = detail::checked_subtract(*result.limbs(), *o.limbs(), typename cpp_int_backend::checked_type()); } else *result.limbs() = detail::checked_add(*result.limbs(), *o.limbs(), typename cpp_int_backend::checked_type()); result.normalize(); } // Simple version for two unsigned arguments: template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if< is_trivial_cpp_int >::value && is_trivial_cpp_int >::value && is_unsigned_number >::value && is_unsigned_number >::value>::type eval_add(cpp_int_backend& result, const cpp_int_backend& o) noexcept((is_non_throwing_cpp_int >::value)) { *result.limbs() = detail::checked_add(*result.limbs(), *o.limbs(), typename cpp_int_backend::checked_type()); result.normalize(); } // signed subtraction: template inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if< is_trivial_cpp_int >::value && is_trivial_cpp_int >::value && (is_signed_number >::value || is_signed_number >::value)>::type eval_subtract( cpp_int_backend& result, const cpp_int_backend& o) noexcept((is_non_throwing_cpp_int >::value)) { if (result.sign() != o.sign()) { *result.limbs() = detail::checked_add(*result.limbs(), *o.limbs(), typename cpp_int_backend::checked_type()); } else if (*result.limbs() < *o.limbs()) { *result.limbs() = detail::checked_subtract(*o.limbs(), *result.limbs(), typename cpp_int_backend::checked_type()); result.negate(); } else *result.limbs() = detail::checked_subtract(*result.limbs(), *o.limbs(), typename cpp_int_backend::checked_type()); result.normalize(); } template BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if< is_trivial_cpp_int >::value && is_trivial_cpp_int >::value && is_unsigned_number >::value && is_unsigned_number >::value>::type eval_subtract( cpp_int_backend& result, const cpp_int_backend& o) noexcept((is_non_throwing_cpp_int >::value)) { *result.limbs() = detail::checked_subtract(*result.limbs(), *o.limbs(), typename cpp_int_backend::checked_type()); result.normalize(); } }}} // namespace boost::multiprecision::backends #endif