29 #ifndef PIRANHA_DIVISOR_HPP 30 #define PIRANHA_DIVISOR_HPP 42 #include <type_traits> 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> 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> 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 87 template <
class Archive>
88 void save(Archive &ar,
unsigned)
const 93 template <
class Archive>
94 void load(Archive &ar,
unsigned)
99 BOOST_SERIALIZATION_SPLIT_MEMBER()
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>> {
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>> {
121 #if defined(PIRANHA_WITH_MSGPACK) 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 136 template <
typename T>
137 struct msgpack_convert_impl<
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 142 std::array<msgpack::object, 2> tmp;
181 template <
typename T>
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");
187 template <
typename,
typename>
195 using p_type = divisor_p_type<value_type>;
196 using v_type =
typename p_type::v_type;
198 struct p_type_hasher {
199 std::size_t operator()(
const p_type &p)
const 212 static bool term_is_canonical(
const p_type &p)
214 bool first_nonzero_found =
false;
216 for (
const auto &n : p.v) {
221 first_nonzero_found =
true;
226 if (cd != 1 && cd != -1) {
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)
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;
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 &)
244 bool destruction_checks()
const 246 const auto it_f = m_container.end();
247 auto it = m_container.begin();
249 for (; it != it_f; ++it) {
255 if (!term_range_check(*it)) {
259 if (!term_is_canonical(*it)) {
263 if (it->v.size() != v_size) {
270 template <
typename Term>
271 void insertion_impl(Term &&term)
274 if (unlikely(!m_container.bucket_count())) {
275 m_container._increase_size();
278 auto bucket_idx = m_container._bucket(term);
279 const auto it = m_container._find(term, bucket_idx);
280 if (it == m_container.end()) {
282 if (unlikely(m_container.size() == std::numeric_limits<size_type>::max())) {
283 piranha_throw(std::overflow_error,
"maximum number of elements reached");
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();
291 bucket_idx = m_container._bucket(term);
294 m_container._unique_insert(std::forward<Term>(term), bucket_idx);
295 m_container._update_size(m_container.size() + size_type(1u));
298 update_exponent(it->e, term.e);
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)
305 piranha_assert(a > 0);
306 piranha_assert(b > 0);
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");
311 a =
static_cast<value_type
>(a + b);
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)
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,
329 static const std::size_t multiply_arity = 1u;
357 if (unlikely(!is_compatible(args))) {
358 piranha_throw(std::invalid_argument,
"the constructed divisor is incompatible with the " 370 piranha_assert(destruction_checks());
419 template <
typename It,
typename Exponent, insert_enabler<It, Exponent> = 0>
420 void insert(It begin, It end,
const Exponent &e)
425 if (unlikely(
term.e <= 0)) {
426 piranha_throw(std::invalid_argument,
"a term of a divisor must have a positive exponent");
429 for (; begin != end; ++begin) {
430 term.v.push_back(safe_cast<value_type>(*begin));
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");
437 if (unlikely(!term_is_canonical(
term))) {
438 piranha_throw(std::invalid_argument,
"term not in canonical form");
441 insertion_impl(std::move(
term));
449 return m_container.size();
488 if (size() != other.
size()) {
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) {
508 return !((*this) == other);
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));
548 return m_container.empty() || (m_container.begin()->v.size() == args.size());
568 return m_container.empty();
595 const auto it_f = m_container.end();
597 for (
auto it = m_container.begin(); it != it_f; ++it) {
598 vector_key_merge_symbols(tmp.v, it->v, ins_map, args);
600 auto ret = retval.m_container.
insert(std::move(tmp));
602 piranha_assert(ret.first != retval.m_container.
end());
603 piranha_assert(ret.second);
622 if (m_container.empty()) {
625 if (unlikely(m_container.begin()->v.size() != args.size())) {
626 piranha_throw(std::invalid_argument,
"invalid size of arguments set");
628 const auto it_f = m_container.end();
629 bool first_term =
true;
631 for (
auto it = m_container.begin(); it != it_f; ++it) {
638 bool printed_something =
false;
640 auto it_args = args.begin();
648 if (it->v[i] > 0 && printed_something) {
652 if (it->v[i] == -1) {
654 }
else if (it->v[i] != 1) {
655 os << detail::prepare_for_print(it->v[i]) <<
'*';
659 printed_something =
true;
664 os <<
"**" << detail::prepare_for_print(it->e);
684 if (m_container.empty()) {
687 if (unlikely(m_container.begin()->v.size() != args.size())) {
688 piranha_throw(std::invalid_argument,
"invalid size of arguments set");
690 const auto it_f = m_container.end();
692 for (
auto it = m_container.begin(); it != it_f; ++it) {
693 bool printed_something =
false;
695 auto it_args = args.begin();
703 if (it->v[i] > 0 && printed_something) {
707 if (it->v[i] == -1) {
709 }
else if (it->v[i] != 1) {
710 os << detail::prepare_for_print(it->v[i]);
714 printed_something =
true;
719 os <<
"^{" << detail::prepare_for_print(it->e) <<
"}";
730 template <
typename U>
731 using eval_sum_type = decltype(std::declval<const value_type &>() * std::declval<const U &>());
732 template <
typename U>
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<
760 template <
typename U>
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()) +
")");
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()) +
")");
777 eval_type<U> retval(1);
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);
786 tmp += it->v[i] * values[
static_cast<decltype(values.size())
>(i)];
797 template <
typename Cf>
798 using multiply_enabler = enable_if_t<has_mul3<Cf>::value,
int>;
825 template <
typename Cf, multiply_enabler<Cf> = 0>
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");
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);
865 if (unlikely(!is_compatible(args))) {
866 piranha_throw(std::invalid_argument,
"invalid arguments set for trim_identify()");
868 if (unlikely(trim_mask.size() != args.size())) {
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()) +
")");
874 const auto it_f = m_container.end();
875 for (
auto it = m_container.begin(); it != it_f; ++it) {
878 trim_mask[
static_cast<decltype(trim_mask.size())
>(i)] = 0;
902 if (unlikely(!is_compatible(args))) {
903 piranha_throw(std::invalid_argument,
"invalid arguments set for trim()");
905 if (unlikely(trim_mask.size() != args.size())) {
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()) +
")");
912 const auto it_f = m_container.end();
913 for (
auto it = m_container.begin(); it != it_f; ++it) {
916 if (!trim_mask[
static_cast<decltype(trim_mask.size())
>(i)]) {
917 tmp.push_back(it->v[i]);
920 retval.
insert(tmp.begin(), tmp.end(), it->e);
942 if (unlikely(!is_compatible(args))) {
943 piranha_throw(std::invalid_argument,
"invalid size of arguments set");
945 if (unlikely(p >= args.size())) {
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()) +
")");
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) {
957 retval.second.m_container.insert(*it);
959 retval.first.m_container.insert(*it);
966 #if !defined(PIRANHA_DOXYGEN_INVOKED) 968 template <
typename Archive,
typename T1>
971 template <
typename Archive,
typename T1>
976 #if defined(PIRANHA_WITH_MSGPACK) 977 template <
typename Stream>
978 using msgpack_pack_enabler
980 template <
typename U>
981 using msgpack_convert_enabler = enable_if_t<has_msgpack_convert<typename U::container_type>::value,
int>;
999 template <
typename Stream, msgpack_pack_enabler<Stream> = 0>
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");
1024 template <
typename U = divisor, msgpack_convert_enabler<U> = 0>
1029 if (unlikely(!destruction_checks())) {
1030 piranha_throw(std::invalid_argument,
"the divisor loaded from a msgpack object failed internal " 1031 "consistency checks");
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");
1045 container_type m_container;
1048 template <
typename T>
1055 namespace serialization
1058 template <
typename Archive,
typename T>
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");
1068 template <
typename Archive,
typename T>
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");
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");
1087 template <
typename Archive,
typename T>
1090 split_free(ar, k, version);
1098 inline namespace impl
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>;
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>;
1116 template <
typename Archive,
typename T>
1132 template <
typename Archive,
typename T>
1141 template <
typename T>
1142 struct hash<
piranha::divisor<T>> {
1144 typedef size_t result_type;
1153 result_type operator()(
const argument_type &a)
const math_pow_t< T, U > pow(const T &x, const U &y)
Exponentiation.
Cf m_cf
Coefficient member.
void boost_load(Archive &ar, T &x)
Load from Boost archive.
container_type & _container()
Mutable access to the internal container.
std::size_t hash() const
Hash value.
Default implementation of piranha::boost_load().
const container_type & _container() const
Const access to the internal container.
void msgpack_convert(T &x, const msgpack::object &o, msgpack_format f)
Convert msgpack object.
divisor(const symbol_fset &)
Constructor from piranha::symbol_fset.
static const std::size_t multiply_arity
Arity of the multiply() method.
divisor(const divisor &other, const symbol_fset &args)
Converting constructor.
In-place divisible type trait.
const_iterator find(const key_type &k) const
Find element.
Implementation of piranha::boost_load() via the Boost API.
In-place addable type trait.
bool operator!=(const divisor &other) const
Inequality operator.
const_iterator end() const
Const end iterator.
Default implementation of piranha::boost_save().
void insert(It begin, It end, const Exponent &e)
Create and insert a term from range and exponent.
divisor merge_symbols(const symbol_idx_fmap< symbol_fset > &ins_map, const symbol_fset &args) const
Merge symbols.
typename container_type::size_type size_type
Size type.
bool is_zero(const symbol_fset &) const noexcept
Zero check.
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.
size_type size() const
Size.
void print(std::ostream &os, const symbol_fset &args) const
Print to stream.
std::pair< divisor, divisor > split(const symbol_idx &p, const symbol_fset &args) const
Split divisor.
bool is_unitary(const symbol_fset &) const
Check if divisor is unitary.
#define piranha_throw(exception_type,...)
Exception-throwing macro.
boost::container::flat_set< std::string > symbol_fset
Flat set of symbols.
Wrapper for the serialization of keys via Boost.
void trim_identify(std::vector< char > &trim_mask, const symbol_fset &args) const
Identify symbols that can be trimmed.
msgpack_format
Serialization format for msgpack.
std::size_t hash() const
Hash value.
void print_tex(std::ostream &os, const symbol_fset &args) const
Print to stream in TeX mode.
bool is_compatible(const symbol_fset &args) const noexcept
Compatibility check.
bool operator==(const divisor &other) const
Equality operator.
symbol_fset::size_type symbol_idx
Symbol index.
std::pair< iterator, bool > insert(U &&k)
Insert element.
bool is_zero(const T &x)
Zero test.
void msgpack_pack(msgpack::packer< Stream > &p, msgpack_format f, const symbol_fset &args) const
Pack in msgpack format.
boost::container::flat_map< symbol_idx, T > symbol_idx_fmap
Flat map of symbol indices.
Implementation of piranha::boost_save() via the Boost API.
void msgpack_pack(msgpack::packer< Stream > &packer, const T &x, msgpack_format f)
Pack generic object in a msgpack stream.
Detect the presence of piranha::msgpack_pack().
std::size_t size_type
Size type.
divisor trim(const std::vector< char > &trim_mask, const symbol_fset &args) const
Trim.
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.
~divisor()
Trivial destructor.
#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.
auto gcd3(T &out, const T &a, const T &b) -> decltype(gcd3_impl< T >()(out, a, b))
Ternary GCD.
safe_cast_type< To, From > safe_cast(const From &x)
Safe cast.
eval_type< U > evaluate(const std::vector< U > &values, const symbol_fset &args) const
Evaluation.