1-- Binomial coefficients by Pascal's rule, and their factorial form2-- Part of Cumulus, the Grace17 mathematics library.3-- Copyright (c) 2026 Grant Passmore.4-- Released under the Apache License 2.0. See LICENSE in the repository root.5-- Authors: Grant Passmore67module Grace.Cumulus.NumberTheory.Binomial8import Nat910-- The row n = 0 of Pascal's triangle: 1 at k = 0, then 0.11def chooseZero (k : Nat) →Nat12| zero =>113| succ(k) =>01415-- Pascal's rule at column k, given the two entries above it: the entry at16-- column 0 is 1, and the entry at column succ(j) is the sum of the entries at17-- columns j and succ(j) of the row above.18def pascalStep (k above left : Nat) →Nat19| zero, above, left=>120| succ(j), above, left=> add(left, above)2122-- The number of k-element subsets of an n-element set, by Pascal's rule:23-- choose(n + 1, k + 1) = choose(n, k) + choose(n, k + 1).24def choose (n k : Nat) →Nat25| zero, k => chooseZero(k)26| succ(n), k => pascalStep(k, choose(n, k), choose(n, predecessor(k)))27descends n2829-- The binomial coefficient is written n choose k; it binds more loosely30-- than +, so 2 * n + 1 choose k is the coefficient of 2n + 1.31notationinfixl55"choose"=> choose forNat3233theorem choose.zero_right (n : Nat)34shows choose(n, 0) =135proof36induction n37| zero =>compute38| succ(k) using ih =>settlewith choose, pascalStep3940theorem choose.zero_left (k : Nat)41shows choose(0, succ(k)) =042proof43simp [choose, chooseZero]4445theorem choose.succ_succ (n k : Nat)46shows choose(succ(n), succ(k)) = choose(n, k) + choose(n, succ(k))47proof48simp [choose, pascalStep, predecessor]4950-- Nothing is chosen above the diagonal.51theorem choose.above (n : Nat)52shows∀ k, Nat(k) → choose(n, n + succ(k)) =053proof54induction n55| zero =>56intros k kNat57have shape : 0+ succ(k) = succ(k)58exact add.left_unit at succ(k) with*59rw shape60exact choose.zero_left at k with*61| succ(m) using ih =>62intros k kNat63have shape : succ(m) + succ(k) = succ(m + succ(k)) byring64rw [shape, choose.succ_succ]65have first := ih with k, kNat66have next : succ(m + succ(k)) = m + succ(succ(k)) byring67rw next68have nextNat : Nat(succ(k)) bytypecheck69have second := ih with succ(k), nextNat70rw [first, second]71compute7273theorem choose.diagonal (n : Nat)74shows choose(n, n) =175proof76induction n77| zero =>compute78| succ(k) using ih =>79rw [choose.succ_succ, ih]80have zeroNat : Nat(0) bytypecheck81have above := choose.above at k with*82have vanish := above with0, zeroNat83have shape : k + succ(0) = succ(k) byring84rw shape at vanish85rw vanish86compute8788-- One row of the factorial form: from row m to row succ(m), by induction on89-- the column.90theorem choose.factorial_row (m k : Nat)91requires row : ∀ i, Nat(i) → choose(m + i, i) * (i! * m!) = (m + i)!92shows choose(succ(m) + k, k) * (k! * succ(m)!) = (succ(m) + k)!93proof94induction k95| zero =>96have shape : succ(m) +0= succ(m) byring97rw shape98rw choose.zero_right99have zero : 0! =1bycompute100rw zero101ring102| succ(j) using inner =>103have shape : succ(m) + succ(j) = succ(succ(m) + j) byring104rw shape105rw choose.succ_succ106have swap : succ(m) + j = m + succ(j) byring107haveright:= row with succ(j), assumption108have factorialSucc : ∀ t, Nat(t) → succ(t)! = succ(t) * t!109intros t tNat110settlewith factorial111have expandJ := factorialSucc with j, assumption112have expandM := factorialSucc with m, assumption113have expandTop := factorialSucc with succ(m) + j, assumption114rw expandTop115rw expandJ116rw expandM117rw expandM at inner118rw expandJ atright119rw← swap atright120have regroup : (choose(succ(m) + j, j) + choose(succ(m) + j, succ(j))) * ((succ(j) * j!) * (succ(m) * m!)) = succ(j) * (choose(succ(m) + j, j) * (j! * (succ(m) * m!))) + succ(m) * (choose(succ(m) + j, succ(j)) * ((succ(j) * j!) * m!))121ring122rw regroup123rw inner124rwright125ring126127-- Pascal's rule and the factorial form agree: choose(n + k, k) · k! · n! = (n + k)!.128theorem choose.factorial (n : Nat)129shows∀ k, Nat(k) → choose(n + k, k) * (k! * n!) = (n + k)!130proof131induction n132| zero =>133intros k kNat134have shape : 0+ k = k135exact add.left_unit at k with*136rw [shape, choose.diagonal]137have zero : 0! =1bycompute138rw zero139ring140| succ(m) using ih =>141intros k kNat142exact choose.factorial_row at m, k with*143