l3lab/ntp-mathlib huggingface dataset:
/-- If the map `S ⟶ T` is mono, then there is a description of the image of `W ×ₛ X ⟶ Y ×ₜ Z`. -/
theorem range_pullback_map {W X Y Z S T : TopCat} (f₁ : W ⟶ S) (f₂ : X ⟶ S) (g₁ : Y ⟶ T)
    (g₂ : Z ⟶ T) (i₁ : W ⟶ Y) (i₂ : X ⟶ Z) (i₃ : S ⟶ T) [H₃ : Mono i₃] (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁)
    (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) :
    Set.range (pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂) =
      (pullback.fst : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₁ ∩
        (pullback.snd : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₂ := by
  ext
  constructor
  · rintro ⟨y, rfl⟩
    simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_range, ← comp_apply, limit.lift_π,
      PullbackCone.mk_pt, PullbackCone.mk_π_app]
    simp only [comp_apply, exists_apply_eq_apply, and_self]
  rintro ⟨⟨x₁, hx₁⟩, ⟨x₂, hx₂⟩⟩
  have : f₁ x₁ = f₂ x₂ := by
    apply (TopCat.mono_iff_injective _).mp H₃
    simp only [← comp_apply, eq₁, eq₂]
    simp only [comp_apply, hx₁, hx₂]
    simp only [← comp_apply, pullback.condition]
  use (pullbackIsoProdSubtype f₁ f₂).inv ⟨⟨x₁, x₂⟩, this⟩
  apply Concrete.limit_ext
  

Proof state:
case h.a
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
W X Y Z S T : TopCat
f₁ : W ⟶ S
f₂ : X ⟶ S
g₁ : Y ⟶ T
g₂ : Z ⟶ T
i₁ : W ⟶ Y
i₂ : X ⟶ Z
i₃ : S ⟶ T
H₃ : Mono i₃
eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁
eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂
x✝ : (forget TopCat).obj (pullback g₁ g₂)
x₁ : (forget TopCat).obj W
hx₁ : i₁ x₁ = pullback.fst x✝
x₂ : (forget TopCat).obj X
hx₂ : i₂ x₂ = pullback.snd x✝
this : f₁ x₁ = f₂ x₂
⊢ ∀ (j : WalkingCospan),
    (limit.π (cospan g₁ g₂) j)
        ((pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂)
          ((pullbackIsoProdSubtype f₁ f₂).inv { val := (x₁, x₂), property := this })) =
      (limit.π (cospan g₁ g₂) j) x✝

Next tactic:
rintro (_ | _ | _)
/-- If the map `S ⟶ T` is mono, then there is a description of the image of `W ×ₛ X ⟶ Y ×ₜ Z`. -/
theorem range_pullback_map {W X Y Z S T : TopCat} (f₁ : W ⟶ S) (f₂ : X ⟶ S) (g₁ : Y ⟶ T)
    (g₂ : Z ⟶ T) (i₁ : W ⟶ Y) (i₂ : X ⟶ Z) (i₃ : S ⟶ T) [H₃ : Mono i₃] (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁)
    (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) :
    Set.range (pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂) =
      (pullback.fst : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₁ ∩
        (pullback.snd : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₂ := by
  ext
  constructor
  · rintro ⟨y, rfl⟩
    simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_range, ← comp_apply, limit.lift_π,
      PullbackCone.mk_pt, PullbackCone.mk_π_app]
    simp only [comp_apply, exists_apply_eq_apply, and_self]
  rintro ⟨⟨x₁, hx₁⟩, ⟨x₂, hx₂⟩⟩
  have : f₁ x₁ = f₂ x₂ := by
    apply (TopCat.mono_iff_injective _).mp H₃
    simp only [← comp_apply, eq₁, eq₂]
    simp only [comp_apply, hx₁, hx₂]
    simp only [← comp_apply, pullback.condition]
  use (pullbackIsoProdSubtype f₁ f₂).inv ⟨⟨x₁, x₂⟩, this⟩
  apply Concrete.limit_ext
  rintro (_ | _ | _) <;>
  

Proof state:
case h.a.none
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
W X Y Z S T : TopCat
f₁ : W ⟶ S
f₂ : X ⟶ S
g₁ : Y ⟶ T
g₂ : Z ⟶ T
i₁ : W ⟶ Y
i₂ : X ⟶ Z
i₃ : S ⟶ T
H₃ : Mono i₃
eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁
eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂
x✝ : (forget TopCat).obj (pullback g₁ g₂)
x₁ : (forget TopCat).obj W
hx₁ : i₁ x₁ = pullback.fst x✝
x₂ : (forget TopCat).obj X
hx₂ : i₂ x₂ = pullback.snd x✝
this : f₁ x₁ = f₂ x₂
⊢ (limit.π (cospan g₁ g₂) none)
      ((pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂)
        ((pullbackIsoProdSubtype f₁ f₂).inv { val := (x₁, x₂), property := this })) =
    (limit.π (cospan g₁ g₂) none) x✝

Next tactic:
simp only [← comp_apply, Category.assoc, limit.lift_π, PullbackCone.mk_π_app_one]
/-- If the map `S ⟶ T` is mono, then there is a description of the image of `W ×ₛ X ⟶ Y ×ₜ Z`. -/
theorem range_pullback_map {W X Y Z S T : TopCat} (f₁ : W ⟶ S) (f₂ : X ⟶ S) (g₁ : Y ⟶ T)
    (g₂ : Z ⟶ T) (i₁ : W ⟶ Y) (i₂ : X ⟶ Z) (i₃ : S ⟶ T) [H₃ : Mono i₃] (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁)
    (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) :
    Set.range (pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂) =
      (pullback.fst : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₁ ∩
        (pullback.snd : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₂ := by
  ext
  constructor
  · rintro ⟨y, rfl⟩
    simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_range, ← comp_apply, limit.lift_π,
      PullbackCone.mk_pt, PullbackCone.mk_π_app]
    simp only [comp_apply, exists_apply_eq_apply, and_self]
  rintro ⟨⟨x₁, hx₁⟩, ⟨x₂, hx₂⟩⟩
  have : f₁ x₁ = f₂ x₂ := by
    apply (TopCat.mono_iff_injective _).mp H₃
    simp only [← comp_apply, eq₁, eq₂]
    simp only [comp_apply, hx₁, hx₂]
    simp only [← comp_apply, pullback.condition]
  use (pullbackIsoProdSubtype f₁ f₂).inv ⟨⟨x₁, x₂⟩, this⟩
  apply Concrete.limit_ext
  rintro (_ | _ | _) <;>
  

Proof state:
case h.a.some.left
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
W X Y Z S T : TopCat
f₁ : W ⟶ S
f₂ : X ⟶ S
g₁ : Y ⟶ T
g₂ : Z ⟶ T
i₁ : W ⟶ Y
i₂ : X ⟶ Z
i₃ : S ⟶ T
H₃ : Mono i₃
eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁
eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂
x✝ : (forget TopCat).obj (pullback g₁ g₂)
x₁ : (forget TopCat).obj W
hx₁ : i₁ x₁ = pullback.fst x✝
x₂ : (forget TopCat).obj X
hx₂ : i₂ x₂ = pullback.snd x✝
this : f₁ x₁ = f₂ x₂
⊢ (limit.π (cospan g₁ g₂) (some WalkingPair.left))
      ((pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂)
        ((pullbackIsoProdSubtype f₁ f₂).inv { val := (x₁, x₂), property := this })) =
    (limit.π (cospan g₁ g₂) (some WalkingPair.left)) x✝

Next tactic:
simp only [← comp_apply, Category.assoc, limit.lift_π, PullbackCone.mk_π_app_one]
/-- If the map `S ⟶ T` is mono, then there is a description of the image of `W ×ₛ X ⟶ Y ×ₜ Z`. -/
theorem range_pullback_map {W X Y Z S T : TopCat} (f₁ : W ⟶ S) (f₂ : X ⟶ S) (g₁ : Y ⟶ T)
    (g₂ : Z ⟶ T) (i₁ : W ⟶ Y) (i₂ : X ⟶ Z) (i₃ : S ⟶ T) [H₃ : Mono i₃] (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁)
    (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) :
    Set.range (pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂) =
      (pullback.fst : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₁ ∩
        (pullback.snd : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₂ := by
  ext
  constructor
  · rintro ⟨y, rfl⟩
    simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_range, ← comp_apply, limit.lift_π,
      PullbackCone.mk_pt, PullbackCone.mk_π_app]
    simp only [comp_apply, exists_apply_eq_apply, and_self]
  rintro ⟨⟨x₁, hx₁⟩, ⟨x₂, hx₂⟩⟩
  have : f₁ x₁ = f₂ x₂ := by
    apply (TopCat.mono_iff_injective _).mp H₃
    simp only [← comp_apply, eq₁, eq₂]
    simp only [comp_apply, hx₁, hx₂]
    simp only [← comp_apply, pullback.condition]
  use (pullbackIsoProdSubtype f₁ f₂).inv ⟨⟨x₁, x₂⟩, this⟩
  apply Concrete.limit_ext
  rintro (_ | _ | _) <;>
  

Proof state:
case h.a.some.right
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
W X Y Z S T : TopCat
f₁ : W ⟶ S
f₂ : X ⟶ S
g₁ : Y ⟶ T
g₂ : Z ⟶ T
i₁ : W ⟶ Y
i₂ : X ⟶ Z
i₃ : S ⟶ T
H₃ : Mono i₃
eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁
eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂
x✝ : (forget TopCat).obj (pullback g₁ g₂)
x₁ : (forget TopCat).obj W
hx₁ : i₁ x₁ = pullback.fst x✝
x₂ : (forget TopCat).obj X
hx₂ : i₂ x₂ = pullback.snd x✝
this : f₁ x₁ = f₂ x₂
⊢ (limit.π (cospan g₁ g₂) (some WalkingPair.right))
      ((pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂)
        ((pullbackIsoProdSubtype f₁ f₂).inv { val := (x₁, x₂), property := this })) =
    (limit.π (cospan g₁ g₂) (some WalkingPair.right)) x✝

Next tactic:
simp only [← comp_apply, Category.assoc, limit.lift_π, PullbackCone.mk_π_app_one]
/-- If the map `S ⟶ T` is mono, then there is a description of the image of `W ×ₛ X ⟶ Y ×ₜ Z`. -/
theorem range_pullback_map {W X Y Z S T : TopCat} (f₁ : W ⟶ S) (f₂ : X ⟶ S) (g₁ : Y ⟶ T)
    (g₂ : Z ⟶ T) (i₁ : W ⟶ Y) (i₂ : X ⟶ Z) (i₃ : S ⟶ T) [H₃ : Mono i₃] (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁)
    (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) :
    Set.range (pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂) =
      (pullback.fst : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₁ ∩
        (pullback.snd : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₂ := by
  ext
  constructor
  · rintro ⟨y, rfl⟩
    simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_range, ← comp_apply, limit.lift_π,
      PullbackCone.mk_pt, PullbackCone.mk_π_app]
    simp only [comp_apply, exists_apply_eq_apply, and_self]
  rintro ⟨⟨x₁, hx₁⟩, ⟨x₂, hx₂⟩⟩
  have : f₁ x₁ = f₂ x₂ := by
    apply (TopCat.mono_iff_injective _).mp H₃
    simp only [← comp_apply, eq₁, eq₂]
    simp only [comp_apply, hx₁, hx₂]
    simp only [← comp_apply, pullback.condition]
  use (pullbackIsoProdSubtype f₁ f₂).inv ⟨⟨x₁, x₂⟩, this⟩
  apply Concrete.limit_ext
  rintro (_ | _ | _) <;>
  simp only [← comp_apply, Category.assoc, limit.lift_π, PullbackCone.mk_π_app_one]
  · 

Proof state:
case h.a.none
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
W X Y Z S T : TopCat
f₁ : W ⟶ S
f₂ : X ⟶ S
g₁ : Y ⟶ T
g₂ : Z ⟶ T
i₁ : W ⟶ Y
i₂ : X ⟶ Z
i₃ : S ⟶ T
H₃ : Mono i₃
eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁
eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂
x✝ : (forget TopCat).obj (pullback g₁ g₂)
x₁ : (forget TopCat).obj W
hx₁ : i₁ x₁ = pullback.fst x✝
x₂ : (forget TopCat).obj X
hx₂ : i₂ x₂ = pullback.snd x✝
this : f₁ x₁ = f₂ x₂
⊢ ((pullbackIsoProdSubtype f₁ f₂).inv ≫ pullback.fst ≫ i₁ ≫ g₁) { val := (x₁, x₂), property := this } =
    (limit.π (cospan g₁ g₂) none) x✝

Next tactic:
simp only [cospan_one, pullbackIsoProdSubtype_inv_fst_assoc, comp_apply,
      pullbackFst_apply, hx₁]
/-- If the map `S ⟶ T` is mono, then there is a description of the image of `W ×ₛ X ⟶ Y ×ₜ Z`. -/
theorem range_pullback_map {W X Y Z S T : TopCat} (f₁ : W ⟶ S) (f₂ : X ⟶ S) (g₁ : Y ⟶ T)
    (g₂ : Z ⟶ T) (i₁ : W ⟶ Y) (i₂ : X ⟶ Z) (i₃ : S ⟶ T) [H₃ : Mono i₃] (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁)
    (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) :
    Set.range (pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂) =
      (pullback.fst : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₁ ∩
        (pullback.snd : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₂ := by
  ext
  constructor
  · rintro ⟨y, rfl⟩
    simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_range, ← comp_apply, limit.lift_π,
      PullbackCone.mk_pt, PullbackCone.mk_π_app]
    simp only [comp_apply, exists_apply_eq_apply, and_self]
  rintro ⟨⟨x₁, hx₁⟩, ⟨x₂, hx₂⟩⟩
  have : f₁ x₁ = f₂ x₂ := by
    apply (TopCat.mono_iff_injective _).mp H₃
    simp only [← comp_apply, eq₁, eq₂]
    simp only [comp_apply, hx₁, hx₂]
    simp only [← comp_apply, pullback.condition]
  use (pullbackIsoProdSubtype f₁ f₂).inv ⟨⟨x₁, x₂⟩, this⟩
  apply Concrete.limit_ext
  rintro (_ | _ | _) <;>
  simp only [← comp_apply, Category.assoc, limit.lift_π, PullbackCone.mk_π_app_one]
  · simp only [cospan_one, pullbackIsoProdSubtype_inv_fst_assoc, comp_apply,
      pullbackFst_apply, hx₁]
    

Proof state:
case h.a.none
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
W X Y Z S T : TopCat
f₁ : W ⟶ S
f₂ : X ⟶ S
g₁ : Y ⟶ T
g₂ : Z ⟶ T
i₁ : W ⟶ Y
i₂ : X ⟶ Z
i₃ : S ⟶ T
H₃ : Mono i₃
eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁
eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂
x✝ : (forget TopCat).obj (pullback g₁ g₂)
x₁ : (forget TopCat).obj W
hx₁ : i₁ x₁ = pullback.fst x✝
x₂ : (forget TopCat).obj X
hx₂ : i₂ x₂ = pullback.snd x✝
this : f₁ x₁ = f₂ x₂
⊢ g₁ (pullback.fst x✝) = (limit.π (cospan g₁ g₂) none) x✝

Next tactic:
rw [← limit.w _ WalkingCospan.Hom.inl, cospan_map_inl, comp_apply (g := g₁)]
/-- If the map `S ⟶ T` is mono, then there is a description of the image of `W ×ₛ X ⟶ Y ×ₜ Z`. -/
theorem range_pullback_map {W X Y Z S T : TopCat} (f₁ : W ⟶ S) (f₂ : X ⟶ S) (g₁ : Y ⟶ T)
    (g₂ : Z ⟶ T) (i₁ : W ⟶ Y) (i₂ : X ⟶ Z) (i₃ : S ⟶ T) [H₃ : Mono i₃] (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁)
    (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) :
    Set.range (pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂) =
      (pullback.fst : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₁ ∩
        (pullback.snd : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₂ := by
  ext
  constructor
  · rintro ⟨y, rfl⟩
    simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_range, ← comp_apply, limit.lift_π,
      PullbackCone.mk_pt, PullbackCone.mk_π_app]
    simp only [comp_apply, exists_apply_eq_apply, and_self]
  rintro ⟨⟨x₁, hx₁⟩, ⟨x₂, hx₂⟩⟩
  have : f₁ x₁ = f₂ x₂ := by
    apply (TopCat.mono_iff_injective _).mp H₃
    simp only [← comp_apply, eq₁, eq₂]
    simp only [comp_apply, hx₁, hx₂]
    simp only [← comp_apply, pullback.condition]
  use (pullbackIsoProdSubtype f₁ f₂).inv ⟨⟨x₁, x₂⟩, this⟩
  apply Concrete.limit_ext
  rintro (_ | _ | _) <;>
  simp only [← comp_apply, Category.assoc, limit.lift_π, PullbackCone.mk_π_app_one]
  · simp only [cospan_one, pullbackIsoProdSubtype_inv_fst_assoc, comp_apply,
      pullbackFst_apply, hx₁]
    rw [← limit.w _ WalkingCospan.Hom.inl, cospan_map_inl, comp_apply (g := g₁)]
  · 

Proof state:
case h.a.some.left
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
W X Y Z S T : TopCat
f₁ : W ⟶ S
f₂ : X ⟶ S
g₁ : Y ⟶ T
g₂ : Z ⟶ T
i₁ : W ⟶ Y
i₂ : X ⟶ Z
i₃ : S ⟶ T
H₃ : Mono i₃
eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁
eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂
x✝ : (forget TopCat).obj (pullback g₁ g₂)
x₁ : (forget TopCat).obj W
hx₁ : i₁ x₁ = pullback.fst x✝
x₂ : (forget TopCat).obj X
hx₂ : i₂ x₂ = pullback.snd x✝
this : f₁ x₁ = f₂ x₂
⊢ ((pullbackIsoProdSubtype f₁ f₂).inv ≫
        (PullbackCone.mk (pullback.fst ≫ i₁) (pullback.snd ≫ i₂)
                (_ : (pullback.fst ≫ i₁) ≫ g₁ = (pullback.snd ≫ i₂) ≫ g₂)).π.app
          (some WalkingPair.left))
      { val := (x₁, x₂), property := this } =
    (limit.π (cospan g₁ g₂) (some WalkingPair.left)) x✝

Next tactic:
simp [hx₁]
/-- If the map `S ⟶ T` is mono, then there is a description of the image of `W ×ₛ X ⟶ Y ×ₜ Z`. -/
theorem range_pullback_map {W X Y Z S T : TopCat} (f₁ : W ⟶ S) (f₂ : X ⟶ S) (g₁ : Y ⟶ T)
    (g₂ : Z ⟶ T) (i₁ : W ⟶ Y) (i₂ : X ⟶ Z) (i₃ : S ⟶ T) [H₃ : Mono i₃] (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁)
    (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) :
    Set.range (pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂) =
      (pullback.fst : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₁ ∩
        (pullback.snd : pullback g₁ g₂ ⟶ _) ⁻¹' Set.range i₂ := by
  ext
  constructor
  · rintro ⟨y, rfl⟩
    simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_range, ← comp_apply, limit.lift_π,
      PullbackCone.mk_pt, PullbackCone.mk_π_app]
    simp only [comp_apply, exists_apply_eq_apply, and_self]
  rintro ⟨⟨x₁, hx₁⟩, ⟨x₂, hx₂⟩⟩
  have : f₁ x₁ = f₂ x₂ := by
    apply (TopCat.mono_iff_injective _).mp H₃
    simp only [← comp_apply, eq₁, eq₂]
    simp only [comp_apply, hx₁, hx₂]
    simp only [← comp_apply, pullback.condition]
  use (pullbackIsoProdSubtype f₁ f₂).inv ⟨⟨x₁, x₂⟩, this⟩
  apply Concrete.limit_ext
  rintro (_ | _ | _) <;>
  simp only [← comp_apply, Category.assoc, limit.lift_π, PullbackCone.mk_π_app_one]
  · simp only [cospan_one, pullbackIsoProdSubtype_inv_fst_assoc, comp_apply,
      pullbackFst_apply, hx₁]
    rw [← limit.w _ WalkingCospan.Hom.inl, cospan_map_inl, comp_apply (g := g₁)]
  · simp [hx₁]
  · 

Proof state:
case h.a.some.right
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
W X Y Z S T : TopCat
f₁ : W ⟶ S
f₂ : X ⟶ S
g₁ : Y ⟶ T
g₂ : Z ⟶ T
i₁ : W ⟶ Y
i₂ : X ⟶ Z
i₃ : S ⟶ T
H₃ : Mono i₃
eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁
eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂
x✝ : (forget TopCat).obj (pullback g₁ g₂)
x₁ : (forget TopCat).obj W
hx₁ : i₁ x₁ = pullback.fst x✝
x₂ : (forget TopCat).obj X
hx₂ : i₂ x₂ = pullback.snd x✝
this : f₁ x₁ = f₂ x₂
⊢ ((pullbackIsoProdSubtype f₁ f₂).inv ≫
        (PullbackCone.mk (pullback.fst ≫ i₁) (pullback.snd ≫ i₂)
                (_ : (pullback.fst ≫ i₁) ≫ g₁ = (pullback.snd ≫ i₂) ≫ g₂)).π.app
          (some WalkingPair.right))
      { val := (x₁, x₂), property := this } =
    (limit.π (cospan g₁ g₂) (some WalkingPair.right)) x✝

Next tactic:
simp [hx₂]
theorem pullback_fst_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.fst : pullback f g ⟶ _) = { x : X | ∃ y : Y, f x = g y } := by
  

Proof state:
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
⊢ Set.range ⇑pullback.fst = {x | ∃ y, f x = g y}

Next tactic:
ext x
theorem pullback_fst_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.fst : pullback f g ⟶ _) = { x : X | ∃ y : Y, f x = g y } := by
  ext x
  

Proof state:
case h
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
x : (forget TopCat).obj X
⊢ x ∈ Set.range ⇑pullback.fst ↔ x ∈ {x | ∃ y, f x = g y}

Next tactic:
constructor
theorem pullback_fst_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.fst : pullback f g ⟶ _) = { x : X | ∃ y : Y, f x = g y } := by
  ext x
  constructor
  · 

Proof state:
case h.mp
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
x : (forget TopCat).obj X
⊢ x ∈ Set.range ⇑pullback.fst → x ∈ {x | ∃ y, f x = g y}

Next tactic:
rintro ⟨y, rfl⟩
theorem pullback_fst_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.fst : pullback f g ⟶ _) = { x : X | ∃ y : Y, f x = g y } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    

Proof state:
case h.mp.intro
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
y : (forget TopCat).obj (pullback f g)
⊢ pullback.fst y ∈ {x | ∃ y, f x = g y}

Next tactic:
use (pullback.snd : pullback f g ⟶ _) y
theorem pullback_fst_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.fst : pullback f g ⟶ _) = { x : X | ∃ y : Y, f x = g y } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    use (pullback.snd : pullback f g ⟶ _) y
    

Proof state:
case h
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
y : (forget TopCat).obj (pullback f g)
⊢ f (pullback.fst y) = g (pullback.snd y)

Next tactic:
exact ConcreteCategory.congr_hom pullback.condition y
theorem pullback_fst_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.fst : pullback f g ⟶ _) = { x : X | ∃ y : Y, f x = g y } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    use (pullback.snd : pullback f g ⟶ _) y
    exact ConcreteCategory.congr_hom pullback.condition y
  · 

Proof state:
case h.mpr
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
x : (forget TopCat).obj X
⊢ x ∈ {x | ∃ y, f x = g y} → x ∈ Set.range ⇑pullback.fst

Next tactic:
rintro ⟨y, eq⟩
theorem pullback_fst_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.fst : pullback f g ⟶ _) = { x : X | ∃ y : Y, f x = g y } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    use (pullback.snd : pullback f g ⟶ _) y
    exact ConcreteCategory.congr_hom pullback.condition y
  · rintro ⟨y, eq⟩
    

Proof state:
case h.mpr.intro
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
x : (forget TopCat).obj X
y : ↑Y
eq : f x = g y
⊢ x ∈ Set.range ⇑pullback.fst

Next tactic:
use (TopCat.pullbackIsoProdSubtype f g).inv ⟨⟨x, y⟩, eq⟩
theorem pullback_fst_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.fst : pullback f g ⟶ _) = { x : X | ∃ y : Y, f x = g y } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    use (pullback.snd : pullback f g ⟶ _) y
    exact ConcreteCategory.congr_hom pullback.condition y
  · rintro ⟨y, eq⟩
    use (TopCat.pullbackIsoProdSubtype f g).inv ⟨⟨x, y⟩, eq⟩
    

Proof state:
case h
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
x : (forget TopCat).obj X
y : ↑Y
eq : f x = g y
⊢ pullback.fst ((pullbackIsoProdSubtype f g).inv { val := (x, y), property := eq }) = x

Next tactic:
simp
theorem pullback_snd_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.snd : pullback f g ⟶ _) = { y : Y | ∃ x : X, f x = g y } := by
  

Proof state:
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
⊢ Set.range ⇑pullback.snd = {y | ∃ x, f x = g y}

Next tactic:
ext y
theorem pullback_snd_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.snd : pullback f g ⟶ _) = { y : Y | ∃ x : X, f x = g y } := by
  ext y
  

Proof state:
case h
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
y : (forget TopCat).obj Y
⊢ y ∈ Set.range ⇑pullback.snd ↔ y ∈ {y | ∃ x, f x = g y}

Next tactic:
constructor
theorem pullback_snd_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.snd : pullback f g ⟶ _) = { y : Y | ∃ x : X, f x = g y } := by
  ext y
  constructor
  · 

Proof state:
case h.mp
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
y : (forget TopCat).obj Y
⊢ y ∈ Set.range ⇑pullback.snd → y ∈ {y | ∃ x, f x = g y}

Next tactic:
rintro ⟨x, rfl⟩
theorem pullback_snd_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.snd : pullback f g ⟶ _) = { y : Y | ∃ x : X, f x = g y } := by
  ext y
  constructor
  · rintro ⟨x, rfl⟩
    

Proof state:
case h.mp.intro
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
x : (forget TopCat).obj (pullback f g)
⊢ pullback.snd x ∈ {y | ∃ x, f x = g y}

Next tactic:
use (pullback.fst : pullback f g ⟶ _) x
theorem pullback_snd_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.snd : pullback f g ⟶ _) = { y : Y | ∃ x : X, f x = g y } := by
  ext y
  constructor
  · rintro ⟨x, rfl⟩
    use (pullback.fst : pullback f g ⟶ _) x
    

Proof state:
case h
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
x : (forget TopCat).obj (pullback f g)
⊢ f (pullback.fst x) = g (pullback.snd x)

Next tactic:
exact ConcreteCategory.congr_hom pullback.condition x
theorem pullback_snd_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.snd : pullback f g ⟶ _) = { y : Y | ∃ x : X, f x = g y } := by
  ext y
  constructor
  · rintro ⟨x, rfl⟩
    use (pullback.fst : pullback f g ⟶ _) x
    exact ConcreteCategory.congr_hom pullback.condition x
  · 

Proof state:
case h.mpr
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
y : (forget TopCat).obj Y
⊢ y ∈ {y | ∃ x, f x = g y} → y ∈ Set.range ⇑pullback.snd

Next tactic:
rintro ⟨x, eq⟩
theorem pullback_snd_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.snd : pullback f g ⟶ _) = { y : Y | ∃ x : X, f x = g y } := by
  ext y
  constructor
  · rintro ⟨x, rfl⟩
    use (pullback.fst : pullback f g ⟶ _) x
    exact ConcreteCategory.congr_hom pullback.condition x
  · rintro ⟨x, eq⟩
    

Proof state:
case h.mpr.intro
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
y : (forget TopCat).obj Y
x : ↑X
eq : f x = g y
⊢ y ∈ Set.range ⇑pullback.snd

Next tactic:
use (TopCat.pullbackIsoProdSubtype f g).inv ⟨⟨x, y⟩, eq⟩
theorem pullback_snd_range {X Y S : TopCat} (f : X ⟶ S) (g : Y ⟶ S) :
    Set.range (pullback.snd : pullback f g ⟶ _) = { y : Y | ∃ x : X, f x = g y } := by
  ext y
  constructor
  · rintro ⟨x, rfl⟩
    use (pullback.fst : pullback f g ⟶ _) x
    exact ConcreteCategory.congr_hom pullback.condition x
  · rintro ⟨x, eq⟩
    use (TopCat.pullbackIsoProdSubtype f g).inv ⟨⟨x, y⟩, eq⟩
    

Proof state:
case h
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z : TopCat
X Y S : TopCat
f : X ⟶ S
g : Y ⟶ S
y : (forget TopCat).obj Y
x : ↑X
eq : f x = g y
⊢ pullback.snd ((pullbackIsoProdSubtype f g).inv { val := (x, y), property := eq }) = y

Next tactic:
simp
/-- If there is a diagram where the morphisms `W ⟶ Y` and `X ⟶ Z` are embeddings,
then the induced morphism `W ×ₛ X ⟶ Y ×ₜ Z` is also an embedding.

  W ⟶ Y
    ↘      ↘
      S ⟶ T
    ↗      ↗
  X ⟶ Z
-/
theorem pullback_map_embedding_of_embeddings {W X Y Z S T : TopCat.{u}} (f₁ : W ⟶ S) (f₂ : X ⟶ S)
    (g₁ : Y ⟶ T) (g₂ : Z ⟶ T) {i₁ : W ⟶ Y} {i₂ : X ⟶ Z} (H₁ : Embedding i₁) (H₂ : Embedding i₂)
    (i₃ : S ⟶ T) (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁) (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) :
    Embedding (pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂) := by
  

Proof state:
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ W X Y Z S T : TopCat
f₁ : W ⟶ S
f₂ : X ⟶ S
g₁ : Y ⟶ T
g₂ : Z ⟶ T
i₁ : W ⟶ Y
i₂ : X ⟶ Z
H₁ : Embedding ⇑i₁
H₂ : Embedding ⇑i₂
i₃ : S ⟶ T
eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁
eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂
⊢ Embedding ⇑(pullback.map f₁ f₂ g₁ g₂ i₁ i₂ i₃ eq₁ eq₂)

Next tactic:
refine'
    embedding_of_embedding_compose (ContinuousMap.continuous_toFun _)
      (show Continuous (prod.lift pullback.fst pullback.snd : pullback g₁ g₂ ⟶ Y ⨯ Z) from
        ContinuousMap.continuous_toFun _)
      _