l3lab/ntp-mathlib huggingface dataset:
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> 

Proof state:
case some.left.some.right
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f✝ : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f✝ ≫ inl = g ≫ inr
f : some WalkingPair.left ⟶ some WalkingPair.right
⊢ (span f✝ g).map f ≫
      (fun j => Option.casesOn j (f✝ ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.right) =
    (fun j => Option.casesOn j (f✝ ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.left) ≫
      ((Functor.const WalkingSpan).obj W).map f

Next tactic:
cases f
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> 

Proof state:
case some.right.none
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f✝ : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f✝ ≫ inl = g ≫ inr
f : some WalkingPair.right ⟶ none
⊢ (span f✝ g).map f ≫ (fun j => Option.casesOn j (f✝ ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) none =
    (fun j => Option.casesOn j (f✝ ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.right) ≫
      ((Functor.const WalkingSpan).obj W).map f

Next tactic:
cases f
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> 

Proof state:
case some.right.some.left
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f✝ : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f✝ ≫ inl = g ≫ inr
f : some WalkingPair.right ⟶ some WalkingPair.left
⊢ (span f✝ g).map f ≫
      (fun j => Option.casesOn j (f✝ ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.left) =
    (fun j => Option.casesOn j (f✝ ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.right) ≫
      ((Functor.const WalkingSpan).obj W).map f

Next tactic:
cases f
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> 

Proof state:
case some.right.some.right
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f✝ : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f✝ ≫ inl = g ≫ inr
f : some WalkingPair.right ⟶ some WalkingPair.right
⊢ (span f✝ g).map f ≫
      (fun j => Option.casesOn j (f✝ ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.right) =
    (fun j => Option.casesOn j (f✝ ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.right) ≫
      ((Functor.const WalkingSpan).obj W).map f

Next tactic:
cases f
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> 

Proof state:
case none.none.id
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ (span f g).map (WidePushoutShape.Hom.id none) ≫
      (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) none =
    (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) none ≫
      ((Functor.const WalkingSpan).obj W).map (WidePushoutShape.Hom.id none)

Next tactic:
dsimp
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> 

Proof state:
case none.some.left.init
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ (span f g).map (init WalkingPair.left) ≫
      (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.left) =
    (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) none ≫
      ((Functor.const WalkingSpan).obj W).map (init WalkingPair.left)

Next tactic:
dsimp
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> 

Proof state:
case none.some.right.init
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ (span f g).map (init WalkingPair.right) ≫
      (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.right) =
    (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) none ≫
      ((Functor.const WalkingSpan).obj W).map (init WalkingPair.right)

Next tactic:
dsimp
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> 

Proof state:
case some.left.some.left.id
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ (span f g).map (WidePushoutShape.Hom.id (some WalkingPair.left)) ≫
      (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.left) =
    (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.left) ≫
      ((Functor.const WalkingSpan).obj W).map (WidePushoutShape.Hom.id (some WalkingPair.left))

Next tactic:
dsimp
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> 

Proof state:
case some.right.some.right.id
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ (span f g).map (WidePushoutShape.Hom.id (some WalkingPair.right)) ≫
      (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.right) =
    (fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr) (some WalkingPair.right) ≫
      ((Functor.const WalkingSpan).obj W).map (WidePushoutShape.Hom.id (some WalkingPair.right))

Next tactic:
dsimp
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> dsimp <;> 

Proof state:
case none.none.id
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ (span f g).map (𝟙 none) ≫ f ≫ inl = (f ≫ inl) ≫ 𝟙 W

Next tactic:
aesop
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> dsimp <;> 

Proof state:
case none.some.left.init
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ f ≫ inl = (f ≫ inl) ≫ 𝟙 W

Next tactic:
aesop
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> dsimp <;> 

Proof state:
case none.some.right.init
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ g ≫ inr = (f ≫ inl) ≫ 𝟙 W

Next tactic:
aesop
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> dsimp <;> 

Proof state:
case some.left.some.left.id
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ (span f g).map (𝟙 (some WalkingPair.left)) ≫ inl = inl ≫ 𝟙 W

Next tactic:
aesop
/-- A pushout cocone on `f` and `g` is determined by morphisms `inl : Y ⟶ W` and `inr : Z ⟶ W` such
    that `f ≫ inl = g ↠ inr`. -/
@[simps]
def mk {W : C} (inl : Y ⟶ W) (inr : Z ⟶ W) (eq : f ≫ inl = g ≫ inr) : PushoutCocone f g where
  pt := W
  ι := { app := fun j => Option.casesOn j (f ≫ inl) fun j' => WalkingPair.casesOn j' inl inr
         naturality := by
          rintro (⟨⟩|⟨⟨⟩⟩) (⟨⟩|⟨⟨⟩⟩) <;> intro f <;> cases f <;> dsimp <;> 

Proof state:
case some.right.some.right.id
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
inl : Y ⟶ W
inr : Z ⟶ W
eq : f ≫ inl = g ≫ inr
⊢ (span f g).map (𝟙 (some WalkingPair.right)) ≫ inr = inr ≫ 𝟙 W

Next tactic:
aesop
/-- To check whether a morphism is coequalized by the maps of a pushout cocone, it suffices to check
  it for `inl t` and `inr t` -/
theorem coequalizer_ext (t : PushoutCocone f g) {W : C} {k l : t.pt ⟶ W}
    (h₀ : inl t ≫ k = inl t ≫ l) (h₁ : inr t ≫ k = inr t ≫ l) :
    ∀ j : WalkingSpan, t.ι.app j ≫ k = t.ι.app j ≫ l
  | some WalkingPair.left => h₀
  | some WalkingPair.right => h₁
  | none => by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
t : PushoutCocone f g
W : C
k l : t.pt ⟶ W
h₀ : inl t ≫ k = inl t ≫ l
h₁ : inr t ≫ k = inr t ≫ l
⊢ t.ι.app none ≫ k = t.ι.app none ≫ l

Next tactic:
rw [← t.w fst, Category.assoc, Category.assoc, h₀]
/-- If `t` is a colimit pushout cocone over `f` and `g` and `h : Y ⟶ W` and `k : Z ⟶ W` are
    morphisms satisfying `f ≫ h = g ≫ k`, then we have a factorization `l : t.pt ⟶ W` such that
    `inl t ≫ l = h` and `inr t ≫ l = k`. -/
def IsColimit.desc' {t : PushoutCocone f g} (ht : IsColimit t) {W : C} (h : Y ⟶ W) (k : Z ⟶ W)
    (w : f ≫ h = g ≫ k) : { l : t.pt ⟶ W // inl t ≫ l = h ∧ inr t ≫ l = k } :=
  ⟨IsColimit.desc ht h k w, by 

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
t : PushoutCocone f g
ht : IsColimit t
W : C
h : Y ⟶ W
k : Z ⟶ W
w : f ≫ h = g ≫ k
⊢ inl t ≫ desc ht h k w = h ∧ inr t ≫ desc ht h k w = k

Next tactic:
simp
theorem epi_inr_of_is_pushout_of_epi {t : PushoutCocone f g} (ht : IsColimit t) [Epi f] :
    Epi t.inr :=
  ⟨fun {W} h k i => IsColimit.hom_ext ht (by 

Proof state:
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
t : PushoutCocone f g
ht : IsColimit t
inst✝ : Epi f
W : C
h k : t.pt ⟶ W
i : inr t ≫ h = inr t ≫ k
⊢ inl t ≫ h = inl t ≫ k

Next tactic:
simp [← cancel_epi f, t.condition_assoc, i]
theorem epi_inl_of_is_pushout_of_epi {t : PushoutCocone f g} (ht : IsColimit t) [Epi g] :
    Epi t.inl :=
  ⟨fun {W} h k i => IsColimit.hom_ext ht i (by 

Proof state:
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
t : PushoutCocone f g
ht : IsColimit t
inst✝ : Epi g
W : C
h k : t.pt ⟶ W
i : inl t ≫ h = inl t ≫ k
⊢ inr t ≫ h = inr t ≫ k

Next tactic:
simp [← cancel_epi g, ← t.condition_assoc, i]
/-- The flip of a pushout square is a pushout square. -/
def flipIsColimit {W : C} {h : Y ⟶ W} {k : Z ⟶ W} {comm : f ≫ h = g ≫ k}
    (t : IsColimit (mk _ _ comm.symm)) : IsColimit (mk _ _ comm) :=
  isColimitAux' _ fun s => by
    

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
h : Y ⟶ W
k : Z ⟶ W
comm : f ≫ h = g ≫ k
t : IsColimit (mk k h (_ : g ≫ k = f ≫ h))
s : PushoutCocone f g
⊢ { l //
    inl (mk h k comm) ≫ l = inl s ∧
      inr (mk h k comm) ≫ l = inr s ∧
        ∀ {m : (mk h k comm).pt ⟶ s.pt}, inl (mk h k comm) ≫ m = inl s → inr (mk h k comm) ≫ m = inr s → m = l }

Next tactic:
refine'
      ⟨(IsColimit.desc' t _ _ s.condition.symm).1, (IsColimit.desc' t _ _ _).2.2,
        (IsColimit.desc' t _ _ _).2.1, fun m₁ m₂ => t.hom_ext _⟩
/-- The flip of a pushout square is a pushout square. -/
def flipIsColimit {W : C} {h : Y ⟶ W} {k : Z ⟶ W} {comm : f ≫ h = g ≫ k}
    (t : IsColimit (mk _ _ comm.symm)) : IsColimit (mk _ _ comm) :=
  isColimitAux' _ fun s => by
    refine'
      ⟨(IsColimit.desc' t _ _ s.condition.symm).1, (IsColimit.desc' t _ _ _).2.2,
        (IsColimit.desc' t _ _ _).2.1, fun m₁ m₂ => t.hom_ext _⟩
    

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
h : Y ⟶ W
k : Z ⟶ W
comm : f ≫ h = g ≫ k
t : IsColimit (mk k h (_ : g ≫ k = f ≫ h))
s : PushoutCocone f g
m✝ : (mk h k comm).pt ⟶ s.pt
m₁ : inl (mk h k comm) ≫ m✝ = inl s
m₂ : inr (mk h k comm) ≫ m✝ = inr s
⊢ ∀ (j : WalkingSpan),
    (mk k h (_ : g ≫ k = f ≫ h)).ι.app j ≫ m✝ =
      (mk k h (_ : g ≫ k = f ≫ h)).ι.app j ≫ ↑(IsColimit.desc' t (inr s) (inl s) (_ : g ≫ inr s = f ≫ inl s))

Next tactic:
apply (mk k h _).coequalizer_ext
/-- The flip of a pushout square is a pushout square. -/
def flipIsColimit {W : C} {h : Y ⟶ W} {k : Z ⟶ W} {comm : f ≫ h = g ≫ k}
    (t : IsColimit (mk _ _ comm.symm)) : IsColimit (mk _ _ comm) :=
  isColimitAux' _ fun s => by
    refine'
      ⟨(IsColimit.desc' t _ _ s.condition.symm).1, (IsColimit.desc' t _ _ _).2.2,
        (IsColimit.desc' t _ _ _).2.1, fun m₁ m₂ => t.hom_ext _⟩
    apply (mk k h _).coequalizer_ext
    · 

Proof state:
case h₀
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
h : Y ⟶ W
k : Z ⟶ W
comm : f ≫ h = g ≫ k
t : IsColimit (mk k h (_ : g ≫ k = f ≫ h))
s : PushoutCocone f g
m✝ : (mk h k comm).pt ⟶ s.pt
m₁ : inl (mk h k comm) ≫ m✝ = inl s
m₂ : inr (mk h k comm) ≫ m✝ = inr s
⊢ inl (mk k h (_ : g ≫ k = f ≫ h)) ≫ m✝ =
    inl (mk k h (_ : g ≫ k = f ≫ h)) ≫ ↑(IsColimit.desc' t (inr s) (inl s) (_ : g ≫ inr s = f ≫ inl s))

Next tactic:
rwa [(IsColimit.desc' t _ _ _).2.1]
/-- The flip of a pushout square is a pushout square. -/
def flipIsColimit {W : C} {h : Y ⟶ W} {k : Z ⟶ W} {comm : f ≫ h = g ≫ k}
    (t : IsColimit (mk _ _ comm.symm)) : IsColimit (mk _ _ comm) :=
  isColimitAux' _ fun s => by
    refine'
      ⟨(IsColimit.desc' t _ _ s.condition.symm).1, (IsColimit.desc' t _ _ _).2.2,
        (IsColimit.desc' t _ _ _).2.1, fun m₁ m₂ => t.hom_ext _⟩
    apply (mk k h _).coequalizer_ext
    · rwa [(IsColimit.desc' t _ _ _).2.1]
    · 

Proof state:
case h₁
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W✝ X Y Z : C
f : X ⟶ Y
g : X ⟶ Z
W : C
h : Y ⟶ W
k : Z ⟶ W
comm : f ≫ h = g ≫ k
t : IsColimit (mk k h (_ : g ≫ k = f ≫ h))
s : PushoutCocone f g
m✝ : (mk h k comm).pt ⟶ s.pt
m₁ : inl (mk h k comm) ≫ m✝ = inl s
m₂ : inr (mk h k comm) ≫ m✝ = inr s
⊢ inr (mk k h (_ : g ≫ k = f ≫ h)) ≫ m✝ =
    inr (mk k h (_ : g ≫ k = f ≫ h)) ≫ ↑(IsColimit.desc' t (inr s) (inl s) (_ : g ≫ inr s = f ≫ inl s))

Next tactic:
rwa [(IsColimit.desc' t _ _ _).2.2]
/--
The pushout cocone `(𝟙 X, 𝟙 X)` for the pair `(f, f)` is a colimit if `f` is an epi. The converse is
shown in `epi_of_isColimit_mk_id_id`.
-/
def isColimitMkIdId (f : X ⟶ Y) [Epi f] : IsColimit (mk (𝟙 Y) (𝟙 Y) rfl : PushoutCocone f f) :=
  IsColimit.mk _ (fun s => s.inl) (fun s => Category.id_comp _)
    (fun s => by 

Proof state:
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W X Y Z : C
f✝ : X ⟶ Y
g : X ⟶ Z
f : X ⟶ Y
inst✝ : Epi f
s : PushoutCocone f f
⊢ 𝟙 Y ≫ (fun s => inl s) s = inr s

Next tactic:
rw [← cancel_epi f, Category.id_comp, s.condition]
/--
The pushout cocone `(𝟙 X, 𝟙 X)` for the pair `(f, f)` is a colimit if `f` is an epi. The converse is
shown in `epi_of_isColimit_mk_id_id`.
-/
def isColimitMkIdId (f : X ⟶ Y) [Epi f] : IsColimit (mk (𝟙 Y) (𝟙 Y) rfl : PushoutCocone f f) :=
  IsColimit.mk _ (fun s => s.inl) (fun s => Category.id_comp _)
    (fun s => by rw [← cancel_epi f, Category.id_comp, s.condition]) fun s m m₁ _ => by
    

Proof state:
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W X Y Z : C
f✝ : X ⟶ Y
g : X ⟶ Z
f : X ⟶ Y
inst✝ : Epi f
s : PushoutCocone f f
m : Y ⟶ s.pt
m₁ : 𝟙 Y ≫ m = inl s
x✝ : 𝟙 Y ≫ m = inr s
⊢ m = (fun s => inl s) s

Next tactic:
simpa using m₁
/-- `f` is an epi if the pushout cocone `(𝟙 X, 𝟙 X)` is a colimit for the pair `(f, f)`.
The converse is given in `PushoutCocone.isColimitMkIdId`.
-/
theorem epi_of_isColimitMkIdId (f : X ⟶ Y)
    (t : IsColimit (mk (𝟙 Y) (𝟙 Y) rfl : PushoutCocone f f)) : Epi f :=
  ⟨fun {Z} g h eq => by
    

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W X Y Z✝ : C
f✝ : X ⟶ Y
g✝ : X ⟶ Z✝
f : X ⟶ Y
t : IsColimit (mk (𝟙 Y) (𝟙 Y) (_ : f ≫ 𝟙 Y = f ≫ 𝟙 Y))
Z : C
g h : Y ⟶ Z
eq : f ≫ g = f ≫ h
⊢ g = h

Next tactic:
rcases PushoutCocone.IsColimit.desc' t _ _ eq with ⟨_, rfl, rfl⟩
/-- `f` is an epi if the pushout cocone `(𝟙 X, 𝟙 X)` is a colimit for the pair `(f, f)`.
The converse is given in `PushoutCocone.isColimitMkIdId`.
-/
theorem epi_of_isColimitMkIdId (f : X ⟶ Y)
    (t : IsColimit (mk (𝟙 Y) (𝟙 Y) rfl : PushoutCocone f f)) : Epi f :=
  ⟨fun {Z} g h eq => by
    rcases PushoutCocone.IsColimit.desc' t _ _ eq with ⟨_, rfl, rfl⟩
    

Proof state:
case mk.intro
C : Type u
inst✝¹ : Category.{v, u} C
D : Type u₂
inst✝ : Category.{v₂, u₂} D
W X Y Z✝ : C
f✝ : X ⟶ Y
g : X ⟶ Z✝
f : X ⟶ Y
t : IsColimit (mk (𝟙 Y) (𝟙 Y) (_ : f ≫ 𝟙 Y = f ≫ 𝟙 Y))
Z : C
val✝ : (mk (𝟙 Y) (𝟙 Y) (_ : f ≫ 𝟙 Y = f ≫ 𝟙 Y)).pt ⟶ Z
eq : f ≫ inl (mk (𝟙 Y) (𝟙 Y) (_ : f ≫ 𝟙 Y = f ≫ 𝟙 Y)) ≫ val✝ = f ≫ inr (mk (𝟙 Y) (𝟙 Y) (_ : f ≫ 𝟙 Y = f ≫ 𝟙 Y)) ≫ val✝
⊢ inl (mk (𝟙 Y) (𝟙 Y) (_ : f ≫ 𝟙 Y = f ≫ 𝟙 Y)) ≫ val✝ = inr (mk (𝟙 Y) (𝟙 Y) (_ : f ≫ 𝟙 Y = f ≫ 𝟙 Y)) ≫ val✝

Next tactic:
rfl
/-- Suppose `f` and `g` are two morphisms with a common domain and `s` is a colimit cocone over the
    diagram formed by `f` and `g`. Suppose `f` and `g` both factor through an epimorphism `h` via
    `x` and `y`, respectively. Then `s` is also a colimit cocone over the diagram formed by `x` and
    `y`.  -/
def isColimitOfFactors (f : X ⟶ Y) (g : X ⟶ Z) (h : X ⟶ W) [Epi h] (x : W ⟶ Y) (y : W ⟶ Z)
    (hhx : h ≫ x = f) (hhy : h ≫ y = g) (s : PushoutCocone f g) (hs : IsColimit s) :
    have reassoc₁ : h ≫ x ≫ inl s = f ≫ inl s := by  -- Porting note: working around reassoc
      

Proof state:
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W X Y Z : C
f✝ : X ⟶ Y
g✝ : X ⟶ Z
f : X ⟶ Y
g : X ⟶ Z
h : X ⟶ W
inst✝ : Epi h
x : W ⟶ Y
y : W ⟶ Z
hhx : h ≫ x = f
hhy : h ≫ y = g
s : PushoutCocone f g
hs : IsColimit s
⊢ h ≫ x ≫ inl s = f ≫ inl s

Next tactic:
rw [← Category.assoc]
/-- Suppose `f` and `g` are two morphisms with a common domain and `s` is a colimit cocone over the
    diagram formed by `f` and `g`. Suppose `f` and `g` both factor through an epimorphism `h` via
    `x` and `y`, respectively. Then `s` is also a colimit cocone over the diagram formed by `x` and
    `y`.  -/
def isColimitOfFactors (f : X ⟶ Y) (g : X ⟶ Z) (h : X ⟶ W) [Epi h] (x : W ⟶ Y) (y : W ⟶ Z)
    (hhx : h ≫ x = f) (hhy : h ≫ y = g) (s : PushoutCocone f g) (hs : IsColimit s) :
    have reassoc₁ : h ≫ x ≫ inl s = f ≫ inl s := by  -- Porting note: working around reassoc
      rw [← Category.assoc]; 

Proof state:
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W X Y Z : C
f✝ : X ⟶ Y
g✝ : X ⟶ Z
f : X ⟶ Y
g : X ⟶ Z
h : X ⟶ W
inst✝ : Epi h
x : W ⟶ Y
y : W ⟶ Z
hhx : h ≫ x = f
hhy : h ≫ y = g
s : PushoutCocone f g
hs : IsColimit s
⊢ (h ≫ x) ≫ inl s = f ≫ inl s

Next tactic:
apply congrArg (· ≫ inl s) hhx
/-- Suppose `f` and `g` are two morphisms with a common domain and `s` is a colimit cocone over the
    diagram formed by `f` and `g`. Suppose `f` and `g` both factor through an epimorphism `h` via
    `x` and `y`, respectively. Then `s` is also a colimit cocone over the diagram formed by `x` and
    `y`.  -/
def isColimitOfFactors (f : X ⟶ Y) (g : X ⟶ Z) (h : X ⟶ W) [Epi h] (x : W ⟶ Y) (y : W ⟶ Z)
    (hhx : h ≫ x = f) (hhy : h ≫ y = g) (s : PushoutCocone f g) (hs : IsColimit s) :
    have reassoc₁ : h ≫ x ≫ inl s = f ≫ inl s := by  -- Porting note: working around reassoc
      rw [← Category.assoc]; apply congrArg (· ≫ inl s) hhx
    have reassoc₂ : h ≫ y ≫ inr s = g ≫ inr s := by
      

Proof state:
C : Type u
inst✝² : Category.{v, u} C
D : Type u₂
inst✝¹ : Category.{v₂, u₂} D
W X Y Z : C
f✝ : X ⟶ Y
g✝ : X ⟶ Z
f : X ⟶ Y
g : X ⟶ Z
h : X ⟶ W
inst✝ : Epi h
x : W ⟶ Y
y : W ⟶ Z
hhx : h ≫ x = f
hhy : h ≫ y = g
s : PushoutCocone f g
hs : IsColimit s
reassoc₁ : h ≫ x ≫ inl s = f ≫ inl s
⊢ h ≫ y ≫ inr s = g ≫ inr s

Next tactic:
rw [← Category.assoc]