1-- The fundamental theorem of arithmetic: prime factorizations exist and are unique2-- 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.Factorization8import Prime9import Coprime10import Core.List1112-- A factorization is a list of natural numbers; its value is their product,13-- written ∏ xs.14def listProduct (xs : List(ℕ)) →Nat15| [] =>116| x :: rest => mul(x, listProduct(rest))1718notationprefix80"∏"=> listProduct1920-- The number of times a natural number occurs in a list. Two lists with the21-- same multiplicities are the same multiset.22def multiplicity (p : Nat, xs : List(ℕ)) →Nat23| p, [] =>024| p, x :: rest => add(chooseNat(equalNat(p, x), 1, 0), multiplicity(p, rest))2526theorem listProduct.nil27shows∏ [] =128proof29simp3031theorem listProduct.cons (x : Nat) (rest : List(ℕ))32shows∏ (x :: rest) = x *∏ rest33proof34unfold listProduct35rfl3637theorem multiplicity.nil (p : Nat)38shows multiplicity(p, []) =039proof40unfold multiplicity41rfl4243theorem multiplicity.cons (p x : Nat) (rest : List(ℕ))44shows multiplicity(p, x :: rest) = chooseNat(equalNat(p, x), 1, 0) + multiplicity(p, rest)45proof46unfold multiplicity47rfl4849theorem multiplicity.cons_self (p : Nat) (rest : List(ℕ))50shows multiplicity(p, p :: rest) =1+ multiplicity(p, rest)51proof52have same := equalNat.reflexive with*53unfold multiplicity54simp [chooseNat]5556theorem multiplicity.cons_other (p x : Nat) (rest : List(ℕ))57requires different : p ≠ x58shows multiplicity(p, x :: rest) = multiplicity(p, rest)59proof60have test := equalNat.false_iff.backward with*61unfold multiplicity62simp [chooseNat, add]6364theorem multiplicity.positive_cons (p x : Nat) (rest : List(ℕ))65requires positive : 0 < multiplicity(p, rest)66shows0 < multiplicity(p, x :: rest)67proof68have equation := multiplicity.cons at p, x, rest with*69rw equation70arith7172-- A list of primes. Membership is positive multiplicity, so two lists of73-- primes with the same counts describe the same multiset of primes.74predicate PrimeList (xs : List(ℕ)) :=75∀ q, Nat(q) →0 < multiplicity(q, xs) → Prime(q)7677theorem PrimeList.nil78shows PrimeList([])79proof80intros q qNat positive81rw multiplicity.nil at positive82exfalso83exact Less.irreflexive at0with*, positive8485theorem PrimeList.cons (x : Nat) (rest : List(ℕ))86requires prime : Prime(x)87requires primes : PrimeList(rest)88shows PrimeList(x :: rest)89proof90intros q qNat positive91havesplit : q = x ∨¬(q = x) bytauto92casessplitwith equal | different93 . rw equal94exact prime95 . have equation := multiplicity.cons_other with*96rw equation at positive97exact primes with q, qNat, positive9899theorem PrimeList.head (x : Nat) (rest : List(ℕ))100requires primes : PrimeList(x :: rest)101shows Prime(x)102proof103have equation := multiplicity.cons_self with*104have positive : 0 < multiplicity(x, x :: rest)105rw equation106arith107exact primes with x, positive108109theorem PrimeList.tail (x : Nat) (rest : List(ℕ))110requires primes : PrimeList(x :: rest)111shows PrimeList(rest)112proof113intros q qNat positive114have lifted := multiplicity.positive_cons at q, x, rest with*115exact primes with q, qNat, lifted116117theorem Prime.nonzero (p : Nat)118requires prime : Prime(p)119shows p ≠0120proof121have above := Prime.greater_than_one with prime122intro zero123rw zero at above124have bad : 0 < 0byarith125exact Less.irreflexive at0with*, bad126127theorem Prime.divides_prime (p q : Nat)128requires primeP : Prime(p)129requires primeQ : Prime(q)130requires divides : p ∣ q131shows p = q132proof133have qNonzero := Prime.nonzero with primeQ134have bound := divides.le with*135have shape := Less.trichotomy at p, q with*136rcases shape with less | equal | greater137 . exfalso138have member := membership.of_Less with*, less139have one := Prime.divisor with primeQ, member, divides140have above := Prime.greater_than_one with primeP141rw one at above142exact Less.irreflexive with*, above143 . exact equal144 . exfalso145have bad : p < p byarith146exact Less.irreflexive with*, bad147148-- A prime not dividing a is coprime to it: its only divisors are 1 and itself.149theorem Prime.coprime_of_not_divides (p a : Nat)150requires prime : Prime(p)151requires notDivides : ¬(p ∣ a)152shows IsGCD(p, a, 1)153proof154haveleft:= one.divides at p with*155haveright:= one.divides at a with*156have greatest : ∀ d, Nat(d) → d ∣ p → d ∣ a → d ∣ 1157intros d dNat dividesP dividesA158have pNonzero := Prime.nonzero with*159have bound := divides.le at d, p with*160have shape := Less.trichotomy at d, p with*161rcases shape with less | equal | greater162 . have member := membership.of_Less with*, less163have one := Prime.divisor with prime, dNat, member, dividesP164rw one165exact divides.reflexive with*166 . exfalso167rw equal at dividesA168exact notDivides with dividesA169 . exfalso170have bad : p < p byarith171exact Less.irreflexive with*, bad172exact IsGCD.intro with*173174-- Euclid's lemma, through the integer Bézout identity.175theorem Prime.divides_mul (p a b : Nat)176requires prime : Prime(p)177requires divides : p ∣ a * b178shows p ∣ a ∨ p ∣ b179proof180havesplit : p ∣ a ∨¬(p ∣ a) bytauto181casessplitwithleft| notLeft182 . left183exactleft184 . right185have certificate := Prime.coprime_of_not_divides with*186have unit := gcd.unique with*187have ofNat := integerGCD.ofNat at p, a with*188have coprime : IntegerCoprime(integerOfNat(p), integerOfNat(a))189unfold IntegerCoprime190 rwa ofNat191have absolute := integerNatAbs.ofNat at a * b with*192have magnitude : p ∣ integerNatAbs(integerOfNat(a * b)) by rwa absolute193have lifted := IntegerDivides.natural_left.backward with*194have product := integerOfNat.mul at a, b with*195rw product at lifted196have cancelled := IntegerCoprime.cancel with*197have back := IntegerDivides.natural_left.forward at p, integerOfNat(b) with*198have absoluteB := integerNatAbs.ofNat at b with*199 rwa absoluteB at back200201theorem Prime.divides_listProduct (p : Nat) (xs : List(ℕ))202requires prime : Prime(p)203shows p ∣ ∏ xs →∃ q, Nat(q) ∧0 < multiplicity(q, xs) ∧ p ∣ q204proof205induction xs206| List.nil =>207intro divides208rw listProduct.nil at divides209exfalso210have one := divides.one_only with*211have above := Prime.greater_than_one with prime212rw one at above213exact Less.irreflexive at1with*, above214| List.cons(x, rest) using ih =>215intro divides216have xNat : Nat(x) bytypecheck217have equation := listProduct.cons at x, rest with*218rw equation at divides219havesplit:= Prime.divides_mul with*220casessplitwith head | tail221 . have count := multiplicity.cons_self at x, rest with*222have positive : 0 < multiplicity(x, x :: rest)223rw count224arith225refine⟨x, xNat, positive, head⟩226 . obtain⟨q, qNat, positive, dividesQ⟩:= ih with tail227have lifted := multiplicity.positive_cons at q, x, rest with*228refine⟨q, qNat, lifted, dividesQ⟩229230-- Removing one occurrence of y from a list leaves a list with the same product231-- after multiplying by y, and the same counts after putting y back.232theorem multiplicity.remove (y : Nat) (ys : List(ℕ))233shows0 < multiplicity(y, ys) →234∃ zs, List(ℕ, zs) ∧∏ ys =∏ (y :: zs) ∧235 (∀ q, Nat(q) → multiplicity(q, ys) = multiplicity(q, y :: zs))236proof237induction ys238| List.nil =>239intro positive240have empty := multiplicity.nil with*241rw empty at positive242exfalso243exact Less.irreflexive at0with*, positive244| List.cons(x, rest) using ih =>245intro positive246havesplit : y = x ∨¬(y = x) bytauto247casessplitwith equal | different248 . refine⟨rest, h_rest, _, _⟩249 . rw equal250rfl251 . intros q qNat252rw equal253rfl254 . have restPositive : 0 < multiplicity(y, rest)255have equation := multiplicity.cons_other with different256 rwa equation at positive257obtain⟨zs, zsList, product, counts⟩:= ih with restPositive258have consList : List(ℕ, x :: zs) bytypecheck259refine⟨x :: zs, consList, _, _⟩260 . have e1 := listProduct.cons at x, rest with*261have e2 := listProduct.cons at y, zs with*262have e3 := listProduct.cons at y, x :: zs with*263have e4 := listProduct.cons at x, zs with*264rw e2 at product265rw [e1, e3, e4, product]266ring267 . intros q qNat268have c1 := multiplicity.cons at q, x, rest with*269have c2 := multiplicity.cons at q, y, x :: zs with*270have c3 := multiplicity.cons at q, x, zs with*271have c4 := multiplicity.cons at q, y, zs with*272have count := counts with q, qNat273rw c4 at count274rw [c1, c2, c3, count]275ring276277-- A list of primes with product one is empty.278theorem listProduct.one (xs : List(ℕ))279requires primes : PrimeList(xs)280requires unit : ∏ xs =1281shows∀ q, Nat(q) → multiplicity(q, xs) =0282proof283have shape := List.cases with*284cases shape with nil | cons285 . intros q qNat286rw nil287exact multiplicity.nil with*288 . exfalso289obtain⟨x, xMember, rest, restList, equal⟩:= cons290rw equal at unit291rw equal at primes292have equation := listProduct.cons at x, rest with*293rw equation at unit294have one := mul.left_factor_one with*, unit295have prime := PrimeList.head with*296have above := Prime.greater_than_one with prime297rw one at above298exact Less.irreflexive with*, above299300-- Uniqueness: two lists of primes with the same product have the same counts.301theorem primeFactorization.unique (xs : List(ℕ))302shows∀ ys, List(ℕ, ys) → PrimeList(xs) → PrimeList(ys) →303∏ xs =∏ ys →304∀ q, Nat(q) → multiplicity(q, xs) = multiplicity(q, ys)305proof306induction xs307| List.nil =>308intros ys ysList primesXs primesYs equal309rw listProduct.nil at equal310 symm at equal311have zero := listProduct.one at ys with*312intros q qNat313have empty := multiplicity.nil at q with*314have count := zero with q, qNat315rw [empty, count]316rfl317| List.cons(x, rest) using ih =>318intros ys ysList primesXs primesYs equal319have primeX := PrimeList.head at x, rest with*320have primesRest := PrimeList.tail at x, rest with*321have e1 := listProduct.cons at x, rest with*322rw e1 at equal323haveself:= divides.reflexive at x with*324have dividesYs : x ∣ ∏ ys325rw← equal326exact divides.multiply_right at x, x, ∏ rest with*327obtain⟨y, yNat, yPositive, dividesY⟩:= Prime.divides_listProduct at x, ys with*, dividesYs328have primeY : Prime(y) byexact primesYs with y, yNat, yPositive329have same := Prime.divides_prime at x, y with*330rw← same at yPositive331obtain⟨zs, zsList, product, counts⟩:= multiplicity.remove at x, ys with*, yPositive332have e2 := listProduct.cons at x, zs with*333rw e2 at product334rw product at equal335have xNonzero := Prime.nonzero at x with*336have restEqual := mul.left_cancel_positive at∏ rest, ∏ zs, x with*337have primesZs : PrimeList(zs)338intros q qNat positive339have count := counts with q, qNat340have lifted := multiplicity.positive_cons at q, x, zs with*341rw← count at lifted342exact primesYs with q, qNat, lifted343have inner := ih with zs, zsList, primesRest, primesZs, restEqual344intros q qNat345have count := counts with q, qNat346have c1 := multiplicity.cons at q, x, rest with*347have c2 := multiplicity.cons at q, x, zs with*348have innerCount := inner with q, qNat349rw [count, c1, c2, innerCount]350rfl351352-- Existence, by strong induction: a prime divisor splits off a smaller cofactor.353lemma primeFactorization.exists_membership (m : Set)354showsNat(m) → m ≠0→∃ xs, List(ℕ, xs) ∧ PrimeList(xs) ∧∏ xs = m355proof356induction m using membership357intros current ih currentNat nonzero358have shape := natural.zero_one_or_above_one with*359rcases shape with zero | one | above360 . exfalso361exact nonzero with zero362 . have nilList : List(ℕ, [])363typecheck364refine⟨[], nilList, PrimeList.nil, ?_⟩365rw one366exact listProduct.nil367 . have oneLess := Less.of_membership at1, current with*, above368obtain⟨p, primeP, dividesP⟩:= natural.exists_prime_divisor at current with*369obtain⟨k, kNat, equation⟩:= dividesP370have kNonzero : k ≠0371intro kZero372rw kZero at equation373have annihilated := mul.right_zero at p with*374rw annihilated at equation375 symm at equation376exact nonzero with equation377have aboveOne := Prime.greater_than_one with*378have smaller : k < current379have scaled := Less.mul_left at1, p, k with*380have unit := mul.right_unit at k with*381have commuted := mul.commutative at p, k with*382 rwa [unit, commuted, equation] at scaled383have member := membership.of_Less at k, current with*, smaller384obtain⟨xs, xsList, primes, product⟩:= ih with k, member, kNat, kNonzero385have consList : List(ℕ, p :: xs) bytypecheck386have consPrimes := PrimeList.cons with*387refine⟨p :: xs, consList, consPrimes, ?_⟩388have e := listProduct.cons at p, xs with*389 rwa [e, product]390391theorem primeFactorization.exists (n : Nat)392requires nonzero : n ≠0393shows∃ xs, List(ℕ, xs) ∧ PrimeList(xs) ∧∏ xs = n394proof395have natural : Nat(n) byassumption396exact primeFactorization.exists_membership with natural, nonzero397398-- The fundamental theorem of arithmetic: every positive natural number is a399-- product of primes, and any two such products use each prime the same number400-- of times.401theorem primeFactorization.fundamental_theorem (n : Nat)402requires nonzero : n ≠0403shows (∃ xs, List(ℕ, xs) ∧ PrimeList(xs) ∧∏ xs = n) ∧404 (∀ xs, List(ℕ, xs) →∀ ys, List(ℕ, ys) → PrimeList(xs) → PrimeList(ys) →405∏ xs = n →∏ ys = n →406∀ q, Nat(q) → multiplicity(q, xs) = multiplicity(q, ys))407proof408constructor409 . exact primeFactorization.exists with*410 . intros xs xsList ys ysList primesXs primesYs productXs productYs411have equal : ∏ xs =∏ ys412rw [productXs, productYs]413rfl414have unique := primeFactorization.unique at xs with*415exact unique with ys, ysList, primesXs, primesYs, equal416