7#include "Stroika/Foundation/Common/Concepts.h"
11namespace Stroika::Foundation::Math {
18 template <
floating_po
int T>
21 return numeric_limits<T>::quiet_NaN ();
29 template <
floating_po
int T>
30 constexpr T infinity ()
32 return numeric_limits<T>::infinity ();
41 template <
unsigned_
integral T>
42 constexpr T RoundUpTo_UnSignedHelper_ (T x, T toNearest)
44 return (((x + toNearest - 1u) / toNearest) * toNearest);
46 template <
unsigned_
integral T>
47 constexpr T RoundDownTo_UnSignedHelper_ (T x, T toNearest)
49 return ((x / toNearest) * toNearest);
51 template <
signed_
integral T>
52 constexpr T RoundUpTo_SignedHelper_ (T x, T toNearest)
54 using UNSIGNED_T = make_unsigned_t<T>;
55 Require (toNearest > 0);
57 return (-
static_cast<T
> (RoundDownTo_UnSignedHelper_ (
static_cast<UNSIGNED_T
> (-x),
static_cast<UNSIGNED_T
> (toNearest))));
60 return static_cast<T
> (RoundUpTo_UnSignedHelper_<UNSIGNED_T> (x, toNearest));
63 template <
signed_
integral T>
64 constexpr T RoundDownTo_SignedHelper_ (T x, T toNearest)
66 using UNSIGNED_T = make_unsigned_t<T>;
67 Require (toNearest > 0);
69 return (-
static_cast<T
> (RoundUpTo_UnSignedHelper_ (
static_cast<UNSIGNED_T
> (-x),
static_cast<UNSIGNED_T
> (toNearest))));
72 return (RoundDownTo_UnSignedHelper_ (
static_cast<UNSIGNED_T
> (x),
static_cast<UNSIGNED_T
> (toNearest)));
75 template <
signed_
integral T>
76 constexpr T RoundUpTo_ (T x, T toNearest)
78 return Private::RoundUpTo_SignedHelper_<T> (x, toNearest);
80 template <
unsigned_
integral T>
81 constexpr T RoundUpTo_ (T x, T toNearest)
83 return Private::RoundUpTo_UnSignedHelper_<T> (x, toNearest);
85 template <
signed_
integral T>
86 constexpr T RoundDownTo_ (T x, T toNearest)
88 return Private::RoundDownTo_SignedHelper_<T> (x, toNearest);
90 template <
unsigned_
integral T>
91 constexpr T RoundDownTo_ (T x, T toNearest)
93 return Private::RoundDownTo_UnSignedHelper_<T> (x, toNearest);
96 template <Common::IBuiltinArithmetic T>
97 constexpr T RoundUpTo (T x, T toNearest)
100 return Private::RoundUpTo_ (x, toNearest);
108 template <Common::IBuiltinArithmetic T>
109 constexpr T RoundDownTo (T x, T toNearest)
112 return Private::RoundDownTo_ (x, toNearest);
120 template <
integral INT_TYPE,
floating_po
int FLOAT_TYPE>
121 constexpr INT_TYPE Round (FLOAT_TYPE n)
123 FLOAT_TYPE tmp = ::round (n);
125#if (defined(__clang_major__) && !defined(__APPLE__) && (__clang_major__ >= 10)) || \
126 (defined(__clang_major__) && defined(__APPLE__) && (__clang_major__ >= 13))
127 DISABLE_COMPILER_CLANG_WARNING_START (
"clang diagnostic ignored \"-Wimplicit-int-float-conversion\"");
129 return tmp >= numeric_limits<INT_TYPE>::max () ? numeric_limits<INT_TYPE>::max () : static_cast<INT_TYPE> (tmp);
130#if (defined(__clang_major__) && !defined(__APPLE__) && (__clang_major__ >= 10)) || \
131 (defined(__clang_major__) && defined(__APPLE__) && (__clang_major__ >= 13))
132 DISABLE_COMPILER_CLANG_WARNING_END (
"clang diagnostic ignored \"-Wimplicit-int-float-conversion\"");
136 return tmp <= numeric_limits<INT_TYPE>::min () ? numeric_limits<INT_TYPE>::min () : static_cast<INT_TYPE> (tmp);
139 template <
floating_po
int FLOAT_TYPE>
140 inline FLOAT_TYPE Round (FLOAT_TYPE n,
unsigned int nDigitsOfPrecision)
142 using Common::StdCompat::isinf;
143 using Common::StdCompat::isnan;
144 Require (nDigitsOfPrecision >= 1);
145 if (isnan (n) or isinf (n)) [[unlikely]] {
148 auto absN = fabs (n);
149 unsigned int digitsBeforeDecimal = 0;
151 digitsBeforeDecimal = Trunc<unsigned int> (log10 (absN)) + 1;
153 FLOAT_TYPE pow10Shifter = pow (10,
static_cast<int> (nDigitsOfPrecision) -
static_cast<int> (digitsBeforeDecimal));
154 return round (n * pow10Shifter) / pow10Shifter;
163 void ThrowTruncOfNAN_ ();
165 template <
integral INT_TYPE,
floating_po
int FLOAT_TYPE>
166 constexpr INT_TYPE Trunc (FLOAT_TYPE n)
168 using Common::StdCompat::isnan;
169 if (isnan (n)) [[unlikely]] {
170 Private_::ThrowTruncOfNAN_ ();
172 FLOAT_TYPE tmp = ::trunc (n);
174#if (defined(__clang_major__) && !defined(__APPLE__) && (__clang_major__ >= 10)) || \
175 (defined(__clang_major__) && defined(__APPLE__) && (__clang_major__ >= 13))
176 DISABLE_COMPILER_CLANG_WARNING_START (
"clang diagnostic ignored \"-Wimplicit-int-float-conversion\"");
178 return tmp >= numeric_limits<INT_TYPE>::max () ? numeric_limits<INT_TYPE>::max () : static_cast<INT_TYPE> (tmp);
179#if (defined(__clang_major__) && !defined(__APPLE__) && (__clang_major__ >= 10)) || \
180 (defined(__clang_major__) && defined(__APPLE__) && (__clang_major__ >= 13))
181 DISABLE_COMPILER_CLANG_WARNING_END (
"clang diagnostic ignored \"-Wimplicit-int-float-conversion\"");
185 return tmp <= numeric_limits<INT_TYPE>::min () ? numeric_limits<INT_TYPE>::min () : static_cast<INT_TYPE> (tmp);
194 template <Common::IBuiltinArithmetic T1, Common::IBuiltinArithmetic T2,
typename EPSILON_TYPE>
195 constexpr bool NearlyEquals (T1 l, T2 r, EPSILON_TYPE epsilon)
197 using Common::StdCompat::isinf;
198 using Common::StdCompat::isnan;
199 if (isnan (l) or isnan (r)) [[unlikely]] {
202 else if (isinf (l) or isinf (r)) [[unlikely]] {
208 Require (epsilon >= 0);
209 Assert (not isnan (l) and not isnan (r) and not isinf (l) and not isinf (r));
210 return Abs (diff) <= epsilon;
212 template <Common::IBuiltinArithmetic T1, Common::IBuiltinArithmetic T2>
213 constexpr bool NearlyEquals (T1 l, T2 r)
215 using TC = common_type_t<T1, T2>;
216 if constexpr (floating_point<TC>) {
217 constexpr TC kEpsilon_ = 10000 * numeric_limits<TC>::epsilon ();
220 TC useEpsilon = kEpsilon_ * std::max<TC> ({
static_cast<TC
> (1.0),
static_cast<TC
> (Abs (l)),
static_cast<TC
> (Abs (r))});
221 return NearlyEquals (l, r, useEpsilon);
233 template <
floating_po
int T>
234 constexpr T PinToSpecialPoint (T p, T special)
236 if (Math::NearlyEquals (p, special)) {
241 template <
floating_po
int T>
242 constexpr T PinToSpecialPoint (T p, T special, T epsilon)
244 if (Math::NearlyEquals (p, special, epsilon)) {
255 template <
typename T>
256 [[deprecated (
"Since Stroika v3.0d12 - use std::clamp")]]
constexpr T PinInRange (T initialValue, T lowerBound, T upperBound)
258 Require (lowerBound <= upperBound);
259 auto r = max (lowerBound, min (upperBound, initialValue));
260 auto r2 = clamp (initialValue, lowerBound, upperBound);
270 template <
typename T>
271 constexpr T AtLeast (T initialValue, T lowerBound)
273 return max (initialValue, lowerBound);
281 template <
typename T>
282 constexpr T AtMost (T initialValue, T upperBound)
284 return min (initialValue, upperBound);
292 template <
typename NEW_T,
typename T>
293 constexpr NEW_T PinToMaxForType (T initialValue)
295 using LargerType =
decltype (NEW_T{} + T{});
296 return static_cast<NEW_T
> (min<LargerType> (initialValue, numeric_limits<NEW_T>::max ()));
304 template <Common::IBuiltinArithmetic T,
typename RESULT_TYPE>
305 constexpr RESULT_TYPE Abs (T v)
307#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23
308 if constexpr (Common::IAnyOf<T, int, intmax_t>) {
311 else if constexpr (Common::IAnyOf<T, long>) {
312 return std::labs (v);
314 else if constexpr (Common::IAnyOf<T, long long>) {
315 return std::llabs (v);
318 return v < 0 ? -v : v;
326 template <
integral T>
327 constexpr bool IsOdd (T v)
337 template <
integral T>
338 constexpr bool IsEven (T v)
340 static_assert (integral<T>);
349 template <
integral T>
361 T checkUpTo =
static_cast<T
> (::sqrt (v)) +
static_cast<T
> (1);
363 for (T d = 2; d <= checkUpTo; ++d) {
constexpr bool isnan(T v) noexcept