1-- Cartesian products and cardinal multiplication2-- 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.Product8import Sum910-- The direct product of two set-valued function graphs. The graph is a11-- bounded Separation subset of (A × B) × (C × D).12def productMap (f g A B C D : Set) : Set:= { p ∈ (A × B) × (C × D) |∃ x ∈ A, ∃ y ∈ B, ∃ z ∈ C, ∃ w ∈ D, relates(f, x, z) ∧ relates(g, y, w) ∧ p = ((x, y) ↦ (z, w)) }1314theorem productMap.relates (f g A B C D x y z w : Set)15requires hx : x ∈ A16requires hy : y ∈ B17requires hz : z ∈ C18requires hw : w ∈ D19requires hf : relates(f, x, z)20requires hg : relates(g, y, w)21shows relates(productMap(f, g, A, B, C, D), (x, y), (z, w))22proof23simp [relates, productMap.membership, product.pair]2425theorem productMap.factors (f g A B C D u v : Set)26requires h : relates(productMap(f, g, A, B, C, D), u, v)27shows∃ x ∈ A, ∃ y ∈ B, ∃ z ∈ C, ∃ w ∈ D, u = (x, y) ∧ v = (z, w) ∧ relates(f, x, z) ∧ relates(g, y, w)28proof29unfold relates at h30obtain⟨bounded, x, hxA, y, hyB, z, hzC, w, hwD, hf, hg, pairShape⟩:= productMap.membership.forward with h31obtain (source, target) := orderedPair.injective with pairShape32refine⟨x, hxA, y, hyB, z, hzC, w, hwD, source, target, hf, hg⟩3334theorem productMap.relation (f g A B C D : Set)35shows RelationBetween(productMap(f, g, A, B, C, D), A × B, C × D)36proof37simp [RelationBetween, Subset]3839theorem productMap.total (f g A B C D : Set)40requires hf : f : A → C41requires hg : g : B → D42shows TotalBetween(productMap(f, g, A, B, C, D), A × B, C × D)43proof44intros u hu45obtain⟨sourceBound, x, hxA, y, hyB, sourceShape⟩:= product.membership.forward with hu46obtain⟨z, hzC, hfxz⟩:= FunctionBetween.exists_value with hf, hxA47obtain⟨w, hwD, hgyw⟩:= FunctionBetween.exists_value with hg, hyB48witness (z, w)49constructor50 . have targetPair := product.pair at z, w, C, D51exact targetPair with hzC, hwD52 . rw sourceShape53exact productMap.relates with hxA, hyB, hzC, hwD, hfxz, hgyw5455theorem productMap.functional (f g A B C D : Set)56requires hf : f : A → C57requires hg : g : B → D58shows Functional(productMap(f, g, A, B, C, D))59proof60intros u v huv q huq61obtain⟨x1, hx1A, y1, hy1B, z1, hz1C, w1, hw1D, source1, target1, hfxz1, hgyw1⟩:= productMap.factors with huv62obtain⟨x2, hx2A, y2, hy2B, z2, hz2C, w2, hw2D, source2, target2, hfxz2, hgyw2⟩:= productMap.factors with huq63rw source1 at source264obtain (sameX, sameY) := orderedPair.injective with source265rw← sameX at hfxz266rw← sameY at hgyw267have fUnique := FunctionBetween.functional with hf, x1, z1, hfxz1, z2, hfxz268have gUnique := FunctionBetween.functional with hg, y1, w1, hgyw1, w2, hgyw269rw [target1, target2, fUnique, gUnique]70rfl7172theorem productMap.function (f g A B C D : Set)73requires hf : f : A → C74requires hg : g : B → D75shows productMap(f, g, A, B, C, D) : A × B → C × D76proof77constructor78 . exact productMap.relation at f, g, A, B, C, D79 . constructor80 . have functional := productMap.functional at f, g, A, B, C, D81exact functional with hf, hg82 . exact productMap.total with hf, hg8384theorem productMap.apply (f g A B C D x y : Set)85requires functionF : f : A → C86requires functionG : g : B → D87requires hx : x ∈ A88requires hy : y ∈ B89shows productMap(f, g, A, B, C, D)((x, y)) = (f(x), g(y))90proof91have function := productMap.function with functionF, functionG92have input := product.pair with hx, hy93have valueF := FunctionBetween.apply_closed with hx94have valueG := FunctionBetween.apply_closed with hy95have output := product.pair with valueF, valueG96have edgeF := FunctionBetween.relates_apply with hx97have edgeG := FunctionBetween.relates_apply with hy98have edge := productMap.relates with hx, hy99exact FunctionBetween.apply_value with*100101theorem productMap.injective (f g A B C D : Set)102requires hf : InjectiveBetween(f, A, C)103requires hg : InjectiveBetween(g, B, D)104shows InjectiveBetween(productMap(f, g, A, B, C, D), A × B, C × D)105proof106constructor107 . have fFunction := Injective.function with hf108have gFunction := Injective.function with hg109exact productMap.function with fFunction, gFunction110 . intros u hu v hv q hq huq hvq111obtain⟨x1, hx1A, y1, hy1B, z1, hz1C, w1, hw1D, source1, target1, hfxz1, hgyw1⟩:= productMap.factors with huq112obtain⟨x2, hx2A, y2, hy2B, z2, hz2C, w2, hw2D, source2, target2, hfxz2, hgyw2⟩:= productMap.factors with hvq113rw target1 at target2114obtain (sameZ, sameW) := orderedPair.injective with target2115rw← sameZ at hfxz2116rw← sameW at hgyw2117have sameX := Injective.cancel with hf, hx1A, hx2A, hz1C, hfxz1, hfxz2118have sameY := Injective.cancel with hg, hy1B, hy2B, hw1D, hgyw1, hgyw2119rw [source1, source2, sameX, sameY]120rfl121122theorem productMap.surjective (f g A B C D : Set)123requires hf : SurjectiveBetween(f, A, C)124requires hg : SurjectiveBetween(g, B, D)125shows SurjectiveBetween(productMap(f, g, A, B, C, D), A × B, C × D)126proof127constructor128 . have fFunction := Surjective.function with hf129have gFunction := Surjective.function with hg130exact productMap.function with fFunction, gFunction131 . intros q hq132obtain⟨targetBound, z, hzC, w, hwD, targetShape⟩:= product.membership.forward with hq133rcases hf with (fFunctionAgain, fOnto)134specialize fOnto with z, hzC135obtain⟨x, hxA, hfxz⟩:= fOnto136rcases hg with (gFunctionAgain, gOnto)137specialize gOnto with w, hwD138obtain⟨y, hyB, hgyw⟩:= gOnto139witness (x, y)140constructor141 . have sourcePair := product.pair at x, y, A, B142exact sourcePair with hxA, hyB143 . rw targetShape144exact productMap.relates with hxA, hyB, hzC, hwD, hfxz, hgyw145146theorem productMap.bijective (f g A B C D : Set)147requires hf : BijectiveBetween(f, A, C)148requires hg : BijectiveBetween(g, B, D)149shows BijectiveBetween(productMap(f, g, A, B, C, D), A × B, C × D)150proof151--! Split the product bijection into its two obligations.152--| Each coordinate is transported by its own set-valued graph.153constructor154 . --! Injectivity coordinatewise155have fInjective := Bijective.injective with hf156have gInjective := Bijective.injective with hg157exact productMap.injective with fInjective, gInjective158 . --! Surjectivity coordinatewise159have fSurjective := Bijective.surjective with hf160have gSurjective := Bijective.surjective with hg161exact productMap.surjective with fSurjective, gSurjective162163theorem Equipotent.product (A B C D : Set)164requires hAC : A ≃ C165requires hBD : B ≃ D166shows (A × B) ≃ (C × D)167proof168obtain (f, hf) := hAC169obtain (g, hg) := hBD170witness productMap(f, g, A, B, C, D)171exact productMap.bijective with hf, hg172173theorem product.zero_left (A : Set)174shows0× A =0175proof176ext p177constructor178 . intro hp179obtain⟨bounded, x, hxZero, rest⟩:= product.membership.forward with hp180rw nat.zero at hxZero181simp182 . intro hp183rw nat.zero at hp184simp185186-- Adding the new point m to the first factor adds one complete row. This187-- graph separates the old rectangle from that row by the existing sum tags.188def productStepMap (m n : Set) : Set:= { p ∈ (succ(m) × n) × ((m × n) ⊎ n) | (∃ x ∈ m, ∃ y ∈ n, p = ((x, y) ↦ inl((x, y)))) ∨ (∃ y ∈ n, p = ((m, y) ↦ inr(y))) }189190theorem productStepMap.relates_old {m : Nat} (n x y : Set)191requires hx : x ∈ m192requires hy : y ∈ n193shows relates(productStepMap(m, n), (x, y), inl((x, y)))194proof195unfold relates196apply productStepMap.membership.backward197constructor198 . apply product.pair at (x, y), inl((x, y)), succ(m) × n, (m × n) ⊎ n199 . apply product.pair at x, y, succ(m), n200 . apply membership.successor_set{n := m}.backward at x201left202exact hx203 . exact hy204 . have pairMember := product.pair with hx, hy205exact disjointSum.left with pairMember206 . left207refine⟨x, hx, y, hy, rfl⟩208209theorem productStepMap.relates_new {m : Nat} (n y : Set)210requires hy : y ∈ n211shows relates(productStepMap(m, n), (m, y), inr(y))212proof213unfold relates214apply productStepMap.membership.backward215constructor216 . apply product.pair at (m, y), inr(y), succ(m) × n, (m × n) ⊎ n217 . apply product.pair at m, y, succ(m), n218 . apply membership.successor_set{n := m}.backward at m219right220rfl221 . exact hy222 . have tagged := disjointSum.right at y, m × n, n223exact tagged with hy224 . right225refine⟨y, hy, rfl⟩226227theorem productStepMap.factors (m n u v : Set)228requires h : relates(productStepMap(m, n), u, v)229shows (∃ x ∈ m, ∃ y ∈ n, u = (x, y) ∧ v = inl((x, y))) ∨ (∃ y ∈ n, u = (m, y) ∧ v = inr(y))230proof231unfold relates at h232obtain (bounded, alternatives) := productStepMap.membership.forward with h233cases alternatives with old | newest234 . obtain (x, hx) := old235rcases hx with⟨hxM, y, hyN, pairShape⟩236obtain (source, target) := orderedPair.injective with pairShape237left238refine⟨x, hxM, y, hyN, source, target⟩239 . obtain (yNew, hyNew) := newest240rcases hyNew with (hyNewN, pairShapeNew)241obtain (sourceNew, targetNew) := orderedPair.injective with pairShapeNew242right243refine⟨yNew, hyNewN, sourceNew, targetNew⟩244245theorem productStepMap.relation (m n : Set)246shows RelationBetween(productStepMap(m, n), succ(m) × n, (m × n) ⊎ n)247proof248simp [RelationBetween, Subset]249250theorem productStepMap.total {m : Nat} (n : Set)251shows TotalBetween(productStepMap(m, n), succ(m) × n, (m × n) ⊎ n)252proof253intros u hu254obtain⟨bounded, x, hxSucc, y, hyN, source⟩:= product.membership.forward with hu255have successorShape := membership.successor_set{n := m}.forward with hxSucc256cases successorShape with old | newest257 . witness inl((x, y))258constructor259 . have pairMember := product.pair with old, hyN260exact disjointSum.left with pairMember261 . rw source262exact productStepMap.relates_old{m := m} at n, x, y263 . witness inr(y)264constructor265 . have tagged := disjointSum.right at y, m × n, n266exact tagged with hyN267 . rw [source, newest]268exact productStepMap.relates_new{m := m} at n, y269270theorem productStepMap.functional {m : Nat} (n : Set)271shows Functional(productStepMap(m, n))272proof273intros u v huv q huq274have first := productStepMap.factors with huv275have second := productStepMap.factors with huq276cases first with firstOld | firstNew277 . cases second with secondOld | secondNew278 . obtain (x1, hx1) := firstOld279rcases hx1 with⟨hx1M, y1, hy1N, source1, target1⟩280obtain⟨x2, hx2M, y2, hy2N, source2, target2⟩:= secondOld281rw source1 at source2282obtain (sameX, sameY) := orderedPair.injective with source2283rw [target1, target2, sameX, sameY]284rfl285 . obtain (xOld, hxOld) := firstOld286rcases hxOld with⟨hxOldM, yOld, hyOldN, sourceOld, targetOld⟩287obtain⟨yNew, hyNewN, sourceNew, targetNew⟩:= secondNew288rw sourceOld at sourceNew289have sameFirst := orderedPair.first with sourceNew290rw sameFirst at hxOldM291have irreflexive := membership.irreflexive with hxOldM292contradiction293 . cases second with secondOldAfterNew | secondNewAfterNew294 . obtain (yNewFirst, hyNewFirst) := firstNew295rcases hyNewFirst with⟨hyNewFirstN, sourceNewFirst, targetNewFirst⟩296obtain⟨xOldSecond, hxOldSecondM, yOldSecond, hyOldSecondN, sourceOldSecond, targetOldSecond⟩:= secondOldAfterNew297rw sourceOldSecond at sourceNewFirst298have sameFirstReverse := orderedPair.first with sourceNewFirst299rw sameFirstReverse at hxOldSecondM300have irreflexiveReverse := membership.irreflexive with hxOldSecondM301contradiction302 . obtain (yNew1, hyNew1) := firstNew303rcases hyNew1 with⟨hyNew1N, sourceNew1, targetNew1⟩304obtain⟨yNew2, hyNew2N, sourceNew2, targetNew2⟩:= secondNewAfterNew305rw sourceNew1 at sourceNew2306obtain (sameM, sameYNew) := orderedPair.injective with sourceNew2307rw [targetNew1, targetNew2, sameYNew]308rfl309310theorem productStepMap.function {m : Nat} (n : Set)311shows productStepMap(m, n) : succ(m) × n → (m × n) ⊎ n312proof313constructor314 . exact productStepMap.relation at m, n315 . constructor316 . exact productStepMap.functional{m := m} at n317 . exact productStepMap.total{m := m} at n318319theorem productStepMap.injective {m : Nat} (n : Set)320shows InjectiveBetween(productStepMap(m, n), succ(m) × n, (m × n) ⊎ n)321proof322constructor323 . exact productStepMap.function{m := m} at n324 . intros u hu v hv q hq huq hvq325have first := productStepMap.factors with huq326have second := productStepMap.factors with hvq327cases first with firstOld | firstNew328 . cases second with secondOld | secondNew329 . obtain (x1, hx1) := firstOld330rcases hx1 with⟨hx1M, y1, hy1N, source1, target1⟩331obtain⟨x2, hx2M, y2, hy2N, source2, target2⟩:= secondOld332rw target1 at target2333have samePair := inl.injective with target2334rw source1335 rwa source2336 . obtain (xOld, hxOld) := firstOld337rcases hxOld with⟨hxOldM, yOld, hyOldN, sourceOld, targetOld⟩338obtain⟨yNew, hyNewN, sourceNew, targetNew⟩:= secondNew339rw targetOld at targetNew340have impossible := tags.disjoint with targetNew341contradiction342 . cases second with secondOldAfterNew | secondNewAfterNew343 . obtain (yNewFirst, hyNewFirst) := firstNew344rcases hyNewFirst with⟨hyNewFirstN, sourceNewFirst, targetNewFirst⟩345obtain⟨xOldSecond, hxOldSecondM, yOldSecond, hyOldSecondN, sourceOldSecond, targetOldSecond⟩:= secondOldAfterNew346rw targetOldSecond at targetNewFirst347have impossibleReverse := tags.disjoint with targetNewFirst348contradiction349 . obtain (yNew1, hyNew1) := firstNew350rcases hyNew1 with⟨hyNew1N, sourceNew1, targetNew1⟩351obtain⟨yNew2, hyNew2N, sourceNew2, targetNew2⟩:= secondNewAfterNew352rw targetNew1 at targetNew2353have sameY := inr.injective with targetNew2354rw [sourceNew1, sourceNew2, sameY]355rfl356357theorem productStepMap.surjective {m : Nat} (n : Set)358shows SurjectiveBetween(productStepMap(m, n), succ(m) × n, (m × n) ⊎ n)359proof360constructor361 . exact productStepMap.function{m := m} at n362 . intros q hq363have targetShape := disjointSum.membership.forward with hq364cases targetShape with old | newest365 . obtain (pairXY, hpairXY) := old366rcases hpairXY with (pairMember, qShape)367obtain⟨pairBound, x, hxM, y, hyN, pairShape⟩:= product.membership.forward with pairMember368witness (x, y)369constructor370 . apply product.pair at x, y, succ(m), n371 . apply membership.successor_set{n := m}.backward at x372left373exact hxM374 . exact hyN375 . rw [qShape, pairShape]376exact productStepMap.relates_old{m := m} at n, x, y377 . obtain (yNew, hyNew) := newest378rcases hyNew with (hyNewN, qShapeNew)379witness (m, yNew)380constructor381 . apply product.pair at m, yNew, succ(m), n382 . apply membership.successor_set{n := m}.backward at m383right384rfl385 . exact hyNewN386 . rw qShapeNew387exact productStepMap.relates_new{m := m} at n, yNew388389theorem productStepMap.bijective {m : Nat} (n : Set)390shows BijectiveBetween(productStepMap(m, n), succ(m) × n, (m × n) ⊎ n)391proof392--! Split a successor rectangle into its old part and newest row.393--| The tags make the two images disjoint without imposing an order on n.394constructor395 . --! Each tagged output has one source pair396exact productStepMap.injective{m := m} at n397 . --! Every old pair ∨ newest-row element is reached398exact productStepMap.surjective{m := m} at n399400-- Recursively assemble the concrete graph witnessing m × n ≃ m*n. At a401-- successor, productStepMap exposes the old rectangle plus one row, sumMap402-- transports the induction hypothesis, and natSumEquiv packs the two blocks403-- into the next native numeral.404def natProductEquiv (m n : Nat) →Set405| zero, n => identity(0)406| succ(m), n => compose(natSumEquiv(m * n, n), compose(sumMap(natProductEquiv(m, n), identity(n), m × n, n, m * n, n), productStepMap(m, n), succ(m) × n, (m × n) ⊎ n, (m * n) ⊎ n), succ(m) × n, (m * n) ⊎ n, m * n + n)407408theorem BijectiveBetween.codomain_eq (f X Y Z : Set)409requires h : BijectiveBetween(f, X, Y)410requires hYZ : Y = Z411shows BijectiveBetween(f, X, Z)412proof413simp414415theorem natProductEquiv.bijective (n m : Nat)416shows BijectiveBetween(natProductEquiv(m, n), m × n, mul(m, n))417proof418induction m419| zero =>420rw [natProductEquiv, mul, product.zero_left]421exact identity.bijective422| succ(k) using ih =>423--! Decompose the successor rectangle.424--| The old rectangle and newest row are separated by productStepMap.425rw [natProductEquiv, mul]426--! Transport the old block and the new row.427have step := productStepMap.bijective{m := k} at n428have identityN := identity.bijective at n429have lifted := sumMap.bijective with ih, identityN430have middle := compose.bijective with step, lifted431--! Pack the two finite blocks into multiplication.432apply BijectiveBetween.codomain_eq at compose(natSumEquiv(k * n, n), compose(sumMap(natProductEquiv(k, n), identity(n), k × n, n, k * n, n), productStepMap(k, n), succ(k) × n, (k × n) ⊎ n, (k * n) ⊎ n), succ(k) × n, (k * n) ⊎ n, k * n + n), succ(k) × n, k * n + n, n + k * n433 . apply compose.bijective at natSumEquiv(k * n, n), compose(sumMap(natProductEquiv(k, n), identity(n), k × n, n, k * n, n), productStepMap(k, n), succ(k) × n, (k × n) ⊎ n, (k * n) ⊎ n), succ(k) × n, (k * n) ⊎ n, k * n + n434 . exact middle435 . apply natSumEquiv.bijective at n, mul(k, n)436 . apply Grace.Core.Nat.Algebra.add.commutative at n, mul(k, n)437438theorem Equipotent.nat_product (m n : Set)439requires hm : Nat(m)440requires hn : Nat(n)441shows (m × n) ≃ mul(m, n)442proof443witness natProductEquiv(m, n)444exact natProductEquiv.bijective with hn, hm445446theorem HasCardinality.product (A B m n : Set)447requires hA : HasCardinality(A, m)448requires hB : HasCardinality(B, n)449shows HasCardinality(A × B, mul(m, n))450proof451rcases hA with (hm, equipotentA)452rcases hB with (hn, equipotentB)453constructor454 . exact Grace.Core.Nat.Algebra.mul.closed at n, m455 . --! Lift the two cardinal witnesses coordinatewise.456have lifted := Equipotent.product with equipotentA, equipotentB457--! Identify the finite rectangle with native multiplication.458have numeric := Equipotent.nat_product with hm, hn459exact Equipotent.transitive with lifted, numeric460461theorem Finite.product (A B : Set)462requires finiteA : Finite(A)463requires finiteB : Finite(B)464shows Finite(A × B)465proof466obtain (m, hA) := finiteA467obtain (n, hB) := finiteB468witness mul(m, n)469exact HasCardinality.product with hA, hB470471-- The canonical exchange of two Cartesian factors.472defunique swappedPair (A B p : Set) (q ∈ B × A) :=473∃ x ∈ A, ∃ y ∈ B, p = (x, y) ∧ q = (y, x)474requires pair : p ∈ A × B475proof476obtain⟨bound, x, hx, y, hy, shape⟩:= product.membership.forward with pair477have output := product.pair with hy, hx478refine⟨(y, x), output, x, hx, y, hy, shape, rfl⟩479uniqueness480intros u hu first v hv second481obtain⟨a, ha, b, hb, shapeU, valueU⟩:= first482obtain⟨c, hc, d, hd, shapeV, valueV⟩:= second483have equal : (a, b) = (c, d) bycc484have coordinates := orderedPair.injective with equal485cc486487defgraph productSwap (A B : Set) (p ∈ A × B) : B × A := swappedPair(A, B, p)488proof489have selected := swappedPair.selected with member490exact selected.left491492theorem productSwap.pair (A B x y : Set)493requires hx : x ∈ A494requires hy : y ∈ B495shows productSwap(A, B)((x, y)) = (y, x)496proof497have pair := product.pair at x, y, A, B with*498have selected := swappedPair.selected with pair499obtain⟨a, ha, b, hb, shape, output⟩:= selected.right500have coordinates := orderedPair.injective with shape501have value := productSwap.value with pair502cc503504theorem productSwap.involution (A B p : Set)505requires pair : p ∈ A × B506shows productSwap(B, A)(productSwap(A, B)(p)) = p507proof508obtain⟨bound, x, hx, y, hy, shape⟩:= product.membership.forward with pair509have first := productSwap.pair with hx, hy510have second := productSwap.pair with hy, hx511cc512513theorem productSwap.bijective (A B : Set)514shows BijectiveBetween(productSwap(A, B), A × B, B × A)515proof516have function := productSwap.function at A, B517have reverse := productSwap.function at B, A518constructor519 . unfold InjectiveBetween520refine⟨function, _⟩521intros x hx y hy z hz edgeX edgeY522have valueX := FunctionBetween.apply_value at productSwap(A, B), A × B, B × A, x, z with*523have valueY := FunctionBetween.apply_value at productSwap(A, B), A × B, B × A, y, z with*524have inverseX := productSwap.involution with hx525have inverseY := productSwap.involution with hy526cc527 . unfold SurjectiveBetween528refine⟨function, _⟩529intros z hz530have input := FunctionBetween.apply_closed at productSwap(B, A), B × A, A × B, z with*531have inverse := productSwap.involution with hz532have edge := FunctionBetween.relates_apply at productSwap(A, B), A × B, B × A, productSwap(B, A)(z) with*533refine⟨productSwap(B, A)(z), input, _⟩534 rwa inverse at edge535