// Copyright 2015-2018 Hans Dembinski // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include #include "throw_exception.hpp" #include "utility_allocator.hpp" #include "utility_axis.hpp" #include "utility_str.hpp" int main() { using namespace boost::histogram; namespace tr = axis::transform; { (void)axis::variant<>{}; } { using meta_type = std::vector; using variant_type = axis::variant, axis::category>; auto a = variant_type{axis::integer(0, 2, "foo")}; BOOST_TEST_EQ(a.index(-10), -1); BOOST_TEST_EQ(a.index(-1), -1); BOOST_TEST_EQ(a.index(0), 0); BOOST_TEST_EQ(a.index(0.5), 0); BOOST_TEST_EQ(a.index(1), 1); BOOST_TEST_EQ(a.index(2), 2); BOOST_TEST_EQ(a.index(10), 2); BOOST_TEST_EQ(a.bin(-1).lower(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(a.bin(a.size()).upper(), std::numeric_limits::infinity()); BOOST_TEST_EQ(a.bin(-10).lower(), -std::numeric_limits::infinity()); BOOST_TEST_EQ(a.bin(a.size() + 10).upper(), std::numeric_limits::infinity()); BOOST_TEST_EQ(a.metadata(), "foo"); a.metadata() = "bar"; BOOST_TEST_EQ(static_cast(a).metadata(), "bar"); BOOST_TEST_EQ(a.options(), axis::option::underflow | axis::option::overflow); a = axis::category({"A", "B"}, {1, 2, 3}); BOOST_TEST_EQ(a.index("A"), 0); BOOST_TEST_EQ(a.index("B"), 1); BOOST_TEST_THROWS(a.metadata(), std::runtime_error); BOOST_TEST_THROWS(static_cast(a).metadata(), std::runtime_error); BOOST_TEST_EQ(a.options(), axis::option::overflow_t::value); } // axis::variant with pointers { using A = axis::integer<>; using B = axis::regular<>; auto a = A(1, 5, "foo"); auto b = B(3, 1, 5, "bar"); axis::variant r1(&a); BOOST_TEST_EQ(r1, a); BOOST_TEST_NE(r1, A(2, 4)); BOOST_TEST_NE(r1, b); BOOST_TEST_EQ(r1.size(), 4); BOOST_TEST_EQ(r1.value(0), 1); BOOST_TEST_EQ(r1.metadata(), a.metadata()); BOOST_TEST_EQ(r1.options(), a.options()); // change original through r1 axis::get(r1).metadata() = "bar"; BOOST_TEST_EQ(a.metadata(), "bar"); r1 = &b; BOOST_TEST_EQ(r1, b); axis::variant r2(static_cast(&b)); BOOST_TEST_EQ(r2, b); BOOST_TEST_NE(r2, B(4, 1, 5)); BOOST_TEST_NE(r2, a); BOOST_TEST_EQ(r2.size(), 3); BOOST_TEST_EQ(r2.value(0), 1); BOOST_TEST_EQ(r2.metadata(), "bar"); b.metadata() = "baz"; BOOST_TEST_EQ(r2.metadata(), "baz"); } // axis::variant copyable { axis::variant> a1(axis::regular<>(2, -1, 1)); axis::variant> a2(a1); BOOST_TEST_EQ(a1, a2); axis::variant> a3; BOOST_TEST_NE(a3, a1); a3 = a1; BOOST_TEST_EQ(a3, a1); axis::variant> a4(axis::regular<>(3, -2, 2)); axis::variant, axis::integer<>> a5(a4); BOOST_TEST_EQ(a4, a5); axis::variant> a6; a6 = a1; BOOST_TEST_EQ(a6, a1); axis::variant, axis::integer<>> a7(axis::integer<>(0, 2)); BOOST_TEST_THROWS(axis::variant> a8(a7), std::runtime_error); BOOST_TEST_THROWS(a4 = a7, std::runtime_error); } // axis::variant movable { axis::variant> a(axis::regular<>(2, -1, 1)); axis::variant> r(a); axis::variant> b(std::move(a)); BOOST_TEST_EQ(b, r); axis::variant> c; BOOST_TEST_NE(c, b); c = std::move(b); BOOST_TEST(c == r); } // axis::variant streamable { auto test = [](auto&& a, const char* ref) { using T = std::decay_t; axis::variant axis(std::move(a)); BOOST_TEST_CSTR_EQ(str(axis).c_str(), ref); }; test(axis::regular<>(2, -1, 1, "regular1"), "regular(2, -1, 1, metadata=\"regular1\", options=underflow | overflow)"); struct user_defined {}; const auto ref = "integer(-1, 1, metadata=" + detail::type_name() + ", options=none)"; test(axis::integer(-1, 1), ref.c_str()); } // bin_type operator<< { auto test = [](auto&& a, const char* ref) { using T = std::decay_t; axis::variant axis(std::move(a)); BOOST_TEST_CSTR_EQ(str(axis.bin(0)).c_str(), ref); }; test(axis::regular<>(2, 1, 2), "[1, 1.5)"); test(axis::category<>({1, 2}), "1"); } // axis::variant operator== { enum { A, B, C }; using variant = axis::variant, axis::regular, axis::category<>, axis::integer<>>; std::vector axes; axes.push_back(axis::regular<>{2, -1, 1}); axes.push_back(axis::regular(tr::pow(0.5), 2, 1, 4)); axes.push_back(axis::category<>({A, B, C})); axes.push_back(axis::integer<>{-1, 1}); for (const auto& a : axes) { BOOST_TEST(!(a == variant())); BOOST_TEST_EQ(a, variant(a)); } BOOST_TEST_NOT(axes == std::vector()); BOOST_TEST(axes == std::vector(axes)); } // axis::variant with axis that has incompatible bin type { auto a = axis::variant>( axis::category({"A", "B", "C"})); BOOST_TEST_THROWS(a.bin(0), std::runtime_error); auto b = axis::variant>(axis::category({2, 1, 3})); BOOST_TEST_EQ(b.bin(0), 2); BOOST_TEST_EQ(b.bin(0).lower(), b.bin(0).upper()); // lower == upper for bin without interval } // axis::variant support for user-defined axis types { struct minimal_axis { int index(int x) const { return x % 2; } int size() const { return 2; } }; axis::variant> axis; BOOST_TEST_EQ(axis.index(0), 0); BOOST_TEST_EQ(axis.index(9), 1); BOOST_TEST_EQ(axis.size(), 2); BOOST_TEST_EQ(axis.metadata(), axis::null_type{}); BOOST_TEST_CSTR_EQ(str(axis).c_str(), ""); BOOST_TEST_THROWS(axis.value(0), std::runtime_error); axis = axis::category({"A", "B"}, "category"); BOOST_TEST_EQ(axis.index("B"), 1); BOOST_TEST_THROWS(axis.value(0), std::runtime_error); } // vector of axes with custom allocators { using M = std::vector>; using T1 = axis::regular; using T2 = axis::integer; using T3 = axis::category>; using axis_type = axis::variant; // no heap allocation using axes_type = std::vector>; tracing_allocator_db db; { auto a = tracing_allocator(db); axes_type axes(a); axes.reserve(3); axes.emplace_back(T1(1, 0, 1, M(3, 'c', a))); axes.emplace_back(T2(0, 4)); axes.emplace_back(T3({1, 2, 3, 4, 5}, {}, a)); } // 3 axis::variant objects BOOST_TEST_EQ(db.at().first, 0); BOOST_TEST_EQ(db.at().second, 3); // label of T1 BOOST_TEST_EQ(db.at().first, 0); BOOST_TEST_EQ(db.at().second, 3); // T3 allocates storage for long array BOOST_TEST_EQ(db.at().first, 0); BOOST_TEST_EQ(db.at().second, 5); } // testing pass-through versions of get { axis::regular<> a(10, 0, 1); axis::integer<> b(0, 3); const auto& ta = axis::get>(a); BOOST_TEST_EQ(ta, a); const auto* tb = axis::get_if>(&b); BOOST_TEST_EQ(tb, &b); const auto* tc = axis::get_if>(&b); BOOST_TEST_EQ(tc, nullptr); } // iterators test_axis_iterator(axis::variant>(axis::regular<>(5, 0, 1)), 0, 5); return boost::report_errors(); }