Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Foundation/Math/Common.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Math_Common_h_
5#define _Stroika_Foundation_Math_Common_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <algorithm>
10#include <cmath> // though perhaps not strictly needed, probably wanted if including Stroika/Foundation/Math/Common.h
11#include <limits>
12#include <numbers>
13#include <type_traits>
14
15#include "Stroika/Foundation/Common/Concepts.h"
16
17/**
18 * TODO:
19 * @todo This rounding code (RoundUpTo/RoundDownTo) doesn't work for floating point numbers.
20 * That should either be fixed or documented better why.
21 *
22 * @todo Think through (and reconsider) the epsilon parameter (10000 times numeric_limits<>::epsilon())
23 * to NearlyEquals ().
24 */
25namespace Stroika::Foundation {
26 using namespace std;
27}
28
29namespace Stroika::Foundation::Math {
30
31 /**
32 * \brief Returns the special value "quiet not-a-number", as represented by the floating-point type T (default double)
33 */
34 template <floating_point T = double>
35 constexpr T nan ();
36
37 /**
38 */
39 template <floating_point T = double>
40 constexpr T infinity ();
41
42 /**
43 */
44 [[deprecated ("Since C++20, use std::numbers::e_v")]] constexpr double kE = numbers::e_v<double>;
45
46 /**
47 */
48 [[deprecated ("Since C++20, use std::numbers::pi_v")]] constexpr double kPi = numbers::pi_v<double>;
49
50 /**
51 * RoundUpTo() - round towards positive infinity.
52 *
53 * T can be any of int, long, or long long, or unsigned int, or unsigned long, or unsigned long long
54 * (after review of this API/implementation, probably should add short/char with unsigned variants
55 * to this list)
56 *
57 * \note - to RoundUp - just use ceil ()
58 */
59 template <Common::IBuiltinArithmetic T>
60 constexpr T RoundUpTo (T x, T toNearest);
61
62 /**
63 * RoundDownTo() - round towards negative infinity.
64 *
65 * T can be any of int, long, or long long, or unsigned int, or unsigned long, or unsigned long long
66 * (after review of this API/implementation, probably should add short/char with unsigned variants
67 * to this list)
68 */
69 template <Common::IBuiltinArithmetic T>
70 constexpr T RoundDownTo (T x, T toNearest);
71
72 /**
73 * Round (FLOAT_TYPE x):
74 * Convert from a floating point value to an integer value - like std::round () - except that round () returns a floating
75 * point value that must be manually converted to an integer. That conversion - if there is overflow - is undefined.
76 *
77 * From http://open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf - section 4.9 Floating-integral conversion
78 * The conversion truncates; that is, the fractional part is discarded. The behavior is undefined
79 * if the truncated value cannot be represented in the destination type
80 *
81 * Round (FLOAT_TYPE x, unsigned int nDigitsOfPrecision)
82 * returns a float with the given number of digits of precision (rounding properly).
83 * Note here 'precision' means significant figures.
84 * in C++26 this will become constexpr, but cannot for now (cuz pow constexpr in c++26).
85 * \pre nDigitsOfPrecision >= 1
86 * EX:
87 * Round (1.2, 2) => 1.2
88 * Round (1.23, 2) => 1.2
89 * Round (123, 2) => 120
90 * Round (3724089.418996166, 6) => 3724090.0 (roughly - within 10 apx)
91 * Round (3724089.418996166, 7) => 3724089.0 (roughly - within 1 apx)
92 * Round (3724089.418996166, 8) -> 3724089.4 (roughly - within .1 apx)
93 * Round (inf or nan, anynumber) => inf or nan
94 *
95 * \note this rounding is imperfect, especially as sizeof FLOAT_TYPE gets smaller and nDigitsOfPrecision gets larger
96 * since there is rounding error. Maybe another algorithm could do better, but its intrinsically not going to be perfect
97 * due to mismatch between bases of numeric representation and tests (base 2 vs base 10).
98 */
99 template <integral INT_TYPE, floating_point FLOAT_TYPE>
100 constexpr INT_TYPE Round (FLOAT_TYPE n);
101 template <floating_point FLOAT_TYPE>
102 FLOAT_TYPE Round (FLOAT_TYPE n, unsigned int nDigitsOfPrecision);
103
104 /**
105 * \brief like std::trunc, except returns integer, and saturates value at ends of range
106 *
107 * \note if n is inf, this is fine, but if nan, throws
108 */
109 template <integral INT_TYPE, floating_point FLOAT_TYPE>
110 constexpr INT_TYPE Trunc (FLOAT_TYPE n);
111
112 /**
113 * NearlyEquals() can be used as a utility for arithmetic (mostly floating point) comparisons.
114 *
115 * Many other stroika classes (e.g. DateTime) provide overloads with the same arguments.
116 *
117 * \note Function is commutative (in first two args):
118 * NearlyEquals(a,b) == NearlyEquals (b,a)
119 *
120 * \note NearlyEquals (Nan,Nan) is TRUE, unlike with operator==, but nan != anything else
121 * https://medium.com/engineering-housing/nan-is-not-equal-to-nan-771321379694
122 *
123 * This choice was made because its often handy to use NAN as a sentinel value, and then
124 * this is generally the right choice (not representing a failed computation, but often missing data).
125 *
126 * \note - this function handles nans and inf values appropriately:
127 * o NearlyEquals (Math::nan(), Math::nan());
128 * o not NearlyEquals (Math::nan(), 3);
129 * o NearlyEquals (Math::infinity (), Math::infinity ())
130 * o not NearlyEquals (Math::infinity (), -Math::infinity ())
131 * o not NearlyEquals (Math::infinity (), 3)
132 *
133 * \pre epsilon >= 0 OR l or r is nan or inf (in which case epsilon ignored)
134 *
135 * \note - this is ROUGHLY the same as abs (l)-abs(r) < 0.00001, except for the issue that
136 * the right comparison depends a bit on the scale of the numbers l and r, and NearlyEquals
137 * automatically adjusts for this (with no epsilon specified).
138 * \see https://realtimecollisiondetection.net/blog/?p=89 for a good treatment of this
139 *
140 * \note this function has changed slightly (simplified and constexpr) - since Stroika v2.1, and may produce
141 * different answers in corner cases (better scale invariance added in v3).
142 */
143 template <Common::IBuiltinArithmetic T1, Common::IBuiltinArithmetic T2>
144 constexpr bool NearlyEquals (T1 l, T2 r);
145 template <Common::IBuiltinArithmetic T1, Common::IBuiltinArithmetic T2, typename EPSILON_TYPE>
146 constexpr bool NearlyEquals (T1 l, T2 r, EPSILON_TYPE epsilon);
147
148 /**
149 * \brief PinToSpecialPoint() returns its first argument, or something NearlyEquals() to it (but better)
150 *
151 * Sometimes with floating point arithmetic you get points to move slightly. For example, if you
152 * want a number between 0.0 and 1.0, you might do some arithmetic and get -0.000000001; This might
153 * cause the value to be out of the range 0..1. You might want to assert/assure the value never goes below
154 * zero or above one, but this is outside that range.
155 *
156 * This helper allows values near a special value (like the endpoint of that range) to 'pin' to be
157 * exactly that endpoint.
158 *
159 * But PinToSpecialPoint () always returns its first argument, or something NearlyEquals() to it.
160 */
161 template <floating_point T>
162 constexpr T PinToSpecialPoint (T p, T special);
163 template <floating_point T>
164 constexpr T PinToSpecialPoint (T p, T special, T epsilon);
165
166 /**
167 * Return a value at this at least the given value. This is the same as "max" as it turns out,
168 * but writing it this way I find more clear
169 *
170 * @see AtMost
171 */
172 template <typename T>
173 constexpr T AtLeast (T initialValue, T lowerBound);
174
175 /**
176 * Return a value at this at no more than the given value. This is the same as "min" as it turns out,
177 * but writing it this way I find more clear
178 *
179 * @see AtLeast
180 */
181 template <typename T>
182 constexpr T AtMost (T initialValue, T upperBound);
183
184 /**
185 * This returns the given value, as the new type NEW_T, but if the value wont fit in NEW_T, it returns
186 * the largest value that would.
187 *
188 * This is handy for stuff like ::write() system calls, where you might be given a ptrdiff_t, and have to
189 * call something taking int, and given how these types might be different sizes, it simplifies picking
190 * the right size to call write with.
191 *
192 * \par Example Usage
193 * \code
194 * size_t nRequested = intoEnd - intoStart;
195 * return static_cast<size_t> (Execution::ThrowPOSIXErrNoIfNegative (::_read (fFD_, intoStart, Math::PinToMaxForType<unsigned int> (nRequested))));
196 * \endcode
197 */
198 template <typename NEW_T, typename T>
199 constexpr NEW_T PinToMaxForType (T initialValue);
200
201 /**
202 * \note we define this as an alternative to std::abs () - since that is not extendible to other types (just an overload for floating point and int types)
203 *
204 * \note when we port stroika bignum package - this should support those bignums.
205 * \note std::abs() not constexpr until C++ 23 (which is why this isn't)
206 */
207 template <Common::IBuiltinArithmetic T, typename RESULT_TYPE = T>
208 constexpr RESULT_TYPE Abs (T v);
209
210 /**
211 * \note - when we port stroika bignum package - this should support those bignums.
212 */
213 template <integral T>
214 constexpr bool IsOdd (T v);
215
216 /**
217 * Note - when we port stroika bignum package - this should support those bignums.
218 */
219 template <integral T>
220 constexpr bool IsEven (T v);
221
222 /**
223 * Note - when we port stroika bignum package - this should support those bignums.
224 *
225 * @todo Also - we want multiple algorithms for this - including simple search (done now), sieve, and
226 * partitioned sieves (so use less ram).
227 *
228 * \pre v >= 0
229 */
230 template <integral T>
231 bool IsPrime (T v);
232
233}
234
235/*
236 ********************************************************************************
237 ***************************** Implementation Details ***************************
238 ********************************************************************************
239 */
240#include "Common.inl"
241
242#endif /*_Stroika_Foundation_Math_Common_h_*/
STL namespace.