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

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

/-!
# Induced categories and full subcategories

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

Given a category `D` and a function `F : C → D `from a type `C` to the
objects of `D`, there is an essentially unique way to give `C` a
category structure such that `F` becomes a fully faithful functor,
namely by taking $$ Hom_C(X, Y) = Hom_D(FX, FY) $$. We call this the
category induced from `D` along `F`.

As a special case, if `C` is a subtype of `D`,
this produces the full subcategory of `D` on the objects belonging to `C`.
In general the induced category is equivalent to the full subcategory of `D` on the
image of `F`.

## Implementation notes

It looks odd to make `D` an explicit argument of `induced_category`,
when it is determined by the argument `F` anyways. The reason to make `D`
explicit is in order to control its syntactic form, so that instances
like `induced_category.has_forget₂` (elsewhere) refer to the correct
form of D. This is used to set up several algebraic categories like

  def CommMon : Type (u+1) := induced_category Mon (bundled.map @comm_monoid.to_monoid)
  -- not `induced_category (bundled monoid) (bundled.map @comm_monoid.to_monoid)`,
  -- even though `Mon = bundled monoid`!
-/

namespace CategoryTheory

universe v v₂ u₁ u₂

-- morphism levels before object levels. See note [category_theory universes].
section Induced

variable {C : Type u₁} (D : Type u₂) [Category.{v} D]

variable (F : C → D)

include F

/- warning: category_theory.induced_category -> CategoryTheory.InducedCategory is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u₁}} (D : Type.{u₂}) [_inst_1 : CategoryTheory.Category.{v, u₂} D], (C -> D) -> Type.{u₁}
but is expected to have type
  forall {C : Type.{u₁}} (D : Type.{u₂}), (C -> D) -> Type.{u₁}
Case conversion may be inaccurate. Consider using '#align category_theory.induced_category CategoryTheory.InducedCategoryₓ'. -/
/-- `induced_category D F`, where `F : C → D`, is a typeclass synonym for `C`,
which provides a category structure so that the morphisms `X ⟶ Y` are the morphisms
in `D` from `F X` to `F Y`.
-/
@[nolint has_nonempty_instance unused_arguments]
def InducedCategory : Type u₁ :=
  C
#align category_theory.induced_category CategoryTheory.InducedCategory

variable {D}

/- warning: category_theory.induced_category.has_coe_to_sort -> CategoryTheory.InducedCategory.hasCoeToSort is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u₁}} {D : Type.{u₂}} [_inst_1 : CategoryTheory.Category.{v, u₂} D] (F : C -> D) {α : Sort.{u_1}} [_inst_2 : CoeSort.{succ u₂, u_1} D α], CoeSort.{succ u₁, u_1} (CategoryTheory.InducedCategory.{v, u₁, u₂} C D _inst_1 F) α
but is expected to have type
  forall {C : Type.{u₁}} {D : Type.{u₂}} (_inst_1 : C -> D) {F : Sort.{u_1}} [α : CoeSort.{succ u₂, u_1} D F], CoeSort.{succ u₁, u_1} (CategoryTheory.InducedCategory.{u₁, u₂} C D _inst_1) F
Case conversion may be inaccurate. Consider using '#align category_theory.induced_category.has_coe_to_sort CategoryTheory.InducedCategory.hasCoeToSortₓ'. -/
instance InducedCategory.hasCoeToSort {α : Sort _} [CoeSort D α] :
    CoeSort (InducedCategory D F) α :=
  ⟨fun c => ↥(F c)⟩
#align category_theory.induced_category.has_coe_to_sort CategoryTheory.InducedCategory.hasCoeToSort

#print CategoryTheory.InducedCategory.category /-
instance InducedCategory.category : Category.{v} (InducedCategory D F)
    where
  Hom X Y := F X ⟶ F Y
  id X := 𝟙 (F X)
  comp _ _ _ f g := f ≫ g
#align category_theory.induced_category.category CategoryTheory.InducedCategory.category
-/

#print CategoryTheory.inducedFunctor /-
/-- The forgetful functor from an induced category to the original category,
forgetting the extra data.
-/
@[simps]
def inducedFunctor : InducedCategory D F ⥤ D
    where
  obj := F
  map x y f := f
#align category_theory.induced_functor CategoryTheory.inducedFunctor
-/

#print CategoryTheory.InducedCategory.full /-
instance InducedCategory.full : Full (inducedFunctor F) where preimage x y f := f
#align category_theory.induced_category.full CategoryTheory.InducedCategory.full
-/

#print CategoryTheory.InducedCategory.faithful /-
instance InducedCategory.faithful : Faithful (inducedFunctor F) where
#align category_theory.induced_category.faithful CategoryTheory.InducedCategory.faithful
-/

end Induced

section FullSubcategory

-- A full subcategory is the special case of an induced category with F = subtype.val.
variable {C : Type u₁} [Category.{v} C]

variable (Z : C → Prop)

/--
A subtype-like structure for full subcategories. Morphisms just ignore the property. We don't use
actual subtypes since the simp-normal form `↑X` of `X.val` does not work well for full
subcategories.

See <https://stacks.math.columbia.edu/tag/001D>. We do not define 'strictly full' subcategories.
-/
@[ext, nolint has_nonempty_instance]
structure FullSubcategory where
  obj : C
  property : Z obj
#align category_theory.full_subcategory CategoryTheory.FullSubcategoryₓ

/- warning: category_theory.full_subcategory.category -> CategoryTheory.FullSubcategory.category is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop), CategoryTheory.Category.{u1, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z)
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop), CategoryTheory.Category.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z)
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory.category CategoryTheory.FullSubcategory.categoryₓ'. -/
instance FullSubcategory.category : Category.{v} (FullSubcategory Z) :=
  InducedCategory.category FullSubcategory.obj
#align category_theory.full_subcategory.category CategoryTheory.FullSubcategory.category

/- warning: category_theory.full_subcategory_inclusion -> CategoryTheory.fullSubcategoryInclusion is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop), CategoryTheory.Functor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.InducedCategory.category.{u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.FullSubcategoryₓ.obj.{u1, u2} C _inst_1 Z)) C _inst_1
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop), CategoryTheory.Functor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z) C _inst_1
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory_inclusion CategoryTheory.fullSubcategoryInclusionₓ'. -/
/-- The forgetful functor from a full subcategory into the original category
("forgetting" the condition).
-/
def fullSubcategoryInclusion : FullSubcategory Z ⥤ C :=
  inducedFunctor FullSubcategory.obj
#align category_theory.full_subcategory_inclusion CategoryTheory.fullSubcategoryInclusion

/- warning: category_theory.full_subcategory_inclusion.obj -> CategoryTheory.fullSubcategoryInclusion.obj is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop) {X : CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z}, Eq.{succ u2} C (CategoryTheory.Functor.obj.{u1, u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.InducedCategory.category.{u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.FullSubcategoryₓ.obj.{u1, u2} C _inst_1 Z)) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z) X) (CategoryTheory.FullSubcategoryₓ.obj.{u1, u2} C _inst_1 Z X)
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop) {X : CategoryTheory.FullSubcategory.{u2} C Z}, Eq.{succ u2} C (Prefunctor.obj.{succ u1, succ u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.Category.toCategoryStruct.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)) X) (CategoryTheory.FullSubcategory.obj.{u2} C Z X)
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory_inclusion.obj CategoryTheory.fullSubcategoryInclusion.objₓ'. -/
@[simp]
theorem fullSubcategoryInclusion.obj {X} : (fullSubcategoryInclusion Z).obj X = X.obj :=
  rfl
#align category_theory.full_subcategory_inclusion.obj CategoryTheory.fullSubcategoryInclusion.obj

/- warning: category_theory.full_subcategory_inclusion.map -> CategoryTheory.fullSubcategoryInclusion.map is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop) {X : CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z} {Y : CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z} {f : Quiver.Hom.{succ u1, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.Category.toCategoryStruct.{u1, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.InducedCategory.category.{u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.FullSubcategoryₓ.obj.{u1, u2} C _inst_1 Z)))) X Y}, Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) (CategoryTheory.Functor.obj.{u1, u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.InducedCategory.category.{u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.FullSubcategoryₓ.obj.{u1, u2} C _inst_1 Z)) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z) X) (CategoryTheory.Functor.obj.{u1, u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.InducedCategory.category.{u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.FullSubcategoryₓ.obj.{u1, u2} C _inst_1 Z)) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z) Y)) (CategoryTheory.Functor.map.{u1, u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.InducedCategory.category.{u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.FullSubcategoryₓ.obj.{u1, u2} C _inst_1 Z)) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z) X Y f) f
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop) {X : CategoryTheory.FullSubcategory.{u2} C Z} {Y : CategoryTheory.FullSubcategory.{u2} C Z} {f : Quiver.Hom.{succ u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.Category.toCategoryStruct.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z))) X Y}, Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) (Prefunctor.obj.{succ u1, succ u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.Category.toCategoryStruct.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)) X) (Prefunctor.obj.{succ u1, succ u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.Category.toCategoryStruct.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)) Y)) (Prefunctor.map.{succ u1, succ u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.Category.toCategoryStruct.{u1, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z))) C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) (CategoryTheory.Functor.toPrefunctor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)) X Y f) f
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory_inclusion.map CategoryTheory.fullSubcategoryInclusion.mapₓ'. -/
@[simp]
theorem fullSubcategoryInclusion.map {X Y} {f : X ⟶ Y} : (fullSubcategoryInclusion Z).map f = f :=
  rfl
#align category_theory.full_subcategory_inclusion.map CategoryTheory.fullSubcategoryInclusion.map

/- warning: category_theory.full_subcategory.full -> CategoryTheory.FullSubcategory.full is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop), CategoryTheory.Full.{u1, u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.InducedCategory.category.{u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.FullSubcategoryₓ.obj.{u1, u2} C _inst_1 Z)) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop), CategoryTheory.Full.{u1, u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory.full CategoryTheory.FullSubcategory.fullₓ'. -/
instance FullSubcategory.full : Full (fullSubcategoryInclusion Z) :=
  InducedCategory.full _
#align category_theory.full_subcategory.full CategoryTheory.FullSubcategory.full

/- warning: category_theory.full_subcategory.faithful -> CategoryTheory.FullSubcategory.faithful is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop), CategoryTheory.Faithful.{u1, u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.InducedCategory.category.{u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.FullSubcategoryₓ.obj.{u1, u2} C _inst_1 Z)) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] (Z : C -> Prop), CategoryTheory.Faithful.{u1, u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z) C _inst_1 (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory.faithful CategoryTheory.FullSubcategory.faithfulₓ'. -/
instance FullSubcategory.faithful : Faithful (fullSubcategoryInclusion Z) :=
  InducedCategory.faithful _
#align category_theory.full_subcategory.faithful CategoryTheory.FullSubcategory.faithful

variable {Z} {Z' : C → Prop}

/- warning: category_theory.full_subcategory.map -> CategoryTheory.FullSubcategory.map is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {Z : C -> Prop} {Z' : C -> Prop}, (forall {{X : C}}, (Z X) -> (Z' X)) -> (CategoryTheory.Functor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z) (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 Z') (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z'))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {Z : C -> Prop} {Z' : C -> Prop}, (forall {{X : C}}, (Z X) -> (Z' X)) -> (CategoryTheory.Functor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C Z) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z) (CategoryTheory.FullSubcategory.{u2} C Z') (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 Z'))
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory.map CategoryTheory.FullSubcategory.mapₓ'. -/
/-- An implication of predicates `Z → Z'` induces a functor between full subcategories. -/
@[simps]
def FullSubcategory.map (h : ∀ ⦃X⦄, Z X → Z' X) : FullSubcategory Z ⥤ FullSubcategory Z'
    where
  obj X := ⟨X.1, h X.2⟩
  map X Y f := f
#align category_theory.full_subcategory.map CategoryTheory.FullSubcategory.map

instance (h : ∀ ⦃X⦄, Z X → Z' X) : Full (FullSubcategory.map h) where preimage X Y f := f

instance (h : ∀ ⦃X⦄, Z X → Z' X) : Faithful (FullSubcategory.map h) where

/- warning: category_theory.full_subcategory.map_inclusion -> CategoryTheory.FullSubcategory.map_inclusion is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {Z : C -> Prop} {Z' : C -> Prop} (h : forall {{X : C}}, (Z X) -> (Z' X)), Eq.{succ (max u1 u2)} (CategoryTheory.Functor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 (fun (X : C) => Z X)) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 (fun (X : C) => Z X)) C _inst_1) (CategoryTheory.Functor.comp.{u1, u1, u1, u2, u2, u2} (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 (fun (X : C) => Z X)) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 (fun (X : C) => Z X)) (CategoryTheory.FullSubcategoryₓ.{u1, u2} C _inst_1 (fun (X : C) => Z' X)) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 (fun (X : C) => Z' X)) C _inst_1 (CategoryTheory.FullSubcategory.map.{u1, u2} C _inst_1 (fun (X : C) => Z X) (fun (X : C) => Z' X) h) (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z')) (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {Z : C -> Prop} {Z' : C -> Prop} (h : forall {{X : C}}, (Z X) -> (Z' X)), Eq.{max (succ u2) (succ u1)} (CategoryTheory.Functor.{u1, u1, u2, u2} (CategoryTheory.FullSubcategory.{u2} C (fun (X : C) => Z X)) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 (fun (X : C) => Z X)) C _inst_1) (CategoryTheory.Functor.comp.{u1, u1, u1, u2, u2, u2} (CategoryTheory.FullSubcategory.{u2} C (fun (X : C) => Z X)) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 (fun (X : C) => Z X)) (CategoryTheory.FullSubcategory.{u2} C (fun (X : C) => Z' X)) (CategoryTheory.FullSubcategory.category.{u1, u2} C _inst_1 (fun (X : C) => Z' X)) C _inst_1 (CategoryTheory.FullSubcategory.map.{u1, u2} C _inst_1 (fun (X : C) => Z X) (fun (X : C) => Z' X) h) (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z')) (CategoryTheory.fullSubcategoryInclusion.{u1, u2} C _inst_1 Z)
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory.map_inclusion CategoryTheory.FullSubcategory.map_inclusionₓ'. -/
@[simp]
theorem FullSubcategory.map_inclusion (h : ∀ ⦃X⦄, Z X → Z' X) :
    FullSubcategory.map h ⋙ fullSubcategoryInclusion Z' = fullSubcategoryInclusion Z :=
  rfl
#align category_theory.full_subcategory.map_inclusion CategoryTheory.FullSubcategory.map_inclusion

section lift

variable {D : Type u₂} [Category.{v₂} D] (P Q : D → Prop)

/- warning: category_theory.full_subcategory.lift -> CategoryTheory.FullSubcategory.lift 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] (P : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2), (forall (X : C), P (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X)) -> (CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))
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] (P : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2), (forall (X : C), P (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)) -> (CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory.lift CategoryTheory.FullSubcategory.liftₓ'. -/
/-- A functor which maps objects to objects satisfying a certain property induces a lift through
    the full subcategory of objects satisfying that property. -/
@[simps]
def FullSubcategory.lift (F : C ⥤ D) (hF : ∀ X, P (F.obj X)) : C ⥤ FullSubcategory P
    where
  obj X := ⟨F.obj X, hF X⟩
  map X Y f := F.map f
#align category_theory.full_subcategory.lift CategoryTheory.FullSubcategory.lift

/- warning: category_theory.full_subcategory.lift_comp_inclusion -> CategoryTheory.FullSubcategory.lift_comp_inclusion 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] (P : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (hF : forall (X : C), P (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X)), CategoryTheory.Iso.{max u3 u2, max u1 u2 u3 u4} (CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u2, u3, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.comp.{u1, u2, u2, u3, u4, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P)) 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] (P : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (hF : forall (X : C), P (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)), CategoryTheory.Iso.{max u3 u2, max (max (max u4 u3) u2) u1} (CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.category.{u1, u2, u3, u4} C _inst_1 D _inst_2) (CategoryTheory.Functor.comp.{u1, u2, u2, u3, u4, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P)) F
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory.lift_comp_inclusion CategoryTheory.FullSubcategory.lift_comp_inclusionₓ'. -/
/-- Composing the lift of a functor through a full subcategory with the inclusion yields the
    original functor. Unfortunately, this is not true by definition, so we only get a natural
    isomorphism, but it is pointwise definitionally true, see
    `full_subcategory.inclusion_obj_lift_obj` and `full_subcategory.inclusion_map_lift_map`. -/
def FullSubcategory.lift_comp_inclusion (F : C ⥤ D) (hF : ∀ X, P (F.obj X)) :
    FullSubcategory.lift P F hF ⋙ fullSubcategoryInclusion P ≅ F :=
  NatIso.ofComponents (fun X => Iso.refl _) (by simp)
#align category_theory.full_subcategory.lift_comp_inclusion CategoryTheory.FullSubcategory.lift_comp_inclusion

/- warning: category_theory.full_subcategory.inclusion_obj_lift_obj -> CategoryTheory.fullSubcategoryInclusion_obj_lift_obj 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] (P : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (hF : forall (X : C), P (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X)) {X : C}, Eq.{succ u4} D (CategoryTheory.Functor.obj.{u2, u2, u4, u4} (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.InducedCategory.category.{u2, u4, u4} (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.FullSubcategoryₓ.obj.{u2, u4} D _inst_2 P)) D _inst_2 (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) X)) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X)
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (P : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (hF : forall (X : C), P (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)) {X : C}, Eq.{succ u4} D (Prefunctor.obj.{succ u2, succ u2, u4, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u2, u4, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P)) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF)) 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) X)
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory.inclusion_obj_lift_obj CategoryTheory.fullSubcategoryInclusion_obj_lift_objₓ'. -/
@[simp]
theorem CategoryTheory.fullSubcategoryInclusion_obj_lift_obj (F : C ⥤ D) (hF : ∀ X, P (F.obj X))
    {X : C} : (fullSubcategoryInclusion P).obj ((FullSubcategory.lift P F hF).obj X) = F.obj X :=
  rfl
#align category_theory.full_subcategory.inclusion_obj_lift_obj CategoryTheory.fullSubcategoryInclusion_obj_lift_obj

/- warning: category_theory.full_subcategory.inclusion_map_lift_map -> CategoryTheory.fullSubcategoryInclusion_map_lift_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] (P : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (hF : forall (X : C), P (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X)) {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), Eq.{succ u2} (Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.obj.{u2, u2, u4, u4} (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.InducedCategory.category.{u2, u4, u4} (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.FullSubcategoryₓ.obj.{u2, u4} D _inst_2 P)) D _inst_2 (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) X)) (CategoryTheory.Functor.obj.{u2, u2, u4, u4} (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.InducedCategory.category.{u2, u4, u4} (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.FullSubcategoryₓ.obj.{u2, u4} D _inst_2 P)) D _inst_2 (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) Y))) (CategoryTheory.Functor.map.{u2, u2, u4, u4} (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.InducedCategory.category.{u2, u4, u4} (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.FullSubcategoryₓ.obj.{u2, u4} D _inst_2 P)) D _inst_2 (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) X) (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) Y) (CategoryTheory.Functor.map.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) X Y f)) (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] (P : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (hF : forall (X : C), P (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)) {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), Eq.{succ u2} (Quiver.Hom.{succ u2, u4} D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (Prefunctor.obj.{succ u2, succ u2, u4, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u2, u4, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P)) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF)) X)) (Prefunctor.obj.{succ u2, succ u2, u4, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u2, u4, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P)) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF)) Y))) (Prefunctor.map.{succ u2, succ u2, u4, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) D (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} D (CategoryTheory.Category.toCategoryStruct.{u2, u4} D _inst_2)) (CategoryTheory.Functor.toPrefunctor.{u2, u2, u4, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) D _inst_2 (CategoryTheory.fullSubcategoryInclusion.{u2, u4} D _inst_2 P)) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF)) X) (Prefunctor.obj.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF)) Y) (Prefunctor.map.{succ u1, succ u2, u3, u4} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u3} C (CategoryTheory.Category.toCategoryStruct.{u1, u3} C _inst_1)) (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.CategoryStruct.toQuiver.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.Category.toCategoryStruct.{u2, u4} (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P))) (CategoryTheory.Functor.toPrefunctor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF)) X Y f)) (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.full_subcategory.inclusion_map_lift_map CategoryTheory.fullSubcategoryInclusion_map_lift_mapₓ'. -/
theorem CategoryTheory.fullSubcategoryInclusion_map_lift_map (F : C ⥤ D) (hF : ∀ X, P (F.obj X))
    {X Y : C} (f : X ⟶ Y) :
    (fullSubcategoryInclusion P).map ((FullSubcategory.lift P F hF).map f) = F.map f :=
  rfl
#align category_theory.full_subcategory.inclusion_map_lift_map CategoryTheory.fullSubcategoryInclusion_map_lift_map

instance (F : C ⥤ D) (hF : ∀ X, P (F.obj X)) [Faithful F] :
    Faithful (FullSubcategory.lift P F hF) :=
  Faithful.of_comp_iso (FullSubcategory.lift_comp_inclusion P F hF)

instance (F : C ⥤ D) (hF : ∀ X, P (F.obj X)) [Full F] : Full (FullSubcategory.lift P F hF) :=
  Full.ofCompFaithfulIso (FullSubcategory.lift_comp_inclusion P F hF)

/- warning: category_theory.full_subcategory.lift_comp_map -> CategoryTheory.FullSubcategory.lift_comp_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] (P : D -> Prop) (Q : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (hF : forall (X : C), P (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X)) (h : forall {{X : D}}, (P X) -> (Q X)), Eq.{succ (max u1 u2 u3 u4)} (CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 (fun (X : D) => Q X)) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 (fun (X : D) => Q X))) (CategoryTheory.Functor.comp.{u1, u2, u2, u3, u4, u4} C _inst_1 (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategoryₓ.{u2, u4} D _inst_2 (fun (X : D) => Q X)) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 (fun (X : D) => Q X)) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) (CategoryTheory.FullSubcategory.map.{u2, u4} D _inst_2 P (fun (X : D) => Q X) h)) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 Q F (fun (X : C) => h (CategoryTheory.Functor.obj.{u1, u2, u3, u4} C _inst_1 D _inst_2 F X) (hF X)))
but is expected to have type
  forall {C : Type.{u3}} [_inst_1 : CategoryTheory.Category.{u1, u3} C] {D : Type.{u4}} [_inst_2 : CategoryTheory.Category.{u2, u4} D] (P : D -> Prop) (Q : D -> Prop) (F : CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 D _inst_2) (hF : forall (X : C), P (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)) (h : forall {{X : D}}, (P X) -> (Q X)), Eq.{max (max (max (succ u3) (succ u4)) (succ u1)) (succ u2)} (CategoryTheory.Functor.{u1, u2, u3, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D (fun (X : D) => Q X)) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 (fun (X : D) => Q X))) (CategoryTheory.Functor.comp.{u1, u2, u2, u3, u4, u4} C _inst_1 (CategoryTheory.FullSubcategory.{u4} D P) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 P) (CategoryTheory.FullSubcategory.{u4} D (fun (X : D) => Q X)) (CategoryTheory.FullSubcategory.category.{u2, u4} D _inst_2 (fun (X : D) => Q X)) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 P F hF) (CategoryTheory.FullSubcategory.map.{u2, u4} D _inst_2 P (fun (X : D) => Q X) h)) (CategoryTheory.FullSubcategory.lift.{u1, u2, u3, u4} C _inst_1 D _inst_2 Q F (fun (X : C) => h (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) (hF X)))
Case conversion may be inaccurate. Consider using '#align category_theory.full_subcategory.lift_comp_map CategoryTheory.FullSubcategory.lift_comp_mapₓ'. -/
@[simp]
theorem FullSubcategory.lift_comp_map (F : C ⥤ D) (hF : ∀ X, P (F.obj X)) (h : ∀ ⦃X⦄, P X → Q X) :
    FullSubcategory.lift P F hF ⋙ FullSubcategory.map h =
      FullSubcategory.lift Q F fun X => h (hF X) :=
  rfl
#align category_theory.full_subcategory.lift_comp_map CategoryTheory.FullSubcategory.lift_comp_map

end lift

end FullSubcategory

end CategoryTheory


/-
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.list.bag_inter
import Mathlib.data.list.erase_dup
import Mathlib.data.list.zip
import Mathlib.logic.relation
import Mathlib.data.nat.factorial
import Mathlib.PostPort

universes uu u_1 vv

namespace Mathlib

/-!
# List permutations
-/

namespace list

/-- `perm l₁ l₂` or `l₁ ~ l₂` asserts that `l₁` and `l₂` are permutations
  of each other. This is defined by induction using pairwise swaps. -/
inductive perm {α : Type uu} : List α → List α → Prop where
| nil : perm [] []
| cons : ∀ (x : α) {l₁ l₂ : List α}, perm l₁ l₂ → perm (x :: l₁) (x :: l₂)
| swap : ∀ (x y : α) (l : List α), perm (y :: x :: l) (x :: y :: l)
| trans : ∀ {l₁ l₂ l₃ : List α}, perm l₁ l₂ → perm l₂ l₃ → perm l₁ l₃

infixl:50 " ~ " => Mathlib.list.perm

protected theorem perm.refl {α : Type uu} (l : List α) : l ~ l := sorry

protected theorem perm.symm {α : Type uu} {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂) : l₂ ~ l₁ :=
  perm.rec_on p perm.nil
    (fun (x : α) (l₁ l₂ : List α) (p₁ : l₁ ~ l₂) (r₁ : l₂ ~ l₁) => perm.cons x r₁)
    (fun (x y : α) (l : List α) => perm.swap y x l)
    fun (l₁ l₂ l₃ : List α) (p₁ : l₁ ~ l₂) (p₂ : l₂ ~ l₃) (r₁ : l₂ ~ l₁) (r₂ : l₃ ~ l₂) =>
      perm.trans r₂ r₁

theorem perm_comm {α : Type uu} {l₁ : List α} {l₂ : List α} : l₁ ~ l₂ ↔ l₂ ~ l₁ :=
  { mp := perm.symm, mpr := perm.symm }

theorem perm.swap' {α : Type uu} (x : α) (y : α) {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂) :
    y :: x :: l₁ ~ x :: y :: l₂ :=
  perm.trans (perm.swap x y l₁) (perm.cons x (perm.cons y p))

theorem perm.eqv (α : Type u_1) : equivalence perm :=
  mk_equivalence perm perm.refl perm.symm perm.trans

protected instance is_setoid (α : Type u_1) : setoid (List α) := setoid.mk perm (perm.eqv α)

theorem perm.subset {α : Type uu} {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂) : l₁ ⊆ l₂ := sorry

theorem perm.mem_iff {α : Type uu} {a : α} {l₁ : List α} {l₂ : List α} (h : l₁ ~ l₂) :
    a ∈ l₁ ↔ a ∈ l₂ :=
  { mp := fun (m : a ∈ l₁) => perm.subset h m,
    mpr := fun (m : a ∈ l₂) => perm.subset (perm.symm h) m }

theorem perm.append_right {α : Type uu} {l₁ : List α} {l₂ : List α} (t₁ : List α) (p : l₁ ~ l₂) :
    l₁ ++ t₁ ~ l₂ ++ t₁ :=
  sorry

theorem perm.append_left {α : Type uu} {t₁ : List α} {t₂ : List α} (l : List α) :
    t₁ ~ t₂ → l ++ t₁ ~ l ++ t₂ :=
  sorry

theorem perm.append {α : Type uu} {l₁ : List α} {l₂ : List α} {t₁ : List α} {t₂ : List α}
    (p₁ : l₁ ~ l₂) (p₂ : t₁ ~ t₂) : l₁ ++ t₁ ~ l₂ ++ t₂ :=
  perm.trans (perm.append_right t₁ p₁) (perm.append_left l₂ p₂)

theorem perm.append_cons {α : Type uu} (a : α) {h₁ : List α} {h₂ : List α} {t₁ : List α}
    {t₂ : List α} (p₁ : h₁ ~ h₂) (p₂ : t₁ ~ t₂) : h₁ ++ a :: t₁ ~ h₂ ++ a :: t₂ :=
  perm.append p₁ (perm.cons a p₂)

@[simp] theorem perm_middle {α : Type uu} {a : α} {l₁ : List α} {l₂ : List α} :
    l₁ ++ a :: l₂ ~ a :: (l₁ ++ l₂) :=
  sorry

@[simp] theorem perm_append_singleton {α : Type uu} (a : α) (l : List α) : l ++ [a] ~ a :: l :=
  perm.trans perm_middle
    (eq.mpr (id (Eq._oldrec (Eq.refl (a :: (l ++ []) ~ a :: l)) (append_nil l)))
      (perm.refl (a :: l)))

theorem perm_append_comm {α : Type uu} {l₁ : List α} {l₂ : List α} : l₁ ++ l₂ ~ l₂ ++ l₁ := sorry

theorem concat_perm {α : Type uu} (l : List α) (a : α) : concat l a ~ a :: l := sorry

theorem perm.length_eq {α : Type uu} {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂) :
    length l₁ = length l₂ :=
  sorry

theorem perm.eq_nil {α : Type uu} {l : List α} (p : l ~ []) : l = [] :=
  eq_nil_of_length_eq_zero (perm.length_eq p)

theorem perm.nil_eq {α : Type uu} {l : List α} (p : [] ~ l) : [] = l :=
  Eq.symm (perm.eq_nil (perm.symm p))

theorem perm_nil {α : Type uu} {l₁ : List α} : l₁ ~ [] ↔ l₁ = [] :=
  { mp := fun (p : l₁ ~ []) => perm.eq_nil p, mpr := fun (e : l₁ = []) => e ▸ perm.refl l₁ }

theorem not_perm_nil_cons {α : Type uu} (x : α) (l : List α) : ¬[] ~ x :: l :=
  fun (ᾰ : [] ~ x :: l) =>
    idRhs (list.no_confusion_type False (x :: l) []) (list.no_confusion (perm.eq_nil (perm.symm ᾰ)))

@[simp] theorem reverse_perm {α : Type uu} (l : List α) : reverse l ~ l := sorry

theorem perm_cons_append_cons {α : Type uu} {l : List α} {l₁ : List α} {l₂ : List α} (a : α)
    (p : l ~ l₁ ++ l₂) : a :: l ~ l₁ ++ a :: l₂ :=
  perm.trans (perm.cons a p) (perm.symm perm_middle)

@[simp] theorem perm_repeat {α : Type uu} {a : α} {n : ℕ} {l : List α} :
    l ~ repeat a n ↔ l = repeat a n :=
  sorry

@[simp] theorem repeat_perm {α : Type uu} {a : α} {n : ℕ} {l : List α} :
    repeat a n ~ l ↔ repeat a n = l :=
  iff.trans (iff.trans perm_comm perm_repeat) eq_comm

@[simp] theorem perm_singleton {α : Type uu} {a : α} {l : List α} : l ~ [a] ↔ l = [a] := perm_repeat

@[simp] theorem singleton_perm {α : Type uu} {a : α} {l : List α} : [a] ~ l ↔ [a] = l := repeat_perm

theorem perm.eq_singleton {α : Type uu} {a : α} {l : List α} (p : l ~ [a]) : l = [a] :=
  iff.mp perm_singleton p

theorem perm.singleton_eq {α : Type uu} {a : α} {l : List α} (p : [a] ~ l) : [a] = l :=
  Eq.symm (perm.eq_singleton (perm.symm p))

theorem singleton_perm_singleton {α : Type uu} {a : α} {b : α} : [a] ~ [b] ↔ a = b := sorry

theorem perm_cons_erase {α : Type uu} [DecidableEq α] {a : α} {l : List α} (h : a ∈ l) :
    l ~ a :: list.erase l a :=
  sorry

theorem perm_induction_on {α : Type uu} {P : List α → List α → Prop} {l₁ : List α} {l₂ : List α}
    (p : l₁ ~ l₂) (h₁ : P [] [])
    (h₂ : ∀ (x : α) (l₁ l₂ : List α), l₁ ~ l₂ → P l₁ l₂ → P (x :: l₁) (x :: l₂))
    (h₃ : ∀ (x y : α) (l₁ l₂ : List α), l₁ ~ l₂ → P l₁ l₂ → P (y :: x :: l₁) (x :: y :: l₂))
    (h₄ : ∀ (l₁ l₂ l₃ : List α), l₁ ~ l₂ → l₂ ~ l₃ → P l₁ l₂ → P l₂ l₃ → P l₁ l₃) : P l₁ l₂ :=
  (fun (P_refl : ∀ (l : List α), P l l) =>
      perm.rec_on p h₁ h₂ (fun (x y : α) (l : List α) => h₃ x y l l (perm.refl l) (P_refl l)) h₄)
    fun (l : List α) =>
      list.rec_on l h₁ fun (x : α) (xs : List α) (ih : P xs xs) => h₂ x xs xs (perm.refl xs) ih

theorem perm.filter_map {α : Type uu} {β : Type vv} (f : α → Option β) {l₁ : List α} {l₂ : List α}
    (p : l₁ ~ l₂) : filter_map f l₁ ~ filter_map f l₂ :=
  sorry

theorem perm.map {α : Type uu} {β : Type vv} (f : α → β) {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂) :
    map f l₁ ~ map f l₂ :=
  filter_map_eq_map f ▸ perm.filter_map (some ∘ f) p

theorem perm.pmap {α : Type uu} {β : Type vv} {p : α → Prop} (f : (a : α) → p a → β) {l₁ : List α}
    {l₂ : List α} :
    l₁ ~ l₂ →
        ∀ {H₁ : ∀ (a : α), a ∈ l₁ → p a} {H₂ : ∀ (a : α), a ∈ l₂ → p a},
          pmap f l₁ H₁ ~ pmap f l₂ H₂ :=
  sorry

theorem perm.filter {α : Type uu} (p : α → Prop) [decidable_pred p] {l₁ : List α} {l₂ : List α}
    (s : l₁ ~ l₂) : filter p l₁ ~ filter p l₂ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (filter p l₁ ~ filter p l₂)) (Eq.symm (filter_map_eq_filter p))))
    (perm.filter_map (option.guard p) s)

theorem exists_perm_sublist {α : Type uu} {l₁ : List α} {l₂ : List α} {l₂' : List α} (s : l₁ <+ l₂)
    (p : l₂ ~ l₂') : ∃ (l₁' : List α), ∃ (H : l₁' ~ l₁), l₁' <+ l₂' :=
  sorry

theorem perm.sizeof_eq_sizeof {α : Type uu} [SizeOf α] {l₁ : List α} {l₂ : List α} (h : l₁ ~ l₂) :
    list.sizeof l₁ = list.sizeof l₂ :=
  sorry

theorem perm_comp_perm {α : Type uu} : relation.comp perm perm = perm := sorry

theorem perm_comp_forall₂ {α : Type uu} {β : Type vv} {r : α → β → Prop} {l : List α} {u : List α}
    {v : List β} (hlu : l ~ u) (huv : forall₂ r u v) : relation.comp (forall₂ r) perm l v :=
  sorry

theorem forall₂_comp_perm_eq_perm_comp_forall₂ {α : Type uu} {β : Type vv} {r : α → β → Prop} :
    relation.comp (forall₂ r) perm = relation.comp perm (forall₂ r) :=
  sorry

theorem rel_perm_imp {α : Type uu} {β : Type vv} {r : α → β → Prop} (hr : relator.right_unique r) :
    relator.lift_fun (forall₂ r) (forall₂ r ⇒ implies) perm perm :=
  sorry

theorem rel_perm {α : Type uu} {β : Type vv} {r : α → β → Prop} (hr : relator.bi_unique r) :
    relator.lift_fun (forall₂ r) (forall₂ r ⇒ Iff) perm perm :=
  sorry

/-- `subperm l₁ l₂`, denoted `l₁ <+~ l₂`, means that `l₁` is a sublist of
  a permutation of `l₂`. This is an analogue of `l₁ ⊆ l₂` which respects
  multiplicities of elements, and is used for the `≤` relation on multisets. -/
def subperm {α : Type uu} (l₁ : List α) (l₂ : List α) := ∃ (l : List α), ∃ (H : l ~ l₁), l <+ l₂

infixl:50 " <+~ " => Mathlib.list.subperm

theorem nil_subperm {α : Type uu} {l : List α} : [] <+~ l :=
  Exists.intro []
    (Exists.intro perm.nil
      (eq.mpr (id (propext ((fun {α : Type uu} (l : List α) => iff_true_intro (nil_sublist l)) l)))
        trivial))

theorem perm.subperm_left {α : Type uu} {l : List α} {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂) :
    l <+~ l₁ ↔ l <+~ l₂ :=
  sorry

theorem perm.subperm_right {α : Type uu} {l₁ : List α} {l₂ : List α} {l : List α} (p : l₁ ~ l₂) :
    l₁ <+~ l ↔ l₂ <+~ l :=
  sorry

theorem sublist.subperm {α : Type uu} {l₁ : List α} {l₂ : List α} (s : l₁ <+ l₂) : l₁ <+~ l₂ :=
  Exists.intro l₁ (Exists.intro (perm.refl l₁) s)

theorem perm.subperm {α : Type uu} {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂) : l₁ <+~ l₂ :=
  Exists.intro l₂ (Exists.intro (perm.symm p) (sublist.refl l₂))

theorem subperm.refl {α : Type uu} (l : List α) : l <+~ l := perm.subperm (perm.refl l)

theorem subperm.trans {α : Type uu} {l₁ : List α} {l₂ : List α} {l₃ : List α} :
    l₁ <+~ l₂ → l₂ <+~ l₃ → l₁ <+~ l₃ :=
  sorry

theorem subperm.length_le {α : Type uu} {l₁ : List α} {l₂ : List α} :
    l₁ <+~ l₂ → length l₁ ≤ length l₂ :=
  sorry

theorem subperm.perm_of_length_le {α : Type uu} {l₁ : List α} {l₂ : List α} :
    l₁ <+~ l₂ → length l₂ ≤ length l₁ → l₁ ~ l₂ :=
  sorry

theorem subperm.antisymm {α : Type uu} {l₁ : List α} {l₂ : List α} (h₁ : l₁ <+~ l₂)
    (h₂ : l₂ <+~ l₁) : l₁ ~ l₂ :=
  subperm.perm_of_length_le h₁ (subperm.length_le h₂)

theorem subperm.subset {α : Type uu} {l₁ : List α} {l₂ : List α} : l₁ <+~ l₂ → l₁ ⊆ l₂ := sorry

theorem sublist.exists_perm_append {α : Type uu} {l₁ : List α} {l₂ : List α} :
    l₁ <+ l₂ → ∃ (l : List α), l₂ ~ l₁ ++ l :=
  sorry

theorem perm.countp_eq {α : Type uu} (p : α → Prop) [decidable_pred p] {l₁ : List α} {l₂ : List α}
    (s : l₁ ~ l₂) : countp p l₁ = countp p l₂ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (countp p l₁ = countp p l₂)) (countp_eq_length_filter p l₁)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (length (filter p l₁) = countp p l₂)) (countp_eq_length_filter p l₂)))
      (perm.length_eq (perm.filter p s)))

theorem subperm.countp_le {α : Type uu} (p : α → Prop) [decidable_pred p] {l₁ : List α}
    {l₂ : List α} : l₁ <+~ l₂ → countp p l₁ ≤ countp p l₂ :=
  sorry

theorem perm.count_eq {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂)
    (a : α) : count a l₁ = count a l₂ :=
  perm.countp_eq (Eq a) p

theorem subperm.count_le {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} (s : l₁ <+~ l₂)
    (a : α) : count a l₁ ≤ count a l₂ :=
  subperm.countp_le (Eq a) s

theorem perm.foldl_eq' {α : Type uu} {β : Type vv} {f : β → α → β} {l₁ : List α} {l₂ : List α}
    (p : l₁ ~ l₂) :
    (∀ (x : α), x ∈ l₁ → ∀ (y : α), y ∈ l₁ → ∀ (z : β), f (f z x) y = f (f z y) x) →
        ∀ (b : β), foldl f b l₁ = foldl f b l₂ :=
  sorry

theorem perm.foldl_eq {α : Type uu} {β : Type vv} {f : β → α → β} {l₁ : List α} {l₂ : List α}
    (rcomm : right_commutative f) (p : l₁ ~ l₂) (b : β) : foldl f b l₁ = foldl f b l₂ :=
  perm.foldl_eq' p fun (x : α) (hx : x ∈ l₁) (y : α) (hy : y ∈ l₁) (z : β) => rcomm z x y

theorem perm.foldr_eq {α : Type uu} {β : Type vv} {f : α → β → β} {l₁ : List α} {l₂ : List α}
    (lcomm : left_commutative f) (p : l₁ ~ l₂) (b : β) : foldr f b l₁ = foldr f b l₂ :=
  sorry

theorem perm.rec_heq {α : Type uu} {β : List α → Sort u_1}
    {f : (a : α) → (l : List α) → β l → β (a :: l)} {b : β []} {l : List α} {l' : List α}
    (hl : l ~ l')
    (f_congr :
      ∀ {a : α} {l l' : List α} {b : β l} {b' : β l'}, l ~ l' → b == b' → f a l b == f a l' b')
    (f_swap :
      ∀ {a a' : α} {l : List α} {b : β l}, f a (a' :: l) (f a' l b) == f a' (a :: l) (f a l b)) :
    List.rec b f l == List.rec b f l' :=
  sorry

theorem perm.fold_op_eq {α : Type uu} {op : α → α → α} [is_associative α op] [is_commutative α op]
    {l₁ : List α} {l₂ : List α} {a : α} (h : l₁ ~ l₂) : foldl op a l₁ = foldl op a l₂ :=
  perm.foldl_eq (right_comm op is_commutative.comm is_associative.assoc) h a

/-- If elements of a list commute with each other, then their product does not
depend on the order of elements-/
theorem perm.sum_eq' {α : Type uu} [add_monoid α] {l₁ : List α} {l₂ : List α} (h : l₁ ~ l₂)
    (hc : pairwise (fun (x y : α) => x + y = y + x) l₁) : sum l₁ = sum l₂ :=
  sorry

theorem perm.sum_eq {α : Type uu} [add_comm_monoid α] {l₁ : List α} {l₂ : List α} (h : l₁ ~ l₂) :
    sum l₁ = sum l₂ :=
  perm.fold_op_eq h

theorem sum_reverse {α : Type uu} [add_comm_monoid α] (l : List α) : sum (reverse l) = sum l :=
  perm.sum_eq (reverse_perm l)

theorem perm_inv_core {α : Type uu} {a : α} {l₁ : List α} {l₂ : List α} {r₁ : List α}
    {r₂ : List α} : l₁ ++ a :: r₁ ~ l₂ ++ a :: r₂ → l₁ ++ r₁ ~ l₂ ++ r₂ :=
  sorry

theorem perm.cons_inv {α : Type uu} {a : α} {l₁ : List α} {l₂ : List α} :
    a :: l₁ ~ a :: l₂ → l₁ ~ l₂ :=
  perm_inv_core

@[simp] theorem perm_cons {α : Type uu} (a : α) {l₁ : List α} {l₂ : List α} :
    a :: l₁ ~ a :: l₂ ↔ l₁ ~ l₂ :=
  { mp := perm.cons_inv, mpr := perm.cons a }

theorem perm_append_left_iff {α : Type uu} {l₁ : List α} {l₂ : List α} (l : List α) :
    l ++ l₁ ~ l ++ l₂ ↔ l₁ ~ l₂ :=
  sorry

theorem perm_append_right_iff {α : Type uu} {l₁ : List α} {l₂ : List α} (l : List α) :
    l₁ ++ l ~ l₂ ++ l ↔ l₁ ~ l₂ :=
  sorry

theorem perm_option_to_list {α : Type uu} {o₁ : Option α} {o₂ : Option α} :
    option.to_list o₁ ~ option.to_list o₂ ↔ o₁ = o₂ :=
  sorry

theorem subperm_cons {α : Type uu} (a : α) {l₁ : List α} {l₂ : List α} :
    a :: l₁ <+~ a :: l₂ ↔ l₁ <+~ l₂ :=
  sorry

theorem cons_subperm_of_mem {α : Type uu} {a : α} {l₁ : List α} {l₂ : List α} (d₁ : nodup l₁)
    (h₁ : ¬a ∈ l₁) (h₂ : a ∈ l₂) (s : l₁ <+~ l₂) : a :: l₁ <+~ l₂ :=
  sorry

theorem subperm_append_left {α : Type uu} {l₁ : List α} {l₂ : List α} (l : List α) :
    l ++ l₁ <+~ l ++ l₂ ↔ l₁ <+~ l₂ :=
  sorry

theorem subperm_append_right {α : Type uu} {l₁ : List α} {l₂ : List α} (l : List α) :
    l₁ ++ l <+~ l₂ ++ l ↔ l₁ <+~ l₂ :=
  iff.trans (iff.trans (perm.subperm_left perm_append_comm) (perm.subperm_right perm_append_comm))
    (subperm_append_left l)

theorem subperm.exists_of_length_lt {α : Type uu} {l₁ : List α} {l₂ : List α} :
    l₁ <+~ l₂ → length l₁ < length l₂ → ∃ (a : α), a :: l₁ <+~ l₂ :=
  sorry

theorem subperm_of_subset_nodup {α : Type uu} {l₁ : List α} {l₂ : List α} (d : nodup l₁)
    (H : l₁ ⊆ l₂) : l₁ <+~ l₂ :=
  sorry

theorem perm_ext {α : Type uu} {l₁ : List α} {l₂ : List α} (d₁ : nodup l₁) (d₂ : nodup l₂) :
    l₁ ~ l₂ ↔ ∀ (a : α), a ∈ l₁ ↔ a ∈ l₂ :=
  sorry

theorem nodup.sublist_ext {α : Type uu} {l₁ : List α} {l₂ : List α} {l : List α} (d : nodup l)
    (s₁ : l₁ <+ l) (s₂ : l₂ <+ l) : l₁ ~ l₂ ↔ l₁ = l₂ :=
  sorry

-- attribute [congr]

theorem perm.erase {α : Type uu} [DecidableEq α] (a : α) {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂) :
    list.erase l₁ a ~ list.erase l₂ a :=
  sorry

theorem subperm_cons_erase {α : Type uu} [DecidableEq α] (a : α) (l : List α) :
    l <+~ a :: list.erase l a :=
  sorry

theorem erase_subperm {α : Type uu} [DecidableEq α] (a : α) (l : List α) : list.erase l a <+~ l :=
  sublist.subperm (erase_sublist a l)

theorem subperm.erase {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} (a : α)
    (h : l₁ <+~ l₂) : list.erase l₁ a <+~ list.erase l₂ a :=
  sorry

theorem perm.diff_right {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} (t : List α)
    (h : l₁ ~ l₂) : list.diff l₁ t ~ list.diff l₂ t :=
  sorry

theorem perm.diff_left {α : Type uu} [DecidableEq α] (l : List α) {t₁ : List α} {t₂ : List α}
    (h : t₁ ~ t₂) : list.diff l t₁ = list.diff l t₂ :=
  sorry

theorem perm.diff {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} {t₁ : List α}
    {t₂ : List α} (hl : l₁ ~ l₂) (ht : t₁ ~ t₂) : list.diff l₁ t₁ ~ list.diff l₂ t₂ :=
  perm.diff_left l₂ ht ▸ perm.diff_right t₁ hl

theorem subperm.diff_right {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} (h : l₁ <+~ l₂)
    (t : List α) : list.diff l₁ t <+~ list.diff l₂ t :=
  sorry

theorem erase_cons_subperm_cons_erase {α : Type uu} [DecidableEq α] (a : α) (b : α) (l : List α) :
    list.erase (a :: l) b <+~ a :: list.erase l b :=
  sorry

theorem subperm_cons_diff {α : Type uu} [DecidableEq α] {a : α} {l₁ : List α} {l₂ : List α} :
    list.diff (a :: l₁) l₂ <+~ a :: list.diff l₁ l₂ :=
  sorry

theorem subset_cons_diff {α : Type uu} [DecidableEq α] {a : α} {l₁ : List α} {l₂ : List α} :
    list.diff (a :: l₁) l₂ ⊆ a :: list.diff l₁ l₂ :=
  subperm.subset subperm_cons_diff

theorem perm.bag_inter_right {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} (t : List α)
    (h : l₁ ~ l₂) : list.bag_inter l₁ t ~ list.bag_inter l₂ t :=
  sorry

theorem perm.bag_inter_left {α : Type uu} [DecidableEq α] (l : List α) {t₁ : List α} {t₂ : List α}
    (p : t₁ ~ t₂) : list.bag_inter l t₁ = list.bag_inter l t₂ :=
  sorry

theorem perm.bag_inter {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} {t₁ : List α}
    {t₂ : List α} (hl : l₁ ~ l₂) (ht : t₁ ~ t₂) : list.bag_inter l₁ t₁ ~ list.bag_inter l₂ t₂ :=
  perm.bag_inter_left l₂ ht ▸ perm.bag_inter_right t₁ hl

theorem cons_perm_iff_perm_erase {α : Type uu} [DecidableEq α] {a : α} {l₁ : List α} {l₂ : List α} :
    a :: l₁ ~ l₂ ↔ a ∈ l₂ ∧ l₁ ~ list.erase l₂ a :=
  sorry

theorem perm_iff_count {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} :
    l₁ ~ l₂ ↔ ∀ (a : α), count a l₁ = count a l₂ :=
  sorry

protected instance decidable_perm {α : Type uu} [DecidableEq α] (l₁ : List α) (l₂ : List α) :
    Decidable (l₁ ~ l₂) :=
  sorry

-- @[congr]

theorem perm.erase_dup {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} (p : l₁ ~ l₂) :
    erase_dup l₁ ~ erase_dup l₂ :=
  sorry

-- attribute [congr]

theorem perm.insert {α : Type uu} [DecidableEq α] (a : α) {l₁ : List α} {l₂ : List α}
    (p : l₁ ~ l₂) : insert a l₁ ~ insert a l₂ :=
  sorry

theorem perm_insert_swap {α : Type uu} [DecidableEq α] (x : α) (y : α) (l : List α) :
    insert x (insert y l) ~ insert y (insert x l) :=
  sorry

theorem perm_insert_nth {α : Type u_1} (x : α) (l : List α) {n : ℕ} (h : n ≤ length l) :
    insert_nth n x l ~ x :: l :=
  sorry

theorem perm.union_right {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} (t₁ : List α)
    (h : l₁ ~ l₂) : l₁ ∪ t₁ ~ l₂ ∪ t₁ :=
  sorry

theorem perm.union_left {α : Type uu} [DecidableEq α] (l : List α) {t₁ : List α} {t₂ : List α}
    (h : t₁ ~ t₂) : l ∪ t₁ ~ l ∪ t₂ :=
  sorry

-- @[congr]

theorem perm.union {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} {t₁ : List α}
    {t₂ : List α} (p₁ : l₁ ~ l₂) (p₂ : t₁ ~ t₂) : l₁ ∪ t₁ ~ l₂ ∪ t₂ :=
  perm.trans (perm.union_right t₁ p₁) (perm.union_left l₂ p₂)

theorem perm.inter_right {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} (t₁ : List α) :
    l₁ ~ l₂ → l₁ ∩ t₁ ~ l₂ ∩ t₁ :=
  perm.filter fun (_x : α) => _x ∈ t₁

theorem perm.inter_left {α : Type uu} [DecidableEq α] (l : List α) {t₁ : List α} {t₂ : List α}
    (p : t₁ ~ t₂) : l ∩ t₁ = l ∩ t₂ :=
  sorry

-- @[congr]

theorem perm.inter {α : Type uu} [DecidableEq α] {l₁ : List α} {l₂ : List α} {t₁ : List α}
    {t₂ : List α} (p₁ : l₁ ~ l₂) (p₂ : t₁ ~ t₂) : l₁ ∩ t₁ ~ l₂ ∩ t₂ :=
  perm.inter_left l₂ p₂ ▸ perm.inter_right t₁ p₁

theorem perm.inter_append {α : Type uu} [DecidableEq α] {l : List α} {t₁ : List α} {t₂ : List α}
    (h : disjoint t₁ t₂) : l ∩ (t₁ ++ t₂) ~ l ∩ t₁ ++ l ∩ t₂ :=
  sorry

theorem perm.pairwise_iff {α : Type uu} {R : α → α → Prop} (S : symmetric R) {l₁ : List α}
    {l₂ : List α} (p : l₁ ~ l₂) : pairwise R l₁ ↔ pairwise R l₂ :=
  sorry

theorem perm.nodup_iff {α : Type uu} {l₁ : List α} {l₂ : List α} :
    l₁ ~ l₂ → (nodup l₁ ↔ nodup l₂) :=
  perm.pairwise_iff ne.symm

theorem perm.bind_right {α : Type uu} {β : Type vv} {l₁ : List α} {l₂ : List α} (f : α → List β)
    (p : l₁ ~ l₂) : list.bind l₁ f ~ list.bind l₂ f :=
  sorry

theorem perm.bind_left {α : Type uu} {β : Type vv} (l : List α) {f : α → List β} {g : α → List β}
    (h : ∀ (a : α), f a ~ g a) : list.bind l f ~ list.bind l g :=
  sorry

theorem perm.product_right {α : Type uu} {β : Type vv} {l₁ : List α} {l₂ : List α} (t₁ : List β)
    (p : l₁ ~ l₂) : product l₁ t₁ ~ product l₂ t₁ :=
  perm.bind_right (fun (a : α) => map (Prod.mk a) t₁) p

theorem perm.product_left {α : Type uu} {β : Type vv} (l : List α) {t₁ : List β} {t₂ : List β}
    (p : t₁ ~ t₂) : product l t₁ ~ product l t₂ :=
  perm.bind_left l fun (a : α) => perm.map (Prod.mk a) p

theorem perm.product {α : Type uu} {β : Type vv} {l₁ : List α} {l₂ : List α} {t₁ : List β}
    {t₂ : List β} (p₁ : l₁ ~ l₂) (p₂ : t₁ ~ t₂) : product l₁ t₁ ~ product l₂ t₂ :=
  perm.trans (perm.product_right t₁ p₁) (perm.product_left l₂ p₂)

theorem sublists_cons_perm_append {α : Type uu} (a : α) (l : List α) :
    sublists (a :: l) ~ sublists l ++ map (List.cons a) (sublists l) :=
  sorry

theorem sublists_perm_sublists' {α : Type uu} (l : List α) : sublists l ~ sublists' l := sorry

theorem revzip_sublists {α : Type uu} (l : List α) (l₁ : List α) (l₂ : List α) :
    (l₁, l₂) ∈ revzip (sublists l) → l₁ ++ l₂ ~ l :=
  sorry

theorem revzip_sublists' {α : Type uu} (l : List α) (l₁ : List α) (l₂ : List α) :
    (l₁, l₂) ∈ revzip (sublists' l) → l₁ ++ l₂ ~ l :=
  sorry

theorem perm_lookmap {α : Type uu} (f : α → Option α) {l₁ : List α} {l₂ : List α}
    (H : pairwise (fun (a b : α) => ∀ (c : α), c ∈ f a → ∀ (d : α), d ∈ f b → a = b ∧ c = d) l₁)
    (p : l₁ ~ l₂) : lookmap f l₁ ~ lookmap f l₂ :=
  sorry

theorem perm.erasep {α : Type uu} (f : α → Prop) [decidable_pred f] {l₁ : List α} {l₂ : List α}
    (H : pairwise (fun (a b : α) => f a → f b → False) l₁) (p : l₁ ~ l₂) :
    erasep f l₁ ~ erasep f l₂ :=
  sorry

theorem perm.take_inter {α : Type u_1} [DecidableEq α] {xs : List α} {ys : List α} (n : ℕ)
    (h : xs ~ ys) (h' : nodup ys) : take n xs ~ list.inter ys (take n xs) :=
  sorry

theorem perm.drop_inter {α : Type u_1} [DecidableEq α] {xs : List α} {ys : List α} (n : ℕ)
    (h : xs ~ ys) (h' : nodup ys) : drop n xs ~ list.inter ys (drop n xs) :=
  sorry

theorem perm.slice_inter {α : Type u_1} [DecidableEq α] {xs : List α} {ys : List α} (n : ℕ) (m : ℕ)
    (h : xs ~ ys) (h' : nodup ys) : slice n m xs ~ ys ∩ slice n m xs :=
  sorry

/- enumerating permutations -/

theorem permutations_aux2_fst {α : Type uu} {β : Type vv} (t : α) (ts : List α) (r : List β)
    (ys : List α) (f : List α → β) : prod.fst (permutations_aux2 t ts r ys f) = ys ++ ts :=
  sorry

@[simp] theorem permutations_aux2_snd_nil {α : Type uu} {β : Type vv} (t : α) (ts : List α)
    (r : List β) (f : List α → β) : prod.snd (permutations_aux2 t ts r [] f) = r :=
  rfl

@[simp] theorem permutations_aux2_snd_cons {α : Type uu} {β : Type vv} (t : α) (ts : List α)
    (r : List β) (y : α) (ys : List α) (f : List α → β) :
    prod.snd (permutations_aux2 t ts r (y :: ys) f) =
        f (t :: y :: ys ++ ts) ::
          prod.snd (permutations_aux2 t ts r ys fun (x : List α) => f (y :: x)) :=
  sorry

theorem permutations_aux2_append {α : Type uu} {β : Type vv} (t : α) (ts : List α) (r : List β)
    (ys : List α) (f : List α → β) :
    prod.snd (permutations_aux2 t ts [] ys f) ++ r = prod.snd (permutations_aux2 t ts r ys f) :=
  sorry

theorem mem_permutations_aux2 {α : Type uu} {t : α} {ts : List α} {ys : List α} {l : List α}
    {l' : List α} :
    l' ∈ prod.snd (permutations_aux2 t ts [] ys (append l)) ↔
        ∃ (l₁ : List α), ∃ (l₂ : List α), l₂ ≠ [] ∧ ys = l₁ ++ l₂ ∧ l' = l ++ l₁ ++ t :: l₂ ++ ts :=
  sorry

theorem mem_permutations_aux2' {α : Type uu} {t : α} {ts : List α} {ys : List α} {l : List α} :
    l ∈ prod.snd (permutations_aux2 t ts [] ys id) ↔
        ∃ (l₁ : List α), ∃ (l₂ : List α), l₂ ≠ [] ∧ ys = l₁ ++ l₂ ∧ l = l₁ ++ t :: l₂ ++ ts :=
  sorry

theorem length_permutations_aux2 {α : Type uu} {β : Type vv} (t : α) (ts : List α) (ys : List α)
    (f : List α → β) : length (prod.snd (permutations_aux2 t ts [] ys f)) = length ys :=
  sorry

theorem foldr_permutations_aux2 {α : Type uu} (t : α) (ts : List α) (r : List (List α))
    (L : List (List α)) :
    foldr (fun (y : List α) (r : List (List α)) => prod.snd (permutations_aux2 t ts r y id)) r L =
        (list.bind L fun (y : List α) => prod.snd (permutations_aux2 t ts [] y id)) ++ r :=
  sorry

theorem mem_foldr_permutations_aux2 {α : Type uu} {t : α} {ts : List α} {r : List (List α)}
    {L : List (List α)} {l' : List α} :
    l' ∈
          foldr (fun (y : List α) (r : List (List α)) => prod.snd (permutations_aux2 t ts r y id)) r
            L ↔
        l' ∈ r ∨
          ∃ (l₁ : List α), ∃ (l₂ : List α), l₁ ++ l₂ ∈ L ∧ l₂ ≠ [] ∧ l' = l₁ ++ t :: l₂ ++ ts :=
  sorry

theorem length_foldr_permutations_aux2 {α : Type uu} (t : α) (ts : List α) (r : List (List α))
    (L : List (List α)) :
    length
          (foldr (fun (y : List α) (r : List (List α)) => prod.snd (permutations_aux2 t ts r y id))
            r L) =
        sum (map length L) + length r :=
  sorry

theorem length_foldr_permutations_aux2' {α : Type uu} (t : α) (ts : List α) (r : List (List α))
    (L : List (List α)) (n : ℕ) (H : ∀ (l : List α), l ∈ L → length l = n) :
    length
          (foldr (fun (y : List α) (r : List (List α)) => prod.snd (permutations_aux2 t ts r y id))
            r L) =
        n * length L + length r :=
  sorry

theorem perm_of_mem_permutations_aux {α : Type uu} {ts : List α} {is : List α} {l : List α} :
    l ∈ permutations_aux ts is → l ~ ts ++ is :=
  sorry

theorem perm_of_mem_permutations {α : Type uu} {l₁ : List α} {l₂ : List α}
    (h : l₁ ∈ permutations l₂) : l₁ ~ l₂ :=
  or.elim (eq_or_mem_of_mem_cons h) (fun (e : l₁ = l₂) => e ▸ perm.refl l₁)
    fun (m : l₁ ∈ permutations_aux l₂ []) => append_nil l₂ ▸ perm_of_mem_permutations_aux m

theorem length_permutations_aux {α : Type uu} (ts : List α) (is : List α) :
    length (permutations_aux ts is) + nat.factorial (length is) =
        nat.factorial (length ts + length is) :=
  sorry

theorem length_permutations {α : Type uu} (l : List α) :
    length (permutations l) = nat.factorial (length l) :=
  length_permutations_aux l []

theorem mem_permutations_of_perm_lemma {α : Type uu} {is : List α} {l : List α}
    (H :
      l ~ [] ++ is →
        (∃ (ts' : List α), ∃ (H : ts' ~ []), l = ts' ++ is) ∨ l ∈ permutations_aux is []) :
    l ~ is → l ∈ permutations is :=
  sorry

theorem mem_permutations_aux_of_perm {α : Type uu} {ts : List α} {is : List α} {l : List α} :
    l ~ is ++ ts →
        (∃ (is' : List α), ∃ (H : is' ~ is), l = is' ++ ts) ∨ l ∈ permutations_aux ts is :=
  sorry

@[simp] theorem mem_permutations {α : Type uu} (s : List α) (t : List α) :
    s ∈ permutations t ↔ s ~ t :=
  { mp := perm_of_mem_permutations,
    mpr := mem_permutations_of_perm_lemma mem_permutations_aux_of_perm }

end Mathlib


/-
Copyright (c) 2021 Oliver Nash. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Oliver Nash, Eric Wieser
-/
import topology.algebra.infinite_sum.basic
import topology.algebra.ring.basic
import topology.algebra.star
import linear_algebra.matrix.nonsingular_inverse
import linear_algebra.matrix.trace

/-!
# Topological properties of matrices

This file is a place to collect topological results about matrices.

## Main definitions:

* `matrix.topological_ring`: square matrices form a topological ring

## Main results

* Continuity:
  * `continuous.matrix_det`: the determinant is continuous over a topological ring.
  * `continuous.matrix_adjugate`: the adjugate is continuous over a topological ring.
* Infinite sums
  * `matrix.transpose_tsum`: transpose commutes with infinite sums
  * `matrix.diagonal_tsum`: diagonal commutes with infinite sums
  * `matrix.block_diagonal_tsum`: block diagonal commutes with infinite sums
  * `matrix.block_diagonal'_tsum`: non-uniform block diagonal commutes with infinite sums
-/

open matrix
open_locale matrix

variables {X α l m n p S R : Type*} {m' n' : l → Type*}

instance [topological_space R] : topological_space (matrix m n R) := Pi.topological_space

instance [topological_space R] [t2_space R] : t2_space (matrix m n R) := Pi.t2_space

/-! ### Lemmas about continuity of operations -/
section continuity
variables [topological_space X] [topological_space R]

instance [has_smul α R] [has_continuous_const_smul α R] :
  has_continuous_const_smul α (matrix m n R) :=
pi.has_continuous_const_smul

instance [topological_space α] [has_smul α R] [has_continuous_smul α R] :
  has_continuous_smul α (matrix m n R) :=
pi.has_continuous_smul

instance [has_add R] [has_continuous_add R] : has_continuous_add (matrix m n R) :=
pi.has_continuous_add

instance [has_neg R] [has_continuous_neg R] : has_continuous_neg (matrix m n R) :=
pi.has_continuous_neg

instance [add_group R] [topological_add_group R] : topological_add_group (matrix m n R) :=
pi.topological_add_group

/-- To show a function into matrices is continuous it suffices to show the coefficients of the
resulting matrix are continuous -/
@[continuity]
lemma continuous_matrix [topological_space α] {f : α → matrix m n R}
  (h : ∀ i j, continuous (λ a, f a i j)) : continuous f :=
continuous_pi $ λ _, continuous_pi $ λ j, h _ _

lemma continuous.matrix_elem {A : X → matrix m n R} (hA : continuous A) (i : m) (j : n) :
  continuous (λ x, A x i j) :=
(continuous_apply_apply i j).comp hA

@[continuity]
lemma continuous.matrix_map [topological_space S] {A : X → matrix m n S} {f : S → R}
   (hA : continuous A) (hf : continuous f) :
  continuous (λ x, (A x).map f) :=
continuous_matrix $ λ i j, hf.comp $ hA.matrix_elem _ _

@[continuity]
lemma continuous.matrix_transpose {A : X → matrix m n R} (hA : continuous A) :
  continuous (λ x, (A x)ᵀ) :=
continuous_matrix $ λ i j, hA.matrix_elem j i

lemma continuous.matrix_conj_transpose [has_star R] [has_continuous_star R] {A : X → matrix m n R}
  (hA : continuous A) : continuous (λ x, (A x)ᴴ) :=
hA.matrix_transpose.matrix_map continuous_star

instance [has_star R] [has_continuous_star R] : has_continuous_star (matrix m m R) :=
⟨continuous_id.matrix_conj_transpose⟩

@[continuity]
lemma continuous.matrix_col {A : X → n → R} (hA : continuous A) : continuous (λ x, col (A x)) :=
continuous_matrix $ λ i j, (continuous_apply _).comp hA

@[continuity]
lemma continuous.matrix_row {A : X → n → R} (hA : continuous A) : continuous (λ x, row (A x)) :=
continuous_matrix $ λ i j, (continuous_apply _).comp hA

@[continuity]
lemma continuous.matrix_diagonal [has_zero R] [decidable_eq n] {A : X → n → R} (hA : continuous A) :
  continuous (λ x, diagonal (A x)) :=
continuous_matrix $ λ i j, ((continuous_apply i).comp hA).if_const _ continuous_zero

@[continuity]
lemma continuous.matrix_dot_product [fintype n] [has_mul R] [add_comm_monoid R]
  [has_continuous_add R] [has_continuous_mul R]
  {A : X → n → R} {B : X → n → R} (hA : continuous A) (hB : continuous B) :
  continuous (λ x, dot_product (A x) (B x)) :=
continuous_finset_sum _ $ λ i _, ((continuous_apply i).comp hA).mul ((continuous_apply i).comp hB)

/-- For square matrices the usual `continuous_mul` can be used. -/
@[continuity]
lemma continuous.matrix_mul [fintype n] [has_mul R] [add_comm_monoid R] [has_continuous_add R]
  [has_continuous_mul R]
  {A : X → matrix m n R} {B : X → matrix n p R} (hA : continuous A) (hB : continuous B) :
  continuous (λ x, (A x).mul (B x)) :=
continuous_matrix $ λ i j, continuous_finset_sum _ $ λ k _,
  (hA.matrix_elem _ _).mul (hB.matrix_elem _ _)

instance [fintype n] [has_mul R] [add_comm_monoid R] [has_continuous_add R]
  [has_continuous_mul R] : has_continuous_mul (matrix n n R) :=
⟨continuous_fst.matrix_mul continuous_snd⟩

instance [fintype n] [non_unital_non_assoc_semiring R] [topological_semiring R] :
  topological_semiring (matrix n n R) :=
{}

instance [fintype n] [non_unital_non_assoc_ring R] [topological_ring R] :
  topological_ring (matrix n n R) :=
{}

@[continuity]
lemma continuous.matrix_vec_mul_vec [has_mul R] [has_continuous_mul R]
  {A : X → m → R} {B : X → n → R} (hA : continuous A) (hB : continuous B) :
  continuous (λ x, vec_mul_vec (A x) (B x)) :=
continuous_matrix $ λ i j, ((continuous_apply _).comp hA).mul ((continuous_apply _).comp hB)

@[continuity]
lemma continuous.matrix_mul_vec [non_unital_non_assoc_semiring R] [has_continuous_add R]
  [has_continuous_mul R] [fintype n]
  {A : X → matrix m n R} {B : X → n → R} (hA : continuous A) (hB : continuous B) :
  continuous (λ x, (A x).mul_vec (B x)) :=
continuous_pi $ λ i, ((continuous_apply i).comp hA).matrix_dot_product hB

@[continuity]
lemma continuous.matrix_vec_mul [non_unital_non_assoc_semiring R] [has_continuous_add R]
  [has_continuous_mul R] [fintype m]
  {A : X → m → R} {B : X → matrix m n R} (hA : continuous A) (hB : continuous B) :
  continuous (λ x, vec_mul (A x) (B x)) :=
continuous_pi $ λ i, hA.matrix_dot_product $ continuous_pi $ λ j, hB.matrix_elem _ _

@[continuity]
lemma continuous.matrix_submatrix
  {A : X → matrix l n R} (hA : continuous A) (e₁ : m → l) (e₂ : p → n) :
  continuous (λ x, (A x).submatrix e₁ e₂) :=
continuous_matrix $ λ i j, hA.matrix_elem _ _

@[continuity]
lemma continuous.matrix_reindex {A : X → matrix l n R}
  (hA : continuous A) (e₁ : l ≃ m) (e₂ : n ≃ p) :
  continuous (λ x, reindex e₁ e₂ (A x)) :=
hA.matrix_submatrix _ _

@[continuity]
lemma continuous.matrix_diag {A : X → matrix n n R} (hA : continuous A) :
  continuous (λ x, matrix.diag (A x)) :=
continuous_pi $ λ _, hA.matrix_elem _ _

-- note this doesn't elaborate well from the above
lemma continuous_matrix_diag : continuous (matrix.diag : matrix n n R → n → R) :=
show continuous (λ x : matrix n n R, matrix.diag x), from continuous_id.matrix_diag

@[continuity]
lemma continuous.matrix_trace [fintype n] [add_comm_monoid R] [has_continuous_add R]
  {A : X → matrix n n R} (hA : continuous A) :
  continuous (λ x, trace (A x)) :=
continuous_finset_sum _ $ λ i hi, hA.matrix_elem _ _

@[continuity]
lemma continuous.matrix_det [fintype n] [decidable_eq n] [comm_ring R] [topological_ring R]
  {A : X → matrix n n R} (hA : continuous A) :
  continuous (λ x, (A x).det) :=
begin
  simp_rw matrix.det_apply,
  refine continuous_finset_sum _ (λ l _, continuous.const_smul _ _),
  refine continuous_finset_prod _ (λ l _, hA.matrix_elem _ _),
end

@[continuity]
lemma continuous.matrix_update_column [decidable_eq n] (i : n)
  {A : X → matrix m n R} {B : X → m → R} (hA : continuous A) (hB : continuous B) :
  continuous (λ x, (A x).update_column i (B x)) :=
continuous_matrix $ λ j k, (continuous_apply k).comp $
  ((continuous_apply _).comp hA).update i ((continuous_apply _).comp hB)

@[continuity]
lemma continuous.matrix_update_row [decidable_eq m] (i : m)
  {A : X → matrix m n R} {B : X → n → R} (hA : continuous A) (hB : continuous B) :
  continuous (λ x, (A x).update_row i (B x)) :=
hA.update i hB

@[continuity]
lemma continuous.matrix_cramer [fintype n] [decidable_eq n] [comm_ring R] [topological_ring R]
  {A : X → matrix n n R} {B : X → n → R} (hA : continuous A) (hB : continuous B) :
  continuous (λ x, (A x).cramer (B x)) :=
continuous_pi $ λ i, (hA.matrix_update_column _ hB).matrix_det

@[continuity]
lemma continuous.matrix_adjugate [fintype n] [decidable_eq n] [comm_ring R] [topological_ring R]
  {A : X → matrix n n R} (hA : continuous A) :
  continuous (λ x, (A x).adjugate) :=
continuous_matrix $ λ j k, (hA.matrix_transpose.matrix_update_column k continuous_const).matrix_det

/-- When `ring.inverse` is continuous at the determinant (such as in a `normed_ring`, or a
`topological_field`), so is `matrix.has_inv`. -/
lemma continuous_at_matrix_inv [fintype n] [decidable_eq n] [comm_ring R] [topological_ring R]
  (A : matrix n n R) (h : continuous_at ring.inverse A.det) :
  continuous_at has_inv.inv A :=
(h.comp continuous_id.matrix_det.continuous_at).smul continuous_id.matrix_adjugate.continuous_at

-- lemmas about functions in `data/matrix/block.lean`
section block_matrices

@[continuity]
lemma continuous.matrix_from_blocks
  {A : X → matrix n l R} {B : X → matrix n m R} {C : X → matrix p l R} {D : X → matrix p m R}
  (hA : continuous A) (hB : continuous B) (hC : continuous C) (hD : continuous D) :
  continuous (λ x, matrix.from_blocks (A x) (B x) (C x) (D x)) :=
continuous_matrix $ λ i j,
  by cases i; cases j; refine continuous.matrix_elem _ i j; assumption

@[continuity]
lemma continuous.matrix_block_diagonal [has_zero R] [decidable_eq p] {A : X → p → matrix m n R}
  (hA : continuous A) :
  continuous (λ x, block_diagonal (A x)) :=
continuous_matrix $ λ ⟨i₁, i₂⟩ ⟨j₁, j₂⟩,
  (((continuous_apply i₂).comp hA).matrix_elem i₁ j₁).if_const _ continuous_zero

@[continuity]
lemma continuous.matrix_block_diag {A : X → matrix (m × p) (n × p) R} (hA : continuous A) :
  continuous (λ x, block_diag (A x)) :=
continuous_pi $ λ i, continuous_matrix $ λ j k, hA.matrix_elem _ _

@[continuity]
lemma continuous.matrix_block_diagonal' [has_zero R] [decidable_eq l]
  {A : X → Π i, matrix (m' i) (n' i) R} (hA : continuous A) :
  continuous (λ x, block_diagonal' (A x)) :=
continuous_matrix $ λ ⟨i₁, i₂⟩ ⟨j₁, j₂⟩, begin
  dsimp only [block_diagonal'_apply'],
  split_ifs,
  { subst h,
    exact ((continuous_apply i₁).comp hA).matrix_elem i₂ j₂ },
  { exact continuous_const },
end

@[continuity]
lemma continuous.matrix_block_diag' {A : X → matrix (Σ i, m' i) (Σ i, n' i) R} (hA : continuous A) :
  continuous (λ x, block_diag' (A x)) :=
continuous_pi $ λ i, continuous_matrix $ λ j k, hA.matrix_elem _ _

end block_matrices

end continuity

/-! ### Lemmas about infinite sums -/
section tsum
variables [semiring α] [add_comm_monoid R] [topological_space R] [module α R]

lemma has_sum.matrix_transpose {f : X → matrix m n R} {a : matrix m n R} (hf : has_sum f a) :
  has_sum (λ x, (f x)ᵀ) aᵀ :=
(hf.map (matrix.transpose_add_equiv m n R) continuous_id.matrix_transpose : _)

lemma summable.matrix_transpose {f : X → matrix m n R} (hf : summable f) :
  summable (λ x, (f x)ᵀ) :=
hf.has_sum.matrix_transpose.summable

@[simp] lemma summable_matrix_transpose {f : X → matrix m n R} :
  summable (λ x, (f x)ᵀ) ↔ summable f :=
(summable.map_iff_of_equiv (matrix.transpose_add_equiv m n R)
    (@continuous_id (matrix m n R) _).matrix_transpose (continuous_id.matrix_transpose) : _)

lemma matrix.transpose_tsum [t2_space R] {f : X → matrix m n R} : (∑' x, f x)ᵀ = ∑' x, (f x)ᵀ :=
begin
  by_cases hf : summable f,
  { exact hf.has_sum.matrix_transpose.tsum_eq.symm },
  { have hft := summable_matrix_transpose.not.mpr hf,
    rw [tsum_eq_zero_of_not_summable hf, tsum_eq_zero_of_not_summable hft, transpose_zero] },
end

lemma has_sum.matrix_conj_transpose [star_add_monoid R] [has_continuous_star R]
  {f : X → matrix m n R} {a : matrix m n R} (hf : has_sum f a) :
  has_sum (λ x, (f x)ᴴ) aᴴ :=
(hf.map (matrix.conj_transpose_add_equiv m n R) continuous_id.matrix_conj_transpose : _)

lemma summable.matrix_conj_transpose [star_add_monoid R] [has_continuous_star R]
  {f : X → matrix m n R} (hf : summable f) :
  summable (λ x, (f x)ᴴ) :=
hf.has_sum.matrix_conj_transpose.summable

@[simp] lemma summable_matrix_conj_transpose [star_add_monoid R] [has_continuous_star R]
  {f : X → matrix m n R} :
  summable (λ x, (f x)ᴴ) ↔ summable f :=
(summable.map_iff_of_equiv (matrix.conj_transpose_add_equiv m n R)
  (@continuous_id (matrix m n R) _).matrix_conj_transpose (continuous_id.matrix_conj_transpose) : _)

lemma matrix.conj_transpose_tsum [star_add_monoid R] [has_continuous_star R] [t2_space R]
  {f : X → matrix m n R} : (∑' x, f x)ᴴ = ∑' x, (f x)ᴴ :=
begin
  by_cases hf : summable f,
  { exact hf.has_sum.matrix_conj_transpose.tsum_eq.symm },
  { have hft := summable_matrix_conj_transpose.not.mpr hf,
    rw [tsum_eq_zero_of_not_summable hf, tsum_eq_zero_of_not_summable hft, conj_transpose_zero] },
end

lemma has_sum.matrix_diagonal [decidable_eq n] {f : X → n → R} {a : n → R} (hf : has_sum f a) :
  has_sum (λ x, diagonal (f x)) (diagonal a) :=
(hf.map (diagonal_add_monoid_hom n R) $ continuous.matrix_diagonal $ by exact continuous_id : _)

lemma summable.matrix_diagonal [decidable_eq n] {f : X → n → R} (hf : summable f) :
  summable (λ x, diagonal (f x)) :=
hf.has_sum.matrix_diagonal.summable

@[simp] lemma summable_matrix_diagonal [decidable_eq n] {f : X → n → R} :
  summable (λ x, diagonal (f x)) ↔ summable f :=
(summable.map_iff_of_left_inverse
  (@matrix.diagonal_add_monoid_hom n R _ _) (matrix.diag_add_monoid_hom n R)
  (by exact continuous.matrix_diagonal continuous_id)
  continuous_matrix_diag
  (λ A, diag_diagonal A) : _)

lemma matrix.diagonal_tsum [decidable_eq n] [t2_space R] {f : X → n → R} :
  diagonal (∑' x, f x) = ∑' x, diagonal (f x) :=
begin
  by_cases hf : summable f,
  { exact hf.has_sum.matrix_diagonal.tsum_eq.symm },
  { have hft := summable_matrix_diagonal.not.mpr hf,
    rw [tsum_eq_zero_of_not_summable hf, tsum_eq_zero_of_not_summable hft],
    exact diagonal_zero },
end

lemma has_sum.matrix_diag {f : X → matrix n n R} {a : matrix n n R} (hf : has_sum f a) :
  has_sum (λ x, diag (f x)) (diag a) :=
(hf.map (diag_add_monoid_hom n R) continuous_matrix_diag : _)

lemma summable.matrix_diag {f : X → matrix n n R} (hf : summable f) : summable (λ x, diag (f x)) :=
hf.has_sum.matrix_diag.summable

section block_matrices

lemma has_sum.matrix_block_diagonal [decidable_eq p]
  {f : X → p → matrix m n R} {a : p → matrix m n R} (hf : has_sum f a) :
  has_sum (λ x, block_diagonal (f x)) (block_diagonal a) :=
(hf.map (block_diagonal_add_monoid_hom m n p R) $
  continuous.matrix_block_diagonal $ by exact continuous_id : _)

lemma summable.matrix_block_diagonal [decidable_eq p] {f : X → p → matrix m n R} (hf : summable f) :
  summable (λ x, block_diagonal (f x)) :=
hf.has_sum.matrix_block_diagonal.summable

lemma summable_matrix_block_diagonal [decidable_eq p] {f : X → p → matrix m n R} :
  summable (λ x, block_diagonal (f x)) ↔ summable f :=
(summable.map_iff_of_left_inverse
  (matrix.block_diagonal_add_monoid_hom m n p R) (matrix.block_diag_add_monoid_hom m n p R)
  (by exact continuous.matrix_block_diagonal continuous_id)
  (by exact continuous.matrix_block_diag continuous_id)
  (λ A, block_diag_block_diagonal A) : _)

lemma matrix.block_diagonal_tsum [decidable_eq p] [t2_space R] {f : X → p → matrix m n R} :
  block_diagonal (∑' x, f x) = ∑' x, block_diagonal (f x) :=
begin
  by_cases hf : summable f,
  { exact hf.has_sum.matrix_block_diagonal.tsum_eq.symm },
  { have hft := summable_matrix_block_diagonal.not.mpr hf,
    rw [tsum_eq_zero_of_not_summable hf, tsum_eq_zero_of_not_summable hft],
    exact block_diagonal_zero },
end

lemma has_sum.matrix_block_diag {f : X → matrix (m × p) (n × p) R}
  {a : matrix (m × p) (n × p) R} (hf : has_sum f a) :
  has_sum (λ x, block_diag (f x)) (block_diag a) :=
(hf.map (block_diag_add_monoid_hom m n p R) $ continuous.matrix_block_diag continuous_id : _)

lemma summable.matrix_block_diag {f : X → matrix (m × p) (n × p) R} (hf : summable f) :
  summable (λ x, block_diag (f x)) :=
hf.has_sum.matrix_block_diag.summable

lemma has_sum.matrix_block_diagonal' [decidable_eq l]
  {f : X → Π i, matrix (m' i) (n' i) R} {a : Π i, matrix (m' i) (n' i) R} (hf : has_sum f a) :
  has_sum (λ x, block_diagonal' (f x)) (block_diagonal' a) :=
(hf.map (block_diagonal'_add_monoid_hom m' n' R) $
  continuous.matrix_block_diagonal' $ by exact continuous_id : _)

lemma summable.matrix_block_diagonal' [decidable_eq l]
  {f : X → Π i, matrix (m' i) (n' i) R} (hf : summable f) :
  summable (λ x, block_diagonal' (f x)) :=
hf.has_sum.matrix_block_diagonal'.summable

lemma summable_matrix_block_diagonal' [decidable_eq l] {f : X → Π i, matrix (m' i) (n' i) R} :
  summable (λ x, block_diagonal' (f x)) ↔ summable f :=
(summable.map_iff_of_left_inverse
  (matrix.block_diagonal'_add_monoid_hom m' n' R) (matrix.block_diag'_add_monoid_hom m' n' R)
  (by exact continuous.matrix_block_diagonal' continuous_id)
  (by exact continuous.matrix_block_diag' continuous_id)
  (λ A, block_diag'_block_diagonal' A) : _)

lemma matrix.block_diagonal'_tsum [decidable_eq l] [t2_space R]
  {f : X → Π i, matrix (m' i) (n' i) R} :
  block_diagonal' (∑' x, f x) = ∑' x, block_diagonal' (f x) :=
begin
  by_cases hf : summable f,
  { exact hf.has_sum.matrix_block_diagonal'.tsum_eq.symm },
  { have hft := summable_matrix_block_diagonal'.not.mpr hf,
    rw [tsum_eq_zero_of_not_summable hf, tsum_eq_zero_of_not_summable hft],
    exact block_diagonal'_zero },
end

lemma has_sum.matrix_block_diag' {f : X → matrix (Σ i, m' i) (Σ i, n' i) R}
  {a : matrix (Σ i, m' i) (Σ i, n' i) R} (hf : has_sum f a) :
  has_sum (λ x, block_diag' (f x)) (block_diag' a) :=
(hf.map (block_diag'_add_monoid_hom m' n' R) $ continuous.matrix_block_diag' continuous_id : _)

lemma summable.matrix_block_diag' {f : X → matrix (Σ i, m' i) (Σ i, n' i) R} (hf : summable f) :
  summable (λ x, block_diag' (f x)) :=
hf.has_sum.matrix_block_diag'.summable

end block_matrices

end tsum


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

! This file was ported from Lean 3 source module data.multiset.sort
! leanprover-community/mathlib commit 008205aa645b3f194c1da47025c5f110c8406eab
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.List.Sort
import Mathlib.Data.Multiset.Basic

/-!
# Construct a sorted list from a multiset.
-/

namespace Multiset

open List

variable {α : Type _}

section sort

variable (r : α → α → Prop) [DecidableRel r] [IsTrans α r] [IsAntisymm α r] [IsTotal α r]

/-- `sort s` constructs a sorted list from the multiset `s`.
  (Uses merge sort algorithm.) -/
def sort (s : Multiset α) : List α :=
  Quot.liftOn s (mergeSort r) fun _ _ h =>
    eq_of_perm_of_sorted ((perm_mergeSort _ _).trans <| h.trans (perm_mergeSort _ _).symm)
      (sorted_mergeSort r _) (sorted_mergeSort r _)
#align multiset.sort Multiset.sort

@[simp]

@[simp]
theorem sort_sorted (s : Multiset α) : Sorted r (sort r s) :=
  Quot.inductionOn s fun _l => sorted_mergeSort r _
#align multiset.sort_sorted Multiset.sort_sorted

@[simp]
theorem sort_eq (s : Multiset α) : ↑(sort r s) = s :=
  Quot.inductionOn s fun _ => Quot.sound <| perm_mergeSort _ _
#align multiset.sort_eq Multiset.sort_eq

@[simp]
theorem mem_sort {s : Multiset α} {a : α} : a ∈ sort r s ↔ a ∈ s := by rw [← mem_coe, sort_eq]
#align multiset.mem_sort Multiset.mem_sort

@[simp]
theorem length_sort {s : Multiset α} : (sort r s).length = card s :=
  Quot.inductionOn s <| length_mergeSort _
#align multiset.length_sort Multiset.length_sort

@[simp]
theorem sort_zero : sort r 0 = [] :=
  List.mergeSort_nil r
#align multiset.sort_zero Multiset.sort_zero

@[simp]
theorem sort_singleton (a : α) : sort r {a} = [a] :=
  List.mergeSort_singleton r a
#align multiset.sort_singleton Multiset.sort_singleton

end sort

-- TODO: use a sort order if available, gh-18166
unsafe instance [Repr α] : Repr (Multiset α) :=
  ⟨fun s _ => "{" ++ Std.Format.joinSep (s.unquot.map repr) ", " ++ "}"⟩

end Multiset


/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.mv_polynomial.rename
import Mathlib.PostPort

universes u_1 u_2 u_4 u_3

namespace Mathlib

/-!
# `comap` operation on `mv_polynomial`

This file defines the `comap` function on `mv_polynomial`.

`mv_polynomial.comap` is a low-tech example of a map of "algebraic varieties," modulo the fact that
`mathlib` does not yet define varieties.

## Notation

As in other polynomial files, we typically use the notation:

+ `σ : Type*` (indexing the variables)

+ `R : Type*` `[comm_semiring R]` (the coefficients)

-/

namespace mv_polynomial

/--
Given an algebra hom `f : mv_polynomial σ R →ₐ[R] mv_polynomial τ R`
and a variable evaluation `v : τ → R`,
`comap f v` produces a variable evaluation `σ → R`.
-/
def comap {σ : Type u_1} {τ : Type u_2} {R : Type u_4} [comm_semiring R] (f : alg_hom R (mv_polynomial σ R) (mv_polynomial τ R)) : (τ → R) → σ → R :=
  fun (x : τ → R) (i : σ) => coe_fn (aeval x) (coe_fn f (X i))

@[simp] theorem comap_apply {σ : Type u_1} {τ : Type u_2} {R : Type u_4} [comm_semiring R] (f : alg_hom R (mv_polynomial σ R) (mv_polynomial τ R)) (x : τ → R) (i : σ) : comap f x i = coe_fn (aeval x) (coe_fn f (X i)) :=
  rfl

@[simp] theorem comap_id_apply {σ : Type u_1} {R : Type u_4} [comm_semiring R] (x : σ → R) : comap (alg_hom.id R (mv_polynomial σ R)) x = x := sorry

theorem comap_id (σ : Type u_1) (R : Type u_4) [comm_semiring R] : comap (alg_hom.id R (mv_polynomial σ R)) = id :=
  funext fun (x : σ → R) => comap_id_apply x

theorem comap_comp_apply {σ : Type u_1} {τ : Type u_2} {υ : Type u_3} {R : Type u_4} [comm_semiring R] (f : alg_hom R (mv_polynomial σ R) (mv_polynomial τ R)) (g : alg_hom R (mv_polynomial τ R) (mv_polynomial υ R)) (x : υ → R) : comap (alg_hom.comp g f) x = comap f (comap g x) := sorry

theorem comap_comp {σ : Type u_1} {τ : Type u_2} {υ : Type u_3} {R : Type u_4} [comm_semiring R] (f : alg_hom R (mv_polynomial σ R) (mv_polynomial τ R)) (g : alg_hom R (mv_polynomial τ R) (mv_polynomial υ R)) : comap (alg_hom.comp g f) = comap f ∘ comap g :=
  funext fun (x : υ → R) => comap_comp_apply f g x

theorem comap_eq_id_of_eq_id {σ : Type u_1} {R : Type u_4} [comm_semiring R] (f : alg_hom R (mv_polynomial σ R) (mv_polynomial σ R)) (hf : ∀ (φ : mv_polynomial σ R), coe_fn f φ = φ) (x : σ → R) : comap f x = x := sorry

theorem comap_rename {σ : Type u_1} {τ : Type u_2} {R : Type u_4} [comm_semiring R] (f : σ → τ) (x : τ → R) : comap (rename f) x = x ∘ f := sorry

/--
If two polynomial types over the same coefficient ring `R` are equivalent,
there is a bijection between the types of functions from their variable types to `R`.
-/
def comap_equiv {σ : Type u_1} {τ : Type u_2} {R : Type u_4} [comm_semiring R] (f : alg_equiv R (mv_polynomial σ R) (mv_polynomial τ R)) : (τ → R) ≃ (σ → R) :=
  equiv.mk (comap ↑f) (comap ↑(alg_equiv.symm f)) sorry sorry

@[simp] theorem comap_equiv_coe {σ : Type u_1} {τ : Type u_2} {R : Type u_4} [comm_semiring R] (f : alg_equiv R (mv_polynomial σ R) (mv_polynomial τ R)) : ⇑(comap_equiv f) = comap ↑f :=
  rfl

@[simp] theorem comap_equiv_symm_coe {σ : Type u_1} {τ : Type u_2} {R : Type u_4} [comm_semiring R] (f : alg_equiv R (mv_polynomial σ R) (mv_polynomial τ R)) : ⇑(equiv.symm (comap_equiv f)) = comap ↑(alg_equiv.symm f) :=
  rfl