Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / histogram / test / axis_regular_test.cpp
1 // Copyright 2015-2019 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 <boost/core/lightweight_test.hpp>
8 #include <boost/histogram/axis/ostream.hpp>
9 #include <boost/histogram/axis/regular.hpp>
10 #include <limits>
11 #include <sstream>
12 #include <type_traits>
13 #include "is_close.hpp"
14 #include "std_ostream.hpp"
15 #include "throw_exception.hpp"
16 #include "utility_axis.hpp"
17 #include "utility_str.hpp"
18
19 int main() {
20   using namespace boost::histogram;
21   using def = use_default;
22   namespace tr = axis::transform;
23
24   BOOST_TEST(std::is_nothrow_move_assignable<axis::regular<>>::value);
25   BOOST_TEST(std::is_nothrow_move_constructible<axis::regular<>>::value);
26
27   // bad_ctors
28   {
29     BOOST_TEST_THROWS(axis::regular<>(1, 0, 0), std::invalid_argument);
30     BOOST_TEST_THROWS(axis::regular<>(0, 0, 1), std::invalid_argument);
31   }
32
33   // ctors and assignment
34   {
35     axis::regular<> a{4, -2, 2};
36     axis::regular<> b;
37     BOOST_TEST_NE(a, b);
38     b = a;
39     BOOST_TEST_EQ(a, b);
40     axis::regular<> c = std::move(b);
41     BOOST_TEST_EQ(c, a);
42     axis::regular<> d;
43     BOOST_TEST_NE(c, d);
44     d = std::move(c);
45     BOOST_TEST_EQ(d, a);
46   }
47
48   // input, output
49   {
50     axis::regular<> a{4, -2, 2, "foo"};
51     BOOST_TEST_EQ(a.metadata(), "foo");
52     BOOST_TEST_EQ(static_cast<const axis::regular<>&>(a).metadata(), "foo");
53     a.metadata() = "bar";
54     BOOST_TEST_EQ(static_cast<const axis::regular<>&>(a).metadata(), "bar");
55     BOOST_TEST_EQ(a.value(0), -2);
56     BOOST_TEST_EQ(a.value(1), -1);
57     BOOST_TEST_EQ(a.value(2), 0);
58     BOOST_TEST_EQ(a.value(3), 1);
59     BOOST_TEST_EQ(a.value(4), 2);
60     BOOST_TEST_EQ(a.bin(-1).lower(), -std::numeric_limits<double>::infinity());
61     BOOST_TEST_EQ(a.bin(-1).upper(), -2);
62     BOOST_TEST_EQ(a.bin(a.size()).lower(), 2);
63     BOOST_TEST_EQ(a.bin(a.size()).upper(), std::numeric_limits<double>::infinity());
64     BOOST_TEST_EQ(a.index(-10.), -1);
65     BOOST_TEST_EQ(a.index(-2.1), -1);
66     BOOST_TEST_EQ(a.index(-2.0), 0);
67     BOOST_TEST_EQ(a.index(-1.1), 0);
68     BOOST_TEST_EQ(a.index(0.0), 2);
69     BOOST_TEST_EQ(a.index(0.9), 2);
70     BOOST_TEST_EQ(a.index(1.0), 3);
71     BOOST_TEST_EQ(a.index(10.), 4);
72     BOOST_TEST_EQ(a.index(-std::numeric_limits<double>::infinity()), -1);
73     BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 4);
74     BOOST_TEST_EQ(a.index(std::numeric_limits<double>::quiet_NaN()), 4);
75
76     BOOST_TEST_EQ(str(a),
77                   "regular(4, -2, 2, metadata=\"bar\", options=underflow | overflow)");
78   }
79
80   // with inverted range
81   {
82     axis::regular<> a{2, 1, -2};
83     BOOST_TEST_EQ(a.bin(-1).lower(), std::numeric_limits<double>::infinity());
84     BOOST_TEST_EQ(a.bin(0).lower(), 1);
85     BOOST_TEST_EQ(a.bin(1).lower(), -0.5);
86     BOOST_TEST_EQ(a.bin(2).lower(), -2);
87     BOOST_TEST_EQ(a.bin(2).upper(), -std::numeric_limits<double>::infinity());
88     BOOST_TEST_EQ(a.index(2), -1);
89     BOOST_TEST_EQ(a.index(1.001), -1);
90     BOOST_TEST_EQ(a.index(1), 0);
91     BOOST_TEST_EQ(a.index(0), 0);
92     BOOST_TEST_EQ(a.index(-0.499), 0);
93     BOOST_TEST_EQ(a.index(-0.5), 1);
94     BOOST_TEST_EQ(a.index(-1), 1);
95     BOOST_TEST_EQ(a.index(-2), 2);
96     BOOST_TEST_EQ(a.index(-20), 2);
97   }
98
99   // with log transform
100   {
101     auto a = axis::regular<double, tr::log>{2, 1e0, 1e2};
102     BOOST_TEST_EQ(a.bin(-1).lower(), 0.0);
103     BOOST_TEST_IS_CLOSE(a.bin(0).lower(), 1.0, 1e-9);
104     BOOST_TEST_IS_CLOSE(a.bin(1).lower(), 10.0, 1e-9);
105     BOOST_TEST_IS_CLOSE(a.bin(2).lower(), 100.0, 1e-9);
106     BOOST_TEST_EQ(a.bin(2).upper(), std::numeric_limits<double>::infinity());
107
108     BOOST_TEST_EQ(a.index(-1), 2); // produces NaN in conversion
109     BOOST_TEST_EQ(a.index(0), -1);
110     BOOST_TEST_EQ(a.index(1), 0);
111     BOOST_TEST_EQ(a.index(9), 0);
112     BOOST_TEST_EQ(a.index(10), 1);
113     BOOST_TEST_EQ(a.index(90), 1);
114     BOOST_TEST_EQ(a.index(100), 2);
115     BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 2);
116
117     BOOST_TEST_THROWS((axis::regular<double, tr::log>{2, -1, 0}), std::invalid_argument);
118
119     BOOST_TEST_EQ(str(a), "regular_log(2, 1, 100, options=underflow | overflow)");
120   }
121
122   // with sqrt transform
123   {
124     axis::regular<double, tr::sqrt> a(2, 0, 4);
125     // this is weird, but -inf * -inf = inf, thus the lower bound
126     BOOST_TEST_EQ(a.bin(-1).lower(), std::numeric_limits<double>::infinity());
127     BOOST_TEST_IS_CLOSE(a.bin(0).lower(), 0.0, 1e-9);
128     BOOST_TEST_IS_CLOSE(a.bin(1).lower(), 1.0, 1e-9);
129     BOOST_TEST_IS_CLOSE(a.bin(2).lower(), 4.0, 1e-9);
130     BOOST_TEST_EQ(a.bin(2).upper(), std::numeric_limits<double>::infinity());
131
132     BOOST_TEST_EQ(a.index(-1), 2); // produces NaN in conversion
133     BOOST_TEST_EQ(a.index(0), 0);
134     BOOST_TEST_EQ(a.index(0.99), 0);
135     BOOST_TEST_EQ(a.index(1), 1);
136     BOOST_TEST_EQ(a.index(3.99), 1);
137     BOOST_TEST_EQ(a.index(4), 2);
138     BOOST_TEST_EQ(a.index(100), 2);
139     BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 2);
140
141     BOOST_TEST_EQ(str(a), "regular_sqrt(2, 0, 4, options=underflow | overflow)");
142   }
143
144   // with pow transform
145   {
146     axis::regular<double, tr::pow> a(tr::pow{0.5}, 2, 0, 4);
147     // this is weird, but -inf * -inf = inf, thus the lower bound
148     BOOST_TEST_EQ(a.bin(-1).lower(), std::numeric_limits<double>::infinity());
149     BOOST_TEST_IS_CLOSE(a.bin(0).lower(), 0.0, 1e-9);
150     BOOST_TEST_IS_CLOSE(a.bin(1).lower(), 1.0, 1e-9);
151     BOOST_TEST_IS_CLOSE(a.bin(2).lower(), 4.0, 1e-9);
152     BOOST_TEST_EQ(a.bin(2).upper(), std::numeric_limits<double>::infinity());
153
154     BOOST_TEST_EQ(a.index(-1), 2); // produces NaN in conversion
155     BOOST_TEST_EQ(a.index(0), 0);
156     BOOST_TEST_EQ(a.index(0.99), 0);
157     BOOST_TEST_EQ(a.index(1), 1);
158     BOOST_TEST_EQ(a.index(3.99), 1);
159     BOOST_TEST_EQ(a.index(4), 2);
160     BOOST_TEST_EQ(a.index(100), 2);
161     BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 2);
162
163     BOOST_TEST_EQ(str(a),
164                   "regular_pow(2, 0, 4, options=underflow | overflow, power=0.5)");
165   }
166
167   // with step
168   {
169     axis::regular<> a(axis::step(0.5), 1, 3);
170     BOOST_TEST_EQ(a.size(), 4);
171     BOOST_TEST_EQ(a.bin(-1).lower(), -std::numeric_limits<double>::infinity());
172     BOOST_TEST_EQ(a.value(0), 1);
173     BOOST_TEST_EQ(a.value(1), 1.5);
174     BOOST_TEST_EQ(a.value(2), 2);
175     BOOST_TEST_EQ(a.value(3), 2.5);
176     BOOST_TEST_EQ(a.value(4), 3);
177     BOOST_TEST_EQ(a.bin(4).upper(), std::numeric_limits<double>::infinity());
178
179     axis::regular<> b(axis::step(0.5), 1, 3.1);
180     BOOST_TEST_EQ(a, b);
181   }
182
183   // with circular option
184   {
185     axis::circular<> a{4, 0, 1};
186     BOOST_TEST_EQ(a.bin(-1).lower(), a.bin(a.size() - 1).lower() - 1);
187     BOOST_TEST_EQ(a.index(-1.0 * 3), 0);
188     BOOST_TEST_EQ(a.index(0.0), 0);
189     BOOST_TEST_EQ(a.index(0.25), 1);
190     BOOST_TEST_EQ(a.index(0.5), 2);
191     BOOST_TEST_EQ(a.index(0.75), 3);
192     BOOST_TEST_EQ(a.index(1.0), 0);
193     BOOST_TEST_EQ(a.index(std::numeric_limits<double>::infinity()), 4);
194     BOOST_TEST_EQ(a.index(-std::numeric_limits<double>::infinity()), 4);
195     BOOST_TEST_EQ(a.index(std::numeric_limits<double>::quiet_NaN()), 4);
196   }
197
198   // with growth
199   {
200     axis::regular<double, def, def, axis::option::growth_t> a{1, 0, 1};
201     BOOST_TEST_EQ(a.size(), 1);
202     BOOST_TEST_EQ(a.update(0), std::make_pair(0, 0));
203     BOOST_TEST_EQ(a.size(), 1);
204     BOOST_TEST_EQ(a.update(1), std::make_pair(1, -1));
205     BOOST_TEST_EQ(a.size(), 2);
206     BOOST_TEST_EQ(a.value(0), 0);
207     BOOST_TEST_EQ(a.value(2), 2);
208     BOOST_TEST_EQ(a.update(-1), std::make_pair(0, 1));
209     BOOST_TEST_EQ(a.size(), 3);
210     BOOST_TEST_EQ(a.value(0), -1);
211     BOOST_TEST_EQ(a.value(3), 2);
212     BOOST_TEST_EQ(a.update(-10), std::make_pair(0, 9));
213     BOOST_TEST_EQ(a.size(), 12);
214     BOOST_TEST_EQ(a.value(0), -10);
215     BOOST_TEST_EQ(a.value(12), 2);
216     BOOST_TEST_EQ(a.update(std::numeric_limits<double>::infinity()),
217                   std::make_pair(a.size(), 0));
218     BOOST_TEST_EQ(a.update(std::numeric_limits<double>::quiet_NaN()),
219                   std::make_pair(a.size(), 0));
220     BOOST_TEST_EQ(a.update(-std::numeric_limits<double>::infinity()),
221                   std::make_pair(-1, 0));
222   }
223
224   // iterators
225   {
226     test_axis_iterator(axis::regular<>(5, 0, 1), 0, 5);
227     test_axis_iterator(axis::regular<double, def, def, axis::option::none_t>(5, 0, 1), 0,
228                        5);
229     test_axis_iterator(axis::circular<>(5, 0, 1), 0, 5);
230   }
231
232   // bin_type streamable
233   {
234     auto test = [](const auto& x, const char* ref) {
235       std::ostringstream os;
236       os << x;
237       BOOST_TEST_EQ(os.str(), std::string(ref));
238     };
239
240     auto a = axis::regular<>(2, 0, 1);
241     test(a.bin(0), "[0, 0.5)");
242   }
243
244   // null_type streamable
245   {
246     auto a = axis::regular<float, def, axis::null_type>(2, 0, 1);
247     BOOST_TEST_EQ(str(a), "regular(2, 0, 1, options=underflow | overflow)");
248   }
249
250   // shrink and rebin
251   {
252     using A = axis::regular<>;
253     auto a = A(5, 0, 5);
254     auto b = A(a, 1, 4, 1);
255     BOOST_TEST_EQ(b.size(), 3);
256     BOOST_TEST_EQ(b.value(0), 1);
257     BOOST_TEST_EQ(b.value(3), 4);
258     auto c = A(a, 0, 4, 2);
259     BOOST_TEST_EQ(c.size(), 2);
260     BOOST_TEST_EQ(c.value(0), 0);
261     BOOST_TEST_EQ(c.value(2), 4);
262     auto e = A(a, 1, 5, 2);
263     BOOST_TEST_EQ(e.size(), 2);
264     BOOST_TEST_EQ(e.value(0), 1);
265     BOOST_TEST_EQ(e.value(2), 5);
266   }
267
268   // shrink and rebin with circular option
269   {
270     using A = axis::circular<>;
271     auto a = A(4, 1, 5);
272     BOOST_TEST_THROWS(A(a, 1, 4, 1), std::invalid_argument);
273     BOOST_TEST_THROWS(A(a, 0, 3, 1), std::invalid_argument);
274     auto b = A(a, 0, 4, 2);
275     BOOST_TEST_EQ(b.size(), 2);
276     BOOST_TEST_EQ(b.value(0), 1);
277     BOOST_TEST_EQ(b.value(2), 5);
278   }
279
280   return boost::report_errors();
281 }