]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/histogram/axis/integer.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / histogram / axis / integer.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_INTEGER_HPP
8#define BOOST_HISTOGRAM_AXIS_INTEGER_HPP
9
10#include <boost/core/nvp.hpp>
11#include <boost/histogram/axis/iterator.hpp>
12#include <boost/histogram/axis/metadata_base.hpp>
13#include <boost/histogram/axis/option.hpp>
14#include <boost/histogram/detail/convert_integer.hpp>
15#include <boost/histogram/detail/limits.hpp>
16#include <boost/histogram/detail/replace_type.hpp>
17#include <boost/histogram/detail/static_if.hpp>
18#include <boost/histogram/fwd.hpp>
19#include <boost/throw_exception.hpp>
20#include <cmath>
21#include <limits>
22#include <stdexcept>
23#include <string>
24#include <type_traits>
25#include <utility>
26
27namespace boost {
28namespace histogram {
29namespace axis {
30
31/**
32 Axis for an interval of integer values with unit steps.
33
34 Binning is a O(1) operation. This axis bins faster than a regular axis.
35
36 @tparam Value input value type. Must be integer or floating point.
37 @tparam MetaData type to store meta data.
38 @tparam Options see boost::histogram::axis::option (all values allowed).
39 */
40template <class Value, class MetaData, class Options>
41class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
42 public metadata_base<MetaData> {
43 static_assert(std::is_integral<Value>::value || std::is_floating_point<Value>::value,
44 "integer axis requires floating point or integral type");
45
46 using value_type = Value;
47 using local_index_type = std::conditional_t<std::is_integral<value_type>::value,
48 index_type, real_index_type>;
49
50 using metadata_type = typename metadata_base<MetaData>::metadata_type;
51 using options_type =
52 detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
53
54 static_assert(!options_type::test(option::circular | option::growth) ||
55 (options_type::test(option::circular) ^
56 options_type::test(option::growth)),
57 "circular and growth options are mutually exclusive");
58
59 static_assert(std::is_floating_point<value_type>::value ||
60 (!options_type::test(option::circular) &&
61 !options_type::test(option::growth)) ||
62 (!options_type::test(option::overflow) &&
63 !options_type::test(option::underflow)),
64 "circular or growing integer axis with integral type "
65 "cannot have entries in underflow or overflow bins");
66
67public:
68 constexpr integer() = default;
69
70 /** Construct over semi-open integer interval [start, stop).
71 *
72 * \param start first integer of covered range.
73 * \param stop one past last integer of covered range.
74 * \param meta description of the axis.
75 */
76 integer(value_type start, value_type stop, metadata_type meta = {})
77 : metadata_base<MetaData>(std::move(meta))
78 , size_(static_cast<index_type>(stop - start))
79 , min_(start) {
80 if (!(stop >= start))
81 BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start required"));
82 }
83
84 /// Constructor used by algorithm::reduce to shrink and rebin.
85 integer(const integer& src, index_type begin, index_type end, unsigned merge)
86 : integer(src.value(begin), src.value(end), src.metadata()) {
87 if (merge > 1)
88 BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for integer axis"));
89 if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
90 BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
91 }
92
93 /// Return index for value argument.
94 index_type index(value_type x) const noexcept {
95 return index_impl(std::is_floating_point<value_type>(), x);
96 }
97
98 /// Returns index and shift (if axis has grown) for the passed argument.
99 auto update(value_type x) noexcept {
100 auto impl = [this](long x) {
101 const auto i = x - min_;
102 if (i >= 0) {
103 const auto k = static_cast<axis::index_type>(i);
104 if (k < size()) return std::make_pair(k, 0);
105 const auto n = k - size() + 1;
106 size_ += n;
107 return std::make_pair(k, -n);
108 }
109 const auto k = static_cast<axis::index_type>(
110 detail::static_if<std::is_floating_point<value_type>>(
111 [](auto x) { return std::floor(x); }, [](auto x) { return x; }, i));
112 min_ += k;
113 size_ -= k;
114 return std::make_pair(0, -k);
115 };
116
117 return detail::static_if<std::is_floating_point<value_type>>(
118 [this, impl](auto x) {
119 if (std::isfinite(x)) return impl(static_cast<long>(std::floor(x)));
120 return std::make_pair(x < 0 ? -1 : this->size(), 0);
121 },
122 impl, x);
123 }
124
125 /// Return value for index argument.
126 value_type value(local_index_type i) const noexcept {
127 if (!options_type::test(option::circular) &&
128 std::is_floating_point<value_type>::value) {
129 if (i < 0) return detail::lowest<value_type>();
130 if (i > size()) return detail::highest<value_type>();
131 }
132 return min_ + i;
133 }
134
135 /// Return bin for index argument.
136 decltype(auto) bin(index_type idx) const noexcept {
137 return detail::static_if<std::is_floating_point<value_type>>(
138 [this](auto idx) { return interval_view<integer>(*this, idx); },
139 [this](auto idx) { return this->value(idx); }, idx);
140 }
141
142 /// Returns the number of bins, without over- or underflow.
143 index_type size() const noexcept { return size_; }
144
145 /// Returns the options.
146 static constexpr unsigned options() noexcept { return options_type::value; }
147
148 /// Whether the axis is inclusive (see axis::traits::is_inclusive).
149 static constexpr bool inclusive() noexcept {
150 return (options() & option::underflow || options() & option::overflow) ||
151 (std::is_integral<value_type>::value &&
152 (options() & (option::growth | option::circular)));
153 }
154
155 template <class V, class M, class O>
156 bool operator==(const integer<V, M, O>& o) const noexcept {
157 return size() == o.size() && min_ == o.min_ && metadata_base<MetaData>::operator==(o);
158 }
159
160 template <class V, class M, class O>
161 bool operator!=(const integer<V, M, O>& o) const noexcept {
162 return !operator==(o);
163 }
164
165 template <class Archive>
166 void serialize(Archive& ar, unsigned /* version */) {
167 ar& make_nvp("size", size_);
168 ar& make_nvp("meta", this->metadata());
169 ar& make_nvp("min", min_);
170 }
171
172private:
173 index_type index_impl(std::false_type, int x) const noexcept {
174 const auto z = x - min_;
175 if (options_type::test(option::circular))
176 return static_cast<index_type>(z - std::floor(float(z) / size()) * size());
177 if (z < size()) return z >= 0 ? z : -1;
178 return size();
179 }
180
181 template <typename T>
182 index_type index_impl(std::true_type, T x) const noexcept {
183 // need to handle NaN, cannot simply cast to int and call int-implementation
184 const auto z = x - min_;
185 if (options_type::test(option::circular)) {
186 if (std::isfinite(z))
187 return static_cast<index_type>(std::floor(z) - std::floor(z / size()) * size());
188 } else if (z < size()) {
189 return z >= 0 ? static_cast<index_type>(z) : -1;
190 }
191 return size();
192 }
193
194 index_type size_{0};
195 value_type min_{0};
196
197 template <class V, class M, class O>
198 friend class integer;
199};
200
201#if __cpp_deduction_guides >= 201606
202
203template <class T>
204integer(T, T)->integer<detail::convert_integer<T, index_type>, null_type>;
205
206template <class T, class M>
207integer(T, T, M)
208 ->integer<detail::convert_integer<T, index_type>,
209 detail::replace_type<std::decay_t<M>, const char*, std::string>>;
210
211#endif
212
213} // namespace axis
214} // namespace histogram
215} // namespace boost
216
217#endif