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