forked from Verified-zkEVM/CompPoly
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCore.lean
More file actions
283 lines (235 loc) · 10 KB
/
Copy pathCore.lean
File metadata and controls
283 lines (235 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/-
Copyright (c) 2025 CompPoly. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Quang Dao, Gregor Mitscha-Baude, Derek Sorensen
-/
import Mathlib.Algebra.Polynomial.Inductions
import Mathlib.Algebra.Ring.TransferInstance
import Mathlib.Algebra.Tropical.Basic
import Mathlib.RingTheory.Polynomial.Basic
import CompPoly.Data.Array.Lemmas
import CompPoly.Univariate.Basic
import CompPoly.Univariate.Linear
/-!
# Computable Univariate To `Polynomial`
Conversions between computable univariate polynomials and mathlib `Polynomial`.
-/
open Polynomial
/-- Convert a mathlib `Polynomial` to a `CPolynomial.Raw` by extracting coefficients
up to the degree. -/
def Polynomial.toImpl {R : Type*} [Semiring R] (p : R[X]) : CompPoly.CPolynomial.Raw R :=
match p.degree with
| ⊥ => #[]
| some d => .ofFn (fun i : Fin (d + 1) => p.coeff i)
namespace CompPoly
namespace CPolynomial
open Raw
variable {R : Type*}
variable {Q : Type*}
section ToPolyDefs
/-- Convert a `CPolynomial.Raw` to a (mathlib) `Polynomial`. -/
noncomputable def Raw.toPoly [Semiring R] (p : CPolynomial.Raw R) : Polynomial R :=
p.eval₂ Polynomial.C Polynomial.X
/-- Alternative definition of `toPoly` using `Finsupp`; currently unused. -/
noncomputable def Raw.toPoly' [Semiring R] (p : CPolynomial.Raw R) : Polynomial R :=
Polynomial.ofFinsupp (Finsupp.onFinset (Finset.range p.size) p.coeff (by
intro n hn
rw [Finset.mem_range]
by_contra! h
have h' : p.coeff n = 0 := by simp [h]
contradiction
))
/-- Convert a canonical polynomial to a (mathlib) `Polynomial`. -/
noncomputable def toPoly [Semiring R] (p : CPolynomial R) : Polynomial R := p.val.toPoly
end ToPolyDefs
section ToPoly
variable [Semiring R] [BEq R]
variable [Semiring Q]
namespace Raw
alias ofPoly := Polynomial.toImpl
/-- Evaluation is preserved by `toPoly`. -/
theorem eval_toPoly_eq_eval (x : Q) (p : CPolynomial.Raw Q) : p.toPoly.eval x = p.eval x := by
unfold Raw.toPoly Raw.eval eval₂
rw [← Array.foldl_hom (Polynomial.eval x)
(g₁ := fun acc (t : Q × ℕ) ↦ acc + Polynomial.C t.1 * Polynomial.X ^ t.2)
(g₂ := fun acc (a, i) ↦ acc + a * x ^ i) ]
· congr; exact Polynomial.eval_zero
simp
/-- The coefficients of `p.toPoly` match those of `p`. -/
lemma coeff_toPoly {p : CPolynomial.Raw Q} {n : ℕ} : p.toPoly.coeff n = p.coeff n := by
unfold Raw.toPoly Raw.eval₂
let f := fun (acc: Q[X]) ((a,i): Q × ℕ) ↦ acc + Polynomial.C a * Polynomial.X ^ i
change (Array.foldl f 0 p.zipIdx).coeff n = p.coeff n
let motive (size: ℕ) (acc: Q[X]) := acc.coeff n = if (n < size) then p.coeff n else 0
have zipIdx_size : p.zipIdx.size = p.size := by simp [Array.zipIdx]
suffices h : motive p.zipIdx.size (Array.foldl f 0 p.zipIdx) by
rw [h, ite_eq_left_iff, zipIdx_size]
intro hn
replace hn : n ≥ p.size := by linarith
rw [Raw.coeff, Array.getD_eq_getD_getElem?, Array.getElem?_eq_none hn, Option.getD_none]
apply Array.foldl_induction motive
· simp [motive]
change ∀ (i : Fin p.zipIdx.size) acc, motive i acc → motive (i + 1) (f acc p.zipIdx[i])
unfold motive f
intros i acc h
have i_lt_p : i < p.size := by linarith [i.is_lt]
have : p.zipIdx[i] = (p[i], ↑i) := by simp [Array.getElem_zipIdx]
rw [this, Polynomial.coeff_add, Polynomial.coeff_C_mul, coeff_X_pow, mul_ite, h]
rcases (Nat.lt_trichotomy i n) with hlt | rfl | hgt
· have h1 : ¬ (n < i) := by linarith
have h2 : ¬ (n = i) := by linarith
have h3 : ¬ (n < i + 1) := by linarith
simp [h1, h2, h3]
· simp [i_lt_p]
· have h1 : ¬ (n = i) := by linarith
have h2 : n < i + 1 := by linarith
simp [hgt, h1, h2]
/-- Case analysis for `toImpl`: either the polynomial is zero or has a specific form. -/
lemma toImpl_elim (p : Q[X]) :
(p = 0 ∧ p.toImpl = #[])
∨ (p ≠ 0 ∧ p.toImpl = .ofFn (fun i : Fin (p.natDegree + 1) => p.coeff i)) := by
unfold toImpl
by_cases hbot : p.degree = ⊥
· left
use degree_eq_bot.mp hbot
rw [hbot]
right
use degree_ne_bot.mp hbot
have hnat : p.degree = p.natDegree := Polynomial.degree_eq_natDegree (degree_ne_bot.mp hbot)
simp [hnat]
/-- `toImpl` is a right-inverse of `toPoly`: the round-trip from `Polynomial` is the identity.
This shows `toPoly` is surjective and `toImpl` is injective. -/
theorem toPoly_toImpl {p : Q[X]} : p.toImpl.toPoly = p := by
ext n
rw [coeff_toPoly]
rcases toImpl_elim p with ⟨rfl, h⟩ | ⟨_, h⟩
· simp [h]
rw [h]
by_cases h : n < p.natDegree + 1
· simp [h]
simp only [Array.getD_eq_getD_getElem?, Array.getElem?_ofFn]
simp only [h, reduceDIte, Option.getD_none]
replace h := Nat.lt_of_succ_le (not_lt.mp h)
symm
exact coeff_eq_zero_of_natDegree_lt h
/-- Trimming doesn't change the `toPoly` image. -/
@[grind =]
lemma toPoly_trim [LawfulBEq R] {p : CPolynomial.Raw R} : p.trim.toPoly = p.toPoly := by
ext n
rw [coeff_toPoly, coeff_toPoly, Trim.coeff_eq_coeff]
/-- `toPoly` preserves addition. -/
@[grind =]
theorem toPoly_addRaw {p q : CPolynomial.Raw Q} : (addRaw p q).toPoly = p.toPoly + q.toPoly := by
ext n
rw [Polynomial.coeff_add, coeff_toPoly, coeff_toPoly, coeff_toPoly, add_coeff?]
/-- `toPoly` of a right-scalar multiplication is multiplication by `Polynomial.C r`
on the right. -/
@[grind =]
theorem toPoly_smulRight {p : CPolynomial.Raw Q} {r : Q} :
(smulRight r p).toPoly = p.toPoly * Polynomial.C r := by
ext n
rw [Polynomial.coeff_mul_C, coeff_toPoly, coeff_toPoly]
show (Array.map (fun a => a * r) p).getD n 0 = p.getD n 0 * r
rw [Array.getD_eq_getD_getElem?, Array.getD_eq_getD_getElem?, Array.getElem?_map]
cases h : p[n]? with
| none => simp
| some a => simp
@[grind =]
lemma toPoly_add [LawfulBEq R] (p q : CPolynomial.Raw R) :
(p + q).toPoly = p.toPoly + q.toPoly := by
change (p.add q).toPoly = p.toPoly + q.toPoly; unfold add
rw [toPoly_trim, toPoly_addRaw]
/-- Non-zero polynomials map to non-empty arrays. -/
lemma toImpl_nonzero {p : Q[X]} (hp : p ≠ 0) : p.toImpl.size > 0 := by
rcases toImpl_elim p with ⟨rfl, _⟩ | ⟨_, h⟩
· contradiction
suffices h : p.toImpl ≠ #[] from Array.size_pos_iff.mpr h
simp [h]
/-- The last coefficient of `toImpl p` is the leading coefficient of `p`. -/
lemma getLast_toImpl {p : Q[X]} (hp : p ≠ 0) : let h : p.toImpl.size > 0 := toImpl_nonzero hp;
p.toImpl[p.toImpl.size - 1] = p.leadingCoeff := by
rcases toImpl_elim p with ⟨rfl, _⟩ | ⟨_, h⟩
· contradiction
simp [h]
omit [BEq R] in
/-- `toImpl` lands in the semantic canonical carrier used by `CPolynomial`. -/
@[simp]
theorem isCanonical_toImpl (p : R[X]) : IsCanonical p.toImpl := by
rcases toImpl_elim p with ⟨rfl, h⟩ | ⟨h_nz, _⟩
· simpa [h] using (Trim.isCanonical_empty (R := R))
· intro hp
have hlast : p.toImpl.getLast hp = p.leadingCoeff := by
simpa [Array.getLast] using (getLast_toImpl (Q := R) (p := p) h_nz)
rw [hlast]
exact Polynomial.leadingCoeff_ne_zero.mpr h_nz
/-- `toImpl` produces canonical polynomials (no trailing zeros). -/
@[simp, grind =]
theorem trim_toImpl [LawfulBEq R] (p : R[X]) : p.toImpl.trim = p.toImpl := by
exact Trim.trim_eq_of_isCanonical (isCanonical_toImpl p)
end Raw
/-- `ofArray` preserves the raw polynomial's `toPoly` image. -/
theorem ofArray_toPoly [LawfulBEq R] (p : CPolynomial.Raw R) :
(CPolynomial.ofArray p).toPoly = p.toPoly := by
unfold CPolynomial.ofArray
exact Raw.toPoly_trim
/-- On canonical polynomials, `toImpl` is a left-inverse of `toPoly`.
This shows `toPoly` is a bijection from `CPolynomial R` to `Polynomial R`. -/
@[grind =]
lemma toImpl_toPoly_of_canonical [LawfulBEq R] (p : CPolynomial R) : p.toPoly.toImpl = p := by
suffices h_inj : ∀ q : CPolynomial R, p.toPoly = q.toPoly → p = q by
have : p.toPoly = p.toPoly.toImpl.toPoly := by rw [toPoly_toImpl]
exact
h_inj ⟨p.toPoly.toImpl, isCanonical_toImpl p.toPoly⟩ this
|> congrArg Subtype.val
|>.symm
intro q hpq
apply CPolynomial.ext
apply Trim.isCanonical_ext p.property q.property
intro i
rw [← coeff_toPoly, ← coeff_toPoly]
exact hpq |> congrArg (fun p => p.coeff i)
/-- The round-trip from `CPolynomial.Raw` to `Polynomial` and back yields the canonical form. -/
@[simp, grind =]
theorem Raw.toImpl_toPoly [LawfulBEq R] (p : CPolynomial.Raw R) : p.toPoly.toImpl = p.trim := by
rw [← toPoly_trim]
exact toImpl_toPoly_of_canonical ⟨ p.trim, Trim.isCanonical_trim p⟩
/-- A nonempty trimmed raw polynomial bounds the degree of its `toPoly` image. -/
theorem Raw.toPoly_natDegree_lt_trim_size_of_pos [LawfulBEq R]
(p : CPolynomial.Raw R) (hp : 0 < p.trim.size) :
p.toPoly.natDegree < p.trim.size := by
have hround := Raw.toImpl_toPoly (R := R) p
have hsize : p.toPoly.toImpl.size = p.trim.size := congrArg Array.size hround
rcases Raw.toImpl_elim p.toPoly with ⟨_hzero, himpl⟩ | ⟨_hnz, himpl⟩
· have : p.trim.size = 0 := by
rw [← hsize, himpl]
simp
omega
· have himpl_size : p.toPoly.toImpl.size = p.toPoly.natDegree + 1 := by
simp [himpl]
omega
/-- `toPoly` maps a canonical polynomial to `0` iff the polynomial is `0`. -/
theorem toPoly_eq_zero_iff [LawfulBEq R] (p : CPolynomial R) :
p.toPoly = 0 ↔ p = 0 := by
constructor
· intro hp
apply CPolynomial.ext
calc
(p : CPolynomial.Raw R) = p.toPoly.toImpl := (toImpl_toPoly_of_canonical p).symm
_ = (0 : CPolynomial.Raw R) := by
simp [hp, Polynomial.toImpl]
· rintro rfl
ext n
rw [CPolynomial.toPoly, Raw.coeff_toPoly, Polynomial.coeff_zero]
simpa [CPolynomial.coeff] using (CPolynomial.coeff_zero (R := R) n)
/-- Evaluation is preserved by `toImpl`. -/
@[simp, grind =]
theorem eval_toImpl_eq_eval [LawfulBEq R] (x : R) (p : R[X]) : p.toImpl.eval x = p.eval x := by
rw [← toPoly_toImpl (p := p), toImpl_toPoly, ← toPoly_trim, eval_toPoly_eq_eval]
/-- Evaluation is unchanged by trimming. -/
@[simp, grind =]
lemma Raw.eval_trim_eq_eval [LawfulBEq R] (x : R) (p : CPolynomial.Raw R) :
p.trim.eval x = p.eval x := by
rw [← toImpl_toPoly, eval_toImpl_eq_eval, eval_toPoly_eq_eval]
end ToPoly
end CPolynomial
end CompPoly