From 8200e3f2c55a9963dc53d634447a685bcfd3ab3f Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Sat, 27 Oct 2018 17:42:35 +0200 Subject: [PATCH] generialization of axis::variant --- CMakeLists.txt | 5 +- examples/guide_custom_modified_axis.cpp | 26 +-- include/boost/histogram/axis/category.hpp | 4 +- include/boost/histogram/axis/circular.hpp | 4 +- include/boost/histogram/axis/integer.hpp | 22 ++- ...nterval_view.hpp => interval_bin_view.hpp} | 12 +- .../histogram/axis/ostream_operators.hpp | 48 +++-- .../histogram/axis/polymorphic_bin_view.hpp | 67 +++++++ include/boost/histogram/axis/regular.hpp | 8 +- .../{value_view.hpp => value_bin_view.hpp} | 14 +- include/boost/histogram/axis/variable.hpp | 4 +- include/boost/histogram/axis/variant.hpp | 47 +++-- include/boost/histogram/detail/meta.hpp | 5 +- include/boost/histogram/histogram_fwd.hpp | 2 +- test/Jamfile | 9 +- test/axis_category_test.cpp | 65 +++++++ test/axis_circular_test.cpp | 56 ++++++ test/axis_integer_test.cpp | 79 +++++++++ test/axis_test.cpp | 166 ------------------ test/axis_variable_test.cpp | 60 +++++++ test/axis_variant_test.cpp | 78 +++++--- 21 files changed, 518 insertions(+), 263 deletions(-) rename include/boost/histogram/axis/{interval_view.hpp => interval_bin_view.hpp} (70%) create mode 100644 include/boost/histogram/axis/polymorphic_bin_view.hpp rename include/boost/histogram/axis/{value_view.hpp => value_bin_view.hpp} (63%) create mode 100644 test/axis_category_test.cpp create mode 100644 test/axis_circular_test.cpp create mode 100644 test/axis_integer_test.cpp delete mode 100644 test/axis_test.cpp create mode 100644 test/axis_variable_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 437e75b5..11cd910b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,8 +81,11 @@ endfunction() compiled_test(test/adaptive_storage_test.cpp) compiled_test(test/array_storage_test.cpp) -compiled_test(test/axis_test.cpp) compiled_test(test/axis_regular_test.cpp) +compiled_test(test/axis_circular_test.cpp) +compiled_test(test/axis_variable_test.cpp) +compiled_test(test/axis_integer_test.cpp) +compiled_test(test/axis_category_test.cpp) compiled_test(test/axis_variant_test.cpp) compiled_test(test/detail_test.cpp) compiled_test(test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp) diff --git a/examples/guide_custom_modified_axis.cpp b/examples/guide_custom_modified_axis.cpp index b09c6ef1..d146e4e8 100644 --- a/examples/guide_custom_modified_axis.cpp +++ b/examples/guide_custom_modified_axis.cpp @@ -1,7 +1,8 @@ //[ guide_custom_modified_axis #include -#include +#include +#include namespace bh = boost::histogram; @@ -18,22 +19,25 @@ struct custom_axis : public bh::axis::integer<> { }; int main() { - auto h = bh::make_histogram(custom_axis(0, 3)); + auto h = bh::make_histogram(custom_axis(3, 6)); h("-10"); - h("0"); - h("1"); + h("3"); + h("4"); h("9"); + std::ostringstream os; for (auto xi : h.axis()) { - std::cout << "bin " << xi.idx() << " [" << xi.lower() << ", " - << xi.upper() << ") " << h.at(xi) << std::endl; + os << "bin " << xi.idx() + << " [" << xi.lower() << ", " << xi.upper() << ") " + << h.at(xi) << "\n"; } - /* prints: - bin 0 [0, 1) 1 - bin 1 [1, 2] 1 - bin 2 [2, 3] 0 - */ + std::cout << os.str() << std::endl; + + assert(os.str() == + "bin 0 [3, 4) 1\n" + "bin 1 [4, 5) 1\n" + "bin 2 [5, 6) 0\n"); } //] diff --git a/include/boost/histogram/axis/category.hpp b/include/boost/histogram/axis/category.hpp index f62b2f46..88c4915d 100644 --- a/include/boost/histogram/axis/category.hpp +++ b/include/boost/histogram/axis/category.hpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -132,7 +132,7 @@ public: return x_.first()[idx]; } - auto operator[](int idx) const noexcept { return value_view(idx, *this); } + auto operator[](int idx) const noexcept { return value_bin_view(idx, *this); } bool operator==(const category& o) const noexcept { return base_type::operator==(o) && diff --git a/include/boost/histogram/axis/circular.hpp b/include/boost/histogram/axis/circular.hpp index 434df99d..d4cd96a8 100644 --- a/include/boost/histogram/axis/circular.hpp +++ b/include/boost/histogram/axis/circular.hpp @@ -8,7 +8,7 @@ #define BOOST_HISTOGRAM_AXIS_CIRCULAR_HPP #include -#include +#include #include #include #include @@ -71,7 +71,7 @@ public: /// Returns axis value for fractional index. value_type value(value_type i) const noexcept { return phase_ + i * delta_; } - auto operator[](int idx) const noexcept { return interval_view(idx, *this); } + auto operator[](int idx) const noexcept { return interval_bin_view(idx, *this); } bool operator==(const circular& o) const noexcept { return base_type::operator==(o) && phase_ == o.phase_ && delta_ == o.delta_; diff --git a/include/boost/histogram/axis/integer.hpp b/include/boost/histogram/axis/integer.hpp index d2b30816..e36e396a 100644 --- a/include/boost/histogram/axis/integer.hpp +++ b/include/boost/histogram/axis/integer.hpp @@ -8,7 +8,8 @@ #define BOOST_HISTOGRAM_AXIS_INTEGER_HPP #include -#include +#include +#include #include #include #include @@ -30,7 +31,8 @@ class integer : public base, public iterator_mixin; using value_type = IntType; using metadata_type = MetaData; - using bin_type = interval_view; + using bin_view = std::conditional_t::value, + value_bin_view, interval_bin_view>; public: /** Construct over semi-open integer interval [start, stop). @@ -58,14 +60,24 @@ public: /// Returns axis value for index. value_type value(value_type i) const noexcept { - if (i < 0) { return std::numeric_limits::min(); } + if (i < 0) { + return detail::static_if>( + [](auto) { return std::numeric_limits::min(); }, + [](auto) { return -std::numeric_limits::infinity(); }, + 0 + ); + } if (i > static_cast(base_type::size())) { - return std::numeric_limits::max(); + return detail::static_if>( + [](auto) { return std::numeric_limits::max(); }, + [](auto) { return std::numeric_limits::infinity(); }, + 0 + ); } return min_ + i; } - bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); } + decltype(auto) operator[](int idx) const noexcept { return bin_view(idx, *this); } bool operator==(const integer& o) const noexcept { return base_type::operator==(o) && min_ == o.min_; diff --git a/include/boost/histogram/axis/interval_view.hpp b/include/boost/histogram/axis/interval_bin_view.hpp similarity index 70% rename from include/boost/histogram/axis/interval_view.hpp rename to include/boost/histogram/axis/interval_bin_view.hpp index 1787e03b..f98bf026 100644 --- a/include/boost/histogram/axis/interval_view.hpp +++ b/include/boost/histogram/axis/interval_bin_view.hpp @@ -4,17 +4,17 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP -#define BOOST_HISTOGRAM_AXIS_INTERVAL_VIEW_HPP +#ifndef BOOST_HISTOGRAM_AXIS_INTERVAL_BIN_VIEW_HPP +#define BOOST_HISTOGRAM_AXIS_INTERVAL_BIN_VIEW_HPP namespace boost { namespace histogram { namespace axis { template -class interval_view { +class interval_bin_view { public: - interval_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} + interval_bin_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} int idx() const noexcept { return idx_; } @@ -23,10 +23,10 @@ public: decltype(auto) center() const noexcept { return axis_.value(idx_ + 0.5); } decltype(auto) width() const noexcept { return upper() - lower(); } - bool operator==(const interval_view& rhs) const noexcept { + bool operator==(const interval_bin_view& rhs) const noexcept { return idx_ == rhs.idx_ && axis_ == rhs.axis_; } - bool operator!=(const interval_view& rhs) const noexcept { return !operator==(rhs); } + bool operator!=(const interval_bin_view& rhs) const noexcept { return !operator==(rhs); } explicit operator int() const noexcept { return idx_; } diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index 6773096d..b3e19ec3 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -13,9 +13,9 @@ #include #include #include -#include +#include #include -#include +#include #include #include #include @@ -61,18 +61,7 @@ void stream_metadata(OStream& os, const T& t) { template void stream_options(OStream& os, const axis::option_type o) { - os << ", options="; - switch (o) { - case axis::option_type::none: - os << "none"; - break; - case axis::option_type::overflow: - os << "overflow"; - break; - case axis::option_type::underflow_and_overflow: - os << "underflow_and_overflow"; - break; - } + os << ", options=" << o; } template @@ -97,6 +86,23 @@ void stream_value(OStream& os, const std::basic_string& t) { namespace axis { +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const axis::option_type o) { + switch (o) { + case axis::option_type::none: + os << "none"; + break; + case axis::option_type::overflow: + os << "overflow"; + break; + case axis::option_type::underflow_and_overflow: + os << "underflow_and_overflow"; + break; + } + return os; +} + template std::basic_ostream& operator<<(std::basic_ostream& os, const empty_metadata_type&) { @@ -105,18 +111,28 @@ std::basic_ostream& operator<<(std::basic_ostream& os, template std::basic_ostream& operator<<(std::basic_ostream& os, - const interval_view& i) { + const interval_bin_view& i) { os << "[" << i.lower() << ", " << i.upper() << ")"; return os; } template std::basic_ostream& operator<<(std::basic_ostream& os, - const value_view& i) { + const value_bin_view& i) { os << i.value(); return os; } +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const polymorphic_bin_view& i) { + if (i.is_continuous()) + os << "[" << i.lower() << ", " << i.upper() << ")"; + else + os << i.value(); + return os; +} + template std::basic_ostream& operator<<(std::basic_ostream& os, const regular& a) { diff --git a/include/boost/histogram/axis/polymorphic_bin_view.hpp b/include/boost/histogram/axis/polymorphic_bin_view.hpp new file mode 100644 index 00000000..29830581 --- /dev/null +++ b/include/boost/histogram/axis/polymorphic_bin_view.hpp @@ -0,0 +1,67 @@ +// Copyright 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) + +#ifndef BOOST_HISTOGRAM_AXIS_POLYMORPHIC_BIN_VIEW_HPP +#define BOOST_HISTOGRAM_AXIS_POLYMORPHIC_BIN_VIEW_HPP + +#include + +namespace boost { +namespace histogram { +namespace axis { + +template +class polymorphic_bin_view { + using value_type = detail::return_type; +public: + polymorphic_bin_view(int idx, const Axis& axis, bool is_continuous) + : idx_(idx), axis_(axis), is_continuous_(is_continuous) {} + + int idx() const noexcept { return idx_; } + + value_type value() const { + if (is_continuous_) + throw std::runtime_error("calling value() for continuous axis is ambiguous"); + return axis_.value(idx_); + } + value_type lower() const { + if (!is_continuous_) + throw std::runtime_error("cannot call lower() for discontinuous axis"); + return axis_.value(idx_); + } + value_type upper() const { + if (!is_continuous_) + throw std::runtime_error("cannot call upper() for discontinuous axis"); + return axis_.value(idx_ + 1); + } + value_type center() const { + if (!is_continuous_) + throw std::runtime_error("cannot call center() for discontinuous axis"); + return axis_.value(idx_ + 0.5); + } + template () - std::declval())> + value_type width() const { return upper() - lower(); } + + bool operator==(const polymorphic_bin_view& rhs) const noexcept { + return idx_ == rhs.idx_ && axis_ == rhs.axis_; + } + bool operator!=(const polymorphic_bin_view& rhs) const noexcept { return !operator==(rhs); } + + explicit operator int() const noexcept { return idx_; } + + bool is_continuous() const noexcept { return is_continuous_; } + +private: + const int idx_; + const Axis& axis_; + const bool is_continuous_; +}; + +} // namespace axis +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/axis/regular.hpp b/include/boost/histogram/axis/regular.hpp index 63800108..53c38f9e 100644 --- a/include/boost/histogram/axis/regular.hpp +++ b/include/boost/histogram/axis/regular.hpp @@ -8,7 +8,7 @@ #define BOOST_HISTOGRAM_AXIS_REGULAR_HPP #include -#include +#include #include #include #include @@ -103,7 +103,9 @@ public: , min_(this->forward(start)) , delta_((this->forward(stop) - this->forward(start)) / n) { if (!std::isfinite(min_) || !std::isfinite(delta_)) - throw std::invalid_argument("forward transform of lower or upper invalid"); + throw std::invalid_argument("forward transform of start or stop invalid"); + if (delta_ == 0) + throw std::invalid_argument("range of forward transformed axis is zero"); } /** Construct n bins over real range [begin, end). @@ -155,7 +157,7 @@ public: } /// Access bin at index - auto operator[](int idx) const noexcept { return interval_view(idx, *this); } + auto operator[](int idx) const noexcept { return interval_bin_view(idx, *this); } bool operator==(const regular& o) const noexcept { return base_type::operator==(o) && transform_type::operator==(o) && min_ == o.min_ && diff --git a/include/boost/histogram/axis/value_view.hpp b/include/boost/histogram/axis/value_bin_view.hpp similarity index 63% rename from include/boost/histogram/axis/value_view.hpp rename to include/boost/histogram/axis/value_bin_view.hpp index b24ce606..ed3e8dff 100644 --- a/include/boost/histogram/axis/value_view.hpp +++ b/include/boost/histogram/axis/value_bin_view.hpp @@ -4,26 +4,28 @@ // (See accompanying file LICENSE_1_0.txt // or copy at http://www.boost.org/LICENSE_1_0.txt) -#ifndef BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP -#define BOOST_HISTOGRAM_AXIS_VALUE_VIEW_HPP +#ifndef BOOST_HISTOGRAM_AXIS_VALUE_BIN_VIEW_HPP +#define BOOST_HISTOGRAM_AXIS_VALUE_BIN_VIEW_HPP + +#include namespace boost { namespace histogram { namespace axis { template -class value_view { +class value_bin_view { public: - value_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} + value_bin_view(int idx, const Axis& axis) : idx_(idx), axis_(axis) {} int idx() const noexcept { return idx_; } decltype(auto) value() const { return axis_.value(idx_); } - bool operator==(const value_view& rhs) const noexcept { + bool operator==(const value_bin_view& rhs) const noexcept { return idx_ == rhs.idx_ && axis_ == rhs.axis_; } - bool operator!=(const value_view& rhs) const noexcept { return !operator==(rhs); } + bool operator!=(const value_bin_view& rhs) const noexcept { return !operator==(rhs); } explicit operator int() const noexcept { return idx_; } diff --git a/include/boost/histogram/axis/variable.hpp b/include/boost/histogram/axis/variable.hpp index e358b19b..787a65d4 100644 --- a/include/boost/histogram/axis/variable.hpp +++ b/include/boost/histogram/axis/variable.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -158,7 +158,7 @@ public: return (1.0 - z) * x_.first()[k] + z * x_.first()[k + 1]; } - auto operator[](int idx) const noexcept { return interval_view(idx, *this); } + auto operator[](int idx) const noexcept { return interval_bin_view(idx, *this); } bool operator==(const variable& o) const noexcept { return base_type::operator==(o) && diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index 5b35432e..9a70274e 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -21,6 +21,7 @@ #include #include #include +#include namespace boost { namespace histogram { @@ -44,7 +45,6 @@ namespace axis { template class variant : private boost::variant, public iterator_mixin> { using base_type = boost::variant; - using bin_type = interval_view; using first_bounded_type = mp11::mp_first; using metadata_type = detail::rm_cvref()))>; @@ -141,22 +141,24 @@ public: *this); } - // Only works for axes with compatible call signature - // and will throw a invalid_argument exception otherwise - int operator()(double x) const { + // Will throw invalid_argument exception if axis has incompatible call signature + template + int operator()(Us... x) const { + auto&& args = std::forward_as_tuple(std::forward(x)...); return visit( - [x](const auto& a) { - using T = detail::rm_cvref; - using args = axis::traits::args; - return detail::static_if_c<( - mp11::mp_size::value == 1 && - std::is_convertible>::value)>( - [x](const auto& a) -> int { return a(x); }, - [](const auto& a) -> int { + [&args](const auto& a) { + using A = detail::rm_cvref; + using args_t = std::tuple; + using expected_args_t = axis::traits::args; + return detail::static_if>( + [&args](const auto& a) -> int { return mp11::tuple_apply(a, args); }, + [](const auto&) -> int { throw std::invalid_argument(detail::cat( - "cannot convert double to ", - boost::core::demangled_name(BOOST_CORE_TYPEID(args)), " for ", - boost::core::demangled_name(BOOST_CORE_TYPEID(decltype(a))), + "cannot convert ", + boost::core::demangled_name(BOOST_CORE_TYPEID(args_t)), + " to ", + boost::core::demangled_name(BOOST_CORE_TYPEID(expected_args_t)), " for ", + boost::core::demangled_name(BOOST_CORE_TYPEID(A)), "; use boost::histogram::axis::get to obtain a reference " "of this axis type")); }, @@ -202,9 +204,16 @@ public: *this); } - // this only works for axes with compatible bin type - // and will throw a runtime_error otherwise - bin_type operator[](const int idx) const { return bin_type(idx, *this); } + decltype(auto) operator[](const int idx) const { + const bool is_continuous = visit( + [](const auto& a) { + using A = detail::rm_cvref; + using T = detail::arg_type; + return !std::is_integral::value; + }, + *this); + return polymorphic_bin_view(idx, *this, is_continuous); + } bool operator==(const variant& rhs) const { return base_type::operator==(static_cast(rhs)); diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index 3a78e957..efec949c 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -133,9 +133,12 @@ struct is_axis_variant_impl> : std::true_type {}; template using is_axis_variant = typename is_axis_variant_impl::type; +template +using is_axis_or_axis_variant = mp11::mp_or, is_axis_variant>; + template using is_axis_vector = mp11::mp_all, - is_axis>>>; + is_axis_or_axis_variant>>>; struct static_container_tag {}; struct iterable_container_tag {}; diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index f9988790..eee3070c 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -43,7 +43,7 @@ template class variable; -template +template class integer; template , diff --git a/test/Jamfile b/test/Jamfile index 0600402a..9b105292 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -15,13 +15,18 @@ project histogram-test alias run-tests : [ run adaptive_storage_serialization_test.cpp /boost/serialization//boost_serialization/static ] - [ run histogram_serialization_test.cpp /boost/serialization//boost_serialization/static ] [ run adaptive_storage_test.cpp ] [ run array_storage_test.cpp ] - [ run axis_test.cpp ] + [ run axis_regular_test.cpp ] + [ run axis_circular_test.cpp ] + [ run axis_variable_test.cpp ] + [ run axis_integer_test.cpp ] + [ run axis_category_test.cpp ] + [ run axis_variant_test.cpp ] [ run detail_test.cpp ] [ run histogram_dynamic_test.cpp ] [ run histogram_mixed_test.cpp ] + [ run histogram_serialization_test.cpp /boost/serialization//boost_serialization/static ] [ run histogram_test.cpp ] [ run index_mapper_test.cpp ] [ run meta_test.cpp ] diff --git a/test/axis_category_test.cpp b/test/axis_category_test.cpp new file mode 100644 index 00000000..6a3c15b5 --- /dev/null +++ b/test/axis_category_test.cpp @@ -0,0 +1,65 @@ +// 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 "utility.hpp" + +using namespace boost::histogram; + +int main() { + // bad_ctors + { + auto empty = std::vector(0); + BOOST_TEST_THROWS((axis::category<>(empty)), std::invalid_argument); + } + + // axis::category + { + std::string A("A"), B("B"), C("C"), other; + axis::category a({A, B, C}); + axis::category b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = axis::category{{B, A, C}}; + BOOST_TEST_NE(a, b); + b = a; + b = b; + BOOST_TEST_EQ(a, b); + axis::category c = std::move(b); + BOOST_TEST_EQ(c, a); + BOOST_TEST_NE(b, a); + axis::category d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + BOOST_TEST_EQ(a.size(), 3); + BOOST_TEST_EQ(a(A), 0); + BOOST_TEST_EQ(a(B), 1); + BOOST_TEST_EQ(a(C), 2); + BOOST_TEST_EQ(a(other), 3); + BOOST_TEST_EQ(a.value(0), A); + BOOST_TEST_EQ(a.value(1), B); + BOOST_TEST_EQ(a.value(2), C); + BOOST_TEST_THROWS(a.value(3), std::out_of_range); + } + + // iterators + { + test_axis_iterator(axis::category<>({3, 1, 2}, ""), 0, 3); + test_axis_iterator(axis::category({"A", "B"}, ""), 0, 2); + } + + return boost::report_errors(); +} diff --git a/test/axis_circular_test.cpp b/test/axis_circular_test.cpp new file mode 100644 index 00000000..97a39086 --- /dev/null +++ b/test/axis_circular_test.cpp @@ -0,0 +1,56 @@ +// 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 "utility.hpp" + +using namespace boost::histogram; + +int main() { + // bad_ctor + { + BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument); + BOOST_TEST_THROWS(axis::circular<>(2, 0, -1), std::invalid_argument); + } + + // axis::circular + { + axis::circular<> a{4, 0, 1}; + BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - 1); + axis::circular<> b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = b; + BOOST_TEST_EQ(a, b); + axis::circular<> c = std::move(b); + BOOST_TEST_EQ(c, a); + axis::circular<> d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + BOOST_TEST_EQ(a(-1.0 * 3), 0); + BOOST_TEST_EQ(a(0.0), 0); + BOOST_TEST_EQ(a(0.25), 1); + BOOST_TEST_EQ(a(0.5), 2); + BOOST_TEST_EQ(a(0.75), 3); + BOOST_TEST_EQ(a(1.0), 0); + BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 4); + BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), 4); + BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); + } + + // iterators + { + test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); + } + + return boost::report_errors(); +} diff --git a/test/axis_integer_test.cpp b/test/axis_integer_test.cpp new file mode 100644 index 00000000..b45f73d7 --- /dev/null +++ b/test/axis_integer_test.cpp @@ -0,0 +1,79 @@ +// 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 +#include +#include +#include +#include "utility.hpp" + +using namespace boost::histogram; + +int main() { + // bad_ctor + { + BOOST_TEST_THROWS(axis::integer<>(1, 1), std::invalid_argument); + BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument); + } + + // axis::integer + { + axis::integer<> a{-1, 2}; + BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); + axis::integer<> b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = b; + BOOST_TEST_EQ(a, b); + axis::integer<> c = std::move(b); + BOOST_TEST_EQ(c, a); + axis::integer<> d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-2), -1); + BOOST_TEST_EQ(a(-1), 0); + BOOST_TEST_EQ(a(0), 1); + BOOST_TEST_EQ(a(1), 2); + BOOST_TEST_EQ(a(2), 3); + BOOST_TEST_EQ(a(10), 3); + } + + // axis::integer with int type + { + axis::integer a{-1, 2}; + BOOST_TEST_EQ(a[-2].value(), std::numeric_limits::min()); + BOOST_TEST_EQ(a[4].value(), std::numeric_limits::max()); + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-2), -1); + BOOST_TEST_EQ(a(-1), 0); + BOOST_TEST_EQ(a(0), 1); + BOOST_TEST_EQ(a(1), 2); + BOOST_TEST_EQ(a(2), 3); + BOOST_TEST_EQ(a(10), 3); + } + + // iterators + { + test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); + } + + return boost::report_errors(); +} diff --git a/test/axis_test.cpp b/test/axis_test.cpp deleted file mode 100644 index 53550c99..00000000 --- a/test/axis_test.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2015-2017 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 -#include -#include -#include -#include -#include "utility.hpp" - -using namespace boost::histogram; - -int main() { - // bad_ctors - { - auto empty = std::vector(0); - BOOST_TEST_THROWS(axis::circular<>(0), std::invalid_argument); - BOOST_TEST_THROWS((axis::variable<>(empty)), std::invalid_argument); - BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument); - BOOST_TEST_THROWS(axis::integer<>(1, -1), std::invalid_argument); - BOOST_TEST_THROWS((axis::category<>(empty)), std::invalid_argument); - } - - // axis::circular - { - axis::circular<> a{4, 0, 1}; - BOOST_TEST_EQ(a[-1].lower(), a[a.size() - 1].lower() - 1); - axis::circular<> b; - BOOST_TEST_NE(a, b); - b = a; - BOOST_TEST_EQ(a, b); - b = b; - BOOST_TEST_EQ(a, b); - axis::circular<> c = std::move(b); - BOOST_TEST_EQ(c, a); - axis::circular<> d; - BOOST_TEST_NE(c, d); - d = std::move(c); - BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a(-1.0 * 3), 0); - BOOST_TEST_EQ(a(0.0), 0); - BOOST_TEST_EQ(a(0.25), 1); - BOOST_TEST_EQ(a(0.5), 2); - BOOST_TEST_EQ(a(0.75), 3); - BOOST_TEST_EQ(a(1.0), 0); - BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 4); - BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), 4); - BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); - } - - // axis::variable - { - axis::variable<> a{-1, 0, 1}; - BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); - BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); - axis::variable<> b; - BOOST_TEST_NE(a, b); - b = a; - BOOST_TEST_EQ(a, b); - b = b; - BOOST_TEST_EQ(a, b); - axis::variable<> c = std::move(b); - BOOST_TEST_EQ(c, a); - BOOST_TEST_NE(b, a); - axis::variable<> d; - BOOST_TEST_NE(c, d); - d = std::move(c); - BOOST_TEST_EQ(d, a); - axis::variable<> e{-2, 0, 2}; - BOOST_TEST_NE(a, e); - BOOST_TEST_EQ(a(-10), -1); - BOOST_TEST_EQ(a(-1), 0); - BOOST_TEST_EQ(a(0), 1); - BOOST_TEST_EQ(a(1), 2); - BOOST_TEST_EQ(a(10), 2); - BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), -1); - BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 2); - BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 2); - } - - // axis::integer - { - axis::integer<> a{-1, 2}; - BOOST_TEST_EQ(a[-1].lower(), std::numeric_limits::min()); - BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::max()); - axis::integer<> b; - BOOST_TEST_NE(a, b); - b = a; - BOOST_TEST_EQ(a, b); - b = b; - BOOST_TEST_EQ(a, b); - axis::integer<> c = std::move(b); - BOOST_TEST_EQ(c, a); - axis::integer<> d; - BOOST_TEST_NE(c, d); - d = std::move(c); - BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a(-10), -1); - BOOST_TEST_EQ(a(-2), -1); - BOOST_TEST_EQ(a(-1), 0); - BOOST_TEST_EQ(a(0), 1); - BOOST_TEST_EQ(a(1), 2); - BOOST_TEST_EQ(a(2), 3); - BOOST_TEST_EQ(a(10), 3); - } - - // axis::category - { - std::string A("A"), B("B"), C("C"), other; - axis::category a({A, B, C}); - axis::category b; - BOOST_TEST_NE(a, b); - b = a; - BOOST_TEST_EQ(a, b); - b = axis::category{{B, A, C}}; - BOOST_TEST_NE(a, b); - b = a; - b = b; - BOOST_TEST_EQ(a, b); - axis::category c = std::move(b); - BOOST_TEST_EQ(c, a); - BOOST_TEST_NE(b, a); - axis::category d; - BOOST_TEST_NE(c, d); - d = std::move(c); - BOOST_TEST_EQ(d, a); - BOOST_TEST_EQ(a.size(), 3); - BOOST_TEST_EQ(a(A), 0); - BOOST_TEST_EQ(a(B), 1); - BOOST_TEST_EQ(a(C), 2); - BOOST_TEST_EQ(a(other), 3); - BOOST_TEST_EQ(a.value(0), A); - BOOST_TEST_EQ(a.value(1), B); - BOOST_TEST_EQ(a.value(2), C); - BOOST_TEST_THROWS(a.value(3), std::out_of_range); - } - - // iterators - { - enum { A, B, C }; - test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); - test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2); - test_axis_iterator(axis::integer<>(0, 4, ""), 0, 4); - test_axis_iterator(axis::category<>({A, B, C}, ""), 0, 3); - test_axis_iterator(axis::variant>(axis::regular<>(5, 0, 1)), 0, 5); - // BOOST_TEST_THROWS(axis::variant>(axis::category<>({A, B, - // C}))[0].lower(), - // std::runtime_error); - } - - return boost::report_errors(); -} diff --git a/test/axis_variable_test.cpp b/test/axis_variable_test.cpp new file mode 100644 index 00000000..719ef1f6 --- /dev/null +++ b/test/axis_variable_test.cpp @@ -0,0 +1,60 @@ +// Copyright 2015-2017 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 "utility.hpp" + +using namespace boost::histogram; + +int main() { + // bad_ctors + { + auto empty = std::vector(0); + BOOST_TEST_THROWS((axis::variable<>(empty)), std::invalid_argument); + BOOST_TEST_THROWS(axis::variable<>({1.0}), std::invalid_argument); + } + + // axis::variable + { + axis::variable<> a{-1, 0, 1}; + BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); + axis::variable<> b; + BOOST_TEST_NE(a, b); + b = a; + BOOST_TEST_EQ(a, b); + b = b; + BOOST_TEST_EQ(a, b); + axis::variable<> c = std::move(b); + BOOST_TEST_EQ(c, a); + BOOST_TEST_NE(b, a); + axis::variable<> d; + BOOST_TEST_NE(c, d); + d = std::move(c); + BOOST_TEST_EQ(d, a); + axis::variable<> e{-2, 0, 2}; + BOOST_TEST_NE(a, e); + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-1), 0); + BOOST_TEST_EQ(a(0), 1); + BOOST_TEST_EQ(a(1), 2); + BOOST_TEST_EQ(a(10), 2); + BOOST_TEST_EQ(a(-std::numeric_limits::infinity()), -1); + BOOST_TEST_EQ(a(std::numeric_limits::infinity()), 2); + BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 2); + } + + // iterators + { + test_axis_iterator(axis::variable<>({1, 2, 3}, ""), 0, 2); + } + + return boost::report_errors(); +} diff --git a/test/axis_variant_test.cpp b/test/axis_variant_test.cpp index 72459dcd..701484ff 100644 --- a/test/axis_variant_test.cpp +++ b/test/axis_variant_test.cpp @@ -17,11 +17,57 @@ #include #include #include +#include #include "utility.hpp" using namespace boost::histogram; int main() { + { + BOOST_TEST_THROWS(axis::integer<>(1, 1), std::invalid_argument); + } + + { + axis::variant< + axis::integer<>, + axis::category + > a{axis::integer<>(0, 2, "int")}; + BOOST_TEST_EQ(a(-10), -1); + BOOST_TEST_EQ(a(-1), -1); + BOOST_TEST_EQ(a(0), 0); + BOOST_TEST_EQ(a(0.5), 0); + BOOST_TEST_EQ(a(1), 1); + BOOST_TEST_EQ(a(2), 2); + BOOST_TEST_EQ(a(10), 2); + BOOST_TEST_EQ(a[-1].lower(), -std::numeric_limits::infinity()); + BOOST_TEST_EQ(a[a.size()].upper(), std::numeric_limits::infinity()); + BOOST_TEST_EQ(a.metadata(), std::string("int")); + BOOST_TEST_EQ(a.options(), axis::option_type::underflow_and_overflow); + + a = axis::category({"A", "B"}, "cat"); + BOOST_TEST_EQ(a("A"), 0); + BOOST_TEST_EQ(a("B"), 1); + // BOOST_TEST_EQ(a.metadata(), std::string("cat")); + // BOOST_TEST_EQ(a.options(), axis::option_type::overflow); + } + + // axis::variant support for minimal_axis + { + struct minimal_axis { + int operator()(double) const { return 0; } + unsigned size() const { return 1; } + }; + + axis::variant axis; + BOOST_TEST_EQ(axis(0), 0); + BOOST_TEST_EQ(axis(10), 0); + BOOST_TEST_EQ(axis.size(), 1); + BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error); + BOOST_TEST_THROWS(axis.value(0), std::runtime_error); + BOOST_TEST_TRAIT_TRUE( + (std::is_same)); + } + // axis::variant copyable { axis::variant> a1(axis::regular<>(2, -1, 1)); @@ -89,23 +135,6 @@ int main() { #endif } - // axis::variant support for minimal_axis - { - struct minimal_axis { - int operator()(double) const { return 0; } - unsigned size() const { return 1; } - }; - - axis::variant axis; - BOOST_TEST_EQ(axis(0), 0); - BOOST_TEST_EQ(axis(10), 0); - BOOST_TEST_EQ(axis.size(), 1); - BOOST_TEST_THROWS(std::ostringstream() << axis, std::runtime_error); - BOOST_TEST_THROWS(axis.value(0), std::runtime_error); - BOOST_TEST_TRAIT_TRUE( - (std::is_same)); - } - // bin_type streamable { auto test = [](const auto& x, const char* ref) { @@ -140,12 +169,16 @@ int main() { BOOST_TEST(axes == std::vector(axes)); } - // axis::variant value_to_index_failure + // axis::variant with unusual args { axis::variant> x = axis::category({"A", "B"}, "category"); - auto cx = axis::get>(x); - BOOST_TEST_EQ(cx("B"), 1); + BOOST_TEST_EQ(x("B"), 1); + } + + { + auto a = axis::variant>(axis::category<>({2, 1, 3})); + BOOST_TEST_THROWS(a[0].lower(), std::runtime_error); } // vector of axes with custom allocators @@ -197,5 +230,10 @@ int main() { #endif } + // iterators + { + test_axis_iterator(axis::variant>(axis::regular<>(5, 0, 1)), 0, 5); + } + return boost::report_errors(); }