1 // Copyright 2019 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/config.hpp>
9 #include <boost/core/ignore_unused.hpp>
10 #include <boost/core/lightweight_test.hpp>
11 #include <boost/histogram/accumulators.hpp>
12 #include <boost/histogram/accumulators/ostream.hpp>
13 #include <boost/histogram/algorithm/sum.hpp>
14 #include <boost/histogram/axis/category.hpp>
15 #include <boost/histogram/axis/integer.hpp>
16 #include <boost/histogram/axis/ostream.hpp>
17 #include <boost/histogram/histogram.hpp>
18 #include <boost/histogram/literals.hpp>
19 #include <boost/histogram/make_histogram.hpp>
20 #include <boost/histogram/ostream.hpp>
21 #include <boost/histogram/storage_adaptor.hpp>
22 #include <boost/variant2/variant.hpp>
29 #include "throw_exception.hpp"
30 #include "utility_histogram.hpp"
32 using namespace boost::histogram;
33 using namespace boost::histogram::algorithm;
34 using namespace boost::histogram::literals; // to get _c suffix
35 using boost::variant2::variant;
37 constexpr auto ndata = 1 << 16; // should be larger than index buffer in fill_n
39 using in = axis::integer<int, axis::null_type>;
40 using in0 = axis::integer<int, axis::null_type, axis::option::none_t>;
41 using ing = axis::integer<double, axis::null_type,
42 decltype(axis::option::growth | axis::option::underflow |
43 axis::option::overflow)>;
44 using cs = axis::category<std::string, axis::null_type>;
45 using csg = axis::category<std::string, axis::null_type, axis::option::growth_t>;
48 auto size() const { return axis::index_type{2}; }
50 auto index(const std::tuple<double, double>& xy) const {
51 const auto x = std::get<0>(xy);
52 const auto y = std::get<1>(xy);
53 const auto r = std::sqrt(x * x + y * y);
54 return std::min(static_cast<axis::index_type>(r), size());
57 friend std::ostream& operator<<(std::ostream& os, const axis2d&) {
64 void run_tests(const std::vector<int>& x, const std::vector<int>& y,
65 const std::vector<double>& w) {
69 auto h = make(Tag(), in{1, 3});
71 for (auto&& xi : x) h(xi);
72 // uses 1D specialization
79 auto h = make(Tag(), in{1, 3});
81 for (auto&& xi : x) h(xi);
90 auto h = make(Tag(), in{1, 3});
93 for (auto&& xi : x) h(xi);
95 boost::variant2::variant<int, std::vector<int>, std::string> v[1];
100 BOOST_TEST_EQ(h, h2);
105 auto h = make(Tag(), in{1, 3});
108 boost::ignore_unused(bad1);
109 BOOST_TEST_THROWS(h.fill(bad1), std::invalid_argument);
111 std::vector<std::array<int, 4>> bad2;
112 boost::ignore_unused(bad2);
113 BOOST_TEST_THROWS(h.fill(bad2), std::invalid_argument);
116 // 1D with category axis
118 auto h = make(Tag(), cs{"A", "B"});
121 const auto s = {"A", "B", "C"};
122 for (auto&& si : s) h(si);
125 variant<int, std::string, std::vector<std::string>> v[1];
130 v[0] = std::vector<std::string>(s.begin(), s.end());
131 for (auto&& si : s) h(si);
134 BOOST_TEST_EQ(h, h2);
139 auto h = make(Tag(), in{1, 3});
142 for (auto&& xi : x) h(weight(2), xi);
143 h2.fill(weight(2), x);
145 for (unsigned i = 0; i < ndata; ++i) h(weight(w[i]), x[i]);
146 h2.fill(weight(w), x);
148 BOOST_TEST_EQ(h, h2);
151 boost::ignore_unused(w2);
152 BOOST_TEST_THROWS(h2.fill(x, weight(w2)), std::invalid_argument);
157 auto h = make(Tag(), in{1, 3}, in0{1, 5});
160 for (int i = 0; i < ndata; ++i) h(x[i], y[i]);
161 const auto xy = {x, y};
164 BOOST_TEST_EQ(h, h2);
167 BOOST_TEST_THROWS(h.fill(x), std::invalid_argument);
170 std::array<std::vector<int>, 2> bad = {{std::vector<int>(1), std::vector<int>(2)}};
171 boost::ignore_unused(bad);
172 BOOST_TEST_THROWS(h2.fill(bad), std::invalid_argument);
175 // 2D variant and weight
177 auto h = make(Tag(), in{1, 3}, in0{1, 5});
179 using V = variant<int, std::vector<int>, std::string>;
187 for (auto&& vi : y) h1(3, vi);
189 BOOST_TEST_EQ(h1, h2);
197 for (auto&& vi : x) h1(vi, 3);
199 BOOST_TEST_EQ(h1, h2);
207 for (auto&& vi : y) h1(3, vi, weight(2));
208 h2.fill(xy, weight(2));
209 BOOST_TEST_EQ(h1, h2);
217 for (unsigned i = 0; i < ndata; ++i) h1(3, y[i], weight(w[i]));
218 h2.fill(xy, weight(w));
219 BOOST_TEST_EQ(sum(h1), sum(h2));
220 BOOST_TEST_EQ(h1, h2);
226 auto h = make(Tag(), ing());
228 for (const auto& xi : x) h(xi);
230 BOOST_TEST_EQ(h, h2);
235 auto h = make(Tag(), in(1, 3), ing());
237 for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i]);
238 const auto xy = {x, y};
240 BOOST_TEST_EQ(h, h2);
245 auto h = make(Tag(), ing(), ing());
247 for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i]);
248 const auto xy = {x, y};
250 BOOST_TEST_EQ(h, h2);
253 // 2D growing with weights A
255 auto h = make(Tag(), in(1, 3), ing());
257 for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i], weight(w[i]));
258 const auto xy = {x, y};
259 h2.fill(xy, weight(w));
260 BOOST_TEST_EQ(h, h2);
263 // 2D growing with weights B
265 auto h = make(Tag(), ing(), ing());
267 for (unsigned i = 0; i < ndata; ++i) h(x[i], y[i], weight(2));
268 const auto xy = {x, y};
269 h2.fill(xy, weight(2));
270 BOOST_TEST_EQ(h, h2);
273 // 2D growing and variant
275 auto h = make(Tag(), csg{}, in{1, 2});
281 using V = variant<std::string, std::vector<std::string>, int, std::vector<int>>;
282 const auto xy = {V("foo"), V(std::vector<int>{1, 2})};
285 BOOST_TEST_EQ(h, h2);
287 const auto bad = {V(std::vector<std::string>(1, "foo")), V(std::vector<int>{1, 2})};
288 boost::ignore_unused(bad);
289 BOOST_TEST_THROWS(h.fill(bad), std::invalid_argument);
292 // 1D profile with samples
294 auto h = make_s(Tag(), profile_storage(), in(1, 3));
297 for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(w[i]));
298 for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(w[i]), weight(w[i]));
299 for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(2), weight(w[i]));
300 for (unsigned i = 0; i < ndata; ++i) h(x[i], sample(w[i]), weight(2));
302 h2.fill(x, sample(w));
303 h2.fill(x, sample(w), weight(w));
304 h2.fill(x, sample(2), weight(w));
305 h2.fill(x, sample(w), weight(2));
307 BOOST_TEST_EQ(h, h2);
310 // 2D weighted profile with samples and weights
312 auto h = make_s(Tag(), weighted_profile_storage(), in(1, 3), in0(1, 3));
315 for (unsigned i = 0; i < ndata; ++i) h(x[i], 3, sample(w[i]), weight(w[i]));
316 for (unsigned i = 0; i < ndata; ++i) h(x[i], 3, sample(2), weight(w[i]));
317 for (unsigned i = 0; i < ndata; ++i) h(x[i], 3, sample(w[i]), weight(2));
319 using V = variant<int, std::vector<int>>;
323 h2.fill(xy, sample(w), weight(w));
324 h2.fill(xy, sample(2), weight(w));
325 h2.fill(xy, sample(w), weight(2));
327 BOOST_TEST_EQ(h, h2);
332 auto h = make(Tag(), axis2d{});
335 std::vector<std::tuple<double, double>> xy;
337 for (unsigned i = 0; i < ndata; ++i) xy.emplace_back(x[i], y[i]);
339 for (auto&& xyi : xy) h(xyi);
342 BOOST_TEST_EQ(h, h2);
348 std::normal_distribution<> id(0, 2);
349 std::vector<int> x(ndata), y(ndata);
350 auto generator = [&] { return static_cast<int>(id(gen)); };
351 std::generate(x.begin(), x.end(), generator);
352 std::generate(y.begin(), y.end(), generator);
353 std::vector<double> w(ndata);
354 // must be all positive
355 std::generate(w.begin(), w.end(), [&] { return 0.5 + std::abs(id(gen)); });
357 run_tests<static_tag>(x, y, w);
358 run_tests<dynamic_tag>(x, y, w);
360 return boost::report_errors();