File indexing completed on 2025-07-05 08:11:20
0001
0002
0003
0004
0005
0006
0007
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
0021
0022
0023
0024
0025
0026
0027
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
0039 using Value = T;
0040 static constexpr std::size_t kNumLevels = sizeof...(BitsPerLevel);
0041
0042
0043 static constexpr MultiIndex Zeros() { return MultiIndex(0u); }
0044
0045
0046
0047
0048
0049
0050
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
0065 explicit constexpr MultiIndex(Value encoded) : m_value(encoded) {}
0066
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
0073 constexpr MultiIndex& operator=(Value encoded) {
0074 m_value = encoded;
0075 return *this;
0076 }
0077
0078
0079 constexpr Value value() const { return m_value; }
0080
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
0086 constexpr MultiIndex& set(std::size_t lvl, Value val) {
0087 assert((lvl < kNumLevels) && "Index level outside allowed range");
0088
0089 Value shiftedMask = (mask(lvl) << shift(lvl));
0090
0091 Value shiftedValue = (val << shift(lvl));
0092
0093 m_value = (m_value & ~shiftedMask) | (shiftedValue & shiftedMask);
0094 return *this;
0095 }
0096
0097
0098 constexpr MultiIndex makeNextSibling(std::size_t lvl) const {
0099 assert((lvl < kNumLevels) && "Index level outside allowed range");
0100
0101 Value upper = (m_value >> shift(lvl));
0102
0103 return MultiIndex{(upper + 1u) << shift(lvl)};
0104 }
0105
0106 constexpr MultiIndex makeLastDescendant(std::size_t lvl) const {
0107 assert((lvl < kNumLevels) && "Index level outside allowed range");
0108
0109 Value maskLower = (Value{1u} << shift(lvl)) - 1u;
0110
0111 return MultiIndex{(m_value & ~maskLower) | maskLower};
0112 }
0113
0114
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
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
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
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 }
0156
0157
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 }