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

! This file was ported from Lean 3 source module linear_algebra.smodeq
! 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.Data.Polynomial.Eval
import Mathbin.RingTheory.Ideal.Quotient

/-!
# modular equivalence for submodule

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

open Submodule

open Polynomial

variable {R : Type _} [Ring R]

variable {M : Type _} [AddCommGroup M] [Module R M] (U U₁ U₂ : Submodule R M)

variable {x x₁ x₂ y y₁ y₂ z z₁ z₂ : M}

variable {N : Type _} [AddCommGroup N] [Module R N] (V V₁ V₂ : Submodule R N)

#print SModEq /-
/-- A predicate saying two elements of a module are equivalent modulo a submodule. -/
def SModEq (x y : M) : Prop :=
  (Submodule.Quotient.mk x : M ⧸ U) = Submodule.Quotient.mk y
#align smodeq SModEq
-/

-- mathport name: «expr ≡ [SMOD ]»
notation:50 x " ≡ " y " [SMOD " N "]" => SModEq N x y

variable {U U₁ U₂}

/- warning: smodeq.def -> SModEq.def is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M} {y : M}, Iff (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x y) (Eq.{succ u2} (HasQuotient.Quotient.{u2, u2} M (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (Submodule.hasQuotient.{u1, u2} R M _inst_1 _inst_2 _inst_3) U) (Submodule.Quotient.mk.{u1, u2} R M _inst_1 _inst_2 _inst_3 U x) (Submodule.Quotient.mk.{u1, u2} R M _inst_1 _inst_2 _inst_3 U y))
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {U : Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3} {x : M} {y : M}, Iff (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x y) (Eq.{succ u1} (HasQuotient.Quotient.{u1, u1} M (Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3) (Submodule.hasQuotient.{u2, u1} R M _inst_1 _inst_2 _inst_3) U) (Submodule.Quotient.mk.{u2, u1} R M _inst_1 _inst_2 _inst_3 U x) (Submodule.Quotient.mk.{u2, u1} R M _inst_1 _inst_2 _inst_3 U y))
Case conversion may be inaccurate. Consider using '#align smodeq.def SModEq.defₓ'. -/
protected theorem SModEq.def :
    x ≡ y [SMOD U] ↔ (Submodule.Quotient.mk x : M ⧸ U) = Submodule.Quotient.mk y :=
  Iff.rfl
#align smodeq.def SModEq.def

namespace SModEq

/- warning: smodeq.sub_mem -> SModEq.sub_mem is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M} {y : M}, Iff (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x y) (Membership.Mem.{u2, u2} M (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (SetLike.hasMem.{u2, u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) M (Submodule.setLike.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3)) (HSub.hSub.{u2, u2, u2} M M M (instHSub.{u2} M (SubNegMonoid.toHasSub.{u2} M (AddGroup.toSubNegMonoid.{u2} M (AddCommGroup.toAddGroup.{u2} M _inst_2)))) x y) U)
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {U : Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3} {x : M} {y : M}, Iff (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x y) (Membership.mem.{u1, u1} M (Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3) (SetLike.instMembership.{u1, u1} (Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3) M (Submodule.setLike.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3)) (HSub.hSub.{u1, u1, u1} M M M (instHSub.{u1} M (SubNegMonoid.toSub.{u1} M (AddGroup.toSubNegMonoid.{u1} M (AddCommGroup.toAddGroup.{u1} M _inst_2)))) x y) U)
Case conversion may be inaccurate. Consider using '#align smodeq.sub_mem SModEq.sub_memₓ'. -/
theorem sub_mem : x ≡ y [SMOD U] ↔ x - y ∈ U := by rw [SModEq.def, Submodule.Quotient.eq]
#align smodeq.sub_mem SModEq.sub_mem

/- warning: smodeq.top -> SModEq.top is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {x : M} {y : M}, SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 (Top.top.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (Submodule.hasTop.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3)) x y
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {x : M} {y : M}, SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 (Top.top.{u1} (Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3) (Submodule.instTopSubmodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3)) x y
Case conversion may be inaccurate. Consider using '#align smodeq.top SModEq.topₓ'. -/
@[simp]
theorem top : x ≡ y [SMOD (⊤ : Submodule R M)] :=
  (Submodule.Quotient.eq ⊤).2 mem_top
#align smodeq.top SModEq.top

/- warning: smodeq.bot -> SModEq.bot is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {x : M} {y : M}, Iff (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 (Bot.bot.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (Submodule.hasBot.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3)) x y) (Eq.{succ u2} M x y)
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {x : M} {y : M}, Iff (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 (Bot.bot.{u1} (Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3) (Submodule.instBotSubmodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3)) x y) (Eq.{succ u1} M x y)
Case conversion may be inaccurate. Consider using '#align smodeq.bot SModEq.botₓ'. -/
@[simp]
theorem bot : x ≡ y [SMOD (⊥ : Submodule R M)] ↔ x = y := by
  rw [SModEq.def, Submodule.Quotient.eq, mem_bot, sub_eq_zero]
#align smodeq.bot SModEq.bot

/- warning: smodeq.mono -> SModEq.mono is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U₁ : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {U₂ : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M} {y : M}, (LE.le.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (Preorder.toLE.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (PartialOrder.toPreorder.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (SetLike.partialOrder.{u2, u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) M (Submodule.setLike.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3)))) U₁ U₂) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U₁ x y) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U₂ x y)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U₁ : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {U₂ : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M} {y : M}, (LE.le.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (Preorder.toLE.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (PartialOrder.toPreorder.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (OmegaCompletePartialOrder.toPartialOrder.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (CompleteLattice.instOmegaCompletePartialOrder.{u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (Submodule.completeLattice.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3))))) U₁ U₂) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U₁ x y) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U₂ x y)
Case conversion may be inaccurate. Consider using '#align smodeq.mono SModEq.monoₓ'. -/
@[mono]
theorem mono (HU : U₁ ≤ U₂) (hxy : x ≡ y [SMOD U₁]) : x ≡ y [SMOD U₂] :=
  (Submodule.Quotient.eq U₂).2 <| HU <| (Submodule.Quotient.eq U₁).1 hxy
#align smodeq.mono SModEq.mono

/- warning: smodeq.refl -> SModEq.refl is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} (x : M), SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x x
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {U : Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3} (x : M), SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x x
Case conversion may be inaccurate. Consider using '#align smodeq.refl SModEq.reflₓ'. -/
@[refl]
protected theorem refl (x : M) : x ≡ x [SMOD U] :=
  @rfl _ _
#align smodeq.refl SModEq.refl

/- warning: smodeq.rfl -> SModEq.rfl is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M}, SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x x
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {U : Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3} {x : M}, SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x x
Case conversion may be inaccurate. Consider using '#align smodeq.rfl SModEq.rflₓ'. -/
protected theorem rfl : x ≡ x [SMOD U] :=
  SModEq.refl _
#align smodeq.rfl SModEq.rfl

instance : IsRefl _ (SModEq U) :=
  ⟨SModEq.refl⟩

/- warning: smodeq.symm -> SModEq.symm is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M} {y : M}, (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x y) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U y x)
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {U : Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3} {x : M} {y : M}, (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x y) -> (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U y x)
Case conversion may be inaccurate. Consider using '#align smodeq.symm SModEq.symmₓ'. -/
@[symm]
theorem symm (hxy : x ≡ y [SMOD U]) : y ≡ x [SMOD U] :=
  hxy.symm
#align smodeq.symm SModEq.symm

/- warning: smodeq.trans -> SModEq.trans is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M} {y : M} {z : M}, (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x y) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U y z) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x z)
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {U : Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3} {x : M} {y : M} {z : M}, (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x y) -> (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U y z) -> (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x z)
Case conversion may be inaccurate. Consider using '#align smodeq.trans SModEq.transₓ'. -/
@[trans]
theorem trans (hxy : x ≡ y [SMOD U]) (hyz : y ≡ z [SMOD U]) : x ≡ z [SMOD U] :=
  hxy.trans hyz
#align smodeq.trans SModEq.trans

/- warning: smodeq.add -> SModEq.add is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x₁ : M} {x₂ : M} {y₁ : M} {y₂ : M}, (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x₁ y₁) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x₂ y₂) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U (HAdd.hAdd.{u2, u2, u2} M M M (instHAdd.{u2} M (AddZeroClass.toHasAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (SubNegMonoid.toAddMonoid.{u2} M (AddGroup.toSubNegMonoid.{u2} M (AddCommGroup.toAddGroup.{u2} M _inst_2)))))) x₁ x₂) (HAdd.hAdd.{u2, u2, u2} M M M (instHAdd.{u2} M (AddZeroClass.toHasAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (SubNegMonoid.toAddMonoid.{u2} M (AddGroup.toSubNegMonoid.{u2} M (AddCommGroup.toAddGroup.{u2} M _inst_2)))))) y₁ y₂))
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {U : Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3} {x₁ : M} {x₂ : M} {y₁ : M} {y₂ : M}, (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x₁ y₁) -> (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x₂ y₂) -> (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U (HAdd.hAdd.{u1, u1, u1} M M M (instHAdd.{u1} M (AddZeroClass.toAdd.{u1} M (AddMonoid.toAddZeroClass.{u1} M (SubNegMonoid.toAddMonoid.{u1} M (AddGroup.toSubNegMonoid.{u1} M (AddCommGroup.toAddGroup.{u1} M _inst_2)))))) x₁ x₂) (HAdd.hAdd.{u1, u1, u1} M M M (instHAdd.{u1} M (AddZeroClass.toAdd.{u1} M (AddMonoid.toAddZeroClass.{u1} M (SubNegMonoid.toAddMonoid.{u1} M (AddGroup.toSubNegMonoid.{u1} M (AddCommGroup.toAddGroup.{u1} M _inst_2)))))) y₁ y₂))
Case conversion may be inaccurate. Consider using '#align smodeq.add SModEq.addₓ'. -/
theorem add (hxy₁ : x₁ ≡ y₁ [SMOD U]) (hxy₂ : x₂ ≡ y₂ [SMOD U]) : x₁ + x₂ ≡ y₁ + y₂ [SMOD U] :=
  by
  rw [SModEq.def] at hxy₁ hxy₂⊢
  simp_rw [quotient.mk_add, hxy₁, hxy₂]
#align smodeq.add SModEq.add

/- warning: smodeq.smul -> SModEq.smul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M} {y : M}, (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x y) -> (forall (c : R), SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U (SMul.smul.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)))) (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (Semiring.toMonoidWithZero.{u1} R (Ring.toSemiring.{u1} R _inst_1))))) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)))) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R (Ring.toSemiring.{u1} R _inst_1)) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)))) (Module.toMulActionWithZero.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3)))) c x) (SMul.smul.{u1, u2} R M (SMulZeroClass.toHasSmul.{u1, u2} R M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)))) (SMulWithZero.toSmulZeroClass.{u1, u2} R M (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (Semiring.toMonoidWithZero.{u1} R (Ring.toSemiring.{u1} R _inst_1))))) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)))) (MulActionWithZero.toSMulWithZero.{u1, u2} R M (Semiring.toMonoidWithZero.{u1} R (Ring.toSemiring.{u1} R _inst_1)) (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)))) (Module.toMulActionWithZero.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3)))) c y))
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {U : Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3} {x : M} {y : M}, (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x y) -> (forall (c : R), SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U (HSMul.hSMul.{u2, u1, u1} R M M (instHSMul.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M (NegZeroClass.toZero.{u1} M (SubNegZeroMonoid.toNegZeroClass.{u1} M (SubtractionMonoid.toSubNegZeroMonoid.{u1} M (SubtractionCommMonoid.toSubtractionMonoid.{u1} M (AddCommGroup.toDivisionAddCommMonoid.{u1} M _inst_2))))) (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R (Semiring.toMonoidWithZero.{u2} R (Ring.toSemiring.{u2} R _inst_1))) (NegZeroClass.toZero.{u1} M (SubNegZeroMonoid.toNegZeroClass.{u1} M (SubtractionMonoid.toSubNegZeroMonoid.{u1} M (SubtractionCommMonoid.toSubtractionMonoid.{u1} M (AddCommGroup.toDivisionAddCommMonoid.{u1} M _inst_2))))) (MulActionWithZero.toSMulWithZero.{u2, u1} R M (Semiring.toMonoidWithZero.{u2} R (Ring.toSemiring.{u2} R _inst_1)) (NegZeroClass.toZero.{u1} M (SubNegZeroMonoid.toNegZeroClass.{u1} M (SubtractionMonoid.toSubNegZeroMonoid.{u1} M (SubtractionCommMonoid.toSubtractionMonoid.{u1} M (AddCommGroup.toDivisionAddCommMonoid.{u1} M _inst_2))))) (Module.toMulActionWithZero.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3))))) c x) (HSMul.hSMul.{u2, u1, u1} R M M (instHSMul.{u2, u1} R M (SMulZeroClass.toSMul.{u2, u1} R M (NegZeroClass.toZero.{u1} M (SubNegZeroMonoid.toNegZeroClass.{u1} M (SubtractionMonoid.toSubNegZeroMonoid.{u1} M (SubtractionCommMonoid.toSubtractionMonoid.{u1} M (AddCommGroup.toDivisionAddCommMonoid.{u1} M _inst_2))))) (SMulWithZero.toSMulZeroClass.{u2, u1} R M (MonoidWithZero.toZero.{u2} R (Semiring.toMonoidWithZero.{u2} R (Ring.toSemiring.{u2} R _inst_1))) (NegZeroClass.toZero.{u1} M (SubNegZeroMonoid.toNegZeroClass.{u1} M (SubtractionMonoid.toSubNegZeroMonoid.{u1} M (SubtractionCommMonoid.toSubtractionMonoid.{u1} M (AddCommGroup.toDivisionAddCommMonoid.{u1} M _inst_2))))) (MulActionWithZero.toSMulWithZero.{u2, u1} R M (Semiring.toMonoidWithZero.{u2} R (Ring.toSemiring.{u2} R _inst_1)) (NegZeroClass.toZero.{u1} M (SubNegZeroMonoid.toNegZeroClass.{u1} M (SubtractionMonoid.toSubNegZeroMonoid.{u1} M (SubtractionCommMonoid.toSubtractionMonoid.{u1} M (AddCommGroup.toDivisionAddCommMonoid.{u1} M _inst_2))))) (Module.toMulActionWithZero.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3))))) c y))
Case conversion may be inaccurate. Consider using '#align smodeq.smul SModEq.smulₓ'. -/
theorem smul (hxy : x ≡ y [SMOD U]) (c : R) : c • x ≡ c • y [SMOD U] :=
  by
  rw [SModEq.def] at hxy⊢
  simp_rw [quotient.mk_smul, hxy]
#align smodeq.smul SModEq.smul

/- warning: smodeq.zero -> SModEq.zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M}, Iff (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x (OfNat.ofNat.{u2} M 0 (OfNat.mk.{u2} M 0 (Zero.zero.{u2} M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M (SubNegMonoid.toAddMonoid.{u2} M (AddGroup.toSubNegMonoid.{u2} M (AddCommGroup.toAddGroup.{u2} M _inst_2))))))))) (Membership.Mem.{u2, u2} M (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) (SetLike.hasMem.{u2, u2} (Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3) M (Submodule.setLike.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3)) x U)
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Ring.{u2} R] {M : Type.{u1}} [_inst_2 : AddCommGroup.{u1} M] [_inst_3 : Module.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2)] {U : Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3} {x : M}, Iff (SModEq.{u2, u1} R _inst_1 M _inst_2 _inst_3 U x (OfNat.ofNat.{u1} M 0 (Zero.toOfNat0.{u1} M (NegZeroClass.toZero.{u1} M (SubNegZeroMonoid.toNegZeroClass.{u1} M (SubtractionMonoid.toSubNegZeroMonoid.{u1} M (SubtractionCommMonoid.toSubtractionMonoid.{u1} M (AddCommGroup.toDivisionAddCommMonoid.{u1} M _inst_2)))))))) (Membership.mem.{u1, u1} M (Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3) (SetLike.instMembership.{u1, u1} (Submodule.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3) M (Submodule.setLike.{u2, u1} R M (Ring.toSemiring.{u2} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} M _inst_2) _inst_3)) x U)
Case conversion may be inaccurate. Consider using '#align smodeq.zero SModEq.zeroₓ'. -/
theorem zero : x ≡ 0 [SMOD U] ↔ x ∈ U := by rw [SModEq.def, Submodule.Quotient.eq, sub_zero]
#align smodeq.zero SModEq.zero

/- warning: smodeq.map -> SModEq.map is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M} {y : M} {N : Type.{u3}} [_inst_4 : AddCommGroup.{u3} N] [_inst_5 : Module.{u1, u3} R N (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4)], (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 U x y) -> (forall (f : LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5), SModEq.{u1, u3} R _inst_1 N _inst_4 _inst_5 (Submodule.map.{u1, u1, u2, u3, max u2 u3} R R M N (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) (RingHomSurjective.ids.{u1} R (Ring.toSemiring.{u1} R _inst_1)) (LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) (LinearMap.semilinearMapClass.{u1, u1, u2, u3} R R M N (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1)))) f U) (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) (fun (_x : LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) => M -> N) (LinearMap.hasCoeToFun.{u1, u1, u2, u3} R R M N (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1)))) f x) (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) (fun (_x : LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) => M -> N) (LinearMap.hasCoeToFun.{u1, u1, u2, u3} R R M N (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1)))) f y))
but is expected to have type
  forall {R : Type.{u3}} [_inst_1 : Ring.{u3} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u3, u2} R M (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {U : Submodule.{u3, u2} R M (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) _inst_3} {x : M} {y : M} {N : Type.{u1}} [_inst_4 : AddCommGroup.{u1} N] [_inst_5 : Module.{u3, u1} R N (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4)], (SModEq.{u3, u2} R _inst_1 M _inst_2 _inst_3 U x y) -> (forall (f : LinearMap.{u3, u3, u2, u1} R R (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5), SModEq.{u3, u1} R _inst_1 N _inst_4 _inst_5 (Submodule.map.{u3, u3, u2, u1, max u2 u1} R R M N (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) (RingHomSurjective.ids.{u3} R (Ring.toSemiring.{u3} R _inst_1)) (LinearMap.{u3, u3, u2, u1} R R (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5) (LinearMap.instSemilinearMapClassLinearMap.{u3, u3, u2, u1} R R M N (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1)))) f U) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (LinearMap.{u3, u3, u2, u1} R R (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Module.LinearMap._hyg.6190 : M) => N) _x) (LinearMap.instFunLikeLinearMap.{u3, u3, u2, u1} R R M N (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1)))) f x) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (LinearMap.{u3, u3, u2, u1} R R (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Module.LinearMap._hyg.6190 : M) => N) _x) (LinearMap.instFunLikeLinearMap.{u3, u3, u2, u1} R R M N (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1)))) f y))
Case conversion may be inaccurate. Consider using '#align smodeq.map SModEq.mapₓ'. -/
theorem map (hxy : x ≡ y [SMOD U]) (f : M →ₗ[R] N) : f x ≡ f y [SMOD U.map f] :=
  (Submodule.Quotient.eq _).2 <| f.map_sub x y ▸ mem_map_of_mem <| (Submodule.Quotient.eq _).1 hxy
#align smodeq.map SModEq.map

/- warning: smodeq.comap -> SModEq.comap is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Ring.{u1} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u1, u2} R M (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {x : M} {y : M} {N : Type.{u3}} [_inst_4 : AddCommGroup.{u3} N] [_inst_5 : Module.{u1, u3} R N (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4)] (V : Submodule.{u1, u3} R N (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_5) {f : LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5}, (SModEq.{u1, u3} R _inst_1 N _inst_4 _inst_5 V (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) (fun (_x : LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) => M -> N) (LinearMap.hasCoeToFun.{u1, u1, u2, u3} R R M N (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1)))) f x) (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) (fun (_x : LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) => M -> N) (LinearMap.hasCoeToFun.{u1, u1, u2, u3} R R M N (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1)))) f y)) -> (SModEq.{u1, u2} R _inst_1 M _inst_2 _inst_3 (Submodule.comap.{u1, u1, u2, u3, max u2 u3} R R M N (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) (LinearMap.{u1, u1, u2, u3} R R (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5) (LinearMap.semilinearMapClass.{u1, u1, u2, u3} R R M N (Ring.toSemiring.{u1} R _inst_1) (Ring.toSemiring.{u1} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u3} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R (Ring.toSemiring.{u1} R _inst_1)))) f V) x y)
but is expected to have type
  forall {R : Type.{u3}} [_inst_1 : Ring.{u3} R] {M : Type.{u2}} [_inst_2 : AddCommGroup.{u2} M] [_inst_3 : Module.{u3, u2} R M (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2)] {x : M} {y : M} {N : Type.{u1}} [_inst_4 : AddCommGroup.{u1} N] [_inst_5 : Module.{u3, u1} R N (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4)] (V : Submodule.{u3, u1} R N (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_5) {f : LinearMap.{u3, u3, u2, u1} R R (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5}, (SModEq.{u3, u1} R _inst_1 N _inst_4 _inst_5 V (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (LinearMap.{u3, u3, u2, u1} R R (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Module.LinearMap._hyg.6190 : M) => N) _x) (LinearMap.instFunLikeLinearMap.{u3, u3, u2, u1} R R M N (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1)))) f x) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (LinearMap.{u3, u3, u2, u1} R R (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Module.LinearMap._hyg.6190 : M) => N) _x) (LinearMap.instFunLikeLinearMap.{u3, u3, u2, u1} R R M N (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1)))) f y)) -> (SModEq.{u3, u2} R _inst_1 M _inst_2 _inst_3 (Submodule.comap.{u3, u3, u2, u1, max u2 u1} R R M N (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) (LinearMap.{u3, u3, u2, u1} R R (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1))) M N (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5) (LinearMap.instSemilinearMapClassLinearMap.{u3, u3, u2, u1} R R M N (Ring.toSemiring.{u3} R _inst_1) (Ring.toSemiring.{u3} R _inst_1) (AddCommGroup.toAddCommMonoid.{u2} M _inst_2) (AddCommGroup.toAddCommMonoid.{u1} N _inst_4) _inst_3 _inst_5 (RingHom.id.{u3} R (NonAssocRing.toNonAssocSemiring.{u3} R (Ring.toNonAssocRing.{u3} R _inst_1)))) f V) x y)
Case conversion may be inaccurate. Consider using '#align smodeq.comap SModEq.comapₓ'. -/
theorem comap {f : M →ₗ[R] N} (hxy : f x ≡ f y [SMOD V]) : x ≡ y [SMOD V.comap f] :=
  (Submodule.Quotient.eq _).2 <|
    show f (x - y) ∈ V from (f.map_sub x y).symm ▸ (Submodule.Quotient.eq _).1 hxy
#align smodeq.comap SModEq.comap

#print SModEq.eval /-
theorem eval {R : Type _} [CommRing R] {I : Ideal R} {x y : R} (h : x ≡ y [SMOD I]) (f : R[X]) :
    f.eval x ≡ f.eval y [SMOD I] := by
  rw [SModEq.def] at h⊢
  show Ideal.Quotient.mk I (f.eval x) = Ideal.Quotient.mk I (f.eval y)
  change Ideal.Quotient.mk I x = Ideal.Quotient.mk I y at h
  rw [← Polynomial.eval₂_at_apply, ← Polynomial.eval₂_at_apply, h]
#align smodeq.eval SModEq.eval
-/

end SModEq


import main
import split_cycle
import algebra.linear_ordered_comm_group_with_zero
open_locale classical

variables {V X : Type}

def simple_lift : Prof V X → Prof V X → X → Prop :=
    λ P' P x, (∀ (a ≠ x) (b ≠ x) i, P i a b ↔ P' i a b)
    ∧ ∀ a i, ((P i x a → P' i x a) ∧ (P' i a x → P i a x))

def monotonicity (F : VSCC) (P P' : Prof V X) : Prop :=
    ∀ (x ∈ F V X P), simple_lift P' P x → x ∈ F V X P'

lemma cardinality_lemma [fintype V] (p q : V → Prop) : (∀ v, (p v → q v)) → ((finset.filter p finset.univ).card ≤ (finset.filter q finset.univ).card) :=
begin
    intro pq,
    have subset : (finset.filter p finset.univ) ⊆ (finset.filter q finset.univ),
    refine finset.subset_iff.mpr _,
    simp,
    exact pq,
    exact (finset.card_le_of_subset subset),
end

lemma cardinality_lemma2 [fintype V] (p q : V → Prop) : (∀ v, (p v ↔ q v)) → ((finset.filter p finset.univ).card = (finset.filter q finset.univ).card) :=
begin
    intro pq,
    congr,
    ext1, simp at *, fsplit, work_on_goal 0 { intros ᾰ }, work_on_goal 1 { intros ᾰ },
    specialize pq x,
    exact pq.mp ᾰ,
    specialize pq x,
    exact pq.mpr ᾰ,
end

lemma margin_lemma (P P' : Prof V X) [fintype V] (a b : X) : (a ≠ b) → (∀ (v : V), (P v a b → P' v a b) ∧ (P' v b a → P v b a)) → margin P a b ≤ margin P' a b :=
begin
    intro h,

    intro lift,
    unfold margin,
    have first : ((finset.filter (λ (x_1 : V), P x_1 a b) finset.univ).card) ≤ ((finset.filter (λ (x_1 : V), P' x_1 a b) finset.univ).card),
    have first_pq : ∀ v, (λ (x_1 : V), P x_1 a b) v → (λ (x_1 : V), P' x_1 a b) v,
    simp,
    intro v, specialize lift v,
    cases lift with lift1 lift2,
    exact lift1,
    exact cardinality_lemma (λ (x_1 : V), P x_1 a b) (λ (x_1 : V), P' x_1 a b) first_pq,
    have second : ((finset.filter (λ (x_1 : V), P' x_1 b a) finset.univ).card) ≤ ((finset.filter (λ (x_1 : V), P x_1 b a) finset.univ).card),
    have second_pq : ∀ v, (λ (x_1 : V), P' x_1 b a) v → (λ (x_1 : V), P x_1 b a) v,
    simp,
    intro v,
    contrapose,
    intro npyx,
    specialize lift v,
    cases lift with lift1 lift2,
    contrapose npyx,
    push_neg, push_neg at npyx,
    exact lift2 npyx,

    exact cardinality_lemma (λ (x_1 : V), P' x_1 b a) (λ (x_1 : V), P x_1 b a) second_pq,
    mono,
    simp,
    exact first,
    simp,
    exact second,
end

-- if the elements are the same between the two Profs, the a = b case is still true.
lemma margin_lemma' (P P' : Prof V X) [fintype V] [profile_asymmetric P'] (a b : X) : (∀ (v : V), (P v a b → P' v a b) ∧ (P' v b a → P v b a)) → margin P a b ≤ margin P' a b :=
begin
    intro pq,
    by_cases a = b,
    rw h,
    rw self_margin_zero, rw self_margin_zero,
    exact margin_lemma P P' a b h pq,
end

lemma margin_lt_margin_of_lift (P P' : Prof V X) [fintype V] (y x : X) : simple_lift P' P x → margin P' y x ≤ margin P y x :=
begin
    intro lift,
    unfold simple_lift at lift,
    cases lift with lift1 lift2,
    specialize lift2 y,
    by_cases x = y,
    rw h, rw self_margin_zero, rw self_margin_zero,

    have test := margin_lemma P P' x y h lift2,
    rw margin_antisymmetric,
    rw margin_antisymmetric P,
    simp,
    exact test,
end

theorem split_cycle_monotonicity [fintype V] (P P' : Prof V X) [profile_asymmetric P'] : monotonicity split_cycle P P' :=
begin
    unfold monotonicity, intros x x_won lift,
    unfold split_cycle, unfold max_el_VSCC,
    rw split_cycle_definitions,
    simp,
    unfold split_cycle_VCCR',
    intro y,
    push_neg,
    use _inst_1,
    intro m, /- "so suppose margin P'(y,x) > 0." -/
    unfold split_cycle at x_won,
    unfold max_el_VSCC at x_won,
    rw split_cycle_definitions at x_won,
    simp at x_won,
    unfold split_cycle_VCCR' at x_won,
    simp at x_won, specialize x_won y, -- now we must show that there is a chain from x to y of margin greater than margin y x

    unfold margin_pos at m,
    have m' := margin_lt_margin_of_lift P P' y x lift,

    cases x_won with _ x_won,
    unfold margin_pos at x_won,
    rw margin_eq_margin x_won_w _inst_1 at x_won,

    specialize x_won (lt_of_lt_of_le m m'),
    cases x_won with l x_won,
    use l,
    cases x_won with nodup x_won,
    cases x_won with nonempty x_won,
    use nonempty,
    use nodup,
    cases x_won with x_nth x_won,
    use x_nth,
    cases x_won with y_nth x_won,
    use y_nth, -- reduced to just the chain. All the properties of the chain have been proven.

    rw list.chain'_iff_nth_le at x_won,
    rw list.chain'_iff_nth_le,
    intro i, intro i_bound, -- for any index i
    specialize x_won i,
    specialize x_won i_bound,
    have test := le_trans m' x_won,
    apply le_trans test, -- by the transitive property it is sufficient to show that the margins exclusively increased due to the lift.

    -- problem: if the cycle is not simple and contains x, it may not be a cycle in P'.
    by_cases i = 0,
    have eq : (l.nth_le i (nat.lt_of_lt_pred i_bound)) = (l.nth_le 0 (list.length_pos_of_ne_nil nonempty)),
    congr, exact h,
    rw eq, rw x_nth,
    cases lift with lift1 lift2,
    rw margin_eq_margin x_won_w _inst_1,
    exact margin_lemma' P P' x (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound)) (lift2 (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound))),
    -- we use the second property of lifts to show this since the first element is x.

    have neq : (l.nth_le i (nat.lt_of_lt_pred i_bound)) ≠ x,
    change l.pairwise ne at nodup,
    rw ←x_nth,
    exact ne.symm (list.pairwise_iff_nth_le.mp nodup 0 i (nat.lt_of_lt_pred i_bound) (zero_lt_iff.mpr h)),

    have neq2 : (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound)) ≠ x,
    change l.pairwise ne at nodup,
    rw ←x_nth,
    exact ne.symm (list.pairwise_iff_nth_le.mp nodup 0 (i + 1) (nat.lt_pred_iff.mp i_bound) (nat.zero_lt_succ i)),

    have pq : ∀ (v : V),
    (P v (l.nth_le i (nat.lt_of_lt_pred i_bound)) (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound)) → P' v (l.nth_le i (nat.lt_of_lt_pred i_bound)) (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound))) ∧
      (P' v (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound)) (l.nth_le i (nat.lt_of_lt_pred i_bound)) → P v (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound)) (l.nth_le i (nat.lt_of_lt_pred i_bound))),

    intro v,
    cases lift with lift1 lift2,
    split,
    exact (lift1 (l.nth_le i (nat.lt_of_lt_pred i_bound)) neq (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound)) neq2 v).mp,
    exact (lift1 (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound)) neq2 (l.nth_le i (nat.lt_of_lt_pred i_bound)) neq v).mpr,
    rw margin_eq_margin x_won_w _inst_1,
    exact margin_lemma' P P' (l.nth_le i (nat.lt_of_lt_pred i_bound)) (l.nth_le (i + 1) (nat.lt_pred_iff.mp i_bound)) pq,
end


/-
Copyright (c) 2021 Justus Springer. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Justus Springer
-/
import algebra.category.Group.filtered_colimits
import algebra.category.Module.basic

/-!
# The forgetful functor from `R`-modules preserves filtered colimits.

Forgetful functors from algebraic categories usually don't preserve colimits. However, they tend
to preserve _filtered_ colimits.

In this file, we start with a ring `R`, a small filtered category `J` and a functor
`F : J ⥤ Module R`. We show that the colimit of `F ⋙ forget₂ (Module R) AddCommGroup`
(in `AddCommGroup`) carries the structure of an `R`-module, thereby showing that the forgetful
functor `forget₂ (Module R) AddCommGroup` preserves filtered colimits. In particular, this implies
that `forget (Module R)` preserves filtered colimits.

-/

universes u v

noncomputable theory
open_locale classical

open category_theory
open category_theory.limits
open category_theory.is_filtered (renaming max → max') -- avoid name collision with `_root_.max`.

open AddMon.filtered_colimits (colimit_zero_eq colimit_add_mk_eq)

namespace Module.filtered_colimits

section

-- We use parameters here, mainly so we can have the abbreviations `M` and `M.mk` below, without
-- passing around `F` all the time.
parameters {R : Type u} [ring R] {J : Type v} [small_category J] [is_filtered J]
parameters (F : J ⥤ Module.{max v u} R)

/--
The colimit of `F ⋙ forget₂ (Module R) AddCommGroup` in the category `AddCommGroup`.
In the following, we will show that this has the structure of an `R`-module.
-/
abbreviation M : AddCommGroup :=
AddCommGroup.filtered_colimits.colimit (F ⋙ forget₂ (Module R) AddCommGroup.{max v u})

/-- The canonical projection into the colimit, as a quotient type. -/
abbreviation M.mk : (Σ j, F.obj j) → M := quot.mk (types.quot.rel (F ⋙ forget (Module R)))

lemma M.mk_eq (x y : Σ j, F.obj j)
  (h : ∃ (k : J) (f : x.1 ⟶ k) (g : y.1 ⟶ k), F.map f x.2 = F.map g y.2) :
  M.mk x = M.mk y :=
quot.eqv_gen_sound (types.filtered_colimit.eqv_gen_quot_rel_of_rel (F ⋙ forget (Module R)) x y h)

/-- The "unlifted" version of scalar multiplication in the colimit. -/
def colimit_smul_aux (r : R) (x : Σ j, F.obj j) : M :=
M.mk ⟨x.1, r • x.2⟩

lemma colimit_smul_aux_eq_of_rel (r : R) (x y : Σ j, F.obj j)
  (h : types.filtered_colimit.rel (F ⋙ forget (Module R)) x y) :
  colimit_smul_aux r x = colimit_smul_aux r y :=
begin
  apply M.mk_eq,
  obtain ⟨k, f, g, hfg⟩ := h,
  use [k, f, g],
  simp only [category_theory.functor.comp_map, forget_map_eq_coe] at hfg,
  rw [linear_map.map_smul, linear_map.map_smul, hfg],
end

/-- Scalar multiplication in the colimit. See also `colimit_smul_aux`. -/
instance colimit_has_smul : has_smul R M :=
{ smul := λ r x, begin
    refine quot.lift (colimit_smul_aux F r) _ x,
    intros x y h,
    apply colimit_smul_aux_eq_of_rel,
    apply types.filtered_colimit.rel_of_quot_rel,
    exact h,
  end }

@[simp]
lemma colimit_smul_mk_eq (r : R) (x : Σ j, F.obj j) : r • M.mk x = M.mk ⟨x.1, r • x.2⟩ := rfl

instance colimit_module : module R M :=
{ one_smul := λ x, begin
    apply quot.induction_on x, clear x, intro x, cases x with j x,
    erw [colimit_smul_mk_eq F 1 ⟨j, x⟩, one_smul],
    refl,
  end,
  mul_smul := λ r s x, begin
    apply quot.induction_on x, clear x, intro x, cases x with j x,
    erw [colimit_smul_mk_eq F (r * s) ⟨j, x⟩, colimit_smul_mk_eq F s ⟨j, x⟩,
      colimit_smul_mk_eq F r ⟨j, _⟩, mul_smul],
  end,
  smul_add := λ r x y, begin
    apply quot.induction_on₂ x y, clear x y, intros x y, cases x with i x, cases y with j y,
    erw [colimit_add_mk_eq _ ⟨i, x⟩ ⟨j, y⟩ (max' i j) (left_to_max i j) (right_to_max i j),
      colimit_smul_mk_eq, smul_add, colimit_smul_mk_eq, colimit_smul_mk_eq,
      colimit_add_mk_eq _ ⟨i, _⟩ ⟨j, _⟩ (max' i j) (left_to_max i j) (right_to_max i j),
      linear_map.map_smul, linear_map.map_smul],
    refl,
  end,
  smul_zero := λ r, begin
    erw [colimit_zero_eq _ (is_filtered.nonempty.some : J), colimit_smul_mk_eq, smul_zero],
    refl,
  end,
  zero_smul := λ x, begin
    apply quot.induction_on x, clear x, intro x, cases x with j x,
    erw [colimit_smul_mk_eq, zero_smul, colimit_zero_eq _ j],
    refl,
  end,
  add_smul := λ r s x, begin
    apply quot.induction_on x, clear x, intro x, cases x with j x,
    erw [colimit_smul_mk_eq, add_smul, colimit_smul_mk_eq, colimit_smul_mk_eq,
      colimit_add_mk_eq _ ⟨j, _⟩ ⟨j, _⟩ j (𝟙 j) (𝟙 j), category_theory.functor.map_id,
      id_apply, id_apply],
    refl,
  end }

/-- The bundled `R`-module giving the filtered colimit of a diagram. -/
def colimit : Module R := Module.of R M

/-- The linear map from a given `R`-module in the diagram to the colimit module. -/
def cocone_morphism (j : J) : F.obj j ⟶ colimit :=
{ map_smul' := λ r x, begin erw colimit_smul_mk_eq F r ⟨j, x⟩, refl, end,
.. (AddCommGroup.filtered_colimits.colimit_cocone
    (F ⋙ forget₂ (Module R) AddCommGroup.{max v u})).ι.app j }

/-- The cocone over the proposed colimit module. -/
def colimit_cocone : cocone F :=
{ X := colimit,
  ι :=
  { app := cocone_morphism,
    naturality' := λ j j' f,
      linear_map.coe_injective ((types.colimit_cocone (F ⋙ forget (Module R))).ι.naturality f) } }

/--
Given a cocone `t` of `F`, the induced monoid linear map from the colimit to the cocone point.
We already know that this is a morphism between additive groups. The only thing left to see is that
it is a linear map, i.e. preserves scalar multiplication.
-/
def colimit_desc (t : cocone F) : colimit ⟶ t.X :=
{ map_smul' := λ r x, begin
    apply quot.induction_on x, clear x, intro x, cases x with j x,
    erw colimit_smul_mk_eq,
    exact linear_map.map_smul (t.ι.app j) r x,
  end,
  .. (AddCommGroup.filtered_colimits.colimit_cocone_is_colimit
      (F ⋙ forget₂ (Module R) AddCommGroup.{max v u})).desc
      ((forget₂ (Module R) AddCommGroup.{max v u}).map_cocone t) }

/-- The proposed colimit cocone is a colimit in `Module R`. -/
def colimit_cocone_is_colimit : is_colimit colimit_cocone :=
{ desc := colimit_desc,
  fac' := λ t j, linear_map.coe_injective $
    (types.colimit_cocone_is_colimit (F ⋙ forget (Module R))).fac
    ((forget (Module R)).map_cocone t) j,
  uniq' := λ t m h, linear_map.coe_injective $
    (types.colimit_cocone_is_colimit (F ⋙ forget (Module R))).uniq
    ((forget (Module R)).map_cocone t) m ((λ j, funext $ λ x, linear_map.congr_fun (h j) x)) }

instance forget₂_AddCommGroup_preserves_filtered_colimits :
  preserves_filtered_colimits (forget₂ (Module R) AddCommGroup.{u}) :=
{ preserves_filtered_colimits := λ J _ _, by exactI
  { preserves_colimit := λ F, preserves_colimit_of_preserves_colimit_cocone
      (colimit_cocone_is_colimit F)
      (AddCommGroup.filtered_colimits.colimit_cocone_is_colimit
        (F ⋙ forget₂ (Module.{u} R) AddCommGroup.{u})) } }

instance forget_preserves_filtered_colimits :
  preserves_filtered_colimits (forget (Module.{u} R)) :=
limits.comp_preserves_filtered_colimits (forget₂ (Module R) AddCommGroup) (forget AddCommGroup)

end

end Module.filtered_colimits


/- Basic Navigation and Rewritting -/

namespace navrw

  example (a b c : Nat) : a * (b * c) = a * (c * b) := by
    conv =>
      -- ⊢ a * (b * c) = a * (c * b)
      lhs
      -- ⊢ a * (b * c)
      congr
      -- ⊢ a ; ⊢ b * c
      skip
      -- ⊢ b * c
      rw [Nat.mul_comm]

  example : (fun x : Nat => 0 + x) = (fun x : Nat => x) := by
    conv =>
      lhs
      -- ⊢ fun x => 0 + x
      intro x
      -- x ⊢ 0 + x
      rw [Nat.zero_add]

end navrw

/- Pattern Matching -/

namespace pm

  example (a b c : Nat) : a * (b * c) = a * (c * b) := by
    conv in b * c => rw [Nat.mul_comm]

  example (a b c : Nat) : a * (b * c) = a * (c * b) := by
    conv =>
      pattern b * c
      rw [Nat.mul_comm]

  /- Structuring -/

  example (a b c : Nat) : (0 + a) * (b * c) = a * (c * b) := by
    conv =>
      lhs
      congr
      . rw [Nat.zero_add]
      . rw [Nat.mul_comm]

end pm

/- Other Tactics -/

namespace ot

  example (a b c : Nat) : a * (b * c) = a * (c * b) := by
    conv =>
      lhs
      arg 2 -- ⊢ b * c
      rw [Nat.mul_comm]

  def f (x : Nat) :=
    if x > 0 then x + 1 else x + 2

  example (g : Nat → Nat) (h1 : g x = x + 1) (h2 : x > 0) : g x = f x := by
    conv =>
      rhs -- ⊢ f x
      simp [f, h2] -- ⊢ x + 1
    exact h1

  example (g : Nat → Nat → Nat) (h1 : ∀ x, x ≠ 0 → g x x = 1)
      (h2 : x ≠ 0) : g x x + x = 1 + x := by
    conv =>
      lhs
      arg 1 -- ⊢ g x x
      rw [h1]
      . skip
      . tactic => exact h2

end ot

namespace Nat

  inductive Tric  (x y: Nat) where
  | eq : x = y → Tric x y
  | gt : (z : Nat) → (x = y + z ∧ z ≠ 0) → Tric x y
  | lt : (z : Nat) → (y = x + z ∧ z ≠ 0) → Tric x y

  def e : 3 = 3 := by rfl
  #check Tric.eq e -- Tric 3 3

  def g : 5 = 3 + 2 ∧ 2 ≠ 0 := by
    constructor <;> simp

  #check Tric.gt 2 g -- Tric 5 3
  #check g

  theorem tric_addition : ∀ (x y : Nat), Tric x y := by
    intro x y
    induction x with
    | zero => match y with
      | zero => apply Tric.eq; rfl
      | succ y =>
        have : succ y = 0 + succ y ∧ succ y ≠ 0 := by
          constructor <;> simp
        exact Tric.lt (succ y) this
    | succ x ih => match ih with
      | Tric.eq e =>
        have : succ x = y + 1 ∧ 1 ≠ 0 := by constructor <;> simp [e]
        exact Tric.gt 1 this
      | Tric.gt z h =>
        have : succ x = y + (z + 1) ∧ (z + 1) ≠ 0 := by constructor <;> simp_arith [h.left]
          exact Tric.gt (z + 1) this
      | Tric.lt z (And.intro he hz) => match z with
        | zero => contradiction
        | succ n =>
          have h1 : y = succ x + n := by simp_arith [he]
          match n with
          | zero =>
            have : succ x = y := by simp_arith [h1]
            exact Tric.eq this
          | succ n =>
            have : succ n ≠ 0 := by simp
            exact Tric.lt (succ n) (And.intro h1 this)

end Nat


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

More theorems about the sum type.
-/

universes u v
variables {α : Type u} {β : Type v}
open sum

attribute [derive decidable_eq] sum

@[simp] theorem sum.forall {p : α ⊕ β → Prop} : (∀ x, p x) ↔ (∀ a, p (inl a)) ∧ (∀ b, p (inr b)) :=
⟨λ h, ⟨λ a, h _, λ b, h _⟩, λ ⟨h₁, h₂⟩, sum.rec h₁ h₂⟩

@[simp] theorem sum.exists {p : α ⊕ β → Prop} : (∃ x, p x) ↔ (∃ a, p (inl a)) ∨ ∃ b, p (inr b) :=
⟨λ h, match h with
| ⟨inl a, h⟩ := or.inl ⟨a, h⟩
| ⟨inr b, h⟩ := or.inr ⟨b, h⟩
end, λ h, match h with
| or.inl ⟨a, h⟩ := ⟨inl a, h⟩
| or.inr ⟨b, h⟩ := ⟨inr b, h⟩
end⟩

namespace sum

@[simp] theorem inl.inj_iff {a b} : (inl a : α ⊕ β) = inl b ↔ a = b :=
⟨inl.inj, congr_arg _⟩

@[simp] theorem inr.inj_iff {a b} : (inr a : α ⊕ β) = inr b ↔ a = b :=
⟨inr.inj, congr_arg _⟩

@[simp] theorem inl_ne_inr {a : α} {b : β} : inl a ≠ inr b.

@[simp] theorem inr_ne_inl {a : α} {b : β} : inr b ≠ inl a.

section
  variables (ra : α → α → Prop) (rb : β → β → Prop)

  /-- Lexicographic order for sum. Sort all the `inl a` before the `inr b`,
    otherwise use the respective order on `α` or `β`. -/
  inductive lex : α ⊕ β → α ⊕ β → Prop
  | inl {a₁ a₂} (h : ra a₁ a₂) : lex (inl a₁) (inl a₂)
  | inr {b₁ b₂} (h : rb b₁ b₂) : lex (inr b₁) (inr b₂)
  | sep (a b) : lex (inl a) (inr b)

  variables {ra rb}

  @[simp] theorem lex_inl_inl {a₁ a₂} : lex ra rb (inl a₁) (inl a₂) ↔ ra a₁ a₂ :=
  ⟨λ h, by cases h; assumption, lex.inl _⟩

  @[simp] theorem lex_inr_inr {b₁ b₂} : lex ra rb (inr b₁) (inr b₂) ↔ rb b₁ b₂ :=
  ⟨λ h, by cases h; assumption, lex.inr _⟩

  @[simp] theorem lex_inr_inl {b a} : ¬ lex ra rb (inr b) (inl a) :=
  λ h, by cases h

  attribute [simp] lex.sep

  theorem lex_acc_inl {a} (aca : acc ra a) : acc (lex ra rb) (inl a) :=
  begin
    induction aca with a H IH,
    constructor, intros y h,
    cases h with a' _ h',
    exact IH _ h'
  end

  theorem lex_acc_inr (aca : ∀ a, acc (lex ra rb) (inl a)) {b} (acb : acc rb b) : acc (lex ra rb) (inr b) :=
  begin
    induction acb with b H IH,
    constructor, intros y h,
    cases h with _ _ _ b' _ h' a,
    { exact IH _ h' },
    { exact aca _ }
  end

  theorem lex_wf (ha : well_founded ra) (hb : well_founded rb) : well_founded (lex ra rb) :=
  have aca : ∀ a, acc (lex ra rb) (inl a), from λ a, lex_acc_inl (ha.apply a),
  ⟨λ x, sum.rec_on x aca (λ b, lex_acc_inr aca (hb.apply b))⟩

end

@[simp] def map {α' β'} (f : α → α') (g : β → β') : α ⊕ β → α' ⊕ β'
| (sum.inl l) := sum.inl $ f l
| (sum.inr r) := sum.inr $ g r

/-- Swap the factors of a sum type -/
@[simp] def swap : α ⊕ β → β ⊕ α
| (inl a) := inr a
| (inr b) := inl b

@[simp] lemma swap_swap (x : α ⊕ β) : swap (swap x) = x :=
by cases x; refl

@[simp] lemma swap_swap_eq : swap ∘ swap = @id (α ⊕ β) :=
funext $ swap_swap

@[simp] lemma swap_left_inverse : function.left_inverse (@swap α β) swap :=
swap_swap

@[simp] lemma swap_right_inverse : function.right_inverse (@swap α β) swap :=
swap_swap

end sum


/-
Copyright (c) 2021 Jordan Brown, Thomas Browning, Patrick Lutz. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jordan Brown, Thomas Browning, Patrick Lutz
-/

import group_theory.abelianization
import data.bracket

/-!
# Solvable Groups

In this file we introduce the notion of a solvable group. We define a solvable group as one whose
derived series is eventually trivial. This requires defining the commutator of two subgroups and
the derived series of a group.

## Main definitions

* `general_commutator H₁ H₂` : the commutator of the subgroups `H₁` and `H₂`
* `derived_series G n` : the `n`th term in the derived series of `G`, defined by iterating
  `general_commutator` starting with the top subgroup
* `is_solvable G` : the group `G` is solvable
-/

open subgroup

variables {G : Type*} [group G]

section general_commutator

/-- The commutator of two subgroups `H₁` and `H₂`. -/
instance general_commutator : has_bracket (subgroup G) (subgroup G) :=
⟨λ H₁ H₂, closure {x | ∃ (p ∈ H₁) (q ∈ H₂), p * q * p⁻¹ * q⁻¹ = x}⟩

lemma general_commutator_def (H₁ H₂ : subgroup G) :
  ⁅H₁, H₂⁆ = closure {x | ∃ (p ∈ H₁) (q ∈ H₂), p * q * p⁻¹ * q⁻¹ = x} := rfl

instance general_commutator_normal (H₁ H₂ : subgroup G) [h₁ : H₁.normal]
  [h₂ : H₂.normal] : normal ⁅H₁, H₂⁆ :=
begin
  let base : set G := {x | ∃ (p ∈ H₁) (q ∈ H₂), p * q * p⁻¹ * q⁻¹ = x},
  suffices h_base : base = group.conjugates_of_set base,
  { dsimp only [general_commutator_def, ←base],
    rw h_base,
    exact subgroup.normal_closure_normal },
  apply set.subset.antisymm group.subset_conjugates_of_set,
  intros a h,
  simp_rw [group.mem_conjugates_of_set_iff, is_conj_iff] at h,
  rcases h with ⟨b, ⟨c, hc, e, he, rfl⟩, d, rfl⟩,
  exact ⟨d * c * d⁻¹, h₁.conj_mem c hc d, d * e * d⁻¹, h₂.conj_mem e he d, by group⟩,
end

lemma general_commutator_mono {H₁ H₂ K₁ K₂ : subgroup G} (h₁ : H₁ ≤ K₁) (h₂ : H₂ ≤ K₂) :
  ⁅H₁, H₂⁆ ≤ ⁅K₁, K₂⁆ :=
begin
  apply closure_mono,
  rintros x ⟨p, hp, q, hq, rfl⟩,
  exact ⟨p, h₁ hp, q, h₂ hq, rfl⟩,
end

lemma general_commutator_def' (H₁ H₂ : subgroup G) [H₁.normal] [H₂.normal] :
  ⁅H₁, H₂⁆ = normal_closure {x | ∃ (p ∈ H₁) (q ∈ H₂), p * q * p⁻¹ * q⁻¹ = x} :=
by rw [← normal_closure_eq_self ⁅H₁, H₂⁆, general_commutator_def,
  normal_closure_closure_eq_normal_closure]

lemma general_commutator_le (H₁ H₂ : subgroup G) (K : subgroup G) :
  ⁅H₁, H₂⁆ ≤ K ↔ ∀ (p ∈ H₁) (q ∈ H₂), p * q * p⁻¹ * q⁻¹ ∈ K :=
begin
  rw [general_commutator, closure_le],
  split,
  { intros h p hp q hq,
    exact h ⟨p, hp, q, hq, rfl⟩, },
  { rintros h x ⟨p, hp, q, hq, rfl⟩,
    exact h p hp q hq, }
end

lemma general_commutator_comm (H₁ H₂ : subgroup G) : ⁅H₁, H₂⁆ = ⁅H₂, H₁⁆ :=
begin
  suffices : ∀ H₁ H₂ : subgroup G, ⁅H₁, H₂⁆ ≤ ⁅H₂, H₁⁆, { exact le_antisymm (this _ _) (this _ _) },
  intros H₁ H₂,
  rw general_commutator_le,
  intros p hp q hq,
  have h : (p * q * p⁻¹ * q⁻¹)⁻¹ ∈ ⁅H₂, H₁⁆ := subset_closure ⟨q, hq, p, hp, by group⟩,
  convert inv_mem ⁅H₂, H₁⁆ h,
  group,
end

lemma general_commutator_le_right (H₁ H₂ : subgroup G) [h : normal H₂] :
  ⁅H₁, H₂⁆ ≤ H₂ :=
begin
  rw general_commutator_le,
  intros p hp q hq,
  exact mul_mem H₂ (h.conj_mem q hq p) (inv_mem H₂ hq),
end

lemma general_commutator_le_left (H₁ H₂ : subgroup G) [h : normal H₁] :
  ⁅H₁, H₂⁆ ≤ H₁ :=
begin
  rw general_commutator_comm,
  exact general_commutator_le_right H₂ H₁,
end

@[simp] lemma general_commutator_bot (H : subgroup G) : ⁅H, ⊥⁆ = (⊥ : subgroup G) :=
by { rw eq_bot_iff, exact general_commutator_le_right H ⊥ }

@[simp] lemma bot_general_commutator (H : subgroup G) : ⁅(⊥ : subgroup G), H⁆ = (⊥ : subgroup G) :=
by { rw eq_bot_iff, exact general_commutator_le_left ⊥ H }

lemma general_commutator_le_inf (H₁ H₂ : subgroup G) [normal H₁] [normal H₂] :
  ⁅H₁, H₂⁆ ≤ H₁ ⊓ H₂ :=
by simp only [general_commutator_le_left, general_commutator_le_right, le_inf_iff, and_self]

end general_commutator

section derived_series

variables (G)

/-- The derived series of the group `G`, obtained by starting from the subgroup `⊤` and repeatedly
  taking the commutator of the previous subgroup with itself for `n` times. -/
def derived_series : ℕ → subgroup G
| 0       := ⊤
| (n + 1) := ⁅(derived_series n), (derived_series n)⁆

@[simp] lemma derived_series_zero : derived_series G 0 = ⊤ := rfl

@[simp] lemma derived_series_succ (n : ℕ) :
  derived_series G (n + 1) = ⁅(derived_series G n), (derived_series G n)⁆ := rfl

lemma derived_series_normal (n : ℕ) : (derived_series G n).normal :=
begin
  induction n with n ih,
  { exact subgroup.top_normal, },
  { rw derived_series_succ,
    exactI general_commutator_normal (derived_series G n) (derived_series G n), }
end

@[simp] lemma general_commutator_eq_commutator :
  ⁅(⊤ : subgroup G), (⊤ : subgroup G)⁆ = commutator G :=
begin
  rw [commutator, general_commutator_def'],
  apply le_antisymm; apply normal_closure_mono,
  { exact λ x ⟨p, _, q, _, h⟩, ⟨p, q, h⟩, },
  { exact λ x ⟨p, q, h⟩, ⟨p, mem_top p, q, mem_top q, h⟩, }
end

lemma commutator_def' : commutator G = subgroup.closure {x : G | ∃ p q, p * q * p⁻¹ * q⁻¹ = x} :=
begin
  rw [← general_commutator_eq_commutator, general_commutator],
  apply le_antisymm; apply closure_mono,
  { exact λ x ⟨p, _, q, _, h⟩, ⟨p, q, h⟩ },
  { exact λ x ⟨p, q, h⟩, ⟨p, mem_top p, q, mem_top q, h⟩ }
end

@[simp] lemma derived_series_one : derived_series G 1 = commutator G :=
general_commutator_eq_commutator G

end derived_series

section commutator_map

variables {G} {G' : Type*} [group G'] {f : G →* G'}

lemma map_commutator_eq_commutator_map (H₁ H₂ : subgroup G) :
  ⁅H₁, H₂⁆.map f = ⁅H₁.map f, H₂.map f⁆ :=
begin
  rw [general_commutator, general_commutator, monoid_hom.map_closure],
  apply le_antisymm; apply closure_mono,
  { rintros _ ⟨x, ⟨p, hp, q, hq, rfl⟩, rfl⟩,
    refine ⟨f p, mem_map.mpr ⟨p, hp, rfl⟩, f q, mem_map.mpr ⟨q, hq, rfl⟩, by simp *⟩, },
  { rintros x ⟨_, ⟨p, hp, rfl⟩, _, ⟨q, hq, rfl⟩, rfl⟩,
    refine ⟨p * q * p⁻¹ * q⁻¹, ⟨p, hp, q, hq, rfl⟩, by simp *⟩, },
end

lemma commutator_le_map_commutator {H₁ H₂ : subgroup G} {K₁ K₂ : subgroup G'} (h₁ : K₁ ≤ H₁.map f)
  (h₂ : K₂ ≤ H₂.map f) : ⁅K₁, K₂⁆ ≤ ⁅H₁, H₂⁆.map f :=
by { rw map_commutator_eq_commutator_map, exact general_commutator_mono h₁ h₂ }

section derived_series_map

variables (f)

lemma map_derived_series_le_derived_series (n : ℕ) :
  (derived_series G n).map f ≤ derived_series G' n :=
begin
  induction n with n ih,
  { simp only [derived_series_zero, le_top], },
  { simp only [derived_series_succ, map_commutator_eq_commutator_map, general_commutator_mono, *], }
end

variables {f}

lemma derived_series_le_map_derived_series (hf : function.surjective f) (n : ℕ) :
  derived_series G' n ≤ (derived_series G n).map f :=
begin
  induction n with n ih,
  { rwa [derived_series_zero, derived_series_zero, top_le_iff, ← monoid_hom.range_eq_map,
    ← monoid_hom.range_top_iff_surjective.mpr], },
  { simp only [*, derived_series_succ, commutator_le_map_commutator], }
end

lemma map_derived_series_eq (hf : function.surjective f) (n : ℕ) :
  (derived_series G n).map f = derived_series G' n :=
le_antisymm (map_derived_series_le_derived_series f n) (derived_series_le_map_derived_series hf n)

end derived_series_map
end commutator_map

section solvable

variables (G)

/-- A group `G` is solvable if its derived series is eventually trivial. We use this definition
  because it's the most convenient one to work with. -/
class is_solvable : Prop :=
(solvable : ∃ n : ℕ, derived_series G n = ⊥)

lemma is_solvable_def : is_solvable G ↔ ∃ n : ℕ, derived_series G n = ⊥ :=
⟨λ h, h.solvable, λ h, ⟨h⟩⟩

@[priority 100]
instance comm_group.is_solvable {G : Type*} [comm_group G] : is_solvable G :=
begin
  use 1,
  rw [eq_bot_iff, derived_series_one],
  calc commutator G ≤ (monoid_hom.id G).ker : abelianization.commutator_subset_ker (monoid_hom.id G)
  ... = ⊥ : rfl,
end

lemma is_solvable_of_comm {G : Type*} [hG : group G]
  (h : ∀ a b : G, a * b = b * a) : is_solvable G :=
begin
  letI hG' : comm_group G := { mul_comm := h .. hG },
  tactic.unfreeze_local_instances,
  cases hG,
  exact comm_group.is_solvable,
end

lemma is_solvable_of_top_eq_bot (h : (⊤ : subgroup G) = ⊥) : is_solvable G :=
⟨⟨0, h⟩⟩

@[priority 100]
instance is_solvable_of_subsingleton [subsingleton G] : is_solvable G :=
is_solvable_of_top_eq_bot G (by ext; simp at *)

variables {G} {G' : Type*} [group G'] {f : G →* G'}

lemma solvable_of_solvable_injective (hf : function.injective f) [h : is_solvable G'] :
  is_solvable G :=
begin
  rw is_solvable_def at *,
  cases h with n hn,
  use n,
  rw ← map_eq_bot_iff_of_injective _ hf,
  rw eq_bot_iff at *,
  calc map f (derived_series G n) ≤ derived_series G' n : map_derived_series_le_derived_series f n
  ... ≤ ⊥ : hn,
end

instance subgroup_solvable_of_solvable (H : subgroup G) [h : is_solvable G] : is_solvable H :=
solvable_of_solvable_injective (show function.injective (subtype H), from subtype.val_injective)

lemma solvable_of_surjective (hf : function.surjective f) [h : is_solvable G] :
  is_solvable G' :=
begin
  rw is_solvable_def at *,
  cases h with n hn,
  use n,
  calc derived_series G' n = (derived_series G n).map f : eq.symm (map_derived_series_eq hf n)
    ... = (⊥ : subgroup G).map f : by rw hn
    ... = ⊥ : map_bot f,
end

instance solvable_quotient_of_solvable (H : subgroup G) [H.normal] [h : is_solvable G] :
  is_solvable (quotient_group.quotient H) :=
solvable_of_surjective (show function.surjective (quotient_group.mk' H), by tidy)

lemma solvable_of_ker_le_range {G' G'' : Type*} [group G'] [group G''] (f : G' →* G)
  (g : G →* G'') (hfg : g.ker ≤ f.range) [hG' : is_solvable G'] [hG'' : is_solvable G''] :
  is_solvable G :=
begin
  tactic.unfreeze_local_instances,
  obtain ⟨n, hn⟩ := hG'',
  suffices : ∀ k : ℕ, derived_series G (n + k) ≤ (derived_series G' k).map f,
  { obtain ⟨m, hm⟩ := hG',
    use n + m,
    specialize this m,
    rwa [hm, map_bot, le_bot_iff] at this },
  intro k,
  induction k with k hk,
  { rw [add_zero, derived_series_zero, ←monoid_hom.range_eq_map],
    refine le_trans _ hfg,
    rw [←map_eq_bot_iff, eq_bot_iff, ←hn],
    exact map_derived_series_le_derived_series g n },
  { rw [nat.add_succ, derived_series_succ, derived_series_succ],
    exact commutator_le_map_commutator hk hk },
end

instance solvable_prod {G' : Type*} [group G'] [h : is_solvable G] [h' : is_solvable G'] :
  is_solvable (G × G') :=
solvable_of_ker_le_range (monoid_hom.inl G G') (monoid_hom.snd G G')
  (λ x hx, ⟨x.1, prod.ext rfl hx.symm⟩)

end solvable

section is_simple_group

variable [is_simple_group G]

lemma is_simple_group.derived_series_succ {n : ℕ} : derived_series G n.succ = commutator G :=
begin
  induction n with n ih,
  { exact derived_series_one _ },
  rw [derived_series_succ, ih],
  cases (commutator.normal G).eq_bot_or_eq_top with h h; simp [h]
end

lemma is_simple_group.comm_iff_is_solvable :
  (∀ a b : G, a * b = b * a) ↔ is_solvable G :=
⟨is_solvable_of_comm, λ ⟨⟨n, hn⟩⟩, begin
  cases n,
  { rw derived_series_zero at hn,
    intros a b,
    refine (mem_bot.1 _).trans (mem_bot.1 _).symm;
    { rw ← hn,
      exact mem_top _ } },
  { rw is_simple_group.derived_series_succ at hn,
    intros a b,
    rw [← mul_inv_eq_one, mul_inv_rev, ← mul_assoc, ← mem_bot, ← hn],
    exact subset_normal_closure ⟨a, b, rfl⟩ }
end⟩

end is_simple_group


import util.list.pairwise

theorem filter_spec {t: Type} {p: t -> Prop} {xs: list t} {x: t}
  [decidable_pred p]:
  (x ∈ xs) ∧ (p x) ↔ x ∈ list.filter p xs :=
begin
  split,
  intro h,
  induction xs,
  exfalso,
  exact list.not_mem_nil x h.left,
  simp [list.filter],
  by_cases h_1: p xs_hd,
  simp [h_1],
  cases h.left,
  left,
  exact h_2,
  right,
  exact xs_ih (and.intro h_2 h.right),
  simp [h_1],
  cases h.left,
  rw h_2 at h,
  exfalso,
  exact h_1 h.right,
  exact xs_ih (and.intro h_2 h.right),  intro h,
  induction xs,
  simp [list.filter] at h,
  exfalso,
  exact h,
  simp [list.filter] at h,
  by_cases h_1: p xs_hd,
  simp [h_1] at h,
  cases h,
  rw h,
  split,
  simp,
  exact h_1,
  cases xs_ih h,
  split,
  simp,
  right,
  exact left,
  exact right,
  simp [h_1] at h,
  cases xs_ih h,
  split,
  simp,
  right,
  exact left,
  exact right,
end

theorem filter_pairwise {t: Type} {r: t -> t -> Prop} {xs: list t}
  (p: t -> Prop)
  [decidable_pred p]:
  pairwise r xs ->
  pairwise r (list.filter p xs) :=
begin
  intro h,
  induction h,
  simp [list.filter],
  exact pairwise.nil r,
  simp [list.filter],
  by_cases p h_x,
  simp [h],
  let f: ∀ (y: t), y ∈ list.filter p h_xs -> r h_x y := fun a, fun b,
    h_a a (iff.elim_right filter_spec b).left,
  exact pairwise.cons f h_ih,
  simp [h],
  exact h_ih,
end

theorem filter_idempotent {t: Type} (p: t -> Prop) (xs: list t)
  [decidable_pred p]:
  list.filter p (list.filter p xs) = list.filter p xs :=
begin
  induction xs,
  simp [list.filter],
  simp [list.filter],
  by_cases p xs_hd,
  simp [h],
  exact xs_ih,
  simp [h],
  exact xs_ih,
end

theorem filter_comm {t: Type} (p p': t -> Prop) (xs: list t)
  [decidable_pred p]
  [decidable_pred p']:
  list.filter p (list.filter p' xs) = list.filter p' (list.filter p xs) :=
begin
  induction xs,
  simp [list.filter],
  simp [list.filter],
  by_cases p xs_hd,
  by_cases h_1: p' xs_hd,
  simp [h, h_1],
  exact xs_ih,
  simp [h, h_1],
  exact xs_ih,
  by_cases h_1: p' xs_hd,
  simp [h, h_1],
  exact xs_ih,
  simp [h, h_1],
  exact xs_ih,
end

theorem not_filter {t: Type} {p: t -> Prop} {xs: list t} {x: t}
  [decidable_pred p]
  [decidable_eq t]:
  x ∉ list.filter p xs ->
  p x ->
  x ∉ xs :=
begin
  intros x_notin px,
  induction xs,
  exact list.not_mem_nil x,
  simp [list.filter] at x_notin,
  by_cases x = xs_hd,
  rw h at x_notin px,
  simp [px] at x_notin,
  exfalso,
  exact x_notin,
  simp,
  intro a,
  cases a,
  exact h a,
  by_cases h_1: p xs_hd,
  simp [h_1] at x_notin,
  let b := fun a, x_notin (or.inr a),
  exact xs_ih b a,
  simp [h_1] at x_notin,
  exact xs_ih x_notin a,
end


import data.finset.basic
import tactic
import group_theory.group_action.basic
import FinitelyGeneratedIdeals

def is_ascending {R} [comm_ring R] (f : ℕ → ideal R) :=
  ∀ n, f n ⊆ f (n + 1)

lemma arbitrary_indices_ascending {R} [comm_ring R] (f : ℕ → ideal R)
  : is_ascending f → ∀ i j, i ≤ j → f i ⊆ f j :=
begin
  intros h i j hij,
  cases (nat.le.dest hij) with k hk,
  revert i j,
  induction k with k ih; intros; subst hk,
  { refl },
  { transitivity (f (i + k)).carrier,
    apply ih; linarith,
    apply h }
end

def ascending_chain_is_directed {R} [comm_ring R] (f : ℕ → ideal R)
  : is_ascending f → directed (⊆) f :=
  λ hAsc i j, ⟨max i j, arbitrary_indices_ascending f hAsc i (max i j) (le_max_iff.mpr (or.inl (le_refl i))),
                        arbitrary_indices_ascending f hAsc j (max i j) (le_max_iff.mpr (or.inr (le_refl j)))⟩

def stabilizes {R} [comm_ring R] (f : ℕ → ideal R) := ∃ N, ∀ n ≥ N, f n = f N

def ACC (R) [comm_ring R] : Prop :=
  ∀ f : ℕ → ideal R, is_ascending f → stabilizes f

def all_ideals_fg (R) [comm_ring R] : Prop :=
  ∀ I : ideal R, fingen I

lemma exists_outside_of_not_fingen {R} [comm_ring R] (I : ideal R) (S : finset R)
  : ¬ fingen I → (∀ x, x ∈ S → x ∈ I) → ∃ y, y ∈ I ∧ y ∉ ideal_gen_by S :=
begin
  intros h g,
  by_contra h',
  apply h,
  existsi S,
  ext,
  constructor,
  { simp at h', apply_assumption },
  { apply gen_by_subset_is_subset, assumption }
end

local attribute [instance] classical.prop_decidable

noncomputable
def ascending_chain_of_generators_in_not_fg_ideal {R} [comm_ring R] (I : ideal R) (h : ¬ fingen I)
  : ℕ → { S : finset R | ∀ x, x ∈ S → x ∈ I }
| 0 := ⟨∅, by simp⟩
| (n+1) :=
  match ascending_chain_of_generators_in_not_fg_ideal n with
  | ⟨S, hS⟩ := match classical.indefinite_description _ (exists_outside_of_not_fingen I S h hS) with
              | ⟨y, hy⟩ := ⟨S.cons y (λ hIn, hy.right (finset_subset_of_ideal_gen_by S y hIn)),
                           λ x h', or.elim (finset.mem_cons.mp h')
                                           (λ hxy, eq.substr hxy hy.left)
                                           (hS x)⟩
              end
  end

lemma ascending_chain_of_generators_in_not_fg_ideal_is_ascending {R} [comm_ring R] (I : ideal R) (h : ¬ fingen I)
  : ∀ n, (ascending_chain_of_generators_in_not_fg_ideal I h n).val ⊆ (ascending_chain_of_generators_in_not_fg_ideal I h (n + 1)).val :=
begin
  intros n x h',
  destruct (ascending_chain_of_generators_in_not_fg_ideal I h n), intros S hS heq1,
  destruct (classical.indefinite_description _ (exists_outside_of_not_fingen I S h hS)), intros y hy heq2,
  rw [ascending_chain_of_generators_in_not_fg_ideal, heq1,
      ascending_chain_of_generators_in_not_fg_ideal._match_1, heq2,
      ascending_chain_of_generators_in_not_fg_ideal._match_2, finset.mem_cons],
  right, rw heq1 at h', exact h'
end

noncomputable
def ascending_chain_of_ideals_in_not_fg_ideal {R} [comm_ring R] (I : ideal R) (h : ¬ fingen I)
  : ℕ → ideal R := λ n, ideal_gen_by (ascending_chain_of_generators_in_not_fg_ideal I h n).val

lemma ascending_chain_of_ideals_in_not_fg_ideal_is_ascending {R} [comm_ring R] (I : ideal R) (h : ¬ fingen I)
  : is_ascending (ascending_chain_of_ideals_in_not_fg_ideal I h) :=
λ n x hx, gen_by_of_subset_is_subset _ _ (ascending_chain_of_generators_in_not_fg_ideal_is_ascending I h n) hx

theorem ACC_iff_all_ideals_fg (R) [comm_ring R] : ACC R ↔ all_ideals_fg R :=
begin
  constructor,
  { intros h I,
    by_contra h',
    have asc := ascending_chain_of_generators_in_not_fg_ideal I h',
    have := h (ascending_chain_of_ideals_in_not_fg_ideal I h') _,
    { cases this with N H,
      specialize H (N + 1) (by linarith),
      dsimp [ascending_chain_of_ideals_in_not_fg_ideal] at H,
      destruct ascending_chain_of_generators_in_not_fg_ideal I h' N, intros S hS h1,
      destruct (classical.indefinite_description _ (exists_outside_of_not_fingen I S h' hS)), intros y hy h2,
      rw [ascending_chain_of_generators_in_not_fg_ideal, h1,
          ascending_chain_of_generators_in_not_fg_ideal._match_1, h2,
          ascending_chain_of_generators_in_not_fg_ideal._match_2] at H,
      apply hy.right, dsimp at H, rw ← H, apply finset_subset_of_ideal_gen_by,
      apply finset.mem_cons_self },
    { apply ascending_chain_of_ideals_in_not_fg_ideal_is_ascending } },
  { intros h f hAsc,
    have : directed (⊆) f := ascending_chain_is_directed f hAsc,
    specialize h (union_of_directed_family f this),
    cases h with S hS,
    have in_S_implies_in_fn : ∀ y : R, y ∈ S → ∃ n, y ∈ f n,
    { intros y hy,
      have : y ∈ union_of_directed_family f this,
      { rw hS, apply finset_subset_of_ideal_gen_by, assumption },
      { cases this with S' hy', cases hy' with w hy', cases w with i hi, subst hi,
        existsi i, assumption } },
    let min_index_containing : S → ℕ := λ x, @nat.find (λ n, x.val ∈ f n) _ (in_S_implies_in_fn x.val x.property),
    let S' := multiset.map min_index_containing S.attach.val,
    suffices stable : ∀ n, (∀ k, k ∈ S' → k ≤ n) → f n = union_of_directed_family f this,
    { existsi (multiset.sum S'),
      intros n hn,
      transitivity union_of_directed_family f this;
      rw stable; intros k hk; transitivity S'.sum,
      { apply multiset.single_le_sum (λ _ _, nat.zero_le _) k hk },
      { assumption },
      { apply multiset.single_le_sum (λ _ _, nat.zero_le _) k hk },
      { refl } },
    intros n hn,
    ext, constructor; intro h,
    { existsi (f n).carrier, existsi _, assumption, existsi n, refl },
    { rw hS at h, cases h with coeffs h, rw h,
      apply linear_combinations_in_ideal,
      intros y hy,
      let k := min_index_containing ⟨y, hy⟩,
      apply arbitrary_indices_ascending f hAsc k n,
      { apply hn, rw multiset.mem_map, existsi (subtype.mk y hy), simp },
      { exact nat.find_spec (in_S_implies_in_fn y hy) } } }
end


theorem test (p q : Prop) (hp : p) (hq : q) : p ∧ q ∧ p :=
begin
  apply and.intro,
  exact hp,
  apply and.intro,
  exact hq,
  exact hp
end

#print test


/-
Copyright (c) 2022 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov
-/
import analysis.normed.group.completion
import analysis.normed_space.operator_norm
import topology.algebra.uniform_mul_action

/-!
# Normed space structure on the completion of a normed space

If `E` is a normed space over `𝕜`, then so is `uniform_space.completion E`. In this file we provide
necessary instances and define `uniform_space.completion.to_complₗᵢ` - coercion
`E → uniform_space.completion E` as a bundled linear isometry.
-/

noncomputable theory

namespace uniform_space
namespace completion

variables (𝕜 E : Type*) [normed_field 𝕜] [normed_group E] [normed_space 𝕜 E]

@[priority 100]
instance normed_space.to_has_uniform_continuous_const_smul :
  has_uniform_continuous_const_smul 𝕜 E :=
⟨λ c, (lipschitz_with_smul c).uniform_continuous⟩

instance : normed_space 𝕜 (completion E) :=
{ smul := (•),
  norm_smul_le := λ c x, induction_on x
    (is_closed_le (continuous_const_smul _).norm (continuous_const.mul continuous_norm)) $
    λ y, by simp only [← coe_smul, norm_coe, norm_smul],
  .. completion.module 𝕜 E }

variables {𝕜 E}

/-- Embedding of a normed space to its completion as a linear isometry. -/
def to_complₗᵢ : E →ₗᵢ[𝕜] completion E :=
{ to_fun := coe,
  map_smul' := coe_smul,
  norm_map' := norm_coe,
  .. to_compl }

@[simp] lemma coe_to_complₗᵢ : ⇑(to_complₗᵢ : E →ₗᵢ[𝕜] completion E) = coe := rfl

/-- Embedding of a normed space to its completion as a continuous linear map. -/
def to_complL : E →L[𝕜] completion E :=
to_complₗᵢ.to_continuous_linear_map

@[simp] lemma coe_to_complL : ⇑(to_complL : E →L[𝕜] completion E) = coe := rfl

@[simp] lemma norm_to_complL {𝕜 E : Type*} [nondiscrete_normed_field 𝕜]
  [normed_group E] [normed_space 𝕜 E] [nontrivial E] : ∥(to_complL : E →L[𝕜] completion E)∥ = 1 :=
(to_complₗᵢ : E →ₗᵢ[𝕜] completion E).norm_to_continuous_linear_map

end completion
end uniform_space


import tactic.basic

open tactic

local attribute [instance] classical.prop_decidable

example : true ↔ ¬false :=
by exact_dec_trivial

#exit

def foo (n : nat) : Prop := n = 0

instance : decidable_pred foo :=
begin intro n, simp [foo], apply_instance end

instance baz {α} {p : α → Prop} [decidable_pred p]
  (l : list α) : decidable (∀ x ∈ l, p x) :=
begin
  cases l, apply decidable.is_true, intros x h, cases h,
  rw list.forall_mem_cons, apply and.decidable,
end

example {α} {p : α → Prop} {a : α} {l : list α} :
  (∀ (x : α), x = a ∨ x ∈ l → p x) ↔ p a ∧ ∀ x ∈ l, p x :=
begin
  simp only [or_imp_distrib],
  simp only [forall_and_distrib],
  simp only [forall_eq],

end

#check @propext
#check @list.forall_mem_cons'
#check @or_imp_distrib
#check @forall_and_distrib
#check @forall_eq
#print axioms list.forall_mem_cons'
#print axioms or_imp_distrib
#print axioms forall_and_distrib
#print axioms forall_eq

#exit
example : ∀ n ∈ [0], foo n :=
by tactic.exact_dec_trivial

#exit

instance bar {α} {p : α → Prop} [decidable_pred p]
  (l : list α) : decidable (∀ x ∈ l, p x) :=
decidable_of_iff _ list.all_iff_forall_prop
--

#exit

def quux (n : nat) : nat :=
begin
  apply n
end

example : 0 = quux 0 := rfl

#exit
import tactic.basic

run_cmd mk_simp_attr `fol

meta def simp_fol := `[simp only with fol]

@[derive has_reflect]
inductive term : Type
| var : nat → term
| fnc : nat → term
| app : term → term → term

notation  `#` := term.var
notation  `&` := term.fnc
notation  t1 `^*` t2 := term.app t1 t2

@[derive has_reflect]
inductive form : Type
| false : form
| true : form
| prd : nat → list term → form
| not : form → form
| or  : form → form → form
| and : form → form → form
| fa  : form → form
| ex  : form → form

notation  `⊤*` := form.true
notation  `⊥*` := form.false
notation  p `**` ts := form.prd p ts
notation  `¬*` := form.not
notation  p `∧*` q := form.and p q
notation  p `∨*` q := form.or p q
notation  `∀*` := form.fa
notation  `∃*` := form.ex

@[fol] def term.max_idx_lt (k) : term → Prop
| (# m)      := m < k
| (& _)      := true
| (t1 ^* t2) := t1.max_idx_lt ∧ t2.max_idx_lt

instance dec_max_idx_lt : ∀ k t, decidable (term.max_idx_lt k t) :=
begin
  intros k t, induction t;
  simp_fol, repeat {apply_instance},
  apply @and.decidable _ _ _ _; assumption
end

@[fol] def form.max_idx_lt : nat → form → Prop
| k ⊥*        := _root_.true
| k ⊤*        := _root_.true
| k (m ** ts) := ∀ t ∈ ts, term.max_idx_lt k t
| k (¬* p)    := p.max_idx_lt k
| k (p ∨* q)  := p.max_idx_lt k ∧ q.max_idx_lt k
| k (p ∧* q)  := p.max_idx_lt k ∧ q.max_idx_lt k
| k (∀* p)    := p.max_idx_lt (k+1)
| k (∃* p)    := p.max_idx_lt (k+1)

@[fol] def closed (p : form) : Prop := p.max_idx_lt 0

open tactic

meta def induce (t : tactic unit := skip) : tactic unit :=
`[ intro p, induction p with k ts p ih p q ihp ihq p q ihp ihq p ih p ih; t ]

instance form.dec_max_idx_lt : ∀ p : form, ∀ k, decidable (p.max_idx_lt k) :=
begin
  induce `[intro k, try {simp_fol}, try {apply_instance},
    try {apply @and.decidable _ _ _ _}, try {apply ih},
    try {apply ihp}, try {apply ihq}],
end

instance dec_closed (p : form) : decidable (closed p) := p.dec_max_idx_lt 0

example : closed (∃* (1 **[# 0])) :=
begin
  exact_dec_trivial
end


/-
Copyright (c) 2019 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot
-/
import topology.uniform_space.cauchy
import topology.uniform_space.separation

/-!
# Indexed product of uniform spaces
-/

noncomputable theory

open_locale uniformity topological_space

section
open filter uniform_space
universe u
variables {ι : Type*} (α : ι → Type u) [U : Πi, uniform_space (α i)]
include U

instance Pi.uniform_space : uniform_space (Πi, α i) :=
uniform_space.of_core_eq
  (⨅i, uniform_space.comap (λ a : Πi, α i, a i) (U i)).to_core
  Pi.topological_space $ eq.symm to_topological_space_infi

lemma Pi.uniformity :
  𝓤 (Π i, α i) = ⨅ i : ι, filter.comap (λ a, (a.1 i, a.2 i)) $ 𝓤 (α i) :=
infi_uniformity

lemma Pi.uniform_continuous_proj (i : ι) : uniform_continuous (λ (a : Π (i : ι), α i), a i) :=
begin
  rw uniform_continuous_iff,
  exact infi_le (λ j, uniform_space.comap (λ (a : Π (i : ι), α i), a j) (U j)) i
end

instance Pi.complete [∀ i, complete_space (α i)] : complete_space (Π i, α i) :=
⟨begin
  intros f hf,
  haveI := hf.1,
  have : ∀ i, ∃ x : α i, filter.map (λ a : Πi, α i, a i) f ≤ 𝓝 x,
  { intro i,
    have key : cauchy (map (λ (a : Π (i : ι), α i), a i) f),
      from hf.map (Pi.uniform_continuous_proj α i),
    exact cauchy_iff_exists_le_nhds.1 key },
  choose x hx using this,
  use x,
  rw [nhds_pi, le_infi_iff],
  exact λ i, map_le_iff_le_comap.mp (hx i),
end⟩

instance Pi.separated [∀ i, separated_space (α i)] : separated_space (Π i, α i) :=
separated_def.2 $ assume x y H,
begin
  ext i,
  apply eq_of_separated_of_uniform_continuous (Pi.uniform_continuous_proj α i),
  apply H,
end
end


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

! This file was ported from Lean 3 source module ring_theory.witt_vector.identities
! leanprover-community/mathlib commit 0798037604b2d91748f9b43925fb7570a5f3256c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.RingTheory.WittVector.Frobenius
import Mathbin.RingTheory.WittVector.Verschiebung
import Mathbin.RingTheory.WittVector.MulP

/-!
## Identities between operations on the ring of Witt vectors

In this file we derive common identities between the Frobenius and Verschiebung operators.

## Main declarations

* `frobenius_verschiebung`: the composition of Frobenius and Verschiebung is multiplication by `p`
* `verschiebung_mul_frobenius`: the “projection formula”: `V(x * F y) = V x * y`
* `iterate_verschiebung_mul_coeff`: an identity from [Haze09] 6.2

## References

* [Hazewinkel, *Witt Vectors*][Haze09]

* [Commelin and Lewis, *Formalizing the Ring of Witt Vectors*][CL21]
-/

namespace WittVector

variable {p : ℕ} {R : Type _} [hp : Fact p.Prime] [CommRing R]

-- mathport name: expr𝕎
local notation "𝕎" => WittVector p

-- type as `\bbW`
include hp

noncomputable section

/-- The composition of Frobenius and Verschiebung is multiplication by `p`. -/
theorem frobenius_verschiebung (x : 𝕎 R) : frobenius (verschiebung x) = x * p :=
  by
  ghost_calc x
  ghost_simp [mul_comm]
#align witt_vector.frobenius_verschiebung WittVector.frobenius_verschiebung

/-- Verschiebung is the same as multiplication by `p` on the ring of Witt vectors of `zmod p`. -/
theorem verschiebung_zMod (x : 𝕎 (ZMod p)) : verschiebung x = x * p := by
  rw [← frobenius_verschiebung, frobenius_zmodp]
#align witt_vector.verschiebung_zmod WittVector.verschiebung_zMod

variable (p R)

theorem coeff_p_pow [CharP R p] (i : ℕ) : (p ^ i : 𝕎 R).coeff i = 1 :=
  by
  induction' i with i h
  · simp only [one_coeff_zero, Ne.def, pow_zero]
  ·
    rw [pow_succ', ← frobenius_verschiebung, coeff_frobenius_char_p, verschiebung_coeff_succ, h,
      one_pow]
#align witt_vector.coeff_p_pow WittVector.coeff_p_pow

theorem coeff_p_pow_eq_zero [CharP R p] {i j : ℕ} (hj : j ≠ i) : (p ^ i : 𝕎 R).coeff j = 0 :=
  by
  induction' i with i hi generalizing j
  · rw [pow_zero, one_coeff_eq_of_pos]
    exact Nat.pos_of_ne_zero hj
  · rw [pow_succ', ← frobenius_verschiebung, coeff_frobenius_char_p]
    cases j
    · rw [verschiebung_coeff_zero, zero_pow]
      exact Nat.Prime.pos hp.out
    · rw [verschiebung_coeff_succ, hi, zero_pow]
      · exact Nat.Prime.pos hp.out
      · exact ne_of_apply_ne (fun j : ℕ => j.succ) hj
#align witt_vector.coeff_p_pow_eq_zero WittVector.coeff_p_pow_eq_zero

theorem coeff_p [CharP R p] (i : ℕ) : (p : 𝕎 R).coeff i = if i = 1 then 1 else 0 :=
  by
  split_ifs with hi
  · simpa only [hi, pow_one] using coeff_p_pow p R 1
  · simpa only [pow_one] using coeff_p_pow_eq_zero p R hi
#align witt_vector.coeff_p WittVector.coeff_p

@[simp]
theorem coeff_p_zero [CharP R p] : (p : 𝕎 R).coeff 0 = 0 :=
  by
  rw [coeff_p, if_neg]
  exact zero_ne_one
#align witt_vector.coeff_p_zero WittVector.coeff_p_zero

@[simp]
theorem coeff_p_one [CharP R p] : (p : 𝕎 R).coeff 1 = 1 := by rw [coeff_p, if_pos rfl]
#align witt_vector.coeff_p_one WittVector.coeff_p_one

theorem p_nonzero [Nontrivial R] [CharP R p] : (p : 𝕎 R) ≠ 0 :=
  by
  intro h
  simpa only [h, zero_coeff, zero_ne_one] using coeff_p_one p R
#align witt_vector.p_nonzero WittVector.p_nonzero

theorem FractionRing.p_nonzero [Nontrivial R] [CharP R p] : (p : FractionRing (𝕎 R)) ≠ 0 := by
  simpa using (IsFractionRing.injective (𝕎 R) (FractionRing (𝕎 R))).Ne (p_nonzero _ _)
#align witt_vector.fraction_ring.p_nonzero WittVector.FractionRing.p_nonzero

variable {p R}

/-- The “projection formula” for Frobenius and Verschiebung. -/
theorem verschiebung_mul_frobenius (x y : 𝕎 R) :
    verschiebung (x * frobenius y) = verschiebung x * y :=
  by
  ghost_calc x y
  rintro ⟨⟩ <;> ghost_simp [mul_assoc]
#align witt_vector.verschiebung_mul_frobenius WittVector.verschiebung_mul_frobenius

theorem mul_charP_coeff_zero [CharP R p] (x : 𝕎 R) : (x * p).coeff 0 = 0 :=
  by
  rw [← frobenius_verschiebung, coeff_frobenius_char_p, verschiebung_coeff_zero, zero_pow]
  exact Nat.Prime.pos hp.out
#align witt_vector.mul_char_p_coeff_zero WittVector.mul_charP_coeff_zero

theorem mul_charP_coeff_succ [CharP R p] (x : 𝕎 R) (i : ℕ) :
    (x * p).coeff (i + 1) = x.coeff i ^ p := by
  rw [← frobenius_verschiebung, coeff_frobenius_char_p, verschiebung_coeff_succ]
#align witt_vector.mul_char_p_coeff_succ WittVector.mul_charP_coeff_succ

theorem verschiebung_frobenius [CharP R p] (x : 𝕎 R) : verschiebung (frobenius x) = x * p :=
  by
  ext ⟨i⟩
  · rw [mul_char_p_coeff_zero, verschiebung_coeff_zero]
  · rw [mul_char_p_coeff_succ, verschiebung_coeff_succ, coeff_frobenius_char_p]
#align witt_vector.verschiebung_frobenius WittVector.verschiebung_frobenius

theorem verschiebung_frobenius_comm [CharP R p] :
    Function.Commute (verschiebung : 𝕎 R → 𝕎 R) frobenius := fun x => by
  rw [verschiebung_frobenius, frobenius_verschiebung]
#align witt_vector.verschiebung_frobenius_comm WittVector.verschiebung_frobenius_comm

/-!
## Iteration lemmas
-/

open Function

theorem iterate_verschiebung_coeff (x : 𝕎 R) (n k : ℕ) :
    ((verschiebung^[n]) x).coeff (k + n) = x.coeff k :=
  by
  induction' n with k ih
  · simp
  · rw [iterate_succ_apply', Nat.add_succ, verschiebung_coeff_succ]
    exact ih
#align witt_vector.iterate_verschiebung_coeff WittVector.iterate_verschiebung_coeff

theorem iterate_verschiebung_mul_left (x y : 𝕎 R) (i : ℕ) :
    (verschiebung^[i]) x * y = (verschiebung^[i]) (x * (frobenius^[i]) y) :=
  by
  induction' i with i ih generalizing y
  · simp
  · rw [iterate_succ_apply', ← verschiebung_mul_frobenius, ih, iterate_succ_apply']
    rfl
#align witt_vector.iterate_verschiebung_mul_left WittVector.iterate_verschiebung_mul_left

section CharP

variable [CharP R p]

theorem iterate_verschiebung_mul (x y : 𝕎 R) (i j : ℕ) :
    (verschiebung^[i]) x * (verschiebung^[j]) y =
      (verschiebung^[i + j]) ((frobenius^[j]) x * (frobenius^[i]) y) :=
  by
  calc
    _ = (verschiebung^[i]) (x * (frobenius^[i]) ((verschiebung^[j]) y)) := _
    _ = (verschiebung^[i]) (x * (verschiebung^[j]) ((frobenius^[i]) y)) := _
    _ = (verschiebung^[i]) ((verschiebung^[j]) ((frobenius^[i]) y) * x) := _
    _ = (verschiebung^[i]) ((verschiebung^[j]) ((frobenius^[i]) y * (frobenius^[j]) x)) := _
    _ = (verschiebung^[i + j]) ((frobenius^[i]) y * (frobenius^[j]) x) := _
    _ = _ := _

  · apply iterate_verschiebung_mul_left
  · rw [verschiebung_frobenius_comm.iterate_iterate] <;> infer_instance
  · rw [mul_comm]
  · rw [iterate_verschiebung_mul_left]
  · rw [iterate_add_apply]
  · rw [mul_comm]
#align witt_vector.iterate_verschiebung_mul WittVector.iterate_verschiebung_mul

theorem iterate_frobenius_coeff (x : 𝕎 R) (i k : ℕ) :
    ((frobenius^[i]) x).coeff k = x.coeff k ^ p ^ i :=
  by
  induction' i with i ih
  · simp
  · rw [iterate_succ_apply', coeff_frobenius_char_p, ih]
    ring
#align witt_vector.iterate_frobenius_coeff WittVector.iterate_frobenius_coeff

/-- This is a slightly specialized form of [Hazewinkel, *Witt Vectors*][Haze09] 6.2 equation 5. -/
theorem iterate_verschiebung_mul_coeff (x y : 𝕎 R) (i j : ℕ) :
    ((verschiebung^[i]) x * (verschiebung^[j]) y).coeff (i + j) =
      x.coeff 0 ^ p ^ j * y.coeff 0 ^ p ^ i :=
  by
  calc
    _ = ((verschiebung^[i + j]) ((frobenius^[j]) x * (frobenius^[i]) y)).coeff (i + j) := _
    _ = ((frobenius^[j]) x * (frobenius^[i]) y).coeff 0 := _
    _ = ((frobenius^[j]) x).coeff 0 * ((frobenius^[i]) y).coeff 0 := _
    _ = _ := _

  · rw [iterate_verschiebung_mul]
  · convert iterate_verschiebung_coeff _ _ _ using 2
    rw [zero_add]
  · apply mul_coeff_zero
  · simp only [iterate_frobenius_coeff]
#align witt_vector.iterate_verschiebung_mul_coeff WittVector.iterate_verschiebung_mul_coeff

end CharP

end WittVector