l3lab/ntp-mathlib huggingface dataset:
@[simp]
theorem basicOpen_zero (X : LocallyRingedSpace) (U : Opens X.carrier) :
    X.toRingedSpace.basicOpen (0 : X.presheaf.obj <| op U) = ⊥ := by
  ext x
  simp only [RingedSpace.basicOpen, Opens.coe_mk, Set.mem_image, Set.mem_setOf_eq, Subtype.exists,
    exists_and_right, exists_eq_right, Opens.coe_bot, Set.mem_empty_iff_false,
    iff_false, not_exists]
  

Proof state:
case h.h
X✝ : LocallyRingedSpace
X : LocallyRingedSpace
U : Opens ↑↑X.toPresheafedSpace
x : ↑↑(toRingedSpace X).toPresheafedSpace
⊢ ∀ (x_1 : x ∈ U),
    ¬IsUnit ((Presheaf.germ (toRingedSpace X).toPresheafedSpace.presheaf { val := x, property := (_ : x ∈ U) }) 0)

Next tactic:
intros hx
@[simp]
theorem basicOpen_zero (X : LocallyRingedSpace) (U : Opens X.carrier) :
    X.toRingedSpace.basicOpen (0 : X.presheaf.obj <| op U) = ⊥ := by
  ext x
  simp only [RingedSpace.basicOpen, Opens.coe_mk, Set.mem_image, Set.mem_setOf_eq, Subtype.exists,
    exists_and_right, exists_eq_right, Opens.coe_bot, Set.mem_empty_iff_false,
    iff_false, not_exists]
  intros hx
  

Proof state:
case h.h
X✝ : LocallyRingedSpace
X : LocallyRingedSpace
U : Opens ↑↑X.toPresheafedSpace
x : ↑↑(toRingedSpace X).toPresheafedSpace
hx : x ∈ U
⊢ ¬IsUnit ((Presheaf.germ (toRingedSpace X).toPresheafedSpace.presheaf { val := x, property := (_ : x ∈ U) }) 0)

Next tactic:
rw [map_zero, isUnit_zero_iff]
@[simp]
theorem basicOpen_zero (X : LocallyRingedSpace) (U : Opens X.carrier) :
    X.toRingedSpace.basicOpen (0 : X.presheaf.obj <| op U) = ⊥ := by
  ext x
  simp only [RingedSpace.basicOpen, Opens.coe_mk, Set.mem_image, Set.mem_setOf_eq, Subtype.exists,
    exists_and_right, exists_eq_right, Opens.coe_bot, Set.mem_empty_iff_false,
    iff_false, not_exists]
  intros hx
  rw [map_zero, isUnit_zero_iff]
  

Proof state:
case h.h
X✝ : LocallyRingedSpace
X : LocallyRingedSpace
U : Opens ↑↑X.toPresheafedSpace
x : ↑↑(toRingedSpace X).toPresheafedSpace
hx : x ∈ U
⊢ ¬0 = 1

Next tactic:
change (0 : X.stalk x) ≠ (1 : X.stalk x)
@[simp]
theorem basicOpen_zero (X : LocallyRingedSpace) (U : Opens X.carrier) :
    X.toRingedSpace.basicOpen (0 : X.presheaf.obj <| op U) = ⊥ := by
  ext x
  simp only [RingedSpace.basicOpen, Opens.coe_mk, Set.mem_image, Set.mem_setOf_eq, Subtype.exists,
    exists_and_right, exists_eq_right, Opens.coe_bot, Set.mem_empty_iff_false,
    iff_false, not_exists]
  intros hx
  rw [map_zero, isUnit_zero_iff]
  change (0 : X.stalk x) ≠ (1 : X.stalk x)
  

Proof state:
case h.h
X✝ : LocallyRingedSpace
X : LocallyRingedSpace
U : Opens ↑↑X.toPresheafedSpace
x : ↑↑(toRingedSpace X).toPresheafedSpace
hx : x ∈ U
⊢ 0 ≠ 1

Next tactic:
exact zero_ne_one
/-- Every subobject of a structured arrow can be projected to a subobject of the underlying
    object. -/
def projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    Subobject A → Subobject A.right := by
  

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A : StructuredArrow S T
⊢ Subobject A → Subobject A.right

Next tactic:
refine' Subobject.lift (fun P f hf => Subobject.mk f.right) _
/-- Every subobject of a structured arrow can be projected to a subobject of the underlying
    object. -/
def projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    Subobject A → Subobject A.right := by
  refine' Subobject.lift (fun P f hf => Subobject.mk f.right) _
  

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A : StructuredArrow S T
⊢ ∀ ⦃A_1 B : StructuredArrow S T⦄ (f : A_1 ⟶ A) (g : B ⟶ A) [inst : Mono f] [inst_1 : Mono g] (i : A_1 ≅ B),
    i.hom ≫ g = f → (fun P f hf => Subobject.mk f.right) A_1 f inst = (fun P f hf => Subobject.mk f.right) B g inst_1

Next tactic:
intro P Q f g hf hg i hi
/-- Every subobject of a structured arrow can be projected to a subobject of the underlying
    object. -/
def projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    Subobject A → Subobject A.right := by
  refine' Subobject.lift (fun P f hf => Subobject.mk f.right) _
  intro P Q f g hf hg i hi
  

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P Q : StructuredArrow S T
f : P ⟶ A
g : Q ⟶ A
hf : Mono f
hg : Mono g
i : P ≅ Q
hi : i.hom ≫ g = f
⊢ (fun P f hf => Subobject.mk f.right) P f hf = (fun P f hf => Subobject.mk f.right) Q g hg

Next tactic:
refine' Subobject.mk_eq_mk_of_comm _ _ ((proj S T).mapIso i) _
/-- Every subobject of a structured arrow can be projected to a subobject of the underlying
    object. -/
def projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    Subobject A → Subobject A.right := by
  refine' Subobject.lift (fun P f hf => Subobject.mk f.right) _
  intro P Q f g hf hg i hi
  refine' Subobject.mk_eq_mk_of_comm _ _ ((proj S T).mapIso i) _
  

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P Q : StructuredArrow S T
f : P ⟶ A
g : Q ⟶ A
hf : Mono f
hg : Mono g
i : P ≅ Q
hi : i.hom ≫ g = f
⊢ ((proj S T).mapIso i).hom ≫ g.right = f.right

Next tactic:
exact congr_arg CommaMorphism.right hi
theorem projectSubobject_factors [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ P : Subobject A, ∃ q, q ≫ T.map (projectSubobject P).arrow = A.hom :=
  Subobject.ind _ fun P f hf =>
    ⟨P.hom ≫ T.map (Subobject.underlyingIso _).inv, by
      

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
⊢ (P.hom ≫ T.map (Subobject.underlyingIso (MonoOver.arrow (MonoOver.mk' f)).right).inv) ≫
      T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) =
    A.hom

Next tactic:
dsimp
theorem projectSubobject_factors [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ P : Subobject A, ∃ q, q ≫ T.map (projectSubobject P).arrow = A.hom :=
  Subobject.ind _ fun P f hf =>
    ⟨P.hom ≫ T.map (Subobject.underlyingIso _).inv, by
      dsimp
      

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
⊢ (P.hom ≫ T.map (Subobject.underlyingIso f.right).inv) ≫ T.map (Subobject.arrow (Subobject.mk f.right)) = A.hom

Next tactic:
simp [← T.map_comp]
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A : StructuredArrow S T
⊢ ∀ ⦃A_1 : StructuredArrow S T⦄ (f : A_1 ⟶ A) [inst : Mono f]
    {q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))}
    (hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom),
    liftSubobject (projectSubobject (Subobject.mk f)) hq = Subobject.mk f

Next tactic:
intro P f hf q hq
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      intro P f hf q hq
      

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))
hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom
⊢ liftSubobject (projectSubobject (Subobject.mk f)) hq = Subobject.mk f

Next tactic:
fapply Subobject.mk_eq_mk_of_comm
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      intro P f hf q hq
      fapply Subobject.mk_eq_mk_of_comm
      · 

Proof state:
case i
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))
hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom
⊢ mk q ≅ P

Next tactic:
fapply isoMk
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      intro P f hf q hq
      fapply Subobject.mk_eq_mk_of_comm
      · fapply isoMk
        · 

Proof state:
case i.g
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))
hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom
⊢ (mk q).right ≅ P.right

Next tactic:
exact Subobject.underlyingIso _
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      intro P f hf q hq
      fapply Subobject.mk_eq_mk_of_comm
      · fapply isoMk
        · exact Subobject.underlyingIso _
        · 

Proof state:
case i.w
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))
hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom
⊢ autoParam ((mk q).hom ≫ T.map (Subobject.underlyingIso (MonoOver.arrow (MonoOver.mk' f)).right).hom = P.hom) _auto✝

Next tactic:
exact (cancel_mono (T.map f.right)).1 (by dsimp; simpa [← T.map_comp] using hq)
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      intro P f hf q hq
      fapply Subobject.mk_eq_mk_of_comm
      · fapply isoMk
        · exact Subobject.underlyingIso _
        · exact (cancel_mono (T.map f.right)).1 (by 

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))
hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom
⊢ ((mk q).hom ≫ T.map (Subobject.underlyingIso (MonoOver.arrow (MonoOver.mk' f)).right).hom) ≫ T.map f.right =
    P.hom ≫ T.map f.right

Next tactic:
dsimp
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      intro P f hf q hq
      fapply Subobject.mk_eq_mk_of_comm
      · fapply isoMk
        · exact Subobject.underlyingIso _
        · exact (cancel_mono (T.map f.right)).1 (by dsimp; 

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))
hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom
⊢ (q ≫ T.map (Subobject.underlyingIso f.right).hom) ≫ T.map f.right = P.hom ≫ T.map f.right

Next tactic:
simpa [← T.map_comp] using hq
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      intro P f hf q hq
      fapply Subobject.mk_eq_mk_of_comm
      · fapply isoMk
        · exact Subobject.underlyingIso _
        · exact (cancel_mono (T.map f.right)).1 (by dsimp; simpa [← T.map_comp] using hq)
      · 

Proof state:
case w
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))
hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom
⊢ (isoMk (Subobject.underlyingIso (MonoOver.arrow (MonoOver.mk' f)).right)).hom ≫ f =
    homMk (Subobject.arrow (projectSubobject (Subobject.mk f)))

Next tactic:
exact ext _ _ (by dsimp; simp)
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      intro P f hf q hq
      fapply Subobject.mk_eq_mk_of_comm
      · fapply isoMk
        · exact Subobject.underlyingIso _
        · exact (cancel_mono (T.map f.right)).1 (by dsimp; simpa [← T.map_comp] using hq)
      · exact ext _ _ (by 

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))
hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom
⊢ ((isoMk (Subobject.underlyingIso (MonoOver.arrow (MonoOver.mk' f)).right)).hom ≫ f).right =
    (homMk (Subobject.arrow (projectSubobject (Subobject.mk f)))).right

Next tactic:
dsimp
/-- Projecting and then lifting a subobject recovers the original subobject, because there is at
    most one morphism making the projected subobject into a structured arrow. -/
theorem lift_projectSubobject [HasLimits C] [PreservesLimits T] {A : StructuredArrow S T} :
    ∀ (P : Subobject A) {q} (hq : q ≫ T.map (projectSubobject P).arrow = A.hom),
      liftSubobject (projectSubobject P) hq = P :=
  Subobject.ind _
    (by
      intro P f hf q hq
      fapply Subobject.mk_eq_mk_of_comm
      · fapply isoMk
        · exact Subobject.underlyingIso _
        · exact (cancel_mono (T.map f.right)).1 (by dsimp; simpa [← T.map_comp] using hq)
      · exact ext _ _ (by dsimp; 

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P : StructuredArrow S T
f : P ⟶ A
hf : Mono f
q : (Functor.fromPUnit S).obj A.left ⟶ T.obj (Subobject.underlying.obj (projectSubobject (Subobject.mk f)))
hq : q ≫ T.map (Subobject.arrow (projectSubobject (Subobject.mk f))) = A.hom
⊢ (Subobject.underlyingIso f.right).hom ≫ f.right = Subobject.arrow (Subobject.mk f.right)

Next tactic:
simp
/-- If `A : S → T.obj B` is a structured arrow for `S : D` and `T : C ⥤ D`, then we can explicitly
    describe the subobjects of `A` as the subobjects `P` of `B` in `C` for which `A.hom` factors
    through the image of `P` under `T`. -/
@[simps!]
def subobjectEquiv [HasLimits C] [PreservesLimits T] (A : StructuredArrow S T) :
    Subobject A ≃o { P : Subobject A.right // ∃ q, q ≫ T.map P.arrow = A.hom } where
  toFun P := ⟨projectSubobject P, projectSubobject_factors P⟩
  invFun P := liftSubobject P.val P.prop.choose_spec
  left_inv P := lift_projectSubobject _ _
  right_inv P := Subtype.ext (by 

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A : StructuredArrow S T
P : { P // ∃ q, q ≫ T.map (Subobject.arrow P) = A.hom }
⊢ ↑((fun P =>
          { val := projectSubobject P,
            property := (_ : ∃ q, q ≫ T.map (Subobject.arrow (projectSubobject P)) = A.hom) })
        ((fun P =>
            liftSubobject ↑P
              (_ :
                Exists.choose (_ : ∃ q, q ≫ T.map (Subobject.arrow ↑P) = A.hom) ≫ T.map (Subobject.arrow ↑P) = A.hom))
          P)) =
    ↑P

Next tactic:
simp only [liftSubobject, homMk_right, projectSubobject_mk,
      Subobject.mk_arrow, Subtype.coe_eta]
/-- If `A : S → T.obj B` is a structured arrow for `S : D` and `T : C ⥤ D`, then we can explicitly
    describe the subobjects of `A` as the subobjects `P` of `B` in `C` for which `A.hom` factors
    through the image of `P` under `T`. -/
@[simps!]
def subobjectEquiv [HasLimits C] [PreservesLimits T] (A : StructuredArrow S T) :
    Subobject A ≃o { P : Subobject A.right // ∃ q, q ≫ T.map P.arrow = A.hom } where
  toFun P := ⟨projectSubobject P, projectSubobject_factors P⟩
  invFun P := liftSubobject P.val P.prop.choose_spec
  left_inv P := lift_projectSubobject _ _
  right_inv P := Subtype.ext (by simp only [liftSubobject, homMk_right, projectSubobject_mk,
      Subobject.mk_arrow, Subtype.coe_eta])
  map_rel_iff' := by
    apply Subobject.ind₂
    intro P Q f g hf hg
    refine' ⟨fun h => Subobject.mk_le_mk_of_comm _ _, fun h => _⟩
    · exact homMk (Subobject.ofMkLEMk _ _ h)
        ((cancel_mono (T.map g.right)).1 (by simp [← T.map_comp]))
    · aesop_cat
    · refine' Subobject.mk_le_mk_of_comm (Subobject.ofMkLEMk _ _ h).right _
      

Proof state:
case h.refine'_3
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : D
T : C ⥤ D
inst✝¹ : HasLimits C
inst✝ : PreservesLimits T
A P Q : StructuredArrow S T
f : P ⟶ A
g : Q ⟶ A
hf : Mono f
hg : Mono g
h : Subobject.mk f ≤ Subobject.mk g
⊢ (Subobject.ofMkLEMk f g h).right ≫ (MonoOver.arrow (MonoOver.mk' g)).right = (MonoOver.arrow (MonoOver.mk' f)).right

Next tactic:
exact congr_arg CommaMorphism.right (Subobject.ofMkLEMk_comp h)
/-- Every quotient of a costructured arrow can be projected to a quotient of the underlying
    object. -/
def projectQuotient [HasColimits C] [PreservesColimits S] {A : CostructuredArrow S T} :
    Subobject (op A) → Subobject (op A.left) := by
  

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : C ⥤ D
T : D
inst✝¹ : HasColimits C
inst✝ : PreservesColimits S
A : CostructuredArrow S T
⊢ Subobject (op A) → Subobject (op A.left)

Next tactic:
refine' Subobject.lift (fun P f hf => Subobject.mk f.unop.left.op) _
/-- Every quotient of a costructured arrow can be projected to a quotient of the underlying
    object. -/
def projectQuotient [HasColimits C] [PreservesColimits S] {A : CostructuredArrow S T} :
    Subobject (op A) → Subobject (op A.left) := by
  refine' Subobject.lift (fun P f hf => Subobject.mk f.unop.left.op) _
  

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : C ⥤ D
T : D
inst✝¹ : HasColimits C
inst✝ : PreservesColimits S
A : CostructuredArrow S T
⊢ ∀ ⦃A_1 B : (CostructuredArrow S T)ᵒᵖ⦄ (f : A_1 ⟶ op A) (g : B ⟶ op A) [inst : Mono f] [inst_1 : Mono g] (i : A_1 ≅ B),
    i.hom ≫ g = f →
      (fun P f hf => Subobject.mk f.unop.left.op) A_1 f inst = (fun P f hf => Subobject.mk f.unop.left.op) B g inst_1

Next tactic:
intro P Q f g hf hg i hi
/-- Every quotient of a costructured arrow can be projected to a quotient of the underlying
    object. -/
def projectQuotient [HasColimits C] [PreservesColimits S] {A : CostructuredArrow S T} :
    Subobject (op A) → Subobject (op A.left) := by
  refine' Subobject.lift (fun P f hf => Subobject.mk f.unop.left.op) _
  intro P Q f g hf hg i hi
  

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : C ⥤ D
T : D
inst✝¹ : HasColimits C
inst✝ : PreservesColimits S
A : CostructuredArrow S T
P Q : (CostructuredArrow S T)ᵒᵖ
f : P ⟶ op A
g : Q ⟶ op A
hf : Mono f
hg : Mono g
i : P ≅ Q
hi : i.hom ≫ g = f
⊢ (fun P f hf => Subobject.mk f.unop.left.op) P f hf = (fun P f hf => Subobject.mk f.unop.left.op) Q g hg

Next tactic:
refine' Subobject.mk_eq_mk_of_comm _ _ ((proj S T).mapIso i.unop).op (Quiver.Hom.unop_inj _)
/-- Every quotient of a costructured arrow can be projected to a quotient of the underlying
    object. -/
def projectQuotient [HasColimits C] [PreservesColimits S] {A : CostructuredArrow S T} :
    Subobject (op A) → Subobject (op A.left) := by
  refine' Subobject.lift (fun P f hf => Subobject.mk f.unop.left.op) _
  intro P Q f g hf hg i hi
  refine' Subobject.mk_eq_mk_of_comm _ _ ((proj S T).mapIso i.unop).op (Quiver.Hom.unop_inj _)
  

Proof state:
C : Type u₁
inst✝³ : Category.{v, u₁} C
D : Type u₂
inst✝² : Category.{v, u₂} D
S : C ⥤ D
T : D
inst✝¹ : HasColimits C
inst✝ : PreservesColimits S
A : CostructuredArrow S T
P Q : (CostructuredArrow S T)ᵒᵖ
f : P ⟶ op A
g : Q ⟶ op A
hf : Mono f
hg : Mono g
i : P ≅ Q
hi : i.hom ≫ g = f
⊢ ((Iso.op ((proj S T).mapIso (Iso.unop i))).hom ≫ g.unop.left.op).unop = f.unop.left.op.unop

Next tactic:
have := congr_arg Quiver.Hom.unop hi