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

! This file was ported from Lean 3 source module category_theory.limits.preserves.shapes.terminal
! 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
import Mathbin.CategoryTheory.Limits.Preserves.Basic

/-!
# Preserving terminal object

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

Constructions to relate the notions of preserving terminal objects and reflecting terminal objects
to concrete objects.

In particular, we show that `terminal_comparison G` is an isomorphism iff `G` preserves terminal
objects.
-/

universe w v v₁ v₂ u u₁ u₂

noncomputable section

open CategoryTheory CategoryTheory.Category CategoryTheory.Limits

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

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

variable (G : C ⥤ D)

namespace CategoryTheory.Limits

variable (X : C)

section Terminal

/- warning: category_theory.limits.is_limit_map_cone_empty_cone_equiv -> CategoryTheory.Limits.isLimitMapConeEmptyConeEquiv is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C), Equiv.{max 1 (succ u4) (succ u2), max 1 (succ u4) (succ u2)} (CategoryTheory.Limits.IsLimit.{0, u2, 0, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) D _inst_2 (CategoryTheory.Functor.comp.{0, u1, u2, 0, u3, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) C _inst_1 D _inst_2 (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G) (CategoryTheory.Functor.mapCone.{0, u1, u2, 0, u3, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) C _inst_1 D _inst_2 (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G (CategoryTheory.Limits.asEmptyCone.{u1, u3} C _inst_1 X))) (CategoryTheory.Limits.IsTerminal.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G X))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C), Equiv.{max (succ u4) (succ u2), max (succ u4) (succ u2)} (CategoryTheory.Limits.IsLimit.{0, u2, 0, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) D _inst_2 (CategoryTheory.Functor.comp.{0, u1, u2, 0, u3, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) C _inst_1 D _inst_2 (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G) (CategoryTheory.Functor.mapCone.{0, u1, u2, 0, u3, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) C _inst_1 D _inst_2 G (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) (CategoryTheory.Limits.asEmptyCone.{u1, u3} C _inst_1 X))) (CategoryTheory.Limits.IsTerminal.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) X))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_limit_map_cone_empty_cone_equiv CategoryTheory.Limits.isLimitMapConeEmptyConeEquivₓ'. -/
/-- The map of an empty cone is a limit iff the mapped object is terminal.
-/
def isLimitMapConeEmptyConeEquiv : IsLimit (G.mapCone (asEmptyCone X)) ≃ IsTerminal (G.obj X) :=
  isLimitEmptyConeEquiv D _ _ (eqToIso rfl)
#align category_theory.limits.is_limit_map_cone_empty_cone_equiv CategoryTheory.Limits.isLimitMapConeEmptyConeEquiv

/- warning: category_theory.limits.is_terminal.is_terminal_obj -> CategoryTheory.Limits.IsTerminal.isTerminalObj is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C) [_inst_3 : CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], (CategoryTheory.Limits.IsTerminal.{u1, u3} C _inst_1 X) -> (CategoryTheory.Limits.IsTerminal.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G X))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C) [_inst_3 : CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], (CategoryTheory.Limits.IsTerminal.{u1, u3} C _inst_1 X) -> (CategoryTheory.Limits.IsTerminal.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) X))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_terminal.is_terminal_obj CategoryTheory.Limits.IsTerminal.isTerminalObjₓ'. -/
/-- The property of preserving terminal objects expressed in terms of `is_terminal`. -/
def IsTerminal.isTerminalObj [PreservesLimit (Functor.empty.{0} C) G] (l : IsTerminal X) :
    IsTerminal (G.obj X) :=
  isLimitMapConeEmptyConeEquiv G X (PreservesLimit.preserves l)
#align category_theory.limits.is_terminal.is_terminal_obj CategoryTheory.Limits.IsTerminal.isTerminalObj

/- warning: category_theory.limits.is_terminal.is_terminal_of_obj -> CategoryTheory.Limits.IsTerminal.isTerminalOfObj is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C) [_inst_3 : CategoryTheory.Limits.ReflectsLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], (CategoryTheory.Limits.IsTerminal.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G X)) -> (CategoryTheory.Limits.IsTerminal.{u1, u3} C _inst_1 X)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C) [_inst_3 : CategoryTheory.Limits.ReflectsLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], (CategoryTheory.Limits.IsTerminal.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) X)) -> (CategoryTheory.Limits.IsTerminal.{u1, u3} C _inst_1 X)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_terminal.is_terminal_of_obj CategoryTheory.Limits.IsTerminal.isTerminalOfObjₓ'. -/
/-- The property of reflecting terminal objects expressed in terms of `is_terminal`. -/
def IsTerminal.isTerminalOfObj [ReflectsLimit (Functor.empty.{0} C) G] (l : IsTerminal (G.obj X)) :
    IsTerminal X :=
  ReflectsLimit.reflects ((isLimitMapConeEmptyConeEquiv G X).symm l)
#align category_theory.limits.is_terminal.is_terminal_of_obj CategoryTheory.Limits.IsTerminal.isTerminalOfObj

#print CategoryTheory.Limits.preservesLimitsOfShapePemptyOfPreservesTerminal /-
/-- Preserving the terminal object implies preserving all limits of the empty diagram. -/
def preservesLimitsOfShapePemptyOfPreservesTerminal [PreservesLimit (Functor.empty.{0} C) G] :
    PreservesLimitsOfShape (Discrete PEmpty) G
    where PreservesLimit K :=
    preservesLimitOfIsoDiagram G (Functor.emptyExt (Functor.empty.{0} C) _)
#align category_theory.limits.preserves_limits_of_shape_pempty_of_preserves_terminal CategoryTheory.Limits.preservesLimitsOfShapePemptyOfPreservesTerminal
-/

variable [HasTerminal C]

/- warning: category_theory.limits.is_limit_of_has_terminal_of_preserves_limit -> CategoryTheory.Limits.isLimitOfHasTerminalOfPreservesLimit is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], CategoryTheory.Limits.IsTerminal.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], CategoryTheory.Limits.IsTerminal.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_limit_of_has_terminal_of_preserves_limit CategoryTheory.Limits.isLimitOfHasTerminalOfPreservesLimitₓ'. -/
/--
If `G` preserves the terminal object and `C` has a terminal object, then the image of the terminal
object is terminal.
-/
def isLimitOfHasTerminalOfPreservesLimit [PreservesLimit (Functor.empty.{0} C) G] :
    IsTerminal (G.obj (⊤_ C)) :=
  terminalIsTerminal.isTerminalObj G (⊤_ C)
#align category_theory.limits.is_limit_of_has_terminal_of_preserves_limit CategoryTheory.Limits.isLimitOfHasTerminalOfPreservesLimit

#print CategoryTheory.Limits.hasTerminal_of_hasTerminal_of_preservesLimit /-
/-- If `C` has a terminal object and `G` preserves terminal objects, then `D` has a terminal object
also.
Note this property is somewhat unique to (co)limits of the empty diagram: for general `J`, if `C`
has limits of shape `J` and `G` preserves them, then `D` does not necessarily have limits of shape
`J`.
-/
theorem hasTerminal_of_hasTerminal_of_preservesLimit [PreservesLimit (Functor.empty.{0} C) G] :
    HasTerminal D :=
  ⟨fun F => by
    haveI := has_limit.mk ⟨_, is_limit_of_has_terminal_of_preserves_limit G⟩
    apply has_limit_of_iso F.unique_from_empty.symm⟩
#align category_theory.limits.has_terminal_of_has_terminal_of_preserves_limit CategoryTheory.Limits.hasTerminal_of_hasTerminal_of_preservesLimit
-/

variable [HasTerminal D]

/- warning: category_theory.limits.preserves_terminal.of_iso_comparison -> CategoryTheory.Limits.PreservesTerminal.ofIsoComparison is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2] [i : CategoryTheory.IsIso.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Limits.terminalComparison.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4)], CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2] [i : CategoryTheory.IsIso.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Limits.terminalComparison.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4)], CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_terminal.of_iso_comparison CategoryTheory.Limits.PreservesTerminal.ofIsoComparisonₓ'. -/
/-- If the terminal comparison map for `G` is an isomorphism, then `G` preserves terminal objects.
-/
def PreservesTerminal.ofIsoComparison [i : IsIso (terminalComparison G)] :
    PreservesLimit (Functor.empty C) G :=
  by
  apply preserves_limit_of_preserves_limit_cone terminal_is_terminal
  apply (is_limit_map_cone_empty_cone_equiv _ _).symm _
  apply is_limit.of_point_iso (limit.is_limit (Functor.empty.{0} D))
  apply i
#align category_theory.limits.preserves_terminal.of_iso_comparison CategoryTheory.Limits.PreservesTerminal.ofIsoComparison

/- warning: category_theory.limits.preserves_terminal_of_is_iso -> CategoryTheory.Limits.preservesTerminalOfIsIso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2] (f : Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4)) [i : CategoryTheory.IsIso.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4) f], CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2] (f : Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4)) [i : CategoryTheory.IsIso.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4) f], CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_terminal_of_is_iso CategoryTheory.Limits.preservesTerminalOfIsIsoₓ'. -/
/-- If there is any isomorphism `G.obj ⊤ ⟶ ⊤`, then `G` preserves terminal objects. -/
def preservesTerminalOfIsIso (f : G.obj (⊤_ C) ⟶ ⊤_ D) [i : IsIso f] :
    PreservesLimit (Functor.empty C) G :=
  by
  rw [Subsingleton.elim f (terminal_comparison G)] at i
  exact preserves_terminal.of_iso_comparison G
#align category_theory.limits.preserves_terminal_of_is_iso CategoryTheory.Limits.preservesTerminalOfIsIso

/- warning: category_theory.limits.preserves_terminal_of_iso -> CategoryTheory.Limits.preservesTerminalOfIso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2], (CategoryTheory.Iso.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4)) -> (CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2], (CategoryTheory.Iso.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4)) -> (CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_terminal_of_iso CategoryTheory.Limits.preservesTerminalOfIsoₓ'. -/
/-- If there is any isomorphism `G.obj ⊤ ≅ ⊤`, then `G` preserves terminal objects. -/
def preservesTerminalOfIso (f : G.obj (⊤_ C) ≅ ⊤_ D) : PreservesLimit (Functor.empty C) G :=
  preservesTerminalOfIsIso G f.Hom
#align category_theory.limits.preserves_terminal_of_iso CategoryTheory.Limits.preservesTerminalOfIso

variable [PreservesLimit (Functor.empty.{0} C) G]

/- warning: category_theory.limits.preserves_terminal.iso -> CategoryTheory.Limits.PreservesTerminal.iso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2] [_inst_5 : CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], CategoryTheory.Iso.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2] [_inst_5 : CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], CategoryTheory.Iso.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_terminal.iso CategoryTheory.Limits.PreservesTerminal.isoₓ'. -/
/-- If `G` preserves terminal objects, then the terminal comparison map for `G` is an isomorphism.
-/
def PreservesTerminal.iso : G.obj (⊤_ C) ≅ ⊤_ D :=
  (isLimitOfHasTerminalOfPreservesLimit G).conePointUniqueUpToIso (limit.isLimit _)
#align category_theory.limits.preserves_terminal.iso CategoryTheory.Limits.PreservesTerminal.iso

/- warning: category_theory.limits.preserves_terminal.iso_hom -> CategoryTheory.Limits.PreservesTerminal.iso_hom is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2] [_inst_5 : CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], Eq.{succ u2} (Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4)) (CategoryTheory.Iso.hom.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Limits.PreservesTerminal.iso.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4 _inst_5)) (CategoryTheory.Limits.terminalComparison.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasTerminal.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasTerminal.{u2, u4} D _inst_2] [_inst_5 : CategoryTheory.Limits.PreservesLimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], Eq.{succ u2} (Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4)) (CategoryTheory.Iso.hom.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.terminal.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.terminal.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Limits.PreservesTerminal.iso.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4 _inst_5)) (CategoryTheory.Limits.terminalComparison.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_terminal.iso_hom CategoryTheory.Limits.PreservesTerminal.iso_homₓ'. -/
@[simp]
theorem PreservesTerminal.iso_hom : (PreservesTerminal.iso G).Hom = terminalComparison G :=
  rfl
#align category_theory.limits.preserves_terminal.iso_hom CategoryTheory.Limits.PreservesTerminal.iso_hom

instance : IsIso (terminalComparison G) :=
  by
  rw [← preserves_terminal.iso_hom]
  infer_instance

end Terminal

section Initial

/- warning: category_theory.limits.is_colimit_map_cocone_empty_cocone_equiv -> CategoryTheory.Limits.isColimitMapCoconeEmptyCoconeEquiv is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C), Equiv.{max 1 (succ u4) (succ u2), max 1 (succ u4) (succ u2)} (CategoryTheory.Limits.IsColimit.{0, u2, 0, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) D _inst_2 (CategoryTheory.Functor.comp.{0, u1, u2, 0, u3, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) C _inst_1 D _inst_2 (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G) (CategoryTheory.Functor.mapCocone.{0, u1, u2, 0, u3, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) C _inst_1 D _inst_2 (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G (CategoryTheory.Limits.asEmptyCocone.{u1, u3} C _inst_1 X))) (CategoryTheory.Limits.IsInitial.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G X))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C), Equiv.{max (succ u4) (succ u2), max (succ u4) (succ u2)} (CategoryTheory.Limits.IsColimit.{0, u2, 0, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) D _inst_2 (CategoryTheory.Functor.comp.{0, u1, u2, 0, u3, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) C _inst_1 D _inst_2 (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G) (CategoryTheory.Functor.mapCocone.{0, u1, u2, 0, u3, u4} (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) C _inst_1 D _inst_2 G (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) (CategoryTheory.Limits.asEmptyCocone.{u1, u3} C _inst_1 X))) (CategoryTheory.Limits.IsInitial.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) X))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_colimit_map_cocone_empty_cocone_equiv CategoryTheory.Limits.isColimitMapCoconeEmptyCoconeEquivₓ'. -/
/-- The map of an empty cocone is a colimit iff the mapped object is initial.
-/
def isColimitMapCoconeEmptyCoconeEquiv :
    IsColimit (G.mapCocone (asEmptyCocone.{v₁} X)) ≃ IsInitial (G.obj X) :=
  isColimitEmptyCoconeEquiv D _ _ (eqToIso rfl)
#align category_theory.limits.is_colimit_map_cocone_empty_cocone_equiv CategoryTheory.Limits.isColimitMapCoconeEmptyCoconeEquiv

/- warning: category_theory.limits.is_initial.is_initial_obj -> CategoryTheory.Limits.IsInitial.isInitialObj is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C) [_inst_3 : CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], (CategoryTheory.Limits.IsInitial.{u1, u3} C _inst_1 X) -> (CategoryTheory.Limits.IsInitial.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G X))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C) [_inst_3 : CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], (CategoryTheory.Limits.IsInitial.{u1, u3} C _inst_1 X) -> (CategoryTheory.Limits.IsInitial.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) X))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_initial.is_initial_obj CategoryTheory.Limits.IsInitial.isInitialObjₓ'. -/
/-- The property of preserving initial objects expressed in terms of `is_initial`. -/
def IsInitial.isInitialObj [PreservesColimit (Functor.empty.{0} C) G] (l : IsInitial X) :
    IsInitial (G.obj X) :=
  isColimitMapCoconeEmptyCoconeEquiv G X (PreservesColimit.preserves l)
#align category_theory.limits.is_initial.is_initial_obj CategoryTheory.Limits.IsInitial.isInitialObj

/- warning: category_theory.limits.is_initial.is_initial_of_obj -> CategoryTheory.Limits.IsInitial.isInitialOfObj is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C) [_inst_3 : CategoryTheory.Limits.ReflectsColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], (CategoryTheory.Limits.IsInitial.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G X)) -> (CategoryTheory.Limits.IsInitial.{u1, u3} C _inst_1 X)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (X : C) [_inst_3 : CategoryTheory.Limits.ReflectsColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], (CategoryTheory.Limits.IsInitial.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) X)) -> (CategoryTheory.Limits.IsInitial.{u1, u3} C _inst_1 X)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_initial.is_initial_of_obj CategoryTheory.Limits.IsInitial.isInitialOfObjₓ'. -/
/-- The property of reflecting initial objects expressed in terms of `is_initial`. -/
def IsInitial.isInitialOfObj [ReflectsColimit (Functor.empty.{0} C) G] (l : IsInitial (G.obj X)) :
    IsInitial X :=
  ReflectsColimit.reflects ((isColimitMapCoconeEmptyCoconeEquiv G X).symm l)
#align category_theory.limits.is_initial.is_initial_of_obj CategoryTheory.Limits.IsInitial.isInitialOfObj

#print CategoryTheory.Limits.preservesColimitsOfShapePemptyOfPreservesInitial /-
/-- Preserving the initial object implies preserving all colimits of the empty diagram. -/
def preservesColimitsOfShapePemptyOfPreservesInitial [PreservesColimit (Functor.empty.{0} C) G] :
    PreservesColimitsOfShape (Discrete PEmpty) G
    where PreservesColimit K :=
    preservesColimitOfIsoDiagram G (Functor.emptyExt (Functor.empty.{0} C) _)
#align category_theory.limits.preserves_colimits_of_shape_pempty_of_preserves_initial CategoryTheory.Limits.preservesColimitsOfShapePemptyOfPreservesInitial
-/

variable [HasInitial C]

/- warning: category_theory.limits.is_colimit_of_has_initial_of_preserves_colimit -> CategoryTheory.Limits.isColimitOfHasInitialOfPreservesColimit is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], CategoryTheory.Limits.IsInitial.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], CategoryTheory.Limits.IsInitial.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3))
Case conversion may be inaccurate. Consider using '#align category_theory.limits.is_colimit_of_has_initial_of_preserves_colimit CategoryTheory.Limits.isColimitOfHasInitialOfPreservesColimitₓ'. -/
/-- If `G` preserves the initial object and `C` has a initial object, then the image of the initial
object is initial.
-/
def isColimitOfHasInitialOfPreservesColimit [PreservesColimit (Functor.empty.{0} C) G] :
    IsInitial (G.obj (⊥_ C)) :=
  initialIsInitial.isInitialObj G (⊥_ C)
#align category_theory.limits.is_colimit_of_has_initial_of_preserves_colimit CategoryTheory.Limits.isColimitOfHasInitialOfPreservesColimit

#print CategoryTheory.Limits.hasInitial_of_hasInitial_of_preservesColimit /-
/-- If `C` has a initial object and `G` preserves initial objects, then `D` has a initial object
also.
Note this property is somewhat unique to colimits of the empty diagram: for general `J`, if `C`
has colimits of shape `J` and `G` preserves them, then `D` does not necessarily have colimits of
shape `J`.
-/
theorem hasInitial_of_hasInitial_of_preservesColimit [PreservesColimit (Functor.empty.{0} C) G] :
    HasInitial D :=
  ⟨fun F => by
    haveI := has_colimit.mk ⟨_, is_colimit_of_has_initial_of_preserves_colimit G⟩
    apply has_colimit_of_iso F.unique_from_empty⟩
#align category_theory.limits.has_initial_of_has_initial_of_preserves_colimit CategoryTheory.Limits.hasInitial_of_hasInitial_of_preservesColimit
-/

variable [HasInitial D]

/- warning: category_theory.limits.preserves_initial.of_iso_comparison -> CategoryTheory.Limits.PreservesInitial.ofIsoComparison is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2] [i : CategoryTheory.IsIso.{u2, u4} D _inst_2 (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.initialComparison.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4)], CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2] [i : CategoryTheory.IsIso.{u2, u4} D _inst_2 (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.initialComparison.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4)], CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_initial.of_iso_comparison CategoryTheory.Limits.PreservesInitial.ofIsoComparisonₓ'. -/
/-- If the initial comparison map for `G` is an isomorphism, then `G` preserves initial objects.
-/
def PreservesInitial.ofIsoComparison [i : IsIso (initialComparison G)] :
    PreservesColimit (Functor.empty C) G :=
  by
  apply preserves_colimit_of_preserves_colimit_cocone initial_is_initial
  apply (is_colimit_map_cocone_empty_cocone_equiv _ _).symm _
  apply is_colimit.of_point_iso (colimit.is_colimit (Functor.empty.{0} D))
  apply i
#align category_theory.limits.preserves_initial.of_iso_comparison CategoryTheory.Limits.PreservesInitial.ofIsoComparison

/- warning: category_theory.limits.preserves_initial_of_is_iso -> CategoryTheory.Limits.preservesInitialOfIsIso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2] (f : Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3))) [i : CategoryTheory.IsIso.{u2, u4} D _inst_2 (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3)) f], CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2] (f : Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3))) [i : CategoryTheory.IsIso.{u2, u4} D _inst_2 (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3)) f], CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_initial_of_is_iso CategoryTheory.Limits.preservesInitialOfIsIsoₓ'. -/
/-- If there is any isomorphism `⊥ ⟶ G.obj ⊥`, then `G` preserves initial objects. -/
def preservesInitialOfIsIso (f : ⊥_ D ⟶ G.obj (⊥_ C)) [i : IsIso f] :
    PreservesColimit (Functor.empty C) G :=
  by
  rw [Subsingleton.elim f (initial_comparison G)] at i
  exact preserves_initial.of_iso_comparison G
#align category_theory.limits.preserves_initial_of_is_iso CategoryTheory.Limits.preservesInitialOfIsIso

/- warning: category_theory.limits.preserves_initial_of_iso -> CategoryTheory.Limits.preservesInitialOfIso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2], (CategoryTheory.Iso.{u2, u4} D _inst_2 (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3))) -> (CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2], (CategoryTheory.Iso.{u2, u4} D _inst_2 (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3))) -> (CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_initial_of_iso CategoryTheory.Limits.preservesInitialOfIsoₓ'. -/
/-- If there is any isomorphism `⊥ ≅ G.obj ⊥ `, then `G` preserves initial objects. -/
def preservesInitialOfIso (f : ⊥_ D ≅ G.obj (⊥_ C)) : PreservesColimit (Functor.empty C) G :=
  preservesInitialOfIsIso G f.Hom
#align category_theory.limits.preserves_initial_of_iso CategoryTheory.Limits.preservesInitialOfIso

variable [PreservesColimit (Functor.empty.{0} C) G]

/- warning: category_theory.limits.preserves_initial.iso -> CategoryTheory.Limits.PreservesInitial.iso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2] [_inst_5 : CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], CategoryTheory.Iso.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2] [_inst_5 : CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], CategoryTheory.Iso.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_initial.iso CategoryTheory.Limits.PreservesInitial.isoₓ'. -/
/-- If `G` preserves initial objects, then the initial comparison map for `G` is an isomorphism. -/
def PreservesInitial.iso : G.obj (⊥_ C) ≅ ⊥_ D :=
  (isColimitOfHasInitialOfPreservesColimit G).coconePointUniqueUpToIso (colimit.isColimit _)
#align category_theory.limits.preserves_initial.iso CategoryTheory.Limits.PreservesInitial.iso

/- warning: category_theory.limits.preserves_initial.iso_hom -> CategoryTheory.Limits.PreservesInitial.iso_hom is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2] [_inst_5 : CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], Eq.{succ u2} (Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3))) (CategoryTheory.Iso.inv.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 G (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Limits.PreservesInitial.iso.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4 _inst_5)) (CategoryTheory.Limits.initialComparison.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (G : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_3 : CategoryTheory.Limits.HasInitial.{u1, u3} C _inst_1] [_inst_4 : CategoryTheory.Limits.HasInitial.{u2, u4} D _inst_2] [_inst_5 : CategoryTheory.Limits.PreservesColimit.{0, 0, u1, u2, u3, u4} C _inst_1 D _inst_2 (CategoryTheory.Discrete.{0} PEmpty.{1}) (CategoryTheory.discreteCategory.{0} PEmpty.{1}) (CategoryTheory.Functor.empty.{0, u1, u3} C _inst_1) G], Eq.{succ u2} (Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3))) (CategoryTheory.Iso.inv.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 G) (CategoryTheory.Limits.initial.{u1, u3} C _inst_1 _inst_3)) (CategoryTheory.Limits.initial.{u2, u4} D _inst_2 _inst_4) (CategoryTheory.Limits.PreservesInitial.iso.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4 _inst_5)) (CategoryTheory.Limits.initialComparison.{u1, u2, u3, u4} C _inst_1 D _inst_2 G _inst_3 _inst_4)
Case conversion may be inaccurate. Consider using '#align category_theory.limits.preserves_initial.iso_hom CategoryTheory.Limits.PreservesInitial.iso_homₓ'. -/
@[simp]
theorem PreservesInitial.iso_hom : (PreservesInitial.iso G).inv = initialComparison G :=
  rfl
#align category_theory.limits.preserves_initial.iso_hom CategoryTheory.Limits.PreservesInitial.iso_hom

instance : IsIso (initialComparison G) :=
  by
  rw [← preserves_initial.iso_hom]
  infer_instance

end Initial

end CategoryTheory.Limits


import data.finsupp

lemma finsupp.on_finset_mem_support {α β : Type*} [decidable_eq α] [decidable_eq β] [has_zero β]
  (s : finset α) (f : α → β) (hf : ∀ (a : α), f a ≠ 0 → a ∈ s) :
  ∀ a : α, a ∈ (finsupp.on_finset s f hf).support ↔ f a ≠ 0 :=
by { intro, rw [finsupp.mem_support_iff, finsupp.on_finset_apply] }

lemma finsupp.on_finset_support {α β : Type*} [decidable_eq α] [decidable_eq β] [has_zero β]
  (s : finset α) (f : α → β) (hf : ∀ (a : α), f a ≠ 0 → a ∈ s) :
  (finsupp.on_finset s f hf).support = s.filter (λ a, f a ≠ 0) :=
begin
  ext a,
  rw [finsupp.on_finset_mem_support, finset.mem_filter],
  specialize hf a,
  finish
end


/-
Copyright (c) 2022 Andrew Yang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Andrew Yang
-/
import ring_theory.ideal.local_ring
import ring_theory.noetherian
import ring_theory.rees_algebra
import ring_theory.finiteness
import data.polynomial.module
import order.hom.lattice

/-!

# `I`-filtrations of modules

This file contains the definitions and basic results around (stable) `I`-filtrations of modules.

## Main results

- `ideal.filtration`: An `I`-filtration on the module `M` is a sequence of decreasing submodules
  `N i` such that `I • N ≤ I (i + 1)`. Note that we do not require the filtration to start from `⊤`.
- `ideal.filtration.stable`: An `I`-filtration is stable if `I • (N i) = N (i + 1)` for large
  enough `i`.
- `ideal.filtration.submodule`: The associated module `⨁ Nᵢ` of a filtration, implemented as a
  submodule of `M[X]`.
- `ideal.filtration.submodule_fg_iff_stable`: If `F.N i` are all finitely generated, then
  `F.stable` iff `F.submodule.fg`.
- `ideal.filtration.stable.of_le`: In a finite module over a noetherian ring,
  if `F' ≤ F`, then `F.stable → F'.stable`.
- `ideal.exists_pow_inf_eq_pow_smul`: **Artin-Rees lemma**.
  given `N ≤ M`, there exists a `k` such that `IⁿM ⊓ N = Iⁿ⁻ᵏ(IᵏM ⊓ N)` for all `n ≥ k`.
- `ideal.infi_pow_eq_bot_of_local_ring`:
  **Krull's intersection theorem** (`⨅ i, I ^ i = ⊥`) for noetherian local rings.
- `ideal.infi_pow_eq_bot_of_is_domain`:
  **Krull's intersection theorem** (`⨅ i, I ^ i = ⊥`) for noetherian domains.

-/

universes u v

variables {R M : Type u} [comm_ring R] [add_comm_group M] [module R M] (I : ideal R)

open polynomial
open_locale polynomial big_operators

/-- An `I`-filtration on the module `M` is a sequence of decreasing submodules `N i` such that
`I • (N i) ≤ N (i + 1)`. Note that we do not require the filtration to start from `⊤`. -/
@[ext]
structure ideal.filtration (M : Type u) [add_comm_group M] [module R M] :=
(N : ℕ → submodule R M)
(mono : ∀ i, N (i + 1) ≤ N i)
(smul_le : ∀ i, I • N i ≤ N (i + 1))

variables (F F' : I.filtration M) {I}

namespace ideal.filtration

lemma pow_smul_le (i j : ℕ) : I ^ i • F.N j ≤ F.N (i + j) :=
begin
  induction i,
  { simp },
  { rw [pow_succ, mul_smul, nat.succ_eq_add_one, add_assoc, add_comm 1, ← add_assoc],
    exact (submodule.smul_mono_right i_ih).trans (F.smul_le _) }
end

lemma pow_smul_le_pow_smul (i j k : ℕ) : I ^ (i + k) • F.N j ≤ I ^ k • F.N (i + j) :=
by { rw [add_comm, pow_add, mul_smul], exact submodule.smul_mono_right (F.pow_smul_le i j) }

protected

/-- The trivial `I`-filtration of `N`. -/
@[simps]
def _root_.ideal.trivial_filtration (I : ideal R) (N : submodule R M) : I.filtration M :=
{ N := λ i, N,
  mono := λ i, le_of_eq rfl,
  smul_le := λ i, submodule.smul_le_right }

/-- The `sup` of two `I.filtration`s is an `I.filtration`. -/
instance : has_sup (I.filtration M) :=
⟨λ F F', ⟨F.N ⊔ F'.N, λ i, sup_le_sup (F.mono i) (F'.mono i),
    λ i, (le_of_eq (submodule.smul_sup _ _ _)).trans $ sup_le_sup (F.smul_le i) (F'.smul_le i)⟩⟩

/-- The `Sup` of a family of `I.filtration`s is an `I.filtration`. -/
instance : has_Sup (I.filtration M) := ⟨λ S,
{ N := Sup (ideal.filtration.N '' S),
  mono := λ i, begin
    apply Sup_le_Sup_of_forall_exists_le _,
    rintros _ ⟨⟨_, F, hF, rfl⟩, rfl⟩,
    exact ⟨_, ⟨⟨_, F, hF, rfl⟩, rfl⟩, F.mono i⟩,
  end,
  smul_le := λ i, begin
    rw [Sup_eq_supr', supr_apply, submodule.smul_supr, supr_apply],
    apply supr_mono _,
    rintro ⟨_, F, hF, rfl⟩,
    exact F.smul_le i,
  end }⟩

/-- The `inf` of two `I.filtration`s is an `I.filtration`. -/
instance : has_inf (I.filtration M) :=
⟨λ F F', ⟨F.N ⊓ F'.N, λ i, inf_le_inf (F.mono i) (F'.mono i),
    λ i, (submodule.smul_inf_le _ _ _).trans $ inf_le_inf (F.smul_le i) (F'.smul_le i)⟩⟩

/-- The `Inf` of a family of `I.filtration`s is an `I.filtration`. -/
instance : has_Inf (I.filtration M) := ⟨λ S,
{ N := Inf (ideal.filtration.N '' S),
  mono := λ i, begin
    apply Inf_le_Inf_of_forall_exists_le _,
    rintros _ ⟨⟨_, F, hF, rfl⟩, rfl⟩,
    exact ⟨_, ⟨⟨_, F, hF, rfl⟩, rfl⟩, F.mono i⟩,
  end,
  smul_le := λ i, begin
    rw [Inf_eq_infi', infi_apply, infi_apply],
    refine submodule.smul_infi_le.trans _,
    apply infi_mono _,
    rintro ⟨_, F, hF, rfl⟩,
    exact F.smul_le i,
  end }⟩

instance : has_top (I.filtration M) := ⟨I.trivial_filtration ⊤⟩
instance : has_bot (I.filtration M) := ⟨I.trivial_filtration ⊥⟩

@[simp] lemma sup_N : (F ⊔ F').N = F.N ⊔ F'.N := rfl
@[simp] lemma Sup_N (S : set (I.filtration M)) : (Sup S).N = Sup (ideal.filtration.N '' S) := rfl
@[simp] lemma inf_N : (F ⊓ F').N = F.N ⊓ F'.N := rfl
@[simp] lemma Inf_N (S : set (I.filtration M)) : (Inf S).N = Inf (ideal.filtration.N '' S) := rfl
@[simp] lemma top_N : (⊤ : I.filtration M).N = ⊤ := rfl
@[simp] lemma bot_N : (⊥ : I.filtration M).N = ⊥ := rfl

@[simp] lemma supr_N {ι : Sort*} (f : ι → I.filtration M) : (supr f).N = ⨆ i, (f i).N :=
congr_arg Sup (set.range_comp _ _).symm

@[simp] lemma infi_N {ι : Sort*} (f : ι → I.filtration M) : (infi f).N = ⨅ i, (f i).N :=
congr_arg Inf (set.range_comp _ _).symm

instance : complete_lattice (I.filtration M) :=
function.injective.complete_lattice ideal.filtration.N ideal.filtration.ext
  sup_N inf_N (λ _, Sup_image) (λ _, Inf_image) top_N bot_N

instance : inhabited (I.filtration M) := ⟨⊥⟩

/-- An `I` filtration is stable if `I • F.N n = F.N (n+1)` for large enough `n`. -/
def stable : Prop :=
∃ n₀, ∀ n ≥ n₀, I • F.N n = F.N (n + 1)

/-- The trivial stable `I`-filtration of `N`. -/
@[simps]
def _root_.ideal.stable_filtration (I : ideal R) (N : submodule R M) :
  I.filtration M :=
{ N := λ i, I ^ i • N,
  mono := λ i, by { rw [add_comm, pow_add, mul_smul], exact submodule.smul_le_right },
  smul_le := λ i, by { rw [add_comm, pow_add, mul_smul, pow_one], exact le_refl _ } }

lemma _root_.ideal.stable_filtration_stable (I : ideal R) (N : submodule R M) :
  (I.stable_filtration N).stable :=
by { use 0, intros n _, dsimp, rw [add_comm, pow_add, mul_smul, pow_one] }

variables {F F'} (h : F.stable)

include h

lemma stable.exists_pow_smul_eq :
  ∃ n₀, ∀ k, F.N (n₀ + k) = I ^ k • F.N n₀ :=
begin
  obtain ⟨n₀, hn⟩ := h,
  use n₀,
  intro k,
  induction k,
  { simp },
  { rw [nat.succ_eq_add_one, ← add_assoc, ← hn, k_ih, add_comm, pow_add, mul_smul, pow_one],
    linarith }
end

lemma stable.exists_pow_smul_eq_of_ge :
  ∃ n₀, ∀ n ≥ n₀, F.N n = I ^ (n - n₀) • F.N n₀ :=
begin
  obtain ⟨n₀, hn₀⟩ := h.exists_pow_smul_eq,
  use n₀,
  intros n hn,
  convert hn₀ (n - n₀),
  rw [add_comm, tsub_add_cancel_of_le hn],
end

omit h

lemma stable_iff_exists_pow_smul_eq_of_ge :
  F.stable ↔ ∃ n₀, ∀ n ≥ n₀, F.N n = I ^ (n - n₀) • F.N n₀ :=
begin
  refine ⟨stable.exists_pow_smul_eq_of_ge, λ h, ⟨h.some, λ n hn, _⟩⟩,
  rw [h.some_spec n hn, h.some_spec (n+1) (by linarith), smul_smul, ← pow_succ,
    tsub_add_eq_add_tsub hn],
end

lemma stable.exists_forall_le (h : F.stable) (e : F.N 0 ≤ F'.N 0) :
  ∃ n₀, ∀ n, F.N (n + n₀) ≤ F'.N n :=
begin
  obtain ⟨n₀, hF⟩ := h,
  use n₀,
  intro n,
  induction n with n hn,
  { refine (F.antitone _).trans e, simp },
  { rw [nat.succ_eq_one_add, add_assoc, add_comm, add_comm 1 n, ← hF],
    exact (submodule.smul_mono_right hn).trans (F'.smul_le _),
    simp },
end

lemma stable.bounded_difference (h : F.stable) (h' : F'.stable) (e : F.N 0 = F'.N 0) :
  ∃ n₀, ∀ n, F.N (n + n₀) ≤ F'.N n ∧ F'.N (n + n₀) ≤ F.N n :=
begin
  obtain ⟨n₁, h₁⟩ := h.exists_forall_le (le_of_eq e),
  obtain ⟨n₂, h₂⟩ := h'.exists_forall_le (le_of_eq e.symm),
  use max n₁ n₂,
  intro n,
  refine ⟨(F.antitone _).trans (h₁ n), (F'.antitone _).trans (h₂ n)⟩; simp
end

open polynomial_module

variables (F F')

/-- The `R[IX]`-submodule of `M[X]` associated with an `I`-filtration. -/
protected
def submodule : submodule (rees_algebra I) (polynomial_module R M) :=
{ carrier := { f | ∀ i, f i ∈ F.N i },
  add_mem' := λ f g hf hg i, submodule.add_mem _ (hf i) (hg i),
  zero_mem' := λ i, submodule.zero_mem _,
  smul_mem' := λ r f hf i, begin
    rw [subalgebra.smul_def, polynomial_module.smul_apply],
    apply submodule.sum_mem,
    rintro ⟨j, k⟩ e,
    rw finset.nat.mem_antidiagonal at e,
    subst e,
    exact F.pow_smul_le j k (submodule.smul_mem_smul (r.2 j) (hf k))
  end }

@[simp]
lemma mem_submodule (f : polynomial_module R M) : f ∈ F.submodule ↔ ∀ i, f i ∈ F.N i := iff.rfl

lemma inf_submodule : (F ⊓ F').submodule = F.submodule ⊓ F'.submodule :=
by { ext, exact forall_and_distrib }

variables (I M)

/-- `ideal.filtration.submodule` as an `inf_hom` -/
def submodule_inf_hom :
  inf_hom (I.filtration M) (submodule (rees_algebra I) (polynomial_module R M)) :=
{ to_fun := ideal.filtration.submodule, map_inf' := inf_submodule }

variables {I M}

lemma submodule_closure_single :
  add_submonoid.closure (⋃ i, single R i '' (F.N i : set M)) = F.submodule.to_add_submonoid :=
begin
  apply le_antisymm,
  { rw [add_submonoid.closure_le, set.Union_subset_iff],
    rintro i _ ⟨m, hm, rfl⟩ j,
    rw single_apply,
    split_ifs,
    { rwa ← h },
    { exact (F.N j).zero_mem } },
  { intros f hf,
    rw [← f.sum_single],
    apply add_submonoid.sum_mem _ _,
    rintros c -,
    exact add_submonoid.subset_closure (set.subset_Union _ c $ set.mem_image_of_mem _ (hf c)) }
end

lemma submodule_span_single :
  submodule.span (rees_algebra I) (⋃ i, single R i '' (F.N i : set M)) = F.submodule :=
begin
  rw [← submodule.span_closure, submodule_closure_single],
  simp,
end

lemma submodule_eq_span_le_iff_stable_ge (n₀ : ℕ) :
  F.submodule = submodule.span _ (⋃ i ≤ n₀, single R i '' (F.N i : set M)) ↔
    ∀ n ≥ n₀, I • F.N n = F.N (n + 1) :=
begin
  rw [← submodule_span_single, ← has_le.le.le_iff_eq, submodule.span_le,
    set.Union_subset_iff],
  swap, { exact submodule.span_mono (set.Union₂_subset_Union _ _) },
  split,
  { intros H n hn,
    refine (F.smul_le n).antisymm _,
    intros x hx,
    obtain ⟨l, hl⟩ := (finsupp.mem_span_iff_total _ _ _).mp (H _ ⟨x, hx, rfl⟩),
    replace hl := congr_arg (λ f : ℕ →₀ M, f (n + 1)) hl,
    dsimp only at hl,
    erw finsupp.single_eq_same at hl,
    rw [← hl, finsupp.total_apply, finsupp.sum_apply],
    apply submodule.sum_mem _ _,
    rintros ⟨_, _, ⟨n', rfl⟩, _, ⟨hn', rfl⟩, m, hm, rfl⟩ -,
    dsimp only [subtype.coe_mk],
    rw [subalgebra.smul_def, smul_single_apply, if_pos (show n' ≤ n + 1, by linarith)],
    have e : n' ≤ n := by linarith,
    have := F.pow_smul_le_pow_smul (n - n') n' 1,
    rw [tsub_add_cancel_of_le e, pow_one, add_comm _ 1, ← add_tsub_assoc_of_le e, add_comm] at this,
    exact this (submodule.smul_mem_smul ((l _).2 $ n + 1 - n') hm) },
  { let F' := submodule.span (rees_algebra I) (⋃ i ≤ n₀, single R i '' (F.N i : set M)),
    intros hF i,
    have : ∀ i ≤ n₀, single R i '' (F.N i : set M) ⊆ F' :=
      λ i hi, set.subset.trans (set.subset_Union₂ i hi) submodule.subset_span,
    induction i with j hj,
    { exact this _ (zero_le _) },
    by_cases hj' : j.succ ≤ n₀,
    { exact this _ hj' },
    simp only [not_le, nat.lt_succ_iff] at hj',
    rw [nat.succ_eq_add_one, ← hF _ hj'],
    rintro _ ⟨m, hm, rfl⟩,
    apply submodule.smul_induction_on hm,
    { intros r hr m' hm',
      rw [add_comm, ← monomial_smul_single],
      exact F'.smul_mem ⟨_, rees_algebra.monomial_mem.mpr (by rwa pow_one)⟩
        (hj $ set.mem_image_of_mem _ hm') },
    { intros x y hx hy, rw map_add, exact F'.add_mem hx hy } }
end

/-- If the components of a filtration are finitely generated, then the filtration is stable iff
its associated submodule of is finitely generated.  -/
lemma submodule_fg_iff_stable (hF' : ∀ i, (F.N i).fg) :
  F.submodule.fg ↔ F.stable :=
begin
  classical,
  delta ideal.filtration.stable,
  simp_rw ← F.submodule_eq_span_le_iff_stable_ge,
  split,
  { rintro H,
    apply H.stablizes_of_supr_eq
      ⟨λ n₀, submodule.span _ (⋃ (i : ℕ) (H : i ≤ n₀), single R i '' ↑(F.N i)), _⟩,
    { dsimp,
      rw [← submodule.span_Union, ← submodule_span_single],
      congr' 1,
      ext,
      simp only [set.mem_Union, set.mem_image, set_like.mem_coe, exists_prop],
      split,
      { rintro ⟨-, i, -, e⟩, exact ⟨i, e⟩ },
      { rintro ⟨i, e⟩, exact ⟨i, i, le_refl i, e⟩ } },
    { intros n m e,
      rw [submodule.span_le, set.Union₂_subset_iff],
      intros i hi,
      refine (set.subset.trans _ (set.subset_Union₂ i (hi.trans e : _))).trans
        submodule.subset_span,
      refl } },
  { rintros ⟨n, hn⟩,
    rw hn,
    simp_rw [submodule.span_Union₂, ← finset.mem_range_succ_iff, supr_subtype'],
    apply submodule.fg_supr,
    rintro ⟨i, hi⟩,
    obtain ⟨s, hs⟩ := hF' i,
    have : submodule.span (rees_algebra I) (s.image (lsingle R i) : set (polynomial_module R M))
      = submodule.span _ (single R i '' (F.N i : set M)),
    { rw [finset.coe_image, ← submodule.span_span_of_tower R, ← submodule.map_span, hs], refl },
    rw [subtype.coe_mk, ← this],
    exact ⟨_, rfl⟩ }
end
.
variables {F}

lemma stable.of_le [is_noetherian_ring R] [h : module.finite R M] (hF : F.stable)
  {F' : I.filtration M} (hf : F' ≤ F) : F'.stable :=
begin
  haveI := is_noetherian_of_fg_of_noetherian' h.1,
  rw ← submodule_fg_iff_stable at hF ⊢,
  any_goals { intro i, exact is_noetherian.noetherian _ },
  have := is_noetherian_of_fg_of_noetherian _ hF,
  rw is_noetherian_submodule at this,
  exact this _ (order_hom_class.mono (submodule_inf_hom M I) hf),
end

lemma stable.inter_right [is_noetherian_ring R] [h : module.finite R M] (hF : F.stable) :
  (F ⊓ F').stable :=
hF.of_le inf_le_left

lemma stable.inter_left [is_noetherian_ring R] [h : module.finite R M] (hF : F.stable) :
  (F' ⊓ F).stable :=
hF.of_le inf_le_right

end ideal.filtration

variable (I)

/-- **Artin-Rees lemma** -/
lemma ideal.exists_pow_inf_eq_pow_smul [is_noetherian_ring R] [h : module.finite R M]
  (N : submodule R M) : ∃ k : ℕ, ∀ n ≥ k, I ^ n • ⊤ ⊓ N = I ^ (n - k) • (I ^ k • ⊤ ⊓ N) :=
((I.stable_filtration_stable ⊤).inter_right (I.trivial_filtration N)).exists_pow_smul_eq_of_ge

lemma ideal.mem_infi_smul_pow_eq_bot_iff [is_noetherian_ring R] [hM : module.finite R M] (x : M) :
  x ∈ (⨅ i : ℕ, I ^ i • ⊤ : submodule R M) ↔ ∃ r : I, (r : R) • x = x :=
begin
  let N := (⨅ i : ℕ, I ^ i • ⊤ : submodule R M),
  have hN : ∀ k, (I.stable_filtration ⊤ ⊓ I.trivial_filtration N).N k = N,
  { intro k, exact inf_eq_right.mpr ((infi_le _ k).trans $ le_of_eq $ by simp) },
  split,
  { haveI := is_noetherian_of_fg_of_noetherian' hM.out,
    obtain ⟨r, hr₁, hr₂⟩ := submodule.exists_mem_and_smul_eq_self_of_fg_of_le_smul I N
      (is_noetherian.noetherian N) _,
    { intro H, exact ⟨⟨r, hr₁⟩, hr₂ _ H⟩ },
    obtain ⟨k, hk⟩ := (I.stable_filtration_stable ⊤).inter_right (I.trivial_filtration N),
    have := hk k (le_refl _),
    rw [hN, hN] at this,
    exact le_of_eq this.symm },
  { rintro ⟨r, eq⟩,
    rw submodule.mem_infi,
    intro i,
    induction i with i hi,
    { simp },
    { rw [nat.succ_eq_one_add, pow_add, ← smul_smul, pow_one, ← eq],
      exact submodule.smul_mem_smul r.prop hi } }
end

lemma ideal.infi_pow_smul_eq_bot_of_local_ring [is_noetherian_ring R] [local_ring R]
  [module.finite R M] (h : I ≠ ⊤) :
  (⨅ i : ℕ, I ^ i • ⊤ : submodule R M) = ⊥ :=
begin
  rw eq_bot_iff,
  intros x hx,
  obtain ⟨r, hr⟩ := (I.mem_infi_smul_pow_eq_bot_iff x).mp hx,
  have := local_ring.is_unit_one_sub_self_of_mem_nonunits _ (local_ring.le_maximal_ideal h r.prop),
  apply this.smul_left_cancel.mp,
  swap, { apply_instance },
  simp [sub_smul, hr],
end

/-- **Krull's intersection theorem** for noetherian local rings. -/
lemma ideal.infi_pow_eq_bot_of_local_ring [is_noetherian_ring R] [local_ring R] (h : I ≠ ⊤) :
  (⨅ i : ℕ, I ^ i) = ⊥ :=
begin
  convert I.infi_pow_smul_eq_bot_of_local_ring h,
  ext i,
  rw [smul_eq_mul, ← ideal.one_eq_top, mul_one],
  apply_instance,
end

/-- **Krull's intersection theorem** for noetherian domains. -/
lemma ideal.infi_pow_eq_bot_of_is_domain [is_noetherian_ring R] [is_domain R] (h : I ≠ ⊤) :
  (⨅ i : ℕ, I ^ i) = ⊥ :=
begin
  rw eq_bot_iff,
  intros x hx,
  by_contra hx',
  have := ideal.mem_infi_smul_pow_eq_bot_iff I x,
  simp_rw [smul_eq_mul, ← ideal.one_eq_top, mul_one] at this,
  obtain ⟨r, hr⟩ := this.mp hx,
  have := mul_right_cancel₀ hx' (hr.trans (one_mul x).symm),
  exact I.eq_top_iff_one.not.mp h (this ▸ r.prop),
end


/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.logic
import Mathlib.Lean3Lib.init.data.ordering.basic

universes u v l

namespace Mathlib

class is_symm_op (α : Type u) (β : outParam (Type v)) (op : α → α → β) where
  symm_op : ∀ (a b : α), op a b = op b a

class is_commutative (α : Type u) (op : α → α → α) where
  comm : ∀ (a b : α), op a b = op b a

protected instance is_symm_op_of_is_commutative (α : Type u) (op : α → α → α)
    [is_commutative α op] : is_symm_op α α op :=
  is_symm_op.mk is_commutative.comm

class is_associative (α : Type u) (op : α → α → α) where
  assoc : ∀ (a b c : α), op (op a b) c = op a (op b c)

class is_left_id (α : Type u) (op : α → α → α) (o : outParam α) where
  left_id : ∀ (a : α), op o a = a

class is_right_id (α : Type u) (op : α → α → α) (o : outParam α) where
  right_id : ∀ (a : α), op a o = a

class is_left_null (α : Type u) (op : α → α → α) (o : outParam α) where
  left_null : ∀ (a : α), op o a = o

class is_right_null (α : Type u) (op : α → α → α) (o : outParam α) where
  right_null : ∀ (a : α), op a o = o

class is_left_cancel (α : Type u) (op : α → α → α) where
  left_cancel : ∀ (a b c : α), op a b = op a c → b = c

class is_right_cancel (α : Type u) (op : α → α → α) where
  right_cancel : ∀ (a b c : α), op a b = op c b → a = c

class is_idempotent (α : Type u) (op : α → α → α) where
  idempotent : ∀ (a : α), op a a = a

class is_left_distrib (α : Type u) (op₁ : α → α → α) (op₂ : outParam (α → α → α)) where
  left_distrib : ∀ (a b c : α), op₁ a (op₂ b c) = op₂ (op₁ a b) (op₁ a c)

class is_right_distrib (α : Type u) (op₁ : α → α → α) (op₂ : outParam (α → α → α)) where
  right_distrib : ∀ (a b c : α), op₁ (op₂ a b) c = op₂ (op₁ a c) (op₁ b c)

class is_left_inv (α : Type u) (op : α → α → α) (inv : outParam (α → α)) (o : outParam α) where
  left_inv : ∀ (a : α), op (inv a) a = o

class is_right_inv (α : Type u) (op : α → α → α) (inv : outParam (α → α)) (o : outParam α) where
  right_inv : ∀ (a : α), op a (inv a) = o

class is_cond_left_inv (α : Type u) (op : α → α → α) (inv : outParam (α → α)) (o : outParam α)
    (p : outParam (α → Prop))
    where
  left_inv : ∀ (a : α), p a → op (inv a) a = o

class is_cond_right_inv (α : Type u) (op : α → α → α) (inv : outParam (α → α)) (o : outParam α)
    (p : outParam (α → Prop))
    where
  right_inv : ∀ (a : α), p a → op a (inv a) = o

class is_distinct (α : Type u) (a : α) (b : α) where
  distinct : a ≠ b

/-
-- The following type class doesn't seem very useful, a regular simp lemma should work for this.

-- The following type class doesn't seem very useful, a regular simp lemma should work for this.
class is_inv (α : Type u) (β : Type v) (f : α → β) (g : out β → α) : Prop :=
(inv : ∀ a, g (f a) = a)

-- The following one can also be handled using a regular simp lemma

-- The following one can also be handled using a regular simp lemma
class is_idempotent (α : Type u) (f : α → α) : Prop :=
(idempotent : ∀ a, f (f a) = f a)
-/

/-- `is_irrefl X r` means the binary relation `r` on `X` is irreflexive (that is, `r x x` never
holds). -/
class is_irrefl (α : Type u) (r : α → α → Prop) where
  irrefl : ∀ (a : α), ¬r a a

/-- `is_refl X r` means the binary relation `r` on `X` is reflexive. -/
class is_refl (α : Type u) (r : α → α → Prop) where
  refl : ∀ (a : α), r a a

/-- `is_symm X r` means the binary relation `r` on `X` is symmetric. -/
class is_symm (α : Type u) (r : α → α → Prop) where
  symm : ∀ (a b : α), r a b → r b a

/-- The opposite of a symmetric relation is symmetric. -/
protected instance is_symm_op_of_is_symm (α : Type u) (r : α → α → Prop) [is_symm α r] :
    is_symm_op α Prop r :=
  is_symm_op.mk fun (a b : α) => propext { mp := is_symm.symm a b, mpr := is_symm.symm b a }

/-- `is_asymm X r` means that the binary relation `r` on `X` is asymmetric, that is,
`r a b → ¬ r b a`. -/
class is_asymm (α : Type u) (r : α → α → Prop) where
  asymm : ∀ (a b : α), r a b → ¬r b a

/-- `is_antisymm X r` means the binary relation `r` on `X` is antisymmetric. -/
class is_antisymm (α : Type u) (r : α → α → Prop) where
  antisymm : ∀ (a b : α), r a b → r b a → a = b

/-- `is_trans X r` means the binary relation `r` on `X` is transitive. -/
class is_trans (α : Type u) (r : α → α → Prop) where
  trans : ∀ (a b c : α), r a b → r b c → r a c

/-- `is_total X r` means that the binary relation `r` on `X` is total, that is, that for any
`x y : X` we have `r x y` or `r y x`.-/
class is_total (α : Type u) (r : α → α → Prop) where
  total : ∀ (a b : α), r a b ∨ r b a

/-- `is_preorder X r` means that the binary relation `r` on `X` is a pre-order, that is, reflexive
and transitive. -/
class is_preorder (α : Type u) (r : α → α → Prop) extends is_refl α r, is_trans α r where

/-- `is_total_preorder X r` means that the binary relation `r` on `X` is total and a preorder. -/
class is_total_preorder (α : Type u) (r : α → α → Prop) extends is_trans α r, is_total α r where

/-- Every total pre-order is a pre-order. -/
protected instance is_total_preorder_is_preorder (α : Type u) (r : α → α → Prop)
    [s : is_total_preorder α r] : is_preorder α r :=
  is_preorder.mk

class is_partial_order (α : Type u) (r : α → α → Prop) extends is_antisymm α r, is_preorder α r
    where

class is_linear_order (α : Type u) (r : α → α → Prop) extends is_total α r, is_partial_order α r
    where

class is_equiv (α : Type u) (r : α → α → Prop) extends is_symm α r, is_preorder α r where

class is_per (α : Type u) (r : α → α → Prop) extends is_symm α r, is_trans α r where

class is_strict_order (α : Type u) (r : α → α → Prop) extends is_trans α r, is_irrefl α r where

class is_incomp_trans (α : Type u) (lt : α → α → Prop) where
  incomp_trans : ∀ (a b c : α), ¬lt a b ∧ ¬lt b a → ¬lt b c ∧ ¬lt c b → ¬lt a c ∧ ¬lt c a

class is_strict_weak_order (α : Type u) (lt : α → α → Prop)
    extends is_incomp_trans α lt, is_strict_order α lt where

class is_trichotomous (α : Type u) (lt : α → α → Prop) where
  trichotomous : ∀ (a b : α), lt a b ∨ a = b ∨ lt b a

class is_strict_total_order (α : Type u) (lt : α → α → Prop)
    extends is_strict_weak_order α lt, is_trichotomous α lt where

protected instance eq_is_equiv (α : Type u) : is_equiv α Eq := is_equiv.mk

theorem irrefl {α : Type u} {r : α → α → Prop} [is_irrefl α r] (a : α) : ¬r a a :=
  is_irrefl.irrefl a

theorem refl {α : Type u} {r : α → α → Prop} [is_refl α r] (a : α) : r a a := is_refl.refl a

theorem trans {α : Type u} {r : α → α → Prop} [is_trans α r] {a : α} {b : α} {c : α} :
    r a b → r b c → r a c :=
  is_trans.trans a b c

theorem symm {α : Type u} {r : α → α → Prop} [is_symm α r] {a : α} {b : α} : r a b → r b a :=
  is_symm.symm a b

theorem antisymm {α : Type u} {r : α → α → Prop} [is_antisymm α r] {a : α} {b : α} :
    r a b → r b a → a = b :=
  is_antisymm.antisymm a b

theorem asymm {α : Type u} {r : α → α → Prop} [is_asymm α r] {a : α} {b : α} : r a b → ¬r b a :=
  is_asymm.asymm a b

theorem trichotomous {α : Type u} {r : α → α → Prop} [is_trichotomous α r] (a : α) (b : α) :
    r a b ∨ a = b ∨ r b a :=
  is_trichotomous.trichotomous

theorem incomp_trans {α : Type u} {r : α → α → Prop} [is_incomp_trans α r] {a : α} {b : α} {c : α} :
    ¬r a b ∧ ¬r b a → ¬r b c ∧ ¬r c b → ¬r a c ∧ ¬r c a :=
  is_incomp_trans.incomp_trans a b c

protected instance is_asymm_of_is_trans_of_is_irrefl {α : Type u} {r : α → α → Prop} [is_trans α r]
    [is_irrefl α r] : is_asymm α r :=
  is_asymm.mk fun (a b : α) (h₁ : r a b) (h₂ : r b a) => absurd (trans h₁ h₂) (irrefl a)

theorem irrefl_of {α : Type u} (r : α → α → Prop) [is_irrefl α r] (a : α) : ¬r a a := irrefl a

theorem refl_of {α : Type u} (r : α → α → Prop) [is_refl α r] (a : α) : r a a := refl a

theorem trans_of {α : Type u} (r : α → α → Prop) [is_trans α r] {a : α} {b : α} {c : α} :
    r a b → r b c → r a c :=
  trans

theorem symm_of {α : Type u} (r : α → α → Prop) [is_symm α r] {a : α} {b : α} : r a b → r b a :=
  symm

theorem asymm_of {α : Type u} (r : α → α → Prop) [is_asymm α r] {a : α} {b : α} : r a b → ¬r b a :=
  asymm

theorem total_of {α : Type u} (r : α → α → Prop) [is_total α r] (a : α) (b : α) : r a b ∨ r b a :=
  is_total.total a b

theorem trichotomous_of {α : Type u} (r : α → α → Prop) [is_trichotomous α r] (a : α) (b : α) :
    r a b ∨ a = b ∨ r b a :=
  trichotomous

theorem incomp_trans_of {α : Type u} (r : α → α → Prop) [is_incomp_trans α r] {a : α} {b : α}
    {c : α} : ¬r a b ∧ ¬r b a → ¬r b c ∧ ¬r c b → ¬r a c ∧ ¬r c a :=
  incomp_trans

namespace strict_weak_order

def equiv {α : Type u} {r : α → α → Prop} (a : α) (b : α) := ¬r a b ∧ ¬r b a

theorem erefl {α : Type u} {r : α → α → Prop} [is_strict_weak_order α r] (a : α) : equiv a a :=
  { left := irrefl a, right := irrefl a }

theorem esymm {α : Type u} {r : α → α → Prop} [is_strict_weak_order α r] {a : α} {b : α} :
    equiv a b → equiv b a :=
  sorry

theorem etrans {α : Type u} {r : α → α → Prop} [is_strict_weak_order α r] {a : α} {b : α} {c : α} :
    equiv a b → equiv b c → equiv a c :=
  incomp_trans

theorem not_lt_of_equiv {α : Type u} {r : α → α → Prop} [is_strict_weak_order α r] {a : α} {b : α} :
    equiv a b → ¬r a b :=
  fun (h : equiv a b) => and.left h

theorem not_lt_of_equiv' {α : Type u} {r : α → α → Prop} [is_strict_weak_order α r] {a : α}
    {b : α} : equiv a b → ¬r b a :=
  fun (h : equiv a b) => and.right h

protected instance is_equiv {α : Type u} {r : α → α → Prop} [is_strict_weak_order α r] :
    is_equiv α equiv :=
  is_equiv.mk

/- Notation for the equivalence relation induced by lt -/

end strict_weak_order

theorem is_strict_weak_order_of_is_total_preorder {α : Type u} {le : α → α → Prop}
    {lt : α → α → Prop} [DecidableRel le] [s : is_total_preorder α le]
    (h : ∀ (a b : α), lt a b ↔ ¬le b a) : is_strict_weak_order α lt :=
  is_strict_weak_order.mk

theorem lt_of_lt_of_incomp {α : Type u} {lt : α → α → Prop} [is_strict_weak_order α lt]
    [DecidableRel lt] {a : α} {b : α} {c : α} : lt a b → ¬lt b c ∧ ¬lt c b → lt a c :=
  sorry

theorem lt_of_incomp_of_lt {α : Type u} {lt : α → α → Prop} [is_strict_weak_order α lt]
    [DecidableRel lt] {a : α} {b : α} {c : α} : ¬lt a b ∧ ¬lt b a → lt b c → lt a c :=
  sorry

theorem eq_of_incomp {α : Type u} {lt : α → α → Prop} [is_trichotomous α lt] {a : α} {b : α} :
    ¬lt a b ∧ ¬lt b a → a = b :=
  sorry

theorem eq_of_eqv_lt {α : Type u} {lt : α → α → Prop} [is_trichotomous α lt] {a : α} {b : α} :
    strict_weak_order.equiv a b → a = b :=
  eq_of_incomp

theorem incomp_iff_eq {α : Type u} {lt : α → α → Prop} [is_trichotomous α lt] [is_irrefl α lt]
    (a : α) (b : α) : ¬lt a b ∧ ¬lt b a ↔ a = b :=
  { mp := eq_of_incomp,
    mpr := fun (hab : a = b) => hab ▸ { left := irrefl_of lt a, right := irrefl_of lt a } }

theorem eqv_lt_iff_eq {α : Type u} {lt : α → α → Prop} [is_trichotomous α lt] [is_irrefl α lt]
    (a : α) (b : α) : strict_weak_order.equiv a b ↔ a = b :=
  incomp_iff_eq a b

theorem not_lt_of_lt {α : Type u} {lt : α → α → Prop} [is_strict_order α lt] {a : α} {b : α} :
    lt a b → ¬lt b a :=
  fun (h₁ : lt a b) (h₂ : lt b a) => absurd (trans_of lt h₁ h₂) (irrefl_of lt a)

end Mathlib


/-
Copyright (c) 2017 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Stephen Morgan, Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.products.basic
import Mathlib.PostPort

universes v₁ v₂ v₃ u₁ u₂ u₃

namespace Mathlib

namespace category_theory.bifunctor

@[simp] theorem map_id {C : Type u₁} {D : Type u₂} {E : Type u₃} [category C] [category D] [category E] (F : C × D ⥤ E) (X : C) (Y : D) : functor.map F (𝟙, 𝟙) = 𝟙 :=
  functor.map_id F (X, Y)

@[simp] theorem map_id_comp {C : Type u₁} {D : Type u₂} {E : Type u₃} [category C] [category D] [category E] (F : C × D ⥤ E) (W : C) {X : D} {Y : D} {Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) : functor.map F (𝟙, f ≫ g) = functor.map F (𝟙, f) ≫ functor.map F (𝟙, g) := sorry

@[simp] theorem map_comp_id {C : Type u₁} {D : Type u₂} {E : Type u₃} [category C] [category D] [category E] (F : C × D ⥤ E) (X : C) (Y : C) (Z : C) (W : D) (f : X ⟶ Y) (g : Y ⟶ Z) : functor.map F (f ≫ g, 𝟙) = functor.map F (f, 𝟙) ≫ functor.map F (g, 𝟙) := sorry

@[simp] theorem diagonal {C : Type u₁} {D : Type u₂} {E : Type u₃} [category C] [category D] [category E] (F : C × D ⥤ E) (X : C) (X' : C) (f : X ⟶ X') (Y : D) (Y' : D) (g : Y ⟶ Y') : functor.map F (𝟙, g) ≫ functor.map F (f, 𝟙) = functor.map F (f, g) := sorry

@[simp] theorem diagonal' {C : Type u₁} {D : Type u₂} {E : Type u₃} [category C] [category D] [category E] (F : C × D ⥤ E) (X : C) (X' : C) (f : X ⟶ X') (Y : D) (Y' : D) (g : Y ⟶ Y') : functor.map F (f, 𝟙) ≫ functor.map F (𝟙, g) = functor.map F (f, g) := sorry


/-
Copyright (c) 2019 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.ring_theory.polynomial.basic
import Mathlib.algebra.algebra.subalgebra
import Mathlib.PostPort

universes u v w

namespace Mathlib

/-!
# Adjoining elements to form subalgebras

This file develops the basic theory of subalgebras of an R-algebra generated
by a set of elements. A basic interface for `adjoin` is set up, and various
results about finitely-generated subalgebras and submodules are proved.

## Definitions

* `fg (S : subalgebra R A)` : A predicate saying that the subalgebra is finitely-generated
as an A-algebra

## Tags

adjoin, algebra, finitely-generated algebra

-/

namespace algebra

theorem subset_adjoin {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] {s : set A} : s ⊆ ↑(adjoin R s) :=
  set.subset.trans (set.subset_union_right (set.range ⇑(algebra_map R A)) s) subsemiring.subset_closure

theorem adjoin_le {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] {s : set A} {S : subalgebra R A} (H : s ⊆ ↑S) : adjoin R s ≤ S :=
  iff.mpr subsemiring.closure_le (set.union_subset (subalgebra.range_le S) H)

theorem adjoin_le_iff {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] {s : set A} {S : subalgebra R A} : adjoin R s ≤ S ↔ s ⊆ ↑S :=
  { mp := set.subset.trans subset_adjoin, mpr := adjoin_le }

theorem adjoin_mono {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] {s : set A} {t : set A} (H : s ⊆ t) : adjoin R s ≤ adjoin R t :=
  iff.mpr subsemiring.closure_le
    (set.subset.trans (set.union_subset_union_right (set.range ⇑(algebra_map R A)) H) subsemiring.subset_closure)

@[simp] theorem adjoin_empty (R : Type u) (A : Type v) [comm_semiring R] [semiring A] [algebra R A] : adjoin R ∅ = ⊥ :=
  iff.mpr eq_bot_iff (adjoin_le (set.empty_subset ↑⊥))

theorem adjoin_eq_span (R : Type u) {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (s : set A) : ↑(adjoin R s) = submodule.span R (monoid.closure s) := sorry

theorem adjoin_image (R : Type u) {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (f : alg_hom R A B) (s : set A) : adjoin R (⇑f '' s) = subalgebra.map (adjoin R s) f :=
  le_antisymm (adjoin_le (set.image_subset (⇑f) subset_adjoin))
    (iff.mpr subalgebra.map_le (adjoin_le (iff.mp set.image_subset_iff subset_adjoin)))

@[simp] theorem adjoin_insert_adjoin (R : Type u) {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (s : set A) (x : A) : adjoin R (insert x ↑↑(adjoin R s)) = adjoin R (insert x s) := sorry

theorem adjoin_union (R : Type u) {A : Type v} [comm_semiring R] [comm_semiring A] [algebra R A] (s : set A) (t : set A) : adjoin R (s ∪ t) = subalgebra.under (adjoin R s) (adjoin (↥(adjoin R s)) t) := sorry

theorem adjoin_eq_range (R : Type u) {A : Type v} [comm_semiring R] [comm_semiring A] [algebra R A] (s : set A) : adjoin R s = alg_hom.range (mv_polynomial.aeval coe) := sorry

theorem adjoin_singleton_eq_range (R : Type u) {A : Type v} [comm_semiring R] [comm_semiring A] [algebra R A] (x : A) : adjoin R (singleton x) = alg_hom.range (polynomial.aeval x) := sorry

theorem adjoin_singleton_one (R : Type u) {A : Type v} [comm_semiring R] [comm_semiring A] [algebra R A] : adjoin R (singleton 1) = ⊥ :=
  iff.mpr eq_bot_iff (adjoin_le (iff.mpr set.singleton_subset_iff (subalgebra.one_mem ⊥)))

theorem adjoin_union_coe_submodule (R : Type u) {A : Type v} [comm_semiring R] [comm_semiring A] [algebra R A] (s : set A) (t : set A) : ↑(adjoin R (s ∪ t)) = ↑(adjoin R s) * ↑(adjoin R t) := sorry

theorem adjoin_int {R : Type u} [comm_ring R] (s : set R) : adjoin ℤ s = subalgebra_of_is_subring (ring.closure s) :=
  le_antisymm (adjoin_le ring.subset_closure) (ring.closure_subset subset_adjoin)

theorem mem_adjoin_iff {R : Type u} {A : Type v} [comm_ring R] [ring A] [algebra R A] {s : set A} {x : A} : x ∈ adjoin R s ↔ x ∈ ring.closure (set.range ⇑(algebra_map R A) ∪ s) := sorry

theorem adjoin_eq_ring_closure {R : Type u} {A : Type v} [comm_ring R] [ring A] [algebra R A] (s : set A) : ↑(adjoin R s) = ring.closure (set.range ⇑(algebra_map R A) ∪ s) :=
  set.ext fun (x : A) => mem_adjoin_iff

theorem fg_trans {R : Type u} {A : Type v} [comm_ring R] [comm_ring A] [algebra R A] {s : set A} {t : set A} (h1 : submodule.fg ↑(adjoin R s)) (h2 : submodule.fg ↑(adjoin (↥(adjoin R s)) t)) : submodule.fg ↑(adjoin R (s ∪ t)) := sorry

end algebra

namespace subalgebra

/-- A subalgebra `S` is finitely generated if there exists `t : finset A` such that
`algebra.adjoin R t = S`. -/
def fg {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (S : subalgebra R A) :=
  ∃ (t : finset A), algebra.adjoin R ↑t = S

theorem fg_adjoin_finset {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (s : finset A) : fg (algebra.adjoin R ↑s) :=
  Exists.intro s rfl

theorem fg_def {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] {S : subalgebra R A} : fg S ↔ ∃ (t : set A), set.finite t ∧ algebra.adjoin R t = S := sorry

theorem fg_bot {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] : fg ⊥ :=
  Exists.intro ∅ (algebra.adjoin_empty R A)

theorem fg_of_fg_to_submodule {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] {S : subalgebra R A} : submodule.fg ↑S → fg S := sorry

theorem fg_of_noetherian {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] [is_noetherian R A] (S : subalgebra R A) : fg S :=
  fg_of_fg_to_submodule (is_noetherian.noetherian ↑S)

theorem fg_of_submodule_fg {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (h : submodule.fg ⊤) : fg ⊤ := sorry

theorem fg_map {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [algebra R A] [semiring B] [algebra R B] (S : subalgebra R A) (f : alg_hom R A B) (hs : fg S) : fg (map S f) := sorry

theorem fg_of_fg_map {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [algebra R A] [semiring B] [algebra R B] (S : subalgebra R A) (f : alg_hom R A B) (hf : function.injective ⇑f) (hs : fg (map S f)) : fg S := sorry

theorem fg_top {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (S : subalgebra R A) : fg ⊤ ↔ fg S := sorry

theorem induction_on_adjoin {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] [is_noetherian R A] (P : subalgebra R A → Prop) (base : P ⊥) (ih : ∀ (S : subalgebra R A) (x : A), P S → P (algebra.adjoin R (insert x ↑S))) (S : subalgebra R A) : P S := sorry

end subalgebra

/-- The image of a Noetherian R-algebra under an R-algebra map is a Noetherian ring. -/
protected instance alg_hom.is_noetherian_ring_range {R : Type u} {A : Type v} {B : Type w} [comm_ring R] [comm_ring A] [comm_ring B] [algebra R A] [algebra R B] (f : alg_hom R A B) [is_noetherian_ring A] : is_noetherian_ring ↥(alg_hom.range f) :=
  Mathlib.is_noetherian_ring_range (alg_hom.to_ring_hom f)

theorem is_noetherian_ring_of_fg {R : Type u} {A : Type v} [comm_ring R] [comm_ring A] [algebra R A] {S : subalgebra R A} (HS : subalgebra.fg S) [is_noetherian_ring R] : is_noetherian_ring ↥S := sorry

theorem is_noetherian_ring_closure {R : Type u} [comm_ring R] (s : set R) (hs : set.finite s) : is_noetherian_ring ↥(ring.closure s) :=
  (fun (this : is_noetherian_ring ↥(subalgebra_of_is_subring (ring.closure s))) => this)
    (algebra.adjoin_int s ▸
      is_noetherian_ring_of_fg (iff.mpr subalgebra.fg_def (Exists.intro s { left := hs, right := rfl })))


import Timelib.Date.Year
import Timelib.Date.Month
import Timelib.Date.ScalarDate
import Timelib.Date.OrdinalDate
import Timelib.Date.Ymd
import Timelib.Date.Convert

def ScalarDate.firstOfTheMonth (year : Year) (m : Month) : ScalarDate :=
  Ymd.toScalarDate ⟨year, m, 1, le_refl _, Month.numDays_pos _ _⟩

def ScalarDate.lastOfTheMonth (year : Year) (m : Month) : ScalarDate :=
  Ymd.toScalarDate ⟨year, m, m.numDays year, Month.numDays_pos _ _, le_refl _⟩

def ScalarDate.newYear (year : Year) : ScalarDate := firstOfTheMonth 1 1

/--
The date of the first k-day (day of the week) on or before the passed date.
E.g. the first Monday on or before January 13, 2022 = January 10, 2022.
-/
def ScalarDate.kDayOnOrBefore (k : Nat) (h : k < 7 := by decide) : ScalarDate → ScalarDate
| ⟨day⟩ => ⟨day - (day - k).rataDie⟩

theorem ScalarDate.kDayOnOrBefore_preserves (k : Nat) (h : k < 7) (d : ScalarDate) :
  (d.kDayOnOrBefore k h).day.rataDie = k := sorry

def ScalarDate.kDayOnOrAfter (k : Nat) (d : ScalarDate) (h : k < 7 := by decide) : ScalarDate :=
  kDayOnOrBefore k h (d.addDays 6)

theorem ScalarDate.kDayOnOrAfter_preserves (k : Nat) (h : k < 7) (d : ScalarDate) :
  (d.kDayOnOrAfter k h).day.rataDie = k := sorry

def ScalarDate.kDayNearest (k : Nat) (d : ScalarDate) (h : k < 7 := by decide) : ScalarDate :=
  kDayOnOrBefore k h (d.addDays 3)

def ScalarDate.kDayBefore (k : Nat) (d : ScalarDate) (h : k < 7 := by decide) : ScalarDate :=
  kDayOnOrBefore k h (d.subDays 1)

def ScalarDate.kDayAfter (k : Nat) (d : ScalarDate) (h : k < 7 := by decide) : ScalarDate :=
  kDayOnOrBefore k h (d.addDays 7)

/--
If n > 0, the date of the nth k-day on or after the date.
If n < 0, counts backwards, returning the nth k-day on or before the date.
-/
def ScalarDate.nthKDay (n : Int) (k : Nat) (d : ScalarDate) (hk : k < 7 := by decide) (hn : n ≠ 0 := by decide) : ScalarDate :=
  if n > 0
  then ⟨7 * n + (d.kDayBefore k hk).day⟩
  else ⟨7 * n + (d.kDayAfter k hk).day⟩

/--
Returns the first k-day on or after the current date. To use this to find the
first k-day of the month, the date should be the first day of the month.
-/
def ScalarDate.firstKDay (k : Nat) (d : ScalarDate) (h : k < 7 := by decide) : ScalarDate :=
  d.nthKDay 1 k h

/--
Returns the last kday on or before the current date. To use this to find the
last kday of a given month, the date should be the final day of that month.
-/
def ScalarDate.lastKDay (k : Nat) (d : ScalarDate) (h : k < 7 := by decide) : ScalarDate :=
  d.nthKDay (-1) k h

def Year.nthKDayOfMonth (y : Year) (n : Nat) (k : Nat) (m : Month) (hk : k < 7 := by decide) (hn : (Int.ofNat n) ≠ 0 := by decide) : ScalarDate :=
  (ScalarDate.firstOfTheMonth y m).nthKDay (.ofNat n) k hk hn

theorem Year.nthKDayOfMonth_preserves (y : Year) (n : Nat) (k : Nat) (m : Month) (hk : k < 7) (hn : (Int.ofNat n) ≠ 0) :
  (y.nthKDayOfMonth n k m hk hn).day.rataDie = k := sorry

def Year.firstKDayOfMonth (y : Year) (k : Nat) (m : Month) (hk : k < 7 := by decide) : ScalarDate :=
  y.nthKDayOfMonth 1 k m hk

def Year.firstKDayOfMonth' (y : Year) (k : Nat) (m : Month) (hk : k < 7 := by decide) : ScalarDate :=
  (ScalarDate.firstOfTheMonth y m).firstKDay k hk

theorem firstKDayOfMonth_eq (y : Year) (k : Nat) (m : Month) (h : k < 7) : y.firstKDayOfMonth k m h = y.firstKDayOfMonth' k m h := by
  simp [Year.firstKDayOfMonth, Year.firstKDayOfMonth', Year.nthKDayOfMonth, ScalarDate.firstKDay]

def Year.lastKDayOfMonth (y : Year) (k : Nat) (m : Month) (hk : k < 7 := by decide) : ScalarDate :=
  (ScalarDate.lastOfTheMonth y m).lastKDay k hk