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

! This file was ported from Lean 3 source module category_theory.types
! leanprover-community/mathlib commit e97cf15cd1aec9bd5c193b2ffac5a6dc9118912b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.EpiMono
import Mathbin.CategoryTheory.Functor.FullyFaithful
import Mathbin.Logic.Equiv.Basic

/-!
# The category `Type`.

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

In this section we set up the theory so that Lean's types and functions between them
can be viewed as a `large_category` in our framework.

Lean can not transparently view a function as a morphism in this category, and needs a hint in
order to be able to type check. We provide the abbreviation `as_hom f` to guide type checking,
as well as a corresponding notation `↾ f`. (Entered as `\upr `.) The notation is enabled using
`open_locale category_theory.Type`.

We provide various simplification lemmas for functors and natural transformations valued in `Type`.

We define `ulift_functor`, from `Type u` to `Type (max u v)`, and show that it is fully faithful
(but not, of course, essentially surjective).

We prove some basic facts about the category `Type`:
*  epimorphisms are surjections and monomorphisms are injections,
* `iso` is both `iso` and `equiv` to `equiv` (at least within a fixed universe),
* every type level `is_lawful_functor` gives a categorical functor `Type ⥤ Type`
  (the corresponding fact about monads is in `src/category_theory/monad/types.lean`).
-/

namespace CategoryTheory

-- morphism levels before object levels. See note [category_theory universes].
universe v v' w u u'

#print CategoryTheory.types /-
/- The `@[to_additive]` attribute is just a hint that expressions involving this instance can
  still be additivized. -/
@[to_additive CategoryTheory.types]
instance types : LargeCategory (Type u)
    where
  Hom a b := a → b
  id a := id
  comp _ _ _ f g := g ∘ f
#align category_theory.types CategoryTheory.types
#align category_theory.types CategoryTheory.types
-/

#print CategoryTheory.types_hom /-
theorem types_hom {α β : Type u} : (α ⟶ β) = (α → β) :=
  rfl
#align category_theory.types_hom CategoryTheory.types_hom
-/

#print CategoryTheory.types_id /-
theorem types_id (X : Type u) : 𝟙 X = id :=
  rfl
#align category_theory.types_id CategoryTheory.types_id
-/

#print CategoryTheory.types_comp /-
theorem types_comp {X Y Z : Type u} (f : X ⟶ Y) (g : Y ⟶ Z) : f ≫ g = g ∘ f :=
  rfl
#align category_theory.types_comp CategoryTheory.types_comp
-/

#print CategoryTheory.types_id_apply /-
@[simp]
theorem types_id_apply (X : Type u) (x : X) : (𝟙 X : X → X) x = x :=
  rfl
#align category_theory.types_id_apply CategoryTheory.types_id_apply
-/

#print CategoryTheory.types_comp_apply /-
@[simp]
theorem types_comp_apply {X Y Z : Type u} (f : X ⟶ Y) (g : Y ⟶ Z) (x : X) : (f ≫ g) x = g (f x) :=
  rfl
#align category_theory.types_comp_apply CategoryTheory.types_comp_apply
-/

#print CategoryTheory.hom_inv_id_apply /-
@[simp]
theorem hom_inv_id_apply {X Y : Type u} (f : X ≅ Y) (x : X) : f.inv (f.Hom x) = x :=
  congr_fun f.hom_inv_id x
#align category_theory.hom_inv_id_apply CategoryTheory.hom_inv_id_apply
-/

#print CategoryTheory.inv_hom_id_apply /-
@[simp]
theorem inv_hom_id_apply {X Y : Type u} (f : X ≅ Y) (y : Y) : f.Hom (f.inv y) = y :=
  congr_fun f.inv_hom_id y
#align category_theory.inv_hom_id_apply CategoryTheory.inv_hom_id_apply
-/

#print CategoryTheory.asHom /-
-- Unfortunately without this wrapper we can't use `category_theory` idioms, such as `is_iso f`.
/-- `as_hom f` helps Lean type check a function as a morphism in the category `Type`. -/
abbrev asHom {α β : Type u} (f : α → β) : α ⟶ β :=
  f
#align category_theory.as_hom CategoryTheory.asHom
-/

-- mathport name: category_theory.as_hom
-- If you don't mind some notation you can use fewer keystrokes:
scoped[CategoryTheory.Type] notation "↾" f:200 => CategoryTheory.asHom f

-- type as \upr in VScode
section

-- We verify the expected type checking behaviour of `as_hom`.
variable (α β γ : Type u) (f : α → β) (g : β → γ)

example : α → γ :=
  ↾f ≫ ↾g

example [IsIso (↾f)] : Mono (↾f) := by infer_instance

example [IsIso (↾f)] : ↾f ≫ inv (↾f) = 𝟙 α := by simp

end

namespace Functor

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

/- warning: category_theory.functor.sections -> CategoryTheory.Functor.sections is a dubious translation:
lean 3 declaration is
  forall {J : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} J] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} J _inst_1 Type.{u2} CategoryTheory.types.{u2}), Set.{max u3 u2} (forall (j : J), CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} J _inst_1 Type.{u2} CategoryTheory.types.{u2} F j)
but is expected to have type
  forall {J : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} J] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} J _inst_1 Type.{u2} CategoryTheory.types.{u2}), Set.{max u3 u2} (forall (j : J), Prefunctor.obj.{succ u1, succ u2, u3, succ u2} J (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} J (CategoryTheory.Category.toCategoryStruct.{u1, u3} J _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} J _inst_1 Type.{u2} CategoryTheory.types.{u2} F) j)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.sections CategoryTheory.Functor.sectionsₓ'. -/
/-- The sections of a functor `J ⥤ Type` are
the choices of a point `u j : F.obj j` for each `j`,
such that `F.map f (u j) = u j` for every morphism `f : j ⟶ j'`.

We later use these to define limits in `Type` and in many concrete categories.
-/
def sections (F : J ⥤ Type w) : Set (∀ j, F.obj j) :=
  { u | ∀ {j j'} (f : j ⟶ j'), F.map f (u j) = u j' }
#align category_theory.functor.sections CategoryTheory.Functor.sections

end Functor

namespace FunctorToTypes

variable {C : Type u} [Category.{v} C] (F G H : C ⥤ Type w) {X Y Z : C}

variable (σ : F ⟶ G) (τ : G ⟶ H)

/- warning: category_theory.functor_to_types.map_comp_apply -> CategoryTheory.FunctorToTypes.map_comp_apply is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} {Y : C} {Z : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Y Z) (a : CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X), Eq.{succ u2} (CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F Z) (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X Z (CategoryTheory.CategoryStruct.comp.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1) X Y Z f g) a) (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F Y Z g (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X Y f a))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} {Y : C} {Z : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Y Z) (a : Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X), Eq.{succ u2} (Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) Z) (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X Z (CategoryTheory.CategoryStruct.comp.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1) X Y Z f g) a) (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) Y Z g (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X Y f a))
Case conversion may be inaccurate. Consider using '#align category_theory.functor_to_types.map_comp_apply CategoryTheory.FunctorToTypes.map_comp_applyₓ'. -/
@[simp]
theorem map_comp_apply (f : X ⟶ Y) (g : Y ⟶ Z) (a : F.obj X) :
    (F.map (f ≫ g)) a = (F.map g) ((F.map f) a) := by simp [types_comp]
#align category_theory.functor_to_types.map_comp_apply CategoryTheory.FunctorToTypes.map_comp_apply

/- warning: category_theory.functor_to_types.map_id_apply -> CategoryTheory.FunctorToTypes.map_id_apply is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} (a : CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X), Eq.{succ u2} (CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X) (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X X (CategoryTheory.CategoryStruct.id.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1) X) a) a
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} (a : Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X), Eq.{succ u2} (Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X) (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X X (CategoryTheory.CategoryStruct.id.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1) X) a) a
Case conversion may be inaccurate. Consider using '#align category_theory.functor_to_types.map_id_apply CategoryTheory.FunctorToTypes.map_id_applyₓ'. -/
@[simp]
theorem map_id_apply (a : F.obj X) : (F.map (𝟙 X)) a = a := by simp [types_id]
#align category_theory.functor_to_types.map_id_apply CategoryTheory.FunctorToTypes.map_id_apply

/- warning: category_theory.functor_to_types.naturality -> CategoryTheory.FunctorToTypes.naturality is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} {Y : C} (σ : Quiver.Hom.{succ (max u3 u2), max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.CategoryStruct.toQuiver.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}))) F G) (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) (x : CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X), Eq.{succ u2} (CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G Y) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G σ Y (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X Y f x)) (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G X Y f (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G σ X x))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} {Y : C} (σ : Quiver.Hom.{max (succ u3) (succ u2), max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.CategoryStruct.toQuiver.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}))) F G) (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) (x : Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X), Eq.{succ u2} (Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G) Y) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G σ Y (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X Y f x)) (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G) X Y f (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G σ X x))
Case conversion may be inaccurate. Consider using '#align category_theory.functor_to_types.naturality CategoryTheory.FunctorToTypes.naturalityₓ'. -/
theorem naturality (f : X ⟶ Y) (x : F.obj X) : σ.app Y ((F.map f) x) = (G.map f) (σ.app X x) :=
  congr_fun (σ.naturality f) x
#align category_theory.functor_to_types.naturality CategoryTheory.FunctorToTypes.naturality

/- warning: category_theory.functor_to_types.comp -> CategoryTheory.FunctorToTypes.comp is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (H : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} (σ : Quiver.Hom.{succ (max u3 u2), max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.CategoryStruct.toQuiver.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}))) F G) (τ : Quiver.Hom.{succ (max u3 u2), max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.CategoryStruct.toQuiver.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}))) G H) (x : CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X), Eq.{succ u2} (CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} H X) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F H (CategoryTheory.CategoryStruct.comp.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2})) F G H σ τ) X x) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G H τ X (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G σ X x))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (H : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} (σ : Quiver.Hom.{max (succ u3) (succ u2), max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.CategoryStruct.toQuiver.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}))) F G) (τ : Quiver.Hom.{max (succ u3) (succ u2), max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.CategoryStruct.toQuiver.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}))) G H) (x : Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X), Eq.{succ u2} (Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} H) X) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F H (CategoryTheory.CategoryStruct.comp.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2})) F G H σ τ) X x) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G H τ X (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G σ X x))
Case conversion may be inaccurate. Consider using '#align category_theory.functor_to_types.comp CategoryTheory.FunctorToTypes.compₓ'. -/
@[simp]
theorem comp (x : F.obj X) : (σ ≫ τ).app X x = τ.app X (σ.app X x) :=
  rfl
#align category_theory.functor_to_types.comp CategoryTheory.FunctorToTypes.comp

variable {D : Type u'} [𝒟 : Category.{u'} D] (I J : D ⥤ C) (ρ : I ⟶ J) {W : D}

/- warning: category_theory.functor_to_types.hcomp -> CategoryTheory.FunctorToTypes.hcomp is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (σ : Quiver.Hom.{succ (max u3 u2), max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.CategoryStruct.toQuiver.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}))) F G) {D : Type.{u4}} [𝒟 : CategoryTheory.Category.{u4, u4} D] (I : CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (J : CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (ρ : Quiver.Hom.{succ (max u4 u1), max u4 u1 u4 u3} (CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (CategoryTheory.CategoryStruct.toQuiver.{max u4 u1, max u4 u1 u4 u3} (CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (CategoryTheory.Category.toCategoryStruct.{max u4 u1, max u4 u1 u4 u3} (CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (CategoryTheory.Functor.category.{u4, u1, u4, u3} D 𝒟 C _inst_1))) I J) {W : D} (x : CategoryTheory.Functor.obj.{u4, u2, u4, succ u2} D 𝒟 Type.{u2} CategoryTheory.types.{u2} (CategoryTheory.Functor.comp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} I F) W), Eq.{succ u2} (CategoryTheory.Functor.obj.{u4, u2, u4, succ u2} D 𝒟 Type.{u2} CategoryTheory.types.{u2} (CategoryTheory.Functor.comp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} J G) W) (CategoryTheory.NatTrans.app.{u4, u2, u4, succ u2} D 𝒟 Type.{u2} CategoryTheory.types.{u2} (CategoryTheory.Functor.comp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} I F) (CategoryTheory.Functor.comp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} J G) (CategoryTheory.NatTrans.hcomp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} I J F G ρ σ) W x) (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G (CategoryTheory.Functor.obj.{u4, u1, u4, u3} D 𝒟 C _inst_1 I W) (CategoryTheory.Functor.obj.{u4, u1, u4, u3} D 𝒟 C _inst_1 J W) (CategoryTheory.NatTrans.app.{u4, u1, u4, u3} D 𝒟 C _inst_1 I J ρ W) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G σ (CategoryTheory.Functor.obj.{u4, u1, u4, u3} D 𝒟 C _inst_1 I W) x))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (σ : Quiver.Hom.{max (succ u3) (succ u2), max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.CategoryStruct.toQuiver.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Category.toCategoryStruct.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}))) F G) {D : Type.{u4}} [𝒟 : CategoryTheory.Category.{u4, u4} D] (I : CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (J : CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (ρ : Quiver.Hom.{max (succ u4) (succ u1), max (max u3 u4) u1} (CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (CategoryTheory.CategoryStruct.toQuiver.{max u4 u1, max (max u3 u4) u1} (CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (CategoryTheory.Category.toCategoryStruct.{max u4 u1, max (max u3 u4) u1} (CategoryTheory.Functor.{u4, u1, u4, u3} D 𝒟 C _inst_1) (CategoryTheory.Functor.category.{u4, u1, u4, u3} D 𝒟 C _inst_1))) I J) {W : D} (x : Prefunctor.obj.{succ u4, succ u2, u4, succ u2} D (CategoryTheory.CategoryStruct.toQuiver.{u4, u4} D (CategoryTheory.Category.toCategoryStruct.{u4, u4} D 𝒟)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u4, u2, u4, succ u2} D 𝒟 Type.{u2} CategoryTheory.types.{u2} (CategoryTheory.Functor.comp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} I F)) W), Eq.{succ u2} (Prefunctor.obj.{succ u4, succ u2, u4, succ u2} D (CategoryTheory.CategoryStruct.toQuiver.{u4, u4} D (CategoryTheory.Category.toCategoryStruct.{u4, u4} D 𝒟)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u4, u2, u4, succ u2} D 𝒟 Type.{u2} CategoryTheory.types.{u2} (CategoryTheory.Functor.comp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} J G)) W) (CategoryTheory.NatTrans.app.{u4, u2, u4, succ u2} D 𝒟 Type.{u2} CategoryTheory.types.{u2} (CategoryTheory.Functor.comp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} I F) (CategoryTheory.Functor.comp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} J G) (CategoryTheory.NatTrans.hcomp.{u4, u1, u2, u4, u3, succ u2} D 𝒟 C _inst_1 Type.{u2} CategoryTheory.types.{u2} I J F G ρ σ) W x) (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G) (Prefunctor.obj.{succ u4, succ u1, u4, u3} D (CategoryTheory.CategoryStruct.toQuiver.{u4, u4} D (CategoryTheory.Category.toCategoryStruct.{u4, u4} D 𝒟)) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u4, u1, u4, u3} D 𝒟 C _inst_1 I) W) (Prefunctor.obj.{succ u4, succ u1, u4, u3} D (CategoryTheory.CategoryStruct.toQuiver.{u4, u4} D (CategoryTheory.Category.toCategoryStruct.{u4, u4} D 𝒟)) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u4, u1, u4, u3} D 𝒟 C _inst_1 J) W) (CategoryTheory.NatTrans.app.{u4, u1, u4, u3} D 𝒟 C _inst_1 I J ρ W) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G σ (Prefunctor.obj.{succ u4, succ u1, u4, u3} D (CategoryTheory.CategoryStruct.toQuiver.{u4, u4} D (CategoryTheory.Category.toCategoryStruct.{u4, u4} D 𝒟)) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u4, u1, u4, u3} D 𝒟 C _inst_1 I) W) x))
Case conversion may be inaccurate. Consider using '#align category_theory.functor_to_types.hcomp CategoryTheory.FunctorToTypes.hcompₓ'. -/
@[simp]
theorem hcomp (x : (I ⋙ F).obj W) : (ρ ◫ σ).app W x = (G.map (ρ.app W)) (σ.app (I.obj W) x) :=
  rfl
#align category_theory.functor_to_types.hcomp CategoryTheory.FunctorToTypes.hcomp

/- warning: category_theory.functor_to_types.map_inv_map_hom_apply -> CategoryTheory.FunctorToTypes.map_inv_map_hom_apply is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} {Y : C} (f : CategoryTheory.Iso.{u1, u3} C _inst_1 X Y) (x : CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X), Eq.{succ u2} (CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X) (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F Y X (CategoryTheory.Iso.inv.{u1, u3} C _inst_1 X Y f) (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X Y (CategoryTheory.Iso.hom.{u1, u3} C _inst_1 X Y f) x)) x
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} {Y : C} (f : CategoryTheory.Iso.{u1, u3} C _inst_1 X Y) (x : Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X), Eq.{succ u2} (Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X) (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) Y X (CategoryTheory.Iso.inv.{u1, u3} C _inst_1 X Y f) (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X Y (CategoryTheory.Iso.hom.{u1, u3} C _inst_1 X Y f) x)) x
Case conversion may be inaccurate. Consider using '#align category_theory.functor_to_types.map_inv_map_hom_apply CategoryTheory.FunctorToTypes.map_inv_map_hom_applyₓ'. -/
@[simp]
theorem map_inv_map_hom_apply (f : X ≅ Y) (x : F.obj X) : F.map f.inv (F.map f.Hom x) = x :=
  congr_fun (F.mapIso f).hom_inv_id x
#align category_theory.functor_to_types.map_inv_map_hom_apply CategoryTheory.FunctorToTypes.map_inv_map_hom_apply

/- warning: category_theory.functor_to_types.map_hom_map_inv_apply -> CategoryTheory.FunctorToTypes.map_hom_map_inv_apply is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} {Y : C} (f : CategoryTheory.Iso.{u1, u3} C _inst_1 X Y) (y : CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F Y), Eq.{succ u2} (CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F Y) (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X Y (CategoryTheory.Iso.hom.{u1, u3} C _inst_1 X Y f) (CategoryTheory.Functor.map.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F Y X (CategoryTheory.Iso.inv.{u1, u3} C _inst_1 X Y f) y)) y
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) {X : C} {Y : C} (f : CategoryTheory.Iso.{u1, u3} C _inst_1 X Y) (y : Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) Y), Eq.{succ u2} (Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) Y) (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X Y (CategoryTheory.Iso.hom.{u1, u3} C _inst_1 X Y f) (Prefunctor.map.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) Y X (CategoryTheory.Iso.inv.{u1, u3} C _inst_1 X Y f) y)) y
Case conversion may be inaccurate. Consider using '#align category_theory.functor_to_types.map_hom_map_inv_apply CategoryTheory.FunctorToTypes.map_hom_map_inv_applyₓ'. -/
@[simp]
theorem map_hom_map_inv_apply (f : X ≅ Y) (y : F.obj Y) : F.map f.Hom (F.map f.inv y) = y :=
  congr_fun (F.mapIso f).inv_hom_id y
#align category_theory.functor_to_types.map_hom_map_inv_apply CategoryTheory.FunctorToTypes.map_hom_map_inv_apply

/- warning: category_theory.functor_to_types.hom_inv_id_app_apply -> CategoryTheory.FunctorToTypes.hom_inv_id_app_apply is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (α : CategoryTheory.Iso.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G) (X : C) (x : CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X), Eq.{succ u2} (CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F X) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G F (CategoryTheory.Iso.inv.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G α) X (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G (CategoryTheory.Iso.hom.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G α) X x)) x
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (α : CategoryTheory.Iso.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G) (X : C) (x : Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X), Eq.{succ u2} (Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F) X) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G F (CategoryTheory.Iso.inv.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G α) X (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G (CategoryTheory.Iso.hom.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G α) X x)) x
Case conversion may be inaccurate. Consider using '#align category_theory.functor_to_types.hom_inv_id_app_apply CategoryTheory.FunctorToTypes.hom_inv_id_app_applyₓ'. -/
@[simp]
theorem hom_inv_id_app_apply (α : F ≅ G) (X) (x) : α.inv.app X (α.Hom.app X x) = x :=
  congr_fun (α.hom_inv_id_app X) x
#align category_theory.functor_to_types.hom_inv_id_app_apply CategoryTheory.FunctorToTypes.hom_inv_id_app_apply

/- warning: category_theory.functor_to_types.inv_hom_id_app_apply -> CategoryTheory.FunctorToTypes.inv_hom_id_app_apply is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (α : CategoryTheory.Iso.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G) (X : C) (x : CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G X), Eq.{succ u2} (CategoryTheory.Functor.obj.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G X) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G (CategoryTheory.Iso.hom.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G α) X (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G F (CategoryTheory.Iso.inv.{max u3 u2, max u1 u2 u3 (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G α) X x)) x
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] (F : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (G : CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (α : CategoryTheory.Iso.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G) (X : C) (x : Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G) X), Eq.{succ u2} (Prefunctor.obj.{succ u1, succ u2, u3, succ u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G) X) (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} F G (CategoryTheory.Iso.hom.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G α) X (CategoryTheory.NatTrans.app.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2} G F (CategoryTheory.Iso.inv.{max u3 u2, max (max u3 u1) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) (CategoryTheory.Functor.category.{u1, u2, u3, succ u2} C _inst_1 Type.{u2} CategoryTheory.types.{u2}) F G α) X x)) x
Case conversion may be inaccurate. Consider using '#align category_theory.functor_to_types.inv_hom_id_app_apply CategoryTheory.FunctorToTypes.inv_hom_id_app_applyₓ'. -/
@[simp]
theorem inv_hom_id_app_apply (α : F ≅ G) (X) (x) : α.Hom.app X (α.inv.app X x) = x :=
  congr_fun (α.inv_hom_id_app X) x
#align category_theory.functor_to_types.inv_hom_id_app_apply CategoryTheory.FunctorToTypes.inv_hom_id_app_apply

end FunctorToTypes

#print CategoryTheory.uliftTrivial /-
/-- The isomorphism between a `Type` which has been `ulift`ed to the same universe,
and the original type.
-/
def uliftTrivial (V : Type u) : ULift.{u} V ≅ V := by tidy
#align category_theory.ulift_trivial CategoryTheory.uliftTrivial
-/

#print CategoryTheory.uliftFunctor /-
/-- The functor embedding `Type u` into `Type (max u v)`.
Write this as `ulift_functor.{5 2}` to get `Type 2 ⥤ Type 5`.
-/
def uliftFunctor : Type u ⥤ Type max u v
    where
  obj X := ULift.{v} X
  map X Y f := fun x : ULift.{v} X => ULift.up (f x.down)
#align category_theory.ulift_functor CategoryTheory.uliftFunctor
-/

/- warning: category_theory.ulift_functor_map -> CategoryTheory.uliftFunctor_map is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u2}} {Y : Type.{u2}} (f : Quiver.Hom.{succ u2, succ u2} Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) X Y) (x : ULift.{u1, u2} X), Eq.{succ (max u2 u1)} (CategoryTheory.Functor.obj.{u2, max u2 u1, succ u2, succ (max u2 u1)} Type.{u2} CategoryTheory.types.{u2} Type.{max u2 u1} CategoryTheory.types.{max u2 u1} CategoryTheory.uliftFunctor.{u1, u2} Y) (CategoryTheory.Functor.map.{u2, max u2 u1, succ u2, succ (max u2 u1)} Type.{u2} CategoryTheory.types.{u2} Type.{max u2 u1} CategoryTheory.types.{max u2 u1} CategoryTheory.uliftFunctor.{u1, u2} X Y f x) (ULift.up.{u1, u2} Y (f (ULift.down.{u1, u2} X x)))
but is expected to have type
  forall {X : Type.{u2}} {Y : Type.{u2}} (f : Quiver.Hom.{succ u2, succ u2} Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) X Y) (x : ULift.{u1, u2} X), Eq.{max (succ u2) (succ u1)} (Prefunctor.obj.{succ u2, max (succ u2) (succ u1), succ u2, max (succ u2) (succ u1)} Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) Type.{max u2 u1} (CategoryTheory.CategoryStruct.toQuiver.{max u2 u1, max (succ u2) (succ u1)} Type.{max u2 u1} (CategoryTheory.Category.toCategoryStruct.{max u2 u1, max (succ u2) (succ u1)} Type.{max u2 u1} CategoryTheory.types.{max u2 u1})) (CategoryTheory.Functor.toPrefunctor.{u2, max u2 u1, succ u2, max (succ u2) (succ u1)} Type.{u2} CategoryTheory.types.{u2} Type.{max u2 u1} CategoryTheory.types.{max u2 u1} CategoryTheory.uliftFunctor.{u1, u2}) Y) (Prefunctor.map.{succ u2, max (succ u2) (succ u1), succ u2, max (succ u2) (succ u1)} Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) Type.{max u2 u1} (CategoryTheory.CategoryStruct.toQuiver.{max u2 u1, max (succ u2) (succ u1)} Type.{max u2 u1} (CategoryTheory.Category.toCategoryStruct.{max u2 u1, max (succ u2) (succ u1)} Type.{max u2 u1} CategoryTheory.types.{max u2 u1})) (CategoryTheory.Functor.toPrefunctor.{u2, max u2 u1, succ u2, max (succ u2) (succ u1)} Type.{u2} CategoryTheory.types.{u2} Type.{max u2 u1} CategoryTheory.types.{max u2 u1} CategoryTheory.uliftFunctor.{u1, u2}) X Y f x) (ULift.up.{u1, u2} Y (f (ULift.down.{u1, u2} X x)))
Case conversion may be inaccurate. Consider using '#align category_theory.ulift_functor_map CategoryTheory.uliftFunctor_mapₓ'. -/
@[simp]
theorem uliftFunctor_map {X Y : Type u} (f : X ⟶ Y) (x : ULift.{v} X) :
    uliftFunctor.map f x = ULift.up (f x.down) :=
  rfl
#align category_theory.ulift_functor_map CategoryTheory.uliftFunctor_map

#print CategoryTheory.uliftFunctorFull /-
instance uliftFunctorFull : Full.{u} uliftFunctor where preimage X Y f x := (f (ULift.up x)).down
#align category_theory.ulift_functor_full CategoryTheory.uliftFunctorFull
-/

#print CategoryTheory.uliftFunctor_faithful /-
instance uliftFunctor_faithful : Faithful uliftFunctor
    where map_injective' X Y f g p :=
    funext fun x =>
      congr_arg ULift.down (congr_fun p (ULift.up x) : ULift.up (f x) = ULift.up (g x))
#align category_theory.ulift_functor_faithful CategoryTheory.uliftFunctor_faithful
-/

#print CategoryTheory.uliftFunctorTrivial /-
/-- The functor embedding `Type u` into `Type u` via `ulift` is isomorphic to the identity functor.
 -/
def uliftFunctorTrivial : uliftFunctor.{u, u} ≅ 𝟭 _ :=
  NatIso.ofComponents uliftTrivial (by tidy)
#align category_theory.ulift_functor_trivial CategoryTheory.uliftFunctorTrivial
-/

#print CategoryTheory.homOfElement /-
-- TODO We should connect this to a general story about concrete categories
-- whose forgetful functor is representable.
/-- Any term `x` of a type `X` corresponds to a morphism `punit ⟶ X`. -/
def homOfElement {X : Type u} (x : X) : PUnit ⟶ X := fun _ => x
#align category_theory.hom_of_element CategoryTheory.homOfElement
-/

#print CategoryTheory.homOfElement_eq_iff /-
theorem homOfElement_eq_iff {X : Type u} (x y : X) : homOfElement x = homOfElement y ↔ x = y :=
  ⟨fun H => congr_fun H PUnit.unit, by cc⟩
#align category_theory.hom_of_element_eq_iff CategoryTheory.homOfElement_eq_iff
-/

#print CategoryTheory.mono_iff_injective /-
/-- A morphism in `Type` is a monomorphism if and only if it is injective.

See <https://stacks.math.columbia.edu/tag/003C>.
-/
theorem mono_iff_injective {X Y : Type u} (f : X ⟶ Y) : Mono f ↔ Function.Injective f :=
  by
  constructor
  · intro H x x' h
    skip
    rw [← hom_of_element_eq_iff] at h⊢
    exact (cancel_mono f).mp h
  · exact fun H => ⟨fun Z => H.compLeft⟩
#align category_theory.mono_iff_injective CategoryTheory.mono_iff_injective
-/

#print CategoryTheory.injective_of_mono /-
theorem injective_of_mono {X Y : Type u} (f : X ⟶ Y) [hf : Mono f] : Function.Injective f :=
  (mono_iff_injective f).1 hf
#align category_theory.injective_of_mono CategoryTheory.injective_of_mono
-/

#print CategoryTheory.epi_iff_surjective /-
/-- A morphism in `Type` is an epimorphism if and only if it is surjective.

See <https://stacks.math.columbia.edu/tag/003C>.
-/
theorem epi_iff_surjective {X Y : Type u} (f : X ⟶ Y) : Epi f ↔ Function.Surjective f :=
  by
  constructor
  · rintro ⟨H⟩
    refine' Function.surjective_of_right_cancellable_Prop fun g₁ g₂ hg => _
    rw [← equiv.ulift.symm.injective.comp_left.eq_iff]
    apply H
    change ULift.up ∘ g₁ ∘ f = ULift.up ∘ g₂ ∘ f
    rw [hg]
  · exact fun H => ⟨fun Z => H.injective_comp_right⟩
#align category_theory.epi_iff_surjective CategoryTheory.epi_iff_surjective
-/

#print CategoryTheory.surjective_of_epi /-
theorem surjective_of_epi {X Y : Type u} (f : X ⟶ Y) [hf : Epi f] : Function.Surjective f :=
  (epi_iff_surjective f).1 hf
#align category_theory.surjective_of_epi CategoryTheory.surjective_of_epi
-/

section

#print CategoryTheory.ofTypeFunctor /-
/-- `of_type_functor m` converts from Lean's `Type`-based `category` to `category_theory`. This
allows us to use these functors in category theory. -/
def ofTypeFunctor (m : Type u → Type v) [Functor m] [LawfulFunctor m] : Type u ⥤ Type v
    where
  obj := m
  map α β := Functor.map
  map_id' α := Functor.map_id
  map_comp' α β γ f g := funext fun a => LawfulFunctor.comp_map f g _
#align category_theory.of_type_functor CategoryTheory.ofTypeFunctor
-/

variable (m : Type u → Type v) [Functor m] [LawfulFunctor m]

/- warning: category_theory.of_type_functor_obj -> CategoryTheory.ofTypeFunctor_obj is a dubious translation:
lean 3 declaration is
  forall (m : Type.{u2} -> Type.{u1}) [_inst_1 : Functor.{u2, u1} m] [_inst_2 : LawfulFunctor.{u2, u1} m _inst_1], Eq.{max (succ (succ u2)) (succ (succ u1))} (Type.{u2} -> Type.{u1}) (CategoryTheory.Functor.obj.{u2, u1, succ u2, succ u1} Type.{u2} CategoryTheory.types.{u2} Type.{u1} CategoryTheory.types.{u1} (CategoryTheory.ofTypeFunctor.{u1, u2} m _inst_1 _inst_2)) m
but is expected to have type
  forall (m : Type.{u2} -> Type.{u1}) [_inst_1 : Functor.{u2, u1} m] [_inst_2 : LawfulFunctor.{u2, u1} m _inst_1], Eq.{max (succ (succ u2)) (succ (succ u1))} (Type.{u2} -> Type.{u1}) (Prefunctor.obj.{succ u2, succ u1, succ u2, succ u1} Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) Type.{u1} (CategoryTheory.CategoryStruct.toQuiver.{u1, succ u1} Type.{u1} (CategoryTheory.Category.toCategoryStruct.{u1, succ u1} Type.{u1} CategoryTheory.types.{u1})) (CategoryTheory.Functor.toPrefunctor.{u2, u1, succ u2, succ u1} Type.{u2} CategoryTheory.types.{u2} Type.{u1} CategoryTheory.types.{u1} (CategoryTheory.ofTypeFunctor.{u1, u2} m _inst_1 _inst_2))) m
Case conversion may be inaccurate. Consider using '#align category_theory.of_type_functor_obj CategoryTheory.ofTypeFunctor_objₓ'. -/
@[simp]
theorem ofTypeFunctor_obj : (ofTypeFunctor m).obj = m :=
  rfl
#align category_theory.of_type_functor_obj CategoryTheory.ofTypeFunctor_obj

/- warning: category_theory.of_type_functor_map -> CategoryTheory.ofTypeFunctor_map is a dubious translation:
lean 3 declaration is
  forall (m : Type.{u2} -> Type.{u1}) [_inst_1 : Functor.{u2, u1} m] [_inst_2 : LawfulFunctor.{u2, u1} m _inst_1] {α : Type.{u2}} {β : Type.{u2}} (f : α -> β), Eq.{succ u1} (Quiver.Hom.{succ u1, succ u1} Type.{u1} (CategoryTheory.CategoryStruct.toQuiver.{u1, succ u1} Type.{u1} (CategoryTheory.Category.toCategoryStruct.{u1, succ u1} Type.{u1} CategoryTheory.types.{u1})) (CategoryTheory.Functor.obj.{u2, u1, succ u2, succ u1} Type.{u2} CategoryTheory.types.{u2} Type.{u1} CategoryTheory.types.{u1} (CategoryTheory.ofTypeFunctor.{u1, u2} m _inst_1 _inst_2) α) (CategoryTheory.Functor.obj.{u2, u1, succ u2, succ u1} Type.{u2} CategoryTheory.types.{u2} Type.{u1} CategoryTheory.types.{u1} (CategoryTheory.ofTypeFunctor.{u1, u2} m _inst_1 _inst_2) β)) (CategoryTheory.Functor.map.{u2, u1, succ u2, succ u1} Type.{u2} CategoryTheory.types.{u2} Type.{u1} CategoryTheory.types.{u1} (CategoryTheory.ofTypeFunctor.{u1, u2} m _inst_1 _inst_2) α β f) (Functor.map.{u2, u1} m _inst_1 α β f)
but is expected to have type
  forall (m : Type.{u2} -> Type.{u1}) [_inst_1 : Functor.{u2, u1} m] [_inst_2 : LawfulFunctor.{u2, u1} m _inst_1] {α : Type.{u2}} {β : Type.{u2}} (f : α -> β), Eq.{succ u1} (Quiver.Hom.{succ u1, succ u1} Type.{u1} (CategoryTheory.CategoryStruct.toQuiver.{u1, succ u1} Type.{u1} (CategoryTheory.Category.toCategoryStruct.{u1, succ u1} Type.{u1} CategoryTheory.types.{u1})) (Prefunctor.obj.{succ u2, succ u1, succ u2, succ u1} Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) Type.{u1} (CategoryTheory.CategoryStruct.toQuiver.{u1, succ u1} Type.{u1} (CategoryTheory.Category.toCategoryStruct.{u1, succ u1} Type.{u1} CategoryTheory.types.{u1})) (CategoryTheory.Functor.toPrefunctor.{u2, u1, succ u2, succ u1} Type.{u2} CategoryTheory.types.{u2} Type.{u1} CategoryTheory.types.{u1} (CategoryTheory.ofTypeFunctor.{u1, u2} m _inst_1 _inst_2)) α) (Prefunctor.obj.{succ u2, succ u1, succ u2, succ u1} Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) Type.{u1} (CategoryTheory.CategoryStruct.toQuiver.{u1, succ u1} Type.{u1} (CategoryTheory.Category.toCategoryStruct.{u1, succ u1} Type.{u1} CategoryTheory.types.{u1})) (CategoryTheory.Functor.toPrefunctor.{u2, u1, succ u2, succ u1} Type.{u2} CategoryTheory.types.{u2} Type.{u1} CategoryTheory.types.{u1} (CategoryTheory.ofTypeFunctor.{u1, u2} m _inst_1 _inst_2)) β)) (Prefunctor.map.{succ u2, succ u1, succ u2, succ u1} Type.{u2} (CategoryTheory.CategoryStruct.toQuiver.{u2, succ u2} Type.{u2} (CategoryTheory.Category.toCategoryStruct.{u2, succ u2} Type.{u2} CategoryTheory.types.{u2})) Type.{u1} (CategoryTheory.CategoryStruct.toQuiver.{u1, succ u1} Type.{u1} (CategoryTheory.Category.toCategoryStruct.{u1, succ u1} Type.{u1} CategoryTheory.types.{u1})) (CategoryTheory.Functor.toPrefunctor.{u2, u1, succ u2, succ u1} Type.{u2} CategoryTheory.types.{u2} Type.{u1} CategoryTheory.types.{u1} (CategoryTheory.ofTypeFunctor.{u1, u2} m _inst_1 _inst_2)) α β f) (Functor.map.{u2, u1} m _inst_1 α β f)
Case conversion may be inaccurate. Consider using '#align category_theory.of_type_functor_map CategoryTheory.ofTypeFunctor_mapₓ'. -/
@[simp]
theorem ofTypeFunctor_map {α β} (f : α → β) :
    (ofTypeFunctor m).map f = (Functor.map f : m α → m β) :=
  rfl
#align category_theory.of_type_functor_map CategoryTheory.ofTypeFunctor_map

end

end CategoryTheory

-- Isomorphisms in Type and equivalences.
namespace Equiv

universe u

variable {X Y : Type u}

#print Equiv.toIso /-
/-- Any equivalence between types in the same universe gives
a categorical isomorphism between those types.
-/
def toIso (e : X ≃ Y) : X ≅ Y where
  Hom := e.toFun
  inv := e.invFun
  hom_inv_id' := funext e.left_inv
  inv_hom_id' := funext e.right_inv
#align equiv.to_iso Equiv.toIso
-/

#print Equiv.toIso_hom /-
@[simp]
theorem toIso_hom {e : X ≃ Y} : e.toIso.Hom = e :=
  rfl
#align equiv.to_iso_hom Equiv.toIso_hom
-/

#print Equiv.toIso_inv /-
@[simp]
theorem toIso_inv {e : X ≃ Y} : e.toIso.inv = e.symm :=
  rfl
#align equiv.to_iso_inv Equiv.toIso_inv
-/

end Equiv

universe u

namespace CategoryTheory.Iso

open CategoryTheory

variable {X Y : Type u}

#print CategoryTheory.Iso.toEquiv /-
/-- Any isomorphism between types gives an equivalence.
-/
def toEquiv (i : X ≅ Y) : X ≃ Y where
  toFun := i.Hom
  invFun := i.inv
  left_inv x := congr_fun i.hom_inv_id x
  right_inv y := congr_fun i.inv_hom_id y
#align category_theory.iso.to_equiv CategoryTheory.Iso.toEquiv
-/

#print CategoryTheory.Iso.toEquiv_fun /-
@[simp]
theorem toEquiv_fun (i : X ≅ Y) : (i.toEquiv : X → Y) = i.Hom :=
  rfl
#align category_theory.iso.to_equiv_fun CategoryTheory.Iso.toEquiv_fun
-/

#print CategoryTheory.Iso.toEquiv_symm_fun /-
@[simp]
theorem toEquiv_symm_fun (i : X ≅ Y) : (i.toEquiv.symm : Y → X) = i.inv :=
  rfl
#align category_theory.iso.to_equiv_symm_fun CategoryTheory.Iso.toEquiv_symm_fun
-/

#print CategoryTheory.Iso.toEquiv_id /-
@[simp]
theorem toEquiv_id (X : Type u) : (Iso.refl X).toEquiv = Equiv.refl X :=
  rfl
#align category_theory.iso.to_equiv_id CategoryTheory.Iso.toEquiv_id
-/

#print CategoryTheory.Iso.toEquiv_comp /-
@[simp]
theorem toEquiv_comp {X Y Z : Type u} (f : X ≅ Y) (g : Y ≅ Z) :
    (f ≪≫ g).toEquiv = f.toEquiv.trans g.toEquiv :=
  rfl
#align category_theory.iso.to_equiv_comp CategoryTheory.Iso.toEquiv_comp
-/

end CategoryTheory.Iso

namespace CategoryTheory

#print CategoryTheory.isIso_iff_bijective /-
/-- A morphism in `Type u` is an isomorphism if and only if it is bijective. -/
theorem isIso_iff_bijective {X Y : Type u} (f : X ⟶ Y) : IsIso f ↔ Function.Bijective f :=
  Iff.intro (fun i => (as_iso f : X ≅ Y).toEquiv.Bijective) fun b =>
    IsIso.of_iso (Equiv.ofBijective f b).toIso
#align category_theory.is_iso_iff_bijective CategoryTheory.isIso_iff_bijective
-/

instance : SplitEpiCategory (Type u)
    where isSplitEpi_of_epi X Y f hf :=
    IsSplitEpi.mk'
      { section_ := Function.surjInv <| (epi_iff_surjective f).1 hf
        id' := funext <| Function.rightInverse_surjInv <| (epi_iff_surjective f).1 hf }

end CategoryTheory

#print equivIsoIso /-
-- We prove `equiv_iso_iso` and then use that to sneakily construct `equiv_equiv_iso`.
-- (In this order the proofs are handled by `obviously`.)
/-- Equivalences (between types in the same universe) are the same as (isomorphic to) isomorphisms
of types. -/
@[simps]
def equivIsoIso {X Y : Type u} : X ≃ Y ≅ X ≅ Y
    where
  Hom e := e.toIso
  inv i := i.toEquiv
#align equiv_iso_iso equivIsoIso
-/

#print equivEquivIso /-
/-- Equivalences (between types in the same universe) are the same as (equivalent to) isomorphisms
of types. -/
def equivEquivIso {X Y : Type u} : X ≃ Y ≃ (X ≅ Y) :=
  equivIsoIso.toEquiv
#align equiv_equiv_iso equivEquivIso
-/

#print equivEquivIso_hom /-
@[simp]
theorem equivEquivIso_hom {X Y : Type u} (e : X ≃ Y) : equivEquivIso e = e.toIso :=
  rfl
#align equiv_equiv_iso_hom equivEquivIso_hom
-/

#print equivEquivIso_inv /-
@[simp]
theorem equivEquivIso_inv {X Y : Type u} (e : X ≅ Y) : equivEquivIso.symm e = e.toEquiv :=
  rfl
#align equiv_equiv_iso_inv equivEquivIso_inv
-/


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import Mathlib.Init.Data.Option.Basic
import Mathlib.Init.Data.Option.Instances
import Mathlib.Init.Function
import Mathlib.Data.Option.Defs
import Mathlib.Logic.Basic

namespace Option

variable {α : Type _} {β : Type _} {γ : Type _}

theorem some_ne_none (x : α) : some x ≠ none :=
  fun h => Option.noConfusion h

protected theorem «forall» {p : Option α → Prop} : (∀ x, p x) ↔ p none ∧ ∀ x, p (some x) :=
  ⟨fun h => ⟨h _, fun x => h _⟩, fun h x => Option.casesOn x h.1 h.2⟩

protected theorem «exists» {p : Option α → Prop} : (∃ x, p x) ↔ p none ∨ ∃ x, p (some x) :=
  ⟨fun | ⟨none, hx⟩ => Or.inl hx | ⟨some x, hx⟩ => Or.inr ⟨x, hx⟩,
    fun h => h.elim (fun h => ⟨_, h⟩) fun ⟨x, hx⟩ => ⟨_, hx⟩⟩

theorem get_mem : ∀ {o : Option α} (h : isSome o), Option.get h ∈ o
| some a, _ => rfl

theorem get_of_mem {a : α} : ∀ {o : Option α} (h : isSome o), a ∈ o → Option.get h = a
| _, _, rfl => rfl

theorem not_mem_none (a : α) : a ∉ (none : Option α) := fun h => Option.noConfusion h

@[simp] theorem some_get : ∀ {x : Option α} (h : isSome x), some (Option.get h) = x
| some x, hx => rfl

@[simp] theorem get_some (x : α) (h : isSome (some x)) : Option.get h = x := rfl

@[simp] theorem getD_some (x y : α) : Option.getD (some x) y = x := rfl

@[simp] theorem getD_none (x : α) : Option.getD none x = x := rfl

theorem getD_of_ne_none {x : Option α} (hx : x ≠ none) (y : α) : some (x.getD y) = x := by
  cases x; {contradiction}; rw [getD_some]

theorem mem_unique {o : Option α} {a b : α} (ha : a ∈ o) (hb : b ∈ o) : a = b :=
  Option.some.inj $ ha.symm.trans hb

theorem some_injective (α : Type _) : Function.injective (@some α) :=
  fun _ _ => some_inj.mp

/-- `option.map f` is injective if `f` is injective. -/
theorem map_injective {f : α → β} (Hf : Function.injective f) : Function.injective (Option.map f)
| none, none, H => rfl
| some a₁, some a₂, H => by rw [Hf (Option.some.inj H)]

@[ext] theorem ext : ∀ {o₁ o₂ : Option α}, (∀ a, a ∈ o₁ ↔ a ∈ o₂) → o₁ = o₂
| none, none, H => rfl
| some a, o, H => ((H _).1 rfl).symm
| o, some b, H => (H _).2 rfl

theorem eq_none_iff_forall_not_mem {o : Option α} : o = none ↔ ∀ a, a ∉ o :=
  ⟨fun e a h => by rw [e] at h; (cases h), fun h => ext $ by simp; exact h⟩

@[simp] theorem none_bind (f : α → Option β) : none.bind f = none := rfl

@[simp] theorem some_bind (a : α) (f : α → Option β) : (some a).bind f = f a := rfl

@[simp] theorem bind_some (x : Option α) : x.bind some = x := by cases x <;> rfl

@[simp] theorem bind_eq_some {α β} {x : Option α} {f : α → Option β} {b : β} :
  x.bind f = some b ↔ ∃ a, x = some a ∧ f a = some b := by cases x <;> simp

@[simp] theorem bind_eq_none {o : Option α} {f : α → Option β} :
  o.bind f = none ↔ ∀ b a, a ∈ o → b ∉ f a := by
  simp only [eq_none_iff_forall_not_mem, not_exists, not_and, mem_def, bind_eq_some]; rfl

theorem bind_comm {α β γ} {f : α → β → Option γ} (a : Option α) (b : Option β) :
  (a.bind fun x => b.bind (f x)) = b.bind fun y => a.bind fun x => f x y := by
  cases a <;> cases b <;> rfl

theorem bind_assoc (x : Option α) (f : α → Option β) (g : β → Option γ) :
  (x.bind f).bind g = x.bind fun y => (f y).bind g := by cases x <;> rfl

theorem join_eq_some {x : Option (Option α)} {a : α} : x.join = some a ↔ x = some (some a) := by
  simp

theorem join_ne_none {x : Option (Option α)} : x.join ≠ none ↔ ∃ z, x = some (some z) := by
  simp

theorem join_ne_none' {x : Option (Option α)} : ¬x.join = none ↔ ∃ z, x = some (some z) := by
  simp

-- theorem join_eq_none {o : Option (Option α)} : o.join = none ↔ o = none ∨ o = some none := by
--   rcases o with _|_|_; simp

theorem bind_id_eq_join {x : Option (Option α)} : x.bind id = x.join := rfl

@[simp] theorem map_eq_map {α β} {f : α → β} : Functor.map f = Option.map f := rfl

theorem map_none {α β} {f : α → β} : f <$> none = none := rfl

theorem map_some {α β} {a : α} {f : α → β} : f <$> some a = some (f a) := rfl

@[simp] theorem map_none' {f : α → β} : none.map f = none := rfl

@[simp] theorem map_some' {a : α} {f : α → β} : (some a).map f = some (f a) := rfl

theorem map_eq_some {α β} {x : Option α} {f : α → β} {b : β} :
  f <$> x = some b ↔ ∃ a, x = some a ∧ f a = b := by cases x <;> simp

@[simp] theorem map_eq_some' {x : Option α} {f : α → β} {b : β} :
  x.map f = some b ↔ ∃ a, x = some a ∧ f a = b := by cases x <;> simp

@[simp] theorem map_eq_none' {x : Option α} {f : α → β} : x.map f = none ↔ x = none := by
  cases x <;> simp only [map_none', map_some', eq_self_iff_true]

theorem map_eq_none {α β} {x : Option α} {f : α → β} : f <$> x = none ↔ x = none := map_eq_none'

theorem map_congr {f g : α → β} {x : Option α} (h : ∀ a ∈ x, f a = g a) : x.map f = x.map g := by
  cases x <;> simp only [map_none', map_some', h, mem_def]

@[simp] theorem map_id' : Option.map (@id α) = id := map_id

@[simp] theorem map_map (h : β → γ) (g : α → β) (x : Option α) :
  (x.map g).map h = x.map (h ∘ g) := by
  cases x <;> simp only [map_none', map_some', ·∘·]

theorem comp_map (h : β → γ) (g : α → β) (x : Option α) : x.map (h ∘ g) = (x.map g).map h :=
  (map_map ..).symm

@[simp] theorem map_comp_map (f : α → β) (g : β → γ) :
  Option.map g ∘ Option.map f = Option.map (g ∘ f) := by funext x; simp

theorem mem_map_of_mem {α β : Type _} {a : α} {x : Option α} (g : α → β) (h : a ∈ x) :
  g a ∈ x.map g :=
  mem_def.mpr ((mem_def.mp h).symm ▸ map_some')

theorem bind_map_comm {α β} {x : Option (Option α)} {f : α → β} :
  x.bind (Option.map f) = (x.map (Option.map f)).bind id := by cases x <;> simp

theorem join_map_eq_map_join {f : α → β} {x : Option (Option α)} :
  (x.map (Option.map f)).join = x.join.map f := by
  -- rcases x with (_ | _ | x) <;> simp
  cases x; {simp}; rename_i x; cases x <;> simp

theorem join_join {x : Option (Option (Option α))} : x.join.join = (x.map join).join := by
  (iterate 2 cases x; {simp}; rename_i x); cases x <;> simp
  -- rcases x with (_ | _ | _ | x) <;> simp

theorem mem_of_mem_join {a : α} {x : Option (Option α)} (h : a ∈ x.join) : some a ∈ x :=
  mem_def.mpr ((mem_def.mp h).symm ▸ join_eq_some.mp h)

@[simp] theorem some_orelse (a : α) (x : Option α) : (some a <|> x) = some a := rfl

@[simp] theorem none_orelse (x : Option α) : (none <|> x) = x := by cases x <;> rfl

@[simp] theorem orelse_none (x : Option α) : (x <|> none) = x := by cases x <;> rfl

@[simp] theorem isSome_none : @isSome α none = false := rfl

@[simp] theorem isSome_some {a : α} : isSome (some a) = true := rfl

theorem isSome_iff_exists {x : Option α} : isSome x ↔ ∃ a, x = some a := by
  cases x <;> simp [isSome] <;> exact ⟨_, rfl⟩

@[simp] theorem isNone_none : @isNone α none = true := rfl

@[simp] theorem isNone_some {a : α} : isNone (some a) = false := rfl

@[simp] theorem not_isSome {a : Option α} : isSome a = false ↔ a.isNone = true := by
  cases a <;> simp

theorem eq_some_iff_get_eq {o : Option α} {a : α} :
  o = some a ↔ ∃ h : o.isSome, Option.get h = a := by cases o <;> simp; intro.

theorem not_isSome_iff_eq_none {o : Option α} : ¬o.isSome ↔ o = none := by
  cases o <;> simp

theorem ne_none_iff_isSome {o : Option α} : o ≠ none ↔ o.isSome := by cases o <;> simp

theorem ne_none_iff_exists {o : Option α} : o ≠ none ↔ ∃ x : α, some x = o := by cases o <;> simp

theorem ne_none_iff_exists' {o : Option α} : o ≠ none ↔ ∃ x : α, o = some x :=
  ne_none_iff_exists.trans $ exists_congr fun _ => eq_comm

theorem bex_ne_none {p : Option α → Prop} : (∃ x, ∃ (_ : x ≠ none), p x) ↔ ∃ x, p (some x) :=
  ⟨fun ⟨x, hx, hp⟩ => ⟨get $ ne_none_iff_isSome.1 hx, by rwa [some_get]⟩,
    fun ⟨x, hx⟩ => ⟨some x, some_ne_none x, hx⟩⟩

theorem ball_ne_none {p : Option α → Prop} : (∀ x (_ : x ≠ none), p x) ↔ ∀ x, p (some x) :=
  ⟨fun h x => h (some x) (some_ne_none x),
    fun h x hx => by
      have := h (get $ ne_none_iff_isSome.1 hx)
      simp [some_get] at this ⊢
      exact this⟩

@[simp] theorem guard_eq_some {p : α → Prop} [DecidablePred p] {a b : α} :
  guard p a = some b ↔ a = b ∧ p a := by
  by_cases h : p a <;> simp [Option.guard, h]

theorem lift_or_get_choice {f : α → α → α} (h : ∀ a b, f a b = a ∨ f a b = b) :
  ∀ o₁ o₂, lift_or_get f o₁ o₂ = o₁ ∨ lift_or_get f o₁ o₂ = o₂
| none, none => Or.inl rfl
| some a, none => Or.inl rfl
| none, some b => Or.inr rfl
| some a, some b => by have := h a b; simp [lift_or_get] at this ⊢; exact this

@[simp] theorem lift_or_get_none_left {f} {b : Option α} : lift_or_get f none b = b := by
  cases b <;> rfl

@[simp] theorem lift_or_get_none_right {f} {a : Option α} : lift_or_get f a none = a := by
  cases a <;> rfl

@[simp] theorem lift_or_get_some_some {f} {a b : α} :
  lift_or_get f (some a) (some b) = f a b := rfl

theorem elim_none (x : β) (f : α → β) : none.elim x f = x := rfl

theorem elim_some (x : β) (f : α → β) (a : α) : (some a).elim x f = f a := rfl

@[simp] theorem getD_map (f : α → β) (x : α) (o : Option α) :
  (o.map f).getD (f x) = f (getD o x) := by cases o <;> rfl

section

attribute [local instance] Classical.propDecidable

/-- An arbitrary `some a` with `a : α` if `α` is nonempty, and otherwise `none`. -/
noncomputable def choice (α : Type _) : Option α :=
  if h : Nonempty α then some (Classical.choice h) else none

theorem choice_eq {α : Type _} [Subsingleton α] (a : α) : choice α = some a := by
  simp [choice]
  rw [dif_pos (⟨a⟩ : Nonempty α)]
  simp; apply Subsingleton.elim

theorem choice_isSome_iff_nonempty {α : Type _} : (choice α).isSome ↔ Nonempty α := by
  constructor
  · intro h
    exact ⟨Option.get h⟩
  · intro h
    simp only [choice]
    rw [dif_pos h]
    exact isSome_some

end

@[simp] theorem to_list_some (a : α) : (a : Option α).toList = [a] := rfl

@[simp] theorem to_list_none (α : Type _) : (none : Option α).toList = [] := rfl


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import algebraic_geometry.presheafed_space
import category_theory.limits.final
import topology.sheaves.stalks

/-!
# Stalks for presheaved spaces

This file lifts constructions of stalks and pushforwards of stalks to work with
the category of presheafed spaces. Additionally, we prove that restriction of
presheafed spaces does not change the stalks.
-/

noncomputable theory

universes v u v' u'

open category_theory
open category_theory.limits category_theory.category category_theory.functor
open algebraic_geometry
open topological_space
open opposite

variables {C : Type u} [category.{v} C] [has_colimits C]

local attribute [tidy] tactic.op_induction'

open Top.presheaf

namespace algebraic_geometry.PresheafedSpace

/--
The stalk at `x` of a `PresheafedSpace`.
-/
abbreviation stalk (X : PresheafedSpace C) (x : X) : C := X.presheaf.stalk x

/--
A morphism of presheafed spaces induces a morphism of stalks.
-/
def stalk_map {X Y : PresheafedSpace.{v} C} (α : X ⟶ Y) (x : X) : Y.stalk (α.base x) ⟶ X.stalk x :=
(stalk_functor C (α.base x)).map (α.c) ≫ X.presheaf.stalk_pushforward C α.base x

@[simp, elementwise, reassoc]
lemma stalk_map_germ {X Y : PresheafedSpace.{v} C} (α : X ⟶ Y) (U : opens Y.carrier)
  (x : (opens.map α.base).obj U) :
  Y.presheaf.germ ⟨α.base x, x.2⟩ ≫ stalk_map α ↑x = α.c.app (op U) ≫ X.presheaf.germ x :=
by rw [stalk_map, stalk_functor_map_germ_assoc, stalk_pushforward_germ]

section restrict

/--
For an open embedding `f : U ⟶ X` and a point `x : U`, we get an isomorphism between the stalk
of `X` at `f x` and the stalk of the restriction of `X` along `f` at t `x`.
-/
def restrict_stalk_iso {U : Top} (X : PresheafedSpace.{v} C)
  {f : U ⟶ (X : Top.{v})} (h : open_embedding f) (x : U) :
  (X.restrict h).stalk x ≅ X.stalk (f x) :=
begin
  -- As a left adjoint, the functor `h.is_open_map.functor_nhds x` is initial.
  haveI := initial_of_adjunction (h.is_open_map.adjunction_nhds x),
  -- Typeclass resolution knows that the opposite of an initial functor is final. The result
  -- follows from the general fact that postcomposing with a final functor doesn't change colimits.
  exact final.colimit_iso (h.is_open_map.functor_nhds x).op
    ((open_nhds.inclusion (f x)).op ⋙ X.presheaf),
end

@[simp, elementwise, reassoc]
lemma restrict_stalk_iso_hom_eq_germ {U : Top} (X : PresheafedSpace.{v} C) {f : U ⟶ (X : Top.{v})}
  (h : open_embedding f) (V : opens U) (x : U) (hx : x ∈ V) :
  (X.restrict h).presheaf.germ ⟨x, hx⟩ ≫ (restrict_stalk_iso X h x).hom =
  X.presheaf.germ ⟨f x, show f x ∈ h.is_open_map.functor.obj V, from ⟨x, hx, rfl⟩⟩ :=
colimit.ι_pre ((open_nhds.inclusion (f x)).op ⋙ X.presheaf)
  (h.is_open_map.functor_nhds x).op (op ⟨V, hx⟩)

@[simp, elementwise, reassoc]
lemma restrict_stalk_iso_inv_eq_germ {U : Top} (X : PresheafedSpace.{v} C) {f : U ⟶ (X : Top.{v})}
  (h : open_embedding f) (V : opens U) (x : U) (hx : x ∈ V) :
  X.presheaf.germ ⟨f x, show f x ∈ h.is_open_map.functor.obj V, from ⟨x, hx, rfl⟩⟩ ≫
  (restrict_stalk_iso X h x).inv = (X.restrict h).presheaf.germ ⟨x, hx⟩ :=
by rw [← restrict_stalk_iso_hom_eq_germ, category.assoc, iso.hom_inv_id, category.comp_id]

lemma restrict_stalk_iso_inv_eq_of_restrict {U : Top} (X : PresheafedSpace.{v} C)
  {f : U ⟶ (X : Top.{v})} (h : open_embedding f) (x : U) :
    (X.restrict_stalk_iso h x).inv = stalk_map (X.of_restrict h) x :=
begin
  ext V,
  induction V using opposite.rec,
  let i : (h.is_open_map.functor_nhds x).obj ((open_nhds.map f x).obj V) ⟶ V :=
    hom_of_le (set.image_preimage_subset f _),
  erw [iso.comp_inv_eq, colimit.ι_map_assoc, colimit.ι_map_assoc, colimit.ι_pre],
  simp_rw category.assoc,
  erw colimit.ι_pre ((open_nhds.inclusion (f x)).op ⋙ X.presheaf)
    (h.is_open_map.functor_nhds x).op,
  erw ← X.presheaf.map_comp_assoc,
  exact (colimit.w ((open_nhds.inclusion (f x)).op ⋙ X.presheaf) i.op).symm,
end

instance of_restrict_stalk_map_is_iso {U : Top} (X : PresheafedSpace.{v} C)
  {f : U ⟶ (X : Top.{v})} (h : open_embedding f) (x : U) :
  is_iso (stalk_map (X.of_restrict h) x) :=
by { rw ← restrict_stalk_iso_inv_eq_of_restrict, apply_instance }

end restrict

namespace stalk_map

@[simp] lemma id (X : PresheafedSpace.{v} C) (x : X) : stalk_map (𝟙 X) x = 𝟙 (X.stalk x) :=
begin
  dsimp [stalk_map],
  simp only [stalk_pushforward.id],
  rw [←map_comp],
  convert (stalk_functor C x).map_id X.presheaf,
  tidy,
end

-- TODO understand why this proof is still gross (i.e. requires using `erw`)
@[simp] lemma comp {X Y Z : PresheafedSpace.{v} C} (α : X ⟶ Y) (β : Y ⟶ Z) (x : X) :
  stalk_map (α ≫ β) x =
    (stalk_map β (α.base x) : Z.stalk (β.base (α.base x)) ⟶ Y.stalk (α.base x)) ≫
    (stalk_map α x : Y.stalk (α.base x) ⟶ X.stalk x) :=
begin
  dsimp [stalk_map, stalk_functor, stalk_pushforward],
  ext U,
  induction U using opposite.rec,
  cases U,
  simp only [colimit.ι_map_assoc, colimit.ι_pre_assoc, colimit.ι_pre,
    whisker_left_app, whisker_right_app,
    assoc, id_comp, map_id, map_comp],
  dsimp,
  simp only [map_id, assoc, pushforward.comp_inv_app],
  -- FIXME Why doesn't simp do this:
  erw [category_theory.functor.map_id],
  erw [category_theory.functor.map_id],
  erw [id_comp, id_comp],
end

/--
If `α = β` and `x = x'`, we would like to say that `stalk_map α x = stalk_map β x'`.
Unfortunately, this equality is not well-formed, as their types are not _definitionally_ the same.
To get a proper congruence lemma, we therefore have to introduce these `eq_to_hom` arrows on
either side of the equality.
-/
lemma congr {X Y : PresheafedSpace.{v} C} (α β : X ⟶ Y) (h₁ : α = β) (x x': X) (h₂ : x = x') :
  stalk_map α x ≫ eq_to_hom (show X.stalk x = X.stalk x', by rw h₂) =
  eq_to_hom (show Y.stalk (α.base x) = Y.stalk (β.base x'), by rw [h₁, h₂]) ≫ stalk_map β x' :=
stalk_hom_ext _ $ λ U hx, by { subst h₁, subst h₂, simp }

lemma congr_hom {X Y : PresheafedSpace.{v} C} (α β : X ⟶ Y) (h : α = β) (x : X) :
  stalk_map α x =
  eq_to_hom (show Y.stalk (α.base x) = Y.stalk (β.base x), by rw h) ≫ stalk_map β x :=
by rw [← stalk_map.congr α β h x x rfl, eq_to_hom_refl, category.comp_id]

lemma congr_point {X Y : PresheafedSpace.{v} C} (α : X ⟶ Y) (x x' : X) (h : x = x') :
  stalk_map α x ≫ eq_to_hom (show X.stalk x = X.stalk x', by rw h) =
  eq_to_hom (show Y.stalk (α.base x) = Y.stalk (α.base x'), by rw h) ≫ stalk_map α x' :=
by rw stalk_map.congr α α rfl x x' h

instance is_iso {X Y : PresheafedSpace.{v} C} (α : X ⟶ Y) [is_iso α] (x : X) :
  is_iso (stalk_map α x) :=
{ out := begin
  let β : Y ⟶ X := category_theory.inv α,
  have h_eq : (α ≫ β).base x = x,
  { rw [is_iso.hom_inv_id α, id_base, Top.id_app] },
  -- Intuitively, the inverse of the stalk map of `α` at `x` should just be the stalk map of `β`
  -- at `α x`. Unfortunately, we have a problem with dependent type theory here: Because `x`
  -- is not *definitionally* equal to `β (α x)`, the map `stalk_map β (α x)` has not the correct
  -- type for an inverse.
  -- To get a proper inverse, we need to compose with the `eq_to_hom` arrow
  -- `X.stalk x ⟶ X.stalk ((α ≫ β).base x)`.
  refine ⟨eq_to_hom (show X.stalk x = X.stalk ((α ≫ β).base x), by rw h_eq) ≫
    (stalk_map β (α.base x) : _), _, _⟩,
  { rw [← category.assoc, congr_point α x ((α ≫ β).base x) h_eq.symm, category.assoc],
    erw ← stalk_map.comp β α (α.base x),
    rw [congr_hom _ _ (is_iso.inv_hom_id α), stalk_map.id, eq_to_hom_trans_assoc,
      eq_to_hom_refl, category.id_comp] },
  { rw [category.assoc, ← stalk_map.comp, congr_hom _ _ (is_iso.hom_inv_id α),
    stalk_map.id, eq_to_hom_trans_assoc, eq_to_hom_refl, category.id_comp] },
end }

/--
An isomorphism between presheafed spaces induces an isomorphism of stalks.
-/
def stalk_iso {X Y : PresheafedSpace.{v} C} (α : X ≅ Y) (x : X) :
  Y.stalk (α.hom.base x) ≅ X.stalk x :=
as_iso (stalk_map α.hom x)

@[simp, reassoc, elementwise]
lemma stalk_specializes_stalk_map {X Y : PresheafedSpace.{v} C} (f : X ⟶ Y) {x y : X} (h : x ⤳ y) :
  Y.presheaf.stalk_specializes (f.base.map_specializes h) ≫ stalk_map f x =
    stalk_map f y ≫ X.presheaf.stalk_specializes h :=
by { delta PresheafedSpace.stalk_map, simp [stalk_map] }

end stalk_map

end algebraic_geometry.PresheafedSpace


import topology.basic
import data.real.basic
import topology.continuous_function.basic
import tactic
import topology.instances.real

open topological_space

namespace Alg_Top

noncomputable theory

def I : set ℝ := {x | 0 ≤ x ∧ x ≤ 1}
variable y : ℝ

theorem h: ↑0 ∈ I :=
begin
  rw I,
  simp,
end

variables {X Y: Type} [topological_space X][topological_space Y]

theorem ge_I (y : ℝ) : |y| + 1 >= 1 :=
begin
  simp,
  apply abs_nonneg,
end

instance : has_zero I :=
begin
  split,split,
  have h: ↑0 ∈ I,
  {
    rw I, simp,
  },
  exact h,
end

instance : has_one I :=
begin
  split,split,
  have h: ↑1 ∈ I,
  {
    rw I, simp,
  },
  exact h,
end

variable α : Type

/- Type of path from a to b

-/
structure Path (a : X)(b : X) :=
(Func : ℝ → X )
(Continuity : continuous Func)
(InitPT: Func 0 = a)
(EndPT: Func 1 = b)

structure Homotopy {a b c d:X}(f : Path a b)(g: Path c d):=
(Func : ℝ × ℝ →  X)
(Continuity : continuous Func)
(InitPT: ∀(x:ℝ), Func (0,x) = f.Func x)
(EndPT: ∀(x:ℝ), Func (1,x) = g.Func x)

/-structure Path_Homotopy-/

def Homotopic {a b c d:X}(f : Path a b)(g: Path c d) := nonempty (Homotopy f g)

/-
structure FixPTHomotopy (a : X)(b : X):=
(Func : C(I × I, X))
(InitPT: ∀(t:I), Func (t,0) = a)
(EndPT: ∀(t:I), Func (t,1) = b)

structure FixPTHomotopic_with (a : X)(b : X)(f : Path a b)(g : Path a b):=
(Homotopy_map : FixPTHomotopy a b)
(InitPTEquiv: ∀(x:I), (Homotopy_map.Func (0,x) = f.Func x ))
(EndPTEquiv: ∀(x:I), (Homotopy_map.Func (1,x) = g.Func x )) -/

noncomputable def func (x : ℝ × ℝ) : ℝ := (x.fst+x.snd)/2
lemma Con : continuous func :=
begin
  apply continuous.mul,
  continuity,
end

variables a b: ℝ
variables f g: Path a b

noncomputable def LinFunc {a b:ℝ} (f g : Path a b) (x : ℝ × ℝ): ℝ :=
begin
  let Out := (1-(x.fst)) * (f.Func (x.snd)) + x.fst * (g.Func (x.snd)),
  exact Out,
end

noncomputable def FixPTLinearHomotopy {a b: ℝ} (f : Path a b) (g : Path a b): Homotopy f g:=
begin

  let Func: ℝ × ℝ → ℝ,
  {
    use LinFunc f g,
  },
  let InitPT: ∀(x:ℝ), LinFunc f g (↑0,x) = f.Func x,
  {
    intro x, rw LinFunc, simp,
  }, simp at InitPT,
  let EndPT: ∀(x:ℝ), LinFunc f g (↑1,x) = g.Func x,
  {
    intro x, rw LinFunc, simp,
  }, simp at EndPT,
  let IsContinuous : continuous (LinFunc f g),
  {
    apply continuous.add,
    continuity, exact f.Continuity, exact g.Continuity,
  },
  split, use IsContinuous, use InitPT,use EndPT,
end

def ConstantHomotopy {a b :X} (f : Path a b): Homotopy f f :=
begin
  split,
  let Func: ℝ × ℝ → X,
  {
    intro x, use f.Func x.snd,
  },
  let IsContinuous : continuous Func,
  {
    continuity,
    exact f.Continuity,
  },
  use IsContinuous,simp,simp,
end

/-
noncomputable lemma Homotopy_if_Homotopic : Homotopic f g → Homotopy f g :=
begin
  intro fRg,
  rw Homotopic at fRg, apply classical.choice, exact fRg,
end
-/

noncomputable theorem Hom_Equiv {a b : X} : equivalence (@Homotopic X _ a b a b):=
begin
  split,
  {
    rw reflexive,
    intro Pab,
    split, apply ConstantHomotopy Pab,
  },
  split,
  {
    rw symmetric,
    intros x y h,
    have hxy: Homotopy x y,
    {
      rw Homotopic at h,
      apply classical.choice, exact h,
    },
    split, split,
    let Func: ℝ × ℝ → X,
    {
      intro x,
      exact hxy.Func (1-(x.fst),x.snd),
    },
    let IsContinuous : continuous Func,
    {
      continuity, exact hxy.Continuity,
    },
    use IsContinuous,simp, intro x,
    rw hxy.EndPT,
    simp, intro x,
    rw hxy.InitPT,
  },

  {
    rw transitive,
    intros x y z xRy yRz,
    have hxy : Homotopy x y,
    {
      rw Homotopic at xRy, exact classical.choice xRy,
    },
    have hyz : Homotopy y z,
    {
      rw Homotopic at yRz, exact classical.choice yRz,
    },
    split, split,
    let Func: ℝ × ℝ → X := λ x, if x.fst ≤ 2⁻¹ then hxy.Func (2*x.fst,x.snd) else hyz.Func (2*x.fst-1,x.snd),
    have IsContinuous : continuous Func,
    {
      apply continuous.if, continuity,
      have h1: a_1 ∈ {x : ℝ × ℝ | x.fst = 2⁻¹},
      {
        apply frontier_le_subset_eq,continuity,
      },
      have h2: a_1.fst = 2⁻¹,
      {
        exact h1,
      },
      rw h2,simp, rw hxy.EndPT,rw hyz.InitPT,
      exact hxy.Continuity,
      exact hyz.Continuity,

    },
    exact IsContinuous,
    simp, intro x_1, rw hxy.InitPT,
    simp, intro x_1, split_ifs, apply false.elim, norm_num at h,
    norm_num, apply hyz.EndPT,
  },
end

lemma homotopic_have_hom {a b c d:X }{f:Path a b}{g:Path c d} : Homotopic f g → Homotopy f g :=
begin
  intro fRg, apply classical.choice,exact fRg,
end

end Alg_Top


import lambda_integer.definition natural.definition natural.addition tactic.nth_rewrite

set_option pp.all false

namespace Z'

def add (a b : Z') : Z' := ⟦((norm a).fst + (norm b).fst, (norm b).snd + (norm a).snd)⟧

instance : has_add Z' := ⟨ add ⟩

lemma add_deconstruct (a b : Z') : a + b = ⟦((norm a).fst + (norm b).fst, (norm b).snd + (norm a).snd)⟧ := rfl

def pair_add (a b : N × N) : N × N := begin
  cases a,
  cases b,
  exact (a_fst + b_fst, a_snd + b_snd),
end

instance : has_add (N × N) := ⟨ pair_add ⟩

lemma pair_add_deconstruction (a b c d : N) : (a, b) + (c, d) = (a + c, b + d) := rfl

lemma pair_add_fst (a b : N × N) : a.fst + b.fst = (a + b).fst := begin
  cases a,
  cases b,
  rw pair_add_deconstruction,
end

lemma pair_add_snd (a b : N × N) : a.snd + b.snd = (a + b).snd := begin
  cases a,
  cases b,
  rw pair_add_deconstruction,
end

lemma add_eq_eqq (a b : Z') : norm (a + b) = eqq (norm a + norm b) := begin
  rw add_deconstruct,
  rw norm_deconstruction,
  cases a.norm,
  cases b.norm,
  repeat {rw fst_eq, rw snd_eq},
  rw pair_add_deconstruction,
  rw N.add_comm snd snd_1,
end

theorem add_zero (a : Z') : a + 0 = a := begin
  rw add_deconstruct,
  rw zero_norm,
  rw fst_eq 0 0,
  rw snd_eq 0 0,
  rw N.add_zero,
  rw N.zero_add,
  exact a_norm_eq_a a,
end

theorem zero_add (a : Z') : 0 + a = a := begin
  rw add_deconstruct,
  rw zero_norm,
  rw fst_eq,
  rw snd_eq 0 0,
  rw N.zero_add,
  rw N.add_zero,
  exact a_norm_eq_a a,
end

theorem add_comm (a b : Z') : a + b = b + a := begin
  repeat {rw add_deconstruct},
  rw N.add_comm b.norm.fst,
  rw N.add_comm b.norm.snd,
end

instance : has_add (quot z_rel) := ⟨ add ⟩

lemma add_eqq_left : ∀ (a b c d : N), (a + c = b + d) -> ((eqq (a, b)).fst + c = (eqq (a, b)).snd + d)
| 0 b c d := begin
  intro h,
  rw eqq_0_a,
  rw fst_eq,
  rw snd_eq,
  exact h,
end
| a 0 c d := begin
  intro h,
  rw eqq_a_0,
  rw fst_eq,
  rw snd_eq,
  exact h,
end
| (N.succ a) (N.succ b) c d := begin
  intro h,
  repeat {rw N.succ_add at h},
  have q := N.succ_eq _ _ h,
  rw eqq_succ_succ,
  exact add_eqq_left _ _ _ _ q,
end

theorem add_distrib (a b: N × N): quot.mk z_rel a + quot.mk z_rel b = quot.mk z_rel (a + b) := begin
  rw ← paren_eq_mk,
  rw add_deconstruct,
  repeat {rw paren_eq_mk},
  rw quot.sound,
  rw z_rel,
  rw fst_eq,
  rw snd_eq ((norm (quot.mk z_rel a)).fst + (norm (quot.mk z_rel b)).fst) ((norm (quot.mk z_rel b)).snd + (norm (quot.mk z_rel a)).snd),
  repeat {rw ← paren_eq_mk},
  cases a,
  cases b,
  repeat {rw norm_deconstruction},
  rw pair_add_deconstruction,
  rw snd_eq,
  rw fst_eq (a_fst + b_fst) (a_snd + b_snd),
  rw N.add_assoc,
  rw N.add_comm (eqq(b_fst, b_snd)).snd (eqq (a_fst, a_snd)).snd,
  rw N.add_assoc,
  apply add_eqq_left,
  repeat {rw ← N.add_assoc},
  rw N.add_comm a_fst,
  rw N.add_comm a_snd,
  repeat {rw N.add_assoc},
  apply add_eqq_left,
  rw N.add_comm a_snd,
  rw ← N.add_assoc a_fst,
  rw N.add_comm a_fst,
  repeat {rw ← N.add_assoc b_fst},
  rw N.add_comm b_fst,
  rw N.add_assoc,
  rw N.add_comm a_fst,
  rw N.add_assoc,
  rw ← N.add_assoc b_fst,
  rw N.add_comm b_fst,
  rw N.add_assoc,
  rw N.add_comm b_fst,
end

theorem add_assoc (a b c : Z') : (a + b) + c = a + (b + c) := begin
  rw ← a_norm_eq_a a,
  rw ← a_norm_eq_a b,
  rw ← a_norm_eq_a c,
  repeat {rw paren_eq_mk},
  repeat {rw add_distrib},
  repeat {rw pair_add_deconstruction},
  repeat {rw N.add_assoc},
end

def nsmul: ∀ (a : ℕ) (b : Z'), Z'
| 0 b := 0
| (nat.succ a) b := b + nsmul a b
lemma nsmul_zero (b : Z') : nsmul 0 b = 0 := rfl
lemma nsmul_succ (a : ℕ) (b : Z') : nsmul (nat.succ a) b = b + nsmul a b := rfl

def nat_cast (a : ℕ): Z' := ⟦(N.nat_cast a, 0)⟧
lemma nat_cast_zero : nat_cast 0 = 0 := rfl
lemma nat_cast_succ (a : ℕ) : nat_cast (a.succ) = (nat_cast a) + 1 := begin
  rw nat_cast,
  rw N.nat_cast,
  rw nat_cast,
  rw one_eq_one, rw one,
  rw add_deconstruct,
  repeat {rw norm_deconstruction},
  repeat {rw eqq_a_0},
  repeat {rw fst_eq},
  repeat {rw snd_eq},
  rw N.add_zero,
  rw N.succ_eq_inc,
end

end Z'


/-
Copyright (c) 2019 Seul Baek. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Seul Baek
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.tactic.omega.term
import Mathlib.PostPort

universes l

namespace Mathlib

/-
Linear integer arithmetic terms in pre-normalized form.
-/

namespace omega

namespace int

/-- The shadow syntax for arithmetic terms. All constants are reified to `cst`
(e.g., `-5` is reified to `cst -5`) and all other atomic terms are reified to
`exp` (e.g., `-5 * (gcd 14 -7)` is reified to `exp -5 \`(gcd 14 -7)`).
`exp` accepts a coefficient of type `int` as its first argument because
multiplication by constant is allowed by the omega test. -/
/-- Similar to `exprterm`, except that all exprs are now replaced with
de Brujin indices of type `nat`. This is akin to generalizing over
the terms represented by the said exprs. -/
inductive preterm
where
| cst : ℤ → preterm
| var : ℤ → ℕ → preterm
| add : preterm → preterm → preterm

namespace preterm

/-- Preterm evaluation -/
@[simp] def val (v : ℕ → ℤ) : preterm → ℤ :=
  sorry

/-- Fresh de Brujin index not used by any variable in argument -/
def fresh_index : preterm → ℕ :=
  sorry

@[simp] def add_one (t : preterm) : preterm :=
  add t (cst 1)

def repr : preterm → string :=
  sorry

end preterm

/-- Return a term (which is in canonical form by definition)
    that is equivalent to the input preterm -/
@[simp] def canonize : preterm → term :=
  sorry

@[simp] theorem val_canonize {v : ℕ → ℤ} {t : preterm} : term.val v (canonize t) = preterm.val v t := sorry