piranha  0.10
divisor.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_DIVISOR_HPP
30 #define PIRANHA_DIVISOR_HPP
31 
32 #include <algorithm>
33 #include <array>
34 #include <cstddef>
35 #include <functional>
36 #include <iostream>
37 #include <iterator>
38 #include <limits>
39 #include <sstream>
40 #include <stdexcept>
41 #include <tuple>
42 #include <type_traits>
43 #include <utility>
44 
45 #include <piranha/config.hpp>
46 #include <piranha/detail/cf_mult_impl.hpp>
47 #include <piranha/detail/divisor_series_fwd.hpp>
48 #include <piranha/detail/prepare_for_print.hpp>
49 #include <piranha/detail/series_fwd.hpp>
50 #include <piranha/exceptions.hpp>
51 #include <piranha/hash_set.hpp>
52 #include <piranha/is_cf.hpp>
53 #include <piranha/is_key.hpp>
54 #include <piranha/math.hpp>
55 #include <piranha/mp_integer.hpp>
56 #include <piranha/pow.hpp>
57 #include <piranha/s11n.hpp>
58 #include <piranha/safe_cast.hpp>
59 #include <piranha/small_vector.hpp>
60 #include <piranha/symbol_utils.hpp>
61 #include <piranha/term.hpp>
62 #include <piranha/type_traits.hpp>
63 
64 namespace piranha
65 {
66 
67 inline namespace impl
68 {
69 
70 // Pair vector-exponent, with the exponent made mutable. For use in the divisor class.
71 template <typename T>
72 struct divisor_p_type {
73  using v_type = small_vector<T>;
74  divisor_p_type() = default;
75  divisor_p_type(const divisor_p_type &) = default;
76  divisor_p_type(divisor_p_type &&) = default;
77  explicit divisor_p_type(const v_type &v_, const T &e_) : v(v_), e(e_) {}
78  explicit divisor_p_type(v_type &&v_, T &&e_) : v(std::move(v_)), e(std::move(e_)) {}
79  ~divisor_p_type() = default;
80  divisor_p_type &operator=(const divisor_p_type &) = default;
81  divisor_p_type &operator=(divisor_p_type &&) = default;
82  bool operator==(const divisor_p_type &other) const
83  {
84  return v == other.v;
85  }
86  // Serialization support.
87  template <class Archive>
88  void save(Archive &ar, unsigned) const
89  {
90  boost_save(ar, v);
91  boost_save(ar, e);
92  }
93  template <class Archive>
94  void load(Archive &ar, unsigned)
95  {
96  boost_load(ar, v);
97  boost_load(ar, e);
98  }
99  BOOST_SERIALIZATION_SPLIT_MEMBER()
100  // Members.
101  v_type v;
102  mutable T e;
103 };
104 }
105 
106 // Serialization methods for the divisor's pair type. Not documented because they are implementation details.
107 template <typename Archive, typename T>
108 struct boost_save_impl<Archive, divisor_p_type<T>,
109  enable_if_t<conjunction<has_boost_save<Archive, T>,
110  has_boost_save<Archive, typename divisor_p_type<T>::v_type>>::value>>
111  : boost_save_via_boost_api<Archive, divisor_p_type<T>> {
112 };
113 
114 template <typename Archive, typename T>
115 struct boost_load_impl<Archive, divisor_p_type<T>,
116  enable_if_t<conjunction<has_boost_load<Archive, T>,
117  has_boost_load<Archive, typename divisor_p_type<T>::v_type>>::value>>
118  : boost_load_via_boost_api<Archive, divisor_p_type<T>> {
119 };
120 
121 #if defined(PIRANHA_WITH_MSGPACK)
122 
123 template <typename Stream, typename T>
124 struct msgpack_pack_impl<
125  Stream, divisor_p_type<T>,
126  enable_if_t<conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, T>,
127  has_msgpack_pack<Stream, typename divisor_p_type<T>::v_type>>::value>> {
128  void operator()(msgpack::packer<Stream> &pk, const divisor_p_type<T> &p, msgpack_format f) const
129  {
130  pk.pack_array(2);
131  msgpack_pack(pk, p.v, f);
132  msgpack_pack(pk, p.e, f);
133  }
134 };
135 
136 template <typename T>
137 struct msgpack_convert_impl<
138  divisor_p_type<T>,
139  enable_if_t<conjunction<has_msgpack_convert<T>, has_msgpack_convert<typename divisor_p_type<T>::v_type>>::value>> {
140  void operator()(divisor_p_type<T> &p, const msgpack::object &o, msgpack_format f) const
141  {
142  std::array<msgpack::object, 2> tmp;
143  o.convert(tmp);
144  msgpack_convert(p.v, tmp[0], f);
145  msgpack_convert(p.e, tmp[1], f);
146  }
147 };
148 
149 #endif
150 
152 
178 // NOTE: if we ever make this completely generic on T, remember there are some hard-coded assumptions. E.g.,
179 // is_zero must be available in split().
180 // NOTE: the implementation defined range restriction is needed in order to make the gcd computations safe.
181 template <typename T>
182 class divisor
183 {
184  static_assert((std::is_signed<T>::value && std::is_integral<T>::value) || is_mp_integer<T>::value,
185  "The value type must be a signed integer or an mp_integer");
186  // Make friend with the divisor series.
187  template <typename, typename>
188  friend class divisor_series;
189 
190 public:
192  using value_type = T;
193 
194 private:
195  using p_type = divisor_p_type<value_type>;
196  using v_type = typename p_type::v_type;
197  // Hasher for the pair type.
198  struct p_type_hasher {
199  std::size_t operator()(const p_type &p) const
200  {
201  return p.v.hash();
202  }
203  };
204 
205 public:
208 
209 private:
210  // Canonical term: the first nonzero element is positive and all the gcd of all elements is 1 or -1.
211  // NOTE: this also includes the check for all zero elements, as gcd(0,0,...,0) = 0.
212  static bool term_is_canonical(const p_type &p)
213  {
214  bool first_nonzero_found = false;
215  value_type cd(0);
216  for (const auto &n : p.v) {
217  if (!first_nonzero_found && !math::is_zero(n)) {
218  if (n < 0) {
219  return false;
220  }
221  first_nonzero_found = true;
222  }
223  // NOTE: gcd(0,n) == n (or +-n, in our case) for all n, zero included.
224  math::gcd3(cd, cd, n);
225  }
226  if (cd != 1 && cd != -1) {
227  return false;
228  }
229  return true;
230  }
231  // Range check on a term - meaningful only if T is a C++ integral type.
232  template <typename U = T, typename std::enable_if<std::is_integral<U>::value, int>::type = 0>
233  static bool term_range_check(const p_type &p)
234  {
235  return std::all_of(p.v.begin(), p.v.end(), [](const value_type &x) {
236  return x >= -detail::safe_abs_sint<value_type>::value && x <= detail::safe_abs_sint<value_type>::value;
237  });
238  }
239  template <typename U = T, typename std::enable_if<!std::is_integral<U>::value, int>::type = 0>
240  static bool term_range_check(const p_type &)
241  {
242  return true;
243  }
244  bool destruction_checks() const
245  {
246  const auto it_f = m_container.end();
247  auto it = m_container.begin();
248  const typename v_type::size_type v_size = (it == it_f) ? 0u : it->v.size();
249  for (; it != it_f; ++it) {
250  // Check: the exponent must be greater than zero.
251  if (it->e <= 0) {
252  return false;
253  }
254  // Check: range.
255  if (!term_range_check(*it)) {
256  return false;
257  }
258  // Check: canonical.
259  if (!term_is_canonical(*it)) {
260  return false;
261  }
262  // Check: all vectors have the same size.
263  if (it->v.size() != v_size) {
264  return false;
265  }
266  }
267  return true;
268  }
269  // Insertion machinery.
270  template <typename Term>
271  void insertion_impl(Term &&term)
272  {
273  // Handle the case of a table with no buckets.
274  if (unlikely(!m_container.bucket_count())) {
275  m_container._increase_size();
276  }
277  // Try to locate the term.
278  auto bucket_idx = m_container._bucket(term);
279  const auto it = m_container._find(term, bucket_idx);
280  if (it == m_container.end()) {
281  // New term.
282  if (unlikely(m_container.size() == std::numeric_limits<size_type>::max())) {
283  piranha_throw(std::overflow_error, "maximum number of elements reached");
284  }
285  // Term is new. Handle the case in which we need to rehash because of load factor.
286  if (unlikely(static_cast<double>(m_container.size() + size_type(1u))
287  / static_cast<double>(m_container.bucket_count())
288  > m_container.max_load_factor())) {
289  m_container._increase_size();
290  // We need a new bucket index in case of a rehash.
291  bucket_idx = m_container._bucket(term);
292  }
293  // Actually perform the insertion and finish by updating the size.
294  m_container._unique_insert(std::forward<Term>(term), bucket_idx);
295  m_container._update_size(m_container.size() + size_type(1u));
296  } else {
297  // Existing term - update the exponent.
298  update_exponent(it->e, term.e);
299  }
300  }
301  template <typename U = T, typename std::enable_if<std::is_integral<U>::value, int>::type = 0>
302  static void update_exponent(value_type &a, const value_type &b)
303  {
304  // Check the range when the exponent is an integral type.
305  piranha_assert(a > 0);
306  piranha_assert(b > 0);
307  // NOTE: this is safe as we require b to be a positive value.
308  if (unlikely(a > std::numeric_limits<value_type>::max() - b)) {
309  piranha_throw(std::invalid_argument, "overflow in the computation of the exponent of a divisor term");
310  }
311  a = static_cast<value_type>(a + b);
312  }
313  template <typename U = T, typename std::enable_if<!std::is_integral<U>::value, int>::type = 0>
314  static void update_exponent(value_type &a, const value_type &b)
315  {
316  // For integer exponents, just do normal addition.
317  a += b;
318  }
319  // Enabler for insertion.
320  template <typename It, typename Exponent>
321  using insert_enabler =
322  typename std::enable_if<is_input_iterator<It>::value
323  && has_safe_cast<value_type, typename std::iterator_traits<It>::value_type>::value
324  && has_safe_cast<value_type, Exponent>::value,
325  int>::type;
326 
327 public:
329  static const std::size_t multiply_arity = 1u;
331 
336 
339  divisor() = default;
341  divisor(const divisor &) = default;
343  divisor(divisor &&) = default;
345 
355  explicit divisor(const divisor &other, const symbol_fset &args) : m_container(other.m_container)
356  {
357  if (unlikely(!is_compatible(args))) {
358  piranha_throw(std::invalid_argument, "the constructed divisor is incompatible with the "
359  "input symbol set");
360  }
361  }
363 
366  explicit divisor(const symbol_fset &) {}
369  {
370  piranha_assert(destruction_checks());
372  }
374 
381  divisor &operator=(const divisor &other) = default;
383 
388  divisor &operator=(divisor &&other) = default;
390 
419  template <typename It, typename Exponent, insert_enabler<It, Exponent> = 0>
420  void insert(It begin, It end, const Exponent &e)
421  {
422  p_type term;
423  // Assign the exponent.
424  term.e = safe_cast<value_type>(e);
425  if (unlikely(term.e <= 0)) {
426  piranha_throw(std::invalid_argument, "a term of a divisor must have a positive exponent");
427  }
428  // Build the vector to be inserted.
429  for (; begin != end; ++begin) {
430  term.v.push_back(safe_cast<value_type>(*begin));
431  }
432  // Range check.
433  if (unlikely(!term_range_check(term))) {
434  piranha_throw(std::invalid_argument, "an element in a term of a divisor is outside the allowed range");
435  }
436  // Check that the term is canonical.
437  if (unlikely(!term_is_canonical(term))) {
438  piranha_throw(std::invalid_argument, "term not in canonical form");
439  }
440  // Perform the insertion.
441  insertion_impl(std::move(term));
442  }
444 
447  size_type size() const
448  {
449  return m_container.size();
450  }
452 
455  const container_type &_container() const
456  {
457  return m_container;
458  }
460 
464  {
465  return m_container;
466  }
468 
471  void clear()
472  {
473  m_container.clear();
474  }
476 
486  bool operator==(const divisor &other) const
487  {
488  if (size() != other.size()) {
489  return false;
490  }
491  const auto it_f_x = m_container.end(), it_f_y = other.m_container.end();
492  for (auto it = m_container.begin(); it != it_f_x; ++it) {
493  const auto tmp_it = other.m_container.find(*it);
494  if (tmp_it == it_f_y || tmp_it->e != it->e) {
495  return false;
496  }
497  }
498  return true;
499  }
501 
506  bool operator!=(const divisor &other) const
507  {
508  return !((*this) == other);
509  }
511 
517  std::size_t hash() const
518  {
519  // NOTE: here we are using a simple sum to compute the hash of a vector of vectors. Indeed,
520  // we need some add/multiply like operation in order to make sure that two divisors
521  // with the same elements stored in different order hash to the same thing. This seems
522  // to be ok, according to this,
523  // http://stackoverflow.com/questions/18021643/hashing-a-set-of-integers-in-an-order-independent-way,
524  // as the hash of each subvector should be rather good (it comes from boost hash combine).
525  // XOR is also commutative and might be used (see link above).
526  // If problem arises, we can consider muliplying the hash of each subvector by a prime
527  // (similar to Kronecker substitution).
528  std::size_t retval = 0u;
529  p_type_hasher hasher;
530  const auto it_f = m_container.end();
531  for (auto it = m_container.begin(); it != it_f; ++it) {
532  retval = static_cast<std::size_t>(retval + hasher(*it));
533  }
534  return retval;
535  }
537 
546  bool is_compatible(const symbol_fset &args) const noexcept
547  {
548  return m_container.empty() || (m_container.begin()->v.size() == args.size());
549  }
551 
556  bool is_zero(const symbol_fset &) const noexcept
557  {
558  return false;
559  }
561 
566  bool is_unitary(const symbol_fset &) const
567  {
568  return m_container.empty();
569  }
571 
593  {
594  divisor retval;
595  const auto it_f = m_container.end();
596  p_type tmp;
597  for (auto it = m_container.begin(); it != it_f; ++it) {
598  vector_key_merge_symbols(tmp.v, it->v, ins_map, args);
599  tmp.e = it->e;
600  auto ret = retval.m_container.insert(std::move(tmp));
601  (void)ret;
602  piranha_assert(ret.first != retval.m_container.end());
603  piranha_assert(ret.second);
604  }
605  return retval;
606  }
608 
619  void print(std::ostream &os, const symbol_fset &args) const
620  {
621  // Don't print anything if there are no terms.
622  if (m_container.empty()) {
623  return;
624  }
625  if (unlikely(m_container.begin()->v.size() != args.size())) {
626  piranha_throw(std::invalid_argument, "invalid size of arguments set");
627  }
628  const auto it_f = m_container.end();
629  bool first_term = true;
630  os << "1/[";
631  for (auto it = m_container.begin(); it != it_f; ++it) {
632  // If this is not the first term, print a leading '*' operator.
633  if (first_term) {
634  first_term = false;
635  } else {
636  os << '*';
637  }
638  bool printed_something = false;
639  os << '(';
640  auto it_args = args.begin();
641  for (typename v_type::size_type i = 0u; i < it->v.size(); ++i, ++it_args) {
642  // If the aij is zero, don't print anything.
643  if (math::is_zero(it->v[i])) {
644  continue;
645  }
646  // A positive aij, in case previous output exists, must be preceded
647  // by a "+" sign.
648  if (it->v[i] > 0 && printed_something) {
649  os << '+';
650  }
651  // Print the aij, unless it's "-1": in that case, just print the minus sign.
652  if (it->v[i] == -1) {
653  os << '-';
654  } else if (it->v[i] != 1) {
655  os << detail::prepare_for_print(it->v[i]) << '*';
656  }
657  // Finally, print name of variable.
658  os << *it_args;
659  printed_something = true;
660  }
661  os << ')';
662  // Print the exponent, if different from one.
663  if (it->e != 1) {
664  os << "**" << detail::prepare_for_print(it->e);
665  }
666  }
667  os << ']';
668  }
670 
681  void print_tex(std::ostream &os, const symbol_fset &args) const
682  {
683  // Don't print anything if there are no terms.
684  if (m_container.empty()) {
685  return;
686  }
687  if (unlikely(m_container.begin()->v.size() != args.size())) {
688  piranha_throw(std::invalid_argument, "invalid size of arguments set");
689  }
690  const auto it_f = m_container.end();
691  os << "\\frac{1}{";
692  for (auto it = m_container.begin(); it != it_f; ++it) {
693  bool printed_something = false;
694  os << "\\left(";
695  auto it_args = args.begin();
696  for (typename v_type::size_type i = 0u; i < it->v.size(); ++i, ++it_args) {
697  // If the aij is zero, don't print anything.
698  if (math::is_zero(it->v[i])) {
699  continue;
700  }
701  // A positive aij, in case previous output exists, must be preceded
702  // by a "+" sign.
703  if (it->v[i] > 0 && printed_something) {
704  os << '+';
705  }
706  // Print the aij, unless it's "-1": in that case, just print the minus sign.
707  if (it->v[i] == -1) {
708  os << '-';
709  } else if (it->v[i] != 1) {
710  os << detail::prepare_for_print(it->v[i]);
711  }
712  // Finally, print name of variable.
713  os << *it_args;
714  printed_something = true;
715  }
716  os << "\\right)";
717  // Print the exponent, if different from one.
718  if (it->e != 1) {
719  os << "^{" << detail::prepare_for_print(it->e) << "}";
720  }
721  }
722  os << '}';
723  }
724 
725 private:
726  // Evaluation utilities.
727  // NOTE: here we are not actually requiring that the eval_type has to be destructible, copyable/movable, etc.
728  // We just assume it is a sane type of some kind.
729  // NOTE: this will have to be fixed in the rework.
730  template <typename U>
731  using eval_sum_type = decltype(std::declval<const value_type &>() * std::declval<const U &>());
732  template <typename U>
733  using eval_type_
734  = decltype(math::pow(std::declval<const eval_sum_type<U> &>(), std::declval<const value_type &>()));
735  template <typename U>
736  using eval_type = enable_if_t<
737  conjunction<std::is_constructible<eval_type_<U>, const int &>, is_divisible_in_place<eval_type_<U>>,
738  std::is_constructible<eval_sum_type<U>, const int &>, is_addable_in_place<eval_sum_type<U>>>::value,
739  eval_type_<U>>;
740 
741 public:
743 
760  template <typename U>
761  eval_type<U> evaluate(const std::vector<U> &values, const symbol_fset &args) const
762  {
763  if (unlikely(args.size() != values.size())) {
764  piranha_throw(std::invalid_argument, "cannot evaluate divisor: the size of the symbol set ("
765  + std::to_string(args.size())
766  + ") differs from the size of the vector of values ("
767  + std::to_string(values.size()) + ")");
768  }
769  const auto em = m_container.empty();
770  if (unlikely(!em && m_container.begin()->v.size() != args.size())) {
771  piranha_throw(std::invalid_argument, "cannot evaluate divisor: the size of the symbol set ("
772  + std::to_string(args.size())
773  + ") differs from the number of symbols in the divisor ("
774  + std::to_string(m_container.begin()->v.size()) + ")");
775  }
776  // Just return 1 if the container is empty.
777  eval_type<U> retval(1);
778  if (em) {
779  return retval;
780  }
781  const auto it_f = m_container.end();
782  for (auto it = m_container.begin(); it != it_f; ++it) {
783  eval_sum_type<U> tmp(0);
784  for (typename v_type::size_type i = 0u; i < it->v.size(); ++i) {
785  // NOTE: might use multadd here eventually for performance.
786  tmp += it->v[i] * values[static_cast<decltype(values.size())>(i)];
787  }
788  // NOTE: consider rewriting this in terms of multiplications as a performance
789  // improvement - the eval_type deduction should be changed accordingly.
790  retval /= math::pow(tmp, it->e);
791  }
792  return retval;
793  }
794 
795 private:
796  // Multiplication utilities.
797  template <typename Cf>
798  using multiply_enabler = enable_if_t<has_mul3<Cf>::value, int>;
799 
800 public:
802 
825  template <typename Cf, multiply_enabler<Cf> = 0>
826  static void multiply(std::array<term<Cf, divisor>, multiply_arity> &res, const term<Cf, divisor> &t1,
827  const term<Cf, divisor> &t2, const symbol_fset &args)
828  {
829  term<Cf, divisor> &t = res[0u];
830  if (unlikely(!t1.m_key.is_compatible(args) || !t2.m_key.is_compatible(args))) {
831  piranha_throw(std::invalid_argument, "cannot multiply terms with divisor keys: at least one of the terms "
832  "is not compatible with the input symbol set");
833  }
834  // Coefficient.
835  cf_mult_impl(t.m_cf, t1.m_cf, t2.m_cf);
836  // Now deal with the key.
837  // Establish the largest and smallest divisor.
838  const divisor &large = (t1.m_key.size() >= t2.m_key.size()) ? t1.m_key : t2.m_key;
839  const divisor &small = (t1.m_key.size() < t2.m_key.size()) ? t1.m_key : t2.m_key;
840  // Assign the large to the result.
841  t.m_key = large;
842  // Run the multiplication.
843  const auto it_f = small.m_container.end();
844  for (auto it = small.m_container.begin(); it != it_f; ++it) {
845  t.m_key.insertion_impl(*it);
846  }
847  }
849 
863  void trim_identify(std::vector<char> &trim_mask, const symbol_fset &args) const
864  {
865  if (unlikely(!is_compatible(args))) {
866  piranha_throw(std::invalid_argument, "invalid arguments set for trim_identify()");
867  }
868  if (unlikely(trim_mask.size() != args.size())) {
869  piranha_throw(std::invalid_argument,
870  "invalid symbol_set for trim_identify() in a divisor: the size of the symbol set ("
871  + std::to_string(args.size()) + ") differs from the size of the trim mask ("
872  + std::to_string(trim_mask.size()) + ")");
873  }
874  const auto it_f = m_container.end();
875  for (auto it = m_container.begin(); it != it_f; ++it) {
876  for (typename v_type::size_type i = 0u; i < it->v.size(); ++i) {
877  if (!math::is_zero(it->v[i])) {
878  trim_mask[static_cast<decltype(trim_mask.size())>(i)] = 0;
879  }
880  }
881  }
882  }
884 
900  divisor trim(const std::vector<char> &trim_mask, const symbol_fset &args) const
901  {
902  if (unlikely(!is_compatible(args))) {
903  piranha_throw(std::invalid_argument, "invalid arguments set for trim()");
904  }
905  if (unlikely(trim_mask.size() != args.size())) {
906  piranha_throw(std::invalid_argument,
907  "invalid symbol_set for trim() in a divisor: the size of the symbol set ("
908  + std::to_string(args.size()) + ") differs from the size of the trim mask ("
909  + std::to_string(trim_mask.size()) + ")");
910  }
911  divisor retval;
912  const auto it_f = m_container.end();
913  for (auto it = m_container.begin(); it != it_f; ++it) {
914  v_type tmp;
915  for (typename v_type::size_type i = 0u; i < it->v.size(); ++i) {
916  if (!trim_mask[static_cast<decltype(trim_mask.size())>(i)]) {
917  tmp.push_back(it->v[i]);
918  }
919  }
920  retval.insert(tmp.begin(), tmp.end(), it->e);
921  }
922  return retval;
923  }
925 
940  std::pair<divisor, divisor> split(const symbol_idx &p, const symbol_fset &args) const
941  {
942  if (unlikely(!is_compatible(args))) {
943  piranha_throw(std::invalid_argument, "invalid size of arguments set");
944  }
945  if (unlikely(p >= args.size())) {
946  piranha_throw(std::invalid_argument,
947  "invalid index for the splitting of a divisor: the value of the index (" + std::to_string(p)
948  + ") is not less than the number of symbols in the divisor ("
949  + std::to_string(args.size()) + ")");
950  }
951  using s_type = typename v_type::size_type;
952  std::pair<divisor, divisor> retval;
953  const auto it_f = m_container.end();
954  for (auto it = m_container.begin(); it != it_f; ++it) {
955  // NOTE: static cast is safe here, as we checked for compatibility.
956  if (math::is_zero(it->v[static_cast<s_type>(p)])) {
957  retval.second.m_container.insert(*it);
958  } else {
959  retval.first.m_container.insert(*it);
960  }
961  }
962  return retval;
963  }
964 
965 private:
966 #if !defined(PIRANHA_DOXYGEN_INVOKED)
967  // Make friend with the s11n functions.
968  template <typename Archive, typename T1>
969  friend void boost::serialization::save(Archive &, const piranha::boost_s11n_key_wrapper<piranha::divisor<T1>> &,
970  unsigned);
971  template <typename Archive, typename T1>
972  friend void boost::serialization::load(Archive &, piranha::boost_s11n_key_wrapper<piranha::divisor<T1>> &,
973  unsigned);
974 #endif
975 
976 #if defined(PIRANHA_WITH_MSGPACK)
977  template <typename Stream>
978  using msgpack_pack_enabler
979  = enable_if_t<conjunction<is_msgpack_stream<Stream>, has_msgpack_pack<Stream, container_type>>::value, int>;
980  template <typename U>
981  using msgpack_convert_enabler = enable_if_t<has_msgpack_convert<typename U::container_type>::value, int>;
982 
983 public:
985 
999  template <typename Stream, msgpack_pack_enabler<Stream> = 0>
1000  void msgpack_pack(msgpack::packer<Stream> &p, msgpack_format f, const symbol_fset &args) const
1001  {
1002  if (unlikely(!is_compatible(args))) {
1003  piranha_throw(std::invalid_argument, "an invalid symbol_set was passed as an argument for the "
1004  "msgpack_pack() method of a divisor");
1005  }
1006  piranha::msgpack_pack(p, m_container, f);
1007  }
1009 
1024  template <typename U = divisor, msgpack_convert_enabler<U> = 0>
1025  void msgpack_convert(const msgpack::object &o, msgpack_format f, const symbol_fset &args)
1026  {
1027  try {
1028  piranha::msgpack_convert(m_container, o, f);
1029  if (unlikely(!destruction_checks())) {
1030  piranha_throw(std::invalid_argument, "the divisor loaded from a msgpack object failed internal "
1031  "consistency checks");
1032  }
1033  if (unlikely(!is_compatible(args))) {
1034  piranha_throw(std::invalid_argument, "the divisor loaded from a msgpack object is not compatible "
1035  "with the supplied symbol set");
1036  }
1037  } catch (...) {
1038  m_container = container_type{};
1039  throw;
1040  }
1041  }
1042 #endif
1043 
1044 private:
1045  container_type m_container;
1046 };
1047 
1048 template <typename T>
1049 const std::size_t divisor<T>::multiply_arity;
1050 }
1051 
1052 // Implementation of the Boost s11n api.
1053 namespace boost
1054 {
1055 namespace serialization
1056 {
1057 
1058 template <typename Archive, typename T>
1059 inline void save(Archive &ar, const piranha::boost_s11n_key_wrapper<piranha::divisor<T>> &k, unsigned)
1060 {
1061  if (unlikely(!k.key().is_compatible(k.ss()))) {
1062  piranha_throw(std::invalid_argument, "an invalid symbol_set was passed as an argument during the "
1063  "Boost serialization of a divisor");
1064  }
1065  piranha::boost_save(ar, k.key().m_container);
1066 }
1067 
1068 template <typename Archive, typename T>
1069 inline void load(Archive &ar, piranha::boost_s11n_key_wrapper<piranha::divisor<T>> &k, unsigned)
1070 {
1071  try {
1072  piranha::boost_load(ar, k.key().m_container);
1073  if (unlikely(!k.key().destruction_checks())) {
1074  piranha_throw(std::invalid_argument, "the divisor loaded from a Boost archive failed internal "
1075  "consistency checks");
1076  }
1077  if (unlikely(!k.key().is_compatible(k.ss()))) {
1078  piranha_throw(std::invalid_argument, "the divisor loaded from a Boost archive is not compatible "
1079  "with the supplied symbol set");
1080  }
1081  } catch (...) {
1082  k.key().m_container = typename piranha::divisor<T>::container_type{};
1083  throw;
1084  }
1085 }
1086 
1087 template <typename Archive, typename T>
1088 inline void serialize(Archive &ar, piranha::boost_s11n_key_wrapper<piranha::divisor<T>> &k, unsigned version)
1089 {
1090  split_free(ar, k, version);
1091 }
1092 }
1093 }
1094 
1095 namespace piranha
1096 {
1097 
1098 inline namespace impl
1099 {
1100 
1101 template <typename Archive, typename T>
1102 using divisor_boost_save_enabler = enable_if_t<has_boost_save<Archive, typename divisor<T>::container_type>::value>;
1103 
1104 template <typename Archive, typename T>
1105 using divisor_boost_load_enabler = enable_if_t<has_boost_load<Archive, typename divisor<T>::container_type>::value>;
1106 }
1107 
1109 
1116 template <typename Archive, typename T>
1117 struct boost_save_impl<Archive, boost_s11n_key_wrapper<divisor<T>>, divisor_boost_save_enabler<Archive, T>>
1118  : boost_save_via_boost_api<Archive, boost_s11n_key_wrapper<divisor<T>>> {
1119 };
1120 
1122 
1132 template <typename Archive, typename T>
1133 struct boost_load_impl<Archive, boost_s11n_key_wrapper<divisor<T>>, divisor_boost_load_enabler<Archive, T>>
1134  : boost_load_via_boost_api<Archive, boost_s11n_key_wrapper<divisor<T>>> {
1135 };
1136 }
1137 
1138 namespace std
1139 {
1140 
1141 template <typename T>
1142 struct hash<piranha::divisor<T>> {
1144  typedef size_t result_type;
1146  typedef piranha::divisor<T> argument_type;
1148 
1153  result_type operator()(const argument_type &a) const
1154  {
1155  return a.hash();
1156  }
1157 };
1158 }
1159 
1160 #endif
math_pow_t< T, U > pow(const T &x, const U &y)
Exponentiation.
Definition: pow.hpp:126
Cf m_cf
Coefficient member.
Definition: term.hpp:189
void boost_load(Archive &ar, T &x)
Load from Boost archive.
Definition: s11n.hpp:469
container_type & _container()
Mutable access to the internal container.
Definition: divisor.hpp:463
std::size_t hash() const
Hash value.
Definition: divisor.hpp:517
Default implementation of piranha::boost_load().
Definition: s11n.hpp:391
const container_type & _container() const
Const access to the internal container.
Definition: divisor.hpp:455
void msgpack_convert(T &x, const msgpack::object &o, msgpack_format f)
Convert msgpack object.
Definition: s11n.hpp:957
divisor(const symbol_fset &)
Constructor from piranha::symbol_fset.
Definition: divisor.hpp:366
static const std::size_t multiply_arity
Arity of the multiply() method.
Definition: divisor.hpp:329
Key type concept check.
Definition: is_key.hpp:65
divisor(const divisor &other, const symbol_fset &args)
Converting constructor.
Definition: divisor.hpp:355
In-place divisible type trait.
const_iterator find(const key_type &k) const
Find element.
Definition: hash_set.hpp:963
Implementation of piranha::boost_load() via the Boost API.
Definition: s11n.hpp:363
In-place addable type trait.
Exceptions.
bool operator!=(const divisor &other) const
Inequality operator.
Definition: divisor.hpp:506
const_iterator end() const
Const end iterator.
Definition: hash_set.hpp:883
Default implementation of piranha::boost_save().
Definition: s11n.hpp:245
STL namespace.
Key m_key
Key member.
Definition: term.hpp:191
void insert(It begin, It end, const Exponent &e)
Create and insert a term from range and exponent.
Definition: divisor.hpp:420
divisor merge_symbols(const symbol_idx_fmap< symbol_fset > &ins_map, const symbol_fset &args) const
Merge symbols.
Definition: divisor.hpp:592
typename container_type::size_type size_type
Size type.
Definition: divisor.hpp:334
bool is_zero(const symbol_fset &) const noexcept
Zero check.
Definition: divisor.hpp:556
static void multiply(std::array< term< Cf, divisor >, multiply_arity > &res, const term< Cf, divisor > &t1, const term< Cf, divisor > &t2, const symbol_fset &args)
Multiply terms with a divisor key.
Definition: divisor.hpp:826
size_type size() const
Size.
Definition: divisor.hpp:447
void print(std::ostream &os, const symbol_fset &args) const
Print to stream.
Definition: divisor.hpp:619
std::pair< divisor, divisor > split(const symbol_idx &p, const symbol_fset &args) const
Split divisor.
Definition: divisor.hpp:940
bool is_unitary(const symbol_fset &) const
Check if divisor is unitary.
Definition: divisor.hpp:566
void clear()
Clear.
Definition: divisor.hpp:471
Term class.
Definition: term.hpp:66
#define piranha_throw(exception_type,...)
Exception-throwing macro.
Definition: exceptions.hpp:118
boost::container::flat_set< std::string > symbol_fset
Flat set of symbols.
Wrapper for the serialization of keys via Boost.
Definition: s11n.hpp:514
void trim_identify(std::vector< char > &trim_mask, const symbol_fset &args) const
Identify symbols that can be trimmed.
Definition: divisor.hpp:863
msgpack_format
Serialization format for msgpack.
Definition: s11n.hpp:673
std::size_t hash() const
Hash value.
Definition: series.hpp:2730
void print_tex(std::ostream &os, const symbol_fset &args) const
Print to stream in TeX mode.
Definition: divisor.hpp:681
bool is_compatible(const symbol_fset &args) const noexcept
Compatibility check.
Definition: divisor.hpp:546
bool operator==(const divisor &other) const
Equality operator.
Definition: divisor.hpp:486
symbol_fset::size_type symbol_idx
Symbol index.
std::pair< iterator, bool > insert(U &&k)
Insert element.
Definition: hash_set.hpp:1020
bool is_zero(const T &x)
Zero test.
Definition: math.hpp:133
void msgpack_pack(msgpack::packer< Stream > &p, msgpack_format f, const symbol_fset &args) const
Pack in msgpack format.
Definition: divisor.hpp:1000
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
void msgpack_pack(msgpack::packer< Stream > &packer, const T &x, msgpack_format f)
Pack generic object in a msgpack stream.
Definition: s11n.hpp:856
Divisor class.
Definition: divisor.hpp:182
Detect the presence of piranha::msgpack_pack().
Definition: s11n.hpp:607
Type traits.
divisor trim(const std::vector< char > &trim_mask, const symbol_fset &args) const
Trim.
Definition: divisor.hpp:900
size_type_impl size_type
A fundamental unsigned integer type representing the number of elements stored in the vector...
void boost_save(Archive &ar, const T &x)
Save to Boost archive.
Definition: s11n.hpp:328
T value_type
Alias for T.
Definition: divisor.hpp:192
~divisor()
Trivial destructor.
Definition: divisor.hpp:368
#define PIRANHA_TT_CHECK(tt,...)
Macro for static type trait checks.
void msgpack_convert(const msgpack::object &o, msgpack_format f, const symbol_fset &args)
Convert from msgpack object.
Definition: divisor.hpp:1025
auto gcd3(T &out, const T &a, const T &b) -> decltype(gcd3_impl< T >()(out, a, b))
Ternary GCD.
Definition: math.hpp:2947
safe_cast_type< To, From > safe_cast(const From &x)
Safe cast.
Definition: safe_cast.hpp:219
eval_type< U > evaluate(const std::vector< U > &values, const symbol_fset &args) const
Evaluation.
Definition: divisor.hpp:761