import data.matrix.notation
import tactic.dec_trivial

import chess.playfield
import chess.piece
import chess.utils

/-!

# Definitions and theorems about a chess board

## Summary

The chess board is a set of indexed `piece`s on a `playfield`. A board is valid,
and can only be constructed, if all the pieces are present on the board, and no two
distinct (by index) pieces share the same position on the board.

## Main definitions

1. The `board` itself, which requires an indexed vector of `piece`s,
and the `playfield` which will serve as the where those pieces are placed.
Additionally, all pieces must be present on the playfield, and no two distinct (by index)
pieces can share a position on the playfield.

2. A way to reduce the board, following the indices to just the pieces. This allows
comparison of boards that are equivalent modulo permutation of indices that point to
equivalent pieces.

3. `board.piece_at`, which extracts the piece which sits on a given square.

## Implementation notes

1. A `board` requires finite dimensions for the `playfield`, finite indices, and a
finite piece set. Ideally, this should be generizable to potentially infinite types.
However, since `playfield`s are usually provided by `matrix`, which is restricted
to finite dimensions, it is easiest to define the board as finite.

2. The requirement of `decidable_eq` on the dimensions and index allows use of
`dec_trivial` to automatically infer proofs for board constraint propositions.
That means instantiation of a `board` will not require explicit proofs for the propositions.

3. The board does not define what are valid position comparisons -- the geometry of
the space is not defined other than what the `playfield` provides.

4. Currently, all pieces are constrained by the definition of a board to be present
on the playfield. That means no capturing moves and no piece introduction moves are possible.

-/

namespace chess

-- The dimensions of the board, finite and decidably equal
variables (m n : Type*) [fintype n] [decidable_eq n] [fintype m] [decidable_eq m]
-- The index associated to pieces on a playfield
variables (ι : Type*) [fintype ι] [decidable_eq ι]
-- The piece type
variables (K : Type*)

/--
A board is axiomatized as a set of indexable (ergo distinguishable) pieces
which are placed on distinct squares of a `playfield`.

No inhabited instance because the index type can be larger than the
cardinality of the playfield dimensions.
-/
@[nolint has_inhabited_instance]
structure board :=
-- The pieces the board holds, provided as an indexed vector
(pieces : ι → K)
-- The playfield on which the pieces are placed
(contents : playfield m n ι)
-- All the pieces in `pieces` are on the `contents`
-- See "Implementation details" for info about `dec_trivial`
(contains : function.surjective contents.index_at . tactic.exact_dec_trivial)
-- Different positions hold different indices
(injects : contents.some_injective . tactic.exact_dec_trivial)

namespace board

variables {m n ι K}
/-- The width of the board. Explicit argument for projection notation. -/
@[nolint unused_arguments]
def width (b : board m n ι K) : ℕ := fintype.card n
/-- The height of the board. Explicit argument for projection notation. -/
@[nolint unused_arguments]
def height (b : board m n ι K) : ℕ := fintype.card m

/-- The state of the board, where pieces of the same type are equivalent -/
def reduce (b : board m n ι K) : playfield m n K :=
λ pos, option.map b.pieces (b.contents pos)

-- Allows saying that `b b' : board m n ι K` are `b ≈ b'`.
instance : has_equiv (board m n ι K) := ⟨λ b b', reduce b = reduce b'⟩

-- An indexed piece is on the board if it is in the board's `playfield`.
instance : has_mem ι (board m n ι K) :=
⟨λ ix b, ix ∈ b.contents⟩

/--
A board contains all of the `ix : ι` indices that it knows of,
stated explicitly. Uses the `board.contains` constraint.
-/
lemma retains_pieces (b : board m n ι K) (ix : ι) : ix ∈ b.contents :=
exists.elim (b.contains ix) (λ pos h, h ▸ playfield.index_at_in pos)

/--
A board maps each index `ix : ι` to a unique position `pos : m × n`,
stated explicitly. Uses the `board.injects` constraint.
-/
lemma no_superimposed (b : board m n ι K) (pos pos' : m × n) (hne : pos ≠ pos')
  (h : b.contents.occupied_at pos) : b.contents pos ≠ b.contents pos' :=
λ H, hne (b.injects h H)

/--
Given that the board is `occupied_at` some `pos : m × n`,
then the index at some `pos' : m × n` is equal to the index at `pos`,
iff that `pos'` is equal `pos' = pos`.
-/
protected lemma inj_iff (b : board m n ι K) :
  ∀ {pos pos' : m × n}, b.contents.occupied_at pos → (b.contents pos = b.contents pos' ↔ pos = pos') :=
λ _ _ H, playfield.inj_iff b.contents b.injects H

section repr

-- A board can be represented if the pieces themselves can be represented
variables [has_repr K]
variables {n' m' ix : ℕ}

/--
A board's `pieces` is a "vector", so `vec_repr` is used to represent it.
-/
def board_repr_pieces (b : board (fin m') (fin n') (fin ix) K) : string :=
chess.utils.vec_repr b.pieces

/--
A board's `contents` can be represented by reducing the board according to
the indexed vector at `pieces`, and placing the pieces on the `playfield`.
We override the default `option K` representation by using `option_wrap`,
and supply an underscore to represent empty positions.
-/
def board_repr_contents (b : board (fin m') (fin n') (fin ix) K) : string :=
chess.utils.matrix_repr (λ x y, chess.utils.option_wrap (b.reduce ⟨x, y⟩) "\uFF3F")

/--
A board's representation is just the concatentation of the representations
of the `pieces` and `contents` via `board_repr_pieces` and `board_repr_contents`,
respectively, with newlines inserted for clarity.
-/
def board_repr {K : Type*} [has_repr K] {n m ix : ℕ}
  (b : board (fin m) (fin n) (fin ix) K) : string :=
b.board_repr_pieces ++ ";\n\n" ++ b.board_repr_contents

/-- A board's representation is provided by `board_repr`. -/
instance board_repr_instance : has_repr (board (fin m') (fin n') (fin ix) K) := ⟨board_repr⟩

end repr

/-- The (colored) `piece` on a given square. -/
def piece_at
  (b : board m n ι K)
  (pos : m × n)
  (h : b.contents.occupied_at pos . tactic.exact_dec_trivial) : K :=
b.pieces (b.contents.index_at ⟨pos, h⟩)

end board

end chess


/-
Copyright (c) 2020 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Y. Lewis
-/

import tactic.norm_cast
import data.int.cast

/-!
# A tactic to shift `ℕ` goals to `ℤ`

It is often easier to work in `ℤ`, where subtraction is well behaved, than in `ℕ` where it isn't.
`zify` is a tactic that casts goals and hypotheses about natural numbers to ones about integers.
It makes use of `push_cast`, part of the `norm_cast` family, to simplify these goals.

## Implementation notes

`zify` is extensible, using the attribute `@[zify]` to label lemmas used for moving propositions
from `ℕ` to `ℤ`.
`zify` lemmas should have the form `∀ a₁ ... aₙ : ℕ, Pz (a₁ : ℤ) ... (aₙ : ℤ) ↔ Pn a₁ ... aₙ`.
For example, `int.coe_nat_le_coe_nat_iff : ∀ (m n : ℕ), ↑m ≤ ↑n ↔ m ≤ n` is a `zify` lemma.

`zify` is very nearly just `simp only with zify push_cast`. There are a few minor differences:
* `zify` lemmas are used in the opposite order of the standard simp form.
  E.g. we will rewrite with `int.coe_nat_le_coe_nat_iff` from right to left.
* `zify` should fail if no `zify` lemma applies (i.e. it was unable to shift any proposition to ℤ).
  However, once this succeeds, it does not necessarily need to rewrite with any `push_cast` rules.
-/

open tactic

namespace zify

/--
The `zify` attribute is used by the `zify` tactic. It applies to lemmas that shift propositions
between `nat` and `int`.

`zify` lemmas should have the form `∀ a₁ ... aₙ : ℕ, Pz (a₁ : ℤ) ... (aₙ : ℤ) ↔ Pn a₁ ... aₙ`.
For example, `int.coe_nat_le_coe_nat_iff : ∀ (m n : ℕ), ↑m ≤ ↑n ↔ m ≤ n` is a `zify` lemma.
-/
@[user_attribute]
meta def zify_attr : user_attribute simp_lemmas unit :=
{ name := `zify,
  descr := "Used to tag lemmas for use in the `zify` tactic",
  cache_cfg :=
    { mk_cache :=
        λ ns, mmap (λ n, do c ← mk_const n, return (c, tt)) ns >>= simp_lemmas.mk.append_with_symm,
      dependencies := [] } }

/--
Given an expression `e`, `lift_to_z e` looks for subterms of `e` that are propositions "about"
natural numbers and change them to propositions about integers.

Returns an expression `e'` and a proof that `e = e'`.

Includes `ge_iff_le` and `gt_iff_lt` in the simp set. These can't be tagged with `zify` as we
want to use them in the "forward", not "backward", direction.
-/
meta def lift_to_z (e : expr) : tactic (expr × expr) :=
do sl ← zify_attr.get_cache,
   sl ← sl.add_simp `ge_iff_le, sl ← sl.add_simp `gt_iff_lt,
   (e', prf, _) ← simplify sl [] e,
   return (e', prf)

attribute [zify] int.coe_nat_le_coe_nat_iff int.coe_nat_lt_coe_nat_iff int.coe_nat_eq_coe_nat_iff

end zify

@[zify] lemma int.coe_nat_ne_coe_nat_iff (a b : ℕ) : (a : ℤ) ≠ b ↔ a ≠ b :=
by simp

/--
`zify extra_lems e` is used to shift propositions in `e` from `ℕ` to `ℤ`.
This is often useful since `ℤ` has well-behaved subtraction.

The list of extra lemmas is used in the `push_cast` step.

Returns an expression `e'` and a proof that `e = e'`.-/
meta def tactic.zify (extra_lems : list simp_arg_type) : expr → tactic (expr × expr) := λ z,
do (z1, p1) ← zify.lift_to_z z <|> fail "failed to find an applicable zify lemma",
   (z2, p2) ← norm_cast.derive_push_cast extra_lems z1,
   prod.mk z2 <$> mk_eq_trans p1 p2

/--
A variant of `tactic.zify` that takes `h`, a proof of a proposition about natural numbers,
and returns a proof of the zified version of that propositon.
-/
meta def tactic.zify_proof (extra_lems : list simp_arg_type) (h : expr) : tactic expr :=
do (_, pf) ← infer_type h >>= tactic.zify extra_lems,
   mk_eq_mp pf h

section

setup_tactic_parser

/--
The `zify` tactic is used to shift propositions from `ℕ` to `ℤ`.
This is often useful since `ℤ` has well-behaved subtraction.

```lean
example (a b c x y z : ℕ) (h : ¬ x*y*z < 0) : c < a + 3*b :=
begin
  zify,
  zify at h,
  /-
  h : ¬↑x * ↑y * ↑z < 0
  ⊢ ↑c < ↑a + 3 * ↑b
  -/
end
```

`zify` can be given extra lemmas to use in simplification. This is especially useful in the
presence of nat subtraction: passing `≤` arguments will allow `push_cast` to do more work.
```
example (a b c : ℕ) (h : a - b < c) (hab : b ≤ a) : false :=
begin
  zify [hab] at h,
  /- h : ↑a - ↑b < ↑c -/
end
```

`zify` makes use of the `@[zify]` attribute to move propositions,
and the `push_cast` tactic to simplify the `ℤ`-valued expressions.

`zify` is in some sense dual to the `lift` tactic. `lift (z : ℤ) to ℕ` will change the type of an
integer `z` (in the supertype) to `ℕ` (the subtype), given a proof that `z ≥ 0`;
propositions concerning `z` will still be over `ℤ`. `zify` changes propositions about `ℕ` (the
subtype) to propositions about `ℤ` (the supertype), without changing the type of any variable.
-/
meta def tactic.interactive.zify (sl : parse simp_arg_list) (l : parse location) : tactic unit :=
do locs ← l.get_locals,
replace_at (tactic.zify sl) locs l.include_goal >>= guardb

end

add_tactic_doc
{ name := "zify",
  category := doc_category.attr,
  decl_names := [`zify.zify_attr],
  tags := ["coercions", "transport"] }

add_tactic_doc
{ name := "zify",
  category := doc_category.tactic,
  decl_names := [`tactic.interactive.zify],
  tags := ["coercions", "transport"] }


-- Copyright (c) 2017 Scott Morrison. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.
-- Authors: Stephen Morgan, Scott Morrison

import category_theory.functor_category
import category_theory.isomorphism
import tactic.interactive

namespace category_theory

universes v₁ v₂ v₃ v₄ u₁ u₂ u₃ u₄ -- declare the `v`'s first; see `category_theory.category` for an explanation

section
variables (C : Type u₁) [𝒞 : category.{v₁} C] (D : Type u₂) [𝒟 : category.{v₂} D]
include 𝒞 𝒟

/--
`prod C D` gives the cartesian product of two categories.
-/
instance prod : category.{max v₁ v₂} (C × D) :=
{ hom     := λ X Y, ((X.1) ⟶ (Y.1)) × ((X.2) ⟶ (Y.2)),
  id      := λ X, ⟨ 𝟙 (X.1), 𝟙 (X.2) ⟩,
  comp    := λ _ _ _ f g, (f.1 ≫ g.1, f.2 ≫ g.2) }

-- rfl lemmas for category.prod
@[simp] lemma prod_id (X : C) (Y : D) : 𝟙 (X, Y) = (𝟙 X, 𝟙 Y) := rfl
@[simp] lemma prod_comp {P Q R : C} {S T U : D} (f : (P, S) ⟶ (Q, T)) (g : (Q, T) ⟶ (R, U)) :
  f ≫ g = (f.1 ≫ g.1, f.2 ≫ g.2) := rfl
@[simp] lemma prod_id_fst (X : prod C D) : _root_.prod.fst (𝟙 X) = 𝟙 X.fst := rfl
@[simp] lemma prod_id_snd (X : prod C D) : _root_.prod.snd (𝟙 X) = 𝟙 X.snd := rfl
@[simp] lemma prod_comp_fst {X Y Z : prod C D} (f : X ⟶ Y) (g : Y ⟶ Z) :
  (f ≫ g).1 = f.1 ≫ g.1 := rfl
@[simp] lemma prod_comp_snd {X Y Z : prod C D} (f : X ⟶ Y) (g : Y ⟶ Z) :
  (f ≫ g).2 = f.2 ≫ g.2 := rfl
end

section
variables (C : Type u₁) [𝒞 : category.{v₁} C] (D : Type u₁) [𝒟 : category.{v₁} D]
include 𝒞 𝒟
/--
`prod.category.uniform C D` is an additional instance specialised so both factors have the same universe levels. This helps typeclass resolution.
-/
instance uniform_prod : category (C × D) := category_theory.prod C D
end
-- Next we define the natural functors into and out of product categories. For now this doesn't address the universal properties.

namespace prod

variables (C : Type u₁) [𝒞 : category.{v₁} C] (D : Type u₂) [𝒟 : category.{v₂} D]
include 𝒞 𝒟

/-- `inl C Z` is the functor `X ↦ (X, Z)`. -/
def inl (Z : D) : C ⥤ C × D :=
{ obj := λ X, (X, Z),
  map := λ X Y f, (f, 𝟙 Z) }

/-- `inr D Z` is the functor `X ↦ (Z, X)`. -/
def inr (Z : C) : D ⥤ C × D :=
{ obj := λ X, (Z, X),
  map := λ X Y f, (𝟙 Z, f) }

/-- `fst` is the functor `(X, Y) ↦ X`. -/
def fst : C × D ⥤ C :=
{ obj := λ X, X.1,
  map := λ X Y f, f.1 }

/-- `snd` is the functor `(X, Y) ↦ Y`. -/
def snd : C × D ⥤ D :=
{ obj := λ X, X.2,
  map := λ X Y f, f.2 }

def swap : C × D ⥤ D × C :=
{ obj := λ X, (X.2, X.1),
  map := λ _ _ f, (f.2, f.1) }

def symmetry : swap C D ⋙ swap D C ≅ functor.id (C × D) :=
{ hom :=
  { app := λ X, 𝟙 X,
    naturality' := begin intros, erw [category.comp_id (C × D), category.id_comp (C × D)], dsimp [swap], simp, end },
  inv :=
  { app := λ X, 𝟙 X,
    naturality' := begin intros, erw [category.comp_id (C × D), category.id_comp (C × D)], dsimp [swap], simp, end } }

end prod

section
variables (C : Type u₁) [𝒞 : category.{v₁} C] (D : Type u₂) [𝒟 : category.{v₂} D]
include 𝒞 𝒟

@[simp] def evaluation : C ⥤ (C ⥤ D) ⥤ D :=
{ obj := λ X,
  { obj := λ F, F.obj X,
    map := λ F G α, α.app X, },
  map := λ X Y f,
  { app := λ F, F.map f,
    naturality' := λ F G α, eq.symm (α.naturality f) },
  map_comp' := λ X Y Z f g,
  begin
    ext, dsimp, rw functor.map_comp,
  end }

@[simp] def evaluation_uncurried : C × (C ⥤ D) ⥤ D :=
{ obj := λ p, p.2.obj p.1,
  map := λ x y f, (x.2.map f.1) ≫ (f.2.app y.1),
  map_comp' := begin
    intros X Y Z f g, cases g, cases f, cases Z, cases Y, cases X, dsimp at *, simp at *,
    erw [←nat_trans.vcomp_app, nat_trans.naturality, category.assoc, nat_trans.naturality]
  end }

end

variables {A : Type u₁} [𝒜 : category.{v₁} A]
          {B : Type u₂} [ℬ : category.{v₂} B]
          {C : Type u₃} [𝒞 : category.{v₃} C]
          {D : Type u₄} [𝒟 : category.{v₄} D]
include 𝒜 ℬ 𝒞 𝒟

namespace functor
/-- The cartesian product of two functors. -/
def prod (F : A ⥤ B) (G : C ⥤ D) : A × C ⥤ B × D :=
{ obj := λ X, (F.obj X.1, G.obj X.2),
  map := λ _ _ f, (F.map f.1, G.map f.2) }

/- Because of limitations in Lean 3's handling of notations, we do not setup a notation `F × G`.
   You can use `F.prod G` as a "poor man's infix", or just write `functor.prod F G`. -/

@[simp] lemma prod_obj (F : A ⥤ B) (G : C ⥤ D) (a : A) (c : C) : (F.prod G).obj (a, c) = (F.obj a, G.obj c) := rfl
@[simp] lemma prod_map (F : A ⥤ B) (G : C ⥤ D) {a a' : A} {c c' : C} (f : (a, c) ⟶ (a', c')) : (F.prod G).map f = (F.map f.1, G.map f.2) := rfl
end functor

namespace nat_trans

/-- The cartesian product of two natural transformations. -/
def prod {F G : A ⥤ B} {H I : C ⥤ D} (α : F ⟹ G) (β : H ⟹ I) : F.prod H ⟹ G.prod I :=
{ app         := λ X, (α.app X.1, β.app X.2),
  naturality' := begin /- `obviously'` says: -/ intros, cases f, cases Y, cases X, dsimp at *, simp, split, rw naturality, rw naturality end }

/- Again, it is inadvisable in Lean 3 to setup a notation `α × β`; use instead `α.prod β` or `nat_trans.prod α β`. -/

@[simp] lemma prod_app  {F G : A ⥤ B} {H I : C ⥤ D} (α : F ⟹ G) (β : H ⟹ I) (a : A) (c : C) :
  (nat_trans.prod α β).app (a, c) = (α.app a, β.app c) := rfl
end nat_trans

end category_theory


import Mathbin

import ZkSNARK.GeneralLemmas.MvDivisibility
import ZkSNARK.Groth16.Vars

noncomputable section

namespace Groth16
open Finset Polynomial BigOperators

/- The finite field parameter of our SNARK -/
variable {F : Type u} [field : Field F]

/- n_stmt - the statement size,
n_wit - the witness size -/
variable {n_stmt n_wit n_var : ℕ}

/- u_stmt and u_wit are fin-indexed collections of polynomials from the square span program -/
variable {u_stmt : Finₓ n_stmt → F[X]}
variable {u_wit : Finₓ n_wit → F[X]}
variable {v_stmt : Finₓ n_stmt → F[X]}
variable {v_wit : Finₓ n_wit → F[X]}
variable {w_stmt : Finₓ n_stmt → F[X]}
variable {w_wit : Finₓ n_wit → F[X]}

-- def l : ℕ := n_stmt
-- def m : ℕ := n_wit

/- The roots of the polynomial t -/
variable (r : Finₓ n_wit → F)

/- t is the polynomial divisibility by which is used to verify satisfaction of the SSP -/
def t : F[X] := ∏ i in finRange n_wit, (x : F[X]) - Polynomial.c (r i)

lemma nat_degree_t : (t r).natDegree = n_wit := by
  rw [t, Polynomial.nat_degree_prod]
  have h1 : ∀ x : F, RingHom.toFun c x = coeFn Polynomial.c x
  · intro
    rw [RingHom.to_fun_eq_coe]
  conv_lhs =>
  · congr
    skip
    ext
    simp_rw [h1 (r _), Polynomial.nat_degree_X_sub_C]
  rw [Finset.sum_const, Finset.fin_range_card, Algebra.id.smul_eq_mul, mul_oneₓ]
  intros
  apply Polynomial.X_sub_C_ne_zero

lemma monic_t : Polynomial.Monic (t r) := by
  rw [t]
  apply Polynomial.monic_prod_of_monic
  intros
  exact Polynomial.monic_X_sub_C (r _)

lemma degree_t_pos (hm : 0 < n_wit) : 0 < (t r).degree := by
  suffices h : (t r).degree = some n_wit
  · rw [h]
    apply WithBot.some_lt_some.2
    exact hm

  have h := nat_degree_t r
  rw [Polynomial.natDegree] at h

  revert h -- this is needed because degree (t r) is not substituted in h otherwise
  induction degree (t r)

  · intro h
    rw [Option.get_or_else_none] at h
    rw [eq_comm]
    rw [← h] at hm
    exfalso
    simp at hm

  intro h
  rw [Option.get_or_else_some] at h
  rw [h]

-- Single variable form of V_wit
def V_wit_sv (a_wit : Finₓ n_wit → F) :  F[X] :=
  ∑ i in finRange n_wit, a_wit i • u_wit i

/- The statement polynomial that the verifier computes from the statement bits,
as a single variable polynomial -/
def V_stmt_sv (a_stmt : Finₓ n_stmt → F) : F[X] :=
  ∑ i in finRange n_stmt, a_stmt i • u_stmt i

/- Checks whether a statement witness pair satisfies the SSP -/
def satisfying (a_stmt : Finₓ n_stmt → F) (a_wit : Finₓ n_wit → F) :=
  (((∑ i in finRange n_stmt, a_stmt i • u_stmt i)
    + ∑ i in finRange n_wit, a_wit i • u_wit i)
  *
  ((∑ i in finRange n_stmt, a_stmt i • v_stmt i)
    + ∑ i in finRange n_wit, a_wit i • v_wit i)
  -
  ((∑ i in finRange n_stmt, a_stmt i • w_stmt i)
    + ∑ i in finRange n_wit, a_wit i • w_wit i) : F[X]) %ₘ (t r) = 0

/- The coefficients of the CRS elements in the algebraic adversary's representation -/
register_simp_attr crs "Attribute for defintions of CRS elements"

/- The CRS elements
These funtions are actually multivariate Laurent polynomials of the toxic waste samples,
but we represent them here as functions on assignments of the variables to values.
-/
variable (F)
def crs_α  (f : Vars → F) : F := f Vars.α

def crs_β (f : Vars → F) : F := f Vars.β

def crs_γ (f : Vars → F) : F := f Vars.γ

def crs_δ (f : Vars → F) : F := f Vars.δ

def crs_powers_of_x (i : Finₓ n_var) (a : F) : F := (a)^(i : ℕ)

def crs_l (i : Finₓ n_stmt) (f : Vars → F) (a : F) : F :=
  ((f Vars.β / f Vars.γ) * (u_stmt i).eval (a)
  +
  (f Vars.α / f Vars.γ) * (v_stmt i).eval (a)
  +
  (w_stmt i).eval (a)) / f Vars.γ

def crs_m (i : Finₓ n_wit) (f : Vars → F) (a : F) : F :=
  ((f Vars.β / f Vars.δ) * (u_wit i).eval (a)
  +
  (f Vars.α / f Vars.δ) * (v_wit i).eval (a)
  +
  (w_wit i).eval (a)) / f Vars.δ

def crs_n (i : Finₓ (n_var - 1)) (f : Vars → F) (a : F) : F :=
  ((a)^(i : ℕ)) * (t r).eval a / f Vars.δ

variable {F}
variable { A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ  : F }
variable { A_x B_x C_x : Finₓ n_var → F }
variable { A_l B_l C_l : Finₓ n_stmt → F }
variable { A_m B_m C_m : Finₓ n_wit → F }
variable { A_h B_h C_h : Finₓ (n_var - 1) → F }

/- Polynomial forms of the adversary's proof representation -/
def A (f : Vars → F) (x : F) : F :=
  (A_α * crs_α F f)
  +
  A_β * crs_β F f
  +
  A_γ * crs_γ F f
  +
  A_δ * crs_δ F f
  +
  ∑ i in (finRange n_var), (A_x i) * (crs_powers_of_x F i x)
  +
  ∑ i in (finRange n_stmt), (A_l i) * (@crs_l F field n_stmt u_stmt v_stmt w_stmt i f x)
  +
  ∑ i in (finRange n_wit), (A_m i) * (@crs_m F field n_wit u_wit v_wit w_wit i f x)
  +
  ∑ i in (finRange (n_var - 1)), (A_h i) * (crs_n F r i f x)

def B (f : Vars → F) (x : F) : F :=
  B_α * crs_α F f
  +
  B_β * crs_β F f
  +
  B_γ * crs_γ F f
  +
  B_δ * crs_δ F f
  +
  ∑ i in (finRange n_var), (B_x i) * (crs_powers_of_x F i x)
  +
  ∑ i in (finRange n_stmt), (B_l i) * (@crs_l F field n_stmt u_stmt v_stmt w_stmt i f x)
  +
  ∑ i in (finRange n_wit), (B_m i) * (@crs_m F field n_wit u_wit v_wit w_wit i f x)
  +
  ∑ i in (finRange (n_var - 1)), (B_h i) * (crs_n F r i f x)

def C (f : Vars → F) (x : F) : F  :=
  C_α * crs_α F f
  +
  C_β * crs_β F f
  +
  C_γ * crs_γ F f
  +
  C_δ * crs_δ F f
  +
  ∑ i in (finRange n_var), (C_x i) * (crs_powers_of_x F i x)
  +
  ∑ i in (finRange n_stmt), (C_l i) * (@crs_l F field n_stmt u_stmt v_stmt w_stmt i f x)
  +
  ∑ i in (finRange n_wit), (C_m i) * (@crs_m F field n_wit u_wit v_wit w_wit i f x)
  +
  ∑ i in (finRange (n_var - 1)), (C_h i) * (crs_n F r i f x)

/- The modified crs elements
these are multivariate (non-Laurent!) polynomials of the toxic waste samples,
obtained by multiplying the Laurent polynomial forms of the CRS through by γδ.
We will later prove that the Laurent polynomial equation is equivalent to a
similar equation of the modified crs elements, allowing us to construct
a proof in terms of polynomials -/
def crs'_α  : MvPolynomial Vars F[X] :=
  let pol₁ := (MvPolynomial.x Vars.α : MvPolynomial Vars F[X])
  let pol₂ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol₃ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  (pol₁ * pol₂) * pol₃

def crs'_β : MvPolynomial Vars F[X] :=
  let pol₁ := (MvPolynomial.x Vars.β : MvPolynomial Vars F[X])
  let pol₂ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol₃ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  (pol₁ * pol₂) * pol₃

def crs'_γ : MvPolynomial Vars F[X] :=
  let pol₁ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol₂ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol₃ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  (pol₁ * pol₂) * pol₃

def crs'_δ : MvPolynomial Vars F[X] :=
  let pol₁ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  let pol₂ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol₃ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  (pol₁ * pol₂) * pol₃

def crs'_powers_of_x (i : Finₓ n_var) : MvPolynomial Vars F[X] :=
  let pol₁ := (MvPolynomial.c ((Polynomial.x)^(i : ℕ)) : MvPolynomial Vars F[X])
  let pol₂ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol₃ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  (pol₁ * pol₂) * pol₃
-- We define prodcuts of these crs elements without the division, then later claim identities. Is this right?

def crs'_l (i : Finₓ n_stmt) : MvPolynomial Vars F[X] :=
  let pol₁ := (MvPolynomial.x Vars.α : MvPolynomial Vars F[X])
  let pol₂ := (MvPolynomial.x Vars.β : MvPolynomial Vars F[X])
  let pol₃ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  let pol₄ := (MvPolynomial.c (u_stmt i) : MvPolynomial Vars F[X])
  let pol₅ := (MvPolynomial.c (v_stmt i) : MvPolynomial Vars F[X])
  let pol₆ := (MvPolynomial.c (w_stmt i) : MvPolynomial Vars F[X])
  (pol₂ * pol₃) * pol₄
  +
  (pol₁  * pol₃) * pol₅
  +
  pol₃ * pol₆

def crs'_m (i : Finₓ n_wit) : MvPolynomial Vars F[X] :=
  let pol₁ := (MvPolynomial.x Vars.α : MvPolynomial Vars F[X])
  let pol₂ := (MvPolynomial.x Vars.β : MvPolynomial Vars F[X])
  let pol₃ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol₄ := (MvPolynomial.c (u_wit i) : MvPolynomial Vars F[X])
  let pol₅ := (MvPolynomial.c (v_wit i) : MvPolynomial Vars F[X])
  let pol₆ := (MvPolynomial.c (w_wit i) : MvPolynomial Vars F[X])
  (pol₂ * pol₃) * pol₄
  +
  (pol₁ * pol₃) * pol₅
  +
  pol₃ * pol₆

def crs'_t (i : Finₓ (n_var - 1)) : MvPolynomial Vars F[X] :=
  let pol₁ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol₂ := ((((Polynomial.x)^(i : ℕ)) * (t r)) : F[X])
  let pol₃ := (MvPolynomial.c pol₂ : MvPolynomial Vars F[X])
  pol₁ * pol₃

/- Polynomial form of A in the adversary's proof representation -/
def A'  : MvPolynomial Vars F[X] :=
  let pol_c := (Polynomial.c A_α : F[X])
  let pol₁ := (MvPolynomial.c pol_c : MvPolynomial Vars F[X])
  let pol₂ := (@crs'_α F field : MvPolynomial Vars F[X])
  let pol_γ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol_aβ := (MvPolynomial.c (Polynomial.c A_β) : MvPolynomial Vars F[X])
  let pol_aγ := (MvPolynomial.c (Polynomial.c A_γ) : MvPolynomial Vars F[X])
  let pol_aδ := (MvPolynomial.c (Polynomial.c A_δ) : MvPolynomial Vars F[X])
  let pol_δ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  let sum_ax := (∑ i in (finRange n_var), ((Polynomial.c (A_x i)) * (Polynomial.x ^ (i : ℕ))) : F[X])
  let sum₁ := (MvPolynomial.c sum_ax : MvPolynomial Vars F[X])
  let sum₂ := (∑ i in (finRange n_stmt),
    let pol₁ := (@crs'_l F field n_stmt u_stmt v_stmt w_stmt i : MvPolynomial Vars F[X])
    let pol₂ := (MvPolynomial.c (Polynomial.c (A_l i)) : MvPolynomial Vars F[X])
    pol₁ * pol₂ : MvPolynomial Vars F[X])
  let sum₃ := (∑ i in (finRange n_wit),
    let pol_am := (MvPolynomial.c (Polynomial.c (A_m i)) : MvPolynomial Vars F[X])
    let pol₁ := @crs'_m F field n_wit u_wit v_wit w_wit i
    pol₁ * pol_am : MvPolynomial Vars F[X])
  let sum₄ := ∑ i in (finRange (n_var - 1)),
    let pol₁ := (@crs'_t F field n_wit n_var r i : MvPolynomial Vars F[X])
    let pol_ah := (MvPolynomial.c (Polynomial.c (A_h i)) : MvPolynomial Vars F[X])
    pol₁ * pol_ah
  (pol₁ * pol₂)
  + -- TODO
  (@crs'_β F field) * pol_aβ
  +
  (@crs'_γ F field) * pol_aγ
  +
  (@crs'_δ F field) * pol_aδ
  +
  (pol_γ * pol_δ) * sum₁
  +
  sum₂
  +
  sum₃
  +
  sum₄

/- Polynomial form of B in the adversary's proof representation -/
def B'  : MvPolynomial Vars F[X] :=
  let pol₁ := (MvPolynomial.c (Polynomial.c B_α) : MvPolynomial Vars F[X])
  let pol_γ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol_δ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  let sum_bx := (∑ i in (finRange n_var), ((Polynomial.c (B_x i)) * (Polynomial.x ^ (i : ℕ))) : F[X])
  let sum₁ := (MvPolynomial.c sum_bx : MvPolynomial Vars F[X])
  let sum₂ := (∑ i in (finRange n_stmt),
    let pol_bl := (MvPolynomial.c (Polynomial.c (B_l i)) : MvPolynomial Vars F[X])
    let pol₁ := @crs'_l F field n_stmt u_stmt v_stmt w_stmt i
    pol₁ * pol_bl : MvPolynomial Vars F[X])
  let sum₃ := (∑ i in (finRange n_wit),
    let pol_bm := (MvPolynomial.c (Polynomial.c (B_m i)) : MvPolynomial Vars F[X])
    (@crs'_m F field n_wit u_wit v_wit w_wit i) * pol_bm : MvPolynomial Vars F[X])
  let sum₄ := (∑ i in (finRange (n_var - 1)),
    let pol_bh := (MvPolynomial.c (Polynomial.c (B_h i)) : MvPolynomial Vars F[X])
    (@crs'_t F field n_wit n_var r i) * pol_bh : MvPolynomial Vars F[X])
  (@crs'_α F field) * pol₁
  +
  (@crs'_β F field) * (MvPolynomial.c (Polynomial.c B_β) : MvPolynomial Vars F[X])
  +
  (@crs'_γ F field) * (MvPolynomial.c (Polynomial.c B_γ) : MvPolynomial Vars F[X])
  +
  (@crs'_δ F field) * (MvPolynomial.c (Polynomial.c B_δ) : MvPolynomial Vars F[X])
  +
  (pol_γ * pol_δ) * sum₁
  +
  sum₂
  +
  sum₃
  +
  sum₄

/- Polynomial form of C in the adversary's proof representation -/
def C'  : MvPolynomial Vars F[X] :=
  let pol_α := (MvPolynomial.c (Polynomial.c C_α) : MvPolynomial Vars F[X])
  let pol_β := (MvPolynomial.c (Polynomial.c C_β) : MvPolynomial Vars F[X])
  let pol_γ := (MvPolynomial.c (Polynomial.c C_γ) : MvPolynomial Vars F[X])
  let pol_δ := (MvPolynomial.c (Polynomial.c C_δ) : MvPolynomial Vars F[X])
  let pol₁ := (MvPolynomial.x Vars.γ : MvPolynomial Vars F[X])
  let pol₂ := (MvPolynomial.x Vars.δ : MvPolynomial Vars F[X])
  let sum_cx := (∑ i in (finRange n_var), ((Polynomial.c (C_x i)) * Polynomial.x ^ (i : ℕ)) : F[X])
  let sum₁ := (MvPolynomial.c sum_cx : MvPolynomial Vars F[X])
  let sum₂ :=
    (∑ i in (finRange n_stmt),
    let pol_cl := (MvPolynomial.c (Polynomial.c (C_l i)) : MvPolynomial Vars F[X])
    (@crs'_l F field n_stmt u_stmt v_stmt w_stmt i) * pol_cl : MvPolynomial Vars F[X])
  let sum₃ := (∑ i in (finRange n_wit),
    let pol_cm := (MvPolynomial.c (Polynomial.c (C_m i)) : MvPolynomial Vars F[X])
    let pol₁ := @crs'_m F field n_wit u_wit v_wit w_wit i
    pol₁ * pol_cm : MvPolynomial Vars F[X])
  let sum₄ := (∑ i in (finRange (n_var - 1)),
    let pol_ch := (MvPolynomial.c (Polynomial.c (C_h i)) : MvPolynomial Vars F[X])
    (@crs'_t F field n_wit n_var r i) * pol_ch : MvPolynomial Vars F[X])
  (@crs'_α F field) * pol_α
  + -- TODO
  (@crs'_β F field) * pol_β
  +
  (@crs'_γ F field) * pol_γ
  +
  (@crs'_δ F field) * pol_δ
  +
  (pol₁ * pol₂) * sum₁
  +
  sum₂
  +
  sum₃
  +
  sum₄

def verified (f : Vars → F) (x : F) (a_stmt : Finₓ n_stmt → F ) : Prop :=
  let A_inst :=
    @A F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ A_x A_l A_m A_h f x
  let B_inst :=
    @B F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r B_α B_β B_γ B_δ B_x B_l B_m B_h f x
  let C_inst :=
    @C F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r C_α C_β C_γ C_δ C_x C_l C_m C_h f x
  let crs_α_inst := @crs_α F f
  let crs_β_inst := @crs_β F f
  A_inst * B_inst =
    (crs_α_inst * crs_β_inst) +
    ((∑ i in finRange n_stmt, (a_stmt i) *
      @crs_l F field n_stmt u_stmt v_stmt w_stmt i f x) * (crs_γ F f) + C_inst * (crs_δ F f))

def verified' (a_stmt : Finₓ n_stmt → F) : Prop :=
  let A'_inst :=
    @A' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ A_x A_l A_m A_h
  let B'_inst :=
    @B' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r B_α B_β B_γ B_δ B_x B_l B_m B_h
  let C'_inst :=
    @C' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r C_α C_β C_γ C_δ C_x C_l C_m C_h
  let crs'_α_inst := (@crs'_α F field : MvPolynomial Vars F[X])
  let crs'_β_inst := (@crs'_β F field : MvPolynomial Vars F[X])
  A'_inst * B'_inst = (crs'_α_inst * crs'_β_inst) +
    ((∑ i in finRange n_stmt,
    let pol_astmt := (MvPolynomial.c (Polynomial.c (a_stmt i)) : MvPolynomial Vars F[X])
    pol_astmt * (@crs'_l F field n_stmt u_stmt v_stmt w_stmt i) ) * (@crs'_γ F field) + C'_inst * (@crs'_δ F field))

lemma modification_equivalence (f : Vars → F) (x : F) (a_stmt : Finₓ n_stmt → F) :
  let verified_inst :=
    @verified F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h f x a_stmt
  let verified'_inst :=
    @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt
  verified_inst → verified'_inst := by sorry

open Finsupp

lemma coeff0023 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
        Polynomial.c B_γ + (Polynomial.c A_γ) *
        (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * x ^ (i : Nat)) =
    0 := by sorry

lemma coeff0013 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * (Polynomial.c (A_m i))) * (Polynomial.c B_γ) +
          (∑ (i : Finₓ (n_var - 1)) in
               finRange (n_var - 1),
               ((Polynomial.x)^(i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
            Polynomial.c B_γ + (Polynomial.c A_γ) * (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * (Polynomial.c (B_m i))) +
      (Polynomial.c A_γ) * (∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) =
    0 := by sorry

lemma coeff0012 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * (Polynomial.c (A_m i))) *
            (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * (Polynomial.x ^ (i : ℕ))) +
          (∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
            ∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * (Polynomial.x ^ (i : ℕ)) +
        (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * (Polynomial.x ^ (i : ℕ))) *
          (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * (Polynomial.c (B_m i))) +
      (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
        ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) =
    0 := by sorry

lemma coeff0002 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) *
          ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i) +
        (∑ (i : Finₓ (n_var - 1)) in
             finRange (n_var - 1),
             (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
          ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i) +
      ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) *
           ∑ (i : Finₓ (n_var - 1)) in
             finRange (n_var - 1),
             (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i)) +
         (∑ (i : Finₓ (n_var - 1)) in
              finRange (n_var - 1),
              (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
           ∑ (i : Finₓ (n_var - 1)) in
             finRange (n_var - 1),
             (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i)))) = 0 := by sorry

lemma coeff0011 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * (Polynomial.c (A_m i))) *
            ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i) +
          (∑ (i : Finₓ (n_var - 1)) in
               finRange (n_var - 1),
               (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
            ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i) +
        (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) *
          ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i) +
      (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) =
    0 := by sorry

lemma coeff0020 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) = 0) ∨
    ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i)) = 0) :=
  by sorry

lemma coeff0021 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ) +
      (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
        ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i)) = 0 := by sorry

lemma coeff0022 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) * Polynomial.c B_γ +
              ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) *
                   Polynomial.c B_δ +
                 (∑ (i : Finₓ (n_var - 1)) in
                      finRange (n_var - 1),
                      (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
                   Polynomial.c B_δ) +
            (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
              (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ)) +
          (Polynomial.c A_γ) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i)) +
        (Polynomial.c A_δ) * (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i)) +
      (Polynomial.c A_δ) * (∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) =
    ∑ (i : Finₓ n_stmt) in finRange n_stmt, (Polynomial.c (a_stmt i)) * (w_stmt i) +
      (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (C_m i) +
         ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (C_h i))) := by sorry

lemma coeff0024 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
 Polynomial.c A_γ = 0 ∨ Polynomial.c B_γ = 0 := by sorry

lemma coeff0111 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) *
              ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i) +
            (∑ (i : Finₓ (n_var - 1)) in
                 finRange (n_var - 1),
                 (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
              ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i) +
          (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) *
            ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i) +
        ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) *
             ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i) +
           (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) *
             ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i)) +
      (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) =
    0 := by sorry

lemma coeff0033 (a_stmt :  Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (Polynomial.c A_δ) * Polynomial.c B_γ + (Polynomial.c A_γ) * Polynomial.c B_δ = Polynomial.c C_γ := by sorry

lemma coeff0102 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * (Polynomial.c (A_m i))) *
            ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i) +
          (∑ (i : Finₓ (n_var - 1)) in
               finRange (n_var - 1),
               (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
            ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i) +
        (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) *
          ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i) +
      (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) *
        ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) =
    0 := by sorry

lemma coeff0042 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
 (Polynomial.c A_δ) * Polynomial.c B_δ = Polynomial.c C_δ := by sorry

lemma coeff0112 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_β +
            (∑ (i : Finₓ (n_var - 1)) in
                 finRange (n_var - 1),
                 (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
              Polynomial.c B_β +
          (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) *
            (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ)) +
        ((∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
             ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i) +
           (Polynomial.c A_β) * (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i))) +
      (Polynomial.c A_β) *
        (∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) =
    0 := by sorry

lemma coeff0031 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) * Polynomial.c B_δ +
      (Polynomial.c A_δ) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i)) =
    ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (C_l i) := by sorry

lemma coeff0032 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
        Polynomial.c B_δ +
      (Polynomial.c A_δ) *
        (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ)) =
    ∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (C_x i)) * Polynomial.x ^ (i : ℕ) := by sorry

lemma coeff0113 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_γ +
      (Polynomial.c A_γ) * (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i)) =
    0 := by sorry

lemma coeff0120 (a_stmt :  Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i) +
      (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i)) =
    0 := by sorry

lemma coeff0123 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (Polynomial.c A_γ) * Polynomial.c B_β + (Polynomial.c A_β) * Polynomial.c B_γ = 0 := by sorry

lemma coeff0121 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) * Polynomial.c B_β +
        (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) *
          (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ)) +
      ((∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
           ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i) +
         (Polynomial.c A_β) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i))) =
    0 := by sorry

lemma coeff0132 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (Polynomial.c A_δ) * Polynomial.c B_β + (Polynomial.c A_β) * Polynomial.c B_δ = Polynomial.c C_β := by sorry

lemma coeff0131 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) * Polynomial.c B_δ +
      (Polynomial.c A_δ) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i)) =
    ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (C_l i) := by sorry

lemma coeff0122 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
                Polynomial.c B_β +
              (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) *
                Polynomial.c B_γ +
            (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_δ +
          (Polynomial.c A_β) *
            (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * (Polynomial.x ^ (i : ℕ))) +
        (Polynomial.c A_γ) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i)) +
      (Polynomial.c A_δ) * (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i)) =
    (∑ (i : Finₓ n_stmt) in finRange n_stmt, (Polynomial.c (a_stmt i)) * (u_stmt i)) +
      (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (C_m i)) := by sorry

lemma coeff0202 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) = 0) ∨
    ((∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i)) = 0) := by sorry

lemma coeff0212 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_β +
      (Polynomial.c A_β) * (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i)) =
    0 := by sorry

lemma coeff0211 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) *
        ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i) +
      (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i)) =
    0 := by sorry

lemma coeff0220 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) = 0 ∨
    (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i)) = 0 := by sorry

lemma coeff0221 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) * Polynomial.c B_β) +
      (Polynomial.c A_β) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i)) =
    0 := by sorry

lemma coeff0222 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (Polynomial.c A_β = 0) ∨ (Polynomial.c B_β) = 0 := by sorry

lemma coeff1002 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) *
            ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i) +
          (∑ (i : Finₓ (n_var - 1)) in
               finRange (n_var - 1),
               (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
            ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i) +
        (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) *
          ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i) +
      (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) *
        ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) =
    0 := by sorry

lemma coeff1011 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) *
              (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i)) +
            (∑ (i : Finₓ (n_var - 1)) in
                 finRange (n_var - 1),
                 (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
              ∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i) +
          (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) *
            ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i) +
        ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) *
             ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i) +
           (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) *
             ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i)) +
      (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) =
    0 := by sorry

lemma coeff1012 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_α +
            (∑ (i : Finₓ (n_var - 1)) in
                 finRange (n_var - 1),
                 (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
              Polynomial.c B_α +
          (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) *
            (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ)) +
        ((∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
             ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i) +
           (Polynomial.c A_α) * (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i))) +
      (Polynomial.c A_α) *
        (∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))) = 0 := by sorry

lemma coeff1013 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_γ +
      (Polynomial.c A_γ) * (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i)) =
    0 := by sorry

lemma coeff1020 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) *
        (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i)) +
      (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i)) =
    0 := by sorry

lemma coeff1021 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)) * Polynomial.c B_α +
        (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) *
          (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ)) +
      ((∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
           (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i)) +
         (Polynomial.c A_α) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i))) =
    0 := by sorry

lemma coeff1023 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (Polynomial.c A_γ) * Polynomial.c B_α + (Polynomial.c A_α) * Polynomial.c B_γ = 0 := by sorry

lemma coeff1022 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
                Polynomial.c B_α +
              (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) *
                Polynomial.c B_γ +
            (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_δ +
          (Polynomial.c A_α) *
            (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ)) +
        (Polynomial.c A_γ) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i)) +
      (Polynomial.c A_δ) * (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i)) =
    (∑ (i : Finₓ n_stmt) in finRange n_stmt, (Polynomial.c (a_stmt i)) * (v_stmt i)) +
      (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (C_m i)) := by sorry

lemma coeff1031 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) * Polynomial.c B_δ +
      (Polynomial.c A_δ) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i)) =
    ∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (C_l i) := by sorry

lemma coeff1111 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) *
          ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i) +
        (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) *
          ∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i) +
      ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) *
           ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i) +
         (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) *
           ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i))) =
    0 := by sorry

lemma coeff1102 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) *
        ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i) +
      (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) *
        ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i)) =
    0 := by sorry

lemma coeff1112 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_α +
        (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_β +
      ((Polynomial.c A_α) * (∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i)) +
         (Polynomial.c A_β) * (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i))) =
    0 := by sorry

lemma coeff1032 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (Polynomial.c A_δ) * Polynomial.c B_α + (Polynomial.c A_α) * Polynomial.c B_δ = Polynomial.c C_α := by sorry

lemma coeff1122 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (Polynomial.c A_β) * Polynomial.c B_α + (Polynomial.c A_α) * Polynomial.c B_β = 1 := by sorry

lemma coeff1120 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i) +
      (∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i)) =
    0 := by sorry

lemma coeff2011 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) *
        ∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i) +
      (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) *
        ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i)) =
    0 := by sorry

lemma coeff2002 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) = 0 ∨
    (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i)) = 0 := by sorry

lemma coeff2012 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_α +
      (Polynomial.c A_α) * (∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i)) =
    0 := by sorry

lemma coeff2020 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) = 0 ∨
    (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i)) = 0 := by sorry

lemma coeff2021 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)) * Polynomial.c B_α +
      (Polynomial.c A_α) * (∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i)) =
    0 := by sorry

lemma coeff2022 (a_stmt : Finₓ n_stmt → F)
      (eqn : @verified' F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h a_stmt) :
  Polynomial.c A_α = 0 ∨ Polynomial.c B_α = 0 := by sorry

lemma coeff0022reformat (a_stmt : Finₓ n_stmt → F) :
  ((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) * Polynomial.c B_δ +
            (∑ (i : Finₓ (n_var - 1)) in
                 finRange (n_var - 1),
                 (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))) *
              Polynomial.c B_δ +
          (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
            ∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ) +
        (Polynomial.c A_δ) * (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i)) +
      (Polynomial.c A_δ) *
        (∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i)))) =
    (∑ (i : Finₓ n_stmt) in finRange n_stmt, (Polynomial.c (a_stmt i)) * w_stmt i) +
      (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (C_m i) +
         ∑ (i : Finₓ (n_var - 1)) in
           finRange (n_var - 1),
           (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (C_h i)))
  ↔
  (((∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)) +
            (∑ (i : Finₓ (n_var - 1)) in
                 finRange (n_var - 1),
                 (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))))  *
              Polynomial.c B_δ +
          (∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (A_x i)) * Polynomial.x ^ (i : ℕ)) *
            ∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.c (B_x i)) * Polynomial.x ^ (i : ℕ) +
        (Polynomial.c A_δ) * (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i) +
      ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i)))) =
    ∑ (i : Finₓ n_stmt) in finRange n_stmt, (Polynomial.c (a_stmt i)) * w_stmt i +
      (∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (C_m i) +
         ∑ (i : Finₓ (n_var - 1)) in
           finRange (n_var - 1),
           (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (C_h i))) := by sorry

register_simp_attr atom "Attribute for defintions of atoms in the proof"

-- @[atom]
def p_A_α := Polynomial.c A_α
-- @[atom]
def p_A_β := Polynomial.c A_β
-- @[atom]
def p_A_γ := Polynomial.c A_γ
-- @[atom]
def p_A_δ := Polynomial.c A_δ
-- @[atom]
def p_B_α := Polynomial.c B_α
-- @[atom]
def p_B_β := Polynomial.c B_β
-- @[atom]
def p_B_γ := Polynomial.c B_γ
-- @[atom]
def p_B_δ := Polynomial.c B_δ
-- @[atom]
def p_C_α := Polynomial.c C_α
-- @[atom]
def p_C_β := Polynomial.c C_β
-- @[atom]
def p_C_γ := Polynomial.c C_γ
-- @[atom]
def p_C_δ := Polynomial.c C_δ

-- @[atom]
def p_u_stmt_A_l := ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (A_l i)
-- @[atom]
def p_v_stmt_A_l := ∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (A_l i)
-- @[atom]
def p_w_stmt_A_l := ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (A_l i)
-- @[atom]
def p_u_stmt_B_l := ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (B_l i)
-- @[atom]
def p_v_stmt_B_l := ∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (B_l i)
-- @[atom]
def p_w_stmt_B_l := ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (B_l i)
-- @[atom]
def p_u_stmt_C_l := ∑ (i : Finₓ n_stmt) in finRange n_stmt, (u_stmt i) * Polynomial.c (C_l i)
-- @[atom]
def p_v_stmt_C_l := ∑ (i : Finₓ n_stmt) in finRange n_stmt, (v_stmt i) * Polynomial.c (C_l i)
-- @[atom]
def p_w_stmt_C_l := ∑ (i : Finₓ n_stmt) in finRange n_stmt, (w_stmt i) * Polynomial.c (C_l i)

-- @[atom]
def p_u_wit_A_m := ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (A_m i)
-- @[atom]
def p_v_wit_A_m := ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (A_m i)
-- @[atom]
def p_w_wit_A_m := ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (A_m i)
-- @[atom]
def p_u_wit_B_m := ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (B_m i)
-- @[atom]
def p_v_wit_B_m := ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (B_m i)
-- @[atom]
def p_w_wit_B_m := ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (B_m i)
-- @[atom]
def p_u_wit_C_m := ∑ (i : Finₓ n_wit) in finRange n_wit, (u_wit i) * Polynomial.c (C_m i)
-- @[atom]
def p_v_wit_C_m := ∑ (i : Finₓ n_wit) in finRange n_wit, (v_wit i) * Polynomial.c (C_m i)
-- @[atom]
def p_w_wit_C_m := ∑ (i : Finₓ n_wit) in finRange n_wit, (w_wit i) * Polynomial.c (C_m i)

-- @[atom]
def p_t_A_h := ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (A_h i))
-- @[atom]
def p_t_B_h := ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (B_h i))
-- @[atom]
def p_t_C_h := ∑ (i : Finₓ (n_var - 1)) in finRange (n_var - 1), (Polynomial.x ^ (i : ℕ)) * ((t r) * Polynomial.c (C_h i))

-- @[atom]
def p_A_x := ∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.x ^ (i : ℕ)) * Polynomial.c (A_x i)
-- @[atom]
def p_B_x := ∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.x ^ (i : ℕ)) * Polynomial.c (B_x i)
-- @[atom]
def p_C_x := ∑ (i : Finₓ n_var) in finRange n_var, (Polynomial.x ^ (i : ℕ)) * Polynomial.c (C_x i)

@[simp] lemma Polynomial.c_eq_one (a : F) : Polynomial.c a = 1 ↔ a = 1 := by sorry
-- calc polynomial.C a = 1 ↔ polynomial.C a = polynomial.C 1 : by rw polynomial.C_1
--         ... ↔ a = 1 : polynomial.C_inj

lemma simplifier1 (i : Finₓ n_stmt) (a_stmt : Finₓ n_stmt → F )
  : (Polynomial.c (a_stmt i)) * (u_stmt i) = (u_stmt i) * (Polynomial.c (a_stmt i))
  := sorry

lemma simplifier2 (i : Finₓ n_stmt) (a_stmt : Finₓ n_stmt → F )
  : (Polynomial.c (a_stmt i)) * v_stmt i = (v_stmt i) * Polynomial.c (a_stmt i)
  := sorry

lemma polynomial.mul_mod_by_monic (t p : F[X]) (mt : Polynomial.Monic t) :
  let prod := (t * p : F[X])
  prod %ₘ t = 0 := by sorry
-- rw [Polynomial.dvd_iff_mod_by_monic_eq_zero]
--  apply dvd_mul_right
--  exact mt

theorem soundness (a_stmt : Finₓ n_stmt → F) (f : Vars → F) (x : F) :
  let verified_inst := @verified F field n_stmt n_wit n_var u_stmt u_wit v_stmt v_wit w_stmt w_wit r A_α A_β A_γ A_δ B_α B_β B_γ B_δ C_α C_β C_γ C_δ A_x B_x C_x A_l B_l C_l A_m B_m C_m A_h B_h C_h f x
  let satisfying_inst := @satisfying F field n_stmt n_wit u_stmt u_wit v_stmt v_wit w_stmt w_wit r
  verified_inst a_stmt
  → (satisfying_inst a_stmt C_m) := by sorry
end Groth16


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.algebra.basic
import Mathlib.algebra.algebra.subalgebra
import Mathlib.algebra.free_algebra
import Mathlib.algebra.category.CommRing.basic
import Mathlib.algebra.category.Module.basic
import Mathlib.PostPort

universes v u l u_1

namespace Mathlib

/-- The category of R-modules and their morphisms. -/
structure Algebra (R : Type u) [comm_ring R] where
  carrier : Type v
  is_ring : ring carrier
  is_algebra : algebra R carrier

namespace Algebra

protected instance has_coe_to_sort (R : Type u) [comm_ring R] : has_coe_to_sort (Algebra R) :=
  has_coe_to_sort.mk (Type v) carrier

protected instance category_theory.category (R : Type u) [comm_ring R] :
    category_theory.category (Algebra R) :=
  category_theory.category.mk

protected instance category_theory.concrete_category (R : Type u) [comm_ring R] :
    category_theory.concrete_category (Algebra R) :=
  category_theory.concrete_category.mk
    (category_theory.functor.mk (fun (R_1 : Algebra R) => ↥R_1)
      fun (R_1 S : Algebra R) (f : R_1 ⟶ S) => ⇑f)

protected instance has_forget_to_Ring (R : Type u) [comm_ring R] :
    category_theory.has_forget₂ (Algebra R) Ring :=
  category_theory.has_forget₂.mk
    (category_theory.functor.mk (fun (A : Algebra R) => Ring.of ↥A)
      fun (A₁ A₂ : Algebra R) (f : A₁ ⟶ A₂) => alg_hom.to_ring_hom f)

protected instance has_forget_to_Module (R : Type u) [comm_ring R] :
    category_theory.has_forget₂ (Algebra R) (Module R) :=
  category_theory.has_forget₂.mk
    (category_theory.functor.mk (fun (M : Algebra R) => Module.of R ↥M)
      fun (M₁ M₂ : Algebra R) (f : M₁ ⟶ M₂) => alg_hom.to_linear_map f)

/-- The object in the category of R-algebras associated to a type equipped with the appropriate
typeclasses. -/
def of (R : Type u) [comm_ring R] (X : Type v) [ring X] [algebra R X] : Algebra R := mk X

protected instance inhabited (R : Type u) [comm_ring R] : Inhabited (Algebra R) :=
  { default := of R R }

@[simp] theorem coe_of (R : Type u) [comm_ring R] (X : Type u) [ring X] [algebra R X] :
    ↥(of R X) = X :=
  rfl

/-- Forgetting to the underlying type and then building the bundled object returns the original
algebra. -/
@[simp] theorem of_self_iso_hom {R : Type u} [comm_ring R] (M : Algebra R) :
    category_theory.iso.hom (of_self_iso M) = 𝟙 :=
  Eq.refl (category_theory.iso.hom (of_self_iso M))

@[simp] theorem id_apply {R : Type u} [comm_ring R] {M : Module R} (m : ↥M) : coe_fn 𝟙 m = m := rfl

@[simp] theorem coe_comp {R : Type u} [comm_ring R] {M : Module R} {N : Module R} {U : Module R}
    (f : M ⟶ N) (g : N ⟶ U) : ⇑(f ≫ g) = ⇑g ∘ ⇑f :=
  rfl

/-- The "free algebra" functor, sending a type `S` to the free algebra on `S`. -/
@[simp] theorem free_obj_is_algebra (R : Type u) [comm_ring R] (S : Type u_1) :
    is_algebra (category_theory.functor.obj (free R) S) = free_algebra.algebra R S :=
  Eq.refl (is_algebra (category_theory.functor.obj (free R) S))

/-- The free/forget ajunction for `R`-algebras. -/
def adj (R : Type u) [comm_ring R] : free R ⊣ category_theory.forget (Algebra R) :=
  category_theory.adjunction.mk_of_hom_equiv
    (category_theory.adjunction.core_hom_equiv.mk
      fun (X : Type (max u u_1)) (A : Algebra R) => equiv.symm (free_algebra.lift R))

end Algebra

/-- Build an isomorphism in the category `Algebra R` from a `alg_equiv` between `algebra`s. -/
@[simp] theorem alg_equiv.to_Algebra_iso_hom {R : Type u} [comm_ring R] {X₁ : Type u} {X₂ : Type u}
    {g₁ : ring X₁} {g₂ : ring X₂} {m₁ : algebra R X₁} {m₂ : algebra R X₂} (e : alg_equiv R X₁ X₂) :
    category_theory.iso.hom (alg_equiv.to_Algebra_iso e) = ↑e :=
  Eq.refl (category_theory.iso.hom (alg_equiv.to_Algebra_iso e))

namespace category_theory.iso

/-- Build a `alg_equiv` from an isomorphism in the category `Algebra R`. -/
@[simp] theorem to_alg_equiv_apply {R : Type u} [comm_ring R] {X : Algebra R} {Y : Algebra R}
    (i : X ≅ Y) : ∀ (ᾰ : ↥X), coe_fn (to_alg_equiv i) ᾰ = coe_fn (hom i) ᾰ :=
  fun (ᾰ : ↥X) => Eq.refl (coe_fn (to_alg_equiv i) ᾰ)

end category_theory.iso

/-- Algebra equivalences between `algebras`s are the same as (isomorphic to) isomorphisms in
`Algebra`. -/
def alg_equiv_iso_Algebra_iso {R : Type u} [comm_ring R] {X : Type u} {Y : Type u} [ring X] [ring Y]
    [algebra R X] [algebra R Y] : alg_equiv R X Y ≅ Algebra.of R X ≅ Algebra.of R Y :=
  category_theory.iso.mk (fun (e : alg_equiv R X Y) => alg_equiv.to_Algebra_iso e)
    fun (i : Algebra.of R X ≅ Algebra.of R Y) => category_theory.iso.to_alg_equiv i

protected instance Algebra.has_coe {R : Type u} [comm_ring R] (X : Type u) [ring X] [algebra R X] :
    has_coe (subalgebra R X) (Algebra R) :=
  has_coe.mk fun (N : subalgebra R X) => Algebra.of R ↥N

protected instance Algebra.forget_reflects_isos {R : Type u} [comm_ring R] :
    category_theory.reflects_isomorphisms (category_theory.forget (Algebra R)) :=
  category_theory.reflects_isomorphisms.mk
    fun (X Y : Algebra R) (f : X ⟶ Y)
      (_x :
      category_theory.is_iso
        (category_theory.functor.map (category_theory.forget (Algebra R)) f)) =>
      let i :
        category_theory.functor.obj (category_theory.forget (Algebra R)) X ≅
          category_theory.functor.obj (category_theory.forget (Algebra R)) Y :=
        category_theory.as_iso (category_theory.functor.map (category_theory.forget (Algebra R)) f);
      let e : alg_equiv R ↥X ↥Y :=
        alg_equiv.mk (alg_hom.to_fun f) (equiv.inv_fun (category_theory.iso.to_equiv i)) sorry sorry
          sorry sorry sorry;
      category_theory.is_iso.mk (category_theory.iso.inv (alg_equiv.to_Algebra_iso e))

end Mathlib


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Johan Commelin
-/
import category_theory.limits.shapes.products
import category_theory.limits.shapes.images
import category_theory.isomorphism_classes

/-!
# Zero objects

A category "has a zero object" if it has an object which is both initial and terminal. Having a
zero object provides zero morphisms, as the unique morphisms factoring through the zero object;
see `category_theory.limits.shapes.zero_morphisms`.

## References

* [F. Borceux, *Handbook of Categorical Algebra 2*][borceux-vol2]
-/

noncomputable theory

universes v u v' u'

open category_theory
open category_theory.category

variables {C : Type u} [category.{v} C]
variables {D : Type u'} [category.{v'} D]

namespace category_theory

namespace limits

/-- An object `X` in a category is a *zero object* if for every object `Y`
there is a unique morphism `to : X → Y` and a unique morphism `from : Y → X`.

This is a characteristic predicate for `has_zero_object`. -/
structure is_zero (X : C) : Prop :=
(unique_to   : ∀ Y, nonempty (unique (X ⟶ Y)))
(unique_from : ∀ Y, nonempty (unique (Y ⟶ X)))

namespace is_zero

variables {X Y : C}

/-- If `h : is_zero X`, then `h.to Y` is a choice of unique morphism `X → Y`. -/
protected def «to» (h : is_zero X) (Y : C) : X ⟶ Y :=
@default (X ⟶ Y) $ @unique.inhabited _ $ (h.unique_to Y).some

lemma eq_to (h : is_zero X) (f : X ⟶ Y) : f = h.to Y :=
@unique.eq_default _ (id _) _

lemma to_eq (h : is_zero X) (f : X ⟶ Y) : h.to Y = f :=
(h.eq_to f).symm

/-- If `h : is_zero X`, then `h.from Y` is a choice of unique morphism `Y → X`. -/
protected def «from» (h : is_zero X) (Y : C) : Y ⟶ X :=
@default (Y ⟶ X) $ @unique.inhabited _ $ (h.unique_from Y).some

lemma eq_from (h : is_zero X) (f : Y ⟶ X) : f = h.from Y :=
@unique.eq_default _ (id _) _

lemma from_eq (h : is_zero X) (f : Y ⟶ X) : h.from Y = f :=
(h.eq_from f).symm

lemma eq_of_src (hX : is_zero X) (f g : X ⟶ Y) : f = g :=
(hX.eq_to f).trans (hX.eq_to g).symm

lemma eq_of_tgt (hX : is_zero X) (f g : Y ⟶ X) : f = g :=
(hX.eq_from f).trans (hX.eq_from g).symm

/-- Any two zero objects are isomorphic. -/
def iso (hX : is_zero X) (hY : is_zero Y) : X ≅ Y :=
{ hom := hX.to Y,
  inv := hX.from Y,
  hom_inv_id' := hX.eq_of_src _ _,
  inv_hom_id' := hY.eq_of_src _ _, }

/-- A zero object is in particular initial. -/
protected def is_initial (hX : is_zero X) : is_initial X :=
@is_initial.of_unique _ _ X $ λ Y, (hX.unique_to Y).some

/-- A zero object is in particular terminal. -/
protected def is_terminal (hX : is_zero X) : is_terminal X :=
@is_terminal.of_unique _ _ X $ λ Y, (hX.unique_from Y).some

/-- The (unique) isomorphism between any initial object and the zero object. -/
def iso_is_initial (hX : is_zero X) (hY : is_initial Y) : X ≅ Y :=
hX.is_initial.unique_up_to_iso hY

/-- The (unique) isomorphism between any terminal object and the zero object. -/
def iso_is_terminal (hX : is_zero X) (hY : is_terminal Y) : X ≅ Y :=
hX.is_terminal.unique_up_to_iso hY

lemma of_iso (hY : is_zero Y) (e : X ≅ Y) : is_zero X :=
begin
  refine ⟨λ Z, ⟨⟨⟨e.hom ≫ hY.to Z⟩, λ f, _⟩⟩, λ Z, ⟨⟨⟨hY.from Z ≫ e.inv⟩, λ f, _⟩⟩⟩,
  { rw ← cancel_epi e.inv, apply hY.eq_of_src, },
  { rw ← cancel_mono e.hom, apply hY.eq_of_tgt, },
end

end is_zero

end limits

open category_theory.limits

lemma iso.is_zero_iff {X Y : C} (e : X ≅ Y) :
  is_zero X ↔ is_zero Y :=
⟨λ h, h.of_iso e.symm, λ h, h.of_iso e⟩

lemma functor.is_zero (F : C ⥤ D) (hF : ∀ X, is_zero (F.obj X)) :
  is_zero F :=
begin
  split; intros G; refine ⟨⟨⟨_⟩, _⟩⟩,
  { refine { app := λ X, (hF _).to _, naturality' := _ },
    intros, exact (hF _).eq_of_src _ _ },
  { intro f, ext, apply (hF _).eq_of_src _ _ },
  { refine { app := λ X, (hF _).from _, naturality' := _ },
    intros, exact (hF _).eq_of_tgt _ _ },
  { intro f, ext, apply (hF _).eq_of_tgt _ _ },
end

namespace limits

variables (C)

/-- A category "has a zero object" if it has an object which is both initial and terminal. -/
class has_zero_object : Prop :=
(zero : ∃ X : C, is_zero X)

instance has_zero_object_punit : has_zero_object (discrete punit) :=
{ zero := ⟨⟨⟨⟩⟩, by tidy, by tidy⟩, }

section

variables [has_zero_object C]

/--
Construct a `has_zero C` for a category with a zero object.
This can not be a global instance as it will trigger for every `has_zero C` typeclass search.
-/
protected def has_zero_object.has_zero : has_zero C :=
{ zero := has_zero_object.zero.some }

localized "attribute [instance] category_theory.limits.has_zero_object.has_zero" in zero_object

lemma is_zero_zero : is_zero (0 : C) :=
has_zero_object.zero.some_spec

end

open_locale zero_object

variables {C}

lemma is_zero.has_zero_object {X : C} (hX : is_zero X) : has_zero_object C := ⟨⟨X, hX⟩⟩

/-- Every zero object is isomorphic to *the* zero object. -/
def is_zero.iso_zero [has_zero_object C] {X : C} (hX : is_zero X) : X ≅ 0 :=
hX.iso (is_zero_zero C)

lemma is_zero.obj [has_zero_object D] {F : C ⥤ D} (hF : is_zero F) (X : C) :
  is_zero (F.obj X) :=
begin
  let G : C ⥤ D := (category_theory.functor.const C).obj 0,
  have hG : is_zero G := functor.is_zero _ (λ X, is_zero_zero _),
  let e : F ≅ G := hF.iso hG,
  exact (is_zero_zero _).of_iso (e.app X),
end

namespace has_zero_object
variables [has_zero_object C]

/-- There is a unique morphism from the zero object to any object `X`. -/
protected def unique_to (X : C) : unique (0 ⟶ X) :=
((is_zero_zero C).unique_to X).some

/-- There is a unique morphism from any object `X` to the zero object. -/
protected def unique_from (X : C) : unique (X ⟶ 0) :=
((is_zero_zero C).unique_from X).some

localized "attribute [instance] category_theory.limits.has_zero_object.unique_to" in zero_object
localized "attribute [instance] category_theory.limits.has_zero_object.unique_from" in zero_object

@[ext]
lemma to_zero_ext {X : C} (f g : X ⟶ 0) : f = g :=
(is_zero_zero C).eq_of_tgt _ _

@[ext]
lemma from_zero_ext {X : C} (f g : 0 ⟶ X) : f = g :=
(is_zero_zero C).eq_of_src _ _

instance (X : C) : subsingleton (X ≅ 0) := by tidy

instance {X : C} (f : 0 ⟶ X) : mono f :=
{ right_cancellation := λ Z g h w, by ext, }

instance {X : C} (f : X ⟶ 0) : epi f :=
{ left_cancellation := λ Z g h w, by ext, }

instance zero_to_zero_is_iso (f : (0 : C) ⟶ 0) :
  is_iso f :=
by convert (show is_iso (𝟙 (0 : C)), by apply_instance)

/-- A zero object is in particular initial. -/
def zero_is_initial : is_initial (0 : C) :=
(is_zero_zero C).is_initial

/-- A zero object is in particular terminal. -/
def zero_is_terminal : is_terminal (0 : C) :=
(is_zero_zero C).is_terminal

/-- A zero object is in particular initial. -/
@[priority 10]
instance has_initial : has_initial C :=
has_initial_of_unique 0

/-- A zero object is in particular terminal. -/
@[priority 10]
instance has_terminal : has_terminal C :=
has_terminal_of_unique 0

/-- The (unique) isomorphism between any initial object and the zero object. -/
def zero_iso_is_initial {X : C} (t : is_initial X) : 0 ≅ X :=
zero_is_initial.unique_up_to_iso t

/-- The (unique) isomorphism between any terminal object and the zero object. -/
def zero_iso_is_terminal {X : C} (t : is_terminal X) : 0 ≅ X :=
zero_is_terminal.unique_up_to_iso t

/-- The (unique) isomorphism between the chosen initial object and the chosen zero object. -/
def zero_iso_initial [has_initial C] : 0 ≅ ⊥_ C :=
zero_is_initial.unique_up_to_iso initial_is_initial

/-- The (unique) isomorphism between the chosen terminal object and the chosen zero object. -/
def zero_iso_terminal [has_terminal C] : 0 ≅ ⊤_ C :=
zero_is_terminal.unique_up_to_iso terminal_is_terminal

@[priority 100]
instance has_strict_initial : initial_mono_class C :=
initial_mono_class.of_is_initial zero_is_initial (λ X, category_theory.mono _)

end has_zero_object

end limits

open category_theory.limits
open_locale zero_object

lemma functor.is_zero_iff [has_zero_object D] (F : C ⥤ D) :
  is_zero F ↔ ∀ X, is_zero (F.obj X) :=
⟨λ hF X, hF.obj X, functor.is_zero _⟩

end category_theory


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.ring_theory.matrix_algebra
import Mathlib.data.polynomial.algebra_map
import Mathlib.PostPort

universes u_1 u_2 w

namespace Mathlib

/-!
# Algebra isomorphism between matrices of polynomials and polynomials of matrices

Given `[comm_ring R] [ring A] [algebra R A]`
we show `polynomial A ≃ₐ[R] (A ⊗[R] polynomial R)`.
Combining this with the isomorphism `matrix n n A ≃ₐ[R] (A ⊗[R] matrix n n R)` proved earlier
in `ring_theory.matrix_algebra`, we obtain the algebra isomorphism
```
def mat_poly_equiv :
  matrix n n (polynomial R) ≃ₐ[R] polynomial (matrix n n R)
```
which is characterized by
```
coeff (mat_poly_equiv m) k i j = coeff (m i j) k
```

We will use this algebra isomorphism to prove the Cayley-Hamilton theorem.
-/

namespace poly_equiv_tensor

/--
(Implementation detail).
The bare function underlying `A ⊗[R] polynomial R →ₐ[R] polynomial A`, on pure tensors.
-/
def to_fun (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A] (a : A)
    (p : polynomial R) : polynomial A :=
  finsupp.sum p
    fun (n : ℕ) (r : R) => coe_fn (polynomial.monomial n) (a * coe_fn (algebra_map R A) r)

/--
(Implementation detail).
The function underlying `A ⊗[R] polynomial R →ₐ[R] polynomial A`,
as a linear map in the second factor.
-/
def to_fun_linear_right (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A]
    (a : A) : linear_map R (polynomial R) (polynomial A) :=
  linear_map.mk (to_fun R A a) sorry sorry

/--
(Implementation detail).
The function underlying `A ⊗[R] polynomial R →ₐ[R] polynomial A`,
as a bilinear function of two arguments.
-/
def to_fun_bilinear (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A] :
    linear_map R A (linear_map R (polynomial R) (polynomial A)) :=
  linear_map.mk (to_fun_linear_right R A) sorry sorry

/--
(Implementation detail).
The function underlying `A ⊗[R] polynomial R →ₐ[R] polynomial A`,
as a linear map.
-/
def to_fun_linear (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A] :
    linear_map R (tensor_product R A (polynomial R)) (polynomial A) :=
  tensor_product.lift (to_fun_bilinear R A)

-- We apparently need to provide the decidable instance here

-- in order to successfully rewrite by this lemma.

theorem to_fun_linear_mul_tmul_mul_aux_1 (R : Type u_1) (A : Type u_2) [comm_semiring R]
    [semiring A] [algebra R A] (p : polynomial R) (k : ℕ)
    (h : Decidable (¬polynomial.coeff p k = 0)) (a : A) :
    ite (¬polynomial.coeff p k = 0) (a * coe_fn (algebra_map R A) (polynomial.coeff p k)) 0 =
        a * coe_fn (algebra_map R A) (polynomial.coeff p k) :=
  sorry

theorem to_fun_linear_mul_tmul_mul_aux_2 (R : Type u_1) (A : Type u_2) [comm_semiring R]
    [semiring A] [algebra R A] (k : ℕ) (a₁ : A) (a₂ : A) (p₁ : polynomial R) (p₂ : polynomial R) :
    a₁ * a₂ * coe_fn (algebra_map R A) (polynomial.coeff (p₁ * p₂) k) =
        finset.sum (finset.nat.antidiagonal k)
          fun (x : ℕ × ℕ) =>
            a₁ * coe_fn (algebra_map R A) (polynomial.coeff p₁ (prod.fst x)) *
              (a₂ * coe_fn (algebra_map R A) (polynomial.coeff p₂ (prod.snd x))) :=
  sorry

theorem to_fun_linear_mul_tmul_mul (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A]
    [algebra R A] (a₁ : A) (a₂ : A) (p₁ : polynomial R) (p₂ : polynomial R) :
    coe_fn (to_fun_linear R A) (tensor_product.tmul R (a₁ * a₂) (p₁ * p₂)) =
        coe_fn (to_fun_linear R A) (tensor_product.tmul R a₁ p₁) *
          coe_fn (to_fun_linear R A) (tensor_product.tmul R a₂ p₂) :=
  sorry

theorem to_fun_linear_algebra_map_tmul_one (R : Type u_1) (A : Type u_2) [comm_semiring R]
    [semiring A] [algebra R A] (r : R) :
    coe_fn (to_fun_linear R A) (tensor_product.tmul R (coe_fn (algebra_map R A) r) 1) =
        coe_fn (algebra_map R (polynomial A)) r :=
  sorry

/--
(Implementation detail).
The algebra homomorphism `A ⊗[R] polynomial R →ₐ[R] polynomial A`.
-/
def to_fun_alg_hom (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A] :
    alg_hom R (tensor_product R A (polynomial R)) (polynomial A) :=
  algebra.tensor_product.alg_hom_of_linear_map_tensor_product (to_fun_linear R A)
    (to_fun_linear_mul_tmul_mul R A) (to_fun_linear_algebra_map_tmul_one R A)

@[simp] theorem to_fun_alg_hom_apply_tmul (R : Type u_1) (A : Type u_2) [comm_semiring R]
    [semiring A] [algebra R A] (a : A) (p : polynomial R) :
    coe_fn (to_fun_alg_hom R A) (tensor_product.tmul R a p) =
        finsupp.sum p
          fun (n : ℕ) (r : R) => coe_fn (polynomial.monomial n) (a * coe_fn (algebra_map R A) r) :=
  sorry

/--
(Implementation detail.)

The bare function `polynomial A → A ⊗[R] polynomial R`.
(We don't need to show that it's an algebra map, thankfully --- just that it's an inverse.)
-/
def inv_fun (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A]
    (p : polynomial A) : tensor_product R A (polynomial R) :=
  polynomial.eval₂ (↑algebra.tensor_product.include_left) (tensor_product.tmul R 1 polynomial.X) p

@[simp] theorem inv_fun_add (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A]
    [algebra R A] {p : polynomial A} {q : polynomial A} :
    inv_fun R A (p + q) = inv_fun R A p + inv_fun R A q :=
  sorry

theorem inv_fun_monomial (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A]
    (n : ℕ) (a : A) :
    inv_fun R A (coe_fn (polynomial.monomial n) a) =
        coe_fn algebra.tensor_product.include_left a * tensor_product.tmul R 1 polynomial.X ^ n :=
  polynomial.eval₂_monomial (↑algebra.tensor_product.include_left)
    (tensor_product.tmul R 1 polynomial.X)

theorem left_inv (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A]
    (x : tensor_product R A (polynomial R)) : inv_fun R A (coe_fn (to_fun_alg_hom R A) x) = x :=
  sorry

theorem right_inv (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A]
    (x : polynomial A) : coe_fn (to_fun_alg_hom R A) (inv_fun R A x) = x :=
  sorry

/--
(Implementation detail)

The equivalence, ignoring the algebra structure, `(A ⊗[R] polynomial R) ≃ polynomial A`.
-/
def equiv (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A] :
    tensor_product R A (polynomial R) ≃ polynomial A :=
  equiv.mk (⇑(to_fun_alg_hom R A)) (inv_fun R A) (left_inv R A) (right_inv R A)

end poly_equiv_tensor

/--
The `R`-algebra isomorphism `polynomial A ≃ₐ[R] (A ⊗[R] polynomial R)`.
-/
def poly_equiv_tensor (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A] [algebra R A] :
    alg_equiv R (polynomial A) (tensor_product R A (polynomial R)) :=
  alg_equiv.symm
    (alg_equiv.mk (alg_hom.to_fun sorry) (equiv.inv_fun sorry) sorry sorry sorry sorry sorry)

@[simp] theorem poly_equiv_tensor_apply (R : Type u_1) (A : Type u_2) [comm_semiring R] [semiring A]
    [algebra R A] (p : polynomial A) :
    coe_fn (poly_equiv_tensor R A) p =
        polynomial.eval₂ (↑algebra.tensor_product.include_left)
          (tensor_product.tmul R 1 polynomial.X) p :=
  rfl

@[simp] theorem poly_equiv_tensor_symm_apply_tmul (R : Type u_1) (A : Type u_2) [comm_semiring R]
    [semiring A] [algebra R A] (a : A) (p : polynomial R) :
    coe_fn (alg_equiv.symm (poly_equiv_tensor R A)) (tensor_product.tmul R a p) =
        finsupp.sum p
          fun (n : ℕ) (r : R) => coe_fn (polynomial.monomial n) (a * coe_fn (algebra_map R A) r) :=
  sorry

/--
The algebra isomorphism stating "matrices of polynomials are the same as polynomials of matrices".

(You probably shouldn't attempt to use this underlying definition ---
it's an algebra equivalence, and characterised extensionally by the lemma
`mat_poly_equiv_coeff_apply` below.)
-/
def mat_poly_equiv {R : Type u_1} [comm_semiring R] {n : Type w} [DecidableEq n] [fintype n] :
    alg_equiv R (matrix n n (polynomial R)) (polynomial (matrix n n R)) :=
  alg_equiv.trans
    (alg_equiv.trans (matrix_equiv_tensor R (polynomial R) n)
      (algebra.tensor_product.comm R (polynomial R) (matrix n n R)))
    (alg_equiv.symm (poly_equiv_tensor R (matrix n n R)))

theorem mat_poly_equiv_coeff_apply_aux_1 {R : Type u_1} [comm_semiring R] {n : Type w}
    [DecidableEq n] [fintype n] (i : n) (j : n) (k : ℕ) (x : R) :
    coe_fn mat_poly_equiv (matrix.std_basis_matrix i j (coe_fn (polynomial.monomial k) x)) =
        coe_fn (polynomial.monomial k) (matrix.std_basis_matrix i j x) :=
  sorry

theorem mat_poly_equiv_coeff_apply_aux_2 {R : Type u_1} [comm_semiring R] {n : Type w}
    [DecidableEq n] [fintype n] (i : n) (j : n) (p : polynomial R) (k : ℕ) :
    polynomial.coeff (coe_fn mat_poly_equiv (matrix.std_basis_matrix i j p)) k =
        matrix.std_basis_matrix i j (polynomial.coeff p k) :=
  sorry

@[simp] theorem mat_poly_equiv_coeff_apply {R : Type u_1} [comm_semiring R] {n : Type w}
    [DecidableEq n] [fintype n] (m : matrix n n (polynomial R)) (k : ℕ) (i : n) (j : n) :
    polynomial.coeff (coe_fn mat_poly_equiv m) k i j = polynomial.coeff (m i j) k :=
  sorry

@[simp] theorem mat_poly_equiv_symm_apply_coeff {R : Type u_1} [comm_semiring R] {n : Type w}
    [DecidableEq n] [fintype n] (p : polynomial (matrix n n R)) (i : n) (j : n) (k : ℕ) :
    polynomial.coeff (coe_fn (alg_equiv.symm mat_poly_equiv) p i j) k = polynomial.coeff p k i j :=
  sorry

theorem mat_poly_equiv_smul_one {R : Type u_1} [comm_semiring R] {n : Type w} [DecidableEq n]
    [fintype n] (p : polynomial R) :
    coe_fn mat_poly_equiv (p • 1) = polynomial.map (algebra_map R (matrix n n R)) p :=
  sorry

end Mathlib