/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import order.antisymmetrization
import order.category.Preorder

/-!
# Category of partial orders

This defines `PartialOrder`, the category of partial orders with monotone maps.
-/

open category_theory

universe u

/-- The category of partially ordered types. -/
def PartialOrder := bundled partial_order

namespace PartialOrder

instance : bundled_hom.parent_projection @partial_order.to_preorder := ⟨⟩

attribute [derive [large_category, concrete_category]] PartialOrder

instance : has_coe_to_sort PartialOrder Type* := bundled.has_coe_to_sort

/-- Construct a bundled PartialOrder from the underlying type and typeclass. -/
def of (α : Type*) [partial_order α] : PartialOrder := bundled.of α

@[simp] lemma coe_of (α : Type*) [partial_order α] : ↥(of α) = α := rfl

instance : inhabited PartialOrder := ⟨of punit⟩

instance (α : PartialOrder) : partial_order α := α.str

instance has_forget_to_Preorder : has_forget₂ PartialOrder Preorder := bundled_hom.forget₂ _ _

/-- Constructs an equivalence between partial orders from an order isomorphism between them. -/
@[simps] def iso.mk {α β : PartialOrder.{u}} (e : α ≃o β) : α ≅ β :=
{ hom := e,
  inv := e.symm,
  hom_inv_id' := by { ext, exact e.symm_apply_apply x },
  inv_hom_id' := by { ext, exact e.apply_symm_apply x } }

/-- `order_dual` as a functor. -/
@[simps] def dual : PartialOrder ⥤ PartialOrder :=
{ obj := λ X, of Xᵒᵈ, map := λ X Y, order_hom.dual }

/-- The equivalence between `PartialOrder` and itself induced by `order_dual` both ways. -/
@[simps functor inverse] def dual_equiv : PartialOrder ≌ PartialOrder :=
equivalence.mk dual dual
  (nat_iso.of_components (λ X, iso.mk $ order_iso.dual_dual X) $ λ X Y f, rfl)
  (nat_iso.of_components (λ X, iso.mk $ order_iso.dual_dual X) $ λ X Y f, rfl)

end PartialOrder

lemma PartialOrder_dual_comp_forget_to_Preorder :
  PartialOrder.dual ⋙ forget₂ PartialOrder Preorder =
    forget₂ PartialOrder Preorder ⋙ Preorder.dual := rfl

/-- `antisymmetrization` as a functor. It is the free functor. -/
def Preorder_to_PartialOrder : Preorder.{u} ⥤ PartialOrder :=
{ obj := λ X, PartialOrder.of (antisymmetrization X (≤)),
  map := λ X Y f, f.antisymmetrization,
  map_id' := λ X,
    by { ext, exact quotient.induction_on' x (λ x, quotient.map'_mk' _ (λ a b, id) _) },
  map_comp' := λ X Y Z f g,
    by { ext, exact quotient.induction_on' x (λ x, order_hom.antisymmetrization_apply_mk _ _) } }

/-- `Preorder_to_PartialOrder` is left adjoint to the forgetful functor, meaning it is the free
functor from `Preorder` to `PartialOrder`. -/
def Preorder_to_PartialOrder_forget_adjunction :
  Preorder_to_PartialOrder.{u} ⊣ forget₂ PartialOrder Preorder :=
adjunction.mk_of_hom_equiv
  { hom_equiv := λ X Y, { to_fun := λ f,
      ⟨f ∘ to_antisymmetrization (≤), f.mono.comp to_antisymmetrization_mono⟩,
    inv_fun := λ f, ⟨λ a, quotient.lift_on' a f $ λ a b h, (antisymm_rel.image h f.mono).eq, λ a b,
      quotient.induction_on₂' a b $ λ a b h, f.mono h⟩,
    left_inv := λ f, order_hom.ext _ _ $ funext $ λ x, quotient.induction_on' x $ λ x, rfl,
    right_inv := λ f, order_hom.ext _ _ $ funext $ λ x, rfl },
  hom_equiv_naturality_left_symm' := λ X Y Z f g,
    order_hom.ext _ _ $ funext $ λ x, quotient.induction_on' x $ λ x, rfl,
  hom_equiv_naturality_right' := λ X Y Z f g, order_hom.ext _ _ $ funext $ λ x, rfl }

/-- `Preorder_to_PartialOrder` and `order_dual` commute. -/
@[simps] def Preorder_to_PartialOrder_comp_to_dual_iso_to_dual_comp_Preorder_to_PartialOrder :
 (Preorder_to_PartialOrder.{u} ⋙ PartialOrder.dual) ≅
    (Preorder.dual ⋙ Preorder_to_PartialOrder) :=
nat_iso.of_components (λ X, PartialOrder.iso.mk $ order_iso.dual_antisymmetrization _) $
  λ X Y f, order_hom.ext _ _ $ funext $ λ x, quotient.induction_on' x $ λ x, rfl


/-
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 algebra.homology.additive
! leanprover-community/mathlib commit 200eda15d8ff5669854ff6bcc10aaf37cb70498f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Homology.Homology
import Mathbin.Algebra.Homology.Single
import Mathbin.CategoryTheory.Preadditive.AdditiveFunctor

/-!
# Homology is an additive functor

When `V` is preadditive, `homological_complex V c` is also preadditive,
and `homology_functor` is additive.

TODO: similarly for `R`-linear.
-/

universe v u

open Classical

noncomputable section

open CategoryTheory CategoryTheory.Category CategoryTheory.Limits HomologicalComplex

variable {ι : Type _}

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

variable {c : ComplexShape ι} {C D E : HomologicalComplex V c}

variable (f g : C ⟶ D) (h k : D ⟶ E) (i : ι)

namespace HomologicalComplex

instance : Zero (C ⟶ D) :=
  ⟨{ f := fun i => 0 }⟩

instance : Add (C ⟶ D) :=
  ⟨fun f g => { f := fun i => f.f i + g.f i }⟩

instance : Neg (C ⟶ D) :=
  ⟨fun f => { f := fun i => -f.f i }⟩

instance : Sub (C ⟶ D) :=
  ⟨fun f g => { f := fun i => f.f i - g.f i }⟩

instance hasNatScalar : SMul ℕ (C ⟶ D) :=
  ⟨fun n f =>
    { f := fun i => n • f.f i
      comm' := fun i j h => by simp [preadditive.nsmul_comp, preadditive.comp_nsmul] }⟩
#align homological_complex.has_nat_scalar HomologicalComplex.hasNatScalar

instance hasIntScalar : SMul ℤ (C ⟶ D) :=
  ⟨fun n f =>
    { f := fun i => n • f.f i
      comm' := fun i j h => by simp [preadditive.zsmul_comp, preadditive.comp_zsmul] }⟩
#align homological_complex.has_int_scalar HomologicalComplex.hasIntScalar

@[simp]
theorem zero_f_apply (i : ι) : (0 : C ⟶ D).f i = 0 :=
  rfl
#align homological_complex.zero_f_apply HomologicalComplex.zero_f_apply

@[simp]
theorem add_f_apply (f g : C ⟶ D) (i : ι) : (f + g).f i = f.f i + g.f i :=
  rfl
#align homological_complex.add_f_apply HomologicalComplex.add_f_apply

@[simp]
theorem neg_f_apply (f : C ⟶ D) (i : ι) : (-f).f i = -f.f i :=
  rfl
#align homological_complex.neg_f_apply HomologicalComplex.neg_f_apply

@[simp]
theorem sub_f_apply (f g : C ⟶ D) (i : ι) : (f - g).f i = f.f i - g.f i :=
  rfl
#align homological_complex.sub_f_apply HomologicalComplex.sub_f_apply

@[simp]
theorem nsmul_f_apply (n : ℕ) (f : C ⟶ D) (i : ι) : (n • f).f i = n • f.f i :=
  rfl
#align homological_complex.nsmul_f_apply HomologicalComplex.nsmul_f_apply

@[simp]
theorem zsmul_f_apply (n : ℤ) (f : C ⟶ D) (i : ι) : (n • f).f i = n • f.f i :=
  rfl
#align homological_complex.zsmul_f_apply HomologicalComplex.zsmul_f_apply

instance : AddCommGroup (C ⟶ D) :=
  Function.Injective.addCommGroup Hom.f HomologicalComplex.hom_f_injective (by tidy) (by tidy)
    (by tidy) (by tidy) (by tidy) (by tidy)

instance : Preadditive (HomologicalComplex V c) where

/-- The `i`-th component of a chain map, as an additive map from chain maps to morphisms. -/
@[simps]
def Hom.fAddMonoidHom {C₁ C₂ : HomologicalComplex V c} (i : ι) : (C₁ ⟶ C₂) →+ (C₁.pt i ⟶ C₂.pt i) :=
  AddMonoidHom.mk' (fun f => Hom.f f i) fun _ _ => rfl
#align homological_complex.hom.f_add_monoid_hom HomologicalComplex.Hom.fAddMonoidHom

end HomologicalComplex

namespace HomologicalComplex

instance eval_additive (i : ι) : (eval V c i).Additive where
#align homological_complex.eval_additive HomologicalComplex.eval_additive

instance cycles_additive [HasEqualizers V] : (cyclesFunctor V c i).Additive where
#align homological_complex.cycles_additive HomologicalComplex.cycles_additive

variable [HasImages V] [HasImageMaps V]

instance boundaries_additive : (boundariesFunctor V c i).Additive where
#align homological_complex.boundaries_additive HomologicalComplex.boundaries_additive

variable [HasEqualizers V] [HasCokernels V]

instance homology_additive : (homologyFunctor V c i).Additive
    where map_add' C D f g := by
    dsimp [homologyFunctor]
    ext
    simp only [homology.π_map, preadditive.comp_add, ← preadditive.add_comp]
    congr
    ext; simp
#align homological_complex.homology_additive HomologicalComplex.homology_additive

end HomologicalComplex

namespace CategoryTheory

variable {W : Type _} [Category W] [Preadditive W]

/-- An additive functor induces a functor between homological complexes.
This is sometimes called the "prolongation".
-/
@[simps]
def Functor.mapHomologicalComplex (F : V ⥤ W) [F.Additive] (c : ComplexShape ι) :
    HomologicalComplex V c ⥤ HomologicalComplex W c
    where
  obj C :=
    { pt := fun i => F.obj (C.pt i)
      d := fun i j => F.map (C.d i j)
      shape' := fun i j w => by rw [C.shape _ _ w, F.map_zero]
      d_comp_d' := fun i j k _ _ => by rw [← F.map_comp, C.d_comp_d, F.map_zero] }
  map C D f :=
    { f := fun i => F.map (f.f i)
      comm' := fun i j h => by
        dsimp
        rw [← F.map_comp, ← F.map_comp, f.comm] }
#align category_theory.functor.map_homological_complex CategoryTheory.Functor.mapHomologicalComplex

variable (V)

/-- The functor on homological complexes induced by the identity functor is
isomorphic to the identity functor. -/
@[simps]
def Functor.mapHomologicalComplexIdIso (c : ComplexShape ι) : (𝟭 V).mapHomologicalComplex c ≅ 𝟭 _ :=
  NatIso.ofComponents (fun K => Hom.isoOfComponents (fun i => Iso.refl _) (by tidy)) (by tidy)
#align category_theory.functor.map_homological_complex_id_iso CategoryTheory.Functor.mapHomologicalComplexIdIso

variable {V}

instance Functor.map_homogical_complex_additive (F : V ⥤ W) [F.Additive] (c : ComplexShape ι) :
    (F.mapHomologicalComplex c).Additive where
#align category_theory.functor.map_homogical_complex_additive CategoryTheory.Functor.map_homogical_complex_additive

instance Functor.mapHomologicalComplex_reflects_iso (F : V ⥤ W) [F.Additive]
    [ReflectsIsomorphisms F] (c : ComplexShape ι) :
    ReflectsIsomorphisms (F.mapHomologicalComplex c) :=
  ⟨fun X Y f => by
    intro
    haveI : ∀ n : ι, is_iso (F.map (f.f n)) := fun n =>
      is_iso.of_iso
        ((HomologicalComplex.eval W c n).mapIso (as_iso ((F.map_homological_complex c).map f)))
    haveI := fun n => is_iso_of_reflects_iso (f.f n) F
    exact HomologicalComplex.Hom.isIso_of_components f⟩
#align category_theory.functor.map_homological_complex_reflects_iso CategoryTheory.Functor.mapHomologicalComplex_reflects_iso

/-- A natural transformation between functors induces a natural transformation
between those functors applied to homological complexes.
-/
@[simps]
def NatTrans.mapHomologicalComplex {F G : V ⥤ W} [F.Additive] [G.Additive] (α : F ⟶ G)
    (c : ComplexShape ι) : F.mapHomologicalComplex c ⟶ G.mapHomologicalComplex c
    where app C := { f := fun i => α.app _ }
#align category_theory.nat_trans.map_homological_complex CategoryTheory.NatTrans.mapHomologicalComplex

@[simp]
theorem NatTrans.mapHomologicalComplex_id (c : ComplexShape ι) (F : V ⥤ W) [F.Additive] :
    NatTrans.mapHomologicalComplex (𝟙 F) c = 𝟙 (F.mapHomologicalComplex c) := by tidy
#align category_theory.nat_trans.map_homological_complex_id CategoryTheory.NatTrans.mapHomologicalComplex_id

@[simp]
theorem NatTrans.mapHomologicalComplex_comp (c : ComplexShape ι) {F G H : V ⥤ W} [F.Additive]
    [G.Additive] [H.Additive] (α : F ⟶ G) (β : G ⟶ H) :
    NatTrans.mapHomologicalComplex (α ≫ β) c =
      NatTrans.mapHomologicalComplex α c ≫ NatTrans.mapHomologicalComplex β c :=
  by tidy
#align category_theory.nat_trans.map_homological_complex_comp CategoryTheory.NatTrans.mapHomologicalComplex_comp

@[simp, reassoc.1]
theorem NatTrans.mapHomologicalComplex_naturality {c : ComplexShape ι} {F G : V ⥤ W} [F.Additive]
    [G.Additive] (α : F ⟶ G) {C D : HomologicalComplex V c} (f : C ⟶ D) :
    (F.mapHomologicalComplex c).map f ≫ (NatTrans.mapHomologicalComplex α c).app D =
      (NatTrans.mapHomologicalComplex α c).app C ≫ (G.mapHomologicalComplex c).map f :=
  by tidy
#align category_theory.nat_trans.map_homological_complex_naturality CategoryTheory.NatTrans.mapHomologicalComplex_naturality

/-- A natural isomorphism between functors induces a natural isomorphism
between those functors applied to homological complexes.
-/
@[simps]
def NatIso.mapHomologicalComplex {F G : V ⥤ W} [F.Additive] [G.Additive] (α : F ≅ G)
    (c : ComplexShape ι) : F.mapHomologicalComplex c ≅ G.mapHomologicalComplex c
    where
  Hom := α.Hom.mapHomologicalComplex c
  inv := α.inv.mapHomologicalComplex c
  hom_inv_id' := by simpa only [← nat_trans.map_homological_complex_comp, α.hom_inv_id]
  inv_hom_id' := by simpa only [← nat_trans.map_homological_complex_comp, α.inv_hom_id]
#align category_theory.nat_iso.map_homological_complex CategoryTheory.NatIso.mapHomologicalComplex

/-- An equivalence of categories induces an equivalences between the respective categories
of homological complex.
-/
@[simps]
def Equivalence.mapHomologicalComplex (e : V ≌ W) [e.Functor.Additive] (c : ComplexShape ι) :
    HomologicalComplex V c ≌ HomologicalComplex W c
    where
  Functor := e.Functor.mapHomologicalComplex c
  inverse := e.inverse.mapHomologicalComplex c
  unitIso :=
    (Functor.mapHomologicalComplexIdIso V c).symm ≪≫ NatIso.mapHomologicalComplex e.unitIso c
  counitIso := NatIso.mapHomologicalComplex e.counitIso c ≪≫ Functor.mapHomologicalComplexIdIso W c
#align category_theory.equivalence.map_homological_complex CategoryTheory.Equivalence.mapHomologicalComplex

end CategoryTheory

namespace ChainComplex

variable {W : Type _} [Category W] [Preadditive W]

variable {α : Type _} [AddRightCancelSemigroup α] [One α] [DecidableEq α]

theorem map_chain_complex_of (F : V ⥤ W) [F.Additive] (X : α → V) (d : ∀ n, X (n + 1) ⟶ X n)
    (sq : ∀ n, d (n + 1) ≫ d n = 0) :
    (F.mapHomologicalComplex _).obj (ChainComplex.of X d sq) =
      ChainComplex.of (fun n => F.obj (X n)) (fun n => F.map (d n)) fun n => by
        rw [← F.map_comp, sq n, functor.map_zero] :=
  by
  refine' HomologicalComplex.ext rfl _
  rintro i j (rfl : j + 1 = i)
  simp only [CategoryTheory.Functor.mapHomologicalComplex_obj_d, of_d, eq_to_hom_refl, comp_id,
    id_comp]
#align chain_complex.map_chain_complex_of ChainComplex.map_chain_complex_of

end ChainComplex

variable [HasZeroObject V] {W : Type _} [Category W] [Preadditive W] [HasZeroObject W]

namespace HomologicalComplex

attribute [local simp] eq_to_hom_map

/-- Turning an object into a complex supported at `j` then applying a functor is
the same as applying the functor then forming the complex.
-/
def singleMapHomologicalComplex (F : V ⥤ W) [F.Additive] (c : ComplexShape ι) (j : ι) :
    single V c j ⋙ F.mapHomologicalComplex _ ≅ F ⋙ single W c j :=
  NatIso.ofComponents
    (fun X =>
      { Hom := { f := fun i => if h : i = j then eqToHom (by simp [h]) else 0 }
        inv := { f := fun i => if h : i = j then eqToHom (by simp [h]) else 0 }
        hom_inv_id' := by
          ext i
          dsimp
          split_ifs with h
          · simp [h]
          · rw [zero_comp, if_neg h]
            exact (zero_of_source_iso_zero _ F.map_zero_object).symm
        inv_hom_id' := by
          ext i
          dsimp
          split_ifs with h
          · simp [h]
          · rw [zero_comp, if_neg h]
            simp })
    fun X Y f => by
    ext i
    dsimp
    split_ifs with h <;> simp [h]
#align homological_complex.single_map_homological_complex HomologicalComplex.singleMapHomologicalComplex

variable (F : V ⥤ W) [Functor.Additive F] (c)

@[simp]
theorem singleMapHomologicalComplex_hom_app_self (j : ι) (X : V) :
    ((singleMapHomologicalComplex F c j).Hom.app X).f j = eqToHom (by simp) := by
  simp [single_map_homological_complex]
#align homological_complex.single_map_homological_complex_hom_app_self HomologicalComplex.singleMapHomologicalComplex_hom_app_self

@[simp]
theorem singleMapHomologicalComplex_hom_app_ne {i j : ι} (h : i ≠ j) (X : V) :
    ((singleMapHomologicalComplex F c j).Hom.app X).f i = 0 := by
  simp [single_map_homological_complex, h]
#align homological_complex.single_map_homological_complex_hom_app_ne HomologicalComplex.singleMapHomologicalComplex_hom_app_ne

@[simp]
theorem singleMapHomologicalComplex_inv_app_self (j : ι) (X : V) :
    ((singleMapHomologicalComplex F c j).inv.app X).f j = eqToHom (by simp) := by
  simp [single_map_homological_complex]
#align homological_complex.single_map_homological_complex_inv_app_self HomologicalComplex.singleMapHomologicalComplex_inv_app_self

@[simp]
theorem singleMapHomologicalComplex_inv_app_ne {i j : ι} (h : i ≠ j) (X : V) :
    ((singleMapHomologicalComplex F c j).inv.app X).f i = 0 := by
  simp [single_map_homological_complex, h]
#align homological_complex.single_map_homological_complex_inv_app_ne HomologicalComplex.singleMapHomologicalComplex_inv_app_ne

end HomologicalComplex

namespace ChainComplex

/-- Turning an object into a chain complex supported at zero then applying a functor is
the same as applying the functor then forming the complex.
-/
def single₀MapHomologicalComplex (F : V ⥤ W) [F.Additive] :
    single₀ V ⋙ F.mapHomologicalComplex _ ≅ F ⋙ single₀ W :=
  NatIso.ofComponents
    (fun X =>
      { Hom :=
          {
            f := fun i =>
              match i with
              | 0 => 𝟙 _
              | i + 1 => F.mapZeroObject.Hom }
        inv :=
          {
            f := fun i =>
              match i with
              | 0 => 𝟙 _
              | i + 1 => F.mapZeroObject.inv }
        hom_inv_id' := by
          ext (_ | i)
          · unfold_aux
            simp
          · unfold_aux
            dsimp
            simp only [comp_f, id_f, zero_comp]
            exact (zero_of_source_iso_zero _ F.map_zero_object).symm
        inv_hom_id' := by
          ext (_ | i) <;>
            · unfold_aux
              dsimp
              simp })
    fun X Y f => by
    ext (_ | i) <;>
      · unfold_aux
        dsimp
        simp
#align chain_complex.single₀_map_homological_complex ChainComplex.single₀MapHomologicalComplex

@[simp]
theorem single₀MapHomologicalComplex_hom_app_zero (F : V ⥤ W) [F.Additive] (X : V) :
    ((single₀MapHomologicalComplex F).Hom.app X).f 0 = 𝟙 _ :=
  rfl
#align chain_complex.single₀_map_homological_complex_hom_app_zero ChainComplex.single₀MapHomologicalComplex_hom_app_zero

@[simp]
theorem single₀MapHomologicalComplex_hom_app_succ (F : V ⥤ W) [F.Additive] (X : V) (n : ℕ) :
    ((single₀MapHomologicalComplex F).Hom.app X).f (n + 1) = 0 :=
  rfl
#align chain_complex.single₀_map_homological_complex_hom_app_succ ChainComplex.single₀MapHomologicalComplex_hom_app_succ

@[simp]
theorem single₀MapHomologicalComplex_inv_app_zero (F : V ⥤ W) [F.Additive] (X : V) :
    ((single₀MapHomologicalComplex F).inv.app X).f 0 = 𝟙 _ :=
  rfl
#align chain_complex.single₀_map_homological_complex_inv_app_zero ChainComplex.single₀MapHomologicalComplex_inv_app_zero

@[simp]
theorem single₀MapHomologicalComplex_inv_app_succ (F : V ⥤ W) [F.Additive] (X : V) (n : ℕ) :
    ((single₀MapHomologicalComplex F).inv.app X).f (n + 1) = 0 :=
  rfl
#align chain_complex.single₀_map_homological_complex_inv_app_succ ChainComplex.single₀MapHomologicalComplex_inv_app_succ

end ChainComplex

namespace CochainComplex

/-- Turning an object into a cochain complex supported at zero then applying a functor is
the same as applying the functor then forming the cochain complex.
-/
def single₀MapHomologicalComplex (F : V ⥤ W) [F.Additive] :
    single₀ V ⋙ F.mapHomologicalComplex _ ≅ F ⋙ single₀ W :=
  NatIso.ofComponents
    (fun X =>
      { Hom :=
          {
            f := fun i =>
              match i with
              | 0 => 𝟙 _
              | i + 1 => F.mapZeroObject.Hom }
        inv :=
          {
            f := fun i =>
              match i with
              | 0 => 𝟙 _
              | i + 1 => F.mapZeroObject.inv }
        hom_inv_id' := by
          ext (_ | i)
          · unfold_aux
            simp
          · unfold_aux
            dsimp
            simp only [comp_f, id_f, zero_comp]
            exact (zero_of_source_iso_zero _ F.map_zero_object).symm
        inv_hom_id' := by
          ext (_ | i) <;>
            · unfold_aux
              dsimp
              simp })
    fun X Y f => by
    ext (_ | i) <;>
      · unfold_aux
        dsimp
        simp
#align cochain_complex.single₀_map_homological_complex CochainComplex.single₀MapHomologicalComplex

@[simp]
theorem single₀MapHomologicalComplex_hom_app_zero (F : V ⥤ W) [F.Additive] (X : V) :
    ((single₀MapHomologicalComplex F).Hom.app X).f 0 = 𝟙 _ :=
  rfl
#align cochain_complex.single₀_map_homological_complex_hom_app_zero CochainComplex.single₀MapHomologicalComplex_hom_app_zero

@[simp]
theorem single₀MapHomologicalComplex_hom_app_succ (F : V ⥤ W) [F.Additive] (X : V) (n : ℕ) :
    ((single₀MapHomologicalComplex F).Hom.app X).f (n + 1) = 0 :=
  rfl
#align cochain_complex.single₀_map_homological_complex_hom_app_succ CochainComplex.single₀MapHomologicalComplex_hom_app_succ

@[simp]
theorem single₀MapHomologicalComplex_inv_app_zero (F : V ⥤ W) [F.Additive] (X : V) :
    ((single₀MapHomologicalComplex F).inv.app X).f 0 = 𝟙 _ :=
  rfl
#align cochain_complex.single₀_map_homological_complex_inv_app_zero CochainComplex.single₀MapHomologicalComplex_inv_app_zero

@[simp]
theorem single₀MapHomologicalComplex_inv_app_succ (F : V ⥤ W) [F.Additive] (X : V) (n : ℕ) :
    ((single₀MapHomologicalComplex F).inv.app X).f (n + 1) = 0 :=
  rfl
#align cochain_complex.single₀_map_homological_complex_inv_app_succ CochainComplex.single₀MapHomologicalComplex_inv_app_succ

end CochainComplex


-- import SciLean.Operators
import SciLean.Core
-- import SciLean.Functions.OdeSolve
import SciLean.Tactic.BubbleLimit

namespace SciLean

inductive ExactSolution {α : Type _} : (spec : α → Prop) → Type _
| exact {spec : α → Prop}
        (a : α) (h : spec a)
        : ExactSolution spec

def ExactSolution.val {α} {spec : α → Prop} : ExactSolution spec → α
| ExactSolution.exact a' _ => a'

-- inductive Parameter where
-- | fin (n : Nat) (m : Fin n) : Parameter
-- | nat (n : Nat) : Parameter
-- | int  (n : Int) : Parameter
-- | float (x : Float) : Parameter
-- | string (s : String) : Parameter

-- def Parameter.type : Parameter → Type
-- | fin n _ => Fin n
-- | nat _ => Nat
-- | int _ => Int
-- | float _ => Float
-- | string _ => String

-- def Parameter.val (p : Parameter) : p.type :=
-- match p with
-- | fin _ m => m
-- | nat n => n
-- | int n => n
-- | float x => x
-- | string s => s

-- This might not be provable as `Fin n = Fin m` does not imply `n = m`
-- instance (p p' : Parameter) : Decidable (p.type = p'.type) :=
-- match p, p' with
-- | .fin n _, .fin n' _ => if h : n = n' then isTrue (by simp[Parameter.type]; rw[h]) else isFalse sorry
-- | .nat _, .nat _ => isTrue (by rfl)
-- | .int _, .int _ => isTrue (by rfl)
-- | .float _, .float _ => isTrue (by rfl)
-- | .string _, .string _ => isTrue (by rfl)
-- | _, _ => isFalse sorry

inductive ApproxSolution {α : Type _} : (spec : α → Prop) → Type _
| exact {spec : α → Prop}
    (impl : α)
    (h : spec impl)
    : ApproxSolution spec
| approx {spec : α → Prop}
    (specₙ : ℕ → α → Prop)
    (consistent : ∀ (aₙ : ℕ → α),
      (∀ n, specₙ n (aₙ n)) →
      (∃ a, (limit aₙ = a) → spec a))
    (n₀ : ℕ) -- default value of `n` to be used when asembling
    (impl : (n : ℕ) → ApproxSolution (specₙ n))
    (key help : String) -- `key` is used to modify value of `n₀`
    : ApproxSolution spec
-- | approx' {spec : α → Prop}
--     (specₙ : P → α → Prop)
--     (filter : Filter P)
--     (consistent : ∀ (aₙ : P → α),
--       (∀ p, specₙ p (aₙ p)) →
--       (∃ a, (lim filter aₙ = a) → spec a))
--     (p₀ : P) -- default value of `n` to be used when asembling
--     (impl : P → α)
--     (h : ∀ p, specₙ p (impl p))
--     (key help : String) -- `key` is used to modify value of `n₀`
--     : ApproxSolution spec
| /--
  There exists a limiting process but we are going to provide only fixed
approximation
 -/
 weakApprox {spec : α → Prop}
    (specₙ : ℕ → α → Prop)
    (consistent : ∀ (aₙ : ℕ → α),
      (∀ n, specₙ n (aₙ n)) →
      (∃ a, (limit aₙ = a) → spec a))
    (n : ℕ) -- fixed aproximation at compile time
    (impl : ApproxSolution (specₙ n))
    (help : String)
    : ApproxSolution spec
-- | param {spec : α → Prop} {β : Type}
--     (impl : β → ApproxSolution spec)
--     (p : Parameter)
--     (h : p.type = β)
--     (key : String)
--     (help : String)
--     : ApproxSolution spec
-- | check {spec : α → Prop}
--     {P : Prop} [dec : Decidable P]
--     (impl : P → ApproxSolution spec)
--     (help : String)
--     : ApproxSolution spec
-- | assumption {spec : α → Prop}
--     {P : Prop}
--     (impl : P → ApproxSolution spec)
--     (help : String)
--     : ApproxSolution spec

def ApproxSolution.val {α} {spec : α → Prop} : ApproxSolution spec → α
| exact impl _ => impl
| approx _ _ n impl _ _ => (impl n).val
-- | param impl p h _ _ => (impl (h ▸ p.val)).val!
| weakApprox _ _ _ impl _ => impl.val
-- | check      impl _ => (impl sorry).val!
-- | assumption impl _ => (impl sorry).val!

-- def ApproxSolution.changeParam {α} [Vec α] {spec : α → Prop}
--   (p : Parameter) (key : String)
--   : ApproxSolution spec → ApproxSolution spec
-- | exact impl h => exact impl h
-- | approx _ h n impl key' help => approx _ h n (λ n => (impl n).changeParam p key) key' help
-- | weakApprox _ h n impl help => weakApprox _ h n (impl.changeParam p key) help
-- | param impl p' h key' help =>
--   if key = key' then
--     if p.type = p'.type then
--       param impl p' h key' help
--     else
--       param (λ p' => (impl p').changeParam p key) p' h key' help
--   else
--     param (λ p' => (impl p').changeParam p key) p' h key' help
-- | check impl help => check (λ h => (impl h).changeParam p key) help
-- | assumption impl help => assumption (λ h => (impl h).changeParam p key) help
-- | e => e

----------------------------------------------------------------------

-- def Impl {α} (a : α) := ExactSolution (λ x => x = a)
-- def Impl.val {α} {a : α} (impl : Impl a) : α := ExactSolution.val impl
-- def Impl.exact {a : α} : Impl a := ExactSolution.exact a rfl

-- @[simp]
-- theorem Impl.impl_eq_spec (x : Impl a) : x.val = a :=
-- by
--   cases x; rename_i a' h;
--   simp[ExactSolution.val, val, h]
--   done

def Approx {α} (a : α) := ApproxSolution (λ x => x = a)
def Approx.val {α} {a : α} (approx : Approx a) : α := ApproxSolution.val approx
def Approx.exact {α} {a : α} : Approx a := ApproxSolution.exact a rfl
def Approx.limit {α} {aₙ : ℕ → α} (x : (n : ℕ) → Approx (aₙ n)) (n₀ : ℕ)
  : Approx (limit aₙ) := ApproxSolution.approx (λ n x => x = (aₙ n)) sorry n₀ x "" ""

-- instance {α} (a : α) : Coe (Approx a) α := ⟨λ approx => approx.val⟩
instance {α β : Type _} (f : α → β) : CoeFun (Approx f) (λ _ => α → β) := ⟨λ approx => approx.val⟩

syntax declModifiers "approx " declId bracketedBinder* (":" term)? ":=" term " by " tacticSeq : command

macro_rules
  | `($mods:declModifiers approx $id $params:bracketedBinder* := $body by $rewrites:tacticSeq) =>
    `($mods:declModifiers def $id $params:bracketedBinder* := (by ($rewrites); (apply Approx.exact) : Approx $body))

-- def foo (s : ℝ) := ∇ (λ x : ℝ => s * x)
-- rewrite_by
--   simp[gradient]

-- Add proof and
macro "approx_limit " n0:term : tactic =>
 `(tactic| ((conv => enter [1]; bubble_lim; (tactic => sorry)); apply (Approx.limit _ ($n0:term))))

approx bar (s : ℝ) (n₀ : ℕ) := ∇ (limit λ n => λ x : ℝ => (s + (1:ℝ)/(n:ℝ)) * x)
by
  approx_limit n₀; intro n;
  symdiff