l3lab/ntp-mathlib huggingface dataset:
@[to_additive]
theorem subsemigroupMap_surjective (f : M →ₙ* N) (M' : Subsemigroup M) :
    Function.Surjective (f.subsemigroupMap M') := by
  

Proof state:
M : Type u_1
N : Type u_2
P : Type u_3
σ : Type u_4
inst✝² : Mul M
inst✝¹ : Mul N
inst✝ : Mul P
S : Subsemigroup M
f : M →ₙ* N
M' : Subsemigroup M
⊢ Function.Surjective ⇑(subsemigroupMap f M')

Next tactic:
rintro ⟨_, x, hx, rfl⟩
@[to_additive]
theorem subsemigroupMap_surjective (f : M →ₙ* N) (M' : Subsemigroup M) :
    Function.Surjective (f.subsemigroupMap M') := by
  rintro ⟨_, x, hx, rfl⟩
  

Proof state:
case mk.intro.intro
M : Type u_1
N : Type u_2
P : Type u_3
σ : Type u_4
inst✝² : Mul M
inst✝¹ : Mul N
inst✝ : Mul P
S : Subsemigroup M
f : M →ₙ* N
M' : Subsemigroup M
x : M
hx : x ∈ ↑M'
⊢ ∃ a, (subsemigroupMap f M') a = { val := f x, property := (_ : ∃ a ∈ ↑M', f a = f x) }

Next tactic:
exact ⟨⟨x, hx⟩, rfl⟩
@[to_additive prod_eq_top_iff]
theorem prod_eq_top_iff [Nonempty M] [Nonempty N] {s : Subsemigroup M} {t : Subsemigroup N} :
    s.prod t = ⊤ ↔ s = ⊤ ∧ t = ⊤ := by
  

Proof state:
M : Type u_1
N : Type u_2
P : Type u_3
σ : Type u_4
inst✝⁴ : Mul M
inst✝³ : Mul N
inst✝² : Mul P
S : Subsemigroup M
inst✝¹ : Nonempty M
inst✝ : Nonempty N
s : Subsemigroup M
t : Subsemigroup N
⊢ prod s t = ⊤ ↔ s = ⊤ ∧ t = ⊤

Next tactic:
simp only [eq_top_iff, le_prod_iff, ← (gc_map_comap _).le_iff_le, ← srange_eq_map, srange_fst,
    srange_snd]
/-- A semigroup homomorphism `f : M →ₙ* N` with a left-inverse `g : N → M` defines a multiplicative
equivalence between `M` and `f.srange`.

This is a bidirectional version of `MulHom.srangeRestrict`. -/
@[to_additive (attr := simps (config := { simpRhs := true }))
      "An additive semigroup homomorphism `f : M →+ N` with a left-inverse
      `g : N → M` defines an additive equivalence between `M` and `f.srange`.
      This is a bidirectional version of `AddHom.srangeRestrict`. "]
def ofLeftInverse (f : M →ₙ* N) {g : N → M} (h : Function.LeftInverse g f) : M ≃* f.srange :=
  { f.srangeRestrict with
    toFun := f.srangeRestrict
    invFun := g ∘ MulMemClass.subtype f.srange
    left_inv := h
    right_inv := fun x =>
      Subtype.ext <|
        let ⟨x', hx'⟩ := MulHom.mem_srange.mp x.prop
        show f (g x) = x by 

Proof state:
M : Type u_1
N : Type u_2
P : Type u_3
σ : Type u_4
inst✝¹ : Mul M
inst✝ : Mul N
S T : Subsemigroup M
f : M →ₙ* N
g : N → M
h : Function.LeftInverse g ⇑f
src✝ : M →ₙ* ↥(MulHom.srange f) := MulHom.srangeRestrict f
x : ↥(MulHom.srange f)
x' : M
hx' : f x' = ↑x
⊢ f (g ↑x) = ↑x

Next tactic:
rw [← hx', h x']
/-- The map of a fan is a limit iff the fan consisting of the mapped morphisms is a limit. This
essentially lets us commute `Fan.mk` with `Functor.mapCone`.
-/
def isLimitMapConeFanMkEquiv {P : C} (g : ∀ j, P ⟶ f j) :
    IsLimit (Functor.mapCone G (Fan.mk P g)) ≃
      IsLimit (Fan.mk _ fun j => G.map (g j) : Fan fun j => G.obj (f j)) := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → P ⟶ f j
⊢ IsLimit (G.mapCone (Fan.mk P g)) ≃ IsLimit (Fan.mk (G.obj P) fun j => G.map (g j))

Next tactic:
refine' (IsLimit.postcomposeHomEquiv _ _).symm.trans (IsLimit.equivIsoLimit _)
/-- The map of a fan is a limit iff the fan consisting of the mapped morphisms is a limit. This
essentially lets us commute `Fan.mk` with `Functor.mapCone`.
-/
def isLimitMapConeFanMkEquiv {P : C} (g : ∀ j, P ⟶ f j) :
    IsLimit (Functor.mapCone G (Fan.mk P g)) ≃
      IsLimit (Fan.mk _ fun j => G.map (g j) : Fan fun j => G.obj (f j)) := by
  refine' (IsLimit.postcomposeHomEquiv _ _).symm.trans (IsLimit.equivIsoLimit _)
  

Proof state:
case refine'_1
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → P ⟶ f j
⊢ (Discrete.functor fun b => f b) ⋙ G ≅ Discrete.functor fun j => G.obj (f j)
case refine'_2
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → P ⟶ f j
⊢ (Cones.postcompose ?refine'_1.hom).obj (G.mapCone (Fan.mk P g)) ≅ Fan.mk (G.obj P) fun j => G.map (g j)

Next tactic:
refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
/-- The map of a fan is a limit iff the fan consisting of the mapped morphisms is a limit. This
essentially lets us commute `Fan.mk` with `Functor.mapCone`.
-/
def isLimitMapConeFanMkEquiv {P : C} (g : ∀ j, P ⟶ f j) :
    IsLimit (Functor.mapCone G (Fan.mk P g)) ≃
      IsLimit (Fan.mk _ fun j => G.map (g j) : Fan fun j => G.obj (f j)) := by
  refine' (IsLimit.postcomposeHomEquiv _ _).symm.trans (IsLimit.equivIsoLimit _)
  refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
  

Proof state:
case refine'_2
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → P ⟶ f j
⊢ (Cones.postcompose (Discrete.natIso fun j => Iso.refl (G.obj (f j.as))).hom).obj (G.mapCone (Fan.mk P g)) ≅
    Fan.mk (G.obj P) fun j => G.map (g j)

Next tactic:
refine' Cones.ext (Iso.refl _) fun j =>
      by dsimp; cases j; simp
/-- The map of a fan is a limit iff the fan consisting of the mapped morphisms is a limit. This
essentially lets us commute `Fan.mk` with `Functor.mapCone`.
-/
def isLimitMapConeFanMkEquiv {P : C} (g : ∀ j, P ⟶ f j) :
    IsLimit (Functor.mapCone G (Fan.mk P g)) ≃
      IsLimit (Fan.mk _ fun j => G.map (g j) : Fan fun j => G.obj (f j)) := by
  refine' (IsLimit.postcomposeHomEquiv _ _).symm.trans (IsLimit.equivIsoLimit _)
  refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
  refine' Cones.ext (Iso.refl _) fun j =>
      by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → P ⟶ f j
j : Discrete J
⊢ ((Cones.postcompose (Discrete.natIso fun j => Iso.refl (G.obj (f j.as))).hom).obj (G.mapCone (Fan.mk P g))).π.app j =
    (Iso.refl
          ((Cones.postcompose (Discrete.natIso fun j => Iso.refl (G.obj (f j.as))).hom).obj
              (G.mapCone (Fan.mk P g))).pt).hom ≫
      (Fan.mk (G.obj P) fun j => G.map (g j)).π.app j

Next tactic:
dsimp
/-- The map of a fan is a limit iff the fan consisting of the mapped morphisms is a limit. This
essentially lets us commute `Fan.mk` with `Functor.mapCone`.
-/
def isLimitMapConeFanMkEquiv {P : C} (g : ∀ j, P ⟶ f j) :
    IsLimit (Functor.mapCone G (Fan.mk P g)) ≃
      IsLimit (Fan.mk _ fun j => G.map (g j) : Fan fun j => G.obj (f j)) := by
  refine' (IsLimit.postcomposeHomEquiv _ _).symm.trans (IsLimit.equivIsoLimit _)
  refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
  refine' Cones.ext (Iso.refl _) fun j =>
      by dsimp; 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → P ⟶ f j
j : Discrete J
⊢ G.map (g j.as) ≫ 𝟙 (G.obj (f j.as)) = 𝟙 (G.obj P) ≫ G.map (g j.as)

Next tactic:
cases j
/-- The map of a fan is a limit iff the fan consisting of the mapped morphisms is a limit. This
essentially lets us commute `Fan.mk` with `Functor.mapCone`.
-/
def isLimitMapConeFanMkEquiv {P : C} (g : ∀ j, P ⟶ f j) :
    IsLimit (Functor.mapCone G (Fan.mk P g)) ≃
      IsLimit (Fan.mk _ fun j => G.map (g j) : Fan fun j => G.obj (f j)) := by
  refine' (IsLimit.postcomposeHomEquiv _ _).symm.trans (IsLimit.equivIsoLimit _)
  refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
  refine' Cones.ext (Iso.refl _) fun j =>
      by dsimp; cases j; 

Proof state:
case mk
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → P ⟶ f j
as✝ : J
⊢ G.map (g { as := as✝ }.as) ≫ 𝟙 (G.obj (f { as := as✝ }.as)) = 𝟙 (G.obj P) ≫ G.map (g { as := as✝ }.as)

Next tactic:
simp
/-- If `pi_comparison G f` is an isomorphism, then `G` preserves the limit of `f`. -/
def PreservesProduct.ofIsoComparison [i : IsIso (piComparison G f)] :
    PreservesLimit (Discrete.functor f) G := by
  

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝¹ : HasProduct f
inst✝ : HasProduct fun j => G.obj (f j)
i : IsIso (piComparison G f)
⊢ PreservesLimit (Discrete.functor f) G

Next tactic:
apply preservesLimitOfPreservesLimitCone (productIsProduct f)
/-- If `pi_comparison G f` is an isomorphism, then `G` preserves the limit of `f`. -/
def PreservesProduct.ofIsoComparison [i : IsIso (piComparison G f)] :
    PreservesLimit (Discrete.functor f) G := by
  apply preservesLimitOfPreservesLimitCone (productIsProduct f)
  

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝¹ : HasProduct f
inst✝ : HasProduct fun j => G.obj (f j)
i : IsIso (piComparison G f)
⊢ IsLimit (G.mapCone (Fan.mk (∏ f) (Pi.π f)))

Next tactic:
apply (isLimitMapConeFanMkEquiv _ _ _).symm _
/-- If `pi_comparison G f` is an isomorphism, then `G` preserves the limit of `f`. -/
def PreservesProduct.ofIsoComparison [i : IsIso (piComparison G f)] :
    PreservesLimit (Discrete.functor f) G := by
  apply preservesLimitOfPreservesLimitCone (productIsProduct f)
  apply (isLimitMapConeFanMkEquiv _ _ _).symm _
  

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝¹ : HasProduct f
inst✝ : HasProduct fun j => G.obj (f j)
i : IsIso (piComparison G f)
⊢ IsLimit (Fan.mk (G.obj (∏ f)) fun j => G.map (Pi.π f j))

Next tactic:
refine @IsLimit.ofPointIso _ _ _ _ _ _ _
    (limit.isLimit (Discrete.functor fun j : J => G.obj (f j))) ?_
/-- If `pi_comparison G f` is an isomorphism, then `G` preserves the limit of `f`. -/
def PreservesProduct.ofIsoComparison [i : IsIso (piComparison G f)] :
    PreservesLimit (Discrete.functor f) G := by
  apply preservesLimitOfPreservesLimitCone (productIsProduct f)
  apply (isLimitMapConeFanMkEquiv _ _ _).symm _
  refine @IsLimit.ofPointIso _ _ _ _ _ _ _
    (limit.isLimit (Discrete.functor fun j : J => G.obj (f j))) ?_
  

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝¹ : HasProduct f
inst✝ : HasProduct fun j => G.obj (f j)
i : IsIso (piComparison G f)
⊢ IsIso
    (IsLimit.lift (limit.isLimit (Discrete.functor fun j => G.obj (f j)))
      (Fan.mk (G.obj (∏ f)) fun j => G.map (Pi.π f j)))

Next tactic:
apply i
instance : IsIso (piComparison G f) := by
  

Proof state:
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝² : HasProduct f
inst✝¹ : HasProduct fun j => G.obj (f j)
inst✝ : PreservesLimit (Discrete.functor f) G
⊢ IsIso (piComparison G f)

Next tactic:
rw [← PreservesProduct.iso_hom]
instance : IsIso (piComparison G f) := by
  rw [← PreservesProduct.iso_hom]
  

Proof state:
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝² : HasProduct f
inst✝¹ : HasProduct fun j => G.obj (f j)
inst✝ : PreservesLimit (Discrete.functor f) G
⊢ IsIso (PreservesProduct.iso G f).hom

Next tactic:
infer_instance
/-- The map of a cofan is a colimit iff the cofan consisting of the mapped morphisms is a colimit.
This essentially lets us commute `Cofan.mk` with `Functor.mapCocone`.
-/
def isColimitMapCoconeCofanMkEquiv {P : C} (g : ∀ j, f j ⟶ P) :
    IsColimit (Functor.mapCocone G (Cofan.mk P g)) ≃
      IsColimit (Cofan.mk _ fun j => G.map (g j) : Cofan fun j => G.obj (f j)) := by
  

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → f j ⟶ P
⊢ IsColimit (G.mapCocone (Cofan.mk P g)) ≃ IsColimit (Cofan.mk (G.obj P) fun j => G.map (g j))

Next tactic:
refine' (IsColimit.precomposeHomEquiv _ _).symm.trans (IsColimit.equivIsoColimit _)
/-- The map of a cofan is a colimit iff the cofan consisting of the mapped morphisms is a colimit.
This essentially lets us commute `Cofan.mk` with `Functor.mapCocone`.
-/
def isColimitMapCoconeCofanMkEquiv {P : C} (g : ∀ j, f j ⟶ P) :
    IsColimit (Functor.mapCocone G (Cofan.mk P g)) ≃
      IsColimit (Cofan.mk _ fun j => G.map (g j) : Cofan fun j => G.obj (f j)) := by
  refine' (IsColimit.precomposeHomEquiv _ _).symm.trans (IsColimit.equivIsoColimit _)
  

Proof state:
case refine'_1
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → f j ⟶ P
⊢ (Discrete.functor fun j => G.obj (f j)) ≅ (Discrete.functor fun b => f b) ⋙ G
case refine'_2
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → f j ⟶ P
⊢ (Cocones.precompose ?refine'_1.hom).obj (G.mapCocone (Cofan.mk P g)) ≅ Cofan.mk (G.obj P) fun j => G.map (g j)

Next tactic:
refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
/-- The map of a cofan is a colimit iff the cofan consisting of the mapped morphisms is a colimit.
This essentially lets us commute `Cofan.mk` with `Functor.mapCocone`.
-/
def isColimitMapCoconeCofanMkEquiv {P : C} (g : ∀ j, f j ⟶ P) :
    IsColimit (Functor.mapCocone G (Cofan.mk P g)) ≃
      IsColimit (Cofan.mk _ fun j => G.map (g j) : Cofan fun j => G.obj (f j)) := by
  refine' (IsColimit.precomposeHomEquiv _ _).symm.trans (IsColimit.equivIsoColimit _)
  refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
  

Proof state:
case refine'_2
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → f j ⟶ P
⊢ (Cocones.precompose (Discrete.natIso fun j => Iso.refl (G.obj (f j.as))).hom).obj (G.mapCocone (Cofan.mk P g)) ≅
    Cofan.mk (G.obj P) fun j => G.map (g j)

Next tactic:
refine' Cocones.ext (Iso.refl _) fun j => by dsimp; cases j; simp
/-- The map of a cofan is a colimit iff the cofan consisting of the mapped morphisms is a colimit.
This essentially lets us commute `Cofan.mk` with `Functor.mapCocone`.
-/
def isColimitMapCoconeCofanMkEquiv {P : C} (g : ∀ j, f j ⟶ P) :
    IsColimit (Functor.mapCocone G (Cofan.mk P g)) ≃
      IsColimit (Cofan.mk _ fun j => G.map (g j) : Cofan fun j => G.obj (f j)) := by
  refine' (IsColimit.precomposeHomEquiv _ _).symm.trans (IsColimit.equivIsoColimit _)
  refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
  refine' Cocones.ext (Iso.refl _) fun j => by 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → f j ⟶ P
j : Discrete J
⊢ ((Cocones.precompose (Discrete.natIso fun j => Iso.refl (G.obj (f j.as))).hom).obj (G.mapCocone (Cofan.mk P g))).ι.app
        j ≫
      (Iso.refl
          ((Cocones.precompose (Discrete.natIso fun j => Iso.refl (G.obj (f j.as))).hom).obj
              (G.mapCocone (Cofan.mk P g))).pt).hom =
    (Cofan.mk (G.obj P) fun j => G.map (g j)).ι.app j

Next tactic:
dsimp
/-- The map of a cofan is a colimit iff the cofan consisting of the mapped morphisms is a colimit.
This essentially lets us commute `Cofan.mk` with `Functor.mapCocone`.
-/
def isColimitMapCoconeCofanMkEquiv {P : C} (g : ∀ j, f j ⟶ P) :
    IsColimit (Functor.mapCocone G (Cofan.mk P g)) ≃
      IsColimit (Cofan.mk _ fun j => G.map (g j) : Cofan fun j => G.obj (f j)) := by
  refine' (IsColimit.precomposeHomEquiv _ _).symm.trans (IsColimit.equivIsoColimit _)
  refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
  refine' Cocones.ext (Iso.refl _) fun j => by dsimp; 

Proof state:
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → f j ⟶ P
j : Discrete J
⊢ (𝟙 (G.obj (f j.as)) ≫ G.map (g j.as)) ≫ 𝟙 (G.obj P) = G.map (g j.as)

Next tactic:
cases j
/-- The map of a cofan is a colimit iff the cofan consisting of the mapped morphisms is a colimit.
This essentially lets us commute `Cofan.mk` with `Functor.mapCocone`.
-/
def isColimitMapCoconeCofanMkEquiv {P : C} (g : ∀ j, f j ⟶ P) :
    IsColimit (Functor.mapCocone G (Cofan.mk P g)) ≃
      IsColimit (Cofan.mk _ fun j => G.map (g j) : Cofan fun j => G.obj (f j)) := by
  refine' (IsColimit.precomposeHomEquiv _ _).symm.trans (IsColimit.equivIsoColimit _)
  refine' Discrete.natIso fun j => Iso.refl (G.obj (f j.as))
  refine' Cocones.ext (Iso.refl _) fun j => by dsimp; cases j; 

Proof state:
case mk
C : Type u₁
inst✝¹ : Category.{v₁, u₁} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
P : C
g : (j : J) → f j ⟶ P
as✝ : J
⊢ (𝟙 (G.obj (f { as := as✝ }.as)) ≫ G.map (g { as := as✝ }.as)) ≫ 𝟙 (G.obj P) = G.map (g { as := as✝ }.as)

Next tactic:
simp
/-- If `sigma_comparison G f` is an isomorphism, then `G` preserves the colimit of `f`. -/
def PreservesCoproduct.ofIsoComparison [i : IsIso (sigmaComparison G f)] :
    PreservesColimit (Discrete.functor f) G := by
  

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝¹ : HasCoproduct f
inst✝ : HasCoproduct fun j => G.obj (f j)
i : IsIso (sigmaComparison G f)
⊢ PreservesColimit (Discrete.functor f) G

Next tactic:
apply preservesColimitOfPreservesColimitCocone (coproductIsCoproduct f)
/-- If `sigma_comparison G f` is an isomorphism, then `G` preserves the colimit of `f`. -/
def PreservesCoproduct.ofIsoComparison [i : IsIso (sigmaComparison G f)] :
    PreservesColimit (Discrete.functor f) G := by
  apply preservesColimitOfPreservesColimitCocone (coproductIsCoproduct f)
  

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝¹ : HasCoproduct f
inst✝ : HasCoproduct fun j => G.obj (f j)
i : IsIso (sigmaComparison G f)
⊢ IsColimit (G.mapCocone (Cofan.mk (∐ f) (Sigma.ι f)))

Next tactic:
apply (isColimitMapCoconeCofanMkEquiv _ _ _).symm _
/-- If `sigma_comparison G f` is an isomorphism, then `G` preserves the colimit of `f`. -/
def PreservesCoproduct.ofIsoComparison [i : IsIso (sigmaComparison G f)] :
    PreservesColimit (Discrete.functor f) G := by
  apply preservesColimitOfPreservesColimitCocone (coproductIsCoproduct f)
  apply (isColimitMapCoconeCofanMkEquiv _ _ _).symm _
  

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝¹ : HasCoproduct f
inst✝ : HasCoproduct fun j => G.obj (f j)
i : IsIso (sigmaComparison G f)
⊢ IsColimit (Cofan.mk (G.obj (∐ f)) fun j => G.map (Sigma.ι f j))

Next tactic:
refine @IsColimit.ofPointIso _ _ _ _ _ _ _
    (colimit.isColimit (Discrete.functor fun j : J => G.obj (f j))) ?_
/-- If `sigma_comparison G f` is an isomorphism, then `G` preserves the colimit of `f`. -/
def PreservesCoproduct.ofIsoComparison [i : IsIso (sigmaComparison G f)] :
    PreservesColimit (Discrete.functor f) G := by
  apply preservesColimitOfPreservesColimitCocone (coproductIsCoproduct f)
  apply (isColimitMapCoconeCofanMkEquiv _ _ _).symm _
  refine @IsColimit.ofPointIso _ _ _ _ _ _ _
    (colimit.isColimit (Discrete.functor fun j : J => G.obj (f j))) ?_
  

Proof state:
C : Type u₁
inst✝³ : Category.{v₁, u₁} C
D : Type u₂
inst✝² : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝¹ : HasCoproduct f
inst✝ : HasCoproduct fun j => G.obj (f j)
i : IsIso (sigmaComparison G f)
⊢ IsIso
    (IsColimit.desc (colimit.isColimit (Discrete.functor fun j => G.obj (f j)))
      (Cofan.mk (G.obj (∐ f)) fun j => G.map (Sigma.ι f j)))

Next tactic:
apply i
instance : IsIso (sigmaComparison G f) := by
  

Proof state:
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝² : HasCoproduct f
inst✝¹ : HasCoproduct fun j => G.obj (f j)
inst✝ : PreservesColimit (Discrete.functor f) G
⊢ IsIso (sigmaComparison G f)

Next tactic:
rw [← PreservesCoproduct.inv_hom]
instance : IsIso (sigmaComparison G f) := by
  rw [← PreservesCoproduct.inv_hom]
  

Proof state:
C : Type u₁
inst✝⁴ : Category.{v₁, u₁} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
G : C ⥤ D
J : Type w
f : J → C
inst✝² : HasCoproduct f
inst✝¹ : HasCoproduct fun j => G.obj (f j)
inst✝ : PreservesColimit (Discrete.functor f) G
⊢ IsIso (PreservesCoproduct.iso G f).inv

Next tactic:
infer_instance
theorem nondenerate_dualProd :
    (dualProd R M).Nondegenerate ↔ Function.Injective (Module.Dual.eval R M) := by
  

Proof state:
R : Type u_1
M : Type u_2
N : Type u_3
inst✝² : CommRing R
inst✝¹ : AddCommGroup M
inst✝ : Module R M
⊢ Nondegenerate (dualProd R M) ↔ Function.Injective ⇑(Module.Dual.eval R M)

Next tactic:
classical
  rw [nondegenerate_iff_ker_eq_bot]
  rw [LinearMap.ker_eq_bot]
  let e := LinearEquiv.prodComm R _ _ ≪≫ₗ Module.dualProdDualEquivDual R (Module.Dual R M) M
  let h_d := e.symm.toLinearMap.comp (BilinForm.toLin <| dualProd R M)
  refine' (Function.Injective.of_comp_iff e.symm.injective
    (BilinForm.toLin <| dualProd R M)).symm.trans _
  rw [← LinearEquiv.coe_toLinearMap, ← LinearMap.coe_comp]
  change Function.Injective h_d ↔ _
  have : h_d = LinearMap.prodMap LinearMap.id (Module.Dual.eval R M) := by
    refine' LinearMap.ext fun x => Prod.ext _ _
    · ext
      dsimp [Module.Dual.eval, LinearEquiv.prodComm]
      simp
    · ext
      dsimp [Module.Dual.eval, LinearEquiv.prodComm]
      simp
  rw [this, LinearMap.coe_prodMap]
  refine' Prod.map_injective.trans _
  exact and_iff_right Function.injective_id
theorem nondenerate_dualProd :
    (dualProd R M).Nondegenerate ↔ Function.Injective (Module.Dual.eval R M) := by
  classical
  

Proof state:
R : Type u_1
M : Type u_2
N : Type u_3
inst✝² : CommRing R
inst✝¹ : AddCommGroup M
inst✝ : Module R M
⊢ Nondegenerate (dualProd R M) ↔ Function.Injective ⇑(Module.Dual.eval R M)

Next tactic:
rw [nondegenerate_iff_ker_eq_bot]
theorem nondenerate_dualProd :
    (dualProd R M).Nondegenerate ↔ Function.Injective (Module.Dual.eval R M) := by
  classical
  rw [nondegenerate_iff_ker_eq_bot]
  

Proof state:
R : Type u_1
M : Type u_2
N : Type u_3
inst✝² : CommRing R
inst✝¹ : AddCommGroup M
inst✝ : Module R M
⊢ LinearMap.ker (toLin (dualProd R M)) = ⊥ ↔ Function.Injective ⇑(Module.Dual.eval R M)

Next tactic:
rw [LinearMap.ker_eq_bot]
theorem nondenerate_dualProd :
    (dualProd R M).Nondegenerate ↔ Function.Injective (Module.Dual.eval R M) := by
  classical
  rw [nondegenerate_iff_ker_eq_bot]
  rw [LinearMap.ker_eq_bot]
  

Proof state:
R : Type u_1
M : Type u_2
N : Type u_3
inst✝² : CommRing R
inst✝¹ : AddCommGroup M
inst✝ : Module R M
⊢ Function.Injective ⇑(toLin (dualProd R M)) ↔ Function.Injective ⇑(Module.Dual.eval R M)

Next tactic:
let e := LinearEquiv.prodComm R _ _ ≪≫ₗ Module.dualProdDualEquivDual R (Module.Dual R M) M
theorem nondenerate_dualProd :
    (dualProd R M).Nondegenerate ↔ Function.Injective (Module.Dual.eval R M) := by
  classical
  rw [nondegenerate_iff_ker_eq_bot]
  rw [LinearMap.ker_eq_bot]
  let e := LinearEquiv.prodComm R _ _ ≪≫ₗ Module.dualProdDualEquivDual R (Module.Dual R M) M
  

Proof state:
R : Type u_1
M : Type u_2
N : Type u_3
inst✝² : CommRing R
inst✝¹ : AddCommGroup M
inst✝ : Module R M
e : (Module.Dual R M × Module.Dual R (Module.Dual R M)) ≃ₗ[R] Module.Dual R (Module.Dual R M × M) :=
  LinearEquiv.prodComm R (Module.Dual R M) (Module.Dual R (Module.Dual R M)) ≪≫ₗ
    Module.dualProdDualEquivDual R (Module.Dual R M) M
⊢ Function.Injective ⇑(toLin (dualProd R M)) ↔ Function.Injective ⇑(Module.Dual.eval R M)

Next tactic:
let h_d := e.symm.toLinearMap.comp (BilinForm.toLin <| dualProd R M)