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

! This file was ported from Lean 3 source module category_theory.preadditive.Mat
! leanprover-community/mathlib commit 829895f162a1f29d0133f4b3538f4cd1fb5bffd3
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.BigOperators.Basic
import Mathbin.Algebra.BigOperators.Pi
import Mathbin.CategoryTheory.Limits.Shapes.Biproducts
import Mathbin.CategoryTheory.Preadditive.Basic
import Mathbin.CategoryTheory.Preadditive.AdditiveFunctor
import Mathbin.Data.Matrix.Dmatrix
import Mathbin.Data.Matrix.Basic
import Mathbin.CategoryTheory.Fintype
import Mathbin.CategoryTheory.Preadditive.SingleObj
import Mathbin.Algebra.Opposites

/-!
# Matrices over a category.

When `C` is a preadditive category, `Mat_ C` is the preadditive category
whose objects are finite tuples of objects in `C`, and
whose morphisms are matrices of morphisms from `C`.

There is a functor `Mat_.embedding : C ⥤ Mat_ C` sending morphisms to one-by-one matrices.

`Mat_ C` has finite biproducts.

## The additive envelope

We show that this construction is the "additive envelope" of `C`,
in the sense that any additive functor `F : C ⥤ D` to a category `D` with biproducts
lifts to a functor `Mat_.lift F : Mat_ C ⥤ D`,
Moreover, this functor is unique (up to natural isomorphisms) amongst functors `L : Mat_ C ⥤ D`
such that `embedding C ⋙ L ≅ F`.
(As we don't have 2-category theory, we can't explicitly state that `Mat_ C` is
the initial object in the 2-category of categories under `C` which have biproducts.)

As a consequence, when `C` already has finite biproducts we have `Mat_ C ≌ C`.

## Future work

We should provide a more convenient `Mat R`, when `R` is a ring,
as a category with objects `n : FinType`,
and whose morphisms are matrices with components in `R`.

Ideally this would conveniently interact with both `Mat_` and `matrix`.

-/

open CategoryTheory CategoryTheory.Preadditive

open BigOperators Classical

noncomputable section

namespace CategoryTheory

universe w v₁ v₂ u₁ u₂

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

/-- An object in `Mat_ C` is a finite tuple of objects in `C`.
-/
structure Mat_ where
  ι : Type
  [f : Fintype ι]
  pt : ι → C
#align category_theory.Mat_ CategoryTheory.Mat_

attribute [instance] Mat_.F

namespace Mat_

variable {C}

/-- A morphism in `Mat_ C` is a dependently typed matrix of morphisms. -/
@[nolint has_nonempty_instance]
def Hom (M N : Mat_ C) : Type v₁ :=
  DMatrix M.ι N.ι fun i j => M.pt i ⟶ N.pt j
#align category_theory.Mat_.hom CategoryTheory.Mat_.Hom

namespace Hom

/-- The identity matrix consists of identity morphisms on the diagonal, and zeros elsewhere. -/
def id (M : Mat_ C) : Hom M M := fun i j => if h : i = j then eqToHom (congr_arg M.pt h) else 0
#align category_theory.Mat_.hom.id CategoryTheory.Mat_.Hom.id

/-- Composition of matrices using matrix multiplication. -/
def comp {M N K : Mat_ C} (f : Hom M N) (g : Hom N K) : Hom M K := fun i k =>
  ∑ j : N.ι, f i j ≫ g j k
#align category_theory.Mat_.hom.comp CategoryTheory.Mat_.Hom.comp

end Hom

section

attribute [local simp] hom.id hom.comp

instance : Category.{v₁} (Mat_ C) where
  hom := Hom
  id := Hom.id
  comp M N K f g := f.comp g
  id_comp' M N f := by simp [dite_comp]
  comp_id' M N f := by simp [comp_dite]
  assoc' M N K L f g h := by
    ext (i k)
    simp_rw [hom.comp, sum_comp, comp_sum, category.assoc]
    rw [Finset.sum_comm]

theorem id_def (M : Mat_ C) :
    (𝟙 M : Hom M M) = fun i j => if h : i = j then eqToHom (congr_arg M.pt h) else 0 :=
  rfl
#align category_theory.Mat_.id_def CategoryTheory.Mat_.id_def

theorem id_apply (M : Mat_ C) (i j : M.ι) :
    (𝟙 M : Hom M M) i j = if h : i = j then eqToHom (congr_arg M.pt h) else 0 :=
  rfl
#align category_theory.Mat_.id_apply CategoryTheory.Mat_.id_apply

@[simp]
theorem id_apply_self (M : Mat_ C) (i : M.ι) : (𝟙 M : Hom M M) i i = 𝟙 _ := by simp [id_apply]
#align category_theory.Mat_.id_apply_self CategoryTheory.Mat_.id_apply_self

@[simp]
theorem id_apply_of_ne (M : Mat_ C) (i j : M.ι) (h : i ≠ j) : (𝟙 M : Hom M M) i j = 0 := by
  simp [id_apply, h]
#align category_theory.Mat_.id_apply_of_ne CategoryTheory.Mat_.id_apply_of_ne

theorem comp_def {M N K : Mat_ C} (f : M ⟶ N) (g : N ⟶ K) :
    f ≫ g = fun i k => ∑ j : N.ι, f i j ≫ g j k :=
  rfl
#align category_theory.Mat_.comp_def CategoryTheory.Mat_.comp_def

@[simp]
theorem comp_apply {M N K : Mat_ C} (f : M ⟶ N) (g : N ⟶ K) (i k) :
    (f ≫ g) i k = ∑ j : N.ι, f i j ≫ g j k :=
  rfl
#align category_theory.Mat_.comp_apply CategoryTheory.Mat_.comp_apply

instance (M N : Mat_ C) : Inhabited (M ⟶ N) :=
  ⟨fun i j => (0 : M.pt i ⟶ N.pt j)⟩

end

instance : Preadditive (Mat_ C)
    where
  homGroup M N := by
    change AddCommGroup (DMatrix M.ι N.ι _)
    infer_instance
  add_comp M N K f f' g := by
    ext
    simp [Finset.sum_add_distrib]
  comp_add M N K f g g' := by
    ext
    simp [Finset.sum_add_distrib]

@[simp]
theorem add_apply {M N : Mat_ C} (f g : M ⟶ N) (i j) : (f + g) i j = f i j + g i j :=
  rfl
#align category_theory.Mat_.add_apply CategoryTheory.Mat_.add_apply

open CategoryTheory.Limits

/-- We now prove that `Mat_ C` has finite biproducts.

Be warned, however, that `Mat_ C` is not necessarily Krull-Schmidt,
and so the internal indexing of a biproduct may have nothing to do with the external indexing,
even though the construction we give uses a sigma type.
See however `iso_biproduct_embedding`.
-/
instance hasFiniteBiproducts : HasFiniteBiproducts (Mat_ C)
    where out n :=
    {
      HasBiproduct := fun f =>
        hasBiproduct_of_total
          { pt := ⟨Σj, (f j).ι, fun p => (f p.1).pt p.2⟩
            π := fun j x y => by
              dsimp at x⊢
              refine' if h : x.1 = j then _ else 0
              refine' if h' : @Eq.ndrec (Fin n) x.1 (fun j => (f j).ι) x.2 _ h = y then _ else 0
              apply eq_to_hom
              substs h h'
            -- Notice we were careful not to use `subst` until we had a goal in `Prop`.
            ι := fun j x y => by
              dsimp at y⊢
              refine' if h : y.1 = j then _ else 0
              refine' if h' : @Eq.ndrec _ y.1 (fun j => (f j).ι) y.2 _ h = x then _ else 0
              apply eq_to_hom
              substs h h'
            ι_π := fun j j' => by
              ext (x y)
              dsimp
              simp_rw [dite_comp, comp_dite]
              simp only [if_t_t, dite_eq_ite, dif_ctx_congr, limits.comp_zero, limits.zero_comp,
                eq_to_hom_trans, Finset.sum_congr]
              erw [Finset.sum_sigma]
              dsimp
              simp only [if_congr, if_true, dif_ctx_congr, Finset.sum_dite_irrel, Finset.mem_univ,
                Finset.sum_const_zero, Finset.sum_congr, Finset.sum_dite_eq']
              split_ifs with h h'
              · substs h h'
                simp only [CategoryTheory.eqToHom_refl, CategoryTheory.Mat_.id_apply_self]
              · subst h
                simp only [id_apply_of_ne _ _ _ h', CategoryTheory.eqToHom_refl]
              · rfl }
          (by
            dsimp
            funext i₁
            dsimp at i₁⊢
            rcases i₁ with ⟨j₁, i₁⟩
            -- I'm not sure why we can't just `simp` by `finset.sum_apply`: something doesn't quite match
            convert Finset.sum_apply _ _ _ using 1
            · rfl
            · apply hEq_of_eq
              symm
              funext i₂
              rcases i₂ with ⟨j₂, i₂⟩
              simp only [comp_apply, dite_comp, comp_dite, if_t_t, dite_eq_ite, if_congr, if_true,
                dif_ctx_congr, Finset.sum_dite_irrel, Finset.sum_dite_eq, Finset.mem_univ,
                Finset.sum_const_zero, Finset.sum_congr, Finset.sum_dite_eq, Finset.sum_apply,
                limits.comp_zero, limits.zero_comp, eq_to_hom_trans, Mat_.id_apply]
              by_cases h : j₁ = j₂
              · subst h
                simp
              · simp [h]) }
#align category_theory.Mat_.has_finite_biproducts CategoryTheory.Mat_.hasFiniteBiproducts

end Mat_

namespace Functor

variable {C} {D : Type _} [Category.{v₁} D] [Preadditive D]

attribute [local simp] Mat_.id_apply eq_to_hom_map

/-- A functor induces a functor of matrix categories.
-/
@[simps]
def mapMat_ (F : C ⥤ D) [Functor.Additive F] : Mat_ C ⥤ Mat_ D
    where
  obj M := ⟨M.ι, fun i => F.obj (M.pt i)⟩
  map M N f i j := F.map (f i j)
  map_comp' M N K f g := by
    ext (i k)
    simp
#align category_theory.functor.map_Mat_ CategoryTheory.Functor.mapMat_

/-- The identity functor induces the identity functor on matrix categories.
-/
@[simps]
def mapMatId : (𝟭 C).mapMat_ ≅ 𝟭 (Mat_ C) :=
  NatIso.ofComponents
    (fun M =>
      eqToIso
        (by
          cases M
          rfl))
    fun M N f => by
    ext (i j)
    cases M; cases N
    simp [comp_dite, dite_comp]
#align category_theory.functor.map_Mat_id CategoryTheory.Functor.mapMatId

/-- Composite functors induce composite functors on matrix categories.
-/
@[simps]
def mapMatComp {E : Type _} [Category.{v₁} E] [Preadditive E] (F : C ⥤ D) [Functor.Additive F]
    (G : D ⥤ E) [Functor.Additive G] : (F ⋙ G).mapMat_ ≅ F.mapMat_ ⋙ G.mapMat_ :=
  NatIso.ofComponents
    (fun M =>
      eqToIso
        (by
          cases M
          rfl))
    fun M N f => by
    ext (i j)
    cases M; cases N
    simp [comp_dite, dite_comp]
#align category_theory.functor.map_Mat_comp CategoryTheory.Functor.mapMatComp

end Functor

namespace Mat_

variable (C)

/-- The embedding of `C` into `Mat_ C` as one-by-one matrices.
(We index the summands by `punit`.) -/
@[simps]
def embedding : C ⥤ Mat_ C where
  obj X := ⟨PUnit, fun _ => X⟩
  map X Y f _ _ := f
  map_id' X := by
    ext (⟨⟩⟨⟩)
    simp
  map_comp' X Y Z f g := by
    ext (⟨⟩⟨⟩)
    simp
#align category_theory.Mat_.embedding CategoryTheory.Mat_.embedding

namespace Embedding

instance : Faithful (embedding C)
    where map_injective' X Y f g h := congr_fun (congr_fun h PUnit.unit) PUnit.unit

instance : Full (embedding C) where Preimage X Y f := f PUnit.unit PUnit.unit

instance : Functor.Additive (embedding C) where

end Embedding

instance [Inhabited C] : Inhabited (Mat_ C) :=
  ⟨(embedding C).obj default⟩

open CategoryTheory.Limits

variable {C}

/-- Every object in `Mat_ C` is isomorphic to the biproduct of its summands.
-/
@[simps]
def isoBiproductEmbedding (M : Mat_ C) : M ≅ ⨁ fun i => (embedding C).obj (M.pt i)
    where
  hom := biproduct.lift fun i j k => if h : j = i then eqToHom (congr_arg M.pt h) else 0
  inv := biproduct.desc fun i j k => if h : i = k then eqToHom (congr_arg M.pt h) else 0
  hom_inv_id' := by
    simp only [biproduct.lift_desc]
    funext i
    dsimp
    convert Finset.sum_apply _ _ _
    · dsimp
      rfl
    · apply hEq_of_eq
      symm
      funext j
      simp only [Finset.sum_apply]
      dsimp
      simp [dite_comp, comp_dite, Mat_.id_apply]
  inv_hom_id' := by
    apply biproduct.hom_ext
    intro i
    apply biproduct.hom_ext'
    intro j
    simp only [category.id_comp, category.assoc, biproduct.lift_π, biproduct.ι_desc_assoc,
      biproduct.ι_π]
    ext (⟨⟩⟨⟩)
    simp [dite_comp, comp_dite]
    split_ifs
    · subst h
      simp
    · simp [h]
#align category_theory.Mat_.iso_biproduct_embedding CategoryTheory.Mat_.isoBiproductEmbedding

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

/-- Every `M` is a direct sum of objects from `C`, and `F` preserves biproducts. -/
@[simps]
def additiveObjIsoBiproduct (F : Mat_ C ⥤ D) [Functor.Additive F] (M : Mat_ C) :
    F.obj M ≅ ⨁ fun i => F.obj ((embedding C).obj (M.pt i)) :=
  F.mapIso (isoBiproductEmbedding M) ≪≫ F.mapBiproduct _
#align category_theory.Mat_.additive_obj_iso_biproduct CategoryTheory.Mat_.additiveObjIsoBiproduct

variable [HasFiniteBiproducts D]

@[reassoc.1]
theorem additiveObjIsoBiproduct_naturality (F : Mat_ C ⥤ D) [Functor.Additive F] {M N : Mat_ C}
    (f : M ⟶ N) :
    F.map f ≫ (additiveObjIsoBiproduct F N).hom =
      (additiveObjIsoBiproduct F M).hom ≫
        biproduct.matrix fun i j => F.map ((embedding C).map (f i j)) :=
  by
  -- This is disappointingly tedious.
  ext
  simp only [additive_obj_iso_biproduct_hom, category.assoc, biproduct.lift_π, functor.map_bicone_π,
    biproduct.bicone_π, biproduct.lift_matrix]
  dsimp [Embedding]
  simp only [← F.map_comp, biproduct.lift_π, biproduct.matrix_π, category.assoc]
  simp only [← F.map_comp, ← F.map_sum, biproduct.lift_desc, biproduct.lift_π_assoc, comp_sum]
  simp only [comp_def, comp_dite, comp_zero, Finset.sum_dite_eq', Finset.mem_univ, if_true]
  dsimp
  simp only [Finset.sum_singleton, dite_comp, zero_comp]
  congr
  symm
  convert Finset.sum_fn _ _
  -- It's hard to use this as a simp lemma!
  simp only [Finset.sum_fn, Finset.sum_dite_eq]
  ext
  simp
#align category_theory.Mat_.additive_obj_iso_biproduct_naturality CategoryTheory.Mat_.additiveObjIsoBiproduct_naturality

@[reassoc.1]
theorem additiveObjIsoBiproduct_naturality' (F : Mat_ C ⥤ D) [Functor.Additive F] {M N : Mat_ C}
    (f : M ⟶ N) :
    (additiveObjIsoBiproduct F M).inv ≫ F.map f =
      biproduct.matrix (fun i j => F.map ((embedding C).map (f i j)) : _) ≫
        (additiveObjIsoBiproduct F N).inv :=
  by rw [iso.inv_comp_eq, ← category.assoc, iso.eq_comp_inv, additive_obj_iso_biproduct_naturality]
#align category_theory.Mat_.additive_obj_iso_biproduct_naturality' CategoryTheory.Mat_.additiveObjIsoBiproduct_naturality'

/-- Any additive functor `C ⥤ D` to a category `D` with finite biproducts extends to
a functor `Mat_ C ⥤ D`. -/
@[simps]
def lift (F : C ⥤ D) [Functor.Additive F] : Mat_ C ⥤ D
    where
  obj X := ⨁ fun i => F.obj (X.pt i)
  map X Y f := biproduct.matrix fun i j => F.map (f i j)
  map_id' X := by
    ext (i j)
    by_cases h : i = j
    · subst h
      simp
    · simp [h, Mat_.id_apply]
  map_comp' X Y Z f g := by
    ext (i j)
    simp
#align category_theory.Mat_.lift CategoryTheory.Mat_.lift

instance lift_additive (F : C ⥤ D) [Functor.Additive F] : Functor.Additive (lift F) where
#align category_theory.Mat_.lift_additive CategoryTheory.Mat_.lift_additive

/-- An additive functor `C ⥤ D` factors through its lift to `Mat_ C ⥤ D`. -/
@[simps]
def embeddingLiftIso (F : C ⥤ D) [Functor.Additive F] : embedding C ⋙ lift F ≅ F :=
  NatIso.ofComponents
    (fun X =>
      { hom := biproduct.desc fun P => 𝟙 (F.obj X)
        inv := biproduct.lift fun P => 𝟙 (F.obj X) })
    fun X Y f => by
    dsimp
    ext
    simp only [category.id_comp, biproduct.ι_desc_assoc]
    erw [biproduct.ι_matrix_assoc]
    -- Not sure why this doesn't fire via `simp`.
    simp
#align category_theory.Mat_.embedding_lift_iso CategoryTheory.Mat_.embeddingLiftIso

/-- `Mat_.lift F` is the unique additive functor `L : Mat_ C ⥤ D` such that `F ≅ embedding C ⋙ L`.
-/
def liftUnique (F : C ⥤ D) [Functor.Additive F] (L : Mat_ C ⥤ D) [Functor.Additive L]
    (α : embedding C ⋙ L ≅ F) : L ≅ lift F :=
  NatIso.ofComponents
    (fun M =>
      additiveObjIsoBiproduct L M ≪≫
        (biproduct.mapIso fun i => α.app (M.pt i)) ≪≫
          (biproduct.mapIso fun i => (embeddingLiftIso F).symm.app (M.pt i)) ≪≫
            (additiveObjIsoBiproduct (lift F) M).symm)
    fun M N f => by
    dsimp only [iso.trans_hom, iso.symm_hom, biproduct.map_iso_hom]
    simp only [additive_obj_iso_biproduct_naturality_assoc]
    simp only [biproduct.matrix_map_assoc, category.assoc]
    simp only [additive_obj_iso_biproduct_naturality']
    simp only [biproduct.map_matrix_assoc, category.assoc]
    congr
    ext (j k⟨⟩)
    dsimp; simp
    exact α.hom.naturality (f j k)
#align category_theory.Mat_.lift_unique CategoryTheory.Mat_.liftUnique

-- TODO is there some uniqueness statement for the natural isomorphism in `lift_unique`?
/-- Two additive functors `Mat_ C ⥤ D` are naturally isomorphic if
their precompositions with `embedding C` are naturally isomorphic as functors `C ⥤ D`. -/
@[ext]
def ext {F G : Mat_ C ⥤ D} [Functor.Additive F] [Functor.Additive G]
    (α : embedding C ⋙ F ≅ embedding C ⋙ G) : F ≅ G :=
  liftUnique (embedding C ⋙ G) _ α ≪≫ (liftUnique _ _ (Iso.refl _)).symm
#align category_theory.Mat_.ext CategoryTheory.Mat_.ext

/-- Natural isomorphism needed in the construction of `equivalence_self_of_has_finite_biproducts`.
-/
def equivalenceSelfOfHasFiniteBiproductsAux [HasFiniteBiproducts C] :
    embedding C ⋙ 𝟭 (Mat_ C) ≅ embedding C ⋙ lift (𝟭 C) ⋙ embedding C :=
  Functor.rightUnitor _ ≪≫
    (Functor.leftUnitor _).symm ≪≫
      isoWhiskerRight (embeddingLiftIso _).symm _ ≪≫ Functor.associator _ _ _
#align category_theory.Mat_.equivalence_self_of_has_finite_biproducts_aux CategoryTheory.Mat_.equivalenceSelfOfHasFiniteBiproductsAux

/--
A preadditive category that already has finite biproducts is equivalent to its additive envelope.

Note that we only prove this for a large category;
otherwise there are universe issues that I haven't attempted to sort out.
-/
def equivalenceSelfOfHasFiniteBiproducts (C : Type (u₁ + 1)) [LargeCategory C] [Preadditive C]
    [HasFiniteBiproducts C] : Mat_ C ≌ C :=
  Equivalence.mk
    (-- I suspect this is already an adjoint equivalence, but it seems painful to verify.
      lift
      (𝟭 C))
    (embedding C) (ext equivalenceSelfOfHasFiniteBiproductsAux) (embeddingLiftIso (𝟭 C))
#align category_theory.Mat_.equivalence_self_of_has_finite_biproducts CategoryTheory.Mat_.equivalenceSelfOfHasFiniteBiproducts

@[simp]
theorem equivalenceSelfOfHasFiniteBiproducts_functor {C : Type (u₁ + 1)} [LargeCategory C]
    [Preadditive C] [HasFiniteBiproducts C] :
    (equivalenceSelfOfHasFiniteBiproducts C).Functor = lift (𝟭 C) :=
  rfl
#align category_theory.Mat_.equivalence_self_of_has_finite_biproducts_functor CategoryTheory.Mat_.equivalenceSelfOfHasFiniteBiproducts_functor

@[simp]
theorem equivalenceSelfOfHasFiniteBiproducts_inverse {C : Type (u₁ + 1)} [LargeCategory C]
    [Preadditive C] [HasFiniteBiproducts C] :
    (equivalenceSelfOfHasFiniteBiproducts C).inverse = embedding C :=
  rfl
#align category_theory.Mat_.equivalence_self_of_has_finite_biproducts_inverse CategoryTheory.Mat_.equivalenceSelfOfHasFiniteBiproducts_inverse

end Mat_

universe u

/-- A type synonym for `Fintype`, which we will equip with a category structure
where the morphisms are matrices with components in `R`. -/
@[nolint unused_arguments]
def Mat (R : Type u) :=
  FintypeCat.{u}deriving Inhabited
#align category_theory.Mat CategoryTheory.Mat

instance (R : Type u) : CoeSort (Mat R) (Type u) :=
  Bundled.hasCoeToSort

open Classical Matrix

instance (R : Type u) [Semiring R] : Category (Mat R)
    where
  hom X Y := Matrix X Y R
  id X := 1
  comp X Y Z f g := f ⬝ g
  assoc' := by
    intros
    simp [Matrix.mul_assoc]

namespace Mat

section

variable (R : Type u) [Semiring R]

theorem id_def (M : Mat R) : 𝟙 M = fun i j => if h : i = j then 1 else 0 :=
  rfl
#align category_theory.Mat.id_def CategoryTheory.Mat.id_def

theorem id_apply (M : Mat R) (i j : M) : (𝟙 M : Matrix M M R) i j = if h : i = j then 1 else 0 :=
  rfl
#align category_theory.Mat.id_apply CategoryTheory.Mat.id_apply

@[simp]
theorem id_apply_self (M : Mat R) (i : M) : (𝟙 M : Matrix M M R) i i = 1 := by simp [id_apply]
#align category_theory.Mat.id_apply_self CategoryTheory.Mat.id_apply_self

@[simp]
theorem id_apply_of_ne (M : Mat R) (i j : M) (h : i ≠ j) : (𝟙 M : Matrix M M R) i j = 0 := by
  simp [id_apply, h]
#align category_theory.Mat.id_apply_of_ne CategoryTheory.Mat.id_apply_of_ne

theorem comp_def {M N K : Mat R} (f : M ⟶ N) (g : N ⟶ K) :
    f ≫ g = fun i k => ∑ j : N, f i j * g j k :=
  rfl
#align category_theory.Mat.comp_def CategoryTheory.Mat.comp_def

@[simp]
theorem comp_apply {M N K : Mat R} (f : M ⟶ N) (g : N ⟶ K) (i k) :
    (f ≫ g) i k = ∑ j : N, f i j * g j k :=
  rfl
#align category_theory.Mat.comp_apply CategoryTheory.Mat.comp_apply

instance (M N : Mat R) : Inhabited (M ⟶ N) :=
  ⟨fun (i : M) (j : N) => (0 : R)⟩

end

variable (R : Type) [Ring R]

open Opposite

/-- Auxiliary definition for `category_theory.Mat.equivalence_single_obj`. -/
@[simps]
def equivalenceSingleObjInverse : Mat_ (SingleObj Rᵐᵒᵖ) ⥤ Mat R
    where
  obj X := FintypeCat.of X.ι
  map X Y f i j := MulOpposite.unop (f i j)
  map_id' X := by
    ext (i j)
    simp [id_def, Mat_.id_def]
    split_ifs <;> rfl
#align category_theory.Mat.equivalence_single_obj_inverse CategoryTheory.Mat.equivalenceSingleObjInverse

instance : Faithful (equivalenceSingleObjInverse R)
    where map_injective' X Y f g w := by
    ext (i j)
    apply_fun MulOpposite.unop using MulOpposite.unop_injective
    exact congr_fun (congr_fun w i) j

instance : Full (equivalenceSingleObjInverse R) where Preimage X Y f i j := MulOpposite.op (f i j)

instance : EssSurj (equivalenceSingleObjInverse R)
    where mem_essImage X :=
    ⟨{  ι := X
        pt := fun _ => PUnit.unit },
      ⟨eqToIso
          (by
            dsimp
            cases X
            congr )⟩⟩

/-- The categorical equivalence between the category of matrices over a ring,
and the category of matrices over that ring considered as a single-object category. -/
def equivalenceSingleObj : Mat R ≌ Mat_ (SingleObj Rᵐᵒᵖ) :=
  haveI := equivalence.of_fully_faithfully_ess_surj (equivalence_single_obj_inverse R)
  (equivalence_single_obj_inverse R).asEquivalence.symm
#align category_theory.Mat.equivalence_single_obj CategoryTheory.Mat.equivalenceSingleObj

instance : Preadditive (Mat R)
    where
  add_comp := by
    intros
    ext
    simp [add_mul, Finset.sum_add_distrib]
  comp_add := by
    intros
    ext
    simp [mul_add, Finset.sum_add_distrib]

-- TODO show `Mat R` has biproducts, and that `biprod.map` "is" forming a block diagonal matrix.
end Mat

end CategoryTheory


import analysis.complex
import algebra.pi_instances
import ring_theory.subring
import analysis.normed_space

local attribute [instance] classical.prop_decidable
noncomputable theory

universes u v

open filter complex

def has_complex_derivative_at
(f : ℂ → ℂ)
(f'z : ℂ)
(z : ℂ) : Prop :=
let error_term (h : ℂ) : ℝ :=
    abs((f (z + h) - (f z + f'z * h))/h) in
(tendsto error_term (nhds 0) (nhds 0))

lemma has_complex_derivative_at_iff (f f'z z) :
  has_complex_derivative_at f f'z z
  ↔ (∀ ε > 0, ∃ δ > 0, ∀ h:ℂ, h ≠ 0 → abs h < δ → abs ((f(z+h)-f(z)-f'z*h)/h) < ε) :=
⟨λ H ε hε, let ⟨δ, hδ1, hδ2⟩ := tendsto_nhds_of_metric.1 H ε hε in
  ⟨δ, hδ1, λ h h1 h2, by simp only [dist, sub_zero, complex.abs_abs, sub_add_eq_sub_sub] at hδ2;
    from hδ2 h2⟩,
λ H, tendsto_nhds_of_metric.2 $ λ ε hε, let ⟨δ, hδ1, hδ2⟩ := H ε hε in
  ⟨δ, hδ1, λ h h1, if H : h = 0 then by unfold dist;
    rwa [H, add_zero, mul_zero, add_zero, sub_self, zero_div, sub_zero, complex.abs_zero, _root_.abs_zero]
  else by unfold dist at h1 ⊢; rw [sub_zero] at h1;
    rw [sub_zero, complex.abs_abs, sub_add_eq_sub_sub]; from hδ2 h H h1⟩⟩

lemma has_complex_derivative_at_iff' (f f'z z) :
  has_complex_derivative_at f f'z z
  ↔ (∀ ε > 0, ∃ δ > 0, ∀ h:ℂ, abs h < δ → abs ((f(z+h)-f(z)-f'z*h)/h) < ε) :=
by simp only [has_complex_derivative_at, tendsto_nhds_of_metric, dist,
sub_zero, complex.abs_abs, sub_add_eq_sub_sub]

lemma has_complex_derivative_at_iff'' (f f'z z) :
  has_complex_derivative_at f f'z z
  ↔ tendsto (λ h, (f(z+h)-f(z)-f'z*h)/h) (nhds 0) (nhds 0) :=
by simp only [has_complex_derivative_at, tendsto_nhds_of_metric, dist,
sub_zero, complex.abs_abs, sub_add_eq_sub_sub]

section
variables {α : Type*} {β : Type*} {s : set α}

-- I would like to remove the following line... but I can't
instance foobar (X : Type u) (R : Type v) [ring R] : module R (X → R) := pi.module

def extend_by_zero [has_zero β] (f : s → β) : α → β :=
λ z, if h : z ∈ s then f ⟨z, h⟩ else 0

lemma extend_by_zero_zero [has_zero β] :
extend_by_zero (λ s, 0 : s → β) = (λ h, 0) :=
by ext z; by_cases h : z ∈ s; simp [extend_by_zero, h]

lemma extend_by_zero_zero' [has_zero β] :
extend_by_zero (0 : s → β) = 0 :=
by ext z; by_cases h : z ∈ s; simp [extend_by_zero, h]

lemma extend_by_zero_add [add_group β] (f g : s → β) :
extend_by_zero (f + g) = extend_by_zero f + extend_by_zero g :=
by ext z; by_cases h : z ∈ s; simp [extend_by_zero, h]

lemma extend_by_zero_mul [semiring β] (f g : s → β) :
extend_by_zero (f * g) = extend_by_zero f * extend_by_zero g :=
by ext z; by_cases h : z ∈ s; simp [extend_by_zero, h]

lemma extend_by_zero_neg [add_group β] (f : s → β) :
extend_by_zero (-f) = -extend_by_zero f :=
by ext z; by_cases h : z ∈ s; simp [extend_by_zero, h]

lemma extend_by_zero_sub [add_group β] (f g : s → β) :
extend_by_zero (f - g) = extend_by_zero f - extend_by_zero g :=
(extend_by_zero_add _ _).trans $ congr_arg _ $ extend_by_zero_neg g

lemma extend_by_zero_smul [ring β] (c : β) (f : s → β) :
extend_by_zero (c • f) = c • extend_by_zero f :=
by ext z; by_cases h : z ∈ s; simp [extend_by_zero, h]

end

/-- holomorphic function from a subset of ℂ. Correct definition if domain is open. -/
def is_holomorphic {domain : set ℂ} (f : domain → ℂ) : Prop :=
∀ z : domain, ∃ f'z, has_complex_derivative_at (extend_by_zero f) f'z z

variable {domain : set ℂ}

lemma const_hol (domain_open : is_open domain) (c : ℂ) : is_holomorphic (λ z : domain, (c : ℂ)) :=
λ z₀, ⟨(0 : ℂ), let ⟨δ, hδ1, hδ2⟩ := is_open_metric.1 domain_open z₀.1 z₀.2 in
tendsto_nhds_of_metric.2 $ λ ε hε, ⟨δ, hδ1, λ z hz,
have H1 : ↑z₀ + z ∈ domain, from show z₀.1 + z ∈ domain,
  from hδ2 $ by simpa [dist] using hz,
have H2 : ↑z₀ ∈ domain, from z₀.2,
by simpa [extend_by_zero, H1, H2, -add_comm] using hε⟩⟩

lemma zero_hol : is_holomorphic (λ z : domain, (0 : ℂ)) :=
λ z₀, ⟨0, show tendsto _ _ _, by simp [extend_by_zero_zero, tendsto_const_nhds]⟩

lemma one_hol (domain_open : is_open domain) : is_holomorphic (λ z : domain, (1 : ℂ)) := const_hol domain_open 1

lemma add_hol (f g : domain → ℂ) (f_hol : is_holomorphic f) (g_hol : is_holomorphic g) : is_holomorphic (f + g) :=
begin
  intro z₀,
  cases f_hol z₀ with f'z₀ Hf,
  cases g_hol z₀ with g'z₀ Hg,
  existsi (f'z₀ + g'z₀),
  rw extend_by_zero_add,
  rw has_complex_derivative_at_iff'' at *,
  convert tendsto_add Hf Hg,
  ext h, rw ← add_div,
  change ((_+_)-(_+_)-_*_)/_=_,
  simp only [add_mul, sub_eq_add_neg, neg_add, add_assoc, add_left_comm]
end

lemma mul_hol (f g : domain → ℂ) (f_hol : is_holomorphic f) (g_hol : is_holomorphic g) : is_holomorphic (f * g) :=
begin
  intro z₀,
  cases f_hol z₀ with f'z₀ Hf,
  cases g_hol z₀ with g'z₀ Hg,
  existsi f'z₀*(extend_by_zero g z₀) + (extend_by_zero f z₀)*g'z₀,
  rw extend_by_zero_mul,
  rw has_complex_derivative_at_iff'' at *,
  generalize_hyp : extend_by_zero f = F at Hf ⊢,
  generalize_hyp : extend_by_zero g = G at Hg ⊢,
  have H1 : (λ h, ((F*G) (↑z₀+h) - (F*G) ↑z₀ - (f'z₀ * G z₀ + F z₀ * g'z₀)*h)/h)
    = (λ h, ((F(↑z₀+h)-F(↑z₀)-f'z₀*h)/h)*((G(↑z₀+h)-G(↑z₀)-g'z₀*h)/h)*h
      + f'z₀*g'z₀*h
      + ((F(↑z₀+h)-F(↑z₀)-f'z₀*h)/h)*(g'z₀*h+G(↑z₀))
      + (F(↑z₀)+f'z₀*h)*((G(↑z₀+h)-G(↑z₀)-g'z₀*h)/h)),
  { funext h, change (_*_-_*_-_)/_=_,
    by_cases h0 : h = 0,
    { subst h0, simp },
    apply (div_eq_iff_mul_eq h0).2,
    simp only [add_div, sub_div, add_mul, sub_mul, mul_add, mul_sub],
    simp only [div_mul_div, div_mul_eq_mul_div, (mul_div_assoc _ _ _).symm],
    simp only [mul_div_cancel _ h0], ring },
  have H2 : (0:ℂ) = 0*0*0+f'z₀*g'z₀*0+0*(g'z₀*0+G(↑z₀))+(F(↑z₀)+f'z₀*0)*0,
  { simp only [zero_mul, mul_zero, zero_add] },
  conv { congr, rw H1, skip, skip, rw H2 },
  exact tendsto_add (tendsto_add (tendsto_add
    (tendsto_mul (tendsto_mul Hf Hg) tendsto_id)
    (tendsto_mul tendsto_const_nhds tendsto_id))
    (tendsto_mul Hf (tendsto_add (tendsto_mul tendsto_const_nhds tendsto_id) tendsto_const_nhds)))
    (tendsto_mul (tendsto_add tendsto_const_nhds (tendsto_mul tendsto_const_nhds tendsto_id)) Hg)
end

lemma neg_hol (f : domain → ℂ) (f_hol : is_holomorphic f) : is_holomorphic (-f) :=
begin
  intro z₀,
  cases f_hol z₀ with f'z₀ H,
  existsi -f'z₀,
  rw extend_by_zero_neg,
  suffices : tendsto (λ (h : ℂ), abs ((-(extend_by_zero f (↑z₀ + h) + -(extend_by_zero f ↑z₀ + f'z₀ * h))) / h))
    (nhds 0) (nhds 0),
  { simpa [has_complex_derivative_at] },
  conv { congr, funext, rw [neg_div, complex.abs_neg], },
  exact H
end

instance (domain_open : is_open domain) : is_subring {f : domain → ℂ | is_holomorphic f} :=
{ zero_mem := zero_hol,
  add_mem  := add_hol,
  neg_mem  := neg_hol,
  mul_mem  := mul_hol,
  one_mem  := one_hol domain_open,
}

instance xyzzy {F : Type u} [normed_field F] : normed_space F F :=
{ dist_eq := normed_field.dist_eq,
  norm_smul := normed_field.norm_mul }

lemma smul_hol (c : ℂ) (f : domain → ℂ) (f_hol : is_holomorphic f) : is_holomorphic (c • f) :=
begin
  intro z₀,
  cases f_hol z₀ with f'z₀ Hf,
  existsi c * f'z₀,
  rw extend_by_zero_smul,
  rw has_complex_derivative_at_iff'' at *,
  conv { congr,
    { funext, simp only [pi.smul_apply, smul_eq_mul],
      rw [← mul_sub, mul_assoc, ← mul_sub, mul_div_assoc] },
    skip, rw ← mul_zero c },
  exact tendsto_mul tendsto_const_nhds Hf
end

instance hol_submodule : is_submodule {f : domain → ℂ | is_holomorphic f} :=
{ zero_ := zero_hol,
  add_  := add_hol,
  smul  := smul_hol }


/-
Copyright (c) 2021 Yaël Dillies, Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Bhavik Mehta

! This file was ported from Lean 3 source module analysis.convex.simplicial_complex.basic
! leanprover-community/mathlib commit 70fd9563a21e7b963887c9360bd29b2393e6225a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Convex.Hull
import Mathbin.LinearAlgebra.AffineSpace.Independent

/-!
# Simplicial complexes

In this file, we define simplicial complexes in `𝕜`-modules. A simplicial complex is a collection
of simplices closed by inclusion (of vertices) and intersection (of underlying sets).

We model them by a downward-closed set of affine independent finite sets whose convex hulls "glue
nicely", each finite set and its convex hull corresponding respectively to the vertices and the
underlying set of a simplex.

## Main declarations

* `simplicial_complex 𝕜 E`: A simplicial complex in the `𝕜`-module `E`.
* `simplicial_complex.vertices`: The zero dimensional faces of a simplicial complex.
* `simplicial_complex.facets`: The maximal faces of a simplicial complex.

## Notation

`s ∈ K` means that `s` is a face of `K`.

`K ≤ L` means that the faces of `K` are faces of `L`.

## Implementation notes

"glue nicely" usually means that the intersection of two faces (as sets in the ambient space) is a
face. Given that we store the vertices, not the faces, this would be a bit awkward to spell.
Instead, `simplicial_complex.inter_subset_convex_hull` is an equivalent condition which works on the
vertices.

## TODO

Simplicial complexes can be generalized to affine spaces once `convex_hull` has been ported.
-/

open Finset Set

variable (𝕜 E : Type _) {ι : Type _} [OrderedRing 𝕜] [AddCommGroup E] [Module 𝕜 E]

namespace Geometry

-- TODO: update to new binder order? not sure what binder order is correct for `down_closed`.
/-- A simplicial complex in a `𝕜`-module is a collection of simplices which glue nicely together.
Note that the textbook meaning of "glue nicely" is given in
`geometry.simplicial_complex.disjoint_or_exists_inter_eq_convex_hull`. It is mostly useless, as
`geometry.simplicial_complex.convex_hull_inter_convex_hull` is enough for all purposes. -/
@[ext]
structure SimplicialComplex where
  faces : Set (Finset E)
  not_empty_mem : ∅ ∉ faces
  indep : ∀ {s}, s ∈ faces → AffineIndependent 𝕜 (coe : (s : Set E) → E)
  down_closed : ∀ {s t}, s ∈ faces → t ⊆ s → t ≠ ∅ → t ∈ faces
  inter_subset_convexHull :
    ∀ {s t},
      s ∈ faces → t ∈ faces → convexHull 𝕜 ↑s ∩ convexHull 𝕜 ↑t ⊆ convexHull 𝕜 (s ∩ t : Set E)
#align geometry.simplicial_complex Geometry.SimplicialComplex

namespace SimplicialComplex

variable {𝕜 E} {K : SimplicialComplex 𝕜 E} {s t : Finset E} {x : E}

/-- A `finset` belongs to a `simplicial_complex` if it's a face of it. -/
instance : Membership (Finset E) (SimplicialComplex 𝕜 E) :=
  ⟨fun s K => s ∈ K.faces⟩

/-- The underlying space of a simplicial complex is the union of its faces. -/
def space (K : SimplicialComplex 𝕜 E) : Set E :=
  ⋃ s ∈ K.faces, convexHull 𝕜 (s : Set E)
#align geometry.simplicial_complex.space Geometry.SimplicialComplex.space

theorem mem_space_iff : x ∈ K.space ↔ ∃ s ∈ K.faces, x ∈ convexHull 𝕜 (s : Set E) :=
  mem_unionᵢ₂
#align geometry.simplicial_complex.mem_space_iff Geometry.SimplicialComplex.mem_space_iff

theorem convexHull_subset_space (hs : s ∈ K.faces) : convexHull 𝕜 ↑s ⊆ K.space :=
  subset_bunionᵢ_of_mem hs
#align geometry.simplicial_complex.convex_hull_subset_space Geometry.SimplicialComplex.convexHull_subset_space

protected theorem subset_space (hs : s ∈ K.faces) : (s : Set E) ⊆ K.space :=
  (subset_convexHull 𝕜 _).trans <| convexHull_subset_space hs
#align geometry.simplicial_complex.subset_space Geometry.SimplicialComplex.subset_space

theorem convexHull_inter_convexHull (hs : s ∈ K.faces) (ht : t ∈ K.faces) :
    convexHull 𝕜 ↑s ∩ convexHull 𝕜 ↑t = convexHull 𝕜 (s ∩ t : Set E) :=
  (K.inter_subset_convexHull hs ht).antisymm <|
    subset_inter (convexHull_mono <| Set.inter_subset_left _ _) <|
      convexHull_mono <| Set.inter_subset_right _ _
#align geometry.simplicial_complex.convex_hull_inter_convex_hull Geometry.SimplicialComplex.convexHull_inter_convexHull

/-- The conclusion is the usual meaning of "glue nicely" in textbooks. It turns out to be quite
unusable, as it's about faces as sets in space rather than simplices. Further,  additional structure
on `𝕜` means the only choice of `u` is `s ∩ t` (but it's hard to prove). -/
theorem disjoint_or_exists_inter_eq_convexHull (hs : s ∈ K.faces) (ht : t ∈ K.faces) :
    Disjoint (convexHull 𝕜 (s : Set E)) (convexHull 𝕜 ↑t) ∨
      ∃ u ∈ K.faces, convexHull 𝕜 (s : Set E) ∩ convexHull 𝕜 ↑t = convexHull 𝕜 ↑u :=
  by
  classical
    by_contra' h
    refine'
      h.2 (s ∩ t)
        (K.down_closed hs (inter_subset_left _ _) fun hst =>
          h.1 <| disjoint_iff_inf_le.mpr <| (K.inter_subset_convex_hull hs ht).trans _)
        _
    · rw [← coe_inter, hst, coe_empty, convexHull_empty]
      rfl
    · rw [coe_inter, convex_hull_inter_convex_hull hs ht]
#align geometry.simplicial_complex.disjoint_or_exists_inter_eq_convex_hull Geometry.SimplicialComplex.disjoint_or_exists_inter_eq_convexHull

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (t «expr ⊆ » s) -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (s t «expr ∈ » faces) -/
/-- Construct a simplicial complex by removing the empty face for you. -/
@[simps]
def ofErase (faces : Set (Finset E))
    (indep : ∀ s ∈ faces, AffineIndependent 𝕜 (coe : (s : Set E) → E))
    (down_closed : ∀ s ∈ faces, ∀ (t) (_ : t ⊆ s), t ∈ faces)
    (inter_subset_convex_hull :
      ∀ (s) (_ : s ∈ faces) (t) (_ : t ∈ faces),
        convexHull 𝕜 ↑s ∩ convexHull 𝕜 ↑t ⊆ convexHull 𝕜 (s ∩ t : Set E)) :
    SimplicialComplex 𝕜 E where
  faces := faces \ {∅}
  not_empty_mem h := h.2 (mem_singleton _)
  indep s hs := indep _ hs.1
  down_closed s t hs hts ht := ⟨down_closed _ hs.1 _ hts, ht⟩
  inter_subset_convexHull s t hs ht := inter_subset_convex_hull _ hs.1 _ ht.1
#align geometry.simplicial_complex.of_erase Geometry.SimplicialComplex.ofErase

/-- Construct a simplicial complex as a subset of a given simplicial complex. -/
@[simps]
def ofSubcomplex (K : SimplicialComplex 𝕜 E) (faces : Set (Finset E)) (subset : faces ⊆ K.faces)
    (down_closed : ∀ {s t}, s ∈ faces → t ⊆ s → t ∈ faces) : SimplicialComplex 𝕜 E :=
  { faces
    not_empty_mem := fun h => K.not_empty_mem (subset h)
    indep := fun s hs => K.indep (subset hs)
    down_closed := fun s t hs hts _ => down_closed hs hts
    inter_subset_convexHull := fun s t hs ht => K.inter_subset_convexHull (subset hs) (subset ht) }
#align geometry.simplicial_complex.of_subcomplex Geometry.SimplicialComplex.ofSubcomplex

/-! ### Vertices -/

/-- The vertices of a simplicial complex are its zero dimensional faces. -/
def vertices (K : SimplicialComplex 𝕜 E) : Set E :=
  { x | {x} ∈ K.faces }
#align geometry.simplicial_complex.vertices Geometry.SimplicialComplex.vertices

theorem mem_vertices : x ∈ K.vertices ↔ {x} ∈ K.faces :=
  Iff.rfl
#align geometry.simplicial_complex.mem_vertices Geometry.SimplicialComplex.mem_vertices

theorem vertices_eq : K.vertices = ⋃ k ∈ K.faces, (k : Set E) :=
  by
  ext x
  refine' ⟨fun h => mem_bUnion h <| mem_coe.2 <| mem_singleton_self x, fun h => _⟩
  obtain ⟨s, hs, hx⟩ := mem_Union₂.1 h
  exact K.down_closed hs (Finset.singleton_subset_iff.2 <| mem_coe.1 hx) (singleton_ne_empty _)
#align geometry.simplicial_complex.vertices_eq Geometry.SimplicialComplex.vertices_eq

theorem vertices_subset_space : K.vertices ⊆ K.space :=
  vertices_eq.Subset.trans <| unionᵢ₂_mono fun x hx => subset_convexHull 𝕜 x
#align geometry.simplicial_complex.vertices_subset_space Geometry.SimplicialComplex.vertices_subset_space

theorem vertex_mem_convexHull_iff (hx : x ∈ K.vertices) (hs : s ∈ K.faces) :
    x ∈ convexHull 𝕜 (s : Set E) ↔ x ∈ s :=
  by
  refine' ⟨fun h => _, fun h => subset_convexHull _ _ h⟩
  classical
    have h := K.inter_subset_convex_hull hx hs ⟨by simp, h⟩
    by_contra H
    rwa [← coe_inter,
      Finset.disjoint_iff_inter_eq_empty.1 (Finset.disjoint_singleton_right.2 H).symm, coe_empty,
      convexHull_empty] at h
#align geometry.simplicial_complex.vertex_mem_convex_hull_iff Geometry.SimplicialComplex.vertex_mem_convexHull_iff

/-- A face is a subset of another one iff its vertices are.  -/
theorem face_subset_face_iff (hs : s ∈ K.faces) (ht : t ∈ K.faces) :
    convexHull 𝕜 (s : Set E) ⊆ convexHull 𝕜 ↑t ↔ s ⊆ t :=
  ⟨fun h x hxs =>
    (vertex_mem_convexHull_iff
          (K.down_closed hs (Finset.singleton_subset_iff.2 hxs) <| singleton_ne_empty _) ht).1
      (h (subset_convexHull 𝕜 (↑s) hxs)),
    convexHull_mono⟩
#align geometry.simplicial_complex.face_subset_face_iff Geometry.SimplicialComplex.face_subset_face_iff

/-! ### Facets -/

/-- A facet of a simplicial complex is a maximal face. -/
def facets (K : SimplicialComplex 𝕜 E) : Set (Finset E) :=
  { s ∈ K.faces | ∀ ⦃t⦄, t ∈ K.faces → s ⊆ t → s = t }
#align geometry.simplicial_complex.facets Geometry.SimplicialComplex.facets

theorem mem_facets : s ∈ K.facets ↔ s ∈ K.faces ∧ ∀ t ∈ K.faces, s ⊆ t → s = t :=
  mem_sep_iff
#align geometry.simplicial_complex.mem_facets Geometry.SimplicialComplex.mem_facets

theorem facets_subset : K.facets ⊆ K.faces := fun s hs => hs.1
#align geometry.simplicial_complex.facets_subset Geometry.SimplicialComplex.facets_subset

theorem not_facet_iff_subface (hs : s ∈ K.faces) : s ∉ K.facets ↔ ∃ t, t ∈ K.faces ∧ s ⊂ t :=
  by
  refine' ⟨fun hs' : ¬(_ ∧ _) => _, _⟩
  · push_neg  at hs'
    obtain ⟨t, ht⟩ := hs' hs
    exact ⟨t, ht.1, ⟨ht.2.1, fun hts => ht.2.2 (subset.antisymm ht.2.1 hts)⟩⟩
  · rintro ⟨t, ht⟩ ⟨hs, hs'⟩
    have := hs' ht.1 ht.2.1
    rw [this] at ht
    exact ht.2.2 (subset.refl t)
#align geometry.simplicial_complex.not_facet_iff_subface Geometry.SimplicialComplex.not_facet_iff_subface

/-!
### The semilattice of simplicial complexes

`K ≤ L` means that `K.faces ⊆ L.faces`.
-/

-- `has_ssubset.ssubset.ne` would be handy here
variable (𝕜 E)

/-- The complex consisting of only the faces present in both of its arguments. -/
instance : Inf (SimplicialComplex 𝕜 E) :=
  ⟨fun K L =>
    { faces := K.faces ∩ L.faces
      not_empty_mem := fun h => K.not_empty_mem (Set.inter_subset_left _ _ h)
      indep := fun s hs => K.indep hs.1
      down_closed := fun s t hs hst ht => ⟨K.down_closed hs.1 hst ht, L.down_closed hs.2 hst ht⟩
      inter_subset_convexHull := fun s t hs ht => K.inter_subset_convexHull hs.1 ht.1 }⟩

instance : SemilatticeInf (SimplicialComplex 𝕜 E) :=
  {
    PartialOrder.lift faces fun x y =>
      ext _ _ with
    inf := (· ⊓ ·)
    inf_le_left := fun K L s hs => hs.1
    inf_le_right := fun K L s hs => hs.2
    le_inf := fun K L M hKL hKM s hs => ⟨hKL hs, hKM hs⟩ }

instance : Bot (SimplicialComplex 𝕜 E) :=
  ⟨{  faces := ∅
      not_empty_mem := Set.not_mem_empty ∅
      indep := fun s hs => (Set.not_mem_empty _ hs).elim
      down_closed := fun s _ hs => (Set.not_mem_empty _ hs).elim
      inter_subset_convexHull := fun s _ hs => (Set.not_mem_empty _ hs).elim }⟩

instance : OrderBot (SimplicialComplex 𝕜 E) :=
  { SimplicialComplex.hasBot 𝕜 E with bot_le := fun K => Set.empty_subset _ }

instance : Inhabited (SimplicialComplex 𝕜 E) :=
  ⟨⊥⟩

variable {𝕜 E}

theorem faces_bot : (⊥ : SimplicialComplex 𝕜 E).faces = ∅ :=
  rfl
#align geometry.simplicial_complex.faces_bot Geometry.SimplicialComplex.faces_bot

theorem space_bot : (⊥ : SimplicialComplex 𝕜 E).space = ∅ :=
  Set.bunionᵢ_empty _
#align geometry.simplicial_complex.space_bot Geometry.SimplicialComplex.space_bot

theorem facets_bot : (⊥ : SimplicialComplex 𝕜 E).facets = ∅ :=
  eq_empty_of_subset_empty facets_subset
#align geometry.simplicial_complex.facets_bot Geometry.SimplicialComplex.facets_bot

end SimplicialComplex

end Geometry


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Abhimanyu Pallavi Sudhir, Jean Lo, Calle Sönne
-/
import analysis.special_functions.log
import analysis.special_functions.exp_deriv

/-!
# Derivative and series expansion of real logarithm

In this file we prove that `real.log` is infinitely smooth at all nonzero `x : ℝ`. We also prove
that the series `∑' n : ℕ, x ^ (n + 1) / (n + 1)` converges to `(-real.log (1 - x))` for all
`x : ℝ`, `|x| < 1`.

## Tags

logarighm, derivative
-/

open filter finset set
open_locale topological_space big_operators

namespace real

variables {x : ℝ}

lemma has_strict_deriv_at_log_of_pos (hx : 0 < x) : has_strict_deriv_at log x⁻¹ x :=
have has_strict_deriv_at log (exp $ log x)⁻¹ x,
from (has_strict_deriv_at_exp $ log x).of_local_left_inverse (continuous_at_log hx.ne')
  (ne_of_gt $ exp_pos _) $ eventually.mono (lt_mem_nhds hx) @exp_log,
by rwa [exp_log hx] at this

lemma has_strict_deriv_at_log (hx : x ≠ 0) : has_strict_deriv_at log x⁻¹ x :=
begin
  cases hx.lt_or_lt with hx hx,
  { convert (has_strict_deriv_at_log_of_pos (neg_pos.mpr hx)).comp x (has_strict_deriv_at_neg x),
    { ext y, exact (log_neg_eq_log y).symm },
    { field_simp [hx.ne] } },
  { exact has_strict_deriv_at_log_of_pos hx }
end

lemma has_deriv_at_log (hx : x ≠ 0) : has_deriv_at log x⁻¹ x :=
(has_strict_deriv_at_log hx).has_deriv_at

lemma differentiable_at_log (hx : x ≠ 0) : differentiable_at ℝ log x :=
(has_deriv_at_log hx).differentiable_at

lemma differentiable_on_log : differentiable_on ℝ log {0}ᶜ :=
λ x hx, (differentiable_at_log hx).differentiable_within_at

@[simp] lemma differentiable_at_log_iff : differentiable_at ℝ log x ↔ x ≠ 0 :=
⟨λ h, continuous_at_log_iff.1 h.continuous_at, differentiable_at_log⟩

lemma deriv_log (x : ℝ) : deriv log x = x⁻¹ :=
if hx : x = 0 then
  by rw [deriv_zero_of_not_differentiable_at (mt differentiable_at_log_iff.1 (not_not.2 hx)), hx,
    inv_zero]
else (has_deriv_at_log hx).deriv

@[simp] lemma deriv_log' : deriv log = has_inv.inv := funext deriv_log

lemma times_cont_diff_on_log {n : with_top ℕ} : times_cont_diff_on ℝ n log {0}ᶜ :=
begin
  suffices : times_cont_diff_on ℝ ⊤ log {0}ᶜ, from this.of_le le_top,
  refine (times_cont_diff_on_top_iff_deriv_of_open is_open_compl_singleton).2 _,
  simp [differentiable_on_log, times_cont_diff_on_inv]
end

lemma times_cont_diff_at_log {n : with_top ℕ} : times_cont_diff_at ℝ n log x ↔ x ≠ 0 :=
⟨λ h, continuous_at_log_iff.1 h.continuous_at,
  λ hx, (times_cont_diff_on_log x hx).times_cont_diff_at $
    is_open.mem_nhds is_open_compl_singleton hx⟩

end real

section log_differentiable
open real

section deriv

variables {f : ℝ → ℝ} {x f' : ℝ} {s : set ℝ}

lemma has_deriv_within_at.log (hf : has_deriv_within_at f f' s x) (hx : f x ≠ 0) :
  has_deriv_within_at (λ y, log (f y)) (f' / (f x)) s x :=
begin
  rw div_eq_inv_mul,
  exact (has_deriv_at_log hx).comp_has_deriv_within_at x hf
end

lemma has_deriv_at.log (hf : has_deriv_at f f' x) (hx : f x ≠ 0) :
  has_deriv_at (λ y, log (f y)) (f' / f x) x :=
begin
  rw ← has_deriv_within_at_univ at *,
  exact hf.log hx
end

lemma has_strict_deriv_at.log (hf : has_strict_deriv_at f f' x) (hx : f x ≠ 0) :
  has_strict_deriv_at (λ y, log (f y)) (f' / f x) x :=
begin
  rw div_eq_inv_mul,
  exact (has_strict_deriv_at_log hx).comp x hf
end

lemma deriv_within.log (hf : differentiable_within_at ℝ f s x) (hx : f x ≠ 0)
  (hxs : unique_diff_within_at ℝ s x) :
  deriv_within (λx, log (f x)) s x = (deriv_within f s x) / (f x) :=
(hf.has_deriv_within_at.log hx).deriv_within hxs

@[simp] lemma deriv.log (hf : differentiable_at ℝ f x) (hx : f x ≠ 0) :
  deriv (λx, log (f x)) x = (deriv f x) / (f x) :=
(hf.has_deriv_at.log hx).deriv

end deriv

section fderiv

variables {E : Type*} [normed_group E] [normed_space ℝ E] {f : E → ℝ} {x : E} {f' : E →L[ℝ] ℝ}
  {s : set E}

lemma has_fderiv_within_at.log (hf : has_fderiv_within_at f f' s x) (hx : f x ≠ 0) :
  has_fderiv_within_at (λ x, log (f x)) ((f x)⁻¹ • f') s x :=
(has_deriv_at_log hx).comp_has_fderiv_within_at x hf

lemma has_fderiv_at.log (hf : has_fderiv_at f f' x) (hx : f x ≠ 0) :
  has_fderiv_at (λ x, log (f x)) ((f x)⁻¹ • f') x :=
(has_deriv_at_log hx).comp_has_fderiv_at x hf

lemma has_strict_fderiv_at.log (hf : has_strict_fderiv_at f f' x) (hx : f x ≠ 0) :
  has_strict_fderiv_at (λ x, log (f x)) ((f x)⁻¹ • f') x :=
(has_strict_deriv_at_log hx).comp_has_strict_fderiv_at x hf

lemma differentiable_within_at.log (hf : differentiable_within_at ℝ f s x) (hx : f x ≠ 0) :
  differentiable_within_at ℝ (λx, log (f x)) s x :=
(hf.has_fderiv_within_at.log hx).differentiable_within_at

@[simp] lemma differentiable_at.log (hf : differentiable_at ℝ f x) (hx : f x ≠ 0) :
  differentiable_at ℝ (λx, log (f x)) x :=
(hf.has_fderiv_at.log hx).differentiable_at

lemma times_cont_diff_at.log {n} (hf : times_cont_diff_at ℝ n f x) (hx : f x ≠ 0) :
  times_cont_diff_at ℝ n (λ x, log (f x)) x :=
(times_cont_diff_at_log.2 hx).comp x hf

lemma times_cont_diff_within_at.log {n} (hf : times_cont_diff_within_at ℝ n f s x) (hx : f x ≠ 0) :
  times_cont_diff_within_at ℝ n (λ x, log (f x)) s x :=
(times_cont_diff_at_log.2 hx).comp_times_cont_diff_within_at x hf

lemma times_cont_diff_on.log {n} (hf : times_cont_diff_on ℝ n f s) (hs : ∀ x ∈ s, f x ≠ 0) :
  times_cont_diff_on ℝ n (λ x, log (f x)) s :=
λ x hx, (hf x hx).log (hs x hx)

lemma times_cont_diff.log {n} (hf : times_cont_diff ℝ n f) (h : ∀ x, f x ≠ 0) :
  times_cont_diff ℝ n (λ x, log (f x)) :=
times_cont_diff_iff_times_cont_diff_at.2 $ λ x, hf.times_cont_diff_at.log (h x)

lemma differentiable_on.log (hf : differentiable_on ℝ f s) (hx : ∀ x ∈ s, f x ≠ 0) :
  differentiable_on ℝ (λx, log (f x)) s :=
λx h, (hf x h).log (hx x h)

@[simp] lemma differentiable.log (hf : differentiable ℝ f) (hx : ∀ x, f x ≠ 0) :
  differentiable ℝ (λx, log (f x)) :=
λx, (hf x).log (hx x)

lemma fderiv_within.log (hf : differentiable_within_at ℝ f s x) (hx : f x ≠ 0)
  (hxs : unique_diff_within_at ℝ s x) :
  fderiv_within ℝ (λx, log (f x)) s x = (f x)⁻¹ • fderiv_within ℝ f s x :=
(hf.has_fderiv_within_at.log hx).fderiv_within hxs

@[simp] lemma fderiv.log (hf : differentiable_at ℝ f x) (hx : f x ≠ 0) :
  fderiv ℝ (λx, log (f x)) x = (f x)⁻¹ • fderiv ℝ f x :=
(hf.has_fderiv_at.log hx).fderiv

end fderiv

end log_differentiable

namespace real

/-- The function `x * log (1 + t / x)` tends to `t` at `+∞`. -/
lemma tendsto_mul_log_one_plus_div_at_top (t : ℝ) :
  tendsto (λ x, x * log (1 + t / x)) at_top (𝓝 t) :=
begin
  have h₁ : tendsto (λ h, h⁻¹ * log (1 + t * h)) (𝓝[{0}ᶜ] 0) (𝓝 t),
  { simpa [has_deriv_at_iff_tendsto_slope] using
      ((has_deriv_at_const _ 1).add ((has_deriv_at_id (0 : ℝ)).const_mul t)).log (by simp) },
  have h₂ : tendsto (λ x : ℝ, x⁻¹) at_top (𝓝[{0}ᶜ] 0) :=
    tendsto_inv_at_top_zero'.mono_right (nhds_within_mono _ (λ x hx, (set.mem_Ioi.mp hx).ne')),
  convert h₁.comp h₂,
  ext,
  field_simp [mul_comm],
end

open_locale big_operators

/-- A crude lemma estimating the difference between `log (1-x)` and its Taylor series at `0`,
where the main point of the bound is that it tends to `0`. The goal is to deduce the series
expansion of the logarithm, in `has_sum_pow_div_log_of_abs_lt_1`.
-/
lemma abs_log_sub_add_sum_range_le {x : ℝ} (h : |x| < 1) (n : ℕ) :
  |((∑ i in range n, x^(i+1)/(i+1)) + log (1-x))| ≤ (|x|)^(n+1) / (1 - |x|) :=
begin
  /- For the proof, we show that the derivative of the function to be estimated is small,
  and then apply the mean value inequality. -/
  let F : ℝ → ℝ := λ x, ∑ i in range n, x^(i+1)/(i+1) + log (1-x),
  -- First step: compute the derivative of `F`
  have A : ∀ y ∈ Ioo (-1 : ℝ) 1, deriv F y = - (y^n) / (1 - y),
  { assume y hy,
    have : (∑ i in range n, (↑i + 1) * y ^ i / (↑i + 1)) = (∑ i in range n, y ^ i),
    { congr' with i,
      have : (i : ℝ) + 1 ≠ 0 := ne_of_gt (nat.cast_add_one_pos i),
      field_simp [this, mul_comm] },
    field_simp [F, this, ← geom_sum_def, geom_sum_eq (ne_of_lt hy.2),
                sub_ne_zero_of_ne (ne_of_gt hy.2), sub_ne_zero_of_ne (ne_of_lt hy.2)],
    ring },
  -- second step: show that the derivative of `F` is small
  have B : ∀ y ∈ Icc (-|x|) (|x|), |deriv F y| ≤ |x|^n / (1 - |x|),
  { assume y hy,
    have : y ∈ Ioo (-(1 : ℝ)) 1 := ⟨lt_of_lt_of_le (neg_lt_neg h) hy.1, lt_of_le_of_lt hy.2 h⟩,
    calc |deriv F y| = | -(y^n) / (1 - y)| : by rw [A y this]
    ... ≤ |x|^n / (1 - |x|) :
      begin
        have : |y| ≤ |x| := abs_le.2 hy,
        have : 0 < 1 - |x|, by linarith,
        have : 1 - |x| ≤ |1 - y| := le_trans (by linarith [hy.2]) (le_abs_self _),
        simp only [← pow_abs, abs_div, abs_neg],
        apply_rules [div_le_div, pow_nonneg, abs_nonneg, pow_le_pow_of_le_left]
      end },
  -- third step: apply the mean value inequality
  have C : ∥F x - F 0∥ ≤ (|x|^n / (1 - |x|)) * ∥x - 0∥,
  { have : ∀ y ∈ Icc (- |x|) (|x|), differentiable_at ℝ F y,
    { assume y hy,
      have : 1 - y ≠ 0 := sub_ne_zero_of_ne (ne_of_gt (lt_of_le_of_lt hy.2 h)),
      simp [F, this] },
    apply convex.norm_image_sub_le_of_norm_deriv_le this B (convex_Icc _ _) _ _,
    { simpa using abs_nonneg x },
    { simp [le_abs_self x, neg_le.mp (neg_le_abs_self x)] } },
  -- fourth step: conclude by massaging the inequality of the third step
  simpa [F, norm_eq_abs, div_mul_eq_mul_div, pow_succ'] using C
end

/-- Power series expansion of the logarithm around `1`. -/
theorem has_sum_pow_div_log_of_abs_lt_1 {x : ℝ} (h : |x| < 1) :
  has_sum (λ (n : ℕ), x ^ (n + 1) / (n + 1)) (-log (1 - x)) :=
begin
  rw summable.has_sum_iff_tendsto_nat,
  show tendsto (λ (n : ℕ), ∑ (i : ℕ) in range n, x ^ (i + 1) / (i + 1)) at_top (𝓝 (-log (1 - x))),
  { rw [tendsto_iff_norm_tendsto_zero],
    simp only [norm_eq_abs, sub_neg_eq_add],
    refine squeeze_zero (λ n, abs_nonneg _) (abs_log_sub_add_sum_range_le h) _,
    suffices : tendsto (λ (t : ℕ), |x| ^ (t + 1) / (1 - |x|)) at_top
      (𝓝 (|x| * 0 / (1 - |x|))), by simpa,
    simp only [pow_succ],
    refine (tendsto_const_nhds.mul _).div_const,
    exact tendsto_pow_at_top_nhds_0_of_lt_1 (abs_nonneg _) h },
  show summable (λ (n : ℕ), x ^ (n + 1) / (n + 1)),
  { refine summable_of_norm_bounded _ (summable_geometric_of_lt_1 (abs_nonneg _) h) (λ i, _),
    calc ∥x ^ (i + 1) / (i + 1)∥
    = |x| ^ (i+1) / (i+1) :
      begin
        have : (0 : ℝ) ≤ i + 1 := le_of_lt (nat.cast_add_one_pos i),
        rw [norm_eq_abs, abs_div, ← pow_abs, abs_of_nonneg this],
      end
    ... ≤ |x| ^ (i+1) / (0 + 1) :
      begin
        apply_rules [div_le_div_of_le_left, pow_nonneg, abs_nonneg, add_le_add_right,
          i.cast_nonneg],
        norm_num,
      end
    ... ≤ |x| ^ i :
      by simpa [pow_succ'] using mul_le_of_le_one_right (pow_nonneg (abs_nonneg x) i) (le_of_lt h) }
end

end real