1-- Countable sets are closed under subsets, images, and finite products2-- 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.Cardinal.CountableClosure8import Countable9import Product1011-- A subset of a countable set is countable: restrict the injection.12theorem Countable.subset (A S : Set)13requires countable : Countable(A)14requires subset : S ⊆ A15shows Countable(S)16proof17obtain⟨g, injection⟩:= countable18have function := Injective.function with injection19have closed : ∀ x ∈ S, g(x) ∈ ℕ20intros x hx21have hxA := subset with x, hx22exact FunctionBetween.apply_closed at g, A, ℕ, x with*23have restricted := restrict.function with*24witness restrict(g, S)25constructor26 . exact restricted27 . intros x hx y hy z hz relatesX relatesY28have dataX := restrict.relates.forward with relatesX29have dataY := restrict.relates.forward with relatesY30rcases dataX with⟨_, gX⟩31rcases dataY with⟨_, gY⟩32have hxA := subset with x, hx33have hyA := subset with y, hy34exact Injective.cancel with*3536-- The codes an injection g assigns to the preimages of b under f.37def surjectionCodes (g f A b : Set) : Set:=38 { n ∈ ℕ |∃ a ∈ A, relates(f, a, b) ∧ g(a) = n }3940theorem surjectionCodes.subset (g f A b : Set)41shows surjectionCodes(g, f, A, b) ⊆ ℕ42proof43simp4445theorem surjectionCodes.nonempty (g f A B b : Set)46requires injection : InjectiveBetween(g, A, ℕ)47requires surjection : SurjectiveBetween(f, A, B)48requires member : b ∈ B49shows Nonempty(surjectionCodes(g, f, A, b))50proof51have gFunction := Injective.function with injection52rcases surjection with⟨fFunction, onto⟩53obtain⟨a, ha, relatesA⟩:= onto with b, member54have code := FunctionBetween.apply_closed at g, A, ℕ, a with*55witness g(a)56apply surjectionCodes.membership.backward57constructor58 . exact code59 . witness a60refine⟨ha, relatesA, rfl⟩6162-- Send each b to the least code of a preimage. Choosing the least code63-- needs no Choice axiom.64defgraph surjectionCode (g f A B : Set) (b ∈ B) : ℕ := leastNatural(surjectionCodes(g, f, A, b))65requires injection : InjectiveBetween(g, A, ℕ)66requires surjection : SurjectiveBetween(f, A, B)67proof68have inhabited := surjectionCodes.nonempty with*69have naturals := surjectionCodes.subset at g, f, A, b70have selected := leastNatural.selected at surjectionCodes(g, f, A, b) with*71exact selected.left7273-- The image of a countable set under a surjection is countable.74theorem Countable.of_surjection (f A B : Set)75requires countable : Countable(A)76requires surjection : SurjectiveBetween(f, A, B)77shows Countable(B)78proof79obtain⟨g, injection⟩:= countable80have gFunction := Injective.function with injection81have fFunction := Surjective.function with surjection82have functional := FunctionBetween.functional with fFunction83have codeFunction := surjectionCode.function at g, f, A, B with*84witness surjectionCode(g, f, A, B)85constructor86 . exact codeFunction87 . intros b1 hb1 b2 hb2 z hz relates1 relates288have value1 := FunctionBetween.apply_value at surjectionCode(g, f, A, B), B, ℕ, b1, z with*89have value2 := FunctionBetween.apply_value at surjectionCode(g, f, A, B), B, ℕ, b2, z with*90have code1 := surjectionCode.value at g, f, A, B, b1 with*91have code2 := surjectionCode.value at g, f, A, B, b2 with*92have inhabited1 := surjectionCodes.nonempty with hb193have inhabited2 := surjectionCodes.nonempty with hb294have naturals1 := surjectionCodes.subset at g, f, A, b195have naturals2 := surjectionCodes.subset at g, f, A, b296have least1 := leastNatural.member at surjectionCodes(g, f, A, b1) with*97have least2 := leastNatural.member at surjectionCodes(g, f, A, b2) with*98have data1 := surjectionCodes.membership.forward with least199have data2 := surjectionCodes.membership.forward with least2100obtain⟨_, a1, ha1, relatesF1, codeA1⟩:= data1101obtain⟨_, a2, ha2, relatesF2, codeA2⟩:= data2102have sameCode : g(a2) = g(a1) bycc103have gRelates1 := FunctionBetween.relates_apply at g, A, ℕ, a1 with*104have gRelates2 := FunctionBetween.relates_apply at g, A, ℕ, a2 with*105rw sameCode at gRelates2106have codeNatural := FunctionBetween.apply_closed at g, A, ℕ, a1 with*107have sameInput := Injective.cancel with ha1, ha2108rw sameInput at relatesF1109exact Functional.unique with*110111-- Two to the power of n is never zero.112theorem pow.two_nonzero (n : Nat)113shows2 ^ n ≠0114proof115induction n116| zero =>117rw pow.zero118intro bad119have positiveOne : 0 < 1byarith120rw bad at positiveOne121exact Less.irreflexive with*, positiveOne122| succ(k) using ih =>123rw pow.succ124have twoNonzero : 2≠0125intro bad126have positiveTwo : 0 < 2byarith127rw bad at positiveTwo128exact Less.irreflexive at0with*, positiveTwo129exact mul.nonzero at2, 2 ^ k with*130131-- The code 2^a · (2b + 1) determines a and b: a is the number of factors of132-- two, and 2b + 1 is the odd part.133theorem pairingCode.strict (a k b d : Nat)134requires equal : 2 ^ a * succ(b + b) =2 ^ (succ(a) + k) * succ(d + d)135shows⊥136proof137havesplit:= pow.add at2, k, succ(a) with*138rwsplitat equal139have step := pow.succ at2, a with*140rw step at equal141have rearranged : (2*2 ^ a) *2 ^ k * succ(d + d) =2 ^ a * (2* (2 ^ k * succ(d + d))) byring142rw rearranged at equal143have nonzero := pow.two_nonzero at a with*144have cancelled := mul.left_cancel_positive with*145have doubled := mul.two_left at2 ^ k * succ(d + d) with*146rw doubled at cancelled147have even := Even.double at2 ^ k * succ(d + d) with*148rw← cancelled at even149have odd := Odd.double_successor at b with*150exact Odd.not_even with*, even151152theorem pairingCode.injective (a b c d : Nat)153requires equal : 2 ^ a * succ(b + b) =2 ^ c * succ(d + d)154shows a = c ∧ b = d155proof156have twoNonzero : 2≠0157intro bad158arith159have shape := Less.trichotomy at a, c with*160rcases shape with less | same | greater161 . exfalso162obtain⟨k, kNat, cShape⟩:= less163rw← cShape at equal164exact pairingCode.strict with equal165 . rw same at equal166have nonzero := pow.two_nonzero at c with*167have cancelled := mul.left_cancel_positive with*168have doubles := Naturals.injective.succ at b + b, d + d with*169have bLe : b ≤ d byarith170have dLe : d ≤ b byarith171exact⟨same, (LessEqual.antisymmetric at b, d with*)⟩172 . exfalso173obtain⟨k, kNat, aShape⟩:= greater174rw← aShape at equal175 symm at equal176exact pairingCode.strict with equal177178-- The code of a pair of naturals.179defunique pairingCode (p : Set) (n ∈ ℕ) :=180∃ a ∈ ℕ, ∃ b ∈ ℕ, p = (a, b) ∧ n =2 ^ a * succ(b + b)181requires pair : p ∈ ℕ × ℕ182proof183obtain⟨bound, a, ha, b, hb, shape⟩:= product.membership.forward with pair184have aNat := nat.omega.backward with ha185have bNat := nat.omega.backward with hb186have codeNat : Nat(2 ^ a * succ(b + b)) bytypecheck187have codeMember := nat.omega.forward with codeNat188refine⟨2 ^ a * succ(b + b), codeMember, a, ha, b, hb, shape, rfl⟩189uniqueness190intros u hu first v hv second191obtain⟨a, ha, b, hb, shapeU, valueU⟩:= first192obtain⟨c, hc, d, hd, shapeV, valueV⟩:= second193have equal : (a, b) = (c, d) bycc194obtain⟨ac, bd⟩:= orderedPair.injective with equal195rw [valueU, valueV, ac, bd]196rfl197198defgraph naturalPairing (p ∈ ℕ × ℕ) : ℕ := pairingCode(p)199proof200have selected := pairingCode.selected with member201exact selected.left202203theorem naturalPairing.injective204shows InjectiveBetween(naturalPairing, ℕ × ℕ, ℕ)205proof206have function := naturalPairing.function207constructor208 . exact function209 . intros p hp q hq z hz relatesP relatesQ210have valueP := FunctionBetween.apply_value at naturalPairing, ℕ × ℕ, ℕ, p, z with*211have valueQ := FunctionBetween.apply_value at naturalPairing, ℕ × ℕ, ℕ, q, z with*212have codeP := naturalPairing.value with hp213have codeQ := naturalPairing.value with hq214have selectedP := pairingCode.selected with hp215have selectedQ := pairingCode.selected with hq216obtain⟨_, a, ha, b, hb, shapeP, formP⟩:= selectedP217obtain⟨_, c, hc, d, hd, shapeQ, formQ⟩:= selectedQ218have aNat := nat.omega.backward with ha219have bNat := nat.omega.backward with hb220have cNat := nat.omega.backward with hc221have dNat := nat.omega.backward with hd222have same : 2 ^ a * succ(b + b) =2 ^ c * succ(d + d) bycc223have coordinates := pairingCode.injective with*224rcases coordinates with⟨ac, bd⟩225rw [shapeP, shapeQ, ac, bd]226rfl227228-- The product of two countable sets is countable.229theorem Countable.product (A B : Set)230requires countableA : Countable(A)231requires countableB : Countable(B)232shows Countable(A × B)233proof234obtain⟨g, injectionG⟩:= countableA235obtain⟨h, injectionH⟩:= countableB236have product := productMap.injective at g, h, A, B, ℕ, ℕ with*237have pairing := naturalPairing.injective238have composed := compose.injective at naturalPairing, productMap(g, h, A, B, ℕ, ℕ), A × B, ℕ × ℕ, ℕ with*239witness compose(naturalPairing, productMap(g, h, A, B, ℕ, ℕ), A × B, ℕ × ℕ, ℕ)240exact composed241242theorem naturals.countable243shows Countable(ℕ)244proof245witness identity(ℕ)246exact identity.injective247