l3lab/ntp-mathlib huggingface dataset:
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    

Proof state:
X Y : Stonean
f : X ⟶ Y
h : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
⊢ U ∈ nhds y

Next tactic:
simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  

Proof state:
X Y : Stonean
f : X ⟶ Y
h : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
⊢ False

Next tactic:
obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  

Proof state:
case intro.intro.intro
X Y : Stonean
f : X ⟶ Y
h : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
⊢ False

Next tactic:
classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    rw [← cancel_epi f]
    ext x
    apply ULift.ext -- why is `ext` not doing this automatically?
    change 1 = ite _ _ _ -- why is `dsimp` not getting me here?
    rw [if_neg]
    refine mt (hVU ·) ?_ -- what would be an idiomatic tactic for this step?
    simpa only [Set.mem_compl_iff, Set.mem_range, not_exists, not_forall, not_not]
      using exists_apply_eq_apply f x
  apply_fun fun e => (e y).down at H
  change 1 = ite _ _ _ at H -- why is `dsimp at H` not getting me here?
  rw [if_pos hyV] at H
  exact one_ne_zero H
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  

Proof state:
case intro.intro.intro
X Y : Stonean
f : X ⟶ Y
h : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
⊢ False

Next tactic:
let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  

Proof state:
case intro.intro.intro
X Y : Stonean
f : X ⟶ Y
h : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
⊢ False

Next tactic:
let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  

Proof state:
case intro.intro.intro
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
⊢ False

Next tactic:
have H : h = g := by
    rw [← cancel_epi f]
    ext x
    apply ULift.ext -- why is `ext` not doing this automatically?
    change 1 = ite _ _ _ -- why is `dsimp` not getting me here?
    rw [if_neg]
    refine mt (hVU ·) ?_ -- what would be an idiomatic tactic for this step?
    simpa only [Set.mem_compl_iff, Set.mem_range, not_exists, not_forall, not_not]
      using exists_apply_eq_apply f x
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    

Proof state:
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
⊢ h = g

Next tactic:
rw [← cancel_epi f]
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    rw [← cancel_epi f]
    

Proof state:
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
⊢ f ≫ h = f ≫ g

Next tactic:
ext x
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    rw [← cancel_epi f]
    ext x
    

Proof state:
case w
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
x : (forget Stonean).obj X
⊢ (f ≫ h) x = (f ≫ g) x

Next tactic:
apply ULift.ext
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    rw [← cancel_epi f]
    ext x
    apply ULift.ext -- why is `ext` not doing this automatically?
    

Proof state:
case w.h
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
x : (forget Stonean).obj X
⊢ ((f ≫ h) x).down = ((f ≫ g) x).down

Next tactic:
change 1 = ite _ _ _
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    rw [← cancel_epi f]
    ext x
    apply ULift.ext -- why is `ext` not doing this automatically?
    change 1 = ite _ _ _ -- why is `dsimp` not getting me here?
    

Proof state:
case w.h
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
x : (forget Stonean).obj X
⊢ 1 = if f x ∈ V then 0 else 1

Next tactic:
rw [if_neg]
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    rw [← cancel_epi f]
    ext x
    apply ULift.ext -- why is `ext` not doing this automatically?
    change 1 = ite _ _ _ -- why is `dsimp` not getting me here?
    rw [if_neg]
    

Proof state:
case w.h.hnc
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
x : (forget Stonean).obj X
⊢ f x ∉ V

Next tactic:
refine mt (hVU ·) ?_
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    rw [← cancel_epi f]
    ext x
    apply ULift.ext -- why is `ext` not doing this automatically?
    change 1 = ite _ _ _ -- why is `dsimp` not getting me here?
    rw [if_neg]
    refine mt (hVU ·) ?_ -- what would be an idiomatic tactic for this step?
    

Proof state:
case w.h.hnc
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
x : (forget Stonean).obj X
⊢ f x ∉ U

Next tactic:
simpa only [Set.mem_compl_iff, Set.mem_range, not_exists, not_forall, not_not]
      using exists_apply_eq_apply f x
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    rw [← cancel_epi f]
    ext x
    apply ULift.ext -- why is `ext` not doing this automatically?
    change 1 = ite _ _ _ -- why is `dsimp` not getting me here?
    rw [if_neg]
    refine mt (hVU ·) ?_ -- what would be an idiomatic tactic for this step?
    simpa only [Set.mem_compl_iff, Set.mem_range, not_exists, not_forall, not_not]
      using exists_apply_eq_apply f x
  

Proof state:
case intro.intro.intro
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
H : h = g
⊢ False

Next tactic:
apply_fun fun e => (e y).down at H
/--
A morphism in `Stonean` is an epi iff it is surjective.

TODO: prove that `forget Stonean` preserves pushouts (in fact it preserves all finite colimits)
and deduce this from general lemmas about concrete categories.
-/
lemma epi_iff_surjective {X Y : Stonean} (f : X ⟶ Y) :
    Epi f ↔ Function.Surjective f := by
  refine ⟨?_, ConcreteCategory.epi_of_surjective _⟩
  dsimp [Function.Surjective]
  intro h y
  by_contra! hy
  let C := Set.range f
  have hC : IsClosed C := (isCompact_range f.continuous).isClosed
  let U := Cᶜ
  have hUy : U ∈ nhds y := by
    simp only [Set.mem_range, hy, exists_false, not_false_eq_true, hC.compl_mem_nhds]
  obtain ⟨V, hV, hyV, hVU⟩ := isTopologicalBasis_isClopen.mem_nhds_iff.mp hUy
  classical
  let g : Y ⟶ mkFinite (ULift (Fin 2)) :=
    ⟨(LocallyConstant.ofIsClopen hV).map ULift.up, LocallyConstant.continuous _⟩
  let h : Y ⟶ mkFinite (ULift (Fin 2)) := ⟨fun _ => ⟨1⟩, continuous_const⟩
  have H : h = g := by
    rw [← cancel_epi f]
    ext x
    apply ULift.ext -- why is `ext` not doing this automatically?
    change 1 = ite _ _ _ -- why is `dsimp` not getting me here?
    rw [if_neg]
    refine mt (hVU ·) ?_ -- what would be an idiomatic tactic for this step?
    simpa only [Set.mem_compl_iff, Set.mem_range, not_exists, not_forall, not_not]
      using exists_apply_eq_apply f x
  apply_fun fun e => (e y).down at H
  

Proof state:
case intro.intro.intro
X Y : Stonean
f : X ⟶ Y
h✝ : Epi f
y : CoeSort.coe Y
hy : ∀ (a : CoeSort.coe X), f a ≠ y
C : Set (CoeSort.coe Y) := Set.range ⇑f
hC : IsClosed C
U : Set (CoeSort.coe Y) := Cᶜ
hUy : U ∈ nhds y
V : Set (CoeSort.coe Y)
hV : V ∈ {s | IsClopen s}
hyV : y ∈ V
hVU : V ⊆ U
g : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) :=
  ContinuousMap.mk ⇑(LocallyConstant.map ULift.up (LocallyConstant.ofIsClopen hV))
h : Y ⟶ mkFinite (ULift.{u_1, 0} (Fin 2)) := ContinuousMap.mk fun x => { down := 1 }
H : (h y).down = (g y).down
⊢ False

Next tactic:
change 1 = ite _ _ _ at H