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