File indexing completed on 2025-10-17 07:58:52
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
0041 static constexpr std::size_t kNumLevels = sizeof...(BitsPerLevel);
0042
0043
0044
0045 static constexpr MultiIndex Zeros() { return MultiIndex(0u); }
0046
0047
0048
0049
0050
0051
0052
0053
0054
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
0069
0070 explicit constexpr MultiIndex(Value encoded) : m_value(encoded) {}
0071
0072 MultiIndex() = default;
0073
0074 MultiIndex(const MultiIndex&) = default;
0075
0076 MultiIndex(MultiIndex&) = default;
0077
0078
0079 MultiIndex& operator=(const MultiIndex&) = default;
0080
0081
0082 MultiIndex& operator=(MultiIndex&&) noexcept = default;
0083
0084
0085
0086 constexpr MultiIndex& operator=(Value encoded) {
0087 m_value = encoded;
0088 return *this;
0089 }
0090
0091
0092
0093 constexpr Value value() const { return m_value; }
0094
0095
0096
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
0102
0103
0104
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
0114 Value shiftedMask = (mask(lvl) << shift(lvl));
0115
0116 Value shiftedValue = (val << shift(lvl));
0117
0118 m_value = (m_value & ~shiftedMask) | (shiftedValue & shiftedMask);
0119 return *this;
0120 }
0121
0122
0123
0124
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
0131
0132
0133 constexpr MultiIndex makeNextSibling(std::size_t lvl) const {
0134 assert((lvl < kNumLevels) && "Index level outside allowed range");
0135
0136 Value upper = (m_value >> shift(lvl));
0137
0138 return MultiIndex{(upper + 1u) << shift(lvl)};
0139 }
0140
0141
0142
0143 constexpr MultiIndex makeLastDescendant(std::size_t lvl) const {
0144 assert((lvl < kNumLevels) && "Index level outside allowed range");
0145
0146 Value maskLower = (Value{1u} << shift(lvl)) - 1u;
0147
0148 return MultiIndex{(m_value & ~maskLower) | maskLower};
0149 }
0150
0151
0152
0153
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
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
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
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 }
0195
0196
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 }