]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/histogram/detail/fill.hpp
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / boost / boost / histogram / detail / fill.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_DETAIL_FILL_HPP
8#define BOOST_HISTOGRAM_DETAIL_FILL_HPP
9
10#include <algorithm>
11#include <boost/assert.hpp>
12#include <boost/config/workaround.hpp>
13#include <boost/histogram/axis/traits.hpp>
14#include <boost/histogram/axis/variant.hpp>
92f5a8d4
TL
15#include <boost/histogram/detail/argument_traits.hpp>
16#include <boost/histogram/detail/axes.hpp>
17#include <boost/histogram/detail/linearize.hpp>
18#include <boost/histogram/detail/make_default.hpp>
19#include <boost/histogram/detail/optional_index.hpp>
f67539c2 20#include <boost/histogram/detail/priority.hpp>
92f5a8d4
TL
21#include <boost/histogram/detail/tuple_slice.hpp>
22#include <boost/histogram/fwd.hpp>
23#include <boost/mp11/algorithm.hpp>
24#include <boost/mp11/integral.hpp>
25#include <boost/mp11/tuple.hpp>
26#include <boost/mp11/utility.hpp>
27#include <mutex>
28#include <tuple>
29#include <type_traits>
30
31namespace boost {
32namespace histogram {
33namespace detail {
34
35template <class T, class U>
36struct sample_args_passed_vs_expected;
37
38template <class... Passed, class... Expected>
39struct sample_args_passed_vs_expected<std::tuple<Passed...>, std::tuple<Expected...>> {
40 static_assert(!(sizeof...(Expected) > 0 && sizeof...(Passed) == 0),
41 "error: accumulator requires samples, but sample argument is missing");
42 static_assert(
43 !(sizeof...(Passed) > 0 && sizeof...(Expected) == 0),
44 "error: accumulator does not accept samples, but sample argument is passed");
45 static_assert(sizeof...(Passed) == sizeof...(Expected),
46 "error: numbers of passed and expected sample arguments differ");
47 static_assert(
48 std::is_convertible<std::tuple<Passed...>, std::tuple<Expected...>>::value,
49 "error: sample argument(s) not convertible to accumulator argument(s)");
50};
51
52template <class A>
53struct storage_grower {
54 const A& axes_;
55 struct {
56 axis::index_type idx, old_extent;
57 std::size_t new_stride;
58 } data_[buffer_size<A>::value];
59 std::size_t new_size_;
60
61 storage_grower(const A& axes) noexcept : axes_(axes) {}
62
63 void from_shifts(const axis::index_type* shifts) noexcept {
64 auto dit = data_;
65 std::size_t s = 1;
66 for_each_axis(axes_, [&](const auto& a) {
67 const auto n = axis::traits::extent(a);
68 *dit++ = {0, n - std::abs(*shifts++), s};
69 s *= n;
70 });
71 new_size_ = s;
72 }
73
74 // must be extents before any shifts were applied
75 void from_extents(const axis::index_type* old_extents) noexcept {
76 auto dit = data_;
77 std::size_t s = 1;
78 for_each_axis(axes_, [&](const auto& a) {
79 const auto n = axis::traits::extent(a);
80 *dit++ = {0, *old_extents++, s};
81 s *= n;
82 });
83 new_size_ = s;
84 }
85
86 template <class S>
87 void apply(S& storage, const axis::index_type* shifts) {
88 auto new_storage = make_default(storage);
89 new_storage.reset(new_size_);
90 const auto dlast = data_ + axes_rank(axes_) - 1;
91 for (const auto& x : storage) {
92 auto ns = new_storage.begin();
93 auto sit = shifts;
94 auto dit = data_;
95 for_each_axis(axes_, [&](const auto& a) {
f67539c2 96 using opt = axis::traits::get_options<std::decay_t<decltype(a)>>;
92f5a8d4
TL
97 if (opt::test(axis::option::underflow)) {
98 if (dit->idx == 0) {
99 // axis has underflow and we are in the underflow bin:
100 // keep storage pointer unchanged
101 ++dit;
102 ++sit;
103 return;
104 }
105 }
106 if (opt::test(axis::option::overflow)) {
107 if (dit->idx == dit->old_extent - 1) {
108 // axis has overflow and we are in the overflow bin:
109 // move storage pointer to corresponding overflow bin position
110 ns += (axis::traits::extent(a) - 1) * dit->new_stride;
111 ++dit;
112 ++sit;
113 return;
114 }
115 }
116 // we are in a normal bin:
f67539c2
TL
117 // move storage pointer to index position; apply positive shifts if any
118 ns += (dit->idx + (*sit >= 0 ? *sit : 0)) * dit->new_stride;
92f5a8d4
TL
119 ++dit;
120 ++sit;
121 });
122 // assign old value to new location
123 *ns = x;
124 // advance multi-dimensional index
125 dit = data_;
126 ++dit->idx;
127 while (dit != dlast && dit->idx == dit->old_extent) {
128 dit->idx = 0;
129 ++(++dit)->idx;
130 }
131 }
132 storage = std::move(new_storage);
133 }
134};
135
136template <class T, class... Us>
f67539c2
TL
137auto fill_storage_element_impl(priority<2>, T&& t, const Us&... args) noexcept
138 -> decltype(t(args...), void()) {
139 t(args...);
92f5a8d4
TL
140}
141
f67539c2
TL
142template <class T, class U>
143auto fill_storage_element_impl(priority<1>, T&& t, const weight_type<U>& w) noexcept
144 -> decltype(t += w, void()) {
145 t += w;
92f5a8d4
TL
146}
147
f67539c2 148// fallback for arithmetic types and accumulators that do not handle the weight
92f5a8d4 149template <class T, class U>
f67539c2
TL
150auto fill_storage_element_impl(priority<0>, T&& t, const weight_type<U>& w) noexcept
151 -> decltype(t += w.value, void()) {
92f5a8d4
TL
152 t += w.value;
153}
154
f67539c2
TL
155template <class T>
156auto fill_storage_element_impl(priority<1>, T&& t) noexcept -> decltype(++t, void()) {
157 ++t;
158}
159
92f5a8d4 160template <class T, class... Us>
f67539c2
TL
161void fill_storage_element(T&& t, const Us&... args) noexcept {
162 fill_storage_element_impl(priority<2>{}, std::forward<T>(t), args...);
92f5a8d4
TL
163}
164
f67539c2 165// t may be a proxy and then it is an rvalue reference, not an lvalue reference
92f5a8d4
TL
166template <class IW, class IS, class T, class U>
167void fill_storage_2(IW, IS, T&& t, U&& u) noexcept {
168 mp11::tuple_apply(
f67539c2
TL
169 [&](const auto&... args) {
170 fill_storage_element(std::forward<T>(t), std::get<IW::value>(u), args...);
92f5a8d4
TL
171 },
172 std::get<IS::value>(u).value);
173}
174
f67539c2 175// t may be a proxy and then it is an rvalue reference, not an lvalue reference
92f5a8d4
TL
176template <class IS, class T, class U>
177void fill_storage_2(mp11::mp_int<-1>, IS, T&& t, const U& u) noexcept {
178 mp11::tuple_apply(
f67539c2 179 [&](const auto&... args) { fill_storage_element(std::forward<T>(t), args...); },
92f5a8d4
TL
180 std::get<IS::value>(u).value);
181}
182
f67539c2 183// t may be a proxy and then it is an rvalue reference, not an lvalue reference
92f5a8d4
TL
184template <class IW, class T, class U>
185void fill_storage_2(IW, mp11::mp_int<-1>, T&& t, const U& u) noexcept {
f67539c2 186 fill_storage_element(std::forward<T>(t), std::get<IW::value>(u));
92f5a8d4
TL
187}
188
f67539c2 189// t may be a proxy and then it is an rvalue reference, not an lvalue reference
92f5a8d4
TL
190template <class T, class U>
191void fill_storage_2(mp11::mp_int<-1>, mp11::mp_int<-1>, T&& t, const U&) noexcept {
f67539c2 192 fill_storage_element(std::forward<T>(t));
92f5a8d4
TL
193}
194
195template <class IW, class IS, class Storage, class Index, class Args>
196auto fill_storage(IW, IS, Storage& s, const Index idx, const Args& a) noexcept {
197 if (is_valid(idx)) {
198 BOOST_ASSERT(idx < s.size());
199 fill_storage_2(IW{}, IS{}, s[idx], a);
200 return s.begin() + idx;
201 }
202 return s.end();
203}
204
205template <int S, int N>
206struct linearize_args {
207 template <class Index, class A, class Args>
208 static void impl(mp11::mp_int<N>, Index&, const std::size_t, A&, const Args&) {}
209
210 template <int I, class Index, class A, class Args>
211 static void impl(mp11::mp_int<I>, Index& o, const std::size_t s, A& ax,
212 const Args& args) {
213 const auto e = linearize(o, s, axis_get<I>(ax), std::get<(S + I)>(args));
214 impl(mp11::mp_int<(I + 1)>{}, o, s * e, ax, args);
215 }
216
217 template <class Index, class A, class Args>
218 static void apply(Index& o, A& ax, const Args& args) {
219 impl(mp11::mp_int<0>{}, o, 1, ax, args);
220 }
221};
222
223template <int S>
224struct linearize_args<S, 1> {
225 template <class Index, class A, class Args>
226 static void apply(Index& o, A& ax, const Args& args) {
227 linearize(o, 1, axis_get<0>(ax), std::get<S>(args));
228 }
229};
230
231template <class A>
232constexpr unsigned min(const unsigned n) noexcept {
233 constexpr unsigned a = static_cast<unsigned>(buffer_size<A>::value);
234 return a < n ? a : n;
235}
236
237// not growing
238template <class ArgTraits, class Storage, class Axes, class Args>
239auto fill_2(ArgTraits, mp11::mp_false, const std::size_t offset, Storage& st,
240 const Axes& axes, const Args& args) {
241 mp11::mp_if<has_non_inclusive_axis<Axes>, optional_index, std::size_t> idx{offset};
242 linearize_args<ArgTraits::start::value, min<Axes>(ArgTraits::nargs::value)>::apply(
243 idx, axes, args);
244 return fill_storage(typename ArgTraits::wpos{}, typename ArgTraits::spos{}, st, idx,
245 args);
246}
247
248// at least one axis is growing
249template <class ArgTraits, class Storage, class Axes, class Args>
250auto fill_2(ArgTraits, mp11::mp_true, const std::size_t, Storage& st, Axes& axes,
251 const Args& args) {
252 std::array<axis::index_type, ArgTraits::nargs::value> shifts;
253 // offset must be zero for linearize_growth
254 mp11::mp_if<has_non_inclusive_axis<Axes>, optional_index, std::size_t> idx{0};
255 std::size_t stride = 1;
256 bool update_needed = false;
257 mp11::mp_for_each<mp11::mp_iota_c<min<Axes>(ArgTraits::nargs::value)>>([&](auto i) {
258 auto& ax = axis_get<i>(axes);
259 const auto extent = linearize_growth(idx, shifts[i], stride, ax,
260 std::get<(ArgTraits::start::value + i)>(args));
261 update_needed |= shifts[i] != 0;
262 stride *= extent;
263 });
264 if (update_needed) {
265 storage_grower<Axes> g(axes);
266 g.from_shifts(shifts.data());
267 g.apply(st, shifts.data());
268 }
269 return fill_storage(typename ArgTraits::wpos{}, typename ArgTraits::spos{}, st, idx,
270 args);
271}
272
273// pack original args tuple into another tuple (which is unpacked later)
274template <int Start, int Size, class IW, class IS, class Args>
275decltype(auto) pack_args(IW, IS, const Args& args) noexcept {
276 return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IW::value>(args),
277 std::get<IS::value>(args));
278}
279
280template <int Start, int Size, class IW, class Args>
281decltype(auto) pack_args(IW, mp11::mp_int<-1>, const Args& args) noexcept {
282 return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IW::value>(args));
283}
284
285template <int Start, int Size, class IS, class Args>
286decltype(auto) pack_args(mp11::mp_int<-1>, IS, const Args& args) noexcept {
287 return std::make_tuple(tuple_slice<Start, Size>(args), std::get<IS::value>(args));
288}
289
290template <int Start, int Size, class Args>
291decltype(auto) pack_args(mp11::mp_int<-1>, mp11::mp_int<-1>, const Args& args) noexcept {
292 return std::make_tuple(args);
293}
294
295#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
296#pragma warning(disable : 4702) // fixing warning would reduce code readability a lot
297#endif
298
299template <class ArgTraits, class S, class A, class Args>
300auto fill(std::true_type, ArgTraits, const std::size_t offset, S& storage, A& axes,
f67539c2 301 const Args& args) -> typename S::iterator {
92f5a8d4
TL
302 using growing = has_growing_axis<A>;
303
304 // Sometimes we need to pack the tuple into another tuple:
305 // - histogram contains one axis which accepts tuple
306 // - user passes tuple to fill(...)
307 // Tuple is normally unpacked and arguments are processed, this causes pos::nargs > 1.
308 // Now we pack tuple into another tuple so that original tuple is send to axis.
309 // Notes:
310 // - has nice side-effect of making histogram::operator(1, 2) work as well
311 // - cannot detect call signature of axis at compile-time in all configurations
312 // (axis::variant provides generic call interface and hides concrete
313 // interface), so we throw at runtime if incompatible argument is passed (e.g.
314 // 3d tuple)
315
316 if (axes_rank(axes) == ArgTraits::nargs::value)
317 return fill_2(ArgTraits{}, growing{}, offset, storage, axes, args);
318 else if (axes_rank(axes) == 1 &&
319 axis::traits::rank(axis_get<0>(axes)) == ArgTraits::nargs::value)
320 return fill_2(
321 argument_traits_holder<
322 1, 0, (ArgTraits::wpos::value >= 0 ? 1 : -1),
323 (ArgTraits::spos::value >= 0 ? (ArgTraits::wpos::value >= 0 ? 2 : 1) : -1),
324 typename ArgTraits::sargs>{},
325 growing{}, offset, storage, axes,
326 pack_args<ArgTraits::start::value, ArgTraits::nargs::value>(
327 typename ArgTraits::wpos{}, typename ArgTraits::spos{}, args));
328 return (BOOST_THROW_EXCEPTION(
329 std::invalid_argument("number of arguments != histogram rank")),
330 storage.end());
331}
332
333#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
334#pragma warning(default : 4702)
335#endif
336
337// empty implementation for bad arguments to stop compiler from showing internals
338template <class ArgTraits, class S, class A, class Args>
f67539c2
TL
339auto fill(std::false_type, ArgTraits, const std::size_t, S& storage, A&, const Args&) ->
340 typename S::iterator {
92f5a8d4
TL
341 return storage.end();
342}
343
344} // namespace detail
345} // namespace histogram
346} // namespace boost
347
348#endif