/-
Copyright (c) 2022 Antoine Labelle, Rémi Bottinelli. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Antoine Labelle, Rémi Bottinelli

! This file was ported from Lean 3 source module combinatorics.quiver.cast
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Combinatorics.Quiver.Basic
import Mathbin.Combinatorics.Quiver.Path

/-!

# Rewriting arrows and paths along vertex equalities

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This files defines `hom.cast` and `path.cast` (and associated lemmas) in order to allow
rewriting arrows and paths along equalities of their endpoints.

-/

universe v v₁ v₂ u u₁ u₂

variable {U : Type _} [Quiver.{u + 1} U]

namespace Quiver

/-!
### Rewriting arrows along equalities of vertices
-/

#print Quiver.Hom.cast /-
/-- Change the endpoints of an arrow using equalities. -/
def Hom.cast {u v u' v' : U} (hu : u = u') (hv : v = v') (e : u ⟶ v) : u' ⟶ v' :=
  Eq.ndrec (Eq.ndrec e hv) hu
#align quiver.hom.cast Quiver.Hom.cast
-/

/- warning: quiver.hom.cast_eq_cast -> Quiver.Hom.cast_eq_cast is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (e : Quiver.Hom.{succ u1, u2} U _inst_1 u v), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v') (Quiver.Hom.cast.{u1, u2} U _inst_1 u v u' v' hu hv e) (cast.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v') (Eq.mpr.{0} (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v')) (id_tag Tactic.IdTag.rw (Eq.{1} Prop (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v'))) (Eq.ndrec.{0, succ u2} U u (fun (_a : U) => Eq.{1} Prop (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 _a v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v'))) (rfl.{1} Prop (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v'))) u' hu)) (Eq.mpr.{0} (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v') (Quiver.Hom.{succ u1, u2} U _inst_1 u' v')) (id_tag Tactic.IdTag.rw (Eq.{1} Prop (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v') (Quiver.Hom.{succ u1, u2} U _inst_1 u' v'))) (Eq.ndrec.{0, succ u2} U v (fun (_a : U) => Eq.{1} Prop (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' _a) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v'))) (rfl.{1} Prop (Eq.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v) (Quiver.Hom.{succ u1, u2} U _inst_1 u' v'))) v' hv)) (rfl.{succ (succ u1)} Type.{u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v')))) e)
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (e : Quiver.Hom.{succ u2, u1} U _inst_1 u v), Eq.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v') (Quiver.Hom.cast.{u2, u1} U _inst_1 u v u' v' hu hv e) (cast.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v') (Eq.mpr.{0} (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v')) (id.{0} (Eq.{1} Prop (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v'))) (Eq.ndrec.{0, succ u1} U u (fun (_a : U) => Eq.{1} Prop (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 _a v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v'))) (Eq.refl.{1} Prop (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v'))) u' hu)) (Eq.mpr.{0} (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v') (Quiver.Hom.{succ u2, u1} U _inst_1 u' v')) (id.{0} (Eq.{1} Prop (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v') (Quiver.Hom.{succ u2, u1} U _inst_1 u' v'))) (Eq.ndrec.{0, succ u1} U v (fun (_a : U) => Eq.{1} Prop (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' _a) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v'))) (Eq.refl.{1} Prop (Eq.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v) (Quiver.Hom.{succ u2, u1} U _inst_1 u' v'))) v' hv)) (Eq.refl.{succ (succ u2)} Type.{u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v')))) e)
Case conversion may be inaccurate. Consider using '#align quiver.hom.cast_eq_cast Quiver.Hom.cast_eq_castₓ'. -/
theorem Hom.cast_eq_cast {u v u' v' : U} (hu : u = u') (hv : v = v') (e : u ⟶ v) :
    e.cast hu hv = cast (by rw [hu, hv]) e :=
  by
  subst_vars
  rfl
#align quiver.hom.cast_eq_cast Quiver.Hom.cast_eq_cast

/- warning: quiver.hom.cast_rfl_rfl -> Quiver.Hom.cast_rfl_rfl is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} (e : Quiver.Hom.{succ u1, u2} U _inst_1 u v), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u v) (Quiver.Hom.cast.{u1, u2} U _inst_1 u v u v (rfl.{succ u2} U u) (rfl.{succ u2} U v) e) e
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} (e : Quiver.Hom.{succ u2, u1} U _inst_1 u v), Eq.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u v) (Quiver.Hom.cast.{u2, u1} U _inst_1 u v u v (rfl.{succ u1} U u) (rfl.{succ u1} U v) e) e
Case conversion may be inaccurate. Consider using '#align quiver.hom.cast_rfl_rfl Quiver.Hom.cast_rfl_rflₓ'. -/
@[simp]
theorem Hom.cast_rfl_rfl {u v : U} (e : u ⟶ v) : e.cast rfl rfl = e :=
  rfl
#align quiver.hom.cast_rfl_rfl Quiver.Hom.cast_rfl_rfl

/- warning: quiver.hom.cast_cast -> Quiver.Hom.cast_cast is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} {u'' : U} {v'' : U} (e : Quiver.Hom.{succ u1, u2} U _inst_1 u v) (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (hu' : Eq.{succ u2} U u' u'') (hv' : Eq.{succ u2} U v' v''), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u'' v'') (Quiver.Hom.cast.{u1, u2} U _inst_1 u' v' u'' v'' hu' hv' (Quiver.Hom.cast.{u1, u2} U _inst_1 u v u' v' hu hv e)) (Quiver.Hom.cast.{u1, u2} U _inst_1 u v u'' v'' (Eq.trans.{succ u2} U u u' u'' hu hu') (Eq.trans.{succ u2} U v v' v'' hv hv') e)
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} {u'' : U} {v'' : U} (e : Quiver.Hom.{succ u2, u1} U _inst_1 u v) (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (hu' : Eq.{succ u1} U u' u'') (hv' : Eq.{succ u1} U v' v''), Eq.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u'' v'') (Quiver.Hom.cast.{u2, u1} U _inst_1 u' v' u'' v'' hu' hv' (Quiver.Hom.cast.{u2, u1} U _inst_1 u v u' v' hu hv e)) (Quiver.Hom.cast.{u2, u1} U _inst_1 u v u'' v'' (Eq.trans.{succ u1} U u u' u'' hu hu') (Eq.trans.{succ u1} U v v' v'' hv hv') e)
Case conversion may be inaccurate. Consider using '#align quiver.hom.cast_cast Quiver.Hom.cast_castₓ'. -/
@[simp]
theorem Hom.cast_cast {u v u' v' u'' v'' : U} (e : u ⟶ v) (hu : u = u') (hv : v = v')
    (hu' : u' = u'') (hv' : v' = v'') :
    (e.cast hu hv).cast hu' hv' = e.cast (hu.trans hu') (hv.trans hv') :=
  by
  subst_vars
  rfl
#align quiver.hom.cast_cast Quiver.Hom.cast_cast

/- warning: quiver.hom.cast_heq -> Quiver.Hom.cast_heq is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (e : Quiver.Hom.{succ u1, u2} U _inst_1 u v), HEq.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v') (Quiver.Hom.cast.{u1, u2} U _inst_1 u v u' v' hu hv e) (Quiver.Hom.{succ u1, u2} U _inst_1 u v) e
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (e : Quiver.Hom.{succ u2, u1} U _inst_1 u v), HEq.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v') (Quiver.Hom.cast.{u2, u1} U _inst_1 u v u' v' hu hv e) (Quiver.Hom.{succ u2, u1} U _inst_1 u v) e
Case conversion may be inaccurate. Consider using '#align quiver.hom.cast_heq Quiver.Hom.cast_heqₓ'. -/
theorem Hom.cast_heq {u v u' v' : U} (hu : u = u') (hv : v = v') (e : u ⟶ v) :
    HEq (e.cast hu hv) e := by
  subst_vars
  rfl
#align quiver.hom.cast_heq Quiver.Hom.cast_heq

/- warning: quiver.hom.cast_eq_iff_heq -> Quiver.Hom.cast_eq_iff_heq is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (e : Quiver.Hom.{succ u1, u2} U _inst_1 u v) (e' : Quiver.Hom.{succ u1, u2} U _inst_1 u' v'), Iff (Eq.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v') (Quiver.Hom.cast.{u1, u2} U _inst_1 u v u' v' hu hv e) e') (HEq.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u v) e (Quiver.Hom.{succ u1, u2} U _inst_1 u' v') e')
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (e : Quiver.Hom.{succ u2, u1} U _inst_1 u v) (e' : Quiver.Hom.{succ u2, u1} U _inst_1 u' v'), Iff (Eq.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v') (Quiver.Hom.cast.{u2, u1} U _inst_1 u v u' v' hu hv e) e') (HEq.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u v) e (Quiver.Hom.{succ u2, u1} U _inst_1 u' v') e')
Case conversion may be inaccurate. Consider using '#align quiver.hom.cast_eq_iff_heq Quiver.Hom.cast_eq_iff_heqₓ'. -/
theorem Hom.cast_eq_iff_heq {u v u' v' : U} (hu : u = u') (hv : v = v') (e : u ⟶ v) (e' : u' ⟶ v') :
    e.cast hu hv = e' ↔ HEq e e' := by
  rw [hom.cast_eq_cast]
  exact cast_eq_iff_heq
#align quiver.hom.cast_eq_iff_heq Quiver.Hom.cast_eq_iff_heq

/- warning: quiver.hom.eq_cast_iff_heq -> Quiver.Hom.eq_cast_iff_heq is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (e : Quiver.Hom.{succ u1, u2} U _inst_1 u v) (e' : Quiver.Hom.{succ u1, u2} U _inst_1 u' v'), Iff (Eq.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v') e' (Quiver.Hom.cast.{u1, u2} U _inst_1 u v u' v' hu hv e)) (HEq.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 u' v') e' (Quiver.Hom.{succ u1, u2} U _inst_1 u v) e)
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (e : Quiver.Hom.{succ u2, u1} U _inst_1 u v) (e' : Quiver.Hom.{succ u2, u1} U _inst_1 u' v'), Iff (Eq.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v') e' (Quiver.Hom.cast.{u2, u1} U _inst_1 u v u' v' hu hv e)) (HEq.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 u' v') e' (Quiver.Hom.{succ u2, u1} U _inst_1 u v) e)
Case conversion may be inaccurate. Consider using '#align quiver.hom.eq_cast_iff_heq Quiver.Hom.eq_cast_iff_heqₓ'. -/
theorem Hom.eq_cast_iff_heq {u v u' v' : U} (hu : u = u') (hv : v = v') (e : u ⟶ v) (e' : u' ⟶ v') :
    e' = e.cast hu hv ↔ HEq e' e :=
  by
  rw [eq_comm, hom.cast_eq_iff_heq]
  exact ⟨HEq.symm, HEq.symm⟩
#align quiver.hom.eq_cast_iff_heq Quiver.Hom.eq_cast_iff_heq

/-!
### Rewriting paths along equalities of vertices
-/

open Path

#print Quiver.Path.cast /-
/-- Change the endpoints of a path using equalities. -/
def Path.cast {u v u' v' : U} (hu : u = u') (hv : v = v') (p : Path u v) : Path u' v' :=
  Eq.ndrec (Eq.ndrec p hv) hu
#align quiver.path.cast Quiver.Path.cast
-/

/- warning: quiver.path.cast_eq_cast -> Quiver.Path.cast_eq_cast is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (p : Quiver.Path.{succ u1, u2} U _inst_1 u v), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v') (Quiver.Path.cast.{u1, u2} U _inst_1 u v u' v' hu hv p) (cast.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v') (Eq.mpr.{0} (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v')) (id_tag Tactic.IdTag.rw (Eq.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v'))) (Eq.ndrec.{0, succ u2} U u (fun (_a : U) => Eq.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 _a v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v'))) (rfl.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v'))) u' hu)) (Eq.mpr.{0} (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v') (Quiver.Path.{succ u1, u2} U _inst_1 u' v')) (id_tag Tactic.IdTag.rw (Eq.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v') (Quiver.Path.{succ u1, u2} U _inst_1 u' v'))) (Eq.ndrec.{0, succ u2} U v (fun (_a : U) => Eq.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' _a) (Quiver.Path.{succ u1, u2} U _inst_1 u' v'))) (rfl.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v) (Quiver.Path.{succ u1, u2} U _inst_1 u' v'))) v' hv)) (rfl.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v')))) p)
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (p : Quiver.Path.{succ u2, u1} U _inst_1 u v), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v') (Quiver.Path.cast.{u2, u1} U _inst_1 u v u' v' hu hv p) (cast.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v') (Eq.mpr.{0} (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v')) (id.{0} (Eq.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v'))) (Eq.ndrec.{0, succ u1} U u (fun (_a : U) => Eq.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 _a v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v'))) (Eq.refl.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v'))) u' hu)) (Eq.mpr.{0} (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v') (Quiver.Path.{succ u2, u1} U _inst_1 u' v')) (id.{0} (Eq.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v') (Quiver.Path.{succ u2, u1} U _inst_1 u' v'))) (Eq.ndrec.{0, succ u1} U v (fun (_a : U) => Eq.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v')) (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' _a) (Quiver.Path.{succ u2, u1} U _inst_1 u' v'))) (Eq.refl.{1} Prop (Eq.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v) (Quiver.Path.{succ u2, u1} U _inst_1 u' v'))) v' hv)) (Eq.refl.{succ (max (succ u2) (succ u1))} Sort.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v')))) p)
Case conversion may be inaccurate. Consider using '#align quiver.path.cast_eq_cast Quiver.Path.cast_eq_castₓ'. -/
theorem Path.cast_eq_cast {u v u' v' : U} (hu : u = u') (hv : v = v') (p : Path u v) :
    p.cast hu hv = cast (by rw [hu, hv]) p :=
  Eq.drec (Eq.drec (Eq.refl (Path.cast (Eq.refl u) (Eq.refl v) p)) hu) hv
#align quiver.path.cast_eq_cast Quiver.Path.cast_eq_cast

/- warning: quiver.path.cast_rfl_rfl -> Quiver.Path.cast_rfl_rfl is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} (p : Quiver.Path.{succ u1, u2} U _inst_1 u v), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u v) (Quiver.Path.cast.{u1, u2} U _inst_1 u v u v (rfl.{succ u2} U u) (rfl.{succ u2} U v) p) p
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} (p : Quiver.Path.{succ u2, u1} U _inst_1 u v), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u v) (Quiver.Path.cast.{u2, u1} U _inst_1 u v u v (rfl.{succ u1} U u) (rfl.{succ u1} U v) p) p
Case conversion may be inaccurate. Consider using '#align quiver.path.cast_rfl_rfl Quiver.Path.cast_rfl_rflₓ'. -/
@[simp]
theorem Path.cast_rfl_rfl {u v : U} (p : Path u v) : p.cast rfl rfl = p :=
  rfl
#align quiver.path.cast_rfl_rfl Quiver.Path.cast_rfl_rfl

/- warning: quiver.path.cast_cast -> Quiver.Path.cast_cast is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} {u'' : U} {v'' : U} (p : Quiver.Path.{succ u1, u2} U _inst_1 u v) (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (hu' : Eq.{succ u2} U u' u'') (hv' : Eq.{succ u2} U v' v''), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u'' v'') (Quiver.Path.cast.{u1, u2} U _inst_1 u' v' u'' v'' hu' hv' (Quiver.Path.cast.{u1, u2} U _inst_1 u v u' v' hu hv p)) (Quiver.Path.cast.{u1, u2} U _inst_1 u v u'' v'' (Eq.trans.{succ u2} U u u' u'' hu hu') (Eq.trans.{succ u2} U v v' v'' hv hv') p)
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} {u'' : U} {v'' : U} (p : Quiver.Path.{succ u2, u1} U _inst_1 u v) (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (hu' : Eq.{succ u1} U u' u'') (hv' : Eq.{succ u1} U v' v''), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u'' v'') (Quiver.Path.cast.{u2, u1} U _inst_1 u' v' u'' v'' hu' hv' (Quiver.Path.cast.{u2, u1} U _inst_1 u v u' v' hu hv p)) (Quiver.Path.cast.{u2, u1} U _inst_1 u v u'' v'' (Eq.trans.{succ u1} U u u' u'' hu hu') (Eq.trans.{succ u1} U v v' v'' hv hv') p)
Case conversion may be inaccurate. Consider using '#align quiver.path.cast_cast Quiver.Path.cast_castₓ'. -/
@[simp]
theorem Path.cast_cast {u v u' v' u'' v'' : U} (p : Path u v) (hu : u = u') (hv : v = v')
    (hu' : u' = u'') (hv' : v' = v'') :
    (p.cast hu hv).cast hu' hv' = p.cast (hu.trans hu') (hv.trans hv') :=
  by
  subst_vars
  rfl
#align quiver.path.cast_cast Quiver.Path.cast_cast

/- warning: quiver.path.cast_nil -> Quiver.Path.cast_nil is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {u' : U} (hu : Eq.{succ u2} U u u'), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' u') (Quiver.Path.cast.{u1, u2} U _inst_1 u u u' u' hu hu (Quiver.Path.nil.{succ u1, u2} U _inst_1 u)) (Quiver.Path.nil.{succ u1, u2} U _inst_1 u')
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {u' : U} (hu : Eq.{succ u1} U u u'), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' u') (Quiver.Path.cast.{u2, u1} U _inst_1 u u u' u' hu hu (Quiver.Path.nil.{succ u2, u1} U _inst_1 u)) (Quiver.Path.nil.{succ u2, u1} U _inst_1 u')
Case conversion may be inaccurate. Consider using '#align quiver.path.cast_nil Quiver.Path.cast_nilₓ'. -/
@[simp]
theorem Path.cast_nil {u u' : U} (hu : u = u') : (Path.nil : Path u u).cast hu hu = Path.nil :=
  by
  subst_vars
  rfl
#align quiver.path.cast_nil Quiver.Path.cast_nil

/- warning: quiver.path.cast_heq -> Quiver.Path.cast_heq is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (p : Quiver.Path.{succ u1, u2} U _inst_1 u v), HEq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v') (Quiver.Path.cast.{u1, u2} U _inst_1 u v u' v' hu hv p) (Quiver.Path.{succ u1, u2} U _inst_1 u v) p
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (p : Quiver.Path.{succ u2, u1} U _inst_1 u v), HEq.{max (succ u1) (succ u2)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v') (Quiver.Path.cast.{u2, u1} U _inst_1 u v u' v' hu hv p) (Quiver.Path.{succ u2, u1} U _inst_1 u v) p
Case conversion may be inaccurate. Consider using '#align quiver.path.cast_heq Quiver.Path.cast_heqₓ'. -/
theorem Path.cast_heq {u v u' v' : U} (hu : u = u') (hv : v = v') (p : Path u v) :
    HEq (p.cast hu hv) p := by
  rw [path.cast_eq_cast]
  exact cast_hEq _ _
#align quiver.path.cast_heq Quiver.Path.cast_heq

/- warning: quiver.path.cast_eq_iff_heq -> Quiver.Path.cast_eq_iff_heq is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (p : Quiver.Path.{succ u1, u2} U _inst_1 u v) (p' : Quiver.Path.{succ u1, u2} U _inst_1 u' v'), Iff (Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v') (Quiver.Path.cast.{u1, u2} U _inst_1 u v u' v' hu hv p) p') (HEq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u v) p (Quiver.Path.{succ u1, u2} U _inst_1 u' v') p')
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (p : Quiver.Path.{succ u2, u1} U _inst_1 u v) (p' : Quiver.Path.{succ u2, u1} U _inst_1 u' v'), Iff (Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v') (Quiver.Path.cast.{u2, u1} U _inst_1 u v u' v' hu hv p) p') (HEq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u v) p (Quiver.Path.{succ u2, u1} U _inst_1 u' v') p')
Case conversion may be inaccurate. Consider using '#align quiver.path.cast_eq_iff_heq Quiver.Path.cast_eq_iff_heqₓ'. -/
theorem Path.cast_eq_iff_heq {u v u' v' : U} (hu : u = u') (hv : v = v') (p : Path u v)
    (p' : Path u' v') : p.cast hu hv = p' ↔ HEq p p' :=
  by
  rw [path.cast_eq_cast]
  exact cast_eq_iff_heq
#align quiver.path.cast_eq_iff_heq Quiver.Path.cast_eq_iff_heq

/- warning: quiver.path.eq_cast_iff_heq -> Quiver.Path.eq_cast_iff_heq is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u2} U u u') (hv : Eq.{succ u2} U v v') (p : Quiver.Path.{succ u1, u2} U _inst_1 u v) (p' : Quiver.Path.{succ u1, u2} U _inst_1 u' v'), Iff (Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v') p' (Quiver.Path.cast.{u1, u2} U _inst_1 u v u' v' hu hv p)) (HEq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' v') p' (Quiver.Path.{succ u1, u2} U _inst_1 u v) p)
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {u' : U} {v' : U} (hu : Eq.{succ u1} U u u') (hv : Eq.{succ u1} U v v') (p : Quiver.Path.{succ u2, u1} U _inst_1 u v) (p' : Quiver.Path.{succ u2, u1} U _inst_1 u' v'), Iff (Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v') p' (Quiver.Path.cast.{u2, u1} U _inst_1 u v u' v' hu hv p)) (HEq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' v') p' (Quiver.Path.{succ u2, u1} U _inst_1 u v) p)
Case conversion may be inaccurate. Consider using '#align quiver.path.eq_cast_iff_heq Quiver.Path.eq_cast_iff_heqₓ'. -/
theorem Path.eq_cast_iff_heq {u v u' v' : U} (hu : u = u') (hv : v = v') (p : Path u v)
    (p' : Path u' v') : p' = p.cast hu hv ↔ HEq p' p :=
  ⟨fun h => ((p.cast_eq_iff_heq hu hv p').1 h.symm).symm, fun h =>
    ((p.cast_eq_iff_heq hu hv p').2 h.symm).symm⟩
#align quiver.path.eq_cast_iff_heq Quiver.Path.eq_cast_iff_heq

/- warning: quiver.path.cast_cons -> Quiver.Path.cast_cons is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {w : U} {u' : U} {w' : U} (p : Quiver.Path.{succ u1, u2} U _inst_1 u v) (e : Quiver.Hom.{succ u1, u2} U _inst_1 v w) (hu : Eq.{succ u2} U u u') (hw : Eq.{succ u2} U w w'), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u' w') (Quiver.Path.cast.{u1, u2} U _inst_1 u w u' w' hu hw (Quiver.Path.cons.{succ u1, u2} U _inst_1 u v w p e)) (Quiver.Path.cons.{succ u1, u2} U _inst_1 u' v w' (Quiver.Path.cast.{u1, u2} U _inst_1 u v u' v hu (rfl.{succ u2} U v) p) (Quiver.Hom.cast.{u1, u2} U _inst_1 v w v w' (rfl.{succ u2} U v) hw e))
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {w : U} {u' : U} {w' : U} (p : Quiver.Path.{succ u2, u1} U _inst_1 u v) (e : Quiver.Hom.{succ u2, u1} U _inst_1 v w) (hu : Eq.{succ u1} U u u') (hw : Eq.{succ u1} U w w'), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u' w') (Quiver.Path.cast.{u2, u1} U _inst_1 u w u' w' hu hw (Quiver.Path.cons.{succ u2, u1} U _inst_1 u v w p e)) (Quiver.Path.cons.{succ u2, u1} U _inst_1 u' v w' (Quiver.Path.cast.{u2, u1} U _inst_1 u v u' v hu (rfl.{succ u1} U v) p) (Quiver.Hom.cast.{u2, u1} U _inst_1 v w v w' (rfl.{succ u1} U v) hw e))
Case conversion may be inaccurate. Consider using '#align quiver.path.cast_cons Quiver.Path.cast_consₓ'. -/
theorem Path.cast_cons {u v w u' w' : U} (p : Path u v) (e : v ⟶ w) (hu : u = u') (hw : w = w') :
    (p.cons e).cast hu hw = (p.cast hu rfl).cons (e.cast rfl hw) :=
  by
  subst_vars
  rfl
#align quiver.path.cast_cons Quiver.Path.cast_cons

/- warning: quiver.cast_eq_of_cons_eq_cons -> Quiver.cast_eq_of_cons_eq_cons is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {v' : U} {w : U} {p : Quiver.Path.{succ u1, u2} U _inst_1 u v} {p' : Quiver.Path.{succ u1, u2} U _inst_1 u v'} {e : Quiver.Hom.{succ u1, u2} U _inst_1 v w} {e' : Quiver.Hom.{succ u1, u2} U _inst_1 v' w} (h : Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u w) (Quiver.Path.cons.{succ u1, u2} U _inst_1 u v w p e) (Quiver.Path.cons.{succ u1, u2} U _inst_1 u v' w p' e')), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u v') (Quiver.Path.cast.{u1, u2} U _inst_1 u v u v' (rfl.{succ u2} U u) (Quiver.Path.obj_eq_of_cons_eq_cons.{u2, succ u1} U _inst_1 u v v' w p p' e e' h) p) p'
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {v' : U} {w : U} {p : Quiver.Path.{succ u2, u1} U _inst_1 u v} {p' : Quiver.Path.{succ u2, u1} U _inst_1 u v'} {e : Quiver.Hom.{succ u2, u1} U _inst_1 v w} {e' : Quiver.Hom.{succ u2, u1} U _inst_1 v' w} (h : Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u w) (Quiver.Path.cons.{succ u2, u1} U _inst_1 u v w p e) (Quiver.Path.cons.{succ u2, u1} U _inst_1 u v' w p' e')), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u v') (Quiver.Path.cast.{u2, u1} U _inst_1 u v u v' (rfl.{succ u1} U u) (Quiver.Path.obj_eq_of_cons_eq_cons.{succ u2, u1} U _inst_1 u v v' w p p' e e' h) p) p'
Case conversion may be inaccurate. Consider using '#align quiver.cast_eq_of_cons_eq_cons Quiver.cast_eq_of_cons_eq_consₓ'. -/
theorem cast_eq_of_cons_eq_cons {u v v' w : U} {p : Path u v} {p' : Path u v'} {e : v ⟶ w}
    {e' : v' ⟶ w} (h : p.cons e = p'.cons e') : p.cast rfl (obj_eq_of_cons_eq_cons h) = p' :=
  by
  rw [path.cast_eq_iff_heq]
  exact heq_of_cons_eq_cons h
#align quiver.cast_eq_of_cons_eq_cons Quiver.cast_eq_of_cons_eq_cons

/- warning: quiver.hom_cast_eq_of_cons_eq_cons -> Quiver.hom_cast_eq_of_cons_eq_cons is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} {v' : U} {w : U} {p : Quiver.Path.{succ u1, u2} U _inst_1 u v} {p' : Quiver.Path.{succ u1, u2} U _inst_1 u v'} {e : Quiver.Hom.{succ u1, u2} U _inst_1 v w} {e' : Quiver.Hom.{succ u1, u2} U _inst_1 v' w} (h : Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 u w) (Quiver.Path.cons.{succ u1, u2} U _inst_1 u v w p e) (Quiver.Path.cons.{succ u1, u2} U _inst_1 u v' w p' e')), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} U _inst_1 v' w) (Quiver.Hom.cast.{u1, u2} U _inst_1 v w v' w (Quiver.Path.obj_eq_of_cons_eq_cons.{u2, succ u1} U _inst_1 u v v' w p p' e e' h) (rfl.{succ u2} U w) e) e'
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} {v' : U} {w : U} {p : Quiver.Path.{succ u2, u1} U _inst_1 u v} {p' : Quiver.Path.{succ u2, u1} U _inst_1 u v'} {e : Quiver.Hom.{succ u2, u1} U _inst_1 v w} {e' : Quiver.Hom.{succ u2, u1} U _inst_1 v' w} (h : Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 u w) (Quiver.Path.cons.{succ u2, u1} U _inst_1 u v w p e) (Quiver.Path.cons.{succ u2, u1} U _inst_1 u v' w p' e')), Eq.{succ u2} (Quiver.Hom.{succ u2, u1} U _inst_1 v' w) (Quiver.Hom.cast.{u2, u1} U _inst_1 v w v' w (Quiver.Path.obj_eq_of_cons_eq_cons.{succ u2, u1} U _inst_1 u v v' w p p' e e' h) (rfl.{succ u1} U w) e) e'
Case conversion may be inaccurate. Consider using '#align quiver.hom_cast_eq_of_cons_eq_cons Quiver.hom_cast_eq_of_cons_eq_consₓ'. -/
theorem hom_cast_eq_of_cons_eq_cons {u v v' w : U} {p : Path u v} {p' : Path u v'} {e : v ⟶ w}
    {e' : v' ⟶ w} (h : p.cons e = p'.cons e') : e.cast (obj_eq_of_cons_eq_cons h) rfl = e' :=
  by
  rw [hom.cast_eq_iff_heq]
  exact hom_heq_of_cons_eq_cons h
#align quiver.hom_cast_eq_of_cons_eq_cons Quiver.hom_cast_eq_of_cons_eq_cons

/- warning: quiver.eq_nil_of_length_zero -> Quiver.eq_nil_of_length_zero is a dubious translation:
lean 3 declaration is
  forall {U : Type.{u2}} [_inst_1 : Quiver.{succ u1, u2} U] {u : U} {v : U} (p : Quiver.Path.{succ u1, u2} U _inst_1 u v) (hzero : Eq.{1} Nat (Quiver.Path.length.{u2, succ u1} U _inst_1 u v p) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u1, u2} U _inst_1 v v) (Quiver.Path.cast.{u1, u2} U _inst_1 u v v v (Quiver.Path.eq_of_length_zero.{u2, succ u1} U _inst_1 u v p hzero) (rfl.{succ u2} U v) p) (Quiver.Path.nil.{succ u1, u2} U _inst_1 v)
but is expected to have type
  forall {U : Type.{u1}} [_inst_1 : Quiver.{succ u2, u1} U] {u : U} {v : U} (p : Quiver.Path.{succ u2, u1} U _inst_1 u v) (hzero : Eq.{1} Nat (Quiver.Path.length.{u1, succ u2} U _inst_1 u v p) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))), Eq.{max (succ u2) (succ u1)} (Quiver.Path.{succ u2, u1} U _inst_1 v v) (Quiver.Path.cast.{u2, u1} U _inst_1 u v v v (Quiver.Path.eq_of_length_zero.{succ u2, u1} U _inst_1 u v p hzero) (rfl.{succ u1} U v) p) (Quiver.Path.nil.{succ u2, u1} U _inst_1 v)
Case conversion may be inaccurate. Consider using '#align quiver.eq_nil_of_length_zero Quiver.eq_nil_of_length_zeroₓ'. -/
theorem eq_nil_of_length_zero {u v : U} (p : Path u v) (hzero : p.length = 0) :
    p.cast (eq_of_length_zero p hzero) rfl = Path.nil := by
  cases p <;> simpa only [Nat.succ_ne_zero, length_cons] using hzero
#align quiver.eq_nil_of_length_zero Quiver.eq_nil_of_length_zero

end Quiver


/-
Copyright (c) 2020 Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bhavik Mehta

! This file was ported from Lean 3 source module category_theory.limits.shapes.split_coequalizer
! leanprover-community/mathlib commit 024a4231815538ac739f52d08dd20a55da0d6b23
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.CategoryTheory.Limits.Shapes.Equalizers

/-!
# Split coequalizers

We define what it means for a triple of morphisms `f g : X ⟶ Y`, `π : Y ⟶ Z` to be a split
coequalizer: there is a section `s` of `π` and a section `t` of `g`, which additionally satisfy
`t ≫ f = π ≫ s`.

In addition, we show that every split coequalizer is a coequalizer
(`CategoryTheory.IsSplitCoequalizer.isCoequalizer`) and absolute
(`CategoryTheory.IsSplitCoequalizer.map`)

A pair `f g : X ⟶ Y` has a split coequalizer if there is a `Z` and `π : Y ⟶ Z` making `f,g,π` a
split coequalizer.
A pair `f g : X ⟶ Y` has a `G`-split coequalizer if `G f, G g` has a split coequalizer.

These definitions and constructions are useful in particular for the monadicity theorems.

## TODO

Dualise to split equalizers.
-/

namespace CategoryTheory

universe v v₂ u u₂

variable {C : Type u} [Category.{v} C]

variable {D : Type u₂} [Category.{v₂} D]

variable (G : C ⥤ D)

variable {X Y : C} (f g : X ⟶ Y)

/-- A split coequalizer diagram consists of morphisms

      f   π
    X ⇉ Y → Z
      g

satisfying `f ≫ π = g ≫ π` together with morphisms

      t   s
    X ← Y ← Z

satisfying `s ≫ π = 𝟙 Z`, `t ≫ g = 𝟙 Y` and `t ≫ f = π ≫ s`.

The name "coequalizer" is appropriate, since any split coequalizer is a coequalizer, see
`Category_theory.IsSplitCoequalizer.isCoequalizer`.
Split coequalizers are also absolute, since a functor preserves all the structure above.
-/
structure IsSplitCoequalizer {Z : C} (π : Y ⟶ Z) where
  /-- A map from the coequalizer to `Y` -/
  rightSection : Z ⟶ Y
  /-- A map in the opposite direction to `f` and `g` -/
  leftSection : Y ⟶ X
  /-- Composition of `π` with `f` and with `g` agree -/
  condition : f ≫ π = g ≫ π
  /-- `rightSection` splits `π` -/
  rightSection_π : rightSection ≫ π = 𝟙 Z
  /-- `leftSection` splits `g` -/
  leftSection_bottom : leftSection ≫ g = 𝟙 Y
  /-- `leftSection` composed with `f` is `pi` composed with `rightSection` -/
  leftSection_top : leftSection ≫ f = π ≫ rightSection
#align category_theory.is_split_coequalizer CategoryTheory.IsSplitCoequalizer
#align category_theory.is_split_coequalizer.right_section CategoryTheory.IsSplitCoequalizer.rightSection
#align category_theory.is_split_coequalizer.left_section CategoryTheory.IsSplitCoequalizer.leftSection
#align category_theory.is_split_coequalizer.right_section_π CategoryTheory.IsSplitCoequalizer.rightSection_π
#align category_theory.is_split_coequalizer.left_section_bottom CategoryTheory.IsSplitCoequalizer.leftSection_bottom
#align category_theory.is_split_coequalizer.left_section_top CategoryTheory.IsSplitCoequalizer.leftSection_top

instance {X : C} : Inhabited (IsSplitCoequalizer (𝟙 X) (𝟙 X) (𝟙 X)) where
  default := ⟨𝟙 X, 𝟙 X, rfl, Category.id_comp _, Category.id_comp _, rfl⟩

open IsSplitCoequalizer

attribute [reassoc] condition

attribute [reassoc (attr := simp)] rightSection_π leftSection_bottom leftSection_top

variable {f g}

/-- Split coequalizers are absolute: they are preserved by any functor. -/
@[simps]
def IsSplitCoequalizer.map {Z : C} {π : Y ⟶ Z} (q : IsSplitCoequalizer f g π) (F : C ⥤ D) :
    IsSplitCoequalizer (F.map f) (F.map g) (F.map π)
    where
  rightSection := F.map q.rightSection
  leftSection := F.map q.leftSection
  condition := by rw [← F.map_comp, q.condition, F.map_comp]
  rightSection_π := by rw [← F.map_comp, q.rightSection_π, F.map_id]
  leftSection_bottom := by rw [← F.map_comp, q.leftSection_bottom, F.map_id]
  leftSection_top := by rw [← F.map_comp, q.leftSection_top, F.map_comp]
#align category_theory.is_split_coequalizer.map CategoryTheory.IsSplitCoequalizer.map

section

open Limits

/-- A split coequalizer clearly induces a cofork. -/
@[simps! pt]
def IsSplitCoequalizer.asCofork {Z : C} {h : Y ⟶ Z} (t : IsSplitCoequalizer f g h) :
    Cofork f g := Cofork.ofπ h t.condition
#align category_theory.is_split_coequalizer.as_cofork CategoryTheory.IsSplitCoequalizer.asCofork

@[simp]
theorem IsSplitCoequalizer.asCofork_π {Z : C} {h : Y ⟶ Z} (t : IsSplitCoequalizer f g h) :
    t.asCofork.π = h := rfl
#align category_theory.is_split_coequalizer.as_cofork_π CategoryTheory.IsSplitCoequalizer.asCofork_π

/--
The cofork induced by a split coequalizer is a coequalizer, justifying the name. In some cases it
is more convenient to show a given cofork is a coequalizer by showing it is split.
-/
def IsSplitCoequalizer.isCoequalizer {Z : C} {h : Y ⟶ Z} (t : IsSplitCoequalizer f g h) :
    IsColimit t.asCofork :=
  Cofork.IsColimit.mk' _ fun s =>
    ⟨t.rightSection ≫ s.π, by
      dsimp
      rw [← t.leftSection_top_assoc, s.condition, t.leftSection_bottom_assoc], fun hm => by
      simp [← hm]⟩
#align category_theory.is_split_coequalizer.is_coequalizer CategoryTheory.IsSplitCoequalizer.isCoequalizer

end

variable (f g)

/--
The pair `f,g` is a split pair if there is a `h : Y ⟶ Z` so that `f, g, h` forms a split coequalizer
in `C`.
-/
class HasSplitCoequalizer : Prop where
  /-- There is some split coequalizer -/
  splittable : ∃ (Z : C)(h : Y ⟶ Z), Nonempty (IsSplitCoequalizer f g h)
#align category_theory.has_split_coequalizer CategoryTheory.HasSplitCoequalizer

/--
The pair `f,g` is a `G`-split pair if there is a `h : G Y ⟶ Z` so that `G f, G g, h` forms a split
coequalizer in `D`.
-/
abbrev Functor.IsSplitPair : Prop :=
  HasSplitCoequalizer (G.map f) (G.map g)
#align category_theory.functor.is_split_pair CategoryTheory.Functor.IsSplitPair

/-- Get the coequalizer object from the typeclass `IsSplitPair`. -/
noncomputable def HasSplitCoequalizer.coequalizerOfSplit [HasSplitCoequalizer f g] : C :=
  (@splittable _ _ _ _ f g).choose
#align category_theory.has_split_coequalizer.coequalizer_of_split CategoryTheory.HasSplitCoequalizer.coequalizerOfSplit

/-- Get the coequalizer morphism from the typeclass `IsSplitPair`. -/
noncomputable def HasSplitCoequalizer.coequalizerπ [HasSplitCoequalizer f g] :
    Y ⟶ HasSplitCoequalizer.coequalizerOfSplit f g :=
  (@splittable _ _ _ _ f g).choose_spec.choose
#align category_theory.has_split_coequalizer.coequalizer_π CategoryTheory.HasSplitCoequalizer.coequalizerπ

/-- The coequalizer morphism `coequalizeπ` gives a split coequalizer on `f,g`. -/
noncomputable def HasSplitCoequalizer.isSplitCoequalizer [HasSplitCoequalizer f g] :
    IsSplitCoequalizer f g (HasSplitCoequalizer.coequalizerπ f g) :=
  Classical.choice (@splittable _ _ _ _ f g).choose_spec.choose_spec
#align category_theory.has_split_coequalizer.is_split_coequalizer CategoryTheory.HasSplitCoequalizer.isSplitCoequalizer

/-- If `f, g` is split, then `G f, G g` is split. -/
instance map_is_split_pair [HasSplitCoequalizer f g] : HasSplitCoequalizer (G.map f) (G.map g)
    where splittable :=
    ⟨_, _, ⟨IsSplitCoequalizer.map (HasSplitCoequalizer.isSplitCoequalizer f g) _⟩⟩
#align category_theory.map_is_split_pair CategoryTheory.map_is_split_pair

namespace Limits

/-- If a pair has a split coequalizer, it has a coequalizer. -/
instance (priority := 1) hasCoequalizer_of_hasSplitCoequalizer [HasSplitCoequalizer f g] :
    HasCoequalizer f g :=
  HasColimit.mk ⟨_, (HasSplitCoequalizer.isSplitCoequalizer f g).isCoequalizer⟩
#align category_theory.limits.has_coequalizer_of_has_split_coequalizer CategoryTheory.Limits.hasCoequalizer_of_hasSplitCoequalizer

end Limits

end CategoryTheory


/-
Copyright (c) 2018 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import algebra.category.Mon.basic
import category_theory.endomorphism

/-!
# Category instances for group, add_group, comm_group, and add_comm_group.

We introduce the bundled categories:
* `Group`
* `AddGroup`
* `CommGroup`
* `AddCommGroup`
along with the relevant forgetful functors between them, and to the bundled monoid categories.
-/

universes u v

open category_theory

/-- The category of groups and group morphisms. -/
@[to_additive AddGroup]
def Group : Type (u+1) := bundled group

/-- The category of additive groups and group morphisms -/
add_decl_doc AddGroup

namespace Group

@[to_additive]
instance : bundled_hom.parent_projection group.to_monoid := ⟨⟩

attribute [derive [has_coe_to_sort, large_category, concrete_category]] Group AddGroup

/-- Construct a bundled `Group` from the underlying type and typeclass. -/
@[to_additive] def of (X : Type u) [group X] : Group := bundled.of X

/-- Construct a bundled `AddGroup` from the underlying type and typeclass. -/
add_decl_doc AddGroup.of

@[to_additive]
instance (G : Group) : group G := G.str

@[simp, to_additive] lemma coe_of (R : Type u) [group R] : (Group.of R : Type u) = R := rfl

@[to_additive]
instance : has_one Group := ⟨Group.of punit⟩

@[to_additive]
instance : inhabited Group := ⟨1⟩

@[to_additive]
instance one.unique : unique (1 : Group) :=
{ default := 1,
  uniq := λ a, begin cases a, refl, end }

@[simp, to_additive]
lemma one_apply (G H : Group) (g : G) : (1 : G ⟶ H) g = 1 := rfl

@[ext, to_additive]

-- should to_additive do this automatically?
attribute [ext] AddGroup.ext

@[to_additive has_forget_to_AddMon]
instance has_forget_to_Mon : has_forget₂ Group Mon := bundled_hom.forget₂ _ _

end Group

/-- The category of commutative groups and group morphisms. -/
@[to_additive AddCommGroup]
def CommGroup : Type (u+1) := bundled comm_group

/-- The category of additive commutative groups and group morphisms. -/
add_decl_doc AddCommGroup

/-- `Ab` is an abbreviation for `AddCommGroup`, for the sake of mathematicians' sanity. -/
abbreviation Ab := AddCommGroup

namespace CommGroup

@[to_additive]
instance : bundled_hom.parent_projection comm_group.to_group := ⟨⟩

attribute [derive [has_coe_to_sort, large_category, concrete_category]] CommGroup AddCommGroup

/-- Construct a bundled `CommGroup` from the underlying type and typeclass. -/
@[to_additive] def of (G : Type u) [comm_group G] : CommGroup := bundled.of G

/-- Construct a bundled `AddCommGroup` from the underlying type and typeclass. -/
add_decl_doc AddCommGroup.of

@[to_additive]
instance comm_group_instance (G : CommGroup) : comm_group G := G.str

@[simp, to_additive] lemma coe_of (R : Type u) [comm_group R] : (CommGroup.of R : Type u) = R := rfl

@[to_additive] instance : has_one CommGroup := ⟨CommGroup.of punit⟩

@[to_additive] instance : inhabited CommGroup := ⟨1⟩

@[to_additive]
instance one.unique : unique (1 : CommGroup) :=
{ default := 1,
  uniq := λ a, begin cases a, refl, end }

@[simp, to_additive]
lemma one_apply (G H : CommGroup) (g : G) : (1 : G ⟶ H) g = 1 := rfl

@[to_additive,ext]
lemma ext (G H : CommGroup) (f₁ f₂ : G ⟶ H) (w : ∀ x, f₁ x = f₂ x) : f₁ = f₂ :=
by { ext1, apply w }

attribute [ext] AddCommGroup.ext

@[to_additive has_forget_to_AddGroup]
instance has_forget_to_Group : has_forget₂ CommGroup Group := bundled_hom.forget₂ _ _

@[to_additive has_forget_to_AddCommMon]
instance has_forget_to_CommMon : has_forget₂ CommGroup CommMon :=
induced_category.has_forget₂ (λ G : CommGroup, CommMon.of G)

end CommGroup

-- This example verifies an improvement possible in Lean 3.8.
-- Before that, to have `monoid_hom.map_map` usable by `simp` here,
-- we had to mark all the concrete category `has_coe_to_sort` instances reducible.
-- Now, it just works.
@[to_additive]
example {R S : CommGroup} (i : R ⟶ S) (r : R) (h : r = 1) : i r = 1 :=
by simp [h]

namespace AddCommGroup

/-- Any element of an abelian group gives a unique morphism from `ℤ` sending
`1` to that element. -/
-- Note that because `ℤ : Type 0`, this forces `G : AddCommGroup.{0}`,
-- so we write this explicitly to be clear.
-- TODO generalize this, requiring a `ulift_instances.lean` file
def as_hom {G : AddCommGroup.{0}} (g : G) : (AddCommGroup.of ℤ) ⟶ G :=
gmultiples_hom G g

@[simp]
lemma as_hom_apply {G : AddCommGroup.{0}} (g : G) (i : ℤ) : (as_hom g) i = i • g := rfl

lemma as_hom_injective {G : AddCommGroup.{0}} : function.injective (@as_hom G) :=
λ h k w, by convert congr_arg (λ k : (AddCommGroup.of ℤ) ⟶ G, (k : ℤ → G) (1 : ℤ)) w; simp

@[ext]
lemma int_hom_ext
  {G : AddCommGroup.{0}} (f g : (AddCommGroup.of ℤ) ⟶ G) (w : f (1 : ℤ) = g (1 : ℤ)) : f = g :=
add_monoid_hom.ext_int w

-- TODO: this argument should be generalised to the situation where
-- the forgetful functor is representable.
lemma injective_of_mono {G H : AddCommGroup.{0}} (f : G ⟶ H) [mono f] : function.injective f :=
λ g₁ g₂ h,
begin
  have t0 : as_hom g₁ ≫ f = as_hom g₂ ≫ f :=
  begin
    ext,
    simpa [as_hom_apply] using h,
  end,
  have t1 : as_hom g₁ = as_hom g₂ := (cancel_mono _).1 t0,
  apply as_hom_injective t1,
end

end AddCommGroup

variables {X Y : Type u}

/-- Build an isomorphism in the category `Group` from a `mul_equiv` between `group`s. -/
@[to_additive add_equiv.to_AddGroup_iso, simps]
def mul_equiv.to_Group_iso [group X] [group Y] (e : X ≃* Y) : Group.of X ≅ Group.of Y :=
{ hom := e.to_monoid_hom,
  inv := e.symm.to_monoid_hom }

/-- Build an isomorphism in the category `AddGroup` from an `add_equiv` between `add_group`s. -/
add_decl_doc add_equiv.to_AddGroup_iso

/-- Build an isomorphism in the category `CommGroup` from a `mul_equiv` between `comm_group`s. -/
@[to_additive add_equiv.to_AddCommGroup_iso, simps]
def mul_equiv.to_CommGroup_iso [comm_group X] [comm_group Y] (e : X ≃* Y) :
  CommGroup.of X ≅ CommGroup.of Y :=
{ hom := e.to_monoid_hom,
  inv := e.symm.to_monoid_hom }

/-- Build an isomorphism in the category `AddCommGroup` from a `add_equiv` between
`add_comm_group`s. -/
add_decl_doc add_equiv.to_AddCommGroup_iso

namespace category_theory.iso

/-- Build a `mul_equiv` from an isomorphism in the category `Group`. -/
@[to_additive AddGroup_iso_to_add_equiv "Build an `add_equiv` from an isomorphism in the category
`AddGroup`.", simps]
def Group_iso_to_mul_equiv {X Y : Group} (i : X ≅ Y) : X ≃* Y :=
i.hom.to_mul_equiv i.inv i.hom_inv_id i.inv_hom_id

/-- Build a `mul_equiv` from an isomorphism in the category `CommGroup`. -/
@[to_additive AddCommGroup_iso_to_add_equiv "Build an `add_equiv` from an isomorphism
in the category `AddCommGroup`.", simps]
def CommGroup_iso_to_mul_equiv {X Y : CommGroup} (i : X ≅ Y) : X ≃* Y :=
i.hom.to_mul_equiv i.inv i.hom_inv_id i.inv_hom_id

end category_theory.iso

/-- multiplicative equivalences between `group`s are the same as (isomorphic to) isomorphisms
in `Group` -/
@[to_additive add_equiv_iso_AddGroup_iso "additive equivalences between `add_group`s are the same
as (isomorphic to) isomorphisms in `AddGroup`"]
def mul_equiv_iso_Group_iso {X Y : Type u} [group X] [group Y] :
  (X ≃* Y) ≅ (Group.of X ≅ Group.of Y) :=
{ hom := λ e, e.to_Group_iso,
  inv := λ i, i.Group_iso_to_mul_equiv, }

/-- multiplicative equivalences between `comm_group`s are the same as (isomorphic to) isomorphisms
in `CommGroup` -/
@[to_additive add_equiv_iso_AddCommGroup_iso "additive equivalences between `add_comm_group`s are
the same as (isomorphic to) isomorphisms in `AddCommGroup`"]
def mul_equiv_iso_CommGroup_iso {X Y : Type u} [comm_group X] [comm_group Y] :
  (X ≃* Y) ≅ (CommGroup.of X ≅ CommGroup.of Y) :=
{ hom := λ e, e.to_CommGroup_iso,
  inv := λ i, i.CommGroup_iso_to_mul_equiv, }

namespace category_theory.Aut

/-- The (bundled) group of automorphisms of a type is isomorphic to the (bundled) group
of permutations. -/
def iso_perm {α : Type u} : Group.of (Aut α) ≅ Group.of (equiv.perm α) :=
{ hom := ⟨λ g, g.to_equiv, (by tidy), (by tidy)⟩,
  inv := ⟨λ g, g.to_iso, (by tidy), (by tidy)⟩ }

/-- The (unbundled) group of automorphisms of a type is `mul_equiv` to the (unbundled) group
of permutations. -/
def mul_equiv_perm {α : Type u} : Aut α ≃* equiv.perm α :=
iso_perm.Group_iso_to_mul_equiv

end category_theory.Aut

@[to_additive]
instance Group.forget_reflects_isos : reflects_isomorphisms (forget Group.{u}) :=
{ reflects := λ X Y f _,
  begin
    resetI,
    let i := as_iso ((forget Group).map f),
    let e : X ≃* Y := { ..f, ..i.to_equiv },
    exact ⟨(is_iso.of_iso e.to_Group_iso).1⟩,
  end }

@[to_additive]
instance CommGroup.forget_reflects_isos : reflects_isomorphisms (forget CommGroup.{u}) :=
{ reflects := λ X Y f _,
  begin
    resetI,
    let i := as_iso ((forget CommGroup).map f),
    let e : X ≃* Y := { ..f, ..i.to_equiv },
    exact ⟨(is_iso.of_iso e.to_CommGroup_iso).1⟩,
  end }


/-
Copyright (c) 2018 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Reid Barton
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.shapes.images
import Mathlib.category_theory.filtered
import Mathlib.tactic.equiv_rw
import Mathlib.PostPort

universes u

namespace Mathlib

namespace category_theory.limits.types

/--
(internal implementation) the limit cone of a functor,
implemented as flat sections of a pi type
-/
def limit_cone {J : Type u} [small_category J] (F : J ⥤ Type u) : cone F :=
  cone.mk (↥(functor.sections F))
    (nat_trans.mk
      fun (j : J) (u : functor.obj (functor.obj (functor.const J) ↥(functor.sections F)) j) => subtype.val u j)

/-- (internal implementation) the fact that the proposed limit cone is the limit -/
def limit_cone_is_limit {J : Type u} [small_category J] (F : J ⥤ Type u) : is_limit (limit_cone F) :=
  is_limit.mk fun (s : cone F) (v : cone.X s) => { val := fun (j : J) => nat_trans.app (cone.π s) j v, property := sorry }

/--
The category of types has all limits.

See https://stacks.math.columbia.edu/tag/002U.
-/
protected instance sort.category_theory.limits.has_limits : has_limits (Type u) :=
  has_limits.mk
    fun (J : Type u) (𝒥 : small_category J) =>
      has_limits_of_shape.mk fun (F : J ⥤ Type u) => has_limit.mk (limit_cone.mk (limit_cone F) (limit_cone_is_limit F))

/--
The equivalence between a limiting cone of `F` in `Type u` and the "concrete" definition as the
sections of `F`.
-/
def is_limit_equiv_sections {J : Type u} [small_category J] {F : J ⥤ Type u} {c : cone F} (t : is_limit c) : cone.X c ≃ ↥(functor.sections F) :=
  iso.to_equiv (is_limit.cone_point_unique_up_to_iso t (limit_cone_is_limit F))

@[simp] theorem is_limit_equiv_sections_apply {J : Type u} [small_category J] {F : J ⥤ Type u} {c : cone F} (t : is_limit c) (j : J) (x : cone.X c) : coe (coe_fn (is_limit_equiv_sections t) x) j = nat_trans.app (cone.π c) j x :=
  rfl

@[simp] theorem is_limit_equiv_sections_symm_apply {J : Type u} [small_category J] {F : J ⥤ Type u} {c : cone F} (t : is_limit c) (x : ↥(functor.sections F)) (j : J) : nat_trans.app (cone.π c) j (coe_fn (equiv.symm (is_limit_equiv_sections t)) x) = coe x j := sorry

/--
The equivalence between the abstract limit of `F` in `Type u`
and the "concrete" definition as the sections of `F`.
-/
def limit_equiv_sections {J : Type u} [small_category J] (F : J ⥤ Type u) : limit F ≃ ↥(functor.sections F) :=
  is_limit_equiv_sections (limit.is_limit F)

@[simp] theorem limit_equiv_sections_apply {J : Type u} [small_category J] (F : J ⥤ Type u) (x : limit F) (j : J) : coe (coe_fn (limit_equiv_sections F) x) j = limit.π F j x :=
  rfl

@[simp] theorem limit_equiv_sections_symm_apply {J : Type u} [small_category J] (F : J ⥤ Type u) (x : ↥(functor.sections F)) (j : J) : limit.π F j (coe_fn (equiv.symm (limit_equiv_sections F)) x) = coe x j :=
  is_limit_equiv_sections_symm_apply (limit.is_limit F) x j

/--
Construct a term of `limit F : Type u` from a family of terms `x : Π j, F.obj j`
which are "coherent": `∀ (j j') (f : j ⟶ j'), F.map f (x j) = x j'`.
-/
def limit.mk {J : Type u} [small_category J] (F : J ⥤ Type u) (x : (j : J) → functor.obj F j) (h : ∀ (j j' : J) (f : j ⟶ j'), functor.map F f (x j) = x j') : limit F :=
  coe_fn (equiv.symm (limit_equiv_sections F)) { val := x, property := h }

@[simp] theorem limit.π_mk {J : Type u} [small_category J] (F : J ⥤ Type u) (x : (j : J) → functor.obj F j) (h : ∀ (j j' : J) (f : j ⟶ j'), functor.map F f (x j) = x j') (j : J) : limit.π F j (limit.mk F x h) = x j := sorry

-- PROJECT: prove this for concrete categories where the forgetful functor preserves limits

theorem limit_ext {J : Type u} [small_category J] (F : J ⥤ Type u) (x : limit F) (y : limit F) (w : ∀ (j : J), limit.π F j x = limit.π F j y) : x = y := sorry

theorem limit_ext_iff {J : Type u} [small_category J] (F : J ⥤ Type u) (x : limit F) (y : limit F) : x = y ↔ ∀ (j : J), limit.π F j x = limit.π F j y :=
  { mp := fun (t : x = y) (_x : J) => t ▸ rfl, mpr := limit_ext F x y }

-- TODO: are there other limits lemmas that should have `_apply` versions?

-- Can we generate these like with `@[reassoc]`?

-- PROJECT: prove these for any concrete category where the forgetful functor preserves limits?

@[simp] theorem limit.w_apply {J : Type u} [small_category J] {F : J ⥤ Type u} {j : J} {j' : J} {x : limit F} (f : j ⟶ j') : functor.map F f (limit.π F j x) = limit.π F j' x :=
  congr_fun (limit.w F f) x

@[simp] theorem limit.lift_π_apply {J : Type u} [small_category J] (F : J ⥤ Type u) (s : cone F) (j : J) (x : cone.X s) : limit.π F j (limit.lift F s x) = nat_trans.app (cone.π s) j x :=
  congr_fun (limit.lift_π s j) x

@[simp] theorem limit.map_π_apply {J : Type u} [small_category J] {F : J ⥤ Type u} {G : J ⥤ Type u} (α : F ⟶ G) (j : J) (x : limit F) : limit.π G j (lim_map α x) = nat_trans.app α j (limit.π F j x) :=
  congr_fun (lim_map_π α j) x

/--
The relation defining the quotient type which implements the colimit of a functor `F : J ⥤ Type u`.
See `category_theory.limits.types.quot`.
-/
def quot.rel {J : Type u} [small_category J] (F : J ⥤ Type u) : (sigma fun (j : J) => functor.obj F j) → (sigma fun (j : J) => functor.obj F j) → Prop :=
  fun (p p' : sigma fun (j : J) => functor.obj F j) =>
    ∃ (f : sigma.fst p ⟶ sigma.fst p'), sigma.snd p' = functor.map F f (sigma.snd p)

/--
A quotient type implementing the colimit of a functor `F : J ⥤ Type u`,
as pairs `⟨j, x⟩` where `x : F.obj j`, modulo the equivalence relation generated by
`⟨j, x⟩ ~ ⟨j', x'⟩` whenever there is a morphism `f : j ⟶ j'` so `F.map f x = x'`.
-/
def quot {J : Type u} [small_category J] (F : J ⥤ Type u) :=
  Quot sorry

/--
(internal implementation) the colimit cocone of a functor,
implemented as a quotient of a sigma type
-/
def colimit_cocone {J : Type u} [small_category J] (F : J ⥤ Type u) : cocone F :=
  cocone.mk (quot F) (nat_trans.mk fun (j : J) (x : functor.obj F j) => Quot.mk (quot.rel F) (sigma.mk j x))

/-- (internal implementation) the fact that the proposed colimit cocone is the colimit -/
def colimit_cocone_is_colimit {J : Type u} [small_category J] (F : J ⥤ Type u) : is_colimit (colimit_cocone F) :=
  is_colimit.mk
    fun (s : cocone F) =>
      Quot.lift (fun (p : sigma fun (j : J) => functor.obj F j) => nat_trans.app (cocone.ι s) (sigma.fst p) (sigma.snd p))
        sorry

/--
The category of types has all colimits.

See https://stacks.math.columbia.edu/tag/002U.
-/
protected instance sort.category_theory.limits.has_colimits : has_colimits (Type u) :=
  has_colimits.mk
    fun (J : Type u) (𝒥 : small_category J) =>
      has_colimits_of_shape.mk
        fun (F : J ⥤ Type u) => has_colimit.mk (colimit_cocone.mk (colimit_cocone F) (colimit_cocone_is_colimit F))

/--
The equivalence between the abstract colimit of `F` in `Type u`
and the "concrete" definition as a quotient.
-/
def colimit_equiv_quot {J : Type u} [small_category J] (F : J ⥤ Type u) : colimit F ≃ quot F :=
  iso.to_equiv (is_colimit.cocone_point_unique_up_to_iso (colimit.is_colimit F) (colimit_cocone_is_colimit F))

@[simp] theorem colimit_equiv_quot_symm_apply {J : Type u} [small_category J] (F : J ⥤ Type u) (j : J) (x : functor.obj F j) : coe_fn (equiv.symm (colimit_equiv_quot F)) (Quot.mk (quot.rel F) (sigma.mk j x)) = colimit.ι F j x :=
  rfl

@[simp] theorem colimit_equiv_quot_apply {J : Type u} [small_category J] (F : J ⥤ Type u) (j : J) (x : functor.obj F j) : coe_fn (colimit_equiv_quot F) (colimit.ι F j x) = Quot.mk (quot.rel F) (sigma.mk j x) := sorry

@[simp] theorem colimit.w_apply {J : Type u} [small_category J] {F : J ⥤ Type u} {j : J} {j' : J} {x : functor.obj F j} (f : j ⟶ j') : colimit.ι F j' (functor.map F f x) = colimit.ι F j x :=
  congr_fun (colimit.w F f) x

@[simp] theorem colimit.ι_desc_apply {J : Type u} [small_category J] (F : J ⥤ Type u) (s : cocone F) (j : J) (x : functor.obj F j) : colimit.desc F s (colimit.ι F j x) = nat_trans.app (cocone.ι s) j x :=
  congr_fun (colimit.ι_desc s j) x

@[simp] theorem colimit.ι_map_apply {J : Type u} [small_category J] {F : J ⥤ Type u} {G : J ⥤ Type u} (α : F ⟶ G) (j : J) (x : functor.obj F j) : functor.map colim α (colimit.ι F j x) = colimit.ι G j (nat_trans.app α j x) :=
  congr_fun (colimit.ι_map α j) x

theorem colimit_sound {J : Type u} [small_category J] {F : J ⥤ Type u} {j : J} {j' : J} {x : functor.obj F j} {x' : functor.obj F j'} (f : j ⟶ j') (w : functor.map F f x = x') : colimit.ι F j x = colimit.ι F j' x' := sorry

theorem colimit_sound' {J : Type u} [small_category J] {F : J ⥤ Type u} {j : J} {j' : J} {x : functor.obj F j} {x' : functor.obj F j'} {j'' : J} (f : j ⟶ j'') (f' : j' ⟶ j'') (w : functor.map F f x = functor.map F f' x') : colimit.ι F j x = colimit.ι F j' x' := sorry

theorem colimit_eq {J : Type u} [small_category J] {F : J ⥤ Type u} {j : J} {j' : J} {x : functor.obj F j} {x' : functor.obj F j'} (w : colimit.ι F j x = colimit.ι F j' x') : eqv_gen (quot.rel F) (sigma.mk j x) (sigma.mk j' x') := sorry

theorem jointly_surjective {J : Type u} [small_category J] (F : J ⥤ Type u) {t : cocone F} (h : is_colimit t) (x : cocone.X t) : ∃ (j : J), ∃ (y : functor.obj F j), nat_trans.app (cocone.ι t) j y = x := sorry

/-- A variant of `jointly_surjective` for `x : colimit F`. -/
theorem jointly_surjective' {J : Type u} [small_category J] {F : J ⥤ Type u} (x : colimit F) : ∃ (j : J), ∃ (y : functor.obj F j), colimit.ι F j y = x :=
  jointly_surjective F (colimit.is_colimit F) x

namespace filtered_colimit

/- For filtered colimits of types, we can give an explicit description
  of the equivalence relation generated by the relation used to form
  the colimit.  -/

/--
An alternative relation on `Σ j, F.obj j`,
which generates the same equivalence relation as we use to define the colimit in `Type` above,
but that is more convenient when working with filtered colimits.

Elements in `F.obj j` and `F.obj j'` are equivalent if there is some `k : J` to the right
where their images are equal.
-/
protected def r {J : Type u} [small_category J] (F : J ⥤ Type u) (x : sigma fun (j : J) => functor.obj F j) (y : sigma fun (j : J) => functor.obj F j) :=
  ∃ (k : J),
    ∃ (f : sigma.fst x ⟶ k), ∃ (g : sigma.fst y ⟶ k), functor.map F f (sigma.snd x) = functor.map F g (sigma.snd y)

protected theorem r_ge {J : Type u} [small_category J] (F : J ⥤ Type u) (x : sigma fun (j : J) => functor.obj F j) (y : sigma fun (j : J) => functor.obj F j) : (∃ (f : sigma.fst x ⟶ sigma.fst y), sigma.snd y = functor.map F f (sigma.snd x)) → filtered_colimit.r F x y := sorry

/-- Recognizing filtered colimits of types. -/
def is_colimit_of {J : Type u} [small_category J] (F : J ⥤ Type u) (t : cocone F) (hsurj : ∀ (x : cocone.X t), ∃ (i : J), ∃ (xi : functor.obj F i), x = nat_trans.app (cocone.ι t) i xi) (hinj : ∀ (i j : J) (xi : functor.obj F i) (xj : functor.obj F j),
  nat_trans.app (cocone.ι t) i xi = nat_trans.app (cocone.ι t) j xj →
    ∃ (k : J), ∃ (f : i ⟶ k), ∃ (g : j ⟶ k), functor.map F f xi = functor.map F g xj) : is_colimit t :=
  is_colimit.of_iso_colimit (colimit.is_colimit F)
    (cocones.ext (equiv.to_iso (equiv.of_bijective (colimit.desc F t) sorry)) sorry)

-- Strategy: Prove that the map from "the" colimit of F (defined above) to t.X

-- is a bijection.

protected theorem r_equiv {J : Type u} [small_category J] (F : J ⥤ Type u) [is_filtered_or_empty J] : equivalence (filtered_colimit.r F) := sorry

protected theorem r_eq {J : Type u} [small_category J] (F : J ⥤ Type u) [is_filtered_or_empty J] : filtered_colimit.r F =
  eqv_gen
    fun (x y : sigma fun (j : J) => functor.obj F j) =>
      ∃ (f : sigma.fst x ⟶ sigma.fst y), sigma.snd y = functor.map F f (sigma.snd x) := sorry

theorem colimit_eq_iff_aux {J : Type u} [small_category J] (F : J ⥤ Type u) [is_filtered_or_empty J] {i : J} {j : J} {xi : functor.obj F i} {xj : functor.obj F j} : nat_trans.app (cocone.ι (colimit_cocone F)) i xi = nat_trans.app (cocone.ι (colimit_cocone F)) j xj ↔
  ∃ (k : J), ∃ (f : i ⟶ k), ∃ (g : j ⟶ k), functor.map F f xi = functor.map F g xj := sorry

theorem is_colimit_eq_iff {J : Type u} [small_category J] (F : J ⥤ Type u) {t : cocone F} [is_filtered_or_empty J] (ht : is_colimit t) {i : J} {j : J} {xi : functor.obj F i} {xj : functor.obj F j} : nat_trans.app (cocone.ι t) i xi = nat_trans.app (cocone.ι t) j xj ↔
  ∃ (k : J), ∃ (f : i ⟶ k), ∃ (g : j ⟶ k), functor.map F f xi = functor.map F g xj := sorry

theorem colimit_eq_iff {J : Type u} [small_category J] (F : J ⥤ Type u) [is_filtered_or_empty J] {i : J} {j : J} {xi : functor.obj F i} {xj : functor.obj F j} : colimit.ι F i xi = colimit.ι F j xj ↔ ∃ (k : J), ∃ (f : i ⟶ k), ∃ (g : j ⟶ k), functor.map F f xi = functor.map F g xj :=
  is_colimit_eq_iff F (colimit.is_colimit F)

end filtered_colimit

/-- the image of a morphism in Type is just `set.range f` -/
def image {α : Type u} {β : Type u} (f : α ⟶ β) :=
  ↥(set.range f)

protected instance image.inhabited {α : Type u} {β : Type u} (f : α ⟶ β) [Inhabited α] : Inhabited (image f) :=
  { default := { val := f Inhabited.default, property := sorry } }

/-- the inclusion of `image f` into the target -/
def image.ι {α : Type u} {β : Type u} (f : α ⟶ β) : image f ⟶ β :=
  subtype.val

protected instance image.ι.category_theory.mono {α : Type u} {β : Type u} (f : α ⟶ β) : mono (image.ι f) :=
  iff.mpr (mono_iff_injective (image.ι f)) subtype.val_injective

/-- the universal property for the image factorisation -/
def image.lift {α : Type u} {β : Type u} {f : α ⟶ β} (F' : mono_factorisation f) : image f ⟶ mono_factorisation.I F' :=
  fun (x : image f) =>
    mono_factorisation.e F'
      (subtype.val (classical.indefinite_description (fun (x_1 : α) => f x_1 = subtype.val x) sorry))

theorem image.lift_fac {α : Type u} {β : Type u} {f : α ⟶ β} (F' : mono_factorisation f) : image.lift F' ≫ mono_factorisation.m F' = image.ι f := sorry

/-- the factorisation of any morphism in Type through a mono. -/
def mono_factorisation {α : Type u} {β : Type u} (f : α ⟶ β) : mono_factorisation f :=
  mono_factorisation.mk (image f) (image.ι f) (set.range_factorization f)

/-- the facorisation through a mono has the universal property of the image. -/
def is_image {α : Type u} {β : Type u} (f : α ⟶ β) : is_image (mono_factorisation f) :=
  is_image.mk image.lift

protected instance category_theory.limits.has_image {α : Type u} {β : Type u} (f : α ⟶ β) : has_image f :=
  has_image.mk (image_factorisation.mk (mono_factorisation f) (is_image f))

protected instance sort.category_theory.limits.has_images : has_images (Type u) :=
  has_images.mk sorry

protected instance sort.category_theory.limits.has_image_maps : has_image_maps (Type u) :=
  has_image_maps.mk sorry


-- Copyright © 2019 François G. Dorais. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.

import .basic
import .relation

@[derive decidable_eq]
inductive {u} index {α : Type u} : list α → Type u
| head (x : α) (xs : list α) : index (x :: xs)
| tail (x : α) (xs : list α) : index xs → index (x :: xs)

namespace index
variable {α : Type*}

@[reducible]
definition val : Π {xs : list α}, index xs → α
| _ (index.head x _) := x
| _ (index.tail _ _ i) := val i

@[simp] theorem val_head (x : α) (xs : list α) : val (index.head x xs) = x := rfl

@[simp] theorem val_tail (x : α) (xs : list α) (i : index xs) : val (index.tail x xs i) = val i := rfl

definition nil_elim {C : Sort*} : Π (i : index (@list.nil α)), C .

@[simp] theorem val_eq_rec : Π {xs ys : list α} (h : xs = ys) (i : index xs), val (eq.rec_on h i : index ys) = i.val
| _ _ rfl _ := rfl

section map
variables {β : Type*} (f : α → β)

definition map : Π {xs : list α}, index xs → index (xs.map f)
| _ (index.head _ _) := index.head _ _
| _ (index.tail _ _ i) := index.tail _ _ (map i)

@[simp] theorem map_head (x : α) (xs : list α) : map f (index.head x xs) = index.head (f x) (xs.map f) := rfl

@[simp] theorem map_tail (x : α) (xs : list α) (i : index xs) : map f (index.tail x xs i) = index.tail (f x) (xs.map f) (map f i) := rfl

@[simp] theorem val_map : ∀ {xs : list α} (i : index xs), (i.map f).val = f i.val
| _ (index.head _ _) := rfl
| _ (index.tail _ _ i) := val_map i

definition unmap : Π {xs : list α}, index (xs.map f) → index xs
| (_::_) (index.head _ _) := index.head _ _
| (_::_) (index.tail _ _ i) := index.tail _ _ (unmap i)

@[simp] theorem map_unmap : ∀ {xs : list α} (i : index (xs.map f)), map f (unmap f i) = i
| (_::_) (index.head _ _) := rfl
| (_::_) (index.tail _ _ i) := congr_arg (index.tail _ _) (map_unmap i)

@[simp] theorem unmap_map : ∀ {xs : list α} (i : index xs), unmap f (map f i) = i
| _ (index.head _ _) := rfl
| _ (index.tail _ _ i) := congr_arg (index.tail _ _) (unmap_map i)

end map

section append

definition append_left : Π (xs ys : list α), index xs → index (xs ++ ys)
| (x::xs) ys (index.head _ _) := index.head x (xs ++ ys)
| (x::xs) ys (index.tail _ _ i) := index.tail x (xs ++ ys) (append_left xs ys i)

@[simp] theorem append_left_val : ∀ (xs ys : list α) (i : index xs), (append_left xs ys i).val = i.val
| (x::xs) ys (index.head _ _) := rfl
| (x::xs) ys (index.tail _ _ i) := append_left_val xs ys i

definition append_right : Π (xs ys : list α), index ys → index (xs ++ ys)
| [] ys i := i
| (x::xs) ys i := index.tail x (xs ++ ys) (append_right xs ys i)

@[simp] theorem append_right_val : ∀ (xs ys : list α) (i : index ys), (append_right xs ys i).val = i.val
| [] ys _ := rfl
| (x::xs) ys i := append_right_val xs ys i

end append

section join

definition join_map : Π (xss : list (list α)) (i : index xss), index i.val → index xss.join
| (xs::xss) (index.head _ _) j := append_left xs xss.join j
| (xs::xss) (index.tail _ _ i) j := append_right xs xss.join $ join_map xss i j

@[simp] theorem join_map_val : ∀ (xss : list (list α)) (i : index xss) (j : index i.val), (join_map xss i j).val = j.val
| (xs::xss) (index.head _ _) j := append_left_val xs xss.join j
| (xs::xss) (index.tail _ _ i) j := eq.trans (append_right_val xs xss.join (join_map xss i j)) (join_map_val xss i j)

end join

definition iota : Π (xs : list α), list (index xs)
| [] := []
| (x :: xs) := (index.head x xs) :: list.map (index.tail x xs) (iota xs)

definition iota_index : Π {xs : list α}, index xs → index (iota xs)
| _ (index.head _ _) := index.head _ _
| _ (index.tail _ _ i) := index.tail _ _ (map _ $ iota_index i)

@[simp] theorem iota_index_val : Π {xs : list α} (i : index xs), (iota_index i).val = i
| _ (index.head _ _) := rfl
| _ (index.tail x xs i) := by rw [iota_index, val_tail, val_map, iota_index_val i]

definition to_fin : Π {xs : list α}, index xs → fin xs.length
| _ (index.head x xs) := eq.rec_on (eq.symm $ list.length_cons x xs) 0
| _ (index.tail x xs i) := eq.rec_on (eq.symm $ list.length_cons x xs) (fin.succ $ to_fin i)

definition of_fin : Π {xs : list α}, fin xs.length → index xs
| [] ⟨i, hi⟩ := absurd hi (nat.not_lt_zero i)
| (x :: xs) ⟨0, _⟩ := index.head x xs
| (x :: xs) ⟨i+1, hi⟩ := index.tail x xs (of_fin ⟨i, nat.lt_of_succ_lt_succ hi⟩)

abbreviation to_nat {xs : list α} (i : index xs) : nat := (to_fin i).val

theorem to_nat_lt_lenth {xs : list α} (i : index xs) : i.to_nat < xs.length := (to_fin i).is_lt

definition of_nat {xs : list α} (i : nat) : option (index xs) :=
if hi : i < xs.length then some (of_fin ⟨i, hi⟩) else none

abbreviation of_nat_lt_length {xs : list α} (i : nat) : i < xs.length → index xs := λ hi, of_fin ⟨i, hi⟩

abbreviation dtup {xs : list α} (β : index xs → Sort*) := Π i, β i

namespace dtup

section nil
variable {β : index (@list.nil α) → Sort*}

definition nil : Π i, β i .

@[simp] theorem eq_nil (t : Π i, β i) : t = nil := funext $ λ i, nil i

theorem eq_of_nil (t₁ t₂ : Π i, β i) : t₁ = t₂ := by rw [dtup.eq_nil t₁, dtup.eq_nil t₂]

end nil

section cons
variables {x : α} {xs : list α} {β : index (x :: xs) → Sort*}

abbreviation head : (Π i, β i) → β (index.head x xs) := λ t, t (index.head x xs)

abbreviation tail : (Π i, β i) → (Π i, β (index.tail x xs i)) := λ t i, t (index.tail x xs i)

definition cons : head β → (Π i, tail β i) → (Π i, β i)
| h _ (index.head _ _) := h
| _ t (index.tail _ _ i) := t i

@[simp] theorem cons_head_tail (t : Π i, β i) : cons (head t) (tail t) = t :=
funext $ λ i, match i with
| (index.head _ _) := rfl
| (index.tail _ _ i) := rfl
end

variables (h : head β) (t : Π i, tail β i)

@[simp] theorem cons_of_index_head : cons h t (index.head x xs) = h := rfl

@[simp] theorem cons_of_index_tail (i : index xs) : cons h t (index.tail x xs i) = t i := rfl

@[simp] theorem head_cons : head (cons h t) = h := rfl

@[simp] theorem tail_cons : tail (cons h t) = t := rfl

end cons

section enum
variables {xs : list α} {β : index xs → Type*} (t : Π i, β i)

abbreviation enum : list (sigma β) := list.map (λ i, ⟨i, t i⟩) (iota xs)

abbreviation enum_index : index xs → index (enum t) := λ i, map _ (iota_index i)

@[simp] theorem enum_index_val (i : index xs) : (enum_index t i).val = ⟨i, t i⟩ :=
by rw [val_map, iota_index_val]

end enum

abbreviation to_list {xs : list α} {β : Type*} (t : index xs → β) : list β :=
list.map (λ (z : sigma (λ _, β)), sigma.snd z) (enum t)

abbreviation to_list_index {xs : list α} {β : Type*} (t : index xs → β) : index xs → index (to_list t) :=
λ i, index.map (λ (z : sigma (λ _, β)), sigma.snd z) (enum_index t i)

@[simp] theorem to_list_index_val {xs : list α} {β : Type*} (t : index xs → β) (i : index xs) : (to_list_index t i).val = t i :=
by rw [val_map, enum_index_val]

section fold

definition foldl : Π {xs : list α} {β : index xs → Sort*} {γ : Sort*} (f : Π {{i}}, γ → β i → γ), γ → (Π i, β i) → γ
| [] _ _ _ z _ := z
| (_::_) _ _ f z t := f (foldl (tail f) z (tail t)) (head t)

definition foldr : Π {xs : list α} {β : index xs → Sort*} {γ : Sort*} (f : Π {{i}}, β i → γ → γ), γ → (Π i, β i) → γ
| [] _ _ _ z _ := z
| (_::_) _ _ f z t := f (head t) (foldr (tail f) z (tail t))

@[simp] theorem foldl_nil {β : index (@list.nil α) → Sort*} {γ : Sort*} (f : Π {{i}}, γ → β i → γ) (z : γ) :
foldl f z nil = z := rfl

@[simp] theorem foldr_nil {β : index (@list.nil α) → Sort*} {γ : Sort*} (f : Π {{i}}, β i → γ → γ) (z : γ) :
foldr f z nil = z := rfl

variables {x : α} {xs : list α} {β : index (x :: xs) → Sort*} {γ : Sort*}

@[simp] theorem foldl_cons (f : Π {{i}}, γ → β i → γ) (z : γ) (h : head β) (t : Π i, tail β i) :
foldl f z (cons h t) = f (foldl (tail f) z t) h := rfl

@[simp] theorem foldr_cons (f : Π {{i}}, β i → γ → γ) (z : γ) (h : head β) (t : Π i, tail β i) :
foldr f z (cons h t) = f h (foldr (tail f) z t) := rfl

@[simp] theorem foldl_head_tail (f : Π {{i}}, γ → β i → γ) (z : γ) (t : Π i, β i) :
foldl f z t = f (foldl (tail f) z (tail t)) (head t) := rfl

@[simp] theorem foldr_head_tail (f : Π {{i}}, β i → γ → γ) (z : γ) (t : Π i, β i) :
foldr f z t = f (head t) (foldr (tail f) z (tail t)) := rfl

end fold

end dtup

definition decidable_exists_def : Π {xs : list α} (p : index xs → Prop) (dp : ∀ i, decidable (p i)), decidable (∃ i, p i)
| [] _ _ := decidable.is_false (λ ⟨i,_⟩, nil_elim i)
| (_::_) p dp :=
  match decidable_exists_def (dtup.tail p) (dtup.tail dp) with
  | decidable.is_true ht := decidable.is_true (exists.elim ht $ λ i hi, ⟨_, hi⟩)
  | decidable.is_false ht :=
    match dtup.head dp with
    | decidable.is_true hh := decidable.is_true ⟨_, hh⟩
    | decidable.is_false hh :=
      have ¬ ∃ i, p i,
      begin
      intro h,
      cases h with i hi,
      cases i,
      exact hh hi,
      exact ht ⟨_, hi⟩,
      end,
      decidable.is_false this
    end
  end

instance decidable_exists {xs : list α} (p : index xs → Prop) [dp : Π i, decidable (p i)] : decidable (∃ i, p i) := decidable_exists_def p dp

definition decidable_forall_def : Π {xs : list α} (p : index xs → Prop) (dp : ∀ i, decidable (p i)), decidable (∀ i, p i)
| [] _ _ := decidable.is_true (λ i, nil_elim i)
| (_::_) p dp :=
  match decidable_forall_def (dtup.tail p) (dtup.tail dp) with
  | decidable.is_false ht := decidable.is_false (λ h, ht (dtup.tail h))
  | decidable.is_true ht :=
    match dtup.head dp with
    | decidable.is_false hh := decidable.is_false (λ h, hh (dtup.head h))
    | decidable.is_true hh := decidable.is_true (dtup.cons hh ht)
    end
  end

instance decidable_forall {xs : list α} (p : index xs → Prop) [dp : Π i, decidable (p i)] : decidable (∀ i, p i) := decidable_forall_def p dp

theorem choice : ∀ {xs : list α} {β : index xs → Sort*}, (∀ i, nonempty (β i)) → nonempty (Π i, β i)
| [] _ _ := nonempty.intro dtup.nil
| (_::_) C H :=
  have Hh : nonempty (dtup.head C), from dtup.head H,
  have Ht : nonempty (Π i, dtup.tail C i), from choice (dtup.tail H),
  nonempty.elim Hh $ λ h, nonempty.elim Ht $ λ t, nonempty.intro (dtup.cons h t)

section relation
open relation

theorem ec_of_ec_tail {x : α} {xs : list α} {β : index (x :: xs) → Sort*} {r : Π i, β i → β i → Prop} (hhr : reflexive (dtup.head r)) {h : dtup.head β} {t₁ t₂ : Π i, dtup.tail β i} :
ec (pi (dtup.tail r)) t₁ t₂ → ec (pi r) (dtup.cons h t₁) (dtup.cons h t₂) :=
λ e, ec.rec_on e
  (λ _ _ hxy, ec.base $ dtup.cons (hhr h) hxy)
  (λ _, ec.refl _)
  (λ _ _ _ _ _ hxy hxz, ec.eucl hxy hxz)

theorem ec_of_ec_head {x : α} {xs : list α} {β : index (x :: xs) → Sort*} {r : Π i, β i → β i → Prop} (htr : reflexive (pi (dtup.tail r))) {h₁ h₂ : dtup.head β} {t : Π i, dtup.tail β i} :
ec (dtup.head r) h₁ h₂ → ec (pi r) (dtup.cons h₁ t) (dtup.cons h₂ t) :=
λ e, ec.rec_on e
  (λ _ _ hxy, ec.base $ dtup.cons hxy (htr _))
  (λ _, ec.refl _)
  (λ _ _ _ _ _ hxy hxz, ec.eucl hxy hxz)

theorem ec_of_ec_head_tail {x : α} {xs : list α} {β : index (x :: xs) → Sort*} {r : Π i, β i → β i → Prop} (hr : ∀ i, reflexive (r i)) {h₁ h₂ : dtup.head β} {t₁ t₂ : Π i, dtup.tail β i} :
ec (dtup.head r) h₁ h₂ → ec (pi (dtup.tail r)) t₁ t₂ → ec (pi r) (dtup.cons h₁ t₁) (dtup.cons h₂ t₂) :=
λ eh et,
have hhr : reflexive (dtup.head r), from dtup.head hr,
have htr : reflexive (pi (dtup.tail r)), from pi_reflexive (dtup.tail hr),
have e₁ : ec (pi r) (dtup.cons h₁ t₁) (dtup.cons h₂ t₁), from ec_of_ec_head htr eh,
have e₂ : ec (pi r) (dtup.cons h₂ t₁) (dtup.cons h₂ t₂), from ec_of_ec_tail hhr et,
ec.trans e₁ e₂

theorem ec_pi_of_pi_ec : ∀ {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} (hr : ∀ i, reflexive (r i)) {t₁ t₂ : Π i, β i}, pi (λ i, ec (r i)) t₁ t₂ → ec (pi r) t₁ t₂
| [] _ _ _ t₁ t₂ _ := by { rw [dtup.eq_nil t₁, dtup.eq_nil t₂], apply ec.refl _ }
| (_::_) _ r hr t₁ t₂ h :=
  have eh : ec (dtup.head r) (dtup.head t₁) (dtup.head t₂), from dtup.head h,
  have et : ec (pi (dtup.tail r)) (dtup.tail t₁) (dtup.tail t₂), from ec_pi_of_pi_ec (dtup.tail hr) (dtup.tail h),
  by {rw [← dtup.cons_head_tail t₁, ← dtup.cons_head_tail t₂], exact ec_of_ec_head_tail hr eh et }

variables {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} (hr : ∀ i, reflexive (r i))

theorem pi_ec_iff_ec_pi (t₁ t₂ : Π i, β i) : pi (λ i, ec (r i)) t₁ t₂ ↔ ec (pi r) t₁ t₂ :=
⟨ec_pi_of_pi_ec hr, pi_ec_of_ec_pi⟩

theorem pi_ec_eq_ec_pi : pi (λ i, ec (r i)) = ec (pi r) :=
funext $ λ t₁, funext $ λ t₂, propext $ pi_ec_iff_ec_pi hr t₁ t₂

end relation

section setoid

instance setoid {xs : list α} (β : index xs → Sort*) [Π i, setoid (β i)] : setoid (Π i, β i) :=
{ r := λ x y, ∀ i, x i ≈ y i
, iseqv := mk_equivalence _
  (λ x i, setoid.refl (x i))
  (λ _ _ h i, setoid.symm (h i))
  (λ _ _ _ hxy hyz i, setoid.trans (hxy i) (hyz i))
}

theorem setoid.r_iff {xs : list α} {C : index xs → Sort*} [Π i, setoid (C i)] (t₁ t₂ : Π i, C i) :
t₁ ≈ t₂ ↔ (∀ i, t₁ i ≈ t₂ i) := iff.rfl

theorem setoid.r_cons_iff {x : α} {xs : list α} {C : index (x :: xs) → Sort*} [Π i, setoid (C i)] (t₁ t₂ : Π i, C i) :
t₁ ≈ t₂ ↔ dtup.head t₁ ≈ dtup.head t₂ ∧ dtup.tail t₁ ≈ dtup.tail t₂ :=
⟨ λ h, have h : ∀ i, t₁ i ≈ t₂ i, from (setoid.r_iff t₁ t₂).mp h, ⟨dtup.head h, dtup.tail h⟩
, λ ⟨hh, ht⟩, have h : ∀ i, t₁ i ≈ t₂ i, from dtup.cons hh ht, (setoid.r_iff t₁ t₂).mpr h
⟩

theorem setoid.r_cons {x : α} {xs : list α} {C : index (x :: xs) → Sort*} [Π i, setoid (C i)]
(h₁ h₂ : dtup.head C) (t₁ t₂ : Π i, dtup.tail C i) : h₁ ≈ h₂ → t₁ ≈ t₂ → dtup.cons h₁ t₁ ≈ dtup.cons h₂ t₂ :=
begin
intros eh et,
rw setoid.r_cons_iff,
simp only [dtup.head_cons, dtup.tail_cons],
split; assumption
end

end setoid

end index

namespace quot
open index
open relation
variable {α : Type*}

abbreviation index_mk {xs : list α} {β : index xs → Sort*} (r : Π i, β i → β i → Prop) (t : Π i, β i) : Π i, quot (r i) := λ i, quot.mk (r i) (t i)

@[elab_as_eliminator]
theorem index_ind : ∀ {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} (hr : ∀ i, reflexive (r i)) {P : (Π i, quot (r i)) → Prop},
(∀ (t : Π i, β i), P (λ i, quot.mk (r i) (t i))) → ∀ q, P q
| [] β r hr P H q := by { rw [dtup.eq_nil q, ← dtup.eq_nil (λ i, @quot.mk (β i) (r i) (dtup.nil i))], apply H }
| (x::xs) β r hr P H q :=
  have ∀ (h : dtup.head β) (t : Π i, dtup.tail β i), (λ i, quot.mk (r i) (dtup.cons h t i)) = dtup.cons (quot.mk (dtup.head r) h) (λ i, quot.mk (dtup.tail r i) (t i)), by { intros, funext i, cases i; reflexivity },
  have IH : ∀ (h : dtup.head β) (q : Π i, quot (dtup.tail r i)), P (dtup.cons (quot.mk (dtup.head r) h) q),
  from λ h q, index_ind (dtup.tail hr) (by {introv, rw ← this, apply H}) q,
  begin
  rw [← dtup.cons_head_tail q],
  induction (dtup.head q) using quot.ind with h,
  apply IH,
  end

@[elab_as_eliminator]
theorem index_induction_on {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} (hr : ∀ i, reflexive (r i)) {P : (Π i, quot (r i)) → Prop} (q : Π i, quot (r i)) :
(∀ (t : Π i, β i), P (λ i, quot.mk (r i) (t i))) → P q :=
λ H, index_ind hr H q

def index_map {xs : list α} {β : index xs → Sort*} (r : Π i, β i → β i → Prop) :
quot (pi r) → Π i, quot (r i) :=
quot.lift (λ (t : Π i, β i) (i : index xs), quot.mk (r i) (t i)) $
λ (t₁ t₂ : Π i, β i) (h : pi r t₁ t₂), funext $ λ i, quot.sound (h i)

def index_map_beta {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} (t : Π i, β i) :
index_map r (quot.mk (pi r) t) = λ i, quot.mk (r i) (t i) := rfl

section index_cons
variables {x : α} {xs : list α} {β : index (x :: xs) → Sort*} {r : Π i, β i → β i → Prop}

def index_cons_func (hr : reflexive (dtup.head r)) : dtup.head β → quot (pi (dtup.tail r)) → quot (pi r) :=
λ h, quot.lift (λ t, quot.mk (pi r) (dtup.cons h t)) $
λ t₁ t₂ ht, quot.sound (dtup.cons (hr h) ht)

theorem index_cons_func_beta {hr : reflexive (dtup.head r)} (h : dtup.head β) (t : Π i, dtup.tail β i) :
index_cons_func hr h (quot.mk (pi (dtup.tail r)) t) = quot.mk (pi r) (dtup.cons h t) := rfl

def index_cons (hr : ∀ i, reflexive (r i)) : quot (dtup.head r) → quot (pi (dtup.tail r)) → quot (pi r) :=
quot.lift (index_cons_func (dtup.head hr)) $
begin
intros h₁ h₂ hh,
funext t,
induction t using quot.ind,
rw index_cons_func_beta,
rw index_cons_func_beta,
apply quot.sound,
apply dtup.cons,
exact hh,
exact pi_reflexive (dtup.tail hr) t,
end

theorem index_cons_beta {hr : ∀ i, reflexive (r i)} (h : dtup.head β) (t : Π i, dtup.tail β i) :
index_cons hr (quot.mk (dtup.head r) h) (quot.mk (pi (dtup.tail r)) t) = quot.mk (pi r) (dtup.cons h t) := rfl

end index_cons

def index_inv : Π {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} (hr : Π i, reflexive (r i)),
(Π i, quot (r i)) → quot (pi r)
| [] _ r _ _ := quot.mk (pi r) dtup.nil
| (_::_) _ _ hr q := index_cons hr (dtup.head q) (index_inv (dtup.tail hr) (dtup.tail q))

theorem index_inv_nil {β : index (@list.nil α) → Sort*} {r : Π i, β i → β i → Prop} {hr : Π i, reflexive (r i)} :
index_inv hr dtup.nil = quot.mk (pi r) dtup.nil := rfl

theorem index_inv_cons {x : α} {xs : list α} {β : index (x :: xs) → Sort*} {r : Π i, β i → β i → Prop} {hr : Π i, reflexive (r i)} (h : quot (dtup.head r)) (t : Π i, quot (dtup.tail r i)) :
index_inv hr (dtup.cons h t) = index_cons hr h (index_inv (dtup.tail hr) t) := rfl

theorem index_inv_head_tail {x : α} {xs : list α} {β : index (x :: xs) → Sort*} {r : Π i, β i → β i → Prop} {hr : Π i, reflexive (r i)} (q : Π i, quot (r i)) :
index_inv hr q = index_cons hr (dtup.head q) (index_inv (dtup.tail hr) (dtup.tail q)) := rfl

theorem index_inv_beta : Π {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} {hr : Π i, reflexive (r i)} (t : Π i, β i),
index_inv hr (λ i, quot.mk (r i) (t i)) = quot.mk (pi r) t
| [] _ _ _ t := by { rw [dtup.eq_nil t], reflexivity }
| (x::xs) β r hr t := by { rw [index_inv_head_tail, index_inv_beta, index_cons_beta], simp }

theorem index_inv_map {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} {hr : ∀ i, reflexive (r i)} :
∀ (q : quot (pi r)), index_inv hr (index_map r q) = q := quot.ind (by intro; rw [index_map_beta, index_inv_beta])

theorem index_map_inv {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} {hr : ∀ i, reflexive (r i)} :
∀ (q : Π i, quot (r i)), index_map r (index_inv hr q) = q := quot.index_ind hr (by intro; rw [index_inv_beta, index_map_beta])

@[elab_as_eliminator, reducible]
def index_lift {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} (hr : Π i, reflexive (r i)) {γ : Sort*} (f : (Π i, β i) → γ) :
(∀ t₁ t₂, pi r t₁ t₂ → f t₁ = f t₂) → (Π i, quot (r i)) → γ := λ H q, quot.lift f H (index_inv hr q)

@[elab_as_eliminator, reducible]
abbreviation index_lift_on {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} (hr : Π i, reflexive (r i)) (q : Π i, quot (r i)) {γ : Sort*} (f : (Π i, β i) → γ) :
(∀ t₁ t₂, pi r t₁ t₂ → f t₁ = f t₂) → γ := λ H, index_lift hr f H q

theorem index_lift_def {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} {hr : Π i, reflexive (r i)} {γ : Sort*} (f : (Π i, β i) → γ) {H : ∀ t₁ t₂, pi r t₁ t₂ → f t₁ = f t₂} (q : Π i, quot (r i)) :
index_lift hr f H q = quot.lift f H (index_inv hr q) := rfl

theorem index_lift_map {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} {hr : Π i, reflexive (r i)} {γ : Sort*} (f : (Π i, β i) → γ) {H : ∀ t₁ t₂, pi r t₁ t₂ → f t₁ = f t₂} (q : quot (pi r)) :
index_lift hr f H (index_map r q) = quot.lift f H q := by rw [← index_inv_map q] {occs:=occurrences.pos[2]}

theorem index_lift_beta {xs : list α} {β : index xs → Sort*} {r : Π i, β i → β i → Prop} {hr : Π i, reflexive (r i)} {γ : Sort*} (f : (Π i, β i) → γ) {H : ∀ t₁ t₂, pi r t₁ t₂ → f t₁ = f t₂} (t : Π i, β i) :
index_lift hr f H (λ i, quot.mk (r i) (t i)) = f t := by rw [← index_map_beta, index_lift_map]

end quot

namespace quotient
open index
variables {α : Type*} {xs : list α} {β : index xs → Sort*} [s : Π i, setoid (β i)]
include s

definition index_lift {γ : Sort*} (f : (Π i, β i) → γ) :
(∀ (t₁ t₂ : Π i, β i), (∀ i, t₁ i ≈ t₂ i) → f t₁ = f t₂) → (Π i, quotient (s i)) → γ :=
have hr : ∀ i, reflexive (setoid.r : β i → β i → Prop), from λ i, (s i).iseqv.refl,
quot.index_lift hr f

theorem index_lift_beta {γ : Sort*} (f : (Π i, β i) → γ) {H : ∀ (t₁ t₂ : Π i, β i), (∀ i, t₁ i ≈ t₂ i) → f t₁ = f t₂} :
∀ (t : Π i, β i), index_lift f H (λ i, ⟦t i⟧) = f t :=
have hr : ∀ i, reflexive (setoid.r : β i → β i → Prop), from λ i, (s i).iseqv.refl,
begin
intro,
dunfold index_lift,
apply quot.index_lift_beta,
end

theorem index_ind {P : (Π i, quotient (s i)) → Prop} :
(∀ (t : Π i, β i), P (λ i, ⟦t i⟧)) → ∀ q, P q :=
have hr : ∀ i, reflexive (setoid.r : β i → β i → Prop), from λ i, (s i).iseqv.refl,
λ H, quot.index_ind hr H

theorem index_induction_on {P : (Π i, quotient (s i)) → Prop} (q : Π i, quotient (s i)) :
(∀ (t : Π i, β i), P (λ i, ⟦t i⟧)) → P q := λ H, index_ind H q

end quotient

namespace list
variable {α : Type*}

theorem mem_join (x : α) : Π {xss : list (list α)} (i : index xss), x ∈ i.val → x ∈ xss.join
| _ (index.head xs xss) hi := show x ∈ xs ++ xss.join, by { rw mem_append, left, exact hi }
| _ (index.tail xs xss i) hi := show x ∈ xs ++ xss.join, by { rw mem_append, right, exact mem_join i hi }

end list