indexed.hpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. // Copyright 2015-2016 Hans Dembinski
  2. //
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_HISTOGRAM_INDEXED_HPP
  7. #define BOOST_HISTOGRAM_INDEXED_HPP
  8. #include <boost/histogram/axis/traits.hpp>
  9. #include <boost/histogram/detail/attribute.hpp>
  10. #include <boost/histogram/detail/axes.hpp>
  11. #include <boost/histogram/detail/meta.hpp>
  12. #include <boost/histogram/fwd.hpp>
  13. #include <boost/histogram/unsafe_access.hpp>
  14. #include <boost/iterator/iterator_adaptor.hpp>
  15. #include <type_traits>
  16. #include <utility>
  17. namespace boost {
  18. namespace histogram {
  19. /**
  20. Coverage mode of the indexed range generator.
  21. Defines options for the iteration strategy.
  22. */
  23. enum class coverage {
  24. inner, /*!< iterate over inner bins, exclude underflow and overflow */
  25. all, /*!< iterate over all bins, including underflow and overflow */
  26. };
  27. /// Range over histogram bins with multi-dimensional index.
  28. template <class Histogram>
  29. class BOOST_HISTOGRAM_DETAIL_NODISCARD indexed_range {
  30. using max_dim = mp11::mp_size_t<
  31. detail::buffer_size<typename detail::remove_cvref_t<Histogram>::axes_type>::value>;
  32. using value_iterator = decltype(std::declval<Histogram>().begin());
  33. struct cache_item {
  34. int idx, begin, end, extent;
  35. };
  36. public:
  37. /**
  38. Pointer-like class to access value and index of current cell.
  39. Its methods allow one to query the current indices and bins. Furthermore, it acts like
  40. a pointer to the cell value.
  41. */
  42. class accessor {
  43. public:
  44. /// Array-like view into the current multi-dimensional index.
  45. class index_view {
  46. public:
  47. #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
  48. class index_iterator
  49. : public boost::iterator_adaptor<index_iterator, const cache_item*> {
  50. public:
  51. index_iterator(const cache_item* i) : index_iterator::iterator_adaptor_(i) {}
  52. decltype(auto) operator*() const noexcept { return index_iterator::base()->idx; }
  53. };
  54. #endif
  55. auto begin() const noexcept { return index_iterator(begin_); }
  56. auto end() const noexcept { return index_iterator(end_); }
  57. auto size() const noexcept { return static_cast<std::size_t>(end_ - begin_); }
  58. int operator[](unsigned d) const noexcept { return begin_[d].idx; }
  59. int at(unsigned d) const { return begin_[d].idx; }
  60. private:
  61. index_view(const cache_item* b, const cache_item* e) : begin_(b), end_(e) {}
  62. const cache_item *begin_, *end_;
  63. friend class accessor;
  64. };
  65. /// Returns the cell value.
  66. decltype(auto) operator*() const noexcept { return *iter_; }
  67. /// Returns the cell value.
  68. decltype(auto) get() const noexcept { return *iter_; }
  69. /// Access fields and methods of the cell object.
  70. decltype(auto) operator-> () const noexcept { return iter_; }
  71. /// Access current index.
  72. /// @param d axis dimension.
  73. int index(unsigned d = 0) const noexcept { return parent_.cache_[d].idx; }
  74. /// Access indices as an iterable range.
  75. auto indices() const noexcept {
  76. return index_view(parent_.cache_, parent_.cache_ + parent_.hist_.rank());
  77. }
  78. /// Access current bin.
  79. /// @tparam N axis dimension.
  80. template <unsigned N = 0>
  81. decltype(auto) bin(std::integral_constant<unsigned, N> = {}) const {
  82. return parent_.hist_.axis(std::integral_constant<unsigned, N>()).bin(index(N));
  83. }
  84. /// Access current bin.
  85. /// @param d axis dimension.
  86. decltype(auto) bin(unsigned d) const { return parent_.hist_.axis(d).bin(index(d)); }
  87. /**
  88. Computes density in current cell.
  89. The density is computed as the cell value divided by the product of bin widths. Axes
  90. without bin widths, like axis::category, are treated as having unit bin with.
  91. */
  92. double density() const {
  93. double x = 1;
  94. auto it = parent_.cache_;
  95. parent_.hist_.for_each_axis([&](const auto& a) {
  96. const auto w = axis::traits::width_as<double>(a, it++->idx);
  97. x *= w ? w : 1;
  98. });
  99. return *iter_ / x;
  100. }
  101. private:
  102. accessor(indexed_range& p, value_iterator i) : parent_(p), iter_(i) {}
  103. indexed_range& parent_;
  104. value_iterator iter_;
  105. friend class indexed_range;
  106. };
  107. class range_iterator
  108. : public boost::iterator_adaptor<range_iterator, value_iterator, accessor,
  109. std::forward_iterator_tag, accessor> {
  110. public:
  111. accessor operator*() const noexcept { return {*parent_, range_iterator::base()}; }
  112. private:
  113. range_iterator(indexed_range* p, value_iterator i) noexcept
  114. : range_iterator::iterator_adaptor_(i), parent_(p) {}
  115. void increment() noexcept {
  116. std::size_t stride = 1;
  117. auto c = parent_->cache_;
  118. ++c->idx;
  119. ++range_iterator::base_reference();
  120. while (c->idx == c->end && (c != (parent_->cache_ + parent_->hist_.rank() - 1))) {
  121. c->idx = c->begin;
  122. range_iterator::base_reference() -= (c->end - c->begin) * stride;
  123. stride *= c->extent;
  124. ++c;
  125. ++c->idx;
  126. range_iterator::base_reference() += stride;
  127. }
  128. }
  129. mutable indexed_range* parent_;
  130. friend class boost::iterator_core_access;
  131. friend class indexed_range;
  132. };
  133. indexed_range(Histogram& h, coverage c)
  134. : hist_(h), cover_all_(c == coverage::all), begin_(hist_.begin()), end_(begin_) {
  135. auto ca = cache_;
  136. const auto clast = ca + hist_.rank() - 1;
  137. std::size_t stride = 1;
  138. h.for_each_axis([&, this](const auto& a) {
  139. using opt = axis::traits::static_options<decltype(a)>;
  140. constexpr int under = opt::test(axis::option::underflow);
  141. constexpr int over = opt::test(axis::option::overflow);
  142. const auto size = a.size();
  143. ca->extent = size + under + over;
  144. // -1 if underflow and cover all, else 0
  145. ca->begin = cover_all_ ? -under : 0;
  146. // size + 1 if overflow and cover all, else size
  147. ca->end = cover_all_ ? size + over : size;
  148. ca->idx = ca->begin;
  149. begin_ += (ca->begin + under) * stride;
  150. if (ca < clast)
  151. end_ += (ca->begin + under) * stride;
  152. else
  153. end_ += (ca->end + under) * stride;
  154. stride *= ca->extent;
  155. ++ca;
  156. });
  157. }
  158. range_iterator begin() noexcept { return {this, begin_}; }
  159. range_iterator end() noexcept { return {nullptr, end_}; }
  160. private:
  161. Histogram& hist_;
  162. const bool cover_all_;
  163. value_iterator begin_, end_;
  164. mutable cache_item cache_[max_dim::value];
  165. };
  166. /**
  167. Generates a range over the histogram entries.
  168. Use this in a range-based for loop:
  169. ```
  170. for (auto x : indexed(hist)) { ... }
  171. ```
  172. The iterators dereference to an indexed_range::accessor, which has methods to query the
  173. current indices and bins, and acts like a pointer to the cell value.
  174. @param hist Reference to the histogram.
  175. @param cov Iterate over all or only inner bins (optional, default: inner).
  176. */
  177. template <typename Histogram>
  178. auto indexed(Histogram&& hist, coverage cov = coverage::inner) {
  179. return indexed_range<std::remove_reference_t<Histogram>>{std::forward<Histogram>(hist),
  180. cov};
  181. }
  182. } // namespace histogram
  183. } // namespace boost
  184. #endif