/-
Copyright (c) 2018 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Markus Himmel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.epi_mono
import Mathlib.category_theory.limits.limits
import Mathlib.PostPort

universes v l u_1 u u₂

namespace Mathlib

/-!
# Equalizers and coequalizers

This file defines (co)equalizers as special cases of (co)limits.

An equalizer is the categorical generalization of the subobject {a ∈ A | f(a) = g(a)} known
from abelian groups or modules. It is a limit cone over the diagram formed by `f` and `g`.

A coequalizer is the dual concept.

## Main definitions

* `walking_parallel_pair` is the indexing category used for (co)equalizer_diagrams
* `parallel_pair` is a functor from `walking_parallel_pair` to our category `C`.
* a `fork` is a cone over a parallel pair.
  * there is really only one interesting morphism in a fork: the arrow from the vertex of the fork
    to the domain of f and g. It is called `fork.ι`.
* an `equalizer` is now just a `limit (parallel_pair f g)`

Each of these has a dual.

## Main statements

* `equalizer.ι_mono` states that every equalizer map is a monomorphism
* `is_iso_limit_cone_parallel_pair_of_self` states that the identity on the domain of `f` is an
  equalizer of `f` and `f`.

## Implementation notes
As with the other special shapes in the limits library, all the definitions here are given as
`abbreviation`s of the general statements for limits, so all the `simp` lemmas and theorems about
general limits can be used.

## References

* [F. Borceux, *Handbook of Categorical Algebra 1*][borceux-vol1]
-/

namespace category_theory.limits

/-- The type of objects for the diagram indexing a (co)equalizer. -/
inductive walking_parallel_pair
where
| zero : walking_parallel_pair
| one : walking_parallel_pair

/-- The type family of morphisms for the diagram indexing a (co)equalizer. -/
inductive walking_parallel_pair_hom : walking_parallel_pair → walking_parallel_pair → Type v
where
| left : walking_parallel_pair_hom walking_parallel_pair.zero walking_parallel_pair.one
| right : walking_parallel_pair_hom walking_parallel_pair.zero walking_parallel_pair.one
| id : (X : walking_parallel_pair) → walking_parallel_pair_hom X X

/-- Satisfying the inhabited linter -/
protected instance walking_parallel_pair_hom.inhabited : Inhabited (walking_parallel_pair_hom walking_parallel_pair.zero walking_parallel_pair.one) :=
  { default := walking_parallel_pair_hom.left }

/-- Composition of morphisms in the indexing diagram for (co)equalizers. -/
def walking_parallel_pair_hom.comp (X : walking_parallel_pair) (Y : walking_parallel_pair) (Z : walking_parallel_pair) (f : walking_parallel_pair_hom X Y) (g : walking_parallel_pair_hom Y Z) : walking_parallel_pair_hom X Z :=
  sorry

protected instance walking_parallel_pair_hom_category : small_category walking_parallel_pair :=
  category.mk

@[simp] theorem walking_parallel_pair_hom_id (X : walking_parallel_pair) : walking_parallel_pair_hom.id X = 𝟙 :=
  rfl

/-- `parallel_pair f g` is the diagram in `C` consisting of the two morphisms `f` and `g` with
    common domain and codomain. -/
def parallel_pair {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) : walking_parallel_pair ⥤ C :=
  functor.mk (fun (x : walking_parallel_pair) => sorry) fun (x y : walking_parallel_pair) (h : x ⟶ y) => sorry

@[simp] theorem parallel_pair_obj_zero {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) : functor.obj (parallel_pair f g) walking_parallel_pair.zero = X :=
  rfl

@[simp] theorem parallel_pair_obj_one {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) : functor.obj (parallel_pair f g) walking_parallel_pair.one = Y :=
  rfl

@[simp] theorem parallel_pair_map_left {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) : functor.map (parallel_pair f g) walking_parallel_pair_hom.left = f :=
  rfl

@[simp] theorem parallel_pair_map_right {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) : functor.map (parallel_pair f g) walking_parallel_pair_hom.right = g :=
  rfl

@[simp] theorem parallel_pair_functor_obj {C : Type u} [category C] {F : walking_parallel_pair ⥤ C} (j : walking_parallel_pair) : functor.obj
    (parallel_pair (functor.map F walking_parallel_pair_hom.left) (functor.map F walking_parallel_pair_hom.right)) j =
  functor.obj F j := sorry

/-- Every functor indexing a (co)equalizer is naturally isomorphic (actually, equal) to a
    `parallel_pair` -/
def diagram_iso_parallel_pair {C : Type u} [category C] (F : walking_parallel_pair ⥤ C) : F ≅ parallel_pair (functor.map F walking_parallel_pair_hom.left) (functor.map F walking_parallel_pair_hom.right) :=
  nat_iso.of_components (fun (j : walking_parallel_pair) => eq_to_iso sorry) sorry

/-- A fork on `f` and `g` is just a `cone (parallel_pair f g)`. -/
def fork {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) :=
  cone (parallel_pair f g)

/-- A cofork on `f` and `g` is just a `cocone (parallel_pair f g)`. -/
def cofork {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) :=
  cocone (parallel_pair f g)

/-- A fork `t` on the parallel pair `f g : X ⟶ Y` consists of two morphisms `t.π.app zero : t.X ⟶ X`
    and `t.π.app one : t.X ⟶ Y`. Of these, only the first one is interesting, and we give it the
    shorter name `fork.ι t`. -/
def fork.ι {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : fork f g) : functor.obj (functor.obj (functor.const walking_parallel_pair) (cone.X t)) walking_parallel_pair.zero ⟶
  functor.obj (parallel_pair f g) walking_parallel_pair.zero :=
  nat_trans.app (cone.π t) walking_parallel_pair.zero

/-- A cofork `t` on the parallel_pair `f g : X ⟶ Y` consists of two morphisms
    `t.ι.app zero : X ⟶ t.X` and `t.ι.app one : Y ⟶ t.X`. Of these, only the second one is
    interesting, and we give it the shorter name `cofork.π t`. -/
def cofork.π {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : cofork f g) : functor.obj (parallel_pair f g) walking_parallel_pair.one ⟶
  functor.obj (functor.obj (functor.const walking_parallel_pair) (cocone.X t)) walking_parallel_pair.one :=
  nat_trans.app (cocone.ι t) walking_parallel_pair.one

@[simp] theorem fork.ι_eq_app_zero {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : fork f g) : fork.ι t = nat_trans.app (cone.π t) walking_parallel_pair.zero :=
  rfl

@[simp] theorem cofork.π_eq_app_one {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : cofork f g) : cofork.π t = nat_trans.app (cocone.ι t) walking_parallel_pair.one :=
  rfl

@[simp] theorem fork.app_zero_left {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (s : fork f g) : nat_trans.app (cone.π s) walking_parallel_pair.zero ≫ f = nat_trans.app (cone.π s) walking_parallel_pair.one := sorry

@[simp] theorem fork.app_zero_right {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (s : fork f g) : nat_trans.app (cone.π s) walking_parallel_pair.zero ≫ g = nat_trans.app (cone.π s) walking_parallel_pair.one := sorry

@[simp] theorem cofork.left_app_one_assoc {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (s : cofork f g) {X' : C} (f' : functor.obj (functor.obj (functor.const walking_parallel_pair) (cocone.X s)) walking_parallel_pair.one ⟶ X') : f ≫ nat_trans.app (cocone.ι s) walking_parallel_pair.one ≫ f' =
  nat_trans.app (cocone.ι s) walking_parallel_pair.zero ≫ f' := sorry

@[simp] theorem cofork.right_app_one_assoc {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (s : cofork f g) {X' : C} (f' : functor.obj (functor.obj (functor.const walking_parallel_pair) (cocone.X s)) walking_parallel_pair.one ⟶ X') : g ≫ nat_trans.app (cocone.ι s) walking_parallel_pair.one ≫ f' =
  nat_trans.app (cocone.ι s) walking_parallel_pair.zero ≫ f' := sorry

/-- A fork on `f g : X ⟶ Y` is determined by the morphism `ι : P ⟶ X` satisfying `ι ≫ f = ι ≫ g`.
-/
@[simp] theorem fork.of_ι_X {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {P : C} (ι : P ⟶ X) (w : ι ≫ f = ι ≫ g) : cone.X (fork.of_ι ι w) = P :=
  Eq.refl (cone.X (fork.of_ι ι w))

/-- A cofork on `f g : X ⟶ Y` is determined by the morphism `π : Y ⟶ P` satisfying
    `f ≫ π = g ≫ π`. -/
@[simp] theorem cofork.of_π_ι_app {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {P : C} (π : Y ⟶ P) (w : f ≫ π = g ≫ π) : ∀ (X_1 : walking_parallel_pair),
  nat_trans.app (cocone.ι (cofork.of_π π w)) X_1 = walking_parallel_pair.cases_on X_1 (f ≫ π) π :=
  fun (X_1 : walking_parallel_pair) => Eq.refl (nat_trans.app (cocone.ι (cofork.of_π π w)) X_1)

theorem fork.ι_of_ι {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {P : C} (ι : P ⟶ X) (w : ι ≫ f = ι ≫ g) : fork.ι (fork.of_ι ι w) = ι :=
  rfl

theorem cofork.π_of_π {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {P : C} (π : Y ⟶ P) (w : f ≫ π = g ≫ π) : cofork.π (cofork.of_π π w) = π :=
  rfl

theorem fork.condition {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : fork f g) : fork.ι t ≫ f = fork.ι t ≫ g := sorry

theorem cofork.condition {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : cofork f g) : f ≫ cofork.π t = g ≫ cofork.π t := sorry

/-- To check whether two maps are equalized by both maps of a fork, it suffices to check it for the
    first map -/
theorem fork.equalizer_ext {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (s : fork f g) {W : C} {k : W ⟶ cone.X s} {l : W ⟶ cone.X s} (h : k ≫ fork.ι s = l ≫ fork.ι s) (j : walking_parallel_pair) : k ≫ nat_trans.app (cone.π s) j = l ≫ nat_trans.app (cone.π s) j := sorry

/-- To check whether two maps are coequalized by both maps of a cofork, it suffices to check it for
    the second map -/
theorem cofork.coequalizer_ext {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (s : cofork f g) {W : C} {k : cocone.X s ⟶ W} {l : cocone.X s ⟶ W} (h : cofork.π s ≫ k = cofork.π s ≫ l) (j : walking_parallel_pair) : nat_trans.app (cocone.ι s) j ≫ k = nat_trans.app (cocone.ι s) j ≫ l := sorry

theorem fork.is_limit.hom_ext {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {s : fork f g} (hs : is_limit s) {W : C} {k : W ⟶ cone.X s} {l : W ⟶ cone.X s} (h : k ≫ fork.ι s = l ≫ fork.ι s) : k = l :=
  is_limit.hom_ext hs (fork.equalizer_ext s h)

theorem cofork.is_colimit.hom_ext {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {s : cofork f g} (hs : is_colimit s) {W : C} {k : cocone.X s ⟶ W} {l : cocone.X s ⟶ W} (h : cofork.π s ≫ k = cofork.π s ≫ l) : k = l :=
  is_colimit.hom_ext hs (cofork.coequalizer_ext s h)

/-- If `s` is a limit fork over `f` and `g`, then a morphism `k : W ⟶ X` satisfying
    `k ≫ f = k ≫ g` induces a morphism `l : W ⟶ s.X` such that `l ≫ fork.ι s = k`. -/
def fork.is_limit.lift' {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {s : fork f g} (hs : is_limit s) {W : C} (k : W ⟶ X) (h : k ≫ f = k ≫ g) : Subtype fun (l : W ⟶ cone.X s) => l ≫ fork.ι s = k :=
  { val := is_limit.lift hs (fork.of_ι k h), property := sorry }

/-- If `s` is a colimit cofork over `f` and `g`, then a morphism `k : Y ⟶ W` satisfying
    `f ≫ k = g ≫ k` induces a morphism `l : s.X ⟶ W` such that `cofork.π s ≫ l = k`. -/
def cofork.is_colimit.desc' {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {s : cofork f g} (hs : is_colimit s) {W : C} (k : Y ⟶ W) (h : f ≫ k = g ≫ k) : Subtype fun (l : cocone.X s ⟶ W) => cofork.π s ≫ l = k :=
  { val := is_colimit.desc hs (cofork.of_π k h), property := sorry }

/-- This is a slightly more convenient method to verify that a fork is a limit cone. It
    only asks for a proof of facts that carry any mathematical content -/
def fork.is_limit.mk {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : fork f g) (lift : (s : fork f g) → cone.X s ⟶ cone.X t) (fac : ∀ (s : fork f g), lift s ≫ fork.ι t = fork.ι s) (uniq : ∀ (s : fork f g) (m : cone.X s ⟶ cone.X t),
  (∀ (j : walking_parallel_pair), m ≫ nat_trans.app (cone.π t) j = nat_trans.app (cone.π s) j) → m = lift s) : is_limit t :=
  is_limit.mk lift

/-- This is another convenient method to verify that a fork is a limit cone. It
    only asks for a proof of facts that carry any mathematical content, and allows access to the
    same `s` for all parts. -/
def fork.is_limit.mk' {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : fork f g) (create : (s : fork f g) →
  Subtype
    fun
      (l :
      functor.obj (functor.obj (functor.const walking_parallel_pair) (cone.X s)) walking_parallel_pair.zero ⟶
        functor.obj (functor.obj (functor.const walking_parallel_pair) (cone.X t)) walking_parallel_pair.zero) =>
      l ≫ fork.ι t = fork.ι s ∧
        ∀
          {m :
            functor.obj (functor.obj (functor.const walking_parallel_pair) (cone.X s)) walking_parallel_pair.zero ⟶
              functor.obj (functor.obj (functor.const walking_parallel_pair) (cone.X t)) walking_parallel_pair.zero},
          m ≫ fork.ι t = fork.ι s → m = l) : is_limit t :=
  fork.is_limit.mk t (fun (s : fork f g) => subtype.val (create s)) sorry sorry

/-- This is a slightly more convenient method to verify that a cofork is a colimit cocone. It
    only asks for a proof of facts that carry any mathematical content -/
def cofork.is_colimit.mk {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : cofork f g) (desc : (s : cofork f g) → cocone.X t ⟶ cocone.X s) (fac : ∀ (s : cofork f g), cofork.π t ≫ desc s = cofork.π s) (uniq : ∀ (s : cofork f g) (m : cocone.X t ⟶ cocone.X s),
  (∀ (j : walking_parallel_pair), nat_trans.app (cocone.ι t) j ≫ m = nat_trans.app (cocone.ι s) j) → m = desc s) : is_colimit t :=
  is_colimit.mk desc

/-- This is another convenient method to verify that a fork is a limit cone. It
    only asks for a proof of facts that carry any mathematical content, and allows access to the
    same `s` for all parts. -/
def cofork.is_colimit.mk' {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (t : cofork f g) (create : (s : cofork f g) →
  Subtype
    fun (l : cocone.X t ⟶ cocone.X s) =>
      cofork.π t ≫ l = cofork.π s ∧
        ∀
          {m :
            functor.obj (functor.obj (functor.const walking_parallel_pair) (cocone.X t)) walking_parallel_pair.one ⟶
              functor.obj (functor.obj (functor.const walking_parallel_pair) (cocone.X s)) walking_parallel_pair.one},
          cofork.π t ≫ m = cofork.π s → m = l) : is_colimit t :=
  cofork.is_colimit.mk t (fun (s : cofork f g) => subtype.val (create s)) sorry sorry

/--
Given a limit cone for the pair `f g : X ⟶ Y`, for any `Z`, morphisms from `Z` to its point are in
bijection with morphisms `h : Z ⟶ X` such that `h ≫ f = h ≫ g`.
Further, this bijection is natural in `Z`: see `fork.is_limit.hom_iso_natural`.
This is a special case of `is_limit.hom_iso'`, often useful to construct adjunctions.
-/
def fork.is_limit.hom_iso {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {t : fork f g} (ht : is_limit t) (Z : C) : (Z ⟶ cone.X t) ≃ Subtype fun (h : Z ⟶ X) => h ≫ f = h ≫ g :=
  equiv.mk (fun (k : Z ⟶ cone.X t) => { val := k ≫ fork.ι t, property := sorry })
    (fun (h : Subtype fun (h : Z ⟶ X) => h ≫ f = h ≫ g) => subtype.val (fork.is_limit.lift' ht ↑h sorry)) sorry sorry

/-- The bijection of `fork.is_limit.hom_iso` is natural in `Z`. -/
theorem fork.is_limit.hom_iso_natural {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {t : fork f g} (ht : is_limit t) {Z : C} {Z' : C} (q : Z' ⟶ Z) (k : Z ⟶ cone.X t) : ↑(coe_fn (fork.is_limit.hom_iso ht Z') (q ≫ k)) = q ≫ ↑(coe_fn (fork.is_limit.hom_iso ht Z) k) :=
  category.assoc q k (fork.ι t)

/--
Given a colimit cocone for the pair `f g : X ⟶ Y`, for any `Z`, morphisms from the cocone point
to `Z` are in bijection with morphisms `h : Y ⟶ Z` such that `f ≫ h = g ≫ h`.
Further, this bijection is natural in `Z`: see `cofork.is_colimit.hom_iso_natural`.
This is a special case of `is_colimit.hom_iso'`, often useful to construct adjunctions.
-/
def cofork.is_colimit.hom_iso {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {t : cofork f g} (ht : is_colimit t) (Z : C) : (cocone.X t ⟶ Z) ≃ Subtype fun (h : Y ⟶ Z) => f ≫ h = g ≫ h :=
  equiv.mk (fun (k : cocone.X t ⟶ Z) => { val := cofork.π t ≫ k, property := sorry })
    (fun (h : Subtype fun (h : Y ⟶ Z) => f ≫ h = g ≫ h) => subtype.val (cofork.is_colimit.desc' ht ↑h sorry)) sorry sorry

/-- The bijection of `cofork.is_colimit.hom_iso` is natural in `Z`. -/
theorem cofork.is_colimit.hom_iso_natural {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {t : cofork f g} {Z : C} {Z' : C} (q : Z ⟶ Z') (ht : is_colimit t) (k : cocone.X t ⟶ Z) : ↑(coe_fn (cofork.is_colimit.hom_iso ht Z') (k ≫ q)) = ↑(coe_fn (cofork.is_colimit.hom_iso ht Z) k) ≫ q :=
  Eq.symm (category.assoc (cofork.π t) k q)

/-- This is a helper construction that can be useful when verifying that a category has all
    equalizers. Given `F : walking_parallel_pair ⥤ C`, which is really the same as
    `parallel_pair (F.map left) (F.map right)`, and a fork on `F.map left` and `F.map right`,
    we get a cone on `F`.

    If you're thinking about using this, have a look at `has_equalizers_of_has_limit_parallel_pair`,
    which you may find to be an easier way of achieving your goal. -/
def cone.of_fork {C : Type u} [category C] {F : walking_parallel_pair ⥤ C} (t : fork (functor.map F walking_parallel_pair_hom.left) (functor.map F walking_parallel_pair_hom.right)) : cone F :=
  cone.mk (cone.X t) (nat_trans.mk fun (X : walking_parallel_pair) => nat_trans.app (cone.π t) X ≫ eq_to_hom sorry)

/-- This is a helper construction that can be useful when verifying that a category has all
    coequalizers. Given `F : walking_parallel_pair ⥤ C`, which is really the same as
    `parallel_pair (F.map left) (F.map right)`, and a cofork on `F.map left` and `F.map right`,
    we get a cocone on `F`.

    If you're thinking about using this, have a look at
    `has_coequalizers_of_has_colimit_parallel_pair`, which you may find to be an easier way of
    achieving your goal. -/
def cocone.of_cofork {C : Type u} [category C] {F : walking_parallel_pair ⥤ C} (t : cofork (functor.map F walking_parallel_pair_hom.left) (functor.map F walking_parallel_pair_hom.right)) : cocone F :=
  cocone.mk (cocone.X t) (nat_trans.mk fun (X : walking_parallel_pair) => eq_to_hom sorry ≫ nat_trans.app (cocone.ι t) X)

@[simp] theorem cone.of_fork_π {C : Type u} [category C] {F : walking_parallel_pair ⥤ C} (t : fork (functor.map F walking_parallel_pair_hom.left) (functor.map F walking_parallel_pair_hom.right)) (j : walking_parallel_pair) : nat_trans.app (cone.π (cone.of_fork t)) j =
  nat_trans.app (cone.π t) j ≫
    eq_to_hom
      (eq.mpr
        (id
          (Eq.trans
            ((fun (a a_1 : C) (e_1 : a = a_1) (ᾰ ᾰ_1 : C) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2)
              (functor.obj
                (parallel_pair (functor.map F walking_parallel_pair_hom.left)
                  (functor.map F walking_parallel_pair_hom.right))
                j)
              (functor.obj F j) (parallel_pair_functor_obj j) (functor.obj F j) (functor.obj F j)
              (Eq.refl (functor.obj F j)))
            (propext (eq_self_iff_true (functor.obj F j)))))
        trivial) :=
  rfl

@[simp] theorem cocone.of_cofork_ι {C : Type u} [category C] {F : walking_parallel_pair ⥤ C} (t : cofork (functor.map F walking_parallel_pair_hom.left) (functor.map F walking_parallel_pair_hom.right)) (j : walking_parallel_pair) : nat_trans.app (cocone.ι (cocone.of_cofork t)) j =
  eq_to_hom
      (eq.mpr
        (id
          (Eq.trans
            ((fun (a a_1 : C) (e_1 : a = a_1) (ᾰ ᾰ_1 : C) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2)
              (functor.obj F j) (functor.obj F j) (Eq.refl (functor.obj F j))
              (functor.obj
                (parallel_pair (functor.map F walking_parallel_pair_hom.left)
                  (functor.map F walking_parallel_pair_hom.right))
                j)
              (functor.obj F j) (parallel_pair_functor_obj j))
            (propext (eq_self_iff_true (functor.obj F j)))))
        trivial) ≫
    nat_trans.app (cocone.ι t) j :=
  rfl

/-- Given `F : walking_parallel_pair ⥤ C`, which is really the same as
    `parallel_pair (F.map left) (F.map right)` and a cone on `F`, we get a fork on
    `F.map left` and `F.map right`. -/
def fork.of_cone {C : Type u} [category C] {F : walking_parallel_pair ⥤ C} (t : cone F) : fork (functor.map F walking_parallel_pair_hom.left) (functor.map F walking_parallel_pair_hom.right) :=
  cone.mk (cone.X t) (nat_trans.mk fun (X : walking_parallel_pair) => nat_trans.app (cone.π t) X ≫ eq_to_hom sorry)

/-- Given `F : walking_parallel_pair ⥤ C`, which is really the same as
    `parallel_pair (F.map left) (F.map right)` and a cocone on `F`, we get a cofork on
    `F.map left` and `F.map right`. -/
def cofork.of_cocone {C : Type u} [category C] {F : walking_parallel_pair ⥤ C} (t : cocone F) : cofork (functor.map F walking_parallel_pair_hom.left) (functor.map F walking_parallel_pair_hom.right) :=
  cocone.mk (cocone.X t) (nat_trans.mk fun (X : walking_parallel_pair) => eq_to_hom sorry ≫ nat_trans.app (cocone.ι t) X)

@[simp] theorem fork.of_cone_π {C : Type u} [category C] {F : walking_parallel_pair ⥤ C} (t : cone F) (j : walking_parallel_pair) : nat_trans.app (cone.π (fork.of_cone t)) j =
  nat_trans.app (cone.π t) j ≫
    eq_to_hom
      (eq.mpr
        (id
          (Eq.trans
            ((fun (a a_1 : C) (e_1 : a = a_1) (ᾰ ᾰ_1 : C) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2)
              (functor.obj F j) (functor.obj F j) (Eq.refl (functor.obj F j))
              (functor.obj
                (parallel_pair (functor.map F walking_parallel_pair_hom.left)
                  (functor.map F walking_parallel_pair_hom.right))
                j)
              (functor.obj F j) (parallel_pair_functor_obj j))
            (propext (eq_self_iff_true (functor.obj F j)))))
        trivial) :=
  rfl

@[simp] theorem cofork.of_cocone_ι {C : Type u} [category C] {F : walking_parallel_pair ⥤ C} (t : cocone F) (j : walking_parallel_pair) : nat_trans.app (cocone.ι (cofork.of_cocone t)) j =
  eq_to_hom
      (eq.mpr
        (id
          (Eq.trans
            ((fun (a a_1 : C) (e_1 : a = a_1) (ᾰ ᾰ_1 : C) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2)
              (functor.obj
                (parallel_pair (functor.map F walking_parallel_pair_hom.left)
                  (functor.map F walking_parallel_pair_hom.right))
                j)
              (functor.obj F j) (parallel_pair_functor_obj j) (functor.obj F j) (functor.obj F j)
              (Eq.refl (functor.obj F j)))
            (propext (eq_self_iff_true (functor.obj F j)))))
        trivial) ≫
    nat_trans.app (cocone.ι t) j :=
  rfl

/--
Helper function for constructing morphisms between equalizer forks.
-/
@[simp] theorem fork.mk_hom_hom {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {s : fork f g} {t : fork f g} (k : cone.X s ⟶ cone.X t) (w : k ≫ fork.ι t = fork.ι s) : cone_morphism.hom (fork.mk_hom k w) = k :=
  Eq.refl (cone_morphism.hom (fork.mk_hom k w))

/--
To construct an isomorphism between forks,
it suffices to give an isomorphism between the cone points
and check that it commutes with the `ι` morphisms.
-/
@[simp] theorem fork.ext_hom {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {s : fork f g} {t : fork f g} (i : cone.X s ≅ cone.X t) (w : iso.hom i ≫ fork.ι t = fork.ι s) : iso.hom (fork.ext i w) = fork.mk_hom (iso.hom i) w :=
  Eq.refl (iso.hom (fork.ext i w))

/--
Helper function for constructing morphisms between coequalizer coforks.
-/
@[simp] theorem cofork.mk_hom_hom {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {s : cofork f g} {t : cofork f g} (k : cocone.X s ⟶ cocone.X t) (w : cofork.π s ≫ k = cofork.π t) : cocone_morphism.hom (cofork.mk_hom k w) = k :=
  Eq.refl (cocone_morphism.hom (cofork.mk_hom k w))

/--
To construct an isomorphism between coforks,
it suffices to give an isomorphism between the cocone points
and check that it commutes with the `π` morphisms.
-/
def cofork.ext {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {s : cofork f g} {t : cofork f g} (i : cocone.X s ≅ cocone.X t) (w : cofork.π s ≫ iso.hom i = cofork.π t) : s ≅ t :=
  iso.mk (cofork.mk_hom (iso.hom i) w) (cofork.mk_hom (iso.inv i) sorry)

/--
`has_equalizer f g` represents a particular choice of limiting cone
for the parallel pair of morphisms `f` and `g`.
-/
def has_equalizer {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) :=
  has_limit (parallel_pair f g)

/-- If an equalizer of `f` and `g` exists, we can access an arbitrary choice of such by
    saying `equalizer f g`. -/
def equalizer {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_equalizer f g] : C :=
  limit (parallel_pair f g)

/-- If an equalizer of `f` and `g` exists, we can access the inclusion
    `equalizer f g ⟶ X` by saying `equalizer.ι f g`. -/
def equalizer.ι {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_equalizer f g] : equalizer f g ⟶ X :=
  limit.π (parallel_pair f g) walking_parallel_pair.zero

/--
An equalizer cone for a parallel pair `f` and `g`.
-/
def equalizer.fork {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_equalizer f g] : fork f g :=
  limit.cone (parallel_pair f g)

@[simp] theorem equalizer.fork_ι {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_equalizer f g] : fork.ι (equalizer.fork f g) = equalizer.ι f g :=
  rfl

@[simp] theorem equalizer.fork_π_app_zero {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_equalizer f g] : nat_trans.app (cone.π (equalizer.fork f g)) walking_parallel_pair.zero = equalizer.ι f g :=
  rfl

theorem equalizer.condition {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_equalizer f g] : equalizer.ι f g ≫ f = equalizer.ι f g ≫ g :=
  fork.condition (limit.cone (parallel_pair f g))

/-- The equalizer built from `equalizer.ι f g` is limiting. -/
def equalizer_is_equalizer {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_equalizer f g] : is_limit (fork.of_ι (equalizer.ι f g) (equalizer.condition f g)) :=
  is_limit.of_iso_limit (limit.is_limit (parallel_pair f g))
    (fork.ext (iso.refl (cone.X (limit.cone (parallel_pair f g)))) sorry)

/-- A morphism `k : W ⟶ X` satisfying `k ≫ f = k ≫ g` factors through the equalizer of `f` and `g`
    via `equalizer.lift : W ⟶ equalizer f g`. -/
def equalizer.lift {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_equalizer f g] {W : C} (k : W ⟶ X) (h : k ≫ f = k ≫ g) : W ⟶ equalizer f g :=
  limit.lift (parallel_pair f g) (fork.of_ι k h)

@[simp] theorem equalizer.lift_ι_assoc {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_equalizer f g] {W : C} (k : W ⟶ X) (h : k ≫ f = k ≫ g) {X' : C} (f' : X ⟶ X') : equalizer.lift k h ≫ equalizer.ι f g ≫ f' = k ≫ f' := sorry

/-- A morphism `k : W ⟶ X` satisfying `k ≫ f = k ≫ g` induces a morphism `l : W ⟶ equalizer f g`
    satisfying `l ≫ equalizer.ι f g = k`. -/
def equalizer.lift' {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_equalizer f g] {W : C} (k : W ⟶ X) (h : k ≫ f = k ≫ g) : Subtype fun (l : W ⟶ equalizer f g) => l ≫ equalizer.ι f g = k :=
  { val := equalizer.lift k h, property := equalizer.lift_ι k h }

/-- Two maps into an equalizer are equal if they are are equal when composed with the equalizer
    map. -/
theorem equalizer.hom_ext {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_equalizer f g] {W : C} {k : W ⟶ equalizer f g} {l : W ⟶ equalizer f g} (h : k ≫ equalizer.ι f g = l ≫ equalizer.ι f g) : k = l :=
  fork.is_limit.hom_ext (limit.is_limit (parallel_pair f g)) h

/-- An equalizer morphism is a monomorphism -/
protected instance equalizer.ι_mono {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_equalizer f g] : mono (equalizer.ι f g) :=
  mono.mk fun (Z : C) (h k : Z ⟶ equalizer f g) (w : h ≫ equalizer.ι f g = k ≫ equalizer.ι f g) => equalizer.hom_ext w

/-- The equalizer morphism in any limit cone is a monomorphism. -/
theorem mono_of_is_limit_parallel_pair {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {c : cone (parallel_pair f g)} (i : is_limit c) : mono (fork.ι c) := sorry

/-- The identity determines a cone on the equalizer diagram of `f` and `g` if `f = g`. -/
def id_fork {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (h : f = g) : fork f g :=
  fork.of_ι 𝟙 sorry

/-- The identity on `X` is an equalizer of `(f, g)`, if `f = g`. -/
def is_limit_id_fork {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (h : f = g) : is_limit (id_fork h) :=
  fork.is_limit.mk (id_fork h) (fun (s : fork f g) => fork.ι s) sorry sorry

/-- Every equalizer of `(f, g)`, where `f = g`, is an isomorphism. -/
def is_iso_limit_cone_parallel_pair_of_eq {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (h₀ : f = g) {c : cone (parallel_pair f g)} (h : is_limit c) : is_iso (nat_trans.app (cone.π c) walking_parallel_pair.zero) :=
  is_iso.of_iso (is_limit.cone_point_unique_up_to_iso h (is_limit_id_fork h₀))

/-- The equalizer of `(f, g)`, where `f = g`, is an isomorphism. -/
def equalizer.ι_of_eq {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_equalizer f g] (h : f = g) : is_iso (equalizer.ι f g) :=
  is_iso_limit_cone_parallel_pair_of_eq h (limit.is_limit (parallel_pair f g))

/-- Every equalizer of `(f, f)` is an isomorphism. -/
def is_iso_limit_cone_parallel_pair_of_self {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {c : cone (parallel_pair f f)} (h : is_limit c) : is_iso (nat_trans.app (cone.π c) walking_parallel_pair.zero) :=
  is_iso_limit_cone_parallel_pair_of_eq sorry h

/-- An equalizer that is an epimorphism is an isomorphism. -/
def is_iso_limit_cone_parallel_pair_of_epi {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {c : cone (parallel_pair f g)} (h : is_limit c) [epi (nat_trans.app (cone.π c) walking_parallel_pair.zero)] : is_iso (nat_trans.app (cone.π c) walking_parallel_pair.zero) :=
  is_iso_limit_cone_parallel_pair_of_eq sorry h

protected instance has_equalizer_of_self {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : has_equalizer f f :=
  has_limit.mk (limit_cone.mk (id_fork rfl) (is_limit_id_fork rfl))

/-- The equalizer inclusion for `(f, f)` is an isomorphism. -/
protected instance equalizer.ι_of_self {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : is_iso (equalizer.ι f f) :=
  equalizer.ι_of_eq sorry

/-- The equalizer of a morphism with itself is isomorphic to the source. -/
def equalizer.iso_source_of_self {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : equalizer f f ≅ X :=
  as_iso (equalizer.ι f f)

@[simp] theorem equalizer.iso_source_of_self_hom {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : iso.hom (equalizer.iso_source_of_self f) = equalizer.ι f f :=
  rfl

@[simp] theorem equalizer.iso_source_of_self_inv {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : iso.inv (equalizer.iso_source_of_self f) =
  equalizer.lift 𝟙
    (eq.mpr
      (id
        (Eq.trans
          ((fun (a a_1 : X ⟶ Y) (e_1 : a = a_1) (ᾰ ᾰ_1 : X ⟶ Y) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2) (𝟙 ≫ f)
            f (category.id_comp f) (𝟙 ≫ f) f (category.id_comp f))
          (propext (eq_self_iff_true f))))
      trivial) :=
  rfl

/--
`has_coequalizer f g` represents a particular choice of colimiting cocone
for the parallel pair of morphisms `f` and `g`.
-/
def has_coequalizer {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) :=
  has_colimit (parallel_pair f g)

/-- If a coequalizer of `f` and `g` exists, we can access an arbitrary choice of such by
    saying `coequalizer f g`. -/
def coequalizer {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_coequalizer f g] : C :=
  colimit (parallel_pair f g)

/--  If a coequalizer of `f` and `g` exists, we can access the corresponding projection by
    saying `coequalizer.π f g`. -/
def coequalizer.π {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_coequalizer f g] : Y ⟶ coequalizer f g :=
  colimit.ι (parallel_pair f g) walking_parallel_pair.one

/--
An arbitrary choice of coequalizer cocone for a parallel pair `f` and `g`.
-/
def coequalizer.cofork {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_coequalizer f g] : cofork f g :=
  colimit.cocone (parallel_pair f g)

@[simp] theorem coequalizer.cofork_π {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_coequalizer f g] : cofork.π (coequalizer.cofork f g) = coequalizer.π f g :=
  rfl

@[simp] theorem coequalizer.cofork_ι_app_one {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_coequalizer f g] : nat_trans.app (cocone.ι (coequalizer.cofork f g)) walking_parallel_pair.one = coequalizer.π f g :=
  rfl

theorem coequalizer.condition_assoc {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_coequalizer f g] {X' : C} (f' : coequalizer f g ⟶ X') : f ≫ coequalizer.π f g ≫ f' = g ≫ coequalizer.π f g ≫ f' := sorry

/-- The cofork built from `coequalizer.π f g` is colimiting. -/
def coequalizer_is_coequalizer {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) [has_coequalizer f g] : is_colimit (cofork.of_π (coequalizer.π f g) (coequalizer.condition f g)) :=
  is_colimit.of_iso_colimit (colimit.is_colimit (parallel_pair f g))
    (cofork.ext (iso.refl (cocone.X (colimit.cocone (parallel_pair f g)))) sorry)

/-- Any morphism `k : Y ⟶ W` satisfying `f ≫ k = g ≫ k` factors through the coequalizer of `f`
    and `g` via `coequalizer.desc : coequalizer f g ⟶ W`. -/
def coequalizer.desc {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_coequalizer f g] {W : C} (k : Y ⟶ W) (h : f ≫ k = g ≫ k) : coequalizer f g ⟶ W :=
  colimit.desc (parallel_pair f g) (cofork.of_π k h)

@[simp] theorem coequalizer.π_desc_assoc {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_coequalizer f g] {W : C} (k : Y ⟶ W) (h : f ≫ k = g ≫ k) {X' : C} (f' : W ⟶ X') : coequalizer.π f g ≫ coequalizer.desc k h ≫ f' = k ≫ f' := sorry

/-- Any morphism `k : Y ⟶ W` satisfying `f ≫ k = g ≫ k` induces a morphism
    `l : coequalizer f g ⟶ W` satisfying `coequalizer.π ≫ g = l`. -/
def coequalizer.desc' {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_coequalizer f g] {W : C} (k : Y ⟶ W) (h : f ≫ k = g ≫ k) : Subtype fun (l : coequalizer f g ⟶ W) => coequalizer.π f g ≫ l = k :=
  { val := coequalizer.desc k h, property := coequalizer.π_desc k h }

/-- Two maps from a coequalizer are equal if they are equal when composed with the coequalizer
    map -/
theorem coequalizer.hom_ext {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_coequalizer f g] {W : C} {k : coequalizer f g ⟶ W} {l : coequalizer f g ⟶ W} (h : coequalizer.π f g ≫ k = coequalizer.π f g ≫ l) : k = l :=
  cofork.is_colimit.hom_ext (colimit.is_colimit (parallel_pair f g)) h

/-- A coequalizer morphism is an epimorphism -/
protected instance coequalizer.π_epi {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_coequalizer f g] : epi (coequalizer.π f g) :=
  epi.mk
    fun (Z : C) (h k : coequalizer f g ⟶ Z) (w : coequalizer.π f g ≫ h = coequalizer.π f g ≫ k) => coequalizer.hom_ext w

/-- The coequalizer morphism in any colimit cocone is an epimorphism. -/
theorem epi_of_is_colimit_parallel_pair {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {c : cocone (parallel_pair f g)} (i : is_colimit c) : epi (nat_trans.app (cocone.ι c) walking_parallel_pair.one) := sorry

/-- The identity determines a cocone on the coequalizer diagram of `f` and `g`, if `f = g`. -/
def id_cofork {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (h : f = g) : cofork f g :=
  cofork.of_π 𝟙 sorry

/-- The identity on `Y` is a coequalizer of `(f, g)`, where `f = g`.  -/
def is_colimit_id_cofork {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (h : f = g) : is_colimit (id_cofork h) :=
  cofork.is_colimit.mk (id_cofork h) (fun (s : cofork f g) => cofork.π s) sorry sorry

/-- Every coequalizer of `(f, g)`, where `f = g`, is an isomorphism. -/
def is_iso_colimit_cocone_parallel_pair_of_eq {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} (h₀ : f = g) {c : cocone (parallel_pair f g)} (h : is_colimit c) : is_iso (nat_trans.app (cocone.ι c) walking_parallel_pair.one) :=
  is_iso.of_iso (is_colimit.cocone_point_unique_up_to_iso (is_colimit_id_cofork h₀) h)

/-- The coequalizer of `(f, g)`, where `f = g`, is an isomorphism. -/
def coequalizer.π_of_eq {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} [has_coequalizer f g] (h : f = g) : is_iso (coequalizer.π f g) :=
  is_iso_colimit_cocone_parallel_pair_of_eq h (colimit.is_colimit (parallel_pair f g))

/-- Every coequalizer of `(f, f)` is an isomorphism. -/
def is_iso_colimit_cocone_parallel_pair_of_self {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {c : cocone (parallel_pair f f)} (h : is_colimit c) : is_iso (nat_trans.app (cocone.ι c) walking_parallel_pair.one) :=
  is_iso_colimit_cocone_parallel_pair_of_eq sorry h

/-- A coequalizer that is a monomorphism is an isomorphism. -/
def is_iso_limit_cocone_parallel_pair_of_epi {C : Type u} [category C] {X : C} {Y : C} {f : X ⟶ Y} {g : X ⟶ Y} {c : cocone (parallel_pair f g)} (h : is_colimit c) [mono (nat_trans.app (cocone.ι c) walking_parallel_pair.one)] : is_iso (nat_trans.app (cocone.ι c) walking_parallel_pair.one) :=
  is_iso_colimit_cocone_parallel_pair_of_eq sorry h

protected instance has_coequalizer_of_self {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : has_coequalizer f f :=
  has_colimit.mk (colimit_cocone.mk (id_cofork rfl) (is_colimit_id_cofork rfl))

/-- The coequalizer projection for `(f, f)` is an isomorphism. -/
protected instance coequalizer.π_of_self {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : is_iso (coequalizer.π f f) :=
  coequalizer.π_of_eq sorry

/-- The coequalizer of a morphism with itself is isomorphic to the target. -/
def coequalizer.iso_target_of_self {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : coequalizer f f ≅ Y :=
  iso.symm (as_iso (coequalizer.π f f))

@[simp] theorem coequalizer.iso_target_of_self_hom {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : iso.hom (coequalizer.iso_target_of_self f) =
  coequalizer.desc 𝟙
    (eq.mpr
      (id
        (Eq.trans
          ((fun (a a_1 : X ⟶ Y) (e_1 : a = a_1) (ᾰ ᾰ_1 : X ⟶ Y) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2) (f ≫ 𝟙)
            f (category.comp_id f) (f ≫ 𝟙) f (category.comp_id f))
          (propext (eq_self_iff_true f))))
      trivial) :=
  rfl

@[simp] theorem coequalizer.iso_target_of_self_inv {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) : iso.inv (coequalizer.iso_target_of_self f) = coequalizer.π f f :=
  rfl

/--
The comparison morphism for the equalizer of `f,g`.
This is an isomorphism iff `G` preserves the equalizer of `f,g`; see
`category_theory/limits/preserves/shapes/equalizers.lean`
-/
def equalizer_comparison {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) {D : Type u₂} [category D] (G : C ⥤ D) [has_equalizer f g] [has_equalizer (functor.map G f) (functor.map G g)] : functor.obj G (equalizer f g) ⟶ equalizer (functor.map G f) (functor.map G g) :=
  equalizer.lift (functor.map G (equalizer.ι f g)) sorry

@[simp] theorem equalizer_comparison_comp_π_assoc {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) {D : Type u₂} [category D] (G : C ⥤ D) [has_equalizer f g] [has_equalizer (functor.map G f) (functor.map G g)] {X' : D} (f' : functor.obj G X ⟶ X') : equalizer_comparison f g G ≫ equalizer.ι (functor.map G f) (functor.map G g) ≫ f' = functor.map G (equalizer.ι f g) ≫ f' := sorry

@[simp] theorem map_lift_equalizer_comparison {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) {D : Type u₂} [category D] (G : C ⥤ D) [has_equalizer f g] [has_equalizer (functor.map G f) (functor.map G g)] {Z : C} {h : Z ⟶ X} (w : h ≫ f = h ≫ g) : functor.map G (equalizer.lift h w) ≫ equalizer_comparison f g G =
  equalizer.lift (functor.map G h)
    (eq.mpr
      (id
        ((fun (a a_1 : functor.obj G Z ⟶ functor.obj G Y) (e_1 : a = a_1) (ᾰ ᾰ_1 : functor.obj G Z ⟶ functor.obj G Y)
            (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2)
          (functor.map G h ≫ functor.map G f) (functor.map G (h ≫ g))
          (Eq.trans (Eq.symm (functor.map_comp G h f))
            ((fun (c : C ⥤ D) {X Y : C} (ᾰ ᾰ_1 : X ⟶ Y) (e_4 : ᾰ = ᾰ_1) => congr_arg (functor.map c) e_4) G (h ≫ f)
              (h ≫ g) w))
          (functor.map G h ≫ functor.map G g) (functor.map G (h ≫ g)) (Eq.symm (functor.map_comp G h g))))
      (Eq.refl (functor.map G (h ≫ g)))) := sorry

/-- The comparison morphism for the coequalizer of `f,g`. -/
def coequalizer_comparison {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) {D : Type u₂} [category D] (G : C ⥤ D) [has_coequalizer f g] [has_coequalizer (functor.map G f) (functor.map G g)] : coequalizer (functor.map G f) (functor.map G g) ⟶ functor.obj G (coequalizer f g) :=
  coequalizer.desc (functor.map G (coequalizer.π f g)) sorry

@[simp] theorem ι_comp_coequalizer_comparison {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) {D : Type u₂} [category D] (G : C ⥤ D) [has_coequalizer f g] [has_coequalizer (functor.map G f) (functor.map G g)] : coequalizer.π (functor.map G f) (functor.map G g) ≫ coequalizer_comparison f g G = functor.map G (coequalizer.π f g) :=
  coequalizer.π_desc (functor.map G (coequalizer.π f g)) (coequalizer_comparison._proof_1 f g G)

@[simp] theorem coequalizer_comparison_map_desc_assoc {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) (g : X ⟶ Y) {D : Type u₂} [category D] (G : C ⥤ D) [has_coequalizer f g] [has_coequalizer (functor.map G f) (functor.map G g)] {Z : C} {h : Y ⟶ Z} (w : f ≫ h = g ≫ h) {X' : D} (f' : functor.obj G Z ⟶ X') : coequalizer_comparison f g G ≫ functor.map G (coequalizer.desc h w) ≫ f' =
  coequalizer.desc (functor.map G h)
      (eq.mpr
        (id
          ((fun (a a_1 : functor.obj G X ⟶ functor.obj G Z) (e_1 : a = a_1) (ᾰ ᾰ_1 : functor.obj G X ⟶ functor.obj G Z)
              (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2)
            (functor.map G f ≫ functor.map G h) (functor.map G (g ≫ h))
            (Eq.trans (Eq.symm (functor.map_comp G f h))
              ((fun (c : C ⥤ D) {X Y : C} (ᾰ ᾰ_1 : X ⟶ Y) (e_4 : ᾰ = ᾰ_1) => congr_arg (functor.map c) e_4) G (f ≫ h)
                (g ≫ h) w))
            (functor.map G g ≫ functor.map G h) (functor.map G (g ≫ h)) (Eq.symm (functor.map_comp G g h))))
        (Eq.refl (functor.map G (g ≫ h)))) ≫
    f' := sorry

/-- `has_equalizers` represents a choice of equalizer for every pair of morphisms -/
def has_equalizers (C : Type u) [category C] :=
  has_limits_of_shape walking_parallel_pair C

/-- `has_coequalizers` represents a choice of coequalizer for every pair of morphisms -/
def has_coequalizers (C : Type u) [category C] :=
  has_colimits_of_shape walking_parallel_pair C

/-- If `C` has all limits of diagrams `parallel_pair f g`, then it has all equalizers -/
theorem has_equalizers_of_has_limit_parallel_pair (C : Type u) [category C] [∀ {X Y : C} {f g : X ⟶ Y}, has_limit (parallel_pair f g)] : has_equalizers C :=
  has_limits_of_shape.mk fun (F : walking_parallel_pair ⥤ C) => has_limit_of_iso (iso.symm (diagram_iso_parallel_pair F))

/-- If `C` has all colimits of diagrams `parallel_pair f g`, then it has all coequalizers -/
theorem has_coequalizers_of_has_colimit_parallel_pair (C : Type u) [category C] [∀ {X Y : C} {f g : X ⟶ Y}, has_colimit (parallel_pair f g)] : has_coequalizers C :=
  has_colimits_of_shape.mk fun (F : walking_parallel_pair ⥤ C) => has_colimit_of_iso (diagram_iso_parallel_pair F)

-- In this section we show that a split mono `f` equalizes `(retraction f ≫ f)` and `(𝟙 Y)`.

/--
A split mono `f` equalizes `(retraction f ≫ f)` and `(𝟙 Y)`.
Here we build the cone, and show in `split_mono_equalizes` that it is a limit cone.
-/
@[simp] theorem cone_of_split_mono_X {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) [split_mono f] : cone.X (cone_of_split_mono f) = X :=
  Eq.refl (cone.X (cone_of_split_mono f))

/--
A split mono `f` equalizes `(retraction f ≫ f)` and `(𝟙 Y)`.
-/
def split_mono_equalizes {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) [split_mono f] : is_limit (cone_of_split_mono f) :=
  fork.is_limit.mk' (cone_of_split_mono f)
    fun (s : fork 𝟙 (retraction f ≫ f)) => { val := fork.ι s ≫ retraction f, property := sorry }

-- In this section we show that a split epi `f` coequalizes `(f ≫ section_ f)` and `(𝟙 X)`.

/--
A split epi `f` coequalizes `(f ≫ section_ f)` and `(𝟙 X)`.
Here we build the cocone, and show in `split_epi_coequalizes` that it is a colimit cocone.
-/
def cocone_of_split_epi {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) [split_epi f] : cocone (parallel_pair 𝟙 (f ≫ section_ f)) :=
  cofork.of_π f sorry

/--
A split epi `f` coequalizes `(f ≫ section_ f)` and `(𝟙 X)`.
-/
def split_epi_coequalizes {C : Type u} [category C] {X : C} {Y : C} (f : X ⟶ Y) [split_epi f] : is_colimit (cocone_of_split_epi f) :=
  cofork.is_colimit.mk' (cocone_of_split_epi f)
    fun (s : cofork 𝟙 (f ≫ section_ f)) => { val := section_ f ≫ cofork.π s, property := sorry }


import algebra.category.Module.change_of_rings
import algebra.group.ulift
import group_theory.quotient_group
import data.rat.basic
import category_theory.preadditive.injective

import .adjunction_general

open category_theory
open_locale tensor_product

universes u v w w'

@[derive add_comm_group]
def rat_circle : Type* :=
  ulift $ ℚ ⧸ (algebra_map ℤ ℚ).to_add_monoid_hom.range

instance rat_circle.inj : injective (AddCommGroup.of rat_circle) := sorry

section character_module

variables {R : Type u} [comm_ring R]
variables (M : Type w) [add_comm_group M] [module R M]
variables (N : Type w) [add_comm_group N] [module R N]
variables (N' : Type w) [add_comm_group N'] [module R N']

@[derive add_comm_group]
def character_module : Type w :=
M →ₗ[ℤ] rat_circle.{w}

instance character_module.coe_to_fun : has_coe_to_fun (character_module M) (λ _, M → rat_circle) :=
{ coe := λ f, f.to_fun }

instance character_module.add_monoid_hom_class :
  add_monoid_hom_class (character_module M) M rat_circle :=
{ coe := λ f, f,
  coe_injective' := λ f g h, linear_map.ext $ λ x, congr_fun h _,
  map_add := λ f, f.map_add,
  map_zero := λ f, f.map_zero }

instance character_module.has_smul : has_smul R (character_module M) :=
{ smul := λ r f,
  { to_fun := λ m, f (r • m),
    map_add' := λ x y, by rw [smul_add, map_add],
    map_smul' := λ z x, by rw [ring_hom.id_apply, ←f.map_smul, smul_comm] } }

@[simp] lemma character_module.smul_apply (r : R) (f : character_module M) (m : M) :
  (r • f) m = f (r • m) := rfl

instance character_module.mul_action : mul_action R (character_module M) :=
{ one_smul := λ f, linear_map.ext $ λ x, by rw [character_module.smul_apply, one_smul],
  mul_smul := λ a b f, linear_map.ext $ λ x,
    by rw [character_module.smul_apply, mul_comm, mul_smul, character_module.smul_apply, character_module.smul_apply],
  ..character_module.has_smul M}

instance character_module.distrib_mul_action : distrib_mul_action R (character_module M) :=
{ smul_zero := λ r, linear_map.ext $ λ x,
    by rw [character_module.smul_apply, linear_map.zero_apply, linear_map.zero_apply],
  smul_add := λ r f g, linear_map.ext $ λ x,
    by rw [character_module.smul_apply, linear_map.add_apply, linear_map.add_apply,
      character_module.smul_apply, character_module.smul_apply],
  ..character_module.mul_action M}

instance character_module.module : module R (character_module M) :=
{ add_smul := λ a b f, linear_map.ext $ λ x,
    by simp only [character_module.smul_apply, add_smul, linear_map.add_apply, map_add],
  zero_smul := λ f, linear_map.ext $ λ x,
    by simp only [character_module.smul_apply, zero_smul, map_zero, linear_map.zero_apply],
  ..character_module.distrib_mul_action M}

namespace character_module

section map

variables {M N}

@[simps apply]
def map (L : M →ₗ[R] N) : character_module N →ₗ[R] character_module M :=
{ to_fun := λ f,
  { to_fun := λ m, f $ L m,
    map_add' := λ m m', by rw [map_add, map_add],
    map_smul' := λ z m, by rw [ring_hom.id_apply, map_zsmul, f.map_smul] },
  map_add' := λ f g, linear_map.ext $ λ x, by simp,
  map_smul' := λ r f, linear_map.ext $ λ x, by simp }

end map

lemma map_id :
  map (linear_map.id : M →ₗ[R] M) =
  (linear_map.id : character_module M →ₗ[R] character_module M) :=
linear_map.ext $ λ f, linear_map.ext $ λ x, by simp

section map

variables {M N N'}

lemma map_comp (L : M →ₗ[R] N) (L' : N →ₗ[R] N') :
  map (L'.comp L) = (map L).comp (map L') :=
linear_map.ext $ λ f, linear_map.ext $ λ x, by simp

lemma map_inj (L : M →ₗ[R] N) (hL : function.injective L) :
  function.surjective (map L) :=
λ g, ⟨begin
  haveI : mono (AddCommGroup.of_hom L.to_add_monoid_hom) :=
    by rwa AddCommGroup.mono_iff_injective,
  refine add_monoid_hom.to_int_linear_map
    (injective.factor_thru (AddCommGroup.of_hom g.to_add_monoid_hom)
    (AddCommGroup.of_hom L.to_add_monoid_hom)),
end, begin
  ext x : 1,
  haveI : mono (AddCommGroup.of_hom L.to_add_monoid_hom) :=
    by rwa AddCommGroup.mono_iff_injective,
  rw [map_apply, linear_map.coe_mk, add_monoid_hom.coe_to_int_linear_map],
  exact fun_like.congr_fun (injective.comp_factor_thru (AddCommGroup.of_hom g.to_add_monoid_hom)
    (AddCommGroup.of_hom L.to_add_monoid_hom)) x,
end⟩

end map

def functor : (Module.{max u v} R)ᵒᵖ ⥤ Module.{max u v} R :=
{ obj := λ M, Module.of R $ character_module M.unop,
  map := λ M N L, map L.unop,
  map_id' := λ M, map_id M.unop,
  map_comp' := λ M N N' L L', map_comp L'.unop L.unop }

@[simps]
def hom_equiv : (N →ₗ[R] character_module M) ≃ (character_module $ N ⊗[R] M) :=
{ to_fun := λ f, add_monoid_hom.to_int_linear_map $ tensor_product.to_add_comm_group' R (λ p, f p.1 p.2)
    (λ m, by rw [map_zero, linear_map.zero_apply])
    (λ n, by rw [map_zero]) (λ m n n', by rw [map_add, linear_map.add_apply])
    (λ n m m', by rw [map_add]) $ λ r n m, by simpa only [map_smul],
  inv_fun := λ g,
  { to_fun := λ n,
    { to_fun := λ m, g (n ⊗ₜ m),
      -- map_zero' := by rw [tensor_product.tmul_zero, map_zero],
      map_add' := λ m m', by rw [tensor_product.tmul_add, map_add],
      map_smul' := λ z m, by rw [ring_hom.id_apply, ←map_zsmul, tensor_product.smul_tmul',
        tensor_product.smul_tmul] },
    map_add' := λ n n', linear_map.ext $ λ m, by simp only [linear_map.coe_mk,
      tensor_product.add_tmul, map_add, linear_map.add_apply],
    map_smul' := λ r n, linear_map.ext $ λ m, by simp only [linear_map.coe_mk,
      ring_hom.id_apply, character_module.smul_apply, tensor_product.smul_tmul], },
  left_inv := λ f, linear_map.ext $ λ n, linear_map.ext $ λ m, by simp only [linear_map.coe_mk,
    add_monoid_hom.coe_to_int_linear_map, tensor_product.to_add_comm_group'.apply_tmul],
  right_inv := λ g,
  begin
    ext1 z,
    induction z using tensor_product.induction_on with m n z z' hz hz',
    { simp only [map_zero] },
    { simp only [linear_map.coe_mk, tensor_product.to_add_comm_group'.apply_tmul,
        add_monoid_hom.coe_to_int_linear_map], },
    { rw [map_add, hz, hz', map_add] },
  end
   }

@[reducible]
noncomputable def aux1_finite_order
  {m : M} (hm : m ≠ 0) (hm_order : add_order_of m ≠ 0)
  (k : ℤ) : rat_circle :=
ulift.up $ quotient_add_group.mk' _ (rat.mk k $ add_order_of m)

lemma aux1_finite_order.map_add {m : M} (hm : m ≠ 0) (hm_order : add_order_of m ≠ 0)
  (k k' : ℤ) :
  aux1_finite_order M hm hm_order (k + k') =
  aux1_finite_order M hm hm_order k + aux1_finite_order M hm hm_order k' :=
begin
  ext1,
  erw quotient_add_group.mk'_eq_mk',
  refine ⟨_, ⟨0, rfl⟩, _⟩,
  erw add_zero,
  rw rat.add_mk,
end

lemma aux1_finite_order.map_zsmul {m : M} (hm : m ≠ 0) (hm_order : add_order_of m ≠ 0)
  (k k' : ℤ) :
  aux1_finite_order M hm hm_order (k * k') =
  k • aux1_finite_order M hm hm_order k' :=
begin
  ext1,
  erw quotient_add_group.mk'_eq_mk',
  refine ⟨_, ⟨0, rfl⟩, _⟩,
  erw add_zero,
  rw zsmul_eq_mul,
  rw rat.coe_int_eq_mk,
  rw rat.mul_def,
  work_on_goal 2 { norm_num },
  work_on_goal 2 { exact_mod_cast hm_order },
  rw one_mul,
end

lemma aux1_finite_order_wd' {m : M} (hm : m ≠ 0) (hm_order : add_order_of m ≠ 0)
  (k k' : ℤ) (EQ : k • m = k' • m) :
  aux1_finite_order M hm hm_order k = aux1_finite_order M hm hm_order k' :=
begin
  ext1,
  rw quotient_add_group.mk'_eq_mk',
  have EQ' : |k - k'| • m = 0,
  { rcases abs_choice (k - k') with h|h;
    rw h;
    try { rw neg_sub };
    rw sub_smul;
    rw EQ;
    rw sub_self, },
  obtain ⟨z, hz⟩ := add_order_of_dvd_iff_zsmul_eq_zero.mpr EQ',
  suffices : ∃ (z : ℚ) (H : z ∈ (algebra_map ℤ ℚ).to_add_monoid_hom.range),
    rat.mk (|k - k'|) ↑(add_order_of m) = z,
  { obtain ⟨_, ⟨z, rfl⟩, (h : _ = (z : ℚ))⟩ := this,
    rcases abs_choice (k - k') with h'|h',
    { rw h' at h,
      rw [sub_eq_add_neg, rat.add_mk, ←rat.neg_def, add_neg_eq_iff_eq_add] at h,
      rw h,
      refine ⟨_, ⟨-z, rfl⟩, _⟩,
      change _ + -(z : ℚ) = _,
      abel, },
    { rw h' at h,
      rw [←rat.neg_def, sub_eq_add_neg, rat.add_mk, neg_eq_iff_neg_eq, ←rat.neg_def] at h,
      replace h := h.symm,
      rw [add_neg_eq_iff_eq_add] at h,
      rw h,
      refine ⟨_, ⟨z, rfl⟩, _⟩,
      change _ + _ + (z : ℚ) = _,
      abel, }, },
  refine ⟨_, ⟨z, rfl⟩, _⟩,
  rw hz,
  change _ = (z : ℚ),
  rw rat.coe_int_eq_mk,
  rw rat.mk_eq,
  { ring },
  { exact_mod_cast hm_order },
  { norm_num },
end

@[simps]
noncomputable def morphism_finite_order {m : M} (hm : m ≠ 0) (hm_order : add_order_of m ≠ 0) :
  (submodule.span ℤ {m} : submodule ℤ M) →ₗ[ℤ] rat_circle :=
{ to_fun := λ m', aux1_finite_order M hm hm_order (submodule.mem_span_singleton.mp m'.2).some,
  map_add' := λ m' m'',
  begin
    generalize_proofs h1 h2 h3,
    have EQ : (h2.some + h3.some) • m = h1.some • m,
    { rw [add_smul, h2.some_spec, h3.some_spec, h1.some_spec], refl, },
    rw ←aux1_finite_order_wd' M hm hm_order _ _ EQ,
    rw aux1_finite_order.map_add,
  end,
  map_smul' := λ z m',
  begin
    generalize_proofs h1 h2,
    have EQ : (z * h2.some) • m = h1.some • m,
    { rw [mul_smul, h2.some_spec, h1.some_spec], refl, },
    rw ←aux1_finite_order_wd' M hm hm_order _ _ EQ,
    rw ring_hom.id_apply,
    rw aux1_finite_order.map_zsmul,
  end }

lemma morphism_finite_order.apply_m {m : M} (hm : m ≠ 0) (hm_order : add_order_of m ≠ 0) :
  morphism_finite_order M hm hm_order ⟨m, submodule.mem_span_singleton_self _⟩ ≠ 0 :=
begin
  rw morphism_finite_order_apply,
  generalize_proofs h1 h2,
  rw show aux1_finite_order M hm hm_order h2.some = aux1_finite_order M hm hm_order 1,
  { rw aux1_finite_order_wd', rw [h2.some_spec, one_smul] },
  change ¬ _,
  rw (ulift.ext_iff _ _).not,
  erw quotient_add_group.eq_zero_iff,
  rintros ⟨z, (hz : (z : ℚ) = _)⟩,
  rw rat.coe_int_eq_mk at hz,
  rw rat.mk_eq at hz,
  work_on_goal 2 { norm_num },
  work_on_goal 2 { exact_mod_cast hm_order },
  rw [one_mul] at hz,
  rw int.mul_eq_one_iff_eq_one_or_neg_one at hz,
  rcases hz with ⟨rfl, hz⟩|⟨rfl, hz⟩,
  { refine hm (add_monoid.order_of_eq_one_iff.mp _),
    exact_mod_cast hz, },
  { have rid := int.coe_nat_nonneg (add_order_of m),
    rw hz at rid,
    linarith only [rid], },
end

@[reducible]
noncomputable def aux1_infinite_order
  {m : M} (hm : m ≠ 0) (hm_order : add_order_of m = 0)
  (k : ℤ) : rat_circle :=
ulift.up $ quotient_add_group.mk' _ (rat.mk k 37)

lemma aux1_infinite_order.eq_zero {m : M} (hm : m ≠ 0) (hm_order : add_order_of m = 0)
  (k : ℤ) (hk : k • m = 0) : k = 0 :=
begin
  by_contra rid,
  have : int.nat_abs k • m = 0,
  { suffices : (int.nat_abs k : ℤ) • m = 0,
    { exact_mod_cast this },
    rw ←int.abs_eq_nat_abs,
    rcases abs_choice k with h|h;
    rw h;
    try { rw neg_smul };
    rw hk,
    rw neg_zero },
  refine add_order_of_eq_zero_iff'.mp hm_order k.nat_abs _ this,
  exact int.nat_abs_pos_of_ne_zero rid,
end

lemma aux1_infinite_order_wd' {m : M} (hm : m ≠ 0) (hm_order : add_order_of m = 0)
  (k k' : ℤ) (hk : k • m = k' • m) : k = k' :=
begin
  have EQ : (k - k') • m = 0,
  { rw [sub_smul, hk, sub_self] },
  rw ←int.sub_eq_zero_iff_eq,
  exact aux1_infinite_order.eq_zero M hm hm_order _ EQ,
end

@[simps]
noncomputable def morphism_infinite_order {m : M} (hm : m ≠ 0) (hm_order : add_order_of m = 0) :
  (submodule.span ℤ {m} : submodule ℤ M) →ₗ[ℤ] rat_circle :=
{ to_fun := λ m', aux1_infinite_order M hm hm_order (submodule.mem_span_singleton.mp m'.2).some,
  map_add' := λ m' m'',
  begin
    generalize_proofs h1 h2 h3,
    have EQ : (h2.some + h3.some) • m = h1.some • m,
    { rw [add_smul, h2.some_spec, h3.some_spec, h1.some_spec], refl, },
    rw ←aux1_infinite_order_wd' M hm hm_order _ _ EQ,
    ext1,
    erw quotient_add_group.mk'_eq_mk',
    refine ⟨_, ⟨0, rfl⟩, _⟩,
    erw add_zero,
    rw rat.add_mk,
  end,
  map_smul' := λ z m',
  begin
    generalize_proofs h1 h2,
    have EQ : (z * h2.some) • m = h1.some • m,
    { rw [mul_smul, h2.some_spec, h1.some_spec], refl, },
    rw ←aux1_infinite_order_wd' M hm hm_order _ _ EQ,
    rw ring_hom.id_apply,
    ext1,
    erw quotient_add_group.mk'_eq_mk',
    refine ⟨_, ⟨0, rfl⟩, _⟩,
    erw add_zero,
    rw zsmul_eq_mul,
    rw rat.coe_int_eq_mk,
    rw rat.mul_def,
    work_on_goal 2 { norm_num },
    work_on_goal 2 { norm_num },
    rw one_mul,
  end }

lemma morphism_infinite_order.apply_m {m : M} (hm : m ≠ 0) (hm_order : add_order_of m = 0) :
  morphism_infinite_order M hm hm_order ⟨m, submodule.mem_span_singleton_self _⟩ ≠ 0 :=
begin
  rw morphism_infinite_order_apply,
  generalize_proofs h1 h2,
  rw show aux1_infinite_order M hm hm_order h2.some = aux1_infinite_order M hm hm_order 1,
  { rw aux1_infinite_order_wd' M hm hm_order h2.some 1 _, rw [h2.some_spec, one_smul] },
  change ¬ _,
  rw (ulift.ext_iff _ _).not,
  erw quotient_add_group.eq_zero_iff,
  rintros ⟨z, (hz : (z : ℚ) = _)⟩,
  rw rat.coe_int_eq_mk at hz,
  rw rat.mk_eq at hz,
  work_on_goal 2 { norm_num },
  work_on_goal 2 { norm_num },
  rw [one_mul] at hz,
  rw int.mul_eq_one_iff_eq_one_or_neg_one at hz,
  rcases hz with ⟨rfl, hz⟩|⟨rfl, hz⟩,
  { linarith, },
  { linarith, },
end

lemma non_zero {m : M} (hm : m ≠ 0) : ∃ (h : character_module M), h m ≠ 0 :=
begin
  let M' : submodule ℤ M := submodule.span ℤ {m},
  suffices : ∃ (h' : M' →ₗ[ℤ] rat_circle), h' ⟨m, submodule.subset_span (set.mem_singleton _)⟩ ≠ 0,
  { obtain ⟨h', hh'⟩ := this,
    let ι : AddCommGroup.of M' ⟶ AddCommGroup.of M := ⟨λ m, m.1, rfl, λ _ _, rfl⟩,
    haveI : mono ι,
    { refine concrete_category.mono_of_injective _ subtype.val_injective, },
    let f' : AddCommGroup.of M ⟶ AddCommGroup.of rat_circle :=
      injective.factor_thru (show AddCommGroup.of M' ⟶ AddCommGroup.of rat_circle,
      from h'.to_add_monoid_hom) ι,
    refine ⟨add_monoid_hom.to_int_linear_map $ show M →+ rat_circle, from f', _⟩,
    have eq0 : _ ≫ f' = _ := injective.comp_factor_thru _ _,
    erw fun_like.congr_fun eq0 ⟨m, submodule.subset_span (set.mem_singleton _)⟩,
    exact hh' },
  by_cases h_order : add_order_of m ≠ 0,
  { exact ⟨morphism_finite_order _ hm h_order, morphism_finite_order.apply_m _ hm h_order⟩, },
  { exact ⟨morphism_infinite_order _ hm (by rwa [not_ne_iff] at h_order),
      morphism_infinite_order.apply_m _ hm (by rwa [not_ne_iff] at h_order)⟩ },
end

end character_module

end character_module


import data.finset.nat_antidiagonal
import analysis.normed_space.basic
import analysis.specific_limits.basic
import laurent_measures.int_nat_shifts

noncomputable theory

open metric finset normed_field --filter
open_locale nnreal classical big_operators topological_space

namespace aux_thm69

section equivalences_def

-- for mathlib?
def range_equiv_Icc {n d : ℤ} (m : ℕ) (hm : n - d = m) :
  range m.succ ≃ (Icc d n) :=
begin
  refine ⟨λ a, ⟨a + d, mem_Icc.mpr ⟨_, _⟩⟩, _, _, _⟩,
  { exact (le_add_iff_nonneg_left _).mpr (int.of_nat_nonneg _) },
  { refine add_le_of_le_sub_right _,
    exact (int.coe_nat_le.mpr (nat.le_of_lt_succ $ (@mem_range m.succ a).mp a.2)).trans hm.ge },
  { rintro ⟨a, hha⟩,
    refine ⟨(a - d).nat_abs, mem_range_succ_iff.mpr _⟩,
    lift (a - d) to ℕ using (sub_nonneg.mpr ((mem_Icc).mp hha).1) with ad had,
    rw [int.nat_abs_of_nat, ← (int.coe_nat_le_coe_nat_iff _ _), had, ← hm],
    exact sub_le_sub_right ((mem_Icc).mp hha).2 _ },
  { exact λ x, subtype.ext (by simp) },
  { rintro ⟨x, hx⟩,
    simp [int.nat_abs_of_nonneg (sub_nonneg.mpr (mem_Icc.mp hx).1), sub_add_cancel] }
end

--for `mathlib`?
def equiv_bdd_integer_nat (N : ℤ) : ℕ ≃ {x // N ≤ x} :=
{ to_fun := λ n, ⟨n + N, le_add_of_nonneg_left (int.coe_nat_nonneg n)⟩,
  inv_fun := λ x, (x.1 - N).nat_abs,
  left_inv := λ n, by simp,
  right_inv := λ ⟨x, hx⟩, subtype.ext (by simp [int.nat_abs_of_nonneg (sub_nonneg.mpr hx)]) }

def equiv_Icc_bdd_nonneg {d : ℤ} (hd : 0 ≤ d) : {x // d ≤ x} ≃
  {x // x ∉ range (int.eq_coe_of_zero_le hd).some}:=
begin
  fconstructor,
  { rintro ⟨_, h⟩,
    have := (int.eq_coe_of_zero_le (hd.trans h)).some_spec,
    rw [(int.eq_coe_of_zero_le hd).some_spec, this, int.coe_nat_le, ← not_lt, ← mem_range] at h,
    exact ⟨_, h⟩ },
  { rintro ⟨_, h⟩,
    rw [mem_range, nat.lt_iff_add_one_le, not_le, nat.lt_add_one_iff, ← int.coe_nat_le,
      ← (int.eq_coe_of_zero_le hd).some_spec] at h,
    exact ⟨_, h⟩ },
  { rintro ⟨_, h⟩,
    simp only [int.coe_nat_inj', int.of_nat_eq_coe],
    exact (int.eq_coe_of_zero_le (hd.trans h)).some_spec.symm },
  { rintro ⟨_, h⟩,
    simp only [int.coe_nat_inj', int.of_nat_eq_coe],
    exact ((@exists_eq' _ _).some_spec).symm },
end

def T {d : ℤ} (hd : d < 0) : finset {x : ℤ // d ≤ x} := Ico ⟨d, le_of_eq rfl⟩ ⟨0, le_of_lt hd⟩

lemma T.zero_le {d : ℤ} (hd : d < 0) {y : {x // d ≤ x}} (h : y ∉ T hd) :
  0 ≤ y.1 :=
suffices (⟨d, rfl.le⟩ : {x // d ≤ x}) ≤ y → (⟨_, hd.le⟩ : {x // d ≤ x}) ≤ y, from this y.2,
by { simpa [T, hd] using h }

def R {d n : ℤ} (hn : 0 ≤ n - d) : finset {x : ℤ // d ≤ x} := Icc ⟨d, le_of_eq rfl⟩ ⟨n, int.le_of_sub_nonneg hn⟩

def equiv_Icc_R {d n : ℤ} (hn : 0 ≤ n - d) : Icc d n ≃ R hn :=
begin
  fconstructor,
  { rintro ⟨m, hm⟩,
    replace hm := mem_Icc.mp hm,
    use ⟨m, hm.1⟩,
    dsimp [R],
    rw mem_Icc,
    use and.intro (subtype.mk_le_mk.mpr hm.1) (subtype.mk_le_mk.mpr hm.2) },
  { dsimp [R],
    rintro ⟨⟨x, hx⟩, h⟩,
    rw mem_Icc at h,
    use x,
    rw mem_Icc,
    use and.intro hx (subtype.mk_le_mk.mp h.2) },
  { simp only [id.def],
    rintro ⟨⟨x, hx⟩, h⟩,
    all_goals {simp only}, },
  { simp only [id.def],
    dsimp [R],
    rintro ⟨⟨x, hx⟩, h⟩,
    simp only },
end

def equiv_compl_R_bdd {d n : ℤ} (hn : 0 ≤ n - d): {a : ℤ // n.succ ≤ a } ≃
  ((R hn)ᶜ : set {x : ℤ // d ≤ x}) :=
begin
  fconstructor,
  { rintro ⟨m, hm⟩,
    have hd : d ≤ m := (int.le_add_one (int.le_of_sub_nonneg hn)).trans hm,
    use ⟨m, hd⟩,
    dsimp only [R],
    simpa [mem_coe, mem_Icc, subtype.mk_le_mk, not_and, hd, forall_true_left,
      not_le, int.lt_iff_add_one_le, hm] },
  { rintro ⟨⟨x, hx⟩, h_mem⟩,
    dsimp only [R] at h_mem,
    simp only [subtype.mk_le_mk, coe_Icc, not_and, not_le, set.mem_Icc, hx, forall_true_left,
      int.lt_iff_add_one_le, set.mem_compl_iff] at h_mem,
    use ⟨x, h_mem⟩ },
  { rintro ⟨_, _⟩, simp only [id.def] },
  { rintro ⟨⟨_, _⟩, _⟩, simpa }
end

end equivalences_def

section equivalences_lemma

--for mathlib?
lemma sum_reverse {β : Type*} [add_comm_group β] (f : ℤ → β) (n : ℕ) :
  ∑ l in (range n.succ), (f (n - l)) = ∑ l in (range n.succ), f l :=
begin
  induction n with n hn generalizing f,
  { simp only [zero_sub, int.coe_nat_zero, sum_singleton, neg_zero, range_one] },
  { rw [sum_range_succ', sum_range_succ (f ∘ coe)],
    simp only [←hn, int.coe_nat_zero, add_sub_add_right_eq_sub, function.comp_app, sub_zero,
      int.coe_nat_succ] }
end

lemma sum_range_sum_Icc' {α : Type*} [field α] (f : ℤ → α) {n d : ℤ} (hn : 0 ≤ n - d) :
 ∑ l in (range (int.eq_coe_of_zero_le hn).some.succ), (f (n - l)) * 2 ^ l =
 ∑ k in (Icc d n), (f k) * 2 ^ (n - k) :=
begin
  let m := (int.eq_coe_of_zero_le hn).some,
  have h := (int.eq_coe_of_zero_le hn).some_spec.symm,
  have sum_swap : ∑ (l : ℕ) in range m.succ, f (n - l) * 2 ^ l =
    ∑ (l : ℕ) in range m.succ, f (l + d) * 2 ^ (m - l),
  { convert (sum_reverse (λ l, f (n - l) * 2 ^ l) m).symm using 1,
    { simp_rw ← zpow_coe_nat },
    refine sum_congr rfl (λ x hx, _),
    rw [mem_range, nat.lt_succ_iff] at hx,
    rw [← zpow_coe_nat, int.coe_nat_sub hx],
    congr' 2,
    rw [h], ring, },
  rw [sum_swap, ← sum_finset_coe, ← sum_finset_coe _ (Icc _ _)],
  refine fintype.sum_equiv (range_equiv_Icc _ h.symm) _ _ (λ x, _),
  dsimp [range_equiv_Icc],
  rw [← sub_sub, sub_right_comm, ← zpow_coe_nat],
  refine congr_arg ((*) _) (congr_arg (pow 2) _),
  have := @nat.cast_sub ℤ _ _ _ (mem_range_succ_iff.mp x.2),
  simpa only [this, h, sub_left_inj, subtype.val_eq_coe],
end

lemma sum_range_sum_Icc (f : ℤ → ℝ) (n d : ℤ) (hn : 0 ≤ n - d) :
 ∑ l in (range (int.eq_coe_of_zero_le hn).some.succ), (f (n - l)) * 2 ^ l =
 ∑ k in (Icc d n), (f k) * 2 ^ (n - k) :=
sum_range_sum_Icc' f hn

lemma equiv_Icc_bdd_nonneg_apply {d : ℤ} (hd : 0 ≤ d) (x : {x // d ≤ x}) :
  ((equiv_Icc_bdd_nonneg hd x) : ℤ) = x.1 :=
begin
  rcases x with ⟨_, h⟩,
  exact (Exists.some_spec (int.eq_coe_of_zero_le (hd.trans h))).symm,
end

lemma equiv_Icc_R_apply {d n : ℤ} (hn : 0 ≤ n - d) (x : Icc d n) : ((equiv_Icc_R hn x) : ℤ) =
  (x : ℤ) := by {rcases x, refl}

lemma equiv_compl_R_bdd_apply {d n : ℤ} (hn : 0 ≤ n - d) (x : {a : ℤ // n.succ ≤ a }) :
(equiv_compl_R_bdd hn x : ℤ) = (x : ℤ) := by {rcases x with ⟨y, hy⟩, simpa}

lemma sum_Icc_sum_tail (f : ℤ → ℤ) (n d : ℤ)
  (hf : (has_sum (λ x : ℤ, (f x : ℝ) * (2 ^ x)⁻¹) 0))
  (hd : ∀ n : ℤ, n < d → f n = 0)
  (hn : 0 ≤ n - d) : - ∑ k in (Icc d n), ((f k) : ℝ) * 2 ^ (n - k) =
  2 ^ n * tsum (λ x : {a : ℤ // n.succ ≤ a }, (f x : ℝ) * (2 ^ x.1)⁻¹) :=
begin
  simp_rw [zpow_sub₀ (@two_ne_zero ℝ _ _), div_eq_mul_inv, ← mul_assoc, (mul_comm _ ((2 : ℝ) ^ n)), mul_assoc, ← mul_sum, neg_mul_eq_mul_neg, mul_eq_mul_left_iff],
  apply or.intro_left,
  have H_supp : function.support (λ n : ℤ, (f n  : ℝ) * (2 ^ n)⁻¹) ⊆ { a : ℤ | d ≤ a},
  { rw function.support_subset_iff,
    intro _,
    rw [← not_imp_not, not_not, mul_eq_zero],
    intro hx,
    simp only [not_le, set.mem_set_of_eq] at hx,
    apply or.intro_left,
    exact int.cast_eq_zero.mpr (hd _ hx), },
  rw ← (@has_sum_subtype_iff_of_support_subset ℝ ℤ _ _ (λ n : ℤ, ( f n ) * (2 ^ n)⁻¹) _ _ H_supp) at hf,
  let g := (λ n : {x : ℤ // d ≤ x}, ( f n : ℝ) * (2 ^ n.1)⁻¹),
  have hg : has_sum g 0 := hf,
  have := @sum_add_tsum_compl _ _ _ _ _ g _ (R hn) hg.summable,
  rw [hg.tsum_eq, add_eq_zero_iff_eq_neg] at this,
  replace this := neg_eq_iff_neg_eq.mpr this.symm,
  convert this using 1,
  { simp only [neg_inj],
    have h_R := @fintype.sum_equiv (Icc d n) (R hn) _ _ _ _ (equiv_Icc_R hn) ((λ x : ℤ, ((f x : ℝ) * (2 ^ x)⁻¹)) ∘ coe) (g ∘ coe),
    rw @sum_subtype ℝ ℤ _ (∈ Icc d n) _ (Icc d n) _ (λ x, ((f x) : ℝ) * (2 ^x)⁻¹),
    rw @sum_subtype ℝ _ _ (∈ R hn) _ (R hn) _ (λ x, g x),
    simp only,
    refine h_R _,
    { intro x,
      dsimp [g],
      rw [← coe_coe, equiv_Icc_R_apply hn x] },
    all_goals { intro _, refl } },
  { dsimp only [g],
    refine eq.trans _ (@equiv.tsum_eq _ _ _ _ _ _ (equiv_compl_R_bdd hn) (λ x, (f x : ℝ) * (2 ^ (x.1 : ℤ))⁻¹)),
    apply tsum_congr,
    intro x,
    simp_rw [← coe_coe],
    nth_rewrite_rhs 0 [subtype.val_eq_coe],
    rw [← coe_coe, equiv_compl_R_bdd_apply hn x, ← subtype.val_eq_coe], }
end

end equivalences_lemma

section summability

lemma summable_shift (f : ℤ → ℝ) (N : ℤ) :
  summable (λ x : ℕ, f (x + N)) ↔ summable (λ x : {x // N ≤ x}, f x) :=
@equiv.summable_iff _ _ _ _ _ (λ x : {x // N ≤ x}, f x) (equiv_bdd_integer_nat N)

lemma int_tsum_shift (f : ℤ → ℝ) (N : ℤ) :
  ∑' (x : ℕ), f (x + N) = ∑' (x : {x // N ≤ x}), f x :=
begin
  apply tsum_eq_tsum_of_has_sum_iff_has_sum,
  intro _,
  apply (@equiv.has_sum_iff ℝ _ ℕ _ _ (f ∘ coe) _ ((equiv_bdd_integer_nat N))),
end

lemma summable_of_eventually_zero (f : ℤ → ℝ) (d : ℤ) (hd : ∀ (n : ℤ), n < d → f n = 0) :
  summable (λ (n : ℕ), f (-↑n - 1)) :=
begin
  refine summable_of_norm_bounded_eventually _ summable_zero _,
  suffices : {x : ℕ | ¬f (-↑x - 1) = 0}.finite, by simpa,
  apply (range (int.to_nat (-d))).finite_to_set.subset _,
  refine λ x h, mem_coe.mpr (mem_range.mpr (int.lt_to_nat.mpr (not_le.mp (λ H, _)))),
  simpa [hd _ (int.sub_one_lt_iff.mpr (neg_le.mp H))] using h,
end

lemma summable_iff_on_nat_less {f : ℤ → ℝ} (d : ℤ) (h : ∀ n : ℤ, n < d → f n = 0) :
  summable f ↔ summable (λ n : ℕ, f n) :=
int_summable_iff.trans $ and_iff_left_iff_imp.mpr $ λ hh, summable_of_eventually_zero f d $
  λ n nd, by simp [h _ nd]

lemma nnreal.summable_iff_on_nat_less {f : ℤ → ℝ≥0} (d : ℤ) (h : ∀ {n : ℤ}, n < d → f n = 0) :
  summable f ↔ summable (λ n : ℕ, f n) :=
nnreal.summable_coe.symm.trans $ (summable_iff_on_nat_less d (λ n nd,
  ((f n).coe_eq_zero.mpr (h nd)))).trans nnreal.summable_coe

lemma int.summable_iff_on_nat {f : ℤ → ℤ} {ρ : ℝ≥0} (d : ℤ) (h : ∀ n : ℤ, n < d → f n = 0) :
summable (λ n, ∥ f n ∥ * ρ ^ n) ↔ summable (λ n : ℕ, ∥ f n ∥ * ρ ^ (n : ℤ)) :=
summable_iff_on_nat_less d (λ n nd, by simp [h _ nd])

lemma nnreal.summable_iff_on_nat_less_shift {f : ℤ → ℝ≥0} (d : ℤ)
  (h : ∀ {n : ℤ}, n < d → f n = 0) (M : ℤ) :
  summable f ↔ summable (λ n : ℕ, f (M + n)) :=
begin
  rw [← nnreal.summable_coe, ← nnreal.summable_coe],
  split,
  { intro hf,
    have hinj : function.injective (λ (n : ℕ), M + n),
    { rintros a b (h2 : M + a = M + b),
      simpa using h2 },
    convert hf.comp_injective hinj,
  },
  { intro hs,
    rw ← (equiv.add_left M).summable_iff,
    rw ← @summable_iff_on_nat_less (λ (a : ℤ), ↑(f (M + a))) (d - M) _ at hs,
    { exact (summable_congr (λ k, rfl)).1 hs },
    { intros n hn,
      dsimp only,
      rw h, norm_num, linarith } },
end

-- nnreal.summable_coe.symm.trans $ (summable_iff_on_nat_less d (λ n nd,
--   ((f n).coe_eq_zero.mpr (h nd)))).trans nnreal.summable_coe

lemma summable_smaller_radius_norm {f : ℤ → ℤ} {ρ : ℝ≥0} (d : ℤ) (ρ_half : 2⁻¹ < ρ)
(hf : summable (λ n : ℤ, ∥ f n ∥ * ρ ^ n))
  (hd : ∀ n : ℤ, n < d → f n = 0) : --(F : ℒ S) (s : S) :
  summable (λ n, ∥ f n ∥ * 2⁻¹ ^ n) :=
begin
  refine int_summable_iff.mpr ⟨_, _⟩,
  { apply (int_summable_iff.mp hf).1.smaller_radius ρ_half.le (by simp) },
  { apply summable_of_eventually_zero (λ n, ∥f n∥ * 2⁻¹ ^ n) d (λ n nd, by simp [hd _ nd]) }
end

lemma summable_smaller_radius {f : ℤ → ℤ} {ρ : ℝ≥0} (d : ℤ)
  (hf : summable (λ n : ℤ, ∥ f n ∥ * ρ ^ n)) (hd : ∀ n : ℤ, n < d → f n = 0) (hρ : 2⁻¹ < ρ) :
  summable (λ n, (f n : ℝ) * 2⁻¹ ^ n) :=
begin
  refine summable_of_summable_norm _,
  simp_rw [norm_mul, norm_zpow, norm_inv, real.norm_two],
  apply (summable_iff_on_nat_less d _).mpr,
  apply summable.smaller_radius _ hρ.le,
  { exact  (λ x, norm_nonneg _) },
  { simpa using (int_summable_iff.mp hf).1 },
  { exact λ n nd, by simp [hd _ nd] }
end

lemma prod_nat_summable {f : ℤ → ℤ} {r : ℝ≥0} (half_lt_r : 2⁻¹ < r)
  (hf : summable (λ n : ℤ, ∥ f n ∥ * r ^ n)) :
  summable (λ lj: ℕ × ℕ,
    (2⁻¹ : ℝ) * ∥ (f (lj.fst + 1 + lj.snd) : ℝ) * 2⁻¹^(lj.snd) ∥ * r ^ (lj.fst)) :=
begin
  have r0 : r ≠ 0 := by { rintro rfl, simpa only [not_lt_zero'] using half_lt_r },
  have : summable (λ (n : ℤ), ∥(f n : ℝ) * (2 * r)⁻¹∥ * r ^ n),
  { convert_to summable (λ (z : ℤ), ((2⁻¹ * r⁻¹) : ℝ) • (∥f z∥ * ↑r ^ z)),
    { ext,
      field_simp,
      congr' 0 },
    { exact summable.const_smul hf } },
  convert prod_nat_summable_aux (_ : (2⁻¹ * r⁻¹) < 1) this,
  { ext,
    field_simp [r0],
    ring_exp },
  { exact nnreal.div_lt_one_of_lt half_lt_r },
end

-- lemma fiberwise_summable_norm {f : ℤ → ℤ} {r : ℝ≥0} (d : ℤ) (half_lt_r : 1 / 2 < r)
--   (hf : summable (λ n : ℤ, ∥ f n ∥ * r ^ n))
--   (hd : ∀ n : ℤ, n < d → f n = 0) :
--   summable (λ (n : ℕ), 1 / 2 * ∥ ∑' (i : ℕ), (f (n + 1 + i) : ℝ) * (1 / 2) ^ i ∥ * ↑r ^ n) :=
-- begin
--   have r_pos : ∀ (b : ℕ), 0 < (r : ℝ) ^ b :=
--     pow_pos (lt_trans (by exact nnreal.coe_pos.mp (one_div_pos.mpr zero_lt_two)) half_lt_r),
--   have smaller_shift : ∀ (b : ℕ), summable (λ j : ℕ, ∥ (f (b + 1 + j) : ℝ)  * (1 / 2) ^ j ∥),
--   { intro b,
--     refine (summable_mul_right_iff (by norm_num : (1 / 2 : ℝ) ^ (b + 1) ≠ 0)).mpr _,
--     obtain hff := (equiv.add_group_add (b+1 : ℤ)).summable_iff.mpr (summable_smaller_radius_norm d
--       half_lt_r hf hd),
--     convert (summable_iff_on_nat_less (d - (b + 1)) _).mp hff,
--     { ext,
--       simp [mul_assoc, zpow_add₀ (two_ne_zero : (2 : ℝ) ≠ 0) (b+1), mul_inv, zpow_coe_nat,
--         mul_comm ((2 : ℝ) ^ x)⁻¹], refl },
--     { exact λ n nd, by simp [hd _ (lt_sub_iff_add_lt'.mp nd)] } },
--   set ϕ := λ lj: ℕ × ℕ,
--     (1 / 2 : ℝ) * ∥ (f (lj.fst + 1 + lj.snd) : ℝ) * (1/2)^(lj.snd) ∥ * r ^ (lj.fst),
--   have H : ∀ b : ℕ, summable (λ i : ℕ, ϕ(b, i)) :=
--     λ n, (summable_mul_right_iff (r_pos n).ne').mp ((smaller_shift n).mul_left (1 / 2)),
--   have := (has_sum.prod_fiberwise (prod_nat_summable half_lt_r hf).has_sum
--     (λ b, (H b).has_sum)).summable,
--   dsimp [ϕ] at this,
--   apply summable_of_nonneg_of_le (λ b, _) (λ b, _) this,
--   { exact mul_nonneg (mul_nonneg one_half_pos.le (norm_nonneg _)) (r_pos b).le },
--   { simp_rw mul_assoc,
--     rw [tsum_mul_left, mul_le_mul_left (@one_half_pos ℝ _), tsum_mul_right,
--       mul_le_mul_right (r_pos b)],
--     exact norm_tsum_le_tsum_norm (smaller_shift b) }
-- end

-- lemma summable_convolution {r : ℝ≥0} (half_lt_r : 1 / 2 < r) (f : ℤ → ℤ) (d : ℤ)
--   (hf : summable (λ n, ∥ f n ∥ * r ^ n)) (hd : ∀ n : ℤ, n < d → f n = 0)
--   (hd_shift : ∀ (n : ℤ), n < d → ∥∑' (i : ℕ), (f (n + 1 + i) : ℝ) * (1 / 2) ^ i∥ = 0) :
--   summable (λ n : ℤ, (1 / 2) * ∥ tsum (λ i : ℕ, ((f (n + 1 + i)) : ℝ) * (1 / 2) ^ i) ∥ * r ^ n) :=
-- begin
--   suffices h_on_nat : summable (λ (n : ℕ),
--     (1 / 2) * ∥∑' (i : ℕ), (f (n + 1 + i) : ℝ) * (1 / 2) ^ i∥ * (r : ℝ) ^ n),
--   { simp_rw mul_assoc at ⊢ h_on_nat,
--     rw [← summable_mul_left_iff (ne_of_gt (@one_half_pos ℝ _))] at ⊢ h_on_nat,
--     refine (summable_iff_on_nat_less d (λ n hn, _)).mpr _,
--     { rw [hd_shift _ hn, zero_mul] },
--     { exact_mod_cast h_on_nat } },
--   apply fiberwise_summable_norm d half_lt_r hf hd,
-- end

end summability

end aux_thm69