File indexing completed on 2025-01-18 09:38:10
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_HISTOGRAM_AXIS_VARIABLE_HPP
0008 #define BOOST_HISTOGRAM_AXIS_VARIABLE_HPP
0009
0010 #include <algorithm>
0011 #include <boost/core/nvp.hpp>
0012 #include <boost/histogram/axis/interval_view.hpp>
0013 #include <boost/histogram/axis/iterator.hpp>
0014 #include <boost/histogram/axis/metadata_base.hpp>
0015 #include <boost/histogram/axis/option.hpp>
0016 #include <boost/histogram/detail/convert_integer.hpp>
0017 #include <boost/histogram/detail/detect.hpp>
0018 #include <boost/histogram/detail/limits.hpp>
0019 #include <boost/histogram/detail/relaxed_equal.hpp>
0020 #include <boost/histogram/detail/replace_type.hpp>
0021 #include <boost/histogram/fwd.hpp>
0022 #include <boost/throw_exception.hpp>
0023 #include <cassert>
0024 #include <cmath>
0025 #include <limits>
0026 #include <memory>
0027 #include <stdexcept>
0028 #include <string>
0029 #include <type_traits>
0030 #include <utility>
0031 #include <vector>
0032
0033 namespace boost {
0034 namespace histogram {
0035 namespace axis {
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055 template <class Value, class MetaData, class Options, class Allocator>
0056 class variable : public iterator_mixin<variable<Value, MetaData, Options, Allocator>>,
0057 public metadata_base_t<MetaData> {
0058
0059 using value_type = Value;
0060 using metadata_base = metadata_base_t<MetaData>;
0061 using metadata_type = typename metadata_base::metadata_type;
0062 using options_type =
0063 detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
0064 using allocator_type = Allocator;
0065 using vector_type = std::vector<Value, allocator_type>;
0066
0067 public:
0068 constexpr variable() = default;
0069 explicit variable(allocator_type alloc) : vec_(alloc) {}
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086 template <class It, class = detail::requires_iterator<It>>
0087 variable(It begin, It end, metadata_type meta = {}, options_type options = {},
0088 allocator_type alloc = {})
0089 : metadata_base(std::move(meta)), vec_(std::move(alloc)) {
0090
0091 static_assert(
0092 std::is_floating_point<value_type>::value,
0093 "current version of variable axis requires floating point type; "
0094 "if you need a variable axis with an integral type, please submit an issue");
0095 static_assert((!options.test(option::circular) && !options.test(option::growth)) ||
0096 (options.test(option::circular) ^ options.test(option::growth)),
0097 "circular and growth options are mutually exclusive");
0098
0099 const auto n = std::distance(begin, end);
0100 if (n < 0)
0101 BOOST_THROW_EXCEPTION(
0102 std::invalid_argument("end must be reachable by incrementing begin"));
0103
0104 if (n < 2) BOOST_THROW_EXCEPTION(std::invalid_argument("bins > 1 required"));
0105
0106 vec_.reserve(n);
0107 vec_.emplace_back(*begin++);
0108 bool strictly_ascending = true;
0109 for (; begin != end; ++begin) {
0110 strictly_ascending &= vec_.back() < *begin;
0111 vec_.emplace_back(*begin);
0112 }
0113
0114 if (!strictly_ascending)
0115 BOOST_THROW_EXCEPTION(
0116 std::invalid_argument("input sequence must be strictly ascending"));
0117 }
0118
0119
0120
0121 template <class It, class A, class = detail::requires_iterator<It>,
0122 class = detail::requires_allocator<A>>
0123 variable(It begin, It end, metadata_type meta, A alloc)
0124 : variable(begin, end, std::move(meta), {}, std::move(alloc)) {}
0125
0126
0127
0128
0129
0130
0131
0132
0133 template <class U, class = detail::requires_iterable<U>>
0134 variable(const U& iterable, metadata_type meta = {}, options_type options = {},
0135 allocator_type alloc = {})
0136 : variable(std::begin(iterable), std::end(iterable), std::move(meta), options,
0137 std::move(alloc)) {}
0138
0139
0140
0141 template <class U, class A, class = detail::requires_iterable<U>,
0142 class = detail::requires_allocator<A>>
0143 variable(const U& iterable, metadata_type meta, A alloc)
0144 : variable(std::begin(iterable), std::end(iterable), std::move(meta), {},
0145 std::move(alloc)) {}
0146
0147
0148
0149
0150
0151
0152
0153
0154 template <class U>
0155 variable(std::initializer_list<U> list, metadata_type meta = {},
0156 options_type options = {}, allocator_type alloc = {})
0157 : variable(list.begin(), list.end(), std::move(meta), options, std::move(alloc)) {}
0158
0159
0160
0161 template <class U, class A, class = detail::requires_allocator<A>>
0162 variable(std::initializer_list<U> list, metadata_type meta, A alloc)
0163 : variable(list.begin(), list.end(), std::move(meta), {}, std::move(alloc)) {}
0164
0165
0166 variable(const variable& src, index_type begin, index_type end, unsigned merge)
0167 : metadata_base(src), vec_(src.get_allocator()) {
0168 assert((end - begin) % merge == 0);
0169 if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
0170 BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
0171 vec_.reserve((end - begin) / merge);
0172 const auto beg = src.vec_.begin();
0173 for (index_type i = begin; i <= end; i += merge) vec_.emplace_back(*(beg + i));
0174 }
0175
0176
0177 index_type index(value_type x) const noexcept {
0178 if (options_type::test(option::circular)) {
0179 const auto a = vec_[0];
0180 const auto b = vec_[size()];
0181 x -= std::floor((x - a) / (b - a)) * (b - a);
0182 }
0183
0184 if (!options_type::test(option::overflow) && x == vec_.back()) return size() - 1;
0185 return static_cast<index_type>(std::upper_bound(vec_.begin(), vec_.end(), x) -
0186 vec_.begin() - 1);
0187 }
0188
0189 std::pair<index_type, index_type> update(value_type x) noexcept {
0190 const auto i = index(x);
0191 if (std::isfinite(x)) {
0192 if (0 <= i) {
0193 if (i < size()) return std::make_pair(i, 0);
0194 const auto d = value(size()) - value(size() - 0.5);
0195 x = std::nextafter(x, (std::numeric_limits<value_type>::max)());
0196 x = (std::max)(x, vec_.back() + d);
0197 vec_.push_back(x);
0198 return {i, -1};
0199 }
0200 const auto d = value(0.5) - value(0);
0201 x = (std::min)(x, value(0) - d);
0202 vec_.insert(vec_.begin(), x);
0203 return {0, -i};
0204 }
0205 return {x < 0 ? -1 : size(), 0};
0206 }
0207
0208
0209 value_type value(real_index_type i) const noexcept {
0210 if (options_type::test(option::circular)) {
0211 auto shift = std::floor(i / size());
0212 i -= shift * size();
0213 double z;
0214 const auto k = static_cast<index_type>(std::modf(i, &z));
0215 const auto a = vec_[0];
0216 const auto b = vec_[size()];
0217 return (1.0 - z) * vec_[k] + z * vec_[k + 1] + shift * (b - a);
0218 }
0219 if (i < 0) return detail::lowest<value_type>();
0220 if (i == size()) return vec_.back();
0221 if (i > size()) return detail::highest<value_type>();
0222 const auto k = static_cast<index_type>(i);
0223 const real_index_type z = i - k;
0224
0225 return (1.0 - z) * vec_[k] + (z == 0 ? 0 : z * vec_[k + 1]);
0226 }
0227
0228
0229 auto bin(index_type idx) const noexcept { return interval_view<variable>(*this, idx); }
0230
0231
0232 index_type size() const noexcept { return static_cast<index_type>(vec_.size()) - 1; }
0233
0234
0235 static constexpr unsigned options() noexcept { return options_type::value; }
0236
0237 template <class V, class M, class O, class A>
0238 bool operator==(const variable<V, M, O, A>& o) const noexcept {
0239 const auto& a = vec_;
0240 const auto& b = o.vec_;
0241 return std::equal(a.begin(), a.end(), b.begin(), b.end()) &&
0242 detail::relaxed_equal{}(this->metadata(), o.metadata());
0243 }
0244
0245 template <class V, class M, class O, class A>
0246 bool operator!=(const variable<V, M, O, A>& o) const noexcept {
0247 return !operator==(o);
0248 }
0249
0250
0251 auto get_allocator() const { return vec_.get_allocator(); }
0252
0253 template <class Archive>
0254 void serialize(Archive& ar, unsigned ) {
0255 ar& make_nvp("seq", vec_);
0256 ar& make_nvp("meta", this->metadata());
0257 }
0258
0259 private:
0260 vector_type vec_;
0261
0262 template <class V, class M, class O, class A>
0263 friend class variable;
0264 };
0265
0266 #if __cpp_deduction_guides >= 201606
0267
0268 template <class T>
0269 variable(std::initializer_list<T>)
0270 -> variable<detail::convert_integer<T, double>, null_type>;
0271
0272 template <class T, class M>
0273 variable(std::initializer_list<T>, M)
0274 -> variable<detail::convert_integer<T, double>,
0275 detail::replace_type<std::decay_t<M>, const char*, std::string>>;
0276
0277 template <class T, class M, unsigned B>
0278 variable(std::initializer_list<T>, M, const option::bitset<B>&)
0279 -> variable<detail::convert_integer<T, double>,
0280 detail::replace_type<std::decay_t<M>, const char*, std::string>,
0281 option::bitset<B>>;
0282
0283 template <class Iterable, class = detail::requires_iterable<Iterable>>
0284 variable(Iterable) -> variable<
0285 detail::convert_integer<
0286 std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
0287 null_type>;
0288
0289 template <class Iterable, class M>
0290 variable(Iterable, M) -> variable<
0291 detail::convert_integer<
0292 std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
0293 detail::replace_type<std::decay_t<M>, const char*, std::string>>;
0294
0295 template <class Iterable, class M, unsigned B>
0296 variable(Iterable, M, const option::bitset<B>&) -> variable<
0297 detail::convert_integer<
0298 std::decay_t<decltype(*std::begin(std::declval<Iterable&>()))>, double>,
0299 detail::replace_type<std::decay_t<M>, const char*, std::string>, option::bitset<B>>;
0300
0301 #endif
0302
0303 }
0304 }
0305 }
0306
0307 #endif