/usr/include/boost/histogram/detail
Edit: /usr/include/boost/histogram/detail/fill.hpp (12609B)
// 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace boost {
namespace histogram {
namespace detail {
template
struct sample_args_passed_vs_expected;
template
struct sample_args_passed_vs_expected, std::tuple> {
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>::value,
"error: sample argument(s) not convertible to accumulator argument(s)");
};
template
struct storage_grower {
const A& axes_;
struct {
axis::index_type idx, old_extent;
std::size_t new_stride;
} data_[buffer_size::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
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 (const 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>;
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
auto fill_storage_element_impl(priority<2>, T&& t, const Us&... args) noexcept
-> decltype(t(args...), void()) {
t(args...);
}
template
auto fill_storage_element_impl(priority<1>, T&& t, const weight_type& w) noexcept
-> decltype(t += w, void()) {
t += w;
}
// fallback for arithmetic types and accumulators that do not handle the weight
template
auto fill_storage_element_impl(priority<0>, T&& t, const weight_type& w) noexcept
-> decltype(t += w.value, void()) {
t += w.value;
}
template
auto fill_storage_element_impl(priority<1>, T&& t) noexcept -> decltype(++t, void()) {
++t;
}
template
void fill_storage_element(T&& t, const Us&... args) noexcept {
fill_storage_element_impl(priority<2>{}, std::forward(t), args...);
}
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
template
void fill_storage_2(IW, IS, T&& t, U&& u) noexcept {
mp11::tuple_apply(
[&](const auto&... args) {
fill_storage_element(std::forward(t), std::get(u), args...);
},
std::get(u).value);
}
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
template
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), args...); },
std::get(u).value);
}
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
template
void fill_storage_2(IW, mp11::mp_int<-1>, T&& t, const U& u) noexcept {
fill_storage_element(std::forward(t), std::get(u));
}
// t may be a proxy and then it is an rvalue reference, not an lvalue reference
template
void fill_storage_2(mp11::mp_int<-1>, mp11::mp_int<-1>, T&& t, const U&) noexcept {
fill_storage_element(std::forward(t));
}
template
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
struct linearize_args {
template
static void impl(mp11::mp_int, Index&, const std::size_t, A&, const Args&) {}
template
static void impl(mp11::mp_int, Index& o, const std::size_t s, A& ax,
const Args& args) {
const auto e = linearize(o, s, axis_get(ax), std::get<(S + I)>(args));
impl(mp11::mp_int<(I + 1)>{}, o, s * e, ax, args);
}
template
static void apply(Index& o, A& ax, const Args& args) {
impl(mp11::mp_int<0>{}, o, 1, ax, args);
}
};
template
struct linearize_args {
template
static void apply(Index& o, A& ax, const Args& args) {
linearize(o, 1, axis_get<0>(ax), std::get(args));
}
};
template
constexpr unsigned min(const unsigned n) noexcept {
constexpr unsigned a = buffer_size::value;
return a < n ? a : n;
}
// not growing
template
auto fill_2(ArgTraits, mp11::mp_false, const std::size_t offset, Storage& st,
const Axes& axes, const Args& args) {
mp11::mp_if, optional_index, std::size_t> idx{offset};
linearize_args(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
auto fill_2(ArgTraits, mp11::mp_true, const std::size_t, Storage& st, Axes& axes,
const Args& args) {
std::array shifts;
// offset must be zero for linearize_growth
mp11::mp_if, optional_index, std::size_t> idx{0};
std::size_t stride = 1;
bool update_needed = false;
mp11::mp_for_each(ArgTraits::nargs::value)>>([&](auto i) {
auto& ax = axis_get(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 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
decltype(auto) pack_args(IW, IS, const Args& args) noexcept {
return std::make_tuple(tuple_slice(args), std::get(args),
std::get(args));
}
template
decltype(auto) pack_args(IW, mp11::mp_int<-1>, const Args& args) noexcept {
return std::make_tuple(tuple_slice(args), std::get(args));
}
template
decltype(auto) pack_args(mp11::mp_int<-1>, IS, const Args& args) noexcept {
return std::make_tuple(tuple_slice(args), std::get(args));
}
template
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
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;
// 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(
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
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