]>
Commit | Line | Data |
---|---|---|
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 | ||
37 | namespace boost { | |
38 | namespace 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 | */ | |
58 | template <class Axes, class Storage> | |
59 | class 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 | ||
64 | public: | |
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 | ||
72 | private: | |
73 | using mutex_base = typename detail::mutex_base<axes_type, storage_type>; | |
74 | ||
75 | public: | |
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 | ||
586 | private: | |
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 | */ | |
601 | template <class A1, class S1, class A2, class S2> | |
602 | auto 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 | */ | |
611 | template <class A1, class S1, class A2, class S2> | |
612 | auto 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 | */ | |
621 | template <class A1, class S1, class A2, class S2> | |
622 | auto 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 | */ | |
631 | template <class A1, class S1, class A2, class S2> | |
632 | auto 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 | */ | |
641 | template <class A, class S> | |
642 | auto 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 | */ | |
651 | template <class A, class S> | |
652 | auto 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 | */ | |
660 | template <class A, class S> | |
661 | auto operator/(const histogram<A, S>& h, double x) { | |
662 | return h * (1.0 / x); | |
663 | } | |
664 | ||
665 | #if __cpp_deduction_guides >= 201606 | |
666 | ||
667 | template <class... Axes, class = detail::requires_axes<std::tuple<std::decay_t<Axes>...>>> | |
668 | histogram(Axes...)->histogram<std::tuple<std::decay_t<Axes>...>>; | |
669 | ||
670 | template <class... Axes, class S, class = detail::requires_storage_or_adaptible<S>> | |
671 | histogram(std::tuple<Axes...>, S) | |
672 | ->histogram<std::tuple<Axes...>, std::conditional_t<detail::is_adaptible<S>::value, | |
673 | storage_adaptor<S>, S>>; | |
674 | ||
675 | template <class Iterable, class = detail::requires_iterable<Iterable>, | |
676 | class = detail::requires_any_axis<typename Iterable::value_type>> | |
677 | histogram(Iterable)->histogram<std::vector<typename Iterable::value_type>>; | |
678 | ||
679 | template <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>> | |
682 | histogram(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 |