/////////////////////////////////////////////////////////////// // Copyright 2020 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 #ifndef BOOST_MP_RATIONAL_ADAPTOR_HPP #define BOOST_MP_RATIONAL_ADAPTOR_HPP #include #include #include #include namespace boost { namespace multiprecision { namespace backends { template struct rational_adaptor { // // Each backend need to declare 3 type lists which declare the types // with which this can interoperate. These lists must at least contain // the widest type in each category - so "long long" must be the final // type in the signed_types list for example. Any narrower types if not // present in the list will get promoted to the next wider type that is // in the list whenever mixed arithmetic involving that type is encountered. // typedef typename Backend::signed_types signed_types; typedef typename Backend::unsigned_types unsigned_types; typedef typename Backend::float_types float_types; typedef typename std::tuple_element<0, unsigned_types>::type ui_type; static Backend get_one() { Backend t; t = static_cast(1); return t; } static Backend get_zero() { Backend t; t = static_cast(0); return t; } static const Backend& one() { static const Backend result(get_one()); return result; } static const Backend& zero() { static const Backend result(get_zero()); return result; } void normalize() { using default_ops::eval_gcd; using default_ops::eval_eq; using default_ops::eval_divide; using default_ops::eval_get_sign; int s = eval_get_sign(m_denom); if(s == 0) { BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); } else if (s < 0) { m_num.negate(); m_denom.negate(); } Backend g, t; eval_gcd(g, m_num, m_denom); if (!eval_eq(g, one())) { eval_divide(t, m_num, g); m_num.swap(t); eval_divide(t, m_denom, g); m_denom = std::move(t); } } // We must have a default constructor: rational_adaptor() : m_num(zero()), m_denom(one()) {} rational_adaptor(const rational_adaptor& o) : m_num(o.m_num), m_denom(o.m_denom) {} rational_adaptor(rational_adaptor&& o) = default; // Optional constructors, we can make this type slightly more efficient // by providing constructors from any type we can handle natively. // These will also cause number<> to be implicitly constructible // from these types unless we make such constructors explicit. // template rational_adaptor(const Arithmetic& val, typename std::enable_if::value && !std::is_floating_point::value>::type const* = nullptr) : m_num(val), m_denom(one()) {} // // Pass-through 2-arg construction of components: // template rational_adaptor(const T& a, const U& b, typename std::enable_if::value && std::is_constructible::value>::type const* = nullptr) : m_num(a), m_denom(b) { normalize(); } template rational_adaptor(T&& a, const U& b, typename std::enable_if::value && std::is_constructible::value>::type const* = nullptr) : m_num(static_cast(a)), m_denom(b) { normalize(); } template rational_adaptor(T&& a, U&& b, typename std::enable_if::value && std::is_constructible::value>::type const* = nullptr) : m_num(static_cast(a)), m_denom(static_cast(b)) { normalize(); } template rational_adaptor(const T& a, U&& b, typename std::enable_if::value && std::is_constructible::value>::type const* = nullptr) : m_num(a), m_denom(static_cast(b)) { normalize(); } // // In the absense of converting constructors, operator= takes the strain. // In addition to the usual suspects, there must be one operator= for each type // listed in signed_types, unsigned_types, and float_types plus a string constructor. // rational_adaptor& operator=(const rational_adaptor& o) = default; rational_adaptor& operator=(rational_adaptor&& o) = default; template inline typename std::enable_if::value, rational_adaptor&>::type operator=(const Arithmetic& i) { m_num = i; m_denom = one(); return *this; } rational_adaptor& operator=(const char* s) { using default_ops::eval_eq; std::string s1; multiprecision::number v1, v2; char c; bool have_hex = false; const char* p = s; // saved for later while ((0 != (c = *s)) && (c == 'x' || c == 'X' || c == '-' || c == '+' || (c >= '0' && c <= '9') || (have_hex && (c >= 'a' && c <= 'f')) || (have_hex && (c >= 'A' && c <= 'F')))) { if (c == 'x' || c == 'X') have_hex = true; s1.append(1, c); ++s; } v1.assign(s1); s1.erase(); if (c == '/') { ++s; while ((0 != (c = *s)) && (c == 'x' || c == 'X' || c == '-' || c == '+' || (c >= '0' && c <= '9') || (have_hex && (c >= 'a' && c <= 'f')) || (have_hex && (c >= 'A' && c <= 'F')))) { if (c == 'x' || c == 'X') have_hex = true; s1.append(1, c); ++s; } v2.assign(s1); } else v2 = 1; if (*s) { BOOST_MP_THROW_EXCEPTION(std::runtime_error(std::string("Could not parse the string \"") + p + std::string("\" as a valid rational number."))); } multiprecision::number gcd; eval_gcd(gcd.backend(), v1.backend(), v2.backend()); if (!eval_eq(gcd.backend(), one())) { v1 /= gcd; v2 /= gcd; } num() = std::move(std::move(v1).backend()); denom() = std::move(std::move(v2).backend()); return *this; } template typename std::enable_if::value, rational_adaptor&>::type operator=(Float i) { using default_ops::eval_eq; BOOST_MP_FLOAT128_USING using std::floor; using std::frexp; using std::ldexp; int e; Float f = frexp(i, &e); #ifdef BOOST_HAS_FLOAT128 f = ldexp(f, std::is_same::value ? 113 : std::numeric_limits::digits); e -= std::is_same::value ? 113 : std::numeric_limits::digits; #else f = ldexp(f, std::numeric_limits::digits); e -= std::numeric_limits::digits; #endif number num(f); number denom(1u); if (e > 0) { num <<= e; } else if (e < 0) { denom <<= -e; } number gcd; eval_gcd(gcd.backend(), num.backend(), denom.backend()); if (!eval_eq(gcd.backend(), one())) { num /= gcd; denom /= gcd; } this->num() = std::move(std::move(num).backend()); this->denom() = std::move(std::move(denom).backend()); return *this; } void swap(rational_adaptor& o) { m_num.swap(o.m_num); m_denom.swap(o.m_denom); } std::string str(std::streamsize digits, std::ios_base::fmtflags f) const { using default_ops::eval_eq; // // We format the string ourselves so we can match what GMP's mpq type does: // std::string result = num().str(digits, f); if (!eval_eq(denom(), one())) { result.append(1, '/'); result.append(denom().str(digits, f)); } return result; } void negate() { m_num.negate(); } int compare(const rational_adaptor& o) const { std::ptrdiff_t s1 = eval_get_sign(*this); std::ptrdiff_t s2 = eval_get_sign(o); if (s1 != s2) { return s1 < s2 ? -1 : 1; } else if (s1 == 0) return 0; // both zero. bool neg = false; if (s1 >= 0) { s1 = eval_msb(num()) + eval_msb(o.denom()); s2 = eval_msb(o.num()) + eval_msb(denom()); } else { Backend t(num()); t.negate(); s1 = eval_msb(t) + eval_msb(o.denom()); t = o.num(); t.negate(); s2 = eval_msb(t) + eval_msb(denom()); neg = true; } s1 -= s2; if (s1 < -1) return neg ? 1 : -1; else if (s1 > 1) return neg ? -1 : 1; Backend t1, t2; eval_multiply(t1, num(), o.denom()); eval_multiply(t2, o.num(), denom()); return t1.compare(t2); } // // Comparison with arithmetic types, default just constructs a temporary: // template typename std::enable_if::value, int>::type compare(A i) const { rational_adaptor t; t = i; // Note: construct directly from i if supported. return compare(t); } Backend& num() { return m_num; } const Backend& num()const { return m_num; } Backend& denom() { return m_denom; } const Backend& denom()const { return m_denom; } #ifndef BOOST_MP_STANDALONE template void serialize(Archive& ar, const std::integral_constant&) { // Saving number n(num()), d(denom()); ar& boost::make_nvp("numerator", n); ar& boost::make_nvp("denominator", d); } template void serialize(Archive& ar, const std::integral_constant&) { // Loading number n, d; ar& boost::make_nvp("numerator", n); ar& boost::make_nvp("denominator", d); num() = n.backend(); denom() = d.backend(); } template void serialize(Archive& ar, const unsigned int /*version*/) { using tag = typename Archive::is_saving; using saving_tag = std::integral_constant; serialize(ar, saving_tag()); } #endif // BOOST_MP_STANDALONE private: Backend m_num, m_denom; }; // // Helpers: // template inline constexpr typename std::enable_if::is_specialized && !std::numeric_limits::is_signed, bool>::type is_minus_one(const T&) { return false; } template inline constexpr typename std::enable_if::is_specialized || std::numeric_limits::is_signed, bool>::type is_minus_one(const T& val) { return val == -1; } // // Required non-members: // template inline void eval_add(rational_adaptor& a, const rational_adaptor& b) { eval_add_subtract_imp(a, a, b, true); } template inline void eval_subtract(rational_adaptor& a, const rational_adaptor& b) { eval_add_subtract_imp(a, a, b, false); } template inline void eval_multiply(rational_adaptor& a, const rational_adaptor& b) { eval_multiply_imp(a, a, b.num(), b.denom()); } template void eval_divide(rational_adaptor& a, const rational_adaptor& b) { using default_ops::eval_divide; rational_adaptor t; eval_divide(t, a, b); a = std::move(t); } // // Conversions: // template inline typename std::enable_if::value == number_kind_floating_point>::type eval_convert_to(R* result, const rational_adaptor& backend) { // // The generic conversion is as good as anything we can write here: // ::boost::multiprecision::detail::generic_convert_rational_to_float(*result, backend); } template inline typename std::enable_if<(number_category::value != number_kind_integer) && (number_category::value != number_kind_floating_point) && !std::is_enum::value>::type eval_convert_to(R* result, const rational_adaptor& backend) { using default_ops::eval_convert_to; R d; eval_convert_to(result, backend.num()); eval_convert_to(&d, backend.denom()); *result /= d; } template inline typename std::enable_if::value == number_kind_integer>::type eval_convert_to(R* result, const rational_adaptor& backend) { using default_ops::eval_divide; using default_ops::eval_convert_to; Backend t; eval_divide(t, backend.num(), backend.denom()); eval_convert_to(result, t); } // // Hashing support, not strictly required, but it is used in our tests: // template inline std::size_t hash_value(const rational_adaptor& arg) { std::size_t result = hash_value(arg.num()); std::size_t result2 = hash_value(arg.denom()); boost::multiprecision::detail::hash_combine(result, result2); return result; } // // assign_components: // template void assign_components(rational_adaptor& result, Backend const& a, Backend const& b) { using default_ops::eval_gcd; using default_ops::eval_divide; using default_ops::eval_eq; using default_ops::eval_is_zero; using default_ops::eval_get_sign; if (eval_is_zero(b)) { BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); } Backend g; eval_gcd(g, a, b); if (eval_eq(g, rational_adaptor::one())) { result.num() = a; result.denom() = b; } else { eval_divide(result.num(), a, g); eval_divide(result.denom(), b, g); } if (eval_get_sign(result.denom()) < 0) { result.num().negate(); result.denom().negate(); } } // // Again for arithmetic types, overload for whatever arithmetic types are directly supported: // template inline void assign_components(rational_adaptor& result, const Arithmetic1& a, typename std::enable_if::value && std::is_arithmetic::value, const Arithmetic2&>::type b) { using default_ops::eval_gcd; using default_ops::eval_divide; using default_ops::eval_eq; if (b == 0) { BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); } Backend g; result.num() = a; eval_gcd(g, result.num(), b); if (eval_eq(g, rational_adaptor::one())) { result.denom() = b; } else { eval_divide(result.num(), g); eval_divide(result.denom(), b, g); } if (eval_get_sign(result.denom()) < 0) { result.num().negate(); result.denom().negate(); } } template inline void assign_components(rational_adaptor& result, const Arithmetic1& a, typename std::enable_if::value || !std::is_arithmetic::value, const Arithmetic2&>::type b) { using default_ops::eval_gcd; using default_ops::eval_divide; using default_ops::eval_eq; Backend g; result.num() = a; result.denom() = b; if (eval_get_sign(result.denom()) == 0) { BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); } eval_gcd(g, result.num(), result.denom()); if (!eval_eq(g, rational_adaptor::one())) { eval_divide(result.num(), g); eval_divide(result.denom(), g); } if (eval_get_sign(result.denom()) < 0) { result.num().negate(); result.denom().negate(); } } // // Optional comparison operators: // template inline bool eval_is_zero(const rational_adaptor& arg) { using default_ops::eval_is_zero; return eval_is_zero(arg.num()); } template inline int eval_get_sign(const rational_adaptor& arg) { using default_ops::eval_get_sign; return eval_get_sign(arg.num()); } template inline bool eval_eq(const rational_adaptor& a, const rational_adaptor& b) { using default_ops::eval_eq; return eval_eq(a.num(), b.num()) && eval_eq(a.denom(), b.denom()); } template inline typename std::enable_if::value&& std::is_integral::value, bool>::type eval_eq(const rational_adaptor& a, Arithmetic b) { using default_ops::eval_eq; return eval_eq(a.denom(), rational_adaptor::one()) && eval_eq(a.num(), b); } template inline typename std::enable_if::value&& std::is_integral::value, bool>::type eval_eq(Arithmetic b, const rational_adaptor& a) { using default_ops::eval_eq; return eval_eq(a.denom(), rational_adaptor::one()) && eval_eq(a.num(), b); } // // Arithmetic operations, starting with addition: // template void eval_add_subtract_imp(rational_adaptor& result, const Arithmetic& arg, bool isaddition) { using default_ops::eval_multiply; using default_ops::eval_divide; using default_ops::eval_add; using default_ops::eval_gcd; Backend t; eval_multiply(t, result.denom(), arg); if (isaddition) eval_add(result.num(), t); else eval_subtract(result.num(), t); // // There is no need to re-normalize here, we have // (a + bm) / b // and gcd(a + bm, b) = gcd(a, b) = 1 // /* eval_gcd(t, result.num(), result.denom()); if (!eval_eq(t, rational_adaptor::one()) != 0) { Backend t2; eval_divide(t2, result.num(), t); t2.swap(result.num()); eval_divide(t2, result.denom(), t); t2.swap(result.denom()); } */ } template inline typename std::enable_if::value && (std::is_integral::value || std::is_same::value)>::type eval_add(rational_adaptor& result, const Arithmetic& arg) { eval_add_subtract_imp(result, arg, true); } template inline typename std::enable_if::value && (std::is_integral::value || std::is_same::value)>::type eval_subtract(rational_adaptor& result, const Arithmetic& arg) { eval_add_subtract_imp(result, arg, false); } template void eval_add_subtract_imp(rational_adaptor& result, const rational_adaptor& a, const rational_adaptor& b, bool isaddition) { using default_ops::eval_eq; using default_ops::eval_multiply; using default_ops::eval_divide; using default_ops::eval_add; using default_ops::eval_subtract; // // Let a = an/ad // b = bn/bd // g = gcd(ad, bd) // result = rn/rd // // Then: // rn = an * (bd/g) + bn * (ad/g) // rd = ad * (bd/g) // = (ad/g) * (bd/g) * g // // And the whole thing can then be rescaled by // gcd(rn, g) // Backend gcd, t1, t2, t3, t4; // // Begin by getting the gcd of the 2 denominators: // eval_gcd(gcd, a.denom(), b.denom()); // // Do we have gcd > 1: // if (!eval_eq(gcd, rational_adaptor::one())) { // // Scale the denominators by gcd, and put the results in t1 and t2: // eval_divide(t1, b.denom(), gcd); eval_divide(t2, a.denom(), gcd); // // multiply the numerators by the scale denominators and put the results in t3, t4: // eval_multiply(t3, a.num(), t1); eval_multiply(t4, b.num(), t2); // // Add them up: // if (isaddition) eval_add(t3, t4); else eval_subtract(t3, t4); // // Get the gcd of gcd and our numerator (t3): // eval_gcd(t4, t3, gcd); if (eval_eq(t4, rational_adaptor::one())) { result.num() = t3; eval_multiply(result.denom(), t1, a.denom()); } else { // // Uncommon case where gcd is not 1, divide the numerator // and the denominator terms by the new gcd. Note we perform division // on the existing gcd value as this is the smallest of the 3 denominator // terms we'll be multiplying together, so there's a good chance it's a // single limb value already: // eval_divide(result.num(), t3, t4); eval_divide(t3, gcd, t4); eval_multiply(t4, t1, t2); eval_multiply(result.denom(), t4, t3); } } else { // // Most common case (approx 60%) where gcd is one: // eval_multiply(t1, a.num(), b.denom()); eval_multiply(t2, a.denom(), b.num()); if (isaddition) eval_add(result.num(), t1, t2); else eval_subtract(result.num(), t1, t2); eval_multiply(result.denom(), a.denom(), b.denom()); } } template inline void eval_add(rational_adaptor& result, const rational_adaptor& a, const rational_adaptor& b) { eval_add_subtract_imp(result, a, b, true); } template inline void eval_subtract(rational_adaptor& result, const rational_adaptor& a, const rational_adaptor& b) { eval_add_subtract_imp(result, a, b, false); } template void eval_add_subtract_imp(rational_adaptor& result, const rational_adaptor& a, const Arithmetic& b, bool isaddition) { using default_ops::eval_add; using default_ops::eval_subtract; using default_ops::eval_multiply; if (&result == &a) return eval_add_subtract_imp(result, b, isaddition); eval_multiply(result.num(), a.denom(), b); if (isaddition) eval_add(result.num(), a.num()); else BOOST_IF_CONSTEXPR(std::numeric_limits::is_signed == false) { Backend t; eval_subtract(t, a.num(), result.num()); result.num() = std::move(t); } else { eval_subtract(result.num(), a.num()); result.negate(); } result.denom() = a.denom(); // // There is no need to re-normalize here, we have // (a + bm) / b // and gcd(a + bm, b) = gcd(a, b) = 1 // } template inline typename std::enable_if::value && (std::is_integral::value || std::is_same::value)>::type eval_add(rational_adaptor& result, const rational_adaptor& a, const Arithmetic& b) { eval_add_subtract_imp(result, a, b, true); } template inline typename std::enable_if::value && (std::is_integral::value || std::is_same::value)>::type eval_subtract(rational_adaptor& result, const rational_adaptor& a, const Arithmetic& b) { eval_add_subtract_imp(result, a, b, false); } // // Multiplication: // template void eval_multiply_imp(rational_adaptor& result, const rational_adaptor& a, const Backend& b_num, const Backend& b_denom) { using default_ops::eval_multiply; using default_ops::eval_divide; using default_ops::eval_gcd; using default_ops::eval_get_sign; using default_ops::eval_eq; Backend gcd_left, gcd_right, t1, t2; eval_gcd(gcd_left, a.num(), b_denom); eval_gcd(gcd_right, b_num, a.denom()); // // Unit gcd's are the most likely case: // bool b_left = eval_eq(gcd_left, rational_adaptor::one()); bool b_right = eval_eq(gcd_right, rational_adaptor::one()); if (b_left && b_right) { eval_multiply(result.num(), a.num(), b_num); eval_multiply(result.denom(), a.denom(), b_denom); } else if (b_left) { eval_divide(t2, b_num, gcd_right); eval_multiply(result.num(), a.num(), t2); eval_divide(t1, a.denom(), gcd_right); eval_multiply(result.denom(), t1, b_denom); } else if (b_right) { eval_divide(t1, a.num(), gcd_left); eval_multiply(result.num(), t1, b_num); eval_divide(t2, b_denom, gcd_left); eval_multiply(result.denom(), a.denom(), t2); } else { eval_divide(t1, a.num(), gcd_left); eval_divide(t2, b_num, gcd_right); eval_multiply(result.num(), t1, t2); eval_divide(t1, a.denom(), gcd_right); eval_divide(t2, b_denom, gcd_left); eval_multiply(result.denom(), t1, t2); } // // We may have b_denom negative if this is actually division, if so just correct things now: // if (eval_get_sign(b_denom) < 0) { result.num().negate(); result.denom().negate(); } } template void eval_multiply(rational_adaptor& result, const rational_adaptor& a, const rational_adaptor& b) { using default_ops::eval_multiply; if (&a == &b) { // squaring, gcd's are 1: eval_multiply(result.num(), a.num(), b.num()); eval_multiply(result.denom(), a.denom(), b.denom()); return; } eval_multiply_imp(result, a, b.num(), b.denom()); } template void eval_multiply_imp(Backend& result_num, Backend& result_denom, Arithmetic arg) { if (arg == 0) { result_num = rational_adaptor::zero(); result_denom = rational_adaptor::one(); return; } else if (arg == 1) return; using default_ops::eval_multiply; using default_ops::eval_divide; using default_ops::eval_gcd; using default_ops::eval_convert_to; Backend gcd, t; Arithmetic integer_gcd; eval_gcd(gcd, result_denom, arg); eval_convert_to(&integer_gcd, gcd); arg /= integer_gcd; if (boost::multiprecision::detail::unsigned_abs(arg) > 1) { eval_multiply(t, result_num, arg); result_num = std::move(t); } else if (is_minus_one(arg)) result_num.negate(); if (integer_gcd > 1) { eval_divide(t, result_denom, integer_gcd); result_denom = std::move(t); } } template void eval_multiply_imp(Backend& result_num, Backend& result_denom, Backend arg) { using default_ops::eval_multiply; using default_ops::eval_divide; using default_ops::eval_gcd; using default_ops::eval_convert_to; using default_ops::eval_is_zero; using default_ops::eval_eq; using default_ops::eval_get_sign; if (eval_is_zero(arg)) { result_num = rational_adaptor::zero(); result_denom = rational_adaptor::one(); return; } else if (eval_eq(arg, rational_adaptor::one())) return; Backend gcd, t; eval_gcd(gcd, result_denom, arg); if (!eval_eq(gcd, rational_adaptor::one())) { eval_divide(t, arg, gcd); arg = t; } else t = arg; if (eval_get_sign(arg) < 0) t.negate(); if (!eval_eq(t, rational_adaptor::one())) { eval_multiply(t, result_num, arg); result_num = std::move(t); } else if (eval_get_sign(arg) < 0) result_num.negate(); if (!eval_eq(gcd, rational_adaptor::one())) { eval_divide(t, result_denom, gcd); result_denom = std::move(t); } } template inline typename std::enable_if::value && (std::is_integral::value || std::is_same::value)>::type eval_multiply(rational_adaptor& result, const Arithmetic& arg) { eval_multiply_imp(result.num(), result.denom(), arg); } template typename std::enable_if::value && std::is_integral::value>::type eval_multiply_imp(rational_adaptor& result, const Backend& a_num, const Backend& a_denom, Arithmetic b) { if (b == 0) { result.num() = rational_adaptor::zero(); result.denom() = rational_adaptor::one(); return; } else if (b == 1) { result.num() = a_num; result.denom() = a_denom; return; } using default_ops::eval_multiply; using default_ops::eval_divide; using default_ops::eval_gcd; using default_ops::eval_convert_to; Backend gcd; Arithmetic integer_gcd; eval_gcd(gcd, a_denom, b); eval_convert_to(&integer_gcd, gcd); b /= integer_gcd; if (boost::multiprecision::detail::unsigned_abs(b) > 1) eval_multiply(result.num(), a_num, b); else if (is_minus_one(b)) { result.num() = a_num; result.num().negate(); } else result.num() = a_num; if (integer_gcd > 1) eval_divide(result.denom(), a_denom, integer_gcd); else result.denom() = a_denom; } template inline void eval_multiply_imp(rational_adaptor& result, const Backend& a_num, const Backend& a_denom, const Backend& b) { result.num() = a_num; result.denom() = a_denom; eval_multiply_imp(result.num(), result.denom(), b); } template inline typename std::enable_if::value && (std::is_integral::value || std::is_same::value)>::type eval_multiply(rational_adaptor& result, const rational_adaptor& a, const Arithmetic& b) { if (&result == &a) return eval_multiply(result, b); eval_multiply_imp(result, a.num(), a.denom(), b); } template inline typename std::enable_if::value && (std::is_integral::value || std::is_same::value)>::type eval_multiply(rational_adaptor& result, const Arithmetic& b, const rational_adaptor& a) { return eval_multiply(result, a, b); } // // Division: // template inline void eval_divide(rational_adaptor& result, const rational_adaptor& a, const rational_adaptor& b) { using default_ops::eval_multiply; using default_ops::eval_get_sign; if (eval_get_sign(b.num()) == 0) { BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); return; } if (&a == &b) { // Huh? Really? result.num() = result.denom() = rational_adaptor::one(); return; } if (&result == &b) { rational_adaptor t(b); return eval_divide(result, a, t); } eval_multiply_imp(result, a, b.denom(), b.num()); } template inline typename std::enable_if::value && (std::is_integral::value || std::is_same::value)>::type eval_divide(rational_adaptor& result, const Arithmetic& b, const rational_adaptor& a) { using default_ops::eval_get_sign; if (eval_get_sign(a.num()) == 0) { BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); return; } if (&a == &result) { eval_multiply_imp(result.denom(), result.num(), b); result.num().swap(result.denom()); } else eval_multiply_imp(result, a.denom(), a.num(), b); if (eval_get_sign(result.denom()) < 0) { result.num().negate(); result.denom().negate(); } } template typename std::enable_if::value && std::is_integral::value>::type eval_divide(rational_adaptor& result, Arithmetic arg) { if (arg == 0) { BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); return; } else if (arg == 1) return; else if (is_minus_one(arg)) { result.negate(); return; } if (eval_get_sign(result) == 0) { return; } using default_ops::eval_multiply; using default_ops::eval_gcd; using default_ops::eval_convert_to; using default_ops::eval_divide; Backend gcd, t; Arithmetic integer_gcd; eval_gcd(gcd, result.num(), arg); eval_convert_to(&integer_gcd, gcd); arg /= integer_gcd; eval_multiply(t, result.denom(), boost::multiprecision::detail::unsigned_abs(arg)); result.denom() = std::move(t); if (arg < 0) { result.num().negate(); } if (integer_gcd > 1) { eval_divide(t, result.num(), integer_gcd); result.num() = std::move(t); } } template void eval_divide(rational_adaptor& result, const rational_adaptor& a, Backend arg) { using default_ops::eval_multiply; using default_ops::eval_gcd; using default_ops::eval_convert_to; using default_ops::eval_divide; using default_ops::eval_is_zero; using default_ops::eval_eq; using default_ops::eval_get_sign; if (eval_is_zero(arg)) { BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); return; } else if (eval_eq(a, rational_adaptor::one()) || (eval_get_sign(a) == 0)) { if (&result != &a) result = a; return; } Backend gcd, u_arg, t; eval_gcd(gcd, a.num(), arg); bool has_unit_gcd = eval_eq(gcd, rational_adaptor::one()); if (!has_unit_gcd) { eval_divide(u_arg, arg, gcd); arg = u_arg; } else u_arg = arg; if (eval_get_sign(u_arg) < 0) u_arg.negate(); eval_multiply(t, a.denom(), u_arg); result.denom() = std::move(t); if (!has_unit_gcd) { eval_divide(t, a.num(), gcd); result.num() = std::move(t); } else if (&result != &a) result.num() = a.num(); if (eval_get_sign(arg) < 0) { result.num().negate(); } } template void eval_divide(rational_adaptor& result, Backend arg) { eval_divide(result, result, arg); } template typename std::enable_if::value && std::is_integral::value>::type eval_divide(rational_adaptor& result, const rational_adaptor& a, Arithmetic arg) { if (&result == &a) return eval_divide(result, arg); if (arg == 0) { BOOST_MP_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); return; } else if (arg == 1) { result = a; return; } else if (is_minus_one(arg)) { result = a; result.num().negate(); return; } if (eval_get_sign(a) == 0) { result = a; return; } using default_ops::eval_multiply; using default_ops::eval_divide; using default_ops::eval_gcd; using default_ops::eval_convert_to; Backend gcd; Arithmetic integer_gcd; eval_gcd(gcd, a.num(), arg); eval_convert_to(&integer_gcd, gcd); arg /= integer_gcd; eval_multiply(result.denom(), a.denom(), boost::multiprecision::detail::unsigned_abs(arg)); if (integer_gcd > 1) { eval_divide(result.num(), a.num(), integer_gcd); } else result.num() = a.num(); if (arg < 0) { result.num().negate(); } } // // Increment and decrement: // template inline void eval_increment(rational_adaptor& arg) { using default_ops::eval_add; eval_add(arg.num(), arg.denom()); } template inline void eval_decrement(rational_adaptor& arg) { using default_ops::eval_subtract; eval_subtract(arg.num(), arg.denom()); } // // abs: // template inline void eval_abs(rational_adaptor& result, const rational_adaptor& arg) { using default_ops::eval_abs; eval_abs(result.num(), arg.num()); result.denom() = arg.denom(); } } // namespace backends // // Import the backend into this namespace: // using boost::multiprecision::backends::rational_adaptor; // // Define a category for this number type, one of: // // number_kind_integer // number_kind_floating_point // number_kind_rational // number_kind_fixed_point // number_kind_complex // template struct number_category > : public std::integral_constant {}; template struct expression_template_default > : public expression_template_default {}; template struct component_type, ExpressionTemplates> > { typedef number type; }; template inline number numerator(const number, ET>& val) { return val.backend().num(); } template inline number denominator(const number, ET>& val) { return val.backend().denom(); } template struct is_unsigned_number > : public is_unsigned_number {}; }} // namespace boost::multiprecision namespace std { template class numeric_limits, ExpressionTemplates> > : public std::numeric_limits > { using base_type = std::numeric_limits >; using number_type = boost::multiprecision::number >; public: static constexpr bool is_integer = false; static constexpr bool is_exact = true; static constexpr number_type(min)() { return (base_type::min)(); } static constexpr number_type(max)() { return (base_type::max)(); } static constexpr number_type lowest() { return -(max)(); } static constexpr number_type epsilon() { return base_type::epsilon(); } static constexpr number_type round_error() { return epsilon() / 2; } static constexpr number_type infinity() { return base_type::infinity(); } static constexpr number_type quiet_NaN() { return base_type::quiet_NaN(); } static constexpr number_type signaling_NaN() { return base_type::signaling_NaN(); } static constexpr number_type denorm_min() { return base_type::denorm_min(); } }; template constexpr bool numeric_limits, ExpressionTemplates> >::is_integer; template constexpr bool numeric_limits, ExpressionTemplates> >::is_exact; } // namespace std #endif