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_MATH_HPP
30 #define PIRANHA_MATH_HPP
31 
32 #include <algorithm>
33 #include <boost/numeric/conversion/cast.hpp>
34 #include <cmath>
35 #include <complex>
36 #include <cstdarg>
37 #include <initializer_list>
38 #include <iterator>
39 #include <stdexcept>
40 #include <string>
41 #include <type_traits>
42 #include <unordered_map>
43 #include <unordered_set>
44 #include <utility>
45 #include <vector>
46 
47 #include "detail/sfinae_types.hpp"
48 #include "exceptions.hpp"
49 #include "is_key.hpp"
50 #include "symbol_set.hpp"
51 #include "type_traits.hpp"
52 
53 namespace piranha
54 {
55 
56 /// Math namespace.
57 /**
58  * Namespace for general-purpose mathematical functions.
59  */
60 namespace math
61 {
62 
63 /// Default functor for the implementation of piranha::math::is_zero().
64 /**
65  * This functor should be specialised via the \p std::enable_if mechanism. The default implementation defines a call
66  * operator which is enabled only if the argument type is constructible from the C++ \p int type and \p T is equality
67  * comparable.
68  */
69 template <typename T, typename = void>
70 struct is_zero_impl {
71 private:
72     // NOTE: the equality comparable requirement already implies that the return type of
73     // the comparison must be convertible to bool.
74     template <typename U>
75     using enabler =
76         typename std::enable_if<std::is_constructible<U, int>::value && is_equality_comparable<U>::value, int>::type;
77 
78 public:
79     /// Call operator.
80     /**
81      * \note
82      * This operator is enabled only if \p U is constructible from \p int and
83      * equality-comparable.
84      *
85      * The operator will compare \p x to an instance of \p U constructed from the literal 0.
86      *
87      * @param x argument to be tested.
88      *
89      * @return \p true if \p x is zero, \p false otherwise.
90      *
91      * @throws unspecified any exception thrown by the construction or comparison of instances of type \p U, or
92      * by the conversion of the result of the comparison to \p bool.
93      */
94     template <typename U, enabler<U> = 0>
operator ()piranha::math::is_zero_impl95     bool operator()(const U &x) const
96     {
97         return x == U(0);
98     }
99 };
100 } // namespace math
101 
102 namespace detail
103 {
104 
105 // Enabler for math::is_zero().
106 template <typename T>
107 using math_is_zero_enabler = typename std::enable_if<
108     std::is_convertible<decltype(math::is_zero_impl<T>{}(std::declval<const T &>())), bool>::value, int>::type;
109 } // namespace detail
110 
111 namespace math
112 {
113 
114 /// Zero test.
115 /**
116  * \note
117  * This function is enabled only if <tt>is_zero_impl<T>{}(x)</tt> is a well-formed expression returning
118  * a type implicitly convertible to \p bool.
119  *
120  * Test if value is zero. The actual implementation of this function is in the piranha::math::is_zero_impl functor's
121  * call operator. The body of this function is equivalent to:
122  * @code
123  * return is_zero_impl<T>{}(x);
124  * @endcode
125  *
126  * @param x value to be tested.
127  *
128  * @return \p true if value is zero, \p false otherwise.
129  *
130  * @throws unspecified any exception thrown by the call operator of the piranha::math::is_zero_impl functor or by
131  * the conversion of the result to \p bool.
132  */
133 template <typename T, detail::math_is_zero_enabler<T> = 0>
is_zero(const T & x)134 inline bool is_zero(const T &x)
135 {
136     return is_zero_impl<T>{}(x);
137 }
138 } // namespace math
139 
140 namespace detail
141 {
142 
143 // Enabler for the std complex specialisation of is_zero.
144 template <typename T>
145 using math_is_zero_std_complex_enabler =
146     typename std::enable_if<std::is_same<T, std::complex<float>>::value || std::is_same<T, std::complex<double>>::value
147                             || std::is_same<T, std::complex<long double>>::value>::type;
148 } // namespace detail
149 
150 namespace math
151 {
152 
153 /// Specialisation of the piranha::math::is_zero() functor for C++ complex floating-point types.
154 /**
155  * This specialisation is enabled if \p T is an <tt>std::complex</tt> of a C++ floating-point type.
156  */
157 template <typename T>
158 struct is_zero_impl<T, detail::math_is_zero_std_complex_enabler<T>> {
159     /// Call operator.
160     /**
161      * The operator will test separately the real and imaginary parts of the complex argument.
162      *
163      * @param c argument to be tested.
164      *
165      * @return \p true if \p c is zero, \p false otherwise.
166      */
operator ()piranha::math::is_zero_impl167     bool operator()(const T &c) const
168     {
169         return is_zero(c.real()) && is_zero(c.imag());
170     }
171 };
172 
173 /// Default functor for the implementation of piranha::math::is_unitary().
174 /**
175  * This functor should be specialised via the \p std::enable_if mechanism. The default implementation defines a call
176  * operator which is enabled only if the argument type is constructible from the C++ \p int type and \p T is equality
177  * comparable.
178  */
179 template <typename T, typename = void>
180 struct is_unitary_impl {
181 private:
182     template <typename U>
183     using enabler =
184         typename std::enable_if<std::is_constructible<U, int>::value && is_equality_comparable<U>::value, int>::type;
185 
186 public:
187     /// Call operator.
188     /**
189      * \note
190      * This template method is enabled only if \p U can be constructed from \p int and \p U is
191      * equality comparable.
192      *
193      * The operator will compare \p x to an instance of \p U constructed from the literal 1.
194      *
195      * @param x argument to be tested.
196      *
197      * @return \p true if \p x is equal to 1, \p false otherwise.
198      *
199      * @throws unspecified any exception thrown by the construction or comparison of instances of type \p U or by
200      * the conversion of the result to \p bool.
201      */
202     template <typename U, enabler<U> = 0>
operator ()piranha::math::is_unitary_impl203     bool operator()(const U &x) const
204     {
205         return x == U(1);
206     }
207 };
208 } // namespace math
209 
210 namespace detail
211 {
212 
213 // Enabler for piranha::math::is_unitary().
214 template <typename T>
215 using math_is_unitary_enabler = typename std::enable_if<
216     std::is_convertible<decltype(math::is_unitary_impl<T>{}(std::declval<const T &>())), bool>::value, int>::type;
217 } // namespace detail
218 
219 namespace math
220 {
221 
222 /// Unitary test.
223 /**
224  * \note
225  * This function is enabled only if <tt>is_unitary_impl<T>{}(x)</tt> is a valid expression, returning a type
226  * which is implicitly convertible to \p bool.
227  *
228  * Test if value is equal to 1. The actual implementation of this function is in the piranha::math::is_unitary_impl
229  * functor's
230  * call operator. The body of this function is equivalent to:
231  * @code
232  * return is_unitary_impl<T>{}(x);
233  * @endcode
234  *
235  * @param x value to be tested.
236  *
237  * @return \p true if value is equal to 1, \p false otherwise.
238  *
239  * @throws unspecified any exception thrown by the call operator of the piranha::math::is_unitary_impl functor, or by
240  * the conversion of the result to \p bool.
241  */
242 template <typename T, detail::math_is_unitary_enabler<T> = 0>
is_unitary(const T & x)243 inline bool is_unitary(const T &x)
244 {
245     return is_unitary_impl<T>{}(x);
246 }
247 
248 /// Default functor for the implementation of piranha::math::negate().
249 /**
250  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will
251  * assign to the input value its negation.
252  */
253 template <typename T, typename = void>
254 struct negate_impl {
255 private:
256     template <typename U>
257     using negate_t = decltype(std::declval<U &>() = -std::declval<U &>());
258     template <typename U>
negate_implepiranha::math::negate_impl259     static void negate_imple(U &x, const std::true_type &)
260     {
261         x = static_cast<U>(-x);
262     }
263     template <typename U>
negate_implepiranha::math::negate_impl264     static void negate_imple(U &x, const std::false_type &)
265     {
266         x = -x;
267     }
268 
269 public:
270     /// Generic call operator.
271     /**
272      * \note
273      * This operator is enabled only if the expression <tt>x = -x</tt> is well-formed.
274      *
275      * The body of the operator is equivalent to:
276      * @code
277      * x = -x;
278      * @endcode
279      *
280      * @param x value to be negated.
281      *
282      * @throws unspecified any exception resulting from the in-place negation or assignment of \p x.
283      */
284     template <typename U, enable_if_t<is_detected<negate_t, U>::value, int> = 0>
operator ()piranha::math::negate_impl285     void operator()(U &x) const
286     {
287         negate_imple(x, std::is_integral<U>{});
288     }
289 };
290 } // namespace math
291 
292 namespace detail
293 {
294 
295 // Enabler for math::negate().
296 template <typename T>
297 using math_negate_enabler = typename std::enable_if<
298     conjunction<negation<std::is_const<T>>, true_tt<decltype(math::negate_impl<T>{}(std::declval<T &>()))>>::value,
299     int>::type;
300 } // namespace detail
301 
302 namespace math
303 {
304 
305 /// In-place negation.
306 /**
307  * \note
308  * This function is enabled only if \p T is not const and if the expression <tt>negate_impl<T>{}(x)</tt> is valid.
309  *
310  * Negate value in-place. The actual implementation of this function is in the piranha::math::negate_impl functor's
311  * call operator. The body of this function is equivalent to:
312  * @code
313  * negate_impl<T>{}(x);
314  * @endcode
315  * The result of the call operator of piranha::math::negate_impl is ignored.
316  *
317  * @param x value to be negated.
318  *
319  * @throws unspecified any exception thrown by the call operator of piranha::math::negate_impl.
320  */
321 template <typename T, detail::math_negate_enabler<T> = 0>
negate(T & x)322 inline void negate(T &x)
323 {
324     negate_impl<T>{}(x);
325 }
326 
327 /// Default functor for the implementation of piranha::math::multiply_accumulate().
328 /**
329  * This functor can be specialised via the \p std::enable_if mechanism.
330  */
331 template <typename T, typename = void>
332 struct multiply_accumulate_impl {
333 private:
334     // NOTE: as usual, we check the expression against const ref arguments.
335     template <typename U>
336     using addmul_t = decltype(std::declval<U &>() += std::declval<const U &>() * std::declval<const U &>());
337     template <typename U>
338     using enabler = enable_if_t<is_detected<addmul_t, U>::value, int>;
339 
340 public:
341     /// Call operator.
342     /**
343      * \note
344      * This call operator is enabled only if the expression <tt>x += y * z</tt> is well-formed.
345      *
346      * The body of the operator is equivalent to:
347      * @code
348      * x += y * z;
349      * @endcode
350      *
351      * @param x target value for accumulation.
352      * @param y first argument.
353      * @param z second argument.
354      *
355      * @throws unspecified any exception resulting from in-place addition or
356      * binary multiplication on the operands.
357      */
358     template <typename U, enabler<U> = 0>
operator ()piranha::math::multiply_accumulate_impl359     void operator()(U &x, const U &y, const U &z) const
360     {
361         x += y * z;
362     }
363 };
364 
365 #if defined(FP_FAST_FMA) && defined(FP_FAST_FMAF) && defined(FP_FAST_FMAL)
366 
367 inline namespace impl
368 {
369 
370 // Enabler for the fast floating-point implementation of multiply_accumulate().
371 template <typename T>
372 using math_multiply_accumulate_float_enabler = enable_if_t<std::is_floating_point<T>::value>;
373 } // namespace impl
374 
375 /// Specialisation of the implementation of piranha::math::multiply_accumulate() for floating-point types.
376 /**
377  * This functor is enabled only if the macros \p FP_FAST_FMA, \p FP_FAST_FMAF and \p FP_FAST_FMAL are defined.
378  */
379 template <typename T>
380 struct multiply_accumulate_impl<T, math_multiply_accumulate_float_enabler<T>> {
381     /// Call operator.
382     /**
383      * This implementation will use the \p std::fma() function.
384      *
385      * @param x target value for accumulation.
386      * @param y first argument.
387      * @param z second argument.
388      */
operator ()piranha::math::multiply_accumulate_impl389     void operator()(T &x, const T &y, const T &z) const
390     {
391         x = std::fma(y, z, x);
392     }
393 };
394 
395 #endif
396 } // namespace math
397 
398 inline namespace impl
399 {
400 
401 // Enabler for multiply_accumulate.
402 template <typename T>
403 using math_multiply_accumulate_t = decltype(
404     math::multiply_accumulate_impl<T>{}(std::declval<T &>(), std::declval<const T &>(), std::declval<const T &>()));
405 
406 template <typename T>
407 using math_multiply_accumulate_enabler = enable_if_t<is_detected<math_multiply_accumulate_t, T>::value, int>;
408 } // namespace impl
409 
410 namespace math
411 {
412 
413 /// Multiply-accumulate.
414 /**
415  * \note
416  * This function is enabled only if the expression <tt>multiply_accumulate_impl<T>{}(x, y, z)</tt> is valid.
417  *
418  * This function will set \p x to <tt>x + y * z</tt>. The actual implementation of this function is in the
419  * piranha::math::multiply_accumulate_impl functor's call operator. The body of this function is equivalent to:
420  * @code
421  * multiply_accumulate_impl<T>{}(x, y, z);
422  * @endcode
423  *
424  * @param x target value for accumulation.
425  * @param y first argument.
426  * @param z second argument.
427  *
428  * @throws unspecified any exception thrown by the call operator of piranha::math::multiply_accumulate_impl.
429  */
430 template <typename T, math_multiply_accumulate_enabler<T> = 0>
multiply_accumulate(T & x,const T & y,const T & z)431 inline void multiply_accumulate(T &x, const T &y, const T &z)
432 {
433     multiply_accumulate_impl<T>{}(x, y, z);
434 }
435 
436 /// Default functor for the implementation of piranha::math::cos().
437 /**
438  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
439  * the call operator, and will hence result in a compilation error when used.
440  */
441 template <typename T, typename Enable = void>
442 struct cos_impl {
443 };
444 
445 /// Specialisation of the piranha::math::cos() functor for floating-point types.
446 /**
447  * This specialisation is activated when \p T is a C++ floating-point type.
448  * The result will be computed via the standard <tt>std::cos()</tt> function.
449  */
450 template <typename T>
451 struct cos_impl<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
452     /// Call operator.
453     /**
454      * The cosine will be computed via <tt>std::cos()</tt>.
455      *
456      * @param x argument.
457      *
458      * @return cosine of \p x.
459      */
operator ()piranha::math::cos_impl460     T operator()(const T &x) const
461     {
462         return std::cos(x);
463     }
464 };
465 
466 /// Specialisation of the piranha::math::cos() functor for integral types.
467 /**
468  * This specialisation is activated when \p T is an integral type.
469  */
470 template <typename T>
471 struct cos_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
472     /// Call operator.
473     /**
474      * @param x argument.
475      *
476      * @return cosine of \p x.
477      *
478      * @throws std::invalid_argument if the argument is not zero.
479      */
operator ()piranha::math::cos_impl480     T operator()(const T &x) const
481     {
482         if (x == T(0)) {
483             return T(1);
484         }
485         piranha_throw(std::invalid_argument, "cannot compute the cosine of a non-zero integral");
486     }
487 };
488 } // namespace math
489 
490 namespace detail
491 {
492 
493 // Type for the result of math::cos().
494 template <typename T>
495 using math_cos_type_ = decltype(math::cos_impl<T>{}(std::declval<const T &>()));
496 
497 template <typename T>
498 using math_cos_type = typename std::enable_if<is_returnable<math_cos_type_<T>>::value, math_cos_type_<T>>::type;
499 } // namespace detail
500 
501 namespace math
502 {
503 
504 /// Cosine.
505 /**
506  * \note
507  * This function is enabled only if the expression <tt>cos_impl<T>{}(x)</tt> is valid, returning
508  * a type which satisfies piranha::is_returnable.
509  *
510  * Returns the cosine of \p x. The actual implementation of this function is in the piranha::math::cos_impl functor's
511  * call operator. The body of this function is equivalent to:
512  * @code
513  * return cos_impl<T>{}(x);
514  * @endcode
515  *
516  * @param x cosine argument.
517  *
518  * @return cosine of \p x.
519  *
520  * @throws unspecified any exception thrown by the call operator of the piranha::math::cos_impl functor.
521  */
522 template <typename T>
cos(const T & x)523 inline detail::math_cos_type<T> cos(const T &x)
524 {
525     return cos_impl<T>{}(x);
526 }
527 
528 /// Default functor for the implementation of piranha::math::sin().
529 /**
530  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
531  * the call operator, and will hence result in a compilation error when used.
532  */
533 template <typename T, typename Enable = void>
534 struct sin_impl {
535 };
536 
537 /// Specialisation of the piranha::math::sin() functor for floating-point types.
538 /**
539  * This specialisation is activated when \p T is a C++ floating-point type.
540  * The result will be computed via the standard <tt>std::sin()</tt> function.
541  */
542 template <typename T>
543 struct sin_impl<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
544     /// Call operator.
545     /**
546      * The sine will be computed via <tt>std::sin()</tt>.
547      *
548      * @param x argument.
549      *
550      * @return sine of \p x.
551      */
operator ()piranha::math::sin_impl552     T operator()(const T &x) const
553     {
554         return std::sin(x);
555     }
556 };
557 
558 /// Specialisation of the piranha::math::sin() functor for integral types.
559 /**
560  * This specialisation is activated when \p T is an integral type.
561  */
562 template <typename T>
563 struct sin_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
564     /// Call operator.
565     /**
566      * @param x argument.
567      *
568      * @return sine of \p x.
569      *
570      * @throws std::invalid_argument if the argument is not zero.
571      */
operator ()piranha::math::sin_impl572     T operator()(const T &x) const
573     {
574         if (x == T(0)) {
575             return T(0);
576         }
577         piranha_throw(std::invalid_argument, "cannot compute the sine of a non-zero integral");
578     }
579 };
580 } // namespace math
581 
582 namespace detail
583 {
584 
585 // Type for the result of math::sin().
586 template <typename T>
587 using math_sin_type_ = decltype(math::sin_impl<T>{}(std::declval<const T &>()));
588 
589 template <typename T>
590 using math_sin_type = typename std::enable_if<is_returnable<math_sin_type_<T>>::value, math_sin_type_<T>>::type;
591 } // namespace detail
592 
593 namespace math
594 {
595 
596 /// Sine.
597 /**
598  * \note
599  * This function is enabled only if the expression <tt>sin_impl<T>{}(x)</tt> is valid, returning
600  * a type which satisfies piranha::is_returnable.
601  *
602  * Returns the sine of \p x. The actual implementation of this function is in the piranha::math::sin_impl functor's
603  * call operator. The body of this function is equivalent to:
604  * @code
605  * return sin_impl<T>{}(x);
606  * @endcode
607  *
608  * @param x sine argument.
609  *
610  * @return sine of \p x.
611  *
612  * @throws unspecified any exception thrown by the call operator of the piranha::math::sin_impl functor.
613  */
614 template <typename T>
sin(const T & x)615 inline detail::math_sin_type<T> sin(const T &x)
616 {
617     return sin_impl<T>{}(x);
618 }
619 
620 /// Default functor for the implementation of piranha::math::partial().
621 /**
622  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
623  * the call operator, and will hence result in a compilation error when used.
624  */
625 template <typename T, typename Enable = void>
626 struct partial_impl {
627 };
628 
629 /// Specialisation of the piranha::math::partial() functor for arithmetic types.
630 /**
631  * This specialisation is activated when \p T is a C++ arithmetic type.
632  * The result will be zero.
633  */
634 template <typename T>
635 struct partial_impl<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {
636     /// Call operator.
637     /**
638      * @return an instance of \p T constructed from zero.
639      */
operator ()piranha::math::partial_impl640     T operator()(const T &, const std::string &) const
641     {
642         return T(0);
643     }
644 };
645 } // namespace math
646 
647 namespace detail
648 {
649 
650 // Return type for math::partial().
651 template <typename T>
652 using math_partial_type_
653     = decltype(math::partial_impl<T>{}(std::declval<const T &>(), std::declval<const std::string &>()));
654 
655 template <typename T>
656 using math_partial_type =
657     typename std::enable_if<is_returnable<math_partial_type_<T>>::value, math_partial_type_<T>>::type;
658 } // namespace detail
659 
660 namespace math
661 {
662 
663 /// Partial derivative.
664 /**
665  * \note
666  * This function is enabled only if the expression <tt>partial_impl<T>{}(x,str)</tt> is valid, returning a type that
667  * satisfies piranha::is_returnable.
668  *
669  * Return the partial derivative of \p x with respect to the symbolic quantity named \p str. The actual
670  * implementation of this function is in the piranha::math::partial_impl functor. The body of this function
671  * is equivalent to:
672  * @code
673  * return partial_impl<T>{}(x,str);
674  * @endcode
675  *
676  * @param x argument for the partial derivative.
677  * @param str name of the symbolic quantity with respect to which the derivative will be computed.
678  *
679  * @return partial derivative of \p x with respect to \p str.
680  *
681  * @throws unspecified any exception thrown by the call operator of piranha::math::partial_impl.
682  */
683 template <typename T>
partial(const T & x,const std::string & str)684 inline detail::math_partial_type<T> partial(const T &x, const std::string &str)
685 {
686     return partial_impl<T>{}(x, str);
687 }
688 
689 /// Default functor for the implementation of piranha::math::integrate().
690 /**
691  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
692  * the call operator, and will hence result in a compilation error when used.
693  */
694 template <typename T, typename Enable = void>
695 struct integrate_impl {
696 };
697 } // namespace math
698 
699 namespace detail
700 {
701 
702 // Return type for math::integrate().
703 template <typename T>
704 using math_integrate_type_
705     = decltype(math::integrate_impl<T>{}(std::declval<const T &>(), std::declval<const std::string &>()));
706 
707 template <typename T>
708 using math_integrate_type =
709     typename std::enable_if<is_returnable<math_integrate_type_<T>>::value, math_integrate_type_<T>>::type;
710 } // namespace detail
711 
712 namespace math
713 {
714 
715 /// Integration.
716 /**
717  * \note
718  * This function is enabled only if the expression <tt>integrate_impl<T>{}(x,str)</tt> is valid, returning a type that
719  * satisfies piranha::is_returnable.
720  *
721  * Return the antiderivative of \p x with respect to the symbolic quantity named \p str. The actual
722  * implementation of this function is in the piranha::math::integrate_impl functor. The body of this function
723  * is equivalent to:
724  * @code
725  * return integrate_impl<T>{}(x,str);
726  * @endcode
727  *
728  * @param x argument for the integration.
729  * @param str name of the symbolic quantity with respect to which the integration will be computed.
730  *
731  * @return antiderivative of \p x with respect to \p str.
732  *
733  * @throws unspecified any exception thrown by the call operator of piranha::math::integrate_impl.
734  */
735 template <typename T>
integrate(const T & x,const std::string & str)736 inline detail::math_integrate_type<T> integrate(const T &x, const std::string &str)
737 {
738     return integrate_impl<T>{}(x, str);
739 }
740 
741 /// Default functor for the implementation of piranha::math::evaluate().
742 /**
743  * This functor should be specialised via the \p std::enable_if mechanism.
744  */
745 template <typename T, typename U, typename Enable = void>
746 struct evaluate_impl {
747 private:
748     template <typename V>
749     using enabler = typename std::enable_if<std::is_copy_constructible<V>::value && is_returnable<V>::value, int>::type;
750 
751 public:
752     /// Call operator.
753     /**
754      * \note
755      * This operator is enabled only if \p V is copy-constructible and if it satisfies piranha::is_returnable.
756      *
757      * The default behaviour is to return the input value \p x unchanged.
758      *
759      * @param x evaluation argument.
760      *
761      * @return copy of \p x.
762      *
763      * @throws unspecified any exception thrown by the invoked copy constructor.
764      */
765     template <typename V, enabler<V> = 0>
766     V operator()(const V &x, const std::unordered_map<std::string, U> &) const
767     {
768         return x;
769     }
770 };
771 } // namespace math
772 
773 namespace detail
774 {
775 
776 // Return type for math::evaluate().
777 template <typename T, typename U>
778 using math_evaluate_type_ = decltype(
779     math::evaluate_impl<T, U>{}(std::declval<const T &>(), std::declval<const std::unordered_map<std::string, U> &>()));
780 
781 template <typename T, typename U>
782 using math_evaluate_type =
783     typename std::enable_if<is_returnable<math_evaluate_type_<T, U>>::value, math_evaluate_type_<T, U>>::type;
784 } // namespace detail
785 
786 namespace math
787 {
788 
789 /// Evaluation.
790 /**
791  * \note
792  * This function is enabled only if <tt>evaluate_impl<T,U>{}(x,dict)</tt> is a valid expression, returning
793  * a type which satisfies piranha::is_returnable.
794  *
795  * Evaluation is the simultaneous substitution of all symbolic arguments in an expression. The input dictionary \p dict
796  * specifies the quantity (value) that will be susbstituted for each argument (key), represented as a string.
797  * The actual implementation of this function is in the piranha::math::evaluate_impl functor.
798  * The body of this function is equivalent to:
799  * @code
800  * return evaluate_impl<T,U>{}(x,dict);
801  * @endcode
802  *
803  * @param x quantity that will be evaluated.
804  * @param dict dictionary that will be used to perform the substitution.
805  *
806  * @return \p x evaluated according to \p dict.
807  *
808  * @throws unspecified any exception thrown by the call operator of piranha::math::evaluate_impl.
809  */
810 template <typename U, typename T>
evaluate(const T & x,const std::unordered_map<std::string,U> & dict)811 inline detail::math_evaluate_type<T, U> evaluate(const T &x, const std::unordered_map<std::string, U> &dict)
812 {
813     return evaluate_impl<T, U>{}(x, dict);
814 }
815 
816 /// Default functor for the implementation of piranha::math::subs().
817 /**
818  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
819  * the call operator, and will hence result in a compilation error when used.
820  */
821 template <typename T, typename U, typename Enable = void>
822 struct subs_impl {
823 };
824 } // namespace math
825 
826 namespace detail
827 {
828 
829 // Return type for math::subs().
830 template <typename T, typename U>
831 using math_subs_type_ = decltype(
832     math::subs_impl<T, U>{}(std::declval<const T &>(), std::declval<const std::string &>(), std::declval<const U &>()));
833 
834 template <typename T, typename U>
835 using math_subs_type =
836     typename std::enable_if<is_returnable<math_subs_type_<T, U>>::value, math_subs_type_<T, U>>::type;
837 } // namespace detail
838 
839 namespace math
840 {
841 
842 /// Substitution.
843 /**
844  * \note
845  * This function is enabled only if <tt>subs_impl<T,U>{}(x,name,y)</tt> is a valid expression, returning
846  * a type which satisfies piranha::is_returnable.
847  *
848  * Substitute a symbolic variable with a generic object.
849  * The actual implementation of this function is in the piranha::math::subs_impl functor.
850  * The body of this function is equivalent to:
851  * @code
852  * return subs_impl<T,U>{}(x,name,y);
853  * @endcode
854  *
855  * @param x quantity that will be subject to substitution.
856  * @param name name of the symbolic variable that will be substituted.
857  * @param y object that will substitute the variable.
858  *
859  * @return \p x after substitution  of \p name with \p y.
860  *
861  * @throws unspecified any exception thrown by the call operator of piranha::math::subs_impl.
862  */
863 template <typename T, typename U>
subs(const T & x,const std::string & name,const U & y)864 inline detail::math_subs_type<T, U> subs(const T &x, const std::string &name, const U &y)
865 {
866     return subs_impl<T, U>{}(x, name, y);
867 }
868 
869 /// Default functor for the implementation of piranha::math::t_subs().
870 /**
871  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
872  * the call operator, and will hence result in a compilation error when used.
873  */
874 template <typename T, typename U, typename V, typename = void>
875 struct t_subs_impl {
876 };
877 } // namespace math
878 
879 namespace detail
880 {
881 
882 // Return type for math::t_subs().
883 template <typename T, typename U, typename V>
884 using math_t_subs_type_
885     = decltype(math::t_subs_impl<T, U, V>{}(std::declval<const T &>(), std::declval<const std::string &>(),
886                                             std::declval<const U &>(), std::declval<const V &>()));
887 
888 template <typename T, typename U, typename V>
889 using math_t_subs_type =
890     typename std::enable_if<is_returnable<math_t_subs_type_<T, U, V>>::value, math_t_subs_type_<T, U, V>>::type;
891 } // namespace detail
892 
893 namespace math
894 {
895 
896 /// Trigonometric substitution.
897 /**
898  * \note
899  * This function is enabled only if <tt>t_subs_impl<T,U,V>{}(x,name,c,s)</tt> is a valid expression, returning
900  * a type which satisfies piranha::is_returnable.
901  *
902  * Substitute the cosine and sine of a symbolic variable with generic objects.
903  * The actual implementation of this function is in the piranha::math::t_subs_impl functor.
904  * The body of this function is equivalent to:
905  * @code
906  * return t_subs_impl<T,U,V>{}(x,name,c,s);
907  * @endcode
908  *
909  * @param x quantity that will be subject to substitution.
910  * @param name name of the symbolic variable that will be substituted.
911  * @param c object that will substitute the cosine of the variable.
912  * @param s object that will substitute the sine of the variable.
913  *
914  * @return \p x after substitution of cosine and sine of \p name with \p c and \p s.
915  *
916  * @throws unspecified any exception thrown by the call operator of piranha::math::t_subs_impl.
917  */
918 template <typename T, typename U, typename V>
t_subs(const T & x,const std::string & name,const U & c,const V & s)919 inline detail::math_t_subs_type<T, U, V> t_subs(const T &x, const std::string &name, const U &c, const V &s)
920 {
921     return t_subs_impl<T, U, V>{}(x, name, c, s);
922 }
923 
924 /// Default functor for the implementation of piranha::math::abs().
925 /**
926  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
927  * the call operator, and will hence result in a compilation error when used.
928  */
929 template <typename T, typename Enable = void>
930 struct abs_impl {
931 };
932 } // namespace math
933 
934 namespace detail
935 {
936 
937 template <typename T>
938 using abs_arith_enabler = typename std::enable_if<(std::is_signed<T>::value && std::is_integral<T>::value)
939                                                   || (std::is_unsigned<T>::value && std::is_integral<T>::value)
940                                                   || std::is_floating_point<T>::value>::type;
941 }
942 
943 namespace math
944 {
945 
946 /// Specialisation of the piranha::math::abs() functor for signed and unsigned integer types, and floating-point types.
947 /**
948  * This specialisation is activated when \p T is a signed or unsigned integer type, or a floating-point type.
949  * The result will be computed via \p std::abs() for floating-point and signed integer types,
950  * while for unsigned integer types it will be the input value unchanged.
951  */
952 template <typename T>
953 struct abs_impl<T, detail::abs_arith_enabler<T>> {
954 private:
955     template <typename U>
implpiranha::math::abs_impl956     static U impl(const U &x, typename std::enable_if<std::is_floating_point<U>::value>::type * = nullptr)
957     {
958         return std::abs(x);
959     }
960     // NOTE: use decltype here so, in the remote case we are dealing with an extended integer types (for which
961     // std::abs()
962     // will not exist and cast to long long might be lossy), the overload will be discarded.
963     template <typename U>
implpiranha::math::abs_impl964     static auto impl(const U &x,
965                      typename std::enable_if<std::is_integral<U>::value && std::is_signed<U>::value>::type * = nullptr)
966         -> decltype(static_cast<U>(std::abs(static_cast<long long>(x))))
967     {
968         // Cast to long long in order to avoid conversion derps when dealing with chars.
969         return static_cast<U>(std::abs(static_cast<long long>(x)));
970     }
971     template <typename U>
implpiranha::math::abs_impl972     static U impl(const U &x,
973                   typename std::enable_if<std::is_integral<U>::value && std::is_unsigned<U>::value>::type * = nullptr)
974     {
975         return x;
976     }
977 
978 public:
979     /// Call operator.
980     /**
981      * @param x input parameter.
982      *
983      * @return absolute value of \p x.
984      */
operator ()piranha::math::abs_impl985     auto operator()(const T &x) const -> decltype(impl(x))
986     {
987         return impl(x);
988     }
989 };
990 
991 /// Absolute value.
992 /**
993  * The actual implementation of this function is in the piranha::math::abs_impl functor.
994  *
995  * @param x quantity whose absolute value will be calculated.
996  *
997  * @return absolute value of \p x.
998  *
999  * @throws unspecified any exception thrown by the call operator of piranha::math::abs_impl.
1000  */
1001 template <typename T>
abs(const T & x)1002 inline auto abs(const T &x) -> decltype(abs_impl<T>()(x))
1003 {
1004     return abs_impl<T>()(x);
1005 }
1006 } // namespace math
1007 
1008 /// Type trait to detect the presence of the piranha::math::is_zero() function.
1009 /**
1010  * The type trait will be \p true if piranha::math::is_zero() can be successfully called on instances of \p T.
1011  */
1012 template <typename T>
1013 class has_is_zero : detail::sfinae_types
1014 {
1015     typedef typename std::decay<T>::type Td;
1016     template <typename T1>
1017     static auto test(const T1 &t) -> decltype(math::is_zero(t), void(), yes());
1018     static no test(...);
1019     static const bool implementation_defined = std::is_same<decltype(test(std::declval<Td>())), yes>::value;
1020 
1021 public:
1022     /// Value of the type trait.
1023     static const bool value = implementation_defined;
1024 };
1025 
1026 // Static init.
1027 template <typename T>
1028 const bool has_is_zero<T>::value;
1029 
1030 /// Type trait to detect the presence of the piranha::math::negate function.
1031 /**
1032  * The type trait will be \p true if piranha::math::negate can be successfully called on instances of \p T
1033  * stripped of reference qualifiers, \p false otherwise.
1034  */
1035 template <typename T>
1036 class has_negate : detail::sfinae_types
1037 {
1038     template <typename T1>
1039     static auto test(T1 &t) -> decltype(math::negate(t), void(), yes());
1040     static no test(...);
1041     static const bool implementation_defined = std::is_same<decltype(test(std::declval<T &>())), yes>::value;
1042 
1043 public:
1044     /// Value of the type trait.
1045     static const bool value = implementation_defined;
1046 };
1047 
1048 template <typename T>
1049 const bool has_negate<T>::value;
1050 
1051 namespace detail
1052 {
1053 
1054 #if !defined(PIRANHA_DOXYGEN_INVOKED)
1055 
1056 // Type definition and type checking for the output of Poisson brackets.
1057 template <typename T>
1058 using pbracket_type_tmp = decltype(math::partial(std::declval<const T &>(), std::string())
1059                                    * math::partial(std::declval<const T &>(), std::string()));
1060 
1061 template <typename T, typename = void>
1062 struct pbracket_type_ {
1063 };
1064 
1065 template <typename T>
1066 struct pbracket_type_<
1067     T, typename std::enable_if<std::is_same<decltype(std::declval<const pbracket_type_tmp<T> &>()
1068                                                      + std::declval<const pbracket_type_tmp<T> &>()),
1069                                             pbracket_type_tmp<T>>::value
1070                                && std::is_same<decltype(std::declval<const pbracket_type_tmp<T> &>()
1071                                                         - std::declval<const pbracket_type_tmp<T> &>()),
1072                                                pbracket_type_tmp<T>>::value
1073                                && std::is_constructible<pbracket_type_tmp<T>, int>::value
1074                                && std::is_assignable<pbracket_type_tmp<T> &, pbracket_type_tmp<T>>::value>::type> {
1075     using type = pbracket_type_tmp<T>;
1076 };
1077 
1078 // The final typedef.
1079 template <typename T>
1080 using pbracket_type = typename pbracket_type_<T>::type;
1081 
1082 #endif
1083 } // namespace detail
1084 
1085 namespace math
1086 {
1087 
1088 /// Poisson bracket.
1089 /**
1090  * \note
1091  * This template function is enabled only if \p T is differentiable and the arithmetic operations needed to compute the
1092  * brackets
1093  * are supported by the types involved in the computation.
1094  *
1095  * The Poisson bracket of \p f and \p g with respect to the list of momenta \p p_list and coordinates \p q_list
1096  * is defined as:
1097  * \f[
1098  * \left\{f,g\right\} = \sum_{i=1}^{N}
1099  * \left[
1100  * \frac{\partial f}{\partial q_{i}} \frac{\partial g}{\partial p_{i}} -
1101  * \frac{\partial f}{\partial p_{i}} \frac{\partial g}{\partial q_{i}}
1102  * \right],
1103  * \f]
1104  * where \f$ p_i \f$ and \f$ q_i \f$ are the elements of \p p_list and \p q_list.
1105  *
1106  * @param f first argument.
1107  * @param g second argument.
1108  * @param p_list list of the names of momenta.
1109  * @param q_list list of the names of coordinates.
1110  *
1111  * @return the poisson bracket of \p f and \p g with respect to \p p_list and \p q_list.
1112  *
1113  * @throws std::invalid_argument if the sizes of \p p_list and \p q_list differ or if
1114  * \p p_list or \p q_list contain duplicate entries.
1115  * @throws unspecified any exception thrown by piranha::math::partial() or by the invoked arithmetic operators,
1116  * constructors and assignment operators.
1117  */
1118 template <typename T>
pbracket(const T & f,const T & g,const std::vector<std::string> & p_list,const std::vector<std::string> & q_list)1119 inline detail::pbracket_type<T> pbracket(const T &f, const T &g, const std::vector<std::string> &p_list,
1120                                          const std::vector<std::string> &q_list)
1121 {
1122     using return_type = detail::pbracket_type<T>;
1123     if (p_list.size() != q_list.size()) {
1124         piranha_throw(std::invalid_argument, "the number of coordinates is different from the number of momenta");
1125     }
1126     if (std::unordered_set<std::string>(p_list.begin(), p_list.end()).size() != p_list.size()) {
1127         piranha_throw(std::invalid_argument, "the list of momenta contains duplicate entries");
1128     }
1129     if (std::unordered_set<std::string>(q_list.begin(), q_list.end()).size() != q_list.size()) {
1130         piranha_throw(std::invalid_argument, "the list of coordinates contains duplicate entries");
1131     }
1132     return_type retval = return_type(0);
1133     for (decltype(p_list.size()) i = 0u; i < p_list.size(); ++i) {
1134         // NOTE: could use multadd/sub here, if we implement it for series.
1135         retval = retval + partial(f, q_list[i]) * partial(g, p_list[i]);
1136         retval = retval - partial(f, p_list[i]) * partial(g, q_list[i]);
1137     }
1138     return retval;
1139 }
1140 } // namespace math
1141 
1142 /// Detect piranha::math::pbracket().
1143 /**
1144  * The type trait will be \p true if piranha::math::pbracket() can be used on instances of type \p T,
1145  * \p false otherwise.
1146  */
1147 template <typename T>
1148 class has_pbracket : detail::sfinae_types
1149 {
1150     using v_string = std::vector<std::string>;
1151     template <typename T1>
1152     static auto test(const T1 &x)
1153         -> decltype(math::pbracket(x, x, std::declval<v_string const &>(), std::declval<v_string const &>()), void(),
1154                     yes());
1155     static no test(...);
1156 
1157 public:
1158     /// Value of the type trait.
1159     static const bool value = std::is_same<decltype(test(std::declval<T>())), yes>::value;
1160 };
1161 
1162 template <typename T>
1163 const bool has_pbracket<T>::value;
1164 
1165 namespace detail
1166 {
1167 
1168 template <typename T>
1169 using is_canonical_enabler = typename std::enable_if<has_pbracket<T>::value && has_is_zero<pbracket_type<T>>::value
1170                                                          && std::is_constructible<pbracket_type<T>, int>::value
1171                                                          && is_equality_comparable<pbracket_type<T>>::value,
1172                                                      int>::type;
1173 
1174 template <typename T>
is_canonical_impl(const std::vector<T const * > & new_p,const std::vector<T const * > & new_q,const std::vector<std::string> & p_list,const std::vector<std::string> & q_list)1175 inline bool is_canonical_impl(const std::vector<T const *> &new_p, const std::vector<T const *> &new_q,
1176                               const std::vector<std::string> &p_list, const std::vector<std::string> &q_list)
1177 {
1178     using p_type = decltype(math::pbracket(*new_q[0], *new_p[0], p_list, q_list));
1179     if (p_list.size() != q_list.size()) {
1180         piranha_throw(std::invalid_argument, "the number of coordinates is different from the number of momenta");
1181     }
1182     if (new_p.size() != new_q.size()) {
1183         piranha_throw(std::invalid_argument,
1184                       "the number of new coordinates is different from the number of new momenta");
1185     }
1186     if (p_list.size() != new_p.size()) {
1187         piranha_throw(std::invalid_argument, "the number of new momenta is different from the number of momenta");
1188     }
1189     if (std::unordered_set<std::string>(p_list.begin(), p_list.end()).size() != p_list.size()) {
1190         piranha_throw(std::invalid_argument, "the list of momenta contains duplicate entries");
1191     }
1192     if (std::unordered_set<std::string>(q_list.begin(), q_list.end()).size() != q_list.size()) {
1193         piranha_throw(std::invalid_argument, "the list of coordinates contains duplicate entries");
1194     }
1195     const auto size = new_p.size();
1196     for (decltype(new_p.size()) i = 0u; i < size; ++i) {
1197         for (decltype(new_p.size()) j = 0u; j < size; ++j) {
1198             // NOTE: no need for actually doing computations when i == j.
1199             if (i != j && !math::is_zero(math::pbracket(*new_p[i], *new_p[j], p_list, q_list))) {
1200                 return false;
1201             }
1202             if (i != j && !math::is_zero(math::pbracket(*new_q[i], *new_q[j], p_list, q_list))) {
1203                 return false;
1204             }
1205             // Poisson bracket needs to be zero for i != j, one for i == j.
1206             // NOTE: cast from bool to int is always 0 or 1.
1207             if (math::pbracket(*new_q[i], *new_p[j], p_list, q_list) != p_type(static_cast<int>(i == j))) {
1208                 return false;
1209             }
1210         }
1211     }
1212     return true;
1213 }
1214 } // namespace detail
1215 
1216 namespace math
1217 {
1218 
1219 /// Check if a transformation is canonical.
1220 /**
1221  * \note
1222  * This function is enabled only if all the following requirements are met:
1223  * - \p T satisfies piranha::has_pbracket,
1224  * - the output type of piranha::has_pbracket for \p T satisfies piranha::has_is_zero, it is constructible from \p int
1225  *   and it is equality comparable.
1226  *
1227  * This function will check if a transformation of Hamiltonian momenta and coordinates is canonical using the Poisson
1228  * bracket test.
1229  * The transformation is expressed as two separate collections of objects, \p new_p and \p new_q, representing the new
1230  * momenta
1231  * and coordinates as functions of the old momenta \p p_list and \p q_list.
1232  *
1233  * @param new_p list of objects representing the new momenta.
1234  * @param new_q list of objects representing the new coordinates.
1235  * @param p_list list of names of the old momenta.
1236  * @param q_list list of names of the old coordinates.
1237  *
1238  * @return \p true if the transformation is canonical, \p false otherwise.
1239  *
1240  * @throws std::invalid_argument if the sizes of the four input arguments are not the same or if either \p p_list or \p
1241  * q_list
1242  * contain duplicate entries.
1243  * @throws unspecified any exception thrown by:
1244  * - piranha::math::pbracket(),
1245  * - construction and comparison of objects of the type returned by piranha::math::pbracket(),
1246  * - piranha::math::is_zero(),
1247  * - memory errors in standard containers.
1248  */
1249 template <typename T, detail::is_canonical_enabler<T> = 0>
transformation_is_canonical(const std::vector<T> & new_p,const std::vector<T> & new_q,const std::vector<std::string> & p_list,const std::vector<std::string> & q_list)1250 inline bool transformation_is_canonical(const std::vector<T> &new_p, const std::vector<T> &new_q,
1251                                         const std::vector<std::string> &p_list, const std::vector<std::string> &q_list)
1252 {
1253     std::vector<T const *> pv, qv;
1254     std::transform(new_p.begin(), new_p.end(), std::back_inserter(pv), [](const T &p) { return &p; });
1255     std::transform(new_q.begin(), new_q.end(), std::back_inserter(qv), [](const T &q) { return &q; });
1256     return detail::is_canonical_impl(pv, qv, p_list, q_list);
1257 }
1258 
1259 // clang-format off
1260 /// Check if a transformation is canonical (alternative overload).
1261 /**
1262  * @param new_p list of objects representing the new momenta.
1263  * @param new_q list of objects representing the new coordinates.
1264  * @param p_list list of names of the old momenta.
1265  * @param q_list list of names of the old coordinates.
1266  *
1267  * @return the output of transformation_is_canonical(const std::vector<T> &, const std::vector<T> &, const std::vector<std::string> &, const std::vector<std::string> &).
1268  *
1269  * @throws unspecified any exception thrown by transformation_is_canonical(const std::vector<T> &, const std::vector<T> &, const std::vector<std::string> &, const std::vector<std::string> &).
1270  */
1271 // clang-format on
1272 template <typename T, detail::is_canonical_enabler<T> = 0>
transformation_is_canonical(std::initializer_list<T> new_p,std::initializer_list<T> new_q,const std::vector<std::string> & p_list,const std::vector<std::string> & q_list)1273 inline bool transformation_is_canonical(std::initializer_list<T> new_p, std::initializer_list<T> new_q,
1274                                         const std::vector<std::string> &p_list, const std::vector<std::string> &q_list)
1275 {
1276     std::vector<T const *> pv, qv;
1277     std::transform(new_p.begin(), new_p.end(), std::back_inserter(pv), [](const T &p) { return &p; });
1278     std::transform(new_q.begin(), new_q.end(), std::back_inserter(qv), [](const T &q) { return &q; });
1279     return detail::is_canonical_impl(pv, qv, p_list, q_list);
1280 }
1281 
1282 /// Default functor for the implementation of piranha::math::degree().
1283 /**
1284  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
1285  * the call operator, and will hence result in a compilation error when used.
1286  *
1287  * Note that the implementation of this functor requires two overloaded call operators, one for the unary form
1288  * of piranha::math::degree() (the total degree), the other for the binary form of piranha::math::degree()
1289  * (the partial degree).
1290  */
1291 template <typename T, typename Enable = void>
1292 struct degree_impl {
1293 };
1294 
1295 /// Total degree.
1296 /**
1297  * Return the total degree (as in polynomial degree).
1298  *
1299  * The actual implementation of this function is in the piranha::math::degree_impl functor.
1300  *
1301  * @param x object whose degree will be computed.
1302  *
1303  * @return total degree.
1304  *
1305  * @throws unspecified any exception thrown by the call operator of piranha::math::degree_impl.
1306  */
1307 template <typename T>
degree(const T & x)1308 inline auto degree(const T &x) -> decltype(degree_impl<T>()(x))
1309 {
1310     return degree_impl<T>()(x);
1311 }
1312 
1313 /// Partial degree.
1314 /**
1315  * Return the partial degree (as in polynomial degree, but only a set of variables is considered in the computation).
1316  *
1317  * The actual implementation of this function is in the piranha::math::degree_impl functor.
1318  *
1319  * @param x object whose partial degree will be computed.
1320  * @param names names of the variables that will be considered in the computation.
1321  *
1322  * @return partial degree.
1323  *
1324  * @throws unspecified any exception thrown by the call operator of piranha::math::degree_impl.
1325  */
1326 template <typename T>
degree(const T & x,const std::vector<std::string> & names)1327 inline auto degree(const T &x, const std::vector<std::string> &names) -> decltype(degree_impl<T>()(x, names))
1328 {
1329     return degree_impl<T>()(x, names);
1330 }
1331 
1332 /// Default functor for the implementation of piranha::math::ldegree().
1333 /**
1334  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
1335  * the call operator, and will hence result in a compilation error when used.
1336  *
1337  * Note that the implementation of this functor requires two overloaded call operators, one for the unary form
1338  * of piranha::math::ldegree() (the total low degree), the other for the binary form of piranha::math::ldegree()
1339  * (the partial low degree).
1340  */
1341 template <typename T, typename Enable = void>
1342 struct ldegree_impl {
1343 };
1344 
1345 /// Total low degree.
1346 /**
1347  * Return the total low degree (as in polynomial low degree).
1348  *
1349  * The actual implementation of this function is in the piranha::math::ldegree_impl functor.
1350  *
1351  * @param x object whose low degree will be computed.
1352  *
1353  * @return total low degree.
1354  *
1355  * @throws unspecified any exception thrown by the call operator of piranha::math::ldegree_impl.
1356  */
1357 template <typename T>
ldegree(const T & x)1358 inline auto ldegree(const T &x) -> decltype(ldegree_impl<T>()(x))
1359 {
1360     return ldegree_impl<T>()(x);
1361 }
1362 
1363 /// Partial low degree.
1364 /**
1365  * Return the partial low degree (as in polynomial low degree, but only a set of variables is considered in the
1366  * computation).
1367  *
1368  * The actual implementation of this function is in the piranha::math::ldegree_impl functor.
1369  *
1370  * @param x object whose partial low degree will be computed.
1371  * @param names names of the variables that will be considered in the computation.
1372  *
1373  * @return partial low degree.
1374  *
1375  * @throws unspecified any exception thrown by the call operator of piranha::math::ldegree_impl.
1376  */
1377 template <typename T>
ldegree(const T & x,const std::vector<std::string> & names)1378 inline auto ldegree(const T &x, const std::vector<std::string> &names) -> decltype(ldegree_impl<T>()(x, names))
1379 {
1380     return ldegree_impl<T>()(x, names);
1381 }
1382 
1383 /// Default functor for the implementation of piranha::math::t_degree().
1384 /**
1385  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
1386  * the call operator, and will hence result in a compilation error when used.
1387  *
1388  * Note that the implementation of this functor requires two overloaded call operators, one for the unary form
1389  * of piranha::math::t_degree() (the total trigonometric degree), the other for the binary form of
1390  * piranha::math::t_degree()
1391  * (the partial trigonometric degree).
1392  */
1393 template <typename T, typename Enable = void>
1394 struct t_degree_impl {
1395 };
1396 
1397 /// Total trigonometric degree.
1398 /**
1399  * A type exposing a trigonometric degree property, in analogy with the concept of polynomial degree,
1400  * should be a linear combination of real or complex trigonometric functions. For instance, the Poisson series
1401  * \f[
1402  * 2\cos\left(3x+y\right) + 3\cos\left(2x-y\right)
1403  * \f]
1404  * has a trigonometric degree of 3+1=4.
1405  *
1406  * The actual implementation of this function is in the piranha::math::t_degree_impl functor.
1407  *
1408  * @param x object whose trigonometric degree will be computed.
1409  *
1410  * @return total trigonometric degree.
1411  *
1412  * @throws unspecified any exception thrown by the call operator of piranha::math::t_degree_impl.
1413  */
1414 template <typename T>
t_degree(const T & x)1415 inline auto t_degree(const T &x) -> decltype(t_degree_impl<T>()(x))
1416 {
1417     return t_degree_impl<T>()(x);
1418 }
1419 
1420 /// Partial trigonometric degree.
1421 /**
1422  * The partial trigonometric degree is the trigonometric degree when only certain variables are considered in
1423  * the computation.
1424  *
1425  * The actual implementation of this function is in the piranha::math::t_degree_impl functor.
1426  *
1427  * @param x object whose trigonometric degree will be computed.
1428  * @param names names of the variables that will be considered in the computation of the degree.
1429  *
1430  * @return partial trigonometric degree.
1431  *
1432  * @throws unspecified any exception thrown by the call operator of piranha::math::t_degree_impl.
1433  *
1434  * @see piranha::math::t_degree().
1435  */
1436 template <typename T>
t_degree(const T & x,const std::vector<std::string> & names)1437 inline auto t_degree(const T &x, const std::vector<std::string> &names) -> decltype(t_degree_impl<T>()(x, names))
1438 {
1439     return t_degree_impl<T>()(x, names);
1440 }
1441 
1442 /// Default functor for the implementation of piranha::math::t_ldegree().
1443 /**
1444  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
1445  * the call operator, and will hence result in a compilation error when used.
1446  *
1447  * Note that the implementation of this functor requires two overloaded call operators, one for the unary form
1448  * of piranha::math::t_ldegree() (the total trigonometric low degree), the other for the binary form of
1449  * piranha::math::t_ldegree()
1450  * (the partial trigonometric low degree).
1451  */
1452 template <typename T, typename Enable = void>
1453 struct t_ldegree_impl {
1454 };
1455 
1456 /// Total trigonometric low degree.
1457 /**
1458  * A type exposing a trigonometric low degree property, in analogy with the concept of polynomial low degree,
1459  * should be a linear combination of real or complex trigonometric functions. For instance, the Poisson series
1460  * \f[
1461  * 2\cos\left(3x+y\right) + 3\cos\left(2x-y\right)
1462  * \f]
1463  * has a trigonometric low degree of 2-1=1.
1464  *
1465  * The actual implementation of this function is in the piranha::math::t_ldegree_impl functor.
1466  *
1467  * @param x object whose trigonometric low degree will be computed.
1468  *
1469  * @return total trigonometric low degree.
1470  *
1471  * @throws unspecified any exception thrown by the call operator of piranha::math::t_ldegree_impl.
1472  */
1473 template <typename T>
t_ldegree(const T & x)1474 inline auto t_ldegree(const T &x) -> decltype(t_ldegree_impl<T>()(x))
1475 {
1476     return t_ldegree_impl<T>()(x);
1477 }
1478 
1479 /// Partial trigonometric low degree.
1480 /**
1481  * The partial trigonometric low degree is the trigonometric low degree when only certain variables are considered in
1482  * the computation.
1483  *
1484  * The actual implementation of this function is in the piranha::math::t_ldegree_impl functor.
1485  *
1486  * @param x object whose trigonometric low degree will be computed.
1487  * @param names names of the variables that will be considered in the computation of the degree.
1488  *
1489  * @return partial trigonometric low degree.
1490  *
1491  * @throws unspecified any exception thrown by the call operator of piranha::math::t_ldegree_impl.
1492  *
1493  * @see piranha::math::t_ldegree().
1494  */
1495 template <typename T>
t_ldegree(const T & x,const std::vector<std::string> & names)1496 inline auto t_ldegree(const T &x, const std::vector<std::string> &names) -> decltype(t_ldegree_impl<T>()(x, names))
1497 {
1498     return t_ldegree_impl<T>()(x, names);
1499 }
1500 
1501 /// Default functor for the implementation of piranha::math::t_order().
1502 /**
1503  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
1504  * the call operator, and will hence result in a compilation error when used.
1505  *
1506  * Note that the implementation of this functor requires two overloaded call operators, one for the unary form
1507  * of piranha::math::t_order() (the total trigonometric order), the other for the binary form of
1508  * piranha::math::t_order()
1509  * (the partial trigonometric order).
1510  */
1511 template <typename T, typename Enable = void>
1512 struct t_order_impl {
1513 };
1514 
1515 /// Total trigonometric order.
1516 /**
1517  * A type exposing a trigonometric order property should be a linear combination of real or complex trigonometric
1518  * functions.
1519  * The order is computed in a way similar to the trigonometric degree, with the key difference that the absolute values
1520  * of
1521  * the trigonometric degrees of each variable are considered in the computation. For instance, the Poisson series
1522  * \f[
1523  * 2\cos\left(3x+y\right) + 3\cos\left(2x-y\right)
1524  * \f]
1525  * has a trigonometric order of abs(3)+abs(1)=4.
1526  *
1527  * The actual implementation of this function is in the piranha::math::t_order_impl functor.
1528  *
1529  * @param x object whose trigonometric order will be computed.
1530  *
1531  * @return total trigonometric order.
1532  *
1533  * @throws unspecified any exception thrown by the call operator of piranha::math::t_order_impl.
1534  */
1535 template <typename T>
t_order(const T & x)1536 inline auto t_order(const T &x) -> decltype(t_order_impl<T>()(x))
1537 {
1538     return t_order_impl<T>()(x);
1539 }
1540 
1541 /// Partial trigonometric order.
1542 /**
1543  * The partial trigonometric order is the trigonometric order when only certain variables are considered in
1544  * the computation.
1545  *
1546  * The actual implementation of this function is in the piranha::math::t_order_impl functor.
1547  *
1548  * @param x object whose trigonometric order will be computed.
1549  * @param names names of the variables that will be considered in the computation of the order.
1550  *
1551  * @return partial trigonometric order.
1552  *
1553  * @throws unspecified any exception thrown by the call operator of piranha::math::t_order_impl.
1554  *
1555  * @see piranha::math::t_order().
1556  */
1557 template <typename T>
t_order(const T & x,const std::vector<std::string> & names)1558 inline auto t_order(const T &x, const std::vector<std::string> &names) -> decltype(t_order_impl<T>()(x, names))
1559 {
1560     return t_order_impl<T>()(x, names);
1561 }
1562 
1563 /// Default functor for the implementation of piranha::math::t_lorder().
1564 /**
1565  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
1566  * the call operator, and will hence result in a compilation error when used.
1567  *
1568  * Note that the implementation of this functor requires two overloaded call operators, one for the unary form
1569  * of piranha::math::t_lorder() (the total trigonometric low order), the other for the binary form of
1570  * piranha::math::t_lorder()
1571  * (the partial trigonometric low order).
1572  */
1573 template <typename T, typename Enable = void>
1574 struct t_lorder_impl {
1575 };
1576 
1577 /// Total trigonometric low order.
1578 /**
1579  * A type exposing a trigonometric low order property should be a linear combination of real or complex trigonometric
1580  * functions.
1581  * The low order is computed in a way similar to the trigonometric low degree, with the key difference that the absolute
1582  * values of
1583  * the trigonometric degrees of each variable are considered in the computation. For instance, the Poisson series
1584  * \f[
1585  * 2\cos\left(3x+y\right) + 3\cos\left(2x-y\right)
1586  * \f]
1587  * has a trigonometric low order of abs(2)+abs(1)=3.
1588  *
1589  * The actual implementation of this function is in the piranha::math::t_lorder_impl functor.
1590  *
1591  * @param x object whose trigonometric low order will be computed.
1592  *
1593  * @return total trigonometric low order.
1594  *
1595  * @throws unspecified any exception thrown by the call operator of piranha::math::t_lorder_impl.
1596  */
1597 template <typename T>
t_lorder(const T & x)1598 inline auto t_lorder(const T &x) -> decltype(t_lorder_impl<T>()(x))
1599 {
1600     return t_lorder_impl<T>()(x);
1601 }
1602 
1603 /// Partial trigonometric low order.
1604 /**
1605  * The partial trigonometric low order is the trigonometric low order when only certain variables are considered in
1606  * the computation.
1607  *
1608  * The actual implementation of this function is in the piranha::math::t_lorder_impl functor.
1609  *
1610  * @param x object whose trigonometric low order will be computed.
1611  * @param names names of the variables that will be considered in the computation of the order.
1612  *
1613  * @return partial trigonometric low order.
1614  *
1615  * @throws unspecified any exception thrown by the call operator of piranha::math::t_lorder_impl.
1616  *
1617  * @see piranha::math::t_lorder().
1618  */
1619 template <typename T>
t_lorder(const T & x,const std::vector<std::string> & names)1620 inline auto t_lorder(const T &x, const std::vector<std::string> &names) -> decltype(t_lorder_impl<T>()(x, names))
1621 {
1622     return t_lorder_impl<T>()(x, names);
1623 }
1624 
1625 /// Implementation of the piranha::math::truncate_degree() functor.
1626 /**
1627  * The default implementation does not define any call operator.
1628  */
1629 template <typename T, typename U, typename = void>
1630 struct truncate_degree_impl {
1631 };
1632 } // namespace math
1633 
1634 namespace detail
1635 {
1636 
1637 // Enablers for the degree truncation methods.
1638 template <typename T, typename U>
1639 using truncate_degree_enabler = typename std::enable_if<
1640     std::is_same<decltype(math::truncate_degree_impl<T, U>()(std::declval<const T &>(), std::declval<const U &>())),
1641                  T>::value,
1642     int>::type;
1643 
1644 template <typename T, typename U>
1645 using truncate_pdegree_enabler = typename std::enable_if<
1646     std::is_same<decltype(math::truncate_degree_impl<T, U>()(std::declval<const T &>(), std::declval<const U &>(),
1647                                                              std::declval<const std::vector<std::string> &>())),
1648                  T>::value,
1649     int>::type;
1650 } // namespace detail
1651 
1652 namespace math
1653 {
1654 
1655 /// Truncation based on the total degree.
1656 /**
1657  * This method is used to eliminate from the input argument \p x all the parts
1658  * whose total degree is greater than \p max_degree.
1659  *
1660  * The actual implementation of this function is in the piranha::math::truncate_degree_impl functor.
1661  *
1662  * The body of this function is equivalent to:
1663 @code
1664 return truncate_degree_impl<T,U>()(x,max_degree);
1665 @endcode
1666  * The call operator of piranha::math::truncate_degree_impl is required to return type \p T, otherwise
1667  * this function will be disabled.
1668  *
1669  * @param x object which will be subject to truncation.
1670  * @param max_degree maximum allowed total degree in the output.
1671  *
1672  * @return the truncated counterpart of \p x.
1673  *
1674  * @throws unspecified any exception thrown by the call operator of piranha::math::truncate_degree_impl.
1675  */
1676 template <typename T, typename U, detail::truncate_degree_enabler<T, U> = 0>
truncate_degree(const T & x,const U & max_degree)1677 inline T truncate_degree(const T &x, const U &max_degree)
1678 {
1679     return truncate_degree_impl<T, U>()(x, max_degree);
1680 }
1681 
1682 /// Truncation based on the partial degree.
1683 /**
1684  * This method is used to eliminate from the input argument \p x all the parts
1685  * whose partial degree is greater than \p max_degree.
1686  *
1687  * The actual implementation of this function is in the piranha::math::truncate_degree_impl functor.
1688  *
1689  * The body of this function is equivalent to:
1690 @code
1691 return truncate_degree_impl<T,U>()(x,max_degree,names);
1692 @endcode
1693  * The call operator of piranha::math::truncate_degree_impl is required to return type \p T, otherwise
1694  * this function will be disabled.
1695  *
1696  * @param x object which will be subject to truncation.
1697  * @param max_degree maximum allowed partial degree in the output.
1698  * @param names names of the variables that will be considered in the computation of the partial degree.
1699  *
1700  * @return the truncated counterpart of \p x.
1701  *
1702  * @throws unspecified any exception thrown by the call operator of piranha::math::truncate_degree_impl.
1703  */
1704 template <typename T, typename U, detail::truncate_pdegree_enabler<T, U> = 0>
truncate_degree(const T & x,const U & max_degree,const std::vector<std::string> & names)1705 inline T truncate_degree(const T &x, const U &max_degree, const std::vector<std::string> &names)
1706 {
1707     return truncate_degree_impl<T, U>()(x, max_degree, names);
1708 }
1709 } // namespace math
1710 
1711 /// Type trait to detect if types can be used in piranha::math::truncate_degree().
1712 /**
1713  * The type trait will be true if instances of types \p T and \p U can be used as arguments of
1714  * piranha::math::truncate_degree()
1715  * (both in the binary and ternary version of the function).
1716  */
1717 template <typename T, typename U>
1718 class has_truncate_degree : detail::sfinae_types
1719 {
1720     template <typename T1, typename U1>
1721     static auto test1(const T1 &t, const U1 &u) -> decltype(math::truncate_degree(t, u), void(), yes());
1722     static no test1(...);
1723     template <typename T1, typename U1>
1724     static auto test2(const T1 &t, const U1 &u)
1725         -> decltype(math::truncate_degree(t, u, std::declval<const std::vector<std::string> &>()), void(), yes());
1726     static no test2(...);
1727 
1728 public:
1729     /// Value of the type trait.
1730     static const bool value = std::is_same<decltype(test1(std::declval<T>(), std::declval<U>())), yes>::value
1731                               && std::is_same<decltype(test2(std::declval<T>(), std::declval<U>())), yes>::value;
1732 };
1733 
1734 template <typename T, typename U>
1735 const bool has_truncate_degree<T, U>::value;
1736 
1737 /// Type trait for differentiable types.
1738 /**
1739  * The type trait will be \p true if piranha::math::partial() can be successfully called on instances of
1740  * type \p T, \p false otherwise.
1741  */
1742 template <typename T>
1743 class is_differentiable : detail::sfinae_types
1744 {
1745     template <typename U>
1746     static auto test(const U &u) -> decltype(math::partial(u, ""), void(), yes());
1747     static no test(...);
1748     static const bool implementation_defined = std::is_same<decltype(test(std::declval<T>())), yes>::value;
1749 
1750 public:
1751     /// Value of the type trait.
1752     static const bool value = implementation_defined;
1753 };
1754 
1755 // Static init.
1756 template <typename T>
1757 const bool is_differentiable<T>::value;
1758 
1759 namespace detail
1760 {
1761 
1762 // A key is differentiable if the type returned by the partial method
1763 // is a pair (sometype,key).
1764 template <typename Key, typename Pair>
1765 struct is_differential_key_pair {
1766     static const bool value = false;
1767 };
1768 
1769 template <typename Key, typename PairFirst>
1770 struct is_differential_key_pair<Key, std::pair<PairFirst, Key>> {
1771     static const bool value = true;
1772 };
1773 } // namespace detail
1774 
1775 /// Type trait to detect differentiable keys.
1776 /**
1777  * This type trait will be \p true if \p Key is a key type providing a const method <tt>partial()</tt> taking a const
1778  * instance of
1779  * piranha::symbol_set::positions and a const instance of piranha::symbol_set as input, and returning an
1780  * <tt>std::pair</tt> of
1781  * any type and \p Key. Otherwise, the type trait will be \p false.
1782  * If \p Key does not satisfy piranha::is_key, a compilation error will be produced.
1783  *
1784  * The decay type of \p Key is considered in this type trait.
1785  */
1786 template <typename T>
1787 class key_is_differentiable : detail::sfinae_types
1788 {
1789     using Td = typename std::decay<T>::type;
1790     PIRANHA_TT_CHECK(is_key, Td);
1791     template <typename U>
1792     static auto test(const U &u)
1793         -> decltype(u.partial(std::declval<const symbol_set::positions &>(), std::declval<const symbol_set &>()));
1794     static no test(...);
1795 
1796 public:
1797     /// Value of the type trait.
1798     static const bool value = detail::is_differential_key_pair<Td, decltype(test(std::declval<Td>()))>::value;
1799 };
1800 
1801 template <typename T>
1802 const bool key_is_differentiable<T>::value;
1803 
1804 /// Type trait for integrable types.
1805 /**
1806  * The type trait will be \p true if piranha::math::integrate() can be successfully called on instances of
1807  * type \p T, \p false otherwise.
1808  */
1809 template <typename T>
1810 class is_integrable : detail::sfinae_types
1811 {
1812     template <typename U>
1813     static auto test(const U &u) -> decltype(math::integrate(u, ""), void(), yes());
1814     static no test(...);
1815     static const bool implementation_defined = std::is_same<decltype(test(std::declval<T>())), yes>::value;
1816 
1817 public:
1818     /// Value of the type trait.
1819     static const bool value = implementation_defined;
1820 };
1821 
1822 template <typename T>
1823 const bool is_integrable<T>::value;
1824 
1825 /// Type trait to detect integrable keys.
1826 /**
1827  * This type trait will be \p true if \p Key is a key type providing a const method <tt>integrate()</tt> taking a const
1828  * instance of
1829  * piranha::symbol and a const instance of piranha::symbol_set as input, and returning an <tt>std::pair</tt> of
1830  * any type and \p Key. Otherwise, the type trait will be \p false.
1831  * If \p Key does not satisfy piranha::is_key, a compilation error will be produced.
1832  *
1833  * The decay type of \p Key is considered in this type trait.
1834  */
1835 template <typename T>
1836 class key_is_integrable : detail::sfinae_types
1837 {
1838     using Td = typename std::decay<T>::type;
1839     PIRANHA_TT_CHECK(is_key, Td);
1840     template <typename U>
1841     static auto test(const U &u)
1842         -> decltype(u.integrate(std::declval<const symbol &>(), std::declval<const symbol_set &>()));
1843     static no test(...);
1844 
1845 public:
1846     /// Value of the type trait.
1847     static const bool value = detail::is_differential_key_pair<Td, decltype(test(std::declval<Td>()))>::value;
1848 };
1849 
1850 template <typename T>
1851 const bool key_is_integrable<T>::value;
1852 
1853 /// Type trait to detect if type has a degree property.
1854 /**
1855  * The type trait will be true if instances of type \p T can be used as arguments of piranha::math::degree()
1856  * (both in the unary and binary version of the function).
1857  */
1858 template <typename T>
1859 class has_degree : detail::sfinae_types
1860 {
1861     template <typename U>
1862     static auto test1(const U &u) -> decltype(math::degree(u), void(), yes());
1863     static no test1(...);
1864     template <typename U>
1865     static auto test2(const U &u)
1866         -> decltype(math::degree(u, std::declval<const std::vector<std::string> &>()), void(), yes());
1867     static no test2(...);
1868 
1869 public:
1870     /// Value of the type trait.
1871     static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1872                               && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1873 };
1874 
1875 // Static init.
1876 template <typename T>
1877 const bool has_degree<T>::value;
1878 
1879 /// Type trait to detect if type has a low degree property.
1880 /**
1881  * The type trait will be true if instances of type \p T can be used as arguments of piranha::math::ldegree()
1882  * (both in the unary and binary version of the function).
1883  */
1884 template <typename T>
1885 class has_ldegree : detail::sfinae_types
1886 {
1887     template <typename U>
1888     static auto test1(const U &u) -> decltype(math::ldegree(u), void(), yes());
1889     static no test1(...);
1890     template <typename U>
1891     static auto test2(const U &u)
1892         -> decltype(math::ldegree(u, std::declval<const std::vector<std::string> &>()), void(), yes());
1893     static no test2(...);
1894 
1895 public:
1896     /// Value of the type trait.
1897     static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1898                               && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1899 };
1900 
1901 // Static init.
1902 template <typename T>
1903 const bool has_ldegree<T>::value;
1904 
1905 /// Type trait to detect if type has a trigonometric degree property.
1906 /**
1907  * The type trait will be true if instances of type \p T can be used as arguments of piranha::math::t_degree()
1908  * (both in the unary and binary version of the function).
1909  */
1910 template <typename T>
1911 class has_t_degree : detail::sfinae_types
1912 {
1913     template <typename U>
1914     static auto test1(const U &u) -> decltype(math::t_degree(u), void(), yes());
1915     static no test1(...);
1916     template <typename U>
1917     static auto test2(const U &u)
1918         -> decltype(math::t_degree(u, std::declval<const std::vector<std::string> &>()), void(), yes());
1919     static no test2(...);
1920 
1921 public:
1922     /// Value of the type trait.
1923     static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1924                               && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1925 };
1926 
1927 // Static init.
1928 template <typename T>
1929 const bool has_t_degree<T>::value;
1930 
1931 /// Type trait to detect if type has a trigonometric low degree property.
1932 /**
1933  * The type trait will be true if instances of type \p T can be used as arguments of piranha::math::t_ldegree()
1934  * (both in the unary and binary version of the function).
1935  */
1936 template <typename T>
1937 class has_t_ldegree : detail::sfinae_types
1938 {
1939     template <typename U>
1940     static auto test1(const U &u) -> decltype(math::t_ldegree(u), void(), yes());
1941     static no test1(...);
1942     template <typename U>
1943     static auto test2(const U &u)
1944         -> decltype(math::t_ldegree(u, std::declval<const std::vector<std::string> &>()), void(), yes());
1945     static no test2(...);
1946 
1947 public:
1948     /// Value of the type trait.
1949     static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1950                               && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1951 };
1952 
1953 // Static init.
1954 template <typename T>
1955 const bool has_t_ldegree<T>::value;
1956 
1957 /// Type trait to detect if type has a trigonometric order property.
1958 /**
1959  * The type trait will be true if instances of type \p T can be used as arguments of piranha::math::t_order()
1960  * (both in the unary and binary version of the function).
1961  */
1962 template <typename T>
1963 class has_t_order : detail::sfinae_types
1964 {
1965     template <typename U>
1966     static auto test1(const U &u) -> decltype(math::t_order(u), void(), yes());
1967     static no test1(...);
1968     template <typename U>
1969     static auto test2(const U &u)
1970         -> decltype(math::t_order(u, std::declval<const std::vector<std::string> &>()), void(), yes());
1971     static no test2(...);
1972 
1973 public:
1974     /// Value of the type trait.
1975     static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1976                               && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1977 };
1978 
1979 // Static init.
1980 template <typename T>
1981 const bool has_t_order<T>::value;
1982 
1983 /// Type trait to detect if type has a trigonometric low order property.
1984 /**
1985  * The type trait will be true if instances of type \p T can be used as arguments of piranha::math::t_lorder()
1986  * (both in the unary and binary version of the function).
1987  */
1988 template <typename T>
1989 class has_t_lorder : detail::sfinae_types
1990 {
1991     template <typename U>
1992     static auto test1(const U &u) -> decltype(math::t_lorder(u), void(), yes());
1993     static no test1(...);
1994     template <typename U>
1995     static auto test2(const U &u)
1996         -> decltype(math::t_lorder(u, std::declval<const std::vector<std::string> &>()), void(), yes());
1997     static no test2(...);
1998 
1999 public:
2000     /// Value of the type trait.
2001     static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
2002                               && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
2003 };
2004 
2005 // Static init.
2006 template <typename T>
2007 const bool has_t_lorder<T>::value;
2008 
2009 /// Type trait to detect if a key type has a degree property.
2010 /**
2011  * The type trait has the same meaning as piranha::has_degree, but it's meant for use with key types.
2012  * It will test the presence of two <tt>degree()</tt> const methods, the first one accepting a const instance of
2013  * piranha::symbol_set, the second one a const instance of piranha::symbol_set::positions and a const instance of
2014  * piranha::symbol_set.
2015  *
2016  * \p Key must satisfy piranha::is_key.
2017  */
2018 template <typename Key>
2019 class key_has_degree : detail::sfinae_types
2020 {
2021     PIRANHA_TT_CHECK(is_key, Key);
2022     // NOTE: here it works (despite the difference in constness with the use below) because none of the two versions
2023     // of test1() is an exact match, and the conversion in constness has a higher priority than the ellipsis conversion.
2024     // For more info:
2025     // http://cpp0x.centaur.ath.cx/over.ics.rank.html
2026     template <typename T>
2027     static auto test1(const T *t) -> decltype(t->degree(std::declval<const symbol_set &>()), void(), yes());
2028     static no test1(...);
2029     template <typename T>
2030     static auto test2(const T *t)
2031         -> decltype(t->degree(std::declval<const symbol_set::positions &>(), std::declval<const symbol_set &>()),
2032                     void(), yes());
2033     static no test2(...);
2034 
2035 public:
2036     /// Value of the type trait.
2037     static const bool value = std::is_same<decltype(test1((Key *)nullptr)), yes>::value
2038                               && std::is_same<decltype(test2((Key *)nullptr)), yes>::value;
2039 };
2040 
2041 template <typename Key>
2042 const bool key_has_degree<Key>::value;
2043 
2044 /// Type trait to detect if a key type has a low degree property.
2045 /**
2046  * The type trait has the same meaning as piranha::has_ldegree, but it's meant for use with key types.
2047  * It will test the presence of two <tt>ldegree()</tt> const methods, the first one accepting a const instance of
2048  * piranha::symbol_set, the second one a const instance of piranha::symbol_set::positions and a const instance of
2049  * piranha::symbol_set.
2050  *
2051  * \p Key must satisfy piranha::is_key.
2052  */
2053 template <typename Key>
2054 class key_has_ldegree : detail::sfinae_types
2055 {
2056     PIRANHA_TT_CHECK(is_key, Key);
2057     template <typename T>
2058     static auto test1(const T *t) -> decltype(t->ldegree(std::declval<const symbol_set &>()), void(), yes());
2059     static no test1(...);
2060     template <typename T>
2061     static auto test2(const T *t)
2062         -> decltype(t->ldegree(std::declval<const symbol_set::positions &>(), std::declval<const symbol_set &>()),
2063                     void(), yes());
2064     static no test2(...);
2065 
2066 public:
2067     /// Value of the type trait.
2068     static const bool value = std::is_same<decltype(test1((Key *)nullptr)), yes>::value
2069                               && std::is_same<decltype(test2((Key *)nullptr)), yes>::value;
2070 };
2071 
2072 template <typename Key>
2073 const bool key_has_ldegree<Key>::value;
2074 
2075 /// Type trait to detect if a key type has a trigonometric degree property.
2076 /**
2077  * The type trait has the same meaning as piranha::has_t_degree, but it's meant for use with key types.
2078  * It will test the presence of two <tt>t_degree()</tt> const methods, the first one accepting a const instance of
2079  * piranha::symbol_set, the second one a const instance of piranha::symbol_set::positions and a const instance of
2080  * piranha::symbol_set.
2081  *
2082  * \p Key must satisfy piranha::is_key.
2083  */
2084 template <typename Key>
2085 class key_has_t_degree : detail::sfinae_types
2086 {
2087     PIRANHA_TT_CHECK(is_key, Key);
2088     template <typename T>
2089     static auto test1(T const *t) -> decltype(t->t_degree(std::declval<const symbol_set &>()), void(), yes());
2090     static no test1(...);
2091     template <typename T>
2092     static auto test2(T const *t)
2093         -> decltype(t->t_degree(std::declval<const symbol_set::positions &>(), std::declval<const symbol_set &>()),
2094                     void(), yes());
2095     static no test2(...);
2096 
2097 public:
2098     /// Value of the type trait.
2099     static const bool value = std::is_same<decltype(test1((Key const *)nullptr)), yes>::value
2100                               && std::is_same<decltype(test2((Key const *)nullptr)), yes>::value;
2101 };
2102 
2103 // Static init.
2104 template <typename T>
2105 const bool key_has_t_degree<T>::value;
2106 
2107 /// Type trait to detect if a key type has a trigonometric low degree property.
2108 /**
2109  * The type trait has the same meaning as piranha::has_t_ldegree, but it's meant for use with key types.
2110  * It will test the presence of two <tt>t_ldegree()</tt> const methods, the first one accepting a const instance of
2111  * piranha::symbol_set, the second one a const instance of piranha::symbol_set::positions and a const instance of
2112  * piranha::symbol_set.
2113  *
2114  * \p Key must satisfy piranha::is_key.
2115  */
2116 template <typename Key>
2117 class key_has_t_ldegree : detail::sfinae_types
2118 {
2119     PIRANHA_TT_CHECK(is_key, Key);
2120     template <typename T>
2121     static auto test1(T const *t) -> decltype(t->t_ldegree(std::declval<const symbol_set &>()), void(), yes());
2122     static no test1(...);
2123     template <typename T>
2124     static auto test2(T const *t)
2125         -> decltype(t->t_ldegree(std::declval<const symbol_set::positions &>(), std::declval<const symbol_set &>()),
2126                     void(), yes());
2127     static no test2(...);
2128 
2129 public:
2130     /// Value of the type trait.
2131     static const bool value = std::is_same<decltype(test1((Key const *)nullptr)), yes>::value
2132                               && std::is_same<decltype(test2((Key const *)nullptr)), yes>::value;
2133 };
2134 
2135 // Static init.
2136 template <typename T>
2137 const bool key_has_t_ldegree<T>::value;
2138 
2139 /// Type trait to detect if a key type has a trigonometric order property.
2140 /**
2141  * The type trait has the same meaning as piranha::has_t_order, but it's meant for use with key types.
2142  * It will test the presence of two <tt>t_order()</tt> const methods, the first one accepting a const instance of
2143  * piranha::symbol_set, the second one a const instance of piranha::symbol_set::positions and a const instance of
2144  * piranha::symbol_set.
2145  *
2146  * \p Key must satisfy piranha::is_key.
2147  */
2148 template <typename Key>
2149 class key_has_t_order : detail::sfinae_types
2150 {
2151     PIRANHA_TT_CHECK(is_key, Key);
2152     template <typename T>
2153     static auto test1(T const *t) -> decltype(t->t_order(std::declval<const symbol_set &>()), void(), yes());
2154     static no test1(...);
2155     template <typename T>
2156     static auto test2(T const *t)
2157         -> decltype(t->t_order(std::declval<const symbol_set::positions &>(), std::declval<const symbol_set &>()),
2158                     void(), yes());
2159     static no test2(...);
2160 
2161 public:
2162     /// Value of the type trait.
2163     static const bool value = std::is_same<decltype(test1((Key const *)nullptr)), yes>::value
2164                               && std::is_same<decltype(test2((Key const *)nullptr)), yes>::value;
2165 };
2166 
2167 // Static init.
2168 template <typename T>
2169 const bool key_has_t_order<T>::value;
2170 
2171 /// Type trait to detect if a key type has a trigonometric low order property.
2172 /**
2173  * The type trait has the same meaning as piranha::has_t_lorder, but it's meant for use with key types.
2174  * It will test the presence of two <tt>t_lorder()</tt> const methods, the first one accepting a const instance of
2175  * piranha::symbol_set, the second one a const instance of piranha::symbol_set::positions and a const instance of
2176  * piranha::symbol_set.
2177  *
2178  * \p Key must satisfy piranha::is_key.
2179  */
2180 template <typename Key>
2181 class key_has_t_lorder : detail::sfinae_types
2182 {
2183     PIRANHA_TT_CHECK(is_key, Key);
2184     template <typename T>
2185     static auto test1(T const *t) -> decltype(t->t_lorder(std::declval<const symbol_set &>()), void(), yes());
2186     static no test1(...);
2187     template <typename T>
2188     static auto test2(T const *t)
2189         -> decltype(t->t_lorder(std::declval<const symbol_set::positions &>(), std::declval<const symbol_set &>()),
2190                     void(), yes());
2191     static no test2(...);
2192 
2193 public:
2194     /// Value of the type trait.
2195     static const bool value = std::is_same<decltype(test1((Key const *)nullptr)), yes>::value
2196                               && std::is_same<decltype(test2((Key const *)nullptr)), yes>::value;
2197 };
2198 
2199 // Static init.
2200 template <typename T>
2201 const bool key_has_t_lorder<T>::value;
2202 
2203 /// Type trait to detect the presence of the piranha::math::is_unitary() function.
2204 /**
2205  * The type trait will be \p true if piranha::math::is_unitary() can be successfully called on instances of \p T.
2206  */
2207 template <typename T>
2208 class has_is_unitary : detail::sfinae_types
2209 {
2210     typedef typename std::decay<T>::type Td;
2211     template <typename T1>
2212     static auto test(const T1 &t) -> decltype(math::is_unitary(t), void(), yes());
2213     static no test(...);
2214     static const bool implementation_defined = std::is_same<decltype(test(std::declval<Td>())), yes>::value;
2215 
2216 public:
2217     /// Value of the type trait.
2218     static const bool value = implementation_defined;
2219 };
2220 
2221 template <typename T>
2222 const bool has_is_unitary<T>::value;
2223 
2224 /// Type trait to detect the presence of the piranha::math::subs function.
2225 /**
2226  * The type trait will be \p true if piranha::math::subs can be successfully called on instances
2227  * of type \p T, with an instance of type \p U as substitution argument.
2228  */
2229 template <typename T, typename U>
2230 class has_subs : detail::sfinae_types
2231 {
2232     typedef typename std::decay<T>::type Td;
2233     typedef typename std::decay<U>::type Ud;
2234     template <typename T1, typename U1>
2235     static auto test(const T1 &t, const U1 &u)
2236         -> decltype(math::subs(t, std::declval<std::string const &>(), u), void(), yes());
2237     static no test(...);
2238     static const bool implementation_defined
2239         = std::is_same<decltype(test(std::declval<Td>(), std::declval<Ud>())), yes>::value;
2240 
2241 public:
2242     /// Value of the type trait.
2243     static const bool value = implementation_defined;
2244 };
2245 
2246 // Static init.
2247 template <typename T, typename U>
2248 const bool has_subs<T, U>::value;
2249 
2250 /// Type trait to detect the presence of the piranha::math::t_subs function.
2251 /**
2252  * The type trait will be \p true if piranha::math::t_subs can be successfully called on instances
2253  * of type \p T, with instances of type \p U and \p V as substitution arguments.
2254  */
2255 template <typename T, typename U, typename V>
2256 class has_t_subs : detail::sfinae_types
2257 {
2258     typedef typename std::decay<T>::type Td;
2259     typedef typename std::decay<U>::type Ud;
2260     typedef typename std::decay<V>::type Vd;
2261     template <typename T1, typename U1, typename V1>
2262     static auto test(const T1 &t, const U1 &u, const V1 &v)
2263         -> decltype(math::t_subs(t, std::declval<std::string const &>(), u, v), void(), yes());
2264     static no test(...);
2265     static const bool implementation_defined
2266         = std::is_same<decltype(test(std::declval<Td>(), std::declval<Ud>(), std::declval<Vd>())), yes>::value;
2267 
2268 public:
2269     /// Value of the type trait.
2270     static const bool value = implementation_defined;
2271 };
2272 
2273 // Static init.
2274 template <typename T, typename U, typename V>
2275 const bool has_t_subs<T, U, V>::value;
2276 
2277 /// Type trait to detect the presence of the trigonometric substitution method in keys.
2278 /**
2279  * This type trait will be \p true if the decay type of \p Key provides a const method <tt>t_subs()</tt> accepting as
2280  * const parameters a string,
2281  * an instance of \p T, an instance of \p U and an instance of piranha::symbol_set. The return value of the method must
2282  * be an <tt>std::vector</tt>
2283  * of pairs in which the second type must be \p Key itself. The <tt>t_subs()</tt> represents the substitution of a
2284  * symbol with its cosine
2285  * and sine passed as instances of \p T and \p U respectively.
2286  *
2287  * The decay type of \p Key must satisfy piranha::is_key.
2288  */
2289 template <typename Key, typename T, typename U>
2290 class key_has_t_subs : detail::sfinae_types
2291 {
2292     typedef typename std::decay<Key>::type Keyd;
2293     typedef typename std::decay<T>::type Td;
2294     typedef typename std::decay<U>::type Ud;
2295     PIRANHA_TT_CHECK(is_key, Keyd);
2296     template <typename Key1, typename T1, typename U1>
2297     static auto test(const Key1 &k, const T1 &t, const U1 &u)
2298         -> decltype(k.t_subs(std::declval<const std::string &>(), t, u, std::declval<const symbol_set &>()));
2299     static no test(...);
2300     template <typename T1>
2301     struct check_result_type {
2302         static const bool value = false;
2303     };
2304     template <typename Res>
2305     struct check_result_type<std::vector<std::pair<Res, Keyd>>> {
2306         static const bool value = true;
2307     };
2308 
2309 public:
2310     /// Value of the type trait.
2311     static const bool value
2312         = check_result_type<decltype(test(std::declval<Keyd>(), std::declval<Td>(), std::declval<Ud>()))>::value;
2313 };
2314 
2315 // Static init.
2316 template <typename Key, typename T, typename U>
2317 const bool key_has_t_subs<Key, T, U>::value;
2318 
2319 /// Type trait to detect the presence of the substitution method in keys.
2320 /**
2321  * This type trait will be \p true if the decay type of \p Key provides a const method <tt>subs()</tt> accepting as
2322  * const parameters a string,
2323  * an instance of \p T and an instance of piranha::symbol_set. The return value of the method must be an
2324  * <tt>std::vector</tt>
2325  * of pairs in which the second type must be \p Key itself. The <tt>subs()</tt> method represents the substitution of a
2326  * symbol with
2327  * an instance of type \p T.
2328  *
2329  * The decay type of \p Key must satisfy piranha::is_key.
2330  */
2331 template <typename Key, typename T>
2332 class key_has_subs : detail::sfinae_types
2333 {
2334     typedef typename std::decay<Key>::type Keyd;
2335     typedef typename std::decay<T>::type Td;
2336     PIRANHA_TT_CHECK(is_key, Keyd);
2337     template <typename Key1, typename T1>
2338     static auto test(const Key1 &k, const T1 &t)
2339         -> decltype(k.subs(std::declval<const std::string &>(), t, std::declval<const symbol_set &>()));
2340     static no test(...);
2341     template <typename T1>
2342     struct check_result_type {
2343         static const bool value = false;
2344     };
2345     template <typename Res>
2346     struct check_result_type<std::vector<std::pair<Res, Keyd>>> {
2347         static const bool value = true;
2348     };
2349 
2350 public:
2351     /// Value of the type trait.
2352     static const bool value = check_result_type<decltype(test(std::declval<Keyd>(), std::declval<Td>()))>::value;
2353 };
2354 
2355 // Static init.
2356 template <typename Key, typename T>
2357 const bool key_has_subs<Key, T>::value;
2358 
2359 /// Type trait to detect the availability of piranha::math::multiply_accumulate().
2360 /**
2361  * This type trait will be \p true if piranha::math::multiply_accumulate() can be called with arguments of type \p T,
2362  * \p false otherwise.
2363  */
2364 template <typename T>
2365 class has_multiply_accumulate
2366 {
2367     template <typename U>
2368     using multiply_accumulate_t = decltype(
2369         math::multiply_accumulate(std::declval<U &>(), std::declval<const U &>(), std::declval<const U &>()));
2370     static const bool implementation_defined = is_detected<multiply_accumulate_t, T>::value;
2371 
2372 public:
2373     /// Value of the type trait.
2374     static const bool value = implementation_defined;
2375 };
2376 
2377 // Static init.
2378 template <typename T>
2379 const bool has_multiply_accumulate<T>::value;
2380 
2381 /// Type trait to detect the availability of piranha::math::evaluate.
2382 /**
2383  * This type trait will be \p true if piranha::math::evaluate can be called with arguments of type \p T and \p U,
2384  * \p false otherwise.
2385  */
2386 template <typename T, typename U>
2387 class is_evaluable : detail::sfinae_types
2388 {
2389     template <typename T2, typename U2>
2390     static auto test(const T2 &t, const std::unordered_map<std::string, U2> &dict)
2391         -> decltype(math::evaluate(t, dict), void(), yes());
2392     static no test(...);
2393     static const bool implementation_defined
2394         = std::is_same<decltype(test(std::declval<T>(), std::declval<std::unordered_map<std::string, U>>())),
2395                        yes>::value;
2396 
2397 public:
2398     /// Value of the type trait.
2399     static const bool value = implementation_defined;
2400 };
2401 
2402 template <typename T, typename U>
2403 const bool is_evaluable<T, U>::value;
2404 
2405 /// Type trait to detect evaluable keys.
2406 /**
2407  * This type trait will be \p true if \p Key is a key type providing a const method <tt>evaluate()</tt> taking a const
2408  * instance of
2409  * piranha::symbol_set::positions_map of \p T and a const instance of piranha::symbol_set as input, \p false otherwise.
2410  * If \p Key does not satisfy piranha::is_key, a compilation error will be produced.
2411  *
2412  * The decay type of \p Key is considered in this type trait.
2413  */
2414 template <typename Key, typename T>
2415 class key_is_evaluable : detail::sfinae_types
2416 {
2417     typedef typename std::decay<Key>::type Keyd;
2418     PIRANHA_TT_CHECK(is_key, Keyd);
2419     template <typename Key1, typename T1>
2420     static auto test(const Key1 &k, const symbol_set::positions_map<T1> &pmap)
2421         -> decltype(k.evaluate(pmap, std::declval<const symbol_set &>()), void(), yes());
2422     static no test(...);
2423 
2424 public:
2425     /// Value of the type trait.
2426     static const bool value
2427         = std::is_same<decltype(test(std::declval<Keyd>(), std::declval<symbol_set::positions_map<T>>())), yes>::value;
2428 };
2429 
2430 template <typename Key, typename T>
2431 const bool key_is_evaluable<Key, T>::value;
2432 
2433 /// Type trait to detect piranha::math::sin().
2434 /**
2435  * The type trait will be \p true if piranha::math::sin() can be used on instances of type \p T,
2436  * \p false otherwise.
2437  */
2438 template <typename T>
2439 class has_sine : detail::sfinae_types
2440 {
2441     template <typename T1>
2442     static auto test(const T1 &x) -> decltype(math::sin(x), void(), yes());
2443     static no test(...);
2444 
2445 public:
2446     /// Value of the type trait.
2447     static const bool value = std::is_same<decltype(test(std::declval<T>())), yes>::value;
2448 };
2449 
2450 template <typename T>
2451 const bool has_sine<T>::value;
2452 
2453 /// Type trait to detect piranha::math::cos().
2454 /**
2455  * The type trait will be \p true if piranha::math::cos() can be used on instances of type \p T,
2456  * \p false otherwise.
2457  */
2458 template <typename T>
2459 class has_cosine : detail::sfinae_types
2460 {
2461     template <typename T1>
2462     static auto test(const T1 &x) -> decltype(math::cos(x), void(), yes());
2463     static no test(...);
2464 
2465 public:
2466     /// Value of the type trait.
2467     static const bool value = std::is_same<decltype(test(std::declval<T>())), yes>::value;
2468 };
2469 
2470 template <typename T>
2471 const bool has_cosine<T>::value;
2472 
2473 /// Detect piranha::math::transformation_is_canonical().
2474 /**
2475  * The type trait will be \p true if piranha::math::transformation_is_canonical() can be used on instances of type \p T,
2476  * \p false otherwise.
2477  */
2478 template <typename T>
2479 class has_transformation_is_canonical : detail::sfinae_types
2480 {
2481     using v_string = std::vector<std::string>;
2482     template <typename T1>
2483     static auto test(const T1 &) -> decltype(math::transformation_is_canonical(std::declval<std::vector<T1> const &>(),
2484                                                                                std::declval<std::vector<T1> const &>(),
2485                                                                                std::declval<v_string const &>(),
2486                                                                                std::declval<v_string const &>()),
2487                                              void(), yes());
2488     static no test(...);
2489 
2490 public:
2491     /// Value of the type trait.
2492     static const bool value = std::is_same<decltype(test(std::declval<T>())), yes>::value;
2493 };
2494 
2495 template <typename T>
2496 const bool has_transformation_is_canonical<T>::value;
2497 
2498 namespace math
2499 {
2500 
2501 /// Default functor for the implementation of piranha::math::add3().
2502 /**
2503  * This functor should be specialised via the \p std::enable_if mechanism.
2504  */
2505 template <typename T, typename Enable = void>
2506 struct add3_impl {
2507     /// Call operator.
2508     /**
2509      * \note
2510      * This operator is enabled only if the expression <tt>a = b + c</tt> is well-formed.
2511      *
2512      * This operator will return the result of the expression <tt>a = b + c</tt>.
2513      *
2514      * @param a the return value.
2515      * @param b the first operand.
2516      * @param c the second operand.
2517      *
2518      * @return <tt>a = b + c</tt>.
2519      *
2520      * @throws unspecified any exception thrown by the invoked binary and/or assignment operators
2521      * of \p U.
2522      */
2523     template <typename U>
operator ()piranha::math::add3_impl2524     auto operator()(U &a, const U &b, const U &c) const -> decltype(a = b + c)
2525     {
2526         return a = b + c;
2527     }
2528 };
2529 
2530 /// Specialisation of the piranha::math::add3() functor for integral types.
2531 /**
2532  * This specialisation is activated when \p T is an integral type.
2533  */
2534 template <typename T>
2535 struct add3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2536     /// Call operator.
2537     /**
2538      * This operator will return the expression <tt>a = static_cast<T>(b + c)</tt>, with <tt>b + c</tt>
2539      * forcibly cast back to \p T in order to avoid compiler warnings with short integral types.
2540      *
2541      * @param a the return value.
2542      * @param b the first operand.
2543      * @param c the second operand.
2544      *
2545      * @return <tt>a = static_cast<T>(b + c)</tt>.
2546      */
operator ()piranha::math::add3_impl2547     T &operator()(T &a, const T &b, const T &c) const
2548     {
2549         return a = static_cast<T>(b + c);
2550     }
2551 };
2552 
2553 /// Ternary addition.
2554 /**
2555  * This function will set \p a to <tt>b + c</tt>. The actual implementation of this function is in the
2556  * piranha::math::add3_impl functor's
2557  * call operator.
2558  *
2559  * @param a the return value.
2560  * @param b the first operand.
2561  * @param c the second operand.
2562  *
2563  * @return the value returned by the call operator of piranha::math::add3_impl.
2564  *
2565  * @throws unspecified any exception thrown by the call operator of the piranha::math::add3_impl functor.
2566  */
2567 template <typename T>
add3(T & a,const T & b,const T & c)2568 inline auto add3(T &a, const T &b, const T &c) -> decltype(add3_impl<T>()(a, b, c))
2569 {
2570     return add3_impl<T>()(a, b, c);
2571 }
2572 
2573 /// Default functor for the implementation of piranha::math::sub3().
2574 /**
2575  * This functor should be specialised via the \p std::enable_if mechanism.
2576  */
2577 template <typename T, typename Enable = void>
2578 struct sub3_impl {
2579     /// Call operator.
2580     /**
2581      * \note
2582      * This operator is enabled only if the expression <tt>a = b - c</tt> is well-formed.
2583      *
2584      * This operator will return the result of the expression <tt>a = b - c</tt>.
2585      *
2586      * @param a the return value.
2587      * @param b the first operand.
2588      * @param c the second operand.
2589      *
2590      * @return <tt>a = b - c</tt>.
2591      *
2592      * @throws unspecified any exception thrown by the invoked binary and/or assignment operators
2593      * of \p U.
2594      */
2595     template <typename U>
operator ()piranha::math::sub3_impl2596     auto operator()(U &a, const U &b, const U &c) const -> decltype(a = b - c)
2597     {
2598         return a = b - c;
2599     }
2600 };
2601 
2602 /// Specialisation of the piranha::math::sub3() functor for integral types.
2603 /**
2604  * This specialisation is activated when \p T is an integral type.
2605  */
2606 template <typename T>
2607 struct sub3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2608     /// Call operator.
2609     /**
2610      * This operator will return the expression <tt>a = static_cast<T>(b - c)</tt>, with <tt>b - c</tt>
2611      * forcibly cast back to \p T in order to avoid compiler warnings with short integral types.
2612      *
2613      * @param a the return value.
2614      * @param b the first operand.
2615      * @param c the second operand.
2616      *
2617      * @return <tt>a = static_cast<T>(b - c)</tt>.
2618      */
operator ()piranha::math::sub3_impl2619     T &operator()(T &a, const T &b, const T &c) const
2620     {
2621         return a = static_cast<T>(b - c);
2622     }
2623 };
2624 
2625 /// Ternary subtraction.
2626 /**
2627  * This function will set \p a to <tt>b - c</tt>. The actual implementation of this function is in the
2628  * piranha::math::sub3_impl functor's
2629  * call operator.
2630  *
2631  * @param a the return value.
2632  * @param b the first operand.
2633  * @param c the second operand.
2634  *
2635  * @return the value returned by the call operator of piranha::math::sub3_impl.
2636  *
2637  * @throws unspecified any exception thrown by the call operator of the piranha::math::sub3_impl functor.
2638  */
2639 template <typename T>
sub3(T & a,const T & b,const T & c)2640 inline auto sub3(T &a, const T &b, const T &c) -> decltype(sub3_impl<T>()(a, b, c))
2641 {
2642     return sub3_impl<T>()(a, b, c);
2643 }
2644 
2645 /// Default functor for the implementation of piranha::math::mul3().
2646 /**
2647  * This functor should be specialised via the \p std::enable_if mechanism.
2648  */
2649 template <typename T, typename Enable = void>
2650 struct mul3_impl {
2651     /// Call operator.
2652     /**
2653      * \note
2654      * This operator is enabled only if the expression <tt>a = b * c</tt> is well-formed.
2655      *
2656      * This operator will return the result of the expression <tt>a = b * c</tt>.
2657      *
2658      * @param a the return value.
2659      * @param b the first operand.
2660      * @param c the second operand.
2661      *
2662      * @return <tt>a = b * c</tt>.
2663      *
2664      * @throws unspecified any exception thrown by the invoked binary and/or assignment operators
2665      * of \p U.
2666      */
2667     template <typename U>
operator ()piranha::math::mul3_impl2668     auto operator()(U &a, const U &b, const U &c) const -> decltype(a = b * c)
2669     {
2670         return a = b * c;
2671     }
2672 };
2673 
2674 /// Specialisation of the piranha::math::mul3() functor for integral types.
2675 /**
2676  * This specialisation is activated when \p T is an integral type.
2677  */
2678 template <typename T>
2679 struct mul3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2680     /// Call operator.
2681     /**
2682      * This operator will return the expression <tt>a = static_cast<T>(b * c)</tt>, with <tt>b * c</tt>
2683      * forcibly cast back to \p T in order to avoid compiler warnings with short integral types.
2684      *
2685      * @param a the return value.
2686      * @param b the first operand.
2687      * @param c the second operand.
2688      *
2689      * @return <tt>a = static_cast<T>(b * c)</tt>.
2690      */
operator ()piranha::math::mul3_impl2691     T &operator()(T &a, const T &b, const T &c) const
2692     {
2693         return a = static_cast<T>(b * c);
2694     }
2695 };
2696 
2697 /// Ternary multiplication.
2698 /**
2699  * This function will set \p a to <tt>b * c</tt>. The actual implementation of this function is in the
2700  * piranha::math::mul3_impl functor's
2701  * call operator.
2702  *
2703  * @param a the return value.
2704  * @param b the first operand.
2705  * @param c the second operand.
2706  *
2707  * @return the value returned by the call operator of piranha::math::mul3_impl.
2708  *
2709  * @throws unspecified any exception thrown by the call operator of the piranha::math::mul3_impl functor.
2710  */
2711 template <typename T>
mul3(T & a,const T & b,const T & c)2712 inline auto mul3(T &a, const T &b, const T &c) -> decltype(mul3_impl<T>()(a, b, c))
2713 {
2714     return mul3_impl<T>()(a, b, c);
2715 }
2716 
2717 /// Default functor for the implementation of piranha::math::div3().
2718 /**
2719  * This functor should be specialised via the \p std::enable_if mechanism.
2720  */
2721 template <typename T, typename Enable = void>
2722 struct div3_impl {
2723     /// Call operator.
2724     /**
2725      * \note
2726      * This operator is enabled only if the expression <tt>a = b / c</tt> is well-formed.
2727      *
2728      * This operator will return the result of the expression <tt>a = b / c</tt>.
2729      *
2730      * @param a the return value.
2731      * @param b the first operand.
2732      * @param c the second operand.
2733      *
2734      * @return <tt>a = b / c</tt>.
2735      *
2736      * @throws unspecified any exception thrown by the invoked binary and/or assignment operators
2737      * of \p U.
2738      */
2739     template <typename U>
operator ()piranha::math::div3_impl2740     auto operator()(U &a, const U &b, const U &c) const -> decltype(a = b / c)
2741     {
2742         return a = b / c;
2743     }
2744 };
2745 
2746 /// Specialisation of the piranha::math::div3() functor for integral types.
2747 /**
2748  * This specialisation is activated when \p T is an integral type.
2749  */
2750 template <typename T>
2751 struct div3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2752     /// Call operator.
2753     /**
2754      * This operator will return the expression <tt>a = static_cast<T>(b / c)</tt>, with <tt>b / c</tt>
2755      * forcibly cast back to \p T in order to avoid compiler warnings with short integral types.
2756      *
2757      * @param a the return value.
2758      * @param b the first operand.
2759      * @param c the second operand.
2760      *
2761      * @return <tt>a = static_cast<T>(b / c)</tt>.
2762      */
operator ()piranha::math::div3_impl2763     T &operator()(T &a, const T &b, const T &c) const
2764     {
2765         return a = static_cast<T>(b / c);
2766     }
2767 };
2768 
2769 /// Ternary division.
2770 /**
2771  * This function will set \p a to <tt>b / c</tt>. The actual implementation of this function is in the
2772  * piranha::math::div3_impl functor's
2773  * call operator.
2774  *
2775  * @param a the return value.
2776  * @param b the first operand.
2777  * @param c the second operand.
2778  *
2779  * @return the value returned by the call operator of piranha::math::div3_impl.
2780  *
2781  * @throws unspecified any exception thrown by the call operator of the piranha::math::div3_impl functor.
2782  */
2783 template <typename T>
div3(T & a,const T & b,const T & c)2784 inline auto div3(T &a, const T &b, const T &c) -> decltype(div3_impl<T>()(a, b, c))
2785 {
2786     return div3_impl<T>()(a, b, c);
2787 }
2788 
2789 /// Exception to signal an inexact division.
2790 struct inexact_division final : std::invalid_argument {
2791     /// Default constructor.
inexact_divisionpiranha::math::inexact_division2792     explicit inexact_division() : std::invalid_argument("inexact division") {}
2793 };
2794 
2795 /// Default functor for the implementation of piranha::math::divexact().
2796 /**
2797  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
2798  * the call operator, and will hence result in a compilation error when used.
2799  */
2800 template <typename T, typename = void>
2801 struct divexact_impl {
2802 };
2803 
2804 /// Exact division.
2805 /**
2806  * This method will write into \p a the exact result of <tt>b / c</tt>. The actual implementation of this function is in
2807  * the piranha::math::divexact_impl functor's call operator. The implementation should throw an instance of
2808  * piranha::math::inexact_division if the division is not exact.
2809  *
2810  * @param a the return value.
2811  * @param b the first operand.
2812  * @param c the second operand.
2813  *
2814  * @return the value returned by the call operator of piranha::math::divexact_impl.
2815  *
2816  * @throws unspecified any exception thrown by the call operator of the piranha::math::divexact_impl functor.
2817  */
2818 template <typename T>
divexact(T & a,const T & b,const T & c)2819 inline auto divexact(T &a, const T &b, const T &c) -> decltype(divexact_impl<T>()(a, b, c))
2820 {
2821     return divexact_impl<T>()(a, b, c);
2822 }
2823 } // namespace math
2824 
2825 namespace detail
2826 {
2827 
2828 // Greatest common divisor using the euclidean algorithm.
2829 // NOTE: this can yield negative values, depending on the signs
2830 // of a and b. Supports C++ integrals and mp_integer.
2831 // NOTE: using this with C++ integrals unchecked on ranges can result in undefined
2832 // behaviour.
2833 template <typename T>
gcd_euclidean(T a,T b)2834 inline T gcd_euclidean(T a, T b)
2835 {
2836     while (true) {
2837         if (math::is_zero(a)) {
2838             return b;
2839         }
2840         b %= a;
2841         if (math::is_zero(b)) {
2842             return a;
2843         }
2844         a %= b;
2845     }
2846 }
2847 } // namespace detail
2848 
2849 namespace math
2850 {
2851 
2852 /// Default functor for the implementation of piranha::math::gcd().
2853 /**
2854  * This functor should be specialised via the \p std::enable_if mechanism. Default implementation will not define
2855  * the call operator, and will hence result in a compilation error when used.
2856  */
2857 template <typename T, typename U, typename = void>
2858 struct gcd_impl {
2859 };
2860 
2861 /// Implementation of piranha::math::gcd() for integral types.
2862 /**
2863  * This specialisation is enabled when \p T and \p U are C++ integral types.
2864  */
2865 template <typename T, typename U>
2866 struct gcd_impl<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type> {
2867     /// The promoted type of T and U.
2868     using p_type = decltype(std::declval<const T &>() + std::declval<const U &>());
2869     /// Call operator.
2870     /**
2871      * The GCD will be computed via the euclidean algorithm. No overflow check is performed during
2872      * the computation.
2873      *
2874      * @param a the first operand.
2875      * @param b the second operand.
2876      *
2877      * @return the GCD of \p a and \p b.
2878      */
operator ()piranha::math::gcd_impl2879     p_type operator()(const T &a, const U &b) const
2880     {
2881         return detail::gcd_euclidean(static_cast<p_type>(a), static_cast<p_type>(b));
2882     }
2883 };
2884 
2885 /// GCD.
2886 /**
2887  * This function will return the GCD of \p a and \p b. The actual implementation of this function is in the
2888  * piranha::math::gcd_impl functor's
2889  * call operator.
2890  *
2891  * @param a the first operand.
2892  * @param b the second operand.
2893  *
2894  * @return the value returned by the call operator of piranha::math::gcd_impl.
2895  *
2896  * @throws unspecified any exception thrown by the call operator of the piranha::math::gcd_impl functor.
2897  */
2898 template <typename T, typename U>
gcd(const T & a,const U & b)2899 inline auto gcd(const T &a, const U &b) -> decltype(gcd_impl<T, U>()(a, b))
2900 {
2901     return gcd_impl<T, U>()(a, b);
2902 }
2903 
2904 /// Default functor for the implementation of piranha::math::gcd3().
2905 /**
2906  * This functor should be specialised via the \p std::enable_if mechanism.
2907  */
2908 template <typename T, typename = void>
2909 struct gcd3_impl {
2910     /// Call operator.
2911     /**
2912      * \note
2913      * This operator is enabled only if the expression <tt>out = math::gcd(a,b)</tt> is well-formed.
2914      *
2915      * @param out the output value.
2916      * @param a the first operand.
2917      * @param b the second operand.
2918      *
2919      * @return <tt>out = math::gcd(a,b)</tt>.
2920      *
2921      * @throws unspecified any exception thrown by piranha::math::gcd() or the invoked
2922      * assignment operator.
2923      */
2924     template <typename T1>
operator ()piranha::math::gcd3_impl2925     auto operator()(T1 &out, const T1 &a, const T1 &b) const -> decltype(out = math::gcd(a, b))
2926     {
2927         return out = math::gcd(a, b);
2928     }
2929 };
2930 
2931 /// Specialisation of the piranha::math::gcd3() functor for integral types.
2932 /**
2933  * This specialisation is enabled when \p T is a C++ integral type.
2934  */
2935 template <typename T>
2936 struct gcd3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2937     /// Call operator.
2938     /**
2939      * This call operator will forcibly cast back to \p T the result of piranha::math::gcd().
2940      *
2941      * @param out the output value.
2942      * @param a the first operand.
2943      * @param b the second operand.
2944      *
2945      * @return <tt>out = static_cast<T>(math::gcd(a,b))</tt>.
2946      */
operator ()piranha::math::gcd3_impl2947     T &operator()(T &out, const T &a, const T &b) const
2948     {
2949         return out = static_cast<T>(math::gcd(a, b));
2950     }
2951 };
2952 
2953 /// Ternary GCD.
2954 /**
2955  * This function will write the GCD of \p a and \p b into \p out. The actual implementation of this function is in the
2956  * piranha::math::gcd3_impl functor's
2957  * call operator.
2958  *
2959  * @param out the output value.
2960  * @param a the first operand.
2961  * @param b the second operand.
2962  *
2963  * @return the value returned by the call operator of piranha::math::gcd3_impl.
2964  *
2965  * @throws unspecified any exception thrown by the call operator of the piranha::math::gcd3_impl functor.
2966  */
2967 template <typename T>
gcd3(T & out,const T & a,const T & b)2968 inline auto gcd3(T &out, const T &a, const T &b) -> decltype(gcd3_impl<T>()(out, a, b))
2969 {
2970     return gcd3_impl<T>()(out, a, b);
2971 }
2972 } // namespace math
2973 
2974 /// Detect piranha::math::add3().
2975 /**
2976  * The type trait will be \p true if piranha::math::add3() can be used on instances of the decay type of \p T,
2977  * \p false otherwise.
2978  */
2979 template <typename T>
2980 class has_add3 : detail::sfinae_types
2981 {
2982     using Td = typename std::decay<T>::type;
2983     template <typename T1>
2984     static auto test(const T1 &)
2985         -> decltype(math::add3(std::declval<T1 &>(), std::declval<const T1 &>(), std::declval<const T1 &>()), void(),
2986                     yes());
2987     static no test(...);
2988 
2989 public:
2990     /// Value of the type trait.
2991     static const bool value = std::is_same<decltype(test(std::declval<Td>())), yes>::value;
2992 };
2993 
2994 template <typename T>
2995 const bool has_add3<T>::value;
2996 
2997 /// Detect piranha::math::sub3().
2998 /**
2999  * The type trait will be \p true if piranha::math::sub3() can be used on instances of the decay type of \p T,
3000  * \p false otherwise.
3001  */
3002 template <typename T>
3003 class has_sub3 : detail::sfinae_types
3004 {
3005     using Td = typename std::decay<T>::type;
3006     template <typename T1>
3007     static auto test(const T1 &)
3008         -> decltype(math::sub3(std::declval<T1 &>(), std::declval<const T1 &>(), std::declval<const T1 &>()), void(),
3009                     yes());
3010     static no test(...);
3011 
3012 public:
3013     /// Value of the type trait.
3014     static const bool value = std::is_same<decltype(test(std::declval<Td>())), yes>::value;
3015 };
3016 
3017 template <typename T>
3018 const bool has_sub3<T>::value;
3019 
3020 /// Detect piranha::math::mul3().
3021 /**
3022  * The type trait will be \p true if piranha::math::mul3() can be used on instances of the decay type of \p T,
3023  * \p false otherwise.
3024  */
3025 template <typename T>
3026 class has_mul3 : detail::sfinae_types
3027 {
3028     using Td = typename std::decay<T>::type;
3029     template <typename T1>
3030     static auto test(const T1 &)
3031         -> decltype(math::mul3(std::declval<T1 &>(), std::declval<const T1 &>(), std::declval<const T1 &>()), void(),
3032                     yes());
3033     static no test(...);
3034 
3035 public:
3036     /// Value of the type trait.
3037     static const bool value = std::is_same<decltype(test(std::declval<Td>())), yes>::value;
3038 };
3039 
3040 template <typename T>
3041 const bool has_mul3<T>::value;
3042 
3043 /// Detect piranha::math::div3().
3044 /**
3045  * The type trait will be \p true if piranha::math::div3() can be used on instances of the decay type of \p T,
3046  * \p false otherwise.
3047  */
3048 template <typename T>
3049 class has_div3 : detail::sfinae_types
3050 {
3051     using Td = typename std::decay<T>::type;
3052     template <typename T1>
3053     static auto test(const T1 &)
3054         -> decltype(math::div3(std::declval<T1 &>(), std::declval<const T1 &>(), std::declval<const T1 &>()), void(),
3055                     yes());
3056     static no test(...);
3057 
3058 public:
3059     /// Value of the type trait.
3060     static const bool value = std::is_same<decltype(test(std::declval<Td>())), yes>::value;
3061 };
3062 
3063 template <typename T>
3064 const bool has_div3<T>::value;
3065 
3066 /// Detect piranha::math::gcd().
3067 /**
3068  * The type trait will be \p true if piranha::math::gcd() can be used on instances of the decay types of \p T and \p U,
3069  * \p false otherwise.
3070  */
3071 template <typename T, typename U = T>
3072 class has_gcd : detail::sfinae_types
3073 {
3074     using Td = typename std::decay<T>::type;
3075     using Ud = typename std::decay<U>::type;
3076     template <typename T1, typename U1>
3077     static auto test(const T1 &a, const U1 &b) -> decltype(math::gcd(a, b), void(), yes());
3078     static no test(...);
3079 
3080 public:
3081     /// Value of the type trait.
3082     static const bool value = std::is_same<decltype(test(std::declval<Td>(), std::declval<Ud>())), yes>::value;
3083 };
3084 
3085 template <typename T, typename U>
3086 const bool has_gcd<T, U>::value;
3087 
3088 /// Detect piranha::math::gcd3().
3089 /**
3090  * The type trait will be \p true if piranha::math::gcd3() can be used on instances of the decay type of \p T,
3091  * \p false otherwise.
3092  */
3093 template <typename T>
3094 class has_gcd3 : detail::sfinae_types
3095 {
3096     using Td = typename std::decay<T>::type;
3097     template <typename T1>
3098     static auto test(const T1 &)
3099         -> decltype(math::gcd3(std::declval<T1 &>(), std::declval<const T1 &>(), std::declval<const T1 &>()), void(),
3100                     yes());
3101     static no test(...);
3102 
3103 public:
3104     /// Value of the type trait.
3105     static const bool value = std::is_same<decltype(test(std::declval<Td>())), yes>::value;
3106 };
3107 
3108 template <typename T>
3109 const bool has_gcd3<T>::value;
3110 
3111 /// Detect piranha::math::divexact().
3112 /**
3113  * The type trait will be \p true if piranha::math::divexact() can be used on instances of the decay type of \p T,
3114  * \p false otherwise.
3115  */
3116 template <typename T>
3117 class has_exact_division : detail::sfinae_types
3118 {
3119     using Td = typename std::decay<T>::type;
3120     template <typename T1>
3121     static auto test(const T1 &)
3122         -> decltype(math::divexact(std::declval<T1 &>(), std::declval<const T1 &>(), std::declval<const T1 &>()),
3123                     void(), yes());
3124     static no test(...);
3125 
3126 public:
3127     /// Value of the type trait.
3128     static const bool value = std::is_same<decltype(test(std::declval<Td>())), yes>::value;
3129 };
3130 
3131 template <typename T>
3132 const bool has_exact_division<T>::value;
3133 } // namespace piranha
3134 
3135 #endif
3136