Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MultiIndex.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file MultiIndex.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
11 #include <array>
12 #include <cassert>
13 #include <climits>
14 #include <ostream>
15 #include <type_traits>
16 #include <utility>
17 
18 namespace Acts {
19 
28 template <typename T, std::size_t... BitsPerLevel>
29 class MultiIndex {
30  public:
31  static_assert(std::is_integral_v<T> and std::is_unsigned_v<T>,
32  "The underlying storage type must be an unsigned integer");
33  static_assert(0 < sizeof...(BitsPerLevel),
34  "At least one level must be defined");
35  static_assert((sizeof(T) * CHAR_BIT) == (... + BitsPerLevel),
36  "The sum of bits per level must match the underlying storage");
37 
39  using Value = T;
40  enum : std::size_t {
41  NumLevels = sizeof...(BitsPerLevel),
42  };
43 
45  static constexpr MultiIndex Zeros() { return MultiIndex(0u); }
53  template <typename... Us>
54  static constexpr MultiIndex Encode(Us&&... us) {
55  static_assert(sizeof...(Us) <= NumLevels,
56  "Can only encode as many levels as in the MultiIndex");
57 
58  MultiIndex index(0u);
59  std::size_t lvl = 0;
60  for (Value val : std::array<Value, sizeof...(Us)>{us...}) {
61  index.set(lvl++, val);
62  }
63  return index;
64  }
65 
67  constexpr MultiIndex(Value encoded) : m_value(encoded) {}
69  MultiIndex() = default;
70  MultiIndex(const MultiIndex&) = default;
71  MultiIndex(MultiIndex&) = default;
72  MultiIndex& operator=(const MultiIndex&) = default;
73  MultiIndex& operator=(MultiIndex&&) = default;
75  constexpr MultiIndex& operator=(Value encoded) {
76  m_value = encoded;
77  return *this;
78  }
79 
81  constexpr Value value() const { return m_value; }
83  constexpr Value level(std::size_t lvl) const {
84  assert((lvl < NumLevels) and "Index level outside allowed range");
85  return (m_value >> shift(lvl)) & mask(lvl);
86  }
88  constexpr MultiIndex& set(std::size_t lvl, Value val) {
89  assert((lvl < NumLevels) and "Index level outside allowed range");
90  // mask of valid bits at the encoded positions for the index level
91  Value shiftedMask = (mask(lvl) << shift(lvl));
92  // value of the index level shifted to its encoded position
93  Value shiftedValue = (val << shift(lvl));
94  // combine existing values with the value for the index level
95  m_value = (m_value & ~shiftedMask) | (shiftedValue & shiftedMask);
96  return *this;
97  }
98 
100  constexpr MultiIndex makeNextSibling(std::size_t lvl) const {
101  assert((lvl < NumLevels) and "Index level outside allowed range");
102  // remove lower levels by shifting the upper levels to the left edge
103  Value upper = (m_value >> shift(lvl));
104  // increase to create sibling and shift back to zero lower levels again
105  return ((upper + 1u) << shift(lvl));
106  }
108  constexpr MultiIndex makeLastDescendant(std::size_t lvl) const {
109  assert((lvl < NumLevels) and "Index level outside allowed range");
110  // mask everything below the selected level
111  Value maskLower = (Value(1u) << shift(lvl)) - 1u;
112  // replace the masked lower levels w/ ones
113  return (m_value & ~maskLower) | maskLower;
114  }
115 
117  static constexpr std::size_t bits(std::size_t lvl) {
118  assert((lvl < NumLevels) and "Index level outside allowed range");
119  return s_bits[lvl];
120  }
121 
122  private:
123  // per-level mask and right-most bit position for shifting
124  static constexpr std::array<std::size_t, NumLevels> s_bits{BitsPerLevel...};
125  static constexpr std::size_t shift(std::size_t lvl) {
126  std::size_t s = 0u;
127  // sum up all bits below the requested level
128  for (std::size_t i = (lvl + 1); i < s_bits.size(); ++i) {
129  s += s_bits[i];
130  }
131  return s;
132  }
133  static constexpr Value mask(std::size_t lvl) {
134  return (Value(1u) << s_bits[lvl]) - 1u;
135  }
136 
138 
139  friend constexpr bool operator<(MultiIndex lhs, MultiIndex rhs) {
140  return lhs.m_value < rhs.m_value;
141  }
142  friend constexpr bool operator==(MultiIndex lhs, MultiIndex rhs) {
143  return lhs.m_value == rhs.m_value;
144  }
145  friend inline std::ostream& operator<<(std::ostream& os, MultiIndex idx) {
146  // one level is always defined
147  os << idx.level(0u);
148  for (std::size_t lvl = 1; lvl < NumLevels; ++lvl) {
149  os << '|' << idx.level(lvl);
150  }
151  return os;
152  }
153 };
154 
155 } // namespace Acts
156 
157 // specialize std::hash so MultiIndex can be used e.g. in an unordered_map
158 namespace std {
159 template <typename Storage, std::size_t... BitsPerLevel>
160 struct hash<Acts::MultiIndex<Storage, BitsPerLevel...>> {
161  auto operator()(
163  return std::hash<Storage>()(idx.value());
164  }
165 };
166 } // namespace std