File indexing completed on 2025-01-18 09:38:10
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_HISTOGRAM_AXIS_INTEGER_HPP
0008 #define BOOST_HISTOGRAM_AXIS_INTEGER_HPP
0009
0010 #include <boost/core/nvp.hpp>
0011 #include <boost/histogram/axis/iterator.hpp>
0012 #include <boost/histogram/axis/metadata_base.hpp>
0013 #include <boost/histogram/axis/option.hpp>
0014 #include <boost/histogram/detail/convert_integer.hpp>
0015 #include <boost/histogram/detail/limits.hpp>
0016 #include <boost/histogram/detail/relaxed_equal.hpp>
0017 #include <boost/histogram/detail/replace_type.hpp>
0018 #include <boost/histogram/detail/static_if.hpp>
0019 #include <boost/histogram/fwd.hpp>
0020 #include <boost/throw_exception.hpp>
0021 #include <cmath>
0022 #include <limits>
0023 #include <stdexcept>
0024 #include <string>
0025 #include <type_traits>
0026 #include <utility>
0027
0028 namespace boost {
0029 namespace histogram {
0030 namespace axis {
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 template <class Value, class MetaData, class Options>
0045 class integer : public iterator_mixin<integer<Value, MetaData, Options>>,
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 =
0052 detail::replace_default<Options, decltype(option::underflow | option::overflow)>;
0053
0054 using local_index_type = std::conditional_t<std::is_integral<value_type>::value,
0055 index_type, real_index_type>;
0056
0057 public:
0058 constexpr integer() = default;
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073 integer(value_type start, value_type stop, metadata_type meta = {},
0074 options_type options = {})
0075 : metadata_base(std::move(meta))
0076 , size_(static_cast<index_type>(stop - start))
0077 , min_(start) {
0078 static_assert(
0079 std::is_integral<value_type>::value || std::is_floating_point<value_type>::value,
0080 "integer axis requires floating point or integral type");
0081
0082 static_assert(!(options.test(option::circular) && options.test(option::growth)),
0083 "circular and growth options are mutually exclusive");
0084
0085 static_assert(
0086 std::is_floating_point<value_type>::value ||
0087 !((options.test(option::growth) || options.test(option::circular)) &&
0088 (options.test(option::overflow) || options.test(option::underflow))),
0089 "circular or growing integer axis with integral type "
0090 "cannot have entries in underflow or overflow bins");
0091
0092 if (!(stop >= start))
0093 BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start required"));
0094 }
0095
0096
0097 integer(const integer& src, index_type begin, index_type end, unsigned merge)
0098 : integer(src.value(begin), src.value(end), src.metadata()) {
0099 if (merge > 1)
0100 BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for integer axis"));
0101 if (options_type::test(option::circular) && !(begin == 0 && end == src.size()))
0102 BOOST_THROW_EXCEPTION(std::invalid_argument("cannot shrink circular axis"));
0103 }
0104
0105
0106 index_type index(value_type x) const noexcept {
0107 return index_impl(options_type::test(axis::option::circular),
0108 std::is_floating_point<value_type>{},
0109 static_cast<double>(x - min_));
0110 }
0111
0112
0113 auto update(value_type x) noexcept {
0114 auto impl = [this](long x) -> std::pair<index_type, index_type> {
0115 const auto i = x - min_;
0116 if (i >= 0) {
0117 const auto k = static_cast<axis::index_type>(i);
0118 if (k < size()) return {k, 0};
0119 const auto n = k - size() + 1;
0120 size_ += n;
0121 return {k, -n};
0122 }
0123 const auto k = static_cast<axis::index_type>(
0124 detail::static_if<std::is_floating_point<value_type>>(
0125 [](auto x) { return std::floor(x); }, [](auto x) { return x; }, i));
0126 min_ += k;
0127 size_ -= k;
0128 return {0, -k};
0129 };
0130
0131 return detail::static_if<std::is_floating_point<value_type>>(
0132 [this, impl](auto x) -> std::pair<index_type, index_type> {
0133 if (std::isfinite(x)) return impl(static_cast<long>(std::floor(x)));
0134 return {x < 0 ? -1 : this->size(), 0};
0135 },
0136 impl, x);
0137 }
0138
0139
0140 value_type value(local_index_type i) const noexcept {
0141 if (!options_type::test(option::circular) &&
0142 std::is_floating_point<value_type>::value) {
0143 if (i < 0) return detail::lowest<value_type>();
0144 if (i > size()) return detail::highest<value_type>();
0145 }
0146 return min_ + i;
0147 }
0148
0149
0150 decltype(auto) bin(index_type idx) const noexcept {
0151 return detail::static_if<std::is_floating_point<value_type>>(
0152 [this](auto idx) { return interval_view<integer>(*this, idx); },
0153 [this](auto idx) { return this->value(idx); }, idx);
0154 }
0155
0156
0157 index_type size() const noexcept { return size_; }
0158
0159
0160 static constexpr unsigned options() noexcept { return options_type::value; }
0161
0162
0163 static constexpr bool inclusive() noexcept {
0164
0165
0166
0167
0168 constexpr bool full_flow = options_type().test(option::underflow | option::overflow);
0169 return full_flow || (std::is_integral<value_type>::value &&
0170 (options() & (option::growth | option::circular)));
0171 }
0172
0173 template <class V, class M, class O>
0174 bool operator==(const integer<V, M, O>& o) const noexcept {
0175 return size() == o.size() && min_ == o.min_ &&
0176 detail::relaxed_equal{}(this->metadata(), o.metadata());
0177 }
0178
0179 template <class V, class M, class O>
0180 bool operator!=(const integer<V, M, O>& o) const noexcept {
0181 return !operator==(o);
0182 }
0183
0184 template <class Archive>
0185 void serialize(Archive& ar, unsigned ) {
0186 ar& make_nvp("size", size_);
0187 ar& make_nvp("meta", this->metadata());
0188 ar& make_nvp("min", min_);
0189 }
0190
0191 private:
0192
0193 template <class B>
0194 index_type index_impl(std::false_type, B, double z) const noexcept {
0195 if (z < size()) return z >= 0 ? static_cast<index_type>(z) : -1;
0196 return size();
0197 }
0198
0199
0200 index_type index_impl(std::true_type, std::false_type, double z) const noexcept {
0201 return static_cast<index_type>(z - std::floor(z / size()) * size());
0202 }
0203
0204
0205 index_type index_impl(std::true_type, std::true_type, double z) const noexcept {
0206 if (std::isfinite(z)) return index_impl(std::true_type{}, std::false_type{}, z);
0207 return z < size() ? -1 : size();
0208 }
0209
0210 index_type size_{0};
0211 value_type min_{0};
0212
0213 template <class V, class M, class O>
0214 friend class integer;
0215 };
0216
0217 #if __cpp_deduction_guides >= 201606
0218
0219 template <class T>
0220 integer(T, T) -> integer<detail::convert_integer<T, index_type>, null_type>;
0221
0222 template <class T, class M>
0223 integer(T, T, M)
0224 -> integer<detail::convert_integer<T, index_type>,
0225 detail::replace_type<std::decay_t<M>, const char*, std::string>>;
0226
0227 template <class T, class M, unsigned B>
0228 integer(T, T, M, const option::bitset<B>&)
0229 -> integer<detail::convert_integer<T, index_type>,
0230 detail::replace_type<std::decay_t<M>, const char*, std::string>,
0231 option::bitset<B>>;
0232
0233 #endif
0234
0235 }
0236 }
0237 }
0238
0239 #endif