// 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) #ifndef BOOST_HISTOGRAM_DETAIL_AXES_HPP #define BOOST_HISTOGRAM_DETAIL_AXES_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Most of the histogram code is generic and works for any number of axes. Buffers with a * fixed maximum capacity are used in some places, which have a size equal to the rank of * a histogram. The buffers are statically allocated to improve performance, which means * that they need a preset maximum capacity. 32 seems like a safe upper limit for the rank * (you can nevertheless increase it here if necessary): the simplest non-trivial axis has * 2 bins; even if counters are used which need only a byte of storage per bin, this still * corresponds to 4 GB of storage. */ #ifndef BOOST_HISTOGRAM_DETAIL_AXES_LIMIT #define BOOST_HISTOGRAM_DETAIL_AXES_LIMIT 32 #endif namespace boost { namespace histogram { namespace detail { template unsigned axes_rank(const T& axes) { using std::begin; using std::end; return static_cast(std::distance(begin(axes), end(axes))); } template constexpr unsigned axes_rank(const std::tuple&) { return static_cast(sizeof...(Ts)); } template void throw_if_axes_is_too_large(const T& axes) { if (axes_rank(axes) > BOOST_HISTOGRAM_DETAIL_AXES_LIMIT) BOOST_THROW_EXCEPTION( std::invalid_argument("length of axis vector exceeds internal buffers, " "recompile with " "-DBOOST_HISTOGRAM_DETAIL_AXES_LIMIT= " "to increase internal buffers")); } // tuple is never too large because internal buffers adapt to size of tuple template void throw_if_axes_is_too_large(const std::tuple&) {} template decltype(auto) axis_get(std::tuple& axes) { return std::get(axes); } template decltype(auto) axis_get(const std::tuple& axes) { return std::get(axes); } template decltype(auto) axis_get(T& axes) { return axes[N]; } template decltype(auto) axis_get(const T& axes) { return axes[N]; } template auto axis_get(std::tuple& axes, const unsigned i) { constexpr auto S = sizeof...(Ts); using V = mp11::mp_unique>; return mp11::mp_with_index(i, [&axes](auto i) { return V(&std::get(axes)); }); } template auto axis_get(const std::tuple& axes, const unsigned i) { constexpr auto S = sizeof...(Ts); using V = mp11::mp_unique>; return mp11::mp_with_index(i, [&axes](auto i) { return V(&std::get(axes)); }); } template decltype(auto) axis_get(T& axes, const unsigned i) { return axes[i]; } template decltype(auto) axis_get(const T& axes, const unsigned i) { return axes[i]; } template bool axes_equal(const std::tuple& ts, const std::tuple& us) { using namespace ::boost::mp11; return static_if, mp_list>>( [](const auto& ts, const auto& us) { using N = mp_size>; bool equal = true; mp_for_each>( [&](auto I) { equal &= relaxed_equal(std::get(ts), std::get(us)); }); return equal; }, [](const auto&, const auto&) { return false; }, ts, us); } template bool axes_equal(const T& t, const std::tuple& u) { using namespace ::boost::mp11; if (t.size() != sizeof...(Us)) return false; bool equal = true; mp_for_each>([&](auto I) { equal &= t[I] == std::get(u); }); return equal; } template bool axes_equal(const std::tuple& t, const U& u) { return axes_equal(u, t); } template bool axes_equal(const T& t, const U& u) { if (t.size() != u.size()) return false; return std::equal(t.begin(), t.end(), u.begin()); } template void axes_assign(std::tuple& t, const std::tuple& u) { using namespace ::boost::mp11; static_if, mp_list>>( [](auto& a, const auto& b) { a = b; }, [](auto&, const auto&) { BOOST_THROW_EXCEPTION( std::invalid_argument("cannot assign axes, types do not match")); }, t, u); } template void axes_assign(std::tuple& t, const U& u) { using namespace ::boost::mp11; mp_for_each>([&](auto I) { using T = mp_at_c, I>; std::get(t) = axis::get(u[I]); }); } template void axes_assign(T& t, const std::tuple& u) { // resize instead of reserve, because t may not be empty and we want exact capacity t.resize(sizeof...(Us)); using namespace ::boost::mp11; mp_for_each>([&](auto I) { t[I] = std::get(u); }); } template void axes_assign(T& t, const U& u) { t.assign(u.begin(), u.end()); } template void axes_serialize(Archive& ar, T& axes) { ar& make_nvp("axes", axes); } template void axes_serialize(Archive& ar, std::tuple& axes) { // needed to keep serialization format backward compatible struct proxy { std::tuple& t; void serialize(Archive& ar, unsigned /* version */) { mp11::tuple_for_each(t, [&ar](auto& x) { ar& make_nvp("item", x); }); } }; proxy p{axes}; ar& make_nvp("axes", p); } // create empty dynamic axis which can store any axes types from the argument template auto make_empty_dynamic_axes(const T& axes) { return make_default(axes); } template auto make_empty_dynamic_axes(const std::tuple&) { using namespace ::boost::mp11; using L = mp_unique>; // return std::vector> or std::vector return std::vector::value == 1), mp_first, L>>{}; } template void axis_index_is_valid(const T& axes, const unsigned N) { BOOST_ASSERT_MSG(N < axes_rank(axes), "index out of range"); } template void for_each_axis_impl(std::true_type, Axes&& axes, V&& v) { for (auto&& a : axes) { axis::visit(std::forward(v), a); } } template void for_each_axis_impl(std::false_type, Axes&& axes, V&& v) { for (auto&& a : axes) std::forward(v)(a); } template void for_each_axis(Axes&& a, V&& v) { using namespace ::boost::mp11; using T = mp_first>; for_each_axis_impl(is_axis_variant(), std::forward(a), std::forward(v)); } template void for_each_axis(const std::tuple& a, V&& v) { mp11::tuple_for_each(a, std::forward(v)); } template void for_each_axis(std::tuple& a, V&& v) { mp11::tuple_for_each(a, std::forward(v)); } // total number of bins including *flow bins template std::size_t bincount(const T& axes) { std::size_t n = 1; for_each_axis(axes, [&n](const auto& a) { const auto old = n; const auto s = axis::traits::extent(a); n *= s; if (s > 0 && n < old) BOOST_THROW_EXCEPTION(std::overflow_error("bincount overflow")); }); return n; } // initial offset for the linear index template std::size_t offset(const T& axes) { std::size_t n = 0; for_each_axis(axes, [&n, stride = static_cast(1)](const auto& a) mutable { if (axis::traits::options(a) & axis::option::growth) n = invalid_index; else if (n != invalid_index && axis::traits::options(a) & axis::option::underflow) n += stride; stride *= axis::traits::extent(a); }); return n; } template using buffer_size_impl = typename std::tuple_size::type; template using buffer_size = mp11::mp_eval_or< std::integral_constant, buffer_size_impl, T>; template class sub_array : public std::array { using base_type = std::array; public: explicit sub_array(std::size_t s) noexcept( std::is_nothrow_default_constructible::value) : size_(s) { BOOST_ASSERT_MSG(size_ <= N, "requested size exceeds size of static buffer"); } sub_array(std::size_t s, const T& value) noexcept(std::is_nothrow_copy_constructible::value) : size_(s) { BOOST_ASSERT_MSG(size_ <= N, "requested size exceeds size of static buffer"); std::array::fill(value); } // need to override both versions of std::array auto end() noexcept { return base_type::begin() + size_; } auto end() const noexcept { return base_type::begin() + size_; } auto size() const noexcept { return size_; } private: std::size_t size_; }; template using stack_buffer = sub_array::value>; // make default-constructed buffer (no initialization for POD types) template auto make_stack_buffer(const T& t) { return stack_buffer(axes_rank(t)); } // make buffer with elements initialized to v template auto make_stack_buffer(const T& t, V&& v) { return stack_buffer(axes_rank(t), std::forward(v)); } template using has_underflow = decltype(axis::traits::static_options::test(axis::option::underflow)); template using is_growing = decltype(axis::traits::static_options::test(axis::option::growth)); template using is_not_inclusive = mp11::mp_not>; // for vector template struct axis_types_impl { using type = mp11::mp_list>; }; // for vector> template struct axis_types_impl> { using type = mp11::mp_list...>; }; // for tuple template struct axis_types_impl> { using type = mp11::mp_list...>; }; template using axis_types = typename axis_types_impl, mp11::mp_first, T>>::type; template