/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl

! This file was ported from Lean 3 source module data.subtype
! 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.Logic.Function.Basic
import Mathbin.Tactic.Ext
import Mathbin.Tactic.Simps

/-!
# Subtypes

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

This file provides basic API for subtypes, which are defined in core.

A subtype is a type made from restricting another type, say `α`, to its elements that satisfy some
predicate, say `p : α → Prop`. Specifically, it is the type of pairs `⟨val, property⟩` where
`val : α` and `property : p val`. It is denoted `subtype p` and notation `{val : α // p val}` is
available.

A subtype has a natural coercion to the parent type, by coercing `⟨val, property⟩` to `val`. As
such, subtypes can be thought of as bundled sets, the difference being that elements of a set are
still of type `α` while elements of a subtype aren't.
-/

open Function

namespace Subtype

variable {α β γ : Sort _} {p q : α → Prop}

/-- See Note [custom simps projection] -/
def Simps.coe (x : Subtype p) : α :=
  x
#align subtype.simps.coe Subtype.Simps.coe

initialize_simps_projections Subtype (val → coe)

#print Subtype.prop /-
/-- A version of `x.property` or `x.2` where `p` is syntactically applied to the coercion of `x`
  instead of `x.1`. A similar result is `subtype.mem` in `data.set.basic`. -/
theorem prop (x : Subtype p) : p x :=
  x.2
#align subtype.prop Subtype.prop
-/

@[simp]
theorem val_eq_coe {x : Subtype p} : x.1 = ↑x :=
  rfl
#align subtype.val_eq_coe Subtype.val_eq_coe

#print Subtype.forall /-
@[simp]
protected theorem forall {q : { a // p a } → Prop} : (∀ x, q x) ↔ ∀ a b, q ⟨a, b⟩ :=
  ⟨fun h a b => h ⟨a, b⟩, fun h ⟨a, b⟩ => h a b⟩
#align subtype.forall Subtype.forall
-/

#print Subtype.forall' /-
/-- An alternative version of `subtype.forall`. This one is useful if Lean cannot figure out `q`
  when using `subtype.forall` from right to left. -/
protected theorem forall' {q : ∀ x, p x → Prop} : (∀ x h, q x h) ↔ ∀ x : { a // p a }, q x x.2 :=
  (@Subtype.forall _ _ fun x => q x.1 x.2).symm
#align subtype.forall' Subtype.forall'
-/

#print Subtype.exists /-
@[simp]
protected theorem exists {q : { a // p a } → Prop} : (∃ x, q x) ↔ ∃ a b, q ⟨a, b⟩ :=
  ⟨fun ⟨⟨a, b⟩, h⟩ => ⟨a, b, h⟩, fun ⟨a, b, h⟩ => ⟨⟨a, b⟩, h⟩⟩
#align subtype.exists Subtype.exists
-/

#print Subtype.exists' /-
/-- An alternative version of `subtype.exists`. This one is useful if Lean cannot figure out `q`
  when using `subtype.exists` from right to left. -/
protected theorem exists' {q : ∀ x, p x → Prop} : (∃ x h, q x h) ↔ ∃ x : { a // p a }, q x x.2 :=
  (@Subtype.exists _ _ fun x => q x.1 x.2).symm
#align subtype.exists' Subtype.exists'
-/

#print Subtype.ext /-
@[ext]
protected theorem ext : ∀ {a1 a2 : { x // p x }}, (a1 : α) = (a2 : α) → a1 = a2
  | ⟨x, h1⟩, ⟨x, h2⟩, rfl => rfl
#align subtype.ext Subtype.ext
-/

#print Subtype.ext_iff /-
theorem ext_iff {a1 a2 : { x // p x }} : a1 = a2 ↔ (a1 : α) = (a2 : α) :=
  ⟨congr_arg _, Subtype.ext⟩
#align subtype.ext_iff Subtype.ext_iff
-/

#print Subtype.heq_iff_coe_eq /-
theorem heq_iff_coe_eq (h : ∀ x, p x ↔ q x) {a1 : { x // p x }} {a2 : { x // q x }} :
    HEq a1 a2 ↔ (a1 : α) = (a2 : α) :=
  Eq.ndrec (fun a2' => heq_iff_eq.trans ext_iff) (funext fun x => propext (h x)) a2
#align subtype.heq_iff_coe_eq Subtype.heq_iff_coe_eq
-/

#print Subtype.heq_iff_coe_heq /-
theorem heq_iff_coe_heq {α β : Sort _} {p : α → Prop} {q : β → Prop} {a : { x // p x }}
    {b : { y // q y }} (h : α = β) (h' : HEq p q) : HEq a b ↔ HEq (a : α) (b : β) :=
  by
  subst h
  subst h'
  rw [heq_iff_eq, heq_iff_eq, ext_iff]
#align subtype.heq_iff_coe_heq Subtype.heq_iff_coe_heq
-/

#print Subtype.ext_val /-
theorem ext_val {a1 a2 : { x // p x }} : a1.1 = a2.1 → a1 = a2 :=
  Subtype.ext
#align subtype.ext_val Subtype.ext_val
-/

#print Subtype.ext_iff_val /-
theorem ext_iff_val {a1 a2 : { x // p x }} : a1 = a2 ↔ a1.1 = a2.1 :=
  ext_iff
#align subtype.ext_iff_val Subtype.ext_iff_val
-/

#print Subtype.coe_eta /-
@[simp]
theorem coe_eta (a : { a // p a }) (h : p a) : mk (↑a) h = a :=
  Subtype.ext rfl
#align subtype.coe_eta Subtype.coe_eta
-/

#print Subtype.coe_mk /-
@[simp]
theorem coe_mk (a h) : (@mk α p a h : α) = a :=
  rfl
#align subtype.coe_mk Subtype.coe_mk
-/

#print Subtype.mk_eq_mk /-
-- built-in reduction doesn't always work
@[simp, nolint simp_nf]
theorem mk_eq_mk {a h a' h'} : @mk α p a h = @mk α p a' h' ↔ a = a' :=
  ext_iff
#align subtype.mk_eq_mk Subtype.mk_eq_mk
-/

#print Subtype.coe_eq_of_eq_mk /-
theorem coe_eq_of_eq_mk {a : { a // p a }} {b : α} (h : ↑a = b) : a = ⟨b, h ▸ a.2⟩ :=
  Subtype.ext h
#align subtype.coe_eq_of_eq_mk Subtype.coe_eq_of_eq_mk
-/

#print Subtype.coe_eq_iff /-
theorem coe_eq_iff {a : { a // p a }} {b : α} : ↑a = b ↔ ∃ h, a = ⟨b, h⟩ :=
  ⟨fun h => h ▸ ⟨a.2, (coe_eta _ _).symm⟩, fun ⟨hb, ha⟩ => ha.symm ▸ rfl⟩
#align subtype.coe_eq_iff Subtype.coe_eq_iff
-/

#print Subtype.coe_injective /-
theorem coe_injective : Injective (coe : Subtype p → α) := fun a b => Subtype.ext
#align subtype.coe_injective Subtype.coe_injective
-/

#print Subtype.val_injective /-
theorem val_injective : Injective (@val _ p) :=
  coe_injective
#align subtype.val_injective Subtype.val_injective
-/

#print Subtype.coe_inj /-
theorem coe_inj {a b : Subtype p} : (a : α) = b ↔ a = b :=
  coe_injective.eq_iff
#align subtype.coe_inj Subtype.coe_inj
-/

#print Subtype.val_inj /-
theorem val_inj {a b : Subtype p} : a.val = b.val ↔ a = b :=
  coe_inj
#align subtype.val_inj Subtype.val_inj
-/

#print exists_eq_subtype_mk_iff /-
@[simp]
theorem exists_eq_subtype_mk_iff {a : Subtype p} {b : α} :
    (∃ h : p b, a = Subtype.mk b h) ↔ ↑a = b :=
  coe_eq_iff.symm
#align exists_eq_subtype_mk_iff exists_eq_subtype_mk_iff
-/

#print exists_subtype_mk_eq_iff /-
@[simp]
theorem exists_subtype_mk_eq_iff {a : Subtype p} {b : α} :
    (∃ h : p b, Subtype.mk b h = a) ↔ b = a := by
  simp only [@eq_comm _ b, exists_eq_subtype_mk_iff, @eq_comm _ _ a]
#align exists_subtype_mk_eq_iff exists_subtype_mk_eq_iff
-/

#print Subtype.restrict /-
/-- Restrict a (dependent) function to a subtype -/
def restrict {α} {β : α → Type _} (p : α → Prop) (f : ∀ x, β x) (x : Subtype p) : β x.1 :=
  f x
#align subtype.restrict Subtype.restrict
-/

/- warning: subtype.restrict_apply -> Subtype.restrict_apply is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : α -> Type.{u2}} (f : forall (x : α), β x) (p : α -> Prop) (x : Subtype.{u1} α p), Eq.{succ u2} (β (Subtype.val.{u1} α p x)) (Subtype.restrict.{u1, u2} α (fun (x : α) => β x) p f x) (f (Subtype.val.{u1} α p x))
but is expected to have type
  forall {α : Sort.{u2}} {β : α -> Type.{u1}} (f : forall (x : α), β x) (p : α -> Prop) (x : Subtype.{u2} α p), Eq.{succ u1} (β (Subtype.val.{u2} α p x)) (Subtype.restrict.{u2, u1} α (fun (x : α) => β x) p f x) (f (Subtype.val.{u2} α p x))
Case conversion may be inaccurate. Consider using '#align subtype.restrict_apply Subtype.restrict_applyₓ'. -/
theorem restrict_apply {α} {β : α → Type _} (f : ∀ x, β x) (p : α → Prop) (x : Subtype p) :
    restrict p f x = f x.1 := by rfl
#align subtype.restrict_apply Subtype.restrict_apply

/- warning: subtype.restrict_def -> Subtype.restrict_def is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Type.{u2}} (f : α -> β) (p : α -> Prop), Eq.{max (max 1 u1) (succ u2)} ((Subtype.{u1} α p) -> β) (Subtype.restrict.{u1, u2} α (fun (ᾰ : α) => β) p f) (Function.comp.{max 1 u1, u1, succ u2} (Subtype.{u1} α p) α β f ((fun (a : Sort.{max 1 u1}) (b : Sort.{u1}) [self : HasLiftT.{max 1 u1, u1} a b] => self.0) (Subtype.{u1} α p) α (HasLiftT.mk.{max 1 u1, u1} (Subtype.{u1} α p) α (CoeTCₓ.coe.{max 1 u1, u1} (Subtype.{u1} α p) α (coeBase.{max 1 u1, u1} (Subtype.{u1} α p) α (coeSubtype.{u1} α (fun (x : α) => p x)))))))
but is expected to have type
  forall {α : Sort.{u2}} {β : Type.{u1}} (f : α -> β) (p : α -> Prop), Eq.{max u2 (succ u1)} ((Subtype.{u2} α p) -> β) (Subtype.restrict.{u2, u1} α (fun (ᾰ : α) => β) p f) (Function.comp.{max 1 u2, u2, succ u1} (Subtype.{u2} α p) α β f (fun (a : Subtype.{u2} α p) => Subtype.val.{u2} α p a))
Case conversion may be inaccurate. Consider using '#align subtype.restrict_def Subtype.restrict_defₓ'. -/
theorem restrict_def {α β} (f : α → β) (p : α → Prop) : restrict p f = f ∘ coe := by rfl
#align subtype.restrict_def Subtype.restrict_def

/- warning: subtype.restrict_injective -> Subtype.restrict_injective is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Type.{u2}} {f : α -> β} (p : α -> Prop), (Function.Injective.{u1, succ u2} α β f) -> (Function.Injective.{max 1 u1, succ u2} (Subtype.{u1} α p) β (Subtype.restrict.{u1, u2} α (fun (ᾰ : α) => β) p f))
but is expected to have type
  forall {α : Sort.{u2}} {β : Type.{u1}} {f : α -> β} (p : α -> Prop), (Function.Injective.{u2, succ u1} α β f) -> (Function.Injective.{max 1 u2, succ u1} (Subtype.{u2} α p) β (Subtype.restrict.{u2, u1} α (fun (ᾰ : α) => β) p f))
Case conversion may be inaccurate. Consider using '#align subtype.restrict_injective Subtype.restrict_injectiveₓ'. -/
theorem restrict_injective {α β} {f : α → β} (p : α → Prop) (h : Injective f) :
    Injective (restrict p f) :=
  h.comp coe_injective
#align subtype.restrict_injective Subtype.restrict_injective

/- warning: subtype.surjective_restrict -> Subtype.surjective_restrict is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : α -> Type.{u2}} [ne : forall (a : α), Nonempty.{succ u2} (β a)] (p : α -> Prop), Function.Surjective.{max u1 (succ u2), max (max 1 u1) (succ u2)} (forall (x : α), β x) (forall (x : Subtype.{u1} α p), β (Subtype.val.{u1} α p x)) (fun (f : forall (x : α), β x) => Subtype.restrict.{u1, u2} α (fun (x : α) => β x) p f)
but is expected to have type
  forall {α : Sort.{u2}} {β : α -> Type.{u1}} [ne : forall (a : α), Nonempty.{succ u1} (β a)] (p : α -> Prop), Function.Surjective.{max u2 (succ u1), max u2 (succ u1)} (forall (x : α), β x) (forall (x : Subtype.{u2} α p), β (Subtype.val.{u2} α p x)) (fun (f : forall (x : α), β x) => Subtype.restrict.{u2, u1} α (fun (x : α) => β x) p f)
Case conversion may be inaccurate. Consider using '#align subtype.surjective_restrict Subtype.surjective_restrictₓ'. -/
theorem surjective_restrict {α} {β : α → Type _} [ne : ∀ a, Nonempty (β a)] (p : α → Prop) :
    Surjective fun f : ∀ x, β x => restrict p f :=
  by
  letI := Classical.decPred p
  refine' fun f => ⟨fun x => if h : p x then f ⟨x, h⟩ else Nonempty.some (Ne x), funext <| _⟩
  rintro ⟨x, hx⟩
  exact dif_pos hx
#align subtype.surjective_restrict Subtype.surjective_restrict

#print Subtype.coind /-
/-- Defining a map into a subtype, this can be seen as an "coinduction principle" of `subtype`-/
@[simps]
def coind {α β} (f : α → β) {p : β → Prop} (h : ∀ a, p (f a)) : α → Subtype p := fun a => ⟨f a, h a⟩
#align subtype.coind Subtype.coind
-/

/- warning: subtype.coind_injective -> Subtype.coind_injective is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} {f : α -> β} {p : β -> Prop} (h : forall (a : α), p (f a)), (Function.Injective.{u1, u2} α β f) -> (Function.Injective.{u1, max 1 u2} α (Subtype.{u2} β p) (Subtype.coind.{u1, u2} α β f p h))
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} {f : α -> β} {p : β -> Prop} (h : forall (a : α), p (f a)), (Function.Injective.{u2, u1} α β f) -> (Function.Injective.{u2, max 1 u1} α (Subtype.{u1} β p) (Subtype.coind.{u2, u1} α β f p h))
Case conversion may be inaccurate. Consider using '#align subtype.coind_injective Subtype.coind_injectiveₓ'. -/
theorem coind_injective {α β} {f : α → β} {p : β → Prop} (h : ∀ a, p (f a)) (hf : Injective f) :
    Injective (coind f h) := fun x y hxy => hf <| by apply congr_arg Subtype.val hxy
#align subtype.coind_injective Subtype.coind_injective

/- warning: subtype.coind_surjective -> Subtype.coind_surjective is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} {f : α -> β} {p : β -> Prop} (h : forall (a : α), p (f a)), (Function.Surjective.{u1, u2} α β f) -> (Function.Surjective.{u1, max 1 u2} α (Subtype.{u2} β p) (Subtype.coind.{u1, u2} α β f p h))
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} {f : α -> β} {p : β -> Prop} (h : forall (a : α), p (f a)), (Function.Surjective.{u2, u1} α β f) -> (Function.Surjective.{u2, max 1 u1} α (Subtype.{u1} β p) (Subtype.coind.{u2, u1} α β f p h))
Case conversion may be inaccurate. Consider using '#align subtype.coind_surjective Subtype.coind_surjectiveₓ'. -/
theorem coind_surjective {α β} {f : α → β} {p : β → Prop} (h : ∀ a, p (f a)) (hf : Surjective f) :
    Surjective (coind f h) := fun x =>
  let ⟨a, ha⟩ := hf x
  ⟨a, coe_injective ha⟩
#align subtype.coind_surjective Subtype.coind_surjective

/- warning: subtype.coind_bijective -> Subtype.coind_bijective is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} {f : α -> β} {p : β -> Prop} (h : forall (a : α), p (f a)), (Function.Bijective.{u1, u2} α β f) -> (Function.Bijective.{u1, max 1 u2} α (Subtype.{u2} β p) (Subtype.coind.{u1, u2} α β f p h))
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} {f : α -> β} {p : β -> Prop} (h : forall (a : α), p (f a)), (Function.Bijective.{u2, u1} α β f) -> (Function.Bijective.{u2, max 1 u1} α (Subtype.{u1} β p) (Subtype.coind.{u2, u1} α β f p h))
Case conversion may be inaccurate. Consider using '#align subtype.coind_bijective Subtype.coind_bijectiveₓ'. -/
theorem coind_bijective {α β} {f : α → β} {p : β → Prop} (h : ∀ a, p (f a)) (hf : Bijective f) :
    Bijective (coind f h) :=
  ⟨coind_injective h hf.1, coind_surjective h hf.2⟩
#align subtype.coind_bijective Subtype.coind_bijective

#print Subtype.map /-
/-- Restriction of a function to a function on subtypes. -/
@[simps]
def map {p : α → Prop} {q : β → Prop} (f : α → β) (h : ∀ a, p a → q (f a)) :
    Subtype p → Subtype q := fun x => ⟨f x, h x x.prop⟩
#align subtype.map Subtype.map
-/

/- warning: subtype.map_comp -> Subtype.map_comp is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} {γ : Sort.{u3}} {p : α -> Prop} {q : β -> Prop} {r : γ -> Prop} {x : Subtype.{u1} α p} (f : α -> β) (h : forall (a : α), (p a) -> (q (f a))) (g : β -> γ) (l : forall (a : β), (q a) -> (r (g a))), Eq.{max 1 u3} (Subtype.{u3} γ r) (Subtype.map.{u2, u3} β γ (fun (a : β) => q a) r g l (Subtype.map.{u1, u2} α β (fun (a : α) => p a) (fun (a : β) => q a) f h x)) (Subtype.map.{u1, u3} α γ (fun (a : α) => p a) r (Function.comp.{u1, u2, u3} α β γ g f) (fun (a : α) (ha : p a) => l (f a) (h a ha)) x)
but is expected to have type
  forall {α : Sort.{u3}} {β : Sort.{u1}} {γ : Sort.{u2}} {p : α -> Prop} {q : β -> Prop} {r : γ -> Prop} {x : Subtype.{u3} α p} (f : α -> β) (h : forall (a : α), (p a) -> (q (f a))) (g : β -> γ) (l : forall (a : β), (q a) -> (r (g a))), Eq.{max 1 u2} (Subtype.{u2} γ r) (Subtype.map.{u1, u2} β γ (fun (a : β) => q a) r g l (Subtype.map.{u3, u1} α β (fun (a : α) => p a) (fun (a : β) => q a) f h x)) (Subtype.map.{u3, u2} α γ (fun (a : α) => p a) r (Function.comp.{u3, u1, u2} α β γ g f) (fun (a : α) (ha : p a) => l (f a) (h a ha)) x)
Case conversion may be inaccurate. Consider using '#align subtype.map_comp Subtype.map_compₓ'. -/
theorem map_comp {p : α → Prop} {q : β → Prop} {r : γ → Prop} {x : Subtype p} (f : α → β)
    (h : ∀ a, p a → q (f a)) (g : β → γ) (l : ∀ a, q a → r (g a)) :
    map g l (map f h x) = map (g ∘ f) (fun a ha => l (f a) <| h a ha) x :=
  rfl
#align subtype.map_comp Subtype.map_comp

#print Subtype.map_id /-
theorem map_id {p : α → Prop} {h : ∀ a, p a → p (id a)} : map (@id α) h = id :=
  funext fun ⟨v, h⟩ => rfl
#align subtype.map_id Subtype.map_id
-/

/- warning: subtype.map_injective -> Subtype.map_injective is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} {β : Sort.{u2}} {p : α -> Prop} {q : β -> Prop} {f : α -> β} (h : forall (a : α), (p a) -> (q (f a))), (Function.Injective.{u1, u2} α β f) -> (Function.Injective.{max 1 u1, max 1 u2} (Subtype.{u1} α (fun (a : α) => p a)) (Subtype.{u2} β q) (Subtype.map.{u1, u2} α β (fun (a : α) => p a) q f h))
but is expected to have type
  forall {α : Sort.{u2}} {β : Sort.{u1}} {p : α -> Prop} {q : β -> Prop} {f : α -> β} (h : forall (a : α), (p a) -> (q (f a))), (Function.Injective.{u2, u1} α β f) -> (Function.Injective.{max 1 u2, max 1 u1} (Subtype.{u2} α (fun (a : α) => p a)) (Subtype.{u1} β q) (Subtype.map.{u2, u1} α β (fun (a : α) => p a) q f h))
Case conversion may be inaccurate. Consider using '#align subtype.map_injective Subtype.map_injectiveₓ'. -/
theorem map_injective {p : α → Prop} {q : β → Prop} {f : α → β} (h : ∀ a, p a → q (f a))
    (hf : Injective f) : Injective (map f h) :=
  coind_injective _ <| hf.comp coe_injective
#align subtype.map_injective Subtype.map_injective

#print Subtype.map_involutive /-
theorem map_involutive {p : α → Prop} {f : α → α} (h : ∀ a, p a → p (f a)) (hf : Involutive f) :
    Involutive (map f h) := fun x => Subtype.ext (hf x)
#align subtype.map_involutive Subtype.map_involutive
-/

instance [HasEquiv α] (p : α → Prop) : HasEquiv (Subtype p) :=
  ⟨fun s t => (s : α) ≈ (t : α)⟩

/- warning: subtype.equiv_iff -> Subtype.equiv_iff is a dubious translation:
lean 3 declaration is
  forall {α : Sort.{u1}} [_inst_1 : HasEquivₓ.{u1} α] {p : α -> Prop} {s : Subtype.{u1} α p} {t : Subtype.{u1} α p}, Iff (HasEquivₓ.Equiv.{max 1 u1} (Subtype.{u1} α p) (Subtype.hasEquiv.{u1} α _inst_1 p) s t) (HasEquivₓ.Equiv.{u1} α _inst_1 ((fun (a : Sort.{max 1 u1}) (b : Sort.{u1}) [self : HasLiftT.{max 1 u1, u1} a b] => self.0) (Subtype.{u1} α p) α (HasLiftT.mk.{max 1 u1, u1} (Subtype.{u1} α p) α (CoeTCₓ.coe.{max 1 u1, u1} (Subtype.{u1} α p) α (coeBase.{max 1 u1, u1} (Subtype.{u1} α p) α (coeSubtype.{u1} α (fun (x : α) => p x))))) s) ((fun (a : Sort.{max 1 u1}) (b : Sort.{u1}) [self : HasLiftT.{max 1 u1, u1} a b] => self.0) (Subtype.{u1} α p) α (HasLiftT.mk.{max 1 u1, u1} (Subtype.{u1} α p) α (CoeTCₓ.coe.{max 1 u1, u1} (Subtype.{u1} α p) α (coeBase.{max 1 u1, u1} (Subtype.{u1} α p) α (coeSubtype.{u1} α (fun (x : α) => p x))))) t))
but is expected to have type
  forall {α : Sort.{u1}} [_inst_1 : HasEquiv.{u1, 0} α] {p : α -> Prop} {s : Subtype.{u1} α p} {t : Subtype.{u1} α p}, Iff (HasEquiv.Equiv.{max 1 u1, 0} (Subtype.{u1} α p) (Subtype.instHasEquivSubtype.{u1, 0} α _inst_1 p) s t) (HasEquiv.Equiv.{u1, 0} α _inst_1 (Subtype.val.{u1} α p s) (Subtype.val.{u1} α p t))
Case conversion may be inaccurate. Consider using '#align subtype.equiv_iff Subtype.equiv_iffₓ'. -/
theorem equiv_iff [HasEquiv α] {p : α → Prop} {s t : Subtype p} : s ≈ t ↔ (s : α) ≈ (t : α) :=
  Iff.rfl
#align subtype.equiv_iff Subtype.equiv_iff

variable [Setoid α]

#print Subtype.refl /-
protected theorem refl (s : Subtype p) : s ≈ s :=
  Setoid.refl ↑s
#align subtype.refl Subtype.refl
-/

#print Subtype.symm /-
protected theorem symm {s t : Subtype p} (h : s ≈ t) : t ≈ s :=
  Setoid.symm h
#align subtype.symm Subtype.symm
-/

#print Subtype.trans /-
protected theorem trans {s t u : Subtype p} (h₁ : s ≈ t) (h₂ : t ≈ u) : s ≈ u :=
  Setoid.trans h₁ h₂
#align subtype.trans Subtype.trans
-/

#print Subtype.equivalence /-
theorem equivalence (p : α → Prop) : Equivalence (@HasEquiv.Equiv (Subtype p) _) :=
  Equivalence.mk _ Subtype.refl (@Subtype.symm _ p _) (@Subtype.trans _ p _)
#align subtype.equivalence Subtype.equivalence
-/

instance (p : α → Prop) : Setoid (Subtype p) :=
  Setoid.mk (· ≈ ·) (equivalence p)

end Subtype

namespace Subtype

/-! Some facts about sets, which require that `α` is a type. -/

variable {α β γ : Type _} {p : α → Prop}

#print Subtype.coe_prop /-
@[simp]
theorem coe_prop {S : Set α} (a : { a // a ∈ S }) : ↑a ∈ S :=
  a.prop
#align subtype.coe_prop Subtype.coe_prop
-/

#print Subtype.val_prop /-
theorem val_prop {S : Set α} (a : { a // a ∈ S }) : a.val ∈ S :=
  a.property
#align subtype.val_prop Subtype.val_prop
-/

end Subtype


/-
Copyright (c) 2018 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Reid Barton, Mario Carneiro, Scott Morrison, Floris van Doorn
-/
import category_theory.limits.is_limit
import category_theory.category.ulift

/-!
# Existence of limits and colimits

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

In `category_theory.limits.is_limit` we defined `is_limit c`,
the data showing that a cone `c` is a limit cone.

The two main structures defined in this file are:
* `limit_cone F`, which consists of a choice of cone for `F` and the fact it is a limit cone, and
* `has_limit F`, asserting the mere existence of some limit cone for `F`.

`has_limit` is a propositional typeclass
(it's important that it is a proposition merely asserting the existence of a limit,
as otherwise we would have non-defeq problems from incompatible instances).

While `has_limit` only asserts the existence of a limit cone,
we happily use the axiom of choice in mathlib,
so there are convenience functions all depending on `has_limit F`:
* `limit F : C`, producing some limit object (of course all such are isomorphic)
* `limit.π F j : limit F ⟶ F.obj j`, the morphisms out of the limit,
* `limit.lift F c : c.X ⟶ limit F`, the universal morphism from any other `c : cone F`, etc.

Key to using the `has_limit` interface is that there is an `@[ext]` lemma stating that
to check `f = g`, for `f g : Z ⟶ limit F`, it suffices to check `f ≫ limit.π F j = g ≫ limit.π F j`
for every `j`.
This, combined with `@[simp]` lemmas, makes it possible to prove many easy facts about limits using
automation (e.g. `tidy`).

There are abbreviations `has_limits_of_shape J C` and `has_limits C`
asserting the existence of classes of limits.
Later more are introduced, for finite limits, special shapes of limits, etc.

Ideally, many results about limits should be stated first in terms of `is_limit`,
and then a result in terms of `has_limit` derived from this.
At this point, however, this is far from uniformly achieved in mathlib ---
often statements are only written in terms of `has_limit`.

## Implementation
At present we simply say everything twice, in order to handle both limits and colimits.
It would be highly desirable to have some automation support,
e.g. a `@[dualize]` attribute that behaves similarly to `@[to_additive]`.

## References
* [Stacks: Limits and colimits](https://stacks.math.columbia.edu/tag/002D)

-/

noncomputable theory

open category_theory category_theory.category category_theory.functor opposite

namespace category_theory.limits

-- morphism levels before object levels. See note [category_theory universes].
universes v₁ u₁ v₂ u₂ v₃ u₃ v v' v'' u u' u''

variables {J : Type u₁} [category.{v₁} J] {K : Type u₂} [category.{v₂} K]
variables {C : Type u} [category.{v} C]

variables {F : J ⥤ C}

section limit

/-- `limit_cone F` contains a cone over `F` together with the information that it is a limit. -/
@[nolint has_nonempty_instance]
structure limit_cone (F : J ⥤ C) :=
(cone : cone F)
(is_limit : is_limit cone)

/-- `has_limit F` represents the mere existence of a limit for `F`. -/
class has_limit (F : J ⥤ C) : Prop :=
mk' :: (exists_limit : nonempty (limit_cone F))

lemma has_limit.mk {F : J ⥤ C} (d : limit_cone F) : has_limit F :=
⟨nonempty.intro d⟩

/-- Use the axiom of choice to extract explicit `limit_cone F` from `has_limit F`. -/
def get_limit_cone (F : J ⥤ C) [has_limit F] : limit_cone F :=
classical.choice $ has_limit.exists_limit

variables (J C)

/-- `C` has limits of shape `J` if there exists a limit for every functor `F : J ⥤ C`. -/
class has_limits_of_shape : Prop :=
(has_limit : Π F : J ⥤ C, has_limit F . tactic.apply_instance)

/--
`C` has all limits of size `v₁ u₁` (`has_limits_of_size.{v₁ u₁} C`)
if it has limits of every shape `J : Type u₁` with `[category.{v₁} J]`.
-/
class has_limits_of_size (C : Type u) [category.{v} C] : Prop :=
(has_limits_of_shape :
  Π (J : Type u₁) [𝒥 : category.{v₁} J], has_limits_of_shape J C . tactic.apply_instance)

/-- `C` has all (small) limits if it has limits of every shape that is as big as its hom-sets. -/
abbreviation has_limits (C : Type u) [category.{v} C] : Prop := has_limits_of_size.{v v} C

lemma has_limits.has_limits_of_shape {C : Type u} [category.{v} C] [has_limits C]
  (J : Type v) [category.{v} J] :
  has_limits_of_shape J C := has_limits_of_size.has_limits_of_shape J

variables {J C}

@[priority 100] -- see Note [lower instance priority]
instance has_limit_of_has_limits_of_shape
  {J : Type u₁} [category.{v₁} J] [H : has_limits_of_shape J C] (F : J ⥤ C) : has_limit F :=
has_limits_of_shape.has_limit F

@[priority 100] -- see Note [lower instance priority]
instance has_limits_of_shape_of_has_limits
  {J : Type u₁} [category.{v₁} J] [H : has_limits_of_size.{v₁ u₁} C] : has_limits_of_shape J C :=
has_limits_of_size.has_limits_of_shape J

/- Interface to the `has_limit` class. -/

/-- An arbitrary choice of limit cone for a functor. -/
def limit.cone (F : J ⥤ C) [has_limit F] : cone F := (get_limit_cone F).cone

/-- An arbitrary choice of limit object of a functor. -/
def limit (F : J ⥤ C) [has_limit F] := (limit.cone F).X

/-- The projection from the limit object to a value of the functor. -/
def limit.π (F : J ⥤ C) [has_limit F] (j : J) : limit F ⟶ F.obj j :=
(limit.cone F).π.app j

@[simp] lemma limit.cone_X {F : J ⥤ C} [has_limit F] :
  (limit.cone F).X = limit F := rfl

@[simp] lemma limit.cone_π {F : J ⥤ C} [has_limit F] :
  (limit.cone F).π.app = limit.π _ := rfl

@[simp, reassoc] lemma limit.w (F : J ⥤ C) [has_limit F] {j j' : J} (f : j ⟶ j') :
  limit.π F j ≫ F.map f = limit.π F j' := (limit.cone F).w f

/-- Evidence that the arbitrary choice of cone provied by `limit.cone F` is a limit cone. -/
def limit.is_limit (F : J ⥤ C) [has_limit F] : is_limit (limit.cone F) :=
(get_limit_cone F).is_limit

/-- The morphism from the cone point of any other cone to the limit object. -/
def limit.lift (F : J ⥤ C) [has_limit F] (c : cone F) : c.X ⟶ limit F :=
(limit.is_limit F).lift c

@[simp] lemma limit.is_limit_lift {F : J ⥤ C} [has_limit F] (c : cone F) :
  (limit.is_limit F).lift c = limit.lift F c := rfl

@[simp, reassoc] lemma limit.lift_π {F : J ⥤ C} [has_limit F] (c : cone F) (j : J) :
  limit.lift F c ≫ limit.π F j = c.π.app j :=
is_limit.fac _ c j

/--
Functoriality of limits.

Usually this morphism should be accessed through `lim.map`,
but may be needed separately when you have specified limits for the source and target functors,
but not necessarily for all functors of shape `J`.
-/
def lim_map {F G : J ⥤ C} [has_limit F] [has_limit G] (α : F ⟶ G) : limit F ⟶ limit G :=
is_limit.map _ (limit.is_limit G) α

@[simp, reassoc] lemma lim_map_π {F G : J ⥤ C} [has_limit F] [has_limit G] (α : F ⟶ G) (j : J) :
  lim_map α ≫ limit.π G j = limit.π F j ≫ α.app j :=
limit.lift_π _ j

/-- The cone morphism from any cone to the arbitrary choice of limit cone. -/
def limit.cone_morphism {F : J ⥤ C} [has_limit F] (c : cone F) :
  c ⟶ limit.cone F :=
(limit.is_limit F).lift_cone_morphism c

@[simp] lemma limit.cone_morphism_hom {F : J ⥤ C} [has_limit F] (c : cone F) :
  (limit.cone_morphism c).hom = limit.lift F c := rfl
lemma limit.cone_morphism_π {F : J ⥤ C} [has_limit F] (c : cone F) (j : J) :
  (limit.cone_morphism c).hom ≫ limit.π F j = c.π.app j :=
by simp

@[simp, reassoc] lemma limit.cone_point_unique_up_to_iso_hom_comp {F : J ⥤ C} [has_limit F]
  {c : cone F} (hc : is_limit c) (j : J) :
  (is_limit.cone_point_unique_up_to_iso hc (limit.is_limit _)).hom ≫ limit.π F j = c.π.app j :=
is_limit.cone_point_unique_up_to_iso_hom_comp _ _ _

@[simp, reassoc] lemma limit.cone_point_unique_up_to_iso_inv_comp {F : J ⥤ C} [has_limit F]
  {c : cone F} (hc : is_limit c) (j : J) :
  (is_limit.cone_point_unique_up_to_iso (limit.is_limit _) hc).inv ≫ limit.π F j = c.π.app j :=
is_limit.cone_point_unique_up_to_iso_inv_comp _ _ _

lemma limit.exists_unique {F : J ⥤ C} [has_limit F] (t : cone F) :
  ∃! (l : t.X ⟶ limit F), ∀ j, l ≫ limit.π F j = t.π.app j :=
(limit.is_limit F).exists_unique _

/--
Given any other limit cone for `F`, the chosen `limit F` is isomorphic to the cone point.
-/
def limit.iso_limit_cone {F : J ⥤ C} [has_limit F] (t : limit_cone F) :
  limit F ≅ t.cone.X :=
is_limit.cone_point_unique_up_to_iso (limit.is_limit F) t.is_limit

@[simp, reassoc] lemma limit.iso_limit_cone_hom_π
  {F : J ⥤ C} [has_limit F] (t : limit_cone F) (j : J) :
  (limit.iso_limit_cone t).hom ≫ t.cone.π.app j = limit.π F j :=
by { dsimp [limit.iso_limit_cone, is_limit.cone_point_unique_up_to_iso], tidy, }

@[simp, reassoc] lemma limit.iso_limit_cone_inv_π
  {F : J ⥤ C} [has_limit F] (t : limit_cone F) (j : J) :
  (limit.iso_limit_cone t).inv ≫ limit.π F j = t.cone.π.app j :=
by { dsimp [limit.iso_limit_cone, is_limit.cone_point_unique_up_to_iso], tidy, }

@[ext] lemma limit.hom_ext {F : J ⥤ C} [has_limit F] {X : C} {f f' : X ⟶ limit F}
  (w : ∀ j, f ≫ limit.π F j = f' ≫ limit.π F j) : f = f' :=
(limit.is_limit F).hom_ext w

@[simp] lemma limit.lift_map {F G : J ⥤ C} [has_limit F] [has_limit G] (c : cone F) (α : F ⟶ G) :
  limit.lift F c ≫ lim_map α = limit.lift G ((cones.postcompose α).obj c) :=
by { ext, rw [assoc, lim_map_π, limit.lift_π_assoc, limit.lift_π], refl }

@[simp] lemma limit.lift_cone {F : J ⥤ C} [has_limit F] :
  limit.lift F (limit.cone F) = 𝟙 (limit F) :=
(limit.is_limit _).lift_self

/--
The isomorphism (in `Type`) between
morphisms from a specified object `W` to the limit object,
and cones with cone point `W`.
-/
def limit.hom_iso (F : J ⥤ C) [has_limit F] (W : C) :
  ulift.{u₁} (W ⟶ limit F : Type v) ≅ (F.cones.obj (op W)) :=
(limit.is_limit F).hom_iso W

@[simp] lemma limit.hom_iso_hom (F : J ⥤ C) [has_limit F] {W : C} (f : ulift (W ⟶ limit F)) :
  (limit.hom_iso F W).hom f = (const J).map f.down ≫ (limit.cone F).π :=
(limit.is_limit F).hom_iso_hom f

/--
The isomorphism (in `Type`) between
morphisms from a specified object `W` to the limit object,
and an explicit componentwise description of cones with cone point `W`.
-/
def limit.hom_iso' (F : J ⥤ C) [has_limit F] (W : C) :
  ulift.{u₁} ((W ⟶ limit F) : Type v) ≅
    { p : Π j, W ⟶ F.obj j // ∀ {j j' : J} (f : j ⟶ j'), p j ≫ F.map f = p j' } :=
(limit.is_limit F).hom_iso' W

lemma limit.lift_extend {F : J ⥤ C} [has_limit F] (c : cone F) {X : C} (f : X ⟶ c.X) :
  limit.lift F (c.extend f) = f ≫ limit.lift F c :=
by obviously

/--
If a functor `F` has a limit, so does any naturally isomorphic functor.
-/
lemma has_limit_of_iso {F G : J ⥤ C} [has_limit F] (α : F ≅ G) : has_limit G :=
has_limit.mk
{ cone := (cones.postcompose α.hom).obj (limit.cone F),
  is_limit :=
  { lift := λ s, limit.lift F ((cones.postcompose α.inv).obj s),
    fac' := λ s j,
    begin
      rw [cones.postcompose_obj_π, nat_trans.comp_app, limit.cone_π, ←category.assoc, limit.lift_π],
      simp
    end,
    uniq' := λ s m w,
    begin
      apply limit.hom_ext, intro j,
      rw [limit.lift_π, cones.postcompose_obj_π, nat_trans.comp_app, ←nat_iso.app_inv,
        iso.eq_comp_inv],
      simpa using w j
    end } }

/-- If a functor `G` has the same collection of cones as a functor `F`
which has a limit, then `G` also has a limit. -/
-- See the construction of limits from products and equalizers
-- for an example usage.
lemma has_limit.of_cones_iso {J K : Type u₁} [category.{v₁} J] [category.{v₂} K] (F : J ⥤ C)
  (G : K ⥤ C) (h : F.cones ≅ G.cones) [has_limit F] : has_limit G :=
has_limit.mk ⟨_, is_limit.of_nat_iso ((is_limit.nat_iso (limit.is_limit F)) ≪≫ h)⟩

/--
The limits of `F : J ⥤ C` and `G : J ⥤ C` are isomorphic,
if the functors are naturally isomorphic.
-/
def has_limit.iso_of_nat_iso {F G : J ⥤ C} [has_limit F] [has_limit G] (w : F ≅ G) :
  limit F ≅ limit G :=
is_limit.cone_points_iso_of_nat_iso (limit.is_limit F) (limit.is_limit G) w

@[simp, reassoc]

@[simp, reassoc]
lemma has_limit.iso_of_nat_iso_inv_π {F G : J ⥤ C} [has_limit F] [has_limit G]
  (w : F ≅ G) (j : J) :
  (has_limit.iso_of_nat_iso w).inv ≫ limit.π F j = limit.π G j ≫ w.inv.app j :=
is_limit.cone_points_iso_of_nat_iso_inv_comp _ _ _ _

@[simp, reassoc]
lemma has_limit.lift_iso_of_nat_iso_hom {F G : J ⥤ C} [has_limit F] [has_limit G] (t : cone F)
  (w : F ≅ G) :
  limit.lift F t ≫ (has_limit.iso_of_nat_iso w).hom =
    limit.lift G ((cones.postcompose w.hom).obj _) :=
is_limit.lift_comp_cone_points_iso_of_nat_iso_hom _ _ _

@[simp, reassoc]
lemma has_limit.lift_iso_of_nat_iso_inv {F G : J ⥤ C} [has_limit F] [has_limit G] (t : cone G)
  (w : F ≅ G) :
  limit.lift G t ≫ (has_limit.iso_of_nat_iso w).inv =
    limit.lift F ((cones.postcompose w.inv).obj _) :=
is_limit.lift_comp_cone_points_iso_of_nat_iso_inv _ _ _

/--
The limits of `F : J ⥤ C` and `G : K ⥤ C` are isomorphic,
if there is an equivalence `e : J ≌ K` making the triangle commute up to natural isomorphism.
-/
def has_limit.iso_of_equivalence {F : J ⥤ C} [has_limit F] {G : K ⥤ C} [has_limit G]
   (e : J ≌ K) (w : e.functor ⋙ G ≅ F) : limit F ≅ limit G :=
is_limit.cone_points_iso_of_equivalence (limit.is_limit F) (limit.is_limit G) e w

@[simp]
lemma has_limit.iso_of_equivalence_hom_π {F : J ⥤ C} [has_limit F] {G : K ⥤ C} [has_limit G]
   (e : J ≌ K) (w : e.functor ⋙ G ≅ F) (k : K) :
  (has_limit.iso_of_equivalence e w).hom ≫ limit.π G k =
    limit.π F (e.inverse.obj k) ≫ w.inv.app (e.inverse.obj k) ≫ G.map (e.counit.app k) :=
begin
  simp only [has_limit.iso_of_equivalence, is_limit.cone_points_iso_of_equivalence_hom],
  dsimp,
  simp,
end

@[simp]
lemma has_limit.iso_of_equivalence_inv_π {F : J ⥤ C} [has_limit F] {G : K ⥤ C} [has_limit G]
   (e : J ≌ K) (w : e.functor ⋙ G ≅ F) (j : J) :
  (has_limit.iso_of_equivalence e w).inv ≫ limit.π F j =
    limit.π G (e.functor.obj j) ≫ w.hom.app j :=
begin
  simp only [has_limit.iso_of_equivalence, is_limit.cone_points_iso_of_equivalence_hom],
  dsimp,
  simp,
end

section pre
variables (F) [has_limit F] (E : K ⥤ J) [has_limit (E ⋙ F)]

/--
The canonical morphism from the limit of `F` to the limit of `E ⋙ F`.
-/
def limit.pre : limit F ⟶ limit (E ⋙ F) :=
limit.lift (E ⋙ F) ((limit.cone F).whisker E)

@[simp, reassoc] lemma limit.pre_π (k : K) :
  limit.pre F E ≫ limit.π (E ⋙ F) k = limit.π F (E.obj k) :=
by { erw is_limit.fac, refl }

@[simp] lemma limit.lift_pre (c : cone F) :
  limit.lift F c ≫ limit.pre F E = limit.lift (E ⋙ F) (c.whisker E) :=
by ext; simp

variables {L : Type u₃} [category.{v₃} L]
variables (D : L ⥤ K) [has_limit (D ⋙ E ⋙ F)]

@[simp] lemma limit.pre_pre : limit.pre F E ≫ limit.pre (E ⋙ F) D = limit.pre F (D ⋙ E) :=
by ext j; erw [assoc, limit.pre_π, limit.pre_π, limit.pre_π]; refl

variables {E F}

/---
If we have particular limit cones available for `E ⋙ F` and for `F`,
we obtain a formula for `limit.pre F E`.
-/
lemma limit.pre_eq (s : limit_cone (E ⋙ F)) (t : limit_cone F) :
  limit.pre F E =
    (limit.iso_limit_cone t).hom ≫ s.is_limit.lift ((t.cone).whisker E) ≫
      (limit.iso_limit_cone s).inv :=
by tidy

end pre

section post
variables {D : Type u'} [category.{v'} D]

variables (F) [has_limit F] (G : C ⥤ D) [has_limit (F ⋙ G)]

/--
The canonical morphism from `G` applied to the limit of `F` to the limit of `F ⋙ G`.
-/
def limit.post : G.obj (limit F) ⟶ limit (F ⋙ G) :=
limit.lift (F ⋙ G) (G.map_cone (limit.cone F))

@[simp, reassoc] lemma limit.post_π (j : J) :
  limit.post F G ≫ limit.π (F ⋙ G) j = G.map (limit.π F j) :=
by { erw is_limit.fac, refl }

@[simp] lemma limit.lift_post (c : cone F) :
  G.map (limit.lift F c) ≫ limit.post F G = limit.lift (F ⋙ G) (G.map_cone c) :=
by { ext, rw [assoc, limit.post_π, ←G.map_comp, limit.lift_π, limit.lift_π], refl }

@[simp] lemma limit.post_post
  {E : Type u''} [category.{v''} E] (H : D ⥤ E) [has_limit ((F ⋙ G) ⋙ H)] :
/- H G (limit F) ⟶ H (limit (F ⋙ G)) ⟶ limit ((F ⋙ G) ⋙ H) equals -/
/- H G (limit F) ⟶ limit (F ⋙ (G ⋙ H)) -/
  H.map (limit.post F G) ≫ limit.post (F ⋙ G) H = limit.post F (G ⋙ H) :=
by ext; erw [assoc, limit.post_π, ←H.map_comp, limit.post_π, limit.post_π]; refl

end post

lemma limit.pre_post {D : Type u'} [category.{v'} D]
  (E : K ⥤ J) (F : J ⥤ C) (G : C ⥤ D)
  [has_limit F] [has_limit (E ⋙ F)] [has_limit (F ⋙ G)] [has_limit ((E ⋙ F) ⋙ G)] :
/- G (limit F) ⟶ G (limit (E ⋙ F)) ⟶ limit ((E ⋙ F) ⋙ G) vs -/
/- G (limit F) ⟶ limit F ⋙ G ⟶ limit (E ⋙ (F ⋙ G)) or -/
  G.map (limit.pre F E) ≫ limit.post (E ⋙ F) G = limit.post F G ≫ limit.pre (F ⋙ G) E :=
by ext; erw [assoc, limit.post_π, ←G.map_comp, limit.pre_π, assoc, limit.pre_π, limit.post_π]; refl

open category_theory.equivalence
instance has_limit_equivalence_comp (e : K ≌ J) [has_limit F] : has_limit (e.functor ⋙ F) :=
has_limit.mk { cone := cone.whisker e.functor (limit.cone F),
  is_limit := is_limit.whisker_equivalence (limit.is_limit F) e, }

local attribute [elab_simple] inv_fun_id_assoc -- not entirely sure why this is needed

/--
If a `E ⋙ F` has a limit, and `E` is an equivalence, we can construct a limit of `F`.
-/
lemma has_limit_of_equivalence_comp (e : K ≌ J) [has_limit (e.functor ⋙ F)] : has_limit F :=
begin
  haveI : has_limit (e.inverse ⋙ e.functor ⋙ F) := limits.has_limit_equivalence_comp e.symm,
  apply has_limit_of_iso (e.inv_fun_id_assoc F),
end

-- `has_limit_comp_equivalence` and `has_limit_of_comp_equivalence`
-- are proved in `category_theory/adjunction/limits.lean`.

section lim_functor

variables [has_limits_of_shape J C]

section

/-- `limit F` is functorial in `F`, when `C` has all limits of shape `J`. -/
@[simps obj]
def lim : (J ⥤ C) ⥤ C :=
{ obj := λ F, limit F,
  map := λ F G α, lim_map α,
  map_id' := λ F, by { ext, erw [lim_map_π, category.id_comp, category.comp_id] },
  map_comp' := λ F G H α β,
    by ext; erw [assoc, is_limit.fac, is_limit.fac, ←assoc, is_limit.fac, assoc]; refl }
end

variables {F} {G : J ⥤ C} (α : F ⟶ G)

-- We generate this manually since `simps` gives it a weird name.
@[simp] lemma lim_map_eq_lim_map : lim.map α = lim_map α := rfl

lemma limit.map_pre [has_limits_of_shape K C] (E : K ⥤ J) :
  lim.map α ≫ limit.pre G E = limit.pre F E ≫ lim.map (whisker_left E α) :=
by { ext, simp }

lemma limit.map_pre' [has_limits_of_shape K C]
  (F : J ⥤ C) {E₁ E₂ : K ⥤ J} (α : E₁ ⟶ E₂) :
  limit.pre F E₂ = limit.pre F E₁ ≫ lim.map (whisker_right α F) :=
by ext1; simp [← category.assoc]

lemma limit.id_pre (F : J ⥤ C) :
limit.pre F (𝟭 _) = lim.map (functor.left_unitor F).inv := by tidy

lemma limit.map_post {D : Type u'} [category.{v'} D] [has_limits_of_shape J D] (H : C ⥤ D) :
/- H (limit F) ⟶ H (limit G) ⟶ limit (G ⋙ H) vs
   H (limit F) ⟶ limit (F ⋙ H) ⟶ limit (G ⋙ H) -/
  H.map (lim_map α) ≫ limit.post G H = limit.post F H ≫ lim_map (whisker_right α H) :=
begin
  ext,
  simp only [whisker_right_app, lim_map_π, assoc, limit.post_π_assoc, limit.post_π, ← H.map_comp],
end

/--
The isomorphism between
morphisms from `W` to the cone point of the limit cone for `F`
and cones over `F` with cone point `W`
is natural in `F`.
-/
def lim_yoneda : lim ⋙ yoneda ⋙ (whiskering_right _ _ _).obj ulift_functor.{u₁}
  ≅ category_theory.cones J C :=
nat_iso.of_components (λ F, nat_iso.of_components (λ W, limit.hom_iso F (unop W)) (by tidy))
  (by tidy)

/--The constant functor and limit functor are adjoint to each other-/
def const_lim_adj : (const J : C ⥤ (J ⥤ C)) ⊣ lim :=
{ hom_equiv := λ c g,
  { to_fun := λ f, limit.lift _ ⟨c, f⟩,
    inv_fun := λ f, { app := λ j, f ≫ limit.π _ _ , naturality' := by tidy },
    left_inv := λ _, nat_trans.ext _ _ $ funext $ λ j, limit.lift_π _ _,
    right_inv := λ α, limit.hom_ext $ λ j, limit.lift_π _ _ },
  unit := { app := λ c, limit.lift _ ⟨_, 𝟙 _⟩, naturality' := λ _ _ _, by tidy },
  counit :=
  { app := λ g, { app := limit.π _, naturality' := by tidy },
    naturality' := λ _ _ _, by tidy },
  hom_equiv_unit' := λ c g f, limit.hom_ext $ λ j, by simp,
  hom_equiv_counit' := λ c g f, nat_trans.ext _ _ $ funext $ λ j, rfl }

instance : is_right_adjoint (lim : (J ⥤ C) ⥤ C) := ⟨_, const_lim_adj⟩

end lim_functor

instance lim_map_mono' {F G : J ⥤ C} [has_limits_of_shape J C] (α : F ⟶ G)
  [mono α] : mono (lim_map α) :=
(lim : (J ⥤ C) ⥤ C).map_mono α

instance lim_map_mono {F G : J ⥤ C} [has_limit F] [has_limit G] (α : F ⟶ G)
  [∀ j, mono (α.app j)] : mono (lim_map α) :=
⟨λ Z u v h, limit.hom_ext $ λ j, (cancel_mono (α.app j)).1 $ by simpa using h =≫ limit.π _ j⟩

/--
We can transport limits of shape `J` along an equivalence `J ≌ J'`.
-/
lemma has_limits_of_shape_of_equivalence {J' : Type u₂} [category.{v₂} J']
  (e : J ≌ J') [has_limits_of_shape J C] : has_limits_of_shape J' C :=
by { constructor, intro F, apply has_limit_of_equivalence_comp e, apply_instance }

variable (C)

/--
`has_limits_of_size_shrink.{v u} C` tries to obtain `has_limits_of_size.{v u} C`
from some other `has_limits_of_size C`.
-/
lemma has_limits_of_size_shrink [has_limits_of_size.{(max v₁ v₂) (max u₁ u₂)} C] :
  has_limits_of_size.{v₁ u₁} C :=
⟨λ J hJ, by exactI has_limits_of_shape_of_equivalence
  (ulift_hom_ulift_category.equiv.{v₂ u₂} J).symm⟩

@[priority 100]
instance has_smallest_limits_of_has_limits [has_limits C] :
  has_limits_of_size.{0 0} C := has_limits_of_size_shrink.{0 0} C

end limit

section colimit

/-- `colimit_cocone F` contains a cocone over `F` together with the information that it is a
    colimit. -/
@[nolint has_nonempty_instance]
structure colimit_cocone (F : J ⥤ C) :=
(cocone : cocone F)
(is_colimit : is_colimit cocone)

/-- `has_colimit F` represents the mere existence of a colimit for `F`. -/
class has_colimit (F : J ⥤ C) : Prop :=
mk' :: (exists_colimit : nonempty (colimit_cocone F))

lemma has_colimit.mk {F : J ⥤ C} (d : colimit_cocone F) : has_colimit F :=
⟨nonempty.intro d⟩

/-- Use the axiom of choice to extract explicit `colimit_cocone F` from `has_colimit F`. -/
def get_colimit_cocone (F : J ⥤ C) [has_colimit F] : colimit_cocone F :=
classical.choice $ has_colimit.exists_colimit

variables (J C)

/-- `C` has colimits of shape `J` if there exists a colimit for every functor `F : J ⥤ C`. -/
class has_colimits_of_shape : Prop :=
(has_colimit : Π F : J ⥤ C, has_colimit F . tactic.apply_instance)

/--
`C` has all colimits of size `v₁ u₁` (`has_colimits_of_size.{v₁ u₁} C`)
if it has colimits of every shape `J : Type u₁` with `[category.{v₁} J]`.
-/
class has_colimits_of_size (C : Type u) [category.{v} C] : Prop :=
(has_colimits_of_shape :
  Π (J : Type u₁) [𝒥 : category.{v₁} J], has_colimits_of_shape J C . tactic.apply_instance)

/--
`C` has all (small) colimits if it has colimits of every shape that is as big as its hom-sets.
-/
abbreviation has_colimits (C : Type u) [category.{v} C] : Prop := has_colimits_of_size.{v v} C

lemma has_colimits.has_colimits_of_shape {C : Type u} [category.{v} C] [has_colimits C]
  (J : Type v) [category.{v} J] :
  has_colimits_of_shape J C := has_colimits_of_size.has_colimits_of_shape J

variables {J C}

@[priority 100] -- see Note [lower instance priority]
instance has_colimit_of_has_colimits_of_shape
  {J : Type u₁} [category.{v₁} J] [H : has_colimits_of_shape J C] (F : J ⥤ C) : has_colimit F :=
has_colimits_of_shape.has_colimit F

@[priority 100] -- see Note [lower instance priority]
instance has_colimits_of_shape_of_has_colimits_of_size {J : Type u₁} [category.{v₁} J]
  [H : has_colimits_of_size.{v₁ u₁} C] : has_colimits_of_shape J C :=
has_colimits_of_size.has_colimits_of_shape J

/- Interface to the `has_colimit` class. -/

/-- An arbitrary choice of colimit cocone of a functor. -/
def colimit.cocone (F : J ⥤ C) [has_colimit F] : cocone F := (get_colimit_cocone F).cocone

/-- An arbitrary choice of colimit object of a functor. -/
def colimit (F : J ⥤ C) [has_colimit F] := (colimit.cocone F).X

/-- The coprojection from a value of the functor to the colimit object. -/
def colimit.ι (F : J ⥤ C) [has_colimit F] (j : J) : F.obj j ⟶ colimit F :=
(colimit.cocone F).ι.app j

@[simp] lemma colimit.cocone_ι {F : J ⥤ C} [has_colimit F] (j : J) :
  (colimit.cocone F).ι.app j = colimit.ι _ j := rfl

@[simp] lemma colimit.cocone_X {F : J ⥤ C} [has_colimit F] :
  (colimit.cocone F).X = colimit F := rfl

@[simp, reassoc] lemma colimit.w (F : J ⥤ C) [has_colimit F] {j j' : J} (f : j ⟶ j') :
  F.map f ≫ colimit.ι F j' = colimit.ι F j := (colimit.cocone F).w f

/-- Evidence that the arbitrary choice of cocone is a colimit cocone. -/
def colimit.is_colimit (F : J ⥤ C) [has_colimit F] : is_colimit (colimit.cocone F) :=
(get_colimit_cocone F).is_colimit

/-- The morphism from the colimit object to the cone point of any other cocone. -/
def colimit.desc (F : J ⥤ C) [has_colimit F] (c : cocone F) : colimit F ⟶ c.X :=
(colimit.is_colimit F).desc c

@[simp] lemma colimit.is_colimit_desc {F : J ⥤ C} [has_colimit F] (c : cocone F) :
  (colimit.is_colimit F).desc c = colimit.desc F c := rfl

/--
We have lots of lemmas describing how to simplify `colimit.ι F j ≫ _`,
and combined with `colimit.ext` we rely on these lemmas for many calculations.

However, since `category.assoc` is a `@[simp]` lemma, often expressions are
right associated, and it's hard to apply these lemmas about `colimit.ι`.

We thus use `reassoc` to define additional `@[simp]` lemmas, with an arbitrary extra morphism.
(see `tactic/reassoc_axiom.lean`)
 -/
@[simp, reassoc] lemma colimit.ι_desc {F : J ⥤ C} [has_colimit F] (c : cocone F) (j : J) :
  colimit.ι F j ≫ colimit.desc F c = c.ι.app j :=
is_colimit.fac _ c j

/--
Functoriality of colimits.

Usually this morphism should be accessed through `colim.map`,
but may be needed separately when you have specified colimits for the source and target functors,
but not necessarily for all functors of shape `J`.
-/
def colim_map {F G : J ⥤ C} [has_colimit F] [has_colimit G] (α : F ⟶ G) : colimit F ⟶ colimit G :=
is_colimit.map (colimit.is_colimit F) _ α

@[simp, reassoc]
lemma ι_colim_map {F G : J ⥤ C} [has_colimit F] [has_colimit G] (α : F ⟶ G) (j : J) :
  colimit.ι F j ≫ colim_map α = α.app j ≫ colimit.ι G j :=
colimit.ι_desc _ j

/-- The cocone morphism from the arbitrary choice of colimit cocone to any cocone. -/
def colimit.cocone_morphism {F : J ⥤ C} [has_colimit F] (c : cocone F) :
  (colimit.cocone F) ⟶ c :=
(colimit.is_colimit F).desc_cocone_morphism c

@[simp] lemma colimit.cocone_morphism_hom {F : J ⥤ C} [has_colimit F] (c : cocone F) :
  (colimit.cocone_morphism c).hom = colimit.desc F c := rfl
lemma colimit.ι_cocone_morphism {F : J ⥤ C} [has_colimit F] (c : cocone F) (j : J) :
  colimit.ι F j ≫ (colimit.cocone_morphism c).hom = c.ι.app j :=
by simp

@[simp, reassoc] lemma colimit.comp_cocone_point_unique_up_to_iso_hom {F : J ⥤ C} [has_colimit F]
  {c : cocone F} (hc : is_colimit c) (j : J) :
  colimit.ι F j ≫ (is_colimit.cocone_point_unique_up_to_iso (colimit.is_colimit _) hc).hom =
    c.ι.app j :=
is_colimit.comp_cocone_point_unique_up_to_iso_hom _ _ _

@[simp, reassoc] lemma colimit.comp_cocone_point_unique_up_to_iso_inv {F : J ⥤ C} [has_colimit F]
  {c : cocone F} (hc : is_colimit c) (j : J) :
  colimit.ι F j ≫ (is_colimit.cocone_point_unique_up_to_iso hc (colimit.is_colimit _)).inv =
    c.ι.app j :=
is_colimit.comp_cocone_point_unique_up_to_iso_inv _ _ _

lemma colimit.exists_unique {F : J ⥤ C} [has_colimit F] (t : cocone F) :
  ∃! (d : colimit F ⟶ t.X), ∀ j, colimit.ι F j ≫ d = t.ι.app j :=
(colimit.is_colimit F).exists_unique _

/--
Given any other colimit cocone for `F`, the chosen `colimit F` is isomorphic to the cocone point.
-/
def colimit.iso_colimit_cocone {F : J ⥤ C} [has_colimit F] (t : colimit_cocone F) :
  colimit F ≅ t.cocone.X :=
is_colimit.cocone_point_unique_up_to_iso (colimit.is_colimit F) t.is_colimit

@[simp, reassoc] lemma colimit.iso_colimit_cocone_ι_hom
  {F : J ⥤ C} [has_colimit F] (t : colimit_cocone F) (j : J) :
  colimit.ι F j ≫ (colimit.iso_colimit_cocone t).hom = t.cocone.ι.app j :=
by { dsimp [colimit.iso_colimit_cocone, is_colimit.cocone_point_unique_up_to_iso], tidy, }

@[simp, reassoc] lemma colimit.iso_colimit_cocone_ι_inv
  {F : J ⥤ C} [has_colimit F] (t : colimit_cocone F) (j : J) :
  t.cocone.ι.app j ≫ (colimit.iso_colimit_cocone t).inv = colimit.ι F j :=
by { dsimp [colimit.iso_colimit_cocone, is_colimit.cocone_point_unique_up_to_iso], tidy, }

@[ext] lemma colimit.hom_ext {F : J ⥤ C} [has_colimit F] {X : C} {f f' : colimit F ⟶ X}
  (w : ∀ j, colimit.ι F j ≫ f = colimit.ι F j ≫ f') : f = f' :=
(colimit.is_colimit F).hom_ext w

@[simp] lemma colimit.desc_cocone {F : J ⥤ C} [has_colimit F] :
  colimit.desc F (colimit.cocone F) = 𝟙 (colimit F) :=
(colimit.is_colimit _).desc_self

/--
The isomorphism (in `Type`) between
morphisms from the colimit object to a specified object `W`,
and cocones with cone point `W`.
-/
def colimit.hom_iso (F : J ⥤ C) [has_colimit F] (W : C) :
  ulift.{u₁} (colimit F ⟶ W : Type v) ≅ (F.cocones.obj W) :=
(colimit.is_colimit F).hom_iso W

@[simp] lemma colimit.hom_iso_hom (F : J ⥤ C) [has_colimit F] {W : C} (f : ulift (colimit F ⟶ W)) :
  (colimit.hom_iso F W).hom f = (colimit.cocone F).ι ≫ (const J).map f.down :=
(colimit.is_colimit F).hom_iso_hom f

/--
The isomorphism (in `Type`) between
morphisms from the colimit object to a specified object `W`,
and an explicit componentwise description of cocones with cone point `W`.
-/
def colimit.hom_iso' (F : J ⥤ C) [has_colimit F] (W : C) :
  ulift.{u₁} ((colimit F ⟶ W) : Type v) ≅
    { p : Π j, F.obj j ⟶ W // ∀ {j j'} (f : j ⟶ j'), F.map f ≫ p j' = p j } :=
(colimit.is_colimit F).hom_iso' W

lemma colimit.desc_extend (F : J ⥤ C) [has_colimit F] (c : cocone F) {X : C} (f : c.X ⟶ X) :
  colimit.desc F (c.extend f) = colimit.desc F c ≫ f :=
begin
  ext1, rw [←category.assoc], simp
end

/--
If `F` has a colimit, so does any naturally isomorphic functor.
-/
-- This has the isomorphism pointing in the opposite direction than in `has_limit_of_iso`.
-- This is intentional; it seems to help with elaboration.
lemma has_colimit_of_iso {F G : J ⥤ C} [has_colimit F] (α : G ≅ F) : has_colimit G :=
has_colimit.mk
{ cocone := (cocones.precompose α.hom).obj (colimit.cocone F),
  is_colimit :=
  { desc := λ s, colimit.desc F ((cocones.precompose α.inv).obj s),
    fac' := λ s j,
    begin
      rw [cocones.precompose_obj_ι, nat_trans.comp_app, colimit.cocone_ι],
      rw [category.assoc, colimit.ι_desc, ←nat_iso.app_hom, ←iso.eq_inv_comp], refl
    end,
    uniq' := λ s m w,
    begin
      apply colimit.hom_ext, intro j,
      rw [colimit.ι_desc, cocones.precompose_obj_ι, nat_trans.comp_app, ←nat_iso.app_inv,
        iso.eq_inv_comp],
      simpa using w j
    end } }

/-- If a functor `G` has the same collection of cocones as a functor `F`
which has a colimit, then `G` also has a colimit. -/
lemma has_colimit.of_cocones_iso {K : Type u₁} [category.{v₂} K] (F : J ⥤ C)
  (G : K ⥤ C) (h : F.cocones ≅ G.cocones)
    [has_colimit F] : has_colimit G :=
has_colimit.mk ⟨_, is_colimit.of_nat_iso (is_colimit.nat_iso (colimit.is_colimit F) ≪≫ h)⟩

/--
The colimits of `F : J ⥤ C` and `G : J ⥤ C` are isomorphic,
if the functors are naturally isomorphic.
-/
def has_colimit.iso_of_nat_iso {F G : J ⥤ C} [has_colimit F] [has_colimit G] (w : F ≅ G) :
  colimit F ≅ colimit G :=
is_colimit.cocone_points_iso_of_nat_iso (colimit.is_colimit F) (colimit.is_colimit G) w

@[simp, reassoc]
lemma has_colimit.iso_of_nat_iso_ι_hom {F G : J ⥤ C} [has_colimit F] [has_colimit G]
  (w : F ≅ G) (j : J) :
  colimit.ι F j ≫ (has_colimit.iso_of_nat_iso w).hom = w.hom.app j ≫ colimit.ι G j :=
is_colimit.comp_cocone_points_iso_of_nat_iso_hom _ _ _ _

@[simp, reassoc]
lemma has_colimit.iso_of_nat_iso_ι_inv {F G : J ⥤ C} [has_colimit F] [has_colimit G]
  (w : F ≅ G) (j : J) :
  colimit.ι G j ≫ (has_colimit.iso_of_nat_iso w).inv = w.inv.app j ≫ colimit.ι F j :=
is_colimit.comp_cocone_points_iso_of_nat_iso_inv _ _ _ _

@[simp, reassoc]
lemma has_colimit.iso_of_nat_iso_hom_desc {F G : J ⥤ C} [has_colimit F] [has_colimit G]
  (t : cocone G) (w : F ≅ G) :
  (has_colimit.iso_of_nat_iso w).hom ≫ colimit.desc G t =
    colimit.desc F ((cocones.precompose w.hom).obj _) :=
is_colimit.cocone_points_iso_of_nat_iso_hom_desc _ _ _

@[simp, reassoc]
lemma has_colimit.iso_of_nat_iso_inv_desc {F G : J ⥤ C} [has_colimit F] [has_colimit G]
  (t : cocone F) (w : F ≅ G) :
  (has_colimit.iso_of_nat_iso w).inv ≫ colimit.desc F t =
    colimit.desc G ((cocones.precompose w.inv).obj _) :=
is_colimit.cocone_points_iso_of_nat_iso_inv_desc _ _ _

/--
The colimits of `F : J ⥤ C` and `G : K ⥤ C` are isomorphic,
if there is an equivalence `e : J ≌ K` making the triangle commute up to natural isomorphism.
-/
def has_colimit.iso_of_equivalence {F : J ⥤ C} [has_colimit F] {G : K ⥤ C} [has_colimit G]
   (e : J ≌ K) (w : e.functor ⋙ G ≅ F) : colimit F ≅ colimit G :=
is_colimit.cocone_points_iso_of_equivalence (colimit.is_colimit F) (colimit.is_colimit G) e w

@[simp]
lemma has_colimit.iso_of_equivalence_hom_π {F : J ⥤ C} [has_colimit F] {G : K ⥤ C} [has_colimit G]
   (e : J ≌ K) (w : e.functor ⋙ G ≅ F) (j : J) :
  colimit.ι F j ≫ (has_colimit.iso_of_equivalence e w).hom =
     F.map (e.unit.app j) ≫ w.inv.app _ ≫ colimit.ι G _ :=
begin
  simp [has_colimit.iso_of_equivalence, is_colimit.cocone_points_iso_of_equivalence_inv],
  dsimp,
  simp,
end

@[simp]
lemma has_colimit.iso_of_equivalence_inv_π {F : J ⥤ C} [has_colimit F] {G : K ⥤ C} [has_colimit G]
   (e : J ≌ K) (w : e.functor ⋙ G ≅ F) (k : K) :
  colimit.ι G k ≫ (has_colimit.iso_of_equivalence e w).inv =
     G.map (e.counit_inv.app k) ≫ w.hom.app (e.inverse.obj k) ≫ colimit.ι F (e.inverse.obj k) :=
begin
  simp [has_colimit.iso_of_equivalence, is_colimit.cocone_points_iso_of_equivalence_inv],
  dsimp,
  simp,
end

section pre
variables (F) [has_colimit F] (E : K ⥤ J) [has_colimit (E ⋙ F)]

/--
The canonical morphism from the colimit of `E ⋙ F` to the colimit of `F`.
-/
def colimit.pre : colimit (E ⋙ F) ⟶ colimit F :=
colimit.desc (E ⋙ F) ((colimit.cocone F).whisker E)

@[simp, reassoc] lemma colimit.ι_pre (k : K) :
  colimit.ι (E ⋙ F) k ≫ colimit.pre F E = colimit.ι F (E.obj k) :=
by { erw is_colimit.fac, refl, }

@[simp, reassoc] lemma colimit.pre_desc (c : cocone F) :
  colimit.pre F E ≫ colimit.desc F c = colimit.desc (E ⋙ F) (c.whisker E) :=
by ext; rw [←assoc, colimit.ι_pre]; simp

variables {L : Type u₃} [category.{v₃} L]
variables (D : L ⥤ K) [has_colimit (D ⋙ E ⋙ F)]

@[simp] lemma colimit.pre_pre : colimit.pre (E ⋙ F) D ≫ colimit.pre F E = colimit.pre F (D ⋙ E) :=
begin
  ext j,
  rw [←assoc, colimit.ι_pre, colimit.ι_pre],
  letI : has_colimit ((D ⋙ E) ⋙ F) := show has_colimit (D ⋙ E ⋙ F), by apply_instance,
  exact (colimit.ι_pre F (D ⋙ E) j).symm
end

variables {E F}

/---
If we have particular colimit cocones available for `E ⋙ F` and for `F`,
we obtain a formula for `colimit.pre F E`.
-/
lemma colimit.pre_eq (s : colimit_cocone (E ⋙ F)) (t : colimit_cocone F) :
  colimit.pre F E =
    (colimit.iso_colimit_cocone s).hom ≫ s.is_colimit.desc ((t.cocone).whisker E) ≫
      (colimit.iso_colimit_cocone t).inv :=
by tidy

end pre

section post
variables {D : Type u'} [category.{v'} D]

variables (F) [has_colimit F] (G : C ⥤ D) [has_colimit (F ⋙ G)]

/--
The canonical morphism from `G` applied to the colimit of `F ⋙ G`
to `G` applied to the colimit of `F`.
-/
def colimit.post : colimit (F ⋙ G) ⟶ G.obj (colimit F) :=
colimit.desc (F ⋙ G) (G.map_cocone (colimit.cocone F))

@[simp, reassoc] lemma colimit.ι_post (j : J) :
  colimit.ι (F ⋙ G) j ≫ colimit.post F G  = G.map (colimit.ι F j) :=
by { erw is_colimit.fac, refl, }

@[simp] lemma colimit.post_desc (c : cocone F) :
  colimit.post F G ≫ G.map (colimit.desc F c) = colimit.desc (F ⋙ G) (G.map_cocone c) :=
by { ext, rw [←assoc, colimit.ι_post, ←G.map_comp, colimit.ι_desc, colimit.ι_desc], refl }

@[simp] lemma colimit.post_post
  {E : Type u''} [category.{v''} E] (H : D ⥤ E) [has_colimit ((F ⋙ G) ⋙ H)] :
/- H G (colimit F) ⟶ H (colimit (F ⋙ G)) ⟶ colimit ((F ⋙ G) ⋙ H) equals -/
/- H G (colimit F) ⟶ colimit (F ⋙ (G ⋙ H)) -/
  colimit.post (F ⋙ G) H ≫ H.map (colimit.post F G) = colimit.post F (G ⋙ H) :=
begin
  ext,
  rw [←assoc, colimit.ι_post, ←H.map_comp, colimit.ι_post],
  exact (colimit.ι_post F (G ⋙ H) j).symm
end

end post

lemma colimit.pre_post {D : Type u'} [category.{v'} D]
  (E : K ⥤ J) (F : J ⥤ C) (G : C ⥤ D)
  [has_colimit F] [has_colimit (E ⋙ F)] [has_colimit (F ⋙ G)] [H : has_colimit ((E ⋙ F) ⋙ G)] :
/- G (colimit F) ⟶ G (colimit (E ⋙ F)) ⟶ colimit ((E ⋙ F) ⋙ G) vs -/
/- G (colimit F) ⟶ colimit F ⋙ G ⟶ colimit (E ⋙ (F ⋙ G)) or -/
  colimit.post (E ⋙ F) G ≫ G.map (colimit.pre F E) =
    (@@colimit.pre _ _ _ (F ⋙ G) _ E H ≫ colimit.post F G : _) :=
begin
  ext,
  rw [←assoc, colimit.ι_post, ←G.map_comp, colimit.ι_pre, ←assoc],
  letI : has_colimit (E ⋙ F ⋙ G) := show has_colimit ((E ⋙ F) ⋙ G), by apply_instance,
  erw [colimit.ι_pre (F ⋙ G) E j, colimit.ι_post]
end

open category_theory.equivalence
instance has_colimit_equivalence_comp (e : K ≌ J) [has_colimit F] : has_colimit (e.functor ⋙ F) :=
has_colimit.mk { cocone := cocone.whisker e.functor (colimit.cocone F),
  is_colimit := is_colimit.whisker_equivalence (colimit.is_colimit F) e, }

/--
If a `E ⋙ F` has a colimit, and `E` is an equivalence, we can construct a colimit of `F`.
-/
lemma has_colimit_of_equivalence_comp (e : K ≌ J) [has_colimit (e.functor ⋙ F)] : has_colimit F :=
begin
  haveI : has_colimit (e.inverse ⋙ e.functor ⋙ F) := limits.has_colimit_equivalence_comp e.symm,
  apply has_colimit_of_iso (e.inv_fun_id_assoc F).symm,
end

section colim_functor

variables [has_colimits_of_shape J C]

section
local attribute [simp] colim_map

/-- `colimit F` is functorial in `F`, when `C` has all colimits of shape `J`. -/
@[simps obj]
def colim : (J ⥤ C) ⥤ C :=
{ obj := λ F, colimit F,
  map := λ F G α, colim_map α,
  map_id' := λ F, by { ext, erw [ι_colim_map, id_comp, comp_id] },
  map_comp' := λ F G H α β,
    by { ext, erw [←assoc, is_colimit.fac, is_colimit.fac, assoc, is_colimit.fac, ←assoc], refl } }

end

variables {F} {G : J ⥤ C} (α : F ⟶ G)

@[simp, reassoc] lemma colimit.ι_map (j : J) :
  colimit.ι F j ≫ colim.map α = α.app j ≫ colimit.ι G j :=
by apply is_colimit.fac

@[simp] lemma colimit.map_desc (c : cocone G) :
  colim.map α ≫ colimit.desc G c = colimit.desc F ((cocones.precompose α).obj c) :=
by ext; rw [←assoc, colimit.ι_map, assoc, colimit.ι_desc, colimit.ι_desc]; refl

lemma colimit.pre_map [has_colimits_of_shape K C] (E : K ⥤ J) :
  colimit.pre F E ≫ colim.map α = colim.map (whisker_left E α) ≫ colimit.pre G E :=
by ext; rw [←assoc, colimit.ι_pre, colimit.ι_map, ←assoc, colimit.ι_map, assoc, colimit.ι_pre]; refl

lemma colimit.pre_map' [has_colimits_of_shape K C]
  (F : J ⥤ C) {E₁ E₂ : K ⥤ J} (α : E₁ ⟶ E₂) :
  colimit.pre F E₁ = colim.map (whisker_right α F) ≫ colimit.pre F E₂ :=
by ext1; simp [← category.assoc]

lemma colimit.pre_id (F : J ⥤ C) :
colimit.pre F (𝟭 _) = colim.map (functor.left_unitor F).hom := by tidy

lemma colimit.map_post {D : Type u'} [category.{v'} D] [has_colimits_of_shape J D] (H : C ⥤ D) :
/- H (colimit F) ⟶ H (colimit G) ⟶ colimit (G ⋙ H) vs
   H (colimit F) ⟶ colimit (F ⋙ H) ⟶ colimit (G ⋙ H) -/
  colimit.post F H ≫ H.map (colim.map α) = colim.map (whisker_right α H) ≫ colimit.post G H:=
begin
  ext,
  rw [←assoc, colimit.ι_post, ←H.map_comp, colimit.ι_map, H.map_comp],
  rw [←assoc, colimit.ι_map, assoc, colimit.ι_post],
  refl
end

/--
The isomorphism between
morphisms from the cone point of the colimit cocone for `F` to `W`
and cocones over `F` with cone point `W`
is natural in `F`.
-/
def colim_coyoneda : colim.op ⋙ coyoneda ⋙ (whiskering_right _ _ _).obj ulift_functor.{u₁} ≅
  category_theory.cocones J C :=
nat_iso.of_components (λ F, nat_iso.of_components (colimit.hom_iso (unop F)) (by tidy))
  (by tidy)

/--
The colimit functor and constant functor are adjoint to each other
-/
def colim_const_adj : (colim : (J ⥤ C) ⥤ C) ⊣ const J :=
{ hom_equiv := λ f c,
  { to_fun := λ g, { app := λ _, colimit.ι _ _ ≫ g, naturality' := by tidy },
    inv_fun := λ g, colimit.desc _ ⟨_, g⟩,
    left_inv := λ _, colimit.hom_ext $ λ j, colimit.ι_desc _ _,
    right_inv := λ _, nat_trans.ext _ _ $ funext $ λ j, colimit.ι_desc _ _ },
  unit := { app := λ g, { app := colimit.ι _, naturality' := by tidy }, naturality' := by tidy },
  counit := { app := λ c, colimit.desc _ ⟨_, 𝟙 _⟩, naturality' := by tidy },
  hom_equiv_unit' := λ _ _ _, nat_trans.ext _ _ $ funext $ λ _ , rfl,
  hom_equiv_counit' := λ _ _ _, colimit.hom_ext $ λ _, by simp }

instance : is_left_adjoint (colim : (J ⥤ C) ⥤ C) := ⟨_, colim_const_adj⟩

end colim_functor

instance colim_map_epi' {F G : J ⥤ C} [has_colimits_of_shape J C] (α : F ⟶ G) [epi α] :
  epi (colim_map α) := (colim : (J ⥤ C) ⥤ C).map_epi α

instance colim_map_epi {F G : J ⥤ C} [has_colimit F] [has_colimit G] (α : F ⟶ G)
  [∀ j, epi (α.app j)] : epi (colim_map α) :=
⟨λ Z u v h, colimit.hom_ext $ λ j, (cancel_epi (α.app j)).1 $ by simpa using colimit.ι _ j ≫= h⟩

/--
We can transport colimits of shape `J` along an equivalence `J ≌ J'`.
-/
lemma has_colimits_of_shape_of_equivalence {J' : Type u₂} [category.{v₂} J']
  (e : J ≌ J') [has_colimits_of_shape J C] : has_colimits_of_shape J' C :=
by { constructor, intro F, apply has_colimit_of_equivalence_comp e, apply_instance }

variable (C)

/--
`has_colimits_of_size_shrink.{v u} C` tries to obtain `has_colimits_of_size.{v u} C`
from some other `has_colimits_of_size C`.
-/
lemma has_colimits_of_size_shrink [has_colimits_of_size.{(max v₁ v₂) (max u₁ u₂)} C] :
  has_colimits_of_size.{v₁ u₁} C :=
⟨λ J hJ, by exactI has_colimits_of_shape_of_equivalence
  (ulift_hom_ulift_category.equiv.{v₂ u₂} J).symm⟩

@[priority 100]
instance has_smallest_colimits_of_has_colimits [has_colimits C] :
  has_colimits_of_size.{0 0} C := has_colimits_of_size_shrink.{0 0} C

end colimit

section opposite

/--
If `t : cone F` is a limit cone, then `t.op : cocone F.op` is a colimit cocone.
-/
def is_limit.op {t : cone F} (P : is_limit t) : is_colimit t.op :=
{ desc := λ s, (P.lift s.unop).op,
  fac' := λ s j, congr_arg quiver.hom.op (P.fac s.unop (unop j)),
  uniq' := λ s m w,
  begin
    rw ← P.uniq s.unop m.unop,
    { refl, },
    { dsimp, intro j, rw ← w, refl, }
  end }

/--
If `t : cocone F` is a colimit cocone, then `t.op : cone F.op` is a limit cone.
-/
def is_colimit.op {t : cocone F} (P : is_colimit t) : is_limit t.op :=
{ lift := λ s, (P.desc s.unop).op,
  fac' := λ s j, congr_arg quiver.hom.op (P.fac s.unop (unop j)),
  uniq' := λ s m w,
  begin
    rw ← P.uniq s.unop m.unop,
    { refl, },
    { dsimp, intro j, rw ← w, refl, }
  end }

/--
If `t : cone F.op` is a limit cone, then `t.unop : cocone F` is a colimit cocone.
-/
def is_limit.unop {t : cone F.op} (P : is_limit t) : is_colimit t.unop :=
{ desc := λ s, (P.lift s.op).unop,
  fac' := λ s j, congr_arg quiver.hom.unop (P.fac s.op (op j)),
  uniq' := λ s m w,
  begin
    rw ← P.uniq s.op m.op,
    { refl, },
    { dsimp, intro j, rw ← w, refl, }
  end }

/--
If `t : cocone F.op` is a colimit cocone, then `t.unop : cone F.` is a limit cone.
-/
def is_colimit.unop {t : cocone F.op} (P : is_colimit t) : is_limit t.unop :=
{ lift := λ s, (P.desc s.op).unop,
  fac' := λ s j, congr_arg quiver.hom.unop (P.fac s.op (op j)),
  uniq' := λ s m w,
  begin
    rw ← P.uniq s.op m.op,
    { refl, },
    { dsimp, intro j, rw ← w, refl, }
  end }

/--
`t : cone F` is a limit cone if and only is `t.op : cocone F.op` is a colimit cocone.
-/
def is_limit_equiv_is_colimit_op {t : cone F} : is_limit t ≃ is_colimit t.op :=
equiv_of_subsingleton_of_subsingleton
  is_limit.op (λ P, P.unop.of_iso_limit (cones.ext (iso.refl _) (by tidy)))

/--
`t : cocone F` is a colimit cocone if and only is `t.op : cone F.op` is a limit cone.
-/
def is_colimit_equiv_is_limit_op {t : cocone F} : is_colimit t ≃ is_limit t.op :=
equiv_of_subsingleton_of_subsingleton
  is_colimit.op (λ P, P.unop.of_iso_colimit (cocones.ext (iso.refl _) (by tidy)))

end opposite

end category_theory.limits


/-
Copyright (c) 2015 Nathaniel Thomas. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nathaniel Thomas, Jeremy Avigad, Johannes Hölzl, Mario Carneiro
-/
import algebra.module.linear_map
import algebra.module.equiv
import group_theory.group_action.sub_mul_action
/-!

# Submodules of a module

In this file we define

* `submodule R M` : a subset of a `module` `M` that contains zero and is closed with respect to
  addition and scalar multiplication.

* `subspace k M` : an abbreviation for `submodule` assuming that `k` is a `field`.

## Tags

submodule, subspace, linear map
-/

open function
open_locale big_operators

universes u'' u' u v w
variables {G : Type u''} {S : Type u'} {R : Type u} {M : Type v} {ι : Type w}

set_option old_structure_cmd true

/-- A submodule of a module is one which is closed under vector operations.
  This is a sufficient condition for the subset of vectors in the submodule
  to themselves form a module. -/
structure submodule (R : Type u) (M : Type v) [semiring R]
  [add_comm_monoid M] [module R M] extends add_submonoid M, sub_mul_action R M : Type v.

/-- Reinterpret a `submodule` as an `add_submonoid`. -/
add_decl_doc submodule.to_add_submonoid

/-- Reinterpret a `submodule` as an `sub_mul_action`. -/
add_decl_doc submodule.to_sub_mul_action

namespace submodule

variables [semiring R] [add_comm_monoid M] [module R M]

instance : set_like (submodule R M) M :=
{ coe := submodule.carrier,
  coe_injective' := λ p q h, by cases p; cases q; congr' }

instance : add_submonoid_class (submodule R M) M :=
{ zero_mem := zero_mem',
  add_mem := add_mem' }

@[simp] theorem mem_to_add_submonoid (p : submodule R M) (x : M) : x ∈ p.to_add_submonoid ↔ x ∈ p :=
iff.rfl

variables {p q : submodule R M}

@[simp]
lemma mem_mk {S : set M} {x : M} (h₁ h₂ h₃) : x ∈ (⟨S, h₁, h₂, h₃⟩ : submodule R M) ↔ x ∈ S :=
iff.rfl

@[simp] lemma coe_set_mk (S : set M) (h₁ h₂ h₃) :
  ((⟨S, h₁, h₂, h₃⟩ : submodule R M) : set M) = S := rfl

@[simp]
lemma mk_le_mk {S S' : set M} (h₁ h₂ h₃ h₁' h₂' h₃') :
  (⟨S, h₁, h₂, h₃⟩ : submodule R M) ≤ (⟨S', h₁', h₂', h₃'⟩ : submodule R M) ↔ S ⊆ S' := iff.rfl

@[ext] theorem ext (h : ∀ x, x ∈ p ↔ x ∈ q) : p = q := set_like.ext h

/-- Copy of a submodule with a new `carrier` equal to the old one. Useful to fix definitional
equalities. -/
protected def copy (p : submodule R M) (s : set M) (hs : s = ↑p) : submodule R M :=
{ carrier := s,
  zero_mem' := hs.symm ▸ p.zero_mem',
  add_mem' := hs.symm ▸ p.add_mem',
  smul_mem' := hs.symm ▸ p.smul_mem' }

@[simp] lemma coe_copy (S : submodule R M) (s : set M) (hs : s = ↑S) :
  (S.copy s hs : set M) = s := rfl

lemma copy_eq (S : submodule R M) (s : set M) (hs : s = ↑S) : S.copy s hs = S :=
set_like.coe_injective hs

theorem to_add_submonoid_injective :
  injective (to_add_submonoid : submodule R M → add_submonoid M) :=
λ p q h, set_like.ext'_iff.2 (show _, from set_like.ext'_iff.1 h)

@[simp] theorem to_add_submonoid_eq : p.to_add_submonoid = q.to_add_submonoid ↔ p = q :=
to_add_submonoid_injective.eq_iff

@[mono] lemma to_add_submonoid_strict_mono :
  strict_mono (to_add_submonoid : submodule R M → add_submonoid M) := λ _ _, id

lemma to_add_submonoid_le : p.to_add_submonoid ≤ q.to_add_submonoid ↔ p ≤ q := iff.rfl

@[mono]
lemma to_add_submonoid_mono : monotone (to_add_submonoid : submodule R M → add_submonoid M) :=
to_add_submonoid_strict_mono.monotone

@[simp] theorem coe_to_add_submonoid (p : submodule R M) :
  (p.to_add_submonoid : set M) = p := rfl

theorem to_sub_mul_action_injective :
  injective (to_sub_mul_action : submodule R M → sub_mul_action R M) :=
λ p q h, set_like.ext'_iff.2 (show _, from set_like.ext'_iff.1 h)

@[simp] theorem to_sub_mul_action_eq : p.to_sub_mul_action = q.to_sub_mul_action ↔ p = q :=
to_sub_mul_action_injective.eq_iff

@[mono] lemma to_sub_mul_action_strict_mono :
  strict_mono (to_sub_mul_action : submodule R M → sub_mul_action R M) := λ _ _, id

@[mono]
lemma to_sub_mul_action_mono : monotone (to_sub_mul_action : submodule R M → sub_mul_action R M) :=
to_sub_mul_action_strict_mono.monotone

@[simp] theorem coe_to_sub_mul_action (p : submodule R M) :
  (p.to_sub_mul_action : set M) = p := rfl

end submodule

namespace submodule

section add_comm_monoid

variables [semiring R] [add_comm_monoid M]

-- We can infer the module structure implicitly from the bundled submodule,
-- rather than via typeclass resolution.
variables {module_M : module R M}
variables {p q : submodule R M}
variables {r : R} {x y : M}

variables (p)
@[simp] lemma mem_carrier : x ∈ p.carrier ↔ x ∈ (p : set M) := iff.rfl

@[simp] lemma zero_mem : (0 : M) ∈ p := p.zero_mem'

lemma add_mem (h₁ : x ∈ p) (h₂ : y ∈ p) : x + y ∈ p := p.add_mem' h₁ h₂

lemma smul_mem (r : R) (h : x ∈ p) : r • x ∈ p := p.smul_mem' r h
lemma smul_of_tower_mem [has_scalar S R] [has_scalar S M] [is_scalar_tower S R M]
  (r : S) (h : x ∈ p) : r • x ∈ p :=
p.to_sub_mul_action.smul_of_tower_mem r h

lemma sum_mem {t : finset ι} {f : ι → M} : (∀c∈t, f c ∈ p) → (∑ i in t, f i) ∈ p :=
p.to_add_submonoid.sum_mem

lemma sum_smul_mem {t : finset ι} {f : ι → M} (r : ι → R)
    (hyp : ∀ c ∈ t, f c ∈ p) : (∑ i in t, r i • f i) ∈ p :=
submodule.sum_mem _ (λ i hi, submodule.smul_mem  _ _ (hyp i hi))

@[simp] lemma smul_mem_iff' [group G] [mul_action G M] [has_scalar G R] [is_scalar_tower G R M]
  (g : G) : g • x ∈ p ↔ x ∈ p :=
p.to_sub_mul_action.smul_mem_iff' g

instance : has_add p := ⟨λx y, ⟨x.1 + y.1, add_mem _ x.2 y.2⟩⟩
instance : has_zero p := ⟨⟨0, zero_mem _⟩⟩
instance : inhabited p := ⟨0⟩
instance [has_scalar S R] [has_scalar S M] [is_scalar_tower S R M] :
  has_scalar S p := ⟨λ c x, ⟨c • x.1, smul_of_tower_mem _ c x.2⟩⟩

instance [has_scalar S R] [has_scalar S M] [is_scalar_tower S R M] : is_scalar_tower S R p :=
p.to_sub_mul_action.is_scalar_tower

instance
  [has_scalar S R] [has_scalar S M] [is_scalar_tower S R M]
  [has_scalar Sᵐᵒᵖ R] [has_scalar Sᵐᵒᵖ M] [is_scalar_tower Sᵐᵒᵖ R M]
  [is_central_scalar S M] : is_central_scalar S p :=
p.to_sub_mul_action.is_central_scalar

protected lemma nonempty : (p : set M).nonempty := ⟨0, p.zero_mem⟩

@[simp] lemma mk_eq_zero {x} (h : x ∈ p) : (⟨x, h⟩ : p) = 0 ↔ x = 0 := subtype.ext_iff_val

variables {p}
@[simp, norm_cast] lemma coe_eq_zero {x : p} : (x : M) = 0 ↔ x = 0 :=
(set_like.coe_eq_coe : (x : M) = (0 : p) ↔ x = 0)
@[simp, norm_cast] lemma coe_add (x y : p) : (↑(x + y) : M) = ↑x + ↑y := rfl
@[simp, norm_cast] lemma coe_zero : ((0 : p) : M) = 0 := rfl
@[norm_cast] lemma coe_smul (r : R) (x : p) : ((r • x : p) : M) = r • ↑x := rfl
@[simp, norm_cast] lemma coe_smul_of_tower [has_scalar S R] [has_scalar S M] [is_scalar_tower S R M]
  (r : S) (x : p) : ((r • x : p) : M) = r • ↑x := rfl
@[simp, norm_cast] lemma coe_mk (x : M) (hx : x ∈ p) : ((⟨x, hx⟩ : p) : M) = x := rfl
@[simp] lemma coe_mem (x : p) : (x : M) ∈ p := x.2

variables (p)

instance : add_comm_monoid p :=
{ add := (+), zero := 0, .. p.to_add_submonoid.to_add_comm_monoid }

instance module' [semiring S] [has_scalar S R] [module S M] [is_scalar_tower S R M] : module S p :=
by refine {smul := (•), ..p.to_sub_mul_action.mul_action', ..};
   { intros, apply set_coe.ext, simp [smul_add, add_smul, mul_smul] }
instance : module R p := p.module'

instance no_zero_smul_divisors [no_zero_smul_divisors R M] : no_zero_smul_divisors R p :=
⟨λ c x h,
  have c = 0 ∨ (x : M) = 0,
  from eq_zero_or_eq_zero_of_smul_eq_zero (congr_arg coe h),
  this.imp_right (@subtype.ext_iff _ _ x 0).mpr⟩

/-- Embedding of a submodule `p` to the ambient space `M`. -/
protected def subtype : p →ₗ[R] M :=
by refine {to_fun := coe, ..}; simp [coe_smul]

theorem subtype_apply (x : p) : p.subtype x = x := rfl

@[simp] lemma coe_subtype : ((submodule.subtype p) : p → M) = coe := rfl

lemma injective_subtype : injective p.subtype := subtype.coe_injective

/-- Note the `add_submonoid` version of this lemma is called `add_submonoid.coe_finset_sum`. -/
@[simp] lemma coe_sum (x : ι → p) (s : finset ι) : ↑(∑ i in s, x i) = ∑ i in s, (x i : M) :=
p.subtype.map_sum

section restrict_scalars
variables (S) [semiring S] [module S M] [module R M] [has_scalar S R] [is_scalar_tower S R M]

/--
`V.restrict_scalars S` is the `S`-submodule of the `S`-module given by restriction of scalars,
corresponding to `V`, an `R`-submodule of the original `R`-module.
-/
def restrict_scalars (V : submodule R M) : submodule S M :=
{ carrier := V,
  zero_mem' := V.zero_mem,
  smul_mem' := λ c m h, V.smul_of_tower_mem c h,
  add_mem' := λ x y hx hy, V.add_mem hx hy }

@[simp]
lemma coe_restrict_scalars (V : submodule R M) : (V.restrict_scalars S : set M) = V :=
rfl

@[simp]
lemma restrict_scalars_mem (V : submodule R M) (m : M) : m ∈ V.restrict_scalars S ↔ m ∈ V :=
iff.refl _

@[simp]
lemma restrict_scalars_self (V : submodule R M) : V.restrict_scalars R = V :=
set_like.coe_injective rfl

variables (R S M)

lemma restrict_scalars_injective :
  function.injective (restrict_scalars S : submodule R M → submodule S M) :=
λ V₁ V₂ h, ext $ set.ext_iff.1 (set_like.ext'_iff.1 h : _)

@[simp] lemma restrict_scalars_inj {V₁ V₂ : submodule R M} :
  restrict_scalars S V₁ = restrict_scalars S V₂ ↔ V₁ = V₂ :=
(restrict_scalars_injective S _ _).eq_iff

/-- Even though `p.restrict_scalars S` has type `submodule S M`, it is still an `R`-module. -/
instance restrict_scalars.orig_module (p : submodule R M) :
  module R (p.restrict_scalars S) :=
(by apply_instance : module R p)

instance (p : submodule R M) : is_scalar_tower S R (p.restrict_scalars S) :=
{ smul_assoc := λ r s x, subtype.ext $ smul_assoc r s (x : M) }

/-- `restrict_scalars S` is an embedding of the lattice of `R`-submodules into
the lattice of `S`-submodules. -/
@[simps]
def restrict_scalars_embedding : submodule R M ↪o submodule S M :=
{ to_fun := restrict_scalars S,
  inj' := restrict_scalars_injective S R M,
  map_rel_iff' := λ p q, by simp [set_like.le_def] }

/-- Turning `p : submodule R M` into an `S`-submodule gives the same module structure
as turning it into a type and adding a module structure. -/
@[simps {simp_rhs := tt}]
def restrict_scalars_equiv (p : submodule R M) : p.restrict_scalars S ≃ₗ[R] p :=
{ to_fun := id, inv_fun := id, map_smul' := λ c x, rfl, .. add_equiv.refl p }

end restrict_scalars

end add_comm_monoid

section add_comm_group

variables [ring R] [add_comm_group M]
variables {module_M : module R M}
variables (p p' : submodule R M)
variables {r : R} {x y : M}

instance [module R M] : add_subgroup_class (submodule R M) M :=
{ neg_mem := λ p x, p.to_sub_mul_action.neg_mem,
  .. submodule.add_submonoid_class }

lemma neg_mem (hx : x ∈ p) : -x ∈ p := neg_mem_class.neg_mem hx

/-- Reinterpret a submodule as an additive subgroup. -/
def to_add_subgroup : add_subgroup M :=
{ neg_mem' := λ _, p.neg_mem , .. p.to_add_submonoid }

@[simp] lemma coe_to_add_subgroup : (p.to_add_subgroup : set M) = p := rfl

@[simp] lemma mem_to_add_subgroup : x ∈ p.to_add_subgroup ↔ x ∈ p := iff.rfl

include module_M

theorem to_add_subgroup_injective : injective (to_add_subgroup : submodule R M → add_subgroup M)
| p q h := set_like.ext (set_like.ext_iff.1 h : _)

@[simp] theorem to_add_subgroup_eq : p.to_add_subgroup = p'.to_add_subgroup ↔ p = p' :=
to_add_subgroup_injective.eq_iff

@[mono] lemma to_add_subgroup_strict_mono :
  strict_mono (to_add_subgroup : submodule R M → add_subgroup M) := λ _ _, id

lemma to_add_subgroup_le : p.to_add_subgroup ≤ p'.to_add_subgroup ↔ p ≤ p' := iff.rfl

@[mono] lemma to_add_subgroup_mono : monotone (to_add_subgroup : submodule R M → add_subgroup M) :=
to_add_subgroup_strict_mono.monotone

omit module_M

lemma sub_mem : x ∈ p → y ∈ p → x - y ∈ p := p.to_add_subgroup.sub_mem

@[simp] lemma neg_mem_iff : -x ∈ p ↔ x ∈ p := p.to_add_subgroup.neg_mem_iff

lemma add_mem_iff_left : y ∈ p → (x + y ∈ p ↔ x ∈ p) := p.to_add_subgroup.add_mem_cancel_right

lemma add_mem_iff_right : x ∈ p → (x + y ∈ p ↔ y ∈ p) := p.to_add_subgroup.add_mem_cancel_left

lemma sub_mem_iff_left (hy : y ∈ p) : (x - y) ∈ p ↔ x ∈ p :=
by rw [sub_eq_add_neg, p.add_mem_iff_left (p.neg_mem hy)]

lemma sub_mem_iff_right (hx : x ∈ p) : (x - y) ∈ p ↔ y ∈ p :=
by rw [sub_eq_add_neg, p.add_mem_iff_right hx, p.neg_mem_iff]

instance : has_neg p := ⟨λx, ⟨-x.1, neg_mem _ x.2⟩⟩

@[simp, norm_cast] lemma coe_neg (x : p) : ((-x : p) : M) = -x := rfl

instance : add_comm_group p :=
{ add := (+), zero := 0, neg := has_neg.neg, ..p.to_add_subgroup.to_add_comm_group }

@[simp, norm_cast] lemma coe_sub (x y : p) : (↑(x - y) : M) = ↑x - ↑y := rfl

end add_comm_group

section is_domain

variables [ring R] [is_domain R]
variables [add_comm_group M] [module R M] {b : ι → M}

lemma not_mem_of_ortho {x : M} {N : submodule R M}
  (ortho : ∀ (c : R) (y ∈ N), c • x + y = (0 : M) → c = 0) :
  x ∉ N :=
by { intro hx, simpa using ortho (-1) x hx }

lemma ne_zero_of_ortho {x : M} {N : submodule R M}
  (ortho : ∀ (c : R) (y ∈ N), c • x + y = (0 : M) → c = 0) :
  x ≠ 0 :=
mt (λ h, show x ∈ N, from h.symm ▸ N.zero_mem) (not_mem_of_ortho ortho)

end is_domain

section ordered_monoid

variables [semiring R]

/-- A submodule of an `ordered_add_comm_monoid` is an `ordered_add_comm_monoid`. -/
instance to_ordered_add_comm_monoid
  {M} [ordered_add_comm_monoid M] [module R M] (S : submodule R M) :
  ordered_add_comm_monoid S :=
subtype.coe_injective.ordered_add_comm_monoid coe rfl (λ _ _, rfl) (λ _ _, rfl)

/-- A submodule of a `linear_ordered_add_comm_monoid` is a `linear_ordered_add_comm_monoid`. -/
instance to_linear_ordered_add_comm_monoid
  {M} [linear_ordered_add_comm_monoid M] [module R M] (S : submodule R M) :
  linear_ordered_add_comm_monoid S :=
subtype.coe_injective.linear_ordered_add_comm_monoid coe rfl (λ _ _, rfl) (λ _ _, rfl)

/-- A submodule of an `ordered_cancel_add_comm_monoid` is an `ordered_cancel_add_comm_monoid`. -/
instance to_ordered_cancel_add_comm_monoid
  {M} [ordered_cancel_add_comm_monoid M] [module R M] (S : submodule R M) :
  ordered_cancel_add_comm_monoid S :=
subtype.coe_injective.ordered_cancel_add_comm_monoid coe rfl (λ _ _, rfl) (λ _ _, rfl)

/-- A submodule of a `linear_ordered_cancel_add_comm_monoid` is a
`linear_ordered_cancel_add_comm_monoid`. -/
instance to_linear_ordered_cancel_add_comm_monoid
  {M} [linear_ordered_cancel_add_comm_monoid M] [module R M] (S : submodule R M) :
  linear_ordered_cancel_add_comm_monoid S :=
subtype.coe_injective.linear_ordered_cancel_add_comm_monoid coe rfl (λ _ _, rfl) (λ _ _, rfl)

end ordered_monoid

section ordered_group

variables [ring R]

/-- A submodule of an `ordered_add_comm_group` is an `ordered_add_comm_group`. -/
instance to_ordered_add_comm_group
  {M} [ordered_add_comm_group M] [module R M] (S : submodule R M) :
  ordered_add_comm_group S :=
subtype.coe_injective.ordered_add_comm_group coe
  rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

/-- A submodule of a `linear_ordered_add_comm_group` is a
`linear_ordered_add_comm_group`. -/
instance to_linear_ordered_add_comm_group
  {M} [linear_ordered_add_comm_group M] [module R M] (S : submodule R M) :
  linear_ordered_add_comm_group S :=
subtype.coe_injective.linear_ordered_add_comm_group coe
  rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

end ordered_group

end submodule

namespace submodule

variables [division_ring S] [semiring R] [add_comm_monoid M] [module R M]
variables [has_scalar S R] [module S M] [is_scalar_tower S R M]

variables (p : submodule R M) {s : S} {x y : M}

theorem smul_mem_iff (s0 : s ≠ 0) : s • x ∈ p ↔ x ∈ p :=
p.to_sub_mul_action.smul_mem_iff s0

end submodule

/-- Subspace of a vector space. Defined to equal `submodule`. -/
abbreviation subspace (R : Type u) (M : Type v)
  [field R] [add_comm_group M] [module R M] :=
submodule R M