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/monomial.hpp"
30
31 #define BOOST_TEST_MODULE monomial_02_test
32 #include <boost/test/included/unit_test.hpp>
33
34 #include <boost/algorithm/string/predicate.hpp>
35 #include <cstddef>
36 #include <functional>
37 #include <initializer_list>
38 #include <iostream>
39 #include <random>
40 #include <sstream>
41 #include <stdexcept>
42 #include <string>
43 #include <tuple>
44 #include <type_traits>
45 #include <vector>
46
47 #include "../src/config.hpp"
48 #include "../src/init.hpp"
49 #include "../src/is_key.hpp"
50 #include "../src/mp_integer.hpp"
51 #include "../src/mp_rational.hpp"
52 #include "../src/s11n.hpp"
53 #include "../src/symbol.hpp"
54 #include "../src/symbol_set.hpp"
55 #include "../src/type_traits.hpp"
56
57 using namespace piranha;
58
59 using expo_types = std::tuple<signed char, int, integer, rational>;
60 using size_types = std::tuple<std::integral_constant<std::size_t, 0u>, std::integral_constant<std::size_t, 1u>,
61 std::integral_constant<std::size_t, 5u>, std::integral_constant<std::size_t, 10u>>;
62
63 static const int ntrials = 100;
64
65 static std::mt19937 rng;
66
67 // This is missing the s11n methods.
68 struct fake_int_01 {
69 fake_int_01();
70 explicit fake_int_01(int);
71 fake_int_01(const fake_int_01 &);
72 fake_int_01(fake_int_01 &&) noexcept;
73 fake_int_01 &operator=(const fake_int_01 &);
74 fake_int_01 &operator=(fake_int_01 &&) noexcept;
75 ~fake_int_01();
76 bool operator==(const fake_int_01 &) const;
77 bool operator!=(const fake_int_01 &) const;
78 bool operator<(const fake_int_01 &) const;
79 fake_int_01 operator+(const fake_int_01 &) const;
80 fake_int_01 &operator+=(const fake_int_01 &);
81 fake_int_01 operator-(const fake_int_01 &) const;
82 fake_int_01 &operator-=(const fake_int_01 &);
83 friend std::ostream &operator<<(std::ostream &, const fake_int_01 &);
84 };
85
86 namespace std
87 {
88
89 template <>
90 struct hash<fake_int_01> {
91 typedef size_t result_type;
92 typedef fake_int_01 argument_type;
93 result_type operator()(const argument_type &) const;
94 };
95 }
96
97 namespace piranha
98 {
99
100 namespace math
101 {
102
103 template <>
104 struct negate_impl<fake_int_01> {
105 void operator()(fake_int_01 &) const;
106 };
107 }
108 }
109
110 template <typename OArchive, typename IArchive, typename Monomial>
boost_round_trip_monomial(const Monomial & m,const symbol_set & s)111 static inline Monomial boost_round_trip_monomial(const Monomial &m, const symbol_set &s)
112 {
113 using w_type = boost_s11n_key_wrapper<Monomial>;
114 std::stringstream ss;
115 {
116 OArchive oa(ss);
117 boost_save(oa, w_type{m, s});
118 }
119 Monomial n;
120 {
121 IArchive ia(ss);
122 w_type w{n, s};
123 boost_load(ia, w);
124 }
125 return n;
126 }
127
128 struct boost_s11n_tester {
129 template <typename T>
130 struct runner {
131 template <typename U>
operator ()boost_s11n_tester::runner132 void operator()(const U &) const
133 {
134 using monomial_type = monomial<T, U>;
135 using w_type = boost_s11n_key_wrapper<monomial_type>;
136 // Test the type traits.
137 BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, w_type>::value));
138 BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, w_type &>::value));
139 BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, const w_type &>::value));
140 BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, const w_type>::value));
141 BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, w_type>::value));
142 BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, w_type &>::value));
143 BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, w_type &&>::value));
144 BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive, const w_type &>::value));
145 BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive, const w_type>::value));
146 BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, w_type>::value));
147 BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, w_type &>::value));
148 BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive &, w_type &&>::value));
149 BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive &, w_type>::value));
150 BOOST_CHECK((!has_boost_save<boost::archive::binary_iarchive, w_type>::value));
151 BOOST_CHECK((!has_boost_load<boost::archive::binary_oarchive, w_type>::value));
152 BOOST_CHECK((!has_boost_save<const boost::archive::binary_oarchive, w_type>::value));
153 BOOST_CHECK((!has_boost_load<const boost::archive::binary_iarchive, w_type>::value));
154 BOOST_CHECK((!has_boost_save<const boost::archive::binary_oarchive &, w_type>::value));
155 BOOST_CHECK((!has_boost_load<const boost::archive::binary_iarchive &, w_type>::value));
156 BOOST_CHECK((!has_boost_save<int, w_type>::value));
157 BOOST_CHECK((!has_boost_load<int, w_type>::value));
158 // Check exceptions.
159 symbol_set s{symbol{"a"}};
160 monomial_type m;
161 std::stringstream ss;
162 {
163 boost::archive::text_oarchive oa(ss);
164 BOOST_CHECK_EXCEPTION(
165 boost_save(oa, w_type{m, s}), std::invalid_argument, [](const std::invalid_argument &ia) {
166 return boost::contains(
167 ia.what(),
168 "incompatible symbol set in monomial serialization: the reference "
169 "symbol set has a size of 1, while the monomial being serialized has a size of 0");
170 });
171 }
172 ss.str("");
173 ss.clear();
174 m = monomial_type{T(1)};
175 {
176 boost::archive::text_oarchive oa(ss);
177 boost_save(oa, w_type{m, s});
178 }
179 {
180 boost::archive::text_iarchive ia(ss);
181 symbol_set s2;
182 w_type w{m, s2};
183 BOOST_CHECK_EXCEPTION(boost_load(ia, w), std::invalid_argument, [](const std::invalid_argument &iae) {
184 return boost::contains(
185 iae.what(),
186 "incompatible symbol set in monomial serialization: the reference "
187 "symbol set has a size of 0, while the monomial being deserialized has a size of 1");
188 });
189 }
190 // A few simple tests.
191 m = monomial_type{};
192 auto n = boost_round_trip_monomial<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(
193 m, symbol_set{});
194 BOOST_CHECK(n == m);
195 n = boost_round_trip_monomial<boost::archive::text_oarchive, boost::archive::text_iarchive>(m,
196 symbol_set{});
197 BOOST_CHECK(n == m);
198 std::vector<T> vexpo = {T(1), T(2), T(3)};
199 m = monomial_type(vexpo.begin(), vexpo.end());
200 n = boost_round_trip_monomial<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(
201 m, symbol_set{symbol{"a"}, symbol{"b"}, symbol{"c"}});
202 BOOST_CHECK(n == m);
203 n = boost_round_trip_monomial<boost::archive::text_oarchive, boost::archive::text_iarchive>(
204 m, symbol_set{symbol{"a"}, symbol{"b"}, symbol{"c"}});
205 BOOST_CHECK(n == m);
206 // Random testing.
207 random_test<U>();
208 }
209 template <typename U, typename V = T, typename std::enable_if<std::is_same<V, integer>::value, int>::type = 0>
random_testboost_s11n_tester::runner210 void random_test() const
211 {
212 using monomial_type = monomial<T, U>;
213 using size_type = typename monomial_type::size_type;
214 std::uniform_int_distribution<size_type> sdist(0u, 10u);
215 std::uniform_int_distribution<int> edist(-10, 10);
216 const std::vector<std::string> vs = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
217 for (auto i = 0; i < ntrials; ++i) {
218 const auto size = sdist(rng);
219 std::vector<T> tmp;
220 for (size_type j = 0; j < size; ++j) {
221 tmp.emplace_back(edist(rng));
222 }
223 monomial_type m(tmp.begin(), tmp.end());
224 symbol_set ss(vs.begin(), vs.begin() + size);
225 BOOST_CHECK(
226 (m == boost_round_trip_monomial<boost::archive::text_oarchive, boost::archive::text_iarchive>(m,
227 ss)));
228 BOOST_CHECK(
229 (m == boost_round_trip_monomial<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(
230 m, ss)));
231 }
232 }
233 template <typename U, typename V = T, typename std::enable_if<std::is_same<V, rational>::value, int>::type = 0>
random_testboost_s11n_tester::runner234 void random_test() const
235 {
236 using monomial_type = monomial<T, U>;
237 using size_type = typename monomial_type::size_type;
238 std::uniform_int_distribution<size_type> sdist(0u, 10u);
239 std::uniform_int_distribution<int> edist(-10, 10);
240 const std::vector<std::string> vs = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
241 for (auto i = 0; i < ntrials; ++i) {
242 const auto size = sdist(rng);
243 std::vector<T> tmp;
244 for (size_type j = 0; j < size; ++j) {
245 int num = edist(rng), den = edist(rng);
246 if (!den) {
247 den = 1;
248 }
249 tmp.emplace_back(num, den);
250 }
251 monomial_type m(tmp.begin(), tmp.end());
252 symbol_set ss(vs.begin(), vs.begin() + size);
253 BOOST_CHECK(
254 (m == boost_round_trip_monomial<boost::archive::text_oarchive, boost::archive::text_iarchive>(m,
255 ss)));
256 BOOST_CHECK(
257 (m == boost_round_trip_monomial<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(
258 m, ss)));
259 }
260 }
261 template <typename U, typename V = T, typename std::enable_if<std::is_integral<V>::value, int>::type = 0>
random_testboost_s11n_tester::runner262 void random_test() const
263 {
264 using monomial_type = monomial<T, U>;
265 using size_type = typename monomial_type::size_type;
266 std::uniform_int_distribution<size_type> sdist(0u, 10u);
267 std::uniform_int_distribution<T> edist(-10, 10);
268 const std::vector<std::string> vs = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
269 for (auto i = 0; i < ntrials; ++i) {
270 const auto size = sdist(rng);
271 std::vector<T> tmp;
272 for (size_type j = 0; j < size; ++j) {
273 tmp.push_back(edist(rng));
274 }
275 monomial_type m(tmp.begin(), tmp.end());
276 symbol_set ss(vs.begin(), vs.begin() + size);
277 BOOST_CHECK(
278 (m == boost_round_trip_monomial<boost::archive::text_oarchive, boost::archive::text_iarchive>(m,
279 ss)));
280 BOOST_CHECK(
281 (m == boost_round_trip_monomial<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(
282 m, ss)));
283 }
284 }
285 };
286 template <typename T>
operator ()boost_s11n_tester287 void operator()(const T &) const
288 {
289 tuple_for_each(size_types{}, runner<T>());
290 }
291 };
292
BOOST_AUTO_TEST_CASE(monomial_boost_s11n_test)293 BOOST_AUTO_TEST_CASE(monomial_boost_s11n_test)
294 {
295 init();
296 tuple_for_each(expo_types{}, boost_s11n_tester());
297 BOOST_CHECK((is_key<monomial<fake_int_01>>::value));
298 BOOST_CHECK(
299 (!has_boost_save<boost::archive::binary_oarchive, boost_s11n_key_wrapper<monomial<fake_int_01>>>::value));
300 BOOST_CHECK(
301 (!has_boost_load<boost::archive::binary_iarchive, boost_s11n_key_wrapper<monomial<fake_int_01>>>::value));
302 }
303
304 #if defined(PIRANHA_WITH_MSGPACK)
305
306 #include <algorithm>
307 #include <atomic>
308 #include <iterator>
309 #include <thread>
310
311 using msgpack::sbuffer;
312 using msgpack::packer;
313
314 template <typename T>
315 using sw = msgpack_stream_wrapper<T>;
316
317 template <typename Monomial>
msgpack_round_trip_monomial(const Monomial & m,const symbol_set & s,msgpack_format f)318 static inline Monomial msgpack_round_trip_monomial(const Monomial &m, const symbol_set &s, msgpack_format f)
319 {
320 sbuffer sbuf;
321 packer<sbuffer> p(sbuf);
322 m.msgpack_pack(p, f, s);
323 auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
324 Monomial n;
325 n.msgpack_convert(oh.get(), f, s);
326 return n;
327 }
328
329 template <typename Monomial>
msgpack_round_trip_monomial_ss(const Monomial & m,const symbol_set & s,msgpack_format f)330 static inline Monomial msgpack_round_trip_monomial_ss(const Monomial &m, const symbol_set &s, msgpack_format f)
331 {
332 sw<std::stringstream> ss;
333 packer<sw<std::stringstream>> p(ss);
334 m.msgpack_pack(p, f, s);
335 std::vector<char> vchar;
336 std::copy(std::istreambuf_iterator<char>(ss), std::istreambuf_iterator<char>(), std::back_inserter(vchar));
337 auto oh = msgpack::unpack(vchar.data(), static_cast<std::size_t>(vchar.size()));
338 Monomial n;
339 n.msgpack_convert(oh.get(), f, s);
340 return n;
341 }
342
343 struct msgpack_tester {
344 template <typename T>
345 struct runner {
346 template <typename U>
operator ()msgpack_tester::runner347 void operator()(const U &) const
348 {
349 using monomial_type = monomial<T, U>;
350 BOOST_CHECK((key_has_msgpack_pack<msgpack::sbuffer, monomial_type>::value));
351 BOOST_CHECK((key_has_msgpack_pack<std::ostringstream, monomial_type>::value));
352 BOOST_CHECK((key_has_msgpack_pack<std::ostringstream, monomial_type &>::value));
353 BOOST_CHECK((key_has_msgpack_pack<std::ostringstream, const monomial_type &>::value));
354 BOOST_CHECK((key_has_msgpack_pack<std::ostringstream, const monomial_type>::value));
355 BOOST_CHECK((key_has_msgpack_pack<sw<std::ostringstream>, monomial_type>::value));
356 BOOST_CHECK((!key_has_msgpack_pack<msgpack::sbuffer &, monomial_type>::value));
357 BOOST_CHECK((!key_has_msgpack_pack<int, monomial_type>::value));
358 BOOST_CHECK((!key_has_msgpack_pack<const std::ostringstream, monomial_type>::value));
359 BOOST_CHECK((!key_has_msgpack_pack<const std::ostringstream &&, monomial_type>::value));
360 BOOST_CHECK((!key_has_msgpack_pack<std::ostringstream &&, monomial_type>::value));
361 BOOST_CHECK((key_has_msgpack_convert<monomial_type>::value));
362 BOOST_CHECK((key_has_msgpack_convert<monomial_type &>::value));
363 BOOST_CHECK((key_has_msgpack_convert<monomial_type &&>::value));
364 BOOST_CHECK((!key_has_msgpack_convert<const monomial_type &>::value));
365 BOOST_CHECK((!key_has_msgpack_convert<const monomial_type>::value));
366 // Some simple checks.
367 for (auto f : {msgpack_format::portable, msgpack_format::binary}) {
368 BOOST_CHECK((msgpack_round_trip_monomial(monomial_type{}, symbol_set{}, f) == monomial_type{}));
369 BOOST_CHECK((msgpack_round_trip_monomial_ss(monomial_type{}, symbol_set{}, f) == monomial_type{}));
370 monomial_type m{T(1), T(2)};
371 symbol_set s{symbol{"a"}, symbol{"b"}};
372 BOOST_CHECK((msgpack_round_trip_monomial(m, s, f) == m));
373 BOOST_CHECK((msgpack_round_trip_monomial_ss(m, s, f) == m));
374 // Test exceptions.
375 sbuffer sbuf;
376 packer<sbuffer> p(sbuf);
377 BOOST_CHECK_EXCEPTION(
378 m.msgpack_pack(p, f, symbol_set{}), std::invalid_argument, [](const std::invalid_argument &ia) {
379 return boost::contains(
380 ia.what(),
381 "incompatible symbol set in monomial serialization: the reference "
382 "symbol set has a size of 0, while the monomial being serialized has a size of 2");
383 });
384 m.msgpack_pack(p, f, s);
385 auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
386 BOOST_CHECK_EXCEPTION(
387 m.msgpack_convert(oh.get(), f, symbol_set{}), std::invalid_argument,
388 [](const std::invalid_argument &ia) {
389 return boost::contains(
390 ia.what(),
391 "incompatible symbol set in monomial serialization: the reference "
392 "symbol set has a size of 0, while the monomial being deserialized has a size of 2");
393 });
394 }
395 // Random checks.
396 random_test<U>();
397 }
398 template <typename U, typename V = T, typename std::enable_if<std::is_same<V, integer>::value, int>::type = 0>
random_testmsgpack_tester::runner399 void random_test() const
400 {
401 using monomial_type = monomial<T, U>;
402 using size_type = typename monomial_type::size_type;
403 std::atomic<bool> flag(true);
404 auto checker = [&flag](unsigned n) {
405 std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
406 std::uniform_int_distribution<size_type> sdist(0u, 10u);
407 std::uniform_int_distribution<int> edist(-10, 10);
408 const std::vector<std::string> vs = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
409 for (auto f : {msgpack_format::portable, msgpack_format::binary}) {
410 for (auto i = 0; i < ntrials; ++i) {
411 const auto size = sdist(eng);
412 std::vector<T> tmp;
413 for (size_type j = 0; j < size; ++j) {
414 tmp.emplace_back(edist(eng));
415 }
416 monomial_type m(tmp.begin(), tmp.end());
417 symbol_set ss(vs.begin(), vs.begin() + size);
418 if (m != msgpack_round_trip_monomial(m, ss, f)) {
419 flag.store(false);
420 }
421 if (m != msgpack_round_trip_monomial_ss(m, ss, f)) {
422 flag.store(false);
423 }
424 }
425 }
426 };
427 std::thread t0(checker, 0);
428 std::thread t1(checker, 1);
429 std::thread t2(checker, 2);
430 std::thread t3(checker, 3);
431 t0.join();
432 t1.join();
433 t2.join();
434 t3.join();
435 BOOST_CHECK(flag.load());
436 }
437 template <typename U, typename V = T, typename std::enable_if<std::is_same<V, rational>::value, int>::type = 0>
random_testmsgpack_tester::runner438 void random_test() const
439 {
440 using monomial_type = monomial<T, U>;
441 using size_type = typename monomial_type::size_type;
442 std::atomic<bool> flag(true);
443 auto checker = [&flag](unsigned n) {
444 std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
445 std::uniform_int_distribution<size_type> sdist(0u, 10u);
446 std::uniform_int_distribution<int> edist(-10, 10);
447 const std::vector<std::string> vs = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
448 for (auto f : {msgpack_format::portable, msgpack_format::binary}) {
449 for (auto i = 0; i < ntrials; ++i) {
450 const auto size = sdist(eng);
451 std::vector<T> tmp;
452 for (size_type j = 0; j < size; ++j) {
453 int num = edist(eng), den = edist(eng);
454 if (!den) {
455 den = 1;
456 }
457 tmp.emplace_back(num, den);
458 }
459 monomial_type m(tmp.begin(), tmp.end());
460 symbol_set ss(vs.begin(), vs.begin() + size);
461 if (m != msgpack_round_trip_monomial(m, ss, f)) {
462 flag.store(false);
463 }
464 if (m != msgpack_round_trip_monomial_ss(m, ss, f)) {
465 flag.store(false);
466 }
467 }
468 }
469 };
470 std::thread t0(checker, 0);
471 std::thread t1(checker, 1);
472 std::thread t2(checker, 2);
473 std::thread t3(checker, 3);
474 t0.join();
475 t1.join();
476 t2.join();
477 t3.join();
478 BOOST_CHECK(flag.load());
479 }
480 template <typename U, typename V = T, typename std::enable_if<std::is_integral<V>::value, int>::type = 0>
random_testmsgpack_tester::runner481 void random_test() const
482 {
483 using monomial_type = monomial<T, U>;
484 using size_type = typename monomial_type::size_type;
485 std::atomic<bool> flag(true);
486 auto checker = [&flag](unsigned n) {
487 std::mt19937 eng(static_cast<std::mt19937::result_type>(n));
488 std::uniform_int_distribution<size_type> sdist(0u, 10u);
489 std::uniform_int_distribution<T> edist(-10, 10);
490 const std::vector<std::string> vs = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
491 for (auto f : {msgpack_format::portable, msgpack_format::binary}) {
492 for (auto i = 0; i < ntrials; ++i) {
493 const auto size = sdist(eng);
494 std::vector<T> tmp;
495 for (size_type j = 0; j < size; ++j) {
496 tmp.push_back(edist(eng));
497 }
498 monomial_type m(tmp.begin(), tmp.end());
499 symbol_set ss(vs.begin(), vs.begin() + size);
500 if (m != msgpack_round_trip_monomial(m, ss, f)) {
501 flag.store(false);
502 }
503 if (m != msgpack_round_trip_monomial_ss(m, ss, f)) {
504 flag.store(false);
505 }
506 }
507 }
508 };
509 std::thread t0(checker, 0);
510 std::thread t1(checker, 1);
511 std::thread t2(checker, 2);
512 std::thread t3(checker, 3);
513 t0.join();
514 t1.join();
515 t2.join();
516 t3.join();
517 BOOST_CHECK(flag.load());
518 }
519 };
520 template <typename T>
operator ()msgpack_tester521 void operator()(const T &) const
522 {
523 tuple_for_each(size_types{}, runner<T>());
524 }
525 };
526
BOOST_AUTO_TEST_CASE(monomial_msgpack_test)527 BOOST_AUTO_TEST_CASE(monomial_msgpack_test)
528 {
529 tuple_for_each(expo_types{}, msgpack_tester());
530 BOOST_CHECK((!key_has_msgpack_pack<msgpack::sbuffer, monomial<fake_int_01>>::value));
531 BOOST_CHECK((!key_has_msgpack_pack<std::ostringstream, monomial<fake_int_01>>::value));
532 BOOST_CHECK((!key_has_msgpack_pack<sw<std::ostringstream>, monomial<fake_int_01>>::value));
533 BOOST_CHECK((!key_has_msgpack_convert<monomial<fake_int_01>>::value));
534 }
535
536 #endif
537