]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
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; | |
f67539c2 | 221 | a[0] += accumulators::weighted_sum<double>(1, 2); |
92f5a8d4 TL |
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); | |
1e59de90 TL |
235 | |
236 | accumulators::weighted_mean<double> b; | |
237 | b(weight(3), 3); | |
238 | a[0] += b; | |
239 | ||
240 | accumulators::weighted_mean<double> c; | |
241 | c(weight(1), 1); | |
242 | c(weight(2), 2); | |
243 | c(weight(3), 3); | |
244 | ||
245 | BOOST_TEST_EQ(a[0].sum_of_weights(), c.sum_of_weights()); | |
246 | BOOST_TEST_IS_CLOSE(a[0].value(), c.value(), 1e-3); | |
247 | BOOST_TEST_IS_CLOSE(a[0].variance(), c.variance(), 1e-3); | |
92f5a8d4 TL |
248 | } |
249 | ||
250 | // exceeding array capacity | |
251 | { | |
252 | auto a = storage_adaptor<std::array<int, 10>>(); | |
253 | a.reset(10); // should not throw | |
254 | BOOST_TEST_THROWS(a.reset(11), std::length_error); | |
255 | auto b = storage_adaptor<std::vector<int>>(); | |
256 | b.reset(11); | |
257 | BOOST_TEST_THROWS(a = b, std::length_error); | |
258 | } | |
259 | ||
260 | // test sparsity of map backend | |
261 | { | |
262 | tracing_allocator_db db; | |
263 | tracing_allocator<char> alloc(db); | |
264 | using map_t = std::map<std::size_t, double, std::less<std::size_t>, | |
265 | tracing_allocator<std::pair<const std::size_t, double>>>; | |
266 | using A = storage_adaptor<map_t>; | |
267 | auto a = A(alloc); | |
268 | // MSVC implementation allocates some structures for debugging | |
269 | const auto baseline = db.second; | |
270 | a.reset(10); | |
271 | BOOST_TEST_EQ(db.first, baseline); // nothing allocated yet | |
272 | // queries do not allocate | |
273 | BOOST_TEST_EQ(a[0], 0); | |
274 | BOOST_TEST_EQ(a[9], 0); | |
275 | BOOST_TEST_EQ(db.first, baseline); | |
276 | ++a[5]; // causes one allocation | |
277 | const auto node = db.first - baseline; | |
278 | BOOST_TEST_EQ(a[5], 1); | |
279 | a[4] += 2; // causes one allocation | |
280 | BOOST_TEST_EQ(a[4], 2); | |
281 | BOOST_TEST_EQ(db.first, baseline + 2 * node); | |
282 | a[3] -= 2; // causes one allocation | |
283 | BOOST_TEST_EQ(a[3], -2); | |
284 | BOOST_TEST_EQ(db.first, baseline + 3 * node); | |
285 | a[2] *= 2; // no allocation | |
286 | BOOST_TEST_EQ(db.first, baseline + 3 * node); | |
287 | a[2] /= 2; // no allocation | |
288 | BOOST_TEST_EQ(db.first, baseline + 3 * node); | |
289 | a[4] = 0; // causes one deallocation | |
290 | BOOST_TEST_EQ(db.first, baseline + 2 * node); | |
291 | ||
292 | auto b = storage_adaptor<std::vector<int>>(); | |
293 | b.reset(5); | |
294 | ++b[2]; | |
295 | a = b; | |
296 | // only one new allocation for non-zero value | |
297 | BOOST_TEST_EQ(db.first, baseline + node); | |
298 | } | |
299 | ||
300 | return boost::report_errors(); | |
301 | } |