1-- The sum over a product set is the double sum: push forward along the2-- first projection, and each fibre {x} × Y is a copy of Y3-- Part of Cumulus, the Grace17 mathematics library.4-- Copyright (c) 2026 Grant Passmore.5-- Released under the Apache License 2.0. See LICENSE in the repository root.6-- Authors: Grant Passmore78module Grace.Cumulus.Algebra.Ring.ProductSetSum9import Regrouping10import FiniteDoubleSum1112theorem finiteRingSum.product_set (X Y A add zero neg mul one u : Set)13requiresring : IsCommRing(A, add, zero, neg, mul, one)14requires finiteX : Finite(X)15requires finiteY : Finite(Y)16requires matrix : u : X × Y → A17shows finiteRingSum(X × Y, A, add, zero, neg, mul, one, u) =18 finiteDoubleSum(X, Y, A, add, zero, neg, mul, one, u)19proof20have finiteXY := Finite.product with finiteX, finiteY21have projection := firstProjection.function at X, Y22have ordinary := IsCommRing.ring withring23have zeroIn : zero ∈ A := IsRing.zero_in_carrier with ordinary24have push := finitePushforward.sum at X × Y, X, A, add, zero, neg, mul, one,25 firstProjection(X, Y), u withring, matrix, finiteXY, finiteX, projection26have pushFunction := finitePushforward.function at X × Y, X, A, add, zero, neg, mul, one,27 firstProjection(X, Y), u withring, finiteXY, matrix28have rows := finiteRowSums.function at X, Y, A, add, zero, neg, mul, one, u29withring, finiteY, matrix30have values : ∀ x ∈ X,31 finitePushforward(X × Y, X, A, add, zero, neg, mul, one, firstProjection(X, Y), u)(x) =32 finiteRowSums(X, Y, A, add, zero, neg, mul, one, u)(x)33intros x hx34have pushValue := finitePushforward.value at X × Y, X, A, add, zero, neg, mul, one,35 firstProjection(X, Y), u, x withring, finiteXY, matrix, hx36have rowValue := finiteRowSums.value at X, Y, A, add, zero, neg, mul, one, u, x37withring, finiteY, matrix, hx38have pushWeights := pushforwardWeights.function at X × Y, A, add, zero, neg, mul, one,39 firstProjection(X, Y), u, x withring, matrix40have rowWeights := finiteRowWeights.function at X, Y, A, u, x with matrix, hx41have fibreSubset : {x} × Y ⊆ X × Y42intros p hp43obtain⟨bound, a, ha, y, hy, shape⟩:= product.membership.forward with hp44have aIs := singleton.membership.forward with ha45rw [shape, aIs]46exact product.pair with hx, hy47have fibreWeights := restrict.domain_function at u, X × Y, A, {x} × Y with matrix, fibreSubset48have agreement : ∀ p ∈ {x} × Y,49 pushforwardWeights(X × Y, A, add, zero, neg, mul, one, firstProjection(X, Y), u, x)(p) =50 restrict(u, {x} × Y)(p)51intros p hp52obtain⟨bound, a, ha, y, hy, shape⟩:= product.membership.forward with hp53have aIs := singleton.membership.forward with ha54have inXY := fibreSubset with p, hp55have weight := pushforwardWeights.value at X × Y, A, add, zero, neg, mul, one,56 firstProjection(X, Y), u, x, p withring, matrix, inXY57have restricted := restrict.domain_apply at u, X × Y, A, {x} × Y, p58with matrix, fibreSubset, hp59have first : firstProjection(X, Y)(p) = x60rw [shape, aIs]61exact firstProjection.pair at X, Y, x, y with hx, hy62have coefficient := FunctionBetween.apply_closed with matrix, inXY63 symm at first64have same := singleValue.same at A, zero, u(p), firstProjection(X, Y)(p), x65with zeroIn, coefficient, first66rw [weight, restricted, same]67rfl68have vanishing : ∀ p ∈ X × Y, ¬(p ∈ {x} × Y) →69 pushforwardWeights(X × Y, A, add, zero, neg, mul, one, firstProjection(X, Y), u, x)(p) =70 zero71intros p hp outside72obtain⟨bound, a, ha, y, hy, shape⟩:= product.membership.forward with hp73have weight := pushforwardWeights.value at X × Y, A, add, zero, neg, mul, one,74 firstProjection(X, Y), u, x, p withring, matrix, hp75have first : firstProjection(X, Y)(p) = a76rw shape77exact firstProjection.pair at X, Y, a, y with ha, hy78have different : x ≠ firstProjection(X, Y)(p)79intro equal80rw [first] at equal81have inFibre : p ∈ {x} × Y82rw [shape, equal]83exact product.pair with (singleton.self at a), hy84exact outside with inFibre85have coefficient := FunctionBetween.apply_closed with matrix, hp86have other := singleValue.other at A, zero, u(p), firstProjection(X, Y)(p), x87with zeroIn, coefficient, different88rw [weight, other]89rfl90have fibreSum := finiteRingSum.zero_extension at X × Y, {x} × Y, A, add, zero, neg, mul, one,91 pushforwardWeights(X × Y, A, add, zero, neg, mul, one, firstProjection(X, Y), u, x),92 restrict(u, {x} × Y) withring, finiteXY, pushWeights, fibreWeights, fibreSubset,93 agreement, vanishing94have finiteFibre := Finite.subset with finiteXY, fibreSubset95have copy := secondProjection.bijective_singleton at x, Y96have copyValues : ∀ p ∈ {x} × Y, restrict(u, {x} × Y)(p) =97 finiteRowWeights(X, Y, A, u, x)(secondProjection({x}, Y)(p))98intros p hp99obtain⟨bound, a, ha, y, hy, shape⟩:= product.membership.forward with hp100have aIs := singleton.membership.forward with ha101have restricted := restrict.domain_apply at u, X × Y, A, {x} × Y, p102with matrix, fibreSubset, hp103have second : secondProjection({x}, Y)(p) = y104rw shape105exact secondProjection.pair at {x}, Y, a, y with ha, hy106have rowWeight := finiteRowWeights.value at X, Y, A, u, x, y with matrix, hx, hy107rw [restricted, second, rowWeight, shape, aIs]108rfl109have reindexed := finiteRingSum.reindex at {x} × Y, Y, A, add, zero, neg, mul, one,110 restrict(u, {x} × Y), finiteRowWeights(X, Y, A, u, x), secondProjection({x}, Y)111withring, finiteFibre, fibreWeights, rowWeights, copy, copyValues112rw [pushValue, rowValue, fibreSum, reindexed]113rfl114have congruent := finiteRingSum.congr at X, A, add, zero, neg, mul, one,115 finitePushforward(X × Y, X, A, add, zero, neg, mul, one, firstProjection(X, Y), u),116 finiteRowSums(X, Y, A, add, zero, neg, mul, one, u)117withring, finiteX, pushFunction, rows, values118unfold finiteDoubleSum119rw [← push, congruent]120rfl121