piranha  0.10
math.hpp
1 /* Copyright 2009-2017 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8  * the GNU Lesser General Public License as published by the Free
9  Software Foundation; either version 3 of the License, or (at your
10  option) any later version.
11 
12 or
13 
14  * the GNU General Public License as published by the Free Software
15  Foundation; either version 3 of the License, or (at your option) any
16  later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library. If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #ifndef PIRANHA_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_set>
43 #include <utility>
44 #include <vector>
45 
46 #include <piranha/detail/sfinae_types.hpp>
47 #include <piranha/exceptions.hpp>
48 #include <piranha/is_key.hpp>
49 #include <piranha/symbol_utils.hpp>
50 #include <piranha/type_traits.hpp>
51 
52 namespace piranha
53 {
54 
56 
59 namespace math
60 {
61 
63 
68 template <typename T, typename = void>
69 struct is_zero_impl {
70 private:
71  // NOTE: the equality comparable requirement already implies that the return type of
72  // the comparison must be convertible to bool.
73  template <typename U>
74  using enabler =
75  typename std::enable_if<std::is_constructible<U, int>::value && is_equality_comparable<U>::value, int>::type;
76 
77 public:
79 
93  template <typename U, enabler<U> = 0>
94  bool operator()(const U &x) const
95  {
96  return x == U(0);
97  }
98 };
99 }
100 
101 namespace detail
102 {
103 
104 // Enabler for math::is_zero().
105 template <typename T>
106 using math_is_zero_enabler = typename std::enable_if<
107  std::is_convertible<decltype(math::is_zero_impl<T>{}(std::declval<const T &>())), bool>::value, int>::type;
108 }
109 
110 namespace math
111 {
112 
114 
132 template <typename T, detail::math_is_zero_enabler<T> = 0>
133 inline bool is_zero(const T &x)
134 {
135  return is_zero_impl<T>{}(x);
136 }
137 }
138 
139 namespace detail
140 {
141 
142 // Enabler for the std complex specialisation of is_zero.
143 template <typename T>
144 using math_is_zero_std_complex_enabler =
145  typename std::enable_if<std::is_same<T, std::complex<float>>::value || std::is_same<T, std::complex<double>>::value
146  || std::is_same<T, std::complex<long double>>::value>::type;
147 }
148 
149 namespace math
150 {
151 
153 
156 template <typename T>
157 struct is_zero_impl<T, detail::math_is_zero_std_complex_enabler<T>> {
159 
166  bool operator()(const T &c) const
167  {
168  return is_zero(c.real()) && is_zero(c.imag());
169  }
170 };
171 
173 
178 template <typename T, typename = void>
180 private:
181  template <typename U>
182  using enabler =
183  typename std::enable_if<std::is_constructible<U, int>::value && is_equality_comparable<U>::value, int>::type;
184 
185 public:
187 
201  template <typename U, enabler<U> = 0>
202  bool operator()(const U &x) const
203  {
204  return x == U(1);
205  }
206 };
207 }
208 
209 namespace detail
210 {
211 
212 // Enabler for piranha::math::is_unitary().
213 template <typename T>
214 using math_is_unitary_enabler = typename std::enable_if<
215  std::is_convertible<decltype(math::is_unitary_impl<T>{}(std::declval<const T &>())), bool>::value, int>::type;
216 }
217 
218 namespace math
219 {
220 
222 
241 template <typename T, detail::math_is_unitary_enabler<T> = 0>
242 inline bool is_unitary(const T &x)
243 {
244  return is_unitary_impl<T>{}(x);
245 }
246 
248 
252 template <typename T, typename = void>
253 struct negate_impl {
254 private:
255  // NOTE: inside this type trait, U is always a non-const non-reference type.
256  template <typename U>
257  using generic_enabler =
258  typename std::enable_if<!std::is_integral<U>::value
259  && detail::true_tt<decltype(std::declval<U &>() = -std::declval<U &>())>::value,
260  int>::type;
261  template <typename U>
262  using integral_enabler = typename std::enable_if<std::is_integral<U>::value, int>::type;
263 
264 public:
266 
281  template <typename U, generic_enabler<U> = 0>
282  void operator()(U &x) const
283  {
284  x = -x;
285  }
287 
290  template <typename U, integral_enabler<U> = 0>
291  void operator()(U &x) const
292  {
293  // NOTE: here we use the explicit static_cast to cope with integral promotions
294  // (e.g., in case of char).
295  x = static_cast<U>(-x);
296  }
297 };
298 }
299 
300 namespace detail
301 {
302 
303 // Enabler for math::negate().
304 template <typename T>
305 using math_negate_enabler = typename std::enable_if<
306  !std::is_const<T>::value && true_tt<decltype(math::negate_impl<T>{}(std::declval<T &>()))>::value, int>::type;
307 }
308 
309 namespace math
310 {
311 
313 
328 template <typename T, detail::math_negate_enabler<T> = 0>
329 inline void negate(T &x)
330 {
331  negate_impl<T>{}(x);
332 }
333 
335 
338 template <typename T, typename = void>
340 private:
341  // NOTE: as usual, we check the expression against const ref arguments.
342  template <typename U>
343  using addmul_t = decltype(std::declval<U &>() += std::declval<const U &>() * std::declval<const U &>());
344  template <typename U>
345  using enabler = enable_if_t<is_detected<addmul_t, U>::value, int>;
346 
347 public:
349 
365  template <typename U, enabler<U> = 0>
366  void operator()(U &x, const U &y, const U &z) const
367  {
368  x += y * z;
369  }
370 };
371 
372 #if defined(FP_FAST_FMA) && defined(FP_FAST_FMAF) && defined(FP_FAST_FMAL)
373 
374 inline namespace impl
375 {
376 
377 // Enabler for the fast floating-point implementation of multiply_accumulate().
378 template <typename T>
379 using math_multiply_accumulate_float_enabler = enable_if_t<std::is_floating_point<T>::value>;
380 }
381 
383 
386 template <typename T>
387 struct multiply_accumulate_impl<T, math_multiply_accumulate_float_enabler<T>> {
389 
396  void operator()(T &x, const T &y, const T &z) const
397  {
398  x = std::fma(y, z, x);
399  }
400 };
401 
402 #endif
403 }
404 
405 inline namespace impl
406 {
407 
408 // Enabler for multiply_accumulate.
409 template <typename T>
410 using math_multiply_accumulate_t = decltype(
411  math::multiply_accumulate_impl<T>{}(std::declval<T &>(), std::declval<const T &>(), std::declval<const T &>()));
412 
413 template <typename T>
414 using math_multiply_accumulate_enabler = enable_if_t<is_detected<math_multiply_accumulate_t, T>::value, int>;
415 }
416 
417 namespace math
418 {
419 
421 
437 template <typename T, math_multiply_accumulate_enabler<T> = 0>
438 inline void multiply_accumulate(T &x, const T &y, const T &z)
439 {
440  multiply_accumulate_impl<T>{}(x, y, z);
441 }
442 
444 
448 template <typename T, typename Enable = void>
449 struct cos_impl {
450 };
451 
453 
457 template <typename T>
458 struct cos_impl<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
460 
467  T operator()(const T &x) const
468  {
469  return std::cos(x);
470  }
471 };
472 
474 
477 template <typename T>
478 struct cos_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
480 
487  T operator()(const T &x) const
488  {
489  if (x == T(0)) {
490  return T(1);
491  }
492  piranha_throw(std::invalid_argument, "cannot compute the cosine of a non-zero integral");
493  }
494 };
495 }
496 
497 namespace detail
498 {
499 
500 // Type for the result of math::cos().
501 template <typename T>
502 using math_cos_type_ = decltype(math::cos_impl<T>{}(std::declval<const T &>()));
503 
504 template <typename T>
505 using math_cos_type = typename std::enable_if<is_returnable<math_cos_type_<T>>::value, math_cos_type_<T>>::type;
506 }
507 
508 namespace math
509 {
510 
512 
529 template <typename T>
530 inline detail::math_cos_type<T> cos(const T &x)
531 {
532  return cos_impl<T>{}(x);
533 }
534 
536 
540 template <typename T, typename Enable = void>
541 struct sin_impl {
542 };
543 
545 
549 template <typename T>
550 struct sin_impl<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
552 
559  T operator()(const T &x) const
560  {
561  return std::sin(x);
562  }
563 };
564 
566 
569 template <typename T>
570 struct sin_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
572 
579  T operator()(const T &x) const
580  {
581  if (x == T(0)) {
582  return T(0);
583  }
584  piranha_throw(std::invalid_argument, "cannot compute the sine of a non-zero integral");
585  }
586 };
587 }
588 
589 namespace detail
590 {
591 
592 // Type for the result of math::sin().
593 template <typename T>
594 using math_sin_type_ = decltype(math::sin_impl<T>{}(std::declval<const T &>()));
595 
596 template <typename T>
597 using math_sin_type = typename std::enable_if<is_returnable<math_sin_type_<T>>::value, math_sin_type_<T>>::type;
598 }
599 
600 namespace math
601 {
602 
604 
621 template <typename T>
622 inline detail::math_sin_type<T> sin(const T &x)
623 {
624  return sin_impl<T>{}(x);
625 }
626 
628 
632 template <typename T, typename Enable = void>
633 struct partial_impl {
634 };
635 
637 
641 template <typename T>
642 struct partial_impl<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {
644 
647  T operator()(const T &, const std::string &) const
648  {
649  return T(0);
650  }
651 };
652 }
653 
654 namespace detail
655 {
656 
657 // Return type for math::partial().
658 template <typename T>
659 using math_partial_type_
660  = decltype(math::partial_impl<T>{}(std::declval<const T &>(), std::declval<const std::string &>()));
661 
662 template <typename T>
663 using math_partial_type =
664  typename std::enable_if<is_returnable<math_partial_type_<T>>::value, math_partial_type_<T>>::type;
665 }
666 
667 namespace math
668 {
669 
671 
690 template <typename T>
691 inline detail::math_partial_type<T> partial(const T &x, const std::string &str)
692 {
693  return partial_impl<T>{}(x, str);
694 }
695 
697 
701 template <typename T, typename Enable = void>
703 };
704 }
705 
706 namespace detail
707 {
708 
709 // Return type for math::integrate().
710 template <typename T>
711 using math_integrate_type_
712  = decltype(math::integrate_impl<T>{}(std::declval<const T &>(), std::declval<const std::string &>()));
713 
714 template <typename T>
715 using math_integrate_type =
716  typename std::enable_if<is_returnable<math_integrate_type_<T>>::value, math_integrate_type_<T>>::type;
717 }
718 
719 namespace math
720 {
721 
723 
742 template <typename T>
743 inline detail::math_integrate_type<T> integrate(const T &x, const std::string &str)
744 {
745  return integrate_impl<T>{}(x, str);
746 }
747 
749 
752 template <typename T, typename U, typename = void>
754 {
755  template <typename T1, typename U1>
756  using ret_t = typename std::conditional<is_detected<add_t, T1, U1>::value, detected_t<add_t, T1, U1>, T1>::type;
757  template <typename T1, typename U1>
758  using ret_type = enable_if_t<
759  conjunction<is_returnable<ret_t<T1, U1>>, std::is_constructible<ret_t<T1, U1>, const T1 &>>::value,
760  ret_t<T1, U1>>;
761 
762 public:
764 
786  template <typename T1, typename U1>
787  ret_type<T1, U1> operator()(const T1 &x, const symbol_fmap<U1> &) const
788  {
789  return ret_type<T1, U1>(x);
790  }
791 };
792 }
793 
794 inline namespace impl
795 {
796 
797 // Return type for math::evaluate().
798 template <typename T, typename U>
799 using math_evaluate_t_
800  = decltype(math::evaluate_impl<T, U>{}(std::declval<const T &>(), std::declval<const symbol_fmap<U> &>()));
801 
802 template <typename T, typename U>
803 using math_evaluate_t = enable_if_t<is_returnable<math_evaluate_t_<T, U>>::value, math_evaluate_t_<T, U>>;
804 }
805 
806 namespace math
807 {
808 
810 
830 template <typename U, typename T>
831 inline math_evaluate_t<T, U> evaluate(const T &x, const symbol_fmap<U> &dict)
832 {
833  return evaluate_impl<T, U>{}(x, dict);
834 }
835 
837 
841 template <typename T, typename U, typename Enable = void>
842 struct subs_impl {
843 };
844 }
845 
846 namespace detail
847 {
848 
849 // Return type for math::subs().
850 template <typename T, typename U>
851 using math_subs_type_
852  = decltype(math::subs_impl<T, U>{}(std::declval<const T &>(), std::declval<const symbol_fmap<U> &>()));
853 
854 template <typename T, typename U>
855 using math_subs_type = enable_if_t<is_returnable<math_subs_type_<T, U>>::value, math_subs_type_<T, U>>;
856 }
857 
858 namespace math
859 {
860 
862 
880 template <typename U, typename T>
881 inline detail::math_subs_type<T, U> subs(const T &x, const symbol_fmap<U> &dict)
882 {
883  return subs_impl<T, U>{}(x, dict);
884 }
885 
887 
891 template <typename T, typename U, typename V, typename = void>
892 struct t_subs_impl {
893 };
894 }
895 
896 namespace detail
897 {
898 
899 // Return type for math::t_subs().
900 template <typename T, typename U, typename V>
901 using math_t_subs_type_
902  = decltype(math::t_subs_impl<T, U, V>{}(std::declval<const T &>(), std::declval<const std::string &>(),
903  std::declval<const U &>(), std::declval<const V &>()));
904 
905 template <typename T, typename U, typename V>
906 using math_t_subs_type =
907  typename std::enable_if<is_returnable<math_t_subs_type_<T, U, V>>::value, math_t_subs_type_<T, U, V>>::type;
908 }
909 
910 namespace math
911 {
912 
914 
935 template <typename T, typename U, typename V>
936 inline detail::math_t_subs_type<T, U, V> t_subs(const T &x, const std::string &name, const U &c, const V &s)
937 {
938  return t_subs_impl<T, U, V>{}(x, name, c, s);
939 }
940 
942 
946 template <typename T, typename Enable = void>
947 struct abs_impl {
948 };
949 }
950 
951 namespace detail
952 {
953 
954 template <typename T>
955 using abs_arith_enabler = typename std::enable_if<(std::is_signed<T>::value && std::is_integral<T>::value)
956  || (std::is_unsigned<T>::value && std::is_integral<T>::value)
957  || std::is_floating_point<T>::value>::type;
958 }
959 
960 namespace math
961 {
962 
964 
969 template <typename T>
970 struct abs_impl<T, detail::abs_arith_enabler<T>> {
971 private:
972  template <typename U>
973  static U impl(const U &x, typename std::enable_if<std::is_floating_point<U>::value>::type * = nullptr)
974  {
975  return std::abs(x);
976  }
977  // NOTE: use decltype here so, in the remote case we are dealing with an extended integer types (for which
978  // std::abs() will not exist and cast to long long might be lossy), the overload will be discarded.
979  template <typename U>
980  static auto impl(const U &x,
981  typename std::enable_if<std::is_integral<U>::value && std::is_signed<U>::value>::type * = nullptr)
982  -> decltype(static_cast<U>(std::abs(static_cast<long long>(x))))
983  {
984  // Cast to long long in order to avoid conversion derps when dealing with chars.
985  return static_cast<U>(std::abs(static_cast<long long>(x)));
986  }
987  template <typename U>
988  static U impl(const U &x,
989  typename std::enable_if<std::is_integral<U>::value && std::is_unsigned<U>::value>::type * = nullptr)
990  {
991  return x;
992  }
993 
994 public:
996 
1001  auto operator()(const T &x) const -> decltype(impl(x))
1002  {
1003  return impl(x);
1004  }
1005 };
1006 
1008 
1017 template <typename T>
1018 inline auto abs(const T &x) -> decltype(abs_impl<T>()(x))
1019 {
1020  return abs_impl<T>()(x);
1021 }
1022 }
1023 
1025 
1028 template <typename T>
1029 class has_abs
1030 {
1031  template <typename U>
1032  using abs_t = decltype(math::abs(std::declval<const U &>()));
1033  static const bool implementation_defined = is_detected<abs_t, T>::value;
1034 
1035 public:
1037  static const bool value = implementation_defined;
1038 };
1039 
1040 // Static init.
1041 template <typename T>
1042 const bool has_abs<T>::value;
1043 
1045 
1048 template <typename T>
1050 {
1051  template <typename U>
1052  using is_zero_t = decltype(math::is_zero(std::declval<const U &>()));
1053  static const bool implementation_defined = is_detected<is_zero_t, T>::value;
1054 
1055 public:
1057  static const bool value = implementation_defined;
1058 };
1059 
1060 // Static init.
1061 template <typename T>
1062 const bool has_is_zero<T>::value;
1063 
1065 
1069 template <typename T>
1071 {
1072  template <typename U>
1073  using negate_t = decltype(math::negate(std::declval<U &>()));
1074  static const bool implementation_defined = is_detected<negate_t, T>::value;
1075 
1076 public:
1078  static const bool value = implementation_defined;
1079 };
1080 
1081 template <typename T>
1082 const bool has_negate<T>::value;
1083 
1084 namespace detail
1085 {
1086 
1087 #if !defined(PIRANHA_DOXYGEN_INVOKED)
1088 
1089 // Type definition and type checking for the output of Poisson brackets.
1090 template <typename T>
1091 using pbracket_type_tmp = decltype(math::partial(std::declval<const T &>(), std::string())
1092  * math::partial(std::declval<const T &>(), std::string()));
1093 
1094 template <typename T, typename = void>
1095 struct pbracket_type_ {
1096 };
1097 
1098 template <typename T>
1099 struct pbracket_type_<
1100  T, typename std::enable_if<std::is_same<decltype(std::declval<const pbracket_type_tmp<T> &>()
1101  + std::declval<const pbracket_type_tmp<T> &>()),
1102  pbracket_type_tmp<T>>::value
1103  && std::is_same<decltype(std::declval<const pbracket_type_tmp<T> &>()
1104  - std::declval<const pbracket_type_tmp<T> &>()),
1105  pbracket_type_tmp<T>>::value
1106  && std::is_constructible<pbracket_type_tmp<T>, int>::value
1107  && std::is_assignable<pbracket_type_tmp<T> &, pbracket_type_tmp<T>>::value>::type> {
1108  using type = pbracket_type_tmp<T>;
1109 };
1110 
1111 // The final typedef.
1112 template <typename T>
1113 using pbracket_type = typename pbracket_type_<T>::type;
1114 
1115 #endif
1116 }
1117 
1118 namespace math
1119 {
1120 
1122 
1151 template <typename T>
1152 inline detail::pbracket_type<T> pbracket(const T &f, const T &g, const std::vector<std::string> &p_list,
1153  const std::vector<std::string> &q_list)
1154 {
1155  using return_type = detail::pbracket_type<T>;
1156  if (p_list.size() != q_list.size()) {
1157  piranha_throw(std::invalid_argument, "the number of coordinates is different from the number of momenta");
1158  }
1159  if (std::unordered_set<std::string>(p_list.begin(), p_list.end()).size() != p_list.size()) {
1160  piranha_throw(std::invalid_argument, "the list of momenta contains duplicate entries");
1161  }
1162  if (std::unordered_set<std::string>(q_list.begin(), q_list.end()).size() != q_list.size()) {
1163  piranha_throw(std::invalid_argument, "the list of coordinates contains duplicate entries");
1164  }
1165  return_type retval = return_type(0);
1166  for (decltype(p_list.size()) i = 0u; i < p_list.size(); ++i) {
1167  // NOTE: could use multadd/sub here, if we implement it for series.
1168  retval = retval + partial(f, q_list[i]) * partial(g, p_list[i]);
1169  retval = retval - partial(f, p_list[i]) * partial(g, q_list[i]);
1170  }
1171  return retval;
1172 }
1173 }
1174 
1176 
1180 template <typename T>
1181 class has_pbracket : detail::sfinae_types
1182 {
1183  using v_string = std::vector<std::string>;
1184  template <typename T1>
1185  static auto test(const T1 &x)
1186  -> decltype(math::pbracket(x, x, std::declval<v_string const &>(), std::declval<v_string const &>()), void(),
1187  yes());
1188  static no test(...);
1189 
1190 public:
1192  static const bool value = std::is_same<decltype(test(std::declval<T>())), yes>::value;
1193 };
1194 
1195 template <typename T>
1196 const bool has_pbracket<T>::value;
1197 
1198 namespace detail
1199 {
1200 
1201 template <typename T>
1202 using is_canonical_enabler = typename std::enable_if<has_pbracket<T>::value && has_is_zero<pbracket_type<T>>::value
1203  && std::is_constructible<pbracket_type<T>, int>::value
1205  int>::type;
1206 
1207 template <typename T>
1208 inline bool is_canonical_impl(const std::vector<T const *> &new_p, const std::vector<T const *> &new_q,
1209  const std::vector<std::string> &p_list, const std::vector<std::string> &q_list)
1210 {
1211  using p_type = decltype(math::pbracket(*new_q[0], *new_p[0], p_list, q_list));
1212  if (p_list.size() != q_list.size()) {
1213  piranha_throw(std::invalid_argument, "the number of coordinates is different from the number of momenta");
1214  }
1215  if (new_p.size() != new_q.size()) {
1216  piranha_throw(std::invalid_argument,
1217  "the number of new coordinates is different from the number of new momenta");
1218  }
1219  if (p_list.size() != new_p.size()) {
1220  piranha_throw(std::invalid_argument, "the number of new momenta is different from the number of momenta");
1221  }
1222  if (std::unordered_set<std::string>(p_list.begin(), p_list.end()).size() != p_list.size()) {
1223  piranha_throw(std::invalid_argument, "the list of momenta contains duplicate entries");
1224  }
1225  if (std::unordered_set<std::string>(q_list.begin(), q_list.end()).size() != q_list.size()) {
1226  piranha_throw(std::invalid_argument, "the list of coordinates contains duplicate entries");
1227  }
1228  const auto size = new_p.size();
1229  for (decltype(new_p.size()) i = 0u; i < size; ++i) {
1230  for (decltype(new_p.size()) j = 0u; j < size; ++j) {
1231  // NOTE: no need for actually doing computations when i == j.
1232  if (i != j && !math::is_zero(math::pbracket(*new_p[i], *new_p[j], p_list, q_list))) {
1233  return false;
1234  }
1235  if (i != j && !math::is_zero(math::pbracket(*new_q[i], *new_q[j], p_list, q_list))) {
1236  return false;
1237  }
1238  // Poisson bracket needs to be zero for i != j, one for i == j.
1239  // NOTE: cast from bool to int is always 0 or 1.
1240  if (math::pbracket(*new_q[i], *new_p[j], p_list, q_list) != p_type(static_cast<int>(i == j))) {
1241  return false;
1242  }
1243  }
1244  }
1245  return true;
1246 }
1247 }
1248 
1249 namespace math
1250 {
1251 
1253 
1282 template <typename T, detail::is_canonical_enabler<T> = 0>
1283 inline bool transformation_is_canonical(const std::vector<T> &new_p, const std::vector<T> &new_q,
1284  const std::vector<std::string> &p_list, const std::vector<std::string> &q_list)
1285 {
1286  std::vector<T const *> pv, qv;
1287  std::transform(new_p.begin(), new_p.end(), std::back_inserter(pv), [](const T &p) { return &p; });
1288  std::transform(new_q.begin(), new_q.end(), std::back_inserter(qv), [](const T &q) { return &q; });
1289  return detail::is_canonical_impl(pv, qv, p_list, q_list);
1290 }
1291 
1292 // clang-format off
1294 
1304 // clang-format on
1305 template <typename T, detail::is_canonical_enabler<T> = 0>
1306 inline bool transformation_is_canonical(std::initializer_list<T> new_p, std::initializer_list<T> new_q,
1307  const std::vector<std::string> &p_list, const std::vector<std::string> &q_list)
1308 {
1309  std::vector<T const *> pv, qv;
1310  std::transform(new_p.begin(), new_p.end(), std::back_inserter(pv), [](const T &p) { return &p; });
1311  std::transform(new_q.begin(), new_q.end(), std::back_inserter(qv), [](const T &q) { return &q; });
1312  return detail::is_canonical_impl(pv, qv, p_list, q_list);
1313 }
1314 
1316 
1324 template <typename T, typename Enable = void>
1325 struct degree_impl {
1326 };
1327 
1329 
1340 template <typename T>
1341 inline auto degree(const T &x) -> decltype(degree_impl<T>()(x))
1342 {
1343  return degree_impl<T>()(x);
1344 }
1345 
1347 
1361 template <typename T>
1362 inline auto degree(const T &x, const symbol_fset &s) -> decltype(degree_impl<T>()(x, s))
1363 {
1364  return degree_impl<T>()(x, s);
1365 }
1366 
1368 
1376 template <typename T, typename Enable = void>
1378 };
1379 
1381 
1392 template <typename T>
1393 inline auto ldegree(const T &x) -> decltype(ldegree_impl<T>()(x))
1394 {
1395  return ldegree_impl<T>()(x);
1396 }
1397 
1399 
1413 template <typename T>
1414 inline auto ldegree(const T &x, const symbol_fset &s) -> decltype(ldegree_impl<T>()(x, s))
1415 {
1416  return ldegree_impl<T>()(x, s);
1417 }
1418 
1420 
1428 template <typename T, typename Enable = void>
1430 };
1431 
1433 
1449 template <typename T>
1450 inline auto t_degree(const T &x) -> decltype(t_degree_impl<T>{}(x))
1451 {
1452  return t_degree_impl<T>{}(x);
1453 }
1454 
1456 
1469 template <typename T>
1470 inline auto t_degree(const T &x, const symbol_fset &names) -> decltype(t_degree_impl<T>{}(x, names))
1471 {
1472  return t_degree_impl<T>{}(x, names);
1473 }
1474 
1476 
1484 template <typename T, typename Enable = void>
1486 };
1487 
1489 
1505 template <typename T>
1506 inline auto t_ldegree(const T &x) -> decltype(t_ldegree_impl<T>{}(x))
1507 {
1508  return t_ldegree_impl<T>{}(x);
1509 }
1510 
1512 
1525 template <typename T>
1526 inline auto t_ldegree(const T &x, const symbol_fset &names) -> decltype(t_ldegree_impl<T>{}(x, names))
1527 {
1528  return t_ldegree_impl<T>{}(x, names);
1529 }
1530 
1532 
1540 template <typename T, typename Enable = void>
1542 };
1543 
1545 
1563 template <typename T>
1564 inline auto t_order(const T &x) -> decltype(t_order_impl<T>{}(x))
1565 {
1566  return t_order_impl<T>{}(x);
1567 }
1568 
1570 
1583 template <typename T>
1584 inline auto t_order(const T &x, const symbol_fset &names) -> decltype(t_order_impl<T>{}(x, names))
1585 {
1586  return t_order_impl<T>{}(x, names);
1587 }
1588 
1590 
1598 template <typename T, typename Enable = void>
1600 };
1601 
1603 
1621 template <typename T>
1622 inline auto t_lorder(const T &x) -> decltype(t_lorder_impl<T>{}(x))
1623 {
1624  return t_lorder_impl<T>{}(x);
1625 }
1626 
1628 
1641 template <typename T>
1642 inline auto t_lorder(const T &x, const symbol_fset &names) -> decltype(t_lorder_impl<T>{}(x, names))
1643 {
1644  return t_lorder_impl<T>{}(x, names);
1645 }
1646 
1648 
1651 template <typename T, typename U, typename = void>
1653 };
1654 }
1655 
1656 namespace detail
1657 {
1658 
1659 // Enablers for the degree truncation methods.
1660 template <typename T, typename U>
1661 using truncate_degree_enabler = typename std::enable_if<
1662  std::is_same<decltype(math::truncate_degree_impl<T, U>()(std::declval<const T &>(), std::declval<const U &>())),
1663  T>::value,
1664  int>::type;
1665 
1666 template <typename T, typename U>
1667 using truncate_pdegree_enabler = typename std::enable_if<
1668  std::is_same<decltype(math::truncate_degree_impl<T, U>()(std::declval<const T &>(), std::declval<const U &>(),
1669  std::declval<const symbol_fset &>())),
1670  T>::value,
1671  int>::type;
1672 }
1673 
1674 namespace math
1675 {
1676 
1678 
1698 template <typename T, typename U, detail::truncate_degree_enabler<T, U> = 0>
1699 inline T truncate_degree(const T &x, const U &max_degree)
1700 {
1701  return truncate_degree_impl<T, U>()(x, max_degree);
1702 }
1703 
1705 
1726 template <typename T, typename U, detail::truncate_pdegree_enabler<T, U> = 0>
1727 inline T truncate_degree(const T &x, const U &max_degree, const symbol_fset &names)
1728 {
1729  return truncate_degree_impl<T, U>()(x, max_degree, names);
1730 }
1731 }
1732 
1734 
1738 template <typename T, typename U>
1739 class has_truncate_degree : detail::sfinae_types
1740 {
1741  template <typename T1, typename U1>
1742  static auto test1(const T1 &t, const U1 &u) -> decltype(math::truncate_degree(t, u), void(), yes());
1743  static no test1(...);
1744  template <typename T1, typename U1>
1745  static auto test2(const T1 &t, const U1 &u)
1746  -> decltype(math::truncate_degree(t, u, std::declval<const symbol_fset &>()), void(), yes());
1747  static no test2(...);
1748 
1749 public:
1751  static const bool value = std::is_same<decltype(test1(std::declval<T>(), std::declval<U>())), yes>::value
1752  && std::is_same<decltype(test2(std::declval<T>(), std::declval<U>())), yes>::value;
1753 };
1754 
1755 template <typename T, typename U>
1757 
1759 
1763 template <typename T>
1764 class is_differentiable : detail::sfinae_types
1765 {
1766  template <typename U>
1767  static auto test(const U &u) -> decltype(math::partial(u, ""), void(), yes());
1768  static no test(...);
1769  static const bool implementation_defined = std::is_same<decltype(test(std::declval<T>())), yes>::value;
1770 
1771 public:
1773  static const bool value = implementation_defined;
1774 };
1775 
1776 // Static init.
1777 template <typename T>
1778 const bool is_differentiable<T>::value;
1779 
1780 inline namespace impl
1781 {
1782 
1783 // A key is differentiable/integrable if the type returned by the partial/integrate method
1784 // is a pair (sometype,key).
1785 template <typename, typename>
1786 struct is_differential_key_pair : std::false_type {
1787 };
1788 
1789 template <typename Key, typename T>
1790 struct is_differential_key_pair<Key, std::pair<T, Key>> : std::true_type {
1791 };
1792 }
1793 
1795 
1806 template <typename Key>
1808 {
1809  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
1810  template <typename U>
1811  using key_partial_t = decltype(
1812  std::declval<const U &>().partial(std::declval<const symbol_idx &>(), std::declval<const symbol_fset &>()));
1813  static const bool implementation_defined
1814  = is_differential_key_pair<uncvref_t<Key>, detected_t<key_partial_t, Key>>::value;
1815 
1816 public:
1818  static const bool value = implementation_defined;
1819 };
1820 
1821 template <typename Key>
1823 
1825 
1829 template <typename T>
1830 class is_integrable : detail::sfinae_types
1831 {
1832  template <typename U>
1833  static auto test(const U &u) -> decltype(math::integrate(u, ""), void(), yes());
1834  static no test(...);
1835  static const bool implementation_defined = std::is_same<decltype(test(std::declval<T>())), yes>::value;
1836 
1837 public:
1839  static const bool value = implementation_defined;
1840 };
1841 
1842 template <typename T>
1843 const bool is_integrable<T>::value;
1844 
1846 
1857 template <typename Key>
1859 {
1860  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
1861  template <typename U>
1862  using key_integrate_t = decltype(
1863  std::declval<const U &>().integrate(std::declval<const std::string &>(), std::declval<const symbol_fset &>()));
1864  static const bool implementation_defined
1865  = is_differential_key_pair<uncvref_t<Key>, detected_t<key_integrate_t, Key>>::value;
1866 
1867 public:
1869  static const bool value = implementation_defined;
1870 };
1871 
1872 template <typename T>
1873 const bool key_is_integrable<T>::value;
1874 
1876 
1880 template <typename T>
1881 class has_degree : detail::sfinae_types
1882 {
1883  template <typename U>
1884  static auto test1(const U &u) -> decltype(math::degree(u), void(), yes());
1885  static no test1(...);
1886  template <typename U>
1887  static auto test2(const U &u) -> decltype(math::degree(u, std::declval<const symbol_fset &>()), void(), yes());
1888  static no test2(...);
1889 
1890 public:
1892  static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1893  && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1894 };
1895 
1896 // Static init.
1897 template <typename T>
1898 const bool has_degree<T>::value;
1899 
1901 
1905 template <typename T>
1906 class has_ldegree : detail::sfinae_types
1907 {
1908  template <typename U>
1909  static auto test1(const U &u) -> decltype(math::ldegree(u), void(), yes());
1910  static no test1(...);
1911  template <typename U>
1912  static auto test2(const U &u) -> decltype(math::ldegree(u, std::declval<const symbol_fset &>()), void(), yes());
1913  static no test2(...);
1914 
1915 public:
1917  static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1918  && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1919 };
1920 
1921 // Static init.
1922 template <typename T>
1923 const bool has_ldegree<T>::value;
1924 
1926 
1930 template <typename T>
1931 class has_t_degree : detail::sfinae_types
1932 {
1933  template <typename U>
1934  static auto test1(const U &u) -> decltype(math::t_degree(u), void(), yes());
1935  static no test1(...);
1936  template <typename U>
1937  static auto test2(const U &u) -> decltype(math::t_degree(u, std::declval<const symbol_fset &>()), void(), yes());
1938  static no test2(...);
1939 
1940 public:
1942  static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1943  && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1944 };
1945 
1946 // Static init.
1947 template <typename T>
1948 const bool has_t_degree<T>::value;
1949 
1951 
1955 template <typename T>
1956 class has_t_ldegree : detail::sfinae_types
1957 {
1958  template <typename U>
1959  static auto test1(const U &u) -> decltype(math::t_ldegree(u), void(), yes());
1960  static no test1(...);
1961  template <typename U>
1962  static auto test2(const U &u) -> decltype(math::t_ldegree(u, std::declval<const symbol_fset &>()), void(), yes());
1963  static no test2(...);
1964 
1965 public:
1967  static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1968  && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1969 };
1970 
1971 // Static init.
1972 template <typename T>
1973 const bool has_t_ldegree<T>::value;
1974 
1976 
1980 template <typename T>
1981 class has_t_order : detail::sfinae_types
1982 {
1983  template <typename U>
1984  static auto test1(const U &u) -> decltype(math::t_order(u), void(), yes());
1985  static no test1(...);
1986  template <typename U>
1987  static auto test2(const U &u) -> decltype(math::t_order(u, std::declval<const symbol_fset &>()), void(), yes());
1988  static no test2(...);
1989 
1990 public:
1992  static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
1993  && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
1994 };
1995 
1996 // Static init.
1997 template <typename T>
1998 const bool has_t_order<T>::value;
1999 
2001 
2005 template <typename T>
2006 class has_t_lorder : detail::sfinae_types
2007 {
2008  template <typename U>
2009  static auto test1(const U &u) -> decltype(math::t_lorder(u), void(), yes());
2010  static no test1(...);
2011  template <typename U>
2012  static auto test2(const U &u) -> decltype(math::t_lorder(u, std::declval<const symbol_fset &>()), void(), yes());
2013  static no test2(...);
2014 
2015 public:
2017  static const bool value = std::is_same<decltype(test1(std::declval<T>())), yes>::value
2018  && std::is_same<decltype(test2(std::declval<T>())), yes>::value;
2019 };
2020 
2021 // Static init.
2022 template <typename T>
2023 const bool has_t_lorder<T>::value;
2024 
2026 
2038 template <typename Key>
2040 {
2041  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
2042  template <typename U>
2043  using total_degree_t = decltype(std::declval<const U &>().degree(std::declval<const symbol_fset &>()));
2044  template <typename U>
2045  using partial_degree_t = decltype(
2046  std::declval<const U &>().degree(std::declval<const symbol_idx_fset &>(), std::declval<const symbol_fset &>()));
2047  static const bool implementation_defined
2048  = conjunction<is_detected<total_degree_t, Key>, is_detected<partial_degree_t, Key>>::value;
2049 
2050 public:
2052  static const bool value = implementation_defined;
2053 };
2054 
2055 template <typename Key>
2056 const bool key_has_degree<Key>::value;
2057 
2059 
2071 template <typename Key>
2073 {
2074  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
2075  template <typename U>
2076  using total_ldegree_t = decltype(std::declval<const U &>().ldegree(std::declval<const symbol_fset &>()));
2077  template <typename U>
2078  using partial_ldegree_t = decltype(std::declval<const U &>().ldegree(std::declval<const symbol_idx_fset &>(),
2079  std::declval<const symbol_fset &>()));
2080  static const bool implementation_defined
2081  = conjunction<is_detected<total_ldegree_t, Key>, is_detected<partial_ldegree_t, Key>>::value;
2082 
2083 public:
2085  static const bool value = implementation_defined;
2086 };
2087 
2088 template <typename Key>
2089 const bool key_has_ldegree<Key>::value;
2090 
2092 
2104 template <typename Key>
2106 {
2107  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
2108  template <typename U>
2109  using total_t_degree_t = decltype(std::declval<const U &>().t_degree(std::declval<const symbol_fset &>()));
2110  template <typename U>
2111  using partial_t_degree_t = decltype(std::declval<const U &>().t_degree(std::declval<const symbol_idx_fset &>(),
2112  std::declval<const symbol_fset &>()));
2113  static const bool implementation_defined
2114  = conjunction<is_detected<total_t_degree_t, Key>, is_detected<partial_t_degree_t, Key>>::value;
2115 
2116 public:
2118  static const bool value = implementation_defined;
2119 };
2120 
2121 // Static init.
2122 template <typename T>
2123 const bool key_has_t_degree<T>::value;
2124 
2126 
2138 template <typename Key>
2140 {
2141  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
2142  template <typename U>
2143  using total_t_ldegree_t = decltype(std::declval<const U &>().t_ldegree(std::declval<const symbol_fset &>()));
2144  template <typename U>
2145  using partial_t_ldegree_t = decltype(std::declval<const U &>().t_ldegree(std::declval<const symbol_idx_fset &>(),
2146  std::declval<const symbol_fset &>()));
2147  static const bool implementation_defined
2148  = conjunction<is_detected<total_t_ldegree_t, Key>, is_detected<partial_t_ldegree_t, Key>>::value;
2149 
2150 public:
2152  static const bool value = implementation_defined;
2153 };
2154 
2155 // Static init.
2156 template <typename T>
2157 const bool key_has_t_ldegree<T>::value;
2158 
2160 
2172 template <typename Key>
2174 {
2175  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
2176  template <typename U>
2177  using total_t_order_t = decltype(std::declval<const U &>().t_order(std::declval<const symbol_fset &>()));
2178  template <typename U>
2179  using partial_t_order_t = decltype(std::declval<const U &>().t_order(std::declval<const symbol_idx_fset &>(),
2180  std::declval<const symbol_fset &>()));
2181  static const bool implementation_defined
2182  = conjunction<is_detected<total_t_order_t, Key>, is_detected<partial_t_order_t, Key>>::value;
2183 
2184 public:
2186  static const bool value = implementation_defined;
2187 };
2188 
2189 // Static init.
2190 template <typename T>
2191 const bool key_has_t_order<T>::value;
2192 
2194 
2206 template <typename Key>
2208 {
2209  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
2210  template <typename U>
2211  using total_t_lorder_t = decltype(std::declval<const U &>().t_lorder(std::declval<const symbol_fset &>()));
2212  template <typename U>
2213  using partial_t_lorder_t = decltype(std::declval<const U &>().t_lorder(std::declval<const symbol_idx_fset &>(),
2214  std::declval<const symbol_fset &>()));
2215  static const bool implementation_defined
2216  = conjunction<is_detected<total_t_lorder_t, Key>, is_detected<partial_t_lorder_t, Key>>::value;
2217 
2218 public:
2220  static const bool value = implementation_defined;
2221 };
2222 
2223 // Static init.
2224 template <typename T>
2225 const bool key_has_t_lorder<T>::value;
2226 
2228 
2231 template <typename T>
2232 class has_is_unitary : detail::sfinae_types
2233 {
2234  typedef typename std::decay<T>::type Td;
2235  template <typename T1>
2236  static auto test(const T1 &t) -> decltype(math::is_unitary(t), void(), yes());
2237  static no test(...);
2238  static const bool implementation_defined = std::is_same<decltype(test(std::declval<Td>())), yes>::value;
2239 
2240 public:
2242  static const bool value = implementation_defined;
2243 };
2244 
2245 template <typename T>
2246 const bool has_is_unitary<T>::value;
2247 
2249 
2253 template <typename T, typename U>
2254 class has_subs : detail::sfinae_types
2255 {
2256  typedef typename std::decay<T>::type Td;
2257  typedef typename std::decay<U>::type Ud;
2258  template <typename T1, typename U1>
2259  static auto test(const T1 &t, const U1 &u)
2260  -> decltype(math::subs(t, std::declval<const symbol_fmap<U1> &>()), void(), yes());
2261  static no test(...);
2262  static const bool implementation_defined
2263  = std::is_same<decltype(test(std::declval<Td>(), std::declval<Ud>())), yes>::value;
2264 
2265 public:
2267  static const bool value = implementation_defined;
2268 };
2269 
2270 // Static init.
2271 template <typename T, typename U>
2272 const bool has_subs<T, U>::value;
2273 
2275 
2279 template <typename T, typename U, typename V>
2280 class has_t_subs : detail::sfinae_types
2281 {
2282  typedef typename std::decay<T>::type Td;
2283  typedef typename std::decay<U>::type Ud;
2284  typedef typename std::decay<V>::type Vd;
2285  template <typename T1, typename U1, typename V1>
2286  static auto test(const T1 &t, const U1 &u, const V1 &v)
2287  -> decltype(math::t_subs(t, std::declval<std::string const &>(), u, v), void(), yes());
2288  static no test(...);
2289  static const bool implementation_defined
2290  = std::is_same<decltype(test(std::declval<Td>(), std::declval<Ud>(), std::declval<Vd>())), yes>::value;
2291 
2292 public:
2294  static const bool value = implementation_defined;
2295 };
2296 
2297 // Static init.
2298 template <typename T, typename U, typename V>
2299 const bool has_t_subs<T, U, V>::value;
2300 
2301 inline namespace impl
2302 {
2303 
2304 // Check the result of key subs methods.
2305 template <typename, typename>
2306 struct key_subs_check_type : std::false_type {
2307 };
2308 
2309 template <typename Key, typename T>
2310 struct key_subs_check_type<Key, std::vector<std::pair<T, Key>>> : std::true_type {
2311 };
2312 }
2313 
2315 
2327 template <typename Key, typename T, typename U>
2329 {
2330  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
2331  template <typename Key1, typename T1, typename U1>
2332  using t_subs_t = decltype(
2333  std::declval<const Key1 &>().t_subs(std::declval<const symbol_idx &>(), std::declval<const T1 &>(),
2334  std::declval<const U1 &>(), std::declval<const symbol_fset &>()));
2335  static const bool implementation_defined
2336  = key_subs_check_type<uncvref_t<Key>, detected_t<t_subs_t, Key, T, U>>::value;
2337 
2338 public:
2340  static const bool value = implementation_defined;
2341 };
2342 
2343 // Static init.
2344 template <typename Key, typename T, typename U>
2346 
2348 
2359 template <typename Key, typename T>
2361 {
2362  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
2363  template <typename Key1, typename T1>
2364  using subs_t = decltype(std::declval<const Key1 &>().subs(std::declval<const symbol_idx_fmap<T1> &>(),
2365  std::declval<const symbol_fset &>()));
2366  static const bool implementation_defined = key_subs_check_type<uncvref_t<Key>, detected_t<subs_t, Key, T>>::value;
2367 
2368 public:
2370  static const bool value = implementation_defined;
2371 };
2372 
2373 // Static init.
2374 template <typename Key, typename T>
2375 const bool key_has_subs<Key, T>::value;
2376 
2378 
2382 template <typename T>
2384 {
2385  template <typename U>
2386  using multiply_accumulate_t = decltype(
2387  math::multiply_accumulate(std::declval<U &>(), std::declval<const U &>(), std::declval<const U &>()));
2388  static const bool implementation_defined = is_detected<multiply_accumulate_t, T>::value;
2389 
2390 public:
2392  static const bool value = implementation_defined;
2393 };
2394 
2395 // Static init.
2396 template <typename T>
2398 
2400 
2404 template <typename T, typename U>
2406 {
2407  template <typename T1, typename U1>
2408  using eval_t = decltype(math::evaluate(std::declval<const T1 &>(), std::declval<const symbol_fmap<U1> &>()));
2409  static const bool implementation_defined = is_detected<eval_t, T, U>::value;
2410 
2411 public:
2413  static const bool value = implementation_defined;
2414 };
2415 
2416 template <typename T, typename U>
2417 const bool is_evaluable<T, U>::value;
2418 
2420 
2430 template <typename Key, typename T>
2432 {
2433  PIRANHA_TT_CHECK(is_key, uncvref_t<Key>);
2434  template <typename Key1, typename T1>
2435  using evaluate_t = decltype(std::declval<const Key1 &>().evaluate(std::declval<const std::vector<T1> &>(),
2436  std::declval<const symbol_fset &>()));
2437  static const bool implementation_defined = is_detected<evaluate_t, Key, T>::value;
2438 
2439 public:
2441  static const bool value = implementation_defined;
2442 };
2443 
2444 template <typename Key, typename T>
2446 
2448 
2452 template <typename T>
2453 class has_sine : detail::sfinae_types
2454 {
2455  template <typename T1>
2456  static auto test(const T1 &x) -> decltype(math::sin(x), void(), yes());
2457  static no test(...);
2458 
2459 public:
2461  static const bool value = std::is_same<decltype(test(std::declval<T>())), yes>::value;
2462 };
2463 
2464 template <typename T>
2465 const bool has_sine<T>::value;
2466 
2468 
2472 template <typename T>
2473 class has_cosine : detail::sfinae_types
2474 {
2475  template <typename T1>
2476  static auto test(const T1 &x) -> decltype(math::cos(x), void(), yes());
2477  static no test(...);
2478 
2479 public:
2481  static const bool value = std::is_same<decltype(test(std::declval<T>())), yes>::value;
2482 };
2483 
2484 template <typename T>
2485 const bool has_cosine<T>::value;
2486 
2488 
2492 template <typename T>
2493 class has_transformation_is_canonical : detail::sfinae_types
2494 {
2495  using v_string = std::vector<std::string>;
2496  template <typename T1>
2497  static auto test(const T1 &) -> decltype(math::transformation_is_canonical(std::declval<std::vector<T1> const &>(),
2498  std::declval<std::vector<T1> const &>(),
2499  std::declval<v_string const &>(),
2500  std::declval<v_string const &>()),
2501  void(), yes());
2502  static no test(...);
2503 
2504 public:
2506  static const bool value = std::is_same<decltype(test(std::declval<T>())), yes>::value;
2507 };
2508 
2509 template <typename T>
2511 
2512 namespace math
2513 {
2514 
2516 
2519 template <typename T, typename Enable = void>
2520 struct add3_impl {
2522 
2537  template <typename U>
2538  auto operator()(U &a, const U &b, const U &c) const -> decltype(a = b + c)
2539  {
2540  return a = b + c;
2541  }
2542 };
2543 
2545 
2548 template <typename T>
2549 struct add3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2551 
2561  T &operator()(T &a, const T &b, const T &c) const
2562  {
2563  return a = static_cast<T>(b + c);
2564  }
2565 };
2566 
2568 
2581 template <typename T>
2582 inline auto add3(T &a, const T &b, const T &c) -> decltype(add3_impl<T>()(a, b, c))
2583 {
2584  return add3_impl<T>()(a, b, c);
2585 }
2586 
2588 
2591 template <typename T, typename Enable = void>
2592 struct sub3_impl {
2594 
2609  template <typename U>
2610  auto operator()(U &a, const U &b, const U &c) const -> decltype(a = b - c)
2611  {
2612  return a = b - c;
2613  }
2614 };
2615 
2617 
2620 template <typename T>
2621 struct sub3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2623 
2633  T &operator()(T &a, const T &b, const T &c) const
2634  {
2635  return a = static_cast<T>(b - c);
2636  }
2637 };
2638 
2640 
2653 template <typename T>
2654 inline auto sub3(T &a, const T &b, const T &c) -> decltype(sub3_impl<T>()(a, b, c))
2655 {
2656  return sub3_impl<T>()(a, b, c);
2657 }
2658 
2660 
2663 template <typename T, typename Enable = void>
2664 struct mul3_impl {
2666 
2681  template <typename U>
2682  auto operator()(U &a, const U &b, const U &c) const -> decltype(a = b * c)
2683  {
2684  return a = b * c;
2685  }
2686 };
2687 
2689 
2692 template <typename T>
2693 struct mul3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2695 
2705  T &operator()(T &a, const T &b, const T &c) const
2706  {
2707  return a = static_cast<T>(b * c);
2708  }
2709 };
2710 
2712 
2725 template <typename T>
2726 inline auto mul3(T &a, const T &b, const T &c) -> decltype(mul3_impl<T>()(a, b, c))
2727 {
2728  return mul3_impl<T>()(a, b, c);
2729 }
2730 
2732 
2735 template <typename T, typename Enable = void>
2736 struct div3_impl {
2738 
2753  template <typename U>
2754  auto operator()(U &a, const U &b, const U &c) const -> decltype(a = b / c)
2755  {
2756  return a = b / c;
2757  }
2758 };
2759 
2761 
2764 template <typename T>
2765 struct div3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2767 
2777  T &operator()(T &a, const T &b, const T &c) const
2778  {
2779  return a = static_cast<T>(b / c);
2780  }
2781 };
2782 
2784 
2797 template <typename T>
2798 inline auto div3(T &a, const T &b, const T &c) -> decltype(div3_impl<T>()(a, b, c))
2799 {
2800  return div3_impl<T>()(a, b, c);
2801 }
2802 }
2803 
2804 namespace detail
2805 {
2806 
2807 // Greatest common divisor using the euclidean algorithm.
2808 // NOTE: this can yield negative values, depending on the signs
2809 // of a and b. Supports C++ integrals and mp_integer.
2810 // NOTE: using this with C++ integrals unchecked on ranges can result in undefined
2811 // behaviour.
2812 template <typename T>
2813 inline T gcd_euclidean(T a, T b)
2814 {
2815  while (true) {
2816  if (math::is_zero(a)) {
2817  return b;
2818  }
2819  b %= a;
2820  if (math::is_zero(b)) {
2821  return a;
2822  }
2823  a %= b;
2824  }
2825 }
2826 }
2827 
2828 namespace math
2829 {
2830 
2832 
2836 template <typename T, typename U, typename = void>
2837 struct gcd_impl {
2838 };
2839 
2841 
2844 template <typename T, typename U>
2845 struct gcd_impl<T, U, typename std::enable_if<std::is_integral<T>::value && std::is_integral<U>::value>::type> {
2847  using p_type = decltype(std::declval<const T &>() + std::declval<const U &>());
2849 
2858  p_type operator()(const T &a, const U &b) const
2859  {
2860  return detail::gcd_euclidean(static_cast<p_type>(a), static_cast<p_type>(b));
2861  }
2862 };
2863 
2865 
2877 template <typename T, typename U>
2878 inline auto gcd(const T &a, const U &b) -> decltype(gcd_impl<T, U>()(a, b))
2879 {
2880  return gcd_impl<T, U>()(a, b);
2881 }
2882 
2884 
2887 template <typename T, typename = void>
2888 struct gcd3_impl {
2890 
2903  template <typename T1>
2904  auto operator()(T1 &out, const T1 &a, const T1 &b) const -> decltype(out = math::gcd(a, b))
2905  {
2906  return out = math::gcd(a, b);
2907  }
2908 };
2909 
2911 
2914 template <typename T>
2915 struct gcd3_impl<T, typename std::enable_if<std::is_integral<T>::value>::type> {
2917 
2926  T &operator()(T &out, const T &a, const T &b) const
2927  {
2928  return out = static_cast<T>(math::gcd(a, b));
2929  }
2930 };
2931 
2933 
2946 template <typename T>
2947 inline auto gcd3(T &out, const T &a, const T &b) -> decltype(gcd3_impl<T>()(out, a, b))
2948 {
2949  return gcd3_impl<T>()(out, a, b);
2950 }
2951 }
2952 
2954 
2958 template <typename T>
2960 {
2961  template <typename U>
2962  using add3_t = decltype(math::add3(std::declval<U &>(), std::declval<const U &>(), std::declval<const U &>()));
2963  static const bool implementation_defined = is_detected<add3_t, T>::value;
2964 
2965 public:
2967  static const bool value = implementation_defined;
2968 };
2969 
2970 template <typename T>
2971 const bool has_add3<T>::value;
2972 
2974 
2978 template <typename T>
2980 {
2981  template <typename U>
2982  using sub3_t = decltype(math::sub3(std::declval<U &>(), std::declval<const U &>(), std::declval<const U &>()));
2983  static const bool implementation_defined = is_detected<sub3_t, T>::value;
2984 
2985 public:
2987  static const bool value = implementation_defined;
2988 };
2989 
2990 template <typename T>
2991 const bool has_sub3<T>::value;
2992 
2994 
2998 template <typename T>
3000 {
3001  template <typename U>
3002  using mul3_t = decltype(math::mul3(std::declval<U &>(), std::declval<const U &>(), std::declval<const U &>()));
3003  static const bool implementation_defined = is_detected<mul3_t, T>::value;
3004 
3005 public:
3007  static const bool value = implementation_defined;
3008 };
3009 
3010 template <typename T>
3011 const bool has_mul3<T>::value;
3012 
3014 
3018 template <typename T>
3020 {
3021  template <typename U>
3022  using div3_t = decltype(math::div3(std::declval<U &>(), std::declval<const U &>(), std::declval<const U &>()));
3023  static const bool implementation_defined = is_detected<div3_t, T>::value;
3024 
3025 public:
3027  static const bool value = implementation_defined;
3028 };
3029 
3030 template <typename T>
3031 const bool has_div3<T>::value;
3032 
3034 
3038 template <typename T, typename U = T>
3039 class has_gcd
3040 {
3041  template <typename T1, typename U1>
3042  using gcd_t = decltype(math::gcd(std::declval<const T1 &>(), std::declval<const U1 &>()));
3043  static const bool implementation_defined = is_detected<gcd_t, T, U>::value;
3044 
3045 public:
3047  static const bool value = implementation_defined;
3048 };
3049 
3050 template <typename T, typename U>
3051 const bool has_gcd<T, U>::value;
3052 
3054 
3058 template <typename T>
3060 {
3061  template <typename U>
3062  using gcd3_t = decltype(math::gcd3(std::declval<U &>(), std::declval<const U &>(), std::declval<const U &>()));
3063  static const bool implementation_defined = is_detected<gcd3_t, T>::value;
3064 
3065 public:
3067  static const bool value = implementation_defined;
3068 };
3069 
3070 template <typename T>
3071 const bool has_gcd3<T>::value;
3072 }
3073 
3074 #endif
Default functor for the implementation of piranha::math::mul3().
Definition: math.hpp:2664
static const bool value
Value of the type trait.
Definition: math.hpp:1773
auto gcd(const T &a, const U &b) -> decltype(gcd_impl< T, U >()(a, b))
GCD.
Definition: math.hpp:2878
Detect piranha::math::gcd3().
Definition: math.hpp:3059
Detect piranha::math::sub3().
Definition: math.hpp:2979
detail::math_integrate_type< T > integrate(const T &x, const std::string &str)
Integration.
Definition: math.hpp:743
Default functor for the implementation of piranha::math::negate().
Definition: math.hpp:253
auto operator()(U &a, const U &b, const U &c) const -> decltype(a=b *c)
Call operator.
Definition: math.hpp:2682
static const bool value
Value of the type trait.
Definition: math.hpp:2017
Default functor for the implementation of piranha::math::div3().
Definition: math.hpp:2736
Default functor for the implementation of piranha::math::t_degree().
Definition: math.hpp:1429
Default functor for the implementation of piranha::math::multiply_accumulate().
Definition: math.hpp:339
Equality-comparable type trait.
detail::math_partial_type< T > partial(const T &x, const std::string &str)
Partial derivative.
Definition: math.hpp:691
Type trait to detect piranha::math::sin().
Definition: math.hpp:2453
T & operator()(T &out, const T &a, const T &b) const
Call operator.
Definition: math.hpp:2926
static const bool value
Value of the type trait.
Definition: math.hpp:1839
Key type concept check.
Definition: is_key.hpp:65
static const bool value
Value of the type trait.
Definition: math.hpp:2267
static const bool value
Value of the type trait.
Definition: math.hpp:1818
static const bool value
Value of the type trait.
Definition: math.hpp:2152
Default functor for the implementation of piranha::math::integrate().
Definition: math.hpp:702
auto add3(T &a, const T &b, const T &c) -> decltype(add3_impl< T >()(a, b, c))
Ternary addition.
Definition: math.hpp:2582
Detect piranha::math::div3().
Definition: math.hpp:3019
auto t_lorder(const T &x) -> decltype(t_lorder_impl< T >
Total trigonometric low order.
Definition: math.hpp:1622
Type trait to detect if type has a degree property.
Definition: math.hpp:1881
auto operator()(U &a, const U &b, const U &c) const -> decltype(a=b - c)
Call operator.
Definition: math.hpp:2610
static const bool value
Value of the type trait.
Definition: math.hpp:2085
static const bool value
Value of the type trait.
Definition: math.hpp:2413
auto operator()(const T &x) const -> decltype(impl(x))
Call operator.
Definition: math.hpp:1001
static const bool value
Value of the type trait.
Definition: math.hpp:2481
static const bool value
Value of the type trait.
Definition: math.hpp:2052
Default functor for the implementation of piranha::math::is_zero().
Definition: math.hpp:69
Default functor for the implementation of piranha::math::ldegree().
Definition: math.hpp:1377
boost::container::flat_map< std::string, T > symbol_fmap
Flat map of symbols.
Type trait to detect the presence of the piranha::math::is_unitary() function.
Definition: math.hpp:2232
Type trait to detect if a key type has a trigonometric degree property.
Definition: math.hpp:2105
ret_type< T1, U1 > operator()(const T1 &x, const symbol_fmap< U1 > &) const
Call operator.
Definition: math.hpp:787
Type trait to detect the presence of the piranha::math::t_subs function.
Definition: math.hpp:2280
detail::math_cos_type< T > cos(const T &x)
Cosine.
Definition: math.hpp:530
Exceptions.
Default functor for the implementation of piranha::math::sin().
Definition: math.hpp:541
Type trait to detect the presence of the piranha::math::subs function.
Definition: math.hpp:2254
void negate(T &x)
In-place negation.
Definition: math.hpp:329
Type trait to detect the presence of the piranha::math::negate function.
Definition: math.hpp:1070
STL namespace.
Default functor for the implementation of piranha::math::cos().
Definition: math.hpp:449
Default functor for the implementation of piranha::math::evaluate().
Definition: math.hpp:753
auto degree(const T &x) -> decltype(degree_impl< T >()(x))
Total degree.
Definition: math.hpp:1341
static const bool value
Value of the type trait.
Definition: math.hpp:1037
auto operator()(T1 &out, const T1 &a, const T1 &b) const -> decltype(out=math::gcd(a, b))
Call operator.
Definition: math.hpp:2904
Type trait to detect the presence of the piranha::math::is_zero() function.
Definition: math.hpp:1049
bool 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)
Check if a transformation is canonical.
Definition: math.hpp:1283
static const bool value
Value of the type trait.
Definition: math.hpp:1192
Type trait to detect if type has a trigonometric low degree property.
Definition: math.hpp:1956
static const bool value
Value of the type trait.
Definition: math.hpp:2242
static const bool value
Value of the type trait.
Definition: math.hpp:2340
static const bool value
Value of the type trait.
Definition: math.hpp:2186
detail::math_t_subs_type< T, U, V > t_subs(const T &x, const std::string &name, const U &c, const V &s)
Trigonometric substitution.
Definition: math.hpp:936
Default functor for the implementation of piranha::math::t_lorder().
Definition: math.hpp:1599
Detect piranha::math::pbracket().
Definition: math.hpp:1181
bool operator()(const U &x) const
Call operator.
Definition: math.hpp:202
static const bool value
Value of the type trait.
Definition: math.hpp:1942
Type trait to detect if type has a trigonometric order property.
Definition: math.hpp:1981
void operator()(U &x) const
Generic call operator.
Definition: math.hpp:282
math_evaluate_t< T, U > evaluate(const T &x, const symbol_fmap< U > &dict)
Evaluation.
Definition: math.hpp:831
Implementation of the piranha::math::truncate_degree() functor.
Definition: math.hpp:1652
Default functor for the implementation of piranha::math::add3().
Definition: math.hpp:2520
static const bool value
Value of the type trait.
Definition: math.hpp:1917
Type trait to detect integrable keys.
Definition: math.hpp:1858
#define piranha_throw(exception_type,...)
Exception-throwing macro.
Definition: exceptions.hpp:118
Type trait for differentiable types.
Definition: math.hpp:1764
boost::container::flat_set< std::string > symbol_fset
Flat set of symbols.
Default functor for the implementation of piranha::math::t_ldegree().
Definition: math.hpp:1485
bool operator()(const U &x) const
Call operator.
Definition: math.hpp:94
Default functor for the implementation of piranha::math::sub3().
Definition: math.hpp:2592
Default functor for the implementation of piranha::math::gcd3().
Definition: math.hpp:2888
Type trait to detect if types can be used in piranha::math::truncate_degree().
Definition: math.hpp:1739
Default functor for the implementation of piranha::math::partial().
Definition: math.hpp:633
Type trait to detect the presence of the substitution method in keys.
Definition: math.hpp:2360
auto operator()(U &a, const U &b, const U &c) const -> decltype(a=b+c)
Call operator.
Definition: math.hpp:2538
auto div3(T &a, const T &b, const T &c) -> decltype(div3_impl< T >()(a, b, c))
Ternary division.
Definition: math.hpp:2798
static const bool value
Value of the type trait.
Definition: math.hpp:1967
static const bool value
Value of the type trait.
Definition: math.hpp:3067
static const bool value
Value of the type trait.
Definition: math.hpp:2370
static const bool value
Value of the type trait.
Definition: math.hpp:2506
auto t_degree(const T &x) -> decltype(t_degree_impl< T >
Total trigonometric degree.
Definition: math.hpp:1450
auto abs(const T &x) -> decltype(abs_impl< T >()(x))
Absolute value.
Definition: math.hpp:1018
Default functor for the implementation of piranha::math::gcd().
Definition: math.hpp:2837
Type trait to detect if a key type has a trigonometric order property.
Definition: math.hpp:2173
bool is_zero(const T &x)
Zero test.
Definition: math.hpp:133
Default functor for the implementation of piranha::math::t_subs().
Definition: math.hpp:892
Type trait to detect if a key type has a trigonometric low order property.
Definition: math.hpp:2207
boost::container::flat_map< symbol_idx, T > symbol_idx_fmap
Flat map of symbol indices.
Type trait to detect if type has a trigonometric degree property.
Definition: math.hpp:1931
static const bool value
Value of the type trait.
Definition: math.hpp:2220
Root piranha namespace.
Definition: array_key.hpp:52
static const bool value
Value of the type trait.
Definition: math.hpp:2294
T truncate_degree(const T &x, const U &max_degree)
Truncation based on the total degree.
Definition: math.hpp:1699
Type trait to detect the availability of piranha::math::multiply_accumulate().
Definition: math.hpp:2383
static const bool value
Value of the type trait.
Definition: math.hpp:2987
Type trait to detect the availability of piranha::math::evaluate().
Definition: math.hpp:2405
Type traits.
auto t_ldegree(const T &x) -> decltype(t_ldegree_impl< T >
Total trigonometric low degree.
Definition: math.hpp:1506
Type trait to detect the presence of the trigonometric substitution method in keys.
Definition: math.hpp:2328
static const bool value
Value of the type trait.
Definition: math.hpp:3027
Type trait for integrable types.
Definition: math.hpp:1830
void multiply_accumulate(T &x, const T &y, const T &z)
Multiply-accumulate.
Definition: math.hpp:438
Detect piranha::math::gcd().
Definition: math.hpp:3039
static const bool value
Value of the type trait.
Definition: math.hpp:1992
static const bool value
Value of the type trait.
Definition: math.hpp:2967
auto ldegree(const T &x) -> decltype(ldegree_impl< T >()(x))
Total low degree.
Definition: math.hpp:1393
static const bool value
Value of the type trait.
Definition: math.hpp:2441
static const bool value
Value of the type trait.
Definition: math.hpp:1892
Type trait to detect if a key type has a low degree property.
Definition: math.hpp:2072
Default functor for the implementation of piranha::math::degree().
Definition: math.hpp:1325
Type trait to detect piranha::math::cos().
Definition: math.hpp:2473
auto mul3(T &a, const T &b, const T &c) -> decltype(mul3_impl< T >()(a, b, c))
Ternary multiplication.
Definition: math.hpp:2726
Type trait to detect if type has a low degree property.
Definition: math.hpp:1906
static const bool value
Value of the type trait.
Definition: math.hpp:1057
Detect piranha::math::add3().
Definition: math.hpp:2959
Type trait to detect if type has a trigonometric low order property.
Definition: math.hpp:2006
Type trait to detect differentiable keys.
Definition: math.hpp:1807
auto t_order(const T &x) -> decltype(t_order_impl< T >
Total trigonometric order.
Definition: math.hpp:1564
auto operator()(U &a, const U &b, const U &c) const -> decltype(a=b/c)
Call operator.
Definition: math.hpp:2754
Type trait to detect if a key type has a degree property.
Definition: math.hpp:2039
void operator()(U &x, const U &y, const U &z) const
Call operator.
Definition: math.hpp:366
static const bool value
Value of the type trait.
Definition: math.hpp:2392
auto sub3(T &a, const T &b, const T &c) -> decltype(sub3_impl< T >()(a, b, c))
Ternary subtraction.
Definition: math.hpp:2654
Detect piranha::math::mul3().
Definition: math.hpp:2999
static const bool value
Value of the type trait.
Definition: math.hpp:1078
static const bool value
Value of the type trait.
Definition: math.hpp:3007
Type trait to detect if a key type has a trigonometric low degree property.
Definition: math.hpp:2139
Detect piranha::math::transformation_is_canonical().
Definition: math.hpp:2493
Default functor for the implementation of piranha::math::t_order().
Definition: math.hpp:1541
Default functor for the implementation of piranha::math::abs().
Definition: math.hpp:947
static const bool value
Value of the type trait.
Definition: math.hpp:1751
Default functor for the implementation of piranha::math::subs().
Definition: math.hpp:842
static const bool value
Value of the type trait.
Definition: math.hpp:2118
Type trait to detect the presence of the piranha::math::abs() function.
Definition: math.hpp:1029
detail::math_subs_type< T, U > subs(const T &x, const symbol_fmap< U > &dict)
Substitution.
Definition: math.hpp:881
auto gcd3(T &out, const T &a, const T &b) -> decltype(gcd3_impl< T >()(out, a, b))
Ternary GCD.
Definition: math.hpp:2947
static const bool value
Value of the type trait.
Definition: math.hpp:2461
decltype(std::declval< const T & >()+std::declval< const U & >()) p_type
The promoted type of T and U.
Definition: math.hpp:2847
static const bool value
Value of the type trait.
Definition: math.hpp:1869
detail::pbracket_type< T > pbracket(const T &f, const T &g, const std::vector< std::string > &p_list, const std::vector< std::string > &q_list)
Poisson bracket.
Definition: math.hpp:1152
detail::math_sin_type< T > sin(const T &x)
Sine.
Definition: math.hpp:622
static const bool value
Value of the type trait.
Definition: math.hpp:3047
Default functor for the implementation of piranha::math::is_unitary().
Definition: math.hpp:179
Type trait to detect evaluable keys.
Definition: math.hpp:2431
bool is_unitary(const T &x)
Unitary test.
Definition: math.hpp:242