1-- Fixed-length polynomial representations and their tracked top coefficients (ZF).2-- 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.Algebra.Polynomial.HornerForms8import HornerArithmetic910-- The terminal coefficient may be zero: n is a bound, not necessarily the degree.11predicate HornerForm (I A add zero neg mul one j p n a : Set) :=12 a ∈ A ∧∃ cs, List(A, cs) ∧ length{A}(cs) = n ∧ p = polynomialFromHorner(I, A, add, zero, neg, mul, one, j, a, cs)1314notationscoped HornerForm(I, R, j, p, n, a) => HornerForm(I, R.carrier, R.add, R.zero, R.neg, R.mul, R.one, j, p, n, a)1516section Rings17variable (I A add zero neg mul one j : Set)18notationring R := (A, add, zero, neg, mul, one)19notationring P := R[I]20hypothesisring : IsCommRing(R)2122theorem HornerForm.of_list (a : Set) (cs : List(A))23requires initial : a ∈ R24shows HornerForm(I, R, j, polynomialFromHorner(I, R, j, a, cs), length{A}(cs), a)25proof26unfold HornerForm27refine⟨initial, cs, assumption, rfl, rfl⟩2829theorem HornerForm.member (p n a : Set)30requires form : HornerForm(I, R, j, p, n, a)31shows p ∈ P32proof33obtain⟨initial, cs, list, size, equal⟩:= form34rw equal35exact polynomialFromHorner.member with*3637theorem HornerForm.constant (a : Set)38requires initial : a ∈ R39shows HornerForm(I, R, j, polynomialCoefficients(I, R)(a), (0 : Nat), a)40proof41have zeroIn : R[0] ∈ R bytypecheck42have empty : List(A, []) bytypecheck43have value := polynomialCoefficients.value at I, A, zero, a with*44have horner := polynomialFromHorner.nil at I, A, add, zero, neg, mul, one, j, a45unfold HornerForm46refine⟨initial, [], empty, ?_⟩47constructor48 . exact length.nil with*49 . cc5051theorem HornerForm.add (p q n a b : Set)52requires first : HornerForm(I, R, j, p, n, a)53requires second : HornerForm(I, R, j, q, n, b)54shows HornerForm(I, R, j, P[p + q], n, R[a + b])55proof56obtain⟨left, cs, csList, csSize, csEqual⟩:= first57obtain⟨right, ds, dsList, dsSize, dsEqual⟩:= second58have ordinary := IsCommRing.ring withring59have initial := IsRing.add_closed with ordinary, left, right60have sizes : length{A}(cs) = length{A}(ds) bycc61have data := hornerAdd.data at A, add, zero, neg, mul, one, cs, ds with*62have equation := polynomialFromHorner.add at I, A, add, zero, neg, mul, one, j, a, b, cs, ds with*63unfold HornerForm64refine⟨initial, hornerAdd{A}(add, zero, cs, ds), data.left, ?_⟩65constructor66 . cc67 . cc6869theorem HornerForm.affine_product (p n a b d : Set)70requires form : HornerForm(I, R, j, p, n, a)71requires constant : b ∈ R72requires slope : d ∈ R73shows HornerForm(I, R, j, P[(polynomialCoefficients(I, R)(b) + polynomialCoefficients(I, R)(d) * polynomialVariable(I, R, j)) * p], succ(n), R[d * a])74proof75have member := HornerForm.member with*76obtain⟨initial, cs, list, size, equal⟩:= form77have ordinary := IsCommRing.ring withring78have zeroIn := IsRing.zero_in_carrier with ordinary79have oneIn : R[1] ∈ R bytypecheck80have leading := IsRing.mul_closed with ordinary, slope, initial81have data := hornerAffineProduct.data at A, add, zero, neg, mul, one, a, b, d, zero, cs with*82have equation := polynomialFromHorner.affine_product at I, A, add, zero, neg, mul, one, j, a, b, d, zero, cs with*83have target := polynomials.comm_ring at I, A, add, zero, neg, mul, one with*84have targetRing := IsCommRing.ring with target85have hom := polynomialCoefficients.hom at I, A, add, zero, neg, mul, one with*86have mapsZero := IsRingHom.zero with hom87have constantIn := IsRingHom.closed with hom, constant88have slopeIn := IsRingHom.closed with hom, slope89have variableIn := polynomialVariable.member at I, A, zero, one, j with*90have linear := IsRing.mul_closed with targetRing, slopeIn, variableIn91have affine := IsRing.add_closed with targetRing, constantIn, linear92have product := IsRing.mul_closed with targetRing, affine, member93have identity := IsRing.add_left_identity with targetRing, product94unfold HornerForm95refine⟨leading, hornerAffineProduct{A}(add, mul, a, b, d, zero, cs), data.left, ?_⟩96constructor97 . cc98 . cc99100theorem HornerForm.scale (p n a b : Set)101requires form : HornerForm(I, R, j, p, n, b)102requires scalar : a ∈ R103shows HornerForm(I, R, j, P[polynomialCoefficients(I, R)(a) * p], n, R[a * b])104proof105obtain⟨initial, cs, list, size, equal⟩:= form106have ordinary := IsCommRing.ring withring107have leading := IsRing.mul_closed with ordinary, scalar, initial108have data := hornerScale.data with scalar109have equation := polynomialFromHorner.scale at I, A, add, zero, neg, mul, one, j, a, b, cs with*110unfold HornerForm111refine⟨leading, hornerScale{A}(mul, a, cs), data.left, ?_⟩112constructor113 . cc114 . cc115116theorem HornerForm.zero (n : Nat)117shows HornerForm(I, R, j, P[0], n, R[0])118proof119induction n120| zero =>121have zeroIn : R[0] ∈ R bytypecheck122have hom := polynomialCoefficients.hom at I, A, add, zero, neg, mul, one with*123have mapsZero := IsRingHom.zero with hom124have form := HornerForm.constant at I, A, add, zero, neg, mul, one, j, zero with*125 rwa ← mapsZero126| succ(k) using ih =>127have zeroIn : R[0] ∈ R bytypecheck128have oneIn : R[1] ∈ R bytypecheck129have target := polynomials.comm_ring at I, A, add, zero, neg, mul, one with*130have targetRing := IsCommRing.ring with target131have hom := polynomialCoefficients.hom at I, A, add, zero, neg, mul, one with*132have mapsZero := IsRingHom.zero with hom133have mapsOne := IsRingHom.one with hom134have variableIn := polynomialVariable.member at I, A, zero, one, j with*135have equation : P[(polynomialCoefficients(I, R)(zero) + polynomialCoefficients(I, R)(one) * polynomialVariable(I, R, j)) *0] = P[0]136rw [mapsZero, mapsOne]137ring138have leading : R[one * zero] = R[0] byring139have form := HornerForm.affine_product at I, A, add, zero, neg, mul, one, j, P[0], k, zero, zero, one with*140 rwa [equation, leading] at form141142theorem HornerForm.leading_coefficient (p n a : Set)143requires index : j ∈ I144requires form : HornerForm(I, R, j, p, n, a)145shows p(monomialShift(I, j, monomialOne(I), n)) = a146proof147obtain⟨initial, cs, list, size, equal⟩:= form148have leading := polynomialFromHorner.leading_coefficient at I, A, add, zero, neg, mul, one, j, a, cs with*149cc150151theorem HornerForm.indexed_sum (n k : Nat) (w c f : Set)152shows (∀ i ∈ k, HornerForm(I, R, j, w(f(i)), n, c(f(i)))) →153 HornerForm(I, R, j, indexedRingSum(w, f, polynomialAdd(I, R), P[0], k), n, indexedRingSum(c, f, add, zero, k))154proof155induction k156| zero =>157intro forms158rw [indexedRingSum, indexedRingSum]159exact HornerForm.zero with*160| succ(r) using ih =>161intro forms162haveprefix : ∀ i ∈ r, HornerForm(I, R, j, w(f(i)), n, c(f(i)))163intros i hi164have next := membership.into_successor at i, r with*165exact forms with i, next166have previous := ih withprefix167have last := forms with r, membership.self_successor at r with*168rw [indexedRingSum, indexedRingSum]169exact HornerForm.add with*170171theorem HornerForm.finite_sum (n : Nat) (V w c : Set)172requires finite : Finite(V)173requires polynomials : w : V → P174requires coefficients : c : V → R175requires forms : ∀ i ∈ V, HornerForm(I, R, j, w(i), n, c(i))176shows HornerForm(I, R, j, finiteRingSum(V, P, w), n, finiteRingSum(V, R, c))177proof178have target := polynomials.comm_ring at I, A, add, zero, neg, mul, one with*179have chosen := finiteRingSum.selected at V, A, add, zero, neg, mul, one, c with*180obtain⟨k, natural, f, enumeration, coefficientSum⟩:= chosen.right181have injective := Bijective.injective with enumeration182have function := Injective.function with injective183have polynomialSum : finiteRingSum(V, P, w) = indexedRingSum(w, f, polynomialAdd(I, R), P[0], k)184exact finiteRingSum.enumeration with*185have entries : ∀ i ∈ k, HornerForm(I, R, j, w(f(i)), n, c(f(i)))186intros i hi187have image := FunctionBetween.apply_closed with*188exact forms with f(i), image189have result : HornerForm(I, R, j, indexedRingSum(w, f, polynomialAdd(I, R), P[0], k), n, indexedRingSum(c, f, add, zero, k))190have closure := HornerForm.indexed_sum at I, A, add, zero, neg, mul, one, j, n, k, w, c, f with*191exact closure with entries192 rwa [polynomialSum, coefficientSum]193194theorem HornerForm.indexed_affine_product (k : Nat) (w b d f : Set)195shows (∀ i ∈ k, b(f(i)) ∈ R ∧ d(f(i)) ∈ R ∧ w(f(i)) = P[polynomialCoefficients(I, R)(b(f(i))) + polynomialCoefficients(I, R)(d(f(i))) * polynomialVariable(I, R, j)]) →196 HornerForm(I, R, j, indexedRingProduct(w, f, polynomialMul(I, R), P[1], k), k, indexedRingProduct(d, f, mul, one, k))197proof198induction k199| zero =>200intro entries201have oneIn : R[1] ∈ R bytypecheck202have hom := polynomialCoefficients.hom at I, A, add, zero, neg, mul, one with*203have mapsOne := IsRingHom.one with hom204have form := HornerForm.constant at I, A, add, zero, neg, mul, one, j, one with*205 rwa [indexedRingProduct, indexedRingProduct, ← mapsOne]206| succ(r) using ih =>207intro entries208haveprefix : ∀ i ∈ r, b(f(i)) ∈ R ∧ d(f(i)) ∈ R ∧ w(f(i)) = P[polynomialCoefficients(I, R)(b(f(i))) + polynomialCoefficients(I, R)(d(f(i))) * polynomialVariable(I, R, j)]209intros i hi210have next := membership.into_successor at i, r with*211exact entries with i, next212have previous := ih withprefix213have last := entries with r, membership.self_successor at r with*214obtain⟨constant, slope, equation⟩:= last215have polynomial := HornerForm.member at I, A, add, zero, neg, mul, one, j, indexedRingProduct(w, f, polynomialMul(I, R), P[1], r), r, indexedRingProduct(d, f, mul, one, r) with*216have data := previous217have topIn := data.left218have result := HornerForm.affine_product at I, A, add, zero, neg, mul, one, j, indexedRingProduct(w, f, polynomialMul(I, R), P[1], r), r, indexedRingProduct(d, f, mul, one, r), b(f(r)), d(f(r)) with*219have target := polynomials.comm_ring at I, A, add, zero, neg, mul, one with*220have hom := polynomialCoefficients.hom at I, A, add, zero, neg, mul, one with*221have constantIn := IsRingHom.closed with hom, constant222have slopeIn := IsRingHom.closed with hom, slope223have zeroIn : R[0] ∈ R bytypecheck224have oneIn : R[1] ∈ R bytypecheck225have variableIn := polynomialVariable.member at I, A, zero, one, j with*226have commute : P[(polynomialCoefficients(I, R)(b(f(r))) + polynomialCoefficients(I, R)(d(f(r))) * polynomialVariable(I, R, j)) * indexedRingProduct(w, f, polynomialMul(I, R), P[1], r)] =227 P[indexedRingProduct(w, f, polynomialMul(I, R), P[1], r) * w(f(r))]228rw equation229ring230have leadingCommutes : R[d(f(r)) * indexedRingProduct(d, f, mul, one, r)] = R[indexedRingProduct(d, f, mul, one, r) * d(f(r))]231ring232rw [commute, leadingCommutes] at result233 rwa [indexedRingProduct, indexedRingProduct]234235theorem HornerForm.finite_affine_product (V w b d : Set)236requires finite : Finite(V)237requires polynomials : w : V → P238requires constants : b : V → R239requires slopes : d : V → R240requires entries : ∀ i ∈ V, w(i) = P[polynomialCoefficients(I, R)(b(i)) + polynomialCoefficients(I, R)(d(i)) * polynomialVariable(I, R, j)]241shows HornerForm(I, R, j, finiteRingProduct(V, P, w), cardinality(V), finiteRingProduct(V, R, d))242proof243have target := polynomials.comm_ring at I, A, add, zero, neg, mul, one with*244have chosen := finiteRingProduct.selected at V, A, add, zero, neg, mul, one, d with*245obtain⟨k, natural, f, enumeration, coefficientProduct⟩:= chosen.right246have injective := Bijective.injective with enumeration247have function := Injective.function with injective248have reverse := converse.bijective at f, k, V with*249have cardinal : HasCardinality(V, k)250unfold HasCardinality251refine⟨natural, ?_⟩252unfold Equipotent253refine⟨converse(f, k, V), reverse⟩254have size := cardinality.eq_of_has at V, k with*255have polynomialProduct : finiteRingProduct(V, P, w) = indexedRingProduct(w, f, polynomialMul(I, R), P[1], k)256exact finiteRingProduct.enumeration with*257have affine : ∀ i ∈ k, b(f(i)) ∈ R ∧ d(f(i)) ∈ R ∧ w(f(i)) = P[polynomialCoefficients(I, R)(b(f(i))) + polynomialCoefficients(I, R)(d(f(i))) * polynomialVariable(I, R, j)]258intros i hi259have image := FunctionBetween.apply_closed at f, k, V, i with*260have constant := FunctionBetween.apply_closed at b, V, A, f(i) with*261have slope := FunctionBetween.apply_closed at d, V, A, f(i) with*262have equation := entries with f(i), image263refine⟨constant, slope, equation⟩264have result : HornerForm(I, R, j, indexedRingProduct(w, f, polynomialMul(I, R), P[1], k), k, indexedRingProduct(d, f, mul, one, k))265have closure := HornerForm.indexed_affine_product at I, A, add, zero, neg, mul, one, j, k, w, b, d, f with*266exact closure with affine267 rwa [polynomialProduct, coefficientProduct, size]268269end Rings270