math

Numeric helpers: arithmetic, rounding, trigonometry, logarithms, and integer theory. Pure functions — nothing here touches the outside world.

Oxigen
introduce math
FunctionSignatureReturns
absabs(x)same type as x
minmin(a, b)one of the arguments
maxmax(a, b)one of the arguments
powpow(base, exp)int
powfpowf(base, exp)float
clampclamp(x, lo, hi)number
signsign(x)int (-1, 0, 1)
sqrtsqrt(x)float
floorfloor(x)int
ceilceil(x)int
roundround(x)int
trunctrunc(x)int
gcdgcd(a, b)int
lcmlcm(a, b)int
factorialfactorial(n)int or Error
is_evenis_even(n)bool
is_oddis_odd(n)bool
hypothypot(a, b)float
fmodfmod(a, b)number
degreesdegrees(rad)float
radiansradians(deg)float
sinsin(x)float
coscos(x)float
tantan(x)float
asinasin(x)float
acosacos(x)float
atanatan(x)float
atan2atan2(y, x)float
expexp(x)float
lnln(x)float
log2log2(x)float
log10log10(x)float

Constants: PI, E, TAU.


#Basic arithmetic

#abs(x)

Absolute value. Keeps the type it was given — an int in, an int out.

Oxigen
println(math.abs(-5))    // 5
println(math.abs(3.5))   // 3.5

#min(a, b)

The smaller of two values. Works on anything <= compares (ints, floats, and mixed int/float).

Oxigen
println(math.min(3, 7))      // 3
println(math.min(2.5, 2))    // 2

#max(a, b)

The larger of two values.

Oxigen
println(math.max(3, 7))      // 7

min/max here take two values. For the smallest element of an array, use array.min / array.max.

#pow(base, exp)

Integer exponentiation by repeated multiplication.

Oxigen
println(math.pow(2, 10))   // 1024
println(math.pow(5, 0))    // 1

A negative exponent returns 1 — the multiply loop simply never runs. Use powf for negative or fractional exponents.

Oxigen
println(math.pow(2, -1))    // 1     <- not 0.5
println(math.powf(2, -1))   // 0.5

#powf(base, exp)

Floating-point exponentiation. Handles fractional and negative exponents.

Oxigen
println(math.powf(2, 0.5))   // 1.4142135623730951
println(math.powf(2, -1))    // 0.5
println(math.powf(27, 1.0 / 3.0))  // 3

#clamp(x, lo, hi)

Constrain x to the range [lo, hi].

Oxigen
println(math.clamp(15, 0, 10))   // 10
println(math.clamp(-3, 0, 10))   // 0
println(math.clamp(5, 0, 10))    // 5

#sign(x)

1 for positive, -1 for negative, 0 for zero. Always an int.

Oxigen
println(math.sign(-9))    // -1
println(math.sign(0))     // 0
println(math.sign(2.5))   // 1

#Rounding

All four return an integer, whatever they were given.

#floor(x)

Round toward negative infinity.

Oxigen
println(math.floor(3.7))    // 3
println(math.floor(-3.2))   // -4

#ceil(x)

Round toward positive infinity.

Oxigen
println(math.ceil(3.2))     // 4
println(math.ceil(-3.7))    // -3

#round(x)

Round to nearest; halves go away from zero.

Oxigen
println(math.round(2.4))    // 2
println(math.round(2.5))    // 3
println(math.round(-2.5))   // -3

#trunc(x)

Round toward zero — drop the fractional part.

Oxigen
println(math.trunc(3.9))    // 3
println(math.trunc(-3.9))   // -3

#Integer helpers

#gcd(a, b)

Greatest common divisor, by Euclid's algorithm. Signs are ignored.

Oxigen
println(math.gcd(12, 18))    // 6
println(math.gcd(-12, 18))   // 6
println(math.gcd(7, 0))      // 7

#lcm(a, b)

Least common multiple. 0 if either argument is 0.

Oxigen
println(math.lcm(4, 6))    // 12
println(math.lcm(0, 5))    // 0

#factorial(n)

n!. Returns a catchable <Error<negative>> for negative n.

Oxigen
println(math.factorial(5))   // 120
println(math.factorial(0))   // 1

f := <type<Error> || <Value>>(math.factorial(-3))
println(f.msg)   // factorial of a negative number

#is_even(n) / is_odd(n)

Oxigen
println(math.is_even(4))   // True
println(math.is_odd(4))    // False

#Float helpers

#hypot(a, b)

sqrt(a*a + b*b) — the length of the hypotenuse.

Oxigen
println(math.hypot(3, 4))   // 5

#fmod(a, b)

Floating-point remainder, truncated toward zero (so the sign follows a).

Oxigen
println(math.fmod(7.5, 2))    // 1.5
println(math.fmod(-7.5, 2))   // -1.5

The % operator is integer-only; fmod is the float version.

#sqrt(x)

Square root, always a float. A negative input gives NaN, not an error.

Oxigen
println(math.sqrt(16))   // 4
println(math.sqrt(2))    // 1.4142135623730951
println(math.sqrt(-1))   // NaN

#Trigonometry

All angles are in radians.

#sin(x), cos(x), tan(x)

Oxigen
println(math.sin(0))              // 0
println(math.cos(0))              // 1
println(math.sin(math.PI / 2))    // 1

#asin(x), acos(x), atan(x)

Inverse trig, results in radians.

Oxigen
println(math.asin(1))   // 1.5707963267948966
println(math.acos(1))   // 0
println(math.atan(1))   // 0.7853981633974483

#atan2(y, x)

The angle of the point (x, y) from the positive x-axis — quadrant-correct, unlike atan(y / x).

Oxigen
println(math.atan2(1, 1))    // 0.7853981633974483   (45°)
println(math.atan2(1, -1))   // 2.356194490192345    (135°)

#degrees(rad) / radians(deg)

Convert between the two.

Oxigen
println(math.degrees(math.PI))   // 180
println(math.radians(180))       // 3.141592653589793

#Exponential and logarithmic

#exp(x)

e**x.

Oxigen
println(math.exp(1))   // 2.718281828459045

#ln(x)

Natural log. ln(0) is -inf, ln(negative) is NaN — neither is an error.

Oxigen
println(math.ln(math.E))   // 1
println(math.ln(0))        // -inf

#log2(x)

Oxigen
println(math.log2(8))   // 3

#log10(x)

Oxigen
println(math.log10(1000))   // 3

#Vectors

These take the vec2, vec3 and vec4 values described in Data Types.

#dot(a, b)

The dot product of two vectors of the same size, as a float.

Oxigen
println(math.dot(vec3(1, 2, 3), vec3(4, 5, 6)))   // 32

#cross(a, b)

The cross product of two vec3s: a vec3 at right angles to both.

Oxigen
println(math.cross(vec3(1, 0, 0), vec3(0, 1, 0)))   // vec3(0, 0, 1)

#length(v)

How long the vector is, as a float.

Oxigen
println(math.length(vec2(3, 4)))   // 5

#distance(a, b)

The length of a - b.

Oxigen
println(math.distance(vec3(1, 1, 1), vec3(1, 4, 5)))   // 5

#normalize(v)

The vector pointing the same way with length 1. A zero-length vector has no direction, so normalizing one is an error.

Oxigen
println(math.normalize(vec2(3, 4)))   // vec2(0.6, 0.8)

#lerp(a, b, t)

Blend from a (at t = 0) to b (at t = 1).

Oxigen
println(math.lerp(vec2(0, 0), vec2(10, 20), 0.5))   // vec2(5, 10)

#Constants

NameValue
math.PI3.141592653589793
math.E2.718281828459045
math.TAU6.283185307179586 (2 * PI)
Oxigen
introduce math

fun circle_area(r <float>) { math.PI * r * r }

println(circle_area(2.0))   // 12.566370614359172

#Worked example

Oxigen
introduce math

// Distance between two points, rounded to whole units.
fun distance(x1 <float>, y1 <float>, x2 <float>, y2 <float>) {
    math.hypot(x2 - x1, y2 - y1)
}

println(distance(0.0, 0.0, 3.0, 4.0))          // 5
println(math.round(distance(0.0, 0.0, 1.0, 1.0)))  // 1

// Reduce a fraction.
fun reduce(n <int>, d <int>) {
    g <int> := math.gcd(n, d)
    (n / g, d / g)
}

println(reduce(6, 8))   // (3, 4)

See also: array for folds over collections, random for random numbers.