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