]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/histogram/histogram.hpp
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / boost / boost / histogram / histogram.hpp
CommitLineData
92f5a8d4
TL
1// Copyright 2015-2018 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_HISTOGRAM_HPP
8#define BOOST_HISTOGRAM_HISTOGRAM_HPP
9
10#include <boost/histogram/detail/accumulator_traits.hpp>
11#include <boost/histogram/detail/argument_traits.hpp>
12#include <boost/histogram/detail/at.hpp>
13#include <boost/histogram/detail/axes.hpp>
14#include <boost/histogram/detail/common_type.hpp>
15#include <boost/histogram/detail/fill.hpp>
16#include <boost/histogram/detail/fill_n.hpp>
17#include <boost/histogram/detail/mutex_base.hpp>
18#include <boost/histogram/detail/non_member_container_access.hpp>
19#include <boost/histogram/detail/span.hpp>
f67539c2 20#include <boost/histogram/detail/static_if.hpp>
92f5a8d4
TL
21#include <boost/histogram/fwd.hpp>
22#include <boost/histogram/sample.hpp>
23#include <boost/histogram/storage_adaptor.hpp>
24#include <boost/histogram/unsafe_access.hpp>
25#include <boost/histogram/weight.hpp>
26#include <boost/mp11/integral.hpp>
27#include <boost/mp11/list.hpp>
28#include <boost/mp11/tuple.hpp>
29#include <boost/throw_exception.hpp>
30#include <mutex>
31#include <stdexcept>
32#include <tuple>
33#include <type_traits>
34#include <utility>
35#include <vector>
36
37namespace boost {
38namespace histogram {
39
40/** Central class of the histogram library.
41
42 Histogram uses the call operator to insert data, like the
43 [Boost.Accumulators](https://www.boost.org/doc/libs/develop/doc/html/accumulators.html).
44
45 Use factory functions (see
46 [make_histogram.hpp](histogram/reference.html#header.boost.histogram.make_histogram_hpp)
47 and
48 [make_profile.hpp](histogram/reference.html#header.boost.histogram.make_profile_hpp)) to
49 conveniently create histograms rather than calling the ctors directly.
50
51 Use the [indexed](boost/histogram/indexed.html) range generator to iterate over filled
52 histograms, which is convenient and faster than hand-written loops for multi-dimensional
53 histograms.
54
55 @tparam Axes std::tuple of axis types OR std::vector of an axis type or axis::variant
56 @tparam Storage class that implements the storage interface
57 */
58template <class Axes, class Storage>
59class histogram : detail::mutex_base<Axes, Storage> {
60 static_assert(std::is_same<std::decay_t<Storage>, Storage>::value,
61 "Storage may not be a reference or const or volatile");
62 static_assert(mp11::mp_size<Axes>::value > 0, "at least one axis required");
63
64public:
65 using axes_type = Axes;
66 using storage_type = Storage;
67 using value_type = typename storage_type::value_type;
68 // typedefs for boost::range_iterator
69 using iterator = typename storage_type::iterator;
70 using const_iterator = typename storage_type::const_iterator;
71
72private:
73 using mutex_base = typename detail::mutex_base<axes_type, storage_type>;
74
75public:
76 histogram() = default;
77
78 template <class A, class S>
79 explicit histogram(histogram<A, S>&& rhs)
80 : storage_(std::move(unsafe_access::storage(rhs)))
81 , offset_(unsafe_access::offset(rhs)) {
82 detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
83 detail::throw_if_axes_is_too_large(axes_);
84 }
85
86 template <class A, class S>
87 explicit histogram(const histogram<A, S>& rhs)
88 : storage_(unsafe_access::storage(rhs)), offset_(unsafe_access::offset(rhs)) {
89 detail::axes_assign(axes_, unsafe_access::axes(rhs));
90 detail::throw_if_axes_is_too_large(axes_);
91 }
92
93 template <class A, class S>
94 histogram& operator=(histogram<A, S>&& rhs) {
95 detail::axes_assign(axes_, std::move(unsafe_access::axes(rhs)));
96 detail::throw_if_axes_is_too_large(axes_);
97 storage_ = std::move(unsafe_access::storage(rhs));
98 offset_ = unsafe_access::offset(rhs);
99 return *this;
100 }
101
102 template <class A, class S>
103 histogram& operator=(const histogram<A, S>& rhs) {
104 detail::axes_assign(axes_, unsafe_access::axes(rhs));
105 detail::throw_if_axes_is_too_large(axes_);
106 storage_ = unsafe_access::storage(rhs);
107 offset_ = unsafe_access::offset(rhs);
108 return *this;
109 }
110
111 template <class A, class = detail::requires_axes<A>>
112 histogram(A&& a, Storage s)
113 : axes_(std::forward<A>(a))
114 , storage_(std::move(s))
115 , offset_(detail::offset(axes_)) {
116 detail::throw_if_axes_is_too_large(axes_);
117 storage_.reset(detail::bincount(axes_));
118 }
119
120 explicit histogram(Axes axes) : histogram(axes, storage_type()) {}
121
122 template <class... As, class = detail::requires_axes<std::tuple<std::decay_t<As>...>>>
123 explicit histogram(As&&... as)
124 : histogram(std::tuple<std::decay_t<As>...>(std::forward<As>(as)...),
125 storage_type()) {}
126
127 /// Number of axes (dimensions).
128 constexpr unsigned rank() const noexcept { return detail::axes_rank(axes_); }
129
130 /// Total number of bins (including underflow/overflow).
131 std::size_t size() const noexcept { return storage_.size(); }
132
133 /// Reset all bins to default initialized values.
134 void reset() { storage_.reset(size()); }
135
136 /// Get N-th axis using a compile-time number.
137 /// This version is more efficient than the one accepting a run-time number.
138 template <unsigned N = 0>
139 decltype(auto) axis(std::integral_constant<unsigned, N> = {}) const {
140 detail::axis_index_is_valid(axes_, N);
141 return detail::axis_get<N>(axes_);
142 }
143
144 /// Get N-th axis with run-time number.
145 /// Prefer the version that accepts a compile-time number, if you can use it.
146 decltype(auto) axis(unsigned i) const {
147 detail::axis_index_is_valid(axes_, i);
148 return detail::axis_get(axes_, i);
149 }
150
151 /// Apply unary functor/function to each axis.
152 template <class Unary>
153 auto for_each_axis(Unary&& unary) const {
154 return detail::for_each_axis(axes_, std::forward<Unary>(unary));
155 }
156
157 /** Fill histogram with values, an optional weight, and/or a sample.
158
f67539c2
TL
159 Returns iterator to located cell.
160
92f5a8d4
TL
161 Arguments are passed in order to the axis objects. Passing an argument type that is
162 not convertible to the value type accepted by the axis or passing the wrong number
163 of arguments causes a throw of `std::invalid_argument`.
164
165 __Optional weight__
166
167 An optional weight can be passed as the first or last argument
168 with the [weight](boost/histogram/weight.html) helper function. Compilation fails if
169 the storage elements do not support weights.
170
171 __Samples__
172
173 If the storage elements accept samples, pass them with the sample helper function
174 in addition to the axis arguments, which can be the first or last argument. The
175 [sample](boost/histogram/sample.html) helper function can pass one or more arguments
176 to the storage element. If samples and weights are used together, they can be passed
177 in any order at the beginning or end of the argument list.
178
179 __Axis with multiple arguments__
180
181 If the histogram contains an axis which accepts a `std::tuple` of arguments, the
f67539c2 182 arguments for that axis need to be passed as a `std::tuple`, for example,
92f5a8d4
TL
183 `std::make_tuple(1.2, 2.3)`. If the histogram contains only this axis and no other,
184 the arguments can be passed directly.
185 */
f67539c2
TL
186 template <class T0, class... Ts,
187 class = std::enable_if_t<(detail::is_tuple<T0>::value == false ||
188 sizeof...(Ts) > 0)>>
189 iterator operator()(const T0& arg0, const Ts&... args) {
92f5a8d4
TL
190 return operator()(std::forward_as_tuple(arg0, args...));
191 }
192
193 /// Fill histogram with values, an optional weight, and/or a sample from a `std::tuple`.
194 template <class... Ts>
195 iterator operator()(const std::tuple<Ts...>& args) {
196 using arg_traits = detail::argument_traits<std::decay_t<Ts>...>;
197 using acc_traits = detail::accumulator_traits<value_type>;
198 constexpr bool weight_valid =
f67539c2 199 arg_traits::wpos::value == -1 || acc_traits::weight_support;
92f5a8d4
TL
200 static_assert(weight_valid, "error: accumulator does not support weights");
201 detail::sample_args_passed_vs_expected<typename arg_traits::sargs,
202 typename acc_traits::args>();
203 constexpr bool sample_valid =
204 std::is_convertible<typename arg_traits::sargs, typename acc_traits::args>::value;
205 std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
206 return detail::fill(mp11::mp_bool<(weight_valid && sample_valid)>{}, arg_traits{},
207 offset_, storage_, axes_, args);
208 }
209
210 /** Fill histogram with several values at once.
211
212 The argument must be an iterable with a size that matches the
213 rank of the histogram. The element of an iterable may be 1) a value or 2) an iterable
214 with contiguous storage over values or 3) a variant of 1) and 2). Sub-iterables must
215 have the same length.
216
217 Values are passed to the corresponding histogram axis in order. If a single value is
218 passed together with an iterable of values, the single value is treated like an
219 iterable with matching length of copies of this value.
220
221 If the histogram has only one axis, an iterable of values may be passed directly.
222
223 @param args iterable as explained in the long description.
224 */
225 template <class Iterable, class = detail::requires_iterable<Iterable>>
226 void fill(const Iterable& args) {
227 using acc_traits = detail::accumulator_traits<value_type>;
228 constexpr unsigned n_sample_args_expected =
229 std::tuple_size<typename acc_traits::args>::value;
230 static_assert(n_sample_args_expected == 0,
231 "sample argument is missing but required by accumulator");
232 std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
233 detail::fill_n(mp11::mp_bool<(n_sample_args_expected == 0)>{}, offset_, storage_,
234 axes_, detail::make_span(args));
235 }
236
237 /** Fill histogram with several values and weights at once.
238
239 @param args iterable of values.
240 @param weights single weight or an iterable of weights.
241 */
242 template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
243 void fill(const Iterable& args, const weight_type<T>& weights) {
244 using acc_traits = detail::accumulator_traits<value_type>;
f67539c2 245 constexpr bool weight_valid = acc_traits::weight_support;
92f5a8d4
TL
246 static_assert(weight_valid, "error: accumulator does not support weights");
247 detail::sample_args_passed_vs_expected<std::tuple<>, typename acc_traits::args>();
248 constexpr bool sample_valid =
249 std::is_convertible<std::tuple<>, typename acc_traits::args>::value;
250 std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
251 detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_, storage_,
252 axes_, detail::make_span(args),
253 weight(detail::to_ptr_size(weights.value)));
254 }
255
256 /** Fill histogram with several values and weights at once.
257
258 @param weights single weight or an iterable of weights.
259 @param args iterable of values.
260 */
261 template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
262 void fill(const weight_type<T>& weights, const Iterable& args) {
263 fill(args, weights);
264 }
265
266 /** Fill histogram with several values and samples at once.
267
268 @param args iterable of values.
269 @param samples single sample or an iterable of samples.
270 */
271 template <class Iterable, class... Ts, class = detail::requires_iterable<Iterable>>
272 void fill(const Iterable& args, const sample_type<std::tuple<Ts...>>& samples) {
273 using acc_traits = detail::accumulator_traits<value_type>;
274 using sample_args_passed =
275 std::tuple<decltype(*detail::to_ptr_size(std::declval<Ts>()).first)...>;
276 detail::sample_args_passed_vs_expected<sample_args_passed,
277 typename acc_traits::args>();
278 std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
279 mp11::tuple_apply(
280 [&](const auto&... sargs) {
281 constexpr bool sample_valid =
282 std::is_convertible<sample_args_passed, typename acc_traits::args>::value;
283 detail::fill_n(mp11::mp_bool<(sample_valid)>{}, offset_, storage_, axes_,
284 detail::make_span(args), detail::to_ptr_size(sargs)...);
285 },
286 samples.value);
287 }
288
289 /** Fill histogram with several values and samples at once.
290
291 @param samples single sample or an iterable of samples.
292 @param args iterable of values.
293 */
294 template <class Iterable, class T, class = detail::requires_iterable<Iterable>>
295 void fill(const sample_type<T>& samples, const Iterable& args) {
296 fill(args, samples);
297 }
298
299 template <class Iterable, class T, class... Ts,
300 class = detail::requires_iterable<Iterable>>
301 void fill(const Iterable& args, const weight_type<T>& weights,
302 const sample_type<std::tuple<Ts...>>& samples) {
303 using acc_traits = detail::accumulator_traits<value_type>;
304 using sample_args_passed =
305 std::tuple<decltype(*detail::to_ptr_size(std::declval<Ts>()).first)...>;
306 detail::sample_args_passed_vs_expected<sample_args_passed,
307 typename acc_traits::args>();
308 std::lock_guard<typename mutex_base::type> guard{mutex_base::get()};
309 mp11::tuple_apply(
310 [&](const auto&... sargs) {
f67539c2 311 constexpr bool weight_valid = acc_traits::weight_support;
92f5a8d4
TL
312 static_assert(weight_valid, "error: accumulator does not support weights");
313 constexpr bool sample_valid =
314 std::is_convertible<sample_args_passed, typename acc_traits::args>::value;
315 detail::fill_n(mp11::mp_bool<(weight_valid && sample_valid)>{}, offset_,
316 storage_, axes_, detail::make_span(args),
317 weight(detail::to_ptr_size(weights.value)),
318 detail::to_ptr_size(sargs)...);
319 },
320 samples.value);
321 }
322
323 template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
324 void fill(const sample_type<T>& samples, const weight_type<U>& weights,
325 const Iterable& args) {
326 fill(args, weights, samples);
327 }
328
329 template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
330 void fill(const weight_type<T>& weights, const sample_type<U>& samples,
331 const Iterable& args) {
332 fill(args, weights, samples);
333 }
334
335 template <class Iterable, class T, class U, class = detail::requires_iterable<Iterable>>
336 void fill(const Iterable& args, const sample_type<T>& samples,
337 const weight_type<U>& weights) {
338 fill(args, weights, samples);
339 }
340
341 /** Access cell value at integral indices.
342
343 You can pass indices as individual arguments, as a std::tuple of integers, or as an
344 interable range of integers. Passing the wrong number of arguments causes a throw of
345 std::invalid_argument. Passing an index which is out of bounds causes a throw of
346 std::out_of_range.
347
348 @param i index of first axis.
349 @param is indices of second, third, ... axes.
350 @returns reference to cell value.
351 */
352 template <class... Indices>
353 decltype(auto) at(axis::index_type i, Indices... is) {
354 return at(std::forward_as_tuple(i, is...));
355 }
356
357 /// Access cell value at integral indices (read-only).
358 template <class... Indices>
359 decltype(auto) at(axis::index_type i, Indices... is) const {
360 return at(std::forward_as_tuple(i, is...));
361 }
362
363 /// Access cell value at integral indices stored in `std::tuple`.
364 template <class... Indices>
365 decltype(auto) at(const std::tuple<Indices...>& is) {
366 if (rank() != sizeof...(Indices))
367 BOOST_THROW_EXCEPTION(
368 std::invalid_argument("number of arguments != histogram rank"));
369 const auto idx = detail::at(axes_, is);
370 if (!is_valid(idx))
371 BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
372 BOOST_ASSERT(idx < storage_.size());
373 return storage_[idx];
374 }
375
376 /// Access cell value at integral indices stored in `std::tuple` (read-only).
f67539c2 377 template <class... Indices>
92f5a8d4
TL
378 decltype(auto) at(const std::tuple<Indices...>& is) const {
379 if (rank() != sizeof...(Indices))
380 BOOST_THROW_EXCEPTION(
381 std::invalid_argument("number of arguments != histogram rank"));
382 const auto idx = detail::at(axes_, is);
383 if (!is_valid(idx))
384 BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
385 BOOST_ASSERT(idx < storage_.size());
386 return storage_[idx];
387 }
388
389 /// Access cell value at integral indices stored in iterable.
390 template <class Iterable, class = detail::requires_iterable<Iterable>>
391 decltype(auto) at(const Iterable& is) {
392 if (rank() != detail::axes_rank(is))
393 BOOST_THROW_EXCEPTION(
394 std::invalid_argument("number of arguments != histogram rank"));
395 const auto idx = detail::at(axes_, is);
396 if (!is_valid(idx))
397 BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
398 BOOST_ASSERT(idx < storage_.size());
399 return storage_[idx];
400 }
401
402 /// Access cell value at integral indices stored in iterable (read-only).
403 template <class Iterable, class = detail::requires_iterable<Iterable>>
404 decltype(auto) at(const Iterable& is) const {
405 if (rank() != detail::axes_rank(is))
406 BOOST_THROW_EXCEPTION(
407 std::invalid_argument("number of arguments != histogram rank"));
408 const auto idx = detail::at(axes_, is);
409 if (!is_valid(idx))
410 BOOST_THROW_EXCEPTION(std::out_of_range("at least one index out of bounds"));
411 BOOST_ASSERT(idx < storage_.size());
412 return storage_[idx];
413 }
414
415 /// Access value at index (number for rank = 1, else `std::tuple` or iterable).
416 template <class Indices>
417 decltype(auto) operator[](const Indices& is) {
418 return at(is);
419 }
420
421 /// Access value at index (read-only).
422 template <class Indices>
423 decltype(auto) operator[](const Indices& is) const {
424 return at(is);
425 }
426
427 /// Equality operator, tests equality for all axes and the storage.
428 template <class A, class S>
429 bool operator==(const histogram<A, S>& rhs) const noexcept {
430 // testing offset is redundant, but offers fast return if it fails
431 return offset_ == unsafe_access::offset(rhs) &&
432 detail::axes_equal(axes_, unsafe_access::axes(rhs)) &&
433 storage_ == unsafe_access::storage(rhs);
434 }
435
436 /// Negation of the equality operator.
437 template <class A, class S>
438 bool operator!=(const histogram<A, S>& rhs) const noexcept {
439 return !operator==(rhs);
440 }
441
f67539c2
TL
442 /** Add values of another histogram.
443
444 This operator is only available if the value_type supports operator+=.
445 */
92f5a8d4 446 template <class A, class S>
f67539c2
TL
447#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
448 histogram&
449#else
92f5a8d4
TL
450 std::enable_if_t<
451 detail::has_operator_radd<value_type, typename histogram<A, S>::value_type>::value,
452 histogram&>
f67539c2 453#endif
92f5a8d4
TL
454 operator+=(const histogram<A, S>& rhs) {
455 if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
456 BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
457 auto rit = unsafe_access::storage(rhs).begin();
458 for (auto&& x : storage_) x += *rit++;
459 return *this;
460 }
461
f67539c2
TL
462 /** Subtract values of another histogram.
463
464 This operator is only available if the value_type supports operator-=.
465 */
92f5a8d4 466 template <class A, class S>
f67539c2
TL
467#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
468 histogram&
469#else
92f5a8d4
TL
470 std::enable_if_t<
471 detail::has_operator_rsub<value_type, typename histogram<A, S>::value_type>::value,
472 histogram&>
f67539c2 473#endif
92f5a8d4
TL
474 operator-=(const histogram<A, S>& rhs) {
475 if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
476 BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
477 auto rit = unsafe_access::storage(rhs).begin();
478 for (auto&& x : storage_) x -= *rit++;
479 return *this;
480 }
481
f67539c2
TL
482 /** Multiply by values of another histogram.
483
484 This operator is only available if the value_type supports operator*=.
485 */
92f5a8d4 486 template <class A, class S>
f67539c2
TL
487#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
488 histogram&
489#else
92f5a8d4
TL
490 std::enable_if_t<
491 detail::has_operator_rmul<value_type, typename histogram<A, S>::value_type>::value,
492 histogram&>
f67539c2 493#endif
92f5a8d4
TL
494 operator*=(const histogram<A, S>& rhs) {
495 if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
496 BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
497 auto rit = unsafe_access::storage(rhs).begin();
498 for (auto&& x : storage_) x *= *rit++;
499 return *this;
500 }
501
f67539c2
TL
502 /** Divide by values of another histogram.
503
504 This operator is only available if the value_type supports operator/=.
505 */
92f5a8d4 506 template <class A, class S>
f67539c2
TL
507#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
508 histogram&
509#else
92f5a8d4
TL
510 std::enable_if_t<
511 detail::has_operator_rdiv<value_type, typename histogram<A, S>::value_type>::value,
512 histogram&>
f67539c2 513#endif
92f5a8d4
TL
514 operator/=(const histogram<A, S>& rhs) {
515 if (!detail::axes_equal(axes_, unsafe_access::axes(rhs)))
516 BOOST_THROW_EXCEPTION(std::invalid_argument("axes of histograms differ"));
517 auto rit = unsafe_access::storage(rhs).begin();
518 for (auto&& x : storage_) x /= *rit++;
519 return *this;
520 }
521
f67539c2 522 /** Multiply all values with a scalar.
92f5a8d4 523
f67539c2
TL
524 This operator is only available if the value_type supports operator*=.
525 */
526#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
527 histogram&
528#else
92f5a8d4 529 template <class V = value_type>
f67539c2
TL
530 std::enable_if_t<(detail::has_operator_rmul<V, double>::value), histogram&>
531#endif
92f5a8d4 532 operator*=(const double x) {
f67539c2
TL
533 // use special storage implementation of scaling if available,
534 // else fallback to scaling item by item
535 detail::static_if<detail::has_operator_rmul<storage_type, double>>(
536 [x](auto& s) { s *= x; },
537 [x](auto& s) {
538 for (auto&& si : s) si *= x;
539 },
540 storage_);
92f5a8d4
TL
541 return *this;
542 }
543
f67539c2
TL
544 /** Divide all values by a scalar.
545
546 This operator is only available if operator*= is available.
547 */
548#ifdef BOOST_HISTOGRAM_DOXYGEN_INVOKED
549 histogram&
550#else
551 template <class H = histogram>
552 std::enable_if_t<(detail::has_operator_rmul<H, double>::value), histogram&>
553#endif
554 operator/=(const double x) {
92f5a8d4
TL
555 return operator*=(1.0 / x);
556 }
557
558 /// Return value iterator to the beginning of the histogram.
559 iterator begin() noexcept { return storage_.begin(); }
560
561 /// Return value iterator to the end in the histogram.
562 iterator end() noexcept { return storage_.end(); }
563
564 /// Return value iterator to the beginning of the histogram (read-only).
565 const_iterator begin() const noexcept { return storage_.begin(); }
566
567 /// Return value iterator to the end in the histogram (read-only).
568 const_iterator end() const noexcept { return storage_.end(); }
569
570 /// Return value iterator to the beginning of the histogram (read-only).
571 const_iterator cbegin() const noexcept { return begin(); }
572
573 /// Return value iterator to the end in the histogram (read-only).
574 const_iterator cend() const noexcept { return end(); }
575
576 template <class Archive>
577 void serialize(Archive& ar, unsigned /* version */) {
578 detail::axes_serialize(ar, axes_);
579 ar& make_nvp("storage", storage_);
580 if (Archive::is_loading::value) {
581 offset_ = detail::offset(axes_);
582 detail::throw_if_axes_is_too_large(axes_);
583 }
584 }
585
586private:
587 axes_type axes_;
588 storage_type storage_;
589 std::size_t offset_ = 0;
590
591 friend struct unsafe_access;
592};
593
594/**
595 Pairwise add cells of two histograms and return histogram with the sum.
596
597 The returned histogram type is the most efficient and safest one constructible from the
598 inputs, if they are not the same type. If one histogram has a tuple axis, the result has
599 a tuple axis. The chosen storage is the one with the larger dynamic range.
600*/
601template <class A1, class S1, class A2, class S2>
602auto operator+(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
603 auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
604 return r += b;
605}
606
607/** Pairwise multiply cells of two histograms and return histogram with the product.
608
609 For notes on the returned histogram type, see operator+.
610*/
611template <class A1, class S1, class A2, class S2>
612auto operator*(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
613 auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
614 return r *= b;
615}
616
617/** Pairwise subtract cells of two histograms and return histogram with the difference.
618
619 For notes on the returned histogram type, see operator+.
620*/
621template <class A1, class S1, class A2, class S2>
622auto operator-(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
623 auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
624 return r -= b;
625}
626
627/** Pairwise divide cells of two histograms and return histogram with the quotient.
628
629 For notes on the returned histogram type, see operator+.
630*/
631template <class A1, class S1, class A2, class S2>
632auto operator/(const histogram<A1, S1>& a, const histogram<A2, S2>& b) {
633 auto r = histogram<detail::common_axes<A1, A2>, detail::common_storage<S1, S2>>(a);
634 return r /= b;
635}
636
637/** Multiply all cells of the histogram by a number and return a new histogram.
638
639 If the original histogram has integer cells, the result has double cells.
640*/
641template <class A, class S>
642auto operator*(const histogram<A, S>& h, double x) {
643 auto r = histogram<A, detail::common_storage<S, dense_storage<double>>>(h);
644 return r *= x;
645}
646
647/** Multiply all cells of the histogram by a number and return a new histogram.
648
649 If the original histogram has integer cells, the result has double cells.
650*/
651template <class A, class S>
652auto operator*(double x, const histogram<A, S>& h) {
653 return h * x;
654}
655
656/** Divide all cells of the histogram by a number and return a new histogram.
657
658 If the original histogram has integer cells, the result has double cells.
659*/
660template <class A, class S>
661auto operator/(const histogram<A, S>& h, double x) {
662 return h * (1.0 / x);
663}
664
665#if __cpp_deduction_guides >= 201606
666
667template <class... Axes, class = detail::requires_axes<std::tuple<std::decay_t<Axes>...>>>
668histogram(Axes...)->histogram<std::tuple<std::decay_t<Axes>...>>;
669
670template <class... Axes, class S, class = detail::requires_storage_or_adaptible<S>>
671histogram(std::tuple<Axes...>, S)
672 ->histogram<std::tuple<Axes...>, std::conditional_t<detail::is_adaptible<S>::value,
673 storage_adaptor<S>, S>>;
674
675template <class Iterable, class = detail::requires_iterable<Iterable>,
676 class = detail::requires_any_axis<typename Iterable::value_type>>
677histogram(Iterable)->histogram<std::vector<typename Iterable::value_type>>;
678
679template <class Iterable, class S, class = detail::requires_iterable<Iterable>,
680 class = detail::requires_any_axis<typename Iterable::value_type>,
681 class = detail::requires_storage_or_adaptible<S>>
682histogram(Iterable, S)
683 ->histogram<
684 std::vector<typename Iterable::value_type>,
685 std::conditional_t<detail::is_adaptible<S>::value, storage_adaptor<S>, S>>;
686
687#endif
688
689} // namespace histogram
690} // namespace boost
691
692#endif