File indexing completed on 2025-09-15 08:13:56
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 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
0095 Value shiftedMask = (mask(lvl) << shift(lvl));
0096
0097 Value shiftedValue = (val << shift(lvl));
0098
0099 m_value = (m_value & ~shiftedMask) | (shiftedValue & shiftedMask);
0100 return *this;
0101 }
0102
0103
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
0110 constexpr MultiIndex makeNextSibling(std::size_t lvl) const {
0111 assert((lvl < kNumLevels) && "Index level outside allowed range");
0112
0113 Value upper = (m_value >> shift(lvl));
0114
0115 return MultiIndex{(upper + 1u) << shift(lvl)};
0116 }
0117
0118 constexpr MultiIndex makeLastDescendant(std::size_t lvl) const {
0119 assert((lvl < kNumLevels) && "Index level outside allowed range");
0120
0121 Value maskLower = (Value{1u} << shift(lvl)) - 1u;
0122
0123 return MultiIndex{(m_value & ~maskLower) | maskLower};
0124 }
0125
0126
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
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
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
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 }
0168
0169
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 }