l3lab/ntp-mathlib huggingface dataset:
open Function.Pullback in
theorem IsSeparatedMap.pullback {f : X → Y} (sep : IsSeparatedMap f) (g : A → Y) :
    IsSeparatedMap (@snd X Y A f g) := by
  rw [isSeparatedMap_iff_isClosed_diagonal] at sep ⊢
  rw [← preimage_map_fst_pullbackDiagonal]
  

Proof state:
X : Type u_1
Y : Sort u_2
A : Type u_3
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
sep : IsClosed (Function.pullbackDiagonal f)
g : A → Y
⊢ IsClosed (Function.PullbackSelf.map_fst ⁻¹' Function.pullbackDiagonal f)

Next tactic:
refine sep.preimage (Continuous.mapPullback ?_ ?_)
open Function.Pullback in
theorem IsSeparatedMap.pullback {f : X → Y} (sep : IsSeparatedMap f) (g : A → Y) :
    IsSeparatedMap (@snd X Y A f g) := by
  rw [isSeparatedMap_iff_isClosed_diagonal] at sep ⊢
  rw [← preimage_map_fst_pullbackDiagonal]
  refine sep.preimage (Continuous.mapPullback ?_ ?_) <;>
  

Proof state:
case refine_1
X : Type u_1
Y : Sort u_2
A : Type u_3
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
sep : IsClosed (Function.pullbackDiagonal f)
g : A → Y
⊢ Continuous fst

Next tactic:
apply_rules [continuous_fst, continuous_subtype_val, Continuous.comp]
open Function.Pullback in
theorem IsSeparatedMap.pullback {f : X → Y} (sep : IsSeparatedMap f) (g : A → Y) :
    IsSeparatedMap (@snd X Y A f g) := by
  rw [isSeparatedMap_iff_isClosed_diagonal] at sep ⊢
  rw [← preimage_map_fst_pullbackDiagonal]
  refine sep.preimage (Continuous.mapPullback ?_ ?_) <;>
  

Proof state:
case refine_2
X : Type u_1
Y : Sort u_2
A : Type u_3
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
sep : IsClosed (Function.pullbackDiagonal f)
g : A → Y
⊢ Continuous fst

Next tactic:
apply_rules [continuous_fst, continuous_subtype_val, Continuous.comp]
theorem IsSeparatedMap.comp_right {f : X → Y} (sep : IsSeparatedMap f) {g : A → X}
    (cont : Continuous g) (inj : g.Injective) : IsSeparatedMap (f ∘ g) := by
  

Proof state:
X : Type u_1
Y : Sort u_2
A : Type u_3
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
sep : IsSeparatedMap f
g : A → X
cont : Continuous g
inj : Function.Injective g
⊢ IsSeparatedMap (f ∘ g)

Next tactic:
rw [isSeparatedMap_iff_isClosed_diagonal] at sep ⊢
theorem IsSeparatedMap.comp_right {f : X → Y} (sep : IsSeparatedMap f) {g : A → X}
    (cont : Continuous g) (inj : g.Injective) : IsSeparatedMap (f ∘ g) := by
  rw [isSeparatedMap_iff_isClosed_diagonal] at sep ⊢
  

Proof state:
X : Type u_1
Y : Sort u_2
A : Type u_3
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
sep : IsClosed (Function.pullbackDiagonal f)
g : A → X
cont : Continuous g
inj : Function.Injective g
⊢ IsClosed (Function.pullbackDiagonal (f ∘ g))

Next tactic:
rw [← inj.preimage_pullbackDiagonal]
theorem IsSeparatedMap.comp_right {f : X → Y} (sep : IsSeparatedMap f) {g : A → X}
    (cont : Continuous g) (inj : g.Injective) : IsSeparatedMap (f ∘ g) := by
  rw [isSeparatedMap_iff_isClosed_diagonal] at sep ⊢
  rw [← inj.preimage_pullbackDiagonal]
  

Proof state:
X : Type u_1
Y : Sort u_2
A : Type u_3
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
sep : IsClosed (Function.pullbackDiagonal f)
g : A → X
cont : Continuous g
inj : Function.Injective g
⊢ IsClosed (Function.mapPullback g id g (_ : f ∘ g = f ∘ g) (_ : f ∘ g = f ∘ g) ⁻¹' Function.pullbackDiagonal f)

Next tactic:
exact sep.preimage (cont.mapPullback cont)
lemma isLocallyInjective_iff_nhds {f : X → Y} :
    IsLocallyInjective f ↔ ∀ x : X, ∃ U ∈ 𝓝 x, U.InjOn f := by
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type ?u.15215
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
⊢ IsLocallyInjective f ↔ ∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn f U

Next tactic:
constructor
lemma isLocallyInjective_iff_nhds {f : X → Y} :
    IsLocallyInjective f ↔ ∀ x : X, ∃ U ∈ 𝓝 x, U.InjOn f := by
  constructor <;> 

Proof state:
case mp
X : Type u_1
Y : Type u_2
A : Type ?u.15215
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
⊢ IsLocallyInjective f → ∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn f U

Next tactic:
intro h x
lemma isLocallyInjective_iff_nhds {f : X → Y} :
    IsLocallyInjective f ↔ ∀ x : X, ∃ U ∈ 𝓝 x, U.InjOn f := by
  constructor <;> 

Proof state:
case mpr
X : Type u_1
Y : Type u_2
A : Type ?u.15215
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
⊢ (∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn f U) → IsLocallyInjective f

Next tactic:
intro h x
lemma isLocallyInjective_iff_nhds {f : X → Y} :
    IsLocallyInjective f ↔ ∀ x : X, ∃ U ∈ 𝓝 x, U.InjOn f := by
  constructor <;> intro h x
  · 

Proof state:
case mp
X : Type u_1
Y : Type u_2
A : Type ?u.15215
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
h : IsLocallyInjective f
x : X
⊢ ∃ U ∈ 𝓝 x, Set.InjOn f U

Next tactic:
obtain ⟨U, ho, hm, hi⟩ := h x
lemma isLocallyInjective_iff_nhds {f : X → Y} :
    IsLocallyInjective f ↔ ∀ x : X, ∃ U ∈ 𝓝 x, U.InjOn f := by
  constructor <;> intro h x
  · obtain ⟨U, ho, hm, hi⟩ := h x; 

Proof state:
case mp.intro.intro.intro
X : Type u_1
Y : Type u_2
A : Type ?u.15215
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
h : IsLocallyInjective f
x : X
U : Set X
ho : IsOpen U
hm : x ∈ U
hi : Set.InjOn f U
⊢ ∃ U ∈ 𝓝 x, Set.InjOn f U

Next tactic:
exact ⟨U, ho.mem_nhds hm, hi⟩
lemma isLocallyInjective_iff_nhds {f : X → Y} :
    IsLocallyInjective f ↔ ∀ x : X, ∃ U ∈ 𝓝 x, U.InjOn f := by
  constructor <;> intro h x
  · obtain ⟨U, ho, hm, hi⟩ := h x; exact ⟨U, ho.mem_nhds hm, hi⟩
  · 

Proof state:
case mpr
X : Type u_1
Y : Type u_2
A : Type ?u.15215
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
h : ∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn f U
x : X
⊢ ∃ U, IsOpen U ∧ x ∈ U ∧ Set.InjOn f U

Next tactic:
obtain ⟨U, hn, hi⟩ := h x
lemma isLocallyInjective_iff_nhds {f : X → Y} :
    IsLocallyInjective f ↔ ∀ x : X, ∃ U ∈ 𝓝 x, U.InjOn f := by
  constructor <;> intro h x
  · obtain ⟨U, ho, hm, hi⟩ := h x; exact ⟨U, ho.mem_nhds hm, hi⟩
  · obtain ⟨U, hn, hi⟩ := h x
    

Proof state:
case mpr.intro.intro
X : Type u_1
Y : Type u_2
A : Type ?u.15215
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
h : ∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn f U
x : X
U : Set X
hn : U ∈ 𝓝 x
hi : Set.InjOn f U
⊢ ∃ U, IsOpen U ∧ x ∈ U ∧ Set.InjOn f U

Next tactic:
exact ⟨interior U, isOpen_interior, mem_interior_iff_mem_nhds.mpr hn, hi.mono interior_subset⟩
theorem isLocallyInjective_iff_isOpen_diagonal {f : X → Y} :
    IsLocallyInjective f ↔ IsOpen f.pullbackDiagonal := by
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type ?u.15686
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
⊢ IsLocallyInjective f ↔ IsOpen (Function.pullbackDiagonal f)

Next tactic:
simp_rw [isLocallyInjective_iff_nhds, isOpen_iff_mem_nhds,
    Subtype.forall, Prod.forall, nhds_induced, nhds_prod_eq, Filter.mem_comap]
theorem isLocallyInjective_iff_isOpen_diagonal {f : X → Y} :
    IsLocallyInjective f ↔ IsOpen f.pullbackDiagonal := by
  simp_rw [isLocallyInjective_iff_nhds, isOpen_iff_mem_nhds,
    Subtype.forall, Prod.forall, nhds_induced, nhds_prod_eq, Filter.mem_comap]
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type ?u.15686
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
⊢ (∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn f U) ↔
    ∀ (a b : X) (b_1 : f a = f b),
      { val := (a, b), property := b_1 } ∈ Function.pullbackDiagonal f →
        ∃ t ∈ 𝓝 a ×ˢ 𝓝 b, Subtype.val ⁻¹' t ⊆ Function.pullbackDiagonal f

Next tactic:
refine ⟨?_, fun h x ↦ ?_⟩
theorem isLocallyInjective_iff_isOpen_diagonal {f : X → Y} :
    IsLocallyInjective f ↔ IsOpen f.pullbackDiagonal := by
  simp_rw [isLocallyInjective_iff_nhds, isOpen_iff_mem_nhds,
    Subtype.forall, Prod.forall, nhds_induced, nhds_prod_eq, Filter.mem_comap]
  refine ⟨?_, fun h x ↦ ?_⟩
  · 

Proof state:
case refine_1
X : Type u_1
Y : Type u_2
A : Type ?u.15686
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
⊢ (∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn f U) →
    ∀ (a b : X) (b_1 : f a = f b),
      { val := (a, b), property := b_1 } ∈ Function.pullbackDiagonal f →
        ∃ t ∈ 𝓝 a ×ˢ 𝓝 b, Subtype.val ⁻¹' t ⊆ Function.pullbackDiagonal f

Next tactic:
rintro h x x' hx (rfl : x = x')
theorem isLocallyInjective_iff_isOpen_diagonal {f : X → Y} :
    IsLocallyInjective f ↔ IsOpen f.pullbackDiagonal := by
  simp_rw [isLocallyInjective_iff_nhds, isOpen_iff_mem_nhds,
    Subtype.forall, Prod.forall, nhds_induced, nhds_prod_eq, Filter.mem_comap]
  refine ⟨?_, fun h x ↦ ?_⟩
  · rintro h x x' hx (rfl : x = x')
    

Proof state:
case refine_1
X : Type u_1
Y : Type u_2
A : Type ?u.15686
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
h : ∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn f U
x : X
hx : f x = f x
⊢ ∃ t ∈ 𝓝 x ×ˢ 𝓝 x, Subtype.val ⁻¹' t ⊆ Function.pullbackDiagonal f

Next tactic:
obtain ⟨U, hn, hi⟩ := h x
theorem isLocallyInjective_iff_isOpen_diagonal {f : X → Y} :
    IsLocallyInjective f ↔ IsOpen f.pullbackDiagonal := by
  simp_rw [isLocallyInjective_iff_nhds, isOpen_iff_mem_nhds,
    Subtype.forall, Prod.forall, nhds_induced, nhds_prod_eq, Filter.mem_comap]
  refine ⟨?_, fun h x ↦ ?_⟩
  · rintro h x x' hx (rfl : x = x')
    obtain ⟨U, hn, hi⟩ := h x
    

Proof state:
case refine_1.intro.intro
X : Type u_1
Y : Type u_2
A : Type ?u.15686
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
h : ∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn f U
x : X
hx : f x = f x
U : Set X
hn : U ∈ 𝓝 x
hi : Set.InjOn f U
⊢ ∃ t ∈ 𝓝 x ×ˢ 𝓝 x, Subtype.val ⁻¹' t ⊆ Function.pullbackDiagonal f

Next tactic:
exact ⟨_, Filter.prod_mem_prod hn hn, fun {p} hp ↦ hi hp.1 hp.2 p.2⟩
theorem isLocallyInjective_iff_isOpen_diagonal {f : X → Y} :
    IsLocallyInjective f ↔ IsOpen f.pullbackDiagonal := by
  simp_rw [isLocallyInjective_iff_nhds, isOpen_iff_mem_nhds,
    Subtype.forall, Prod.forall, nhds_induced, nhds_prod_eq, Filter.mem_comap]
  refine ⟨?_, fun h x ↦ ?_⟩
  · rintro h x x' hx (rfl : x = x')
    obtain ⟨U, hn, hi⟩ := h x
    exact ⟨_, Filter.prod_mem_prod hn hn, fun {p} hp ↦ hi hp.1 hp.2 p.2⟩
  · 

Proof state:
case refine_2
X : Type u_1
Y : Type u_2
A : Type ?u.15686
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
h :
  ∀ (a b : X) (b_1 : f a = f b),
    { val := (a, b), property := b_1 } ∈ Function.pullbackDiagonal f →
      ∃ t ∈ 𝓝 a ×ˢ 𝓝 b, Subtype.val ⁻¹' t ⊆ Function.pullbackDiagonal f
x : X
⊢ ∃ U ∈ 𝓝 x, Set.InjOn f U

Next tactic:
obtain ⟨t, ht, t_sub⟩ := h x x rfl rfl
theorem isLocallyInjective_iff_isOpen_diagonal {f : X → Y} :
    IsLocallyInjective f ↔ IsOpen f.pullbackDiagonal := by
  simp_rw [isLocallyInjective_iff_nhds, isOpen_iff_mem_nhds,
    Subtype.forall, Prod.forall, nhds_induced, nhds_prod_eq, Filter.mem_comap]
  refine ⟨?_, fun h x ↦ ?_⟩
  · rintro h x x' hx (rfl : x = x')
    obtain ⟨U, hn, hi⟩ := h x
    exact ⟨_, Filter.prod_mem_prod hn hn, fun {p} hp ↦ hi hp.1 hp.2 p.2⟩
  · obtain ⟨t, ht, t_sub⟩ := h x x rfl rfl
    

Proof state:
case refine_2.intro.intro
X : Type u_1
Y : Type u_2
A : Type ?u.15686
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
h :
  ∀ (a b : X) (b_1 : f a = f b),
    { val := (a, b), property := b_1 } ∈ Function.pullbackDiagonal f →
      ∃ t ∈ 𝓝 a ×ˢ 𝓝 b, Subtype.val ⁻¹' t ⊆ Function.pullbackDiagonal f
x : X
t : Set (X × X)
ht : t ∈ 𝓝 x ×ˢ 𝓝 x
t_sub : Subtype.val ⁻¹' t ⊆ Function.pullbackDiagonal f
⊢ ∃ U ∈ 𝓝 x, Set.InjOn f U

Next tactic:
obtain ⟨t₁, h₁, t₂, h₂, prod_sub⟩ := Filter.mem_prod_iff.mp ht
theorem isLocallyInjective_iff_isOpen_diagonal {f : X → Y} :
    IsLocallyInjective f ↔ IsOpen f.pullbackDiagonal := by
  simp_rw [isLocallyInjective_iff_nhds, isOpen_iff_mem_nhds,
    Subtype.forall, Prod.forall, nhds_induced, nhds_prod_eq, Filter.mem_comap]
  refine ⟨?_, fun h x ↦ ?_⟩
  · rintro h x x' hx (rfl : x = x')
    obtain ⟨U, hn, hi⟩ := h x
    exact ⟨_, Filter.prod_mem_prod hn hn, fun {p} hp ↦ hi hp.1 hp.2 p.2⟩
  · obtain ⟨t, ht, t_sub⟩ := h x x rfl rfl
    obtain ⟨t₁, h₁, t₂, h₂, prod_sub⟩ := Filter.mem_prod_iff.mp ht
    

Proof state:
case refine_2.intro.intro.intro.intro.intro.intro
X : Type u_1
Y : Type u_2
A : Type ?u.15686
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
h :
  ∀ (a b : X) (b_1 : f a = f b),
    { val := (a, b), property := b_1 } ∈ Function.pullbackDiagonal f →
      ∃ t ∈ 𝓝 a ×ˢ 𝓝 b, Subtype.val ⁻¹' t ⊆ Function.pullbackDiagonal f
x : X
t : Set (X × X)
ht : t ∈ 𝓝 x ×ˢ 𝓝 x
t_sub : Subtype.val ⁻¹' t ⊆ Function.pullbackDiagonal f
t₁ : Set X
h₁ : t₁ ∈ 𝓝 x
t₂ : Set X
h₂ : t₂ ∈ 𝓝 x
prod_sub : t₁ ×ˢ t₂ ⊆ t
⊢ ∃ U ∈ 𝓝 x, Set.InjOn f U

Next tactic:
exact ⟨t₁ ∩ t₂, Filter.inter_mem h₁ h₂,
      fun x₁ h₁ x₂ h₂ he ↦ @t_sub ⟨(x₁, x₂), he⟩ (prod_sub ⟨h₁.1, h₂.2⟩)⟩
theorem IsLocallyInjective_iff_openEmbedding {f : X → Y} :
    IsLocallyInjective f ↔ OpenEmbedding (toPullbackDiag f) := by
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type ?u.17954
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
⊢ IsLocallyInjective f ↔ OpenEmbedding (toPullbackDiag f)

Next tactic:
rw [isLocallyInjective_iff_isOpen_diagonal, ← range_toPullbackDiag]
theorem IsLocallyInjective_iff_openEmbedding {f : X → Y} :
    IsLocallyInjective f ↔ OpenEmbedding (toPullbackDiag f) := by
  rw [isLocallyInjective_iff_isOpen_diagonal, ← range_toPullbackDiag]
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type ?u.17954
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
⊢ IsOpen (Set.range (toPullbackDiag f)) ↔ OpenEmbedding (toPullbackDiag f)

Next tactic:
exact ⟨fun h ↦ ⟨embedding_toPullbackDiag f, h⟩, fun h ↦ h.2⟩
theorem discreteTopology_iff_locallyInjective (y : Y) :
    DiscreteTopology X ↔ IsLocallyInjective fun _ : X ↦ y := by
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type ?u.18569
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
y : Y
⊢ DiscreteTopology X ↔ IsLocallyInjective fun x => y

Next tactic:
rw [discreteTopology_iff_singleton_mem_nhds, isLocallyInjective_iff_nhds]
theorem discreteTopology_iff_locallyInjective (y : Y) :
    DiscreteTopology X ↔ IsLocallyInjective fun _ : X ↦ y := by
  rw [discreteTopology_iff_singleton_mem_nhds, isLocallyInjective_iff_nhds]
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type ?u.18569
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
y : Y
⊢ (∀ (x : X), {x} ∈ 𝓝 x) ↔ ∀ (x : X), ∃ U ∈ 𝓝 x, Set.InjOn (fun x => y) U

Next tactic:
refine forall_congr' fun x ↦ ⟨fun h ↦ ⟨{x}, h, Set.injOn_singleton _ _⟩, fun ⟨U, hU, inj⟩ ↦ ?_⟩
theorem discreteTopology_iff_locallyInjective (y : Y) :
    DiscreteTopology X ↔ IsLocallyInjective fun _ : X ↦ y := by
  rw [discreteTopology_iff_singleton_mem_nhds, isLocallyInjective_iff_nhds]
  refine forall_congr' fun x ↦ ⟨fun h ↦ ⟨{x}, h, Set.injOn_singleton _ _⟩, fun ⟨U, hU, inj⟩ ↦ ?_⟩
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type ?u.18569
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
y : Y
x : X
x✝ : ∃ U ∈ 𝓝 x, Set.InjOn (fun x => y) U
U : Set X
hU : U ∈ 𝓝 x
inj : Set.InjOn (fun x => y) U
⊢ {x} ∈ 𝓝 x

Next tactic:
convert hU
theorem discreteTopology_iff_locallyInjective (y : Y) :
    DiscreteTopology X ↔ IsLocallyInjective fun _ : X ↦ y := by
  rw [discreteTopology_iff_singleton_mem_nhds, isLocallyInjective_iff_nhds]
  refine forall_congr' fun x ↦ ⟨fun h ↦ ⟨{x}, h, Set.injOn_singleton _ _⟩, fun ⟨U, hU, inj⟩ ↦ ?_⟩
  convert hU; 

Proof state:
case h.e'_4
X : Type u_1
Y : Type u_2
A : Type ?u.18569
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
y : Y
x : X
x✝ : ∃ U ∈ 𝓝 x, Set.InjOn (fun x => y) U
U : Set X
hU : U ∈ 𝓝 x
inj : Set.InjOn (fun x => y) U
⊢ {x} = U

Next tactic:
ext x'
theorem discreteTopology_iff_locallyInjective (y : Y) :
    DiscreteTopology X ↔ IsLocallyInjective fun _ : X ↦ y := by
  rw [discreteTopology_iff_singleton_mem_nhds, isLocallyInjective_iff_nhds]
  refine forall_congr' fun x ↦ ⟨fun h ↦ ⟨{x}, h, Set.injOn_singleton _ _⟩, fun ⟨U, hU, inj⟩ ↦ ?_⟩
  convert hU; ext x'; 

Proof state:
case h.e'_4.h
X : Type u_1
Y : Type u_2
A : Type ?u.18569
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
y : Y
x : X
x✝ : ∃ U ∈ 𝓝 x, Set.InjOn (fun x => y) U
U : Set X
hU : U ∈ 𝓝 x
inj : Set.InjOn (fun x => y) U
x' : X
⊢ x' ∈ {x} ↔ x' ∈ U

Next tactic:
refine ⟨?_, fun h ↦ inj h (mem_of_mem_nhds hU) rfl⟩
theorem discreteTopology_iff_locallyInjective (y : Y) :
    DiscreteTopology X ↔ IsLocallyInjective fun _ : X ↦ y := by
  rw [discreteTopology_iff_singleton_mem_nhds, isLocallyInjective_iff_nhds]
  refine forall_congr' fun x ↦ ⟨fun h ↦ ⟨{x}, h, Set.injOn_singleton _ _⟩, fun ⟨U, hU, inj⟩ ↦ ?_⟩
  convert hU; ext x'; refine ⟨?_, fun h ↦ inj h (mem_of_mem_nhds hU) rfl⟩
  

Proof state:
case h.e'_4.h
X : Type u_1
Y : Type u_2
A : Type ?u.18569
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
y : Y
x : X
x✝ : ∃ U ∈ 𝓝 x, Set.InjOn (fun x => y) U
U : Set X
hU : U ∈ 𝓝 x
inj : Set.InjOn (fun x => y) U
x' : X
⊢ x' ∈ {x} → x' ∈ U

Next tactic:
rintro rfl
theorem discreteTopology_iff_locallyInjective (y : Y) :
    DiscreteTopology X ↔ IsLocallyInjective fun _ : X ↦ y := by
  rw [discreteTopology_iff_singleton_mem_nhds, isLocallyInjective_iff_nhds]
  refine forall_congr' fun x ↦ ⟨fun h ↦ ⟨{x}, h, Set.injOn_singleton _ _⟩, fun ⟨U, hU, inj⟩ ↦ ?_⟩
  convert hU; ext x'; refine ⟨?_, fun h ↦ inj h (mem_of_mem_nhds hU) rfl⟩
  rintro rfl; 

Proof state:
case h.e'_4.h
X : Type u_1
Y : Type u_2
A : Type ?u.18569
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
y : Y
U : Set X
inj : Set.InjOn (fun x => y) U
x' : X
x✝ : ∃ U ∈ 𝓝 x', Set.InjOn (fun x => y) U
hU : U ∈ 𝓝 x'
⊢ x' ∈ U

Next tactic:
exact mem_of_mem_nhds hU
theorem IsLocallyInjective.comp_right {f : X → Y} (hf : IsLocallyInjective f) {g : A → X}
    (cont : Continuous g) (hg : g.Injective) : IsLocallyInjective (f ∘ g) := by
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type u_3
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
hf : IsLocallyInjective f
g : A → X
cont : Continuous g
hg : Function.Injective g
⊢ IsLocallyInjective (f ∘ g)

Next tactic:
rw [isLocallyInjective_iff_isOpen_diagonal] at hf ⊢
theorem IsLocallyInjective.comp_right {f : X → Y} (hf : IsLocallyInjective f) {g : A → X}
    (cont : Continuous g) (hg : g.Injective) : IsLocallyInjective (f ∘ g) := by
  rw [isLocallyInjective_iff_isOpen_diagonal] at hf ⊢
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type u_3
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
hf : IsOpen (Function.pullbackDiagonal f)
g : A → X
cont : Continuous g
hg : Function.Injective g
⊢ IsOpen (Function.pullbackDiagonal (f ∘ g))

Next tactic:
rw [← hg.preimage_pullbackDiagonal]
theorem IsLocallyInjective.comp_right {f : X → Y} (hf : IsLocallyInjective f) {g : A → X}
    (cont : Continuous g) (hg : g.Injective) : IsLocallyInjective (f ∘ g) := by
  rw [isLocallyInjective_iff_isOpen_diagonal] at hf ⊢
  rw [← hg.preimage_pullbackDiagonal]
  

Proof state:
X : Type u_1
Y : Type u_2
A : Type u_3
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
hf : IsOpen (Function.pullbackDiagonal f)
g : A → X
cont : Continuous g
hg : Function.Injective g
⊢ IsOpen (Function.mapPullback g id g (_ : f ∘ g = f ∘ g) (_ : f ∘ g = f ∘ g) ⁻¹' Function.pullbackDiagonal f)

Next tactic:
apply hf.preimage (cont.mapPullback cont)
theorem IsSeparatedMap.isClosed_eqLocus (he : f ∘ g₁ = f ∘ g₂) : IsClosed {a | g₁ a = g₂ a} :=
  let g : A → f.Pullback f := fun a ↦ ⟨⟨g₁ a, g₂ a⟩, congr_fun he a⟩
  (isSeparatedMap_iff_isClosed_diagonal.mp sep).preimage (by 

Proof state:
X : Type u_3
Y : Type u_2
A : Type u_1
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
sep : IsSeparatedMap f
inj : IsLocallyInjective f
g₁ g₂ : A → X
h₁ : Continuous g₁
h₂ : Continuous g₂
he : f ∘ g₁ = f ∘ g₂
g : A → Function.Pullback f f := fun a => { val := (g₁ a, g₂ a), property := (_ : (f ∘ g₁) a = (f ∘ g₂) a) }
⊢ Continuous g

Next tactic:
continuity
theorem IsLocallyInjective.isOpen_eqLocus (he : f ∘ g₁ = f ∘ g₂) : IsOpen {a | g₁ a = g₂ a} :=
  let g : A → f.Pullback f := fun a ↦ ⟨⟨g₁ a, g₂ a⟩, congr_fun he a⟩
  (isLocallyInjective_iff_isOpen_diagonal.mp inj).preimage (by 

Proof state:
X : Type u_3
Y : Type u_2
A : Type u_1
inst✝¹ : TopologicalSpace X
inst✝ : TopologicalSpace A
f : X → Y
sep : IsSeparatedMap f
inj : IsLocallyInjective f
g₁ g₂ : A → X
h₁ : Continuous g₁
h₂ : Continuous g₂
he : f ∘ g₁ = f ∘ g₂
g : A → Function.Pullback f f := fun a => { val := (g₁ a, g₂ a), property := (_ : (f ∘ g₁) a = (f ∘ g₂) a) }
⊢ Continuous g

Next tactic:
continuity
/-- If `p` is a locally injective separated map, and `A` is a connected space,
  then two lifts `g₁, g₂ : A → E` of a map `f : A → X` are equal if they agree at one point. -/
theorem eq_of_comp_eq [PreconnectedSpace A] (h₁ : Continuous g₁) (h₂ : Continuous g₂)
    (he : p ∘ g₁ = p ∘ g₂) (a : A) (ha : g₁ a = g₂ a) : g₁ = g₂ := funext fun a' ↦ by
  

Proof state:
X : Type u_3
Y : ?m.26395
A✝ : Type ?u.26404
inst✝⁴ : TopologicalSpace X
inst✝³ : TopologicalSpace A✝
E : Type u_1
A : Type u_2
inst✝² : TopologicalSpace E
inst✝¹ : TopologicalSpace A
p : E → X
sep : IsSeparatedMap p
inj : IsLocallyInjective p
s : Set A
hs : IsPreconnected s
g g₁ g₂ : A → E
inst✝ : PreconnectedSpace A
h₁ : Continuous g₁
h₂ : Continuous g₂
he : p ∘ g₁ = p ∘ g₂
a : A
ha : g₁ a = g₂ a
a' : A
⊢ g₁ a' = g₂ a'

Next tactic:
apply (IsClopen.eq_univ ⟨inj.isOpen_eqLocus h₁ h₂ he, sep.isClosed_eqLocus h₁ h₂ he⟩ ⟨a, ha⟩).symm
    ▸ Set.mem_univ a'
theorem eqOn_of_comp_eqOn (h₁ : ContinuousOn g₁ s) (h₂ : ContinuousOn g₂ s)
    (he : s.EqOn (p ∘ g₁) (p ∘ g₂)) {a : A} (has : a ∈ s) (ha : g₁ a = g₂ a) : s.EqOn g₁ g₂ := by
  

Proof state:
X : Type u_3
Y : ?m.26952
A✝ : Type ?u.26961
inst✝³ : TopologicalSpace X
inst✝² : TopologicalSpace A✝
E : Type u_1
A : Type u_2
inst✝¹ : TopologicalSpace E
inst✝ : TopologicalSpace A
p : E → X
sep : IsSeparatedMap p
inj : IsLocallyInjective p
s : Set A
hs : IsPreconnected s
g g₁ g₂ : A → E
h₁ : ContinuousOn g₁ s
h₂ : ContinuousOn g₂ s
he : Set.EqOn (p ∘ g₁) (p ∘ g₂) s
a : A
has : a ∈ s
ha : g₁ a = g₂ a
⊢ Set.EqOn g₁ g₂ s

Next tactic:
rw [← Set.restrict_eq_restrict_iff] at he ⊢
theorem eqOn_of_comp_eqOn (h₁ : ContinuousOn g₁ s) (h₂ : ContinuousOn g₂ s)
    (he : s.EqOn (p ∘ g₁) (p ∘ g₂)) {a : A} (has : a ∈ s) (ha : g₁ a = g₂ a) : s.EqOn g₁ g₂ := by
  rw [← Set.restrict_eq_restrict_iff] at he ⊢
  

Proof state:
X : Type u_3
Y : ?m.26952
A✝ : Type ?u.26961
inst✝³ : TopologicalSpace X
inst✝² : TopologicalSpace A✝
E : Type u_1
A : Type u_2
inst✝¹ : TopologicalSpace E
inst✝ : TopologicalSpace A
p : E → X
sep : IsSeparatedMap p
inj : IsLocallyInjective p
s : Set A
hs : IsPreconnected s
g g₁ g₂ : A → E
h₁ : ContinuousOn g₁ s
h₂ : ContinuousOn g₂ s
he : Set.restrict s (p ∘ g₁) = Set.restrict s (p ∘ g₂)
a : A
has : a ∈ s
ha : g₁ a = g₂ a
⊢ Set.restrict s g₁ = Set.restrict s g₂

Next tactic:
rw [continuousOn_iff_continuous_restrict] at h₁ h₂
theorem eqOn_of_comp_eqOn (h₁ : ContinuousOn g₁ s) (h₂ : ContinuousOn g₂ s)
    (he : s.EqOn (p ∘ g₁) (p ∘ g₂)) {a : A} (has : a ∈ s) (ha : g₁ a = g₂ a) : s.EqOn g₁ g₂ := by
  rw [← Set.restrict_eq_restrict_iff] at he ⊢
  rw [continuousOn_iff_continuous_restrict] at h₁ h₂
  

Proof state:
X : Type u_3
Y : ?m.26952
A✝ : Type ?u.26961
inst✝³ : TopologicalSpace X
inst✝² : TopologicalSpace A✝
E : Type u_1
A : Type u_2
inst✝¹ : TopologicalSpace E
inst✝ : TopologicalSpace A
p : E → X
sep : IsSeparatedMap p
inj : IsLocallyInjective p
s : Set A
hs : IsPreconnected s
g g₁ g₂ : A → E
h₁ : Continuous (Set.restrict s g₁)
h₂ : Continuous (Set.restrict s g₂)
he : Set.restrict s (p ∘ g₁) = Set.restrict s (p ∘ g₂)
a : A
has : a ∈ s
ha : g₁ a = g₂ a
⊢ Set.restrict s g₁ = Set.restrict s g₂

Next tactic:
rw [isPreconnected_iff_preconnectedSpace] at hs
theorem eqOn_of_comp_eqOn (h₁ : ContinuousOn g₁ s) (h₂ : ContinuousOn g₂ s)
    (he : s.EqOn (p ∘ g₁) (p ∘ g₂)) {a : A} (has : a ∈ s) (ha : g₁ a = g₂ a) : s.EqOn g₁ g₂ := by
  rw [← Set.restrict_eq_restrict_iff] at he ⊢
  rw [continuousOn_iff_continuous_restrict] at h₁ h₂
  rw [isPreconnected_iff_preconnectedSpace] at hs
  

Proof state:
X : Type u_3
Y : ?m.26952
A✝ : Type ?u.26961
inst✝³ : TopologicalSpace X
inst✝² : TopologicalSpace A✝
E : Type u_1
A : Type u_2
inst✝¹ : TopologicalSpace E
inst✝ : TopologicalSpace A
p : E → X
sep : IsSeparatedMap p
inj : IsLocallyInjective p
s : Set A
hs : PreconnectedSpace ↑s
g g₁ g₂ : A → E
h₁ : Continuous (Set.restrict s g₁)
h₂ : Continuous (Set.restrict s g₂)
he : Set.restrict s (p ∘ g₁) = Set.restrict s (p ∘ g₂)
a : A
has : a ∈ s
ha : g₁ a = g₂ a
⊢ Set.restrict s g₁ = Set.restrict s g₂

Next tactic:
exact sep.eq_of_comp_eq inj h₁ h₂ he ⟨a, has⟩ ha