1 // Copyright 2016-2021 Francesco Biscani (bluescarni@gmail.com)
2 //
3 // This file is part of the mp++ library.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla
6 // Public License v. 2.0. If a copy of the MPL was not distributed
7 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #if defined(_MSC_VER)
10 
11 #define _SCL_SECURE_NO_WARNINGS
12 
13 #endif
14 
15 // NOTE: in order to use the MPFR print functions,
16 // we must make sure that stdio.h is included before mpfr.h:
17 // https://www.mpfr.org/mpfr-current/mpfr.html#Formatted-Output-Functions
18 #include <cstdio>
19 
20 #include <mp++/config.hpp>
21 
22 #include <algorithm>
23 #include <array>
24 #include <cassert>
25 #include <cerrno>
26 #include <cmath>
27 #include <cstddef>
28 #include <ios>
29 #include <iostream>
30 #include <limits>
31 #include <locale>
32 #include <memory>
33 #include <sstream>
34 #include <stdexcept>
35 #include <string>
36 #include <tuple>
37 #include <type_traits>
38 #include <utility>
39 #include <vector>
40 
41 #if defined(MPPP_HAVE_STRING_VIEW)
42 #include <string_view>
43 #endif
44 
45 #if defined(MPPP_WITH_BOOST_S11N)
46 
47 #include <boost/archive/binary_iarchive.hpp>
48 #include <boost/archive/binary_oarchive.hpp>
49 #include <boost/serialization/binary_object.hpp>
50 
51 #endif
52 
53 #include <mp++/detail/gmp.hpp>
54 #include <mp++/detail/mpfr.hpp>
55 #include <mp++/detail/utils.hpp>
56 #include <mp++/integer.hpp>
57 #include <mp++/real.hpp>
58 
59 #if defined(MPPP_WITH_QUADMATH)
60 #include <mp++/real128.hpp>
61 #endif
62 
63 namespace mppp
64 {
65 
66 namespace detail
67 {
68 
69 namespace
70 {
71 
72 // Some misc tests to check that the mpfr struct conforms to our expectations.
73 struct expected_mpfr_struct_t {
74     ::mpfr_prec_t _mpfr_prec;
75     ::mpfr_sign_t _mpfr_sign;
76     ::mpfr_exp_t _mpfr_exp;
77     ::mp_limb_t *_mpfr_d;
78 };
79 
80 static_assert(sizeof(expected_mpfr_struct_t) == sizeof(mpfr_struct_t) && offsetof(mpfr_struct_t, _mpfr_prec) == 0u
81                   && offsetof(mpfr_struct_t, _mpfr_sign) == offsetof(expected_mpfr_struct_t, _mpfr_sign)
82                   && offsetof(mpfr_struct_t, _mpfr_exp) == offsetof(expected_mpfr_struct_t, _mpfr_exp)
83                   && offsetof(mpfr_struct_t, _mpfr_d) == offsetof(expected_mpfr_struct_t, _mpfr_d)
84                   && std::is_same<::mp_limb_t *, decltype(std::declval<mpfr_struct_t>()._mpfr_d)>::value,
85               "Invalid mpfr_t struct layout and/or MPFR types.");
86 
87 // Double check that real is a standard layout class.
88 static_assert(std::is_standard_layout<real>::value, "real is not a standard layout class.");
89 
90 #if MPPP_CPLUSPLUS >= 201703L
91 
92 // If we have C++17, we can use structured bindings to test the layout of mpfr_struct_t
93 // and its members' types.
test_mpfr_struct_t()94 constexpr bool test_mpfr_struct_t()
95 {
96     auto [prec, sign, exp, ptr] = mpfr_struct_t{};
97     static_assert(std::is_same<decltype(ptr), ::mp_limb_t *>::value);
98     ignore(prec, sign, exp, ptr);
99 
100     return true;
101 }
102 
103 static_assert(test_mpfr_struct_t());
104 
105 #endif
106 
107 } // namespace
108 
109 // Wrapper for calling mpfr_lgamma().
real_lgamma_wrapper(::mpfr_t rop,const::mpfr_t op,::mpfr_rnd_t)110 void real_lgamma_wrapper(::mpfr_t rop, const ::mpfr_t op, ::mpfr_rnd_t)
111 {
112     // NOTE: we ignore the sign for consistency with lgamma.
113     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
114     int signp;
115     ::mpfr_lgamma(rop, &signp, op, MPFR_RNDN);
116 }
117 
118 // Wrapper for calling mpfr_li2().
real_li2_wrapper(::mpfr_t rop,const::mpfr_t op,::mpfr_rnd_t rnd)119 void real_li2_wrapper(::mpfr_t rop, const ::mpfr_t op, ::mpfr_rnd_t rnd)
120 {
121     // NOTE: mpfr_li2() returns the *real* part of the result,
122     // which, for op >= 1, is a complex number. For consistency
123     // with other functions, if the result is complex just set
124     // rop to nan.
125     // NOTE: check for nan before checking for >= 1, otherwise
126     // the comparison function will set the erange flag.
127     if (!mpfr_nan_p(op) && mpfr_cmp_ui(op, 1u) >= 0) {
128         ::mpfr_set_nan(rop);
129     } else {
130         ::mpfr_li2(rop, op, rnd);
131     }
132 }
133 
134 // Wrappers for calling integer and remainder-related functions
135 // with NaN checking.
real_ceil_wrapper(::mpfr_t rop,const::mpfr_t op)136 void real_ceil_wrapper(::mpfr_t rop, const ::mpfr_t op)
137 {
138     if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
139         throw std::domain_error("Cannot compute the ceiling of a NaN value");
140     }
141 
142     mpfr_ceil(rop, op);
143 }
144 
real_floor_wrapper(::mpfr_t rop,const::mpfr_t op)145 void real_floor_wrapper(::mpfr_t rop, const ::mpfr_t op)
146 {
147     if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
148         throw std::domain_error("Cannot compute the floor of a NaN value");
149     }
150 
151     mpfr_floor(rop, op);
152 }
153 
real_round_wrapper(::mpfr_t rop,const::mpfr_t op)154 void real_round_wrapper(::mpfr_t rop, const ::mpfr_t op)
155 {
156     if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
157         throw std::domain_error("Cannot round a NaN value");
158     }
159 
160     mpfr_round(rop, op);
161 }
162 
163 #if defined(MPPP_MPFR_HAVE_MPFR_ROUNDEVEN)
164 
real_roundeven_wrapper(::mpfr_t rop,const::mpfr_t op)165 void real_roundeven_wrapper(::mpfr_t rop, const ::mpfr_t op)
166 {
167     if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
168         throw std::domain_error("Cannot round a NaN value");
169     }
170 
171     ::mpfr_roundeven(rop, op);
172 }
173 
174 #endif
175 
real_trunc_wrapper(::mpfr_t rop,const::mpfr_t op)176 void real_trunc_wrapper(::mpfr_t rop, const ::mpfr_t op)
177 {
178     if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
179         throw std::domain_error("Cannot truncate a NaN value");
180     }
181 
182     mpfr_trunc(rop, op);
183 }
184 
real_frac_wrapper(::mpfr_t rop,const::mpfr_t op)185 void real_frac_wrapper(::mpfr_t rop, const ::mpfr_t op)
186 {
187     if (mppp_unlikely(mpfr_nan_p(op) != 0)) {
188         throw std::domain_error("Cannot compute the fractional part of a NaN value");
189     }
190 
191     ::mpfr_frac(rop, op, MPFR_RNDN);
192 }
193 
mpfr_to_stream(const::mpfr_t r,std::ostream & os,int base)194 void mpfr_to_stream(const ::mpfr_t r, std::ostream &os, int base)
195 {
196     // All chars potentially used by MPFR for representing the digits up to base 62, sorted.
197     constexpr char all_chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
198     // Check the base.
199     if (mppp_unlikely(base < 2 || base > 62)) {
200         throw std::invalid_argument("Cannot convert a real to a string in base " + to_string(base)
201                                     + ": the base must be in the [2,62] range");
202     }
203     // Special values first.
204     if (mpfr_nan_p(r)) {
205         // NOTE: up to base 16 we can use nan, inf, etc., but with larger
206         // bases we have to use the syntax with @.
207         os << (base <= 16 ? "nan" : "@nan@");
208         return;
209     }
210     if (mpfr_inf_p(r)) {
211         if (mpfr_sgn(r) < 0) {
212             os << '-';
213         }
214         os << (base <= 16 ? "inf" : "@inf@");
215         return;
216     }
217 
218     // Get the string fractional representation via the MPFR function,
219     // and wrap it into a smart pointer.
220     ::mpfr_exp_t exp(0);
221     std::unique_ptr<char, void (*)(char *)> str(::mpfr_get_str(nullptr, &exp, base, 0, r, MPFR_RNDN), ::mpfr_free_str);
222     // LCOV_EXCL_START
223     if (mppp_unlikely(!str)) {
224         throw std::runtime_error("Error in the conversion of a real to string: the call to mpfr_get_str() failed");
225     }
226     // LCOV_EXCL_STOP
227 
228     // Print the string, inserting a decimal point after the first digit.
229     bool dot_added = false;
230     // NOLINTNEXTLINE(llvm-qualified-auto, readability-qualified-auto)
231     for (auto cptr = str.get(); *cptr != '\0'; ++cptr) {
232         os << (*cptr);
233         if (!dot_added) {
234             if (base <= 10) {
235                 // For bases up to 10, we can use the followig guarantee
236                 // from the standard:
237                 // http://stackoverflow.com/questions/13827180/char-ascii-relation
238                 // """
239                 // The mapping of integer values for characters does have one guarantee given
240                 // by the Standard: the values of the decimal digits are contiguous.
241                 // (i.e., '1' - '0' == 1, ... '9' - '0' == 9)
242                 // """
243                 // http://eel.is/c++draft/lex.charset#3
244                 if (*cptr >= '0' && *cptr <= '9') {
245                     os << '.';
246                     dot_added = true;
247                 }
248             } else {
249                 // For bases larger than 10, we do a binary search among all the allowed characters.
250                 // NOTE: we need to search into the whole all_chars array (instead of just up to all_chars
251                 // + base) because apparently mpfr_get_str() seems to ignore lower/upper case when the base
252                 // is small enough (e.g., it uses 'a' instead of 'A' when printing in base 11).
253                 // NOTE: the range needs to be sizeof() - 1 because sizeof() also includes the terminator.
254                 if (std::binary_search(all_chars, all_chars + (sizeof(all_chars) - 1u), *cptr)) {
255                     os << '.';
256                     dot_added = true;
257                 }
258             }
259         }
260     }
261     assert(dot_added);
262 
263     // Adjust the exponent. Do it in multiprec in order to avoid potential overflow.
264     integer<1> z_exp{exp};
265     --z_exp;
266     const auto exp_sgn = z_exp.sgn();
267     if (exp_sgn != 0 && !mpfr_zero_p(r)) {
268         // Add the exponent at the end of the string, if both the value and the exponent
269         // are nonzero.
270         // NOTE: for bases greater than 10 we need '@' for the exponent, rather than 'e' or 'E'.
271         // https://www.mpfr.org/mpfr-current/mpfr.html#Assignment-Functions
272         os << (base <= 10 ? 'e' : '@');
273         if (exp_sgn == 1) {
274             // Add extra '+' if the exponent is positive, for consistency with
275             // real128's string format (and possibly other formats too?).
276             os << '+';
277         }
278         os << z_exp;
279     }
280 }
281 
282 } // namespace detail
283 
284 // Default constructor.
285 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real()286 real::real()
287 {
288     ::mpfr_init2(&m_mpfr, real_prec_min());
289     ::mpfr_set_zero(&m_mpfr, 1);
290 }
291 
292 // Init a real with precision p, setting its value to nan. No precision
293 // checking is performed.
294 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const ptag &,::mpfr_prec_t p,bool ignore_prec)295 real::real(const ptag &, ::mpfr_prec_t p, bool ignore_prec)
296 {
297     assert(ignore_prec);
298     assert(detail::real_prec_check(p));
299     detail::ignore(ignore_prec);
300     ::mpfr_init2(&m_mpfr, p);
301 }
302 
303 // Copy constructor.
real(const real & other)304 real::real(const real &other) : real(&other.m_mpfr) {}
305 
306 // Copy constructor with custom precision.
307 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const real & other,::mpfr_prec_t p)308 real::real(const real &other, ::mpfr_prec_t p)
309 {
310     // Init with custom precision, and then set.
311     ::mpfr_init2(&m_mpfr, check_init_prec(p));
312     mpfr_set(&m_mpfr, &other.m_mpfr, MPFR_RNDN);
313 }
314 
315 // Move constructor with custom precision.
316 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(real && other,::mpfr_prec_t p)317 real::real(real &&other, ::mpfr_prec_t p)
318 {
319     // Check the precision first of all.
320     check_init_prec(p);
321 
322     // Shallow copy other.
323     m_mpfr = other.m_mpfr;
324     // Mark the other as moved-from.
325     other.m_mpfr._mpfr_d = nullptr;
326 
327     // Apply the precision.
328     prec_round_impl<false>(p);
329 }
330 
331 // Construction from FPs.
332 template <typename Func, typename T>
dispatch_fp_construction(const Func & func,const T & x)333 void real::dispatch_fp_construction(const Func &func, const T &x)
334 {
335     func(&m_mpfr, x, MPFR_RNDN);
336 }
337 
dispatch_construction(const float & x)338 void real::dispatch_construction(const float &x)
339 {
340     dispatch_fp_construction(::mpfr_set_flt, x);
341 }
342 
dispatch_construction(const double & x)343 void real::dispatch_construction(const double &x)
344 {
345     dispatch_fp_construction(::mpfr_set_d, x);
346 }
347 
dispatch_construction(const long double & x)348 void real::dispatch_construction(const long double &x)
349 {
350     dispatch_fp_construction(::mpfr_set_ld, x);
351 }
352 
353 // Special casing for bool, otherwise MSVC warns if we fold this into the
354 // constructor from unsigned.
dispatch_construction(const bool & b)355 void real::dispatch_construction(const bool &b)
356 {
357     mpfr_set_ui(&m_mpfr, static_cast<unsigned long>(b), MPFR_RNDN);
358 }
359 
dispatch_mpz_construction(const::mpz_t n)360 void real::dispatch_mpz_construction(const ::mpz_t n)
361 {
362     ::mpfr_set_z(&m_mpfr, n, MPFR_RNDN);
363 }
364 
dispatch_mpq_construction(const::mpq_t q)365 void real::dispatch_mpq_construction(const ::mpq_t q)
366 {
367     ::mpfr_set_q(&m_mpfr, q, MPFR_RNDN);
368 }
369 
370 #if defined(MPPP_WITH_QUADMATH)
371 
dispatch_construction(const real128 & x)372 void real::dispatch_construction(const real128 &x)
373 {
374     assign_real128(x);
375 }
376 
377 #endif
378 
379 // Various helpers and constructors from string-like entities.
construct_from_c_string(const char * s,int base,::mpfr_prec_t p)380 void real::construct_from_c_string(const char *s, int base, ::mpfr_prec_t p)
381 {
382     if (mppp_unlikely(base && (base < 2 || base > 62))) {
383         throw std::invalid_argument("Cannot construct a real from a string in base " + detail::to_string(base)
384                                     + ": the base must either be zero or in the [2,62] range");
385     }
386     ::mpfr_init2(&m_mpfr, check_init_prec(p));
387     const auto ret = ::mpfr_set_str(&m_mpfr, s, base, MPFR_RNDN);
388     if (mppp_unlikely(ret == -1)) {
389         ::mpfr_clear(&m_mpfr);
390         throw std::invalid_argument(std::string{"The string '"} + s + "' does not represent a valid real in base "
391                                     + detail::to_string(base));
392     }
393 }
394 
395 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const ptag &,const char * s,int base,::mpfr_prec_t p)396 real::real(const ptag &, const char *s, int base, ::mpfr_prec_t p)
397 {
398     construct_from_c_string(s, base, p);
399 }
400 
real(const ptag &,const std::string & s,int base,::mpfr_prec_t p)401 real::real(const ptag &, const std::string &s, int base, ::mpfr_prec_t p) : real(s.c_str(), base, p) {}
402 
403 #if defined(MPPP_HAVE_STRING_VIEW)
real(const ptag &,const std::string_view & s,int base,::mpfr_prec_t p)404 real::real(const ptag &, const std::string_view &s, int base, ::mpfr_prec_t p)
405     : real(s.data(), s.data() + s.size(), base, p)
406 {
407 }
408 #endif
409 
410 // Constructor from range of characters, base and precision.
411 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const char * begin,const char * end,int base,::mpfr_prec_t p)412 real::real(const char *begin, const char *end, int base, ::mpfr_prec_t p)
413 {
414     MPPP_MAYBE_TLS std::vector<char> buffer;
415     buffer.assign(begin, end);
416     buffer.emplace_back('\0');
417     construct_from_c_string(buffer.data(), base, p);
418 }
419 
420 // Constructor from range of characters and precision.
real(const char * begin,const char * end,::mpfr_prec_t p)421 real::real(const char *begin, const char *end, ::mpfr_prec_t p) : real(begin, end, 10, p) {}
422 
423 // Constructor from a special value, sign and precision.
424 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(real_kind k,int sign,::mpfr_prec_t p)425 real::real(real_kind k, int sign, ::mpfr_prec_t p)
426 {
427     ::mpfr_init2(&m_mpfr, check_init_prec(p));
428     // NOTE: handle all cases explicitly, in order to avoid
429     // compiler warnings.
430     switch (k) {
431         case real_kind::nan:
432             break;
433         case real_kind::inf:
434             set_inf(sign);
435             break;
436         case real_kind::zero:
437             set_zero(sign);
438             break;
439         default:
440             // Clean up before throwing.
441             ::mpfr_clear(&m_mpfr);
442             using kind_cast_t = std::underlying_type<::mpfr_kind_t>::type;
443             throw std::invalid_argument(
444                 "The 'real_kind' value passed to the constructor of a real ("
445                 + std::to_string(static_cast<kind_cast_t>(k)) + ") is not one of the three allowed values ('nan'="
446                 + std::to_string(static_cast<kind_cast_t>(real_kind::nan))
447                 + ", 'inf'=" + std::to_string(static_cast<kind_cast_t>(real_kind::inf))
448                 + " and 'zero'=" + std::to_string(static_cast<kind_cast_t>(real_kind::zero)) + ")");
449     }
450 }
451 
452 // Constructor from a special value and precision.
real(real_kind k,::mpfr_prec_t p)453 real::real(real_kind k, ::mpfr_prec_t p) : real(k, 0, p) {}
454 
455 // Constructors from n*2**e.
456 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(unsigned long n,::mpfr_exp_t e,::mpfr_prec_t p)457 real::real(unsigned long n, ::mpfr_exp_t e, ::mpfr_prec_t p)
458 {
459     ::mpfr_init2(&m_mpfr, check_init_prec(p));
460     set_ui_2exp(*this, n, e);
461 }
462 
463 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(long n,::mpfr_exp_t e,::mpfr_prec_t p)464 real::real(long n, ::mpfr_exp_t e, ::mpfr_prec_t p)
465 {
466     ::mpfr_init2(&m_mpfr, check_init_prec(p));
467     set_si_2exp(*this, n, e);
468 }
469 
470 // Copy constructor from mpfr_t.
471 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
real(const::mpfr_t x)472 real::real(const ::mpfr_t x)
473 {
474     // Init with the same precision as other, and then set.
475     ::mpfr_init2(&m_mpfr, mpfr_get_prec(x));
476     mpfr_set(&m_mpfr, x, MPFR_RNDN);
477 }
478 
479 // Copy assignment operator.
operator =(const real & other)480 real &real::operator=(const real &other)
481 {
482     if (mppp_likely(this != &other)) {
483         if (is_valid()) {
484             // this has not been moved-from.
485             // Copy the precision. This will also reset the internal value.
486             // No need for prec checking as we assume other has a valid prec.
487             set_prec_impl<false>(other.get_prec());
488         } else {
489             // this has been moved-from: init before setting.
490             ::mpfr_init2(&m_mpfr, other.get_prec());
491         }
492         // Perform the actual copy from other.
493         mpfr_set(&m_mpfr, &other.m_mpfr, MPFR_RNDN);
494     }
495     return *this;
496 }
497 
498 // Implementation of the assignment from string.
string_assignment_impl(const char * s,int base)499 void real::string_assignment_impl(const char *s, int base)
500 {
501     if (mppp_unlikely(base && (base < 2 || base > 62))) {
502         throw std::invalid_argument("Cannot assign a real from a string in base " + detail::to_string(base)
503                                     + ": the base must either be zero or in the [2,62] range");
504     }
505     const auto ret = ::mpfr_set_str(&m_mpfr, s, base, MPFR_RNDN);
506     if (mppp_unlikely(ret == -1)) {
507         ::mpfr_set_nan(&m_mpfr);
508         throw std::invalid_argument(std::string{"The string '"} + s
509                                     + "' cannot be interpreted as a floating-point value in base "
510                                     + detail::to_string(base));
511     }
512 }
513 
514 #if defined(MPPP_WITH_QUADMATH)
515 
516 // Implementation of real128's assignment
517 // from real.
operator =(const real & x)518 real128 &real128::operator=(const real &x)
519 {
520     // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator)
521     return *this = static_cast<real128>(x);
522 }
523 
524 #endif
525 
526 // Copy assignment from mpfr_t.
operator =(const::mpfr_t x)527 real &real::operator=(const ::mpfr_t x)
528 {
529     // Set the precision, assuming the prec of x is valid.
530     set_prec_impl<false>(mpfr_get_prec(x));
531     // Set the value.
532     mpfr_set(&m_mpfr, x, MPFR_RNDN);
533     return *this;
534 }
535 
536 #if !defined(_MSC_VER) || defined(__clang__)
537 
538 // Move assignment from mpfr_t.
operator =(::mpfr_t && x)539 real &real::operator=(::mpfr_t &&x)
540 {
541     // Clear this.
542     ::mpfr_clear(&m_mpfr);
543     // Shallow copy x.
544     m_mpfr = *x;
545     return *this;
546 }
547 
548 #endif
549 
550 // Set to another real.
set(const real & other)551 real &real::set(const real &other)
552 {
553     return set(&other.m_mpfr);
554 }
555 
556 // Implementation of string setters.
set_impl(const char * s,int base)557 real &real::set_impl(const char *s, int base)
558 {
559     string_assignment_impl(s, base);
560     return *this;
561 }
562 
set_impl(const std::string & s,int base)563 real &real::set_impl(const std::string &s, int base)
564 {
565     return set(s.c_str(), base);
566 }
567 
568 #if defined(MPPP_HAVE_STRING_VIEW)
set_impl(const std::string_view & s,int base)569 real &real::set_impl(const std::string_view &s, int base)
570 {
571     return set(s.data(), s.data() + s.size(), base);
572 }
573 #endif
574 
575 // Set to character range.
set(const char * begin,const char * end,int base)576 real &real::set(const char *begin, const char *end, int base)
577 {
578     MPPP_MAYBE_TLS std::vector<char> buffer;
579     buffer.assign(begin, end);
580     buffer.emplace_back('\0');
581     return set(buffer.data(), base);
582 }
583 
584 // Set to an mpfr_t.
set(const::mpfr_t x)585 real &real::set(const ::mpfr_t x)
586 {
587     mpfr_set(&m_mpfr, x, MPFR_RNDN);
588     return *this;
589 }
590 
591 // Set to NaN.
set_nan()592 real &real::set_nan()
593 {
594     ::mpfr_set_nan(&m_mpfr);
595     return *this;
596 }
597 
598 // Set to infinity.
set_inf(int sign)599 real &real::set_inf(int sign)
600 {
601     ::mpfr_set_inf(&m_mpfr, sign);
602     return *this;
603 }
604 
605 // Set to zero.
set_zero(int sign)606 real &real::set_zero(int sign)
607 {
608     ::mpfr_set_zero(&m_mpfr, sign);
609     return *this;
610 }
611 
612 // Detect one.
is_one() const613 bool real::is_one() const
614 {
615     // NOTE: preempt calling the comparison function, if this is NaN
616     // (in such case, the range flag will be touched and we do not
617     // want to bother with that).
618     return !nan_p() && (mpfr_cmp_ui(&m_mpfr, 1u) == 0);
619 }
620 
621 #if defined(MPPP_WITH_QUADMATH)
622 
623 namespace detail
624 {
625 
626 // Some private machinery for the interaction between real and real128.
627 namespace
628 {
629 
630 // Helper to create a real with the value 2**112. This represents the "hidden bit"
631 // of the significand of a quadruple-precision FP.
real_2_112()632 real real_2_112()
633 {
634     // NOTE: we need only 1 bit of precision,
635     // but make sure that we don't choose a value
636     // too small.
637     real retval{1, clamp_mpfr_prec(1)};
638 
639     // NOTE: do the computation using the MPFR API,
640     // in order to avoid mp++'s precision management.
641     ::mpfr_mul_2ui(retval._get_mpfr_t(), retval.get_mpfr_t(), 112u, MPFR_RNDN);
642 
643     return retval;
644 }
645 
646 } // namespace
647 
648 } // namespace detail
649 
assign_real128(const real128 & x)650 void real::assign_real128(const real128 &x)
651 {
652     // Get the IEEE repr. of x.
653     const auto t = x.get_ieee();
654     // A utility function to write the significand of x
655     // as a big integer inside m_mpfr.
656     auto write_significand = [this, &t]() {
657         // The 4 32-bits part of the significand, from most to least
658         // significant digits.
659         const auto p1 = std::get<2>(t) >> 32;
660         const auto p2 = std::get<2>(t) % (1ull << 32);
661         const auto p3 = std::get<3>(t) >> 32;
662         const auto p4 = std::get<3>(t) % (1ull << 32);
663         // Build the significand, from most to least significant.
664         // NOTE: unsigned long is guaranteed to be at least 32 bit.
665         mpfr_set_ui(&this->m_mpfr, static_cast<unsigned long>(p1), MPFR_RNDN);
666         ::mpfr_mul_2ui(&this->m_mpfr, &this->m_mpfr, 32ul, MPFR_RNDN);
667         ::mpfr_add_ui(&this->m_mpfr, &this->m_mpfr, static_cast<unsigned long>(p2), MPFR_RNDN);
668         ::mpfr_mul_2ui(&this->m_mpfr, &this->m_mpfr, 32ul, MPFR_RNDN);
669         ::mpfr_add_ui(&this->m_mpfr, &this->m_mpfr, static_cast<unsigned long>(p3), MPFR_RNDN);
670         ::mpfr_mul_2ui(&this->m_mpfr, &this->m_mpfr, 32ul, MPFR_RNDN);
671         ::mpfr_add_ui(&this->m_mpfr, &this->m_mpfr, static_cast<unsigned long>(p4), MPFR_RNDN);
672     };
673     // Check if the significand is zero.
674     const bool sig_zero = std::get<2>(t) == 0u && std::get<3>(t) == 0u;
675     if (std::get<1>(t) == 0u) {
676         // Zero or subnormal numbers.
677         if (sig_zero) {
678             // Zero.
679             ::mpfr_set_zero(&m_mpfr, 1);
680         } else {
681             // Subnormal.
682             write_significand();
683             ::mpfr_div_2ui(&m_mpfr, &m_mpfr, 16382ul + 112ul, MPFR_RNDN);
684         }
685     } else if (std::get<1>(t) == 32767u) {
686         // NaN or inf.
687         if (sig_zero) {
688             // inf.
689             ::mpfr_set_inf(&m_mpfr, 1);
690         } else {
691             // NaN.
692             ::mpfr_set_nan(&m_mpfr);
693         }
694     } else {
695         // Write the significand into this.
696         write_significand();
697         // Add the hidden bit on top.
698         const MPPP_MAYBE_TLS real r_2_112 = detail::real_2_112();
699         ::mpfr_add(&m_mpfr, &m_mpfr, r_2_112.get_mpfr_t(), MPFR_RNDN);
700         // Multiply by 2 raised to the adjusted exponent.
701         ::mpfr_mul_2si(&m_mpfr, &m_mpfr, static_cast<long>(std::get<1>(t)) - (16383l + 112), MPFR_RNDN);
702     }
703     if (std::get<0>(t) != 0u) {
704         // Negate if the sign bit is set.
705         ::mpfr_neg(&m_mpfr, &m_mpfr, MPFR_RNDN);
706     }
707 }
708 
convert_to_real128() const709 real128 real::convert_to_real128() const
710 {
711     // Handle the special cases first.
712     if (nan_p()) {
713         return real128_nan();
714     }
715     // NOTE: the number 2**18 = 262144 is chosen so that it's amply outside the exponent
716     // range of real128 (a 15 bit value with some offset) but well within the
717     // range of long (around +-2**31 guaranteed by the standard).
718     //
719     // NOTE: the reason why we do these checks with large positive and negative exponents
720     // is that they ensure we can safely convert _mpfr_exp to long later.
721     if (inf_p() || m_mpfr._mpfr_exp > (1l << 18)) {
722         return sgn() > 0 ? real128_inf() : -real128_inf();
723     }
724     if (zero_p() || m_mpfr._mpfr_exp < -(1l << 18)) {
725         // Preserve the signedness of zero.
726         return signbit() ? -real128{} : real128{};
727     }
728     // NOTE: this is similar to the code in real128.hpp for the constructor from integer,
729     // with some modification due to the different padding in MPFR vs GMP (see below).
730     const auto prec = get_prec();
731     // Number of limbs in this.
732     // NOLINTNEXTLINE(readability-implicit-bool-conversion)
733     auto nlimbs = prec / ::mp_bits_per_limb + static_cast<bool>(prec % ::mp_bits_per_limb);
734     assert(nlimbs != 0);
735     // NOTE: in MPFR the most significant (nonzero) bit of the significand
736     // is always at the high end of the most significand limb. In other words,
737     // MPFR pads the multiprecision significand on the right, the opposite
738     // of GMP integers (which have padding on the left, i.e., in the top limb).
739     //
740     // NOTE: contrary to real128, the MPFR format does not have a hidden bit on top.
741     //
742     // Init retval with the highest limb.
743     //
744     // NOTE: MPFR does not support nail builds in GMP, so we don't have to worry about that.
745     real128 retval{m_mpfr._mpfr_d[--nlimbs]};
746     // Init the number of read bits.
747     // NOTE: we have read a full limb in the line above, so mp_bits_per_limb bits. If mp_bits_per_limb > 113,
748     // then the constructor of real128 truncated the input limb value to 113 bits of precision, so effectively
749     // we have read 113 bits only in such a case.
750     unsigned read_bits = detail::c_min(static_cast<unsigned>(::mp_bits_per_limb), real128_sig_digits());
751     while (nlimbs != 0 && read_bits < real128_sig_digits()) {
752         // Number of bits to be read from the current limb. Either mp_bits_per_limb or less.
753         const unsigned rbits
754             = detail::c_min(static_cast<unsigned>(::mp_bits_per_limb), real128_sig_digits() - read_bits);
755         // Shift up by rbits.
756         // NOTE: cast to int is safe, as rbits is no larger than mp_bits_per_limb which is
757         // representable by int.
758         retval = scalbn(retval, static_cast<int>(rbits));
759         // Add the next limb, removing lower bits if they are not to be read.
760         retval += m_mpfr._mpfr_d[--nlimbs] >> (static_cast<unsigned>(::mp_bits_per_limb) - rbits);
761         // Update the number of read bits.
762         // NOTE: due to the definition of rbits, read_bits can never reach past real128_sig_digits().
763         // Hence, this addition can never overflow (as sig_digits is unsigned itself).
764         read_bits += rbits;
765     }
766     // NOTE: from earlier we know the exponent is well within the range of long, and read_bits
767     // cannot be larger than 113.
768     retval = scalbln(retval, static_cast<long>(m_mpfr._mpfr_exp) - static_cast<long>(read_bits));
769     return sgn() > 0 ? retval : -retval;
770 }
771 
dispatch_get(real128 & x) const772 bool real::dispatch_get(real128 &x) const
773 {
774     x = static_cast<real128>(*this);
775     return true;
776 }
777 
778 #endif
779 
780 // Wrapper to apply the input unary MPFR function to this with
781 // MPFR_RNDN rounding mode. Returns a reference to this.
782 template <typename T>
self_mpfr_unary(T && f)783 real &real::self_mpfr_unary(T &&f)
784 {
785     std::forward<T>(f)(&m_mpfr, &m_mpfr, MPFR_RNDN);
786     return *this;
787 }
788 
789 // In-place Gamma function.
gamma()790 real &real::gamma()
791 {
792     return self_mpfr_unary(::mpfr_gamma);
793 }
794 
795 // In-place logarithm of the Gamma function.
lngamma()796 real &real::lngamma()
797 {
798     return self_mpfr_unary(::mpfr_lngamma);
799 }
800 
801 // In-place logarithm of the absolute value of the Gamma function.
lgamma()802 real &real::lgamma()
803 {
804     detail::real_lgamma_wrapper(&m_mpfr, &m_mpfr, MPFR_RNDN);
805     return *this;
806 }
807 
808 // In-place Digamma function.
digamma()809 real &real::digamma()
810 {
811     return self_mpfr_unary(::mpfr_digamma);
812 }
813 
814 // In-place Bessel function of the first kind of order 0.
j0()815 real &real::j0()
816 {
817     return self_mpfr_unary(::mpfr_j0);
818 }
819 
820 // In-place Bessel function of the first kind of order 1.
j1()821 real &real::j1()
822 {
823     return self_mpfr_unary(::mpfr_j1);
824 }
825 
826 // In-place Bessel function of the second kind of order 0.
y0()827 real &real::y0()
828 {
829     return self_mpfr_unary(::mpfr_y0);
830 }
831 
832 // In-place Bessel function of the second kind of order 1.
y1()833 real &real::y1()
834 {
835     return self_mpfr_unary(::mpfr_y1);
836 }
837 
838 // In-place exponential integral.
eint()839 real &real::eint()
840 {
841     return self_mpfr_unary(::mpfr_eint);
842 }
843 
844 // In-place dilogarithm.
li2()845 real &real::li2()
846 {
847     return self_mpfr_unary(detail::real_li2_wrapper);
848 }
849 
850 // In-place Riemann Zeta function.
zeta()851 real &real::zeta()
852 {
853     return self_mpfr_unary(::mpfr_zeta);
854 }
855 
856 // In-place error function.
erf()857 real &real::erf()
858 {
859     return self_mpfr_unary(::mpfr_erf);
860 }
861 
862 // In-place complementary error function.
erfc()863 real &real::erfc()
864 {
865     return self_mpfr_unary(::mpfr_erfc);
866 }
867 
868 // In-place Airy function.
ai()869 real &real::ai()
870 {
871     return self_mpfr_unary(::mpfr_ai);
872 }
873 
874 // Negate in-place.
neg()875 real &real::neg()
876 {
877     return self_mpfr_unary(::mpfr_neg);
878 }
879 
880 // In-place absolute value.
abs()881 real &real::abs()
882 {
883     return self_mpfr_unary(::mpfr_abs);
884 }
885 
886 // Destructively set the precision.
set_prec(::mpfr_prec_t p)887 real &real::set_prec(::mpfr_prec_t p)
888 {
889     set_prec_impl<true>(p);
890     return *this;
891 }
892 
893 // Set the precision maintaining the current value.
prec_round(::mpfr_prec_t p)894 real &real::prec_round(::mpfr_prec_t p)
895 {
896     prec_round_impl<true>(p);
897     return *this;
898 }
899 
900 // Convert to string.
to_string(int base) const901 std::string real::to_string(int base) const
902 {
903     std::ostringstream oss;
904     oss.exceptions(std::ios_base::failbit | std::ios_base::badbit);
905     detail::mpfr_to_stream(&m_mpfr, oss, base);
906     return oss.str();
907 }
908 
909 // In-place square root.
sqrt()910 real &real::sqrt()
911 {
912     return self_mpfr_unary(::mpfr_sqrt);
913 }
914 
915 // In-place reciprocal square root.
rec_sqrt()916 real &real::rec_sqrt()
917 {
918     return self_mpfr_unary(::mpfr_rec_sqrt);
919 }
920 
921 // In-place cubic root.
cbrt()922 real &real::cbrt()
923 {
924     return self_mpfr_unary(::mpfr_cbrt);
925 }
926 
927 // In-place squaring.
sqr()928 real &real::sqr()
929 {
930     return self_mpfr_unary(::mpfr_sqr);
931 }
932 
933 // In-place sine.
sin()934 real &real::sin()
935 {
936     return self_mpfr_unary(::mpfr_sin);
937 }
938 
939 // In-place cosine.
cos()940 real &real::cos()
941 {
942     return self_mpfr_unary(::mpfr_cos);
943 }
944 
945 // In-place tangent.
tan()946 real &real::tan()
947 {
948     return self_mpfr_unary(::mpfr_tan);
949 }
950 
951 // In-place secant.
sec()952 real &real::sec()
953 {
954     return self_mpfr_unary(::mpfr_sec);
955 }
956 
957 // In-place cosecant.
csc()958 real &real::csc()
959 {
960     return self_mpfr_unary(::mpfr_csc);
961 }
962 
963 // In-place cotangent.
cot()964 real &real::cot()
965 {
966     return self_mpfr_unary(::mpfr_cot);
967 }
968 
969 // In-place arccosine.
acos()970 real &real::acos()
971 {
972     return self_mpfr_unary(::mpfr_acos);
973 }
974 
975 // In-place arcsine.
asin()976 real &real::asin()
977 {
978     return self_mpfr_unary(::mpfr_asin);
979 }
980 
981 // In-place arctangent.
atan()982 real &real::atan()
983 {
984     return self_mpfr_unary(::mpfr_atan);
985 }
986 
987 // In-place hyperbolic cosine.
cosh()988 real &real::cosh()
989 {
990     return self_mpfr_unary(::mpfr_cosh);
991 }
992 
993 // In-place hyperbolic sine.
sinh()994 real &real::sinh()
995 {
996     return self_mpfr_unary(::mpfr_sinh);
997 }
998 
999 // In-place hyperbolic tangent.
tanh()1000 real &real::tanh()
1001 {
1002     return self_mpfr_unary(::mpfr_tanh);
1003 }
1004 
1005 // In-place hyperbolic secant.
sech()1006 real &real::sech()
1007 {
1008     return self_mpfr_unary(::mpfr_sech);
1009 }
1010 
1011 // In-place hyperbolic cosecant.
csch()1012 real &real::csch()
1013 {
1014     return self_mpfr_unary(::mpfr_csch);
1015 }
1016 
1017 // In-place hyperbolic cotangent.
coth()1018 real &real::coth()
1019 {
1020     return self_mpfr_unary(::mpfr_coth);
1021 }
1022 
1023 // In-place inverse hyperbolic cosine.
acosh()1024 real &real::acosh()
1025 {
1026     return self_mpfr_unary(::mpfr_acosh);
1027 }
1028 
1029 // In-place inverse hyperbolic sine.
asinh()1030 real &real::asinh()
1031 {
1032     return self_mpfr_unary(::mpfr_asinh);
1033 }
1034 
1035 // In-place inverse hyperbolic tangent.
atanh()1036 real &real::atanh()
1037 {
1038     return self_mpfr_unary(::mpfr_atanh);
1039 }
1040 
1041 // In-place exponential.
exp()1042 real &real::exp()
1043 {
1044     return self_mpfr_unary(::mpfr_exp);
1045 }
1046 
1047 // In-place base-2 exponential.
exp2()1048 real &real::exp2()
1049 {
1050     return self_mpfr_unary(::mpfr_exp2);
1051 }
1052 
1053 // In-place base-10 exponential.
exp10()1054 real &real::exp10()
1055 {
1056     return self_mpfr_unary(::mpfr_exp10);
1057 }
1058 
1059 // In-place exponential minus 1.
expm1()1060 real &real::expm1()
1061 {
1062     return self_mpfr_unary(::mpfr_expm1);
1063 }
1064 
1065 // In-place logarithm.
log()1066 real &real::log()
1067 {
1068     return self_mpfr_unary(::mpfr_log);
1069 }
1070 
1071 // In-place base-2 logarithm.
log2()1072 real &real::log2()
1073 {
1074     return self_mpfr_unary(::mpfr_log2);
1075 }
1076 
1077 // In-place base-10 logarithm.
log10()1078 real &real::log10()
1079 {
1080     return self_mpfr_unary(::mpfr_log10);
1081 }
1082 
1083 // In-place augmented logarithm.
log1p()1084 real &real::log1p()
1085 {
1086     return self_mpfr_unary(::mpfr_log1p);
1087 }
1088 
1089 // Check if the value is an integer.
integer_p() const1090 bool real::integer_p() const
1091 {
1092     return ::mpfr_integer_p(&m_mpfr) != 0;
1093 }
1094 
1095 // In-place integer and remainder-related functions.
ceil()1096 real &real::ceil()
1097 {
1098     return self_mpfr_unary_nornd(detail::real_ceil_wrapper);
1099 }
1100 
floor()1101 real &real::floor()
1102 {
1103     return self_mpfr_unary_nornd(detail::real_floor_wrapper);
1104 }
1105 
round()1106 real &real::round()
1107 {
1108     return self_mpfr_unary_nornd(detail::real_round_wrapper);
1109 }
1110 
1111 #if defined(MPPP_MPFR_HAVE_MPFR_ROUNDEVEN)
1112 
roundeven()1113 real &real::roundeven()
1114 {
1115     return self_mpfr_unary_nornd(detail::real_roundeven_wrapper);
1116 }
1117 
1118 #endif
1119 
trunc()1120 real &real::trunc()
1121 {
1122     return self_mpfr_unary_nornd(detail::real_trunc_wrapper);
1123 }
1124 
frac()1125 real &real::frac()
1126 {
1127     return self_mpfr_unary_nornd(detail::real_frac_wrapper);
1128 }
1129 
1130 // Set to n*2**e.
set_ui_2exp(real & r,unsigned long n,::mpfr_exp_t e)1131 real &set_ui_2exp(real &r, unsigned long n, ::mpfr_exp_t e)
1132 {
1133     ::mpfr_set_ui_2exp(r._get_mpfr_t(), n, e, MPFR_RNDN);
1134     return r;
1135 }
1136 
set_si_2exp(real & r,long n,::mpfr_exp_t e)1137 real &set_si_2exp(real &r, long n, ::mpfr_exp_t e)
1138 {
1139     ::mpfr_set_si_2exp(r._get_mpfr_t(), n, e, MPFR_RNDN);
1140     return r;
1141 }
1142 
1143 // Implementation bits for in-place addition.
1144 namespace detail
1145 {
1146 
dispatch_real_in_place_add_integer_impl(real & a,const::mpz_t n,::mpfr_prec_t dprec)1147 void dispatch_real_in_place_add_integer_impl(real &a, const ::mpz_t n, ::mpfr_prec_t dprec)
1148 {
1149     auto wrapper = [&n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_add_z(r, o, n, MPFR_RNDN); };
1150 
1151     // NOTE: in these mpfr_nary_op_impl() invocations, we are passing a min_prec
1152     // which is by definition valid because it is produced by an invocation of
1153     // real_deduce_precision() (which does clamping).
1154     mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1155 }
1156 
dispatch_real_in_place_add(real & a,bool n)1157 void dispatch_real_in_place_add(real &a, bool n)
1158 {
1159     auto wrapper = [n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_add_ui(r, o, static_cast<unsigned long>(n), MPFR_RNDN); };
1160 
1161     mpfr_nary_op_impl<false>(real_deduce_precision(n), wrapper, a, a);
1162 }
1163 
dispatch_real_in_place_add_rational_impl(real & a,const::mpq_t q,::mpfr_prec_t dprec)1164 void dispatch_real_in_place_add_rational_impl(real &a, const ::mpq_t q, ::mpfr_prec_t dprec)
1165 {
1166     auto wrapper = [&q](::mpfr_t r, const ::mpfr_t o) { ::mpfr_add_q(r, o, q, MPFR_RNDN); };
1167 
1168     mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1169 }
1170 
1171 namespace
1172 {
1173 
1174 template <typename T>
dispatch_real_in_place_add_fd_impl(real & a,const T & x)1175 inline void dispatch_real_in_place_add_fd_impl(real &a, const T &x)
1176 {
1177     // NOTE: the MPFR docs state that mpfr_add_d() assumes that
1178     // the radix of double is a power of 2. If we ever run into platforms
1179     // for which this is not true, we can add a compile-time dispatch
1180     // that uses the long double implementation instead.
1181     constexpr auto dradix = static_cast<unsigned>(std::numeric_limits<double>::radix);
1182     static_assert(!(dradix & (dradix - 1)), "mpfr_add_d() requires the radix of the 'double' type to be a power of 2.");
1183 
1184     auto wrapper = [x](::mpfr_t r, const ::mpfr_t o) { ::mpfr_add_d(r, o, static_cast<double>(x), MPFR_RNDN); };
1185 
1186     mpfr_nary_op_impl<false>(real_deduce_precision(x), wrapper, a, a);
1187 }
1188 
1189 } // namespace
1190 
dispatch_real_in_place_add(real & a,const float & x)1191 void dispatch_real_in_place_add(real &a, const float &x)
1192 {
1193     dispatch_real_in_place_add_fd_impl(a, x);
1194 }
1195 
dispatch_real_in_place_add(real & a,const double & x)1196 void dispatch_real_in_place_add(real &a, const double &x)
1197 {
1198     dispatch_real_in_place_add_fd_impl(a, x);
1199 }
1200 
1201 namespace
1202 {
1203 
1204 template <typename T>
dispatch_real_in_place_add_generic_impl(real & a,const T & x)1205 inline void dispatch_real_in_place_add_generic_impl(real &a, const T &x)
1206 {
1207     MPPP_MAYBE_TLS real tmp;
1208     tmp.set_prec(c_max(a.get_prec(), real_deduce_precision(x)));
1209     tmp.set(x);
1210     dispatch_real_in_place_add(a, tmp);
1211 }
1212 
1213 } // namespace
1214 
dispatch_real_in_place_add(real & a,const long double & x)1215 void dispatch_real_in_place_add(real &a, const long double &x)
1216 {
1217     dispatch_real_in_place_add_generic_impl(a, x);
1218 }
1219 
1220 #if defined(MPPP_WITH_QUADMATH)
1221 
dispatch_real_in_place_add(real & a,const real128 & x)1222 void dispatch_real_in_place_add(real &a, const real128 &x)
1223 {
1224     dispatch_real_in_place_add_generic_impl(a, x);
1225 }
1226 
1227 #endif
1228 
1229 } // namespace detail
1230 
operator ++(real & x)1231 real &operator++(real &x)
1232 {
1233     if (mppp_unlikely(x.get_prec() < detail::real_deduce_precision(1))) {
1234         x.prec_round(detail::real_deduce_precision(1));
1235     }
1236     ::mpfr_add_ui(x._get_mpfr_t(), x.get_mpfr_t(), 1ul, MPFR_RNDN);
1237     return x;
1238 }
1239 
operator ++(real & x,int)1240 real operator++(real &x, int)
1241 {
1242     auto retval(x);
1243     ++x;
1244     return retval;
1245 }
1246 
1247 // Implementation bits for in-place subtraction.
1248 namespace detail
1249 {
1250 
dispatch_real_in_place_sub_integer_impl(real & a,const::mpz_t n,::mpfr_prec_t dprec)1251 void dispatch_real_in_place_sub_integer_impl(real &a, const ::mpz_t n, ::mpfr_prec_t dprec)
1252 {
1253     auto wrapper = [&n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_sub_z(r, o, n, MPFR_RNDN); };
1254 
1255     // NOTE: in these mpfr_nary_op_impl() invocations, we are passing a min_prec
1256     // which is by definition valid because it is produced by an invocation of
1257     // real_deduce_precision() (which does clamping).
1258     mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1259 }
1260 
dispatch_real_in_place_sub(real & a,bool n)1261 void dispatch_real_in_place_sub(real &a, bool n)
1262 {
1263     auto wrapper = [n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_sub_ui(r, o, static_cast<unsigned long>(n), MPFR_RNDN); };
1264 
1265     mpfr_nary_op_impl<false>(real_deduce_precision(n), wrapper, a, a);
1266 }
1267 
dispatch_real_in_place_sub_rational_impl(real & a,const::mpq_t q,::mpfr_prec_t dprec)1268 void dispatch_real_in_place_sub_rational_impl(real &a, const ::mpq_t q, ::mpfr_prec_t dprec)
1269 {
1270     auto wrapper = [&q](::mpfr_t r, const ::mpfr_t o) { ::mpfr_sub_q(r, o, q, MPFR_RNDN); };
1271 
1272     mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1273 }
1274 
1275 namespace
1276 {
1277 
1278 template <typename T>
dispatch_real_in_place_sub_fd_impl(real & a,const T & x)1279 inline void dispatch_real_in_place_sub_fd_impl(real &a, const T &x)
1280 {
1281     // NOTE: the MPFR docs state that mpfr_sub_d() assumes that
1282     // the radix of double is a power of 2. If we ever run into platforms
1283     // for which this is not true, we can add a compile-time dispatch
1284     // that uses the long double implementation instead.
1285     constexpr auto dradix = static_cast<unsigned>(std::numeric_limits<double>::radix);
1286     static_assert(!(dradix & (dradix - 1)), "mpfr_sub_d() requires the radix of the 'double' type to be a power of 2.");
1287 
1288     auto wrapper = [x](::mpfr_t r, const ::mpfr_t o) { ::mpfr_sub_d(r, o, static_cast<double>(x), MPFR_RNDN); };
1289 
1290     mpfr_nary_op_impl<false>(real_deduce_precision(x), wrapper, a, a);
1291 }
1292 
1293 } // namespace
1294 
dispatch_real_in_place_sub(real & a,const float & x)1295 void dispatch_real_in_place_sub(real &a, const float &x)
1296 {
1297     dispatch_real_in_place_sub_fd_impl(a, x);
1298 }
1299 
dispatch_real_in_place_sub(real & a,const double & x)1300 void dispatch_real_in_place_sub(real &a, const double &x)
1301 {
1302     dispatch_real_in_place_sub_fd_impl(a, x);
1303 }
1304 
1305 namespace
1306 {
1307 
1308 template <typename T>
dispatch_real_in_place_sub_generic_impl(real & a,const T & x)1309 inline void dispatch_real_in_place_sub_generic_impl(real &a, const T &x)
1310 {
1311     MPPP_MAYBE_TLS real tmp;
1312     tmp.set_prec(c_max(a.get_prec(), real_deduce_precision(x)));
1313     tmp.set(x);
1314     dispatch_real_in_place_sub(a, tmp);
1315 }
1316 
1317 } // namespace
1318 
dispatch_real_in_place_sub(real & a,const long double & x)1319 void dispatch_real_in_place_sub(real &a, const long double &x)
1320 {
1321     dispatch_real_in_place_sub_generic_impl(a, x);
1322 }
1323 
1324 #if defined(MPPP_WITH_QUADMATH)
1325 
dispatch_real_in_place_sub(real & a,const real128 & x)1326 void dispatch_real_in_place_sub(real &a, const real128 &x)
1327 {
1328     dispatch_real_in_place_sub_generic_impl(a, x);
1329 }
1330 
1331 #endif
1332 
1333 } // namespace detail
1334 
operator --(real & x)1335 real &operator--(real &x)
1336 {
1337     if (mppp_unlikely(x.get_prec() < detail::real_deduce_precision(1))) {
1338         x.prec_round(detail::real_deduce_precision(1));
1339     }
1340     ::mpfr_sub_ui(x._get_mpfr_t(), x.get_mpfr_t(), 1ul, MPFR_RNDN);
1341     return x;
1342 }
1343 
operator --(real & x,int)1344 real operator--(real &x, int)
1345 {
1346     auto retval(x);
1347     --x;
1348     return retval;
1349 }
1350 
1351 // Implementation bits for in-place multiplication.
1352 namespace detail
1353 {
1354 
dispatch_real_in_place_mul_integer_impl(real & a,const::mpz_t n,::mpfr_prec_t dprec)1355 void dispatch_real_in_place_mul_integer_impl(real &a, const ::mpz_t n, ::mpfr_prec_t dprec)
1356 {
1357     auto wrapper = [&n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_mul_z(r, o, n, MPFR_RNDN); };
1358 
1359     // NOTE: in these mpfr_nary_op_impl() invocations, we are passing a min_prec
1360     // which is by definition valid because it is produced by an invocation of
1361     // real_deduce_precision() (which does clamping).
1362     mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1363 }
1364 
dispatch_real_in_place_mul(real & a,bool n)1365 void dispatch_real_in_place_mul(real &a, bool n)
1366 {
1367     auto wrapper = [n](::mpfr_t r, const ::mpfr_t o) { mpfr_mul_ui(r, o, static_cast<unsigned long>(n), MPFR_RNDN); };
1368 
1369     mpfr_nary_op_impl<false>(real_deduce_precision(n), wrapper, a, a);
1370 }
1371 
dispatch_real_in_place_mul_rational_impl(real & a,const::mpq_t q,::mpfr_prec_t dprec)1372 void dispatch_real_in_place_mul_rational_impl(real &a, const ::mpq_t q, ::mpfr_prec_t dprec)
1373 {
1374     auto wrapper = [&q](::mpfr_t r, const ::mpfr_t o) { ::mpfr_mul_q(r, o, q, MPFR_RNDN); };
1375 
1376     mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1377 }
1378 
1379 namespace
1380 {
1381 
1382 template <typename T>
dispatch_real_in_place_mul_fd_impl(real & a,const T & x)1383 inline void dispatch_real_in_place_mul_fd_impl(real &a, const T &x)
1384 {
1385     // NOTE: the MPFR docs state that mpfr_mul_d() assumes that
1386     // the radix of double is a power of 2. If we ever run into platforms
1387     // for which this is not true, we can add a compile-time dispatch
1388     // that uses the long double implementation instead.
1389     constexpr auto dradix = static_cast<unsigned>(std::numeric_limits<double>::radix);
1390     static_assert(!(dradix & (dradix - 1)), "mpfr_mul_d() requires the radix of the 'double' type to be a power of 2.");
1391 
1392     auto wrapper = [x](::mpfr_t r, const ::mpfr_t o) { ::mpfr_mul_d(r, o, static_cast<double>(x), MPFR_RNDN); };
1393 
1394     mpfr_nary_op_impl<false>(real_deduce_precision(x), wrapper, a, a);
1395 }
1396 
1397 } // namespace
1398 
dispatch_real_in_place_mul(real & a,const float & x)1399 void dispatch_real_in_place_mul(real &a, const float &x)
1400 {
1401     dispatch_real_in_place_mul_fd_impl(a, x);
1402 }
1403 
dispatch_real_in_place_mul(real & a,const double & x)1404 void dispatch_real_in_place_mul(real &a, const double &x)
1405 {
1406     dispatch_real_in_place_mul_fd_impl(a, x);
1407 }
1408 
1409 namespace
1410 {
1411 
1412 template <typename T>
dispatch_real_in_place_mul_generic_impl(real & a,const T & x)1413 inline void dispatch_real_in_place_mul_generic_impl(real &a, const T &x)
1414 {
1415     MPPP_MAYBE_TLS real tmp;
1416     tmp.set_prec(c_max(a.get_prec(), real_deduce_precision(x)));
1417     tmp.set(x);
1418     dispatch_real_in_place_mul(a, tmp);
1419 }
1420 
1421 } // namespace
1422 
dispatch_real_in_place_mul(real & a,const long double & x)1423 void dispatch_real_in_place_mul(real &a, const long double &x)
1424 {
1425     dispatch_real_in_place_mul_generic_impl(a, x);
1426 }
1427 
1428 #if defined(MPPP_WITH_QUADMATH)
1429 
dispatch_real_in_place_mul(real & a,const real128 & x)1430 void dispatch_real_in_place_mul(real &a, const real128 &x)
1431 {
1432     dispatch_real_in_place_mul_generic_impl(a, x);
1433 }
1434 
1435 #endif
1436 
1437 } // namespace detail
1438 
1439 // Implementation bits for in-place division.
1440 namespace detail
1441 {
1442 
dispatch_real_in_place_div_integer_impl(real & a,const::mpz_t n,::mpfr_prec_t dprec)1443 void dispatch_real_in_place_div_integer_impl(real &a, const ::mpz_t n, ::mpfr_prec_t dprec)
1444 {
1445     auto wrapper = [&n](::mpfr_t r, const ::mpfr_t o) { ::mpfr_div_z(r, o, n, MPFR_RNDN); };
1446 
1447     // NOTE: in these mpfr_nary_op_impl() invocations, we are passing a min_prec
1448     // which is by definition valid because it is produced by an invocation of
1449     // real_deduce_precision() (which does clamping).
1450     mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1451 }
1452 
dispatch_real_in_place_div(real & a,bool n)1453 void dispatch_real_in_place_div(real &a, bool n)
1454 {
1455     auto wrapper = [n](::mpfr_t r, const ::mpfr_t o) { mpfr_div_ui(r, o, static_cast<unsigned long>(n), MPFR_RNDN); };
1456 
1457     mpfr_nary_op_impl<false>(real_deduce_precision(n), wrapper, a, a);
1458 }
1459 
dispatch_real_in_place_div_rational_impl(real & a,const::mpq_t q,::mpfr_prec_t dprec)1460 void dispatch_real_in_place_div_rational_impl(real &a, const ::mpq_t q, ::mpfr_prec_t dprec)
1461 {
1462     auto wrapper = [&q](::mpfr_t r, const ::mpfr_t o) { ::mpfr_div_q(r, o, q, MPFR_RNDN); };
1463 
1464     mpfr_nary_op_impl<false>(dprec, wrapper, a, a);
1465 }
1466 
1467 namespace
1468 {
1469 
1470 template <typename T>
dispatch_real_in_place_div_fd_impl(real & a,const T & x)1471 inline void dispatch_real_in_place_div_fd_impl(real &a, const T &x)
1472 {
1473     // NOTE: the MPFR docs state that mpfr_div_d() assumes that
1474     // the radix of double is a power of 2. If we ever run into platforms
1475     // for which this is not true, we can add a compile-time dispatch
1476     // that uses the long double implementation instead.
1477     constexpr auto dradix = static_cast<unsigned>(std::numeric_limits<double>::radix);
1478     static_assert(!(dradix & (dradix - 1)), "mpfr_div_d() requires the radix of the 'double' type to be a power of 2.");
1479 
1480     auto wrapper = [x](::mpfr_t r, const ::mpfr_t o) { ::mpfr_div_d(r, o, static_cast<double>(x), MPFR_RNDN); };
1481 
1482     mpfr_nary_op_impl<false>(real_deduce_precision(x), wrapper, a, a);
1483 }
1484 
1485 } // namespace
1486 
dispatch_real_in_place_div(real & a,const float & x)1487 void dispatch_real_in_place_div(real &a, const float &x)
1488 {
1489     dispatch_real_in_place_div_fd_impl(a, x);
1490 }
1491 
dispatch_real_in_place_div(real & a,const double & x)1492 void dispatch_real_in_place_div(real &a, const double &x)
1493 {
1494     dispatch_real_in_place_div_fd_impl(a, x);
1495 }
1496 
1497 namespace
1498 {
1499 
1500 template <typename T>
dispatch_real_in_place_div_generic_impl(real & a,const T & x)1501 inline void dispatch_real_in_place_div_generic_impl(real &a, const T &x)
1502 {
1503     MPPP_MAYBE_TLS real tmp;
1504     tmp.set_prec(c_max(a.get_prec(), real_deduce_precision(x)));
1505     tmp.set(x);
1506     dispatch_real_in_place_div(a, tmp);
1507 }
1508 
1509 } // namespace
1510 
dispatch_real_in_place_div(real & a,const long double & x)1511 void dispatch_real_in_place_div(real &a, const long double &x)
1512 {
1513     dispatch_real_in_place_div_generic_impl(a, x);
1514 }
1515 
1516 #if defined(MPPP_WITH_QUADMATH)
1517 
dispatch_real_in_place_div(real & a,const real128 & x)1518 void dispatch_real_in_place_div(real &a, const real128 &x)
1519 {
1520     dispatch_real_in_place_div_generic_impl(a, x);
1521 }
1522 
1523 #endif
1524 
1525 } // namespace detail
1526 
1527 // Implementation details for equality.
1528 namespace detail
1529 {
1530 
dispatch_real_equality(const real & x,const real & y)1531 bool dispatch_real_equality(const real &x, const real &y)
1532 {
1533     return ::mpfr_equal_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
1534 }
1535 
dispatch_real_equality_integer_impl(const real & r,const::mpz_t n)1536 bool dispatch_real_equality_integer_impl(const real &r, const ::mpz_t n)
1537 {
1538     if (r.nan_p()) {
1539         return false;
1540     } else {
1541         return ::mpfr_cmp_z(r.get_mpfr_t(), n) == 0;
1542     }
1543 }
1544 
dispatch_real_equality(const real & r,bool b)1545 bool dispatch_real_equality(const real &r, bool b)
1546 {
1547     if (r.nan_p()) {
1548         return false;
1549     } else {
1550         return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) == 0;
1551     }
1552 }
1553 
dispatch_real_equality_rational_impl(const real & r,const::mpq_t q)1554 bool dispatch_real_equality_rational_impl(const real &r, const ::mpq_t q)
1555 {
1556     if (r.nan_p()) {
1557         return false;
1558     } else {
1559         return ::mpfr_cmp_q(r.get_mpfr_t(), q) == 0;
1560     }
1561 }
1562 
dispatch_real_equality(const real & r,const float & x)1563 bool dispatch_real_equality(const real &r, const float &x)
1564 {
1565     if (r.nan_p() || std::isnan(x)) {
1566         return false;
1567     } else {
1568         return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) == 0;
1569     }
1570 }
1571 
dispatch_real_equality(const real & r,const double & x)1572 bool dispatch_real_equality(const real &r, const double &x)
1573 {
1574     if (r.nan_p() || std::isnan(x)) {
1575         return false;
1576     } else {
1577         return ::mpfr_cmp_d(r.get_mpfr_t(), x) == 0;
1578     }
1579 }
1580 
dispatch_real_equality(const real & r,const long double & x)1581 bool dispatch_real_equality(const real &r, const long double &x)
1582 {
1583     if (r.nan_p() || std::isnan(x)) {
1584         return false;
1585     } else {
1586         return ::mpfr_cmp_ld(r.get_mpfr_t(), x) == 0;
1587     }
1588 }
1589 
1590 #if defined(MPPP_WITH_QUADMATH)
1591 
dispatch_real_equality(const real & r1,const real128 & r2)1592 bool dispatch_real_equality(const real &r1, const real128 &r2)
1593 {
1594     // NOTE: straight assignment here is fine: tmp
1595     // will represent r2 exactly.
1596     MPPP_MAYBE_TLS real tmp;
1597     tmp = r2;
1598 
1599     return dispatch_real_equality(r1, tmp);
1600 }
1601 
1602 #endif
1603 
1604 } // namespace detail
1605 
1606 // Implementation details for gt.
1607 namespace detail
1608 {
1609 
dispatch_real_gt(const real & x,const real & y)1610 bool dispatch_real_gt(const real &x, const real &y)
1611 {
1612     return ::mpfr_greater_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
1613 }
1614 
dispatch_real_gt_integer_impl(const real & r,const::mpz_t n)1615 bool dispatch_real_gt_integer_impl(const real &r, const ::mpz_t n)
1616 {
1617     if (r.nan_p()) {
1618         return false;
1619     } else {
1620         return ::mpfr_cmp_z(r.get_mpfr_t(), n) > 0;
1621     }
1622 }
1623 
dispatch_real_gt_integer_impl(const::mpz_t n,const real & r)1624 bool dispatch_real_gt_integer_impl(const ::mpz_t n, const real &r)
1625 {
1626     if (r.nan_p()) {
1627         return false;
1628     } else {
1629         return ::mpfr_cmp_z(r.get_mpfr_t(), n) < 0;
1630     }
1631 }
1632 
dispatch_real_gt(const real & r,bool b)1633 bool dispatch_real_gt(const real &r, bool b)
1634 {
1635     if (r.nan_p()) {
1636         return false;
1637     } else {
1638         return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) > 0;
1639     }
1640 }
1641 
dispatch_real_gt(bool b,const real & r)1642 bool dispatch_real_gt(bool b, const real &r)
1643 {
1644     if (r.nan_p()) {
1645         return false;
1646     } else {
1647         return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) < 0;
1648     }
1649 }
1650 
dispatch_real_gt_rational_impl(const real & r,const::mpq_t q)1651 bool dispatch_real_gt_rational_impl(const real &r, const ::mpq_t q)
1652 {
1653     if (r.nan_p()) {
1654         return false;
1655     } else {
1656         return ::mpfr_cmp_q(r.get_mpfr_t(), q) > 0;
1657     }
1658 }
1659 
dispatch_real_gt_rational_impl(const::mpq_t q,const real & r)1660 bool dispatch_real_gt_rational_impl(const ::mpq_t q, const real &r)
1661 {
1662     if (r.nan_p()) {
1663         return false;
1664     } else {
1665         return ::mpfr_cmp_q(r.get_mpfr_t(), q) < 0;
1666     }
1667 }
1668 
dispatch_real_gt(const real & r,const float & x)1669 bool dispatch_real_gt(const real &r, const float &x)
1670 {
1671     if (r.nan_p() || std::isnan(x)) {
1672         return false;
1673     } else {
1674         return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) > 0;
1675     }
1676 }
1677 
dispatch_real_gt(const real & r,const double & x)1678 bool dispatch_real_gt(const real &r, const double &x)
1679 {
1680     if (r.nan_p() || std::isnan(x)) {
1681         return false;
1682     } else {
1683         return ::mpfr_cmp_d(r.get_mpfr_t(), x) > 0;
1684     }
1685 }
1686 
dispatch_real_gt(const real & r,const long double & x)1687 bool dispatch_real_gt(const real &r, const long double &x)
1688 {
1689     if (r.nan_p() || std::isnan(x)) {
1690         return false;
1691     } else {
1692         return ::mpfr_cmp_ld(r.get_mpfr_t(), x) > 0;
1693     }
1694 }
1695 
dispatch_real_gt(const float & x,const real & r)1696 bool dispatch_real_gt(const float &x, const real &r)
1697 {
1698     if (r.nan_p() || std::isnan(x)) {
1699         return false;
1700     } else {
1701         return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) < 0;
1702     }
1703 }
1704 
dispatch_real_gt(const double & x,const real & r)1705 bool dispatch_real_gt(const double &x, const real &r)
1706 {
1707     if (r.nan_p() || std::isnan(x)) {
1708         return false;
1709     } else {
1710         return ::mpfr_cmp_d(r.get_mpfr_t(), x) < 0;
1711     }
1712 }
1713 
dispatch_real_gt(const long double & x,const real & r)1714 bool dispatch_real_gt(const long double &x, const real &r)
1715 {
1716     if (r.nan_p() || std::isnan(x)) {
1717         return false;
1718     } else {
1719         return ::mpfr_cmp_ld(r.get_mpfr_t(), x) < 0;
1720     }
1721 }
1722 
1723 #if defined(MPPP_WITH_QUADMATH)
1724 
dispatch_real_gt(const real & r1,const real128 & r2)1725 bool dispatch_real_gt(const real &r1, const real128 &r2)
1726 {
1727     // NOTE: straight assignment here is fine: tmp
1728     // will represent r2 exactly.
1729     MPPP_MAYBE_TLS real tmp;
1730     tmp = r2;
1731 
1732     return dispatch_real_gt(r1, tmp);
1733 }
1734 
dispatch_real_gt(const real128 & r1,const real & r2)1735 bool dispatch_real_gt(const real128 &r1, const real &r2)
1736 {
1737     MPPP_MAYBE_TLS real tmp;
1738     tmp = r1;
1739 
1740     return dispatch_real_gt(tmp, r2);
1741 }
1742 
1743 #endif
1744 
1745 } // namespace detail
1746 
1747 // Implementation details for gte.
1748 namespace detail
1749 {
1750 
dispatch_real_gte(const real & x,const real & y)1751 bool dispatch_real_gte(const real &x, const real &y)
1752 {
1753     return ::mpfr_greaterequal_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
1754 }
1755 
dispatch_real_gte_integer_impl(const real & r,const::mpz_t n)1756 bool dispatch_real_gte_integer_impl(const real &r, const ::mpz_t n)
1757 {
1758     if (r.nan_p()) {
1759         return false;
1760     } else {
1761         return ::mpfr_cmp_z(r.get_mpfr_t(), n) >= 0;
1762     }
1763 }
1764 
dispatch_real_gte_integer_impl(const::mpz_t n,const real & r)1765 bool dispatch_real_gte_integer_impl(const ::mpz_t n, const real &r)
1766 {
1767     if (r.nan_p()) {
1768         return false;
1769     } else {
1770         return ::mpfr_cmp_z(r.get_mpfr_t(), n) <= 0;
1771     }
1772 }
1773 
dispatch_real_gte(const real & r,bool b)1774 bool dispatch_real_gte(const real &r, bool b)
1775 {
1776     if (r.nan_p()) {
1777         return false;
1778     } else {
1779         return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) >= 0;
1780     }
1781 }
1782 
dispatch_real_gte(bool b,const real & r)1783 bool dispatch_real_gte(bool b, const real &r)
1784 {
1785     if (r.nan_p()) {
1786         return false;
1787     } else {
1788         return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) <= 0;
1789     }
1790 }
1791 
dispatch_real_gte_rational_impl(const real & r,const::mpq_t q)1792 bool dispatch_real_gte_rational_impl(const real &r, const ::mpq_t q)
1793 {
1794     if (r.nan_p()) {
1795         return false;
1796     } else {
1797         return ::mpfr_cmp_q(r.get_mpfr_t(), q) >= 0;
1798     }
1799 }
1800 
dispatch_real_gte_rational_impl(const::mpq_t q,const real & r)1801 bool dispatch_real_gte_rational_impl(const ::mpq_t q, const real &r)
1802 {
1803     if (r.nan_p()) {
1804         return false;
1805     } else {
1806         return ::mpfr_cmp_q(r.get_mpfr_t(), q) <= 0;
1807     }
1808 }
1809 
dispatch_real_gte(const real & r,const float & x)1810 bool dispatch_real_gte(const real &r, const float &x)
1811 {
1812     if (r.nan_p() || std::isnan(x)) {
1813         return false;
1814     } else {
1815         return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) >= 0;
1816     }
1817 }
1818 
dispatch_real_gte(const real & r,const double & x)1819 bool dispatch_real_gte(const real &r, const double &x)
1820 {
1821     if (r.nan_p() || std::isnan(x)) {
1822         return false;
1823     } else {
1824         return ::mpfr_cmp_d(r.get_mpfr_t(), x) >= 0;
1825     }
1826 }
1827 
dispatch_real_gte(const real & r,const long double & x)1828 bool dispatch_real_gte(const real &r, const long double &x)
1829 {
1830     if (r.nan_p() || std::isnan(x)) {
1831         return false;
1832     } else {
1833         return ::mpfr_cmp_ld(r.get_mpfr_t(), x) >= 0;
1834     }
1835 }
1836 
dispatch_real_gte(const float & x,const real & r)1837 bool dispatch_real_gte(const float &x, const real &r)
1838 {
1839     if (r.nan_p() || std::isnan(x)) {
1840         return false;
1841     } else {
1842         return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) <= 0;
1843     }
1844 }
1845 
dispatch_real_gte(const double & x,const real & r)1846 bool dispatch_real_gte(const double &x, const real &r)
1847 {
1848     if (r.nan_p() || std::isnan(x)) {
1849         return false;
1850     } else {
1851         return ::mpfr_cmp_d(r.get_mpfr_t(), x) <= 0;
1852     }
1853 }
1854 
dispatch_real_gte(const long double & x,const real & r)1855 bool dispatch_real_gte(const long double &x, const real &r)
1856 {
1857     if (r.nan_p() || std::isnan(x)) {
1858         return false;
1859     } else {
1860         return ::mpfr_cmp_ld(r.get_mpfr_t(), x) <= 0;
1861     }
1862 }
1863 
1864 #if defined(MPPP_WITH_QUADMATH)
1865 
dispatch_real_gte(const real & r1,const real128 & r2)1866 bool dispatch_real_gte(const real &r1, const real128 &r2)
1867 {
1868     // NOTE: straight assignment here is fine: tmp
1869     // will represent r2 exactly.
1870     MPPP_MAYBE_TLS real tmp;
1871     tmp = r2;
1872 
1873     return dispatch_real_gte(r1, tmp);
1874 }
1875 
dispatch_real_gte(const real128 & r1,const real & r2)1876 bool dispatch_real_gte(const real128 &r1, const real &r2)
1877 {
1878     MPPP_MAYBE_TLS real tmp;
1879     tmp = r1;
1880 
1881     return dispatch_real_gte(tmp, r2);
1882 }
1883 
1884 #endif
1885 
1886 } // namespace detail
1887 
1888 // Implementation details for lt.
1889 namespace detail
1890 {
1891 
dispatch_real_lt(const real & x,const real & y)1892 bool dispatch_real_lt(const real &x, const real &y)
1893 {
1894     return ::mpfr_less_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
1895 }
1896 
dispatch_real_lt_integer_impl(const real & r,const::mpz_t n)1897 bool dispatch_real_lt_integer_impl(const real &r, const ::mpz_t n)
1898 {
1899     if (r.nan_p()) {
1900         return false;
1901     } else {
1902         return ::mpfr_cmp_z(r.get_mpfr_t(), n) < 0;
1903     }
1904 }
1905 
dispatch_real_lt_integer_impl(const::mpz_t n,const real & r)1906 bool dispatch_real_lt_integer_impl(const ::mpz_t n, const real &r)
1907 {
1908     if (r.nan_p()) {
1909         return false;
1910     } else {
1911         return ::mpfr_cmp_z(r.get_mpfr_t(), n) > 0;
1912     }
1913 }
1914 
dispatch_real_lt(const real & r,bool b)1915 bool dispatch_real_lt(const real &r, bool b)
1916 {
1917     if (r.nan_p()) {
1918         return false;
1919     } else {
1920         return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) < 0;
1921     }
1922 }
1923 
dispatch_real_lt(bool b,const real & r)1924 bool dispatch_real_lt(bool b, const real &r)
1925 {
1926     if (r.nan_p()) {
1927         return false;
1928     } else {
1929         return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) > 0;
1930     }
1931 }
1932 
dispatch_real_lt_rational_impl(const real & r,const::mpq_t q)1933 bool dispatch_real_lt_rational_impl(const real &r, const ::mpq_t q)
1934 {
1935     if (r.nan_p()) {
1936         return false;
1937     } else {
1938         return ::mpfr_cmp_q(r.get_mpfr_t(), q) < 0;
1939     }
1940 }
1941 
dispatch_real_lt_rational_impl(const::mpq_t q,const real & r)1942 bool dispatch_real_lt_rational_impl(const ::mpq_t q, const real &r)
1943 {
1944     if (r.nan_p()) {
1945         return false;
1946     } else {
1947         return ::mpfr_cmp_q(r.get_mpfr_t(), q) > 0;
1948     }
1949 }
1950 
dispatch_real_lt(const real & r,const float & x)1951 bool dispatch_real_lt(const real &r, const float &x)
1952 {
1953     if (r.nan_p() || std::isnan(x)) {
1954         return false;
1955     } else {
1956         return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) < 0;
1957     }
1958 }
1959 
dispatch_real_lt(const real & r,const double & x)1960 bool dispatch_real_lt(const real &r, const double &x)
1961 {
1962     if (r.nan_p() || std::isnan(x)) {
1963         return false;
1964     } else {
1965         return ::mpfr_cmp_d(r.get_mpfr_t(), x) < 0;
1966     }
1967 }
1968 
dispatch_real_lt(const real & r,const long double & x)1969 bool dispatch_real_lt(const real &r, const long double &x)
1970 {
1971     if (r.nan_p() || std::isnan(x)) {
1972         return false;
1973     } else {
1974         return ::mpfr_cmp_ld(r.get_mpfr_t(), x) < 0;
1975     }
1976 }
1977 
dispatch_real_lt(const float & x,const real & r)1978 bool dispatch_real_lt(const float &x, const real &r)
1979 {
1980     if (r.nan_p() || std::isnan(x)) {
1981         return false;
1982     } else {
1983         return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) > 0;
1984     }
1985 }
1986 
dispatch_real_lt(const double & x,const real & r)1987 bool dispatch_real_lt(const double &x, const real &r)
1988 {
1989     if (r.nan_p() || std::isnan(x)) {
1990         return false;
1991     } else {
1992         return ::mpfr_cmp_d(r.get_mpfr_t(), x) > 0;
1993     }
1994 }
1995 
dispatch_real_lt(const long double & x,const real & r)1996 bool dispatch_real_lt(const long double &x, const real &r)
1997 {
1998     if (r.nan_p() || std::isnan(x)) {
1999         return false;
2000     } else {
2001         return ::mpfr_cmp_ld(r.get_mpfr_t(), x) > 0;
2002     }
2003 }
2004 
2005 #if defined(MPPP_WITH_QUADMATH)
2006 
dispatch_real_lt(const real & r1,const real128 & r2)2007 bool dispatch_real_lt(const real &r1, const real128 &r2)
2008 {
2009     // NOTE: straight assignment here is fine: tmp
2010     // will represent r2 exactly.
2011     MPPP_MAYBE_TLS real tmp;
2012     tmp = r2;
2013 
2014     return dispatch_real_lt(r1, tmp);
2015 }
2016 
dispatch_real_lt(const real128 & r1,const real & r2)2017 bool dispatch_real_lt(const real128 &r1, const real &r2)
2018 {
2019     MPPP_MAYBE_TLS real tmp;
2020     tmp = r1;
2021 
2022     return dispatch_real_lt(tmp, r2);
2023 }
2024 
2025 #endif
2026 
2027 } // namespace detail
2028 
2029 // Implementation details for lte.
2030 namespace detail
2031 {
2032 
dispatch_real_lte(const real & x,const real & y)2033 bool dispatch_real_lte(const real &x, const real &y)
2034 {
2035     return ::mpfr_lessequal_p(x.get_mpfr_t(), y.get_mpfr_t()) != 0;
2036 }
2037 
dispatch_real_lte_integer_impl(const real & r,const::mpz_t n)2038 bool dispatch_real_lte_integer_impl(const real &r, const ::mpz_t n)
2039 {
2040     if (r.nan_p()) {
2041         return false;
2042     } else {
2043         return ::mpfr_cmp_z(r.get_mpfr_t(), n) <= 0;
2044     }
2045 }
2046 
dispatch_real_lte_integer_impl(const::mpz_t n,const real & r)2047 bool dispatch_real_lte_integer_impl(const ::mpz_t n, const real &r)
2048 {
2049     if (r.nan_p()) {
2050         return false;
2051     } else {
2052         return ::mpfr_cmp_z(r.get_mpfr_t(), n) >= 0;
2053     }
2054 }
2055 
dispatch_real_lte(const real & r,bool b)2056 bool dispatch_real_lte(const real &r, bool b)
2057 {
2058     if (r.nan_p()) {
2059         return false;
2060     } else {
2061         return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) <= 0;
2062     }
2063 }
2064 
dispatch_real_lte(bool b,const real & r)2065 bool dispatch_real_lte(bool b, const real &r)
2066 {
2067     if (r.nan_p()) {
2068         return false;
2069     } else {
2070         return mpfr_cmp_ui(r.get_mpfr_t(), static_cast<unsigned long>(b)) >= 0;
2071     }
2072 }
2073 
dispatch_real_lte_rational_impl(const real & r,const::mpq_t q)2074 bool dispatch_real_lte_rational_impl(const real &r, const ::mpq_t q)
2075 {
2076     if (r.nan_p()) {
2077         return false;
2078     } else {
2079         return ::mpfr_cmp_q(r.get_mpfr_t(), q) <= 0;
2080     }
2081 }
2082 
dispatch_real_lte_rational_impl(const::mpq_t q,const real & r)2083 bool dispatch_real_lte_rational_impl(const ::mpq_t q, const real &r)
2084 {
2085     if (r.nan_p()) {
2086         return false;
2087     } else {
2088         return ::mpfr_cmp_q(r.get_mpfr_t(), q) >= 0;
2089     }
2090 }
2091 
dispatch_real_lte(const real & r,const float & x)2092 bool dispatch_real_lte(const real &r, const float &x)
2093 {
2094     if (r.nan_p() || std::isnan(x)) {
2095         return false;
2096     } else {
2097         return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) <= 0;
2098     }
2099 }
2100 
dispatch_real_lte(const real & r,const double & x)2101 bool dispatch_real_lte(const real &r, const double &x)
2102 {
2103     if (r.nan_p() || std::isnan(x)) {
2104         return false;
2105     } else {
2106         return ::mpfr_cmp_d(r.get_mpfr_t(), x) <= 0;
2107     }
2108 }
2109 
dispatch_real_lte(const real & r,const long double & x)2110 bool dispatch_real_lte(const real &r, const long double &x)
2111 {
2112     if (r.nan_p() || std::isnan(x)) {
2113         return false;
2114     } else {
2115         return ::mpfr_cmp_ld(r.get_mpfr_t(), x) <= 0;
2116     }
2117 }
2118 
dispatch_real_lte(const float & x,const real & r)2119 bool dispatch_real_lte(const float &x, const real &r)
2120 {
2121     if (r.nan_p() || std::isnan(x)) {
2122         return false;
2123     } else {
2124         return ::mpfr_cmp_d(r.get_mpfr_t(), static_cast<double>(x)) >= 0;
2125     }
2126 }
2127 
dispatch_real_lte(const double & x,const real & r)2128 bool dispatch_real_lte(const double &x, const real &r)
2129 {
2130     if (r.nan_p() || std::isnan(x)) {
2131         return false;
2132     } else {
2133         return ::mpfr_cmp_d(r.get_mpfr_t(), x) >= 0;
2134     }
2135 }
2136 
dispatch_real_lte(const long double & x,const real & r)2137 bool dispatch_real_lte(const long double &x, const real &r)
2138 {
2139     if (r.nan_p() || std::isnan(x)) {
2140         return false;
2141     } else {
2142         return ::mpfr_cmp_ld(r.get_mpfr_t(), x) >= 0;
2143     }
2144 }
2145 
2146 #if defined(MPPP_WITH_QUADMATH)
2147 
dispatch_real_lte(const real & r1,const real128 & r2)2148 bool dispatch_real_lte(const real &r1, const real128 &r2)
2149 {
2150     // NOTE: straight assignment here is fine: tmp
2151     // will represent r2 exactly.
2152     MPPP_MAYBE_TLS real tmp;
2153     tmp = r2;
2154 
2155     return dispatch_real_lte(r1, tmp);
2156 }
2157 
dispatch_real_lte(const real128 & r1,const real & r2)2158 bool dispatch_real_lte(const real128 &r1, const real &r2)
2159 {
2160     MPPP_MAYBE_TLS real tmp;
2161     tmp = r1;
2162 
2163     return dispatch_real_lte(tmp, r2);
2164 }
2165 
2166 #endif
2167 
2168 } // namespace detail
2169 
2170 // Three-way comparison.
cmp(const real & a,const real & b)2171 int cmp(const real &a, const real &b)
2172 {
2173     ::mpfr_clear_erangeflag();
2174     auto retval = mpfr_cmp(a.get_mpfr_t(), b.get_mpfr_t());
2175     if (mppp_unlikely(::mpfr_erangeflag_p())) {
2176         ::mpfr_clear_erangeflag();
2177         throw std::domain_error("Cannot compare two reals if at least one of them is NaN");
2178     }
2179     return retval;
2180 }
2181 
2182 // Three-way comparison of absolute values.
cmpabs(const real & a,const real & b)2183 int cmpabs(const real &a, const real &b)
2184 {
2185     ::mpfr_clear_erangeflag();
2186     auto retval = ::mpfr_cmpabs(a.get_mpfr_t(), b.get_mpfr_t());
2187     if (mppp_unlikely(::mpfr_erangeflag_p())) {
2188         ::mpfr_clear_erangeflag();
2189         throw std::domain_error("Cannot compare the absolute values of two reals if at least one of them is NaN");
2190     }
2191     return retval;
2192 }
2193 
2194 // Comparison with n*2**e.
cmp_ui_2exp(const real & x,unsigned long n,::mpfr_exp_t e)2195 int cmp_ui_2exp(const real &x, unsigned long n, ::mpfr_exp_t e)
2196 {
2197     ::mpfr_clear_erangeflag();
2198     auto retval = ::mpfr_cmp_ui_2exp(x.get_mpfr_t(), n, e);
2199     if (mppp_unlikely(::mpfr_erangeflag_p())) {
2200         ::mpfr_clear_erangeflag();
2201         throw std::domain_error("Cannot compare a real NaN to an integral multiple of a power of 2");
2202     }
2203     return retval;
2204 }
2205 
cmp_si_2exp(const real & x,long n,::mpfr_exp_t e)2206 int cmp_si_2exp(const real &x, long n, ::mpfr_exp_t e)
2207 {
2208     ::mpfr_clear_erangeflag();
2209     auto retval = ::mpfr_cmp_si_2exp(x.get_mpfr_t(), n, e);
2210     if (mppp_unlikely(::mpfr_erangeflag_p())) {
2211         ::mpfr_clear_erangeflag();
2212         throw std::domain_error("Cannot compare a real NaN to an integral multiple of a power of 2");
2213     }
2214     return retval;
2215 }
2216 
2217 // Equality predicate with special NaN handling.
real_equal_to(const real & a,const real & b)2218 bool real_equal_to(const real &a, const real &b)
2219 {
2220     const bool a_nan = a.nan_p(), b_nan = b.nan_p();
2221     return (!a_nan && !b_nan) ? (::mpfr_equal_p(a.get_mpfr_t(), b.get_mpfr_t()) != 0) : (a_nan && b_nan);
2222 }
2223 
2224 // Less-than predicate with special NaN/moved handling.
real_lt(const real & a,const real & b)2225 bool real_lt(const real &a, const real &b)
2226 {
2227     if (!a.is_valid()) {
2228         // a is moved-from, consider it the largest possible value.
2229         return false;
2230     }
2231     if (!b.is_valid()) {
2232         // a is not moved-from, b is. a is smaller.
2233         return true;
2234     }
2235     const bool a_nan = a.nan_p();
2236     return (!a_nan && !b.nan_p()) ? (::mpfr_less_p(a.get_mpfr_t(), b.get_mpfr_t()) != 0) : !a_nan;
2237 }
2238 
2239 // Greater-than predicate with special NaN/moved handling.
real_gt(const real & a,const real & b)2240 bool real_gt(const real &a, const real &b)
2241 {
2242     if (!b.is_valid()) {
2243         // b is moved-from, nothing can be bigger.
2244         return false;
2245     }
2246     if (!a.is_valid()) {
2247         // b is not moved-from, a is. a is bigger.
2248         return true;
2249     }
2250     const bool b_nan = b.nan_p();
2251     return (!a.nan_p() && !b_nan) ? (::mpfr_greater_p(a.get_mpfr_t(), b.get_mpfr_t()) != 0) : !b_nan;
2252 }
2253 
2254 // Output stream operator.
operator <<(std::ostream & os,const real & r)2255 std::ostream &operator<<(std::ostream &os, const real &r)
2256 {
2257     // Get the stream width.
2258     const auto width = os.width();
2259 
2260     // Fetch the stream's flags.
2261     const auto flags = os.flags();
2262 
2263     // Check if we are using scientific, fixed or both.
2264     const auto scientific = (flags & std::ios_base::scientific) != 0;
2265     const auto fixed = (flags & std::ios_base::fixed) != 0;
2266     const auto hexfloat = scientific && fixed;
2267 
2268     // Force decimal point character?
2269     const auto showpoint = (flags & std::ios_base::showpoint) != 0;
2270 
2271     // Force the plus sign?
2272     const bool with_plus = (flags & std::ios_base::showpos) != 0;
2273 
2274     // Uppercase?
2275     const bool uppercase = (flags & std::ios_base::uppercase) != 0;
2276 
2277     // Fetch the precision too.
2278     auto precision = os.precision();
2279     // NOTE: if the precision is negative, reset it
2280     // to the default value of 6.
2281     if (precision < 0) {
2282         precision = 6;
2283     }
2284 
2285     // Put together the format string.
2286     std::ostringstream oss;
2287     oss.exceptions(std::ios_base::failbit | std::ios_base::badbit);
2288     oss.imbue(std::locale::classic());
2289 
2290     oss << '%';
2291 
2292     if (showpoint) {
2293         oss << '#';
2294     }
2295 
2296     if (with_plus) {
2297         oss << '+';
2298     }
2299 
2300     if (hexfloat) {
2301         // NOTE: in hexfloat mode, we want to ignore the precision
2302         // setting in the stream object and print the exact representation:
2303         // https://en.cppreference.com/w/cpp/locale/num_put/put
2304         oss << 'R';
2305     } else {
2306         oss << '.' << precision << 'R';
2307     }
2308 
2309     if (hexfloat) {
2310         oss << (uppercase ? 'A' : 'a');
2311     } else if (scientific) {
2312         oss << (uppercase ? 'E' : 'e');
2313     } else if (fixed) {
2314         // NOTE: in fixed format, the uppercase
2315         // setting is ignored for floating-point types:
2316         // https://en.cppreference.com/w/cpp/locale/num_put/put
2317         oss << 'f';
2318     } else {
2319         oss << (uppercase ? 'G' : 'g');
2320     }
2321 
2322     const auto fmt_str = oss.str();
2323 
2324     // Print out via the MPFR printf() function.
2325     char *out_str = nullptr;
2326     const auto ret = ::mpfr_asprintf(&out_str, fmt_str.c_str(), r.get_mpfr_t());
2327     if (ret == -1) {
2328         // LCOV_EXCL_START
2329         // NOTE: the MPFR docs state that if printf() returns -1, the errno variable and erange
2330         // flag are set. Let's clear them out before throwing.
2331         errno = 0;
2332         ::mpfr_clear_erangeflag();
2333 
2334         throw std::invalid_argument("The mpfr_asprintf() function returned the error code -1");
2335         // LCOV_EXCL_STOP
2336     }
2337 
2338     // printf() was successful, let's set up the RAII machinery to automatically
2339     // clear out_str before anything else.
2340     // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
2341     struct out_str_cleaner {
2342         ~out_str_cleaner()
2343         {
2344             ::mpfr_free_str(ptr);
2345         }
2346         char *ptr;
2347     };
2348 
2349     out_str_cleaner osc{out_str};
2350 
2351     // NOTE: I am not sure if it is possible to get a string of size 0
2352     // from a printf() function. Let's throw for the time being in such a case,
2353     // as zero-length output complicates indexing below.
2354     if (ret == 0) {
2355         // LCOV_EXCL_START
2356         throw std::invalid_argument("The mpfr_asprintf() function returned an empty string");
2357         // LCOV_EXCL_STOP
2358     }
2359 
2360     // We are going to do the filling
2361     // only if the stream width is larger
2362     // than the total size of the string repr.
2363     // NOTE: width > ret already implies width >= 0.
2364     if (width > ret) {
2365         // Create a std::string from the string outout
2366         // of printf().
2367         std::string tmp(out_str, out_str + ret);
2368 
2369         // Determine the fill type.
2370         const auto fill = detail::stream_flags_to_fill(flags);
2371 
2372         // Compute how much fill we need.
2373         const auto fill_size = detail::safe_cast<decltype(tmp.size())>(width - ret);
2374 
2375         // Get the fill character.
2376         const auto fill_char = os.fill();
2377 
2378         switch (fill) {
2379             case 1:
2380                 // Left fill: fill characters at the end.
2381                 tmp.insert(tmp.end(), fill_size, fill_char);
2382                 break;
2383             case 2:
2384                 // Right fill: fill characters at the beginning.
2385                 tmp.insert(tmp.begin(), fill_size, fill_char);
2386                 break;
2387             default: {
2388                 assert(fill == 3);
2389 
2390                 // Internal fill: the fill characters are always after the sign (if present).
2391                 const auto delta = static_cast<int>(tmp[0] == '+' || tmp[0] == '-');
2392                 tmp.insert(tmp.begin() + delta, fill_size, fill_char);
2393                 break;
2394             }
2395         }
2396 
2397         os.write(tmp.data(), detail::safe_cast<std::streamsize>(tmp.size()));
2398     } else {
2399         os.write(out_str, detail::safe_cast<std::streamsize>(ret));
2400     }
2401 
2402     // Reset the stream width to zero, like the operator<<() does for builtin types.
2403     // https://en.cppreference.com/w/cpp/io/manip/setw
2404     // Do it here so we ensure we don't alter the state of the stream until the very end.
2405     os.width(0);
2406 
2407     return os;
2408 }
2409 
2410 namespace detail
2411 {
2412 
2413 // NOTE: don't put in unnamed namespace as
2414 // this needs do be just in detail:: for friendship
2415 // with real.
2416 template <typename F>
real_constant(const F & f,::mpfr_prec_t p)2417 inline real real_constant(const F &f, ::mpfr_prec_t p)
2418 {
2419     if (mppp_unlikely(!real_prec_check(p))) {
2420         throw std::invalid_argument("Cannot init a real constant with a precision of " + detail::to_string(p)
2421                                     + ": the value must be between " + detail::to_string(real_prec_min()) + " and "
2422                                     + detail::to_string(real_prec_max()));
2423     }
2424 
2425     real retval{real::ptag{}, p, true};
2426     f(retval._get_mpfr_t(), MPFR_RNDN);
2427     return retval;
2428 }
2429 
2430 } // namespace detail
2431 
2432 // Pi constant.
real_pi(::mpfr_prec_t p)2433 real real_pi(::mpfr_prec_t p)
2434 {
2435     return detail::real_constant(::mpfr_const_pi, p);
2436 }
2437 
real_pi(real & rop)2438 real &real_pi(real &rop)
2439 {
2440     ::mpfr_const_pi(rop._get_mpfr_t(), MPFR_RNDN);
2441     return rop;
2442 }
2443 
real_log2(::mpfr_prec_t p)2444 real real_log2(::mpfr_prec_t p)
2445 {
2446     return detail::real_constant(::mpfr_const_log2, p);
2447 }
2448 
real_log2(real & rop)2449 real &real_log2(real &rop)
2450 {
2451     ::mpfr_const_log2(rop._get_mpfr_t(), MPFR_RNDN);
2452     return rop;
2453 }
2454 
real_euler(::mpfr_prec_t p)2455 real real_euler(::mpfr_prec_t p)
2456 {
2457     return detail::real_constant(::mpfr_const_euler, p);
2458 }
2459 
real_euler(real & rop)2460 real &real_euler(real &rop)
2461 {
2462     ::mpfr_const_euler(rop._get_mpfr_t(), MPFR_RNDN);
2463     return rop;
2464 }
2465 
real_catalan(::mpfr_prec_t p)2466 real real_catalan(::mpfr_prec_t p)
2467 {
2468     return detail::real_constant(::mpfr_const_catalan, p);
2469 }
2470 
real_catalan(real & rop)2471 real &real_catalan(real &rop)
2472 {
2473     ::mpfr_const_catalan(rop._get_mpfr_t(), MPFR_RNDN);
2474     return rop;
2475 }
2476 
2477 namespace detail
2478 {
2479 
2480 namespace
2481 {
2482 
2483 // std::size_t addition with overflow checking.
rbs_checked_add(std::size_t a,std::size_t b)2484 std::size_t rbs_checked_add(std::size_t a, std::size_t b)
2485 {
2486     // LCOV_EXCL_START
2487     if (mppp_unlikely(a > std::numeric_limits<std::size_t>::max() - b)) {
2488         throw std::overflow_error("Overflow detected in the computation of the binary size of a real");
2489     }
2490     // LCOV_EXCL_STOP
2491 
2492     return a + b;
2493 }
2494 
2495 // Turn an MPFR precision into the number of limbs
2496 // necessary to represent the significand of
2497 // a number with that precision.
rbs_prec_to_nlimbs(::mpfr_prec_t p)2498 std::size_t rbs_prec_to_nlimbs(::mpfr_prec_t p)
2499 {
2500     // NOTE: currently both mpfr_prec_t and GMP_NUMB_BITS
2501     // are signed.
2502     using uprec_t = std::make_unsigned<::mpfr_prec_t>::type;
2503     return safe_cast<std::size_t>(static_cast<uprec_t>(p / GMP_NUMB_BITS + static_cast<int>((p % GMP_NUMB_BITS) != 0)));
2504 }
2505 
2506 // Turn an MPFR precision into the number of bytes
2507 // necessary to represent the significand of
2508 // a number with that precision.
rbs_prec_to_size(::mpfr_prec_t p)2509 std::size_t rbs_prec_to_size(::mpfr_prec_t p)
2510 {
2511     const auto nlimbs = rbs_prec_to_nlimbs(p);
2512 
2513     // LCOV_EXCL_START
2514     if (mppp_unlikely(nlimbs > std::numeric_limits<std::uint32_t>::max() / sizeof(::mp_limb_t))) {
2515         throw std::overflow_error("Overflow detected in the computation of the binary size of a real");
2516     }
2517     // LCOV_EXCL_STOP
2518 
2519     return nlimbs * sizeof(::mp_limb_t);
2520 }
2521 
2522 // The base binary size of a real: accounts for the size of precision,
2523 // sign bit and exponent.
rbs_base_size()2524 std::size_t rbs_base_size()
2525 {
2526     // Precision.
2527     auto retval = sizeof(::mpfr_prec_t);
2528     // Sign bit.
2529     retval = rbs_checked_add(retval, sizeof(::mpfr_sign_t));
2530     // Exponent.
2531     return rbs_checked_add(retval, sizeof(::mpfr_exp_t));
2532 }
2533 
2534 // The minimum binary size of a real (a real must have at least 1 limb
2535 // of data).
rbs_min_size()2536 std::size_t rbs_min_size()
2537 {
2538     return rbs_checked_add(rbs_base_size(), sizeof(::mp_limb_t));
2539 }
2540 
2541 // Load the serializaed precision value from a char buffer.
rbs_read_prec(const char * src)2542 ::mpfr_prec_t rbs_read_prec(const char *src)
2543 {
2544     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2545     ::mpfr_prec_t p;
2546     std::copy(src, src + sizeof(p), reinterpret_cast<char *>(&p));
2547     return p;
2548 }
2549 
2550 } // namespace
2551 
2552 } // namespace detail
2553 
2554 #if defined(MPPP_MPFR_HAVE_MPFR_GET_STR_NDIGITS)
2555 
2556 // Get the number of significant digits required for a round-tripping representation.
get_str_ndigits(int base) const2557 std::size_t real::get_str_ndigits(int base) const
2558 {
2559     if (mppp_unlikely(base < 2 || base > 62)) {
2560         throw std::invalid_argument(
2561             "Invalid base value for get_str_ndigits(): the base must be in the [2,62] range, but it is "
2562             + std::to_string(base) + " instead");
2563     }
2564 
2565     return ::mpfr_get_str_ndigits(base, get_prec());
2566 }
2567 
get_str_ndigits(const real & r,int base)2568 std::size_t get_str_ndigits(const real &r, int base)
2569 {
2570     return r.get_str_ndigits(base);
2571 }
2572 
2573 #endif
2574 
2575 // Size of the serialised binary representation: base size + limbs data.
binary_size() const2576 std::size_t real::binary_size() const
2577 {
2578     return detail::rbs_checked_add(detail::rbs_base_size(), detail::rbs_prec_to_size(get_prec()));
2579 }
2580 
binary_size(const real & x)2581 std::size_t binary_size(const real &x)
2582 {
2583     return x.binary_size();
2584 }
2585 
2586 // Save to a char buffer, given a binary size computed
2587 // via binary_size().
binary_save_impl(char * dest,std::size_t bs) const2588 void real::binary_save_impl(char *dest, std::size_t bs) const
2589 {
2590     // Save the precision.
2591     std::copy(reinterpret_cast<const char *>(&m_mpfr._mpfr_prec),
2592               reinterpret_cast<const char *>(&m_mpfr._mpfr_prec) + sizeof(::mpfr_prec_t), dest);
2593     dest += sizeof(::mpfr_prec_t);
2594 
2595     // Save the sign bit.
2596     std::copy(reinterpret_cast<const char *>(&m_mpfr._mpfr_sign),
2597               reinterpret_cast<const char *>(&m_mpfr._mpfr_sign) + sizeof(::mpfr_sign_t), dest);
2598     dest += sizeof(::mpfr_sign_t);
2599 
2600     // Save the exponent.
2601     std::copy(reinterpret_cast<const char *>(&m_mpfr._mpfr_exp),
2602               reinterpret_cast<const char *>(&m_mpfr._mpfr_exp) + sizeof(::mpfr_exp_t), dest);
2603     dest += sizeof(::mpfr_exp_t);
2604 
2605     // Save the significand.
2606     std::copy(reinterpret_cast<const char *>(m_mpfr._mpfr_d),
2607               reinterpret_cast<const char *>(m_mpfr._mpfr_d) + (bs - detail::rbs_base_size()), dest);
2608 }
2609 
2610 // Save to a char buffer.
binary_save(char * dest) const2611 std::size_t real::binary_save(char *dest) const
2612 {
2613     const auto bs = binary_size();
2614     binary_save_impl(dest, bs);
2615     return bs;
2616 }
2617 
2618 // Save to a std::vector buffer.
binary_save(std::vector<char> & dest) const2619 std::size_t real::binary_save(std::vector<char> &dest) const
2620 {
2621     const auto bs = binary_size();
2622     if (dest.size() < bs) {
2623         dest.resize(detail::safe_cast<decltype(dest.size())>(bs));
2624     }
2625     binary_save_impl(dest.data(), bs);
2626     return bs;
2627 }
2628 
2629 // Save to a stream.
binary_save(std::ostream & dest) const2630 std::size_t real::binary_save(std::ostream &dest) const
2631 {
2632     const auto bs = binary_size();
2633     // NOTE: there does not seem to be a reliable way of detecting how many bytes
2634     // are actually written via write(). See the question here and especially the comments:
2635     // https://stackoverflow.com/questions/14238572/how-many-bytes-actually-written-by-ostreamwrite
2636     // Seems almost like tellp() would work, but if an error occurs in the stream, then
2637     // it returns unconditionally -1, so it is not very useful for our purposes.
2638     // Thus, we will just return 0 on failure, and the full binary size otherwise.
2639     //
2640     // Write the raw data to stream.
2641     dest.write(reinterpret_cast<const char *>(&m_mpfr._mpfr_prec),
2642                detail::safe_cast<std::streamsize>(sizeof(::mpfr_prec_t)));
2643     if (!dest.good()) {
2644         // !dest.good() means that the last write operation failed. Bail out now.
2645         return 0;
2646     }
2647 
2648     dest.write(reinterpret_cast<const char *>(&m_mpfr._mpfr_sign),
2649                detail::safe_cast<std::streamsize>(sizeof(::mpfr_sign_t)));
2650     if (!dest.good()) {
2651         // LCOV_EXCL_START
2652         return 0;
2653         // LCOV_EXCL_STOP
2654     }
2655 
2656     dest.write(reinterpret_cast<const char *>(&m_mpfr._mpfr_exp),
2657                detail::safe_cast<std::streamsize>(sizeof(::mpfr_exp_t)));
2658     if (!dest.good()) {
2659         // LCOV_EXCL_START
2660         return 0;
2661         // LCOV_EXCL_STOP
2662     }
2663 
2664     dest.write(reinterpret_cast<const char *>(m_mpfr._mpfr_d),
2665                detail::safe_cast<std::streamsize>(bs - detail::rbs_base_size()));
2666     return dest.good() ? bs : 0u;
2667 }
2668 
2669 // Load from a char buffer of unknown size.
binary_load_impl(const char * src)2670 std::size_t real::binary_load_impl(const char *src)
2671 {
2672     // Fetch the precision first.
2673     const auto p = detail::rbs_read_prec(src);
2674     src += sizeof(p);
2675 
2676     // Fetch the sign bit.
2677     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2678     ::mpfr_sign_t sb;
2679     std::copy(src, src + sizeof(sb), reinterpret_cast<char *>(&sb));
2680     src += sizeof(sb);
2681 
2682     // Fetch the exponent.
2683     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2684     ::mpfr_exp_t e;
2685     std::copy(src, src + sizeof(e), reinterpret_cast<char *>(&e));
2686     src += sizeof(e);
2687 
2688     // Compute the size in bytes of the significand from the precision.
2689     const auto sbs = detail::rbs_prec_to_size(p);
2690 
2691     // Compute the return value.
2692     auto retval = detail::rbs_checked_add(detail::rbs_base_size(), sbs);
2693 
2694     // Set the precision of this.
2695     set_prec(p);
2696     // NOTE: from now on, everything is noexcept.
2697     // Set the sign bit.
2698     m_mpfr._mpfr_sign = sb;
2699     // Set the exponent.
2700     m_mpfr._mpfr_exp = e;
2701     // Copy over the limbs.
2702     std::copy(src, src + sbs, reinterpret_cast<char *>(m_mpfr._mpfr_d));
2703 
2704     return retval;
2705 }
2706 
2707 // Load from a char buffer of size 'size' belonging to a container of type 'name'.
binary_load_impl(const char * src,std::size_t size,const char * name)2708 std::size_t real::binary_load_impl(const char *src, std::size_t size, const char *name)
2709 {
2710     if (mppp_unlikely(size < detail::rbs_min_size())) {
2711         throw std::invalid_argument(std::string("Invalid size detected in the deserialisation of a real via a ") + name
2712                                     + ": the " + name + " size must be at least "
2713                                     + std::to_string(detail::rbs_min_size()) + " bytes, but it is only "
2714                                     + std::to_string(size) + " bytes");
2715     }
2716 
2717     // Read the precision.
2718     const auto p = detail::rbs_read_prec(src);
2719 
2720     // Determine the binary size from the loaded precision.
2721     const auto expected_size = detail::rbs_checked_add(detail::rbs_base_size(), detail::rbs_prec_to_size(p));
2722 
2723     // Check that the buffer contains at least the expected amount of data.
2724     if (mppp_unlikely(size < expected_size)) {
2725         throw std::invalid_argument(std::string("Invalid size detected in the deserialisation of a real via a ") + name
2726                                     + ": the " + name + " size must be at least " + std::to_string(expected_size)
2727                                     + " bytes, but it is only " + std::to_string(size) + " bytes");
2728     }
2729 
2730     return binary_load_impl(src);
2731 }
2732 
2733 // Load from a char buffer of unknown size.
binary_load(const char * src)2734 std::size_t real::binary_load(const char *src)
2735 {
2736     return binary_load_impl(src);
2737 }
2738 
2739 // Load from a std::vector.
binary_load(const std::vector<char> & v)2740 std::size_t real::binary_load(const std::vector<char> &v)
2741 {
2742     return binary_load_impl(v.data(), detail::safe_cast<std::size_t>(v.size()), "std::vector");
2743 }
2744 
2745 // Load from an input stream.
binary_load(std::istream & src)2746 std::size_t real::binary_load(std::istream &src)
2747 {
2748     // Let's try to read the precision first.
2749     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2750     ::mpfr_prec_t p;
2751     src.read(reinterpret_cast<char *>(&p), detail::safe_cast<std::streamsize>(sizeof(p)));
2752     if (!src.good()) {
2753         // Something went wrong with reading, return 0.
2754         return 0;
2755     }
2756 
2757     // The sign bit.
2758     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2759     ::mpfr_sign_t sb;
2760     src.read(reinterpret_cast<char *>(&sb), detail::safe_cast<std::streamsize>(sizeof(sb)));
2761     if (!src.good()) {
2762         return 0;
2763     }
2764 
2765     // The exponent.
2766     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2767     ::mpfr_exp_t e;
2768     src.read(reinterpret_cast<char *>(&e), detail::safe_cast<std::streamsize>(sizeof(e)));
2769     if (!src.good()) {
2770         return 0;
2771     }
2772 
2773     // Compute the size in bytes of the significand from the precision.
2774     const auto sbs = detail::rbs_prec_to_size(p);
2775 
2776     // Read the limb data into a local buffer.
2777     MPPP_MAYBE_TLS std::vector<char> buffer;
2778     buffer.resize(detail::safe_cast<decltype(buffer.size())>(sbs));
2779     src.read(buffer.data(), detail::safe_cast<std::streamsize>(sbs));
2780     if (!src.good()) {
2781         // Something went wrong with reading, return 0.
2782         return 0;
2783     }
2784 
2785     // Compute the return value.
2786     auto retval = detail::rbs_checked_add(detail::rbs_base_size(), sbs);
2787 
2788     // Set the precision of this.
2789     set_prec(p);
2790     // NOTE: from now on, everything is noexcept.
2791     // Set the sign bit.
2792     m_mpfr._mpfr_sign = sb;
2793     // Set the exponent.
2794     m_mpfr._mpfr_exp = e;
2795     // Copy over the limbs.
2796     std::copy(buffer.begin(), buffer.end(), reinterpret_cast<char *>(m_mpfr._mpfr_d));
2797 
2798     return retval;
2799 }
2800 
2801 #if defined(MPPP_WITH_BOOST_S11N)
2802 
2803 // Fast serialization implementations for Boost's binary archives.
save(boost::archive::binary_oarchive & ar,unsigned) const2804 void real::save(boost::archive::binary_oarchive &ar, unsigned) const
2805 {
2806     MPPP_MAYBE_TLS std::vector<char> buffer;
2807     binary_save(buffer);
2808 
2809     // Record the size and the raw data.
2810     ar << buffer.size();
2811     ar << boost::serialization::make_binary_object(buffer.data(), detail::safe_cast<std::size_t>(buffer.size()));
2812 }
2813 
load(boost::archive::binary_iarchive & ar,unsigned)2814 void real::load(boost::archive::binary_iarchive &ar, unsigned)
2815 {
2816     MPPP_MAYBE_TLS std::vector<char> buffer;
2817 
2818     // Recover the size.
2819     // NOLINTNEXTLINE(cppcoreguidelines-init-variables)
2820     decltype(buffer.size()) s;
2821     ar >> s;
2822     buffer.resize(s);
2823 
2824     ar >> boost::serialization::make_binary_object(buffer.data(), detail::safe_cast<std::size_t>(buffer.size()));
2825 
2826     binary_load(buffer);
2827 }
2828 
2829 #endif
2830 
2831 } // namespace mppp
2832 
2833 #if defined(_MSC_VER)
2834 
2835 #undef _SCL_SECURE_NO_WARNINGS
2836 
2837 #endif
2838