l3lab/ntp-mathlib huggingface dataset:
theorem isOpen_iff (U : Set 𝖣.glued) : IsOpen U ↔ ∀ i, IsOpen (𝖣.ι i ⁻¹' U) := by
  delta CategoryTheory.GlueData.ι
  simp_rw [← Multicoequalizer.ι_sigmaπ 𝖣.diagram]
  rw [← (homeoOfIso (Multicoequalizer.isoCoequalizer 𝖣.diagram).symm).isOpen_preimage]
  rw [coequalizer_isOpen_iff]
  dsimp only [GlueData.diagram_l, GlueData.diagram_left, GlueData.diagram_r, GlueData.diagram_right,
    parallelPair_obj_one]
  rw [colimit_isOpen_iff.{_,u}]  -- porting note: changed `.{u}` to `.{_,u}`.  fun fact: the proof
                                 -- breaks down if this `rw` is merged with the `rw` above.
  constructor
  · intro h j; exact h ⟨j⟩
  · intro h j; 

Proof state:
case mpr
D : GlueData
U : Set ↑(GlueData.glued D.toGlueData)
h : ∀ (i : D.J), IsOpen (⇑(Sigma.ι D.U i ≫ Multicoequalizer.sigmaπ (GlueData.diagram D.toGlueData)) ⁻¹' U)
j : Discrete D.J
⊢ IsOpen
    (⇑(colimit.ι (Discrete.functor D.U) j) ⁻¹'
      (⇑(colimit.ι
            (parallelPair (MultispanIndex.fstSigmaMap (GlueData.diagram D.toGlueData))
              (MultispanIndex.sndSigmaMap (GlueData.diagram D.toGlueData)))
            WalkingParallelPair.one) ⁻¹'
        (⇑(homeoOfIso (Multicoequalizer.isoCoequalizer (GlueData.diagram D.toGlueData)).symm) ⁻¹' U)))

Next tactic:
cases j
theorem isOpen_iff (U : Set 𝖣.glued) : IsOpen U ↔ ∀ i, IsOpen (𝖣.ι i ⁻¹' U) := by
  delta CategoryTheory.GlueData.ι
  simp_rw [← Multicoequalizer.ι_sigmaπ 𝖣.diagram]
  rw [← (homeoOfIso (Multicoequalizer.isoCoequalizer 𝖣.diagram).symm).isOpen_preimage]
  rw [coequalizer_isOpen_iff]
  dsimp only [GlueData.diagram_l, GlueData.diagram_left, GlueData.diagram_r, GlueData.diagram_right,
    parallelPair_obj_one]
  rw [colimit_isOpen_iff.{_,u}]  -- porting note: changed `.{u}` to `.{_,u}`.  fun fact: the proof
                                 -- breaks down if this `rw` is merged with the `rw` above.
  constructor
  · intro h j; exact h ⟨j⟩
  · intro h j; cases j; 

Proof state:
case mpr.mk
D : GlueData
U : Set ↑(GlueData.glued D.toGlueData)
h : ∀ (i : D.J), IsOpen (⇑(Sigma.ι D.U i ≫ Multicoequalizer.sigmaπ (GlueData.diagram D.toGlueData)) ⁻¹' U)
as✝ : D.J
⊢ IsOpen
    (⇑(colimit.ι (Discrete.functor D.U) { as := as✝ }) ⁻¹'
      (⇑(colimit.ι
            (parallelPair (MultispanIndex.fstSigmaMap (GlueData.diagram D.toGlueData))
              (MultispanIndex.sndSigmaMap (GlueData.diagram D.toGlueData)))
            WalkingParallelPair.one) ⁻¹'
        (⇑(homeoOfIso (Multicoequalizer.isoCoequalizer (GlueData.diagram D.toGlueData)).symm) ⁻¹' U)))

Next tactic:
apply h
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    

Proof state:
D : GlueData
⊢ ∀ {x y : (i : D.J) × ↑(GlueData.U D.toGlueData i)}, Rel D x y → Rel D y x

Next tactic:
rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    

Proof state:
case inl.refl
D : GlueData
a : (i : D.J) × ↑(GlueData.U D.toGlueData i)
⊢ Rel D a a
case inr.intro.intro
D : GlueData
a b : (i : D.J) × ↑(GlueData.U D.toGlueData i)
x : ↑(GlueData.V D.toGlueData (a.fst, b.fst))
e₁ : (GlueData.f D.toGlueData a.fst b.fst) x = a.snd
e₂ : (GlueData.f D.toGlueData b.fst a.fst) ((GlueData.t D.toGlueData a.fst b.fst) x) = b.snd
⊢ Rel D b a

Next tactic:
exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩]
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by 

Proof state:
D : GlueData
a b : (i : D.J) × ↑(GlueData.U D.toGlueData i)
x : ↑(GlueData.V D.toGlueData (a.fst, b.fst))
e₁ : (GlueData.f D.toGlueData a.fst b.fst) x = a.snd
e₂ : (GlueData.f D.toGlueData b.fst a.fst) ((GlueData.t D.toGlueData a.fst b.fst) x) = b.snd
⊢ (GlueData.f D.toGlueData b.fst a.fst) ((GlueData.t D.toGlueData a.fst b.fst) x) = b.snd ∧
    (GlueData.f D.toGlueData a.fst b.fst)
        ((GlueData.t D.toGlueData b.fst a.fst) ((GlueData.t D.toGlueData a.fst b.fst) x)) =
      a.snd

Next tactic:
simp [e₁, e₂]
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    

Proof state:
D : GlueData
⊢ ∀ {x y z : (i : D.J) × ↑(GlueData.U D.toGlueData i)}, Rel D x y → Rel D y z → Rel D x z

Next tactic:
rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); 

Proof state:
case mk.mk.mk.inl.refl
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
⊢ Rel D { fst := i, snd := a } { fst := k, snd := c } → Rel D { fst := i, snd := a } { fst := k, snd := c }
case mk.mk.mk.inr.intro.intro
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
⊢ Rel D { fst := j, snd := b } { fst := k, snd := c } → Rel D { fst := i, snd := a } { fst := k, snd := c }

Next tactic:
exact id
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); exact id
    

Proof state:
case mk.mk.mk.inr.intro.intro
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
⊢ Rel D { fst := j, snd := b } { fst := k, snd := c } → Rel D { fst := i, snd := a } { fst := k, snd := c }

Next tactic:
rintro (⟨⟨⟩⟩ | ⟨y, e₃, e₄⟩)
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); exact id
    rintro (⟨⟨⟩⟩ | ⟨y, e₃, e₄⟩); 

Proof state:
case mk.mk.mk.inr.intro.intro.inl.refl
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
⊢ Rel D { fst := i, snd := a } { fst := j, snd := b }
case mk.mk.mk.inr.intro.intro.inr.intro.intro
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
y : ↑(GlueData.V D.toGlueData ({ fst := j, snd := b }.fst, { fst := k, snd := c }.fst))
e₃ : (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y = { fst := j, snd := b }.snd
e₄ :
  (GlueData.f D.toGlueData { fst := k, snd := c }.fst { fst := j, snd := b }.fst)
      ((GlueData.t D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y) =
    { fst := k, snd := c }.snd
⊢ Rel D { fst := i, snd := a } { fst := k, snd := c }

Next tactic:
exact Or.inr ⟨x, e₁, e₂⟩
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); exact id
    rintro (⟨⟨⟩⟩ | ⟨y, e₃, e₄⟩); exact Or.inr ⟨x, e₁, e₂⟩
    

Proof state:
case mk.mk.mk.inr.intro.intro.inr.intro.intro
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
y : ↑(GlueData.V D.toGlueData ({ fst := j, snd := b }.fst, { fst := k, snd := c }.fst))
e₃ : (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y = { fst := j, snd := b }.snd
e₄ :
  (GlueData.f D.toGlueData { fst := k, snd := c }.fst { fst := j, snd := b }.fst)
      ((GlueData.t D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y) =
    { fst := k, snd := c }.snd
⊢ Rel D { fst := i, snd := a } { fst := k, snd := c }

Next tactic:
let z := (pullbackIsoProdSubtype (D.f j i) (D.f j k)).inv ⟨⟨_, _⟩, e₂.trans e₃.symm⟩
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); exact id
    rintro (⟨⟨⟩⟩ | ⟨y, e₃, e₄⟩); exact Or.inr ⟨x, e₁, e₂⟩
    let z := (pullbackIsoProdSubtype (D.f j i) (D.f j k)).inv ⟨⟨_, _⟩, e₂.trans e₃.symm⟩
    

Proof state:
case mk.mk.mk.inr.intro.intro.inr.intro.intro
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
y : ↑(GlueData.V D.toGlueData ({ fst := j, snd := b }.fst, { fst := k, snd := c }.fst))
e₃ : (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y = { fst := j, snd := b }.snd
e₄ :
  (GlueData.f D.toGlueData { fst := k, snd := c }.fst { fst := j, snd := b }.fst)
      ((GlueData.t D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y) =
    { fst := k, snd := c }.snd
z : (forget TopCat).obj (pullback (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k)) :=
  (pullbackIsoProdSubtype (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k)).inv
    { val := ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x, y),
      property :=
        (_ :
          (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
              ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
            (GlueData.f D.toGlueData j k)
              ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x, y).2) }
⊢ Rel D { fst := i, snd := a } { fst := k, snd := c }

Next tactic:
have eq₁ : (D.t j i) ((pullback.fst : _ /-(D.f j k)-/ ⟶ D.V (j, i)) z) = x := by simp
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); exact id
    rintro (⟨⟨⟩⟩ | ⟨y, e₃, e₄⟩); exact Or.inr ⟨x, e₁, e₂⟩
    let z := (pullbackIsoProdSubtype (D.f j i) (D.f j k)).inv ⟨⟨_, _⟩, e₂.trans e₃.symm⟩
    have eq₁ : (D.t j i) ((pullback.fst : _ /-(D.f j k)-/ ⟶ D.V (j, i)) z) = x := by 

Proof state:
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
y : ↑(GlueData.V D.toGlueData ({ fst := j, snd := b }.fst, { fst := k, snd := c }.fst))
e₃ : (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y = { fst := j, snd := b }.snd
e₄ :
  (GlueData.f D.toGlueData { fst := k, snd := c }.fst { fst := j, snd := b }.fst)
      ((GlueData.t D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y) =
    { fst := k, snd := c }.snd
z : (forget TopCat).obj (pullback (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k)) :=
  (pullbackIsoProdSubtype (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k)).inv
    { val := ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x, y),
      property :=
        (_ :
          (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
              ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
            (GlueData.f D.toGlueData j k)
              ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x, y).2) }
⊢ (GlueData.t D.toGlueData j i) (pullback.fst z) = x

Next tactic:
simp
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); exact id
    rintro (⟨⟨⟩⟩ | ⟨y, e₃, e₄⟩); exact Or.inr ⟨x, e₁, e₂⟩
    let z := (pullbackIsoProdSubtype (D.f j i) (D.f j k)).inv ⟨⟨_, _⟩, e₂.trans e₃.symm⟩
    have eq₁ : (D.t j i) ((pullback.fst : _ /-(D.f j k)-/ ⟶ D.V (j, i)) z) = x := by simp
    

Proof state:
case mk.mk.mk.inr.intro.intro.inr.intro.intro
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
y : ↑(GlueData.V D.toGlueData ({ fst := j, snd := b }.fst, { fst := k, snd := c }.fst))
e₃ : (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y = { fst := j, snd := b }.snd
e₄ :
  (GlueData.f D.toGlueData { fst := k, snd := c }.fst { fst := j, snd := b }.fst)
      ((GlueData.t D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y) =
    { fst := k, snd := c }.snd
z : (forget TopCat).obj (pullback (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k)) :=
  (pullbackIsoProdSubtype (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k)).inv
    { val := ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x, y),
      property :=
        (_ :
          (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
              ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
            (GlueData.f D.toGlueData j k)
              ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x, y).2) }
eq₁ : (GlueData.t D.toGlueData j i) (pullback.fst z) = x
⊢ Rel D { fst := i, snd := a } { fst := k, snd := c }

Next tactic:
have eq₂ : (pullback.snd : _ ⟶ D.V _) z = y := pullbackIsoProdSubtype_inv_snd_apply _ _ _
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); exact id
    rintro (⟨⟨⟩⟩ | ⟨y, e₃, e₄⟩); exact Or.inr ⟨x, e₁, e₂⟩
    let z := (pullbackIsoProdSubtype (D.f j i) (D.f j k)).inv ⟨⟨_, _⟩, e₂.trans e₃.symm⟩
    have eq₁ : (D.t j i) ((pullback.fst : _ /-(D.f j k)-/ ⟶ D.V (j, i)) z) = x := by simp
    have eq₂ : (pullback.snd : _ ⟶ D.V _) z = y := pullbackIsoProdSubtype_inv_snd_apply _ _ _
    

Proof state:
case mk.mk.mk.inr.intro.intro.inr.intro.intro
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
y : ↑(GlueData.V D.toGlueData ({ fst := j, snd := b }.fst, { fst := k, snd := c }.fst))
e₃ : (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y = { fst := j, snd := b }.snd
e₄ :
  (GlueData.f D.toGlueData { fst := k, snd := c }.fst { fst := j, snd := b }.fst)
      ((GlueData.t D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y) =
    { fst := k, snd := c }.snd
z : (forget TopCat).obj (pullback (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k)) :=
  (pullbackIsoProdSubtype (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k)).inv
    { val := ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x, y),
      property :=
        (_ :
          (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
              ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
            (GlueData.f D.toGlueData j k)
              ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x, y).2) }
eq₁ : (GlueData.t D.toGlueData j i) (pullback.fst z) = x
eq₂ : pullback.snd z = y
⊢ Rel D { fst := i, snd := a } { fst := k, snd := c }

Next tactic:
clear_value z
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); exact id
    rintro (⟨⟨⟩⟩ | ⟨y, e₃, e₄⟩); exact Or.inr ⟨x, e₁, e₂⟩
    let z := (pullbackIsoProdSubtype (D.f j i) (D.f j k)).inv ⟨⟨_, _⟩, e₂.trans e₃.symm⟩
    have eq₁ : (D.t j i) ((pullback.fst : _ /-(D.f j k)-/ ⟶ D.V (j, i)) z) = x := by simp
    have eq₂ : (pullback.snd : _ ⟶ D.V _) z = y := pullbackIsoProdSubtype_inv_snd_apply _ _ _
    clear_value z
    

Proof state:
case mk.mk.mk.inr.intro.intro.inr.intro.intro
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
y : ↑(GlueData.V D.toGlueData ({ fst := j, snd := b }.fst, { fst := k, snd := c }.fst))
e₃ : (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y = { fst := j, snd := b }.snd
e₄ :
  (GlueData.f D.toGlueData { fst := k, snd := c }.fst { fst := j, snd := b }.fst)
      ((GlueData.t D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y) =
    { fst := k, snd := c }.snd
z : (forget TopCat).obj (pullback (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k))
eq₁ : (GlueData.t D.toGlueData j i) (pullback.fst z) = x
eq₂ : pullback.snd z = y
⊢ Rel D { fst := i, snd := a } { fst := k, snd := c }

Next tactic:
right
theorem rel_equiv : Equivalence D.Rel :=
  ⟨fun x => Or.inl (refl x), by
    rintro a b (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩)
    exacts [Or.inl rfl, Or.inr ⟨D.t _ _ x, by simp [e₁, e₂]⟩], by
    rintro ⟨i, a⟩ ⟨j, b⟩ ⟨k, c⟩ (⟨⟨⟩⟩ | ⟨x, e₁, e₂⟩); exact id
    rintro (⟨⟨⟩⟩ | ⟨y, e₃, e₄⟩); exact Or.inr ⟨x, e₁, e₂⟩
    let z := (pullbackIsoProdSubtype (D.f j i) (D.f j k)).inv ⟨⟨_, _⟩, e₂.trans e₃.symm⟩
    have eq₁ : (D.t j i) ((pullback.fst : _ /-(D.f j k)-/ ⟶ D.V (j, i)) z) = x := by simp
    have eq₂ : (pullback.snd : _ ⟶ D.V _) z = y := pullbackIsoProdSubtype_inv_snd_apply _ _ _
    clear_value z
    right
    

Proof state:
case mk.mk.mk.inr.intro.intro.inr.intro.intro.h
D : GlueData
i : D.J
a : ↑(GlueData.U D.toGlueData i)
j : D.J
b : ↑(GlueData.U D.toGlueData j)
k : D.J
c : ↑(GlueData.U D.toGlueData k)
x : ↑(GlueData.V D.toGlueData ({ fst := i, snd := a }.fst, { fst := j, snd := b }.fst))
e₁ : (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x = { fst := i, snd := a }.snd
e₂ :
  (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := i, snd := a }.fst)
      ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := j, snd := b }.fst) x) =
    { fst := j, snd := b }.snd
y : ↑(GlueData.V D.toGlueData ({ fst := j, snd := b }.fst, { fst := k, snd := c }.fst))
e₃ : (GlueData.f D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y = { fst := j, snd := b }.snd
e₄ :
  (GlueData.f D.toGlueData { fst := k, snd := c }.fst { fst := j, snd := b }.fst)
      ((GlueData.t D.toGlueData { fst := j, snd := b }.fst { fst := k, snd := c }.fst) y) =
    { fst := k, snd := c }.snd
z : (forget TopCat).obj (pullback (GlueData.f D.toGlueData j i) (GlueData.f D.toGlueData j k))
eq₁ : (GlueData.t D.toGlueData j i) (pullback.fst z) = x
eq₂ : pullback.snd z = y
⊢ ∃ x,
    (GlueData.f D.toGlueData { fst := i, snd := a }.fst { fst := k, snd := c }.fst) x = { fst := i, snd := a }.snd ∧
      (GlueData.f D.toGlueData { fst := k, snd := c }.fst { fst := i, snd := a }.fst)
          ((GlueData.t D.toGlueData { fst := i, snd := a }.fst { fst := k, snd := c }.fst) x) =
        { fst := k, snd := c }.snd

Next tactic:
use (pullback.fst : _ ⟶ D.V (i, k)) (D.t' _ _ _ z)