]>
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> | |
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 | ||
39 | namespace boost { | |
40 | namespace 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 | */ | |
60 | template <class Axes, class Storage> | |
61 | class 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 | ||
66 | public: | |
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 | |
75 | private: | |
76 | using mutex_base = typename detail::mutex_base<axes_type, storage_type>; | |
77 | ||
78 | public: | |
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 | ||
609 | private: | |
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 | */ | |
624 | template <class A1, class S1, class A2, class S2> | |
625 | auto 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 | */ | |
634 | template <class A1, class S1, class A2, class S2> | |
635 | auto 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 | */ | |
644 | template <class A1, class S1, class A2, class S2> | |
645 | auto 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 | */ | |
654 | template <class A1, class S1, class A2, class S2> | |
655 | auto 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 | */ | |
664 | template <class A, class S> | |
665 | auto 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 | */ | |
674 | template <class A, class S> | |
675 | auto 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 | */ | |
683 | template <class A, class S> | |
684 | auto operator/(const histogram<A, S>& h, double x) { | |
685 | return h * (1.0 / x); | |
686 | } | |
687 | ||
688 | #if __cpp_deduction_guides >= 201606 | |
689 | ||
690 | template <class... Axes, class = detail::requires_axes<std::tuple<std::decay_t<Axes>...>>> | |
20effc67 | 691 | histogram(Axes...) -> histogram<std::tuple<std::decay_t<Axes>...>>; |
92f5a8d4 TL |
692 | |
693 | template <class... Axes, class S, class = detail::requires_storage_or_adaptible<S>> | |
694 | histogram(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 | |
698 | template <class Iterable, class = detail::requires_iterable<Iterable>, | |
699 | class = detail::requires_any_axis<typename Iterable::value_type>> | |
20effc67 | 700 | histogram(Iterable) -> histogram<std::vector<typename Iterable::value_type>>; |
92f5a8d4 TL |
701 | |
702 | template <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 |
705 | histogram(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 |