int_max fn
fn int_max() -> IntThe largest Int, 2^63 - 1.
Package
Number theory, exact rounding modes, primes, combinatorics and statistics for Int, F64 and BigInt, pure and tested in both tiers
0.1.1 MIT
sspur add numeric@^0.1.1or in sspur.toml:
numeric = { version = "^0.1.1" }Number theory, rounding with explicit modes, interpolation, primes, combinatorics and descriptive statistics for SSPUR. It builds on the builtins (Int.gcd, checked_*, clamp, the F64 cmath methods, BigInt) and adds what they leave out, in the spirit of Python's math and statistics modules, Apache Commons Math's ArithmeticUtils/CombinatoricsUtils/DescriptiveStatistics and Guava's IntMath/LongMath. Every function is pure, works for every Int including int_min() and int_max() without overflow traps unless documented, and is tested natively and in the interpreter.
Conventions: a function that can have no answer returns Opt (an empty list, a constant series, an Int overflow); an argument that can only be a caller's bug is a where refinement or pre clause. Statistics take List[F64] (floats(xs) converts Ints). A NaN anywhere in the input makes a numeric result NaN. SSPUR compares F64 with a total order (NaN equals itself and sorts last, -0.0 < 0.0), so the functions test for NaN and negative zero explicitly rather than relying on == and <.
use numeric.{HalfEven, HalfAwayFromZero, Exclusive, round_to, div_round, prime_factors, comb, mean, stdev, quantiles, linear_regression}
fn main() -> Unit ! log
= do
log(round_to(2.675, 2, HalfAwayFromZero).str)
log(div_round(7, 2, HalfEven).str)
log(prime_factors(9223372036854775807).str)
log(comb(100, 50).str)
xs = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
log("{mean(xs)} {stdev(xs)}")
log(quantiles([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0], 4, Exclusive).str)
log(linear_regression([1.0, 2.0, 3.0], [2.0, 4.0, 6.0]).str)
prints, natively and with --interp:
2.68
4
[7, 7, 73, 127, 337, 92737, 649657]
100891344545564193334812497256
some(1.6071428571428572) some(1.1713342006121947)
some([2.75, 5.5, 8.25])
some(Fit{slope: 2.0, intercept: 0.0, r_squared: 1.0})
| Definition | What it does |
|---|---|
int_max(), int_min() | 2^63 - 1 and -2^63 |
checked_abs(x) | the absolute value, or none for int_min() |
sign_int(x), sign_f64(x) | -1, 0 or 1; the F64 one keeps -0.0 and NaN |
gcd_all(xs) | gcd of a list, never negative, 0 for [] |
checked_lcm(a, b), lcm_all(xs) | lcm, none on overflow; lcm_all([]) == some(1) |
egcd(a, b) | (g, x, y) with a * x + b * y == g |
is_coprime(a, b) | gcd is 1 |
floor_div, floor_mod | Python's // and % (remainder takes the divisor's sign) |
ceil_div | division rounded toward +infinity |
euclid_div, euclid_mod | Euclidean division: the remainder is never negative |
addmod, mulmod | (a + b) mod m, (a * b) mod m without overflow for any Ints |
mod_pow(b, e, m), mod_inv(a, m) | modular power and inverse (none when not coprime) |
isqrt, icbrt, iroot(n, k) | exact floor of square, cube and k-th roots |
checked_pow(b, e) | b ^ e or none on overflow (big(b).pow(e) for exact big powers) |
is_perfect_square, perfect_power, is_perfect_power | square test; largest (base, k) with base ^ k == n |
is_power_of_two, next_power_of_two | powers of two, none above 2^62 |
| Definition | What it does |
|---|---|
digits(n, base), from_digits(ds, base) | digits of the absolute value, most significant first, and back (none on a bad digit or overflow) |
digit_sum(n), num_digits(n, base), digital_root(n) | decimal digit sum, digit count, repeated digit sum |
to_radix(n, base), from_radix(s, base) | bases 2 to 36, lowercase out, either case in, optional sign, none on anything else |
Rounding is HalfUp (ties toward +infinity), HalfDown (ties toward -infinity), HalfEven (banker's), HalfAwayFromZero (school rounding, Java's HALF_UP), HalfTowardZero (Java's HALF_DOWN), Up (away from zero), Down (toward zero), Ceiling and Floor.
| Definition | What it does |
|---|---|
div_round(a, b, mode) | a / b rounded with mode, exact |
round_int_to_multiple(n, step, mode) | nearest multiple by mode, none on overflow |
round_to(x, places, mode) | F64 to places decimals (negative: tens, hundreds, ...) |
round_to_multiple(x, step, mode) | F64 to a multiple of step |
F64 rounding is exact on the shortest decimal that prints as the value (its .str), then converted back to the nearest double. So round_to(2.675, 2, HalfAwayFromZero) == 2.68 and round_to_multiple(0.3, 0.1, Floor) == 0.3, as a person reading the numbers expects, where rounding the binary values gives 2.67 (Python's round) and 0.2 ((0.3 / 0.1).floor). The result is still a double: round_to(x, 2, m) is the double nearest the two-place decimal, not that decimal. Use Dec for money.
| Definition | What it does |
|---|---|
lerp(a, b, t), inverse_lerp(a, b, v), remap(v, in_lo, in_hi, out_lo, out_hi) | linear interpolation and its inverse (none for an empty range) |
smoothstep(e0, e1, x) | GLSL smoothstep, clamped |
approx_equal(a, b, rel_tol, abs_tol), is_close(a, b) | Python's math.isclose |
deg_to_rad, rad_to_deg | conversions |
wrap(x, lo, hi), normalize_degrees, normalize_radians | wrap into lo .. hi, 0 .. 360, -pi .. pi |
| Definition | What it does |
|---|---|
is_prime(n) | deterministic for every Int (Miller-Rabin, 12 bases) |
primes_up_to(limit) | sieve of Eratosthenes, limit up to 10,000,000 |
next_prime(n), prev_prime(n) | none past the largest Int prime or below 2 |
prime_factors(n), factorize(n) | with multiplicity, or as (prime, exponent); trial division then Pollard's rho, any Int |
divisors(n), totient(n) | sorted divisors, Euler's phi |
| Definition | What it does |
|---|---|
factorial(n), factorial_int(n) | exact BigInt (n up to 100,000), or Int up to 20! |
comb(n, k), comb_int(n, k) | binomial coefficient; 0 outside 0..=n; Int version none on overflow |
perm(n, k), perm_int(n, k) | n! / (n - k)! |
multinomial(ks), catalan(n) | multinomial coefficient, Catalan numbers |
fibonacci(n), fibonacci_int(n) | fast doubling, BigInt or Int up to F(92) |
| Definition | What it does |
|---|---|
floats(xs) | List[Int] to List[F64] |
checked_sum, checked_product | Int sum and product, none on overflow |
compensated_sum, cumulative_sum, product | Neumaier summation, running sums, F64 product |
mean, weighted_mean, geometric_mean, harmonic_mean | averages; mean survives overflow of the sum |
median, median_low, median_high | as Python's statistics |
mode, multimode | for any type, first seen on ties |
pvariance, variance, pstdev, stdev | population and sample, Welford's algorithm |
Quantile, quantile(xs, p, method), percentile(xs, p), quantiles(xs, n, method) | Linear (R-7, NumPy's default), Exclusive (R-6, Python's default), Lower, Higher, Midpoint, Nearest, NearestRank |
iqr, value_range | Q3 - Q1, max - min |
zscores, min_max_normalize | standardize, or scale to 0 .. 1 (none for constant data) |
covariance, correlation, ranks, spearman | sample covariance, Pearson, average ranks, Spearman |
Fit, linear_regression(xs, ys) | least squares slope, intercept and r^2 |
Bin, histogram(xs, bins), histogram_range(xs, lo, hi, bins) | equal-width bins |
moving_average(xs, w), exponential_moving_average(xs, alpha) | trailing simple and exponential averages |
Dec and Ratio. Correctly rounded fsum (Python's): compensated_sum is Neumaier's, accurate to about one ulp, not always the nearest double.Complex covers complex numbers), special functions beyond the builtin gamma/erf, numerical integration, root finding and optimization (Commons Math's analysis and optim packages).sspur add numeric
MIT licensed.
fn int_max() -> IntThe largest Int, 2^63 - 1.
fn int_min() -> IntThe smallest Int, -2^63.
fn checked_abs(x: Int) -> Opt[Int]The absolute value of x, or none for int_min(), whose absolute value is not an Int.
fn sign_int(x: Int) -> IntThe sign of x: -1, 0 or 1.
fn sign_f64(x: F64) -> F64The sign of x: -1.0, 1.0, or x itself for zeros (keeping -0.0) and NaN.
fn gcd_all(xs: List[Int]) -> IntThe greatest common divisor of every number in xs, never negative; 0 for an empty list or all zeros. Traps, like Int.gcd, only when the result would be 2^63 (only int_min() and zeros).
fn checked_lcm(a: Int, b: Int) -> Opt[Int]The least common multiple of a and b, never negative (0 if either is 0), or none when it doesn't fit in an Int.
fn lcm_all(xs: List[Int]) -> Opt[Int]The least common multiple of every number in xs, never negative: 1 for an empty list, 0 if any is 0, none when it doesn't fit in an Int.
fn egcd(a: Int where _ != -9223372036854775807 - 1, b: Int where _ != -9223372036854775807 - 1) -> (Int, Int, Int)The extended Euclidean algorithm: (g, x, y) with g = gcd(a, b) >= 0 and a * x + b * y == g. Neither argument may be int_min().
fn is_coprime(a: Int, b: Int) -> BoolWhether a and b have no common divisor above 1.
fn floor_div(a: Int, b: Int where _ != 0) -> IntDivision rounded toward negative infinity, like Python's //: floor_div(-7, 2) == -4. Traps on int_min() / -1, like /.
fn floor_mod(a: Int, b: Int where _ != 0) -> IntThe remainder that goes with floor_div, with the sign of b, like Python's %: floor_mod(-7, 2) == 1, floor_mod(7, -2) == -1.
fn ceil_div(a: Int, b: Int where _ != 0) -> IntDivision rounded toward positive infinity: ceil_div(7, 2) == 4, ceil_div(-7, 2) == -3.
fn euclid_mod(a: Int, b: Int where _ != 0) -> IntThe Euclidean remainder: always in 0 .. |b|, whatever the signs: euclid_mod(-7, 3) == 2. This is the "mod that is never negative".
fn euclid_div(a: Int, b: Int where _ != 0) -> IntThe Euclidean quotient: a == b * euclid_div(a, b) + euclid_mod(a, b).
fn addmod(a: Int, b: Int, m: Int where _ > 0) -> Int(a + b) mod m in 0 .. m, without overflow for any Int inputs.
fn mulmod(a: Int, b: Int, m: Int where _ > 0) -> Int(a * b) mod m in 0 .. m, without overflow for any Int inputs (it uses BigInt when the product doesn't fit).
fn mod_pow(base: Int, exp: Int where _ >= 0, m: Int where _ > 0) -> Intbase ^ exp mod m in 0 .. m, by square and multiply; mod_pow(x, 0, m) is 1 mod m.
fn mod_inv(a: Int, m: Int where _ > 0) -> Opt[Int]The inverse of a modulo m: x in 0 .. m with a * x mod m == 1 mod m, or none when a and m are not coprime.
fn isqrt(n: Int where _ >= 0) -> IntThe integer square root: the largest r with r * r <= n.
fn iroot(n: Int where _ >= 0, k: Int where _ >= 1) -> IntThe integer k-th root: the largest r >= 0 with r ^ k <= n.
fn icbrt(n: Int) -> IntThe integer cube root, rounded toward negative infinity, for any n: icbrt(27) == 3, icbrt(26) == 2, icbrt(-9) == -3.
fn checked_pow(base: Int, exp: Int where _ >= 0) -> Opt[Int]base ^ exp, or none if it overflows. checked_pow(0, 0) == some(1). For exact big powers use big(base).pow(exp).
fn is_perfect_square(n: Int) -> BoolWhether n is the square of an Int (0 and 1 are; negatives are not).
fn perfect_power(n: Int) -> Opt[(Int, Int)]The largest exponent k >= 2 and its base b with b ^ k == n, or none. Negative n can only be odd powers (-8 is (-2, 3)); 0, 1 and -1 give none, since every exponent fits.
fn is_perfect_power(n: Int) -> BoolWhether n is b ^ k for some Int b and k >= 2 (see perfect_power).
fn is_power_of_two(n: Int) -> BoolWhether n is a positive power of two (1, 2, 4, ...).
fn next_power_of_two(n: Int) -> Opt[Int]The smallest power of two >= n (1 for n <= 1), or none above 2^62.
fn digits(n: Int, base: Int where _ >= 2) -> List[Int]The digits of |n| in base, most significant first; [0] for 0. Works for int_min().
fn digit_sum(n: Int) -> IntThe sum of the decimal digits of |n|.
fn num_digits(n: Int, base: Int where _ >= 2) -> IntThe number of digits of |n| in base (1 for 0).
fn digital_root(n: Int) -> IntThe repeated digit sum of |n| down to one decimal digit (0 for 0, else 1 to 9).
fn from_digits(ds: List[Int], base: Int where _ >= 2) -> Opt[Int]The number with these digits in base, most significant first, or none if a digit is outside 0 .. base or the value overflows. from_digits([], b) == some(0).
fn to_radix(n: Int, base: Int where _ >= 2 and _ <= 36) -> Strn written in base 2 to 36 with lowercase letters and a leading - when negative: to_radix(255, 16) == "ff", to_radix(-5, 2) == "-101".
fn from_radix(s: Str, base: Int where _ >= 2 and _ <= 36) -> Opt[Int]Parses s as an Int in base 2 to 36: an optional + or -, then one or more digits in either case. No spaces, prefixes or separators. none on anything else or on overflow.
pub type Rounding = HalfUp | HalfDown | HalfEven | HalfAwayFromZero | HalfTowardZero | Up | Down | Ceiling | Floor derive Eq, ShowHow to round a value that falls between two candidates. The Half modes only differ on exact ties; the others look only at whether anything is discarded. HalfUp ties toward +infinity (2.5 to 3, -2.5 to -2); HalfDown ties toward -infinity; HalfEven ties to the even neighbour (banker's rounding); HalfAwayFromZero ties away from zero (school rounding, Java's HALF_UP); HalfTowardZero ties toward zero (Java's HALF_DOWN); Up rounds away from zero; Down toward zero (truncation); Ceiling toward +infinity; Floor toward -infinity.
fn div_round(a: Int, b: Int where _ != 0, mode: Rounding) -> Inta / b rounded to an Int with mode (the exact quotient decides, with no overflow in the comparison): div_round(7, 2, HalfEven) == 4, div_round(-7, 2, Floor) == -4. Traps on int_min() / -1, like /.
fn round_int_to_multiple(n: Int, step: Int where _ > 0, mode: Rounding) -> Opt[Int]n rounded to a multiple of step with mode, or none if that multiple overflows: round_int_to_multiple(17, 5, HalfEven) == some(15).
fn round_to(x: F64, places: Int where _ >= -400 and _ <= 400, mode: Rounding) -> F64x rounded to places decimal places (negative places round to tens, hundreds, ...) with mode. The rounding is exact on the shortest decimal that prints as x (its .str), so round_to(2.675, 2, HalfAwayFromZero) == 2.68 even though the binary double is slightly below 2.675; the result is the double nearest to the rounded decimal. NaN, infinities and zeros come back unchanged, and a value already short enough is returned as it is.
fn round_to_multiple(x: F64, step: F64 where _ > 0.0, mode: Rounding) -> F64x rounded to a multiple of step with mode: round_to_multiple(7.3, 0.5, HalfEven) == 7.5. Like round_to, it works exactly on the shortest decimals that print as x and step, so round_to_multiple(0.3, 0.1, Floor) == 0.3 (where (0.3 / 0.1).floor is 2), and returns the double nearest the exact multiple. NaN and infinities come back unchanged; a multiple too large for F64 is an infinity.
fn lerp(a: F64, b: F64, t: F64) -> F64The value t of the way from a to b: a at 0, b at 1, extrapolating outside (lerp(10.0, 20.0, 0.25) == 12.5). Exact at both ends.
fn inverse_lerp(a: F64, b: F64, v: F64) -> Opt[F64]Where v lies between a and b (0 at a, 1 at b), or none when a == b.
fn remap(v: F64, in_lo: F64, in_hi: F64, out_lo: F64, out_hi: F64) -> Opt[F64]v mapped linearly from the range in_lo .. in_hi to out_lo .. out_hi (not clamped), or none when the input range is empty.
fn smoothstep(edge0: F64, edge1: F64, x: F64) -> F64The smooth Hermite step from 0 at edge0 to 1 at edge1 (GLSL smoothstep), clamped to 0 and 1 outside; when the edges are equal it is a hard step at edge0.
fn approx_equal(a: F64, b: F64, rel_tol: F64 where _ >= 0.0, abs_tol: F64 where _ >= 0.0) -> BoolWhether a and b are close, as Python's math.isclose: |a - b| <= max(rel_tol * max(|a|, |b|), abs_tol). NaN is close to nothing (not even NaN), an infinity only to itself, and 0.0 and -0.0 are close. Use an abs_tol above 0 to compare with zero.
fn is_close(a: F64, b: F64) -> Boolapprox_equal(a, b, 1e-9, 0.0), Python's default.
fn deg_to_rad(d: F64) -> F64Degrees to radians.
fn rad_to_deg(r: F64) -> F64Radians to degrees.
fn wrap(x: F64, lo: F64, hi: F64) -> F64x wrapped into the half-open range lo .. hi, as an angle or a ring buffer index would wrap: wrap(370.0, 0.0, 360.0) == 10.0, wrap(-1.0, 0.0, 360.0) == 359.0. NaN and the infinities give NaN.
fn normalize_degrees(d: F64) -> F64An angle in degrees normalized to 0 .. 360.
fn normalize_radians(r: F64) -> F64An angle in radians normalized to -pi .. pi.
fn is_prime(n: Int) -> BoolWhether n is prime. Deterministic for every Int: trial division by the primes below 40, then Miller-Rabin with the first twelve prime bases, which has no false positives below 3.3 * 10^24.
fn primes_up_to(limit: Int where _ >= 0 and _ <= 10000000) -> List[Int]Every prime up to and including limit, in order, by the sieve of Eratosthenes. The limit is capped at 10,000,000 (664,579 primes) to keep the memory bounded.
fn next_prime(n: Int) -> Opt[Int]The smallest prime greater than n, or none when that is above int_max() (the largest Int prime is 9223372036854775783).
fn prev_prime(n: Int) -> Opt[Int]The largest prime less than n, or none when n <= 2.
fn prime_factors(n: Int where _ >= 1) -> List[Int]The prime factors of n with multiplicity, in increasing order: prime_factors(360) == [2, 2, 2, 3, 3, 5], prime_factors(1) == []. Trial division by the primes below 1000, then Pollard's rho (with Miller-Rabin to stop), so any Int factors in well under a second natively.
fn factorize(n: Int where _ >= 1) -> List[(Int, Int)]The factorization of n as (prime, exponent) pairs in increasing order of prime: factorize(360) == [(2, 3), (3, 2), (5, 1)], factorize(1) == [].
fn divisors(n: Int where _ >= 1) -> List[Int]Every positive divisor of n, in increasing order: divisors(12) == [1, 2, 3, 4, 6, 12].
fn totient(n: Int where _ >= 1) -> IntEuler's totient: how many of 1 ..= n are coprime to n.
fn factorial(n: Int where _ >= 0 and _ <= 100000) -> BigIntn!, exactly. n is capped at 100,000 (456,574 digits).
fn factorial_int(n: Int where _ >= 0) -> Opt[Int]n! as an Int, or none above 20!.
fn comb(n: Int where _ >= 0, k: Int) -> BigIntThe binomial coefficient "n choose k", exactly: 0 when k < 0 or k > n. Takes min(k, n - k) BigInt steps.
fn comb_int(n: Int where _ >= 0, k: Int) -> Opt[Int]"n choose k" as an Int, or none if it overflows; 0 when k < 0 or k > n.
fn perm(n: Int where _ >= 0, k: Int) -> BigIntThe number of ordered arrangements of k items out of n, n! / (n - k)!, exactly; 0 when k < 0 or k > n.
fn perm_int(n: Int where _ >= 0, k: Int) -> Opt[Int]perm(n, k) as an Int, or none if it overflows.
fn multinomial(ks: List[Int] where _.all(k => k >= 0)) -> BigIntThe multinomial coefficient (k1 + k2 + ...)! / (k1! k2! ...), exactly; 1 for an empty list.
fn catalan(n: Int where _ >= 0) -> BigIntThe nth Catalan number, comb(2n, n) / (n + 1): 1, 1, 2, 5, 14, 42, ...
fn fibonacci(n: Int where _ >= 0) -> BigIntThe nth Fibonacci number (F(0) = 0, F(1) = 1), exactly, by fast doubling.
fn fibonacci_int(n: Int where _ >= 0) -> Opt[Int]The nth Fibonacci number as an Int, or none above F(92).
fn floats(xs: List[Int]) -> List[F64]The Int list as F64s, to feed the statistics functions.
fn checked_sum(xs: List[Int]) -> Opt[Int]The sum of xs, or none if it overflows (the builtin sum traps instead).
fn checked_product(xs: List[Int]) -> Opt[Int]The product of xs (1 for an empty list), or none if it overflows.
fn compensated_sum(xs: List[F64]) -> F64The sum of xs with Neumaier's compensated summation, which keeps the rounding error of long or cancelling sums near one ulp: compensated_sum([1e100, 1.0, -1e100]) == 1.0 where sum gives 0.0. Infinities and NaN give what a plain sum gives. 0.0 for an empty list.
fn product(xs: List[F64]) -> F64The product of xs (1.0 for an empty list).
fn cumulative_sum(xs: List[F64]) -> List[F64]The running compensated sums: cumulative_sum([1.0, 2.0, 3.0]) == [1.0, 3.0, 6.0].
fn mean(xs: List[F64]) -> Opt[F64]The arithmetic mean, or none for an empty list. Uses the compensated sum, and rescales when the sum of finite values would overflow, so mean([1e308, 1e308]) == some(1e308).
fn weighted_mean(xs: List[F64], ws: List[F64]) -> Opt[F64]The mean of xs weighted by ws, or none if the lengths differ, the list is empty, a weight is negative or the weights sum to 0.
fn geometric_mean(xs: List[F64]) -> Opt[F64]The geometric mean, or none for an empty list or a negative value; 0.0 if any value is 0.
fn harmonic_mean(xs: List[F64]) -> Opt[F64]The harmonic mean, or none for an empty list or a negative value; 0.0 if any value is 0 (as Python's statistics.harmonic_mean).
fn median(xs: List[F64]) -> Opt[F64]The median (the mean of the two middle values for an even count), or none for an empty list.
fn median_low(xs: List[F64]) -> Opt[F64]The lower of the two middle values (the middle one for an odd count), or none if empty.
fn median_high(xs: List[F64]) -> Opt[F64]The higher of the two middle values (the middle one for an odd count), or none if empty.
fn mode[T](xs: List[T]) -> Opt[T]The most common value, the first one seen on ties, or none for an empty list. Works for any type; values compare with == (so NaN counts as equal to NaN).
fn multimode[T](xs: List[T]) -> List[T]Every most common value, in first-seen order; [] for an empty list.
fn pvariance(xs: List[F64]) -> Opt[F64]The population variance (divided by n), or none for an empty list. Welford's algorithm.
fn variance(xs: List[F64]) -> Opt[F64]The sample variance (divided by n - 1), or none for fewer than two values.
fn pstdev(xs: List[F64]) -> Opt[F64]The population standard deviation, or none for an empty list.
fn stdev(xs: List[F64]) -> Opt[F64]The sample standard deviation, or none for fewer than two values.
pub type Quantile = Linear | Exclusive | Lower | Higher | Midpoint | Nearest | NearestRank derive Eq, ShowHow a quantile is read off the sorted data (h is the 0-based position (n - 1) * p): Linear interpolates at h (R-7, the default of NumPy, Excel PERCENTILE.INC and Python's method="inclusive"); Exclusive interpolates at (n + 1) * p - 1, clamped to the data (R-6, Python's default method="exclusive", Excel PERCENTILE.EXC); Lower and Higher take the element at floor(h) or ceil(h); Midpoint averages those two; Nearest takes the element nearest h, ties to even (NumPy's "nearest"); NearestRank takes the element at rank ceil(p * n) (the classical nearest-rank method, never interpolated).
fn quantile(xs: List[F64], p: F64 where _ >= 0.0 and _ <= 1.0, method: Quantile) -> Opt[F64]The p quantile (0 to 1) of xs by method, or none for an empty list; NaN if xs holds a NaN. quantile(xs, 0.5, Linear) is the median.
fn percentile(xs: List[F64], p: F64 where _ >= 0.0 and _ <= 100.0) -> Opt[F64]The p percentile (0 to 100) with linear interpolation (Linear), or none if empty.
fn quantiles(xs: List[F64], n: Int where _ >= 1, method: Quantile) -> Opt[List[F64]]The n - 1 cut points that divide xs into n groups of equal probability, as Python's statistics.quantiles(xs, n=n, method=...) (pass Exclusive for its default): n = 4 gives the quartiles. none for an empty list.
fn iqr(xs: List[F64]) -> Opt[F64]The interquartile range, Q3 - Q1 with Linear quantiles, or none for an empty list.
fn value_range(xs: List[F64]) -> Opt[F64]The largest value minus the smallest, or none for an empty list.
fn zscores(xs: List[F64]) -> Opt[List[F64]]The z-score of each value, (x - mean) / pstdev, or none for an empty list or one whose values are all equal.
fn min_max_normalize(xs: List[F64]) -> Opt[List[F64]]Each value scaled to 0 .. 1 by (x - min) / (max - min), or none for an empty list or one whose values are all equal.
fn covariance(xs: List[F64], ys: List[F64]) -> Opt[F64]The sample covariance (divided by n - 1), as Python's statistics.covariance, or none if the lengths differ or there are fewer than two pairs.
fn correlation(xs: List[F64], ys: List[F64]) -> Opt[F64]Pearson's correlation coefficient in -1 to 1, or none if the lengths differ, there are fewer than two pairs, or either series is constant.
fn ranks(xs: List[F64]) -> List[F64]The rank of each value, 1 for the smallest, with ties given the average of their ranks (ranks([10.0, 20.0, 10.0]) == [1.5, 3.0, 1.5]), as used by Spearman's correlation.
fn spearman(xs: List[F64], ys: List[F64]) -> Opt[F64]Spearman's rank correlation (Pearson's on the ranks, ties averaged), or none as for correlation.
pub type Fit = {slope: F64, intercept: F64, r_squared: F64} derive Eq, ShowThe least squares line through a set of points: y = slope * x + intercept, and r_squared, the share of the variance in y it explains (1.0 when y is constant and the fit is exact).
fn linear_regression(xs: List[F64], ys: List[F64]) -> Opt[Fit]Simple linear regression of ys on xs by ordinary least squares, or none if the lengths differ, there are fewer than two points, or every x is the same.
pub type Bin = {lo: F64, hi: F64, count: Int} derive Eq, ShowOne histogram bin: the values v with lo <= v < hi (the last bin also takes hi).
fn histogram_range(xs: List[F64], lo: F64, hi: F64, bins: Int where _ >= 1) -> List[Bin]bins equal-width bins from lo to hi, counting the values of xs inside that range; values outside it and NaN are not counted.
fn histogram(xs: List[F64], bins: Int where _ >= 1) -> List[Bin]bins equal-width bins spanning the smallest to the largest finite value of xs; [] when there is none. When every value is the same, one bin [v, v] holds them all.
fn moving_average(xs: List[F64], window: Int where _ >= 1) -> List[F64]The trailing simple moving average over window values: one result per full window, so xs.len - window + 1 results ([] when xs is shorter than the window).
fn exponential_moving_average(xs: List[F64], alpha: F64 where _ > 0.0 and _ <= 1.0) -> List[F64]The exponential moving average with smoothing factor alpha (0 < alpha <= 1), seeded with the first value: e[0] = x[0], e[i] = alpha * x[i] + (1 - alpha) * e[i - 1].
| Version | Published | Commit | Source | Effects |
|---|---|---|---|---|
| 0.1.1 | 2026-10-18 | c1029415e6 | snapshot | |
| 0.1.0 | 2026-10-16 | 3a9747ca64 | snapshot |