Back to home page

EIC code displayed by LXR

 
 

    


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

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_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 /** Maps at a set of unique values to bin indices.
0030 
0031   The axis maps a set of values to bins, following the order of arguments in the
0032   constructor. The optional overflow bin for this axis counts input values that
0033   are not part of the set. Binning has O(N) complexity, but with a very small
0034   factor. For small N (the typical use case) it beats other kinds of lookup.
0035 
0036   @tparam Value input value type, must be equal-comparable.
0037   @tparam MetaData type to store meta data.
0038   @tparam Options see boost::histogram::axis::option.
0039   @tparam Allocator allocator to use for dynamic memory management.
0040 
0041   The options `underflow` and `circular` are not allowed. The options `growth`
0042   and `overflow` are mutually exclusive.
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   // 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 = 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   /** Construct from forward iterator range of unique values.
0060 
0061     @param begin    begin of category range of unique values.
0062     @param end      end of category range of unique values.
0063     @param meta     description of the axis (optional).
0064     @param options  see boost::histogram::axis::option (optional).
0065     @param alloc    allocator instance to use (optional).
0066 
0067     The constructor throws `std::invalid_argument` if iterator range is invalid. If the
0068     range contains duplicated values, the behavior of the axis is undefined.
0069 
0070     The arguments meta and alloc are passed by value. If you move either of them into the
0071     axis and the constructor throws, their values are lost. Do not move if you cannot
0072     guarantee that the bin description is not valid.
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     // static_asserts were moved here from class scope to satisfy deduction in gcc>=11
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   // kept for backward compatibility; requires_allocator is a workaround for deduction
0092   // guides in gcc>=11
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   /** Construct axis from iterable sequence of unique values.
0099 
0100      @param iterable sequence of unique values.
0101      @param meta     description of the axis.
0102      @param options  see boost::histogram::axis::option (optional).
0103      @param alloc    allocator instance to use.
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   // kept for backward compatibility; requires_allocator is a workaround for deduction
0112   // guides in gcc>=11
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   /** Construct axis from an initializer list of unique values.
0120 
0121      @param list     `std::initializer_list` of unique values.
0122      @param meta     description of the axis.
0123      @param options  see boost::histogram::axis::option (optional).
0124      @param alloc    allocator instance to use.
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   // kept for backward compatibility; requires_allocator is a workaround for deduction
0132   // guides in gcc>=11
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   /// Constructor used by algorithm::reduce to shrink and rebin (not for users).
0138   category(const category& src, index_type begin, index_type end, unsigned merge)
0139       // LCOV_EXCL_START: gcc-8 is missing the delegated ctor for no reason
0140       : category(src.vec_.begin() + begin, src.vec_.begin() + end, src.metadata(), {},
0141                  src.get_allocator())
0142   // LCOV_EXCL_STOP
0143   {
0144     if (merge > 1)
0145       BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for category axis"));
0146   }
0147 
0148   /// Return index for value argument.
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   /// Returns index and shift (if axis has grown) for the passed argument.
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   /// Return value for index argument.
0164   /// Throws `std::out_of_range` if the index is out of bounds.
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   /// Return value for index argument; alias for value(...).
0174   decltype(auto) bin(index_type idx) const { return value(idx); }
0175 
0176   /// Returns the number of bins, without over- or underflow.
0177   index_type size() const noexcept { return static_cast<index_type>(vec_.size()); }
0178 
0179   /// Returns the options.
0180   static constexpr unsigned options() noexcept { return options_type::value; }
0181 
0182   /// Whether the axis is inclusive (see axis::traits::is_inclusive).
0183   static constexpr bool inclusive() noexcept {
0184     return options() & (option::overflow | option::growth);
0185   }
0186 
0187   /// Indicate that the axis is not ordered.
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 /* version */) {
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 } // namespace axis
0237 } // namespace histogram
0238 } // namespace boost
0239 
0240 #endif