File indexing completed on 2025-01-18 09:38:09
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_HISTOGRAM_AXIS_CATEGORY_HPP
0008 #define BOOST_HISTOGRAM_AXIS_CATEGORY_HPP
0009
0010 #include <algorithm>
0011 #include <boost/core/nvp.hpp>
0012 #include <boost/histogram/axis/iterator.hpp>
0013 #include <boost/histogram/axis/metadata_base.hpp>
0014 #include <boost/histogram/axis/option.hpp>
0015 #include <boost/histogram/detail/detect.hpp>
0016 #include <boost/histogram/detail/relaxed_equal.hpp>
0017 #include <boost/histogram/fwd.hpp>
0018 #include <boost/throw_exception.hpp>
0019 #include <stdexcept>
0020 #include <string>
0021 #include <type_traits>
0022 #include <utility>
0023 #include <vector>
0024
0025 namespace boost {
0026 namespace histogram {
0027 namespace axis {
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 template <class Value, class MetaData, class Options, class Allocator>
0045 class category : public iterator_mixin<category<Value, MetaData, Options, Allocator>>,
0046 public metadata_base_t<MetaData> {
0047
0048 using value_type = Value;
0049 using metadata_base = metadata_base_t<MetaData>;
0050 using metadata_type = typename metadata_base::metadata_type;
0051 using options_type = detail::replace_default<Options, option::overflow_t>;
0052 using allocator_type = Allocator;
0053 using vector_type = std::vector<value_type, allocator_type>;
0054
0055 public:
0056 constexpr category() = default;
0057 explicit category(allocator_type alloc) : vec_(alloc) {}
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074 template <class It, class = detail::requires_iterator<It>>
0075 category(It begin, It end, metadata_type meta = {}, options_type options = {},
0076 allocator_type alloc = {})
0077 : metadata_base(std::move(meta)), vec_(alloc) {
0078
0079 static_assert(!options.test(option::underflow),
0080 "category axis cannot have underflow");
0081 static_assert(!options.test(option::circular), "category axis cannot be circular");
0082 static_assert(!(options.test(option::growth) && options.test(option::overflow)),
0083 "growing category axis cannot have entries in overflow bin");
0084 if (std::distance(begin, end) < 0)
0085 BOOST_THROW_EXCEPTION(
0086 std::invalid_argument("end must be reachable by incrementing begin"));
0087 vec_.reserve(std::distance(begin, end));
0088 while (begin != end) vec_.emplace_back(*begin++);
0089 }
0090
0091
0092
0093 template <class It, class A, class = detail::requires_iterator<It>,
0094 class = detail::requires_allocator<A>>
0095 category(It begin, It end, metadata_type meta, A alloc)
0096 : category(begin, end, std::move(meta), {}, std::move(alloc)) {}
0097
0098
0099
0100
0101
0102
0103
0104
0105 template <class C, class = detail::requires_iterable<C>>
0106 category(const C& iterable, metadata_type meta = {}, options_type options = {},
0107 allocator_type alloc = {})
0108 : category(std::begin(iterable), std::end(iterable), std::move(meta), options,
0109 std::move(alloc)) {}
0110
0111
0112
0113 template <class C, class A, class = detail::requires_iterable<C>,
0114 class = detail::requires_allocator<A>>
0115 category(const C& iterable, metadata_type meta, A alloc)
0116 : category(std::begin(iterable), std::end(iterable), std::move(meta), {},
0117 std::move(alloc)) {}
0118
0119
0120
0121
0122
0123
0124
0125
0126 template <class U>
0127 category(std::initializer_list<U> list, metadata_type meta = {},
0128 options_type options = {}, allocator_type alloc = {})
0129 : category(list.begin(), list.end(), std::move(meta), options, std::move(alloc)) {}
0130
0131
0132
0133 template <class U, class A, class = detail::requires_allocator<A>>
0134 category(std::initializer_list<U> list, metadata_type meta, A alloc)
0135 : category(list.begin(), list.end(), std::move(meta), {}, std::move(alloc)) {}
0136
0137
0138 category(const category& src, index_type begin, index_type end, unsigned merge)
0139
0140 : category(src.vec_.begin() + begin, src.vec_.begin() + end, src.metadata(), {},
0141 src.get_allocator())
0142
0143 {
0144 if (merge > 1)
0145 BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for category axis"));
0146 }
0147
0148
0149 index_type index(const value_type& x) const noexcept {
0150 const auto beg = vec_.begin();
0151 const auto end = vec_.end();
0152 return static_cast<index_type>(std::distance(beg, std::find(beg, end, x)));
0153 }
0154
0155
0156 std::pair<index_type, index_type> update(const value_type& x) {
0157 const auto i = index(x);
0158 if (i < size()) return {i, 0};
0159 vec_.emplace_back(x);
0160 return {i, -1};
0161 }
0162
0163
0164
0165 auto value(index_type idx) const
0166 -> std::conditional_t<std::is_scalar<value_type>::value, value_type,
0167 const value_type&> {
0168 if (idx < 0 || idx >= size())
0169 BOOST_THROW_EXCEPTION(std::out_of_range("category index out of range"));
0170 return vec_[idx];
0171 }
0172
0173
0174 decltype(auto) bin(index_type idx) const { return value(idx); }
0175
0176
0177 index_type size() const noexcept { return static_cast<index_type>(vec_.size()); }
0178
0179
0180 static constexpr unsigned options() noexcept { return options_type::value; }
0181
0182
0183 static constexpr bool inclusive() noexcept {
0184 return options() & (option::overflow | option::growth);
0185 }
0186
0187
0188 static constexpr bool ordered() noexcept { return false; }
0189
0190 template <class V, class M, class O, class A>
0191 bool operator==(const category<V, M, O, A>& o) const noexcept {
0192 const auto& a = vec_;
0193 const auto& b = o.vec_;
0194 return std::equal(a.begin(), a.end(), b.begin(), b.end(), detail::relaxed_equal{}) &&
0195 detail::relaxed_equal{}(this->metadata(), o.metadata());
0196 }
0197
0198 template <class V, class M, class O, class A>
0199 bool operator!=(const category<V, M, O, A>& o) const noexcept {
0200 return !operator==(o);
0201 }
0202
0203 allocator_type get_allocator() const { return vec_.get_allocator(); }
0204
0205 template <class Archive>
0206 void serialize(Archive& ar, unsigned ) {
0207 ar& make_nvp("seq", vec_);
0208 ar& make_nvp("meta", this->metadata());
0209 }
0210
0211 private:
0212 vector_type vec_;
0213
0214 template <class V, class M, class O, class A>
0215 friend class category;
0216 };
0217
0218 #if __cpp_deduction_guides >= 201606
0219
0220 template <class T>
0221 category(std::initializer_list<T>)
0222 -> category<detail::replace_cstring<std::decay_t<T>>, null_type>;
0223
0224 template <class T, class M>
0225 category(std::initializer_list<T>, M)
0226 -> category<detail::replace_cstring<std::decay_t<T>>,
0227 detail::replace_cstring<std::decay_t<M>>>;
0228
0229 template <class T, class M, unsigned B>
0230 category(std::initializer_list<T>, M, const option::bitset<B>&)
0231 -> category<detail::replace_cstring<std::decay_t<T>>,
0232 detail::replace_cstring<std::decay_t<M>>, option::bitset<B>>;
0233
0234 #endif
0235
0236 }
0237 }
0238 }
0239
0240 #endif