1-- Horner evaluation commutes with a ring homomorphism, coefficient by coefficient.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.HornerHom8import Horner9import CoefficientLists1011section Homomorphisms12variable (A add zero neg mul one B plus z minus times unit h leading x : Set)13notationring R := (A, add, zero, neg, mul, one)14notationring S := (B, plus, z, minus, times, unit)15hypothesisring : IsCommRing(R)16hypothesis hom : IsRingHom(h, R, S)17hypothesis initial : leading ∈ R18hypothesis element : x ∈ R1920theorem hornerAt.map (cs : List(A))21shows h(hornerAt{A}(add, mul, leading, x, cs)) = hornerAt{B}(plus, times, h(leading), h(x), listMap{A}(h, cs))22proof23induction cs24| List.nil =>25rw [listMap, hornerAt.nil, hornerAt.nil]26rfl27| List.cons(c, tail) using ih =>28have coefficient : c ∈ R byassumption29have function := IsRingHom.function with hom30have image := IsRingHom.closed with hom, coefficient31have value := hornerAt.closed at A, add, zero, neg, mul, one, leading, x, tail with*32have ordinary := IsCommRing.ring withring33have product := IsRing.mul_closed with ordinary, element, value34have sumMap := IsRingHom.add with hom, coefficient, product35have productMap := IsRingHom.mul with hom, element, value36have source := hornerAt.cons at A, add, mul, leading, x, c, tail with*37have target := hornerAt.cons at B, plus, times, h(leading), h(x), h(c), listMap{A}(h, tail) with*38rw [listMap, source, target]39cc4041end Homomorphisms42theorem hornerValue.cons (A f plus times leading x c : Set) (cs : List(A))43requires coefficient : c ∈ A44shows monicValue{A}(f, plus, times, leading, x, c :: cs) = plus(f(c), times(x, monicValue{A}(f, plus, times, leading, x, cs)))45proof46rw monicValue47rfl4849section Rings50variable (A add zero neg mul one B plus z minus times unit f leading a : Set)51notationring R := (A, add, zero, neg, mul, one)52notationring S := (B, plus, z, minus, times, unit)53hypothesis source : IsCommRing(R)54hypothesis target : IsCommRing(S)55hypothesis hom : IsRingHom(f, R, S)56hypothesis initial : leading ∈ R57hypothesis center : a ∈ R5859theorem hornerAt.map_cons (c : Set) (cs : List(A))60requires coefficient : c ∈ R61shows f(hornerAt{A}(add, mul, leading, a, c :: cs)) = S[f(c) + f(a) * f(hornerAt{A}(add, mul, leading, a, cs))]62proof63have ordinary := IsCommRing.ring with source64have value := hornerAt.closed at A, add, zero, neg, mul, one, leading, a, cs with*65have product := IsRing.mul_closed with ordinary, center, value66have mappedProduct := IsRingHom.mul with hom, center, value67have mappedSum := IsRingHom.add with hom, coefficient, product68rw [hornerAt.cons, mappedSum, mappedProduct]69rfl7071-- Evaluation at an arbitrary extension element; no injectivity is required.72theorem hornerValue.synthetic_division (x c : Set) (cs : List(A))73requires element : x ∈ S74requires coefficient : c ∈ R75shows monicValue{A}(f, plus, times, f(leading), x, c :: cs) = S[f(hornerAt{A}(add, mul, leading, a, c :: cs)) + (x - f(a)) * monicValue{A}(f, plus, times, f(leading), x, hornerQuotient{A}(add, mul, leading, a, cs))]76proof77induction cs generalizing c78| List.nil =>79intros c coefficient80have imageC := IsRingHom.closed with hom, coefficient81have imageA := IsRingHom.closed with hom, center82have imageLeading := IsRingHom.closed with hom, initial83have mapped := hornerAt.map_cons at A, add, zero, neg, mul, one, B, plus, z, minus, times, unit, f, leading, a, c, [] with*84rw [mapped, hornerAt.nil, hornerQuotient, monicValue, monicValue, monicValue]85ring86| List.cons(d, tail) using ih =>87intros c coefficient88have dIn : d ∈ R byassumption89have imageC := IsRingHom.closed with hom, coefficient90have imageA := IsRingHom.closed with hom, center91have imageLeading := IsRingHom.closed with hom, initial92have baseValue := hornerAt.closed at A, add, zero, neg, mul, one, leading, a, d :: tail with*93have imageValue := IsRingHom.closed with hom, baseValue94have quotient := hornerQuotient.closed at A, add, zero, neg, mul, one, leading, a, tail with*95have valueQ := hornerValue.closed at A, add, zero, neg, mul, one, B, plus, z, minus, times, unit, f, x, f(leading), hornerQuotient{A}(add, mul, leading, a, tail) with*96have previous := ih with d, dIn97have mapped := hornerAt.map_cons at A, add, zero, neg, mul, one, B, plus, z, minus, times, unit, f, leading, a, c, d :: tail with*98have outer := hornerValue.cons at A, f, plus, times, f(leading), x, c, d :: tail with*99have quotientValue := hornerValue.cons at A, f, plus, times, f(leading), x, hornerAt{A}(add, mul, leading, a, d :: tail), hornerQuotient{A}(add, mul, leading, a, tail) with*100rw [mapped, hornerQuotient, outer, quotientValue, previous]101ring102103end Rings104