Package

numeric

Number theory, exact rounding modes, primes, combinatorics and statistics for Int, F64 and BigInt, pure and tested in both tiers

0.1.1 MIT

Install

sspur add numeric@^0.1.1

or in sspur.toml:

numeric = { version = "^0.1.1" }

README

numeric

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 <.

Example

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})

API

Integers

DefinitionWhat 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_modPython's // and % (remainder takes the divisor's sign)
ceil_divdivision rounded toward +infinity
euclid_div, euclid_modEuclidean 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_powersquare test; largest (base, k) with base ^ k == n
is_power_of_two, next_power_of_twopowers of two, none above 2^62

Digits and radix

DefinitionWhat 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

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.

DefinitionWhat 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.

Interpolation, closeness and angles

DefinitionWhat 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_degconversions
wrap(x, lo, hi), normalize_degrees, normalize_radianswrap into lo .. hi, 0 .. 360, -pi .. pi

Primes

DefinitionWhat 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

Combinatorics

DefinitionWhat 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)

Statistics

DefinitionWhat it does
floats(xs)List[Int] to List[F64]
checked_sum, checked_productInt sum and product, none on overflow
compensated_sum, cumulative_sum, productNeumaier summation, running sums, F64 product
mean, weighted_mean, geometric_mean, harmonic_meanaverages; mean survives overflow of the sum
median, median_low, median_highas Python's statistics
mode, multimodefor any type, first seen on ties
pvariance, variance, pstdev, stdevpopulation 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_rangeQ3 - Q1, max - min
zscores, min_max_normalizestandardize, or scale to 0 .. 1 (none for constant data)
covariance, correlation, ranks, spearmansample 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

Out of scope

  • Arbitrary-precision decimals and exact money arithmetic: use the builtin Dec and Ratio. Correctly rounded fsum (Python's): compensated_sum is Neumaier's, accurate to about one ulp, not always the nearest double.
  • Linear algebra, matrices, complex analysis (the builtin Complex covers complex numbers), special functions beyond the builtin gamma/erf, numerical integration, root finding and optimization (Commons Math's analysis and optim packages).
  • Probability distributions and random sampling (the builtins have seeded generators and distributions), hypothesis tests and confidence intervals.
  • Multiple regression and other models; streaming or windowed statistics objects.

Install

sspur add numeric

MIT licensed.

API

int_max fn

fn int_max() -> Int

The largest Int, 2^63 - 1.

int_min fn

fn int_min() -> Int

The smallest Int, -2^63.

checked_abs fn

fn checked_abs(x: Int) -> Opt[Int]

The absolute value of x, or none for int_min(), whose absolute value is not an Int.

sign_int fn

fn sign_int(x: Int) -> Int

The sign of x: -1, 0 or 1.

sign_f64 fn

fn sign_f64(x: F64) -> F64

The sign of x: -1.0, 1.0, or x itself for zeros (keeping -0.0) and NaN.

gcd_all fn

fn gcd_all(xs: List[Int]) -> Int

The 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).

checked_lcm fn

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.

lcm_all fn

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.

egcd fn

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().

is_coprime fn

fn is_coprime(a: Int, b: Int) -> Bool

Whether a and b have no common divisor above 1.

floor_div fn

fn floor_div(a: Int, b: Int where _ != 0) -> Int

Division rounded toward negative infinity, like Python's //: floor_div(-7, 2) == -4. Traps on int_min() / -1, like /.

floor_mod fn

fn floor_mod(a: Int, b: Int where _ != 0) -> Int

The remainder that goes with floor_div, with the sign of b, like Python's %: floor_mod(-7, 2) == 1, floor_mod(7, -2) == -1.

ceil_div fn

fn ceil_div(a: Int, b: Int where _ != 0) -> Int

Division rounded toward positive infinity: ceil_div(7, 2) == 4, ceil_div(-7, 2) == -3.

euclid_mod fn

fn euclid_mod(a: Int, b: Int where _ != 0) -> Int

The Euclidean remainder: always in 0 .. |b|, whatever the signs: euclid_mod(-7, 3) == 2. This is the "mod that is never negative".

euclid_div fn

fn euclid_div(a: Int, b: Int where _ != 0) -> Int

The Euclidean quotient: a == b * euclid_div(a, b) + euclid_mod(a, b).

addmod fn

fn addmod(a: Int, b: Int, m: Int where _ > 0) -> Int

(a + b) mod m in 0 .. m, without overflow for any Int inputs.

mulmod fn

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).

mod_pow fn

fn mod_pow(base: Int, exp: Int where _ >= 0, m: Int where _ > 0) -> Int

base ^ exp mod m in 0 .. m, by square and multiply; mod_pow(x, 0, m) is 1 mod m.

mod_inv fn

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.

isqrt fn

fn isqrt(n: Int where _ >= 0) -> Int

The integer square root: the largest r with r * r <= n.

iroot fn

fn iroot(n: Int where _ >= 0, k: Int where _ >= 1) -> Int

The integer k-th root: the largest r >= 0 with r ^ k <= n.

icbrt fn

fn icbrt(n: Int) -> Int

The integer cube root, rounded toward negative infinity, for any n: icbrt(27) == 3, icbrt(26) == 2, icbrt(-9) == -3.

checked_pow fn

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).

is_perfect_square fn

fn is_perfect_square(n: Int) -> Bool

Whether n is the square of an Int (0 and 1 are; negatives are not).

perfect_power fn

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.

is_perfect_power fn

fn is_perfect_power(n: Int) -> Bool

Whether n is b ^ k for some Int b and k >= 2 (see perfect_power).

is_power_of_two fn

fn is_power_of_two(n: Int) -> Bool

Whether n is a positive power of two (1, 2, 4, ...).

next_power_of_two fn

fn next_power_of_two(n: Int) -> Opt[Int]

The smallest power of two >= n (1 for n <= 1), or none above 2^62.

digits fn

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().

digit_sum fn

fn digit_sum(n: Int) -> Int

The sum of the decimal digits of |n|.

num_digits fn

fn num_digits(n: Int, base: Int where _ >= 2) -> Int

The number of digits of |n| in base (1 for 0).

digital_root fn

fn digital_root(n: Int) -> Int

The repeated digit sum of |n| down to one decimal digit (0 for 0, else 1 to 9).

from_digits fn

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).

to_radix fn

fn to_radix(n: Int, base: Int where _ >= 2 and _ <= 36) -> Str

n written in base 2 to 36 with lowercase letters and a leading - when negative: to_radix(255, 16) == "ff", to_radix(-5, 2) == "-101".

from_radix fn

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.

Rounding type

pub type Rounding = HalfUp | HalfDown | HalfEven | HalfAwayFromZero | HalfTowardZero | Up | Down | Ceiling | Floor derive Eq, Show

How 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.

div_round fn

fn div_round(a: Int, b: Int where _ != 0, mode: Rounding) -> Int

a / 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 /.

round_int_to_multiple fn

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).

round_to fn

fn round_to(x: F64, places: Int where _ >= -400 and _ <= 400, mode: Rounding) -> F64

x 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.

round_to_multiple fn

fn round_to_multiple(x: F64, step: F64 where _ > 0.0, mode: Rounding) -> F64

x 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.

lerp fn

fn lerp(a: F64, b: F64, t: F64) -> F64

The 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.

inverse_lerp fn

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.

remap fn

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.

smoothstep fn

fn smoothstep(edge0: F64, edge1: F64, x: F64) -> F64

The 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.

approx_equal fn

fn approx_equal(a: F64, b: F64, rel_tol: F64 where _ >= 0.0, abs_tol: F64 where _ >= 0.0) -> Bool

Whether 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.

is_close fn

fn is_close(a: F64, b: F64) -> Bool

approx_equal(a, b, 1e-9, 0.0), Python's default.

deg_to_rad fn

fn deg_to_rad(d: F64) -> F64

Degrees to radians.

rad_to_deg fn

fn rad_to_deg(r: F64) -> F64

Radians to degrees.

wrap fn

fn wrap(x: F64, lo: F64, hi: F64) -> F64

x 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.

normalize_degrees fn

fn normalize_degrees(d: F64) -> F64

An angle in degrees normalized to 0 .. 360.

normalize_radians fn

fn normalize_radians(r: F64) -> F64

An angle in radians normalized to -pi .. pi.

is_prime fn

fn is_prime(n: Int) -> Bool

Whether 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.

primes_up_to fn

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.

next_prime fn

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).

prev_prime fn

fn prev_prime(n: Int) -> Opt[Int]

The largest prime less than n, or none when n <= 2.

prime_factors fn

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.

factorize fn

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) == [].

divisors fn

fn divisors(n: Int where _ >= 1) -> List[Int]

Every positive divisor of n, in increasing order: divisors(12) == [1, 2, 3, 4, 6, 12].

totient fn

fn totient(n: Int where _ >= 1) -> Int

Euler's totient: how many of 1 ..= n are coprime to n.

factorial fn

fn factorial(n: Int where _ >= 0 and _ <= 100000) -> BigInt

n!, exactly. n is capped at 100,000 (456,574 digits).

factorial_int fn

fn factorial_int(n: Int where _ >= 0) -> Opt[Int]

n! as an Int, or none above 20!.

comb fn

fn comb(n: Int where _ >= 0, k: Int) -> BigInt

The binomial coefficient "n choose k", exactly: 0 when k < 0 or k > n. Takes min(k, n - k) BigInt steps.

comb_int fn

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.

perm fn

fn perm(n: Int where _ >= 0, k: Int) -> BigInt

The number of ordered arrangements of k items out of n, n! / (n - k)!, exactly; 0 when k < 0 or k > n.

perm_int fn

fn perm_int(n: Int where _ >= 0, k: Int) -> Opt[Int]

perm(n, k) as an Int, or none if it overflows.

multinomial fn

fn multinomial(ks: List[Int] where _.all(k => k >= 0)) -> BigInt

The multinomial coefficient (k1 + k2 + ...)! / (k1! k2! ...), exactly; 1 for an empty list.

catalan fn

fn catalan(n: Int where _ >= 0) -> BigInt

The nth Catalan number, comb(2n, n) / (n + 1): 1, 1, 2, 5, 14, 42, ...

fibonacci fn

fn fibonacci(n: Int where _ >= 0) -> BigInt

The nth Fibonacci number (F(0) = 0, F(1) = 1), exactly, by fast doubling.

fibonacci_int fn

fn fibonacci_int(n: Int where _ >= 0) -> Opt[Int]

The nth Fibonacci number as an Int, or none above F(92).

floats fn

fn floats(xs: List[Int]) -> List[F64]

The Int list as F64s, to feed the statistics functions.

checked_sum fn

fn checked_sum(xs: List[Int]) -> Opt[Int]

The sum of xs, or none if it overflows (the builtin sum traps instead).

checked_product fn

fn checked_product(xs: List[Int]) -> Opt[Int]

The product of xs (1 for an empty list), or none if it overflows.

compensated_sum fn

fn compensated_sum(xs: List[F64]) -> F64

The 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.

product fn

fn product(xs: List[F64]) -> F64

The product of xs (1.0 for an empty list).

cumulative_sum fn

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].

mean fn

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).

weighted_mean fn

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.

geometric_mean fn

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.

harmonic_mean fn

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).

median fn

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.

median_low fn

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.

median_high fn

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.

mode fn

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).

multimode fn

fn multimode[T](xs: List[T]) -> List[T]

Every most common value, in first-seen order; [] for an empty list.

pvariance fn

fn pvariance(xs: List[F64]) -> Opt[F64]

The population variance (divided by n), or none for an empty list. Welford's algorithm.

variance fn

fn variance(xs: List[F64]) -> Opt[F64]

The sample variance (divided by n - 1), or none for fewer than two values.

pstdev fn

fn pstdev(xs: List[F64]) -> Opt[F64]

The population standard deviation, or none for an empty list.

stdev fn

fn stdev(xs: List[F64]) -> Opt[F64]

The sample standard deviation, or none for fewer than two values.

Quantile type

pub type Quantile = Linear | Exclusive | Lower | Higher | Midpoint | Nearest | NearestRank derive Eq, Show

How 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).

quantile fn

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.

percentile fn

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.

quantiles fn

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.

iqr fn

fn iqr(xs: List[F64]) -> Opt[F64]

The interquartile range, Q3 - Q1 with Linear quantiles, or none for an empty list.

value_range fn

fn value_range(xs: List[F64]) -> Opt[F64]

The largest value minus the smallest, or none for an empty list.

zscores fn

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.

min_max_normalize fn

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.

covariance fn

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.

correlation fn

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.

ranks fn

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.

spearman fn

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.

Fit type

pub type Fit = {slope: F64, intercept: F64, r_squared: F64} derive Eq, Show

The 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).

linear_regression fn

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.

Bin type

pub type Bin = {lo: F64, hi: F64, count: Int} derive Eq, Show

One histogram bin: the values v with lo <= v < hi (the last bin also takes hi).

histogram_range fn

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.

histogram fn

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.

moving_average fn

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).

exponential_moving_average fn

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].

Versions

VersionPublishedCommitSourceEffects
0.1.1 2026-10-18c1029415e6snapshot
0.1.0 2026-10-163a9747ca64snapshot