Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:38:10

0001 // Copyright 2015-2018 Hans Dembinski
0002 //
0003 // Distributed under the Boost Software License, Version 1.0.
0004 // (See accompanying file LICENSE_1_0.txt
0005 // or copy at http://www.boost.org/LICENSE_1_0.txt)
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 /** Axis for an interval of integer values with unit steps.
0033 
0034   Binning is a O(1) operation. This axis bins even faster than a regular axis.
0035 
0036   The options `growth` and `circular` are mutually exclusive. If the axis uses
0037   integers and either `growth` or `circular` are set, the axis cannot have
0038   the options `underflow` or `overflow` set.
0039 
0040   @tparam Value     input value type. Must be integer or floating point.
0041   @tparam MetaData  type to store meta data.
0042   @tparam Options   see boost::histogram::axis::option.
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   // these must be private, so that they are not automatically inherited
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   /** Construct over semi-open integer interval [start, stop).
0061 
0062     @param start    first integer of covered range.
0063     @param stop     one past last integer of covered range.
0064     @param meta     description of the axis (optional).
0065     @param options  see boost::histogram::axis::option (optional).
0066 
0067     The constructor throws `std::invalid_argument` if start is not less than stop.
0068 
0069     The arguments meta and alloc are passed by value. If you move either of them into the
0070     axis and the constructor throws, their values are lost. Do not move if you cannot
0071     guarantee that the bin description is not valid.
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)) // double negation so it works with NaN
0093       BOOST_THROW_EXCEPTION(std::invalid_argument("stop >= start required"));
0094   }
0095 
0096   /// Constructor used by algorithm::reduce to shrink and rebin.
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   /// Return index for value argument.
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   /// Returns index and shift (if axis has grown) for the passed argument.
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   /// Return value for index argument.
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   /// Return bin for index argument.
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   /// Returns the number of bins, without over- or underflow.
0157   index_type size() const noexcept { return size_; }
0158 
0159   /// Returns the options.
0160   static constexpr unsigned options() noexcept { return options_type::value; }
0161 
0162   /// Whether the axis is inclusive (see axis::traits::is_inclusive).
0163   static constexpr bool inclusive() noexcept {
0164     // If axis has underflow and overflow, it is inclusive.
0165     // If axis is growing or circular:
0166     // - it is inclusive if value_type is an integer.
0167     // - it is not inclusive if value_type is floating point, because of nan and inf.
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 /* version */) {
0186     ar& make_nvp("size", size_);
0187     ar& make_nvp("meta", this->metadata());
0188     ar& make_nvp("min", min_);
0189   }
0190 
0191 private:
0192   // axis not circular
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   // value_type is integer, axis circular
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   // value_type is floating point, must handle +/-infinite or nan, axis circular
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 } // namespace axis
0236 } // namespace histogram
0237 } // namespace boost
0238 
0239 #endif