Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / histogram / test / storage_adaptor_test.cpp
1 // Copyright 2015-2017 Hans Dembinski
2 //
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt
5 // or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7 #include <array>
8 #include <boost/core/lightweight_test.hpp>
9 #include <boost/histogram/accumulators/weighted_mean.hpp>
10 #include <boost/histogram/accumulators/weighted_sum.hpp>
11 #include <boost/histogram/storage_adaptor.hpp>
12 #include <boost/histogram/unlimited_storage.hpp>
13 #include <boost/histogram/weight.hpp>
14 #include <cmath>
15 #include <deque>
16 #include <limits>
17 #include <map>
18 #include <sstream>
19 #include <vector>
20 #include "is_close.hpp"
21 #include "throw_exception.hpp"
22 #include "utility_allocator.hpp"
23
24 using namespace boost::histogram;
25 using namespace std::literals;
26
27 template <class T>
28 auto str(const T& t) {
29   std::ostringstream os;
30   os << t;
31   return os.str();
32 }
33
34 template <typename T>
35 void tests() {
36   using Storage = storage_adaptor<T>;
37   // ctor, copy, move
38   {
39     Storage a;
40     a.reset(2);
41     Storage b(a);
42     Storage c;
43     c = a;
44     BOOST_TEST_EQ(std::distance(a.begin(), a.end()), 2);
45     BOOST_TEST_EQ(a.size(), 2);
46     BOOST_TEST_EQ(b.size(), 2);
47     BOOST_TEST_EQ(c.size(), 2);
48
49     Storage d(std::move(a));
50     BOOST_TEST_EQ(d.size(), 2);
51     Storage e;
52     e = std::move(d);
53     BOOST_TEST_EQ(e.size(), 2);
54
55     const auto t = T();
56     storage_adaptor<T> g(t); // tests converting ctor
57     BOOST_TEST_EQ(g.size(), 0);
58     const auto u = std::vector<typename Storage::value_type>(3, 1);
59     Storage h(u); // tests converting ctor
60     BOOST_TEST_EQ(h.size(), 3);
61     BOOST_TEST_EQ(h[0], 1);
62     BOOST_TEST_EQ(h[1], 1);
63     BOOST_TEST_EQ(h[2], 1);
64   }
65
66   // increment, add, sub, set, reset, compare
67   {
68     Storage a;
69     a.reset(1);
70     ++a[0];
71     const auto save = a[0]++;
72     BOOST_TEST_EQ(save, 1);
73     BOOST_TEST_EQ(a[0], 2);
74     a.reset(2);
75     BOOST_TEST_EQ(a.size(), 2);
76     ++a[0];
77     a[0] += 2;
78     a[1] += 5;
79     BOOST_TEST_EQ(a[0], 3);
80     BOOST_TEST_EQ(a[1], 5);
81     a[0] -= 2;
82     a[1] -= 5;
83     BOOST_TEST_EQ(a[0], 1);
84     BOOST_TEST_EQ(a[1], 0);
85     a[1] = 9;
86     BOOST_TEST_EQ(a[0], 1);
87     BOOST_TEST_EQ(a[1], 9);
88     BOOST_TEST_LT(a[0], 2);
89     BOOST_TEST_LT(0, a[1]);
90     BOOST_TEST_GT(a[1], 4);
91     BOOST_TEST_GT(3, a[0]);
92     a[1] = a[0];
93     BOOST_TEST_EQ(a[0], 1);
94     BOOST_TEST_EQ(a[1], 1);
95     a.reset(0);
96     BOOST_TEST_EQ(a.size(), 0);
97   }
98
99   // copy
100   {
101     Storage a;
102     a.reset(1);
103     ++a[0];
104     Storage b;
105     b.reset(2);
106     BOOST_TEST(!(a == b));
107     b = a;
108     BOOST_TEST(a == b);
109     BOOST_TEST_EQ(b.size(), 1);
110     BOOST_TEST_EQ(b[0], 1);
111
112     Storage c(a);
113     BOOST_TEST(a == c);
114     BOOST_TEST_EQ(c.size(), 1);
115     BOOST_TEST_EQ(c[0], 1);
116   }
117
118   // move
119   {
120     Storage a;
121     a.reset(1);
122     ++a[0];
123     Storage b;
124     BOOST_TEST(!(a == b));
125     b = std::move(a);
126     BOOST_TEST_EQ(b.size(), 1);
127     BOOST_TEST_EQ(b[0], 1);
128     Storage c(std::move(b));
129     BOOST_TEST_EQ(c.size(), 1);
130     BOOST_TEST_EQ(c[0], 1);
131   }
132
133   {
134     Storage a;
135     a.reset(1);
136     a[0] += 2;
137     BOOST_TEST_EQ(str(a[0]), "2"s);
138   }
139 }
140
141 template <typename A, typename B>
142 void mixed_tests() {
143   // comparison
144   {
145     A a, b;
146     a.reset(1);
147     b.reset(1);
148     B c, d;
149     c.reset(1);
150     d.reset(2);
151     ++a[0];
152     ++b[0];
153     c[0] += 2;
154     d[0] = 3;
155     d[1] = 5;
156     BOOST_TEST_EQ(a[0], 1);
157     BOOST_TEST_EQ(b[0], 1);
158     BOOST_TEST_EQ(c[0], 2);
159     BOOST_TEST_EQ(d[0], 3);
160     BOOST_TEST_EQ(d[1], 5);
161     BOOST_TEST(a == a);
162     BOOST_TEST(a == b);
163     BOOST_TEST(!(a == c));
164     BOOST_TEST(!(a == d));
165   }
166
167   // ctor, copy, move, assign
168   {
169     A a;
170     a.reset(2);
171     ++a[1];
172     B b(a);
173     B c;
174     c = a;
175     BOOST_TEST_EQ(c[0], 0);
176     BOOST_TEST_EQ(c[1], 1);
177     c = A();
178     BOOST_TEST_EQ(c.size(), 0);
179     B d(std::move(a));
180     B e;
181     e = std::move(d);
182     BOOST_TEST_EQ(e[0], 0);
183     BOOST_TEST_EQ(e[1], 1);
184   }
185 }
186
187 int main() {
188   tests<std::vector<int>>();
189   tests<std::array<int, 100>>();
190   tests<std::deque<int>>();
191   tests<std::map<std::size_t, int>>();
192   tests<std::unordered_map<std::size_t, int>>();
193
194   mixed_tests<storage_adaptor<std::vector<int>>,
195               storage_adaptor<std::array<double, 100>>>();
196   mixed_tests<unlimited_storage<>, storage_adaptor<std::vector<double>>>();
197   mixed_tests<storage_adaptor<std::vector<int>>, unlimited_storage<>>();
198   mixed_tests<storage_adaptor<std::vector<int>>,
199               storage_adaptor<std::map<std::size_t, int>>>();
200
201   // special case for division of map-based storage_adaptor
202   {
203     auto a = storage_adaptor<std::map<std::size_t, double>>();
204     a.reset(2);
205     a[0] /= 2;
206     BOOST_TEST_EQ(a[0], 0);
207     a[0] = 2;
208     a[0] /= 2;
209     BOOST_TEST_EQ(a[0], 1);
210     a[1] /= std::numeric_limits<double>::quiet_NaN();
211     BOOST_TEST(std::isnan(static_cast<double>(a[1])));
212   }
213
214   // with accumulators::weighted_sum
215   {
216     auto a = storage_adaptor<std::vector<accumulators::weighted_sum<double>>>();
217     a.reset(1);
218     ++a[0];
219     a[0] += 1;
220     a[0] += 2;
221     a[0] += accumulators::weighted_sum<double>(1, 0);
222     BOOST_TEST_EQ(a[0].value(), 5);
223     BOOST_TEST_EQ(a[0].variance(), 6);
224     a[0] *= 2;
225     BOOST_TEST_EQ(a[0].value(), 10);
226     BOOST_TEST_EQ(a[0].variance(), 24);
227   }
228
229   // with accumulators::weighted_mean
230   {
231     auto a = storage_adaptor<std::vector<accumulators::weighted_mean<double>>>();
232     a.reset(1);
233     a[0](/* sample */ 1);
234     a[0](weight(2), /* sample */ 2);
235     a[0] += accumulators::weighted_mean<>(1, 0, 0, 0);
236     BOOST_TEST_EQ(a[0].sum_of_weights(), 4);
237     BOOST_TEST_IS_CLOSE(a[0].value(), 1.25, 1e-3);
238     BOOST_TEST_IS_CLOSE(a[0].variance(), 0.242, 1e-3);
239   }
240
241   // exceeding array capacity
242   {
243     auto a = storage_adaptor<std::array<int, 10>>();
244     a.reset(10); // should not throw
245     BOOST_TEST_THROWS(a.reset(11), std::length_error);
246     auto b = storage_adaptor<std::vector<int>>();
247     b.reset(11);
248     BOOST_TEST_THROWS(a = b, std::length_error);
249   }
250
251   // test sparsity of map backend
252   {
253     tracing_allocator_db db;
254     tracing_allocator<char> alloc(db);
255     using map_t = std::map<std::size_t, double, std::less<std::size_t>,
256                            tracing_allocator<std::pair<const std::size_t, double>>>;
257     using A = storage_adaptor<map_t>;
258     auto a = A(alloc);
259     // MSVC implementation allocates some structures for debugging
260     const auto baseline = db.second;
261     a.reset(10);
262     BOOST_TEST_EQ(db.first, baseline); // nothing allocated yet
263     // queries do not allocate
264     BOOST_TEST_EQ(a[0], 0);
265     BOOST_TEST_EQ(a[9], 0);
266     BOOST_TEST_EQ(db.first, baseline);
267     ++a[5]; // causes one allocation
268     const auto node = db.first - baseline;
269     BOOST_TEST_EQ(a[5], 1);
270     a[4] += 2; // causes one allocation
271     BOOST_TEST_EQ(a[4], 2);
272     BOOST_TEST_EQ(db.first, baseline + 2 * node);
273     a[3] -= 2; // causes one allocation
274     BOOST_TEST_EQ(a[3], -2);
275     BOOST_TEST_EQ(db.first, baseline + 3 * node);
276     a[2] *= 2; // no allocation
277     BOOST_TEST_EQ(db.first, baseline + 3 * node);
278     a[2] /= 2; // no allocation
279     BOOST_TEST_EQ(db.first, baseline + 3 * node);
280     a[4] = 0; // causes one deallocation
281     BOOST_TEST_EQ(db.first, baseline + 2 * node);
282
283     auto b = storage_adaptor<std::vector<int>>();
284     b.reset(5);
285     ++b[2];
286     a = b;
287     // only one new allocation for non-zero value
288     BOOST_TEST_EQ(db.first, baseline + node);
289   }
290
291   return boost::report_errors();
292 }