Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:11:20

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     // mask of valid bits at the encoded positions for the index level
0089     Value shiftedMask = (mask(lvl) << shift(lvl));
0090     // value of the index level shifted to its encoded position
0091     Value shiftedValue = (val << shift(lvl));
0092     // combine existing values with the value for the index level
0093     m_value = (m_value & ~shiftedMask) | (shiftedValue & shiftedMask);
0094     return *this;
0095   }
0096 
0097   /// Create index with the selected level increased and levels below zeroed.
0098   constexpr MultiIndex makeNextSibling(std::size_t lvl) const {
0099     assert((lvl < kNumLevels) && "Index level outside allowed range");
0100     // remove lower levels by shifting the upper levels to the left edge
0101     Value upper = (m_value >> shift(lvl));
0102     // increase to create sibling and shift back to zero lower levels again
0103     return MultiIndex{(upper + 1u) << shift(lvl)};
0104   }
0105   /// Create index with every level below the selected level maximized.
0106   constexpr MultiIndex makeLastDescendant(std::size_t lvl) const {
0107     assert((lvl < kNumLevels) && "Index level outside allowed range");
0108     // mask everything below the selected level
0109     Value maskLower = (Value{1u} << shift(lvl)) - 1u;
0110     // replace the masked lower levels w/ ones
0111     return MultiIndex{(m_value & ~maskLower) | maskLower};
0112   }
0113 
0114   /// Get the number of bits for the associated level
0115   static constexpr std::size_t bits(std::size_t lvl) {
0116     assert((lvl < kNumLevels) && "Index level outside allowed range");
0117     return s_bits[lvl];
0118   }
0119 
0120  private:
0121   // per-level mask and right-most bit position for shifting
0122   static constexpr std::array<std::size_t, kNumLevels> s_bits{BitsPerLevel...};
0123   static constexpr std::size_t shift(std::size_t lvl) {
0124     std::size_t s = 0u;
0125     // sum up all bits below the requested level
0126     for (std::size_t i = (lvl + 1); i < s_bits.size(); ++i) {
0127       s += s_bits[i];
0128     }
0129     return s;
0130   }
0131   static constexpr Value mask(std::size_t lvl) {
0132     return (Value{1u} << s_bits[lvl]) - 1u;
0133   }
0134 
0135   Value m_value;
0136 
0137   friend constexpr bool operator<(MultiIndex lhs, MultiIndex rhs) {
0138     return lhs.m_value < rhs.m_value;
0139   }
0140 
0141   friend constexpr bool operator==(MultiIndex lhs, MultiIndex rhs) {
0142     return lhs.m_value == rhs.m_value;
0143   }
0144 
0145   friend inline std::ostream& operator<<(std::ostream& os, MultiIndex idx) {
0146     // one level is always defined
0147     os << idx.level(0u);
0148     for (std::size_t lvl = 1; lvl < kNumLevels; ++lvl) {
0149       os << '|' << idx.level(lvl);
0150     }
0151     return os;
0152   }
0153 };
0154 
0155 }  // namespace Acts
0156 
0157 // specialize std::hash so MultiIndex can be used e.g. in an unordered_map
0158 namespace std {
0159 template <typename Storage, std::size_t... BitsPerLevel>
0160 struct hash<Acts::MultiIndex<Storage, BitsPerLevel...>> {
0161   auto operator()(
0162       Acts::MultiIndex<Storage, BitsPerLevel...> idx) const noexcept {
0163     return std::hash<Storage>()(idx.value());
0164   }
0165 };
0166 }  // namespace std