1-- Stirling numbers of the second kind by their triangle recurrence2-- S(n + 1, k + 1) = S(n, k) + (k + 1) · S(n, k + 1): the first and last3-- columns, vanishing above the diagonal, S(n + 1, 2) = 2 ^ n − 1,4-- S(n + 2, n + 1) = (n + 2 choose 2), positivity, and the Bell numbers5-- as row sums6-- Part of Cumulus, the Grace17 mathematics library.7-- Copyright (c) 2026 Grant Passmore.8-- Released under the Apache License 2.0. See LICENSE in the repository root.9-- Authors: Grant Passmore1011module Grace.Cumulus.Combinatorics.Stirling12import BinomialMore1314-- Row 0 of the Stirling triangle: 1 at k = 0, then 0.15def stirlingZero (k : Nat) →Nat16| zero =>117| succ(k) =>01819-- The recurrence at column k from the entries above and above-left: the20-- entry at column 0 is 0, and the entry at column succ(j) is the entry at21-- column j of the row above plus succ(j) times the entry at column succ(j).22def stirlingStep (k above left : Nat) →Nat23| zero, above, left=>024| succ(j), above, left=> add(left, mul(succ(j), above))2526-- The number of partitions of an n-element set into k blocks.27def stirling (n k : Nat) →Nat28| zero, k => stirlingZero(k)29| succ(n), k => stirlingStep(k, stirling(n, k), stirling(n, predecessor(k)))30descends n3132theorem stirling.zero_zero33shows stirling(0, 0) =134proof35compute3637theorem stirling.zero_succ (k : Nat)38shows stirling(0, k +1) =039proof40have hk : k +1= succ(k) := add.right_one with*41rw hk42simp [stirling, stirlingZero]4344theorem stirling.succ_zero (n : Nat)45shows stirling(n +1, 0) =046proof47have hn : n +1= succ(n) := add.right_one with*48rw hn49simp [stirling, stirlingStep]5051theorem stirling.succ_succ (n k : Nat)52shows stirling(n +1, k +1) = stirling(n, k) + (k +1) * stirling(n, k +1)53proof54have hn : n +1= succ(n) := add.right_one with*55have hk : k +1= succ(k) := add.right_one with*56rw [hn, hk]57simp [stirling, stirlingStep, predecessor]5859theorem stirling.natural (n : Nat)60shows∀ k, Nat(k) →Nat(stirling(n, k))61proof62induction n63| zero =>64intros k kNat65havesplit:= natural.zero_or_successor at k66casessplitwith isZero | isSucc67 . rw [isZero, stirling.zero_zero]68typecheck69 . obtain⟨j, jNat, shape⟩:= isSucc70have hj : succ(j) = j +171 symm72exact add.right_one at j73rw [shape, hj, stirling.zero_succ]74typecheck75| m +1using ih =>76intros k kNat77havesplit:= natural.zero_or_successor at k78casessplitwith isZero | isSucc79 . rw [isZero, stirling.succ_zero]80typecheck81 . obtain⟨j, jNat, shape⟩:= isSucc82have hj : succ(j) = j +183 symm84exact add.right_one at j85rw [shape, hj, stirling.succ_succ]86have first := ih with j, jNat87have nextNat : Nat(j +1) bytypecheck88have second := ih with j +1, nextNat89typecheck9091theorem stirling.natural_at (n k : Nat)92showsNat(stirling(n, k))93proof94have all := stirling.natural at n95exact all with k, assumption9697theorem stirling.three_two98shows stirling(3, 2) =399proof100compute101102theorem stirling.four_two103shows stirling(4, 2) =7104proof105compute106107theorem stirling.four_three108shows stirling(4, 3) =6109proof110compute111112theorem stirling.five_two113shows stirling(5, 2) =15114proof115compute116117theorem stirling.five_three118shows stirling(5, 3) =25119proof120compute121122theorem stirling.five_four123shows stirling(5, 4) =10124proof125compute126127-- One block: S(n + 1, 1) = 1.128theorem stirling.one (n : Nat)129shows stirling(n +1, 1) =1130proof131induction n132| zero =>compute133| k +1using ih =>134have step := stirling.succ_succ at k +1, 0135have one : 0+1=1bycompute136rw one at step137rw [step, stirling.succ_zero, ih]138compute139140-- Nothing above the diagonal.141theorem stirling.above (n : Nat)142shows∀ d, Nat(d) → stirling(n, n +1+ d) =0143proof144induction n145| zero =>146intros d dNat147have shape : 0+1+ d = d +1byring148rw shape149exact stirling.zero_succ at d150| k +1using ih =>151intros d dNat152have shape : k +1+1+ d = (k +1+ d) +1byring153rw [shape, stirling.succ_succ]154have first := ih with d, dNat155have nextNat : Nat(d +1) bytypecheck156have second := ih with d +1, nextNat157have shape2 : k +1+ (d +1) = k +1+ d +1byring158rw shape2 at second159rw [first, second]160ring161162theorem stirling.above_diagonal (n k : Nat)163requires below : n < k164shows stirling(n, k) =0165proof166obtain⟨d, dNat, shape⟩:= below167have form : k = n +1+ d168rw← shape169ring170rw form171have all := stirling.above at n172exact all with d, dNat173174-- Singletons: S(n, n) = 1.175theorem stirling.diagonal (n : Nat)176shows stirling(n, n) =1177proof178induction n179| zero =>compute180| k +1using ih =>181rw [stirling.succ_succ, ih]182have below : k < k +1byarith183have vanish := stirling.above_diagonal with below184rw vanish185ring186187-- Two blocks: S(n + 1, 2) = 2 ^ n − 1.188theorem stirling.two (n : Nat)189shows stirling(n +1, 2) +1=2 ^ n190proof191induction n192| zero =>compute193| k +1using ih =>194have step := stirling.succ_succ at k +1, 1195have two : 1+1=2bycompute196rw two at step197rw [step, stirling.one, pow.add_one]198have sNat := stirling.natural_at at k +1, 2199have powNat := pow.closed at2, k200ring [ih]201202-- n + 1 blocks of n + 2 points: S(n + 2, n + 1) = (n + 2 choose 2).203theorem stirling.sub_one (n : Nat)204shows2* stirling(n +2, n +1) = (n +2) * (n +1)205proof206induction n207| zero =>compute208| k +1using ih =>209have shape : k +1+2= (k +2) +1byring210have shape2 : k +1+1= k +2byring211rw [shape, stirling.succ_succ, shape2, stirling.diagonal]212have sNat := stirling.natural_at at k +2, k +1213ring [ih]214215theorem stirling.sub_one_choose (n : Nat)216shows stirling(n +2, n +1) = (n +2) choose 2217proof218have first := stirling.sub_one at n219have second := choose.two at n +1220have shape : n +1+1= n +2byring221rw shape at second222rw← second at first223have sNat := stirling.natural_at at n +2, n +1224have cNat := choose.natural at n +2, 2225have twoPositive : 0 < 2byarith226have two := Less.ne_zero_of_positive with twoPositive227exact mul.left_cancel_positive with two, first228229-- Positivity on and below the diagonal.230theorem stirling.positive (n : Nat)231shows∀ k, Nat(k) → k ≤ n →0 < stirling(n +1, k +1)232proof233induction n234| zero =>235intros k kNat le236havesplit:= natural.zero_or_successor at k237casessplitwith isZero | isSucc238 . rw isZero239have one : 0+1=1bycompute240have value : stirling(1, 1) =1bycompute241rw [one, value]242arith243 . obtain⟨i, iNat, shape⟩:= isSucc244exfalso245rw shape at le246arith247| j +1using ih =>248intros k kNat le249havesplit:= natural.zero_or_successor at k250casessplitwith isZero | isSucc251 . rw isZero252have one : 0+1=1bycompute253rw [one, stirling.one]254arith255 . obtain⟨i, iNat, shape⟩:= isSucc256have hi : succ(i) = i +1257 symm258exact add.right_one at i259rw [shape, hi, stirling.succ_succ]260rw [shape, hi] at le261have earlier : i ≤ j byarith262have inner := ih with i, iNat, earlier263have restNat := stirling.natural_at at j +1, i +1+1264have productNat : Nat((i +1+1) * stirling(j +1, i +1+1))265typecheck266arith267268theorem stirling.positive_at (n k : Nat)269requires le : k ≤ n270shows0 < stirling(n +1, k +1)271proof272have all := stirling.positive at n273exact all with k, assumption, le274275-- Partial row sums of the Stirling triangle.276def bellSum (n k : Nat) →Nat277| n, zero =>0278| n, succ(k) => bellSum(n, k) + stirling(n, k)279descends k280281-- The Bell numbers: the number of partitions of an n-element set.282def bell (n : Nat) : Nat:= bellSum(n, n +1)283284theorem bellSum.zero (n : Nat)285shows bellSum(n, 0) =0286proof287simp [bellSum]288289theorem bellSum.succ (n k : Nat)290shows bellSum(n, k +1) = bellSum(n, k) + stirling(n, k)291proof292have hk : k +1= succ(k) := add.right_one with*293rw hk294simp [bellSum]295296theorem bellSum.natural (n k : Nat)297showsNat(bellSum(n, k))298proof299induction k300| zero =>301rw bellSum.zero302typecheck303| j +1using ih =>304rw bellSum.succ305have sNat := stirling.natural_at at n, j306typecheck307308theorem bell.value (n : Nat)309shows bell(n) = bellSum(n, n +1)310proof311simp [bell]312313theorem bell.natural (n : Nat)314showsNat(bell(n))315proof316rw bell.value317exact bellSum.natural at n, n +1318319theorem bell.zero320shows bell(0) =1321proof322compute323324theorem bell.one325shows bell(1) =1326proof327compute328329theorem bell.two330shows bell(2) =2331proof332compute333334theorem bell.three335shows bell(3) =5336proof337compute338339theorem bell.four340shows bell(4) =15341proof342compute343344theorem bell.five345shows bell(5) =52346proof347compute348349theorem bell.ge_one (n : Nat)350shows1≤ bell(n)351proof352rw [bell.value, bellSum.succ, stirling.diagonal]353have sumNat := bellSum.natural at n, n354arith355356-- The row sum is unchanged beyond the diagonal.357theorem bellSum.beyond (n d : Nat)358shows bellSum(n, n +1+ d) = bell(n)359proof360induction d361| zero =>362have shape : n +1+0= n +1byring363rw [shape, bell.value]364rfl365| j +1using ih =>366have shape : n +1+ (j +1) = (n +1+ j) +1byring367rw [shape, bellSum.succ, ih]368have below : n < n +1+ j byarith369have vanish := stirling.above_diagonal with below370rw vanish371have bNat := bell.natural at n372ring373