File indexing completed on 2026-04-09 07:45:57
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include <cassert>
0012 #include <cmath>
0013 #include <concepts>
0014 #include <stdexcept>
0015 #include <type_traits>
0016
0017 namespace Acts {
0018
0019
0020
0021
0022
0023 template <typename T>
0024 constexpr T abs(const T n) {
0025 if (std::is_constant_evaluated()) {
0026 if constexpr (std::is_signed_v<T>) {
0027 if (n < 0) {
0028 return -n;
0029 }
0030 }
0031 return n;
0032 } else {
0033 return std::abs(n);
0034 }
0035 }
0036
0037
0038
0039
0040
0041
0042
0043 template <typename out_t, typename sign_t>
0044 constexpr out_t copySign(const out_t& copyTo, const sign_t& sign) {
0045 if constexpr (std::is_enum_v<sign_t>) {
0046 return copySign(copyTo, static_cast<std::underlying_type_t<sign_t>>(sign));
0047 } else {
0048 constexpr sign_t zero = 0;
0049 return sign >= zero ? copyTo : -copyTo;
0050 }
0051 }
0052
0053
0054
0055
0056
0057 template <typename T, std::integral P>
0058 constexpr T pow(T x, P p) {
0059 if (std::is_constant_evaluated()) {
0060 constexpr T one = 1;
0061 if constexpr (std::is_signed_v<P>) {
0062 if (p < 0 && abs(x) > std::numeric_limits<T>::epsilon()) {
0063 x = one / x;
0064 p = -p;
0065 }
0066 }
0067 using unsigned_p = std::make_unsigned_t<P>;
0068 return p == 0 ? one : x * pow(x, static_cast<unsigned_p>(p) - 1);
0069 } else {
0070 return static_cast<T>(std::pow(x, static_cast<T>(p)));
0071 }
0072 }
0073
0074
0075
0076
0077 template <typename T>
0078 constexpr auto square(T x) {
0079 return x * x;
0080 }
0081
0082
0083
0084
0085 template <typename... T>
0086 constexpr auto hypotSquare(T... args) {
0087 return (square(args) + ...);
0088 }
0089
0090
0091
0092
0093 template <typename... T>
0094 constexpr auto fastHypot(T... args) {
0095 return std::sqrt(hypotSquare(args...));
0096 }
0097
0098
0099
0100 template <typename T>
0101 constexpr auto fastHypot(T arg) {
0102 return std::abs(arg);
0103 }
0104
0105
0106
0107
0108 template <typename... T>
0109 constexpr auto slowHypot(T... args) {
0110 decltype((args + ...)) r = 0;
0111 ((r = std::hypot(r, args)), ...);
0112 return r;
0113 }
0114
0115
0116
0117 template <typename T>
0118 constexpr auto slowHypot(T arg) {
0119 return std::abs(arg);
0120 }
0121
0122
0123
0124
0125 template <typename T>
0126 constexpr auto slowHypot(T x, T y) {
0127 return std::hypot(x, y);
0128 }
0129
0130
0131
0132
0133
0134 template <typename T>
0135 constexpr auto slowHypot(T x, T y, T z) {
0136 return std::hypot(x, y, z);
0137 }
0138
0139
0140
0141
0142
0143
0144
0145 template <typename T, typename... Args>
0146 constexpr auto cathetusSquareUnchecked(T hypotenuse, Args... args) {
0147 return square(hypotenuse) - hypotSquare(args...);
0148 }
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159 template <typename T, typename... Args>
0160 constexpr auto cathetusSquare(T hypotenuse, Args... args) {
0161 const auto hypotenuseSquare = square(hypotenuse);
0162 const auto argsSquareSum = hypotSquare(args...);
0163 if (std::floating_point<T> && hypotenuseSquare < argsSquareSum) {
0164 return std::numeric_limits<T>::quiet_NaN();
0165 } else if (std::integral<T> && hypotenuseSquare < argsSquareSum) {
0166 throw std::domain_error(
0167 "hypotenuse must be greater than or equal to the cathetus");
0168 }
0169 return hypotenuseSquare - argsSquareSum;
0170 }
0171
0172
0173
0174
0175
0176 template <typename T, typename... Args>
0177 constexpr auto fastCathetus(T hypotenuse, Args... args) {
0178 const auto hypotArgs = fastHypot(args...);
0179 return std::sqrt((hypotenuse - hypotArgs) * (hypotenuse + hypotArgs));
0180 }
0181
0182
0183
0184
0185
0186
0187 template <typename T, typename... Args>
0188 constexpr auto slowCathetus(T hypotenuse, Args... args) {
0189 const auto hypotArgs = slowHypot(args...);
0190 return std::sqrt((hypotenuse - hypotArgs) * (hypotenuse + hypotArgs));
0191 }
0192
0193
0194
0195
0196 template <std::integral T>
0197 constexpr T sumUpToN(const T N) {
0198 return N * (N + 1) / 2;
0199 }
0200
0201
0202
0203
0204
0205
0206
0207 template <std::unsigned_integral T>
0208 constexpr T product(const T lowerN, const T upperN) {
0209 if (lowerN == T{0}) {
0210 return T{0};
0211 }
0212
0213 T value{1};
0214 for (T iter = lowerN; iter <= upperN; ++iter) {
0215 if (std::is_constant_evaluated() &&
0216 value > std::numeric_limits<T>::max() / iter) {
0217 throw std::overflow_error("product overflow");
0218 }
0219 assert(value <= std::numeric_limits<T>::max() / iter);
0220
0221 value *= iter;
0222 }
0223
0224 return value;
0225 }
0226
0227
0228
0229
0230 template <std::unsigned_integral T>
0231 constexpr T factorial(const T N) {
0232 return product<T>(1, N);
0233 }
0234
0235
0236
0237
0238
0239
0240
0241
0242 template <std::unsigned_integral T>
0243 constexpr T binomial(const T n, const T k) {
0244 if (std::is_constant_evaluated() && k > n) {
0245 throw std::overflow_error("k must be <= n");
0246 }
0247 assert(k <= n && "k must be <= n");
0248
0249 return product<T>(n - k + 1, n) / factorial<T>(k);
0250 }
0251
0252 }