cheatah
Source

stdlib/math/math.hpp

1// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).
2// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.
3#pragma once
5/**
6 * @file math.hpp
7 * @brief cheatah `math` — scalar math: Python's `math` module plus the
8 * `abs`/`min`/`max`/`pow` built-ins. `import math` to use it.
9 *
10 * Every function here is **pure and allocation-free** (operates on `double` /
11 * `long long` by value). Unit tests: `stdlib/tests/math_test.cpp`. The whole
12 * suite runs under AddressSanitizer (the `asan` preset) and Valgrind
13 * (`security/run-valgrind.sh`) on every QA-gate run.
14 *
15 * Doc convention (see also the other stdlib headers): each function documents
16 * its runtime complexity with @complexity, its heap allocation with @alloc, and
17 * the @test that covers it.
18 */
19#include <cmath>
20#include <concepts>
21#include <limits>
22#include <numbers>
23#include <type_traits>
25namespace cheatah::math {
27// Concepts naming what each scalar op needs, so a misuse fails with the concept's
28// name rather than a deep template error (see constrain-all-templates policy).
29/// Numeric<T>: an arithmetic type — the int/float family `abs`/`pow` operate on.
30template <typename T>
31concept Numeric = std::is_arithmetic_v<T>;
32/// Ordered<T>: `<`-comparable, so `min`/`max` can pick the smaller/larger.
33template <typename T>
34concept Ordered = requires(const T& a, const T& b) {
35 { a < b } -> std::convertible_to<bool>;
36};
38// ---- constants ----
39inline constexpr double pi = std::numbers::pi; ///< π.
40inline constexpr double e = std::numbers::e; ///< Euler's number e.
41inline constexpr double tau = 2.0 * pi; ///< τ = 2π.
42inline constexpr double inf = std::numeric_limits<double>::infinity(); ///< +∞.
43inline constexpr double nan = std::numeric_limits<double>::quiet_NaN(); ///< quiet NaN.
45// ---- math-related built-ins (templated) ----
47/**
48 * Absolute value.
49 * @param x any signed value.
50 * @return |@p x|.
51 * @complexity O(1) time.
52 * @alloc none.
53 * @warning For a signed integer type the most-negative value cannot be negated:
54 * `abs` of it overflows (undefined behavior).
55 * @test CheatahMath.BuiltinLikeOps
56 * @crtest MathCompileRun.Abs
57 * @systest StdlibE2E.Math
58 */
59template <Numeric T>
60T abs(T x) { return x < T{} ? -x : x; }
62/**
63 * Smallest of two-or-more values (variadic; the overloads chain to fold extra args).
64 *
65 * Returns a reference bound to whichever argument compares smaller; on a tie
66 * (neither `b < a`) it returns @p a. Because the result is a reference into the
67 * caller's arguments, it dangles when the operands are temporaries.
68 * @param a,b the values to compare (`operator<` required).
69 * @return a reference to the minimum.
70 * @complexity O(n) in the argument count.
71 * @alloc none.
72 * @test CheatahMath.BuiltinLikeOps
73 * @crtest MathCompileRun.Min
74 * @systest StdlibE2E.Math
75 */
76template <Ordered T>
77const T& min(const T& a, const T& b) { return (b < a) ? b : a; }
78/**
79 * Smallest of three-or-more values (folds the extra args onto the two-argument overload).
80 * @param a,b the first two values.
81 * @param rest the remaining values (`operator<` required).
82 * @return a reference to the minimum.
83 * @complexity O(n) in the argument count.
84 * @alloc none.
85 * @test CheatahMath.BuiltinLikeOps
86 * @crtest MathCompileRun.Min
87 * @systest StdlibE2E.Math
88 */
89template <Ordered T, Ordered... Rest>
90const T& min(const T& a, const T& b, const Rest&... rest) { return min(min(a, b), rest...); }
92/**
93 * Largest of two-or-more values (variadic; the overloads chain to fold extra args).
94 *
95 * Returns a reference bound to whichever argument compares larger; on a tie
96 * (neither `a < b`) it returns @p a. As with min, the returned reference
97 * dangles if the operands are temporaries.
98 * @param a,b the values to compare (`operator<` required).
99 * @return a reference to the maximum.
100 * @complexity O(n) in the argument count.
101 * @alloc none.
102 * @test CheatahMath.BuiltinLikeOps
103 * @crtest MathCompileRun.Max
104 * @systest StdlibE2E.Math
105 */
106template <Ordered T>
107const T& max(const T& a, const T& b) { return (a < b) ? b : a; }
108/**
109 * Largest of three-or-more values (folds the extra args onto the two-argument overload).
110 * @param a,b the first two values.
111 * @param rest the remaining values (`operator<` required).
112 * @return a reference to the maximum.
113 * @complexity O(n) in the argument count.
114 * @alloc none.
115 * @test CheatahMath.BuiltinLikeOps
116 * @crtest MathCompileRun.Max
117 * @systest StdlibE2E.Math
118 */
119template <Ordered T, Ordered... Rest>
120const T& max(const T& a, const T& b, const Rest&... rest) { return max(max(a, b), rest...); }
122/**
123 * Power.
124 *
125 * Both operands are cast to `double` and forwarded to `std::pow`, so this
126 * follows IEEE-754 semantics (e.g. `pow(0, 0)` is 1, and a negative base with a
127 * non-integer exponent yields NaN); integer arguments lose exactness beyond
128 * 2^53.
129 * @param base the base.
130 * @param exp the exponent.
131 * @return @p base raised to @p exp (computed as `double`).
132 * @complexity O(1) time.
133 * @alloc none.
134 * @test CheatahMath.BuiltinLikeOps
135 * @crtest MathCompileRun.Pow
136 * @systest StdlibE2E.Math
137 */
138template <Numeric Base, Numeric Exp>
139double pow(Base base, Exp exp) {
140 return std::pow(static_cast<double>(base), static_cast<double>(exp));
143// ---- scalar functions (compiled into the library) ----
144// All of the following are O(1) time with no heap allocation.
146/**
147 * Square root.
148 *
149 * Returns NaN (rather than throwing) for a negative radicand; `sqrt(-0.0)` is
150 * `-0.0` and `sqrt(+inf)` is `+inf`.
151 * @param x radicand (NaN if @p x < 0).
152 * @return √@p x.
153 * @complexity O(1).
154 * @alloc none.
155 * @test CheatahMath.ScalarFunctions
156 * @crtest MathCompileRun.Sqrt
157 * @systest StdlibE2E.Math
158 */
159double sqrt(double x);
160/**
161 * Cube root.
162 *
163 * Defined for the whole real line, including negatives (unlike sqrt): the
164 * result keeps the sign of @p x, so `cbrt(-8)` is `-2`.
165 * @param x any real.
166 * @return ∛@p x.
167 * @complexity O(1).
168 * @alloc none.
169 * @test CheatahMath.TranscendentalAndRounding
170 * @crtest MathCompileRun.Cbrt
171 * @systest StdlibE2E.Math
172 */
173double cbrt(double x);
174/**
175 * Absolute value of a double.
176 * @param x any real.
177 * @return |@p x|.
178 * @complexity O(1).
179 * @alloc none.
180 * @test CheatahMath.TranscendentalAndRounding
181 * @crtest MathCompileRun.Fabs
182 * @systest StdlibE2E.Math
183 */
184double fabs(double x);
185/**
186 * Round toward −∞.
187 *
188 * Returns the largest integral value not greater than @p x as a `double`;
189 * already-integral, NaN, and ±∞ inputs are returned unchanged, and the sign of
190 * zero is preserved.
191 * @param x any real.
192 * @return ⌊@p x⌋.
193 * @complexity O(1).
194 * @alloc none.
195 * @test CheatahMath.ScalarFunctions
196 * @crtest MathCompileRun.Floor
197 * @systest StdlibE2E.Math
198 */
199double floor(double x);
200/**
201 * Round toward +∞.
202 *
203 * Returns the smallest integral value not less than @p x as a `double`;
204 * already-integral, NaN, and ±∞ inputs are returned unchanged. For @p x in
205 * (−1, 0) the result is `-0.0`.
206 * @param x any real.
207 * @return ⌈@p x⌉.
208 * @complexity O(1).
209 * @alloc none.
210 * @test CheatahMath.ScalarFunctions
211 * @crtest MathCompileRun.Ceil
212 * @systest StdlibE2E.Math
213 */
214double ceil(double x);
215/**
216 * Round toward zero.
217 *
218 * Discards the fractional part, rounding toward zero rather than ±∞ (so it
219 * differs from floor on negatives, e.g. `trunc(-2.7)` is `-2.0`); the sign of
220 * @p x, NaN, and ±∞ are preserved.
221 * @param x any real.
222 * @return @p x with the fraction dropped.
223 * @complexity O(1).
224 * @alloc none.
225 * @test CheatahMath.TranscendentalAndRounding
226 * @crtest MathCompileRun.Trunc
227 * @systest StdlibE2E.Math
228 */
229double trunc(double x);
230/**
231 * Round to nearest (half away from zero).
232 *
233 * Halfway cases are rounded away from zero, not to even, so `round(2.5)` is `3`
234 * and `round(-2.5)` is `-3` — this differs from Python's banker's rounding;
235 * NaN and ±∞ pass through unchanged.
236 * @param x any real.
237 * @return rounded @p x.
238 * @complexity O(1).
239 * @alloc none.
240 * @test CheatahMath.ScalarFunctions
241 * @crtest MathCompileRun.Round
242 * @systest StdlibE2E.Math
243 */
244double round(double x);
245/**
246 * Exponential.
247 *
248 * Overflows to `+inf` for large @p x and underflows to `0` for large negative
249 * @p x; `exp(-inf)` is `0` and `exp(+inf)` is `+inf`.
250 * @param x any real.
251 * @return e^@p x.
252 * @complexity O(1).
253 * @alloc none.
254 * @test CheatahMath.TranscendentalAndRounding
255 * @crtest MathCompileRun.Exp
256 * @systest StdlibE2E.Math
257 */
258double exp(double x);
259/**
260 * Natural logarithm.
261 *
262 * Returns `-inf` for @p x == 0 and NaN (rather than throwing) for negative
263 * @p x; out-of-domain input never raises an exception.
264 * @param x > 0.
265 * @return ln(@p x).
266 * @complexity O(1).
267 * @alloc none.
268 * @test CheatahMath.TranscendentalAndRounding
269 * @crtest MathCompileRun.Log
270 * @systest StdlibE2E.Math
271 */
272double log(double x);
273/**
274 * Base-2 logarithm.
275 *
276 * Returns `-inf` for @p x == 0 and NaN for negative @p x, matching log's
277 * out-of-domain behavior.
278 * @param x > 0.
279 * @return log₂(@p x).
280 * @complexity O(1).
281 * @alloc none.
282 * @test CheatahMath.ScalarFunctions
283 * @crtest MathCompileRun.Log2
284 * @systest StdlibE2E.Math
285 */
286double log2(double x);
287/**
288 * Base-10 logarithm.
289 *
290 * Returns `-inf` for @p x == 0 and NaN for negative @p x, matching log's
291 * out-of-domain behavior.
292 * @param x > 0.
293 * @return log₁₀(@p x).
294 * @complexity O(1).
295 * @alloc none.
296 * @test CheatahMath.TranscendentalAndRounding
297 * @crtest MathCompileRun.Log10
298 * @systest StdlibE2E.Math
299 */
300double log10(double x);
301/**
302 * Sine.
303 *
304 * The argument is interpreted in radians; precision degrades for very large
305 * magnitudes due to argument reduction, and `sin(±inf)` is NaN.
306 * @param x radians.
307 * @return sin(@p x).
308 * @complexity O(1).
309 * @alloc none.
310 * @test CheatahMath.Trigonometry
311 * @crtest MathCompileRun.Sin
312 * @systest StdlibE2E.Math
313 */
314double sin(double x);
315/**
316 * Cosine.
317 *
318 * The argument is interpreted in radians; precision degrades for very large
319 * magnitudes due to argument reduction, and `cos(±inf)` is NaN.
320 * @param x radians.
321 * @return cos(@p x).
322 * @complexity O(1).
323 * @alloc none.
324 * @test CheatahMath.Trigonometry
325 * @crtest MathCompileRun.Cos
326 * @systest StdlibE2E.Math
327 */
328double cos(double x);
329/**
330 * Tangent.
331 *
332 * The argument is in radians; near the poles (odd multiples of π/2, which are
333 * not exactly representable) the result is a large finite value rather than
334 * ±∞, and `tan(±inf)` is NaN.
335 * @param x radians.
336 * @return tan(@p x).
337 * @complexity O(1).
338 * @alloc none.
339 * @test CheatahMath.Trigonometry
340 * @crtest MathCompileRun.Tan
341 * @systest StdlibE2E.Math
342 */
343double tan(double x);
344/**
345 * Arcsine.
346 *
347 * Returns a value in [−π/2, π/2]; arguments outside [−1, 1] yield NaN rather
348 * than throwing.
349 * @param x in [−1, 1].
350 * @return asin(@p x) in radians.
351 * @complexity O(1).
352 * @alloc none.
353 * @test CheatahMath.Trigonometry
354 * @crtest MathCompileRun.Asin
355 * @systest StdlibE2E.Math
356 */
357double asin(double x);
358/**
359 * Arccosine.
360 *
361 * Returns a value in [0, π]; arguments outside [−1, 1] yield NaN rather than
362 * throwing.
363 * @param x in [−1, 1].
364 * @return acos(@p x) in radians.
365 * @complexity O(1).
366 * @alloc none.
367 * @test CheatahMath.Trigonometry
368 * @crtest MathCompileRun.Acos
369 * @systest StdlibE2E.Math
370 */
371double acos(double x);
372/**
373 * Arctangent.
374 *
375 * Accepts the whole real line and returns a value in (−π/2, π/2), approaching
376 * ±π/2 as @p x → ±∞.
377 * @param x any real.
378 * @return atan(@p x) in radians.
379 * @complexity O(1).
380 * @alloc none.
381 * @test CheatahMath.Trigonometry
382 * @crtest MathCompileRun.Atan
383 * @systest StdlibE2E.Math
384 */
385double atan(double x);
386/**
387 * Two-argument arctangent.
388 *
389 * Uses the signs of both arguments to select the correct quadrant, returning a
390 * value in (−π, π]; it is well-defined when @p x is zero (including the
391 * `atan2(0, 0)` case, which returns 0).
392 * @param y,x the coordinates.
393 * @return atan2(@p y, @p x) in radians.
394 * @complexity O(1).
395 * @alloc none.
396 * @test CheatahMath.Trigonometry
397 * @crtest MathCompileRun.Atan2
398 * @systest StdlibE2E.Math
399 */
400double atan2(double y, double x);
401/**
402 * Hypotenuse.
403 *
404 * Computes the 2-norm while avoiding intermediate overflow/underflow that a
405 * naive `sqrt(x*x + y*y)` would suffer; returns `+inf` if either argument is
406 * infinite (even when the other is NaN).
407 * @param x,y the legs.
408 * @return √(@p x²+@p y²) without overflow.
409 * @complexity O(1).
410 * @alloc none.
411 * @test CheatahMath.ScalarFunctions
412 * @crtest MathCompileRun.Hypot
413 * @systest StdlibE2E.Math
414 */
415double hypot(double x, double y);
416/**
417 * Floating-point remainder.
418 *
419 * Returns `x - n*y` for the integer `n` truncated toward zero, so the result
420 * takes the sign of the dividend @p x (unlike a Python-style modulo); a zero
421 * divisor yields NaN rather than throwing.
422 * @param x,y dividend, divisor.
423 * @return @p x mod @p y.
424 * @complexity O(1).
425 * @alloc none.
426 * @test CheatahMath.TranscendentalAndRounding
427 * @crtest MathCompileRun.Fmod
428 * @systest StdlibE2E.Math
429 */
430double fmod(double x, double y);
431/**
432 * Copy sign.
433 *
434 * Takes the magnitude from @p x and the sign bit from @p y; because it copies
435 * the IEEE sign bit, it distinguishes `+0.0` from `-0.0` and works even when
436 * @p x is NaN.
437 * @param x magnitude source.
438 * @param y sign source.
439 * @return |@p x| with @p y's sign.
440 * @complexity O(1).
441 * @alloc none.
442 * @test CheatahMath.TranscendentalAndRounding
443 * @crtest MathCompileRun.Copysign
444 * @systest StdlibE2E.Math
445 */
446double copysign(double x, double y);
447/**
448 * Radians → degrees.
449 * @param radians angle in radians.
450 * @return the angle in degrees.
451 * @complexity O(1).
452 * @alloc none.
453 * @test CheatahMath.ScalarFunctions
454 * @crtest MathCompileRun.Degrees
455 * @systest StdlibE2E.Math
456 */
457double degrees(double radians);
458/**
459 * Degrees → radians.
460 * @param degrees angle in degrees.
461 * @return the angle in radians.
462 * @complexity O(1).
463 * @alloc none.
464 * @test CheatahMath.TranscendentalAndRounding
465 * @crtest MathCompileRun.Radians
466 * @systest StdlibE2E.Math
467 */
468double radians(double degrees);
469/**
470 * Is NaN?
471 *
472 * The reliable NaN test, since NaN compares unequal to everything including
473 * itself (so `x != x` is the only other portable check).
474 * @param x any real.
475 * @return true iff @p x is NaN.
476 * @complexity O(1).
477 * @alloc none.
478 * @test CheatahMath.IsFiniteIsNanIsInf
479 * @crtest MathCompileRun.Isnan
480 * @systest StdlibE2E.Math
481 */
482bool isnan(double x);
483/**
484 * Is infinite?
485 *
486 * True for both `+inf` and `-inf`; false for NaN (use isnan for that) and for
487 * every finite value.
488 * @param x any real.
489 * @return true iff @p x is ±∞.
490 * @complexity O(1).
491 * @alloc none.
492 * @test CheatahMath.IsFiniteIsNanIsInf
493 * @crtest MathCompileRun.Isinf
494 * @systest StdlibE2E.Math
495 */
496bool isinf(double x);
497/**
498 * Is finite?
499 * @param x any real.
500 * @return true iff @p x is neither NaN nor ±∞.
501 * @complexity O(1).
502 * @alloc none.
503 * @test CheatahMath.IsFiniteIsNanIsInf
504 * @crtest MathCompileRun.Isfinite
505 * @systest StdlibE2E.Math
506 */
507bool isfinite(double x);
509/**
510 * Greatest common divisor.
511 *
512 * Operates on the absolute values via the Euclidean algorithm, so the result is
513 * non-negative; `gcd(0, 0)` is 0 and `gcd(n, 0)` is `|n|`. Passing
514 * `LLONG_MIN` overflows when negated.
515 * @param a,b integers.
516 * @return gcd(|@p a|, |@p b|).
517 * @complexity O(log min(a,b)) time.
518 * @alloc none.
519 * @test CheatahMath.Integer
520 * @crtest MathCompileRun.Gcd
521 * @systest StdlibE2E.Math
522 */
523long long gcd(long long a, long long b);
524/**
525 * Factorial.
526 *
527 * Computed by an iterative product from 2; @p n of 0 or 1 returns 1, and any
528 * negative @p n also returns 1 since the loop never executes (no error is
529 * raised). Results past 20! silently overflow `long long`.
530 * @param n ≥ 0 (small; overflows `long long` past 20!).
531 * @return @p n!.
532 * @complexity O(@p n) time.
533 * @alloc none.
534 * @test CheatahMath.Integer
535 * @crtest MathCompileRun.Factorial
536 * @systest StdlibE2E.Math
537 */
538long long factorial(long long n);
540} // namespace cheatah::math