piranha  0.10
monomial.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_MONOMIAL_HPP
30 #define PIRANHA_MONOMIAL_HPP
31 
32 #include <algorithm>
33 #include <array>
34 #include <cstddef>
35 #include <cstdint>
36 #include <functional>
37 #include <initializer_list>
38 #include <iostream>
39 #include <iterator>
40 #include <limits>
41 #include <sstream>
42 #include <stdexcept>
43 #include <string>
44 #include <tuple>
45 #include <type_traits>
46 #include <utility>
47 #include <vector>
48 
49 #include <piranha/array_key.hpp>
50 #include <piranha/config.hpp>
51 #include <piranha/detail/cf_mult_impl.hpp>
52 #include <piranha/detail/monomial_common.hpp>
53 #include <piranha/detail/prepare_for_print.hpp>
54 #include <piranha/detail/safe_integral_adder.hpp>
55 #include <piranha/exceptions.hpp>
56 #include <piranha/forwarding.hpp>
57 #include <piranha/is_cf.hpp>
58 #include <piranha/is_key.hpp>
59 #include <piranha/math.hpp>
60 #include <piranha/mp_integer.hpp>
61 #include <piranha/mp_rational.hpp>
62 #include <piranha/pow.hpp>
63 #include <piranha/s11n.hpp>
64 #include <piranha/safe_cast.hpp>
65 #include <piranha/symbol_utils.hpp>
66 #include <piranha/term.hpp>
67 #include <piranha/type_traits.hpp>
68 
69 namespace piranha
70 {
71 
73 
99 template <typename T, typename S = std::integral_constant<std::size_t, 0u>>
100 class monomial : public array_key<T, monomial<T, S>, S>
101 {
102  PIRANHA_TT_CHECK(has_is_unitary, T);
103  PIRANHA_TT_CHECK(is_ostreamable, T);
104  PIRANHA_TT_CHECK(has_negate, T);
105  PIRANHA_TT_CHECK(std::is_copy_assignable, T);
106  using base = array_key<T, monomial<T, S>, S>;
107  // Less-than operator.
108  template <typename U>
109  using comparison_enabler = typename std::enable_if<is_less_than_comparable<U>::value, int>::type;
110 
111 public:
113  static const std::size_t multiply_arity = 1u;
115  monomial() = default;
117  monomial(const monomial &) = default;
119  monomial(monomial &&) = default;
120 
121 private:
122  // Enabler for ctor from init list.
123  template <typename U>
124  using init_list_enabler = enable_if_t<std::is_constructible<base, std::initializer_list<U>>::value, int>;
125 
126 public:
128 
136  template <typename U, init_list_enabler<U> = 0>
137  explicit monomial(std::initializer_list<U> list) : base(list)
138  {
139  }
140 
141 private:
142  // Enabler for ctor from range.
143  template <typename Iterator>
144  using it_ctor_enabler
145  = enable_if_t<conjunction<is_input_iterator<Iterator>,
146  has_safe_cast<T, typename std::iterator_traits<Iterator>::value_type>>::value,
147  int>;
148 
149 public:
151 
166  template <typename Iterator, it_ctor_enabler<Iterator> = 0>
167  explicit monomial(Iterator begin, Iterator end)
168  {
169  for (; begin != end; ++begin) {
170  this->push_back(safe_cast<T>(*begin));
171  }
172  }
174 
193  template <typename Iterator, it_ctor_enabler<Iterator> = 0>
194  explicit monomial(Iterator begin, Iterator end, const symbol_fset &s) : monomial(begin, end)
195  {
196  if (unlikely(this->size() != s.size())) {
197  piranha_throw(std::invalid_argument, "the monomial constructor from range and symbol set "
198  "yielded an invalid monomial: the final size is "
199  + std::to_string(this->size())
200  + ", while the size of the symbol set is "
201  + std::to_string(s.size()));
202  }
203  }
207  {
208  PIRANHA_TT_CHECK(is_key, monomial);
209  }
211 
218  monomial &operator=(const monomial &other) = default;
220 
225  monomial &operator=(monomial &&other) = default;
227 
235  bool is_compatible(const symbol_fset &args) const noexcept
236  {
237  return this->size() == args.size();
238  }
240 
245  bool is_zero(const symbol_fset &) const noexcept
246  {
247  return false;
248  }
250 
260  bool is_unitary(const symbol_fset &args) const
261  {
262  const auto sbe = this->size_begin_end();
263  if (unlikely(args.size() != std::get<0>(sbe))) {
264  piranha_throw(std::invalid_argument,
265  "invalid sizes in the invocation of is_unitary() for a monomial: the monomial has a size of "
266  + std::to_string(std::get<0>(sbe)) + ", while the reference symbol set has a size of "
267  + std::to_string(args.size()));
268  }
269  return std::all_of(std::get<1>(sbe), std::get<2>(sbe), [](const T &element) { return math::is_zero(element); });
270  }
271 
272 private:
273  // Machinery to determine the degree type.
274  template <typename U>
275  using degree_type = enable_if_t<conjunction<std::is_constructible<add_t<U, U>, int>,
277  add_t<U, U>>;
278  // Helpers to add exponents in the degree computation.
279  template <typename U, enable_if_t<std::is_integral<U>::value, int> = 0>
280  static void expo_add(degree_type<U> &retval, const U &n)
281  {
282  detail::safe_integral_adder(retval, static_cast<degree_type<U>>(n));
283  }
284  template <typename U, enable_if_t<!std::is_integral<U>::value, int> = 0>
285  static void expo_add(degree_type<U> &retval, const U &x)
286  {
287  retval += x;
288  }
289 
290 public:
292 
312  template <typename U = T>
313  degree_type<U> degree(const symbol_fset &args) const
314  {
315  auto sbe = this->size_begin_end();
316  if (unlikely(args.size() != std::get<0>(sbe))) {
318  std::invalid_argument,
319  "invalid symbol set for the computation of the degree of a monomial: the size of the symbol set ("
320  + std::to_string(args.size()) + ") differs from the size of the monomial ("
321  + std::to_string(std::get<0>(sbe)) + ")");
322  }
323  degree_type<U> retval(0);
324  for (; std::get<1>(sbe) != std::get<2>(sbe); ++std::get<1>(sbe)) {
325  expo_add(retval, *std::get<1>(sbe));
326  }
327  return retval;
328  }
330 
356  template <typename U = T>
357  degree_type<U> degree(const symbol_idx_fset &p, const symbol_fset &args) const
358  {
359  auto sbe = this->size_begin_end();
360  if (unlikely(args.size() != std::get<0>(sbe))) {
361  piranha_throw(std::invalid_argument, "invalid symbol set for the computation of the partial degree of a "
362  "monomial: the size of the symbol set ("
363  + std::to_string(args.size())
364  + ") differs from the size of the monomial ("
365  + std::to_string(std::get<0>(sbe)) + ")");
366  }
367  if (unlikely(p.size() && *p.rbegin() >= args.size())) {
368  piranha_throw(std::invalid_argument, "the largest value in the positions set for the computation of the "
369  "partial degree of a monomial is "
370  + std::to_string(*p.rbegin())
371  + ", but the monomial has a size of only "
372  + std::to_string(args.size()));
373  }
374  degree_type<U> retval(0);
375  for (const auto &i : p) {
376  expo_add(retval, std::get<1>(sbe)[i]);
377  }
378  return retval;
379  }
381 
388  template <typename U = T>
389  degree_type<U> ldegree(const symbol_fset &args) const
390  {
391  return degree(args);
392  }
394 
402  template <typename U = T>
403  degree_type<U> ldegree(const symbol_idx_fset &p, const symbol_fset &args) const
404  {
405  return degree(p, args);
406  }
408 
421  std::pair<bool, symbol_idx> is_linear(const symbol_fset &args) const
422  {
423  auto sbe = this->size_begin_end();
424  if (unlikely(args.size() != std::get<0>(sbe))) {
425  piranha_throw(std::invalid_argument, "invalid symbol set for the identification of a linear "
426  "monomial: the size of the symbol set ("
427  + std::to_string(args.size())
428  + ") differs from the size of the monomial ("
429  + std::to_string(std::get<0>(sbe)) + ")");
430  }
431  using size_type = typename base::size_type;
432  size_type n_linear = 0u, candidate = 0u;
433  for (size_type i = 0u; i < std::get<0>(sbe); ++i, ++std::get<1>(sbe)) {
434  // NOTE: is_zero()'s availability is guaranteed by array_key's reqs,
435  // is_unitary() is required by the monomial reqs.
436  if (math::is_zero(*std::get<1>(sbe))) {
437  continue;
438  }
439  if (!math::is_unitary(*std::get<1>(sbe))) {
440  return std::make_pair(false, symbol_idx{0});
441  }
442  candidate = i;
443  ++n_linear;
444  }
445  piranha_assert(std::get<1>(sbe) == std::get<2>(sbe));
446  if (n_linear != 1u) {
447  return std::make_pair(false, symbol_idx{0});
448  }
449  return std::make_pair(true, symbol_idx{candidate});
450  }
451 
452 private:
453  // Enabler for pow.
454  template <typename U>
455  using pow_enabler = monomial_pow_enabler<T, U>;
456 
457 public:
459 
488  template <typename U, pow_enabler<U> = 0>
489  monomial pow(const U &x, const symbol_fset &args) const
490  {
491  auto sbe = this->size_begin_end();
492  if (unlikely(args.size() != std::get<0>(sbe))) {
493  piranha_throw(std::invalid_argument, "invalid symbol set for the exponentiation of a "
494  "monomial: the size of the symbol set ("
495  + std::to_string(args.size())
496  + ") differs from the size of the monomial ("
497  + std::to_string(std::get<0>(sbe)) + ")");
498  }
499  // Init with zeroes.
500  monomial retval(args);
501  for (decltype(retval.size()) i = 0u; i < std::get<0>(sbe); ++i, ++std::get<1>(sbe)) {
502  monomial_pow_mult_exp(retval[i], *std::get<1>(sbe), x, monomial_pow_dispatcher<T, U>{});
503  }
504  return retval;
505  }
506 
507 private:
508  // Partial utils.
509  // Detect decrement operator.
510  template <typename U>
511  using dec_t = decltype(--std::declval<U &>());
512  // Main dispatcher.
513  template <typename U>
514  using monomial_partial_dispatcher = disjunction_idx<
515  // Case 0: U is integral.
516  std::is_integral<U>,
517  // Case 1: U supports the decrement operator.
518  is_detected<dec_t, U>>;
519  // In-place decrement by one, checked for integral types.
520  template <typename U>
521  static void ip_dec(U &x, const std::integral_constant<std::size_t, 0u> &)
522  {
523  // NOTE: maybe replace with the safe integral subber, eventually.
524  if (unlikely(x == std::numeric_limits<U>::min())) {
525  piranha_throw(std::overflow_error, "negative overflow error in the calculation of the "
526  "partial derivative of a monomial");
527  }
528  x = static_cast<U>(x - U(1));
529  }
530  template <typename U>
531  static void ip_dec(U &x, const std::integral_constant<std::size_t, 1u> &)
532  {
533  --x;
534  }
535  // The enabler.
536  template <typename U>
537  using partial_enabler = enable_if_t<(monomial_partial_dispatcher<U>::value < 2u), int>;
538 
539 public:
541 
565  template <typename U = T, partial_enabler<U> = 0>
566  std::pair<T, monomial> partial(const symbol_idx &p, const symbol_fset &args) const
567  {
568  auto sbe = this->size_begin_end();
569  if (unlikely(args.size() != std::get<0>(sbe))) {
570  piranha_throw(std::invalid_argument,
571  "invalid symbol set for the computation of the partial derivative of a "
572  "monomial: the size of the symbol set ("
573  + std::to_string(args.size()) + ") differs from the size of the monomial ("
574  + std::to_string(std::get<0>(sbe)) + ")");
575  }
576  if (p >= std::get<0>(sbe) || math::is_zero(std::get<1>(sbe)[p])) {
577  // Derivative wrt a variable not in the monomial: position is outside the bounds, or it refers to a
578  // variable with zero exponent.
579  return std::make_pair(T(0), monomial{args});
580  }
581  // Copy the original exponent.
582  T expo(std::get<1>(sbe)[p]);
583  // Copy the original monomial.
584  monomial m(*this);
585  // Decrement the affected exponent in m.
586  ip_dec(m[static_cast<typename base::size_type>(p)], monomial_partial_dispatcher<U>{});
587  // Return the result.
588  return std::make_pair(std::move(expo), std::move(m));
589  }
590 
591 private:
592  // Integrate utils.
593  // Detect increment operator.
594  template <typename U>
595  using inc_t = decltype(++std::declval<U &>());
596  // Main dispatcher.
597  template <typename U>
598  using monomial_int_dispatcher = disjunction_idx<
599  // Case 0: U is integral.
600  std::is_integral<U>,
601  // Case 1: U supports the increment operator.
602  is_detected<inc_t, U>>;
603  // In-place increment by one, checked for integral types.
604  template <typename U>
605  static void ip_inc(U &x, const std::integral_constant<std::size_t, 0u> &)
606  {
607  // NOTE: maybe replace with the safe integral adder, eventually.
608  if (unlikely(x == std::numeric_limits<U>::max())) {
609  piranha_throw(std::overflow_error, "positive overflow error in the calculation of the "
610  "antiderivative of a monomial");
611  }
612  x = static_cast<U>(x + U(1));
613  }
614  template <typename U>
615  static void ip_inc(U &x, const std::integral_constant<std::size_t, 1u> &)
616  {
617  ++x;
618  }
619  // The enabler.
620  template <typename U>
621  using integrate_enabler = enable_if_t<(monomial_int_dispatcher<U>::value < 2u), int>;
622 
623 public:
625 
653  template <typename U = T, integrate_enabler<U> = 0>
654  std::pair<T, monomial> integrate(const std::string &s, const symbol_fset &args) const
655  {
656  auto sbe = this->size_begin_end();
657  if (unlikely(args.size() != std::get<0>(sbe))) {
658  piranha_throw(std::invalid_argument, "invalid symbol set for the computation of the antiderivative of a "
659  "monomial: the size of the symbol set ("
660  + std::to_string(args.size())
661  + ") differs from the size of the monomial ("
662  + std::to_string(std::get<0>(sbe)) + ")");
663  }
664  monomial retval;
665  T expo(0);
666  const PIRANHA_MAYBE_TLS T one(1);
667  auto it_args = args.begin();
668  for (decltype(retval.size()) i = 0; std::get<1>(sbe) != std::get<2>(sbe); ++i, ++std::get<1>(sbe), ++it_args) {
669  const auto &cur_sym = *it_args;
670  if (math::is_zero(expo) && s < cur_sym) {
671  // If we went past the position of s in args and still we
672  // have not performed the integration, it means that we need to add
673  // a new exponent.
674  retval.push_back(one);
675  expo = one;
676  }
677  retval.push_back(*std::get<1>(sbe));
678  if (cur_sym == s) {
679  // NOTE: here using i is safe: if retval gained an extra exponent in the condition above,
680  // we are never going to land here as cur_sym is at this point never going to be s.
681  auto &ref = retval[i];
682  // Do the addition and check for zero later, to detect -1 expo.
683  ip_inc(ref, monomial_int_dispatcher<U>{});
684  if (unlikely(math::is_zero(ref))) {
685  piranha_throw(std::invalid_argument,
686  "unable to perform monomial integration: a negative "
687  "unitary exponent was encountered in correspondence of the variable '"
688  + cur_sym + "'");
689  }
690  expo = ref;
691  }
692  }
693  // If expo is still zero, it means we need to add a new exponent at the end.
694  if (math::is_zero(expo)) {
695  retval.push_back(one);
696  expo = one;
697  }
698  return std::make_pair(std::move(expo), std::move(retval));
699  }
700 
701 private:
702  // Let's hard code the custom behaviour for rational exponents for the moment.
703  // We can offer a customization point in the future.
704  template <typename U, enable_if_t<is_mp_rational<U>::value, int> = 0>
705  static void print_exponent(std::ostream &os, const U &e)
706  {
707  if (math::is_unitary(e.den())) {
708  os << e;
709  } else {
710  os << '(' << e << ')';
711  }
712  }
713  template <typename U, enable_if_t<!is_mp_rational<U>::value, int> = 0>
714  static void print_exponent(std::ostream &os, const U &e)
715  {
716  os << detail::prepare_for_print(e);
717  }
718 
719 public:
721 
732  void print(std::ostream &os, const symbol_fset &args) const
733  {
734  auto sbe = this->size_begin_end();
735  if (unlikely(args.size() != std::get<0>(sbe))) {
736  piranha_throw(std::invalid_argument,
737  "cannot print monomial: the size of the symbol set (" + std::to_string(args.size())
738  + ") differs from the size of the monomial (" + std::to_string(std::get<0>(sbe)) + ")");
739  }
740  bool empty_output = true;
741  auto it_args = args.begin();
742  for (decltype(args.size()) i = 0; std::get<1>(sbe) != std::get<2>(sbe); ++i, ++std::get<1>(sbe), ++it_args) {
743  if (!math::is_zero(*std::get<1>(sbe))) {
744  // If we are going to print a symbol, and something has been printed before,
745  // then we are going to place the multiplication sign.
746  if (!empty_output) {
747  os << '*';
748  }
749  os << *it_args;
750  empty_output = false;
751  if (!math::is_unitary(*std::get<1>(sbe))) {
752  os << "**";
753  print_exponent(os, *std::get<1>(sbe));
754  }
755  }
756  }
757  }
759 
771  void print_tex(std::ostream &os, const symbol_fset &args) const
772  {
773  auto sbe = this->size_begin_end();
774  if (unlikely(args.size() != std::get<0>(sbe))) {
775  piranha_throw(std::invalid_argument, "cannot print monomial in TeX mode: the size of the symbol set ("
776  + std::to_string(args.size())
777  + ") differs from the size of the monomial ("
778  + std::to_string(std::get<0>(sbe)) + ")");
779  }
780  std::ostringstream oss_num, oss_den, *cur_oss;
781  const PIRANHA_MAYBE_TLS T zero(0);
782  T cur_value;
783  auto it_args = args.begin();
784  for (decltype(args.size()) i = 0; std::get<1>(sbe) != std::get<2>(sbe); ++i, ++std::get<1>(sbe), ++it_args) {
785  cur_value = *std::get<1>(sbe);
786  if (!math::is_zero(cur_value)) {
787  // NOTE: use this weird form for the test because the presence of operator<()
788  // is already guaranteed and thus we don't need additional requirements on T.
789  // Maybe in the future we want to do it with a math::sign() function.
790  if (zero < cur_value) {
791  cur_oss = std::addressof(oss_num);
792  } else {
793  math::negate(cur_value);
794  cur_oss = std::addressof(oss_den);
795  }
796  *cur_oss << "{" << *it_args << "}";
797  if (!math::is_unitary(cur_value)) {
798  *cur_oss << "^{" << detail::prepare_for_print(cur_value) << "}";
799  }
800  }
801  }
802  const std::string num_str = oss_num.str(), den_str = oss_den.str();
803  if (!num_str.empty() && !den_str.empty()) {
804  os << "\\frac{" << num_str << "}{" << den_str << "}";
805  } else if (!num_str.empty() && den_str.empty()) {
806  os << num_str;
807  } else if (num_str.empty() && !den_str.empty()) {
808  os << "\\frac{1}{" << den_str << "}";
809  }
810  }
811 
812 private:
813  // Eval type definition.
814  template <typename U>
815  using eval_type
816  = enable_if_t<conjunction<is_multipliable_in_place<pow_t<U, T>>, std::is_constructible<pow_t<U, T>, int>,
817  is_returnable<pow_t<U, T>>>::value,
818  pow_t<U, T>>;
819 
820 public:
822 
845  template <typename U>
846  eval_type<U> evaluate(const std::vector<U> &values, const symbol_fset &args) const
847  {
848  auto sbe = this->size_begin_end();
849  if (unlikely(args.size() != std::get<0>(sbe))) {
850  piranha_throw(std::invalid_argument,
851  "cannot evaluate monomial: the size of the symbol set (" + std::to_string(args.size())
852  + ") differs from the size of the monomial (" + std::to_string(std::get<0>(sbe)) + ")");
853  }
854  if (unlikely(values.size() != std::get<0>(sbe))) {
855  piranha_throw(std::invalid_argument,
856  "cannot evaluate monomial: the size of the vector of values (" + std::to_string(values.size())
857  + ") differs from the size of the monomial (" + std::to_string(std::get<0>(sbe)) + ")");
858  }
859  if (args.size()) {
860  eval_type<U> retval(math::pow(values[0], *std::get<1>(sbe)++));
861  for (decltype(values.size()) i = 1; i < values.size(); ++i, ++std::get<1>(sbe)) {
862  // NOTE: here maybe we could use mul3() and pow3() (to be implemented?).
863  // NOTE: math::pow() for C++ integrals produces an integer result, no need
864  // to worry about overflows.
865  retval *= math::pow(values[i], *std::get<1>(sbe));
866  }
867  piranha_assert(std::get<1>(sbe) == std::get<2>(sbe));
868  return retval;
869  }
870  return eval_type<U>(1);
871  }
872 
873 private:
874  // Subs type is the same as eval type.
875  template <typename U>
876  using subs_type = eval_type<U>;
877 
878 public:
880 
914  template <typename U>
915  std::vector<std::pair<subs_type<U>, monomial>> subs(const symbol_idx_fmap<U> &smap, const symbol_fset &args) const
916  {
917  auto sbe = this->size_begin_end();
918  if (unlikely(args.size() != std::get<0>(sbe))) {
919  piranha_throw(std::invalid_argument,
920  "cannot perform substitution in a monomial: the size of the symbol set ("
921  + std::to_string(args.size()) + ") differs from the size of the monomial ("
922  + std::to_string(std::get<0>(sbe)) + ")");
923  }
924  if (unlikely(smap.size() && smap.rbegin()->first >= std::get<0>(sbe))) {
925  // The last element of the substitution map must be a valid index.
926  piranha_throw(std::invalid_argument,
927  "invalid argument(s) for substitution in a monomial: the last index of the substitution map ("
928  + std::to_string(smap.rbegin()->first) + ") must be smaller than the monomial's size ("
929  + std::to_string(std::get<0>(sbe)) + ")");
930  }
931  std::vector<std::pair<subs_type<U>, monomial>> retval;
932  if (smap.size()) {
933  // The substitution map contains something, proceed to the substitution.
934  // Cache-init the zero constant.
935  const PIRANHA_MAYBE_TLS T zero(0);
936  // Init the subs return value from the exponentiation of the first value in the map.
937  auto it = smap.begin();
938  auto ret(math::pow(it->second, std::get<1>(sbe)[it->first]));
939  // Init the monomial return value with a copy of this.
940  auto mon_ret(*this);
941  // Zero out the corresponding exponent.
942  mon_ret[static_cast<decltype(mon_ret.size())>(it->first)] = zero;
943  // NOTE: move to the next element in the init statement of the for loop.
944  for (++it; it != smap.end(); ++it) {
945  ret *= math::pow(it->second, std::get<1>(sbe)[it->first]);
946  mon_ret[static_cast<decltype(mon_ret.size())>(it->first)] = zero;
947  }
948  // NOTE: the is_returnable requirement ensures we can emplace back a pair
949  // containing the subs type.
950  retval.emplace_back(std::move(ret), std::move(mon_ret));
951  } else {
952  // Otherwise, the substitution yields 1 and the monomial is the original one.
953  retval.emplace_back(subs_type<U>(1), *this);
954  }
955  return retval;
956  }
957 
958 private:
959  // ipow subs utilities.
960  // Dispatcher for the assignment of an exponent to an integer.
961  template <typename U>
962  using ipow_subs_d_assign_dispatcher = disjunction_idx<
963  // Case 0: U is integer or a C++ integral.
964  disjunction<std::is_integral<U>, std::is_same<integer, U>>,
965  // Case 1: U supports safe cast to integer.
966  has_safe_cast<integer, U>>;
967  template <typename U>
968  static void ipow_subs_d_assign(integer &d, const U &expo, const std::integral_constant<std::size_t, 0> &)
969  {
970  d = expo;
971  }
972  template <typename U>
973  static void ipow_subs_d_assign(integer &d, const U &expo, const std::integral_constant<std::size_t, 1> &)
974  {
975  d = safe_cast<integer>(expo);
976  }
977  // Dispatcher for the assignment of an integer to an exponent.
978  template <typename U>
979  using ipow_subs_expo_assign_dispatcher = disjunction_idx<
980  // Case 0: U is integer.
981  std::is_same<integer, U>,
982  // Case 1: integer supports safe cast to U.
983  has_safe_cast<U, integer>>;
984  template <typename U>
985  static void ipow_subs_expo_assign(U &expo, const integer &r, const std::integral_constant<std::size_t, 0> &)
986  {
987  expo = r;
988  }
989  template <typename U>
990  static void ipow_subs_expo_assign(U &expo, const integer &r, const std::integral_constant<std::size_t, 1> &)
991  {
992  expo = safe_cast<U>(r);
993  }
994  // Definition of the return type.
995  template <typename U>
996  using ips_type = decltype(math::pow(std::declval<const U &>(), std::declval<const integer &>()));
997  // Final enabler.
998  template <typename U>
999  using ipow_subs_type
1000  = enable_if_t<conjunction<std::is_constructible<ips_type<U>, int>, is_returnable<ips_type<U>>>::value
1001  && (ipow_subs_d_assign_dispatcher<T>::value < 2u)
1002  && (ipow_subs_expo_assign_dispatcher<T>::value < 2u),
1003  ips_type<U>>;
1004 
1005 public:
1007 
1043  template <typename U>
1044  std::vector<std::pair<ipow_subs_type<U>, monomial>> ipow_subs(const symbol_idx &p, const integer &n, const U &x,
1045  const symbol_fset &args) const
1046  {
1047  if (unlikely(this->size() != args.size())) {
1048  piranha_throw(std::invalid_argument,
1049  "cannot perform integral power substitution in a monomial: the size of the symbol set ("
1050  + std::to_string(args.size()) + ") differs from the size of the monomial ("
1051  + std::to_string(this->size()) + ")");
1052  }
1053  if (unlikely(!n.sgn())) {
1054  piranha_throw(std::invalid_argument,
1055  "invalid integral power for ipow_subs() in a monomial: the power must be nonzero");
1056  }
1057  // Initialise the return value.
1058  std::vector<std::pair<ipow_subs_type<U>, monomial>> retval;
1059  auto mon(*this);
1060  if (p < args.size()) {
1061  auto &expo = mon[static_cast<decltype(mon.size())>(p)];
1062  PIRANHA_MAYBE_TLS integer q, r, d;
1063  // Assign expo to d, possibly safely converting it.
1064  ipow_subs_d_assign(d, expo, ipow_subs_d_assign_dispatcher<T>{});
1065  // NOTE: regarding the sign of r: tdiv_qr() sets the sign of r to the sign of q.
1066  // The only two cases we are interested in here are where d and n have the same sign
1067  // (otherwise q will have negative sign and we never enter the 'if' below). With
1068  // d and n positive, everything is straightforward (r's sign will be positive).
1069  // If d and n are both negative, r will have negative sign, and it will satisfy:
1070  // q*n + r == d (with d < 0 and d < q*n)
1071  // This is the result we want: r is the number of steps towards -inf that q*n
1072  // must take to reach d.
1073  tdiv_qr(q, r, d, n);
1074  if (q.sgn() > 0) {
1075  // Assign back the remainder r to expo, possibly with a safe
1076  // conversion involved.
1077  ipow_subs_expo_assign(expo, r, ipow_subs_expo_assign_dispatcher<T>{});
1078  retval.emplace_back(math::pow(x, q), std::move(mon));
1079  return retval;
1080  }
1081  }
1082  // Otherwise, the substitution yields 1 and the monomial is the original one.
1083  retval.emplace_back(ipow_subs_type<U>(1), std::move(mon));
1084  return retval;
1085  }
1086 
1087 private:
1088  // Multiplication.
1089  template <typename Cf>
1090  using multiply_enabler = enable_if_t<conjunction<has_add3<T>, has_mul3<Cf>>::value, int>;
1091 
1092 public:
1094 
1113  // NOTE: it should be ok to use this method (and the one below) with overlapping arguments, as this is allowed
1114  // in small_vector::add().
1115  template <typename Cf, multiply_enabler<Cf> = 0>
1116  static void multiply(std::array<term<Cf, monomial>, multiply_arity> &res, const term<Cf, monomial> &t1,
1117  const term<Cf, monomial> &t2, const symbol_fset &args)
1118  {
1119  term<Cf, monomial> &t = res[0u];
1120  // NOTE: the check on the monomials' size is in vector_add().
1121  if (unlikely(t1.m_key.size() != args.size())) {
1122  piranha_throw(std::invalid_argument,
1123  "cannot multiply terms with monomial keys: the size of the symbol set ("
1124  + std::to_string(args.size()) + ") differs from the size of the first monomial ("
1125  + std::to_string(t1.m_key.size()) + ")");
1126  }
1127  // Coefficient.
1128  cf_mult_impl(t.m_cf, t1.m_cf, t2.m_cf);
1129  // Now deal with the key.
1130  t1.m_key.vector_add(t.m_key, t2.m_key);
1131  }
1133 
1143  bool operator<(const monomial &other) const
1144  {
1145  const auto sbe1 = this->size_begin_end();
1146  const auto sbe2 = other.size_begin_end();
1147  if (unlikely(std::get<0u>(sbe1) != std::get<0u>(sbe2))) {
1148  piranha_throw(std::invalid_argument,
1149  "mismatched sizes in a monomial comparison: the first monomial has a size of "
1150  + std::to_string(std::get<0u>(sbe1)) + ", the second monomial has a size of "
1151  + std::to_string(std::get<0u>(sbe2)));
1152  }
1153  return std::lexicographical_compare(std::get<1u>(sbe1), std::get<2u>(sbe1), std::get<1u>(sbe2),
1154  std::get<2u>(sbe2));
1155  }
1156 
1157 private:
1158 #if !defined(PIRANHA_DOXYGEN_INVOKED)
1159  // Make friend with the s11n functions.
1160  template <typename Archive, typename T1, typename S1>
1161  friend void
1162  boost::serialization::save(Archive &, const piranha::boost_s11n_key_wrapper<piranha::monomial<T1, S1>> &, unsigned);
1163  template <typename Archive, typename T1, typename S1>
1164  friend void boost::serialization::load(Archive &, piranha::boost_s11n_key_wrapper<piranha::monomial<T1, S1>> &,
1165  unsigned);
1166 #endif
1167 #if defined(PIRANHA_WITH_MSGPACK)
1168  // Enablers for msgpack serialization.
1169  template <typename Stream>
1170  using msgpack_pack_enabler = enable_if_t<
1171  conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, typename base::container_type>>::value, int>;
1172  template <typename U>
1173  using msgpack_convert_enabler = enable_if_t<has_msgpack_convert<typename U::container_type>::value, int>;
1174 
1175 public:
1177 
1191  template <typename Stream, msgpack_pack_enabler<Stream> = 0>
1192  void msgpack_pack(msgpack::packer<Stream> &packer, msgpack_format f, const symbol_fset &s) const
1193  {
1194  if (unlikely(this->size() != s.size())) {
1195  piranha_throw(std::invalid_argument, "incompatible symbol set in monomial serialization: the reference "
1196  "symbol set has a size of "
1197  + std::to_string(s.size())
1198  + ", while the monomial being serialized has a size of "
1199  + std::to_string(this->size()));
1200  }
1201  piranha::msgpack_pack(packer, this->m_container, f);
1202  }
1204 
1218  template <typename U = monomial, msgpack_convert_enabler<U> = 0>
1219  void msgpack_convert(const msgpack::object &o, msgpack_format f, const symbol_fset &s)
1220  {
1222  if (unlikely(this->size() != s.size())) {
1223  piranha_throw(std::invalid_argument, "incompatible symbol set in monomial serialization: the reference "
1224  "symbol set has a size of "
1225  + std::to_string(s.size())
1226  + ", while the monomial being deserialized has a size of "
1227  + std::to_string(this->size()));
1228  }
1229  }
1230 #endif
1231 };
1232 
1233 template <typename T, typename S>
1234 const std::size_t monomial<T, S>::multiply_arity;
1235 }
1236 
1237 // Implementation of the Boost s11n api.
1238 namespace boost
1239 {
1240 namespace serialization
1241 {
1242 
1243 template <typename Archive, typename T, typename S>
1244 inline void save(Archive &ar, const piranha::boost_s11n_key_wrapper<piranha::monomial<T, S>> &k, unsigned)
1245 {
1246  if (unlikely(k.key().size() != k.ss().size())) {
1247  piranha_throw(std::invalid_argument, "incompatible symbol set in monomial serialization: the reference "
1248  "symbol set has a size of "
1249  + std::to_string(k.ss().size())
1250  + ", while the monomial being serialized has a size of "
1251  + std::to_string(k.key().size()));
1252  }
1253  piranha::boost_save(ar, k.key().m_container);
1254 }
1255 
1256 template <typename Archive, typename T, typename S>
1257 inline void load(Archive &ar, piranha::boost_s11n_key_wrapper<piranha::monomial<T, S>> &k, unsigned)
1258 {
1259  piranha::boost_load(ar, k.key().m_container);
1260  if (unlikely(k.key().size() != k.ss().size())) {
1261  piranha_throw(std::invalid_argument, "incompatible symbol set in monomial serialization: the reference "
1262  "symbol set has a size of "
1263  + std::to_string(k.ss().size())
1264  + ", while the monomial being deserialized has a size of "
1265  + std::to_string(k.key().size()));
1266  }
1267 }
1268 
1269 template <typename Archive, typename T, typename S>
1270 inline void serialize(Archive &ar, piranha::boost_s11n_key_wrapper<piranha::monomial<T, S>> &k, unsigned version)
1271 {
1272  split_free(ar, k, version);
1273 }
1274 }
1275 }
1276 
1277 namespace piranha
1278 {
1279 
1280 inline namespace impl
1281 {
1282 
1283 // Enablers for the boost s11n methods.
1284 template <typename Archive, typename T, typename S>
1285 using monomial_boost_save_enabler
1286  = enable_if_t<has_boost_save<Archive, typename monomial<T, S>::container_type>::value>;
1287 
1288 template <typename Archive, typename T, typename S>
1289 using monomial_boost_load_enabler
1290  = enable_if_t<has_boost_load<Archive, typename monomial<T, S>::container_type>::value>;
1291 }
1292 
1294 
1302 template <typename Archive, typename T, typename S>
1303 struct boost_save_impl<Archive, boost_s11n_key_wrapper<monomial<T, S>>, monomial_boost_save_enabler<Archive, T, S>>
1304  : boost_save_via_boost_api<Archive, boost_s11n_key_wrapper<monomial<T, S>>> {
1305 };
1306 
1308 
1317 template <typename Archive, typename T, typename S>
1318 struct boost_load_impl<Archive, boost_s11n_key_wrapper<monomial<T, S>>, monomial_boost_load_enabler<Archive, T, S>>
1319  : boost_load_via_boost_api<Archive, boost_s11n_key_wrapper<monomial<T, S>>> {
1320 };
1321 }
1322 
1323 namespace std
1324 {
1325 
1327 
1330 template <typename T, typename S>
1331 struct hash<piranha::monomial<T, S>> : public hash<piranha::array_key<T, piranha::monomial<T, S>, S>> {
1332 };
1333 }
1334 
1335 #endif
math_pow_t< T, U > pow(const T &x, const U &y)
Exponentiation.
Definition: pow.hpp:126
bool is_unitary(const symbol_fset &args) const
Check if the monomial is unitary.
Definition: monomial.hpp:260
void msgpack_convert(const msgpack::object &o, msgpack_format f, const symbol_fset &s)
Deserialize from msgpack object.
Definition: monomial.hpp:1219
Cf m_cf
Coefficient member.
Definition: term.hpp:189
void print(std::ostream &os, const symbol_fset &args) const
Print.
Definition: monomial.hpp:732
void boost_load(Archive &ar, T &x)
Load from Boost archive.
Definition: s11n.hpp:469
Multiprecision integer class.
Definition: mp++.hpp:869
Default implementation of piranha::boost_load().
Definition: s11n.hpp:391
mp_integer< 1 > integer
Alias for piranha::mp_integer with 1 limb of static storage.
Definition: mp_integer.hpp:63
void msgpack_convert(T &x, const msgpack::object &o, msgpack_format f)
Convert msgpack object.
Definition: s11n.hpp:957
std::vector< std::pair< ipow_subs_type< U >, monomial > > ipow_subs(const symbol_idx &p, const integer &n, const U &x, const symbol_fset &args) const
Substitution of integral power.
Definition: monomial.hpp:1044
Key type concept check.
Definition: is_key.hpp:65
monomial()=default
Defaulted default constructor.
monomial(Iterator begin, Iterator end, const symbol_fset &s)
Constructor from range and symbol set.
Definition: monomial.hpp:194
monomial(Iterator begin, Iterator end)
Constructor from range.
Definition: monomial.hpp:167
void print_tex(std::ostream &os, const symbol_fset &args) const
Print in TeX mode.
Definition: monomial.hpp:771
monomial(std::initializer_list< U > list)
Constructor from initializer list.
Definition: monomial.hpp:137
monomial pow(const U &x, const symbol_fset &args) const
Monomial exponentiation.
Definition: monomial.hpp:489
bool is_compatible(const symbol_fset &args) const noexcept
Compatibility check.
Definition: monomial.hpp:235
Implementation of piranha::boost_load() via the Boost API.
Definition: s11n.hpp:363
container_type m_container
Internal container.
Definition: array_key.hpp:517
Type trait to detect the presence of the piranha::math::is_unitary() function.
Definition: math.hpp:2232
In-place addable type trait.
bool is_zero(const symbol_fset &) const noexcept
Zero check.
Definition: monomial.hpp:245
Exceptions.
Default implementation of piranha::boost_save().
Definition: s11n.hpp:245
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.
#define PIRANHA_FORWARDING_CTOR(Derived, Base)
Constructor-forwarding macro.
Definition: forwarding.hpp:50
degree_type< U > degree(const symbol_idx_fset &p, const symbol_fset &args) const
Partial degree.
Definition: monomial.hpp:357
Key m_key
Key member.
Definition: term.hpp:191
std::vector< std::pair< subs_type< U >, monomial > > subs(const symbol_idx_fmap< U > &smap, const symbol_fset &args) const
Substitution.
Definition: monomial.hpp:915
degree_type< U > ldegree(const symbol_idx_fset &p, const symbol_fset &args) const
Partial low degree (equivalent to the partial degree).
Definition: monomial.hpp:403
Forwarding macros.
Term class.
Definition: term.hpp:66
#define piranha_throw(exception_type,...)
Exception-throwing macro.
Definition: exceptions.hpp:118
std::pair< T, monomial > partial(const symbol_idx &p, const symbol_fset &args) const
Partial derivative.
Definition: monomial.hpp:566
boost::container::flat_set< std::string > symbol_fset
Flat set of symbols.
auto size_begin_end() const -> decltype(m_container.size_begin_end())
Get size, begin and end iterator (const version).
Definition: array_key.hpp:525
Array key.
Definition: array_key.hpp:86
Wrapper for the serialization of keys via Boost.
Definition: s11n.hpp:514
msgpack_format
Serialization format for msgpack.
Definition: s11n.hpp:673
Monomial class.
Definition: monomial.hpp:100
Detect if type can be returned from a function.
std::pair< T, monomial > integrate(const std::string &s, const symbol_fset &args) const
Integration.
Definition: monomial.hpp:654
monomial & operator=(const monomial &other)=default
Copy assignment operator.
static void multiply(std::array< term< Cf, monomial >, multiply_arity > &res, const term< Cf, monomial > &t1, const term< Cf, monomial > &t2, const symbol_fset &args)
Multiply terms with a monomial key.
Definition: monomial.hpp:1116
typename container_type::size_type size_type
Size type.
Definition: array_key.hpp:107
symbol_fset::size_type symbol_idx
Symbol index.
~monomial()
Destructor.
Definition: monomial.hpp:206
bool is_zero(const T &x)
Zero test.
Definition: math.hpp:133
bool operator<(const monomial &other) const
Comparison operator.
Definition: monomial.hpp:1143
boost::container::flat_map< symbol_idx, T > symbol_idx_fmap
Flat map of symbol indices.
Implementation of piranha::boost_save() via the Boost API.
Definition: s11n.hpp:217
Root piranha namespace.
Definition: array_key.hpp:52
eval_type< U > evaluate(const std::vector< U > &values, const symbol_fset &args) const
Evaluation.
Definition: monomial.hpp:846
void msgpack_pack(msgpack::packer< Stream > &packer, const T &x, msgpack_format f)
Pack generic object in a msgpack stream.
Definition: s11n.hpp:856
Detect the presence of piranha::msgpack_pack().
Definition: s11n.hpp:607
Type traits.
degree_type< U > ldegree(const symbol_fset &args) const
Low degree (equivalent to the degree).
Definition: monomial.hpp:389
std::pair< bool, symbol_idx > is_linear(const symbol_fset &args) const
Detect linear monomial.
Definition: monomial.hpp:421
boost::container::flat_set< symbol_idx > symbol_idx_fset
Flat set of symbol indices.
Type trait to detect piranha::safe_cast().
Definition: safe_cast.hpp:237
void boost_save(Archive &ar, const T &x)
Save to Boost archive.
Definition: s11n.hpp:328
Type trait for classes that can be output-streamed.
Detect piranha::math::mul3().
Definition: math.hpp:2999
static const std::size_t multiply_arity
Arity of the multiply() method.
Definition: monomial.hpp:113
void msgpack_pack(msgpack::packer< Stream > &packer, msgpack_format f, const symbol_fset &s) const
Serialize in msgpack format.
Definition: monomial.hpp:1192
void push_back(value_type &&x)
Move-add element at the end.
Definition: array_key.hpp:308
int sgn() const
Sign.
Definition: mp++.hpp:1611
degree_type< U > degree(const symbol_fset &args) const
Degree.
Definition: monomial.hpp:313
safe_cast_type< To, From > safe_cast(const From &x)
Safe cast.
Definition: safe_cast.hpp:219
bool is_unitary(const T &x)
Unitary test.
Definition: math.hpp:242