mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-12 05:31:51 +00:00
149 lines
4.7 KiB
C++
149 lines
4.7 KiB
C++
// Copyright 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_ALGORITHM_REDUCE_HPP
|
|
#define BOOST_HISTOGRAM_ALGORITHM_REDUCE_HPP
|
|
|
|
#include <boost/assert.hpp>
|
|
#include <boost/histogram/detail/axes.hpp>
|
|
#include <boost/histogram/detail/index_mapper.hpp>
|
|
#include <boost/histogram/detail/meta.hpp>
|
|
#include <boost/histogram/histogram_fwd.hpp>
|
|
#include <boost/histogram/unsafe_access.hpp>
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
|
|
namespace boost {
|
|
namespace histogram {
|
|
namespace algorithm {
|
|
|
|
struct reduce_option_type {
|
|
unsigned iaxis = 0;
|
|
double lower, upper;
|
|
unsigned merge = 0;
|
|
|
|
reduce_option_type() noexcept = default;
|
|
|
|
reduce_option_type(unsigned i, double l, double u, unsigned m)
|
|
: iaxis(i), lower(l), upper(u), merge(m) {
|
|
if (lower == upper) throw std::invalid_argument("lower != upper required");
|
|
if (merge == 0) throw std::invalid_argument("merge > 0 required");
|
|
}
|
|
|
|
operator bool() const noexcept { return merge; }
|
|
};
|
|
|
|
reduce_option_type shrink_and_rebin(unsigned iaxis, double lower, double upper,
|
|
unsigned merge) {
|
|
return {iaxis, lower, upper, merge};
|
|
}
|
|
|
|
reduce_option_type shrink(unsigned iaxis, double lower, double upper) {
|
|
return {iaxis, lower, upper, 1};
|
|
}
|
|
|
|
reduce_option_type rebin(unsigned iaxis, unsigned merge) {
|
|
return {iaxis, std::numeric_limits<double>::quiet_NaN(),
|
|
std::numeric_limits<double>::quiet_NaN(), merge};
|
|
}
|
|
|
|
/// Convenience overload for single axis.
|
|
reduce_option_type shrink_and_rebin(double lower, double upper, unsigned merge) {
|
|
return shrink_and_rebin(0, lower, upper, merge);
|
|
}
|
|
|
|
/// Convenience overload for single axis.
|
|
reduce_option_type shrink(double lower, double upper) { return shrink(0, lower, upper); }
|
|
|
|
/// Convenience overload for single axis.
|
|
reduce_option_type rebin(unsigned merge) { return rebin(0, merge); }
|
|
|
|
template <typename A, typename S, typename C, typename = detail::requires_iterable<C>>
|
|
histogram<A, S> reduce(const histogram<A, S>& h, const C& c) {
|
|
auto options = detail::axes_buffer<A, reduce_option_type>(h.rank());
|
|
for (const auto& o : c) {
|
|
auto& opt_ref = options[o.iaxis];
|
|
if (opt_ref) throw std::invalid_argument("indices must be unique");
|
|
opt_ref.lower = o.lower;
|
|
opt_ref.upper = o.upper;
|
|
opt_ref.merge = o.merge;
|
|
}
|
|
|
|
auto r_axes = detail::make_empty_axes(unsafe_access::axes(h));
|
|
|
|
detail::index_mapper_reduce<A> im(h.rank());
|
|
auto im_iter = im.begin();
|
|
std::size_t stride[2] = {1, 1};
|
|
unsigned iaxis = 0;
|
|
h.for_each_axis([&](const auto& a) {
|
|
using T = detail::unqual<decltype(a)>;
|
|
|
|
const auto n = axis::traits::extend(a);
|
|
im.total *= n;
|
|
|
|
im_iter->stride[0] = stride[0];
|
|
stride[0] *= n;
|
|
im_iter->underflow[0] = axis::traits::underflow_index(a);
|
|
im_iter->overflow[0] = axis::traits::overflow_index(a);
|
|
|
|
const auto& opt = options[iaxis];
|
|
unsigned begin = 0, end = a.size(), merge = 1;
|
|
|
|
T* tp;
|
|
if (opt) {
|
|
merge = opt.merge;
|
|
if (opt.lower < opt.upper) {
|
|
while (begin != end && a.value(begin) < opt.lower) ++begin;
|
|
while (end != begin && a.value(end - 1) >= opt.upper) --end;
|
|
} else if (opt.lower > opt.upper) {
|
|
// for inverted axis::regular
|
|
while (begin != end && a.value(begin) > opt.lower) ++begin;
|
|
while (end != begin && a.value(end - 1) <= opt.upper) --end;
|
|
}
|
|
end -= (end - begin) % merge;
|
|
auto a2 = T(a, begin, end, merge);
|
|
tp = &(axis::get<T>(detail::axis_get(r_axes, iaxis)) = a2);
|
|
} else {
|
|
tp = &(axis::get<T>(detail::axis_get(r_axes, iaxis)) = a);
|
|
}
|
|
im_iter->begin = begin;
|
|
im_iter->end = end;
|
|
im_iter->merge = merge;
|
|
|
|
im_iter->stride[1] = stride[1];
|
|
stride[1] *= axis::traits::extend(*tp);
|
|
im_iter->underflow[1] = axis::traits::underflow_index(*tp);
|
|
im_iter->overflow[1] = axis::traits::overflow_index(*tp);
|
|
|
|
++im_iter;
|
|
++iaxis;
|
|
});
|
|
|
|
auto h_r = histogram<A, S>(
|
|
std::move(r_axes),
|
|
detail::static_if<detail::has_allocator<S>>(
|
|
[&h](auto) { return S(unsafe_access::storage(h).get_allocator()); },
|
|
[](auto) { return S(); }, 0));
|
|
|
|
im(unsafe_access::storage(h_r), unsafe_access::storage(h));
|
|
return h_r;
|
|
}
|
|
|
|
template <typename A, typename S, typename... Ts>
|
|
histogram<A, S> reduce(const histogram<A, S>& h, const reduce_option_type& t,
|
|
Ts&&... ts) {
|
|
// this must be in one line, because any of the ts could be a temporary
|
|
return reduce(h, std::initializer_list<reduce_option_type>{t, ts...});
|
|
}
|
|
|
|
} // namespace algorithm
|
|
} // namespace histogram
|
|
} // namespace boost
|
|
|
|
#endif
|