/-
Copyright (c) 2021 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser

! This file was ported from Lean 3 source module algebra.char_p.two
! leanprover-community/mathlib commit 10bf4f825ad729c5653adc039dafa3622e7f93c9
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.CharP.Basic

/-!
# Lemmas about rings of characteristic two

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file contains results about `char_p R 2`, in the `char_two` namespace.

The lemmas in this file with a `_sq` suffix are just special cases of the `_pow_char` lemmas
elsewhere, with a shorter name for ease of discovery, and no need for a `[fact (prime 2)]` argument.
-/

variable {R ι : Type _}

namespace CharTwo

section Semiring

variable [Semiring R] [CharP R 2]

/- warning: char_two.two_eq_zero -> CharTwo.two_eq_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))], Eq.{succ u1} R (OfNat.ofNat.{u1} R 2 (OfNat.mk.{u1} R 2 (bit0.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (One.one.{u1} R (AddMonoidWithOne.toOne.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))))))) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))], Eq.{succ u1} R (OfNat.ofNat.{u1} R 2 (instOfNat.{u1} R 2 (Semiring.toNatCast.{u1} R _inst_1) (instAtLeastTwoHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align char_two.two_eq_zero CharTwo.two_eq_zeroₓ'. -/
theorem two_eq_zero : (2 : R) = 0 := by rw [← Nat.cast_two, CharP.cast_eq_zero]
#align char_two.two_eq_zero CharTwo.two_eq_zero

/- warning: char_two.add_self_eq_zero -> CharTwo.add_self_eq_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (x : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) x x) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (x : R), Eq.{succ u1} R (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) x x) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align char_two.add_self_eq_zero CharTwo.add_self_eq_zeroₓ'. -/
@[simp]
theorem add_self_eq_zero (x : R) : x + x = 0 := by rw [← two_smul R x, two_eq_zero, zero_smul]
#align char_two.add_self_eq_zero CharTwo.add_self_eq_zero

/- warning: char_two.bit0_eq_zero -> CharTwo.bit0_eq_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))], Eq.{succ u1} (R -> R) (bit0.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) (OfNat.ofNat.{u1} (R -> R) 0 (OfNat.mk.{u1} (R -> R) 0 (Zero.zero.{u1} (R -> R) (Pi.instZero.{u1, u1} R (fun (a : R) => R) (fun (i : R) => MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))], Eq.{succ u1} (R -> R) (bit0.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) (OfNat.ofNat.{u1} (R -> R) 0 (Zero.toOfNat0.{u1} (R -> R) (Pi.instZero.{u1, u1} R (fun (a : R) => R) (fun (i : R) => MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)))))
Case conversion may be inaccurate. Consider using '#align char_two.bit0_eq_zero CharTwo.bit0_eq_zeroₓ'. -/
@[simp]
theorem bit0_eq_zero : (bit0 : R → R) = 0 := by
  funext
  exact add_self_eq_zero _
#align char_two.bit0_eq_zero CharTwo.bit0_eq_zero

/- warning: char_two.bit0_apply_eq_zero -> CharTwo.bit0_apply_eq_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (x : R), Eq.{succ u1} R (bit0.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) x) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (x : R), Eq.{succ u1} R (bit0.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) x) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align char_two.bit0_apply_eq_zero CharTwo.bit0_apply_eq_zeroₓ'. -/
theorem bit0_apply_eq_zero (x : R) : (bit0 x : R) = 0 := by simp
#align char_two.bit0_apply_eq_zero CharTwo.bit0_apply_eq_zero

/- warning: char_two.bit1_eq_one -> CharTwo.bit1_eq_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))], Eq.{succ u1} (R -> R) (bit1.{u1} R (AddMonoidWithOne.toOne.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) (OfNat.ofNat.{u1} (R -> R) 1 (OfNat.mk.{u1} (R -> R) 1 (One.one.{u1} (R -> R) (Pi.instOne.{u1, u1} R (fun (a : R) => R) (fun (i : R) => AddMonoidWithOne.toOne.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))], Eq.{succ u1} (R -> R) (bit1.{u1} R (Semiring.toOne.{u1} R _inst_1) (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) (OfNat.ofNat.{u1} (R -> R) 1 (One.toOfNat1.{u1} (R -> R) (Pi.instOne.{u1, u1} R (fun (a : R) => R) (fun (i : R) => Semiring.toOne.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align char_two.bit1_eq_one CharTwo.bit1_eq_oneₓ'. -/
@[simp]
theorem bit1_eq_one : (bit1 : R → R) = 1 := by
  funext
  simp [bit1]
#align char_two.bit1_eq_one CharTwo.bit1_eq_one

/- warning: char_two.bit1_apply_eq_one -> CharTwo.bit1_apply_eq_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (x : R), Eq.{succ u1} R (bit1.{u1} R (AddMonoidWithOne.toOne.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) x) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (AddMonoidWithOne.toOne.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (x : R), Eq.{succ u1} R (bit1.{u1} R (Semiring.toOne.{u1} R _inst_1) (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) x) (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (Semiring.toOne.{u1} R _inst_1)))
Case conversion may be inaccurate. Consider using '#align char_two.bit1_apply_eq_one CharTwo.bit1_apply_eq_oneₓ'. -/
theorem bit1_apply_eq_one (x : R) : (bit1 x : R) = 1 := by simp
#align char_two.bit1_apply_eq_one CharTwo.bit1_apply_eq_one

end Semiring

section Ring

variable [Ring R] [CharP R 2]

/- warning: char_two.neg_eq -> CharTwo.neg_eq is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : CharP.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (x : R), Eq.{succ u1} R (Neg.neg.{u1} R (SubNegMonoid.toHasNeg.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1))))) x) x
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : CharP.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (Ring.toAddGroupWithOne.{u1} R _inst_1)) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (x : R), Eq.{succ u1} R (Neg.neg.{u1} R (Ring.toNeg.{u1} R _inst_1) x) x
Case conversion may be inaccurate. Consider using '#align char_two.neg_eq CharTwo.neg_eqₓ'. -/
@[simp]
theorem neg_eq (x : R) : -x = x := by
  rw [neg_eq_iff_add_eq_zero, ← two_smul R x, two_eq_zero, zero_smul]
#align char_two.neg_eq CharTwo.neg_eq

/- warning: char_two.neg_eq' -> CharTwo.neg_eq' is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : CharP.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))], Eq.{succ u1} (R -> R) (Neg.neg.{u1} R (SubNegMonoid.toHasNeg.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1)))))) (id.{succ u1} R)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : CharP.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (Ring.toAddGroupWithOne.{u1} R _inst_1)) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))], Eq.{succ u1} (R -> R) (Neg.neg.{u1} R (Ring.toNeg.{u1} R _inst_1)) (id.{succ u1} R)
Case conversion may be inaccurate. Consider using '#align char_two.neg_eq' CharTwo.neg_eq'ₓ'. -/
theorem neg_eq' : Neg.neg = (id : R → R) :=
  funext neg_eq
#align char_two.neg_eq' CharTwo.neg_eq'

/- warning: char_two.sub_eq_add -> CharTwo.sub_eq_add is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : CharP.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (x : R) (y : R), Eq.{succ u1} R (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (SubNegMonoid.toHasSub.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1)))))) x y) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (Ring.toDistrib.{u1} R _inst_1))) x y)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : CharP.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (Ring.toAddGroupWithOne.{u1} R _inst_1)) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (x : R) (y : R), Eq.{succ u1} R (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (Ring.toSub.{u1} R _inst_1)) x y) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1)))))) x y)
Case conversion may be inaccurate. Consider using '#align char_two.sub_eq_add CharTwo.sub_eq_addₓ'. -/
@[simp]
theorem sub_eq_add (x y : R) : x - y = x + y := by rw [sub_eq_add_neg, neg_eq]
#align char_two.sub_eq_add CharTwo.sub_eq_add

/- warning: char_two.sub_eq_add' -> CharTwo.sub_eq_add' is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : CharP.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))], Eq.{succ u1} (R -> R -> R) (HSub.hSub.{u1, u1, u1} R R R (instHSub.{u1} R (SubNegMonoid.toHasSub.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1))))))) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (Ring.toDistrib.{u1} R _inst_1))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : CharP.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (Ring.toAddGroupWithOne.{u1} R _inst_1)) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))], Eq.{succ u1} (R -> R -> R) (Sub.sub.{u1} R (Ring.toSub.{u1} R _inst_1)) (fun (x._@.Mathlib.Algebra.CharP.Two._hyg.477 : R) (x._@.Mathlib.Algebra.CharP.Two._hyg.479 : R) => HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1)))))) x._@.Mathlib.Algebra.CharP.Two._hyg.477 x._@.Mathlib.Algebra.CharP.Two._hyg.479)
Case conversion may be inaccurate. Consider using '#align char_two.sub_eq_add' CharTwo.sub_eq_add'ₓ'. -/
theorem sub_eq_add' : Sub.sub = ((· + ·) : R → R → R) :=
  funext fun x => funext fun y => sub_eq_add x y
#align char_two.sub_eq_add' CharTwo.sub_eq_add'

end Ring

section CommSemiring

variable [CommSemiring R] [CharP R 2]

/- warning: char_two.add_sq -> CharTwo.add_sq is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (x : R) (y : R), Eq.{succ u1} R (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) x y) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) x (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) y (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (x : R) (y : R), Eq.{succ u1} R (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) x y) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) x (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) y (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))))
Case conversion may be inaccurate. Consider using '#align char_two.add_sq CharTwo.add_sqₓ'. -/
theorem add_sq (x y : R) : (x + y) ^ 2 = x ^ 2 + y ^ 2 :=
  add_pow_char _ _ _
#align char_two.add_sq CharTwo.add_sq

/- warning: char_two.add_mul_self -> CharTwo.add_mul_self is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (x : R) (y : R), Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) x y) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) x y)) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) x x) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) y y))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (x : R) (y : R), Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) x y) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) x y)) (HAdd.hAdd.{u1, u1, u1} R R R (instHAdd.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) x x) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) y y))
Case conversion may be inaccurate. Consider using '#align char_two.add_mul_self CharTwo.add_mul_selfₓ'. -/
theorem add_mul_self (x y : R) : (x + y) * (x + y) = x * x + y * y := by
  rw [← pow_two, ← pow_two, ← pow_two, add_sq]
#align char_two.add_mul_self CharTwo.add_mul_self

open BigOperators

/- warning: char_two.list_sum_sq -> CharTwo.list_sum_sq is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (l : List.{u1} R), Eq.{succ u1} R (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (List.sum.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) l) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) (List.sum.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (List.map.{u1, u1} R R (fun (_x : R) => HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) _x (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) l))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (l : List.{u1} R), Eq.{succ u1} R (HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (List.sum.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R _inst_1)) l) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) (List.sum.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R _inst_1)) (List.map.{u1, u1} R R (fun (_x : R) => HPow.hPow.{u1, 0, u1} R Nat R (instHPow.{u1, 0} R Nat (Monoid.Pow.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) _x (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) l))
Case conversion may be inaccurate. Consider using '#align char_two.list_sum_sq CharTwo.list_sum_sqₓ'. -/
theorem list_sum_sq (l : List R) : l.Sum ^ 2 = (l.map (· ^ 2)).Sum :=
  list_sum_pow_char _ _
#align char_two.list_sum_sq CharTwo.list_sum_sq

/- warning: char_two.list_sum_mul_self -> CharTwo.list_sum_mul_self is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (l : List.{u1} R), Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) (List.sum.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) l) (List.sum.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) l)) (List.sum.{u1} R (Distrib.toHasAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (List.map.{u1, u1} R R (fun (x : R) => HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) x x) l))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (l : List.{u1} R), Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (List.sum.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R _inst_1)) l) (List.sum.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R _inst_1)) l)) (List.sum.{u1} R (Distrib.toAdd.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (CommMonoidWithZero.toZero.{u1} R (CommSemiring.toCommMonoidWithZero.{u1} R _inst_1)) (List.map.{u1, u1} R R (fun (x : R) => HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) x x) l))
Case conversion may be inaccurate. Consider using '#align char_two.list_sum_mul_self CharTwo.list_sum_mul_selfₓ'. -/
theorem list_sum_mul_self (l : List R) : l.Sum * l.Sum = (List.map (fun x => x * x) l).Sum := by
  simp_rw [← pow_two, list_sum_sq]
#align char_two.list_sum_mul_self CharTwo.list_sum_mul_self

#print CharTwo.multiset_sum_sq /-
theorem multiset_sum_sq (l : Multiset R) : l.Sum ^ 2 = (l.map (· ^ 2)).Sum :=
  multiset_sum_pow_char _ _
#align char_two.multiset_sum_sq CharTwo.multiset_sum_sq
-/

/- warning: char_two.multiset_sum_mul_self -> CharTwo.multiset_sum_mul_self is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (l : Multiset.{u1} R), Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) (Multiset.sum.{u1} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) l) (Multiset.sum.{u1} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) l)) (Multiset.sum.{u1} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (Multiset.map.{u1, u1} R R (fun (x : R) => HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) x x) l))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (l : Multiset.{u1} R), Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (Multiset.sum.{u1} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) l) (Multiset.sum.{u1} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) l)) (Multiset.sum.{u1} R (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (Multiset.map.{u1, u1} R R (fun (x : R) => HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) x x) l))
Case conversion may be inaccurate. Consider using '#align char_two.multiset_sum_mul_self CharTwo.multiset_sum_mul_selfₓ'. -/
theorem multiset_sum_mul_self (l : Multiset R) :
    l.Sum * l.Sum = (Multiset.map (fun x => x * x) l).Sum := by simp_rw [← pow_two, multiset_sum_sq]
#align char_two.multiset_sum_mul_self CharTwo.multiset_sum_mul_self

#print CharTwo.sum_sq /-
theorem sum_sq (s : Finset ι) (f : ι → R) : (∑ i in s, f i) ^ 2 = ∑ i in s, f i ^ 2 :=
  sum_pow_char _ _ _
#align char_two.sum_sq CharTwo.sum_sq
-/

/- warning: char_two.sum_mul_self -> CharTwo.sum_mul_self is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {ι : Type.{u2}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))] (s : Finset.{u2} ι) (f : ι -> R), Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) (Finset.sum.{u1, u2} R ι (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) s (fun (i : ι) => f i)) (Finset.sum.{u1, u2} R ι (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) s (fun (i : ι) => f i))) (Finset.sum.{u1, u2} R ι (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) s (fun (i : ι) => HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))))) (f i) (f i)))
but is expected to have type
  forall {R : Type.{u1}} {ι : Type.{u2}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : CharP.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))] (s : Finset.{u2} ι) (f : ι -> R), Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (Finset.sum.{u1, u2} R ι (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) s (fun (i : ι) => f i)) (Finset.sum.{u1, u2} R ι (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) s (fun (i : ι) => f i))) (Finset.sum.{u1, u2} R ι (NonUnitalNonAssocSemiring.toAddCommMonoid.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)))) s (fun (i : ι) => HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1))))) (f i) (f i)))
Case conversion may be inaccurate. Consider using '#align char_two.sum_mul_self CharTwo.sum_mul_selfₓ'. -/
theorem sum_mul_self (s : Finset ι) (f : ι → R) :
    ((∑ i in s, f i) * ∑ i in s, f i) = ∑ i in s, f i * f i := by simp_rw [← pow_two, sum_sq]
#align char_two.sum_mul_self CharTwo.sum_mul_self

end CommSemiring

end CharTwo

section ringChar

variable [Ring R]

/- warning: neg_one_eq_one_iff -> neg_one_eq_one_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : Nontrivial.{u1} R], Iff (Eq.{succ u1} R (Neg.neg.{u1} R (SubNegMonoid.toHasNeg.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1))))) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (AddMonoidWithOne.toOne.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1)))))))) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (AddMonoidWithOne.toOne.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1)))))))) (Eq.{1} Nat (ringChar.{u1} R (NonAssocRing.toNonAssocSemiring.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : Nontrivial.{u1} R], Iff (Eq.{succ u1} R (Neg.neg.{u1} R (Ring.toNeg.{u1} R _inst_1) (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (NonAssocRing.toOne.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1))))) (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (NonAssocRing.toOne.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1))))) (Eq.{1} Nat (ringChar.{u1} R (NonAssocRing.toNonAssocSemiring.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)))
Case conversion may be inaccurate. Consider using '#align neg_one_eq_one_iff neg_one_eq_one_iffₓ'. -/
theorem neg_one_eq_one_iff [Nontrivial R] : (-1 : R) = 1 ↔ ringChar R = 2 :=
  by
  refine' ⟨fun h => _, fun h => @CharTwo.neg_eq _ (ringChar.of_eq h) 1⟩
  rw [eq_comm, ← sub_eq_zero, sub_neg_eq_add, ← Nat.cast_one, ← Nat.cast_add] at h
  exact ((Nat.dvd_prime Nat.prime_two).mp (ringChar.dvd h)).resolve_left CharP.ringChar_ne_one
#align neg_one_eq_one_iff neg_one_eq_one_iff

/- warning: order_of_neg_one -> orderOf_neg_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : Nontrivial.{u1} R], Eq.{1} Nat (orderOf.{u1} R (Ring.toMonoid.{u1} R _inst_1) (Neg.neg.{u1} R (SubNegMonoid.toHasNeg.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1))))) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (AddMonoidWithOne.toOne.{u1} R (AddGroupWithOne.toAddMonoidWithOne.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1))))))))) (ite.{1} Nat (Eq.{1} Nat (ringChar.{u1} R (NonAssocRing.toNonAssocSemiring.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) (Nat.decidableEq (ringChar.{u1} R (NonAssocRing.toNonAssocSemiring.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] [_inst_2 : Nontrivial.{u1} R], Eq.{1} Nat (orderOf.{u1} R (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R (Ring.toSemiring.{u1} R _inst_1))) (Neg.neg.{u1} R (Ring.toNeg.{u1} R _inst_1) (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (NonAssocRing.toOne.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1)))))) (ite.{1} Nat (Eq.{1} Nat (ringChar.{u1} R (NonAssocRing.toNonAssocSemiring.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) (instDecidableEqNat (ringChar.{u1} R (NonAssocRing.toNonAssocSemiring.{u1} R (Ring.toNonAssocRing.{u1} R _inst_1))) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)))
Case conversion may be inaccurate. Consider using '#align order_of_neg_one orderOf_neg_oneₓ'. -/
@[simp]
theorem orderOf_neg_one [Nontrivial R] : orderOf (-1 : R) = if ringChar R = 2 then 1 else 2 :=
  by
  split_ifs
  · rw [neg_one_eq_one_iff.2 h, orderOf_one]
  apply orderOf_eq_prime
  · simp
  simpa [neg_one_eq_one_iff] using h
#align order_of_neg_one orderOf_neg_one

end ringChar


import prelim.induction prelim.collections
import matroid.rankfun matroid.indep matroid.submatroid.order
import .uniform

open matroid set
open_locale classical big_operators

noncomputable theory

section relax
variables {α : Type*} [fintype α] --[decidable_eq (set α)]

def relax.r (M : matroid α) (C : set α) (X : set α) : ℤ:=
  ite (X = C ∧ M.is_circuit_hyperplane C) (M.r X + 1) (M.r X)

lemma relax_r_eq {M : matroid α} {C : set α} (hC : M.is_circuit_hyperplane C) (X : set α) :
  relax.r M C X = ite (X = C) (M.r X + 1) (M.r X) :=
by {rcases em (X = C) with (rfl | hX), simp [relax.r], tauto, rw [relax.r, if_neg, if_neg]; tauto}

lemma relax_junk_r_eq {M : matroid α} {C : set α} (hC : ¬M.is_circuit_hyperplane C) (X : set α) :
  relax.r M C X = M.r X :=
by {rw [relax.r, if_neg _], tauto,}

lemma relax.r_of_C_eq_univ {M : matroid α} {C : set α} (hC : is_circuit_hyperplane M C) :
  relax.r M C C = M.r univ :=
  by {rw [relax_r_eq hC, if_pos rfl], linarith [circuit_hyperplane_rank hC]}

lemma relax.r_of_C {M : matroid α} {C : set α} (hC : is_circuit_hyperplane M C) :
  relax.r M C C = M.r C + 1 :=
  by {rw [relax_r_eq hC, if_pos rfl]}

lemma relax.r_of_not_C (M : matroid α){C X: set α} (hXC : X ≠ C) :
  relax.r M C X = M.r X :=
  by {unfold relax.r, finish, }

lemma r_le_relax_r (M : matroid α) (C X : set α) :
  M.r X ≤ relax.r M C X :=
begin
  by_cases hC:  (M.is_circuit_hyperplane C),
    rw [relax_r_eq hC], by_cases X = C,
      rw if_pos h, linarith,
    rw if_neg h,
  rw relax_junk_r_eq hC,
end

lemma relax.r_le_univ {M : matroid α} {C : set α} (X : set α) :
  relax.r M C X ≤ M.r univ :=
begin
  by_cases hC : M.is_circuit_hyperplane C, swap,
  { rw relax_junk_r_eq hC, apply rank_le_univ},
  by_cases h : X = C,
  rw [h, relax.r_of_C hC, circuit_hyperplane_rank hC], linarith,
  rw [relax.r_of_not_C M h], apply rank_le_univ,
end

lemma relax.R0 (M : matroid α) (C : set α) :
  satisfies_R0 (relax.r M C) :=
  λ X, le_trans (M.rank_nonneg X) (r_le_relax_r M C X)

lemma relax.R1 {M : matroid α} {C : set α} :
  satisfies_R1 (relax.r M C) :=
begin
  by_cases hC : M.is_circuit_hyperplane C, swap,
  { intro X, rw relax_junk_r_eq hC, apply rank_le_size },
  intro X, rw relax_r_eq hC, by_cases h : X = C,
  rw if_pos h,
  rcases hC with ⟨h₁,h₂⟩,
  rw circuit_iff_r at h₁,
  rw h, linarith,
  rw if_neg h,
  from M.rank_le_size X,
end

lemma relax.R2 {M : matroid α} {C : set α} :
  satisfies_R2 (relax.r M C) :=
begin
  by_cases hC : M.is_circuit_hyperplane C, swap,
  { intros X Y hXY, simp_rw relax_junk_r_eq hC, apply rank_mono _ hXY,  },
  intros X Y hXY,
  rcases em (X = C) with rfl | h,
  { rw [relax.r_of_C_eq_univ hC],
    cases subset_ssubset_or_eq hXY with h' h',
    { linarith [circuit_hyperplane_ssupset_rank hC h',
        relax.r_of_not_C M (ne_of_ssubset h').symm]},
      rwa [←h', relax.r_of_C_eq_univ]},
  linarith [relax.r_of_not_C M h, r_le_relax_r M C Y, rank_mono M hXY],
end

lemma relax.R3 {M : matroid α} {C : set α} :
  satisfies_R3 (relax.r M C) :=
begin
  by_cases hC : M.is_circuit_hyperplane C, swap,
  { intros X Y, simp_rw relax_junk_r_eq hC, apply rank_submod},
  intros X Y,
  by_cases hi : X ∩ Y = C; by_cases hu : X ∪ Y = C,

  simp only [ eq_of_union_eq_inter (eq.trans hu hi.symm), inter_self, union_self],

  rw [relax.r_of_not_C M hu,hi],
  have hCXY' : (C ⊆ X) ∧ (C ⊆ Y) := by {simp_rw ←hi, from ⟨inter_subset_left _ _, inter_subset_right _ _⟩, },
  have hCXY : (C ⊂ X ∨ X = C) ∧ (C ⊂ Y ∨ Y = C) := by {simp_rw [eq_comm, ←subset_iff_ssubset_or_eq], from hCXY'},
  rcases hCXY with ⟨(hCX | hCX), (hCY|hCY)⟩,
  rw [relax.r_of_not_C M (ne.symm (ne_of_ssubset hCX)), circuit_hyperplane_ssupset_rank hC hCX],
  rw [relax.r_of_not_C M (ne.symm (ne_of_ssubset hCY)), circuit_hyperplane_ssupset_rank hC hCY],
  linarith [rank_le_univ M (X ∪ Y), relax.r_of_C hC, circuit_hyperplane_rank hC],
  rw [hCY, union_comm X, subset_iff_union_eq_left.mp hCXY'.1],
  linarith [r_le_relax_r M C X],
  rw [hCX, subset_iff_union_eq_left.mp hCXY'.2],
  linarith [r_le_relax_r M C Y],
  rw [hCY, hCX, union_self],
  linarith [r_le_relax_r M C C],

  have hCXY' : (X ⊆ C) ∧ (Y ⊆ C) := by {simp_rw ←hu, from ⟨subset_union_left _ _, subset_union_right _ _⟩, },
  have hCXY : (X ⊂ C ∨ X = C) ∧ (Y ⊂ C ∨ Y = C) := by {simp_rw [←subset_iff_ssubset_or_eq], from hCXY'},
  rw [relax.r_of_not_C M hi, hu],
  rcases hCXY with ⟨(hCX|hCX),(hCY|hCY)⟩,
  rw [relax.r_of_not_C M (ne_of_ssubset hCX), circuit_hyperplane_ssubset_rank hC hCX],
  rw [relax.r_of_not_C M (ne_of_ssubset hCY), circuit_hyperplane_ssubset_rank hC hCY],
  rw [relax.r_of_C hC, circuit_hyperplane_rank_size hC, ←hu],
  linarith [size_modular X Y, M.rank_le_size (X ∩ Y)],
  rw [hCY], linarith [relax.r_of_not_C M (ne_of_ssubset hCX), M.rank_mono (inter_subset_left X C)],
  rw [hCX], linarith [relax.r_of_not_C M (ne_of_ssubset hCY), M.rank_mono (inter_subset_right C Y)],
  rw [hCY, hCX, inter_self], linarith [r_le_relax_r M C C],

  rw [relax.r_of_not_C M hi, relax.r_of_not_C M hu],
  linarith [M.R3 X Y, r_le_relax_r M C X, r_le_relax_r M C Y],
end

/-- relaxation of the circuit_hyperplane C in M -/
def relax (M : matroid α) (C : set α) : matroid α :=
  ⟨relax.r M C, relax.R0 M C, relax.R1, relax.R2, relax.R3⟩

lemma relax_r_def (M : matroid α) (C : set α) :
  (relax M C).r = relax.r M C :=
rfl

lemma relax_weak_image (M : matroid α) (C : set α) :
  M ≤ (relax M C) :=
λ X, r_le_relax_r _ _ X

theorem relax.dual (M : matroid α) (C : set α) :
  (relax M C).dual = relax (M.dual) Cᶜ :=
begin
  by_cases hC : M.is_circuit_hyperplane C, swap,
  { ext X, have hCd := hC, rw circuit_hyperplane_dual at hCd,
    simp_rw [dual_r, relax_r_def, relax_junk_r_eq hC, relax_junk_r_eq hCd, dual_r],  },
  let hCc := circuit_hyperplane_dual.mp hC,

  ext X,
  have hCuniv : univ ≠ C,
  { intro h, have := circuit_hyperplane_rank hC, rw ←h at this, linarith},
  by_cases h : X = Cᶜ,
  { simp_rw [dual_r, h, compl_compl, relax, relax.r_of_C hC, relax.r_of_C hCc],
    rw [dual_r, compl_compl, relax.r_of_not_C M hCuniv], linarith},

  have h' : Xᶜ ≠ C := λ hcon, by {rw [←hcon, compl_compl] at h, finish},
  simp_rw [dual_r, relax_r_def, relax.r_of_not_C _ h, dual_r,
    relax.r_of_not_C M h', relax.r_of_not_C M hCuniv ],
end

theorem single_rank_neq_is_relaxation {M₁ M₂ : matroid α} {X : set α} (hr : M₁.r univ = M₂.r univ)
(hX : M₁.r X < M₂.r X) (h_other : ∀ Y, Y ≠ X → M₁.r Y = M₂.r Y) :
  is_circuit_hyperplane M₁ X ∧ M₂ = relax M₁ X :=
begin
  have hne : M₁ ≠ M₂ := λ h, by {rw h at hX, from lt_irrefl _ hX },
  cases circuit_ind_of_distinct hne with X' hX',
  have hXX' : X' = X := by
  {
    by_contra hXX',
    simp_rw [circuit_iff_r, indep_iff_r] at hX',
    cases hX';
    linarith [h_other _ hXX'],
  },
  simp_rw hXX' at hX',
  have : is_circuit M₁ X ∧ is_indep M₂ X := by
  {
    cases hX', assumption,
    simp_rw [circuit_iff_r, indep_iff_r] at hX',
    linarith,
  },
  cases this with hXcct hXind,
  have hdne : dual M₁ ≠ dual M₂ := λ heq, hne (dual_inj heq),
  cases circuit_ind_of_distinct hdne with Z hZ,
  have hXZ : Zᶜ = X := by
  {
    by_contra hXZ,
    repeat {rw [←is_cocircuit] at hZ},
    simp_rw [cocircuit_iff_r, coindep_iff_r] at hZ,
    cases hZ;
    linarith [h_other _ hXZ],
  },
  have : is_circuit (dual M₁) Z ∧ is_indep (dual M₂) Z := by
  {
    cases hZ,
    assumption,
    rw [←is_cocircuit, cocircuit_iff_r, coindep_iff_r, hXZ] at hZ,
    linarith,
  },
  rw (compl_pair hXZ) at this,
  cases this with hXhp _,
  rw [←is_cocircuit, cocircuit_iff_compl_hyperplane, compl_compl] at hXhp,
  let hch : is_circuit_hyperplane M₁ X := ⟨hXcct, hXhp⟩,
  use hch,
  ext Y,
  by_cases hYX : Y = X,
  simp_rw [hYX, relax, relax.r_of_C hch],
  linarith [r_cct hXcct, r_indep hXind],
  simp_rw [relax, relax.r_of_not_C _ hYX, eq_comm],
  from h_other Y hYX,
end

lemma single_rank_disagreement_univ (hα : nonempty α){M₁ M₂ : matroid α} :
   M₁.r univ < M₂.r univ → (∀ X, X ≠ univ → M₁.r X = M₂.r X) →
    M₁ = unif.circuit_matroid_on α ∧ M₂ = unif.free_matroid_on α  :=
begin
  intros huniv hother,
  rw [unif.circuit_matroid_iff_univ_circuit, unif.free_iff_univ_indep],
  have hM₁M₂ : M₁ ≠ M₂ := λ h, by {rw h at huniv, from lt_irrefl _ huniv},
  cases circuit_ind_of_distinct hM₁M₂ with Z hZ,
  by_cases Z = univ; cases hZ,
  rw h at hZ, assumption,
  rw [h,circuit_iff_r, indep_iff_r] at hZ, linarith,
  rw [circuit_iff_r, indep_iff_r] at hZ, linarith [hother Z h],
  rw [circuit_iff_r, indep_iff_r] at hZ, linarith [hother Z h],
  assumption,
end

end relax


theorem add_le_add_left {a b : mynat} (h : a ≤ b) (t : mynat) :
  t + a ≤ t + b :=
begin
cases h with d hd,
use d,
rw hd,
cc,
end


/-
Author: <Redacted for anonymized submission>
-/
import data.mv_polynomial.comm_ring
import data.polynomial.div
import data.polynomial.field_division
import algebra.polynomial.big_operators
import .vars
import ...attributes
import ...integral_domain_tactic

/-!
# Knowledge Soundness

This file proves the knowledge-soundness property of the second, refined protocol described in the GGPR paper.

-/

open_locale big_operators

section

noncomputable theory

universes u

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

/-- The naturals representing:
  m - m from paper - The QAP size
  n_in - n from paper - the number of inputs
  n_out - n' from paper - the number of outputs
  n_mid - (m - N) from paper - the number of internal gates
  d - the degree of h -/
parameters {n_in n_out n_mid d : ℕ}

-- N from paper
def n_stmt := n_in + n_out

-- Alternative name
def n_wit := n_mid

def m := n_stmt + n_wit

/-- fin-indexed collections of polynomials from the quadratic arithmetic program -/
parameter {v_stmt : fin n_stmt → polynomial F }
-- parameter {w_stmt : fin n_stmt → polynomial F }
-- parameter {y_stmt : fin n_stmt → polynomial F }
parameter {v_wit : fin n_wit → polynomial F }
-- parameter {w_wit : fin n_wit → polynomial F }
parameter {w : fin m → polynomial F }
-- parameter {y_wit : fin n_wit → polynomial F }
parameter {v_0 : polynomial F }
parameter {w_0 : polynomial F }
-- parameter {y_0 : polynomial F }

/-- The roots of the polynomial t -/
parameter {r : fin m → F}
/-- t is the polynomial divisibility by which is used to verify satisfaction of the QAP -/
def t : polynomial F := ∏ i in (finset.fin_range m), (polynomial.X - polynomial.C (r i))

/-- t has degree m -/
lemma nat_degree_t : t.nat_degree = m
:=
begin
  -- rw polynomial.nat_degree,
  rw t,
  rw polynomial.nat_degree_prod,
  simp,
  intros i hi,
  exact polynomial.X_sub_C_ne_zero (r i),
end

lemma monic_t : t.monic
:=
begin
  rw t,
  apply polynomial.monic_prod_of_monic,
  intros i hi,
  exact polynomial.monic_X_sub_C (r i),
end

lemma degree_t_pos : 0 < m → 0 < t.degree
:=
begin
  intro hm,
  suffices h : t.degree = some m,
    rw h,
    apply with_bot.some_lt_some.2,
    exact hm,

  have h := nat_degree_t,
  rw polynomial.nat_degree at h,

  induction h1 : t.degree,

  rw h1 at h,
  rw option.get_or_else at h,
  rw h at hm,
  have h2 := has_lt.lt.false hm,
  exfalso,
  exact h2,

  rw h1 at h,
  rw option.get_or_else at h,
  rw h,
end

-- -- Single variable form of V_wit
-- def V_wit_sv (a_wit : fin n_wit → F) : polynomial F
-- := ∑ i in finset.fin_range 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) : polynomial F
-- := ∑ i in (finset.fin_range n_stmt), a_stmt i • u_stmt i

def satisfying (a_stmt : fin n_stmt → F) (a_wit : fin n_wit → F) (b : fin m -> F) :=
(
  (v_0
    + (∑ i in (finset.fin_range n_stmt), a_stmt i • v_stmt i)
    + ∑ i in (finset.fin_range n_wit), a_wit i • v_wit i)
 *
  (w_0
    + (∑ i in (finset.fin_range m), b i • w i)))
   %ₘ t = 0

/- Multivariable polynomial definititons and ultilities -/

run_cmd mk_simp_attr `poly
run_cmd tactic.add_doc_string `simp_attr.poly "Attribute for defintions of poly elements"

-- Helpers for representing vars as polynomials

@[poly] def β_v_poly : mv_polynomial vars (polynomial F) := mv_polynomial.X vars.β_v
@[poly] def β_w_poly : mv_polynomial vars (polynomial F) := mv_polynomial.X vars.β_w
@[poly] def s_poly : mv_polynomial vars (polynomial F) := mv_polynomial.C (polynomial.X)
@[poly] def α_poly : mv_polynomial vars (polynomial F) := mv_polynomial.X vars.α
@[poly] def γ_poly : mv_polynomial vars (polynomial F) := mv_polynomial.X vars.γ

@[poly] def crs_powers (i : fin d) : (mv_polynomial vars (polynomial F)) := mv_polynomial.C (polynomial.X ^ (i : ℕ))
@[poly] def crs_α_powers (i : fin d) : (mv_polynomial vars (polynomial F)) := α_poly * mv_polynomial.C (polynomial.X ^ (i : ℕ))
@[poly] def crs_v_wit_k (k : fin n_wit) : mv_polynomial vars (polynomial F) :=
  mv_polynomial.C (v_wit k)
@[poly] def crs_w_k (k : fin m) : mv_polynomial vars (polynomial F) :=
  mv_polynomial.C (w k)
@[poly] def crs_α_v_wit_k (k : fin n_wit) : mv_polynomial vars (polynomial F) :=
  α_poly * mv_polynomial.C (v_wit k)
@[poly] def crs_α_w_k (k : fin m) : mv_polynomial vars (polynomial F) :=
  α_poly * mv_polynomial.C (w k)
@[poly] def crs_β_v_wit_k (k : fin n_wit) : mv_polynomial vars (polynomial F) :=
  β_v_poly * mv_polynomial.C (v_wit k)
@[poly] def crs_β_w_k (k : fin m) : mv_polynomial vars (polynomial F) :=
  β_w_poly * mv_polynomial.C (w k)

-- The crs elements as multivariate polynomials of the toxic waste samples: Verification key elements

@[poly] def crs_α : mv_polynomial vars (polynomial F) := α_poly
@[poly] def crs_γ : mv_polynomial vars (polynomial F) := γ_poly
@[poly] def crs_β_v_γ : mv_polynomial vars (polynomial F) := β_v_poly * γ_poly
@[poly] def crs_β_w_γ : mv_polynomial vars (polynomial F) := β_w_poly * γ_poly
@[poly] def crs_v_0 : mv_polynomial vars (polynomial F) :=
  mv_polynomial.C (v_0)
@[poly] def crs_v_stmt (i : fin n_stmt) : mv_polynomial vars (polynomial F) :=
  mv_polynomial.C (v_stmt i)
@[poly] def crs_w_0 : mv_polynomial vars (polynomial F) :=
  mv_polynomial.C (w_0)
@[poly] def crs_t : mv_polynomial vars (polynomial F) :=
  mv_polynomial.C (t)

-- Coefficients of the CRS elements in the representation of the v_wit proof element in the AGM
parameters {v_wit_comp_crs_powers : fin d → F}
parameters {v_wit_comp_crs_α_powers : fin d → F}
parameters {v_wit_comp_crs_v_wit_k : fin n_wit →  F}
parameters {v_wit_comp_crs_w_k : fin m → F}
parameters {v_wit_comp_crs_α_v_wit_k : fin n_wit →  F}
parameters {v_wit_comp_crs_α_w_k : fin m →  F}
parameters {v_wit_comp_crs_β_v_wit_k : fin n_wit →  F}
parameters {v_wit_comp_crs_β_w_k : fin m →  F}

parameters {v_wit_comp_crs_α : F}
parameters {v_wit_comp_crs_γ : F}
parameters {v_wit_comp_crs_β_v_γ : F}
parameters {v_wit_comp_crs_β_w_γ : F}
parameters {v_wit_comp_crs_v_0 : F}
parameters {v_wit_comp_crs_w_0 : F}

parameters {v_wit_comp_crs_v_stmt : fin n_stmt → F}

parameters {v_wit_comp_crs_t : F}

/-- Polynomial form of the representation of the v_wit proof element in the AGM -/
def proof_v_wit : mv_polynomial vars (polynomial F) :=
  ∑ i in (finset.fin_range d), (crs_powers i) * mv_polynomial.C (polynomial.C (v_wit_comp_crs_powers i))
  +
  ∑ i in (finset.fin_range d), (crs_α_powers i) * mv_polynomial.C (polynomial.C (v_wit_comp_crs_α_powers i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_v_wit_k i) * mv_polynomial.C (polynomial.C (v_wit_comp_crs_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_w_k i) * mv_polynomial.C (polynomial.C (v_wit_comp_crs_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_α_v_wit_k i) * mv_polynomial.C (polynomial.C (v_wit_comp_crs_α_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_α_w_k i) * mv_polynomial.C (polynomial.C (v_wit_comp_crs_α_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_β_v_wit_k i) * mv_polynomial.C (polynomial.C (v_wit_comp_crs_β_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_β_w_k i) * mv_polynomial.C (polynomial.C (v_wit_comp_crs_β_w_k i))
  +
  crs_α * mv_polynomial.C (polynomial.C (v_wit_comp_crs_α))
  +
  crs_γ * mv_polynomial.C (polynomial.C (v_wit_comp_crs_γ))
  +
  crs_β_v_γ * mv_polynomial.C (polynomial.C (v_wit_comp_crs_β_v_γ))
  +
  crs_β_w_γ * mv_polynomial.C (polynomial.C (v_wit_comp_crs_β_w_γ))
  +
  crs_t * mv_polynomial.C (polynomial.C (v_wit_comp_crs_t))
  +
  crs_v_0 * mv_polynomial.C (polynomial.C (v_wit_comp_crs_v_0))
  +
  crs_w_0 * mv_polynomial.C (polynomial.C (v_wit_comp_crs_w_0))
  +
  ∑ i in (finset.fin_range n_stmt), (crs_v_stmt i) * mv_polynomial.C (polynomial.C (v_wit_comp_crs_v_stmt i))

-- Coefficients of the CRS elements in the representation of the w proof element in the AGM
parameters {w_comp_crs_powers : fin d → F}
parameters {w_comp_crs_α_powers : fin d → F}
parameters {w_comp_crs_v_wit_k : fin n_wit →  F}
parameters {w_comp_crs_w_k : fin m → F}
parameters {w_comp_crs_α_v_wit_k : fin n_wit →  F}
parameters {w_comp_crs_α_w_k : fin m →  F}
parameters {w_comp_crs_β_v_wit_k : fin n_wit →  F}
parameters {w_comp_crs_β_w_k : fin m →  F}

parameters {w_comp_crs_α : F}
parameters {w_comp_crs_γ : F}
parameters {w_comp_crs_β_v_γ : F}
parameters {w_comp_crs_β_w_γ : F}
parameters {w_comp_crs_v_0 : F}
parameters {w_comp_crs_w_0 : F}

parameters {w_comp_crs_v_stmt : fin n_stmt → F}

parameters {w_comp_crs_t : F}

/-- Polynomial form of the representation of the w proof element in the AGM -/
def proof_w : mv_polynomial vars (polynomial F) :=
  ∑ i in (finset.fin_range d), (crs_powers i) * mv_polynomial.C (polynomial.C (w_comp_crs_powers i))
  +
  ∑ i in (finset.fin_range d), (crs_α_powers i) * mv_polynomial.C (polynomial.C (w_comp_crs_α_powers i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_v_wit_k i) * mv_polynomial.C (polynomial.C (w_comp_crs_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_w_k i) * mv_polynomial.C (polynomial.C (w_comp_crs_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_α_v_wit_k i) * mv_polynomial.C (polynomial.C (w_comp_crs_α_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_α_w_k i) * mv_polynomial.C (polynomial.C (w_comp_crs_α_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_β_v_wit_k i) * mv_polynomial.C (polynomial.C (w_comp_crs_β_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_β_w_k i) * mv_polynomial.C (polynomial.C (w_comp_crs_β_w_k i))
  +
  crs_α * mv_polynomial.C (polynomial.C (w_comp_crs_α))
  +
  crs_γ * mv_polynomial.C (polynomial.C (w_comp_crs_γ))
  +
  crs_β_v_γ * mv_polynomial.C (polynomial.C (w_comp_crs_β_v_γ))
  +
  crs_β_w_γ * mv_polynomial.C (polynomial.C (w_comp_crs_β_w_γ))
  +
  crs_t * mv_polynomial.C (polynomial.C (w_comp_crs_t))
  +
  crs_v_0 * mv_polynomial.C (polynomial.C (w_comp_crs_v_0))
  +
  crs_w_0 * mv_polynomial.C (polynomial.C (w_comp_crs_w_0))
  +
  ∑ i in (finset.fin_range n_stmt), (crs_v_stmt i) * mv_polynomial.C (polynomial.C (w_comp_crs_v_stmt i))

-- Coefficients of the CRS elements in the representation of the h proof element in the AGM
parameters {h_comp_crs_powers : fin d → F}
parameters {h_comp_crs_α_powers : fin d → F}
parameters {h_comp_crs_v_wit_k : fin n_wit →  F}
parameters {h_comp_crs_w_k : fin m → F}
parameters {h_comp_crs_α_v_wit_k : fin n_wit →  F}
parameters {h_comp_crs_α_w_k : fin m →  F}
parameters {h_comp_crs_β_v_wit_k : fin n_wit →  F}
parameters {h_comp_crs_β_w_k : fin m →  F}

parameters {h_comp_crs_α : F}
parameters {h_comp_crs_γ : F}
parameters {h_comp_crs_β_v_γ : F}
parameters {h_comp_crs_β_w_γ : F}
parameters {h_comp_crs_v_0 : F}
parameters {h_comp_crs_w_0 : F}

parameters {h_comp_crs_v_stmt : fin n_stmt → F}

parameters {h_comp_crs_t : F}

/-- Polynomial form of the representation of the v_wit proof element in the AGM -/
def proof_h : mv_polynomial vars (polynomial F) :=
  ∑ i in (finset.fin_range d), (crs_powers i) * mv_polynomial.C (polynomial.C (h_comp_crs_powers i))
  +
  ∑ i in (finset.fin_range d), (crs_α_powers i) * mv_polynomial.C (polynomial.C (h_comp_crs_α_powers i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_v_wit_k i) * mv_polynomial.C (polynomial.C (h_comp_crs_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_w_k i) * mv_polynomial.C (polynomial.C (h_comp_crs_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_α_v_wit_k i) * mv_polynomial.C (polynomial.C (h_comp_crs_α_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_α_w_k i) * mv_polynomial.C (polynomial.C (h_comp_crs_α_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_β_v_wit_k i) * mv_polynomial.C (polynomial.C (h_comp_crs_β_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_β_w_k i) * mv_polynomial.C (polynomial.C (h_comp_crs_β_w_k i))
  +
  crs_α * mv_polynomial.C (polynomial.C (h_comp_crs_α))
  +
  crs_γ * mv_polynomial.C (polynomial.C (h_comp_crs_γ))
  +
  crs_β_v_γ * mv_polynomial.C (polynomial.C (h_comp_crs_β_v_γ))
  +
  crs_β_w_γ * mv_polynomial.C (polynomial.C (h_comp_crs_β_w_γ))
  +
  crs_t * mv_polynomial.C (polynomial.C (h_comp_crs_t))
  +
  crs_v_0 * mv_polynomial.C (polynomial.C (h_comp_crs_v_0))
  +
  crs_w_0 * mv_polynomial.C (polynomial.C (h_comp_crs_w_0))
  +
  ∑ i in (finset.fin_range n_stmt), (crs_v_stmt i) * mv_polynomial.C (polynomial.C (h_comp_crs_v_stmt i))

-- Coefficients of the CRS elements in the representation of the α_v_wit proof element in the AGM
parameters {α_v_wit_comp_crs_powers : fin d → F}
parameters {α_v_wit_comp_crs_α_powers : fin d → F}
parameters {α_v_wit_comp_crs_v_wit_k : fin n_wit →  F}
parameters {α_v_wit_comp_crs_w_k : fin m → F}
parameters {α_v_wit_comp_crs_α_v_wit_k : fin n_wit →  F}
parameters {α_v_wit_comp_crs_α_w_k : fin m →  F}
parameters {α_v_wit_comp_crs_β_v_wit_k : fin n_wit →  F}
parameters {α_v_wit_comp_crs_β_w_k : fin m →  F}

parameters {α_v_wit_comp_crs_α : F}
parameters {α_v_wit_comp_crs_γ : F}
parameters {α_v_wit_comp_crs_β_v_γ : F}
parameters {α_v_wit_comp_crs_β_w_γ : F}
parameters {α_v_wit_comp_crs_v_0 : F}
parameters {α_v_wit_comp_crs_w_0 : F}

parameters {α_v_wit_comp_crs_v_stmt : fin n_stmt → F}

parameters {α_v_wit_comp_crs_t : F}

/-- Polynomial form of the representation of the v_wit proof element in the AGM -/
def proof_α_v_wit : mv_polynomial vars (polynomial F) :=
  ∑ i in (finset.fin_range d), (crs_powers i) * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_powers i))
  +
  ∑ i in (finset.fin_range d), (crs_α_powers i) * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_α_powers i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_v_wit_k i) * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_w_k i) * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_α_v_wit_k i) * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_α_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_α_w_k i) * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_α_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_β_v_wit_k i) * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_β_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_β_w_k i) * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_β_w_k i))
  +
  crs_α * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_α))
  +
  crs_γ * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_γ))
  +
  crs_β_v_γ * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_β_v_γ))
  +
  crs_β_w_γ * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_β_w_γ))
  +
  crs_t * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_t))
  +
  crs_v_0 * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_v_0))
  +
  crs_w_0 * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_w_0))
  +
  ∑ i in (finset.fin_range n_stmt), (crs_v_stmt i) * mv_polynomial.C (polynomial.C (α_v_wit_comp_crs_v_stmt i))

-- Coefficients of the CRS elements in the representation of the α_w proof element in the AGM
parameters {α_w_comp_crs_powers : fin d → F}
parameters {α_w_comp_crs_α_powers : fin d → F}
parameters {α_w_comp_crs_v_wit_k : fin n_wit →  F}
parameters {α_w_comp_crs_w_k : fin m → F}
parameters {α_w_comp_crs_α_v_wit_k : fin n_wit →  F}
parameters {α_w_comp_crs_α_w_k : fin m →  F}
parameters {α_w_comp_crs_β_v_wit_k : fin n_wit →  F}
parameters {α_w_comp_crs_β_w_k : fin m →  F}

parameters {α_w_comp_crs_α : F}
parameters {α_w_comp_crs_γ : F}
parameters {α_w_comp_crs_β_v_γ : F}
parameters {α_w_comp_crs_β_w_γ : F}
parameters {α_w_comp_crs_v_0 : F}
parameters {α_w_comp_crs_w_0 : F}

parameters {α_w_comp_crs_v_stmt : fin n_stmt → F}

parameters {α_w_comp_crs_t : F}

/-- Polynomial form of the representation of the v_wit proof element in the AGM -/
def proof_α_w : mv_polynomial vars (polynomial F) :=
  ∑ i in (finset.fin_range d), (crs_powers i) * mv_polynomial.C (polynomial.C (α_w_comp_crs_powers i))
  +
  ∑ i in (finset.fin_range d), (crs_α_powers i) * mv_polynomial.C (polynomial.C (α_w_comp_crs_α_powers i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_v_wit_k i) * mv_polynomial.C (polynomial.C (α_w_comp_crs_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_w_k i) * mv_polynomial.C (polynomial.C (α_w_comp_crs_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_α_v_wit_k i) * mv_polynomial.C (polynomial.C (α_w_comp_crs_α_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_α_w_k i) * mv_polynomial.C (polynomial.C (α_w_comp_crs_α_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_β_v_wit_k i) * mv_polynomial.C (polynomial.C (α_w_comp_crs_β_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_β_w_k i) * mv_polynomial.C (polynomial.C (α_w_comp_crs_β_w_k i))
  +
  crs_α * mv_polynomial.C (polynomial.C (α_w_comp_crs_α))
  +
  crs_γ * mv_polynomial.C (polynomial.C (α_w_comp_crs_γ))
  +
  crs_β_v_γ * mv_polynomial.C (polynomial.C (α_w_comp_crs_β_v_γ))
  +
  crs_β_w_γ * mv_polynomial.C (polynomial.C (α_w_comp_crs_β_w_γ))
  +
  crs_t * mv_polynomial.C (polynomial.C (α_w_comp_crs_t))
  +
  crs_v_0 * mv_polynomial.C (polynomial.C (α_w_comp_crs_v_0))
  +
  crs_w_0 * mv_polynomial.C (polynomial.C (α_w_comp_crs_w_0))
  +
  ∑ i in (finset.fin_range n_stmt), (crs_v_stmt i) * mv_polynomial.C (polynomial.C (α_w_comp_crs_v_stmt i))

-- Coefficients of the CRS elements in the representation of the α_h proof element in the AGM
parameters {α_h_comp_crs_powers : fin d → F}
parameters {α_h_comp_crs_α_powers : fin d → F}
parameters {α_h_comp_crs_v_wit_k : fin n_wit →  F}
parameters {α_h_comp_crs_w_k : fin m → F}
parameters {α_h_comp_crs_α_v_wit_k : fin n_wit →  F}
parameters {α_h_comp_crs_α_w_k : fin m →  F}
parameters {α_h_comp_crs_β_v_wit_k : fin n_wit →  F}
parameters {α_h_comp_crs_β_w_k : fin m →  F}

parameters {α_h_comp_crs_α : F}
parameters {α_h_comp_crs_γ : F}
parameters {α_h_comp_crs_β_v_γ : F}
parameters {α_h_comp_crs_β_w_γ : F}
parameters {α_h_comp_crs_v_0 : F}
parameters {α_h_comp_crs_w_0 : F}

parameters {α_h_comp_crs_v_stmt : fin n_stmt → F}

parameters {α_h_comp_crs_t : F}

/-- Polynomial form of the representation of the α_h proof element in the AGM -/
def proof_α_h : mv_polynomial vars (polynomial F) :=
  ∑ i in (finset.fin_range d), (crs_powers i) * mv_polynomial.C (polynomial.C (α_h_comp_crs_powers i))
  +
  ∑ i in (finset.fin_range d), (crs_α_powers i) * mv_polynomial.C (polynomial.C (α_h_comp_crs_α_powers i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_v_wit_k i) * mv_polynomial.C (polynomial.C (α_h_comp_crs_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_w_k i) * mv_polynomial.C (polynomial.C (α_h_comp_crs_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_α_v_wit_k i) * mv_polynomial.C (polynomial.C (α_h_comp_crs_α_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_α_w_k i) * mv_polynomial.C (polynomial.C (α_h_comp_crs_α_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_β_v_wit_k i) * mv_polynomial.C (polynomial.C (α_h_comp_crs_β_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_β_w_k i) * mv_polynomial.C (polynomial.C (α_h_comp_crs_β_w_k i))
  +
  crs_α * mv_polynomial.C (polynomial.C (α_h_comp_crs_α))
  +
  crs_γ * mv_polynomial.C (polynomial.C (α_h_comp_crs_γ))
  +
  crs_β_v_γ * mv_polynomial.C (polynomial.C (α_h_comp_crs_β_v_γ))
  +
  crs_β_w_γ * mv_polynomial.C (polynomial.C (α_h_comp_crs_β_w_γ))
  +
  crs_t * mv_polynomial.C (polynomial.C (α_h_comp_crs_t))
  +
  crs_v_0 * mv_polynomial.C (polynomial.C (α_h_comp_crs_v_0))
  +
  crs_w_0 * mv_polynomial.C (polynomial.C (α_h_comp_crs_w_0))
  +
  ∑ i in (finset.fin_range n_stmt), (crs_v_stmt i) * mv_polynomial.C (polynomial.C (α_h_comp_crs_v_stmt i))

  -- Coefficients of the CRS elements in the representation of the y proof element in the AGM
parameters {y_comp_crs_powers : fin d → F}
parameters {y_comp_crs_α_powers : fin d → F}
parameters {y_comp_crs_v_wit_k : fin n_wit →  F}
parameters {y_comp_crs_w_k : fin m → F}
parameters {y_comp_crs_α_v_wit_k : fin n_wit →  F}
parameters {y_comp_crs_α_w_k : fin m →  F}
parameters {y_comp_crs_β_v_wit_k : fin n_wit →  F}
parameters {y_comp_crs_β_w_k : fin m →  F}

parameters {y_comp_crs_α : F}
parameters {y_comp_crs_γ : F}
parameters {y_comp_crs_β_v_γ : F}
parameters {y_comp_crs_β_w_γ : F}
parameters {y_comp_crs_v_0 : F}
parameters {y_comp_crs_w_0 : F}

parameters {y_comp_crs_v_stmt : fin n_stmt → F}

parameters {y_comp_crs_t : F}

/-- Polynomial form of the representation of the y proof element in the AGM -/
def proof_y : mv_polynomial vars (polynomial F) :=
  ∑ i in (finset.fin_range d), (crs_powers i) * mv_polynomial.C (polynomial.C (y_comp_crs_powers i))
  +
  ∑ i in (finset.fin_range d), (crs_α_powers i) * mv_polynomial.C (polynomial.C (y_comp_crs_α_powers i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_v_wit_k i) * mv_polynomial.C (polynomial.C (y_comp_crs_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_w_k i) * mv_polynomial.C (polynomial.C (y_comp_crs_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_α_v_wit_k i) * mv_polynomial.C (polynomial.C (y_comp_crs_α_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_α_w_k i) * mv_polynomial.C (polynomial.C (y_comp_crs_α_w_k i))
  +
  ∑ i in (finset.fin_range n_wit), (crs_β_v_wit_k i) * mv_polynomial.C (polynomial.C (y_comp_crs_β_v_wit_k i))
  +
  ∑ i in (finset.fin_range m), (crs_β_w_k i) * mv_polynomial.C (polynomial.C (y_comp_crs_β_w_k i))
  +
  crs_α * mv_polynomial.C (polynomial.C (y_comp_crs_α))
  +
  crs_γ * mv_polynomial.C (polynomial.C (y_comp_crs_γ))
  +
  crs_β_v_γ * mv_polynomial.C (polynomial.C (y_comp_crs_β_v_γ))
  +
  crs_β_w_γ * mv_polynomial.C (polynomial.C (y_comp_crs_β_w_γ))
  +
  crs_t * mv_polynomial.C (polynomial.C (y_comp_crs_t))
  +
  crs_v_0 * mv_polynomial.C (polynomial.C (y_comp_crs_v_0))
  +
  crs_w_0 * mv_polynomial.C (polynomial.C (y_comp_crs_w_0))
  +
  ∑ i in (finset.fin_range n_stmt), (crs_v_stmt i) * mv_polynomial.C (polynomial.C (y_comp_crs_v_stmt i))

-- Single variable form of V_wit
def v_wit_sv (a_wit : fin n_wit → F) : polynomial F
:= ∑ i in finset.fin_range n_wit, a_wit i • v_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) : polynomial F
:= ∑ i in (finset.fin_range n_stmt), a_stmt i • v_stmt i

@[poly] def v_stmt_mv (a_stmt : fin n_stmt → F) : mv_polynomial vars (polynomial F)
:= mv_polynomial.C (v_stmt_sv a_stmt)

/-- The statement polynomial that the verifier computes from the statement bits, as a single variable polynomial -/
def w_sv (b : fin m → F) : polynomial F
:= ∑ i in (finset.fin_range m), b i • w i

@[poly] def w_mv (a : fin m → F) : mv_polynomial vars (polynomial F)
:= mv_polynomial.C (w_sv a)

/-- Multivariable version of t -/
@[poly] def t_mv : mv_polynomial vars (polynomial F) := mv_polynomial.C t

-- Equations

def eqnI (c_stmt : fin n_stmt → F ) : Prop :=
  (crs_v_0 + v_stmt_mv c_stmt + proof_v_wit) * (crs_w_0 + proof_w) = (t_mv * proof_h)

def eqnII : Prop :=
  proof_α_v_wit = proof_v_wit * crs_α

def eqnIII : Prop :=
  proof_α_w = proof_w * crs_α

def eqnIV : Prop :=
  proof_α_h = proof_h * crs_α

def eqnV : Prop :=
  proof_y * crs_γ = proof_v_wit * crs_β_v_γ + proof_w * crs_β_w_γ

-- Coefficients
open mv_polynomial finsupp

-- lemma sadojfh (a b c : mv_polynomial vars (polynomial F)) : a + b - c = a -c + b :=
-- begin
--   ring,
-- end
-- example (a b c d e f g h i : F) : a + d + b + e + c - (a + b) = 0 :=
-- begin
--   abel,
-- end

lemma move_C_left (p : polynomial F) (f : F) :
  p * polynomial.C f = polynomial.C f * p :=
begin
  ring,
end

-- set_option profiler true

lemma eqnIIresults (eqnII_verified : eqnII) :
  proof_v_wit
  = mv_polynomial.C (
        ∑ i in (finset.fin_range n_wit),  (v_wit i) *  (polynomial.C (v_wit_comp_crs_v_wit_k i))
        + v_0 * (polynomial.C v_wit_comp_crs_v_0)
        + ∑ i in finset.fin_range n_stmt, (v_stmt i) * (polynomial.C (v_wit_comp_crs_v_stmt i))
        + ∑ x in finset.fin_range d, (polynomial.X ^ (x : ℕ)) * (polynomial.C (v_wit_comp_crs_powers x))
        + polynomial.C v_wit_comp_crs_w_0 * w_0
        + polynomial.C v_wit_comp_crs_t * t
        + ∑ (x : fin m) in finset.fin_range m, polynomial.C (v_wit_comp_crs_w_k x) * w x
    )
    :=
begin

  rw eqnII at eqnII_verified,

  simp_rw [proof_v_wit, proof_α_v_wit] at eqnII_verified ⊢,
  -- simp_rw [proof_v_wit, proof_w_wit, proof_y_wit, proof_h,
  --       proof_v_wit', proof_w_wit', proof_y_wit', proof_Z] at eqnII_verified,
  simp only [] with poly at eqnII_verified ⊢,
  -- simp only [] with polynomial_nf_3 at eqn',
  simp only [mv_polynomial.X, C_apply, mv_polynomial.monomial_mul, one_mul, mul_one, add_zero, zero_add, finset.sum_add_distrib, finset.sum_hom, mul_add, add_mul, sum_monomial_hom] at eqnII_verified ⊢,

  have h11eqnII := congr_arg (coeff (single vars.α 1 + single vars.β_v 1 + single vars.γ 1)) eqnII_verified,
  have h12eqnII := congr_arg (coeff (single vars.α 1 + single vars.β_w 1 + single vars.γ 1)) eqnII_verified,
  have h19eqnII := congr_arg (coeff (single vars.α 1 + single vars.γ 1)) eqnII_verified,
  have h21eqnII := congr_arg (coeff (single vars.α 1 + single vars.β_v 1)) eqnII_verified,
  have h22eqnII := congr_arg (coeff (single vars.α 2)) eqnII_verified,
  have h71eqnII := congr_arg (coeff (single vars.α 1)) eqnII_verified,
  have h74eqnII := congr_arg (coeff (single vars.α 1 + single vars.β_w 1)) eqnII_verified,

  clear eqnII_verified,

  simp only [finsupp_vars_eq_ext] with coeff_simp finsupp_eq at *,
  simp only [] with finsupp_simp at *,

  simp only [h11eqnII, h12eqnII, h19eqnII, h21eqnII, h22eqnII,  h71eqnII, h74eqnII],
  simp only [monomial_zero, add_zero, zero_add, <-monomial_add],

  rw <-sub_eq_iff_eq_add',
  ring_nf,

  rw <-sub_eq_zero,
  simp_rw [move_C_left],
  ring_nf,

  have h22eqnII' := congr_arg (monomial (single vars.α 1)) h22eqnII,
  simp only [monomial_zero, add_zero, zero_add, <-monomial_add] at h22eqnII',

  rw <-h22eqnII',
  rw <-sub_eq_iff_eq_add,
  -- simp_rw [add_assoc],
  abel,
  rw <-sub_eq_iff_eq_add',
  simp_rw [move_C_left],

  -- simp_rw [add_assoc],
  abel,

end

lemma eqnIIIresults (eqnIII_verified : eqnIII) :
  proof_w
  = mv_polynomial.C (
        ∑ i in (finset.fin_range n_wit),  (v_wit i) *  (polynomial.C (w_comp_crs_v_wit_k i))
        + v_0 * (polynomial.C w_comp_crs_v_0)
        + ∑ i in finset.fin_range n_stmt, (v_stmt i) * (polynomial.C (w_comp_crs_v_stmt i))
        + ∑ x in finset.fin_range d, (polynomial.X ^ (x : ℕ)) * ( polynomial.C (w_comp_crs_powers x))
        + polynomial.C w_comp_crs_w_0 * w_0
        + polynomial.C w_comp_crs_t * t
        + ∑ (x : fin m) in finset.fin_range m, polynomial.C (w_comp_crs_w_k x) * w x
    ) :=
begin

  rw eqnIII at eqnIII_verified,

  simp_rw [proof_w, proof_α_w] at eqnIII_verified ⊢,
  -- simp_rw [proof_v_wit, proof_w_wit, proof_y_wit, proof_h,
  --       proof_v_wit', proof_w_wit', proof_y_wit', proof_Z] at eqnIII_verified,
  simp only [] with poly at eqnIII_verified ⊢,
  -- simp only [] with polynomial_nf_3 at eqn',
  simp only [mv_polynomial.X, C_apply, mv_polynomial.monomial_mul, one_mul, mul_one, add_zero, zero_add, finset.sum_add_distrib, finset.sum_hom, mul_add, add_mul, sum_monomial_hom] at eqnIII_verified ⊢,

  have h11eqnIII := congr_arg (coeff (single vars.α 1 + single vars.β_v 1 + single vars.γ 1)) eqnIII_verified,
  have h12eqnIII := congr_arg (coeff (single vars.α 1 + single vars.β_w 1 + single vars.γ 1)) eqnIII_verified,
  have h19eqnIII := congr_arg (coeff (single vars.α 1 + single vars.γ 1)) eqnIII_verified,
  have h21eqnIII := congr_arg (coeff (single vars.α 1 + single vars.β_v 1)) eqnIII_verified,
  have h22eqnIII := congr_arg (coeff (single vars.α 2)) eqnIII_verified,
  have h71eqnIII := congr_arg (coeff (single vars.α 1)) eqnIII_verified,
  have h74eqnIII := congr_arg (coeff (single vars.α 1 + single vars.β_w 1)) eqnIII_verified,

  clear eqnIII_verified,

  simp only [finsupp_vars_eq_ext] with coeff_simp finsupp_eq at *,
  simp only [] with finsupp_simp at *,

  simp only [h11eqnIII, h12eqnIII, h19eqnIII, h21eqnIII, h22eqnIII, h71eqnIII, h74eqnIII],
  simp only [monomial_zero, add_zero, zero_add, <-monomial_add],

  rw <-sub_eq_iff_eq_add',
  ring_nf,

  rw <-sub_eq_zero,
  simp_rw [move_C_left],
  ring_nf,

  have h22eqnIII' := congr_arg (monomial (single vars.α 1)) h22eqnIII,
  simp only [monomial_zero, add_zero, zero_add, <-monomial_add] at h22eqnIII',

  rw <-h22eqnIII',
  rw <-sub_eq_iff_eq_add,
  -- simp_rw [add_assoc],
  abel,
  rw <-sub_eq_iff_eq_add',
  simp_rw [move_C_left],

  -- simp_rw [add_assoc],
  abel,
end

-- lemma eqnIVresults (eqnIV_verified : eqnIV) :
--   proof_h
--   = mv_polynomial.C (
--         ∑ i in (finset.fin_range n_wit),  (v_wit i) *  (polynomial.C (h_comp_crs_v_wit_k i))
--         + v_0 * (polynomial.C h_comp_crs_v_0)
--         + ∑ i in finset.fin_range n_stmt, (v_stmt i) * (polynomial.C (h_comp_crs_v_stmt i))
--         + ∑ x in finset.fin_range d, (polynomial.X ^ (x : ℕ)) * ( polynomial.C (h_comp_crs_powers x))
--         + polynomial.C h_comp_crs_w_0 * w_0
--         + polynomial.C h_comp_crs_t * t
--         + ∑ (x : fin m) in finset.fin_range m, polynomial.C (h_comp_crs_w_k x) * w x
--     )  :=
-- begin
--   rw eqnIV at eqnIV_verified,

--   simp_rw [proof_h, proof_α_h] at eqnIV_verified ⊢,
--   -- simp_rw [proof_v_wit, proof_w_wit, proof_y_wit, proof_h,
--   --       proof_v_wit', proof_w_wit', proof_y_wit', proof_Z] at eqnIV_verified,
--   simp only [] with poly at eqnIV_verified ⊢,
--   -- simp only [] with polynomial_nf_3 at eqn',
--   simp only [mv_polynomial.X, C_apply, mv_polynomial.monomial_mul, one_mul, mul_one, add_zero, zero_add, finset.sum_add_distrib, finset.sum_hom, mul_add, add_mul, sum_monomial_hom] at eqnIV_verified ⊢,

--   have h11eqnIV := congr_arg (coeff (single vars.α 1 + single vars.β_v 1 + single vars.γ 1)) eqnIV_verified,
--   have h12eqnIV := congr_arg (coeff (single vars.α 1 + single vars.β_w 1 + single vars.γ 1)) eqnIV_verified,
--   have h19eqnIV := congr_arg (coeff (single vars.α 1 + single vars.γ 1)) eqnIV_verified,
--   have h21eqnIV := congr_arg (coeff (single vars.α 1 + single vars.β_v 1)) eqnIV_verified,
--   have h22eqnIV := congr_arg (coeff (single vars.α 2)) eqnIV_verified,
--   have h71eqnIV := congr_arg (coeff (single vars.α 1)) eqnIV_verified,
--   have h74eqnIV := congr_arg (coeff (single vars.α 1 + single vars.β_w 1)) eqnIV_verified,

--   clear eqnIV_verified,

--   simp only [finsupp_vars_eq_ext] with coeff_simp finsupp_eq at *,
--   simp only [] with finsupp_simp at *,

--   simp only [h11eqnIV, h12eqnIV, h19eqnIV, h21eqnIV, h22eqnIV, h71eqnIV, h74eqnIV],
--   simp only [monomial_zero, add_zero, zero_add, <-monomial_add],

--   rw <-sub_eq_iff_eq_add',
--   ring_nf,

--   rw <-sub_eq_zero,
--   simp_rw [move_C_left],
--   ring_nf,

--   have h22eqnIV' := congr_arg (monomial (single vars.α 1)) h22eqnIV,
--   simp only [monomial_zero, add_zero, zero_add, <-monomial_add] at h22eqnIV',

--   rw <-h22eqnIV',
--   rw <-sub_eq_iff_eq_add,
--   -- simp_rw [add_assoc],
--   abel,
--   rw <-sub_eq_iff_eq_add',
--   simp_rw [move_C_left],

--   -- simp_rw [add_assoc],
--   abel,
-- end

lemma polynomial.mul_mod_by_monic (t p : polynomial F) (mt : t.monic) : (t * p) %ₘ t = 0 :=
begin
  rw polynomial.dvd_iff_mod_by_monic_eq_zero,
  apply dvd_mul_right,
  exact mt,
end

lemma soundness (c_stmt : fin n_stmt → F ) (eqnI_verified : eqnI c_stmt)
  (eqnII_verified : eqnII) (eqnIII_verified : eqnIII) -- (eqnIV_verified : eqnIV)
  (eqnV_verified : eqnV) : (satisfying c_stmt y_comp_crs_β_v_wit_k y_comp_crs_β_w_k) :=
begin
  rw eqnV at eqnV_verified,

  have eqnII' := eqnIIresults eqnII_verified,
  have eqnIII' := eqnIIIresults eqnIII_verified,
  -- have eqnIV' := eqnIVresults eqnIV_verified,

  simp_rw [eqnII', eqnIII', proof_y] at eqnV_verified,
  simp only [] with poly at eqnV_verified,
  -- simp only [] with polynomial_nf_3 at eqn',
  simp only [mv_polynomial.X, C_apply, mv_polynomial.monomial_mul, one_mul, mul_one, add_zero, zero_add, finset.sum_add_distrib, finset.sum_hom, mul_add, add_mul, sum_monomial_hom] at eqnV_verified,

  have h2eqnV := congr_arg (coeff (single vars.β_v 1 + single vars.γ 1)) eqnV_verified,
  have h3eqnV := congr_arg (coeff (single vars.β_w 1 + single vars.γ 1)) eqnV_verified,
  -- have h4eqnV := congr_arg (coeff (single vars.r_v 1 + single vars.r_w 1 + single vars.β 1 + single vars.γ 1)) eqnV_verified,

  clear eqnV_verified,

  simp only [finsupp_vars_eq_ext] with coeff_simp finsupp_eq at *,
  simp only [] with finsupp_simp at *,

  rw [<-h2eqnV] at eqnII',
  rw [<-h3eqnV] at eqnIII',
  -- rw [<-h4eqnV] at eqnIV',

  rw [eqnI] at eqnI_verified,
  rw satisfying,

  simp_rw [eqnII', eqnIII', proof_h] at eqnI_verified,

  simp only [] with poly at eqnI_verified,
  -- simp only [] with polynomial_nf_3 at eqn',
  simp only [mv_polynomial.X, C_apply, mv_polynomial.monomial_mul, one_mul, mul_one, add_zero, zero_add, finset.sum_add_distrib, finset.sum_hom, mul_add, add_mul, sum_monomial_hom] at eqnI_verified,

  have h1eqnI := congr_arg (coeff 0) eqnI_verified,

  clear eqnI_verified,

  simp only [finsupp_vars_eq_ext] with coeff_simp finsupp_eq at h1eqnI,
  simp only [] with finsupp_simp at h1eqnI,
  -- rw <-sub_eq_iff_eq_add at h1eqnI,
  simp_rw [<-mul_add t] at h1eqnI,
  -- rw <-sub_eq_zero at h1eqnI,
  -- rw polynomial.dvd_iff_mod_by_monic_eq_zero,

  have h2eqnI := congr_arg (%ₘ t) h1eqnI,
  simp only [polynomial.zero_mod_by_monic] at h2eqnI,
  rw polynomial.mul_mod_by_monic at h2eqnI,
  rw <- h2eqnI,
  congr' 1,
  simp_rw [v_stmt_sv, polynomial.smul_eq_C_mul],
  simp only [add_mul, mul_add],
  simp_rw [move_C_left],
  -- congr' 1,
  -- rw <-sub_eq_zero,

  abel,

  exact monic_t,

end

end