Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-02 09:28:05

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