diff --git a/CMakeLists.txt b/CMakeLists.txt index d05d7c5b..4ca96c16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required (VERSION 3.5) project(histogram CXX) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(MIN_BOOST_VERSION 1.66) diff --git a/examples/getting_started_listing_02.cpp b/examples/getting_started_listing_02.cpp index d5d020a6..ce95ee23 100644 --- a/examples/getting_started_listing_02.cpp +++ b/examples/getting_started_listing_02.cpp @@ -37,7 +37,7 @@ int main() { this is here the case for the category axis. */ using cas = bh::axis::category; - for (auto cbin : static_cast(h.axis(0))) { + for (auto cbin : bh::axis::get(h.axis(0))) { for (auto ybin : h.axis(2)) { // rows for (auto xbin : h.axis(1)) { // columns const auto v = h.at(cbin, xbin, ybin); diff --git a/examples/guide_custom_modified_axis.cpp b/examples/guide_custom_modified_axis.cpp index 60018e7c..b09c6ef1 100644 --- a/examples/guide_custom_modified_axis.cpp +++ b/examples/guide_custom_modified_axis.cpp @@ -26,7 +26,7 @@ int main() { for (auto xi : h.axis()) { std::cout << "bin " << xi.idx() << " [" << xi.lower() << ", " - << xi.upper() << ") " << h.at(xi).value() << std::endl; + << xi.upper() << ") " << h.at(xi) << std::endl; } /* prints: diff --git a/include/boost/histogram/axis/base.hpp b/include/boost/histogram/axis/base.hpp index c481f4b4..5190c822 100644 --- a/include/boost/histogram/axis/base.hpp +++ b/include/boost/histogram/axis/base.hpp @@ -47,8 +47,8 @@ class base } return *this; } - data(const metadata_type& m, int s, option_type o) - : metadata_type(m), size(s), opt(o) {} + data(metadata_type&& m, int s, option_type o) + : metadata_type(std::move(m)), size(s), opt(o) {} bool operator==(const data& rhs) const noexcept { return size == rhs.size && opt == rhs.opt && @@ -85,8 +85,8 @@ public: } protected: - base(unsigned size, const metadata_type& m, option_type opt) - : data_(m, size, opt) + base(unsigned size, metadata_type&& m, option_type opt) + : data_(std::move(m), size, opt) { if (size == 0) { throw std::invalid_argument("bins > 0 required"); } const auto max_index = static_cast(std::numeric_limits::max() diff --git a/include/boost/histogram/axis/ostream_operators.hpp b/include/boost/histogram/axis/ostream_operators.hpp index a8f36754..1ad43d28 100644 --- a/include/boost/histogram/axis/ostream_operators.hpp +++ b/include/boost/histogram/axis/ostream_operators.hpp @@ -23,8 +23,8 @@ namespace detail { template const char* to_string(const axis::transform::identity&) { return ""; } template const char* to_string(const axis::transform::log&) { return "_log"; } template const char* to_string(const axis::transform::sqrt&) { return "_sqrt"; } -template const char* to_string(const axis::transform::quantity&) { return "_quantity"; } template const char* to_string(const axis::transform::pow&) { return "_pow"; } +template const char* to_string(const axis::transform::quantity&) { return "_quantity"; } template void escape_string(OStream& os, const std::string& s) { @@ -68,8 +68,8 @@ void stream_transform(OStream& os, const axis::transform::pow& t) { os << ", power=" << t.power; } -template -void stream_transform(OStream& os, const axis::transform::quantity& t) { +template +void stream_transform(OStream& os, const axis::transform::quantity& t) { os << ", unit=" << t.unit; } diff --git a/include/boost/histogram/axis/traits.hpp b/include/boost/histogram/axis/traits.hpp index 9f281431..abd30b1b 100644 --- a/include/boost/histogram/axis/traits.hpp +++ b/include/boost/histogram/axis/traits.hpp @@ -9,39 +9,42 @@ #include #include -#include -#include namespace boost { namespace histogram { - namespace detail { -template -axis::option_type axis_traits_options_impl(std::true_type, const T& t) { - return t.options(); -} - -template -axis::option_type axis_traits_options_impl(std::false_type, const T&) { - return axis::option_type::none; -} + template + struct metadata_type_impl; + template + struct metadata_type_impl { + using type = detail::rm_cvref().metadata())>; + }; + template + struct metadata_type_impl { + using type = axis::missing_metadata_type; + }; } // namespace detail - namespace axis { +namespace traits { + template + using args = detail::args_type; -template -struct traits { - using args = boost::callable_traits::args_t; + template + using metadata_type = typename detail::metadata_type_impl, T>::type; - static option_type options(const T& t) { - return detail::axis_traits_options_impl(detail::has_method_options(), t); + template + option_type options(const T& t) { + return detail::overload( + [](std::true_type, const auto& x) { return x.options(); }, + [](std::false_type, const auto&) { return axis::option_type::none; } + )(detail::has_method_options(), t); } - static unsigned extend(const T& t) { + template + unsigned extend(const T& t) { return t.size() + static_cast(options(t)); } -}; - +} // namespace traits } // namespace axis } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/axis/types.hpp b/include/boost/histogram/axis/types.hpp index c0ddb3d6..e224cec0 100644 --- a/include/boost/histogram/axis/types.hpp +++ b/include/boost/histogram/axis/types.hpp @@ -50,19 +50,6 @@ struct sqrt : public identity { static T inverse(T x) { return x * x; } }; -template -struct quantity { - T unit; - using U = decltype(std::declval() / std::declval()); - - U forward(T x) const { return x / unit; } - T inverse(U x) const { return x * unit; } - - bool operator==(const quantity& o) const noexcept { return unit == o.unit; } - template - void serialize(Archive&, unsigned); -}; - template struct pow { T power = 1.0; @@ -77,6 +64,22 @@ struct pow { template void serialize(Archive&, unsigned); }; + +template +struct quantity { + Unit unit; + + quantity(const Unit& u) : unit(u) {} + + using Dimensionless = decltype(std::declval() / std::declval()); + + Dimensionless forward(Quantity x) const { return x / unit; } + Quantity inverse(Dimensionless x) const { return x * unit; } + + bool operator==(const quantity& o) const noexcept { return unit == o.unit; } + template + void serialize(Archive&, unsigned); +}; } // namespace transform /** Axis for equidistant intervals on the real line. @@ -120,18 +123,18 @@ public: /** Construct axis with n bins over real range [begin, end). * * \param n number of bins. - * \param begin low edge of first bin. - * \param end high edge of last bin. + * \param start low edge of first bin. + * \param stop high edge of last bin. * \param metadata description of the axis. * \param options extra bin options. * \param trans transform instance to use. */ - regular(unsigned n, value_type begin, value_type end, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow, - const transform_type& trans = transform_type()) - : base_type(n, m, o) - , data_(trans, n, begin, end) + regular(unsigned n, value_type start, value_type stop, + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + transform_type trans = transform_type()) + : base_type(n, std::move(m), o) + , data_(std::move(trans), n, start, stop) { if (!std::isfinite(data_.min) || !std::isfinite(data_.delta)) throw std::invalid_argument("forward transform of lower or upper invalid"); @@ -159,11 +162,11 @@ public: /// Returns lower edge of bin. value_type lower(int idx) const noexcept { const auto z = internal_type(idx) / base_type::size(); - value_type x; + internal_type x; if (z < 0) - x = -std::numeric_limits::infinity(); + x = -std::numeric_limits::infinity(); else if (z > 1) - x = std::numeric_limits::infinity(); + x = std::numeric_limits::infinity(); else { x = (1 - z) * data_.min + z * (data_.min + data_.delta * base_type::size()); } @@ -212,9 +215,9 @@ public: */ explicit circular(unsigned n, value_type phase = 0.0, value_type perimeter = two_pi(), - const metadata_type& m = metadata_type(), - const option_type o = option_type::overflow) - : base_type(n, m, o == option_type::underflow_and_overflow ? + metadata_type m = metadata_type(), + option_type o = option_type::overflow) + : base_type(n, std::move(m), o == option_type::underflow_and_overflow ? option_type::overflow : o) , phase_(phase), delta_(perimeter / n) { @@ -295,11 +298,11 @@ public: template > variable(Iterator begin, Iterator end, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow, - const allocator_type& a = allocator_type()) - : base_type(begin == end ? 0 : std::distance(begin, end) - 1, m, o) - , data_(a) + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + allocator_type a = allocator_type()) + : base_type(begin == end ? 0 : std::distance(begin, end) - 1, std::move(m), o) + , data_(std::move(a)) { using AT = std::allocator_traits; data_.x = AT::allocate(data_, nx()); @@ -335,18 +338,18 @@ public: * \param allocator allocator instance to use. */ template > - variable(T iterable, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow, - const allocator_type& a = allocator_type()) - : variable(std::begin(iterable), std::end(iterable), m, o, a) {} + variable(const T& t, + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + allocator_type a = allocator_type()) + : variable(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} template - variable(const std::initializer_list& t, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow, - const allocator_type& a = allocator_type()) - : variable(t.begin(), t.end(), m, o, a) {} + variable(std::initializer_list t, + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow, + allocator_type a = allocator_type()) + : variable(t.begin(), t.end(), std::move(m), o, std::move(a)) {} variable() = default; @@ -438,9 +441,9 @@ public: * \param options extra bin options. */ integer(value_type begin, value_type end, - const metadata_type& m = metadata_type(), - const option_type o = option_type::underflow_and_overflow) - : base_type(end - begin, m, o), min_(begin) { + metadata_type m = metadata_type(), + option_type o = option_type::underflow_and_overflow) + : base_type(end - begin, std::move(m), o), min_(begin) { if (begin >= end) { throw std::invalid_argument("begin < end required"); } } @@ -520,11 +523,11 @@ public: template > category(Iterator begin, Iterator end, - const metadata_type& m = metadata_type(), - const option_type o = option_type::overflow, - const allocator_type& a = allocator_type()) - : base_type(std::distance(begin, end), m, o) - , data_(a) + metadata_type m = metadata_type(), + option_type o = option_type::overflow, + allocator_type a = allocator_type()) + : base_type(std::distance(begin, end), std::move(m), o) + , data_(std::move(a)) { data_.x = detail::create_buffer_from_iter(data_, base_type::size(), begin); } @@ -534,19 +537,20 @@ public: * \param seq sequence of unique values. * \param metadata description of the axis. */ - template > - category(T t, - const metadata_type& m = metadata_type(), - const option_type o = option_type::overflow, - const allocator_type& a = allocator_type()) - : category(std::begin(t), std::end(t), m, o, a) {} + template > + category(const T& t, + metadata_type m = metadata_type(), + option_type o = option_type::overflow, + allocator_type a = allocator_type()) + : category(std::begin(t), std::end(t), std::move(m), o, std::move(a)) {} template - category(const std::initializer_list& t, - const metadata_type& m = metadata_type(), - const option_type o = option_type::overflow, - const allocator_type& a = allocator_type()) - : category(t.begin(), t.end(), m, o, a) {} + category(std::initializer_list t, + metadata_type m = metadata_type(), + option_type o = option_type::overflow, + allocator_type a = allocator_type()) + : category(t.begin(), t.end(), std::move(m), o, std::move(a)) {} category() = default; diff --git a/include/boost/histogram/axis/variant.hpp b/include/boost/histogram/axis/variant.hpp index b85653a1..550839f0 100644 --- a/include/boost/histogram/axis/variant.hpp +++ b/include/boost/histogram/axis/variant.hpp @@ -32,7 +32,7 @@ struct call_visitor { call_visitor(const double arg) : x(arg) {} template int operator()(const T& t) const { - using args = typename axis::traits::args; + using args = axis::traits::args; return impl(mp11::mp_bool< (mp11::mp_size::value == 1 && std::is_convertible>::value) @@ -40,12 +40,11 @@ struct call_visitor { } template int impl(std::true_type, const T& a) const { - using U = mp11::mp_first::args>; - return a(static_cast(x)); + return a(x); } template int impl(std::false_type, const T&) const { - using args = typename axis::traits::args; + using args = axis::traits::args; throw std::invalid_argument(detail::cat( "cannot convert double to ", boost::core::demangled_name( BOOST_CORE_TYPEID(args) ), @@ -55,97 +54,10 @@ struct call_visitor { } }; -struct size_visitor { - template - unsigned operator()(const T& a) const { - return a.size(); - } -}; - -struct options_visitor { - template - axis::option_type operator()(const T& t) const { - return axis::traits::options(t); - } -}; - -struct lower_visitor { - int idx; - lower_visitor(int i) : idx(i) {} - template - double operator()(const T& a) const { - return impl(detail::has_method_lower(), a); - } - template - double impl(std::true_type, const T& a) const { - return a.lower(idx); - } - template - double impl(std::false_type, const T&) const { - throw std::runtime_error(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), - " has no lower method")); - } -}; - -template -struct assign_visitor { - T& t; - assign_visitor(T& x) : t(x) {} - template - void operator()(const U& u) const { - impl(mp11::mp_contains, U>(), u); - } - - template - void impl(std::true_type, const U& u) const { - t = u; - } - - template - void impl(std::false_type, const U&) const { - throw std::invalid_argument(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), - " is not a bounded type of ", - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ) - )); - } -}; - -template -struct equal_visitor { - const T& t; - equal_visitor(const T& x) : t(x) {} - template - bool operator()(const U& u) const { - return t == u; - } -}; - -template -struct ostream_visitor { - OStream& os; - ostream_visitor(OStream& o) : os(o) {} - template - void operator()(const T& t) const { - impl(is_streamable(), t); - } - template - void impl(std::true_type, const T& t) const { - os << t; - } - template - void impl(std::false_type, const T&) const { - throw std::invalid_argument(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), - " is not streamable")); - } -}; - template struct functor_wrapper : public boost::static_visitor { - F fcn; - functor_wrapper(F f) : fcn(std::forward(f)) {} + F& fcn; + functor_wrapper(F& f) : fcn(f) {} template R operator()(T&& t) const { @@ -165,14 +77,19 @@ class variant { using base_type = boost::variant; using bin_type = interval_view; + using first_bounded_type = mp11::mp_first; + using metadata_type = traits::metadata_type; template using requires_bounded_type = - mp11::mp_if>, void>; + mp11::mp_if>, void>; public: variant() = default; + variant(const variant&) = default; + variant& operator=(const variant&) = default; + variant(variant&&) = default; + variant& operator=(variant&&) = default; template > variant(T&& t) : base_type(std::forward(t)) {} @@ -185,21 +102,57 @@ public: template variant(const variant& u) { - visit(detail::assign_visitor(*this), u); + this->operator=(u); } template variant& operator=(const variant& u) { - visit(detail::assign_visitor(*this), u); + visit([this](const auto& u) { + using U = detail::rm_cvref; + detail::overload( + [this](std::true_type, const auto& u) { this->operator=(u); }, + [](std::false_type, const auto&) { + throw std::runtime_error(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), + " is not a bounded type of ", + boost::core::demangled_name( BOOST_CORE_TYPEID(variant) ) + )); + } + )(mp11::mp_contains(), u); + }, u); return *this; } unsigned size() const { - return visit(detail::size_visitor(), *this); + return visit([](const auto& x) { return x.size(); }, *this); } option_type options() const { - return visit(detail::options_visitor(), *this); + return visit([](const auto& x) { return axis::traits::options(x); }, *this); + } + + const metadata_type& metadata() const { + return visit([](const auto& x) { + using T = detail::rm_cvref; + using U = traits::metadata_type; + return detail::overload( + [](std::true_type, const auto& x) { return x.metadata(); }, + [](std::false_type, const auto&) -> const metadata_type& { + throw std::runtime_error(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), + " cannot return metadata of type ", + boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), + " through variant interface which uses type ", + boost::core::demangled_name( BOOST_CORE_TYPEID(metadata_type) ) + )); + } + )(std::is_same(), x); + }, *this); + } + + metadata_type& metadata() { + // return visit([](auto& x) { return axis::traits::metadata(x); }, *this); + return metadata_type(); } // Only works for axes with compatible call signature @@ -211,7 +164,17 @@ public: // Only works for axes with a lower method // and will throw a runtime_error otherwise double lower(int idx) const { - return visit(detail::lower_visitor(idx), *this); + return visit([idx](const auto& x) { + using T = detail::rm_cvref; + return detail::overload( + [idx](std::true_type, const auto& x) -> double { return x.lower(idx); }, + [](std::false_type, const auto&) -> double { + throw std::runtime_error(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), + " has no lower method")); + } + )(detail::has_method_lower(), x); + }, *this); } // this only works for axes with compatible bin type @@ -224,7 +187,7 @@ public: template bool operator==(const variant& u) const { - return visit(detail::equal_visitor(u), *this); + return visit([&u](const auto& x) { return u == x; }, *this); } template @@ -267,7 +230,7 @@ auto visit(Functor&& f, Variant&& v) -> detail::visitor_return_type { using R = detail::visitor_return_type; - return boost::apply_visitor(detail::functor_wrapper(std::forward(f)), + return boost::apply_visitor(detail::functor_wrapper(f), static_cast< detail::copy_qualifiers::base_type> @@ -277,34 +240,58 @@ auto visit(Functor&& f, Variant&& v) template std::basic_ostream& operator<<(std::basic_ostream& os, const variant& v) { - visit(detail::ostream_visitor(os), v); + visit([&os](const auto& x) { + using T = detail::rm_cvref; + detail::overload( + [&os](std::true_type, const auto& x) { os << x; }, + [](std::false_type, const auto&) { + throw std::runtime_error(detail::cat( + boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), + " is not streamable")); + } + )(detail::is_streamable(), x); + }, v); return os; } template T& get(variant& v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type&>(v)); } template const T& get(const variant& v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type&>(v)); } template T&& get(variant&& v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type&&>(v)); } template T* get(variant* v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type*>(v)); } template const T* get(const variant* v) { - return boost::relaxed_get(v); + return boost::get(static_cast::base_type*>(v)); } + +// pass-through if T is an axis instead of a variant +template >, + typename = mp11::mp_if< + std::is_same< + detail::rm_cvref, + detail::rm_cvref + >, void> + > +U get(U&& u) { + return std::forward(u); +} + } // namespace axis } // namespace histogram } // namespace boost diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index 9440999a..f00567cd 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -26,11 +26,6 @@ namespace detail { namespace { -template -unsigned extend(const T& t) { - return axis::traits::extend(t); -} - template struct axes_equal_static_dynamic_impl { bool& equal; @@ -164,7 +159,7 @@ auto axis_get(T&& axes) -> decltype(std::get(std::forward(axes))) { } template > + typename = requires_axis_vector> auto axis_get(T&& axes) -> decltype(std::forward(axes)[N]) { return std::forward(axes)[N]; } @@ -186,7 +181,7 @@ struct field_counter { std::size_t value = 1; template void operator()(const T& t) { - value *= extend(t); + value *= axis::traits::extend(t); } }; } @@ -226,7 +221,7 @@ struct shape_collector { shape_collector(std::vector::iterator i) : iter(i) {} template void operator()(const T& t) { - *iter++ = extend(t); + *iter++ = axis::traits::extend(t); } }; @@ -329,7 +324,7 @@ void indices_to_index(optional_index& idx, const Axes& axes, const int j, const Us... us) { const auto& a = axis_get(axes); const auto a_size = static_cast(a.size()); - const auto a_shape = static_cast(extend(a)); + const auto a_shape = static_cast(axis::traits::extend(a)); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); indices_to_index<(D + 1)>(idx, axes, us...); @@ -345,7 +340,7 @@ void indices_to_index_iter(mp11::mp_size_t, optional_index& idx, constexpr auto D = mp11::mp_size_t() - N; const auto& a = std::get(axes); const auto a_size = static_cast(a.size()); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = static_cast(*iter); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); @@ -357,7 +352,7 @@ void indices_to_index_iter(optional_index& idx, const std::vector& axes, Iterator iter) { for (const auto& a : axes) { const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = static_cast(*iter++); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); @@ -372,8 +367,8 @@ void indices_to_index_get(mp11::mp_size_t, optional_index& idx, const Axes& a const T& t) { constexpr std::size_t D = mp_size() - N; const auto& a = axis_get(axes); - const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_size = static_cast(a.size()); + const auto a_shape = axis::traits::extend(a); const auto j = static_cast(std::get(t)); idx.stride *= (-1 <= j && j <= a_size); // set to 0, if j is invalid linearize(idx, a_size, a_shape, j); @@ -388,7 +383,7 @@ void args_to_index(optional_index& idx, const std::tuple& axes, const U& const Us&... us) { const auto& a = std::get(axes); const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = a(u); linearize(idx, a_size, a_shape, j); args_to_index<(D + 1)>(idx, axes, us...); @@ -404,7 +399,7 @@ void args_to_index_iter(mp11::mp_size_t, optional_index& idx, constexpr std::size_t D = sizeof...(Ts)-N; const auto& a = axis_get(axes); const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = a(*iter); linearize(idx, a_size, a_shape, j); args_to_index_iter(mp11::mp_size_t<(N - 1)>(), idx, axes, ++iter); @@ -420,7 +415,7 @@ void args_to_index_get(mp11::mp_size_t, optional_index& idx, constexpr std::size_t D = mp_size::value - N; const auto& a = std::get(axes); const auto a_size = a.size(); - const auto a_shape = extend(a); + const auto a_shape = axis::traits::extend(a); const auto j = a(std::get(t)); linearize(idx, a_size, a_shape, j); args_to_index_get(mp11::mp_size_t<(N - 1)>(), idx, axes, t); @@ -432,29 +427,29 @@ struct args_to_index_visitor { optional_index& idx; const T& val; args_to_index_visitor(optional_index& i, const T& v) : idx(i), val(v) {} - template - void operator()(const Axis& a) const { - using value_type = return_type; - impl(std::is_convertible(), a); + template + void operator()(const U& a) const { + using arg_type = mp11::mp_first>; + impl(std::is_convertible(), a); } - template - void impl(std::true_type, const Axis& a) const { - using value_type = return_type; + template + void impl(std::true_type, const U& a) const { + using arg_type = mp11::mp_first>; const auto a_size = a.size(); - const auto a_shape = extend(a); - const auto j = a(static_cast(val)); + const auto a_shape = axis::traits::extend(a); + const auto j = a(static_cast(val)); linearize(idx, a_size, a_shape, j); } - template - void impl(std::false_type, const Axis&) const { - using value_type = return_type; + template + void impl(std::false_type, const U&) const { + using arg_type = mp11::mp_first>; throw std::invalid_argument(detail::cat( - boost::core::demangled_name( BOOST_CORE_TYPEID(Axis) ), + boost::core::demangled_name( BOOST_CORE_TYPEID(U) ), ": cannot convert argument of type ", boost::core::demangled_name( BOOST_CORE_TYPEID(T) ), " to ", - boost::core::demangled_name( BOOST_CORE_TYPEID(value_type) ))); + boost::core::demangled_name( BOOST_CORE_TYPEID(arg_type) ))); } }; } @@ -519,7 +514,7 @@ optional_index call_impl(static_container_tag, const std::tuple& } template -optional_index call_impl(dynamic_container_tag, const std::tuple& axes, +optional_index call_impl(iterable_container_tag, const std::tuple& axes, const U& u) { dimension_check(axes, u.size()); optional_index i; @@ -548,7 +543,7 @@ optional_index call_impl(static_container_tag, const std::vector& axes, } template -optional_index call_impl(dynamic_container_tag, const std::vector& axes, +optional_index call_impl(iterable_container_tag, const std::vector& axes, const U& u) { if (axes.size() == 1) // do not unpack for 1d histograms, it is ambiguous return call_impl(no_container_tag(), axes, u); @@ -583,7 +578,7 @@ std::size_t at_impl(detail::static_container_tag, const A& axes, const U& u) { } template -std::size_t at_impl(detail::dynamic_container_tag, const std::tuple& axes, +std::size_t at_impl(detail::iterable_container_tag, const std::tuple& axes, const U& u) { dimension_check(axes, std::distance(std::begin(u), std::end(u))); auto index = detail::optional_index(); @@ -594,7 +589,7 @@ std::size_t at_impl(detail::dynamic_container_tag, const std::tuple& axes } template -std::size_t at_impl(detail::dynamic_container_tag, const std::vector& axes, +std::size_t at_impl(detail::iterable_container_tag, const std::vector& axes, const U& u) { dimension_check(axes, std::distance(std::begin(u), std::end(u))); auto index = detail::optional_index(); diff --git a/include/boost/histogram/detail/index_cache.hpp b/include/boost/histogram/detail/index_cache.hpp index af3cd889..25884ace 100644 --- a/include/boost/histogram/detail/index_cache.hpp +++ b/include/boost/histogram/detail/index_cache.hpp @@ -9,6 +9,7 @@ #include #include +#include namespace boost { namespace histogram { @@ -35,19 +36,19 @@ struct index_cache : public std::unique_ptr { struct dim_visitor { mutable std::size_t stride; mutable block_t* b; - template - void operator()(const Axis& a) const noexcept { - b->dim = dim_t{0, a.size(), stride}; + template + void operator()(const T& a) const noexcept { + b->dim = dim_t{0, static_cast(a.size()), stride}; ++b; - stride *= a.shape(); + stride *= axis::traits::extend(a); } }; template void set(const H& h) { - if (!(*this) || h.dim() != ptr_t::get()->state.dim) { - ptr_t::reset(new block_t[h.dim() + 1]); - ptr_t::get()->state.dim = h.dim(); + if (!(*this) || h.rank() != ptr_t::get()->state.dim) { + ptr_t::reset(new block_t[h.rank() + 1]); + ptr_t::get()->state.dim = h.rank(); ptr_t::get()->state.idx = 0; } h.for_each_axis(dim_visitor{1, ptr_t::get() + 1}); diff --git a/include/boost/histogram/detail/meta.hpp b/include/boost/histogram/detail/meta.hpp index 652dc437..aea73067 100644 --- a/include/boost/histogram/detail/meta.hpp +++ b/include/boost/histogram/detail/meta.hpp @@ -55,7 +55,11 @@ template using return_type = typename boost::callable_traits::return_type::type; template -using args_type = boost::callable_traits::args_t; +using args_type = mp11::mp_if< + std::is_member_function_pointer, + mp11::mp_pop_front>, + boost::callable_traits::args_t +>; template using arg_type = typename mp11::mp_at_c, (N < 0 ? mp11::mp_size>::value + N : N)>; @@ -63,6 +67,16 @@ using arg_type = typename mp11::mp_at_c, (N < 0 ? mp11::mp_size using visitor_return_type = decltype(std::declval()(std::declval>>())); +template +struct overload_type : T1, T2 { + overload_type(T1 t1, T2 t2) : T1(t1), T2(t2) {} + using T1::operator(); + using T2::operator(); +}; + +template +overload_type overload(T1 t1, T2 t2) { return overload_type(t1, t2); } + #define BOOST_HISTOGRAM_MAKE_SFINAE(name, cond) \ template \ struct name##_impl { \ @@ -89,7 +103,7 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_method_options, BOOST_HISTOGRAM_MAKE_SFINAE(has_method_metadata, (std::declval().metadata())); -BOOST_HISTOGRAM_MAKE_SFINAE(is_dynamic_container, +BOOST_HISTOGRAM_MAKE_SFINAE(is_random_access_container, (std::declval()[0], std::declval().size())); BOOST_HISTOGRAM_MAKE_SFINAE(is_static_container, @@ -124,7 +138,7 @@ using is_axis_variant = typename is_axis_variant_impl::type; template using is_axis_vector = mp11::mp_all< - is_dynamic_container, + is_random_access_container, mp11::mp_any< is_axis_variant>>, is_axis>> @@ -132,14 +146,14 @@ using is_axis_vector = mp11::mp_all< >; struct static_container_tag {}; -struct dynamic_container_tag {}; +struct iterable_container_tag {}; struct no_container_tag {}; template using classify_container = typename std::conditional< - is_static_container::value, static_container_tag, - typename std::conditional::value, - dynamic_container_tag, no_container_tag>::type>::type; + is_iterable::value, iterable_container_tag, + typename std::conditional::value, + static_container_tag, no_container_tag>::type>::type; namespace { struct bool_mask_impl { @@ -171,15 +185,9 @@ struct requires_iterator {}; template , void>> struct requires_iterable {}; -template , void>> -struct requires_dynamic_container {}; - template , void>> struct requires_static_container {}; -template , void>> -struct requires_tuple {}; - template , void>> struct requires_axis {}; @@ -191,7 +199,7 @@ struct requires_axis_or_axis_variant {}; template , - typename = mp11::mp_if_c<(is_dynamic_container::value && + typename = mp11::mp_if_c<(is_random_access_container::value && (is_axis::value || is_axis_variant::value)), void>> struct requires_axis_vector {}; diff --git a/include/boost/histogram/histogram.hpp b/include/boost/histogram/histogram.hpp index 894a4870..c97b22ac 100644 --- a/include/boost/histogram/histogram.hpp +++ b/include/boost/histogram/histogram.hpp @@ -125,7 +125,7 @@ public: /// Get N-th axis with runtime index (const version) template > + typename = detail::requires_axis_vector> auto axis(std::size_t i) const -> const detail::container_element_type& { BOOST_ASSERT_MSG(i < axes_.size(), "index out of range"); return axes_[i]; @@ -133,7 +133,7 @@ public: /// Get N-th axis with runtime index template > + typename = detail::requires_axis_vector> auto axis(std::size_t i) -> detail::container_element_type& { BOOST_ASSERT_MSG(i < axes_.size(), "index out of range"); return axes_[i]; @@ -219,7 +219,7 @@ public: /// Returns a lower-dimensional histogram // precondition: sequence must be strictly ascending axis indices template , + typename = detail::requires_axis_vector, typename = detail::requires_iterator> histogram reduce_to(Iterator begin, Iterator end) const { BOOST_ASSERT_MSG(std::is_sorted(begin, end, std::less_equal()), diff --git a/include/boost/histogram/histogram_fwd.hpp b/include/boost/histogram/histogram_fwd.hpp index 4604b199..7734aee1 100644 --- a/include/boost/histogram/histogram_fwd.hpp +++ b/include/boost/histogram/histogram_fwd.hpp @@ -16,6 +16,8 @@ namespace histogram { namespace axis { +struct missing_metadata_type {}; + enum class option_type { none = 0, overflow = 1, @@ -26,8 +28,8 @@ namespace transform { template struct identity; template struct log; template struct sqrt; -template struct quantity; template struct pow; +template struct quantity; } // namespace transform template , diff --git a/include/boost/histogram/iterator.hpp b/include/boost/histogram/iterator.hpp index 73696f8b..18905676 100644 --- a/include/boost/histogram/iterator.hpp +++ b/include/boost/histogram/iterator.hpp @@ -32,7 +32,7 @@ public: cache_.reset(); } - std::size_t dim() const noexcept { return histogram_.dim(); } + std::size_t rank() const noexcept { return histogram_.rank(); } int idx(std::size_t dim = 0) const noexcept { if (!cache_) { cache_.set(histogram_); } diff --git a/include/boost/histogram/ostream_operators.hpp b/include/boost/histogram/ostream_operators.hpp index 734728df..a42de867 100644 --- a/include/boost/histogram/ostream_operators.hpp +++ b/include/boost/histogram/ostream_operators.hpp @@ -33,7 +33,7 @@ std::basic_ostream& operator<<(std::basic_ostream& using OS = std::basic_ostream; os << "histogram("; h.for_each_axis(detail::axis_ostream_visitor(os)); - os << (h.dim() ? "\n)" : ")"); + os << (h.rank() ? "\n)" : ")"); return os; } diff --git a/include/boost/histogram/storage/weight_counter.hpp b/include/boost/histogram/storage/weight_counter.hpp index b0b86f36..e3bd083f 100644 --- a/include/boost/histogram/storage/weight_counter.hpp +++ b/include/boost/histogram/storage/weight_counter.hpp @@ -19,7 +19,7 @@ class access; namespace histogram { /// Double counter which holds a sum of weights and a sum of squared weights -template +template class weight_counter { public: /// Beware: For performance reasons counters are not initialized diff --git a/test/axis_test.cpp b/test/axis_test.cpp index 902f9dc6..52b40c85 100644 --- a/test/axis_test.cpp +++ b/test/axis_test.cpp @@ -12,9 +12,11 @@ #include #include #include +#include #include #include #include +#include #include "utility.hpp" using namespace boost::histogram; @@ -36,6 +38,33 @@ void test_axis_iterator(const Axis& a, int begin, int end) { } } +// quantity with unit for testing +template +struct quantity { + double value; +}; + +struct length { + double value; +}; + +auto meter = length{1.0}; + +template +double operator/(const quantity& a, const Unit& u) { + return a.value / u.value; +} + +template +quantity operator*(double x, const Unit& u) { + return quantity{x * u.value}; +} + +template +quantity operator-(const quantity& a, const quantity& b) { + return quantity{a.value - b.value}; +} + int main() { // bad_ctors { @@ -79,6 +108,19 @@ int main() { BOOST_TEST_EQ(a(std::numeric_limits::quiet_NaN()), 4); } + // regular axis with inverted range + { + axis::regular<> a(2, 1, -1); + BOOST_TEST_EQ(a[0].lower(), 1); + BOOST_TEST_EQ(a[1].lower(), 0); + BOOST_TEST_EQ(a[2].lower(), -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), 2); + } + // axis::regular with log transform { axis::regular> b{2, 1e0, 1e2}; @@ -118,6 +160,27 @@ int main() { BOOST_TEST_EQ(b(std::numeric_limits::infinity()), 2); } + // axis::regular with quantity transform + { + axis::regular, length>> b{ + 2, 0*meter, 2*meter, {}, axis::option_type::underflow_and_overflow, meter + }; + BOOST_TEST_EQ(b[-1].lower()/meter, -std::numeric_limits::infinity()); + BOOST_TEST_IS_CLOSE(b[0].lower()/meter, 0.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[1].lower()/meter, 1.0, 1e-9); + BOOST_TEST_IS_CLOSE(b[2].lower()/meter, 2.0, 1e-9); + BOOST_TEST_EQ(b[2].upper()/meter, std::numeric_limits::infinity()); + + BOOST_TEST_EQ(b(-1*meter), -1); // produces NaN in conversion + BOOST_TEST_EQ(b(0*meter), 0); + BOOST_TEST_EQ(b(0.99*meter), 0); + BOOST_TEST_EQ(b(1*meter), 1); + BOOST_TEST_EQ(b(1.99*meter), 1); + BOOST_TEST_EQ(b(2*meter), 2); + BOOST_TEST_EQ(b(100*meter), 2); + BOOST_TEST_EQ(b(std::numeric_limits::infinity()*meter), 2); + } + // axis::circular { axis::circular<> a{4, 0, 1}; @@ -248,7 +311,7 @@ int main() { // std::runtime_error); } - // axis::any copyable + // axis::variant copyable { axis::variant> a1(axis::regular<>(2, -1, 1)); axis::variant> a2(a1); @@ -264,11 +327,11 @@ int main() { a6 = a1; BOOST_TEST_EQ(a6, a1); axis::variant, axis::integer<>> a7(axis::integer<>(0, 2)); - BOOST_TEST_THROWS(axis::variant> a8(a7), std::invalid_argument); - BOOST_TEST_THROWS(a4 = a7, std::invalid_argument); + BOOST_TEST_THROWS(axis::variant> a8(a7), std::runtime_error); + BOOST_TEST_THROWS(a4 = a7, std::runtime_error); } - // axis::any movable + // axis::variant movable { axis::variant> a(axis::regular<>(2, -1, 1)); axis::variant> r(a); @@ -280,51 +343,68 @@ int main() { BOOST_TEST(c == r); } - // axis::any streamable + // axis::variant streamable { - enum { A, B, C }; - std::string a = "A"; - std::string b = "B"; - std::vector, - axis::regular>, - axis::regular>, - axis::circular<>, - axis::variable<>, - axis::category<>, - axis::category, - axis::integer<> - >> axes; - axes.push_back(axis::regular<>{2, -1, 1, "regular1"}); - axes.push_back(axis::regular>(2, 1, 10, "regular2", - axis::option_type::none)); - axes.push_back(axis::regular>(2, 1, 10, "regular3", - axis::option_type::overflow, 0.5)); - axes.push_back(axis::regular>(2, 1, 10, "regular4", - axis::option_type::none, -0.5)); - axes.push_back(axis::circular<>(4, 0.1, 1.0, "polar")); - axes.push_back(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none)); - axes.push_back(axis::category<>({A, B, C}, "category")); - axes.push_back(axis::category({a, b}, "category2")); - axes.push_back(axis::integer<>(-1, 1, "integer", axis::option_type::none)); - std::ostringstream os; - for (const auto& a : axes) { os << a << "\n"; } - os << axes.back()[0]; - const std::string ref = - "regular(2, -1, 1, metadata='regular1', options=underflow_and_overflow)\n" - "regular_log(2, 1, 10, metadata='regular2', options=none)\n" - "regular_pow(2, 1, 10, metadata='regular3', options=overflow, power=0.5)\n" - "regular_pow(2, 1, 10, metadata='regular4', options=none, power=-0.5)\n" - "circular(4, 0.1, 1.1, metadata='polar', options=overflow)\n" - "variable(-1, 0, 1, metadata='variable', options=none)\n" - "category(0, 1, 2, metadata='category', options=overflow)\n" - "category('A', 'B', metadata='category2', options=overflow)\n" - "integer(-1, 1, metadata='integer', options=none)\n" - "[-1, 0)"; - BOOST_TEST_EQ(os.str(), ref); + auto test = [](auto&& a, const char* ref) { + using T = detail::rm_cvref; + axis::variant axis(std::move(a)); + std::ostringstream os; + os << axis; + BOOST_TEST_EQ(os.str(), ref); + }; + + test(axis::regular<>{2, -1, 1, "regular1"}, + "regular(2, -1, 1, metadata='regular1', options=underflow_and_overflow)"); + test(axis::regular>(2, 1, 10, "regular2", axis::option_type::none), + "regular_log(2, 1, 10, metadata='regular2', options=none)"); + test(axis::regular>(2, 1, 10, "regular3", axis::option_type::overflow, 0.5), + "regular_pow(2, 1, 10, metadata='regular3', options=overflow, power=0.5)"); + test(axis::regular>(2, 1, 10, "regular4", axis::option_type::none, -0.5), + "regular_pow(2, 1, 10, metadata='regular4', options=none, power=-0.5)"); + test(axis::circular<>(4, 0.1, 1.0, "polar"), + "circular(4, 0.1, 1.1, metadata='polar', options=overflow)"); + test(axis::variable<>({-1, 0, 1}, "variable", axis::option_type::none), + "variable(-1, 0, 1, metadata='variable', options=none)"); + test(axis::category<>({0, 1, 2}, "category"), + "category(0, 1, 2, metadata='category', options=overflow)"); + test(axis::category({"A", "B"}, "category2"), + "category('A', 'B', metadata='category2', options=overflow)"); + test(axis::integer<>(-1, 1, "integer", axis::option_type::none), + "integer(-1, 1, metadata='integer', options=none)"); } - // axis::any equal_comparable + // 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.lower(0), std::runtime_error); + BOOST_TEST_TRAIT_TRUE((std::is_same)); + } + + // bin_type streamable + { + auto test = [](const auto& x, const char* ref) { + std::ostringstream os; + os << x; + BOOST_TEST_EQ(os.str(), ref); + }; + + auto a = axis::regular<>(2, 0, 1); + test(a[0], "[0, 0.5)"); + + auto b = axis::category<>({1, 2}); + test(b[0], "1"); + } + + // axis::variant equal_comparable { enum { A, B, C }; using variant = axis::variant< @@ -351,12 +431,11 @@ int main() { BOOST_TEST(axes == std::vector(axes)); } - // axis::any value_to_index_failure + // axis::variant value_to_index_failure { - std::string a = "A", b = "B"; - axis::variant> x = axis::category({a, b}, "category"); + axis::variant> x = axis::category({"A", "B"}, "category"); auto cx = axis::get>(x); - BOOST_TEST_EQ(cx(b), 1); + // BOOST_TEST_EQ(cx(b), 1); } // sequence equality @@ -484,13 +563,13 @@ int main() { axes.emplace_back(T4(0, 4)); axes.emplace_back(T5({1, 2, 3, 4, 5}, {}, axis::option_type::overflow, a)); } - // 5 axis::any objects + // 5 axis::variant objects BOOST_TEST_EQ(db[typeid(axis_type)].first, db[typeid(axis_type)].second); BOOST_TEST_EQ(db[typeid(axis_type)].first, 5); - // 5 labels + // label BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second); - BOOST_TEST_GE(db[typeid(char)].first, 3u); + BOOST_TEST_EQ(db[typeid(char)].first, 3u); // nothing to allocate for T1 // nothing to allocate for T2 diff --git a/test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp b/test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp index 177529f3..59d02778 100644 --- a/test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp +++ b/test/histogram_dynamic_fill_one_dimensional_tuple_fail.cpp @@ -9,6 +9,7 @@ using namespace boost::histogram; int main() { - auto a = make_dynamic_histogram(axis::integer<>(0, 2)); + std::vector>> v(1, axis::integer<>(0, 2)); + auto a = make_histogram(v); a(std::make_tuple(1)); // fails, because tuple is intentionally not unpacked } diff --git a/test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp b/test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp index bb49e7ef..f57d5fe4 100644 --- a/test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp +++ b/test/histogram_dynamic_fill_one_dimensional_vector_fail.cpp @@ -9,6 +9,7 @@ using namespace boost::histogram; int main() { - auto a = make_dynamic_histogram(axis::integer<>(0, 2)); + std::vector>> v(1, axis::integer<>(0, 2)); + auto a = make_histogram(v); a(std::vector(1)); // fails, because tuple is intentionally not unpacked } diff --git a/test/histogram_static_add_fail.cpp b/test/histogram_static_add_fail.cpp index 7e73c286..91c9b3ab 100644 --- a/test/histogram_static_add_fail.cpp +++ b/test/histogram_static_add_fail.cpp @@ -8,7 +8,7 @@ using namespace boost::histogram; int main() { - auto a = make_static_histogram(axis::integer<>(0, 2)); - auto b = make_static_histogram(axis::integer<>(0, 3)); + auto a = make_histogram(axis::integer<>(0, 2)); + auto b = make_histogram(axis::integer<>(0, 3)); a += b; } diff --git a/test/histogram_test.cpp b/test/histogram_test.cpp index 41936d0d..587b71ae 100644 --- a/test/histogram_test.cpp +++ b/test/histogram_test.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -38,23 +39,23 @@ void run_tests() { // init_1 { auto h = make(Tag(), axis::regular<>{3, -1, 1}); - BOOST_TEST_EQ(h.dim(), 1); + BOOST_TEST_EQ(h.rank(), 1); BOOST_TEST_EQ(h.size(), 5); - BOOST_TEST_EQ(h.axis(0_c).shape(), 5); - BOOST_TEST_EQ(h.axis().shape(), 5); + BOOST_TEST_EQ(h.axis(0_c).size(), 3); + BOOST_TEST_EQ(h.axis().size(), 3); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}); BOOST_TEST_EQ(h2, h); } // init_2 { - auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}); - BOOST_TEST_EQ(h.dim(), 2); - BOOST_TEST_EQ(h.size(), 25); - BOOST_TEST_EQ(h.axis(0_c).shape(), 5); - BOOST_TEST_EQ(h.axis(1_c).shape(), 5); + auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 3}); + BOOST_TEST_EQ(h.rank(), 2); + BOOST_TEST_EQ(h.size(), 30); + BOOST_TEST_EQ(h.axis(0_c).size(), 3); + BOOST_TEST_EQ(h.axis(1_c).size(), 4); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, - axis::integer<>{-1, 2}); + axis::integer<>{-1, 3}); BOOST_TEST_EQ(h2, h); } @@ -62,7 +63,7 @@ void run_tests() { { auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}); - BOOST_TEST_EQ(h.dim(), 3); + BOOST_TEST_EQ(h.rank(), 3); BOOST_TEST_EQ(h.size(), 75); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}); @@ -73,7 +74,7 @@ void run_tests() { { auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}, axis::variable<>{-1, 0, 1}); - BOOST_TEST_EQ(h.dim(), 4); + BOOST_TEST_EQ(h.rank(), 4); BOOST_TEST_EQ(h.size(), 300); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, @@ -87,7 +88,7 @@ void run_tests() { auto h = make(Tag(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}, axis::variable<>{-1, 0, 1}, axis::category<>{{A, B, C}}); - BOOST_TEST_EQ(h.dim(), 5); + BOOST_TEST_EQ(h.rank(), 5); BOOST_TEST_EQ(h.size(), 1200); auto h2 = make_s(Tag(), array_storage(), axis::regular<>{3, -1, 1}, axis::integer<>{-1, 2}, axis::circular<>{3}, @@ -131,51 +132,49 @@ void run_tests() { const auto href = h; decltype(h) h2(std::move(h)); // static axes cannot shrink to zero - BOOST_TEST_EQ(h.dim(), expected_moved_from_dim(Tag(), 2)); - BOOST_TEST_EQ(sum(h).value(), 0); + BOOST_TEST_EQ(h.rank(), expected_moved_from_dim(Tag(), 2)); + BOOST_TEST_EQ(sum(h), 0); BOOST_TEST_EQ(h.size(), 0); BOOST_TEST_EQ(h2, href); decltype(h) h3; h3 = std::move(h2); // static axes cannot shrink to zero - BOOST_TEST_EQ(h2.dim(), expected_moved_from_dim(Tag(), 2)); - BOOST_TEST_EQ(sum(h2).value(), 0); + BOOST_TEST_EQ(h2.rank(), expected_moved_from_dim(Tag(), 2)); + BOOST_TEST_EQ(sum(h2), 0); BOOST_TEST_EQ(h2.size(), 0); BOOST_TEST_EQ(h3, href); } // axis methods { - enum { A = 3, B = 5 }; auto a = make(Tag(), axis::regular<>(1, 1, 2, "foo")); BOOST_TEST_EQ(a.axis().size(), 1); - BOOST_TEST_EQ(a.axis().shape(), 3); - BOOST_TEST_EQ(a.axis().index(1), 0); BOOST_TEST_EQ(a.axis()[0].lower(), 1); BOOST_TEST_EQ(a.axis()[0].upper(), 2); - BOOST_TEST_EQ(a.axis().label(), "foo"); - a.axis().label("bar"); - BOOST_TEST_EQ(a.axis().label(), "bar"); + BOOST_TEST_EQ(a.axis().metadata(), "foo"); + a.axis().metadata() = "bar"; + BOOST_TEST_EQ(a.axis().metadata(), "bar"); - auto b = make(Tag(), axis::integer<>(1, 2)); - BOOST_TEST_EQ(b.axis().size(), 1); - BOOST_TEST_EQ(b.axis().shape(), 3); - BOOST_TEST_EQ(b.axis().index(1), 0); - BOOST_TEST_EQ(b.axis()[0].lower(), 1); - BOOST_TEST_EQ(b.axis()[0].upper(), 2); - b.axis().label("foo"); - BOOST_TEST_EQ(b.axis().label(), "foo"); + auto b = make(Tag(), axis::regular<>(1, 1, 2, "foo"), + axis::integer<>(1, 3)); + BOOST_TEST_EQ(b.axis(0_c).size(), 1); + BOOST_TEST_EQ(b.axis(0_c)[0].lower(), 1); + BOOST_TEST_EQ(b.axis(0_c)[0].upper(), 2); + BOOST_TEST_EQ(b.axis(1_c).size(), 2); + BOOST_TEST_EQ(b.axis(1_c)[0].lower(), 1); + BOOST_TEST_EQ(b.axis(1_c)[0].upper(), 3); + b.axis(1_c).metadata() = "bar"; + BOOST_TEST_EQ(b.axis(0_c).metadata(), "foo"); + BOOST_TEST_EQ(b.axis(1_c).metadata(), "bar"); - auto c = make(Tag(), axis::category<>({A, B})); + enum class C { A = 3, B = 5 }; + auto c = make(Tag(), axis::category({C::A, C::B})); BOOST_TEST_EQ(c.axis().size(), 2); - BOOST_TEST_EQ(c.axis().shape(), 3); - BOOST_TEST_EQ(c.axis().index(A), 0); - BOOST_TEST_EQ(c.axis().index(B), 1); - c.axis().label("foo"); - BOOST_TEST_EQ(c.axis().label(), "foo"); + c.axis().metadata() = "foo"; + BOOST_TEST_EQ(c.axis().metadata(), "foo"); // need to cast here for this to work with Tag == dynamic_tag, too - auto ca = static_cast&>(c.axis()); - BOOST_TEST_EQ(ca[0].value(), A); + auto ca = axis::get>(c.axis()); + BOOST_TEST(ca[0].value() == C::A); } // equal_compare @@ -211,9 +210,8 @@ void run_tests() { h(-1); h(10); - BOOST_TEST_EQ(h.dim(), 1); - BOOST_TEST_EQ(h.axis(0_c).size(), 2); - BOOST_TEST_EQ(h.axis(0_c).shape(), 4); + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.axis().size(), 2); BOOST_TEST_EQ(sum(h), 4); BOOST_TEST_EQ(h.at(-1), 1); @@ -224,15 +222,14 @@ void run_tests() { // d1_2 { - auto h = make(Tag(), axis::integer<>(0, 2, "", axis::uoflow_type::off)); + auto h = make(Tag(), axis::integer<>(0, 2, "", axis::option_type::none)); h(0); h(-0); h(-1); h(10); - BOOST_TEST_EQ(h.dim(), 1); - BOOST_TEST_EQ(h.axis(0_c).size(), 2); - BOOST_TEST_EQ(h.axis(0_c).shape(), 2); + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.axis().size(), 2); BOOST_TEST_EQ(sum(h), 2); BOOST_TEST_EQ(h.at(0), 2); @@ -247,18 +244,19 @@ void run_tests() { h("D"); h("E"); - BOOST_TEST_EQ(h.dim(), 1); - BOOST_TEST_EQ(h.axis(0_c).size(), 2); - BOOST_TEST_EQ(h.axis(0_c).shape(), 3); + BOOST_TEST_EQ(h.rank(), 1); + BOOST_TEST_EQ(h.axis().size(), 2); BOOST_TEST_EQ(sum(h), 4); BOOST_TEST_EQ(h.at(0), 1); BOOST_TEST_EQ(h.at(1), 1); + BOOST_TEST_EQ(h.at(2), 2); // overflow bin } // d1w { - auto h = make(Tag(), axis::integer<>(0, 2)); + auto h = make_s(Tag(), array_storage>(), + axis::integer<>(0, 2)); h(-1); h(0); h(weight(0.5), 0); @@ -282,17 +280,15 @@ void run_tests() { // d2 { auto h = make(Tag(), axis::regular<>(2, -1, 1), - axis::integer<>(-1, 2, "", axis::uoflow_type::off)); + axis::integer<>(-1, 2, {}, axis::option_type::none)); h(-1, -1); h(-1, 0); h(-1, -10); h(-10, 0); - BOOST_TEST_EQ(h.dim(), 2); + BOOST_TEST_EQ(h.rank(), 2); BOOST_TEST_EQ(h.axis(0_c).size(), 2); - BOOST_TEST_EQ(h.axis(0_c).shape(), 4); BOOST_TEST_EQ(h.axis(1_c).size(), 3); - BOOST_TEST_EQ(h.axis(1_c).shape(), 3); BOOST_TEST_EQ(sum(h), 3); BOOST_TEST_EQ(h.at(-1, 0), 0); @@ -314,8 +310,10 @@ void run_tests() { // d2w { - auto h = make(Tag(), axis::regular<>(2, -1, 1), - axis::integer<>(-1, 2, "", axis::uoflow_type::off)); + auto h = make_s(Tag(), + array_storage>(), + axis::regular<>(2, -1, 1), + axis::integer<>(-1, 2, {}, axis::option_type::none)); h(-1, 0); // -> 0, 1 h(weight(10), -1, -1); // -> 0, 0 h(weight(5), -1, -10); // is ignored @@ -359,17 +357,20 @@ void run_tests() { // d3w { - auto h = - make(Tag(), axis::integer<>(0, 3), axis::integer<>(0, 4), axis::integer<>(0, 5)); - for (auto i = 0; i < h.axis(0_c).size(); ++i) { - for (auto j = 0; j < h.axis(1_c).size(); ++j) { - for (auto k = 0; k < h.axis(2_c).size(); ++k) { h(weight(i + j + k), i, j, k); } + auto h = make_s(Tag(), + array_storage>(), + axis::integer<>(0, 3), + axis::integer<>(0, 4), + axis::integer<>(0, 5)); + for (auto i = 0u; i < h.axis(0_c).size(); ++i) { + for (auto j = 0u; j < h.axis(1_c).size(); ++j) { + for (auto k = 0u; k < h.axis(2_c).size(); ++k) { h(weight(i + j + k), i, j, k); } } } - for (auto i = 0; i < h.axis(0_c).size(); ++i) { - for (auto j = 0; j < h.axis(1_c).size(); ++j) { - for (auto k = 0; k < h.axis(2_c).size(); ++k) { + for (auto i = 0u; i < h.axis(0_c).size(); ++i) { + for (auto j = 0u; j < h.axis(1_c).size(); ++j) { + for (auto k = 0u; k < h.axis(2_c).size(); ++k) { BOOST_TEST_EQ(h.at(i, j, k).value(), i + j + k); BOOST_TEST_EQ(h.at(i, j, k).variance(), (i + j + k) * (i + j + k)); } @@ -402,8 +403,10 @@ void run_tests() { // add_2 { - auto a = make(Tag(), axis::integer<>(0, 2)); - auto b = make(Tag(), axis::integer<>(0, 2)); + auto a = make_s(Tag(), array_storage>(), + axis::integer<>(0, 2)); + auto b = make_s(Tag(), array_storage>(), + axis::integer<>(0, 2)); a(0); BOOST_TEST_EQ(a.at(0).variance(), 1); @@ -476,20 +479,20 @@ void run_tests() { BOOST_TEST_EQ(d.at(1), 3); auto e = 3 * a; auto f = b * 2; - BOOST_TEST_EQ(e.at(0).value(), 3); - BOOST_TEST_EQ(e.at(1).value(), 0); - BOOST_TEST_EQ(f.at(0).value(), 0); - BOOST_TEST_EQ(f.at(1).value(), 2); + BOOST_TEST_EQ(e.at(0), 3); + BOOST_TEST_EQ(e.at(1), 0); + BOOST_TEST_EQ(f.at(0), 0); + BOOST_TEST_EQ(f.at(1), 2); auto r = a; r += b; r += e; - BOOST_TEST_EQ(r.at(0).value(), 4); - BOOST_TEST_EQ(r.at(1).value(), 1); + BOOST_TEST_EQ(r.at(0), 4); + BOOST_TEST_EQ(r.at(1), 1); BOOST_TEST_EQ(r, a + b + 3 * a); auto s = r / 4; r /= 4; - BOOST_TEST_EQ(r.at(0).value(), 1); - BOOST_TEST_EQ(r.at(1).value(), 0.25); + BOOST_TEST_EQ(r.at(0), 1); + BOOST_TEST_EQ(r.at(1), 0.25); BOOST_TEST_EQ(r, s); } @@ -500,14 +503,14 @@ void run_tests() { os << a; BOOST_TEST_EQ(os.str(), "histogram(" - "\n regular(3, -1, 1, label='r')," - "\n integer(0, 2, label='i')," + "\n regular(3, -1, 1, metadata='r', options=underflow_and_overflow)," + "\n integer(0, 2, metadata='i', options=underflow_and_overflow)," "\n)"); } // histogram_reset { - auto h = make(Tag(), axis::integer<>(0, 2, "", axis::uoflow_type::off)); + auto h = make(Tag(), axis::integer<>(0, 2, {}, axis::option_type::none)); h(0); h(1); BOOST_TEST_EQ(h.at(0), 1); @@ -540,16 +543,14 @@ void run_tests() { */ auto h1_0 = h1.reduce_to(0_c); - BOOST_TEST_EQ(h1_0.dim(), 1); + BOOST_TEST_EQ(h1_0.rank(), 1); BOOST_TEST_EQ(sum(h1_0), 5); BOOST_TEST_EQ(h1_0.at(0), 2); BOOST_TEST_EQ(h1_0.at(1), 3); - BOOST_TEST_EQ(h1_0.axis()[0].lower(), 0); - BOOST_TEST_EQ(h1_0.axis()[1].lower(), 1); BOOST_TEST(h1_0.axis() == h1.axis(0_c)); auto h1_1 = h1.reduce_to(1_c); - BOOST_TEST_EQ(h1_1.dim(), 1); + BOOST_TEST_EQ(h1_1.rank(), 1); BOOST_TEST_EQ(sum(h1_1), 5); BOOST_TEST_EQ(h1_1.at(0), 2); BOOST_TEST_EQ(h1_1.at(1), 2); @@ -565,21 +566,21 @@ void run_tests() { h2(1, 0, 2); auto h2_0 = h2.reduce_to(0_c); - BOOST_TEST_EQ(h2_0.dim(), 1); + BOOST_TEST_EQ(h2_0.rank(), 1); BOOST_TEST_EQ(sum(h2_0), 5); BOOST_TEST_EQ(h2_0.at(0), 4); BOOST_TEST_EQ(h2_0.at(1), 1); BOOST_TEST(h2_0.axis() == axis::integer<>(0, 2)); auto h2_1 = h2.reduce_to(1_c); - BOOST_TEST_EQ(h2_1.dim(), 1); + BOOST_TEST_EQ(h2_1.rank(), 1); BOOST_TEST_EQ(sum(h2_1), 5); BOOST_TEST_EQ(h2_1.at(0), 3); BOOST_TEST_EQ(h2_1.at(1), 2); BOOST_TEST(h2_1.axis() == axis::integer<>(0, 3)); auto h2_2 = h2.reduce_to(2_c); - BOOST_TEST_EQ(h2_2.dim(), 1); + BOOST_TEST_EQ(h2_2.rank(), 1); BOOST_TEST_EQ(sum(h2_2), 5); BOOST_TEST_EQ(h2_2.at(0), 2); BOOST_TEST_EQ(h2_2.at(1), 1); @@ -587,7 +588,7 @@ void run_tests() { BOOST_TEST(h2_2.axis() == axis::integer<>(0, 4)); auto h2_01 = h2.reduce_to(0_c, 1_c); - BOOST_TEST_EQ(h2_01.dim(), 2); + BOOST_TEST_EQ(h2_01.rank(), 2); BOOST_TEST_EQ(sum(h2_01), 5); BOOST_TEST_EQ(h2_01.at(0, 0), 2); BOOST_TEST_EQ(h2_01.at(0, 1), 2); @@ -596,7 +597,7 @@ void run_tests() { BOOST_TEST(h2_01.axis(1_c) == axis::integer<>(0, 3)); auto h2_02 = h2.reduce_to(0_c, 2_c); - BOOST_TEST_EQ(h2_02.dim(), 2); + BOOST_TEST_EQ(h2_02.rank(), 2); BOOST_TEST_EQ(sum(h2_02), 5); BOOST_TEST_EQ(h2_02.at(0, 0), 2); BOOST_TEST_EQ(h2_02.at(0, 1), 1); @@ -606,7 +607,7 @@ void run_tests() { BOOST_TEST(h2_02.axis(1_c) == axis::integer<>(0, 4)); auto h2_12 = h2.reduce_to(1_c, 2_c); - BOOST_TEST_EQ(h2_12.dim(), 2); + BOOST_TEST_EQ(h2_12.rank(), 2); BOOST_TEST_EQ(sum(h2_12), 5); BOOST_TEST_EQ(h2_12.at(0, 0), 1); BOOST_TEST_EQ(h2_12.at(1, 0), 1); @@ -619,14 +620,12 @@ void run_tests() { // custom axis { struct custom_axis : public axis::integer<> { - using value_type = const char*; // type that is fed to the axis - using integer::integer; // inherit ctors of base - // the customization point - // - accept const char* and convert to int - // - then call index method of base class - int index(value_type s) const { return integer::index(std::atoi(s)); } + // customization point: convert argument and call base class + int operator()(const char* s) const { + return integer::operator()(std::atoi(s)); + } }; auto h = make(Tag(), custom_axis(0, 3)); @@ -635,7 +634,7 @@ void run_tests() { h("1"); h("9"); - BOOST_TEST_EQ(h.dim(), 1); + BOOST_TEST_EQ(h.rank(), 1); BOOST_TEST_EQ(h.axis(), custom_axis(0, 3)); BOOST_TEST_EQ(h.at(0), 1); BOOST_TEST_EQ(h.at(1), 1); @@ -644,13 +643,14 @@ void run_tests() { // histogram iterator 1D { - auto h = make(Tag(), axis::integer<>(0, 3)); + auto h = make_s(Tag(), array_storage>(), + axis::integer<>(0, 3)); const auto& a = h.axis(); h(weight(2), 0); h(1); h(1); auto it = h.begin(); - BOOST_TEST_EQ(it.dim(), 1); + BOOST_TEST_EQ(it.rank(), 1); BOOST_TEST_EQ(it.idx(), 0); BOOST_TEST_EQ(it.bin(), a[0]); @@ -678,8 +678,10 @@ void run_tests() { // histogram iterator 2D { - auto h = make(Tag(), axis::integer<>(0, 1), - axis::integer<>(2, 4, "", axis::uoflow_type::off)); + auto h = make_s(Tag(), + array_storage>(), + axis::integer<>(0, 1), + axis::integer<>(2, 4, "", axis::option_type::none)); const auto& a0 = h.axis(0_c); const auto& a1 = h.axis(1_c); h(weight(2), 0, 2); @@ -687,7 +689,7 @@ void run_tests() { h(1, 3); auto it = h.begin(); - BOOST_TEST_EQ(it.dim(), 2); + BOOST_TEST_EQ(it.rank(), 2); BOOST_TEST_EQ(it.idx(0), 0); BOOST_TEST_EQ(it.idx(1), 0); @@ -742,16 +744,18 @@ void run_tests() { { auto h = make(Tag(), axis::integer<>(0, 3)); for (int i = 0; i < 3; ++i) h(i); - auto a = std::vector>(); + auto a = std::vector(); std::partial_sum(h.begin(), h.end(), std::back_inserter(a)); - BOOST_TEST_EQ(a[0].value(), 1); - BOOST_TEST_EQ(a[1].value(), 2); - BOOST_TEST_EQ(a[2].value(), 3); + BOOST_TEST_EQ(a[0], 1); + BOOST_TEST_EQ(a[1], 2); + BOOST_TEST_EQ(a[2], 3); } // using STL containers { - auto h = make(Tag(), axis::integer<>(0, 2), axis::regular<>(2, 2, 4)); + auto h = make_s(Tag(), + array_storage>(), + axis::integer<>(0, 2), axis::regular<>(2, 2, 4)); // vector in h(std::vector({0, 2})); // pair in @@ -805,9 +809,8 @@ void run_tests() { tracing_allocator_db db; { tracing_allocator a(db); - auto h = make_s(Tag(), array_storage>(a), - axis::integer>( - 0, 1024, std::string(512, 'c'), axis::uoflow_type::on, a)); + auto h = make_s(Tag(), array_storage>(a), + axis::integer<>(0, 1024)); h(0); } @@ -815,12 +818,8 @@ void run_tests() { BOOST_TEST_EQ(db[typeid(int)].first, db[typeid(int)].second); BOOST_TEST_GE(db[typeid(int)].first, 1024u); - // char allocation for axis label - BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second); - BOOST_TEST_GE(db[typeid(char)].first, 512u); - - if (Tag()) { // axis::any allocation, only for dynamic histogram - using T = axis::any>>; + if (Tag()) { // axis::variant allocation, only for dynamic histogram + using T = axis::variant>; BOOST_TEST_EQ(db[typeid(T)].first, db[typeid(T)].second); BOOST_TEST_GE(db[typeid(T)].first, 1u); } diff --git a/test/meta_test.cpp b/test/meta_test.cpp index 480a43e3..6e3e5c1f 100644 --- a/test/meta_test.cpp +++ b/test/meta_test.cpp @@ -126,6 +126,14 @@ int main() { BOOST_TEST_TRAIT_TRUE((is_iterable)); } + // is_streamable + { + struct Foo {}; + BOOST_TEST_TRAIT_TRUE((is_streamable)); + BOOST_TEST_TRAIT_TRUE((is_streamable)); + BOOST_TEST_TRAIT_FALSE((is_streamable)); + } + // is_axis_variant { struct A {}; @@ -136,29 +144,36 @@ int main() { // classify_container { - using result1 = classify_container; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using A = classify_container; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result1a = classify_container; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using B = classify_container; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result2 = classify_container>; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using C = classify_container>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result2a = classify_container&>; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using D = classify_container&>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result3 = classify_container>; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using E = classify_container>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result3a = classify_container&>; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using F = classify_container&>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result4 = classify_container; - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using G = classify_container; + BOOST_TEST_TRAIT_TRUE((std::is_same)); - using result5 = classify_container; // has no std::end - BOOST_TEST_TRAIT_TRUE((std::is_same)); + using H = classify_container; // has no std::end + BOOST_TEST_TRAIT_TRUE((std::is_same)); + + using I = classify_container>; + BOOST_TEST_TRAIT_TRUE((std::is_same)); + + auto j = {0, 1}; + using J = classify_container; + BOOST_TEST_TRAIT_TRUE((std::is_same)); } // bool mask @@ -244,6 +259,17 @@ int main() { BOOST_TEST_TRAIT_TRUE((std::is_same, int>)); } + // args_type + { + struct Foo { + static int f1(char); + int f2(long) const; + }; + + BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, std::tuple>)); + } + // visitor_return_type { using V1 = bh::axis::variant;