import fol
import to_mathlib
import Rings.Notation
import data.fin.basic
import Rings.ToMathlib.fin
import Rings.ToMathlib.dvector
import data.set_like.basic
import data.set.lattice
import order.closure
import completeness

namespace fol

/-- repeated ∧ for a FOL -/
def bd_big_and {L : Language} {d : ℕ} :
  Π (n : ℕ), (fin n → (bounded_formula L d)) → bounded_formula L d
| nat.zero fs := bd_not bd_falsum
| (nat.succ n) fs := bd_and (bd_big_and n (λ i, fs i)) (fs n)

/-- forward direction for following lemma -/
lemma realize_bounded_formula_bd_alls'_aux {L} {n} : Π {k} {f : bounded_formula L (n + k)}
  {S : Structure L} (v : dvector S n),
  (realize_bounded_formula v (bd_alls' k n f) dvector.nil)
  →
  (∀ xs : dvector S k, realize_bounded_formula (dvector.append xs v) f dvector.nil)
| nat.zero  f S v hf (dvector.nil) :=
by simpa using hf
| (nat.succ n) f S v hf (dvector.cons x xs) :=
begin
  simp only [bd_alls'] at hf,
  have hf' := realize_bounded_formula_bd_alls'_aux v hf xs,
  simp only [realize_bounded_formula] at hf',
  simpa using hf' x,
end

/-- realizing ∀ x_1 ... x_k, f is the same as realizing f for each (x_1 ... x_k) -/
lemma realize_bounded_formula_bd_alls' {L} {n} {k} {f : bounded_formula L (n + k)}
  {S : Structure L} (v : dvector S n) :
  (realize_bounded_formula v (bd_alls' k n f) dvector.nil)
  ↔
  (∀ xs : dvector S k, realize_bounded_formula (dvector.append xs v) f dvector.nil) :=
begin
  split, {apply realize_bounded_formula_bd_alls'_aux},
  intro hf,
  induction k with k hk,
  {simpa using hf dvector.nil},
  {
    simp only [bd_alls'],
    apply hk,
    simp only [realize_bounded_formula],
    intros xs x,
    apply hf (dvector.cons x xs),
  }
end

/-- copy of bd_alls with ∃'s instead--/
@[simp] def bd_exs' {L : Language} (k n : ℕ) :
  bounded_formula L (n + k) → bounded_formula L n :=
λ f, bd_not (bd_alls' k n (bd_not f))

/-- realizing ∀ x_1 ... x_k, f is the same as realizing f for each (x_1 ... x_k) -/
lemma realize_bounded_formula_bd_exs' {L} {n} {k} {f : bounded_formula L (n + k)}
  {S : Structure L} (v : dvector S n) :
  (realize_bounded_formula v (bd_exs' k n f) dvector.nil)
  ↔
  (∃ xs : dvector S k, realize_bounded_formula (dvector.append xs v) f dvector.nil) :=
begin
  have hrw : (∀ (x : dvector ↥S k), ¬realize_bounded_formula (x.append v) f dvector.nil)
    ↔ (∀ (x : dvector ↥S k), realize_bounded_formula (x.append v) (bd_not f) dvector.nil),
  {simp only [realize_bounded_formula_not]},
  simp only [bd_exs'],
  rw [← not_iff_not, not_exists, hrw, realize_bounded_formula_not, not_not,
    realize_bounded_formula_bd_alls'],
end

@[simp] lemma realize_bounded_formula_bd_big_and {L} {S : Structure L} {n : ℕ}
  {v : dvector S n} : Π {m : ℕ} (f : fin m → bounded_formula L n),
  realize_bounded_formula v (bd_big_and m f) dvector.nil
  ↔
  (Π k : fin m, realize_bounded_formula v (f k) dvector.nil)
| nat.zero f :=
begin
  simp only [bd_big_and, realize_bounded_formula_not,
    realize_bounded_formula, true_iff, not_false_iff],
  exact fin_zero_elim,
end
| (nat.succ m) f :=
begin
  simp only [bd_big_and, realize_bounded_formula_and],
  split,
  {
    intros hf k,
    rw realize_bounded_formula_bd_big_and at hf,
    cases fin.lt_or_eq_fin k with hk,
    -- by_cases hk : (k : ℕ) < m,
    {
      have h :=  hf.1 ⟨ k , _ ⟩,
      {simpa using h},
      {
        rw fin.lt_coe_iff_val_lt,
        exact hk,
        exact lt_add_one m,
      },
    },
    {rw h, exact hf.2}
  },
  {
    intro hf,
    split,
    {
      rw realize_bounded_formula_bd_big_and,
      intro k,
      apply hf k,
    },
    {exact hf m}
  },
end

lemma all_realize_sentence_union {L} (S : Structure L) (T₁ T₂ : Theory L) :
  S ⊨ T₁ ∪ T₂ ↔ S ⊨ T₁ ∧ S ⊨ T₂ :=
by simp only [all_realize_sentence, set.mem_union_eq,
  or_imp_distrib, forall_and_distrib]

lemma all_realize_sentence_range {L} {α}
  (S : Structure L) (fs : α → sentence L) :
S ⊨ set.range fs ↔ ∀ a : α, S ⊨ fs a :=
by simp only [all_realize_sentence, forall_apply_eq_imp_iff',
  set.mem_range, exists_imp_distrib]

lemma all_realize_sentence_image {L} {α}
  (S : Structure L) (fs : α → sentence L) (s : set α) :
S ⊨ set.image fs s ↔ ∀ a : α, a ∈ s → S ⊨ fs a :=
by simp [all_realize_sentence]

lemma all_realize_sentence_insert {L} {s : Theory L} (ϕ : sentence L)
  (S : Structure L) :
  S ⊨ set.insert ϕ s ↔ S ⊨ ϕ ∧ S ⊨ s :=
⟨ (λ h, ⟨ h (set.mem_insert _ _) , λ f hf, h (set.mem_insert_of_mem _ hf) ⟩)
  ,
  ( λ h f hf,
  begin
    cases hf with hf hf,
    { rw hf, exact h.1 },
    { exact h.2 hf },
  end ) ⟩

namespace bounded_preterm

-- I don't know if this is already in fol but
@[simp] def lift_succ {L} {n : ℕ} : Π {l},
  fol.bounded_preterm L n l → fol.bounded_preterm L n.succ l
| l (bd_var k) := x_ k
| l (bd_func f) := fol.bounded_preterm.bd_func f
| l (bd_app t s) :=
  fol.bounded_preterm.bd_app (@lift_succ l.succ t) (@lift_succ 0 s)

  -- instance has_lift_succ {L} {n : ℕ} : Π l,
  --   has_lift (fol.bounded_preterm L n l) (fol.bounded_preterm L n.succ l) :=
  --   λ l, ⟨ lift_succ ⟩

lemma lift_succ_x_k {L} {n : ℕ} {k : fin n} :
  lift_succ (x_ k : fol.bounded_preterm L n 0) = x_ k := rfl

lemma realize_lift_succ {L} {n : ℕ} {S : Structure L} :
  Π {l} {t : bounded_preterm L n l} {as : dvector S (n+1)} {bs},
  realize_bounded_term as (lift_succ t) bs
  = realize_bounded_term (dvector.remove_mth n as) t bs
| l (bd_var k) as bs :=
begin
  simp only [lift_succ, realize_bounded_term],
  rw dvector.nth_remove_mth_big_m _ k.2 k.2,
  simp only [fin.val_eq_coe, fin.coe_eq_cast_succ, fin.coe_cast_succ],
end
| l (bd_func f) as bs := by simp only [lift_succ, realize_bounded_term]
| l (bd_app t s) as bs :=
by simp only [lift_succ, realize_bounded_term, realize_lift_succ]

end bounded_preterm

variables {L : Language}

namespace bounded_preformula

lemma bd_not.inj {n} {f0 f1 : bounded_formula L n} :
  ∼ f0 = ∼ f1 → f0 = f1 := λ h, (bd_imp.inj h).1

lemma bd_notequal.inj {n} {t0 t1 s0 s1 : bounded_term L n} :
  t0 ≄ s0 = t1 ≄ s1 → t0 = t1 ∧ s0 = s1 :=
bd_equal.inj ∘ bd_not.inj

end bounded_preformula

-- not to be confused with is_complete
/-- A theory is_complete' if it deduces any sentence or its complement -/
def is_complete' (T : Theory L) : Prop :=
∀ (ϕ : sentence L), T ⊨ ϕ ∨ T ⊨ ∼ ϕ

/-- A theory is_complete'' if deductions for the model transfer to deductions for the theory-/
def is_complete'' (T : Theory L) : Prop :=
∀ (M : Structure L) (hM : nonempty M) (ϕ : sentence L), M ⊨ T → M ⊨ ϕ → T ⊨ ϕ

/-- When T is satisfied by some model then two notions of complete coincide -/
lemma is_complete''_iff_is_complete' {T : Theory L} :
  is_complete' T ↔ is_complete'' T :=
begin
  split,
  { intros H M hM ϕ hMT hMϕ,
    cases H ϕ with hTϕ hTϕ,
    { exact hTϕ },
    {
      have hbot := hTϕ hM hMT,
      rw realize_sentence_not at hbot,
      exfalso,
      exact hbot hMϕ } },
  { intros H ϕ,
    by_cases hM : ∃ M : Structure L, nonempty M ∧ M ⊨ T,
    {
      rcases hM with ⟨ M , hM0 , hMT ⟩,
      by_cases hMϕ : M ⊨ ϕ,
      { left, exact H M hM0 ϕ hMT hMϕ },
      { right,
        rw ← realize_sentence_not at hMϕ,
        exact H M hM0 _ hMT hMϕ} },
    { left,
      intros M hM0 hMT,
      exfalso,
      apply hM ⟨ M , hM0 , hMT ⟩} },
end

end fol

/- Copied from mathlib but renamed to fit original fol definitions
## References
For the Flypitch project:
- [J. Han, F. van Doorn, *A formal proof of the independence of the continuum hypothesis*]
[flypitch_cpp]
- [J. Han, F. van Doorn, *A formalization of forcing and the unprovability of
the continuum hypothesis*][flypitch_itp]

-/

namespace fol

namespace Language

/-- The empty language has no symbols. -/
def empty : Language := ⟨λ _, pempty, λ _, pempty⟩

instance : inhabited Language := ⟨empty⟩

variable (L : Language)

/-- A Language is relational when it has no function symbols. -/
class is_relational : Prop :=
(empty_functions : ∀ n, L.functions n → false)

/-- A Language is algebraic when it has no relation symbols. -/
class is_algebraic : Prop :=
(empty_relations : ∀ n, L.relations n → false)

variable {L}

instance is_relational_of_empty_functions {symb : ℕ → Type*} : is_relational ⟨λ _, pempty, symb⟩ :=
⟨by { intro n, apply pempty.elim }⟩

instance is_algebraic_of_empty_relations {symb : ℕ → Type*}  : is_algebraic ⟨symb, λ _, pempty⟩ :=
⟨by { intro n, apply pempty.elim }⟩

instance is_relational_empty : is_relational (empty) := Language.is_relational_of_empty_functions
instance is_algebraic_empty : is_algebraic (empty) := Language.is_algebraic_of_empty_relations

variables (L) (M : Structure L) (N : Structure L)

/- A first-order structure on a type `M` consists of interpretations of all the symbols in a given
  Language. Each function of arity `n` is interpreted as a function sending tuples of length `n`
  (modeled as `(fin n → M)`) to `M`, and a relation of arity `n` is a function from tuples of length
  `n` to `Prop`. -/

open Structure

/-- A homomorphism between first-order structures is a function that commutes with the
  interpretations of functions and maps tuples in one structure where a given relation is true to
  tuples in the second structure where that relation is still true. -/
protected structure hom :=
(to_fun : M → N)
(map_fun' : ∀{n} (f : L.functions n) x, to_fun (M.fun_map f x)
  = N.fun_map f (dvector.map to_fun x) . obviously)
(map_rel' : ∀{n} (r : L.relations n) x, M.rel_map r x
  → N.rel_map r (dvector.map to_fun x) . obviously)

localized "notation A ` →[`:25 L `] ` B := L.hom A B" in fol

/-- An embedding of first-order structures is an embedding that commutes with the
  interpretations of functions and relations. -/
protected structure embedding extends M ↪ N :=
(map_fun' : ∀{n} (f : L.functions n) x, to_fun (M.fun_map f x)
  = N.fun_map f (dvector.map to_fun x) . obviously)
(map_rel' : ∀{n} (r : L.relations n) x, N.rel_map r (dvector.map to_fun x)
  ↔ M.rel_map r x . obviously)

localized "notation A ` ↪[`:25 L `] ` B := L.embedding A B" in fol

/-- An equivalence of first-order structures is an equivalence that commutes with the
  interpretations of functions and relations. -/
protected structure equiv extends _root_.equiv M N :=
(map_fun' : ∀{n} (f : L.functions n) x, to_fun (M.fun_map f x)
  = N.fun_map f (dvector.map to_fun x) . obviously)
(map_rel' : ∀{n} (r : L.relations n) x, N.rel_map r (dvector.map to_fun x)
  ↔ M.rel_map r x . obviously)

localized "notation A ` ≃[`:25 L `] ` B := L.equiv A B" in fol

variables {L M N} {P : Structure L} {Q : Structure L}

instance : has_coe_t L.constants M :=
⟨λ c, M.fun_map c dvector.nil⟩

lemma fun_map_eq_coe_constants {c : L.constants} {x : dvector M 0} :
  M.fun_map c x = c := by cases x; refl

namespace hom

@[simps] instance has_coe_to_fun : has_coe_to_fun (M →[L] N) (λ _, M → N) :=
⟨fol.Language.hom.to_fun⟩

@[simp] lemma to_fun_eq_coe {f : M →[L] N} : f.to_fun = (f : M → N) := rfl

lemma coe_injective : @function.injective (M →[L] N) (M → N) coe_fn
| f g h := by {cases f, cases g, cases h, refl}

@[ext]
lemma ext ⦃f g : M →[L] N⦄ (h : ∀ x, f x = g x) : f = g :=
coe_injective (funext h)

lemma ext_iff {f g : M →[L] N} : f = g ↔ ∀ x, f x = g x :=
⟨λ h x, h ▸ rfl, λ h, ext h⟩

@[simp] lemma map_fun (φ : M →[L] N) {n : ℕ} (f : L.functions n) (x : dvector M n) :
  φ (M.fun_map f x) = N.fun_map f (dvector.map φ x) := φ.map_fun' f x

@[simp] lemma map_constants (φ : M →[L] N) (c : L.constants) : φ c = c :=
(φ.map_fun c dvector.nil).trans rfl

@[simp] lemma map_rel (φ : M →[L] N) {n : ℕ} (r : L.relations n) (x : dvector M n) :
  M.rel_map r x → N.rel_map r (dvector.map φ x) := φ.map_rel' r x

variables (L) (M)
/-- The identity map from a structure to itself -/
@[refl] def id : M →[L] M :=
{ to_fun := id,
  map_fun' := λ n f x, by rw dvector.map_id' x; refl,
  map_rel' := λ n r x hr, by rw dvector.map_id' x; exact hr}

variables {L} {M}

instance : inhabited (M →[L] M) := ⟨id L M⟩

@[simp] lemma id_apply (x : M) :
  id L M x = x := rfl

/-- Composition of first-order homomorphisms -/
@[trans] def comp (hnp : N →[L] P) (hmn : M →[L] N) : M →[L] P :=
{ to_fun := hnp ∘ hmn,
  map_rel' := λ n r xs h,
  begin
    simp [dvector.map_comp],
    apply hnp.map_rel',
    simp only [dvector.map_map, dvector.map_id, dvector.map_comp],
    apply hmn.map_rel',
    simp only [dvector.map_id],
    exact h,
  end  }

@[simp] lemma comp_apply (g : N →[L] P) (f : M →[L] N) (x : M) :
  g.comp f x = g (f x) := rfl

/-- Composition of first-order homomorphisms is associative. -/
lemma comp_assoc (f : M →[L] N) (g : N →[L] P) (h : P →[L] Q) :
  (h.comp g).comp f = h.comp (g.comp f) := rfl

end hom

namespace embedding

@[simps] instance has_coe_to_fun : has_coe_to_fun (M ↪[L] N) (λ _, M → N) :=
⟨λ f, f.to_fun⟩

@[simp] lemma map_fun (φ : M ↪[L] N) {n : ℕ} (f : L.functions n) (x : dvector M n) :
  φ (M.fun_map f x) = N.fun_map f (dvector.map φ x) := φ.map_fun' f x

@[simp] lemma map_constants (φ : M ↪[L] N) (c : L.constants) : φ c = c :=
(φ.map_fun c dvector.nil).trans rfl

@[simp] lemma map_rel (φ : M ↪[L] N) {n : ℕ} (r : L.relations n) (x : dvector M n) :
  N.rel_map r (dvector.map φ x) ↔ M.rel_map r x := φ.map_rel' r x

/-- A first-order embedding is also a first-order homomorphism. -/
def to_hom (f : M ↪[L] N) : M →[L] N :=
{ to_fun := f }

@[simp]
lemma coe_to_hom {f : M ↪[L] N} : (f.to_hom : M → N) = f := rfl

lemma coe_injective : @function.injective (M ↪[L] N) (M → N) coe_fn
| f g h :=
begin
  cases f,
  cases g,
  simp only,
  ext x,
  exact function.funext_iff.1 h x,
end

@[ext]
lemma ext ⦃f g : M ↪[L] N⦄ (h : ∀ x, f x = g x) : f = g :=
coe_injective (funext h)

lemma ext_iff {f g : M ↪[L] N} : f = g ↔ ∀ x, f x = g x :=
⟨λ h x, h ▸ rfl, λ h, ext h⟩

lemma injective (f : M ↪[L] N) : function.injective f :=
f.to_embedding.injective

-- /-- The identity embedding from a structure to itself -/
-- @[refl] def refl (L : Language) (M : Structure L) : L.embedding M M :=
-- { to_embedding := function.embedding.refl M }

-- #check refl

variables {L} {M}

-- instance : inhabited (M ↪[L] M) := ⟨ refl L M ⟩

-- @[simp] lemma refl_apply (x : M) :
--   refl L M x = x := rfl

-- Composition of first-order embeddings
-- @[trans] def comp (hnp : N ↪[L] P) (hmn : M ↪[L] N) : M ↪[L] P :=
-- { to_fun := hnp ∘ hmn,
--   inj' := hnp.injective.comp hmn.injective }

-- @[simp] lemma comp_apply (g : N ↪[L] P) (f : M ↪[L] N) (x : M) :
--   g.comp f x = g (f x) := rfl

-- /-- Composition of first-order embeddings is associative. -/
-- lemma comp_assoc (f : M ↪[L] N) (g : N ↪[L] P) (h : P ↪[L] Q) :
--   (h.comp g).comp f = h.comp (g.comp f) := rfl

end embedding

namespace equiv

/-- The inverse of a first-order equivalence is a first-order equivalence. -/
@[symm] def symm (f : M ≃[L] N) : N ≃[L] M :=
{ map_fun' := λ n f' x, begin
    simp only [equiv.to_fun_as_coe],
    rw [equiv.symm_apply_eq],
    refine eq.trans _ (f.map_fun' f' (dvector.map f.to_equiv.symm x)).symm,
    rw [equiv.to_fun_as_coe, ← dvector.map_comp, equiv.self_comp_symm,
      dvector.map_id'],
  end,
  map_rel' := λ n r x, begin
    simp only [equiv.to_fun_as_coe],
    refine (f.map_rel' r (dvector.map f.to_equiv.symm x)).symm.trans _,
    rw [equiv.to_fun_as_coe, ← dvector.map_comp, equiv.self_comp_symm,
      dvector.map_id'],
  end,
  .. f.to_equiv.symm }

@[simps] instance has_coe_to_fun : has_coe_to_fun (M ≃[L] N) (λ _, M → N) :=
⟨λ f, f.to_fun⟩

@[simp] lemma map_fun (φ : M ≃[L] N) {n : ℕ} (f : L.functions n) (x : dvector M n) :
  φ (M.fun_map f x) = N.fun_map f (dvector.map φ x) := φ.map_fun' f x

@[simp] lemma map_constants (φ : M ≃[L] N) (c : L.constants) : φ c = c :=
(φ.map_fun c dvector.nil).trans rfl

@[simp] lemma map_rel (φ : M ≃[L] N) {n : ℕ} (r : L.relations n) (x : dvector M n) :
  N.rel_map r (dvector.map φ x) ↔ M.rel_map r x := φ.map_rel' r x

/-- A first-order equivalence is also a first-order embedding. -/
def to_embedding (f : M ≃[L] N) : M ↪[L] N :=
{ to_fun := f,
  inj' := f.to_equiv.injective }

/-- A first-order equivalence is also a first-order embedding. -/
def to_hom (f : M ≃[L] N) : M →[L] N :=
{ to_fun := f }

@[simp] lemma to_embedding_to_hom (f : M ≃[L] N) : f.to_embedding.to_hom = f.to_hom := rfl

@[simp]
lemma coe_to_hom {f : M ≃[L] N} : (f.to_hom : M → N) = (f : M → N) := rfl

@[simp] lemma coe_to_embedding (f : M ≃[L] N) : (f.to_embedding : M → N) = (f : M → N) := rfl

lemma coe_injective : @function.injective (M ≃[L] N) (M → N) coe_fn
| f g h :=
begin
  cases f,
  cases g,
  simp only,
  ext x,
  exact function.funext_iff.1 h x,
end

@[ext]
lemma ext ⦃f g : M ≃[L] N⦄ (h : ∀ x, f x = g x) : f = g :=
coe_injective (funext h)

lemma ext_iff {f g : M ≃[L] N} : f = g ↔ ∀ x, f x = g x :=
⟨λ h x, h ▸ rfl, λ h, ext h⟩

lemma injective (f : M ≃[L] N) : function.injective f := f.to_embedding.injective

-- variables (L) (M)
-- The identity equivalence from a structure to itself
-- @[refl] def refl : M ≃[L] M :=
-- { to_equiv := equiv.refl M }

-- variables {L} {M}

-- instance : inhabited (M ≃[L] M) := ⟨refl L M⟩

-- @[simp] lemma refl_apply (x : M) :
  -- refl L M x = x := rfl

-- Composition of first-order equivalences
-- @[trans] def comp (hnp : N ≃[L] P) (hmn : M ≃[L] N) : M ≃[L] P :=
-- { to_fun := hnp ∘ hmn,
  -- .. (hmn.to_equiv.trans hnp.to_equiv) }

-- @[simp] lemma comp_apply (g : N ≃[L] P) (f : M ≃[L] N) (x : M) :
  -- g.comp f x = g (f x) := rfl

-- Composition of first-order homomorphisms is associative.
-- lemma comp_assoc (f : M ≃[L] N) (g : N ≃[L] P) (h : P ≃[L] Q) :
  -- (h.comp g).comp f = h.comp (g.comp f) := rfl

end equiv

section closed_under

open set

variables {n : ℕ} (f : L.functions n) (s : set M)

/-- Indicates that a set in a given structure is a closed under a function symbol. -/
def closed_under : Prop :=
∀ (x : dvector M n), (∀ i : fin n, dvector.nth' x i ∈ s) → M.fun_map f x ∈ s

variables {f} {s} {t : set M}

namespace closed_under

lemma inter (hs : closed_under f s) (ht : closed_under f t) :
  closed_under f (s ∩ t) :=
λ x h, mem_inter (hs x (λ i, mem_of_mem_inter_left (h i)))
  (ht x (λ i, mem_of_mem_inter_right (h i)))

lemma inf (hs : closed_under f s) (ht : closed_under f t) :
  closed_under f (s ⊓ t) := hs.inter ht

variables {S : set (set M)}

lemma Inf (hS : ∀ s, s ∈ S → closed_under f s) : closed_under f (Inf S) :=
λ x h s hs, hS s hs x (λ i, h i s hs)

end closed_under
end closed_under

variables (L) (M)

/-- A substructure of a structure `M` is a set closed under application of function symbols. -/
structure substructure :=
(carrier : set M)
(fun_mem : ∀{n}, ∀ (f : L.functions n), closed_under f carrier)

variables {L} {M}

namespace substructure

instance : set_like (L.substructure M) M :=
⟨substructure.carrier, λ p q h, by cases p; cases q; congr'⟩

/-- See Note [custom simps projection] -/
def simps.coe (S : L.substructure M) : set M := S
initialize_simps_projections substructure (carrier → coe)

@[simp]
lemma mem_carrier {s : L.substructure M} {x : M} : x ∈ s.carrier ↔ x ∈ s := iff.rfl

/-- Two substructures are equal if they have the same elements. -/
@[ext]
theorem ext {S T : L.substructure M}
  (h : ∀ x, x ∈ S ↔ x ∈ T) : S = T := set_like.ext h

/-- Copy a substructure replacing `carrier` with a set that is equal to it. -/
protected def copy (S : L.substructure M) (s : set M) (hs : s = S) : L.substructure M :=
{ carrier := s,
  fun_mem := λ n f, hs.symm ▸ (S.fun_mem f) }

variable {S : L.substructure M}

@[simp] lemma coe_copy {s : set M} (hs : s = S) :
  (S.copy s hs : set M) = s := rfl

lemma copy_eq {s : set M} (hs : s = S) : S.copy s hs = S :=
set_like.coe_injective hs

lemma constants_mem {c : L.constants} : ↑c ∈ S :=
mem_carrier.2 (S.fun_mem c _ fin.elim0)

/-- The substructure `M` of the structure `M`. -/
instance : has_top (L.substructure M) :=
⟨{ carrier := set.univ,
   fun_mem := λ n f x h, set.mem_univ _ }⟩

instance : inhabited (L.substructure M) := ⟨⊤⟩

@[simp] lemma mem_top (x : M) : x ∈ (⊤ : L.substructure M) := set.mem_univ x

@[simp] lemma coe_top : ((⊤ : L.substructure M) : set M) = set.univ := rfl

/-- The inf of two substructures is their intersection. -/
instance : has_inf (L.substructure M) :=
⟨λ S₁ S₂,
  { carrier := S₁ ∩ S₂,
    fun_mem := λ n f, (S₁.fun_mem f).inf (S₂.fun_mem f) }⟩

@[simp]
lemma coe_inf (p p' : L.substructure M) : ((p ⊓ p' : L.substructure M) : set M) = p ∩ p' := rfl

@[simp]
lemma mem_inf {p p' : L.substructure M} {x : M} : x ∈ p ⊓ p' ↔ x ∈ p ∧ x ∈ p' := iff.rfl

lemma set.Inter_pos {α} {p : Prop} {μ : p → set α} (hp : p) : (⋂h:p, μ h) = μ hp := infi_pos hp

lemma set.Inter_neg {α} {p : Prop} {μ : p → set α} (hp : ¬ p) : (⋂h:p, μ h) = set.univ := infi_neg hp

instance : has_Inf (L.substructure M) :=
⟨λ s, { carrier := ⋂ t ∈ s, ↑t,
        fun_mem := λ n f, closed_under.Inf begin
          rintro _ ⟨t, rfl⟩,
          simp only,
          by_cases h : t ∈ s,
          { rw [set.Inter_pos h],
            exact t.fun_mem f },
          { rw [set.Inter_neg h],
            exact λ _ _, set.mem_univ _ }
        end }⟩

@[simp, norm_cast]
lemma coe_Inf (S : set (L.substructure M)) :
  ((Inf S : L.substructure M) : set M) = ⋂ s ∈ S, ↑s := rfl

lemma mem_Inf {S : set (L.substructure M)} {x : M} : x ∈ Inf S ↔ ∀ p ∈ S, x ∈ p :=
  set.mem_Inter₂

lemma mem_infi {ι : Sort*} {S : ι → L.substructure M} {x : M} : (x ∈ ⨅ i, S i) ↔ ∀ i, x ∈ S i :=
by simp only [infi, mem_Inf, set.forall_range_iff]

@[simp, norm_cast]
lemma coe_infi {ι : Sort*} {S : ι → L.substructure M} : (↑(⨅ i, S i) : set M) = ⋂ i, S i :=
by simp only [infi, coe_Inf, set.bInter_range]

/-- Substructures of a structure form a complete lattice. -/
instance : complete_lattice (L.substructure M) :=
{ le           := (≤),
  lt           := (<),
  top          := (⊤),
  le_top       := λ S x hx, mem_top x,
  inf          := (⊓),
  Inf          := has_Inf.Inf,
  le_inf       := λ a b c ha hb x hx, ⟨ha hx, hb hx⟩,
  inf_le_left  := λ a b x, and.left,
  inf_le_right := λ a b x, and.right,
  .. complete_lattice_of_Inf (L.substructure M) $ λ s,
    is_glb.of_image (λ S T,
      show (S : set M) ≤ T ↔ S ≤ T, from set_like.coe_subset_coe) is_glb_binfi }

variable (L)

/-- The `L.substructure` generated by a set. -/
def closure : lower_adjoint (coe : L.substructure M → set M) := ⟨λ s, Inf {S | s ⊆ S},
  λ s S, ⟨set.subset.trans (λ x hx, mem_Inf.2 $ λ S hS, hS hx), λ h, Inf_le h⟩⟩

variables {L} {s : set M}

lemma mem_closure {x : M} : x ∈ closure L s ↔ ∀ S : L.substructure M, s ⊆ S → x ∈ S :=
mem_Inf

/-- The substructure generated by a set includes the set. -/
@[simp]
lemma subset_closure : s ⊆ closure L s := (closure L).le_closure s

@[simp]
lemma closed (S : L.substructure M) : (closure L).closed (S : set M) :=
_root_.congr rfl ((closure L).eq_of_le set.subset.rfl (λ x xS, mem_closure.2 (λ T hT, hT xS)))

open set

/-- A substructure `S` includes `closure L s` if and only if it includes `s`. -/
@[simp]
lemma closure_le : closure L s ≤ S ↔ s ⊆ S := (closure L).closure_le_closed_iff_le s S.closed

/-- Substructure closure of a set is monotone in its argument: if `s ⊆ t`,
then `closure L s ≤ closure L t`. -/
lemma closure_mono ⦃s t : set M⦄ (h : s ⊆ t) : closure L s ≤ closure L t :=
(closure L).monotone h

lemma closure_eq_of_le (h₁ : s ⊆ S) (h₂ : S ≤ closure L s) : closure L s = S :=
(closure L).eq_of_le h₁ h₂

variable (S)

/-- An induction principle for closure membership. If `p` holds for all elements of `s`, and
is preserved under function symbols, then `p` holds for all elements of the closure of `s`. -/
@[elab_as_eliminator] lemma closure_induction
  {p : M → Prop} {x} (h : x ∈ closure L s)
  (Hs : ∀ x ∈ s, p x)
  (Hfun : ∀ {n : ℕ} (f : L.functions n), closed_under f (set_of p)) : p x :=
(@closure_le L M ⟨set_of p, λ n, Hfun⟩ _).2 Hs h

-- (@closure_le L M _ ⟨set_of p, λ n, Hfun⟩ _).2 Hs h

/-- If `s` is a dense set in a structure `M`, `substructure.closure L s = ⊤`, then in order to prove
that some predicate `p` holds for all `x : M` it suffices to verify `p x` for `x ∈ s`, and verify
that `p` is preserved under function symbols. -/
@[elab_as_eliminator] lemma dense_induction {p : M → Prop} (x : M) {s : set M}
  (hs : closure L s = ⊤) (Hs : ∀ x ∈ s, p x)
  (Hfun : ∀ {n : ℕ} (f : L.functions n), closed_under f (set_of p)) : p x :=
have ∀ x ∈ closure L s, p x, from λ x hx, closure_induction hx Hs (λ n, Hfun),
by simpa [hs] using this x

variables (L) (M)

/-- `closure` forms a Galois insertion with the coercion to set. -/
protected def gi : galois_insertion (@closure L M) coe :=
{ choice := λ s _, closure L s,
  gc := λ s t, closure_le,
  le_l_u := λ s, subset_closure,
  choice_eq := λ s h, rfl }

variables {L} {M}

/-- Closure of a substructure `S` equals `S`. -/
@[simp] lemma closure_eq : closure L (S : set M) = S := (substructure.gi L M).l_u_eq S

@[simp] lemma closure_empty : closure L (∅ : set M) = ⊥ :=
(substructure.gi L M).gc.l_bot

@[simp] lemma closure_univ : closure L (set.univ : set M) = ⊤ :=
@coe_top L M ▸ closure_eq ⊤

lemma closure_union (s t : set M) : closure L (s ∪ t) = closure L s ⊔ closure L t :=
(substructure.gi L M).gc.l_sup

lemma closure_Union {ι} (s : ι → set M) : closure L (⋃ i, s i) = ⨆ i, closure L (s i) :=
(substructure.gi L M).gc.l_supr

end substructure

namespace hom

open substructure

-- /-- The substructure of elements `x : M` such that `f x = g x` -/
-- def eq_locus (f g : M →[L] N) : substructure L M :=
-- { carrier := {x : M | f x = g x},
--   fun_mem := λ n fn x hx,
--   begin
--     have h0 : f (M.fun_map fn x) = g (M.fun_map fn x),
--     {
--       simp only [dvector.map_comp, fol.Language.hom.map_fun],
--       rw dvector.map_id,
--       congr1,
--       rw dvector.ext,
--       intro i,
--       simp only [dvector.nth', dvector.map_nth],
--       apply hx,
--     },
--     exact h0,
--   end}

-- /-- If two `L.hom`s are equal on a set, then they are equal on its substructure closure. -/
-- lemma eq_on_closure {f g : M →[L] N} {s : set M} (h : set.eq_on f g s) :
--   set.eq_on f g (closure L s) :=
-- show substructure.closure L s ≤ f.eq_locus g, from closure_le.2 h

-- lemma eq_of_eq_on_top {f g : M →[L] N} (h : set.eq_on f g (⊤ : substructure L M)) :
--   f = g :=
-- ext $ λ x, h trivial

-- variable {s : set M}

-- lemma eq_of_eq_on_dense (hs : substructure.closure L s = ⊤) {f g : M →[L] N} (h : s.eq_on f g) :
--   f = g :=
-- eq_of_eq_on_top $ hs ▸ eq_on_closure h

end hom

namespace equiv

lemma realize_bounded_term {M N : Structure L} (equiv : M ≃[L] N) {n l} (v : dvector M n)
  (t : bounded_preterm L n l) (xs : dvector M l) :
  realize_bounded_term (dvector.map equiv v) t (dvector.map equiv xs) =
    equiv (realize_bounded_term v t xs) :=
begin
  induction t with _ _ _ _ t s ht hs,
  { simp only [realize_bounded_term, dvector.map_nth] },
  { simp only [realize_bounded_term, dvector.map_nth, map_fun] },
  { specialize hs dvector.nil, rw dvector.map at hs,
    simp only [realize_bounded_term, hs, dvector.map],
    apply ht (xs.cons $ realize_bounded_term v s dvector.nil) }
end

/-- Isomorphic structures realize the same formulas -/
lemma realize_bounded_formula {M N : Structure L} (g : M ≃[L] N) {n l} (v : dvector M n)
  (ϕ : bounded_preformula L n l) (xs : dvector M l) :
  (realize_bounded_formula (v.map g) ϕ (xs.map g)
  ↔ realize_bounded_formula v ϕ xs) :=
begin
  induction ϕ with _ _ _ _ _ _ _ _ _ s t hs _ ϕ ψ hϕ hψ _ _ hϕ,
  { refl },
  { simp only [realize_bounded_formula, realize_bounded_term, g.injective.eq_iff] },
  { simp only [realize_bounded_formula, map_rel] },
  { specialize hs v (xs.cons $ fol.realize_bounded_term v t dvector.nil),
    simp only [dvector.map, ← equiv.realize_bounded_term g v t dvector.nil] at hs,
    rw [realize_bounded_formula, hs, realize_bounded_formula] },
  { simp [realize_bounded_formula, hϕ, hψ] },
  { split,
    { intros H _, rw ← hϕ, apply H },
    { intros H x, specialize hϕ (v.cons $ g.inv_fun x) xs,
      have fun_inv : g (g.to_equiv.inv_fun x) = x, {unfold_coes, simp},
      simp only [dvector.map, fun_inv] at hϕ, rw hϕ, apply H } }
end

/-- Isomorphic structures realize the same sentences -/
lemma realize_sentence {M N : Structure L} (ϕ : sentence L) (g : M ≃[L] N) : (N ⊨ ϕ ↔ M ⊨ ϕ) :=
by {convert realize_bounded_formula g dvector.nil ϕ dvector.nil }

/-- Isomorphic structures model the same theories -/
lemma all_realize_sentence (M N : Structure L) (T : Theory L) :
(M ≃[L] N) → (M ⊨ T ↔ N ⊨ T) :=
λ H, by simp only [all_realize_sentence, Language.equiv.realize_sentence _ H]

end equiv
end Language

section only_infinite

open_locale cardinal

variable {L : Language}

/-- Theory T only has infinitely large models -/
def only_infinite (T : Theory L) : Prop :=
∀ (M : Model T), infinite M.1

lemma only_infinite_subset {T₀ T₁ : Theory L} (hsub : T₀ ⊆ T₁) :
only_infinite T₀ → only_infinite T₁ :=
λ hinf M, hinf ⟨ M.1 , all_realize_sentence_of_subset M.2 hsub ⟩

end only_infinite

section categorical

variable {L : Language}

open_locale cardinal fol

/-- Categoricity states any two models of the same cardinality κ are isomorphic -/
def categorical (κ : cardinal) (T : Theory L) :=
∀ (M N : Structure L) (hM : M ⊨ T) (hN : N ⊨ T), #M = κ → #N = κ → nonempty (M ≃[L] N)

/-- The theory doesn't deduce ϕ ↔ there is a model satisfying its negation -/
lemma not_ssatisfied {T : Theory L} {ϕ : sentence L} :
¬ T ⊨ ϕ ↔ ∃ M : Structure L, nonempty M ∧ M ⊨ set.insert (∼ ϕ) T :=
by { simp only [ssatisfied, not_forall, not_imp, all_realize_sentence_insert,
       realize_sentence_not], tauto }

end categorical

variable {L : Language}

def Structure.constants (S : Structure L) (c : L.constants) : S :=
Structure.fun_map S c dvector.nil

lemma is_complete'_of_is_complete {T : Theory L} (hT : is_complete T) :
  is_complete' T :=
begin
  intro f,
  cases hT.2 f,
  { left,
    intros M _ hM,
    exact hM h },
  { right,
    intros M _ hM,
    exact hM h },
end

lemma is_complete.mem_iff_ssatisfied {T : Theory L} (hT : is_complete T) (f : sentence L) :
  f ∈ T ↔ T ⊨ f :=
begin
  split,
  { intros hf M _ hM, exact hM hf },
  { intro hTf,
    apply or.resolve_right (hT.2 f),
    intro hfT,
    apply hT.1,
    rw completeness,
    intros M hM0 hM,
    simp only [false_of_satisfied_false],
    have hMf := hM hfT,
    simp only [realize_sentence_not] at hMf,
    exact hMf (hTf hM0 hM) },
end

@[simp] lemma Th.ssatisfied_iff_satisfied {S : Structure L} (hS : nonempty S) {f : sentence L} :
  Th S ⊨ f ↔ S ⊨ f :=
by { rw ← is_complete.mem_iff_ssatisfied (is_complete_Th S hS), exact in_theory_iff_satisfied }

end fol


inductive Weekday where
  | sunday    : Weekday
  | monday    : Weekday
  | tuesday   : Weekday
  | wednesday : Weekday
  | thursday  : Weekday
  | friday    : Weekday
  | saturday  : Weekday

def Weekday.next : Weekday -> Weekday :=
  fun d => match d with
    | sunday    => monday
    | monday    => tuesday
    | tuesday   => wednesday
    | wednesday => thursday
    | thursday  => friday
    | friday    => saturday
    | saturday  => sunday

def Weekday.previous : Weekday -> Weekday
  | sunday    => saturday
  | monday    => sunday
  | tuesday   => monday
  | wednesday => tuesday
  | thursday  => wednesday
  | friday    => thursday
  | saturday  => friday

theorem Weekday.nextOfPrevious (d : Weekday) : next (previous d) = d := by
  cases d
  all_goals rfl

theorem Weekday.nextOfPrevious' (d : Weekday) : previous (next d) = d ∧ next (previous d) = d := by
  apply And.intro
  cases d <;> rfl
  cases d <;> rfl

theorem Weekday.nextOfPrevious'' (d : Weekday) : previous (next d) = d ∧ next (previous d) = d := by
  apply And.intro <;> cases d <;> rfl

open Lean.Parser.Tactic in
macro "rwd " x:term : tactic => `(tactic| (rw [$x:term]; done))

theorem ex (a b c : α) (h₁ : a = b) (h₂ : a = c) : b = a ∧ c = a := by
  apply And.intro <;> first | rwd h₁ | rwd h₂

theorem idEq (a : α) : id a = a :=
  rfl

theorem Weekday.test (d : Weekday) : next (previous d) = id d := by
  cases d
  trace_state
  all_goals rw [idEq]
  trace_state
  all_goals rfl

theorem Weekday.test2 (d : Weekday) : next (previous d) = id d := by
  cases d <;> rw [idEq]
  trace_state
  all_goals rfl

def bug {a b c : Nat} (h₁ : a = b) (h₂ : b = c) : a = c := by
  apply Eq.trans <;> assumption


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison

! This file was ported from Lean 3 source module category_theory.limits.shapes.zero_morphisms
! leanprover-community/mathlib commit 69c6a5a12d8a2b159f20933e60115a4f2de62b58
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Pi.Algebra
import Mathbin.CategoryTheory.Limits.Shapes.Products
import Mathbin.CategoryTheory.Limits.Shapes.Images
import Mathbin.CategoryTheory.IsomorphismClasses
import Mathbin.CategoryTheory.Limits.Shapes.ZeroObjects

/-!
# Zero morphisms and zero objects

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

A category "has zero morphisms" if there is a designated "zero morphism" in each morphism space,
and compositions of zero morphisms with anything give the zero morphism. (Notice this is extra
structure, not merely a property.)

A category "has a zero object" if it has an object which is both initial and terminal. Having a
zero object provides zero morphisms, as the unique morphisms factoring through the zero object.

## References

* https://en.wikipedia.org/wiki/Zero_morphism
* [F. Borceux, *Handbook of Categorical Algebra 2*][borceux-vol2]
-/

noncomputable section

universe v u

universe v' u'

open CategoryTheory

open CategoryTheory.Category

open Classical

namespace CategoryTheory.Limits

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

variable (D : Type u') [Category.{v'} D]

#print CategoryTheory.Limits.HasZeroMorphisms /-
/-- A category "has zero morphisms" if there is a designated "zero morphism" in each morphism space,
and compositions of zero morphisms with anything give the zero morphism. -/
class HasZeroMorphisms where
  [Zero : ∀ X Y : C, Zero (X ⟶ Y)]
  comp_zero : ∀ {X Y : C} (f : X ⟶ Y) (Z : C), f ≫ (0 : Y ⟶ Z) = (0 : X ⟶ Z) := by obviously
  zero_comp : ∀ (X : C) {Y Z : C} (f : Y ⟶ Z), (0 : X ⟶ Y) ≫ f = (0 : X ⟶ Z) := by obviously
#align category_theory.limits.has_zero_morphisms CategoryTheory.Limits.HasZeroMorphisms
-/

attribute [instance] has_zero_morphisms.has_zero

restate_axiom has_zero_morphisms.comp_zero'

restate_axiom has_zero_morphisms.zero_comp'

variable {C}

#print CategoryTheory.Limits.comp_zero /-
@[simp]
theorem comp_zero [HasZeroMorphisms C] {X Y : C} {f : X ⟶ Y} {Z : C} :
    f ≫ (0 : Y ⟶ Z) = (0 : X ⟶ Z) :=
  HasZeroMorphisms.comp_zero f Z
#align category_theory.limits.comp_zero CategoryTheory.Limits.comp_zero
-/

#print CategoryTheory.Limits.zero_comp /-
@[simp]
theorem zero_comp [HasZeroMorphisms C] {X : C} {Y Z : C} {f : Y ⟶ Z} :
    (0 : X ⟶ Y) ≫ f = (0 : X ⟶ Z) :=
  HasZeroMorphisms.zero_comp X f
#align category_theory.limits.zero_comp CategoryTheory.Limits.zero_comp
-/

#print CategoryTheory.Limits.hasZeroMorphismsPempty /-
instance hasZeroMorphismsPempty : HasZeroMorphisms (Discrete PEmpty) where Zero := by tidy
#align category_theory.limits.has_zero_morphisms_pempty CategoryTheory.Limits.hasZeroMorphismsPempty
-/

#print CategoryTheory.Limits.hasZeroMorphismsPunit /-
instance hasZeroMorphismsPunit : HasZeroMorphisms (Discrete PUnit) where Zero := by tidy
#align category_theory.limits.has_zero_morphisms_punit CategoryTheory.Limits.hasZeroMorphismsPunit
-/

namespace HasZeroMorphisms

variable {C}

/-- This lemma will be immediately superseded by `ext`, below. -/
private theorem ext_aux (I J : HasZeroMorphisms C)
    (w :
      ∀ X Y : C,
        (@HasZeroMorphisms.hasZero _ _ I X Y).zero = (@HasZeroMorphisms.hasZero _ _ J X Y).zero) :
    I = J := by
  cases I; cases J
  congr
  · ext (X Y)
    exact w X Y
  · apply proof_irrel_heq
  · apply proof_irrel_heq
#align category_theory.limits.has_zero_morphisms.ext_aux category_theory.limits.has_zero_morphisms.ext_aux

#print CategoryTheory.Limits.HasZeroMorphisms.ext /-
/-- If you're tempted to use this lemma "in the wild", you should probably
carefully consider whether you've made a mistake in allowing two
instances of `has_zero_morphisms` to exist at all.

See, particularly, the note on `zero_morphisms_of_zero_object` below.
-/
theorem ext (I J : HasZeroMorphisms C) : I = J :=
  by
  apply ext_aux
  intro X Y
  rw [← @has_zero_morphisms.comp_zero _ _ I X X (@has_zero_morphisms.has_zero _ _ J X X).zero]
  rw [@has_zero_morphisms.zero_comp _ _ J]
#align category_theory.limits.has_zero_morphisms.ext CategoryTheory.Limits.HasZeroMorphisms.ext
-/

instance : Subsingleton (HasZeroMorphisms C) :=
  ⟨ext⟩

end HasZeroMorphisms

open Opposite HasZeroMorphisms

#print CategoryTheory.Limits.hasZeroMorphismsOpposite /-
instance hasZeroMorphismsOpposite [HasZeroMorphisms C] : HasZeroMorphisms Cᵒᵖ
    where
  Zero X Y := ⟨(0 : unop Y ⟶ unop X).op⟩
  comp_zero X Y f Z := congr_arg Quiver.Hom.op (HasZeroMorphisms.zero_comp (unop Z) f.unop)
  zero_comp X Y Z f := congr_arg Quiver.Hom.op (HasZeroMorphisms.comp_zero f.unop (unop X))
#align category_theory.limits.has_zero_morphisms_opposite CategoryTheory.Limits.hasZeroMorphismsOpposite
-/

section

variable {C} [HasZeroMorphisms C]

#print CategoryTheory.Limits.zero_of_comp_mono /-
theorem zero_of_comp_mono {X Y Z : C} {f : X ⟶ Y} (g : Y ⟶ Z) [Mono g] (h : f ≫ g = 0) : f = 0 :=
  by
  rw [← zero_comp, cancel_mono] at h
  exact h
#align category_theory.limits.zero_of_comp_mono CategoryTheory.Limits.zero_of_comp_mono
-/

#print CategoryTheory.Limits.zero_of_epi_comp /-
theorem zero_of_epi_comp {X Y Z : C} (f : X ⟶ Y) {g : Y ⟶ Z} [Epi f] (h : f ≫ g = 0) : g = 0 :=
  by
  rw [← comp_zero, cancel_epi] at h
  exact h
#align category_theory.limits.zero_of_epi_comp CategoryTheory.Limits.zero_of_epi_comp
-/

#print CategoryTheory.Limits.eq_zero_of_image_eq_zero /-
theorem eq_zero_of_image_eq_zero {X Y : C} {f : X ⟶ Y} [HasImage f] (w : image.ι f = 0) : f = 0 :=
  by rw [← image.fac f, w, has_zero_morphisms.comp_zero]
#align category_theory.limits.eq_zero_of_image_eq_zero CategoryTheory.Limits.eq_zero_of_image_eq_zero
-/

#print CategoryTheory.Limits.nonzero_image_of_nonzero /-
theorem nonzero_image_of_nonzero {X Y : C} {f : X ⟶ Y} [HasImage f] (w : f ≠ 0) : image.ι f ≠ 0 :=
  fun h => w (eq_zero_of_image_eq_zero h)
#align category_theory.limits.nonzero_image_of_nonzero CategoryTheory.Limits.nonzero_image_of_nonzero
-/

end

section

variable [HasZeroMorphisms D]

instance : HasZeroMorphisms (C ⥤ D) where Zero F G := ⟨{ app := fun X => 0 }⟩

/- warning: category_theory.limits.zero_app -> CategoryTheory.Limits.zero_app is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (D : Type.{u4}) [_inst_2 : CategoryTheory.Category.{u3, u4} D] [_inst_3 : CategoryTheory.Limits.HasZeroMorphisms.{u3, u4} D _inst_2] (F : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (G : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (j : C), Eq.{succ u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F j) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 G j)) (CategoryTheory.NatTrans.app.{u1, u3, u2, u4} C _inst_1 D _inst_2 F G (OfNat.ofNat.{max u2 u3} (Quiver.Hom.{succ (max u2 u3), max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.CategoryStruct.toQuiver.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Category.toCategoryStruct.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2))) F G) 0 (OfNat.mk.{max u2 u3} (Quiver.Hom.{succ (max u2 u3), max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.CategoryStruct.toQuiver.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Category.toCategoryStruct.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2))) F G) 0 (Zero.zero.{max u2 u3} (Quiver.Hom.{succ (max u2 u3), max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.CategoryStruct.toQuiver.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Category.toCategoryStruct.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2))) F G) (CategoryTheory.Limits.HasZeroMorphisms.hasZero.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.CategoryTheory.Functor.hasZeroMorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 _inst_3) F G)))) j) (OfNat.ofNat.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F j) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 G j)) 0 (OfNat.mk.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F j) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 G j)) 0 (Zero.zero.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F j) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 G j)) (CategoryTheory.Limits.HasZeroMorphisms.hasZero.{u3, u4} D _inst_2 _inst_3 (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F j) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 G j)))))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (D : Type.{u4}) [_inst_2 : CategoryTheory.Category.{u3, u4} D] [_inst_3 : CategoryTheory.Limits.HasZeroMorphisms.{u3, u4} D _inst_2] (F : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (G : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (j : C), Eq.{succ u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) j) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 G) j)) (CategoryTheory.NatTrans.app.{u1, u3, u2, u4} C _inst_1 D _inst_2 F G (OfNat.ofNat.{max u2 u3} (Quiver.Hom.{max (succ u2) (succ u3), max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.CategoryStruct.toQuiver.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Category.toCategoryStruct.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2))) F G) 0 (Zero.toOfNat0.{max u2 u3} (Quiver.Hom.{max (succ u2) (succ u3), max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.CategoryStruct.toQuiver.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Category.toCategoryStruct.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2))) F G) (CategoryTheory.Limits.HasZeroMorphisms.Zero.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.instHasZeroMorphismsFunctorCategory.{u1, u2, u3, u4} C _inst_1 D _inst_2 _inst_3) F G))) j) (OfNat.ofNat.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) j) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 G) j)) 0 (Zero.toOfNat0.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) j) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 G) j)) (CategoryTheory.Limits.HasZeroMorphisms.Zero.{u3, u4} D _inst_2 _inst_3 (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) j) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 G) j))))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.zero_app CategoryTheory.Limits.zero_appₓ'. -/
@[simp]
theorem zero_app (F G : C ⥤ D) (j : C) : (0 : F ⟶ G).app j = 0 :=
  rfl
#align category_theory.limits.zero_app CategoryTheory.Limits.zero_app

end

namespace IsZero

variable [HasZeroMorphisms C]

#print CategoryTheory.Limits.IsZero.eq_zero_of_src /-
theorem eq_zero_of_src {X Y : C} (o : IsZero X) (f : X ⟶ Y) : f = 0 :=
  o.eq_of_src _ _
#align category_theory.limits.is_zero.eq_zero_of_src CategoryTheory.Limits.IsZero.eq_zero_of_src
-/

#print CategoryTheory.Limits.IsZero.eq_zero_of_tgt /-
theorem eq_zero_of_tgt {X Y : C} (o : IsZero Y) (f : X ⟶ Y) : f = 0 :=
  o.eq_of_tgt _ _
#align category_theory.limits.is_zero.eq_zero_of_tgt CategoryTheory.Limits.IsZero.eq_zero_of_tgt
-/

#print CategoryTheory.Limits.IsZero.iff_id_eq_zero /-
theorem iff_id_eq_zero (X : C) : IsZero X ↔ 𝟙 X = 0 :=
  ⟨fun h => h.eq_of_src _ _, fun h =>
    ⟨fun Y => ⟨⟨⟨0⟩, fun f => by rw [← id_comp f, ← id_comp default, h, zero_comp, zero_comp]⟩⟩,
      fun Y => ⟨⟨⟨0⟩, fun f => by rw [← comp_id f, ← comp_id default, h, comp_zero, comp_zero]⟩⟩⟩⟩
#align category_theory.limits.is_zero.iff_id_eq_zero CategoryTheory.Limits.IsZero.iff_id_eq_zero
-/

#print CategoryTheory.Limits.IsZero.of_mono_zero /-
theorem of_mono_zero (X Y : C) [Mono (0 : X ⟶ Y)] : IsZero X :=
  (iff_id_eq_zero X).mpr ((cancel_mono (0 : X ⟶ Y)).1 (by simp))
#align category_theory.limits.is_zero.of_mono_zero CategoryTheory.Limits.IsZero.of_mono_zero
-/

#print CategoryTheory.Limits.IsZero.of_epi_zero /-
theorem of_epi_zero (X Y : C) [Epi (0 : X ⟶ Y)] : IsZero Y :=
  (iff_id_eq_zero Y).mpr ((cancel_epi (0 : X ⟶ Y)).1 (by simp))
#align category_theory.limits.is_zero.of_epi_zero CategoryTheory.Limits.IsZero.of_epi_zero
-/

#print CategoryTheory.Limits.IsZero.of_mono_eq_zero /-
theorem of_mono_eq_zero {X Y : C} (f : X ⟶ Y) [Mono f] (h : f = 0) : IsZero X :=
  by
  subst h
  apply of_mono_zero X Y
#align category_theory.limits.is_zero.of_mono_eq_zero CategoryTheory.Limits.IsZero.of_mono_eq_zero
-/

#print CategoryTheory.Limits.IsZero.of_epi_eq_zero /-
theorem of_epi_eq_zero {X Y : C} (f : X ⟶ Y) [Epi f] (h : f = 0) : IsZero Y :=
  by
  subst h
  apply of_epi_zero X Y
#align category_theory.limits.is_zero.of_epi_eq_zero CategoryTheory.Limits.IsZero.of_epi_eq_zero
-/

#print CategoryTheory.Limits.IsZero.iff_isSplitMono_eq_zero /-
theorem iff_isSplitMono_eq_zero {X Y : C} (f : X ⟶ Y) [IsSplitMono f] : IsZero X ↔ f = 0 :=
  by
  rw [iff_id_eq_zero]
  constructor
  · intro h
    rw [← category.id_comp f, h, zero_comp]
  · intro h
    rw [← is_split_mono.id f]
    simp [h]
#align category_theory.limits.is_zero.iff_is_split_mono_eq_zero CategoryTheory.Limits.IsZero.iff_isSplitMono_eq_zero
-/

#print CategoryTheory.Limits.IsZero.iff_isSplitEpi_eq_zero /-
theorem iff_isSplitEpi_eq_zero {X Y : C} (f : X ⟶ Y) [IsSplitEpi f] : IsZero Y ↔ f = 0 :=
  by
  rw [iff_id_eq_zero]
  constructor
  · intro h
    rw [← category.comp_id f, h, comp_zero]
  · intro h
    rw [← is_split_epi.id f]
    simp [h]
#align category_theory.limits.is_zero.iff_is_split_epi_eq_zero CategoryTheory.Limits.IsZero.iff_isSplitEpi_eq_zero
-/

#print CategoryTheory.Limits.IsZero.of_mono /-
theorem of_mono {X Y : C} (f : X ⟶ Y) [Mono f] (i : IsZero Y) : IsZero X :=
  by
  have hf := i.eq_zero_of_tgt f
  subst hf
  exact is_zero.of_mono_zero X Y
#align category_theory.limits.is_zero.of_mono CategoryTheory.Limits.IsZero.of_mono
-/

#print CategoryTheory.Limits.IsZero.of_epi /-
theorem of_epi {X Y : C} (f : X ⟶ Y) [Epi f] (i : IsZero X) : IsZero Y :=
  by
  have hf := i.eq_zero_of_src f
  subst hf
  exact is_zero.of_epi_zero X Y
#align category_theory.limits.is_zero.of_epi CategoryTheory.Limits.IsZero.of_epi
-/

end IsZero

#print CategoryTheory.Limits.IsZero.hasZeroMorphisms /-
/-- A category with a zero object has zero morphisms.

    It is rarely a good idea to use this. Many categories that have a zero object have zero
    morphisms for some other reason, for example from additivity. Library code that uses
    `zero_morphisms_of_zero_object` will then be incompatible with these categories because
    the `has_zero_morphisms` instances will not be definitionally equal. For this reason library
    code should generally ask for an instance of `has_zero_morphisms` separately, even if it already
    asks for an instance of `has_zero_objects`. -/
def IsZero.hasZeroMorphisms {O : C} (hO : IsZero O) : HasZeroMorphisms C
    where
  Zero X Y := { zero := hO.from X ≫ hO.to Y }
  zero_comp X Y Z f := by
    rw [category.assoc]
    congr
    apply hO.eq_of_src
  comp_zero X Y Z f := by
    rw [← category.assoc]
    congr
    apply hO.eq_of_tgt
#align category_theory.limits.is_zero.has_zero_morphisms CategoryTheory.Limits.IsZero.hasZeroMorphisms
-/

namespace HasZeroObject

variable [HasZeroObject C]

open ZeroObject

#print CategoryTheory.Limits.HasZeroObject.zeroMorphismsOfZeroObject /-
/-- A category with a zero object has zero morphisms.

    It is rarely a good idea to use this. Many categories that have a zero object have zero
    morphisms for some other reason, for example from additivity. Library code that uses
    `zero_morphisms_of_zero_object` will then be incompatible with these categories because
    the `has_zero_morphisms` instances will not be definitionally equal. For this reason library
    code should generally ask for an instance of `has_zero_morphisms` separately, even if it already
    asks for an instance of `has_zero_objects`. -/
def zeroMorphismsOfZeroObject : HasZeroMorphisms C
    where
  Zero X Y := { zero := (default : X ⟶ 0) ≫ default }
  zero_comp X Y Z f := by
    dsimp only [Zero.zero]
    rw [category.assoc]
    congr
  comp_zero X Y Z f := by
    dsimp only [Zero.zero]
    rw [← category.assoc]
    congr
#align category_theory.limits.has_zero_object.zero_morphisms_of_zero_object CategoryTheory.Limits.HasZeroObject.zeroMorphismsOfZeroObject
-/

section HasZeroMorphisms

variable [HasZeroMorphisms C]

#print CategoryTheory.Limits.HasZeroObject.zeroIsoIsInitial_hom /-
@[simp]
theorem zeroIsoIsInitial_hom {X : C} (t : IsInitial X) : (zeroIsoIsInitial t).Hom = 0 := by ext
#align category_theory.limits.has_zero_object.zero_iso_is_initial_hom CategoryTheory.Limits.HasZeroObject.zeroIsoIsInitial_hom
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoIsInitial_inv /-
@[simp]
theorem zeroIsoIsInitial_inv {X : C} (t : IsInitial X) : (zeroIsoIsInitial t).inv = 0 := by ext
#align category_theory.limits.has_zero_object.zero_iso_is_initial_inv CategoryTheory.Limits.HasZeroObject.zeroIsoIsInitial_inv
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoIsTerminal_hom /-
@[simp]
theorem zeroIsoIsTerminal_hom {X : C} (t : IsTerminal X) : (zeroIsoIsTerminal t).Hom = 0 := by ext
#align category_theory.limits.has_zero_object.zero_iso_is_terminal_hom CategoryTheory.Limits.HasZeroObject.zeroIsoIsTerminal_hom
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoIsTerminal_inv /-
@[simp]
theorem zeroIsoIsTerminal_inv {X : C} (t : IsTerminal X) : (zeroIsoIsTerminal t).inv = 0 := by ext
#align category_theory.limits.has_zero_object.zero_iso_is_terminal_inv CategoryTheory.Limits.HasZeroObject.zeroIsoIsTerminal_inv
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoInitial_hom /-
@[simp]
theorem zeroIsoInitial_hom [HasInitial C] : zeroIsoInitial.Hom = (0 : 0 ⟶ ⊥_ C) := by ext
#align category_theory.limits.has_zero_object.zero_iso_initial_hom CategoryTheory.Limits.HasZeroObject.zeroIsoInitial_hom
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoInitial_inv /-
@[simp]
theorem zeroIsoInitial_inv [HasInitial C] : zeroIsoInitial.inv = (0 : ⊥_ C ⟶ 0) := by ext
#align category_theory.limits.has_zero_object.zero_iso_initial_inv CategoryTheory.Limits.HasZeroObject.zeroIsoInitial_inv
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoTerminal_hom /-
@[simp]
theorem zeroIsoTerminal_hom [HasTerminal C] : zeroIsoTerminal.Hom = (0 : 0 ⟶ ⊤_ C) := by ext
#align category_theory.limits.has_zero_object.zero_iso_terminal_hom CategoryTheory.Limits.HasZeroObject.zeroIsoTerminal_hom
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoTerminal_inv /-
@[simp]
theorem zeroIsoTerminal_inv [HasTerminal C] : zeroIsoTerminal.inv = (0 : ⊤_ C ⟶ 0) := by ext
#align category_theory.limits.has_zero_object.zero_iso_terminal_inv CategoryTheory.Limits.HasZeroObject.zeroIsoTerminal_inv
-/

end HasZeroMorphisms

open ZeroObject

instance {B : Type _} [Category B] : HasZeroObject (B ⥤ C) :=
  (((CategoryTheory.Functor.const B).obj (0 : C)).IsZero fun X => isZero_zero _).HasZeroObject

end HasZeroObject

open ZeroObject

variable {D}

/- warning: category_theory.limits.is_zero.map -> CategoryTheory.Limits.IsZero.map is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u3, u4} D] [_inst_3 : CategoryTheory.Limits.HasZeroObject.{u3, u4} D _inst_2] [_inst_4 : CategoryTheory.Limits.HasZeroMorphisms.{u3, u4} D _inst_2] {F : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2}, (CategoryTheory.Limits.IsZero.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) F) -> (forall {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y), Eq.{succ u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F Y)) (CategoryTheory.Functor.map.{u1, u3, u2, u4} C _inst_1 D _inst_2 F X Y f) (OfNat.ofNat.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F Y)) 0 (OfNat.mk.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F Y)) 0 (Zero.zero.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F Y)) (CategoryTheory.Limits.HasZeroMorphisms.hasZero.{u3, u4} D _inst_2 _inst_4 (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F Y))))))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u3, u4} D] [_inst_3 : CategoryTheory.Limits.HasZeroObject.{u3, u4} D _inst_2] [_inst_4 : CategoryTheory.Limits.HasZeroMorphisms.{u3, u4} D _inst_2] {F : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2}, (CategoryTheory.Limits.IsZero.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) F) -> (forall {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y), Eq.{succ u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) Y)) (Prefunctor.map.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) X Y f) (OfNat.ofNat.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) Y)) 0 (Zero.toOfNat0.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) Y)) (CategoryTheory.Limits.HasZeroMorphisms.Zero.{u3, u4} D _inst_2 _inst_4 (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 F) Y)))))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_zero.map CategoryTheory.Limits.IsZero.mapₓ'. -/
@[simp]
theorem IsZero.map [HasZeroObject D] [HasZeroMorphisms D] {F : C ⥤ D} (hF : IsZero F) {X Y : C}
    (f : X ⟶ Y) : F.map f = 0 :=
  (hF.obj _).eq_of_src _ _
#align category_theory.limits.is_zero.map CategoryTheory.Limits.IsZero.map

/- warning: category_theory.functor.zero_obj -> CategoryTheory.Functor.zero_obj is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u3, u4} D] [_inst_3 : CategoryTheory.Limits.HasZeroObject.{u3, u4} D _inst_2] (X : C), CategoryTheory.Limits.IsZero.{u3, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (OfNat.mk.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) X)
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u3, u4} D] [_inst_3 : CategoryTheory.Limits.HasZeroObject.{u3, u4} D _inst_2] (X : C), CategoryTheory.Limits.IsZero.{u3, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) X)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.zero_obj CategoryTheory.Functor.zero_objₓ'. -/
@[simp]
theorem CategoryTheory.Functor.zero_obj [HasZeroObject D] (X : C) : IsZero ((0 : C ⥤ D).obj X) :=
  (isZero_zero _).obj _
#align category_theory.functor.zero_obj CategoryTheory.Functor.zero_obj

/- warning: category_theory.zero_map -> CategoryTheory.zero_map is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u3, u4} D] [_inst_3 : CategoryTheory.Limits.HasZeroObject.{u3, u4} D _inst_2] [_inst_4 : CategoryTheory.Limits.HasZeroMorphisms.{u3, u4} D _inst_2] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y), Eq.{succ u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (OfNat.mk.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (OfNat.mk.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) Y)) (CategoryTheory.Functor.map.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (OfNat.mk.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) X Y f) (OfNat.ofNat.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))) X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))) Y)) 0 (OfNat.mk.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))) X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))) Y)) 0 (Zero.zero.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))) X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))) Y)) (CategoryTheory.Limits.HasZeroMorphisms.hasZero.{u3, u4} D _inst_2 _inst_4 (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))) X) (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 (Zero.zero.{max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max u1 u3 u2 u4} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.CategoryTheory.Functor.hasZeroObject.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))) Y)))))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u3, u4} D] [_inst_3 : CategoryTheory.Limits.HasZeroObject.{u3, u4} D _inst_2] [_inst_4 : CategoryTheory.Limits.HasZeroMorphisms.{u3, u4} D _inst_2] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y), Eq.{succ u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) X) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) Y)) (Prefunctor.map.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) X Y f) (OfNat.ofNat.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) X) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) Y)) 0 (Zero.toOfNat0.{u3} (Quiver.Hom.{succ u3, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) X) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) Y)) (CategoryTheory.Limits.HasZeroMorphisms.Zero.{u3, u4} D _inst_2 _inst_4 (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) X) (Prefunctor.obj.{succ u1, succ u3, u2, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u4} D (CategoryTheory.Category.toCategoryStruct.{u3, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u3, u2, u4} C _inst_1 D _inst_2 (OfNat.ofNat.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) 0 (Zero.toOfNat0.{max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.zero'.{max u2 u3, max (max (max u2 u4) u1) u3} (CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u3, u2, u4} C _inst_1 D _inst_2) (CategoryTheory.Limits.HasZeroObject.instHasZeroObjectFunctorCategory.{u3, u4, u2, u1} D _inst_2 _inst_3 C _inst_1))))) Y))))
Case conversion may be inaccurate. Consider using '#align category_theory.zero_map CategoryTheory.zero_mapₓ'. -/
@[simp]
theorem CategoryTheory.zero_map [HasZeroObject D] [HasZeroMorphisms D] {X Y : C} (f : X ⟶ Y) :
    (0 : C ⥤ D).map f = 0 :=
  (isZero_zero _).map _
#align category_theory.zero_map CategoryTheory.zero_map

section

variable [HasZeroObject C] [HasZeroMorphisms C]

open ZeroObject

#print CategoryTheory.Limits.id_zero /-
@[simp]
theorem id_zero : 𝟙 (0 : C) = (0 : 0 ⟶ 0) := by ext
#align category_theory.limits.id_zero CategoryTheory.Limits.id_zero
-/

#print CategoryTheory.Limits.zero_of_to_zero /-
-- This can't be a `simp` lemma because the left hand side would be a metavariable.
/-- An arrow ending in the zero object is zero -/
theorem zero_of_to_zero {X : C} (f : X ⟶ 0) : f = 0 := by ext
#align category_theory.limits.zero_of_to_zero CategoryTheory.Limits.zero_of_to_zero
-/

#print CategoryTheory.Limits.zero_of_target_iso_zero /-
theorem zero_of_target_iso_zero {X Y : C} (f : X ⟶ Y) (i : Y ≅ 0) : f = 0 :=
  by
  have h : f = f ≫ i.hom ≫ 𝟙 0 ≫ i.inv := by simp only [iso.hom_inv_id, id_comp, comp_id]
  simpa using h
#align category_theory.limits.zero_of_target_iso_zero CategoryTheory.Limits.zero_of_target_iso_zero
-/

#print CategoryTheory.Limits.zero_of_from_zero /-
/-- An arrow starting at the zero object is zero -/
theorem zero_of_from_zero {X : C} (f : 0 ⟶ X) : f = 0 := by ext
#align category_theory.limits.zero_of_from_zero CategoryTheory.Limits.zero_of_from_zero
-/

#print CategoryTheory.Limits.zero_of_source_iso_zero /-
theorem zero_of_source_iso_zero {X Y : C} (f : X ⟶ Y) (i : X ≅ 0) : f = 0 :=
  by
  have h : f = i.hom ≫ 𝟙 0 ≫ i.inv ≫ f := by simp only [iso.hom_inv_id_assoc, id_comp, comp_id]
  simpa using h
#align category_theory.limits.zero_of_source_iso_zero CategoryTheory.Limits.zero_of_source_iso_zero
-/

#print CategoryTheory.Limits.zero_of_source_iso_zero' /-
theorem zero_of_source_iso_zero' {X Y : C} (f : X ⟶ Y) (i : IsIsomorphic X 0) : f = 0 :=
  zero_of_source_iso_zero f (Nonempty.some i)
#align category_theory.limits.zero_of_source_iso_zero' CategoryTheory.Limits.zero_of_source_iso_zero'
-/

#print CategoryTheory.Limits.zero_of_target_iso_zero' /-
theorem zero_of_target_iso_zero' {X Y : C} (f : X ⟶ Y) (i : IsIsomorphic Y 0) : f = 0 :=
  zero_of_target_iso_zero f (Nonempty.some i)
#align category_theory.limits.zero_of_target_iso_zero' CategoryTheory.Limits.zero_of_target_iso_zero'
-/

#print CategoryTheory.Limits.mono_of_source_iso_zero /-
theorem mono_of_source_iso_zero {X Y : C} (f : X ⟶ Y) (i : X ≅ 0) : Mono f :=
  ⟨fun Z g h w => by rw [zero_of_target_iso_zero g i, zero_of_target_iso_zero h i]⟩
#align category_theory.limits.mono_of_source_iso_zero CategoryTheory.Limits.mono_of_source_iso_zero
-/

#print CategoryTheory.Limits.epi_of_target_iso_zero /-
theorem epi_of_target_iso_zero {X Y : C} (f : X ⟶ Y) (i : Y ≅ 0) : Epi f :=
  ⟨fun Z g h w => by rw [zero_of_source_iso_zero g i, zero_of_source_iso_zero h i]⟩
#align category_theory.limits.epi_of_target_iso_zero CategoryTheory.Limits.epi_of_target_iso_zero
-/

#print CategoryTheory.Limits.idZeroEquivIsoZero /-
/-- An object `X` has `𝟙 X = 0` if and only if it is isomorphic to the zero object.

Because `X ≅ 0` contains data (even if a subsingleton), we express this `↔` as an `≃`.
-/
def idZeroEquivIsoZero (X : C) : 𝟙 X = 0 ≃ (X ≅ 0)
    where
  toFun h :=
    { Hom := 0
      inv := 0 }
  invFun i := zero_of_target_iso_zero (𝟙 X) i
  left_inv := by tidy
  right_inv := by tidy
#align category_theory.limits.id_zero_equiv_iso_zero CategoryTheory.Limits.idZeroEquivIsoZero
-/

#print CategoryTheory.Limits.idZeroEquivIsoZero_apply_hom /-
@[simp]
theorem idZeroEquivIsoZero_apply_hom (X : C) (h : 𝟙 X = 0) : ((idZeroEquivIsoZero X) h).Hom = 0 :=
  rfl
#align category_theory.limits.id_zero_equiv_iso_zero_apply_hom CategoryTheory.Limits.idZeroEquivIsoZero_apply_hom
-/

#print CategoryTheory.Limits.idZeroEquivIsoZero_apply_inv /-
@[simp]
theorem idZeroEquivIsoZero_apply_inv (X : C) (h : 𝟙 X = 0) : ((idZeroEquivIsoZero X) h).inv = 0 :=
  rfl
#align category_theory.limits.id_zero_equiv_iso_zero_apply_inv CategoryTheory.Limits.idZeroEquivIsoZero_apply_inv
-/

#print CategoryTheory.Limits.isoZeroOfMonoZero /-
/-- If `0 : X ⟶ Y` is an monomorphism, then `X ≅ 0`. -/
@[simps]
def isoZeroOfMonoZero {X Y : C} (h : Mono (0 : X ⟶ Y)) : X ≅ 0
    where
  Hom := 0
  inv := 0
  hom_inv_id' := (cancel_mono (0 : X ⟶ Y)).mp (by simp)
#align category_theory.limits.iso_zero_of_mono_zero CategoryTheory.Limits.isoZeroOfMonoZero
-/

#print CategoryTheory.Limits.isoZeroOfEpiZero /-
/-- If `0 : X ⟶ Y` is an epimorphism, then `Y ≅ 0`. -/
@[simps]
def isoZeroOfEpiZero {X Y : C} (h : Epi (0 : X ⟶ Y)) : Y ≅ 0
    where
  Hom := 0
  inv := 0
  hom_inv_id' := (cancel_epi (0 : X ⟶ Y)).mp (by simp)
#align category_theory.limits.iso_zero_of_epi_zero CategoryTheory.Limits.isoZeroOfEpiZero
-/

#print CategoryTheory.Limits.isoZeroOfMonoEqZero /-
/-- If a monomorphism out of `X` is zero, then `X ≅ 0`. -/
def isoZeroOfMonoEqZero {X Y : C} {f : X ⟶ Y} [Mono f] (h : f = 0) : X ≅ 0 :=
  by
  subst h
  apply iso_zero_of_mono_zero ‹_›
#align category_theory.limits.iso_zero_of_mono_eq_zero CategoryTheory.Limits.isoZeroOfMonoEqZero
-/

#print CategoryTheory.Limits.isoZeroOfEpiEqZero /-
/-- If an epimorphism in to `Y` is zero, then `Y ≅ 0`. -/
def isoZeroOfEpiEqZero {X Y : C} {f : X ⟶ Y} [Epi f] (h : f = 0) : Y ≅ 0 :=
  by
  subst h
  apply iso_zero_of_epi_zero ‹_›
#align category_theory.limits.iso_zero_of_epi_eq_zero CategoryTheory.Limits.isoZeroOfEpiEqZero
-/

#print CategoryTheory.Limits.isoOfIsIsomorphicZero /-
/-- If an object `X` is isomorphic to 0, there's no need to use choice to construct
an explicit isomorphism: the zero morphism suffices. -/
def isoOfIsIsomorphicZero {X : C} (P : IsIsomorphic X 0) : X ≅ 0
    where
  Hom := 0
  inv := 0
  hom_inv_id' := by
    cases P
    rw [← P.hom_inv_id]
    rw [← category.id_comp P.inv]
    simp
  inv_hom_id' := by simp
#align category_theory.limits.iso_of_is_isomorphic_zero CategoryTheory.Limits.isoOfIsIsomorphicZero
-/

end

section IsIso

variable [HasZeroMorphisms C]

#print CategoryTheory.Limits.isIsoZeroEquiv /-
/-- A zero morphism `0 : X ⟶ Y` is an isomorphism if and only if
the identities on both `X` and `Y` are zero.
-/
@[simps]
def isIsoZeroEquiv (X Y : C) : IsIso (0 : X ⟶ Y) ≃ 𝟙 X = 0 ∧ 𝟙 Y = 0
    where
  toFun := by
    intro i
    rw [← is_iso.hom_inv_id (0 : X ⟶ Y)]
    rw [← is_iso.inv_hom_id (0 : X ⟶ Y)]
    simp
  invFun h := ⟨⟨(0 : Y ⟶ X), by tidy⟩⟩
  left_inv := by tidy
  right_inv := by tidy
#align category_theory.limits.is_iso_zero_equiv CategoryTheory.Limits.isIsoZeroEquiv
-/

#print CategoryTheory.Limits.isIsoZeroSelfEquiv /-
/-- A zero morphism `0 : X ⟶ X` is an isomorphism if and only if
the identity on `X` is zero.
-/
def isIsoZeroSelfEquiv (X : C) : IsIso (0 : X ⟶ X) ≃ 𝟙 X = 0 := by simpa using is_iso_zero_equiv X X
#align category_theory.limits.is_iso_zero_self_equiv CategoryTheory.Limits.isIsoZeroSelfEquiv
-/

variable [HasZeroObject C]

open ZeroObject

#print CategoryTheory.Limits.isIsoZeroEquivIsoZero /-
/-- A zero morphism `0 : X ⟶ Y` is an isomorphism if and only if
`X` and `Y` are isomorphic to the zero object.
-/
def isIsoZeroEquivIsoZero (X Y : C) : IsIso (0 : X ⟶ Y) ≃ (X ≅ 0) × (Y ≅ 0) :=
  by
  -- This is lame, because `prod` can't cope with `Prop`, so we can't use `equiv.prod_congr`.
  refine' (is_iso_zero_equiv X Y).trans _
  symm
  fconstructor
  · rintro ⟨eX, eY⟩
    fconstructor
    exact (id_zero_equiv_iso_zero X).symm eX
    exact (id_zero_equiv_iso_zero Y).symm eY
  · rintro ⟨hX, hY⟩
    fconstructor
    exact (id_zero_equiv_iso_zero X) hX
    exact (id_zero_equiv_iso_zero Y) hY
  · tidy
  · tidy
#align category_theory.limits.is_iso_zero_equiv_iso_zero CategoryTheory.Limits.isIsoZeroEquivIsoZero
-/

#print CategoryTheory.Limits.isIso_of_source_target_iso_zero /-
theorem isIso_of_source_target_iso_zero {X Y : C} (f : X ⟶ Y) (i : X ≅ 0) (j : Y ≅ 0) : IsIso f :=
  by
  rw [zero_of_source_iso_zero f i]
  exact (is_iso_zero_equiv_iso_zero _ _).invFun ⟨i, j⟩
#align category_theory.limits.is_iso_of_source_target_iso_zero CategoryTheory.Limits.isIso_of_source_target_iso_zero
-/

#print CategoryTheory.Limits.isIsoZeroSelfEquivIsoZero /-
/-- A zero morphism `0 : X ⟶ X` is an isomorphism if and only if
`X` is isomorphic to the zero object.
-/
def isIsoZeroSelfEquivIsoZero (X : C) : IsIso (0 : X ⟶ X) ≃ (X ≅ 0) :=
  (isIsoZeroEquivIsoZero X X).trans subsingletonProdSelfEquiv
#align category_theory.limits.is_iso_zero_self_equiv_iso_zero CategoryTheory.Limits.isIsoZeroSelfEquivIsoZero
-/

end IsIso

#print CategoryTheory.Limits.hasZeroObject_of_hasInitial_object /-
/-- If there are zero morphisms, any initial object is a zero object. -/
theorem hasZeroObject_of_hasInitial_object [HasZeroMorphisms C] [HasInitial C] : HasZeroObject C :=
  by
  refine' ⟨⟨⊥_ C, fun X => ⟨⟨⟨0⟩, by tidy⟩⟩, fun X => ⟨⟨⟨0⟩, fun f => _⟩⟩⟩⟩
  calc
    f = f ≫ 𝟙 _ := (category.comp_id _).symm
    _ = f ≫ 0 := by congr
    _ = 0 := has_zero_morphisms.comp_zero _ _

#align category_theory.limits.has_zero_object_of_has_initial_object CategoryTheory.Limits.hasZeroObject_of_hasInitial_object
-/

#print CategoryTheory.Limits.hasZeroObject_of_hasTerminal_object /-
/-- If there are zero morphisms, any terminal object is a zero object. -/
theorem hasZeroObject_of_hasTerminal_object [HasZeroMorphisms C] [HasTerminal C] :
    HasZeroObject C :=
  by
  refine' ⟨⟨⊤_ C, fun X => ⟨⟨⟨0⟩, fun f => _⟩⟩, fun X => ⟨⟨⟨0⟩, by tidy⟩⟩⟩⟩
  calc
    f = 𝟙 _ ≫ f := (category.id_comp _).symm
    _ = 0 ≫ f := by congr
    _ = 0 := zero_comp

#align category_theory.limits.has_zero_object_of_has_terminal_object CategoryTheory.Limits.hasZeroObject_of_hasTerminal_object
-/

section Image

variable [HasZeroMorphisms C]

#print CategoryTheory.Limits.image_ι_comp_eq_zero /-
theorem image_ι_comp_eq_zero {X Y Z : C} {f : X ⟶ Y} {g : Y ⟶ Z} [HasImage f]
    [Epi (factorThruImage f)] (h : f ≫ g = 0) : image.ι f ≫ g = 0 :=
  zero_of_epi_comp (factorThruImage f) <| by simp [h]
#align category_theory.limits.image_ι_comp_eq_zero CategoryTheory.Limits.image_ι_comp_eq_zero
-/

#print CategoryTheory.Limits.comp_factorThruImage_eq_zero /-
theorem comp_factorThruImage_eq_zero {X Y Z : C} {f : X ⟶ Y} {g : Y ⟶ Z} [HasImage g]
    (h : f ≫ g = 0) : f ≫ factorThruImage g = 0 :=
  zero_of_comp_mono (image.ι g) <| by simp [h]
#align category_theory.limits.comp_factor_thru_image_eq_zero CategoryTheory.Limits.comp_factorThruImage_eq_zero
-/

variable [HasZeroObject C]

open ZeroObject

#print CategoryTheory.Limits.monoFactorisationZero /-
/-- The zero morphism has a `mono_factorisation` through the zero object.
-/
@[simps]
def monoFactorisationZero (X Y : C) : MonoFactorisation (0 : X ⟶ Y)
    where
  i := 0
  m := 0
  e := 0
#align category_theory.limits.mono_factorisation_zero CategoryTheory.Limits.monoFactorisationZero
-/

#print CategoryTheory.Limits.imageFactorisationZero /-
/-- The factorisation through the zero object is an image factorisation.
-/
def imageFactorisationZero (X Y : C) : ImageFactorisation (0 : X ⟶ Y)
    where
  f := monoFactorisationZero X Y
  IsImage := { lift := fun F' => 0 }
#align category_theory.limits.image_factorisation_zero CategoryTheory.Limits.imageFactorisationZero
-/

#print CategoryTheory.Limits.hasImage_zero /-
instance hasImage_zero {X Y : C} : HasImage (0 : X ⟶ Y) :=
  HasImage.mk <| imageFactorisationZero _ _
#align category_theory.limits.has_image_zero CategoryTheory.Limits.hasImage_zero
-/

#print CategoryTheory.Limits.imageZero /-
/-- The image of a zero morphism is the zero object. -/
def imageZero {X Y : C} : image (0 : X ⟶ Y) ≅ 0 :=
  IsImage.isoExt (Image.isImage (0 : X ⟶ Y)) (imageFactorisationZero X Y).IsImage
#align category_theory.limits.image_zero CategoryTheory.Limits.imageZero
-/

#print CategoryTheory.Limits.imageZero' /-
/-- The image of a morphism which is equal to zero is the zero object. -/
def imageZero' {X Y : C} {f : X ⟶ Y} (h : f = 0) [HasImage f] : image f ≅ 0 :=
  image.eqToIso h ≪≫ imageZero
#align category_theory.limits.image_zero' CategoryTheory.Limits.imageZero'
-/

#print CategoryTheory.Limits.image.ι_zero /-
@[simp]
theorem image.ι_zero {X Y : C} [HasImage (0 : X ⟶ Y)] : image.ι (0 : X ⟶ Y) = 0 :=
  by
  rw [← image.lift_fac (mono_factorisation_zero X Y)]
  simp
#align category_theory.limits.image.ι_zero CategoryTheory.Limits.image.ι_zero
-/

#print CategoryTheory.Limits.image.ι_zero' /-
/-- If we know `f = 0`,
it requires a little work to conclude `image.ι f = 0`,
because `f = g` only implies `image f ≅ image g`.
-/
@[simp]
theorem image.ι_zero' [HasEqualizers C] {X Y : C} {f : X ⟶ Y} (h : f = 0) [HasImage f] :
    image.ι f = 0 := by
  rw [image.eq_fac h]
  simp
#align category_theory.limits.image.ι_zero' CategoryTheory.Limits.image.ι_zero'
-/

end Image

#print CategoryTheory.Limits.isSplitMono_sigma_ι /-
/-- In the presence of zero morphisms, coprojections into a coproduct are (split) monomorphisms. -/
instance isSplitMono_sigma_ι {β : Type u'} [HasZeroMorphisms C] (f : β → C)
    [HasColimit (Discrete.functor f)] (b : β) : IsSplitMono (Sigma.ι f b) :=
  IsSplitMono.mk' { retraction := Sigma.desc <| Pi.single b (𝟙 _) }
#align category_theory.limits.is_split_mono_sigma_ι CategoryTheory.Limits.isSplitMono_sigma_ι
-/

#print CategoryTheory.Limits.isSplitEpi_pi_π /-
/-- In the presence of zero morphisms, projections into a product are (split) epimorphisms. -/
instance isSplitEpi_pi_π {β : Type u'} [HasZeroMorphisms C] (f : β → C)
    [HasLimit (Discrete.functor f)] (b : β) : IsSplitEpi (Pi.π f b) :=
  IsSplitEpi.mk' { section_ := Pi.lift <| Pi.single b (𝟙 _) }
#align category_theory.limits.is_split_epi_pi_π CategoryTheory.Limits.isSplitEpi_pi_π
-/

#print CategoryTheory.Limits.isSplitMono_coprod_inl /-
/-- In the presence of zero morphisms, coprojections into a coproduct are (split) monomorphisms. -/
instance isSplitMono_coprod_inl [HasZeroMorphisms C] {X Y : C} [HasColimit (pair X Y)] :
    IsSplitMono (coprod.inl : X ⟶ X ⨿ Y) :=
  IsSplitMono.mk' { retraction := coprod.desc (𝟙 X) 0 }
#align category_theory.limits.is_split_mono_coprod_inl CategoryTheory.Limits.isSplitMono_coprod_inl
-/

#print CategoryTheory.Limits.isSplitMono_coprod_inr /-
/-- In the presence of zero morphisms, coprojections into a coproduct are (split) monomorphisms. -/
instance isSplitMono_coprod_inr [HasZeroMorphisms C] {X Y : C} [HasColimit (pair X Y)] :
    IsSplitMono (coprod.inr : Y ⟶ X ⨿ Y) :=
  IsSplitMono.mk' { retraction := coprod.desc 0 (𝟙 Y) }
#align category_theory.limits.is_split_mono_coprod_inr CategoryTheory.Limits.isSplitMono_coprod_inr
-/

#print CategoryTheory.Limits.isSplitEpi_prod_fst /-
/-- In the presence of zero morphisms, projections into a product are (split) epimorphisms. -/
instance isSplitEpi_prod_fst [HasZeroMorphisms C] {X Y : C} [HasLimit (pair X Y)] :
    IsSplitEpi (prod.fst : X ⨯ Y ⟶ X) :=
  IsSplitEpi.mk' { section_ := prod.lift (𝟙 X) 0 }
#align category_theory.limits.is_split_epi_prod_fst CategoryTheory.Limits.isSplitEpi_prod_fst
-/

#print CategoryTheory.Limits.isSplitEpi_prod_snd /-
/-- In the presence of zero morphisms, projections into a product are (split) epimorphisms. -/
instance isSplitEpi_prod_snd [HasZeroMorphisms C] {X Y : C} [HasLimit (pair X Y)] :
    IsSplitEpi (prod.snd : X ⨯ Y ⟶ Y) :=
  IsSplitEpi.mk' { section_ := prod.lift 0 (𝟙 Y) }
#align category_theory.limits.is_split_epi_prod_snd CategoryTheory.Limits.isSplitEpi_prod_snd
-/

end CategoryTheory.Limits