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