Skip to content

Math & Statistics

Overview

Frankie has a built-in math and statistics library covering everything from basic arithmetic to descriptive statistics.

The statistical functions are borrowed from R — mean, median, stdev, variance are first-class citizens, not an afterthought. Since v1.22, the library leans harder into its FORTRAN heritage too: real matrix math, complex numbers, and a full set of intrinsic math functions (sin, log, factorial, and friends).

All functions are available without any import.


Arithmetic Operators

Operator Meaning Example Result
+ Addition 10 + 3 13
- Subtraction 10 - 3 7
* Multiplication 10 * 3 30
/ Float division 10 / 3 3.333...
// Integer division 10 // 3 3
% Modulo 10 % 3 1
** Exponentiation 2 ** 8 256

/ always returns a Float. Use // (borrowed from Fortran) for a whole-number result.

puts 10 / 3     # 3.3333333333333335
puts 10 // 3    # 3
puts 10 % 3     # 1
puts 2 ** 10    # 1024
puts 2 ** 0     # 1

Integer Division and Modulo with Negatives

// uses floor division — it rounds toward negative infinity:

puts 17 // 5    #  3
puts (-17) // 5 # -4  (floors toward -∞)
puts 17 % 5     #  2
puts (-17) % 5  #  3  (result has the sign of the divisor)

Core Math Functions

abs(x)

Absolute value

puts abs(-42)    → 42
puts abs(42)     → 42
puts abs(-3.14)  → 3.14

sqrt(x)

Square root

Always returns a Float.

puts sqrt(144)   → 12.0
puts sqrt(2)     → 1.4142135623730951
puts sqrt(0)     → 0.0

floor(x)

Round down

Returns the largest integer less than or equal to x.

puts floor(3.7)    → 3
puts floor(3.0)    → 3
puts floor(-3.2)   → -4  (toward -∞, not toward zero)

ceil(x)

Round up

Returns the smallest integer greater than or equal to x.

puts ceil(3.2)    → 4
puts ceil(3.0)    → 3
puts ceil(-3.7)   → -3

round(x, n)

Round to n decimal places

n defaults to 0. Returns a Float when n > 0, an integer-like Float when n = 0.

puts round(3.14159, 2)   → 3.14
puts round(3.14159, 4)   → 3.1416
puts round(3.5)          → 4.0
puts round(-3.5)         → -4.0
puts round(2.71828, 3)   → 2.718

sin(x), cos(x), tan(x), asin(x), acos(x), atan(x), atan2(y, x) (v1.22)

Trigonometric functions

Angles are in radians, matching FORTRAN's intrinsic SIN/COS/TAN family.

puts sin(0)          → 0.0
puts cos(0)          → 1.0
puts atan2(1, 1)      → 0.7853981633974483   — π/4

exp(x), log(x, base = nil), log10(x), log2(x) (v1.22)

Exponential and logarithms

log(x) is natural log by default; pass a second argument for a specific base.

puts exp(1)          → 2.718281828459045
puts log(E)          → 1.0
puts log(8, 2)        → 3.0
puts log10(1000)      → 3.0
puts log2(8)          → 3.0

PI and E (v1.22)

Constants

puts PI    → 3.141592653589793
puts E     → 2.718281828459045

factorial(n), gcd(a, b), lcm(a, b) (v1.22)

Number theory

puts factorial(5)    → 120
puts gcd(12, 18)      → 6
puts lcm(4, 6)         → 12

factorial raises for negative input.

clamp(x, lo, hi)

Constrain to a range

Returns lo if x < lo, hi if x > hi, otherwise x.

puts clamp(15, 0, 10)       → 10  — above max
puts clamp(-5, 0, 10)       → 0   — below min
puts clamp(5,  0, 10)       → 5   — within range
puts clamp(3.14, 0.0, 3.0)  → 3.0

min and max

Work on two values or on a vector:

puts min(3, 7)                → 3
puts max(3, 7)                → 7
puts min([3, 1, 4, 1, 5, 9])  → 1
puts max([3, 1, 4, 1, 5, 9])  → 9

# Also available as vector methods
puts [3, 1, 4, 1, 5, 9].min   → 1
puts [3, 1, 4, 1, 5, 9].max   → 9

Statistics Functions

All statistics functions take a vector of numbers.

sum(v)

Total

puts sum([1, 2, 3, 4, 5])    → 15
puts [10, 20, 30].sum        → 60  — method form

mean(v)

Arithmetic mean

data = [23, 45, 12, 67, 34, 89, 56]
puts mean(data)    → 46.57142857142857
puts data.mean     → same — method form

median(v)

Middle value

For even-length vectors, returns the average of the two middle values.

puts median([1, 2, 3, 4, 5])       → 3
puts median([1, 2, 3, 4, 5, 6])    → 3.5
puts median([23, 45, 12, 67, 34])  → 34

stdev(v)

Sample standard deviation

Measures spread. Uses n-1 (Bessel's correction) — sample standard deviation, not population.

data = [23, 45, 12, 67, 34, 89, 56, 11, 78, 42]
puts stdev(data)   → 26.84130316425705

variance(v)

Sample variance

The square of the standard deviation.

puts variance(data)   → 720.4555555555554

Sequences and Ranges

seq(start, stop, step)

Numeric sequence

Generates a vector from start to stop in steps of step. Borrowed from R.

puts seq(1, 5)            → [1, 2, 3, 4, 5]
puts seq(0, 10, 2)        → [0, 2, 4, 6, 8, 10]
puts seq(5, 1, -1)        → [5, 4, 3, 2, 1]
puts seq(0.0, 1.0, 0.25)  → [0.0, 0.25, 0.5, 0.75, 1.0]

linspace(start, stop, n)

Evenly spaced values

Generates exactly n values evenly distributed between start and stop, inclusive.

puts linspace(0.0, 1.0, 5)    → [0.0, 0.25, 0.5, 0.75, 1.0]
puts linspace(0.0, 100.0, 6)  → [0.0, 20.0, 40.0, 60.0, 80.0, 100.0]

vec(range)

Vector from a range literal

puts vec(1..10)    → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
puts vec(0...5)    → [0, 1, 2, 3, 4]

rep(x, n)

Repeat a value

puts rep(0, 5)      → [0, 0, 0, 0, 0]
puts rep(3.14, 3)   → [3.14, 3.14, 3.14]
puts rep("ha", 3)   → ["ha", "ha", "ha"]

Vectorised Arithmetic

Operations on vectors apply element-wise — R does this natively, and it's exactly the whole-array math FORTRAN 90+ pioneered decades earlier (C = A + B adds two arrays element-wise with no explicit loop). +, -, /, %, and ** all work this way:

v = [1, 2, 3, 4, 5]

puts v + [10,10,10,10,10]   → [11, 12, 13, 14, 15]
puts v ** 2                 → [1, 4, 9, 16, 25]
puts v - [1, 1, 1, 1, 1]    → [0, 1, 2, 3, 4]

Vectors must be the same length for binary operations. Use linspace or rep to create a matching vector.

* is the one exception — v * 2 repeats the vector (Python-list semantics: [1,2,3] * 2 → [1,2,3,1,2,3]), not element-wise scaling. For scaling, either add the vector to itself the needed number of times, or reach for matrix_scale if you're working with a matrix (below).


Matrix Math (v1.22)

A matrix is just a vector of vectors, and since v1.22 arithmetic recurses through nested vectors at every depth — so +, -, /, %, and ** on two matrices are genuinely element-wise, not a silent row-by-row concatenation:

m1 = [[1, 2], [3, 4]]
m2 = [[5, 6], [7, 8]]

puts m1 + m2   → [[6, 8], [10, 12]]
puts m1 - m2   → [[-4, -4], [-4, -4]]

matrix_new(rows, cols, fill = 0)

Build a filled matrix

puts matrix_new(2, 3, 0)   → [[0, 0, 0], [0, 0, 0]]

matrix_identity(n)

Build an n×n identity matrix

puts matrix_identity(3)   → [[1, 0, 0], [0, 1, 0], [0, 0, 1]]

matrix_shape(m)

Return [rows, cols]

puts matrix_shape(matrix_new(2, 5, 0))   → [2, 5]

matrix_transpose(m)

Swap rows and columns

puts matrix_transpose([[1, 2, 3], [4, 5, 6]])   → [[1, 4], [2, 5], [3, 6]]

matrix_scale(m, n)

Multiply every element by n

Since * stays as vector-repeat at the top level (unchanged, matching pre-v1.22 behavior), use matrix_scale for true element-wise scalar multiplication:

puts matrix_scale([[1, 2], [3, 4]], 3)   → [[3, 6], [9, 12]]

matrix_multiply(m1, m2)

Real matrix multiplication (row · column)

This is different from *, which stays element-wise for two matrices of the same shape — matrix_multiply does the actual linear-algebra dot-product multiplication, and requires m1's column count to match m2's row count.

puts matrix_multiply([[1, 2], [3, 4]], [[5, 6], [7, 8]])   → [[19, 22], [43, 50]]

Raises if the shapes are incompatible.


Complex Numbers (v1.22)

FORTRAN has shipped a native COMPLEX type since FORTRAN 66. Frankie exposes Python's built-in complex number support the same flat, no-import-needed way as everything else in this library — +, -, *, /, and ** all work natively once you have one.

c1 = complex_new(3, 4)
c2 = complex_new(1, 2)

puts c1              → 3+4i
puts c1 + c2          → 4+6i
puts c1 * c2          → -5+10i

Printed in conventional math notation (3+4i), not Python's electrical-engineering-flavored 3+4j.

complex_new(real, imag = 0)

Build a complex number

complex_real(c) / complex_imag(c)

Read the real / imaginary parts

c = complex_new(3, 4)
puts complex_real(c)   → 3.0
puts complex_imag(c)   → 4.0

complex_conj(c)

Complex conjugate (negates the imaginary part)

puts complex_conj(complex_new(3, 4))   → 3-4i

complex_abs(c)

Magnitude

puts complex_abs(complex_new(3, 4))   → 5.0   — 3-4-5 triangle

The Pipe Operator for Data Pipelines

|> passes the left value as the argument to the right function. Combines naturally with stats:

data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]

data |> sum  |> puts    → 39
data |> mean |> puts    → 3.9
data |> min  |> puts    → 1
data |> max  |> puts    → 9

# Chain transformations
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  .select do |x| x % 2 == 0 end
  |> sum
  |> puts

→ 30

Randomness

rand_int(a, b)

Random integer

Returns a random integer between a and b inclusive.

puts rand_int(1, 6)      → dice roll — 1 to 6
puts rand_int(0, 100)    → 0 to 100

rand_float(a, b)

Random float

Returns a random float between a and b.

puts rand_float(0.0, 1.0)    → e.g. 0.7493381866018054
puts rand_float(-1.0, 1.0)   → e.g. -0.234...

rand()

Random float 0..1

puts rand()   # e.g. 0.4217...

rand_seed(n)

Seed for reproducibility

Fix the random seed to get the same sequence every time — useful for tests and reproducible simulations.

rand_seed(42)
puts rand_int(1, 100)   → always the same value for seed 42
puts rand_int(1, 100)   → next value in the same sequence

shuffle(v)

Randomly reorder a vector

v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
puts shuffle(v)   → e.g. [4, 9, 1, 7, 3, 10, 5, 2, 8, 6]

sample(v, n)

Random sample without replacement

v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
puts sample(v, 3)   → e.g. [7, 2, 9]
puts sample(v, 1)   → e.g. [4]

Numeric Formatting

Use sprintf for precise control over how numbers are displayed:

puts sprintf("%.2f", 3.14159)                    → 3.14
puts sprintf("%.4f", 3.14159)                    → 3.1416
puts sprintf("%08.3f", 3.14)                     → 0003.140
puts sprintf("%+d", 42)                          → +42
puts sprintf("%+d", -42)                         → -42
puts sprintf("%d items at $%.2f each", 5, 1.99)  → 5 items at $1.99 each

Or use round for display without format strings:

data = [1.23456, 7.89012, 3.45678]
puts data.map do |x| 
  round(x, 2) 
end             

→ [1.23, 7.89, 3.46]

Quick Reference

Function Description Example
abs(x) Absolute value abs(-5) → 5
sqrt(x) Square root sqrt(9) → 3.0
floor(x) Round down floor(3.9) → 3
ceil(x) Round up ceil(3.1) → 4
round(x, n) Round to n places round(3.14159, 2) → 3.14
clamp(x, lo, hi) Constrain to range clamp(15, 0, 10) → 10
min(a, b) / min(v) Minimum min(3, 7) → 3
max(a, b) / max(v) Maximum max(3, 7) → 7
sum(v) Sum of all elements sum([1,2,3]) → 6
mean(v) Arithmetic mean mean([1,2,3]) → 2.0
median(v) Middle value median([1,2,3]) → 2
stdev(v) Sample standard deviation stdev([2,4,4,4,5,5,7,9]) → 2.0
variance(v) Sample variance variance([2,4,6]) → 4.0
seq(start, stop, step) Numeric sequence seq(1, 5) → [1,2,3,4,5]
linspace(start, stop, n) n evenly-spaced values linspace(0.0, 1.0, 3) → [0.0, 0.5, 1.0]
vec(range) Vector from range vec(1..5) → [1,2,3,4,5]
rep(x, n) Repeat value rep(0, 3) → [0,0,0]
rand_int(a, b) Random integer a..b rand_int(1, 6)
rand_float(a, b) Random float a..b rand_float(0.0, 1.0)
rand() Random float 0..1 rand()
rand_seed(n) Fix random seed rand_seed(42)
shuffle(v) Randomly reorder shuffle([1,2,3,4,5])
sample(v, n) Random sample sample(v, 3)
sprintf(fmt, ...) Formatted number string sprintf("%.2f", 3.14159)
sin(x) / cos(x) / tan(x) (v1.22) Trigonometric functions sin(0) → 0.0
asin(x) / acos(x) / atan(x) / atan2(y, x) (v1.22) Inverse trig atan2(1, 1) → 0.785...
exp(x) (v1.22) e^x exp(1) → 2.718...
log(x, base = nil) (v1.22) Logarithm log(8, 2) → 3.0
log10(x) / log2(x) (v1.22) Base-10 / base-2 log log10(1000) → 3.0
PI / E (v1.22) Constants PI → 3.14159...
factorial(n) (v1.22) n! factorial(5) → 120
gcd(a, b) / lcm(a, b) (v1.22) Number theory gcd(12, 18) → 6
matrix_new(rows, cols, fill) (v1.22) Filled matrix matrix_new(2, 2, 0) → [[0,0],[0,0]]
matrix_identity(n) (v1.22) Identity matrix matrix_identity(2) → [[1,0],[0,1]]
matrix_shape(m) (v1.22) [rows, cols] matrix_shape([[1,2,3]]) → [1,3]
matrix_transpose(m) (v1.22) Swap rows/cols matrix_transpose([[1,2]]) → [[1],[2]]
matrix_scale(m, n) (v1.22) Element-wise scalar multiply matrix_scale([[1,2]], 2) → [[2,4]]
matrix_multiply(m1, m2) (v1.22) Real matrix multiplication matrix_multiply([[1,2]], [[1],[2]]) → [[5]]
complex_new(real, imag) (v1.22) Complex number complex_new(3, 4) → 3+4i
complex_real(c) / complex_imag(c) (v1.22) Real / imaginary part complex_real(complex_new(3,4)) → 3.0
complex_conj(c) (v1.22) Complex conjugate complex_conj(complex_new(3,4)) → 3-4i
complex_abs(c) (v1.22) Magnitude complex_abs(complex_new(3,4)) → 5.0