| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- // Copyright 2015-2016 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_INDEXED_HPP
- #define BOOST_HISTOGRAM_INDEXED_HPP
- #include <boost/histogram/axis/traits.hpp>
- #include <boost/histogram/detail/attribute.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/iterator/iterator_adaptor.hpp>
- #include <type_traits>
- #include <utility>
- namespace boost {
- namespace histogram {
- /**
- Coverage mode of the indexed range generator.
- Defines options for the iteration strategy.
- */
- enum class coverage {
- inner, /*!< iterate over inner bins, exclude underflow and overflow */
- all, /*!< iterate over all bins, including underflow and overflow */
- };
- /// Range over histogram bins with multi-dimensional index.
- template <class Histogram>
- class BOOST_HISTOGRAM_DETAIL_NODISCARD indexed_range {
- using max_dim = mp11::mp_size_t<
- detail::buffer_size<typename detail::remove_cvref_t<Histogram>::axes_type>::value>;
- using value_iterator = decltype(std::declval<Histogram>().begin());
- struct cache_item {
- int idx, begin, end, extent;
- };
- public:
- /**
- Pointer-like class to access value and index of current cell.
- Its methods allow one to query the current indices and bins. Furthermore, it acts like
- a pointer to the cell value.
- */
- class accessor {
- public:
- /// Array-like view into the current multi-dimensional index.
- class index_view {
- public:
- #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
- class index_iterator
- : public boost::iterator_adaptor<index_iterator, const cache_item*> {
- public:
- index_iterator(const cache_item* i) : index_iterator::iterator_adaptor_(i) {}
- decltype(auto) operator*() const noexcept { return index_iterator::base()->idx; }
- };
- #endif
- auto begin() const noexcept { return index_iterator(begin_); }
- auto end() const noexcept { return index_iterator(end_); }
- auto size() const noexcept { return static_cast<std::size_t>(end_ - begin_); }
- int operator[](unsigned d) const noexcept { return begin_[d].idx; }
- int at(unsigned d) const { return begin_[d].idx; }
- private:
- index_view(const cache_item* b, const cache_item* e) : begin_(b), end_(e) {}
- const cache_item *begin_, *end_;
- friend class accessor;
- };
- /// Returns the cell value.
- decltype(auto) operator*() const noexcept { return *iter_; }
- /// Returns the cell value.
- decltype(auto) get() const noexcept { return *iter_; }
- /// Access fields and methods of the cell object.
- decltype(auto) operator-> () const noexcept { return iter_; }
- /// Access current index.
- /// @param d axis dimension.
- int index(unsigned d = 0) const noexcept { return parent_.cache_[d].idx; }
- /// Access indices as an iterable range.
- auto indices() const noexcept {
- return index_view(parent_.cache_, parent_.cache_ + parent_.hist_.rank());
- }
- /// Access current bin.
- /// @tparam N axis dimension.
- template <unsigned N = 0>
- decltype(auto) bin(std::integral_constant<unsigned, N> = {}) const {
- return parent_.hist_.axis(std::integral_constant<unsigned, N>()).bin(index(N));
- }
- /// Access current bin.
- /// @param d axis dimension.
- decltype(auto) bin(unsigned d) const { return parent_.hist_.axis(d).bin(index(d)); }
- /**
- Computes density in current cell.
- The density is computed as the cell value divided by the product of bin widths. Axes
- without bin widths, like axis::category, are treated as having unit bin with.
- */
- double density() const {
- double x = 1;
- auto it = parent_.cache_;
- parent_.hist_.for_each_axis([&](const auto& a) {
- const auto w = axis::traits::width_as<double>(a, it++->idx);
- x *= w ? w : 1;
- });
- return *iter_ / x;
- }
- private:
- accessor(indexed_range& p, value_iterator i) : parent_(p), iter_(i) {}
- indexed_range& parent_;
- value_iterator iter_;
- friend class indexed_range;
- };
- class range_iterator
- : public boost::iterator_adaptor<range_iterator, value_iterator, accessor,
- std::forward_iterator_tag, accessor> {
- public:
- accessor operator*() const noexcept { return {*parent_, range_iterator::base()}; }
- private:
- range_iterator(indexed_range* p, value_iterator i) noexcept
- : range_iterator::iterator_adaptor_(i), parent_(p) {}
- void increment() noexcept {
- std::size_t stride = 1;
- auto c = parent_->cache_;
- ++c->idx;
- ++range_iterator::base_reference();
- while (c->idx == c->end && (c != (parent_->cache_ + parent_->hist_.rank() - 1))) {
- c->idx = c->begin;
- range_iterator::base_reference() -= (c->end - c->begin) * stride;
- stride *= c->extent;
- ++c;
- ++c->idx;
- range_iterator::base_reference() += stride;
- }
- }
- mutable indexed_range* parent_;
- friend class boost::iterator_core_access;
- friend class indexed_range;
- };
- indexed_range(Histogram& h, coverage c)
- : hist_(h), cover_all_(c == coverage::all), begin_(hist_.begin()), end_(begin_) {
- auto ca = cache_;
- const auto clast = ca + hist_.rank() - 1;
- std::size_t stride = 1;
- h.for_each_axis([&, this](const auto& a) {
- using opt = axis::traits::static_options<decltype(a)>;
- constexpr int under = opt::test(axis::option::underflow);
- constexpr int over = opt::test(axis::option::overflow);
- const auto size = a.size();
- ca->extent = size + under + over;
- // -1 if underflow and cover all, else 0
- ca->begin = cover_all_ ? -under : 0;
- // size + 1 if overflow and cover all, else size
- ca->end = cover_all_ ? size + over : size;
- ca->idx = ca->begin;
- begin_ += (ca->begin + under) * stride;
- if (ca < clast)
- end_ += (ca->begin + under) * stride;
- else
- end_ += (ca->end + under) * stride;
- stride *= ca->extent;
- ++ca;
- });
- }
- range_iterator begin() noexcept { return {this, begin_}; }
- range_iterator end() noexcept { return {nullptr, end_}; }
- private:
- Histogram& hist_;
- const bool cover_all_;
- value_iterator begin_, end_;
- mutable cache_item cache_[max_dim::value];
- };
- /**
- Generates a range over the histogram entries.
- Use this in a range-based for loop:
- ```
- for (auto x : indexed(hist)) { ... }
- ```
- The iterators dereference to an indexed_range::accessor, which has methods to query the
- current indices and bins, and acts like a pointer to the cell value.
- @param hist Reference to the histogram.
- @param cov Iterate over all or only inner bins (optional, default: inner).
- */
- template <typename Histogram>
- auto indexed(Histogram&& hist, coverage cov = coverage::inner) {
- return indexed_range<std::remove_reference_t<Histogram>>{std::forward<Histogram>(hist),
- cov};
- }
- } // namespace histogram
- } // namespace boost
- #endif
|