l3lab/ntp-mathlib huggingface dataset:
/-- An auxiliary equivalence to be used in `multiequalizerEquiv` below.-/
def multiequalizerEquivAux (I : MulticospanIndex C) :
    (I.multicospan ⋙ forget C).sections ≃
    { x : ∀ i : I.L, I.left i // ∀ i : I.R, I.fst i (x _) = I.snd i (x _) } where
  toFun x :=
    ⟨fun i => x.1 (WalkingMulticospan.left _), fun i => by
      have a := x.2 (WalkingMulticospan.Hom.fst i)
      have b := x.2 (WalkingMulticospan.Hom.snd i)
      rw [← b] at a
      exact a⟩
  invFun x :=
    { val := fun j =>
        match j with
        | WalkingMulticospan.left a => x.1 _
        | WalkingMulticospan.right b => I.fst b (x.1 _)
      property := by
        rintro (a | b) (a' | b') (f | f | f)
        · simp
        · rfl
        · dsimp
          exact (x.2 b').symm
        · simp }
  left_inv := by
    intro x; ext (a | b)
    · rfl
    · rw [← x.2 (WalkingMulticospan.Hom.fst b)]
      

Proof state:
case a.h.right
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : ConcreteCategory C
I : MulticospanIndex C
x : ↑(Functor.sections (MulticospanIndex.multicospan I ⋙ forget C))
b : I.R
⊢ ↑((fun x =>
            {
              val := fun j =>
                match j with
                | WalkingMulticospan.left a => ↑x a
                | WalkingMulticospan.right b => (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)),
              property :=
                (_ :
                  ∀ {j j' : WalkingMulticospan I.fstTo I.sndTo} (f : j ⟶ j'),
                    (MulticospanIndex.multicospan I ⋙ forget C).map f
                        ((fun j =>
                            match j with
                            | WalkingMulticospan.left a => ↑x a
                            | WalkingMulticospan.right b =>
                              (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)))
                          j) =
                      (fun j =>
                          match j with
                          | WalkingMulticospan.left a => ↑x a
                          | WalkingMulticospan.right b => (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)))
                        j') })
          ((fun x =>
              { val := fun i => ↑x (WalkingMulticospan.left i),
                property :=
                  (_ :
                    ∀ (i : I.R),
                      (MulticospanIndex.multicospan I ⋙ forget C).map (WalkingMulticospan.Hom.fst i)
                          (↑x (WalkingMulticospan.left (MulticospanIndex.fstTo I i))) =
                        (MulticospanIndex.multicospan I ⋙ forget C).map (WalkingMulticospan.Hom.snd i)
                          (↑x (WalkingMulticospan.left (MulticospanIndex.sndTo I i)))) })
            x))
      (WalkingMulticospan.right b) =
    (MulticospanIndex.multicospan I ⋙ forget C).map (WalkingMulticospan.Hom.fst b)
      (↑x (WalkingMulticospan.left (MulticospanIndex.fstTo I b)))

Next tactic:
rfl
/-- An auxiliary equivalence to be used in `multiequalizerEquiv` below.-/
def multiequalizerEquivAux (I : MulticospanIndex C) :
    (I.multicospan ⋙ forget C).sections ≃
    { x : ∀ i : I.L, I.left i // ∀ i : I.R, I.fst i (x _) = I.snd i (x _) } where
  toFun x :=
    ⟨fun i => x.1 (WalkingMulticospan.left _), fun i => by
      have a := x.2 (WalkingMulticospan.Hom.fst i)
      have b := x.2 (WalkingMulticospan.Hom.snd i)
      rw [← b] at a
      exact a⟩
  invFun x :=
    { val := fun j =>
        match j with
        | WalkingMulticospan.left a => x.1 _
        | WalkingMulticospan.right b => I.fst b (x.1 _)
      property := by
        rintro (a | b) (a' | b') (f | f | f)
        · simp
        · rfl
        · dsimp
          exact (x.2 b').symm
        · simp }
  left_inv := by
    intro x; ext (a | b)
    · rfl
    · rw [← x.2 (WalkingMulticospan.Hom.fst b)]
      rfl
  right_inv := by
    

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : ConcreteCategory C
I : MulticospanIndex C
⊢ Function.RightInverse
    (fun x =>
      {
        val := fun j =>
          match j with
          | WalkingMulticospan.left a => ↑x a
          | WalkingMulticospan.right b => (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)),
        property :=
          (_ :
            ∀ {j j' : WalkingMulticospan I.fstTo I.sndTo} (f : j ⟶ j'),
              (MulticospanIndex.multicospan I ⋙ forget C).map f
                  ((fun j =>
                      match j with
                      | WalkingMulticospan.left a => ↑x a
                      | WalkingMulticospan.right b => (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)))
                    j) =
                (fun j =>
                    match j with
                    | WalkingMulticospan.left a => ↑x a
                    | WalkingMulticospan.right b => (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)))
                  j') })
    fun x =>
    { val := fun i => ↑x (WalkingMulticospan.left i),
      property :=
        (_ :
          ∀ (i : I.R),
            (MulticospanIndex.multicospan I ⋙ forget C).map (WalkingMulticospan.Hom.fst i)
                (↑x (WalkingMulticospan.left (MulticospanIndex.fstTo I i))) =
              (MulticospanIndex.multicospan I ⋙ forget C).map (WalkingMulticospan.Hom.snd i)
                (↑x (WalkingMulticospan.left (MulticospanIndex.sndTo I i)))) }

Next tactic:
intro x
/-- An auxiliary equivalence to be used in `multiequalizerEquiv` below.-/
def multiequalizerEquivAux (I : MulticospanIndex C) :
    (I.multicospan ⋙ forget C).sections ≃
    { x : ∀ i : I.L, I.left i // ∀ i : I.R, I.fst i (x _) = I.snd i (x _) } where
  toFun x :=
    ⟨fun i => x.1 (WalkingMulticospan.left _), fun i => by
      have a := x.2 (WalkingMulticospan.Hom.fst i)
      have b := x.2 (WalkingMulticospan.Hom.snd i)
      rw [← b] at a
      exact a⟩
  invFun x :=
    { val := fun j =>
        match j with
        | WalkingMulticospan.left a => x.1 _
        | WalkingMulticospan.right b => I.fst b (x.1 _)
      property := by
        rintro (a | b) (a' | b') (f | f | f)
        · simp
        · rfl
        · dsimp
          exact (x.2 b').symm
        · simp }
  left_inv := by
    intro x; ext (a | b)
    · rfl
    · rw [← x.2 (WalkingMulticospan.Hom.fst b)]
      rfl
  right_inv := by
    intro x
    

Proof state:
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : ConcreteCategory C
I : MulticospanIndex C
x :
  { x //
    ∀ (i : I.R),
      (MulticospanIndex.fst I i) (x (MulticospanIndex.fstTo I i)) =
        (MulticospanIndex.snd I i) (x (MulticospanIndex.sndTo I i)) }
⊢ (fun x =>
        { val := fun i => ↑x (WalkingMulticospan.left i),
          property :=
            (_ :
              ∀ (i : I.R),
                (MulticospanIndex.multicospan I ⋙ forget C).map (WalkingMulticospan.Hom.fst i)
                    (↑x (WalkingMulticospan.left (MulticospanIndex.fstTo I i))) =
                  (MulticospanIndex.multicospan I ⋙ forget C).map (WalkingMulticospan.Hom.snd i)
                    (↑x (WalkingMulticospan.left (MulticospanIndex.sndTo I i)))) })
      ((fun x =>
          {
            val := fun j =>
              match j with
              | WalkingMulticospan.left a => ↑x a
              | WalkingMulticospan.right b => (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)),
            property :=
              (_ :
                ∀ {j j' : WalkingMulticospan I.fstTo I.sndTo} (f : j ⟶ j'),
                  (MulticospanIndex.multicospan I ⋙ forget C).map f
                      ((fun j =>
                          match j with
                          | WalkingMulticospan.left a => ↑x a
                          | WalkingMulticospan.right b => (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)))
                        j) =
                    (fun j =>
                        match j with
                        | WalkingMulticospan.left a => ↑x a
                        | WalkingMulticospan.right b => (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)))
                      j') })
        x) =
    x

Next tactic:
ext i
/-- An auxiliary equivalence to be used in `multiequalizerEquiv` below.-/
def multiequalizerEquivAux (I : MulticospanIndex C) :
    (I.multicospan ⋙ forget C).sections ≃
    { x : ∀ i : I.L, I.left i // ∀ i : I.R, I.fst i (x _) = I.snd i (x _) } where
  toFun x :=
    ⟨fun i => x.1 (WalkingMulticospan.left _), fun i => by
      have a := x.2 (WalkingMulticospan.Hom.fst i)
      have b := x.2 (WalkingMulticospan.Hom.snd i)
      rw [← b] at a
      exact a⟩
  invFun x :=
    { val := fun j =>
        match j with
        | WalkingMulticospan.left a => x.1 _
        | WalkingMulticospan.right b => I.fst b (x.1 _)
      property := by
        rintro (a | b) (a' | b') (f | f | f)
        · simp
        · rfl
        · dsimp
          exact (x.2 b').symm
        · simp }
  left_inv := by
    intro x; ext (a | b)
    · rfl
    · rw [← x.2 (WalkingMulticospan.Hom.fst b)]
      rfl
  right_inv := by
    intro x
    ext i
    

Proof state:
case a.h
C : Type u
inst✝¹ : Category.{v, u} C
inst✝ : ConcreteCategory C
I : MulticospanIndex C
x :
  { x //
    ∀ (i : I.R),
      (MulticospanIndex.fst I i) (x (MulticospanIndex.fstTo I i)) =
        (MulticospanIndex.snd I i) (x (MulticospanIndex.sndTo I i)) }
i : I.L
⊢ ↑((fun x =>
            { val := fun i => ↑x (WalkingMulticospan.left i),
              property :=
                (_ :
                  ∀ (i : I.R),
                    (MulticospanIndex.multicospan I ⋙ forget C).map (WalkingMulticospan.Hom.fst i)
                        (↑x (WalkingMulticospan.left (MulticospanIndex.fstTo I i))) =
                      (MulticospanIndex.multicospan I ⋙ forget C).map (WalkingMulticospan.Hom.snd i)
                        (↑x (WalkingMulticospan.left (MulticospanIndex.sndTo I i)))) })
          ((fun x =>
              {
                val := fun j =>
                  match j with
                  | WalkingMulticospan.left a => ↑x a
                  | WalkingMulticospan.right b => (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)),
                property :=
                  (_ :
                    ∀ {j j' : WalkingMulticospan I.fstTo I.sndTo} (f : j ⟶ j'),
                      (MulticospanIndex.multicospan I ⋙ forget C).map f
                          ((fun j =>
                              match j with
                              | WalkingMulticospan.left a => ↑x a
                              | WalkingMulticospan.right b =>
                                (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)))
                            j) =
                        (fun j =>
                            match j with
                            | WalkingMulticospan.left a => ↑x a
                            | WalkingMulticospan.right b =>
                              (MulticospanIndex.fst I b) (↑x (MulticospanIndex.fstTo I b)))
                          j') })
            x))
      i =
    ↑x i

Next tactic:
rfl
theorem widePushout_exists_rep {B : C} {α : Type _} {X : α → C} (f : ∀ j : α, B ⟶ X j)
    [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : (∃ y : B, head f y = x) ∨ ∃ (i : α) (y : X i), ι f i y = x := by
  

Proof state:
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : ConcreteCategory C
B : C
α : Type v
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
x : (forget C).obj (widePushout B X f)
⊢ (∃ y, (head f) y = x) ∨ ∃ i y, (ι f i) y = x

Next tactic:
obtain ⟨_ | j, y, rfl⟩ := Concrete.colimit_exists_rep _ x
theorem widePushout_exists_rep {B : C} {α : Type _} {X : α → C} (f : ∀ j : α, B ⟶ X j)
    [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : (∃ y : B, head f y = x) ∨ ∃ (i : α) (y : X i), ι f i y = x := by
  obtain ⟨_ | j, y, rfl⟩ := Concrete.colimit_exists_rep _ x
  · 

Proof state:
case intro.none.intro
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : ConcreteCategory C
B : C
α : Type v
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
y : (forget C).obj ((wideSpan B X f).obj none)
⊢ (∃ y_1, (head f) y_1 = (colimit.ι (wideSpan B X f) none) y) ∨
    ∃ i y_1, (ι f i) y_1 = (colimit.ι (wideSpan B X f) none) y

Next tactic:
left
theorem widePushout_exists_rep {B : C} {α : Type _} {X : α → C} (f : ∀ j : α, B ⟶ X j)
    [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : (∃ y : B, head f y = x) ∨ ∃ (i : α) (y : X i), ι f i y = x := by
  obtain ⟨_ | j, y, rfl⟩ := Concrete.colimit_exists_rep _ x
  · left
    

Proof state:
case intro.none.intro.h
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : ConcreteCategory C
B : C
α : Type v
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
y : (forget C).obj ((wideSpan B X f).obj none)
⊢ ∃ y_1, (head f) y_1 = (colimit.ι (wideSpan B X f) none) y

Next tactic:
use y
theorem widePushout_exists_rep {B : C} {α : Type _} {X : α → C} (f : ∀ j : α, B ⟶ X j)
    [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : (∃ y : B, head f y = x) ∨ ∃ (i : α) (y : X i), ι f i y = x := by
  obtain ⟨_ | j, y, rfl⟩ := Concrete.colimit_exists_rep _ x
  · left
    use y
    

Proof state:
case h
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : ConcreteCategory C
B : C
α : Type v
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
y : (forget C).obj ((wideSpan B X f).obj none)
⊢ (head f) y = (colimit.ι (wideSpan B X f) none) y

Next tactic:
rfl
theorem widePushout_exists_rep {B : C} {α : Type _} {X : α → C} (f : ∀ j : α, B ⟶ X j)
    [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : (∃ y : B, head f y = x) ∨ ∃ (i : α) (y : X i), ι f i y = x := by
  obtain ⟨_ | j, y, rfl⟩ := Concrete.colimit_exists_rep _ x
  · left
    use y
    rfl
  · 

Proof state:
case intro.some.intro
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : ConcreteCategory C
B : C
α : Type v
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
j : α
y : (forget C).obj ((wideSpan B X f).obj (some j))
⊢ (∃ y_1, (head f) y_1 = (colimit.ι (wideSpan B X f) (some j)) y) ∨
    ∃ i y_1, (ι f i) y_1 = (colimit.ι (wideSpan B X f) (some j)) y

Next tactic:
right
theorem widePushout_exists_rep {B : C} {α : Type _} {X : α → C} (f : ∀ j : α, B ⟶ X j)
    [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : (∃ y : B, head f y = x) ∨ ∃ (i : α) (y : X i), ι f i y = x := by
  obtain ⟨_ | j, y, rfl⟩ := Concrete.colimit_exists_rep _ x
  · left
    use y
    rfl
  · right
    

Proof state:
case intro.some.intro.h
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : ConcreteCategory C
B : C
α : Type v
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
j : α
y : (forget C).obj ((wideSpan B X f).obj (some j))
⊢ ∃ i y_1, (ι f i) y_1 = (colimit.ι (wideSpan B X f) (some j)) y

Next tactic:
use j, y
theorem widePushout_exists_rep {B : C} {α : Type _} {X : α → C} (f : ∀ j : α, B ⟶ X j)
    [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : (∃ y : B, head f y = x) ∨ ∃ (i : α) (y : X i), ι f i y = x := by
  obtain ⟨_ | j, y, rfl⟩ := Concrete.colimit_exists_rep _ x
  · left
    use y
    rfl
  · right
    use j, y
    

Proof state:
case h
C : Type u
inst✝³ : Category.{v, u} C
inst✝² : ConcreteCategory C
B : C
α : Type v
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
j : α
y : (forget C).obj ((wideSpan B X f).obj (some j))
⊢ (ι f j) y = (colimit.ι (wideSpan B X f) (some j)) y

Next tactic:
rfl
theorem widePushout_exists_rep' {B : C} {α : Type _} [Nonempty α] {X : α → C}
    (f : ∀ j : α, B ⟶ X j) [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : ∃ (i : α) (y : X i), ι f i y = x := by
  

Proof state:
C : Type u
inst✝⁴ : Category.{v, u} C
inst✝³ : ConcreteCategory C
B : C
α : Type v
inst✝² : Nonempty α
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
x : (forget C).obj (widePushout B X f)
⊢ ∃ i y, (ι f i) y = x

Next tactic:
rcases Concrete.widePushout_exists_rep f x with (⟨y, rfl⟩ | ⟨i, y, rfl⟩)
theorem widePushout_exists_rep' {B : C} {α : Type _} [Nonempty α] {X : α → C}
    (f : ∀ j : α, B ⟶ X j) [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : ∃ (i : α) (y : X i), ι f i y = x := by
  rcases Concrete.widePushout_exists_rep f x with (⟨y, rfl⟩ | ⟨i, y, rfl⟩)
  · 

Proof state:
case inl.intro
C : Type u
inst✝⁴ : Category.{v, u} C
inst✝³ : ConcreteCategory C
B : C
α : Type v
inst✝² : Nonempty α
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
y : (forget C).obj B
⊢ ∃ i y_1, (ι f i) y_1 = (head f) y

Next tactic:
inhabit α
theorem widePushout_exists_rep' {B : C} {α : Type _} [Nonempty α] {X : α → C}
    (f : ∀ j : α, B ⟶ X j) [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : ∃ (i : α) (y : X i), ι f i y = x := by
  rcases Concrete.widePushout_exists_rep f x with (⟨y, rfl⟩ | ⟨i, y, rfl⟩)
  · inhabit α
    

Proof state:
case inl.intro
C : Type u
inst✝⁴ : Category.{v, u} C
inst✝³ : ConcreteCategory C
B : C
α : Type v
inst✝² : Nonempty α
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
y : (forget C).obj B
inhabited_h : Inhabited α
⊢ ∃ i y_1, (ι f i) y_1 = (head f) y

Next tactic:
use default, f _ y
theorem widePushout_exists_rep' {B : C} {α : Type _} [Nonempty α] {X : α → C}
    (f : ∀ j : α, B ⟶ X j) [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : ∃ (i : α) (y : X i), ι f i y = x := by
  rcases Concrete.widePushout_exists_rep f x with (⟨y, rfl⟩ | ⟨i, y, rfl⟩)
  · inhabit α
    use default, f _ y
    

Proof state:
case h
C : Type u
inst✝⁴ : Category.{v, u} C
inst✝³ : ConcreteCategory C
B : C
α : Type v
inst✝² : Nonempty α
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
y : (forget C).obj B
inhabited_h : Inhabited α
⊢ (ι f default) ((f default) y) = (head f) y

Next tactic:
simp only [← arrow_ι _ default, comp_apply]
theorem widePushout_exists_rep' {B : C} {α : Type _} [Nonempty α] {X : α → C}
    (f : ∀ j : α, B ⟶ X j) [HasWidePushout.{v} B X f] [PreservesColimit (wideSpan B X f) (forget C)]
    (x : ↑(widePushout B X f)) : ∃ (i : α) (y : X i), ι f i y = x := by
  rcases Concrete.widePushout_exists_rep f x with (⟨y, rfl⟩ | ⟨i, y, rfl⟩)
  · inhabit α
    use default, f _ y
    simp only [← arrow_ι _ default, comp_apply]
  · 

Proof state:
case inr.intro.intro
C : Type u
inst✝⁴ : Category.{v, u} C
inst✝³ : ConcreteCategory C
B : C
α : Type v
inst✝² : Nonempty α
X : α → C
f : (j : α) → B ⟶ X j
inst✝¹ : HasWidePushout B X f
inst✝ : PreservesColimit (wideSpan B X f) (forget C)
i : α
y : (forget C).obj (X i)
⊢ ∃ i_1 y_1, (ι f i_1) y_1 = (ι f i) y

Next tactic:
use i, y
theorem cokernel_funext {C : Type*} [Category C] [HasZeroMorphisms C] [ConcreteCategory C]
    {M N K : C} {f : M ⟶ N} [HasCokernel f] {g h : cokernel f ⟶ K}
    (w : ∀ n : N, g (cokernel.π f n) = h (cokernel.π f n)) : g = h := by
  

Proof state:
C✝ : Type u
inst✝⁴ : Category.{v, u} C✝
C : Type u_1
inst✝³ : Category.{u_2, u_1} C
inst✝² : HasZeroMorphisms C
inst✝¹ : ConcreteCategory C
M N K : C
f : M ⟶ N
inst✝ : HasCokernel f
g h : cokernel f ⟶ K
w : ∀ (n : (forget C).obj N), g ((cokernel.π f) n) = h ((cokernel.π f) n)
⊢ g = h

Next tactic:
ext x
theorem cokernel_funext {C : Type*} [Category C] [HasZeroMorphisms C] [ConcreteCategory C]
    {M N K : C} {f : M ⟶ N} [HasCokernel f] {g h : cokernel f ⟶ K}
    (w : ∀ n : N, g (cokernel.π f n) = h (cokernel.π f n)) : g = h := by
  ext x
  

Proof state:
case h.w
C✝ : Type u
inst✝⁴ : Category.{v, u} C✝
C : Type u_1
inst✝³ : Category.{u_2, u_1} C
inst✝² : HasZeroMorphisms C
inst✝¹ : ConcreteCategory C
M N K : C
f : M ⟶ N
inst✝ : HasCokernel f
g h : cokernel f ⟶ K
w : ∀ (n : (forget C).obj N), g ((cokernel.π f) n) = h ((cokernel.π f) n)
x : (forget C).obj N
⊢ (coequalizer.π f 0 ≫ g) x = (coequalizer.π f 0 ≫ h) x

Next tactic:
simpa using w x
/-- If `f` is a conservative map and `s` is a measurable set of nonzero measure, then
for infinitely many values of `m` a positive measure of points `x ∈ s` returns back to `s`
after `m` iterations of `f`. -/
theorem frequently_measure_inter_ne_zero (hf : Conservative f μ) (hs : MeasurableSet s)
    (h0 : μ s ≠ 0) : ∃ᶠ m in atTop, μ (s ∩ f^[m] ⁻¹' s) ≠ 0 := by
  

Proof state:
ι : Type u_1
α : Type u_2
inst✝ : MeasurableSpace α
f : α → α
s : Set α
μ : Measure α
hf : Conservative f μ
hs : MeasurableSet s
h0 : ↑↑μ s ≠ 0
⊢ ∃ᶠ (m : ℕ) in atTop, ↑↑μ (s ∩ f^[m] ⁻¹' s) ≠ 0

Next tactic:
by_contra H
/-- If `f` is a conservative map and `s` is a measurable set of nonzero measure, then
for infinitely many values of `m` a positive measure of points `x ∈ s` returns back to `s`
after `m` iterations of `f`. -/
theorem frequently_measure_inter_ne_zero (hf : Conservative f μ) (hs : MeasurableSet s)
    (h0 : μ s ≠ 0) : ∃ᶠ m in atTop, μ (s ∩ f^[m] ⁻¹' s) ≠ 0 := by
  by_contra H
  

Proof state:
ι : Type u_1
α : Type u_2
inst✝ : MeasurableSpace α
f : α → α
s : Set α
μ : Measure α
hf : Conservative f μ
hs : MeasurableSet s
h0 : ↑↑μ s ≠ 0
H : ¬∃ᶠ (m : ℕ) in atTop, ↑↑μ (s ∩ f^[m] ⁻¹' s) ≠ 0
⊢ False

Next tactic:
simp only [not_frequently, eventually_atTop, Ne.def, Classical.not_not] at H
/-- If `f` is a conservative map and `s` is a measurable set of nonzero measure, then
for infinitely many values of `m` a positive measure of points `x ∈ s` returns back to `s`
after `m` iterations of `f`. -/
theorem frequently_measure_inter_ne_zero (hf : Conservative f μ) (hs : MeasurableSet s)
    (h0 : μ s ≠ 0) : ∃ᶠ m in atTop, μ (s ∩ f^[m] ⁻¹' s) ≠ 0 := by
  by_contra H
  simp only [not_frequently, eventually_atTop, Ne.def, Classical.not_not] at H
  

Proof state:
ι : Type u_1
α : Type u_2
inst✝ : MeasurableSpace α
f : α → α
s : Set α
μ : Measure α
hf : Conservative f μ
hs : MeasurableSet s
h0 : ↑↑μ s ≠ 0
H : ∃ a, ∀ b ≥ a, ↑↑μ (s ∩ f^[b] ⁻¹' s) = 0
⊢ False

Next tactic:
rcases H with ⟨N, hN⟩
/-- If `f` is a conservative map and `s` is a measurable set of nonzero measure, then
for infinitely many values of `m` a positive measure of points `x ∈ s` returns back to `s`
after `m` iterations of `f`. -/
theorem frequently_measure_inter_ne_zero (hf : Conservative f μ) (hs : MeasurableSet s)
    (h0 : μ s ≠ 0) : ∃ᶠ m in atTop, μ (s ∩ f^[m] ⁻¹' s) ≠ 0 := by
  by_contra H
  simp only [not_frequently, eventually_atTop, Ne.def, Classical.not_not] at H
  rcases H with ⟨N, hN⟩
  

Proof state:
case intro
ι : Type u_1
α : Type u_2
inst✝ : MeasurableSpace α
f : α → α
s : Set α
μ : Measure α
hf : Conservative f μ
hs : MeasurableSet s
h0 : ↑↑μ s ≠ 0
N : ℕ
hN : ∀ b ≥ N, ↑↑μ (s ∩ f^[b] ⁻¹' s) = 0
⊢ False

Next tactic:
induction' N with N ihN
/-- If `f` is a conservative map and `s` is a measurable set of nonzero measure, then
for infinitely many values of `m` a positive measure of points `x ∈ s` returns back to `s`
after `m` iterations of `f`. -/
theorem frequently_measure_inter_ne_zero (hf : Conservative f μ) (hs : MeasurableSet s)
    (h0 : μ s ≠ 0) : ∃ᶠ m in atTop, μ (s ∩ f^[m] ⁻¹' s) ≠ 0 := by
  by_contra H
  simp only [not_frequently, eventually_atTop, Ne.def, Classical.not_not] at H
  rcases H with ⟨N, hN⟩
  induction' N with N ihN
  · 

Proof state:
case intro.zero
ι : Type u_1
α : Type u_2
inst✝ : MeasurableSpace α
f : α → α
s : Set α
μ : Measure α
hf : Conservative f μ
hs : MeasurableSet s
h0 : ↑↑μ s ≠ 0
hN : ∀ b ≥ Nat.zero, ↑↑μ (s ∩ f^[b] ⁻¹' s) = 0
⊢ False

Next tactic:
apply h0