Require Import List Lia Relation_Definitions Relation_Operators Operators_Properties.
Import ListNotations.
Require Import Undecidability.SystemF.SysF Undecidability.SystemF.Autosubst.syntax Undecidability.SystemF.Autosubst.unscoped.
Import UnscopedNotations.
From Undecidability.SystemF.Util Require Import Facts poly_type_facts pure_term_facts term_facts typing_facts iipc2_facts pure_typing_facts.

Require Import ssreflect ssrbool ssrfun.

Set Default Goal Selector "!".

Definition K' M N := pure_app (pure_abs (ren_pure_term S M)) N.

Lemma pure_typing_K'I {Gamma M N t}:
  pure_typing M t pure_typable N pure_typing (K' M N) t.
Proof.
  move HM [tN HtN]. apply: (pure_typing_pure_app_simpleI (s := tN)); last done.
  apply: pure_typing_pure_abs_simpleI. apply: pure_typing_ren_pure_term; [by eassumption | done].
Qed.


Lemma pure_typing_K'E {Gamma M N t} : pure_typing (K' M N) t
  pure_typing M t pure_typable N.
Proof.
  rewrite /K'.
  move /pure_typingE [n] [?] [?] [?] [+] [+] [H1C ] /pure_typingE'.
  move /(pure_typing_ren_pure_term_allfv_pure_term Nat.pred ( := (map (ren_poly_type (Nat.add n)) ))).
  apply: unnest.
  { rewrite allfv_pure_term_ren_pure_term /=. by apply: allfv_pure_term_TrueI. }
  rewrite renRen_pure_term ren_pure_term_id.
  move /(pure_typing_contains H1C) /pure_typing_many_poly_absI HM.
  move /(pure_typing_ren_poly_type ( x x - n)) /pure_typableI HN.
  constructor; first done.
  congr pure_typable: HN. rewrite map_map. apply: map_id' ?.
  rewrite ?poly_type_norm ren_poly_type_id' /=; by [|].
Qed.


Lemma pure_typable_K'I {Gamma M N}:
  pure_typable M pure_typable N pure_typable (K' M N).
Proof. move [tM] ? ?. exists tM. by apply: pure_typing_K'I. Qed.

Lemma pure_typable_K'E {Gamma M N} : pure_typable (K' M N)
  pure_typable M pure_typable N.
Proof. by move [?] /pure_typing_K'E [/pure_typableI]. Qed.

Fixpoint leftmost_poly_var (t: poly_type) :=
  match t with
  | poly_var x Some x
  | poly_arr s t leftmost_poly_var s
  | poly_abs t if leftmost_poly_var t is Some (S x) then Some x else None
  end.

Fixpoint leftmost_path_length (t: poly_type) :=
  match t with
  | poly_var x 0
  | poly_arr s t 1 + leftmost_path_length s
  | poly_abs t leftmost_path_length t
  end.

Lemma leftmost_poly_var_ren_poly_type {ξ t} :
  leftmost_poly_var (ren_poly_type ξ t) = omap ξ (leftmost_poly_var t).
Proof.
  elim: t ξ; [done | by move ? + ? _ ? /= |].
  move t + ? /= . case: (leftmost_poly_var t); [by case | done].
Qed.


Lemma leftmost_poly_var_subst_poly_type {σ t} :
  leftmost_poly_var (subst_poly_type σ t) = obind (σ >> leftmost_poly_var) (leftmost_poly_var t).
Proof.
  elim: t σ; [done | by move ? + ? _ ? /= |].
  move t + σ /= . case: (leftmost_poly_var t); last done.
  move [|x] /=; first done.
  rewrite /funcomp /= leftmost_poly_var_ren_poly_type.
  by case: (leftmost_poly_var _).
Qed.


Lemma leftmost_poly_var_many_poly_abs {n t x} :
  leftmost_poly_var (many_poly_abs n t) = Some x
  leftmost_poly_var t = Some (n+x).
Proof.
  elim: n x; first done.
  move n /= IH x. have : S (n + x) = n + S x by .
  rewrite -IH. case: (leftmost_poly_var (many_poly_abs n t)); last done.
  move [|y]; first done. constructor; by move [].
Qed.


Lemma leftmost_path_length_ren_poly_type {ξ t} :
  leftmost_path_length (ren_poly_type ξ t) = leftmost_path_length t.
Proof.
  elim: t ξ; [done | by move ? + ? _ ? /= |].
  move t + ? /= . case: (leftmost_poly_var t); [by case | done].
Qed.


Lemma leftmost_path_length_subst_poly_type {σ t} :
  leftmost_path_length (subst_poly_type σ t) =
  (if leftmost_poly_var t is Some x then leftmost_path_length (σ x) else 0) +
    leftmost_path_length t.
Proof.
  elim: t σ; [done | by move ? + ? _ ? /= |].
  move t + σ /= . case: (leftmost_poly_var t); last done.
  move [|x] /=; first done.
  by rewrite /funcomp /= leftmost_path_length_ren_poly_type.
Qed.


Lemma leftmost_path_length_many_poly_abs {n t} :
  leftmost_path_length (many_poly_abs n t) = leftmost_path_length t.
Proof. by elim: n. Qed.

Lemma contains_leftmost_path_length_eq {n s t} :
  contains (many_poly_abs n s) t
  not (is_poly_abs s)
  match leftmost_poly_var s with
  | None leftmost_path_length s = leftmost_path_length t
  | Some x n x leftmost_path_length s = leftmost_path_length t
  end.
Proof.
  move Es': (many_poly_abs n s) s' /clos_rt_rt1n_iff .
  elim: n s Es'.
  - move > _. rewrite ?leftmost_path_length_many_poly_abs.
    by case: (leftmost_poly_var _).
  - move > [] r > _ IH [|n]; first by case /=.
    move s [?]. subst. evar (s'' : poly_type).
    have := IH n s''. rewrite subst_poly_type_many_poly_abs.
    subst s'' /(_ ltac:(done)).
    move: s {IH} [x | s {}t | /=]; last done.
    + move /= + _ ?.
      have : x = n + (1 + (x - n - 1)) by .
      rewrite ?iter_up_poly_type_poly_type ?leftmost_poly_var_ren_poly_type
          ?leftmost_path_length_ren_poly_type /=.
      apply; by [|].
    + move /(_ ltac:(done)).
      rewrite leftmost_poly_var_subst_poly_type /=.
      move Eox: (leftmost_poly_var s) ox. case: ox Eox.
      * move x Hsx /= + _ ?.
        rewrite leftmost_path_length_subst_poly_type Hsx.
        have : x = n + (1 + (x - n - 1)) by .
        rewrite ?iter_up_poly_type_poly_type ?leftmost_poly_var_ren_poly_type
          ?leftmost_path_length_ren_poly_type /=.
        apply. by .
      * move /= Hs _. by rewrite leftmost_path_length_subst_poly_type Hs.
Qed.


Lemma pure_typable_self_application {Gamma x n t} :
  not (is_poly_abs t)
  nth_error x = Some (many_poly_abs n t)
  pure_typable (pure_app (pure_var x) (pure_var x))
   y, y < n leftmost_poly_var t = Some y.
Proof.
  move Ht Hx /pure_typableE [?] [?] [].
  move /pure_typingE' [?] []. rewrite Hx [[]] H1C.
  move /pure_typingE [] [?] [s'] [+] [].
  rewrite nth_error_map Hx [[?]] H2C ?. subst.
  move: Ht H1C H2C. clear Ht + /contains_ren_poly_type_addLR.
  move /contains_leftmost_path_length_eq + /contains_leftmost_path_length_eq.
  move /(_ Ht) + /(_ Ht).
  case: (leftmost_poly_var t).
  - move y H1y H2y. exists y. constructor; last done.
    suff: (not (n y)) by .
    move /copy [/H1y + /H2y] /=.
    rewrite leftmost_path_length_many_poly_abs leftmost_path_length_ren_poly_type.
    by .
  - move /=.
    rewrite leftmost_path_length_many_poly_abs leftmost_path_length_ren_poly_type.
    by .
Qed.


Definition Mω := pure_abs (pure_app (pure_var 0) (pure_var 0)).

Lemma pure_typing_Mω {Gamma t} :
  pure_typing Mω t
   n1 n2 s' t' y,
    t = many_poly_abs (poly_arr (many_poly_abs s') t')
    y < leftmost_poly_var s' = Some y.
Proof.
  rewrite /Mω. move /pure_typingE [] [s] [t'] [+ ].
  have := many_poly_absI s. move [n] [s'] [].
  move + /pure_typableI /pure_typable_self_application.
  move + H /H /= /(_ _ ltac:(done)) [y] [? ?].
  by exists , n, s', t', y.
Qed.


Definition M_Wells N :=
  pure_abs (K' (pure_var 0) (K' (pure_abs (pure_app (pure_var 1) (pure_app (pure_var 1) (pure_var 0)))) N)).

Definition M_Wells_J N := pure_app
  (pure_abs (K' (pure_app (pure_var 0) (pure_var 0)) (pure_app (pure_var 0) Mω)))
  (M_Wells N).

Lemma leftmost_poly_var_contains_poly_arr {s x s' t'} :
  leftmost_poly_var s = Some x
  contains s (poly_arr s' t')
   n'' s'' t'', s = many_poly_abs n'' (poly_arr s'' t'').
Proof.
  have [n'' [s'' [?]]] := many_poly_absI s. subst.
  case Es'': (s''); [ move _ | by move *; do 3 eexists | by move /=].
  rewrite leftmost_poly_var_many_poly_abs /=.
  move [?]. subst /contains_tidyI.
  rewrite tidy_many_poly_abs_le /=; first by .
  by move /containsE.
Qed.


Lemma pure_typable_pure_app_0_Mω {n1 n2 s x t1 t2 Gamma} :
  leftmost_poly_var s = Some ( + x)
  pure_typable
    (many_poly_abs (poly_arr (many_poly_abs (poly_arr s )) ) :: )
    (pure_app (pure_var 0) Mω)
   n, s = many_poly_abs n (poly_var (n + ( + x))).
Proof.
  move H /pure_typableE [?] [?] [/pure_typingE'] [?] [[]].
  move /contains_many_poly_absE [?] [?].
  set σ := (fold_right _ _ _). rewrite subst_poly_type_many_poly_abs /=.
  set n := (_ - length _). case Hn: (n); last done. subst n.
  move [? ?]. subst /pure_typing_Mω [?] [?] [?] [?] [?] [+] [?].
  rewrite subst_poly_type_many_poly_abs /=.
  move /many_poly_abs_eqE' [?] [+ ?]. subst.
  have [n' [s' [?]]] := many_poly_absI s. subst.
  case Es': (s'); [ | | by move /=].
  - subst _. move: H. rewrite leftmost_poly_var_many_poly_abs /=.
    move [?]. subst *. by eexists.
  - rewrite subst_poly_type_many_poly_abs /=.
    move _ /esym /many_poly_abs_eqE'' /(_ ltac:(done)) [?] [?] ?. subst.
    move: H. rewrite ?leftmost_poly_var_many_poly_abs /= leftmost_poly_var_subst_poly_type.
    move . rewrite /= ?iter_up_poly_type_poly_type ?leftmost_poly_var_ren_poly_type.
    case: (leftmost_poly_var _); last done.
    move ? /= []. by .
Qed.


Lemma pure_typable_pure_app_0_0' {n1 n2 n3 n4 x y n5 s t Gamma}:
  pure_typable
    (many_poly_abs
      (poly_arr
          (many_poly_abs
            (poly_arr (many_poly_abs (poly_var ( + ( + x)))) (many_poly_abs (poly_var y))))
          (many_poly_abs (poly_arr s t))) :: )
    (pure_app (pure_var 0) (pure_var 0))
   y', y = + ( + y').
Proof.
  move /pure_typableE [?] [?] [/pure_typingE'] [?] [[?]] H1c. subst.
  move /pure_typingE [] [?] [?] [[?]] [H2c ?]. subst.
  move: H1c /contains_poly_arrE [?] [?] /= [+ ?]. subst.
  set σ := (fold_right _ _ _).
  rewrite subst_poly_type_many_poly_abs /=.
  move /many_poly_abs_eqE'' /(_ ltac:(done)) [] [? ?]. subst.
  move: H2c. rewrite ren_poly_type_many_poly_abs /=.
  move /contains_sub' [_].
  rewrite ren_poly_type_many_poly_abs /= many_poly_abs_many_poly_abs.
  move /contains_sub [?] [?] [?] [+] _.
  rewrite subst_poly_type_many_poly_abs /= many_poly_abs_many_poly_abs.
  move /many_poly_abs_eqE'' /(_ ltac:(done)) [] [+ ?]. subst.
  have [?|?] : y < + + + + y by .
  - rewrite iter_plus iter_up_poly_type_poly_type_lt; first by .
    by case: .
  - move _. exists (y - - - ). by .
Qed.


Theorem pure_typable_intro_poly_arr_0_0 M : { N |
   Gamma,
    pure_typable (map tidy ((poly_arr (poly_var 0) (poly_var 0)) :: (map (ren_poly_type S) ))) M
    pure_typable (map tidy ) N }.
Proof.
  exists (M_Wells_J M) . constructor.
  - pose tI := poly_arr (poly_var 0) (poly_var 0).
    move HM. rewrite /M_Wells_J. pose := poly_arr tI tI. exists .
    apply: (pure_typing_pure_app_simpleI (s := poly_abs )).
    + apply: pure_typing_pure_abs_simpleI. apply: pure_typing_K'I.
      * apply: (pure_typing_pure_app_simpleI (s := )).
        ** apply: (pure_typing_pure_var 0); [done | by apply /rt_step /contains_step_subst].
        ** apply: (pure_typing_pure_var 0); [done | by apply /rt_step /contains_step_subst].
      * pose := poly_arr (poly_abs (poly_var 0)) (poly_abs (poly_var 0)). exists .
        apply: (pure_typing_pure_app_simpleI (s := )).
        ** apply: (pure_typing_pure_var 0); [done | by apply /rt_step /contains_step_subst].
        ** apply: pure_typing_pure_abs_simpleI.
           apply: (pure_typing_pure_app_simpleI (s := (poly_abs (poly_var 0)))).
           *** apply: (pure_typing_pure_var 0); [done | by apply /rt_step /contains_step_subst].
           *** apply: (pure_typing_pure_var 0); [done | by apply /rt_step /contains_step_subst].
    + rewrite /M_Wells. apply: (pure_typing_pure_abs 1).
      apply: pure_typing_K'I; first by apply: pure_typing_pure_var_simpleI.
      apply: pure_typable_K'I.
      * exists tI. apply: pure_typing_pure_abs_simpleI.
        apply: pure_typing_pure_app_simpleI; first by apply: pure_typing_pure_var_simpleI.
        apply: pure_typing_pure_app_simpleI; by apply: pure_typing_pure_var_simpleI.
      * move: HM. rewrite /= ?map_map. congr pure_typable. congr cons.
        apply: map_ext ?. by rewrite tidy_ren_poly_type.
  - rewrite /M_Wells_J. move /pure_typableE [s] [?] [].
    move /pure_typingE' /pure_typableI /pure_typable_K'E [].
    have [ns [s' [? Hs]]] := many_poly_absI s. subst s. rename s' into s.
    move /copy [] /pure_typable_self_application /(_ _ _ ltac:(eassumption) ltac:(done)).
    move [y [Hyns Hsy]] H. rewrite /M_Wells.
    move /pure_typingE [nM] [?] [?] [+ /many_poly_abs_eqE'].
    case Es: (s); [by move + [] | | by subst s].
    clear Hs + [?] [? ?]. subst.
    move /pure_typing_K'E [] /pure_typingE [?] [?] [?] [[?]] [H1c ?]. subst.
    move /pure_typable_K'E [/pure_typableE] [?] /pure_typableE [?] [?] [].
    move /pure_typingE' [?] [[?]] H2c. subst.
    move /pure_typingE [?] [?] [?] [?] [+] [_] [H3c ?]. subst.
    move /pure_typingE' [?] [[?]] H4c HM. subst.
    move: (Hsy) /= /leftmost_poly_var_contains_poly_arr /(_ _ _ ltac:(eassumption)).
    move [?] [?] [?] ?. subst.

    move: Hsy. rewrite /= leftmost_poly_var_many_poly_abs /=.
    move: H /pure_typable_pure_app_0_Mω H /H{H} [? ?]. subst.

    move: H2c /contains_poly_arrE [?] [?] [+ ?]. subst.
    rewrite subst_poly_type_many_poly_abs /= iter_up_poly_type_poly_type.
    rewrite fold_right_length_ts ?iter_up_poly_type_poly_type /=.
    move /many_poly_abs_eqE'' /(_ ltac:(done)) [?] [? ?]. subst.

    move: H4c. rewrite ren_poly_type_many_poly_abs /=.
    move /contains_poly_arrE [?] [?] [? ?]. subst.

    move: (H3c) /contains_tidyI. rewrite tidy_many_poly_abs_le /=; first by .
    move /contains_poly_varE [?] [?].
    set t := (t in ren_poly_type _ t).
    have [nt' [t' [?]]] := many_poly_absI t. subst t. subst.
    case Et': (t'); [move _ | move _ | by subst t'; move /= ].
    + subst t'.
      move: (H1c). rewrite ren_poly_type_many_poly_abs /=.
      move /contains_sub [?] [?] [?] [?] _. subst.
      move: . rewrite many_poly_abs_many_poly_abs.
      move /pure_typable_pure_app_0_0' [y' ?] _. subst.

      move: H3c /contains_tidyI. rewrite tidy_many_poly_abs_le /=; first by .
      rewrite tidy_subst_poly_type tidy_ren_poly_type tidy_many_poly_abs_le /=; first by .

      rewrite iter_up_ren_ge; first by .
      rewrite fold_right_length_ts_ge /=; first by .
      move /containsE [?]. have ? : y = y' by . subst.

      move: HM [?] /pure_typing_tidyI /=.
      rewrite tidy_many_poly_abs_le /=.
      { rewrite ?allfv_poly_type_many_poly_abs /= ?iter_scons_ge; by . }
      rewrite tidy_many_poly_abs_le /=; first by .
      rewrite tidy_many_poly_abs_le /=; first by .

      move /(pure_typing_ren_poly_type ( x x - (nM - 1))) /= /pure_typableI.
      congr pure_typable. congr cons.
      * congr poly_arr; congr poly_var; by .
      * rewrite ?map_map. apply: map_ext ?.
        rewrite ?tidy_ren_poly_type tidy_tidy ?poly_type_norm /=.
        apply: extRen_poly_type. by .
    + rewrite ren_poly_type_many_poly_abs subst_poly_type_many_poly_abs /=.
      rewrite (svalP tidy_many_poly_abs).
      by move: (sval _) [? ?] /many_poly_abs_eqE' /= [].
Qed.


Theorem pure_typable_intro_poly_bot M : { N |
   Gamma,
    pure_typable (map tidy ((poly_abs (poly_var 0)) :: )) M
    pure_typable (map tidy ) N }.
Proof.
  exists (pure_abs M) . constructor.
  - by move [tM] /= /pure_typing_pure_abs_simpleI /pure_typableI.
  - move /pure_typableE [s] [t] HM /=. exists t.
    apply: (pure_typing_weaken_closed (s' := s) ( := [])); [done | | done].
    apply: (pure_typing_pure_var 0); [done | by apply /rt_step /contains_step_subst].
Qed.


Theorem pure_typable_intro_poly_var_0 M : { N |
   Gamma,
    pure_typable (map tidy (poly_var 0 :: poly_arr (poly_var 0) (poly_var 0) :: (map (ren_poly_type S) ))) M
    pure_typable (map tidy ) N }.
Proof.
  pose N := pure_app (ren_pure_term S (pure_abs M)) (pure_app (pure_var 1) (pure_var 0)).
  have [N' HN'] := pure_typable_intro_poly_bot N.
  have [N'' HN''] := pure_typable_intro_poly_arr_0_0 N'.
  exists N'' . constructor.
  - move [tM] HtM. apply /HN'' /HN' {HN'' HN'}.
    eexists. apply: (pure_typing_pure_app_simpleI (s := poly_var 0)).
    + move /=. apply: pure_typing_pure_abs_simpleI.
      apply: pure_typing_ren_pure_term; first by eassumption. by case.
    + apply: (pure_typing_pure_app_simpleI (s := poly_var 0)).
      * by apply: pure_typing_pure_var_simpleI.
      * apply: (pure_typing_pure_var 0); first by reflexivity.
        by apply /rt_step /(contains_step_subst (s := poly_var 0)).
  - move /HN'' /HN' {HN'' HN'}. rewrite /N.
    move /pure_typableE [?] [?] [] /= /pure_typingE' HM.
    move /pure_typingE [] [?] [?] [?] [+] [+] [H2C ?]. subst.
    move /pure_typingE' [?] [[?]] H3C _. subst.
    move: H3C /containsE [H1E ?]. subst.
    move: H2C /containsE ?. subst.
    move: HM /=.
    set s := (s in pure_typing (s :: _) _ _).
    set := ( in pure_typing (s :: _ :: ) _ _).

    move /(pure_typing_ren_pure_term_allfv_pure_term (scons 0 (scons 1 S)) ( := s :: )).
    apply: unnest.
    { rewrite allfv_pure_term_ren_pure_term /=. apply: allfv_pure_term_TrueI. by case. }
    rewrite renRen_pure_term /= ren_pure_term_id'; first by case.
    subst s .
    move /pure_typing_tidyI /=. rewrite tidy_many_poly_abs_le /=; first by .
    move /pure_typableI.
    congr pure_typable. congr cons; last congr cons.
    + congr poly_var. by .
    + rewrite ?map_map. apply: map_ext ?. by rewrite tidy_tidy.
Qed.


Ltac first_pure_typingE :=
  match goal with
  | |- pure_typing _ (pure_app _ _) (poly_var _) _
    move /pure_typingE' [?] [?] [+] [+] ?
  | |- pure_typing _ (pure_app _ _) (poly_arr _ _) _
    move /pure_typingE' [?] [?] [+] [+] ?
  | |- pure_typing _ (pure_app _ _) _ _
    move /pure_typingE [?] [?] [?] [?] [+] [+] [? ?]
  | |- pure_typing _ (pure_abs _) (poly_arr _ _) _
    move /pure_typingE'
  | |- pure_typing _ (pure_abs _) _ _
    move /pure_typingE [?] [?] [?] [+ ?]
  | |- pure_typing _ (pure_var _) (poly_var _) _
    move /pure_typingE' [?] [[?]] ?
  | |- pure_typing _ (pure_var _) (poly_arr _ _) _
    move /pure_typingE' [?] [[?]] ?
  | |- pure_typing _ (pure_var _) _ _
    move /pure_typingE [?] [?] [?] [[?]] [? ?]
  | |- pure_typing _ (K' _ _) _ _
    move /pure_typing_K'E []
  | |- pure_typable _ (K' _ _) _
    move /pure_typable_K'E []
  | |- pure_typable _ (pure_var _) _
    move _
  | |- pure_typable _ (pure_app _ _) _
    move /pure_typableE [?] [?] []
  | |- pure_typable _ (pure_abs _) _
    move /pure_typableE [?]
  end.

Ltac simplify_poly_type_eqs :=
  match goal with
    | H : contains (poly_var _) _ |- _
      move: H /containsE ?
    | H : contains (poly_arr _ _) _ |- _
      move: H /containsE ?
    | H : contains (many_poly_abs _ (poly_arr _ _)) (poly_arr _ _) |- _
      move: H /contains_poly_arrE [?] [?] [? ?]
    | H : contains (subst_poly_type (fold_right scons poly_var _) _) _ |- _
      move /contains_subst_poly_type_fold_rightE in H; rewrite ?many_poly_abs_many_poly_abs in H
    | H : many_poly_abs _ (poly_var _) = subst_poly_type _ _ |- _
      move: H /many_poly_abs_poly_var_eq_subst_poly_typeE [[?]] [?] [?] [? ?]
    | H : contains (ren_poly_type _ (ren_poly_type _ _)) _ |- _
      rewrite ?renRen_poly_type /= in H
    | H : contains (ren_poly_type _ (many_poly_abs ?n (poly_var (?n + _)))) _ |- _
      rewrite ren_poly_type_many_poly_abs /= iter_up_ren in H
    | H : contains (ren_poly_type _ (many_poly_abs ?n (poly_arr (poly_var (?n + _)) (poly_var (?n + _))))) _ |- _
      rewrite ren_poly_type_many_poly_abs /= ?iter_up_ren in H
    | H : contains (many_poly_abs ?n (poly_var (?n + _))) _ |- _
      move: H /contains_many_poly_abs_free [?] [?] [? ?]
    | H : poly_arr _ _ = many_poly_abs ?n _ |- _
      (have ? : n = 0 by move : (n) H; case); subst n; rewrite /= in H
    | H : many_poly_abs ?n _ = poly_var _ |- _
      (have ? : n = 0 by move : (n) H; case); subst n; rewrite /= in H
    | H : poly_var _ = poly_var _ |- _
      move: H [?]
  end.

Arguments funcomp {X Y Z} _ _ / _.

Definition poly_abba := poly_abs (poly_abs (poly_arr (poly_var 1) (poly_arr (poly_var 0) (poly_arr (poly_var 0) (poly_var 1))))).

Lemma contains_poly_abbaI {s t s' t'} : s' = s t' = t
  contains poly_abba (poly_arr s (poly_arr t (poly_arr t' s'))).
Proof.
  move .
  apply: rt_trans; apply: rt_step; apply: contains_stepI; first done.
  by rewrite /= ?poly_type_norm subst_poly_type_poly_var.
Qed.


Theorem pure_typing_intro_poly_abba M : { N |
   Gamma,
    pure_typable (map tidy (poly_abba :: )) M
    pure_typable (map tidy ) N }.
Proof.
  pose := (pure_abs (pure_abs (pure_abs (pure_abs (
    K' (pure_var 2) (K'
      (pure_app (pure_var 3) (pure_var 0)) (pure_app (pure_var 3) (pure_var 1)))))))).
  pose := (pure_abs (K' (pure_app (pure_var 0) (pure_var 1))
    (many_pure_app (pure_var 0) [pure_var 3; pure_var 2; pure_var 2; pure_var 2]))).
  pose := pure_app (ren_pure_term (Nat.add 3) (pure_abs M)) (pure_app ).
  have [ ] := pure_typable_intro_poly_bot .
  have [ ] := pure_typable_intro_poly_var_0 .
  exists . constructor.
  - move [tM] HtM. apply / / { }.
    eexists. apply: (pure_typing_pure_app_simpleI (s := poly_abba)).
    + move /=. apply: pure_typing_pure_abs_simpleI.
      move: HtM /(pure_typing_ren_poly_type S) HtM.
      apply: pure_typing_ren_pure_term; first by eassumption.
      move [|]; first done.
      move ? ? /= . congr nth_error. rewrite ?map_map.
      apply: map_ext ?. by rewrite tidy_ren_poly_type.
    + apply: (pure_typing_pure_app_simpleI
        (s := poly_abs (poly_abs (poly_arr (poly_arr (poly_var 0) (poly_var 0)) (poly_arr (poly_var 1) (poly_arr (poly_var 0) (poly_arr (poly_var 0) (poly_var 1)))))))).
      * apply: pure_typing_pure_abs_simpleI. apply: pure_typing_K'I.
        ** apply: (pure_typing_pure_app 2 (s := poly_arr (poly_var 0) (poly_var 0))).
           *** apply: (pure_typing_pure_var 0); first by reflexivity.
               apply: rt_trans; apply: rt_step; apply: contains_stepI; first done.
               by rewrite /= ?poly_type_norm subst_poly_type_poly_var.
           *** apply: (pure_typing_pure_var 0); first by reflexivity.
               constructor. by apply: contains_stepI.
           *** by apply: rt_refl.
        ** exists (poly_var 0). do 4 (apply: pure_typing_pure_app_simpleI; last by apply: pure_typing_pure_var_simpleI).
           move /=. apply: (pure_typing_pure_var 0); first by reflexivity.
           apply: rt_trans; apply: rt_step; apply: contains_stepI; first done.
           by rewrite /= ?poly_type_norm subst_poly_type_poly_var.
      * apply: (pure_typing_pure_abs 2). do 3 (apply: pure_typing_pure_abs_simpleI).
        apply: pure_typing_K'I; first by apply: pure_typing_pure_var_simpleI.
        exists (poly_var 0). apply: pure_typing_K'I;
          first by apply: pure_typing_pure_app_simpleI; apply: pure_typing_pure_var_simpleI.
        exists (poly_var 0). by apply: pure_typing_pure_app_simpleI; apply: pure_typing_pure_var_simpleI.
  - move / / { }. rewrite / / / /= { }.
    do ? first_pure_typingE. move HM.
    do ? first_pure_typingE. subst.
    do ? (simplify_poly_type_eqs; subst).

    do ? (match goal with H : many_poly_abs _ (poly_arr _ _) = subst_poly_type _ _ |- _ move: H end).
    move /many_poly_abs_poly_arr_eq_subst_poly_typeE [].
    { move [?] [? ?]. subst. do ? (simplify_poly_type_eqs; subst). done. }
    move [?] [?] [? ?]. subst.

    do ? (simplify_poly_type_eqs; subst).
    do 2 (match goal with H : _ = fold_right scons poly_var _ (length _ + _) |- _ move: H end).
    do 2 (rewrite fold_right_length_ts_ge; first by ).
    move ? ?. do ? (simplify_poly_type_eqs; subst).

    match goal with H : contains _ (poly_arr _ _) |- _ move: H end.
    rewrite ren_poly_type_many_poly_abs /=.

    move /contains_poly_arrE [?] [?] [? ?]. subst.
    match goal with H : many_poly_abs _ _ = subst_poly_type _ _ |- _ move: H end.
    rewrite ?poly_type_norm ?subst_poly_type_many_poly_abs /= ?iter_up_poly_type_poly_type.
    move /many_poly_abs_eqE'' /(_ ltac:(done)) [?] [? ?]. subst.
    do ? (simplify_poly_type_eqs; subst).

    move: HM /=.
    set s := (s in pure_typing (s :: _) _ _).
    set := ( in pure_typing (s :: _ :: _ :: _ :: ) _ _).

    move /(pure_typing_ren_pure_term_allfv_pure_term (scons 0 (scons 0 (scons 0 (scons 0 S)))) ( := s :: )).
    apply: unnest.
    { rewrite allfv_pure_term_ren_pure_term /=. apply: allfv_pure_term_TrueI. by case. }
    rewrite renRen_pure_term /= ren_pure_term_id'; first by case. subst .

    have : pure_typing [poly_abba] (pure_var 0) (tidy s).
    {
      have Habba : [poly_abba] = map tidy [poly_abba] by done.
      rewrite /s {s}.
      rewrite Habba. apply: pure_typing_tidy_many_poly_abs_closed; first done.
      rewrite Habba. apply: pure_typing_many_poly_abs_contains_tidy_closed; [done | by eassumption |].
      rewrite Habba. apply: pure_typing_many_poly_abs_contains_tidy_closed; [done | by eassumption |].
      set ξ := (ξ in ren_poly_type ξ _). rewrite tidy_ren_poly_type.
      have : [poly_abba] = map (ren_poly_type ξ) [poly_abba] by done. apply: pure_typing_ren_poly_type.
      rewrite Habba. apply: pure_typing_tidy_many_poly_abs_closed; first done.
      rewrite /= tidy_many_poly_abs_le /=; first by .
      rewrite tidy_many_poly_abs_le /=.
      { do ? (rewrite ?allfv_poly_type_many_poly_abs /=).
        rewrite ?iter_scons ?iter_scons_ge; by . }
      rewrite tidy_many_poly_abs_le /=; first by .
      rewrite tidy_many_poly_abs_le /=.
      { do ? (rewrite ?allfv_poly_type_many_poly_abs /=).
        rewrite ?iter_scons ?iter_scons_ge; by . }
      rewrite tidy_many_poly_abs_le /=; first by .
      rewrite tidy_many_poly_abs_le /=; first by .

      apply: (pure_typing_pure_var 0); first done.
      apply: contains_poly_abbaI; congr poly_var; by .
    }
    move /pure_typing_weaken_closed /(_ []) H /pure_typing_tidyI /= /H{H} /(_ ltac:(done)).
    move /(pure_typing_ren_poly_type (Nat.pred)) /= /pure_typableI.
    congr pure_typable. congr cons. rewrite ?map_map. apply: map_ext ?.
    by rewrite ?tidy_ren_poly_type tidy_tidy ?poly_type_norm ren_poly_type_id /=.
Qed.


Fixpoint trace_poly_type (e: ) (t: poly_type) : pure_term :=
  match t with
  | poly_var x pure_var x
  | poly_arr s t pure_app (pure_app (pure_var e) (trace_poly_type e t)) (trace_poly_type e s)
  | poly_abs _ pure_var 0
  end.

Module pure_typable_intro_prenex_aux.

Lemma aux_pure_typing_gxs {Gamma x M n s} :
  nth_error x = Some (poly_abs (poly_var 0))
  pure_typing M (Nat.iter n ( s' : poly_type poly_abs (poly_arr (poly_var 0) s')) s)
  pure_typing (many_pure_app M (repeat (pure_var x) n)) (many_poly_abs n s).
Proof.
  move Hx HM. apply: pure_typing_many_poly_absI.
  elim: n s HM; first by (move >; rewrite map_ren_poly_type_id).
  move n IH s.
  rewrite (ltac:() : S n = n + 1) -repeat_appP many_pure_app_app /= HM.
  apply: (pure_typing_pure_app_simpleI (s := poly_var 0)).
  - have : map (ren_poly_type (Nat.add (n + 1))) =
      map (ren_poly_type S) (map (ren_poly_type (Nat.add n)) ).
    { rewrite ?map_map. apply: map_ext ?. rewrite poly_type_norm /=. by apply: extRen_poly_type; . }
    apply: (pure_typing_many_poly_absE (n := 1)). apply: IH.
    move: HM. by rewrite -iter_plus.
  - apply: (pure_typing_pure_var 0); first by rewrite ?nth_error_map Hx /=.
    by apply /rt_step /contains_step_subst.
Qed.


Lemma aux_pure_typable_gys {Gamma x M n s} :
  nth_error x = Some (poly_var 0)
  pure_typing M (Nat.iter n ( s' : poly_type poly_abs (poly_arr (poly_var 0) s')) s)
  pure_typable (many_pure_app M (repeat (pure_var x) n)).
Proof.
  move Hx HM. exists (ren_poly_type ( x x - n) s). elim: n s HM.
  { move > /=. rewrite ren_poly_type_id'; by [|]. }
  move n IH s. rewrite (ltac:() : S n = n + 1) -repeat_appP many_pure_app_app /=.
  rewrite -iter_plus /= /IH /pure_typing_to_typing [?] [] /=.
  move /(typing_ty_app (t := poly_var 0)) /= /(typing_app (Q := var x)).
  apply: unnest; first by apply: typing_var.
  move /typing_to_pure_typing /=. congr pure_typing.
  rewrite poly_type_norm ren_as_subst_poly_type /=. apply: ext_poly_type.
  case; first done. move ?. congr poly_var. by .
Qed.


Lemma aux_pure_typing_N0 {s n Gamma e }:
  is_simple s
  allfv_poly_type (gt (n+e)) s
  allfv_poly_type ( x nth_error x = Some (poly_var x)) (many_poly_abs n s)
  nth_error e = Some poly_abba
  pure_typing (many_pure_term_abs n (trace_poly_type (n+e) s))
    (Nat.iter n ( s' : poly_type poly_abs (poly_arr (poly_var 0) s')) s).
Proof.
  move Hs. elim: n e.
  - move e /= _. elim: s Hs.
    + move /= *. by apply: pure_typing_pure_var_simpleI.
    + move s IHs t IHt /= [/IHs {}IHs /IHt {}IHt] [/IHs {}IHs /IHt {}IHt].
      move /copy [He] /copy [/IHs {}IHs /IHt {}IHt].
      apply: (pure_typing_pure_app_simpleI (s := s)); last done.
      apply: (pure_typing_pure_app 0 (s := t)); rewrite ?map_ren_poly_type_id; last by apply: rt_refl.
      * apply: (pure_typing_pure_var 0); [by rewrite nth_error_map He | ].
        apply: rt_trans; apply: rt_step.
        ** by apply: contains_stepI.
        ** apply: contains_stepI. by rewrite /= poly_type_norm /= subst_poly_type_poly_var.
      * done.
    + done.
  - move n IH e /= H1ns H2ns He. apply: (pure_typing_pure_abs 1).
    have : S (n + e) = n + S e by . apply: IH.
    + apply: allfv_poly_type_impl H1ns. by .
    + apply: allfv_poly_type_impl H2ns. case; first done.
      move ? /=. by rewrite nth_error_map .
    + by rewrite /= nth_error_map He.
Qed.


Lemma pure_typing_many_pure_term_absE {Gamma n M t}:
  pure_typing (many_pure_term_abs n M) t
     (nss : list ( * poly_type)) t',
    n = length nss
    t = fold_right ( '(n, s) r many_poly_abs n (poly_arr s r)) t' nss
    pure_typing (fold_left ( Gamma '(n, s) s :: map (ren_poly_type (Nat.add n)) ) nss ) M t'.
Proof.
  elim: n t; first by move t /= HM; exists [], t.
  move n IH t /= /pure_typingE [] [] [] [+] .
  move /IH [nss] [t'] [] [] HM.
  by exists ((, ) :: nss), t'.
Qed.


Lemma pure_typing_fold_right_many_pure_app {Gamma x s' nss t M} :
  nth_error x = Some
    (fold_right ( '(n, s) r many_poly_abs n (poly_arr s r)) s' nss)
  pure_typing
    
    (many_pure_app (pure_var x) (repeat M (length nss))) t
   ns' ξ' nt' t',
    t = many_poly_abs nt' t'
    contains (many_poly_abs ns' (ren_poly_type ξ' s')) t'.
Proof.
  rewrite -[ in pure_typing _ _]map_ren_poly_type_id. move: (id) ξ.
  elim /rev_ind: nss ξ s' t.
  - move ξ > Hx /= /pure_typingE [] [?] [?].
    rewrite ?map_map nth_error_map Hx [[[]]].
    rewrite poly_type_norm /= [[HC ]].
    exists 0. do 3 eexists. constructor; [by reflexivity | by eassumption].
  - move [n s] nss IH /= ξ s' t.
    rewrite fold_right_app /= /IH {}IH.
    rewrite app_length -repeat_appP many_pure_app_app /=.
    move /pure_typingE [] [?] [?] [?] [+] [_] [H1C ?]. subst.
    rewrite ?map_map. under map_ext ? do rewrite poly_type_norm.
    move /IH [n1s'] [n2s'] [nt'] [t'] []. case: nt'; last done. move /= ?. subst.
    rewrite ren_poly_type_many_poly_abs many_poly_abs_many_poly_abs /=.
    move /contains_poly_arrE [?] [?] [? ?]. subst.
    move: H1C /contains_subst_poly_type_fold_rightE.
    clear ?. do 4 eexists. constructor; [by reflexivity | by eassumption].
Qed.


Lemma pure_typable_many_pure_app_repeat_poly_var {Gamma x y ny nss t} :
  nth_error x = Some
    (fold_right ( '(n, s) r many_poly_abs n (poly_arr s r)) t nss)
  nth_error y = Some (poly_var ny)
  pure_typable (many_pure_app (pure_var x) (repeat (pure_var y) (length nss)))
  Forall ( '(_, s) n x, s = many_poly_abs n (poly_var (n + x))) nss.
Proof.
  move + Hy. elim /rev_ind: nss t; first done.
  move [n s] nss IH t. rewrite fold_right_app /= Hx.
  rewrite app_length -repeat_appP many_pure_app_app /=.
  move /pure_typableE [?] [?] [].
  move /copy [/pure_typableI /IH] /(_ _ Hx) Hnss.
  move /(pure_typing_fold_right_many_pure_app Hx) [?] [?] [[|?]] [?] []; last done.
  move /= ?. subst. rewrite ren_poly_type_many_poly_abs many_poly_abs_many_poly_abs /=.
  move /contains_poly_arrE [?] [?] [? ?]. subst.
  move /pure_typingE [?] [?] [?] [+] [+]. rewrite nth_error_map Hy [[?]]. subst.
  rewrite poly_type_norm. move /containsE .
  move /esym /many_poly_abs_poly_var_eq_subst_poly_typeE [?] [?] [?] [? ].
  apply /Forall_appP. constructor; first done.
  constructor; last done. clear.
  by do 2 eexists.
Qed.


Lemma pure_typing_trace_poly_typeE {s ns Gamma t n}:
  is_simple s
  allfv_poly_type (gt (length ns)) s
  pure_typing
    ((map ( x poly_var (n + x)) ns) poly_abba :: )
    (trace_poly_type (length ns) s) t
  ren_poly_type ( x n + nth x ns 0) s = tidy t.
Proof.
  elim: s t n.
  - move x t n _ /= ? /pure_typingE [] [?] [?].
    rewrite ?nth_error_map nth_error_app1; first by rewrite map_length; .
    rewrite nth_error_map.
    have := nth_error_nth' ns 0; first by .
    move: (nth x ns 0) y [[?]] []. subst.
    move /containsE ? . subst.
    rewrite tidy_many_poly_abs_le /=; first by .
    congr poly_var. by .
  - move t n /= [/ {} / {}] [/ {} / {}].
    move /pure_typingE [] [?] [?] [?] [+] [+] [H1C ?]. subst.
    move /pure_typingE' [?] [?] [+] [+] H2C.
    move /pure_typingE' [?] [+] H3C.
    rewrite ?nth_error_map nth_error_app2; first by rewrite ?map_length; .
    rewrite ?map_length (ltac:() : length ns - length ns = 0) /=.
    move [?]. subst. move: H3C /(contains_poly_arrE (n := 2)) - [[| [| [|? ?]]]] [] //=.
    move _ [? ?]. subst. move: H2C /containsE [? ?]. subst.
    move: H1C /containsE ?. subst.
    rewrite ?map_app /= ?map_map /=.
    under [map _ ns]map_ext x do rewrite (ltac:() : + (n + x) = ( + n) + x).
    move / {} / {}. rewrite -tidy_many_poly_abs_tidy /= - -.
    rewrite tidy_many_poly_abs_le /=.
    { rewrite ?allfv_poly_type_ren_poly_type /=. constructor; apply: allfv_poly_type_TrueI; by . }
    rewrite ?tidy_tidy - - ?poly_type_norm /=.
    congr poly_arr; apply: extRen_poly_type; by .
  - done.
Qed.


End pure_typable_intro_prenex_aux.

Import pure_typable_intro_prenex_aux.

Theorem pure_typable_intro_prenex M s n :
  is_simple s allfv_poly_type (gt n) s
  { N |
     Gamma,
      pure_typable (map tidy (many_poly_abs n s :: )) M
      pure_typable (map tidy ) N }.
Proof.
  move Hs Hns.
  pose := many_pure_term_abs n (trace_poly_type n s).
  pose := pure_abs (K' (many_pure_app (pure_var 0) (repeat (pure_var 2) n))
    (many_pure_app (pure_var 0) (repeat (pure_var 3) n))).
  pose := pure_app (ren_pure_term (Nat.add 4) (pure_abs M)) (pure_app ).
  have [N2_1 HN2_1]:= pure_typing_intro_poly_abba .   have [N2_2 HN2_2] := pure_typable_intro_poly_bot N2_1.   have [N2_3 HN2_3] := pure_typable_intro_poly_var_0 N2_2.   exists N2_3 . constructor.
  - move [tM] HtM. apply /HN2_3 /HN2_2 /HN2_1 {HN2_3 HN2_2 HN2_1}.
    eexists. apply: (pure_typing_pure_app_simpleI (s := tidy (many_poly_abs n s))).
    + move /=. apply: pure_typing_pure_abs_simpleI.
      move: HtM /(pure_typing_ren_poly_type S) HtM.
      apply: pure_typing_ren_pure_term; first by eassumption. case.
      * move ? /= . rewrite -tidy_ren_poly_type. congr Some. congr tidy.
        rewrite ren_poly_type_allfv_id ?allfv_poly_type_many_poly_abs; last done.
        apply: allfv_poly_type_impl Hns ? ?. rewrite iter_scons_lt; by [ |].
      * move ? ? /= . congr nth_error. rewrite ?map_map.
        apply: map_ext ?. by rewrite tidy_ren_poly_type.
    + apply: (pure_typing_pure_app_simpleI
        (s := tidy (Nat.iter n ( s' poly_abs (poly_arr (poly_var 0) s')) s))).
      * rewrite -/(tidy (poly_arr _ _)). apply: pure_typing_tidyI.
        apply: pure_typing_pure_abs_simpleI. apply: pure_typing_K'I.
        ** apply: aux_pure_typing_gxs; [done | by apply: pure_typing_pure_var_simpleI].
        ** apply: aux_pure_typable_gys; [done | by apply: pure_typing_pure_var_simpleI].
      * apply: pure_typing_tidyI. rewrite /.
        have : trace_poly_type n = trace_poly_type (n+0) by congr trace_poly_type; .
        apply: aux_pure_typing_N0; [done | by apply: allfv_poly_type_impl Hns; | | done].
        rewrite allfv_poly_type_many_poly_abs. apply: allfv_poly_type_impl Hns.
        move ? ?. rewrite iter_scons_lt; by [|].
  - move /HN2_3 /HN2_2 /HN2_1 {HN2_3 HN2_2 HN2_1}.
    rewrite / / / /= {N2_3 N2_2 N2_1}.
    move /pure_typableE [?] [?] [].
    move /pure_typingE' HM.
    move /pure_typingE [] [?] [?] [?] [+] [+] [H2C ?]. subst.
    move /pure_typingE'.
    move /pure_typing_K'E [HN1_1] HN1_2.
    move /pure_typing_many_pure_term_absE [nss] [?] [?] [?]. subst.
    move: HN1_2 /pure_typable_many_pure_app_repeat_poly_var.
    evar (r : poly_type) /(_ ( + 0) r). apply: unnest; first by subst r.
    apply: unnest; first done.
    move Hnss.     set := ( in pure_typing _ _).
    have : ss Gamma'', length ss = length nss
       = ss poly_abba ::
      Forall ( 's n x, s = many_poly_abs n (poly_var (n + x))) ss.
    {
      rewrite /. elim /rev_ind: (nss) Hnss; first by move ?; exists []; eexists.
      clear [[n s]] nss IH /Forall_appP [/IH] {}IH /Forall_singletonP [] [] .

      rewrite fold_left_app app_length.
      move: IH [ss] [] [] /= [] Hss.
      eexists (_ :: map (ren_poly_type (Nat.add n)) ss), (map (ren_poly_type (Nat.add n)) ).
      rewrite /= map_length map_app /= Forall_consP Forall_mapP.
      constructor; first by .
      constructor; first done.
      constructor; first by do 2 eexists.
      apply: Forall_impl Hss ? [?] [? ]. rewrite ren_poly_type_many_poly_abs /= iter_up_ren.
      by do 2 eexists.
    }
    move [ss] [?] [H1ss] [ {}] H2ss. rewrite -H1ss.
    move /pure_typing_tidyI. rewrite map_app /=.
    have [ns [H1ns H2ns]] : ns, length ss = length ns map tidy ss = map poly_var ns.
    {
      elim: (ss) H2ss; first by move _; exists [].
      clear s ss IH /Forall_consP [] [?] [n] /IH [ns] /= [ ].
      exists (n :: ns). constructor; first done.
      rewrite tidy_many_poly_abs_le /=; first by .
      congr cons. congr poly_var. by .
    }
    rewrite H1ns H2ns. move /(pure_typing_trace_poly_typeE (n := 0)).
    rewrite -H1ns H1ss tidy_tidy /(_ ltac:(done) ltac:(done)).
    move: HN1_1 /pure_typing_fold_right_many_pure_app /= /(_ _ ltac:(done)).
    move [] [ξs] [] [?] [? H3C]. subst.

    move H2s. move: HM /=.
    set := ( in pure_typing ( :: _) _ _).
    set := ( in pure_typing ( :: _ :: _ :: _ :: _ :: ) _ _).

    move /(pure_typing_ren_pure_term_allfv_pure_term (scons 0 (scons 0 (scons 0 (scons 0 (scons 0 S))))) ( := :: )).
    apply: unnest.
    { rewrite allfv_pure_term_ren_pure_term /=. apply: allfv_pure_term_TrueI. by case. }
    rewrite renRen_pure_term /= ren_pure_term_id'; first by case. subst .
    have H4s : allfv_poly_type ( False) (tidy (many_poly_abs (length nss) s)).
    {
      rewrite allfv_poly_type_tidy allfv_poly_type_many_poly_abs.
      apply: allfv_poly_type_impl Hns ? ?. rewrite iter_scons_lt; by [|].
    }
    
    have : pure_typing [tidy (many_poly_abs (length nss) s)] (pure_var 0) (tidy ).
    {
      have H3s : [tidy (many_poly_abs (length nss) s)] = map tidy [tidy (many_poly_abs (length nss) s)]
        by rewrite /= tidy_tidy.

      rewrite / {}.
      rewrite H3s. apply: pure_typing_tidy_many_poly_abs_closed; first done.
      rewrite H3s. apply: pure_typing_many_poly_abs_contains_tidy_closed; [done | by eassumption |].
      rewrite H3s. apply: pure_typing_many_poly_abs_contains_tidy_closed; [done | by eassumption |].
      rewrite tidy_ren_poly_type -H2s ?poly_type_norm /=.
      rewrite -[s in ren_poly_type _ s](tidy_is_simple Hs) -tidy_ren_poly_type -/(map tidy [_]).
      apply: pure_typing_tidyI. apply: (pure_typing_pure_var 0); first done.
      rewrite ren_poly_type_id ren_as_subst_poly_type. by apply: contains_many_poly_abs_closedI.
    }
    move /pure_typing_weaken_closed /(_ []) H /pure_typing_tidyI /= /H{H} /(_ ltac:(done)).
    move /(pure_typing_ren_poly_type ( x x - 1)) /= /pure_typableI.
    congr pure_typable. congr cons.
    + rewrite ren_poly_type_allfv_id; last done. by apply: allfv_poly_type_impl H4s.
    + rewrite ?map_map. apply: map_ext ?.
      rewrite ?tidy_ren_poly_type tidy_tidy ?poly_type_norm /=.
      apply: ren_poly_type_id'. by .
Qed.