]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/histogram/axis/regular.hpp
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / boost / boost / histogram / axis / regular.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_AXIS_REGULAR_HPP
8#define BOOST_HISTOGRAM_AXIS_REGULAR_HPP
9
10#include <boost/assert.hpp>
11#include <boost/core/nvp.hpp>
12#include <boost/histogram/axis/interval_view.hpp>
13#include <boost/histogram/axis/iterator.hpp>
14#include <boost/histogram/axis/metadata_base.hpp>
15#include <boost/histogram/axis/option.hpp>
16#include <boost/histogram/detail/convert_integer.hpp>
17#include <boost/histogram/detail/relaxed_equal.hpp>
18#include <boost/histogram/detail/replace_type.hpp>
19#include <boost/histogram/fwd.hpp>
20#include <boost/mp11/utility.hpp>
21#include <boost/throw_exception.hpp>
22#include <cmath>
23#include <limits>
24#include <stdexcept>
25#include <string>
26#include <type_traits>
27#include <utility>
28
29namespace boost {
30namespace histogram {
31namespace detail {
32
33template <class T>
34using get_scale_type_helper = typename T::value_type;
35
36template <class T>
37using get_scale_type = mp11::mp_eval_or<T, detail::get_scale_type_helper, T>;
38
39struct one_unit {};
40
41template <class T>
42T operator*(T&& t, const one_unit&) {
43 return std::forward<T>(t);
44}
45
46template <class T>
47T operator/(T&& t, const one_unit&) {
48 return std::forward<T>(t);
49}
50
51template <class T>
52using get_unit_type_helper = typename T::unit_type;
53
54template <class T>
55using get_unit_type = mp11::mp_eval_or<one_unit, detail::get_unit_type_helper, T>;
56
57template <class T, class R = get_scale_type<T>>
58R get_scale(const T& t) {
59 return t / get_unit_type<T>();
60}
61
62} // namespace detail
63
64namespace axis {
65
66namespace transform {
67
68/// Identity transform for equidistant bins.
69struct id {
70 /// Pass-through.
71 template <class T>
72 static T forward(T&& x) noexcept {
73 return std::forward<T>(x);
74 }
75
76 /// Pass-through.
77 template <class T>
78 static T inverse(T&& x) noexcept {
79 return std::forward<T>(x);
80 }
81
82 template <class Archive>
83 void serialize(Archive&, unsigned /* version */) {}
84};
85
86/// Log transform for equidistant bins in log-space.
87struct log {
88 /// Returns log(x) of external value x.
89 template <class T>
90 static T forward(T x) {
91 return std::log(x);
92 }
93
94 /// Returns exp(x) for internal value x.
95 template <class T>
96 static T inverse(T x) {
97 return std::exp(x);
98 }
99
100 template <class Archive>
101 void serialize(Archive&, unsigned /* version */) {}
102};
103
104/// Sqrt transform for equidistant bins in sqrt-space.
105struct sqrt {
106 /// Returns sqrt(x) of external value x.
107 template <class T>
108 static T forward(T x) {
109 return std::sqrt(x);
110 }
111
112 /// Returns x^2 of internal value x.
113 template <class T>
114 static T inverse(T x) {
115 return x * x;
116 }
117
118 template <class Archive>
119 void serialize(Archive&, unsigned /* version */) {}
120};
121
122/// Pow transform for equidistant bins in pow-space.
123struct pow {
124 double power = 1; /**< power index */
125
126 /// Make transform with index p.
127 explicit pow(double p) : power(p) {}
128 pow() = default;
129
130 /// Returns pow(x, power) of external value x.
131 template <class T>
132 auto forward(T x) const {
133 return std::pow(x, power);
134 }
135
136 /// Returns pow(x, 1/power) of external value x.
137 template <class T>
138 auto inverse(T x) const {
139 return std::pow(x, 1.0 / power);
140 }
141
142 bool operator==(const pow& o) const noexcept { return power == o.power; }
143
144 template <class Archive>
145 void serialize(Archive& ar, unsigned /* version */) {
146 ar& make_nvp("power", power);
147 }
148};
149
150} // namespace transform
151
152#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
153// Type envelope to mark value as step size
154template <class T>
155struct step_type {
156 T value;
157};
158#endif
159
160/**
161 Helper function to mark argument as step size.
162 */
163template <class T>
164step_type<T> step(T t) {
165 return step_type<T>{t};
166}
167
168/**
169 Axis for equidistant intervals on the real line.
170
171 The most common binning strategy. Very fast. Binning is a O(1) operation.
172
173 @tparam Value input value type, must be floating point.
174 @tparam Transform builtin or user-defined transform type.
175 @tparam MetaData type to store meta data.
176 @tparam Options see boost::histogram::axis::option (all values allowed).
177 */
178template <class Value, class Transform, class MetaData, class Options>
179class regular : public iterator_mixin<regular<Value, Transform, MetaData, Options>>,
180 protected detail::replace_default<Transform, transform::id>,
181 public metadata_base<MetaData> {
f67539c2 182 // these must be private, so that they are not automatically inherited
92f5a8d4
TL
183 using value_type = Value;
184 using transform_type = detail::replace_default<Transform, transform::id>;
185 using metadata_type = typename metadata_base<MetaData>::metadata_type;
186 using options_type =
187 detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
188
189 static_assert(std::is_nothrow_move_constructible<transform_type>::value,
190 "transform must be no-throw move constructible");
191 static_assert(std::is_nothrow_move_assignable<transform_type>::value,
192 "transform must be no-throw move assignable");
193
194 using unit_type = detail::get_unit_type<value_type>;
195 using internal_value_type = detail::get_scale_type<value_type>;
196
197 static_assert(std::is_floating_point<internal_value_type>::value,
198 "regular axis requires floating point type");
199
200 static_assert(
201 (!options_type::test(option::circular) && !options_type::test(option::growth)) ||
202 (options_type::test(option::circular) ^ options_type::test(option::growth)),
203 "circular and growth options are mutually exclusive");
204
205public:
206 constexpr regular() = default;
207
208 /** Construct n bins over real transformed range [start, stop).
209 *
210 * @param trans transform instance to use.
211 * @param n number of bins.
212 * @param start low edge of first bin.
213 * @param stop high edge of last bin.
214 * @param meta description of the axis (optional).
215 */
216 regular(transform_type trans, unsigned n, value_type start, value_type stop,
217 metadata_type meta = {})
218 : transform_type(std::move(trans))
219 , metadata_base<MetaData>(std::move(meta))
220 , size_(static_cast<index_type>(n))
221 , min_(this->forward(detail::get_scale(start)))
222 , delta_(this->forward(detail::get_scale(stop)) - min_) {
223 if (size() == 0) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 0 required"));
224 if (!std::isfinite(min_) || !std::isfinite(delta_))
225 BOOST_THROW_EXCEPTION(
226 std::invalid_argument("forward transform of start or stop invalid"));
227 if (delta_ == 0)
228 BOOST_THROW_EXCEPTION(std::invalid_argument("range of axis is zero"));
229 }
230
231 /** Construct n bins over real range [start, stop).
232 *
233 * @param n number of bins.
234 * @param start low edge of first bin.
235 * @param stop high edge of last bin.
236 * @param meta description of the axis (optional).
237 */
238 regular(unsigned n, value_type start, value_type stop, metadata_type meta = {})
239 : regular({}, n, start, stop, std::move(meta)) {}
240
241 /** Construct bins with the given step size over real transformed range
242 * [start, stop).
243 *
244 * @param trans transform instance to use.
245 * @param step width of a single bin.
246 * @param start low edge of first bin.
247 * @param stop upper limit of high edge of last bin (see below).
248 * @param meta description of the axis (optional).
249 *
250 * The axis computes the number of bins as n = abs(stop - start) / step,
251 * rounded down. This means that stop is an upper limit to the actual value
252 * (start + n * step).
253 */
254 template <class T>
255 regular(transform_type trans, step_type<T> step, value_type start, value_type stop,
256 metadata_type meta = {})
257 : regular(trans, static_cast<index_type>(std::abs(stop - start) / step.value),
258 start,
259 start + static_cast<index_type>(std::abs(stop - start) / step.value) *
260 step.value,
261 std::move(meta)) {}
262
263 /** Construct bins with the given step size over real range [start, stop).
264 *
265 * @param step width of a single bin.
266 * @param start low edge of first bin.
267 * @param stop upper limit of high edge of last bin (see below).
268 * @param meta description of the axis (optional).
269 *
270 * The axis computes the number of bins as n = abs(stop - start) / step,
271 * rounded down. This means that stop is an upper limit to the actual value
272 * (start + n * step).
273 */
274 template <class T>
275 regular(step_type<T> step, value_type start, value_type stop, metadata_type meta = {})
276 : regular({}, step, start, stop, std::move(meta)) {}
277
278 /// Constructor used by algorithm::reduce to shrink and rebin (not for users).
279 regular(const regular& src, index_type begin, index_type end, unsigned merge)
280 : regular(src.transform(), (end - begin) / merge, src.value(begin), src.value(end),
281 src.metadata()) {
282 BOOST_ASSERT((end - begin) % merge == 0);
283 if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
284 BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
285 }
286
287 /// Return instance of the transform type.
288 const transform_type& transform() const noexcept { return *this; }
289
290 /// Return index for value argument.
291 index_type index(value_type x) const noexcept {
292 // Runs in hot loop, please measure impact of changes
293 auto z = (this->forward(x / unit_type{}) - min_) / delta_;
294 if (options_type::test(option::circular)) {
295 if (std::isfinite(z)) {
296 z -= std::floor(z);
297 return static_cast<index_type>(z * size());
298 }
299 } else {
300 if (z < 1) {
301 if (z >= 0)
302 return static_cast<index_type>(z * size());
303 else
304 return -1;
305 }
306 }
307 return size(); // also returned if x is NaN
308 }
309
310 /// Returns index and shift (if axis has grown) for the passed argument.
f67539c2 311 std::pair<index_type, index_type> update(value_type x) noexcept {
92f5a8d4
TL
312 BOOST_ASSERT(options_type::test(option::growth));
313 const auto z = (this->forward(x / unit_type{}) - min_) / delta_;
314 if (z < 1) { // don't use i here!
315 if (z >= 0) {
316 const auto i = static_cast<axis::index_type>(z * size());
f67539c2 317 return {i, 0};
92f5a8d4
TL
318 }
319 if (z != -std::numeric_limits<internal_value_type>::infinity()) {
320 const auto stop = min_ + delta_;
321 const auto i = static_cast<axis::index_type>(std::floor(z * size()));
322 min_ += i * (delta_ / size());
323 delta_ = stop - min_;
324 size_ -= i;
f67539c2 325 return {0, -i};
92f5a8d4
TL
326 }
327 // z is -infinity
f67539c2 328 return {-1, 0};
92f5a8d4
TL
329 }
330 // z either beyond range, infinite, or NaN
331 if (z < std::numeric_limits<internal_value_type>::infinity()) {
332 const auto i = static_cast<axis::index_type>(z * size());
333 const auto n = i - size() + 1;
334 delta_ /= size();
335 delta_ *= size() + n;
336 size_ += n;
f67539c2 337 return {i, -n};
92f5a8d4
TL
338 }
339 // z either infinite or NaN
f67539c2 340 return {size(), 0};
92f5a8d4
TL
341 }
342
343 /// Return value for fractional index argument.
344 value_type value(real_index_type i) const noexcept {
345 auto z = i / size();
346 if (!options_type::test(option::circular) && z < 0.0)
347 z = -std::numeric_limits<internal_value_type>::infinity() * delta_;
348 else if (options_type::test(option::circular) || z <= 1.0)
349 z = (1.0 - z) * min_ + z * (min_ + delta_);
350 else {
351 z = std::numeric_limits<internal_value_type>::infinity() * delta_;
352 }
353 return static_cast<value_type>(this->inverse(z) * unit_type());
354 }
355
356 /// Return bin for index argument.
357 decltype(auto) bin(index_type idx) const noexcept {
358 return interval_view<regular>(*this, idx);
359 }
360
361 /// Returns the number of bins, without over- or underflow.
362 index_type size() const noexcept { return size_; }
363
364 /// Returns the options.
365 static constexpr unsigned options() noexcept { return options_type::value; }
366
367 template <class V, class T, class M, class O>
368 bool operator==(const regular<V, T, M, O>& o) const noexcept {
369 return detail::relaxed_equal(transform(), o.transform()) && size() == o.size() &&
370 min_ == o.min_ && delta_ == o.delta_ && metadata_base<MetaData>::operator==(o);
371 }
372 template <class V, class T, class M, class O>
373 bool operator!=(const regular<V, T, M, O>& o) const noexcept {
374 return !operator==(o);
375 }
376
377 template <class Archive>
378 void serialize(Archive& ar, unsigned /* version */) {
379 ar& make_nvp("transform", static_cast<transform_type&>(*this));
380 ar& make_nvp("size", size_);
381 ar& make_nvp("meta", this->metadata());
382 ar& make_nvp("min", min_);
383 ar& make_nvp("delta", delta_);
384 }
385
386private:
387 index_type size_{0};
388 internal_value_type min_{0}, delta_{1};
389
390 template <class V, class T, class M, class O>
391 friend class regular;
392};
393
394#if __cpp_deduction_guides >= 201606
395
396template <class T>
397regular(unsigned, T, T)
398 ->regular<detail::convert_integer<T, double>, transform::id, null_type>;
399
400template <class T, class M>
401regular(unsigned, T, T, M)
402 ->regular<detail::convert_integer<T, double>, transform::id,
403 detail::replace_cstring<std::decay_t<M>>>;
404
405template <class Tr, class T, class = detail::requires_transform<Tr, T>>
406regular(Tr, unsigned, T, T)->regular<detail::convert_integer<T, double>, Tr, null_type>;
407
408template <class Tr, class T, class M>
409regular(Tr, unsigned, T, T, M)
410 ->regular<detail::convert_integer<T, double>, Tr,
411 detail::replace_cstring<std::decay_t<M>>>;
412
413#endif
414
415/// Regular axis with circular option already set.
416template <class Value = double, class MetaData = use_default, class Options = use_default>
417#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
418using circular = regular<Value, transform::id, MetaData,
419 decltype(detail::replace_default<Options, option::overflow_t>{} |
420 option::circular)>;
421#else
422class circular;
423#endif
424
425} // namespace axis
426} // namespace histogram
427} // namespace boost
428
429#endif