Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 09:13:37

0001 // -*- C++ -*-
0002 //
0003 // This file is part of YODA -- Yet more Objects for Data Analysis
0004 // Copyright (C) 2008-2024 The YODA collaboration (see AUTHORS for details)
0005 //
0006 #ifndef YODA_Bin_h
0007 #define YODA_Bin_h
0008 
0009 #include "YODA/Utils/BinUtils.h"
0010 #include "YODA/Utils/Traits.h"
0011 #include <iostream>
0012 
0013 namespace YODA {
0014 
0015   /// @brief Arithmetic wrapper to emulate inheritance from arithmetic types
0016   template <typename T>
0017   struct ArithmeticWrapper {
0018     /// @name Constructors
0019     // {
0020     ArithmeticWrapper() : _storedNumber(0) {}
0021 
0022     ArithmeticWrapper(T num) : _storedNumber(num) {}
0023     // }
0024 
0025     /// @name Arithmetic operators
0026     // {
0027 
0028     ArithmeticWrapper<T>& operator+=(T&& rhs) {
0029       _storedNumber += std::forward<T>(rhs);
0030       return *this;
0031     }
0032     ArithmeticWrapper<T>& operator-=(T&& rhs) {
0033       _storedNumber -= std::forward<T>(rhs);
0034       return *this;
0035     }
0036     ArithmeticWrapper<T>& operator/=(T&& rhs) {
0037       _storedNumber /= std::forward<T>(rhs);
0038       return *this;
0039     }
0040     ArithmeticWrapper<T>& operator*=(T&& rhs) {
0041       _storedNumber *= std::forward<T>(rhs);
0042       return *this;
0043     }
0044 
0045     /// @note Template to make enable_if work.
0046     template<typename RetT = ArithmeticWrapper<T>>
0047     auto operator%=(T&& rhs)
0048     -> std::enable_if_t<std::is_integral<T>::value, RetT&> {
0049       _storedNumber %= std::forward<T>(rhs);
0050       return *this;
0051     }
0052     // }
0053 
0054     /// @brief Casts ArithmeticWrapper to the contained type.
0055     ///
0056     /// @note Used in arithmetic operators, so the default
0057     /// language arithmetic operators are used.
0058     operator T() { return _storedNumber; }
0059 
0060     /// @brief Casts ArithmeticWrapper to the contained type (const version).
0061     ///
0062     /// @note Used in raw() function of BinBase.
0063     operator const T&() const { return _storedNumber; }
0064 
0065     T _storedNumber;
0066   };
0067 
0068   /// @brief Bin base class consisting of mix of histogram bin content and
0069   /// space characteristics of this bin (widths, min, max, mid, etc...)
0070   ///
0071   /// @note Since the BinBase class inherits from the content of the
0072   /// histogram bin (to emulate its behavior), an arithmetic wrapper is
0073   /// introduced to inherit from arithmetic types (it's not possible to
0074   /// inherit from fundamental types by default).
0075   ///
0076   /// @note We use CRTP to introduce dimension-specific member functions
0077   /// like xWidth(), yMid() to avoid the user having to call
0078   /// e.g. bin(i).width<N>() or similar.
0079   template <typename T, typename BinningT>
0080   class BinBase : public std::conditional_t<std::is_arithmetic<T>::value,
0081                                             ArithmeticWrapper<T>, T> {
0082   protected:
0083 
0084     /// @name Utilities
0085     // {
0086     using isArithmetic = std::conditional_t<std::is_arithmetic<T>::value,
0087                                             std::true_type, std::false_type>;
0088 
0089     using BaseT = std::conditional_t<isArithmetic::value,
0090                                      ArithmeticWrapper<T>, T>;
0091 
0092     template <size_t axisNum>
0093     using axisEdgeT = typename BinningT::template getAxisT<axisNum>::EdgeT;
0094 
0095     // }
0096 
0097   public:
0098     /// @name Constructors
0099     // {
0100 
0101     // For self-consistency, the user should
0102     // always pass the parent binning!
0103     BinBase() = delete;
0104 
0105     // Default copy constructor for STL vector
0106     BinBase(const BinBase& rhs) = default;
0107 
0108     // Default move constructor for STL vector
0109     BinBase(BinBase&& rhs) = default;
0110 
0111 
0112     BinBase(size_t binIndex, const BinningT& binning)
0113         : _binIndex(binIndex), _binning(&binning) { }
0114 
0115     /// @brief Setting constructor
0116     BinBase(const T& storedVal, size_t binIndex, const BinningT& binning)
0117         : BaseT(storedVal), _binIndex(binIndex), _binning(&binning) { }
0118 
0119     BinBase(T&& storedVal, size_t binIndex, const BinningT& binning)
0120         : BaseT(std::move(storedVal)), _binIndex(binIndex), _binning(&binning) { }
0121 
0122     BinBase(const BinBase& other, const BinningT& binning)
0123         : BaseT(other), _binIndex(other._binIndex), _binning(&binning) { }
0124 
0125     /// @brief Assignment operator of an rvalue content type
0126     ///
0127     /// @note Cython is not a fan of perfect forwarding yet
0128     BinBase& operator=(BaseT&& rhs) noexcept {
0129       //BaseT::operator=(std::forward<BaseT>(rhs));
0130       BaseT::operator=(std::move(rhs));
0131       return *this;
0132     }
0133 
0134     /// @brief Assignment operator of a content type
0135     BinBase& operator=(const BaseT& rhs) noexcept {
0136       BaseT::operator=(rhs);
0137       return *this;
0138     }
0139 
0140     /// @brief Copy assignment operator
0141     ///
0142     /// @note _binIdx is not altered to keep correct indices while using
0143     /// std::vector<...>().erase() on _bins (bins storage).
0144     BinBase& operator=(const BinBase& rhs) noexcept {
0145       if (this != &rhs) {
0146         BaseT::operator=(rhs);
0147       }
0148       return *this;
0149     }
0150 
0151     /// @brief Move assignment operator
0152     ///
0153     /// @note _binIdx is not altered to keep correct indices while using
0154     /// std::vector<...>().erase() on _bins (bins storage).
0155     BinBase& operator=(BinBase&& rhs) noexcept {
0156       if (this != &rhs) {
0157         BaseT::operator=(std::move(rhs));
0158       }
0159       return *this;
0160     }
0161 
0162     // @}
0163 
0164     /// @name Utility methods
0165     // @{
0166 
0167     /// @brief return stored content
0168     const T& raw() const noexcept {
0169       return *this;
0170     }
0171 
0172     /// @brief return stored index
0173     size_t index() const noexcept {
0174       return _binIndex;
0175     }
0176 
0177     bool isMasked() const noexcept {
0178       return _binning->isMasked(_binIndex);
0179     }
0180 
0181     bool isVisible() const noexcept {
0182       return _binning->isVisible(_binIndex);
0183     }
0184 
0185     // @}
0186 
0187     /// @name Bin space characteristics
0188     // @{
0189 
0190     /// @brief Differential volume of this bin (i.e. product of bin widths)
0191     double dVol() const noexcept {
0192       return _binning->dVol(_binIndex);
0193     }
0194 
0195     /// @brief Width of this bin along a specific axis.
0196     ///
0197     /// @note Bin width is the projection of the
0198     /// bin surface (its area) along a specific axis.
0199     /// Therefore, "bin.width()" will not compile:
0200     /// it's not an intrinsic property of the bin.
0201     /// One should always specify *along which axis*.
0202     /// Convenient short-hands like bin.xWidth() etc.
0203     /// are being defined via CRTP Mixin, though.
0204     ///
0205     /// @note Only supported for continuous axes.
0206     template <size_t dimNum>
0207     enable_if_CAxisT<axisEdgeT<dimNum>> width() const noexcept {
0208       const auto& axis = _binning->template axis<dimNum>();
0209       size_t binIdx = _binning->globalToLocalIndices(_binIndex)[dimNum];
0210       return axis.width(binIdx);
0211     }
0212 
0213     /// @brief Maximum of this bin interval.
0214     ///
0215     /// @note Only supported for continuous axes.
0216     template <size_t dimNum>
0217     enable_if_CAxisT<axisEdgeT<dimNum>> max() const noexcept {
0218       const auto& axis = _binning->template axis<dimNum>();
0219       size_t binIdx = _binning->globalToLocalIndices(_binIndex)[dimNum];
0220       return axis.max(binIdx);
0221     }
0222 
0223     /// @brief Minimum of this bin interval.
0224     ///
0225     /// @note Only supported for continuous axes.
0226     template <size_t dimNum>
0227     enable_if_CAxisT<axisEdgeT<dimNum>> min() const noexcept {
0228       const auto& axis = _binning->template axis<dimNum>();
0229       size_t binIdx = _binning->globalToLocalIndices(_binIndex)[dimNum];
0230       return axis.min(binIdx);
0231     }
0232 
0233     /// @brief Middle of this bin interval.
0234     ///
0235     /// @note Only supported for continuous axes.
0236     template <size_t dimNum>
0237     enable_if_CAxisT<axisEdgeT<dimNum>> mid() const noexcept {
0238       const auto& axis = _binning->template axis<dimNum>();
0239       size_t binIdx = _binning->globalToLocalIndices(_binIndex)[dimNum];
0240       return axis.mid(binIdx);
0241     }
0242 
0243     /// @brief Edge of this bin.
0244     ///
0245     /// @note Only supported for discrete axes.
0246     template <size_t dimNum>
0247     enable_if_DAxisT<axisEdgeT<dimNum>> edge() const noexcept {
0248       const auto& axis = _binning->template axis<dimNum>();
0249       size_t binIdx = _binning->globalToLocalIndices(_binIndex)[dimNum];
0250       return axis.edge(binIdx);
0251     }
0252 
0253     // @}
0254 
0255   protected:
0256 
0257     const size_t _binIndex;
0258 
0259     const BinningT* _binning;
0260   };
0261 
0262 
0263   /// @brief generic Bin version that derives from BinBase
0264   template <size_t N, typename T, typename BinningT>
0265   class Bin : public BinBase<T, BinningT> {
0266 
0267   protected:
0268 
0269     using BaseT = BinBase<T, BinningT>;
0270 
0271   public:
0272 
0273     using BaseT::BaseT;
0274     using BaseT::operator=;
0275 
0276     // For self-consistency, the user should
0277     // always pass the parent binning!
0278     Bin() = delete;
0279 
0280     /* This would have been a nice idea, except we cannot guarantee
0281       that the content type even has the concept of a value.
0282     double value(const bool divbyvol = false) const noexcept {
0283       const double scale = divbyvol? BaseT::dVol() : 1.0;
0284       if constexpr (hasFillDim<T>::value) {
0285         if constexpr (T::FillDim::value > N) {
0286           return BaseT::mean(T::FillDim::value) / scale;// profiles
0287         }
0288         return BaseT::sumW() / scale; // histograms
0289       }
0290       return BaseT::value() / scale; // other
0291     }
0292 
0293     /// @brief Error on the value of the bin
0294     double valueErr(const bool divbyvol = false) const noexcept {
0295       const double scale = divbyvol? BaseT::dVol() : 1.0;
0296       if constexpr (hasFillDim<T>::value) {
0297         if constexpr (T::FillDim::value > N) { // profiles
0298           return BaseT::stdErr(T::FillDim::value) / scale;
0299         }
0300         return BaseT::errW() / scale; // histograms
0301       }
0302       return BaseT::valueErr() / scale; // other
0303     }
0304 
0305     /// @brief Relative size of the bin error
0306     double relErr() const noexcept {
0307       return value()? valueErr() / value() : std::numeric_limits<double>::quiet_NaN();
0308     }
0309 
0310     /// @brief Density of the bin
0311     double density() const noexcept {
0312       return value() / BaseT::dVol();
0313     }
0314 
0315     /// @brief Error on the bin density
0316     double densityErr() const noexcept {
0317       return valueErr() / BaseT::dVol();
0318     }*/
0319 
0320   };
0321 
0322 
0323   /// @brief CRTP specialisation in 1D
0324   template<typename T, typename BinningT>
0325   class Bin<1, T, BinningT>
0326                : public BinBase<T, BinningT>,
0327                  public XBinMixin<Bin<1, T, BinningT>,
0328                                   typename BinningT::template getEdgeT<0>> {
0329   protected:
0330 
0331     using BaseT = BinBase<T, BinningT>;
0332 
0333     template <size_t axisNum>
0334     using axisEdgeT = typename BinningT::template getAxisT<axisNum>::EdgeT;
0335 
0336   public:
0337 
0338     using BaseT::BaseT;
0339     using BaseT::operator=;
0340 
0341     // For self-consistency, the user should
0342     // always pass the parent binning!
0343     Bin() = delete;
0344 
0345     /// @brief Differential length of this bin (i.e. the bin width)
0346     double dLen() const noexcept { return BaseT::dVol(); }
0347 
0348   };
0349 
0350 
0351   /// @brief CRTP specialisation in 2D
0352   template<typename T, typename BinningT>
0353   class Bin<2, T, BinningT>
0354                : public BinBase<T, BinningT>,
0355                  public XBinMixin<Bin<2, T, BinningT>,
0356                                   typename BinningT::template getEdgeT<0>>,
0357                  public YBinMixin<Bin<2, T, BinningT>,
0358                                   typename BinningT::template getEdgeT<1>> {
0359 
0360   protected:
0361 
0362     using BaseT = BinBase<T, BinningT>;
0363 
0364     template <size_t axisNum>
0365     using axisEdgeT = typename BinningT::template getAxisT<axisNum>::EdgeT;
0366 
0367   public:
0368 
0369     using BaseT::BaseT;
0370     using BaseT::operator=;
0371 
0372     // For self-consistency, the user should
0373     // always pass the parent binning!
0374     Bin() = delete;
0375 
0376     /// @brief Differential area of this bin (i.e. product of bin widths)
0377     double dArea() const noexcept { return BaseT::dVol(); }
0378 
0379   };
0380 
0381 
0382   /// @brief CRTP specialisation in 3D
0383   template<typename T, typename BinningT>
0384   class Bin<3, T, BinningT>
0385                : public BinBase<T, BinningT>,
0386                  public XBinMixin<Bin<3, T, BinningT>,
0387                                   typename BinningT::template getEdgeT<0>>,
0388                  public YBinMixin<Bin<3, T, BinningT>,
0389                                   typename BinningT::template getEdgeT<1>>,
0390                  public ZBinMixin<Bin<3, T, BinningT>,
0391                                   typename BinningT::template getEdgeT<2>> {
0392 
0393   protected:
0394 
0395     using BaseT = BinBase<T, BinningT>;
0396 
0397     template <size_t axisNum>
0398     using axisEdgeT = typename BinningT::template getAxisT<axisNum>::EdgeT;
0399 
0400   public:
0401 
0402     using BaseT::BaseT;
0403     using BaseT::operator=;
0404 
0405     // For self-consistency, the user should
0406     // always pass the parent binning!
0407     Bin() = delete;
0408 
0409   };
0410 
0411 }
0412 
0413 #endif