Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / histogram / detail / axes.hpp
index 1372936..0e9e93d 100644 (file)
@@ -9,8 +9,11 @@
 
 #include <array>
 #include <boost/assert.hpp>
+#include <boost/core/nvp.hpp>
 #include <boost/histogram/axis/traits.hpp>
 #include <boost/histogram/axis/variant.hpp>
+#include <boost/histogram/detail/make_default.hpp>
+#include <boost/histogram/detail/optional_index.hpp>
 #include <boost/histogram/detail/static_if.hpp>
 #include <boost/histogram/fwd.hpp>
 #include <boost/mp11/algorithm.hpp>
@@ -19,6 +22,7 @@
 #include <boost/mp11/utility.hpp>
 #include <boost/throw_exception.hpp>
 #include <stdexcept>
+#include <string>
 #include <tuple>
 #include <type_traits>
 
@@ -50,6 +54,20 @@ constexpr unsigned axes_rank(const std::tuple<Ts...>&) {
   return static_cast<unsigned>(sizeof...(Ts));
 }
 
+template <class T>
+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=<new max size> "
+                              "to increase internal buffers"));
+}
+
+// tuple is never too large because internal buffers adapt to size of tuple
+template <class... Ts>
+void throw_if_axes_is_too_large(const std::tuple<Ts...>&) {}
+
 template <unsigned N, class... Ts>
 decltype(auto) axis_get(std::tuple<Ts...>& axes) {
   return std::get<N>(axes);
@@ -71,38 +89,37 @@ decltype(auto) axis_get(const T& axes) {
 }
 
 template <class... Ts>
-decltype(auto) axis_get(std::tuple<Ts...>& axes, unsigned i) {
-  using namespace boost::mp11;
+auto axis_get(std::tuple<Ts...>& axes, const unsigned i) {
   constexpr auto S = sizeof...(Ts);
-  using V = mp_unique<axis::variant<Ts*...>>;
-  return mp_with_index<S>(i, [&axes](auto i) { return V(&std::get<i>(axes)); });
+  using V = mp11::mp_unique<axis::variant<Ts*...>>;
+  return mp11::mp_with_index<S>(i, [&axes](auto i) { return V(&std::get<i>(axes)); });
 }
 
 template <class... Ts>
-decltype(auto) axis_get(const std::tuple<Ts...>& axes, unsigned i) {
-  using namespace boost::mp11;
+auto axis_get(const std::tuple<Ts...>& axes, const unsigned i) {
   constexpr auto S = sizeof...(Ts);
-  using V = mp_unique<axis::variant<const Ts*...>>;
-  return mp_with_index<S>(i, [&axes](auto i) { return V(&std::get<i>(axes)); });
+  using V = mp11::mp_unique<axis::variant<const Ts*...>>;
+  return mp11::mp_with_index<S>(i, [&axes](auto i) { return V(&std::get<i>(axes)); });
 }
 
 template <class T>
-decltype(auto) axis_get(T& axes, unsigned i) {
-  return axes.at(i);
+decltype(auto) axis_get(T& axes, const unsigned i) {
+  return axes[i];
 }
 
 template <class T>
-decltype(auto) axis_get(const T& axes, unsigned i) {
-  return axes.at(i);
+decltype(auto) axis_get(const T& axes, const unsigned i) {
+  return axes[i];
 }
 
 template <class... Ts, class... Us>
 bool axes_equal(const std::tuple<Ts...>& ts, const std::tuple<Us...>& us) {
-  return static_if<std::is_same<mp11::mp_list<Ts...>, mp11::mp_list<Us...>>>(
+  using namespace ::boost::mp11;
+  return static_if<std::is_same<mp_list<Ts...>, mp_list<Us...>>>(
       [](const auto& ts, const auto& us) {
-        using N = mp11::mp_size<std::decay_t<decltype(ts)>>;
+        using N = mp_size<std::decay_t<decltype(ts)>>;
         bool equal = true;
-        mp11::mp_for_each<mp11::mp_iota<N>>(
+        mp_for_each<mp_iota<N>>(
             [&](auto I) { equal &= relaxed_equal(std::get<I>(ts), std::get<I>(us)); });
         return equal;
       },
@@ -111,10 +128,10 @@ bool axes_equal(const std::tuple<Ts...>& ts, const std::tuple<Us...>& us) {
 
 template <class T, class... Us>
 bool axes_equal(const T& t, const std::tuple<Us...>& u) {
+  using namespace ::boost::mp11;
   if (t.size() != sizeof...(Us)) return false;
   bool equal = true;
-  mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Us)>>(
-      [&](auto I) { equal &= t[I] == std::get<I>(u); });
+  mp_for_each<mp_iota_c<sizeof...(Us)>>([&](auto I) { equal &= t[I] == std::get<I>(u); });
   return equal;
 }
 
@@ -131,7 +148,8 @@ bool axes_equal(const T& t, const U& u) {
 
 template <class... Ts, class... Us>
 void axes_assign(std::tuple<Ts...>& t, const std::tuple<Us...>& u) {
-  static_if<std::is_same<mp11::mp_list<Ts...>, mp11::mp_list<Us...>>>(
+  using namespace ::boost::mp11;
+  static_if<std::is_same<mp_list<Ts...>, mp_list<Us...>>>(
       [](auto& a, const auto& b) { a = b; },
       [](auto&, const auto&) {
         BOOST_THROW_EXCEPTION(
@@ -142,8 +160,9 @@ void axes_assign(std::tuple<Ts...>& t, const std::tuple<Us...>& u) {
 
 template <class... Ts, class U>
 void axes_assign(std::tuple<Ts...>& t, const U& u) {
-  mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Ts)>>([&](auto I) {
-    using T = mp11::mp_at_c<std::tuple<Ts...>, I>;
+  using namespace ::boost::mp11;
+  mp_for_each<mp_iota_c<sizeof...(Ts)>>([&](auto I) {
+    using T = mp_at_c<std::tuple<Ts...>, I>;
     std::get<I>(t) = axis::get<T>(u[I]);
   });
 }
@@ -152,8 +171,8 @@ template <class T, class... Us>
 void axes_assign(T& t, const std::tuple<Us...>& u) {
   // resize instead of reserve, because t may not be empty and we want exact capacity
   t.resize(sizeof...(Us));
-  mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Us)>>(
-      [&](auto I) { t[I] = std::get<I>(u); });
+  using namespace ::boost::mp11;
+  mp_for_each<mp_iota_c<sizeof...(Us)>>([&](auto I) { t[I] = std::get<I>(u); });
 }
 
 template <typename T, typename U>
@@ -161,6 +180,24 @@ void axes_assign(T& t, const U& u) {
   t.assign(u.begin(), u.end());
 }
 
+template <class Archive, class T>
+void axes_serialize(Archive& ar, T& axes) {
+  ar& make_nvp("axes", axes);
+}
+
+template <class Archive, class... Ts>
+void axes_serialize(Archive& ar, std::tuple<Ts...>& axes) {
+  // needed to keep serialization format backward compatible
+  struct proxy {
+    std::tuple<Ts...>& 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 <class T>
 auto make_empty_dynamic_axes(const T& axes) {
@@ -175,33 +212,40 @@ auto make_empty_dynamic_axes(const std::tuple<Ts...>&) {
   return std::vector<mp_if_c<(mp_size<L>::value == 1), mp_first<L>, L>>{};
 }
 
-template <typename T>
+template <class T>
 void axis_index_is_valid(const T& axes, const unsigned N) {
   BOOST_ASSERT_MSG(N < axes_rank(axes), "index out of range");
 }
 
-template <typename F, typename T>
-void for_each_axis_impl(std::true_type, const T& axes, F&& f) {
-  for (const auto& x : axes) { axis::visit(std::forward<F>(f), x); }
+template <class Axes, class V>
+void for_each_axis_impl(std::true_type, Axes&& axes, V&& v) {
+  for (auto&& a : axes) { axis::visit(std::forward<V>(v), a); }
+}
+
+template <class Axes, class V>
+void for_each_axis_impl(std::false_type, Axes&& axes, V&& v) {
+  for (auto&& a : axes) std::forward<V>(v)(a);
 }
 
-template <typename F, typename T>
-void for_each_axis_impl(std::false_type, const T& axes, F&& f) {
-  for (const auto& x : axes) std::forward<F>(f)(x);
+template <class Axes, class V>
+void for_each_axis(Axes&& a, V&& v) {
+  using namespace ::boost::mp11;
+  using T = mp_first<std::decay_t<Axes>>;
+  for_each_axis_impl(is_axis_variant<T>(), std::forward<Axes>(a), std::forward<V>(v));
 }
 
-template <typename F, typename T>
-void for_each_axis(const T& axes, F&& f) {
-  using U = mp11::mp_first<T>;
-  for_each_axis_impl(is_axis_variant<U>(), axes, std::forward<F>(f));
+template <class V, class... Axis>
+void for_each_axis(const std::tuple<Axis...>& a, V&& v) {
+  mp11::tuple_for_each(a, std::forward<V>(v));
 }
 
-template <typename F, typename... Ts>
-void for_each_axis(const std::tuple<Ts...>& axes, F&& f) {
-  mp11::tuple_for_each(axes, std::forward<F>(f));
+template <class V, class... Axis>
+void for_each_axis(std::tuple<Axis...>& a, V&& v) {
+  mp11::tuple_for_each(a, std::forward<V>(v));
 }
 
-template <typename T>
+// total number of bins including *flow bins
+template <class T>
 std::size_t bincount(const T& axes) {
   std::size_t n = 1;
   for_each_axis(axes, [&n](const auto& a) {
@@ -213,13 +257,27 @@ std::size_t bincount(const T& axes) {
   return n;
 }
 
+// initial offset for the linear index
 template <class T>
-using tuple_size_t = typename std::tuple_size<T>::type;
+std::size_t offset(const T& axes) {
+  std::size_t n = 0;
+  for_each_axis(axes, [&n, stride = static_cast<std::size_t>(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 <class T>
+using buffer_size_impl = typename std::tuple_size<T>::type;
 
 template <class T>
 using buffer_size = mp11::mp_eval_or<
-    std::integral_constant<std::size_t, BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>, tuple_size_t,
-    T>;
+    std::integral_constant<std::size_t, BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>,
+    buffer_size_impl, T>;
 
 template <class T, std::size_t N>
 class sub_array : public std::array<T, N> {
@@ -252,11 +310,74 @@ private:
 template <class U, class T>
 using stack_buffer = sub_array<U, buffer_size<T>::value>;
 
-template <class U, class T, class... Ts>
-auto make_stack_buffer(const T& t, const Ts&... ts) {
-  return stack_buffer<U, T>(axes_rank(t), ts...);
+// make default-constructed buffer (no initialization for POD types)
+template <class U, class T>
+auto make_stack_buffer(const T& t) {
+  return stack_buffer<U, T>(axes_rank(t));
+}
+
+// make buffer with elements initialized to v
+template <class U, class T, class V>
+auto make_stack_buffer(const T& t, V&& v) {
+  return stack_buffer<U, T>(axes_rank(t), std::forward<V>(v));
 }
 
+template <class T>
+using has_underflow =
+    decltype(axis::traits::static_options<T>::test(axis::option::underflow));
+
+template <class T>
+using is_growing = decltype(axis::traits::static_options<T>::test(axis::option::growth));
+
+template <class T>
+using is_not_inclusive = mp11::mp_not<axis::traits::static_is_inclusive<T>>;
+
+// for vector<T>
+template <class T>
+struct axis_types_impl {
+  using type = mp11::mp_list<std::decay_t<T>>;
+};
+
+// for vector<variant<Ts...>>
+template <class... Ts>
+struct axis_types_impl<axis::variant<Ts...>> {
+  using type = mp11::mp_list<std::decay_t<Ts>...>;
+};
+
+// for tuple<Ts...>
+template <class... Ts>
+struct axis_types_impl<std::tuple<Ts...>> {
+  using type = mp11::mp_list<std::decay_t<Ts>...>;
+};
+
+template <class T>
+using axis_types =
+    typename axis_types_impl<mp11::mp_if<is_vector_like<T>, mp11::mp_first<T>, T>>::type;
+
+template <template <class> class Trait, class Axes>
+using has_special_axis = mp11::mp_any_of<axis_types<Axes>, Trait>;
+
+template <class Axes>
+using has_growing_axis = mp11::mp_any_of<axis_types<Axes>, is_growing>;
+
+template <class Axes>
+using has_non_inclusive_axis = mp11::mp_any_of<axis_types<Axes>, is_not_inclusive>;
+
+template <class T>
+constexpr std::size_t type_score() {
+  return sizeof(T) *
+         (std::is_integral<T>::value ? 1 : std::is_floating_point<T>::value ? 10 : 100);
+}
+
+// arbitrary ordering of types
+template <class T, class U>
+using type_less = mp11::mp_bool<(type_score<T>() < type_score<U>())>;
+
+template <class Axes>
+using value_types = mp11::mp_sort<
+    mp11::mp_unique<mp11::mp_transform<axis::traits::value_type, axis_types<Axes>>>,
+    type_less>;
+
 } // namespace detail
 } // namespace histogram
 } // namespace boost