]>
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_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> | |
20effc67 | 16 | #include <boost/histogram/detail/relaxed_equal.hpp> |
92f5a8d4 TL |
17 | #include <boost/histogram/detail/replace_type.hpp> |
18 | #include <boost/histogram/detail/static_if.hpp> | |
19 | #include <boost/histogram/fwd.hpp> | |
20 | #include <boost/throw_exception.hpp> | |
21 | #include <cmath> | |
22 | #include <limits> | |
23 | #include <stdexcept> | |
24 | #include <string> | |
25 | #include <type_traits> | |
26 | #include <utility> | |
27 | ||
28 | namespace boost { | |
29 | namespace histogram { | |
30 | namespace axis { | |
31 | ||
1e59de90 | 32 | /** Axis for an interval of integer values with unit steps. |
92f5a8d4 | 33 | |
1e59de90 | 34 | Binning is a O(1) operation. This axis bins faster than a regular axis. |
92f5a8d4 | 35 | |
1e59de90 TL |
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. | |
92f5a8d4 TL |
39 | */ |
40 | template <class Value, class MetaData, class Options> | |
41 | class integer : public iterator_mixin<integer<Value, MetaData, Options>>, | |
20effc67 | 42 | public metadata_base_t<MetaData> { |
f67539c2 | 43 | // these must be private, so that they are not automatically inherited |
92f5a8d4 | 44 | using value_type = Value; |
20effc67 TL |
45 | using metadata_base = metadata_base_t<MetaData>; |
46 | using metadata_type = typename metadata_base::metadata_type; | |
92f5a8d4 TL |
47 | using options_type = |
48 | detail::replace_default<Options, decltype(option::underflow | option::overflow)>; | |
49 | ||
f67539c2 TL |
50 | static_assert(std::is_integral<value_type>::value || |
51 | std::is_floating_point<value_type>::value, | |
52 | "integer axis requires floating point or integral type"); | |
53 | ||
92f5a8d4 TL |
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 | ||
f67539c2 TL |
67 | using local_index_type = std::conditional_t<std::is_integral<value_type>::value, |
68 | index_type, real_index_type>; | |
69 | ||
92f5a8d4 TL |
70 | public: |
71 | constexpr integer() = default; | |
72 | ||
73 | /** Construct over semi-open integer interval [start, stop). | |
1e59de90 TL |
74 | |
75 | @param start first integer of covered range. | |
76 | @param stop one past last integer of covered range. | |
77 | @param meta description of the axis (optional). | |
78 | @param options see boost::histogram::axis::option (optional). | |
92f5a8d4 | 79 | */ |
1e59de90 TL |
80 | integer(value_type start, value_type stop, metadata_type meta = {}, |
81 | options_type options = {}) | |
20effc67 | 82 | : metadata_base(std::move(meta)) |
92f5a8d4 TL |
83 | , size_(static_cast<index_type>(stop - start)) |
84 | , min_(start) { | |
1e59de90 | 85 | (void)options; |
92f5a8d4 TL |
86 | if (!(stop >= start)) |
87 | BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start required")); | |
88 | } | |
89 | ||
90 | /// Constructor used by algorithm::reduce to shrink and rebin. | |
91 | integer(const integer& src, index_type begin, index_type end, unsigned merge) | |
92 | : integer(src.value(begin), src.value(end), src.metadata()) { | |
93 | if (merge > 1) | |
94 | BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for integer axis")); | |
95 | if (options_type::test(option::circular) && !(begin == 0 && end == src.size())) | |
96 | BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis")); | |
97 | } | |
98 | ||
99 | /// Return index for value argument. | |
100 | index_type index(value_type x) const noexcept { | |
20effc67 TL |
101 | return index_impl(options_type::test(axis::option::circular), |
102 | std::is_floating_point<value_type>{}, | |
103 | static_cast<double>(x - min_)); | |
92f5a8d4 TL |
104 | } |
105 | ||
106 | /// Returns index and shift (if axis has grown) for the passed argument. | |
107 | auto update(value_type x) noexcept { | |
f67539c2 | 108 | auto impl = [this](long x) -> std::pair<index_type, index_type> { |
92f5a8d4 TL |
109 | const auto i = x - min_; |
110 | if (i >= 0) { | |
111 | const auto k = static_cast<axis::index_type>(i); | |
f67539c2 | 112 | if (k < size()) return {k, 0}; |
92f5a8d4 TL |
113 | const auto n = k - size() + 1; |
114 | size_ += n; | |
f67539c2 | 115 | return {k, -n}; |
92f5a8d4 TL |
116 | } |
117 | const auto k = static_cast<axis::index_type>( | |
118 | detail::static_if<std::is_floating_point<value_type>>( | |
119 | [](auto x) { return std::floor(x); }, [](auto x) { return x; }, i)); | |
120 | min_ += k; | |
121 | size_ -= k; | |
f67539c2 | 122 | return {0, -k}; |
92f5a8d4 TL |
123 | }; |
124 | ||
125 | return detail::static_if<std::is_floating_point<value_type>>( | |
f67539c2 | 126 | [this, impl](auto x) -> std::pair<index_type, index_type> { |
92f5a8d4 | 127 | if (std::isfinite(x)) return impl(static_cast<long>(std::floor(x))); |
f67539c2 | 128 | return {x < 0 ? -1 : this->size(), 0}; |
92f5a8d4 TL |
129 | }, |
130 | impl, x); | |
131 | } | |
132 | ||
133 | /// Return value for index argument. | |
134 | value_type value(local_index_type i) const noexcept { | |
135 | if (!options_type::test(option::circular) && | |
136 | std::is_floating_point<value_type>::value) { | |
137 | if (i < 0) return detail::lowest<value_type>(); | |
138 | if (i > size()) return detail::highest<value_type>(); | |
139 | } | |
140 | return min_ + i; | |
141 | } | |
142 | ||
143 | /// Return bin for index argument. | |
144 | decltype(auto) bin(index_type idx) const noexcept { | |
145 | return detail::static_if<std::is_floating_point<value_type>>( | |
146 | [this](auto idx) { return interval_view<integer>(*this, idx); }, | |
147 | [this](auto idx) { return this->value(idx); }, idx); | |
148 | } | |
149 | ||
150 | /// Returns the number of bins, without over- or underflow. | |
151 | index_type size() const noexcept { return size_; } | |
152 | ||
153 | /// Returns the options. | |
154 | static constexpr unsigned options() noexcept { return options_type::value; } | |
155 | ||
156 | /// Whether the axis is inclusive (see axis::traits::is_inclusive). | |
157 | static constexpr bool inclusive() noexcept { | |
1e59de90 TL |
158 | // If axis has underflow and overflow, it is inclusive. |
159 | // If axis is growing or circular: | |
160 | // - it is inclusive if value_type is int. | |
161 | // - it is not inclusive if value_type is float, because of nan and inf. | |
162 | constexpr bool full_flow = | |
163 | options() & option::underflow && options() & option::overflow; | |
164 | return full_flow || (std::is_integral<value_type>::value && | |
165 | (options() & (option::growth | option::circular))); | |
92f5a8d4 TL |
166 | } |
167 | ||
168 | template <class V, class M, class O> | |
169 | bool operator==(const integer<V, M, O>& o) const noexcept { | |
20effc67 TL |
170 | return size() == o.size() && min_ == o.min_ && |
171 | detail::relaxed_equal{}(this->metadata(), o.metadata()); | |
92f5a8d4 TL |
172 | } |
173 | ||
174 | template <class V, class M, class O> | |
175 | bool operator!=(const integer<V, M, O>& o) const noexcept { | |
176 | return !operator==(o); | |
177 | } | |
178 | ||
179 | template <class Archive> | |
180 | void serialize(Archive& ar, unsigned /* version */) { | |
181 | ar& make_nvp("size", size_); | |
182 | ar& make_nvp("meta", this->metadata()); | |
183 | ar& make_nvp("min", min_); | |
184 | } | |
185 | ||
186 | private: | |
20effc67 TL |
187 | // axis not circular |
188 | template <class B> | |
189 | index_type index_impl(std::false_type, B, double z) const noexcept { | |
190 | if (z < size()) return z >= 0 ? static_cast<index_type>(z) : -1; | |
191 | return size(); | |
192 | } | |
193 | ||
194 | // value_type is integer, axis circular | |
195 | index_type index_impl(std::true_type, std::false_type, double z) const noexcept { | |
196 | return static_cast<index_type>(z - std::floor(z / size()) * size()); | |
197 | } | |
198 | ||
199 | // value_type is floating point, must handle +/-infinite or nan, axis circular | |
200 | index_type index_impl(std::true_type, std::true_type, double z) const noexcept { | |
201 | if (std::isfinite(z)) return index_impl(std::true_type{}, std::false_type{}, z); | |
202 | return z < size() ? -1 : size(); | |
203 | } | |
204 | ||
92f5a8d4 TL |
205 | index_type size_{0}; |
206 | value_type min_{0}; | |
207 | ||
208 | template <class V, class M, class O> | |
209 | friend class integer; | |
210 | }; | |
211 | ||
212 | #if __cpp_deduction_guides >= 201606 | |
213 | ||
214 | template <class T> | |
1e59de90 | 215 | integer(T, T) -> integer<detail::convert_integer<T, index_type>, null_type>; |
92f5a8d4 TL |
216 | |
217 | template <class T, class M> | |
218 | integer(T, T, M) | |
1e59de90 TL |
219 | -> integer<detail::convert_integer<T, index_type>, |
220 | detail::replace_type<std::decay_t<M>, const char*, std::string>>; | |
221 | ||
222 | template <class T, class M, unsigned B> | |
223 | integer(T, T, M, const option::bitset<B>&) | |
224 | -> integer<detail::convert_integer<T, index_type>, | |
225 | detail::replace_type<std::decay_t<M>, const char*, std::string>, | |
226 | option::bitset<B>>; | |
92f5a8d4 TL |
227 | |
228 | #endif | |
229 | ||
230 | } // namespace axis | |
231 | } // namespace histogram | |
232 | } // namespace boost | |
233 | ||
234 | #endif |