• 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/lightweight_test.hpp>
8 #include <boost/core/lightweight_test_trait.hpp>
9 #include <boost/histogram/axis.hpp>
10 #include <boost/histogram/axis/ostream.hpp>
11 #include <boost/histogram/detail/type_name.hpp>
12 #include <string>
13 #include <type_traits>
14 #include <vector>
15 #include "std_ostream.hpp"
16 #include "throw_exception.hpp"
17 #include "utility_allocator.hpp"
18 #include "utility_axis.hpp"
19 #include "utility_str.hpp"
20 
main()21 int main() {
22   using namespace boost::histogram;
23   namespace tr = axis::transform;
24 
25   {
26     (void)axis::variant<>{};
27   }
28 
29   {
30     using meta_type = std::vector<int>;
31     using variant_type =
32         axis::variant<axis::integer<double>, axis::category<std::string, meta_type>>;
33     auto a = variant_type{axis::integer<double>(0, 2, "foo")};
34     BOOST_TEST_EQ(a.index(-10), -1);
35     BOOST_TEST_EQ(a.index(-1), -1);
36     BOOST_TEST_EQ(a.index(0), 0);
37     BOOST_TEST_EQ(a.index(0.5), 0);
38     BOOST_TEST_EQ(a.index(1), 1);
39     BOOST_TEST_EQ(a.index(2), 2);
40     BOOST_TEST_EQ(a.index(10), 2);
41     BOOST_TEST_EQ(a.bin(-1).lower(), -std::numeric_limits<double>::infinity());
42     BOOST_TEST_EQ(a.bin(a.size()).upper(), std::numeric_limits<double>::infinity());
43     BOOST_TEST_EQ(a.bin(-10).lower(), -std::numeric_limits<double>::infinity());
44     BOOST_TEST_EQ(a.bin(a.size() + 10).upper(), std::numeric_limits<double>::infinity());
45     BOOST_TEST_EQ(a.metadata(), "foo");
46     a.metadata() = "bar";
47     BOOST_TEST_EQ(static_cast<const variant_type&>(a).metadata(), "bar");
48     BOOST_TEST_EQ(a.options(), axis::option::underflow | axis::option::overflow);
49 
50     a = axis::category<std::string, meta_type>({"A", "B"}, {1, 2, 3});
51     BOOST_TEST_EQ(a.index("A"), 0);
52     BOOST_TEST_EQ(a.index("B"), 1);
53     BOOST_TEST_THROWS(a.metadata(), std::runtime_error);
54     BOOST_TEST_THROWS(static_cast<const variant_type&>(a).metadata(), std::runtime_error);
55     BOOST_TEST_EQ(a.options(), axis::option::overflow_t::value);
56   }
57 
58   // axis::variant with pointers
59   {
60     using A = axis::integer<>;
61     using B = axis::regular<>;
62     auto a = A(1, 5, "foo");
63     auto b = B(3, 1, 5, "bar");
64     axis::variant<A*, B*> r1(&a);
65     BOOST_TEST_EQ(r1, a);
66     BOOST_TEST_NE(r1, A(2, 4));
67     BOOST_TEST_NE(r1, b);
68     BOOST_TEST_EQ(r1.size(), 4);
69     BOOST_TEST_EQ(r1.value(0), 1);
70     BOOST_TEST_EQ(r1.metadata(), a.metadata());
71     BOOST_TEST_EQ(r1.options(), a.options());
72     // change original through r1
73     r1.metadata() = "bar";
74     BOOST_TEST_EQ(a.metadata(), "bar");
75     r1 = &b;
76     BOOST_TEST_EQ(r1, b);
77 
78     axis::variant<const A*, const B*> r2(static_cast<const B*>(&b));
79     BOOST_TEST_EQ(r2, b);
80     BOOST_TEST_NE(r2, B(4, 1, 5));
81     BOOST_TEST_NE(r2, a);
82     BOOST_TEST_EQ(r2.size(), 3);
83     BOOST_TEST_EQ(r2.value(0), 1);
84     BOOST_TEST_EQ(r2.metadata(), "bar");
85     r2.metadata() = "baz"; // change original through r2
86     BOOST_TEST_EQ(b.metadata(), "baz");
87   }
88 
89   // axis::variant copyable
90   {
91     axis::variant<axis::regular<>> a1(axis::regular<>(2, -1, 1));
92     axis::variant<axis::regular<>> a2(a1);
93     BOOST_TEST_EQ(a1, a2);
94     axis::variant<axis::regular<>> a3;
95     BOOST_TEST_NE(a3, a1);
96     a3 = a1;
97     BOOST_TEST_EQ(a3, a1);
98     axis::variant<axis::regular<>> a4(axis::regular<>(3, -2, 2));
99     axis::variant<axis::regular<>, axis::integer<>> a5(a4);
100     BOOST_TEST_EQ(a4, a5);
101     axis::variant<axis::regular<>> a6;
102     a6 = a1;
103     BOOST_TEST_EQ(a6, a1);
104     axis::variant<axis::regular<>, axis::integer<>> a7(axis::integer<>(0, 2));
105     BOOST_TEST_THROWS(axis::variant<axis::regular<>> a8(a7), std::runtime_error);
106     BOOST_TEST_THROWS(a4 = a7, std::runtime_error);
107   }
108 
109   // axis::variant movable
110   {
111     axis::variant<axis::regular<>> a(axis::regular<>(2, -1, 1));
112     axis::variant<axis::regular<>> r(a);
113     axis::variant<axis::regular<>> b(std::move(a));
114     BOOST_TEST_EQ(b, r);
115     axis::variant<axis::regular<>> c;
116     BOOST_TEST_NE(c, b);
117     c = std::move(b);
118     BOOST_TEST(c == r);
119   }
120 
121   // axis::variant streamable
122   {
123     auto test = [](auto&& a, const char* ref) {
124       using T = std::decay_t<decltype(a)>;
125       axis::variant<T> axis(std::move(a));
126       BOOST_TEST_CSTR_EQ(str(axis).c_str(), ref);
127     };
128 
129     test(axis::regular<>{2, -1, 1, "foo"},
130          "regular(2, -1, 1, metadata=\"foo\", options=underflow | overflow)");
131 
132     test(axis::boolean<>{"bar"}, "boolean(metadata=\"bar\")");
133 
134     struct user_defined {};
135     const auto ref = "integer(-1, 1, metadata=" + detail::type_name<user_defined>() +
136                      ", options=none)";
137     test(axis::integer<int, user_defined, axis::option::none_t>(-1, 1), ref.c_str());
138   }
139 
140   // bin_type operator<<
141   {
142     auto test = [](auto&& a, const char* ref) {
143       using T = std::decay_t<decltype(a)>;
144       axis::variant<T> axis(std::move(a));
145       BOOST_TEST_CSTR_EQ(str(axis.bin(0)).c_str(), ref);
146     };
147 
148     test(axis::regular<>(2, 1, 2), "[1, 1.5)");
149     test(axis::category<>({1, 2}), "1");
150   }
151 
152   // axis::variant operator==
153   {
154     enum { A, B, C };
155     using variant =
156         axis::variant<axis::regular<>, axis::regular<double, axis::transform::pow>,
157                       axis::category<>, axis::integer<>, axis::boolean<>>;
158     std::vector<variant> axes;
159     axes.push_back(axis::regular<>{2, -1, 1});
160     axes.push_back(axis::regular<double, tr::pow>{tr::pow{0.5}, 2, 1, 4});
161     axes.push_back(axis::category<>{A, B, C});
162     axes.push_back(axis::integer<>{-1, 1});
163     axes.push_back(axis::boolean<>{});
164     for (const auto& a : axes) {
165       BOOST_TEST_NE(a, variant{});
166       BOOST_TEST_EQ(a, variant(a));
167     }
168     BOOST_TEST_NE(axes, std::vector<variant>{});
169     BOOST_TEST(axes == std::vector<variant>(axes));
170   }
171 
172   // axis::variant with axis that has incompatible bin type
173   {
174     auto a = axis::variant<axis::category<std::string>>{
175         axis::category<std::string>{"A", "B", "C"}};
176     BOOST_TEST_THROWS(a.bin(0), std::runtime_error);
177     auto b = axis::variant<axis::category<int>>{axis::category<int>{2, 1, 3}};
178     BOOST_TEST_EQ(b.bin(0), 2);
179     BOOST_TEST_EQ(b.bin(0).lower(),
180                   b.bin(0).upper()); // lower == upper for bin without interval
181   }
182 
183   // axis::variant support for user-defined axis types
184   {
185     struct minimal_axis {
186       int index(int x) const { return x % 2; }
187       int size() const { return 2; }
188     };
189 
190     axis::variant<minimal_axis, axis::category<std::string>> axis;
191     BOOST_TEST_EQ(axis.index(0), 0);
192     BOOST_TEST_EQ(axis.index(9), 1);
193     BOOST_TEST_EQ(axis.size(), 2);
194     BOOST_TEST_EQ(axis.metadata(), axis::null_type{});
195     BOOST_TEST_EQ(str(axis), detail::type_name<minimal_axis>());
196     BOOST_TEST_THROWS(axis.value(0), std::runtime_error);
197 
198     axis = axis::category<std::string>({"A", "B"}, "category");
199     BOOST_TEST_EQ(axis.index("B"), 1);
200     BOOST_TEST_THROWS(axis.value(0), std::runtime_error);
201   }
202 
203   // vector of axes with custom allocators
204   {
205     using M = std::vector<char, tracing_allocator<char>>;
206     using T1 = axis::regular<double, tr::id, M>;
207     using T2 = axis::integer<int, axis::null_type>;
208     using T3 = axis::category<long, axis::null_type, axis::option::overflow_t,
209                               tracing_allocator<long>>;
210     using axis_type = axis::variant<T1, T2, T3>; // no heap allocation
211     using axes_type = std::vector<axis_type, tracing_allocator<axis_type>>;
212 
213     tracing_allocator_db db;
214     {
215       auto a = tracing_allocator<char>(db);
216       axes_type axes(a);
217       axes.reserve(3);
218       axes.emplace_back(T1(1, 0, 1, M(3, 'c', a)));
219       axes.emplace_back(T2(0, 4));
220       axes.emplace_back(T3({1, 2, 3, 4, 5}, {}, a));
221     }
222     // 3 axis::variant objects
223     BOOST_TEST_EQ(db.at<axis_type>().first, 0);
224     BOOST_TEST_EQ(db.at<axis_type>().second, 3);
225 
226     // label of T1
227     BOOST_TEST_EQ(db.at<char>().first, 0);
228     BOOST_TEST_EQ(db.at<char>().second, 3);
229 
230     // T3 allocates storage for long array
231     BOOST_TEST_EQ(db.at<long>().first, 0);
232     BOOST_TEST_EQ(db.at<long>().second, 5);
233   }
234 
235   // testing pass-through versions of get
236   {
237     axis::regular<> a(10, 0, 1);
238     axis::integer<> b(0, 3);
239     const auto& ta = axis::get<axis::regular<>>(a);
240     BOOST_TEST_EQ(ta, a);
241     const auto* tb = axis::get_if<axis::integer<>>(&b);
242     BOOST_TEST_EQ(tb, &b);
243     const auto* tc = axis::get_if<axis::regular<>>(&b);
244     BOOST_TEST_EQ(tc, nullptr);
245   }
246 
247   // iterators
248   test_axis_iterator(axis::variant<axis::regular<>>(axis::regular<>(5, 0, 1)), 0, 5);
249 
250   return boost::report_errors();
251 }
252