Set Implicit Arguments.
Require Import RelationClasses Morphisms List Lia
      Arith Lia Init.Nat Setoid.
From Undecidability.HOU Require Import std.std calculus.calculus third_order.pcp.
Import ListNotations ArsInstances.
Section Encoding.

  Context {X: Const}.
  Variable (u v: ).
  Hypothesis (u_neq_v: u v).


  Definition encb (b: symbol) : exp X :=
    var (if b then u else v).

  Definition enc (w: word) : exp X :=
     (AppL (renL shift (map encb w)) (var 0)).

  Notation Enc := (map enc).

  Section Typing.

    Lemma encb_typing (Gamma: ctx) b:
      ( ⊢(3) @var X u : )
      ( ⊢(3) @var X v : )
       ⊢(3) encb b : ( ).
    Proof.
      intros . destruct b; (eauto 2).
    Qed.


    Lemma enc_typing (Gamma: ctx) w:
      ( ⊢(3) @var X u : )
      ( ⊢(3) @var X v : )
       ⊢(3) enc w : .
    Proof.
      intros.
      econstructor; trivial. inv H; inv .
      eapply AppL_ordertyping_repeated; auto.
      eapply orderlisttyping_preservation_under_renaming.
      eapply repeated_ordertyping; simplify.
      intros; mapinj. eapply encb_typing. all: eauto 2.
      intros ??; cbn; (eauto 2).
    Qed.


    Lemma Enc_typing (Gamma: ctx) W:
      ( ⊢(3) @var X u : )
      ( ⊢(3) @var X v : )
       ⊢₊(3) Enc W : repeat ( ) (length W).
    Proof.
      intros. eapply repeated_ordertyping; simplify; trivial.
      intros; mapinj. eapply enc_typing; (eauto 2).
    Qed.

  End Typing.


  Section Reduction.

    Lemma enc_nil s: enc [] s s.
    Proof.
      unfold enc; cbn.
      rewrite stepBeta; asimpl; cbn; (eauto 2).
    Qed.


    Lemma enc_cons b w s: enc (b :: w) s encb b (enc w s).
    Proof.
      unfold enc. eapply equiv_join. { now rewrite stepBeta. }
      rewrite stepBeta; [|reflexivity]. asimpl.
      rewrite map_map, map_id_list. rewrite map_map, map_id_list. eauto.
      all: intros; now asimpl.
    Qed.


    Hint Rewrite enc_nil enc_cons : simplify.

    Lemma enc_app w1 w2 s:
      enc ( ) s enc (enc s).
    Proof.
      induction ; cbn; simplify; [auto..|].
      now rewrite .
    Qed.


    Hint Rewrite enc_app : simplify.

    Lemma enc_concat W s:
      AppL (Enc W) s enc (concat W) s.
    Proof.
      induction W; cbn; simplify; [auto..|].
      now rewrite IHW.
    Qed.

  End Reduction.


  Hint Rewrite enc_nil enc_cons enc_app : simplify.

  Section Substitution.

    Lemma encb_subst_id a (sigma: fin exp X):
       u = var u v = var v encb a = encb a.
    Proof.
      intros; unfold encb; cbn; destruct a; (eauto 2).
    Qed.


    Lemma enc_subst_id (sigma: fin exp X) w:
       u = var u v = var v (enc w) = enc w.
    Proof.
      unfold enc.
      intros . cbn. f_equal.
      asimpl. f_equal. rewrite map_id_list; [auto..|].
      intros x ?; mapinj; mapinj. asimpl.
      rewrite compComp_exp, encb_subst_id; (eauto 2).
    Qed.


    Lemma Enc_subst_id (sigma: fin exp X) W:
       u = var u v = var v •₊ Enc W = Enc W.
    Proof.
      intros. eapply map_id_list.
      intros; mapinj;eapply enc_subst_id; (eauto 2).
    Qed.


  End Substitution.


  Definition nostring (t: exp X) :=
     s, t var u s t var v s.

  Section Injectivity.
    Lemma encb_eq a b:
      encb a encb b a = b.
    Proof using u u_neq_v.
      intros H % equiv_head_equal; cbn in *; trivial.
      injection H; destruct a, b; intros; (eauto 1); exfalso; eapply u_neq_v; (eauto 2).
    Qed.



    Lemma enc_eq w1 w2 s t:
      enc s enc t nostring s nostring t
       = s t.
    Proof using u_neq_v.
      simplify. intros. induction in , H |-*; destruct ; cbn in *; (auto 2).
      - simplify in H; intuition easy.
      - simplify in H. destruct b; firstorder easy.
      - simplify in H; symmetry in H; destruct a; firstorder easy.
      - simplify in H; Injection H.
        eapply in as [ ].
        now rewrite encb_eq with (a := a) (b := b).
    Qed.

  End Injectivity.

  Section EquivalenceInversion.

    Variables (s t: exp X) (x: ) (: fin exp X) (S: list (exp X)).
    Hypothesis (: y, isVar ( y) y var x).
    Hypothesis (N: normal s).
    Hypothesis (EQ: S .+ s (var x) t).

    Lemma end_head_var:
       (h: ) T s', s = AppR (var h) T nth S h = Some s'.
    Proof using x v u_neq_v t N EQ.
      eapply normal_nf in N as N'. inv N'. destruct k; cbn in *; (auto 5); [|Discriminate].
      destruct (); cbn in H; intuition idtac; clear H.
      - assert(f < |S| f |S|) as [] by ; auto.
        eapply nth_error_lt_Some in H as ; destruct . now eauto.
        asimpl in EQ; rewrite sapp_ge_in in EQ; (eauto 2).
        specialize ( (f - |S|)). intuition.
        eapply equiv_head_equal in EQ; cbn; simplify; (eauto 3). simplify in EQ; cbn in EQ; (eauto 1).
        destruct ( (f - (|S|))); cbn in *; intuition.
      - asimpl in EQ.
        eapply equiv_head_equal in EQ; cbn; simplify in *; [|auto..].
        cbn in EQ; discriminate.
    Qed.


    Lemma end_is_var_typed Gamma A C:
      S = Enc C
      (repeat ( ) (|S|) ⊢(3) s : A)
       i e, i < |S| s = var i e.
    Proof using x u_neq_v t N EQ.
      intros ; destruct end_head_var as (h' & T & s' & ); intuition idtac; subst.
      destruct T as [| [| T]].
      + cbn in EQ. erewrite nth_error_sapp in EQ; (eauto 2).
        rewrite nth_error_map_option in ; destruct nth; try discriminate.
        cbn in ; injection as .
        unfold enc in EQ; Discriminate.
      + exists h'. exists . intuition. now eapply nth_error_Some_lt in .
      + eapply AppR_ordertyping_inv in .
        destruct as [L]; intuition.
        inv . rewrite nth_error_app1, nth_error_repeated in ; simplify in *.
        inv . inv . cbn in ; injection as ?.
        rewrite !Arr_app in H; cbn in H. eapply (f_equal arity) in H.
        rewrite arity_Arr in H; cbn in H. .
        all: eapply nth_error_Some_lt in ; simplify in ; (eauto 2).
    Qed.



  End EquivalenceInversion.


  Lemma AppL_decomposition (s: exp X) (n: ):
    { '(I, u) | I nats n s = AppL (map var I) u x v, u = var x v x < n }.
  Proof.
    destruct (@AppL_largest _ ( s match s with var x x < n | _ False end) s)
      as (S & t & & & ). intros []; (intuition (auto with typeclass_instances)); now right.
    subst. induction S.
    - exists (nil, t). cbn; intuition (auto with listdb). eapply ; (eauto 2).
    - edestruct IHS as [[I s'] ( & & )].
      intros; eapply ; intuition (auto with datatypes).
      specialize ( a); mp ; destruct a; intuition (auto with datatypes).
      exists (f :: I, s'); cbn; intuition try congruence.
      eapply lt_nats in . auto with listdb.
  Qed.


  Section MainLemma.

    Variable ( : ctx) (s: exp X) (n: ).

    Hypothesis (Ts: ⊢(3) s : Arr (repeat ( ) n) ).
    Hypothesis (Vu: u vars s) (Vv: v vars s).

    Lemma main_lemma:
       I, I nats n W, |W| = n t,
        AppR s (Enc W) AppL (select I (Enc W)) t nostring t.
    Proof using u_neq_v Vv Vu Ts .
      pose (mv := x match x == u, x == v with right _,right _ x | _,_ S(u + v + x) end).
      assert ( x, mv x x) as GE.
      { eauto; intros; unfold funcomp; intuition idtac; unfold mv in *.
        eauto; intros; edestruct eq_dec; [|]; destruct eq_dec; (eauto 3).
      }
      assert ( x, var (mv x) @var X u) as Nu.
      { intros x H; injection H; unfold mv; destruct eq_dec; [|]; destruct eq_dec; [|].
        intros; subst; (eauto 2).
      }
      assert ( x, var (mv x) @var X v) as Nv.
      { intros x H; injection H; unfold mv; destruct eq_dec; [|]; destruct eq_dec; [|].
        intros; subst; (eauto 2).
      }
      replace s with (ren mv s).
      2: { asimpl; erewrite subst_extensional with ( := var); asimpl; (auto 2).
        intros; unfold funcomp, mv; destruct eq_dec; subst; [exfalso; eapply Vu; eauto|].
        destruct eq_dec; subst; [exfalso; eapply Vv; eauto|eauto].
      }
      eapply ordertyping_termination_steps in Ts as N; destruct N as [t [E N]].
      eapply normal_nf in N as . destruct as [k a ? T _ isA ].
      eapply ordertyping_preservation_under_steps in Ts as Tv; (eauto 2).
      eapply Lambda_ordertyping_inv in Tv as (L & B & & & ).
      destruct (Nat.lt_total n (|L|)) as [?|[?|?]].
      - exfalso. eapply (f_equal arity) in ; simplify in ; .
      - destruct (AppL_decomposition (AppR a T) n) as [[I v''] (&&)].
        exists I. intuition idtac. exists (Enc W .+ mv >> var v'').
        split.
        + rewrite E.
          rewrite Lambda_ren, AppR_Lambda'; simplify; [|congruence].
          rewrite it_up_var_sapp, , AppL_subst, select_variables_subst; simplify; (auto 2).
          all: rewrite ?; (eauto 2).
        + eapply Arr_inversion in as [[] [ ]]; simplify; [|discriminate|].
          cbn in , . simplify in . subst.
          rewrite in ; eapply AppL_ordertyping_inv in as (?&?&?&?).
          split; intros EQ; eapply end_is_var_typed in EQ as (? & ? & ? & ?).
          1, 6: eapply ; simplify in ; (eauto 1);
            rewrite ; easy. 3, 7: eauto.
          1, 4: intros; unfold funcomp; intuition eauto.
          1, 3: rewrite in N; eapply normal_AppL_right, normal_Lambda, N.
          all: simplify; rewrite in ; simplify in ; rewrite in ; (eauto 2).
      - remember mv as . exists nil; intuition (auto with listdb); cbn; simplify. eexists. rewrite E. intuition eauto.
        edestruct (@list_decompose_alt (length L) _ W) as (&&?&?);
          subst; (auto 2).
        simplify. rewrite !AppR_app, !Lambda_ren. split; rewrite !AppR_Lambda'; simplify; (auto 2).
        all: rewrite !it_up_var_sapp; simplify; (eauto 1); rewrite AppR_subst.
        all: intros EQ.
        all: destruct a as [y| d | |]; cbn in isA;
          (intuition idtac); [destruct (le_lt_dec (length ) y)|].
        all: revert EQ.
        3, 6: cbn; intros EQ' % equiv_head_equal; cbn; simplify; cbn; auto 1.
        3, 4: simplify in EQ'; cbn in EQ'; subst; discriminate.
        1, 3: cbn; rewrite sapp_ge_in; simplify; (eauto 2).
        1, 2: intros EQ' % equiv_head_equal; cbn; simplify; cbn; auto 1.
        1, 2: simplify in EQ'; cbn in EQ'; subst; (eauto 2).
        all: eapply AppR_ordertyping_inv in as [? [_ ]]; intuition idtac; inv .
        all: symmetry in ; eapply Arr_inversion in as ; simplify; try .
        all: destruct as (?&?&?); eapply repeated_app_inv in .
        all: intuition idtac; subst; rewrite in *; simplify in ; simplify in ; rewrite in l.
        all: eapply id in as HH;
          rewrite nth_error_app1, nth_error_repeated in HH; simplify; (auto 2).
        all: injection HH as HH; destruct ; simplify in ; simplify in .
        all: simplify in H; try ; cbn in ; injection as .
        all: eapply (f_equal ord) in HH; simplify in HH.
        all: symmetry in HH; eapply Nat.eq_le_incl in HH; simplify in HH.
        all: intuition idtac; cbn [ord'] in .
        all: cbn [add] in ; rewrite Nat.succ_max_distr in .
        all: eapply Nat.max_lub_l in ; cbn in ; .
    Qed.

  End MainLemma.


End Encoding.

#[export] Hint Rewrite @enc_app @enc_nil: simplify.
Notation Enc u v := (map (enc u v)).

Section ReductionEncodings.

  Context {X: Const}.

  Definition finst I n := Lambda n (AppL (map var I) (@var X n)).

  Lemma finst_typing I n :
    I nats n
    [] ⊢(3) finst I n : Arr (repeat ( ) n) .
  Proof.
    intros H; eapply Lambda_ordertyping; simplify; (eauto 1).
    eapply AppL_ordertyping_repeated.
    eapply repeated_ordertyping; simplify; (eauto 1).
    intros; mapinj.
    econstructor; simplify; cbn; [auto|].
    rewrite nth_error_app1; simplify; (eauto 1).
    eapply nth_error_repeated; (eauto 1).
    1 - 2: eapply nats_lt, H, .
    econstructor; simplify; (auto 2).
    rewrite nth_error_app2; simplify; (eauto 1).
  Qed.



  Lemma finst_equivalence u v I W delta:
    I nats (length W)
    AppR (ren (finst I (length W))) (Enc u v W) enc u v (concat (select I W)) (var ( 0)).
  Proof.
    intros. unfold finst. rewrite !Lambda_ren, !AppL_ren.
    rewrite !map_id_list.
    2: intros ? ?; mapinj; cbn; eapply H, nats_lt in ; now rewrite it_up_ren_lt.
    cbn; rewrite it_up_ren_ge; (eauto 2); simplify.
    rewrite !AppR_Lambda'; simplify; (eauto 2). asimpl.
    rewrite !sapp_ge_in; simplify; (eauto 2).
    rewrite !select_variables_subst; [|simplify; (eauto 2)..].
    now rewrite !select_map, enc_concat.
  Qed.


End ReductionEncodings.