std.math

Math functions and constants live at std.math.

Functions

abs(x)  max(a, b, ...) | max(array)  min(a, b, ...) | min(array)
sign(x)  clamp(value, min, max)  lerp(a, b, t)
pow(base, exp)  sqrt(x)  cbrt(x)  hypot(x, y)
exp(x)  exp2(x)  log(x)  log2(x)  log10(x)
floor(x)  ceil(x)  round(x)  trunc(x)
sin(x)  cos(x)  tan(x)  asin(x)  acos(x)  atan(x)  atan2(y, x)
sinh(x)  cosh(x)  tanh(x)
deg(x)  rad(x)  fmod(x, y)  remainder(x, y)
is_nan(x)  is_inf(x)  is_finite(x)
sum(arr)  mean(arr)  median(arr)  variance(arr)  stddev(arr)
random()          # double in [0, 1)
rand_int(min, max)  # integer in [min, max]
random_seed(seed)   # deterministic sequences after seeding

Constants

PI, E, TAU, PHI, SQRT2, SQRT1_2, SQRT3, LN2, LN10,
LOG2E, LOG10E, INV_PI, INV_SQRTPI, DEG, RAD, nan, inf.

DEG / RAD are conversion factors; deg(x) converts degrees to radians
and rad(x) converts radians to degrees. Many results come back as
floats/doubles, so they often print with a decimal point.

Basic arithmetic

max / min take either a spread of arguments or a single array
(passing an object is a TYPE_ERROR — extract values first):

print(std.math.max(4, 9, 2))    # 9
print(std.math.max([4, 9, 2]))  # 9
print(std.math.min(4, 9, 2))    # 2

print(std.math.abs(-42))        # 42
print(std.math.sign(-3))        # -1
print(std.math.pow(2, 10))      # 1024

# fmod vs remainder differ on sign of the result
print(std.math.fmod(7, 3))      # 1

Clamping and interpolation

clamp(value, min, max) pins a value into a range; lerp(a, b, t) blends
between a and b by the fraction t (0 → a, 1 → b). Together they are
the workhorses of simulation and animation code:

print(std.math.clamp(15, 0, 10))   # 10   (too high)
print(std.math.clamp(-5, 0, 10))   # 0    (too low)
print(std.math.clamp(7, 0, 10))    # 7    (already in range)

var hp = 40
hp = std.math.clamp(hp + 500, 0, 100)   # heals but never exceeds 100
print(hp)                              # 100

print(std.math.lerp(0, 10, 0.5))       # 5   (midpoint)
print(std.math.lerp(0, 10, 0.25))      # 2.5

Rounding

round to nearest whole, floor down, ceil up, trunc toward zero.
round breaks ties away from zero (2.5 → 3, -2.5 → -3) and always
returns a double:

print(std.math.round(2.5))    # 3.0
print(std.math.floor(2.7))    # 2
print(std.math.ceil(2.2))     # 3
print(std.math.trunc(-2.7))   # -2   (toward zero, not down)

Angles

Trig functions take radians. Convert with deg / rad:

print(std.math.deg(180))          # ~3.1416  (radians for 180°)
print(std.math.rad(std.math.PI))  # ~180     (degrees for π rad)

A typical pattern is to compute in degrees and convert before calling trig —
as in placing 5 points on a unit circle:

var points = []
for (i : std.seq.range(5)) {
  var a = std.math.deg(std.math.lerp(0, 360, i / 5.0))  # degrees 0,72,144,...
  points:insert({x: std.math.cos(a), y: std.math.sin(a)})
}
print(points[0])   # {"x": 1, "y": 0}

Statistics

Operate on arrays:

print(std.math.sum([1, 2, 3, 4]))      # 10
print(std.math.mean([1, 2, 3, 4]))     # 2.5

var scores = [88, 92, 79, 95, 61]
print(std.math.median(scores))         # 88
print(std.math.max(scores))            # 95
print(std.math.min(scores))            # 61
print(std.math.variance([1, 2, 3]))    # 0.666... (population: divides by N)

variance / stddev are population statistics (divide by N, not N−1).
Empty arrays throw INVALID_ARGUMENT. log(x) is the natural logarithm
(log2 / log10 for the others); non-positive input throws.

Randomness

random() returns a double in [0, 1). rand_int(min, max) returns an
integer in [min, max] (inclusive) and is the easy way to roll dice:

print(std.math.random())               # e.g. 0.531912...
var die = std.math.rand_int(1, 6)      # a d6
print("rolled", die)

# seed for reproducible (test) sequences
std.math.random_seed(42)
print(std.math.rand_int(0, 100000))    # deterministic
std.math.random_seed(42)
print(std.math.rand_int(0, 100000))    # same as above

Classification

print(std.math.is_nan(std.math.nan))   # true (note: 0.0 / 0.0 throws
                                       # DIVISION_BY_ZERO instead — use std.math.nan)
print(std.math.is_inf(1e308D * 10))    # true (D suffix: double holds 1e308; float maxes out near 3.4e38)
print(std.math.is_finite(42))          # true