l3lab/ntp-mathlib huggingface dataset:
/-- `(X₁ ×[Y₁] X₂) ×[Y₂] X₃` is the pullback `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)`. -/
def pullbackAssocIsPullback [HasPullback (g₂ ≫ f₃) f₄] :
    IsLimit
      (PullbackCone.mk (l₁ ≫ g₁) l₂
        (show (l₁ ≫ g₁) ≫ f₁ = l₂ ≫ g₃ ≫ f₂ by
          rw [pullback.lift_fst_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  

Proof state:
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback (pullback.snd ≫ f₃) f₄
⊢ IsLimit
    (PullbackCone.mk (pullback.fst ≫ pullback.fst)
      (pullback.lift (pullback.fst ≫ pullback.snd) pullback.snd
        (_ : (pullback.fst ≫ pullback.snd) ≫ f₃ = pullback.snd ≫ f₄))
      (_ :
        (pullback.fst ≫ pullback.fst) ≫ f₁ =
          pullback.lift (pullback.fst ≫ pullback.snd) pullback.snd
              (_ : (pullback.fst ≫ pullback.snd) ≫ f₃ = pullback.snd ≫ f₄) ≫
            pullback.fst ≫ f₂))

Next tactic:
apply PullbackCone.flipIsLimit
/-- `(X₁ ×[Y₁] X₂) ×[Y₂] X₃` is the pullback `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)`. -/
def pullbackAssocIsPullback [HasPullback (g₂ ≫ f₃) f₄] :
    IsLimit
      (PullbackCone.mk (l₁ ≫ g₁) l₂
        (show (l₁ ≫ g₁) ≫ f₁ = l₂ ≫ g₃ ≫ f₂ by
          rw [pullback.lift_fst_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  apply PullbackCone.flipIsLimit
  

Proof state:
case t
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback (pullback.snd ≫ f₃) f₄
⊢ IsLimit
    (PullbackCone.mk
      (pullback.lift (pullback.fst ≫ pullback.snd) pullback.snd
        (_ : (pullback.fst ≫ pullback.snd) ≫ f₃ = pullback.snd ≫ f₄))
      (pullback.fst ≫ pullback.fst)
      (_ :
        pullback.lift (pullback.fst ≫ pullback.snd) pullback.snd
              (_ : (pullback.fst ≫ pullback.snd) ≫ f₃ = pullback.snd ≫ f₄) ≫
            pullback.fst ≫ f₂ =
          (pullback.fst ≫ pullback.fst) ≫ f₁))

Next tactic:
apply bigSquareIsPullback
/-- `(X₁ ×[Y₁] X₂) ×[Y₂] X₃` is the pullback `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)`. -/
def pullbackAssocIsPullback [HasPullback (g₂ ≫ f₃) f₄] :
    IsLimit
      (PullbackCone.mk (l₁ ≫ g₁) l₂
        (show (l₁ ≫ g₁) ≫ f₁ = l₂ ≫ g₃ ≫ f₂ by
          rw [pullback.lift_fst_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  apply PullbackCone.flipIsLimit
  apply bigSquareIsPullback
  · 

Proof state:
case t.H
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback (pullback.snd ≫ f₃) f₄
⊢ IsLimit (PullbackCone.mk ?t.i₂ pullback.fst ?t.h₂)

Next tactic:
apply PullbackCone.flipIsLimit
/-- `(X₁ ×[Y₁] X₂) ×[Y₂] X₃` is the pullback `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)`. -/
def pullbackAssocIsPullback [HasPullback (g₂ ≫ f₃) f₄] :
    IsLimit
      (PullbackCone.mk (l₁ ≫ g₁) l₂
        (show (l₁ ≫ g₁) ≫ f₁ = l₂ ≫ g₃ ≫ f₂ by
          rw [pullback.lift_fst_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  apply PullbackCone.flipIsLimit
  apply bigSquareIsPullback
  · apply PullbackCone.flipIsLimit
    

Proof state:
case t.H.t
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback (pullback.snd ≫ f₃) f₄
⊢ IsLimit (PullbackCone.mk pullback.fst ?t.i₂ (_ : pullback.fst ≫ f₁ = ?t.i₂ ≫ f₂))

Next tactic:
exact pullbackIsPullback f₁ f₂
/-- `(X₁ ×[Y₁] X₂) ×[Y₂] X₃` is the pullback `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)`. -/
def pullbackAssocIsPullback [HasPullback (g₂ ≫ f₃) f₄] :
    IsLimit
      (PullbackCone.mk (l₁ ≫ g₁) l₂
        (show (l₁ ≫ g₁) ≫ f₁ = l₂ ≫ g₃ ≫ f₂ by
          rw [pullback.lift_fst_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  apply PullbackCone.flipIsLimit
  apply bigSquareIsPullback
  · apply PullbackCone.flipIsLimit
    exact pullbackIsPullback f₁ f₂
  · 

Proof state:
case t.H'
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback (pullback.snd ≫ f₃) f₄
⊢ IsLimit
    (PullbackCone.mk
      (pullback.lift (pullback.fst ≫ pullback.snd) pullback.snd
        (_ : (pullback.fst ≫ pullback.snd) ≫ f₃ = pullback.snd ≫ f₄))
      pullback.fst ?t.h₁)

Next tactic:
apply PullbackCone.flipIsLimit
/-- `(X₁ ×[Y₁] X₂) ×[Y₂] X₃` is the pullback `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)`. -/
def pullbackAssocIsPullback [HasPullback (g₂ ≫ f₃) f₄] :
    IsLimit
      (PullbackCone.mk (l₁ ≫ g₁) l₂
        (show (l₁ ≫ g₁) ≫ f₁ = l₂ ≫ g₃ ≫ f₂ by
          rw [pullback.lift_fst_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  apply PullbackCone.flipIsLimit
  apply bigSquareIsPullback
  · apply PullbackCone.flipIsLimit
    exact pullbackIsPullback f₁ f₂
  · apply PullbackCone.flipIsLimit
    

Proof state:
case t.H'.t
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback (pullback.snd ≫ f₃) f₄
⊢ IsLimit
    (PullbackCone.mk pullback.fst
      (pullback.lift (pullback.fst ≫ pullback.snd) pullback.snd
        (_ : (pullback.fst ≫ pullback.snd) ≫ f₃ = pullback.snd ≫ f₄))
      (_ :
        pullback.fst ≫ pullback.snd =
          pullback.lift (pullback.fst ≫ pullback.snd) pullback.snd
              (_ : (pullback.fst ≫ pullback.snd) ≫ f₃ = pullback.snd ≫ f₄) ≫
            pullback.fst))

Next tactic:
apply pullbackPullbackLeftIsPullback
/-- `(X₁ ×[Y₁] X₂) ×[Y₂] X₃` is the pullback `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)`. -/
def pullbackAssocIsPullback [HasPullback (g₂ ≫ f₃) f₄] :
    IsLimit
      (PullbackCone.mk (l₁ ≫ g₁) l₂
        (show (l₁ ≫ g₁) ≫ f₁ = l₂ ≫ g₃ ≫ f₂ by
          rw [pullback.lift_fst_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  apply PullbackCone.flipIsLimit
  apply bigSquareIsPullback
  · apply PullbackCone.flipIsLimit
    exact pullbackIsPullback f₁ f₂
  · apply PullbackCone.flipIsLimit
    apply pullbackPullbackLeftIsPullback
  · 

Proof state:
case t.h₁
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback (pullback.snd ≫ f₃) f₄
⊢ pullback.lift (pullback.fst ≫ pullback.snd) pullback.snd
        (_ : (pullback.fst ≫ pullback.snd) ≫ f₃ = pullback.snd ≫ f₄) ≫
      pullback.fst =
    pullback.fst ≫ pullback.snd

Next tactic:
exact pullback.lift_fst _ _ _
/-- `(X₁ ×[Y₁] X₂) ×[Y₂] X₃` is the pullback `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)`. -/
def pullbackAssocIsPullback [HasPullback (g₂ ≫ f₃) f₄] :
    IsLimit
      (PullbackCone.mk (l₁ ≫ g₁) l₂
        (show (l₁ ≫ g₁) ≫ f₁ = l₂ ≫ g₃ ≫ f₂ by
          rw [pullback.lift_fst_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  apply PullbackCone.flipIsLimit
  apply bigSquareIsPullback
  · apply PullbackCone.flipIsLimit
    exact pullbackIsPullback f₁ f₂
  · apply PullbackCone.flipIsLimit
    apply pullbackPullbackLeftIsPullback
  · exact pullback.lift_fst _ _ _
  · 

Proof state:
case t.h₂
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback (pullback.snd ≫ f₃) f₄
⊢ pullback.snd ≫ f₂ = pullback.fst ≫ f₁

Next tactic:
exact pullback.condition.symm
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[X₂] (X₂ ×[Y₂] X₃)`. -/
def pullbackPullbackRightIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit (PullbackCone.mk l₁' l₂' (show l₁' ≫ g₂ = l₂' ≫ g₃ from pullback.lift_snd _ _ _)) := by
  

Proof state:
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit
    (PullbackCone.mk
      (pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂))
      pullback.snd
      (_ :
        pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
              (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
            pullback.snd =
          pullback.snd ≫ pullback.fst))

Next tactic:
apply PullbackCone.flipIsLimit
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[X₂] (X₂ ×[Y₂] X₃)`. -/
def pullbackPullbackRightIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit (PullbackCone.mk l₁' l₂' (show l₁' ≫ g₂ = l₂' ≫ g₃ from pullback.lift_snd _ _ _)) := by
  apply PullbackCone.flipIsLimit
  

Proof state:
case t
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit
    (PullbackCone.mk pullback.snd
      (pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂))
      (_ :
        pullback.snd ≫ pullback.fst =
          pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
              (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
            pullback.snd))

Next tactic:
apply leftSquareIsPullback
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[X₂] (X₂ ×[Y₂] X₃)`. -/
def pullbackPullbackRightIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit (PullbackCone.mk l₁' l₂' (show l₁' ≫ g₂ = l₂' ≫ g₃ from pullback.lift_snd _ _ _)) := by
  apply PullbackCone.flipIsLimit
  apply leftSquareIsPullback
  · 

Proof state:
case t.H
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit (PullbackCone.mk pullback.snd ?t.f₂ ?t.h₂)

Next tactic:
apply PullbackCone.flipIsLimit
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[X₂] (X₂ ×[Y₂] X₃)`. -/
def pullbackPullbackRightIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit (PullbackCone.mk l₁' l₂' (show l₁' ≫ g₂ = l₂' ≫ g₃ from pullback.lift_snd _ _ _)) := by
  apply PullbackCone.flipIsLimit
  apply leftSquareIsPullback
  · apply PullbackCone.flipIsLimit
    

Proof state:
case t.H.t
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit (PullbackCone.mk ?t.f₂ pullback.snd (_ : ?t.f₂ ≫ ?t.i₃ = pullback.snd ≫ ?t.g₂))

Next tactic:
exact pullbackIsPullback f₁ f₂
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[X₂] (X₂ ×[Y₂] X₃)`. -/
def pullbackPullbackRightIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit (PullbackCone.mk l₁' l₂' (show l₁' ≫ g₂ = l₂' ≫ g₃ from pullback.lift_snd _ _ _)) := by
  apply PullbackCone.flipIsLimit
  apply leftSquareIsPullback
  · apply PullbackCone.flipIsLimit
    exact pullbackIsPullback f₁ f₂
  · 

Proof state:
case t.H'
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit
    (PullbackCone.mk pullback.snd
      (pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
          (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
        pullback.fst)
      (_ :
        pullback.snd ≫ pullback.fst ≫ f₂ =
          (pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
                (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
              pullback.fst) ≫
            f₁))

Next tactic:
apply PullbackCone.flipIsLimit
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[X₂] (X₂ ×[Y₂] X₃)`. -/
def pullbackPullbackRightIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit (PullbackCone.mk l₁' l₂' (show l₁' ≫ g₂ = l₂' ≫ g₃ from pullback.lift_snd _ _ _)) := by
  apply PullbackCone.flipIsLimit
  apply leftSquareIsPullback
  · apply PullbackCone.flipIsLimit
    exact pullbackIsPullback f₁ f₂
  · apply PullbackCone.flipIsLimit
    

Proof state:
case t.H'.t
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit
    (PullbackCone.mk
      (pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
          (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
        pullback.fst)
      pullback.snd
      (_ :
        (pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
                (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
              pullback.fst) ≫
            f₁ =
          pullback.snd ≫ pullback.fst ≫ f₂))

Next tactic:
convert pullbackIsPullback f₁ (g₃ ≫ f₂)
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[X₂] (X₂ ×[Y₂] X₃)`. -/
def pullbackPullbackRightIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit (PullbackCone.mk l₁' l₂' (show l₁' ≫ g₂ = l₂' ≫ g₃ from pullback.lift_snd _ _ _)) := by
  apply PullbackCone.flipIsLimit
  apply leftSquareIsPullback
  · apply PullbackCone.flipIsLimit
    exact pullbackIsPullback f₁ f₂
  · apply PullbackCone.flipIsLimit
    convert pullbackIsPullback f₁ (g₃ ≫ f₂)
    

Proof state:
case h.e'_6.h.e'_9
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
      pullback.fst =
    pullback.fst

Next tactic:
rw [pullback.lift_fst]
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[X₂] (X₂ ×[Y₂] X₃)`. -/
def pullbackPullbackRightIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit (PullbackCone.mk l₁' l₂' (show l₁' ≫ g₂ = l₂' ≫ g₃ from pullback.lift_snd _ _ _)) := by
  apply PullbackCone.flipIsLimit
  apply leftSquareIsPullback
  · apply PullbackCone.flipIsLimit
    exact pullbackIsPullback f₁ f₂
  · apply PullbackCone.flipIsLimit
    convert pullbackIsPullback f₁ (g₃ ≫ f₂)
    rw [pullback.lift_fst]
  · 

Proof state:
case t.h₂
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ pullback.snd ≫ f₂ = pullback.fst ≫ f₁

Next tactic:
exact pullback.condition.symm
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[Y₂] X₃`. -/
def pullbackAssocSymmIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit
      (PullbackCone.mk l₁' (l₂' ≫ g₄)
        (show l₁' ≫ g₂ ≫ f₃ = (l₂' ≫ g₄) ≫ f₄ by
          

Proof state:
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
      pullback.snd ≫ f₃ =
    (pullback.snd ≫ pullback.snd) ≫ f₄

Next tactic:
rw [pullback.lift_snd_assoc, Category.assoc, Category.assoc, pullback.condition]
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[Y₂] X₃`. -/
def pullbackAssocSymmIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit
      (PullbackCone.mk l₁' (l₂' ≫ g₄)
        (show l₁' ≫ g₂ ≫ f₃ = (l₂' ≫ g₄) ≫ f₄ by
          rw [pullback.lift_snd_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  

Proof state:
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit
    (PullbackCone.mk
      (pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂))
      (pullback.snd ≫ pullback.snd)
      (_ :
        pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
              (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
            pullback.snd ≫ f₃ =
          (pullback.snd ≫ pullback.snd) ≫ f₄))

Next tactic:
apply bigSquareIsPullback
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[Y₂] X₃`. -/
def pullbackAssocSymmIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit
      (PullbackCone.mk l₁' (l₂' ≫ g₄)
        (show l₁' ≫ g₂ ≫ f₃ = (l₂' ≫ g₄) ≫ f₄ by
          rw [pullback.lift_snd_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  apply bigSquareIsPullback
  

Proof state:
case H
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit (PullbackCone.mk ?i₂ pullback.snd ?h₂)
case H'
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit
    (PullbackCone.mk
      (pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂))
      pullback.snd ?h₁)
case i₂
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ Z₂ ⟶ X₂
case h₁
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
      pullback.snd =
    pullback.snd ≫ ?i₂
case h₂
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ ?i₂ ≫ f₃ = pullback.snd ≫ f₄

Next tactic:
exact pullbackIsPullback f₃ f₄
/-- `X₁ ×[Y₁] (X₂ ×[Y₂] X₃)` is the pullback `(X₁ ×[Y₁] X₂) ×[Y₂] X₃`. -/
def pullbackAssocSymmIsPullback [HasPullback f₁ (g₃ ≫ f₂)] :
    IsLimit
      (PullbackCone.mk l₁' (l₂' ≫ g₄)
        (show l₁' ≫ g₂ ≫ f₃ = (l₂' ≫ g₄) ≫ f₄ by
          rw [pullback.lift_snd_assoc, Category.assoc, Category.assoc, pullback.condition])) := by
  apply bigSquareIsPullback
  exact pullbackIsPullback f₃ f₄
  

Proof state:
case H'
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ IsLimit
    (PullbackCone.mk
      (pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂))
      pullback.snd ?h₁)
case h₁
C : Type u
inst✝⁴ : Category.{v, u} C
D : Type u₂
inst✝³ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝² : HasPullback f₁ f₂
inst✝¹ : HasPullback f₃ f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
      pullback.snd =
    pullback.snd ≫ pullback.fst

Next tactic:
apply pullbackPullbackRightIsPullback
@[reassoc (attr := simp)]
theorem pullbackAssoc_inv_fst_fst [HasPullback ((pullback.snd : Z₁ ⟶ X₂) ≫ f₃) f₄]
    [HasPullback f₁ ((pullback.fst : Z₂ ⟶ X₂) ≫ f₂)] :
    (pullbackAssoc f₁ f₂ f₃ f₄).inv ≫ pullback.fst ≫ pullback.fst = pullback.fst := by
  

Proof state:
C : Type u
inst✝⁷ : Category.{v, u} C
D : Type u₂
inst✝⁶ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝⁵ : HasPullback f₁ f₂
inst✝⁴ : HasPullback f₃ f₄
inst✝³ : HasPullback (pullback.snd ≫ sorryAx (sorryAx C true ⟶ Y₂) true) f₄
inst✝² : HasPullback f₁ (pullback.fst ≫ f₂)
inst✝¹ : HasPullback (pullback.snd ≫ f₃) f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ (pullbackAssoc f₁ f₂ f₃ f₄).inv ≫ pullback.fst ≫ pullback.fst = pullback.fst

Next tactic:
trans l₁' ≫ pullback.fst
@[reassoc (attr := simp)]
theorem pullbackAssoc_inv_fst_fst [HasPullback ((pullback.snd : Z₁ ⟶ X₂) ≫ f₃) f₄]
    [HasPullback f₁ ((pullback.fst : Z₂ ⟶ X₂) ≫ f₂)] :
    (pullbackAssoc f₁ f₂ f₃ f₄).inv ≫ pullback.fst ≫ pullback.fst = pullback.fst := by
  trans l₁' ≫ pullback.fst
  

Proof state:
C : Type u
inst✝⁷ : Category.{v, u} C
D : Type u₂
inst✝⁶ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝⁵ : HasPullback f₁ f₂
inst✝⁴ : HasPullback f₃ f₄
inst✝³ : HasPullback (pullback.snd ≫ sorryAx (sorryAx C true ⟶ Y₂) true) f₄
inst✝² : HasPullback f₁ (pullback.fst ≫ f₂)
inst✝¹ : HasPullback (pullback.snd ≫ f₃) f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ (pullbackAssoc f₁ f₂ f₃ f₄).inv ≫ pullback.fst ≫ pullback.fst =
    pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
      pullback.fst
C : Type u
inst✝⁷ : Category.{v, u} C
D : Type u₂
inst✝⁶ : Category.{v₂, u₂} D
W X Y Z X₁ X₂ X₃ Y₁ Y₂ : C
f₁ : X₁ ⟶ Y₁
f₂ : X₂ ⟶ Y₁
f₃ : X₂ ⟶ Y₂
f₄ : X₃ ⟶ Y₂
inst✝⁵ : HasPullback f₁ f₂
inst✝⁴ : HasPullback f₃ f₄
inst✝³ : HasPullback (pullback.snd ≫ sorryAx (sorryAx C true ⟶ Y₂) true) f₄
inst✝² : HasPullback f₁ (pullback.fst ≫ f₂)
inst✝¹ : HasPullback (pullback.snd ≫ f₃) f₄
inst✝ : HasPullback f₁ (pullback.fst ≫ f₂)
⊢ pullback.lift pullback.fst (pullback.snd ≫ pullback.fst)
        (_ : pullback.fst ≫ f₁ = (pullback.snd ≫ pullback.fst) ≫ f₂) ≫
      pullback.fst =
    pullback.fst

Next tactic:
rw [← Category.assoc]