Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-16 09:21:23

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_RSliceBinIndexMapper
0006 #define ROOT_RSliceBinIndexMapper
0007 
0008 #include "RBinIndex.hxx"
0009 #include "RBinIndexRange.hxx"
0010 #include "RSliceSpec.hxx"
0011 
0012 #include <cstdint>
0013 #include <stdexcept>
0014 #include <utility>
0015 #include <vector>
0016 
0017 namespace ROOT {
0018 namespace Experimental {
0019 namespace Internal {
0020 
0021 /**
0022 Mapper of bin indices for slice operations.
0023 */
0024 class RSliceBinIndexMapper final {
0025    /// The requested slice specifications
0026    std::vector<RSliceSpec> fSliceSpecs;
0027    /// The expected dimensionality of the mapped indices
0028    std::size_t fMappedDimensionality;
0029 
0030    static std::size_t ComputeMappedDimensionality(const std::vector<RSliceSpec> &sliceSpecs)
0031    {
0032       std::size_t dimensionality = 0;
0033       for (auto &&spec : sliceSpecs) {
0034          // A sum operation makes the dimension disappear.
0035          if (spec.GetOperationSum() == nullptr) {
0036             dimensionality++;
0037          }
0038       }
0039       return dimensionality;
0040    }
0041 
0042 public:
0043    /// \param[in] sliceSpecs the slice specifications, must have size > 0
0044    explicit RSliceBinIndexMapper(std::vector<RSliceSpec> sliceSpecs)
0045       : fSliceSpecs(std::move(sliceSpecs)), fMappedDimensionality(ComputeMappedDimensionality(fSliceSpecs))
0046    {
0047       if (fSliceSpecs.empty()) {
0048          throw std::invalid_argument("must have at least 1 slice specification");
0049       }
0050    }
0051 
0052    const std::vector<RSliceSpec> &GetSliceSpecs() const { return fSliceSpecs; }
0053    std::size_t GetMappedDimensionality() const { return fMappedDimensionality; }
0054 
0055    /// Map a vector of RBinIndex according to the slice specifications.
0056    ///
0057    /// \param[in] original the original bin indices
0058    /// \param[out] mapped the mapped bin indices
0059    /// \return whether the mapping was successful or the bin content should be discarded
0060    bool Map(const std::vector<RBinIndex> &original, std::vector<RBinIndex> &mapped) const
0061    {
0062       if (original.size() != fSliceSpecs.size()) {
0063          throw std::invalid_argument("invalid number of original indices passed to RSliceBinIndexMapper::Map");
0064       }
0065       if (mapped.size() != fMappedDimensionality) {
0066          throw std::invalid_argument("invalid size of mapped indices passed to RSliceBinIndexMapper::Map");
0067       }
0068 
0069       std::size_t mappedPos = 0;
0070       for (std::size_t i = 0; i < original.size(); i++) {
0071          RBinIndex index = original[i];
0072          if (index.IsInvalid()) {
0073             throw std::invalid_argument("invalid bin index passed to RSliceBinIndexMapper::Map");
0074          }
0075 
0076          const RSliceSpec &sliceSpec = fSliceSpecs[i];
0077          const auto &range = sliceSpec.GetRange();
0078          bool contained = true;
0079          if (!range.IsInvalid()) {
0080             // Underflow and overflow indices map to themselves, but they may not actually be contained in the range.
0081             // This is important for the sum operation below.
0082             if (index.IsUnderflow()) {
0083                contained = range.GetBegin().IsUnderflow();
0084             } else if (index.IsOverflow()) {
0085                contained = range.GetEnd().IsInvalid();
0086             } else if (index.IsNormal()) {
0087                const auto &begin = range.GetBegin();
0088                const auto &end = range.GetEnd();
0089                if (begin.IsNormal() && index < begin) {
0090                   index = RBinIndex::Underflow();
0091                   contained = false;
0092                } else if (end.IsNormal() && index >= end) {
0093                   index = RBinIndex::Overflow();
0094                   contained = false;
0095                } else if (begin.IsNormal()) {
0096                   // This normal bin is contained in the range. Its index must be shifted according to the begin of the
0097                   // range.
0098                   index -= begin.GetIndex();
0099                   assert(!index.IsInvalid());
0100                }
0101             }
0102          }
0103 
0104          if (auto *opRebin = sliceSpec.GetOperationRebin()) {
0105             if (index.IsNormal()) {
0106                index = RBinIndex(index.GetIndex() / opRebin->GetNGroup());
0107             }
0108          } else if (sliceSpec.GetOperationSum() != nullptr) {
0109             // This dimension disappears. If there is a range and the index is not contained, discard it.
0110             if (!contained) {
0111                return false;
0112             }
0113             // Otherwise got to the next dimension.
0114             continue;
0115          }
0116 
0117          mapped[mappedPos] = index;
0118          mappedPos++;
0119       }
0120 
0121       // If we got here, the loop should have filled all mapped indices.
0122       assert(mappedPos == mapped.size());
0123       return true;
0124    }
0125 };
0126 
0127 } // namespace Internal
0128 } // namespace Experimental
0129 } // namespace ROOT
0130 
0131 #endif