/-
Copyright (c) 2022 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel

! This file was ported from Lean 3 source module category_theory.functor.epi_mono
! leanprover-community/mathlib commit ef7acf407d265ad4081c8998687e994fa80ba70c
! 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.Limits.Shapes.StrongEpi
import Mathbin.CategoryTheory.LiftingProperties.Adjunction

/-!
# Preservation and reflection of monomorphisms and epimorphisms

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

We provide typeclasses that state that a functor preserves or reflects monomorphisms or
epimorphisms.
-/

open CategoryTheory

universe v₁ v₂ v₃ u₁ u₂ u₃

namespace CategoryTheory.Functor

variable {C : Type u₁} [Category.{v₁} C] {D : Type u₂} [Category.{v₂} D] {E : Type u₃}
  [Category.{v₃} E]

#print CategoryTheory.Functor.PreservesMonomorphisms /-
/-- A functor preserves monomorphisms if it maps monomorphisms to monomorphisms. -/
class PreservesMonomorphisms (F : C ⥤ D) : Prop where
  preserves : ∀ {X Y : C} (f : X ⟶ Y) [Mono f], Mono (F.map f)
#align category_theory.functor.preserves_monomorphisms CategoryTheory.Functor.PreservesMonomorphisms
-/

/- warning: category_theory.functor.map_mono -> CategoryTheory.Functor.map_mono is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.Functor.PreservesMonomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_5 : CategoryTheory.Mono.{u1, u3} C _inst_1 X Y f], CategoryTheory.Mono.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.Functor.PreservesMonomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_5 : CategoryTheory.Mono.{u1, u3} C _inst_1 X Y f], CategoryTheory.Mono.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.map_mono CategoryTheory.Functor.map_monoₓ'. -/
instance map_mono (F : C ⥤ D) [PreservesMonomorphisms F] {X Y : C} (f : X ⟶ Y) [Mono f] :
    Mono (F.map f) :=
  PreservesMonomorphisms.preserves f
#align category_theory.functor.map_mono CategoryTheory.Functor.map_mono

#print CategoryTheory.Functor.PreservesEpimorphisms /-
/-- A functor preserves epimorphisms if it maps epimorphisms to epimorphisms. -/
class PreservesEpimorphisms (F : C ⥤ D) : Prop where
  preserves : ∀ {X Y : C} (f : X ⟶ Y) [Epi f], Epi (F.map f)
#align category_theory.functor.preserves_epimorphisms CategoryTheory.Functor.PreservesEpimorphisms
-/

/- warning: category_theory.functor.map_epi -> CategoryTheory.Functor.map_epi is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.Functor.PreservesEpimorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_5 : CategoryTheory.Epi.{u1, u3} C _inst_1 X Y f], CategoryTheory.Epi.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.Functor.PreservesEpimorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_5 : CategoryTheory.Epi.{u1, u3} C _inst_1 X Y f], CategoryTheory.Epi.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.map_epi CategoryTheory.Functor.map_epiₓ'. -/
instance map_epi (F : C ⥤ D) [PreservesEpimorphisms F] {X Y : C} (f : X ⟶ Y) [Epi f] :
    Epi (F.map f) :=
  PreservesEpimorphisms.preserves f
#align category_theory.functor.map_epi CategoryTheory.Functor.map_epi

#print CategoryTheory.Functor.ReflectsMonomorphisms /-
/-- A functor reflects monomorphisms if morphisms that are mapped to monomorphisms are themselves
    monomorphisms. -/
class ReflectsMonomorphisms (F : C ⥤ D) : Prop where
  reflects : ∀ {X Y : C} (f : X ⟶ Y), Mono (F.map f) → Mono f
#align category_theory.functor.reflects_monomorphisms CategoryTheory.Functor.ReflectsMonomorphisms
-/

/- warning: category_theory.functor.mono_of_mono_map -> CategoryTheory.Functor.mono_of_mono_map is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.Functor.ReflectsMonomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] {X : C} {Y : C} {f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y}, (CategoryTheory.Mono.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f)) -> (CategoryTheory.Mono.{u1, u3} C _inst_1 X Y f)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.Functor.ReflectsMonomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] {X : C} {Y : C} {f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y}, (CategoryTheory.Mono.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f)) -> (CategoryTheory.Mono.{u1, u3} C _inst_1 X Y f)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.mono_of_mono_map CategoryTheory.Functor.mono_of_mono_mapₓ'. -/
theorem mono_of_mono_map (F : C ⥤ D) [ReflectsMonomorphisms F] {X Y : C} {f : X ⟶ Y}
    (h : Mono (F.map f)) : Mono f :=
  ReflectsMonomorphisms.reflects f h
#align category_theory.functor.mono_of_mono_map CategoryTheory.Functor.mono_of_mono_map

#print CategoryTheory.Functor.ReflectsEpimorphisms /-
/-- A functor reflects epimorphisms if morphisms that are mapped to epimorphisms are themselves
    epimorphisms. -/
class ReflectsEpimorphisms (F : C ⥤ D) : Prop where
  reflects : ∀ {X Y : C} (f : X ⟶ Y), Epi (F.map f) → Epi f
#align category_theory.functor.reflects_epimorphisms CategoryTheory.Functor.ReflectsEpimorphisms
-/

/- warning: category_theory.functor.epi_of_epi_map -> CategoryTheory.Functor.epi_of_epi_map is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.Functor.ReflectsEpimorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] {X : C} {Y : C} {f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y}, (CategoryTheory.Epi.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f)) -> (CategoryTheory.Epi.{u1, u3} C _inst_1 X Y f)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) [_inst_4 : CategoryTheory.Functor.ReflectsEpimorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] {X : C} {Y : C} {f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y}, (CategoryTheory.Epi.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f)) -> (CategoryTheory.Epi.{u1, u3} C _inst_1 X Y f)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.epi_of_epi_map CategoryTheory.Functor.epi_of_epi_mapₓ'. -/
theorem epi_of_epi_map (F : C ⥤ D) [ReflectsEpimorphisms F] {X Y : C} {f : X ⟶ Y}
    (h : Epi (F.map f)) : Epi f :=
  ReflectsEpimorphisms.reflects f h
#align category_theory.functor.epi_of_epi_map CategoryTheory.Functor.epi_of_epi_map

#print CategoryTheory.Functor.preservesMonomorphisms_comp /-
instance preservesMonomorphisms_comp (F : C ⥤ D) (G : D ⥤ E) [PreservesMonomorphisms F]
    [PreservesMonomorphisms G] : PreservesMonomorphisms (F ⋙ G)
    where preserves X Y f h := by
    rw [comp_map]
    exact inferInstance
#align category_theory.functor.preserves_monomorphisms_comp CategoryTheory.Functor.preservesMonomorphisms_comp
-/

#print CategoryTheory.Functor.preservesEpimorphisms_comp /-
instance preservesEpimorphisms_comp (F : C ⥤ D) (G : D ⥤ E) [PreservesEpimorphisms F]
    [PreservesEpimorphisms G] : PreservesEpimorphisms (F ⋙ G)
    where preserves X Y f h := by
    rw [comp_map]
    exact inferInstance
#align category_theory.functor.preserves_epimorphisms_comp CategoryTheory.Functor.preservesEpimorphisms_comp
-/

#print CategoryTheory.Functor.reflectsMonomorphisms_comp /-
instance reflectsMonomorphisms_comp (F : C ⥤ D) (G : D ⥤ E) [ReflectsMonomorphisms F]
    [ReflectsMonomorphisms G] : ReflectsMonomorphisms (F ⋙ G)
    where reflects X Y f h := F.mono_of_mono_map (G.mono_of_mono_map h)
#align category_theory.functor.reflects_monomorphisms_comp CategoryTheory.Functor.reflectsMonomorphisms_comp
-/

#print CategoryTheory.Functor.reflectsEpimorphisms_comp /-
instance reflectsEpimorphisms_comp (F : C ⥤ D) (G : D ⥤ E) [ReflectsEpimorphisms F]
    [ReflectsEpimorphisms G] : ReflectsEpimorphisms (F ⋙ G)
    where reflects X Y f h := F.epi_of_epi_map (G.epi_of_epi_map h)
#align category_theory.functor.reflects_epimorphisms_comp CategoryTheory.Functor.reflectsEpimorphisms_comp
-/

#print CategoryTheory.Functor.preservesEpimorphisms_of_preserves_of_reflects /-
theorem preservesEpimorphisms_of_preserves_of_reflects (F : C ⥤ D) (G : D ⥤ E)
    [PreservesEpimorphisms (F ⋙ G)] [ReflectsEpimorphisms G] : PreservesEpimorphisms F :=
  ⟨fun X Y f hf => G.epi_of_epi_map <| show Epi ((F ⋙ G).map f) from inferInstance⟩
#align category_theory.functor.preserves_epimorphisms_of_preserves_of_reflects CategoryTheory.Functor.preservesEpimorphisms_of_preserves_of_reflects
-/

#print CategoryTheory.Functor.preservesMonomorphisms_of_preserves_of_reflects /-
theorem preservesMonomorphisms_of_preserves_of_reflects (F : C ⥤ D) (G : D ⥤ E)
    [PreservesMonomorphisms (F ⋙ G)] [ReflectsMonomorphisms G] : PreservesMonomorphisms F :=
  ⟨fun X Y f hf => G.mono_of_mono_map <| show Mono ((F ⋙ G).map f) from inferInstance⟩
#align category_theory.functor.preserves_monomorphisms_of_preserves_of_reflects CategoryTheory.Functor.preservesMonomorphisms_of_preserves_of_reflects
-/

#print CategoryTheory.Functor.reflectsEpimorphisms_of_preserves_of_reflects /-
theorem reflectsEpimorphisms_of_preserves_of_reflects (F : C ⥤ D) (G : D ⥤ E)
    [PreservesEpimorphisms G] [ReflectsEpimorphisms (F ⋙ G)] : ReflectsEpimorphisms F :=
  ⟨fun X Y f hf => (F ⋙ G).epi_of_epi_map <| show Epi (G.map (F.map f)) from inferInstance⟩
#align category_theory.functor.reflects_epimorphisms_of_preserves_of_reflects CategoryTheory.Functor.reflectsEpimorphisms_of_preserves_of_reflects
-/

#print CategoryTheory.Functor.reflectsMonomorphisms_of_preserves_of_reflects /-
theorem reflectsMonomorphisms_of_preserves_of_reflects (F : C ⥤ D) (G : D ⥤ E)
    [PreservesMonomorphisms G] [ReflectsMonomorphisms (F ⋙ G)] : ReflectsMonomorphisms F :=
  ⟨fun X Y f hf => (F ⋙ G).mono_of_mono_map <| show Mono (G.map (F.map f)) from inferInstance⟩
#align category_theory.functor.reflects_monomorphisms_of_preserves_of_reflects CategoryTheory.Functor.reflectsMonomorphisms_of_preserves_of_reflects
-/

#print CategoryTheory.Functor.preservesMonomorphisms.of_iso /-
theorem preservesMonomorphisms.of_iso {F G : C ⥤ D} [PreservesMonomorphisms F] (α : F ≅ G) :
    PreservesMonomorphisms G :=
  {
    preserves := fun X Y f h =>
      by
      haveI : mono (F.map f ≫ (α.app Y).Hom) := mono_comp _ _
      convert(mono_comp _ _ : mono ((α.app X).inv ≫ F.map f ≫ (α.app Y).Hom))
      rw [iso.eq_inv_comp, iso.app_hom, iso.app_hom, nat_trans.naturality] }
#align category_theory.functor.preserves_monomorphisms.of_iso CategoryTheory.Functor.preservesMonomorphisms.of_iso
-/

#print CategoryTheory.Functor.preservesMonomorphisms.iso_iff /-
theorem preservesMonomorphisms.iso_iff {F G : C ⥤ D} (α : F ≅ G) :
    PreservesMonomorphisms F ↔ PreservesMonomorphisms G :=
  ⟨fun h => preserves_monomorphisms.of_iso α, fun h => preserves_monomorphisms.of_iso α.symm⟩
#align category_theory.functor.preserves_monomorphisms.iso_iff CategoryTheory.Functor.preservesMonomorphisms.iso_iff
-/

#print CategoryTheory.Functor.preservesEpimorphisms.of_iso /-
theorem preservesEpimorphisms.of_iso {F G : C ⥤ D} [PreservesEpimorphisms F] (α : F ≅ G) :
    PreservesEpimorphisms G :=
  {
    preserves := fun X Y f h =>
      by
      haveI : epi (F.map f ≫ (α.app Y).Hom) := epi_comp _ _
      convert(epi_comp _ _ : epi ((α.app X).inv ≫ F.map f ≫ (α.app Y).Hom))
      rw [iso.eq_inv_comp, iso.app_hom, iso.app_hom, nat_trans.naturality] }
#align category_theory.functor.preserves_epimorphisms.of_iso CategoryTheory.Functor.preservesEpimorphisms.of_iso
-/

#print CategoryTheory.Functor.preservesEpimorphisms.iso_iff /-
theorem preservesEpimorphisms.iso_iff {F G : C ⥤ D} (α : F ≅ G) :
    PreservesEpimorphisms F ↔ PreservesEpimorphisms G :=
  ⟨fun h => preserves_epimorphisms.of_iso α, fun h => preserves_epimorphisms.of_iso α.symm⟩
#align category_theory.functor.preserves_epimorphisms.iso_iff CategoryTheory.Functor.preservesEpimorphisms.iso_iff
-/

#print CategoryTheory.Functor.reflectsMonomorphisms.of_iso /-
theorem reflectsMonomorphisms.of_iso {F G : C ⥤ D} [ReflectsMonomorphisms F] (α : F ≅ G) :
    ReflectsMonomorphisms G :=
  {
    reflects := fun X Y f h => by
      apply F.mono_of_mono_map
      haveI : mono (G.map f ≫ (α.app Y).inv) := mono_comp _ _
      convert(mono_comp _ _ : mono ((α.app X).Hom ≫ G.map f ≫ (α.app Y).inv))
      rw [← category.assoc, iso.eq_comp_inv, iso.app_hom, iso.app_hom, nat_trans.naturality] }
#align category_theory.functor.reflects_monomorphisms.of_iso CategoryTheory.Functor.reflectsMonomorphisms.of_iso
-/

#print CategoryTheory.Functor.reflectsMonomorphisms.iso_iff /-
theorem reflectsMonomorphisms.iso_iff {F G : C ⥤ D} (α : F ≅ G) :
    ReflectsMonomorphisms F ↔ ReflectsMonomorphisms G :=
  ⟨fun h => reflects_monomorphisms.of_iso α, fun h => reflects_monomorphisms.of_iso α.symm⟩
#align category_theory.functor.reflects_monomorphisms.iso_iff CategoryTheory.Functor.reflectsMonomorphisms.iso_iff
-/

#print CategoryTheory.Functor.reflectsEpimorphisms.of_iso /-
theorem reflectsEpimorphisms.of_iso {F G : C ⥤ D} [ReflectsEpimorphisms F] (α : F ≅ G) :
    ReflectsEpimorphisms G :=
  {
    reflects := fun X Y f h => by
      apply F.epi_of_epi_map
      haveI : epi (G.map f ≫ (α.app Y).inv) := epi_comp _ _
      convert(epi_comp _ _ : epi ((α.app X).Hom ≫ G.map f ≫ (α.app Y).inv))
      rw [← category.assoc, iso.eq_comp_inv, iso.app_hom, iso.app_hom, nat_trans.naturality] }
#align category_theory.functor.reflects_epimorphisms.of_iso CategoryTheory.Functor.reflectsEpimorphisms.of_iso
-/

#print CategoryTheory.Functor.reflectsEpimorphisms.iso_iff /-
theorem reflectsEpimorphisms.iso_iff {F G : C ⥤ D} (α : F ≅ G) :
    ReflectsEpimorphisms F ↔ ReflectsEpimorphisms G :=
  ⟨fun h => reflects_epimorphisms.of_iso α, fun h => reflects_epimorphisms.of_iso α.symm⟩
#align category_theory.functor.reflects_epimorphisms.iso_iff CategoryTheory.Functor.reflectsEpimorphisms.iso_iff
-/

#print CategoryTheory.Functor.preservesEpimorphsisms_of_adjunction /-
theorem preservesEpimorphsisms_of_adjunction {F : C ⥤ D} {G : D ⥤ C} (adj : F ⊣ G) :
    PreservesEpimorphisms F :=
  {
    preserves := fun X Y f hf =>
      ⟨by
        intro Z g h H
        replace H := congr_arg (adj.hom_equiv X Z) H
        rwa [adj.hom_equiv_naturality_left, adj.hom_equiv_naturality_left, cancel_epi,
          Equiv.apply_eq_iff_eq] at H⟩ }
#align category_theory.functor.preserves_epimorphsisms_of_adjunction CategoryTheory.Functor.preservesEpimorphsisms_of_adjunction
-/

#print CategoryTheory.Functor.preservesEpimorphisms_of_isLeftAdjoint /-
instance (priority := 100) preservesEpimorphisms_of_isLeftAdjoint (F : C ⥤ D) [IsLeftAdjoint F] :
    PreservesEpimorphisms F :=
  preservesEpimorphsisms_of_adjunction (Adjunction.ofLeftAdjoint F)
#align category_theory.functor.preserves_epimorphisms_of_is_left_adjoint CategoryTheory.Functor.preservesEpimorphisms_of_isLeftAdjoint
-/

#print CategoryTheory.Functor.preservesMonomorphisms_of_adjunction /-
theorem preservesMonomorphisms_of_adjunction {F : C ⥤ D} {G : D ⥤ C} (adj : F ⊣ G) :
    PreservesMonomorphisms G :=
  {
    preserves := fun X Y f hf =>
      ⟨by
        intro Z g h H
        replace H := congr_arg (adj.hom_equiv Z Y).symm H
        rwa [adj.hom_equiv_naturality_right_symm, adj.hom_equiv_naturality_right_symm, cancel_mono,
          Equiv.apply_eq_iff_eq] at H⟩ }
#align category_theory.functor.preserves_monomorphisms_of_adjunction CategoryTheory.Functor.preservesMonomorphisms_of_adjunction
-/

#print CategoryTheory.Functor.preservesMonomorphisms_of_isRightAdjoint /-
instance (priority := 100) preservesMonomorphisms_of_isRightAdjoint (F : C ⥤ D) [IsRightAdjoint F] :
    PreservesMonomorphisms F :=
  preservesMonomorphisms_of_adjunction (Adjunction.ofRightAdjoint F)
#align category_theory.functor.preserves_monomorphisms_of_is_right_adjoint CategoryTheory.Functor.preservesMonomorphisms_of_isRightAdjoint
-/

#print CategoryTheory.Functor.reflectsMonomorphisms_of_faithful /-
instance (priority := 100) reflectsMonomorphisms_of_faithful (F : C ⥤ D) [Faithful F] :
    ReflectsMonomorphisms F
    where reflects X Y f hf :=
    ⟨fun Z g h hgh =>
      F.map_injective ((cancel_mono (F.map f)).1 (by rw [← F.map_comp, hgh, F.map_comp]))⟩
#align category_theory.functor.reflects_monomorphisms_of_faithful CategoryTheory.Functor.reflectsMonomorphisms_of_faithful
-/

#print CategoryTheory.Functor.reflectsEpimorphisms_of_faithful /-
instance (priority := 100) reflectsEpimorphisms_of_faithful (F : C ⥤ D) [Faithful F] :
    ReflectsEpimorphisms F
    where reflects X Y f hf :=
    ⟨fun Z g h hgh =>
      F.map_injective ((cancel_epi (F.map f)).1 (by rw [← F.map_comp, hgh, F.map_comp]))⟩
#align category_theory.functor.reflects_epimorphisms_of_faithful CategoryTheory.Functor.reflectsEpimorphisms_of_faithful
-/

section

variable (F : C ⥤ D) {X Y : C} (f : X ⟶ Y)

/- warning: category_theory.functor.split_epi_equiv -> CategoryTheory.Functor.splitEpiEquiv is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_4 : CategoryTheory.Full.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Equiv.{succ u1, succ u2} (CategoryTheory.SplitEpi.{u1, u3} C _inst_1 X Y f) (CategoryTheory.SplitEpi.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_4 : CategoryTheory.Full.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Equiv.{succ u1, succ u2} (CategoryTheory.SplitEpi.{u1, u3} C _inst_1 X Y f) (CategoryTheory.SplitEpi.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f))
Case conversion may be inaccurate. Consider using '#align category_theory.functor.split_epi_equiv CategoryTheory.Functor.splitEpiEquivₓ'. -/
/-- If `F` is a fully faithful functor, split epimorphisms are preserved and reflected by `F`. -/
def splitEpiEquiv [Full F] [Faithful F] : SplitEpi f ≃ SplitEpi (F.map f)
    where
  toFun f := f.map F
  invFun s := by
    refine' ⟨F.preimage s.section_, _⟩
    apply F.map_injective
    simp only [map_comp, image_preimage, map_id]
    apply split_epi.id
  left_inv := by tidy
  right_inv := by tidy
#align category_theory.functor.split_epi_equiv CategoryTheory.Functor.splitEpiEquiv

/- warning: category_theory.functor.is_split_epi_iff -> CategoryTheory.Functor.isSplitEpi_iff is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_4 : CategoryTheory.Full.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Iff (CategoryTheory.IsSplitEpi.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f)) (CategoryTheory.IsSplitEpi.{u1, u3} C _inst_1 X Y f)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_4 : CategoryTheory.Full.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Iff (CategoryTheory.IsSplitEpi.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f)) (CategoryTheory.IsSplitEpi.{u1, u3} C _inst_1 X Y f)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.is_split_epi_iff CategoryTheory.Functor.isSplitEpi_iffₓ'. -/
@[simp]
theorem isSplitEpi_iff [Full F] [Faithful F] : IsSplitEpi (F.map f) ↔ IsSplitEpi f :=
  by
  constructor
  · intro h
    exact is_split_epi.mk' ((split_epi_equiv F f).invFun h.exists_split_epi.some)
  · intro h
    exact is_split_epi.mk' ((split_epi_equiv F f).toFun h.exists_split_epi.some)
#align category_theory.functor.is_split_epi_iff CategoryTheory.Functor.isSplitEpi_iff

/- warning: category_theory.functor.split_mono_equiv -> CategoryTheory.Functor.splitMonoEquiv is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_4 : CategoryTheory.Full.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Equiv.{succ u1, succ u2} (CategoryTheory.SplitMono.{u1, u3} C _inst_1 X Y f) (CategoryTheory.SplitMono.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_4 : CategoryTheory.Full.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Equiv.{succ u1, succ u2} (CategoryTheory.SplitMono.{u1, u3} C _inst_1 X Y f) (CategoryTheory.SplitMono.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f))
Case conversion may be inaccurate. Consider using '#align category_theory.functor.split_mono_equiv CategoryTheory.Functor.splitMonoEquivₓ'. -/
/-- If `F` is a fully faithful functor, split monomorphisms are preserved and reflected by `F`. -/
def splitMonoEquiv [Full F] [Faithful F] : SplitMono f ≃ SplitMono (F.map f)
    where
  toFun f := f.map F
  invFun s := by
    refine' ⟨F.preimage s.retraction, _⟩
    apply F.map_injective
    simp only [map_comp, image_preimage, map_id]
    apply split_mono.id
  left_inv := by tidy
  right_inv := by tidy
#align category_theory.functor.split_mono_equiv CategoryTheory.Functor.splitMonoEquiv

/- warning: category_theory.functor.is_split_mono_iff -> CategoryTheory.Functor.isSplitMono_iff is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_4 : CategoryTheory.Full.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Iff (CategoryTheory.IsSplitMono.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f)) (CategoryTheory.IsSplitMono.{u1, u3} C _inst_1 X Y f)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [_inst_4 : CategoryTheory.Full.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [_inst_5 : CategoryTheory.Faithful.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Iff (CategoryTheory.IsSplitMono.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f)) (CategoryTheory.IsSplitMono.{u1, u3} C _inst_1 X Y f)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.is_split_mono_iff CategoryTheory.Functor.isSplitMono_iffₓ'. -/
@[simp]
theorem isSplitMono_iff [Full F] [Faithful F] : IsSplitMono (F.map f) ↔ IsSplitMono f :=
  by
  constructor
  · intro h
    exact is_split_mono.mk' ((split_mono_equiv F f).invFun h.exists_split_mono.some)
  · intro h
    exact is_split_mono.mk' ((split_mono_equiv F f).toFun h.exists_split_mono.some)
#align category_theory.functor.is_split_mono_iff CategoryTheory.Functor.isSplitMono_iff

/- warning: category_theory.functor.epi_map_iff_epi -> CategoryTheory.Functor.epi_map_iff_epi is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [hF₁ : CategoryTheory.Functor.PreservesEpimorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [hF₂ : CategoryTheory.Functor.ReflectsEpimorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Iff (CategoryTheory.Epi.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f)) (CategoryTheory.Epi.{u1, u3} C _inst_1 X Y f)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [hF₁ : CategoryTheory.Functor.PreservesEpimorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [hF₂ : CategoryTheory.Functor.ReflectsEpimorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Iff (CategoryTheory.Epi.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f)) (CategoryTheory.Epi.{u1, u3} C _inst_1 X Y f)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.epi_map_iff_epi CategoryTheory.Functor.epi_map_iff_epiₓ'. -/
@[simp]
theorem epi_map_iff_epi [hF₁ : PreservesEpimorphisms F] [hF₂ : ReflectsEpimorphisms F] :
    Epi (F.map f) ↔ Epi f := by
  constructor
  · exact F.epi_of_epi_map
  · intro h
    exact F.map_epi f
#align category_theory.functor.epi_map_iff_epi CategoryTheory.Functor.epi_map_iff_epi

/- warning: category_theory.functor.mono_map_iff_mono -> CategoryTheory.Functor.mono_map_iff_mono is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [hF₁ : CategoryTheory.Functor.PreservesMonomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [hF₂ : CategoryTheory.Functor.ReflectsMonomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Iff (CategoryTheory.Mono.{u2, u4} D _inst_2 (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X Y f)) (CategoryTheory.Mono.{u1, u3} C _inst_1 X Y f)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u3} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) X Y) [hF₁ : CategoryTheory.Functor.PreservesMonomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F] [hF₂ : CategoryTheory.Functor.ReflectsMonomorphisms.{u1, u2, u3, u4} C _inst_1 D _inst_2 F], Iff (CategoryTheory.Mono.{u2, u4} D _inst_2 (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 D _inst_2 F) X Y f)) (CategoryTheory.Mono.{u1, u3} C _inst_1 X Y f)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.mono_map_iff_mono CategoryTheory.Functor.mono_map_iff_monoₓ'. -/
@[simp]
theorem mono_map_iff_mono [hF₁ : PreservesMonomorphisms F] [hF₂ : ReflectsMonomorphisms F] :
    Mono (F.map f) ↔ Mono f := by
  constructor
  · exact F.mono_of_mono_map
  · intro h
    exact F.map_mono f
#align category_theory.functor.mono_map_iff_mono CategoryTheory.Functor.mono_map_iff_mono

#print CategoryTheory.Functor.splitEpiCategoryImpOfIsEquivalence /-
/-- If `F : C ⥤ D` is an equivalence of categories and `C` is a `split_epi_category`,
then `D` also is. -/
def splitEpiCategoryImpOfIsEquivalence [IsEquivalence F] [SplitEpiCategory C] :
    SplitEpiCategory D :=
  ⟨fun X Y f => by
    intro
    rw [← F.inv.is_split_epi_iff f]
    apply is_split_epi_of_epi⟩
#align category_theory.functor.split_epi_category_imp_of_is_equivalence CategoryTheory.Functor.splitEpiCategoryImpOfIsEquivalence
-/

end

end CategoryTheory.Functor

namespace CategoryTheory.Adjunction

variable {C D : Type _} [Category C] [Category D] {F : C ⥤ D} {F' : D ⥤ C} {A B : C}

/- warning: category_theory.adjunction.strong_epi_map_of_strong_epi -> CategoryTheory.Adjunction.strongEpi_map_of_strongEpi is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u1}} {D : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u3, u1} C] [_inst_2 : CategoryTheory.Category.{u4, u2} D] {F : CategoryTheory.Functor.{u3, u4, u1, u2} C _inst_1 D _inst_2} {F' : CategoryTheory.Functor.{u4, u3, u2, u1} D _inst_2 C _inst_1} {A : C} {B : C}, (CategoryTheory.Adjunction.{u3, u4, u1, u2} C _inst_1 D _inst_2 F F') -> (forall (f : Quiver.Hom.{succ u3, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} C (CategoryTheory.Category.toCategoryStruct.{u3, u1} C _inst_1)) A B) [h₁ : CategoryTheory.Functor.PreservesMonomorphisms.{u4, u3, u2, u1} D _inst_2 C _inst_1 F'] [h₂ : CategoryTheory.Functor.PreservesEpimorphisms.{u3, u4, u1, u2} C _inst_1 D _inst_2 F] [_inst_3 : CategoryTheory.StrongEpi.{u3, u1} C _inst_1 A B f], CategoryTheory.StrongEpi.{u4, u2} D _inst_2 (CategoryTheory.Functor.obj.{u3, u4, u1, u2} C _inst_1 D _inst_2 F A) (CategoryTheory.Functor.obj.{u3, u4, u1, u2} C _inst_1 D _inst_2 F B) (CategoryTheory.Functor.map.{u3, u4, u1, u2} C _inst_1 D _inst_2 F A B f))
but is expected to have type
  forall {C : Type.{u2}} {D : Type.{u1}} [_inst_1 : CategoryTheory.Category.{u4, u2} C] [_inst_2 : CategoryTheory.Category.{u3, u1} D] {F : CategoryTheory.Functor.{u4, u3, u2, u1} C _inst_1 D _inst_2} {F' : CategoryTheory.Functor.{u3, u4, u1, u2} D _inst_2 C _inst_1} {A : C} {B : C}, (CategoryTheory.Adjunction.{u4, u3, u2, u1} C _inst_1 D _inst_2 F F') -> (forall (f : Quiver.Hom.{succ u4, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} C (CategoryTheory.Category.toCategoryStruct.{u4, u2} C _inst_1)) A B) [h₁ : CategoryTheory.Functor.PreservesMonomorphisms.{u3, u4, u1, u2} D _inst_2 C _inst_1 F'] [h₂ : CategoryTheory.Functor.PreservesEpimorphisms.{u4, u3, u2, u1} C _inst_1 D _inst_2 F] [_inst_3 : CategoryTheory.StrongEpi.{u4, u2} C _inst_1 A B f], CategoryTheory.StrongEpi.{u3, u1} D _inst_2 (Prefunctor.obj.{succ u4, succ u3, u2, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} C (CategoryTheory.Category.toCategoryStruct.{u4, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} D (CategoryTheory.Category.toCategoryStruct.{u3, u1} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u4, u3, u2, u1} C _inst_1 D _inst_2 F) A) (Prefunctor.obj.{succ u4, succ u3, u2, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} C (CategoryTheory.Category.toCategoryStruct.{u4, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} D (CategoryTheory.Category.toCategoryStruct.{u3, u1} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u4, u3, u2, u1} C _inst_1 D _inst_2 F) B) (Prefunctor.map.{succ u4, succ u3, u2, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} C (CategoryTheory.Category.toCategoryStruct.{u4, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} D (CategoryTheory.Category.toCategoryStruct.{u3, u1} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u4, u3, u2, u1} C _inst_1 D _inst_2 F) A B f))
Case conversion may be inaccurate. Consider using '#align category_theory.adjunction.strong_epi_map_of_strong_epi CategoryTheory.Adjunction.strongEpi_map_of_strongEpiₓ'. -/
theorem strongEpi_map_of_strongEpi (adj : F ⊣ F') (f : A ⟶ B) [h₁ : F'.PreservesMonomorphisms]
    [h₂ : F.PreservesEpimorphisms] [StrongEpi f] : StrongEpi (F.map f) :=
  ⟨inferInstance, fun X Y Z => by
    intro
    rw [adj.has_lifting_property_iff]
    infer_instance⟩
#align category_theory.adjunction.strong_epi_map_of_strong_epi CategoryTheory.Adjunction.strongEpi_map_of_strongEpi

/- warning: category_theory.adjunction.strong_epi_map_of_is_equivalence -> CategoryTheory.Adjunction.strongEpi_map_of_isEquivalence is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u1}} {D : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u3, u1} C] [_inst_2 : CategoryTheory.Category.{u4, u2} D] {F : CategoryTheory.Functor.{u3, u4, u1, u2} C _inst_1 D _inst_2} {A : C} {B : C} [_inst_3 : CategoryTheory.IsEquivalence.{u3, u4, u1, u2} C _inst_1 D _inst_2 F] (f : Quiver.Hom.{succ u3, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} C (CategoryTheory.Category.toCategoryStruct.{u3, u1} C _inst_1)) A B) [h : CategoryTheory.StrongEpi.{u3, u1} C _inst_1 A B f], CategoryTheory.StrongEpi.{u4, u2} D _inst_2 (CategoryTheory.Functor.obj.{u3, u4, u1, u2} C _inst_1 D _inst_2 F A) (CategoryTheory.Functor.obj.{u3, u4, u1, u2} C _inst_1 D _inst_2 F B) (CategoryTheory.Functor.map.{u3, u4, u1, u2} C _inst_1 D _inst_2 F A B f)
but is expected to have type
  forall {C : Type.{u1}} {D : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u3, u1} C] [_inst_2 : CategoryTheory.Category.{u4, u2} D] {F : CategoryTheory.Functor.{u3, u4, u1, u2} C _inst_1 D _inst_2} {A : C} {B : C} [_inst_3 : CategoryTheory.IsEquivalence.{u3, u4, u1, u2} C _inst_1 D _inst_2 F] (f : Quiver.Hom.{succ u3, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} C (CategoryTheory.Category.toCategoryStruct.{u3, u1} C _inst_1)) A B) [h : CategoryTheory.StrongEpi.{u3, u1} C _inst_1 A B f], CategoryTheory.StrongEpi.{u4, u2} D _inst_2 (Prefunctor.obj.{succ u3, succ u4, u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} C (CategoryTheory.Category.toCategoryStruct.{u3, u1} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} D (CategoryTheory.Category.toCategoryStruct.{u4, u2} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u3, u4, u1, u2} C _inst_1 D _inst_2 F) A) (Prefunctor.obj.{succ u3, succ u4, u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} C (CategoryTheory.Category.toCategoryStruct.{u3, u1} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} D (CategoryTheory.Category.toCategoryStruct.{u4, u2} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u3, u4, u1, u2} C _inst_1 D _inst_2 F) B) (Prefunctor.map.{succ u3, succ u4, u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} C (CategoryTheory.Category.toCategoryStruct.{u3, u1} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} D (CategoryTheory.Category.toCategoryStruct.{u4, u2} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u3, u4, u1, u2} C _inst_1 D _inst_2 F) A B f)
Case conversion may be inaccurate. Consider using '#align category_theory.adjunction.strong_epi_map_of_is_equivalence CategoryTheory.Adjunction.strongEpi_map_of_isEquivalenceₓ'. -/
instance strongEpi_map_of_isEquivalence [IsEquivalence F] (f : A ⟶ B) [h : StrongEpi f] :
    StrongEpi (F.map f) :=
  F.asEquivalence.toAdjunction.strongEpi_map_of_strongEpi f
#align category_theory.adjunction.strong_epi_map_of_is_equivalence CategoryTheory.Adjunction.strongEpi_map_of_isEquivalence

end CategoryTheory.Adjunction

namespace CategoryTheory.Functor

variable {C D : Type _} [Category C] [Category D] {F : C ⥤ D} {A B : C} (f : A ⟶ B)

/- warning: category_theory.functor.strong_epi_map_iff_strong_epi_of_is_equivalence -> CategoryTheory.Functor.strongEpi_map_iff_strongEpi_of_isEquivalence is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u1}} {D : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u3, u1} C] [_inst_2 : CategoryTheory.Category.{u4, u2} D] {F : CategoryTheory.Functor.{u3, u4, u1, u2} C _inst_1 D _inst_2} {A : C} {B : C} (f : Quiver.Hom.{succ u3, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} C (CategoryTheory.Category.toCategoryStruct.{u3, u1} C _inst_1)) A B) [_inst_3 : CategoryTheory.IsEquivalence.{u3, u4, u1, u2} C _inst_1 D _inst_2 F], Iff (CategoryTheory.StrongEpi.{u4, u2} D _inst_2 (CategoryTheory.Functor.obj.{u3, u4, u1, u2} C _inst_1 D _inst_2 F A) (CategoryTheory.Functor.obj.{u3, u4, u1, u2} C _inst_1 D _inst_2 F B) (CategoryTheory.Functor.map.{u3, u4, u1, u2} C _inst_1 D _inst_2 F A B f)) (CategoryTheory.StrongEpi.{u3, u1} C _inst_1 A B f)
but is expected to have type
  forall {C : Type.{u2}} {D : Type.{u1}} [_inst_1 : CategoryTheory.Category.{u4, u2} C] [_inst_2 : CategoryTheory.Category.{u3, u1} D] {F : CategoryTheory.Functor.{u4, u3, u2, u1} C _inst_1 D _inst_2} {A : C} {B : C} (f : Quiver.Hom.{succ u4, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} C (CategoryTheory.Category.toCategoryStruct.{u4, u2} C _inst_1)) A B) [_inst_3 : CategoryTheory.IsEquivalence.{u4, u3, u2, u1} C _inst_1 D _inst_2 F], Iff (CategoryTheory.StrongEpi.{u3, u1} D _inst_2 (Prefunctor.obj.{succ u4, succ u3, u2, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} C (CategoryTheory.Category.toCategoryStruct.{u4, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} D (CategoryTheory.Category.toCategoryStruct.{u3, u1} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u4, u3, u2, u1} C _inst_1 D _inst_2 F) A) (Prefunctor.obj.{succ u4, succ u3, u2, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} C (CategoryTheory.Category.toCategoryStruct.{u4, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} D (CategoryTheory.Category.toCategoryStruct.{u3, u1} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u4, u3, u2, u1} C _inst_1 D _inst_2 F) B) (Prefunctor.map.{succ u4, succ u3, u2, u1} C (CategoryTheory.CategoryStruct.toQuiver.{u4, u2} C (CategoryTheory.Category.toCategoryStruct.{u4, u2} C _inst_1)) D (CategoryTheory.CategoryStruct.toQuiver.{u3, u1} D (CategoryTheory.Category.toCategoryStruct.{u3, u1} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u4, u3, u2, u1} C _inst_1 D _inst_2 F) A B f)) (CategoryTheory.StrongEpi.{u4, u2} C _inst_1 A B f)
Case conversion may be inaccurate. Consider using '#align category_theory.functor.strong_epi_map_iff_strong_epi_of_is_equivalence CategoryTheory.Functor.strongEpi_map_iff_strongEpi_of_isEquivalenceₓ'. -/
@[simp]
theorem strongEpi_map_iff_strongEpi_of_isEquivalence [IsEquivalence F] :
    StrongEpi (F.map f) ↔ StrongEpi f := by
  constructor
  · intro
    have e : arrow.mk f ≅ arrow.mk (F.inv.map (F.map f)) :=
      arrow.iso_of_nat_iso F.as_equivalence.unit_iso (arrow.mk f)
    rw [strong_epi.iff_of_arrow_iso e]
    infer_instance
  · intro
    infer_instance
#align category_theory.functor.strong_epi_map_iff_strong_epi_of_is_equivalence CategoryTheory.Functor.strongEpi_map_iff_strongEpi_of_isEquivalence

end CategoryTheory.Functor


import ring_theory.noetherian
import ring_theory.polynomial.basic
import ring_theory.adjoin_root
import ring_theory.norm
import data.zmod.basic
import ring_theory.dedekind_domain.ideal
import linear_algebra.free_module.finite.basic
import ring_theory.class_group
import ring_theory.principal_ideal_domain
import tactic.slim_check
.

/-! This is essentially one big for_mathlib file
-/

/-! # Parity stuff-/
section parity

-- @[parity_simps]
lemma nat.coprime_two_iff_odd (n : ℕ) : n.coprime 2 ↔ ¬ even n :=
by rw [nat.coprime_comm, nat.prime.coprime_iff_not_dvd nat.prime_two,
    nat.two_dvd_ne_zero, nat.not_even_iff]
  -- simp only [nat.two_dvd_ne_zero, nat.not_even_iff] with parity_simps,
  -- TODO squeeze simp with doesn't remove the with?
-- @[parity_simps]
lemma nat.two_coprime_iff_odd (n : ℕ) : (2 : ℕ).coprime n ↔ ¬ even n :=
by rw [nat.coprime_comm, nat.coprime_two_iff_odd]

@[parity_simps]
lemma nat.coprime_bit0 (n m : ℕ) : n.coprime (bit0 m) ↔ ¬ even n ∧ n.coprime m :=
by rw [bit0_eq_two_mul, nat.coprime_mul_iff_right, nat.coprime_two_iff_odd]

@[parity_simps]
lemma nat.bit0_coprime (n m : ℕ) : (bit0 n).coprime m ↔ ¬ even m ∧ n.coprime m :=
by rw [nat.coprime_comm, nat.coprime_bit0, nat.coprime_comm]

example : ¬ (8 : ℕ).coprime 6 := by simp with parity_simps
example : (8 : ℕ).coprime 9 := by simp with parity_simps

end parity

/-! # zeroness of zmod powers-/
section pow_zmod_nat
variables {n m : ℕ} (e : ℕ)

-- TODO think if any generalized version of this with exponents in hyp would be useful

lemma pow_zmod_nat (h : (m : zmod n) = 0) : (m ^ e : zmod (n ^ e)) = 0 :=
begin
  norm_cast,
  rw zmod.nat_coe_zmod_eq_zero_iff_dvd at *,
  exact pow_dvd_pow_of_dvd h e,
end
end pow_zmod_nat
section pow_zmod_int
variables {n : ℕ} {m : ℤ} (e : ℕ)

lemma pow_zmod_int (h : (m : zmod n) = 0) : (m ^ e : zmod (n ^ e)) = 0 :=
begin
  norm_cast,
  rw zmod.int_coe_zmod_eq_zero_iff_dvd at *,
  push_cast,
  exact pow_dvd_pow_of_dvd h e,
end

end pow_zmod_int

section norm_zero
namespace algebra
lemma norm_zero_of_basis {R S ι : Type*} [comm_ring R] [comm_ring S] [algebra R S] [nontrivial S]
  [fintype ι] (b : basis ι R S) : algebra.norm R (0 : S) = 0 :=
begin
  have hι : nonempty ι := b.index_nonempty,
  letI := classical.dec_eq ι,
  rw algebra.norm_eq_matrix_det b,
  rw [alg_hom.map_zero, matrix.det_zero hι],
end

@[simp]
lemma norm_zero {R S : Type*} [comm_ring R] [comm_ring S] [algebra R S] [nontrivial R]
  [nontrivial S] [_root_.module.finite R S] [_root_.module.free R S] : algebra.norm R (0 : S) = 0 :=
norm_zero_of_basis (module.free.choose_basis R S)

end algebra
end norm_zero

section int_squarefree
lemma int.squarefree_iff_squarefree_nat_abs {z : ℤ} : squarefree z ↔ squarefree (int.nat_abs z) :=
begin
  rw [squarefree, squarefree],
  split; intros h x hx,
  { specialize h x _, norm_cast, rw ← int.coe_nat_dvd_left at hx, exact hx,
    norm_cast at h, exact h },
  { specialize h x.nat_abs _, rw ← int.nat_abs_mul, simpa, simp [nat.is_unit_iff] at h,
    exact int.is_unit_iff_nat_abs_eq.mpr h, }
end
-- namespace norm_num
-- open norm_num
-- open tactic
-- TODO try to get this working
-- @[norm_num] meta def eval_squarefree_int : expr → tactic (expr × expr)
-- | `(squarefree (%%e : ℤ)) := do
--   trace "hi mom",
--   return (`(squarefree (int.nat_abs (%%e : int))), `((@int.squarefree_iff_squarefree_nat_abs %%e).symm))
-- | _ := failed
-- end norm_num

end int_squarefree

section irred

lemma irreducible_of_map_irreducible {α β : Type*} [monoid α] [monoid β] [unique βˣ] {p : α}
  {F : Type*} [monoid_hom_class F α β] (f : F) (hf : ∀ x, f x = 1 → x = 1)
  (h : irreducible $ f p) : irreducible p :=
{ not_unit := λ hn, h.not_unit (hn.map f),
  is_unit_or_is_unit' := λ a b hab, begin
    have := map_mul f a b,
    rw ← hab at this,
    apply or.imp _ _ (h.is_unit_or_is_unit this);
    rintro ⟨ua, ha⟩;
    [rw hf a, rw hf b];
    { exact is_unit_one } <|>
    { simp [← ha], },
  end }

end irred

section zero_generator

open ideal
section noncomm
variables {R : Type*} [ring R]

-- TODO maybe submodule version

@[simp] lemma span_zero' : span ({0} : set R) = ⊥ := by simp
@[simp] lemma span_one : span ({1} : set R) = ⊤ := by simp
instance : submodule.is_principal (⊥ : ideal R) :=
{ principal := ⟨0, by {rw ← submodule.span_zero, refl}⟩ }
instance {a : R} : submodule.is_principal (span ({a} : set R)) := { principal := ⟨a, rfl⟩ }

@[simp]
lemma generator_bot : submodule.is_principal.generator (⊥ : ideal R) = 0 :=
begin
  apply mem_bot.mp,
  exact submodule.is_principal.generator_mem _,
end

lemma generator_zero : submodule.is_principal.generator (span ({0} : set R)) = 0 :=
by simp

open submodule.is_principal

lemma span_generator' (a : R) :
  ideal.span ({(generator (span ({a} : set R)))} : set R) = ideal.span {a} :=
ideal.span_singleton_generator _
end noncomm

section comm
variables {R : Type*} [comm_ring R]
open submodule.is_principal

local infix ` ~ᵤ ` : 50 := associated

-- TODO is the domain assumption needed?
lemma associated_generator_span_singleton [is_domain R] (a : R) :
  generator (span ({a} : set R)) ~ᵤ a := -- TODO notation for associated?
begin
  apply associated_of_dvd_dvd; rw ← mem_span_singleton,
  rw ideal.span_singleton_generator,
  exact submodule.mem_span_singleton_self _, -- maybe also an ideal version needed
  exact generator_mem _, -- TODO need ideal version for library search
end

-- TODO generalize these two
lemma norm_unit (u : Rˣ) : is_unit $ algebra.norm ℤ (u : R) :=
begin
  apply is_unit_of_mul_eq_one _ (algebra.norm ℤ (↑u⁻¹ : R)),
  simp [← _root_.map_mul],
end

-- TODO generalize these two
lemma norm_associated_of_associated {a b : R} (h : associated a b) :
  associated (algebra.norm ℤ a) (algebra.norm ℤ b) :=
begin
  obtain ⟨u, hu⟩ := h,
  apply_fun (algebra.norm ℤ) at hu,
  rw _root_.map_mul at hu,
  obtain ⟨Nu, hNu⟩ := norm_unit u,
  use Nu,
  rwa hNu,
end

end comm

end zero_generator

section top_generator

variables (R : Type*) [comm_ring R]
open submodule.is_principal

lemma is_unit_generator_top : is_unit (generator (⊤ : ideal R)) :=
is_unit_of_dvd_one (generator ⊤) $ (mem_iff_generator_dvd ⊤).mp submodule.mem_top

end top_generator
section class_group_span_singleton

@[simp]
lemma class_group.mk0_span_singleton {R K : Type*} [comm_ring R] [field K] [algebra R K]
  [is_fraction_ring R K] [is_domain R] [is_dedekind_domain R] (x : R) (h) :
  class_group.mk0 ⟨ideal.span ({x} : set R), h⟩ = 1 :=
begin
  rw class_group.mk0_eq_one_iff,
  exact ideal.span.submodule.is_principal,
end
end class_group_span_singleton

section mk0_pow
lemma units.mk0_pow {G₀ : Type*} [group_with_zero G₀] (x : G₀) {n : ℕ} (hn : 0 < n) (hxy) :
  units.mk0 (x ^ n) hxy = (units.mk0 x (λ h, hxy (by simp only [h, zero_pow_eq_zero, hn]))) ^ n :=
by { ext, simp, }
end mk0_pow

section poly_lemma
open_locale polynomial
open polynomial

-- TODO cleanup, maybe a general version for other powers less than multiplicity?
-- TODO probably delete as dup of mul_div_by_monic_eq_iff_is_root
lemma polynomial.div_by_monic_mul_eq {R : Type*} [comm_ring R] [is_domain R]
  (p : R[X]) (a : R) (ha : a ∈ p.roots) :
  p /ₘ (X - C a) * (X - C a) = p :=
have monic (X - C a), from (monic_X_sub_C _),
by conv_rhs { rw [← mod_by_monic_add_div p this,
    (dvd_iff_mod_by_monic_eq_zero this).2
    ((dvd_pow_self _ begin have := is_root_of_mem_roots ha,
        rw ← root_multiplicity_pos at this, exact this.ne.symm,
        intro hp, simpa [hp] using ha, end).trans (pow_root_multiplicity_dvd _ _))] };
  simp [mul_comm]

lemma polynomial.not_is_unit_of_nat_degree_pos {R : Type*} [comm_ring R] [is_domain R]
  (p : R[X]) (hpl : 0 < p.nat_degree) : ¬ is_unit p :=
begin
  intro h,
  have : p.nat_degree = 0,
  { simp [polynomial.nat_degree_eq_zero_iff_degree_le_zero,
      polynomial.degree_eq_zero_of_is_unit h], },
  simpa [this] using hpl,
end

lemma polynomial.irreducible_iff_roots_empty_of_degree_le_three {R : Type*} [field R]
  {p : R[X]} (hp : p.nat_degree ≤ 3) (hpl : 2 ≤ p.nat_degree) : irreducible p ↔ p.roots = 0 :=
begin
  have hpz : p ≠ 0 := polynomial.ne_zero_of_nat_degree_gt hpl,
  have hpu : ¬ is_unit p := p.not_is_unit_of_nat_degree_pos (pos_of_gt hpl),
  rw irreducible_iff,
  simp only [hpu, not_false_iff, true_and],
  split,
  { intro h,
    contrapose! h,
    obtain ⟨r, hr⟩ := multiset.exists_mem_of_ne_zero h,
    -- rw ← polynomial.div_by_monic_mul_pow_root_multiplicity_eq p r,
    refine ⟨p /ₘ (X - C r), X - C r, _, _, _⟩,
    { conv_lhs
      { rw [← mul_div_by_monic_eq_iff_is_root.mpr (is_root_of_mem_roots hr), mul_comm] }, },
    { apply polynomial.not_is_unit_of_nat_degree_pos,
      rw nat_degree_div_by_monic _ (monic_X_sub_C r), -- TODO why is there no degree version? is it false?
      simpa using hpl, },
    { apply polynomial.not_is_unit_of_nat_degree_pos,
      simp, }, },
  { rintro h a b rfl,
    simp only [not_or_distrib, ne.def, mul_eq_zero] at hpz,
    rw polynomial.nat_degree_mul hpz.1 hpz.2 at hp,
    -- sad wlog
    have key : ∀ (A B : R[X]) (Hpl : 2 ≤ (A * B).nat_degree) (Hpu : ¬is_unit (A * B))
      (H : (A * B).roots = 0) (Hpz : ¬A = 0 ∧ ¬B = 0) (Hp : A.nat_degree + B.nat_degree ≤ 3)
      (this : 1 ≤ A.nat_degree),
      is_unit A ∨ is_unit B,
    { clear_except,
      intros A B Hpl Hpu H Hpz Hp this,
      have : A.nat_degree ≤ 3 := le_of_add_le_left Hp,
      interval_cases A.nat_degree with H_eq,
      { exfalso,
        have : A.roots ≠ 0,
        { convert roots_ne_zero_of_splits (ring_hom.id _) (splits_of_nat_degree_eq_one _ H_eq)
            (by simp [H_eq]), -- TODO this probably should be simpler
          simp, },
        rw [roots_mul (mul_ne_zero Hpz.1 Hpz.2), add_eq_zero_iff] at H,
        exact this H.1, },
      { -- TODO deduplicate these somehow
        simp only [H_eq, is_unit.mul_iff, not_and, le_refl, nat.bit0_le_bit1_iff, eq_self_iff_true,
          nat.one_le_bit0_iff, nat.lt_one_iff] at *,
        rw ← le_tsub_iff_left (by norm_num : 2 ≤ 3) at Hp,
        norm_num at Hp,
        rw or_iff_not_imp_right,
        intro HB,
        exfalso,
        have : B.nat_degree = 1,
        { rw [is_unit_iff_degree_eq_zero, degree_eq_nat_degree Hpz.2, with_top.coe_eq_zero] at HB,
          interval_cases B.nat_degree with Hi,
          { simpa [Hi] using HB, },
          { simp [Hi], }, },
        have : B.roots ≠ 0,
        { convert roots_ne_zero_of_splits (ring_hom.id _) (splits_of_nat_degree_le_one _ Hp)
            (by simp [this]), -- TODO this probably should be simpler
          simp, },
        rw [roots_mul (mul_ne_zero Hpz.1 Hpz.2), add_eq_zero_iff] at H,
        exact this H.2, },
      { simp only [H_eq, ne.def, mul_eq_zero, is_unit.mul_iff, not_and, le_refl,
          nat.bit0_le_bit1_iff, eq_self_iff_true, add_le_iff_nonpos_right, le_zero_iff,
          polynomial.is_unit_iff_degree_eq_zero, not_or_distrib] at ⊢ Hp,
        right,
        rw [polynomial.degree_eq_nat_degree Hpz.2, Hp, enat.coe_zero], }, },
    have : 1 ≤ a.nat_degree ∨ 1 ≤ b.nat_degree, -- TODO this should be a lemma, with ceil (d / 2)
    { rw [nat_degree_mul hpz.1 hpz.2] at hpl,
      contrapose! hpl,
      linarith, },
    cases this,
    exact key a b hpl hpu h hpz hp this,
    exact (key b a (by simpa [mul_comm] using hpl) (by simpa [mul_comm] using hpu)
      (by simpa [mul_comm] using h) hpz.symm (by simpa [add_comm] using hp) this).symm, },
end

end poly_lemma

section unique_unit
@[simp] lemma is_unit_iff_eq_one {M : Type*} [monoid M] [hu : unique Mˣ] (m : M) :
  is_unit m ↔ m = 1 :=
begin
  split; intro h,
  { lift m to Mˣ using h,
    simp, },
  { simp [h], },
end
end unique_unit

section ring_equiv

@[simp] -- TODO which way is simp, also in the library?
lemma ring_equiv.to_equiv_symm {R S : Type*} [semiring R] [semiring S] (f : R ≃+* S) :
  (f : R ≃ S).symm = (f.symm : S ≃ R) := rfl

end ring_equiv

section dvd_sub_self

variables {α : Type*} [ring α]

theorem dvd_sub_iff_left {a b c : α} (h : a ∣ c) : a ∣ b ↔ a ∣ b - c :=
⟨λh₂, dvd_sub h₂ h, λH, by have t := dvd_add H h; rwa sub_add_cancel at t⟩

theorem dvd_sub_iff_right {a b c : α} (h : a ∣ b) : a ∣ c ↔ a ∣ b - c :=
⟨λh₂, dvd_sub h h₂, λH, by have t := dvd_sub h H; rwa [← sub_add, sub_self, zero_add] at t⟩

/-- If an element a divides another element c in a commutative ring, a divides the sum of another
  element b with c iff a divides b. -/
theorem dvd_sub_left {a b c : α} (h : a ∣ c) : a ∣ b - c ↔ a ∣ b :=
(dvd_sub_iff_left h).symm

/-- If an element a divides another element b in a commutative ring, a divides the sum of b and
  another element c iff a divides c. -/
theorem dvd_sub_right {a b c : α} (h : a ∣ b) : a ∣ b - c ↔ a ∣ c :=
(dvd_sub_iff_right h).symm

/-- An element a divides the difference a - b if and only if a divides b.-/
@[simp] lemma dvd_sub_self_left {a b : α} : a ∣ a - b ↔ a ∣ b :=
dvd_sub_right (dvd_refl a)

/-- An element a divides the difference b - a if and only if a divides b.-/
@[simp] lemma dvd_sub_self_right {a b : α} : a ∣ b - a ↔ a ∣ b :=
dvd_sub_left (dvd_refl a)

end dvd_sub_self

section zmod_reduced

instance {n : ℕ} [fact $ squarefree n] : is_reduced (zmod n) :=
⟨begin
  casesI n,
  { exfalso,
    apply not_squarefree_zero _inst_1.out, },
  rintro ⟨x, hx⟩ ⟨_ | m, h⟩,
  { rw [pow_zero, fin.one_eq_zero_iff] at h,
    rw [h, nat.lt_one_iff] at hx,
    simp only [hx, fin.mk_zero], },
  { have : ((⟨x, hx⟩ : zmod n.succ) = (x : zmod n.succ)),
    { ext,
      simp only [fin.coe_mk, fin.coe_of_nat_eq_mod],
      exact (nat.mod_eq_of_lt hx).symm, },
    rw this at h ⊢,
    norm_cast at h,
    rw zmod.nat_coe_zmod_eq_zero_iff_dvd at h ⊢,
    rwa unique_factorization_monoid.dvd_pow_iff_dvd_of_squarefree at h,
    exact fact.out _,
    exact ne_zero.ne _, }
end⟩

end zmod_reduced

section squarefree_gcd_of_squarefree

variables {α : Type*} [cancel_comm_monoid_with_zero α] [gcd_monoid α]

lemma squarefree_gcd_of_squarefree_right {a b : α} (hb : squarefree b) :
  squarefree (gcd a b) :=
λ x hx, hb x $ hx.trans $ gcd_dvd_right _ _

lemma squarefree_gcd_of_squarefree_left {a b : α} (ha : squarefree a) :
  squarefree (gcd a b) :=
λ x hx, ha x $ hx.trans $ gcd_dvd_left _ _

end squarefree_gcd_of_squarefree

@[simp]
lemma int.is_unit_coe_nat {n : ℕ} : is_unit (n : ℤ) ↔ n = 1 :=
by rw [int.is_unit_iff_nat_abs_eq, int.nat_abs_of_nat]

instance : Π (n : ℕ), slim_check.sampleable (zmod n)
| 0 := show slim_check.sampleable ℤ, by apply_instance
| (n+1) := show slim_check.sampleable (fin (n + 1)), by apply_instance

lemma is_square.nonneg {α : Type*} [linear_ordered_field α] {a : α} (h : is_square a) : 0 ≤ a :=
begin
  rcases h with ⟨h_w, rfl⟩,
  exact mul_self_nonneg h_w,
end


inductive Mem (a : α) : List α → Prop where
  | head {as} : Mem a (a::as)
  | tail {as} : Mem a as → Mem a (a'::as)

infix:50 (priority := high) " ∈ " => Mem

example (a b : Nat) (h : a ∈ [b]) : b = a :=
  match h with
  | Mem.head => rfl

example {as : List α} (h : a ∈ b :: as) : b = a ∨ a ∈ as :=
  match h with
  | Mem.head    => Or.inl rfl
  | Mem.tail h' => Or.inr h'

example (a b : Nat) (h : a.succ.succ = b.succ.succ.succ) : a = b.succ :=
  match h with
  | rfl => rfl

inductive Vec (α : Type u) : Nat → Type u where
  | nil  : Vec α 0
  | cons : α → Vec α n → Vec α (n + 1)

def hv (xs : Vec Nat (n+1)) : Nat :=
  match xs with
  | Vec.cons a .. => a

def addHead (p : Vec Nat n × Vec Nat n) : Nat :=
  match p with
  | (Vec.cons a _, Vec.cons b _) => a + b
  | (Vec.nil, Vec.nil) => 0

inductive HVec : {n : Nat} → Vec (Type u) n → Type (u+1)
  | nil  : HVec Vec.nil
  | cons : {αs : Vec (Type u) n} → α → HVec αs → HVec (Vec.cons α αs)

abbrev HVec.TypeHead {αs : Vec (Type u) n} (xs : HVec αs) : Type u :=
  match xs with
  | HVec.nil => PUnit
  | HVec.cons (α := α) .. => α

def HVec.head {αs : Vec (Type u) n} (xs : HVec αs) : TypeHead xs :=
  match xs with
  | HVec.cons a _ => a
  | HVec.nil      => PUnit.unit


/-
Copyright (c) 2018 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Johannes Hölzl

Continuous linear functions -- functions between normed vector spaces which are bounded and linear.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.analysis.normed_space.multilinear
import Mathlib.PostPort

universes u_5 u_6 u_7 l u_1 u_2 u_3 u_4

namespace Mathlib

/-- A function `f` satisfies `is_bounded_linear_map 𝕜 f` if it is linear and satisfies the
inequality `∥ f x ∥ ≤ M * ∥ x ∥` for some positive constant `M`. -/
structure is_bounded_linear_map (𝕜 : Type u_5) [normed_field 𝕜] {E : Type u_6} [normed_group E] [normed_space 𝕜 E] {F : Type u_7} [normed_group F] [normed_space 𝕜 F] (f : E → F)
extends is_linear_map 𝕜 f
where
  bound : ∃ (M : ℝ), 0 < M ∧ ∀ (x : E), norm (f x) ≤ M * norm x

theorem is_linear_map.with_bound {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (hf : is_linear_map 𝕜 f) (M : ℝ) (h : ∀ (x : E), norm (f x) ≤ M * norm x) : is_bounded_linear_map 𝕜 f := sorry

/-- A continuous linear map satisfies `is_bounded_linear_map` -/
theorem continuous_linear_map.is_bounded_linear_map {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] (f : continuous_linear_map 𝕜 E F) : is_bounded_linear_map 𝕜 ⇑f := sorry

namespace is_bounded_linear_map

/-- Construct a linear map from a function `f` satisfying `is_bounded_linear_map 𝕜 f`. -/
def to_linear_map {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] (f : E → F) (h : is_bounded_linear_map 𝕜 f) : linear_map 𝕜 E F :=
  is_linear_map.mk' f sorry

/-- Construct a continuous linear map from is_bounded_linear_map -/
def to_continuous_linear_map {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (hf : is_bounded_linear_map 𝕜 f) : continuous_linear_map 𝕜 E F :=
  continuous_linear_map.mk (linear_map.mk (linear_map.to_fun (to_linear_map f hf)) sorry sorry)

theorem zero {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] : is_bounded_linear_map 𝕜 fun (x : E) => 0 := sorry

theorem id {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] : is_bounded_linear_map 𝕜 fun (x : E) => x := sorry

theorem fst {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] : is_bounded_linear_map 𝕜 fun (x : E × F) => prod.fst x := sorry

theorem snd {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] : is_bounded_linear_map 𝕜 fun (x : E × F) => prod.snd x := sorry

theorem smul {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (c : 𝕜) (hf : is_bounded_linear_map 𝕜 f) : is_bounded_linear_map 𝕜 fun (e : E) => c • f e := sorry

theorem neg {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (hf : is_bounded_linear_map 𝕜 f) : is_bounded_linear_map 𝕜 fun (e : E) => -f e := sorry

theorem add {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} {g : E → F} (hf : is_bounded_linear_map 𝕜 f) (hg : is_bounded_linear_map 𝕜 g) : is_bounded_linear_map 𝕜 fun (e : E) => f e + g e := sorry

theorem sub {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} {g : E → F} (hf : is_bounded_linear_map 𝕜 f) (hg : is_bounded_linear_map 𝕜 g) : is_bounded_linear_map 𝕜 fun (e : E) => f e - g e := sorry

theorem comp {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E → F} {g : F → G} (hg : is_bounded_linear_map 𝕜 g) (hf : is_bounded_linear_map 𝕜 f) : is_bounded_linear_map 𝕜 (g ∘ f) :=
  continuous_linear_map.is_bounded_linear_map
    (continuous_linear_map.comp (to_continuous_linear_map hg) (to_continuous_linear_map hf))

protected theorem tendsto {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (x : E) (hf : is_bounded_linear_map 𝕜 f) : filter.tendsto f (nhds x) (nhds (f x)) := sorry

theorem continuous {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (hf : is_bounded_linear_map 𝕜 f) : continuous f :=
  iff.mpr continuous_iff_continuous_at fun (_x : E) => is_bounded_linear_map.tendsto _x hf

theorem lim_zero_bounded_linear_map {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (hf : is_bounded_linear_map 𝕜 f) : filter.tendsto f (nhds 0) (nhds 0) :=
  linear_map.map_zero (is_linear_map.mk' f (to_is_linear_map hf)) ▸ iff.mp continuous_iff_continuous_at (continuous hf) 0

theorem is_O_id {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (h : is_bounded_linear_map 𝕜 f) (l : filter E) : asymptotics.is_O f (fun (x : E) => x) l := sorry

theorem is_O_comp {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {E : Type u_2} {g : F → G} (hg : is_bounded_linear_map 𝕜 g) {f : E → F} (l : filter E) : asymptotics.is_O (fun (x' : E) => g (f x')) f l :=
  asymptotics.is_O.comp_tendsto (is_O_id hg ⊤) le_top

theorem is_O_sub {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (h : is_bounded_linear_map 𝕜 f) (l : filter E) (x : E) : asymptotics.is_O (fun (x' : E) => f (x' - x)) (fun (x' : E) => x' - x) l :=
  is_O_comp h l

end is_bounded_linear_map

/-- Taking the cartesian product of two continuous linear maps is a bounded linear operation. -/
theorem is_bounded_linear_map_prod_iso {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] : is_bounded_linear_map 𝕜
  fun (p : continuous_linear_map 𝕜 E F × continuous_linear_map 𝕜 E G) =>
    continuous_linear_map.prod (prod.fst p) (prod.snd p) := sorry

/-- Taking the cartesian product of two continuous multilinear maps is a bounded linear operation. -/
theorem is_bounded_linear_map_prod_multilinear {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {ι : Type u_5} [DecidableEq ι] [fintype ι] {E : ι → Type u_2} [(i : ι) → normed_group (E i)] [(i : ι) → normed_space 𝕜 (E i)] : is_bounded_linear_map 𝕜
  fun (p : continuous_multilinear_map 𝕜 E F × continuous_multilinear_map 𝕜 E G) =>
    continuous_multilinear_map.prod (prod.fst p) (prod.snd p) := sorry

/-- Given a fixed continuous linear map `g`, associating to a continuous multilinear map `f` the
continuous multilinear map `f (g m₁, ..., g mₙ)` is a bounded linear operation. -/
theorem is_bounded_linear_map_continuous_multilinear_map_comp_linear {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {ι : Type u_5} [DecidableEq ι] [fintype ι] (g : continuous_linear_map 𝕜 G E) : is_bounded_linear_map 𝕜
  fun (f : continuous_multilinear_map 𝕜 (fun (i : ι) => E) F) =>
    continuous_multilinear_map.comp_continuous_linear_map f fun (_x : ι) => g := sorry

/-- A map `f : E × F → G` satisfies `is_bounded_bilinear_map 𝕜 f` if it is bilinear and
continuous. -/
structure is_bounded_bilinear_map (𝕜 : Type u_1) [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] (f : E × F → G)
where
  add_left : ∀ (x₁ x₂ : E) (y : F), f (x₁ + x₂, y) = f (x₁, y) + f (x₂, y)
  smul_left : ∀ (c : 𝕜) (x : E) (y : F), f (c • x, y) = c • f (x, y)
  add_right : ∀ (x : E) (y₁ y₂ : F), f (x, y₁ + y₂) = f (x, y₁) + f (x, y₂)
  smul_right : ∀ (c : 𝕜) (x : E) (y : F), f (x, c • y) = c • f (x, y)
  bound : ∃ (C : ℝ), ∃ (H : C > 0), ∀ (x : E) (y : F), norm (f (x, y)) ≤ C * norm x * norm y

protected theorem is_bounded_bilinear_map.is_O {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) : asymptotics.is_O f (fun (p : E × F) => norm (prod.fst p) * norm (prod.snd p)) ⊤ := sorry

theorem is_bounded_bilinear_map.is_O_comp {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} {α : Type u_5} (H : is_bounded_bilinear_map 𝕜 f) {g : α → E} {h : α → F} {l : filter α} : asymptotics.is_O (fun (x : α) => f (g x, h x)) (fun (x : α) => norm (g x) * norm (h x)) l :=
  asymptotics.is_O.comp_tendsto (is_bounded_bilinear_map.is_O H) le_top

protected theorem is_bounded_bilinear_map.is_O' {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) : asymptotics.is_O f (fun (p : E × F) => norm p * norm p) ⊤ :=
  asymptotics.is_O.trans (is_bounded_bilinear_map.is_O h)
    (asymptotics.is_O.mul (asymptotics.is_O.norm_norm asymptotics.is_O_fst_prod')
      (asymptotics.is_O.norm_norm asymptotics.is_O_snd_prod'))

theorem is_bounded_bilinear_map.map_sub_left {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) {x : E} {y : E} {z : F} : f (x - y, z) = f (x, z) - f (y, z) := sorry

theorem is_bounded_bilinear_map.map_sub_right {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) {x : E} {y : F} {z : F} : f (x, y - z) = f (x, y) - f (x, z) := sorry

theorem is_bounded_bilinear_map.is_bounded_linear_map_left {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) (y : F) : is_bounded_linear_map 𝕜 fun (x : E) => f (x, y) := sorry

theorem is_bounded_bilinear_map.is_bounded_linear_map_right {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) (x : E) : is_bounded_linear_map 𝕜 fun (y : F) => f (x, y) := sorry

theorem is_bounded_bilinear_map_smul {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] : is_bounded_bilinear_map 𝕜 fun (p : 𝕜 × E) => prod.fst p • prod.snd p := sorry

theorem is_bounded_bilinear_map_smul_algebra {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {𝕜' : Type u_2} [normed_field 𝕜'] [normed_algebra 𝕜 𝕜'] {E : Type u_3} [normed_group E] [normed_space 𝕜 E] [normed_space 𝕜' E] [is_scalar_tower 𝕜 𝕜' E] : is_bounded_bilinear_map 𝕜 fun (p : 𝕜' × E) => prod.fst p • prod.snd p := sorry

theorem is_bounded_bilinear_map_mul {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] : is_bounded_bilinear_map 𝕜 fun (p : 𝕜 × 𝕜) => prod.fst p * prod.snd p :=
  is_bounded_bilinear_map_smul

theorem is_bounded_bilinear_map_comp {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] : is_bounded_bilinear_map 𝕜
  fun (p : continuous_linear_map 𝕜 E F × continuous_linear_map 𝕜 F G) =>
    continuous_linear_map.comp (prod.snd p) (prod.fst p) := sorry

theorem continuous_linear_map.is_bounded_linear_map_comp_left {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] (g : continuous_linear_map 𝕜 F G) : is_bounded_linear_map 𝕜 fun (f : continuous_linear_map 𝕜 E F) => continuous_linear_map.comp g f :=
  is_bounded_bilinear_map.is_bounded_linear_map_left is_bounded_bilinear_map_comp g

theorem continuous_linear_map.is_bounded_linear_map_comp_right {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] (f : continuous_linear_map 𝕜 E F) : is_bounded_linear_map 𝕜 fun (g : continuous_linear_map 𝕜 F G) => continuous_linear_map.comp g f :=
  is_bounded_bilinear_map.is_bounded_linear_map_right is_bounded_bilinear_map_comp f

theorem is_bounded_bilinear_map_apply {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] : is_bounded_bilinear_map 𝕜 fun (p : continuous_linear_map 𝕜 E F × E) => coe_fn (prod.fst p) (prod.snd p) := sorry

/-- The function `continuous_linear_map.smul_right`, associating to a continuous linear map
`f : E → 𝕜` and a scalar `c : F` the tensor product `f ⊗ c` as a continuous linear map from `E` to
`F`, is a bounded bilinear map. -/
theorem is_bounded_bilinear_map_smul_right {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] : is_bounded_bilinear_map 𝕜
  fun (p : continuous_linear_map 𝕜 E 𝕜 × F) => continuous_linear_map.smul_right (prod.fst p) (prod.snd p) := sorry

/-- The composition of a continuous linear map with a continuous multilinear map is a bounded
bilinear operation. -/
theorem is_bounded_bilinear_map_comp_multilinear {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {ι : Type u_2} {E : ι → Type u_5} [DecidableEq ι] [fintype ι] [(i : ι) → normed_group (E i)] [(i : ι) → normed_space 𝕜 (E i)] : is_bounded_bilinear_map 𝕜
  fun (p : continuous_linear_map 𝕜 F G × continuous_multilinear_map 𝕜 E F) =>
    continuous_linear_map.comp_continuous_multilinear_map (prod.fst p) (prod.snd p) := sorry

/-- Definition of the derivative of a bilinear map `f`, given at a point `p` by
`q ↦ f(p.1, q.2) + f(q.1, p.2)` as in the standard formula for the derivative of a product.
We define this function here a bounded linear map from `E × F` to `G`. The fact that this
is indeed the derivative of `f` is proved in `is_bounded_bilinear_map.has_fderiv_at` in
`fderiv.lean`-/
def is_bounded_bilinear_map.linear_deriv {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) (p : E × F) : linear_map 𝕜 (E × F) G :=
  linear_map.mk (fun (q : E × F) => f (prod.fst p, prod.snd q) + f (prod.fst q, prod.snd p)) sorry sorry

/-- The derivative of a bounded bilinear map at a point `p : E × F`, as a continuous linear map
from `E × F` to `G`. -/
def is_bounded_bilinear_map.deriv {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) (p : E × F) : continuous_linear_map 𝕜 (E × F) G :=
  linear_map.mk_continuous_of_exists_bound (is_bounded_bilinear_map.linear_deriv h p) sorry

@[simp] theorem is_bounded_bilinear_map_deriv_coe {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) (p : E × F) (q : E × F) : coe_fn (is_bounded_bilinear_map.deriv h p) q = f (prod.fst p, prod.snd q) + f (prod.fst q, prod.snd p) :=
  rfl

/-- The function `lmul_left_right : 𝕜' × 𝕜' → (𝕜' →L[𝕜] 𝕜')` is a bounded bilinear map. -/
theorem continuous_linear_map.lmul_left_right_is_bounded_bilinear (𝕜 : Type u_1) [nondiscrete_normed_field 𝕜] (𝕜' : Type u_2) [normed_ring 𝕜'] [normed_algebra 𝕜 𝕜'] : is_bounded_bilinear_map 𝕜 (continuous_linear_map.lmul_left_right 𝕜 𝕜') := sorry

/-- Given a bounded bilinear map `f`, the map associating to a point `p` the derivative of `f` at
`p` is itself a bounded linear map. -/
theorem is_bounded_bilinear_map.is_bounded_linear_map_deriv {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) : is_bounded_linear_map 𝕜 fun (p : E × F) => is_bounded_bilinear_map.deriv h p := sorry

/-- A linear isometry preserves the norm. -/
theorem linear_map.norm_apply_of_isometry {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] (f : linear_map 𝕜 E F) {x : E} (hf : isometry ⇑f) : norm (coe_fn f x) = norm x := sorry

/-- Construct a continuous linear equiv from a linear map that is also an isometry with full range. -/
def continuous_linear_equiv.of_isometry {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] (f : linear_map 𝕜 E F) (hf : isometry ⇑f) (hfr : linear_map.range f = ⊤) : continuous_linear_equiv 𝕜 E F :=
  continuous_linear_equiv.of_homothety 𝕜 (linear_equiv.of_bijective f sorry hfr) 1 zero_lt_one sorry


import category_theory.limits.shapes.pullbacks

/-!
Thanks to Markus Himmel for suggesting this question.
-/

open category_theory
open category_theory.limits

/-!
Let C be a category, X and Y be objects and f : X ⟶ Y be a morphism. Show that f is an epimorphism
if and only if the diagram

X --f--→ Y
|        |
f        𝟙
|        |
↓        ↓
Y --𝟙--→ Y

is a pushout.
-/

universes v u

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

def pushout_of_epi {X Y : C} (f : X ⟶ Y) [epi f] :
  is_colimit (pushout_cocone.mk (𝟙 Y) (𝟙 Y) rfl : pushout_cocone f f) :=
begin
  fapply pushout_cocone.is_colimit.mk,
  all_goals { sorry, },
end

theorem epi_of_pushout {X Y : C} (f : X ⟶ Y)
  (is_colim : is_colimit (pushout_cocone.mk (𝟙 Y) (𝟙 Y) rfl : pushout_cocone f f)) : epi f :=
{ left_cancellation := λ Z g h hf,
  begin
    let a := pushout_cocone.mk _ _ hf,
    sorry,
  end }