Math
Precision-focused numeric utilities — C-like exactness with a Python-like API. Import from the root or from koyojs/Math.
// Root import
import { gcd, lcm, factorial, isPrime, isqrt, floorDiv, mod, comb, perm, isClose, kahanSum, roundTo } from "koyojs";
// Per-module import
import { factorial } from "koyojs/Math";Bigint vs. number
Every function in this module is either bigint-only-output (factorial, comb, perm — by necessity, since the exact result can exceed Number.MAX_SAFE_INTEGER) or number-only. No function silently mixes the two return types. Each bigint-returning function has a documented number-only Unsafe counterpart for callers who want raw numbers and accept the precision ceiling, instead of silently overflowing.
Greatest common divisor of two integers, computed via the Euclidean algorithm. Sign is ignored — the result is always non-negative. Throws if either argument is not an integer.
| Parameter | Type | Description |
|---|---|---|
a | number | First integer. |
b | number | Second integer. |
| returns | number | — |
import { gcd } from "koyojs";
gcd(48, 18);
// → 6
gcd(-12, 8);
// → 4
gcd(7, 0);
// → 7Least common multiple of two integers, derived from `gcd`. Returns 0 if either argument is 0. Throws if either argument is not an integer.
| Parameter | Type | Description |
|---|---|---|
a | number | First integer. |
b | number | Second integer. |
| returns | number | — |
import { lcm } from "koyojs";
lcm(4, 6);
// → 12
lcm(21, 6);
// → 42
lcm(0, 5);
// → 0Exact factorial of a non-negative integer, returned as a `bigint` so results beyond `Number.MAX_SAFE_INTEGER` stay precise. Throws a RangeError if `n` is negative or not an integer. For a number-only result that throws instead of silently losing precision, use `factorialUnsafe`.
| Parameter | Type | Description |
|---|---|---|
n | number | Non-negative integer. |
| returns | bigint | — |
import { factorial } from "koyojs";
factorial(5);
// → 120n
factorial(20);
// → 2432902008176640000n
// Stays exact well past Number.MAX_SAFE_INTEGER
factorial(25);
// → 15511210043330985984000000nNumber-only factorial. Behaves like `factorial` but returns a plain `number` and throws a RangeError the moment the result would exceed `Number.MAX_SAFE_INTEGER`, instead of silently returning an imprecise value. Use `factorial` when you need the exact result beyond that ceiling.
| Parameter | Type | Description |
|---|---|---|
n | number | Non-negative integer. |
| returns | number | — |
import { factorialUnsafe } from "koyojs";
factorialUnsafe(10);
// → 3628800
factorialUnsafe(21);
// → RangeError: factorialUnsafe(21) exceeds Number.MAX_SAFE_INTEGER;
// use factorial() for an exact BigInt resultPrimality test using 6k±1 trial division after eliminating multiples of 2 and 3. Returns `false` for numbers less than 2. Throws a TypeError if `n` is not an integer.
| Parameter | Type | Description |
|---|---|---|
n | number | Integer to test. |
| returns | boolean | — |
import { isPrime } from "koyojs";
isPrime(2);
// → true
isPrime(17);
// → true
isPrime(1);
// → false
isPrime(100);
// → falseExact integer square root. Uses BigInt Newton's method internally so large inputs stay precise, then floors the result for non-perfect squares. Throws a RangeError if `n` is negative or not an integer.
| Parameter | Type | Description |
|---|---|---|
n | number | Non-negative integer. |
| returns | number | — |
import { isqrt } from "koyojs";
isqrt(16);
// → 4
isqrt(17);
// → 4 (floored — 17 is not a perfect square)
isqrt(0);
// → 0Python-style floor division — always rounds toward negative infinity, unlike JavaScript's `Math.trunc(a / b)` which rounds toward zero. Throws a RangeError on division by zero.
| Parameter | Type | Description |
|---|---|---|
a | number | Dividend. |
b | number | Divisor. |
| returns | number | — |
import { floorDiv } from "koyojs";
floorDiv(7, 2);
// → 3
floorDiv(-7, 2);
// → -4 (JS `-7 / 2 | 0` gives -3 — truncation, not flooring)
floorDiv(7, -2);
// → -4Python-style modulo. The result always takes the sign of the divisor, unlike JavaScript's `%` operator which takes the sign of the dividend. Built on `floorDiv`. Throws a RangeError on division by zero.
| Parameter | Type | Description |
|---|---|---|
a | number | Dividend. |
b | number | Divisor. |
| returns | number | — |
import { mod } from "koyojs";
mod(-7, 3);
// → 2 (JS `-7 % 3` gives -1)
mod(7, -3);
// → -2 (JS `7 % -3` gives 1)
mod(7, 3);
// → 1n choose k — the number of ways to choose k items from n without regard to order. Exact, returned as `bigint`. Returns `0n` when `k > n`. Throws a RangeError for negative or non-integer arguments. For a number-only result, use `combUnsafe`.
| Parameter | Type | Description |
|---|---|---|
n | number | Total number of items. |
k | number | Number of items to choose. |
| returns | bigint | — |
import { comb } from "koyojs";
comb(5, 2);
// → 10n
comb(52, 5);
// → 2598960n (5-card poker hands)
comb(4, 10);
// → 0nNumber-only variant of `comb`. Throws a RangeError the moment the intermediate result would exceed `Number.MAX_SAFE_INTEGER`, instead of returning an imprecise value. Use `comb` when you need the exact result beyond that ceiling.
| Parameter | Type | Description |
|---|---|---|
n | number | Total number of items. |
k | number | Number of items to choose. |
| returns | number | — |
import { combUnsafe } from "koyojs";
combUnsafe(52, 5);
// → 2598960
combUnsafe(1000, 500);
// → RangeError: combUnsafe(1000, 500) exceeds Number.MAX_SAFE_INTEGER;
// use comb() for an exact BigInt resultn permute k — the number of ways to arrange k items out of n, order mattering. Exact, returned as `bigint`. `k` defaults to `n` (full permutation). Returns `0n` when `k > n`. Throws a RangeError for negative or non-integer arguments. For a number-only result, use `permUnsafe`.
| Parameter | Type | Description |
|---|---|---|
n | number | Total number of items. |
k | number | Number of items to arrange. Defaults to `n`. |
| returns | bigint | — |
import { perm } from "koyojs";
perm(5, 2);
// → 20n
perm(5);
// → 120n (same as factorial(5))
perm(4, 10);
// → 0nNumber-only variant of `perm`. Throws a RangeError the moment the intermediate result would exceed `Number.MAX_SAFE_INTEGER`, instead of returning an imprecise value. Use `perm` when you need the exact result beyond that ceiling.
| Parameter | Type | Description |
|---|---|---|
n | number | Total number of items. |
k | number | Number of items to arrange. Defaults to `n`. |
| returns | number | — |
import { permUnsafe } from "koyojs";
permUnsafe(10, 3);
// → 720
permUnsafe(20, 15);
// → RangeError: permUnsafe(20, 15) exceeds Number.MAX_SAFE_INTEGER;
// use perm() for an exact BigInt resultPython's `math.isclose` — combined relative and absolute tolerance in one call, more robust than a single fixed epsilon across widely different magnitudes. `relTol` defaults to `1e-9`, `absTol` defaults to `0`. Non-finite values (NaN, Infinity) are only equal to themselves via strict equality, never "close".
| Parameter | Type | Description |
|---|---|---|
a | number | First value. |
b | number | Second value. |
options.relTol | number | Relative tolerance. Default `1e-9`. |
options.absTol | number | Absolute tolerance. Default `0`. |
| returns | boolean | — |
import { isClose } from "koyojs";
isClose(1.0000000001, 1.0000000002);
// → true
isClose(1000, 1000.5, { relTol: 0.001 });
// → true
isClose(0, 1e-10, { absTol: 1e-9 });
// → true (relative tolerance breaks down near zero — use absTol)Thin wrapper over `isClose` with only absolute tolerance active (`relTol` forced to 0). No options object, no ambiguity about which tolerance takes priority — for callers who want a single, unambiguous absolute bound.
| Parameter | Type | Description |
|---|---|---|
a | number | First value. |
b | number | Second value. |
absTol | number | Absolute tolerance. |
| returns | boolean | — |
import { isCloseAbs } from "koyojs";
isCloseAbs(10.001, 10.002, 0.01);
// → true
isCloseAbs(10.001, 10.05, 0.01);
// → falseThin wrapper over `isClose` with only relative tolerance active (`absTol` forced to 0). For callers who want a single, unambiguous percentage-style bound without reaching for an options object.
| Parameter | Type | Description |
|---|---|---|
a | number | First value. |
b | number | Second value. |
relTol | number | Relative tolerance. |
| returns | boolean | — |
import { isCloseRel } from "koyojs";
isCloseRel(1000, 1005, 0.01);
// → true (0.5% difference, within 1% tolerance)
isCloseRel(1000, 1200, 0.01);
// → falseCompensated (Kahan) summation. Tracks the lost low-order bits from each addition and feeds them back in, yielding a much lower accumulated error than a naive `reduce` over large arrays of floating-point numbers.
| Parameter | Type | Description |
|---|---|---|
values | number[] | Numbers to sum. |
| returns | number | — |
import { kahanSum } from "koyojs";
kahanSum([0.1, 0.2, 0.3]);
// → 0.6
// Naive reduce accumulates error over many small values;
// kahanSum stays accurate at scale
const values = Array(1_000_000).fill(0.1);
kahanSum(values);
// → 100000 (naive reduce drifts noticeably by this point)Epsilon-corrected rounding to a given number of decimal places. Adds `Number.EPSILON` before scaling and rounding, which fixes cases like `1.005` rounding down to `1` under naive `Math.round(value * 100) / 100`. `decimals` defaults to `0`. Throws a TypeError if `decimals` is not an integer.
| Parameter | Type | Description |
|---|---|---|
value | number | Value to round. |
decimals | number | Decimal places to round to. Default `0`. |
| returns | number | — |
import { roundTo } from "koyojs";
roundTo(1.005, 2);
// → 1.01 (naive Math.round(1.005 * 100) / 100 gives 1)
roundTo(3.14159, 2);
// → 3.14
roundTo(42.7);
// → 43Returns the magnitude of `x` with the sign of `y`. Correctly treats `-0` as negative, matching IEEE 754 semantics.
| Parameter | Type | Description |
|---|---|---|
x | number | Value supplying the magnitude. |
y | number | Value supplying the sign. |
| returns | number | — |
import { copySign } from "koyojs";
copySign(3, -5);
// → -3
copySign(-3, 5);
// → 3
copySign(3, -0);
// → -3Converts an angle from radians to degrees.
| Parameter | Type | Description |
|---|---|---|
radians | number | Angle in radians. |
| returns | number | — |
import { degrees } from "koyojs";
degrees(Math.PI);
// → 180
degrees(Math.PI / 2);
// → 90Converts an angle from degrees to radians. The inverse of `degrees`.
| Parameter | Type | Description |
|---|---|---|
degrees | number | Angle in degrees. |
| returns | number | — |
import { radians } from "koyojs";
radians(180);
// → 3.141592653589793
radians(90);
// → 1.5707963267948966Linear interpolation between `a` and `b` at parameter `t`. `t = 0` returns `a`, `t = 1` returns `b`; values outside `[0, 1]` extrapolate.
| Parameter | Type | Description |
|---|---|---|
a | number | Start value. |
b | number | End value. |
t | number | Interpolation factor. |
| returns | number | — |
import { lerp } from "koyojs";
lerp(0, 10, 0.5);
// → 5
lerp(0, 10, 0);
// → 0
lerp(10, 20, 1.5);
// → 25 (extrapolates past b)N-dimensional hypotenuse — a thin, explicitly variadic wrapper over the platform's `Math.hypot`, kept in the API for a consistent import surface alongside `dist` and the rest of the Math module.
| Parameter | Type | Description |
|---|---|---|
values | number[] | Any number of components. |
| returns | number | — |
import { hypot } from "koyojs";
hypot(3, 4);
// → 5
hypot(2, 3, 6);
// → 7Euclidean distance between two points of equal dimension. Throws a RangeError if `p` and `q` have different lengths.
| Parameter | Type | Description |
|---|---|---|
p | number[] | First point. |
q | number[] | Second point, same dimension as `p`. |
| returns | number | — |
import { dist } from "koyojs";
dist([0, 0], [3, 4]);
// → 5
dist([0, 0, 0], [1, 2, 2]);
// → 3
dist([1, 2], [1, 2, 3]);
// → RangeError: dist requires points of equal dimension