]>
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_CATEGORY_HPP | |
8 | #define BOOST_HISTOGRAM_AXIS_CATEGORY_HPP | |
9 | ||
10 | #include <algorithm> | |
11 | #include <boost/core/nvp.hpp> | |
12 | #include <boost/histogram/axis/iterator.hpp> | |
13 | #include <boost/histogram/axis/metadata_base.hpp> | |
14 | #include <boost/histogram/axis/option.hpp> | |
20effc67 TL |
15 | #include <boost/histogram/detail/detect.hpp> |
16 | #include <boost/histogram/detail/relaxed_equal.hpp> | |
92f5a8d4 TL |
17 | #include <boost/histogram/fwd.hpp> |
18 | #include <boost/throw_exception.hpp> | |
19 | #include <stdexcept> | |
20 | #include <string> | |
21 | #include <type_traits> | |
22 | #include <utility> | |
23 | #include <vector> | |
24 | ||
25 | namespace boost { | |
26 | namespace histogram { | |
27 | namespace axis { | |
28 | ||
29 | /** | |
30 | Maps at a set of unique values to bin indices. | |
31 | ||
32 | The axis maps a set of values to bins, following the order of arguments in the | |
33 | constructor. The optional overflow bin for this axis counts input values that | |
34 | are not part of the set. Binning has O(N) complexity, but with a very small | |
35 | factor. For small N (the typical use case) it beats other kinds of lookup. | |
36 | ||
37 | @tparam Value input value type, must be equal-comparable. | |
38 | @tparam MetaData type to store meta data. | |
39 | @tparam Options see boost::histogram::axis::option. | |
40 | @tparam Allocator allocator to use for dynamic memory management. | |
41 | ||
42 | The options `underflow` and `circular` are not allowed. The options `growth` | |
43 | and `overflow` are mutually exclusive. | |
44 | */ | |
45 | template <class Value, class MetaData, class Options, class Allocator> | |
46 | class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>>, | |
20effc67 | 47 | public metadata_base_t<MetaData> { |
f67539c2 | 48 | // these must be private, so that they are not automatically inherited |
92f5a8d4 | 49 | using value_type = Value; |
20effc67 TL |
50 | using metadata_base = metadata_base_t<MetaData>; |
51 | using metadata_type = typename metadata_base::metadata_type; | |
92f5a8d4 TL |
52 | using options_type = detail::replace_default<Options, option::overflow_t>; |
53 | using allocator_type = Allocator; | |
54 | using vector_type = std::vector<value_type, allocator_type>; | |
55 | ||
56 | static_assert(!options_type::test(option::underflow), | |
57 | "category axis cannot have underflow"); | |
58 | static_assert(!options_type::test(option::circular), | |
59 | "category axis cannot be circular"); | |
60 | static_assert(!(options_type::test(option::growth) && | |
61 | options_type::test(option::overflow)), | |
62 | "growing category axis cannot have entries in overflow bin"); | |
63 | ||
64 | public: | |
65 | constexpr category() = default; | |
66 | explicit category(allocator_type alloc) : vec_(alloc) {} | |
67 | ||
68 | /** Construct from iterator range of unique values. | |
69 | * | |
70 | * \param begin begin of category range of unique values. | |
71 | * \param end end of category range of unique values. | |
72 | * \param meta description of the axis. | |
73 | * \param alloc allocator instance to use. | |
74 | */ | |
75 | template <class It, class = detail::requires_iterator<It>> | |
76 | category(It begin, It end, metadata_type meta = {}, allocator_type alloc = {}) | |
20effc67 | 77 | : metadata_base(std::move(meta)), vec_(alloc) { |
92f5a8d4 TL |
78 | if (std::distance(begin, end) < 0) |
79 | BOOST_THROW_EXCEPTION( | |
80 | std::invalid_argument("end must be reachable by incrementing begin")); | |
81 | vec_.reserve(std::distance(begin, end)); | |
82 | while (begin != end) vec_.emplace_back(*begin++); | |
83 | } | |
84 | ||
85 | /** Construct axis from iterable sequence of unique values. | |
86 | * | |
87 | * \param iterable sequence of unique values. | |
88 | * \param meta description of the axis. | |
89 | * \param alloc allocator instance to use. | |
90 | */ | |
91 | template <class C, class = detail::requires_iterable<C>> | |
92 | category(const C& iterable, metadata_type meta = {}, allocator_type alloc = {}) | |
93 | : category(std::begin(iterable), std::end(iterable), std::move(meta), | |
94 | std::move(alloc)) {} | |
95 | ||
96 | /** Construct axis from an initializer list of unique values. | |
97 | * | |
98 | * \param list `std::initializer_list` of unique values. | |
99 | * \param meta description of the axis. | |
100 | * \param alloc allocator instance to use. | |
101 | */ | |
102 | template <class U> | |
103 | category(std::initializer_list<U> list, metadata_type meta = {}, | |
104 | allocator_type alloc = {}) | |
105 | : category(list.begin(), list.end(), std::move(meta), std::move(alloc)) {} | |
106 | ||
f67539c2 TL |
107 | /// Constructor used by algorithm::reduce to shrink and rebin (not for users). |
108 | category(const category& src, index_type begin, index_type end, unsigned merge) | |
109 | // LCOV_EXCL_START: gcc-8 is missing the delegated ctor for no reason | |
110 | : category(src.vec_.begin() + begin, src.vec_.begin() + end, src.metadata(), | |
111 | src.get_allocator()) | |
112 | // LCOV_EXCL_STOP | |
113 | { | |
114 | if (merge > 1) | |
115 | BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for category axis")); | |
116 | } | |
117 | ||
92f5a8d4 TL |
118 | /// Return index for value argument. |
119 | index_type index(const value_type& x) const noexcept { | |
120 | const auto beg = vec_.begin(); | |
121 | const auto end = vec_.end(); | |
122 | return static_cast<index_type>(std::distance(beg, std::find(beg, end, x))); | |
123 | } | |
124 | ||
125 | /// Returns index and shift (if axis has grown) for the passed argument. | |
f67539c2 | 126 | std::pair<index_type, index_type> update(const value_type& x) { |
92f5a8d4 | 127 | const auto i = index(x); |
f67539c2 | 128 | if (i < size()) return {i, 0}; |
92f5a8d4 | 129 | vec_.emplace_back(x); |
f67539c2 | 130 | return {i, -1}; |
92f5a8d4 TL |
131 | } |
132 | ||
133 | /// Return value for index argument. | |
134 | /// Throws `std::out_of_range` if the index is out of bounds. | |
135 | auto value(index_type idx) const | |
136 | -> std::conditional_t<std::is_scalar<value_type>::value, value_type, | |
137 | const value_type&> { | |
138 | if (idx < 0 || idx >= size()) | |
139 | BOOST_THROW_EXCEPTION(std::out_of_range("category index out of range")); | |
140 | return vec_[idx]; | |
141 | } | |
142 | ||
f67539c2 TL |
143 | /// Return value for index argument; alias for value(...). |
144 | decltype(auto) bin(index_type idx) const { return value(idx); } | |
92f5a8d4 TL |
145 | |
146 | /// Returns the number of bins, without over- or underflow. | |
147 | index_type size() const noexcept { return static_cast<index_type>(vec_.size()); } | |
148 | ||
149 | /// Returns the options. | |
150 | static constexpr unsigned options() noexcept { return options_type::value; } | |
151 | ||
152 | /// Whether the axis is inclusive (see axis::traits::is_inclusive). | |
153 | static constexpr bool inclusive() noexcept { | |
154 | return options() & (option::overflow | option::growth); | |
155 | } | |
156 | ||
f67539c2 TL |
157 | /// Indicate that the axis is not ordered. |
158 | static constexpr bool ordered() noexcept { return false; } | |
159 | ||
92f5a8d4 TL |
160 | template <class V, class M, class O, class A> |
161 | bool operator==(const category<V, M, O, A>& o) const noexcept { | |
162 | const auto& a = vec_; | |
163 | const auto& b = o.vec_; | |
20effc67 TL |
164 | return std::equal(a.begin(), a.end(), b.begin(), b.end(), detail::relaxed_equal{}) && |
165 | detail::relaxed_equal{}(this->metadata(), o.metadata()); | |
92f5a8d4 TL |
166 | } |
167 | ||
168 | template <class V, class M, class O, class A> | |
169 | bool operator!=(const category<V, M, O, A>& o) const noexcept { | |
170 | return !operator==(o); | |
171 | } | |
172 | ||
f67539c2 | 173 | allocator_type get_allocator() const { return vec_.get_allocator(); } |
92f5a8d4 TL |
174 | |
175 | template <class Archive> | |
176 | void serialize(Archive& ar, unsigned /* version */) { | |
177 | ar& make_nvp("seq", vec_); | |
178 | ar& make_nvp("meta", this->metadata()); | |
179 | } | |
180 | ||
181 | private: | |
182 | vector_type vec_; | |
183 | ||
184 | template <class V, class M, class O, class A> | |
185 | friend class category; | |
186 | }; | |
187 | ||
188 | #if __cpp_deduction_guides >= 201606 | |
189 | ||
190 | template <class T> | |
191 | category(std::initializer_list<T>) | |
192 | ->category<detail::replace_cstring<std::decay_t<T>>, null_type>; | |
193 | ||
194 | template <class T, class M> | |
195 | category(std::initializer_list<T>, M) | |
196 | ->category<detail::replace_cstring<std::decay_t<T>>, | |
197 | detail::replace_cstring<std::decay_t<M>>>; | |
198 | ||
199 | #endif | |
200 | ||
201 | } // namespace axis | |
202 | } // namespace histogram | |
203 | } // namespace boost | |
204 | ||
205 | #endif |