axis_variant_test.cpp 8.4 KB

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