• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015-2018 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/is_same.hpp>
8 #include <boost/core/lightweight_test.hpp>
9 #include <boost/core/lightweight_test_trait.hpp>
10 #include <boost/histogram/axis.hpp>
11 #include <boost/histogram/axis/ostream.hpp>
12 #include <boost/histogram/detail/detect.hpp>
13 #include <boost/histogram/histogram.hpp>
14 #include <boost/histogram/ostream.hpp>
15 #include <boost/throw_exception.hpp>
16 #include <string>
17 #include <vector>
18 #include "dummy_storage.hpp"
19 #include "std_ostream.hpp"
20 #include "throw_exception.hpp"
21 #include "utility_histogram.hpp"
22 
23 using namespace boost::histogram;
24 
25 template <typename Tag>
run_tests()26 void run_tests() {
27   // arithmetic operators
28   {
29     auto a = make(Tag(), axis::integer<>(0, 2));
30     auto b = a;
31     a.at(-1) = 2;
32     a.at(0) = 1;
33     b.at(-1) = 1;
34     b.at(1) = 1;
35     b.at(2) = 3;
36 
37     auto c = a + b;
38     BOOST_TEST_EQ(c.at(-1), 3);
39     BOOST_TEST_EQ(c.at(0), 1);
40     BOOST_TEST_EQ(c.at(1), 1);
41     BOOST_TEST_EQ(c.at(2), 3);
42 
43     c += b;
44     BOOST_TEST_EQ(c.at(-1), 4);
45     BOOST_TEST_EQ(c.at(0), 1);
46     BOOST_TEST_EQ(c.at(1), 2);
47     BOOST_TEST_EQ(c.at(2), 6);
48 
49     auto d = a + b + c;
50     BOOST_TEST_TRAIT_SAME(decltype(d), decltype(a));
51     BOOST_TEST_EQ(d.at(-1), 7);
52     BOOST_TEST_EQ(d.at(0), 2);
53     BOOST_TEST_EQ(d.at(1), 3);
54     BOOST_TEST_EQ(d.at(2), 9);
55 
56     auto d2 = d - a - b - c;
57     BOOST_TEST_TRAIT_SAME(decltype(d2), decltype(a));
58     BOOST_TEST_EQ(d2.at(-1), 0);
59     BOOST_TEST_EQ(d2.at(0), 0);
60     BOOST_TEST_EQ(d2.at(1), 0);
61     BOOST_TEST_EQ(d2.at(2), 0);
62 
63     d2 -= a;
64     BOOST_TEST_EQ(d2.at(-1), -2);
65     BOOST_TEST_EQ(d2.at(0), -1);
66     BOOST_TEST_EQ(d2.at(1), 0);
67     BOOST_TEST_EQ(d2.at(2), 0);
68 
69     auto d3 = d;
70     d3 *= d;
71     BOOST_TEST_EQ(d3.at(-1), 49);
72     BOOST_TEST_EQ(d3.at(0), 4);
73     BOOST_TEST_EQ(d3.at(1), 9);
74     BOOST_TEST_EQ(d3.at(2), 81);
75 
76     auto d4 = d3 * (1 * d); // converted return type
77     BOOST_TEST_TRAIT_FALSE((boost::core::is_same<decltype(d4), decltype(d3)>));
78     BOOST_TEST_EQ(d4.at(0), 8);
79     BOOST_TEST_EQ(d4.at(1), 27);
80     d4 /= d;
81     BOOST_TEST_EQ(d4.at(0), 4);
82     BOOST_TEST_EQ(d4.at(1), 9);
83     auto d5 = d4 / d;
84     BOOST_TEST_EQ(d5.at(0), 2);
85     BOOST_TEST_EQ(d5.at(1), 3);
86 
87     auto e = 3 * a; // converted return type
88     auto f = b * 2; // converted return type
89     BOOST_TEST_TRAIT_FALSE((boost::core::is_same<decltype(e), decltype(a)>));
90     BOOST_TEST_TRAIT_FALSE((boost::core::is_same<decltype(f), decltype(a)>));
91     BOOST_TEST_EQ(e.at(0), 3);
92     BOOST_TEST_EQ(e.at(1), 0);
93     BOOST_TEST_EQ(f.at(0), 0);
94     BOOST_TEST_EQ(f.at(1), 2);
95     auto r = 1.0 * a;
96     r += b;
97     r += e;
98     BOOST_TEST_EQ(r.at(0), 4);
99     BOOST_TEST_EQ(r.at(1), 1);
100     BOOST_TEST_EQ(r, a + b + 3 * a);
101     auto s = r / 4;
102     r /= 4;
103     BOOST_TEST_EQ(r.at(0), 1);
104     BOOST_TEST_EQ(r.at(1), 0.25);
105     BOOST_TEST_EQ(r, s);
106   }
107 
108   // arithmetic operators with mixed storage: unlimited vs. vector<unsigned>
109   {
110     auto ia = axis::integer<int, axis::null_type, axis::option::none_t>(0, 2);
111     auto a = make(Tag(), ia);
112     a(0, weight(2));
113     a(1, weight(2));
114     auto b = a;
115     auto c = make_s(Tag(), std::vector<int>(), ia);
116     c(0, weight(2));
117     c(1, weight(2));
118     auto a2 = a;
119     a2 += c;
120     BOOST_TEST_EQ(a2, (a + b));
121     auto a3 = a;
122     a3 *= c;
123     BOOST_TEST_EQ(a3, (a * b));
124     auto a4 = a;
125     a4 -= c;
126     BOOST_TEST_EQ(a4, (a - b));
127     auto a5 = a;
128     a5 /= c;
129     BOOST_TEST_EQ(a5, (a / b));
130   }
131 
132   // arithmetic operators with mixed storage: vector<unsigned char> vs. vector<unsigned>
133   {
134     auto ia = axis::integer<int, axis::null_type, axis::option::none_t>(0, 2);
135     auto a = make_s(Tag(), std::vector<unsigned long>{}, ia);
136     auto c = make_s(Tag(), std::vector<unsigned>(), ia);
137     a(0, weight(2u));
138     a(1, weight(2u));
139     auto b = a;
140     c(0, weight(2u));
141     c(1, weight(2u));
142     auto a2 = a;
143     a2 += c;
144     BOOST_TEST_EQ(a2, (a + b));
145     auto a3 = a;
146     a3 *= c;
147     BOOST_TEST_EQ(a3, (a * b));
148     auto a4 = a;
149     a4 -= c;
150     BOOST_TEST_EQ(a4, (a - b));
151     auto a5 = a;
152     a5 /= c;
153     BOOST_TEST_EQ(a5, (a / b));
154   }
155 
156   // add operators with weighted storage
157   {
158     auto ia = axis::integer<int, axis::null_type, axis::option::none_t>(0, 2);
159     auto a = make_s(Tag(), std::vector<accumulators::weighted_sum<>>(), ia);
160     auto b = make_s(Tag(), std::vector<accumulators::weighted_sum<>>(), ia);
161 
162     a(0);
163     BOOST_TEST_EQ(a.at(0).variance(), 1);
164     b(weight(3), 1);
165     BOOST_TEST_EQ(b.at(1).variance(), 9);
166     auto c = a;
167     c += b;
168     BOOST_TEST_EQ(c.at(0).value(), 1);
169     BOOST_TEST_EQ(c.at(0).variance(), 1);
170     BOOST_TEST_EQ(c.at(1).value(), 3);
171     BOOST_TEST_EQ(c.at(1).variance(), 9);
172     auto d = a;
173     d += b;
174     BOOST_TEST_EQ(d.at(0).value(), 1);
175     BOOST_TEST_EQ(d.at(0).variance(), 1);
176     BOOST_TEST_EQ(d.at(1).value(), 3);
177     BOOST_TEST_EQ(d.at(1).variance(), 9);
178 
179     // add unweighted histogram
180     auto e = make_s(Tag(), std::vector<int>(), ia);
181     std::fill(e.begin(), e.end(), 2);
182 
183     d += e;
184     BOOST_TEST_EQ(d.at(0).value(), 3);
185     BOOST_TEST_EQ(d.at(0).variance(), 3);
186     BOOST_TEST_EQ(d.at(1).value(), 5);
187     BOOST_TEST_EQ(d.at(1).variance(), 11);
188   }
189 
190   // merging add
191   {
192     using C = axis::category<int, use_default, axis::option::growth_t>;
193     using I = axis::integer<int, axis::null_type, axis::option::growth_t>;
194 
195     {
196       auto empty = std::initializer_list<int>{};
197       auto a = make(Tag(), C(empty, "foo"));
198       auto b = make(Tag(), C(empty, "foo"));
199       a(2);
200       a(1);
201       b(2);
202       b(3);
203       b(4);
204       a += b;
205       BOOST_TEST_EQ(a.axis(), C({2, 1, 3, 4}, "foo"));
206       BOOST_TEST_EQ(a[0], 2);
207       BOOST_TEST_EQ(a[1], 1);
208       BOOST_TEST_EQ(a[2], 1);
209       BOOST_TEST_EQ(a[3], 1);
210     }
211 
212     {
213       auto a = make(Tag(), C{1, 2}, I{4, 5});
214       auto b = make(Tag(), C{2, 3}, I{5, 6});
215 
216       std::fill(a.begin(), a.end(), 1);
217       std::fill(b.begin(), b.end(), 1);
218 
219       a += b;
220 
221       BOOST_TEST_EQ(a.axis(0), (C{1, 2, 3}));
222       BOOST_TEST_EQ(a.axis(1), (I{4, 6}));
223       BOOST_TEST_EQ(a.at(0, 0), 1);
224       BOOST_TEST_EQ(a.at(1, 0), 1);
225       BOOST_TEST_EQ(a.at(2, 0), 0); // v=(3, 4) did not exist in a or b
226       BOOST_TEST_EQ(a.at(0, 1), 0); // v=(1, 5) did not exist in a or b
227       BOOST_TEST_EQ(a.at(1, 1), 1);
228       BOOST_TEST_EQ(a.at(2, 1), 1);
229     }
230 
231     {
232       using CI = axis::category<int, use_default, axis::option::growth_t>;
233       using CS = axis::category<std::string, use_default, axis::option::growth_t>;
234 
235       auto h1 = make(Tag{}, CI{}, CS{});
236       auto h2 = make(Tag{}, CI{}, CS{});
237       auto h3 = make(Tag{}, CI{}, CS{});
238 
239       h1(1, "b");
240       h1(2, "a");
241       h1(1, "a");
242       h1(2, "b");
243 
244       h2(2, "b");
245       h2(3, "b");
246       h2(4, "c");
247       h2(5, "c");
248 
249       h3(1, "b");
250       h3(2, "a");
251       h3(1, "a");
252       h3(2, "b");
253       h3(2, "b");
254       h3(3, "b");
255       h3(4, "c");
256       h3(5, "c");
257 
258       BOOST_TEST_EQ(h3, h1 + h2);
259     }
260 
261     {
262       // C2 is not growing and has overflow
263       using C2 = axis::category<int>;
264       auto a = make(Tag(), C{1, 2}, C2{4, 5});
265       auto b = make(Tag(), C{1, 2}, C2{5, 6});
266 
267       // axis C2 is incompatible
268       BOOST_TEST_THROWS(a += b, std::invalid_argument);
269 
270       std::fill(a.begin(), a.end(), 1);
271       b = a;
272       b(3, 4);
273       a += b;
274       BOOST_TEST_EQ(a.at(0, 0), 2);
275       BOOST_TEST_EQ(a.at(1, 0), 2);
276       BOOST_TEST_EQ(a.at(2, 0), 1);
277       BOOST_TEST_EQ(a.at(0, 1), 2);
278       BOOST_TEST_EQ(a.at(1, 1), 2);
279       BOOST_TEST_EQ(a.at(2, 1), 0);
280       BOOST_TEST_EQ(a.at(0, 2), 2);
281       BOOST_TEST_EQ(a.at(1, 2), 2);
282       BOOST_TEST_EQ(a.at(2, 2), 0);
283 
284       // incompatible labels
285       b.axis(0).metadata() = "foo";
286       BOOST_TEST_THROWS(a += b, std::invalid_argument);
287 
288       // incompatible axis types
289       auto c = make(Tag(), C{1, 2}, I{4, 6});
290       BOOST_TEST_THROWS(a += c, std::invalid_argument);
291     }
292   }
293 
294   // bad operations
295   {
296     auto a = make(Tag(), axis::regular<>(2, 0, 4));
297     auto b = make(Tag(), axis::regular<>(2, 0, 2));
298     BOOST_TEST_THROWS(a += b, std::invalid_argument);
299     BOOST_TEST_THROWS(a -= b, std::invalid_argument);
300     BOOST_TEST_THROWS(a *= b, std::invalid_argument);
301     BOOST_TEST_THROWS(a /= b, std::invalid_argument);
302     auto c = make(Tag(), axis::regular<>(2, 0, 2), axis::regular<>(2, 0, 4));
303     BOOST_TEST_THROWS(a += c, std::invalid_argument);
304   }
305 
306   // scaling
307   {
308     auto b = make_s(Tag{}, dummy_storage<double, true>{}, axis::integer<>(0, 1));
309     b(0);
310     BOOST_TEST_EQ(b[0], 1);
311     b *= 2; // intentionally does not do anything
312     BOOST_TEST_EQ(b[0], 1);
313 
314     auto c = make_s(Tag{}, dummy_storage<double, false>{}, axis::integer<>(0, 1));
315     c(0);
316     BOOST_TEST_EQ(c[0], 1);
317     c *= 2; // this calls *= on each element
318     BOOST_TEST_EQ(c[0], 2);
319 
320     using h1_t = decltype(
321         make_s(Tag{}, dummy_storage<unscaleable, false>{}, axis::integer<>(0, 1)));
322     BOOST_TEST_NOT((detail::has_operator_rmul<h1_t, double>::value));
323 
324     using h2_t = decltype(
325         make_s(Tag{}, dummy_storage<unscaleable, true>{}, axis::integer<>(0, 1)));
326     BOOST_TEST_NOT((detail::has_operator_rmul<h2_t, double>::value));
327   }
328 }
329 
main()330 int main() {
331   run_tests<static_tag>();
332   run_tests<dynamic_tag>();
333 
334   return boost::report_errors();
335 }
336