diff --git a/include/boost/histogram/algorithm/reduce.hpp b/include/boost/histogram/algorithm/reduce.hpp index eff3a5b0..8340c25b 100644 --- a/include/boost/histogram/algorithm/reduce.hpp +++ b/include/boost/histogram/algorithm/reduce.hpp @@ -25,15 +25,14 @@ namespace algorithm { struct reduce_option_type { unsigned iaxis = 0; - bool has_range = false; double lower, upper; unsigned merge = 0; reduce_option_type() noexcept = default; - reduce_option_type(unsigned i, bool b, double l, double u, unsigned m) - : iaxis(i), has_range(b), lower(l), upper(u), merge(m) { - if (has_range && l == u) throw std::invalid_argument("lower != upper required"); + 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"); } @@ -41,16 +40,17 @@ struct reduce_option_type { }; reduce_option_type shrink(unsigned iaxis, double lower, double upper) { - return {iaxis, true, lower, upper, 1}; + return {iaxis, lower, upper, 1}; } reduce_option_type shrink_and_rebin(unsigned iaxis, double lower, double upper, unsigned merge) { - return {iaxis, true, lower, upper, merge}; + return {iaxis, lower, upper, merge}; } reduce_option_type rebin(unsigned iaxis, unsigned merge) { - return {iaxis, false, 0.0, 0.0, merge}; + return {iaxis, std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), merge}; } template > @@ -100,7 +100,8 @@ histogram reduce(const histogram& h, const C& c) { 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.upper < opt.lower) { + } 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; } diff --git a/include/boost/histogram/axis/circular.hpp b/include/boost/histogram/axis/circular.hpp index dc70158f..775e50f1 100644 --- a/include/boost/histogram/axis/circular.hpp +++ b/include/boost/histogram/axis/circular.hpp @@ -57,6 +57,15 @@ public: throw std::invalid_argument("invalid phase or perimeter"); } + /// Constructor used by algorithm::reduce to shrink and rebin (not for users). + circular(const circular& src, unsigned begin, unsigned end, unsigned merge) + : base_type(src.size() / merge, src.metadata(), src.options()) + , phase_(src.phase_) + , delta_(src.delta_ * merge) { + if (!(begin == 0 && end == src.size())) + throw std::invalid_argument("cannot shrink circular axis"); + } + circular() = default; /// Returns the bin index for the passed argument. diff --git a/test/algorithm_reduce_test.cpp b/test/algorithm_reduce_test.cpp index fa5575bc..bb75dd84 100644 --- a/test/algorithm_reduce_test.cpp +++ b/test/algorithm_reduce_test.cpp @@ -7,8 +7,12 @@ #include #include #include +#include +#include +#include #include #include +#include #include #include #include "utility_histogram.hpp" @@ -116,6 +120,17 @@ void run_tests() { BOOST_TEST_THROWS(reduce(h, rebin(0, 2)), std::invalid_argument); } + // reduce on circular axis, shrink must fail, also rebin with remainder + { + auto h = make(Tag(), axis::circular<>(4, 1, 4)); + BOOST_TEST_THROWS(reduce(h, shrink(0, 0, 2)), std::invalid_argument); + BOOST_TEST_THROWS(reduce(h, rebin(0, 3)), std::invalid_argument); + auto hr = reduce(h, rebin(0, 2)); + BOOST_TEST_EQ(hr.axis().size(), 2); + BOOST_TEST_EQ(hr.axis()[0].lower(), 1); + BOOST_TEST_EQ(hr.axis()[1].upper(), 5); + } + // reduce on axis with inverted range { auto h = make(Tag(), regular(4, 2, -2)); diff --git a/test/axis_circular_test.cpp b/test/axis_circular_test.cpp index ff0dc2d3..792ea0a1 100644 --- a/test/axis_circular_test.cpp +++ b/test/axis_circular_test.cpp @@ -49,5 +49,17 @@ int main() { // iterators { test_axis_iterator(axis::circular<>(5, 0, 1, ""), 0, 5); } + // shrink and rebin + { + using A = axis::circular<>; + auto a = A(4, 1, 4); + BOOST_TEST_THROWS(A(a, 1, 4, 1), std::invalid_argument); + BOOST_TEST_THROWS(A(a, 0, 3, 1), std::invalid_argument); + auto b = A(a, 0, 4, 2); + BOOST_TEST_EQ(b.size(), 2); + BOOST_TEST_EQ(b.value(0), 1); + BOOST_TEST_EQ(b.value(2), 5); + } + return boost::report_errors(); }