Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-15 08:13:56

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   static constexpr std::size_t kNumLevels = sizeof...(BitsPerLevel);
0041 
0042   /// Construct a MultiIndex with all levels set to zero.
0043   static constexpr MultiIndex Zeros() { return MultiIndex(0u); }
0044   /// Construct a MultiIndex from values for multiple level.
0045   ///
0046   /// This functionality must be implemented as a static, named constructor
0047   /// to avoid confusion with other constructors. If it would be implemented
0048   /// as a regular constructor, constructing a MultiIndex from a single
0049   /// encoded value and encoding only the first level would have the same
0050   /// signature and could not be distinguished.
0051   template <typename... Us>
0052   static constexpr MultiIndex Encode(Us&&... us) {
0053     static_assert(sizeof...(Us) <= kNumLevels,
0054                   "Can only encode as many levels as in the MultiIndex");
0055 
0056     MultiIndex index(0u);
0057     std::size_t lvl = 0;
0058     for (Value val : std::array<Value, sizeof...(Us)>{us...}) {
0059       index.set(lvl++, val);
0060     }
0061     return index;
0062   }
0063 
0064   /// Construct a MultiIndex from an already encoded value.
0065   explicit constexpr MultiIndex(Value encoded) : m_value(encoded) {}
0066   /// Construct a default MultiIndex with undefined values for each level.
0067   MultiIndex() = default;
0068   MultiIndex(const MultiIndex&) = default;
0069   MultiIndex(MultiIndex&) = default;
0070   MultiIndex& operator=(const MultiIndex&) = default;
0071   MultiIndex& operator=(MultiIndex&&) noexcept = default;
0072   /// Allow setting the MultiIndex from an already encoded value.
0073   constexpr MultiIndex& operator=(Value encoded) {
0074     m_value = encoded;
0075     return *this;
0076   }
0077 
0078   /// Get the encoded value of all index levels.
0079   constexpr Value value() const { return m_value; }
0080   /// Get the value for the index level.
0081   constexpr Value level(std::size_t lvl) const {
0082     assert((lvl < kNumLevels) && "Index level outside allowed range");
0083     return (m_value >> shift(lvl)) & mask(lvl);
0084   }
0085   /// Set the value of the index level.
0086   constexpr MultiIndex& set(std::size_t lvl, Value val) {
0087     assert((lvl < kNumLevels) && "Index level outside allowed range");
0088     if (val > maxValue(lvl)) {
0089       throw std::out_of_range(
0090           "Value " + std::to_string(val) + " for index level " +
0091           std::to_string(lvl) +
0092           " exceeds allowed range (max=" + std::to_string(maxValue(lvl)) + ")");
0093     }
0094     // mask of valid bits at the encoded positions for the index level
0095     Value shiftedMask = (mask(lvl) << shift(lvl));
0096     // value of the index level shifted to its encoded position
0097     Value shiftedValue = (val << shift(lvl));
0098     // combine existing values with the value for the index level
0099     m_value = (m_value & ~shiftedMask) | (shiftedValue & shiftedMask);
0100     return *this;
0101   }
0102 
0103   // Return the maximum allowed value for this level
0104   constexpr std::size_t maxValue(std::size_t lvl) const {
0105     assert((lvl < kNumLevels) && "Index level outside allowed range");
0106     return (1 << s_bits.at(lvl)) - 1;
0107   }
0108 
0109   /// Create index with the selected level increased and levels below zeroed.
0110   constexpr MultiIndex makeNextSibling(std::size_t lvl) const {
0111     assert((lvl < kNumLevels) && "Index level outside allowed range");
0112     // remove lower levels by shifting the upper levels to the left edge
0113     Value upper = (m_value >> shift(lvl));
0114     // increase to create sibling and shift back to zero lower levels again
0115     return MultiIndex{(upper + 1u) << shift(lvl)};
0116   }
0117   /// Create index with every level below the selected level maximized.
0118   constexpr MultiIndex makeLastDescendant(std::size_t lvl) const {
0119     assert((lvl < kNumLevels) && "Index level outside allowed range");
0120     // mask everything below the selected level
0121     Value maskLower = (Value{1u} << shift(lvl)) - 1u;
0122     // replace the masked lower levels w/ ones
0123     return MultiIndex{(m_value & ~maskLower) | maskLower};
0124   }
0125 
0126   /// Get the number of bits for the associated level
0127   static constexpr std::size_t bits(std::size_t lvl) {
0128     assert((lvl < kNumLevels) && "Index level outside allowed range");
0129     return s_bits[lvl];
0130   }
0131 
0132  private:
0133   // per-level mask and right-most bit position for shifting
0134   static constexpr std::array<std::size_t, kNumLevels> s_bits{BitsPerLevel...};
0135   static constexpr std::size_t shift(std::size_t lvl) {
0136     std::size_t s = 0u;
0137     // sum up all bits below the requested level
0138     for (std::size_t i = (lvl + 1); i < s_bits.size(); ++i) {
0139       s += s_bits[i];
0140     }
0141     return s;
0142   }
0143   static constexpr Value mask(std::size_t lvl) {
0144     return (Value{1u} << s_bits[lvl]) - 1u;
0145   }
0146 
0147   Value m_value;
0148 
0149   friend constexpr bool operator<(MultiIndex lhs, MultiIndex rhs) {
0150     return lhs.m_value < rhs.m_value;
0151   }
0152 
0153   friend constexpr bool operator==(MultiIndex lhs, MultiIndex rhs) {
0154     return lhs.m_value == rhs.m_value;
0155   }
0156 
0157   friend inline std::ostream& operator<<(std::ostream& os, MultiIndex idx) {
0158     // one level is always defined
0159     os << idx.level(0u);
0160     for (std::size_t lvl = 1; lvl < kNumLevels; ++lvl) {
0161       os << '|' << idx.level(lvl);
0162     }
0163     return os;
0164   }
0165 };
0166 
0167 }  // namespace Acts
0168 
0169 // specialize std::hash so MultiIndex can be used e.g. in an unordered_map
0170 namespace std {
0171 template <typename Storage, std::size_t... BitsPerLevel>
0172 struct hash<Acts::MultiIndex<Storage, BitsPerLevel...>> {
0173   auto operator()(
0174       Acts::MultiIndex<Storage, BitsPerLevel...> idx) const noexcept {
0175     return std::hash<Storage>()(idx.value());
0176   }
0177 };
0178 }  // namespace std