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_RATIONAL_FUNCTION_HPP
30 #define PIRANHA_RATIONAL_FUNCTION_HPP
31 
32 #include <algorithm>
33 #include <array>
34 #include <cstddef>
35 #include <functional>
36 #include <initializer_list>
37 #include <iostream>
38 #include <mutex>
39 #include <stdexcept>
40 #include <string>
41 #include <tuple>
42 #include <type_traits>
43 #include <unordered_map>
44 #include <utility>
45 #include <vector>
46 
47 #include "config.hpp"
48 #include "exceptions.hpp"
49 #include "is_cf.hpp"
50 #include "math.hpp"
51 #include "mp_integer.hpp"
52 #include "mp_rational.hpp"
53 #include "polynomial.hpp"
54 #include "pow.hpp"
55 #include "print_tex_coefficient.hpp"
56 #include "s11n.hpp"
57 #include "series.hpp"
58 #include "type_traits.hpp"
59 
60 namespace piranha
61 {
62 
63 namespace detail
64 {
65 
66 struct rational_function_tag {
67 };
68 }
69 
70 /// Rational function.
71 /**
72  * This class represents rational functions, that is, mathematical objects of the form
73  * \f[
74  * \frac{f\left(x_0,x_1,\ldots\right)}{g\left(x_0,x_1,\ldots\right)},
75  * \f]
76  * where \f$f\left(x_0,x_1,\ldots\right)\f$ and \f$g\left(x_0,x_1,\ldots\right)\f$ are polynomials in the variables
77  * \f$x_0,x_1,\ldots\f$ over \f$\mathbb{Z}\f$.
78  * The monomial representation is determined by the \p Key template parameter. Only monomial types with integral
79  * exponents are allowed; signed exponent
80  * types are allowed, but if a negative exponent is ever generated or encountered while operating on a rational function
81  * an error will be produced.
82  *
83  * Internally, a piranha::rational_function consists of a numerator and a denominator represented as piranha::polynomial
84  * with piranha::integer coefficients.
85  * Rational functions are always kept in a canonical form defined by the following properties:
86  * - numerator and denominator are coprime,
87  * - zero is always represented as <tt>0 / 1</tt>,
88  * - the denominator is never zero and its leading term is always positive.
89  *
90  * This class satisfies the piranha::is_cf type trait.
91  *
92  * ## Interoperability with other types ##
93  *
94  * Instances of piranha::rational_function can interoperate with:
95  * - piranha::integer,
96  * - piranha::rational,
97  * - the polynomial type representing numerator and denominator (that is, piranha::rational_function::p_type) and its
98  *   counterpart with rational coefficients (that is, piranha::rational_function::q_type).
99  *
100  * ## Type requirements ##
101  *
102  * \p Key must be usable as second template parameter for piranha::polynomial, and the exponent type must be a C++
103  * integral type or piranha::integer.
104  *
105  * ## Exception safety guarantee ##
106  *
107  * Unless noted otherwise, this class provides the strong exception safety guarantee.
108  *
109  * ## Move semantics ##
110  *
111  * Move operations will leave objects of this class in a state which is destructible and assignable.
112  */
113 template <typename Key>
114 class rational_function : public detail::rational_function_tag
115 {
116     // Make friend with the math::partial() specialisation functor.
117     template <typename, typename>
118     friend struct math::partial_impl;
119     // Shortcut for supported integral type.
120     template <typename T>
121     using is_integral = std::integral_constant<bool, std::is_same<integer, T>::value || std::is_integral<T>::value>;
122     PIRANHA_TT_CHECK(detail::is_polynomial_key, Key);
123     static_assert(is_integral<typename Key::value_type>::value, "The exponent type must be an integral type.");
124     // Shortcut from C++14.
125     template <typename T>
126     using decay_t = typename std::decay<T>::type;
127 
128 public:
129     /// The polynomial type of numerator and denominator.
130     using p_type = polynomial<integer, Key>;
131     /// The counterpart of rational_function::p_type with rational coefficients.
132     using q_type = polynomial<rational, Key>;
133 
134 private:
135     // Detect types interoperable for arithmetics.
136     template <typename T>
137     using is_interoperable
138         = std::integral_constant<bool, is_integral<T>::value || std::is_same<T, rational>::value
139                                            || std::is_same<T, p_type>::value || std::is_same<T, q_type>::value>;
140     // Canonicalisation.
canonicalise_impl(const p_type & n,const p_type & d)141     static std::pair<p_type, p_type> canonicalise_impl(const p_type &n, const p_type &d)
142     {
143         // First let's check for negative exponents.
144         detail::poly_expo_checker(n);
145         detail::poly_expo_checker(d);
146         // Handle a zero divisor.
147         if (unlikely(math::is_zero(d))) {
148             piranha_throw(zero_division_error, "null denominator in rational function");
149         }
150         // If the numerator is null, return {0,1}.
151         if (math::is_zero(n)) {
152             return std::make_pair(p_type{}, p_type{1});
153         }
154         // NOTE: maybe these checks should go directly into the poly GCD routine. Keep it
155         // in mind for the future.
156         // NOTE: it would make sense here to deal with the single coefficient denominator case as well.
157         // This should give good performance when one is using rational_function as a rational polynomial,
158         // no need to go through a costly canonicalisation.
159         // If the denominator is unitary, just return the input values.
160         if (math::is_unitary(d)) {
161             return std::make_pair(n, d);
162         }
163         // Handle single-coefficient polys.
164         if (n.is_single_coefficient() && d.is_single_coefficient()) {
165             piranha_assert(n.size() == 1u && d.size() == 1u);
166             // Use a rational to construct the canonical form.
167             rational tmp{n._container().begin()->m_cf, d._container().begin()->m_cf};
168             return std::make_pair(p_type{tmp.num()}, p_type{tmp.den()});
169         }
170         // Compute the GCD and create the return values.
171         auto tup_res = p_type::gcd(n, d, true);
172         auto retval = std::make_pair(std::move(std::get<1u>(tup_res)), std::move(std::get<2u>(tup_res)));
173         // Check if we need to adjust the sign of the leading term
174         // of the denominator.
175         // NOTE: here we ask for the lterm into something that potentially might be zero
176         // if truncation is active. Keep this in mind.
177         if (detail::poly_lterm(retval.second)->m_cf.sign() < 0) {
178             math::negate(retval.first);
179             math::negate(retval.second);
180         }
181         return retval;
182     }
183     // Utility function to convert a q_type to a pair (p_type,integer), representing the numerator
184     // and denominator of a rational function.
q_to_p_type(const q_type & q)185     static std::pair<p_type, integer> q_to_p_type(const q_type &q)
186     {
187         // Init the numerator.
188         p_type ret_p;
189         ret_p._container().rehash(q._container().bucket_count());
190         ret_p.set_symbol_set(q.get_symbol_set());
191         // Compute the least common multiplier.
192         integer lcm{1};
193         // The GCD.
194         integer g;
195         for (const auto &t : q._container()) {
196             math::gcd3(g, lcm, t.m_cf.den());
197             math::mul3(lcm, lcm, t.m_cf.den());
198             integer::_divexact(lcm, lcm, g);
199         }
200         // All these computations involve only positive numbers,
201         // the GCD must always be positive.
202         piranha_assert(lcm.sign() == 1);
203         // Fill in the numerator.
204         for (const auto &t : q._container()) {
205             using term_type = typename p_type::term_type;
206             // NOTE: possibility of unique insertion here.
207             // NOTE: possibility of exact division.
208             ret_p.insert(term_type{lcm / t.m_cf.den() * t.m_cf.num(), t.m_key});
209         }
210         return std::make_pair(std::move(ret_p), std::move(lcm));
211     }
212     // Generic ctors utils.
213     // Enabler for the constructor from p_type, integral and string.
214     template <typename T>
215     using pzs_enabler
216         = std::integral_constant<bool, std::is_same<decay_t<T>, p_type>::value || is_integral<decay_t<T>>::value
217                                            || std::is_same<decay_t<T>, std::string>::value
218                                            || std::is_same<decay_t<T>, char *>::value
219                                            || std::is_same<decay_t<T>, const char *>::value>;
220     // Ctor from p_type, integrals and string.
221     template <typename T, typename std::enable_if<pzs_enabler<T>::value, int>::type = 0>
dispatch_unary_ctor(T && x)222     void dispatch_unary_ctor(T &&x)
223     {
224         m_num = p_type{std::forward<T>(x)};
225         m_den = p_type{1};
226         // Check for negative expos in the num if cting from a polynomial.
227         if (std::is_same<decay_t<T>, p_type>::value) {
228             detail::poly_expo_checker(m_num);
229         }
230     }
231     // Ctor from rationals.
dispatch_unary_ctor(const rational & q)232     void dispatch_unary_ctor(const rational &q)
233     {
234         // NOTE: here we are assuming that q is canonicalised, as it should be.
235         m_num = q.num();
236         m_den = q.den();
237     }
238     // Ctor from q_type.
dispatch_unary_ctor(const q_type & q)239     void dispatch_unary_ctor(const q_type &q)
240     {
241         auto res = q_to_p_type(q);
242         m_num = std::move(res.first);
243         m_den = std::move(res.second);
244         // Check the numerator's exponents.
245         detail::poly_expo_checker(m_num);
246     }
247     // Enabler for generic unary ctor.
248     template <typename T, typename U>
249     using unary_ctor_enabler = typename std::enable_if<
250         // NOTE: this should disable the ctor if T derives from rational_function,
251         // as the dispatcher does not support that.
252         detail::true_tt<decltype(std::declval<U &>().dispatch_unary_ctor(std::declval<const decay_t<T> &>()))>::value,
253         int>::type;
254     // Binary ctor enabler.
255     template <typename T, typename U, typename V>
256     using binary_ctor_enabler =
257         typename std::enable_if<std::is_constructible<V, T &&>::value && std::is_constructible<V, U &&>::value,
258                                 int>::type;
259     // Enabler for generic assignment.
260     // Here we are re-using the enabler for generic unary construction. This should make sure that
261     // the generic assignment operator is never used when T is rational_function.
262     template <typename T, typename U>
263     using generic_assignment_enabler = unary_ctor_enabler<T, U>;
264     // Addition.
dispatch_binary_add(const rational_function & a,const rational_function & b)265     static rational_function dispatch_binary_add(const rational_function &a, const rational_function &b)
266     {
267         // Detect unitary denominators.
268         const bool uda = math::is_unitary(a.den()), udb = math::is_unitary(b.den());
269         rational_function retval;
270         if (uda && udb) {
271             // With unitary denominators, just add the numerators and set den to 1.
272             retval.m_num = a.m_num + b.m_num;
273             retval.m_den = 1;
274         } else if (uda) {
275             // Only a is unitary.
276             retval.m_num = a.m_num * b.m_den + b.m_num;
277             retval.m_den = b.m_den;
278         } else if (udb) {
279             // Only b is unitary.
280             retval.m_num = a.m_num + b.m_num * a.m_den;
281             retval.m_den = a.m_den;
282         } else {
283             // The general case.
284             retval.m_num = a.m_num * b.m_den + a.m_den * b.m_num;
285             retval.m_den = a.m_den * b.m_den;
286             // NOTE: if the canonicalisation fails for some reason, it is not a problem
287             // as retval is a local variable that will just be destroyed. The destructor
288             // does not run any check.
289             retval.canonicalise();
290         }
291         return retval;
292     }
293     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_binary_add(const rational_function & a,const T & b)294     static rational_function dispatch_binary_add(const rational_function &a, const T &b)
295     {
296         return a + rational_function{b};
297     }
298     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_binary_add(const T & a,const rational_function & b)299     static rational_function dispatch_binary_add(const T &a, const rational_function &b)
300     {
301         return rational_function{a} + b;
302     }
303     template <typename T, typename U>
304     using binary_add_enabler = typename std::
305         enable_if<detail::true_tt<
306                       // NOTE: here the rational_function:: specifier is apprently needed only in GCC, probably a bug.
307                       decltype(rational_function::dispatch_binary_add(std::declval<const decay_t<T> &>(),
308                                                                       std::declval<const decay_t<U> &>()))>::value,
309                   int>::type;
310     template <typename T, typename U>
311     using in_place_add_enabler =
312         typename std::enable_if<detail::true_tt<decltype(std::declval<const decay_t<T> &>()
313                                                          + std::declval<const decay_t<U> &>())>::value,
314                                 int>::type;
315     // Subtraction.
dispatch_binary_sub(const rational_function & a,const rational_function & b)316     static rational_function dispatch_binary_sub(const rational_function &a, const rational_function &b)
317     {
318         const bool uda = math::is_unitary(a.den()), udb = math::is_unitary(b.den());
319         rational_function retval;
320         if (uda && udb) {
321             retval.m_num = a.m_num - b.m_num;
322             retval.m_den = 1;
323         } else if (uda) {
324             retval.m_num = a.m_num * b.m_den - b.m_num;
325             retval.m_den = b.m_den;
326         } else if (udb) {
327             retval.m_num = a.m_num - b.m_num * a.m_den;
328             retval.m_den = a.m_den;
329         } else {
330             retval.m_num = a.m_num * b.m_den - a.m_den * b.m_num;
331             retval.m_den = a.m_den * b.m_den;
332             retval.canonicalise();
333         }
334         return retval;
335     }
336     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_binary_sub(const rational_function & a,const T & b)337     static rational_function dispatch_binary_sub(const rational_function &a, const T &b)
338     {
339         return a - rational_function{b};
340     }
341     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_binary_sub(const T & a,const rational_function & b)342     static rational_function dispatch_binary_sub(const T &a, const rational_function &b)
343     {
344         return rational_function{a} - b;
345     }
346     template <typename T, typename U>
347     using binary_sub_enabler =
348         typename std::enable_if<detail::true_tt<decltype(rational_function::dispatch_binary_sub(
349                                     std::declval<const decay_t<T> &>(), std::declval<const decay_t<U> &>()))>::value,
350                                 int>::type;
351     template <typename T, typename U>
352     using in_place_sub_enabler =
353         typename std::enable_if<detail::true_tt<decltype(std::declval<const decay_t<T> &>()
354                                                          - std::declval<const decay_t<U> &>())>::value,
355                                 int>::type;
356     // Multiplication.
dispatch_binary_mul(const rational_function & a,const rational_function & b)357     static rational_function dispatch_binary_mul(const rational_function &a, const rational_function &b)
358     {
359         const bool uda = math::is_unitary(a.den()), udb = math::is_unitary(b.den());
360         rational_function retval;
361         if (uda && udb) {
362             retval.m_num = a.m_num * b.m_num;
363             retval.m_den = 1;
364         } else {
365             retval.m_num = a.m_num * b.m_num;
366             retval.m_den = a.m_den * b.m_den;
367             retval.canonicalise();
368         }
369         return retval;
370     }
371     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_binary_mul(const rational_function & a,const T & b)372     static rational_function dispatch_binary_mul(const rational_function &a, const T &b)
373     {
374         return a * rational_function{b};
375     }
376     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_binary_mul(const T & a,const rational_function & b)377     static rational_function dispatch_binary_mul(const T &a, const rational_function &b)
378     {
379         return rational_function{a} * b;
380     }
381     template <typename T, typename U>
382     using binary_mul_enabler =
383         typename std::enable_if<detail::true_tt<decltype(rational_function::dispatch_binary_mul(
384                                     std::declval<const decay_t<T> &>(), std::declval<const decay_t<U> &>()))>::value,
385                                 int>::type;
386     template <typename T, typename U>
387     using in_place_mul_enabler =
388         typename std::enable_if<detail::true_tt<decltype(std::declval<const decay_t<T> &>()
389                                                          * std::declval<const decay_t<U> &>())>::value,
390                                 int>::type;
391     // Division.
dispatch_binary_div(const rational_function & a,const rational_function & b)392     static rational_function dispatch_binary_div(const rational_function &a, const rational_function &b)
393     {
394         // NOTE: division is like a multiplication with inverted second argument, so we need
395         // to check the num of b.
396         const bool uda = math::is_unitary(a.den()), udb = math::is_unitary(b.num());
397         rational_function retval;
398         if (uda && udb) {
399             retval.m_num = a.m_num * b.m_den;
400             retval.m_den = 1;
401         } else {
402             retval.m_num = a.m_num * b.m_den;
403             retval.m_den = a.m_den * b.m_num;
404             retval.canonicalise();
405         }
406         return retval;
407     }
408     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_binary_div(const rational_function & a,const T & b)409     static rational_function dispatch_binary_div(const rational_function &a, const T &b)
410     {
411         return a / rational_function{b};
412     }
413     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_binary_div(const T & a,const rational_function & b)414     static rational_function dispatch_binary_div(const T &a, const rational_function &b)
415     {
416         return rational_function{a} / b;
417     }
418     template <typename T, typename U>
419     using binary_div_enabler =
420         typename std::enable_if<detail::true_tt<decltype(rational_function::dispatch_binary_div(
421                                     std::declval<const decay_t<T> &>(), std::declval<const decay_t<U> &>()))>::value,
422                                 int>::type;
423     template <typename T, typename U>
424     using in_place_div_enabler =
425         typename std::enable_if<detail::true_tt<decltype(std::declval<const decay_t<T> &>()
426                                                          / std::declval<const decay_t<U> &>())>::value,
427                                 int>::type;
428     // Comparison.
dispatch_equality(const rational_function & a,const rational_function & b)429     static bool dispatch_equality(const rational_function &a, const rational_function &b)
430     {
431         return a.m_num == b.m_num && a.m_den == b.m_den;
432     }
433     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_equality(const rational_function & a,const T & b)434     static bool dispatch_equality(const rational_function &a, const T &b)
435     {
436         return a == rational_function{b};
437     }
438     template <typename T, typename std::enable_if<is_interoperable<T>::value, int>::type = 0>
dispatch_equality(const T & a,const rational_function & b)439     static bool dispatch_equality(const T &a, const rational_function &b)
440     {
441         return b == rational_function{a};
442     }
443     template <typename T, typename U>
444     using eq_enabler = typename std::enable_if<detail::true_tt<decltype(rational_function::dispatch_equality(
445                                                    std::declval<const T &>(), std::declval<const U &>()))>::value,
446                                                int>::type;
447     // subs().
448     template <typename T>
449     using has_special_subs
450         = std::integral_constant<bool, is_interoperable<T>::value || std::is_same<T, rational_function>::value>;
451     // Type resulting from the normal substitution method.
452     template <typename T>
453     using normal_subs_type
454         = decltype(math::subs(std::declval<const p_type &>(), std::string{}, std::declval<const T &>())
455                    / math::subs(std::declval<const p_type &>(), std::string{}, std::declval<const T &>()));
456     template <typename T>
457     using has_normal_subs
458         = std::integral_constant<bool, !has_special_subs<T>::value && is_returnable<normal_subs_type<T>>::value>;
459     template <typename T, typename std::enable_if<has_special_subs<T>::value, int>::type = 0>
subs_impl(const rational_function & r,const std::string & name,const T & x)460     static rational_function subs_impl(const rational_function &r, const std::string &name, const T &x)
461     {
462         return rational_function{math::subs(r.num(), name, x), math::subs(r.den(), name, x)};
463     }
464     template <typename T, typename std::enable_if<has_normal_subs<T>::value, int>::type = 0>
subs_impl(const rational_function & r,const std::string & name,const T & x)465     static normal_subs_type<T> subs_impl(const rational_function &r, const std::string &name, const T &x)
466     {
467         return math::subs(r.num(), name, x) / math::subs(r.den(), name, x);
468     }
469     template <typename T>
470     using subs_type
471         = decltype(subs_impl(std::declval<const rational_function &>(), std::string{}, std::declval<const T &>()));
472     // ipow_subs().
473     template <typename T>
474     using has_special_ipow_subs = has_special_subs<T>;
475     // Type resulting from the normal substitution method.
476     template <typename T>
477     using normal_ipow_subs_type = decltype(
478         math::ipow_subs(std::declval<const p_type &>(), std::string{}, integer{}, std::declval<const T &>())
479         / math::ipow_subs(std::declval<const p_type &>(), std::string{}, integer{}, std::declval<const T &>()));
480     template <typename T>
481     using has_normal_ipow_subs = std::integral_constant<bool, !has_special_ipow_subs<T>::value
482                                                                   && is_returnable<normal_ipow_subs_type<T>>::value>;
483     template <typename T, typename std::enable_if<has_special_ipow_subs<T>::value, int>::type = 0>
ipow_subs_impl(const rational_function & r,const std::string & name,const integer & n,const T & x)484     static rational_function ipow_subs_impl(const rational_function &r, const std::string &name, const integer &n,
485                                             const T &x)
486     {
487         return rational_function{math::ipow_subs(r.num(), name, n, x), math::ipow_subs(r.den(), name, n, x)};
488     }
489     template <typename T, typename std::enable_if<has_normal_ipow_subs<T>::value, int>::type = 0>
ipow_subs_impl(const rational_function & r,const std::string & name,const integer & n,const T & x)490     static normal_ipow_subs_type<T> ipow_subs_impl(const rational_function &r, const std::string &name,
491                                                    const integer &n, const T &x)
492     {
493         return math::ipow_subs(r.num(), name, n, x) / math::ipow_subs(r.den(), name, n, x);
494     }
495     template <typename T>
496     using ipow_subs_type = decltype(
497         ipow_subs_impl(std::declval<const rational_function &>(), std::string{}, integer{}, std::declval<const T &>()));
498     // Serialization support.
499     friend class boost::serialization::access;
500     template <class Archive>
save(Archive & ar,unsigned) const501     void save(Archive &ar, unsigned) const
502     {
503         boost_save(ar, num());
504         boost_save(ar, den());
505     }
506     template <class Archive>
load(Archive & ar,unsigned)507     void load(Archive &ar, unsigned)
508     {
509         p_type n, d;
510         boost_load(ar, n);
511         boost_load(ar, d);
512         if (std::is_same<Archive, boost::archive::binary_iarchive>::value) {
513             _num() = std::move(n);
514             _den() = std::move(d);
515         } else {
516             *this = rational_function{std::move(n), std::move(d)};
517         }
518     }
519     BOOST_SERIALIZATION_SPLIT_MEMBER()
520     // Hashing utils.
521     struct rf_hasher {
522         template <typename T>
operator ()piranha::rational_function::rf_hasher523         std::size_t operator()(const T &s) const
524         {
525             return s.hash();
526         }
527     };
528     struct rf_equal_to {
529         template <typename T>
operator ()piranha::rational_function::rf_equal_to530         bool operator()(const T &a, const T &b) const
531         {
532             return a.is_identical(b);
533         }
534     };
535     // Pow machinery, with cache.
536     template <typename T>
537     using pow_enabler = typename std::enable_if<is_integral<T>::value, int>::type;
538     template <typename RF>
539     using pow_map_type = std::unordered_map<RF, std::vector<RF>, rf_hasher, rf_equal_to>;
540     template <typename RF = rational_function>
get_pow_cache()541     static pow_map_type<RF> &get_pow_cache()
542     {
543         static pow_map_type<RF> s_pow_cache;
544         return s_pow_cache;
545     }
546     // The mutex for access to the pow cache.
547     static std::mutex s_pow_mutex;
548     // Custom derivatives boilerplate.
549     template <typename T>
550     using cp_map_type = std::unordered_map<std::string, std::function<T(const T &)>>;
551     template <typename T = rational_function>
get_cp_map()552     static cp_map_type<T> &get_cp_map()
553     {
554         static cp_map_type<T> cp_map;
555         return cp_map;
556     }
557     template <typename F>
558     using custom_partial_enabler = typename std::
559         enable_if<std::is_constructible<std::function<rational_function(const rational_function &)>, F>::value,
560                   int>::type;
561     // The mutex for access to the custom derivatives.
562     static std::mutex s_cp_mutex;
563 
564 public:
565     /// Default constructor.
566     /**
567      * The numerator will be set to zero, the denominator to 1.
568      *
569      * @throws unspecified any exception thrown by the constructor of rational_function::p_type from \p int.
570      */
rational_function()571     rational_function() : m_num(), m_den(1)
572     {
573     }
574     /// Defaulted copy constructor.
575     rational_function(const rational_function &) = default;
576     /// Defaulted move constructor.
577     rational_function(rational_function &&) = default;
578     /// Generic unary constructor.
579     /**
580      * \note
581      * This constructor is enabled if the decay type of \p T is either an interoperable type or a string type.
582      *
583      * The constructor operates as follows:
584      * - if \p T is rational_function::p_type, a C++ integral type, piranha::integer or a string type, then the
585      * numerator
586      *   is constructed from \p x, and the denominator is set to 1;
587      * - if \p T is piranha::rational, then the numerator is constructed from the numerator of \p x, and the denominator
588      *   from the denominator of \p x;
589      * - if \p T is rational_function::q_type, then, after construction, \p this will be mathematically equivalent to \p
590      * x (that is, the coefficients
591      *   of \p x are multiplied by their LCM and the result is used to construct the numerator, and the denominator is
592      * constructed from the LCM
593      *   of the coefficients).
594      *
595      * @param x construction argument.
596      *
597      * @throws std::invalid_argument if, when constructing from rational_function::p_type or rational_function::q_type,
598      * a negative exponent is
599      * encountered.
600      * @throws unspecified any exception thrown by:
601      * - the constructors and the assignment operators of rational_function::p_type,
602      * - the constructors of the term type of rational_function::p_type,
603      * - the public interface of piranha::series and piranha::hash_set.
604      */
605     template <typename T, typename U = rational_function, unary_ctor_enabler<T, U> = 0>
rational_function(T && x)606     explicit rational_function(T &&x)
607     {
608         dispatch_unary_ctor(std::forward<T>(x));
609     }
610     /// Generic binary constructor.
611     /**
612      * \note
613      * This constructor is enabled only if piranha::rational_function can be constructed from \p T and \p U.
614      *
615      * The body of this constructor is equivalent to:
616      * @code
617      * *this = rational_function(std::forward<T>(x)) / rational_function(std::forward<U>(y));
618      * @endcode
619      *
620      * @param x first argument.
621      * @param y second argument.
622      *
623      * @throws unspecified any exception thrown by:
624      * - the invoked unary constructors,
625      * - the division operator of piranha::rational_function.
626      */
627     template <typename T, typename U, typename V = rational_function, binary_ctor_enabler<T, U, V> = 0>
rational_function(T && x,U && y)628     explicit rational_function(T &&x, U &&y)
629     {
630         *this = rational_function(std::forward<T>(x)) / rational_function(std::forward<U>(y));
631     }
632     /// Copy-assignment operator.
633     /**
634      * @param other the assignment argument.
635      *
636      * @return a reference to \p this.
637      *
638      * @throws unspecified any exception thrown by the copy constructor.
639      */
operator =(const rational_function & other)640     rational_function &operator=(const rational_function &other)
641     {
642         if (likely(&other != this)) {
643             *this = rational_function(other);
644         }
645         return *this;
646     }
647     /// Move-assignment operator.
648     /**
649      * @param other the assignment argument.
650      *
651      * @return a reference to \p this.
652      */
653     rational_function &operator=(rational_function &&other) = default;
654     /// Generic assignment operator.
655     /**
656      * \note
657      * This operator is enabled only if the corresponding generic unary constructor is enabled.
658      *
659      * The operator is implemented as the assignment from a piranha::rational_function constructed
660      * from \p x.
661      *
662      * @param x assignment argument.
663      *
664      * @return a reference to \p this.
665      *
666      * @throws unspecified any exception thrown by the invoked constructor.
667      */
668     template <typename T, typename U = rational_function, generic_assignment_enabler<T, U> = 0>
operator =(T && x)669     rational_function &operator=(T &&x)
670     {
671         // Check this is never invoked when T is a rational function.
672         static_assert(!std::is_base_of<rational_function, decay_t<T>>::value, "Type error.");
673         return *this = rational_function(std::forward<T>(x));
674     }
675     /// Trivial destructor.
~rational_function()676     ~rational_function()
677     {
678         PIRANHA_TT_CHECK(is_cf, rational_function);
679     }
680     /// Canonicalisation.
681     /**
682      * This method will put \p this in canonical form. Normally, it is never necessary to call this method,
683      * unless low-level methods that do not keep \p this in canonical form have been invoked.
684      *
685      * If any exception is thrown by this method, \p this will not be modified.
686      *
687      * @throws piranha::zero_division_error if the numerator is zero.
688      * @throws std::invalid_argument if either the numerator or the denominator have a negative exponent.
689      * @throws unspecified any exception thrown by construction, division and GCD operations involving objects of
690      * type rational_function::p_type.
691      */
canonicalise()692     void canonicalise()
693     {
694         auto res = canonicalise_impl(m_num, m_den);
695         // This is noexcept.
696         m_num = std::move(res.first);
697         m_den = std::move(res.second);
698     }
699     /// Numerator.
700     /**
701      * @return a const reference to the numerator.
702      */
num() const703     const p_type &num() const
704     {
705         return m_num;
706     }
707     /// Denominator.
708     /**
709      * @return a const reference to the denominator.
710      */
den() const711     const p_type &den() const
712     {
713         return m_den;
714     }
715     /// Stream operator.
716     /**
717      * Will stream to \p os a human-readable representation of \p r.
718      *
719      * @param os target stream.
720      * @param r the piranha::rational_function to be streamed.
721      *
722      * @return a reference to \p os.
723      *
724      * @throws unspecified any exception thrown by the stream opertor of rational_function::p_type.
725      */
operator <<(std::ostream & os,const rational_function & r)726     friend std::ostream &operator<<(std::ostream &os, const rational_function &r)
727     {
728         if (math::is_zero(r.m_num)) {
729             // Special case for zero.
730             os << "0";
731         } else if (math::is_unitary(r.m_den)) {
732             // If the denominator is 1, print just the numerator.
733             os << r.m_num;
734         } else {
735             // First let's deal with the numerator.
736             if (r.m_num.size() == 1u) {
737                 // If on top there's only 1 term, don't print brackets.
738                 os << r.m_num;
739             } else {
740                 os << '(' << r.m_num << ')';
741             }
742             os << '/';
743             if (r.m_den.is_single_coefficient()
744                 || (r.m_den.size() == 1u && math::is_unitary(r.m_den._container().begin()->m_cf)
745                     && integer{r.m_den.degree()} == 1)) {
746                 // If the denominator is a single coefficient or it has a single term with unitary coefficient
747                 // and degree 1 (that is, it is of the form "x"), don't print the brackets.
748                 os << r.m_den;
749             } else {
750                 os << '(' << r.m_den << ')';
751             }
752         }
753         return os;
754     }
755     /// Print in TeX mode.
756     /**
757      * This method will stream to \p os a TeX representation of \p this.
758      *
759      * @param os target stream.
760      *
761      * @throws unspecified any exception thrown by:
762      * - piranha::rational_function::p_type::print_tex(),
763      * - the unary negation operator of piranha::rational_function::p_type.
764      */
print_tex(std::ostream & os) const765     void print_tex(std::ostream &os) const
766     {
767         if (math::is_zero(*this)) {
768             // Special case for zero.
769             os << "0";
770         } else if (math::is_unitary(m_den)) {
771             // If the denominator is 1, print just the numerator.
772             m_num.print_tex(os);
773         } else {
774             // The idea here is to have the first term of num and den positive.
775             // NOTE: here we are relying on the fact that negation of poly proceeds
776             // by copy + in-place negation, and thus the order of the terms is preserved.
777             piranha_assert(m_num.size() >= 1u);
778             piranha_assert(m_den.size() >= 1u);
779             const bool negate_num = m_num._container().begin()->m_cf.sign() < 0;
780             const bool negate_den = m_den._container().begin()->m_cf.sign() < 0;
781             if (negate_num != negate_den) {
782                 // If we need to negate only one of num/den,
783                 // then we need to prepend the minus sign.
784                 os << '-';
785             }
786             os << "\\frac{";
787             if (negate_num) {
788                 (-m_num).print_tex(os);
789             } else {
790                 m_num.print_tex(os);
791             }
792             os << "}{";
793             if (negate_den) {
794                 (-m_den).print_tex(os);
795             } else {
796                 m_den.print_tex(os);
797             }
798             os << '}';
799         }
800     }
801     /** @name Low-level interface
802      * Low-level methods.
803      */
804     //@{
805     /// Mutable reference to the numerator.
806     /**
807      * @return a mutable reference to the numerator.
808      */
_num()809     p_type &_num()
810     {
811         return m_num;
812     }
813     /// Mutable reference to the denominator.
814     /**
815      * @return a mutable reference to the denominator.
816      */
_den()817     p_type &_den()
818     {
819         return m_den;
820     }
821     //@}
822     /// Canonicality check.
823     /**
824      * This method will return \p true if \p this is in canonical form, \p false otherwise.
825      * Unless low-level methods are used, non-canonical rational functions can be generated only
826      * by move operations (after which the moved-from object is left with zero numerator and denominator).
827      *
828      * @return \p true if \p this is in canonical form, \p false otherwise.
829      *
830      * @throws unspecified any exception thrown by piranha::math::gcd() or the comparison operator
831      * of piranha::rational_function::p_type.
832      */
is_canonical() const833     bool is_canonical() const
834     {
835         auto g = math::gcd(m_num, m_den);
836         if (g != 1 && g != -1) {
837             return false;
838         }
839         // NOTE: this catches only the case (0,-1), which gives a GCD
840         // of -1. (0,1) is canonical and (0,n) gives a GCD of n, which
841         // is filtered above.
842         if (math::is_zero(m_num) && !math::is_unitary(m_den)) {
843             return false;
844         }
845         if (math::is_zero(m_den) || detail::poly_lterm(m_den)->m_cf.sign() < 0) {
846             return false;
847         }
848         return true;
849     }
850     /// Equality operator.
851     /**
852      * \note
853      * This operator is enabled only if one of the arguments is piranha::rational_function
854      * and the other argument is either piranha::rational_function or an interoperable type.
855      *
856      * The numerator and denominator of \p a and \p b will be compared (after any necessary conversion
857      * of \p a or \p b to piranha::rational_function).
858      *
859      * @param a first argument.
860      * @param b second argument.
861      *
862      * @return \p true if the numerator and denominator of \p this are equal to the numerator and
863      * denominator of \p other, \p false otherwise.
864      *
865      * @throws unspecified any exception thrown by the generic unary constructor, if a
866      * parameter is not piranha::rational_function.
867      */
868     template <typename T, typename U, eq_enabler<T, U> = 0>
operator ==(const T & a,const U & b)869     friend bool operator==(const T &a, const U &b)
870     {
871         return dispatch_equality(a, b);
872     }
873     /// Inequality operator.
874     /**
875      * \note
876      * This operator is enabled only if one of the arguments is piranha::rational_function
877      * and the other argument is either piranha::rational_function or an interoperable type.
878      *
879      * This operator is the opposite of operator==().
880      *
881      * @param a first argument.
882      * @param b second argument.
883      *
884      * @return the opposite of operator==().
885      *
886      * @throws unspecified any exception thrown by operator==().
887      */
888     template <typename T, typename U, eq_enabler<T, U> = 0>
operator !=(const T & a,const U & b)889     friend bool operator!=(const T &a, const U &b)
890     {
891         return !dispatch_equality(a, b);
892     }
893     /// Identity operator.
894     /**
895      * @return a copy of \p this.
896      *
897      * @throws unspecified any exception thrown by the copy constructor.
898      */
operator +() const899     rational_function operator+() const
900     {
901         return *this;
902     }
903     /// Binary addition.
904     /**
905      * \note
906      * This operator is enabled only if one of the arguments is piranha::rational_function
907      * and the other argument is either piranha::rational_function or an interoperable type.
908      *
909      * This operator will compute the result of adding \p a to \p b.
910      *
911      * @param a first argument.
912      * @param b second argument.
913      *
914      * @return <tt>a + b</tt>.
915      *
916      * @throws unspecified any exception thrown by:
917      * - construction, assignment, multiplication, addition of piranha::rational_function::p_type,
918      * - the generic unary constructor of piranha::rational_function,
919      * - canonicalise().
920      */
921     template <typename T, typename U, binary_add_enabler<T, U> = 0>
operator +(T && a,U && b)922     friend rational_function operator+(T &&a, U &&b)
923     {
924         return dispatch_binary_add(a, b);
925     }
926     /// In-place addition.
927     /**
928      * \note
929      * This operator is enabled only if \p T is an interoperable type.
930      *
931      * This operator is equivalent to the expression:
932      * @code
933      * return *this = *this + other;
934      * @endcode
935      *
936      * @param other argument.
937      *
938      * @return a reference to \p this.
939      *
940      * @throws unspecified any exception thrown by the corresponding binary operator.
941      */
942     template <typename T, typename U = rational_function, in_place_add_enabler<T, U> = 0>
operator +=(T && other)943     rational_function &operator+=(T &&other)
944     {
945         // NOTE: *this + other will always return rational_function.
946         // NOTE: we should consider in the future std::move(*this) + std::forward<T>(other) to potentially improve
947         // performance,
948         // if the addition operator is also modified to take advantage of rvalues.
949         return *this = *this + other;
950     }
951     /// Negated copy.
952     /**
953      * @return a copy of <tt>-this</tt>.
954      *
955      * @throws unspecified any exception thrown by the copy constructor.
956      */
operator -() const957     rational_function operator-() const
958     {
959         rational_function retval{*this};
960         math::negate(retval.m_num);
961         return retval;
962     }
963     /// Binary subtraction.
964     /**
965      * \note
966      * This operator is enabled only if one of the arguments is piranha::rational_function
967      * and the other argument is either piranha::rational_function or an interoperable type.
968      *
969      * This operator will compute the result of subtracting \p b from \p a.
970      *
971      * @param a first argument.
972      * @param b second argument.
973      *
974      * @return <tt>a - b</tt>.
975      *
976      * @throws unspecified any exception thrown by:
977      * - construction, assignment, multiplication, subtraction of piranha::rational_function::p_type,
978      * - the generic unary constructor of piranha::rational_function,
979      * - canonicalise().
980      */
981     template <typename T, typename U, binary_sub_enabler<T, U> = 0>
operator -(T && a,U && b)982     friend rational_function operator-(T &&a, U &&b)
983     {
984         return dispatch_binary_sub(a, b);
985     }
986     /// In-place subtraction.
987     /**
988      * \note
989      * This operator is enabled only if \p T is an interoperable type.
990      *
991      * This operator is equivalent to the expression:
992      * @code
993      * return *this = *this - other;
994      * @endcode
995      *
996      * @param other argument.
997      *
998      * @return a reference to \p this.
999      *
1000      * @throws unspecified any exception thrown by the corresponding binary operator.
1001      */
1002     template <typename T, typename U = rational_function, in_place_sub_enabler<T, U> = 0>
operator -=(T && other)1003     rational_function &operator-=(T &&other)
1004     {
1005         return *this = *this - other;
1006     }
1007     /// Binary multiplication.
1008     /**
1009      * \note
1010      * This operator is enabled only if one of the arguments is piranha::rational_function
1011      * and the other argument is either piranha::rational_function or an interoperable type.
1012      *
1013      * This operator will compute the result of multiplying \p a by \p b.
1014      *
1015      * @param a first argument.
1016      * @param b second argument.
1017      *
1018      * @return <tt>a * b</tt>.
1019      *
1020      * @throws unspecified any exception thrown by:
1021      * - construction, assignment, multiplication of piranha::rational_function::p_type,
1022      * - the generic unary constructor of piranha::rational_function,
1023      * - canonicalise().
1024      */
1025     template <typename T, typename U, binary_mul_enabler<T, U> = 0>
operator *(T && a,U && b)1026     friend rational_function operator*(T &&a, U &&b)
1027     {
1028         return dispatch_binary_mul(a, b);
1029     }
1030     /// In-place multiplication.
1031     /**
1032      * \note
1033      * This operator is enabled only if \p T is an interoperable type.
1034      *
1035      * This operator is equivalent to the expression:
1036      * @code
1037      * return *this = *this * other;
1038      * @endcode
1039      *
1040      * @param other argument.
1041      *
1042      * @return a reference to \p this.
1043      *
1044      * @throws unspecified any exception thrown by the corresponding binary operator.
1045      */
1046     template <typename T, typename U = rational_function, in_place_mul_enabler<T, U> = 0>
operator *=(T && other)1047     rational_function &operator*=(T &&other)
1048     {
1049         return *this = *this * other;
1050     }
1051     /// Binary division.
1052     /**
1053      * \note
1054      * This operator is enabled only if one of the arguments is piranha::rational_function
1055      * and the other argument is either piranha::rational_function or an interoperable type.
1056      *
1057      * This operator will compute the result of dividing \p a by \p b.
1058      *
1059      * @param a first argument.
1060      * @param b second argument.
1061      *
1062      * @return <tt>a / b</tt>.
1063      *
1064      * @throws unspecified any exception thrown by:
1065      * - construction, assignment, multiplication of piranha::rational_function::p_type,
1066      * - the generic unary constructor of piranha::rational_function,
1067      * - canonicalise().
1068      */
1069     template <typename T, typename U, binary_div_enabler<T, U> = 0>
operator /(T && a,U && b)1070     friend rational_function operator/(T &&a, U &&b)
1071     {
1072         return dispatch_binary_div(a, b);
1073     }
1074     /// In-place division.
1075     /**
1076      * \note
1077      * This operator is enabled only if \p T is an interoperable type.
1078      *
1079      * This operator is equivalent to the expression:
1080      * @code
1081      * return *this = *this / other;
1082      * @endcode
1083      *
1084      * @param other argument.
1085      *
1086      * @return a reference to \p this.
1087      *
1088      * @throws unspecified any exception thrown by the corresponding binary operator.
1089      */
1090     template <typename T, typename U = rational_function, in_place_div_enabler<T, U> = 0>
operator /=(T && other)1091     rational_function &operator/=(T &&other)
1092     {
1093         return *this = *this / other;
1094     }
1095     /// Exponentiation.
1096     /**
1097      * \note
1098      * This method is enabled only if \p T is a C++ integral type or piranha::integer.
1099      *
1100      * Similarly to piranha::series::pow(), this method keeps a cache of natural powers of
1101      * rational functions in order to expedite computations when the same powers of rational
1102      * functions are requested repeatedly (e.g., during substitution operations). The cache is
1103      * thread-safe and it can be cleared with piranha::rational_function::clear_pow_cache().
1104      *
1105      * @param n an integral exponent.
1106      *
1107      * @return \p this raised to the power of \p n.
1108      *
1109      * @throws piranha::zero_division_error if \p n is negative and \p this is zero.
1110      * @throws unspecified any exception thrown by:
1111      * - threading primitives,
1112      * - memory errors in standard containers,
1113      * - construction, assignment and arithmetic operations on piranha::rational_function.
1114      */
1115     template <typename T, pow_enabler<T> = 0>
pow(const T & n) const1116     rational_function pow(const T &n) const
1117     {
1118         // NOTE: here we are renouncing the pow() optimisation
1119         // implemented for the polynomials. Consider bringing
1120         // it back if it becomes important.
1121         const integer n_int{n}, un_int = n_int.abs();
1122         rational_function retval;
1123         {
1124             // Lock the cache.
1125             std::lock_guard<std::mutex> lock(s_pow_mutex);
1126             auto &v = get_pow_cache()[*this];
1127             using s_type = decltype(v.size());
1128             // Init the vector, if needed.
1129             if (!v.size()) {
1130                 v.push_back(rational_function{1});
1131             }
1132             // Fill in the missing powers.
1133             while (v.size() <= un_int) {
1134                 // NOTE: avoid canonicalisation by setting directly
1135                 // the num/den.
1136                 // NOTE: this will have to be replaced by explicit untruncated
1137                 // multiplication.
1138                 rational_function tmp;
1139                 tmp.m_num = v.back().m_num * m_num;
1140                 tmp.m_den = v.back().m_den * m_den;
1141                 v.push_back(std::move(tmp));
1142             }
1143             retval = v[static_cast<s_type>(un_int)];
1144         }
1145         // We need to fix retval in case of negative powers.
1146         if (n_int.sign() < 0) {
1147             if (unlikely(math::is_zero(retval.m_num))) {
1148                 piranha_throw(zero_division_error, "zero denominator in rational_function exponentiation");
1149             }
1150             // Swap num/den.
1151             std::swap(retval.m_num, retval.m_den);
1152             // The only canonicalisation we need is checking the sign of the new
1153             // denominator.
1154             if (detail::poly_lterm(retval.m_den)->m_cf.sign() < 0) {
1155                 math::negate(retval.m_num);
1156                 math::negate(retval.m_den);
1157             }
1158         }
1159         return retval;
1160     }
1161     /// Clear the internal cache of natural powers.
1162     /**
1163      * This method can be used to clear the cache of natural powers of series maintained by
1164      * piranha::rational_function::pow().
1165      *
1166      * @throws unspecified any exception thrown by threading primitives.
1167      */
clear_pow_cache()1168     static void clear_pow_cache()
1169     {
1170         std::lock_guard<std::mutex> lock(s_pow_mutex);
1171         get_pow_cache().clear();
1172     }
1173     /// Substitution.
1174     /**
1175      * \note
1176      * This method is enabled only if either:
1177      * - \p T is an interoperable type or piranha::rational_function, or
1178      * - the expression <tt>math::subs(num(),name,x) / math::subs(den(),name,x)</tt>
1179      *   is well-formed, yielding a type that satisfies piranha::is_returnable.
1180      *
1181      * If \p T is an interoperable type or piranha::rational_function, the body of this method is equivalent to:
1182      * @code
1183      * return rational_function{math::subs(num(),name,x),math::subs(den(),name,x)};
1184      * @endcode
1185      * Otherwise, the body of this method is equivalent to:
1186      * @code
1187      * return math::subs(num(),name,x) / math::subs(den(),name,x);
1188      * @endcode
1189      *
1190      * @param name name of the variable to be substituted.
1191      * @param x substitution argument.
1192      *
1193      * @return the result of substituting \p name with \p x in \p this.
1194      *
1195      * @throws unspecified any exception thrown by:
1196      * - piranha::math::subs(),
1197      * - the binary constructor of piranha::rational_function,
1198      * - the division operator on the type resulting from the substitution
1199      *   in the numerator and denominator.
1200      */
1201     template <typename T>
subs(const std::string & name,const T & x) const1202     subs_type<T> subs(const std::string &name, const T &x) const
1203     {
1204         return subs_impl(*this, name, x);
1205     }
1206     /// Substitution of integral power.
1207     /**
1208      * \note
1209      * This method is enabled only if either:
1210      * - \p T is an interoperable type or piranha::rational_function, or
1211      * - the expression <tt>math::ipow_subs(num(),name,n,x) / math::ipow_subs(den(),name,n,x)</tt>
1212      *   is well-formed, yielding a type that satisfies piranha::is_returnable.
1213      *
1214      * If \p T is an interoperable type or piranha::rational_function, the body of this method is equivalent to:
1215      * @code
1216      * return rational_function{math::ipow_subs(num(),name,n,x),math::ipow_subs(den(),name,n,x)};
1217      * @endcode
1218      * Otherwise, the body of this method is equivalent to:
1219      * @code
1220      * return math::ipow_subs(num(),name,n,x) / math::ipow_subs(den(),name,n,x);
1221      * @endcode
1222      *
1223      * @param name name of the variable to be substituted.
1224      * @param n power of \p name to be substituted.
1225      * @param x substitution argument.
1226      *
1227      * @return the result of substituting \p name to the power of \p n with \p x in \p this.
1228      *
1229      * @throws unspecified any exception thrown by:
1230      * - piranha::math::ipow_subs(),
1231      * - the binary constructor of piranha::rational_function,
1232      * - the division operator on the type resulting from the substitution
1233      *   in the numerator and denominator.
1234      */
1235     template <typename T>
ipow_subs(const std::string & name,const integer & n,const T & x) const1236     ipow_subs_type<T> ipow_subs(const std::string &name, const integer &n, const T &x) const
1237     {
1238         return ipow_subs_impl(*this, name, n, x);
1239     }
1240     /// Trim.
1241     /**
1242      * This method will return a copy of \p this whose numerator and denominator have been generated
1243      * by calling piranha::series::trim() on the numerator and denominator of \p this.
1244      *
1245      * @return a copy of \p this with the ignorable arguments removed.
1246      *
1247      * @throws unspecified any exception thrown by piranha::series::trim().
1248      */
trim() const1249     rational_function trim() const
1250     {
1251         // Don't use the ctor, as the result will be canonical by construction.
1252         rational_function retval;
1253         retval.m_num = m_num.trim();
1254         retval.m_den = m_den.trim();
1255         return retval;
1256     }
1257     /// Hash value.
1258     /**
1259      * The hash of a rational function is computed by combining the hashes of
1260      * numerator and denominator.
1261      *
1262      * @return a hash value for \p this.
1263      */
hash() const1264     std::size_t hash() const
1265     {
1266         return static_cast<std::size_t>(num().hash() + den().hash());
1267     }
1268     /// Identical check.
1269     /**
1270      * Two rational functions are identical if their numerators and denominators are, via
1271      * piranha::rational_function::p_type::is_identical().
1272      *
1273      * @param other comparison argument.
1274      *
1275      * @return \p true if \p this is identical to \p other, \p false otherwise.
1276      */
is_identical(const rational_function & other) const1277     bool is_identical(const rational_function &other) const
1278     {
1279         return m_num.is_identical(other.m_num) && m_den.is_identical(other.m_den);
1280     }
1281     /// Register custom partial derivative.
1282     /**
1283      * \note
1284      * This method is enabled only if \p F is a type that can be used to construct
1285      * <tt>std::function<rational_function(const rational_function &)</tt>.
1286      *
1287      * Register a copy of a callable \p func associated to the symbol called \p name for use by
1288      * piranha::math::partial().
1289      * \p func will be used to compute the partial derivative of piranha::rational_function with respect to
1290      * \p name in place of the default partial differentiation algorithm.
1291      *
1292      * It is safe to call this method from multiple threads.
1293      *
1294      * @param name symbol for which the custom partial derivative function will be registered.
1295      * @param func custom partial derivative function.
1296      *
1297      * @throws unspecified any exception thrown by:
1298      * - failure(s) in threading primitives,
1299      * - lookup and insertion operations on \p std::unordered_map,
1300      * - construction and move-assignment of \p std::function.
1301      */
1302     template <typename F, custom_partial_enabler<F> = 0>
register_custom_derivative(const std::string & name,F func)1303     static void register_custom_derivative(const std::string &name, F func)
1304     {
1305         using f_type = std::function<rational_function(const rational_function &)>;
1306         std::lock_guard<std::mutex> lock(s_cp_mutex);
1307         get_cp_map()[name] = f_type(func);
1308     }
1309     /// Unregister custom partial derivative.
1310     /**
1311      * Unregister the custom partial derivative function associated to the symbol called \p name. If no custom
1312      * partial derivative was previously registered using register_custom_derivative(), calling this function will be a
1313      * no-op.
1314      *
1315      * It is safe to call this method from multiple threads.
1316      *
1317      * @param name symbol for which the custom partial derivative function will be unregistered.
1318      *
1319      * @throws unspecified any exception thrown by:
1320      * - failure(s) in threading primitives,
1321      * - lookup and erase operations on \p std::unordered_map.
1322      */
unregister_custom_derivative(const std::string & name)1323     static void unregister_custom_derivative(const std::string &name)
1324     {
1325         std::lock_guard<std::mutex> lock(s_cp_mutex);
1326         auto it = get_cp_map().find(name);
1327         if (it != get_cp_map().end()) {
1328             get_cp_map().erase(it);
1329         }
1330     }
1331     /// Unregister all custom partial derivatives.
1332     /**
1333      * Will unregister all custom derivatives currently registered via register_custom_derivative().
1334      * It is safe to call this method from multiple threads.
1335      *
1336      * @throws unspecified any exception thrown by failure(s) in threading primitives.
1337      */
unregister_all_custom_derivatives()1338     static void unregister_all_custom_derivatives()
1339     {
1340         std::lock_guard<std::mutex> lock(s_cp_mutex);
1341         get_cp_map().clear();
1342     }
1343     /// Partial derivative.
1344     /**
1345      * The result is computed via the quotient rule. Internally, this method will call
1346      * piranha::math::partial() on the numerator and denominator of \p this.
1347      *
1348      * @param name name of the variable with respect to which to differentiate.
1349      *
1350      * @return the partial derivative of \p r with respect to \p name.
1351      *
1352      * @throws unspecified any exception thrown by:
1353      * - the partial derivative of numerator and denominator,
1354      * - arithmetic operations on piranha::rational_function::p_type,
1355      * - the binary constructor of piranha::rational_function.
1356      */
partial(const std::string & name) const1357     rational_function partial(const std::string &name) const
1358     {
1359         using math::partial;
1360         return rational_function{partial(num(), name) * den() - num() * partial(den(), name), den() * den()};
1361     }
1362 
1363 private:
1364     p_type m_num;
1365     p_type m_den;
1366 };
1367 
1368 template <typename Key>
1369 std::mutex rational_function<Key>::s_pow_mutex;
1370 
1371 template <typename Key>
1372 std::mutex rational_function<Key>::s_cp_mutex;
1373 
1374 /// Specialisation of piranha::print_tex_coefficient() for piranha::rational_function.
1375 /**
1376  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1377  */
1378 template <typename T>
1379 struct print_tex_coefficient_impl<T, typename std::enable_if<std::is_base_of<detail::rational_function_tag,
1380                                                                              T>::value>::type> {
1381     /// Call operator.
1382     /**
1383      * This operator will call internally piranha::rational_function::print_tex().
1384      *
1385      * @param os target stream.
1386      * @param r piranha::rational_function argument.
1387      *
1388      * @throws unspecified any exception thrown by piranha::rational_function::print_tex().
1389      */
operator ()piranha::print_tex_coefficient_impl1390     void operator()(std::ostream &os, const T &r) const
1391     {
1392         r.print_tex(os);
1393     }
1394 };
1395 
1396 namespace math
1397 {
1398 
1399 /// Specialisation of piranha::math::is_zero() for piranha::rational_function.
1400 /**
1401  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1402  */
1403 template <typename T>
1404 struct is_zero_impl<T, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1405     /// Call operator.
1406     /**
1407      * @param r the piranha::rational_function to be tested.
1408      *
1409      * @return the result of calling piranha::math::is_zero() on the numerator of \p r.
1410      */
operator ()piranha::math::is_zero_impl1411     bool operator()(const T &r) const
1412     {
1413         return is_zero(r.num());
1414     }
1415 };
1416 
1417 /// Specialisation of piranha::math::pow() for piranha::rational_function bases.
1418 /**
1419  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1420  */
1421 template <typename T, typename U>
1422 struct pow_impl<T, U, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1423 private:
1424     template <typename V>
1425     using pow_enabler = typename std::
1426         enable_if<detail::true_tt<decltype(std::declval<const T &>().pow(std::declval<const V &>()))>::value,
1427                   int>::type;
1428 
1429 public:
1430     /// Call operator.
1431     /**
1432      * \note
1433      * The call operator is enabled only if <tt>return r.pow(n)</tt> is a valid expression.
1434      *
1435      * @param r the piranha::rational_function base.
1436      * @param n the integral exponent.
1437      *
1438      * @return <tt>r.pow(n)</tt>.
1439      *
1440      * @throws unspecified any exception thrown by piranha::rational_function::pow().
1441      */
1442     template <typename V, pow_enabler<V> = 0>
operator ()piranha::math::pow_impl1443     T operator()(const T &r, const V &n) const
1444     {
1445         return r.pow(n);
1446     }
1447 };
1448 
1449 /// Specialisation of piranha::math::subs() for piranha::rational_function.
1450 /**
1451  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1452  */
1453 template <typename T, typename U>
1454 struct subs_impl<T, U, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1455 private:
1456     template <typename V>
1457     using subs_type = decltype(std::declval<const T &>().subs(std::string{}, std::declval<const V &>()));
1458 
1459 public:
1460     /// Call operator.
1461     /**
1462      * \note
1463      * The call operator is enabled only if <tt>return r.subs(s,x)</tt> is a valid expression.
1464      *
1465      * @param r the piranha::rational_function argument.
1466      * @param s the name of the variable to be substituted.
1467      * @param x the substitution argument.
1468      *
1469      * @return <tt>r.subs(s,x)</tt>.
1470      *
1471      * @throws unspecified any exception thrown by piranha::rational_function::subs().
1472      */
1473     template <typename V>
operator ()piranha::math::subs_impl1474     subs_type<V> operator()(const T &r, const std::string &s, const V &x) const
1475     {
1476         return r.subs(s, x);
1477     }
1478 };
1479 
1480 /// Specialisation of piranha::math::ipow_subs() for piranha::rational_function.
1481 /**
1482  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1483  */
1484 template <typename T, typename U>
1485 struct ipow_subs_impl<T, U, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1486 private:
1487     template <typename V>
1488     using ipow_subs_type
1489         = decltype(std::declval<const T &>().ipow_subs(std::string{}, integer{}, std::declval<const V &>()));
1490 
1491 public:
1492     /// Call operator.
1493     /**
1494      * \note
1495      * The call operator is enabled only if <tt>return r.ipow_subs(s,n,x)</tt> is a valid expression.
1496      *
1497      * @param r the piranha::rational_function argument.
1498      * @param s the name of the variable to be substituted.
1499      * @param n the power of \p s to be substituted.
1500      * @param x the substitution argument.
1501      *
1502      * @return <tt>r.ipow_subs(s,n,x)</tt>.
1503      *
1504      * @throws unspecified any exception thrown by piranha::rational_function::ipow_subs().
1505      */
1506     template <typename V>
operator ()piranha::math::ipow_subs_impl1507     ipow_subs_type<V> operator()(const T &r, const std::string &s, const integer &n, const V &x) const
1508     {
1509         return r.ipow_subs(s, n, x);
1510     }
1511 };
1512 }
1513 
1514 namespace detail
1515 {
1516 
1517 // Enabler for the math::partial() specialisation for rf.
1518 template <typename T>
1519 using rf_partial_enabler = typename std::enable_if<std::is_base_of<rational_function_tag, T>::value>::type;
1520 }
1521 
1522 namespace math
1523 {
1524 
1525 /// Specialisation of the piranha::math::partial() functor for piranha::rational_function.
1526 /**
1527  * This specialisation is activated when \p T is an instance of piranha::rational_function.
1528  */
1529 template <typename T>
1530 struct partial_impl<T, detail::rf_partial_enabler<T>> {
1531     /// Call operator.
1532     /**
1533      * The call operator will first check whether a custom partial derivative for \p T was registered
1534      * via piranha::rational_function::register_custom_derivative(). In such a case, the custom derivative function will
1535      * be used
1536      * to compute the return value. Otherwise, the output of piranha::rational_function::partial() will be returned.
1537      *
1538      * @param r input piranha::rational_function.
1539      * @param name name of the argument with respect to which the differentiation will be calculated.
1540      *
1541      * @return the partial derivative of \p r with respect to \p name.
1542      *
1543      * @throws unspecified any exception thrown by:
1544      * - piranha::rational_function::partial(),
1545      * - failure(s) in threading primitives,
1546      * - lookup operations on \p std::unordered_map,
1547      * - the copy assignment and call operators of the registered custom partial derivative function.
1548      */
operator ()piranha::math::partial_impl1549     T operator()(const T &r, const std::string &name) const
1550     {
1551         bool custom = false;
1552         std::function<T(const T &)> func;
1553         // Try to locate a custom partial derivative and copy it into func, if found.
1554         {
1555             std::lock_guard<std::mutex> lock(T::s_cp_mutex);
1556             auto it = T::get_cp_map().find(name);
1557             if (it != T::get_cp_map().end()) {
1558                 func = it->second;
1559                 custom = true;
1560             }
1561         }
1562         if (custom) {
1563             return func(r);
1564         }
1565         return r.partial(name);
1566     }
1567 };
1568 
1569 /// Specialisation of the piranha::math::integrate() functor for piranha::rational_function.
1570 /**
1571  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1572  */
1573 template <typename T>
1574 struct integrate_impl<T, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1575     /// Call operator.
1576     /**
1577      * The integration will be successful only if the denominator of \p r does not depend on the integration variable.
1578      *
1579      * @param r piranha::rational_function argument.
1580      * @param name name of the integration variable.
1581      *
1582      * @return an antiderivative of \p r with respect to \p name.
1583      *
1584      * @throws std::invalid_argument if the denominator of \p r depends on the integration variable.
1585      * @throws unspecified any exception thrown by:
1586      * - piranha::math::integrate(),
1587      * - the constructor of piranha::rational_function::q_type from piranha::rational_function::p_type,
1588      * - the binary constructor of piranha::rational_function.
1589      */
operator ()piranha::math::integrate_impl1590     T operator()(const T &r, const std::string &name)
1591     {
1592         if (!is_zero(degree(r.den(), {name}))) {
1593             piranha_throw(std::invalid_argument, "cannot compute the integral of a rational function whose "
1594                                                  "denominator depends on the integration variable");
1595         }
1596         return T{integrate(typename T::q_type{r.num()}, name), r.den()};
1597     }
1598 };
1599 
1600 /// Specialisation of the piranha::math::evaluate() functor for piranha::rational_function.
1601 /**
1602  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1603  */
1604 template <typename T, typename U>
1605 struct evaluate_impl<T, U, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1606 private:
1607     // This is the real eval type.
1608     template <typename V>
1609     using eval_type_ = decltype(
1610         evaluate(std::declval<const T &>().num(), std::declval<const std::unordered_map<std::string, V> &>())
1611         / evaluate(std::declval<const T &>().den(), std::declval<const std::unordered_map<std::string, V> &>()));
1612     template <typename V>
1613     using eval_type = typename std::enable_if<is_returnable<eval_type_<V>>::value, eval_type_<V>>::type;
1614 
1615 public:
1616     /// Call operator.
1617     /**
1618      * \note
1619      * This operator is enabled only if the algorithm described below is supported by the involved types,
1620      * and it returns a type which satisfies piranha::is_returnable.
1621      *
1622      * The evaluation of a rational function is constructed from the ratio of the evaluation of its numerator
1623      * by the evaluation of its denominator. The return type is the type resulting from this operation.
1624      *
1625      * @param r the piranha::rational_function argument.
1626      * @param m the evaluation map.
1627      *
1628      * @return the evaluation of \p r according to \p m.
1629      *
1630      * @throws unspecified any exception thrown by:
1631      * - the evaluation of the numerator and denominator of \p r,
1632      * - the division of the results of the evaluations of numerator and denominator.
1633      */
1634     template <typename V>
operator ()piranha::math::evaluate_impl1635     eval_type<V> operator()(const T &r, const std::unordered_map<std::string, V> &m) const
1636     {
1637         return evaluate(r.num(), m) / evaluate(r.den(), m);
1638     }
1639 };
1640 
1641 /// Specialisation of the piranha::math::cos() functor for piranha::rational_function.
1642 /**
1643  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1644  */
1645 template <typename T>
1646 struct cos_impl<T, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1647     /// Call operator.
1648     /**
1649      * @param r the piranha::rational_function argument.
1650      *
1651      * @return 1 if \p r is zero.
1652      *
1653      * @throws std::invalid_argument if \p r is not zero.
1654      * @throws unspecified any exception thrown by the constructor of piranha::rational_function from \p int.
1655      */
operator ()piranha::math::cos_impl1656     T operator()(const T &r) const
1657     {
1658         if (!is_zero(r)) {
1659             piranha_throw(std::invalid_argument, "cannot compute the cosine of a nonzero rational function");
1660         }
1661         return T{1};
1662     }
1663 };
1664 
1665 /// Specialisation of the piranha::math::sin() functor for piranha::rational_function.
1666 /**
1667  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1668  */
1669 template <typename T>
1670 struct sin_impl<T, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1671     /// Call operator.
1672     /**
1673      * @param r the piranha::rational_function argument.
1674      *
1675      * @return 0 if \p r is zero.
1676      *
1677      * @throws std::invalid_argument if \p r is not zero.
1678      */
operator ()piranha::math::sin_impl1679     T operator()(const T &r) const
1680     {
1681         if (!is_zero(r)) {
1682             piranha_throw(std::invalid_argument, "cannot compute the sine of a nonzero rational function");
1683         }
1684         return T{};
1685     }
1686 };
1687 
1688 /// Specialisation of the piranha::math::degree() functor for piranha::rational_function.
1689 /**
1690  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1691  */
1692 template <typename T>
1693 struct degree_impl<T, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1694     /// Call operator.
1695     /**
1696      * The (partial) degree of a rational function is defined as the maximum (partial)
1697      * degree of numerator and denominator.
1698      *
1699      * @param r the piranha::rational_function argument.
1700      * @param args either an empty pack, or a univariate pack containing a vector
1701      * of strings representing the names of the variables with respect to which
1702      * the partial degree is computed.
1703      *
1704      * @return the (partial) degree of \p r.
1705      *
1706      * @throws unspecified any exception thrown by the computation of the degrees
1707      * of the numerator or denominator of \p r.
1708      */
1709     template <typename... Args>
operator ()piranha::math::degree_impl1710     auto operator()(const T &r, const Args &... args) const -> decltype(degree(r.num(), args...))
1711     {
1712         // NOTE: very important here, std::max returns a reference, so in the decltype()
1713         // above we must not use the std::max otherwise we will return a reference.
1714         return std::max(degree(r.num(), args...), degree(r.den(), args...));
1715     }
1716 };
1717 
1718 /// Specialisation of the piranha::math::divexact() functor for piranha::rational_function.
1719 /**
1720  * This specialisation is enabled if \p T is an instance of piranha::rational_function.
1721  */
1722 template <typename T>
1723 struct divexact_impl<T, typename std::enable_if<std::is_base_of<detail::rational_function_tag, T>::value>::type> {
1724     /// Call operator.
1725     /**
1726      * This call operator is equivalent to:
1727      * @code
1728      * retval = n / d;
1729      * @endcode
1730      *
1731      * @param retval the result of exact division.
1732      * @param n the numerator.
1733      * @param d the denominator.
1734      *
1735      * @throws unspecified any exception thrown by piranha::rational_function::operator/().
1736      */
operator ()piranha::math::divexact_impl1737     void operator()(T &retval, const T &n, const T &d) const
1738     {
1739         retval = n / d;
1740     }
1741 };
1742 }
1743 
1744 namespace detail
1745 {
1746 
1747 template <typename T>
1748 using sri_rf_enabler =
1749     typename std::enable_if<std::is_base_of<detail::rational_function_tag, typename std::decay<T>::type>::value>::type;
1750 }
1751 
1752 /// Specialisation of piranha::series_recursion_index for piranha::rational_function.
1753 /**
1754  * This specialisation is enabled when \p T is an instance of piranha::rational_function.
1755  * Although piranha::rational_function is not a piranha::series, it is assigned a recursion index
1756  * of 1 in order to provide useful type coercion in a number of situations (e.g., adding
1757  * a Poisson series over rational functions to a polynomial).
1758  */
1759 // NOTE: this was prompted by the necessity of allowing subs with generic objects in subs() and
1760 // ipow_subs(). It seems to work ok and it does not seem to conflict with the general series operators
1761 // (which is the only place where the recursion index is used), but we should tread carefully:
1762 // rational_function is not a series and does not provide any term/cf/key types and the likes.
1763 // The logic in the series operators is SFINAE friendly and does not error out due to the lack
1764 // of these typedefs.
1765 template <typename T>
1766 class series_recursion_index<T, detail::sri_rf_enabler<T>>
1767 {
1768 public:
1769     /// Value of the type trait.
1770     static const std::size_t value = 1u;
1771 };
1772 
1773 template <typename T>
1774 const std::size_t series_recursion_index<T, detail::sri_rf_enabler<T>>::value;
1775 
1776 inline namespace impl
1777 {
1778 
1779 template <typename Archive, typename T>
1780 using rf_boost_save_enabler = enable_if_t<conjunction<std::is_base_of<detail::rational_function_tag, T>,
1781                                                       has_boost_save<Archive, typename T::p_type>>::value>;
1782 
1783 template <typename Archive, typename T>
1784 using rf_boost_load_enabler = enable_if_t<conjunction<std::is_base_of<detail::rational_function_tag, T>,
1785                                                       has_boost_load<Archive, typename T::p_type>>::value>;
1786 }
1787 
1788 /// Specialisation of piranha::boost_save() for piranha::rational_function.
1789 /**
1790  * \note
1791  * This specialisation is enabled only if \p T is an instance of piranha::rational_function whose numerator/denominator
1792  * type satisfies piranha::has_boost_save.
1793  *
1794  * @throws unspecified any exception thrown by piranha::boost_save().
1795  */
1796 template <typename Archive, typename T>
1797 struct boost_save_impl<Archive, T, rf_boost_save_enabler<Archive, T>> : boost_save_via_boost_api<Archive, T> {
1798 };
1799 
1800 /// Specialisation of piranha::boost_load() for piranha::rational_function.
1801 /**
1802  * \note
1803  * This specialisation is enabled only if \p T is an instance of piranha::rational_function whose numerator/denominator
1804  * type satisfies piranha::has_boost_load.
1805  *
1806  * If \p Archive is \p boost::archive::binary_iarchive, no checking is
1807  * performed on the content of \p ar. Otherwise, it will be ensured that
1808  * the deserialized rational function is in canonical form.
1809  *
1810  * @throws unspecified any exception thrown by piranha::boost_load() or by the constructor of
1811  * piranha::rational_function from numerator and denominator.
1812  */
1813 template <typename Archive, typename T>
1814 struct boost_load_impl<Archive, T, rf_boost_load_enabler<Archive, T>> : boost_load_via_boost_api<Archive, T> {
1815 };
1816 
1817 #if defined(PIRANHA_WITH_MSGPACK)
1818 
1819 inline namespace impl
1820 {
1821 
1822 template <typename Stream, typename T>
1823 using rf_mspack_pack_enabler
1824     = enable_if_t<conjunction<std::is_base_of<detail::rational_function_tag, T>, is_msgpack_stream<Stream>,
1825                               has_msgpack_pack<Stream, typename T::p_type>>::value>;
1826 
1827 template <typename T>
1828 using rf_mspack_convert_enabler = enable_if_t<conjunction<std::is_base_of<detail::rational_function_tag, T>,
1829                                                           has_msgpack_convert<typename T::p_type>>::value>;
1830 }
1831 
1832 /// Specialisation of piranha::msgpack_pack() for piranha::rational_function.
1833 /**
1834  * \note
1835  * This specialisation is enabled if \p T is an instance of piranha::rational_function whose numerator/denominator
1836  * type satisfies piranha::has_msgpack_pack, and \p Stream satisfies piranha::is_msgpack_stream.
1837  */
1838 template <typename Stream, typename T>
1839 struct msgpack_pack_impl<Stream, T, rf_mspack_pack_enabler<Stream, T>> {
1840     /// Call operator.
1841     /**
1842      * The call operator will pack into \p p the numerator and denominator of \p r as a pair.
1843      *
1844      * @param p the target \p msgpack::packer.
1845      * @param r the rational function to be serialised.
1846      * @param f the desired piranha::msgpack_format.
1847      *
1848      * @throws unspecified any exception thrown by the public interface of msgpack::packer or piranha::msgpack_pack().
1849      */
operator ()piranha::msgpack_pack_impl1850     void operator()(msgpack::packer<Stream> &p, const T &r, msgpack_format f) const
1851     {
1852         p.pack_array(2);
1853         msgpack_pack(p, r.num(), f);
1854         msgpack_pack(p, r.den(), f);
1855     }
1856 };
1857 
1858 /// Specialisation of piranha::msgpack_convert() for piranha::rational_function.
1859 /**
1860  * \note
1861  * This specialisation is enabled if \p T is an instance of piranha::rational_function whose numerator/denominator
1862  * type satisfies piranha::has_msgpack_convert.
1863  */
1864 template <typename T>
1865 struct msgpack_convert_impl<T, rf_mspack_convert_enabler<T>> {
1866     /// Call operator.
1867     /**
1868      * This method will convert \p o into \p r using the format \p f.
1869      *
1870      * If \p f is piranha::msgpack_format::binary, no checking is
1871      * performed on the content of \p o. Otherwise, this method will ensure that
1872      * the deserialized rational function is in canonical form.
1873      *
1874      * @param r the rational function into which \p o will be converted.
1875      * @param o the source \p msgpack::object.
1876      * @param f the desired piranha::msgpack_format.
1877      *
1878      * @throws unspecified any exception thrown by:
1879      * - the public interface of \p msgpack::object,
1880      * - piranha::msgpack_convert(),
1881      * - the constructor of piranha::rational_function from numerator and denominator.
1882      */
operator ()piranha::msgpack_convert_impl1883     void operator()(T &r, const msgpack::object &o, msgpack_format f) const
1884     {
1885         std::array<msgpack::object, 2> tmp;
1886         o.convert(tmp);
1887         typename T::p_type n, d;
1888         msgpack_convert(n, tmp[0], f);
1889         msgpack_convert(d, tmp[1], f);
1890         if (f == msgpack_format::portable) {
1891             r = T{std::move(n), std::move(d)};
1892         } else {
1893             r._num() = std::move(n);
1894             r._den() = std::move(d);
1895         }
1896     }
1897 };
1898 
1899 #endif
1900 }
1901 
1902 #endif
1903