-- SSA/Projects/FullyHomomorphicEncryption/Basic.lean

import Mathlib.RingTheory.Polynomial.Quotient

import Mathlib.RingTheory.Ideal.Defs

import Mathlib.RingTheory.Ideal.Basic

import Mathlib.Data.ZMod.Defs

import Mathlib.Data.ZMod.Basic

import Mathlib.Algebra.MonoidAlgebra.Basic

import Mathlib.Algebra.Polynomial.RingDivision

import Mathlib.Data.Finset.Sort

import Mathlib.Data.List.ToFinsupp

import Mathlib.Data.List.Basic

import Mathlib.Tactic.Cases

import LeanMLIR.Framework

open Polynomial -- for R[X] notation

section CommRing

variable (q t : Nat) [Fact (q > 1)] (n : Nat)

noncomputable def f : (ZMod q)[X] := X^(2^n) + 1

abbrev R := (ZMod q)[X] ⧸ (Ideal.span {f q n})

end CommRing

section Representative

variable (q t n : Nat)

private noncomputable def R.representative' :
    R q n → (ZMod q)[X] := Function.surjInv (R.surjective_fromPoly q n)

noncomputable def R.representative :
    R q n → (ZMod q)[X] := fun x => R.representative' q n x %ₘ (f q n)

variable [Fact (q > 1)]

end Representative

noncomputable def R.repLength {q n} (a : R q n) : Nat := match
  Polynomial.degree a.representative with
    | none => 0
    | some d => d + 1

-- Repository definitions used:
content: ...

-- Repository lemmas used:
content: theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) :\n(l₁ ++ l₂)[i] = l₁[i]

-- Local definitions used:
content: noncomputable def f : (ZMod q)[X] := X^(2^n) + 1

content: abbrev R := (ZMod q)[X] ⧸ (Ideal.span {f q n})

content: private noncomputable def R.representative' :
    R q n → (ZMod q)[X] := Function.surjInv (R.surjective_fromPoly q n)

content: noncomputable def R.representative :
    R q n → (ZMod q)[X] := fun x => R.representative' q n x %ₘ (f q n)

content: noncomputable def R.repLength {q n} (a : R q n) : Nat := match
  Polynomial.degree a.representative with
    | none => 0
    | some d => d + 1

-- Local lemmas used:
content: theorem f_deg_eq : (f q n).degree = 2^n

content: theorem f_monic : Monic (f q n)

-- Target theorem and proof:
theorem R.repLength_lt_n_plus_1 [Fact (q > 1)]: forall a : R q n, a.repLength < 2^n + 1
:= by
  intro a
  simp only [R.repLength, representative]
  have : Polynomial.degree ( R.representative' q n a %ₘ f q n) < 2^n := by
    rw [← f_deg_eq q n]
    apply (Polynomial.degree_modByMonic_lt)
    apply f_monic
  /- simp only [LT.lt] at this -/
  /- let ⟨val, VAL, VAL_EQN⟩ := this -/
  rcases H : degree (R.representative' q n a %ₘ f q n) <;> simp
  case some val' =>
    rw [H] at this
    rw [WithBot.lt_def] at this
    simp at this
    obtain ⟨val'', twon, hval', heq1, heq2⟩ := this
    rw [Option.some_inj.mp heq1]
    have := Option.some_inj.mp heq2
    subst this
    simp_all