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) : semi_normed_group (locally_constant X V) :=
locally_constant.semi_normed_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_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_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_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]
  [semi_normed_group V] [semi_normed_group W]
  (f : X → Y) (hf : continuous f) (g : normed_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) :=
@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))
  (@semi_normed_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.semi_normed_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 : semi_normed_group (locally_constant ↥(X.to_CompHaus.to_Top) ↥V),
      { exact locally_constant.semi_normed_group },
      letI : semi_normed_group (locally_constant ↥(Y.to_CompHaus.to_Top) ↥V),
      { exact locally_constant.semi_normed_group },
      exact normed_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_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_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_group_hom.uniform_continuous, },
  { apply normed_group_hom.uniform_continuous, },
  conv_lhs
  { dsimp only [function.comp, normed_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_group_hom.uniform_continuous, },
  { apply normed_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_group_hom.to_add_monoid_hom.{u u} normed_with_aut.T.{u}.inv)
  (normed_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_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_group_hom.coe_to_add_monoid_hom],
  rw uniform_space.completion.map_comp',
  rotate,
  { apply normed_group_hom.uniform_continuous },
  { apply normed_group_hom.uniform_continuous },
  rw uniform_space.completion.map_comp',
  rotate,
  { apply normed_group_hom.uniform_continuous },
  { apply normed_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₂],
  rw nat_iso.of_components.inv_app,
  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 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) ≫
    (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 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) :=
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_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_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_group_hom.to_add_monoid_hom.{u u}
      normed_with_aut.T.{u}.inv) (normed_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_group_hom.to_add_monoid_hom.{u u}
        normed_with_aut.T.{u}.inv) (normed_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_group_hom.to_add_monoid_hom.{u u}
      normed_with_aut.T.{u}.inv) (normed_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


meta def get_file (fn : name) : vm format :=
do {
  d ← vm.get_decl fn,
  some n ← return (vm_decl.olean d) | failure,
  return (to_fmt n)
}
<|>
return (to_fmt "<curr file>")

meta def pos_info (fn : name) : vm format :=
do {
  d        ← vm.get_decl fn,
  some pos ← return (vm_decl.pos d) | failure,
  file             ← get_file fn,
  return (file ++ ":" ++ pos.1 ++ ":" ++ pos.2)
}
<|>
return (to_fmt "<position not available>")

meta def obj_fmt (o : vm_obj) : vm format :=
match o^.kind with
| vm_obj_kind.tactic_state :=
     return (to_fmt "state:" ++ format.nest 8 (format.line ++ o^.to_tactic_state^.to_format))
| _ := do s ← vm.obj_to_string o, return $ to_fmt s
end

meta def display_args_aux : nat → vm unit
| i := do
   sz ← vm.stack_size,
   if i = sz then return ()
   else do
     o ← vm.stack_obj i,
     (n, t) ← vm.stack_obj_info i,
     fmt ← obj_fmt o,
     vm.trace (to_fmt "  " ++ to_fmt n ++ " := " ++ fmt),
     display_args_aux (i+1)

meta def display_args : vm unit :=
do bp ← vm.bp,
   display_args_aux bp

@[vm_monitor]
meta def basic_monitor : vm_monitor nat :=
{ init := 1000,
  step := λ sz, do
    csz ← vm.call_stack_size,
    if sz = csz then return sz
    else
      do {
      fn  ← vm.curr_fn,
      pos ← pos_info fn,
      vm.trace (to_fmt "[" ++ csz ++ "]: " ++ to_fmt fn ++ " @ " ++ pos),
      display_args,
      return csz
      }
      <|>
      return csz -- curr_fn failed
}

set_option debugger true
open tactic

example (a b : Prop) : a → b → a ∧ b :=
by (intros >> constructor >> repeat assumption)


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

universes l

namespace Mathlib

/-!
# Documentation commands

We generate html documentation from mathlib. It is convenient to collect lists of tactics, commands,
notes, etc. To facilitate this, we declare these documentation entries in the library
using special commands.

* `library_note` adds a note describing a certain feature or design decision. These can be
  referenced in doc strings with the text `note [name of note]`.
* `add_tactic_doc` adds an entry documenting an interactive tactic, command, hole command, or
  attribute.

Since these commands are used in files imported by `tactic.core`, this file has no imports.

## Implementation details

`library_note note_id note_msg` creates a declaration `` `library_note.i `` for some `i`.
This declaration is a pair of strings `note_id` and `note_msg`, and it gets tagged with the
`library_note` attribute.

Similarly, `add_tactic_doc` creates a declaration `` `tactic_doc.i `` that stores the provided
information.
-/

/-- A rudimentary hash function on strings. -/
def string.hash (s : string) : ℕ :=
  string.fold 1 (fun (h : ℕ) (c : char) => (bit1 (bit0 (bit0 (bit0 (bit0 1)))) * h + char.val c) % unsigned_sz) s

/-- `mk_hashed_name nspace id` hashes the string `id` to a value `i` and returns the name
`nspace._i` -/
/--
`copy_doc_string fr to` copies the docstring from the declaration named `fr`
to each declaration named in the list `to`. -/
/--
`copy_doc_string source → target_1 target_2 ... target_n` copies the doc string of the
declaration named `source` to each of `target_1`, `target_2`, ..., `target_n`.
 -/
/-! ### The `library_note` command -/

/-- A user attribute `library_note` for tagging decls of type `string × string` for use in note
output. -/
/--
`mk_reflected_definition name val` constructs a definition declaration by reflection.

Example: ``mk_reflected_definition `foo 17`` constructs the definition
declaration corresponding to `def foo : ℕ := 17`
-/
/-- If `note_name` and `note` are `pexpr`s representing strings,
`add_library_note note_name note` adds a declaration of type `string × string` and tags it with
the `library_note` attribute. -/
/--
A command to add library notes. Syntax:
```
/--
note message
-/
/-- Collects all notes in the current environment.
Returns a list of pairs `(note_id, note_content)` -/
/-! ### The `add_tactic_doc_entry` command -/

/-- The categories of tactic doc entry. -/
inductive doc_category
where
| tactic : doc_category
| cmd : doc_category
| hole_cmd : doc_category
| attr : doc_category

/-- Format a `doc_category` -/
/-- The information used to generate a tactic doc entry -/
structure tactic_doc_entry
where
  name : string
  category : doc_category
  decl_names : List name
  tags : List string
  description : string
  inherit_description_from : Option name

/-- Turns a `tactic_doc_entry` into a JSON representation. -/
/-- `update_description_from tde inh_id` replaces the `description` field of `tde` with the
    doc string of the declaration named `inh_id`. -/
/--
`update_description tde` replaces the `description` field of `tde` with:

* the doc string of `tde.inherit_description_from`, if this field has a value
* the doc string of the entry in `tde.decl_names`, if this field has length 1

If neither of these conditions are met, it returns `tde`. -/
/-- A user attribute `tactic_doc` for tagging decls of type `tactic_doc_entry`
for use in doc output -/
/-- Collects everything in the environment tagged with the attribute `tactic_doc`. -/
/-- `add_tactic_doc tde` adds a declaration to the environment
with `tde` as its body and tags it with the `tactic_doc`
attribute. If `tde.decl_names` has exactly one entry `` `decl`` and
if `tde.description` is the empty string, `add_tactic_doc` uses the doc
string of `decl` as the description. -/
/--
A command used to add documentation for a tactic, command, hole command, or attribute.

Usage: after defining an interactive tactic, command, or attribute,
add its documentation as follows.
```lean
/--
describe what the command does here
-/
/--
At various places in mathlib, we leave implementation notes that are referenced from many other
files. To keep track of these notes, we use the command `library_note`. This makes it easy to
retrieve a list of all notes, e.g. for documentation output.

These notes can be referenced in mathlib with the syntax `Note [note id]`.
Often, these references will be made in code comments (`--`) that won't be displayed in docs.
If such a reference is made in a doc string or module doc, it will be linked to the corresponding
note in the doc display.

Syntax:
```
/--
note message
-/
/--
Some declarations work with open expressions, i.e. an expr that has free variables.
Terms will free variables are not well-typed, and one should not use them in tactics like
`infer_type` or `unify`. You can still do syntactic analysis/manipulation on them.
The reason for working with open types is for performance: instantiating variables requires
iterating through the expression. In one performance test `pi_binders` was more than 6x
quicker than `mk_local_pis` (when applied to the type of all imported declarations 100x).
-/
-- See Note [open expressions]

/-- behavior of f -/
-- add docs to core tactics

/--
The congruence closure tactic `cc` tries to solve the goal by chaining
equalities from context and applying congruence (i.e. if `a = b`, then `f a = f b`).
It is a finishing tactic, i.e. it is meant to close
the current goal, not to make some inconclusive progress.
A mostly trivial example would be:

```lean
example (a b c : ℕ) (f : ℕ → ℕ) (h: a = b) (h' : b = c) : f a = f c := by cc
```

As an example requiring some thinking to do by hand, consider:

```lean
example (f : ℕ → ℕ) (x : ℕ)
  (H1 : f (f (f x)) = x) (H2 : f (f (f (f (f x)))) = x) :
  f x = x :=
by cc
```

The tactic works by building an equality matching graph. It's a graph where
the vertices are terms and they are linked by edges if they are known to
be equal. Once you've added all the equalities in your context, you take
the transitive closure of the graph and, for each connected component
(i.e. equivalence class) you can elect a term that will represent the
whole class and store proofs that the other elements are equal to it.
You then take the transitive closure of these equalities under the
congruence lemmas.

The `cc` implementation in Lean does a few more tricks: for example it
derives `a=b` from `nat.succ a = nat.succ b`, and `nat.succ a !=
nat.zero` for any `a`.

* The starting reference point is Nelson, Oppen, [Fast decision procedures based on congruence
closure](http://www.cs.colorado.edu/~bec/courses/csci5535-s09/reading/nelson-oppen-congruence.pdf),
Journal of the ACM (1980)

* The congruence lemmas for dependent type theory as used in Lean are described in
[Congruence closure in intensional type theory](https://leanprover.github.io/papers/congr.pdf)
(de Moura, Selsam IJCAR 2016).
-/
/--
`conv {...}` allows the user to perform targeted rewriting on a goal or hypothesis,
by focusing on particular subexpressions.

See <https://leanprover-community.github.io/extras/conv.html> for more details.

Inside `conv` blocks, mathlib currently additionally provides
* `erw`,
* `ring`, `ring2` and `ring_exp`,
* `norm_num`,
* `norm_cast`,
* `apply_congr`, and
* `conv` (within another `conv`).

`apply_congr` applies congruence lemmas to step further inside expressions,
and sometimes gives between results than the automatically generated
congruence lemmas used by `congr`.

Using `conv` inside a `conv` block allows the user to return to the previous
state of the outer `conv` block after it is finished. Thus you can continue
editing an expression without having to start a new `conv` block and re-scoping
everything. For example:
```lean
example (a b c d : ℕ) (h₁ : b = c) (h₂ : a + c = a + d) : a + b = a + d :=
by conv {
  to_lhs,
  conv {
    congr, skip,
    rw h₁,
  },
  rw h₂,
}
```
Without `conv`, the above example would need to be proved using two successive
`conv` blocks, each beginning with `to_lhs`.

Also, as a shorthand, `conv_lhs` and `conv_rhs` are provided, so that
```lean
example : 0 + 0 = 0 :=
begin
  conv_lhs { simp }
end
```
just means
```lean
example : 0 + 0 = 0 :=
begin
  conv { to_lhs, simp }
end
```
and likewise for `to_rhs`.
-/
/--
Accepts terms with the type `component tactic_state string` or `html empty` and
renders them interactively.
Requires a compatible version of the vscode extension to view the resulting widget.

### Example:

```lean
/-- A simple counter that can be incremented or decremented with some buttons. -/
/--
The `add_decl_doc` command is used to add a doc string to an existing declaration.

```lean
def foo := 5

/--
Doc string for foo.
-/


/-
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Sebastian Ullrich

Term-Level parsers
-/
prelude
import init.lean.parser.level init.lean.parser.notation
import init.lean.expr

namespace Lean
namespace Parser
open Combinators Parser.HasView MonadParsec

local postfix `?`:10000 := optional
local postfix *:10000 := Combinators.many
local postfix +:10000 := Combinators.many1

set_option class.instance_max_depth 200

@[derive Parser.HasTokens Parser.HasView]
def identUnivSpec.Parser : basicParser :=
node! identUnivSpec [".{", levels: Level.Parser+, "}"]

@[derive Parser.HasTokens Parser.HasView]
def identUnivs.Parser : termParser :=
node! identUnivs [id: ident.Parser, univs: (monadLift identUnivSpec.Parser)?]

namespace Term
/-- Access leading Term -/
def getLeading : trailingTermParser := read
instance : HasTokens getLeading := default _
instance : HasView Syntax getLeading := default _

@[derive Parser.HasTokens Parser.HasView]
def paren.Parser : termParser :=
node! «paren» ["(":maxPrec,
  content: node! parenContent [
    Term: Term.Parser,
    special: nodeChoice! parenSpecial {
      /- Do not allow trailing comma. Looks a bit weird and would clash with
      adding support for tuple sections (https://downloads.haskell.org/~ghc/8.2.1/docs/html/usersGuide/glasgowExts.html#tuple-sections). -/
      tuple: node! tuple [", ", tail: sepBy (Term.Parser 0) (symbol ", ") false],
      typed: node! typed [" : ", type: Term.Parser],
    }?,
  ]?,
  ")"
]

@[derive Parser.HasTokens Parser.HasView]
def hole.Parser : termParser :=
node! hole [hole: symbol "_" maxPrec]

@[derive Parser.HasTokens Parser.HasView]
def sort.Parser : termParser :=
nodeChoice! sort {"Sort":maxPrec, "Type":maxPrec}

@[derive HasTokens HasView]
def typeSpec.Parser : termParser :=
node! typeSpec [" : ", type: Term.Parser 0]

@[derive HasTokens HasView]
def optType.Parser : termParser :=
typeSpec.Parser?

instance optType.viewDefault : HasViewDefault optType.Parser _ none := ⟨⟩

section binder
@[derive HasTokens HasView]
def binderIdent.Parser : termParser :=
nodeChoice! binderIdent {id: ident.Parser, hole: hole.Parser}

@[derive HasTokens HasView]
def binderDefault.Parser : termParser :=
nodeChoice! binderDefault {
  val: node! binderDefaultVal [":=", Term: Term.Parser 0],
  tac: node! binderDefaultTac [".", Term: Term.Parser 0],
}

@[derive HasTokens HasView]
def binderContent.Parser (requireType := false) : termParser :=
node! binderContent [
  ids: binderIdent.Parser+,
  type: optional typeSpec.Parser requireType,
  default: binderDefault.Parser?
]

@[derive HasTokens HasView]
def simpleBinder.Parser : termParser :=
nodeChoice! simpleBinder {
  explicit: node! simpleExplicitBinder ["(", id: ident.Parser, " : ", type: Term.Parser 0, right: symbol ")"],
  implicit: node! simpleImplicitBinder ["{", id: ident.Parser, " : ", type: Term.Parser 0, right: symbol "}"],
  strictImplicit: node! simpleStrictImplicitBinder ["⦃", id: ident.Parser, " : ", type: Term.Parser 0, right: symbol "⦄"],
  instImplicit: node! simpleInstImplicitBinder ["[", id: ident.Parser, " : ", type: Term.Parser 0, right: symbol "]"],
}

def simpleBinder.View.toBinderInfo : simpleBinder.View → (BinderInfo × SyntaxIdent × Syntax)
| (simpleBinder.View.explicit {id := id, type := type})       := (BinderInfo.default, id, type)
| (simpleBinder.View.implicit {id := id, type := type})       := (BinderInfo.implicit, id, type)
| (simpleBinder.View.strictImplicit {id := id, type := type}) := (BinderInfo.strictImplicit, id, type)
| (simpleBinder.View.instImplicit {id := id, type := type})   := (BinderInfo.instImplicit, id, type)

@[derive Parser.HasTokens Parser.HasView]
def anonymousConstructor.Parser : termParser :=
node! anonymousConstructor ["⟨":maxPrec, args: sepBy (Term.Parser 0) (symbol ","), "⟩"]

/- All binders must be surrounded with some kind of bracket. (e.g., '()', '{}', '[]').
   We use this feature when parsing examples/definitions/theorems. The goal is to avoid counter-intuitive
   declarations such as:

     example p : False := trivial
     def main proof : False := trivial

   which would be parsed as

     example (p : False) : _ := trivial

     def main (proof : False) : _ := trivial

   where `_` in both cases is elaborated into `True`. This issue was raised by @gebner in the slack channel.

   Remark: we still want implicit delimiters for lambda/pi expressions. That is, we want to
   write

       fun x : t, s
   or
       fun x, s

   instead of

       fun (x : t), s -/
@[derive HasTokens HasView]
def bracketedBinder.Parser (requireType := false) : termParser :=
nodeChoice! bracketedBinder {
  explicit: node! explicitBinder ["(", content: nodeChoice! explicitBinderContent {
    «notation»: command.notationLike.Parser,
    other: binderContent.Parser requireType
  }, right: symbol ")"],
  implicit: node! implicitBinder ["{", content: binderContent.Parser, "}"],
  strictImplicit: node! strictImplicitBinder ["⦃", content: binderContent.Parser, "⦄"],
  instImplicit: node! instImplicitBinder ["[", content: nodeLongestChoice! instImplicitBinderContent {
    named: node! instImplicitNamedBinder [id: ident.Parser, " : ", type: Term.Parser 0],
    anonymous: node! instImplicitAnonymousBinder [type: Term.Parser 0]
  }, "]"],
  anonymousConstructor: anonymousConstructor.Parser,
}

@[derive HasTokens HasView]
def binder.Parser : termParser :=
nodeChoice! binder {
  bracketed: bracketedBinder.Parser,
  unbracketed: binderContent.Parser,
}

@[derive HasTokens HasView]
def bindersExt.Parser : termParser :=
node! bindersExt [
  leadingIds: binderIdent.Parser*,
  remainder: nodeChoice! bindersRemainder {
    type: node! bindersTypes [":", type: Term.Parser 0],
    -- we allow mixing like in `a (b : β) c`, but not `a : α (b : β) c : γ`
    mixed: nodeChoice! mixedBinder {
      bracketed: bracketedBinder.Parser,
      id: binderIdent.Parser,
    }+,
  }?
]

/-- We normalize binders to simpler singleton ones during expansion. -/
@[derive HasTokens HasView]
def binders.Parser : termParser :=
nodeChoice! binders {
  extended: bindersExt.Parser,
  -- a strict subset of `extended`, so only useful after parsing
  simple: simpleBinder.Parser,
}

/-- We normalize binders to simpler ones during expansion. These always-bracketed
    binders are used in declarations and cannot be reduced to nested singleton binders. -/
@[derive HasTokens HasView]
def bracketedBinders.Parser : termParser :=
nodeChoice! bracketedBinders {
  extended: bracketedBinder.Parser*,
  -- a strict subset of `extended`, so only useful after parsing
  simple: simpleBinder.Parser*,
}
end binder

@[derive Parser.HasTokens Parser.HasView]
def lambda.Parser : termParser :=
node! lambda [
  op: unicodeSymbol "λ" "fun" maxPrec,
  binders: binders.Parser,
  ",",
  body: Term.Parser 0
]

@[derive Parser.HasTokens Parser.HasView]
def assume.Parser : termParser :=
node! «assume» [
  "assume ":maxPrec,
  binders: nodeChoice! assumeBinders {
    anonymous: node! assumeAnonymous [": ", type: Term.Parser],
    binders: binders.Parser
  },
  ", ",
  body: Term.Parser 0
]

@[derive Parser.HasTokens Parser.HasView]
def pi.Parser : termParser :=
node! pi [
  op: anyOf [unicodeSymbol "Π" "Pi" maxPrec, unicodeSymbol "∀" "forall" maxPrec],
  binders: binders.Parser,
  ",",
  range: Term.Parser 0
]

@[derive Parser.HasTokens Parser.HasView]
def explicit.Parser : termParser :=
node! explicit [
  mod: nodeChoice! explicitModifier {
    explicit: symbol "@" maxPrec,
    partialExplicit: symbol "@@" maxPrec
  },
  id: identUnivs.Parser
]

@[derive Parser.HasTokens Parser.HasView]
def from.Parser : termParser :=
node! «from» ["from ", proof: Term.Parser]

@[derive Parser.HasTokens Parser.HasView]
def let.Parser : termParser :=
node! «let» [
  "let ",
  lhs: nodeChoice! letLhs {
    id: node! letLhsId [
      id: ident.Parser,
      -- NOTE: after expansion, binders are Empty
      binders: bracketedBinder.Parser*,
      type: optType.Parser,
    ],
    pattern: Term.Parser
  },
  " := ",
  value: Term.Parser,
  " in ",
  body: Term.Parser,
]

@[derive Parser.HasTokens Parser.HasView]
def optIdent.Parser : termParser :=
(try node! optIdent [id: ident.Parser, " : "])?

@[derive Parser.HasTokens Parser.HasView]
def have.Parser : termParser :=
node! «have» [
  "have ",
  id: optIdent.Parser,
  prop: Term.Parser,
  proof: nodeChoice! haveProof {
    Term: node! haveTerm [" := ", Term: Term.Parser],
    «from»: node! haveFrom [", ", «from»: from.Parser],
  },
  ", ",
  body: Term.Parser,
]

@[derive Parser.HasTokens Parser.HasView]
def show.Parser : termParser :=
node! «show» [
  "show ",
  prop: Term.Parser,
  ", ",
  «from»: from.Parser,
]

@[derive Parser.HasTokens Parser.HasView]
def match.Parser : termParser :=
node! «match» [
  "match ",
  scrutinees: sepBy1 Term.Parser (symbol ", ") false,
  type: optType.Parser,
  " with ",
  optBar: (symbol " | ")?,
  equations: sepBy1
    node! «matchEquation» [
      lhs: sepBy1 Term.Parser (symbol ", ") false, ":=", rhs: Term.Parser]
    (symbol " | ") false,
]

@[derive Parser.HasTokens Parser.HasView]
def if.Parser : termParser :=
node! «if» [
  "if ",
  id: optIdent.Parser,
  prop: Term.Parser,
  " then ",
  thenBranch: Term.Parser,
  " else ",
  elseBranch: Term.Parser,
]

@[derive Parser.HasTokens Parser.HasView]
def structInst.Parser : termParser :=
node! structInst [
  "{":maxPrec,
  type: (try node! structInstType [id: ident.Parser, " . "])?,
  «with»: (try node! structInstWith [source: Term.Parser, " with "])?,
  items: sepBy nodeChoice! structInstItem {
    field: node! structInstField [id: ident.Parser, " := ", val: Term.Parser],
    source: node! structInstSource ["..", source: Term.Parser?],
  } (symbol ", "),
  "}",
]

@[derive Parser.HasTokens Parser.HasView]
def Subtype.Parser : termParser :=
node! Subtype [
  "{":maxPrec,
  id: ident.Parser,
  type: optType.Parser,
  "//",
  prop: Term.Parser,
  "}"
]

@[derive Parser.HasTokens Parser.HasView]
def inaccessible.Parser : termParser :=
node! inaccessible [".(":maxPrec, Term: Term.Parser, ")"]

@[derive Parser.HasTokens Parser.HasView]
def anonymousInaccessible.Parser : termParser :=
node! anonymousInaccessible ["._":maxPrec]

@[derive Parser.HasTokens Parser.HasView]
def sorry.Parser : termParser :=
node! «sorry» ["sorry":maxPrec]

def borrowPrec := maxPrec - 1
@[derive Parser.HasTokens Parser.HasView]
def borrowed.Parser : termParser :=
node! borrowed ["@&":maxPrec, Term: Term.Parser borrowPrec]

--- Agda's `(x : e) → f`
@[derive Parser.HasTokens Parser.HasView]
def depArrow.Parser : termParser :=
node! depArrow [binder: bracketedBinder.Parser true, op: unicodeSymbol "→" "->" 25, range: Term.Parser 24]

-- TODO(Sebastian): replace with attribute
@[derive HasTokens]
def builtinLeadingParsers : TokenMap termParser := TokenMap.ofList [
  (`ident, identUnivs.Parser),
  (number.name, number.Parser),
  (stringLit.name, stringLit.Parser),
  ("(", paren.Parser),
  ("(", depArrow.Parser),
  ("_", hole.Parser),
  ("Sort", sort.Parser),
  ("Type", sort.Parser),
  ("λ", lambda.Parser),
  ("fun", lambda.Parser),
  ("Π", pi.Parser),
  ("Pi", pi.Parser),
  ("∀", pi.Parser),
  ("forall", pi.Parser),
  ("⟨", anonymousConstructor.Parser),
  ("@", explicit.Parser),
  ("@@", explicit.Parser),
  ("let", let.Parser),
  ("have", have.Parser),
  ("show", show.Parser),
  ("assume", assume.Parser),
  ("match", match.Parser),
  ("if", if.Parser),
  ("{", structInst.Parser),
  ("{", Subtype.Parser),
  ("{", depArrow.Parser),
  ("[", depArrow.Parser),
  (".(", inaccessible.Parser),
  ("._", anonymousInaccessible.Parser),
  ("sorry", sorry.Parser),
  ("@&", borrowed.Parser)
]

@[derive Parser.HasTokens Parser.HasView]
def sortApp.Parser : trailingTermParser :=
do { l ← getLeading, guard $ l.isOfKind sort } *>
node! sortApp [fn: getLeading, Arg: monadLift (Level.Parser maxPrec).run]

@[derive Parser.HasTokens Parser.HasView]
def app.Parser : trailingTermParser :=
node! app [fn: getLeading, Arg: Term.Parser maxPrec]

def mkApp (fn : Syntax) (args : List Syntax) : Syntax :=
args.foldl (λ fn Arg, Syntax.mkNode app [fn, Arg]) fn

@[derive Parser.HasTokens Parser.HasView]
def arrow.Parser : trailingTermParser :=
node! arrow [dom: getLeading, op: unicodeSymbol "→" "->" 25, range: Term.Parser 24]

@[derive Parser.HasView]
def projection.Parser : trailingTermParser :=
try $ node! projection [
  Term: getLeading,
  -- do not consume trailing whitespace
  «.»: rawStr ".",
  proj: nodeChoice! projectionSpec {
    id: Parser.ident.Parser,
    num: number.Parser,
  },
]

-- register '.' manually because of `rawStr`
instance projection.tokens : HasTokens projection.Parser :=
/- Use maxPrec + 1 so that it bind more tightly than application:
   `a (b).c` should be parsed as `a ((b).c)`. -/
⟨[{«prefix» := ".", lbp := maxPrec.succ}]⟩

@[derive HasTokens]
def builtinTrailingParsers : TokenMap trailingTermParser := TokenMap.ofList [
  ("→", arrow.Parser),
  ("->", arrow.Parser),
  (".", projection.Parser)
]

end Term

private def trailing (cfg : CommandParserConfig) : trailingTermParser :=
-- try local parsers first, starting with the newest one
(do ps ← indexed cfg.localTrailingTermParsers, ps.foldr (<|>) (error ""))
<|>
-- next try all non-local parsers
(do ps ← indexed cfg.trailingTermParsers, longestMatch ps)
<|>
-- The application parsers should only be tried as a fall-back;
-- e.g. `a + b` should not be parsed as `a (+ b)`.
-- TODO(Sebastian): We should be able to remove this workaround using
-- the proposed more robust precedence handling
anyOf [Term.sortApp.Parser, Term.app.Parser]

private def leading (cfg : CommandParserConfig) : termParser :=
(do ps ← indexed cfg.localLeadingTermParsers, ps.foldr (<|>) (error ""))
<|>
(do ps ← indexed cfg.leadingTermParsers, longestMatch ps)

def termParser.run (p : termParser) : commandParser :=
do cfg ← read,
   adaptReader coe $ prattParser (leading cfg) (trailing cfg) p

end Parser
end Lean


/-
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich
-/
import Std.ShareCommon
import Lean.Parser.Command
import Lean.Util.CollectLevelParams
import Lean.Util.FoldConsts
import Lean.Meta.ForEachExpr
import Lean.Meta.CollectFVars
import Lean.Elab.Command
import Lean.Elab.SyntheticMVars
import Lean.Elab.Binders
import Lean.Elab.DeclUtil
namespace Lean.Elab

inductive DefKind where
  | «def» | «theorem» | «example» | «opaque» | «abbrev»
  deriving Inhabited, BEq

def DefKind.isTheorem : DefKind → Bool
  | «theorem» => true
  | _         => false

def DefKind.isDefOrAbbrevOrOpaque : DefKind → Bool
  | «def»    => true
  | «opaque» => true
  | «abbrev» => true
  | _        => false

def DefKind.isExample : DefKind → Bool
  | «example» => true
  | _         => false

structure DefView where
  kind          : DefKind
  ref           : Syntax
  modifiers     : Modifiers
  declId        : Syntax
  binders       : Syntax
  type?         : Option Syntax
  value         : Syntax
  deriving?     : Option (Array Syntax) := none
  deriving Inhabited

namespace Command

open Meta

def mkDefViewOfAbbrev (modifiers : Modifiers) (stx : Syntax) : DefView :=
  -- leading_parser "abbrev " >> declId >> optDeclSig >> declVal
  let (binders, type) := expandOptDeclSig stx[2]
  let modifiers       := modifiers.addAttribute { name := `inline }
  let modifiers       := modifiers.addAttribute { name := `reducible }
  { ref := stx, kind := DefKind.abbrev, modifiers,
    declId := stx[1], binders, type? := type, value := stx[3] }

def mkDefViewOfDef (modifiers : Modifiers) (stx : Syntax) : DefView :=
  -- leading_parser "def " >> declId >> optDeclSig >> declVal >> optDefDeriving
  let (binders, type) := expandOptDeclSig stx[2]
  let deriving? := if stx[4].isNone then none else some stx[4][1].getSepArgs
  { ref := stx, kind := DefKind.def, modifiers,
    declId := stx[1], binders, type? := type, value := stx[3], deriving? }

def mkDefViewOfTheorem (modifiers : Modifiers) (stx : Syntax) : DefView :=
  -- leading_parser "theorem " >> declId >> declSig >> declVal
  let (binders, type) := expandDeclSig stx[2]
  { ref := stx, kind := DefKind.theorem, modifiers,
    declId := stx[1], binders, type? := some type, value := stx[3] }

def mkFreshInstanceName : CommandElabM Name := do
  let s ← get
  let idx := s.nextInstIdx
  modify fun s => { s with nextInstIdx := s.nextInstIdx + 1 }
  return Lean.Elab.mkFreshInstanceName s.env idx

/--
  Generate a name for an instance with the given type.
  Note that we elaborate the type twice. Once for producing the name, and another when elaborating the declaration. -/
def mkInstanceName (binders : Array Syntax) (type : Syntax) : CommandElabM Name := do
  let savedState ← get
  try
    let result ← runTermElabM `inst fun _ => Term.withAutoBoundImplicit <| Term.elabBinders binders fun _ => Term.withoutErrToSorry do
      let type ← instantiateMVars (← Term.elabType type)
      let ref ← IO.mkRef ""
      Meta.forEachExpr type fun e => do
        if e.isForall then ref.modify (. ++ "ForAll")
        else if e.isProp then ref.modify (. ++ "Prop")
        else if e.isType then ref.modify (. ++ "Type")
        else if e.isSort then ref.modify (. ++ "Sort")
        else if e.isConst then
          match e.constName!.eraseMacroScopes with
          | Name.str _ str _ =>
              if str[0].isLower then
                ref.modify (. ++ str.capitalize)
              else
                ref.modify (. ++ str)
          | _ => pure ()
      ref.get
    set savedState
    liftMacroM <| mkUnusedBaseName <| Name.mkSimple ("inst" ++ result)
  catch ex =>
    set savedState
    mkFreshInstanceName

def mkDefViewOfInstance (modifiers : Modifiers) (stx : Syntax) : CommandElabM DefView := do
  -- leading_parser Term.attrKind >> "instance " >> optNamedPrio >> optional declId >> declSig >> declVal
  let attrKind        ← liftMacroM <| toAttributeKind stx[0]
  let prio            ← liftMacroM <| expandOptNamedPrio stx[2]
  let attrStx         ← `(attr| instance $(quote prio):numLit)
  let (binders, type) := expandDeclSig stx[4]
  let modifiers       := modifiers.addAttribute { kind := attrKind, name := `instance, stx := attrStx }
  let declId ← match stx[3].getOptional? with
    | some declId => pure declId
    | none        =>
      let id ← mkInstanceName binders.getArgs type
      pure <| mkNode ``Parser.Command.declId #[mkIdentFrom stx id, mkNullNode]
  return {
    ref := stx, kind := DefKind.def, modifiers := modifiers,
    declId := declId, binders := binders, type? := type, value := stx[5]
  }

def mkDefViewOfConstant (modifiers : Modifiers) (stx : Syntax) : CommandElabM DefView := do
  -- leading_parser "constant " >> declId >> declSig >> optional declValSimple
  let (binders, type) := expandDeclSig stx[2]
  let val ← match stx[3].getOptional? with
    | some val => pure val
    | none     =>
      let val ← `(default_or_ofNonempty%)
      pure <| mkNode ``Parser.Command.declValSimple #[ mkAtomFrom stx ":=", val ]
  return {
    ref := stx, kind := DefKind.opaque, modifiers := modifiers,
    declId := stx[1], binders := binders, type? := some type, value := val
  }

def mkDefViewOfExample (modifiers : Modifiers) (stx : Syntax) : DefView :=
  -- leading_parser "example " >> declSig >> declVal
  let (binders, type) := expandDeclSig stx[1]
  let id              := mkIdentFrom stx `_example
  let declId          := mkNode ``Parser.Command.declId #[id, mkNullNode]
  { ref := stx, kind := DefKind.example, modifiers := modifiers,
    declId := declId, binders := binders, type? := some type, value := stx[2] }

def isDefLike (stx : Syntax) : Bool :=
  let declKind := stx.getKind
  declKind == ``Parser.Command.«abbrev» ||
  declKind == ``Parser.Command.«def» ||
  declKind == ``Parser.Command.«theorem» ||
  declKind == ``Parser.Command.«constant» ||
  declKind == ``Parser.Command.«instance» ||
  declKind == ``Parser.Command.«example»

def mkDefView (modifiers : Modifiers) (stx : Syntax) : CommandElabM DefView :=
  let declKind := stx.getKind
  if declKind == ``Parser.Command.«abbrev» then
    return mkDefViewOfAbbrev modifiers stx
  else if declKind == ``Parser.Command.«def» then
    return mkDefViewOfDef modifiers stx
  else if declKind == ``Parser.Command.«theorem» then
    return mkDefViewOfTheorem modifiers stx
  else if declKind == ``Parser.Command.«constant» then
    mkDefViewOfConstant modifiers stx
  else if declKind == ``Parser.Command.«instance» then
    mkDefViewOfInstance modifiers stx
  else if declKind == ``Parser.Command.«example» then
    return mkDefViewOfExample modifiers stx
  else
    throwError "unexpected kind of definition"

builtin_initialize registerTraceClass `Elab.definition

end Command
end Lean.Elab


import tactic.interactive

namespace tactic.interactive

meta def get_nat (x : expr) : tactic unit :=
begin

end

end tactic.interactive

lemma (n : ℕ) :


import Lean
import Verbose.Common

open Lean Parser Meta Elab Tactic Option

/- Restore rewrite using a single term without brackets. -/
declare_syntax_cat myRwRuleSeq
syntax rwRule : myRwRuleSeq
syntax "[" rwRule,*,? "]" : myRwRuleSeq

/--
We rewrite
-/
macro (name := weRewrite) rw:"We" "rewrite using" c:(config)? s:myRwRuleSeq l:(location)? : tactic =>
  match s with
  | `(myRwRuleSeq| [%$lbrak $rs:rwRule,* ]%$rbrak) =>
    -- We show the `rfl` state on `]`
    `(tactic| rewrite%$rw $(c)? [%$lbrak $rs,*] $(l)?; try (with_reducible rfl%$rbrak))
  | `(myRwRuleSeq| $rs:rwRule) =>
    `(tactic| rewrite%$rw $(c)? [$rs] $(l)?; try (with_reducible rfl))
  | _ => Macro.throwUnsupported

example (a b : Nat) (h : a = b) (h' : b = 0): a = 0 := by
  We rewrite using ← h at h'
  exact h'

def discussOr (input : Term) : TacticM Unit := do
    evalApplyLikeTactic Meta.apply <| ← `(Or.elim $input)

elab "We" "discuss using" exp:term : tactic =>
  discussOr exp

example (P Q : Prop) (h : P ∨ Q) : True := by
  We discuss using h
  . intro _hP
    trivial
  . intro _hQ
    trivial

macro "We" "discuss depending on" exp:term : tactic =>
`(tactic| We discuss using Classical.em $exp)

example (P : Prop) : True := by
  We discuss depending on P
  . intro _hP
    trivial
  . intro _hnP
    trivial


/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Meta.Basic

namespace Lean.Elab.Term

/--
  Set `isDefEq` configuration for the elaborator.
  Note that we enable all approximations but `quasiPatternApprox`

  In Lean3 and Lean 4, we used to use the quasi-pattern approximation during elaboration.
  The example:
  ```
  def ex : StateT δ (StateT σ Id) σ :=
  monadLift (get : StateT σ Id σ)
  ```
  demonstrates why it produces counterintuitive behavior.
  We have the `Monad-lift` application:
  ```
  @monadLift ?m ?n ?c ?α (get : StateT σ id σ) : ?n ?α
  ```
  It produces the following unification problem when we process the expected type:
  ```
  ?n ?α =?= StateT δ (StateT σ id) σ
  ==> (approximate using first-order unification)
  ?n := StateT δ (StateT σ id)
  ?α := σ
  ```
  Then, we need to solve:
  ```
  ?m ?α =?= StateT σ id σ
  ==> instantiate metavars
  ?m σ =?= StateT σ id σ
  ==> (approximate since it is a quasi-pattern unification constraint)
  ?m := fun σ => StateT σ id σ
  ```
  Note that the constraint is not a Milner pattern because σ is in
  the local context of `?m`. We are ignoring the other possible solutions:
  ```
  ?m := fun σ' => StateT σ id σ
  ?m := fun σ' => StateT σ' id σ
  ?m := fun σ' => StateT σ id σ'
  ```

  We need the quasi-pattern approximation for elaborating recursor-like expressions (e.g., dependent `match with` expressions).

  If we had use first-order unification, then we would have produced
  the right answer: `?m := StateT σ id`

  Haskell would work on this example since it always uses
  first-order unification.
-/
def setElabConfig (cfg : Meta.Config) : Meta.Config :=
  { cfg with foApprox := true, ctxApprox := true, constApprox := false, quasiPatternApprox := false }

end Lean.Elab.Term