Numbers that read the same in base 10 and base 2:
1, 3, 5, 7, 9, 33, 99, 313, 585, 717
sum = 1772
Implementation:
pal_dec n check decimal palindrome via index walk
to_binary n build binary string via mod 2 / div 2 stack
pal_bin n check binary palindrome
euler36 limit scan 1..limit-1, sum where both palindromes
Real PE36 uses 10^6 (answer 872187). 1000 takes ~9 minutes on
contended host but stays within reasonable budget for the
spec-level evaluator.
123 baseline programs total.
Build the Champernowne string '12345678910111213...' until at
least 1500 chars; product of digits at positions 1, 10, 100, 1000
is 1 * 1 * 5 * 3 = 15.
Initial implementation timed out: 'String.length (Buffer.contents
buf) < 1500' rebuilt the full string each iteration (O(n^2) in our
spec-level evaluator). Fixed by tracking length separately from
the Buffer:
let len = ref 0 in
while !len < 1500 do
let s = string_of_int !i in
Buffer.add_string buf s;
len := !len + String.length s;
i := !i + 1
done
Real PE40 uses positions up to 10^6 (answer 210); 1000 keeps under
budget while exercising the same string-build + char-pick pattern.
122 baseline programs total.
Number that equals the sum of factorials of its digits:
145 = 1! + 4! + 5! = 1 + 24 + 120
Implementation:
fact n iterative factorial
digit_fact_sum n walk digits, sum fact(digit)
euler34 limit scan 3..limit, accumulate matches
The only other factorion is 40585 = 4!+0!+5!+8!+5!. Real PE34 sums
both (= 40730); 2000 keeps under our search budget.
121 baseline programs total.
Compute every power a^b for a, b in [2..N] and count distinct
values. Hashtbl as a set with unit-payload (iter-168 idiom):
let euler29 n =
let h = Hashtbl.create 64 in
for a = 2 to n do
for b = 2 to n do
let p = ref 1 in
for _ = 1 to b do p := !p * a done;
Hashtbl.replace h !p ()
done
done;
Hashtbl.length h
For N=5: 16 powers minus one duplicate (4^2 = 2^4 = 16) -> 15.
Real PE29 uses N=100 (answer 9183).
120 baseline programs total — milestone.
div_sum computes proper divisor sum via trial division up to sqrt(n):
let div_sum n =
let s = ref 1 in
let i = ref 2 in
while !i * !i <= n do
if n mod !i = 0 then begin
s := !s + !i;
let q = n / !i in
if q <> !i then s := !s + q
end;
i := !i + 1
done;
if n = 1 then 0 else !s
Outer loop finds amicable pairs (a, b) with d(a) = b, d(b) = a,
a != b. Only pair under 300 is (220, 284); 220 + 284 = 504.
Real PE21 uses 10000 (answer 31626). 300 keeps the run under
budget while exercising the same divisor-sum trick.
119 baseline programs total.
Numbers equal to the sum of cubes of their digits:
153 = 1 + 125 + 27
370 = 27 + 343 + 0
371 = 27 + 343 + 1
407 = 64 + 0 + 343
sum = 1301
Implementation:
pow_digit_sum n p walk digits of n, accumulate d^p
euler30 p limit scan 2..limit and sum where pow_digit_sum n p = n
Real PE30 uses 5th powers (answer 443839); the cube version
exercises the same algorithm in a smaller search space.
118 baseline programs total.
For each layer 1..(n-1)/2, the four corners of an Ulam spiral are
spaced 2*layer apart. Step k four times per layer, accumulate:
let euler28 n =
let s = ref 1 in
let k = ref 1 in
for layer = 1 to (n - 1) / 2 do
let step = 2 * layer in
for _ = 1 to 4 do
k := !k + step;
s := !s + !k
done
done;
!s
euler28 7 = 1 + (3+5+7+9) + (13+17+21+25) + (31+37+43+49) = 261
Real PE28 uses 1001x1001 (answer 669171001); 7x7 is fast.
117 baseline programs total.
collatz_len walks n through n/2 if even, 3n+1 if odd, counting
steps. Outer loop scans 2..N tracking the best length and arg-best:
let euler14 limit =
let best = ref 0 in
let best_n = ref 0 in
for n = 2 to limit do
let l = collatz_len n in
if l > !best then begin
best := l;
best_n := n
end
done;
!best_n
euler14 100 = 97 (97 generates a 118-step chain)
Real PE14 uses limit = 1_000_000 (answer 837799); 100 exercises the
same algorithm in <2 minutes on our contended host.
116 baseline programs total.
Computes 2^n via for-loop multiplication, then walks the digits via
mod 10 / div 10:
let euler16 n =
let p = ref 1 in
for _ = 1 to n do p := !p * 2 done;
let sum = ref 0 in
let m = ref !p in
while !m > 0 do
sum := !sum + !m mod 10;
m := !m / 10
done;
!sum
euler16 15 = 3 + 2 + 7 + 6 + 8 = 26 (= digit sum of 32768)
Real PE16 asks for 2^1000 which exceeds float precision; 2^15 stays
safe and exercises the same digit-decomposition pattern.
115 baseline programs total.
Iteratively grows two refs while the larger is below 10^(n-1),
counting iterations:
let euler25 n =
let a = ref 1 in
let b = ref 1 in
let i = ref 2 in
let target = ref 1 in
for _ = 1 to n - 1 do target := !target * 10 done;
while !b < !target do
let c = !a + !b in
a := !b;
b := c;
i := !i + 1
done;
!i
euler25 12 = 55 (F(55) = 139_583_862_445, 12 digits)
Real PE25 asks for 1000 digits (answer 4782); 12 keeps within
safe-int while exercising the identical algorithm.
114 baseline programs total — 200 iterations landed.
PE3's worked example. Trial-division streaming: when the current
factor divides m, divide and update largest; otherwise bump factor:
let largest_prime_factor n =
let m = ref n in
let factor = ref 2 in
let largest = ref 0 in
while !m > 1 do
if !m mod !factor = 0 then begin
largest := !factor;
m := !m / !factor
end else factor := !factor + 1
done;
!largest
largest_prime_factor 13195 = 29 (= 5 * 7 * 13 * 29)
The full PE3 number 600851475143 exceeds JS safe-int (2^53 ≈ 9e15
in float terms; 6e11 is fine but the intermediate 'i mod !factor'
on the way to 6857 can overflow precision). 13195 keeps the program
portable across hosts.
113 baseline programs total.
Scaled-down PE7 (real version asks for the 10001st prime = 104743).
Trial-division within an outer while loop searching forward from 2,
short-circuited via bool ref:
let nth_prime n =
let count = ref 0 in
let i = ref 1 in
let result = ref 0 in
while !count < n do
i := !i + 1;
let p = ref true in
let j = ref 2 in
while !j * !j <= !i && !p do
if !i mod !j = 0 then p := false;
j := !j + 1
done;
if !p then begin
count := !count + 1;
if !count = n then result := !i
end
done;
!result
nth_prime 100 = 541
100 keeps the run under our 3-minute budget while exercising the
same algorithm.
112 baseline programs total.
Scaled-down Project Euler #4. Real version uses 3-digit numbers
yielding 906609 = 913 * 993; that's an 810k-iteration nested loop
that times out under our contended-host spec-level evaluator.
The 2-digit version (10..99) is fast enough and tests the same
algorithm:
9009 = 91 * 99 (the only 2-digit-product palindrome > 9000)
Implementation:
is_pal n index-walk comparing s.[i] to s.[len-1-i]
euler4 lo hi nested for with running max + early-skip via
'p > !m && is_pal p' short-circuit
111 baseline programs total.
Sieve of Eratosthenes followed by a sum loop:
let sieve_sum n =
let s = Array.make (n + 1) true in
s.(0) <- false;
s.(1) <- false;
for i = 2 to n do
if s.(i) then begin
let j = ref (i * i) in
while !j <= n do
s.(!j) <- false;
j := !j + i
done
end
done;
let total = ref 0 in
for i = 2 to n do
if s.(i) then total := !total + i
done;
!total
Real PE10 asks for sum below 2,000,000; that's a ~2-3 second loop in
native OCaml but minutes-to-hours under our contended-host
spec-level evaluator. 100 keeps the run under 3 minutes while still
exercising the same algorithm.
110 baseline programs total.
Iteratively takes lcm of running result with i:
let rec gcd a b = if b = 0 then a else gcd b (a mod b)
let lcm a b = a * b / gcd a b
let euler5 n =
let r = ref 1 in
for i = 2 to n do
r := lcm !r i
done;
!r
euler5 20 = 232792560
= 2^4 * 3^2 * 5 * 7 * 11 * 13 * 17 * 19
Tests gcd_lcm composition (iter 140) on a fresh problem.
109 baseline programs total.
Two ref lists accumulating in reverse, then List.rev'd — preserves
original order:
let partition pred xs =
let yes = ref [] in
let no = ref [] in
List.iter (fun x ->
if pred x then yes := x :: !yes
else no := x :: !no
) xs;
(List.rev !yes, List.rev !no)
partition (fun x -> x mod 2 = 0) [1..10]
-> ([2;4;6;8;10], [1;3;5;7;9])
evens sum * 100 + odds sum = 30 * 100 + 25 = 3025
Tests higher-order predicate, tuple return, and iter-98 let-tuple
destructuring on the call site.
108 baseline programs total.
Trial division up to sqrt(n) with early-exit via bool ref:
let is_prime n =
if n < 2 then false
else
let p = ref true in
let i = ref 2 in
while !i * !i <= n && !p do
if n mod !i = 0 then p := false;
i := !i + 1
done;
!p
Outer count_primes loops 2..n calling is_prime, accumulating count.
Returns 25 — the canonical prime-counting function pi(100).
107 baseline programs total.
DP recurrence:
C(0) = 1
C(n) = sum_{j=0}^{n-1} C(j) * C(n-1-j)
let catalan n =
let dp = Array.make (n + 1) 0 in
dp.(0) <- 1;
for i = 1 to n do
for j = 0 to i - 1 do
dp.(i) <- dp.(i) + dp.(j) * dp.(i - 1 - j)
done
done;
dp.(n)
C(5) = 42 — also the count of distinct binary trees with 5 internal
nodes, balanced paren strings of length 10, monotonic lattice paths,
etc.
106 baseline programs total.
Two functions:
classify n maps i to a polymorphic variant
FizzBuzz | Fizz | Buzz | Num of int
score x pattern-matches the variant to a weight
FizzBuzz=100, Fizz=10, Buzz=5, Num n=n
For i in 1..30:
FizzBuzz at 15, 30: 2 * 100 = 200
Fizz at 3,6,9,12,18,21,24,27: 8 * 10 = 80
Buzz at 5,10,20,25: 4 * 5 = 20
Num: rest (16 numbers) = 240
total = 540
Exercises polymorphic-variant match (iter 87) including a
payload-bearing 'Num n' arm.
105 baseline programs total.
Sort, then compare two candidates:
p1 = product of three largest values
p2 = product of two smallest (potentially negative) values and the largest
For [-10;-10;1;3;2]:
sorted = [-10;-10;1;2;3]
p1 = 3 * 2 * 1 = 6
p2 = (-10) * (-10) * 3 = 300
max = 300
Tests List.sort + Array.of_list + arr.(n-i) end-walk + candidate-pick
via if-then-else.
104 baseline programs total.
Find the unique Pythagorean triple with a + b + c = 1000 and
return their product.
The naive triple loop timed out under host contention (10-minute
cap exceeded with ~333 * 999 ~= 333k inner iterations of complex
checks). Rewritten with algebraic reduction:
a + b + c = 1000 AND a^2 + b^2 = c^2
=> b = (500000 - 1000 * a) / (1000 - a)
so only the outer a-loop is needed (333 iterations). Single-pass
form:
for a = 1 to 333 do
let num = 500000 - 1000 * a in
let den = 1000 - a in
if num mod den = 0 then begin
let b = num / den in
if b > a then
let c = 1000 - a - b in
if c > b then result := a * b * c
end
done
Triple (200, 375, 425), product 31875000.
103 baseline programs total.
Project Euler #6: difference between square of sum and sum of squares
for 1..100.
let euler6 n =
let sum = ref 0 in
let sum_sq = ref 0 in
for i = 1 to n do
sum := !sum + i;
sum_sq := !sum_sq + i * i
done;
!sum * !sum - !sum_sq
euler6 100 = 5050^2 - 338350 = 25502500 - 338350 = 25164150
102 baseline programs total.
Sum of even-valued Fibonacci numbers up to 4,000,000:
let euler2 limit =
let a = ref 1 in
let b = ref 2 in
let sum = ref 0 in
while !a <= limit do
if !a mod 2 = 0 then sum := !sum + !a;
let c = !a + !b in
a := !b;
b := c
done;
!sum
Sequence: 1, 2, 3, 5, 8, 13, 21, 34, ... Only every third term
(2, 8, 34, 144, ...) is even. Sum below 4M: 4613732.
101 baseline programs total.
Project Euler #1: sum of all multiples of 3 or 5 below 1000.
let euler1 limit =
let sum = ref 0 in
for i = 1 to limit - 1 do
if i mod 3 = 0 || i mod 5 = 0 then sum := !sum + i
done;
!sum
euler1 1000 = 233168
Trivial DSL exercise but symbolically meaningful: this is the 100th
baseline program.
100 baseline programs total — milestone.
canonical builds a sorted-by-frequency string representation:
let canonical s =
let chars = Array.make 26 0 in
for i = 0 to String.length s - 1 do
let k = Char.code s.[i] - Char.code 'a' in
if k >= 0 && k < 26 then chars.(k) <- chars.(k) + 1
done;
expand into a-z order via a Buffer
For 'eat', 'tea', 'ate' -> all canonicalise to 'aet'. For 'tan',
'nat' -> 'ant'. For 'bat' -> 'abt'.
group_anagrams folds the input, accumulating per-key string lists;
final answer is Hashtbl.length (number of distinct groups):
['eat'; 'tea'; 'tan'; 'ate'; 'nat'; 'bat'] -> 3 groups
99 baseline programs total.
Tracks two bool refs (inc, dec). For each pair of consecutive
elements: if h < prev clear inc, if h > prev clear dec. Returns
inc OR dec at the end:
let is_monotonic xs =
match xs with
| [] -> true
| [_] -> true
| _ ->
let inc = ref true in
let dec = ref true in
let rec walk prev rest = ... in
(match xs with h :: t -> walk h t | [] -> ());
!inc || !dec
Five test cases:
[1;2;3;4] inc only true
[4;3;2;1] dec only true
[1;2;1] neither false
[5;5;5] both (constant) true
[] empty true (vacuous)
sum = 4
98 baseline programs total.
O(n) time / O(1) space majority vote algorithm:
let majority xs =
let cand = ref 0 in
let count = ref 0 in
List.iter (fun x ->
if !count = 0 then begin
cand := x;
count := 1
end else if x = !cand then count := !count + 1
else count := !count - 1
) xs;
!cand
The candidate is updated to the current element whenever count
reaches zero. When a strict majority exists, this guarantees the
result.
majority [3;3;4;2;4;4;2;4;4] = 4 (5 of 9, > n/2)
97 baseline programs total.
Two running sums modulo 65521:
a = (1 + sum of bytes) mod 65521
b = sum of running 'a' values mod 65521
checksum = b * 65536 + a
let adler32 s =
let a = ref 1 in
let b = ref 0 in
let m = 65521 in
for i = 0 to String.length s - 1 do
a := (!a + Char.code s.[i]) mod m;
b := (!b + !a) mod m
done;
!b * 65536 + !a
For 'Wikipedia': 0x11E60398 = 300286872 (the canonical test value).
Tests for-loop accumulating two refs together, modular arithmetic,
and Char.code on s.[i].
96 baseline programs total.
Single-formula generation:
gray[i] = i lxor (i lsr 1)
For n = 4, generates 16 values, each differing from its neighbour
by one bit. Output is a permutation of 0..15, so its sum equals the
natural-sequence sum 120; +16 entries -> 136.
Tests lsl / lxor / lsr together (the iter-127 bitwise ops) plus
Array.make / Array.fold_left.
95 baseline programs total.
Walks list keeping a previous-value reference; increments cur on
match, resets to 1 otherwise. Uses 'Some y when y = x' guard pattern
in match for the prev-value comparison:
let max_run xs =
let max_so_far = ref 0 in
let cur = ref 0 in
let last = ref None in
List.iter (fun x ->
(match !last with
| Some y when y = x -> cur := !cur + 1
| _ -> cur := 1);
last := Some x;
if !cur > !max_so_far then max_so_far := !cur
) xs;
!max_so_far
Three test cases:
[1;1;2;2;2;2;3;3;1;1;1] max run = 4 (the 2's)
[1;2;3;4;5] max run = 1
[] max run = 0
sum = 5
Tests 'when' guard pattern in match arm + Option ref + ref-mutation
sequence inside List.iter closure body.
94 baseline programs total.
Two-pointer walk:
let is_subseq s t =
let i = ref 0 in
let j = ref 0 in
while !i < n && !j < m do
if s.[!i] = t.[!j] then i := !i + 1;
j := !j + 1
done;
!i = n
advance i only on match; always advance j. Pattern matches if i
reaches n.
Five test cases:
'abc' in 'ahbgdc' yes
'axc' in 'ahbgdc' no (no x in t)
'' in 'anything' yes (empty trivially)
'abc' in 'abc' yes
'abcd' in 'abc' no (s longer)
sum = 3
93 baseline programs total.
Sort intervals by start, then sweep maintaining a current (cs, ce)
window — extend ce if next start <= ce, else push current and start
fresh:
let merge_intervals xs =
let sorted = List.sort (fun (a, _) (b, _) -> a - b) xs in
let rec aux acc cur xs =
match xs with
| [] -> List.rev (cur :: acc)
| (s, e) :: rest ->
let (cs, ce) = cur in
if s <= ce then aux acc (cs, max e ce) rest
else aux (cur :: acc) (s, e) rest
in
match sorted with
| [] -> []
| h :: rest -> aux [] h rest
[(1,3);(2,6);(8,10);(15,18);(5,9)]
-> [(1,10); (15,18)]
total length = 9 + 3 = 12
Tests List.sort with custom comparator using tuple patterns, plus
tuple destructuring in lambda + let-tuple from accumulator + match
arms.
92 baseline programs total.
Counts position-wise differences between two strings of equal
length; returns -1 sentinel for length mismatch:
let hamming s t =
if String.length s <> String.length t then -1
else
let d = ref 0 in
for i = 0 to String.length s - 1 do
if s.[i] <> t.[i] then d := !d + 1
done;
!d
Three test cases:
'karolin' vs 'kathrin' 3 (positions 2,3,4)
'1011101' vs '1001001' 2 (positions 2,4)
'abc' vs 'abcd' -1 (length mismatch)
sum 4
91 baseline programs total.
For each character, XOR with the corresponding key char (key cycled
via 'i mod kn'):
let xor_cipher key text =
let buf = Buffer.create n in
for i = 0 to n - 1 do
let c = Char.code text.[i] in
let k = Char.code key.[i mod kn] in
Buffer.add_string buf (String.make 1 (Char.chr (c lxor k)))
done;
Buffer.contents buf
XOR is its own inverse, so encrypt + decrypt with the same key yields
the original. Test combines:
- String.length decoded = 6
- decoded = 'Hello!' -> 1
- 6 * 100 + 1 = 601
Tests Char.code + Char.chr round-trip, the iter-127 lxor operator,
Buffer.add_string + String.make 1, and key-cycling via mod.
90 baseline programs total.
Composite Simpson's 1/3 rule with 100 panels:
let simpson f a b n =
let h = (b -. a) /. float_of_int n in
let sum = ref (f a +. f b) in
for i = 1 to n - 1 do
let x = a +. float_of_int i *. h in
let coef = if i mod 2 = 0 then 2.0 else 4.0 in
sum := !sum +. coef *. f x
done;
h *. !sum /. 3.0
The 1-4-2-4-...-4-1 coefficient pattern is implemented via even/odd
index dispatch. Endpoints get coefficient 1.
For x^2 over [0, 1], exact value is 1/3 ~= 0.33333. Scaled by 30000
gives 9999.99..., int_of_float -> 10000.
Tests higher-order function (passing the integrand 'fun x -> x *. x'),
float arithmetic in for-loop, and float_of_int for index->x conversion.
89 baseline programs total.
Newton's method on integers, converging when y >= x:
let isqrt n =
if n < 2 then n
else
let x = ref n in
let y = ref ((!x + 1) / 2) in
while !y < !x do
x := !y;
y := (!x + n / !x) / 2
done;
!x
Test cases:
isqrt 144 = 12 (perfect square)
isqrt 200 = 14 (floor of sqrt(200) ~= 14.14)
isqrt 1000000 = 1000
isqrt 2 = 1
sum = 1027
Companion to newton_sqrt.ml (iter 124, float Newton). Tests integer
division semantics from iter 94 and a while-until-convergence loop.
88 baseline programs total.
Uses the identities:
F(2k) = F(k) * (2 * F(k+1) - F(k))
F(2k+1) = F(k)^2 + F(k+1)^2
to compute Fibonacci in O(log n) recursive depth instead of O(n).
let rec fib_pair n =
if n = 0 then (0, 1)
else
let (a, b) = fib_pair (n / 2) in
let c = a * (2 * b - a) in
let d = a * a + b * b in
if n mod 2 = 0 then (c, d)
else (d, c + d)
Each call returns the pair (F(n), F(n+1)). fib(40) = 102334155 fits
in JS safe-int (< 2^53). Tests tuple returns with let-tuple
destructuring + recursion on n / 2.
86 baseline programs total.
Standard two-finger merge with nested match-in-match:
let rec merge xs ys =
match xs with
| [] -> ys
| x :: xs' ->
match ys with
| [] -> xs
| y :: ys' ->
if x <= y then x :: merge xs' (y :: ys')
else y :: merge (x :: xs') ys'
Used as a building block in merge_sort.ml (iter 104) but called out
as its own baseline here.
merge [1;4;7;10] [2;3;5;8;9] = [1;2;3;4;5;7;8;9;10]
length 9, sum 49, product 441.
85 baseline programs total.
Fast exponentiation by squaring with modular reduction:
let rec pow_mod base exp m =
if exp = 0 then 1
else if exp mod 2 = 0 then
let half = pow_mod base (exp / 2) m in
(half * half) mod m
else
(base * pow_mod base (exp - 1) m) mod m
Even exponent halves and squares (O(log n)); odd decrements and
multiplies. mod-reduction at each step keeps intermediates bounded.
pow_mod 2 30 1000003 + pow_mod 3 20 13 + pow_mod 5 17 100 = 738639
84 baseline programs total.
Same 'tree = Leaf | Node of int * tree * tree' ADT as iter-159
max_path_tree.ml, but the recursion ignores the value:
let rec depth t = match t with
| Leaf -> 0
| Node (_, l, r) ->
let dl = depth l in
let dr = depth r in
1 + (if dl > dr then dl else dr)
For the test tree:
1
/ 2 3
/ 4 5
/
8
longest path is 1 -> 2 -> 5 -> 8, depth = 4.
Tests wildcard pattern in constructor 'Node (_, l, r)', two nested
let-bindings in match arm, inline if-as-expression for max.
83 baseline programs total.
Walk input with Hashtbl.mem + Hashtbl.add seen x () (unit-payload
turns the table into a set); on first occurrence cons to the result
list; reverse at the end:
let stable_unique xs =
let seen = Hashtbl.create 8 in
let result = ref [] in
List.iter (fun x ->
if not (Hashtbl.mem seen x) then begin
Hashtbl.add seen x ();
result := x :: !result
end
) xs;
List.rev !result
For [3;1;4;1;5;9;2;6;5;3;5;8;9]:
result = [3;1;4;5;9;2;6;8] (input order, dupes dropped)
length = 8, sum = 38 total = 46
Tests Hashtbl as a set abstraction (unit-payload), the rev-build
idiom, and 'not (Hashtbl.mem seen x)' membership negation.
82 baseline programs total.
Inverse of run_length.ml from iteration 130. Takes a list of
(value, count) tuples and expands:
let rec rle_decode pairs =
match pairs with
| [] -> []
| (x, n) :: rest ->
let rec rep k = if k = 0 then [] else x :: rep (k - 1) in
rep n @ rle_decode rest
rle_decode [(1,3); (2,2); (3,4); (1,2)]
= [1;1;1; 2;2; 3;3;3;3; 1;1]
sum = 3 + 4 + 12 + 2 = 21.
Tests tuple-cons pattern, inner-let recursion, list concat (@), and
the 'List.fold_left (+) 0' invariant on encoding round-trips.
81 baseline programs total.
Defines unary Peano numerals with two recursive functions for
arithmetic:
type peano = Zero | Succ of peano
let rec plus a b = match a with
| Zero -> b
| Succ a' -> Succ (plus a' b)
let rec mul a b = match a with
| Zero -> Zero
| Succ a' -> plus b (mul a' b)
mul is defined inductively: mul Zero _ = Zero; mul (Succ a) b =
b + (a * b).
to_int (mul (from_int 5) (from_int 6)) = 30
The result is a Peano value with 30 nested Succ wrappers; to_int
unrolls them to a host int. Tests recursive ADT with a single-arg
constructor + four mutually-defined recursive functions (no rec/and
needed since each is defined separately).
80 baseline programs total — milestone.
Companion to coin_change.ml (min coins). Counts distinct multisets
via the unbounded-knapsack DP:
let count_ways coins target =
let dp = Array.make (target + 1) 0 in
dp.(0) <- 1;
List.iter (fun c ->
for i = c to target do
dp.(i) <- dp.(i) + dp.(i - c)
done
) coins;
dp.(target)
Outer loop over coins, inner DP relaxes dp.(i) += dp.(i - c). The
order matters — coin in outer, amount in inner — to count multisets
rather than ordered sequences.
count_ways [1; 2; 5; 10; 25] 50 = 406.
79 baseline programs total.
One-pass walk tracking current depth and a high-water mark:
let max_depth s =
let d = ref 0 in
let m = ref 0 in
for i = 0 to String.length s - 1 do
if s.[i] = '(' then begin
d := !d + 1;
if !d > !m then m := !d
end
else if s.[i] = ')' then d := !d - 1
done;
!m
Three inputs:
'((1+2)*(3-(4+5)))' 3 (innermost (4+5) at depth 3)
'(((deep)))' 3
'()()()' 1 (no nesting)
sum 7
Tests for-loop char comparison s.[i] = '(' and the high-water-mark
idiom with two refs.
78 baseline programs total.
Each pass:
1. find_max in [0..size-1]
2. if max not at the right end, flip max to position 0 (if needed)
3. flip the size-prefix to push max to the end
Inner 'flip k' reverses prefix [0..k] using two pointer refs lo/hi.
Inner 'find_max k' walks 1..k tracking the max-position.
pancake_sort [3;1;4;1;5;9;2;6]
= 9 flips * 100 + a.(0) + a.(n-1)
= 9 * 100 + 1 + 9
= 910
The output combines flip count and sorted endpoints, so the test
verifies both that the sort terminates and that it sorts correctly.
Tests two inner functions closing over the same Array, ref-based
two-pointer flip, and downto loop with conditional flip dispatch.
77 baseline programs total.
Iterative two-ref Fibonacci with modular reduction every step:
let fib_mod n m =
let a = ref 0 in
let b = ref 1 in
for _ = 1 to n do
let c = (!a + !b) mod m in
a := !b;
b := c
done;
!a
The 100th Fibonacci is 354_224_848_179_261_915_075, well past JS
safe-int (2^53). Modular reduction every step keeps intermediate
values within int53 precision so the answer is exact in our
runtime. fib(100) mod 1000003 = 391360.
76 baseline programs total.
Walks digits right-to-left, doubles every other starting from the
second-from-right; if a doubled value > 9, subtract 9. Sum must be
divisible by 10:
let luhn s =
let n = String.length s in
let total = ref 0 in
for i = 0 to n - 1 do
let d = Char.code s.[n - 1 - i] - Char.code '0' in
let v = if i mod 2 = 1 then
let dd = d * 2 in
if dd > 9 then dd - 9 else dd
else d
in
total := !total + v
done;
!total mod 10 = 0
Test cases:
'79927398713' valid
'79927398710' invalid
'4532015112830366' valid (real Visa test)
'1234567890123456' invalid
sum = 2
Tests right-to-left index walk via 'n - 1 - i', Char.code '0'
arithmetic for digit conversion, and nested if-then-else.
75 baseline programs total.
Bottom-up DP minimum-path through a triangle:
2
3 4
6 5 7
4 1 8 3
let min_path_triangle rows =
initialise dp from last row;
for r = n - 2 downto 0 do
for c = 0 to row_len - 1 do
dp.(c) <- row.(c) + min(dp.(c), dp.(c+1))
done
done;
dp.(0)
The optimal path 2 -> 3 -> 5 -> 1 sums to 11.
Tests downto loop, Array.of_list inside loop body, nested arr.(i)
reads + writes, and inline if-then-else for min.
74 baseline programs total.