import Lbar.ext_aux1

noncomputable theory

universes v u u'

open opposite category_theory category_theory.limits category_theory.preadditive
open_locale nnreal zero_object

variables (r r' : ℝ≥0)
variables [fact (0 < r)] [fact (r < r')] [fact (r < 1)]

section

open bounded_homotopy_category

variables (BD : breen_deligne.data)
variables (κ κ₂ : ℝ≥0 → ℕ → ℝ≥0)
variables [∀ (c : ℝ≥0), BD.suitable (κ c)] [∀ n, fact (monotone (function.swap κ n))]
variables [∀ (c : ℝ≥0), BD.suitable (κ₂ c)] [∀ n, fact (monotone (function.swap κ₂ n))]
variables (M : ProFiltPseuNormGrpWithTinv₁.{u} r')
variables (V : SemiNormedGroup.{u})

lemma QprimeFP_map (c₁ c₂ : ℝ≥0) (h : c₁ ⟶ c₂) :
  (QprimeFP r' BD κ M).map h = of'_hom ((QprimeFP_int r' BD κ _).map h) := rfl

instance aaahrg (X : Profinite) : seminormed_add_comm_group (locally_constant X V) :=
locally_constant.seminormed_add_comm_group

def V_T_inv (r : ℝ≥0) (V : SemiNormedGroup.{u}) [normed_with_aut r V] : V ⟶ V :=
normed_with_aut.T.{u}.inv

variables [fact (0 < r')] [fact (r' < 1)]

section

variables [complete_space V] [separated_space V]

set_option pp.universes true

lemma final_boss_aux₁ (X : Profinite) (x) :
 ((LCC_iso_Cond_of_top_ab_add_equiv.{u} X V).symm) x =
 (LCC_iso_Cond_of_top_ab_equiv X V).symm x := rfl

lemma final_boss_aux₂ [normed_with_aut r V] (X : Profinite) (x : locally_constant X V) :
((locally_constant.map_hom.{u u u} (V_T_inv r V)).completion)
  (uniform_space.completion.cpkg.{u}.coe x) =
  uniform_space.completion.map (locally_constant.map_hom (V_T_inv r V)) x := rfl

-- should this be a global instance earlier in mathlib?
local attribute [instance]
abstract_completion.uniform_struct

lemma final_boss_aux₃ [normed_with_aut r V] (X : Profinite) :
  continuous.{u u}
  (λ (x : C(X,V)),
  ((locally_constant.map_hom.{u u u} normed_with_aut.T.{u}.inv).completion)
  (((uniform_space.completion.cpkg.{u}.compare_equiv (locally_constant.pkg.{u} X ↥V)).symm) x)) :=
begin
  dsimp [abstract_completion.compare_equiv],
  refine (normed_add_group_hom.continuous _).comp _,
  refine ((locally_constant.pkg X V).uniform_continuous_compare _).continuous,
end

example {β : Type*} [uniform_space β] (a : abstract_completion β) : uniform_space a.space :=
by apply_instance

lemma final_boss_aux₄ [normed_with_aut r V] (X : Profinite) :
@continuous.{u u} _ _ _ (uniform_space.completion.cpkg.uniform_struct.to_topological_space)
  (λ (x : C(X,V)),
  ((locally_constant.pkg X V).compare
    uniform_space.completion.cpkg.{u}
  {to_fun := (V_T_inv r V) ∘ x.to_fun, continuous_to_fun :=
  (normed_with_aut.T.inv.continuous.comp x.2)})) :=
begin
  let e : C(X,V) → C(X,V) := λ e, ⟨(V_T_inv r V) ∘ e,
    (V_T_inv r V).continuous.comp e.2⟩,
  have he : continuous e := continuous_map.continuous_comp
    ((⟨(V_T_inv r V), (V_T_inv r V).continuous⟩ : C(V,V))),
  refine continuous.comp _ he,
  refine ((locally_constant.pkg X V).uniform_continuous_compare _).continuous,
end

lemma final_boss [normed_with_aut r V] (X : Profinite)
  (x : ((Condensed.of_top_ab.presheaf V).obj (op X))) :
((locally_constant.map_hom (V_T_inv r V)).completion)
    (((LCC_iso_Cond_of_top_ab_add_equiv X V).symm) x) =
  ((LCC_iso_Cond_of_top_ab_add_equiv X V).symm)
    {to_fun := (normed_with_aut.T.inv) ∘ x.1, continuous_to_fun :=
      (normed_with_aut.T.inv.continuous.comp x.2)} :=
begin
  rw final_boss_aux₁,
  rw final_boss_aux₁,
  dsimp only [V_T_inv],
  dsimp only [LCC_iso_Cond_of_top_ab_equiv],
  change C(X,V) at x,
  apply abstract_completion.induction_on (locally_constant.pkg.{u} X ↥V) x,
  { apply is_closed_eq,
    { apply final_boss_aux₃ },
    { apply final_boss_aux₄ } },
  clear x,
  intros x,
  change ((locally_constant.map_hom.{u u u} normed_with_aut.T.{u}.inv).completion)
    ((locally_constant.pkg.{u} X ↥V).compare uniform_space.completion.cpkg.{u}
       ((locally_constant.pkg.{u} X ↥V).coe x)) = _,
  --dsimp [abstract_completion.compare_equiv],
  rw abstract_completion.compare_coe,
  erw final_boss_aux₂,
  erw uniform_space.completion.map_coe,
  let q : C(X,V) :=
    {to_fun := (normed_with_aut.T.{u}.inv) ∘ ((locally_constant.pkg.{u} X ↥V).coe x).to_fun,
    continuous_to_fun := _},
  swap,
  { apply continuous.comp,
    apply normed_add_group_hom.continuous,
    refine ((locally_constant.pkg.{u} X ↥V).coe x).2 },
  have hq : q = (locally_constant.pkg X V).coe
    ((locally_constant.map_hom.{u u u} (V_T_inv.{u} r V)) x),
  { ext, refl },

  change _ =
    ((locally_constant.pkg.{u} X ↥V).compare uniform_space.completion.cpkg) q,
  rw hq,

  rw abstract_completion.compare_coe,

  refl,

  apply normed_add_group_hom.uniform_continuous,
end

end

@[reassoc]
lemma massive_aux₁ (X Y : Profinite.{u}) (f : X ⟶ Y) :
  (preadditive_yoneda.{u+1 u+2}.obj V.to_Cond).map (freeCond.{u}.map f).op ≫
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab.{u} V.to_Cond X).hom =
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab.{u} V.to_Cond Y).hom ≫
  V.to_Cond.val.map f.op :=
begin
  erw preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab_natural',
  refl,
end

lemma add_equiv.mk_symm {A B : Type*} [add_comm_group A] [add_comm_group B]
  (f : A →+ B) (g : B →+ A) (h1 h2 h3) :
  (add_equiv.mk f g h1 h2 h3).symm =
  add_equiv.mk g f h2 h1 (by { intros x y, apply h1.injective, rw [h3, h2, h2, h2] }) := rfl

lemma add_equiv.mk_symm_apply {A B : Type*} [add_comm_group A] [add_comm_group B]
  (f : A →+ B) (g : B →+ A) (h1 h2 h3) (x : B) :
  (add_equiv.mk f g h1 h2 h3).symm x = g x := rfl

lemma locally_constant.comap_hom_map_hom {X Y V W : Type*}
  [topological_space X] [compact_space X]
  [topological_space Y] [compact_space Y]
  [seminormed_add_comm_group V] [seminormed_add_comm_group W]
  (f : X → Y) (hf : continuous f) (g : normed_add_group_hom V W) (φ : locally_constant Y V) :
  locally_constant.comap_hom f hf (locally_constant.map_hom g φ) =
  ((locally_constant.map_hom g) ∘ (locally_constant.comap_hom f hf)) φ :=
begin
  dsimp only [locally_constant.comap_hom_apply, locally_constant.map_hom_apply, function.comp],
  rw locally_constant.comap_map,
  exact hf
end

instance (X : Profinite) :
  uniform_space.{u} (locally_constant.{u u} X V) :=
@pseudo_metric_space.to_uniform_space.{u}
  (@locally_constant.{u u} (@coe_sort.{u+2 u+2} Profinite.{u} (Type u) Profinite.has_coe_to_sort.{u} X)
     (@coe_sort.{u+2 u+2} SemiNormedGroup.{u} (Type u) SemiNormedGroup.has_coe_to_sort.{u} V)
     (Top.topological_space.{u} X.to_CompHaus.to_Top))
  (@seminormed_add_comm_group.to_pseudo_metric_space.{u}
     (@locally_constant.{u u} (@coe_sort.{u+2 u+2} Profinite.{u} (Type u) Profinite.has_coe_to_sort.{u} X)
        (@coe_sort.{u+2 u+2} SemiNormedGroup.{u} (Type u) SemiNormedGroup.has_coe_to_sort.{u} V)
        (Top.topological_space.{u} X.to_CompHaus.to_Top))
     locally_constant.seminormed_add_comm_group)

instance (X : Profinite) : topological_space ↥(V.to_Cond.val.obj (op X)) :=
@ulift.topological_space _ (continuous_map.compact_open.{u u})

variables [complete_space V] [separated_space V]

lemma to_Cond_val_map_apply (X Y : Profinite.{u}) (f : X ⟶ Y) (x) :
  V.to_Cond.val.map f.op x = ⟨continuous_map.comp_right_continuous_map V f x.down⟩ :=
rfl

lemma to_Cond_val_map (X Y : Profinite.{u}) (f : X ⟶ Y) :
  ⇑(V.to_Cond.val.map f.op) =
  (λ x, ⟨continuous_map.comp_right_continuous_map V f x.down⟩ : ↥(V.to_Cond.val.obj (op Y)) → ↥(V.to_Cond.val.obj (op X))) :=
by { ext x, rw to_Cond_val_map_apply }

lemma massive_aux₂ (X Y : Profinite.{u}) (f : X ⟶ Y) (x : (V.to_Cond.val.obj (op.{u+2} Y))) :
  uniform_space.completion.map.{u u} (locally_constant.comap_hom.{u u u} f f.continuous)
    ((locally_constant.pkg.{u} Y ↥V).compare uniform_space.completion.cpkg.{u} x.down) =
  ((locally_constant.pkg.{u} X ↥V).compare uniform_space.completion.cpkg.{u})
    ((V.to_Cond.val.map f.op) x).down :=
begin
  cases x,
  apply abstract_completion.induction_on (locally_constant.pkg.{u} Y V) x,
  { apply is_closed_eq,
    { apply uniform_space.completion.continuous_map.comp,
      apply (abstract_completion.uniform_continuous_compare _ _).continuous },
    { apply (abstract_completion.uniform_continuous_compare _ _).continuous.comp,
      let φ : C(Y, V) → C(X, V) := _, change continuous φ,
      let ψ := V.to_Cond.val.map f.op, have hψ : φ = ulift.down ∘ ψ ∘ ulift.up := rfl,
      rw hψ, clear hψ,
      refine continuous_induced_dom.comp _,
      refine continuous.comp _ continuous_ulift_up,
      rw [to_Cond_val_map],
      refine continuous.comp _ _, { exact continuous_ulift_up },
      dsimp only [Condensed.of_top_ab, Condensed.of_top_ab.presheaf],
      exact (map_continuous (continuous_map.comp_right_continuous_map ↥V f)).comp continuous_induced_dom, } },
  { intro φ,
    dsimp only,
    simp only [abstract_completion.compare_coe, to_Cond_val_map_apply,
      uniform_space.completion.map],
    rw [abstract_completion.map_coe],
    swap,
    { letI : seminormed_add_comm_group (locally_constant ↥(X.to_CompHaus.to_Top) ↥V),
      { exact locally_constant.seminormed_add_comm_group },
      letI : seminormed_add_comm_group (locally_constant ↥(Y.to_CompHaus.to_Top) ↥V),
      { exact locally_constant.seminormed_add_comm_group },
      exact normed_add_group_hom.uniform_continuous _, },
    have : (continuous_map.comp_right_continuous_map ↥V f) ((locally_constant.pkg Y V).coe φ) =
      (locally_constant.pkg X V).coe _ := _,
    rw [this, abstract_completion.compare_coe],
    ext1,
    erw [locally_constant.coe_comap],
    refl,
    exact f.continuous },
end

lemma massive_aux (X Y : Profinite.{u}) (f : X ⟶ Y) :
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab.{u} V.to_Cond Y).hom ≫
      Ab.ulift.{u+1 u}.map ((LCC_iso_Cond_of_top_ab.{u} V).inv.app (op.{u+2} Y)) ≫
        (ExtQprime_iso_aux_system_obj_aux'.{u} V Y).hom ≫
          (forget₂.{u+2 u+2 u+1 u+1 u+1} SemiNormedGroup.{u+1} Ab.{u+1}).map
            ((FreeAb.eval.{u+1 u+2} SemiNormedGroup.{u+1}ᵒᵖ).map
              ((CLC.{u+1 u} (SemiNormedGroup.ulift.{u+1 u}.obj V)).right_op.map_FreeAb.map
                  ((FreeAb.of_functor.{u+1 u} Profinite.{u}).map f))).unop =
    (preadditive_yoneda.{u+1 u+2}.obj V.to_Cond).map
        ((FreeAb.eval.{u+1 u+2} (Condensed.{u u+1 u+2} Ab.{u+1})).map
          (freeCond.{u}.map_FreeAb.map ((FreeAb.of_functor.{u+1 u} Profinite.{u}).map f))).op ≫
      (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab.{u} V.to_Cond X).hom ≫
        Ab.ulift.{u+1 u}.map ((LCC_iso_Cond_of_top_ab.{u} V).inv.app (op.{u+2} X)) ≫
          (ExtQprime_iso_aux_system_obj_aux'.{u} V X).hom :=
begin
  dsimp only [functor.map_FreeAb, FreeAb.of_functor, FreeAb.eval],
  simp only [free_abelian_group.map_of_apply, free_abelian_group.lift.of, id],
  dsimp only [functor.right_op_map, quiver.hom.op_unop, quiver.hom.unop_op],
  rw massive_aux₁_assoc, congr' 1,
  ext1 x, simp only [comp_apply],
  dsimp only [ExtQprime_iso_aux_system_obj_aux', LCC_iso_Cond_of_top_ab,
    LCC_iso_Cond_of_top_ab_add_equiv, LCC_iso_Cond_of_top_ab_equiv, CLC, LC, functor.comp_map,
    Condensed.of_top_ab],
  simp only [add_equiv.to_fun_eq_coe, normed_add_group_hom.completion_coe_to_fun,
    add_equiv.to_AddCommGroup_iso_hom, add_equiv.coe_to_add_monoid_hom, add_equiv.trans_apply,
    add_equiv.ulift_apply, equiv.to_fun_as_coe, equiv.ulift_apply_2,
    Ab.ulift_map_apply_down, add_equiv.coe_mk, nat_iso.of_components_inv_app,
    add_equiv.to_AddCommGroup_iso, add_equiv.mk_symm,
    SemiNormedGroup.forget₂_Ab_map, normed_add_group_hom.coe_to_add_monoid_hom],
  let F := SemiNormedGroup.Completion.{u+1}.map ((SemiNormedGroup.LocallyConstant.{u+1 u}.obj
    (SemiNormedGroup.ulift.{u+1 u}.obj V)).map f.op),
  let g := _,
  let Z := _,
  change F ((uniform_space.completion.map g) Z) = _,
  change (F ∘ uniform_space.completion.map g) Z = _,
  erw [uniform_space.completion.map_comp],
  rotate,
  { apply normed_add_group_hom.uniform_continuous, },
  { apply normed_add_group_hom.uniform_continuous, },
  conv_lhs
  { dsimp only [function.comp, normed_add_group_hom.coe_to_add_monoid_hom, g,
      SemiNormedGroup.LocallyConstant_obj_map], },
  simp only [locally_constant.comap_hom_map_hom],
  letI : uniform_space.{u} (locally_constant.{u u} ↥(unop.{u+2} (op.{u+2} X)) ↥V) := _,
  erw [← uniform_space.completion.map_comp],
  rotate,
  { apply normed_add_group_hom.uniform_continuous, },
  { apply normed_add_group_hom.uniform_continuous, },
  dsimp only [function.comp, Z, quiver.hom.unop_op],
  congr' 1, clear Z g F,
  exact massive_aux₂ V X Y f x,
end

lemma massive (X Y : FreeAb Profinite.{u}) (f : X ⟶ Y) :
  (((preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab.{u} V.to_Cond Y.as).hom ≫
    (Condensed_Ab_to_presheaf.{u}.map (Condensed_LCC_iso_of_top_ab.{u} V).inv).app (op.{u+2} Y.as) ≫
    (ExtQprime_iso_aux_system_obj_aux'.{u} V Y.as).hom) ≫
    (𝟙 _)) ≫
    (forget₂.{u+2 u+2 u+1 u+1 u+1} SemiNormedGroup.{u+1} Ab.{u+1}).map
      (((CLC.{u+1 u} (SemiNormedGroup.ulift.{u+1 u}.obj V)).right_op.map_FreeAb ⋙
        FreeAb.eval.{u+1 u+2} SemiNormedGroup.{u+1}ᵒᵖ).map f).unop =
  (preadditive_yoneda.{u+1 u+2}.obj V.to_Cond).map
    ((freeCond.{u}.map_FreeAb ⋙ FreeAb.eval.{u+1 u+2} (Condensed.{u u+1 u+2} Ab.{u+1})).map f).op ≫
    ((preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab.{u} V.to_Cond X.as).hom ≫
    (Condensed_Ab_to_presheaf.{u}.map (Condensed_LCC_iso_of_top_ab.{u} V).inv).app (op.{u+2} X.as) ≫
    (ExtQprime_iso_aux_system_obj_aux'.{u} V X.as).hom) ≫  𝟙 _ :=
begin
  simp only [Condensed_Ab_to_presheaf_map, category.assoc, category.comp_id, functor.comp_map],
  dsimp only [Condensed_LCC_iso_of_top_ab, Sheaf.iso.mk_inv_val,
    iso_whisker_right_inv, whisker_right_app],
  apply free_abelian_group.induction_on f; clear f,
  { simp only [functor.map_zero, unop_zero, comp_zero, op_zero, zero_comp], },
  { apply massive_aux },
  { intros f hf,
    simp only [functor.map_neg, unop_neg, op_neg, comp_neg, neg_comp, hf], },
  { intros f g hf hg,
    simp only [functor.map_add, unop_add, op_add, comp_add, add_comp, hf, hg], },
end

lemma hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_c (c₁ c₂) (h : c₁ ⟶ c₂) :
  (hom_complex_QprimeFP_nat_iso_aux_system r' BD κ M V c₂).hom ≫
  (category_theory.functor.map _ h.op) =
  (category_theory.functor.map _
  begin
    refine homological_complex.op_functor.map (quiver.hom.op _),
    refine category_theory.functor.map _ h,
  end) ≫ (hom_complex_QprimeFP_nat_iso_aux_system r' BD κ M V c₁).hom :=
begin
  ext n : 2,
  have aux : ∀ (n : ℕ), (monotone.{0 0} (function.swap.{1 1 1} κ n)),
  { intro n, exact fact.out _ },
  haveI : fact (κ c₁ n ≤ κ c₂ n) := ⟨aux n h.le⟩,
  have := massive V
    (breen_deligne.FPsystem.X.{u} r' BD ⟨M⟩ κ c₁ n)
    (breen_deligne.FPsystem.X.{u} r' BD ⟨M⟩ κ c₂ n)
    ((breen_deligne.FP2.res.{u} r' _ _ _).app ⟨M⟩),
  exact this
end

lemma hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_κ (c : (ℝ≥0))
  [∀ (c : ℝ≥0) (n : ℕ), fact (κ₂ c n ≤ κ c n)] :
  (hom_complex_QprimeFP_nat_iso_aux_system r' BD κ M V c).hom ≫
  (whisker_right (aux_system.res _ _ _ _ _ _) _).app _ =
  begin
    refine category_theory.functor.map _ _,
    refine homological_complex.op_functor.map (quiver.hom.op _),
    refine (QprimeFP_nat.ι BD κ₂ κ M).app _,
  end ≫ (hom_complex_QprimeFP_nat_iso_aux_system r' BD κ₂ M V c).hom :=
begin
  ext n : 2,
  have := massive V
    (breen_deligne.FPsystem.X.{u} r' BD ⟨M⟩ κ₂ c n)
    (breen_deligne.FPsystem.X.{u} r' BD ⟨M⟩ κ c n)
    ((breen_deligne.FP2.res.{u} r' _ _ _).app ⟨M⟩),
  exact this
end

lemma hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_Tinv (c : ℝ≥0)
  [∀ (c : ℝ≥0) (n : ℕ), fact (κ₂ c n ≤ r' * κ c n)] :
  (hom_complex_QprimeFP_nat_iso_aux_system r' BD κ M V c).hom ≫
  (whisker_right
    (aux_system.Tinv _ _ _ _ _ _) _).app _ =
  begin
    refine category_theory.functor.map _ _,
    refine homological_complex.op_functor.map (quiver.hom.op _),
    refine (QprimeFP_nat.Tinv BD κ₂ κ M).app _,
  end
  ≫ (hom_complex_QprimeFP_nat_iso_aux_system r' BD κ₂ M V c).hom :=
begin
  ext n : 2,
  have := massive V
    (breen_deligne.FPsystem.X.{u} r' BD ⟨M⟩ κ₂ c n)
    (breen_deligne.FPsystem.X.{u} r' BD ⟨M⟩ κ c n)
    (((breen_deligne.FPsystem.Tinv.{u} r' BD ⟨M⟩ κ₂ κ).app c).f n),
  exact this,
end

def to_Cond_T_inv (r : ℝ≥0) (V : SemiNormedGroup.{u}) [normed_with_aut r V] : V.to_Cond ⟶ V.to_Cond :=
(Condensed.of_top_ab_map.{u} (normed_add_group_hom.to_add_monoid_hom.{u u} normed_with_aut.T.{u}.inv)
  (normed_add_group_hom.continuous _))

lemma uniform_space.completion.map_comp'
  {α β γ : Type*} [uniform_space α] [uniform_space β] [uniform_space γ]
  {g : β → γ} {f : α → β}
  (hg : uniform_continuous g) (hf : uniform_continuous f) (x) :
  uniform_space.completion.map g (uniform_space.completion.map f x) =
  uniform_space.completion.map (g ∘ f) x :=
begin
  rw [← uniform_space.completion.map_comp hg hf],
end

lemma hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_T_inv_aux_helper
  (r : ℝ≥0) (V : SemiNormedGroup.{u}) [normed_with_aut r V] [complete_space V] [separated_space V]
  (X : Profinite.{u}) :
  (ExtQprime_iso_aux_system_obj_aux' V X).hom ≫
  category_theory.functor.map _
  (SemiNormedGroup.Completion.map
  (nat_trans.app
    (SemiNormedGroup.LocallyConstant.map
    (category_theory.functor.map _ $ V_T_inv _ _)) _)) =
  Ab.ulift.map
  (category_theory.functor.map _ $
  category_theory.functor.map _ $
  nat_trans.app
  (SemiNormedGroup.LocallyConstant.map $ V_T_inv _ _) _) ≫
  (ExtQprime_iso_aux_system_obj_aux' V X).hom
   :=
begin
  ext1 ⟨f⟩,
  simp only [comp_apply],
  dsimp only [ExtQprime_iso_aux_system_obj_aux', add_equiv.to_AddCommGroup_iso,
    add_equiv.coe_to_add_monoid_hom, add_equiv.trans_apply],
  simp only [add_equiv.to_fun_eq_coe, SemiNormedGroup.LocallyConstant_map_app, SemiNormedGroup.Completion_map,
  normed_add_group_hom.completion_coe_to_fun, add_equiv.ulift_apply, equiv.to_fun_as_coe, equiv.ulift_apply_2,
  add_equiv.coe_mk, Ab.ulift_map_apply_down, SemiNormedGroup.forget₂_Ab_map,
    normed_add_group_hom.coe_to_add_monoid_hom],
  rw uniform_space.completion.map_comp',
  rotate,
  { apply normed_add_group_hom.uniform_continuous },
  { apply normed_add_group_hom.uniform_continuous },
  rw uniform_space.completion.map_comp',
  rotate,
  { apply normed_add_group_hom.uniform_continuous },
  { apply normed_add_group_hom.uniform_continuous },
  refl
end

lemma another_aux_lemma [normed_with_aut r V] (X : Profinite) :
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab V.to_Cond X).hom
  ≫ (Condensed_Ab_to_presheaf.map_iso (Condensed_LCC_iso_of_top_ab V)).inv.app (op X)
  ≫
  begin
    refine nat_trans.app _ _,
    refine Condensed_Ab_to_presheaf.map _,
    refine Sheaf.hom.mk _,
    dsimp [Condensed_LCC],
    refine whisker_right _ _,
    refine whisker_right _ _,
    refine SemiNormedGroup.LCC.map _,
    exact V_T_inv r V,
  end =
  (preadditive_yoneda.map
    (to_Cond_T_inv r V)).app _ ≫
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab V.to_Cond X).hom ≫
  (Condensed_Ab_to_presheaf.map_iso (Condensed_LCC_iso_of_top_ab V)).inv.app _ :=
begin
  have := preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab_natural
    (to_Cond_T_inv r V) X,
  erw ← reassoc_of this,
  congr' 1,
  dsimp only [Condensed_Ab_to_presheaf, functor.map_iso_inv, nat_iso.app_inv,
    Sheaf_to_presheaf_map, id, whisker_right_app, SemiNormedGroup.LCC,
    curry, uncurry, curry_obj, functor.comp_map],
  simp only [category_theory.functor.map_id, category.comp_id],
  rw ← nat_trans.comp_app,
  rw ← Sheaf.hom.comp_val, -- how to make those commute?
  ext ⟨x⟩,
  dsimp only [Condensed_LCC_iso_of_top_ab, Sheaf.iso.mk, iso_whisker_right, to_Cond_T_inv,
    Ab.ulift],
  simp only [comp_apply],
  dsimp [Condensed.of_top_ab_map],
  simp only [comp_apply],
  dsimp [LCC_iso_Cond_of_top_ab, forget₂, has_forget₂.forget₂],
  apply final_boss,
end

lemma hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_T_inv_aux (c : ℝ≥0)
  [normed_with_aut r V] (n : ℕ) (t) :
((forget₂.{u+2 u+2 u+1 u+1 u+1} SemiNormedGroup.{u+1} Ab.{u+1}).map
       (((aux_system.T_inv.{u u+1} r r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1 u}.obj V) κ).app
           (op.{1} c)).f n))
    ((((ExtQprime_iso_aux_system_obj_aux.{u} V).hom.app
           (((breen_deligne.FPsystem.{u} r' BD ⟨M⟩ κ).obj c).X n)).unop) t) =
  (((ExtQprime_iso_aux_system_obj_aux.{u} V).hom.app
        (((breen_deligne.FPsystem.{u} r' BD ⟨M⟩ κ).obj c).X n)).unop)
        (t ≫ to_Cond_T_inv.{u} r V) :=
begin
  /-
  Note: This should reduce to some calcuation with the sheafification adjunction,
  as well as something about completion/ulift compatibiity.
  If we can reduce this to such statements, we will be in pretty good shape.
  -/
  /- This code block is pretty slow.
  dsimp [ExtQprime_iso_aux_system_obj_aux, ExtQprime_iso_aux_system_obj_aux'],
  simp only [comp_apply],
  dsimp [forget₂, has_forget₂.forget₂, aux_system.T_inv,
    Condensed_LCC_iso_of_top_ab, LCC_iso_Cond_of_top_ab],
  rw nat_iso.of_components_inv_app,
  dsimp only [unop_op],
  -/
  dsimp only [forget₂, has_forget₂.forget₂, ExtQprime_iso_aux_system_obj_aux,
    nat_iso.of_components_hom_app, id, iso.op, iso.trans_hom, iso.symm,
    nat_iso.app_inv, aux_system.T_inv, quiver.hom.op_unop, quiver.hom.unop_op,
    homological_complex.unop],
  simp only [comp_apply],
  let X : Profinite := (((breen_deligne.FPsystem r' BD ⟨M⟩ κ).obj c).X n).as,
  have := preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab_natural
    (to_Cond_T_inv r V) X,
  apply_fun (λ e, e t) at this,
  erw this, clear this,
  simp only [comp_apply],
  dsimp only [SemiNormedGroup.LocallyConstant],
  have := hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_T_inv_aux_helper r V X,
  let s := ((Condensed_Ab_to_presheaf.map_iso (Condensed_LCC_iso_of_top_ab V)).inv.app (op X))
    (((preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab V.to_Cond X).hom)
    (t)),
  apply_fun (λ e, e s) at this,
  erw this, clear this,
  simp only [comp_apply],
  congr' 1, dsimp only [s],
  simp only [← comp_apply],
  congr' 1,
  simp only [category.assoc],
  erw ← another_aux_lemma r V X,
  congr' 2,
  ext1 ⟨x⟩, dsimp only [Ab.ulift, Condensed_Ab_to_presheaf, whisker_right_app,
    Sheaf_to_presheaf],
  ext1,
  dsimp,
  congr' 2,
  dsimp only [SemiNormedGroup.LCC, curry, curry_obj, functor.comp_map, uncurry],
  simp only [category_theory.functor.map_id, category.comp_id],
  refl,
end

lemma hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_T_inv (c : ℝ≥0)
  [normed_with_aut r V] :
(hom_complex_QprimeFP_nat_iso_aux_system.{u} r' BD κ M V c).hom ≫
  ((forget₂.{u+2 u+2 u+1 u+1 u+1} SemiNormedGroup.{u+1} Ab.{u+1}).map_homological_complex
       (complex_shape.up.{0} ℕ)).map (nat_trans.app
        ((aux_system.T_inv.{u u+1} r r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1 u}.obj V) κ)) _) =
  begin
    let e := preadditive_yoneda.map (to_Cond_T_inv r V),
    let e' := nat_trans.map_homological_complex e (complex_shape.down ℕ).symm,
    let Q := ((QprimeFP_nat r' BD κ M).obj c).op,
    exact e'.app Q,
  end ≫
  (hom_complex_QprimeFP_nat_iso_aux_system.{u} r' BD κ M V (c)).hom :=
begin
  ext n : 2, ext1 t,
  dsimp [hom_complex_QprimeFP_nat_iso_aux_system],
  simp only [comp_apply],
  dsimp [nat_iso.map_homological_complex, forget₂_unop],
  erw id_apply, erw id_apply,
  erw [functor.map_homological_complex_map_f],
  apply hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_T_inv_aux,
end

namespace ExtQprime_iso_aux_system_obj_naturality_setup

/-
lemma aux₁ (c₁ c₂ : ℝ≥0) (h : c₁ ⟶ c₂) :
homological_complex.unop_functor.{u+2 u+1 0}.map
    (((preadditive_yoneda_obj.{u+1 u+2} V.to_Cond ⋙
         forget₂.{u+2 u+2 u+1 u+1 u+1} (Module.{u+1 u+1} (End.{u+1 u+2} V.to_Cond))
           AddCommGroup.{u+1}).right_op.map_homological_complex
        (complex_shape.up.{0} ℤ)).map
       ((homological_complex.embed.{0 0 u+2 u+1} complex_shape.embedding.nat_down_int_up).map
          ((QprimeFP_nat.{u} r' BD κ M).map h))).op ≫
  homological_complex.unop_functor.{u+2 u+1 0}.map
      ((map_homological_complex_embed.{u+2 u+2 u+1 u+1}
          (preadditive_yoneda_obj.{u+1 u+2} V.to_Cond ⋙
             forget₂.{u+2 u+2 u+1 u+1 u+1} (Module.{u+1 u+1} (End.{u+1 u+2} V.to_Cond))
               AddCommGroup.{u+1}).right_op).inv.app
         ((QprimeFP_nat.{u} r' BD κ M).obj c₁)).op ≫
    embed_unop.{u+2 u+1}.hom.app
      (op.{u+3}
         (((preadditive_yoneda_obj.{u+1 u+2} V.to_Cond ⋙
              forget₂.{u+2 u+2 u+1 u+1 u+1} (Module.{u+1 u+1} (End.{u+1 u+2} V.to_Cond))
                Ab.{u+1}).right_op.map_homological_complex
             (complex_shape.down.{0} ℕ)).obj
            ((QprimeFP_nat.{u} r' BD κ M).obj c₁))) =
  begin
    dsimp,
    let e := (QprimeFP_nat r' BD κ M).map h,
    let e₁ := ((preadditive_yoneda_obj.{u+1 u+2} V.to_Cond ⋙
      forget₂.{u+2 u+2 u+1 u+1 u+1} (Module.{u+1 u+1} (End.{u+1 u+2} V.to_Cond))
      Ab.{u+1}).right_op.map_homological_complex
      (complex_shape.down.{0} ℕ)).map e,
    let e₂ := homological_complex.unop_functor.map e₁.op,
    refine _ ≫
      (homological_complex.embed.{0 0 u+2 u+1} complex_shape.embedding.nat_up_int_down).map
      e₂,
    refine homological_complex.unop_functor.{u+2 u+1 0}.map
    ((map_homological_complex_embed.{u+2 u+2 u+1 u+1}
        (preadditive_yoneda_obj.{u+1 u+2} V.to_Cond ⋙
           forget₂.{u+2 u+2 u+1 u+1 u+1} (Module.{u+1 u+1} (End.{u+1 u+2} V.to_Cond))
             AddCommGroup.{u+1}).right_op).inv.app
       ((QprimeFP_nat.{u} r' BD κ M).obj c₂)).op ≫
    embed_unop.{u+2 u+1}.hom.app
    (op.{u+3}
       (((preadditive_yoneda_obj.{u+1 u+2} V.to_Cond ⋙
            forget₂.{u+2 u+2 u+1 u+1 u+1} (Module.{u+1 u+1} (End.{u+1 u+2} V.to_Cond))
              Ab.{u+1}).right_op.map_homological_complex
           (complex_shape.down.{0} ℕ)).obj
          ((QprimeFP_nat.{u} r' BD κ M).obj c₂)))
  end := admit

def F : ℝ≥0 ⥤
  (homological_complex.{u+1 u+2 0} AddCommGroup.{u+1} (complex_shape.down.{0} ℕ).symm)ᵒᵖ :=
QprimeFP_nat.{u} r' BD κ M ⋙
  (preadditive_yoneda_obj.{u+1 u+2} V.to_Cond ⋙
     forget₂.{u+2 u+2 u+1 u+1 u+1} (Module.{u+1 u+1} (End.{u+1 u+2} V.to_Cond))
       AddCommGroup.{u+1}).right_op.map_homological_complex
    (complex_shape.down.{0} ℕ) ⋙ homological_complex.unop_functor.right_op

@[reassoc]
lemma naturality_helper {c₁ c₂ : ℝ≥0} (h : c₁ ⟶ c₂) (n : ℕ) (w1 w2) :
  (homological_complex.homology_embed_nat_iso.{0 0 u+2 u+1} Ab.{u+1} complex_shape.embedding.nat_up_int_down
   nat_up_int_down_c_iff n (-↑n) w1).hom.app
    (((preadditive_yoneda.{u+1 u+2}.obj
    V.to_Cond).right_op.map_homological_complex (complex_shape.down.{0} ℕ)).obj
     ((QprimeFP_nat.{u} r' BD κ M).obj c₂)).unop ≫
     (homology_functor _ _ _).map
     (homological_complex.map_unop _ _ $
     category_theory.functor.map _ $ category_theory.functor.map _ h) =
  category_theory.functor.map _
  (homological_complex.map_unop _ _ $
    category_theory.functor.map _ $ category_theory.functor.map _ h) ≫
    (homological_complex.homology_embed_nat_iso.{0 0 u+2 u+1} Ab.{u+1} complex_shape.embedding.nat_up_int_down
  nat_up_int_down_c_iff n (-↑n) w2).hom.app
    (((preadditive_yoneda.{u+1 u+2}.obj
    V.to_Cond).right_op.map_homological_complex (complex_shape.down.{0} ℕ)).obj
    ((QprimeFP_nat.{u} r' BD κ M).obj c₁)).unop :=
admit
-/

lemma aux₁ (c₁ c₂ : ℝ≥0) (h : c₁ ⟶ c₂) (n : ℕ) :
  (homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℕ) n).map
  (hom_complex_QprimeFP_nat_iso_aux_system.{u} r' BD κ M V c₂).hom ≫
  (homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℕ) n).map
  ((aux_system.{u u+1} r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1 u}.obj V) κ).to_Ab.map h.op) =
  (homology_functor _ _ _).map
  (category_theory.functor.map _
      (homological_complex.op_functor.map ((QprimeFP_nat r' BD κ M).map h).op)) ≫
  (homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℕ) n).map
  (hom_complex_QprimeFP_nat_iso_aux_system.{u} r' BD κ M V c₁).hom :=
begin
  rw [← functor.map_comp, ← functor.map_comp],
  congr' 1,
  erw ← hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_c,
end

lemma aux₂ (c₁ c₂ : ℝ≥0) (h : c₁ ⟶ c₂) (n : ℕ) :
  (homological_complex.homology_embed_nat_iso.{0 0 u+2 u+1} Ab.{u+1}
    complex_shape.embedding.nat_up_int_down n (-↑n) (by { cases n; refl})).hom.app
    (hom_complex_nat.{u} ((QprimeFP_nat.{u} r' BD κ M).obj c₂) V.to_Cond) ≫
    (homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℕ) n).map
    (((preadditive_yoneda.{u+1 u+2}.obj V.to_Cond).map_homological_complex
    (complex_shape.down.{0} ℕ).symm).map (homological_complex.op_functor.{u+2 u+1 0}.map
    ((QprimeFP_nat.{u} r' BD κ M).map h).op)) =
  (homological_complex.embed.{0 0 u+2 u+1} complex_shape.embedding.nat_up_int_down ⋙
  homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.down.{0} ℤ) (-↑n)).map
  (category_theory.functor.map _
      (homological_complex.op_functor.map ((QprimeFP_nat r' BD κ M).map h).op)) ≫
  (homological_complex.homology_embed_nat_iso.{0 0 u+2 u+1} Ab.{u+1}
  complex_shape.embedding.nat_up_int_down n (-↑n) (by { cases n; refl})).hom.app
  (hom_complex_nat.{u} ((QprimeFP_nat.{u} r' BD κ M).obj c₁) V.to_Cond) :=
begin
  erw nat_trans.naturality,
end

lemma aux₃ (c₁ c₂ : ℝ≥0) (h : c₁ ⟶ c₂) (n : ℕ) :
  (homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℤ).symm (-↑n)).map
  (embed_hom_complex_nat_iso.{u} ((QprimeFP_nat.{u} r' BD κ M).obj c₂) V.to_Cond).hom ≫
  (homological_complex.embed.{0 0 u+2 u+1} complex_shape.embedding.nat_up_int_down ⋙
  homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.down.{0} ℤ) (-↑n)).map
  (((preadditive_yoneda.{u+1 u+2}.obj V.to_Cond).map_homological_complex
  (complex_shape.down.{0} ℕ).symm).map (homological_complex.op_functor.{u+2 u+1 0}.map
  ((QprimeFP_nat.{u} r' BD κ M).map h).op))
  =
  ((homology_functor.{u+1 u+2 0} AddCommGroup.{u+1}
  (complex_shape.up.{0} ℤ).symm (-↑n)).op.map
  (homological_complex.unop_functor.{u+2 u+1 0}.right_op.map
  (((preadditive_yoneda.{u+1 u+2}.obj V.to_Cond).right_op.map_homological_complex
  (complex_shape.up.{0} ℤ)).map ((QprimeFP_int.{u} r' BD κ M).map h)))).unop ≫
  (homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℤ).symm (-↑n)).map
  (embed_hom_complex_nat_iso.{u} ((QprimeFP_nat.{u} r' BD κ M).obj c₁) V.to_Cond).hom
  :=
begin
  dsimp only [functor.op_map, functor.comp_map],
  erw [← functor.map_comp],
  erw [← functor.map_comp],
  congr' 1,
  ext ((_ | k) | k ) : 2,
  { refine (category.id_comp _).trans (category.comp_id _).symm },
  { apply is_zero.eq_of_tgt,
    exact is_zero_zero _ },
  { refine (category.id_comp _).trans (category.comp_id _).symm },
end
/-
lemma naturality_helper {c₂ : ℝ≥0} (n : ℕ) :
  (homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℤ).symm (-↑n)).map
  (embed_hom_complex_nat_iso.{u} ((QprimeFP_nat.{u} r' BD κ M).obj c₂) V.to_Cond).hom ≫
  (homological_complex.homology_embed_nat_iso.{0 0 u+2 u+1} Ab.{u+1}
  complex_shape.embedding.nat_up_int_down nat_up_int_down_c_iff n (-↑n) (by { cases n; refl})).hom.app
  (hom_complex_nat.{u} ((QprimeFP_nat.{u} r' BD κ M).obj c₂) V.to_Cond) =
  _
-/

end ExtQprime_iso_aux_system_obj_naturality_setup

lemma QprimeFP_acyclic (c) (k i : ℤ) (hi : 0 < i) :
  is_zero (((Ext' i).obj (op (((QprimeFP_int.{u} r' BD κ M).obj c).X k))).obj V.to_Cond) :=
begin
  rcases k with ((_|k)|k),
  { apply free_acyclic, exact hi },
  { rw [← functor.flip_obj_obj], refine functor.map_is_zero _ _, refine (is_zero_zero _).op, },
  { apply free_acyclic, exact hi },
end

lemma ExtQprime_iso_aux_system_obj_natrality (c₁ c₂ : ℝ≥0) (h : c₁ ⟶ c₂) (n : ℕ) :
  (ExtQprime_iso_aux_system_obj r' BD κ M V c₂ n).hom ≫
  (homology_functor _ _ _).map
  ((system_of_complexes.to_Ab _).map h.op)  =
  ((Ext n).map ((QprimeFP r' BD κ _).map h).op).app _ ≫
  (ExtQprime_iso_aux_system_obj r' BD κ M V c₁ n).hom :=
begin
  dsimp only [ExtQprime_iso_aux_system_obj,
    iso.trans_hom, id, functor.map_iso_hom],
  haveI : ((homotopy_category.quotient.{u+1 u+2 0}
    (Condensed.{u u+1 u+2} Ab.{u+1}) (complex_shape.up.{0} ℤ)).obj
     ((QprimeFP_int.{u} r' BD κ M).obj c₁)).is_bounded_above :=
    chain_complex.is_bounded_above _,
  haveI : ((homotopy_category.quotient.{u+1 u+2 0}
    (Condensed.{u u+1 u+2} Ab.{u+1}) (complex_shape.up.{0} ℤ)).obj
     ((QprimeFP_int.{u} r' BD κ M).obj c₂)).is_bounded_above :=
    chain_complex.is_bounded_above _,
  have := Ext_compute_with_acyclic_naturality
    ((QprimeFP_int.{u} r' BD κ M).obj c₁)
    ((QprimeFP_int.{u} r' BD κ M).obj c₂)
    V.to_Cond _ _
    ((QprimeFP_int.{u} r' BD κ M).map h) n,
  rotate,
  { intros k i hi, apply QprimeFP_acyclic, exact hi },
  { intros k i hi, apply QprimeFP_acyclic, exact hi },
  dsimp only [functor.comp_map] at this,
  erw reassoc_of this, clear this,
  simp only [category.assoc, nat_iso.app_hom],
  congr' 1,
  rw ExtQprime_iso_aux_system_obj_naturality_setup.aux₁ r' BD κ M V c₁ c₂ h n,
  simp only [← category.assoc], congr' 1,
  simp only [category.assoc],
  rw ExtQprime_iso_aux_system_obj_naturality_setup.aux₂ r' BD κ M V c₁ c₂ h n,
  simp only [← category.assoc], congr' 1,

  exact ExtQprime_iso_aux_system_obj_naturality_setup.aux₃ r' BD κ M V c₁ c₂ h n,

  --- OLD PROOF FROM HERE
  --have := ExtQprime_iso_aux_system_obj_naturality_setup.naturality_helper r' BD κ
  --  M V h n _ _,
  --simp only [category.assoc, functor.map_comp],
  --slice_rhs 3 4
  --{ erw ← this },

  /-
  dsimp only [QprimeFP_int],
  congr' 1,
  dsimp only [nat_iso.app_hom],
  simp only [functor.map_comp, functor.comp_map, nat_trans.naturality,
    nat_trans.naturality_assoc],
  dsimp only [functor.op_map, quiver.hom.unop_op, functor.right_op_map],
  simp only [← functor.map_comp, ← functor.map_comp_assoc, category.assoc],
  dsimp [-homology_functor_map],
  rw ExtQprime_iso_aux_system_obj_naturality_setup.aux₁,
  dsimp [-homology_functor_map],
  simp only [functor.map_comp, functor.map_comp_assoc,
    category.assoc, nat_trans.naturality_assoc],
  congr' 2,
  dsimp [-homology_functor_map],
  dsimp only [← functor.comp_map, ← functor.comp_obj],
  --erw nat_trans.naturality_assoc,
  --refine congr_arg2 _ _ (congr_arg2 _ rfl _),

  --congr' 1,
  --refl,
  admit

  -/
end

def ExtQprime_iso_aux_system (n : ℕ) :
  (QprimeFP r' BD κ M).op ⋙ (Ext n).flip.obj ((single _ 0).obj V.to_Cond) ≅
  aux_system r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1}.obj V) κ ⋙
    (forget₂ _ Ab).map_homological_complex _ ⋙ homology_functor _ _ n :=
nat_iso.of_components (λ c, ExtQprime_iso_aux_system_obj r' BD κ M V (unop c) n)
begin
  intros c₁ c₂ h,
  dsimp [-homology_functor_map],
  rw ← ExtQprime_iso_aux_system_obj_natrality,
  refl,
end

/-- The `Tinv` map induced by `M` -/
def ExtQprime.Tinv
  [∀ c n, fact (κ₂ c n ≤ κ c n)] [∀ c n, fact (κ₂ c n ≤ r' * κ c n)]
  (n : ℤ) :
  (QprimeFP r' BD κ M).op ⋙ (Ext n).flip.obj ((single _ 0).obj V.to_Cond) ⟶
  (QprimeFP r' BD κ₂ M).op ⋙ (Ext n).flip.obj ((single _ 0).obj V.to_Cond) :=
whisker_right (nat_trans.op $ QprimeFP.Tinv BD _ _ M) _

/-- The `T_inv` map induced by `V` -/
def ExtQprime.T_inv [normed_with_aut r V]
  [∀ c n, fact (κ₂ c n ≤ κ c n)] [∀ c n, fact (κ₂ c n ≤ r' * κ c n)]
  (n : ℤ) :
  (QprimeFP r' BD κ M).op ⋙ (Ext n).flip.obj ((single _ 0).obj V.to_Cond) ⟶
  (QprimeFP r' BD κ₂ M).op ⋙ (Ext n).flip.obj ((single _ 0).obj V.to_Cond) :=
whisker_right (nat_trans.op $ QprimeFP.ι BD _ _ M) _ ≫ whisker_left _ ((Ext n).flip.map $ (single _ _).map $
  (Condensed.of_top_ab_map (normed_with_aut.T.inv).to_add_monoid_hom
  (normed_add_group_hom.continuous _)))

def ExtQprime.Tinv2 [normed_with_aut r V]
  [∀ c n, fact (κ₂ c n ≤ κ c n)] [∀ c n, fact (κ₂ c n ≤ r' * κ c n)]
  (n : ℤ) :
  (QprimeFP r' BD κ M).op ⋙ (Ext n).flip.obj ((single _ 0).obj V.to_Cond) ⟶
  (QprimeFP r' BD κ₂ M).op ⋙ (Ext n).flip.obj ((single _ 0).obj V.to_Cond) :=
ExtQprime.Tinv r' BD κ κ₂ M V n - ExtQprime.T_inv r r' BD κ κ₂ M V n

namespace ExtQprime_iso_aux_system_comm_Tinv_setup

variables (c : (ℝ≥0)ᵒᵖ) (n : ℕ)
  [∀ (c : ℝ≥0) (n : ℕ), fact (κ₂ c n ≤ r' * κ c n)]

lemma aux₁  :
(homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℕ) n).map
    (hom_complex_QprimeFP_nat_iso_aux_system.{u} r' BD κ M V (unop.{1} c)).hom ≫
  ((forget₂.{u+2 u+2 u+1 u+1 u+1} SemiNormedGroup.{u+1} Ab.{u+1}).map_homological_complex
       (complex_shape.up.{0} ℕ) ⋙
     homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℕ) n).map
    ((aux_system.Tinv.{u u+1} r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1 u}.obj V) κ₂ κ).app c) =
  (homology_functor _ _ _).map
  (category_theory.functor.map _
      (homological_complex.op_functor.map (quiver.hom.op $
      (QprimeFP_nat.Tinv  BD κ₂ κ M).app _))) ≫
  (homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℕ) n).map
  (hom_complex_QprimeFP_nat_iso_aux_system.{u} r' BD κ₂ M V (unop.{1} c)).hom :=
begin
  simp only [← functor.map_comp, functor.comp_map], congr' 1,
  apply hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_Tinv,
end

lemma aux₂ :
(homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℤ).symm (-↑n)).map
      (embed_hom_complex_nat_iso.{u} ((QprimeFP_nat.{u} r' BD κ M).obj (unop.{1} c)) V.to_Cond).hom ≫
    (homological_complex.embed.{0 0 u+2 u+1} complex_shape.embedding.nat_up_int_down ⋙
       homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.down.{0} ℤ) (-↑n)).map
      (((preadditive_yoneda.{u+1 u+2}.obj V.to_Cond).map_homological_complex (complex_shape.down.{0} ℕ).symm).map
         (homological_complex.op_functor.{u+2 u+1 0}.map ((QprimeFP_nat.Tinv.{u} BD κ₂ κ M).app (unop.{1} c)).op)) =
  (((preadditive_yoneda.{u+1 u+2}.obj V.to_Cond).right_op.map_homological_complex (complex_shape.up.{0} ℤ) ⋙
        homological_complex.unop_functor.{u+2 u+1 0}.right_op ⋙
          (homology_functor.{u+1 u+2 0} AddCommGroup.{u+1} (complex_shape.up.{0} ℤ).symm (-↑n)).op).map
       ((QprimeFP_int.Tinv.{u} BD κ₂ κ M).app (unop.{1} c))).unop ≫
    (homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℤ).symm (-↑n)).map
      (embed_hom_complex_nat_iso.{u} ((QprimeFP_nat.{u} r' BD κ₂ M).obj (unop.{1} c)) V.to_Cond).hom :=
begin
  dsimp only [functor.op_map, functor.comp_map],
  erw [← functor.map_comp],
  erw [← functor.map_comp],
  congr' 1,
  ext ((_ | k) | k ) : 2,
  { refine (category.id_comp _).trans (category.comp_id _).symm },
  { apply is_zero.eq_of_tgt,
    exact is_zero_zero _ },
  { refine (category.id_comp _).trans (category.comp_id _).symm },
end

end ExtQprime_iso_aux_system_comm_Tinv_setup

lemma ExtQprime_iso_aux_system_comm_Tinv
  [∀ c n, fact (κ₂ c n ≤ κ c n)] [∀ c n, fact (κ₂ c n ≤ r' * κ c n)] (n : ℕ) :
  (ExtQprime_iso_aux_system r' BD κ M V n).hom ≫
  whisker_right (aux_system.Tinv.{u} r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1}.obj V) κ₂ κ)
    ((forget₂ _ _).map_homological_complex _ ⋙ homology_functor Ab.{u+1} (complex_shape.up ℕ) n) =
  ExtQprime.Tinv r' BD κ κ₂ M V n ≫
  (ExtQprime_iso_aux_system r' BD κ₂ M V n).hom :=
begin
  ext c : 2,
  dsimp only [ExtQprime_iso_aux_system_obj,
    ExtQprime_iso_aux_system,
    iso.trans_hom, id, functor.map_iso_hom, nat_iso.of_components_hom_app,
    nat_trans.comp_app],
  haveI : ((homotopy_category.quotient.{u+1 u+2 0} (Condensed.{u u+1 u+2} Ab.{u+1}) (complex_shape.up.{0} ℤ)).obj
     ((QprimeFP_int.{u} r' BD κ M).obj (unop.{1} c))).is_bounded_above :=
     chain_complex.is_bounded_above _,
  haveI : ((homotopy_category.quotient.{u+1 u+2 0} (Condensed.{u u+1 u+2} Ab.{u+1}) (complex_shape.up.{0} ℤ)).obj
     ((QprimeFP_int.{u} r' BD κ₂ M).obj (unop.{1} c))).is_bounded_above :=
     chain_complex.is_bounded_above _,
  have := Ext_compute_with_acyclic_naturality
    ((QprimeFP_int.{u} r' BD κ₂ M).obj c.unop)
    ((QprimeFP_int.{u} r' BD κ M).obj c.unop)
    V.to_Cond _ _
    ((QprimeFP_int.Tinv BD κ₂ κ M).app _) n,
  rotate,
  { intros k i hi, apply QprimeFP_acyclic, exact hi },
  { intros k i hi, apply QprimeFP_acyclic, exact hi },
  erw reassoc_of this, clear this, simp only [category.assoc], congr' 1,
  dsimp only [whisker_right_app],
  rw ExtQprime_iso_aux_system_comm_Tinv_setup.aux₁ r' BD κ κ₂ M V c n,
  simp only [← category.assoc], congr' 1, simp only [category.assoc],
  erw ← nat_trans.naturality,
  simp only [← category.assoc], congr' 1,
  exact ExtQprime_iso_aux_system_comm_Tinv_setup.aux₂ r' BD κ κ₂ M V c n,
end

-- lemma ExtQprime_iso_aux_system_comm_T_inv [normed_with_aut r V] (n : ℕ) (c : ℝ≥0ᵒᵖ) :
--   (ExtQprime_iso_aux_system_obj.{u} r' BD κ₂ M V (unop.{1} c) n).hom ≫
--     ((forget₂.{u+2 u+2 u+1 u+1 u+1} SemiNormedGroup.{u+1} Ab.{u+1}).map_homological_complex (complex_shape.up.{0} ℕ) ⋙
--    homology_functor.{u+1 u+2 0} Ab.{u+1} (complex_shape.up.{0} ℕ) n).map
--   ((aux_system.res.{u u+1} r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1 u}.obj V) κ₂ κ).app c) =
--   ((Ext.{u+1 u+2} ↑n).flip.map
--       ((single.{u+1 u+2} (Condensed.{u u+1 u+2} Ab.{u+1}) 0).map
--           (Condensed.of_top_ab_map.{u} (normed_add_group_hom.to_add_monoid_hom.{u u} normed_with_aut.T.{u}.inv) _))).app
--       ((QprimeFP.{u} r' BD κ₂ M).op.obj c) ≫
--     (ExtQprime_iso_aux_system_obj.{u} r' BD κ₂ M V (unop.{1} c) n).hom :=
-- by admit

def homological_complex.map_unop {A M : Type*} [category A] [abelian A]
  {c : complex_shape M} (C₁ C₂ : homological_complex Aᵒᵖ c) (f : C₁ ⟶ C₂) :
  C₂.unop ⟶ C₁.unop :=
homological_complex.unop_functor.map f.op

namespace ExtQprime_iso_aux_system_comm_setup

include r
variables [normed_with_aut r V] [∀ (c : ℝ≥0) (n : ℕ), fact (κ₂ c n ≤ κ c n)]

def hom_complex_map_T_inv (c : (ℝ≥0)ᵒᵖ) :
  hom_complex_nat.{u} ((QprimeFP_nat.{u} r' BD κ M).obj (unop.{1} c)) V.to_Cond ⟶
  hom_complex_nat.{u} ((QprimeFP_nat.{u} r' BD κ₂ M).obj (unop.{1} c)) V.to_Cond :=
  begin
    refine nat_trans.app _ _,
    refine nat_trans.map_homological_complex _ _,
    refine preadditive_yoneda.map _,
    refine Condensed.of_top_ab_map.{u} (normed_add_group_hom.to_add_monoid_hom.{u u}
      normed_with_aut.T.{u}.inv) (normed_add_group_hom.continuous _)
  end ≫
  (category_theory.functor.map _
      (homological_complex.op_functor.map (quiver.hom.op $
      (QprimeFP_nat.ι BD κ₂ κ M).app _)))

omit r

lemma embed_hom_complex_nat_iso₀ (c : (ℝ≥0)ᵒᵖ) : (embed_hom_complex_nat_iso.{u} ((QprimeFP_nat.{u} r' BD κ₂ M).obj (unop.{1} c)) V.to_Cond).hom.f (int.of_nat 0) = 𝟙 _ := rfl

lemma embed_hom_complex_nat_iso_neg (n : ℕ) (c : (ℝ≥0)ᵒᵖ) : (embed_hom_complex_nat_iso.{u} ((QprimeFP_nat.{u} r' BD κ₂ M).obj (unop.{1} c)) V.to_Cond).hom.f (-[1+ n]) = 𝟙 _ := rfl

lemma add_equiv.to_AddCommGroup_iso_apply (A B : AddCommGroup.{u})
  (e : A ≃+ B) (a : A) : e.to_AddCommGroup_iso.hom a = e a := rfl

lemma preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab_apply (M) (X) (t) :
  (preadditive_yoneda_obj_obj_CondensedSet_to_Condensed_Ab M X).hom t =
  yoneda'_equiv _ _ (Condensed_Ab_CondensedSet_adjunction.hom_equiv X.to_Condensed M t).val := rfl

include r

lemma aux₁ (c : (ℝ≥0)ᵒᵖ):
(hom_complex_QprimeFP_nat_iso_aux_system.{u} r' BD κ M V (unop.{1} c)).hom ≫
  ((forget₂.{u+2 u+2 u+1 u+1 u+1} SemiNormedGroup.{u+1} Ab.{u+1}).map_homological_complex
     (complex_shape.up.{0} ℕ)).map ((aux_system.T_inv.{u u+1} r r' BD
    ⟨M⟩ (SemiNormedGroup.ulift.{u+1 u}.obj V) κ).app c ≫
  (aux_system.res.{u u+1} r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1 u}.obj V) κ₂ κ).app c) =
  hom_complex_map_T_inv _ _ _ _ _ _ _ _ ≫
  (hom_complex_QprimeFP_nat_iso_aux_system.{u} r' BD κ₂ M V (unop.{1} c)).hom :=
begin
  --simp only [← category_theory.functor.map_comp, functor.comp_map], congr' 1,
  dsimp only [hom_complex_map_T_inv], simp only [category.assoc],
  rw ← hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_κ r' BD κ κ₂ M V c.unop,
  simp only [functor.map_comp, ← category.assoc], congr' 1,
  apply hom_complex_QprimeFP_nat_iso_aux_system_naturality_in_T_inv

  /- -- IGNORE THIS
  ext k t : 3,
  dsimp [hom_complex_nat] at t,
  dsimp only [hom_complex_QprimeFP_nat_iso_aux_system, aux_system.T_inv,
    aux_system.res, hom_complex_nat, functor.map_iso, iso.trans_hom,
    homological_complex.unop_functor, homological_complex.comp_f,
    nat_iso.map_homological_complex, nat_iso.app_hom, iso.op_hom, quiver.hom.unop_op,
    nat_trans.map_homological_complex_app_f, ExtQprime_iso_aux_system_obj_aux,
    nat_iso.of_components_hom_app, id, iso.symm_hom, nat_iso.app_inv,
    whisker_right_app, nat_trans.op, functor.comp_map],
  simp only [category_theory.functor.map_comp],
  dsimp only [homological_complex.comp_f, functor.map_homological_complex, functor.op_obj,
    functor.unop, forget₂_unop, nat_iso.of_components_hom_app,
    homological_complex.hom.iso_of_components, iso.refl],
  simp only [category.assoc, category.id_comp],
  erw category.id_comp,
  dsimp only [functor.op, quiver.hom.unop_op],
  erw category.comp_id,
  repeat { rw [comp_apply] },
  -/ -- UUUUGGGHHH

end

lemma aux₂ (c : (ℝ≥0)ᵒᵖ) :
((((preadditive_yoneda.{u+1 u+2}.obj (Condensed.of_top_ab.{u} ↥V)).right_op.map_homological_complex
         (complex_shape.up.{0} ℤ)).obj
        ((QprimeFP_int.{u} r' BD κ M).obj (unop.{1} c))).map_unop
       (((preadditive_yoneda.{u+1 u+2}.obj (Condensed.of_top_ab.{u} ↥V)).right_op.map_homological_complex
           (complex_shape.up.{0} ℤ)).obj
          ((QprimeFP_int.{u} r' BD κ M).obj (unop.{1} c)))
       ((nat_trans.map_homological_complex.{u+1 u+2 0 u+2 u+1}
           (nat_trans.right_op.{u+1 u+1 u+2 u+2} (preadditive_yoneda.{u+1 u+2}.map
           (Condensed.of_top_ab_map.{u} (normed_add_group_hom.to_add_monoid_hom.{u u}
        normed_with_aut.T.{u}.inv) (normed_add_group_hom.continuous _))))
           (complex_shape.up.{0} ℤ)).app
          ((QprimeFP_int.{u} r' BD κ M).obj (unop.{1} c))) ≫
     (homological_complex.unop_functor.{u+2 u+1 0}.right_op.map
        (((preadditive_yoneda.{u+1 u+2}.obj V.to_Cond).right_op.map_homological_complex (complex_shape.up.{0} ℤ)).map
           ((QprimeFP_int.ι.{u} BD κ₂ κ M).app (unop.{1} c)))).unop) ≫
  (embed_hom_complex_nat_iso.{u} ((QprimeFP_nat.{u} r' BD κ₂ M).obj (unop.{1} c)) V.to_Cond).hom =
  (embed_hom_complex_nat_iso.{u} ((QprimeFP_nat.{u} r' BD κ M).obj (unop.{1} c)) V.to_Cond).hom ≫
  category_theory.functor.map _
  (hom_complex_map_T_inv _ _ _ _ _ _ _ _) :=
begin
  ext ((_ | k) | k ) : 2,
  { dsimp only [functor.comp],
    simp only [functor.right_op_map, quiver.hom.unop_op, category.assoc, homological_complex.comp_f,
  homological_complex.unop_functor_map_f, functor.map_homological_complex_map_f],
  rw embed_hom_complex_nat_iso₀,
  rw embed_hom_complex_nat_iso₀,
  ext, refl },
  { apply is_zero.eq_of_tgt,
    exact is_zero_zero _ },
  { dsimp only [functor.comp],
    simp only [functor.right_op_map, quiver.hom.unop_op, category.assoc, homological_complex.comp_f,
  homological_complex.unop_functor_map_f, functor.map_homological_complex_map_f],
  rw embed_hom_complex_nat_iso_neg,
  rw embed_hom_complex_nat_iso_neg,
  ext, refl },
end

end ExtQprime_iso_aux_system_comm_setup

section naturality_snd_var

variables {A : Type*} [category A] [abelian A] [enough_projectives A]
  (X : cochain_complex A ℤ)
  [((homotopy_category.quotient A (complex_shape.up.{0} ℤ)).obj X).is_bounded_above]
  {B₁ B₂ : A} (f : B₁ ⟶ B₂) -- (h₁) (h₂) (i)

@[reassoc]
lemma Ext_compute_with_acyclic_aux₁_naturality_snd_var (i)
  (e : (0 : ℤ) - i = -i) :
  (Ext_compute_with_acyclic_aux₁ X B₁ i).hom ≫
  begin
    refine nat_trans.app _ _,
    refine preadditive_yoneda.map _,
    refine category_theory.functor.map _ f,
  end =
  category_theory.functor.map _
  (category_theory.functor.map _ f) ≫
  (Ext_compute_with_acyclic_aux₁ X B₂ i).hom :=
begin
  ext t,
  simp only [comp_apply],
  dsimp [Ext_compute_with_acyclic_aux₁, Ext],
  simp only [category.assoc],
  generalize_proofs h1 h2,
  let φ₁ := λ j, (single _ j).obj B₁,
  let φ₂ := λ j, (single _ j).obj B₂,
  change t ≫ _ ≫ eq_to_hom (congr_arg φ₁ e) ≫ _ =
    _ ≫ _ ≫ _ ≫ eq_to_hom (congr_arg φ₂ e),
  induction e,
  dsimp, simp only [category.id_comp, category.comp_id],
  erw ← nat_trans.naturality,
  refl,
end

@[reassoc]
lemma Ext_compute_with_acyclic_aux₂_naturality_snd_var (i) :
  (Ext_compute_with_acyclic_aux₂ X B₁ i).hom ≫
  (homology_functor _ _ _).map
  begin
    refine nat_trans.app _ _,
    refine nat_trans.map_homological_complex _ _,
    exact preadditive_yoneda.map f,
  end =
  nat_trans.app
  (preadditive_yoneda.map $ category_theory.functor.map _ f) _ ≫
  (Ext_compute_with_acyclic_aux₂ X B₂ i).hom :=
begin
  dsimp only [Ext_compute_with_acyclic_aux₂, unop_op],
  have := hom_single_iso_naturality_snd_var_good (of' X).replace (-i) f,
  erw ← this,
end

include f
lemma Ext_compute_with_acyclic_aux₃_naturality_snd_var (i) :
  (homology_functor _ _ _).map
  begin
    refine homological_complex.map_unop _ _ _,
    refine nat_trans.app _ _,
    refine nat_trans.map_homological_complex _ _,
    refine nat_trans.right_op _,
    exact preadditive_yoneda.map f,
  end ≫ Ext_compute_with_acyclic_aux₃ X B₂ i =
  Ext_compute_with_acyclic_aux₃ X B₁ i ≫
  (homology_functor _ _ _).map
  begin
    refine nat_trans.app _ _,
    refine nat_trans.map_homological_complex _ _,
    exact preadditive_yoneda.map f,
  end :=
begin
  dsimp only [Ext_compute_with_acyclic_aux₃],
  erw ← (homology_functor.{u_2 u_2+1 0} AddCommGroup.{u_2}
    (complex_shape.up.{0} ℤ).symm (-i)).map_comp,
  erw ← (homology_functor.{u_2 u_2+1 0} AddCommGroup.{u_2}
    (complex_shape.up.{0} ℤ).symm (-i)).map_comp,
  congr' 1,
  ext t x,
  dsimp [Ext_compute_with_acyclic_HomB],
  simp only [comp_apply],
  dsimp [nat_trans.map_homological_complex, functor.right_op,
    homological_complex.map_unop],
  simp only [category.assoc],
end

lemma Ext_compute_with_acyclic_naturality_snd_var
  (h₁) (h₂) (i) :
  (Ext_compute_with_acyclic X B₁ h₁ i).hom ≫
  (homology_functor _ _ _).map
  (begin
    refine homological_complex.map_unop _ _ _,
    refine nat_trans.app _ _,
    refine nat_trans.map_homological_complex _ _,
    exact (preadditive_yoneda.map f).right_op,
  end) =
  category_theory.functor.map _
  (category_theory.functor.map _ f) ≫ (Ext_compute_with_acyclic X B₂ h₂ i).hom :=
begin
  dsimp [Ext_compute_with_acyclic, - homology_functor_map],
  simp only [category.assoc],
  rw ← Ext_compute_with_acyclic_aux₁_naturality_snd_var_assoc,
  rw ← Ext_compute_with_acyclic_aux₂_naturality_snd_var_assoc,
  simp only [category.assoc], congr' 2,
  rw [is_iso.eq_comp_inv, category.assoc, is_iso.inv_comp_eq],
  apply Ext_compute_with_acyclic_aux₃_naturality_snd_var,
  simp,
end

end naturality_snd_var

lemma ExtQprime_iso_aux_system_comm [normed_with_aut r V]
  [∀ c n, fact (κ₂ c n ≤ κ c n)] [∀ c n, fact (κ₂ c n ≤ r' * κ c n)] (n : ℕ) :
  (ExtQprime_iso_aux_system r' BD κ M V n).hom ≫
  whisker_right (aux_system.Tinv2.{u} r r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1}.obj V) κ₂ κ)
    ((forget₂ _ _).map_homological_complex _ ⋙ homology_functor Ab.{u+1} (complex_shape.up ℕ) n) =
  ExtQprime.Tinv2 r r' BD κ κ₂ M V n ≫
  (ExtQprime_iso_aux_system r' BD κ₂ M V n).hom :=
begin
  ext c : 2, dsimp only [aux_system.Tinv2, ExtQprime.Tinv2, nat_trans.comp_app, whisker_right_app],
  simp only [sub_comp, nat_trans.app_sub, functor.map_sub, comp_sub],
  refine congr_arg2 _ _ _,
  { rw [← nat_trans.comp_app, ← ExtQprime_iso_aux_system_comm_Tinv], refl },

  dsimp only [ExtQprime_iso_aux_system_obj,
    ExtQprime_iso_aux_system,
    iso.trans_hom, id, functor.map_iso_hom, nat_iso.of_components_hom_app,
    nat_trans.comp_app],

  haveI : ((homotopy_category.quotient.{u+1 u+2 0} (Condensed.{u u+1 u+2} Ab.{u+1})
    (complex_shape.up.{0} ℤ)).obj
     ((QprimeFP_int.{u} r' BD κ M).obj (unop.{1} c))).is_bounded_above :=
     chain_complex.is_bounded_above _,
  haveI : ((homotopy_category.quotient.{u+1 u+2 0} (Condensed.{u u+1 u+2} Ab.{u+1})
    (complex_shape.up.{0} ℤ)).obj
     ((QprimeFP_int.{u} r' BD κ₂ M).obj (unop.{1} c))).is_bounded_above :=
     chain_complex.is_bounded_above _,
  have := Ext_compute_with_acyclic_naturality
    ((QprimeFP_int.{u} r' BD κ₂ M).obj c.unop)
    ((QprimeFP_int.{u} r' BD κ M).obj c.unop)
    V.to_Cond _ _
    ((QprimeFP_int.ι BD κ₂ κ M).app _) n,
  rotate,
  { intros k i hi, apply QprimeFP_acyclic, exact hi },
  { intros k i hi, apply QprimeFP_acyclic, exact hi },

  simp only [category.assoc], dsimp only [ExtQprime.T_inv, nat_trans.comp_app,
    whisker_right_app, whisker_left_app, functor.flip],
  let η := (Ext.{u+1 u+2} ↑n).map ((nat_trans.op.{0 u+1 0 u+2} (QprimeFP.ι.{u} BD κ₂ κ M)).app c),

  slice_rhs 1 2 { erw ← η.naturality },
  slice_rhs 2 3 { erw this },
  simp only [category.assoc], clear this η,

  let t : Condensed.of_top_ab V ⟶ _ :=
    Condensed.of_top_ab_map.{u} (normed_add_group_hom.to_add_monoid_hom.{u u}
      normed_with_aut.T.{u}.inv) (normed_add_group_hom.continuous _),
  have := Ext_compute_with_acyclic_naturality_snd_var
    ((QprimeFP_int r' BD κ M).obj c.unop) t _ _ n,
  rotate,
  { intros k i hi, apply QprimeFP_acyclic, exact hi },
  { intros k i hi, apply QprimeFP_acyclic, exact hi },
  erw ← reassoc_of this, clear this, congr' 1,
  simp only [functor.comp_map, category_theory.functor.map_comp,
    functor.op_map, quiver.hom.unop_op],
  slice_rhs 1 2 { rw ← category_theory.functor.map_comp },
  slice_lhs 4 5 { rw ← category_theory.functor.map_comp },
  simp only [category.assoc,
    ← category_theory.functor.map_comp, ← functor.map_comp_assoc],

  rw ExtQprime_iso_aux_system_comm_setup.aux₁ r r' BD κ κ₂ M V c,
  slice_lhs 2 4
  { simp only [category_theory.functor.map_comp] },

  simp only [← category.assoc], congr' 1,

  rw ExtQprime_iso_aux_system_comm_setup.aux₂ r r' BD κ κ₂ M V c,
  simp only [category_theory.functor.map_comp, category.assoc],
  congr' 1,

  rw [nat_iso.app_hom, ← nat_trans.naturality],
  congr' 1,

  -- have := Ext_compute_with_acyclic_naturality, <-- we need naturality in the other variable?!

  --simp only [category.assoc],
  --erw reassoc_of this,
   --clear this, simp only [category.assoc], congr' 1,

  /-
  rw [nat_trans.comp_app, functor.map_comp, ExtQprime.T_inv,
    nat_trans.comp_app, whisker_right_app, whisker_left_app, category.assoc],
  dsimp only [ExtQprime_iso_aux_system, nat_iso.of_components_hom_app, aux_system,
    aux_system.res, functor.comp_map],
  -/
end

lemma ExtQprime_iso_aux_system_comm' [normed_with_aut r V]
  [∀ c n, fact (κ₂ c n ≤ κ c n)] [∀ c n, fact (κ₂ c n ≤ r' * κ c n)] (n : ℕ) :
  whisker_right (aux_system.Tinv2.{u} r r' BD ⟨M⟩ (SemiNormedGroup.ulift.{u+1}.obj V) κ₂ κ)
    ((forget₂ _ _).map_homological_complex _ ⋙ homology_functor Ab.{u+1} (complex_shape.up ℕ) n) ≫
  (ExtQprime_iso_aux_system r' BD κ₂ M V n).inv =
  (ExtQprime_iso_aux_system r' BD κ M V n).inv ≫
  ExtQprime.Tinv2 r r' BD κ κ₂ M V n :=
begin
  rw [iso.comp_inv_eq, category.assoc, iso.eq_inv_comp],
  apply ExtQprime_iso_aux_system_comm
end

end

section

def _root_.category_theory.functor.map_commsq
  {C D : Type*} [category C] [abelian C] [category D] [abelian D] (F : C ⥤ D) {X Y Z W : C}
  {f₁ : X ⟶ Y} {g₁ : X ⟶ Z} {g₂ : Y ⟶ W} {f₂ : Z ⟶ W} (sq : commsq f₁ g₁ g₂ f₂) :
  commsq (F.map f₁) (F.map g₁) (F.map g₂) (F.map f₂) :=
commsq.of_eq $ by rw [← F.map_comp, sq.w, F.map_comp]

end

section

variables {r'}
variables (BD : breen_deligne.package)
variables (κ κ₂ : ℝ≥0 → ℕ → ℝ≥0)
variables [∀ (c : ℝ≥0), BD.data.suitable (κ c)] [∀ n, fact (monotone (function.swap κ n))]
variables [∀ (c : ℝ≥0), BD.data.suitable (κ₂ c)] [∀ n, fact (monotone (function.swap κ₂ n))]
variables (M : ProFiltPseuNormGrpWithTinv₁.{u} r')
variables (V : SemiNormedGroup.{u}) [complete_space V] [separated_space V]

open bounded_homotopy_category

-- move me
instance eval'_is_bounded_above :
  ((homotopy_category.quotient (Condensed Ab) (complex_shape.up ℤ)).obj
    ((BD.eval' freeCond').obj M.to_Condensed)).is_bounded_above :=
by { delta breen_deligne.package.eval', refine ⟨⟨1, _⟩⟩, apply chain_complex.bounded_by_one }

variables (ι : ulift.{u+1} ℕ → ℝ≥0) (hι : monotone ι)

def Ext_Tinv2
  {𝓐 : Type*} [category 𝓐] [abelian 𝓐] [enough_projectives 𝓐]
  {A B V : bounded_homotopy_category 𝓐}
  (Tinv : A ⟶ B) (ι : A ⟶ B) (T_inv : V ⟶ V) (i : ℤ) :
  ((Ext i).obj (op B)).obj V ⟶ ((Ext i).obj (op A)).obj V :=
(((Ext i).map Tinv.op).app V - (((Ext i).map ι.op).app V ≫ ((Ext i).obj _).map T_inv))

open category_theory.preadditive

def Ext_Tinv2_commsq
  {𝓐 : Type*} [category 𝓐] [abelian 𝓐] [enough_projectives 𝓐]
  {A₁ B₁ A₂ B₂ V : bounded_homotopy_category 𝓐}
  (Tinv₁ : A₁ ⟶ B₁) (ι₁ : A₁ ⟶ B₁)
  (Tinv₂ : A₂ ⟶ B₂) (ι₂ : A₂ ⟶ B₂)
  (f : A₁ ⟶ A₂) (g : B₁ ⟶ B₂) (sqT : f ≫ Tinv₂ = Tinv₁ ≫ g) (sqι : f ≫ ι₂ = ι₁ ≫ g)
  (T_inv : V ⟶ V) (i : ℤ) :
  commsq
    (((Ext i).map g.op).app V)
    (Ext_Tinv2 Tinv₂ ι₂ T_inv i)
    (Ext_Tinv2 Tinv₁ ι₁ T_inv i)
    (((Ext i).map f.op).app V) :=
commsq.of_eq
begin
  delta Ext_Tinv2,
  simp only [comp_sub, sub_comp, ← nat_trans.comp_app, ← functor.map_comp, ← op_comp, sqT,
    ← nat_trans.naturality, ← nat_trans.naturality_assoc, category.assoc, sqι],
end

open category_theory.preadditive

lemma auux
  {𝓐 : Type*} [category 𝓐] [abelian 𝓐] [enough_projectives 𝓐]
  {A₁ B₁ A₂ B₂ : cochain_complex 𝓐 ℤ}
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj A₁).is_bounded_above]
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj B₁).is_bounded_above]
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj A₂).is_bounded_above]
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj B₂).is_bounded_above]
  {f₁ : A₁ ⟶ B₁} {f₂ : A₂ ⟶ B₂} {α : A₁ ⟶ A₂} {β : B₁ ⟶ B₂}
  (sq1 : commsq f₁ α β f₂) :
  of_hom f₁ ≫ of_hom β = of_hom α ≫ of_hom f₂ :=
begin
  have := sq1.w,
  apply_fun (λ f, (homotopy_category.quotient _ _).map f) at this,
  simp only [functor.map_comp] at this,
  exact this,
end

@[simp] lemma of_hom_id
  {𝓐 : Type*} [category 𝓐] [abelian 𝓐] [enough_projectives 𝓐]
  {A : cochain_complex 𝓐 ℤ}
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj A).is_bounded_above] :
  of_hom (𝟙 A) = 𝟙 _ :=
by { delta of_hom, rw [category_theory.functor.map_id], refl }

lemma Ext_iso_of_bicartesian_of_bicartesian
  {𝓐 : Type*} [category 𝓐] [abelian 𝓐] [enough_projectives 𝓐]
  {A₁ B₁ C A₂ B₂ : cochain_complex 𝓐 ℤ}
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj A₁).is_bounded_above]
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj B₁).is_bounded_above]
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj C).is_bounded_above]
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj A₂).is_bounded_above]
  [((homotopy_category.quotient 𝓐 (complex_shape.up ℤ)).obj B₂).is_bounded_above]
  {f₁ : A₁ ⟶ B₁} {g₁ : B₁ ⟶ C} (w₁ : ∀ n, short_exact (f₁.f n) (g₁.f n))
  {f₂ : A₂ ⟶ B₂} {g₂ : B₂ ⟶ C} (w₂ : ∀ n, short_exact (f₂.f n) (g₂.f n))
  (α : A₁ ⟶ A₂) (β : B₁ ⟶ B₂) (γ : C ⟶ C)
  (ιA : A₁ ⟶ A₂) (ιB : B₁ ⟶ B₂)
  (sq1 : commsq f₁ α β f₂) (sq2 : commsq g₁ β γ g₂)
  (sq1' : commsq f₁ ιA ιB f₂) (sq2' : commsq g₁ ιB (𝟙 _) g₂)
  (V : bounded_homotopy_category 𝓐) (T_inv : V ⟶ V)
  (i : ℤ)
  (H1 : (Ext_Tinv2_commsq (of_hom α) (of_hom ιA) (of_hom β) (of_hom ιB) (of_hom f₁) (of_hom f₂)
    (auux sq1) (auux sq1') T_inv i).bicartesian)
  (H2 : (Ext_Tinv2_commsq (of_hom α) (of_hom ιA) (of_hom β) (of_hom ιB) (of_hom f₁) (of_hom f₂)
    (auux sq1) (auux sq1') T_inv (i+1)).bicartesian) :
  is_iso (Ext_Tinv2 (of_hom γ) (𝟙 _) T_inv (i+1)) :=
begin
  have LES₁ := (((Ext_five_term_exact_seq' _ _ i V w₁).drop 2).pair.cons (Ext_five_term_exact_seq' _ _ (i+1) V w₁)),
  replace LES₁ := (((Ext_five_term_exact_seq' _ _ i V w₁).drop 1).pair.cons LES₁).extract 0 4,
  have LES₂ := (((Ext_five_term_exact_seq' _ _ i V w₂).drop 2).pair.cons (Ext_five_term_exact_seq' _ _ (i+1) V w₂)).extract 0 4,
  replace LES₂ := (((Ext_five_term_exact_seq' _ _ i V w₂).drop 1).pair.cons LES₂).extract 0 4,
  refine iso_of_bicartesian_of_bicartesian LES₂ LES₁ _ _ _ _ H1 H2,
  { apply commsq.of_eq, delta Ext_Tinv2, clear LES₁ LES₂,
    rw [sub_comp, comp_sub, ← functor.flip_obj_map, ← functor.flip_obj_map],
    rw ← Ext_δ_natural i V _ _ _ _ α β γ sq1.w sq2.w w₁ w₂,
    congr' 1,
    rw [← nat_trans.naturality, ← functor.flip_obj_map, category.assoc,
      Ext_δ_natural i V _ _ _ _ ιA ιB (𝟙 _) sq1'.w sq2'.w w₁ w₂],
    simp only [op_id, category_theory.functor.map_id, nat_trans.id_app,
      category.id_comp, of_hom_id, category.comp_id],
    erw [category.id_comp],
    symmetry,
    apply Ext_δ_natural', },
  { apply Ext_Tinv2_commsq,
    { exact auux sq2 },
    { exact auux sq2' }, },
end

end


import data.rat
import data.list

import tidy.lib.list
import tidy.rewrite_search.core.shared

import ..types
import .common

open tactic
open tidy.rewrite_search

namespace tidy.rewrite_search.discovery

def BUNDLE_CHUNK_SIZE := 1

-- TODO Be smarter about calculating this.
meta def score_bundle (b : bundle_ref) (sample : list expr) : tactic ℚ := do
  mems ← b.get_members,
  mems.mfoldl (λ sum n, do
    e ← mk_const n,
    ret ← are_promising_rewrites (rewrite_list_from_lemma e) sample,
    return $ if ret then sum + 1 else sum
  ) 0

-- TODO report the lemma(s) which caused a selected bundle to be chosen,
-- so that that lemma could just be tagged individually.

-- TODO at the end of the search report which "desperations" things happened
-- (bundles added, random lemmas found and used) so that they can be addressed
-- more easily/conveniently.

meta def try_bundles (conf : config) (p : progress) (sample : list expr) : tactic (progress × list (expr × bool)) :=
  if p.persistence < persistence.try_bundles then
    return (p, [])
  else do
    bs ← list.filter (λ b, ¬p.seen_bundles.contains b) <$> get_bundles,
    bs ← bs.mmap $ λ b, (do s ← score_bundle b sample, return (b, s)),
    (awful_bs, interesting_bs) ← pure $ bs.partition $ λ b, b.2 = 0,
    let p := {p with seen_bundles := p.seen_bundles.append (awful_bs.map prod.fst)},
    match interesting_bs.min_rel (λ a b, a.2 > b.2) with
    | none := do
      if conf.trace_discovery then
      discovery_trace format!"Could not find any promising bundles of the {bs.length} non-suggested bundles considered: {bs.map $ λ b, b.1.bundle.name}"
      else skip,
      return (p, [])
    | some (b, score) := do
      if conf.trace_discovery then
      discovery_trace format!"Found a promising bundle (of {bs.length} considered) \"{b.bundle.name}\"! If we succeed, please suggest this bundle for consideration."
      else skip,
      ms ← b.get_members >>= load_names,
      return (p, rewrite_list_from_lemmas ms)
    end

end tidy.rewrite_search.discovery


import LeanCodePrompts.FirstTacticData
import LeanCodePrompts.ParseJson
import LeanCodePrompts.Translate
import Lean

open Lean Meta Elab Tactic Parser

initialize cacheTacticJson : IO.Ref (HashMap String Json) ← IO.mkRef (HashMap.empty)

def getTacticString : TacticM String := do
  let s ← saveState
  let target ← getMainTarget
  let lctx ←  getLCtx
  let decls := lctx.decls.toList.tail!
  let mut statement := ""
  for decl in decls do
    match decl with
    | some <| LocalDecl.ldecl _ _ n t .. =>
      statement := statement ++ s!"({n.eraseMacroScopes} : {← t.view}) "
      pure ()
    | some <| LocalDecl.cdecl _ _ n t bi _ => do
      let core := s!"{n.eraseMacroScopes} : {← t.view}"
      let typeString :=s!"{← t.view}"
      let argString := match bi with
      | BinderInfo.implicit => "{"++ core ++ "}"
      | BinderInfo.strictImplicit => "{{ "++ core ++ "}}"
      | BinderInfo.instImplicit =>
        if (`inst).isPrefixOf n then s!"[{typeString}]"
          else s!"[{core}]"
      | BinderInfo.default => s!"({core})"
      statement := statement ++ argString ++ " "
      pure ()
    | none => pure ()
  statement := statement ++ ": " ++ (←  target.view)
  s.restore
  return statement.replace "✝" ""

elab "name_inacessibles" : tactic => do
  withMainContext do
  let lctx ←  getLCtx
  let decls := lctx.decls
  let mut statement := "rename_i"
  for decl in decls do
    match decl with
    | some <| LocalDecl.ldecl _ _ n .. =>
      if n != n.eraseMacroScopes then
        statement := statement ++ s!" {n.eraseMacroScopes}"
      pure ()
    | some <| LocalDecl.cdecl _ _ n .. => do
      if n != n.eraseMacroScopes then
        statement := statement ++ s!" {n.eraseMacroScopes}"
      pure ()
    | none => pure ()
  unless statement == "rename_i" do
    let tac? := runParserCategory (← getEnv) `tactic statement
      match tac? with
      | Except.ok tac => do
        evalTactic tac
      | Except.error e => do
        throwError e

elab "show_goal" : tactic =>
  withMainContext do
    let view ← getTacticString
    logInfo view
    return ()

def silly {α  : Type}(n m : Nat)[DecidableEq α] : n + m = n + m := by
    show_goal
    let a  := n
    let _ : a = a := rfl
    show_goal
    rfl

def silly' : (n m : Nat)  →  n + m = n + m := by
    intros
    show_goal
    rfl
    done

example : (n m : Nat)  →  n + m = n + m := by
    intros
    name_inacessibles
    rfl
    done

structure TacticStateProxy where
  binders: Array <| Name ×  BinderInfo
  letData : Array <| Name × Expr × Expr
  target : Expr

def getTacticStateProxy : TacticM <| Option TacticStateProxy :=
  withoutModifyingState do
  try
    let target ← getMainTarget
    let lctx ←  getLCtx
    let decls := lctx.decls
    let mut binders := #[]
    let mut letData := #[]
    let mut fvars : Array Expr := #[]
    for decl in decls do
      match decl with
      | some <| LocalDecl.ldecl _ _ n t b .. =>
        let t ← mkForallFVars fvars t
        let b ← mkLambdaFVars fvars b
        letData := letData.push (n, t, b)
        pure ()
      | some <| LocalDecl.cdecl _ fVarId n _ bi _ => do
        binders := binders.push  (n, bi)
        fvars := fvars.push <| mkFVar fVarId
        pure ()
      | none => pure ()
    let target ← mkForallFVars fvars target
    return some {binders := binders, letData := letData, target := target}
  catch _ => return none

#check List.allM

def equalStates (s₁ s₂ : TacticStateProxy) : TacticM Bool :=
   withMainContext do
    return s₁.binders == s₂.binders
            && s₁.letData.size == s₂.letData.size &&
            (← isDefEq s₁.target s₂.target) &&
            (← (List.range s₁.letData.size).allM (fun i =>
              let (n₁, t₁, b₁) := s₁.letData.get! i
              let (n₂, t₂, b₂) := s₂.letData.get! i
              return n₁ == n₂ && (← isDefEq t₁ t₂) && (← isDefEq b₁ b₂)))

def firstEffectiveTactic (tacStrings: List String)(warnOnly: Bool := Bool.true) : TacticM Unit :=
  withMainContext do
  let env ← getEnv
  let goal ← getTacticString
  logInfo m!"goal: {goal}"
  logInfo m!"trying tactics: {tacStrings}"
  let s ← saveState
  let s₁? ← getTacticStateProxy
  for tacString in tacStrings do
    -- logInfo m!"Trying tactic {tacString}"
    try
      let tac? := runParserCategory env `tactic tacString
      match tac? with
      | Except.ok tac => do
          Term.withoutErrToSorry do
            evalTactic tac
          let gs ← getUnsolvedGoals
          if gs.isEmpty then
              logInfo m!"tactic `{tacString}` was effective"
              return
          else
            let check : Bool ←
            try
              let s₂? ← getTacticStateProxy
              match s₁?, s₂? with
              | some s₁, some s₂ => equalStates s₁ s₂
              | _,_ => pure Bool.true
            catch _ =>
              -- logWarning
                -- m!"Failed to check state after {tacString}; error : {e.toMessageData}"
              pure Bool.true
            if check then
              s.restore
            else
              let checkForSorries : Bool ←
                try
                  let target ← getMainTarget
                  pure target.hasSyntheticSorry
                catch _ => pure Bool.false
              -- logInfo m!"sorries? {checkForSorries}"
              if checkForSorries then
                s.restore
              else
                logInfo m!"tactic `{tacString}` was effective"
                return
      | Except.error _ =>
        pure ()
    catch _ =>
      s.restore
  unless warnOnly do
    throwError m!"No effective tactic found for {goal} in {tacStrings}"
  logWarning "No tactic in the list was effective"

elab "first_effective_tactic" : tactic =>
  withMainContext do
    firstEffectiveTactic ["unparsable", "exact blah", "intros", "rfl"]

-- proved by reflexivity
def silly'' (n m : Nat)  : n + m = n + m := by
    intros -- legal but no effect
    first_effective_tactic

def silly''' : (n m : Nat)  →  n + m = n + m := by
    first_effective_tactic
    rfl

def silly'''' : (n m : Nat)  →  n + m = n + m := by
    repeat (first_effective_tactic)

def getTacticPrompts(s: String)(numSim : Nat)
   : TermElabM (Array String) := do
      let jsData := Json.mkObj [
        ("filename", "data/lean4-thms.json"),
        ("field", "core-prompt"),
        ("core-prompt", s),
        ("n", numSim),
        ("model_name", "all-mpnet-base-v2")
      ]
      let simJsonOut ←
        IO.Process.output {cmd:= "curl", args:=
          #["-X", "POST", "-H", "Content-type: application/json", "-d", jsData.pretty, s!"{← leanAideIP}/nearest_prompts"]}
      if simJsonOut.exitCode > 0 then
        throwError m!"Failed to get prompts from server: {simJsonOut.stderr}"
      else
        let json ← readJson simJsonOut.stdout
        match json.getArr? with
        | Except.ok arr =>
          let mut prompts := #[]
          for j in arr do
            match j.getObjVal? "tactic-prompt" with
            | Except.ok s =>
              match s.getStr? with
              | Except.ok s =>
                prompts := prompts.push s
              | Except.error e =>
                throwError m!"Failed to parse json {j}; error: {e}"
            | Except.error e =>
              throwError m!"Failed to parse json {j}; error: {e}"
          return prompts
        | Except.error e =>
            throwError m!"Failed to parse json: {e}"

def fourSquaresPrompt := ": ∀ p : Nat, Prime p → (p % 4 = 1) → ∃ a b : Nat, a ^ 2 + b ^ 2 = p"

-- #eval getTacticPrompts fourSquaresPrompt 20

def makeTacticPrompt (n: Nat)  : TacticM String := do
  let core ← getTacticString
  let prompts ← getTacticPrompts core n
  let prompt := prompts.foldr (fun  p acc =>
s!"{p}

{acc}"
          ) s!"
theorem {core} := by "
  return prompt

def tacticList : TacticM <| List String := do
  let core ← getTacticString
  let prompts ← getTacticPrompts core 5
  let promptPairs := prompts.map (fun p =>
    let arr := p.splitOn ":= by"
    (arr.get! 0++ ":= by", arr.get! 1))
  let prompt := GPT.makePrompt core promptPairs
  -- let prompt ← makeTacticPrompt 20
  let cache ← cacheTacticJson.get
  let fullJson ←
    match cache.find? prompt.pretty with
    | some json => pure json
    | none =>
      let res ← gptQuery prompt 5 ⟨8, 1⟩ #[";", "sorry", "\n"]
      cacheTacticJson.set <| cache.insert prompt.pretty res
      pure res
  let outJson :=
        (fullJson.getObjVal? "choices").toOption.getD (Json.arr #[])
  let arr ← GPT.jsonToExprStrArray outJson
  let arr := arr.map (fun s =>
      if s.endsWith "<" then s.dropRight 1 |>.trim else s.trim)
  return arr.toList.eraseDups

elab "aide?" : tactic =>
  withMainContext do
    let tacStrings ← tacticList
    let tacStrings := tacStrings.filter (fun s => s != "sorry" && s != "admit")
    let tac ← `(tactic|name_inacessibles)
    evalTactic tac
    firstEffectiveTactic tacStrings Bool.true

elab "aide!" : tactic =>
  withMainContext do
    let tacStrings ← tacticList
    let tac ← `(tactic|name_inacessibles)
    evalTactic tac
    let tacStrings := tacStrings.filter (fun s => s != "sorry" && s != "admit")
    firstEffectiveTactic tacStrings Bool.false

macro "aide" : tactic =>
  `(tactic| aide? ; save)

elab "show_tactic_prompt" : tactic =>
  withMainContext do
    let view ← makeTacticPrompt 20
    logInfo view
    return ()

elab "lookahead" tac:tactic : tactic =>
  withMainContext do
    let s ← saveState
    try
      evalTactic tac
      s.restore
    catch e =>
      s.restore
      let msg := e.toMessageData
      throwError s!"{← msg.toString}"

def lookaheadTactics (ss: List String) : List String :=
    ss.map (fun s => s!"{s} ; done") ++
    ss.map (fun s => s!"({s} <;> (lookahead aide!)) ; done") ++
    ss.map (fun s => s!"{s} <;> (lookahead aide!)") ++
    ss

example : 1 = 1 := by
  lookahead rfl
  lookahead rfl
  rfl

elab "aide_aux" : tactic =>
  withMainContext do
    let tacStrings ← tacticList
    let tacStrings := tacStrings.filter (fun s => s != "sorry" && s != "admit")
    let tac ← `(tactic|name_inacessibles)
    evalTactic tac
    let tacStrings := lookaheadTactics tacStrings
    firstEffectiveTactic tacStrings Bool.false

macro "aide_lookahead" : tactic => `(checkpoint aide_aux)


/-
Copyright (c) 2020 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.tactic.core
import Mathlib.PostPort

namespace Mathlib

/-!
# list_unused_decls

`#list_unused_decls` is a command used for theory development.
When writing a new theory one often tries
multiple variations of the same definitions: `foo`, `foo'`, `foo₂`,
`foo₃`, etc. Once the main definition or theorem has been written,
it's time to clean up and the file can contain a lot of dead code.
Mark the main declarations with `@[main_declaration]` and
`#list_unused_decls` will show the declarations in the file
that are not needed to define the main declarations.

Some of the so-called "unused" declarations may turn out to be useful
after all. The oversight can be corrected by marking those as
`@[main_declaration]`. `#list_unused_decls` will revise the list of
unused declarations. By default, the list of unused declarations will
not include any dependency of the main declarations.

The `@[main_declaration]` attribute should be removed before submitting
code to mathlib as it is merely a tool for cleaning up a module.
-/

namespace tactic

/-- Attribute `main_declaration` is used to mark declarations that are featured
in the current file.  Then, the `#list_unused_decls` command can be used to
list the declaration present in the file that are not used by the main
declarations of the file. -/
/-- `update_unsed_decls_list n m` removes from the map of unneeded declarations those
referenced by declaration named `n` which is considerred to be a
main declaration -/
/-- In the current file, list all the declaration that are not marked as `@[main_declaration]` and
that are not referenced by such declarations -/
/-- expecting a string literal (e.g. `"src/tactic/find_unused.lean"`)
-/
/-- The command `#list_unused_decls` lists the declarations that that
are not used the main features of the present file. The main features
of a file are taken as the declaration tagged with
`@[main_declaration]`.

A list of files can be given to `#list_unused_decls` as follows:

```lean
#list_unused_decls ["src/tactic/core.lean","src/tactic/interactive.lean"]
```

They are given in a list that contains file names written as Lean
strings. With a list of files, the declarations from all those files
in addition to the declarations above `#list_unused_decls` in the
current file will be considered and their interdependencies will be
analyzed to see which declarations are unused by declarations marked
as `@[main_declaration]`. The files listed must be imported by the
current file. The path of the file names is expected to be relative to
the root of the project (i.e. the location of `leanpkg.toml` when it
is present).

Neither `#list_unused_decls` nor `@[main_declaration]` should appear
in a finished mathlib development. -/
end Mathlib


/-!
# Monad Transformers

In the previous sections you learned about some handy monads [Option](monads.lean.md),
[IO](monads.lean.md), [Reader](readers.lean.md), [State](states.lean.md) and
[Except](except.lean.md), and you now know how to make your function use one of these, but what you
do not yet know is how to make your function use multiple monads at once.

For example, suppose you need a function that wants to access some Reader context and optionally throw
an exception?  This would require composition of two monads `ReaderM` and `Except` and this is what
monad transformers are for.

A monad transformer is fundamentally a wrapper type. It is generally parameterized by another
monadic type. You can then run actions from the inner monad, while adding your own customized
behavior for combining actions in this new monad. The common transformers add `T` to the end of an
existing monad name. You will find `OptionT`, `ExceptT`, `ReaderT`, `StateT` but there is no transformer
for `IO`.  So generally if you need `IO` it becomes the innermost wrapped monad.

In the following example we use `ReaderT` to provide some read only context to a function
and this `ReaderT` transformer will wrap an `Except` monad.  If all goes well the
`requiredArgument` returns the value of a required argument and `optionalSwitch`
returns true if the optional argument is present.

-/
abbrev Arguments := List String

def indexOf? [BEq α] (xs : List α) (s : α) (start := 0): Option Nat :=
  match xs with
  | [] => none
  | a :: tail => if a == s then some start else indexOf? tail s (start+1)

def requiredArgument (name : String) : ReaderT Arguments (Except String) String := do
  let args ← read
  let value := match indexOf? args name with
    | some i => if i + 1 < args.length then args[i+1]! else ""
    | none => ""
  if value == "" then throw s!"Command line argument {name} missing"
  return value

def optionalSwitch (name : String) : ReaderT Arguments (Except String) Bool := do
  let args ← read
  return match (indexOf? args name) with
  | some _ => true
  | none => false

#eval requiredArgument "--input" |>.run ["--input", "foo"]
-- Except.ok "foo"

#eval requiredArgument "--input" |>.run ["foo", "bar"]
-- Except.error "Command line argument --input missing"

#eval optionalSwitch "--help" |>.run ["--help"]
-- Except.ok true

#eval optionalSwitch "--help" |>.run []
-- Except.ok false

/-!
Notice that `throw` was available from the inner `Except` monad. The cool thing is you can switch
this around and get the exact same result using `ExceptT` as the outer monad transformer and
`ReaderM` as the wrapped monad. Try changing requiredArgument to `ExceptT String (ReaderM Arguments) Bool`.

Note: the `|>.` notation is described in [Readers](readers.lean.md#the-reader-solution).

## Adding more layers

Here's the best part about monad transformers. Since the result of a monad transformer is itself a
monad, you can wrap it inside another transformer! Suppose you need to pass in some read only context
like the command line arguments, update some read-write state (like program Config) and optionally
throw an exception, then you could write this:

-/
structure Config where
  help : Bool := false
  verbose : Bool := false
  input : String := ""
  deriving Repr

abbrev CliConfigM := StateT Config (ReaderT Arguments (Except String))

def parseArguments : CliConfigM Bool := do
  let mut config ← get
  if (← optionalSwitch "--help") then
    throw "Usage: example [--help] [--verbose] [--input <input file>]"
  config := { config with
    verbose := (← optionalSwitch "--verbose"),
    input := (← requiredArgument "--input") }
  set config
  return true

def main (args : List String) : IO Unit := do
  let config : Config := { input := "default"}
  match parseArguments |>.run config |>.run args with
  | Except.ok (_, c) => do
    IO.println s!"Processing input '{c.input}' with verbose={c.verbose}"
  | Except.error s => IO.println s

#eval main ["--help"]
-- Usage: example [--help] [--verbose] [--input <input file>]

#eval main ["--input", "foo"]
-- Processing input file 'foo' with verbose=false

#eval main ["--verbose", "--input", "bar"]
-- Processing input 'bar' with verbose=true

/-!
In this example `parseArguments` is actually three stacked monads, `StateM`, `ReaderM`, `Except`. Notice
the convention of abbreviating long monadic types with an alias like `CliConfigM`.

## Monad Lifting

Lean makes it easy to compose functions that use different monads using a concept of automatic monad
lifting.  You already used lifting in the above code, because you were able to compose
`optionalSwitch` which has type `ReaderT Arguments (Except String) Bool` and call it from
`parseArguments` which has a bigger type `StateT Config (ReaderT Arguments (Except String))`.
This "just worked" because Lean did some magic with monad lifting.

To give you a simpler example of this, suppose you have the following function:
-/
def divide (x : Float ) (y : Float): ExceptT String Id Float :=
  if y == 0 then
    throw "can't divide by zero"
  else
    pure (x / y)

#eval divide 6 3 -- Except.ok 2.000000
#eval divide 1 0 -- Except.error "can't divide by zero"
/-!

Notice here we used the `ExceptT` transformer, but we composed it with the `Id` identity monad.
This is then the same as writing `Except String Float` since the identity monad does nothing.

Now suppose you want to count the number of times divide is called and store the result in some
global state:
-/

def divideCounter (x : Float) (y : Float) : StateT Nat (ExceptT String Id) Float := do
  modify fun s => s + 1
  divide x y

#eval divideCounter 6 3 |>.run 0    -- Except.ok (2.000000, 1)
#eval divideCounter 1 0 |>.run 0    -- Except.error "can't divide by zero"

/-!

The `modify` function is a helper which makes it easier to use `modifyGet` from the `StateM` monad.
But something interesting is happening here, `divideCounter` is returning the value of
`divide`, but the types don't match, yet it works?  This is monad lifting in action.

You can see this more clearly with the following test:

-/
def liftTest (x : Except String Float) :
  StateT Nat (Except String) Float := x

#eval liftTest (divide 5 1) |>.run 3 -- Except.ok (5.000000, 3)

/-!

Notice that `liftTest` returned `x` without doing anything to it, yet that matched the return type
`StateT Nat (Except String) Float`.  Monad lifting is provided by monad transformers.  if you
`#print liftTest` you will see that Lean is implementing this using a call to a function named
`monadLift` from the `MonadLift` type class:

```lean,ignore
class MonadLift (m : Type u → Type v) (n : Type u → Type w) where
  monadLift : {α : Type u} → m α → n α
```

So `monadLift` is a function for lifting a computation from an inner `Monad m α ` to an outer `Monad n α`.
You could replace `x` in `liftTest` with `monadLift x` if you want to be explicit about it.

The StateT monad transformer defines an instance of `MonadLift` like this:

```lean
@[inline] protected def lift {α : Type u} (t : m α) : StateT σ m α :=
  fun s => do let a ← t; pure (a, s)

instance : MonadLift m (StateT σ m) := ⟨StateT.lift⟩
```
This means that any monad `m` can be wrapped in a `StateT` monad by using the function
`fun s => do let a ← t; pure (a, s)` that takes state `s`, runs the inner monad action `t`, and
returns the result and the new state in a pair `(a, s)` without making any changes to `s`.

Because `MonadLift` is a type class, Lean can automatically find the required `monadLift`
instances in order to make your code compile and in this way it was able to find the `StateT.lift`
function and use it to wrap the result of `divide` so that the correct type is returned from
`divideCounter`.

If you have an instance `MonadLift m n` that means there is a way to turn a computation that happens
inside of `m` into one that happens inside of `n` and (this is the key part) usually *without* the
instance itself creating any additional data that feeds into the computation. This means you can in
principle declare lifting instances from any monad to any other monad, it does not, however, mean
that you should do this in all cases.  You can get a very nice report on how all this was done by
adding the line `set_option trace.Meta.synthInstance true in` before `divideCounter` and moving you
cursor to the end of the first line after `do`.

This was a lot of detail, but it is very important to understand how monad lifting works because it
is used heavily in Lean programs.

## Transitive lifting

There is also a transitive version of `MonadLift` called `MonadLiftT` which can lift multiple
monad layers at once.  In the following example we added another monad layer with
`ReaderT String ...` and notice that `x` is also automatically lifted to match.

-/
def liftTest2 (x : Except String Float) :
  ReaderT String (StateT Nat (Except String)) Float := x

#eval liftTest2 (divide 5 1) |>.run "" |>.run 3
-- Except.ok (5.000000, 3)

/-!

The ReaderT monadLift is even simpler than the one for StateT:

```lean,ignore
instance  : MonadLift m (ReaderT ρ m) where
  monadLift x := fun _ => x
```

This lift operation creates a function that defines the required `ReaderT` input
argument, but the inner monad doesn't know or care about `ReaderT` so the
monadLift function throws it away with the `_` then calls the inner monad action `x`.
This is a perfectly legal implementation of the `ReaderM` monad.

## Add your own Custom MonadLift

This does not compile:
-/
def main2 : IO Unit := do
  try
    let ret ← divideCounter 5 2 |>.run 0
    IO.println (toString ret)
  catch e =>
    IO.println e

/-!
saying:
```
typeclass instance problem is stuck, it is often due to metavariables
  ToString ?m.4786
```

The reason is `divideCounter` returns the big `StateT Nat (ExceptT String Id) Float` and that type
cannot be automatically lifted into the `main` return type of `IO Unit` unless you give it some
help.

The following custom `MonadLift` solves this problem:

-/
def liftIO (t : ExceptT String Id α) : IO α := do
  match t with
  | .ok r => EStateM.Result.ok r
  | .error s => EStateM.Result.error s

instance : MonadLift (ExceptT String Id) IO where
  monadLift := liftIO

def main3 : IO Unit := do
  try
    let ret ← divideCounter 5 2 |>.run 0
    IO.println (toString ret)
  catch e =>
    IO.println e

#eval main3 -- (2.500000, 1)
/-!

It turns out that the `IO` monad you see in your `main` function is based on the `EStateM.Result` type
which is similar to the `Except` type but it has an additional return value. The `liftIO` function
converts any `Except String α` into `IO α` by simply mapping the ok case of the `Except` to the
`Result.ok` and the error case to the `Result.error`.

## Lifting ExceptT

In the previous [Except](except.lean.md) section you saw functions that `throw` Except
values. When you get all the way back up to your `main` function which has type `IO Unit` you have
the same problem you had above, because `Except String Float` doesn't match even if you use a
`try/catch`.

-/

def main4 : IO Unit := do
  try
    let ret ← divide 5 0
    IO.println (toString ret)  -- lifting happens here.
  catch e =>
    IO.println s!"Unhandled exception: {e}"

#eval main4 -- Unhandled exception: can't divide by zero

/-!

Without the `liftIO` the `(toString ret)` expression would not compile with a similar error:

```
typeclass instance problem is stuck, it is often due to metavariables
  ToString ?m.6007
```

So the general lesson is that if you see an error like this when using monads, check for
a missing `MonadLift`.

## Summary

Now that you know how to combine your monads together, you're almost done with understanding the key
concepts of monads! You could probably go out now and start writing some pretty nice code! But to
truly master monads, you should know how to make your own, and there's one final concept that you
should understand for that. This is the idea of type "laws". Each of the structures you've learned
so far has a series of laws associated with it. And for your instances of these classes to make
sense, they should follow the laws! Check out [Monad Laws](laws.lean.md).
-/