1 /* Copyright 2009-2016 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8   * the GNU Lesser General Public License as published by the Free
9     Software Foundation; either version 3 of the License, or (at your
10     option) any later version.
11 
12 or
13 
14   * the GNU General Public License as published by the Free Software
15     Foundation; either version 3 of the License, or (at your option) any
16     later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library.  If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #include "../src/series.hpp"
30 
31 #define BOOST_TEST_MODULE series_02_test
32 #include <boost/test/included/unit_test.hpp>
33 
34 #include <cstddef>
35 #include <functional>
36 #include <iostream>
37 #include <random>
38 #include <sstream>
39 #include <stdexcept>
40 #include <string>
41 #include <tuple>
42 #include <type_traits>
43 #include <unordered_map>
44 #include <utility>
45 
46 #include "../src/base_series_multiplier.hpp"
47 #include "../src/debug_access.hpp"
48 #include "../src/exceptions.hpp"
49 #include "../src/forwarding.hpp"
50 #include "../src/init.hpp"
51 #include "../src/key_is_multipliable.hpp"
52 #include "../src/math.hpp"
53 #include "../src/monomial.hpp"
54 #include "../src/mp_integer.hpp"
55 #include "../src/mp_rational.hpp"
56 #include "../src/pow.hpp"
57 #include "../src/real.hpp"
58 #include "../src/s11n.hpp"
59 #include "../src/series_multiplier.hpp"
60 #include "../src/symbol.hpp"
61 #include "../src/symbol_set.hpp"
62 #include "../src/type_traits.hpp"
63 
64 static const int ntries = 1000;
65 static std::mt19937 rng;
66 
67 using namespace piranha;
68 
69 using cf_types = std::tuple<double, integer, rational>;
70 using expo_types = std::tuple<unsigned, integer>;
71 
72 template <typename Cf, typename Expo>
73 class g_series_type : public series<Cf, monomial<Expo>, g_series_type<Cf, Expo>>
74 {
75     typedef series<Cf, monomial<Expo>, g_series_type<Cf, Expo>> base;
76 
77 public:
78     template <typename Cf2>
79     using rebind = g_series_type<Cf2, Expo>;
80     g_series_type() = default;
81     g_series_type(const g_series_type &) = default;
82     g_series_type(g_series_type &&) = default;
g_series_type(const char * name)83     explicit g_series_type(const char *name) : base()
84     {
85         typedef typename base::term_type term_type;
86         // Insert the symbol.
87         this->m_symbol_set.add(name);
88         // Construct and insert the term.
89         this->insert(term_type(Cf(1), typename term_type::key_type{Expo(1)}));
90     }
91     g_series_type &operator=(const g_series_type &) = default;
92     g_series_type &operator=(g_series_type &&) = default;
93     PIRANHA_FORWARDING_CTOR(g_series_type, base)
94     PIRANHA_FORWARDING_ASSIGNMENT(g_series_type, base)
95 };
96 
97 // This is essentially the same as above, just a different type.
98 template <typename Cf, typename Expo>
99 class g_series_type2 : public series<Cf, monomial<Expo>, g_series_type2<Cf, Expo>>
100 {
101 public:
102     typedef series<Cf, monomial<Expo>, g_series_type2<Cf, Expo>> base;
103     g_series_type2() = default;
104     g_series_type2(const g_series_type2 &) = default;
105     g_series_type2(g_series_type2 &&) = default;
g_series_type2(const char * name)106     explicit g_series_type2(const char *name) : base()
107     {
108         typedef typename base::term_type term_type;
109         // Insert the symbol.
110         this->m_symbol_set.add(name);
111         // Construct and insert the term.
112         this->insert(term_type(Cf(1), typename term_type::key_type{Expo(1)}));
113     }
114     g_series_type2 &operator=(const g_series_type2 &) = default;
115     g_series_type2 &operator=(g_series_type2 &&) = default;
PIRANHA_FORWARDING_CTOR(g_series_type2,base)116     PIRANHA_FORWARDING_CTOR(g_series_type2, base)
117     PIRANHA_FORWARDING_ASSIGNMENT(g_series_type2, base)
118     // Provide fake sin/cos methods to test math overloads.
119     g_series_type2 sin() const
120     {
121         return g_series_type2(42);
122     }
cos() const123     g_series_type2 cos() const
124     {
125         return g_series_type2(-42);
126     }
127 };
128 
129 template <typename Cf, typename Key>
130 class g_series_type3 : public series<Cf, Key, g_series_type3<Cf, Key>>
131 {
132     typedef series<Cf, Key, g_series_type3<Cf, Key>> base;
133 
134 public:
135     template <typename Cf2>
136     using rebind = g_series_type3<Cf2, Key>;
137     g_series_type3() = default;
138     g_series_type3(const g_series_type3 &) = default;
139     g_series_type3(g_series_type3 &&) = default;
140     g_series_type3 &operator=(const g_series_type3 &) = default;
141     g_series_type3 &operator=(g_series_type3 &&) = default;
142     PIRANHA_FORWARDING_CTOR(g_series_type3, base)
143     PIRANHA_FORWARDING_ASSIGNMENT(g_series_type3, base)
144 };
145 
146 namespace piranha
147 {
148 
149 template <typename Cf, typename Key>
150 class series_multiplier<g_series_type<Cf, Key>, void> : public base_series_multiplier<g_series_type<Cf, Key>>
151 {
152     using base = base_series_multiplier<g_series_type<Cf, Key>>;
153     template <typename T>
154     using call_enabler = typename std::enable_if<key_is_multipliable<typename T::term_type::cf_type,
155                                                                      typename T::term_type::key_type>::value,
156                                                  int>::type;
157 
158 public:
159     using base::base;
160     template <typename T = g_series_type<Cf, Key>, call_enabler<T> = 0>
operator ()() const161     g_series_type<Cf, Key> operator()() const
162     {
163         return this->plain_multiplication();
164     }
165 };
166 
167 template <typename Cf, typename Key>
168 class series_multiplier<g_series_type2<Cf, Key>, void> : public base_series_multiplier<g_series_type2<Cf, Key>>
169 {
170     using base = base_series_multiplier<g_series_type2<Cf, Key>>;
171     template <typename T>
172     using call_enabler = typename std::enable_if<key_is_multipliable<typename T::term_type::cf_type,
173                                                                      typename T::term_type::key_type>::value,
174                                                  int>::type;
175 
176 public:
177     using base::base;
178     template <typename T = g_series_type2<Cf, Key>, call_enabler<T> = 0>
operator ()() const179     g_series_type2<Cf, Key> operator()() const
180     {
181         return this->plain_multiplication();
182     }
183 };
184 
185 template <typename Cf, typename Key>
186 class series_multiplier<g_series_type3<Cf, Key>, void> : public base_series_multiplier<g_series_type3<Cf, Key>>
187 {
188     using base = base_series_multiplier<g_series_type3<Cf, Key>>;
189     template <typename T>
190     using call_enabler = typename std::enable_if<key_is_multipliable<typename T::term_type::cf_type,
191                                                                      typename T::term_type::key_type>::value,
192                                                  int>::type;
193 
194 public:
195     using base::base;
196     template <typename T = g_series_type3<Cf, Key>, call_enabler<T> = 0>
operator ()() const197     g_series_type3<Cf, Key> operator()() const
198     {
199         return this->plain_multiplication();
200     }
201 };
202 }
203 
204 // Mock coefficient, not differentiable.
205 struct mock_cf {
206     mock_cf();
207     explicit mock_cf(const int &);
208     mock_cf(const mock_cf &);
209     mock_cf(mock_cf &&) noexcept;
210     mock_cf &operator=(const mock_cf &);
211     mock_cf &operator=(mock_cf &&) noexcept;
212     friend std::ostream &operator<<(std::ostream &, const mock_cf &);
213     mock_cf operator-() const;
214     bool operator==(const mock_cf &) const;
215     bool operator!=(const mock_cf &) const;
216     mock_cf &operator+=(const mock_cf &);
217     mock_cf &operator-=(const mock_cf &);
218     mock_cf operator+(const mock_cf &) const;
219     mock_cf operator-(const mock_cf &) const;
220     mock_cf &operator*=(const mock_cf &);
221     mock_cf operator*(const mock_cf &)const;
222 };
223 
BOOST_AUTO_TEST_CASE(series_partial_test)224 BOOST_AUTO_TEST_CASE(series_partial_test)
225 {
226     init();
227     {
228         typedef g_series_type<rational, int> p_type1;
229         p_type1 x1{"x"};
230         BOOST_CHECK(is_differentiable<p_type1>::value);
231         BOOST_CHECK((std::is_same<decltype(x1.partial("foo")), p_type1>::value));
232         p_type1 x{"x"}, y{"y"};
233         BOOST_CHECK_EQUAL(math::partial(x, "x"), 1);
234         BOOST_CHECK_EQUAL(math::partial(x, "y"), 0);
235         BOOST_CHECK_EQUAL(math::partial(-4 * x.pow(2), "x"), -8 * x);
236         BOOST_CHECK_EQUAL(math::partial(-4 * x.pow(2) + y * x, "y"), x);
237         BOOST_CHECK_EQUAL(math::partial(math::partial(-4 * x.pow(2), "x"), "x"), -8);
238         BOOST_CHECK_EQUAL(math::partial(math::partial(math::partial(-4 * x.pow(2), "x"), "x"), "x"), 0);
239         BOOST_CHECK_EQUAL(math::partial(-x + 1, "x"), -1);
240         BOOST_CHECK_EQUAL(math::partial((1 + 2 * x).pow(10), "x"), 20 * (1 + 2 * x).pow(9));
241         BOOST_CHECK_EQUAL(math::partial((1 + 2 * x + y).pow(10), "x"), 20 * (1 + 2 * x + y).pow(9));
242         BOOST_CHECK_EQUAL(math::partial(x * (1 + 2 * x + y).pow(10), "x"),
243                           20 * x * (1 + 2 * x + y).pow(9) + (1 + 2 * x + y).pow(10));
244         BOOST_CHECK(math::partial((1 + 2 * x + y).pow(0), "x").empty());
245         // Custom derivatives.
246         p_type1::register_custom_derivative("x", [](const p_type1 &) { return p_type1{rational(1, 314)}; });
247         BOOST_CHECK_EQUAL(math::partial(x, "x"), rational(1, 314));
248         p_type1::register_custom_derivative("x", [](const p_type1 &) { return p_type1{rational(1, 315)}; });
249         BOOST_CHECK_EQUAL(math::partial(x, "x"), rational(1, 315));
250         p_type1::unregister_custom_derivative("x");
251         p_type1::unregister_custom_derivative("x");
252         BOOST_CHECK_EQUAL(math::partial(x, "x"), 1);
253         // y as implicit function of x: y = x**2.
254         p_type1::register_custom_derivative(
255             "x", [x](const p_type1 &p) -> p_type1 { return p.partial("x") + math::partial(p, "y") * 2 * x; });
256         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1 + 2 * x);
257         p_type1::unregister_custom_derivative("y");
258         p_type1::unregister_custom_derivative("x");
259         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1);
260         BOOST_CHECK_EQUAL(math::partial(x + 2 * y, "y"), 2);
261         p_type1::register_custom_derivative("x", [](const p_type1 &p) { return p.partial("x"); });
262         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1);
263         BOOST_CHECK_EQUAL(math::partial(x + y * x, "x"), y + 1);
264         p_type1::register_custom_derivative(
265             "x", [x](const p_type1 &p) -> p_type1 { return p.partial("x") + math::partial(p, "y") * 2 * x; });
266         p_type1::register_custom_derivative("y", [](const p_type1 &p) -> p_type1 { return 2 * p; });
267         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1 + 4 * x * (x + y));
268         BOOST_CHECK_EQUAL(math::partial(x + y, "y"), 2 * (x + y));
269         p_type1::unregister_all_custom_derivatives();
270         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1);
271         BOOST_CHECK_EQUAL(math::partial(x + 3 * y, "y"), 3);
272     }
273     {
274         typedef g_series_type<integer, rational> p_type2;
275         using p_type2_diff = g_series_type<rational, rational>;
276         p_type2 x2{"x"};
277         BOOST_CHECK(is_differentiable<p_type2>::value);
278         BOOST_CHECK((std::is_same<decltype(x2.partial("foo")), p_type2_diff>::value));
279         p_type2 x{"x"}, y{"y"};
280         BOOST_CHECK_EQUAL(math::partial(x, "x"), 1);
281         BOOST_CHECK_EQUAL(math::partial(x, "y"), 0);
282         BOOST_CHECK_EQUAL(math::partial(-4 * x.pow(2), "x"), -8 * x);
283         BOOST_CHECK_EQUAL(math::partial(-4 * x.pow(2) + y * x, "y"), x);
284         BOOST_CHECK_EQUAL(math::partial(math::partial(-4 * x.pow(2), "x"), "x"), -8);
285         BOOST_CHECK_EQUAL(math::partial(math::partial(math::partial(-4 * x.pow(2), "x"), "x"), "x"), 0);
286         BOOST_CHECK_EQUAL(math::partial(-x + 1, "x"), -1);
287         BOOST_CHECK_EQUAL(math::partial((1 + 2 * x).pow(10), "x"), 20 * (1 + 2 * x).pow(9));
288         BOOST_CHECK_EQUAL(math::partial((1 + 2 * x + y).pow(10), "x"), 20 * (1 + 2 * x + y).pow(9));
289         BOOST_CHECK_EQUAL(math::partial(x * (1 + 2 * x + y).pow(10), "x"),
290                           20 * x * (1 + 2 * x + y).pow(9) + (1 + 2 * x + y).pow(10));
291         BOOST_CHECK(math::partial((1 + 2 * x + y).pow(0), "x").empty());
292         // Custom derivatives.
293         p_type2::register_custom_derivative("x", [](const p_type2 &) { return p_type2_diff{rational(1, 314)}; });
294         BOOST_CHECK_EQUAL(math::partial(x, "x"), rational(1, 314));
295         p_type2::register_custom_derivative("x", [](const p_type2 &) { return p_type2_diff{rational(1, 315)}; });
296         BOOST_CHECK_EQUAL(math::partial(x, "x"), rational(1, 315));
297         p_type2::unregister_custom_derivative("x");
298         BOOST_CHECK_EQUAL(math::partial(x, "x"), 1);
299         // y as implicit function of x: y = x**2.
300         p_type2::register_custom_derivative(
301             "x", [x](const p_type2 &p) { return p.partial("x") + math::partial(p, "y") * 2 * x; });
302         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1 + 2 * x);
303         p_type2::unregister_custom_derivative("y");
304         p_type2::unregister_custom_derivative("x");
305         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1);
306         BOOST_CHECK_EQUAL(math::partial(x + 2 * y, "y"), 2);
307         p_type2::register_custom_derivative("x", [](const p_type2 &p) { return p.partial("x"); });
308         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1);
309         BOOST_CHECK_EQUAL(math::partial(x + y * x, "x"), y + 1);
310         p_type2::register_custom_derivative(
311             "x", [x](const p_type2 &p) { return p.partial("x") + math::partial(p, "y") * 2 * x; });
312         p_type2::register_custom_derivative("y", [](const p_type2 &p) { return 2 * p_type2_diff{p}; });
313         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1 + 4 * x * (x + y));
314         BOOST_CHECK_EQUAL(math::partial(x + y, "y"), 2 * (x + y));
315         p_type2::unregister_all_custom_derivatives();
316         BOOST_CHECK_EQUAL(math::partial(x + y, "x"), 1);
317         BOOST_CHECK_EQUAL(math::partial(x + 3 * y, "y"), 3);
318     }
319     // Check with mock_cf.
320     BOOST_CHECK((!is_differentiable<g_series_type<mock_cf, rational>>::value));
321     {
322         using s0 = g_series_type<double, rational>;
323         using ss0 = g_series_type<s0, rational>;
324         // Series as coefficient.
325         BOOST_CHECK((is_differentiable<ss0>::value));
326         BOOST_CHECK_EQUAL(math::partial(s0{"y"} * ss0{"x"}, "y"), ss0{"x"});
327         BOOST_CHECK_EQUAL(math::partial(s0{"y"} * ss0{"x"}, "x"), s0{"y"});
328         BOOST_CHECK_EQUAL(math::partial(s0{"y"} * math::pow(ss0{"x"}, 5), "x"), 5 * s0{"y"} * math::pow(ss0{"x"}, 4));
329     }
330 }
331 
BOOST_AUTO_TEST_CASE(series_serialization_test)332 BOOST_AUTO_TEST_CASE(series_serialization_test)
333 {
334     // Serialization test done with a randomly-generated series.
335     typedef g_series_type<rational, int> p_type1;
336     auto x = p_type1{"x"}, y = p_type1{"y"}, z = p_type1{"z"};
337     std::uniform_int_distribution<int> int_dist(0, 5);
338     std::uniform_int_distribution<unsigned> size_dist(0u, 10u);
339     p_type1 tmp;
340     for (int i = 0; i < ntries; ++i) {
341         p_type1 p;
342         const unsigned size = size_dist(rng);
343         for (unsigned j = 0u; j < size; ++j) {
344             p += math::pow(x, int_dist(rng)) * math::pow(y, int_dist(rng)) * math::pow(z, int_dist(rng));
345         }
346         p *= int_dist(rng);
347         const auto div = int_dist(rng);
348         if (div) {
349             p /= div;
350         }
351         std::stringstream ss;
352         {
353             boost::archive::text_oarchive oa(ss);
354             oa << p;
355         }
356         {
357             boost::archive::text_iarchive ia(ss);
358             ia >> tmp;
359         }
360         BOOST_CHECK_EQUAL(tmp, p);
361     }
362 }
363 
364 struct mock_key {
365     mock_key() = default;
366     mock_key(const mock_key &) = default;
367     mock_key(mock_key &&) noexcept;
368     mock_key &operator=(const mock_key &) = default;
369     mock_key &operator=(mock_key &&) noexcept;
370     mock_key(const symbol_set &);
371     bool operator==(const mock_key &) const;
372     bool operator!=(const mock_key &) const;
373     bool is_compatible(const symbol_set &) const noexcept;
374     bool is_ignorable(const symbol_set &) const noexcept;
375     mock_key merge_args(const symbol_set &, const symbol_set &) const;
376     bool is_unitary(const symbol_set &) const;
377     void print(std::ostream &, const symbol_set &) const;
378     void print_tex(std::ostream &, const symbol_set &) const;
379     void trim_identify(symbol_set &, const symbol_set &) const;
380     mock_key trim(const symbol_set &, const symbol_set &) const;
381 };
382 
383 namespace std
384 {
385 
386 template <>
387 struct hash<mock_key> {
388     std::size_t operator()(const mock_key &) const;
389 };
390 }
391 
BOOST_AUTO_TEST_CASE(series_evaluate_test)392 BOOST_AUTO_TEST_CASE(series_evaluate_test)
393 {
394     typedef g_series_type<rational, int> p_type1;
395     typedef std::unordered_map<std::string, rational> dict_type;
396     typedef std::unordered_map<std::string, int> dict_type_int;
397     typedef std::unordered_map<std::string, long> dict_type_long;
398     BOOST_CHECK((is_evaluable<p_type1, rational>::value));
399     BOOST_CHECK((is_evaluable<p_type1, integer>::value));
400     BOOST_CHECK((is_evaluable<p_type1, int>::value));
401     BOOST_CHECK((is_evaluable<p_type1, long>::value));
402     BOOST_CHECK((std::is_same<rational, decltype(p_type1{}.evaluate(dict_type_int{}))>::value));
403     BOOST_CHECK((std::is_same<rational, decltype(p_type1{}.evaluate(dict_type_long{}))>::value));
404     BOOST_CHECK_EQUAL(p_type1{}.evaluate(dict_type{}), 0);
405     p_type1 x{"x"}, y{"y"};
406     BOOST_CHECK_THROW(x.evaluate(dict_type{}), std::invalid_argument);
407     BOOST_CHECK_EQUAL(x.evaluate(dict_type{{"x", rational(1)}}), 1);
408     BOOST_CHECK_THROW((x + (2 * y).pow(3)).evaluate(dict_type{{"x", rational(1)}}), std::invalid_argument);
409     BOOST_CHECK_EQUAL((x + (2 * y).pow(3)).evaluate(dict_type{{"x", rational(1)}, {"y", rational(2, 3)}}),
410                       rational(1) + (2 * rational(2, 3)).pow(3));
411     BOOST_CHECK_EQUAL((x + (2 * y).pow(3)).evaluate(dict_type{{"x", rational(1)}, {"y", rational(2, 3)}}),
412                       math::evaluate(x + (2 * y).pow(3), dict_type{{"x", rational(1)}, {"y", rational(2, 3)}}));
413     BOOST_CHECK((std::is_same<decltype(p_type1{}.evaluate(dict_type{})), rational>::value));
414     typedef std::unordered_map<std::string, real> dict_type2;
415     BOOST_CHECK((is_evaluable<p_type1, real>::value));
416     BOOST_CHECK_EQUAL((x + (2 * y).pow(3)).evaluate(dict_type2{{"x", real(1.234)}, {"y", real(-5.678)}, {"z", real()}}),
417                       real(1.234) + math::pow(2 * real(-5.678), 3));
418     BOOST_CHECK_EQUAL(
419         (x + (2 * y).pow(3)).evaluate(dict_type2{{"x", real(1.234)}, {"y", real(-5.678)}, {"z", real()}}),
420         math::evaluate(x + math::pow(2 * y, 3), dict_type2{{"x", real(1.234)}, {"y", real(-5.678)}, {"z", real()}}));
421     BOOST_CHECK((std::is_same<decltype(p_type1{}.evaluate(dict_type2{})), real>::value));
422     typedef std::unordered_map<std::string, double> dict_type3;
423     BOOST_CHECK((is_evaluable<p_type1, double>::value));
424     BOOST_CHECK_EQUAL((x + (2 * y).pow(3)).evaluate(dict_type3{{"x", 1.234}, {"y", -5.678}, {"z", 0.0001}}),
425                       1.234 + math::pow(2 * -5.678, 3));
426     BOOST_CHECK_EQUAL((x + (2 * y).pow(3)).evaluate(dict_type3{{"x", 1.234}, {"y", -5.678}, {"z", 0.0001}}),
427                       math::evaluate(x + math::pow(2 * y, 3), dict_type3{{"x", 1.234}, {"y", -5.678}, {"z", 0.0001}}));
428     BOOST_CHECK((std::is_same<decltype(p_type1{}.evaluate(dict_type3{})), double>::value));
429     BOOST_CHECK((!is_evaluable<g_series_type3<double, mock_key>, double>::value));
430     // NOTE: this used to be true before we changed the ctor from int of mock_cf to explicit.
431     BOOST_CHECK((!is_evaluable<g_series_type3<mock_cf, monomial<int>>, double>::value));
432     BOOST_CHECK((!is_evaluable<g_series_type3<mock_cf, mock_key>, double>::value));
433     BOOST_CHECK((is_evaluable<g_series_type3<double, monomial<int>>, double>::value));
434     // Check the syntax from initializer list with explicit template parameter.
435     BOOST_CHECK_EQUAL(p_type1{}.evaluate<int>({{"foo", 4.}}), 0);
436     BOOST_CHECK_EQUAL(p_type1{}.evaluate<double>({{"foo", 4.}, {"bar", 7}}), 0);
437     BOOST_CHECK_EQUAL(math::evaluate<int>(p_type1{}, {{"foo", 4.}}), 0);
438     BOOST_CHECK_EQUAL(math::evaluate<double>(p_type1{}, {{"foo", 4.}, {"bar", 7}}), 0);
439 }
440 
441 template <typename Expo>
442 class g_series_type_nr : public series<float, monomial<Expo>, g_series_type_nr<Expo>>
443 {
444     typedef series<float, monomial<Expo>, g_series_type_nr<Expo>> base;
445 
446 public:
447     g_series_type_nr() = default;
448     g_series_type_nr(const g_series_type_nr &) = default;
449     g_series_type_nr(g_series_type_nr &&) = default;
450     g_series_type_nr &operator=(const g_series_type_nr &) = default;
451     g_series_type_nr &operator=(g_series_type_nr &&) = default;
452     PIRANHA_FORWARDING_CTOR(g_series_type_nr, base)
453     PIRANHA_FORWARDING_ASSIGNMENT(g_series_type_nr, base)
454 };
455 
456 template <typename Expo>
457 class g_series_type_nr2 : public series<short, monomial<Expo>, g_series_type_nr2<Expo>>
458 {
459     typedef series<short, monomial<Expo>, g_series_type_nr2<Expo>> base;
460 
461 public:
462     template <typename Expo2>
463     using rebind = g_series_type_nr2<Expo2>;
464     g_series_type_nr2() = default;
465     g_series_type_nr2(const g_series_type_nr2 &) = default;
466     g_series_type_nr2(g_series_type_nr2 &&) = default;
467     g_series_type_nr2 &operator=(const g_series_type_nr2 &) = default;
468     g_series_type_nr2 &operator=(g_series_type_nr2 &&) = default;
469     PIRANHA_FORWARDING_CTOR(g_series_type_nr2, base)
470     PIRANHA_FORWARDING_ASSIGNMENT(g_series_type_nr2, base)
471 };
472 
473 template <typename Expo>
474 class g_series_type_nr3 : public series<float, monomial<Expo>, g_series_type_nr3<Expo>>
475 {
476     typedef series<float, monomial<Expo>, g_series_type_nr3<Expo>> base;
477 
478 public:
479     template <typename Expo2>
480     using rebind = void;
481     g_series_type_nr3() = default;
482     g_series_type_nr3(const g_series_type_nr3 &) = default;
483     g_series_type_nr3(g_series_type_nr3 &&) = default;
484     g_series_type_nr3 &operator=(const g_series_type_nr3 &) = default;
485     g_series_type_nr3 &operator=(g_series_type_nr3 &&) = default;
486     PIRANHA_FORWARDING_CTOR(g_series_type_nr3, base)
487     PIRANHA_FORWARDING_ASSIGNMENT(g_series_type_nr3, base)
488 };
489 
BOOST_AUTO_TEST_CASE(series_series_is_rebindable_test)490 BOOST_AUTO_TEST_CASE(series_series_is_rebindable_test)
491 {
492     typedef g_series_type<rational, int> p_type1;
493     BOOST_CHECK((series_is_rebindable<p_type1, int>::value));
494     BOOST_CHECK((std::is_same<series_rebind<p_type1, int>, g_series_type<int, int>>::value));
495     BOOST_CHECK((series_is_rebindable<p_type1, rational>::value));
496     BOOST_CHECK((std::is_same<series_rebind<p_type1, rational>, p_type1>::value));
497     BOOST_CHECK((std::is_same<series_rebind<p_type1 &, rational const>, p_type1>::value));
498     BOOST_CHECK((series_is_rebindable<p_type1, p_type1>::value));
499     BOOST_CHECK((series_is_rebindable<p_type1 &, p_type1>::value));
500     BOOST_CHECK((series_is_rebindable<p_type1 &, const p_type1>::value));
501     BOOST_CHECK((std::is_same<series_rebind<p_type1, p_type1>, g_series_type<p_type1, int>>::value));
502     typedef g_series_type_nr<int> p_type_nr;
503     BOOST_CHECK((!series_is_rebindable<p_type_nr, unsigned>::value));
504     BOOST_CHECK((!series_is_rebindable<p_type_nr, integer>::value));
505     BOOST_CHECK((!series_is_rebindable<p_type_nr &, unsigned const>::value));
506     BOOST_CHECK((!series_is_rebindable<p_type_nr &&, const integer &>::value));
507     typedef g_series_type_nr2<int> p_type_nr2;
508     BOOST_CHECK((!series_is_rebindable<p_type_nr2, unsigned>::value));
509     BOOST_CHECK((!series_is_rebindable<p_type_nr2, integer>::value));
510     typedef g_series_type_nr3<int> p_type_nr3;
511     BOOST_CHECK((!series_is_rebindable<p_type_nr3, unsigned>::value));
512     BOOST_CHECK((!series_is_rebindable<p_type_nr3, integer>::value));
513     // Check when the requirements on the input types are not satisfied.
514     BOOST_CHECK((!series_is_rebindable<p_type1, std::string>::value));
515     BOOST_CHECK((!series_is_rebindable<p_type1, std::vector<std::string>>::value));
516     BOOST_CHECK((!series_is_rebindable<p_type1, std::vector<std::string> &>::value));
517     BOOST_CHECK((!series_is_rebindable<p_type1, const std::vector<std::string> &>::value));
518     BOOST_CHECK((!series_is_rebindable<p_type1, std::vector<std::string> &&>::value));
519     BOOST_CHECK((!series_is_rebindable<std::string, std::vector<std::string>>::value));
520     BOOST_CHECK((!series_is_rebindable<const std::string &, std::vector<std::string>>::value));
521     BOOST_CHECK((!series_is_rebindable<const std::string &, std::vector<std::string> &&>::value));
522 }
523 
BOOST_AUTO_TEST_CASE(series_series_recursion_index_test)524 BOOST_AUTO_TEST_CASE(series_series_recursion_index_test)
525 {
526     BOOST_CHECK_EQUAL(series_recursion_index<int>::value, 0u);
527     BOOST_CHECK_EQUAL(series_recursion_index<double>::value, 0u);
528     BOOST_CHECK_EQUAL(series_recursion_index<float>::value, 0u);
529     BOOST_CHECK_EQUAL((series_recursion_index<g_series_type<rational, int>>::value), 1u);
530     BOOST_CHECK_EQUAL((series_recursion_index<g_series_type<float, int>>::value), 1u);
531     BOOST_CHECK_EQUAL((series_recursion_index<g_series_type<double, int>>::value), 1u);
532     BOOST_CHECK_EQUAL((series_recursion_index<g_series_type<g_series_type<double, int>, int>>::value), 2u);
533     BOOST_CHECK_EQUAL((series_recursion_index<g_series_type<g_series_type<double, int>, long>>::value), 2u);
534     BOOST_CHECK_EQUAL(
535         (series_recursion_index<g_series_type<g_series_type<g_series_type<double, int>, int>, long>>::value), 3u);
536     BOOST_CHECK_EQUAL(
537         (series_recursion_index<g_series_type<g_series_type<g_series_type<rational, int>, int>, long>>::value), 3u);
538     BOOST_CHECK_EQUAL(
539         (series_recursion_index<g_series_type<g_series_type<g_series_type<rational, int>, int>, long> &>::value), 3u);
540     BOOST_CHECK_EQUAL(
541         (series_recursion_index<g_series_type<g_series_type<g_series_type<rational, int>, int>, long> const>::value),
542         3u);
543     BOOST_CHECK_EQUAL(
544         (series_recursion_index<g_series_type<g_series_type<g_series_type<rational, int>, int>, long> const &>::value),
545         3u);
546 }
547 
548 PIRANHA_DECLARE_HAS_TYPEDEF(type);
549 
550 template <typename T, typename U>
551 using binary_series_op_return_type = detail::binary_series_op_return_type<T, U, 0>;
552 
BOOST_AUTO_TEST_CASE(series_binary_series_op_return_type_test)553 BOOST_AUTO_TEST_CASE(series_binary_series_op_return_type_test)
554 {
555     // Check missing type in case both operands are not series.
556     BOOST_CHECK((!has_typedef_type<binary_series_op_return_type<int, int>>::value));
557     BOOST_CHECK((!has_typedef_type<binary_series_op_return_type<int, double>>::value));
558     BOOST_CHECK((!has_typedef_type<binary_series_op_return_type<float, double>>::value));
559     // Case 0.
560     // NOTE: this cannot fail in any way as we require coefficients to be addable in is_cf.
561     typedef g_series_type<rational, int> p_type1;
562     BOOST_CHECK((std::is_same<p_type1, binary_series_op_return_type<p_type1, p_type1>::type>::value));
563     // Case 1 and 2.
564     typedef g_series_type<double, int> p_type2;
565     BOOST_CHECK((std::is_same<p_type2, binary_series_op_return_type<p_type2, p_type1>::type>::value));
566     BOOST_CHECK((std::is_same<p_type2, binary_series_op_return_type<p_type1, p_type2>::type>::value));
567     // mock_cf supports only multiplication vs mock_cf.
568     BOOST_CHECK((!has_typedef_type<binary_series_op_return_type<g_series_type<double, int>,
569                                                                 g_series_type<mock_cf, int>>>::value));
570     BOOST_CHECK((!has_typedef_type<binary_series_op_return_type<g_series_type<mock_cf, int>,
571                                                                 g_series_type<double, int>>>::value));
572     // Case 3.
573     typedef g_series_type<short, int> p_type3;
574     BOOST_CHECK((std::is_same<g_series_type<int, int>, binary_series_op_return_type<p_type3, p_type3>::type>::value));
575     typedef g_series_type<char, int> p_type4;
576     BOOST_CHECK((std::is_same<g_series_type<int, int>, binary_series_op_return_type<p_type3, p_type4>::type>::value));
577     BOOST_CHECK((std::is_same<g_series_type<int, int>, binary_series_op_return_type<p_type4, p_type3>::type>::value));
578     // Wrong rebind implementations.
579     BOOST_CHECK(
580         (!has_typedef_type<binary_series_op_return_type<g_series_type_nr2<int>, g_series_type<char, int>>>::value));
581     BOOST_CHECK(
582         (!has_typedef_type<binary_series_op_return_type<g_series_type<char, int>, g_series_type_nr2<int>>>::value));
583     // Case 4 and 6.
584     BOOST_CHECK((std::is_same<p_type2, binary_series_op_return_type<p_type2, int>::type>::value));
585     BOOST_CHECK((std::is_same<p_type2, binary_series_op_return_type<int, p_type2>::type>::value));
586     // mock_cf does not support multiplication with int.
587     BOOST_CHECK((!has_typedef_type<binary_series_op_return_type<g_series_type<mock_cf, int>, int>>::value));
588     BOOST_CHECK((!has_typedef_type<binary_series_op_return_type<int, g_series_type<mock_cf, int>>>::value));
589     // Case 5 and 7.
590     BOOST_CHECK((std::is_same<p_type2, binary_series_op_return_type<p_type3, double>::type>::value));
591     BOOST_CHECK((std::is_same<p_type2, binary_series_op_return_type<double, p_type3>::type>::value));
592     BOOST_CHECK((std::is_same<g_series_type<int, int>, binary_series_op_return_type<p_type4, short>::type>::value));
593     BOOST_CHECK((std::is_same<g_series_type<int, int>, binary_series_op_return_type<short, p_type4>::type>::value));
594     // These need rebinding, but rebind is not supported.
595     BOOST_CHECK((!has_typedef_type<binary_series_op_return_type<g_series_type_nr<int>, double>>::value));
596     BOOST_CHECK((!has_typedef_type<binary_series_op_return_type<double, g_series_type_nr<int>>>::value));
597     // Wrong implementation of rebind.
598     BOOST_CHECK(
599         (!has_typedef_type<binary_series_op_return_type<g_series_type_nr2<char>, g_series_type<char, char>>>::value));
600     BOOST_CHECK(
601         (!has_typedef_type<binary_series_op_return_type<g_series_type<char, char>, g_series_type_nr2<char>>>::value));
602     // Same coefficients, amibguity in series type.
603     BOOST_CHECK(
604         (!has_typedef_type<binary_series_op_return_type<g_series_type_nr<int>, g_series_type<float, int>>>::value));
605 }
606 
607 struct arithmetics_add_tag {
608 };
609 
610 namespace piranha
611 {
612 template <>
613 class debug_access<arithmetics_add_tag>
614 {
615 public:
616     template <typename Cf>
617     struct runner {
618         template <typename Expo>
operator ()piranha::debug_access::runner619         void operator()(const Expo &) const
620         {
621             typedef g_series_type<Cf, Expo> p_type1;
622             typedef g_series_type2<Cf, Expo> p_type2;
623             typedef g_series_type<int, Expo> p_type3;
624             // Binary add first.
625             // Some type checks - these are not addable as they result in an ambiguity
626             // between two series with same coefficient but different series types.
627             BOOST_CHECK((!is_addable<p_type1, p_type2>::value));
628             BOOST_CHECK((!is_addable<p_type2, p_type1>::value));
629             BOOST_CHECK((!is_addable_in_place<p_type1, p_type2>::value));
630             BOOST_CHECK((!is_addable_in_place<p_type2, p_type1>::value));
631             // Various subcases of case 0.
632             p_type1 x{"x"}, y{"y"};
633             // No need to merge args.
634             auto tmp = x + x;
635             BOOST_CHECK_EQUAL(tmp.size(), 1u);
636             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1));
637             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
638             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
639             // Try with moves on both sides.
640             tmp = p_type1{x} + x;
641             BOOST_CHECK_EQUAL(tmp.size(), 1u);
642             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1));
643             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
644             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
645             tmp = x + p_type1{x};
646             BOOST_CHECK_EQUAL(tmp.size(), 1u);
647             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1));
648             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
649             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
650             tmp = p_type1{x} + p_type1{x};
651             BOOST_CHECK_EQUAL(tmp.size(), 1u);
652             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1));
653             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
654             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
655             // Check that move erases.
656             auto x_copy(x);
657             tmp = std::move(x) + x_copy;
658             BOOST_CHECK_EQUAL(x.size(), 0u);
659             x = x_copy;
660             tmp = x_copy + std::move(x);
661             BOOST_CHECK_EQUAL(x.size(), 0u);
662             x = x_copy;
663             // A few self move tests.
664             tmp = std::move(x) + std::move(x);
665             BOOST_CHECK_EQUAL(tmp.size(), 1u);
666             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1));
667             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
668             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
669             x = p_type1{"x"};
670             tmp = x + std::move(x);
671             BOOST_CHECK_EQUAL(tmp.size(), 1u);
672             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1));
673             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
674             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
675             x = p_type1{"x"};
676             tmp = std::move(x) + x;
677             BOOST_CHECK_EQUAL(tmp.size(), 1u);
678             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1));
679             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
680             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
681             x = p_type1{"x"};
682             // Now with merging.
683             tmp = x + y;
684             BOOST_CHECK_EQUAL(tmp.size(), 2u);
685             auto it = tmp.m_container.begin();
686             BOOST_CHECK(it->m_cf == Cf(1));
687             BOOST_CHECK(it->m_key.size() == 2u);
688             ++it;
689             BOOST_CHECK(it->m_cf == Cf(1));
690             BOOST_CHECK(it->m_key.size() == 2u);
691             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
692             // With moves.
693             tmp = p_type1{x} + y;
694             BOOST_CHECK_EQUAL(tmp.size(), 2u);
695             it = tmp.m_container.begin();
696             BOOST_CHECK(it->m_cf == Cf(1));
697             BOOST_CHECK(it->m_key.size() == 2u);
698             ++it;
699             BOOST_CHECK(it->m_cf == Cf(1));
700             BOOST_CHECK(it->m_key.size() == 2u);
701             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
702             tmp = x + p_type1{y};
703             BOOST_CHECK_EQUAL(tmp.size(), 2u);
704             it = tmp.m_container.begin();
705             BOOST_CHECK(it->m_cf == Cf(1));
706             BOOST_CHECK(it->m_key.size() == 2u);
707             ++it;
708             BOOST_CHECK(it->m_cf == Cf(1));
709             BOOST_CHECK(it->m_key.size() == 2u);
710             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
711             // Test the swapping of operands when one series is larger than the other.
712             tmp = (x + y) + x;
713             BOOST_CHECK_EQUAL(tmp.size(), 2u);
714             it = tmp.m_container.begin();
715             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(2));
716             BOOST_CHECK(it->m_key.size() == 2u);
717             ++it;
718             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(2));
719             BOOST_CHECK(it->m_key.size() == 2u);
720             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
721             tmp = x + (y + x);
722             BOOST_CHECK_EQUAL(tmp.size(), 2u);
723             it = tmp.m_container.begin();
724             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(2));
725             BOOST_CHECK(it->m_key.size() == 2u);
726             ++it;
727             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(2));
728             BOOST_CHECK(it->m_key.size() == 2u);
729             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
730             // Some tests for case 1/4.
731             tmp = x + p_type3{"y"};
732             BOOST_CHECK_EQUAL(tmp.size(), 2u);
733             it = tmp.m_container.begin();
734             BOOST_CHECK(it->m_cf == 1);
735             BOOST_CHECK(it->m_key.size() == 2u);
736             ++it;
737             BOOST_CHECK(it->m_cf == 1);
738             BOOST_CHECK(it->m_key.size() == 2u);
739             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
740             tmp = x + (p_type3{"y"} + p_type3{"x"});
741             BOOST_CHECK_EQUAL(tmp.size(), 2u);
742             it = tmp.m_container.begin();
743             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
744             BOOST_CHECK(it->m_key.size() == 2u);
745             ++it;
746             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
747             BOOST_CHECK(it->m_key.size() == 2u);
748             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
749             tmp = x + 1;
750             BOOST_CHECK_EQUAL(tmp.size(), 2u);
751             it = tmp.m_container.begin();
752             BOOST_CHECK(it->m_cf == 1);
753             BOOST_CHECK(it->m_key.size() == 1u);
754             ++it;
755             BOOST_CHECK(it->m_cf == 1);
756             BOOST_CHECK(it->m_key.size() == 1u);
757             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
758             // Symmetric of the previous case.
759             tmp = p_type3{"y"} + x;
760             BOOST_CHECK_EQUAL(tmp.size(), 2u);
761             it = tmp.m_container.begin();
762             BOOST_CHECK(it->m_cf == 1);
763             BOOST_CHECK(it->m_key.size() == 2u);
764             ++it;
765             BOOST_CHECK(it->m_cf == 1);
766             BOOST_CHECK(it->m_key.size() == 2u);
767             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
768             tmp = (p_type3{"y"} + p_type3{"x"}) + x;
769             BOOST_CHECK_EQUAL(tmp.size(), 2u);
770             it = tmp.m_container.begin();
771             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
772             BOOST_CHECK(it->m_key.size() == 2u);
773             ++it;
774             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
775             BOOST_CHECK(it->m_key.size() == 2u);
776             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
777             tmp = 1 + x;
778             BOOST_CHECK_EQUAL(tmp.size(), 2u);
779             it = tmp.m_container.begin();
780             BOOST_CHECK(it->m_cf == 1);
781             BOOST_CHECK(it->m_key.size() == 1u);
782             ++it;
783             BOOST_CHECK(it->m_cf == 1);
784             BOOST_CHECK(it->m_key.size() == 1u);
785             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
786             // Case 3/5 and symmetric.
787             using p_type4 = g_series_type<g_series_type<int, Expo>, Expo>;
788             using p_type5 = g_series_type<double, Expo>;
789             auto tmp2 = p_type4{"x"} + p_type5{"y"};
790             BOOST_CHECK_EQUAL(tmp2.size(), 2u);
791             BOOST_CHECK((std::is_same<decltype(tmp2), g_series_type<g_series_type<double, Expo>, Expo>>::value));
792             auto it2 = tmp2.m_container.begin();
793             BOOST_CHECK((it2->m_cf == g_series_type<double, Expo>{"y"} || it2->m_cf == 1));
794             BOOST_CHECK(it2->m_key.size() == 1u);
795             ++it2;
796             BOOST_CHECK((it2->m_cf == g_series_type<double, Expo>{"y"} || it2->m_cf == 1));
797             BOOST_CHECK(it2->m_key.size() == 1u);
798             BOOST_CHECK((tmp2.m_symbol_set == symbol_set{symbol{"x"}}));
799             tmp2 = p_type5{"y"} + p_type4{"x"};
800             BOOST_CHECK_EQUAL(tmp2.size(), 2u);
801             BOOST_CHECK((std::is_same<decltype(tmp2), g_series_type<g_series_type<double, Expo>, Expo>>::value));
802             it2 = tmp2.m_container.begin();
803             BOOST_CHECK((it2->m_cf == g_series_type<double, Expo>{"y"} || it2->m_cf == 1));
804             BOOST_CHECK(it2->m_key.size() == 1u);
805             ++it2;
806             BOOST_CHECK((it2->m_cf == g_series_type<double, Expo>{"y"} || it2->m_cf == 1));
807             BOOST_CHECK(it2->m_key.size() == 1u);
808             BOOST_CHECK((tmp2.m_symbol_set == symbol_set{symbol{"x"}}));
809             // Now in-place.
810             // Case 0.
811             tmp = x;
812             tmp += x;
813             BOOST_CHECK_EQUAL(tmp.size(), 1u);
814             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1));
815             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
816             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
817             // Move.
818             tmp = x;
819             tmp += p_type1{x};
820             BOOST_CHECK_EQUAL(tmp.size(), 1u);
821             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1));
822             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
823             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
824             // Check that a move really happens.
825             tmp = x;
826             tmp += std::move(x);
827             // NOTE: here the symbol set has still size 1 as it does not get moved
828             // (it gets moved only when operands are swapped because of difference
829             // in sizes or because it's a sub operation).
830             BOOST_CHECK_EQUAL(x.size(), 0u);
831             x = p_type1{"x"};
832             // Move self.
833             tmp += std::move(tmp);
834             BOOST_CHECK_EQUAL(tmp.size(), 1u);
835             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1) + Cf(1) + Cf(1) + Cf(1));
836             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
837             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
838             // Now with merging.
839             tmp = x;
840             tmp += y;
841             BOOST_CHECK_EQUAL(tmp.size(), 2u);
842             it = tmp.m_container.begin();
843             BOOST_CHECK(it->m_cf == Cf(1));
844             BOOST_CHECK(it->m_key.size() == 2u);
845             ++it;
846             BOOST_CHECK(it->m_cf == Cf(1));
847             BOOST_CHECK(it->m_key.size() == 2u);
848             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
849             // With moves.
850             tmp = x;
851             tmp += p_type1{y};
852             BOOST_CHECK_EQUAL(tmp.size(), 2u);
853             it = tmp.m_container.begin();
854             BOOST_CHECK(it->m_cf == Cf(1));
855             BOOST_CHECK(it->m_key.size() == 2u);
856             ++it;
857             BOOST_CHECK(it->m_cf == Cf(1));
858             BOOST_CHECK(it->m_key.size() == 2u);
859             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
860             // Test the swapping of operands when one series is larger than the other.
861             tmp = x + y;
862             tmp += x;
863             BOOST_CHECK_EQUAL(tmp.size(), 2u);
864             it = tmp.m_container.begin();
865             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
866             BOOST_CHECK(it->m_key.size() == 2u);
867             ++it;
868             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
869             BOOST_CHECK(it->m_key.size() == 2u);
870             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
871             tmp = x;
872             tmp += y + x;
873             BOOST_CHECK_EQUAL(tmp.size(), 2u);
874             it = tmp.m_container.begin();
875             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
876             BOOST_CHECK(it->m_key.size() == 2u);
877             ++it;
878             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
879             BOOST_CHECK(it->m_key.size() == 2u);
880             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
881             // Some tests for case 1/4.
882             tmp = x;
883             tmp += p_type3{"y"};
884             BOOST_CHECK_EQUAL(tmp.size(), 2u);
885             it = tmp.m_container.begin();
886             BOOST_CHECK(it->m_cf == 1);
887             BOOST_CHECK(it->m_key.size() == 2u);
888             ++it;
889             BOOST_CHECK(it->m_cf == 1);
890             BOOST_CHECK(it->m_key.size() == 2u);
891             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
892             tmp = x;
893             tmp += p_type3{"y"} + p_type3{"x"};
894             BOOST_CHECK_EQUAL(tmp.size(), 2u);
895             it = tmp.m_container.begin();
896             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
897             BOOST_CHECK(it->m_key.size() == 2u);
898             ++it;
899             BOOST_CHECK(it->m_cf == 1 || it->m_cf == 2);
900             BOOST_CHECK(it->m_key.size() == 2u);
901             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
902             tmp = x;
903             tmp += 1;
904             BOOST_CHECK_EQUAL(tmp.size(), 2u);
905             it = tmp.m_container.begin();
906             BOOST_CHECK(it->m_cf == 1);
907             BOOST_CHECK(it->m_key.size() == 1u);
908             ++it;
909             BOOST_CHECK(it->m_cf == 1);
910             BOOST_CHECK(it->m_key.size() == 1u);
911             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
912             // Symmetric of the previous case.
913             p_type3 tmp3{"y"};
914             tmp3 += x;
915             BOOST_CHECK_EQUAL(tmp3.size(), 2u);
916             auto it3 = tmp3.m_container.begin();
917             BOOST_CHECK(it3->m_cf == 1);
918             BOOST_CHECK(it3->m_key.size() == 2u);
919             ++it3;
920             BOOST_CHECK(it3->m_cf == 1);
921             BOOST_CHECK(it3->m_key.size() == 2u);
922             BOOST_CHECK((tmp3.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
923             tmp3 += p_type3{"y"} + p_type3{"x"};
924             tmp3 += x;
925             BOOST_CHECK_EQUAL(tmp3.size(), 2u);
926             it3 = tmp3.m_container.begin();
927             BOOST_CHECK(it3->m_cf == 2 || it3->m_cf == 3);
928             BOOST_CHECK(it3->m_key.size() == 2u);
929             ++it3;
930             BOOST_CHECK(it3->m_cf == 2 || it3->m_cf == 3);
931             BOOST_CHECK(it3->m_key.size() == 2u);
932             BOOST_CHECK((tmp3.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
933             // Case 3/5.
934             auto tmp4 = p_type4{"x"};
935             tmp4 += p_type5{"y"};
936             BOOST_CHECK_EQUAL(tmp4.size(), 2u);
937             auto it4 = tmp4.m_container.begin();
938             BOOST_CHECK((std::is_same<decltype(it4->m_cf), g_series_type<int, Expo>>::value));
939             BOOST_CHECK((it4->m_cf == g_series_type<int, Expo>{"y"} || it4->m_cf == 1));
940             BOOST_CHECK(it4->m_key.size() == 1u);
941             ++it4;
942             BOOST_CHECK((it4->m_cf == g_series_type<int, Expo>{"y"} || it4->m_cf == 1));
943             BOOST_CHECK(it4->m_key.size() == 1u);
944             BOOST_CHECK((tmp4.m_symbol_set == symbol_set{symbol{"x"}}));
945             // Check with scalar on the left.
946             BOOST_CHECK((!is_addable_in_place<int, p_type1>::value));
947             BOOST_CHECK((!is_addable_in_place<int, p_type2>::value));
948             BOOST_CHECK((!is_addable_in_place<int, p_type3>::value));
949         }
950     };
951     template <typename Cf>
operator ()(const Cf &) const952     void operator()(const Cf &) const
953     {
954         tuple_for_each(expo_types{}, runner<Cf>());
955     }
956 };
957 }
958 
959 typedef debug_access<arithmetics_add_tag> arithmetics_add_tester;
960 
BOOST_AUTO_TEST_CASE(series_arithmetics_add_test)961 BOOST_AUTO_TEST_CASE(series_arithmetics_add_test)
962 {
963     // Functional testing.
964     tuple_for_each(cf_types{}, arithmetics_add_tester());
965     // Type testing for binary addition.
966     typedef g_series_type<rational, int> p_type1;
967     typedef g_series_type<int, rational> p_type2;
968     typedef g_series_type<short, rational> p_type3;
969     typedef g_series_type<char, rational> p_type4;
970     // First let's check the output type.
971     // Case 0.
972     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} + p_type1{})>::value));
973     // Case 1.
974     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} + p_type2{})>::value));
975     // Case 2.
976     BOOST_CHECK((std::is_same<p_type1, decltype(p_type2{} + p_type1{})>::value));
977     // Case 3, symmetric.
978     BOOST_CHECK((std::is_same<p_type2, decltype(p_type3{} + p_type4{})>::value));
979     BOOST_CHECK((std::is_same<p_type2, decltype(p_type4{} + p_type3{})>::value));
980     // Case 4.
981     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} + 0)>::value));
982     // Case 5.
983     BOOST_CHECK((std::is_same<p_type2, decltype(p_type3{} + 0)>::value));
984     // Case 6.
985     BOOST_CHECK((std::is_same<p_type1, decltype(0 + p_type1{})>::value));
986     // Case 7.
987     BOOST_CHECK((std::is_same<p_type2, decltype(0 + p_type3{})>::value));
988     // Check non-addable series.
989     typedef g_series_type2<rational, int> p_type5;
990     BOOST_CHECK((!is_addable<p_type1, p_type5>::value));
991     BOOST_CHECK((!is_addable<p_type5, p_type1>::value));
992     // Check coefficient series.
993     typedef g_series_type<p_type1, int> p_type11;
994     typedef g_series_type<p_type2, rational> p_type22;
995     typedef g_series_type<p_type1, rational> p_type21;
996     BOOST_CHECK((std::is_same<p_type11, decltype(p_type1{} + p_type11{})>::value));
997     BOOST_CHECK((std::is_same<p_type11, decltype(p_type11{} + p_type1{})>::value));
998     BOOST_CHECK((std::is_same<p_type21, decltype(p_type1{} + p_type22{})>::value));
999     BOOST_CHECK((std::is_same<p_type21, decltype(p_type22{} + p_type1{})>::value));
1000     BOOST_CHECK((std::is_same<p_type11, decltype(p_type11{} + p_type22{})>::value));
1001     BOOST_CHECK((std::is_same<p_type11, decltype(p_type22{} + p_type11{})>::value));
1002     // Type testing for in-place addition.
1003     // Case 0.
1004     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() += p_type1{})>::value));
1005     // Case 1.
1006     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() += p_type2{})>::value));
1007     // Case 2.
1008     BOOST_CHECK((std::is_same<p_type2 &, decltype(std::declval<p_type2 &>() += p_type1{})>::value));
1009     // Case 3, symmetric.
1010     BOOST_CHECK((std::is_same<p_type3 &, decltype(std::declval<p_type3 &>() += p_type4{})>::value));
1011     BOOST_CHECK((std::is_same<p_type4 &, decltype(std::declval<p_type4 &>() += p_type3{})>::value));
1012     // Case 4.
1013     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() += 0)>::value));
1014     // Case 5.
1015     BOOST_CHECK((std::is_same<p_type3 &, decltype(std::declval<p_type3 &>() += 0)>::value));
1016     // Cases 6 and 7 do not make sense at the moment.
1017     BOOST_CHECK((!is_addable_in_place<int, p_type3>::value));
1018     BOOST_CHECK((!is_addable_in_place<p_type1, p_type11>::value));
1019     // Checks for coefficient series.
1020     p_type11 tmp;
1021     BOOST_CHECK((std::is_same<p_type11 &, decltype(tmp += p_type1{})>::value));
1022     p_type22 tmp2;
1023     BOOST_CHECK((std::is_same<p_type22 &, decltype(tmp2 += p_type1{})>::value));
1024 }
1025 
1026 struct arithmetics_sub_tag {
1027 };
1028 
1029 namespace piranha
1030 {
1031 template <>
1032 class debug_access<arithmetics_sub_tag>
1033 {
1034 public:
1035     template <typename Cf>
1036     struct runner {
1037         template <typename Expo>
operator ()piranha::debug_access::runner1038         void operator()(const Expo &) const
1039         {
1040             typedef g_series_type<Cf, Expo> p_type1;
1041             typedef g_series_type2<Cf, Expo> p_type2;
1042             typedef g_series_type<int, Expo> p_type3;
1043             // Binary sub first.
1044             // Some type checks - these are not subtractable as they result in an ambiguity
1045             // between two series with same coefficient but different series types.
1046             BOOST_CHECK((!is_subtractable<p_type1, p_type2>::value));
1047             BOOST_CHECK((!is_subtractable<p_type2, p_type1>::value));
1048             BOOST_CHECK((!is_subtractable_in_place<p_type1, p_type2>::value));
1049             BOOST_CHECK((!is_subtractable_in_place<p_type2, p_type1>::value));
1050             // Various subcases of case 0.
1051             p_type1 x{"x"}, y{"y"}, x2 = x + x;
1052             // No need to merge args.
1053             auto tmp = x2 - x;
1054             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1055             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1));
1056             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1057             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1058             // Check going to zero.
1059             tmp = x - x;
1060             BOOST_CHECK(tmp.size() == 0u);
1061             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1062             // Try with moves on both sides.
1063             tmp = p_type1{x} - x2;
1064             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1065             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(-1));
1066             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1067             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1068             tmp = x2 - p_type1{x};
1069             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1070             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1));
1071             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1072             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1073             tmp = p_type1{x2} - p_type1{x};
1074             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1075             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1));
1076             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1077             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1078             // Check that move erases.
1079             auto x_copy(x);
1080             tmp = std::move(x) - x_copy;
1081             BOOST_CHECK_EQUAL(x.size(), 0u);
1082             x = x_copy;
1083             tmp = x_copy - std::move(x);
1084             BOOST_CHECK_EQUAL(x.size(), 0u);
1085             x = x_copy;
1086             // Self move tests.
1087             tmp = std::move(x) - std::move(x);
1088             BOOST_CHECK_EQUAL(tmp.size(), 0u);
1089             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1090             x = p_type1{"x"};
1091             tmp = x - std::move(x);
1092             BOOST_CHECK_EQUAL(tmp.size(), 0u);
1093             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1094             x = p_type1{"x"};
1095             tmp = std::move(x) - x;
1096             BOOST_CHECK_EQUAL(tmp.size(), 0u);
1097             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1098             x = p_type1{"x"};
1099             // Now with merging.
1100             tmp = x - y;
1101             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1102             auto it = tmp.m_container.begin();
1103             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1104             BOOST_CHECK(it->m_key.size() == 2u);
1105             ++it;
1106             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1107             BOOST_CHECK(it->m_key.size() == 2u);
1108             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1109             // With moves.
1110             tmp = p_type1{x} - y;
1111             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1112             it = tmp.m_container.begin();
1113             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1114             BOOST_CHECK(it->m_key.size() == 2u);
1115             ++it;
1116             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1117             BOOST_CHECK(it->m_key.size() == 2u);
1118             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1119             tmp = x - p_type1{y};
1120             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1121             it = tmp.m_container.begin();
1122             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1123             BOOST_CHECK(it->m_key.size() == 2u);
1124             ++it;
1125             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1126             BOOST_CHECK(it->m_key.size() == 2u);
1127             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1128             // Test the swapping of operands when one series is larger than the other.
1129             tmp = (x2 - y) - x;
1130             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1131             it = tmp.m_container.begin();
1132             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1133             BOOST_CHECK(it->m_key.size() == 2u);
1134             ++it;
1135             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1136             BOOST_CHECK(it->m_key.size() == 2u);
1137             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1138             tmp = x2 - (y - x);
1139             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1140             it = tmp.m_container.begin();
1141             BOOST_CHECK(it->m_cf == 3 || it->m_cf == -1);
1142             BOOST_CHECK(it->m_key.size() == 2u);
1143             ++it;
1144             BOOST_CHECK(it->m_cf == 3 || it->m_cf == -1);
1145             BOOST_CHECK(it->m_key.size() == 2u);
1146             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1147             // Some tests for case 1/4.
1148             tmp = x - p_type3{"y"};
1149             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1150             it = tmp.m_container.begin();
1151             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1152             BOOST_CHECK(it->m_key.size() == 2u);
1153             ++it;
1154             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1155             BOOST_CHECK(it->m_key.size() == 2u);
1156             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1157             tmp = x2 - (p_type3{"y"} - p_type3{"x"});
1158             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1159             it = tmp.m_container.begin();
1160             BOOST_CHECK(it->m_cf == 3 || it->m_cf == -1);
1161             BOOST_CHECK(it->m_key.size() == 2u);
1162             ++it;
1163             BOOST_CHECK(it->m_cf == 3 || it->m_cf == -1);
1164             BOOST_CHECK(it->m_key.size() == 2u);
1165             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1166             tmp = x - 1;
1167             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1168             it = tmp.m_container.begin();
1169             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1170             BOOST_CHECK(it->m_key.size() == 1u);
1171             ++it;
1172             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1173             BOOST_CHECK(it->m_key.size() == 1u);
1174             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
1175             // Symmetric of the previous case.
1176             tmp = p_type3{"y"} - x;
1177             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1178             it = tmp.m_container.begin();
1179             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1180             BOOST_CHECK(it->m_key.size() == 2u);
1181             ++it;
1182             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1183             BOOST_CHECK(it->m_key.size() == 2u);
1184             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1185             tmp = (p_type3{"y"} - p_type3{"x"}) - x2;
1186             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1187             it = tmp.m_container.begin();
1188             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -3);
1189             BOOST_CHECK(it->m_key.size() == 2u);
1190             ++it;
1191             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -3);
1192             BOOST_CHECK(it->m_key.size() == 2u);
1193             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1194             tmp = 1 - x;
1195             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1196             it = tmp.m_container.begin();
1197             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1198             BOOST_CHECK(it->m_key.size() == 1u);
1199             ++it;
1200             BOOST_CHECK(it->m_cf == 1 || it->m_cf == -1);
1201             BOOST_CHECK(it->m_key.size() == 1u);
1202             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
1203             // Case 3/5 and symmetric.
1204             using p_type4 = g_series_type<g_series_type<int, Expo>, Expo>;
1205             using p_type5 = g_series_type<double, Expo>;
1206             auto tmp2 = p_type4{"x"} - p_type5{"y"};
1207             BOOST_CHECK_EQUAL(tmp2.size(), 2u);
1208             BOOST_CHECK((std::is_same<decltype(tmp2), g_series_type<g_series_type<double, Expo>, Expo>>::value));
1209             auto it2 = tmp2.m_container.begin();
1210             BOOST_CHECK((it2->m_cf == -g_series_type<double, Expo>{"y"} || it2->m_cf == 1));
1211             BOOST_CHECK(it2->m_key.size() == 1u);
1212             ++it2;
1213             BOOST_CHECK((it2->m_cf == -g_series_type<double, Expo>{"y"} || it2->m_cf == 1));
1214             BOOST_CHECK(it2->m_key.size() == 1u);
1215             BOOST_CHECK((tmp2.m_symbol_set == symbol_set{symbol{"x"}}));
1216             tmp2 = p_type5{"y"} - p_type4{"x"};
1217             BOOST_CHECK_EQUAL(tmp2.size(), 2u);
1218             BOOST_CHECK((std::is_same<decltype(tmp2), g_series_type<g_series_type<double, Expo>, Expo>>::value));
1219             it2 = tmp2.m_container.begin();
1220             BOOST_CHECK((it2->m_cf == g_series_type<double, Expo>{"y"} || it2->m_cf == -1));
1221             BOOST_CHECK(it2->m_key.size() == 1u);
1222             ++it2;
1223             BOOST_CHECK((it2->m_cf == g_series_type<double, Expo>{"y"} || it2->m_cf == -1));
1224             BOOST_CHECK(it2->m_key.size() == 1u);
1225             BOOST_CHECK((tmp2.m_symbol_set == symbol_set{symbol{"x"}}));
1226             // Now in-place.
1227             // Case 0.
1228             tmp = x2;
1229             tmp -= x;
1230             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1231             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1));
1232             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1233             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1234             // Check that a move really happens.
1235             tmp = x;
1236             tmp -= std::move(x);
1237             BOOST_CHECK_EQUAL(x.size(), 0u);
1238             x = p_type1{"x"};
1239             // Move.
1240             tmp = x2;
1241             tmp -= p_type1{x};
1242             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1243             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(1));
1244             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1245             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1246             // Now with merging.
1247             tmp = x;
1248             tmp -= y;
1249             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1250             it = tmp.m_container.begin();
1251             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1252             BOOST_CHECK(it->m_key.size() == 2u);
1253             ++it;
1254             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1255             BOOST_CHECK(it->m_key.size() == 2u);
1256             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1257             // With moves.
1258             tmp = x;
1259             tmp -= p_type1{y};
1260             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1261             it = tmp.m_container.begin();
1262             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1263             BOOST_CHECK(it->m_key.size() == 2u);
1264             ++it;
1265             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1266             BOOST_CHECK(it->m_key.size() == 2u);
1267             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1268             // Move self.
1269             tmp -= std::move(tmp);
1270             BOOST_CHECK_EQUAL(tmp.size(), 0u);
1271             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1272             // Test the swapping of operands when one series is larger than the other.
1273             tmp = x2 - y;
1274             tmp -= x;
1275             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1276             it = tmp.m_container.begin();
1277             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1278             BOOST_CHECK(it->m_key.size() == 2u);
1279             ++it;
1280             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1281             BOOST_CHECK(it->m_key.size() == 2u);
1282             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1283             tmp = x;
1284             tmp -= y - x2;
1285             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1286             it = tmp.m_container.begin();
1287             BOOST_CHECK(it->m_cf == Cf(3) || it->m_cf == Cf(-1));
1288             BOOST_CHECK(it->m_key.size() == 2u);
1289             ++it;
1290             BOOST_CHECK(it->m_cf == Cf(3) || it->m_cf == Cf(-1));
1291             BOOST_CHECK(it->m_key.size() == 2u);
1292             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1293             // Some tests for case 1/4.
1294             tmp = x;
1295             tmp -= p_type3{"y"};
1296             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1297             it = tmp.m_container.begin();
1298             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1299             BOOST_CHECK(it->m_key.size() == 2u);
1300             ++it;
1301             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1302             BOOST_CHECK(it->m_key.size() == 2u);
1303             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1304             tmp = x2;
1305             tmp -= p_type3{"y"} - p_type3{"x"};
1306             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1307             it = tmp.m_container.begin();
1308             BOOST_CHECK(it->m_cf == Cf(3) || it->m_cf == Cf(-1));
1309             BOOST_CHECK(it->m_key.size() == 2u);
1310             ++it;
1311             BOOST_CHECK(it->m_cf == Cf(3) || it->m_cf == Cf(-1));
1312             BOOST_CHECK(it->m_key.size() == 2u);
1313             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1314             tmp = x;
1315             tmp -= 1;
1316             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1317             it = tmp.m_container.begin();
1318             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1319             BOOST_CHECK(it->m_key.size() == 1u);
1320             ++it;
1321             BOOST_CHECK(it->m_cf == Cf(1) || it->m_cf == Cf(-1));
1322             BOOST_CHECK(it->m_key.size() == 1u);
1323             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
1324             // Symmetric of the previous case.
1325             p_type3 tmp3{"y"};
1326             tmp3 -= x;
1327             BOOST_CHECK_EQUAL(tmp3.size(), 2u);
1328             auto it3 = tmp3.m_container.begin();
1329             BOOST_CHECK(it3->m_cf == Cf(1) || it3->m_cf == Cf(-1));
1330             BOOST_CHECK(it3->m_key.size() == 2u);
1331             ++it3;
1332             BOOST_CHECK(it3->m_cf == Cf(1) || it3->m_cf == Cf(-1));
1333             BOOST_CHECK(it3->m_key.size() == 2u);
1334             BOOST_CHECK((tmp3.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1335             tmp3 = p_type3{"x"};
1336             tmp3 -= p_type3{"y"} - p_type3{"x"};
1337             tmp3 -= x;
1338             BOOST_CHECK_EQUAL(tmp3.size(), 2u);
1339             it3 = tmp3.m_container.begin();
1340             BOOST_CHECK(it3->m_cf == Cf(1) || it3->m_cf == Cf(-1));
1341             BOOST_CHECK(it3->m_key.size() == 2u);
1342             ++it3;
1343             BOOST_CHECK(it3->m_cf == Cf(1) || it3->m_cf == Cf(-1));
1344             BOOST_CHECK(it3->m_key.size() == 2u);
1345             BOOST_CHECK((tmp3.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1346             // Case 3/5.
1347             auto tmp4 = p_type4{"x"};
1348             tmp4 -= p_type5{"y"};
1349             BOOST_CHECK_EQUAL(tmp4.size(), 2u);
1350             auto it4 = tmp4.m_container.begin();
1351             BOOST_CHECK((std::is_same<decltype(it4->m_cf), g_series_type<int, Expo>>::value));
1352             BOOST_CHECK((it4->m_cf == -g_series_type<int, Expo>{"y"} || it4->m_cf == 1));
1353             BOOST_CHECK(it4->m_key.size() == 1u);
1354             ++it4;
1355             BOOST_CHECK((it4->m_cf == -g_series_type<int, Expo>{"y"} || it4->m_cf == 1));
1356             BOOST_CHECK(it4->m_key.size() == 1u);
1357             BOOST_CHECK((tmp4.m_symbol_set == symbol_set{symbol{"x"}}));
1358             // Check with scalar on the left.
1359             BOOST_CHECK((!is_subtractable_in_place<int, p_type1>::value));
1360             BOOST_CHECK((!is_subtractable_in_place<int, p_type2>::value));
1361             BOOST_CHECK((!is_subtractable_in_place<int, p_type3>::value));
1362         }
1363     };
1364     template <typename Cf>
operator ()(const Cf &) const1365     void operator()(const Cf &) const
1366     {
1367         tuple_for_each(expo_types{}, runner<Cf>());
1368     }
1369 };
1370 }
1371 
1372 typedef debug_access<arithmetics_sub_tag> arithmetics_sub_tester;
1373 
BOOST_AUTO_TEST_CASE(series_arithmetics_sub_test)1374 BOOST_AUTO_TEST_CASE(series_arithmetics_sub_test)
1375 {
1376     // Functional testing.
1377     tuple_for_each(cf_types{}, arithmetics_sub_tester());
1378     // Type testing for binary subtraction.
1379     typedef g_series_type<rational, int> p_type1;
1380     typedef g_series_type<int, rational> p_type2;
1381     typedef g_series_type<short, rational> p_type3;
1382     typedef g_series_type<char, rational> p_type4;
1383     // First let's check the output type.
1384     // Case 0.
1385     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} - p_type1{})>::value));
1386     // Case 1.
1387     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} - p_type2{})>::value));
1388     // Case 2.
1389     BOOST_CHECK((std::is_same<p_type1, decltype(p_type2{} - p_type1{})>::value));
1390     // Case 3, symmetric.
1391     BOOST_CHECK((std::is_same<p_type2, decltype(p_type3{} - p_type4{})>::value));
1392     BOOST_CHECK((std::is_same<p_type2, decltype(p_type4{} - p_type3{})>::value));
1393     // Case 4.
1394     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} - 0)>::value));
1395     // Case 5.
1396     BOOST_CHECK((std::is_same<p_type2, decltype(p_type3{} - 0)>::value));
1397     // Case 6.
1398     BOOST_CHECK((std::is_same<p_type1, decltype(0 - p_type1{})>::value));
1399     // Case 7.
1400     BOOST_CHECK((std::is_same<p_type2, decltype(0 - p_type3{})>::value));
1401     // Check non-subtractable series.
1402     typedef g_series_type2<rational, int> p_type5;
1403     BOOST_CHECK((!is_subtractable<p_type1, p_type5>::value));
1404     BOOST_CHECK((!is_subtractable<p_type5, p_type1>::value));
1405     // Check coefficient series.
1406     typedef g_series_type<p_type1, int> p_type11;
1407     typedef g_series_type<p_type2, rational> p_type22;
1408     typedef g_series_type<p_type1, rational> p_type21;
1409     BOOST_CHECK((std::is_same<p_type11, decltype(p_type1{} - p_type11{})>::value));
1410     BOOST_CHECK((std::is_same<p_type11, decltype(p_type11{} - p_type1{})>::value));
1411     BOOST_CHECK((std::is_same<p_type21, decltype(p_type1{} - p_type22{})>::value));
1412     BOOST_CHECK((std::is_same<p_type21, decltype(p_type22{} - p_type1{})>::value));
1413     BOOST_CHECK((std::is_same<p_type11, decltype(p_type11{} - p_type22{})>::value));
1414     BOOST_CHECK((std::is_same<p_type11, decltype(p_type22{} - p_type11{})>::value));
1415     // Type testing for in-place subtraction.
1416     // Case 0.
1417     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() -= p_type1{})>::value));
1418     // Case 1.
1419     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() -= p_type2{})>::value));
1420     // Case 2.
1421     BOOST_CHECK((std::is_same<p_type2 &, decltype(std::declval<p_type2 &>() -= p_type1{})>::value));
1422     // Case 3, symmetric.
1423     BOOST_CHECK((std::is_same<p_type3 &, decltype(std::declval<p_type3 &>() -= p_type4{})>::value));
1424     BOOST_CHECK((std::is_same<p_type4 &, decltype(std::declval<p_type4 &>() -= p_type3{})>::value));
1425     // Case 4.
1426     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() -= 0)>::value));
1427     // Case 5.
1428     BOOST_CHECK((std::is_same<p_type3 &, decltype(std::declval<p_type3 &>() -= 0)>::value));
1429     // Cases 6 and 7 do not make sense at the moment.
1430     BOOST_CHECK((!is_subtractable_in_place<int, p_type3>::value));
1431     BOOST_CHECK((!is_subtractable_in_place<p_type1, p_type11>::value));
1432     // Checks for coefficient series.
1433     p_type11 tmp;
1434     BOOST_CHECK((std::is_same<p_type11 &, decltype(tmp -= p_type1{})>::value));
1435     p_type22 tmp2;
1436     BOOST_CHECK((std::is_same<p_type22 &, decltype(tmp2 -= p_type1{})>::value));
1437 }
1438 
1439 struct arithmetics_mul_tag {
1440 };
1441 
1442 namespace piranha
1443 {
1444 template <>
1445 class debug_access<arithmetics_mul_tag>
1446 {
1447 public:
1448     template <typename Cf>
1449     struct runner {
1450         template <typename Expo>
operator ()piranha::debug_access::runner1451         void operator()(const Expo &) const
1452         {
1453             typedef g_series_type<Cf, Expo> p_type1;
1454             typedef g_series_type2<Cf, Expo> p_type2;
1455             typedef g_series_type<int, Expo> p_type3;
1456             // Binary mul first.
1457             // Some type checks - these are not multipliable as they result in an ambiguity
1458             // between two series with same coefficient but different series types.
1459             BOOST_CHECK((!is_multipliable<p_type1, p_type2>::value));
1460             BOOST_CHECK((!is_multipliable<p_type2, p_type1>::value));
1461             BOOST_CHECK((!is_multipliable_in_place<p_type1, p_type2>::value));
1462             BOOST_CHECK((!is_multipliable_in_place<p_type2, p_type1>::value));
1463             // Various subcases of case 0.
1464             p_type1 x{"x"}, y{"y"};
1465             // No need to merge args.
1466             auto tmp = 2 * x * x;
1467             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1468             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(2) * Cf(1));
1469             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1470             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1471             // Try with moves on both sides.
1472             tmp = 3 * p_type1{x} * 2 * x;
1473             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1474             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(3) * Cf(2));
1475             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1476             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1477             tmp = 2 * x * 3 * p_type1{x};
1478             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1479             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(2) * Cf(3));
1480             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1481             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1482             // Now with merging.
1483             tmp = x * y;
1484             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1485             auto it = tmp.m_container.begin();
1486             BOOST_CHECK(it->m_cf == Cf(1) * Cf(1));
1487             BOOST_CHECK(it->m_key.size() == 2u);
1488             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1489             // With moves.
1490             tmp = p_type1{x} * y;
1491             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1492             it = tmp.m_container.begin();
1493             BOOST_CHECK(it->m_cf == Cf(1) * Cf(1));
1494             BOOST_CHECK(it->m_key.size() == 2u);
1495             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1496             tmp = x * p_type1{y};
1497             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1498             it = tmp.m_container.begin();
1499             BOOST_CHECK(it->m_cf == Cf(1) * Cf(1));
1500             BOOST_CHECK(it->m_key.size() == 2u);
1501             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1502             // Test the swapping of operands when one series is larger than the other.
1503             tmp = (x + y) * 2 * x;
1504             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1505             it = tmp.m_container.begin();
1506             BOOST_CHECK(it->m_cf == Cf(2) * Cf(1));
1507             BOOST_CHECK(it->m_key.size() == 2u);
1508             ++it;
1509             BOOST_CHECK(it->m_cf == Cf(2) * Cf(1));
1510             BOOST_CHECK(it->m_key.size() == 2u);
1511             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1512             tmp = x * (2 * y + 2 * x);
1513             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1514             it = tmp.m_container.begin();
1515             BOOST_CHECK(it->m_cf == Cf(2) * Cf(1));
1516             BOOST_CHECK(it->m_key.size() == 2u);
1517             ++it;
1518             BOOST_CHECK(it->m_cf == Cf(2) * Cf(1));
1519             BOOST_CHECK(it->m_key.size() == 2u);
1520             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1521             // Some tests for case 1/4.
1522             tmp = 3 * x * p_type3{"y"};
1523             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1524             it = tmp.m_container.begin();
1525             BOOST_CHECK(it->m_cf == 3);
1526             BOOST_CHECK(it->m_key.size() == 2u);
1527             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1528             tmp = 3 * x * (p_type3{"y"} + p_type3{"x"});
1529             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1530             it = tmp.m_container.begin();
1531             BOOST_CHECK(it->m_cf == 3);
1532             BOOST_CHECK(it->m_key.size() == 2u);
1533             ++it;
1534             BOOST_CHECK(it->m_cf == 3);
1535             BOOST_CHECK(it->m_key.size() == 2u);
1536             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1537             tmp = x * 2;
1538             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1539             it = tmp.m_container.begin();
1540             BOOST_CHECK(it->m_cf == 2);
1541             BOOST_CHECK(it->m_key.size() == 1u);
1542             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
1543             // Symmetric of the previous case.
1544             tmp = p_type3{"y"} * x * 3;
1545             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1546             it = tmp.m_container.begin();
1547             BOOST_CHECK(it->m_cf == 3);
1548             BOOST_CHECK(it->m_key.size() == 2u);
1549             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1550             tmp = (p_type3{"y"} + p_type3{"x"}) * 4 * x;
1551             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1552             it = tmp.m_container.begin();
1553             BOOST_CHECK(it->m_cf == 4);
1554             BOOST_CHECK(it->m_key.size() == 2u);
1555             ++it;
1556             BOOST_CHECK(it->m_cf == 4);
1557             BOOST_CHECK(it->m_key.size() == 2u);
1558             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1559             tmp = -2 * x;
1560             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1561             it = tmp.m_container.begin();
1562             BOOST_CHECK(it->m_cf == -2);
1563             BOOST_CHECK(it->m_key.size() == 1u);
1564             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
1565             // Case 3/5 and symmetric.
1566             using p_type4 = g_series_type<g_series_type<int, Expo>, Expo>;
1567             using p_type5 = g_series_type<double, Expo>;
1568             auto tmp2 = p_type4{"x"} * p_type5{"y"} * -1;
1569             BOOST_CHECK_EQUAL(tmp2.size(), 1u);
1570             BOOST_CHECK((std::is_same<decltype(tmp2), g_series_type<g_series_type<double, Expo>, Expo>>::value));
1571             auto it2 = tmp2.m_container.begin();
1572             BOOST_CHECK((it2->m_cf == -g_series_type<double, Expo>{"y"}));
1573             BOOST_CHECK(it2->m_key.size() == 1u);
1574             BOOST_CHECK((tmp2.m_symbol_set == symbol_set{symbol{"x"}}));
1575             tmp2 = p_type5{"y"} * p_type4{"x"} * 2;
1576             BOOST_CHECK_EQUAL(tmp2.size(), 1u);
1577             BOOST_CHECK((std::is_same<decltype(tmp2), g_series_type<g_series_type<double, Expo>, Expo>>::value));
1578             it2 = tmp2.m_container.begin();
1579             BOOST_CHECK((it2->m_cf == 2 * g_series_type<double, Expo>{"y"}));
1580             BOOST_CHECK(it2->m_key.size() == 1u);
1581             BOOST_CHECK((tmp2.m_symbol_set == symbol_set{symbol{"x"}}));
1582             // Now in-place.
1583             // Case 0.
1584             tmp = 2 * x;
1585             tmp *= x;
1586             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1587             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(2));
1588             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1589             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1590             // Move.
1591             tmp = 2 * x;
1592             tmp *= p_type1{x};
1593             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1594             BOOST_CHECK(tmp.m_container.begin()->m_cf == Cf(2));
1595             BOOST_CHECK(tmp.m_container.begin()->m_key.size() == 1u);
1596             BOOST_CHECK(tmp.m_symbol_set == symbol_set{symbol{"x"}});
1597             // Now with merging.
1598             tmp = -3 * x;
1599             tmp *= y;
1600             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1601             it = tmp.m_container.begin();
1602             BOOST_CHECK(it->m_cf == Cf(-3));
1603             BOOST_CHECK(it->m_key.size() == 2u);
1604             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1605             // With moves.
1606             tmp = 4 * x;
1607             tmp *= p_type1{y};
1608             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1609             it = tmp.m_container.begin();
1610             BOOST_CHECK(it->m_cf == Cf(4));
1611             BOOST_CHECK(it->m_key.size() == 2u);
1612             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1613             // Test the swapping of operands when one series is larger than the other.
1614             tmp = 4 * (x + y);
1615             tmp *= x;
1616             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1617             it = tmp.m_container.begin();
1618             BOOST_CHECK(it->m_cf == 4);
1619             BOOST_CHECK(it->m_key.size() == 2u);
1620             ++it;
1621             BOOST_CHECK(it->m_cf == 4);
1622             BOOST_CHECK(it->m_key.size() == 2u);
1623             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1624             tmp = x;
1625             tmp *= 3 * (y + x);
1626             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1627             it = tmp.m_container.begin();
1628             BOOST_CHECK(it->m_cf == 3);
1629             BOOST_CHECK(it->m_key.size() == 2u);
1630             ++it;
1631             BOOST_CHECK(it->m_cf == 3);
1632             BOOST_CHECK(it->m_key.size() == 2u);
1633             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1634             // Some tests for case 1/4.
1635             tmp = 4 * x;
1636             tmp *= p_type3{"y"};
1637             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1638             it = tmp.m_container.begin();
1639             BOOST_CHECK(it->m_cf == 4);
1640             BOOST_CHECK(it->m_key.size() == 2u);
1641             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1642             tmp = x;
1643             tmp *= -4 * (p_type3{"y"} + p_type3{"x"});
1644             BOOST_CHECK_EQUAL(tmp.size(), 2u);
1645             it = tmp.m_container.begin();
1646             BOOST_CHECK(it->m_cf == -4);
1647             BOOST_CHECK(it->m_key.size() == 2u);
1648             ++it;
1649             BOOST_CHECK(it->m_cf == -4);
1650             BOOST_CHECK(it->m_key.size() == 2u);
1651             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1652             tmp = x;
1653             tmp *= 3;
1654             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1655             it = tmp.m_container.begin();
1656             BOOST_CHECK(it->m_cf == 3);
1657             BOOST_CHECK(it->m_key.size() == 1u);
1658             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
1659             // Symmetric of the previous case.
1660             p_type3 tmp3{"y"};
1661             tmp3 *= -4 * x;
1662             BOOST_CHECK_EQUAL(tmp3.size(), 1u);
1663             auto it3 = tmp3.m_container.begin();
1664             BOOST_CHECK(it3->m_cf == -4);
1665             BOOST_CHECK(it3->m_key.size() == 2u);
1666             BOOST_CHECK((tmp3.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1667             tmp3 *= p_type3{"y"} + p_type3{"x"};
1668             tmp3 *= -x;
1669             BOOST_CHECK_EQUAL(tmp3.size(), 2u);
1670             it3 = tmp3.m_container.begin();
1671             BOOST_CHECK(it3->m_cf == 4);
1672             BOOST_CHECK(it3->m_key.size() == 2u);
1673             BOOST_CHECK((tmp3.m_symbol_set == symbol_set{symbol{"x"}, symbol{"y"}}));
1674             // Case 3/5.
1675             auto tmp4 = p_type4{"x"};
1676             tmp4 *= p_type5{"y"} * 3;
1677             BOOST_CHECK_EQUAL(tmp4.size(), 1u);
1678             auto it4 = tmp4.m_container.begin();
1679             BOOST_CHECK((std::is_same<decltype(it4->m_cf), g_series_type<int, Expo>>::value));
1680             BOOST_CHECK((it4->m_cf == 3 * g_series_type<int, Expo>{"y"}));
1681             BOOST_CHECK(it4->m_key.size() == 1u);
1682             BOOST_CHECK((tmp4.m_symbol_set == symbol_set{symbol{"x"}}));
1683             // Check with scalar on the left.
1684             BOOST_CHECK((!is_multipliable_in_place<int, p_type1>::value));
1685             BOOST_CHECK((!is_multipliable_in_place<int, p_type2>::value));
1686             BOOST_CHECK((!is_multipliable_in_place<int, p_type3>::value));
1687         }
1688     };
1689     template <typename Cf>
operator ()(const Cf &) const1690     void operator()(const Cf &) const
1691     {
1692         tuple_for_each(expo_types{}, runner<Cf>());
1693     }
1694 };
1695 }
1696 
1697 typedef debug_access<arithmetics_mul_tag> arithmetics_mul_tester;
1698 
BOOST_AUTO_TEST_CASE(series_arithmetics_mul_test)1699 BOOST_AUTO_TEST_CASE(series_arithmetics_mul_test)
1700 {
1701     // Functional testing.
1702     tuple_for_each(cf_types{}, arithmetics_mul_tester());
1703     // Type testing for binary multiplication.
1704     typedef g_series_type<rational, int> p_type1;
1705     typedef g_series_type<int, rational> p_type2;
1706     typedef g_series_type<short, rational> p_type3;
1707     typedef g_series_type<char, rational> p_type4;
1708     // First let's check the output type.
1709     // Case 0.
1710     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} * p_type1{})>::value));
1711     // Case 1.
1712     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} * p_type2{})>::value));
1713     // Case 2.
1714     BOOST_CHECK((std::is_same<p_type1, decltype(p_type2{} * p_type1{})>::value));
1715     // Case 3, symmetric.
1716     BOOST_CHECK((std::is_same<p_type2, decltype(p_type3{} * p_type4{})>::value));
1717     BOOST_CHECK((std::is_same<p_type2, decltype(p_type4{} * p_type3{})>::value));
1718     // Case 4.
1719     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} * 0)>::value));
1720     // Case 5.
1721     BOOST_CHECK((std::is_same<p_type2, decltype(p_type3{} * 0)>::value));
1722     // Case 6.
1723     BOOST_CHECK((std::is_same<p_type1, decltype(0 * p_type1{})>::value));
1724     // Case 7.
1725     BOOST_CHECK((std::is_same<p_type2, decltype(0 * p_type3{})>::value));
1726     // Check non-multipliable series.
1727     typedef g_series_type2<rational, int> p_type5;
1728     BOOST_CHECK((!is_multipliable<p_type1, p_type5>::value));
1729     BOOST_CHECK((!is_multipliable<p_type5, p_type1>::value));
1730     // Check coefficient series.
1731     typedef g_series_type<p_type1, int> p_type11;
1732     typedef g_series_type<p_type2, rational> p_type22;
1733     typedef g_series_type<p_type1, rational> p_type21;
1734     BOOST_CHECK((std::is_same<p_type11, decltype(p_type1{} * p_type11{})>::value));
1735     BOOST_CHECK((std::is_same<p_type11, decltype(p_type11{} * p_type1{})>::value));
1736     BOOST_CHECK((std::is_same<p_type21, decltype(p_type1{} * p_type22{})>::value));
1737     BOOST_CHECK((std::is_same<p_type21, decltype(p_type22{} * p_type1{})>::value));
1738     BOOST_CHECK((std::is_same<p_type11, decltype(p_type11{} * p_type22{})>::value));
1739     BOOST_CHECK((std::is_same<p_type11, decltype(p_type22{} * p_type11{})>::value));
1740     // Type testing for in-place multiplication.
1741     // Case 0.
1742     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() *= p_type1{})>::value));
1743     // Case 1.
1744     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() *= p_type2{})>::value));
1745     // Case 2.
1746     BOOST_CHECK((std::is_same<p_type2 &, decltype(std::declval<p_type2 &>() *= p_type1{})>::value));
1747     // Case 3, symmetric.
1748     BOOST_CHECK((std::is_same<p_type3 &, decltype(std::declval<p_type3 &>() *= p_type4{})>::value));
1749     BOOST_CHECK((std::is_same<p_type4 &, decltype(std::declval<p_type4 &>() *= p_type3{})>::value));
1750     // Case 4.
1751     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() *= 0)>::value));
1752     // Case 5.
1753     BOOST_CHECK((std::is_same<p_type3 &, decltype(std::declval<p_type3 &>() *= 0)>::value));
1754     // Cases 6 and 7 do not make sense at the moment.
1755     BOOST_CHECK((!is_multipliable_in_place<int, p_type3>::value));
1756     BOOST_CHECK((!is_multipliable_in_place<p_type1, p_type11>::value));
1757     // Checks for coefficient series.
1758     p_type11 tmp;
1759     BOOST_CHECK((std::is_same<p_type11 &, decltype(tmp *= p_type1{})>::value));
1760     p_type22 tmp2;
1761     BOOST_CHECK((std::is_same<p_type22 &, decltype(tmp2 *= p_type1{})>::value));
1762 }
1763 
1764 struct arithmetics_div_tag {
1765 };
1766 
1767 namespace piranha
1768 {
1769 template <>
1770 class debug_access<arithmetics_div_tag>
1771 {
1772 public:
1773     template <typename Cf>
1774     struct runner {
1775         template <typename Expo>
operator ()piranha::debug_access::runner1776         void operator()(const Expo &) const
1777         {
1778             typedef g_series_type<Cf, Expo> p_type1;
1779             p_type1 x{"x"};
1780             // Some tests for case 4.
1781             auto tmp = 3 * x / 2;
1782             BOOST_CHECK_EQUAL(tmp.size(), 1u);
1783             auto it = tmp.m_container.begin();
1784             BOOST_CHECK(it->m_cf == Cf(3) / 2);
1785             BOOST_CHECK(it->m_key.size() == 1u);
1786             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
1787             // Case 5.
1788             auto tmp2 = 3 * x / 2.;
1789             auto it2 = tmp2.m_container.begin();
1790             BOOST_CHECK(it2->m_cf == Cf(3) / 2.);
1791             BOOST_CHECK(it2->m_key.size() == 1u);
1792             BOOST_CHECK((tmp2.m_symbol_set == symbol_set{symbol{"x"}}));
1793             // In-place.
1794             // Case 4.
1795             tmp = 3 * x;
1796             tmp /= 2;
1797             it = tmp.m_container.begin();
1798             BOOST_CHECK(it->m_cf == Cf(3) / 2);
1799             BOOST_CHECK(it->m_key.size() == 1u);
1800             BOOST_CHECK((tmp.m_symbol_set == symbol_set{symbol{"x"}}));
1801             // Case 5.
1802             tmp2 = 3 * x;
1803             tmp2 /= 2.;
1804             it2 = tmp2.m_container.begin();
1805             BOOST_CHECK(it2->m_cf == Cf(3) / 2.);
1806             BOOST_CHECK(it2->m_key.size() == 1u);
1807             BOOST_CHECK((tmp2.m_symbol_set == symbol_set{symbol{"x"}}));
1808             // Test division by zero of empty series.
1809             if (std::is_same<integer, Cf>::value || std::is_same<rational, Cf>::value) {
1810                 BOOST_CHECK_THROW(p_type1{} / 0, zero_division_error);
1811                 p_type1 zero;
1812                 BOOST_CHECK_THROW(zero /= 0, zero_division_error);
1813             }
1814             // Check with scalar on the left.
1815             BOOST_CHECK((!is_divisible_in_place<int, p_type1>::value));
1816         }
1817     };
1818     template <typename Cf>
operator ()(const Cf &) const1819     void operator()(const Cf &) const
1820     {
1821         tuple_for_each(expo_types{}, runner<Cf>());
1822     }
1823 };
1824 }
1825 
1826 typedef debug_access<arithmetics_div_tag> arithmetics_div_tester;
1827 
BOOST_AUTO_TEST_CASE(series_arithmetics_div_test)1828 BOOST_AUTO_TEST_CASE(series_arithmetics_div_test)
1829 {
1830     // Functional testing.
1831     tuple_for_each(cf_types{}, arithmetics_div_tester());
1832     // Type testing for binary division.
1833     typedef g_series_type<rational, int> p_type1;
1834     typedef g_series_type<p_type1, int> p_type11;
1835     typedef g_series_type<double, int> p_type1d;
1836     typedef g_series_type<float, int> p_type1f;
1837     // First let's check the output type.
1838     // Case 4.
1839     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} / 0)>::value));
1840     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} / integer{})>::value));
1841     BOOST_CHECK((std::is_same<p_type1, decltype(p_type1{} / rational{})>::value));
1842     // Case 5.
1843     BOOST_CHECK((std::is_same<p_type1d, decltype(p_type1{} / 0.)>::value));
1844     BOOST_CHECK((std::is_same<p_type1f, decltype(p_type1{} / 0.f)>::value));
1845     // Some scalars on the first argument.
1846     BOOST_CHECK((is_divisible<double, p_type1>::value));
1847     BOOST_CHECK((std::is_same<decltype(3. / p_type1{}), g_series_type<double, int>>::value));
1848     BOOST_CHECK((is_divisible<int, p_type1>::value));
1849     BOOST_CHECK((std::is_same<decltype(3 / p_type1{}), p_type1>::value));
1850     BOOST_CHECK((is_divisible<integer, p_type1>::value));
1851     BOOST_CHECK((std::is_same<decltype(3_z / p_type1{}), p_type1>::value));
1852     // Type testing for in-place division.
1853     // Case 4.
1854     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() /= 0)>::value));
1855     // Case 5.
1856     BOOST_CHECK((std::is_same<p_type1 &, decltype(std::declval<p_type1 &>() /= 0.)>::value));
1857     // Not divisible in-place.
1858     BOOST_CHECK((!is_divisible_in_place<int, p_type1>::value));
1859     // Divisible in-place after recent changes.
1860     BOOST_CHECK((is_divisible_in_place<p_type11, p_type1>::value));
1861     // Special cases to test the erasing of terms.
1862     using pint = g_series_type<integer, int>;
1863     pint x{"x"}, y{"y"};
1864     auto tmp = 2 * x + y;
1865     tmp /= 2;
1866     BOOST_CHECK_EQUAL(tmp, x);
1867     tmp = 2 * x + 2 * y;
1868     tmp /= 3;
1869     BOOST_CHECK(tmp.empty());
1870     // Check zero division error.
1871     tmp = 2 * x + y;
1872     BOOST_CHECK_THROW(tmp /= 0, zero_division_error);
1873     BOOST_CHECK(tmp.empty());
1874 }
1875 
1876 struct eq_tag {
1877 };
1878 
1879 namespace piranha
1880 {
1881 template <>
1882 class debug_access<eq_tag>
1883 {
1884 public:
1885     template <typename Cf>
1886     struct runner {
1887         template <typename Expo>
operator ()piranha::debug_access::runner1888         void operator()(const Expo &) const
1889         {
1890             typedef g_series_type<Cf, Expo> p_type1;
1891             typedef g_series_type2<Cf, Expo> p_type2;
1892             typedef g_series_type<int, Expo> p_type3;
1893             // Some type checks - these are not comparable as they result in an ambiguity
1894             // between two series with same coefficient but different series types.
1895             BOOST_CHECK((!is_equality_comparable<p_type1, p_type2>::value));
1896             BOOST_CHECK((!is_equality_comparable<p_type2, p_type1>::value));
1897             BOOST_CHECK((!is_equality_comparable<p_type1, p_type2>::value));
1898             BOOST_CHECK((!is_equality_comparable<p_type2, p_type1>::value));
1899             // Various subcases of case 0.
1900             p_type1 x{"x"}, y{"y"};
1901             BOOST_CHECK_EQUAL(x, x);
1902             BOOST_CHECK_EQUAL(y, y);
1903             BOOST_CHECK_EQUAL(x, x + y - y);
1904             BOOST_CHECK_EQUAL(y, y + x - x);
1905             // Arguments merging on both sides.
1906             BOOST_CHECK(x != y);
1907             // Check with series of different size.
1908             BOOST_CHECK(x != y + x);
1909             // Arguments merging on the other side.
1910             BOOST_CHECK(y + x != y);
1911             // Some tests for case 1/4.
1912             BOOST_CHECK(x != p_type3{"y"});
1913             BOOST_CHECK(y != p_type3{"x"});
1914             BOOST_CHECK(x != p_type3{"y"} + p_type3{"x"});
1915             BOOST_CHECK(y != p_type3{"x"} + p_type3{"y"});
1916             BOOST_CHECK_EQUAL(x, p_type3{"x"});
1917             BOOST_CHECK_EQUAL(x, p_type3{"x"} + p_type3{"y"} - p_type3{"y"});
1918             BOOST_CHECK(x != 0);
1919             BOOST_CHECK(y != 0);
1920             BOOST_CHECK_EQUAL(x - x, 0);
1921             BOOST_CHECK_EQUAL(p_type1{1}, 1);
1922             BOOST_CHECK_EQUAL(p_type1{-1}, -1);
1923             // Symmetric of above.
1924             BOOST_CHECK(p_type3{"y"} != x);
1925             BOOST_CHECK(p_type3{"x"} != y);
1926             BOOST_CHECK(p_type3{"y"} + p_type3{"x"} != x);
1927             BOOST_CHECK(p_type3{"x"} + p_type3{"y"} != y);
1928             BOOST_CHECK_EQUAL(p_type3{"x"}, x);
1929             BOOST_CHECK_EQUAL(p_type3{"x"} + p_type3{"y"} - p_type3{"y"}, x);
1930             BOOST_CHECK(0 != x);
1931             BOOST_CHECK(0 != y);
1932             BOOST_CHECK_EQUAL(0, x - x);
1933             BOOST_CHECK_EQUAL(1, p_type1{1});
1934             BOOST_CHECK_EQUAL(-1, p_type1{-1});
1935             // Case 3/5 and symmetric.
1936             using p_type4 = g_series_type<g_series_type<int, Expo>, Expo>;
1937             using p_type5 = g_series_type<double, Expo>;
1938             BOOST_CHECK_EQUAL((p_type4{g_series_type<int, Expo>{"x"}}), p_type5{"x"});
1939             BOOST_CHECK_EQUAL(p_type5{"x"}, (p_type4{g_series_type<int, Expo>{"x"}}));
1940             BOOST_CHECK((p_type4{g_series_type<int, Expo>{"x"}} != p_type5{"y"}));
1941             BOOST_CHECK((p_type5{"y"} != p_type4{g_series_type<int, Expo>{"x"}}));
1942         }
1943     };
1944     template <typename Cf>
operator ()(const Cf &) const1945     void operator()(const Cf &) const
1946     {
1947         tuple_for_each(expo_types{}, runner<Cf>());
1948     }
1949 };
1950 }
1951 
1952 typedef debug_access<eq_tag> eq_tester;
1953 
BOOST_AUTO_TEST_CASE(series_eq_test)1954 BOOST_AUTO_TEST_CASE(series_eq_test)
1955 {
1956     // Functional testing.
1957     tuple_for_each(cf_types{}, eq_tester());
1958     // Type testing for binary addition.
1959     typedef g_series_type<rational, int> p_type1;
1960     typedef g_series_type<int, rational> p_type2;
1961     typedef g_series_type<short, rational> p_type3;
1962     typedef g_series_type<char, rational> p_type4;
1963     // First let's check the output type.
1964     // Case 0.
1965     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} == p_type1{})>::value));
1966     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} != p_type1{})>::value));
1967     // Case 1.
1968     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} == p_type2{})>::value));
1969     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} != p_type2{})>::value));
1970     // Case 2.
1971     BOOST_CHECK((std::is_same<bool, decltype(p_type2{} == p_type1{})>::value));
1972     BOOST_CHECK((std::is_same<bool, decltype(p_type2{} != p_type1{})>::value));
1973     // Case 3, symmetric.
1974     BOOST_CHECK((std::is_same<bool, decltype(p_type3{} == p_type4{})>::value));
1975     BOOST_CHECK((std::is_same<bool, decltype(p_type3{} != p_type4{})>::value));
1976     BOOST_CHECK((std::is_same<bool, decltype(p_type4{} == p_type3{})>::value));
1977     BOOST_CHECK((std::is_same<bool, decltype(p_type4{} != p_type3{})>::value));
1978     // Case 4.
1979     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} == 0)>::value));
1980     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} != 0)>::value));
1981     // Case 5.
1982     BOOST_CHECK((std::is_same<bool, decltype(p_type3{} == 0)>::value));
1983     BOOST_CHECK((std::is_same<bool, decltype(p_type3{} != 0)>::value));
1984     // Case 6.
1985     BOOST_CHECK((std::is_same<bool, decltype(0 == p_type1{})>::value));
1986     BOOST_CHECK((std::is_same<bool, decltype(0 != p_type1{})>::value));
1987     // Case 7.
1988     BOOST_CHECK((std::is_same<bool, decltype(0 == p_type3{})>::value));
1989     BOOST_CHECK((std::is_same<bool, decltype(0 != p_type3{})>::value));
1990     // Check non-addable series.
1991     typedef g_series_type2<rational, int> p_type5;
1992     BOOST_CHECK((!is_equality_comparable<p_type1, p_type5>::value));
1993     BOOST_CHECK((!is_equality_comparable<p_type5, p_type1>::value));
1994     // Check coefficient series.
1995     typedef g_series_type<p_type1, int> p_type11;
1996     typedef g_series_type<p_type2, rational> p_type22;
1997     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} == p_type11{})>::value));
1998     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} != p_type11{})>::value));
1999     BOOST_CHECK((std::is_same<bool, decltype(p_type11{} == p_type1{})>::value));
2000     BOOST_CHECK((std::is_same<bool, decltype(p_type11{} != p_type1{})>::value));
2001     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} == p_type22{})>::value));
2002     BOOST_CHECK((std::is_same<bool, decltype(p_type1{} != p_type22{})>::value));
2003     BOOST_CHECK((std::is_same<bool, decltype(p_type22{} == p_type1{})>::value));
2004     BOOST_CHECK((std::is_same<bool, decltype(p_type22{} != p_type1{})>::value));
2005     BOOST_CHECK((std::is_same<bool, decltype(p_type11{} == p_type22{})>::value));
2006     BOOST_CHECK((std::is_same<bool, decltype(p_type11{} != p_type22{})>::value));
2007     BOOST_CHECK((std::is_same<bool, decltype(p_type22{} == p_type11{})>::value));
2008     BOOST_CHECK((std::is_same<bool, decltype(p_type22{} != p_type11{})>::value));
2009 }
2010 
BOOST_AUTO_TEST_CASE(series_hash_test)2011 BOOST_AUTO_TEST_CASE(series_hash_test)
2012 {
2013     typedef g_series_type<rational, int> p_type1;
2014     typedef g_series_type<integer, int> p_type2;
2015     BOOST_CHECK_EQUAL(p_type1{}.hash(), 0u);
2016     BOOST_CHECK_EQUAL(p_type2{}.hash(), 0u);
2017     // Check that only the key is used to compute the hash.
2018     BOOST_CHECK_EQUAL(p_type1{"x"}.hash(), p_type2{"x"}.hash());
2019     auto x = p_type1{"x"}, y = p_type1{"y"}, x2 = (x + y) - y;
2020     // NOTE: this is not 100% sure as the hash mixing in the monomial could actually lead to identical hashes.
2021     // But the probability should be rather low.
2022     BOOST_CHECK(x.hash() != x2.hash());
2023     // This shows we cannot use standard equality operator in hash tables.
2024     BOOST_CHECK_EQUAL(x, x2);
2025     // A bit more testing.
2026     BOOST_CHECK_EQUAL((x + 2 * y).hash(), (x + y + y).hash());
2027     BOOST_CHECK_EQUAL((x + 2 * y - y).hash(), (x + y).hash());
2028 }
2029 
BOOST_AUTO_TEST_CASE(series_is_identical_test)2030 BOOST_AUTO_TEST_CASE(series_is_identical_test)
2031 {
2032     typedef g_series_type<rational, int> p_type1;
2033     BOOST_CHECK(p_type1{}.is_identical(p_type1{}));
2034     auto x = p_type1{"x"}, y = p_type1{"y"}, x2 = (x + y) - y;
2035     BOOST_CHECK(x.is_identical(x));
2036     BOOST_CHECK(x.is_identical(p_type1{"x"}));
2037     BOOST_CHECK(!x.is_identical(y));
2038     BOOST_CHECK(!y.is_identical(x));
2039     BOOST_CHECK_EQUAL(x2, x);
2040     BOOST_CHECK(!x2.is_identical(x));
2041     BOOST_CHECK(!x.is_identical(x2));
2042     BOOST_CHECK(x.is_identical(x2.trim()));
2043     BOOST_CHECK(x2.trim().is_identical(x));
2044 }
2045 
2046 // Mock cf with wrong specialisation of mul3.
2047 struct mock_cf3 {
2048     mock_cf3();
2049     explicit mock_cf3(const int &);
2050     mock_cf3(const mock_cf3 &);
2051     mock_cf3(mock_cf3 &&) noexcept;
2052     mock_cf3 &operator=(const mock_cf3 &);
2053     mock_cf3 &operator=(mock_cf3 &&) noexcept;
2054     friend std::ostream &operator<<(std::ostream &, const mock_cf3 &);
2055     mock_cf3 operator-() const;
2056     bool operator==(const mock_cf3 &) const;
2057     bool operator!=(const mock_cf3 &) const;
2058     mock_cf3 &operator+=(const mock_cf3 &);
2059     mock_cf3 &operator-=(const mock_cf3 &);
2060     mock_cf3 operator+(const mock_cf3 &) const;
2061     mock_cf3 operator-(const mock_cf3 &) const;
2062     mock_cf3 &operator*=(const mock_cf3 &);
2063     mock_cf3 operator*(const mock_cf3 &)const;
2064 };
2065 
2066 namespace piranha
2067 {
2068 namespace math
2069 {
2070 
2071 template <typename T>
2072 struct mul3_impl<T, typename std::enable_if<std::is_same<T, mock_cf3>::value>::type> {
2073 };
2074 }
2075 }
2076 
BOOST_AUTO_TEST_CASE(series_has_series_multiplier_test)2077 BOOST_AUTO_TEST_CASE(series_has_series_multiplier_test)
2078 {
2079     typedef g_series_type<rational, int> p_type1;
2080     BOOST_CHECK(series_has_multiplier<p_type1>::value);
2081     BOOST_CHECK(series_has_multiplier<p_type1 &>::value);
2082     BOOST_CHECK(series_has_multiplier<const p_type1 &>::value);
2083     typedef g_series_type<mock_cf3, int> p_type2;
2084     BOOST_CHECK(!series_has_multiplier<p_type2>::value);
2085     BOOST_CHECK(!series_has_multiplier<p_type2 const>::value);
2086     BOOST_CHECK(!series_has_multiplier<p_type2 const &>::value);
2087     typedef g_series_type3<double, mock_key> p_type3;
2088     BOOST_CHECK(!series_has_multiplier<p_type3>::value);
2089     BOOST_CHECK(!series_has_multiplier<p_type3 &>::value);
2090     BOOST_CHECK(!series_has_multiplier<p_type3 &&>::value);
2091 }
2092 
2093 // A non-multipliable series, missing a suitable series_multiplier specialisation.
2094 template <typename Cf, typename Expo>
2095 class g_series_type_nm : public series<Cf, monomial<Expo>, g_series_type_nm<Cf, Expo>>
2096 {
2097     typedef series<Cf, monomial<Expo>, g_series_type_nm<Cf, Expo>> base;
2098 
2099 public:
2100     template <typename Cf2>
2101     using rebind = g_series_type_nm<Cf2, Expo>;
2102     g_series_type_nm() = default;
2103     g_series_type_nm(const g_series_type_nm &) = default;
2104     g_series_type_nm(g_series_type_nm &&) = default;
g_series_type_nm(const char * name)2105     explicit g_series_type_nm(const char *name) : base()
2106     {
2107         typedef typename base::term_type term_type;
2108         // Insert the symbol.
2109         this->m_symbol_set.add(name);
2110         // Construct and insert the term.
2111         this->insert(term_type(Cf(1), typename term_type::key_type{Expo(1)}));
2112     }
2113     g_series_type_nm &operator=(const g_series_type_nm &) = default;
2114     g_series_type_nm &operator=(g_series_type_nm &&) = default;
2115     PIRANHA_FORWARDING_CTOR(g_series_type_nm, base)
2116     PIRANHA_FORWARDING_ASSIGNMENT(g_series_type_nm, base)
2117 };
2118 
2119 namespace piranha
2120 {
2121 
2122 template <typename Cf, typename Expo>
2123 class series_multiplier<g_series_type_nm<Cf, Expo>, void>
2124 {
2125 };
2126 }
2127 
BOOST_AUTO_TEST_CASE(series_no_series_multiplier_test)2128 BOOST_AUTO_TEST_CASE(series_no_series_multiplier_test)
2129 {
2130     typedef g_series_type_nm<rational, int> p_type1;
2131     BOOST_CHECK(!is_multipliable<p_type1>::value);
2132 }
2133 
2134 // Mock coefficient, with weird semantics for operator+(integer): the output is not a coefficient type.
2135 struct mock_cf2 {
2136     mock_cf2();
2137     explicit mock_cf2(const int &);
2138     mock_cf2(const mock_cf2 &);
2139     mock_cf2(mock_cf2 &&) noexcept;
2140     mock_cf2 &operator=(const mock_cf2 &);
2141     mock_cf2 &operator=(mock_cf2 &&) noexcept;
2142     friend std::ostream &operator<<(std::ostream &, const mock_cf2 &);
2143     mock_cf2 operator-() const;
2144     bool operator==(const mock_cf2 &) const;
2145     bool operator!=(const mock_cf2 &) const;
2146     mock_cf2 &operator+=(const mock_cf2 &);
2147     mock_cf2 &operator-=(const mock_cf2 &);
2148     mock_cf2 operator+(const mock_cf2 &) const;
2149     mock_cf2 operator-(const mock_cf2 &) const;
2150     mock_cf2 &operator*=(const mock_cf2 &);
2151     mock_cf2 operator*(const mock_cf2 &)const;
2152     std::string operator+(const integer &) const;
2153     std::vector<std::string> operator*(const integer &)const;
2154     std::vector<std::string> operator-(const integer &) const;
2155 };
2156 
2157 // Check that attempting to rebind to an invalid coefficient disables the operator, rather
2158 // than resulting in a static assertion firing (as it was the case in the past).
BOOST_AUTO_TEST_CASE(series_rebind_failure_test)2159 BOOST_AUTO_TEST_CASE(series_rebind_failure_test)
2160 {
2161     BOOST_CHECK(is_cf<mock_cf2>::value);
2162     BOOST_CHECK((!is_addable<g_series_type<integer, int>, g_series_type<mock_cf2, int>>::value));
2163     BOOST_CHECK((!is_addable<g_series_type<mock_cf2, int>, g_series_type<integer, int>>::value));
2164     BOOST_CHECK((is_addable<g_series_type<mock_cf2, int>, g_series_type<mock_cf2, int>>::value));
2165     BOOST_CHECK((!is_subtractable<g_series_type<integer, int>, g_series_type<mock_cf2, int>>::value));
2166     BOOST_CHECK((!is_subtractable<g_series_type<mock_cf2, int>, g_series_type<integer, int>>::value));
2167     BOOST_CHECK((is_subtractable<g_series_type<mock_cf2, int>, g_series_type<mock_cf2, int>>::value));
2168     BOOST_CHECK((!is_multipliable<g_series_type<integer, int>, g_series_type<mock_cf2, int>>::value));
2169     BOOST_CHECK((!is_multipliable<g_series_type<mock_cf2, int>, g_series_type<integer, int>>::value));
2170     BOOST_CHECK((is_multipliable<g_series_type<mock_cf2, int>, g_series_type<mock_cf2, int>>::value));
2171 }
2172