std.math

Reed's standard library. Imported with use std.math; not on disk.

Methods

i32.absAbsolute value of a signed 32-bit integer.
i64.absAbsolute value of a signed 64-bit integer.
i32.minThe smaller of two signed integers.
i32.maxThe larger of two signed integers.
i32.min_uThe smaller of two integers, compared as unsigned.
i32.max_uThe larger of two integers, compared as unsigned.
i64.minThe smaller of two signed 64-bit integers.
i64.maxThe larger of two signed 64-bit integers.
i32.clampConstrains a value to the inclusive range lo..=hi.
i32.signumThe sign of a value: -1, 0, or 1.
i64.signumThe sign of a 64-bit value, as an i32.
i32.isqrtInteger square root: the largest r with r * r <= self.
i32.powRaises self to a non-negative power, by binary exponentiation.
i64.powRaises a 64-bit self to a non-negative power, by binary exponentiation.
i32.gcdGreatest common divisor, by the binary (Stein's) algorithm.
i32.lcmLeast common multiple.
i32.log2Floor of the base-2 logarithm: the index of the highest set bit.
i32.log10Floor of the base-10 logarithm: one less than the number of decimal digits.
i32.is_pow2Whether self is a power of two.
i32.next_pow2The smallest power of two greater than or equal to self.
i32.div_floorEuclidean division: the quotient rounded toward negative infinity.
i32.mod_floorEuclidean remainder: the remainder of div_floor, carrying the divisor's sign.
i32.sat_addAddition that saturates at the i32 bounds instead of wrapping.
i32.sat_subSubtraction that saturates at the i32 bounds instead of wrapping.
i32.lerpLinear interpolation between self and to, in fixed point.
i32.is_primeWhether self is prime, by trial division against 2, 3, and then 6k±1.

i32.abs #

line 32
func i32.abs(self: i32) -> i32

Absolute value of a signed 32-bit integer.

Branchless: negating via the sign mask costs three instructions and no jump.

Overflow. (-2147483648).abs() returns -2147483648, because +2147483648 has no i32 representation. This mirrors what every two's-complement machine does and what wasm.i32.sub(0, x) would give; it is deliberately not a trap.

Returns
the magnitude of self, except at i32 minimum
See also
i64.abs
Source
func i32.abs(self: i32) -> i32 {
  let mask: i32 = self >> 31;
  return (self ^ mask) - mask;
}

i64.abs #

line 42
func i64.abs(self: i64) -> i64

Absolute value of a signed 64-bit integer.

(-9223372036854775808).abs() returns itself, for the reason i32.abs documents.

Returns
the magnitude of self, except at i64 minimum
Source
func i64.abs(self: i64) -> i64 {
  let mask: i64 = self >> 63;
  return (self ^ mask) - mask;
}

i32.min #

line 54
func i32.min(self: i32, rhs: i32) -> i32

The smaller of two signed integers.

WebAssembly's min instruction exists only for floats, so this is a real comparison and a select, not a single instruction.

Parameters
rhs — the value to compare against
See also
i32.max, i32.min_u
Source
func i32.min(self: i32, rhs: i32) -> i32 {
  return self < rhs ? self : rhs;
}

i32.max #

line 62
func i32.max(self: i32, rhs: i32) -> i32

The larger of two signed integers.

Parameters
rhs — the value to compare against
See also
i32.min
Source
func i32.max(self: i32, rhs: i32) -> i32 {
  return self > rhs ? self : rhs;
}

i32.min_u #

line 73
func i32.min_u(self: i32, rhs: i32) -> i32

The smaller of two integers, compared as unsigned.

Distinct from min, not a stylistic variant: (-1).min(1) is -1, while (-1).min_u(1) is 1, since -1 read as unsigned is 4294967295.

Parameters
rhs — the value to compare against
See also
i32.min
Source
func i32.min_u(self: i32, rhs: i32) -> i32 {
  return self.lt_u(rhs) ? self : rhs;
}

i32.max_u #

line 81
func i32.max_u(self: i32, rhs: i32) -> i32

The larger of two integers, compared as unsigned.

Parameters
rhs — the value to compare against
See also
i32.max, i32.min_u
Source
func i32.max_u(self: i32, rhs: i32) -> i32 {
  return self.gt_u(rhs) ? self : rhs;
}

i64.min #

line 88
func i64.min(self: i64, rhs: i64) -> i64

The smaller of two signed 64-bit integers.

Parameters
rhs — the value to compare against
Source
func i64.min(self: i64, rhs: i64) -> i64 {
  return self < rhs ? self : rhs;
}

i64.max #

line 95
func i64.max(self: i64, rhs: i64) -> i64

The larger of two signed 64-bit integers.

Parameters
rhs — the value to compare against
Source
func i64.max(self: i64, rhs: i64) -> i64 {
  return self > rhs ? self : rhs;
}

i32.clamp #

line 107
func i32.clamp(self: i32, lo: i32, hi: i32) -> i32

Constrains a value to the inclusive range lo..=hi.

When lo > hi the range is empty and there is no correct answer; this returns lo, matching the order the two comparisons are applied in, rather than trapping.

Parameters
lo — the lower bound, inclusive
hi — the upper bound, inclusive
See also
i32.min, i32.max
Source
func i32.clamp(self: i32, lo: i32, hi: i32) -> i32 {
  if (self < lo) { return lo; }
  if (self > hi) { return hi; }
  return self;
}

i32.signum #

line 118
func i32.signum(self: i32) -> i32

The sign of a value: -1, 0, or 1.

Branchless. Correct at i32 minimum, unlike a formulation built on abs.

Returns
-1 when negative, 0 when zero, 1 when positive
Source
func i32.signum(self: i32) -> i32 {
  return (self > 0 ? 1 : 0) - (self < 0 ? 1 : 0);
}

i64.signum #

line 128
func i64.signum(self: i64) -> i32

The sign of a 64-bit value, as an i32.

The result is i32 rather than i64 because a sign is not a wide quantity and the caller almost always wants to compare or branch on it.

Returns
-1 when negative, 0 when zero, 1 when positive
Source
func i64.signum(self: i64) -> i32 {
  return (self > 0 ? 1 : 0) - (self < 0 ? 1 : 0);
}

i32.isqrt #

line 142
func i32.isqrt(self: i32) -> i32

Integer square root: the largest r with r * r <= self.

Exact at every input, including every perfect square -- this is a digit-by-digit binary method over the integers, not a rounded f64.sqrt, which starts disagreeing with the exact answer for inputs above 2^52.

A negative input has no real square root; this returns 0 rather than trapping.

Returns
the floor of the square root, or 0 for a negative input
See also
i32.pow
Source
func i32.isqrt(self: i32) -> i32 {
  if (self <= 0) { return 0; }
  var rem: i32 = self;
  var root: i32 = 0;
  // The highest even power of two not exceeding `self`, which is where the digit scan
  // must start. `clz` finds it directly rather than by looping down from 2^30.
  var bit: i32 = 1 << ((31 - self.clz()) & ~1);
  loop scan() {
    if (bit == 0) { break scan(); }
    if (rem >= root + bit) {
      rem = rem - (root + bit);
      root = (root >> 1) + bit;
    } else {
      root = root >> 1;
    }
    bit = bit >> 2;
    continue scan();
  }
  return root;
}

i32.pow #

line 174
func i32.pow(self: i32, exp: i32) -> i32

Raises self to a non-negative power, by binary exponentiation.

Wrapping. The result wraps modulo 2^32 exactly as * does; (2).pow(31) is -2147483648. There is no overflow check, so this stays a handful of instructions.

A negative exponent would give a fraction, which is not an i32; this returns 0 for one (matching the mathematical truncation of 1/x^n for |x| > 1), except that (1).pow(-n) and (-1).pow(-n) are not special-cased and also return 0.

Parameters
exp — the exponent; negative exponents yield 0
Returns
self raised to exp, wrapping on overflow
Source
func i32.pow(self: i32, exp: i32) -> i32 {
  if (exp < 0) { return 0; }
  var result: i32 = 1;
  var base: i32 = self;
  var n: i32 = exp;
  loop square() {
    if (n == 0) { break square(); }
    if ((n & 1) == 1) { result = result * base; }
    base = base * base;
    n = n >> 1;
    continue square();
  }
  return result;
}

i64.pow #

line 194
func i64.pow(self: i64, exp: i32) -> i64

Raises a 64-bit self to a non-negative power, by binary exponentiation.

Wraps modulo 2^64, and returns 0 for a negative exponent, exactly as i32.pow does.

Parameters
exp — the exponent; negative exponents yield 0
Source
func i64.pow(self: i64, exp: i32) -> i64 {
  if (exp < 0) { return 0; }
  var result: i64 = 1;
  var base: i64 = self;
  var n: i32 = exp;
  loop square() {
    if (n == 0) { break square(); }
    if ((n & 1) == 1) { result = result * base; }
    base = base * base;
    n = n >> 1;
    continue square();
  }
  return result;
}

i32.gcd #

line 226
func i32.gcd(self: i32, rhs: i32) -> i32

Greatest common divisor, by the binary (Stein's) algorithm.

Both operands are taken as magnitudes, so the result is always non-negative: (-12).gcd(18) is 6. gcd(0, n) is |n|, and gcd(0, 0) is 0.

Stein's algorithm rather than Euclid's because it needs no division: ctz and shifts only, which matters since i32.div_s is among the slowest instructions available.

At i32 minimum, whose magnitude 2^31 is not representable, the operand is treated as the unsigned value 2147483648 -- the honest reading of its bits, and the one that makes the result mathematically correct: (-2147483648).gcd(2) is 2. The result is still returned as an i32, so a gcd of exactly 2^31 (only possible when both operands are i32 minimum) comes back as -2147483648.

Parameters
rhs — the other operand
Returns
the largest integer dividing both, read as unsigned where noted above
See also
i32.lcm
Source
func i32.gcd(self: i32, rhs: i32) -> i32 {
  var a: i32 = self.abs();
  var b: i32 = rhs.abs();
  if (a == 0) { return b; }
  if (b == 0) { return a; }
  // Factor out the common powers of two; the loop below then only ever sees odd values.
  //
  // Both shifts are `>>>` (unsigned), not `>>`. A signed shift of `i32` minimum by its own
  // `ctz` of 31 sign-extends to `-1` instead of yielding `1`, so the values would never be
  // odd and the reduction below would never terminate.
  let shift: i32 = (a | b).ctz();
  a = a >>> a.ctz();
  loop reduce() {
    b = b >>> b.ctz();
    // **Unsigned** comparison, and this is load-bearing rather than stylistic. `abs` of
    // `i32` minimum is itself, still negative, so a signed `>` would never order it first;
    // the subtraction below would then run away from zero and the loop would never
    // terminate. Both values are magnitudes here, so unsigned is also the correct reading.
    if (a.gt_u(b)) {
      let t: i32 = a;
      a = b;
      b = t;
    }
    b = b - a;
    if (b == 0) { break reduce(); }
    continue reduce();
  }
  return a << shift;
}

i32.lcm #

line 266
func i32.lcm(self: i32, rhs: i32) -> i32

Least common multiple.

Divides before multiplying, so lcm is exact whenever the true result fits in i32; the naive a * b / gcd overflows for operands whose product does not fit even when the LCM itself would.

lcm(0, n) is 0. Overflow wraps, like *.

Parameters
rhs — the other operand
See also
i32.gcd
Source
func i32.lcm(self: i32, rhs: i32) -> i32 {
  if (self == 0 || rhs == 0) { return 0; }
  let g: i32 = self.gcd(rhs);
  return (self.abs() / g) * rhs.abs();
}

i32.log2 #

line 281
func i32.log2(self: i32) -> i32

Floor of the base-2 logarithm: the index of the highest set bit.

log2(0) is undefined mathematically; this returns -1, which is the value that makes 1 << (n.log2() + 1) a correct "round up to a power of two" for n == 0 as well.

Treats self as unsigned, so a negative input answers 31 rather than -1.

Returns
the floor of log2, or -1 for zero
See also
i32.is_pow2, i32.next_pow2
Source
func i32.log2(self: i32) -> i32 {
  return 31 - self.clz();
}

i32.log10 #

line 291
func i32.log10(self: i32) -> i32

Floor of the base-10 logarithm: one less than the number of decimal digits.

Returns -1 for zero and for every negative input, mirroring log2's convention for an input with no logarithm.

Returns
the floor of log10, or -1 when self <= 0
Source
func i32.log10(self: i32) -> i32 {
  if (self <= 0) { return -1; }
  var n: i32 = self;
  var digits: i32 = 0;
  loop count() {
    n = n / 10;
    if (n == 0) { break count(); }
    digits = digits + 1;
    continue count();
  }
  return digits;
}

i32.is_pow2 #

line 310
func i32.is_pow2(self: i32) -> i32

Whether self is a power of two.

Zero is not a power of two, and neither is any negative value, both of which the naive (n & (n - 1)) == 0 gets wrong (it accepts zero and accepts i32 minimum).

See also
i32.next_pow2
Source
func i32.is_pow2(self: i32) -> i32 {
  return self > 0 && (self & (self - 1)) == 0;
}

i32.next_pow2 #

line 322
func i32.next_pow2(self: i32) -> i32

The smallest power of two greater than or equal to self.

Returns 1 for every input at or below 1, since 1 = 2^0 is the smallest power of two available. Inputs above 2^30 have no representable answer and return 0, the wrapped value of 2^32 -- the same answer 1 << 32 would give.

Returns
the next power of two at or above self
See also
i32.is_pow2
Source
func i32.next_pow2(self: i32) -> i32 {
  if (self <= 1) { return 1; }
  return 1 << (32 - (self - 1).clz());
}

i32.div_floor #

line 337
func i32.div_floor(self: i32, rhs: i32) -> i32

Euclidean division: the quotient rounded toward negative infinity.

Differs from /, which truncates toward zero: (-7).div_floor(2) is -4, while -7 / 2 is -3. Floor division is what indexing into a grid or bucketing a signed coordinate almost always wants.

Traps on a zero divisor, like /.

Parameters
rhs — the divisor
See also
i32.mod_floor
Source
func i32.div_floor(self: i32, rhs: i32) -> i32 {
  let q: i32 = self / rhs;
  // Truncation and flooring differ exactly when the operands have opposite signs and the
  // division was not exact.
  if ((self % rhs != 0) && ((self < 0) != (rhs < 0))) { return q - 1; }
  return q;
}

i32.mod_floor #

line 355
func i32.mod_floor(self: i32, rhs: i32) -> i32

Euclidean remainder: the remainder of div_floor, carrying the divisor's sign.

Differs from %, which carries the dividend's sign: (-7).mod_floor(2) is 1, while -7 % 2 is -1. The result is always in [0, rhs) for a positive divisor, which is what makes it usable directly as an array index.

Traps on a zero divisor, like %.

Parameters
rhs — the divisor
See also
i32.div_floor
Source
func i32.mod_floor(self: i32, rhs: i32) -> i32 {
  let r: i32 = self % rhs;
  if (r != 0 && ((r < 0) != (rhs < 0))) { return r + rhs; }
  return r;
}

i32.sat_add #

line 367
func i32.sat_add(self: i32, rhs: i32) -> i32

Addition that saturates at the i32 bounds instead of wrapping.

2147483647.sat_add(1) is 2147483647, where + would give -2147483648.

Parameters
rhs — the value to add
See also
i32.sat_sub
Source
func i32.sat_add(self: i32, rhs: i32) -> i32 {
  let sum: i32 = self + rhs;
  // Overflow happened exactly when both operands share a sign that the result does not.
  if (((self ^ sum) & (rhs ^ sum)) < 0) {
    return self < 0 ? -2147483648 : 2147483647;
  }
  return sum;
}

i32.sat_sub #

line 380
func i32.sat_sub(self: i32, rhs: i32) -> i32

Subtraction that saturates at the i32 bounds instead of wrapping.

Parameters
rhs — the value to subtract
See also
i32.sat_add
Source
func i32.sat_sub(self: i32, rhs: i32) -> i32 {
  let diff: i32 = self - rhs;
  if (((self ^ rhs) & (self ^ diff)) < 0) {
    return self < 0 ? -2147483648 : 2147483647;
  }
  return diff;
}

i32.lerp #

line 399
func i32.lerp(self: i32, to: i32, t: i32, scale: i32) -> i32

Linear interpolation between self and to, in fixed point.

t is a fraction over scale (pass scale = 1000 for per-mille, 256 for a binary fraction). Computes self + (to - self) * t / scale with the multiply before the divide, so precision is not lost to an early truncation.

Traps when scale is zero. Values outside 0..=scale extrapolate rather than clamp.

Parameters
to — the value at t == scale
t — the interpolation position
scale — what t is a fraction of
Source
func i32.lerp(self: i32, to: i32, t: i32, scale: i32) -> i32 {
  return self + ((to - self) * t) / scale;
}

i32.is_prime #

line 411
func i32.is_prime(self: i32) -> i32

Whether self is prime, by trial division against 2, 3, and then 6k±1.

Every value below 2 is composite by definition, including all negatives.

Trial division rather than a Miller-Rabin round: for the 32-bit range the loop runs at most ~7600 iterations, and it is exact with no witness set to get subtly wrong.

Returns
1 when prime, 0 otherwise
Source
func i32.is_prime(self: i32) -> i32 {
  if (self < 2) { return 0; }
  if (self < 4) { return 1; }
  if (self % 2 == 0) { return 0; }
  if (self % 3 == 0) { return 0; }
  // The loop bound is compared against `isqrt(self)` rather than testing `f * f > self`.
  // That square overflows for any `f` above 46340, wrapping negative, so the guard silently
  // stops being true and the scan runs on until it hits a spurious "divisor" -- which made
  // `(2147483647).is_prime()` answer 0. Computing the limit once avoids the multiply
  // entirely and is faster besides.
  let limit: i32 = self.isqrt();
  // Every prime above 3 is 6k±1, so the scan can step by 6 and test two candidates.
  var f: i32 = 5;
  loop trial() {
    if (f > limit) { break trial(); }
    if (self % f == 0) { return 0; }
    if (self % (f + 2) == 0) { return 0; }
    f = f + 6;
    continue trial();
  }
  return 1;
}