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

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

/-!
# Zero objects

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

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;
see `category_theory.limits.shapes.zero_morphisms`.

## References

* [F. Borceux, *Handbook of Categorical Algebra 2*][borceux-vol2]
-/

noncomputable section

universe v u v' u'

open CategoryTheory

open CategoryTheory.Category

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

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

namespace CategoryTheory

namespace Limits

#print CategoryTheory.Limits.IsZero /-
/-- An object `X` in a category is a *zero object* if for every object `Y`
there is a unique morphism `to : X → Y` and a unique morphism `from : Y → X`.

This is a characteristic predicate for `has_zero_object`. -/
structure IsZero (X : C) : Prop where
  unique_to : ∀ Y, Nonempty (Unique (X ⟶ Y))
  unique_from : ∀ Y, Nonempty (Unique (Y ⟶ X))
#align category_theory.limits.is_zero CategoryTheory.Limits.IsZero
-/

namespace IsZero

variable {X Y : C}

#print CategoryTheory.Limits.IsZero.to_ /-
/-- If `h : is_zero X`, then `h.to Y` is a choice of unique morphism `X → Y`. -/
protected def to_ (h : IsZero X) (Y : C) : X ⟶ Y :=
  @default (X ⟶ Y) <| @Unique.inhabited _ <| (h.unique_to Y).some
#align category_theory.limits.is_zero.to CategoryTheory.Limits.IsZero.to_
-/

#print CategoryTheory.Limits.IsZero.eq_to /-
theorem eq_to (h : IsZero X) (f : X ⟶ Y) : f = h.to Y :=
  @Unique.eq_default _ (id _) _
#align category_theory.limits.is_zero.eq_to CategoryTheory.Limits.IsZero.eq_to
-/

#print CategoryTheory.Limits.IsZero.to_eq /-
theorem to_eq (h : IsZero X) (f : X ⟶ Y) : h.to Y = f :=
  (h.eq_to f).symm
#align category_theory.limits.is_zero.to_eq CategoryTheory.Limits.IsZero.to_eq
-/

#print CategoryTheory.Limits.IsZero.from_ /-
/-- If `h : is_zero X`, then `h.from Y` is a choice of unique morphism `Y → X`. -/
protected def from_ (h : IsZero X) (Y : C) : Y ⟶ X :=
  @default (Y ⟶ X) <| @Unique.inhabited _ <| (h.unique_from Y).some
#align category_theory.limits.is_zero.from CategoryTheory.Limits.IsZero.from_
-/

#print CategoryTheory.Limits.IsZero.eq_from /-
theorem eq_from (h : IsZero X) (f : Y ⟶ X) : f = h.from Y :=
  @Unique.eq_default _ (id _) _
#align category_theory.limits.is_zero.eq_from CategoryTheory.Limits.IsZero.eq_from
-/

#print CategoryTheory.Limits.IsZero.from_eq /-
theorem from_eq (h : IsZero X) (f : Y ⟶ X) : h.from Y = f :=
  (h.eq_from f).symm
#align category_theory.limits.is_zero.from_eq CategoryTheory.Limits.IsZero.from_eq
-/

#print CategoryTheory.Limits.IsZero.eq_of_src /-
theorem eq_of_src (hX : IsZero X) (f g : X ⟶ Y) : f = g :=
  (hX.eq_to f).trans (hX.eq_to g).symm
#align category_theory.limits.is_zero.eq_of_src CategoryTheory.Limits.IsZero.eq_of_src
-/

#print CategoryTheory.Limits.IsZero.eq_of_tgt /-
theorem eq_of_tgt (hX : IsZero X) (f g : Y ⟶ X) : f = g :=
  (hX.eq_from f).trans (hX.eq_from g).symm
#align category_theory.limits.is_zero.eq_of_tgt CategoryTheory.Limits.IsZero.eq_of_tgt
-/

#print CategoryTheory.Limits.IsZero.iso /-
/-- Any two zero objects are isomorphic. -/
def iso (hX : IsZero X) (hY : IsZero Y) : X ≅ Y
    where
  Hom := hX.to Y
  inv := hX.from Y
  hom_inv_id' := hX.eq_of_src _ _
  inv_hom_id' := hY.eq_of_src _ _
#align category_theory.limits.is_zero.iso CategoryTheory.Limits.IsZero.iso
-/

#print CategoryTheory.Limits.IsZero.isInitial /-
/-- A zero object is in particular initial. -/
protected def isInitial (hX : IsZero X) : IsInitial X :=
  @IsInitial.ofUnique _ _ X fun Y => (hX.unique_to Y).some
#align category_theory.limits.is_zero.is_initial CategoryTheory.Limits.IsZero.isInitial
-/

#print CategoryTheory.Limits.IsZero.isTerminal /-
/-- A zero object is in particular terminal. -/
protected def isTerminal (hX : IsZero X) : IsTerminal X :=
  @IsTerminal.ofUnique _ _ X fun Y => (hX.unique_from Y).some
#align category_theory.limits.is_zero.is_terminal CategoryTheory.Limits.IsZero.isTerminal
-/

#print CategoryTheory.Limits.IsZero.isoIsInitial /-
/-- The (unique) isomorphism between any initial object and the zero object. -/
def isoIsInitial (hX : IsZero X) (hY : IsInitial Y) : X ≅ Y :=
  hX.IsInitial.uniqueUpToIso hY
#align category_theory.limits.is_zero.iso_is_initial CategoryTheory.Limits.IsZero.isoIsInitial
-/

#print CategoryTheory.Limits.IsZero.isoIsTerminal /-
/-- The (unique) isomorphism between any terminal object and the zero object. -/
def isoIsTerminal (hX : IsZero X) (hY : IsTerminal Y) : X ≅ Y :=
  hX.IsTerminal.uniqueUpToIso hY
#align category_theory.limits.is_zero.iso_is_terminal CategoryTheory.Limits.IsZero.isoIsTerminal
-/

#print CategoryTheory.Limits.IsZero.of_iso /-
theorem of_iso (hY : IsZero Y) (e : X ≅ Y) : IsZero X :=
  by
  refine' ⟨fun Z => ⟨⟨⟨e.hom ≫ hY.to Z⟩, fun f => _⟩⟩, fun Z => ⟨⟨⟨hY.from Z ≫ e.inv⟩, fun f => _⟩⟩⟩
  · rw [← cancel_epi e.inv]
    apply hY.eq_of_src
  · rw [← cancel_mono e.hom]
    apply hY.eq_of_tgt
#align category_theory.limits.is_zero.of_iso CategoryTheory.Limits.IsZero.of_iso
-/

#print CategoryTheory.Limits.IsZero.op /-
theorem op (h : IsZero X) : IsZero (Opposite.op X) :=
  ⟨fun Y => ⟨⟨⟨(h.from (Opposite.unop Y)).op⟩, fun f => Quiver.Hom.unop_inj (h.eq_of_tgt _ _)⟩⟩,
    fun Y => ⟨⟨⟨(h.to (Opposite.unop Y)).op⟩, fun f => Quiver.Hom.unop_inj (h.eq_of_src _ _)⟩⟩⟩
#align category_theory.limits.is_zero.op CategoryTheory.Limits.IsZero.op
-/

#print CategoryTheory.Limits.IsZero.unop /-
theorem unop {X : Cᵒᵖ} (h : IsZero X) : IsZero (Opposite.unop X) :=
  ⟨fun Y => ⟨⟨⟨(h.from (Opposite.op Y)).unop⟩, fun f => Quiver.Hom.op_inj (h.eq_of_tgt _ _)⟩⟩,
    fun Y => ⟨⟨⟨(h.to (Opposite.op Y)).unop⟩, fun f => Quiver.Hom.op_inj (h.eq_of_src _ _)⟩⟩⟩
#align category_theory.limits.is_zero.unop CategoryTheory.Limits.IsZero.unop
-/

end IsZero

end Limits

open CategoryTheory.Limits

#print CategoryTheory.Iso.isZero_iff /-
theorem Iso.isZero_iff {X Y : C} (e : X ≅ Y) : IsZero X ↔ IsZero Y :=
  ⟨fun h => h.of_iso e.symm, fun h => h.of_iso e⟩
#align category_theory.iso.is_zero_iff CategoryTheory.Iso.isZero_iff
-/

/- warning: category_theory.functor.is_zero -> CategoryTheory.Functor.isZero 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] (F : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2), (forall (X : C), CategoryTheory.Limits.IsZero.{u3, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F X)) -> (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)
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] (F : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2), (forall (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 F) X)) -> (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)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.is_zero CategoryTheory.Functor.isZeroₓ'. -/
theorem Functor.isZero (F : C ⥤ D) (hF : ∀ X, IsZero (F.obj X)) : IsZero F :=
  by
  constructor <;> intro G <;> refine' ⟨⟨⟨_⟩, _⟩⟩
  · refine'
      { app := fun X => (hF _).to _
        naturality' := _ }
    intros
    exact (hF _).eq_of_src _ _
  · intro f
    ext
    apply (hF _).eq_of_src _ _
  · refine'
      { app := fun X => (hF _).from _
        naturality' := _ }
    intros
    exact (hF _).eq_of_tgt _ _
  · intro f
    ext
    apply (hF _).eq_of_tgt _ _
#align category_theory.functor.is_zero CategoryTheory.Functor.isZero

namespace Limits

variable (C)

#print CategoryTheory.Limits.HasZeroObject /-
/-- A category "has a zero object" if it has an object which is both initial and terminal. -/
class HasZeroObject : Prop where
  zero : ∃ X : C, IsZero X
#align category_theory.limits.has_zero_object CategoryTheory.Limits.HasZeroObject
-/

#print CategoryTheory.Limits.hasZeroObject_pUnit /-
instance hasZeroObject_pUnit : HasZeroObject (Discrete PUnit) where zero := ⟨⟨⟨⟩⟩, by tidy, by tidy⟩
#align category_theory.limits.has_zero_object_punit CategoryTheory.Limits.hasZeroObject_pUnit
-/

section

variable [HasZeroObject C]

#print CategoryTheory.Limits.HasZeroObject.zero' /-
/-- Construct a `has_zero C` for a category with a zero object.
This can not be a global instance as it will trigger for every `has_zero C` typeclass search.
-/
protected def HasZeroObject.zero' : Zero C where zero := HasZeroObject.zero.some
#align category_theory.limits.has_zero_object.has_zero CategoryTheory.Limits.HasZeroObject.zero'
-/

scoped[ZeroObject] attribute [instance] CategoryTheory.Limits.HasZeroObject.zero'

#print CategoryTheory.Limits.isZero_zero /-
theorem isZero_zero : IsZero (0 : C) :=
  HasZeroObject.zero.choose_spec
#align category_theory.limits.is_zero_zero CategoryTheory.Limits.isZero_zero
-/

#print CategoryTheory.Limits.hasZeroObject_op /-
instance hasZeroObject_op : HasZeroObject Cᵒᵖ :=
  ⟨⟨Opposite.op 0, IsZero.op (isZero_zero C)⟩⟩
#align category_theory.limits.has_zero_object_op CategoryTheory.Limits.hasZeroObject_op
-/

end

open ZeroObject

#print CategoryTheory.Limits.hasZeroObject_unop /-
theorem hasZeroObject_unop [HasZeroObject Cᵒᵖ] : HasZeroObject C :=
  ⟨⟨Opposite.unop 0, IsZero.unop (isZero_zero Cᵒᵖ)⟩⟩
#align category_theory.limits.has_zero_object_unop CategoryTheory.Limits.hasZeroObject_unop
-/

variable {C}

#print CategoryTheory.Limits.IsZero.hasZeroObject /-
theorem IsZero.hasZeroObject {X : C} (hX : IsZero X) : HasZeroObject C :=
  ⟨⟨X, hX⟩⟩
#align category_theory.limits.is_zero.has_zero_object CategoryTheory.Limits.IsZero.hasZeroObject
-/

#print CategoryTheory.Limits.IsZero.isoZero /-
/-- Every zero object is isomorphic to *the* zero object. -/
def IsZero.isoZero [HasZeroObject C] {X : C} (hX : IsZero X) : X ≅ 0 :=
  hX.Iso (isZero_zero C)
#align category_theory.limits.is_zero.iso_zero CategoryTheory.Limits.IsZero.isoZero
-/

/- warning: category_theory.limits.is_zero.obj -> CategoryTheory.Limits.IsZero.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] {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), CategoryTheory.Limits.IsZero.{u3, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F 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] {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), 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 F) X))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_zero.obj CategoryTheory.Limits.IsZero.objₓ'. -/
theorem IsZero.obj [HasZeroObject D] {F : C ⥤ D} (hF : IsZero F) (X : C) : IsZero (F.obj X) :=
  by
  let G : C ⥤ D := (CategoryTheory.Functor.const C).obj 0
  have hG : is_zero G := functor.is_zero _ fun X => is_zero_zero _
  let e : F ≅ G := hF.iso hG
  exact (is_zero_zero _).of_iso (e.app X)
#align category_theory.limits.is_zero.obj CategoryTheory.Limits.IsZero.obj

namespace HasZeroObject

variable [HasZeroObject C]

#print CategoryTheory.Limits.HasZeroObject.uniqueTo /-
/-- There is a unique morphism from the zero object to any object `X`. -/
protected def uniqueTo (X : C) : Unique (0 ⟶ X) :=
  ((isZero_zero C).unique_to X).some
#align category_theory.limits.has_zero_object.unique_to CategoryTheory.Limits.HasZeroObject.uniqueTo
-/

#print CategoryTheory.Limits.HasZeroObject.uniqueFrom /-
/-- There is a unique morphism from any object `X` to the zero object. -/
protected def uniqueFrom (X : C) : Unique (X ⟶ 0) :=
  ((isZero_zero C).unique_from X).some
#align category_theory.limits.has_zero_object.unique_from CategoryTheory.Limits.HasZeroObject.uniqueFrom
-/

scoped[ZeroObject] attribute [instance] CategoryTheory.Limits.HasZeroObject.uniqueTo

scoped[ZeroObject] attribute [instance] CategoryTheory.Limits.HasZeroObject.uniqueFrom

#print CategoryTheory.Limits.HasZeroObject.to_zero_ext /-
@[ext]
theorem to_zero_ext {X : C} (f g : X ⟶ 0) : f = g :=
  (isZero_zero C).eq_of_tgt _ _
#align category_theory.limits.has_zero_object.to_zero_ext CategoryTheory.Limits.HasZeroObject.to_zero_ext
-/

#print CategoryTheory.Limits.HasZeroObject.from_zero_ext /-
@[ext]
theorem from_zero_ext {X : C} (f g : 0 ⟶ X) : f = g :=
  (isZero_zero C).eq_of_src _ _
#align category_theory.limits.has_zero_object.from_zero_ext CategoryTheory.Limits.HasZeroObject.from_zero_ext
-/

instance (X : C) : Subsingleton (X ≅ 0) := by tidy

instance {X : C} (f : 0 ⟶ X) : Mono f where right_cancellation Z g h w := by ext

instance {X : C} (f : X ⟶ 0) : Epi f where left_cancellation Z g h w := by ext

#print CategoryTheory.Limits.HasZeroObject.zero_to_zero_isIso /-
instance zero_to_zero_isIso (f : (0 : C) ⟶ 0) : IsIso f := by
  convert show is_iso (𝟙 (0 : C)) by infer_instance
#align category_theory.limits.has_zero_object.zero_to_zero_is_iso CategoryTheory.Limits.HasZeroObject.zero_to_zero_isIso
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsInitial /-
/-- A zero object is in particular initial. -/
def zeroIsInitial : IsInitial (0 : C) :=
  (isZero_zero C).IsInitial
#align category_theory.limits.has_zero_object.zero_is_initial CategoryTheory.Limits.HasZeroObject.zeroIsInitial
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsTerminal /-
/-- A zero object is in particular terminal. -/
def zeroIsTerminal : IsTerminal (0 : C) :=
  (isZero_zero C).IsTerminal
#align category_theory.limits.has_zero_object.zero_is_terminal CategoryTheory.Limits.HasZeroObject.zeroIsTerminal
-/

#print CategoryTheory.Limits.HasZeroObject.hasInitial /-
/-- A zero object is in particular initial. -/
instance (priority := 10) hasInitial : HasInitial C :=
  hasInitial_of_unique 0
#align category_theory.limits.has_zero_object.has_initial CategoryTheory.Limits.HasZeroObject.hasInitial
-/

#print CategoryTheory.Limits.HasZeroObject.hasTerminal /-
/-- A zero object is in particular terminal. -/
instance (priority := 10) hasTerminal : HasTerminal C :=
  hasTerminal_of_unique 0
#align category_theory.limits.has_zero_object.has_terminal CategoryTheory.Limits.HasZeroObject.hasTerminal
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoIsInitial /-
/-- The (unique) isomorphism between any initial object and the zero object. -/
def zeroIsoIsInitial {X : C} (t : IsInitial X) : 0 ≅ X :=
  zeroIsInitial.uniqueUpToIso t
#align category_theory.limits.has_zero_object.zero_iso_is_initial CategoryTheory.Limits.HasZeroObject.zeroIsoIsInitial
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoIsTerminal /-
/-- The (unique) isomorphism between any terminal object and the zero object. -/
def zeroIsoIsTerminal {X : C} (t : IsTerminal X) : 0 ≅ X :=
  zeroIsTerminal.uniqueUpToIso t
#align category_theory.limits.has_zero_object.zero_iso_is_terminal CategoryTheory.Limits.HasZeroObject.zeroIsoIsTerminal
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoInitial /-
/-- The (unique) isomorphism between the chosen initial object and the chosen zero object. -/
def zeroIsoInitial [HasInitial C] : 0 ≅ ⊥_ C :=
  zeroIsInitial.uniqueUpToIso initialIsInitial
#align category_theory.limits.has_zero_object.zero_iso_initial CategoryTheory.Limits.HasZeroObject.zeroIsoInitial
-/

#print CategoryTheory.Limits.HasZeroObject.zeroIsoTerminal /-
/-- The (unique) isomorphism between the chosen terminal object and the chosen zero object. -/
def zeroIsoTerminal [HasTerminal C] : 0 ≅ ⊤_ C :=
  zeroIsTerminal.uniqueUpToIso terminalIsTerminal
#align category_theory.limits.has_zero_object.zero_iso_terminal CategoryTheory.Limits.HasZeroObject.zeroIsoTerminal
-/

#print CategoryTheory.Limits.HasZeroObject.initialMonoClass /-
instance (priority := 100) initialMonoClass : InitialMonoClass C :=
  InitialMonoClass.of_isInitial zeroIsInitial fun X => CategoryTheory.mono _
#align category_theory.limits.has_zero_object.has_strict_initial CategoryTheory.Limits.HasZeroObject.initialMonoClass
-/

end HasZeroObject

end Limits

open CategoryTheory.Limits

open ZeroObject

/- warning: category_theory.functor.is_zero_iff -> CategoryTheory.Functor.isZero_iff 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] (F : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2), Iff (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), CategoryTheory.Limits.IsZero.{u3, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u3, u2, u4} C _inst_1 D _inst_2 F 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] (F : CategoryTheory.Functor.{u1, u3, u2, u4} C _inst_1 D _inst_2), Iff (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), 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 F) X))
Case conversion may be inaccurate. Consider using '#align category_theory.functor.is_zero_iff CategoryTheory.Functor.isZero_iffₓ'. -/
theorem Functor.isZero_iff [HasZeroObject D] (F : C ⥤ D) : IsZero F ↔ ∀ X, IsZero (F.obj X) :=
  ⟨fun hF X => hF.obj X, Functor.isZero _⟩
#align category_theory.functor.is_zero_iff CategoryTheory.Functor.isZero_iff

end CategoryTheory


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

! This file was ported from Lean 3 source module algebra.group_with_zero.units.lemmas
! leanprover-community/mathlib commit dc6c365e751e34d100e80fe6e314c3c3e0fd2988
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.GroupWithZero.Commute
import Mathlib.Algebra.Hom.Units
import Mathlib.GroupTheory.GroupAction.Units
import Mathlib.Algebra.GroupWithZero.Units.Basic

/-!
# Further lemmas about units in a `MonoidWithZero` or a `GroupWithZero`.

-/

variable {α M₀ G₀ M₀' G₀' F F' : Type _}

variable [MonoidWithZero M₀]

section GroupWithZero

variable [GroupWithZero G₀] {a b c : G₀}

@[simp]
theorem div_self (h : a ≠ 0) : a / a = 1 :=
  IsUnit.div_self h.isUnit
#align div_self div_self

theorem eq_mul_inv_iff_mul_eq₀ (hc : c ≠ 0) : a = b * c⁻¹ ↔ a * c = b :=
  IsUnit.eq_mul_inv_iff_mul_eq hc.isUnit
#align eq_mul_inv_iff_mul_eq₀ eq_mul_inv_iff_mul_eq₀

theorem eq_inv_mul_iff_mul_eq₀ (hb : b ≠ 0) : a = b⁻¹ * c ↔ b * a = c :=
  IsUnit.eq_inv_mul_iff_mul_eq  hb.isUnit
#align eq_inv_mul_iff_mul_eq₀ eq_inv_mul_iff_mul_eq₀

theorem inv_mul_eq_iff_eq_mul₀ (ha : a ≠ 0) : a⁻¹ * b = c ↔ b = a * c :=
  IsUnit.inv_mul_eq_iff_eq_mul ha.isUnit
#align inv_mul_eq_iff_eq_mul₀ inv_mul_eq_iff_eq_mul₀

theorem mul_inv_eq_iff_eq_mul₀ (hb : b ≠ 0) : a * b⁻¹ = c ↔ a = c * b :=
  IsUnit.mul_inv_eq_iff_eq_mul hb.isUnit
#align mul_inv_eq_iff_eq_mul₀ mul_inv_eq_iff_eq_mul₀

theorem mul_inv_eq_one₀ (hb : b ≠ 0) : a * b⁻¹ = 1 ↔ a = b :=
  IsUnit.mul_inv_eq_one hb.isUnit
#align mul_inv_eq_one₀ mul_inv_eq_one₀

theorem inv_mul_eq_one₀ (ha : a ≠ 0) : a⁻¹ * b = 1 ↔ a = b :=
  IsUnit.inv_mul_eq_one ha.isUnit
#align inv_mul_eq_one₀ inv_mul_eq_one₀

theorem mul_eq_one_iff_eq_inv₀ (hb : b ≠ 0) : a * b = 1 ↔ a = b⁻¹ :=
  IsUnit.mul_eq_one_iff_eq_inv hb.isUnit
#align mul_eq_one_iff_eq_inv₀ mul_eq_one_iff_eq_inv₀

theorem mul_eq_one_iff_inv_eq₀ (ha : a ≠ 0) : a * b = 1 ↔ a⁻¹ = b :=
  IsUnit.mul_eq_one_iff_inv_eq ha.isUnit
#align mul_eq_one_iff_inv_eq₀ mul_eq_one_iff_inv_eq₀

@[simp]
theorem div_mul_cancel (a : G₀) (h : b ≠ 0) : a / b * b = a :=
  IsUnit.div_mul_cancel h.isUnit _
#align div_mul_cancel div_mul_cancel

@[simp]
theorem mul_div_cancel (a : G₀) (h : b ≠ 0) : a * b / b = a :=
  IsUnit.mul_div_cancel h.isUnit _
#align mul_div_cancel mul_div_cancel

theorem mul_one_div_cancel (h : a ≠ 0) : a * (1 / a) = 1 :=
  IsUnit.mul_one_div_cancel h.isUnit
#align mul_one_div_cancel mul_one_div_cancel

theorem one_div_mul_cancel (h : a ≠ 0) : 1 / a * a = 1 :=
  IsUnit.one_div_mul_cancel h.isUnit
#align one_div_mul_cancel one_div_mul_cancel

theorem div_left_inj' (hc : c ≠ 0) : a / c = b / c ↔ a = b :=
  IsUnit.div_left_inj hc.isUnit
#align div_left_inj' div_left_inj'

@[field_simps]
theorem div_eq_iff (hb : b ≠ 0) : a / b = c ↔ a = c * b :=
  IsUnit.div_eq_iff hb.isUnit
#align div_eq_iff div_eq_iff

@[field_simps]
theorem eq_div_iff (hb : b ≠ 0) : c = a / b ↔ c * b = a :=
  IsUnit.eq_div_iff hb.isUnit
#align eq_div_iff eq_div_iff

theorem div_eq_iff_mul_eq (hb : b ≠ 0) : a / b = c ↔ c * b = a :=
  (IsUnit.div_eq_iff hb.isUnit).trans eq_comm
#align div_eq_iff_mul_eq div_eq_iff_mul_eq

theorem eq_div_iff_mul_eq (hc : c ≠ 0) : a = b / c ↔ a * c = b :=
  IsUnit.eq_div_iff hc.isUnit
#align eq_div_iff_mul_eq eq_div_iff_mul_eq

theorem div_eq_of_eq_mul (hb : b ≠ 0) : a = c * b → a / b = c :=
  IsUnit.div_eq_of_eq_mul hb.isUnit
#align div_eq_of_eq_mul div_eq_of_eq_mul

theorem eq_div_of_mul_eq (hc : c ≠ 0) : a * c = b → a = b / c :=
  IsUnit.eq_div_of_mul_eq hc.isUnit
#align eq_div_of_mul_eq eq_div_of_mul_eq

theorem div_eq_one_iff_eq (hb : b ≠ 0) : a / b = 1 ↔ a = b :=
  IsUnit.div_eq_one_iff_eq hb.isUnit
#align div_eq_one_iff_eq div_eq_one_iff_eq

theorem div_mul_left (hb : b ≠ 0) : b / (a * b) = 1 / a :=
  IsUnit.div_mul_left hb.isUnit
#align div_mul_left div_mul_left

theorem mul_div_mul_right (a b : G₀) (hc : c ≠ 0) : a * c / (b * c) = a / b :=
  IsUnit.mul_div_mul_right hc.isUnit _ _
#align mul_div_mul_right mul_div_mul_right

theorem mul_mul_div (a : G₀) (hb : b ≠ 0) : a = a * b * (1 / b) :=
  (IsUnit.mul_mul_div _ hb.isUnit).symm
#align mul_mul_div mul_mul_div

theorem div_div_div_cancel_right (a : G₀) (hc : c ≠ 0) : a / c / (b / c) = a / b := by
  rw [div_div_eq_mul_div, div_mul_cancel _ hc]
#align div_div_div_cancel_right div_div_div_cancel_right

theorem div_mul_div_cancel (a : G₀) (hc : c ≠ 0) : a / c * (c / b) = a / b := by
  rw [← mul_div_assoc, div_mul_cancel _ hc]
#align div_mul_div_cancel div_mul_div_cancel

theorem div_mul_cancel_of_imp {a b : G₀} (h : b = 0 → a = 0) : a / b * b = a :=
  Classical.by_cases (fun hb : b = 0 => by simp [*]) (div_mul_cancel a)
#align div_mul_cancel_of_imp div_mul_cancel_of_imp

theorem mul_div_cancel_of_imp {a b : G₀} (h : b = 0 → a = 0) : a * b / b = a :=
  Classical.by_cases (fun hb : b = 0 => by simp [*]) (mul_div_cancel a)
#align mul_div_cancel_of_imp mul_div_cancel_of_imp

@[simp]
theorem divp_mk0 (a : G₀) {b : G₀} (hb : b ≠ 0) : a /ₚ Units.mk0 b hb = a / b :=
  divp_eq_div _ _
#align divp_mk0 divp_mk0

end GroupWithZero

section CommGroupWithZero

-- comm
variable [CommGroupWithZero G₀] {a b c d : G₀}

theorem div_mul_right (b : G₀) (ha : a ≠ 0) : a / (a * b) = 1 / b :=
  IsUnit.div_mul_right ha.isUnit _
#align div_mul_right div_mul_right

theorem mul_div_cancel_left_of_imp {a b : G₀} (h : a = 0 → b = 0) : a * b / a = b := by
  rw [mul_comm, mul_div_cancel_of_imp h]
#align mul_div_cancel_left_of_imp mul_div_cancel_left_of_imp

theorem mul_div_cancel_left (b : G₀) (ha : a ≠ 0) : a * b / a = b :=
  IsUnit.mul_div_cancel_left ha.isUnit _
#align mul_div_cancel_left mul_div_cancel_left

theorem mul_div_cancel_of_imp' {a b : G₀} (h : b = 0 → a = 0) : b * (a / b) = a := by
  rw [mul_comm, div_mul_cancel_of_imp h]
#align mul_div_cancel_of_imp' mul_div_cancel_of_imp'

theorem mul_div_cancel' (a : G₀) (hb : b ≠ 0) : b * (a / b) = a :=
  IsUnit.mul_div_cancel' hb.isUnit _
#align mul_div_cancel' mul_div_cancel'

theorem mul_div_mul_left (a b : G₀) (hc : c ≠ 0) : c * a / (c * b) = a / b :=
  IsUnit.mul_div_mul_left hc.isUnit _ _
#align mul_div_mul_left mul_div_mul_left

theorem mul_eq_mul_of_div_eq_div (a : G₀) {b : G₀} (c : G₀) {d : G₀} (hb : b ≠ 0) (hd : d ≠ 0)
    (h : a / b = c / d) : a * d = c * b := by
  rw [← mul_one a, ← div_self hb, ← mul_comm_div, h, div_mul_eq_mul_div, div_mul_cancel _ hd]
#align mul_eq_mul_of_div_eq_div mul_eq_mul_of_div_eq_div

@[field_simps]
theorem div_eq_div_iff (hb : b ≠ 0) (hd : d ≠ 0) : a / b = c / d ↔ a * d = c * b :=
  IsUnit.div_eq_div_iff hb.isUnit hd.isUnit
#align div_eq_div_iff div_eq_div_iff

theorem div_div_cancel' (ha : a ≠ 0) : a / (a / b) = b :=
  IsUnit.div_div_cancel ha.isUnit
#align div_div_cancel' div_div_cancel'

theorem div_div_cancel_left' (ha : a ≠ 0) : a / b / a = b⁻¹ :=
  ha.isUnit.div_div_cancel_left
#align div_div_cancel_left' div_div_cancel_left'

theorem div_helper (b : G₀) (h : a ≠ 0) : 1 / (a * b) * a = 1 / b := by
  rw [div_mul_eq_mul_div, one_mul, div_mul_right _ h]
#align div_helper div_helper

end CommGroupWithZero

section MonoidWithZero

variable [GroupWithZero G₀] [Nontrivial M₀] [MonoidWithZero M₀'] [MonoidWithZeroHomClass F G₀ M₀]
  [MonoidWithZeroHomClass F' G₀ M₀'] (f : F) {a : G₀}

theorem map_ne_zero : f a ≠ 0 ↔ a ≠ 0 :=
  ⟨fun hfa ha => hfa <| ha.symm ▸ map_zero f, fun ha => ((IsUnit.mk0 a ha).map f).ne_zero⟩
#align map_ne_zero map_ne_zero

@[simp]
theorem map_eq_zero : f a = 0 ↔ a = 0 :=
  not_iff_not.1 (map_ne_zero f)
#align map_eq_zero map_eq_zero

theorem eq_on_inv₀ (f g : F') (h : f a = g a) : f a⁻¹ = g a⁻¹ := by
  rcases eq_or_ne a 0 with (rfl | ha)
  · rw [inv_zero, map_zero, map_zero]
  · exact (IsUnit.mk0 a ha).eq_on_inv f g h
#align eq_on_inv₀ eq_on_inv₀

end MonoidWithZero

section GroupWithZero

variable [GroupWithZero G₀] [GroupWithZero G₀'] [MonoidWithZeroHomClass F G₀ G₀'] (f : F) (a b : G₀)

/-- A monoid homomorphism between groups with zeros sending `0` to `0` sends `a⁻¹` to `(f a)⁻¹`. -/
@[simp]
theorem map_inv₀ : f a⁻¹ = (f a)⁻¹ := by
  by_cases h : a = 0
  · simp [h, map_zero f]
  · apply eq_inv_of_mul_eq_one_left
    rw [← map_mul, inv_mul_cancel h, map_one]
#align map_inv₀ map_inv₀

@[simp]
theorem map_div₀ : f (a / b) = f a / f b :=
  map_div' f (map_inv₀ f) a b
#align map_div₀ map_div₀

end GroupWithZero

/-- We define the inverse as a `MonoidWithZeroHom` by extending the inverse map by zero
on non-units. -/
noncomputable def MonoidWithZero.inverse {M : Type _} [CommMonoidWithZero M] :
    M →*₀ M where
  toFun := Ring.inverse
  map_zero' := Ring.inverse_zero _
  map_one' := Ring.inverse_one _
  map_mul' x y := (Ring.mul_inverse_rev x y).trans (mul_comm _ _)
#align monoid_with_zero.inverse MonoidWithZero.inverse

@[simp]
theorem MonoidWithZero.coe_inverse {M : Type _} [CommMonoidWithZero M] :
    (MonoidWithZero.inverse : M → M) = Ring.inverse :=
  rfl
#align monoid_with_zero.coe_inverse MonoidWithZero.coe_inverse

@[simp]
theorem MonoidWithZero.inverse_apply {M : Type _} [CommMonoidWithZero M] (a : M) :
    MonoidWithZero.inverse a = Ring.inverse a :=
  rfl
#align monoid_with_zero.inverse_apply MonoidWithZero.inverse_apply

/-- Inversion on a commutative group with zero, considered as a monoid with zero homomorphism. -/
def invMonoidWithZeroHom {G₀ : Type _} [CommGroupWithZero G₀] : G₀ →*₀ G₀ :=
  { invMonoidHom with map_zero' := inv_zero }
#align inv_monoid_with_zero_hom invMonoidWithZeroHom

namespace Units

variable [GroupWithZero G₀]

variable {a b : G₀}

@[simp]
theorem smul_mk0 {α : Type _} [SMul G₀ α] {g : G₀} (hg : g ≠ 0) (a : α) : mk0 g hg • a = g • a :=
  rfl
#align units.smul_mk0 Units.smul_mk0

end Units