l3lab/ntp-mathlib huggingface dataset:
/-- Given a morphism `f : X ⟶ Y`, we can take morphisms over `Y` to morphisms over `X` via
pullbacks. This is right adjoint to `over.map` (TODO) -/
@[simps! (config := { simpRhs := true}) obj_left obj_hom map_left]
def baseChange [HasPullbacks C] {X Y : C} (f : X ⟶ Y) : Over Y ⥤ Over X where
  obj g := Over.mk (pullback.snd : pullback g.hom f ⟶ _)
  map i := Over.homMk (pullback.map _ _ _ _ i.left (𝟙 _) (𝟙 _) (by simp) (by simp))
  map_id Z := by
    apply Over.OverMorphism.ext; apply pullback.hom_ext
    · dsimp; simp
    · dsimp; simp
  map_comp f g := by
    apply Over.OverMorphism.ext; apply pullback.hom_ext
    · dsimp; 

Proof state:
case h.h₀
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W X✝¹ Y✝¹ Z : C
inst✝ : HasPullbacks C
X Y : C
f✝ : X ⟶ Y
X✝ Y✝ Z✝ : Over Y
f : X✝ ⟶ Y✝
g : Y✝ ⟶ Z✝
⊢ pullback.map X✝.hom f✝ Z✝.hom f✝ (f.left ≫ g.left) (𝟙 X) (𝟙 Y) (_ : X✝.hom ≫ 𝟙 Y = (f.left ≫ g.left) ≫ Z✝.hom)
        (_ : f✝ ≫ 𝟙 Y = 𝟙 X ≫ f✝) ≫
      pullback.fst =
    (pullback.map X✝.hom f✝ Y✝.hom f✝ f.left (𝟙 X) (𝟙 Y) (_ : X✝.hom ≫ 𝟙 Y = f.left ≫ Y✝.hom)
          (_ : f✝ ≫ 𝟙 Y = 𝟙 X ≫ f✝) ≫
        pullback.map Y✝.hom f✝ Z✝.hom f✝ g.left (𝟙 X) (𝟙 Y) (_ : Y✝.hom ≫ 𝟙 Y = g.left ≫ Z✝.hom)
          (_ : f✝ ≫ 𝟙 Y = 𝟙 X ≫ f✝)) ≫
      pullback.fst

Next tactic:
simp
/-- Given a morphism `f : X ⟶ Y`, we can take morphisms over `Y` to morphisms over `X` via
pullbacks. This is right adjoint to `over.map` (TODO) -/
@[simps! (config := { simpRhs := true}) obj_left obj_hom map_left]
def baseChange [HasPullbacks C] {X Y : C} (f : X ⟶ Y) : Over Y ⥤ Over X where
  obj g := Over.mk (pullback.snd : pullback g.hom f ⟶ _)
  map i := Over.homMk (pullback.map _ _ _ _ i.left (𝟙 _) (𝟙 _) (by simp) (by simp))
  map_id Z := by
    apply Over.OverMorphism.ext; apply pullback.hom_ext
    · dsimp; simp
    · dsimp; simp
  map_comp f g := by
    apply Over.OverMorphism.ext; apply pullback.hom_ext
    · dsimp; simp
    · 

Proof state:
case h.h₁
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W X✝¹ Y✝¹ Z : C
inst✝ : HasPullbacks C
X Y : C
f✝ : X ⟶ Y
X✝ Y✝ Z✝ : Over Y
f : X✝ ⟶ Y✝
g : Y✝ ⟶ Z✝
⊢ ({ obj := fun g => Over.mk pullback.snd,
              map := fun {X_1 Y_1} i =>
                Over.homMk
                  (pullback.map X_1.hom f✝ Y_1.hom f✝ i.left (𝟙 X) (𝟙 ((Functor.fromPUnit Y).obj X_1.right))
                    (_ : X_1.hom ≫ 𝟙 Y = i.left ≫ Y_1.hom) (_ : f✝ ≫ 𝟙 Y = 𝟙 X ≫ f✝)) }.map
          (f ≫ g)).left ≫
      pullback.snd =
    ({ obj := fun g => Over.mk pullback.snd,
                map := fun {X_1 Y_1} i =>
                  Over.homMk
                    (pullback.map X_1.hom f✝ Y_1.hom f✝ i.left (𝟙 X) (𝟙 ((Functor.fromPUnit Y).obj X_1.right))
                      (_ : X_1.hom ≫ 𝟙 Y = i.left ≫ Y_1.hom) (_ : f✝ ≫ 𝟙 Y = 𝟙 X ≫ f✝)) }.map
            f ≫
          { obj := fun g => Over.mk pullback.snd,
                map := fun {X_1 Y_1} i =>
                  Over.homMk
                    (pullback.map X_1.hom f✝ Y_1.hom f✝ i.left (𝟙 X) (𝟙 ((Functor.fromPUnit Y).obj X_1.right))
                      (_ : X_1.hom ≫ 𝟙 Y = i.left ≫ Y_1.hom) (_ : f✝ ≫ 𝟙 Y = 𝟙 X ≫ f✝)) }.map
            g).left ≫
      pullback.snd

Next tactic:
dsimp
/-- Given a morphism `f : X ⟶ Y`, we can take morphisms over `Y` to morphisms over `X` via
pullbacks. This is right adjoint to `over.map` (TODO) -/
@[simps! (config := { simpRhs := true}) obj_left obj_hom map_left]
def baseChange [HasPullbacks C] {X Y : C} (f : X ⟶ Y) : Over Y ⥤ Over X where
  obj g := Over.mk (pullback.snd : pullback g.hom f ⟶ _)
  map i := Over.homMk (pullback.map _ _ _ _ i.left (𝟙 _) (𝟙 _) (by simp) (by simp))
  map_id Z := by
    apply Over.OverMorphism.ext; apply pullback.hom_ext
    · dsimp; simp
    · dsimp; simp
  map_comp f g := by
    apply Over.OverMorphism.ext; apply pullback.hom_ext
    · dsimp; simp
    · dsimp; 

Proof state:
case h.h₁
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W X✝¹ Y✝¹ Z : C
inst✝ : HasPullbacks C
X Y : C
f✝ : X ⟶ Y
X✝ Y✝ Z✝ : Over Y
f : X✝ ⟶ Y✝
g : Y✝ ⟶ Z✝
⊢ pullback.map X✝.hom f✝ Z✝.hom f✝ (f.left ≫ g.left) (𝟙 X) (𝟙 Y) (_ : X✝.hom ≫ 𝟙 Y = (f.left ≫ g.left) ≫ Z✝.hom)
        (_ : f✝ ≫ 𝟙 Y = 𝟙 X ≫ f✝) ≫
      pullback.snd =
    (pullback.map X✝.hom f✝ Y✝.hom f✝ f.left (𝟙 X) (𝟙 Y) (_ : X✝.hom ≫ 𝟙 Y = f.left ≫ Y✝.hom)
          (_ : f✝ ≫ 𝟙 Y = 𝟙 X ≫ f✝) ≫
        pullback.map Y✝.hom f✝ Z✝.hom f✝ g.left (𝟙 X) (𝟙 Y) (_ : Y✝.hom ≫ 𝟙 Y = g.left ≫ Z✝.hom)
          (_ : f✝ ≫ 𝟙 Y = 𝟙 X ≫ f✝)) ≫
      pullback.snd

Next tactic:
simp
/-- If a type carries two unital binary operations that distribute over each other,
then they have the same unit elements.

In fact, the two operations are the same, and give a commutative monoid structure,
see `eckmann_hilton.CommMonoid`. -/
theorem one : e₁ = e₂ := by
  

Proof state:
X : Type u
m₁ m₂ : X → X → X
e₁ e₂ : X
h₁ : IsUnital m₁ e₁
h₂ : IsUnital m₂ e₂
distrib : ∀ (a b c d : X), m₁ (m₂ a b) (m₂ c d) = m₂ (m₁ a c) (m₁ b d)
⊢ e₁ = e₂

Next tactic:
simpa only [h₁.left_id, h₁.right_id, h₂.left_id, h₂.right_id] using distrib e₂ e₁ e₁ e₂
/-- If a type carries two unital binary operations that distribute over each other,
then these operations are equal.

In fact, they give a commutative monoid structure, see `eckmann_hilton.CommMonoid`. -/
theorem mul : m₁ = m₂ := by
  

Proof state:
X : Type u
m₁ m₂ : X → X → X
e₁ e₂ : X
h₁ : IsUnital m₁ e₁
h₂ : IsUnital m₂ e₂
distrib : ∀ (a b c d : X), m₁ (m₂ a b) (m₂ c d) = m₂ (m₁ a c) (m₁ b d)
⊢ m₁ = m₂

Next tactic:
funext a b
/-- If a type carries two unital binary operations that distribute over each other,
then these operations are equal.

In fact, they give a commutative monoid structure, see `eckmann_hilton.CommMonoid`. -/
theorem mul : m₁ = m₂ := by
  funext a b
  

Proof state:
case h.h
X : Type u
m₁ m₂ : X → X → X
e₁ e₂ : X
h₁ : IsUnital m₁ e₁
h₂ : IsUnital m₂ e₂
distrib : ∀ (a b c d : X), m₁ (m₂ a b) (m₂ c d) = m₂ (m₁ a c) (m₁ b d)
a b : X
⊢ m₁ a b = m₂ a b

Next tactic:
calc
    m₁ a b = m₁ (m₂ a e₁) (m₂ e₁ b) := by
      { simp only [one h₁ h₂ distrib, h₁.left_id, h₁.right_id, h₂.left_id, h₂.right_id] }
    _ = m₂ a b := by simp only [distrib, h₁.left_id, h₁.right_id, h₂.left_id, h₂.right_id]
/-- If a type carries two unital binary operations that distribute over each other,
then these operations are equal.

In fact, they give a commutative monoid structure, see `eckmann_hilton.CommMonoid`. -/
theorem mul : m₁ = m₂ := by
  funext a b
  calc
    m₁ a b = m₁ (m₂ a e₁) (m₂ e₁ b) := by
      

Proof state:
X : Type u
m₁ m₂ : X → X → X
e₁ e₂ : X
h₁ : IsUnital m₁ e₁
h₂ : IsUnital m₂ e₂
distrib : ∀ (a b c d : X), m₁ (m₂ a b) (m₂ c d) = m₂ (m₁ a c) (m₁ b d)
a b : X
⊢ m₁ a b = m₁ (m₂ a e₁) (m₂ e₁ b)

Next tactic:
{ simp only [one h₁ h₂ distrib, h₁.left_id, h₁.right_id, h₂.left_id, h₂.right_id] }
/-- If a type carries two unital binary operations that distribute over each other,
then these operations are equal.

In fact, they give a commutative monoid structure, see `eckmann_hilton.CommMonoid`. -/
theorem mul : m₁ = m₂ := by
  funext a b
  calc
    m₁ a b = m₁ (m₂ a e₁) (m₂ e₁ b) := by
      { 

Proof state:
X : Type u
m₁ m₂ : X → X → X
e₁ e₂ : X
h₁ : IsUnital m₁ e₁
h₂ : IsUnital m₂ e₂
distrib : ∀ (a b c d : X), m₁ (m₂ a b) (m₂ c d) = m₂ (m₁ a c) (m₁ b d)
a b : X
⊢ m₁ a b = m₁ (m₂ a e₁) (m₂ e₁ b)

Next tactic:
simp only [one h₁ h₂ distrib, h₁.left_id, h₁.right_id, h₂.left_id, h₂.right_id]
/-- If a type carries two unital binary operations that distribute over each other,
then these operations are equal.

In fact, they give a commutative monoid structure, see `eckmann_hilton.CommMonoid`. -/
theorem mul : m₁ = m₂ := by
  funext a b
  calc
    m₁ a b = m₁ (m₂ a e₁) (m₂ e₁ b) := by
      { simp only [one h₁ h₂ distrib, h₁.left_id, h₁.right_id, h₂.left_id, h₂.right_id] }
    _ = m₂ a b := by 

Proof state:
X : Type u
m₁ m₂ : X → X → X
e₁ e₂ : X
h₁ : IsUnital m₁ e₁
h₂ : IsUnital m₂ e₂
distrib : ∀ (a b c d : X), m₁ (m₂ a b) (m₂ c d) = m₂ (m₁ a c) (m₁ b d)
a b : X
⊢ m₁ (m₂ a e₁) (m₂ e₁ b) = m₂ a b

Next tactic:
simp only [distrib, h₁.left_id, h₁.right_id, h₂.left_id, h₂.right_id]
/-- If a type carries two unital binary operations that distribute over each other,
then these operations are commutative.

In fact, they give a commutative monoid structure, see `eckmann_hilton.CommMonoid`. -/
theorem mul_comm : IsCommutative _ m₂ :=
  ⟨fun a b => by 

Proof state:
X : Type u
m₁ m₂ : X → X → X
e₁ e₂ : X
h₁ : IsUnital m₁ e₁
h₂ : IsUnital m₂ e₂
distrib : ∀ (a b c d : X), m₁ (m₂ a b) (m₂ c d) = m₂ (m₁ a c) (m₁ b d)
a b : X
⊢ m₂ a b = m₂ b a

Next tactic:
simpa [mul h₁ h₂ distrib, h₂.left_id, h₂.right_id] using distrib e₂ a b e₂
/-- If a type carries two unital binary operations that distribute over each other,
then these operations are associative.

In fact, they give a commutative monoid structure, see `eckmann_hilton.CommMonoid`. -/
theorem mul_assoc : IsAssociative _ m₂ :=
  ⟨fun a b c => by 

Proof state:
X : Type u
m₁ m₂ : X → X → X
e₁ e₂ : X
h₁ : IsUnital m₁ e₁
h₂ : IsUnital m₂ e₂
distrib : ∀ (a b c d : X), m₁ (m₂ a b) (m₂ c d) = m₂ (m₁ a c) (m₁ b d)
a b c : X
⊢ m₂ (m₂ a b) c = m₂ a (m₂ b c)

Next tactic:
simpa [mul h₁ h₂ distrib, h₂.left_id, h₂.right_id] using distrib a b e₂ c
@[reassoc (attr := simp)]
lemma ι_mapBifunctorMapMap {X₁ X₂ : GradedObject I C₁} (f : X₁ ⟶ X₂)
    {Y₁ Y₂ : GradedObject J C₂} (g : Y₁ ⟶ Y₂)
    [HasMap (((mapBifunctor F I J).obj X₁).obj Y₁) p]
    [HasMap (((mapBifunctor F I J).obj X₂).obj Y₂) p]
    (i : I) (j : J) (k : K) (h : p ⟨i, j⟩ = k) :
    ιMapBifunctorMapObj F p X₁ Y₁ i j k h ≫ mapBifunctorMapMap F p f g k =
      (F.map (f i)).app (Y₁ j) ≫ (F.obj (X₂ i)).map (g j) ≫
        ιMapBifunctorMapObj F p X₂ Y₂ i j k h := by
  

Proof state:
C₁ : Type u_1
C₂ : Type u_2
C₃ : Type u_3
inst✝⁴ : Category.{u_7, u_1} C₁
inst✝³ : Category.{u_8, u_2} C₂
inst✝² : Category.{u_9, u_3} C₃
F : C₁ ⥤ C₂ ⥤ C₃
I : Type u_4
J : Type u_5
K : Type u_6
p : I × J → K
X₁ X₂ : GradedObject I C₁
f : X₁ ⟶ X₂
Y₁ Y₂ : GradedObject J C₂
g : Y₁ ⟶ Y₂
inst✝¹ : HasMap (((mapBifunctor F I J).obj X₁).obj Y₁) p
inst✝ : HasMap (((mapBifunctor F I J).obj X₂).obj Y₂) p
i : I
j : J
k : K
h : p (i, j) = k
⊢ ιMapBifunctorMapObj F p X₁ Y₁ i j k h ≫ mapBifunctorMapMap F p f g k =
    (F.map (f i)).app (Y₁ j) ≫ (F.obj (X₂ i)).map (g j) ≫ ιMapBifunctorMapObj F p X₂ Y₂ i j k h

Next tactic:
simp [ιMapBifunctorMapObj, mapBifunctorMapMap]
/-- Given a bifunctor `F : C₁ ⥤ C₂ ⥤ C₃` and a map `p : I × J → K`, this is the
functor `GradedObject I C₁ ⥤ GradedObject J C₂ ⥤ GradedObject K C₃` sending
`X : GradedObject I C₁` and `Y : GradedObject J C₂` to the `K`-graded object sending
`k` to the coproduct of `(F.obj (X i)).obj (Y j)` for `p ⟨i, j⟩ = k`. -/
@[simps]
noncomputable def mapBifunctorMap [∀ X Y, HasMap (((mapBifunctor F I J).obj X).obj Y) p] :
    GradedObject I C₁ ⥤ GradedObject J C₂ ⥤ GradedObject K C₃ where
  obj X :=
    { obj := fun Y => mapBifunctorMapObj F p X Y
      map := fun ψ => mapBifunctorMapMap F p (𝟙 X) ψ }
  map {X₁ X₂} φ :=
    { app := fun Y => mapBifunctorMapMap F p φ (𝟙 Y)
      naturality := fun {Y₁ Y₂} ψ => by
        

Proof state:
C₁ : Type u_1
C₂ : Type u_2
C₃ : Type u_3
inst✝³ : Category.{?u.46193, u_1} C₁
inst✝² : Category.{?u.46197, u_2} C₂
inst✝¹ : Category.{?u.46201, u_3} C₃
F : C₁ ⥤ C₂ ⥤ C₃
I : Type u_4
J : Type u_5
K : Type u_6
p : I × J → K
inst✝ : ∀ (X : GradedObject I C₁) (Y : GradedObject J C₂), HasMap (((mapBifunctor F I J).obj X).obj Y) p
X₁ X₂ : GradedObject I C₁
φ : X₁ ⟶ X₂
Y₁ Y₂ : GradedObject J C₂
ψ : Y₁ ⟶ Y₂
⊢ ((fun X =>
              Functor.mk
                { obj := fun Y => mapBifunctorMapObj F p X Y, map := fun {X_1 Y} ψ => mapBifunctorMapMap F p (𝟙 X) ψ })
            X₁).map
        ψ ≫
      (fun Y => mapBifunctorMapMap F p φ (𝟙 Y)) Y₂ =
    (fun Y => mapBifunctorMapMap F p φ (𝟙 Y)) Y₁ ≫
      ((fun X =>
              Functor.mk
                { obj := fun Y => mapBifunctorMapObj F p X Y, map := fun {X_1 Y} ψ => mapBifunctorMapMap F p (𝟙 X) ψ })
            X₂).map
        ψ

Next tactic:
dsimp
/-- Given a bifunctor `F : C₁ ⥤ C₂ ⥤ C₃` and a map `p : I × J → K`, this is the
functor `GradedObject I C₁ ⥤ GradedObject J C₂ ⥤ GradedObject K C₃` sending
`X : GradedObject I C₁` and `Y : GradedObject J C₂` to the `K`-graded object sending
`k` to the coproduct of `(F.obj (X i)).obj (Y j)` for `p ⟨i, j⟩ = k`. -/
@[simps]
noncomputable def mapBifunctorMap [∀ X Y, HasMap (((mapBifunctor F I J).obj X).obj Y) p] :
    GradedObject I C₁ ⥤ GradedObject J C₂ ⥤ GradedObject K C₃ where
  obj X :=
    { obj := fun Y => mapBifunctorMapObj F p X Y
      map := fun ψ => mapBifunctorMapMap F p (𝟙 X) ψ }
  map {X₁ X₂} φ :=
    { app := fun Y => mapBifunctorMapMap F p φ (𝟙 Y)
      naturality := fun {Y₁ Y₂} ψ => by
        dsimp
        

Proof state:
C₁ : Type u_1
C₂ : Type u_2
C₃ : Type u_3
inst✝³ : Category.{?u.46193, u_1} C₁
inst✝² : Category.{?u.46197, u_2} C₂
inst✝¹ : Category.{?u.46201, u_3} C₃
F : C₁ ⥤ C₂ ⥤ C₃
I : Type u_4
J : Type u_5
K : Type u_6
p : I × J → K
inst✝ : ∀ (X : GradedObject I C₁) (Y : GradedObject J C₂), HasMap (((mapBifunctor F I J).obj X).obj Y) p
X₁ X₂ : GradedObject I C₁
φ : X₁ ⟶ X₂
Y₁ Y₂ : GradedObject J C₂
ψ : Y₁ ⟶ Y₂
⊢ mapMap (((mapBifunctor F I J).map (𝟙 X₁)).app Y₁ ≫ ((mapBifunctor F I J).obj X₁).map ψ) p ≫
      mapMap (((mapBifunctor F I J).map φ).app Y₂ ≫ ((mapBifunctor F I J).obj X₂).map (𝟙 Y₂)) p =
    mapMap (((mapBifunctor F I J).map φ).app Y₁ ≫ ((mapBifunctor F I J).obj X₂).map (𝟙 Y₁)) p ≫
      mapMap (((mapBifunctor F I J).map (𝟙 X₂)).app Y₁ ≫ ((mapBifunctor F I J).obj X₂).map ψ) p

Next tactic:
simp only [Functor.map_id, NatTrans.id_app, id_comp, comp_id,
          ← mapMap_comp, NatTrans.naturality]
theorem comap_leq_ker_subToSupQuotient (p p' : Submodule R M) :
    comap (Submodule.subtype p) (p ⊓ p') ≤ ker (subToSupQuotient p p') := by
  

Proof state:
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
⊢ comap (Submodule.subtype p) (p ⊓ p') ≤ ker (subToSupQuotient p p')

Next tactic:
rw [LinearMap.ker_comp, Submodule.inclusion, comap_codRestrict, ker_mkQ, map_comap_subtype]
theorem comap_leq_ker_subToSupQuotient (p p' : Submodule R M) :
    comap (Submodule.subtype p) (p ⊓ p') ≤ ker (subToSupQuotient p p') := by
  rw [LinearMap.ker_comp, Submodule.inclusion, comap_codRestrict, ker_mkQ, map_comap_subtype]
  

Proof state:
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
⊢ comap (Submodule.subtype p) (p ⊓ p') ≤ comap (Submodule.subtype p) ((p ⊔ p') ⊓ p')

Next tactic:
exact comap_mono (inf_le_inf_right _ le_sup_left)
theorem quotientInfEquivSupQuotient_injective (p p' : Submodule R M) :
    Function.Injective (quotientInfToSupQuotient p p') := by
  

Proof state:
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
⊢ Function.Injective ⇑(quotientInfToSupQuotient p p')

Next tactic:
rw [← ker_eq_bot, quotientInfToSupQuotient, ker_liftQ_eq_bot]
theorem quotientInfEquivSupQuotient_injective (p p' : Submodule R M) :
    Function.Injective (quotientInfToSupQuotient p p') := by
  rw [← ker_eq_bot, quotientInfToSupQuotient, ker_liftQ_eq_bot]
  

Proof state:
case h'
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
⊢ ker (subToSupQuotient p p') ≤ comap (Submodule.subtype p) (p ⊓ p')

Next tactic:
rw [ker_comp, ker_mkQ]
theorem quotientInfEquivSupQuotient_injective (p p' : Submodule R M) :
    Function.Injective (quotientInfToSupQuotient p p') := by
  rw [← ker_eq_bot, quotientInfToSupQuotient, ker_liftQ_eq_bot]
  rw [ker_comp, ker_mkQ]
  

Proof state:
case h'
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
⊢ comap (inclusion (_ : p ≤ p ⊔ p')) (comap (Submodule.subtype (p ⊔ p')) p') ≤ comap (Submodule.subtype p) (p ⊓ p')

Next tactic:
exact fun ⟨x, hx1⟩ hx2 => ⟨hx1, hx2⟩
theorem quotientInfEquivSupQuotient_surjective (p p' : Submodule R M) :
    Function.Surjective (quotientInfToSupQuotient p p') := by
  

Proof state:
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
⊢ Function.Surjective ⇑(quotientInfToSupQuotient p p')

Next tactic:
rw [← range_eq_top, quotientInfToSupQuotient, range_liftQ, eq_top_iff']
theorem quotientInfEquivSupQuotient_surjective (p p' : Submodule R M) :
    Function.Surjective (quotientInfToSupQuotient p p') := by
  rw [← range_eq_top, quotientInfToSupQuotient, range_liftQ, eq_top_iff']
  

Proof state:
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
⊢ ∀ (x : ↥(p ⊔ p') ⧸ comap (Submodule.subtype (p ⊔ p')) p'), x ∈ range (subToSupQuotient p p')

Next tactic:
rintro ⟨x, hx⟩
theorem quotientInfEquivSupQuotient_surjective (p p' : Submodule R M) :
    Function.Surjective (quotientInfToSupQuotient p p') := by
  rw [← range_eq_top, quotientInfToSupQuotient, range_liftQ, eq_top_iff']
  rintro ⟨x, hx⟩; 

Proof state:
case mk.mk
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
x✝ : ↥(p ⊔ p') ⧸ comap (Submodule.subtype (p ⊔ p')) p'
x : M
hx : x ∈ p ⊔ p'
⊢ Quot.mk Setoid.r { val := x, property := hx } ∈ range (subToSupQuotient p p')

Next tactic:
rcases mem_sup.1 hx with ⟨y, hy, z, hz, rfl⟩
theorem quotientInfEquivSupQuotient_surjective (p p' : Submodule R M) :
    Function.Surjective (quotientInfToSupQuotient p p') := by
  rw [← range_eq_top, quotientInfToSupQuotient, range_liftQ, eq_top_iff']
  rintro ⟨x, hx⟩; rcases mem_sup.1 hx with ⟨y, hy, z, hz, rfl⟩
  

Proof state:
case mk.mk.intro.intro.intro.intro
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
x✝ : ↥(p ⊔ p') ⧸ comap (Submodule.subtype (p ⊔ p')) p'
y : M
hy : y ∈ p
z : M
hz : z ∈ p'
hx : y + z ∈ p ⊔ p'
⊢ Quot.mk Setoid.r { val := y + z, property := hx } ∈ range (subToSupQuotient p p')

Next tactic:
use ⟨y, hy⟩
theorem quotientInfEquivSupQuotient_surjective (p p' : Submodule R M) :
    Function.Surjective (quotientInfToSupQuotient p p') := by
  rw [← range_eq_top, quotientInfToSupQuotient, range_liftQ, eq_top_iff']
  rintro ⟨x, hx⟩; rcases mem_sup.1 hx with ⟨y, hy, z, hz, rfl⟩
  use ⟨y, hy⟩; 

Proof state:
case h
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
x✝ : ↥(p ⊔ p') ⧸ comap (Submodule.subtype (p ⊔ p')) p'
y : M
hy : y ∈ p
z : M
hz : z ∈ p'
hx : y + z ∈ p ⊔ p'
⊢ (subToSupQuotient p p') { val := y, property := hy } = Quot.mk Setoid.r { val := y + z, property := hx }

Next tactic:
apply (Submodule.Quotient.eq _).2
theorem quotientInfEquivSupQuotient_surjective (p p' : Submodule R M) :
    Function.Surjective (quotientInfToSupQuotient p p') := by
  rw [← range_eq_top, quotientInfToSupQuotient, range_liftQ, eq_top_iff']
  rintro ⟨x, hx⟩; rcases mem_sup.1 hx with ⟨y, hy, z, hz, rfl⟩
  use ⟨y, hy⟩; apply (Submodule.Quotient.eq _).2
  

Proof state:
case h
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
x✝ : ↥(p ⊔ p') ⧸ comap (Submodule.subtype (p ⊔ p')) p'
y : M
hy : y ∈ p
z : M
hz : z ∈ p'
hx : y + z ∈ p ⊔ p'
⊢ (inclusion (_ : p ≤ p ⊔ p')) { val := y, property := hy } - { val := y + z, property := hx } ∈
    comap (Submodule.subtype (p ⊔ p')) p'

Next tactic:
simp only [mem_comap, map_sub, coeSubtype, coe_inclusion, sub_add_cancel', neg_mem_iff, hz]
theorem quotientInfEquivSupQuotient_symm_apply_left (p p' : Submodule R M) (x : ↥(p ⊔ p'))
    (hx : (x : M) ∈ p) :
    (quotientInfEquivSupQuotient p p').symm (Submodule.Quotient.mk x) =
      Submodule.Quotient.mk ⟨x, hx⟩ :=
  (LinearEquiv.symm_apply_eq _).2 <| by
    -- Porting note: Was `simp`.
    

Proof state:
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
x : ↥(p ⊔ p')
hx : ↑x ∈ p
⊢ Submodule.Quotient.mk x = (quotientInfEquivSupQuotient p p') (Submodule.Quotient.mk { val := ↑x, property := hx })

Next tactic:
rw [quotientInfEquivSupQuotient_apply_mk, inclusion_apply]
theorem quotientInfEquivSupQuotient_symm_apply_eq_zero_iff {p p' : Submodule R M} {x : ↥(p ⊔ p')} :
    (quotientInfEquivSupQuotient p p').symm (Submodule.Quotient.mk x) = 0 ↔ (x : M) ∈ p' :=
  (LinearEquiv.symm_apply_eq _).trans <| by
    -- Porting note: Was `simp`.
    

Proof state:
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
p p' : Submodule R M
x : ↥(p ⊔ p')
⊢ Submodule.Quotient.mk x = (quotientInfEquivSupQuotient p p') 0 ↔ ↑x ∈ p'

Next tactic:
rw [_root_.map_zero, Quotient.mk_eq_zero, mem_comap, Submodule.coeSubtype]
/-- The map from the third isomorphism theorem for modules: `(M / S) / (T / S) → M / T`. -/
def quotientQuotientEquivQuotientAux (h : S ≤ T) : (M ⧸ S) ⧸ T.map S.mkQ →ₗ[R] M ⧸ T :=
  liftQ _ (mapQ S T LinearMap.id h)
    (by
      

Proof state:
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
S T : Submodule R M
h✝ h : S ≤ T
⊢ map (mkQ S) T ≤ LinearMap.ker (mapQ S T LinearMap.id h)

Next tactic:
rintro _ ⟨x, hx, rfl⟩
/-- The map from the third isomorphism theorem for modules: `(M / S) / (T / S) → M / T`. -/
def quotientQuotientEquivQuotientAux (h : S ≤ T) : (M ⧸ S) ⧸ T.map S.mkQ →ₗ[R] M ⧸ T :=
  liftQ _ (mapQ S T LinearMap.id h)
    (by
      rintro _ ⟨x, hx, rfl⟩
      

Proof state:
case intro.intro
R : Type u_1
M : Type u_2
M₂ : Type u_3
M₃ : Type u_4
inst✝⁶ : Ring R
inst✝⁵ : AddCommGroup M
inst✝⁴ : AddCommGroup M₂
inst✝³ : AddCommGroup M₃
inst✝² : Module R M
inst✝¹ : Module R M₂
inst✝ : Module R M₃
f : M →ₗ[R] M₂
S T : Submodule R M
h✝ h : S ≤ T
x : M
hx : x ∈ ↑T
⊢ (mkQ S) x ∈ LinearMap.ker (mapQ S T LinearMap.id h)

Next tactic:
rw [LinearMap.mem_ker, mkQ_apply, mapQ_apply]