1 /* Copyright 2009-2016 Francesco Biscani (bluescarni@gmail.com)
2
3 This file is part of the Piranha library.
4
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7
8 * the GNU Lesser General Public License as published by the Free
9 Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11
12 or
13
14 * the GNU General Public License as published by the Free Software
15 Foundation; either version 3 of the License, or (at your option) any
16 later version.
17
18 or both in parallel, as here.
19
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library. If not,
27 see https://www.gnu.org/licenses/. */
28
29 #ifndef PIRANHA_REAL_HPP
30 #define PIRANHA_REAL_HPP
31
32 #include <algorithm>
33 #include <array>
34 #include <boost/lexical_cast.hpp>
35 #include <cmath>
36 #include <cstddef>
37 #include <cstdint>
38 #include <iostream>
39 #include <limits>
40 #include <memory>
41 #include <sstream>
42 #include <stdexcept>
43 #include <string>
44 #include <type_traits>
45
46 #include "binomial.hpp"
47 #include "config.hpp"
48 #include "detail/demangle.hpp"
49 #include "detail/is_digit.hpp"
50 #include "detail/mpfr.hpp"
51 #include "detail/real_fwd.hpp"
52 #include "exceptions.hpp"
53 #include "is_cf.hpp"
54 #include "math.hpp"
55 #include "mp_integer.hpp"
56 #include "mp_rational.hpp"
57 #include "pow.hpp"
58 #include "s11n.hpp"
59 #include "safe_cast.hpp"
60 #include "type_traits.hpp"
61
62 #undef mpfr_set
63
64 namespace piranha
65 {
66
67 namespace detail
68 {
69
70 template <typename = int>
71 struct real_base {
72 // Default rounding mode.
73 // All operations will use the MPFR_RNDN (round to nearest) rounding mode.
74 static const ::mpfr_rnd_t default_rnd = MPFR_RNDN;
75 // Default significand precision.
76 // The precision is the number of bits used to represent the significand of a floating-point number.
77 // This default value is equivalent to the IEEE 754 quadruple-precision binary floating-point format.
78 static const ::mpfr_prec_t default_prec = 113;
79 };
80
81 template <typename T>
82 const ::mpfr_rnd_t real_base<T>::default_rnd;
83
84 template <typename T>
85 const ::mpfr_prec_t real_base<T>::default_prec;
86
87 // Types interoperable with real.
88 template <typename T>
89 struct is_real_interoperable_type {
90 static const bool value = detail::is_mp_integer_interoperable_type<T>::value || detail::is_mp_integer<T>::value
91 || detail::is_mp_rational<T>::value;
92 };
93 }
94
95 /// Arbitrary precision floating-point class.
96 /**
97 * This class represents floating-point ("real") numbers of arbitrary size (i.e., the size is limited only by the
98 * available memory).
99 * The implementation consists of a C++ wrapper around the \p mpfr_t type from the multiprecision MPFR library. Real
100 * numbers
101 * are represented in binary format and they consist of an arbitrary-size significand coupled to a fixed-size exponent.
102 *
103 * Unless noted otherwise, this implementation always uses the \p MPFR_RNDN (round to nearest) rounding mode for all
104 * operations.
105 *
106 * ## Interoperability with other types ##
107 *
108 * This class interoperates with the same types as piranha::mp_integer and piranha::mp_rational,
109 * plus piranha::mp_integer and piranha::mp_rational themselves.
110 * The same caveats with respect to interoperability with floating-point types mentioned in the documentation
111 * of piranha::mp_integer apply.
112 *
113 * ## Exception safety guarantee ##
114 *
115 * Unless noted otherwise, this class provides the strong exception safety guarantee for all operations.
116 * In case of memory allocation errors by GMP/MPFR routines, the program will terminate.
117 *
118 * ## Move semantics ##
119 *
120 * Move construction and move assignment will leave the moved-from object in a state that is destructible and
121 * assignable.
122 *
123 * @see http://www.mpfr.org
124 */
125 // NOTES:
126 // - if we overhaul the tests, put random precision values as well.
127 // - maybe we should have a setter as well for the global default precision. It would need to be an atomic
128 // variable, and we need perf measures to understand the performance impact of this.
129 // - For series evaluation, we need to be careful performance-wise with the possible conversions that might go
130 // on when mixing real with other types. E.g., pow(real,int) when evaluating polynomials. We need to make sure
131 // the conversions are as fast as possible.
132 // - At the moment, this class is technically not sortable because moved-from reals cannot be compared. For use in
133 // std::sort, we should add special casing for moved-from objects. See:
134 // http://stackoverflow.com/questions/26579132/what-is-the-post-condition-of-a-move-constructor
135 class real : public detail::real_base<>
136 {
137 // Shortcut for interop type detector.
138 template <typename T>
139 using is_interoperable_type = detail::is_real_interoperable_type<T>;
140 // Enabler for generic ctor.
141 template <typename T>
142 using generic_ctor_enabler = typename std::enable_if<is_interoperable_type<T>::value, int>::type;
143 // Enabler for conversion operator.
144 template <typename T>
145 using cast_enabler = generic_ctor_enabler<T>;
146 // Enabler for in-place arithmetic operations with interop on the left.
147 template <typename T>
148 using generic_in_place_enabler =
149 typename std::enable_if<is_interoperable_type<T>::value && !std::is_const<T>::value, int>::type;
150 // Precision check.
prec_check(const::mpfr_prec_t & prec)151 static void prec_check(const ::mpfr_prec_t &prec)
152 {
153 if (prec < MPFR_PREC_MIN || prec > MPFR_PREC_MAX) {
154 piranha_throw(std::invalid_argument, "invalid significand precision requested");
155 }
156 }
157 // Construction.
construct_from_string(const char * str,const::mpfr_prec_t & prec)158 void construct_from_string(const char *str, const ::mpfr_prec_t &prec)
159 {
160 prec_check(prec);
161 ::mpfr_init2(m_value, prec);
162 const int retval = ::mpfr_set_str(m_value, str, 10, default_rnd);
163 if (retval != 0) {
164 ::mpfr_clear(m_value);
165 piranha_throw(std::invalid_argument, "invalid string input for real");
166 }
167 }
168 // The idea here is that we use the largest integral and fp types supported by the MPFR api for construction,
169 // and down-cast as needed.
170 template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
construct_from_generic(const T & x)171 void construct_from_generic(const T &x)
172 {
173 ::mpfr_set_ld(m_value, static_cast<long double>(x), default_rnd);
174 }
175 template <typename T,
176 typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, int>::type = 0>
construct_from_generic(const T & si)177 void construct_from_generic(const T &si)
178 {
179 ::mpfr_set_sj(m_value, static_cast<std::intmax_t>(si), default_rnd);
180 }
181 template <typename T,
182 typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, int>::type = 0>
construct_from_generic(const T & ui)183 void construct_from_generic(const T &ui)
184 {
185 ::mpfr_set_uj(m_value, static_cast<std::uintmax_t>(ui), default_rnd);
186 }
187 template <typename T, typename std::enable_if<detail::is_mp_integer<T>::value, int>::type = 0>
construct_from_generic(const T & n)188 void construct_from_generic(const T &n)
189 {
190 auto v = n.get_mpz_view();
191 ::mpfr_set_z(m_value, v, default_rnd);
192 }
193 template <typename T, typename std::enable_if<detail::is_mp_rational<T>::value, int>::type = 0>
construct_from_generic(const T & q)194 void construct_from_generic(const T &q)
195 {
196 auto v = q.get_mpq_view();
197 ::mpfr_set_q(m_value, v, default_rnd);
198 }
199 // Assignment.
assign_from_string(const char * str)200 void assign_from_string(const char *str)
201 {
202 piranha_assert(m_value->_mpfr_d);
203 const int retval = ::mpfr_set_str(m_value, str, 10, default_rnd);
204 if (retval != 0) {
205 // Reset the internal value, as it might have been changed by ::mpfr_set_str().
206 ::mpfr_set_zero(m_value, 0);
207 piranha_throw(std::invalid_argument, "invalid string input for real");
208 }
209 }
210 // Conversion.
211 template <typename T>
convert_to_impl() const212 typename std::enable_if<std::is_same<T, bool>::value, T>::type convert_to_impl() const
213 {
214 return (sign() != 0);
215 }
216 template <typename T>
convert_to_impl() const217 typename std::enable_if<detail::is_mp_integer<T>::value, T>::type convert_to_impl() const
218 {
219 if (is_nan() || is_inf()) {
220 piranha_throw(std::overflow_error, "cannot convert non-finite real to an integral value");
221 }
222 T retval;
223 retval.promote();
224 // Explicitly request rounding to zero in this case.
225 ::mpfr_get_z(&retval.m_int.g_dy(), m_value, MPFR_RNDZ);
226 // NOTE: demote candidate.
227 return retval;
228 }
229 template <typename T>
230 typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value, T>::type
convert_to_impl() const231 convert_to_impl() const
232 {
233 // NOTE: of course, this can be optimised by avoiding going through the integer conversion and
234 // using directly the MPFR functions.
235 return static_cast<T>(static_cast<integer>(*this));
236 }
237 template <typename T>
convert_to_impl() const238 typename std::enable_if<std::is_floating_point<T>::value, T>::type convert_to_impl() const
239 {
240 if (is_nan()) {
241 if (std::numeric_limits<T>::has_quiet_NaN) {
242 return std::numeric_limits<T>::quiet_NaN();
243 } else {
244 piranha_throw(std::overflow_error, "cannot convert NaN to floating-point type");
245 }
246 }
247 if (is_inf()) {
248 piranha_assert(sign() != 0);
249 if (std::numeric_limits<T>::has_infinity && sign() > 0) {
250 return std::numeric_limits<T>::infinity();
251 } else if (std::numeric_limits<T>::has_infinity && sign() < 0) {
252 return std::copysign(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::lowest());
253 } else {
254 piranha_throw(std::overflow_error, "cannot convert infinity to floating-point type");
255 }
256 }
257 if (std::is_same<T, long double>::value) {
258 return static_cast<T>(::mpfr_get_ld(m_value, default_rnd));
259 }
260 if (std::is_same<T, double>::value) {
261 return static_cast<T>(::mpfr_get_d(m_value, default_rnd));
262 }
263 return static_cast<T>(::mpfr_get_flt(m_value, default_rnd));
264 }
265 // Smart pointer to handle the string output from mpfr.
266 typedef std::unique_ptr<char, void (*)(char *)> smart_mpfr_str;
267 template <typename T>
convert_to_impl() const268 typename std::enable_if<detail::is_mp_rational<T>::value, T>::type convert_to_impl() const
269 {
270 if (is_nan()) {
271 piranha_throw(std::overflow_error, "cannot convert NaN to rational");
272 }
273 if (is_inf()) {
274 piranha_throw(std::overflow_error, "cannot convert infinity to rational");
275 }
276 if (sign() == 0) {
277 return T{};
278 }
279 // Get string representation.
280 ::mpfr_exp_t exp(0);
281 char *cptr = ::mpfr_get_str(nullptr, &exp, 10, 0, m_value, default_rnd);
282 if (unlikely(!cptr)) {
283 piranha_throw(std::overflow_error,
284 "error in conversion of real to rational: the call to the MPFR function failed");
285 }
286 smart_mpfr_str str_ptr(cptr, ::mpfr_free_str);
287 // Transform into fraction.
288 std::size_t digits = 0u;
289 for (; *cptr != '\0'; ++cptr) {
290 if (detail::is_digit(*cptr)) {
291 ++digits;
292 }
293 }
294 if (!digits) {
295 piranha_throw(std::overflow_error, "error in conversion of real to rational: invalid number of digits");
296 }
297 // NOTE: here the only exception that can be thrown is when raising to a power
298 // that cannot be represented by unsigned long.
299 try {
300 T retval(str_ptr.get());
301 // NOTE: possible optimizations here include going through direct GMP routines.
302 retval *= T(1, 10).pow(digits);
303 retval *= T(10).pow(exp);
304 return retval;
305 } catch (...) {
306 piranha_throw(std::overflow_error, "error in conversion of real to rational: exponent is too large");
307 }
308 }
309 // In-place addition.
310 // NOTE: all sorts of optimisations, here and in binary add, are possible (e.g., steal from rvalue ref,
311 // avoid setting precision twice in binary operators, etc.). For the moment we keep it basic.
in_place_add(const real & r)312 real &in_place_add(const real &r)
313 {
314 if (r.get_prec() > get_prec()) {
315 // Re-init this with the prec of r.
316 *this = real{*this, r.get_prec()};
317 }
318 ::mpfr_add(m_value, m_value, r.m_value, default_rnd);
319 return *this;
320 }
321 template <typename T, typename std::enable_if<detail::is_mp_rational<T>::value, int>::type = 0>
in_place_add(const T & q)322 real &in_place_add(const T &q)
323 {
324 auto v = q.get_mpq_view();
325 ::mpfr_add_q(m_value, m_value, v, default_rnd);
326 return *this;
327 }
328 template <typename T, typename std::enable_if<detail::is_mp_integer<T>::value, int>::type = 0>
in_place_add(const T & n)329 real &in_place_add(const T &n)
330 {
331 auto v = n.get_mpz_view();
332 ::mpfr_add_z(m_value, m_value, v, default_rnd);
333 return *this;
334 }
335 // NOTE: possible optimisations here.
336 template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
in_place_add(const T & n)337 real &in_place_add(const T &n)
338 {
339 return in_place_add(integer(n));
340 }
341 // NOTE: possible optimisations here as well.
342 template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
in_place_add(const T & x)343 real &in_place_add(const T &x)
344 {
345 // Construct real with the same precision as this, then add.
346 return in_place_add(real{x, get_prec()});
347 }
348 // Binary add.
binary_add(const real & a,const real & b)349 static real binary_add(const real &a, const real &b)
350 {
351 real retval{a};
352 retval += b;
353 return retval;
354 }
355 // Single implementation for all interoperable types.
356 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_add(const real & a,const T & b)357 static real binary_add(const real &a, const T &b)
358 {
359 real retval{a};
360 retval += b;
361 return retval;
362 }
363 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_add(const T & a,const real & b)364 static real binary_add(const T &a, const real &b)
365 {
366 return binary_add(b, a);
367 }
368 // In-place subtraction.
in_place_sub(const real & r)369 real &in_place_sub(const real &r)
370 {
371 if (r.get_prec() > get_prec()) {
372 *this = real{*this, r.get_prec()};
373 }
374 ::mpfr_sub(m_value, m_value, r.m_value, default_rnd);
375 return *this;
376 }
377 template <typename T, typename std::enable_if<detail::is_mp_rational<T>::value, int>::type = 0>
in_place_sub(const T & q)378 real &in_place_sub(const T &q)
379 {
380 auto v = q.get_mpq_view();
381 ::mpfr_sub_q(m_value, m_value, v, default_rnd);
382 return *this;
383 }
384 template <typename T, typename std::enable_if<detail::is_mp_integer<T>::value, int>::type = 0>
in_place_sub(const T & n)385 real &in_place_sub(const T &n)
386 {
387 auto v = n.get_mpz_view();
388 ::mpfr_sub_z(m_value, m_value, v, default_rnd);
389 return *this;
390 }
391 template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
in_place_sub(const T & n)392 real &in_place_sub(const T &n)
393 {
394 return in_place_sub(integer(n));
395 }
396 template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
in_place_sub(const T & x)397 real &in_place_sub(const T &x)
398 {
399 return in_place_sub(real{x, get_prec()});
400 }
401 // Binary sub.
binary_sub(const real & a,const real & b)402 static real binary_sub(const real &a, const real &b)
403 {
404 real retval{a};
405 retval -= b;
406 return retval;
407 }
408 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_sub(const real & a,const T & b)409 static real binary_sub(const real &a, const T &b)
410 {
411 real retval{a};
412 retval -= b;
413 return retval;
414 }
415 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_sub(const T & a,const real & b)416 static real binary_sub(const T &a, const real &b)
417 {
418 auto retval = binary_sub(b, a);
419 retval.negate();
420 return retval;
421 }
422 // In-place multiplication.
in_place_mul(const real & r)423 real &in_place_mul(const real &r)
424 {
425 if (r.get_prec() > get_prec()) {
426 *this = real{*this, r.get_prec()};
427 }
428 ::mpfr_mul(m_value, m_value, r.m_value, default_rnd);
429 return *this;
430 }
431 template <typename T, typename std::enable_if<detail::is_mp_rational<T>::value, int>::type = 0>
in_place_mul(const T & q)432 real &in_place_mul(const T &q)
433 {
434 auto v = q.get_mpq_view();
435 ::mpfr_mul_q(m_value, m_value, v, default_rnd);
436 return *this;
437 }
438 template <typename T, typename std::enable_if<detail::is_mp_integer<T>::value, int>::type = 0>
in_place_mul(const T & n)439 real &in_place_mul(const T &n)
440 {
441 auto v = n.get_mpz_view();
442 ::mpfr_mul_z(m_value, m_value, v, default_rnd);
443 return *this;
444 }
445 template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
in_place_mul(const T & n)446 real &in_place_mul(const T &n)
447 {
448 return in_place_mul(integer(n));
449 }
450 template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
in_place_mul(const T & x)451 real &in_place_mul(const T &x)
452 {
453 return in_place_mul(real{x, get_prec()});
454 }
455 // Binary mul.
binary_mul(const real & a,const real & b)456 static real binary_mul(const real &a, const real &b)
457 {
458 real retval{a};
459 retval *= b;
460 return retval;
461 }
462 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_mul(const real & a,const T & b)463 static real binary_mul(const real &a, const T &b)
464 {
465 real retval{a};
466 retval *= b;
467 return retval;
468 }
469 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_mul(const T & a,const real & b)470 static real binary_mul(const T &a, const real &b)
471 {
472 return binary_mul(b, a);
473 }
474 // In-place division.
in_place_div(const real & r)475 real &in_place_div(const real &r)
476 {
477 if (r.get_prec() > get_prec()) {
478 *this = real{*this, r.get_prec()};
479 }
480 ::mpfr_div(m_value, m_value, r.m_value, default_rnd);
481 return *this;
482 }
483 template <typename T, typename std::enable_if<detail::is_mp_rational<T>::value, int>::type = 0>
in_place_div(const T & q)484 real &in_place_div(const T &q)
485 {
486 auto v = q.get_mpq_view();
487 ::mpfr_div_q(m_value, m_value, v, default_rnd);
488 return *this;
489 }
490 template <typename T, typename std::enable_if<detail::is_mp_integer<T>::value, int>::type = 0>
in_place_div(const T & n)491 real &in_place_div(const T &n)
492 {
493 auto v = n.get_mpz_view();
494 ::mpfr_div_z(m_value, m_value, v, default_rnd);
495 return *this;
496 }
497 template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
in_place_div(const T & n)498 real &in_place_div(const T &n)
499 {
500 return in_place_div(integer(n));
501 }
502 template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
in_place_div(const T & x)503 real &in_place_div(const T &x)
504 {
505 return in_place_div(real{x, get_prec()});
506 }
507 // Binary div.
binary_div(const real & a,const real & b)508 static real binary_div(const real &a, const real &b)
509 {
510 real retval{a};
511 retval /= b;
512 return retval;
513 }
514 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_div(const real & a,const T & b)515 static real binary_div(const real &a, const T &b)
516 {
517 real retval{a};
518 retval /= b;
519 return retval;
520 }
521 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_div(const T & a,const real & b)522 static real binary_div(const T &a, const real &b)
523 {
524 // Create with same precision as b.
525 real retval{a, b.get_prec()};
526 retval /= b;
527 return retval;
528 }
529 // Equality.
binary_equality(const real & r1,const real & r2)530 static bool binary_equality(const real &r1, const real &r2)
531 {
532 return (::mpfr_equal_p(r1.m_value, r2.m_value) != 0);
533 }
534 template <typename T, typename std::enable_if<detail::is_mp_integer<T>::value, int>::type = 0>
binary_equality(const real & r,const T & n)535 static bool binary_equality(const real &r, const T &n)
536 {
537 if (r.is_nan()) {
538 return false;
539 }
540 auto v = n.get_mpz_view();
541 return (::mpfr_cmp_z(r.m_value, v) == 0);
542 }
543 template <typename T, typename std::enable_if<detail::is_mp_rational<T>::value, int>::type = 0>
binary_equality(const real & r,const T & q)544 static bool binary_equality(const real &r, const T &q)
545 {
546 if (r.is_nan()) {
547 return false;
548 }
549 auto v = q.get_mpq_view();
550 return (::mpfr_cmp_q(r.m_value, v) == 0);
551 }
552 template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
binary_equality(const real & r,const T & n)553 static bool binary_equality(const real &r, const T &n)
554 {
555 if (r.is_nan()) {
556 return false;
557 }
558 return r == integer(n);
559 }
560 template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
binary_equality(const real & r,const T & x)561 static bool binary_equality(const real &r, const T &x)
562 {
563 if (r.is_nan() || std::isnan(x)) {
564 return false;
565 }
566 return r == real{x, r.get_prec()};
567 }
568 // NOTE: this is the reverse of above.
569 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_equality(const T & x,const real & r)570 static bool binary_equality(const T &x, const real &r)
571 {
572 return binary_equality(r, x);
573 }
574 // Binary less-than.
binary_less_than(const real & r1,const real & r2)575 static bool binary_less_than(const real &r1, const real &r2)
576 {
577 return (::mpfr_less_p(r1.m_value, r2.m_value) != 0);
578 }
579 template <typename T, typename std::enable_if<detail::is_mp_rational<T>::value, int>::type = 0>
binary_less_than(const real & r,const T & q)580 static bool binary_less_than(const real &r, const T &q)
581 {
582 auto v = q.get_mpq_view();
583 return (::mpfr_cmp_q(r.m_value, v) < 0);
584 }
585 template <typename T, typename std::enable_if<detail::is_mp_integer<T>::value, int>::type = 0>
binary_less_than(const real & r,const T & n)586 static bool binary_less_than(const real &r, const T &n)
587 {
588 auto v = n.get_mpz_view();
589 return (::mpfr_cmp_z(r.m_value, v) < 0);
590 }
591 template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
binary_less_than(const real & r,const T & n)592 static bool binary_less_than(const real &r, const T &n)
593 {
594 return r < integer(n);
595 }
596 template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
binary_less_than(const real & r,const T & x)597 static bool binary_less_than(const real &r, const T &x)
598 {
599 return r < real{x, r.get_prec()};
600 }
601 // Binary less-than or equal.
binary_leq(const real & r1,const real & r2)602 static bool binary_leq(const real &r1, const real &r2)
603 {
604 return (::mpfr_lessequal_p(r1.m_value, r2.m_value) != 0);
605 }
606 template <typename T, typename std::enable_if<detail::is_mp_rational<T>::value, int>::type = 0>
binary_leq(const real & r,const T & q)607 static bool binary_leq(const real &r, const T &q)
608 {
609 auto v = q.get_mpq_view();
610 return (::mpfr_cmp_q(r.m_value, v) <= 0);
611 }
612 template <typename T, typename std::enable_if<detail::is_mp_integer<T>::value, int>::type = 0>
binary_leq(const real & r,const T & n)613 static bool binary_leq(const real &r, const T &n)
614 {
615 auto v = n.get_mpz_view();
616 return (::mpfr_cmp_z(r.m_value, v) <= 0);
617 }
618 template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
binary_leq(const real & r,const T & n)619 static bool binary_leq(const real &r, const T &n)
620 {
621 return r <= integer(n);
622 }
623 template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
binary_leq(const real & r,const T & x)624 static bool binary_leq(const real &r, const T &x)
625 {
626 return r <= real{x, r.get_prec()};
627 }
628 // Inverse forms of less-than and leq.
629 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_less_than(const T & x,const real & r)630 static bool binary_less_than(const T &x, const real &r)
631 {
632 return !binary_leq(r, x);
633 }
634 template <typename T, typename std::enable_if<is_interoperable_type<T>::value, int>::type = 0>
binary_leq(const T & x,const real & r)635 static bool binary_leq(const T &x, const real &r)
636 {
637 return !binary_less_than(r, x);
638 }
639 // NOTE: we need to handle separately the NaNs as we cannot resort to the inversion of the comparison operators for
640 // them.
check_nan(const real & r)641 static bool check_nan(const real &r)
642 {
643 return r.is_nan();
644 }
645 template <typename T>
check_nan(const T & x,typename std::enable_if<std::is_floating_point<T>::value>::type * =nullptr)646 static bool check_nan(const T &x, typename std::enable_if<std::is_floating_point<T>::value>::type * = nullptr)
647 {
648 return std::isnan(x);
649 }
650 template <typename T>
check_nan(const T &,typename std::enable_if<!std::is_floating_point<T>::value>::type * =nullptr)651 static bool check_nan(const T &, typename std::enable_if<!std::is_floating_point<T>::value>::type * = nullptr)
652 {
653 return false;
654 }
655 template <typename T, typename U>
is_nan_comparison(const T & a,const U & b)656 static bool is_nan_comparison(const T &a, const U &b)
657 {
658 return (check_nan(a) || check_nan(b));
659 }
660 // Serialization support.
661 friend class boost::serialization::access;
662 // Utility function to infer the size (in number of limbs) from the precision.
size_from_prec(::mpfr_prec_t prec)663 static ::mpfr_prec_t size_from_prec(::mpfr_prec_t prec)
664 {
665 const ::mpfr_prec_t q = prec / ::mp_bits_per_limb, r = prec % ::mp_bits_per_limb;
666 return q + (r != 0);
667 }
668 // Portable s11n.
669 template <class Archive, enable_if_t<!std::is_same<Archive, boost::archive::binary_oarchive>::value, int> = 0>
save(Archive & ar,unsigned) const670 void save(Archive &ar, unsigned) const
671 {
672 // NOTE: like in mp_integer, the performance here can be improved significantly.
673 std::ostringstream oss;
674 oss << *this;
675 auto prec = get_prec();
676 auto s = oss.str();
677 piranha::boost_save(ar, prec);
678 piranha::boost_save(ar, s);
679 }
680 template <class Archive, enable_if_t<!std::is_same<Archive, boost::archive::binary_iarchive>::value, int> = 0>
load(Archive & ar,unsigned)681 void load(Archive &ar, unsigned)
682 {
683 ::mpfr_prec_t prec;
684 PIRANHA_MAYBE_TLS std::string s;
685 piranha::boost_load(ar, prec);
686 piranha::boost_load(ar, s);
687 *this = real(s, prec);
688 }
689 // Binary s11n.
690 template <class Archive, enable_if_t<std::is_same<Archive, boost::archive::binary_oarchive>::value, int> = 0>
save(Archive & ar,unsigned) const691 void save(Archive &ar, unsigned) const
692 {
693 piranha::boost_save(ar, m_value->_mpfr_prec);
694 piranha::boost_save(ar, m_value->_mpfr_sign);
695 piranha::boost_save(ar, m_value->_mpfr_exp);
696 const ::mpfr_prec_t s = size_from_prec(m_value->_mpfr_prec);
697 // NOTE: no need to save the size, as it can be recovered from the prec.
698 for (::mpfr_prec_t i = 0; i < s; ++i) {
699 piranha::boost_save(ar, m_value->_mpfr_d[i]);
700 }
701 }
702 template <class Archive, enable_if_t<std::is_same<Archive, boost::archive::binary_iarchive>::value, int> = 0>
load(Archive & ar,unsigned)703 void load(Archive &ar, unsigned)
704 {
705 // First we recover the non-limb members.
706 ::mpfr_prec_t prec;
707 decltype(m_value->_mpfr_sign) sign;
708 decltype(m_value->_mpfr_exp) exp;
709 piranha::boost_load(ar, prec);
710 piranha::boost_load(ar, sign);
711 piranha::boost_load(ar, exp);
712 // Recover the size in limbs from prec.
713 const ::mpfr_prec_t s = size_from_prec(prec);
714 // Set the precision.
715 set_prec(prec);
716 piranha_assert(m_value->_mpfr_prec == prec);
717 m_value->_mpfr_sign = sign;
718 m_value->_mpfr_exp = exp;
719 try {
720 // NOTE: protect in try/catch as in theory boost_load() could throw even
721 // in case of valid archive (e.g., memory errors maybe?) and we want
722 // to deal with this case.
723 for (::mpfr_prec_t i = 0; i < s; ++i) {
724 piranha::boost_load(ar, *(m_value->_mpfr_d + i));
725 }
726 } catch (...) {
727 // Set to zero before re-throwing.
728 ::mpfr_set_ui(m_value, 0u, default_rnd);
729 throw;
730 }
731 }
732 BOOST_SERIALIZATION_SPLIT_MEMBER()
733 public:
734 /// Default constructor.
735 /**
736 * Will initialize the number to zero, using real::default_prec as significand precision.
737 */
real()738 real()
739 {
740 ::mpfr_init2(m_value, default_prec);
741 ::mpfr_set_zero(m_value, 0);
742 }
743 /// Copy constructor.
744 /**
745 * Will deep-copy \p other.
746 *
747 * @param other real to be copied.
748 */
real(const real & other)749 real(const real &other)
750 {
751 // Init with the same precision as other, and then set.
752 ::mpfr_init2(m_value, other.get_prec());
753 ::mpfr_set(m_value, other.m_value, default_rnd);
754 }
755 /// Move constructor.
756 /**
757 * @param other real to be moved.
758 */
real(real && other)759 real(real &&other) noexcept
760 {
761 m_value->_mpfr_prec = other.m_value->_mpfr_prec;
762 m_value->_mpfr_sign = other.m_value->_mpfr_sign;
763 m_value->_mpfr_exp = other.m_value->_mpfr_exp;
764 m_value->_mpfr_d = other.m_value->_mpfr_d;
765 // Erase other.
766 other.m_value->_mpfr_prec = 0;
767 other.m_value->_mpfr_sign = 0;
768 other.m_value->_mpfr_exp = 0;
769 other.m_value->_mpfr_d = nullptr;
770 }
771 /// Constructor from C string.
772 /**
773 * Will use the string \p str and precision \p prec to initialize the number.
774 * The expected string format, assuming representation in base 10, is described in the MPFR documentation.
775 *
776 * @param str string representation of the real number.
777 * @param prec desired significand precision.
778 *
779 * @throws std::invalid_argument if the conversion from string fails or if the requested significand precision
780 * is not within the range allowed by the MPFR library.
781 */
real(const char * str,const::mpfr_prec_t & prec=default_prec)782 explicit real(const char *str, const ::mpfr_prec_t &prec = default_prec)
783 {
784 construct_from_string(str, prec);
785 }
786 /// Constructor from C++ string.
787 /**
788 * Equivalent to the constructor from C string.
789 *
790 * @param str string representation of the real number.
791 * @param prec desired significand precision.
792 *
793 * @throws unspecified any exception thrown by the constructor from C string.
794 */
real(const std::string & str,const::mpfr_prec_t & prec=default_prec)795 explicit real(const std::string &str, const ::mpfr_prec_t &prec = default_prec)
796 {
797 construct_from_string(str.c_str(), prec);
798 }
799 /// Copy constructor with different precision.
800 /**
801 * \p this will be first initialised with precision \p prec, and then \p other will be assigned to \p this.
802 *
803 * @param other real to be copied.
804 * @param prec desired significand precision.
805 *
806 * @throws std::invalid_argument if the requested significand precision
807 * is not within the range allowed by the MPFR library.
808 */
real(const real & other,const::mpfr_prec_t & prec)809 explicit real(const real &other, const ::mpfr_prec_t &prec)
810 {
811 prec_check(prec);
812 ::mpfr_init2(m_value, prec);
813 ::mpfr_set(m_value, other.m_value, default_rnd);
814 }
815 /// Generic constructor.
816 /**
817 * \note
818 * This constructor is enabled only if \p T is an interoperable type.
819 *
820 * @param x object used to construct \p this.
821 * @param prec desired significand precision.
822 *
823 * @throws std::invalid_argument if the requested significand precision
824 * is not within the range allowed by the MPFR library.
825 */
826 template <typename T, typename = generic_ctor_enabler<T>>
real(const T & x,const::mpfr_prec_t & prec=default_prec)827 explicit real(const T &x, const ::mpfr_prec_t &prec = default_prec)
828 {
829 prec_check(prec);
830 ::mpfr_init2(m_value, prec);
831 construct_from_generic(x);
832 }
833 /// Destructor.
834 /**
835 * Will clear the internal MPFR variable.
836 */
837 ~real();
838 /// Copy assignment operator.
839 /**
840 * The assignment operation will deep-copy \p other (i.e., including its precision).
841 *
842 * @param other real to be copied.
843 *
844 * @return reference to \p this.
845 */
operator =(const real & other)846 real &operator=(const real &other)
847 {
848 if (this != &other) {
849 // Handle assignment to moved-from objects.
850 if (m_value->_mpfr_d) {
851 // Copy the precision. This will also reset the internal value.
852 set_prec(other.get_prec());
853 } else {
854 piranha_assert(!m_value->_mpfr_prec && !m_value->_mpfr_sign && !m_value->_mpfr_exp);
855 // Reinit before setting.
856 ::mpfr_init2(m_value, other.get_prec());
857 }
858 ::mpfr_set(m_value, other.m_value, default_rnd);
859 }
860 return *this;
861 }
862 /// Move assignment operator.
863 /**
864 * @param other real to be moved.
865 *
866 * @return reference to \p this.
867 */
operator =(real && other)868 real &operator=(real &&other) noexcept
869 {
870 // NOTE: swap() already has the check for this.
871 swap(other);
872 return *this;
873 }
874 /// Assignment operator from C++ string.
875 /**
876 * The implementation is equivalent to the assignment operator from C string.
877 *
878 * @param str string representation of the real to be assigned.
879 *
880 * @return reference to \p this.
881 *
882 * @throws unspecified any exception thrown by the assignment operator from C string.
883 */
operator =(const std::string & str)884 real &operator=(const std::string &str)
885 {
886 return operator=(str.c_str());
887 }
888 /// Assignment operator from C string.
889 /**
890 * The parsing rules are the same as in the constructor from string. The precision of \p this
891 * will not be changed by the assignment operation, unless \p this was the target of a move operation that
892 * left it in an uninitialised state.
893 * In that case, \p this will be re-initialised with the default precision.
894 *
895 * In case \p str is malformed, before an exception is thrown the value of \p this will be reset to zero.
896 *
897 * @param str string representation of the real to be assigned.
898 *
899 * @return reference to \p this.
900 *
901 * @throws std::invalid_argument if the conversion from string fails.
902 */
operator =(const char * str)903 real &operator=(const char *str)
904 {
905 // Handle moved-from objects.
906 if (m_value->_mpfr_d) {
907 assign_from_string(str);
908 } else {
909 piranha_assert(!m_value->_mpfr_prec && !m_value->_mpfr_sign && !m_value->_mpfr_exp);
910 construct_from_string(str, default_prec);
911 }
912 return *this;
913 }
914 /// Generic assignment operator.
915 /**
916 * \note
917 * This assignment operator is enabled only if \p T is an interoperable type.
918 *
919 * The precision of \p this
920 * will not be changed by the assignment operation, unless \p this was the target of a move operation that
921 * left it in an uninitialised state.
922 * In that case, \p this will be re-initialised with the default precision.
923 *
924 * @param x object that will be assigned to \p this.
925 *
926 * @return reference to \p this.
927 */
928 template <typename T, typename = generic_ctor_enabler<T>>
operator =(const T & x)929 real &operator=(const T &x)
930 {
931 if (!m_value->_mpfr_d) {
932 piranha_assert(!m_value->_mpfr_prec && !m_value->_mpfr_sign && !m_value->_mpfr_exp);
933 // Re-init with default prec if it was moved-from.
934 ::mpfr_init2(m_value, default_prec);
935 }
936 // NOTE: all construct_from_generic() methods here are really assignments.
937 construct_from_generic(x);
938 return *this;
939 }
940 /// Conversion operator.
941 /**
942 * \note
943 * This operator is enabled only if \p T is an interoperable type.
944 *
945 * Extract an instance of type \p T from \p this.
946 *
947 * Conversion to \p bool is always successful, and returns <tt>sign() != 0</tt>.
948 * Conversion to the other integral types is truncated (i.e., rounded to zero), its success depending on whether or
949 * not the target type can represent the truncated value.
950 *
951 * Conversion to floating point types is exact if the target type can represent exactly the current value.
952 * If that is not the case, the output value will be the nearest adjacent. If \p this is not finite,
953 * corresponding non-finite values will be produced if the floating-point type supports them, otherwise
954 * an error will be produced.
955 *
956 * Conversion of finite values to piranha::mp_rational will be exact. Conversion of non-finite values will result in
957 * runtime errors.
958 *
959 * @return result of the conversion to target type T.
960 *
961 * @throws std::overflow_error if the conversion fails in one of the ways described above.
962 */
963 template <typename T, typename = cast_enabler<T>>
operator T() const964 explicit operator T() const
965 {
966 return convert_to_impl<T>();
967 }
968 /// Swap.
969 /**
970 * Swap \p this with \p other.
971 *
972 * @param other swap argument.
973 */
swap(real & other)974 void swap(real &other)
975 {
976 if (this == &other) {
977 return;
978 }
979 ::mpfr_swap(m_value, other.m_value);
980 }
981 /// Sign.
982 /**
983 * @return 1 if <tt>this > 0</tt>, 0 if <tt>this == 0</tt> and -1 if <tt>this < 0</tt>. If \p this is NaN, zero will
984 * be returned.
985 */
sign() const986 int sign() const
987 {
988 if (is_nan()) {
989 return 0;
990 } else {
991 return mpfr_sgn(m_value);
992 }
993 }
994 /// Test for zero.
995 /**
996 * @return \p true it \p this is zero, \p false otherwise.
997 */
is_zero() const998 bool is_zero() const
999 {
1000 return (mpfr_zero_p(m_value) != 0);
1001 }
1002 /// Test for NaN.
1003 /**
1004 * @return \p true if \p this is NaN, \p false otherwise.
1005 */
is_nan() const1006 bool is_nan() const
1007 {
1008 return mpfr_nan_p(m_value) != 0;
1009 }
1010 /// Test for infinity.
1011 /**
1012 * @return \p true if \p this represents infinity, \p false otherwise.
1013 */
is_inf() const1014 bool is_inf() const
1015 {
1016 return mpfr_inf_p(m_value) != 0;
1017 }
1018 /// Get precision.
1019 /**
1020 * @return the number of bits used to represent the significand of \p this.
1021 */
get_prec() const1022 ::mpfr_prec_t get_prec() const
1023 {
1024 return mpfr_get_prec(m_value);
1025 }
1026 /// Set precision.
1027 /**
1028 * Will set the significand precision of \p this to exactly \p prec bits, and reset the value of \p this to NaN.
1029 *
1030 * @param prec desired significand precision.
1031 *
1032 * @throws std::invalid_argument if the requested significand precision
1033 * is not within the range allowed by the MPFR library.
1034 */
set_prec(const::mpfr_prec_t & prec)1035 void set_prec(const ::mpfr_prec_t &prec)
1036 {
1037 prec_check(prec);
1038 ::mpfr_set_prec(m_value, prec);
1039 }
1040 /// Negate in-place.
1041 /**
1042 * Will set \p this to <tt>-this</tt>.
1043 */
negate()1044 void negate()
1045 {
1046 ::mpfr_neg(m_value, m_value, default_rnd);
1047 }
1048 /// Truncate in-place.
1049 /**
1050 * Set \p this to the next representable integer towards zero. If \p this is infinity or NaN, there will be no
1051 * effect.
1052 */
truncate()1053 void truncate()
1054 {
1055 if (is_inf() || is_nan()) {
1056 return;
1057 }
1058 ::mpfr_trunc(m_value, m_value);
1059 }
1060 /// Truncated copy.
1061 /**
1062 * @return a truncated copy of \p this.
1063 */
truncated() const1064 real truncated() const
1065 {
1066 real retval{*this};
1067 retval.truncate();
1068 return retval;
1069 }
1070 /// In-place addition.
1071 /**
1072 * \note
1073 * This operator is enabled only if \p T is an interoperable type or piranha::real.
1074 *
1075 * Add \p x to the current value of the real object.
1076 *
1077 * If the precision \p prec of \p x is greater than the precision of \p this,
1078 * the precision of \p this is changed to \p prec before the operation takes place.
1079 *
1080 * @param x argument for the addition.
1081 *
1082 * @return reference to \p this.
1083 *
1084 * @throws unspecified any exception thrown by the contructor of piranha::mp_integer, if invoked.
1085 */
1086 template <typename T>
operator +=(const T & x)1087 auto operator+=(const T &x) -> decltype(this->in_place_add(x))
1088 {
1089 return in_place_add(x);
1090 }
1091 /// Generic in-place addition with piranha::real.
1092 /**
1093 * \note
1094 * This operator is enabled only if \p T is a non-const interoperable type.
1095 *
1096 * Add a piranha::real in-place.
1097 * This method will first compute <tt>r + x</tt>, cast it back to \p T via \p static_cast and finally assign the
1098 * result to \p x.
1099 *
1100 * @param x first argument.
1101 * @param r second argument.
1102 *
1103 * @return reference to \p x.
1104 *
1105 * @throws unspecified any exception resulting from the binary operator or by casting piranha::real to \p T.
1106 */
1107 template <typename T, generic_in_place_enabler<T> = 0>
operator +=(T & x,const real & r)1108 friend T &operator+=(T &x, const real &r)
1109 {
1110 // NOTE: for the supported types, move assignment can never throw.
1111 return x = static_cast<T>(r + x);
1112 }
1113 /// Generic binary addition involving piranha::real.
1114 /**
1115 * \note
1116 * This template operator is enabled only if either:
1117 * - \p T is piranha::real and \p U is an interoperable type,
1118 * - \p U is piranha::real and \p T is an interoperable type,
1119 * - both \p T and \p U are piranha::real.
1120 *
1121 * The return type is always piranha::real.
1122 *
1123 * @param x first argument
1124 * @param y second argument.
1125 *
1126 * @return <tt>x + y</tt>.
1127 *
1128 * @throws unspecified any exception thrown by the corresponding in-place operator.
1129 */
1130 template <typename T, typename U>
operator +(const T & x,const U & y)1131 friend auto operator+(const T &x, const U &y) -> decltype(real::binary_add(x, y))
1132 {
1133 return binary_add(x, y);
1134 }
1135 /// Identity operator.
1136 /**
1137 * @return copy of \p this.
1138 */
operator +() const1139 real operator+() const
1140 {
1141 return *this;
1142 }
1143 /// Prefix increment.
1144 /**
1145 * Increment \p this by one.
1146 *
1147 * @return reference to \p this after the increment.
1148 */
operator ++()1149 real &operator++()
1150 {
1151 return operator+=(1);
1152 }
1153 /// Suffix increment.
1154 /**
1155 * Increment \p this by one and return a copy of \p this as it was before the increment.
1156 *
1157 * @return copy of \p this before the increment.
1158 */
operator ++(int)1159 real operator++(int)
1160 {
1161 const real retval(*this);
1162 ++(*this);
1163 return retval;
1164 }
1165 /// In-place subtraction.
1166 /**
1167 * \note
1168 * This operator is enabled only if \p T is an interoperable type or piranha::real.
1169 *
1170 * Subtract \p x from the current value of the real object.
1171 *
1172 * If the precision \p prec of \p x is greater than the precision of \p this,
1173 * the precision of \p this is changed to \p prec before the operation takes place.
1174 *
1175 * @param x argument for the subtraction.
1176 *
1177 * @return reference to \p this.
1178 *
1179 * @throws unspecified any exception thrown by the contructor of piranha::mp_integer, if invoked.
1180 */
1181 template <typename T>
operator -=(const T & x)1182 auto operator-=(const T &x) -> decltype(this->in_place_sub(x))
1183 {
1184 return in_place_sub(x);
1185 }
1186 /// Generic in-place subtraction with piranha::real.
1187 /**
1188 * \note
1189 * This operator is enabled only if \p T is a non-const interoperable type.
1190 *
1191 * Subtract a piranha::real in-place.
1192 * This method will first compute <tt>x - r</tt>, cast it back to \p T via \p static_cast and finally assign the
1193 * result to \p x.
1194 *
1195 * @param x first argument.
1196 * @param r second argument.
1197 *
1198 * @return reference to \p x.
1199 *
1200 * @throws unspecified any exception resulting from the binary operator or by casting piranha::real to \p T.
1201 */
1202 template <typename T, generic_in_place_enabler<T> = 0>
operator -=(T & x,const real & r)1203 friend T &operator-=(T &x, const real &r)
1204 {
1205 return x = static_cast<T>(x - r);
1206 }
1207 /// Generic binary subtraction involving piranha::real.
1208 /**
1209 * \note
1210 * This template operator is enabled only if either:
1211 * - \p T is piranha::real and \p U is an interoperable type,
1212 * - \p U is piranha::real and \p T is an interoperable type,
1213 * - both \p T and \p U are piranha::real.
1214 *
1215 * The return type is always piranha::real.
1216 *
1217 * @param x first argument
1218 * @param y second argument.
1219 *
1220 * @return <tt>x - y</tt>.
1221 *
1222 * @throws unspecified any exception thrown by the corresponding in-place operator.
1223 */
1224 template <typename T, typename U>
operator -(const T & x,const U & y)1225 friend auto operator-(const T &x, const U &y) -> decltype(real::binary_sub(x, y))
1226 {
1227 return binary_sub(x, y);
1228 }
1229 /// Negated copy.
1230 /**
1231 * @return copy of \p -this.
1232 */
operator -() const1233 real operator-() const
1234 {
1235 real retval(*this);
1236 retval.negate();
1237 return retval;
1238 }
1239 /// Prefix decrement.
1240 /**
1241 * Decrement \p this by one and return.
1242 *
1243 * @return reference to \p this.
1244 */
operator --()1245 real &operator--()
1246 {
1247 return operator-=(1);
1248 }
1249 /// Suffix decrement.
1250 /**
1251 * Decrement \p this by one and return a copy of \p this as it was before the decrement.
1252 *
1253 * @return copy of \p this before the decrement.
1254 */
operator --(int)1255 real operator--(int)
1256 {
1257 const real retval(*this);
1258 --(*this);
1259 return retval;
1260 }
1261 /// In-place multiplication.
1262 /**
1263 * \note
1264 * This operator is enabled only if \p T is an interoperable type or piranha::real.
1265 *
1266 * Multiply by \p x the current value of the real object.
1267 *
1268 * If the precision \p prec of \p x is greater than the precision of \p this,
1269 * the precision of \p this is changed to \p prec before the operation takes place.
1270 *
1271 * @param x argument for the multiplication.
1272 *
1273 * @return reference to \p this.
1274 *
1275 * @throws unspecified any exception thrown by the contructor of piranha::mp_integer, if invoked.
1276 */
1277 template <typename T>
operator *=(const T & x)1278 auto operator*=(const T &x) -> decltype(this->in_place_mul(x))
1279 {
1280 return in_place_mul(x);
1281 }
1282 /// Generic in-place multiplication by piranha::real.
1283 /**
1284 * \note
1285 * This operator is enabled only if \p T is a non-const interoperable type.
1286 *
1287 * Multiply by a piranha::real in-place.
1288 * This method will first compute <tt>x * r</tt>, cast it back to \p T via \p static_cast and finally assign the
1289 * result to \p x.
1290 *
1291 * @param x first argument.
1292 * @param r second argument.
1293 *
1294 * @return reference to \p x.
1295 *
1296 * @throws unspecified any exception resulting from the binary operator or by casting piranha::real to \p T.
1297 */
1298 template <typename T, generic_in_place_enabler<T> = 0>
operator *=(T & x,const real & r)1299 friend T &operator*=(T &x, const real &r)
1300 {
1301 return x = static_cast<T>(x * r);
1302 }
1303 /// Generic binary multiplication involving piranha::real.
1304 /**
1305 * \note
1306 * This template operator is enabled only if either:
1307 * - \p T is piranha::real and \p U is an interoperable type,
1308 * - \p U is piranha::real and \p T is an interoperable type,
1309 * - both \p T and \p U are piranha::real.
1310 *
1311 * The return type is always piranha::real.
1312 *
1313 * @param x first argument
1314 * @param y second argument.
1315 *
1316 * @return <tt>x * y</tt>.
1317 *
1318 * @throws unspecified any exception thrown by the corresponding in-place operator.
1319 */
1320 template <typename T, typename U>
operator *(const T & x,const U & y)1321 friend auto operator*(const T &x, const U &y) -> decltype(real::binary_mul(x, y))
1322 {
1323 return binary_mul(x, y);
1324 }
1325 /// In-place division.
1326 /**
1327 * \note
1328 * This operator is enabled only if \p T is an interoperable type or piranha::real.
1329 *
1330 * Divide by \p x the current value of the real object.
1331 *
1332 * If the precision \p prec of \p x is greater than the precision of \p this,
1333 * the precision of \p this is changed to \p prec before the operation takes place.
1334 *
1335 * @param x argument for the division.
1336 *
1337 * @return reference to \p this.
1338 *
1339 * @throws unspecified any exception thrown by the contructor of piranha::mp_integer, if invoked.
1340 */
1341 template <typename T>
operator /=(const T & x)1342 auto operator/=(const T &x) -> decltype(this->in_place_div(x))
1343 {
1344 return in_place_div(x);
1345 }
1346 /// Generic in-place division by piranha::real.
1347 /**
1348 * \note
1349 * This operator is enabled only if \p T is a non-const interoperable type.
1350 *
1351 * Divide by a piranha::real in-place.
1352 * This method will first compute <tt>x / r</tt>, cast it back to \p T via \p static_cast and finally assign the
1353 * result to \p x.
1354 *
1355 * @param x first argument.
1356 * @param r second argument.
1357 *
1358 * @return reference to \p x.
1359 *
1360 * @throws unspecified any exception resulting from the binary operator or by casting piranha::real to \p T.
1361 */
1362 template <typename T, generic_in_place_enabler<T> = 0>
operator /=(T & x,const real & r)1363 friend T &operator/=(T &x, const real &r)
1364 {
1365 return x = static_cast<T>(x / r);
1366 }
1367 /// Generic binary division involving piranha::real.
1368 /**
1369 * \note
1370 * This template operator is enabled only if either:
1371 * - \p T is piranha::real and \p U is an interoperable type,
1372 * - \p U is piranha::real and \p T is an interoperable type,
1373 * - both \p T and \p U are piranha::real.
1374 *
1375 * The return type is always piranha::real.
1376 *
1377 * @param x first argument
1378 * @param y second argument.
1379 *
1380 * @return <tt>x / y</tt>.
1381 *
1382 * @throws unspecified any exception thrown by the corresponding in-place operator.
1383 */
1384 template <typename T, typename U>
operator /(const T & x,const U & y)1385 friend auto operator/(const T &x, const U &y) -> decltype(real::binary_div(x, y))
1386 {
1387 return binary_div(x, y);
1388 }
1389 /// Combined multiply-add.
1390 /**
1391 * Sets \p this to <tt>this + (r1 * r2)</tt>. If the precision of \p this is less than the maximum precision \p
1392 * max_prec of the two operands
1393 * \p r1 and \p r2, the precision of \p this will be set to \p max_prec before performing the operation.
1394 *
1395 * @param r1 first argument.
1396 * @param r2 second argument.
1397 *
1398 * @return reference to \p this.
1399 */
multiply_accumulate(const real & r1,const real & r2)1400 real &multiply_accumulate(const real &r1, const real &r2)
1401 {
1402 const ::mpfr_prec_t prec1 = std::max<::mpfr_prec_t>(r1.get_prec(), r2.get_prec());
1403 if (prec1 > get_prec()) {
1404 *this = real{*this, prec1};
1405 }
1406 // So the story here is that mpfr_fma has been reported to be slower than the two separate
1407 // operations. Benchmarks on fateman1 indicate this is indeed the case (3.6 vs 2.7 secs
1408 // on 4 threads). Hopefully it will be fixed in the future, for now adopt the workaround.
1409 // http://www.loria.fr/~zimmerma/mpfr-mpc-2014.html
1410 // NOTE: this optimisation requires the thread_local keyword.
1411 #if defined(PIRANHA_HAVE_THREAD_LOCAL)
1412 static thread_local real tmp;
1413 // NOTE: set the same precision as this, which is now the max precision of the 3 operands.
1414 // If we do not do this, then tmp has an undeterminate precision. Use the raw MPFR function
1415 // in order to avoid the checks in get_prec(), as we know the precision has a sane value.
1416 ::mpfr_set_prec(tmp.m_value, mpfr_get_prec(m_value));
1417 ::mpfr_mul(tmp.m_value, r1.m_value, r2.m_value, MPFR_RNDN);
1418 ::mpfr_add(m_value, m_value, tmp.m_value, MPFR_RNDN);
1419 #else
1420 ::mpfr_fma(m_value, r1.m_value, r2.m_value, m_value, default_rnd);
1421 #endif
1422 return *this;
1423 }
1424 /// Generic equality operator involving piranha::real.
1425 /**
1426 * \note
1427 * This template operator is enabled only if either:
1428 * - \p T is piranha::real and \p U is an interoperable type,
1429 * - \p U is piranha::real and \p T is an interoperable type,
1430 * - both \p T and \p U are piranha::real.
1431 *
1432 * Note that in all comparison operators, apart from piranha::real::operator!=(), if any operand is NaN \p false
1433 * will be returned.
1434 *
1435 * @param x first argument
1436 * @param y second argument.
1437 *
1438 * @return \p true if <tt>x == y</tt>, \p false otherwise.
1439 */
1440 template <typename T, typename U>
operator ==(const T & x,const U & y)1441 friend auto operator==(const T &x, const U &y) -> decltype(real::binary_equality(x, y))
1442 {
1443 return binary_equality(x, y);
1444 }
1445 /// Generic inequality operator involving piranha::real.
1446 /**
1447 * \note
1448 * This template operator is enabled only if either:
1449 * - \p T is piranha::real and \p U is an interoperable type,
1450 * - \p U is piranha::real and \p T is an interoperable type,
1451 * - both \p T and \p U are piranha::real.
1452 *
1453 * Note that in all comparison operators, apart from piranha::real::operator!=(), if any operand is NaN \p false
1454 * will be returned.
1455 *
1456 * @param x first argument
1457 * @param y second argument.
1458 *
1459 * @return \p true if <tt>x != y</tt>, \p false otherwise.
1460 */
1461 template <typename T, typename U>
operator !=(const T & x,const U & y)1462 friend auto operator!=(const T &x, const U &y) -> decltype(!real::binary_equality(x, y))
1463 {
1464 return !binary_equality(x, y);
1465 }
1466 /// Generic less-than operator involving piranha::real.
1467 /**
1468 * \note
1469 * This template operator is enabled only if either:
1470 * - \p T is piranha::real and \p U is an interoperable type,
1471 * - \p U is piranha::real and \p T is an interoperable type,
1472 * - both \p T and \p U are piranha::real.
1473 *
1474 * Note that in all comparison operators, apart from piranha::real::operator!=(), if any operand is NaN \p false
1475 * will be returned.
1476 *
1477 * @param x first argument
1478 * @param y second argument.
1479 *
1480 * @return \p true if <tt>x < y</tt>, \p false otherwise.
1481 */
1482 template <typename T, typename U>
operator <(const T & x,const U & y)1483 friend auto operator<(const T &x, const U &y) -> decltype(real::binary_less_than(x, y))
1484 {
1485 if (is_nan_comparison(x, y)) {
1486 return false;
1487 }
1488 return binary_less_than(x, y);
1489 }
1490 /// Generic less-than or equal operator involving piranha::real.
1491 /**
1492 * \note
1493 * This template operator is enabled only if either:
1494 * - \p T is piranha::real and \p U is an interoperable type,
1495 * - \p U is piranha::real and \p T is an interoperable type,
1496 * - both \p T and \p U are piranha::real.
1497 *
1498 * Note that in all comparison operators, apart from piranha::real::operator!=(), if any operand is NaN \p false
1499 * will be returned.
1500 *
1501 * @param x first argument
1502 * @param y second argument.
1503 *
1504 * @return \p true if <tt>x <= y</tt>, \p false otherwise.
1505 */
1506 template <typename T, typename U>
operator <=(const T & x,const U & y)1507 friend auto operator<=(const T &x, const U &y) -> decltype(real::binary_leq(x, y))
1508 {
1509 if (is_nan_comparison(x, y)) {
1510 return false;
1511 }
1512 return binary_leq(x, y);
1513 }
1514 /// Generic greater-than operator involving piranha::real.
1515 /**
1516 * \note
1517 * This template operator is enabled only if either:
1518 * - \p T is piranha::real and \p U is an interoperable type,
1519 * - \p U is piranha::real and \p T is an interoperable type,
1520 * - both \p T and \p U are piranha::real.
1521 *
1522 * Note that in all comparison operators, apart from piranha::real::operator!=(), if any operand is NaN \p false
1523 * will be returned.
1524 *
1525 * @param x first argument
1526 * @param y second argument.
1527 *
1528 * @return \p true if <tt>x > y</tt>, \p false otherwise.
1529 */
1530 template <typename T, typename U>
operator >(const T & x,const U & y)1531 friend auto operator>(const T &x, const U &y) -> decltype(real::binary_less_than(y, x))
1532 {
1533 if (is_nan_comparison(x, y)) {
1534 return false;
1535 }
1536 return y < x;
1537 }
1538 /// Generic greater-than or equal operator involving piranha::real.
1539 /**
1540 * \note
1541 * This template operator is enabled only if either:
1542 * - \p T is piranha::real and \p U is an interoperable type,
1543 * - \p U is piranha::real and \p T is an interoperable type,
1544 * - both \p T and \p U are piranha::real.
1545 *
1546 * Note that in all comparison operators, apart from piranha::real::operator!=(), if any operand is NaN \p false
1547 * will be returned.
1548 *
1549 * @param x first argument
1550 * @param y second argument.
1551 *
1552 * @return \p true if <tt>x >= y</tt>, \p false otherwise.
1553 */
1554 template <typename T, typename U>
operator >=(const T & x,const U & y)1555 friend auto operator>=(const T &x, const U &y) -> decltype(real::binary_leq(y, x))
1556 {
1557 if (is_nan_comparison(x, y)) {
1558 return false;
1559 }
1560 return y <= x;
1561 }
1562 /// Exponentiation.
1563 /**
1564 * The operation is carried out with the maximum precision between \p this and \p exp.
1565 *
1566 * @param exp exponent.
1567 *
1568 * @return <tt>this ** exp</tt>.
1569 */
pow(const real & exp) const1570 real pow(const real &exp) const
1571 {
1572 real retval{0, get_prec()};
1573 if (exp.get_prec() > get_prec()) {
1574 retval.set_prec(exp.get_prec());
1575 }
1576 ::mpfr_pow(retval.m_value, m_value, exp.m_value, default_rnd);
1577 return retval;
1578 }
1579 /// Gamma function.
1580 /**
1581 * @return gamma of \p this.
1582 */
gamma() const1583 real gamma() const
1584 {
1585 real retval{0, get_prec()};
1586 ::mpfr_gamma(retval.m_value, m_value, default_rnd);
1587 return retval;
1588 }
1589 /// Logarithm of the gamma function.
1590 /**
1591 * @return logarithm of the absolute value of the gamma of \p this.
1592 */
lgamma() const1593 real lgamma() const
1594 {
1595 real retval{0, get_prec()};
1596 // This is the sign of gamma(*this). We don't use this.
1597 int sign;
1598 ::mpfr_lgamma(retval.m_value, &sign, m_value, default_rnd);
1599 return retval;
1600 }
1601 /// Exponential.
1602 /**
1603 * @return the exponential of \p this.
1604 */
exp() const1605 real exp() const
1606 {
1607 real retval{0, get_prec()};
1608 ::mpfr_exp(retval.m_value, m_value, default_rnd);
1609 return retval;
1610 }
1611 real binomial(const real &) const;
1612 /// Absolute value.
1613 /**
1614 * @return absolute value of \p this.
1615 */
abs() const1616 real abs() const
1617 {
1618 real retval(*this);
1619 ::mpfr_abs(retval.m_value, retval.m_value, default_rnd);
1620 return retval;
1621 }
1622 /// Sine.
1623 /**
1624 * @return sine of \p this, computed with the precision of \p this.
1625 */
sin() const1626 real sin() const
1627 {
1628 real retval(0, get_prec());
1629 ::mpfr_sin(retval.m_value, m_value, default_rnd);
1630 return retval;
1631 }
1632 /// Cosine.
1633 /**
1634 * @return cosine of \p this, computed with the precision of \p this.
1635 */
cos() const1636 real cos() const
1637 {
1638 real retval(0, get_prec());
1639 ::mpfr_cos(retval.m_value, m_value, default_rnd);
1640 return retval;
1641 }
1642 /// Pi constant.
1643 /**
1644 * @return pi constant calculated to the current precision of \p this.
1645 */
pi() const1646 real pi() const
1647 {
1648 real retval(0, get_prec());
1649 ::mpfr_const_pi(retval.m_value, default_rnd);
1650 return retval;
1651 }
1652 /// Overload output stream operator for piranha::real.
1653 /**
1654 * The output format for finite numbers is normalised scientific notation, where the exponent is signalled by the
1655 * letter 'e'
1656 * and suppressed if null.
1657 *
1658 * For non-finite numbers, the string representation is one of "nan", "inf" or "-inf".
1659 *
1660 * @param os output stream.
1661 * @param r piranha::real to be directed to stream.
1662 *
1663 * @return reference to \p os.
1664 *
1665 * @throws std::invalid_argument if the conversion to string via the MPFR API fails.
1666 * @throws std::overflow_error if the exponent is smaller than an implementation-defined minimum.
1667 * @throws unspecified any exception thrown by memory allocation errors in standard containers.
1668 */
operator <<(std::ostream & os,const real & r)1669 friend std::ostream &operator<<(std::ostream &os, const real &r)
1670 {
1671 if (r.is_nan()) {
1672 os << "nan";
1673 return os;
1674 }
1675 if (r.is_inf()) {
1676 if (r.sign() > 0) {
1677 os << "inf";
1678 } else {
1679 os << "-inf";
1680 }
1681 return os;
1682 }
1683 ::mpfr_exp_t exp(0);
1684 char *cptr = ::mpfr_get_str(nullptr, &exp, 10, 0, r.m_value, default_rnd);
1685 if (unlikely(!cptr)) {
1686 piranha_throw(std::overflow_error,
1687 "error in conversion of real to rational: the call to the MPFR function failed");
1688 }
1689 smart_mpfr_str str(cptr, ::mpfr_free_str);
1690 // Copy into C++ string.
1691 std::string cpp_str(str.get());
1692 // Insert the radix point.
1693 auto it = std::find_if(cpp_str.begin(), cpp_str.end(), [](char c) { return detail::is_digit(c); });
1694 if (it != cpp_str.end()) {
1695 ++it;
1696 cpp_str.insert(it, '.');
1697 if (exp == std::numeric_limits<::mpfr_exp_t>::min()) {
1698 piranha_throw(std::overflow_error, "overflow in conversion of real to string");
1699 }
1700 --exp;
1701 if (exp != ::mpfr_exp_t(0) && r.sign() != 0) {
1702 cpp_str.append("e" + boost::lexical_cast<std::string>(exp));
1703 }
1704 }
1705 os << cpp_str;
1706 return os;
1707 }
1708 /// Overload input stream operator for piranha::real.
1709 /**
1710 * Equivalent to extracting a line from the stream and then assigning it to \p r.
1711 *
1712 * @param is input stream.
1713 * @param r real to which the contents of the stream will be assigned.
1714 *
1715 * @return reference to \p is.
1716 *
1717 * @throws unspecified any exception thrown by the assignment operator from string of piranha::real.
1718 */
operator >>(std::istream & is,real & r)1719 friend std::istream &operator>>(std::istream &is, real &r)
1720 {
1721 std::string tmp_str;
1722 std::getline(is, tmp_str);
1723 r = tmp_str;
1724 return is;
1725 }
1726 /** @name Low-level interface
1727 * Low-level methods. These methods allow direct access to the internal
1728 * MPFR instance.
1729 */
1730 //@{
1731 /// Get a mutable reference to the internal <tt>mpfr_t</tt> instance.
get_mpfr_t()1732 std::remove_extent<::mpfr_t>::type *get_mpfr_t()
1733 {
1734 return &m_value[0u];
1735 }
1736 /// Get a const reference to the internal <tt>mpfr_t</tt> instance.
get_mpfr_t() const1737 const std::remove_extent<::mpfr_t>::type *get_mpfr_t() const
1738 {
1739 return &m_value[0u];
1740 }
1741 //@}
1742
1743 #if defined(PIRANHA_WITH_MSGPACK)
1744 private:
1745 // msgpack enabler.
1746 template <typename Stream>
1747 using msgpack_pack_enabler
1748 = enable_if_t<conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, ::mpfr_prec_t>,
1749 has_msgpack_pack<Stream, std::string>,
1750 has_msgpack_pack<Stream, decltype(std::declval<const ::mpfr_t &>()->_mpfr_sign)>,
1751 has_msgpack_pack<Stream, decltype(std::declval<const ::mpfr_t &>()->_mpfr_exp)>,
1752 has_msgpack_pack<Stream, ::mp_limb_t>,
1753 has_safe_cast<std::uint32_t, ::mpfr_prec_t>>::value,
1754 int>;
1755 template <typename U>
1756 using msgpack_convert_enabler
1757 = enable_if_t<conjunction<std::is_same<U, U>, // For SFINAE.
1758 has_msgpack_convert<::mpfr_prec_t>, has_msgpack_convert<std::string>,
1759 has_msgpack_convert<decltype(std::declval<const ::mpfr_t &>()->_mpfr_sign)>,
1760 has_msgpack_convert<decltype(std::declval<const ::mpfr_t &>()->_mpfr_exp)>,
1761 has_msgpack_convert<::mp_limb_t>>::value,
1762 int>;
1763
1764 public:
1765 /// Pack in msgpack format.
1766 /**
1767 * \note
1768 * This method is enabled only if:
1769 * - \p Stream satisfies piranha::is_msgpack_stream,
1770 * - \p std::string and the integral types in terms of which the internal representation of
1771 * piranha::real is defined satisfy piranha::has_msgpack_pack,
1772 * - \p mpfr_prec_t can be safely converted to \p std::uint32_t.
1773 *
1774 * This method will pack \p this into \p p. If \p f is msgpack_format::portable, then
1775 * the precision of \p this and a decimal string representation of \p this are packed in an array.
1776 * Otherwise, an array of 4 elements storing the internal MPFR representation of \p this is packed.
1777 *
1778 * @param p target <tt>msgpack::packer</tt>.
1779 * @param f the desired piranha::msgpack_format.
1780 *
1781 * @throws unspecified any exception thrown by:
1782 * - piranha::safe_cast(),
1783 * - the public interface of <tt>msgpack::packer</tt>,
1784 * - piranha::msgpack_pack(),
1785 * - the conversion of \p this to string.
1786 */
1787 template <typename Stream, msgpack_pack_enabler<Stream> = 0>
msgpack_pack(msgpack::packer<Stream> & p,msgpack_format f) const1788 void msgpack_pack(msgpack::packer<Stream> &p, msgpack_format f) const
1789 {
1790 if (f == msgpack_format::portable) {
1791 std::ostringstream oss;
1792 oss << *this;
1793 auto prec = get_prec();
1794 auto s = oss.str();
1795 p.pack_array(2);
1796 piranha::msgpack_pack(p, prec, f);
1797 piranha::msgpack_pack(p, s, f);
1798 } else {
1799 // NOTE: storing both prec and the number of limbs is a bit redundant: it is possible to
1800 // infer the number of limbs from prec but not viceversa (only an upper/lower bound). So let's
1801 // store them both.
1802 p.pack_array(4);
1803 piranha::msgpack_pack(p, m_value->_mpfr_prec, f);
1804 piranha::msgpack_pack(p, m_value->_mpfr_sign, f);
1805 piranha::msgpack_pack(p, m_value->_mpfr_exp, f);
1806 const auto s = safe_cast<std::uint32_t>(size_from_prec(m_value->_mpfr_prec));
1807 p.pack_array(s);
1808 // NOTE: no need to save the size, as it can be recovered from the prec.
1809 for (std::uint32_t i = 0; i < s; ++i) {
1810 piranha::msgpack_pack(p, m_value->_mpfr_d[i], f);
1811 }
1812 }
1813 }
1814 /// Convert from msgpack object.
1815 /**
1816 * \note
1817 * This method is enabled only if \p std::string and all the integral types used to define the internal
1818 * representation of piranha::real satisfy piranha::has_msgpack_convert.
1819 *
1820 * This method will convert the object \p o into \p this. If \p f is piranha::msgpack_format::binary,
1821 * this method offers the basic exception safety guarantee and it performs minimal checking on the input data.
1822 * Calling this method in binary mode will result in undefined behaviour if \p o does not contain an integer
1823 * serialized via msgpack_pack().
1824 *
1825 * @param o source object.
1826 * @param f the desired piranha::msgpack_format.
1827 *
1828 * @throws std::invalid_argument if, in binary mode, the number of serialized limbs is inconsistent with the
1829 * precision.
1830 * @throws unspecified any exception thrown by:
1831 * - piranha::safe_cast(),
1832 * - set_prec(),
1833 * - memory errors in standard containers,
1834 * - the public interface of <tt>msgpack::object</tt>,
1835 * - piranha::msgpack_convert(),
1836 * - the constructor of piranha::real from string.
1837 */
1838 template <typename U = real, msgpack_convert_enabler<U> = 0>
msgpack_convert(const msgpack::object & o,msgpack_format f)1839 void msgpack_convert(const msgpack::object &o, msgpack_format f)
1840 {
1841 if (f == msgpack_format::portable) {
1842 PIRANHA_MAYBE_TLS std::array<msgpack::object, 2> vobj;
1843 o.convert(vobj);
1844 ::mpfr_prec_t prec;
1845 PIRANHA_MAYBE_TLS std::string s;
1846 piranha::msgpack_convert(prec, vobj[0], f);
1847 piranha::msgpack_convert(s, vobj[1], f);
1848 *this = real(s, prec);
1849 } else {
1850 PIRANHA_MAYBE_TLS std::array<msgpack::object, 4> vobj;
1851 o.convert(vobj);
1852 // First let's handle the non-limbs members.
1853 ::mpfr_prec_t prec;
1854 decltype(m_value->_mpfr_sign) sign;
1855 decltype(m_value->_mpfr_exp) exp;
1856 piranha::msgpack_convert(prec, vobj[0], f);
1857 piranha::msgpack_convert(sign, vobj[1], f);
1858 piranha::msgpack_convert(exp, vobj[2], f);
1859 set_prec(prec);
1860 piranha_assert(m_value->_mpfr_prec == prec);
1861 m_value->_mpfr_sign = sign;
1862 m_value->_mpfr_exp = exp;
1863 // Next the limbs. Protect in try/catch so if anything goes wrong we can fix this in the
1864 // catch block before re-throwing.
1865 try {
1866 PIRANHA_MAYBE_TLS std::vector<msgpack::object> vlimbs;
1867 vobj[3].convert(vlimbs);
1868 const auto s = safe_cast<std::vector<msgpack::object>::size_type>(size_from_prec(prec));
1869 if (unlikely(s != vlimbs.size())) {
1870 piranha_throw(std::invalid_argument,
1871 std::string("error in the msgpack deserialization of a real: the number "
1872 "of serialized limbs (")
1873 + std::to_string(vlimbs.size())
1874 + ") is not consistent with the number of limbs inferred from the precision ("
1875 + std::to_string(s) + ")");
1876 }
1877 for (decltype(vlimbs.size()) i = 0; i < s; ++i) {
1878 piranha::msgpack_convert(m_value->_mpfr_d[i], vlimbs[i], f);
1879 }
1880 } catch (...) {
1881 // Set to zero before re-throwing.
1882 ::mpfr_set_ui(m_value, 0u, default_rnd);
1883 throw;
1884 }
1885 }
1886 }
1887 #endif
1888
1889 private:
1890 ::mpfr_t m_value;
1891 };
1892
1893 namespace math
1894 {
1895
1896 /// Specialisation of the piranha::math::negate() functor for piranha::real.
1897 template <typename T>
1898 struct negate_impl<T, typename std::enable_if<std::is_same<T, real>::value>::type> {
1899 /// Call operator.
1900 /**
1901 * @param x piranha::real to be negated.
1902 */
operator ()piranha::math::negate_impl1903 void operator()(real &x) const
1904 {
1905 x.negate();
1906 }
1907 };
1908
1909 /// Specialisation of the piranha::math::is_zero() functor for piranha::real.
1910 template <typename T>
1911 struct is_zero_impl<T, typename std::enable_if<std::is_same<T, real>::value>::type> {
1912 /// Call operator.
1913 /**
1914 * @param r piranha::real to be tested.
1915 *
1916 * @return \p true if \p r is zero, \p false otherwise.
1917 */
operator ()piranha::math::is_zero_impl1918 bool operator()(const T &r) const
1919 {
1920 return r.is_zero();
1921 }
1922 };
1923 }
1924
1925 namespace detail
1926 {
1927
1928 // Enabler for real pow.
1929 template <typename T, typename U>
1930 using real_pow_enabler =
1931 typename std::enable_if<(std::is_same<real, T>::value && is_real_interoperable_type<U>::value)
1932 || (std::is_same<real, U>::value && is_real_interoperable_type<T>::value)
1933 || (std::is_same<real, T>::value && std::is_same<real, U>::value)>::type;
1934
1935 // Binomial follows the same rules as pow.
1936 template <typename T, typename U>
1937 using real_binomial_enabler = real_pow_enabler<T, U>;
1938 }
1939
1940 namespace math
1941 {
1942
1943 /// Specialisation of the piranha::math::pow() functor for piranha::real.
1944 /**
1945 * This specialisation is activated when one of the arguments is piranha::real
1946 * and the other is either piranha::real or an interoperable type for piranha::real.
1947 *
1948 * The implementation follows these rules:
1949 * - if base and exponent are both piranha::real, then piranha::real::pow() is used;
1950 * - otherwise, the non-real argument is converted to piranha::real and then piranha::real::pow()
1951 * is used.
1952 */
1953 template <typename T, typename U>
1954 struct pow_impl<T, U, detail::real_pow_enabler<T, U>> {
1955 /// Call operator, real--real overload.
1956 /**
1957 * @param r base.
1958 * @param x exponent.
1959 *
1960 * @return \p r to the power of \p x.
1961 *
1962 * @throws unspecified any exception thrown by piranha::real::pow().
1963 */
operator ()piranha::math::pow_impl1964 real operator()(const real &r, const real &x) const
1965 {
1966 return r.pow(x);
1967 }
1968 /// Call operator, real base overload.
1969 /**
1970 * @param r base.
1971 * @param x exponent.
1972 *
1973 * @return \p r to the power of \p x.
1974 *
1975 * @throws unspecified any exception thrown by piranha::real::pow() or by
1976 * the invoked piranha::real constructor.
1977 */
1978 template <typename T2>
operator ()piranha::math::pow_impl1979 real operator()(const real &r, const T2 &x) const
1980 {
1981 // NOTE: init with the same precision as r in order
1982 // to maintain the same precision in the result.
1983 return r.pow(real{x, r.get_prec()});
1984 }
1985 /// Call operator, real exponent overload.
1986 /**
1987 * @param r base.
1988 * @param x exponent.
1989 *
1990 * @return \p r to the power of \p x.
1991 *
1992 * @throws unspecified any exception thrown by piranha::real::pow() or by
1993 * the invoked piranha::real constructor.
1994 */
1995 template <typename T2>
operator ()piranha::math::pow_impl1996 real operator()(const T2 &r, const real &x) const
1997 {
1998 return real{r, x.get_prec()}.pow(x);
1999 }
2000 };
2001
2002 /// Specialisation of the piranha::math::sin() functor for piranha::real.
2003 template <typename T>
2004 struct sin_impl<T, typename std::enable_if<std::is_same<T, real>::value>::type> {
2005 /// Call operator.
2006 /**
2007 * The operation will return the output of piranha::real::sin().
2008 *
2009 * @param r argument.
2010 *
2011 * @return sine of \p r.
2012 */
operator ()piranha::math::sin_impl2013 real operator()(const T &r) const
2014 {
2015 return r.sin();
2016 }
2017 };
2018
2019 /// Specialisation of the piranha::math::cos() functor for piranha::real.
2020 template <typename T>
2021 struct cos_impl<T, typename std::enable_if<std::is_same<T, real>::value>::type> {
2022 /// Call operator.
2023 /**
2024 * The operation will return the output of piranha::real::cos().
2025 *
2026 * @param r argument.
2027 *
2028 * @return cosine of \p r.
2029 */
operator ()piranha::math::cos_impl2030 real operator()(const T &r) const
2031 {
2032 return r.cos();
2033 }
2034 };
2035
2036 /// Specialisation of the piranha::math::abs() functor for piranha::real.
2037 template <typename T>
2038 struct abs_impl<T, typename std::enable_if<std::is_same<T, real>::value>::type> {
2039 /// Call operator.
2040 /**
2041 * @param x input parameter.
2042 *
2043 * @return absolute value of \p x.
2044 */
operator ()piranha::math::abs_impl2045 T operator()(const T &x) const
2046 {
2047 return x.abs();
2048 }
2049 };
2050
2051 /// Specialisation of the piranha::math::partial() functor for piranha::real.
2052 template <typename T>
2053 struct partial_impl<T, typename std::enable_if<std::is_same<T, real>::value>::type> {
2054 /// Call operator.
2055 /**
2056 * @return an instance of piranha::real constructed from zero.
2057 */
operator ()piranha::math::partial_impl2058 real operator()(const real &, const std::string &) const
2059 {
2060 return real(0);
2061 }
2062 };
2063
2064 /// Specialisation of the piranha::math::binomial() functor for piranha::real.
2065 /**
2066 * This specialisation is activated when one of the arguments is piranha::real
2067 * and the other is either piranha::real or an interoperable type for piranha::real.
2068 *
2069 * The implementation follows these rules:
2070 * - if top and bottom are both piranha::real, then piranha::real::binomial() is used;
2071 * - otherwise, the non-real argument is converted to piranha::real and then piranha::real::binomial()
2072 * is used.
2073 */
2074 template <typename T, typename U>
2075 struct binomial_impl<T, U, detail::real_binomial_enabler<T, U>> {
2076 /// Call operator, real--real overload.
2077 /**
2078 * @param x top.
2079 * @param y bottom.
2080 *
2081 * @return \p x choose \p y.
2082 *
2083 * @throws unspecified any exception thrown by piranha::real::binomial().
2084 */
operator ()piranha::math::binomial_impl2085 real operator()(const real &x, const real &y) const
2086 {
2087 return x.binomial(y);
2088 }
2089 /// Call operator, real top overload.
2090 /**
2091 * @param x top.
2092 * @param y bottom.
2093 *
2094 * @return \p x choose \p y.
2095 *
2096 * @throws unspecified any exception thrown by piranha::real::binomial() or by the invoked
2097 * piranha::real constructor.
2098 */
2099 template <typename T2>
operator ()piranha::math::binomial_impl2100 real operator()(const real &x, const T2 &y) const
2101 {
2102 // NOTE: init with the same precision as r in order
2103 // to maintain the same precision in the result.
2104 return x.binomial(real{y, x.get_prec()});
2105 }
2106 /// Call operator, real bottom overload.
2107 /**
2108 * @param x top.
2109 * @param y bottom.
2110 *
2111 * @return \p x choose \p y.
2112 *
2113 * @throws unspecified any exception thrown by piranha::real::binomial() or by the invoked
2114 * piranha::real constructor.
2115 */
2116 template <typename T2>
operator ()piranha::math::binomial_impl2117 real operator()(const T2 &x, const real &y) const
2118 {
2119 return real{x, y.get_prec()}.binomial(y);
2120 }
2121 };
2122
2123 /// Specialisation of the implementation of piranha::math::multiply_accumulate() for piranha::real.
2124 template <>
2125 struct multiply_accumulate_impl<real> {
2126 /// Call operator.
2127 /**
2128 * This implementation will use piranha::real::multiply_accumulate().
2129 *
2130 * @param x target value for accumulation.
2131 * @param y first argument.
2132 * @param z second argument.
2133 */
operator ()piranha::math::multiply_accumulate_impl2134 void operator()(real &x, const real &y, const real &z) const
2135 {
2136 x.multiply_accumulate(y, z);
2137 }
2138 };
2139 }
2140
~real()2141 inline real::~real()
2142 {
2143 PIRANHA_TT_CHECK(is_cf, real);
2144 static_assert(default_prec >= MPFR_PREC_MIN && default_prec <= MPFR_PREC_MAX,
2145 "Invalid value for default precision.");
2146 if (m_value->_mpfr_d) {
2147 ::mpfr_clear(m_value);
2148 } else {
2149 // NOTE: the story here is that ICC has a weird behaviour when the thread_local
2150 // storage. Essentially, the thread-local static variable in the fma() function
2151 // upon destruction has the _mpfr_d set to zero for some reason but the other members
2152 // are not zeroed out. This results in the asserts below firing, and probably a memory
2153 // leak as well as the variable is not cleared. We just disable the asserts for now.
2154 #if !defined(PIRANHA_COMPILER_IS_INTEL)
2155 piranha_assert(!m_value->_mpfr_prec);
2156 piranha_assert(!m_value->_mpfr_sign);
2157 piranha_assert(!m_value->_mpfr_exp);
2158 #endif
2159 }
2160 }
2161
2162 namespace detail
2163 {
2164
2165 // Compute gamma(a)/(gamma(b) * gamma(c)), assuming a, b and c are not negative ints. The logarithm
2166 // of the gamma function is used internally.
real_compute_3_gamma(const real & a,const real & b,const real & c,const::mpfr_prec_t & prec)2167 inline real real_compute_3_gamma(const real &a, const real &b, const real &c, const ::mpfr_prec_t &prec)
2168 {
2169 // Here we should never enter with negative ints.
2170 piranha_assert(a.sign() >= 0 || a.truncated() != a);
2171 piranha_assert(b.sign() >= 0 || b.truncated() != b);
2172 piranha_assert(c.sign() >= 0 || c.truncated() != c);
2173 const real pi = real{0, prec}.pi();
2174 real tmp0(0), tmp1(1);
2175 if (a.sign() < 0) {
2176 tmp0 -= (1 - a).lgamma();
2177 tmp1 *= pi / (a * pi).sin();
2178 } else {
2179 tmp0 += a.lgamma();
2180 }
2181 if (b.sign() < 0) {
2182 tmp0 += (1 - b).lgamma();
2183 tmp1 *= (b * pi).sin() / pi;
2184 } else {
2185 tmp0 -= b.lgamma();
2186 }
2187 if (c.sign() < 0) {
2188 tmp0 += (1 - c).lgamma();
2189 tmp1 *= (c * pi).sin() / pi;
2190 } else {
2191 tmp0 -= c.lgamma();
2192 }
2193 return tmp0.exp() * tmp1;
2194 }
2195 }
2196
2197 /// Binomial coefficient.
2198 /**
2199 * This method will return \p this choose \p y. Any combination of real values is supported.
2200 * The implementation uses the logarithm of the gamma function, thus the result will not be in
2201 * general exact (even if \p this and \p y are integral values).
2202 *
2203 * The returned value will have the maximum precision between \p this and \p y.
2204 *
2205 * @param y bottom value.
2206 *
2207 * @return \p this choose \p y.
2208 *
2209 * @throws std::invalid_argument if either \p this or \p y is not finite.
2210 * @throws unspecified any exception resulting from arithmetic operations on piranha::real.
2211 */
binomial(const real & y) const2212 inline real real::binomial(const real &y) const
2213 {
2214 if (unlikely(is_nan() || is_inf() || y.is_nan() || y.is_inf())) {
2215 piranha_throw(std::invalid_argument, "cannot compute binomial coefficient with non-finite real argument(s)");
2216 }
2217 // Work with the max precision.
2218 const ::mpfr_prec_t max_prec = std::max<::mpfr_prec_t>(get_prec(), y.get_prec());
2219 const bool neg_int_x = truncated() == (*this) && sign() < 0, neg_int_y = y.truncated() == y && y.sign() < 0,
2220 neg_int_x_y = ((*this) - y).truncated() == ((*this) - y) && ((*this) - y).sign() < 0;
2221 const unsigned mask = unsigned(neg_int_x) + (unsigned(neg_int_y) << 1u) + (unsigned(neg_int_x_y) << 2u);
2222 switch (mask) {
2223 case 0u:
2224 // Case 0 is the non-special one, use the default implementation.
2225 return detail::real_compute_3_gamma((*this) + 1, y + 1, (*this) - y + 1, max_prec);
2226 // NOTE: case 1 is not possible: x < 0, y > 0 implies x - y < 0 always.
2227 case 2u:
2228 case 4u:
2229 // These are finite numerators with infinite denominators.
2230 return real{0, max_prec};
2231 // NOTE: case 6 is not possible: x > 0, y < 0 implies x - y > 0 always.
2232 case 3u: {
2233 // 3 and 5 are the cases with 1 inf in num and 1 inf in den. Use the transformation
2234 // formula to make them finite.
2235 // NOTE: the phase here is really just a sign, but it seems tricky to compute this exactly
2236 // due to potential rounding errors. We are attempting to err on the safe side by using pow()
2237 // here.
2238 const auto phase = math::pow(-1, (*this) + 1) / math::pow(-1, y + 1);
2239 return detail::real_compute_3_gamma(-y, -(*this), (*this) - y + 1, max_prec) * phase;
2240 }
2241 case 5u: {
2242 const auto phase = math::pow(-1, (*this) - y + 1) / math::pow(-1, (*this) + 1);
2243 return detail::real_compute_3_gamma(-((*this) - y), y + 1, -(*this), max_prec) * phase;
2244 }
2245 }
2246 // Case 7 returns zero -> from inf / (inf * inf) it becomes a / (b * inf) after the transform.
2247 // NOTE: put it here so the compiler does not complain about missing return statement in the switch block.
2248 return real{0, max_prec};
2249 }
2250
2251 inline namespace impl
2252 {
2253
2254 template <typename To, typename From>
2255 using sc_real_enabler
2256 = enable_if_t<conjunction<disjunction<std::is_integral<To>, detail::is_mp_integer<To>, detail::is_mp_rational<To>>,
2257 std::is_same<From, real>>::value>;
2258 }
2259
2260 /// Specialisation of piranha::safe_cast() for conversions involving piranha::real.
2261 /**
2262 * \note
2263 * This specialisation is enabled if \p To is an integral type, piranha::mp_integer or piranha::mp_rational, and \p From
2264 * is piranha::real.
2265 */
2266 template <typename To, typename From>
2267 struct safe_cast_impl<To, From, sc_real_enabler<To, From>> {
2268 private:
2269 template <typename T>
2270 using integral_enabler = enable_if_t<disjunction<std::is_integral<T>, detail::is_mp_integer<T>>::value, int>;
2271 template <typename T>
2272 using rational_enabler = enable_if_t<detail::is_mp_rational<T>::value, int>;
2273
2274 public:
2275 /// Call operator, real to integral overload.
2276 /**
2277 * \note
2278 * This operator is enabled if \p To is an integral type or an instance of piranha::mp_integer.
2279 *
2280 * The conversion will succeed if \p r is a finite integral value representable by
2281 * the target type.
2282 *
2283 * @param r conversion argument.
2284 *
2285 * @return \p r converted to \p To.
2286 *
2287 * @throws piranha::safe_cast_failure if the conversion fails.
2288 * @throws unspecified any exception thrown by \p boost::lexical_cast().
2289 */
2290 template <typename T = To, integral_enabler<T> = 0>
2291 T operator()(const real &r) const
2292 {
2293 // NOTE: the finiteness check here is repeated in the cast below, but we need it here in order
2294 // to make sure that we can compute the truncated r.
2295 if (unlikely(r.is_inf() || r.is_nan() || r.truncated() != r)) {
2296 piranha_throw(safe_cast_failure, "cannot convert the input real " + boost::lexical_cast<std::string>(r)
2297 + " to the integral type '" + detail::demangle<To>()
2298 + "', as the input real does not represent a finite integral value");
2299 }
2300 try {
2301 return static_cast<T>(r);
2302 } catch (const std::overflow_error &) {
2303 piranha_throw(safe_cast_failure, "cannot convert the input real " + boost::lexical_cast<std::string>(r)
2304 + " to the integral type '" + detail::demangle<To>()
2305 + "', as the conversion would not preserve the value");
2306 }
2307 }
2308 /// Call operator, real to rational overload.
2309 /**
2310 * \note
2311 * This operator is enabled if \p To is an instance of piranha::mp_rational.
2312 *
2313 * @param r conversion argument.
2314 *
2315 * @return \p r converted to piranha::mp_rational.
2316 *
2317 * @throws piranha::safe_cast_failure if the conversion fails.
2318 * @throws unspecified any exception thrown by \p boost::lexical_cast().
2319 */
2320 template <typename T = To, rational_enabler<T> = 0>
2321 T operator()(const real &r) const
2322 {
2323 try {
2324 return static_cast<T>(r);
2325 } catch (const std::overflow_error &) {
2326 piranha_throw(safe_cast_failure, "cannot convert the input real " + boost::lexical_cast<std::string>(r)
2327 + " to the rational type '" + detail::demangle<To>()
2328 + "', as the conversion would not preserve the value");
2329 }
2330 }
2331 };
2332
2333 inline namespace literals
2334 {
2335
2336 /// Literal for piranha::real.
2337 /**
2338 * The return value will be constructed with default precision.
2339 *
2340 * @param s literal string.
2341 *
2342 * @return a piranha::real constructed from \p s.
2343 *
2344 * @throws unspecified any exception thrown by the constructor of
2345 * piranha::real from string.
2346 */
operator ""_r(const char * s)2347 inline real operator"" _r(const char *s)
2348 {
2349 return real(s);
2350 }
2351 }
2352
2353 inline namespace impl
2354 {
2355
2356 template <typename Archive>
2357 using real_boost_save_enabler
2358 = enable_if_t<conjunction<has_boost_save<Archive, ::mpfr_prec_t>, has_boost_save<Archive, std::string>,
2359 has_boost_save<Archive, decltype(std::declval<const ::mpfr_t &>()->_mpfr_sign)>,
2360 has_boost_save<Archive, decltype(std::declval<const ::mpfr_t &>()->_mpfr_exp)>,
2361 has_boost_save<Archive, ::mp_limb_t>>::value>;
2362
2363 template <typename Archive>
2364 using real_boost_load_enabler
2365 = enable_if_t<conjunction<has_boost_load<Archive, ::mpfr_prec_t>, has_boost_load<Archive, std::string>,
2366 has_boost_load<Archive, decltype(std::declval<const ::mpfr_t &>()->_mpfr_sign)>,
2367 has_boost_load<Archive, decltype(std::declval<const ::mpfr_t &>()->_mpfr_exp)>,
2368 has_boost_load<Archive, ::mp_limb_t>>::value>;
2369 }
2370
2371 /// Specialisation of piranha::boost_save() for piranha::real.
2372 /**
2373 * \note
2374 * This specialisation is enabled only if \p std::string and all the integral types in terms of which piranha::real
2375 * is implemented satisfy piranha::has_boost_save.
2376 *
2377 * If \p Archive is \p boost::archive::binary_oarchive, a platform dependent binary representation of the input
2378 * piranha::real will be saved. Otherwise, the piranha::real is serialized in string form.
2379 *
2380 * @throws unspecified any exception thrown by piranha::boost_save() or by the conversion of the input piranha::real to
2381 * string.
2382 */
2383 template <typename Archive>
2384 struct boost_save_impl<Archive, real, real_boost_save_enabler<Archive>> : boost_save_via_boost_api<Archive, real> {
2385 };
2386
2387 /// Specialisation of piranha::boost_load() for piranha::real.
2388 /**
2389 * \note
2390 * This specialisation is enabled only if \p std::string and all the integral types in terms of which piranha::real
2391 * is implemented satisfy piranha::has_boost_load.
2392 *
2393 * If \p Archive is \p boost::archive::binary_iarchive, no checking is performed on the deserialized piranha::real
2394 * and the implementation offers the basic exception safety guarantee.
2395 *
2396 * @throws unspecified any exception thrown by piranha::boost_load(), or by the constructor of
2397 * piranha::real from string.
2398 */
2399 template <typename Archive>
2400 struct boost_load_impl<Archive, real, real_boost_load_enabler<Archive>> : boost_load_via_boost_api<Archive, real> {
2401 };
2402
2403 #if defined(PIRANHA_WITH_MSGPACK)
2404
2405 inline namespace impl
2406 {
2407
2408 // Enablers for msgpack serialization.
2409 template <typename Stream>
2410 using real_msgpack_pack_enabler = enable_if_t<is_detected<msgpack_pack_member_t, Stream, real>::value>;
2411
2412 template <typename T>
2413 using real_msgpack_convert_enabler
2414 = enable_if_t<conjunction<std::is_same<real, T>, is_detected<msgpack_convert_member_t, T>>::value>;
2415 }
2416
2417 /// Specialisation of piranha::msgpack_pack() for piranha::real.
2418 /**
2419 * \note
2420 * This specialisation is enabled if \p T is piranha::real and
2421 * the piranha::real::msgpack_pack() method is supported with a stream of type \p Stream.
2422 */
2423 template <typename Stream>
2424 struct msgpack_pack_impl<Stream, real, real_msgpack_pack_enabler<Stream>> {
2425 /// Call operator.
2426 /**
2427 * The call operator will use piranha::real::msgpack_pack() internally.
2428 *
2429 * @param p target <tt>msgpack::packer</tt>.
2430 * @param x piranha::real to be serialized.
2431 * @param f the desired piranha::msgpack_format.
2432 *
2433 * @throws unspecified any exception thrown by piranha::real::msgpack_pack().
2434 */
operator ()piranha::msgpack_pack_impl2435 void operator()(msgpack::packer<Stream> &p, const real &x, msgpack_format f) const
2436 {
2437 x.msgpack_pack(p, f);
2438 }
2439 };
2440
2441 /// Specialisation of piranha::msgpack_convert() for piranha::real.
2442 /**
2443 * \note
2444 * This specialisation is enabled if \p T is piranha::real and
2445 * the piranha::real::msgpack_convert() method is supported.
2446 */
2447 template <typename T>
2448 struct msgpack_convert_impl<T, real_msgpack_convert_enabler<T>> {
2449 /// Call operator.
2450 /**
2451 * The call operator will use piranha::real::msgpack_convert() internally.
2452 *
2453 * @param x target piranha::real.
2454 * @param o the <tt>msgpack::object</tt> to be converted into \p n.
2455 * @param f the desired piranha::msgpack_format.
2456 *
2457 * @throws unspecified any exception thrown by piranha::real::msgpack_convert().
2458 */
operator ()piranha::msgpack_convert_impl2459 void operator()(T &x, const msgpack::object &o, msgpack_format f) const
2460 {
2461 x.msgpack_convert(o, f);
2462 }
2463 };
2464
2465 #endif
2466
2467 inline namespace impl
2468 {
2469
2470 template <typename T>
2471 using real_zero_is_absorbing_enabler = enable_if_t<std::is_same<uncvref_t<T>, real>::value>;
2472 }
2473
2474 /// Specialisation of piranha::zero_is_absorbing for piranha::real.
2475 /**
2476 * \note
2477 * This specialisation is enabled if \p T, after the removal of cv/reference qualifiers, is piranha::real.
2478 *
2479 * Due to the presence of NaN, the zero element is not absorbing for piranha::real.
2480 */
2481 template <typename T>
2482 struct zero_is_absorbing<T, real_zero_is_absorbing_enabler<T>> {
2483 /// Value of the type trait.
2484 static const bool value = false;
2485 };
2486
2487 template <typename T>
2488 const bool zero_is_absorbing<T, real_zero_is_absorbing_enabler<T>>::value;
2489 }
2490
2491 #endif
2492