1111#include < boost/config.hpp> // BOOST_ATTRIBUTE_NODISCARD
1212#include < boost/histogram/axis/traits.hpp>
1313#include < boost/histogram/detail/axes.hpp>
14+ #include < boost/histogram/detail/detect.hpp>
1415#include < boost/histogram/detail/iterator_adaptor.hpp>
1516#include < boost/histogram/detail/operators.hpp>
1617#include < boost/histogram/fwd.hpp>
18+ #include < boost/histogram/unsafe_access.hpp>
1719#include < iterator>
1820#include < type_traits>
19- #include < utility>
21+ #include < utility> // std::get
2022
2123namespace boost {
2224namespace histogram {
2325
26+ namespace detail {
27+ using std::get;
28+
29+ template <std::size_t I, class T >
30+ auto get (T&& t) -> decltype(t[0 ]) {
31+ return t[I];
32+ }
33+ } // namespace detail
34+
2435/* * Coverage mode of the indexed range generator.
2536
2637 Defines options for the iteration strategy.
@@ -32,8 +43,7 @@ enum class coverage {
3243
3344/* * Input iterator range over histogram bins with multi-dimensional index.
3445
35- The iterator returned by begin() can only be incremented. begin() may only be called
36- once, calling it a second time returns the end() iterator. If several copies of the
46+ The iterator returned by begin() can only be incremented. If several copies of the
3747 input iterators exist, the other copies become invalid if one of them is incremented.
3848*/
3949template <class Histogram >
@@ -44,10 +54,13 @@ class BOOST_ATTRIBUTE_NODISCARD indexed_range {
4454 detail::buffer_size<typename std::decay_t <histogram_type>::axes_type>::value;
4555
4656public:
57+ // / implementation detail
4758 using value_iterator = std::conditional_t <std::is_const<histogram_type>::value,
4859 typename histogram_type::const_iterator,
4960 typename histogram_type::iterator>;
61+ // / implementation detail
5062 using value_reference = typename std::iterator_traits<value_iterator>::reference;
63+ // / implementation detail
5164 using value_type = typename std::iterator_traits<value_iterator>::value_type;
5265
5366 class iterator ;
@@ -304,38 +317,59 @@ class BOOST_ATTRIBUTE_NODISCARD indexed_range {
304317 };
305318
306319 indexed_range (histogram_type& hist, coverage cov)
320+ : indexed_range(hist, make_range(hist, cov)) {}
321+
322+ template <class Iterable , class = detail::requires_iterable<Iterable>>
323+ indexed_range (histogram_type& hist, Iterable&& range)
307324 : begin_(hist.begin(), hist), end_(hist.end(), hist) {
308- begin_.indices_ .hist_ ->for_each_axis ([ca = begin_.indices_ .begin (), cov,
325+ auto r_begin = std::begin (range);
326+ assert (std::distance (r_begin, std::end (range)) == hist.rank ());
327+
328+ begin_.indices_ .hist_ ->for_each_axis ([ca = begin_.indices_ .begin (), r_begin,
309329 stride = std::size_t {1 },
310330 this ](const auto & a) mutable {
311- using opt = axis::traits::get_options<std::decay_t <decltype (a)>>;
312- constexpr axis::index_type under = opt::test (axis::option::underflow);
313- constexpr axis::index_type over = opt::test (axis::option::overflow);
314331 const auto size = a.size ();
315332
316- // -1 if underflow and cover all, else 0
317- ca->begin = cov == coverage::all ? -under : 0 ;
318- // size + 1 if overflow and cover all, else size
319- ca->end = cov == coverage::all ? size + over : size;
333+ using opt = axis::traits::get_options<std::decay_t <decltype (a)>>;
334+ constexpr axis::index_type start = opt::test (axis::option::underflow) ? -1 : 0 ;
335+ const auto stop = size + (opt::test (axis::option::overflow) ? 1 : 0 );
336+
337+ ca->begin = std::max (start, detail::get<0 >(*r_begin));
338+ ca->end = std::min (stop, detail::get<1 >(*r_begin));
339+ assert (ca->begin <= ca->end );
320340 ca->idx = ca->begin ;
321341
322- // if axis has *flow and coverage::all OR axis has no *flow:
323- // begin + under == 0, size + over - end == 0
324- // if axis has *flow and coverage::inner:
325- // begin + under == 1, size + over - end == 1
326- ca->begin_skip = static_cast <std::size_t >(ca->begin + under) * stride;
327- ca->end_skip = static_cast <std::size_t >(size + over - ca->end ) * stride;
342+ ca->begin_skip = static_cast <std::size_t >(ca->begin - start) * stride;
343+ ca->end_skip = static_cast <std::size_t >(stop - ca->end ) * stride;
328344 begin_.iter_ += ca->begin_skip ;
329345
330- stride *= size + under + over;
346+ stride *= stop - start;
347+
331348 ++ca;
349+ ++r_begin;
332350 });
333351 }
334352
335353 iterator begin () noexcept { return begin_; }
336354 iterator end () noexcept { return end_; }
337355
338356private:
357+ auto make_range (histogram_type& hist, coverage cov) {
358+ using range_item = std::array<axis::index_type, 2 >;
359+ auto b = detail::make_stack_buffer<range_item>(unsafe_access::axes (hist));
360+ hist.for_each_axis ([cov, it = std::begin (b)](const auto & a) mutable {
361+ (*it)[0 ] = 0 ;
362+ (*it)[1 ] = a.size ();
363+ if (cov == coverage::all) {
364+ (*it)[0 ] -= 1 ;
365+ (*it)[1 ] += 1 ;
366+ } else
367+ assert (cov == coverage::inner);
368+ ++it;
369+ });
370+ return b;
371+ }
372+
339373 iterator begin_, end_;
340374};
341375
@@ -366,6 +400,34 @@ auto indexed(Histogram&& hist, coverage cov = coverage::inner) {
366400 cov};
367401}
368402
403+ /* * Generates and indexed range <a
404+ href="https://en.cppreference.com/w/cpp/named_req/ForwardIterator">forward iterators</a>
405+ over a rectangular region of histogram cells.
406+
407+ Use this in a range-based for loop. Example:
408+ ```
409+ auto hist = make_histogram(axis::integer<>(0, 4), axis::integer<>(2, 6));
410+ axis::index_type range[2] = {{1, 3}, {0, 2}};
411+ for (auto&& x : indexed(hist, range)) { ... }
412+ ```
413+ This skips the first and last index of the first axis, and the last two indices of the
414+ second.
415+
416+ @returns indexed_range
417+
418+ @param hist Reference to the histogram.
419+ @param range Iterable over items with two axis::index_type values, which mark the
420+ begin and end index of each axis. The length of the iterable must be
421+ equal to the rank of the histogram. The begin index must be smaller than
422+ the end index. Index ranges wider than the actual range are reduced to
423+ the actual range including underflow and overflow indices.
424+ */
425+ template <class Histogram , class Iterable , class = detail::requires_iterable<Iterable>>
426+ auto indexed (Histogram&& hist, Iterable&& range) {
427+ return indexed_range<std::remove_reference_t <Histogram>>{std::forward<Histogram>(hist),
428+ std::forward<Iterable>(range)};
429+ }
430+
369431} // namespace histogram
370432} // namespace boost
371433
0 commit comments