1-- Synthetic division for ascending coefficient lists over commutative rings.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.Horner8import Field.Algebraic910-- cs stores the lower coefficients; leading is the coefficient of X^length(cs).11def hornerAt {A : Set} (add mul leading x : Set) (cs : List(A)) : Set:=12 monicValue{A}(identity(A), add, mul, leading, x, cs)1314-- For c :: cs, division by X-a has lower coefficients given by this list.15def hornerQuotient {A : Set} (add mul leading a : Set) (cs : List(A)) →Set16| add, mul, leading, a, List.nil => []17| add, mul, leading, a, List.cons(c, tail) => hornerAt{A}(add, mul, leading, a, c :: tail) :: hornerQuotient(add, mul, leading, a, tail)18descends cs1920theorem hornerAt.nil (A add mul leading x : Set)21shows hornerAt{A}(add, mul, leading, x, []) = leading22proof23unfold hornerAt24rw monicValue25rfl2627theorem hornerAt.cons (A add mul leading x c : Set) (cs : List(A))28requires coefficient : c ∈ A29shows hornerAt{A}(add, mul, leading, x, c :: cs) = call2(add, c, call2(mul, x, hornerAt{A}(add, mul, leading, x, cs)))30proof31have value := identity.apply with coefficient32unfold hornerAt33rw [monicValue, value]34rfl3536section Rings37variable (A add zero neg mul one leading : Set)38notationring R := (A, add, zero, neg, mul, one)39hypothesisring : IsCommRing(R)40hypothesis initial : leading ∈ R4142theorem hornerAt.closed (x : Set) (cs : List(A))43requires element : x ∈ R44shows hornerAt{A}(add, mul, leading, x, cs) ∈ R45proof46have hom := IsRingHom.identity withring47unfold hornerAt48exact hornerValue.closed with*4950theorem hornerQuotient.closed (a : Set) (cs : List(A))51requires center : a ∈ R52shows List(A, hornerQuotient{A}(add, mul, leading, a, cs))53proof54induction cs55| List.nil =>56rw hornerQuotient57typecheck58| List.cons(c, tail) using ih =>59have coefficient : c ∈ R byassumption60have value := hornerAt.closed at A, add, zero, neg, mul, one, leading, a, c :: tail with*61rw hornerQuotient62typecheck6364theorem hornerQuotient.length (a : Set) (cs : List(A))65requires center : a ∈ R66shows length{A}(hornerQuotient{A}(add, mul, leading, a, cs)) = length{A}(cs)67proof68induction cs69| List.nil =>70rw hornerQuotient71rfl72| List.cons(c, tail) using ih =>73have coefficient : c ∈ R byassumption74have value := hornerAt.closed at A, add, zero, neg, mul, one, leading, a, c :: tail with*75have quotient := hornerQuotient.closed at A, add, zero, neg, mul, one, leading, a, tail with*76rw [hornerQuotient, length.cons, length.cons, ih]77rfl7879theorem hornerAt.synthetic_division (a x c : Set) (cs : List(A))80requires center : a ∈ R81requires element : x ∈ R82requires coefficient : c ∈ R83shows hornerAt{A}(add, mul, leading, x, c :: cs) = R[hornerAt{A}(add, mul, leading, a, c :: cs) + (x - a) * hornerAt{A}(add, mul, leading, x, hornerQuotient{A}(add, mul, leading, a, cs))]84proof85induction cs generalizing c86| List.nil =>87intros c coefficient88have empty : List(A, []) bytypecheck89rw [hornerQuotient, hornerAt.cons, hornerAt.cons, hornerAt.nil, hornerAt.nil]90ring91| List.cons(d, tail) using ih =>92intros c coefficient93have dIn : d ∈ R byassumption94have quotient := hornerQuotient.closed at A, add, zero, neg, mul, one, leading, a, tail with*95have valueA := hornerAt.closed at A, add, zero, neg, mul, one, leading, a, d :: tail with*96have valueQ := hornerAt.closed at A, add, zero, neg, mul, one, leading, x, hornerQuotient{A}(add, mul, leading, a, tail) with*97have previous := ih with d, dIn98have outerX := hornerAt.cons at A, add, mul, leading, x, c, d :: tail with*99have outerA := hornerAt.cons at A, add, mul, leading, a, c, d :: tail with*100have quotientValue := hornerAt.cons at A, add, mul, leading, x, hornerAt{A}(add, mul, leading, a, d :: tail), hornerQuotient{A}(add, mul, leading, a, tail) with*101rw [outerX, outerA, hornerQuotient, quotientValue, previous]102ring103104end Rings105