Skip to content

Commit cd771ff

Browse files
JuanCoRoclaudealexanderlhicks
authored
feat(Coding Theory): Add Gao decoder and correctness (#249)
* Add `ReedSolomon.lean` under `Data/CodingTheory` * Add `GaoDecoder.lean` * Add `ToMathlib/Polynomial/Roots.lean` * Add `ToMathlib/Order/WithBot.lean` * `EuclideanAlgorithm.lean`: Add Bézout invariants for `xgcd` * `DivisionCorrectness.lean`: add `exactDiv_toPoly_iff` * Add `GaoCorrectness.lean` * `CompPoly.lean`: update imports * Place encoder-only results in `ReedSolomon.lean` * chore(coding-theory): address review nits on Gao decoder Cosmetic cleanups to the Gao decoder formalization, no change to statements or proof terms (axioms remain [propext, Classical.choice, Quot.sound]; full build + lint-style pass): - Convert bare `simp`/`simp [...]` to explicit `simp only [...]` at the seven sites in `GaoCorrectness.lean`/`ReedSolomon.lean`, per the repo's performance guidance (bare `simp` pulls in the full simp set). - Rename lemmas mixing camelCase `errorLocator` into snake_case names: `disagreement_subset_errorLocator_zeros`, `disagreement_card_le_errorLocator_natDegree`, `hammingDist_le_errorLocator_natDegree` -> `*_error_locator_*`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(coding-theory): move RS/Gao under Univariate/ReedSolomon Relocate the Reed-Solomon code and Gao decoder from Data/CodingTheory/ to Univariate/ReedSolomon/, matching how the Guruswami-Sudan decoder sits under its representation family (Bivariate/GuruswamiSudan/). Data/ is documented as helper lemmas and support definitions, not user-facing decoders. Namespaces (CompPoly.ReedSolomon[.Gao]) are unchanged; only file paths and the two internal imports move. CompPoly.lean regenerated via update-lib.sh. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Alexander Hicks <25369263+alexanderlhicks@users.noreply.github.com>
1 parent c5ff3bc commit cd771ff

8 files changed

Lines changed: 704 additions & 0 deletions

File tree

CompPoly.lean

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,13 @@ import CompPoly.Multivariate.VarsDegrees
154154
import CompPoly.Multivariate.Wheels
155155
import CompPoly.ToMathlib.Finsupp.Fin
156156
import CompPoly.ToMathlib.MvPolynomial.Equiv
157+
import CompPoly.ToMathlib.Order.WithBot
157158
import CompPoly.ToMathlib.Polynomial.BivariateDegree
158159
import CompPoly.ToMathlib.Polynomial.BivariateEvaluation
159160
import CompPoly.ToMathlib.Polynomial.BivariateMultiplicity
160161
import CompPoly.ToMathlib.Polynomial.BivariateWeightedDegree
161162
import CompPoly.ToMathlib.Polynomial.Div
163+
import CompPoly.ToMathlib.Polynomial.Roots
162164
import CompPoly.Univariate.Barycentric
163165
import CompPoly.Univariate.Basic
164166
import CompPoly.Univariate.BatchEval
@@ -211,6 +213,9 @@ import CompPoly.Univariate.Raw.Division
211213
import CompPoly.Univariate.Raw.Modular
212214
import CompPoly.Univariate.Raw.Ops
213215
import CompPoly.Univariate.Raw.Proofs
216+
import CompPoly.Univariate.ReedSolomon
217+
import CompPoly.Univariate.ReedSolomon.GaoCorrectness
218+
import CompPoly.Univariate.ReedSolomon.GaoDecoder
214219
import CompPoly.Univariate.Roots
215220
import CompPoly.Univariate.Roots.Backend
216221
import CompPoly.Univariate.Roots.Context
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/-
2+
Copyright (c) 2026 CompPoly. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Juan Conejero
5+
-/
6+
import Mathlib.Algebra.Order.Monoid.WithTop
7+
8+
/-!
9+
# Strict additive monotonicity on `WithBot ℕ`
10+
11+
Mathlib's `WithBot.add_lt_add_of_le_of_lt` / `WithBot.add_lt_add_of_lt_of_le` carry `≠ ⊥`
12+
side conditions; the fully strict version below needs none of them over `ℕ`.
13+
Upstreaming candidate.
14+
-/
15+
16+
namespace WithBot
17+
18+
/-- Strict monotonicity of `+` on `WithBot ℕ`: `a < b → c < d → a + c < b + d`. -/
19+
lemma add_lt_add {a b c d : WithBot ℕ} (h1 : a < b) (h2 : c < d) : a + c < b + d := by
20+
cases a <;> cases b <;> cases c <;> cases d <;> simp_all [← WithBot.coe_add]; omega
21+
22+
end WithBot
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/-
2+
Copyright (c) 2026 CompPoly. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Juan Conejero
5+
-/
6+
import Mathlib.Algebra.Polynomial.Roots
7+
8+
/-!
9+
# Root sets of products of distinct linear factors
10+
11+
- `∏ i ∈ s, (X - C (a i))` over injectively indexed points: it divides every polynomial
12+
vanishing on the points (`prod_X_sub_C_dvd`)
13+
- A nonzero polynomial vanishes at boundedly many of the points (`card_eval_zero_le_natDegree`)
14+
- The product divides `(∏ i ∈ E, (X - C (a i))) * (f - b)` whenever `f` and `b` agree off `E`
15+
(`prod_X_sub_C_dvd_prod_mul_sub`).
16+
-/
17+
18+
namespace Polynomial
19+
20+
variable {R : Type*}
21+
22+
/-- `∏ i ∈ s, (X - aᵢ)` divides `p` when the distinct `aᵢ` are all roots of `p`. -/
23+
lemma prod_X_sub_C_dvd [Field R]
24+
{ι : Type*} (s : Finset ι) (a : ι → R) (hinj : Set.InjOn a s)
25+
(p : R[X]) (hr : ∀ i ∈ s, p.eval (a i) = 0) :
26+
(∏ i ∈ s, (X - C (a i))) ∣ p :=
27+
Finset.prod_dvd_of_coprime
28+
(fun _ hi _ hj hij =>
29+
isCoprime_X_sub_C_of_isUnit_sub (sub_ne_zero.mpr (hij <| hinj hi hj ·)).isUnit)
30+
(fun i hi => dvd_iff_isRoot.mpr (hr i hi))
31+
32+
/-- A nonzero `v` vanishes at at most `v.natDegree` of the distinct points. -/
33+
lemma card_eval_zero_le_natDegree [CommRing R] [IsDomain R]
34+
{ι : Type*} [DecidableEq R] (s : Finset ι) (a : ι → R)
35+
(hinj : Set.InjOn a s) (v : R[X]) (hv : v ≠ 0) :
36+
{i ∈ s | v.eval (a i) = 0}.card ≤ v.natDegree := by
37+
rw [← Finset.card_image_of_injOn (hinj.mono (Finset.filter_subset _ _))]
38+
refine card_le_degree_of_subset_roots fun x hx => ?_
39+
obtain ⟨i, hi, rfl⟩ := Finset.mem_image.mp hx
40+
exact (mem_roots hv).mpr (Finset.mem_filter.mp hi).2
41+
42+
/-- When `f, b` agree off `E ⊆ s`, the product `∏_{i∈s}(X−aᵢ)` divides
43+
`(∏_{i∈E}(X−aᵢ))·(f − b)`. -/
44+
lemma prod_X_sub_C_dvd_prod_mul_sub [Field R] {ι : Type*} [DecidableEq ι]
45+
(s E : Finset ι) (hE : E ⊆ s) (a : ι → R) (hinj : Set.InjOn a s)
46+
(f b : R[X]) (hagree : ∀ i ∈ s \ E, f.eval (a i) = b.eval (a i)) :
47+
(∏ i ∈ s, (X - C (a i))) ∣ (∏ i ∈ E, (X - C (a i))) * (f - b) := by
48+
rw [← Finset.prod_sdiff hE, mul_comm]
49+
exact mul_dvd_mul_left _ (prod_X_sub_C_dvd _ _
50+
(hinj.mono (Finset.coe_subset.mpr Finset.sdiff_subset)) _
51+
fun i hi => by rw [eval_sub, hagree i hi, sub_self])
52+
53+
end Polynomial

CompPoly/Univariate/DivisionCorrectness.lean

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,20 @@ theorem mod_toPoly_eq_smul_mod (p q : CPolynomial R) :
552552
←Polynomial.smul_modByMonic, ← Polynomial.smul_eq_C_mul,
553553
mod_toPoly_eq_modByMonic p q hq]
554554

555+
/-- Exact-division bridge: `G.toPoly` factors as `f.toPoly * V.toPoly` iff the computable
556+
`mod` vanishes and the computable division recovers `f`. -/
557+
theorem exactDiv_toPoly_iff (G V f : CPolynomial R) (hV : V.toPoly ≠ 0) :
558+
G.toPoly = f.toPoly * V.toPoly ↔ G.mod V = 0 ∧ (G / V).toPoly = f.toPoly := by
559+
have hdivP : (G / V).toPoly = G.toPoly / V.toPoly := div_toPoly_eq_div _ _
560+
have hmod : G.mod V = 0 ↔ G.toPoly % V.toPoly = 0 := by
561+
rw [← toPoly_eq_zero_iff, mod_toPoly_eq_smul_mod, smul_eq_zero_iff_right
562+
(inv_ne_zero (leadingCoeff_ne_zero ((toPoly_eq_zero_iff V).not.mp hV)))]
563+
refine ⟨fun hGV => ?_, fun ⟨hmod0, hdiv⟩ => ?_⟩
564+
· exact ⟨hmod.mpr (EuclideanDomain.mod_eq_zero.mpr (Dvd.intro_left _ hGV.symm)),
565+
by rw [hdivP, hGV, mul_div_cancel_right₀ _ hV]⟩
566+
· rw [← hdiv, hdivP, _root_.mul_comm,
567+
EuclideanDomain.mul_div_cancel' hV (EuclideanDomain.mod_eq_zero.mp (hmod.mp hmod0))]
568+
555569
private theorem reversal_remainder_toPoly_eq_modByMonic
556570
(M : Raw.MulLowContext R) (p q : CPolynomial R)
557571
(hmonic : (q.leadingCoeff == 1) = true)

CompPoly/Univariate/EuclideanAlgorithm.lean

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@ Authors: Juan Conejero, Valerii Huhnin
55
-/
66
import CompPoly.Univariate.Basic
77
import CompPoly.Univariate.DivisionCorrectness
8+
import CompPoly.ToMathlib.Order.WithBot
89

910
/-!
1011
# Extended Euclidean Algorithm for `CPolynomial`
1112
1213
`xgcd p q` returns a triple `(r, s, t)` with `r = s * p + t * q`.
1314
Early stop when `r.natDegree < threshold`.
1415
The default `threshold = 0` makes `r` the Greatest Common Divisor of `p` and `q`.
16+
17+
For positive thresholds, `xgcd_stopSpec` characterizes the output: `BezoutStopSpec` packages
18+
the Bézout identity together with the residue/cofactor degree bounds at the stopping point,
19+
via the loop invariant `BezoutDegreeInvariant`.
1520
-/
1621

1722
namespace CompPoly
@@ -362,4 +367,143 @@ theorem normXgcd_fst_comm
362367

363368
end CPolynomial
364369

370+
/-! ## Stop specification for positive thresholds
371+
372+
`BezoutDegreeInvariant` is the invariant of the partial-EEA (`xgcd`) loop: each non-stop
373+
step preserves it (`BezoutDegreeInvariant.step`), and at the stopping threshold it yields
374+
the output specification `BezoutStopSpec` (`xgcd_stopSpec`).
375+
376+
This is the classical degree theory of the extended Euclidean algorithm — the row identity
377+
`deg t + deg r' = deg g₀` and the complementary cofactor bound at the stop — i.e. the
378+
contract behind rational-function reconstruction and Reed-Solomon decoding. -/
379+
380+
open Polynomial
381+
open CPolynomial hiding X C add_comm zero_add mul_comm mul_zero mul_one one_mul mul_assoc
382+
383+
variable {F : Type*}
384+
385+
/-- Loop invariant of one partial-EEA (`xgcd`) step. -/
386+
structure BezoutDegreeInvariant [Ring F] (g₀ g₁ : F[X]) (threshold : ℕ)
387+
(r s t r' s' t' : F[X]) : Prop where
388+
bez : r = s * g₀ + t * g₁
389+
bez' : r' = s' * g₀ + t' * g₁
390+
det : IsUnit (s' * t - s * t')
391+
degt : t'.degree < t.degree
392+
degr : r.degree < r'.degree
393+
thr : threshold ≤ r'.degree
394+
395+
/-- `BezoutDegreeInvariant` on the `toPoly` images of a `CPolynomial` reference pair `g₀, g₁` and
396+
the two state triples `(r, s, t)`, `(r', s', t')`. -/
397+
abbrev BezoutDegreeInvariantOf [Ring F] (g₀ g₁ : CPolynomial F) (threshold : ℕ)
398+
(r s t r' s' t' : CPolynomial F) : Prop :=
399+
BezoutDegreeInvariant
400+
g₀.toPoly g₁.toPoly threshold r.toPoly s.toPoly t.toPoly r'.toPoly s'.toPoly t'.toPoly
401+
402+
/-- `BezoutDegreeInvariant` implies `deg t + deg r' = deg g₀`. -/
403+
lemma BezoutDegreeInvariant.degree_complement [CommRing F] [NoZeroDivisors F] [Nontrivial F]
404+
{g₀ g₁ : F[X]} {threshold : ℕ} {r s t r' s' t' : F[X]}
405+
(h : BezoutDegreeInvariant g₀ g₁ threshold r s t r' s' t') :
406+
t.degree + r'.degree = g₀.degree := by
407+
have hid : t * r' - t' * r = (s' * t - s * t') * g₀ := by rw [h.bez, h.bez']; ring
408+
have hdom : (t' * r).degree < (t * r').degree := by simpa using WithBot.add_lt_add h.degt h.degr
409+
rw [← degree_mul, ← degree_sub_eq_left_of_degree_lt hdom, hid, degree_mul,
410+
degree_eq_zero_of_isUnit h.det, zero_add]
411+
412+
/-- One Euclidean step grows the cofactor degree: `deg t < deg (t' - (r'/r) t)`. -/
413+
private lemma cofactor_degree_grows [Field F]
414+
(r r' t t' : F[X]) (hr : r ≠ 0)
415+
(hrr : r.degree < r'.degree) (htt : t'.degree < t.degree) :
416+
t.degree < (t' - r' / r * t).degree := by
417+
have hgt : t.degree < (r' / r * t).degree := by
418+
simpa using WithBot.add_lt_add_right (degree_ne_bot.mpr (ne_zero_of_degree_gt htt))
419+
(pos_of_lt_add_right (degree_add_div hr hrr.le ▸ hrr))
420+
rwa [degree_sub_eq_right_of_degree_lt (htt.trans hgt)]
421+
422+
/-- A non-stop Euclidean step preserves the invariant. -/
423+
lemma BezoutDegreeInvariant.step [Field F]
424+
{g₀ g₁ : F[X]} {threshold : ℕ} {r s t r' s' t' : F[X]}
425+
(h : BezoutDegreeInvariant g₀ g₁ threshold r s t r' s' t') (hr : r ≠ 0)
426+
(hstop : ¬ r.natDegree < threshold) :
427+
BezoutDegreeInvariant
428+
g₀ g₁ threshold (r' - r' / r * r) (s' - r' / r * s) (t' - r' / r * t) r s t := by
429+
refine ⟨(by rw [h.bez, h.bez']; ring), h.bez, ?_, ?_, ?_, ?_⟩
430+
· ring_nf; simpa [mul_comm] using h.det.neg
431+
· exact cofactor_degree_grows _ _ _ _ hr h.degr h.degt
432+
· rw [mul_comm, ← EuclideanDomain.mod_eq_sub_mul_div]; exact degree_mod_lt _ hr
433+
· rw [degree_eq_natDegree hr]; exact_mod_cast not_lt.mp hstop
434+
435+
/-- Stop spec of the partial EEA: `R = S g₀ + T g₁`, residue degree below `threshold`,
436+
and the cofactor `T` nonzero with complementary degree `deg T + threshold ≤ deg g₀`. -/
437+
structure BezoutStopSpec [Semiring F] (g₀ g₁ : F[X]) (threshold : ℕ) (R S T : F[X]) : Prop where
438+
bez : R = S * g₀ + T * g₁
439+
resDeg : R.natDegree < threshold
440+
cofDeg : T.degree + threshold ≤ g₀.degree
441+
cofNe : T ≠ 0
442+
443+
/-- `BezoutStopSpec` on the `toPoly` images of a `CPolynomial` reference pair `g₀, g₁` and
444+
result triple. -/
445+
abbrev BezoutStopSpecOf [Semiring F] (g₀ g₁ : CPolynomial F) (threshold : ℕ)
446+
(result : CPolynomial F × CPolynomial F × CPolynomial F) : Prop :=
447+
BezoutStopSpec
448+
g₀.toPoly g₁.toPoly threshold result.1.toPoly result.2.1.toPoly result.2.2.toPoly
449+
450+
/-- `BezoutDegreeInvariant` implies `xgcdAux`'s output satisfies the stop spec. -/
451+
lemma xgcdAux_stopSpec_of_invariant
452+
[Field F] [BEq F] [LawfulBEq F] (g₀ g₁ : CPolynomial F) (threshold : ℕ) (hthr : 1 ≤ threshold)
453+
(hg₀ : g₀.toPoly ≠ 0) :
454+
∀ (n : ℕ) (r s t r' s' t' : CPolynomial F),
455+
BezoutDegreeInvariantOf g₀ g₁ threshold r s t r' s' t' →
456+
r.natDegree < n →
457+
BezoutStopSpecOf g₀ g₁ threshold (xgcdAux threshold n r s t r' s' t') := by
458+
intro n; induction n with
459+
| zero => intros; contradiction
460+
| succ k ih =>
461+
intro r s t r' s' t' hinv hlt; simp only [xgcdAux]
462+
by_cases hd : r.natDegree < threshold
463+
· rw [if_pos hd]
464+
exact ⟨hinv.bez, natDegree_toPoly r ▸ hd,
465+
(add_le_add le_rfl hinv.thr).trans_eq hinv.degree_complement, fun htz =>
466+
hg₀ (Polynomial.degree_eq_bot.mp (by simp [← hinv.degree_complement, htz]))⟩
467+
· rw [if_neg hd]; have hrne : r ≠ 0 := fun h => hd (by rwa [h]); rw [if_neg (by simpa)]
468+
have hd' : ¬ r.toPoly.natDegree < threshold := natDegree_toPoly r ▸ hd
469+
have hstep := hinv.step ((toPoly_eq_zero_iff _).not.mpr hrne) hd'
470+
simp only [← toPoly_sub_div_mul] at hstep
471+
refine ih _ _ _ _ _ _ hstep ((Nat.lt_succ_iff.mp hlt).trans_lt' ?_)
472+
rw [natDegree_toPoly, natDegree_toPoly, toPoly_sub_div_mul, _root_.mul_comm,
473+
← EuclideanDomain.mod_eq_sub_mul_div]
474+
exact r'.toPoly.natDegree_mod_lt (by omega)
475+
476+
/-- `xgcd`'s output satisfies the stop spec. -/
477+
lemma xgcd_stopSpec
478+
[Field F] [BEq F] [LawfulBEq F] (g₀ g₁ : CPolynomial F) (threshold : ℕ)
479+
(hdeg : g₁.degree < g₀.degree)
480+
(hthr : 1 ≤ threshold) (hthr' : threshold ≤ g₀.degree) :
481+
BezoutStopSpecOf g₀ g₁ threshold (xgcd g₀ g₁ threshold) := by
482+
have hdeg' : g₁.toPoly.degree < g₀.toPoly.degree := by rwa [← degree_toPoly, ← degree_toPoly]
483+
have hg₀poly : g₀.toPoly ≠ 0 := Polynomial.ne_zero_of_degree_gt hdeg'
484+
have hthr'poly : threshold ≤ g₀.toPoly.degree := degree_toPoly g₀ ▸ hthr'
485+
have hstop : ¬ g₀.natDegree < threshold :=
486+
natDegree_toPoly g₀ ▸ (le_natDegree_of_coe_le_degree hthr'poly).not_gt
487+
have hge1 : 1 ≤ g₀.toPoly.natDegree := natDegree_toPoly g₀ ▸ hthr.trans (not_lt.mp hstop)
488+
have hM : g₀.toPoly.natDegree + 1 ≤ g₀.val.size :=
489+
(Polynomial.natDegree_lt_iff_degree_lt hg₀poly).mpr
490+
(degree_toPoly g₀ ▸ mem_degreeLT_iff_size_le.mpr le_rfl)
491+
obtain ⟨N, hN⟩ : ∃ N, g₀.val.size = N + 1 := ⟨g₀.val.size - 1, by omega⟩
492+
obtain ⟨hr1, hs1, ht1⟩ : (g₁ - g₁/g₀*g₀).toPoly = g₁.toPoly ∧ (0 - g₁/g₀*1).toPoly = 0
493+
(1 - g₁/g₀*0).toPoly = 1 := by
494+
refine ⟨?_, ?_, ?_⟩ <;>
495+
rw [toPoly_sub_div_mul, (Polynomial.div_eq_zero_iff hg₀poly).mpr hdeg'] <;>
496+
simp [toPoly_zero, toPoly_one]
497+
simp only [xgcd, hN, xgcdAux]
498+
rw [if_neg hstop, if_neg (by simpa using (toPoly_eq_zero_iff _).not.mp hg₀poly)]
499+
have hinv : BezoutDegreeInvariantOf g₀ g₁ threshold
500+
(g₁ - g₁/g₀*g₀) (0 - g₁/g₀*1) (1 - g₁/g₀*0) g₀ 1 0 := by
501+
rw [BezoutDegreeInvariantOf, hr1, hs1, ht1, toPoly_one, toPoly_zero]
502+
exact ⟨by ring, by ring, by simp, by simp, hdeg', hthr'poly⟩
503+
have hfuel : (g₁ - g₁/g₀*g₀).natDegree < N := by
504+
rw [natDegree_toPoly, hr1]
505+
rcases eq_or_ne g₁.toPoly 0 with hz | hz; rw [hz, Polynomial.natDegree_zero]; omega
506+
exact (Polynomial.natDegree_lt_natDegree hz hdeg').trans_le (by omega)
507+
exact xgcdAux_stopSpec_of_invariant _ _ _ hthr hg₀poly _ _ _ _ _ _ _ hinv hfuel
508+
365509
end CompPoly
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/-
2+
Copyright (c) 2026 CompPoly. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Juan Conejero
5+
-/
6+
import CompPoly.Univariate.ToPoly.Impl
7+
8+
/-!
9+
# Reed-Solomon Codes
10+
11+
The Reed-Solomon code over a finite field: evaluation domain, message polynomial, and
12+
encoder. Decoders live in their own files.
13+
14+
A Reed-Solomon code is fixed by an evaluation domain `(a₀, ..., aₙ₋₁) ∈ Fⁿ` of distinct
15+
points and a message length `k`. The codeword for a message `(m₀, ..., mₖ₋₁) ∈ Fᵏ` is
16+
`cᵢ = f(aᵢ)` with `f(x) = m₀ + m₁ x + ... + mₖ₋₁ xᵏ⁻¹`. For `1 ≤ k < n ≤ #F` this is an
17+
`(n, k, d)` code of minimum distance `d = n − k + 1`.
18+
19+
## Main definitions
20+
21+
* `Domain`: evaluation domain, an array of distinct points of `F`.
22+
* `messagePoly`, `encode`: the message polynomial of a coefficient vector and the encoder
23+
evaluating it on the domain.
24+
-/
25+
26+
namespace CompPoly
27+
28+
namespace ReedSolomon
29+
30+
variable {F : Type*}
31+
32+
/-- Evaluation domain: an array of distinct points in `F`. -/
33+
def Domain (F : Type*) := {a : Array F // a.toList.Nodup}
34+
35+
namespace Domain
36+
37+
/-- Block length. -/
38+
abbrev n (D : Domain F) : ℕ := D.val.size
39+
40+
/-- Indexing into the (nodup) domain array is injective on `Fin D.n`. -/
41+
lemma node_injective (D : Domain F) :
42+
Function.Injective (fun i : Fin D.n => D.val[i]) :=
43+
fun _ _ hxy => Fin.ext ((List.getElem_inj D.property).mp hxy)
44+
45+
end Domain
46+
47+
/-- Message polynomial `m₀ + m₁ x + ... + mₖ₋₁ xᵏ⁻¹` from a length-`k`
48+
coefficient vector `msg`. -/
49+
def messagePoly [Zero F] [BEq F] [LawfulBEq F]
50+
{k : ℕ} (msg : Vector F k) : CPolynomial F :=
51+
⟨(CPolynomial.Raw.mk msg.toArray).trim,
52+
CPolynomial.Raw.Trim.isCanonical_trim _⟩
53+
54+
/-- `(messagePoly msg).degree < k`. -/
55+
lemma messagePoly_degree_lt [Zero F] [BEq F] [LawfulBEq F] {k : ℕ} (msg : Vector F k) :
56+
(messagePoly msg).degree < k :=
57+
CPolynomial.mem_degreeLT_iff_size_le.mpr
58+
((CPolynomial.Raw.Trim.size_le_size msg.toArray).trans_eq msg.size_toArray)
59+
60+
/-- `messagePoly` recovers a `CPolynomial` of degree `< k` from its bounded coefficient vector. -/
61+
lemma messagePoly_ofFn_coeff [Semiring F] [BEq F] [LawfulBEq F] (k : ℕ) (f : CPolynomial F)
62+
(hf : f.degree < k) : messagePoly (Vector.ofFn fun i : Fin k => f.coeff i) = f := by
63+
rw [CPolynomial.eq_iff_coeff]; intro j
64+
rw [messagePoly, CPolynomial.coeff, CPolynomial.Raw.Trim.coeff_eq_coeff, CPolynomial.Raw.coeff,
65+
CPolynomial.Raw.mk, Vector.toArray_ofFn, Array.getD_eq_getD_getElem?, Array.getElem?_ofFn]
66+
split; rfl
67+
rename_i hjk
68+
rw [Option.getD_none, CPolynomial.coeff_toPoly, Polynomial.coeff_eq_zero_of_degree_lt
69+
((CPolynomial.degree_toPoly f ▸ hf).trans_le (Nat.cast_le.mpr (not_lt.mp hjk)))]
70+
71+
/-- Encode: the message polynomial `messagePoly msg` evaluated at the domain `D`. -/
72+
def encode [Semiring F] [BEq F] [LawfulBEq F]
73+
(D : Domain F) {k : ℕ} (msg : Vector F k) : Vector F D.n :=
74+
⟨D.val.map (messagePoly msg).eval, by simp only [Array.size_map, Domain.n]⟩
75+
76+
/-- The codeword entry at node `i` is `messagePoly msg` evaluated at `D.val[i]`. -/
77+
lemma encode_get [Semiring F] [BEq F] [LawfulBEq F] (D : Domain F) {k : ℕ}
78+
(msg : Vector F k) (i : Fin D.n) :
79+
(encode D msg).get i = (messagePoly msg).eval (D.val[i]) := Array.getElem_map ..
80+
81+
end ReedSolomon
82+
83+
end CompPoly

0 commit comments

Comments
 (0)