Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-10-17 07:58:52

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 #include <array>
0012 #include <cassert>
0013 #include <climits>
0014 #include <ostream>
0015 #include <type_traits>
0016 #include <utility>
0017 
0018 namespace Acts {
0019 
0020 /// A set of (hierarchical) indices bitpacked into a single value.
0021 ///
0022 /// The underlying value is split into blocks of bits with variable size.
0023 /// Each block is a level within the index hierarchy and can be set and
0024 /// retrieved separately. The encoded MultiIndex can be ordered and compared
0025 /// for equality. The ordering follows the hierarchy, i.e. indices are
0026 /// first ordered by the highest level, then within the highest level by the
0027 /// second level and so on.
0028 template <typename T, std::size_t... BitsPerLevel>
0029 class MultiIndex {
0030  public:
0031   static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>,
0032                 "The underlying storage type must be an unsigned integer");
0033   static_assert(0 < sizeof...(BitsPerLevel),
0034                 "At least one level must be defined");
0035   static_assert((sizeof(T) * CHAR_BIT) == (... + BitsPerLevel),
0036                 "The sum of bits per level must match the underlying storage");
0037 
0038   /// The type of their underlying storage value.
0039   using Value = T;
0040   /// Number of levels in the multi-index hierarchy
0041   static constexpr std::size_t kNumLevels = sizeof...(BitsPerLevel);
0042 
0043   /// Construct a MultiIndex with all levels set to zero.
0044   /// @return MultiIndex with all levels initialized to zero
0045   static constexpr MultiIndex Zeros() { return MultiIndex(0u); }
0046   /// Construct a MultiIndex from values for multiple level.
0047   ///
0048   /// This functionality must be implemented as a static, named constructor
0049   /// to avoid confusion with other constructors. If it would be implemented
0050   /// as a regular constructor, constructing a MultiIndex from a single
0051   /// encoded value and encoding only the first level would have the same
0052   /// signature and could not be distinguished.
0053   /// @param us Values for each index level to encode
0054   /// @return MultiIndex encoded with the provided level values
0055   template <typename... Us>
0056   static constexpr MultiIndex Encode(Us&&... us) {
0057     static_assert(sizeof...(Us) <= kNumLevels,
0058                   "Can only encode as many levels as in the MultiIndex");
0059 
0060     MultiIndex index(0u);
0061     std::size_t lvl = 0;
0062     for (Value val : std::array<Value, sizeof...(Us)>{us...}) {
0063       index.set(lvl++, val);
0064     }
0065     return index;
0066   }
0067 
0068   /// Construct a MultiIndex from an already encoded value.
0069   /// @param encoded Pre-encoded multi-index value
0070   explicit constexpr MultiIndex(Value encoded) : m_value(encoded) {}
0071   /// Construct a default MultiIndex with undefined values for each level.
0072   MultiIndex() = default;
0073   /// @brief Copy constructor
0074   MultiIndex(const MultiIndex&) = default;
0075   /// @brief Non-const copy constructor
0076   MultiIndex(MultiIndex&) = default;
0077   /// @brief Copy assignment operator
0078   /// @return Reference to this MultiIndex
0079   MultiIndex& operator=(const MultiIndex&) = default;
0080   /// @brief Move assignment operator
0081   /// @return Reference to this MultiIndex
0082   MultiIndex& operator=(MultiIndex&&) noexcept = default;
0083   /// Allow setting the MultiIndex from an already encoded value.
0084   /// @param encoded Pre-encoded multi-index value to assign
0085   /// @return Reference to this MultiIndex for chaining
0086   constexpr MultiIndex& operator=(Value encoded) {
0087     m_value = encoded;
0088     return *this;
0089   }
0090 
0091   /// Get the encoded value of all index levels.
0092   /// @return The complete encoded multi-index value
0093   constexpr Value value() const { return m_value; }
0094   /// Get the value for the index level.
0095   /// @param lvl Level index to retrieve
0096   /// @return Value stored at the specified level
0097   constexpr Value level(std::size_t lvl) const {
0098     assert((lvl < kNumLevels) && "Index level outside allowed range");
0099     return (m_value >> shift(lvl)) & mask(lvl);
0100   }
0101   /// Set the value of the index level.
0102   /// @param lvl Level index to set
0103   /// @param val Value to set for the specified level
0104   /// @return Reference to this MultiIndex for chaining
0105   constexpr MultiIndex& set(std::size_t lvl, Value val) {
0106     assert((lvl < kNumLevels) && "Index level outside allowed range");
0107     if (val > maxValue(lvl)) {
0108       throw std::out_of_range(
0109           "Value " + std::to_string(val) + " for index level " +
0110           std::to_string(lvl) +
0111           " exceeds allowed range (max=" + std::to_string(maxValue(lvl)) + ")");
0112     }
0113     // mask of valid bits at the encoded positions for the index level
0114     Value shiftedMask = (mask(lvl) << shift(lvl));
0115     // value of the index level shifted to its encoded position
0116     Value shiftedValue = (val << shift(lvl));
0117     // combine existing values with the value for the index level
0118     m_value = (m_value & ~shiftedMask) | (shiftedValue & shiftedMask);
0119     return *this;
0120   }
0121 
0122   /// @brief Return the maximum allowed value for a specific index level
0123   /// @param lvl Level index to query maximum value for
0124   /// @return Maximum value that can be stored at the specified level
0125   constexpr std::size_t maxValue(std::size_t lvl) const {
0126     assert((lvl < kNumLevels) && "Index level outside allowed range");
0127     return (1 << s_bits.at(lvl)) - 1;
0128   }
0129 
0130   /// Create index with the selected level increased and levels below zeroed.
0131   /// @param lvl Level to increment for creating the sibling
0132   /// @return New MultiIndex with the next sibling at the specified level
0133   constexpr MultiIndex makeNextSibling(std::size_t lvl) const {
0134     assert((lvl < kNumLevels) && "Index level outside allowed range");
0135     // remove lower levels by shifting the upper levels to the left edge
0136     Value upper = (m_value >> shift(lvl));
0137     // increase to create sibling and shift back to zero lower levels again
0138     return MultiIndex{(upper + 1u) << shift(lvl)};
0139   }
0140   /// Create index with every level below the selected level maximized.
0141   /// @param lvl Level below which all levels are maximized
0142   /// @return New MultiIndex representing the last descendant
0143   constexpr MultiIndex makeLastDescendant(std::size_t lvl) const {
0144     assert((lvl < kNumLevels) && "Index level outside allowed range");
0145     // mask everything below the selected level
0146     Value maskLower = (Value{1u} << shift(lvl)) - 1u;
0147     // replace the masked lower levels w/ ones
0148     return MultiIndex{(m_value & ~maskLower) | maskLower};
0149   }
0150 
0151   /// Get the number of bits for the associated level
0152   /// @param lvl Level index to query
0153   /// @return Number of bits allocated for the specified level
0154   static constexpr std::size_t bits(std::size_t lvl) {
0155     assert((lvl < kNumLevels) && "Index level outside allowed range");
0156     return s_bits[lvl];
0157   }
0158 
0159  private:
0160   // per-level mask and right-most bit position for shifting
0161   static constexpr std::array<std::size_t, kNumLevels> s_bits{BitsPerLevel...};
0162   static constexpr std::size_t shift(std::size_t lvl) {
0163     std::size_t s = 0u;
0164     // sum up all bits below the requested level
0165     for (std::size_t i = (lvl + 1); i < s_bits.size(); ++i) {
0166       s += s_bits[i];
0167     }
0168     return s;
0169   }
0170   static constexpr Value mask(std::size_t lvl) {
0171     return (Value{1u} << s_bits[lvl]) - 1u;
0172   }
0173 
0174   Value m_value;
0175 
0176   friend constexpr bool operator<(MultiIndex lhs, MultiIndex rhs) {
0177     return lhs.m_value < rhs.m_value;
0178   }
0179 
0180   friend constexpr bool operator==(MultiIndex lhs, MultiIndex rhs) {
0181     return lhs.m_value == rhs.m_value;
0182   }
0183 
0184   friend inline std::ostream& operator<<(std::ostream& os, MultiIndex idx) {
0185     // one level is always defined
0186     os << idx.level(0u);
0187     for (std::size_t lvl = 1; lvl < kNumLevels; ++lvl) {
0188       os << '|' << idx.level(lvl);
0189     }
0190     return os;
0191   }
0192 };
0193 
0194 }  // namespace Acts
0195 
0196 // specialize std::hash so MultiIndex can be used e.g. in an unordered_map
0197 namespace std {
0198 template <typename Storage, std::size_t... BitsPerLevel>
0199 struct hash<Acts::MultiIndex<Storage, BitsPerLevel...>> {
0200   auto operator()(
0201       Acts::MultiIndex<Storage, BitsPerLevel...> idx) const noexcept {
0202     return std::hash<Storage>()(idx.value());
0203   }
0204 };
0205 }  // namespace std