1 // Copyright 2015-2017 Hans Dembinski
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)
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>
20 #include "is_close.hpp"
21 #include "throw_exception.hpp"
22 #include "utility_allocator.hpp"
24 using namespace boost::histogram;
25 using namespace std::literals;
28 auto str(const T& t) {
29 std::ostringstream os;
36 using Storage = storage_adaptor<T>;
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);
49 Storage d(std::move(a));
50 BOOST_TEST_EQ(d.size(), 2);
53 BOOST_TEST_EQ(e.size(), 2);
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);
66 // increment, add, sub, set, reset, compare
71 const auto save = a[0]++;
72 BOOST_TEST_EQ(save, 1);
73 BOOST_TEST_EQ(a[0], 2);
75 BOOST_TEST_EQ(a.size(), 2);
79 BOOST_TEST_EQ(a[0], 3);
80 BOOST_TEST_EQ(a[1], 5);
83 BOOST_TEST_EQ(a[0], 1);
84 BOOST_TEST_EQ(a[1], 0);
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]);
93 BOOST_TEST_EQ(a[0], 1);
94 BOOST_TEST_EQ(a[1], 1);
96 BOOST_TEST_EQ(a.size(), 0);
106 BOOST_TEST(!(a == b));
109 BOOST_TEST_EQ(b.size(), 1);
110 BOOST_TEST_EQ(b[0], 1);
114 BOOST_TEST_EQ(c.size(), 1);
115 BOOST_TEST_EQ(c[0], 1);
124 BOOST_TEST(!(a == b));
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);
137 BOOST_TEST_EQ(str(a[0]), "2"s);
141 template <typename A, typename B>
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);
163 BOOST_TEST(!(a == c));
164 BOOST_TEST(!(a == d));
167 // ctor, copy, move, assign
175 BOOST_TEST_EQ(c[0], 0);
176 BOOST_TEST_EQ(c[1], 1);
178 BOOST_TEST_EQ(c.size(), 0);
182 BOOST_TEST_EQ(e[0], 0);
183 BOOST_TEST_EQ(e[1], 1);
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>>();
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>>>();
201 // special case for division of map-based storage_adaptor
203 auto a = storage_adaptor<std::map<std::size_t, double>>();
206 BOOST_TEST_EQ(a[0], 0);
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])));
214 // with accumulators::weighted_sum
216 auto a = storage_adaptor<std::vector<accumulators::weighted_sum<double>>>();
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);
225 BOOST_TEST_EQ(a[0].value(), 10);
226 BOOST_TEST_EQ(a[0].variance(), 24);
229 // with accumulators::weighted_mean
231 auto a = storage_adaptor<std::vector<accumulators::weighted_mean<double>>>();
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);
241 // exceeding array capacity
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>>();
248 BOOST_TEST_THROWS(a = b, std::length_error);
251 // test sparsity of map backend
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>;
259 // MSVC implementation allocates some structures for debugging
260 const auto baseline = db.second;
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);
283 auto b = storage_adaptor<std::vector<int>>();
287 // only one new allocation for non-zero value
288 BOOST_TEST_EQ(db.first, baseline + node);
291 return boost::report_errors();