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