]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/histogram/indexed.hpp
buildsys: switch source download to quincy
[ceph.git] / ceph / src / boost / boost / histogram / indexed.hpp
CommitLineData
92f5a8d4
TL
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
7#ifndef BOOST_HISTOGRAM_INDEXED_HPP
8#define BOOST_HISTOGRAM_INDEXED_HPP
9
10#include <array>
11#include <boost/config.hpp>
12#include <boost/histogram/axis/traits.hpp>
13#include <boost/histogram/detail/axes.hpp>
14#include <boost/histogram/detail/iterator_adaptor.hpp>
15#include <boost/histogram/detail/operators.hpp>
16#include <boost/histogram/fwd.hpp>
17#include <iterator>
18#include <type_traits>
19#include <utility>
20
21namespace boost {
22namespace histogram {
23
24/** Coverage mode of the indexed range generator.
25
26 Defines options for the iteration strategy.
27*/
28enum class coverage {
29 inner, /*!< iterate over inner bins, exclude underflow and overflow */
30 all, /*!< iterate over all bins, including underflow and overflow */
31};
32
33/** Input iterator range over histogram bins with multi-dimensional index.
34
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
37 input iterators exist, the other copies become invalid if one of them is incremented.
38*/
39template <class Histogram>
40class BOOST_ATTRIBUTE_NODISCARD indexed_range {
41private:
42 using histogram_type = Histogram;
43 static constexpr std::size_t buffer_size =
44 detail::buffer_size<typename std::remove_const_t<histogram_type>::axes_type>::value;
45
46public:
47 using value_iterator = std::conditional_t<std::is_const<histogram_type>::value,
48 typename histogram_type::const_iterator,
49 typename histogram_type::iterator>;
50 using value_reference = typename std::iterator_traits<value_iterator>::reference;
51 using value_type = typename std::iterator_traits<value_iterator>::value_type;
52
53 class iterator;
f67539c2 54 using range_iterator [[deprecated("use iterator instead")]] = iterator; ///< deprecated
92f5a8d4
TL
55
56 /** Lightweight view to access value and index of current cell.
57
58 The methods provide access to the current cell indices and bins. It acts like a
59 pointer to the cell value, and in a limited way also like a reference. To interoperate
60 with the algorithms of the standard library, the accessor is implicitly convertible to
61 a cell value. Assignments and comparisons are passed through to the cell. An accessor
62 is coupled to its parent indexed_range::iterator. Moving the parent iterator
63 forward also updates the linked accessor. Accessors are not copyable. They cannot be
64 stored in containers, but indexed_range::iterator can be stored.
65 */
66 class BOOST_ATTRIBUTE_NODISCARD accessor : detail::mirrored<accessor, void> {
67 public:
68 /// Array-like view into the current multi-dimensional index.
69 class index_view {
70 using index_pointer = const typename iterator::index_data*;
71
72 public:
73 using const_reference = const axis::index_type&;
f67539c2
TL
74 using reference [[deprecated("use const_reference instead")]] =
75 const_reference; ///< deprecated
92f5a8d4
TL
76
77 /// implementation detail
78 class const_iterator
79 : public detail::iterator_adaptor<const_iterator, index_pointer,
80 const_reference> {
81 public:
82 const_reference operator*() const noexcept { return const_iterator::base()->idx; }
83
84 private:
85 explicit const_iterator(index_pointer i) noexcept
86 : const_iterator::iterator_adaptor_(i) {}
87
88 friend class index_view;
89 };
90
91 const_iterator begin() const noexcept { return const_iterator{begin_}; }
92 const_iterator end() const noexcept { return const_iterator{end_}; }
93 std::size_t size() const noexcept {
94 return static_cast<std::size_t>(end_ - begin_);
95 }
96 const_reference operator[](unsigned d) const noexcept { return begin_[d].idx; }
97 const_reference at(unsigned d) const { return begin_[d].idx; }
98
99 private:
100 /// implementation detail
101 index_view(index_pointer b, index_pointer e) : begin_(b), end_(e) {}
102
103 index_pointer begin_, end_;
104 friend class accessor;
105 };
106
107 // assignment is pass-through
108 accessor& operator=(const accessor& o) {
109 get() = o.get();
110 return *this;
111 }
112
113 // assignment is pass-through
114 template <class T>
115 accessor& operator=(const T& x) {
116 get() = x;
117 return *this;
118 }
119
120 /// Returns the cell reference.
121 value_reference get() const noexcept { return *(iter_.iter_); }
122 /// @copydoc get()
123 value_reference operator*() const noexcept { return get(); }
124 /// Access fields and methods of the cell object.
125 value_iterator operator->() const noexcept { return iter_.iter_; }
126
127 /// Access current index.
128 /// @param d axis dimension.
129 axis::index_type index(unsigned d = 0) const noexcept {
130 return iter_.indices_[d].idx;
131 }
132
133 /// Access indices as an iterable range.
134 index_view indices() const noexcept {
135 BOOST_ASSERT(iter_.indices_.hist_);
f67539c2 136 return {iter_.indices_.begin(), iter_.indices_.end()};
92f5a8d4
TL
137 }
138
139 /// Access current bin.
140 /// @tparam N axis dimension.
141 template <unsigned N = 0>
142 decltype(auto) bin(std::integral_constant<unsigned, N> = {}) const {
143 BOOST_ASSERT(iter_.indices_.hist_);
144 return iter_.indices_.hist_->axis(std::integral_constant<unsigned, N>())
145 .bin(index(N));
146 }
147
148 /// Access current bin.
149 /// @param d axis dimension.
150 decltype(auto) bin(unsigned d) const {
151 BOOST_ASSERT(iter_.indices_.hist_);
152 return iter_.indices_.hist_->axis(d).bin(index(d));
153 }
154
155 /** Computes density in current cell.
156
157 The density is computed as the cell value divided by the product of bin widths. Axes
158 without bin widths, like axis::category, are treated as having unit bin with.
159 */
160 double density() const {
161 BOOST_ASSERT(iter_.indices_.hist_);
162 double x = 1;
163 unsigned d = 0;
164 iter_.indices_.hist_->for_each_axis([&](const auto& a) {
165 const auto w = axis::traits::width_as<double>(a, this->index(d++));
166 x *= w ? w : 1;
167 });
168 return get() / x;
169 }
170
171 // forward all comparison operators to the value
172 bool operator<(const accessor& o) noexcept { return get() < o.get(); }
173 bool operator>(const accessor& o) noexcept { return get() > o.get(); }
174 bool operator==(const accessor& o) noexcept { return get() == o.get(); }
175 bool operator!=(const accessor& o) noexcept { return get() != o.get(); }
176 bool operator<=(const accessor& o) noexcept { return get() <= o.get(); }
177 bool operator>=(const accessor& o) noexcept { return get() >= o.get(); }
178
179 template <class U>
180 bool operator<(const U& o) const noexcept {
181 return get() < o;
182 }
183
184 template <class U>
185 bool operator>(const U& o) const noexcept {
186 return get() > o;
187 }
188
189 template <class U>
190 bool operator==(const U& o) const noexcept {
191 return get() == o;
192 }
193
194 template <class U>
195 bool operator!=(const U& o) const noexcept {
196 return get() != o;
197 }
198
199 template <class U>
200 bool operator<=(const U& o) const noexcept {
201 return get() <= o;
202 }
203
204 template <class U>
205 bool operator>=(const U& o) const noexcept {
206 return get() >= o;
207 }
208
209 operator value_type() const noexcept { return get(); }
210
211 private:
212 accessor(iterator& i) noexcept : iter_(i) {}
213
214 accessor(const accessor&) = default; // only callable by indexed_range::iterator
215
216 iterator& iter_;
217
218 friend class iterator;
219 };
220
221 /// implementation detail
222 class iterator {
223 public:
224 using value_type = typename indexed_range::value_type;
225 using reference = accessor;
226
227 private:
228 struct pointer_proxy {
229 reference* operator->() noexcept { return std::addressof(ref_); }
230 reference ref_;
231 };
232
233 public:
234 using pointer = pointer_proxy;
235 using difference_type = std::ptrdiff_t;
236 using iterator_category = std::forward_iterator_tag;
237
238 reference operator*() noexcept { return *this; }
239 pointer operator->() noexcept { return pointer_proxy{operator*()}; }
240
241 iterator& operator++() {
f67539c2
TL
242 BOOST_ASSERT(iter_ < indices_.hist_->end());
243 const auto cbeg = indices_.begin();
244 auto c = cbeg;
92f5a8d4 245 ++iter_;
f67539c2
TL
246 ++c->idx;
247 if (c->idx < c->end) return *this;
248 while (c->idx == c->end) {
249 iter_ += c->end_skip;
250 if (++c == indices_.end()) return *this;
92f5a8d4 251 ++c->idx;
f67539c2
TL
252 }
253 while (c-- != cbeg) {
254 c->idx = c->begin;
255 iter_ += c->begin_skip;
92f5a8d4
TL
256 }
257 return *this;
258 }
259
260 iterator operator++(int) {
261 auto prev = *this;
262 operator++();
263 return prev;
264 }
265
266 bool operator==(const iterator& x) const noexcept { return iter_ == x.iter_; }
267 bool operator!=(const iterator& x) const noexcept { return !operator==(x); }
268
f67539c2
TL
269 // make iterator ready for C++17 sentinels
270 bool operator==(const value_iterator& x) const noexcept { return iter_ == x; }
271 bool operator!=(const value_iterator& x) const noexcept { return !operator==(x); }
272
273 // useful for iterator debugging
274 std::size_t offset() const noexcept { return iter_ - indices_.hist_->begin(); }
275
92f5a8d4 276 private:
f67539c2 277 iterator(value_iterator i, histogram_type& h) : iter_(i), indices_(&h) {}
92f5a8d4
TL
278
279 value_iterator iter_;
280
281 struct index_data {
f67539c2
TL
282 axis::index_type idx, begin, end;
283 std::size_t begin_skip, end_skip;
92f5a8d4
TL
284 };
285
f67539c2 286 struct indices_t : private std::array<index_data, buffer_size> {
92f5a8d4 287 using base_type = std::array<index_data, buffer_size>;
f67539c2
TL
288 using pointer = index_data*;
289 using const_pointer = const index_data*;
92f5a8d4
TL
290
291 indices_t(histogram_type* h) noexcept : hist_{h} {}
292
f67539c2
TL
293 using base_type::operator[];
294 unsigned size() const noexcept { return hist_->rank(); }
295 pointer begin() noexcept { return base_type::data(); }
296 const_pointer begin() const noexcept { return base_type::data(); }
297 pointer end() noexcept { return begin() + size(); }
298 const_pointer end() const noexcept { return begin() + size(); }
299
92f5a8d4
TL
300 histogram_type* hist_;
301 } indices_;
302
303 friend class indexed_range;
304 };
305
f67539c2
TL
306 indexed_range(histogram_type& hist, coverage cov)
307 : begin_(hist.begin(), hist), end_(hist.end(), hist) {
308 begin_.indices_.hist_->for_each_axis([ca = begin_.indices_.begin(), cov,
309 stride = std::size_t{1},
310 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);
314 const auto size = a.size();
315
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;
320 ca->idx = ca->begin;
321
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;
328 begin_.iter_ += ca->begin_skip;
329
330 stride *= size + under + over;
331 ++ca;
332 });
92f5a8d4
TL
333 }
334
335 iterator begin() noexcept { return begin_; }
336 iterator end() noexcept { return end_; }
337
338private:
339 iterator begin_, end_;
340};
341
342/** Generates an indexed range of <a
343 href="https://en.cppreference.com/w/cpp/named_req/ForwardIterator">forward iterators</a>
344 over the histogram cells.
345
346 Use this in a range-based for loop:
347
348 ```
349 for (auto&& x : indexed(hist)) { ... }
350 ```
351
352 This generates an optimized loop which is nearly always faster than a hand-written loop
353 over the histogram cells. The iterators dereference to an indexed_range::accessor, which
354 has methods to query the current indices and bins and acts like a pointer to the cell
355 value. The returned iterators are forward iterators. They can be stored in a container,
356 but may not be used after the life-time of the histogram ends.
357
358 @returns indexed_range
359
360 @param hist Reference to the histogram.
361 @param cov Iterate over all or only inner bins (optional, default: inner).
362 */
363template <class Histogram>
364auto indexed(Histogram&& hist, coverage cov = coverage::inner) {
365 return indexed_range<std::remove_reference_t<Histogram>>{std::forward<Histogram>(hist),
366 cov};
367}
368
369} // namespace histogram
370} // namespace boost
371
372#endif