Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:11:11

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   enum : std::size_t {
0041     NumLevels = sizeof...(BitsPerLevel),
0042   };
0043 
0044   /// Construct a MultiIndex with all levels set to zero.
0045   static constexpr MultiIndex Zeros() { return MultiIndex(0u); }
0046   /// Construct a MultiIndex from values for multiple level.
0047   ///
0048   /// This functionality must be implemented as a static, named constructor
0049   /// to avoid confusion with other constructors. If it would be implemented
0050   /// as a regular constructor, constructing a MultiIndex from a single
0051   /// encoded value and encoding only the first level would have the same
0052   /// signature and could not be distinguished.
0053   template <typename... Us>
0054   static constexpr MultiIndex Encode(Us&&... us) {
0055     static_assert(sizeof...(Us) <= NumLevels,
0056                   "Can only encode as many levels as in the MultiIndex");
0057 
0058     MultiIndex index(0u);
0059     std::size_t lvl = 0;
0060     for (Value val : std::array<Value, sizeof...(Us)>{us...}) {
0061       index.set(lvl++, val);
0062     }
0063     return index;
0064   }
0065 
0066   /// Construct a MultiIndex from an already encoded value.
0067   constexpr MultiIndex(Value encoded) : m_value(encoded) {}
0068   /// Construct a default MultiIndex with undefined values for each level.
0069   MultiIndex() = default;
0070   MultiIndex(const MultiIndex&) = default;
0071   MultiIndex(MultiIndex&) = default;
0072   MultiIndex& operator=(const MultiIndex&) = default;
0073   MultiIndex& operator=(MultiIndex&&) noexcept = default;
0074   /// Allow setting the MultiIndex from an already encoded value.
0075   constexpr MultiIndex& operator=(Value encoded) {
0076     m_value = encoded;
0077     return *this;
0078   }
0079 
0080   /// Get the encoded value of all index levels.
0081   constexpr Value value() const { return m_value; }
0082   /// Get the value for the index level.
0083   constexpr Value level(std::size_t lvl) const {
0084     assert((lvl < NumLevels) && "Index level outside allowed range");
0085     return (m_value >> shift(lvl)) & mask(lvl);
0086   }
0087   /// Set the value of the index level.
0088   constexpr MultiIndex& set(std::size_t lvl, Value val) {
0089     assert((lvl < NumLevels) && "Index level outside allowed range");
0090     // mask of valid bits at the encoded positions for the index level
0091     Value shiftedMask = (mask(lvl) << shift(lvl));
0092     // value of the index level shifted to its encoded position
0093     Value shiftedValue = (val << shift(lvl));
0094     // combine existing values with the value for the index level
0095     m_value = (m_value & ~shiftedMask) | (shiftedValue & shiftedMask);
0096     return *this;
0097   }
0098 
0099   /// Create index with the selected level increased and levels below zeroed.
0100   constexpr MultiIndex makeNextSibling(std::size_t lvl) const {
0101     assert((lvl < NumLevels) && "Index level outside allowed range");
0102     // remove lower levels by shifting the upper levels to the left edge
0103     Value upper = (m_value >> shift(lvl));
0104     // increase to create sibling and shift back to zero lower levels again
0105     return ((upper + 1u) << shift(lvl));
0106   }
0107   /// Create index with every level below the selected level maximized.
0108   constexpr MultiIndex makeLastDescendant(std::size_t lvl) const {
0109     assert((lvl < NumLevels) && "Index level outside allowed range");
0110     // mask everything below the selected level
0111     Value maskLower = (Value{1u} << shift(lvl)) - 1u;
0112     // replace the masked lower levels w/ ones
0113     return (m_value & ~maskLower) | maskLower;
0114   }
0115 
0116   /// Get the number of bits for the associated level
0117   static constexpr std::size_t bits(std::size_t lvl) {
0118     assert((lvl < NumLevels) && "Index level outside allowed range");
0119     return s_bits[lvl];
0120   }
0121 
0122  private:
0123   // per-level mask and right-most bit position for shifting
0124   static constexpr std::array<std::size_t, NumLevels> s_bits{BitsPerLevel...};
0125   static constexpr std::size_t shift(std::size_t lvl) {
0126     std::size_t s = 0u;
0127     // sum up all bits below the requested level
0128     for (std::size_t i = (lvl + 1); i < s_bits.size(); ++i) {
0129       s += s_bits[i];
0130     }
0131     return s;
0132   }
0133   static constexpr Value mask(std::size_t lvl) {
0134     return (Value{1u} << s_bits[lvl]) - 1u;
0135   }
0136 
0137   Value m_value;
0138 
0139   friend constexpr bool operator<(MultiIndex lhs, MultiIndex rhs) {
0140     return lhs.m_value < rhs.m_value;
0141   }
0142 
0143   friend constexpr bool operator==(MultiIndex lhs, MultiIndex rhs) {
0144     return lhs.m_value == rhs.m_value;
0145   }
0146 
0147   friend inline std::ostream& operator<<(std::ostream& os, MultiIndex idx) {
0148     // one level is always defined
0149     os << idx.level(0u);
0150     for (std::size_t lvl = 1; lvl < NumLevels; ++lvl) {
0151       os << '|' << idx.level(lvl);
0152     }
0153     return os;
0154   }
0155 };
0156 
0157 }  // namespace Acts
0158 
0159 // specialize std::hash so MultiIndex can be used e.g. in an unordered_map
0160 namespace std {
0161 template <typename Storage, std::size_t... BitsPerLevel>
0162 struct hash<Acts::MultiIndex<Storage, BitsPerLevel...>> {
0163   auto operator()(
0164       Acts::MultiIndex<Storage, BitsPerLevel...> idx) const noexcept {
0165     return std::hash<Storage>()(idx.value());
0166   }
0167 };
0168 }  // namespace std