Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-29 07:33:05

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 "Acts/Utilities/PointerTraits.hpp"
0012 
0013 #include <algorithm>
0014 #include <array>
0015 #include <cmath>
0016 #include <iostream>
0017 #include <limits>
0018 #include <memory>
0019 #include <ranges>
0020 #include <type_traits>
0021 #include <vector>
0022 
0023 #define ACTS_CHECK_BIT(value, mask) ((value & mask) == mask)
0024 
0025 namespace Acts {
0026 
0027 /// Helper function to unpack a vector of smart pointers (e.g. @c shared_ptr ) into a vector of raw
0028 /// const pointers
0029 /// @tparam T the stored type
0030 /// @param items The vector of smart pointers
0031 /// @return The unpacked vector
0032 
0033 template <SmartPointerConcept T>
0034 std::vector<std::add_pointer_t<std::add_const_t<typename T::element_type>>>
0035 unpackConstSmartPointers(const std::vector<T>& items) {
0036   std::vector<std::add_pointer_t<std::add_const_t<typename T::element_type>>>
0037       rawPtrs{};
0038   rawPtrs.reserve(items.size());
0039   for (const auto& ptr : items) {
0040     rawPtrs.push_back(ptr.operator->());
0041   }
0042   return rawPtrs;
0043 }
0044 
0045 /// Helper function to unpack a vector of @c shared_ptr into a vector of raw
0046 /// pointers
0047 /// @tparam T the stored type
0048 /// @param items The vector of @c shared_ptr
0049 /// @return The unpacked vector
0050 template <SmartPointerConcept T>
0051 std::vector<std::add_pointer_t<typename T::element_type>> unpackSmartPointers(
0052     const std::vector<T>& items) {
0053   std::vector<std::add_pointer_t<typename T::element_type>> rawPtrs{};
0054   rawPtrs.reserve(items.size());
0055   for (const auto& ptr : items) {
0056     rawPtrs.push_back(&*ptr);
0057   }
0058   return rawPtrs;
0059 }
0060 
0061 /// Helper function to unpack a vector of @c shared_ptr into a vector of raw
0062 /// pointers (const version)
0063 /// @tparam T the stored type
0064 /// @param items The vector of @c shared_ptr
0065 /// @return The unpacked vector
0066 template <typename T>
0067 std::vector<const T*> unpackSmartPointers(
0068     const std::vector<std::shared_ptr<const T>>& items) {
0069   std::vector<const T*> rawPtrs;
0070   rawPtrs.reserve(items.size());
0071   for (const std::shared_ptr<const T>& item : items) {
0072     rawPtrs.push_back(item.get());
0073   }
0074   return rawPtrs;
0075 }
0076 
0077 /// @brief Converts a vector to a fixed-size array with truncating or padding.
0078 ///
0079 /// This function copies elements from the input vector into a fixed-size array.
0080 /// If the vector contains more than `kDIM` elements, the array is truncated to
0081 /// fit. If the vector contains fewer elements than `kDIM`, the remaining array
0082 /// elements are value-initialized (default-initialized, i.e., filled with zero
0083 /// or default values).
0084 ///
0085 /// @tparam kDIM The size of the resulting array.
0086 /// @tparam value_t The type of elements in the vector and the array.
0087 /// @param vecvals The input vector to be converted to an array.
0088 ///
0089 /// @return An array containing the first `kDIM` elements of the vector.
0090 template <std::size_t kDIM, typename value_t>
0091 std::array<value_t, kDIM> toArray(const std::vector<value_t>& vecvals) {
0092   std::array<value_t, kDIM> arr = {};
0093   std::copy_n(vecvals.begin(), std::min(vecvals.size(), kDIM), arr.begin());
0094   return arr;
0095 }
0096 
0097 /// @brief Dispatch a call based on a runtime value on a function taking the
0098 /// value at compile time.
0099 ///
0100 /// This function allows to write a templated functor, which accepts a @c std::size_t
0101 /// like parameter at compile time. It is then possible to make a call to the
0102 /// corresponding instance of the functor based on a runtime value. To achieve
0103 /// this, the function essentially created a if cascade between @c N and @c
0104 /// NMAX, attempting to find the right instance. Because the cascade is visible
0105 /// to the compiler entirely, it should be able to optimize.
0106 ///
0107 /// @tparam Callable Type which takes a std::size_t as a compile time param
0108 /// @tparam N Value from which to start the dispatch chain, i.e. 0 in most cases
0109 /// @tparam NMAX Maximum value up to which to attempt a dispatch
0110 /// @param v The runtime value to dispatch on
0111 /// @param args Additional arguments passed to @c Callable::invoke().
0112 /// @return The result of calling the dispatched template instance
0113 /// @note @c Callable is expected to have a static member function @c invoke
0114 /// that is callable with @c Args
0115 template <template <std::size_t> class Callable, std::size_t N,
0116           std::size_t NMAX, typename... Args>
0117 auto template_switch(std::size_t v, Args&&... args) {
0118   if (v == N) {
0119     return Callable<N>::invoke(std::forward<Args>(args)...);
0120   }
0121   if (v == 0) {
0122     std::cerr << "template_switch<Fn, " << N << ", " << NMAX << ">(v=" << v
0123               << ") is not valid (v == 0 and N != 0)" << std::endl;
0124     std::abort();
0125   }
0126   if constexpr (N < NMAX) {
0127     return template_switch<Callable, N + 1, NMAX>(v,
0128                                                   std::forward<Args>(args)...);
0129   }
0130   std::cerr << "template_switch<Fn, " << N << ", " << NMAX << ">(v=" << v
0131             << ") is not valid (v > NMAX)" << std::endl;
0132   std::abort();
0133 }
0134 
0135 /// Alternative version of @c template_switch which accepts a generic
0136 /// lambda and communicates the dimension via an integral constant type
0137 /// @tparam N Value from which to start the dispatch chain, i.e. 0 in most cases
0138 /// @tparam NMAX Maximum value up to which to attempt a dispatch
0139 /// @param v The runtime value to dispatch on
0140 /// @param func The lambda to invoke
0141 /// @param args Additional arguments passed to @p func
0142 /// @return The result of calling the dispatched lambda function
0143 template <std::size_t N, std::size_t NMAX, typename Lambda, typename... Args>
0144 auto template_switch_lambda(std::size_t v, Lambda&& func, Args&&... args) {
0145   if (v == N) {
0146     return func(std::integral_constant<std::size_t, N>{},
0147                 std::forward<Args>(args)...);
0148   }
0149   if (v == 0) {
0150     std::cerr << "template_switch<Fn, " << N << ", " << NMAX << ">(v=" << v
0151               << ") is not valid (v == 0 and N != 0)" << std::endl;
0152     std::abort();
0153   }
0154   if constexpr (N < NMAX) {
0155     return template_switch_lambda<N + 1, NMAX>(v, func,
0156                                                std::forward<Args>(args)...);
0157   }
0158   std::cerr << "template_switch<Fn, " << N << ", " << NMAX << ">(v=" << v
0159             << ") is not valid (v > NMAX)" << std::endl;
0160   std::abort();
0161 }
0162 
0163 /// Clamp a numeric value to another type, respecting range of the target type
0164 /// @tparam T the target type
0165 /// @tparam U the source type
0166 /// @param value the value to clamp
0167 /// @return the clamped value
0168 template <typename T, typename U>
0169 T clampValue(U value) {
0170   if (std::numeric_limits<U>::has_infinity && std::isinf(value)) {
0171     if (!std::numeric_limits<T>::has_infinity) {
0172       throw std::logic_error(
0173           "Cannot convert infinite value to type without infinity support");
0174     }
0175     return (value > 0) ? std::numeric_limits<T>::infinity()
0176                        : -std::numeric_limits<T>::infinity();
0177   }
0178   if (std::numeric_limits<U>::has_quiet_NaN && std::isnan(value)) {
0179     if (!std::numeric_limits<T>::has_quiet_NaN) {
0180       throw std::logic_error(
0181           "Cannot convert NaN value to type without NaN support");
0182     }
0183     return std::numeric_limits<T>::quiet_NaN();
0184   }
0185   return static_cast<T>(
0186       std::clamp(value, static_cast<U>(std::numeric_limits<T>::lowest()),
0187                  static_cast<U>(std::numeric_limits<T>::max())));
0188 }
0189 
0190 /// Return range and medium of an unsorted numeric series
0191 ///
0192 /// @tparam T a numeric series
0193 ///
0194 /// @param tseries is the number series
0195 ///
0196 /// @return [ range, medium ] in an tuple
0197 template <typename T>
0198 std::tuple<typename T::value_type, double> range_medium(const T& tseries) {
0199   auto [minIt, maxIt] = std::ranges::minmax_element(tseries);
0200   typename T::value_type range = (*maxIt - *minIt);
0201   double medium = static_cast<double>((*maxIt + *minIt) * 0.5);
0202   return {range, medium};
0203 }
0204 
0205 /// Convert enum to its underlying type value
0206 /// @param value Enum value to convert
0207 /// @return Underlying type value
0208 template <typename enum_t>
0209 constexpr std::underlying_type_t<enum_t> toUnderlying(enum_t value) {
0210   return static_cast<std::underlying_type_t<enum_t>>(value);
0211 }
0212 
0213 /// This can be replaced with C++23 to use the std::ranges::contains method
0214 ///
0215 /// This function searches through the given range for a specified value
0216 /// and returns `true` if the value is found, or `false` otherwise.
0217 ///
0218 /// @tparam R The type of the range (e.g., vector, list, array).
0219 /// @tparam T The type of the value to search for within the range.
0220 ///
0221 /// @param range The range to search within. This can be any range-compatible container.
0222 /// @param value The value to search for in the range.
0223 ///
0224 /// @return `true` if the value is found within the range, `false` otherwise.
0225 template <typename R, typename T>
0226 bool rangeContainsValue(const R& range, const T& value) {
0227   return std::ranges::find(range, value) != std::ranges::end(range);
0228 }
0229 
0230 /// This function checks if at least one string from a given range is
0231 /// contained within a specified string (value).
0232 ///
0233 /// @tparam R The type of the range (e.g., vector<string>, list<string>, array<string>).
0234 /// @param range The range to search within.
0235 /// @param value The string in which we search for substrings from the range
0236 ///
0237 /// @return `true` if a such a string in range is found, `false` otherwise.
0238 template <typename R>
0239 bool rangeContainsSubstring(const R& range, std::string_view value) {
0240   return std::ranges::any_of(range, [&](std::string_view s) {
0241     return value.find(s) != std::string_view::npos;
0242   });
0243 }
0244 
0245 /// Helper struct that can turn a set of lambdas into a single entity with
0246 /// overloaded call operator. This can be useful for example in a std::visit
0247 /// call.
0248 /// ```cpp
0249 /// std::visit(overloaded{
0250 ///  [](const int& i) { std::cout << "int: " << i << std::endl; },
0251 ///  [](const std::string& s) { std::cout << "string: " << s << std::endl; },
0252 /// }, variant);
0253 /// ```
0254 template <class... Ts>
0255 struct overloaded : Ts... {
0256   using Ts::operator()...;
0257 };
0258 
0259 /// Deduction guide for overloaded visitor pattern
0260 template <class... Ts>
0261 overloaded(Ts...) -> overloaded<Ts...>;
0262 
0263 namespace detail {
0264 
0265 /// Computes the minimum, maximum, and bin count for a given vector of values.
0266 ///
0267 /// This function processes a vector of doubles to compute:
0268 /// - The minimum value (@c xMin)
0269 /// - The maximum value (@c xMax), adjusted to include an additional bin
0270 /// - The bin count (@c xBinCount) based on the number of unique values
0271 ///
0272 /// The computation is performed as follows:
0273 /// 1. Sorts the input vector using @c std::ranges::sort to prepare for uniqueness.
0274 /// 2. Determines the number of unique values using @c std::ranges::unique and
0275 ///    calculates the bin count from the size of the unique prefix (not the tail
0276 ///    subrange).
0277 /// 3. Calculates the minimum and maximum using @c std::ranges::minmax.
0278 /// 4. Adjusts the maximum to include an additional bin by adding the bin step
0279 /// size.
0280 ///
0281 /// @param xPos A reference to a vector of doubles.
0282 /// @return A tuple containing:
0283 ///         - The minimum value (double)
0284 ///         - The adjusted maximum value (double)
0285 ///         - The bin count (std::size_t)
0286 ///
0287 /// @note The vector xPos will be modified during the call.
0288 inline auto getMinMaxAndBinCount(std::vector<double>& xPos) {
0289   // sort the values for unique()
0290   std::ranges::sort(xPos);
0291 
0292   // get the number of bins over unique values
0293   // ranges::unique returns [ret, end): duplicate tail; unique elements are
0294   // [begin, ret)
0295   const auto uniqueTail = std::ranges::unique(xPos);
0296   const std::size_t xBinCount = static_cast<std::size_t>(
0297       std::ranges::distance(xPos.begin(), uniqueTail.begin()));
0298 
0299   // get the minimum and maximum
0300   auto [xMin, xMax] = std::ranges::minmax(xPos);
0301 
0302   // calculate maxima (add one last bin, because bin value always corresponds to
0303   // left boundary)
0304   const double stepX = (xMax - xMin) / static_cast<double>(xBinCount - 1);
0305   xMax += stepX;
0306 
0307   // Return all values as a tuple
0308   return std::make_tuple(xMin, xMax, xBinCount);
0309 }
0310 
0311 }  // namespace detail
0312 
0313 }  // namespace Acts