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/real_trigonometric_kronecker_monomial.hpp"
30 
31 #define BOOST_TEST_MODULE real_trigonometric_kronecker_monomial_02_test
32 #include <boost/test/included/unit_test.hpp>
33 
34 #include <algorithm>
35 #include <boost/algorithm/string/predicate.hpp>
36 #include <boost/archive/xml_iarchive.hpp>
37 #include <boost/archive/xml_oarchive.hpp>
38 #include <initializer_list>
39 #include <mutex>
40 #include <random>
41 #include <sstream>
42 #include <stdexcept>
43 #include <string>
44 #include <thread>
45 #include <tuple>
46 #include <vector>
47 
48 #include "../src/init.hpp"
49 #include "../src/s11n.hpp"
50 #include "../src/symbol_set.hpp"
51 #include "../src/type_traits.hpp"
52 
53 using namespace piranha;
54 
55 using int_types = std::tuple<signed char, int, long, long long>;
56 
57 static const int ntries = 1000;
58 
59 static std::mutex mut;
60 
61 template <typename OArchive, typename IArchive, typename T>
boost_roundtrip(const T & x,const symbol_set & args,bool mt=false)62 static inline void boost_roundtrip(const T &x, const symbol_set &args, bool mt = false)
63 {
64     using w_type = boost_s11n_key_wrapper<T>;
65     {
66         std::stringstream ss;
67         {
68             OArchive oa(ss);
69             boost_save(oa, w_type{x, args});
70         }
71         T retval;
72         {
73             IArchive ia(ss);
74             w_type w{retval, args};
75             boost_load(ia, w);
76         }
77         if (mt) {
78             std::lock_guard<std::mutex> lock(mut);
79             BOOST_CHECK(x == retval);
80         } else {
81             BOOST_CHECK(x == retval);
82         }
83     }
84     {
85         std::stringstream ss;
86         {
87             OArchive oa(ss);
88             w_type w{x, args};
89             oa << w;
90         }
91         T retval;
92         {
93             IArchive ia(ss);
94             w_type w{retval, args};
95             ia >> w;
96         }
97         if (mt) {
98             std::lock_guard<std::mutex> lock(mut);
99             BOOST_CHECK(x == retval);
100         } else {
101             BOOST_CHECK(x == retval);
102         }
103     }
104 }
105 
106 struct boost_s11n_tester {
107     template <typename T>
operator ()boost_s11n_tester108     void operator()(const T &) const
109     {
110         using k_type = real_trigonometric_kronecker_monomial<T>;
111         using w_type = boost_s11n_key_wrapper<k_type>;
112         BOOST_CHECK((has_boost_save<boost::archive::binary_oarchive, w_type>::value));
113         BOOST_CHECK((has_boost_save<boost::archive::text_oarchive, w_type>::value));
114         BOOST_CHECK((has_boost_load<boost::archive::binary_iarchive, w_type>::value));
115         BOOST_CHECK((has_boost_load<boost::archive::text_iarchive, w_type>::value));
116         BOOST_CHECK((has_boost_save<boost::archive::xml_oarchive, w_type>::value));
117         BOOST_CHECK((has_boost_load<boost::archive::xml_iarchive, w_type>::value));
118         BOOST_CHECK((!has_boost_save<boost::archive::text_iarchive, w_type>::value));
119         BOOST_CHECK((!has_boost_load<boost::archive::text_oarchive, w_type>::value));
120         BOOST_CHECK((!has_boost_save<boost::archive::binary_oarchive const, w_type>::value));
121         BOOST_CHECK((!has_boost_save<void, w_type>::value));
122         BOOST_CHECK((!has_boost_load<boost::archive::binary_iarchive const, w_type>::value));
123         BOOST_CHECK((!has_boost_load<void, w_type>::value));
124         const std::vector<std::string> names = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "l"};
125         auto t_func = [&names](unsigned n) {
126             std::uniform_int_distribution<unsigned> sdist(0, 10);
127             std::uniform_int_distribution<int> edist(-10, 10);
128             std::uniform_int_distribution<int> fdist(0, 1);
129             std::mt19937 rng(n);
130             std::vector<T> expos;
131             for (int i = 0; i < ntries; ++i) {
132                 auto s = sdist(rng);
133                 expos.resize(s);
134                 std::generate(expos.begin(), expos.end(), [&rng, &edist]() { return edist(rng); });
135                 k_type k;
136                 try {
137                     k = k_type(expos.begin(), expos.end());
138                 } catch (...) {
139                     continue;
140                 }
141                 k.set_flavour(static_cast<bool>(fdist(rng)));
142                 boost_roundtrip<boost::archive::text_oarchive, boost::archive::text_iarchive>(
143                     k, symbol_set(names.begin(), names.begin() + s), true);
144                 boost_roundtrip<boost::archive::binary_oarchive, boost::archive::binary_iarchive>(
145                     k, symbol_set(names.begin(), names.begin() + s), true);
146             }
147         };
148         std::thread t0(t_func, 0);
149         std::thread t1(t_func, 1);
150         std::thread t2(t_func, 2);
151         std::thread t3(t_func, 3);
152         t0.join();
153         t1.join();
154         t2.join();
155         t3.join();
156         // Test inconsistent size.
157         {
158             std::stringstream ss;
159             {
160                 boost::archive::text_oarchive oa(ss);
161                 boost_save(oa, w_type{k_type{}, symbol_set{}});
162             }
163             k_type retval{T(1), T(2)};
164             {
165                 boost::archive::text_iarchive ia(ss);
166                 symbol_set new_ss{symbol{"x"}};
167                 w_type w{retval, new_ss};
168                 BOOST_CHECK_EXCEPTION(boost_load(ia, w), std::invalid_argument, [](const std::invalid_argument &iae) {
169                     return boost::contains(
170                         iae.what(),
171                         "invalid size detected in the deserialization of a real Kronercker "
172                         "trigonometric monomial: the deserialized size is 0 but the reference symbol set has a "
173                         "size of 1");
174                 });
175             }
176             BOOST_CHECK((retval == k_type{T(1), T(2)}));
177         }
178     }
179 };
180 
BOOST_AUTO_TEST_CASE(rtkm_constructor_test)181 BOOST_AUTO_TEST_CASE(rtkm_constructor_test)
182 {
183     init();
184     tuple_for_each(int_types{}, boost_s11n_tester());
185 }
186 
187 #if defined(PIRANHA_WITH_MSGPACK)
188 
189 template <typename T>
msgpack_roundtrip(const T & x,const symbol_set & args,msgpack_format f,bool mt=false)190 static inline void msgpack_roundtrip(const T &x, const symbol_set &args, msgpack_format f, bool mt = false)
191 {
192     msgpack::sbuffer sbuf;
193     msgpack::packer<msgpack::sbuffer> p(sbuf);
194     x.msgpack_pack(p, f, args);
195     T retval;
196     auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
197     retval.msgpack_convert(oh.get(), f, args);
198     if (mt) {
199         std::lock_guard<std::mutex> lock(mut);
200         BOOST_CHECK(x == retval);
201     } else {
202         BOOST_CHECK(x == retval);
203     }
204 }
205 
206 struct msgpack_s11n_tester {
207     template <typename T>
operator ()msgpack_s11n_tester208     void operator()(const T &) const
209     {
210         using k_type = real_trigonometric_kronecker_monomial<T>;
211         BOOST_CHECK((key_has_msgpack_pack<msgpack::sbuffer, k_type>::value));
212         BOOST_CHECK((!key_has_msgpack_pack<msgpack::sbuffer &, k_type>::value));
213         BOOST_CHECK((!key_has_msgpack_pack<int, k_type>::value));
214         BOOST_CHECK((!key_has_msgpack_pack<void, k_type>::value));
215         BOOST_CHECK((key_has_msgpack_convert<k_type>::value));
216         BOOST_CHECK((!key_has_msgpack_convert<k_type const &>::value));
217         const std::vector<std::string> names = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "l"};
218         auto t_func = [&names](unsigned n) {
219             std::uniform_int_distribution<unsigned> sdist(0, 10);
220             std::uniform_int_distribution<int> edist(-10, 10);
221             std::uniform_int_distribution<int> fdist(0, 1);
222             std::mt19937 rng(n);
223             std::vector<T> expos;
224             for (auto f : {msgpack_format::portable, msgpack_format::binary}) {
225                 for (int i = 0; i < ntries; ++i) {
226                     auto s = sdist(rng);
227                     expos.resize(s);
228                     std::generate(expos.begin(), expos.end(), [&rng, &edist]() { return edist(rng); });
229                     k_type k;
230                     try {
231                         k = k_type(expos.begin(), expos.end());
232                     } catch (...) {
233                         continue;
234                     }
235                     k.set_flavour(fdist(rng));
236                     msgpack_roundtrip(k, symbol_set(names.begin(), names.begin() + s), f, true);
237                 }
238             }
239         };
240         std::thread t0(t_func, 0);
241         std::thread t1(t_func, 1);
242         std::thread t2(t_func, 2);
243         std::thread t3(t_func, 3);
244         t0.join();
245         t1.join();
246         t2.join();
247         t3.join();
248         // Test inconsistent size.
249         {
250             msgpack::sbuffer sbuf;
251             msgpack::packer<msgpack::sbuffer> p(sbuf);
252             p.pack_array(2);
253             p.pack_array(1);
254             msgpack_pack(p, T(1), msgpack_format::portable);
255             msgpack_pack(p, true, msgpack_format::portable);
256             k_type retval{T(2)};
257             auto oh = msgpack::unpack(sbuf.data(), sbuf.size());
258             BOOST_CHECK_EXCEPTION(
259                 retval.msgpack_convert(oh.get(), msgpack_format::portable, symbol_set{}), std::invalid_argument,
260                 [](const std::invalid_argument &ia) {
261                     return boost::contains(
262                         ia.what(),
263                         "incompatible symbol set in trigonometric monomial serialization: the reference "
264                         "symbol set has a size of 0, while the trigonometric monomial being deserialized has "
265                         "a size of 1");
266                 });
267             BOOST_CHECK((retval == k_type{T(2)}));
268         }
269     }
270 };
271 
BOOST_AUTO_TEST_CASE(kronecker_monomial_msgpack_s11n_test)272 BOOST_AUTO_TEST_CASE(kronecker_monomial_msgpack_s11n_test)
273 {
274     tuple_for_each(int_types{}, msgpack_s11n_tester());
275 }
276 
277 #endif
278