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 once5
/**6
* @file math.hpp7
* @brief cheatah `math` — scalar math: Python's `math` module plus the8
* `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 whole12
* suite runs under AddressSanitizer (the `asan` preset) and Valgrind13
* (`security/run-valgrind.sh`) on every QA-gate run.14
*15
* Doc convention (see also the other stdlib headers): each function documents16
* its runtime complexity with @complexity, its heap allocation with @alloc, and17
* the @test that covers it.18
*/19
#include <cmath>20
#include <concepts>21
#include <limits>22
#include <numbers>23
#include <type_traits>25
namespace cheatah::math {27
// Concepts naming what each scalar op needs, so a misuse fails with the concept's28
// 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.30
template <typename T>31
concept Numeric = std::is_arithmetic_v<T>;32
/// Ordered<T>: `<`-comparable, so `min`/`max` can pick the smaller/larger.33
template <typename T>34
concept Ordered = requires(const T& a, const T& b) {35
{ a < b } -> std::convertible_to<bool>;36
};38
// ---- constants ----39
inline constexpr double pi = std::numbers::pi; ///< π.40
inline constexpr double e = std::numbers::e; ///< Euler's number e.41
inline constexpr double tau = 2.0 * pi; ///< τ = 2π.42
inline constexpr double inf = std::numeric_limits<double>::infinity(); ///< +∞.43
inline 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.BuiltinLikeOps56
* @crtest MathCompileRun.Abs57
* @systest StdlibE2E.Math58
*/59
template <Numeric T>60
T 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 tie66
* (neither `b < a`) it returns @p a. Because the result is a reference into the67
* 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.BuiltinLikeOps73
* @crtest MathCompileRun.Min74
* @systest StdlibE2E.Math75
*/76
template <Ordered T>77
const 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.BuiltinLikeOps86
* @crtest MathCompileRun.Min87
* @systest StdlibE2E.Math88
*/89
template <Ordered T, Ordered... Rest>90
const 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 tie96
* (neither `a < b`) it returns @p a. As with min, the returned reference97
* 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.BuiltinLikeOps103
* @crtest MathCompileRun.Max104
* @systest StdlibE2E.Math105
*/106
template <Ordered T>107
const 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.BuiltinLikeOps116
* @crtest MathCompileRun.Max117
* @systest StdlibE2E.Math118
*/119
template <Ordered T, Ordered... Rest>120
const 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 this126
* follows IEEE-754 semantics (e.g. `pow(0, 0)` is 1, and a negative base with a127
* non-integer exponent yields NaN); integer arguments lose exactness beyond128
* 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.BuiltinLikeOps135
* @crtest MathCompileRun.Pow136
* @systest StdlibE2E.Math137
*/138
template <Numeric Base, Numeric Exp>139
double pow(Base base, Exp exp) {140
return std::pow(static_cast<double>(base), static_cast<double>(exp));141
}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)` is150
* `-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.ScalarFunctions156
* @crtest MathCompileRun.Sqrt157
* @systest StdlibE2E.Math158
*/159
double sqrt(double x);160
/**161
* Cube root.162
*163
* Defined for the whole real line, including negatives (unlike sqrt): the164
* 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.TranscendentalAndRounding170
* @crtest MathCompileRun.Cbrt171
* @systest StdlibE2E.Math172
*/173
double 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.TranscendentalAndRounding181
* @crtest MathCompileRun.Fabs182
* @systest StdlibE2E.Math183
*/184
double 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 of190
* zero is preserved.191
* @param x any real.192
* @return ⌊@p x⌋.193
* @complexity O(1).194
* @alloc none.195
* @test CheatahMath.ScalarFunctions196
* @crtest MathCompileRun.Floor197
* @systest StdlibE2E.Math198
*/199
double 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 in205
* (−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.ScalarFunctions211
* @crtest MathCompileRun.Ceil212
* @systest StdlibE2E.Math213
*/214
double ceil(double x);215
/**216
* Round toward zero.217
*218
* Discards the fractional part, rounding toward zero rather than ±∞ (so it219
* differs from floor on negatives, e.g. `trunc(-2.7)` is `-2.0`); the sign of220
* @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.TranscendentalAndRounding226
* @crtest MathCompileRun.Trunc227
* @systest StdlibE2E.Math228
*/229
double 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.ScalarFunctions241
* @crtest MathCompileRun.Round242
* @systest StdlibE2E.Math243
*/244
double round(double x);245
/**246
* Exponential.247
*248
* Overflows to `+inf` for large @p x and underflows to `0` for large negative249
* @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.TranscendentalAndRounding255
* @crtest MathCompileRun.Exp256
* @systest StdlibE2E.Math257
*/258
double exp(double x);259
/**260
* Natural logarithm.261
*262
* Returns `-inf` for @p x == 0 and NaN (rather than throwing) for negative263
* @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.TranscendentalAndRounding269
* @crtest MathCompileRun.Log270
* @systest StdlibE2E.Math271
*/272
double log(double x);273
/**274
* Base-2 logarithm.275
*276
* Returns `-inf` for @p x == 0 and NaN for negative @p x, matching log's277
* out-of-domain behavior.278
* @param x > 0.279
* @return log₂(@p x).280
* @complexity O(1).281
* @alloc none.282
* @test CheatahMath.ScalarFunctions283
* @crtest MathCompileRun.Log2284
* @systest StdlibE2E.Math285
*/286
double log2(double x);287
/**288
* Base-10 logarithm.289
*290
* Returns `-inf` for @p x == 0 and NaN for negative @p x, matching log's291
* out-of-domain behavior.292
* @param x > 0.293
* @return log₁₀(@p x).294
* @complexity O(1).295
* @alloc none.296
* @test CheatahMath.TranscendentalAndRounding297
* @crtest MathCompileRun.Log10298
* @systest StdlibE2E.Math299
*/300
double log10(double x);301
/**302
* Sine.303
*304
* The argument is interpreted in radians; precision degrades for very large305
* 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.Trigonometry311
* @crtest MathCompileRun.Sin312
* @systest StdlibE2E.Math313
*/314
double sin(double x);315
/**316
* Cosine.317
*318
* The argument is interpreted in radians; precision degrades for very large319
* 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.Trigonometry325
* @crtest MathCompileRun.Cos326
* @systest StdlibE2E.Math327
*/328
double cos(double x);329
/**330
* Tangent.331
*332
* The argument is in radians; near the poles (odd multiples of π/2, which are333
* not exactly representable) the result is a large finite value rather than334
* ±∞, and `tan(±inf)` is NaN.335
* @param x radians.336
* @return tan(@p x).337
* @complexity O(1).338
* @alloc none.339
* @test CheatahMath.Trigonometry340
* @crtest MathCompileRun.Tan341
* @systest StdlibE2E.Math342
*/343
double tan(double x);344
/**345
* Arcsine.346
*347
* Returns a value in [−π/2, π/2]; arguments outside [−1, 1] yield NaN rather348
* 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.Trigonometry354
* @crtest MathCompileRun.Asin355
* @systest StdlibE2E.Math356
*/357
double asin(double x);358
/**359
* Arccosine.360
*361
* Returns a value in [0, π]; arguments outside [−1, 1] yield NaN rather than362
* throwing.363
* @param x in [−1, 1].364
* @return acos(@p x) in radians.365
* @complexity O(1).366
* @alloc none.367
* @test CheatahMath.Trigonometry368
* @crtest MathCompileRun.Acos369
* @systest StdlibE2E.Math370
*/371
double acos(double x);372
/**373
* Arctangent.374
*375
* Accepts the whole real line and returns a value in (−π/2, π/2), approaching376
* ±π/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.Trigonometry382
* @crtest MathCompileRun.Atan383
* @systest StdlibE2E.Math384
*/385
double atan(double x);386
/**387
* Two-argument arctangent.388
*389
* Uses the signs of both arguments to select the correct quadrant, returning a390
* value in (−π, π]; it is well-defined when @p x is zero (including the391
* `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.Trigonometry397
* @crtest MathCompileRun.Atan2398
* @systest StdlibE2E.Math399
*/400
double atan2(double y, double x);401
/**402
* Hypotenuse.403
*404
* Computes the 2-norm while avoiding intermediate overflow/underflow that a405
* naive `sqrt(x*x + y*y)` would suffer; returns `+inf` if either argument is406
* 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.ScalarFunctions412
* @crtest MathCompileRun.Hypot413
* @systest StdlibE2E.Math414
*/415
double 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 result420
* takes the sign of the dividend @p x (unlike a Python-style modulo); a zero421
* 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.TranscendentalAndRounding427
* @crtest MathCompileRun.Fmod428
* @systest StdlibE2E.Math429
*/430
double 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 copies435
* the IEEE sign bit, it distinguishes `+0.0` from `-0.0` and works even when436
* @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.TranscendentalAndRounding443
* @crtest MathCompileRun.Copysign444
* @systest StdlibE2E.Math445
*/446
double 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.ScalarFunctions454
* @crtest MathCompileRun.Degrees455
* @systest StdlibE2E.Math456
*/457
double 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.TranscendentalAndRounding465
* @crtest MathCompileRun.Radians466
* @systest StdlibE2E.Math467
*/468
double radians(double degrees);469
/**470
* Is NaN?471
*472
* The reliable NaN test, since NaN compares unequal to everything including473
* 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.IsFiniteIsNanIsInf479
* @crtest MathCompileRun.Isnan480
* @systest StdlibE2E.Math481
*/482
bool isnan(double x);483
/**484
* Is infinite?485
*486
* True for both `+inf` and `-inf`; false for NaN (use isnan for that) and for487
* 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.IsFiniteIsNanIsInf493
* @crtest MathCompileRun.Isinf494
* @systest StdlibE2E.Math495
*/496
bool 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.IsFiniteIsNanIsInf504
* @crtest MathCompileRun.Isfinite505
* @systest StdlibE2E.Math506
*/507
bool isfinite(double x);509
/**510
* Greatest common divisor.511
*512
* Operates on the absolute values via the Euclidean algorithm, so the result is513
* non-negative; `gcd(0, 0)` is 0 and `gcd(n, 0)` is `|n|`. Passing514
* `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.Integer520
* @crtest MathCompileRun.Gcd521
* @systest StdlibE2E.Math522
*/523
long 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 any528
* negative @p n also returns 1 since the loop never executes (no error is529
* 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.Integer535
* @crtest MathCompileRun.Factorial536
* @systest StdlibE2E.Math537
*/538
long long factorial(long long n);540
} // namespace cheatah::math