|
|
|||
File indexing completed on 2026-08-27 08:30:46
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 decltype(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 decltype(auto) template_switch_lambda(std::size_t v, Lambda&& func, 0145 Args&&... args) { 0146 if (v == N) { 0147 return func(std::integral_constant<std::size_t, N>{}, 0148 std::forward<Args>(args)...); 0149 } 0150 if (v == 0) { 0151 std::cerr << "template_switch<Fn, " << N << ", " << NMAX << ">(v=" << v 0152 << ") is not valid (v == 0 and N != 0)" << std::endl; 0153 std::abort(); 0154 } 0155 if constexpr (N < NMAX) { 0156 return template_switch_lambda<N + 1, NMAX>(v, func, 0157 std::forward<Args>(args)...); 0158 } 0159 std::cerr << "template_switch<Fn, " << N << ", " << NMAX << ">(v=" << v 0160 << ") is not valid (v > NMAX)" << std::endl; 0161 std::abort(); 0162 } 0163 0164 /// Clamp a numeric value to another type, respecting range of the target type 0165 /// @tparam T the target type 0166 /// @tparam U the source type 0167 /// @param value the value to clamp 0168 /// @return the clamped value 0169 template <typename T, typename U> 0170 T clampValue(U value) { 0171 if (std::numeric_limits<U>::has_infinity && std::isinf(value)) { 0172 if (!std::numeric_limits<T>::has_infinity) { 0173 throw std::logic_error( 0174 "Cannot convert infinite value to type without infinity support"); 0175 } 0176 return (value > 0) ? std::numeric_limits<T>::infinity() 0177 : -std::numeric_limits<T>::infinity(); 0178 } 0179 if (std::numeric_limits<U>::has_quiet_NaN && std::isnan(value)) { 0180 if (!std::numeric_limits<T>::has_quiet_NaN) { 0181 throw std::logic_error( 0182 "Cannot convert NaN value to type without NaN support"); 0183 } 0184 return std::numeric_limits<T>::quiet_NaN(); 0185 } 0186 return static_cast<T>( 0187 std::clamp(value, static_cast<U>(std::numeric_limits<T>::lowest()), 0188 static_cast<U>(std::numeric_limits<T>::max()))); 0189 } 0190 0191 /// Return range and medium of an unsorted numeric series 0192 /// 0193 /// @tparam T a numeric series 0194 /// 0195 /// @param tseries is the number series 0196 /// 0197 /// @return [ range, medium ] in an tuple 0198 template <typename T> 0199 std::tuple<typename T::value_type, double> range_medium(const T& tseries) { 0200 auto [minIt, maxIt] = std::ranges::minmax_element(tseries); 0201 typename T::value_type range = (*maxIt - *minIt); 0202 double medium = static_cast<double>((*maxIt + *minIt) * 0.5); 0203 return {range, medium}; 0204 } 0205 0206 /// Convert enum to its underlying type value 0207 /// @param value Enum value to convert 0208 /// @return Underlying type value 0209 template <typename enum_t> 0210 constexpr std::underlying_type_t<enum_t> toUnderlying(enum_t value) { 0211 return static_cast<std::underlying_type_t<enum_t>>(value); 0212 } 0213 0214 /// This can be replaced with C++23 to use the std::ranges::contains method 0215 /// 0216 /// This function searches through the given range for a specified value 0217 /// and returns `true` if the value is found, or `false` otherwise. 0218 /// 0219 /// @tparam R The type of the range (e.g., vector, list, array). 0220 /// @tparam T The type of the value to search for within the range. 0221 /// 0222 /// @param range The range to search within. This can be any range-compatible container. 0223 /// @param value The value to search for in the range. 0224 /// 0225 /// @return `true` if the value is found within the range, `false` otherwise. 0226 template <typename R, typename T> 0227 bool rangeContainsValue(const R& range, const T& value) { 0228 return std::ranges::find(range, value) != std::ranges::end(range); 0229 } 0230 0231 /// This function checks if at least one string from a given range is 0232 /// contained within a specified string (value). 0233 /// 0234 /// @tparam R The type of the range (e.g., vector<string>, list<string>, array<string>). 0235 /// @param range The range to search within. 0236 /// @param value The string in which we search for substrings from the range 0237 /// 0238 /// @return `true` if a such a string in range is found, `false` otherwise. 0239 template <typename R> 0240 bool rangeContainsSubstring(const R& range, std::string_view value) { 0241 return std::ranges::any_of(range, [&](std::string_view s) { 0242 return value.find(s) != std::string_view::npos; 0243 }); 0244 } 0245 0246 /// Helper struct that can turn a set of lambdas into a single entity with 0247 /// overloaded call operator. This can be useful for example in a std::visit 0248 /// call. 0249 /// ```cpp 0250 /// std::visit(overloaded{ 0251 /// [](const int& i) { std::cout << "int: " << i << std::endl; }, 0252 /// [](const std::string& s) { std::cout << "string: " << s << std::endl; }, 0253 /// }, variant); 0254 /// ``` 0255 template <class... Ts> 0256 struct overloaded : Ts... { 0257 using Ts::operator()...; 0258 }; 0259 0260 /// Deduction guide for overloaded visitor pattern 0261 template <class... Ts> 0262 overloaded(Ts...) -> overloaded<Ts...>; 0263 0264 namespace detail { 0265 0266 /// Computes the minimum, maximum, and bin count for a given vector of values. 0267 /// 0268 /// This function processes a vector of doubles to compute: 0269 /// - The minimum value (@c xMin) 0270 /// - The maximum value (@c xMax), adjusted to include an additional bin 0271 /// - The bin count (@c xBinCount) based on the number of unique values 0272 /// 0273 /// The computation is performed as follows: 0274 /// 1. Sorts the input vector using @c std::ranges::sort to prepare for uniqueness. 0275 /// 2. Determines the number of unique values using @c std::ranges::unique and 0276 /// calculates the bin count from the size of the unique prefix (not the tail 0277 /// subrange). 0278 /// 3. Calculates the minimum and maximum using @c std::ranges::minmax. 0279 /// 4. Adjusts the maximum to include an additional bin by adding the bin step 0280 /// size. 0281 /// 0282 /// @param xPos A reference to a vector of doubles. 0283 /// @return A tuple containing: 0284 /// - The minimum value (double) 0285 /// - The adjusted maximum value (double) 0286 /// - The bin count (std::size_t) 0287 /// 0288 /// @note The vector xPos will be modified during the call. 0289 inline auto getMinMaxAndBinCount(std::vector<double>& xPos) { 0290 // sort the values for unique() 0291 std::ranges::sort(xPos); 0292 0293 // get the number of bins over unique values 0294 // ranges::unique returns [ret, end): duplicate tail; unique elements are 0295 // [begin, ret) 0296 const auto uniqueTail = std::ranges::unique(xPos); 0297 const std::size_t xBinCount = static_cast<std::size_t>( 0298 std::ranges::distance(xPos.begin(), uniqueTail.begin())); 0299 0300 // get the minimum and maximum 0301 auto [xMin, xMax] = std::ranges::minmax(xPos); 0302 0303 // calculate maxima (add one last bin, because bin value always corresponds to 0304 // left boundary) 0305 const double stepX = (xMax - xMin) / static_cast<double>(xBinCount - 1); 0306 xMax += stepX; 0307 0308 // Return all values as a tuple 0309 return std::make_tuple(xMin, xMax, xBinCount); 0310 } 0311 0312 } // namespace detail 0313 0314 } // namespace Acts
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|