l3lab/ntp-mathlib huggingface dataset:
theorem pullback_base (i j k : D.J) (S : Set (D.V (i, j)).carrier) :
    (π₂ i, j, k) '' ((π₁ i, j, k) ⁻¹' S) = D.f i k ⁻¹' (D.f i j '' S) := by
  have eq₁ : _ = (π₁ i, j, k).base := PreservesPullback.iso_hom_fst (forget C) _ _
  have eq₂ : _ = (π₂ i, j, k).base := PreservesPullback.iso_hom_snd (forget C) _ _
  rw [← eq₁, ← eq₂]
  -- Porting note : `rw` to `erw` on `coe_comp`
  erw [coe_comp]
  rw [Set.image_comp]
  -- Porting note : `rw` to `erw` on `coe_comp`
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
S : Set ↑↑(V D.toGlueData (i, j))
eq₁ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.fst = pullback.fst.base
eq₂ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.snd = pullback.snd.base
⊢ ⇑pullback.snd ''
      (⇑(PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ''
        (⇑((PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.fst) ⁻¹' S)) =
    ⇑(f D.toGlueData i k).base ⁻¹' (⇑(f D.toGlueData i j).base '' S)

Next tactic:
erw [coe_comp]
theorem pullback_base (i j k : D.J) (S : Set (D.V (i, j)).carrier) :
    (π₂ i, j, k) '' ((π₁ i, j, k) ⁻¹' S) = D.f i k ⁻¹' (D.f i j '' S) := by
  have eq₁ : _ = (π₁ i, j, k).base := PreservesPullback.iso_hom_fst (forget C) _ _
  have eq₂ : _ = (π₂ i, j, k).base := PreservesPullback.iso_hom_snd (forget C) _ _
  rw [← eq₁, ← eq₂]
  -- Porting note : `rw` to `erw` on `coe_comp`
  erw [coe_comp]
  rw [Set.image_comp]
  -- Porting note : `rw` to `erw` on `coe_comp`
  erw [coe_comp]
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
S : Set ↑↑(V D.toGlueData (i, j))
eq₁ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.fst = pullback.fst.base
eq₂ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.snd = pullback.snd.base
⊢ ⇑pullback.snd ''
      (⇑(PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ''
        (⇑pullback.fst ∘ ⇑(PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ⁻¹' S)) =
    ⇑(f D.toGlueData i k).base ⁻¹' (⇑(f D.toGlueData i j).base '' S)

Next tactic:
rw [Set.preimage_comp, Set.image_preimage_eq, TopCat.pullback_snd_image_fst_preimage]
theorem pullback_base (i j k : D.J) (S : Set (D.V (i, j)).carrier) :
    (π₂ i, j, k) '' ((π₁ i, j, k) ⁻¹' S) = D.f i k ⁻¹' (D.f i j '' S) := by
  have eq₁ : _ = (π₁ i, j, k).base := PreservesPullback.iso_hom_fst (forget C) _ _
  have eq₂ : _ = (π₂ i, j, k).base := PreservesPullback.iso_hom_snd (forget C) _ _
  rw [← eq₁, ← eq₂]
  -- Porting note : `rw` to `erw` on `coe_comp`
  erw [coe_comp]
  rw [Set.image_comp]
  -- Porting note : `rw` to `erw` on `coe_comp`
  erw [coe_comp]
  rw [Set.preimage_comp, Set.image_preimage_eq, TopCat.pullback_snd_image_fst_preimage]
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
S : Set ↑↑(V D.toGlueData (i, j))
eq₁ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.fst = pullback.fst.base
eq₂ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.snd = pullback.snd.base
⊢ ⇑((forget C).map (f D.toGlueData i k)) ⁻¹' (⇑((forget C).map (f D.toGlueData i j)) '' S) =
    ⇑(f D.toGlueData i k).base ⁻¹' (⇑(f D.toGlueData i j).base '' S)
case h
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
S : Set ↑↑(V D.toGlueData (i, j))
eq₁ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.fst = pullback.fst.base
eq₂ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.snd = pullback.snd.base
⊢ Function.Surjective ⇑(PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom

Next tactic:
rfl
theorem pullback_base (i j k : D.J) (S : Set (D.V (i, j)).carrier) :
    (π₂ i, j, k) '' ((π₁ i, j, k) ⁻¹' S) = D.f i k ⁻¹' (D.f i j '' S) := by
  have eq₁ : _ = (π₁ i, j, k).base := PreservesPullback.iso_hom_fst (forget C) _ _
  have eq₂ : _ = (π₂ i, j, k).base := PreservesPullback.iso_hom_snd (forget C) _ _
  rw [← eq₁, ← eq₂]
  -- Porting note : `rw` to `erw` on `coe_comp`
  erw [coe_comp]
  rw [Set.image_comp]
  -- Porting note : `rw` to `erw` on `coe_comp`
  erw [coe_comp]
  rw [Set.preimage_comp, Set.image_preimage_eq, TopCat.pullback_snd_image_fst_preimage]
  rfl
  

Proof state:
case h
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
S : Set ↑↑(V D.toGlueData (i, j))
eq₁ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.fst = pullback.fst.base
eq₂ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.snd = pullback.snd.base
⊢ Function.Surjective ⇑(PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom

Next tactic:
rw [← TopCat.epi_iff_surjective]
theorem pullback_base (i j k : D.J) (S : Set (D.V (i, j)).carrier) :
    (π₂ i, j, k) '' ((π₁ i, j, k) ⁻¹' S) = D.f i k ⁻¹' (D.f i j '' S) := by
  have eq₁ : _ = (π₁ i, j, k).base := PreservesPullback.iso_hom_fst (forget C) _ _
  have eq₂ : _ = (π₂ i, j, k).base := PreservesPullback.iso_hom_snd (forget C) _ _
  rw [← eq₁, ← eq₂]
  -- Porting note : `rw` to `erw` on `coe_comp`
  erw [coe_comp]
  rw [Set.image_comp]
  -- Porting note : `rw` to `erw` on `coe_comp`
  erw [coe_comp]
  rw [Set.preimage_comp, Set.image_preimage_eq, TopCat.pullback_snd_image_fst_preimage]
  rfl
  rw [← TopCat.epi_iff_surjective]
  

Proof state:
case h
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
S : Set ↑↑(V D.toGlueData (i, j))
eq₁ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.fst = pullback.fst.base
eq₂ :
  (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom ≫ pullback.snd = pullback.snd.base
⊢ Epi (PreservesPullback.iso (forget C) (f D.toGlueData i j) (f D.toGlueData i k)).hom

Next tactic:
infer_instance
/-- The red and the blue arrows in ![this diagram](https://i.imgur.com/0GiBUh6.png) commute. -/
@[simp, reassoc]
theorem f_invApp_f_app (i j k : D.J) (U : Opens (D.V (i, j)).carrier) :
    (D.f_open i j).invApp U ≫ (D.f i k).c.app _ =
      (π₁ i, j, k).c.app (op U) ≫
        (π₂⁻¹ i, j, k) (unop _) ≫
          (D.V _).presheaf.map
            (eqToHom
              (by
                

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
U : Opens ↑↑(V D.toGlueData (i, j))
⊢ op
      ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion pullback.snd)).obj
        {
            unop :=
              { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop) =
    (Opens.map (f D.toGlueData i k).base).op.obj
      (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U))

Next tactic:
delta IsOpenImmersion.openFunctor
/-- The red and the blue arrows in ![this diagram](https://i.imgur.com/0GiBUh6.png) commute. -/
@[simp, reassoc]
theorem f_invApp_f_app (i j k : D.J) (U : Opens (D.V (i, j)).carrier) :
    (D.f_open i j).invApp U ≫ (D.f i k).c.app _ =
      (π₁ i, j, k).c.app (op U) ≫
        (π₂⁻¹ i, j, k) (unop _) ≫
          (D.V _).presheaf.map
            (eqToHom
              (by
                delta IsOpenImmersion.openFunctor
                

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
U : Opens ↑↑(V D.toGlueData (i, j))
⊢ op
      ((IsOpenMap.functor (_ : IsOpenMap ⇑pullback.snd.base)).obj
        {
            unop :=
              { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop) =
    (Opens.map (f D.toGlueData i k).base).op.obj
      (op ((IsOpenMap.functor (_ : IsOpenMap ⇑(f D.toGlueData i j).base)).obj U))

Next tactic:
dsimp only [Functor.op, IsOpenMap.functor, Opens.map, unop_op]
/-- The red and the blue arrows in ![this diagram](https://i.imgur.com/0GiBUh6.png) commute. -/
@[simp, reassoc]
theorem f_invApp_f_app (i j k : D.J) (U : Opens (D.V (i, j)).carrier) :
    (D.f_open i j).invApp U ≫ (D.f i k).c.app _ =
      (π₁ i, j, k).c.app (op U) ≫
        (π₂⁻¹ i, j, k) (unop _) ≫
          (D.V _).presheaf.map
            (eqToHom
              (by
                delta IsOpenImmersion.openFunctor
                dsimp only [Functor.op, IsOpenMap.functor, Opens.map, unop_op]
                

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
U : Opens ↑↑(V D.toGlueData (i, j))
⊢ op
      {
        carrier :=
          ⇑pullback.snd.base ''
            ↑{ carrier := ⇑pullback.fst.base ⁻¹' ↑U, is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑U)) },
        is_open' :=
          (_ :
            IsOpen
              (⇑pullback.snd.base ''
                ↑{ carrier := ⇑pullback.fst.base ⁻¹' ↑U, is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑U)) })) } =
    op
      {
        carrier :=
          ⇑(f D.toGlueData i k).base ⁻¹'
            ↑{ carrier := ⇑(f D.toGlueData i j).base '' ↑U,
                is_open' := (_ : IsOpen (⇑(f D.toGlueData i j).base '' ↑U)) },
        is_open' :=
          (_ :
            IsOpen
              (⇑(f D.toGlueData i k).base ⁻¹'
                ↑{ carrier := ⇑(f D.toGlueData i j).base '' ↑U,
                    is_open' := (_ : IsOpen (⇑(f D.toGlueData i j).base '' ↑U)) })) }

Next tactic:
congr
/-- The red and the blue arrows in ![this diagram](https://i.imgur.com/0GiBUh6.png) commute. -/
@[simp, reassoc]
theorem f_invApp_f_app (i j k : D.J) (U : Opens (D.V (i, j)).carrier) :
    (D.f_open i j).invApp U ≫ (D.f i k).c.app _ =
      (π₁ i, j, k).c.app (op U) ≫
        (π₂⁻¹ i, j, k) (unop _) ≫
          (D.V _).presheaf.map
            (eqToHom
              (by
                delta IsOpenImmersion.openFunctor
                dsimp only [Functor.op, IsOpenMap.functor, Opens.map, unop_op]
                congr
                

Proof state:
case e_x.e_carrier
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
U : Opens ↑↑(V D.toGlueData (i, j))
⊢ ⇑pullback.snd.base ''
      ↑{ carrier := ⇑pullback.fst.base ⁻¹' ↑U, is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑U)) } =
    ⇑(f D.toGlueData i k).base ⁻¹'
      ↑{ carrier := ⇑(f D.toGlueData i j).base '' ↑U, is_open' := (_ : IsOpen (⇑(f D.toGlueData i j).base '' ↑U)) }

Next tactic:
apply pullback_base
/-- The red and the blue arrows in ![this diagram](https://i.imgur.com/0GiBUh6.png) commute. -/
@[simp, reassoc]
theorem f_invApp_f_app (i j k : D.J) (U : Opens (D.V (i, j)).carrier) :
    (D.f_open i j).invApp U ≫ (D.f i k).c.app _ =
      (π₁ i, j, k).c.app (op U) ≫
        (π₂⁻¹ i, j, k) (unop _) ≫
          (D.V _).presheaf.map
            (eqToHom
              (by
                delta IsOpenImmersion.openFunctor
                dsimp only [Functor.op, IsOpenMap.functor, Opens.map, unop_op]
                congr
                apply pullback_base)) := by
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
U : Opens ↑↑(V D.toGlueData (i, j))
⊢ IsOpenImmersion.invApp (_ : IsOpenImmersion (f D.toGlueData i j)) U ≫
      (f D.toGlueData i k).c.app (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U)) =
    pullback.fst.c.app (op U) ≫
      IsOpenImmersion.invApp (_ : IsOpenImmersion pullback.snd)
          {
              unop :=
                { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                  is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop ≫
        (V D.toGlueData (i, k)).presheaf.map
          (eqToHom
            (_ :
              op
                  ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion pullback.snd)).obj
                    {
                        unop :=
                          { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                            is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop) =
                (Opens.map (f D.toGlueData i k).base).op.obj
                  (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U))))

Next tactic:
have := PresheafedSpace.congr_app (@pullback.condition _ _ _ _ _ (D.f i j) (D.f i k) _)
/-- The red and the blue arrows in ![this diagram](https://i.imgur.com/0GiBUh6.png) commute. -/
@[simp, reassoc]
theorem f_invApp_f_app (i j k : D.J) (U : Opens (D.V (i, j)).carrier) :
    (D.f_open i j).invApp U ≫ (D.f i k).c.app _ =
      (π₁ i, j, k).c.app (op U) ≫
        (π₂⁻¹ i, j, k) (unop _) ≫
          (D.V _).presheaf.map
            (eqToHom
              (by
                delta IsOpenImmersion.openFunctor
                dsimp only [Functor.op, IsOpenMap.functor, Opens.map, unop_op]
                congr
                apply pullback_base)) := by
  have := PresheafedSpace.congr_app (@pullback.condition _ _ _ _ _ (D.f i j) (D.f i k) _)
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
U : Opens ↑↑(V D.toGlueData (i, j))
this :
  ∀ (U : (Opens ↑↑(CategoryTheory.GlueData.U D.toGlueData i))ᵒᵖ),
    (pullback.fst ≫ f D.toGlueData i j).c.app U =
      (pullback.snd ≫ f D.toGlueData i k).c.app U ≫
        (pullback (f D.toGlueData i j) (f D.toGlueData i k)).presheaf.map
          (eqToHom
            (_ :
              (Opens.map (pullback.snd ≫ f D.toGlueData i k).base).op.obj U =
                (Opens.map (pullback.fst ≫ f D.toGlueData i j).base).op.obj U))
⊢ IsOpenImmersion.invApp (_ : IsOpenImmersion (f D.toGlueData i j)) U ≫
      (f D.toGlueData i k).c.app (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U)) =
    pullback.fst.c.app (op U) ≫
      IsOpenImmersion.invApp (_ : IsOpenImmersion pullback.snd)
          {
              unop :=
                { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                  is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop ≫
        (V D.toGlueData (i, k)).presheaf.map
          (eqToHom
            (_ :
              op
                  ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion pullback.snd)).obj
                    {
                        unop :=
                          { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                            is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop) =
                (Opens.map (f D.toGlueData i k).base).op.obj
                  (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U))))

Next tactic:
dsimp only [comp_c_app] at this
/-- The red and the blue arrows in ![this diagram](https://i.imgur.com/0GiBUh6.png) commute. -/
@[simp, reassoc]
theorem f_invApp_f_app (i j k : D.J) (U : Opens (D.V (i, j)).carrier) :
    (D.f_open i j).invApp U ≫ (D.f i k).c.app _ =
      (π₁ i, j, k).c.app (op U) ≫
        (π₂⁻¹ i, j, k) (unop _) ≫
          (D.V _).presheaf.map
            (eqToHom
              (by
                delta IsOpenImmersion.openFunctor
                dsimp only [Functor.op, IsOpenMap.functor, Opens.map, unop_op]
                congr
                apply pullback_base)) := by
  have := PresheafedSpace.congr_app (@pullback.condition _ _ _ _ _ (D.f i j) (D.f i k) _)
  dsimp only [comp_c_app] at this
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
U : Opens ↑↑(V D.toGlueData (i, j))
this :
  ∀ (U : (Opens ↑↑(CategoryTheory.GlueData.U D.toGlueData i))ᵒᵖ),
    (f D.toGlueData i j).c.app U ≫ pullback.fst.c.app (op ((Opens.map (f D.toGlueData i j).base).obj U.unop)) =
      ((f D.toGlueData i k).c.app U ≫ pullback.snd.c.app (op ((Opens.map (f D.toGlueData i k).base).obj U.unop))) ≫
        (pullback (f D.toGlueData i j) (f D.toGlueData i k)).presheaf.map
          (eqToHom
            (_ :
              (Opens.map (pullback.snd ≫ f D.toGlueData i k).base).op.obj U =
                (Opens.map (pullback.fst ≫ f D.toGlueData i j).base).op.obj U))
⊢ IsOpenImmersion.invApp (_ : IsOpenImmersion (f D.toGlueData i j)) U ≫
      (f D.toGlueData i k).c.app (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U)) =
    pullback.fst.c.app (op U) ≫
      IsOpenImmersion.invApp (_ : IsOpenImmersion pullback.snd)
          {
              unop :=
                { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                  is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop ≫
        (V D.toGlueData (i, k)).presheaf.map
          (eqToHom
            (_ :
              op
                  ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion pullback.snd)).obj
                    {
                        unop :=
                          { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                            is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop) =
                (Opens.map (f D.toGlueData i k).base).op.obj
                  (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U))))

Next tactic:
rw [← cancel_epi (inv ((D.f_open i j).invApp U)), IsIso.inv_hom_id_assoc,
    IsOpenImmersion.inv_invApp]
/-- The red and the blue arrows in ![this diagram](https://i.imgur.com/0GiBUh6.png) commute. -/
@[simp, reassoc]
theorem f_invApp_f_app (i j k : D.J) (U : Opens (D.V (i, j)).carrier) :
    (D.f_open i j).invApp U ≫ (D.f i k).c.app _ =
      (π₁ i, j, k).c.app (op U) ≫
        (π₂⁻¹ i, j, k) (unop _) ≫
          (D.V _).presheaf.map
            (eqToHom
              (by
                delta IsOpenImmersion.openFunctor
                dsimp only [Functor.op, IsOpenMap.functor, Opens.map, unop_op]
                congr
                apply pullback_base)) := by
  have := PresheafedSpace.congr_app (@pullback.condition _ _ _ _ _ (D.f i j) (D.f i k) _)
  dsimp only [comp_c_app] at this
  rw [← cancel_epi (inv ((D.f_open i j).invApp U)), IsIso.inv_hom_id_assoc,
    IsOpenImmersion.inv_invApp]
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
U : Opens ↑↑(V D.toGlueData (i, j))
this :
  ∀ (U : (Opens ↑↑(CategoryTheory.GlueData.U D.toGlueData i))ᵒᵖ),
    (f D.toGlueData i j).c.app U ≫ pullback.fst.c.app (op ((Opens.map (f D.toGlueData i j).base).obj U.unop)) =
      ((f D.toGlueData i k).c.app U ≫ pullback.snd.c.app (op ((Opens.map (f D.toGlueData i k).base).obj U.unop))) ≫
        (pullback (f D.toGlueData i j) (f D.toGlueData i k)).presheaf.map
          (eqToHom
            (_ :
              (Opens.map (pullback.snd ≫ f D.toGlueData i k).base).op.obj U =
                (Opens.map (pullback.fst ≫ f D.toGlueData i j).base).op.obj U))
⊢ (f D.toGlueData i k).c.app (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U)) =
    ((f D.toGlueData i j).c.app (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U)) ≫
        (V D.toGlueData (i, j)).presheaf.map
          (eqToHom
            (_ :
              op
                  ((Opens.map (f D.toGlueData i j).base).obj
                    (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U)).unop) =
                op U))) ≫
      pullback.fst.c.app (op U) ≫
        IsOpenImmersion.invApp (_ : IsOpenImmersion pullback.snd)
            {
                unop :=
                  { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                    is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop ≫
          (V D.toGlueData (i, k)).presheaf.map
            (eqToHom
              (_ :
                op
                    ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion pullback.snd)).obj
                      {
                          unop :=
                            { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                              is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop) =
                  (Opens.map (f D.toGlueData i k).base).op.obj
                    (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U))))

Next tactic:
simp_rw [Category.assoc]
/-- The red and the blue arrows in ![this diagram](https://i.imgur.com/0GiBUh6.png) commute. -/
@[simp, reassoc]
theorem f_invApp_f_app (i j k : D.J) (U : Opens (D.V (i, j)).carrier) :
    (D.f_open i j).invApp U ≫ (D.f i k).c.app _ =
      (π₁ i, j, k).c.app (op U) ≫
        (π₂⁻¹ i, j, k) (unop _) ≫
          (D.V _).presheaf.map
            (eqToHom
              (by
                delta IsOpenImmersion.openFunctor
                dsimp only [Functor.op, IsOpenMap.functor, Opens.map, unop_op]
                congr
                apply pullback_base)) := by
  have := PresheafedSpace.congr_app (@pullback.condition _ _ _ _ _ (D.f i j) (D.f i k) _)
  dsimp only [comp_c_app] at this
  rw [← cancel_epi (inv ((D.f_open i j).invApp U)), IsIso.inv_hom_id_assoc,
    IsOpenImmersion.inv_invApp]
  simp_rw [Category.assoc]
  

Proof state:
C : Type u
inst✝ : Category.{v, u} C
D : GlueData C
i j k : D.J
U : Opens ↑↑(V D.toGlueData (i, j))
this :
  ∀ (U : (Opens ↑↑(CategoryTheory.GlueData.U D.toGlueData i))ᵒᵖ),
    (f D.toGlueData i j).c.app U ≫ pullback.fst.c.app (op ((Opens.map (f D.toGlueData i j).base).obj U.unop)) =
      ((f D.toGlueData i k).c.app U ≫ pullback.snd.c.app (op ((Opens.map (f D.toGlueData i k).base).obj U.unop))) ≫
        (pullback (f D.toGlueData i j) (f D.toGlueData i k)).presheaf.map
          (eqToHom
            (_ :
              (Opens.map (pullback.snd ≫ f D.toGlueData i k).base).op.obj U =
                (Opens.map (pullback.fst ≫ f D.toGlueData i j).base).op.obj U))
⊢ (f D.toGlueData i k).c.app (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U)) =
    (f D.toGlueData i j).c.app (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U)) ≫
      (V D.toGlueData (i, j)).presheaf.map
          (eqToHom
            (_ :
              op
                  ((Opens.map (f D.toGlueData i j).base).obj
                    (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U)).unop) =
                op U)) ≫
        pullback.fst.c.app (op U) ≫
          IsOpenImmersion.invApp (_ : IsOpenImmersion pullback.snd)
              { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } ≫
            (V D.toGlueData (i, k)).presheaf.map
              (eqToHom
                (_ :
                  op
                      ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion pullback.snd)).obj
                        {
                            unop :=
                              { carrier := ⇑pullback.fst.base ⁻¹' ↑(op U).unop,
                                is_open' := (_ : IsOpen (⇑pullback.fst.base ⁻¹' ↑(op U).unop)) } }.unop) =
                    (Opens.map (f D.toGlueData i k).base).op.obj
                      (op ((IsOpenImmersion.openFunctor (_ : IsOpenImmersion (f D.toGlueData i j))).obj U))))

Next tactic:
erw [(π₁ i, j, k).c.naturality_assoc, reassoc_of% this, ← Functor.map_comp_assoc,
    IsOpenImmersion.inv_naturality_assoc, IsOpenImmersion.app_invApp_assoc, ←
    (D.V (i, k)).presheaf.map_comp, ← (D.V (i, k)).presheaf.map_comp]