Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-14 10:31:21

0001 /// \file
0002 /// \warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
0003 /// Feedback is welcome!
0004 
0005 #ifndef ROOT_RVariableBinAxis
0006 #define ROOT_RVariableBinAxis
0007 
0008 #include "RBinIndex.hxx"
0009 #include "RBinIndexRange.hxx"
0010 #include "RLinearizedIndex.hxx"
0011 
0012 #include <cassert>
0013 #include <cstddef>
0014 #include <stdexcept>
0015 #include <string>
0016 #include <utility>
0017 #include <vector>
0018 
0019 class TBuffer;
0020 
0021 namespace ROOT {
0022 namespace Experimental {
0023 
0024 /**
0025 An axis with variable bins defined by their edges.
0026 
0027 For example, the following creates an axis with 3 log-spaced bins:
0028 \code
0029 std::vector<double> binEdges = {1, 10, 100, 1000};
0030 ROOT::Experimental::RVariableBinAxis axis(binEdges);
0031 \endcode
0032 
0033 It is possible to disable underflow and overflow bins by passing `enableFlowBins = false`. In that case, arguments
0034 outside the axis will be silently discarded.
0035 
0036 \warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
0037 Feedback is welcome!
0038 */
0039 class RVariableBinAxis final {
0040 public:
0041    using ArgumentType = double;
0042 
0043 private:
0044    /// The (ordered) edges of the normal bins
0045    std::vector<double> fBinEdges;
0046    /// Whether underflow and overflow bins are enabled
0047    bool fEnableFlowBins;
0048 
0049 public:
0050    /// Construct an axis object with variable bins.
0051    ///
0052    /// \param[in] binEdges the (ordered) edges of the normal bins, must define at least one bin (i.e. size >= 2)
0053    /// \param[in] enableFlowBins whether to enable underflow and overflow bins
0054    explicit RVariableBinAxis(std::vector<double> binEdges, bool enableFlowBins = true)
0055       : fBinEdges(std::move(binEdges)), fEnableFlowBins(enableFlowBins)
0056    {
0057       if (fBinEdges.size() < 2) {
0058          throw std::invalid_argument("must have >= 2 bin edges");
0059       }
0060       for (std::size_t i = 1; i < fBinEdges.size(); i++) {
0061          if (fBinEdges[i - 1] >= fBinEdges[i]) {
0062             std::string msg = "binEdges must be sorted, but for bin " + std::to_string(i - 1) + ": ";
0063             msg += std::to_string(fBinEdges[i - 1]) + " >= " + std::to_string(fBinEdges[i]);
0064             throw std::invalid_argument(msg);
0065          }
0066       }
0067    }
0068 
0069    std::size_t GetNNormalBins() const { return fBinEdges.size() - 1; }
0070    std::size_t GetTotalNBins() const { return fEnableFlowBins ? fBinEdges.size() + 1 : fBinEdges.size() - 1; }
0071    const std::vector<double> &GetBinEdges() const { return fBinEdges; }
0072    bool HasFlowBins() const { return fEnableFlowBins; }
0073 
0074    friend bool operator==(const RVariableBinAxis &lhs, const RVariableBinAxis &rhs)
0075    {
0076       return lhs.fBinEdges == rhs.fBinEdges && lhs.fEnableFlowBins == rhs.fEnableFlowBins;
0077    }
0078 
0079    /// Compute the linarized index for a single argument.
0080    ///
0081    /// The normal bins have indices \f$0\f$ to \f$fBinEdges.size() - 2\f$, the underflow bin has index
0082    /// \f$fBinEdges.size() - 1\f$, and the overflow bin has index \f$fBinEdges.size()\f$. If the argument is outside all
0083    /// bin edges and the flow bins are disabled, the return value is invalid.
0084    ///
0085    /// \param[in] x the argument
0086    /// \return the linearized index that may be invalid
0087    RLinearizedIndex ComputeLinearizedIndex(double x) const
0088    {
0089       bool underflow = x < fBinEdges.front();
0090       // Put NaNs into overflow bin.
0091       bool overflow = !(x < fBinEdges.back());
0092       if (underflow) {
0093          return {fBinEdges.size() - 1, fEnableFlowBins};
0094       } else if (overflow) {
0095          return {fBinEdges.size(), fEnableFlowBins};
0096       }
0097 
0098       // TODO (for later): The following can be optimized with binary search...
0099       for (std::size_t bin = 0; bin < fBinEdges.size() - 2; bin++) {
0100          if (x < fBinEdges[bin + 1]) {
0101             return {bin, true};
0102          }
0103       }
0104       std::size_t bin = fBinEdges.size() - 2;
0105       return {bin, true};
0106    }
0107 
0108    /// Get the linearized index for an RBinIndex.
0109    ///
0110    /// The normal bins have indices \f$0\f$ to \f$fBinEdges.size() - 2\f$, the underflow bin has index
0111    /// \f$fBinEdges.size() - 1\f$, and the overflow bin has index \f$fBinEdges.size()\f$.
0112    ///
0113    /// \param[in] index the RBinIndex
0114    /// \return the linearized index that may be invalid
0115    RLinearizedIndex GetLinearizedIndex(RBinIndex index) const
0116    {
0117       if (index.IsUnderflow()) {
0118          return {fBinEdges.size() - 1, fEnableFlowBins};
0119       } else if (index.IsOverflow()) {
0120          return {fBinEdges.size(), fEnableFlowBins};
0121       } else if (index.IsInvalid()) {
0122          return {0, false};
0123       }
0124       assert(index.IsNormal());
0125       std::size_t bin = index.GetIndex();
0126       return {bin, bin < fBinEdges.size() - 1};
0127    }
0128 
0129    /// Get the range of all normal bins.
0130    ///
0131    /// \return the bin index range from the first to the last normal bin, inclusive
0132    RBinIndexRange GetNormalRange() const
0133    {
0134       return Internal::CreateBinIndexRange(RBinIndex(0), RBinIndex(fBinEdges.size() - 1), 0);
0135    }
0136 
0137    /// Get a range of normal bins.
0138    ///
0139    /// \param[in] begin the begin of the bin index range (inclusive), must be normal
0140    /// \param[in] end the end of the bin index range (exclusive), must be normal and >= begin
0141    /// \return a bin index range \f$[begin, end)\f$
0142    RBinIndexRange GetNormalRange(RBinIndex begin, RBinIndex end) const
0143    {
0144       if (!begin.IsNormal()) {
0145          throw std::invalid_argument("begin must be a normal bin");
0146       }
0147       if (begin.GetIndex() >= fBinEdges.size() - 1) {
0148          throw std::invalid_argument("begin must be inside the axis");
0149       }
0150       if (!end.IsNormal()) {
0151          throw std::invalid_argument("end must be a normal bin");
0152       }
0153       if (end.GetIndex() > fBinEdges.size() - 1) {
0154          throw std::invalid_argument("end must be inside or past the axis");
0155       }
0156       if (!(end >= begin)) {
0157          throw std::invalid_argument("end must be >= begin");
0158       }
0159       return Internal::CreateBinIndexRange(begin, end, 0);
0160    }
0161 
0162    /// Get the full range of all bins.
0163    ///
0164    /// This includes underflow and overflow bins, if enabled.
0165    ///
0166    /// \return the bin index range of all bins
0167    RBinIndexRange GetFullRange() const
0168    {
0169       return fEnableFlowBins ? Internal::CreateBinIndexRange(RBinIndex::Underflow(), RBinIndex(), fBinEdges.size() - 1)
0170                              : GetNormalRange();
0171    }
0172 
0173    /// %ROOT Streamer function to throw when trying to store an object of this class.
0174    void Streamer(TBuffer &) { throw std::runtime_error("unable to store RVariableBinAxis"); }
0175 };
0176 
0177 } // namespace Experimental
0178 } // namespace ROOT
0179 
0180 #endif