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