mirror of
https://github.com/boostorg/histogram.git
synced 2026-02-20 02:42:17 +00:00
wip, support for growing axes
This commit is contained in:
@@ -8,7 +8,6 @@
|
||||
#define BOOST_HISTOGRAM_ALGORITHM_PROJECT_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
#include <boost/histogram/indexed.hpp>
|
||||
@@ -30,18 +29,27 @@ namespace algorithm {
|
||||
histogram is summed over the removed axes.
|
||||
*/
|
||||
template <class A, class S, unsigned N, typename... Ns>
|
||||
auto project(const histogram<A, S>& h, std::integral_constant<unsigned, N> n, Ns... ns) {
|
||||
using LN = mp11::mp_list<decltype(n), Ns...>;
|
||||
auto project(const histogram<A, S>& h, std::integral_constant<unsigned, N> I, Ns... Is) {
|
||||
using LN = mp11::mp_list<decltype(I), Ns...>;
|
||||
static_assert(mp11::mp_is_set<LN>::value, "indices must be unique");
|
||||
|
||||
auto axes = detail::make_sub_axes(unsafe_access::axes(h), n, ns...);
|
||||
auto result = histogram<decltype(axes), S>(
|
||||
std::move(axes), detail::make_default(unsafe_access::storage(h)));
|
||||
const auto& old_axes = unsafe_access::axes(h);
|
||||
auto axes = detail::static_if<detail::is_tuple<A>>(
|
||||
[&](const auto& old_axes) {
|
||||
return std::make_tuple(std::get<I>(old_axes), std::get<Is>(old_axes)...);
|
||||
},
|
||||
[&](const auto& old_axes) {
|
||||
return detail::naked<decltype(old_axes)>({old_axes[I], old_axes[Is]...});
|
||||
},
|
||||
old_axes);
|
||||
|
||||
detail::axes_buffer<decltype(axes), int> idx(result.rank());
|
||||
const auto& old_storage = unsafe_access::storage(h);
|
||||
using A2 = decltype(axes);
|
||||
auto result = histogram<A2, S>(std::move(axes), detail::make_default(old_storage));
|
||||
auto idx = detail::make_stack_buffer<int>(unsafe_access::axes(result));
|
||||
for (auto x : indexed(h, true)) {
|
||||
auto i = idx.begin();
|
||||
mp11::mp_for_each<LN>([&i, &x](auto I) { *i++ = x[I]; });
|
||||
mp11::mp_for_each<LN>([&i, &x](auto J) { *i++ = x[J]; });
|
||||
result.at(idx) += *x;
|
||||
}
|
||||
return result;
|
||||
@@ -61,22 +69,22 @@ auto project(const histogram<A, S>& h, const Iterable& c) {
|
||||
const auto& old_axes = unsafe_access::axes(h);
|
||||
auto axes = detail::make_default(old_axes);
|
||||
axes.reserve(c.size());
|
||||
detail::axes_buffer<A, bool> seen(old_axes.size(), false);
|
||||
auto seen = detail::make_stack_buffer<bool>(old_axes, false);
|
||||
for (auto d : c) {
|
||||
if (seen[d]) BOOST_THROW_EXCEPTION(std::invalid_argument("indices must be unique"));
|
||||
seen[d] = true;
|
||||
axes.emplace_back(old_axes[d]);
|
||||
}
|
||||
|
||||
auto result =
|
||||
histogram<A, S>(std::move(axes), detail::make_default(unsafe_access::storage(h)));
|
||||
|
||||
detail::axes_buffer<A, int> idx(result.rank());
|
||||
const auto& old_storage = unsafe_access::storage(h);
|
||||
auto result = histogram<A, S>(std::move(axes), detail::make_default(old_storage));
|
||||
auto idx = detail::make_stack_buffer<int>(unsafe_access::axes(result));
|
||||
for (auto x : indexed(h, true)) {
|
||||
auto i = idx.begin();
|
||||
for (auto d : c) *i++ = x[d];
|
||||
result.at(idx) += *x;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,33 +67,44 @@ inline reduce_option_type rebin(unsigned merge) { return rebin(0, merge); }
|
||||
|
||||
template <class Histogram, class C, class = detail::requires_iterable<C>>
|
||||
decltype(auto) reduce(const Histogram& h, const C& options) {
|
||||
using axes_type = typename Histogram::axes_type;
|
||||
const auto& old_axes = unsafe_access::axes(h);
|
||||
|
||||
struct option_item : reduce_option_type {
|
||||
int begin, end;
|
||||
bool is_set() const noexcept { return reduce_option_type::merge > 0; }
|
||||
};
|
||||
|
||||
auto options_internal = detail::axes_buffer<axes_type, option_item>(h.rank());
|
||||
auto opts = detail::make_stack_buffer<option_item>(old_axes);
|
||||
for (const auto& o : options) {
|
||||
auto& oi = options_internal[o.iaxis];
|
||||
if (oi.is_set()) // did we already set the option for this axis?
|
||||
auto& oi = opts[o.iaxis];
|
||||
if (oi.merge > 0) // did we already set the option for this axis?
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("indices must be unique"));
|
||||
oi.lower = o.lower;
|
||||
oi.upper = o.upper;
|
||||
oi.merge = o.merge;
|
||||
}
|
||||
|
||||
auto axes = detail::make_empty_axes(unsafe_access::axes(h));
|
||||
auto axes = detail::static_if<detail::is_tuple<detail::naked<decltype(old_axes)>>>(
|
||||
[](const auto& c) { return detail::naked<decltype(c)>(); },
|
||||
[](const auto& c) {
|
||||
using A = detail::naked<decltype(c)>;
|
||||
auto axes = A(c.get_allocator());
|
||||
axes.reserve(c.size());
|
||||
detail::for_each_axis(c, [&axes](const auto& a) {
|
||||
using U = detail::naked<decltype(a)>;
|
||||
axes.emplace_back(U());
|
||||
});
|
||||
return axes;
|
||||
},
|
||||
old_axes);
|
||||
|
||||
unsigned iaxis = 0;
|
||||
h.for_each_axis([&](const auto& a) {
|
||||
using T = detail::unqual<decltype(a)>;
|
||||
using T = detail::naked<decltype(a)>;
|
||||
|
||||
auto& o = options_internal[iaxis];
|
||||
auto& o = opts[iaxis];
|
||||
o.begin = 0;
|
||||
o.end = a.size();
|
||||
if (o.is_set()) {
|
||||
if (o.merge > 0) { // option is set?
|
||||
if (o.lower < o.upper) {
|
||||
while (o.begin != o.end && a.value(o.begin) < o.lower) ++o.begin;
|
||||
while (o.end != o.begin && a.value(o.end - 1) >= o.upper) --o.end;
|
||||
@@ -112,13 +123,13 @@ decltype(auto) reduce(const Histogram& h, const C& options) {
|
||||
++iaxis;
|
||||
});
|
||||
|
||||
auto result =
|
||||
Histogram(std::move(axes), detail::make_default(unsafe_access::storage(h)));
|
||||
auto storage = detail::make_default(unsafe_access::storage(h));
|
||||
auto result = Histogram(std::move(axes), std::move(storage));
|
||||
|
||||
detail::axes_buffer<axes_type, int> idx(h.rank());
|
||||
auto idx = detail::make_stack_buffer<int>(unsafe_access::axes(result));
|
||||
for (auto x : indexed(h, true)) {
|
||||
auto i = idx.begin();
|
||||
auto o = options_internal.begin();
|
||||
auto o = opts.begin();
|
||||
for (auto j : x) {
|
||||
*i = (j - o->begin);
|
||||
if (*i <= -1)
|
||||
|
||||
@@ -37,8 +37,7 @@ void stream_metadata(OStream& os, const T& t) {
|
||||
if (!oss.str().empty()) { os << ", metadata=" << std::quoted(oss.str()); }
|
||||
},
|
||||
[&os](const auto&) {
|
||||
using U = detail::unqual<T>;
|
||||
os << ", metadata=" << boost::core::demangled_name(BOOST_CORE_TYPEID(U));
|
||||
os << ", metadata=" << boost::core::demangled_name(BOOST_CORE_TYPEID(T));
|
||||
},
|
||||
t);
|
||||
}
|
||||
@@ -111,7 +110,7 @@ template <typename C, typename T, typename U>
|
||||
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const polymorphic_bin<U>& i) {
|
||||
if (i.is_discrete())
|
||||
os << i;
|
||||
os << static_cast<double>(i);
|
||||
else
|
||||
os << "[" << i.lower() << ", " << i.upper() << ")";
|
||||
return os;
|
||||
@@ -169,7 +168,7 @@ std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& os,
|
||||
const variant<Ts...>& v) {
|
||||
visit(
|
||||
[&os](const auto& x) {
|
||||
using A = detail::unqual<decltype(x)>;
|
||||
using A = detail::naked<decltype(x)>;
|
||||
detail::static_if<detail::is_streamable<A>>(
|
||||
[&os](const auto& x) { os << x; },
|
||||
[](const auto&) {
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
polymorphic_bin(value_type lower, value_type upper)
|
||||
: lower_or_value_(lower), upper_(upper) {}
|
||||
|
||||
operator value_type() const noexcept { return lower_or_value_; }
|
||||
operator const value_type&() const noexcept { return lower_or_value_; }
|
||||
|
||||
value_type lower() const noexcept { return lower_or_value_; }
|
||||
value_type upper() const noexcept { return upper_; }
|
||||
|
||||
@@ -30,15 +30,14 @@ axis::option_type options_impl(const T& t, std::true_type) {
|
||||
|
||||
template <typename FIntArg, typename FDoubleArg, typename T>
|
||||
decltype(auto) value_method_switch(FIntArg&& iarg, FDoubleArg&& darg, const T& t) {
|
||||
using U = unqual<T>;
|
||||
return static_if<has_method_value<U>>(
|
||||
return static_if<has_method_value<T>>(
|
||||
[](FIntArg&& iarg, FDoubleArg&& darg, const auto& t) {
|
||||
using A = unqual<decltype(t)>;
|
||||
using A = naked<decltype(t)>;
|
||||
return static_if<std::is_same<arg_type<decltype(&A::value), 0>, int>>(
|
||||
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
|
||||
},
|
||||
[](FIntArg&&, FDoubleArg&&, const auto& t) {
|
||||
using A = unqual<decltype(t)>;
|
||||
using A = naked<decltype(t)>;
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(detail::cat(
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(A)), " has no value method")));
|
||||
return 0;
|
||||
@@ -48,16 +47,15 @@ decltype(auto) value_method_switch(FIntArg&& iarg, FDoubleArg&& darg, const T& t
|
||||
|
||||
template <typename R1, typename R2, typename FIntArg, typename FDoubleArg, typename T>
|
||||
R2 value_method_switch_with_return_type(FIntArg&& iarg, FDoubleArg&& darg, const T& t) {
|
||||
using U = unqual<T>;
|
||||
return static_if<has_method_value_with_convertible_return_type<U, R1>>(
|
||||
return static_if<has_method_value_with_convertible_return_type<T, R1>>(
|
||||
[](FIntArg&& iarg, FDoubleArg&& darg, const auto& t) -> R2 {
|
||||
using A = unqual<decltype(t)>;
|
||||
using A = naked<decltype(t)>;
|
||||
return static_if<std::is_same<arg_type<decltype(&A::value), 0>, int>>(
|
||||
std::forward<FIntArg>(iarg), std::forward<FDoubleArg>(darg), t);
|
||||
},
|
||||
[](FIntArg&&, FDoubleArg&&, const auto&) -> R2 {
|
||||
BOOST_THROW_EXCEPTION(std::runtime_error(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(U)),
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(T)),
|
||||
" has no value method or return type is not convertible to ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(R1)))));
|
||||
},
|
||||
@@ -69,7 +67,7 @@ namespace axis {
|
||||
namespace traits {
|
||||
template <typename T>
|
||||
decltype(auto) metadata(T&& t) noexcept {
|
||||
return detail::static_if<detail::has_method_metadata<T>>(
|
||||
return detail::static_if<detail::has_method_metadata<detail::naked<T>>>(
|
||||
[](auto&& x) -> decltype(auto) { return x.metadata(); },
|
||||
[](T &&) -> detail::copy_qualifiers<T, axis::null_type> {
|
||||
static axis::null_type m;
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
BOOST_ASSERT((end - begin) % merge == 0);
|
||||
if (Options & option_type::circular && !(begin == 0 && end == src.size()))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
|
||||
using It = const detail::unqual<decltype(*src.x_.first())>*;
|
||||
using It = const detail::naked<decltype(*src.x_.first())>*;
|
||||
struct skip_iterator {
|
||||
It it;
|
||||
unsigned skip;
|
||||
@@ -230,17 +230,17 @@ variable(std::initializer_list<U>, M)->variable<T, M>;
|
||||
|
||||
template <class Iterable,
|
||||
class T = detail::convert_integer<
|
||||
detail::unqual<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
|
||||
detail::naked<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
|
||||
variable(Iterable)->variable<T>;
|
||||
|
||||
template <class Iterable,
|
||||
class T = detail::convert_integer<
|
||||
detail::unqual<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
|
||||
detail::naked<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
|
||||
variable(Iterable, const char*)->variable<T>;
|
||||
|
||||
template <class Iterable, class M,
|
||||
class T = detail::convert_integer<
|
||||
detail::unqual<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
|
||||
detail::naked<decltype(*std::begin(std::declval<Iterable&>()))>, double>>
|
||||
variable(Iterable, M)->variable<T, M>;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -45,16 +45,16 @@ namespace axis {
|
||||
template <typename... Ts>
|
||||
class variant : private boost::variant<Ts...>, public iterator_mixin<variant<Ts...>> {
|
||||
using base_type = boost::variant<Ts...>;
|
||||
using first_bounded_type = detail::unqual<mp11::mp_first<base_type>>;
|
||||
using first_bounded_type = detail::naked<mp11::mp_first<base_type>>;
|
||||
|
||||
using types = mp11::mp_transform<detail::unqual, base_type>;
|
||||
using types = mp11::mp_transform<detail::naked, base_type>;
|
||||
template <typename T>
|
||||
using requires_bounded_type =
|
||||
mp11::mp_if<mp11::mp_contains<types, detail::unqual<T>>, void>;
|
||||
mp11::mp_if<mp11::mp_contains<types, detail::naked<T>>, void>;
|
||||
|
||||
public:
|
||||
using metadata_type =
|
||||
detail::unqual<decltype(traits::metadata(std::declval<first_bounded_type&>()))>;
|
||||
detail::naked<decltype(traits::metadata(std::declval<first_bounded_type&>()))>;
|
||||
|
||||
variant() = default;
|
||||
variant(const variant&) = default;
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
variant& operator=(const variant<Us...>& u) {
|
||||
visit(
|
||||
[this](const auto& u) {
|
||||
using U = detail::unqual<decltype(u)>;
|
||||
using U = detail::naked<decltype(u)>;
|
||||
detail::static_if<mp11::mp_contains<types, U>>(
|
||||
[this](const auto& u) { this->operator=(u); },
|
||||
[](const auto&) {
|
||||
@@ -146,7 +146,7 @@ public:
|
||||
int operator()(U&& x) const {
|
||||
return visit(
|
||||
[&x](const auto& a) {
|
||||
using A = detail::unqual<decltype(a)>;
|
||||
using A = detail::naked<decltype(a)>;
|
||||
using arg_t = detail::arg_type<A>;
|
||||
return detail::static_if<std::is_convertible<U, arg_t>>(
|
||||
[&x](const auto& a) -> int { return a(x); },
|
||||
@@ -272,21 +272,21 @@ const T* get(const variant<Us...>* v) {
|
||||
}
|
||||
|
||||
// pass-through version for generic programming, if U is axis instead of variant
|
||||
template <typename T, typename U, typename = detail::requires_axis<detail::unqual<U>>>
|
||||
template <typename T, typename U, typename = detail::requires_axis<detail::naked<U>>>
|
||||
auto get(U&& u) -> detail::copy_qualifiers<U, T> {
|
||||
return std::forward<U>(u);
|
||||
}
|
||||
|
||||
// pass-through version for generic programming, if U is axis instead of variant
|
||||
template <typename T, typename U, typename = detail::requires_axis<detail::unqual<U>>>
|
||||
template <typename T, typename U, typename = detail::requires_axis<detail::naked<U>>>
|
||||
T* get(U* u) {
|
||||
return std::is_same<T, detail::unqual<U>>::value ? reinterpret_cast<T*>(u) : nullptr;
|
||||
return std::is_same<T, detail::naked<U>>::value ? reinterpret_cast<T*>(u) : nullptr;
|
||||
}
|
||||
|
||||
// pass-through version for generic programming, if U is axis instead of variant
|
||||
template <typename T, typename U, typename = detail::requires_axis<detail::unqual<U>>>
|
||||
template <typename T, typename U, typename = detail::requires_axis<detail::naked<U>>>
|
||||
const T* get(const U* u) {
|
||||
return std::is_same<T, detail::unqual<U>>::value ? reinterpret_cast<const T*>(u)
|
||||
return std::is_same<T, detail::naked<U>>::value ? reinterpret_cast<const T*>(u)
|
||||
: nullptr;
|
||||
}
|
||||
} // namespace axis
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_AXES_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_AXES_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
@@ -19,86 +17,61 @@
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#ifdef BOOST_HISTOGRAM_WITH_ACCUMULATORS_SUPPORT
|
||||
#include <boost/accumulators/accumulators.hpp>
|
||||
#endif
|
||||
|
||||
/* Most of the histogram code is generic and works for any number of axes. Buffers with a
|
||||
* fixed maximum capacity are used in some places, which have a size equal to the rank of
|
||||
* a histogram. The buffers are statically allocated to improve performance, which means
|
||||
* that they need a preset maximum capacity. 32 seems like a safe upper limit for the rank
|
||||
* (you can nevertheless increase it here if necessary): the simplest non-trivial axis has
|
||||
* 2 bins; even if counters are used which need only a byte of storage per bin, this still
|
||||
* corresponds to 4 GB of storage.
|
||||
*/
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_AXES_LIMIT
|
||||
#define BOOST_HISTOGRAM_DETAIL_AXES_LIMIT 32
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
struct is_accumulator_set : std::false_type {};
|
||||
|
||||
#ifdef BOOST_HISTOGRAM_WITH_ACCUMULATORS_SUPPORT
|
||||
template <typename... Ts>
|
||||
struct is_accumulator_set<::boost::accumulators::accumulator_set<Ts...>>
|
||||
: std::true_type {};
|
||||
#endif
|
||||
|
||||
template <unsigned N, typename... Ts>
|
||||
template <unsigned N, class... Ts>
|
||||
decltype(auto) axis_get(std::tuple<Ts...>& axes) {
|
||||
return std::get<N>(axes);
|
||||
}
|
||||
|
||||
template <unsigned N, typename... Ts>
|
||||
template <unsigned N, class... Ts>
|
||||
decltype(auto) axis_get(const std::tuple<Ts...>& axes) {
|
||||
return std::get<N>(axes);
|
||||
}
|
||||
|
||||
template <unsigned N, typename T>
|
||||
template <unsigned N, class T>
|
||||
decltype(auto) axis_get(T& axes) {
|
||||
return axes[N];
|
||||
}
|
||||
|
||||
template <unsigned N, typename T>
|
||||
template <unsigned N, class T>
|
||||
decltype(auto) axis_get(const T& axes) {
|
||||
return axes[N];
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
template <class... Ts>
|
||||
decltype(auto) axis_get(std::tuple<Ts...>& axes, unsigned i) {
|
||||
return mp11::mp_with_index<sizeof...(Ts)>(
|
||||
i, [&](auto I) { return axis::variant<Ts&...>(std::get<I>(axes)); });
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
template <class... Ts>
|
||||
decltype(auto) axis_get(const std::tuple<Ts...>& axes, unsigned i) {
|
||||
return mp11::mp_with_index<sizeof...(Ts)>(
|
||||
i, [&](auto I) { return axis::variant<const Ts&...>(std::get<I>(axes)); });
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <class T>
|
||||
decltype(auto) axis_get(T& axes, unsigned i) {
|
||||
return axes.at(i);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <class T>
|
||||
decltype(auto) axis_get(const T& axes, unsigned i) {
|
||||
return axes.at(i);
|
||||
}
|
||||
|
||||
template <typename... Ts, typename... Us>
|
||||
template <class... Ts, class... Us>
|
||||
bool axes_equal(const std::tuple<Ts...>& t, const std::tuple<Us...>& u) {
|
||||
return static_if<std::is_same<mp11::mp_list<Ts...>, mp11::mp_list<Us...>>>(
|
||||
[](const auto& a, const auto& b) { return relaxed_equal(a, b); },
|
||||
[](const auto&, const auto&) { return false; }, t, u);
|
||||
}
|
||||
|
||||
template <typename... Ts, typename U>
|
||||
template <class... Ts, class U>
|
||||
bool axes_equal(const std::tuple<Ts...>& t, const U& u) {
|
||||
if (sizeof...(Ts) != u.size()) return false;
|
||||
bool equal = true;
|
||||
@@ -110,18 +83,18 @@ bool axes_equal(const std::tuple<Ts...>& t, const U& u) {
|
||||
return equal;
|
||||
}
|
||||
|
||||
template <typename T, typename... Us>
|
||||
template <class T, class... Us>
|
||||
bool axes_equal(const T& t, const std::tuple<Us...>& u) {
|
||||
return axes_equal(u, t);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
template <class T, class U>
|
||||
bool axes_equal(const T& t, const U& u) {
|
||||
if (t.size() != u.size()) return false;
|
||||
return std::equal(t.begin(), t.end(), u.begin());
|
||||
}
|
||||
|
||||
template <typename... Ts, typename... Us>
|
||||
template <class... Ts, class... Us>
|
||||
void axes_assign(std::tuple<Ts...>& t, const std::tuple<Us...>& u) {
|
||||
static_if<std::is_same<mp11::mp_list<Ts...>, mp11::mp_list<Us...>>>(
|
||||
[](auto& a, const auto& b) { a = b; },
|
||||
@@ -132,7 +105,7 @@ void axes_assign(std::tuple<Ts...>& t, const std::tuple<Us...>& u) {
|
||||
t, u);
|
||||
}
|
||||
|
||||
template <typename... Ts, typename U>
|
||||
template <class... Ts, class U>
|
||||
void axes_assign(std::tuple<Ts...>& t, const U& u) {
|
||||
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Ts)>>([&](auto I) {
|
||||
using T = mp11::mp_at_c<std::tuple<Ts...>, I>;
|
||||
@@ -140,7 +113,7 @@ void axes_assign(std::tuple<Ts...>& t, const U& u) {
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T, typename... Us>
|
||||
template <class T, class... Us>
|
||||
void axes_assign(T& t, const std::tuple<Us...>& u) {
|
||||
t.resize(sizeof...(Us));
|
||||
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Us)>>(
|
||||
@@ -153,18 +126,8 @@ void axes_assign(T& t, const U& u) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr std::size_t axes_size(const T& axes) noexcept {
|
||||
return static_if<has_fixed_size<unqual<T>>>(
|
||||
[](const auto& a) {
|
||||
using U = unqual<decltype(a)>;
|
||||
return std::tuple_size<U>::value;
|
||||
},
|
||||
[&](const auto& a) { return a.size(); }, axes);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void rank_check(const T& axes, const unsigned N) {
|
||||
BOOST_ASSERT_MSG(N < axes_size(axes), "index out of range");
|
||||
void axis_index_is_valid(const T& axes, const unsigned N) {
|
||||
BOOST_ASSERT_MSG(N < get_size(axes), "index out of range");
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
@@ -174,12 +137,12 @@ void for_each_axis_impl(std::true_type, const T& axes, F&& f) {
|
||||
|
||||
template <typename F, typename T>
|
||||
void for_each_axis_impl(std::false_type, const T& axes, F&& f) {
|
||||
for (const auto& x : axes) f(x);
|
||||
for (const auto& x : axes) std::forward<F>(f)(x);
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
void for_each_axis(const T& axes, F&& f) {
|
||||
using U = mp11::mp_first<unqual<T>>;
|
||||
using U = mp11::mp_first<T>;
|
||||
for_each_axis_impl(is_axis_variant<U>(), axes, std::forward<F>(f));
|
||||
}
|
||||
|
||||
@@ -188,28 +151,6 @@ void for_each_axis(const std::tuple<Ts...>& axes, F&& f) {
|
||||
mp11::tuple_for_each(axes, std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename Axes, typename T>
|
||||
using axes_buffer = boost::container::static_vector<
|
||||
T, mp11::mp_eval_if_c<!(has_fixed_size<Axes>::value),
|
||||
mp11::mp_size_t<BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>,
|
||||
std::tuple_size, Axes>::value>;
|
||||
|
||||
template <typename T>
|
||||
auto make_empty_axes(const T& t) {
|
||||
auto r = T(t.get_allocator());
|
||||
r.reserve(t.size());
|
||||
for_each_axis(t, [&r](const auto& a) {
|
||||
using U = unqual<decltype(a)>;
|
||||
r.emplace_back(U());
|
||||
});
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
auto make_empty_axes(const std::tuple<Ts...>&) {
|
||||
return std::tuple<Ts...>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t bincount(const T& axes) {
|
||||
std::size_t n = 1;
|
||||
@@ -217,234 +158,6 @@ std::size_t bincount(const T& axes) {
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename... Ns, typename... Ts>
|
||||
auto make_sub_axes(const std::tuple<Ts...>& t, Ns... ns) {
|
||||
return std::make_tuple(std::get<ns>(t)...);
|
||||
}
|
||||
|
||||
template <typename... Ns, typename T>
|
||||
auto make_sub_axes(const T& t, Ns... ns) {
|
||||
return T({t[ns]...}, t.get_allocator());
|
||||
}
|
||||
|
||||
/// Index with an invalid state
|
||||
struct optional_index {
|
||||
std::size_t idx = 0;
|
||||
std::size_t stride = 1;
|
||||
operator bool() const { return stride > 0; }
|
||||
std::size_t operator*() const { return idx; }
|
||||
};
|
||||
|
||||
inline void linearize(optional_index& out, const int axis_shape, int j) noexcept {
|
||||
// j is internal index, which is potentially shifted by +1 wrt external index
|
||||
out.idx += j * out.stride;
|
||||
// set stride to 0, if j is invalid
|
||||
out.stride *= (0 <= j && j < axis_shape) * axis_shape;
|
||||
}
|
||||
|
||||
template <typename A, typename U>
|
||||
void linearize_value(std::true_type, optional_index& out, const A& axis, const U& u) {
|
||||
const auto extend = axis::traits::extend(axis);
|
||||
const auto opt = axis::traits::options(axis);
|
||||
const auto j = axis(u) + (opt & axis::option_type::underflow);
|
||||
return linearize(out, extend, j);
|
||||
}
|
||||
|
||||
template <typename A, typename U>
|
||||
void linearize_value(std::false_type, optional_index&, const A&, const U&) {
|
||||
// protect against instantiation with wrong template argument
|
||||
using arg_t = arg_type<A>;
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(A)),
|
||||
": cannot convert argument of type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(U)), " to ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(arg_t)))));
|
||||
}
|
||||
|
||||
template <typename A, typename U>
|
||||
void linearize_value(optional_index& out, const A& axis, const U& u) {
|
||||
// protect against instantiation with wrong template argument
|
||||
using arg_t = arg_type<A>;
|
||||
return linearize_value(std::is_convertible<U, arg_t>(), out, axis, u);
|
||||
}
|
||||
|
||||
template <typename... Ts, typename U>
|
||||
void linearize_value(optional_index& out, const axis::variant<Ts...>& axis, const U& u) {
|
||||
axis::visit([&](const auto& a) { linearize_value(out, a, u); }, axis);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void linearize_index(optional_index& out, const T& axis, const int j) {
|
||||
const auto extend = axis::traits::extend(axis);
|
||||
const auto opt = axis::traits::options(axis);
|
||||
linearize(out, extend, j + (opt & axis::option_type::underflow));
|
||||
}
|
||||
|
||||
// special case: if histogram::operator()(tuple(1, 2)) is called on 1d histogram with axis
|
||||
// that accepts 2d tuple, this should not fail
|
||||
// - solution is to forward tuples of size > 1 directly to axis for 1d histograms
|
||||
// - 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)
|
||||
template <unsigned Offset, unsigned N, typename T, typename U>
|
||||
optional_index args_to_index(const std::tuple<T>& axes, const U& args) {
|
||||
optional_index idx;
|
||||
if (N > 1) {
|
||||
linearize_value(idx, std::get<0>(axes), sub_tuple<Offset, N>(args));
|
||||
} else {
|
||||
linearize_value(idx, std::get<0>(axes), std::get<Offset>(args));
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
template <unsigned Offset, unsigned N, typename T0, typename T1, typename... Ts,
|
||||
typename U>
|
||||
optional_index args_to_index(const std::tuple<T0, T1, Ts...>& axes, const U& args) {
|
||||
static_assert(sizeof...(Ts) + 2 == N, "number of arguments != histogram rank");
|
||||
optional_index idx;
|
||||
mp11::mp_for_each<mp11::mp_iota_c<N>>([&](auto I) {
|
||||
linearize_value(idx, std::get<I>(axes), std::get<(Offset + I)>(args));
|
||||
});
|
||||
return idx;
|
||||
}
|
||||
|
||||
// overload for dynamic axes
|
||||
template <unsigned Offset, unsigned N, typename T, typename U>
|
||||
optional_index args_to_index(const T& axes, const U& args) {
|
||||
const unsigned m = axes.size();
|
||||
optional_index idx;
|
||||
if (m == 1 && N > 1)
|
||||
linearize_value(idx, axes[0], sub_tuple<Offset, N>(args));
|
||||
else {
|
||||
if (m != N)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("number of arguments != histogram rank"));
|
||||
mp11::mp_for_each<mp11::mp_iota_c<N>>(
|
||||
[&](auto I) { linearize_value(idx, axes[I], std::get<(Offset + I)>(args)); });
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr std::pair<int, int> weight_sample_indices() {
|
||||
if (is_weight<U>::value) return std::make_pair(0, -1);
|
||||
if (is_sample<U>::value) return std::make_pair(-1, 0);
|
||||
return std::make_pair(-1, -1);
|
||||
}
|
||||
|
||||
template <typename U0, typename U1, typename... Us>
|
||||
constexpr std::pair<int, int> weight_sample_indices() {
|
||||
using L = mp11::mp_list<U0, U1, Us...>;
|
||||
const int n = sizeof...(Us) + 1;
|
||||
if (is_weight<mp11::mp_at_c<L, 0>>::value) {
|
||||
if (is_sample<mp11::mp_at_c<L, 1>>::value) return std::make_pair(0, 1);
|
||||
if (is_sample<mp11::mp_at_c<L, n>>::value) return std::make_pair(0, n);
|
||||
return std::make_pair(0, -1);
|
||||
}
|
||||
if (is_sample<mp11::mp_at_c<L, 0>>::value) {
|
||||
if (is_weight<mp11::mp_at_c<L, 1>>::value) return std::make_pair(1, 0);
|
||||
if (is_weight<mp11::mp_at_c<L, n>>::value) return std::make_pair(n, 0);
|
||||
return std::make_pair(-1, 0);
|
||||
}
|
||||
if (is_weight<mp11::mp_at_c<L, n>>::value) {
|
||||
// 0, n already covered
|
||||
if (is_sample<mp11::mp_at_c<L, (n - 1)>>::value) return std::make_pair(n, n - 1);
|
||||
return std::make_pair(n, -1);
|
||||
}
|
||||
if (is_sample<mp11::mp_at_c<L, n>>::value) {
|
||||
// n, 0 already covered
|
||||
if (is_weight<mp11::mp_at_c<L, (n - 1)>>::value) return std::make_pair(n - 1, n);
|
||||
return std::make_pair(-1, n);
|
||||
}
|
||||
return std::make_pair(-1, -1);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void fill_storage_impl(mp11::mp_int<-1>, mp11::mp_int<-1>, T&& t, U&&) {
|
||||
static_if<is_incrementable<T>>([](auto&& t) { ++t; }, [](auto&& t) { t(); },
|
||||
std::forward<T>(t));
|
||||
}
|
||||
|
||||
template <class IW, class T, class U>
|
||||
void fill_storage_impl(IW, mp11::mp_int<-1>, T&& t, U&& args) {
|
||||
static_if<is_incrementable<T>>(
|
||||
[](auto&& t, const auto& w) { t += w; },
|
||||
[](auto&& t, const auto& w) {
|
||||
#ifdef BOOST_HISTOGRAM_WITH_ACCUMULATORS_SUPPORT
|
||||
static_if<is_accumulator_set<unqual<T>>>(
|
||||
[w](auto&& t) { t(::boost::accumulators::weight = w); },
|
||||
[w](auto&& t) { t(w); }, t);
|
||||
#else
|
||||
t(w);
|
||||
#endif
|
||||
},
|
||||
std::forward<T>(t), std::get<IW::value>(args).value);
|
||||
}
|
||||
|
||||
template <class IS, class T, class U>
|
||||
void fill_storage_impl(mp11::mp_int<-1>, IS, T&& t, U&& args) {
|
||||
mp11::tuple_apply([&t](auto&&... args) { t(args...); },
|
||||
std::get<IS::value>(args).value);
|
||||
}
|
||||
|
||||
template <class IW, class IS, class T, class U>
|
||||
void fill_storage_impl(IW, IS, T&& t, U&& args) {
|
||||
#ifdef BOOST_HISTOGRAM_WITH_ACCUMULATORS_SUPPORT
|
||||
static_if<is_accumulator_set<unqual<T>>>(
|
||||
[](auto&& t, const auto& w, const auto& s) {
|
||||
mp11::tuple_apply(
|
||||
[&](auto&&... args) { t(args..., ::boost::accumulators::weight = w); }, s);
|
||||
},
|
||||
[](auto&& t, const auto& w, const auto& s) {
|
||||
mp11::tuple_apply([&](auto&&... args) { t(w, args...); }, s);
|
||||
},
|
||||
std::forward<T>(t), std::get<IW::value>(args).value,
|
||||
std::get<IS::value>(args).value);
|
||||
#else
|
||||
mp11::tuple_apply(
|
||||
[&](auto&&... args2) { t(std::get<IW::value>(args).value, args2...); },
|
||||
std::get<IS::value>(args).value);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename... Us>
|
||||
void fill_impl(S& storage, const T& axes, const std::tuple<Us...>& args) {
|
||||
constexpr std::pair<int, int> iws = weight_sample_indices<Us...>();
|
||||
constexpr unsigned n = sizeof...(Us) - (iws.first > -1) - (iws.second > -1);
|
||||
constexpr unsigned offset = (iws.first == 0 || iws.second == 0)
|
||||
? (iws.first == 1 || iws.second == 1 ? 2 : 1)
|
||||
: 0;
|
||||
optional_index idx = args_to_index<offset, n>(axes, args);
|
||||
if (idx) {
|
||||
fill_storage_impl(mp11::mp_int<iws.first>(), mp11::mp_int<iws.second>(),
|
||||
storage[*idx], args);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename A, typename... Us>
|
||||
optional_index at_impl(const A& axes, const std::tuple<Us...>& args) {
|
||||
if (axes_size(axes) != sizeof...(Us))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("number of arguments != histogram rank"));
|
||||
optional_index idx;
|
||||
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Us)>>([&](auto I) {
|
||||
linearize_index(idx, axis_get<I>(axes), static_cast<int>(std::get<I>(args)));
|
||||
});
|
||||
return idx;
|
||||
}
|
||||
|
||||
template <typename A, typename U>
|
||||
optional_index at_impl(const A& axes, const U& args) {
|
||||
if (axes_size(axes) != args.size())
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("number of arguments != histogram rank"));
|
||||
optional_index idx;
|
||||
using std::begin;
|
||||
auto it = begin(args);
|
||||
for_each_axis(axes,
|
||||
[&](const auto& a) { linearize_index(idx, a, static_cast<int>(*it++)); });
|
||||
return idx;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
289
include/boost/histogram/detail/linearize.hpp
Normal file
289
include/boost/histogram/detail/linearize.hpp
Normal file
@@ -0,0 +1,289 @@
|
||||
// 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_LINEARIZE_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_LINEARIZE_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/histogram/axis/traits.hpp>
|
||||
#include <boost/histogram/axis/variant.hpp>
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/histogram/unsafe_access.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#ifdef BOOST_HISTOGRAM_WITH_ACCUMULATORS_SUPPORT
|
||||
#include <boost/accumulators/accumulators.hpp>
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
struct is_accumulator_set : std::false_type {};
|
||||
|
||||
#ifdef BOOST_HISTOGRAM_WITH_ACCUMULATORS_SUPPORT
|
||||
template <class... Ts>
|
||||
struct is_accumulator_set<::boost::accumulators::accumulator_set<Ts...>>
|
||||
: std::true_type {};
|
||||
#endif
|
||||
|
||||
/// Index with an invalid state
|
||||
struct optional_index {
|
||||
std::size_t idx = 0;
|
||||
std::size_t stride = 1;
|
||||
operator bool() const { return stride > 0; }
|
||||
std::size_t operator*() const { return idx; }
|
||||
};
|
||||
|
||||
inline void linearize(optional_index& out, const int axis_shape, int j) noexcept {
|
||||
// j is internal index, shifted by +1 wrt external index if axis has underflow bin
|
||||
out.idx += j * out.stride;
|
||||
// set stride to 0, if j is invalid
|
||||
out.stride *= (0 <= j && j < axis_shape) * axis_shape;
|
||||
}
|
||||
|
||||
template <class A, class V>
|
||||
void linearize_value(optional_index& out, int& shift, A& axis, const V& value) {
|
||||
using T = arg_type<A>;
|
||||
// turn instantiation with wrong template argument into runtime error
|
||||
static_if<std::is_convertible<V, T>>(
|
||||
[&](auto& axis, const V& value) {
|
||||
const auto opt = axis::traits::options(axis);
|
||||
const auto j = static_if<has_method_update<naked<decltype(axis)>>>(
|
||||
[&shift](auto& axis, const V& value) {
|
||||
int j;
|
||||
std::tie(j, shift) = axis.update(value);
|
||||
return j;
|
||||
},
|
||||
[](const auto& axis, const V& value) { return axis(value); },
|
||||
axis, value) +
|
||||
(opt & axis::option_type::underflow);
|
||||
linearize(out, axis::traits::extend(axis), j);
|
||||
},
|
||||
[](A&, const V&) {
|
||||
using T = arg_type<A>;
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument(
|
||||
detail::cat(boost::core::demangled_name(BOOST_CORE_TYPEID(A)),
|
||||
": cannot convert argument of type ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(V)), " to ",
|
||||
boost::core::demangled_name(BOOST_CORE_TYPEID(T)))));
|
||||
},
|
||||
axis, value);
|
||||
}
|
||||
|
||||
template <class... Ts, class V>
|
||||
void linearize_value(optional_index& out, int& s, axis::variant<Ts...>& axis,
|
||||
const V& v) {
|
||||
axis::visit([&](auto& a) { linearize_value(out, s, a, v); }, axis);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void linearize_index(optional_index& out, const T& axis, const int j) {
|
||||
const auto extend = axis::traits::extend(axis);
|
||||
const auto opt = axis::traits::options(axis);
|
||||
linearize(out, extend, j + (opt & axis::option_type::underflow));
|
||||
}
|
||||
|
||||
template <class S, class A, class T>
|
||||
void maybe_replace_storage(S& storage, const A& axes, const T& shifts) {
|
||||
bool update_needed = false;
|
||||
for (int s : shifts) update_needed |= s != 0;
|
||||
if (!update_needed) return;
|
||||
struct item {
|
||||
int idx, size;
|
||||
std::size_t stride;
|
||||
};
|
||||
auto data = make_stack_buffer<item>(axes);
|
||||
auto sit = shifts.begin();
|
||||
auto dit = data.begin();
|
||||
std::size_t s = 1;
|
||||
for_each_axis(axes, [&](const auto& a) {
|
||||
const auto n = axis::traits::extend(a);
|
||||
*dit++ = {0, n, s};
|
||||
s *= n - std::abs(*sit++);
|
||||
});
|
||||
auto new_storage = make_default(storage);
|
||||
new_storage.reset(detail::bincount(axes));
|
||||
for (const auto& x : storage) {
|
||||
auto ns = new_storage.begin();
|
||||
sit = shifts.begin();
|
||||
for (const auto& d : data) { ns += (d.idx + std::max(*sit++, 9)) * d.stride; }
|
||||
auto dit = data.begin();
|
||||
const auto last = data.end() - 1;
|
||||
++dit->idx;
|
||||
while (dit != last && dit->idx == dit->size) {
|
||||
dit->idx = 0;
|
||||
++(++dit)->idx;
|
||||
}
|
||||
*ns = x;
|
||||
}
|
||||
storage = std::move(new_storage);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct size_or_zero : mp11::mp_size_t<0> {};
|
||||
|
||||
template <class... Ts>
|
||||
struct size_or_zero<std::tuple<Ts...>> : mp11::mp_size_t<sizeof...(Ts)> {};
|
||||
|
||||
// special case: if histogram::operator()(tuple(1, 2)) is called on 1d histogram with axis
|
||||
// that accepts 2d tuple, this should not fail
|
||||
// - solution is to forward tuples of size > 1 directly to axis for 1d histograms
|
||||
// - 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)
|
||||
template <unsigned I, unsigned N, class S, class T, class U>
|
||||
optional_index args_to_index(S& storage, T& axes, const U& args) {
|
||||
optional_index idx;
|
||||
auto shifts = make_stack_buffer<int>(axes);
|
||||
const auto rank = get_size(axes);
|
||||
if (rank == 1 && N > 1)
|
||||
linearize_value(idx, shifts[0], axis_get<0>(axes), tuple_slice<I, N>(args));
|
||||
else {
|
||||
if (rank != N)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument("number of arguments != histogram rank"));
|
||||
constexpr unsigned M = size_or_zero<naked<decltype(axes)>>::value;
|
||||
mp11::mp_for_each<mp11::mp_iota_c<(M == 0 ? N : M)>>([&](auto J) {
|
||||
linearize_value(idx, shifts[J], axis_get<J>(axes), std::get<(J + I)>(args));
|
||||
});
|
||||
}
|
||||
maybe_replace_storage(storage, axes, shifts);
|
||||
return idx;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
constexpr auto weight_sample_indices() {
|
||||
if (is_weight<U>::value) return std::make_pair(0, -1);
|
||||
if (is_sample<U>::value) return std::make_pair(-1, 0);
|
||||
return std::make_pair(-1, -1);
|
||||
}
|
||||
|
||||
template <typename U0, typename U1, typename... Us>
|
||||
constexpr auto weight_sample_indices() {
|
||||
using L = mp11::mp_list<U0, U1, Us...>;
|
||||
const int n = sizeof...(Us) + 1;
|
||||
if (is_weight<mp11::mp_at_c<L, 0>>::value) {
|
||||
if (is_sample<mp11::mp_at_c<L, 1>>::value) return std::make_pair(0, 1);
|
||||
if (is_sample<mp11::mp_at_c<L, n>>::value) return std::make_pair(0, n);
|
||||
return std::make_pair(0, -1);
|
||||
}
|
||||
if (is_sample<mp11::mp_at_c<L, 0>>::value) {
|
||||
if (is_weight<mp11::mp_at_c<L, 1>>::value) return std::make_pair(1, 0);
|
||||
if (is_weight<mp11::mp_at_c<L, n>>::value) return std::make_pair(n, 0);
|
||||
return std::make_pair(-1, 0);
|
||||
}
|
||||
if (is_weight<mp11::mp_at_c<L, n>>::value) {
|
||||
// 0, n already covered
|
||||
if (is_sample<mp11::mp_at_c<L, (n - 1)>>::value) return std::make_pair(n, n - 1);
|
||||
return std::make_pair(n, -1);
|
||||
}
|
||||
if (is_sample<mp11::mp_at_c<L, n>>::value) {
|
||||
// n, 0 already covered
|
||||
if (is_weight<mp11::mp_at_c<L, (n - 1)>>::value) return std::make_pair(n - 1, n);
|
||||
return std::make_pair(-1, n);
|
||||
}
|
||||
return std::make_pair(-1, -1);
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
void fill_storage_impl(mp11::mp_int<-1>, mp11::mp_int<-1>, T&& t, U&&) {
|
||||
static_if<is_incrementable<naked<T>>>([](auto&& t) { ++t; }, [](auto&& t) { t(); },
|
||||
std::forward<T>(t));
|
||||
}
|
||||
|
||||
template <class IW, class T, class U>
|
||||
void fill_storage_impl(IW, mp11::mp_int<-1>, T&& t, U&& args) {
|
||||
static_if<is_incrementable<naked<T>>>(
|
||||
[](auto&& t, const auto& w) { t += w; },
|
||||
[](auto&& t, const auto& w) {
|
||||
#ifdef BOOST_HISTOGRAM_WITH_ACCUMULATORS_SUPPORT
|
||||
static_if<is_accumulator_set<naked<T>>>(
|
||||
[w](auto&& t) { t(::boost::accumulators::weight = w); },
|
||||
[w](auto&& t) { t(w); }, t);
|
||||
#else
|
||||
t(w);
|
||||
#endif
|
||||
},
|
||||
std::forward<T>(t), std::get<IW::value>(args).value);
|
||||
}
|
||||
|
||||
template <class IS, class T, class U>
|
||||
void fill_storage_impl(mp11::mp_int<-1>, IS, T&& t, U&& args) {
|
||||
mp11::tuple_apply([&t](auto&&... args) { t(args...); },
|
||||
std::get<IS::value>(args).value);
|
||||
}
|
||||
|
||||
template <class IW, class IS, class T, class U>
|
||||
void fill_storage_impl(IW, IS, T&& t, U&& args) {
|
||||
#ifdef BOOST_HISTOGRAM_WITH_ACCUMULATORS_SUPPORT
|
||||
static_if<is_accumulator_set<naked<T>>>(
|
||||
[](auto&& t, const auto& w, const auto& s) {
|
||||
mp11::tuple_apply(
|
||||
[&](auto&&... args) { t(args..., ::boost::accumulators::weight = w); }, s);
|
||||
},
|
||||
[](auto&& t, const auto& w, const auto& s) {
|
||||
mp11::tuple_apply([&](auto&&... args) { t(w, args...); }, s);
|
||||
},
|
||||
std::forward<T>(t), std::get<IW::value>(args).value,
|
||||
std::get<IS::value>(args).value);
|
||||
#else
|
||||
mp11::tuple_apply(
|
||||
[&](auto&&... args2) { t(std::get<IW::value>(args).value, args2...); },
|
||||
std::get<IS::value>(args).value);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class S, class A, class... Us>
|
||||
void fill_impl(S& storage, A& axes, const std::tuple<Us...>& args) {
|
||||
constexpr std::pair<int, int> iws = weight_sample_indices<Us...>();
|
||||
constexpr unsigned n = sizeof...(Us) - (iws.first > -1) - (iws.second > -1);
|
||||
constexpr unsigned i = (iws.first == 0 || iws.second == 0)
|
||||
? (iws.first == 1 || iws.second == 1 ? 2 : 1)
|
||||
: 0;
|
||||
optional_index idx = args_to_index<i, n>(storage, axes, args);
|
||||
if (idx) {
|
||||
fill_storage_impl(mp11::mp_int<iws.first>(), mp11::mp_int<iws.second>(),
|
||||
storage[*idx], args);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename A, typename... Us>
|
||||
optional_index at_impl(const A& axes, const std::tuple<Us...>& args) {
|
||||
if (get_size(axes) != sizeof...(Us))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("number of arguments != histogram rank"));
|
||||
optional_index idx;
|
||||
mp11::mp_for_each<mp11::mp_iota_c<sizeof...(Us)>>([&](auto I) {
|
||||
linearize_index(idx, axis_get<I>(axes), static_cast<int>(std::get<I>(args)));
|
||||
});
|
||||
return idx;
|
||||
}
|
||||
|
||||
template <typename A, typename U>
|
||||
optional_index at_impl(const A& axes, const U& args) {
|
||||
if (get_size(axes) != args.size())
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("number of arguments != histogram rank"));
|
||||
optional_index idx;
|
||||
using std::begin;
|
||||
auto it = begin(args);
|
||||
for_each_axis(axes,
|
||||
[&](const auto& a) { linearize_index(idx, a, static_cast<int>(*it++)); });
|
||||
return idx;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
@@ -7,6 +7,18 @@
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_META_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_META_HPP
|
||||
|
||||
/* Most of the histogram code is generic and works for any number of axes. Buffers with a
|
||||
* fixed maximum capacity are used in some places, which have a size equal to the rank of
|
||||
* a histogram. The buffers are statically allocated to improve performance, which means
|
||||
* that they need a preset maximum capacity. 32 seems like a safe upper limit for the rank
|
||||
* (you can nevertheless increase it here if necessary): the simplest non-trivial axis has
|
||||
* 2 bins; even if counters are used which need only a byte of storage per bin, this still
|
||||
* corresponds to 4 GB of storage.
|
||||
*/
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_AXES_LIMIT
|
||||
#define BOOST_HISTOGRAM_DETAIL_AXES_LIMIT 32
|
||||
#endif
|
||||
|
||||
#include <boost/config/workaround.hpp>
|
||||
#if BOOST_WORKAROUND(BOOST_GCC, >= 60000)
|
||||
#pragma GCC diagnostic push
|
||||
@@ -17,6 +29,7 @@
|
||||
#if BOOST_WORKAROUND(BOOST_GCC, >= 60000)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
#include <functional>
|
||||
@@ -30,16 +43,10 @@ namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
using unqual = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
using naked = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
template <class T, class U>
|
||||
using convert_integer = mp11::mp_if<std::is_integral<unqual<T>>, U, T>;
|
||||
|
||||
template <class T>
|
||||
using mp_size = mp11::mp_size<unqual<T>>;
|
||||
|
||||
template <typename T, unsigned N>
|
||||
using mp_at_c = mp11::mp_at_c<unqual<T>, N>;
|
||||
using convert_integer = mp11::mp_if<std::is_integral<naked<T>>, U, T>;
|
||||
|
||||
template <template <class> class F, typename T, typename E>
|
||||
using mp_eval_or = mp11::mp_eval_if_c<!(mp11::mp_valid<F, T>::value), E, F, T>;
|
||||
@@ -56,12 +63,11 @@ template <typename S, typename L>
|
||||
using mp_set_union = mp11::mp_apply_q<mp11::mp_bind_front<mp11::mp_set_push_back, S>, L>;
|
||||
|
||||
template <typename L>
|
||||
using mp_last = mp11::mp_at_c<L, (mp_size<L>::value - 1)>;
|
||||
using mp_last = mp11::mp_at_c<L, (mp11::mp_size<L>::value - 1)>;
|
||||
|
||||
template <typename T>
|
||||
using args_type = mp11::mp_if<std::is_member_function_pointer<T>,
|
||||
mp11::mp_pop_front<boost::callable_traits::args_t<T>>,
|
||||
boost::callable_traits::args_t<T>>;
|
||||
template <class T, class Args = boost::callable_traits::args_t<T>>
|
||||
using args_type =
|
||||
mp11::mp_if<std::is_member_function_pointer<T>, mp11::mp_pop_front<Args>, Args>;
|
||||
|
||||
template <typename T, std::size_t N = 0>
|
||||
using arg_type = typename mp11::mp_at_c<args_type<T>, N>;
|
||||
@@ -71,9 +77,8 @@ using return_type = typename boost::callable_traits::return_type<T>::type;
|
||||
|
||||
template <typename V>
|
||||
struct variant_first_arg_qualified_impl {
|
||||
using UV = unqual<V>;
|
||||
using T0 = mp11::mp_first<UV>;
|
||||
using type = copy_qualifiers<V, unqual<T0>>;
|
||||
using T0 = mp11::mp_first<naked<V>>;
|
||||
using type = copy_qualifiers<V, naked<T0>>;
|
||||
};
|
||||
|
||||
template <typename F, typename V,
|
||||
@@ -121,20 +126,15 @@ constexpr float highest() {
|
||||
return std::numeric_limits<float>::infinity();
|
||||
}
|
||||
|
||||
template <class... Ns>
|
||||
struct sub_tuple_impl {
|
||||
template <typename T>
|
||||
static decltype(auto) apply(const T& t) {
|
||||
return std::forward_as_tuple(std::get<Ns::value>(t)...);
|
||||
}
|
||||
};
|
||||
template <std::size_t I, class T, std::size_t... N>
|
||||
decltype(auto) tuple_slice_impl(T&& t, mp11::index_sequence<N...>) {
|
||||
return std::forward_as_tuple(std::get<(N + I)>(std::forward<T>(t))...);
|
||||
}
|
||||
|
||||
template <std::size_t Offset, std::size_t N, typename T>
|
||||
decltype(auto) sub_tuple(const T& t) {
|
||||
using LN = mp11::mp_iota_c<N>;
|
||||
using OffsetAdder = mp11::mp_bind_front<mp11::mp_plus, mp11::mp_size_t<Offset>>;
|
||||
using LN2 = mp11::mp_transform_q<OffsetAdder, LN>;
|
||||
return mp11::mp_rename<LN2, sub_tuple_impl>::apply(t);
|
||||
template <std::size_t I, std::size_t N, class T>
|
||||
decltype(auto) tuple_slice(T&& t) {
|
||||
static_assert(I + N <= mp11::mp_size<naked<T>>::value, "I and N must describe a slice");
|
||||
return tuple_slice_impl<I>(std::forward<T>(t), mp11::make_index_sequence<N>{});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -180,6 +180,8 @@ BOOST_HISTOGRAM_MAKE_SFINAE(has_method_lower, &T::lower);
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_value, &T::value);
|
||||
|
||||
BOOST_HISTOGRAM_MAKE_SFINAE(has_method_update, &T::update);
|
||||
|
||||
template <typename T>
|
||||
using get_value_method_return_type_impl = decltype(std::declval<T&>().value(0));
|
||||
|
||||
@@ -274,7 +276,7 @@ template <typename T>
|
||||
struct is_weight_impl<weight_type<T>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
using is_weight = is_weight_impl<unqual<T>>;
|
||||
using is_weight = is_weight_impl<naked<T>>;
|
||||
|
||||
template <typename T>
|
||||
struct is_sample_impl : std::false_type {};
|
||||
@@ -283,7 +285,7 @@ template <typename T>
|
||||
struct is_sample_impl<sample_type<T>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
using is_sample = is_sample_impl<unqual<T>>;
|
||||
using is_sample = is_sample_impl<naked<T>>;
|
||||
|
||||
// poor-mans concept checks
|
||||
template <class B>
|
||||
@@ -292,40 +294,67 @@ using requires = std::enable_if_t<B::value>;
|
||||
template <class T, class = decltype(*std::declval<T&>(), ++std::declval<T&>())>
|
||||
struct requires_iterator {};
|
||||
|
||||
template <class T, class = requires<is_iterable<unqual<T>>>>
|
||||
template <class T, class = requires<is_iterable<naked<T>>>>
|
||||
struct requires_iterable {};
|
||||
|
||||
template <class T, class = requires<is_axis<unqual<T>>>>
|
||||
template <class T, class = requires<is_axis<naked<T>>>>
|
||||
struct requires_axis {};
|
||||
|
||||
template <class T, class = requires<is_any_axis<unqual<T>>>>
|
||||
template <class T, class = requires<is_any_axis<naked<T>>>>
|
||||
struct requires_any_axis {};
|
||||
|
||||
template <class T, class = requires<is_sequence_of_axis<unqual<T>>>>
|
||||
template <class T, class = requires<is_sequence_of_axis<naked<T>>>>
|
||||
struct requires_sequence_of_axis {};
|
||||
|
||||
template <class T, class = requires<is_sequence_of_axis_variant<unqual<T>>>>
|
||||
template <class T, class = requires<is_sequence_of_axis_variant<naked<T>>>>
|
||||
struct requires_sequence_of_axis_variant {};
|
||||
|
||||
template <class T, class = requires<is_sequence_of_any_axis<unqual<T>>>>
|
||||
template <class T, class = requires<is_sequence_of_any_axis<naked<T>>>>
|
||||
struct requires_sequence_of_any_axis {};
|
||||
|
||||
template <class T, class = requires<is_any_axis<mp11::mp_first<unqual<T>>>>>
|
||||
template <class T, class = requires<is_any_axis<mp11::mp_first<naked<T>>>>>
|
||||
struct requires_axes {};
|
||||
|
||||
template <class T, class U, class = requires<std::is_convertible<T, U>>>
|
||||
struct requires_convertible {};
|
||||
|
||||
template <typename T>
|
||||
template <class T>
|
||||
auto make_default(const T& t) {
|
||||
using U = unqual<T>;
|
||||
return static_if<has_allocator<U>>([](const auto& t) { return U(t.get_allocator()); },
|
||||
[](const auto&) { return U(); }, t);
|
||||
return static_if<has_allocator<T>>([](const auto& t) { return T(t.get_allocator()); },
|
||||
[](const auto&) { return T(); }, t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
using get_tuple_size = typename std::tuple_size<T>::type;
|
||||
|
||||
template <class T>
|
||||
std::size_t get_size_impl(std::true_type, const T&) noexcept {
|
||||
return get_tuple_size<T>::value;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::size_t get_size_impl(std::false_type, const T& t) noexcept {
|
||||
return t.size();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::size_t get_size(const T& t) noexcept {
|
||||
return get_size_impl(mp11::mp_valid<get_tuple_size, T>(), t);
|
||||
}
|
||||
|
||||
template <class U, class T>
|
||||
using stack_buffer = boost::container::static_vector<
|
||||
U, mp_eval_or<get_tuple_size, T,
|
||||
mp11::mp_size_t<BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>>::value>;
|
||||
|
||||
template <class U, class T, class... Ts>
|
||||
auto make_stack_buffer(const T& t, Ts... ts) {
|
||||
return stack_buffer<U, T>(get_size(t), std::forward<Ts>(ts)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool relaxed_equal(const T& a, const T& b) noexcept {
|
||||
return static_if<has_operator_equal<unqual<T>>>(
|
||||
return static_if<has_operator_equal<T>>(
|
||||
[](const auto& a, const auto& b) { return a == b; },
|
||||
[](const auto&, const auto&) { return true; }, a, b);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <boost/histogram/detail/axes.hpp>
|
||||
#include <boost/histogram/detail/common_type.hpp>
|
||||
#include <boost/histogram/detail/linearize.hpp>
|
||||
#include <boost/histogram/detail/meta.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
@@ -74,7 +75,7 @@ public:
|
||||
explicit histogram(A&& a) : histogram(std::forward<A>(a), storage_type()) {}
|
||||
|
||||
/// Number of axes (dimensions)
|
||||
unsigned rank() const noexcept { return detail::axes_size(axes_); }
|
||||
unsigned rank() const noexcept { return detail::get_size(axes_); }
|
||||
|
||||
/// Total number of bins (including underflow/overflow)
|
||||
std::size_t size() const noexcept { return storage_.size(); }
|
||||
@@ -85,14 +86,14 @@ public:
|
||||
/// Get N-th axis
|
||||
template <unsigned N>
|
||||
decltype(auto) axis(std::integral_constant<unsigned, N>) {
|
||||
detail::rank_check(axes_, N);
|
||||
detail::axis_index_is_valid(axes_, N);
|
||||
return detail::axis_get<N>(axes_);
|
||||
}
|
||||
|
||||
/// Get N-th axis (const version)
|
||||
template <unsigned N>
|
||||
decltype(auto) axis(std::integral_constant<unsigned, N>) const {
|
||||
detail::rank_check(axes_, N);
|
||||
detail::axis_index_is_valid(axes_, N);
|
||||
return detail::axis_get<N>(axes_);
|
||||
}
|
||||
|
||||
@@ -104,13 +105,13 @@ public:
|
||||
|
||||
/// Get N-th axis with runtime index
|
||||
decltype(auto) axis(unsigned i) {
|
||||
detail::rank_check(axes_, i);
|
||||
detail::axis_index_is_valid(axes_, i);
|
||||
return detail::axis_get(axes_, i);
|
||||
}
|
||||
|
||||
/// Get N-th axis with runtime index (const version)
|
||||
decltype(auto) axis(unsigned i) const {
|
||||
detail::rank_check(axes_, i);
|
||||
detail::axis_index_is_valid(axes_, i);
|
||||
return detail::axis_get(axes_, i);
|
||||
}
|
||||
|
||||
@@ -266,11 +267,11 @@ auto operator+(const histogram<A, S>& a, histogram<A, S>&& b) {
|
||||
#if __cpp_deduction_guides >= 201606
|
||||
|
||||
template <class Axes>
|
||||
histogram(Axes&& axes)->histogram<detail::unqual<Axes>, default_storage>;
|
||||
histogram(Axes&& axes)->histogram<detail::naked<Axes>, default_storage>;
|
||||
|
||||
template <class Axes, class Storage>
|
||||
histogram(Axes&& axes, Storage&& storage)
|
||||
->histogram<detail::unqual<Axes>, detail::unqual<Storage>>;
|
||||
->histogram<detail::naked<Axes>, detail::naked<Storage>>;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -23,13 +23,14 @@ namespace histogram {
|
||||
/// Range over histogram bins with multi-dimensional index.
|
||||
template <class Histogram>
|
||||
class BOOST_HISTOGRAM_NODISCARD indexed_range {
|
||||
using value_type = typename detail::unqual<Histogram>::value_type;
|
||||
using value_iterator = detail::unqual<decltype(std::declval<Histogram&>().begin())>;
|
||||
using histogram_iterator =
|
||||
mp11::mp_if<std::is_const<Histogram>, typename Histogram::const_iterator,
|
||||
typename Histogram::iterator>;
|
||||
struct cache_item {
|
||||
int idx, begin, end, extend;
|
||||
};
|
||||
using cache_type =
|
||||
detail::axes_buffer<typename detail::unqual<Histogram>::axes_type, cache_item>;
|
||||
detail::stack_buffer<cache_item, typename detail::naked<Histogram>::axes_type>;
|
||||
|
||||
public:
|
||||
class accessor {
|
||||
@@ -65,21 +66,21 @@ public:
|
||||
return *iter_ / x;
|
||||
}
|
||||
|
||||
accessor(indexed_range* parent, value_iterator i) : parent_(parent), iter_(i) {}
|
||||
accessor(indexed_range* parent, histogram_iterator i) : parent_(parent), iter_(i) {}
|
||||
|
||||
decltype(auto) operator*() const noexcept { return *iter_; }
|
||||
auto operator-> () const noexcept { return iter_; }
|
||||
|
||||
private:
|
||||
indexed_range* parent_;
|
||||
value_iterator iter_;
|
||||
histogram_iterator iter_;
|
||||
};
|
||||
|
||||
class range_iterator
|
||||
: public boost::iterator_adaptor<range_iterator, value_iterator, accessor,
|
||||
: public boost::iterator_adaptor<range_iterator, histogram_iterator, accessor,
|
||||
boost::forward_traversal_tag, accessor> {
|
||||
public:
|
||||
range_iterator(indexed_range* p, value_iterator i) noexcept
|
||||
range_iterator(indexed_range* p, histogram_iterator i) noexcept
|
||||
: range_iterator::iterator_adaptor_(i), parent_(p) {}
|
||||
|
||||
accessor operator*() const noexcept { return {parent_, range_iterator::base()}; }
|
||||
@@ -94,7 +95,7 @@ public:
|
||||
++range_iterator::base_reference();
|
||||
while (c->idx == c->end && ((c + 1) != parent_->cache_.end())) {
|
||||
c->idx = c->begin;
|
||||
range_iterator::base_reference() -= (c->end - c->idx) * stride;
|
||||
range_iterator::base_reference() -= (c->end - c->begin) * stride;
|
||||
stride *= c->extend;
|
||||
++c;
|
||||
++c->idx;
|
||||
@@ -140,7 +141,7 @@ public:
|
||||
private:
|
||||
Histogram& hist_;
|
||||
const bool include_extra_bins_;
|
||||
value_iterator begin_, end_;
|
||||
histogram_iterator begin_, end_;
|
||||
mutable cache_type cache_;
|
||||
}; // namespace histogram
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ template <typename StorageOrContainer, typename T, typename... Ts,
|
||||
typename = detail::requires_axis<T>>
|
||||
auto make_histogram_with(StorageOrContainer&& s, T&& axis0, Ts&&... axis) {
|
||||
auto axes = std::make_tuple(std::forward<T>(axis0), std::forward<Ts>(axis)...);
|
||||
using U = detail::unqual<StorageOrContainer>;
|
||||
using U = detail::naked<StorageOrContainer>;
|
||||
using S = mp11::mp_if<detail::is_storage<U>, U, storage_adaptor<U>>;
|
||||
return histogram<decltype(axes), S>(std::move(axes),
|
||||
S(std::forward<StorageOrContainer>(s)));
|
||||
@@ -48,9 +48,9 @@ auto make_weighted_histogram(T&& axis0, Ts&&... axis) {
|
||||
template <typename StorageOrContainer, typename Iterable,
|
||||
typename = detail::requires_sequence_of_any_axis<Iterable>>
|
||||
auto make_histogram_with(StorageOrContainer&& s, Iterable&& c) {
|
||||
using U = detail::unqual<StorageOrContainer>;
|
||||
using U = detail::naked<StorageOrContainer>;
|
||||
using S = mp11::mp_if<detail::is_storage<U>, U, storage_adaptor<U>>;
|
||||
using It = detail::unqual<Iterable>;
|
||||
using It = detail::naked<Iterable>;
|
||||
using A = mp11::mp_if<detail::is_indexable_container<It>, It,
|
||||
boost::container::vector<mp11::mp_first<It>>>;
|
||||
return histogram<A, S>(std::forward<Iterable>(c),
|
||||
@@ -73,7 +73,7 @@ auto make_weighted_histogram(Iterable&& c) {
|
||||
template <typename StorageOrContainer, typename Iterator,
|
||||
typename = detail::requires_iterator<Iterator>>
|
||||
auto make_histogram_with(StorageOrContainer&& s, Iterator begin, Iterator end) {
|
||||
using T = detail::unqual<decltype(*begin)>;
|
||||
using T = detail::naked<decltype(*begin)>;
|
||||
return make_histogram_with(std::forward<StorageOrContainer>(s),
|
||||
boost::container::vector<T>(begin, end));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user