1-- Finite real polynomials2-- 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.Real.Polynomial8import FiniteSum9import Power1011-- A polynomial is represented by a real coefficient sequence and a finite12-- cutoff. Only the first n coefficients affect the value.13def polynomialEval (coeff : Set, n : Nat, X : Real) →Set14| coeff, zero, X => realZero15| coeff, succ(k), X =>16 realAddValue(polynomialEval(coeff, k, X), realMulValue(coeff[k], realPow(X, k)))1718-- A polynomial's weighted terms form a real sequence, permitting general19-- finite-sum laws to be applied to polynomial evaluations.20defgraph polynomialTerms (coeff : Set, X : Real) (k ∈ ℕ) : ℝ :=21 realMulValue(coeff[k], realPow(X, k))22requires coefficients : RealSequence(coeff)23proof24have typedK := nat.omega.backward at k with member25have coefficient := RealSequence.value at coeff, k with coefficients26have power := realPow.real at X, k withassumption, typedK27have product := realMulValue.real at coeff[k], realPow(X, k) with28 coefficient, power29unfoldRealat product30unfold realMulValue31unfold call2 at product32exact product3334theorem polynomialTerms.sequence (coeff : Set, X : Real)35requires coefficients : RealSequence(coeff)36shows RealSequence(polynomialTerms(coeff, X))37proof38unfold RealSequence39exact polynomialTerms.function with coefficients4041theorem polynomialEval.zero (coeff : Set) (X : Real)42shows polynomialEval(coeff, 0, X) = (0:R)43proof44rw polynomialEval45rfl4647theorem polynomialEval.succ (coeff : Set) (k : Nat, X : Real)48shows polynomialEval(coeff, succ(k), X) =49 realAddValue(polynomialEval(coeff, k, X),50 realMulValue(coeff[k], realPow(X, k)))51proof52rw polynomialEval53rfl5455theorem polynomialEval.real (coeff : Set, n : Nat, X : Real)56requires coefficients : RealSequence(coeff)57showsReal(polynomialEval(coeff, n, X))58proof59induction n60| zero =>61rw polynomialEval.zero62exact realZero.real63| succ(k) using ih =>64rw polynomialEval.succ65have coefficient := RealSequence.value with coefficients66have power := realPow.real at X, k with*67have term := realMulValue.real with coefficient, power68have typedTerm : Real(realMulValue(coeff[k], realPow(X, k)))69unfold realMulValue70unfold call2 at term71exact term72have sum := realAddValue.real at73 polynomialEval(coeff, k, X), realMulValue(coeff[k], realPow(X, k)) with ih, typedTerm74unfold realAddValue75unfold call2 at sum76exact sum7778theorem polynomialEval.as_sum (coeff : Set, n : Nat, X : Real)79requires coefficients : RealSequence(coeff)80shows polynomialEval(coeff, n, X) = realPartialSum(polynomialTerms(coeff, X), n)81proof82induction n83| zero =>84rw [polynomialEval.zero, realPartialSum.zero]85rfl86| succ(k) using ih =>87have value := polynomialTerms.value with coefficients88rw [polynomialEval.succ, realPartialSum.succ, ih, value]89rfl9091-- Increasing a cutoff adds precisely the weighted terms in its new suffix.92theorem polynomialEval.split_at (coeff : Set, n m : Nat, X : Real)93requires coefficients : RealSequence(coeff)94shows polynomialEval(coeff, add(n, m), X) =95 (polynomialEval(coeff, n, X):R) +96 realPartialSum(shiftedRealSequence(polynomialTerms(coeff, X), n), m)97proof98have terms := polynomialTerms.sequence with coefficients99rw [polynomialEval.as_sum, polynomialEval.as_sum]100exact realPartialSum.split_at with terms101102-- Constant coefficients form a scalar multiple of the geometric polynomial.103theorem polynomialEval.geometric_coefficients (C X : Real, n : Nat)104shows polynomialEval(constantSequence(C), n, X) =105 C * (realPartialSum(geometricTerms(X), n):R)106proof107induction n108| zero =>109rw [polynomialEval.zero, realPartialSum.zero]110ring111| succ(k) using ih =>112have constants := constantSequence.sequence at C113have coefficient := constantSequence.value at C, k with*114have geometric := geometricTerms.value at X, k with*115rw [polynomialEval.succ, realPartialSum.succ, coefficient, geometric, ih]116ring117118theorem polynomialEval.geometric_identity (X : Real, n : Nat)119shows ((1:R) - X) * (polynomialEval(constantSequence(realOne), n, X):R) =120 (1:R) - X ^ n121proof122haveeval:= polynomialEval.geometric_coefficients at realOne, X, n123have sum := realPartialSum.geometric with*124have sequence := geometricTerms.sequence at X125have sumReal := realPartialSum.real with sequence126have one := realMulValue.one_left with sumReal127 rwa [eval, one]128129theorem polynomialEval.add (u v : Set, n : Nat, X : Real)130requires sequenceU : RealSequence(u)131requires sequenceV : RealSequence(v)132shows polynomialEval(addSequence(u, v), n, X) =133 (polynomialEval(u, n, X):R) + (polynomialEval(v, n, X):R)134proof135induction n136| zero =>137rw [polynomialEval.zero, polynomialEval.zero, polynomialEval.zero]138ring139| succ(k) using ih =>140have valueU := RealSequence.value with sequenceU141have valueV := RealSequence.value with sequenceV142have combined := addSequence.sequence with sequenceU, sequenceV143have value := addSequence.value with sequenceU, sequenceV, valueU, valueV144rw [polynomialEval.succ, polynomialEval.succ, polynomialEval.succ, ih, value]145ring146147-- Evaluation depends only on coefficients below the cutoff.148theorem polynomialEval.eq_prefix_aux (X : Real, n : Nat)149shows∀ u, ∀ v, RealSequence(u) → RealSequence(v) →150 (∀ k ∈ ℕ, k < n → u[k] = v[k]) →151 polynomialEval(u, n, X) = polynomialEval(v, n, X)152proof153induction n154| zero =>155intros u v sequenceU sequenceV equal156rw [polynomialEval.zero, polynomialEval.zero]157rfl158| succ(k) using ih =>159intros u v sequenceU sequenceV equal160have naturalK := nat.omega.forward at k withassumption161have before : k < succ(k)162unfold Less163exact LessEqual.reflexive at succ(k) with164 Naturals.constructor.succ at k withassumption165have termEqual := equal with k, naturalK, before166haveprefix : ∀ j ∈ ℕ, j < k → u[j] = v[j]167intros j naturalJ smaller168have typedJ := nat.omega.backward with naturalJ169have below := Less.transitive_LessEqual at j, k, succ(k) with170 typedJ, assumption, Naturals.constructor.succ at k withassumption,171 smaller, LessEqual.successor at k withassumption172exact equal with j, naturalJ, below173have sumEqual := ih with u, v, sequenceU, sequenceV, prefix174rw [polynomialEval.succ, polynomialEval.succ, sumEqual, termEqual]175rfl176177theorem polynomialEval.eq_prefix (u v : Set, n : Nat, X : Real)178requires sequenceU : RealSequence(u)179requires sequenceV : RealSequence(v)180requires equal : ∀ k ∈ ℕ, k < n → u[k] = v[k]181shows polynomialEval(u, n, X) = polynomialEval(v, n, X)182proof183exact polynomialEval.eq_prefix_aux at X, n with184 u, v, sequenceU, sequenceV, equal185186-- Multiplying every coefficient by a real scalar multiplies the value.187theorem polynomialEval.scale (coeff : Set, n : Nat, X C : Real)188requires coefficients : RealSequence(coeff)189shows polynomialEval(mulSequence(constantSequence(C), coeff), n, X) =190 C * (polynomialEval(coeff, n, X):R)191proof192induction n193| zero =>194rw [polynomialEval.zero, polynomialEval.zero]195ring196| succ(k) using ih =>197have constants := constantSequence.sequence at C198have scaled := mulSequence.sequence with constants, coefficients199have constantValue := RealSequence.value with constants200have coeffValue := RealSequence.value with coefficients201have value := mulSequence.value with constants, coefficients, constantValue, coeffValue202have constant := constantSequence.value at C, k with*203rw [polynomialEval.succ, polynomialEval.succ, ih, value, constant]204ring205206theorem polynomialEval.neg (coeff : Set, n : Nat, X : Real)207requires coefficients : RealSequence(coeff)208shows polynomialEval(negSequence(coeff), n, X) =209 -(polynomialEval(coeff, n, X):R)210proof211induction n212| zero =>213rw [polynomialEval.zero, polynomialEval.zero]214ring215| succ(k) using ih =>216have coefficient := RealSequence.value with coefficients217have negated := negSequence.sequence with coefficients218have value := negSequence.value with coefficients, coefficient219rw [polynomialEval.succ, polynomialEval.succ, ih, value]220ring221222theorem polynomialEval.sub (u v : Set, n : Nat, X : Real)223requires sequenceU : RealSequence(u)224requires sequenceV : RealSequence(v)225shows polynomialEval(subSequence(u, v), n, X) =226 (polynomialEval(u, n, X):R) - (polynomialEval(v, n, X):R)227proof228have negated := negSequence.sequence with sequenceV229unfold subSequence230rw [polynomialEval.add, polynomialEval.neg]231unfold realSubValue232rfl233234-- At one, evaluation is exactly the finite sum of the coefficients.235theorem polynomialEval.at_one (coeff : Set, n : Nat)236requires coefficients : RealSequence(coeff)237shows polynomialEval(coeff, n, realOne) = realPartialSum(coeff, n)238proof239induction n240| zero =>241rw [polynomialEval.zero, realPartialSum.zero]242rfl243| succ(k) using ih =>244rw [polynomialEval.succ, realPartialSum.succ, ih, realPow.one]245have coefficient := RealSequence.value with coefficients246have product : realMulValue(coeff[k], realOne) = coeff[k]247have value := realMulValue.one_right with coefficient248unfold realMulValue249unfold call2 at value250exact value251rw product252rfl253254theorem polynomialEval.constant (coeff : Set) (X : Real)255requires coefficients : RealSequence(coeff)256shows polynomialEval(coeff, succ(0), X) = coeff[0]257proof258rw [polynomialEval.succ, polynomialEval.zero, realPow.zero]259have coefficient := RealSequence.value at coeff, 0with coefficients260have product : realMulValue(coeff[0], realOne) = coeff[0]261have value := realMulValue.one_right with coefficient262unfold realMulValue263unfold call2 at value264exact value265rw product266have zero : realAddValue(realZero, coeff[0]) = coeff[0]267have value := realAddValue.zero_left with coefficient268unfold realAddValue269unfold call2 at value270exact value271exact zero272273-- A nonempty coefficient prefix evaluated at zero is its constant term.274theorem polynomialEval.at_zero_succ (coeff : Set, n : Nat)275requires coefficients : RealSequence(coeff)276shows polynomialEval(coeff, succ(n), realZero) = coeff[0]277proof278induction n279| zero =>280exact polynomialEval.constant at coeff, realZero with coefficients281| succ(k) using ih =>282rw [polynomialEval.succ, ih, realPow.zero_succ]283have coefficient := RealSequence.value at coeff, succ(k) with coefficients284have constant := RealSequence.value at coeff, 0with coefficients285have product : realMulValue(coeff[succ(k)], realZero) = realZero286have value := realMulValue.zero_right with coefficient287unfold realMulValue288unfold call2 at value289exact value290rw product291have sum : realAddValue(coeff[0], realZero) = coeff[0]292have value := realAddValue.zero_right with constant293unfold realAddValue294unfold call2 at value295exact value296exact sum297298theorem polynomialEval.linear (coeff : Set) (X : Real)299requires coefficients : RealSequence(coeff)300shows polynomialEval(coeff, succ(succ(0)), X) =301 (coeff[0]:R) + (coeff[succ(0)]:R) * X302proof303rw [polynomialEval.succ, polynomialEval.constant]304have first := RealSequence.value at coeff, 0with coefficients305have second := RealSequence.value at coeff, succ(0) with coefficients306have powerOne : X ^ succ(0) = X307rw [realPow.succ, realPow.zero]308exact realMulValue.one_right withassumption309rw powerOne310unfold realAddValue311unfold realMulValue312unfold call2313rfl314315theorem polynomialEval.quadratic (coeff : Set) (X : Real)316requires coefficients : RealSequence(coeff)317shows polynomialEval(coeff, succ(succ(succ(0))), X) =318 ((coeff[0]:R) + (coeff[succ(0)]:R) * X) +319 (coeff[succ(succ(0))]:R) * (X * X)320proof321rw [polynomialEval.succ, polynomialEval.linear, realPow.two]322have first := RealSequence.value at coeff, 0with coefficients323have second := RealSequence.value at coeff, succ(0) with coefficients324have third := RealSequence.value at coeff, succ(succ(0)) with coefficients325ring326