feat():initial version

This commit is contained in:
2026-06-09 20:16:47 +08:00
commit 85fbb3188c
14809 changed files with 3044607 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
// Copyright 2019 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_ACCUMULATOR_TRAITS_HPP
#define BOOST_HISTOGRAM_DETAIL_ACCUMULATOR_TRAITS_HPP
#include <boost/histogram/detail/priority.hpp>
#include <boost/histogram/fwd.hpp>
#include <tuple>
#include <type_traits>
namespace boost {
// forward declare accumulator_set so that it can be matched below
namespace accumulators {
template <class, class, class>
struct accumulator_set;
}
namespace histogram {
namespace detail {
template <bool WeightSupport, class... Ts>
struct accumulator_traits_holder {
static constexpr bool weight_support = WeightSupport;
using args = std::tuple<Ts...>;
};
// member function pointer with weight_type as first argument is better match
template <class R, class T, class U, class... Ts>
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_call_op(
R (T::*)(boost::histogram::weight_type<U>, Ts...));
template <class R, class T, class U, class... Ts>
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_call_op(
R (T::*)(boost::histogram::weight_type<U>&, Ts...));
template <class R, class T, class U, class... Ts>
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_call_op(
R (T::*)(boost::histogram::weight_type<U>&&, Ts...));
template <class R, class T, class U, class... Ts>
accumulator_traits_holder<true, Ts...> accumulator_traits_impl_call_op(
R (T::*)(const boost::histogram::weight_type<U>&, Ts...));
// member function pointer only considered if all specializations above fail
template <class R, class T, class... Ts>
accumulator_traits_holder<false, Ts...> accumulator_traits_impl_call_op(R (T::*)(Ts...));
template <class T>
auto accumulator_traits_impl(T&, priority<1>)
-> decltype(accumulator_traits_impl_call_op(&T::operator()));
template <class T>
auto accumulator_traits_impl(T&, priority<1>)
-> decltype(std::declval<T&>() += 0, accumulator_traits_holder<true>{});
template <class T>
auto accumulator_traits_impl(T&, priority<0>) -> accumulator_traits_holder<false>;
// for boost.accumulators compatibility
template <class S, class F, class W>
accumulator_traits_holder<false, S> accumulator_traits_impl(
boost::accumulators::accumulator_set<S, F, W>&, priority<1>) {
static_assert(std::is_same<W, void>::value,
"accumulator_set with weights is not directly supported, please use "
"a wrapper class that implements the Accumulator concept");
}
template <class T>
using accumulator_traits =
decltype(accumulator_traits_impl(std::declval<T&>(), priority<1>{}));
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,63 @@
// 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_ARGS_TYPE_HPP
#define BOOST_HISTOGRAM_DETAIL_ARGS_TYPE_HPP
#include <tuple>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
struct args_type_impl {
using T::ERROR_this_should_never_be_instantiated_please_write_an_issue;
};
template <class R, class T, class... Ts>
struct args_type_impl<R (T::*)(Ts...)> {
using type = std::tuple<Ts...>;
};
template <class R, class T, class... Ts>
struct args_type_impl<R (T ::*)(Ts...) const> {
using type = std::tuple<Ts...>;
};
template <class R, class... Ts>
struct args_type_impl<R (*)(Ts...)> {
using type = std::tuple<Ts...>;
};
#if __cpp_noexcept_function_type >= 201510
template <class R, class T, class... Ts>
struct args_type_impl<R (T::*)(Ts...) noexcept> {
using type = std::tuple<Ts...>;
};
template <class R, class T, class... Ts>
struct args_type_impl<R (T ::*)(Ts...) const noexcept> {
using type = std::tuple<Ts...>;
};
template <class R, class... Ts>
struct args_type_impl<R (*)(Ts...) noexcept> {
using type = std::tuple<Ts...>;
};
#endif
template <class FunctionPointer>
using args_type = typename args_type_impl<FunctionPointer>::type;
template <class T, std::size_t N = 0>
using arg_type = std::tuple_element_t<N, args_type<T>>;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,83 @@
// Copyright 2019 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_ARGUMENT_TRAITS_HPP
#define BOOST_HISTOGRAM_DETAIL_ARGUMENT_TRAITS_HPP
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/mp11/list.hpp>
#include <tuple>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
struct is_weight_impl : mp11::mp_false {};
template <class T>
struct is_weight_impl<weight_type<T>> : mp11::mp_true {};
template <class T>
using is_weight = is_weight_impl<T>;
template <class T>
struct is_sample_impl : mp11::mp_false {};
template <class T>
struct is_sample_impl<sample_type<T>> : mp11::mp_true {};
template <class T>
using is_sample = is_sample_impl<T>;
template <int Idx, class L>
struct sample_args_impl {
using type = mp11::mp_first<std::decay_t<mp11::mp_at_c<L, (Idx >= 0 ? Idx : 0)>>>;
};
template <class L>
struct sample_args_impl<-1, L> {
using type = std::tuple<>;
};
template <std::size_t NArgs, std::size_t Start, int WeightPos, int SamplePos,
class SampleArgs>
struct argument_traits_holder {
using nargs = mp11::mp_size_t<NArgs>;
using start = mp11::mp_size_t<Start>;
using wpos = mp11::mp_int<WeightPos>;
using spos = mp11::mp_int<SamplePos>;
using sargs = SampleArgs;
};
template <class... Ts>
struct argument_traits_impl {
using list_ = mp11::mp_list<Ts...>;
static constexpr std::size_t size_ = sizeof...(Ts);
static constexpr std::size_t weight_ = mp11::mp_find_if<list_, is_weight>::value;
static constexpr std::size_t sample_ = mp11::mp_find_if<list_, is_sample>::value;
static constexpr int spos_ = (sample_ < size_ ? static_cast<int>(sample_) : -1);
static constexpr int wpos_ = (weight_ < size_ ? static_cast<int>(weight_) : -1);
using type =
argument_traits_holder<(size_ - (weight_ < size_) - (sample_ < size_)),
(weight_ < size_ && sample_ < size_ &&
(weight_ + sample_ < 2)
? 2
: ((weight_ == 0 || sample_ == 0) ? 1 : 0)),
wpos_, spos_, typename sample_args_impl<spos_, list_>::type>;
};
template <class... Ts>
using argument_traits = typename argument_traits_impl<Ts...>::type;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,67 @@
// Copyright 2019 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_ARRAY_WRAPPER_HPP
#define BOOST_HISTOGRAM_DETAIL_ARRAY_WRAPPER_HPP
#include <boost/core/nvp.hpp>
#include <boost/histogram/detail/span.hpp>
#include <boost/histogram/detail/static_if.hpp>
#include <boost/mp11/function.hpp>
#include <boost/mp11/utility.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class = decltype(&T::template save_array<int>)>
struct has_save_array_impl;
template <class T, class = decltype(&T::template load_array<int>)>
struct has_load_array_impl;
template <class T>
using has_array_optimization = mp11::mp_or<mp11::mp_valid<has_save_array_impl, T>,
mp11::mp_valid<has_load_array_impl, T>>;
template <class T>
struct array_wrapper {
using pointer = T*;
pointer ptr;
std::size_t size;
template <class Archive>
void serialize(Archive& ar, unsigned /* version */) {
static_if_c<(has_array_optimization<Archive>::value &&
std::is_trivially_copyable<T>::value)>(
[this](auto& ar) {
// cannot use and therefore bypass save_array / load_array interface, because
// it requires exact type boost::serialization::array_wrapper<T>
static_if_c<Archive::is_loading::value>(
[this](auto& ar) { ar.load_binary(this->ptr, sizeof(T) * this->size); },
[this](auto& ar) { ar.save_binary(this->ptr, sizeof(T) * this->size); },
ar);
},
[this](auto& ar) {
for (auto&& x : boost::histogram::detail::make_span(this->ptr, this->size))
ar& make_nvp("item", x);
},
ar);
}
};
template <class T>
auto make_array_wrapper(T* t, std::size_t s) {
return array_wrapper<T>{t, s};
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,439 @@
// 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 <array>
#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/nonmember_container_access.hpp>
#include <boost/histogram/detail/optional_index.hpp>
#include <boost/histogram/detail/priority.hpp>
#include <boost/histogram/detail/relaxed_tuple_size.hpp>
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/detail/sub_array.hpp>
#include <boost/histogram/detail/try_cast.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integer_sequence.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/mp11/utility.hpp>
#include <boost/throw_exception.hpp>
#include <cassert>
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <vector>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class Unary>
void for_each_axis_impl(dynamic_size, T& t, Unary& p) {
for (auto& a : t) axis::visit(p, a);
}
template <class N, class T, class Unary>
void for_each_axis_impl(N, T& t, Unary& p) {
mp11::tuple_for_each(t, p);
}
// also matches const T and const Unary
template <class T, class Unary>
void for_each_axis(T&& t, Unary&& p) {
for_each_axis_impl(relaxed_tuple_size(t), t, p);
}
// merge if a and b are discrete and growing
struct axis_merger {
template <class T, class U>
T operator()(const T& a, const U& u) {
const T* bp = ptr_cast<T>(&u);
if (!bp) BOOST_THROW_EXCEPTION(std::invalid_argument("axes not mergable"));
using O = axis::traits::get_options<T>;
constexpr bool discrete_and_growing =
axis::traits::is_continuous<T>::value == false && O::test(axis::option::growth);
return impl(mp11::mp_bool<discrete_and_growing>{}, a, *bp);
}
template <class T>
T impl(std::false_type, const T& a, const T& b) {
if (!relaxed_equal{}(a, b))
BOOST_THROW_EXCEPTION(std::invalid_argument("axes not mergable"));
return a;
}
template <class T>
T impl(std::true_type, const T& a, const T& b) {
if (relaxed_equal{}(axis::traits::metadata(a), axis::traits::metadata(b))) {
auto r = a;
if (axis::traits::is_ordered<T>::value) {
r.update(b.value(0));
r.update(b.value(b.size() - 1));
} else
for (auto&& v : b) r.update(v);
return r;
}
return impl(std::false_type{}, a, b);
}
};
// create empty dynamic axis which can store any axes types from the argument
template <class T>
auto make_empty_dynamic_axes(const T& axes) {
return make_default(axes);
}
template <class... Ts>
auto make_empty_dynamic_axes(const std::tuple<Ts...>&) {
using namespace ::boost::mp11;
using L = mp_unique<axis::variant<Ts...>>;
// return std::vector<axis::variant<Axis0, Axis1, ...>> or std::vector<Axis0>
return std::vector<mp_if_c<(mp_size<L>::value == 1), mp_first<L>, L>>{};
}
template <class T, class Functor, std::size_t... Is>
auto axes_transform_impl(const T& t, Functor&& f, mp11::index_sequence<Is...>) {
return std::make_tuple(f(Is, std::get<Is>(t))...);
}
// warning: sequential order of functor execution is platform-dependent!
template <class... Ts, class Functor>
auto axes_transform(const std::tuple<Ts...>& old_axes, Functor&& f) {
return axes_transform_impl(old_axes, std::forward<Functor>(f),
mp11::make_index_sequence<sizeof...(Ts)>{});
}
// changing axes type is not supported
template <class T, class Functor>
T axes_transform(const T& old_axes, Functor&& f) {
T axes = make_default(old_axes);
axes.reserve(old_axes.size());
for_each_axis(old_axes, [&](const auto& a) { axes.emplace_back(f(axes.size(), a)); });
return axes;
}
template <class... Ts, class Binary, std::size_t... Is>
std::tuple<Ts...> axes_transform_impl(const std::tuple<Ts...>& lhs,
const std::tuple<Ts...>& rhs, Binary&& bin,
mp11::index_sequence<Is...>) {
return std::make_tuple(bin(std::get<Is>(lhs), std::get<Is>(rhs))...);
}
template <class... Ts, class Binary>
std::tuple<Ts...> axes_transform(const std::tuple<Ts...>& lhs,
const std::tuple<Ts...>& rhs, Binary&& bin) {
return axes_transform_impl(lhs, rhs, bin, mp11::make_index_sequence<sizeof...(Ts)>{});
}
template <class T, class Binary>
T axes_transform(const T& lhs, const T& rhs, Binary&& bin) {
T ax = make_default(lhs);
ax.reserve(lhs.size());
using std::begin;
auto ir = begin(rhs);
for (auto&& li : lhs) {
axis::visit(
[&](const auto& li) {
axis::visit([&](const auto& ri) { ax.emplace_back(bin(li, ri)); }, *ir);
},
li);
++ir;
}
return ax;
}
template <class T>
unsigned axes_rank(const T& axes) {
using std::begin;
using std::end;
return static_cast<unsigned>(std::distance(begin(axes), end(axes)));
}
template <class... Ts>
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);
}
template <unsigned N, class... Ts>
decltype(auto) axis_get(const std::tuple<Ts...>& axes) {
return std::get<N>(axes);
}
template <unsigned N, class T>
decltype(auto) axis_get(T& axes) {
return axes[N];
}
template <unsigned N, class T>
decltype(auto) axis_get(const T& axes) {
return axes[N];
}
template <class... Ts>
auto axis_get(std::tuple<Ts...>& axes, const unsigned i) {
constexpr auto S = sizeof...(Ts);
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>
auto axis_get(const std::tuple<Ts...>& axes, const unsigned i) {
constexpr auto S = sizeof...(Ts);
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, const unsigned i) {
return axes[i];
}
template <class T>
decltype(auto) axis_get(const T& axes, const unsigned i) {
return axes[i];
}
template <class T, class U, std::size_t... Is>
bool axes_equal_impl(const T& t, const U& u, mp11::index_sequence<Is...>) noexcept {
bool result = true;
// operator folding emulation
(void)std::initializer_list<bool>{
(result &= relaxed_equal{}(std::get<Is>(t), std::get<Is>(u)))...};
return result;
}
template <class... Ts, class... Us>
bool axes_equal_impl(const std::tuple<Ts...>& t, const std::tuple<Us...>& u) noexcept {
return axes_equal_impl(
t, u, mp11::make_index_sequence<std::min(sizeof...(Ts), sizeof...(Us))>{});
}
template <class... Ts, class U>
bool axes_equal_impl(const std::tuple<Ts...>& t, const U& u) noexcept {
using std::begin;
auto iu = begin(u);
bool result = true;
mp11::tuple_for_each(t, [&](const auto& ti) {
axis::visit([&](const auto& ui) { result &= relaxed_equal{}(ti, ui); }, *iu);
++iu;
});
return result;
}
template <class T, class... Us>
bool axes_equal_impl(const T& t, const std::tuple<Us...>& u) noexcept {
return axes_equal_impl(u, t);
}
template <class T, class U>
bool axes_equal_impl(const T& t, const U& u) noexcept {
using std::begin;
auto iu = begin(u);
bool result = true;
for (auto&& ti : t) {
axis::visit(
[&](const auto& ti) {
axis::visit([&](const auto& ui) { result &= relaxed_equal{}(ti, ui); }, *iu);
},
ti);
++iu;
}
return result;
}
template <class T, class U>
bool axes_equal(const T& t, const U& u) noexcept {
return axes_rank(t) == axes_rank(u) && axes_equal_impl(t, u);
}
// enable_if_t needed by msvc :(
template <class... Ts, class... Us>
std::enable_if_t<!(std::is_same<std::tuple<Ts...>, std::tuple<Us...>>::value)>
axes_assign(std::tuple<Ts...>&, const std::tuple<Us...>&) {
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot assign axes, types do not match"));
}
template <class... Ts>
void axes_assign(std::tuple<Ts...>& t, const std::tuple<Ts...>& u) {
t = u;
}
template <class... Ts, class U>
void axes_assign(std::tuple<Ts...>& t, const U& u) {
if (sizeof...(Ts) == detail::size(u)) {
using std::begin;
auto iu = begin(u);
mp11::tuple_for_each(t, [&](auto& ti) {
using T = std::decay_t<decltype(ti)>;
ti = axis::get<T>(*iu);
++iu;
});
return;
}
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot assign axes, sizes do not match"));
}
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));
using std::begin;
auto it = begin(t);
mp11::tuple_for_each(u, [&](const auto& ui) { *it++ = ui; });
}
template <class T, class U>
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);
}
// 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) {
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 <class T>
std::size_t offset(const T& axes) {
std::size_t n = 0;
auto stride = static_cast<std::size_t>(1);
for_each_axis(axes, [&](const auto& a) {
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;
}
// make default-constructed buffer (no initialization for POD types)
template <class T, class A>
auto make_stack_buffer(const A& a) {
return sub_array<T, buffer_size<A>::value>(axes_rank(a));
}
// make buffer with elements initialized to v
template <class T, class A>
auto make_stack_buffer(const A& a, const T& t) {
return sub_array<T, buffer_size<A>::value>(axes_rank(a), t);
}
template <class T>
using has_underflow =
decltype(axis::traits::get_options<T>::test(axis::option::underflow));
template <class T>
using is_growing = decltype(axis::traits::get_options<T>::test(axis::option::growth));
template <class T>
using is_not_inclusive = mp11::mp_not<axis::traits::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
#endif

View File

@@ -0,0 +1,47 @@
// 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_COMMON_TYPE_HPP
#define BOOST_HISTOGRAM_DETAIL_COMMON_TYPE_HPP
#include <boost/histogram/detail/detect.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/utility.hpp>
#include <tuple>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
// clang-format off
template <class T, class U>
using common_axes = mp11::mp_cond<
is_tuple<T>, T,
is_tuple<U>, U,
is_sequence_of_axis<T>, T,
is_sequence_of_axis<U>, U,
std::true_type, T
>;
// clang-format on
// Non-PODs rank highest, then floats, than integers; types with more capacity are higher
template <class Storage>
constexpr std::size_t type_rank() {
using T = typename Storage::value_type;
return !std::is_arithmetic<T>::value * 10000 + std::is_floating_point<T>::value * 100 +
10 * sizeof(T) + 2 * is_array_like<Storage>::value +
is_vector_like<Storage>::value;
;
}
template <class T, class U>
using common_storage = mp11::mp_if_c<(type_rank<T>() >= type_rank<U>()), T, U>;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,24 @@
// Copyright 2018-2019 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_CONVERT_INTEGER_HPP
#define BOOST_HISTOGRAM_DETAIL_CONVERT_INTEGER_HPP
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class U>
using convert_integer =
std::conditional_t<std::is_integral<std::decay_t<T>>::value, U, T>;
}
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,77 @@
// Copyright 2019 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_COUNTING_STREAMBUF_HPP
#define BOOST_HISTOGRAM_DETAIL_COUNTING_STREAMBUF_HPP
#include <boost/core/exchange.hpp>
#include <ostream>
#include <streambuf>
namespace boost {
namespace histogram {
namespace detail {
// detect how many characters will be printed by formatted output
template <class CharT, class Traits = std::char_traits<CharT>>
struct counting_streambuf : std::basic_streambuf<CharT, Traits> {
using base_t = std::basic_streambuf<CharT, Traits>;
using typename base_t::char_type;
using typename base_t::int_type;
std::streamsize* p_count;
counting_streambuf(std::streamsize& c) : p_count(&c) {}
std::streamsize xsputn(const char_type* /* s */, std::streamsize n) override {
*p_count += n;
return n;
}
int_type overflow(int_type ch) override {
++*p_count;
return ch;
}
};
template <class C, class T>
struct count_guard {
using bos = std::basic_ostream<C, T>;
using bsb = std::basic_streambuf<C, T>;
counting_streambuf<C, T> csb;
bos* p_os;
bsb* p_rdbuf;
count_guard(bos& os, std::streamsize& s) : csb(s), p_os(&os), p_rdbuf(os.rdbuf(&csb)) {}
count_guard(count_guard&& o)
: csb(o.csb), p_os(boost::exchange(o.p_os, nullptr)), p_rdbuf(o.p_rdbuf) {}
count_guard& operator=(count_guard&& o) {
if (this != &o) {
csb = std::move(o.csb);
p_os = boost::exchange(o.p_os, nullptr);
p_rdbuf = o.p_rdbuf;
}
return *this;
}
~count_guard() {
if (p_os) p_os->rdbuf(p_rdbuf);
}
};
template <class C, class T>
count_guard<C, T> make_count_guard(std::basic_ostream<C, T>& os, std::streamsize& s) {
return {os, s};
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,207 @@
// Copyright 2015-2019 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_DETECT_HPP
#define BOOST_HISTOGRAM_DETAIL_DETECT_HPP
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/function.hpp> // mp_and, mp_or
#include <boost/mp11/integral.hpp> // mp_not
#include <boost/mp11/list.hpp> // mp_first
#include <iterator>
#include <tuple>
#include <type_traits>
// forward declaration
namespace boost {
namespace variant2 {
template <class...>
class variant;
} // namespace variant2
} // namespace boost
namespace boost {
namespace histogram {
namespace detail {
#define BOOST_HISTOGRAM_DETAIL_DETECT(name, cond) \
template <class U> \
struct name##_impl { \
typedef char yes[1]; \
typedef char no[2]; \
template <class T> \
static yes& test(T& t, decltype(cond, 0)); \
template <class T> \
static no& test(T&, float); \
using type = \
std::integral_constant<bool, (sizeof(test(std::declval<U&>(), 0)) == 1)>; \
}; \
template <class T> \
using name = typename name##_impl<T>::type
#define BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(name, cond) \
template <class V, class W> \
struct name##_impl { \
typedef char yes[1]; \
typedef char no[2]; \
template <class T, class U> \
static yes& test(decltype(cond, 0)); \
template <class, class> \
static no& test(float); \
using type = std::integral_constant<bool, (sizeof(test<V, W>(0)) == 1)>; \
}; \
template <class T, class U = T> \
using name = typename name##_impl<T, U>::type
// reset has overloads, trying to get pmf in this case always fails
BOOST_HISTOGRAM_DETAIL_DETECT(has_method_reset, t.reset(0));
BOOST_HISTOGRAM_DETAIL_DETECT(is_indexable, t[0]);
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(
is_transform,
(std::declval<T&>().inverse(std::declval<T&>().forward(std::declval<U>()))));
BOOST_HISTOGRAM_DETAIL_DETECT(is_indexable_container,
(t[0], t.size(), std::begin(t), std::end(t)));
BOOST_HISTOGRAM_DETAIL_DETECT(is_vector_like,
(t[0], t.size(), t.resize(0), std::begin(t), std::end(t)));
BOOST_HISTOGRAM_DETAIL_DETECT(is_array_like, (t[0], t.size(), std::tuple_size<T>::value,
std::begin(t), std::end(t)));
BOOST_HISTOGRAM_DETAIL_DETECT(is_map_like, ((typename T::key_type*)nullptr,
(typename T::mapped_type*)nullptr,
std::begin(t), std::end(t)));
// ok: is_axis is false for axis::variant, because T::index is templated
BOOST_HISTOGRAM_DETAIL_DETECT(is_axis, (t.size(), &T::index));
BOOST_HISTOGRAM_DETAIL_DETECT(is_iterable, (std::begin(t), std::end(t)));
BOOST_HISTOGRAM_DETAIL_DETECT(is_iterator,
(typename std::iterator_traits<T>::iterator_category{}));
BOOST_HISTOGRAM_DETAIL_DETECT(is_streamable, (std::declval<std::ostream&>() << t));
BOOST_HISTOGRAM_DETAIL_DETECT(has_operator_preincrement, ++t);
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_equal, (std::declval<const T&>() ==
std::declval<const U&>()));
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_radd,
(std::declval<T&>() += std::declval<U>()));
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_rsub,
(std::declval<T&>() -= std::declval<U>()));
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_rmul,
(std::declval<T&>() *= std::declval<U>()));
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(has_operator_rdiv,
(std::declval<T&>() /= std::declval<U>()));
BOOST_HISTOGRAM_DETAIL_DETECT_BINARY(
has_method_eq, (std::declval<const T&>().operator==(std::declval<const U&>())));
BOOST_HISTOGRAM_DETAIL_DETECT(has_threading_support, (T::has_threading_support));
template <class T>
using is_storage = mp11::mp_and<is_indexable_container<T>, has_method_reset<T>,
has_threading_support<T>>;
template <class T>
using is_adaptible =
mp11::mp_and<mp11::mp_not<is_storage<T>>,
mp11::mp_or<is_vector_like<T>, is_array_like<T>, is_map_like<T>>>;
template <class T>
struct is_tuple_impl : std::false_type {};
template <class... Ts>
struct is_tuple_impl<std::tuple<Ts...>> : std::true_type {};
template <class T>
using is_tuple = typename is_tuple_impl<T>::type;
template <class T>
struct is_variant_impl : std::false_type {};
template <class... Ts>
struct is_variant_impl<boost::variant2::variant<Ts...>> : std::true_type {};
template <class T>
using is_variant = typename is_variant_impl<T>::type;
template <class T>
struct is_axis_variant_impl : std::false_type {};
template <class... Ts>
struct is_axis_variant_impl<axis::variant<Ts...>> : std::true_type {};
template <class T>
using is_axis_variant = typename is_axis_variant_impl<T>::type;
template <class T>
using is_any_axis = mp11::mp_or<is_axis<T>, is_axis_variant<T>>;
template <class T>
using is_sequence_of_axis = mp11::mp_and<is_iterable<T>, is_axis<mp11::mp_first<T>>>;
template <class T>
using is_sequence_of_axis_variant =
mp11::mp_and<is_iterable<T>, is_axis_variant<mp11::mp_first<T>>>;
template <class T>
using is_sequence_of_any_axis =
mp11::mp_and<is_iterable<T>, is_any_axis<mp11::mp_first<T>>>;
// poor-mans concept checks
template <class T, class = std::enable_if_t<is_storage<std::decay_t<T>>::value>>
struct requires_storage {};
template <class T, class _ = std::decay_t<T>,
class = std::enable_if_t<(is_storage<_>::value || is_adaptible<_>::value)>>
struct requires_storage_or_adaptible {};
template <class T, class = std::enable_if_t<is_iterator<std::decay_t<T>>::value>>
struct requires_iterator {};
template <class T, class = std::enable_if_t<
is_iterable<std::remove_cv_t<std::remove_reference_t<T>>>::value>>
struct requires_iterable {};
template <class T, class = std::enable_if_t<is_axis<std::decay_t<T>>::value>>
struct requires_axis {};
template <class T, class = std::enable_if_t<is_any_axis<std::decay_t<T>>::value>>
struct requires_any_axis {};
template <class T, class = std::enable_if_t<is_sequence_of_axis<std::decay_t<T>>::value>>
struct requires_sequence_of_axis {};
template <class T,
class = std::enable_if_t<is_sequence_of_axis_variant<std::decay_t<T>>::value>>
struct requires_sequence_of_axis_variant {};
template <class T,
class = std::enable_if_t<is_sequence_of_any_axis<std::decay_t<T>>::value>>
struct requires_sequence_of_any_axis {};
template <class T,
class = std::enable_if_t<is_any_axis<mp11::mp_first<std::decay_t<T>>>::value>>
struct requires_axes {};
template <class T, class U,
class = std::enable_if_t<is_transform<std::decay_t<T>, U>::value>>
struct requires_transform {};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,348 @@
// 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_FILL_HPP
#define BOOST_HISTOGRAM_DETAIL_FILL_HPP
#include <algorithm>
#include <boost/config/workaround.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/argument_traits.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/linearize.hpp>
#include <boost/histogram/detail/make_default.hpp>
#include <boost/histogram/detail/optional_index.hpp>
#include <boost/histogram/detail/priority.hpp>
#include <boost/histogram/detail/tuple_slice.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/mp11/utility.hpp>
#include <cassert>
#include <mutex>
#include <tuple>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class U>
struct sample_args_passed_vs_expected;
template <class... Passed, class... Expected>
struct sample_args_passed_vs_expected<std::tuple<Passed...>, std::tuple<Expected...>> {
static_assert(!(sizeof...(Expected) > 0 && sizeof...(Passed) == 0),
"error: accumulator requires samples, but sample argument is missing");
static_assert(
!(sizeof...(Passed) > 0 && sizeof...(Expected) == 0),
"error: accumulator does not accept samples, but sample argument is passed");
static_assert(sizeof...(Passed) == sizeof...(Expected),
"error: numbers of passed and expected sample arguments differ");
static_assert(
std::is_convertible<std::tuple<Passed...>, std::tuple<Expected...>>::value,
"error: sample argument(s) not convertible to accumulator argument(s)");
};
template <class A>
struct storage_grower {
const A& axes_;
struct {
axis::index_type idx, old_extent;
std::size_t new_stride;
} data_[buffer_size<A>::value];
std::size_t new_size_;
storage_grower(const A& axes) noexcept : axes_(axes) {}
void from_shifts(const axis::index_type* shifts) noexcept {
auto dit = data_;
std::size_t s = 1;
for_each_axis(axes_, [&](const auto& a) {
const auto n = axis::traits::extent(a);
*dit++ = {0, n - std::abs(*shifts++), s};
s *= n;
});
new_size_ = s;
}
// must be extents before any shifts were applied
void from_extents(const axis::index_type* old_extents) noexcept {
auto dit = data_;
std::size_t s = 1;
for_each_axis(axes_, [&](const auto& a) {
const auto n = axis::traits::extent(a);
*dit++ = {0, *old_extents++, s};
s *= n;
});
new_size_ = s;
}
template <class S>
void apply(S& storage, const axis::index_type* shifts) {
auto new_storage = make_default(storage);
new_storage.reset(new_size_);
const auto dlast = data_ + axes_rank(axes_) - 1;
for (auto&& x : storage) {
auto ns = new_storage.begin();
auto sit = shifts;
auto dit = data_;
for_each_axis(axes_, [&](const auto& a) {
using opt = axis::traits::get_options<std::decay_t<decltype(a)>>;
if (opt::test(axis::option::underflow)) {
if (dit->idx == 0) {
// axis has underflow and we are in the underflow bin:
// keep storage pointer unchanged
++dit;
++sit;
return;
}
}
if (opt::test(axis::option::overflow)) {
if (dit->idx == dit->old_extent - 1) {
// axis has overflow and we are in the overflow bin:
// move storage pointer to corresponding overflow bin position
ns += (axis::traits::extent(a) - 1) * dit->new_stride;
++dit;
++sit;
return;
}
}
// we are in a normal bin:
// move storage pointer to index position; apply positive shifts if any
ns += (dit->idx + (*sit >= 0 ? *sit : 0)) * dit->new_stride;
++dit;
++sit;
});
// assign old value to new location
*ns = x;
// advance multi-dimensional index
dit = data_;
++dit->idx;
while (dit != dlast && dit->idx == dit->old_extent) {
dit->idx = 0;
++(++dit)->idx;
}
}
storage = std::move(new_storage);
}
};
template <class T, class... Us>
auto fill_storage_element_impl(priority<2>, T&& t, const Us&... args) noexcept
-> decltype(t(args...), void()) {
t(args...);
}
template <class T, class U>
auto fill_storage_element_impl(priority<1>, T&& t, const weight_type<U>& w) noexcept
-> decltype(t += w, void()) {
t += w;
}
// fallback for arithmetic types and accumulators that do not handle the weight
template <class T, class U>
auto fill_storage_element_impl(priority<0>, T&& t, const weight_type<U>& w) noexcept
-> decltype(t += w.value, void()) {
t += w.value;
}
template <class T>
auto fill_storage_element_impl(priority<1>, T&& t) noexcept -> decltype(++t, void()) {
++t;
}
template <class T, class... Us>
void fill_storage_element(T&& t, const Us&... args) noexcept {
fill_storage_element_impl(priority<2>{}, std::forward<T>(t), args...);
}
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
template <class IW, class IS, class T, class U>
void fill_storage_2(IW, IS, T&& t, U&& u) noexcept {
mp11::tuple_apply(
[&](const auto&... args) {
fill_storage_element(std::forward<T>(t), std::get<IW::value>(u), args...);
},
std::get<IS::value>(u).value);
}
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
template <class IS, class T, class U>
void fill_storage_2(mp11::mp_int<-1>, IS, T&& t, const U& u) noexcept {
mp11::tuple_apply(
[&](const auto&... args) { fill_storage_element(std::forward<T>(t), args...); },
std::get<IS::value>(u).value);
}
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
template <class IW, class T, class U>
void fill_storage_2(IW, mp11::mp_int<-1>, T&& t, const U& u) noexcept {
fill_storage_element(std::forward<T>(t), std::get<IW::value>(u));
}
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
template <class T, class U>
void fill_storage_2(mp11::mp_int<-1>, mp11::mp_int<-1>, T&& t, const U&) noexcept {
fill_storage_element(std::forward<T>(t));
}
template <class IW, class IS, class Storage, class Index, class Args>
auto fill_storage(IW, IS, Storage& s, const Index idx, const Args& a) noexcept {
if (is_valid(idx)) {
assert(idx < s.size());
fill_storage_2(IW{}, IS{}, s[idx], a);
return s.begin() + idx;
}
return s.end();
}
template <int S, int N>
struct linearize_args {
template <class Index, class A, class Args>
static void impl(mp11::mp_int<N>, Index&, const std::size_t, A&, const Args&) {}
template <int I, class Index, class A, class Args>
static void impl(mp11::mp_int<I>, Index& o, const std::size_t s, A& ax,
const Args& args) {
const auto e = linearize(o, s, axis_get<I>(ax), std::get<(S + I)>(args));
impl(mp11::mp_int<(I + 1)>{}, o, s * e, ax, args);
}
template <class Index, class A, class Args>
static void apply(Index& o, A& ax, const Args& args) {
impl(mp11::mp_int<0>{}, o, 1, ax, args);
}
};
template <int S>
struct linearize_args<S, 1> {
template <class Index, class A, class Args>
static void apply(Index& o, A& ax, const Args& args) {
linearize(o, 1, axis_get<0>(ax), std::get<S>(args));
}
};
template <class A>
constexpr unsigned min(const unsigned n) noexcept {
constexpr unsigned a = buffer_size<A>::value;
return a < n ? a : n;
}
// not growing
template <class ArgTraits, class Storage, class Axes, class Args>
auto fill_2(ArgTraits, mp11::mp_false, const std::size_t offset, Storage& st,
const Axes& axes, const Args& args) {
mp11::mp_if<has_non_inclusive_axis<Axes>, optional_index, std::size_t> idx{offset};
linearize_args<ArgTraits::start::value, min<Axes>(ArgTraits::nargs::value)>::apply(
idx, axes, args);
return fill_storage(typename ArgTraits::wpos{}, typename ArgTraits::spos{}, st, idx,
args);
}
// at least one axis is growing
template <class ArgTraits, class Storage, class Axes, class Args>
auto fill_2(ArgTraits, mp11::mp_true, const std::size_t, Storage& st, Axes& axes,
const Args& args) {
std::array<axis::index_type, ArgTraits::nargs::value> shifts;
// offset must be zero for linearize_growth
mp11::mp_if<has_non_inclusive_axis<Axes>, optional_index, std::size_t> idx{0};
std::size_t stride = 1;
bool update_needed = false;
mp11::mp_for_each<mp11::mp_iota_c<min<Axes>(ArgTraits::nargs::value)>>([&](auto i) {
auto& ax = axis_get<i>(axes);
const auto extent = linearize_growth(idx, shifts[i], stride, ax,
std::get<(ArgTraits::start::value + i)>(args));
update_needed |= shifts[i] != 0;
stride *= extent;
});
if (update_needed) {
storage_grower<Axes> g(axes);
g.from_shifts(shifts.data());
g.apply(st, shifts.data());
}
return fill_storage(typename ArgTraits::wpos{}, typename ArgTraits::spos{}, st, idx,
args);
}
// pack original args tuple into another tuple (which is unpacked later)
template <int Start, int Size, class IW, class IS, class Args>
decltype(auto) pack_args(IW, IS, const Args& args) noexcept {
return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IW::value>(args),
std::get<IS::value>(args));
}
template <int Start, int Size, class IW, class Args>
decltype(auto) pack_args(IW, mp11::mp_int<-1>, const Args& args) noexcept {
return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IW::value>(args));
}
template <int Start, int Size, class IS, class Args>
decltype(auto) pack_args(mp11::mp_int<-1>, IS, const Args& args) noexcept {
return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IS::value>(args));
}
template <int Start, int Size, class Args>
decltype(auto) pack_args(mp11::mp_int<-1>, mp11::mp_int<-1>, const Args& args) noexcept {
return std::make_tuple(args);
}
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
#pragma warning(disable : 4702) // fixing warning would reduce code readability a lot
#endif
template <class ArgTraits, class S, class A, class Args>
auto fill(std::true_type, ArgTraits, const std::size_t offset, S& storage, A& axes,
const Args& args) -> typename S::iterator {
using growing = has_growing_axis<A>;
// Sometimes we need to pack the tuple into another tuple:
// - histogram contains one axis which accepts tuple
// - user passes tuple to fill(...)
// Tuple is normally unpacked and arguments are processed, this causes pos::nargs > 1.
// Now we pack tuple into another tuple so that original tuple is send to axis.
// Notes:
// - has nice side-effect of making histogram::operator(1, 2) work as well
// - cannot detect call signature of axis at compile-time in all configurations
// (axis::variant provides generic call interface and hides concrete
// interface), so we throw at runtime if incompatible argument is passed (e.g.
// 3d tuple)
if (axes_rank(axes) == ArgTraits::nargs::value)
return fill_2(ArgTraits{}, growing{}, offset, storage, axes, args);
else if (axes_rank(axes) == 1 &&
axis::traits::rank(axis_get<0>(axes)) == ArgTraits::nargs::value)
return fill_2(
argument_traits_holder<
1, 0, (ArgTraits::wpos::value >= 0 ? 1 : -1),
(ArgTraits::spos::value >= 0 ? (ArgTraits::wpos::value >= 0 ? 2 : 1) : -1),
typename ArgTraits::sargs>{},
growing{}, offset, storage, axes,
pack_args<ArgTraits::start::value, ArgTraits::nargs::value>(
typename ArgTraits::wpos{}, typename ArgTraits::spos{}, args));
return BOOST_THROW_EXCEPTION(
std::invalid_argument("number of arguments != histogram rank")),
storage.end();
}
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
#pragma warning(default : 4702)
#endif
// empty implementation for bad arguments to stop compiler from showing internals
template <class ArgTraits, class S, class A, class Args>
auto fill(std::false_type, ArgTraits, const std::size_t, S& storage, A&, const Args&) ->
typename S::iterator {
return storage.end();
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,339 @@
// Copyright 2019 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_FILL_N_HPP
#define BOOST_HISTOGRAM_DETAIL_FILL_N_HPP
#include <algorithm>
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/histogram/detail/detect.hpp>
#include <boost/histogram/detail/fill.hpp>
#include <boost/histogram/detail/linearize.hpp>
#include <boost/histogram/detail/nonmember_container_access.hpp>
#include <boost/histogram/detail/optional_index.hpp>
#include <boost/histogram/detail/span.hpp>
#include <boost/histogram/detail/static_if.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/bind.hpp>
#include <boost/mp11/utility.hpp>
#include <boost/throw_exception.hpp>
#include <boost/variant2/variant.hpp>
#include <cassert>
#include <initializer_list>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace detail {
namespace dtl = boost::histogram::detail;
template <class Axes, class T>
using is_convertible_to_any_value_type =
mp11::mp_any_of_q<value_types<Axes>, mp11::mp_bind_front<std::is_convertible, T>>;
template <class T>
auto to_ptr_size(const T& x) {
return static_if<std::is_scalar<T>>(
[](const auto& x) { return std::make_pair(&x, static_cast<std::size_t>(0)); },
[](const auto& x) { return std::make_pair(dtl::data(x), dtl::size(x)); }, x);
}
template <class F, class V>
decltype(auto) maybe_visit(F&& f, V&& v) {
return static_if<is_variant<std::decay_t<V>>>(
[](auto&& f, auto&& v) {
return variant2::visit(std::forward<F>(f), std::forward<V>(v));
},
[](auto&& f, auto&& v) { return std::forward<F>(f)(std::forward<V>(v)); },
std::forward<F>(f), std::forward<V>(v));
}
template <class Index, class Axis, class IsGrowing>
struct index_visitor {
using index_type = Index;
using pointer = index_type*;
using value_type = axis::traits::value_type<Axis>;
using Opt = axis::traits::get_options<Axis>;
Axis& axis_;
const std::size_t stride_, start_, size_; // start and size of value collection
const pointer begin_;
axis::index_type* shift_;
index_visitor(Axis& a, std::size_t& str, const std::size_t& sta, const std::size_t& si,
const pointer it, axis::index_type* shift)
: axis_(a), stride_(str), start_(sta), size_(si), begin_(it), shift_(shift) {}
template <class T>
void call_2(std::true_type, pointer it, const T& x) const {
// must use this code for all axes if one of them is growing
axis::index_type shift;
linearize_growth(*it, shift, stride_, axis_,
try_cast<value_type, std::invalid_argument>(x));
if (shift > 0) { // shift previous indices, because axis zero-point has changed
while (it != begin_) *--it += static_cast<std::size_t>(shift) * stride_;
*shift_ += shift;
}
}
template <class T>
void call_2(std::false_type, pointer it, const T& x) const {
// no axis is growing
linearize(*it, stride_, axis_, try_cast<value_type, std::invalid_argument>(x));
}
template <class T>
void call_1(std::false_type, const T& iterable) const {
// T is iterable; fill N values
const auto* tp = dtl::data(iterable) + start_;
for (auto it = begin_; it != begin_ + size_; ++it) call_2(IsGrowing{}, it, *tp++);
}
template <class T>
void call_1(std::true_type, const T& value) const {
// T is compatible value; fill single value N times
index_type idx{*begin_};
call_2(IsGrowing{}, &idx, value);
if (is_valid(idx)) {
const auto delta =
static_cast<std::intptr_t>(idx) - static_cast<std::intptr_t>(*begin_);
for (auto&& i : make_span(begin_, size_)) i += delta;
} else
std::fill(begin_, begin_ + size_, invalid_index);
}
template <class T>
void operator()(const T& iterable_or_value) const {
call_1(mp11::mp_bool<(std::is_convertible<T, value_type>::value ||
!is_iterable<T>::value)>{},
iterable_or_value);
}
};
template <class Index, class S, class Axes, class T>
void fill_n_indices(Index* indices, const std::size_t start, const std::size_t size,
const std::size_t offset, S& storage, Axes& axes, const T* viter) {
axis::index_type extents[buffer_size<Axes>::value];
axis::index_type shifts[buffer_size<Axes>::value];
for_each_axis(axes, [eit = extents, sit = shifts](const auto& a) mutable {
*sit++ = 0;
*eit++ = axis::traits::extent(a);
}); // LCOV_EXCL_LINE: gcc-8 is missing this line for no reason
// offset must be zero for growing axes
using IsGrowing = has_growing_axis<Axes>;
std::fill(indices, indices + size, IsGrowing::value ? 0 : offset);
for_each_axis(axes, [&, stride = static_cast<std::size_t>(1),
pshift = shifts](auto& axis) mutable {
using Axis = std::decay_t<decltype(axis)>;
maybe_visit(
index_visitor<Index, Axis, IsGrowing>{axis, stride, start, size, indices, pshift},
*viter++);
stride *= static_cast<std::size_t>(axis::traits::extent(axis));
++pshift;
});
bool update_needed = false;
for_each_axis(axes, [&update_needed, eit = extents](const auto& a) mutable {
update_needed |= *eit++ != axis::traits::extent(a);
});
if (update_needed) {
storage_grower<Axes> g(axes);
g.from_extents(extents);
g.apply(storage, shifts);
}
}
template <class S, class Index, class... Ts>
void fill_n_storage(S& s, const Index idx, Ts&&... p) noexcept {
if (is_valid(idx)) {
assert(idx < s.size());
fill_storage_element(s[idx], *p.first...);
}
// operator folding emulation
(void)std::initializer_list<int>{(p.second ? (++p.first, 0) : 0)...};
}
template <class S, class Index, class T, class... Ts>
void fill_n_storage(S& s, const Index idx, weight_type<T>&& w, Ts&&... ps) noexcept {
if (is_valid(idx)) {
assert(idx < s.size());
fill_storage_element(s[idx], weight(*w.value.first), *ps.first...);
}
if (w.value.second) ++w.value.first;
// operator folding emulation
(void)std::initializer_list<int>{(ps.second ? (++ps.first, 0) : 0)...};
}
// general Nd treatment
template <class Index, class S, class A, class T, class... Ts>
void fill_n_nd(const std::size_t offset, S& storage, A& axes, const std::size_t vsize,
const T* values, Ts&&... ts) {
constexpr std::size_t buffer_size = 1ul << 14;
Index indices[buffer_size];
/*
Parallelization options.
A) Run the whole fill2 method in parallel, each thread fills its own buffer of
indices, synchronization (atomics) are needed to synchronize the incrementing of
the storage cells. This leads to a lot of congestion for small histograms.
B) Run only fill_n_indices in parallel, subsections of the indices buffer
can be filled by different threads. The final loop that fills the storage runs
in the main thread, this requires no synchronization for the storage, cells do
not need to support atomic operations.
C) Like B), then sort the indices in the main thread and fill the
storage in parallel, where each thread uses a disjunct set of indices. This
should create less congestion and requires no synchronization for the storage.
Note on C): Let's say we have an axis with 5 bins (with *flow to simplify).
Then after filling 10 values, converting to indices and sorting, the index
buffer may look like this: 0 0 0 1 2 2 2 4 4 5. Let's use two threads to fill
the storage. Still in the main thread, we compute an iterator to the middle of
the index buffer and move it to the right until the pointee changes. Now we have
two ranges which contain disjunct sets of indices. We pass these ranges to the
threads which then fill the storage. Since the threads by construction do not
compete to increment the same cell, no further synchronization is required.
In all cases, growing axes cannot be parallelized.
*/
for (std::size_t start = 0; start < vsize; start += buffer_size) {
const std::size_t n = std::min(buffer_size, vsize - start);
// fill buffer of indices...
fill_n_indices(indices, start, n, offset, storage, axes, values);
// ...and fill corresponding storage cells
for (auto&& idx : make_span(indices, n))
fill_n_storage(storage, idx, std::forward<Ts>(ts)...);
}
}
template <class S, class... As, class T, class... Us>
void fill_n_1(const std::size_t offset, S& storage, std::tuple<As...>& axes,
const std::size_t vsize, const T* values, Us&&... us) {
using index_type =
mp11::mp_if<has_non_inclusive_axis<std::tuple<As...>>, optional_index, std::size_t>;
fill_n_nd<index_type>(offset, storage, axes, vsize, values, std::forward<Us>(us)...);
}
template <class S, class A, class T, class... Us>
void fill_n_1(const std::size_t offset, S& storage, A& axes, const std::size_t vsize,
const T* values, Us&&... us) {
bool all_inclusive = true;
for_each_axis(axes,
[&](const auto& ax) { all_inclusive &= axis::traits::inclusive(ax); });
if (axes_rank(axes) == 1) {
axis::visit(
[&](auto& ax) {
std::tuple<decltype(ax)> axes{ax};
fill_n_1(offset, storage, axes, vsize, values, std::forward<Us>(us)...);
},
axes[0]);
} else {
if (all_inclusive)
fill_n_nd<std::size_t>(offset, storage, axes, vsize, values,
std::forward<Us>(us)...);
else
fill_n_nd<optional_index>(offset, storage, axes, vsize, values,
std::forward<Us>(us)...);
}
}
template <class A, class T, std::size_t N>
std::size_t get_total_size(const A& axes, const dtl::span<const T, N>& values) {
// supported cases (T = value type; CT = containter of T; V<T, CT, ...> = variant):
// - span<CT, N>: for any histogram, N == rank
// - span<V<T, CT>, N>: for any histogram, N == rank
assert(axes_rank(axes) == values.size());
constexpr auto unset = static_cast<std::size_t>(-1);
std::size_t size = unset;
for_each_axis(axes, [&size, vit = values.begin()](const auto& ax) mutable {
using AV = axis::traits::value_type<std::decay_t<decltype(ax)>>;
maybe_visit(
[&size](const auto& v) {
// v is either convertible to value or a sequence of values
using V = std::remove_const_t<std::remove_reference_t<decltype(v)>>;
static_if_c<(std::is_convertible<decltype(v), AV>::value ||
!is_iterable<V>::value)>(
[](const auto&) {},
[&size](const auto& v) {
const auto n = dtl::size(v);
// must repeat this here for msvc :(
constexpr auto unset = static_cast<std::size_t>(-1);
if (size == unset)
size = dtl::size(v);
else if (size != n)
BOOST_THROW_EXCEPTION(
std::invalid_argument("spans must have compatible lengths"));
},
v);
},
*vit++);
});
// if all arguments are not iterables, return size of 1
return size == unset ? 1 : size;
}
inline void fill_n_check_extra_args(std::size_t) noexcept {}
template <class T, class... Ts>
void fill_n_check_extra_args(std::size_t size, T&& x, Ts&&... ts) {
// sequences must have same lengths, but sequences of length 0 are broadcast
if (x.second != 0 && x.second != size)
BOOST_THROW_EXCEPTION(std::invalid_argument("spans must have compatible lengths"));
fill_n_check_extra_args(size, std::forward<Ts>(ts)...);
}
template <class T, class... Ts>
void fill_n_check_extra_args(std::size_t size, weight_type<T>&& w, Ts&&... ts) {
fill_n_check_extra_args(size, w.value, std::forward<Ts>(ts)...);
}
template <class S, class A, class T, std::size_t N, class... Us>
void fill_n(std::true_type, const std::size_t offset, S& storage, A& axes,
const dtl::span<const T, N> values, Us&&... us) {
// supported cases (T = value type; CT = containter of T; V<T, CT, ...> = variant):
// - span<T, N>: only valid for 1D histogram, N > 1 allowed
// - span<CT, N>: for any histogram, N == rank
// - span<V<T, CT>, N>: for any histogram, N == rank
static_if<is_convertible_to_any_value_type<A, T>>(
[&](const auto& values, auto&&... us) {
// T matches one of the axis value types, must be 1D special case
if (axes_rank(axes) != 1)
BOOST_THROW_EXCEPTION(
std::invalid_argument("number of arguments must match histogram rank"));
fill_n_check_extra_args(values.size(), std::forward<Us>(us)...);
fill_n_1(offset, storage, axes, values.size(), &values, std::forward<Us>(us)...);
},
[&](const auto& values, auto&&... us) {
// generic ND case
if (axes_rank(axes) != values.size())
BOOST_THROW_EXCEPTION(
std::invalid_argument("number of arguments must match histogram rank"));
const auto vsize = get_total_size(axes, values);
fill_n_check_extra_args(vsize, std::forward<Us>(us)...);
fill_n_1(offset, storage, axes, vsize, values.data(), std::forward<Us>(us)...);
},
values, std::forward<Us>(us)...);
}
// empty implementation for bad arguments to stop compiler from showing internals
template <class... Ts>
void fill_n(std::false_type, Ts...) {}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DETAIL_FILL_N_HPP

View File

@@ -0,0 +1,113 @@
// Copyright 2019 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_INDEX_TRANSLATOR_HPP
#define BOOST_HISTOGRAM_DETAIL_INDEX_TRANSLATOR_HPP
#include <algorithm>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/relaxed_equal.hpp>
#include <boost/histogram/detail/relaxed_tuple_size.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/multi_index.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integer_sequence.hpp>
#include <cassert>
#include <initializer_list>
#include <tuple>
#include <vector>
namespace boost {
namespace histogram {
namespace detail {
template <class A>
struct index_translator {
using index_type = axis::index_type;
using multi_index_type = multi_index<relaxed_tuple_size_t<A>::value>;
using cref = const A&;
cref dst, src;
bool pass_through[buffer_size<A>::value];
index_translator(cref d, cref s) : dst{d}, src{s} { init(dst, src); }
template <class T>
void init(const T& a, const T& b) {
std::transform(a.begin(), a.end(), b.begin(), pass_through,
[](const auto& a, const auto& b) {
return axis::visit(
[&](const auto& a) {
using U = std::decay_t<decltype(a)>;
return relaxed_equal{}(a, axis::get<U>(b));
},
a);
});
}
template <class... Ts>
void init(const std::tuple<Ts...>& a, const std::tuple<Ts...>& b) {
using Seq = mp11::mp_iota_c<sizeof...(Ts)>;
mp11::mp_for_each<Seq>([&](auto I) {
pass_through[I] = relaxed_equal{}(std::get<I>(a), std::get<I>(b));
});
}
template <class T>
static index_type translate(const T& dst, const T& src, index_type i) noexcept {
assert(axis::traits::is_continuous<T>::value == false); // LCOV_EXCL_LINE: unreachable
return dst.index(src.value(i));
}
template <class... Ts, class It>
void impl(const std::tuple<Ts...>& a, const std::tuple<Ts...>& b, It i,
index_type* j) const noexcept {
using Seq = mp11::mp_iota_c<sizeof...(Ts)>;
mp11::mp_for_each<Seq>([&](auto I) {
if (pass_through[I])
*(j + I) = *(i + I);
else
*(j + I) = this->translate(std::get<I>(a), std::get<I>(b), *(i + I));
});
}
template <class T, class It>
void impl(const T& a, const T& b, It i, index_type* j) const noexcept {
const bool* p = pass_through;
for (unsigned k = 0; k < a.size(); ++k, ++i, ++j, ++p) {
if (*p)
*j = *i;
else {
const auto& bk = b[k];
axis::visit(
[&](const auto& ak) {
using U = std::decay_t<decltype(ak)>;
*j = this->translate(ak, axis::get<U>(bk), *i);
},
a[k]);
}
}
}
template <class Indices>
auto operator()(const Indices& seq) const noexcept {
auto mi = multi_index_type::create(seq.size());
impl(dst, src, seq.begin(), mi.begin());
return mi;
}
};
template <class Axes>
auto make_index_translator(const Axes& dst, const Axes& src) noexcept {
return index_translator<Axes>{dst, src};
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,162 @@
// Copyright 2019 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)
//
// Uses code segments from boost/iterator/iterator_adaptor.hpp
// and boost/iterator/iterator_fascade.hpp
#ifndef BOOST_HISTOGRAM_DETAIL_ITERATOR_ADAPTOR_HPP
#define BOOST_HISTOGRAM_DETAIL_ITERATOR_ADAPTOR_HPP
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace detail {
// operator->() needs special support for input iterators to strictly meet the
// standard's requirements. If *i is not a reference type, we must still
// produce an lvalue to which a pointer can be formed. We do that by
// returning a proxy object containing an instance of the reference object.
template <class Reference>
struct operator_arrow_dispatch_t // proxy references
{
struct proxy {
explicit proxy(Reference const& x) noexcept : m_ref(x) {}
Reference* operator->() noexcept { return std::addressof(m_ref); }
Reference m_ref;
};
using result_type = proxy;
static result_type apply(Reference const& x) noexcept { return proxy(x); }
};
template <class T>
struct operator_arrow_dispatch_t<T&> // "real" references
{
using result_type = T*;
static result_type apply(T& x) noexcept { return std::addressof(x); }
};
// only for random access Base
template <class Derived, class Base, class Reference = std::remove_pointer_t<Base>&,
class Value = std::decay_t<Reference>>
class iterator_adaptor {
using operator_arrow_dispatch = operator_arrow_dispatch_t<Reference>;
public:
using base_type = Base;
using reference = Reference;
using value_type = std::remove_const_t<Value>;
using pointer = typename operator_arrow_dispatch::result_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
iterator_adaptor() = default;
explicit iterator_adaptor(base_type const& iter) : iter_(iter) {}
pointer operator->() const noexcept {
return operator_arrow_dispatch::apply(this->derived().operator*());
}
reference operator[](difference_type n) const { return *(this->derived() + n); }
Derived& operator++() {
++iter_;
return this->derived();
}
Derived& operator--() {
--iter_;
return this->derived();
}
Derived operator++(int) {
Derived tmp(this->derived());
++iter_;
return tmp;
}
Derived operator--(int) {
Derived tmp(this->derived());
--iter_;
return tmp;
}
Derived& operator+=(difference_type n) {
iter_ += n;
return this->derived();
}
Derived& operator-=(difference_type n) {
iter_ -= n;
return this->derived();
}
Derived operator+(difference_type n) const {
Derived tmp(this->derived());
tmp += n;
return tmp;
}
Derived operator-(difference_type n) const { return operator+(-n); }
template <class... Ts>
difference_type operator-(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ - x.iter_;
}
template <class... Ts>
bool operator==(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ == x.iter_;
}
template <class... Ts>
bool operator!=(const iterator_adaptor<Ts...>& x) const noexcept {
return !this->derived().operator==(x); // equal operator may be overridden in derived
}
template <class... Ts>
bool operator<(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ < x.iter_;
}
template <class... Ts>
bool operator>(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ > x.iter_;
}
template <class... Ts>
bool operator<=(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ <= x.iter_;
}
template <class... Ts>
bool operator>=(const iterator_adaptor<Ts...>& x) const noexcept {
return iter_ >= x.iter_;
}
friend Derived operator+(difference_type n, const Derived& x) { return x + n; }
Base const& base() const noexcept { return iter_; }
protected:
// for convenience in derived classes
using iterator_adaptor_ = iterator_adaptor;
private:
Derived& derived() noexcept { return *static_cast<Derived*>(this); }
const Derived& derived() const noexcept { return *static_cast<Derived const*>(this); }
Base iter_;
template <class, class, class, class>
friend class iterator_adaptor;
};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,255 @@
// Copyright 2018-2019 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_LARGE_INT_HPP
#define BOOST_HISTOGRAM_DETAIL_LARGE_INT_HPP
#include <boost/histogram/detail/operators.hpp>
#include <boost/histogram/detail/safe_comparison.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/function.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/utility.hpp>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <utility>
#include <vector>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
using is_unsigned_integral = mp11::mp_and<std::is_integral<T>, std::is_unsigned<T>>;
template <class T>
bool safe_increment(T& t) {
if (t < (std::numeric_limits<T>::max)()) {
++t;
return true;
}
return false;
}
template <class T, class U>
bool safe_radd(T& t, const U& u) {
static_assert(is_unsigned_integral<T>::value, "T must be unsigned integral type");
static_assert(is_unsigned_integral<U>::value, "T must be unsigned integral type");
if (static_cast<T>((std::numeric_limits<T>::max)() - t) >= u) {
t += static_cast<T>(u); // static_cast to suppress conversion warning
return true;
}
return false;
}
// An integer type which can grow arbitrarily large (until memory is exhausted).
// Use boost.multiprecision.cpp_int in your own code, it is much more sophisticated.
// We use it only to reduce coupling between boost libs.
template <class Allocator>
struct large_int : totally_ordered<large_int<Allocator>, large_int<Allocator>>,
partially_ordered<large_int<Allocator>, void> {
explicit large_int(const Allocator& a = {}) : data(1, 0, a) {}
explicit large_int(std::uint64_t v, const Allocator& a = {}) : data(1, v, a) {}
large_int(const large_int&) = default;
large_int& operator=(const large_int&) = default;
large_int(large_int&&) = default;
large_int& operator=(large_int&&) = default;
large_int& operator=(std::uint64_t o) {
data = decltype(data)(1, o);
return *this;
}
large_int& operator++() {
assert(data.size() > 0u);
std::size_t i = 0;
while (!safe_increment(data[i])) {
data[i] = 0;
++i;
if (i == data.size()) {
data.push_back(1);
break;
}
}
return *this;
}
large_int& operator+=(const large_int& o) {
if (this == &o) {
auto tmp{o};
return operator+=(tmp);
}
bool carry = false;
std::size_t i = 0;
for (std::uint64_t oi : o.data) {
auto& di = maybe_extend(i);
if (carry) {
if (safe_increment(oi))
carry = false;
else {
++i;
continue;
}
}
if (!safe_radd(di, oi)) {
add_remainder(di, oi);
carry = true;
}
++i;
}
while (carry) {
auto& di = maybe_extend(i);
if (safe_increment(di)) break;
di = 0;
++i;
}
return *this;
}
large_int& operator+=(std::uint64_t o) {
assert(data.size() > 0u);
if (safe_radd(data[0], o)) return *this;
add_remainder(data[0], o);
// carry the one, data may grow several times
std::size_t i = 1;
while (true) {
auto& di = maybe_extend(i);
if (safe_increment(di)) break;
di = 0;
++i;
}
return *this;
}
explicit operator double() const noexcept {
assert(data.size() > 0u);
double result = static_cast<double>(data[0]);
std::size_t i = 0;
while (++i < data.size())
result += static_cast<double>(data[i]) * std::pow(2.0, i * 64);
return result;
}
bool operator<(const large_int& o) const noexcept {
assert(data.size() > 0u);
assert(o.data.size() > 0u);
// no leading zeros allowed
assert(data.size() == 1 || data.back() > 0u);
assert(o.data.size() == 1 || o.data.back() > 0u);
if (data.size() < o.data.size()) return true;
if (data.size() > o.data.size()) return false;
auto s = data.size();
while (s > 0u) {
--s;
if (data[s] < o.data[s]) return true;
if (data[s] > o.data[s]) return false;
}
return false; // args are equal
}
bool operator==(const large_int& o) const noexcept {
assert(data.size() > 0u);
assert(o.data.size() > 0u);
// no leading zeros allowed
assert(data.size() == 1 || data.back() > 0u);
assert(o.data.size() == 1 || o.data.back() > 0u);
if (data.size() != o.data.size()) return false;
return std::equal(data.begin(), data.end(), o.data.begin());
}
template <class U>
std::enable_if_t<std::is_integral<U>::value, bool> operator<(
const U& o) const noexcept {
assert(data.size() > 0u);
return data.size() == 1 && safe_less()(data[0], o);
}
template <class U>
std::enable_if_t<std::is_integral<U>::value, bool> operator>(
const U& o) const noexcept {
assert(data.size() > 0u);
assert(data.size() == 1 || data.back() > 0u); // no leading zeros allowed
return data.size() > 1 || safe_less()(o, data[0]);
}
template <class U>
std::enable_if_t<std::is_integral<U>::value, bool> operator==(
const U& o) const noexcept {
assert(data.size() > 0u);
return data.size() == 1 && safe_equal()(data[0], o);
}
template <class U>
std::enable_if_t<std::is_floating_point<U>::value, bool> operator<(
const U& o) const noexcept {
return operator double() < o;
}
template <class U>
std::enable_if_t<std::is_floating_point<U>::value, bool> operator>(
const U& o) const noexcept {
return operator double() > o;
}
template <class U>
std::enable_if_t<std::is_floating_point<U>::value, bool> operator==(
const U& o) const noexcept {
return operator double() == o;
}
template <class U>
std::enable_if_t<
(!std::is_arithmetic<U>::value && std::is_convertible<U, double>::value), bool>
operator<(const U& o) const noexcept {
return operator double() < o;
}
template <class U>
std::enable_if_t<
(!std::is_arithmetic<U>::value && std::is_convertible<U, double>::value), bool>
operator>(const U& o) const noexcept {
return operator double() > o;
}
template <class U>
std::enable_if_t<
(!std::is_arithmetic<U>::value && std::is_convertible<U, double>::value), bool>
operator==(const U& o) const noexcept {
return operator double() == o;
}
std::uint64_t& maybe_extend(std::size_t i) {
while (i >= data.size()) data.push_back(0);
return data[i];
}
static void add_remainder(std::uint64_t& d, const std::uint64_t o) noexcept {
assert(d > 0u);
// in decimal system it would look like this:
// 8 + 8 = 6 = 8 - (9 - 8) - 1
// 9 + 1 = 0 = 9 - (9 - 1) - 1
auto tmp = (std::numeric_limits<std::uint64_t>::max)();
tmp -= o;
--d -= tmp;
}
template <class Archive>
void serialize(Archive& ar, unsigned /* version */) {
ar& make_nvp("data", data);
}
std::vector<std::uint64_t, Allocator> data;
};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,50 @@
// Copyright 2015-2019 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_LIMITS_HPP
#define BOOST_HISTOGRAM_DETAIL_LIMITS_HPP
#include <limits>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
constexpr T lowest() {
return std::numeric_limits<T>::lowest();
}
template <>
constexpr double lowest() {
return -std::numeric_limits<double>::infinity();
}
template <>
constexpr float lowest() {
return -std::numeric_limits<float>::infinity();
}
template <class T>
constexpr T highest() {
return (std::numeric_limits<T>::max)();
}
template <>
constexpr double highest() {
return std::numeric_limits<double>::infinity();
}
template <>
constexpr float highest() {
return std::numeric_limits<float>::infinity();
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,123 @@
// Copyright 2019 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_LINEARIZE_HPP
#define BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP
#include <boost/histogram/axis/option.hpp>
#include <boost/histogram/axis/traits.hpp>
#include <boost/histogram/axis/variant.hpp>
#include <boost/histogram/detail/optional_index.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/histogram/multi_index.hpp>
#include <cassert>
namespace boost {
namespace histogram {
namespace detail {
// initial offset to out must be set
template <class Index, class Opts>
std::size_t linearize(Opts, Index& out, const std::size_t stride,
const axis::index_type size, const axis::index_type idx) {
constexpr bool u = Opts::test(axis::option::underflow);
constexpr bool o = Opts::test(axis::option::overflow);
// must be non-const to avoid if constexpr warning from msvc
bool fast_track = std::is_same<Index, std::size_t>::value || (u && o);
if (fast_track) {
assert(idx >= (u ? -1 : 0));
assert(idx < (o ? size + 1 : size));
assert(idx >= 0 || static_cast<std::size_t>(-idx * stride) <= out);
out += idx * stride;
} else {
assert(idx >= -1);
assert(idx < size + 1);
// must be non-const to avoid if constexpr warning from msvc
bool is_valid = (u || idx >= 0) && (o || idx < size);
if (is_valid)
out += idx * stride;
else
out = invalid_index;
}
return size + u + o;
}
template <class Index, class Axis, class Value>
std::size_t linearize(Index& out, const std::size_t stride, const Axis& ax,
const Value& v) {
// mask options to reduce no. of template instantiations
constexpr auto opts = axis::traits::get_options<Axis>{} &
(axis::option::underflow | axis::option::overflow);
return linearize(opts, out, stride, ax.size(), axis::traits::index(ax, v));
}
// initial offset of out must be zero
template <class Index, class Axis, class Value>
std::size_t linearize_growth(Index& out, axis::index_type& shift,
const std::size_t stride, Axis& a, const Value& v) {
axis::index_type idx;
std::tie(idx, shift) = axis::traits::update(a, v);
constexpr bool u = axis::traits::get_options<Axis>::test(axis::option::underflow);
if (u) ++idx;
if (std::is_same<Index, std::size_t>::value) {
assert(idx < axis::traits::extent(a));
out += idx * stride;
} else {
if (0 <= idx && idx < axis::traits::extent(a))
out += idx * stride;
else
out = invalid_index;
}
return axis::traits::extent(a);
}
// initial offset of out must be zero
template <class A>
std::size_t linearize_index(optional_index& out, const std::size_t stride, const A& ax,
const axis::index_type idx) noexcept {
const auto opt = axis::traits::get_options<A>();
const axis::index_type begin = opt & axis::option::underflow ? -1 : 0;
const axis::index_type end = opt & axis::option::overflow ? ax.size() + 1 : ax.size();
const axis::index_type extent = end - begin;
// i may be arbitrarily out of range
if (begin <= idx && idx < end)
out += (idx - begin) * stride;
else
out = invalid_index;
return extent;
}
template <class A, std::size_t N>
optional_index linearize_indices(const A& axes, const multi_index<N>& indices) noexcept {
assert(axes_rank(axes) == detail::size(indices));
optional_index idx{0}; // offset not used by linearize_index
auto stride = static_cast<std::size_t>(1);
using std::begin;
auto i = begin(indices);
for_each_axis(axes,
[&](const auto& a) { stride *= linearize_index(idx, stride, a, *i++); });
return idx;
}
template <class Index, class... Ts, class Value>
std::size_t linearize(Index& o, const std::size_t s, const axis::variant<Ts...>& a,
const Value& v) {
return axis::visit([&o, &s, &v](const auto& a) { return linearize(o, s, a, v); }, a);
}
template <class Index, class... Ts, class Value>
std::size_t linearize_growth(Index& o, axis::index_type& sh, const std::size_t st,
axis::variant<Ts...>& a, const Value& v) {
return axis::visit([&](auto& a) { return linearize_growth(o, sh, st, a, v); }, a);
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP

View File

@@ -0,0 +1,33 @@
// Copyright 2015-2019 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_MAKE_DEFAULT_HPP
#define BOOST_HISTOGRAM_DETAIL_MAKE_DEFAULT_HPP
namespace boost {
namespace histogram {
namespace detail {
template <class T>
T make_default_impl(const T& t, decltype(t.get_allocator(), 0)) {
return T(t.get_allocator());
}
template <class T>
T make_default_impl(const T&, float) {
return T{};
}
template <class T>
T make_default(const T& t) {
return make_default_impl(t, 0);
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,41 @@
// Copyright 2019 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_NOOP_MUTEX_HPP
#define BOOST_HISTOGRAM_DETAIL_NOOP_MUTEX_HPP
#include <boost/core/empty_value.hpp>
#include <boost/histogram/detail/axes.hpp>
#include <boost/mp11/utility.hpp> // mp_if
#include <mutex>
namespace boost {
namespace histogram {
namespace detail {
struct null_mutex {
bool try_lock() noexcept { return true; }
void lock() noexcept {}
void unlock() noexcept {}
};
template <class Axes, class Storage,
class DetailMutex = mp11::mp_if_c<(Storage::has_threading_support &&
detail::has_growing_axis<Axes>::value),
std::mutex, detail::null_mutex>>
struct mutex_base : empty_value<DetailMutex> {
mutex_base() = default;
// do not copy or move mutex
mutex_base(const mutex_base&) : empty_value<DetailMutex>() {}
// do not copy or move mutex
mutex_base& operator=(const mutex_base&) { return *this; }
};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,70 @@
// Copyright 2019 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_NONMEMBER_CONTAINER_ACCESS_HPP
#define BOOST_HISTOGRAM_DETAIL_NONMEMBER_CONTAINER_ACCESS_HPP
#if __cpp_lib_nonmember_container_access >= 201411
#include <iterator>
namespace boost {
namespace histogram {
namespace detail {
using std::data;
using std::size;
} // namespace detail
} // namespace histogram
} // namespace boost
#else // std implementations are not available
#include <initializer_list>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class C>
constexpr auto data(C& c) -> decltype(c.data()) {
return c.data();
}
template <class C>
constexpr auto data(const C& c) -> decltype(c.data()) {
return c.data();
}
template <class T, std::size_t N>
constexpr T* data(T (&array)[N]) noexcept {
return array;
}
template <class E>
constexpr const E* data(std::initializer_list<E> il) noexcept {
return il.begin();
}
template <class C>
constexpr auto size(const C& c) -> decltype(c.size()) {
return c.size();
}
template <class T, std::size_t N>
constexpr std::size_t size(const T (&)[N]) noexcept {
return N;
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif
#endif // BOOST_HISTOGRAM_DETAIL_NONMEMBER_CONTAINER_ACCESS_HPP

View File

@@ -0,0 +1,141 @@
// Copyright 2019 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_OPERATORS_HPP
#define BOOST_HISTOGRAM_DETAIL_OPERATORS_HPP
#include <boost/histogram/detail/detect.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/utility.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class U>
using if_not_same = std::enable_if_t<(!std::is_same<T, U>::value), bool>;
// template <class T, class U>
// using if_not_same_and_has_eq =
// std::enable_if_t<(!std::is_same<T, U>::value && !has_method_eq<T, U>::value),
// bool>;
// totally_ordered is for types with a <= b == !(a > b) [floats with NaN violate this]
// Derived must implement <,== for symmetric form and <,>,== for non-symmetric.
// partially_ordered is for types with a <= b == a < b || a == b [for floats with NaN]
// Derived must implement <,== for symmetric form and <,>,== for non-symmetric.
template <class T, class U>
struct mirrored {
friend bool operator<(const U& a, const T& b) noexcept { return b > a; }
friend bool operator>(const U& a, const T& b) noexcept { return b < a; }
friend bool operator==(const U& a, const T& b) noexcept { return b == a; }
friend bool operator<=(const U& a, const T& b) noexcept { return b >= a; }
friend bool operator>=(const U& a, const T& b) noexcept { return b <= a; }
friend bool operator!=(const U& a, const T& b) noexcept { return b != a; }
}; // namespace histogram
template <class T>
struct mirrored<T, void> {
template <class U>
friend if_not_same<T, U> operator<(const U& a, const T& b) noexcept {
return b > a;
}
template <class U>
friend if_not_same<T, U> operator>(const U& a, const T& b) noexcept {
return b < a;
}
template <class U>
friend std::enable_if_t<(!has_method_eq<U, T>::value), bool> operator==(
const U& a, const T& b) noexcept {
return b.operator==(a);
}
template <class U>
friend if_not_same<T, U> operator<=(const U& a, const T& b) noexcept {
return b >= a;
}
template <class U>
friend if_not_same<T, U> operator>=(const U& a, const T& b) noexcept {
return b <= a;
}
template <class U>
friend if_not_same<T, U> operator!=(const U& a, const T& b) noexcept {
return b != a;
}
};
template <class T>
struct mirrored<T, T> {
friend bool operator>(const T& a, const T& b) noexcept { return b.operator<(a); }
};
template <class T, class U>
struct equality {
friend bool operator!=(const T& a, const U& b) noexcept { return !a.operator==(b); }
};
template <class T>
struct equality<T, void> {
template <class U>
friend if_not_same<T, U> operator!=(const T& a, const U& b) noexcept {
return !(a == b);
}
};
template <class T, class U>
struct totally_ordered_impl : equality<T, U>, mirrored<T, U> {
friend bool operator<=(const T& a, const U& b) noexcept { return !(a > b); }
friend bool operator>=(const T& a, const U& b) noexcept { return !(a < b); }
};
template <class T>
struct totally_ordered_impl<T, void> : equality<T, void>, mirrored<T, void> {
template <class U>
friend if_not_same<T, U> operator<=(const T& a, const U& b) noexcept {
return !(a > b);
}
template <class U>
friend if_not_same<T, U> operator>=(const T& a, const U& b) noexcept {
return !(a < b);
}
};
template <class T, class... Ts>
using totally_ordered = mp11::mp_rename<
mp11::mp_product<totally_ordered_impl, mp11::mp_list<T>, mp11::mp_list<Ts...> >,
mp11::mp_inherit>;
template <class T, class U>
struct partially_ordered_impl : equality<T, U>, mirrored<T, U> {
friend bool operator<=(const T& a, const U& b) noexcept { return a < b || a == b; }
friend bool operator>=(const T& a, const U& b) noexcept { return a > b || a == b; }
};
template <class T>
struct partially_ordered_impl<T, void> : equality<T, void>, mirrored<T, void> {
template <class U>
friend if_not_same<T, U> operator<=(const T& a, const U& b) noexcept {
return a < b || a == b;
}
template <class U>
friend if_not_same<T, U> operator>=(const T& a, const U& b) noexcept {
return a > b || a == b;
}
};
template <class T, class... Ts>
using partially_ordered = mp11::mp_rename<
mp11::mp_product<partially_ordered_impl, mp11::mp_list<T>, mp11::mp_list<Ts...> >,
mp11::mp_inherit>;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif // BOOST_HISTOGRAM_DETAIL_OPERATORS_HPP

View File

@@ -0,0 +1,55 @@
// Copyright 2019 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_OPTIONAL_INDEX_HPP
#define BOOST_HISTOGRAM_DETAIL_OPTIONAL_INDEX_HPP
#include <cassert>
#include <cstdint>
namespace boost {
namespace histogram {
namespace detail {
constexpr auto invalid_index = ~static_cast<std::size_t>(0);
// integer with a persistent invalid state, similar to NaN
struct optional_index {
std::size_t value;
optional_index& operator=(std::size_t x) noexcept {
value = x;
return *this;
}
optional_index& operator+=(std::intptr_t x) noexcept {
assert(x >= 0 || static_cast<std::size_t>(-x) <= value);
if (value != invalid_index) { value += x; }
return *this;
}
optional_index& operator+=(const optional_index& x) noexcept {
if (value != invalid_index) return operator+=(x.value);
value = invalid_index;
return *this;
}
operator std::size_t() const noexcept { return value; }
friend bool operator<=(std::size_t x, optional_index idx) noexcept {
return x <= idx.value;
}
};
constexpr inline bool is_valid(const std::size_t) noexcept { return true; }
inline bool is_valid(const optional_index x) noexcept { return x.value != invalid_index; }
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,28 @@
// Copyright 2019 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_PRIORITY_HPP
#define BOOST_HISTOGRAM_DETAIL_PRIORITY_HPP
#include <cstdint>
namespace boost {
namespace histogram {
namespace detail {
// priority is used to priorise ambiguous overloads
template <std::size_t N>
struct priority : priority<(N - 1)> {};
template <>
struct priority<0> {};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,82 @@
// Copyright 2020 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_REDUCE_COMMAND_HPP
#define BOOST_HISTOGRAM_DETAIL_REDUCE_COMMAND_HPP
#include <boost/histogram/detail/span.hpp>
#include <boost/histogram/fwd.hpp>
#include <boost/throw_exception.hpp>
#include <cassert>
#include <stdexcept>
#include <string>
namespace boost {
namespace histogram {
namespace detail {
struct reduce_command {
static constexpr unsigned unset = static_cast<unsigned>(-1);
unsigned iaxis = unset;
enum class range_t : char {
none,
indices,
values,
} range = range_t::none;
union {
axis::index_type index;
double value;
} begin{0}, end{0};
unsigned merge = 0; // default value indicates unset option
bool crop = false;
// for internal use by the reduce algorithm
bool is_ordered = true;
bool use_underflow_bin = true;
bool use_overflow_bin = true;
};
// - puts commands in correct axis order
// - sets iaxis for positional commands
// - detects and fails on invalid settings
// - fuses merge commands with non-merge commands
inline void normalize_reduce_commands(span<reduce_command> out,
span<const reduce_command> in) {
unsigned iaxis = 0;
for (const auto& o_in : in) {
assert(o_in.merge > 0);
if (o_in.iaxis != reduce_command::unset && o_in.iaxis >= out.size())
BOOST_THROW_EXCEPTION(std::invalid_argument("invalid axis index"));
auto& o_out = out.begin()[o_in.iaxis == reduce_command::unset ? iaxis : o_in.iaxis];
if (o_out.merge == 0) {
o_out = o_in;
} else {
// Some command was already set for this axis, try to fuse commands.
if (!((o_in.range == reduce_command::range_t::none) ^
(o_out.range == reduce_command::range_t::none)) ||
(o_out.merge > 1 && o_in.merge > 1))
BOOST_THROW_EXCEPTION(std::invalid_argument(
"multiple conflicting reduce commands for axis " +
std::to_string(o_in.iaxis == reduce_command::unset ? iaxis : o_in.iaxis)));
if (o_in.range != reduce_command::range_t::none) {
o_out.range = o_in.range;
o_out.begin = o_in.begin;
o_out.end = o_in.end;
} else {
o_out.merge = o_in.merge;
}
}
++iaxis;
}
iaxis = 0;
for (auto& o : out) o.iaxis = iaxis++;
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,46 @@
// Copyright 2015-2019 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_RELAXED_EQUAL_HPP
#define BOOST_HISTOGRAM_DETAIL_RELAXED_EQUAL_HPP
#include <boost/histogram/detail/priority.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
struct relaxed_equal {
template <class T, class U>
constexpr auto impl(const T& t, const U& u, priority<1>) const noexcept
-> decltype(t == u) const {
return t == u;
}
// consider T and U not equal, if there is no operator== defined for them
template <class T, class U>
constexpr bool impl(const T&, const U&, priority<0>) const noexcept {
return false;
}
// consider two T equal if they are stateless
template <class T>
constexpr bool impl(const T&, const T&, priority<0>) const noexcept {
return std::is_empty<T>::value;
}
template <class T, class U>
constexpr bool operator()(const T& t, const U& u) const noexcept {
return impl(t, u, priority<1>{});
}
};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,37 @@
// Copyright 2019 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_RELAXED_TUPLE_SIZE_HPP
#define BOOST_HISTOGRAM_DETAIL_RELAXED_TUPLE_SIZE_HPP
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
using dynamic_size = std::integral_constant<std::size_t, static_cast<std::size_t>(-1)>;
// Returns static size of tuple or dynamic_size
template <class T>
constexpr dynamic_size relaxed_tuple_size(const T&) noexcept {
return {};
}
template <class... Ts>
constexpr std::integral_constant<std::size_t, sizeof...(Ts)> relaxed_tuple_size(
const std::tuple<Ts...>&) noexcept {
return {};
}
template <class T>
using relaxed_tuple_size_t = decltype(relaxed_tuple_size(std::declval<T>()));
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,31 @@
// Copyright 2019 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_REPLACE_TYPE_HPP
#define BOOST_HISTOGRAM_DETAIL_REPLACE_TYPE_HPP
#include <boost/core/use_default.hpp>
#include <string>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class From, class To>
using replace_type = std::conditional_t<std::is_same<T, From>::value, To, T>;
template <class T, class Default>
using replace_default = replace_type<T, boost::use_default, Default>;
template <class T>
using replace_cstring = replace_type<T, const char*, std::string>;
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,87 @@
// Copyright 2019 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_SAFE_COMPARISON_HPP
#define BOOST_HISTOGRAM_DETAIL_SAFE_COMPARISON_HPP
#include <boost/mp11/utility.hpp>
#include <boost/type.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
auto make_unsigned(const T& t) noexcept {
static_assert(std::is_integral<T>::value, "");
return static_cast<std::make_unsigned_t<T>>(t);
}
template <class T>
using number_category =
mp11::mp_if<std::is_integral<T>,
mp11::mp_if<std::is_signed<T>, type<int>, type<unsigned>>, type<void>>;
// version of std::equal_to<> which handles signed and unsigned integers correctly
struct safe_equal {
template <class T, class U>
bool operator()(const T& t, const U& u) const noexcept {
return impl(number_category<T>{}, number_category<U>{}, t, u);
}
template <class C1, class C2, class T, class U>
bool impl(C1, C2, const T& t, const U& u) const noexcept {
return t == u;
}
template <class T, class U>
bool impl(type<int>, type<unsigned>, const T& t, const U& u) const noexcept {
return t >= 0 && make_unsigned(t) == u;
}
template <class T, class U>
bool impl(type<unsigned>, type<int>, const T& t, const U& u) const noexcept {
return impl(type<int>{}, type<unsigned>{}, u, t);
}
};
// version of std::less<> which handles signed and unsigned integers correctly
struct safe_less {
template <class T, class U>
bool operator()(const T& t, const U& u) const noexcept {
return impl(number_category<T>{}, number_category<U>{}, t, u);
}
template <class C1, class C2, class T, class U>
bool impl(C1, C2, const T& t, const U& u) const noexcept {
return t < u;
}
template <class T, class U>
bool impl(type<int>, type<unsigned>, const T& t, const U& u) const noexcept {
return t < 0 || make_unsigned(t) < u;
}
template <class T, class U>
bool impl(type<unsigned>, type<int>, const T& t, const U& u) const noexcept {
return 0 < u && t < make_unsigned(u);
}
};
// version of std::greater<> which handles signed and unsigned integers correctly
struct safe_greater {
template <class T, class U>
bool operator()(const T& t, const U& u) const noexcept {
return safe_less()(u, t);
}
};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,277 @@
// Copyright 2019 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_SPAN_HPP
#define BOOST_HISTOGRAM_DETAIL_SPAN_HPP
#ifdef __has_include
#if __has_include(<version>)
#include <version>
#ifdef __cpp_lib_span
#if __cpp_lib_span >= 201902
#define BOOST_HISTOGRAM_DETAIL_HAS_STD_SPAN
#endif
#endif
#endif
#endif
#ifdef BOOST_HISTOGRAM_DETAIL_HAS_STD_SPAN
#include <span>
namespace boost {
namespace histogram {
namespace detail {
using std::span;
} // namespace detail
} // namespace histogram
} // namespace boost
#else // C++17 span not available, so we use our implementation
// to be replaced by boost::span
#include <array>
#include <boost/histogram/detail/nonmember_container_access.hpp>
#include <cassert>
#include <initializer_list>
#include <iterator>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
namespace dtl = ::boost::histogram::detail;
static constexpr std::size_t dynamic_extent = ~static_cast<std::size_t>(0);
template <class T, std::size_t N>
class span_base {
public:
constexpr T* data() noexcept { return begin_; }
constexpr const T* data() const noexcept { return begin_; }
constexpr std::size_t size() const noexcept { return N; }
protected:
constexpr span_base(T* b, std::size_t s) noexcept : begin_(b) {
(void)s;
assert(N == s);
}
constexpr void set(T* b, std::size_t s) noexcept {
(void)s;
begin_ = b;
assert(N == s);
}
private:
T* begin_;
};
template <class T>
class span_base<T, dynamic_extent> {
public:
constexpr span_base() noexcept : begin_(nullptr), size_(0) {}
constexpr T* data() noexcept { return begin_; }
constexpr const T* data() const noexcept { return begin_; }
constexpr std::size_t size() const noexcept { return size_; }
protected:
constexpr span_base(T* b, std::size_t s) noexcept : begin_(b), size_(s) {}
constexpr void set(T* b, std::size_t s) noexcept {
begin_ = b;
size_ = s;
}
private:
T* begin_;
std::size_t size_;
};
template <class T, std::size_t Extent = dynamic_extent>
class span : public span_base<T, Extent> {
using base = span_base<T, Extent>;
public:
using element_type = T;
using value_type = std::remove_cv_t<T>;
using index_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr std::size_t extent = Extent;
using base::base;
constexpr span(pointer first, pointer last)
: span(first, static_cast<std::size_t>(last - first)) {
assert(extent == dynamic_extent ||
static_cast<difference_type>(extent) == (last - first));
}
constexpr span(pointer ptr, index_type count) : base(ptr, count) {}
template <std::size_t N>
constexpr span(element_type (&arr)[N]) noexcept : span(dtl::data(arr), N) {
static_assert(extent == dynamic_extent || extent == N, "static sizes do not match");
}
template <std::size_t N,
class = std::enable_if_t<(extent == dynamic_extent || extent == N)> >
constexpr span(std::array<value_type, N>& arr) noexcept : span(dtl::data(arr), N) {}
template <std::size_t N,
class = std::enable_if_t<(extent == dynamic_extent || extent == N)> >
constexpr span(const std::array<value_type, N>& arr) noexcept
: span(dtl::data(arr), N) {}
template <class Container, class = std::enable_if_t<std::is_convertible<
decltype(dtl::size(std::declval<const Container&>()),
dtl::data(std::declval<const Container&>())),
pointer>::value> >
constexpr span(const Container& cont) : span(dtl::data(cont), dtl::size(cont)) {}
template <class Container, class = std::enable_if_t<std::is_convertible<
decltype(dtl::size(std::declval<Container&>()),
dtl::data(std::declval<Container&>())),
pointer>::value> >
constexpr span(Container& cont) : span(dtl::data(cont), dtl::size(cont)) {}
template <class U, std::size_t N,
class = std::enable_if_t<((extent == dynamic_extent || extent == N) &&
std::is_convertible<U, element_type>::value)> >
constexpr span(const span<U, N>& s) noexcept : span(s.data(), s.size()) {}
template <class U, std::size_t N,
class = std::enable_if_t<((extent == dynamic_extent || extent == N) &&
std::is_convertible<U, element_type>::value)> >
constexpr span(span<U, N>& s) noexcept : span(s.data(), s.size()) {}
constexpr span(const span& other) noexcept = default;
constexpr iterator begin() { return base::data(); }
constexpr const_iterator begin() const { return base::data(); }
constexpr const_iterator cbegin() const { return base::data(); }
constexpr iterator end() { return base::data() + base::size(); }
constexpr const_iterator end() const { return base::data() + base::size(); }
constexpr const_iterator cend() const { return base::data() + base::size(); }
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const { return reverse_iterator(end()); }
const_reverse_iterator crbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const { return reverse_iterator(begin()); }
const_reverse_iterator crend() { return reverse_iterator(begin()); }
constexpr reference front() { return *base::data(); }
constexpr reference back() { return *(base::data() + base::size() - 1); }
constexpr reference operator[](index_type idx) const { return base::data()[idx]; }
constexpr std::size_t size_bytes() const noexcept {
return base::size() * sizeof(element_type);
}
constexpr bool empty() const noexcept { return base::size() == 0; }
template <std::size_t Count>
constexpr span<element_type, Count> first() const {
assert(Count <= base::size());
return span<element_type, Count>(base::data(), Count);
}
constexpr span<element_type, dynamic_extent> first(std::size_t count) const {
assert(count <= base::size());
return span<element_type, dynamic_extent>(base::data(), count);
}
template <std::size_t Count>
constexpr span<element_type, Count> last() const {
assert(Count <= base::size());
return span<element_type, Count>(base::data() + base::size() - Count, Count);
}
constexpr span<element_type, dynamic_extent> last(std::size_t count) const {
assert(count <= base::size());
return span<element_type, dynamic_extent>(base::data() + base::size() - count, count);
}
template <std::size_t Offset, std::size_t Count = dynamic_extent>
constexpr span<element_type,
(Count != dynamic_extent
? Count
: (extent != dynamic_extent ? extent - Offset : dynamic_extent))>
subspan() const {
assert(Offset <= base::size());
constexpr std::size_t E =
(Count != dynamic_extent
? Count
: (extent != dynamic_extent ? extent - Offset : dynamic_extent));
assert(E == dynamic_extent || E <= base::size());
return span<element_type, E>(base::data() + Offset,
Count == dynamic_extent ? base::size() - Offset : Count);
}
constexpr span<element_type, dynamic_extent> subspan(
std::size_t offset, std::size_t count = dynamic_extent) const {
assert(offset <= base::size());
const std::size_t s = count == dynamic_extent ? base::size() - offset : count;
assert(s <= base::size());
return span<element_type, dynamic_extent>(base::data() + offset, s);
}
};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif
#include <boost/histogram/detail/nonmember_container_access.hpp>
#include <utility>
namespace boost {
namespace histogram {
namespace detail {
namespace dtl = ::boost::histogram::detail;
template <class T>
auto make_span(T* begin, T* end) {
return dtl::span<T>{begin, end};
}
template <class T>
auto make_span(T* begin, std::size_t size) {
return dtl::span<T>{begin, size};
}
template <class Container, class = decltype(dtl::size(std::declval<Container>()),
dtl::data(std::declval<Container>()))>
auto make_span(const Container& cont) {
return make_span(dtl::data(cont), dtl::size(cont));
}
template <class T, std::size_t N>
auto make_span(T (&arr)[N]) {
return dtl::span<T, N>(arr, N);
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,47 @@
// Copyright 2018-2019 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_STATIC_IF_HPP
#define BOOST_HISTOGRAM_DETAIL_STATIC_IF_HPP
#include <type_traits>
#include <utility>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class F, class... Args>
constexpr decltype(auto) static_if_impl(
std::true_type, T&& t, F&&,
Args&&... args) noexcept(noexcept(std::declval<T>()(std::declval<Args>()...))) {
return std::forward<T>(t)(std::forward<Args>(args)...);
}
template <class T, class F, class... Args>
constexpr decltype(auto) static_if_impl(
std::false_type, T&&, F&& f,
Args&&... args) noexcept(noexcept(std::declval<F>()(std::declval<Args>()...))) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template <bool B, class... Ts>
constexpr decltype(auto) static_if_c(Ts&&... ts) noexcept(
noexcept(static_if_impl(std::integral_constant<bool, B>{}, std::declval<Ts>()...))) {
return static_if_impl(std::integral_constant<bool, B>{}, std::forward<Ts>(ts)...);
}
template <class Bool, class... Ts>
constexpr decltype(auto) static_if(Ts&&... ts) noexcept(
noexcept(static_if_impl(Bool{}, std::declval<Ts>()...))) {
return static_if_impl(Bool{}, std::forward<Ts>(ts)...);
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,120 @@
// Copyright 2019 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_SUB_ARRAY_HPP
#define BOOST_HISTOGRAM_DETAIL_SUB_ARRAY_HPP
#include <algorithm>
#include <boost/throw_exception.hpp>
#include <stdexcept>
namespace boost {
namespace histogram {
namespace detail {
template <class T, std::size_t N>
class sub_array {
constexpr bool swap_element_is_noexcept() noexcept {
using std::swap;
return noexcept(swap(std::declval<T&>(), std::declval<T&>()));
}
public:
using value_type = T;
using size_type = std::size_t;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using iterator = pointer;
using const_iterator = const_pointer;
sub_array() = default;
explicit sub_array(std::size_t s) noexcept : size_(s) { assert(size_ <= N); }
sub_array(std::size_t s, const T& value) noexcept(
std::is_nothrow_assignable<T, const_reference>::value)
: sub_array(s) {
fill(value);
}
reference at(size_type pos) noexcept {
if (pos >= size()) BOOST_THROW_EXCEPTION(std::out_of_range{"pos is out of range"});
return data_[pos];
}
const_reference at(size_type pos) const noexcept {
if (pos >= size()) BOOST_THROW_EXCEPTION(std::out_of_range{"pos is out of range"});
return data_[pos];
}
reference operator[](size_type pos) noexcept { return data_[pos]; }
const_reference operator[](size_type pos) const noexcept { return data_[pos]; }
reference front() noexcept { return data_[0]; }
const_reference front() const noexcept { return data_[0]; }
reference back() noexcept { return data_[size_ - 1]; }
const_reference back() const noexcept { return data_[size_ - 1]; }
pointer data() noexcept { return static_cast<pointer>(data_); }
const_pointer data() const noexcept { return static_cast<const_pointer>(data_); }
iterator begin() noexcept { return data_; }
const_iterator begin() const noexcept { return data_; }
iterator end() noexcept { return begin() + size_; }
const_iterator end() const noexcept { return begin() + size_; }
const_iterator cbegin() noexcept { return data_; }
const_iterator cbegin() const noexcept { return data_; }
const_iterator cend() noexcept { return cbegin() + size_; }
const_iterator cend() const noexcept { return cbegin() + size_; }
constexpr size_type max_size() const noexcept { return N; }
size_type size() const noexcept { return size_; }
bool empty() const noexcept { return size_ == 0; }
void fill(const_reference value) noexcept(
std::is_nothrow_assignable<T, const_reference>::value) {
std::fill(begin(), end(), value);
}
void swap(sub_array& other) noexcept(swap_element_is_noexcept()) {
using std::swap;
for (auto i = begin(), j = other.begin(); i != end(); ++i, ++j) swap(*i, *j);
}
private:
size_type size_ = 0;
value_type data_[N];
};
template <class T, std::size_t N>
bool operator==(const sub_array<T, N>& a, const sub_array<T, N>& b) noexcept {
return std::equal(a.begin(), a.end(), b.begin());
}
template <class T, std::size_t N>
bool operator!=(const sub_array<T, N>& a, const sub_array<T, N>& b) noexcept {
return !(a == b);
}
} // namespace detail
} // namespace histogram
} // namespace boost
namespace std {
template <class T, std::size_t N>
void swap(::boost::histogram::detail::sub_array<T, N>& a,
::boost::histogram::detail::sub_array<T, N>& b) noexcept(noexcept(a.swap(b))) {
a.swap(b);
}
} // namespace std
#endif

View File

@@ -0,0 +1,61 @@
// Copyright 2019 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_TRY_CAST_HPP
#define BOOST_HISTOGRAM_DETAIL_TRY_CAST_HPP
#include <boost/config.hpp> // BOOST_NORETURN
#include <boost/throw_exception.hpp>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <class T, class U>
constexpr T* ptr_cast(U*) noexcept {
return nullptr;
}
template <class T>
constexpr T* ptr_cast(T* p) noexcept {
return p;
}
template <class T>
constexpr const T* ptr_cast(const T* p) noexcept {
return p;
}
template <class T, class E, class U>
BOOST_NORETURN T try_cast_impl(std::false_type, std::false_type, U&&) {
BOOST_THROW_EXCEPTION(E("type cast error"));
}
// converting cast
template <class T, class E, class U>
T try_cast_impl(std::false_type, std::true_type, U&& u) noexcept {
return static_cast<T>(u); // cast to avoid warnings
}
// pass-through cast
template <class T, class E>
T&& try_cast_impl(std::true_type, std::true_type, T&& t) noexcept {
return std::forward<T>(t);
}
// cast fails at runtime with exception E instead of compile-time, T must be a value
template <class T, class E, class U>
T try_cast(U&& u) noexcept(std::is_convertible<U, T>::value) {
return try_cast_impl<T, E>(std::is_same<U, T>{}, std::is_convertible<U, T>{},
std::forward<U>(u));
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,34 @@
// Copyright 2015-2019 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_TUPLE_SLICE_HPP
#define BOOST_HISTOGRAM_DETAIL_TUPLE_SLICE_HPP
#include <boost/mp11/integer_sequence.hpp>
#include <tuple>
#include <type_traits>
namespace boost {
namespace histogram {
namespace detail {
template <std::size_t I, class T, std::size_t... K>
decltype(auto) tuple_slice_impl(T&& t, mp11::index_sequence<K...>) {
return std::forward_as_tuple(std::get<(I + K)>(std::forward<T>(t))...);
}
template <std::size_t I, std::size_t N, class Tuple>
decltype(auto) tuple_slice(Tuple&& t) {
constexpr auto S = std::tuple_size<std::decay_t<Tuple>>::value;
static_assert(I + N <= S, "I, N must be a valid subset");
return tuple_slice_impl<I>(std::forward<Tuple>(t), mp11::make_index_sequence<N>{});
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,47 @@
// Copyright 2019 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_TYPE_NAME_HPP
#define BOOST_HISTOGRAM_DETAIL_TYPE_NAME_HPP
#include <boost/core/typeinfo.hpp>
#include <boost/type.hpp>
#include <string>
namespace boost {
namespace histogram {
namespace detail {
template <class T>
std::string type_name_impl(boost::type<T>) {
return boost::core::demangled_name(BOOST_CORE_TYPEID(T));
}
template <class T>
std::string type_name_impl(boost::type<T const>) {
return type_name_impl(boost::type<T>{}) + " const";
}
template <class T>
std::string type_name_impl(boost::type<T&>) {
return type_name_impl(boost::type<T>{}) + " &";
}
template <class T>
std::string type_name_impl(boost::type<T&&>) {
return type_name_impl(boost::type<T>{}) + " &&";
}
template <class T>
std::string type_name() {
return type_name_impl(boost::type<T>{});
}
} // namespace detail
} // namespace histogram
} // namespace boost
#endif

View File

@@ -0,0 +1,67 @@
// Copyright 2019 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_VARIANT_PROXY_HPP
#define BOOST_HISTOGRAM_DETAIL_VARIANT_PROXY_HPP
#include <boost/core/nvp.hpp>
#include <boost/histogram/axis/traits.hpp> // variant_access
#include <boost/histogram/detail/static_if.hpp>
#include <boost/mp11/algorithm.hpp> // mp_with_index, mp_find, mp_at
#include <boost/mp11/list.hpp> // mp_size
#include <boost/throw_exception.hpp>
#include <stdexcept>
namespace boost {
namespace histogram {
namespace detail {
// This is a workaround to remain backward compatible in the serialization format. The
// proxy uses only the public interface of axis::variant for serialization and works
// independently of the underlying variant implementation.
template <class Variant>
struct variant_proxy {
Variant& variant;
template <class Archive>
void serialize(Archive& ar, unsigned /* version */) {
detail::static_if_c<Archive::is_loading::value>(
[this](auto& ar) { // loading
int which = 0;
ar >> make_nvp("which", which);
constexpr unsigned N = mp11::mp_size<Variant>::value;
if (which < 0 || static_cast<unsigned>(which) >= N)
// throw if which >= N, can happen if type was removed from variant
BOOST_THROW_EXCEPTION(
std::runtime_error("variant has fewer types than stored version"));
mp11::mp_with_index<N>(static_cast<unsigned>(which), [&ar, this](auto i) {
using T = mp11::mp_at_c<Variant, i>;
T value;
ar >> make_nvp("value", value);
this->variant = std::move(value);
T* new_address = variant_access::template get_if<T>(&this->variant);
ar.reset_object_address(new_address, &value);
});
},
[this](auto& ar) { // saving
visit(
[&ar](const auto& value) {
using T = std::decay_t<decltype(value)>;
const int which = static_cast<int>(mp11::mp_find<Variant, T>::value);
ar << make_nvp("which", which);
ar << make_nvp("value", value);
},
this->variant);
},
ar);
}
};
} // namespace detail
} // namespace histogram
} // namespace boost
#endif