piranha  0.10
mp_rational.hpp
1 /* Copyright 2009-2017 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_MP_RATIONAL_HPP
30 #define PIRANHA_MP_RATIONAL_HPP
31 
32 #include <array>
33 #include <boost/functional/hash.hpp>
34 #include <boost/lexical_cast.hpp>
35 #include <climits>
36 #include <cmath>
37 #include <cstddef>
38 #include <cstring>
39 #include <functional>
40 #include <iostream>
41 #include <limits>
42 #include <stdexcept>
43 #include <string>
44 #include <type_traits>
45 #include <unordered_map>
46 #include <utility>
47 
48 #include <piranha/binomial.hpp>
49 #include <piranha/config.hpp>
50 #include <piranha/detail/demangle.hpp>
51 #include <piranha/detail/mp_rational_fwd.hpp>
52 #include <piranha/exceptions.hpp>
53 #include <piranha/math.hpp>
54 #include <piranha/mp_integer.hpp>
55 #include <piranha/pow.hpp>
56 #include <piranha/print_tex_coefficient.hpp>
57 #include <piranha/s11n.hpp>
58 #include <piranha/safe_cast.hpp>
59 #include <piranha/type_traits.hpp>
60 
61 namespace piranha
62 {
63 
65 
87 template <std::size_t SSize>
88 class mp_rational
89 {
90 public:
93 
94 private:
95  // Shortcut for interop type detector.
96  template <typename T>
97  using is_interoperable_type = disjunction<mppp::mppp_impl::is_supported_interop<T>, std::is_same<T, int_type>>;
98  // Enabler for ctor from num den pair.
99  template <typename I0, typename I1>
100  using nd_ctor_enabler
101  = enable_if_t<conjunction<disjunction<std::is_integral<I0>, std::is_same<I0, int_type>>,
102  disjunction<std::is_integral<I1>, std::is_same<I1, int_type>>>::value,
103  int>;
104  // Enabler for generic ctor.
105  template <typename T>
106  using generic_ctor_enabler = enable_if_t<is_interoperable_type<T>::value, int>;
107  // Enabler for in-place arithmetic operations with interop on the left.
108  template <typename T>
109  using generic_in_place_enabler
110  = enable_if_t<conjunction<is_interoperable_type<T>, negation<std::is_const<T>>>::value, int>;
111  // Generic constructor implementation.
112  template <typename T, enable_if_t<std::is_integral<T>::value, int> = 0>
113  void construct_from_interoperable(const T &x)
114  {
115  m_num = int_type(x);
116  m_den = 1;
117  }
118  template <typename T, enable_if_t<std::is_same<T, int_type>::value, int> = 0>
119  void construct_from_interoperable(const T &x)
120  {
121  m_num = x;
122  m_den = 1;
123  }
124  template <typename Float, enable_if_t<std::is_floating_point<Float>::value, int> = 0>
125  void construct_from_interoperable(const Float &x)
126  {
127  if (unlikely(!std::isfinite(x))) {
128  piranha_throw(std::invalid_argument, "cannot construct a rational from a non-finite floating-point number");
129  }
130  // Denominator is always inited as 1.
131  m_den = 1;
132  if (x == Float(0)) {
133  // m_den is 1 already.
134  return;
135  }
136  Float abs_x = std::abs(x);
137  const unsigned radix = static_cast<unsigned>(std::numeric_limits<Float>::radix);
138  int_type i_part;
139  int exp = std::ilogb(abs_x);
140  while (exp >= 0) {
141  i_part += math::pow(int_type(radix), exp);
142  const Float tmp = std::scalbn(Float(1), exp);
143  if (unlikely(tmp == HUGE_VAL)) {
144  piranha_throw(std::invalid_argument, "output of std::scalbn is HUGE_VAL");
145  }
146  abs_x -= tmp;
147  // Break out if x is an exact integer.
148  if (abs_x == Float(0)) {
149  // m_den is 1 already.
150  m_num = i_part;
151  if (x < Float(0)) {
152  m_num.neg();
153  }
154  return;
155  }
156  exp = std::ilogb(abs_x);
157  if (unlikely(exp == INT_MAX || exp == FP_ILOGBNAN)) {
158  piranha_throw(std::invalid_argument, "error calling std::ilogb");
159  }
160  }
161  piranha_assert(abs_x < Float(1));
162  // Lift up the decimal part into an integer.
163  while (abs_x != Float(0)) {
164  abs_x = std::scalbln(abs_x, 1);
165  if (unlikely(abs_x == HUGE_VAL)) {
166  piranha_throw(std::invalid_argument, "output of std::scalbn is HUGE_VAL");
167  }
168  const auto t_abs_x = std::trunc(abs_x);
169  m_den *= radix;
170  m_num *= radix;
171  // NOTE: here t_abs_x is guaranteed to be in
172  // [0,radix - 1], so the cast to unsigned should be ok.
173  // Note that floating-point numbers are guaranteed to be able
174  // to represent exactly at least a [-exp,+exp] exponent range
175  // (see the minimum values for the FLT constants in the C standard).
176  m_num += static_cast<unsigned>(t_abs_x);
177  abs_x -= t_abs_x;
178  }
179  math::multiply_accumulate(m_num, i_part, m_den);
180  canonicalise();
181  if (x < Float(0)) {
182  m_num.neg();
183  }
184  }
185  // Enabler for conversion operator.
186  template <typename T>
187  using cast_enabler = generic_ctor_enabler<T>;
188  // Conversion operator implementation.
189  template <typename Float, enable_if_t<std::is_floating_point<Float>::value, int> = 0>
190  Float convert_to_impl() const
191  {
192  // NOTE: there are better ways of doing this. For instance, here we might end up generating an inf even
193  // if the result is actually representable. It also would be nice if this routine could short-circuit,
194  // that is, for every rational generated from a float we get back exactly the same float after the cast.
195  // The approach in GMP mpq might work for this, but it's not essential at the moment.
196  return static_cast<Float>(m_num) / static_cast<Float>(m_den);
197  }
198  template <typename Integral, enable_if_t<std::is_integral<Integral>::value, int> = 0>
199  Integral convert_to_impl() const
200  {
201  return static_cast<Integral>(static_cast<int_type>(*this));
202  }
203  template <typename MpInteger, enable_if_t<std::is_same<MpInteger, int_type>::value, int> = 0>
204  MpInteger convert_to_impl() const
205  {
206  return m_num / m_den;
207  }
208  // In-place add.
209  mp_rational &in_place_add(const mp_rational &other)
210  {
211  // NOTE: all this should never throw because we only operate on mp_integer objects,
212  // no conversions involved, etc.
213  const bool u1 = m_den.is_one(), u2 = other.m_den.is_one();
214  if (u1 && u2) {
215  // Both are integers, just add without canonicalising. This is safe if
216  // this and other are the same object.
217  m_num += other.m_num;
218  } else if (u1) {
219  // Only this is an integer.
220  // NOTE: figure out a way here to use multiply_accumulate(). Most likely we need
221  // a tmp copy, so better profile it first.
222  m_num = m_num * other.m_den + other.m_num;
223  m_den = other.m_den;
224  } else if (u2) {
225  // Only other is an integer.
226  math::multiply_accumulate(m_num, m_den, other.m_num);
227  } else if (m_den == other.m_den) {
228  // Denominators are the same, add numerators and canonicalise.
229  // NOTE: safe if this and other coincide.
230  m_num += other.m_num;
231  canonicalise();
232  } else {
233  // The general case.
234  // NOTE: here dens are different (cannot coincide), and thus
235  // this and other must be separate objects.
236  m_num *= other.m_den;
237  math::multiply_accumulate(m_num, m_den, other.m_num);
238  m_den *= other.m_den;
239  canonicalise();
240  }
241  return *this;
242  }
243  mp_rational &in_place_add(const int_type &other)
244  {
245  if (m_den.is_one()) {
246  // If den is unitary, no need to multiply.
247  m_num += other;
248  } else {
249  math::multiply_accumulate(m_num, m_den, other);
250  }
251  return *this;
252  }
253  template <typename T, enable_if_t<std::is_integral<T>::value, int> = 0>
254  mp_rational &in_place_add(const T &n)
255  {
256  return in_place_add(int_type(n));
257  }
258  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
259  mp_rational &in_place_add(const T &x)
260  {
261  return (*this = static_cast<T>(*this) + x);
262  }
263  // Binary add.
264  template <typename T>
265  static mp_rational binary_plus_impl(const mp_rational &q1, const T &x)
266  {
267  auto retval(q1);
268  retval += x;
269  return retval;
270  }
271  static mp_rational binary_plus(const mp_rational &q1, const mp_rational &q2)
272  {
273  return binary_plus_impl(q1, q2);
274  }
275  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
276  static mp_rational binary_plus(const mp_rational &q1, const T &x)
277  {
278  return binary_plus_impl(q1, x);
279  }
280  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
281  static mp_rational binary_plus(const T &x, const mp_rational &q2)
282  {
283  return binary_plus(q2, x);
284  }
285  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
286  static T binary_plus(const mp_rational &q1, const T &x)
287  {
288  return x + static_cast<T>(q1);
289  }
290  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
291  static T binary_plus(const T &x, const mp_rational &q2)
292  {
293  return binary_plus(q2, x);
294  }
295  // In-place sub.
296  mp_rational &in_place_sub(const mp_rational &other)
297  {
298  // NOTE: optimisations are possible here if we implement multiply_sub
299  // or do some trickery with in-place negation + multiply_accumulate().
300  // Keep it in mind for future optimisations.
301  const bool u1 = m_den.is_one(), u2 = other.m_den.is_one();
302  if (u1 && u2) {
303  m_num -= other.m_num;
304  } else if (u1) {
305  m_num = m_num * other.m_den - other.m_num;
306  m_den = other.m_den;
307  } else if (u2) {
308  m_num = m_num - m_den * other.m_num;
309  } else if (m_den == other.m_den) {
310  m_num -= other.m_num;
311  canonicalise();
312  } else {
313  m_num *= other.m_den;
314  // Negate temporarily in order to use multiply_accumulate.
315  // NOTE: candidate for multiply_sub if we ever implement it.
316  m_den.neg();
317  math::multiply_accumulate(m_num, m_den, other.m_num);
318  m_den.neg();
319  m_den *= other.m_den;
320  canonicalise();
321  }
322  return *this;
323  }
324  mp_rational &in_place_sub(const int_type &other)
325  {
326  if (m_den.is_one()) {
327  m_num -= other;
328  } else {
329  m_den.neg();
330  math::multiply_accumulate(m_num, m_den, other);
331  m_den.neg();
332  }
333  return *this;
334  }
335  template <typename T, enable_if_t<std::is_integral<T>::value, int> = 0>
336  mp_rational &in_place_sub(const T &n)
337  {
338  return in_place_sub(int_type(n));
339  }
340  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
341  mp_rational &in_place_sub(const T &x)
342  {
343  return (*this = static_cast<T>(*this) - x);
344  }
345  // Binary sub.
346  template <typename T>
347  static mp_rational binary_minus_impl(const mp_rational &q1, const T &x)
348  {
349  auto retval(q1);
350  retval -= x;
351  return retval;
352  }
353  static mp_rational binary_minus(const mp_rational &q1, const mp_rational &q2)
354  {
355  return binary_minus_impl(q1, q2);
356  }
357  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
358  static mp_rational binary_minus(const mp_rational &q1, const T &x)
359  {
360  return binary_minus_impl(q1, x);
361  }
362  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
363  static mp_rational binary_minus(const T &x, const mp_rational &q2)
364  {
365  auto retval = binary_minus(q2, x);
366  retval.negate();
367  return retval;
368  }
369  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
370  static T binary_minus(const mp_rational &q1, const T &x)
371  {
372  return static_cast<T>(q1) - x;
373  }
374  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
375  static T binary_minus(const T &x, const mp_rational &q2)
376  {
377  return -binary_minus(q2, x);
378  }
379  // In-place mult.
380  mp_rational &in_place_mult(const mp_rational &other)
381  {
382  if (m_den.is_one() && other.m_den.is_one()) {
383  m_num *= other.m_num;
384  } else {
385  // NOTE: no issue here if this and other are the same object.
386  m_num *= other.m_num;
387  m_den *= other.m_den;
388  canonicalise();
389  }
390  return *this;
391  }
392  mp_rational &in_place_mult(const int_type &other)
393  {
394  m_num *= other;
395  if (!m_den.is_one()) {
396  canonicalise();
397  }
398  return *this;
399  }
400  template <typename T, enable_if_t<std::is_integral<T>::value, int> = 0>
401  mp_rational &in_place_mult(const T &n)
402  {
403  return in_place_mult(int_type(n));
404  }
405  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
406  mp_rational &in_place_mult(const T &x)
407  {
408  return (*this = static_cast<T>(*this) * x);
409  }
410  // Binary mult.
411  template <typename T>
412  static mp_rational binary_mult_impl(const mp_rational &q1, const T &x)
413  {
414  auto retval(q1);
415  retval *= x;
416  return retval;
417  }
418  static mp_rational binary_mult(const mp_rational &q1, const mp_rational &q2)
419  {
420  return binary_mult_impl(q1, q2);
421  }
422  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
423  static mp_rational binary_mult(const mp_rational &q1, const T &x)
424  {
425  return binary_mult_impl(q1, x);
426  }
427  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
428  static mp_rational binary_mult(const T &x, const mp_rational &q2)
429  {
430  return binary_mult(q2, x);
431  }
432  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
433  static T binary_mult(const mp_rational &q1, const T &x)
434  {
435  return x * static_cast<T>(q1);
436  }
437  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
438  static T binary_mult(const T &x, const mp_rational &q2)
439  {
440  return binary_mult(q2, x);
441  }
442  // In-place div.
443  mp_rational &in_place_div(const mp_rational &other)
444  {
445  // NOTE: need to do this, otherwise the cross num/dem
446  // operations below will mess num and den up. This is ok
447  // if this == 0, as this is checked in the outer operator.
448  if (unlikely(this == &other)) {
449  m_num = 1;
450  m_den = 1;
451  } else {
452  m_num *= other.m_den;
453  m_den *= other.m_num;
454  canonicalise();
455  }
456  return *this;
457  }
458  mp_rational &in_place_div(const int_type &other)
459  {
460  m_den *= other;
461  canonicalise();
462  return *this;
463  }
464  template <typename T, enable_if_t<std::is_integral<T>::value, int> = 0>
465  mp_rational &in_place_div(const T &n)
466  {
467  return in_place_div(int_type(n));
468  }
469  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
470  mp_rational &in_place_div(const T &x)
471  {
472  return (*this = static_cast<T>(*this) / x);
473  }
474  // Binary div.
475  template <typename T>
476  static mp_rational binary_div_impl(const mp_rational &q1, const T &x)
477  {
478  auto retval(q1);
479  retval /= x;
480  return retval;
481  }
482  static mp_rational binary_div(const mp_rational &q1, const mp_rational &q2)
483  {
484  return binary_div_impl(q1, q2);
485  }
486  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
487  static mp_rational binary_div(const mp_rational &q1, const T &x)
488  {
489  return binary_div_impl(q1, x);
490  }
491  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
492  static mp_rational binary_div(const T &x, const mp_rational &q2)
493  {
494  mp_rational retval(x);
495  retval /= q2;
496  return retval;
497  }
498  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
499  static T binary_div(const mp_rational &q1, const T &x)
500  {
501  return static_cast<T>(q1) / x;
502  }
503  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
504  static T binary_div(const T &x, const mp_rational &q2)
505  {
506  return x / static_cast<T>(q2);
507  }
508  // Equality operator.
509  static bool binary_eq(const mp_rational &q1, const mp_rational &q2)
510  {
511  return q1.num() == q2.num() && q1.den() == q2.den();
512  }
513  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
514  static bool binary_eq(const mp_rational &q, const T &x)
515  {
516  return q.den().is_one() && q.num() == x;
517  }
518  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
519  static bool binary_eq(const T &x, const mp_rational &q)
520  {
521  return binary_eq(q, x);
522  }
523  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
524  static bool binary_eq(const mp_rational &q, const T &x)
525  {
526  return static_cast<T>(q) == x;
527  }
528  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
529  static bool binary_eq(const T &x, const mp_rational &q)
530  {
531  return binary_eq(q, x);
532  }
533  // Less-than operator.
534  static bool binary_less_than(const mp_rational &q1, const mp_rational &q2)
535  {
536  // NOTE: this is going to be slow in general. The implementation in GMP
537  // checks the limbs number before doing any multiplication, and probably
538  // other tricks. If this ever becomes a bottleneck, we probably need to do
539  // something similar (actually we could just use the view and piggy-back
540  // on mpq_cmp()...).
541  if (q1.m_den == q2.m_den) {
542  return q1.m_num < q2.m_num;
543  }
544  return q1.num() * q2.den() < q2.num() * q1.den();
545  }
546  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
547  static bool binary_less_than(const mp_rational &q, const T &x)
548  {
549  return q.num() < q.den() * x;
550  }
551  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
552  static bool binary_less_than(const T &x, const mp_rational &q)
553  {
554  return q.den() * x < q.num();
555  }
556  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
557  static bool binary_less_than(const mp_rational &q, const T &x)
558  {
559  return static_cast<T>(q) < x;
560  }
561  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
562  static bool binary_less_than(const T &x, const mp_rational &q)
563  {
564  return x < static_cast<T>(q);
565  }
566  // Greater-than operator.
567  static bool binary_greater_than(const mp_rational &q1, const mp_rational &q2)
568  {
569  if (q1.m_den == q2.m_den) {
570  return q1.m_num > q2.m_num;
571  }
572  return q1.num() * q2.den() > q2.num() * q1.den();
573  }
574  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
575  static bool binary_greater_than(const mp_rational &q, const T &x)
576  {
577  return q.num() > q.den() * x;
578  }
579  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
580  static bool binary_greater_than(const T &x, const mp_rational &q)
581  {
582  return q.den() * x > q.num();
583  }
584  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
585  static bool binary_greater_than(const mp_rational &q, const T &x)
586  {
587  return static_cast<T>(q) > x;
588  }
589  template <typename T, enable_if_t<std::is_floating_point<T>::value, int> = 0>
590  static bool binary_greater_than(const T &x, const mp_rational &q)
591  {
592  return x > static_cast<T>(q);
593  }
594  // mpq view class.
595  class mpq_view
596  {
597  using mpq_struct_t = std::remove_extent<::mpq_t>::type;
598 
599  public:
600  explicit mpq_view(const mp_rational &q) : m_n_view(q.num().get_mpz_view()), m_d_view(q.den().get_mpz_view())
601  {
602  // Shallow copy over to m_mpq the data from the views.
603  auto n_ptr = m_n_view.get();
604  auto d_ptr = m_d_view.get();
605  mpq_numref(&m_mpq)->_mp_alloc = n_ptr->_mp_alloc;
606  mpq_numref(&m_mpq)->_mp_size = n_ptr->_mp_size;
607  mpq_numref(&m_mpq)->_mp_d = n_ptr->_mp_d;
608  mpq_denref(&m_mpq)->_mp_alloc = d_ptr->_mp_alloc;
609  mpq_denref(&m_mpq)->_mp_size = d_ptr->_mp_size;
610  mpq_denref(&m_mpq)->_mp_d = d_ptr->_mp_d;
611  }
612  mpq_view(const mpq_view &) = delete;
613  mpq_view(mpq_view &&) = default;
614  mpq_view &operator=(const mpq_view &) = delete;
615  mpq_view &operator=(mpq_view &&) = delete;
616  operator mpq_struct_t const *() const
617  {
618  return get();
619  }
620  mpq_struct_t const *get() const
621  {
622  return &m_mpq;
623  }
624 
625  private:
626  decltype(std::declval<const int_type &>().get_mpz_view()) m_n_view;
627  decltype(std::declval<const int_type &>().get_mpz_view()) m_d_view;
628  mpq_struct_t m_mpq;
629  };
630  // Pow enabler.
631  template <typename T>
632  using pow_enabler = enable_if_t<
633  std::is_same<decltype(math::pow(std::declval<const int_type &>(), std::declval<const T &>())), int_type>::value,
634  int>;
635  // Serialization support.
636  friend class boost::serialization::access;
637  template <class Archive>
638  void save(Archive &ar, unsigned) const
639  {
640  piranha::boost_save(ar, m_num);
641  piranha::boost_save(ar, m_den);
642  }
643  template <class Archive, enable_if_t<!std::is_same<Archive, boost::archive::binary_iarchive>::value, int> = 0>
644  void load(Archive &ar, unsigned)
645  {
646  int_type num, den;
649  // This ensures that if we load from a bad archive with non-coprime
650  // num and den or negative den, or... we get anyway a canonicalised
651  // rational or an error.
652  *this = mp_rational{std::move(num), std::move(den)};
653  }
654  template <class Archive, enable_if_t<std::is_same<Archive, boost::archive::binary_iarchive>::value, int> = 0>
655  void load(Archive &ar, unsigned)
656  {
657  int_type num, den;
660  m_num = std::move(num);
661  m_den = std::move(den);
662  }
663  BOOST_SERIALIZATION_SPLIT_MEMBER()
664 public:
666 
670  mp_rational() : m_num(), m_den(1) {}
672 
675  mp_rational(const mp_rational &other) = default;
677 
680  mp_rational(mp_rational &&other) noexcept : m_num(std::move(other.m_num)), m_den(std::move(other.m_den))
681  {
682  // Fix the denominator of other, as its state depends on the implementation of piranha::mp_integer.
683  // Set it to 1, so other will have a valid canonical state regardless of what happens
684  // to the numerator.
685  other.m_den = 1;
686  }
688 
698  template <typename I0, typename I1, nd_ctor_enabler<I0, I1> = 0>
699  explicit mp_rational(const I0 &n, const I1 &d) : m_num(n), m_den(d)
700  {
701  if (unlikely(m_den.sgn() == 0)) {
702  piranha_throw(zero_division_error, "zero denominator");
703  }
704  canonicalise();
705  }
707 
716  template <typename T, generic_ctor_enabler<T> = 0>
717  explicit mp_rational(const T &x)
718  {
719  construct_from_interoperable(x);
720  }
722 
735  explicit mp_rational(const char *str) : m_num(), m_den(1)
736  {
737  auto ptr = str;
738  std::size_t num_size = 0u;
739  while (*ptr != '\0' && *ptr != '/') {
740  ++num_size;
741  ++ptr;
742  }
743  m_num = int_type(std::string(str, str + num_size));
744  if (*ptr == '/') {
745  m_den = int_type(std::string(ptr + 1u));
746  if (unlikely(math::is_zero(m_den))) {
747  piranha_throw(zero_division_error, "zero denominator");
748  }
749  canonicalise();
750  }
751  }
753 
760  explicit mp_rational(const std::string &str) : mp_rational(str.c_str()) {}
763  {
764  // NOTE: no checks no the numerator as we might mess it up
765  // with the low-level methods.
766  piranha_assert(m_den.sgn() > 0);
767  }
769 
774  mp_rational &operator=(const mp_rational &other) = default;
776 
781  mp_rational &operator=(mp_rational &&other) noexcept
782  {
783  if (unlikely(this == &other)) {
784  return *this;
785  }
786  m_num = std::move(other.m_num);
787  m_den = std::move(other.m_den);
788  // See comments in the move ctor.
789  other.m_den = 1;
790  return *this;
791  }
793 
806  template <typename T, generic_ctor_enabler<T> = 0>
807  mp_rational &operator=(const T &x)
808  {
809  return (*this = mp_rational(x));
810  }
812 
822  mp_rational &operator=(const char *str)
823  {
824  return (*this = mp_rational(str));
825  }
827 
837  mp_rational &operator=(const std::string &str)
838  {
839  return (*this = str.c_str());
840  }
842 
854  friend std::ostream &operator<<(std::ostream &os, const mp_rational &q)
855  {
856  if (q.m_den.is_one()) {
857  os << q.m_num;
858  } else {
859  os << q.m_num << '/' << q.m_den;
860  }
861  return os;
862  }
864 
874  friend std::istream &operator>>(std::istream &is, mp_rational &q)
875  {
876  std::string tmp_str;
877  std::getline(is, tmp_str);
878  q = tmp_str;
879  return is;
880  }
882 
885  const int_type &num() const
886  {
887  return m_num;
888  }
890 
893  const int_type &den() const
894  {
895  return m_den;
896  }
898 
913  mpq_view get_mpq_view() const
914  {
915  return mpq_view{*this};
916  }
918 
926  bool is_canonical() const
927  {
928  // NOTE: here the GCD only involves operations on mp_integers
929  // and thus it never throws. The construction from 1 in the comparisons will
930  // not throw either.
931  // NOTE: there should be no way to set a negative denominator, so no check is performed.
932  // The condition is checked in the dtor.
933  const auto gcd = math::gcd(m_num, m_den);
934  return (m_num.sgn() != 0 && (gcd == 1 || gcd == -1)) || (m_num.sgn() == 0 && m_den == 1);
935  }
937 
943  {
944  // If the top is null, den must be one.
945  if (math::is_zero(m_num)) {
946  m_den = 1;
947  return;
948  }
949  // NOTE: here we can avoid the further division by gcd if it is one or -one.
950  // Consider this as a possible optimisation in the future.
951  const int_type gcd = math::gcd(m_num, m_den);
952  piranha_assert(!math::is_zero(gcd));
953  divexact(m_num, m_num, gcd);
954  divexact(m_den, m_den, gcd);
955  // Fix mismatch in signs.
956  if (m_den.sgn() == -1) {
957  m_num.neg();
958  m_den.neg();
959  }
960  // NOTE: this could be a nice place to use the demote() method of mp_integer.
961  }
963 
976  template <typename T, cast_enabler<T> = 0>
977  explicit operator T() const
978  {
979  return convert_to_impl<T>();
980  }
986 
997  explicit mp_rational(const ::mpq_t q) : m_num(mpq_numref(q)), m_den(mpq_denref(q))
998  {
999  if (unlikely(m_den.sgn() == 0)) {
1000  piranha_throw(zero_division_error, "zero denominator");
1001  }
1002  }
1004 
1008  {
1009  return m_num;
1010  }
1012 
1016  {
1017  return m_den;
1018  }
1020 
1027  void _set_den(const int_type &den)
1028  {
1029  if (unlikely(den.sgn() <= 0)) {
1030  piranha_throw(std::invalid_argument, "cannot set non-positive denominator in rational");
1031  }
1032  m_den = den;
1033  }
1035 
1040  {
1041  return mp_rational{*this};
1042  }
1044 
1050  {
1051  return operator+=(1);
1052  }
1054 
1060  {
1061  const mp_rational retval(*this);
1062  ++(*this);
1063  return retval;
1064  }
1066 
1085  template <typename T>
1086  auto operator+=(const T &x) -> decltype(this->in_place_add(x))
1087  {
1088  return in_place_add(x);
1089  }
1091 
1105  template <typename T, generic_in_place_enabler<T> = 0>
1106  friend T &operator+=(T &x, const mp_rational &q)
1107  {
1108  return x = static_cast<T>(q + x);
1109  }
1111 
1134  template <typename T, typename U>
1135  friend auto operator+(const T &x, const U &y) -> decltype(mp_rational::binary_plus(x, y))
1136  {
1137  return mp_rational::binary_plus(x, y);
1138  }
1140  void negate()
1141  {
1142  m_num.neg();
1143  }
1145 
1149  {
1150  mp_rational retval(*this);
1151  retval.negate();
1152  return retval;
1153  }
1155 
1161  {
1162  return operator-=(1);
1163  }
1165 
1171  {
1172  const mp_rational retval(*this);
1173  --(*this);
1174  return retval;
1175  }
1177 
1196  template <typename T>
1197  auto operator-=(const T &x) -> decltype(this->in_place_sub(x))
1198  {
1199  return in_place_sub(x);
1200  }
1202 
1216  template <typename T, generic_in_place_enabler<T> = 0>
1217  friend T &operator-=(T &x, const mp_rational &q)
1218  {
1219  return x = static_cast<T>(x - q);
1220  }
1222 
1245  template <typename T, typename U>
1246  friend auto operator-(const T &x, const U &y) -> decltype(mp_rational::binary_minus(x, y))
1247  {
1248  return mp_rational::binary_minus(x, y);
1249  }
1251 
1270  template <typename T>
1271  auto operator*=(const T &x) -> decltype(this->in_place_mult(x))
1272  {
1273  return in_place_mult(x);
1274  }
1276 
1290  template <typename T, generic_in_place_enabler<T> = 0>
1291  friend T &operator*=(T &x, const mp_rational &q)
1292  {
1293  return x = static_cast<T>(x * q);
1294  }
1296 
1319  template <typename T, typename U>
1320  friend auto operator*(const T &x, const U &y) -> decltype(mp_rational::binary_mult(x, y))
1321  {
1322  return mp_rational::binary_mult(x, y);
1323  }
1325 
1345  template <typename T>
1346  auto operator/=(const T &x) -> decltype(this->in_place_div(x))
1347  {
1348  if (unlikely(math::is_zero(x))) {
1349  piranha_throw(zero_division_error, "division of a rational by zero");
1350  }
1351  return in_place_div(x);
1352  }
1354 
1368  template <typename T, generic_in_place_enabler<T> = 0>
1369  friend T &operator/=(T &x, const mp_rational &q)
1370  {
1371  return x = static_cast<T>(x / q);
1372  }
1374 
1398  template <typename T, typename U>
1399  friend auto operator/(const T &x, const U &y) -> decltype(mp_rational::binary_div(x, y))
1400  {
1401  return mp_rational::binary_div(x, y);
1402  }
1404 
1426  template <typename T, typename U>
1427  friend auto operator==(const T &x, const U &y) -> decltype(mp_rational::binary_eq(x, y))
1428  {
1429  return mp_rational::binary_eq(x, y);
1430  }
1432 
1452  template <typename T, typename U>
1453  friend auto operator!=(const T &x, const U &y) -> decltype(!mp_rational::binary_eq(x, y))
1454  {
1455  return !mp_rational::binary_eq(x, y);
1456  }
1458 
1480  template <typename T, typename U>
1481  friend auto operator<(const T &x, const U &y) -> decltype(mp_rational::binary_less_than(x, y))
1482  {
1483  return mp_rational::binary_less_than(x, y);
1484  }
1486 
1506  template <typename T, typename U>
1507  friend auto operator>=(const T &x, const U &y) -> decltype(!mp_rational::binary_less_than(x, y))
1508  {
1509  return !mp_rational::binary_less_than(x, y);
1510  }
1512 
1534  template <typename T, typename U>
1535  friend auto operator>(const T &x, const U &y) -> decltype(mp_rational::binary_greater_than(x, y))
1536  {
1537  return mp_rational::binary_greater_than(x, y);
1538  }
1540 
1560  template <typename T, typename U>
1561  friend auto operator<=(const T &x, const U &y) -> decltype(!mp_rational::binary_greater_than(x, y))
1562  {
1563  return !mp_rational::binary_greater_than(x, y);
1564  }
1566 
1582  template <typename T, pow_enabler<T> = 0>
1583  mp_rational pow(const T &exp) const
1584  {
1585  mp_rational retval;
1586  if (exp >= T(0)) {
1587  // For non-negative exponents, we can just raw-construct
1588  // a rational value.
1589  // NOTE: in case of exceptions here we are good, the worst that can happen
1590  // is that the numerator has some value and den is still 1 from the initialisation.
1591  retval.m_num = math::pow(num(), exp);
1592  retval.m_den = math::pow(den(), exp);
1593  } else {
1594  if (unlikely(math::is_zero(num()))) {
1595  piranha_throw(zero_division_error, "zero denominator in rational exponentiation");
1596  }
1597  // For negative exponents, invert.
1598  const int_type n_exp = -int_type(exp);
1599  // NOTE: exception safe here as well.
1600  retval.m_num = math::pow(den(), n_exp);
1601  retval.m_den = math::pow(num(), n_exp);
1602  if (retval.m_den.sgn() < 0) {
1603  math::negate(retval.m_num);
1604  math::negate(retval.m_den);
1605  }
1606  }
1607  return retval;
1608  }
1610 
1614  {
1615  mp_rational retval{*this};
1616  if (retval.m_num.sgn() < 0) {
1617  retval.m_num.neg();
1618  }
1619  return retval;
1620  }
1622 
1627  std::size_t hash() const
1628  {
1629  std::size_t retval = std::hash<int_type>()(m_num);
1630  boost::hash_combine(retval, std::hash<int_type>()(m_den));
1631  return retval;
1632  }
1633 
1634 private:
1635  // Generic binomial implementation.
1636  template <typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1637  static bool generic_binomial_check_k(const T &, const T &)
1638  {
1639  return false;
1640  }
1641  template <typename T, enable_if_t<!std::is_unsigned<T>::value, int> = 0>
1642  static bool generic_binomial_check_k(const T &k, const T &zero)
1643  {
1644  return k < zero;
1645  }
1646  // Generic binomial implementation using the falling factorial. U must be an integer
1647  // type, T can be anything that supports basic arithmetics. k must be non-negative.
1648  template <typename T, typename U>
1649  static T generic_binomial(const T &x, const U &k)
1650  {
1651  const U zero(0), one(1);
1652  if (generic_binomial_check_k(k, zero)) {
1653  piranha_throw(std::invalid_argument, "negative k value in binomial coefficient");
1654  }
1655  // Zero at bottom results always in 1.
1656  if (k == zero) {
1657  return T(1);
1658  }
1659  T tmp(x), retval = x / T(k);
1660  --tmp;
1661  for (auto i = static_cast<U>(k - one); i >= one; --i, --tmp) {
1662  retval *= tmp;
1663  retval /= T(i);
1664  }
1665  return retval;
1666  }
1667 
1668 public:
1670 
1683  template <typename T, enable_if_t<disjunction<std::is_integral<T>, std::is_same<T, int_type>>::value, int> = 0>
1684  mp_rational binomial(const T &n) const
1685  {
1686  if (m_den.is_one()) {
1687  // If this is an integer, offload to mp_integer::binomial().
1688  return mp_rational{math::binomial(m_num, n), 1};
1689  }
1690  if (n < T(0)) {
1691  // (rational negative-int) will always give zero.
1692  return mp_rational{};
1693  }
1694  // (rational non-negative-int) uses the generic implementation.
1695  // NOTE: this is going to be really slow, it can be improved by orders
1696  // of magnitude.
1697  return generic_binomial(*this, n);
1698  }
1699 
1700 #if defined(PIRANHA_WITH_MSGPACK)
1701 private:
1702  template <typename Stream>
1703  using msgpack_pack_enabler
1704  = enable_if_t<conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, int_type>>::value, int>;
1705  template <typename U>
1706  using msgpack_convert_enabler = enable_if_t<has_msgpack_convert<typename U::int_type>::value, int>;
1707 
1708 public:
1710 
1724  template <typename Stream, msgpack_pack_enabler<Stream> = 0>
1725  void msgpack_pack(msgpack::packer<Stream> &p, msgpack_format f) const
1726  {
1727  p.pack_array(2u);
1728  piranha::msgpack_pack(p, m_num, f);
1729  piranha::msgpack_pack(p, m_den, f);
1730  }
1732 
1749  template <typename U = mp_rational, msgpack_convert_enabler<U> = 0>
1750  void msgpack_convert(const msgpack::object &o, msgpack_format f)
1751  {
1752  std::array<msgpack::object, 2u> v;
1753  o.convert(v);
1754  int_type num, den;
1755  piranha::msgpack_convert(num, v[0], f);
1756  piranha::msgpack_convert(den, v[1], f);
1757  if (f == msgpack_format::binary) {
1758  m_num = std::move(num);
1759  m_den = std::move(den);
1760  } else {
1761  *this = mp_rational{std::move(num), std::move(den)};
1762  }
1763  }
1764 #endif
1765 
1766 private:
1767  int_type m_num;
1768  int_type m_den;
1769 };
1770 
1773 
1774 inline namespace literals
1775 {
1776 
1778 
1786 inline rational operator"" _q(const char *s)
1787 {
1788  return rational(s);
1789 }
1790 }
1791 
1792 inline namespace impl
1793 {
1794 
1795 // TMP structure to detect mp_rational types.
1796 template <typename>
1797 struct is_mp_rational : std::false_type {
1798 };
1799 
1800 template <std::size_t SSize>
1801 struct is_mp_rational<mp_rational<SSize>> : std::true_type {
1802 };
1803 
1804 // Detect if T and U are both mp_rational with same SSize.
1805 template <typename, typename>
1806 struct is_same_mp_rational : std::false_type {
1807 };
1808 
1809 template <std::size_t SSize>
1810 struct is_same_mp_rational<mp_rational<SSize>, mp_rational<SSize>> : std::true_type {
1811 };
1812 
1813 // Detect if type T is an interoperable type for the mp_rational type Rational.
1814 // NOTE: we need to split this in 2 as we might be using this in a context in which
1815 // Rational is not an mp_rational, in which case we cannot and don't need to check
1816 // against the inner int type.
1817 template <typename T, typename Rational>
1818 struct is_mp_rational_interoperable_type : mppp::mppp_impl::is_supported_interop<T> {
1819 };
1820 
1821 template <typename T, std::size_t SSize>
1822 struct is_mp_rational_interoperable_type<T, mp_rational<SSize>>
1823  : disjunction<mppp::mppp_impl::is_supported_interop<T>, std::is_same<T, typename mp_rational<SSize>::int_type>> {
1824 };
1825 }
1826 
1828 template <std::size_t SSize>
1831 
1837  void operator()(std::ostream &os, const mp_rational<SSize> &cf) const
1838  {
1839  if (math::is_zero(cf.num())) {
1840  os << "0";
1841  return;
1842  }
1843  if (cf.den().is_one()) {
1844  os << cf.num();
1845  return;
1846  }
1847  auto num = cf.num();
1848  if (num.sgn() < 0) {
1849  os << "-";
1850  num.neg();
1851  }
1852  os << "\\frac{" << num << "}{" << cf.den() << "}";
1853  }
1854 };
1855 
1856 namespace math
1857 {
1858 
1860 template <std::size_t SSize>
1861 struct is_zero_impl<mp_rational<SSize>> {
1863 
1868  bool operator()(const mp_rational<SSize> &q) const
1869  {
1870  return is_zero(q.num());
1871  }
1872 };
1873 
1875 template <std::size_t SSize>
1878 
1883  bool operator()(const mp_rational<SSize> &q) const
1884  {
1885  return is_unitary(q.num()) && is_unitary(q.den());
1886  }
1887 };
1888 
1890 template <std::size_t SSize>
1891 struct negate_impl<mp_rational<SSize>> {
1893 
1897  {
1898  q.negate();
1899  }
1900 };
1901 }
1902 
1903 inline namespace impl
1904 {
1905 
1906 // Enabler for the pow specialisation.
1907 template <typename T, typename U>
1908 using math_rational_pow_enabler
1909  = enable_if_t<disjunction<conjunction<is_mp_rational<T>, is_mp_rational_interoperable_type<U, T>>,
1910  conjunction<is_mp_rational<U>, is_mp_rational_interoperable_type<T, U>>,
1911  is_same_mp_rational<T, U>>::value>;
1912 }
1913 
1914 namespace math
1915 {
1916 
1918 
1933 template <typename T, typename U>
1934 struct pow_impl<T, U, math_rational_pow_enabler<T, U>> {
1935 private:
1936  template <std::size_t SSize, typename T2>
1937  static auto impl(const mp_rational<SSize> &b, const T2 &e) -> decltype(b.pow(e))
1938  {
1939  return b.pow(e);
1940  }
1941  template <std::size_t SSize, typename T2, enable_if_t<std::is_floating_point<T2>::value, int> = 0>
1942  static T2 impl(const mp_rational<SSize> &b, const T2 &e)
1943  {
1944  return math::pow(static_cast<T2>(b), e);
1945  }
1946  template <std::size_t SSize, typename T2, enable_if_t<std::is_floating_point<T2>::value, int> = 0>
1947  static T2 impl(const T2 &e, const mp_rational<SSize> &b)
1948  {
1949  return math::pow(e, static_cast<T2>(b));
1950  }
1951  template <std::size_t SSize>
1952  static mp_rational<SSize> impl(const mp_rational<SSize> &b, const mp_rational<SSize> &e)
1953  {
1954  // Special casing.
1955  if (is_unitary(b)) {
1956  return b;
1957  }
1958  if (is_zero(b)) {
1959  const auto sign = e.num().sgn();
1960  if (sign > 0) {
1961  // 0**q = 1
1962  return mp_rational<SSize>(0);
1963  }
1964  if (sign == 0) {
1965  // 0**0 = 1
1966  return mp_rational<SSize>(1);
1967  }
1968  // 0**-q -> division by zero.
1969  piranha_throw(zero_division_error, "unable to raise zero to a negative power");
1970  }
1971  if (!e.den().is_one()) {
1972  piranha_throw(std::invalid_argument,
1973  "unable to raise rational to a rational power whose denominator is not 1");
1974  }
1975  return b.pow(e.num());
1976  }
1977  template <std::size_t SSize, typename T2,
1978  enable_if_t<disjunction<std::is_integral<T2>, is_mp_integer<T2>>::value, int> = 0>
1979  static auto impl(const T2 &b, const mp_rational<SSize> &e) -> decltype(math::pow(b, e.num()))
1980  {
1981  using ret_t = decltype(math::pow(b, e.num()));
1982  if (is_unitary(b)) {
1983  return ret_t(b);
1984  }
1985  if (is_zero(b)) {
1986  const auto sign = e.num().sgn();
1987  if (sign > 0) {
1988  return ret_t(0);
1989  }
1990  if (sign == 0) {
1991  return ret_t(1);
1992  }
1993  piranha_throw(zero_division_error, "unable to raise zero to a negative power");
1994  }
1995  if (!e.den().is_one()) {
1996  piranha_throw(std::invalid_argument,
1997  "unable to raise an integral to a rational power whose denominator is not 1");
1998  }
1999  return math::pow(b, e.num());
2000  }
2001  using ret_type = decltype(impl(std::declval<const T &>(), std::declval<const U &>()));
2002 
2003 public:
2005 
2017  ret_type operator()(const T &b, const U &e) const
2018  {
2019  return impl(b, e);
2020  }
2021 };
2022 
2024 template <std::size_t SSize>
2025 struct sin_impl<mp_rational<SSize>> {
2027 
2035  {
2036  if (is_zero(q)) {
2037  return mp_rational<SSize>(0);
2038  }
2039  piranha_throw(std::invalid_argument, "cannot compute the sine of a non-zero rational");
2040  }
2041 };
2042 
2044 template <std::size_t SSize>
2045 struct cos_impl<mp_rational<SSize>> {
2047 
2055  {
2056  if (is_zero(q)) {
2057  return mp_rational<SSize>(1);
2058  }
2059  piranha_throw(std::invalid_argument, "cannot compute the cosine of a non-zero rational");
2060  }
2061 };
2062 
2064 template <std::size_t SSize>
2065 struct abs_impl<mp_rational<SSize>> {
2067 
2073  {
2074  return q.abs();
2075  }
2076 };
2077 
2079 template <std::size_t SSize>
2080 struct partial_impl<mp_rational<SSize>> {
2082 
2085  mp_rational<SSize> operator()(const mp_rational<SSize> &, const std::string &) const
2086  {
2087  return mp_rational<SSize>{};
2088  }
2089 };
2090 }
2091 
2092 inline namespace impl
2093 {
2094 
2095 // Binomial follows the same rules as pow.
2096 template <typename T, typename U>
2097 using math_rational_binomial_enabler = math_rational_pow_enabler<T, U>;
2098 }
2099 
2100 namespace math
2101 {
2102 
2104 
2118 template <typename T, typename U>
2119 struct binomial_impl<T, U, math_rational_binomial_enabler<T, U>> {
2120 private:
2121  template <std::size_t SSize, typename T2>
2122  static auto impl(const mp_rational<SSize> &x, const T2 &y) -> decltype(x.binomial(y))
2123  {
2124  return x.binomial(y);
2125  }
2126  template <std::size_t SSize, typename T2, enable_if_t<std::is_floating_point<T2>::value, int> = 0>
2127  static T2 impl(const mp_rational<SSize> &x, const T2 &y)
2128  {
2129  return math::binomial(static_cast<T2>(x), y);
2130  }
2131  template <std::size_t SSize, typename T2, enable_if_t<std::is_floating_point<T2>::value, int> = 0>
2132  static T2 impl(const T2 &x, const mp_rational<SSize> &y)
2133  {
2134  return math::binomial(x, static_cast<T2>(y));
2135  }
2136  template <std::size_t SSize>
2137  static double impl(const mp_rational<SSize> &x, const mp_rational<SSize> &y)
2138  {
2139  return math::binomial(static_cast<double>(x), static_cast<double>(y));
2140  }
2141  template <std::size_t SSize, typename T2,
2142  enable_if_t<disjunction<std::is_integral<T2>, is_mp_integer<T2>>::value, int> = 0>
2143  static double impl(const T2 &x, const mp_rational<SSize> &y)
2144  {
2145  return math::binomial(static_cast<double>(x), static_cast<double>(y));
2146  }
2147  using ret_type = decltype(impl(std::declval<const T &>(), std::declval<const U &>()));
2148 
2149 public:
2151 
2161  ret_type operator()(const T &x, const U &y) const
2162  {
2163  return impl(x, y);
2164  }
2165 };
2166 }
2167 
2168 inline namespace impl
2169 {
2170 
2171 template <typename To, typename From>
2172 using sc_rat_enabler = enable_if_t<
2173  disjunction<conjunction<is_mp_rational<To>, disjunction<std::is_arithmetic<From>, is_mp_integer<From>>>,
2174  conjunction<is_mp_rational<From>, disjunction<std::is_integral<To>, is_mp_integer<To>>>>::value>;
2175 }
2176 
2178 
2184 template <typename To, typename From>
2185 struct safe_cast_impl<To, From, sc_rat_enabler<To, From>> {
2186 private:
2187  template <typename T, enable_if_t<disjunction<std::is_arithmetic<T>, is_mp_integer<T>>::value, int> = 0>
2188  static To impl(const T &x)
2189  {
2190  try {
2191  // NOTE: checks for finiteness of an fp value are in the ctor.
2192  return To(x);
2193  } catch (const std::invalid_argument &) {
2194  piranha_throw(safe_cast_failure, "cannot convert value " + boost::lexical_cast<std::string>(x)
2195  + " of type '" + detail::demangle<T>()
2196  + "' to a rational, as the conversion would not preserve the value");
2197  }
2198  }
2199  template <typename T, enable_if_t<is_mp_rational<T>::value, int> = 0>
2200  static To impl(const T &q)
2201  {
2202  if (unlikely(!q.den().is_one())) {
2203  piranha_throw(safe_cast_failure, "cannot convert the rational value " + boost::lexical_cast<std::string>(q)
2204  + " to the integral type '" + detail::demangle<To>()
2205  + "', as the rational value as non-unitary denominator");
2206  }
2207  try {
2208  return static_cast<To>(q);
2209  } catch (const std::overflow_error &) {
2210  piranha_throw(safe_cast_failure, "cannot convert the rational value " + boost::lexical_cast<std::string>(q)
2211  + " to the integral type '" + detail::demangle<To>()
2212  + "', as the conversion cannot preserve the value");
2213  }
2214  }
2215 
2216 public:
2218 
2227  To operator()(const From &x) const
2228  {
2229  return impl(x);
2230  }
2231 };
2232 
2233 inline namespace impl
2234 {
2235 
2236 template <typename Archive, std::size_t SSize>
2237 using mp_rational_boost_save_enabler
2238  = enable_if_t<has_boost_save<Archive, typename mp_rational<SSize>::int_type>::value>;
2239 
2240 template <typename Archive, std::size_t SSize>
2241 using mp_rational_boost_load_enabler
2242  = enable_if_t<has_boost_load<Archive, typename mp_rational<SSize>::int_type>::value>;
2243 }
2244 
2246 
2255 template <typename Archive, std::size_t SSize>
2256 struct boost_save_impl<Archive, mp_rational<SSize>, mp_rational_boost_save_enabler<Archive, SSize>>
2257  : boost_save_via_boost_api<Archive, mp_rational<SSize>> {
2258 };
2259 
2261 
2272 template <typename Archive, std::size_t SSize>
2273 struct boost_load_impl<Archive, mp_rational<SSize>, mp_rational_boost_load_enabler<Archive, SSize>>
2274  : boost_load_via_boost_api<Archive, mp_rational<SSize>> {
2275 };
2276 
2277 #if defined(PIRANHA_WITH_MSGPACK)
2278 
2279 inline namespace impl
2280 {
2281 
2282 template <typename Stream, typename T>
2283 using mp_rational_msgpack_pack_enabler
2284  = enable_if_t<conjunction<is_mp_rational<T>, is_detected<msgpack_pack_member_t, Stream, T>>::value>;
2285 
2286 template <typename T>
2287 using mp_rational_msgpack_convert_enabler
2288  = enable_if_t<conjunction<is_mp_rational<T>, is_detected<msgpack_convert_member_t, T>>::value>;
2289 }
2290 
2292 
2297 template <typename Stream, typename T>
2298 struct msgpack_pack_impl<Stream, T, mp_rational_msgpack_pack_enabler<Stream, T>> {
2300 
2309  void operator()(msgpack::packer<Stream> &p, const T &q, msgpack_format f) const
2310  {
2311  q.msgpack_pack(p, f);
2312  }
2313 };
2314 
2316 
2321 template <typename T>
2322 struct msgpack_convert_impl<T, mp_rational_msgpack_convert_enabler<T>> {
2324 
2333  void operator()(T &q, const msgpack::object &o, msgpack_format f) const
2334  {
2335  q.msgpack_convert(o, f);
2336  }
2337 };
2338 
2339 #endif
2340 }
2341 
2342 namespace std
2343 {
2344 
2346 template <std::size_t SSize>
2347 struct hash<piranha::mp_rational<SSize>> {
2349  typedef size_t result_type;
2353 
2361  {
2362  return q.hash();
2363  }
2364 };
2365 }
2366 
2367 #endif
math_pow_t< T, U > pow(const T &x, const U &y)
Exponentiation.
Definition: pow.hpp:126
auto gcd(const T &a, const U &b) -> decltype(gcd_impl< T, U >()(a, b))
GCD.
Definition: math.hpp:2878
void operator()(msgpack::packer< Stream > &p, const T &q, msgpack_format f) const
Call operator.
friend auto operator<=(const T &x, const U &y) -> decltype(!mp_rational::binary_greater_than(x, y))
Generic less-than or equal operator involving piranha::mp_rational.
void boost_load(Archive &ar, T &x)
Load from Boost archive.
Definition: s11n.hpp:469
Default functor for the implementation of piranha::math::negate().
Definition: math.hpp:253
auto operator+=(const T &x) -> decltype(this->in_place_add(x))
In-place addition.
Multiprecision integer class.
Definition: mp++.hpp:869
void canonicalise()
Canonicalise.
mp_integer< SSize > int_type
The underlying piranha::mp_integer type used to represent numerator and denominator.
Definition: mp_rational.hpp:92
Default implementation of piranha::boost_load().
Definition: s11n.hpp:391
friend T & operator-=(T &x, const mp_rational &q)
Generic in-place subtraction with piranha::mp_rational.
void msgpack_convert(T &x, const msgpack::object &o, msgpack_format f)
Convert msgpack object.
Definition: s11n.hpp:957
bool is_canonical() const
Canonicality check.
friend auto operator>(const T &x, const U &y) -> decltype(mp_rational::binary_greater_than(x, y))
Generic greater-than operator involving piranha::mp_rational.
mp_rational< SSize > operator()(const mp_rational< SSize > &q) const
Call operator.
mp_rational< SSize > operator()(const mp_rational< SSize > &q) const
Call operator.
Default functor for the implementation of piranha::math::binomial().
Definition: binomial.hpp:140
friend auto operator+(const T &x, const U &y) -> decltype(mp_rational::binary_plus(x, y))
Generic binary addition involving piranha::mp_rational.
void msgpack_pack(msgpack::packer< Stream > &p, msgpack_format f) const
Pack in msgpack format.
Default functor for the implementation of piranha::math::is_zero().
Definition: math.hpp:69
Implementation of piranha::boost_load() via the Boost API.
Definition: s11n.hpp:363
bool operator()(const mp_rational< SSize > &q) const
Call operator.
friend T & operator+=(T &x, const mp_rational &q)
Generic in-place addition with piranha::mp_rational.
auto operator*=(const T &x) -> decltype(this->in_place_mult(x))
In-place multiplication.
mp_rational(const ::mpq_t q)
Constructor from mpq_t.
friend auto operator<(const T &x, const U &y) -> decltype(mp_rational::binary_less_than(x, y))
Generic less-than operator involving piranha::mp_rational.
Exceptions.
mp_rational< 1 > rational
Alias for piranha::mp_rational with 1 static limb.
mp_rational operator++(int)
Post-increment operator.
Default functor for the implementation of piranha::math::sin().
Definition: math.hpp:541
Default implementation of piranha::boost_save().
Definition: s11n.hpp:245
friend std::ostream & operator<<(std::ostream &os, const mp_rational &q)
Stream operator.
void negate(T &x)
In-place negation.
Definition: math.hpp:329
STL namespace.
friend auto operator>=(const T &x, const U &y) -> decltype(!mp_rational::binary_less_than(x, y))
Generic greater-than or equal operator involving piranha::mp_rational.
Default functor for the implementation of piranha::math::cos().
Definition: math.hpp:449
void msgpack_convert(const msgpack::object &o, msgpack_format f)
Convert from msgpack object.
ret_type operator()(const T &x, const U &y) const
Call operator.
mp_rational(const std::string &str)
Constructor from C++ string.
std::size_t hash() const
Hash value.
result_type operator()(const argument_type &q) const
Hash operator.
Default functor for the implementation of piranha::math::pow().
Definition: pow.hpp:52
mp_rational & operator=(const mp_rational &other)=default
Copy assignment operator.
void operator()(mp_rational< SSize > &q) const
Call operator.
mp_rational operator-() const
Negated copy.
mp_rational & operator=(const T &x)
Generic assignment operator.
ret_type operator()(const T &b, const U &e) const
Call operator.
mp_rational(mp_rational &&other) noexcept
Move constructor.
friend auto operator/(const T &x, const U &y) -> decltype(mp_rational::binary_div(x, y))
Generic binary division involving piranha::mp_rational.
void operator()(T &q, const msgpack::object &o, msgpack_format f) const
Call operator.
mp_rational binomial(const T &n) const
Binomial coefficient.
friend auto operator==(const T &x, const U &y) -> decltype(mp_rational::binary_eq(x, y))
Generic equality operator involving piranha::mp_rational.
Default functor for the implementation of piranha::msgpack_convert().
Definition: s11n.hpp:867
#define piranha_throw(exception_type,...)
Exception-throwing macro.
Definition: exceptions.hpp:118
Default functor for the implementation of piranha::msgpack_pack().
Definition: s11n.hpp:686
int_type & _den()
Mutable reference to the denominator.
Exception for signalling division by zero.
Definition: exceptions.hpp:136
msgpack_format
Serialization format for msgpack.
Definition: s11n.hpp:673
friend T & operator*=(T &x, const mp_rational &q)
Generic in-place multiplication with piranha::mp_rational.
mp_integer & neg()
Negate in-place.
Definition: mp++.hpp:1662
mp_rational pow(const T &exp) const
Exponentiation.
mp_rational & operator=(const char *str)
Assignment operator from C string.
Default functor for the implementation of piranha::math::partial().
Definition: math.hpp:633
Multiple precision rational class.
~mp_rational()
Destructor.
Default implementation of piranha::safe_cast().
Definition: safe_cast.hpp:63
mp_rational(const char *str)
Constructor from C string.
mp_rational()
Default constructor.
mp_rational operator+() const
Identity operator.
friend std::istream & operator>>(std::istream &is, mp_rational &q)
Overload input stream operator for piranha::mp_rational.
Default functor for the implementation of piranha::convert_to().
Definition: convert_to.hpp:43
bool is_zero(const T &x)
Zero test.
Definition: math.hpp:133
mp_rational< SSize > operator()(const mp_rational< SSize > &, const std::string &) const
Call operator.
void operator()(std::ostream &os, const mp_rational< SSize > &cf) const
Call operator.
mp_rational & operator=(const std::string &str)
Assignment operator from C++ string.
Implementation of piranha::boost_save() via the Boost API.
Definition: s11n.hpp:217
mp_rational(const I0 &n, const I1 &d)
Constructor from numerator/denominator pair.
math_binomial_type< T, U > binomial(const T &x, const U &y)
Generalised binomial coefficient.
Definition: binomial.hpp:215
mp_rational abs() const
Absolute value.
const int_type & den() const
Get const reference to the denominator.
mp_rational & operator++()
Pre-increment operator.
Root piranha namespace.
Definition: array_key.hpp:52
void msgpack_pack(msgpack::packer< Stream > &packer, const T &x, msgpack_format f)
Pack generic object in a msgpack stream.
Definition: s11n.hpp:856
bool is_one() const
Test if the value is equal to one.
Definition: mp++.hpp:5137
Detect the presence of piranha::msgpack_pack().
Definition: s11n.hpp:607
void _set_den(const int_type &den)
Set denominator.
friend auto operator-(const T &x, const U &y) -> decltype(mp_rational::binary_minus(x, y))
Generic binary subtraction involving piranha::mp_rational.
Type traits.
bool operator()(const mp_rational< SSize > &q) const
Call operator.
void multiply_accumulate(T &x, const T &y, const T &z)
Multiply-accumulate.
Definition: math.hpp:438
auto operator-=(const T &x) -> decltype(this->in_place_sub(x))
In-place subtraction.
friend auto operator*(const T &x, const U &y) -> decltype(mp_rational::binary_mult(x, y))
Generic binary multiplication involving piranha::mp_rational.
int_type & _num()
Mutable reference to the numerator.
mp_rational< SSize > operator()(const mp_rational< SSize > &q) const
Call operator.
piranha::mp_rational< SSize > argument_type
Argument type.
mp_rational operator--(int)
Post-decrement operator.
mp_rational(const T &x)
Generic constructor.
void boost_save(Archive &ar, const T &x)
Save to Boost archive.
Definition: s11n.hpp:328
void negate()
Negate in-place.
Default functor for piranha::print_tex_coefficient().
int sgn() const
Sign.
Definition: mp++.hpp:1611
mpq_view get_mpq_view() const
Get an mpq view of this.
Default functor for the implementation of piranha::math::abs().
Definition: math.hpp:947
friend T & operator/=(T &x, const mp_rational &q)
Generic in-place division with piranha::mp_rational.
auto operator/=(const T &x) -> decltype(this->in_place_div(x))
In-place division.
mp_rational & operator=(mp_rational &&other) noexcept
Move assignment operator.
const int_type & num() const
Get const reference to the numerator.
Exception to signal failure in piranha::safe_cast().
Definition: safe_cast.hpp:53
Default functor for the implementation of piranha::math::is_unitary().
Definition: math.hpp:179
bool is_unitary(const T &x)
Unitary test.
Definition: math.hpp:242
mp_rational & operator--()
Pre-decrement operator.
friend auto operator!=(const T &x, const U &y) -> decltype(!mp_rational::binary_eq(x, y))
Generic inequality operator involving piranha::mp_rational.