/-
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.creates
! 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.Preserves.Basic

/-!
# Creating (co)limits

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

We say that `F` creates limits of `K` if, given any limit cone `c` for `K ⋙ F`
(i.e. below) we can lift it to a cone "above", and further that `F` reflects
limits for `K`.
-/

open CategoryTheory CategoryTheory.Limits

noncomputable section

namespace CategoryTheory

universe w' w v₁ v₂ v₃ u₁ u₂ u₃

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

section Creates

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

variable {J : Type w} [Category.{w'} J] {K : J ⥤ C}

#print CategoryTheory.LiftableCone /-
/-- Define the lift of a cone: For a cone `c` for `K ⋙ F`, give a cone for `K`
which is a lift of `c`, i.e. the image of it under `F` is (iso) to `c`.

We will then use this as part of the definition of creation of limits:
every limit cone has a lift.

Note this definition is really only useful when `c` is a limit already.
-/
structure LiftableCone (K : J ⥤ C) (F : C ⥤ D) (c : Cone (K ⋙ F)) where
  liftedCone : Cone K
  validLift : F.mapCone lifted_cone ≅ c
#align category_theory.liftable_cone CategoryTheory.LiftableCone
-/

#print CategoryTheory.LiftableCocone /-
/-- Define the lift of a cocone: For a cocone `c` for `K ⋙ F`, give a cocone for
`K` which is a lift of `c`, i.e. the image of it under `F` is (iso) to `c`.

We will then use this as part of the definition of creation of colimits:
every limit cocone has a lift.

Note this definition is really only useful when `c` is a colimit already.
-/
structure LiftableCocone (K : J ⥤ C) (F : C ⥤ D) (c : Cocone (K ⋙ F)) where
  liftedCocone : Cocone K
  validLift : F.mapCocone lifted_cocone ≅ c
#align category_theory.liftable_cocone CategoryTheory.LiftableCocone
-/

#print CategoryTheory.CreatesLimit /-
/-- Definition 3.3.1 of [Riehl].
We say that `F` creates limits of `K` if, given any limit cone `c` for `K ⋙ F`
(i.e. below) we can lift it to a cone "above", and further that `F` reflects
limits for `K`.

If `F` reflects isomorphisms, it suffices to show only that the lifted cone is
a limit - see `creates_limit_of_reflects_iso`.
-/
class CreatesLimit (K : J ⥤ C) (F : C ⥤ D) extends ReflectsLimit K F where
  lifts : ∀ c, IsLimit c → LiftableCone K F c
#align category_theory.creates_limit CategoryTheory.CreatesLimit
-/

#print CategoryTheory.CreatesLimitsOfShape /-
/-- `F` creates limits of shape `J` if `F` creates the limit of any diagram
`K : J ⥤ C`.
-/
class CreatesLimitsOfShape (J : Type w) [Category.{w'} J] (F : C ⥤ D) where
  CreatesLimit : ∀ {K : J ⥤ C}, CreatesLimit K F := by infer_instance
#align category_theory.creates_limits_of_shape CategoryTheory.CreatesLimitsOfShape
-/

#print CategoryTheory.CreatesLimitsOfSize /-
-- This should be used with explicit universe variables.
/-- `F` creates limits if it creates limits of shape `J` for any `J`. -/
@[nolint check_univs]
class CreatesLimitsOfSize (F : C ⥤ D) where
  CreatesLimitsOfShape : ∀ {J : Type w} [Category.{w'} J], CreatesLimitsOfShape J F := by
    infer_instance
#align category_theory.creates_limits_of_size CategoryTheory.CreatesLimitsOfSize
-/

#print CategoryTheory.CreatesLimits /-
/-- `F` creates small limits if it creates limits of shape `J` for any small `J`. -/
abbrev CreatesLimits (F : C ⥤ D) :=
  CreatesLimitsOfSize.{v₂, v₂} F
#align category_theory.creates_limits CategoryTheory.CreatesLimits
-/

#print CategoryTheory.CreatesColimit /-
/-- Dual of definition 3.3.1 of [Riehl].
We say that `F` creates colimits of `K` if, given any limit cocone `c` for
`K ⋙ F` (i.e. below) we can lift it to a cocone "above", and further that `F`
reflects limits for `K`.

If `F` reflects isomorphisms, it suffices to show only that the lifted cocone is
a limit - see `creates_limit_of_reflects_iso`.
-/
class CreatesColimit (K : J ⥤ C) (F : C ⥤ D) extends ReflectsColimit K F where
  lifts : ∀ c, IsColimit c → LiftableCocone K F c
#align category_theory.creates_colimit CategoryTheory.CreatesColimit
-/

#print CategoryTheory.CreatesColimitsOfShape /-
/-- `F` creates colimits of shape `J` if `F` creates the colimit of any diagram
`K : J ⥤ C`.
-/
class CreatesColimitsOfShape (J : Type w) [Category.{w'} J] (F : C ⥤ D) where
  CreatesColimit : ∀ {K : J ⥤ C}, CreatesColimit K F := by infer_instance
#align category_theory.creates_colimits_of_shape CategoryTheory.CreatesColimitsOfShape
-/

#print CategoryTheory.CreatesColimitsOfSize /-
-- This should be used with explicit universe variables.
/-- `F` creates colimits if it creates colimits of shape `J` for any small `J`. -/
@[nolint check_univs]
class CreatesColimitsOfSize (F : C ⥤ D) where
  CreatesColimitsOfShape : ∀ {J : Type w} [Category.{w'} J], CreatesColimitsOfShape J F := by
    infer_instance
#align category_theory.creates_colimits_of_size CategoryTheory.CreatesColimitsOfSize
-/

#print CategoryTheory.CreatesColimits /-
/-- `F` creates small colimits if it creates colimits of shape `J` for any small `J`. -/
abbrev CreatesColimits (F : C ⥤ D) :=
  CreatesColimitsOfSize.{v₂, v₂} F
#align category_theory.creates_colimits CategoryTheory.CreatesColimits
-/

attribute [instance]
  creates_limits_of_shape.creates_limit creates_limits_of_size.creates_limits_of_shape creates_colimits_of_shape.creates_colimit creates_colimits_of_size.creates_colimits_of_shape

#print CategoryTheory.liftLimit /-
-- see Note [lower instance priority]
-- Interface to the `creates_limit` class.
/-- `lift_limit t` is the cone for `K` given by lifting the limit `t` for `K ⋙ F`. -/
def liftLimit {K : J ⥤ C} {F : C ⥤ D} [CreatesLimit K F] {c : Cone (K ⋙ F)} (t : IsLimit c) :
    Cone K :=
  (CreatesLimit.lifts c t).liftedCone
#align category_theory.lift_limit CategoryTheory.liftLimit
-/

/- warning: category_theory.lifted_limit_maps_to_original -> CategoryTheory.liftedLimitMapsToOriginal is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F] {c : CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)} (t : CategoryTheory.Limits.IsLimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) c), CategoryTheory.Iso.{u4, max u2 u6 u4} (CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F (CategoryTheory.liftLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F _inst_4 c t)) c
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F] {c : CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)} (t : CategoryTheory.Limits.IsLimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) c), CategoryTheory.Iso.{u4, max (max u6 u4) u2} (CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 F K (CategoryTheory.liftLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F _inst_4 c t)) c
Case conversion may be inaccurate. Consider using '#align category_theory.lifted_limit_maps_to_original CategoryTheory.liftedLimitMapsToOriginalₓ'. -/
/-- The lifted cone has an image isomorphic to the original cone. -/
def liftedLimitMapsToOriginal {K : J ⥤ C} {F : C ⥤ D} [CreatesLimit K F] {c : Cone (K ⋙ F)}
    (t : IsLimit c) : F.mapCone (liftLimit t) ≅ c :=
  (CreatesLimit.lifts c t).validLift
#align category_theory.lifted_limit_maps_to_original CategoryTheory.liftedLimitMapsToOriginal

#print CategoryTheory.liftedLimitIsLimit /-
/-- The lifted cone is a limit. -/
def liftedLimitIsLimit {K : J ⥤ C} {F : C ⥤ D} [CreatesLimit K F] {c : Cone (K ⋙ F)}
    (t : IsLimit c) : IsLimit (liftLimit t) :=
  ReflectsLimit.reflects (IsLimit.ofIsoLimit t (liftedLimitMapsToOriginal t).symm)
#align category_theory.lifted_limit_is_limit CategoryTheory.liftedLimitIsLimit
-/

#print CategoryTheory.hasLimit_of_created /-
/-- If `F` creates the limit of `K` and `K ⋙ F` has a limit, then `K` has a limit. -/
theorem hasLimit_of_created (K : J ⥤ C) (F : C ⥤ D) [HasLimit (K ⋙ F)] [CreatesLimit K F] :
    HasLimit K :=
  HasLimit.mk
    { Cone := liftLimit (limit.isLimit (K ⋙ F))
      IsLimit := liftedLimitIsLimit _ }
#align category_theory.has_limit_of_created CategoryTheory.hasLimit_of_created
-/

#print CategoryTheory.hasLimitsOfShape_of_hasLimitsOfShape_createsLimitsOfShape /-
/-- If `F` creates limits of shape `J`, and `D` has limits of shape `J`, then
`C` has limits of shape `J`.
-/
theorem hasLimitsOfShape_of_hasLimitsOfShape_createsLimitsOfShape (F : C ⥤ D) [HasLimitsOfShape J D]
    [CreatesLimitsOfShape J F] : HasLimitsOfShape J C :=
  ⟨fun G => hasLimit_of_created G F⟩
#align category_theory.has_limits_of_shape_of_has_limits_of_shape_creates_limits_of_shape CategoryTheory.hasLimitsOfShape_of_hasLimitsOfShape_createsLimitsOfShape
-/

#print CategoryTheory.has_limits_of_has_limits_creates_limits /-
/-- If `F` creates limits, and `D` has all limits, then `C` has all limits. -/
theorem has_limits_of_has_limits_creates_limits (F : C ⥤ D) [HasLimitsOfSize.{w, w'} D]
    [CreatesLimitsOfSize.{w, w'} F] : HasLimitsOfSize.{w, w'} C :=
  ⟨fun J I => has_limits_of_shape_of_has_limits_of_shape_creates_limits_of_shape F⟩
#align category_theory.has_limits_of_has_limits_creates_limits CategoryTheory.has_limits_of_has_limits_creates_limits
-/

#print CategoryTheory.liftColimit /-
-- Interface to the `creates_colimit` class.
/-- `lift_colimit t` is the cocone for `K` given by lifting the colimit `t` for `K ⋙ F`. -/
def liftColimit {K : J ⥤ C} {F : C ⥤ D} [CreatesColimit K F] {c : Cocone (K ⋙ F)}
    (t : IsColimit c) : Cocone K :=
  (CreatesColimit.lifts c t).liftedCocone
#align category_theory.lift_colimit CategoryTheory.liftColimit
-/

/- warning: category_theory.lifted_colimit_maps_to_original -> CategoryTheory.liftedColimitMapsToOriginal is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F] {c : CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)} (t : CategoryTheory.Limits.IsColimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) c), CategoryTheory.Iso.{u4, max u2 u6 u4} (CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cocone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCocone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F (CategoryTheory.liftColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F _inst_4 c t)) c
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F] {c : CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)} (t : CategoryTheory.Limits.IsColimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) c), CategoryTheory.Iso.{u4, max (max u6 u4) u2} (CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cocone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCocone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 F K (CategoryTheory.liftColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F _inst_4 c t)) c
Case conversion may be inaccurate. Consider using '#align category_theory.lifted_colimit_maps_to_original CategoryTheory.liftedColimitMapsToOriginalₓ'. -/
/-- The lifted cocone has an image isomorphic to the original cocone. -/
def liftedColimitMapsToOriginal {K : J ⥤ C} {F : C ⥤ D} [CreatesColimit K F] {c : Cocone (K ⋙ F)}
    (t : IsColimit c) : F.mapCocone (liftColimit t) ≅ c :=
  (CreatesColimit.lifts c t).validLift
#align category_theory.lifted_colimit_maps_to_original CategoryTheory.liftedColimitMapsToOriginal

#print CategoryTheory.liftedColimitIsColimit /-
/-- The lifted cocone is a colimit. -/
def liftedColimitIsColimit {K : J ⥤ C} {F : C ⥤ D} [CreatesColimit K F] {c : Cocone (K ⋙ F)}
    (t : IsColimit c) : IsColimit (liftColimit t) :=
  ReflectsColimit.reflects (IsColimit.ofIsoColimit t (liftedColimitMapsToOriginal t).symm)
#align category_theory.lifted_colimit_is_colimit CategoryTheory.liftedColimitIsColimit
-/

#print CategoryTheory.hasColimit_of_created /-
/-- If `F` creates the limit of `K` and `K ⋙ F` has a limit, then `K` has a limit. -/
theorem hasColimit_of_created (K : J ⥤ C) (F : C ⥤ D) [HasColimit (K ⋙ F)] [CreatesColimit K F] :
    HasColimit K :=
  HasColimit.mk
    { Cocone := liftColimit (colimit.isColimit (K ⋙ F))
      IsColimit := liftedColimitIsColimit _ }
#align category_theory.has_colimit_of_created CategoryTheory.hasColimit_of_created
-/

#print CategoryTheory.hasColimitsOfShape_of_hasColimitsOfShape_createsColimitsOfShape /-
/-- If `F` creates colimits of shape `J`, and `D` has colimits of shape `J`, then
`C` has colimits of shape `J`.
-/
theorem hasColimitsOfShape_of_hasColimitsOfShape_createsColimitsOfShape (F : C ⥤ D)
    [HasColimitsOfShape J D] [CreatesColimitsOfShape J F] : HasColimitsOfShape J C :=
  ⟨fun G => hasColimit_of_created G F⟩
#align category_theory.has_colimits_of_shape_of_has_colimits_of_shape_creates_colimits_of_shape CategoryTheory.hasColimitsOfShape_of_hasColimitsOfShape_createsColimitsOfShape
-/

#print CategoryTheory.has_colimits_of_has_colimits_creates_colimits /-
/-- If `F` creates colimits, and `D` has all colimits, then `C` has all colimits. -/
theorem has_colimits_of_has_colimits_creates_colimits (F : C ⥤ D) [HasColimitsOfSize.{w, w'} D]
    [CreatesColimitsOfSize.{w, w'} F] : HasColimitsOfSize.{w, w'} C :=
  ⟨fun J I => has_colimits_of_shape_of_has_colimits_of_shape_creates_colimits_of_shape F⟩
#align category_theory.has_colimits_of_has_colimits_creates_colimits CategoryTheory.has_colimits_of_has_colimits_creates_colimits
-/

#print CategoryTheory.reflectsLimitsOfShapeOfCreatesLimitsOfShape /-
instance (priority := 10) reflectsLimitsOfShapeOfCreatesLimitsOfShape (F : C ⥤ D)
    [CreatesLimitsOfShape J F] : ReflectsLimitsOfShape J F where
#align category_theory.reflects_limits_of_shape_of_creates_limits_of_shape CategoryTheory.reflectsLimitsOfShapeOfCreatesLimitsOfShape
-/

#print CategoryTheory.reflectsLimitsOfCreatesLimits /-
instance (priority := 10) reflectsLimitsOfCreatesLimits (F : C ⥤ D)
    [CreatesLimitsOfSize.{w, w'} F] : ReflectsLimitsOfSize.{w, w'} F where
#align category_theory.reflects_limits_of_creates_limits CategoryTheory.reflectsLimitsOfCreatesLimits
-/

#print CategoryTheory.reflectsColimitsOfShapeOfCreatesColimitsOfShape /-
instance (priority := 10) reflectsColimitsOfShapeOfCreatesColimitsOfShape (F : C ⥤ D)
    [CreatesColimitsOfShape J F] : ReflectsColimitsOfShape J F where
#align category_theory.reflects_colimits_of_shape_of_creates_colimits_of_shape CategoryTheory.reflectsColimitsOfShapeOfCreatesColimitsOfShape
-/

#print CategoryTheory.reflectsColimitsOfCreatesColimits /-
instance (priority := 10) reflectsColimitsOfCreatesColimits (F : C ⥤ D)
    [CreatesColimitsOfSize.{w, w'} F] : ReflectsColimitsOfSize.{w, w'} F where
#align category_theory.reflects_colimits_of_creates_colimits CategoryTheory.reflectsColimitsOfCreatesColimits
-/

#print CategoryTheory.LiftsToLimit /-
/-- A helper to show a functor creates limits. In particular, if we can show
that for any limit cone `c` for `K ⋙ F`, there is a lift of it which is
a limit and `F` reflects isomorphisms, then `F` creates limits.
Usually, `F` creating limits says that _any_ lift of `c` is a limit, but
here we only need to show that our particular lift of `c` is a limit.
-/
structure LiftsToLimit (K : J ⥤ C) (F : C ⥤ D) (c : Cone (K ⋙ F)) (t : IsLimit c) extends
  LiftableCone K F c where
  makesLimit : IsLimit lifted_cone
#align category_theory.lifts_to_limit CategoryTheory.LiftsToLimit
-/

#print CategoryTheory.LiftsToColimit /-
/-- A helper to show a functor creates colimits. In particular, if we can show
that for any limit cocone `c` for `K ⋙ F`, there is a lift of it which is
a limit and `F` reflects isomorphisms, then `F` creates colimits.
Usually, `F` creating colimits says that _any_ lift of `c` is a colimit, but
here we only need to show that our particular lift of `c` is a colimit.
-/
structure LiftsToColimit (K : J ⥤ C) (F : C ⥤ D) (c : Cocone (K ⋙ F)) (t : IsColimit c) extends
  LiftableCocone K F c where
  makesColimit : IsColimit lifted_cocone
#align category_theory.lifts_to_colimit CategoryTheory.LiftsToColimit
-/

#print CategoryTheory.createsLimitOfReflectsIso /-
/-- If `F` reflects isomorphisms and we can lift any limit cone to a limit cone,
then `F` creates limits.
In particular here we don't need to assume that F reflects limits.
-/
def createsLimitOfReflectsIso {K : J ⥤ C} {F : C ⥤ D} [ReflectsIsomorphisms F]
    (h : ∀ c t, LiftsToLimit K F c t) : CreatesLimit K F
    where
  lifts c t := (h c t).toLiftableCone
  toReflectsLimit :=
    {
      reflects := fun (d : Cone K) (hd : IsLimit (F.mapCone d)) =>
        by
        let d' : cone K := (h (F.map_cone d) hd).toLiftableCone.liftedCone
        let i : F.map_cone d' ≅ F.map_cone d := (h (F.map_cone d) hd).toLiftableCone.validLift
        let hd' : is_limit d' := (h (F.map_cone d) hd).makesLimit
        let f : d ⟶ d' := hd'.lift_cone_morphism d
        have : (cones.functoriality K F).map f = i.inv :=
          (hd.of_iso_limit i.symm).uniq_cone_morphism
        haveI : is_iso ((cones.functoriality K F).map f) :=
          by
          rw [this]
          infer_instance
        haveI : is_iso f := is_iso_of_reflects_iso f (cones.functoriality K F)
        exact is_limit.of_iso_limit hd' (as_iso f).symm }
#align category_theory.creates_limit_of_reflects_iso CategoryTheory.createsLimitOfReflectsIso
-/

/- warning: category_theory.creates_limit_of_fully_faithful_of_lift' -> CategoryTheory.createsLimitOfFullyFaithfulOfLift' is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] {l : CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)}, (CategoryTheory.Limits.IsLimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l) -> (forall (c : CategoryTheory.Limits.Cone.{u1, u3, u2, u5} J _inst_3 C _inst_1 K), (CategoryTheory.Iso.{u4, max u2 u6 u4} (CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F c) l) -> (CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F))
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] {l : CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)}, (CategoryTheory.Limits.IsLimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l) -> (forall (c : CategoryTheory.Limits.Cone.{u1, u3, u2, u5} J _inst_3 C _inst_1 K), (CategoryTheory.Iso.{u4, max (max u6 u4) u2} (CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 F K c) l) -> (CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F))
Case conversion may be inaccurate. Consider using '#align category_theory.creates_limit_of_fully_faithful_of_lift' CategoryTheory.createsLimitOfFullyFaithfulOfLift'ₓ'. -/
-- Notice however that even if the isomorphism is `iso.refl _`,
-- this construction will insert additional identity morphisms in the cone maps,
-- so the constructed limits may not be ideal, definitionally.
/--
When `F` is fully faithful, to show that `F` creates the limit for `K` it suffices to exhibit a lift
of a limit cone for `K ⋙ F`.
-/
def createsLimitOfFullyFaithfulOfLift' {K : J ⥤ C} {F : C ⥤ D} [Full F] [Faithful F]
    {l : Cone (K ⋙ F)} (hl : IsLimit l) (c : Cone K) (i : F.mapCone c ≅ l) : CreatesLimit K F :=
  createsLimitOfReflectsIso fun c' t =>
    { liftedCone := c
      validLift := i ≪≫ IsLimit.uniqueUpToIso hl t
      makesLimit :=
        IsLimit.ofFaithful F (IsLimit.ofIsoLimit hl i.symm) _ fun s => F.image_preimage _ }
#align category_theory.creates_limit_of_fully_faithful_of_lift' CategoryTheory.createsLimitOfFullyFaithfulOfLift'

/- warning: category_theory.creates_limit_of_fully_faithful_of_lift -> CategoryTheory.createsLimitOfFullyFaithfulOfLift is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_6 : CategoryTheory.Limits.HasLimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)] (c : CategoryTheory.Limits.Cone.{u1, u3, u2, u5} J _inst_3 C _inst_1 K), (CategoryTheory.Iso.{u4, max u2 u6 u4} (CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F c) (CategoryTheory.Limits.limit.cone.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) _inst_6)) -> (CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F)
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_6 : CategoryTheory.Limits.HasLimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)] (c : CategoryTheory.Limits.Cone.{u1, u3, u2, u5} J _inst_3 C _inst_1 K), (CategoryTheory.Iso.{u4, max (max u6 u4) u2} (CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 F K c) (CategoryTheory.Limits.limit.cone.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) _inst_6)) -> (CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F)
Case conversion may be inaccurate. Consider using '#align category_theory.creates_limit_of_fully_faithful_of_lift CategoryTheory.createsLimitOfFullyFaithfulOfLiftₓ'. -/
-- Notice however that even if the isomorphism is `iso.refl _`,
-- this construction will insert additional identity morphisms in the cone maps,
-- so the constructed limits may not be ideal, definitionally.
/-- When `F` is fully faithful, and `has_limit (K ⋙ F)`, to show that `F` creates the limit for `K`
it suffices to exhibit a lift of the chosen limit cone for `K ⋙ F`.
-/
def createsLimitOfFullyFaithfulOfLift {K : J ⥤ C} {F : C ⥤ D} [Full F] [Faithful F]
    [HasLimit (K ⋙ F)] (c : Cone K) (i : F.mapCone c ≅ limit.cone (K ⋙ F)) : CreatesLimit K F :=
  createsLimitOfFullyFaithfulOfLift' (limit.isLimit _) c i
#align category_theory.creates_limit_of_fully_faithful_of_lift CategoryTheory.createsLimitOfFullyFaithfulOfLift

/- warning: category_theory.creates_limit_of_fully_faithful_of_iso' -> CategoryTheory.createsLimitOfFullyFaithfulOfIso' is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] {l : CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)}, (CategoryTheory.Limits.IsLimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l) -> (forall (X : C), (CategoryTheory.Iso.{u4, u6} D _inst_2 (CategoryTheory.Functor.obj.{u3, u4, u5, u6} C _inst_1 D _inst_2 F X) (CategoryTheory.Limits.Cone.pt.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l)) -> (CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F))
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] {l : CategoryTheory.Limits.Cone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)}, (CategoryTheory.Limits.IsLimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l) -> (forall (X : C), (CategoryTheory.Iso.{u4, u6} D _inst_2 (Prefunctor.obj.{succ u3, succ u4, u5, u6} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} C (CategoryTheory.Category.toCategoryStruct.{u3, u5} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u4, u6} D (CategoryTheory.Category.toCategoryStruct.{u4, u6} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u3, u4, u5, u6} C _inst_1 D _inst_2 F) X) (CategoryTheory.Limits.Cone.pt.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l)) -> (CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F))
Case conversion may be inaccurate. Consider using '#align category_theory.creates_limit_of_fully_faithful_of_iso' CategoryTheory.createsLimitOfFullyFaithfulOfIso'ₓ'. -/
-- Notice however that even if the isomorphism is `iso.refl _`,
-- this construction will insert additional identity morphisms in the cone maps,
-- so the constructed limits may not be ideal, definitionally.
/--
When `F` is fully faithful, to show that `F` creates the limit for `K` it suffices to show that a
limit point is in the essential image of `F`.
-/
def createsLimitOfFullyFaithfulOfIso' {K : J ⥤ C} {F : C ⥤ D} [Full F] [Faithful F]
    {l : Cone (K ⋙ F)} (hl : IsLimit l) (X : C) (i : F.obj X ≅ l.pt) : CreatesLimit K F :=
  createsLimitOfFullyFaithfulOfLift' hl
    { pt
      π :=
        { app := fun j => F.preimage (i.Hom ≫ l.π.app j)
          naturality' := fun Y Z f =>
            F.map_injective <| by
              dsimp
              simpa using (l.w f).symm } }
    (Cones.ext i fun j => by simp only [functor.image_preimage, functor.map_cone_π_app])
#align category_theory.creates_limit_of_fully_faithful_of_iso' CategoryTheory.createsLimitOfFullyFaithfulOfIso'

/- warning: category_theory.creates_limit_of_fully_faithful_of_iso -> CategoryTheory.createsLimitOfFullyFaithfulOfIso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_6 : CategoryTheory.Limits.HasLimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)] (X : C), (CategoryTheory.Iso.{u4, u6} D _inst_2 (CategoryTheory.Functor.obj.{u3, u4, u5, u6} C _inst_1 D _inst_2 F X) (CategoryTheory.Limits.limit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) _inst_6)) -> (CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F)
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_6 : CategoryTheory.Limits.HasLimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)] (X : C), (CategoryTheory.Iso.{u4, u6} D _inst_2 (Prefunctor.obj.{succ u3, succ u4, u5, u6} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} C (CategoryTheory.Category.toCategoryStruct.{u3, u5} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u4, u6} D (CategoryTheory.Category.toCategoryStruct.{u4, u6} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u3, u4, u5, u6} C _inst_1 D _inst_2 F) X) (CategoryTheory.Limits.limit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) _inst_6)) -> (CategoryTheory.CreatesLimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F)
Case conversion may be inaccurate. Consider using '#align category_theory.creates_limit_of_fully_faithful_of_iso CategoryTheory.createsLimitOfFullyFaithfulOfIsoₓ'. -/
-- Notice however that even if the isomorphism is `iso.refl _`,
-- this construction will insert additional identity morphisms in the cone maps,
-- so the constructed limits may not be ideal, definitionally.
/-- When `F` is fully faithful, and `has_limit (K ⋙ F)`, to show that `F` creates the limit for `K`
it suffices to show that the chosen limit point is in the essential image of `F`.
-/
def createsLimitOfFullyFaithfulOfIso {K : J ⥤ C} {F : C ⥤ D} [Full F] [Faithful F]
    [HasLimit (K ⋙ F)] (X : C) (i : F.obj X ≅ limit (K ⋙ F)) : CreatesLimit K F :=
  createsLimitOfFullyFaithfulOfIso' (limit.isLimit _) X i
#align category_theory.creates_limit_of_fully_faithful_of_iso CategoryTheory.createsLimitOfFullyFaithfulOfIso

#print CategoryTheory.preservesLimitOfCreatesLimitAndHasLimit /-
-- see Note [lower instance priority]
/-- `F` preserves the limit of `K` if it creates the limit and `K ⋙ F` has the limit. -/
instance (priority := 100) preservesLimitOfCreatesLimitAndHasLimit (K : J ⥤ C) (F : C ⥤ D)
    [CreatesLimit K F] [HasLimit (K ⋙ F)] : PreservesLimit K F
    where preserves c t :=
    IsLimit.ofIsoLimit (limit.isLimit _)
      ((liftedLimitMapsToOriginal (limit.isLimit _)).symm ≪≫
        (Cones.functoriality K F).mapIso ((liftedLimitIsLimit (limit.isLimit _)).uniqueUpToIso t))
#align category_theory.preserves_limit_of_creates_limit_and_has_limit CategoryTheory.preservesLimitOfCreatesLimitAndHasLimit
-/

#print CategoryTheory.preservesLimitOfShapeOfCreatesLimitsOfShapeAndHasLimitsOfShape /-
-- see Note [lower instance priority]
/-- `F` preserves the limit of shape `J` if it creates these limits and `D` has them. -/
instance (priority := 100) preservesLimitOfShapeOfCreatesLimitsOfShapeAndHasLimitsOfShape
    (F : C ⥤ D) [CreatesLimitsOfShape J F] [HasLimitsOfShape J D] : PreservesLimitsOfShape J F where
#align category_theory.preserves_limit_of_shape_of_creates_limits_of_shape_and_has_limits_of_shape CategoryTheory.preservesLimitOfShapeOfCreatesLimitsOfShapeAndHasLimitsOfShape
-/

#print CategoryTheory.preservesLimitsOfCreatesLimitsAndHasLimits /-
-- see Note [lower instance priority]
/-- `F` preserves limits if it creates limits and `D` has limits. -/
instance (priority := 100) preservesLimitsOfCreatesLimitsAndHasLimits (F : C ⥤ D)
    [CreatesLimitsOfSize.{w, w'} F] [HasLimitsOfSize.{w, w'} D] : PreservesLimitsOfSize.{w, w'} F
    where
#align category_theory.preserves_limits_of_creates_limits_and_has_limits CategoryTheory.preservesLimitsOfCreatesLimitsAndHasLimits
-/

#print CategoryTheory.createsColimitOfReflectsIso /-
/-- If `F` reflects isomorphisms and we can lift any colimit cocone to a colimit cocone,
then `F` creates colimits.
In particular here we don't need to assume that F reflects colimits.
-/
def createsColimitOfReflectsIso {K : J ⥤ C} {F : C ⥤ D} [ReflectsIsomorphisms F]
    (h : ∀ c t, LiftsToColimit K F c t) : CreatesColimit K F
    where
  lifts c t := (h c t).toLiftableCocone
  toReflectsColimit :=
    {
      reflects := fun (d : Cocone K) (hd : IsColimit (F.mapCocone d)) =>
        by
        let d' : cocone K := (h (F.map_cocone d) hd).toLiftableCocone.liftedCocone
        let i : F.map_cocone d' ≅ F.map_cocone d :=
          (h (F.map_cocone d) hd).toLiftableCocone.validLift
        let hd' : is_colimit d' := (h (F.map_cocone d) hd).makesColimit
        let f : d' ⟶ d := hd'.desc_cocone_morphism d
        have : (cocones.functoriality K F).map f = i.hom :=
          (hd.of_iso_colimit i.symm).uniq_cocone_morphism
        haveI : is_iso ((cocones.functoriality K F).map f) :=
          by
          rw [this]
          infer_instance
        haveI := is_iso_of_reflects_iso f (cocones.functoriality K F)
        exact is_colimit.of_iso_colimit hd' (as_iso f) }
#align category_theory.creates_colimit_of_reflects_iso CategoryTheory.createsColimitOfReflectsIso
-/

/- warning: category_theory.creates_colimit_of_fully_faithful_of_lift' -> CategoryTheory.createsColimitOfFullyFaithfulOfLift' is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] {l : CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)}, (CategoryTheory.Limits.IsColimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l) -> (forall (c : CategoryTheory.Limits.Cocone.{u1, u3, u2, u5} J _inst_3 C _inst_1 K), (CategoryTheory.Iso.{u4, max u2 u6 u4} (CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cocone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCocone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F c) l) -> (CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F))
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] {l : CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)}, (CategoryTheory.Limits.IsColimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l) -> (forall (c : CategoryTheory.Limits.Cocone.{u1, u3, u2, u5} J _inst_3 C _inst_1 K), (CategoryTheory.Iso.{u4, max (max u6 u4) u2} (CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cocone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCocone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 F K c) l) -> (CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F))
Case conversion may be inaccurate. Consider using '#align category_theory.creates_colimit_of_fully_faithful_of_lift' CategoryTheory.createsColimitOfFullyFaithfulOfLift'ₓ'. -/
-- Notice however that even if the isomorphism is `iso.refl _`,
-- this construction will insert additional identity morphisms in the cocone maps,
-- so the constructed colimits may not be ideal, definitionally.
/--
When `F` is fully faithful, to show that `F` creates the colimit for `K` it suffices to exhibit a
lift of a colimit cocone for `K ⋙ F`.
-/
def createsColimitOfFullyFaithfulOfLift' {K : J ⥤ C} {F : C ⥤ D} [Full F] [Faithful F]
    {l : Cocone (K ⋙ F)} (hl : IsColimit l) (c : Cocone K) (i : F.mapCocone c ≅ l) :
    CreatesColimit K F :=
  createsColimitOfReflectsIso fun c' t =>
    { liftedCocone := c
      validLift := i ≪≫ IsColimit.uniqueUpToIso hl t
      makesColimit :=
        IsColimit.ofFaithful F (IsColimit.ofIsoColimit hl i.symm) _ fun s => F.image_preimage _ }
#align category_theory.creates_colimit_of_fully_faithful_of_lift' CategoryTheory.createsColimitOfFullyFaithfulOfLift'

/- warning: category_theory.creates_colimit_of_fully_faithful_of_lift -> CategoryTheory.createsColimitOfFullyFaithfulOfLift is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_6 : CategoryTheory.Limits.HasColimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)] (c : CategoryTheory.Limits.Cocone.{u1, u3, u2, u5} J _inst_3 C _inst_1 K), (CategoryTheory.Iso.{u4, max u2 u6 u4} (CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cocone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCocone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F c) (CategoryTheory.Limits.colimit.cocone.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) _inst_6)) -> (CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F)
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_6 : CategoryTheory.Limits.HasColimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)] (c : CategoryTheory.Limits.Cocone.{u1, u3, u2, u5} J _inst_3 C _inst_1 K), (CategoryTheory.Iso.{u4, max (max u6 u4) u2} (CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Limits.Cocone.category.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)) (CategoryTheory.Functor.mapCocone.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 F K c) (CategoryTheory.Limits.colimit.cocone.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) _inst_6)) -> (CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F)
Case conversion may be inaccurate. Consider using '#align category_theory.creates_colimit_of_fully_faithful_of_lift CategoryTheory.createsColimitOfFullyFaithfulOfLiftₓ'. -/
-- Notice however that even if the isomorphism is `iso.refl _`,
-- this construction will insert additional identity morphisms in the cocone maps,
-- so the constructed colimits may not be ideal, definitionally.
/--
When `F` is fully faithful, and `has_colimit (K ⋙ F)`, to show that `F` creates the colimit for `K`
it suffices to exhibit a lift of the chosen colimit cocone for `K ⋙ F`.
-/
def createsColimitOfFullyFaithfulOfLift {K : J ⥤ C} {F : C ⥤ D} [Full F] [Faithful F]
    [HasColimit (K ⋙ F)] (c : Cocone K) (i : F.mapCocone c ≅ colimit.cocone (K ⋙ F)) :
    CreatesColimit K F :=
  createsColimitOfFullyFaithfulOfLift' (colimit.isColimit _) c i
#align category_theory.creates_colimit_of_fully_faithful_of_lift CategoryTheory.createsColimitOfFullyFaithfulOfLift

/- warning: category_theory.creates_colimit_of_fully_faithful_of_iso' -> CategoryTheory.createsColimitOfFullyFaithfulOfIso' is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] {l : CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)}, (CategoryTheory.Limits.IsColimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l) -> (forall (X : C), (CategoryTheory.Iso.{u4, u6} D _inst_2 (CategoryTheory.Functor.obj.{u3, u4, u5, u6} C _inst_1 D _inst_2 F X) (CategoryTheory.Limits.Cocone.pt.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l)) -> (CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F))
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] {l : CategoryTheory.Limits.Cocone.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)}, (CategoryTheory.Limits.IsColimit.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l) -> (forall (X : C), (CategoryTheory.Iso.{u4, u6} D _inst_2 (Prefunctor.obj.{succ u3, succ u4, u5, u6} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} C (CategoryTheory.Category.toCategoryStruct.{u3, u5} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u4, u6} D (CategoryTheory.Category.toCategoryStruct.{u4, u6} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u3, u4, u5, u6} C _inst_1 D _inst_2 F) X) (CategoryTheory.Limits.Cocone.pt.{u1, u4, u2, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) l)) -> (CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F))
Case conversion may be inaccurate. Consider using '#align category_theory.creates_colimit_of_fully_faithful_of_iso' CategoryTheory.createsColimitOfFullyFaithfulOfIso'ₓ'. -/
-- Notice however that even if the isomorphism is `iso.refl _`,
-- this construction will insert additional identity morphisms in the cocone maps,
-- so the constructed colimits may not be ideal, definitionally.
/--
When `F` is fully faithful, to show that `F` creates the colimit for `K` it suffices to show that
a colimit point is in the essential image of `F`.
-/
def createsColimitOfFullyFaithfulOfIso' {K : J ⥤ C} {F : C ⥤ D} [Full F] [Faithful F]
    {l : Cocone (K ⋙ F)} (hl : IsColimit l) (X : C) (i : F.obj X ≅ l.pt) : CreatesColimit K F :=
  createsColimitOfFullyFaithfulOfLift' hl
    { pt
      ι :=
        { app := fun j => F.preimage (l.ι.app j ≫ i.inv)
          naturality' := fun Y Z f =>
            F.map_injective <| by
              dsimp
              simpa [← cancel_mono i.hom] using l.w f } }
    (Cocones.ext i fun j => by simp)
#align category_theory.creates_colimit_of_fully_faithful_of_iso' CategoryTheory.createsColimitOfFullyFaithfulOfIso'

/- warning: category_theory.creates_colimit_of_fully_faithful_of_iso -> CategoryTheory.createsColimitOfFullyFaithfulOfIso is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_6 : CategoryTheory.Limits.HasColimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)] (X : C), (CategoryTheory.Iso.{u4, u6} D _inst_2 (CategoryTheory.Functor.obj.{u3, u4, u5, u6} C _inst_1 D _inst_2 F X) (CategoryTheory.Limits.colimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) _inst_6)) -> (CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F)
but is expected to have type
  forall {C : Type.{u5}} [_inst_1 : CategoryTheory.Category.{u3, u5} C] {D : Type.{u6}} [_inst_2 : CategoryTheory.Category.{u4, u6} D] {J : Type.{u2}} [_inst_3 : CategoryTheory.Category.{u1, u2} J] {K : CategoryTheory.Functor.{u1, u3, u2, u5} J _inst_3 C _inst_1} {F : CategoryTheory.Functor.{u3, u4, u5, u6} C _inst_1 D _inst_2} [_inst_4 : CategoryTheory.Full.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u3, u4, u5, u6} C _inst_1 D _inst_2 F] [_inst_6 : CategoryTheory.Limits.HasColimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F)] (X : C), (CategoryTheory.Iso.{u4, u6} D _inst_2 (Prefunctor.obj.{succ u3, succ u4, u5, u6} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u5} C (CategoryTheory.Category.toCategoryStruct.{u3, u5} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u4, u6} D (CategoryTheory.Category.toCategoryStruct.{u4, u6} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u3, u4, u5, u6} C _inst_1 D _inst_2 F) X) (CategoryTheory.Limits.colimit.{u1, u2, u4, u6} J _inst_3 D _inst_2 (CategoryTheory.Functor.comp.{u1, u3, u4, u2, u5, u6} J _inst_3 C _inst_1 D _inst_2 K F) _inst_6)) -> (CategoryTheory.CreatesColimit.{u1, u2, u3, u4, u5, u6} C _inst_1 D _inst_2 J _inst_3 K F)
Case conversion may be inaccurate. Consider using '#align category_theory.creates_colimit_of_fully_faithful_of_iso CategoryTheory.createsColimitOfFullyFaithfulOfIsoₓ'. -/
-- Notice however that even if the isomorphism is `iso.refl _`,
-- this construction will insert additional identity morphisms in the cocone maps,
-- so the constructed colimits may not be ideal, definitionally.
/--
When `F` is fully faithful, and `has_colimit (K ⋙ F)`, to show that `F` creates the colimit for `K`
it suffices to show that the chosen colimit point is in the essential image of `F`.
-/
def createsColimitOfFullyFaithfulOfIso {K : J ⥤ C} {F : C ⥤ D} [Full F] [Faithful F]
    [HasColimit (K ⋙ F)] (X : C) (i : F.obj X ≅ colimit (K ⋙ F)) : CreatesColimit K F :=
  createsColimitOfFullyFaithfulOfIso' (colimit.isColimit _) X i
#align category_theory.creates_colimit_of_fully_faithful_of_iso CategoryTheory.createsColimitOfFullyFaithfulOfIso

#print CategoryTheory.preservesColimitOfCreatesColimitAndHasColimit /-
-- see Note [lower instance priority]
/-- `F` preserves the colimit of `K` if it creates the colimit and `K ⋙ F` has the colimit. -/
instance (priority := 100) preservesColimitOfCreatesColimitAndHasColimit (K : J ⥤ C) (F : C ⥤ D)
    [CreatesColimit K F] [HasColimit (K ⋙ F)] : PreservesColimit K F
    where preserves c t :=
    IsColimit.ofIsoColimit (colimit.isColimit _)
      ((liftedColimitMapsToOriginal (colimit.isColimit _)).symm ≪≫
        (Cocones.functoriality K F).mapIso
          ((liftedColimitIsColimit (colimit.isColimit _)).uniqueUpToIso t))
#align category_theory.preserves_colimit_of_creates_colimit_and_has_colimit CategoryTheory.preservesColimitOfCreatesColimitAndHasColimit
-/

#print CategoryTheory.preservesColimitOfShapeOfCreatesColimitsOfShapeAndHasColimitsOfShape /-
-- see Note [lower instance priority]
/-- `F` preserves the colimit of shape `J` if it creates these colimits and `D` has them. -/
instance (priority := 100) preservesColimitOfShapeOfCreatesColimitsOfShapeAndHasColimitsOfShape
    (F : C ⥤ D) [CreatesColimitsOfShape J F] [HasColimitsOfShape J D] : PreservesColimitsOfShape J F
    where
#align category_theory.preserves_colimit_of_shape_of_creates_colimits_of_shape_and_has_colimits_of_shape CategoryTheory.preservesColimitOfShapeOfCreatesColimitsOfShapeAndHasColimitsOfShape
-/

#print CategoryTheory.preservesColimitsOfCreatesColimitsAndHasColimits /-
-- see Note [lower instance priority]
/-- `F` preserves limits if it creates limits and `D` has limits. -/
instance (priority := 100) preservesColimitsOfCreatesColimitsAndHasColimits (F : C ⥤ D)
    [CreatesColimitsOfSize.{w, w'} F] [HasColimitsOfSize.{w, w'} D] :
    PreservesColimitsOfSize.{w, w'} F where
#align category_theory.preserves_colimits_of_creates_colimits_and_has_colimits CategoryTheory.preservesColimitsOfCreatesColimitsAndHasColimits
-/

#print CategoryTheory.createsLimitOfIsoDiagram /-
/-- Transfer creation of limits along a natural isomorphism in the diagram. -/
def createsLimitOfIsoDiagram {K₁ K₂ : J ⥤ C} (F : C ⥤ D) (h : K₁ ≅ K₂) [CreatesLimit K₁ F] :
    CreatesLimit K₂ F :=
  { reflectsLimitOfIsoDiagram F h with
    lifts := fun c t =>
      let t' := (IsLimit.postcomposeInvEquiv (isoWhiskerRight h F : _) c).symm t
      { liftedCone := (Cones.postcompose h.Hom).obj (liftLimit t')
        validLift :=
          F.mapConePostcompose ≪≫
            (Cones.postcompose (isoWhiskerRight h F).Hom).mapIso (liftedLimitMapsToOriginal t') ≪≫
              Cones.ext (Iso.refl _) fun j => by
                dsimp
                rw [category.assoc, ← F.map_comp]
                simp } }
#align category_theory.creates_limit_of_iso_diagram CategoryTheory.createsLimitOfIsoDiagram
-/

#print CategoryTheory.createsLimitOfNatIso /-
/-- If `F` creates the limit of `K` and `F ≅ G`, then `G` creates the limit of `K`. -/
def createsLimitOfNatIso {F G : C ⥤ D} (h : F ≅ G) [CreatesLimit K F] : CreatesLimit K G
    where
  lifts c t :=
    { liftedCone := liftLimit ((IsLimit.postcomposeInvEquiv (isoWhiskerLeft K h : _) c).symm t)
      validLift := by
        refine' (is_limit.map_cone_equiv h _).uniqueUpToIso t
        apply is_limit.of_iso_limit _ (lifted_limit_maps_to_original _).symm
        apply (is_limit.postcompose_inv_equiv _ _).symm t }
  toReflectsLimit := reflectsLimitOfNatIso _ h
#align category_theory.creates_limit_of_nat_iso CategoryTheory.createsLimitOfNatIso
-/

#print CategoryTheory.createsLimitsOfShapeOfNatIso /-
/-- If `F` creates limits of shape `J` and `F ≅ G`, then `G` creates limits of shape `J`. -/
def createsLimitsOfShapeOfNatIso {F G : C ⥤ D} (h : F ≅ G) [CreatesLimitsOfShape J F] :
    CreatesLimitsOfShape J G where CreatesLimit K := createsLimitOfNatIso h
#align category_theory.creates_limits_of_shape_of_nat_iso CategoryTheory.createsLimitsOfShapeOfNatIso
-/

#print CategoryTheory.createsLimitsOfNatIso /-
/-- If `F` creates limits and `F ≅ G`, then `G` creates limits. -/
def createsLimitsOfNatIso {F G : C ⥤ D} (h : F ≅ G) [CreatesLimitsOfSize.{w, w'} F] :
    CreatesLimitsOfSize.{w, w'} G
    where CreatesLimitsOfShape J 𝒥₁ := creates_limits_of_shape_of_nat_iso h
#align category_theory.creates_limits_of_nat_iso CategoryTheory.createsLimitsOfNatIso
-/

#print CategoryTheory.createsColimitOfIsoDiagram /-
/-- Transfer creation of colimits along a natural isomorphism in the diagram. -/
def createsColimitOfIsoDiagram {K₁ K₂ : J ⥤ C} (F : C ⥤ D) (h : K₁ ≅ K₂) [CreatesColimit K₁ F] :
    CreatesColimit K₂ F :=
  { reflectsColimitOfIsoDiagram F h with
    lifts := fun c t =>
      let t' := (IsColimit.precomposeHomEquiv (isoWhiskerRight h F : _) c).symm t
      { liftedCocone := (Cocones.precompose h.inv).obj (liftColimit t')
        validLift :=
          F.mapCoconePrecompose ≪≫
            (Cocones.precompose (isoWhiskerRight h F).inv).mapIso
                (liftedColimitMapsToOriginal t') ≪≫
              Cocones.ext (Iso.refl _) fun j => by
                dsimp
                rw [← F.map_comp_assoc]
                simp } }
#align category_theory.creates_colimit_of_iso_diagram CategoryTheory.createsColimitOfIsoDiagram
-/

#print CategoryTheory.createsColimitOfNatIso /-
/-- If `F` creates the colimit of `K` and `F ≅ G`, then `G` creates the colimit of `K`. -/
def createsColimitOfNatIso {F G : C ⥤ D} (h : F ≅ G) [CreatesColimit K F] : CreatesColimit K G
    where
  lifts c t :=
    { liftedCocone := liftColimit ((IsColimit.precomposeHomEquiv (isoWhiskerLeft K h : _) c).symm t)
      validLift := by
        refine' (is_colimit.map_cocone_equiv h _).uniqueUpToIso t
        apply is_colimit.of_iso_colimit _ (lifted_colimit_maps_to_original _).symm
        apply (is_colimit.precompose_hom_equiv _ _).symm t }
  toReflectsColimit := reflectsColimitOfNatIso _ h
#align category_theory.creates_colimit_of_nat_iso CategoryTheory.createsColimitOfNatIso
-/

#print CategoryTheory.createsColimitsOfShapeOfNatIso /-
/-- If `F` creates colimits of shape `J` and `F ≅ G`, then `G` creates colimits of shape `J`. -/
def createsColimitsOfShapeOfNatIso {F G : C ⥤ D} (h : F ≅ G) [CreatesColimitsOfShape J F] :
    CreatesColimitsOfShape J G where CreatesColimit K := createsColimitOfNatIso h
#align category_theory.creates_colimits_of_shape_of_nat_iso CategoryTheory.createsColimitsOfShapeOfNatIso
-/

#print CategoryTheory.createsColimitsOfNatIso /-
/-- If `F` creates colimits and `F ≅ G`, then `G` creates colimits. -/
def createsColimitsOfNatIso {F G : C ⥤ D} (h : F ≅ G) [CreatesColimitsOfSize.{w, w'} F] :
    CreatesColimitsOfSize.{w, w'} G
    where CreatesColimitsOfShape J 𝒥₁ := creates_colimits_of_shape_of_nat_iso h
#align category_theory.creates_colimits_of_nat_iso CategoryTheory.createsColimitsOfNatIso
-/

#print CategoryTheory.liftsToLimitOfCreates /-
-- For the inhabited linter later.
/-- If F creates the limit of K, any cone lifts to a limit. -/
def liftsToLimitOfCreates (K : J ⥤ C) (F : C ⥤ D) [CreatesLimit K F] (c : Cone (K ⋙ F))
    (t : IsLimit c) : LiftsToLimit K F c t
    where
  liftedCone := liftLimit t
  validLift := liftedLimitMapsToOriginal t
  makesLimit := liftedLimitIsLimit t
#align category_theory.lifts_to_limit_of_creates CategoryTheory.liftsToLimitOfCreates
-/

#print CategoryTheory.liftsToColimitOfCreates /-
-- For the inhabited linter later.
/-- If F creates the colimit of K, any cocone lifts to a colimit. -/
def liftsToColimitOfCreates (K : J ⥤ C) (F : C ⥤ D) [CreatesColimit K F] (c : Cocone (K ⋙ F))
    (t : IsColimit c) : LiftsToColimit K F c t
    where
  liftedCocone := liftColimit t
  validLift := liftedColimitMapsToOriginal t
  makesColimit := liftedColimitIsColimit t
#align category_theory.lifts_to_colimit_of_creates CategoryTheory.liftsToColimitOfCreates
-/

#print CategoryTheory.idLiftsCone /-
/-- Any cone lifts through the identity functor. -/
def idLiftsCone (c : Cone (K ⋙ 𝟭 C)) : LiftableCone K (𝟭 C) c
    where
  liftedCone :=
    { pt := c.pt
      π := c.π ≫ K.rightUnitor.Hom }
  validLift := Cones.ext (Iso.refl _) (by tidy)
#align category_theory.id_lifts_cone CategoryTheory.idLiftsCone
-/

#print CategoryTheory.idCreatesLimits /-
/-- The identity functor creates all limits. -/
instance idCreatesLimits : CreatesLimitsOfSize.{w, w'} (𝟭 C)
    where CreatesLimitsOfShape J 𝒥 :=
    { CreatesLimit := fun F => { lifts := fun c t => id_lifts_cone c } }
#align category_theory.id_creates_limits CategoryTheory.idCreatesLimits
-/

#print CategoryTheory.idLiftsCocone /-
/-- Any cocone lifts through the identity functor. -/
def idLiftsCocone (c : Cocone (K ⋙ 𝟭 C)) : LiftableCocone K (𝟭 C) c
    where
  liftedCocone :=
    { pt := c.pt
      ι := K.rightUnitor.inv ≫ c.ι }
  validLift := Cocones.ext (Iso.refl _) (by tidy)
#align category_theory.id_lifts_cocone CategoryTheory.idLiftsCocone
-/

#print CategoryTheory.idCreatesColimits /-
/-- The identity functor creates all colimits. -/
instance idCreatesColimits : CreatesColimitsOfSize.{w, w'} (𝟭 C)
    where CreatesColimitsOfShape J 𝒥 :=
    { CreatesColimit := fun F => { lifts := fun c t => id_lifts_cocone c } }
#align category_theory.id_creates_colimits CategoryTheory.idCreatesColimits
-/

#print CategoryTheory.inhabitedLiftableCone /-
/-- Satisfy the inhabited linter -/
instance inhabitedLiftableCone (c : Cone (K ⋙ 𝟭 C)) : Inhabited (LiftableCone K (𝟭 C) c) :=
  ⟨idLiftsCone c⟩
#align category_theory.inhabited_liftable_cone CategoryTheory.inhabitedLiftableCone
-/

#print CategoryTheory.inhabitedLiftableCocone /-
instance inhabitedLiftableCocone (c : Cocone (K ⋙ 𝟭 C)) : Inhabited (LiftableCocone K (𝟭 C) c) :=
  ⟨idLiftsCocone c⟩
#align category_theory.inhabited_liftable_cocone CategoryTheory.inhabitedLiftableCocone
-/

#print CategoryTheory.inhabitedLiftsToLimit /-
/-- Satisfy the inhabited linter -/
instance inhabitedLiftsToLimit (K : J ⥤ C) (F : C ⥤ D) [CreatesLimit K F] (c : Cone (K ⋙ F))
    (t : IsLimit c) : Inhabited (LiftsToLimit _ _ _ t) :=
  ⟨liftsToLimitOfCreates K F c t⟩
#align category_theory.inhabited_lifts_to_limit CategoryTheory.inhabitedLiftsToLimit
-/

#print CategoryTheory.inhabitedLiftsToColimit /-
instance inhabitedLiftsToColimit (K : J ⥤ C) (F : C ⥤ D) [CreatesColimit K F] (c : Cocone (K ⋙ F))
    (t : IsColimit c) : Inhabited (LiftsToColimit _ _ _ t) :=
  ⟨liftsToColimitOfCreates K F c t⟩
#align category_theory.inhabited_lifts_to_colimit CategoryTheory.inhabitedLiftsToColimit
-/

section Comp

variable {E : Type u₃} [ℰ : Category.{v₃} E]

variable (F : C ⥤ D) (G : D ⥤ E)

#print CategoryTheory.compCreatesLimit /-
instance compCreatesLimit [CreatesLimit K F] [CreatesLimit (K ⋙ F) G] : CreatesLimit K (F ⋙ G)
    where lifts c t :=
    { liftedCone := liftLimit (liftedLimitIsLimit t)
      validLift :=
        (Cones.functoriality (K ⋙ F) G).mapIso (liftedLimitMapsToOriginal (liftedLimitIsLimit t)) ≪≫
          liftedLimitMapsToOriginal t }
#align category_theory.comp_creates_limit CategoryTheory.compCreatesLimit
-/

#print CategoryTheory.compCreatesLimitsOfShape /-
instance compCreatesLimitsOfShape [CreatesLimitsOfShape J F] [CreatesLimitsOfShape J G] :
    CreatesLimitsOfShape J (F ⋙ G) where CreatesLimit := inferInstance
#align category_theory.comp_creates_limits_of_shape CategoryTheory.compCreatesLimitsOfShape
-/

#print CategoryTheory.compCreatesLimits /-
instance compCreatesLimits [CreatesLimitsOfSize.{w, w'} F] [CreatesLimitsOfSize.{w, w'} G] :
    CreatesLimitsOfSize.{w, w'} (F ⋙ G) where CreatesLimitsOfShape := inferInstance
#align category_theory.comp_creates_limits CategoryTheory.compCreatesLimits
-/

#print CategoryTheory.compCreatesColimit /-
instance compCreatesColimit [CreatesColimit K F] [CreatesColimit (K ⋙ F) G] :
    CreatesColimit K (F ⋙ G)
    where lifts c t :=
    { liftedCocone := liftColimit (liftedColimitIsColimit t)
      validLift :=
        (Cocones.functoriality (K ⋙ F) G).mapIso
            (liftedColimitMapsToOriginal (liftedColimitIsColimit t)) ≪≫
          liftedColimitMapsToOriginal t }
#align category_theory.comp_creates_colimit CategoryTheory.compCreatesColimit
-/

#print CategoryTheory.compCreatesColimitsOfShape /-
instance compCreatesColimitsOfShape [CreatesColimitsOfShape J F] [CreatesColimitsOfShape J G] :
    CreatesColimitsOfShape J (F ⋙ G) where CreatesColimit := inferInstance
#align category_theory.comp_creates_colimits_of_shape CategoryTheory.compCreatesColimitsOfShape
-/

#print CategoryTheory.compCreatesColimits /-
instance compCreatesColimits [CreatesColimitsOfSize.{w, w'} F] [CreatesColimitsOfSize.{w, w'} G] :
    CreatesColimitsOfSize.{w, w'} (F ⋙ G) where CreatesColimitsOfShape := inferInstance
#align category_theory.comp_creates_colimits CategoryTheory.compCreatesColimits
-/

end Comp

end Creates

end CategoryTheory


example {x y : Nat} (h : y > x) : x < y := by
  -- simp should unfold `>` when inserting into the discrimination tree
  simp [h]

abbrev good (n : Nat) :=
  n > 42

example (h : good n) : n > 42 := by
  -- simp should unfold `good` when inserting into the discrimination tree
  simp [h]

example {x y : Nat} (h : x ≠ y) : x ≠ y := by
  -- ≠ is also an abbreviation
  simp [h]


import ring_theory.polynomial.eisenstein.is_integral
import number_theory.cyclotomic.galois_action_on_cyclo
import number_theory.cyclotomic.rat
import number_theory.cyclotomic.Unit_lemmas
import ring_theory.dedekind_domain.ideal
import number_theory.cyclotomic.zeta_sub_one_prime
import number_theory.cyclotomic.cyclotomic_units
import algebra.char_p.quotient

universes u

open finite_dimensional polynomial algebra nat finset fintype

variables (p : ℕ+) (L : Type u) [field L] [char_zero L] [is_cyclotomic_extension {p} ℚ L]

section int_facts

noncomputable theory

open_locale number_field big_operators

open ideal is_cyclotomic_extension

-- TODO can we make a relative version of this with another base ring instead of ℤ ?
-- A version of flt_facts_3 indep of the ring
lemma exists_int_sub_pow_prime_dvd {A : Type*} [comm_ring A] [is_cyclotomic_extension {p} ℤ A]
  [hp : fact (p : ℕ).prime] (a : A) : ∃ (m : ℤ), (a ^ (p : ℕ) - m) ∈ span ({p} : set A) :=
begin
  have : a ∈ algebra.adjoin ℤ _ := @adjoin_roots {p} ℤ A _ _ _ _ a,
  apply algebra.adjoin_induction this,
  { intros x hx,
    rcases hx with ⟨hx_w, hx_m, hx_p⟩,
    simp only [set.mem_singleton_iff] at hx_m,
    rw [hx_m] at hx_p,
    simp only [hx_p, coe_coe],
    use 1,
    simp, },
  { intros r,
    use r ^ (p : ℕ),
    simp, },
  { rintros x y ⟨b, hb⟩ ⟨c, hc⟩,
    obtain ⟨r, hr⟩ := exists_add_pow_prime_eq hp.out x y,
    rw [hr],
    use c + b,
    push_cast,
    rw [sub_add_eq_sub_sub, sub_eq_add_neg, sub_eq_add_neg, add_comm _ (↑↑p * r),
        add_assoc, add_assoc],
    apply' ideal.add_mem _ _,
    { convert ideal.add_mem _ hb hc using 1,
      ring },
    { rw [mem_span_singleton, coe_coe],
      exact dvd_mul_right _ _ } },
  { rintros x y ⟨b, hb⟩ ⟨c, hc⟩,
    rw mul_pow,
    use b * c,
    have := ideal.mul_mem_left _ (x ^ (p : ℕ)) hc,
    rw [mul_sub] at this,
    rw [←ideal.quotient.eq_zero_iff_mem, map_sub] at this ⊢ hb,
    convert this using 2,
    rw [int.cast_mul, _root_.map_mul, _root_.map_mul],
    congr' 1,
    exact (sub_eq_zero.mp hb).symm }
end

example {R A : Type*} [comm_ring R] [comm_ring A] [algebra R A] [is_cyclotomic_extension {p} R A]
  [fact (p : ℕ).prime] (a : A) :
  ∃ (m : R), (a ^ (p : ℕ) - (algebra_map R A m)) ∈ span ({p} : set A) :=
begin
  by_cases hpunit : ↑(p : ℕ) ∈ nonunits A,
  swap,
  { simp only [mem_nonunits_iff, not_not] at hpunit,
    exact ⟨0, by simp [ideal.span_singleton_eq_top.2 hpunit]⟩ },
  have : a ∈ algebra.adjoin R _ := @adjoin_roots {p} R A _ _ _ _ a,
  refine algebra.adjoin_induction this _ (λ r, ⟨r ^ (p : ℕ), by simp⟩) _ _,
  { rintros x ⟨hx_w, hx_m, hx_p⟩,
    simp only [set.mem_singleton_iff] at hx_m,
    rw [hx_m] at hx_p,
    exact ⟨1, by simp [hx_p, coe_coe]⟩ },
  { rintros x y ⟨b, hb⟩ ⟨c, hc⟩,
    use c + b,
    rw [←ideal.quotient.eq_zero_iff_mem, map_sub, sub_eq_zero] at ⊢ hb hc,
    haveI : char_p (A ⧸ span ({p} : set A)) (p : ℕ) := by convert char_p.quotient A (p : ℕ) hpunit,
    rw [map_add, map_pow, map_add, add_pow_char_of_commute, ← map_pow, ← map_pow],
    { simp [hb, hc, add_comm] },
    { exact commute.all _ _ } },
  { rintros x y ⟨b, hb⟩ ⟨c, hc⟩,
    rw mul_pow,
    use b * c,
    rw [←ideal.quotient.eq_zero_iff_mem, map_sub, sub_eq_zero] at ⊢ hb hc,
    simp [hb, hc] }
end

instance aaaa [fact ((p : ℕ).prime)] : is_cyclotomic_extension {p} ℤ (𝓞 L) :=
let _ := (zeta_spec p ℚ L).adjoin_is_cyclotomic_extension ℤ in
 by exactI is_cyclotomic_extension.equiv {p} _ _ (zeta_spec p ℚ L).adjoin_equiv_ring_of_integers'

local notation `R` := 𝓞 (cyclotomic_field p ℚ)

local attribute [instance] algebra_rat_subsingleton

--This is still annoying
instance [hp : fact ((p : ℕ).prime)] : is_cyclotomic_extension {p} ℤ ↥(𝓞 (cyclotomic_field p ℚ)) :=
@aaaa p (cyclotomic_field p ℚ) _ _
  (by { convert cyclotomic_field.is_cyclotomic_extension p ℚ, exact subsingleton.elim _ _ }) _

-- TODO I (alex) am not sure whether this is better as ideal span,
-- or fractional_ideal.span_singleton
/-- The principal ideal generated by `x + y ζ^i` for integer `x` and `y` -/
@[nolint unused_arguments]
def flt_ideals [fact ((p : ℕ).prime)] (x y : ℤ) {η : R}
  (hη : η ∈ nth_roots_finset p R) : ideal R :=
  ideal.span ({ x + η * y } : set R)

variable {p} -- why does this not update (n : ℕ+)?

lemma mem_flt_ideals [fact ((p : ℕ).prime)] (x y : ℤ) {η : R}
  (hη : η ∈ nth_roots_finset p R) :
  ↑x + η * ↑y ∈ flt_ideals p x y hη :=
mem_span_singleton.mpr dvd_rfl

lemma ideal.le_add (a b c d : ideal R) (hab : a ≤ b) (hcd : c ≤ d) : a + c ≤ b + d :=
begin
  simp at *,
  split,
  apply le_trans hab (@le_sup_left _ _ _ _ ),
  apply le_trans hcd (@le_sup_right _ _ _ _ ),
end

lemma not_coprime_not_top (a b : ideal R) : ¬ is_coprime a b ↔ a + b ≠ ⊤ :=
begin
  apply not_iff_not_of_iff,
  rw is_coprime,
  split,
  intro h,
  obtain ⟨x, y , hxy ⟩ := h,
  rw eq_top_iff_one,
  have h2 : x * a + y * b ≤ a + b, by {apply ideal.le_add, all_goals {apply mul_le_left},  },
  apply h2,
  rw hxy,
  simp,
  intro h,
  refine ⟨1,1,_⟩,
  simp [h],
end

instance a1 : is_galois ℚ (cyclotomic_field p ℚ) := is_galois p _ _

instance a2 :  finite_dimensional ℚ (cyclotomic_field p ℚ) := finite_dimensional {p} _ _

instance a3 : number_field (cyclotomic_field p ℚ) := number_field {p} ℚ _

open is_primitive_root

lemma nth_roots_prim [fact (p : ℕ).prime] {η : R} (hη : η ∈ nth_roots_finset p R)
  (hne1 : η ≠ 1) : is_primitive_root η p :=
begin
  have hζ' := (zeta_spec p ℚ ((cyclotomic_field p ℚ))).unit'_coe,
    rw (nth_roots_one_eq_bUnion_primitive_roots' hζ') at hη,
    simp at *,
    obtain ⟨a, ha, h2⟩ := hη,
    have ha2 : a = p, by {rw (dvd_prime _inst_4.out) at ha,
    cases ha,
    exfalso,
    rw ha at h2,
    simp at h2,
    rw h2 at hne1,
    simp at *,
    exact hne1,
    exact ha,},
    rw ha2 at h2,
    have hn :  0 <(p : ℕ), by {norm_num,},
    rw (mem_primitive_roots hn) at h2,
    exact h2,
end

lemma prim_coe (ζ : R) (hζ : is_primitive_root ζ p) :
  is_primitive_root (ζ : (cyclotomic_field p ℚ))  p :=
coe_submonoid_class_iff.mpr hζ

lemma zeta_sub_one_dvb_p [fact (p : ℕ).prime] (ph : 5 ≤ p) {η : R} (hη : η ∈ nth_roots_finset p R)
  (hne1 : η ≠ 1): (1 - η) ∣ (p : R) :=
begin
  have h00 : (1 - η) ∣ (p : R) ↔ (η - 1) ∣ (p : R), by {have hh : -(η - 1) = (1 - η), by {ring},
  simp_rw [←hh],
  apply neg_dvd},
  rw h00,
  have : is_primitive_root (η : (cyclotomic_field p ℚ))  p, by {
    apply prim_coe p η (nth_roots_prim hη hne1)},
  have h0 : p ≠ 2, by   { intro hP,
    norm_num [hP] at ph },
  have h := ring_of_integers.dvd_norm ℚ ((η - 1) : R),
  have h2 := is_primitive_root.sub_one_norm_prime this (cyclotomic.irreducible_rat p.2) h0,
  convert h,
  ext,
  rw ring_of_integers.coe_algebra_map_norm,
  norm_cast at h2,
  rw h2,
  simp,
end

local attribute [-instance] cyclotomic_field.algebra
local notation `K` := cyclotomic_field p ℚ
local notation `R` := 𝓞 K

lemma one_sub_zeta_prime [fact (p : ℕ).prime] (ph : 5 ≤ p) {η : R} (hη : η ∈ nth_roots_finset p R)
  (hne1 : η ≠ 1) : prime (1 - η) :=
begin
  replace ph : p ≠ 2,
  { intro h,
    rw [h] at ph,
    simpa using ph },
  haveI diamond : is_cyclotomic_extension {p} ℚ K,
  { convert cyclotomic_field.is_cyclotomic_extension p ℚ,
    exact subsingleton.elim _ _ },
  have h := (prim_coe p η (nth_roots_prim hη hne1)),
  have := rat.zeta_sub_one_prime' h ph,
  have H : ((⟨η - 1, subalgebra.sub_mem _ (h.is_integral p.pos) (subalgebra.one_mem _)⟩ : R)) =
    η -1 := rfl,
  rw [H] at this,
  convert this.neg,
  ring,
end

lemma diff_of_roots [hp : fact (p : ℕ).prime] (ph : 5 ≤ p) {η₁ η₂ : R} (hη₁ : η₁ ∈ nth_roots_finset p R)
  (hη₂ : η₂ ∈ nth_roots_finset p R) (hdiff : η₁ ≠ η₂) (hwlog : η₁ ≠ 1) :
  ∃ (u : Rˣ), (η₁ - η₂) = u * (1 - η₁)  :=
begin
  replace ph : 2 ≤ p := le_trans (by norm_num) ph,
  have h := nth_roots_prim hη₁ hwlog,
  obtain ⟨i, ⟨H, hi⟩⟩ := h.eq_pow_of_pow_eq_one ((mem_nth_roots_finset hp.out.pos).1 hη₂) hp.out.pos,
  have hi1 : 1 ≠ i,
  { intro hi1,
    rw [← hi1, pow_one] at hi,
    exact hdiff hi },
  obtain ⟨u, hu⟩ := cyclotomic_unit.is_primitive_root.zeta_pow_sub_eq_unit_zeta_sub_one
    R ph hp.out hp.out.one_lt H hi1 h,
  refine ⟨u, _⟩,
  rw [← hu, hi, pow_one],
end

lemma diff_of_roots2  [fact (p : ℕ).prime] (ph : 5 ≤ p) {η₁ η₂ : R} (hη₁ : η₁ ∈ nth_roots_finset p R)
  (hη₂ : η₂ ∈ nth_roots_finset p R) (hdiff : η₁ ≠ η₂) (hwlog : η₁ ≠ 1) :
  ∃ (u : Rˣ), (η₂ - η₁) = u * (1 - η₁)  :=
begin
  obtain ⟨u, hu⟩ := diff_of_roots ph hη₁ hη₂ hdiff hwlog,
  refine ⟨-u, _⟩,
  rw [units.coe_neg, neg_mul, ← hu],
  ring
end

instance arg : is_dedekind_domain R := infer_instance

lemma flt_ideals_coprime2 [fact (p : ℕ).prime] (ph : 5 ≤ p) {x y : ℤ} {η₁ η₂ : R}
  (hη₁ : η₁ ∈ nth_roots_finset p R) (hη₂ : η₂ ∈ nth_roots_finset p R) (hdiff : η₁ ≠ η₂)
  (hp : is_coprime x y) (hp2 : ¬ (p : ℤ) ∣ (x + y : ℤ) ) (hwlog : η₁ ≠ 1) :
  is_coprime (flt_ideals p x y hη₁) (flt_ideals p x y hη₂) :=
begin
  let I := flt_ideals p x y hη₁ + flt_ideals p x y hη₂,
  by_contra,
  have he := (not_coprime_not_top p (flt_ideals p x y hη₁)  (flt_ideals p x y hη₂)).1 h,
  have := exists_le_maximal I he,
  obtain ⟨P, hP1, hP2⟩:= this,
  have hiP : (flt_ideals p x y hη₁) ≤ P := le_trans le_sup_left hP2,
  have hjP : (flt_ideals p x y hη₂) ≤ P := le_trans le_sup_right hP2,
  have hel1: ∃ v : Rˣ, (v : R) * y * (1 - η₁) ∈ I,
  { have : ↑x + η₁ * ↑y + (-1) * (↑x + η₂ * ↑y) ∈ I := ideal.add_mem _
      (mem_sup_left (mem_flt_ideals _ _ hη₁))
      (mul_mem_left _ (-1) (mem_sup_right (mem_flt_ideals _ _ _))),
    simp only [neg_mul, one_mul, neg_add_rev] at this,
    rw [neg_mul_eq_mul_neg, add_comm] at this,
    simp only [← add_assoc] at this,
    simp at this,
    have hh := diff_of_roots ph hη₁ hη₂ hdiff hwlog,
    obtain ⟨v, hv⟩ := hh,
    refine ⟨v, _⟩,
    have  h3 : -(η₂ * ↑y) + η₁ * ↑y = (η₁ - η₂) * y, by {ring},
    rw h3 at this,
    rw hv at this,
    have h4 :  ↑v * (1 - η₁) * ↑y = v * y * (1-η₁) , by {ring},
    rw ←h4,
    apply this },
  have hel2 : ∃ v : Rˣ, (v : R) * x * (1 - η₁) ∈ I,
  { have : η₂ * (↑x + η₁ * ↑y) + -η₁ * (↑x + η₂ * ↑y) ∈ I := ideal.add_mem _
      (mul_mem_left _ _ (mem_sup_left (mem_flt_ideals _ _ hη₁)))
      (mul_mem_left _ _ (mem_sup_right (mem_flt_ideals _ _ _))),
    have h1 :  η₂ * (↑x + η₁ * ↑y) + -η₁ * (↑x + η₂ * ↑y) = (η₂ - η₁) * x, by ring,
    rw h1 at this,
    have hh := diff_of_roots2 ph hη₁ hη₂ hdiff hwlog,
    obtain ⟨v, hv⟩ := hh,
    refine ⟨v, _⟩,
    rw hv at this,
    have h4 :  ↑v * (1 - η₁) * ↑x = v * x * (1-η₁) , by {ring},
    rw h4 at this,
    exact this },
  have hel11:  (y : R) * (1 - η₁) ∈ P, by {
    obtain ⟨v, hv ⟩:= hel1,
    rw mul_assoc at hv,
    have hvunit : is_unit (v : R), by {exact units.is_unit v, },
    apply (unit_mul_mem_iff_mem P hvunit).1 _,
    apply hP2,
    apply hv,},
  have hel22 : (x : R) * (1 - η₁) ∈ P, by {
      obtain ⟨v, hv ⟩:= hel2,
    rw mul_assoc at hv,
    have hvunit : is_unit (v : R), by {exact units.is_unit v, },
    apply (unit_mul_mem_iff_mem P hvunit).1 _,
    apply hP2,
    apply hv,
  },
  have hPrime:= hP1.is_prime,
  have hprime2 := is_prime.mem_or_mem hPrime hel11,
  have hprime3 := is_prime.mem_or_mem hPrime hel22,
  have HC : (1 - η₁) ∈ P → false,
    begin
    intro h,
    have eta_sub_one_ne_zero :=  sub_ne_zero.mpr (ne.symm hwlog),
    have hηprime : is_prime (ideal.span ({1 - η₁} : set R)) := by {
      rw span_singleton_prime eta_sub_one_ne_zero,
      apply one_sub_zeta_prime ph hη₁ hwlog,},
    have H5 : is_prime (ideal.span ({(p : ℤ)} : set ℤ)), by {
    have h2 : (p : ℤ) ≠ 0, by {simp, },
    have h1 : prime (p : ℤ) , by {simp only [coe_coe], rw  ←prime_iff_prime_int, exact _inst_4.out,},
    rw (span_singleton_prime h2),
    apply h1,  },
    have hηP : (ideal.span ({1 - η₁} : set R)) = P, by {
      have hRdim1 : ring.dimension_le_one R,  by {exact is_dedekind_domain.dimension_le_one,},
      have hle : (ideal.span ({1 - η₁} : set R)) ≤ P, by {rw span_le, simp [h],},
      apply ((@ring.dimension_le_one.prime_le_prime_iff_eq _ _ hRdim1 _ _ hηprime hPrime _).1 hle),
      simp,
      exact sub_ne_zero.mpr (ne.symm hwlog)},
    have hcapZ : P.comap (int.cast_ring_hom R) = ideal.span ({(p : ℤ)} : set ℤ), by {
      have H1 : ideal.span ({(p : ℤ)} : set ℤ) ≤ P.comap (int.cast_ring_hom R), by {
        rw ←hηP,
        apply le_comap_of_map_le _,
        rw map_span,
        simp,
        rw span_singleton_le_span_singleton,
        apply zeta_sub_one_dvb_p ph hη₁ hwlog},
      have H2 : is_prime (P.comap (int.cast_ring_hom R)),
        by {apply @is_prime.comap _ _ _ _ _ _ _ _ hPrime,},
      have H3 : ring.dimension_le_one ℤ, by {exact is_dedekind_domain.dimension_le_one, },
      have H4 :  (ideal.span ({(p : ℤ)} : set ℤ)) ≠ ⊥, by {simp,},
      apply ((@ring.dimension_le_one.prime_le_prime_iff_eq _ _ H3 _ _ H5 H2 H4).1 H1).symm,},
    have hxyinP : (x + y : R) ∈ P, by {
      have H1 : (x : R) + η₁* y ∈ P, by { apply hiP, apply submodule.mem_span_singleton_self},
      have H2 : η₁ * y = y - y * (1 - η₁), by {ring},
      rw H2 at H1,
      have H3 : ↑x + (↑y - ↑y * (1 - η₁)) = (↑x + ↑y) + (-↑y * (1 - η₁)), by {ring},
      rw H3 at H1,
      have H4 : -↑y * (1 - η₁) ∈ P, by {rw ←hηP, rw ideal.mem_span_singleton',
      refine ⟨-(y : R), rfl⟩, },
      apply (ideal.add_mem_iff_left P H4).1 H1,},
    have hxyinP2 : (x + y ) ∈ ideal.span ({(p : ℤ)} : set ℤ), by {rw ←hcapZ, simp [hxyinP]},
    rw mem_span_singleton at hxyinP2,
    apply absurd hxyinP2 hp2,
    end,
  cases hprime2,
  cases hprime3,
  obtain ⟨a, b, hab ⟩ := hp,
  have hone := P.add_mem (ideal.mul_mem_left _ a hprime3) (ideal.mul_mem_left _ b hprime2),
  norm_cast at hone,
  rw hab at hone,
  norm_cast at hone,
  rw ←eq_top_iff_one at hone,
  have hcontra := is_prime.ne_top hPrime,
  rw hone at hcontra,
  simp only [ne.def, eq_self_iff_true, not_true] at hcontra,
  exact hcontra,
  apply HC hprime3,
  apply HC hprime2,
end

lemma aux_lem_flt [fact (p : ℕ).prime] {x y z : ℤ}
  (H : x ^ (p : ℕ) + y ^ (p : ℕ) = z ^ (p : ℕ))
  (caseI : ¬ ↑p ∣ x * y * z) : ¬ (p : ℤ) ∣ (x + y : ℤ) :=
begin
  intro habs,
  replace habs : ↑(p : ℕ) ∣ (x + y : ℤ) := by simpa using habs,
  rw [← zmod.int_coe_zmod_eq_zero_iff_dvd, int.cast_add] at habs,
  replace H := congr_arg (λ x : ℤ, (x : zmod p)) H.symm,
  simp only [int.cast_add, int.cast_pow, zmod.pow_card, habs, zmod.int_coe_zmod_eq_zero_iff_dvd,
    ← coe_coe] at H,
  exact caseI (has_dvd.dvd.mul_left H _)
end

lemma flt_ideals_coprime [fact (p : ℕ).prime] (p5 : 5 ≤ p) {x y z : ℤ}
  (H : x ^ (p : ℕ) + y ^ (p : ℕ) = z ^ (p : ℕ)) {η₁ η₂ : R} (hxy : is_coprime x y)
  (hη₁ : η₁ ∈ nth_roots_finset p R) (hη₂ : η₂ ∈ nth_roots_finset p R) (hdiff : η₁ ≠ η₂)
  (caseI : ¬ ↑p ∣ x * y * z) : is_coprime (flt_ideals p x y hη₁) (flt_ideals p x y hη₂) :=
begin
   --how does wlog work? I want to have η₁ ≠ 1...
  by_cases h : η₁ ≠ 1,
   apply flt_ideals_coprime2 p5 hη₁ hη₂ hdiff hxy (aux_lem_flt H caseI) h,
  have h2 : η₂ ≠ 1, by {simp at h, rw h at hdiff, exact hdiff.symm},
  have := flt_ideals_coprime2 p5 hη₂ hη₁ hdiff.symm hxy (aux_lem_flt H caseI) h2,
  apply is_coprime.symm,
  exact this,
end

variable {L}

lemma dvd_last_coeff_cycl_integer [hp : fact (p : ℕ).prime] {ζ : 𝓞 L} (hζ : is_primitive_root ζ p)
  {f : fin p → ℤ} (hf : ∃ i, f i = 0) {m : ℤ}
  (hdiv : ↑m ∣ ∑ j, f j • ζ ^ (j : ℕ)) :
  m ∣ f ⟨(p : ℕ).pred, pred_lt hp.out.ne_zero⟩ :=
begin
  obtain ⟨i, Hi⟩ := hf,
  have hlast : (fin.cast (succ_pred_prime hp.out)) (fin.last (p : ℕ).pred) =
    ⟨(p : ℕ).pred, pred_lt hp.out.ne_zero⟩ := fin.ext rfl,
  have h : ∀ x, (fin.cast (succ_pred_prime hp.out)) (fin.cast_succ x) =
    ⟨x, lt_trans x.2 (pred_lt hp.out.ne_zero)⟩ := λ x, fin.ext rfl,
  let ζ' := (ζ : L),
  have hζ' : is_primitive_root ζ' p :=
    is_primitive_root.coe_submonoid_class_iff.2 hζ,
  have hcoe : ζ = ⟨ζ', hζ'.is_integral p.pos⟩ := by simp,
  set b := hζ'.integral_power_basis' with hb,
  have hdim : b.dim = (p : ℕ).pred,
  { rw [hζ'.power_basis_int'_dim, totient_prime hp.out, pred_eq_sub_one] },

  by_cases H : i = ⟨(p : ℕ).pred, pred_lt hp.out.ne_zero⟩,
  { simp [H.symm, Hi] },
  have hi : ↑i < (p : ℕ).pred,
  { by_contra' habs,
    simpa [le_antisymm habs (le_pred_of_lt (fin.is_lt i))] using H },
  obtain ⟨y, hy⟩ := hdiv,
  rw [← equiv.sum_comp (fin.cast (succ_pred_prime hp.out)).to_equiv, fin.sum_univ_cast_succ] at hy,
  simp only [hlast, h, rel_iso.coe_fn_to_equiv, fin.coe_mk] at hy,
  rw [hζ.pow_sub_one_eq hp.out.one_lt, ← sum_neg_distrib, smul_sum, sum_range, ← sum_add_distrib,
    ← (fin.cast hdim).to_equiv.sum_comp] at hy,
  simp only [rel_iso.coe_fn_to_equiv, fin.coe_cast, mul_neg, ← subtype.coe_inj] at hy,
  push_cast at hy,
  conv_lhs at hy { congr, skip, funext,
    rw [smul_neg, hcoe, ← hζ'.integral_power_basis'_gen, ← hb, ← subsemiring_class.coe_pow,
      ← show ∀ x, _ = _, from λ x, congr_fun b.coe_basis x, ← sub_eq_add_neg] },
  norm_cast at hy,
  rw [sum_sub_distrib] at hy,
  replace hy := congr_arg (b.basis.coord ((fin.cast hdim.symm) ⟨i, hi⟩)) hy,
  rw [← b.basis.equiv_fun_symm_apply, ← b.basis.equiv_fun_symm_apply, linear_map.map_sub,
    b.basis.coord_equiv_fun_symm, b.basis.coord_equiv_fun_symm] at hy,
  simp only [Hi, fin.coe_cast, smul_eq_mul, mul_boole, sum_ite_eq', mem_univ, fin.coe_mk,
    fin.eta, zero_sub, if_true] at hy,
  rw [← smul_eq_mul, ← zsmul_eq_smul_cast, neg_eq_iff_eq_neg] at hy,
  obtain ⟨n, hn⟩ := b.basis.dvd_coord_smul ((fin.cast hdim.symm) ⟨i, hi⟩) y m,
  rw [hn] at hy,
  simp [hy, dvd_neg]
end

lemma dvd_coeff_cycl_integer [hp : fact (p : ℕ).prime] {ζ : 𝓞 L} (hζ : is_primitive_root ζ p)
  {f : fin p → ℤ} (hf : ∃ i, f i = 0) {m : ℤ}
  (hdiv : ↑m ∣ ∑ j, f j • ζ ^ (j : ℕ)) : ∀ j, m ∣ f j :=
begin
  let ζ' := (ζ : L),
  have hζ' : is_primitive_root ζ' p :=
    is_primitive_root.coe_submonoid_class_iff.2 hζ,
  have hcoe : ζ = ⟨ζ', hζ'.is_integral p.pos⟩ := by simp,
  have hlast : (fin.cast (succ_pred_prime hp.out)) (fin.last (p : ℕ).pred) =
    ⟨(p : ℕ).pred, pred_lt hp.out.ne_zero⟩ := fin.ext rfl,
  have h : ∀ x, (fin.cast (succ_pred_prime hp.out)) (fin.cast_succ x) =
    ⟨x, lt_trans x.2 (pred_lt hp.out.ne_zero)⟩ := λ x, fin.ext rfl,
  set b := hζ'.integral_power_basis' with hb,
  have hdim : b.dim = (p : ℕ).pred,
  { rw [hζ'.power_basis_int'_dim, totient_prime hp.out, pred_eq_sub_one] },
  have last_dvd := dvd_last_coeff_cycl_integer hζ hf hdiv,

  intro j,
  by_cases H : j = ⟨(p : ℕ).pred, pred_lt hp.out.ne_zero⟩,
  { simpa [H] using last_dvd },
  have hj : ↑j < (p : ℕ).pred,
  { by_contra' habs,
    simpa [le_antisymm habs (le_pred_of_lt (fin.is_lt j))] using H },
  obtain ⟨y, hy⟩ := hdiv,
  rw [← equiv.sum_comp (fin.cast (succ_pred_prime hp.out)).to_equiv, fin.sum_univ_cast_succ] at hy,
  simp only [hlast, h, rel_iso.coe_fn_to_equiv, fin.coe_mk] at hy,
  rw [hζ.pow_sub_one_eq hp.out.one_lt, ← sum_neg_distrib, smul_sum, sum_range, ← sum_add_distrib,
    ← (fin.cast hdim).to_equiv.sum_comp] at hy,
  simp only [rel_iso.coe_fn_to_equiv, fin.coe_cast, mul_neg, ← subtype.coe_inj] at hy,
  push_cast at hy,
  conv_lhs at hy { congr, skip, funext,
    rw [smul_neg, hcoe, ← hζ'.integral_power_basis'_gen, ← hb, ← subsemiring_class.coe_pow,
      ← show ∀ x, _ = _, from λ x, congr_fun b.coe_basis x, ← sub_eq_add_neg] },
  norm_cast at hy,
  rw [sum_sub_distrib] at hy,
  replace hy := congr_arg (b.basis.coord ((fin.cast hdim.symm) ⟨j, hj⟩)) hy,
  rw [← b.basis.equiv_fun_symm_apply, ← b.basis.equiv_fun_symm_apply, linear_map.map_sub,
    b.basis.coord_equiv_fun_symm, b.basis.coord_equiv_fun_symm] at hy,
  simp only [fin.cast_mk, fin.coe_mk, fin.eta, basis.coord_apply, sub_eq_iff_eq_add] at hy,
  obtain ⟨n, hn⟩ := b.basis.dvd_coord_smul ((fin.cast hdim.symm) ⟨j, hj⟩) y m,
  rw [hy, ← smul_eq_mul, ← zsmul_eq_smul_cast, ← b.basis.coord_apply, ← fin.cast_mk, hn],
  exact dvd_add (dvd_mul_right _ _) last_dvd
end

end int_facts


import .temporal
       .first
import galois.subset.fixpoint

open subset

namespace temporal

universes u v

def always_fixpoint {T : Type u} (P : tProp T) (X : tProp T) := P ∩ ◯ X

def until_fixpoint {T : Type u} (P Q : tProp T) (X : tProp T) :=
  Q ∪ (P ∩ ◯ X)

lemma always_fixpoint_mono {T : Type u} (P : tProp T) :
  monotone (always_fixpoint P)
:= begin
unfold monotone, intros X Y H,
unfold always_fixpoint, apply bintersection_mono,
apply included_refl, apply next_mono, assumption
end

lemma nextn_continuous {T : Type u} (n : ℕ)
  : continuous (@nextn T n)
:= begin
unfold continuous, intros Ix f,
apply included_eq,
{ intros x H, intros ix,
  dsimp [function.comp], apply H, },
{ intros x H,
  unfold nextn, intros ix, apply H, }
end

lemma next_continuous {T : Type u}
  : continuous (@next T) := nextn_continuous 1

lemma always_fixpoint_continuous {T : Type u} (P : tProp T)
  : continuous_inh (always_fixpoint P)
:= begin
unfold continuous_inh, intros Ix inh f,
unfold always_fixpoint,
rw next_continuous,
unfold has_inter.inter, rw and_continuous_r,
reflexivity,
end

lemma until_fixpoint_mono {T : Type u} (P Q : tProp T) :
  monotone (until_fixpoint P Q)
:= begin
unfold monotone, intros X Y H,
unfold until_fixpoint, apply bunion_mono,
apply included_refl, apply bintersection_mono,
apply included_refl, apply next_mono, assumption
end

lemma until_fixpoint_continuous {T : Type u} (P Q : tProp T)
  [decidable_pred Q]
  : continuous_inh (until_fixpoint P Q)
:= begin
unfold continuous_inh, intros Ix inh f,
unfold until_fixpoint,
rw next_continuous,
unfold has_inter.inter, rw and_continuous_r,
unfold has_union.union,
rw or_continuous_r,
reflexivity,
end

lemma nextn_cocontinuous {T : Type u} (n : ℕ)
  : cocontinuous (@nextn T n)
:= begin
unfold cocontinuous, intros Ix f,
apply included_eq,
{ intros x H, induction H, constructor; assumption },
{ intros x H, induction H, constructor; assumption }
end

lemma next_cocontinuous {T : Type u}
  : cocontinuous (@next T)
:= nextn_cocontinuous 1

lemma until_fixpoint_cocontinuous_l {T : Type u}
  {Ix : Type} [inhabited Ix] (P : Ix → tProp T)
  (Q : tProp T)
  : until_fixpoint (union_ix P) Q
  = λ x, union_ix (λ ix, until_fixpoint (P ix) Q x)
:= begin
apply funext, intros X,
unfold until_fixpoint,
rw inter_comm,
unfold has_inter.inter,
rw and_cocontinuous_r,
unfold has_union.union,
rw or_cocontinuous_r,
dsimp [function.comp],
f_equal, apply funext, intros ix,
f_equal, apply inter_comm,
end

def weak_until {T : Type u} (P Q : tProp T) : tProp T :=
  greatest_fixpointn (until_fixpoint P Q)

-- \MCW
infix `𝓦` : 50 := weak_until

@[trace_map]
lemma weak_until_map {A : Type v} {T : Type u} (P Q : tProp T)
  (f : A → T)
  : (P 𝓦 Q) ∘ trace.map f = ((P ∘ trace.map f) 𝓦 (Q ∘ trace.map f))
:= begin
unfold weak_until,
unfold greatest_fixpointn, rw intersection_ix_precompose,
f_equal, apply funext, intros n,
induction n; simp [iterate],
reflexivity, rw ← ih_1, unfold until_fixpoint,
rw or_precompose,
rw and_precompose, rw next_map,
end

@[ltl]
def release {T : Type u} (P Q : tProp T) : tProp T :=
  Q 𝓦 (Q ∩ P)

-- \MCR
infix `𝓡` : 50 := release

lemma always_fixpoint_fixes {T : Type u} (P : tProp T)
  : always_fixpoint P (□ P) = □ P
:= begin
unfold always_fixpoint, symmetry, apply always_and_next
end

lemma always_greatest_fixpoint {T : Type u} (P : tProp T)
  : □ P = greatest_fixpoint (always_fixpoint P)
:= begin
apply included_eq,
{ intros x H, apply (union_st.mk (□ P)),
  unfold always_fixpoint,
  rw ← always_and_next, apply (included_refl _),
  assumption },
{ apply (greatest_fixpoint_le _ _),
  apply always_fixpoint_mono,
  intros x H,
  unfold always_fixpoint at H,
  intros tr xtr,
  have Hx : □ x tr,
  { apply temporal_induction, assumption,
    intros n IH, rw H at IH, induction IH with H1 H2,
    assumption
  },
  { rw H at Hx,
  rw ← always_and at Hx,
  induction Hx with H1 H2, assumption,
  }
}
end

lemma always_greatest_fixpointn {T : Type u} (P : tProp T)
  : □ P = greatest_fixpointn (always_fixpoint P)
:= begin
rw always_greatest_fixpoint, symmetry,
apply greatest_fixpointn_same,
apply continuous_chain_cocont, apply always_fixpoint_continuous,
end

lemma curry {T : Type u} {P Q R : tProp T}
  (H : ⊩ P ∩ Q => R)
  : ⊩ P => Q => R
:= begin
intros tr HP HQ, apply H, constructor; assumption
end

lemma weak_until_unfold {T : Type u} (P Q : tProp T)
  [decidable_pred Q]
  : P 𝓦 Q = (Q ∪ (P ∩ ◯ (P 𝓦 Q)))
:= begin
have H : P 𝓦 Q = until_fixpoint P Q (P 𝓦 Q),
unfold weak_until,
symmetry, apply greatest_fixpointn_fixed,
apply continuous_chain_cocont,
apply until_fixpoint_continuous,
apply H
end

lemma weak_until_not_always_lemma
  {T : Type u} (P Q : tProp T) (n : ℕ)
  : ⊩ □ (tNot Q)
    => iterate (until_fixpoint P Q) tt n
    => iterate (always_fixpoint P) tt n
:= begin
induction n; intros tr notQ H; simp [iterate],
simp [iterate] at H,
unfold until_fixpoint at H,
unfold always_fixpoint,
induction H with H H,
specialize (notQ 0),
rw delayn_zero at notQ, contradiction,
induction H with Hl Hr,
constructor, assumption,
unfold next nextn, apply ih_1,
intros n, rw delayn_combine, apply notQ,
apply next_delay, rw delayn_zero, assumption
end

lemma weak_until_not_always {T : Type u} (P Q : tProp T)
  : ⊩ P 𝓦 Q => □ (tNot Q) => □ P
:= begin
intros tr PWQ notQ,
rw always_greatest_fixpointn,
intros n, apply weak_until_not_always_lemma, assumption,
apply PWQ,
end

lemma weak_until_always_mono {T : Type u} (A B P : tProp T)
  : ⊩ □ (A => B) => A 𝓦 P => B 𝓦 P
:= begin
intros tr H AWP n,
specialize (AWP n), revert tr,
induction n,
case nat.zero { simp [iterate], },
case nat.succ a ih_1 {
  simp [iterate],
  intros tr AB H,
  unfold until_fixpoint, unfold until_fixpoint at H,
  induction H with H H, apply or.inl, assumption,
  apply or.inr, induction H with Hl Hr,
  constructor, rw ← (delayn_zero tr), apply AB,
  rw delayn_zero, assumption,
  unfold next nextn, apply ih_1,
  intros n, rw delayn_combine, apply AB,
  apply Hr
},
end

lemma weak_until_mono {T : Type u} {A B P : tProp T}
  (AB : A ≤ B)
  : (A 𝓦 P) ≤ (B 𝓦 P)
:= begin
intros tr AP,  apply weak_until_always_mono,
intros n, apply AB, assumption
end

lemma not_weakuntil_yes {T : Type u}
  (P : tProp T) [decP : decidable_pred P]
  : ⊩ tNot P 𝓦 P
:= begin
intros tr n, revert tr, induction n; simp [iterate]; intros tr,
trivial,
have H := decP tr, induction H with H H,
{ apply or.inr,
  constructor, apply H,
  apply ih_1 },
{ apply or.inl, assumption }
end

lemma always_weak_until {T : Type u} (P Q : tProp T) [decidable_pred Q] :
  ⊩ □ (P ∪ Q) => P 𝓦 Q
:= begin
rw always_greatest_fixpointn,
apply greatest_fixpointn_mono,
apply chain_cocont_mono,
apply continuous_chain_cocont,
apply always_fixpoint_continuous,
unfold always_fixpoint until_fixpoint,
intros X tr H, induction H with Hl Hr,
induction Hl with Hl Hl,
{ apply or.inr,
  constructor; assumption },
{ apply or.inl, assumption }
end

/-- An anologue of `temporal_induction`. If we can prove
    `P` holds now, and if, assuming `Q` doesn't hold,
    `P` implies `◯ P`, then `◯ P` holds weak-until `Q`.
-/
lemma weak_until_induction {T : Type u} (P Q : tProp T)
  [decQ : decidable_pred Q]
  : ⊩ (P => □ (tNot Q => P => (◯ P)) => ◯ P 𝓦 Q) :=
begin
intros tr H0 HS n,
revert tr, induction n; simp [iterate]; intros tr H0 HS,
trivial,
unfold until_fixpoint,
have H := decQ tr, induction H with HQ HQ,
{ apply or.inr, constructor, specialize (HS 0),
  rw delayn_zero at HS, specialize (HS HQ H0), assumption,
  unfold next nextn, apply ih_1,
  specialize (HS 0 HQ H0), assumption,
  intros n, rw delayn_combine, apply HS },
{ apply or.inl, assumption }
end

lemma eventually_strengthen_until {T : Type u}
  {P Q : tProp T}
  [decidable_pred Q]
  : ⊩ ◇ Q => (P 𝓦 Q) => (P 𝓤 Q)
:= begin
intros tr evQ PWQ,
have H := eventually_first_dec _ _ evQ,
clear evQ,
unfold first at H,
induction H with k Hk, induction Hk with Hkl Hkr,
constructor, split, assumption, clear Hkl,
revert tr,
induction k; intros,
{ exfalso, apply nat.not_lt_zero, assumption, },
{
cases n' with n' n',
specialize (Hkr _ a_1),
specialize (PWQ 1),
simp [iterate] at PWQ,
unfold until_fixpoint at PWQ,
induction PWQ with H H,
exfalso, apply Hkr, assumption,
induction H with Hl Hr,
rw delayn_zero, assumption,
rw ← (@delayn_combine _ 1 n'),
apply ih_1, rw weak_until_unfold at PWQ,
induction PWQ with H H,
exfalso, apply Hkr, tactic.swap,
rw delayn_zero, assumption,
apply nat.zero_lt_succ,
induction H with Hl Hr,
assumption,
intros,
rw delayn_combine, apply Hkr,
rw nat.succ_lt_succ_iff, assumption,
rw ← nat.succ_lt_succ_iff, assumption,
}
end

lemma fair_strengthen_until {T : Type u}
  (P Q : tProp T) [decidable_pred Q] :
  ⊩ □ (P 𝓦 Q)
  => □ (◇ Q)
  => □ (P 𝓤 Q)
:= begin
intros tr PQ fairQ  n,
apply eventually_strengthen_until; apply PQ <|> apply fairQ,
end

lemma weak_until_implies_release {T : Type u} {P Q : tProp T}
  : ⊩ P => (◯ P 𝓦 Q) => Q 𝓡 P
:= begin
intros tr HP HPimpQ n,
specialize (HPimpQ n), revert tr,
induction n; simp [iterate]; intros tr H0 HS,
unfold until_fixpoint, unfold until_fixpoint at HS,
induction HS with HS HS,
{ apply or.inl, constructor; assumption },
{ apply or.inr, induction HS with HSl HSr,
  constructor, assumption,
  unfold next nextn, apply ih_1, assumption,
  apply HSr }
end

lemma release_induction {T : Type u} (P Q : tProp T)
  [decQ : decidable_pred Q]
  : ⊩ (P => □ (tNot Q => P => (◯ P)) => Q 𝓡 P)
:= begin
intros tr H0 HS,
apply weak_until_implies_release, assumption,
apply weak_until_induction; assumption
end

lemma next_weak_until_always_loop_lemma {T : Type u}
  (P Q : tProp T)
: ⊩ □ (P=>◯ P𝓦Q) => □ (P ∩ Q=>◯ P) => □ (P=>◯ P)
:= begin
intros tr PuntilQ QimpP n HP,
specialize (PuntilQ n HP),
specialize (PuntilQ 1), simp [iterate] at PuntilQ,
unfold until_fixpoint at PuntilQ,
induction PuntilQ with H H, apply QimpP, split; assumption,
induction H with H H, assumption,
end

lemma next_weak_until_always_loop {T : Type u}
  (P Q : tProp T)
: ⊩ P => □ (P => ◯ P 𝓦 Q) => □ (Q => ◯ P) => □ P
:= begin
intros tr HP PuntilQ QimpP,
apply temporal_induction, assumption,
clear HP,
apply next_weak_until_always_loop_lemma; try { assumption },
intros n Hn, induction Hn with HP HQ, apply QimpP, assumption,
end

lemma next_weak_until_always_loop' {T : Type u}
  (P Q : tProp T)
: ⊩ P => □ (P => ◯ P 𝓦 Q) => □ (P ∩ Q => ◯ P) => □ P
:= begin
intros tr HP PuntilQ QimpP,
apply temporal_induction, assumption,
clear HP,
apply next_weak_until_always_loop_lemma; assumption
end

lemma next_weak_until_combine {T : Type u}
  (P Q : tProp T) [decidable_pred Q]
: ⊩ ◇ P => □ (P => ◯ P 𝓦 Q) => fair Q => ◇ (P ∩ Q)
:= begin
intros tr evP PuntilQ evQ,
apply eventually_cut, apply evP,
intros n Pnow,
specialize (evQ n),
specialize (PuntilQ n Pnow),
have H := eventually_strengthen_until _ evQ PuntilQ,
apply now_until_eventually, assumption, assumption,
end

lemma now_continuous {T : Type u}
  : continuous (@now T)
:= begin
unfold continuous now later intersection_ix,
dsimp [function.comp], intros,
apply funext, intros tr, unfold now later,
end

lemma now_cocontinuous {T : Type u}
  : cocontinuous (@now T)
:= begin
unfold cocontinuous now later intersection_ix,
dsimp [function.comp], intros,
apply funext, intros tr, unfold now later,
apply propext, split; intros H; induction H;
constructor; assumption,
end

end temporal


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_354
  (a d : ℝ)
  (h₀ : a + 6 * d = 30)
  (h₁ : a + 10 * d = 60) :
  a + 20 * d = 135 :=
begin
  linarith,
end