l3lab/ntp-mathlib huggingface dataset:
theorem pullback_topology {X Y Z : TopCat.{u}} (f : X ⟶ Z) (g : Y ⟶ Z) :
    (pullback f g).str =
      induced (pullback.fst : pullback f g ⟶ _) X.str ⊓
        induced (pullback.snd : pullback f g ⟶ _) Y.str := by
  

Proof state:
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
⊢ (pullback f g).str = induced (⇑pullback.fst) X.str ⊓ induced (⇑pullback.snd) Y.str

Next tactic:
let homeo := homeoOfIso (pullbackIsoProdSubtype f g)
theorem pullback_topology {X Y Z : TopCat.{u}} (f : X ⟶ Z) (g : Y ⟶ Z) :
    (pullback f g).str =
      induced (pullback.fst : pullback f g ⟶ _) X.str ⊓
        induced (pullback.snd : pullback f g ⟶ _) Y.str := by
  let homeo := homeoOfIso (pullbackIsoProdSubtype f g)
  

Proof state:
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
homeo : ↑(pullback f g) ≃ₜ ↑(of { p // f p.1 = g p.2 }) := homeoOfIso (pullbackIsoProdSubtype f g)
⊢ (pullback f g).str = induced (⇑pullback.fst) X.str ⊓ induced (⇑pullback.snd) Y.str

Next tactic:
refine' homeo.inducing.induced.trans _
theorem pullback_topology {X Y Z : TopCat.{u}} (f : X ⟶ Z) (g : Y ⟶ Z) :
    (pullback f g).str =
      induced (pullback.fst : pullback f g ⟶ _) X.str ⊓
        induced (pullback.snd : pullback f g ⟶ _) Y.str := by
  let homeo := homeoOfIso (pullbackIsoProdSubtype f g)
  refine' homeo.inducing.induced.trans _
  

Proof state:
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
homeo : ↑(pullback f g) ≃ₜ ↑(of { p // f p.1 = g p.2 }) := homeoOfIso (pullbackIsoProdSubtype f g)
⊢ induced (⇑homeo) (topologicalSpace_coe (of { p // f p.1 = g p.2 })) =
    induced (⇑pullback.fst) X.str ⊓ induced (⇑pullback.snd) Y.str

Next tactic:
change induced homeo (induced _ ( (induced Prod.fst X.str) ⊓ (induced Prod.snd Y.str))) = _
theorem pullback_topology {X Y Z : TopCat.{u}} (f : X ⟶ Z) (g : Y ⟶ Z) :
    (pullback f g).str =
      induced (pullback.fst : pullback f g ⟶ _) X.str ⊓
        induced (pullback.snd : pullback f g ⟶ _) Y.str := by
  let homeo := homeoOfIso (pullbackIsoProdSubtype f g)
  refine' homeo.inducing.induced.trans _
  change induced homeo (induced _ ( (induced Prod.fst X.str) ⊓ (induced Prod.snd Y.str))) = _
  

Proof state:
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
homeo : ↑(pullback f g) ≃ₜ ↑(of { p // f p.1 = g p.2 }) := homeoOfIso (pullbackIsoProdSubtype f g)
⊢ induced (⇑homeo) (induced Subtype.val (induced Prod.fst X.str ⊓ induced Prod.snd Y.str)) =
    induced (⇑pullback.fst) X.str ⊓ induced (⇑pullback.snd) Y.str

Next tactic:
simp only [induced_compose, induced_inf]
theorem pullback_topology {X Y Z : TopCat.{u}} (f : X ⟶ Z) (g : Y ⟶ Z) :
    (pullback f g).str =
      induced (pullback.fst : pullback f g ⟶ _) X.str ⊓
        induced (pullback.snd : pullback f g ⟶ _) Y.str := by
  let homeo := homeoOfIso (pullbackIsoProdSubtype f g)
  refine' homeo.inducing.induced.trans _
  change induced homeo (induced _ ( (induced Prod.fst X.str) ⊓ (induced Prod.snd Y.str))) = _
  simp only [induced_compose, induced_inf]
  

Proof state:
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
homeo : ↑(pullback f g) ≃ₜ ↑(of { p // f p.1 = g p.2 }) := homeoOfIso (pullbackIsoProdSubtype f g)
⊢ induced ((Prod.fst ∘ Subtype.val) ∘ ⇑(homeoOfIso (pullbackIsoProdSubtype f g))) X.str ⊓
      induced ((Prod.snd ∘ Subtype.val) ∘ ⇑(homeoOfIso (pullbackIsoProdSubtype f g))) Y.str =
    induced (⇑pullback.fst) X.str ⊓ induced (⇑pullback.snd) Y.str

Next tactic:
congr
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  

Proof state:
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
⊢ Set.range ⇑(prod.lift pullback.fst pullback.snd) = {x | (prod.fst ≫ f) x = (prod.snd ≫ g) x}

Next tactic:
ext x
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  

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

Next tactic:
constructor
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · 

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

Next tactic:
rintro ⟨y, rfl⟩
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := 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 Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
y : (forget TopCat).obj (pullback f g)
⊢ (prod.lift pullback.fst pullback.snd) y ∈ {x | (prod.fst ≫ f) x = (prod.snd ≫ g) x}

Next tactic:
simp only [← comp_apply, Set.mem_setOf_eq]
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    

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

Next tactic:
congr 1
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    congr 1
    

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

Next tactic:
simp [pullback.condition]
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    congr 1
    simp [pullback.condition]
  · 

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

Next tactic:
rintro (h : f (_, _).1 = g (_, _).2)
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    congr 1
    simp [pullback.condition]
  · rintro (h : f (_, _).1 = g (_, _).2)
    

Proof state:
case h.mpr
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
x : (forget TopCat).obj (X ⨯ Y)
h : f (prod.fst x, ?m.52257).1 = g (?m.52291, prod.snd x).2
⊢ x ∈ Set.range ⇑(prod.lift pullback.fst pullback.snd)

Next tactic:
use (pullbackIsoProdSubtype f g).inv ⟨⟨_, _⟩, h⟩
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    congr 1
    simp [pullback.condition]
  · rintro (h : f (_, _).1 = g (_, _).2)
    use (pullbackIsoProdSubtype f g).inv ⟨⟨_, _⟩, h⟩
    

Proof state:
case h
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
x : (forget TopCat).obj (X ⨯ Y)
h : f (prod.fst x, prod.snd x).1 = g (prod.fst x, prod.snd x).2
⊢ (prod.lift pullback.fst pullback.snd)
      ((pullbackIsoProdSubtype f g).inv { val := (prod.fst x, prod.snd x), property := h }) =
    x

Next tactic:
apply Concrete.limit_ext
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    congr 1
    simp [pullback.condition]
  · rintro (h : f (_, _).1 = g (_, _).2)
    use (pullbackIsoProdSubtype f g).inv ⟨⟨_, _⟩, h⟩
    apply Concrete.limit_ext
    

Proof state:
case h.a
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
x : (forget TopCat).obj (X ⨯ Y)
h : f (prod.fst x, prod.snd x).1 = g (prod.fst x, prod.snd x).2
⊢ ∀ (j : Discrete WalkingPair),
    (limit.π (pair X Y) j)
        ((prod.lift pullback.fst pullback.snd)
          ((pullbackIsoProdSubtype f g).inv { val := (prod.fst x, prod.snd x), property := h })) =
      (limit.π (pair X Y) j) x

Next tactic:
rintro ⟨⟨⟩⟩
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    congr 1
    simp [pullback.condition]
  · rintro (h : f (_, _).1 = g (_, _).2)
    use (pullbackIsoProdSubtype f g).inv ⟨⟨_, _⟩, h⟩
    apply Concrete.limit_ext
    rintro ⟨⟨⟩⟩ <;>
    

Proof state:
case h.a.mk.left
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
x : (forget TopCat).obj (X ⨯ Y)
h : f (prod.fst x, prod.snd x).1 = g (prod.fst x, prod.snd x).2
⊢ (limit.π (pair X Y) { as := WalkingPair.left })
      ((prod.lift pullback.fst pullback.snd)
        ((pullbackIsoProdSubtype f g).inv { val := (prod.fst x, prod.snd x), property := h })) =
    (limit.π (pair X Y) { as := WalkingPair.left }) x

Next tactic:
rw [← comp_apply, ← comp_apply, limit.lift_π]
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    congr 1
    simp [pullback.condition]
  · rintro (h : f (_, _).1 = g (_, _).2)
    use (pullbackIsoProdSubtype f g).inv ⟨⟨_, _⟩, h⟩
    apply Concrete.limit_ext
    rintro ⟨⟨⟩⟩ <;>
    

Proof state:
case h.a.mk.right
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
x : (forget TopCat).obj (X ⨯ Y)
h : f (prod.fst x, prod.snd x).1 = g (prod.fst x, prod.snd x).2
⊢ (limit.π (pair X Y) { as := WalkingPair.right })
      ((prod.lift pullback.fst pullback.snd)
        ((pullbackIsoProdSubtype f g).inv { val := (prod.fst x, prod.snd x), property := h })) =
    (limit.π (pair X Y) { as := WalkingPair.right }) x

Next tactic:
rw [← comp_apply, ← comp_apply, limit.lift_π]
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    congr 1
    simp [pullback.condition]
  · rintro (h : f (_, _).1 = g (_, _).2)
    use (pullbackIsoProdSubtype f g).inv ⟨⟨_, _⟩, h⟩
    apply Concrete.limit_ext
    rintro ⟨⟨⟩⟩ <;>
    rw [← comp_apply, ← comp_apply, limit.lift_π] <;>
    -- This used to be `simp` before leanprover/lean4#2644
    

Proof state:
case h.a.mk.left
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
x : (forget TopCat).obj (X ⨯ Y)
h : f (prod.fst x, prod.snd x).1 = g (prod.fst x, prod.snd x).2
⊢ ((pullbackIsoProdSubtype f g).inv ≫ (BinaryFan.mk pullback.fst pullback.snd).π.app { as := WalkingPair.left })
      { val := (prod.fst x, prod.snd x), property := h } =
    (limit.π (pair X Y) { as := WalkingPair.left }) x

Next tactic:
aesop_cat
theorem range_pullback_to_prod {X Y Z : TopCat} (f : X ⟶ Z) (g : Y ⟶ Z) :
    Set.range (prod.lift pullback.fst pullback.snd : pullback f g ⟶ X ⨯ Y) =
      { x | (Limits.prod.fst ≫ f) x = (Limits.prod.snd ≫ g) x } := by
  ext x
  constructor
  · rintro ⟨y, rfl⟩
    simp only [← comp_apply, Set.mem_setOf_eq]
    congr 1
    simp [pullback.condition]
  · rintro (h : f (_, _).1 = g (_, _).2)
    use (pullbackIsoProdSubtype f g).inv ⟨⟨_, _⟩, h⟩
    apply Concrete.limit_ext
    rintro ⟨⟨⟩⟩ <;>
    rw [← comp_apply, ← comp_apply, limit.lift_π] <;>
    -- This used to be `simp` before leanprover/lean4#2644
    

Proof state:
case h.a.mk.right
J : Type v
inst✝ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X Y Z : TopCat
f : X ⟶ Z
g : Y ⟶ Z
x : (forget TopCat).obj (X ⨯ Y)
h : f (prod.fst x, prod.snd x).1 = g (prod.fst x, prod.snd x).2
⊢ ((pullbackIsoProdSubtype f g).inv ≫ (BinaryFan.mk pullback.fst pullback.snd).π.app { as := WalkingPair.right })
      { val := (prod.fst x, prod.snd x), property := h } =
    (limit.π (pair X Y) { as := WalkingPair.right }) x

Next tactic:
aesop_cat
/-- The pullback along an embedding is (isomorphic to) the preimage. -/
noncomputable
def pullbackHomeoPreimage
    {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] [TopologicalSpace Z]
    (f : X → Z) (hf : Continuous f) (g : Y → Z) (hg : Embedding g) :
    { p : X × Y // f p.1 = g p.2 } ≃ₜ f ⁻¹' Set.range g where
  toFun := fun x ↦ ⟨x.1.1, _, x.2.symm⟩
  invFun := fun x ↦ ⟨⟨x.1, Exists.choose x.2⟩, (Exists.choose_spec x.2).symm⟩
  left_inv := by
    

Proof state:
J : Type v
inst✝³ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X : Type u_1
Y : Type u_2
Z : Type u_3
inst✝² : TopologicalSpace X
inst✝¹ : TopologicalSpace Y
inst✝ : TopologicalSpace Z
f : X → Z
hf : Continuous f
g : Y → Z
hg : Embedding g
⊢ Function.LeftInverse
    (fun x =>
      { val := (↑x, Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g)),
        property := (_ : f ↑x = g (Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g))) })
    fun x => { val := (↑x).1, property := (_ : ∃ y, g y = f (↑x).1) }

Next tactic:
intro x
/-- The pullback along an embedding is (isomorphic to) the preimage. -/
noncomputable
def pullbackHomeoPreimage
    {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] [TopologicalSpace Z]
    (f : X → Z) (hf : Continuous f) (g : Y → Z) (hg : Embedding g) :
    { p : X × Y // f p.1 = g p.2 } ≃ₜ f ⁻¹' Set.range g where
  toFun := fun x ↦ ⟨x.1.1, _, x.2.symm⟩
  invFun := fun x ↦ ⟨⟨x.1, Exists.choose x.2⟩, (Exists.choose_spec x.2).symm⟩
  left_inv := by
    intro x
    

Proof state:
J : Type v
inst✝³ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X : Type u_1
Y : Type u_2
Z : Type u_3
inst✝² : TopologicalSpace X
inst✝¹ : TopologicalSpace Y
inst✝ : TopologicalSpace Z
f : X → Z
hf : Continuous f
g : Y → Z
hg : Embedding g
x : { p // f p.1 = g p.2 }
⊢ (fun x =>
        { val := (↑x, Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g)),
          property := (_ : f ↑x = g (Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g))) })
      ((fun x => { val := (↑x).1, property := (_ : ∃ y, g y = f (↑x).1) }) x) =
    x

Next tactic:
ext
/-- The pullback along an embedding is (isomorphic to) the preimage. -/
noncomputable
def pullbackHomeoPreimage
    {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] [TopologicalSpace Z]
    (f : X → Z) (hf : Continuous f) (g : Y → Z) (hg : Embedding g) :
    { p : X × Y // f p.1 = g p.2 } ≃ₜ f ⁻¹' Set.range g where
  toFun := fun x ↦ ⟨x.1.1, _, x.2.symm⟩
  invFun := fun x ↦ ⟨⟨x.1, Exists.choose x.2⟩, (Exists.choose_spec x.2).symm⟩
  left_inv := by
    intro x
    ext <;> 

Proof state:
case a.a
J : Type v
inst✝³ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X : Type u_1
Y : Type u_2
Z : Type u_3
inst✝² : TopologicalSpace X
inst✝¹ : TopologicalSpace Y
inst✝ : TopologicalSpace Z
f : X → Z
hf : Continuous f
g : Y → Z
hg : Embedding g
x : { p // f p.1 = g p.2 }
⊢ (↑((fun x =>
            { val := (↑x, Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g)),
              property := (_ : f ↑x = g (Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g))) })
          ((fun x => { val := (↑x).1, property := (_ : ∃ y, g y = f (↑x).1) }) x))).1 =
    (↑x).1

Next tactic:
dsimp
/-- The pullback along an embedding is (isomorphic to) the preimage. -/
noncomputable
def pullbackHomeoPreimage
    {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] [TopologicalSpace Z]
    (f : X → Z) (hf : Continuous f) (g : Y → Z) (hg : Embedding g) :
    { p : X × Y // f p.1 = g p.2 } ≃ₜ f ⁻¹' Set.range g where
  toFun := fun x ↦ ⟨x.1.1, _, x.2.symm⟩
  invFun := fun x ↦ ⟨⟨x.1, Exists.choose x.2⟩, (Exists.choose_spec x.2).symm⟩
  left_inv := by
    intro x
    ext <;> 

Proof state:
case a.a
J : Type v
inst✝³ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X : Type u_1
Y : Type u_2
Z : Type u_3
inst✝² : TopologicalSpace X
inst✝¹ : TopologicalSpace Y
inst✝ : TopologicalSpace Z
f : X → Z
hf : Continuous f
g : Y → Z
hg : Embedding g
x : { p // f p.1 = g p.2 }
⊢ (↑((fun x =>
            { val := (↑x, Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g)),
              property := (_ : f ↑x = g (Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g))) })
          ((fun x => { val := (↑x).1, property := (_ : ∃ y, g y = f (↑x).1) }) x))).2 =
    (↑x).2

Next tactic:
dsimp
/-- The pullback along an embedding is (isomorphic to) the preimage. -/
noncomputable
def pullbackHomeoPreimage
    {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] [TopologicalSpace Z]
    (f : X → Z) (hf : Continuous f) (g : Y → Z) (hg : Embedding g) :
    { p : X × Y // f p.1 = g p.2 } ≃ₜ f ⁻¹' Set.range g where
  toFun := fun x ↦ ⟨x.1.1, _, x.2.symm⟩
  invFun := fun x ↦ ⟨⟨x.1, Exists.choose x.2⟩, (Exists.choose_spec x.2).symm⟩
  left_inv := by
    intro x
    ext <;> dsimp
    

Proof state:
case a.a
J : Type v
inst✝³ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X : Type u_1
Y : Type u_2
Z : Type u_3
inst✝² : TopologicalSpace X
inst✝¹ : TopologicalSpace Y
inst✝ : TopologicalSpace Z
f : X → Z
hf : Continuous f
g : Y → Z
hg : Embedding g
x : { p // f p.1 = g p.2 }
⊢ Exists.choose (_ : ∃ y, g y = f (↑x).1) = (↑x).2

Next tactic:
apply hg.inj
/-- The pullback along an embedding is (isomorphic to) the preimage. -/
noncomputable
def pullbackHomeoPreimage
    {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] [TopologicalSpace Z]
    (f : X → Z) (hf : Continuous f) (g : Y → Z) (hg : Embedding g) :
    { p : X × Y // f p.1 = g p.2 } ≃ₜ f ⁻¹' Set.range g where
  toFun := fun x ↦ ⟨x.1.1, _, x.2.symm⟩
  invFun := fun x ↦ ⟨⟨x.1, Exists.choose x.2⟩, (Exists.choose_spec x.2).symm⟩
  left_inv := by
    intro x
    ext <;> dsimp
    apply hg.inj
    

Proof state:
case a.a.a
J : Type v
inst✝³ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X : Type u_1
Y : Type u_2
Z : Type u_3
inst✝² : TopologicalSpace X
inst✝¹ : TopologicalSpace Y
inst✝ : TopologicalSpace Z
f : X → Z
hf : Continuous f
g : Y → Z
hg : Embedding g
x : { p // f p.1 = g p.2 }
⊢ g (Exists.choose (_ : ∃ y, g y = f (↑x).1)) = g (↑x).2

Next tactic:
convert x.prop
/-- The pullback along an embedding is (isomorphic to) the preimage. -/
noncomputable
def pullbackHomeoPreimage
    {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] [TopologicalSpace Z]
    (f : X → Z) (hf : Continuous f) (g : Y → Z) (hg : Embedding g) :
    { p : X × Y // f p.1 = g p.2 } ≃ₜ f ⁻¹' Set.range g where
  toFun := fun x ↦ ⟨x.1.1, _, x.2.symm⟩
  invFun := fun x ↦ ⟨⟨x.1, Exists.choose x.2⟩, (Exists.choose_spec x.2).symm⟩
  left_inv := by
    intro x
    ext <;> dsimp
    apply hg.inj
    convert x.prop
    

Proof state:
case h.e'_2
J : Type v
inst✝³ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X : Type u_1
Y : Type u_2
Z : Type u_3
inst✝² : TopologicalSpace X
inst✝¹ : TopologicalSpace Y
inst✝ : TopologicalSpace Z
f : X → Z
hf : Continuous f
g : Y → Z
hg : Embedding g
x : { p // f p.1 = g p.2 }
⊢ g (Exists.choose (_ : ∃ y, g y = f (↑x).1)) = f (↑x).1

Next tactic:
exact Exists.choose_spec (p := fun y ↦ g y = f (↑x : X × Y).1) _
/-- The pullback along an embedding is (isomorphic to) the preimage. -/
noncomputable
def pullbackHomeoPreimage
    {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] [TopologicalSpace Z]
    (f : X → Z) (hf : Continuous f) (g : Y → Z) (hg : Embedding g) :
    { p : X × Y // f p.1 = g p.2 } ≃ₜ f ⁻¹' Set.range g where
  toFun := fun x ↦ ⟨x.1.1, _, x.2.symm⟩
  invFun := fun x ↦ ⟨⟨x.1, Exists.choose x.2⟩, (Exists.choose_spec x.2).symm⟩
  left_inv := by
    intro x
    ext <;> dsimp
    apply hg.inj
    convert x.prop
    exact Exists.choose_spec (p := fun y ↦ g y = f (↑x : X × Y).1) _
  right_inv := fun x ↦ rfl
  continuous_toFun := by
    

Proof state:
J : Type v
inst✝³ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X : Type u_1
Y : Type u_2
Z : Type u_3
inst✝² : TopologicalSpace X
inst✝¹ : TopologicalSpace Y
inst✝ : TopologicalSpace Z
f : X → Z
hf : Continuous f
g : Y → Z
hg : Embedding g
⊢ Continuous
    { toFun := fun x => { val := (↑x).1, property := (_ : ∃ y, g y = f (↑x).1) },
        invFun := fun x =>
          { val := (↑x, Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g)),
            property := (_ : f ↑x = g (Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g))) },
        left_inv :=
          (_ :
            ∀ (x : { p // f p.1 = g p.2 }),
              (fun x =>
                    { val := (↑x, Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g)),
                      property := (_ : f ↑x = g (Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g))) })
                  ((fun x => { val := (↑x).1, property := (_ : ∃ y, g y = f (↑x).1) }) x) =
                x),
        right_inv :=
          (_ :
            ∀ (x : ↑(f ⁻¹' Set.range g)),
              (fun x => { val := (↑x).1, property := (_ : ∃ y, g y = f (↑x).1) })
                  ((fun x =>
                      { val := (↑x, Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g)),
                        property := (_ : f ↑x = g (Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g))) })
                    x) =
                (fun x => { val := (↑x).1, property := (_ : ∃ y, g y = f (↑x).1) })
                  ((fun x =>
                      { val := (↑x, Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g)),
                        property := (_ : f ↑x = g (Exists.choose (_ : ↑x ∈ f ⁻¹' Set.range g))) })
                    x)) }.toFun

Next tactic:
apply Continuous.subtype_mk
/-- The pullback along an embedding is (isomorphic to) the preimage. -/
noncomputable
def pullbackHomeoPreimage
    {X Y Z : Type*} [TopologicalSpace X] [TopologicalSpace Y] [TopologicalSpace Z]
    (f : X → Z) (hf : Continuous f) (g : Y → Z) (hg : Embedding g) :
    { p : X × Y // f p.1 = g p.2 } ≃ₜ f ⁻¹' Set.range g where
  toFun := fun x ↦ ⟨x.1.1, _, x.2.symm⟩
  invFun := fun x ↦ ⟨⟨x.1, Exists.choose x.2⟩, (Exists.choose_spec x.2).symm⟩
  left_inv := by
    intro x
    ext <;> dsimp
    apply hg.inj
    convert x.prop
    exact Exists.choose_spec (p := fun y ↦ g y = f (↑x : X × Y).1) _
  right_inv := fun x ↦ rfl
  continuous_toFun := by
    apply Continuous.subtype_mk
    

Proof state:
case h
J : Type v
inst✝³ : SmallCategory J
X✝ Y✝ Z✝ : TopCat
X : Type u_1
Y : Type u_2
Z : Type u_3
inst✝² : TopologicalSpace X
inst✝¹ : TopologicalSpace Y
inst✝ : TopologicalSpace Z
f : X → Z
hf : Continuous f
g : Y → Z
hg : Embedding g
⊢ Continuous fun x => (↑x).1

Next tactic:
exact continuous_fst.comp continuous_subtype_val