NAME

Sidef::Math::Math - Mathematical functions and algorithms for Sidef

DESCRIPTION

The Math class provides a comprehensive collection of mathematical functions, including arithmetic operations, number theory algorithms, sequence generators, and advanced computational methods. All methods are called as class methods on the Math object.

SYNOPSIS

# Basic statistics
say Math.avg(1, 2, 3, 4)                    #=> 2.5
say Math.geometric_mean(2, 8, 32)           #=> 8

# Number theory
say Math.gcd(12, 18, 24)                    #=> 6
say Math.lcm(4, 6, 8)                       #=> 24
say Math.chinese([14,643], [254,419])       #=> 87041638

# Sequences and recurrences
say Math.linear_rec([1,1], [0,1], 0, 10)    # Fibonacci: [0,1,1,2,3,5,8,13,21,34,55]
say Math.seq(2, {.last.next_prime}).first(5) #=> [2,3,5,7,11]

# Advanced algorithms
say Math.batch_gcd(1909, 2923, 291, 205)    # Efficient batch GCD
say Math.product_tree(10, 20, 30, 40)       # Product tree structure

INHERITS

Inherits methods from:

* Sidef::Object::Object

METHODS

Arithmetic and Statistical Functions

avg

Math.avg(list...)

Returns the arithmetic mean (average) of a list of numbers. The arithmetic mean is calculated as the sum of all values divided by the count of values.

say Math.avg(42, 50, 99, 147)               #=> 84.5
say Math.avg(1, 2, 3, 4, 5)                 #=> 3
say Math.avg(10)                            #=> 10

Mathematical Formula: mean = (Σx_i) / n

Aliases: arithmetic_mean

geometric_mean

Math.geometric_mean(list...)

Returns the geometric mean of a list of numbers. The geometric mean is the n-th root of the product of n numbers, useful for averaging ratios and growth rates.

say Math.geometric_mean(42, 50, 99, 147)    #=> 74.352051512093712...
say Math.geometric_mean(1, 2, 4, 8)         #=> 2.8284271247461903...
say Math.geometric_mean(3, 27)              #=> 9

Mathematical Formula: geometric_mean = (Πx_i)^(1/n)

Use Case: Averaging growth rates, ratios, and proportional data.

harmonic_mean

Math.harmonic_mean(list...)

Returns the harmonic mean of a list of numbers. The harmonic mean is the reciprocal of the arithmetic mean of reciprocals, useful for averaging rates.

say Math.harmonic_mean(42, 50, 99, 147)     #=> 65.883471411109602...
say Math.harmonic_mean(1, 2, 4)             #=> 1.7142857142857142...
say Math.harmonic_mean(60, 40)              #=> 48

Mathematical Formula: harmonic_mean = n / (Σ(1/x_i))

Use Case: Averaging speeds, rates, and ratios (e.g., average speed for a round trip with different speeds in each direction).

max

Math.max(list...)

Returns the largest numerical value from a list of numbers.

say Math.max(42, 17, 99, 5)                 #=> 99
say Math.max(-10, -5, -20)                  #=> -5
say Math.max(3.14159)                       #=> 3.14159

min

Math.min(list...)

Returns the smallest numerical value from a list of numbers.

say Math.min(42, 17, 99, 5)                 #=> 5
say Math.min(-10, -5, -20)                  #=> -20
say Math.min(2.71828)                       #=> 2.71828

sum

Math.sum(list...)

Returns the sum of all numbers in the list. Returns 0 for an empty list.

say Math.sum(1, 2, 3, 4, 5)                 #=> 15
say Math.sum(10, -5, 3.5)                   #=> 8.5
say Math.sum(100)                           #=> 100

prod

Math.prod(list...)

Returns the product of all numbers in the list. Returns 1 for an empty list.

say Math.prod(1, 2, 3, 4, 5)                #=> 120
say Math.prod(2, 3, 5, 7)                   #=> 210
say Math.prod(10, 0.5)                      #=> 5

Aliases: product

Number Theory Functions

gcd

Math.gcd(list...)

Returns the greatest common divisor (GCD) of a list of integers. The GCD is the largest positive integer that divides all given numbers without remainder.

say Math.gcd(12, 18)                        #=> 6
say Math.gcd(48, 18, 24)                    #=> 6
say Math.gcd(100, 75, 50, 25)               #=> 25
say Math.gcd(17, 19)                        #=> 1 (coprime)

Algorithm: Uses Euclid's algorithm for efficiency.

Properties: • gcd(a, 0) = a • gcd(a, b) = gcd(b, a mod b) • gcd is commutative and associative

lcm

Math.lcm(list...)

Returns the least common multiple (LCM) of a list of integers. The LCM is the smallest positive integer that is divisible by all given numbers.

say Math.lcm(4, 6)                          #=> 12
say Math.lcm(3, 4, 5)                       #=> 60
say Math.lcm(12, 18, 24)                    #=> 72
say Math.lcm(7, 5, 3)                       #=> 105

Relationship to GCD: lcm(a, b) = (a × b) / gcd(a, b)

batch_gcd

Math.batch_gcd(list...)

Efficiently computes the batch-GCD of a list of integers using a product tree algorithm. This finds the GCD between all possible pairs of numbers in the list, which is useful for finding common factors across many numbers simultaneously.

say Math.batch_gcd(1909, 2923, 291, 205, 989, 62, 451, 1943, 1079, 2419)

Output:

[1909, 1, 1, 41, 23, 1, 41, 1, 83, 41]

Algorithm: Uses the product tree method from facthacks.cr.yp.to

Complexity: More efficient than pairwise GCD for large lists.

Use Case: Factoring large numbers, finding shared factors in datasets, cryptanalysis of RSA keys with shared factors.

batch_invmod

Math.batch_invmod(array, n)

Efficiently computes the modular multiplicative inverses of a list of integers modulo n. For each element x in the array, finds y such that (x × y) ≡ 1 (mod n).

say Math.batch_invmod([33, 42, 99, 103], 2017)      #=> [489, 1969, 163, 235]
say Math.batch_invmod([3, 5, 7], 11)                #=> [4, 9, 8]

Verification:

(33 × 489) mod 2017 = 1
(42 × 1969) mod 2017 = 1

Algorithm: Uses Algorithm 2.11 (MultipleInversion) from Modern Computer Arithmetic.

Complexity: O(n) with only one modular inversion instead of n separate inversions.

Requirement: Each element must be coprime to n (gcd(x, n) = 1).

chinese

Math.chinese(pairs...)

Solves a system of modular congruences using the Chinese Remainder Theorem (CRT). Given pairs [a_k, m_k], finds x such that x ≡ a_k (mod m_k) for all k.

say Math.chinese([14,643], [254,419], [87,733])     #=> 87041638
say Math.chinese([2,3], [3,5], [2,7])               #=> 23

# Verification for second example:
# 23 mod 3 = 2 ✓
# 23 mod 5 = 3 ✓
# 23 mod 7 = 2 ✓

System of Congruences: x ≡ a₁ (mod m₁) x ≡ a₂ (mod m₂) ... x ≡ aₙ (mod mₙ)

Requirement: The moduli must be pairwise coprime (gcd(mᵢ, mⱼ) = 1 for i ≠ j).

Use Case: Solving simultaneous modular equations, calendar calculations, RSA cryptography optimizations.

gcd_factors

Math.gcd_factors(n, [a, b, ...])

Given a positive integer n and an array of integers, attempts to find non-trivial factors of n by computing gcd(n, array[i]) for each element. This is particularly useful when you have auxiliary information that might share factors with n.

var n = 43 * 43 * 97 * 503          # n = 8717237
var a = [19*43*97, 1, 13*41*43*101]
say Math.gcd_factors(n, a)          #=> [43, 43, 97, 503]

Returns: An array of factors whose product equals n. Note that some factors may be composite (not fully factored into primes).

Use Case: Partial factorization, leveraging side-channel information, batch factoring with shared computation.

remainders

Math.remainders(n, array)

Efficiently computes the remainders of n when divided by each integer in the given array. Uses a product tree algorithm for optimal performance with many divisors.

say Math.remainders(8675309, [11, 13, 17, 19, 23])  #=> [5, 6, 5, 4, 8]
say Math.remainders(100, [3, 5, 7, 11])             #=> [1, 0, 2, 1]

Verification:

8675309 mod 11 = 5
8675309 mod 13 = 6
8675309 mod 17 = 5

Algorithm: Uses product tree method from facthacks.cr.yp.to

Complexity: More efficient than computing each remainder separately for large arrays.

product_tree

Math.product_tree(list...)

Constructs a product tree from a list of integers. A product tree is a hierarchical structure where each level contains products of adjacent pairs from the level below.

say Math.product_tree(10, 20, 30, 40, 50, 60)

Output structure:

[
  [10, 20, 30, 40, 50, 60],      # Level 0: original values
  [200, 1200, 3000],              # Level 1: pairwise products
  [240000, 3000],                 # Level 2: pairwise products
  [720000000]                     # Level 3: final product
]

Algorithm: Based on facthacks.cr.yp.to/product.html

Use Case: Building block for efficient batch operations (batch_gcd, remainders), divide-and-conquer algorithms.

Binary and Generic Algorithms

binary_exp

Math.binary_exp(c, x, n, {|a,b| ... })

Generic implementation of the binary exponentiation algorithm (also known as exponentiation by squaring). Uses the binary expansion of n to efficiently compute a result using a custom binary operation.

# Standard exponentiation: 3^43
say Math.binary_exp(1, 3, 43, {|a,b| a * b })       #=> 2058911320946491

# Repeated addition: 3 × 43
say Math.binary_exp(0, 3, 43, {|a,b| a + b })       #=> 129

# String repetition
say Math.binary_exp("", "Hi", 5, {|a,b| a + b })    #=> "HiHiHiHiHi"

Algorithm: Examines each bit of n from right to left, squaring x and conditionally multiplying the result based on the bit value.

Parameters: • c: Initial accumulator value (identity element) • x: Base value • n: Exponent (must be non-negative integer) • block: Binary operation taking two arguments

Complexity: O(log n) operations instead of O(n)

binsplit

Math.binsplit(block, list...)

Binary splitting algorithm for associative operations. Recursively divides the list in half, applies the operation to each half, then combines results.

var arr = [1, 2, 3, 4, 5, 6, 7, 8]

# Sum using binary splitting
say Math.binsplit({|a,b| a + b }, arr...)           #=> 36

# Product using binary splitting
say Math.binsplit({|a,b| a * b }, arr...)           #=> 40320

# Find maximum using binary splitting
say Math.binsplit({|a,b| a > b ? a : b }, arr...)   #=> 8

Algorithm: Divide-and-conquer approach that can be more cache-friendly and parallelizable than sequential accumulation.

Complexity: O(n) operations with better cache locality than sequential folds.

Use Case: Parallel computation, numerical stability in floating-point arithmetic.

Sequence and Recurrence Functions

linear_rec

Math.linear_rec(ker, init, n)
Math.linear_rec(ker, init, from, to)

Computes terms of a linear recurrence relation. Returns the n-th term, or an array of terms from index 'from' to 'to'.

Definition: A linear recurrence satisfies: a(n) = k₁·a(n-1) + k₂·a(n-2) + ... + kₘ·a(n-m)

where ker = [k₁, k₂, ..., kₘ] is the kernel and init provides initial values.

# Fibonacci: F(n) = F(n-1) + F(n-2)
say Math.linear_rec([1, 1], [0, 1], 0, 10)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

# Triangular numbers: T(n) = 3·T(n-1) - 3·T(n-2) + T(n-3)
say Math.linear_rec([3, -3, 1], [0, 1, 3], 0, 5)
#=> [0, 1, 3, 6, 10, 15]

# Tetrahedral numbers (sum of triangular numbers)
say Math.linear_rec([4, -6, 4, -1], [0, 1, 5, 14], 100000)
#=> 333338333350000

Algorithm: Uses matrix exponentiation for efficient computation: O(m³ log n) where m is the recurrence order.

Examples: • Fibonacci: ker=[1,1], init=[0,1] • Lucas: ker=[1,1], init=[2,1] • Tribonacci: ker=[1,1,1], init=[0,0,1] • Pell: ker=[2,1], init=[0,1]

Aliases: linear_recurrence

linear_recmod

Math.linear_recmod(ker, init, n, m)

Computes the n-th term of a linear recurrence modulo m. This is much more efficient than computing the full term and then taking modulo for large n.

# Fibonacci mod 43: F(43) mod 43
say Math.linear_recmod([1, 1], [0, 1], 43, 43)      #=> 42

# Fibonacci mod 1000
say Math.linear_recmod([1, 1], [0, 1], 100, 1000)   #=> 515

# Lucas numbers mod 97
say Math.linear_recmod([1, 1], [2, 1], 50, 97)      #=> 79

Algorithm: Uses modular matrix exponentiation with matrix powmod operation.

Complexity: O(m³ log n) where m is the recurrence order, with all operations performed modulo m to keep numbers bounded.

Use Case: Computing large Fibonacci/Lucas numbers modulo m, cryptographic applications, periodic sequence analysis.

Aliases: linear_recurrence_mod

linear_recurrence_matrix

Math.linear_recurrence_matrix(ker)

Constructs the companion matrix for a given linear recurrence kernel. This matrix can be exponentiated to efficiently compute recurrence terms.

# Fibonacci companion matrix
var M = Math.linear_recurrence_matrix([1, 1])
say M               #=> Matrix([[0,1], [1,1]])

# Tribonacci companion matrix
var T = Math.linear_recurrence_matrix([1, 1, 1])
say T               #=> Matrix([[0,1,0], [0,0,1], [1,1,1]])

Properties: For kernel [k₁, k₂, ..., kₘ], creates an m×m matrix where: • Upper rows form a shift matrix (identity shifted one position) • Bottom row contains the kernel in reverse order

Use Case: Matrix-based recurrence computation, studying recurrence properties through eigenvalue analysis.

solve_rec_seq

Math.solve_rec_seq(array)

Analyzes a sequence and attempts to find the minimal linear recurrence that generates it. Returns the kernel of the recurrence relation.

# Fibonacci sequence
say Math.solve_rec_seq(30.of { .fibonacci })        #=> [1, 1]

# Square numbers: n²
say Math.solve_rec_seq(30.of { .square })           #=> [3, -3, 1]

# Sum of squares: 1² + 2² + ... + n²
say Math.solve_rec_seq(30.of { .faulhaber(2) })     #=> [4, -6, 4, -1]

# Catalan numbers
say Math.solve_rec_seq(20.of { .catalan })          #=> [4, -6, 2]

Algorithm: Uses the Berlekamp-Massey algorithm to find the shortest linear feedback shift register (LFSR) that generates the sequence.

Complexity: O(n²) where n is the sequence length.

Note: Requires enough terms to uniquely determine the recurrence. For a recurrence of order k, you need at least 2k terms.

Aliases: find_linear_recurrence

solve_seq

Math.solve_seq(array, offset=0)

Finds a polynomial that generates the given sequence. Uses polynomial interpolation to construct the generating function.

# Polynomial for squares: n²
say Math.solve_seq(20.of { .square })               #=> x^2

# Polynomial for sum of squares: n(n+1)(2n+1)/6
say Math.solve_seq(20.of { .faulhaber(2) })         #=> 1/3*x^3 + 1/2*x^2 + 1/6*x

# Polynomial for cubes: n³
say Math.solve_seq(20.of { .cube })                 #=> x^3

With offset (starting index):

# Generate sequence starting at n=10
say Math.solve_seq(20.of { (_+10)**3 })             #=> x^3 + 30*x^2 + 300*x + 1000

# Same sequence with offset=10
say Math.solve_seq(20.of { (_+10)**3 }, 10)         #=> x^3

Use Case: Finding closed-form formulas for sequences, polynomial regression, generating functions.

seq

Math.seq(x, y, z, ..., {|a,n| ... })

Creates an Enumerator that generates an infinite sequence. Initial terms are provided, followed by a block that computes subsequent terms based on the sequence history.

# Prime numbers (starting from 2)
say Math.seq(2, { .last.next_prime }).first(10)
#=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

# Fibonacci numbers
say Math.seq(1, 1, { .last(2).sum }).first(15)
#=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

# Tribonacci numbers
say Math.seq(0, 0, 1, { .last(3).sum }).first(10)
#=> [0, 0, 1, 1, 2, 4, 7, 13, 24, 44]

# Powers of 2
say Math.seq(1, { .last * 2 }).first(10)
#=> [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

Block Parameters: • First parameter: Array reference to the sequence so far • Second parameter: Index of the term being computed

Note: The sequence is lazy-evaluated, computing terms only as needed.

for

Math.for(initial, conditionBlock, nextTermBlock)

Creates an Enumerator that generates a sequence based on an initial value, a condition, and a function for computing the next term. Similar to a for-loop but returns an enumerable sequence.

# Count from 1 to 10
Math.for(1, { _ <= 10 }, { .inc }).each { .say }

# Even numbers from 2 to 20
Math.for(2, { _ <= 20 }, { _ + 2 }).each { .say }

# Powers of 2 up to 1000
Math.for(1, { _ < 1000 }, { _ * 2 }).each { .say }

Infinite sequences (condition = nil):

# First 25 prime numbers
say Math.for(2, nil, { .next_prime }).first(25)

# Fibonacci-like sequence (infinite)
var fib = Math.for([0,1], nil, { [_[1], _[0] + _[1]] })
say fib.map { _[0] }.first(20)

Parameters: • initial: Starting value • conditionBlock: Condition to continue (nil for infinite) • nextTermBlock: Function to compute next term from current

smooth_numbers

Math.smooth_numbers(primes...)

Returns an Enumerator that generates smooth numbers (numbers whose prime factors are all from a given set). These are also known as regular numbers or numbers with restricted prime divisors.

# 7-smooth numbers (Hamming numbers with extra prime)
var a = Math.smooth_numbers(2, 3, 5, 7)
say a.first(30)
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18, 20, ...]

# 5-smooth numbers (regular/Hamming numbers)
var b = Math.smooth_numbers(2, 3, 5)
say b.first(20)
#=> [1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, ...]

# 7-smooth numbers not divisible by 3
var c = Math.smooth_numbers(2, 5, 7)
say c.first(20)

Iteration with conditions:

var smooth = Math.smooth_numbers(2, 3, 5, 7)
smooth.each {|k|
    if (k > 1e5) {
        say k       #=> 100352
        break
    }
}

Algorithm: Uses a priority queue-like approach with multiple streams, one for each prime.

Use Case: Generating Hamming numbers, finding numbers with small prime factors, music theory (just intonation), computer science (optimal hash sizes).

Mapping and Transformation Functions

map

Math.map(value, in_min, in_max, out_min, out_max)

Maps a value from one numeric range to another proportionally. Linear interpolation between ranges.

# Map 25 from range [1,100] to range [1,10]
say Math.map(25, 1, 100, 1, 10)             #=> 3.1818181818...

# Map 5 from [0,10] to [0,100]
say Math.map(5, 0, 10, 0, 100)              #=> 50

# Map 0.5 from [0,1] to [-1,1]
say Math.map(0.5, 0, 1, -1, 1)              #=> 0

Formula: output = (value - in_min) × (out_max - out_min) / (in_max - in_min) + out_min

Use Case: Scaling sensor data, converting between units, normalizing values, graphics programming (coordinate transformations).

range_map

Math.range_map(amount, from, to)

Creates a RangeNumber object representing a value mapped proportionally between two endpoints.

say Math.range_map(10, 2, 5)                #=> RangeNum(2, 5, 3/10)

# Creates a range number representing 30% of [2,5]
# Value = 2 + (5-2) × 3/10 = 2.9

Use Case: Creating proportional values, progress indicators, interpolation objects.

num2percent

Math.num2percent(num, min, max)

Converts a value to a percentage based on its position within a given range.

# Value 25 in range [1,400]
say Math.num2percent(25, 1, 400)            #=> 6.01503759398...

# Value 50 in range [0,100]
say Math.num2percent(50, 0, 100)            #=> 50

# Value 75 in range [50,100]
say Math.num2percent(75, 50, 100)           #=> 50

Formula: percent = ((range_size - distance_from_max) / range_size) × 100

Use Case: Progress calculations, statistical analysis, data normalization.

range_sum

Math.range_sum(from, to, step)

Computes the sum of an arithmetic sequence with optional step size using a closed-form formula instead of iteration.

# Sum of 1+2+3+...+10
say Math.range_sum(1, 10)                   #=> 55

# Sum of arithmetic sequence with step 2: 1+3+5+7+9
say Math.range_sum(1, 10, 2)                #=> 30.25

# Sum of 10+11+12+...+100
say Math.range_sum(10, 100)                 #=> 5005

Formula: sum = (first + last) × count / 2 where count = (last - first) / step + 1

Complexity: O(1) - constant time, regardless of range size.

Use Case: Computing sums without iteration, mathematical series, algorithm analysis.

EXAMPLES

Computing Fibonacci Numbers

# Using linear recurrence
var fib10 = Math.linear_rec([1,1], [0,1], 10)
say fib10                               #=> 55

# Generating Fibonacci sequence
var fibs = Math.linear_rec([1,1], [0,1], 0, 15)
say fibs
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

Finding Common Factors

# Find GCD of multiple numbers
say Math.gcd(48, 180, 120)              #=> 12

# Batch GCD for factoring
var numbers = [143, 221, 323, 391]
say Math.batch_gcd(numbers...)          #=> [11, 13, 17, 17]

Solving Modular Equations

# Chinese Remainder Theorem
# Find x where: x ≡ 2 (mod 3), x ≡ 3 (mod 4), x ≡ 1 (mod 5)
say Math.chinese([2,3], [3,4], [1,5])   #=> 11

Generating Special Sequences

# Prime numbers
var primes = Math.seq(2, { .last.next_prime })
say primes.first(20)

# Perfect squares
var squares = Math.for(1, { _ <= 100 }, { .inc })
say squares.map { .square }.to_a

# Hamming numbers (2,3,5-smooth)
var hamming = Math.smooth_numbers(2, 3, 5)
say hamming.first(20)

Working with Statistical Measures

var data = [23, 45, 67, 89, 12, 34, 56, 78]

say "Min: #{Math.min(data...)}"                     #=> Min: 12
say "Max: #{Math.max(data...)}"                     #=> Max: 89
say "Sum: #{Math.sum(data...)}"                     #=> Sum: 404
say "Arithmetic Mean: #{Math.avg(data...)}"         #=> 50.5
say "Geometric Mean: #{Math.geometric_mean(data...)}"
say "Harmonic Mean: #{Math.harmonic_mean(data...)}"

Sequence Analysis

# Find the recurrence for a sequence
var seq = 20.of { .fibonacci }
var kernel = Math.solve_rec_seq(seq)
say "Kernel: #{kernel}"                             #=> [1, 1]

# Verify by regenerating
var regenerated = Math.linear_rec(kernel, [0,1], 0, 19)
say (seq == regenerated)                            #=> true

MATHEMATICAL BACKGROUND

Linear Recurrence Relations

A linear recurrence relation of order m has the form:

a(n) = c₁·a(n-1) + c₂·a(n-2) + ... + cₘ·a(n-m)

The coefficients [c₁, c₂, ..., cₘ] form the recurrence kernel. Famous examples:

Fibonacci (order 2): F(n) = F(n-1) + F(n-2), kernel = [1, 1]

Tribonacci (order 3): T(n) = T(n-1) + T(n-2) + T(n-3), kernel = [1, 1, 1]

Lucas (order 2): L(n) = L(n-1) + L(n-2), kernel = [1, 1], init = [2, 1]

The order of a polynomial sequence of degree d is d+1: • Linear (degree 1): order 2, kernel = [2, -1] • Quadratic (degree 2): order 3, kernel = [3, -3, 1] • Cubic (degree 3): order 4, kernel = [4, -6, 4, -1]

Chinese Remainder Theorem

The CRT states that if m₁, m₂, ..., mₙ are pairwise coprime, then the system:

x ≡ a₁ (mod m₁)
x ≡ a₂ (mod m₂)
...
x ≡ aₙ (mod mₙ)

has a unique solution modulo M = m₁ × m₂ × ... × mₙ.

Smooth Numbers

A number is k-smooth (or k-friable) if all its prime factors are ≤ k. Special cases:

5-smooth numbers (Hamming/regular numbers): Only factors 2, 3, 5 • Used in Babylonian mathematics • Appear in music theory (just intonation) • Sequence: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, ...

7-smooth numbers: Factors from {2, 3, 5, 7}

Batch Algorithms

Batch algorithms process multiple inputs simultaneously, often more efficiently than processing each individually:

batch_gcd: Finds GCD between all pairs using a product tree in O(n log² n) instead of O(n²) pairwise comparisons.

batch_invmod: Computes n modular inverses using only one inversion plus O(n) multiplications, instead of n separate inversions.

PERFORMANCE NOTES

  • Linear recurrence using matrix exponentiation is O(m³ log n) where m is the order and n is the term index. This is vastly faster than naive iteration for large n.

  • Batch algorithms (batch_gcd, batch_invmod, remainders) are asymptotically faster for large inputs compared to individual operations.

  • Product trees enable efficient divide-and-conquer for many number-theoretic operations.

  • The seq() and for() functions return lazy enumerators that compute values on-demand, enabling work with infinite sequences.

  • Binary exponentiation reduces O(n) operations to O(log n).

SEE ALSO

REFERENCES

  • Product trees and batch algorithms: https://facthacks.cr.yp.to/

  • Chinese Remainder Theorem: Hardy & Wright, "An Introduction to the Theory of Numbers"

  • Linear recurrences: Fieker & Hofmann, "Fast Computation of Linear Generators"

  • Berlekamp-Massey algorithm: Massey, "Shift-Register Synthesis and BCH Decoding"

AUTHOR

Daniel "Trizen" Șuteu