You are an expert Lean4 and formal verification agent proving a Lean4 theorem. Your output **must** strictly adhere to the specified format.

### Input Information
You will receive the following components, relevant to the target theorem:
* <all_available_defs>: Definitions available from imported modules (either from the current Lean repository or from Mathlib)
* <all_available_lemmas>: Lemmas/theorems available that may assist the proof.
* <local_ctx>...</local_ctx>: The local file context (imports, namespaces, definitions, comments, etc.) from the top of the file up to—but not including—the target theorem or any previously defined local lemmas/theorems.
* <target_theorem>...</target_theorem>: The target theorem statement, ending with `:=`.

**Notes**
1. Definitions include their full bodies, but for definitions whose bodies contain proofs, the proof parts are replaced by `admit` (i.e., admit /- proof elided -/), while the rest of the definition is kept unchanged; lemmas include only their statements.
2. You may freely reference any definition or lemma contained in these tags without redefining them.
3. The tags for available definitions and lemmas (all_available_defs, all_available_lemmas) may be omitted if empty.

### Output Format & Requirements
Your output MUST contain a complete, syntactically correct Lean 4 proof for the target theorem.

**Strict Output Structure:**
<reasoning> 
...
</reasoning> 

<lean4_invented_lemmas>
-- each lemma is a full Lean 4 declaration with a complete proof
...
</lean4_invented_lemmas>

<lean4_proof>
-- Lean 4 proof body of the target theorem
...
</lean4_proof>

**Requirements:**
1. <reasoning> and <lean4_invented_lemmas> are OPTIONAL. Include them only if they aid proof construction.
2. <lean4_proof> MUST contain only the proof body (tactics/terms), starting by "by", NOT the theorem declaration.
3. <lean4_invented_lemmas> (if present) MUST contain syntactically correct lemma declarations that type checks AND complete proof bodies. Defining a lemma using `axiom` is NOT allowed.
4. <reasoning> contains high-level proof planning.
5. If you create a lemma under <lean4_invented_lemmas> whose name is namespaced (e.g., X.Y.Z), you MUST always refer to it using its fully qualified name. Do NOT rely on shortened names. For example, write rw [X.Y.Z], not rw [Z] for invented lemmas.
6. All Lean 4 code MUST be syntactically correct. No `sorry`, `admit`, or non-Lean text. Comments allowed using `--` or `/- ... -/`.
7. Follow the tags and structure EXACTLY. No extra text before, between, or after the sections.
8. Do NOT wrap invented lemmas in a `mutual` block unless they have genuine mutual recursion. Declare each lemma independently.

### Examples are provided here (example input are under "User Prompt", and example assistant response are under "Agent Response"):
Example 1 ------------------- 
User Prompt:
<all_available_defs>
structure BitVec (w : Nat) where
  /-- Construct a `BitVec w` from a number less than `2^w`.
  O(1), because we use `Fin` as the internal representation of a bitvector. -/
  ofFin ::

  /-- Interpret a bitvector as a number less than `2^w`.
  O(1), because we use `Fin` as the internal representation of a bitvector. -/
  toFin : Fin (hPow 2 w)
</all_available_defs>

<all_available_lemmas>
theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) : (l₁ ++ l₂)[i] = l₁[i]
theorem getElem_eq_testBit_toNat (x : BitVec w) (i : Nat) (h : i < w) :
  x[i] = x.toNat.testBit i := rfl
@[simp, grind =] theorem toNat_cons (b : Bool) (x : BitVec w) :
    (cons b x).toNat = (b.toNat <<< w) ||| x.toNat
@[simp, grind =] theorem getLsbD_of_ge (x : BitVec w) (i : Nat) (ge : w ≤ i) : getLsbD x i = false
theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsbD i
</all_available_lemmas>

<local_ctx>
import Mathlib.Computability.NFA
import Mathlib.Data.FinEnum
import Mathlib.Data.Rel
import Mathlib.Data.Vector.Basic
import Blase.AutoStructs.ForLean

open Set
open Mathlib
open SetRel

-- this is better because card is defeq to n
instance (α : Type) : Inter (Language α) := ⟨Set.inter⟩
instance (α : Type) : Union (Language α) := ⟨Set.union⟩

/--
The set of `n`-tuples of bit vectors of an arbitrary width.
-/
structure BitVecs (n : Nat) where
  w : Nat
  bvs : List.Vector (BitVec w) n

def BitVecs.cast (bvs : BitVecs n) (h : n = n') : BitVecs n' :=
  { w := bvs.w, bvs := h ▸ bvs.bvs }

abbrev BitVecs.empty : BitVecs n := ⟨0, List.Vector.replicate n .nil⟩
abbrev BitVecs.singleton {w : Nat} (bv : BitVec w) : BitVecs 1 := ⟨w, bv ::ᵥ .nil⟩
abbrev BitVecs.pair {w : Nat} (bv1 bv2 : BitVec w) : BitVecs 2 := ⟨w, bv1 ::ᵥ bv2 ::ᵥ .nil⟩

/--
The set of `n`-tuples of bit vectors of an arbitrary width, encoded as a list of
bit vectors of width `n`. The width of the encoded bit vectors is the length of
the list.
-/
abbrev BitVecs' (n : Nat) := List (BitVec n)

@[simps]
def dec (bvs' : BitVecs' n) : BitVecs n where
  w := bvs'.length
  bvs := List.Vector.ofFn fun k => BitVec.ofFn fun i => bvs'[i].getLsbD k

@[simp]
</local_ctx>

<target_theorem>
lemma dec_snoc n (bvs' : BitVecs' n) (a : BitVec n) :
  dec (bvs' ++ [a]) =
    { w := bvs'.length + 1
      bvs := List.Vector.ofFn fun k =>
        BitVec.cons (a.getLsbD k) ((dec bvs').bvs.get k) } :=
</target_theorem>

Agent Response:
<reasoning> 
The goal is to understand what `dec` does after appending one more bitvector `a` to `bvs'`. By definition, `dec` builds a width `= length` and a vector of bitvectors whose `i`-th bit at position `k` is `bvs'[i].getLsbD k`. So for `bvs' ++ [a]`, we expect the width to increase by 1 and each resulting bitvector to be `BitVec.cons` of the new bit coming from `a`.

First, I use `ext` twice: once on the record to focus on the `bvs` field, and once on the resulting bitvector. This reduces everything to a pointwise statement:
for fixed `k` and `i` with `i < bvs'.length + 1`,
the bit `(bvs' ++ [a])[i].getLsbD k` should match the bit at index `i` of `BitVec.cons (a.getLsbD k) ((dec bvs').bvs.get k)`.

At this point I need a clean description of indexing into `BitVec.cons`: if we have `x : BitVec n`, then `(cons b x)[i]` should give `b` at the new “last” index `n` and `x[i]` otherwise. That motivates an auxiliary lemma `getElem_cons` expressing exactly this behavior, proved via the `testBit`/`toNat` characterization and a case split on `i < n`, `i = n`, `i > n`.

Using `getElem_cons`, the RHS becomes an `if` on whether `i = bvs'.length`. So I split into cases:
- If `i = bvs'.length`, then `(bvs' ++ [a])[i]` is the appended `a`, so both sides reduce to `a.getLsbD k`.
- If `i ≠ bvs'.length`, the bound `i < bvs'.length + 1` forces `i < bvs'.length`. Then `(bvs' ++ [a])[i] = bvs'[i]` by `List.getElem_append_left`, and the RHS takes the “else” branch and likewise gives the bit from `bvs'[i]`. In both subcases, `simp` (plus the definition of `dec`) finishes.

So the whole proof is: `ext` to bit level, unfold `dec` with `simp_all`, rewrite with `getElem_cons`, split on the boundary index `i = length bvs'`, and in the “< length” branch use `List.getElem_append_left` and bounds to line up both sides.
</reasoning> 

<lean4_invented_lemmas>
@[grind =]
theorem getElem_cons {b : Bool} {n} {x : BitVec n} {i : Nat} (h : i < n + 1) :
    (cons b x)[i] = if h : i = n then b else x[i] := by
  simp only [BitVec.getElem_eq_testBit_toNat, BitVec.toNat_cons, Nat.testBit_or]
  rw [Nat.testBit_shiftLeft]
  rcases Nat.lt_trichotomy i n with i_lt_n | i_eq_n | n_lt_i
  · have p1 : ¬(n ≤ i) := by omega
    have p2 : i ≠ n := by omega
    simp [p1, p2]
  · simp only [i_eq_n, ge_iff_le, Nat.le_refl, decide_true, Nat.sub_self, Nat.testBit_zero,
    Bool.true_and, BitVec.testBit_toNat, BitVec.getLsbD_of_ge, Bool.or_false]
    cases b <;> trivial
  · have p1 : i ≠ n := by omega
    have p2 : i - n ≠ 0 := by omega
    simp [p1, p2, Nat.testBit_bool_toNat]
</lean4_invented_lemmas>

<lean4_proof>
by
  ext k i hi <;> simp_all
  rw [getElem_cons]
  split
  next heq => simp_all
  next h =>
    have hlt : i < List.length bvs' := by simp at hi; omega
    rw [List.getElem_append_left hlt]
    simp
</lean4_proof>

### Your task:
<all_available_defs>
Eq in Init.Prelude
id in Init.Prelude
BitVec in Init.Prelude
Nat in Init.Prelude
BitVec.cast in Init.Data.BitVec.Basic
BitVec.zero in Init.Data.BitVec.Basic
BitVec.append in Init.Data.BitVec.Basic
BitVec.extractLsb in Init.Data.BitVec.Basic
BitVec.xor in Init.Data.BitVec.Basic
AddCommGroup in Mathlib.Algebra.Group.Defs
Int in Init.Data.Int.Basic
Int.negSucc in Init.Data.Int.Basic
Ring in Mathlib.Algebra.Ring.Defs
DivisionRing in Mathlib.Algebra.Field.Defs
NNRat in Mathlib.Data.Rat.Init
NNRat.castRec in Mathlib.Algebra.Field.Defs
Rat in Init.Data.Rat.Basic
Rat.castRec in Mathlib.Algebra.Field.Defs
Field in Mathlib.Algebra.Field.Defs
Finset in Mathlib.Data.Finset.Defs
Finset.range in Mathlib.Data.Finset.Range
Polynomial.C in Mathlib.Algebra.Polynomial.Basic
Polynomial.X in Mathlib.Algebra.Polynomial.Basic
AdjoinRoot.instField in Mathlib.RingTheory.AdjoinRoot
Fintype in Mathlib.Data.Fintype.Defs
Fintype.card in Mathlib.Data.Fintype.Card
Irreducible in Mathlib.Algebra.Group.Irreducible.Defs
List in Init.Prelude
List.Vector in Mathlib.Data.Vector.Defs
NeZero in Init.Data.NeZero
Polynomial in Mathlib.Algebra.Polynomial.Basic
AdjoinRoot in Mathlib.RingTheory.AdjoinRoot
AdjoinRoot.of in Mathlib.RingTheory.AdjoinRoot
AdjoinRoot.root in Mathlib.RingTheory.AdjoinRoot
Eq.mp in Init.Core
CharP in Mathlib.Algebra.CharP.Defs
DivisionSemiring in Mathlib.Algebra.Field.Defs
Finset.Icc in Mathlib.Order.Interval.Finset.Defs
GroupWithZero in Mathlib.Algebra.GroupWithZero.Defs
Ne in Init.Core
Equiv in Mathlib.Logic.Equiv.Defs
Equiv.ofBijective in Mathlib.Logic.Equiv.Defs
Fact in Mathlib.Logic.Basic
Fintype.ofEquiv in Mathlib.Data.Fintype.OfMap
Function.Bijective in Mathlib.Logic.Function.Defs
Function.Injective in Init.Data.Function
Function.Surjective in Init.Data.Function
Polynomial.Monic in Mathlib.Algebra.Polynomial.Degree.Definitions
Polynomial.eval₂ in Mathlib.Algebra.Polynomial.Eval.Defs
Prod in Init.Prelude
Semiring in Mathlib.Algebra.Ring.Defs
Fintype.ofFinite in Mathlib.Data.Fintype.EquivFin
List.Vector.cons in Mathlib.Data.Vector.Defs
List.Vector.nil in Mathlib.Data.Vector.Defs
toFun in ToMathlib.Control.Monad.Hom
Algebra in Mathlib.Algebra.Algebra.Defs
RingHom in Mathlib.Algebra.Ring.Hom.Defs
RingHom.toAlgebra in Mathlib.Algebra.Algebra.Defs
invFun in ToMathlib.Control.Monad.Equiv
left_inv in ToMathlib.Control.Monad.Equiv
right_inv in ToMathlib.Control.Monad.Equiv
False.elim in Init.Prelude
Finite in Mathlib.Data.Finite.Defs
absurd in Init.Prelude
instAlgebra in Mathlib.LinearAlgebra.TensorAlgebra.Basic
instFintypeProd in Mathlib.Data.Fintype.Prod
reduceDIte in Lean.Meta.Tactic.Simp.BuiltinSimprocs.Core
RingHom.id in Mathlib.Algebra.Ring.Hom.Defs
CommSemiring in Mathlib.Algebra.Ring.Defs
Preorder in Mathlib.Order.Defs.PartialOrder
algebraMap in Mathlib.Algebra.Algebra.Defs
CommMonoid in Mathlib.Algebra.Group.Defs
Fin in Init.Prelude
Module.Basis in Mathlib.LinearAlgebra.Basis.Defs
Algebra.algebraMap in Mathlib.Algebra.Algebra.Defs
Module.Basis.mk in Mathlib.LinearAlgebra.Basis.Basic
Set in Mathlib.Data.Set.Defs
Submodule in Mathlib.Algebra.Module.Submodule.Defs
PowerBasis in Mathlib.RingTheory.PowerBasis
gen in VCVio.CryptoFoundations.FiatShamir
minpoly in Mathlib.FieldTheory.Minpoly.Basic
IsUnit in Mathlib.Algebra.Group.Units.Defs
Polynomial.leadingCoeff in Mathlib.Algebra.Polynomial.Degree.Definitions
BitVec.ofNat in Init.Prelude
BitVec.extractLsb' in Init.Data.BitVec.Basic
BitVec.setWidth in Init.Data.BitVec.Basic
cast in Init.Prelude
And in Init.Prelude
OfNat in Init.Prelude
pos in ToMathlib.Control.Comonad.Instances
BitVec.toNat in Init.Prelude
Polynomial.aeval in Mathlib.Algebra.Polynomial.AlgebraMap
MonoidHom in Mathlib.Algebra.Group.Hom.Defs
OneHom in Mathlib.Algebra.Group.Hom.Defs

notation : 10 "GF(" term : 10 ")" => GaloisField term 1

class DCast (α : Sort*) (β : α → Sort*) where
  dcast : ∀ {a a' : α}, a = a' → β a → β a'
  dcast_id : ∀ {a : α}, dcast (Eq.refl a) = id

@[simp]
def sumZeroIffEq (k : ℕ) : ∀ (x y : BTField k),
  x + y = 0 ↔ x = y := (BinaryTowerAux k).2.sumZeroIffEq

@[simp]
def BTField (k : ℕ) := (BinaryTowerAux k).1

def BinaryTowerAux (k : ℕ) : (Σ' (F : Type 0), BinaryTowerResult F k) :=
  match k with
  | 0 => 
    let curBTField := GF(2)
    let newList : List.Vector (GF(2)) 1 := List.Vector.cons (1 : GF(2)) List.Vector.nil
    let specialElement : GF(2) := newList.1.headI
    let firstElementOfVecIsSpecialElement : newList.1.headI = specialElement := rfl
    let specialElementIs1 : specialElement = 1 := by admit /- proof elided -/

structure BinaryTowerInductiveStepResult (k : ℕ) (prevBTField : Type _)
  (prevBTResult : BinaryTowerResult prevBTField k) [instPrevBTFieldIsField : Field prevBTField]
  (prevPoly : Polynomial prevBTField) (F : Type _) where
  binaryTowerResult : BinaryTowerResult F (k+1)
  eq_adjoin : F = AdjoinRoot prevPoly
  u_is_root : Eq.mp (eq_adjoin) binaryTowerResult.specialElement = AdjoinRoot.root prevPoly
  eval_defining_poly_at_root : Eq.mp (eq_adjoin) binaryTowerResult.specialElement^2 +
    Eq.mp (eq_adjoin) binaryTowerResult.specialElement * (of prevPoly) prevBTResult.specialElement
    + 1 = 0

structure BinaryTowerResult (F : Type _) (k : ℕ) where
  vec : (List.Vector F (k + 1))
  instField : (Field F)
  instFintype : Fintype F
  specialElement : F
  specialElementNeZero : NeZero specialElement
  firstElementOfVecIsSpecialElement [Inhabited F] : vec.1.headI = specialElement
  instIrreduciblePoly : (Irreducible (p := (definingPoly specialElement)))
  sumZeroIffEq : ∀ (x y : F), x + y = 0 ↔ x = y
  fieldFintypeCard : Fintype.card F = 2^(2^k)
  traceMapEvalAtRootsIs1 : TraceMapProperty F specialElement k

@[simp]
instance Inhabited (k : ℕ) : Inhabited (BTField k) where
  default := (0 : BTField k)

structure TraceMapProperty (F : Type*) [Field F] (u : F) (k : ℕ) : Prop where
  element_trace : ∑ i ∈ Finset.range (2 ^ k), u ^ (2 ^ i) = 1
  inverse_trace : ∑ i ∈ Finset.range (2 ^ k), (u⁻¹) ^ (2 ^ i) = 1

noncomputable def definingPoly {F : Type*} [instField : Field F] (s : F)
  := X ^ 2 + C s * X + 1

def binary_tower_inductive_step
  (k : Nat)
  (prevBTField : Type _) [Field prevBTField]
  (prevBTResult : BinaryTowerResult prevBTField k)
: Σ' (F : Type _), BinaryTowerInductiveStepResult (k:=k) (prevBTField:=prevBTField)
  (prevBTResult:=prevBTResult) (prevPoly:=definingPoly (F:=prevBTField)
    (instField:=prevBTResult.instField) (s:=prevBTResult.specialElement)) (F:=F)
  (instPrevBTFieldIsField:=prevBTResult.instField) :=

def AlgebraTowerEquiv.symm (e : AlgebraTowerEquiv A B) : AlgebraTowerEquiv B A where
  toRingEquiv := fun i => (e.toRingEquiv i).symm
  commutesLeft' := fun i j h r => by admit /- proof elided -/

structure AlgebraTowerEquiv (A : ι → Type*) [∀ i, CommSemiring (A i)] [a : AlgebraTower A]
  (B : ι → Type*) [∀ i, CommSemiring (B i)] [b : AlgebraTower B]
  where
    toRingEquiv : ∀ i, (A i ≃+* B i)
    commutesLeft' : ∀ (i j : ι) (h : i ≤ j) (r : A i),
      (b.algebraMap (i:=i) (j:=j) (h:=h)) ((toRingEquiv i) r) =
      (toRingEquiv j) (a.algebraMap (i:=i) (j:=j) (h:=h) r)

structure SpecialElementRelation {F_prev : Type*} [Field F_prev] (t1 : F_prev)
  {F_cur : Type*} [Field F_cur] (u : F_cur) [Algebra F_prev F_cur] : Prop where
    sum_inv_eq : u + u⁻¹ = algebraMap F_prev F_cur t1
    h_u_square : u^2 = u * (algebraMap F_prev F_cur t1) + 1

instance irreducible_quadratic_defining_poly_of_traceMap_eq_1
  {F : Type*} [Field F] [Fintype F] [CharP F 2] (s : F) [NeZero s] (k : ℕ)
  (trace_map_prop : TraceMapProperty F s k)
  (fintypeCard : Fintype.card F = 2 ^ (2 ^ k))
  : Irreducible (definingPoly s) :=

instance charP_eq_2_of_add_self_eq_zero {F : Type*} [Field F]
    (sumZeroIffEq : ∀ (x y : F), x + y = 0 ↔ x = y) : CharP F 2 :=
  have h_two : (2 : (F)) = 0 := by admit /- proof elided -/

def coeff.{u} {F : Type u} [Semiring F] (f : F[X][Y]) (i j : ℕ) : F := (f.coeff j).coeff i

instance GF_2_fintype : Fintype (GF(2)) := Fintype.ofFinite (GF(2))

@[simp]
instance AlgebraTower.toIsScalarTower (a : AlgebraTower C) {i j k : ι}
    (h1 : i ≤ j) (h2 : j ≤ k) :
    letI : Algebra (C i) (C j) :=

@[simp]
def AlgebraTower.toAlgebra {i j : ι} (h : i ≤ j) : Algebra (A i) (A j) :=
  (AlgebraTower.algebraMap (i:=i) (j:=j) (h:=h)).toAlgebra

@[simps!]
def MessageIdx.sumEquiv :
    MessageIdx pSpec₁ ⊕ MessageIdx pSpec₂ ≃ MessageIdx (pSpec₁ ++ₚ pSpec₂) where
  toFun := Sum.elim (MessageIdx.inl) (MessageIdx.inr)
  invFun := fun ⟨i, h⟩ => by admit /- proof elided -/

class VerifierOnly (pSpec : ProtocolSpec 1) extends VerifierFirst pSpec

sendMessage
  | ⟨0, _⟩ => fun ⟨stmtIn, oStmtIn, witIn⟩ => do
    let fℓ : OracleFunction 𝔽q β (h_ℓ_add_R_rate := h_ℓ_add_R_rate) (i := ⟨ℓ, by admit /- proof elided -/
    ⟩)
      := witIn.f
    
    let c : L := fℓ ⟨0, by admit /- proof elided -/
    ⟩ 
    pure ⟨c, (stmtIn, oStmtIn, witIn, c)⟩

instance instDCast₃ : DCast₃ Nat (fun n => Fin (n + 1)) (fun n _ => ProtocolSpec n)
    (fun _ k pSpec => pSpec.Transcript k) where
  dcast₃ h h' h'' T := Transcript.cast h
    (by admit /- proof elided -/
    )
    (by admit /- proof elided -/
    )
    T
  dcast₃_id := cast_id

input := fun i => ⟨i.1.2, i.2⟩

example : toList example1 = [1, 2, 3] := rfl

@[simp]
def finRangeTo {k : ℕ} (i : ℕ) : Finset (Fin k) :=
  (List.take i (List.finRange k)).toFinset

MulOneClass in Mathlib.Algebra.Group.Defs

def firstOracleWitnessConsistencyProp (t : MultilinearPoly L ℓ)
    (f₀ : sDomain 𝔽q β h_ℓ_add_R_rate 0 → L) : Prop :=
  let P₀: L[X]_(2 ^ ℓ) := polynomialFromNovelCoeffsF₂ 𝔽q β ℓ (by admit /- proof elided -/
  ) (fun ω => t.val.eval ω)
  
  2 * hammingDist (fun x => P₀.val.eval x.val) f₀ < BBF_CodeDistance ℓ 𝓡 ⟨0, by admit /- proof elided -/
  ⟩

if ok then
          return a

Multiset.map in Mathlib.Data.Multiset.MapFold

Monad in Init.Prelude

Gen_α : ∀ i : Fin (k + 1), ProximityGenerator (indexPowT S φ i) F :=
    fun i => RSGenerator.genRSC (parℓ_type i) (φ_i i) (m - i) (exp i)
  inst6 : ∀ i : Fin (k + 1), Fintype (Gen_α i).parℓ

noncomputable def disagreementSet (f : ι → F) (S : Finset F) (Ans : S → F) : Finset F :=
  Set.toFinset ({x : ι | x.val ∈ S ∧ (ansPoly S Ans).eval x.val ≠ f x}.image Subtype.val)

noncomputable def finalSumcheckOracleReduction :
  OracleReduction
    (oSpec := []ₒ)
    (StmtIn := Statement (L := L) (SumcheckBaseContext L ℓ) (Fin.last ℓ))
    (OStmtIn := OracleStatement 𝔽q β (h_ℓ_add_R_rate := h_ℓ_add_R_rate) ϑ (Fin.last ℓ))
    (WitIn := Witness (L := L) 𝔽q β (h_ℓ_add_R_rate := h_ℓ_add_R_rate) (Fin.last ℓ))
    (StmtOut := FinalSumcheckStatementOut (L := L) (ℓ := ℓ))
    (OStmtOut := OracleStatement 𝔽q β (h_ℓ_add_R_rate := h_ℓ_add_R_rate) ϑ (Fin.last ℓ))
    (WitOut := Unit)
    (pSpec := pSpecFinalSumcheckStep (L := L)) where
  prover := finalSumcheckProver 𝔽q β (ϑ := ϑ) (h_ℓ_add_R_rate := h_ℓ_add_R_rate)
  verifier := finalSumcheckVerifier 𝔽q β (h_ℓ_add_R_rate := h_ℓ_add_R_rate)

Nat.pow in Init.Prelude

private def Fi (B : Finset (Fin n → F)) (i : Fin n) (α : F) : Finset (Fin n → F) :=
  { x | x ∈ B ∧ x i = α }

def soundness (langIn : Set StmtIn) (langOut : Set StmtOut)
    (verifier : Verifier oSpec StmtIn StmtOut pSpec)
    (soundnessError : ℝ≥0) : Prop :=
  ∀ WitIn WitOut : Type,
  ∀ witIn : WitIn,
  ∀ prover : Prover oSpec StmtIn WitIn StmtOut WitOut pSpec,
  ∀ stmtIn ∉ langIn,
    letI reduction := Reduction.mk prover verifier
    [fun ⟨_, stmtOut⟩ => stmtOut ∈ langOut
    | do (simulateQ (impl ++ₛₒ challengeQueryImpl : QueryImpl _ (StateT σ ProbComp))
        <| reduction.run stmtIn witIn).run' (← init)] ≤ soundnessError

while d = 1 do
    x := g n c x
    y := g n c (g n c y)
    d := Nat.gcd (Int.natAbs (x - y)) n

def finOneEquiv : MvPolynomial (Fin 1) R ≃ₐ[R] Polynomial R :=
  (finSuccEquiv R 0).trans (Polynomial.mapAlgEquiv (isEmptyAlgEquiv R (Fin 0)))

noncomputable def H_tilde (H : F[X][Y]) : Polynomial (RatFunc F) :=
  let hᵢ (i : ℕ) := H.coeff i
  let d := H.natDegree
  let W := (RingHom.comp Polynomial.C univPolyHom) (hᵢ d)
  let S : Polynomial (RatFunc F) := Polynomial.X / W
  let H' := Polynomial.eval₂ (RingHom.comp Polynomial.C univPolyHom) S H
  W ^ (d - 1) * H'

instance : PrimeOrder G where
  p := p
  hCard := PrimeOrderWith.hCard

@[reducible, simp]
def relIn : Set (Statement × Unit) := { ⟨stmt, _⟩ | pred stmt }

@[reducible, simp]
def rbrSoundness
    (langIn : Set (Statement × ∀ i, OStatement i))
    (verifier : OracleVerifier oSpec Statement OStatement Bool (fun _ : Empty => Unit) pSpec)
    (rbrSoundnessError : pSpec.ChallengeIdx → ℝ≥0) : Prop :=
  verifier.rbrSoundness init impl langIn acceptRejectOracleRel.language rbrSoundnessError

structure ConcreteBTFieldProps (k : ℕ) extends (ConcreteBTFDivisionRingProps k) where
  
  mul_comm : ∀ a b : ConcreteBTField k, concrete_mul a b = concrete_mul b a

instance {n : ℕ} : ToString (UnverifiedPrattCertificate n) where
  toString := fun c => s!"{repr c}"

noncomputable def proximity_gap_johnson (rho : ℚ) (m : ℕ) : ℝ :=
  (1 : ℝ) - Real.sqrt rho - Real.sqrt rho / (2 * m)

@[reducible]
def SimOracle.Stateless (spec : OracleSpec ι) (specₜ : OracleSpec ιₜ) :=
  QueryImpl spec (OracleComp specₜ)

@[reducible, inline, specialize]
def UniPoly (R : Type*) := Array R

AffineSubspace in Mathlib.LinearAlgebra.AffineSpace.AffineSubspace.Defs

abbrev OracleFunction (i : Fin (ℓ + 1)) : Type _ := sDomain 𝔽q β h_ℓ_add_R_rate ⟨i, by admit /- proof elided -/
    ⟩ → L

instance instHDivConcreteBTF {k : ℕ} : HDiv (ConcreteBTField k) (ConcreteBTField k)
  (ConcreteBTField k) where hDiv a b := a * (concrete_inv b)

@[reducible, inline, specialize, simp]
def srChallengeQueryImpl {Statement : Type} {pSpec : ProtocolSpec n}
    [∀ i, SelectableType (pSpec.Challenge i)] :
    QueryImpl (srChallengeOracle Statement pSpec) ProbComp where
  impl | query i _ => uniformOfFintype (pSpec.Challenge i)

def SkeletonInternalIndex.toNodeIndex {s : Skeleton} (idx : SkeletonInternalIndex s) :
    SkeletonNodeIndex s :=
  match idx with
  | SkeletonInternalIndex.ofInternal => SkeletonNodeIndex.ofInternal
  | SkeletonInternalIndex.ofLeft idxLeft =>
    SkeletonNodeIndex.ofLeft (SkeletonInternalIndex.toNodeIndex idxLeft)
  | SkeletonInternalIndex.ofRight idxRight =>
    SkeletonNodeIndex.ofRight (SkeletonInternalIndex.toNodeIndex idxRight)

@[reducible]
def GroupOpOracle : OracleSpec Unit := fun _ => (ι × ι × ι, Unit)

ReaderT in Init.Prelude

@[reducible]
def coeffs {R : Type*} (p : UniPoly R) : Array R := p

def ranges {n : ℕ} (a : Fin n → ℕ) : (i : Fin n) → Fin (a i) → ℕ :=
  match n with
  | 0 => fun i => elim0 i
  | n + 1 => fun i => by admit /- proof elided -/

Fintype.decidablePiFintype in Mathlib.Data.Fintype.Defs

def duplexSpongeHybridOracle : OracleSpec pSpec.ChallengeIdx :=
  fun i =>
    ⟨StmtIn × ((j : pSpec.MessageIdx) → (j.1 < i.1) → Vector U (pSpec.Lₚᵢ j)),
    Vector U (pSpec.Lᵥᵢ i)⟩

def existPriorSameQuery (log : QueryLog spec) (idx : Fin log.length) : Prop :=
  ∃ j' < idx, log[j'] = log[idx]

Quotient.lift₂ in Init.Core

inst6 : ∀ i : Fin (M + 1), ∀ j : Fin ((P.foldingParam i) + 1), Fintype (Gen_α i j).parℓ

noncomputable def toPoly' (p : UniPoly R) : Polynomial R :=
  Polynomial.ofFinsupp (Finsupp.onFinset (Finset.range p.size) p.coeff (by admit /- proof elided -/
  ))

noncomputable instance : Algebra K (Left ⊗[K] Right) := Algebra.TensorProduct.instAlgebra

def towerAlgebraMap (l r : ℕ) (h_le : l ≤ r) : BTField l →+* BTField r :=

@[ext]
class OracleInterface (Message : Type u) where
  Query : Type v
  Response : Type w
  answer : Message → Query → Response

List.replicate in Init.Data.List.Basic

instance inhabited [Inhabited R] : Inhabited (MlPolyEval R n) :=

@[inline] def pred : AssocNat → AssocNat :=
  fun a => match a.toNat with
  | 0 => zero
  | Nat.succ k => ofNat k

def foldKnowledgeError (i : Fin ℓ) (_ : (pSpecFold (L := L)).ChallengeIdx) : ℝ≥0 :=
  let err_SC := (1 : ℝ≥0) / (Fintype.card L)
  
  let err_BE := if hi : ϑ ∣ (i.val + 1) then
    
    ϑ * (Fintype.card ((sDomain 𝔽q β h_ℓ_add_R_rate)
      ⟨i.val + 1, by admit /- proof elided -/
        ⟩) : ℝ≥0) / (Fintype.card L)
  else 0
  err_SC + err_BE

Algebra.TensorProduct.includeRight in Mathlib.RingTheory.TensorProduct.Basic

def foldStepRelOut (i : Fin ℓ) :
    Set ((Statement (L := L) Context i.succ ×
      (∀ j, OracleStatement 𝔽q β (h_ℓ_add_R_rate := h_ℓ_add_R_rate) ϑ i.castSucc j)) ×
      Witness (L := L) 𝔽q β (h_ℓ_add_R_rate := h_ℓ_add_R_rate) i.succ) :=
  { input | foldStepRelOutProp (mp := mp) (𝓑 := 𝓑) 𝔽q β i input}

Eq in Init.Prelude

Subgroup in Mathlib.Algebra.Group.Subgroup.Defs

def one : ChurchNat α := succ zero

@[inline]
def zero [Zero R] : MlPoly R n := Vector.replicate (2 ^ n) 0

parℓ_type : ∀ i : Fin (M + 1), ∀ _ : Fin ((P.foldingParam i) + 1), Type
  inst5 : ∀ i : Fin (M + 1), ∀ j : Fin ((P.foldingParam i) + 1), Fintype (parℓ_type i j)

instance : NatCast (UniPoly R) := ⟨fun n => UniPoly.C (n : R)⟩

def FullTranscript.removeSalt (transcript : (pSpec.addSalt Salt).FullTranscript) :
    pSpec.FullTranscript :=
  Transcript.removeSalt (pSpec := pSpec) (k := Fin.last n) transcript

@[reducible, inline, specialize]
def Messages' (pSpec : ProtocolSpec n) : Type :=
  ∀ i, (hi : pSpec.dir i = .P_to_V) → pSpec.«Type» i

noncomputable def queryProver :
  OracleProver []ₒ
    (FinalStatement F k) (FinalOracleStatement D x s) (Witness F s d (Fin.last (k + 1)))
    (FinalStatement F k) (FinalOracleStatement D x s) (Witness F s d (Fin.last (k + 1)))
    (pSpec D x l) where
  PrvState
  | _ =>
    (FinalStatement F k × ((i : Fin (k + 2)) → FinalOracleStatement D x s i)) ×
      Witness F s d (Fin.last (k + 1))

abbrev rtake (m : ℕ) (h : m ≤ n)
    (transcript : FullTranscript pSpec) : FullTranscript (pSpec.rtake m h) :=
  Fin.rtake m h transcript

noncomputable def fixFirstVariablesOfMQP (v : Fin (ℓ + 1))
  (H : MvPolynomial (Fin ℓ) L) (challenges : Fin v → L) : MvPolynomial (Fin (ℓ - v)) L :=
  have h_l_eq : ℓ = (ℓ - v) + v := by rw [Nat.add_comm]; exact (Nat.add_sub_of_le v.is_le).symm
  -- Step 1 : Rename L[X Fin ℓ] to L[X (Fin (ℓ - v) ⊕ Fin v)]
  let finEquiv := finSumFinEquiv (m := ℓ - v) (n := v).symm
  let H_sum : L[X (Fin (ℓ - v) ⊕ Fin v)] :=

@[reducible]
def GroupEqOracle : OracleSpec Unit := fun _ => (ι × ι, Bool)

def findFinIdxIfTrue (p : α → Bool) (l : List α) (h : ∃ x ∈ l, p x) : Fin l.length :=
  ⟨List.findIdx' p l, by admit /- proof elided -/
  ⟩

@[simp]
def transpose {m n : ℕ} (matrix : Matrix α m n) : Matrix α n m :=
  ofFn (fun j => ofFn (fun i => (matrix.get i).get j))

noncomputable def discriminant {F : Type} [Field F] [Inhabited F] (f : F[X]) : F :=
  1/f.leadingCoeff * Polynomial.resultant f (Polynomial.derivative f)

cond in Init.Prelude

def finBitVecEquiv {n : ℕ} : Fin (2 ^ n) ≃ BitVec n where
  toFun := fun i => BitVec.ofFin i
  invFun := fun u => u.toFin
  left_inv _ := rfl
  right_inv _ := rfl

noncomputable instance fintype_sDomain (i : Fin r) :
  Fintype (sDomain 𝔽q β h_ℓ_add_R_rate i) :=

List.rtake in Mathlib.Data.List.DropRight

hEq := fun i => by admit /- proof elided -/

@[reducible, inline, specialize]
def MessagesUpTo (k : Fin (n + 1)) (pSpec : ProtocolSpec n) : Type :=
  ∀ i, pSpec.MessageUpTo k i

instance instEquivalenceEquiv : Equivalence (equiv (R := R)) where
  refl := equiv_refl
  symm := equiv_symm
  trans := equiv_trans

@[reducible]
def GroupExpOracle : OracleSpec Unit := fun _ => (ι × ZMod p × ι, Unit)

MvPolynomial.degreeOf in Mathlib.Algebra.MvPolynomial.Degrees

Finset.Nonempty in Mathlib.Data.Finset.Empty

@[reducible, simp]
def knowledgeSoundness (relation : Set (Statement × Bool))
    (verifier : Verifier oSpec Statement Bool pSpec)
    (knowledgeError : ℝ≥0) : Prop :=
  verifier.knowledgeSoundness init impl relation acceptRejectRel knowledgeError

IsUnit in Mathlib.Algebra.Group.Units.Defs

def OracleSpec.proverOracle (StmtIn : Type) {n : ℕ} (pSpec : ProtocolSpec n) :
    OracleSpec pSpec.MessageIdx :=
  fun i => (StmtIn × pSpec.Transcript i.val.castSucc, pSpec.Message i)

def badEventExistsProp
    (stmtIdx : Fin (ℓ + 1)) (oracleIdx : Fin (ℓ + 1))
    (oStmt : ∀ j, (OracleStatement 𝔽q β (h_ℓ_add_R_rate := h_ℓ_add_R_rate) ϑ (i := oracleIdx) j))
    (challenges : Fin stmtIdx → L) : Prop :=
  ∃ j, foldingBadEventAtBlock 𝔽q β (stmtIdx := stmtIdx) (oracleIdx := oracleIdx)
    (oStmt := oStmt) (challenges := challenges) j

noncomputable def intermediateNormVpoly
    
    (i: Fin (ℓ+1)) (k : Fin (ℓ - i + 1)) : L[X] :=
  
  Fin.foldl (n:=k) (fun acc j =>
    (qMap 𝔽q β ⟨(i : ℕ) + (j : ℕ), by admit /- proof elided -/
    ⟩).comp acc) (X)

def map {α β : Fin n → Sort*} (f : (i : Fin n) → α i → β i) (a : (i : Fin n) → α i) :
    (i : Fin n) → β i := fun i => f i (a i)

noncomputable instance {α : Type} (s : Set α) [inst : Finite s] : Fintype s := Fintype.ofFinite _

inductive ScalarVectorOp where | scale

structure DomainSeparatorMismatch where
  message : String
deriving Inhabited, DecidableEq, Repr

def sub (m n : ChurchNat α) : ChurchNat α := sorry

Iff in Init.Core

inductive Program where
</all_available_defs>

<all_available_lemmas>
Nat.sub_add_cancel in Init.Data.Nat.Basic
Nat.sub_zero in Init.Data.Nat.Basic
BitVec.ofNat_toNat in Init.Data.BitVec.Bootstrap
BitVec.setWidth_eq in Init.Data.BitVec.Lemmas
BitVec.toNat_setWidth in Init.Data.BitVec.Bootstrap
Nat.and_two_pow_sub_one_eq_mod in Init.Data.Nat.Bitwise.Lemmas
Nat.shiftRight_zero in Init.Data.Nat.Bitwise.Basic
Nat.add_sub_self_left in Init.Data.Nat.Basic
Nat.mul_two in Init.Data.Nat.Basic
Nat.pow_succ in Init.Data.Nat.Basic
Nat.succ_eq_add_one in Init.Data.Nat.Basic
Nat.mul_comm in Init.Data.Nat.Basic
Nat.two_pow_pred_mul_two in Init.Data.Nat.Lemmas
id_eq in Init.Prelude
Nat.add_sub_cancel in Init.Data.Nat.Basic
Nat.sub_add_comm in Init.Data.Nat.Lemmas
BitVec.extractLsb'_append_eq_of_le in Init.Data.BitVec.Lemmas
Nat.le_refl in Init.Prelude
congrArg in Init.Prelude
eq_mp_eq_cast in Init.PropLemmas
BitVec.append_eq in Init.Data.BitVec.Basic
BitVec.setWidth_append in Init.Data.BitVec.Lemmas
Nat.add_comm in Init.Data.Nat.Basic
Nat.div_eq_sub_mod_div in Init.Data.Nat.Lemmas
Nat.div_lt_of_lt_mul in Init.Data.Nat.Mod
Nat.div_mul_cancel in Init.Data.Nat.Dvd
Nat.dvd_sub_mod in Init.Data.Nat.Lemmas
Nat.mod_eq_of_lt in Init.Data.Nat.Div.Basic
Nat.pow_add in Init.Data.Nat.Lemmas
Nat.shiftLeft_eq in Init.Data.Nat.Bitwise.Basic
Nat.shiftRight_eq_div_pow in Init.Data.Nat.Bitwise.Basic
Nat.mod_le in Init.Data.Nat.Div.Basic
Nat.mod_lt in Init.Prelude
Nat.shiftLeft_add_eq_or_of_lt in Init.Data.Nat.Bitwise.Lemmas
BitVec.eq_of_toNat_eq in Init.Data.BitVec.Bootstrap
BitVec.isLt in Init.Data.BitVec.BasicAux
BitVec.toNat_append in Init.Data.BitVec.Bootstrap
BitVec.toNat_ofNat in Init.Data.BitVec.Bootstrap
Nat.ofNat_pos in Mathlib.Data.Nat.Cast.Order.Ring
OfNat.ofNat_ne_one in Mathlib.Algebra.CharZero.Defs
ne_eq in Init.SimpLemmas
not_false_eq_true in Init.SimpLemmas
pow_right_inj₀ in Mathlib.Algebra.Order.GroupWithZero.Unbundled.Basic
Nat.two_pow_pos in Init.Data.Nat.Basic
Prod.ext_iff in Init.Ext
BitVec.extractLsb_ofNat in Init.Data.BitVec.Lemmas
BitVec.zero_eq in Init.Data.BitVec.Basic
Nat.zero_mod in Init.Data.Nat.Div.Basic
Nat.zero_shiftRight in Init.Data.Nat.Lemmas
Nat.ne_zero_of_lt in Init.Data.Nat.Basic
Nat.one_lt_two_pow_iff in Init.Data.Nat.Lemmas
Nat.shiftRight_eq_zero in Init.Data.Nat.Bitwise.Basic
Nat.one_mod_two_pow_eq_one in Init.Data.Nat.Lemmas
Nat.one_mod_two_pow in Init.Data.Nat.Lemmas
Nat.zero_lt_two in Init.Data.Nat.Basic
pow_pos in Mathlib.Algebra.Order.GroupWithZero.Unbundled.Basic
BitVec.zero_xor in Init.Data.BitVec.Lemmas
BitVec.xor_zero in Init.Data.BitVec.Lemmas
Nat.add_one_sub_one in Init.Data.Nat.Basic
pow_two in Mathlib.Algebra.Group.Defs
BitVec.ofNat_xor in Init.Data.BitVec.Lemmas
BitVec.xor_eq in Init.Data.BitVec.Basic
BitVec.toNat_xor in Init.Data.BitVec.Lemmas
Nat.and_xor_distrib_right in Init.Data.Nat.Bitwise.Lemmas
Nat.shiftRight_xor_distrib in Init.Data.Nat.Bitwise.Lemmas
mul_one in Mathlib.Algebra.Group.Defs
Ne.dite_eq_left_iff in Mathlib.Logic.Basic
Nat.left_eq_add in Init.Data.Nat.Lemmas
RingHom.comp_id in Mathlib.Algebra.Ring.Hom.Defs
cast_eq in Init.Core
one_ne_zero in Mathlib.Algebra.NeZero
BitVec.xor_assoc in Init.Data.BitVec.Lemmas
BitVec.xor_self in Init.Data.BitVec.Lemmas
Polynomial.C_mul' in Mathlib.Algebra.Polynomial.Coeff
Polynomial.aeval_def in Mathlib.Algebra.Polynomial.AlgebraMap
Polynomial.eval₂_X in Mathlib.Algebra.Polynomial.Eval.Defs
Polynomial.eval₂_X_pow in Mathlib.Algebra.Polynomial.Eval.Defs
Polynomial.eval₂_add in Mathlib.Algebra.Polynomial.Eval.Defs
Polynomial.eval₂_one in Mathlib.Algebra.Polynomial.Eval.Defs
Polynomial.eval₂_smul in Mathlib.Algebra.Polynomial.Eval.SMul
mul_comm in Mathlib.Algebra.Group.Defs
MonoidHom.coe_mk in Mathlib.Algebra.Group.Hom.Defs
Nat.sub_one_add_one in Init.Data.Nat.Basic
OneHom.coe_mk in Mathlib.Algebra.Group.Hom.Defs
RingHom.coe_mk in Mathlib.Algebra.Ring.Hom.Defs
cast_cast in Init.PropLemmas
eqRec_eq_cast in Batteries.Logic
Polynomial.aeval_C in Mathlib.Algebra.Polynomial.AlgebraMap
Polynomial.aeval_X in Mathlib.Algebra.Polynomial.AlgebraMap
Polynomial.degree_eq_iff_natDegree_eq in Mathlib.Algebra.Polynomial.Degree.Definitions
Polynomial.degree_ne_bot in Mathlib.Algebra.Polynomial.Degree.Definitions
Polynomial.eq_C_of_natDegree_eq_zero in Mathlib.Algebra.Polynomial.Degree.Operations
Polynomial.eq_X_add_C_of_degree_eq_one in Mathlib.Algebra.Polynomial.Degree.SmallDegree
Polynomial.leadingCoeff_eq_zero in Mathlib.Algebra.Polynomial.Degree.Definitions
Polynomial.natDegree_lt_iff_degree_lt in Mathlib.Algebra.Polynomial.Degree.Definitions
false_or in Init.SimpLemmas
map_add in Mathlib.Algebra.Group.Hom.Defs
map_eq_zero in Mathlib.Algebra.GroupWithZero.Units.Lemmas
map_smul in Mathlib.GroupTheory.GroupAction.Hom
map_zero in Mathlib.Algebra.Group.Hom.Defs
minpoly.unique' in Mathlib.FieldTheory.Minpoly.Basic
ne_zero_of_eq_one in Mathlib.Algebra.NeZero
not_true_eq_false in Init.SimpLemmas
or_false in Init.SimpLemmas

theorem one_le_two_pow_n (n : ℕ) : 1 ≤ 2 ^ n

@[simp]
theorem dcast_eq : dcast (Eq.refl a) b = b

theorem one_le_sub_consecutive_two_pow (n : ℕ): 1 ≤ 2^(n+1) - 2^n

theorem dcast_eq_root_cast (h : a = a') : dcast h b = _root_.cast (congrArg β h) b

theorem dcast_symm (ha : a = a') (hb : dcast ha b = b') : dcast (ha.symm) b' = b

theorem dcast_eq_dcast_iff (h : a = a'') (h' : a' = a'') :
    dcast h b = dcast h' b' ↔ b = dcast (h'.trans h.symm) b'

theorem zero_lt_pow_n (m : ℕ) (n : ℕ) (h_m : m > 0): 0 < m^n

theorem is_unit_iff_deg_0 {R : Type*} [Field R] {p : R[X]} : p.degree = 0 ↔ IsUnit p

lemma definingPoly_is_monic {F : Type*} [Field F] [Fintype F] (s : F) [NeZero s] :
  (definingPoly s).Monic

lemma degree_s_smul_X_add_1 {F : Type*} [Field F] [Fintype F] (s : F) [NeZero s] :
  (C s * (X : (F)[X]) + 1).degree = 1

lemma degree_s_smul_X {F : Type*} [Field F] [Fintype F] (s : F) [NeZero s] :
  (C s * (X : (F)[X])).degree = 1

lemma degree_definingPoly {F : Type*} [Field F] [Fintype F] (s : F) [NeZero s] :
  (definingPoly s).degree = 2

theorem liftContext_runWithLog
    {lens : Context.Lens OuterStmtIn OuterStmtOut InnerStmtIn InnerStmtOut
                        OuterWitIn OuterWitOut InnerWitIn InnerWitOut}
    {outerStmtIn : OuterStmtIn} {outerWitIn : OuterWitIn}
    {P : Prover oSpec InnerStmtIn InnerWitIn InnerStmtOut InnerWitOut pSpec} :
      (P.liftContext lens).runWithLog outerStmtIn outerWitIn
      = do
        let ⟨⟨fullTranscript, innerCtxOut⟩, queryLog⟩ ←
          P.runWithLog.uncurry (lens.proj (outerStmtIn, outerWitIn))
        return ⟨⟨fullTranscript, lens.lift (outerStmtIn, outerWitIn) innerCtxOut⟩, queryLog⟩

theorem coeff_eq_coeff [LawfulBEq R] (p : UniPoly R) (i : ℕ) :
  p.trim.coeff i = p.coeff i

@[grind]
lemma coeff_ne_zero {f q : F[X][Y]} (hg : q * f ≠ 0) : q.coeff ≠ 0

theorem rightpad_toList {a : Array α} {n : Nat} {unit : α} :
    a.rightpad n unit = mk (a.toList.rightpad n unit)

@[simp]
theorem reduction_completeness (h : init.neverFails)
    (hRel : ∀ stmtIn witIn, (stmtIn, witIn) ∈ relIn ↔
      (mapStmt stmtIn, mapWit stmtIn witIn) ∈ relOut) :
    (reduction oSpec mapStmt mapWit).perfectCompleteness init impl relIn relOut

lemma isPrimitiveRoot_twoAdicGenerator (bits : Fin (twoAdicity + 1)) :
    IsPrimitiveRoot (twoAdicGenerators[bits]) (2 ^ (bits : Nat))

theorem eq_trans {α : Type u} (mx my mz : m α) (hxy : eq mx my) (hyz : eq my mz) : eq mx mz

@[simp]
lemma length_eq_domain_size (inj : Function.Injective α) :
  length (ReedSolomon.code ⟨α, inj⟩ deg) = m

@[simp]
lemma finite_possibleDeltas : (possibleDeltas U V).Finite

lemma ϑ_sub_one_le_self : ϑ - 1 < ϑ

lemma divide_by_2_def {s : Finset ℕ} :
    divide_by_2 s = (erase_odd s).image (fun n => n / 2)

private lemma sum_of_not_equals :
  ∑ x ∈ B ×ˢ B with x.1 ≠ x.2, (if x.1 i ≠ x.2 i then 1 else 0)
  =
  2 * choose_2 #B - 2 * ∑ α, choose_2 (K B i α)

lemma getBit_eq_zero_or_one {k n : Nat} :
  getBit k n = 0 ∨ getBit k n = 1

theorem add_coeff? (p q : UniPoly Q) (i : ℕ) :
  (add_raw p q).coeff i = p.coeff i + q.coeff i

theorem support_finSuccEquivNth_nonempty {f : MvPolynomial (Fin (n + 1)) R} (h : f ≠ 0) :
    (finSuccEquivNth R p f).support.Nonempty

@[simp]
theorem minPoly_of_powerBasisSucc_generator (k : ℕ) :
  (minpoly (BTField k) (powerBasisSucc k).gen) = X^2 + (Z k) • X + 1

@[simp]
lemma domain_def : (spec α).domain () = (α × α)

theorem append_run (tr : (pSpec₁ ++ₚ pSpec₂).FullTranscript) :
      (V₁.append V₂).run stmt tr =
        (do
          let stmt₂ ← V₁.run stmt tr.fst
          let stmt₃ ← V₂.run stmt₂ tr.snd
          return stmt₃)

theorem zero_add : 0 + p = p

theorem fconcat_left_injective {α : Fin n → A} {β : A} (a : F β) :
    Function.Injective (fun v : (i : Fin n) → F (α i) => fconcat v a)

@[simp]
lemma degree_polynomialOfCoeffs_deg_lt_deg :
  (polynomialOfCoeffs coeffs).degree < deg

@[simp]
lemma natDegree_polynomialOfCoeffs_deg_lt_deg
  [NeZero deg] {coeffs : Fin deg → F} :
  (polynomialOfCoeffs coeffs).natDegree < deg

Fin.eta in Init.Data.Fin.Lemmas

theorem relayOracleReduction_perfectCompleteness (hInit : init.neverFails) (i : Fin ℓ)
    (hNCR : ¬ isCommitmentRound ℓ ϑ i) :
    OracleReduction.perfectCompleteness
      (pSpec := pSpecRelay)
      (relIn := foldStepRelOut (mp := mp) 𝔽q β (ϑ := ϑ) (h_ℓ_add_R_rate := h_ℓ_add_R_rate)
        (𝓑 := 𝓑) i)
      (relOut := roundRelation (mp := mp) 𝔽q β (ϑ := ϑ) (h_ℓ_add_R_rate := h_ℓ_add_R_rate)
        (𝓑 := 𝓑) i.succ)
      (oracleReduction := relayOracleReduction 𝔽q β (h_ℓ_add_R_rate := h_ℓ_add_R_rate)
        i hNCR)
      (init := init)
      (impl := impl)

beq_iff_eq in Init.Core

omit [Fintype L] [DecidableEq L] in
theorem Polynomial.comp_same_inner_eq_if_same_outer (f g : L[X]) (h_f_eq_g : f = g) :
  ∀ x, f.comp x = g.comp x

theorem eqPolynomial_zeroOne (r : σ → Fin 2) : (eqPolynomial r : MvPolynomial σ R) =
    ∏ i : σ, if r i = 0 then 1 - X i else X i

theorem reduction_perfectCompleteness (hInit : init.neverFails) :
    (reduction R n deg D oSpec i).perfectCompleteness init impl
    (relationRound R n deg D i.castSucc) (relationRound R n deg D i.succ)

@[simp]
theorem dflatten_two_eq_append {n : Fin 2 → ℕ} {motive : (k : Fin (vsum n)) → Sort*}
    {v : (i : Fin 2) → (j : Fin (n i)) → motive (embedSum i j)} :
    dflatten (motive := motive) v = dappend (v 0) (v 1)

theorem singleEqPolynomial_zeroOne (r : Fin 2) (x : MvPolynomial σ R) :
    singleEqPolynomial (r : R) x = if r = 0 then 1 - x else x

Fin.append_right in Mathlib.Data.Fin.Tuple.Basic

theorem unique_linear_decomposition_succ (k : ℕ) :
  letI : Algebra (ConcreteBTField k) (ConcreteBTField (k+1)) :=
    ConcreteBTFieldAlgebra (l:=k) (r:=k+1) (h_le:=by omega)
  ∀ (x : ConcreteBTField (k+1)), ∃! (p : ConcreteBTField k × ConcreteBTField k),
    x = join_via_add_smul (k+1) (by omega) p.1 p.2

theorem join_add_join {k : ℕ} (h_pos : k > 0) (hi₀ lo₀ hi₁ lo₁ : ConcreteBTField (k - 1)) :
  《 hi₀, lo₀ 》 + 《 hi₁, lo₁ 》 = 《 hi₀ + hi₁, lo₀ + lo₁ 》

lemma rank_eq_rowRank : U.rank = rowRank U

theorem seqCompose_perfectCompleteness (hInit : init.neverFails)
    (rel : (i : Fin (m + 1)) → Set ((Stmt i × ∀ j, OStmt i j) × Wit i))
    (R : ∀ i, OracleReduction oSpec (Stmt i.castSucc) (OStmt i.castSucc) (Wit i.castSucc)
      (Stmt i.succ) (OStmt i.succ) (Wit i.succ) (pSpec i))
    (h : ∀ i, (R i).perfectCompleteness init impl (rel i.castSucc) (rel i.succ)) :
      (OracleReduction.seqCompose Stmt OStmt Wit R).perfectCompleteness
        init impl (rel 0) (rel (Fin.last m))

theorem cast_eq_dcast {m n : ℕ} (h : m = n) (a : Fin m) :
    Fin.cast h a = dcast h a

@[simp]
theorem BTField.Basis_cast_dest_apply {ι : Type*} (α β γ : ℕ) (h_le1 : α ≤ β) (h_le2 : α ≤ γ)
    (h_eq : β = γ) {k : ι} (b : @Basis ι (BTField α) (BTField β) _ _
    (@binaryAlgebraTower (l := α) (r := β) (h_le := h_le1)).toModule) :
    let castBasis : @Basis ι (BTField α) (BTField γ) _ _
      (@binaryAlgebraTower (l := α) (r := γ) (h_le := h_le2)).toModule :=
      cast (by
        exact Basis_cast_dest_eq α γ β h_le2 h_le1 h_eq
      ) b
    (castBasis k) = cast (by exact BTField.cast_BTField_eq β γ h_eq) (b k)

@[simp]
theorem fcons_succ {β : Fin n → A} (a : F α) (v : (i : Fin n) → F (β i)) (i : Fin n) :
    fcons a v i.succ = cast (by simp [vcons_succ]) (v i)

lemma matching_set_is_a_sub_of_coeffs_of_close_proximity
  (h_gs : ModifiedGuruswami m n k ωs Q u₀ u₁)
  : matching_set k ωs δ u₀ u₁ h_gs ⊆ coeffs_of_close_proximity k ωs δ u₀ u₁

lemma concrete_one_mul0 (a : ConcreteBTField 0) :
  concrete_mul (one (k:=0)) a = a

omit [Finite F] in
lemma domain_surjective (i : ℕ) : i ≤ n → Function.Surjective (domain D n i)

lemma trim_add_trim [LawfulBEq R] (p q : UniPoly R) : p.trim + q = p + q

lemma le_sInf_of_LB {S : Set ℕ} (hne : S.Nonempty) {i : ℕ}
    (hLB : ∀ s ∈ S, i ≤ s) : i ≤ sInf S

theorem BerlekampWelchCondition_iff_Solution {e k : ℕ} [NeZero n]
  {ωs f : Fin n → F} {v : Fin (2 * e + k) → F}
  :
  IsBerlekampWelchSolution e k ωs f v
  ↔
  (BerlekampWelchCondition e k ωs f (solutionToE e k v) (solutionToQ e k v))

theorem vcons_injective2 : Function.Injective2 (@vcons α n)

lemma toOutCodewordsCount_mul_ϑ_eq_i_succ (i : Fin ℓ) (hCR : isCommitmentRound ℓ ϑ i) :
  (toOutCodewordsCount ℓ ϑ i.castSucc) * ϑ = i.val + 1

@[simp]
theorem cast_dir_idx_symm {hn} (hSpec : pSpec₁.cast hn = pSpec₂) {i : Fin n₂} :
    pSpec₁.dir (Fin.cast hn.symm i) = pSpec₂.dir i

lemma definingPoly_is_not_unit {F : Type*} [Field F] [Fintype F] (s : F) [NeZero s] :
  ¬IsUnit (definingPoly s)

@[simp]
theorem oracleReduction_perfectCompleteness (hInit : init.neverFails) :
    (oracleReduction oSpec Statement OStatement Witness).perfectCompleteness init impl rel rel

lemma zero_is_0 {k : ℕ} : (zero (k:=k)) = (0 : ConcreteBTField k)

@[simp]
lemma coeff_truncate : (truncate p n).coeff k = if k < n then p.coeff k else 0

@[simp]
lemma solutionToE_zero_eq_C {v : Fin (2 * 0 + k) → F} :
  solutionToE 0 k v = C (1 : F)

theorem get_rtake_eq_rtake_get_comp_cast {α : Type*} {m : ℕ} (l : List α) (h : m ≤ l.length) :
    (l.rtake m).get = rtake m h l.get ∘ Fin.cast (by simp [List.rtake]; omega)

Nat.le_add_left in Init.Data.Nat.Basic

theorem fconcat_injective2 {α : Fin n → A} {β : A} :
    Function.Injective2 (@fconcat A F n α β)

lemma getMiddleBits_eq_mod {n offset len : ℕ} :
  getMiddleBits offset len n = (n >>> offset) % (2 ^ len)

theorem append_run (stmt : Stmt₁) (wit : Wit₁) :
      (P₁.append P₂).run stmt wit = (do
        let ⟨transcript₁, stmt₂, wit₂⟩ ← liftM (P₁.run stmt wit)
        let ⟨transcript₂, stmt₃, wit₃⟩ ← liftM (P₂.run stmt₂ wit₂)
        return ⟨transcript₁ ++ₜ transcript₂, stmt₃, wit₃⟩)

@[simp]
theorem Z_succ_eq_adjointRoot_root (k : ℕ) : Z (k+1) = AdjoinRoot.root (poly k)

omit [DecidableEq 𝔽q] hF₂ h_β₀_eq_1 in
lemma getSDomainBasisCoeff_of_sum_repr [NeZero R_rate] (i : Fin (ℓ + 1))
    (x : (sDomain 𝔽q β h_ℓ_add_R_rate) ⟨i, by omega⟩)
    (x_coeffs : Fin (ℓ + R_rate - i) → 𝔽q)
    (hx : x = ∑ j_x, (x_coeffs j_x) • (sDomain_basis 𝔽q β
      h_ℓ_add_R_rate (i := ⟨i, by omega⟩) (h_i := by
        simp only; apply Nat.lt_add_of_pos_right_of_le; omega) j_x).val) :
    ∀ (j: Fin (ℓ + R_rate - i)), ((sDomain_basis 𝔽q β
      h_ℓ_add_R_rate (i := ⟨i, by omega⟩) (h_i := by
        simp only; apply Nat.lt_add_of_pos_right_of_le; omega)).repr x) j = x_coeffs j

@[simp]
theorem FullData.leftSubtree_internal {α} {s_left s_right : Skeleton}
    (value : α) (left : FullData α s_left) (right : FullData α s_right) :
    (FullData.internal value left right).leftSubtree = left

@[simp]
theorem cast_id : MessageIdx.cast (Eq.refl n₁) rfl = (id : pSpec₁.MessageIdx → _)

@[simp]
theorem shift_left_mem {s : Finset ℕ} {d : ℕ} : d ∈ shift_left s ↔ (d + 1) ∈ s

theorem Fin.sum_univ_odd_even {n : ℕ} {M : Type*} [AddCommMonoid M] (f : ℕ → M) :
    (∑ i : Fin (2 ^ n), f (2 * i)) + (∑ i : Fin (2 ^ n), f (2 * i + 1))
    = ∑ i: Fin (2 ^ (n+1)), f i

theorem zero_add {n : ℕ} (a : CNat (n + 1)) : 0 + a = a

theorem sum_of_pow_exp_of_2 {F : Type*} [Field F]
  (sumZeroIffEq : ∀ (x y : F), x + y = 0 ↔ x = y)
  (i : ℕ) : ∀ (a b c : F), a + b = c → a^(2^i) + b^(2^i) = c^(2^i)

have h_pb_type_eq : Basis (Fin bCast.dim) (ConcreteBTField r1) (ConcreteBTField r) =
      Basis (Fin 2) (ConcreteBTField r1) (ConcreteBTField r) := by
      congr

theorem empty_unique (v : Fin 0 → α) : v = !v[]

lemma qMap_total_fiber_repr_coeff (i : Fin ℓ) (steps : ℕ) (h_i_add_steps : i.val + steps ≤ ℓ)
    (y : sDomain 𝔽q β h_ℓ_add_R_rate (i := ⟨i.val + steps, by omega⟩))
    (k : Fin (2 ^ steps)) :
    let x := qMap_total_fiber 𝔽q β (i := ⟨i, by omega⟩)
      (steps := steps)
      (h_i_add_steps := by simp only; exact fin_ℓ_steps_lt_ℓ_add_R i steps h_i_add_steps) (y := y) k
    let basis_y := sDomain_basis 𝔽q β h_ℓ_add_R_rate (i := ⟨i.val + steps, by omega⟩)
      (h_i := by simp only; exact fin_ℓ_steps_lt_ℓ_add_R i steps h_i_add_steps)
    let y_coeffs := basis_y.repr y
    ∀ j, -- j refers to bit index of the fiber point x
      ((sDomain_basis 𝔽q β h_ℓ_add_R_rate (i := ⟨i, by omega⟩) (by simp only; omega)).repr x) j
      = fiber_coeff (i := i) (steps := steps) (j := j) (elementIdx := k)
        (y_coeffs := y_coeffs)

theorem cast_symm {α β : Sort u} {h : α = β} {a : α} {b : β} :
    cast h a = b ↔ a = cast h.symm b

Fin.isLt in Init.Prelude
</all_available_lemmas>

<local_ctx>
import ArkLib.Data.Classes.DCast

import ArkLib.Data.FieldTheory.BinaryField.Tower.Basic

namespace ConcreteBinaryTower

open Polynomial

def ConcreteBTField : ℕ → Type := fun k => BitVec (2 ^ k)

section BitVecDCast

instance BitVec.instDCast : DCast Nat BitVec where
  dcast h := BitVec.cast h
  dcast_id := by admit /- proof elided -/

end BitVecDCast

section ConversionUtils

def fromNat {k : ℕ} (n : Nat) : ConcreteBTField k :=
  BitVec.ofNat (2 ^ k) n

instance ConcreteBTField.instDCast_local : DCast ℕ ConcreteBTField where
  dcast h_k_eq term_k1 := BitVec.cast (congrArg (fun n => 2 ^ n) h_k_eq) term_k1
  dcast_id := by admit /- proof elided -/

end ConversionUtils

section NumericLemmas

end NumericLemmas

section FieldOperationsAndInstances

def zero {k : ℕ} : ConcreteBTField k := BitVec.zero (2 ^ k)

def one {k : ℕ} : ConcreteBTField k := 1#(2 ^ k)

def add {k : ℕ} (x y : ConcreteBTField k) : ConcreteBTField k := BitVec.xor x y

def neg {k : ℕ} (x : ConcreteBTField k) : ConcreteBTField k := x

def split {k : ℕ} (h : k > 0) (x : ConcreteBTField k) :
    ConcreteBTField (k - 1) × ConcreteBTField (k - 1) :=
  let lo_bits : BitVec (2 ^ (k - 1) - 1 - 0 + 1) :=
    BitVec.extractLsb (hi := 2 ^ (k - 1) - 1) (lo := 0) x
  let hi_bits : BitVec (2 ^ k - 1 - 2 ^ (k - 1) + 1) :=
    BitVec.extractLsb (hi := 2 ^ k - 1) (lo := 2 ^ (k - 1)) x
  have h_lo : 2 ^ (k - 1) - 1 - 0 + 1 = 2 ^ (k - 1) := by admit /- proof elided -/

def join {k : ℕ} (h_pos : k > 0) (hi lo : ConcreteBTField (k - 1)) : ConcreteBTField k :=

structure ConcreteBTFAddCommGroupProps (k : ℕ) where
  add_assoc : ∀ a b c : ConcreteBTField k, (a + b) + c = a + (b + c) := add_assoc
  add_comm : ∀ a b : ConcreteBTField k, a + b = b + a := add_comm
  add_zero : ∀ a : ConcreteBTField k, a + zero = a := add_zero
  zero_add : ∀ a : ConcreteBTField k, zero + a = a := zero_add
  add_neg : ∀ a : ConcreteBTField k, a + (neg a) = zero := neg_add_cancel

def mkAddCommGroupInstance {k : ℕ} : AddCommGroup (ConcreteBTField k) := {
  zero := zero
  neg := neg
  sub := fun x y => add x y
  add_assoc := add_assoc
  add_comm := add_comm
  zero_add := zero_add
  add_zero := add_zero
  nsmul := fun n x => if n % 2 = (0 : ℕ) then zero else x
  zsmul := fun (n : ℤ) x => if n % 2 = 0 then zero else x  
  neg_add_cancel := neg_add_cancel
  nsmul_succ := nsmul_succ
  zsmul_succ' := fun n a => zsmul_succ n a
  add := add
  zsmul_neg' := zsmul_neg' (k := k)
}

def Z (k : ℕ) : ConcreteBTField k :=
  if h_k : k = 0 then one
  else
    《 one (k:=k-1), zero (k:=k-1) 》

def equivProd {k : ℕ} (h_k_pos : k > 0) :
  ConcreteBTField k ≃ ConcreteBTField (k - 1) × ConcreteBTField (k - 1) where
  toFun := split h_k_pos
  invFun := fun (hi, lo) => 《 hi, lo 》
  left_inv := fun x => Eq.symm (join_of_split h_k_pos x _ _ rfl)
  right_inv := fun ⟨hi, lo⟩ => Eq.symm (split_of_join h_k_pos _ hi lo rfl)

def concrete_mul {k : ℕ} (a b : ConcreteBTField k) : ConcreteBTField k :=
  if h_k_zero : k = 0 then
    if a = zero then zero
    else if b = zero then zero
    else if a = one then b
    else if b = one then a
    else zero 
  else
    have h_k_gt_0 : k > 0 := by admit /- proof elided -/

def concrete_inv {k : ℕ} (a : ConcreteBTField k) : ConcreteBTField k :=
  if h_k_zero : k = 0 then
    if a = 0 then 0 else 1
  else
    if h_a_zero : a = 0 then 0
    else if h_a_one : a = 1 then 1
    else
      let h_k_gt_0 : k > 0 := Nat.zero_lt_of_ne_zero h_k_zero
      let (a_hi, a_lo) := split (k:=k) (h:=h_k_gt_0) a
      let prevZ := Z (k - 1)
      let a_lo_next := a_lo + concrete_mul a_hi prevZ
      let delta := concrete_mul a_lo a_lo_next + concrete_mul a_hi a_hi
      let delta_inverse := concrete_inv delta
      let out_hi := concrete_mul delta_inverse a_hi
      let out_lo := concrete_mul delta_inverse a_lo_next
      let res := 《 out_hi, out_lo 》
      res

section FieldLemmasOfLevel0

end FieldLemmasOfLevel0

section NumericCasting

def natCast {k : ℕ} (n : ℕ) : ConcreteBTField k := if n % 2 = 0 then zero else one

def natCast_zero {k : ℕ} : natCast (k:=k) 0 = zero :=

def natCast_succ {k : ℕ} (n : ℕ) : natCast (k:=k) (n + 1) = natCast (k:=k) n + 1 :=

def intCast {k : ℕ} (n : ℤ) : ConcreteBTField k := if n % 2 = 0 then zero else one

def intCast_ofNat {k : ℕ} (n : ℕ) : intCast (k:=k) (n : ℤ) = natCast n :=

def intCast_negSucc {k : ℕ} (n : ℕ) : intCast (k:=k) (Int.negSucc n)
  = - (↑(n + 1) : ConcreteBTField k) :=

end NumericCasting

structure ConcreteBTFRingProps (k : ℕ) extends (ConcreteBTFAddCommGroupProps k) where
  
  mul_eq : ∀ (a b : ConcreteBTField k) (h_k : k > 0)
    {a₁ a₀ b₁ b₀ : ConcreteBTField (k - 1)}
    (_h_a : (a₁, a₀) = split h_k a) (_h_b : (b₁, b₀) = split h_k b),
    concrete_mul a b =
      《 concrete_mul a₀ b₁ + concrete_mul b₀ a₁ + concrete_mul (concrete_mul a₁ b₁) (Z (k - 1)),
      concrete_mul a₀ b₀ + concrete_mul a₁ b₁ 》

  
  zero_mul : ∀ a : ConcreteBTField k, concrete_mul zero a = zero
  zero_mul' : ∀ a : ConcreteBTField k, concrete_mul 0 a = 0
  mul_zero : ∀ a : ConcreteBTField k, concrete_mul a zero = zero
  mul_zero' : ∀ a : ConcreteBTField k, concrete_mul a 0 = 0
  one_mul : ∀ a : ConcreteBTField k, concrete_mul one a = a
  mul_one : ∀ a : ConcreteBTField k, concrete_mul a one = a

  
  mul_assoc : ∀ a b c : ConcreteBTField k, concrete_mul (concrete_mul a b) c
    = concrete_mul a (concrete_mul b c)
  mul_left_distrib : ∀ a b c : ConcreteBTField k, concrete_mul a (b + c)
    = concrete_mul a b + concrete_mul a c
  mul_right_distrib : ∀ a b c : ConcreteBTField k, concrete_mul (a + b) c
    = concrete_mul a c + concrete_mul b c

structure ConcreteBTFDivisionRingProps (k : ℕ) extends (ConcreteBTFRingProps k) where
  
  mul_inv_cancel : ∀ a : ConcreteBTField k, a ≠ zero → concrete_mul a (concrete_inv a) = one

structure ConcreteBTFieldProps (k : ℕ) extends (ConcreteBTFDivisionRingProps k) where
  
  mul_comm : ∀ a b : ConcreteBTField k, concrete_mul a b = concrete_mul b a

def mkRingInstance {k : ℕ} (props : ConcreteBTFieldProps k) : Ring (ConcreteBTField k) where
  toAddCommGroup := mkAddCommGroupInstance
  toOne := inferInstance
  mul := concrete_mul
  mul_assoc := props.mul_assoc
  one_mul := props.one_mul
  mul_one := props.mul_one
  left_distrib := props.mul_left_distrib
  right_distrib := props.mul_right_distrib
  zero_mul := props.zero_mul
  mul_zero := props.mul_zero

  natCast n := natCast n
  natCast_zero := natCast_zero
  natCast_succ n := natCast_succ n
  intCast n := intCast n
  intCast_ofNat n := intCast_ofNat n
  intCast_negSucc n := intCast_negSucc n

def mkDivisionRingInstance {k : ℕ} (props : ConcreteBTFieldProps k)
    : DivisionRing (ConcreteBTField k) where
  toRing := mkRingInstance (k:=k) props
  inv := concrete_inv
  exists_pair_ne := concrete_exists_pair_ne (k := k)
  mul_inv_cancel := props.mul_inv_cancel
  inv_zero := concrete_inv_zero
  qsmul := (Rat.castRec · * ·)
  nnqsmul := (NNRat.castRec · * ·)

def mkFieldInstance {k : ℕ} (props : ConcreteBTFieldProps k) : Field (ConcreteBTField k) where
  toDivisionRing := mkDivisionRingInstance (k:=k) props
  mul_comm := props.mul_comm

structure ConcreteBTFStepResult (k : ℕ) extends (ConcreteBTFieldProps k) where
  instFintype : Fintype (ConcreteBTField k)
  fieldFintypeCard : Fintype.card (ConcreteBTField k) = 2^(2^k)
  
  sumZeroIffEq : ∀ (x y : ConcreteBTField k), x + y = 0 ↔ x = y
  traceMapEvalAtRootsIs1 :
    letI := mkFieldInstance (k:=k) (props:=toConcreteBTFieldProps)
    TraceMapProperty (ConcreteBTField k) (u:=Z k) k
  instIrreduciblePoly :
    letI := mkFieldInstance (k:=k) (props:=toConcreteBTFieldProps)
    (Irreducible (p := (definingPoly (s:=(Z k)))))

end FieldOperationsAndInstances

section BTFieldPropsOneLevelLiftingLemmas

variable {k : ℕ} {h_k : k > 0}

end BTFieldPropsOneLevelLiftingLemmas

section TowerFieldsConstruction

def liftBTFieldProps (k : ℕ) (prevBTFResult : ConcreteBTFStepResult (k := k)) :
  ConcreteBTFieldProps (k + 1) := {
    zero_mul := concrete_zero_mul (prevBTFResult.toConcreteBTFieldProps),
    zero_mul' := fun a => by admit /- proof elided -/

def liftConcreteBTField (k : ℕ) (prevBTFResult : ConcreteBTFStepResult (k := k)) :
  Field (ConcreteBTField (k + 1)) :=

def concreteCanonicalEmbedding (k : ℕ)
    (prevBTFieldProps : ConcreteBTFieldProps (k := (k)))
    (curBTFieldProps : ConcreteBTFieldProps (k := (k + 1))) :
  letI := mkFieldInstance prevBTFieldProps
  letI := mkFieldInstance curBTFieldProps
  ConcreteBTField k →+* ConcreteBTField (k + 1) :=

instance instAlgebraLiftConcreteBTField (k : ℕ)
  (prevBTFResult : ConcreteBTFStepResult (k := k)) :
  letI := mkFieldInstance (prevBTFResult.toConcreteBTFieldProps)
  letI := liftConcreteBTField (k:=k) prevBTFResult
  Algebra (ConcreteBTField k) (ConcreteBTField (k + 1)) :=
  letI := mkFieldInstance (prevBTFResult.toConcreteBTFieldProps)
  letI := liftConcreteBTField (k:=k) prevBTFResult
  RingHom.toAlgebra (R:=ConcreteBTField k) (S:=ConcreteBTField (k + 1))
    (i:=(concreteCanonicalEmbedding (k:=k)
      (prevBTFieldProps:=prevBTFResult.toConcreteBTFieldProps)
      (curBTFieldProps:=liftBTFieldProps (k:=k) (prevBTFResult:=prevBTFResult))))

def getBTFResult (k : ℕ) : ConcreteBTFStepResult k :=
  match k with
  | 0 =>
    let base : ConcreteBTFieldProps 0 := {
      mul_eq := fun a b h_k _ _ _ _ _ _ => by admit /- proof elided -/
| c1_one
        · 
          rw [c1_zero] at h_mul
          
          simp at h_mul
        · 
          rcases c2_cases with c2_zero | c2_one
          · 
            rw [c2_zero] at h_mul
            
            simp at h_mul
          · 
            
            exact ⟨c1_one, c2_one⟩
      
      have specialElement_eq_zero : specialElement = 0 := by admit /- proof elided -/

end TowerFieldsConstruction

section ConcreteBTFieldAlgebraConstruction

def canonicalAlgMap (k : ℕ) := concreteCanonicalEmbedding (k:=k)
  (prevBTFieldProps:= ((getBTFResult k).toConcreteBTFieldProps))
  (curBTFieldProps:= ((getBTFResult (k + 1)).toConcreteBTFieldProps))

def concreteTowerAlgebraMap (l r : ℕ) (h_le : l ≤ r) :
    ConcreteBTField l →+* ConcreteBTField r :=

instance instAlgebraTowerConcreteBTF : AlgebraTower (ConcreteBTField) where
  algebraMap := concreteTowerAlgebraMap
  commutes' := by admit /- proof elided -/

def ConcreteBTFieldAlgebra {l r : ℕ} (h_le : l ≤ r) :
    Algebra (ConcreteBTField l) (ConcreteBTField r) := instAlgebraTowerConcreteBTF.toAlgebra h_le

def join_via_add_smul (k : ℕ) (h_pos : k > 0) (hi_btf lo_btf : ConcreteBTField (k - 1)) :
    ConcreteBTField k :=

end ConcreteBTFieldAlgebraConstruction

noncomputable section ConcreteMultilinearBasis

open Module

def basisSucc (k : ℕ) : Basis (Fin 2) (ConcreteBTField k) (ConcreteBTField (k + 1)) :=

def powerBasisSucc (k : ℕ) :
    PowerBasis (ConcreteBTField k) (ConcreteBTField (k + 1)) :=
</local_ctx>

<target_theorem>
@[simp]
theorem minPoly_of_powerBasisSucc_generator (k : ℕ) :
  (minpoly (ConcreteBTField k) (powerBasisSucc k).gen) = X^2 + (Z k) • X + 1 :=
</target_theorem>

