Require Import List Arith Lia Max.

From Undecidability.Shared.Libs.DLW
  Require Import utils pos vec subcode sss.

From Undecidability.MinskyMachines
  Require Import env mm_defs mme_defs mme_utils.

From Undecidability.MuRec
  Require Import recalg ra_mm_env.

Set Implicit Arguments.

Set Default Proof Using "Type".

Tactic Notation "rew" "length" := autorewrite with length_db.

Local Notation "i /e/ s '-1>' t" := (mm_sss_env eq_nat_dec i s t) (at level 70, no associativity).
Local Notation "P /e/ s > t" := (sss_compute (mm_sss_env eq_nat_dec) P s t) (at level 70, no associativity).
Local Notation "P /e/ s ~~> t" := (sss_output (mm_sss_env eq_nat_dec) P s t) (at level 70, no associativity).
Local Notation "P /e/ s -[ k ] t" := (sss_steps (mm_sss_env eq_nat_dec) P k s t) (at level 70, no associativity).
Local Notation "P /e/ s ↓" := (sss_terminates (mm_sss_env eq_nat_dec) P s) (at level 70, no associativity).

Local Notation "i /v/ s '-1>' t" := (@mm_sss _ i s t) (at level 70, no associativity).
Local Notation "P /v/ s > t" := (sss_compute (@mm_sss _) P s t) (at level 70, no associativity).
Local Notation "P /v/ s ~~> t" := (sss_output (@mm_sss _) P s t) (at level 70, no associativity).
Local Notation "P /v/ s -[ k ] t" := (sss_steps (@mm_sss _) P k s t) (at level 70, no associativity).
Local Notation "P /v/ s ↓" := (sss_terminates (@mm_sss _) P s) (at level 70, no associativity).

Local Notation " e ⇢ x " := (@get_env _ _ e x).
Local Notation " e ⦃ x ⇠ v ⦄ " := (@set_env _ _ eq_nat_dec e x v).

Local Notation "'⟦' f '⟧'" := (@ra_rel _ f) (at level 0).

Section mm_nat_pos.

  Definition mm_var_map (X Y : Set) (f : X Y) (i : mm_instr X) :=
    match i with
      | INC x INC (f x)
      | DEC x p DEC (f x) p
    end.

  Definition mm_instr_var X (i : mm_instr X) :=
    match i with
      | INC x x
      | DEC x _ x
    end.

  Definition mm_linstr_vars X := map (@mm_instr_var X).

  Definition mm_pos_nat n := map (mm_var_map (@pos2nat n)).

  Definition mm_nat_pos_full n l : Forall ( x x < n) (mm_linstr_vars l) { m | l = @mm_pos_nat n m }.
  Proof.
    induction l as [ | [ x | x p ] l IHl ]; simpl; intros H.
    { exists nil; auto. }
    1,2: rewrite Forall_cons_inv in H; destruct H as [ ];
         destruct (IHl ) as (m & Hm).
    + exists (INC (nat2pos ) :: m); simpl; f_equal; auto; f_equal.
      rewrite pos2nat_nat2pos; auto.
    + exists (DEC (nat2pos ) p :: m); simpl; f_equal; auto; f_equal.
      rewrite pos2nat_nat2pos; auto.
  Qed.


  Definition mm_nat_bound l := S (lmax (mm_linstr_vars l)).

  Fact mm_nat_bound_spec l : Forall ( x x < mm_nat_bound l) (mm_linstr_vars l).
  Proof.
    cut (Forall ( x x lmax (mm_linstr_vars l)) (mm_linstr_vars l)).
    + apply Forall_impl; intros; unfold mm_nat_bound; .
    + apply lmax_spec; auto.
  Qed.


  Definition mm_nat_pos n l : mm_nat_bound l n { m | l = @mm_pos_nat n m }.
  Proof.
    intros H; apply mm_nat_pos_full.
    generalize (mm_nat_bound_spec l).
    apply Forall_impl; intros; .
  Qed.


End mm_nat_pos.

Section mm_pos_nat_sem.

  Variable (n : ).

  Implicit Types (e : ) (v : vec n).

  Ltac dest x y := destruct (pos_eq_dec x y) as [ | ]; [ subst x | ]; rew env.

  Notation "v '⋈' e" := ( p, vec_pos v p = epos2nat p) (at level 70, no associativity).

  Fact sss_mm_pos_nat rho st1 st2 e1 :
            snd
          /v/ -1>
          e2, snd
                     mm_var_map (@pos2nat n) /e/ (fst ,) -1> (fst ,).
  Proof.
    revert ; intros (,) (,); simpl.
    intros .
    destruct as [ x | x p ]; simpl in *;
      [ | case_eq (vec_pos x); [ intros | intros q Hq ] ].
    - exists (pos2nat x S (pos2nat x)).
      apply mm_sss_INC_inv in .
      destruct as (? & ?); subst; split.
      2: constructor.
      intros p.
      destruct (pos_eq_dec x p) as [ | C ]; rew vec; rew env.
      rewrite ; assert (pos2nat x pos2nat p); rew env.
      contradict C; revert C; apply pos2nat_inj.
    - exists .
      apply mm_sss_DEC0_inv in ; destruct ; subst; auto.
      split; auto; constructor; rewrite ; auto.
    - exists (pos2nat x q).
      apply mm_sss_DEC1_inv with (1 := Hq) in .
      destruct ; subst; split.
      2: constructor; rewrite ; auto.
      intros j.
      destruct (pos_eq_dec x j) as [ | C ]; rew vec; rew env.
      rewrite ; assert (pos2nat x pos2nat j); rew env.
      contradict C; revert C; apply pos2nat_inj.
  Qed.


  Fact sss_mm_pos_nat_inv rho st1 st2 v1 :
             snd
          mm_var_map (@pos2nat n) /e/ -1>
          v2, snd ()
                     /v/ (fst ,) -1> (fst ,).
  Proof.
    revert ; intros (,) (,); simpl.
    intros .
    destruct as [ x | x p ]; simpl in *;
      [ | case_eq (pos2nat x); [ intros | intros q Hq ] ].
    - exists (vec_change x (S (vec_pos x))).
      apply mm_sss_env_INC_inv in .
      destruct as (? & ?); subst; split.
      2: constructor.
      intros p.
      destruct (pos_eq_dec x p) as [ | C ]; rew vec; rew env.
      rewrite ; assert (pos2nat x pos2nat p); rew env.
      contradict C; revert C; apply pos2nat_inj.
    - exists .
      apply mm_sss_env_DEC0_inv in ; destruct ; subst; auto.
      split; auto; constructor; rewrite ; auto.
    - exists (vec_change x q).
      apply mm_sss_env_DEC1_inv with (1 := Hq) in .
      destruct ; subst; split.
      2: constructor; rewrite ; auto.
      intros j.
      destruct (pos_eq_dec x j) as [ | C ]; rew vec; rew env.
      rewrite ; assert (pos2nat x pos2nat j); rew env.
      contradict C; revert C; apply pos2nat_inj.
  Qed.


  Fact sss_steps_mm_pos_nat i P k st1 st2 e1 :
            snd
          (i,P) /v/ -[k]->
          e2, snd
                     (i,@mm_pos_nat n P) /e/ (fst ,) -[k]-> (fst ,).
  Proof.
    intros ; revert .
    induction 1 as [ (j,v) | k (,) (,) (,) ];
      simpl; intros .
    + exists ; split; auto; constructor.
    + destruct as (q & l & & r & e & [= ] & [= ] & ).
      destruct sss_mm_pos_nat with (2 := ) (1 := ) as ( & & ); simpl in *.
      destruct with (1 := ) as ( & & ).
      exists ; split; auto.
      constructor 2 with ( := (,)); auto.
      exists i, (mm_pos_nat l), (mm_var_map (@pos2nat _) ), (mm_pos_nat r), ; msplit 2; auto.
      * f_equal; subst P; unfold mm_pos_nat; repeat (rewrite map_app; simpl); auto.
      * unfold mm_pos_nat; rew length; auto.
  Qed.


  Fact sss_steps_mm_pos_nat_inv i P k st1 st2 v1 :
             snd
          (i,@mm_pos_nat n P) /e/ -[k]->
          v2, snd ()
                     (i,P) /v/ (fst ,) -[k]-> (fst ,).
  Proof.
    intros ; revert .
    induction 1 as [ (j,e) | k (,) (,) (,) ];
      simpl; intros .
    + exists ; split; auto; constructor.
    + destruct as (q & l & & r & e & [= ] & [= ] & ).
      unfold mm_pos_nat in ; apply map_middle_inv in .
      destruct as (l' & & r' & & & & ).
      subst l r.
      destruct sss_mm_pos_nat_inv with (2 := ) (1 := ) as ( & & ); simpl in *.
      destruct with (1 := ) as ( & & ).
      exists ; split; auto.
      constructor 2 with ( := (,)); auto.
      exists i, l', , r', ; msplit 2; subst; auto; rew length; auto.
  Qed.


End mm_pos_nat_sem.

Section ra_mm_comp.

  Theorem ra_mm_compiler (n : ) (f : recalg n) :
      { m & { P : list (mm_instr (pos (n+S m)))
            | ( x v, f v x (1,P) /v/ (1,vec_app v vec_zero) ->> (1+length P,vec_app v (x##vec_zero)))
             ( v, (1,P) /v/ (1,vec_app v vec_zero) ex (f v)) } }.
  Proof.
    destruct ra_mm_env_simulator with (f := f) as (P & HP).
    set (k := max (S n) (mm_nat_bound P)).
    assert (S n k) as by apply le_max_l.
    assert { m | k = n+S m } as .
    { exists (k-S n); . }
    clear .
    destruct as (m & Hm).
    exists m.
    destruct mm_nat_pos with (n := k) (l := P) as (Q & HQ).
    + apply le_max_r.
    + revert Q HQ; rewrite Hm; clear k Hm; intros Q HQ.
      exists Q; split.
      * intros x v H.
        destruct (HP v ( j match le_lt_dec n j with left _ 0 | right Hj vec_pos v (nat2pos Hj) end))
          as [ _ ].
        - intros p; generalize (pos2nat_prop p); intros ; unfold get_env.
          destruct (le_lt_dec n (pos2nat p)); try .
          f_equal; apply nat2pos_pos2nat.
        - intros j Hj; unfold get_env.
          destruct (le_lt_dec n j); .
        - destruct ( _ H) as (e' & & k & ); auto.
          exists k.
          rewrite HQ in at 1.
          apply sss_steps_mm_pos_nat_inv with ( := vec_app v vec_zero) in .
          ++ destruct as ( & & ); simpl in .
             eq goal ; do 2 f_equal.
             { rewrite HQ; unfold mm_pos_nat; rewrite map_length; auto. }
             apply vec_pos_ext, pos_left_right_rect.
             ** intros p; rewrite , vec_pos_app_left, ; simpl.
                rewrite pos2nat_left.
                generalize (pos2nat_prop p); intros .
                rewrite get_set_env_neq; try .
                unfold get_env.
                destruct (le_lt_dec n (pos2nat p)); try .
                f_equal; apply nat2pos_pos2nat.
             ** intros p.
                rewrite vec_pos_app_right, , pos2nat_right; simpl snd.
                rewrite plus_comm.
                analyse pos p.
                -- rewrite pos2nat_fst; simpl.
                   rewrite ; rew env.
                -- rewrite pos2nat_nxt; simpl.
                   unfold vec_zero; rewrite vec_pos_set.
                   rewrite , get_set_env_neq; try .
                   unfold get_env; simpl.
                   destruct (le_lt_dec n (S (pos2nat p+n))); .
          ++ simpl; apply pos_left_right_rect.
             ** intros p; rewrite vec_pos_app_left, pos2nat_left.
                unfold get_env.
                generalize (pos2nat_prop p); intros.
                destruct (le_lt_dec n (pos2nat p)); try ; f_equal.
                rewrite nat2pos_pos2nat; auto.
             ** intros p; rewrite vec_pos_app_right, pos2nat_right.
                unfold vec_zero; rewrite vec_pos_set.
                unfold get_env.
                destruct (le_lt_dec n (n+pos2nat p)); try .
      * intros v ((j,w) & (k & ) & ); simpl in .
        set ( := j match le_lt_dec n j with left _ 0 | right Hj vec_pos v (nat2pos Hj) end).
        apply sss_steps_mm_pos_nat with ( := ) in .
        destruct as ( & & ); simpl in .
        apply HP with .
        - intros p; unfold , get_env.
          generalize (pos2nat_prop p); intros.
          destruct (le_lt_dec n (pos2nat p)); try .
          rewrite nat2pos_pos2nat; auto.
        - intros p Hp.
          unfold , get_env.
          destruct (le_lt_dec n p); .
        - rewrite HQ; exists (j,); split.
          ++ exists k; auto.
          ++ simpl; unfold mm_pos_nat; rew length; auto.
        - simpl; apply pos_left_right_rect.
          ++ intros p; rewrite vec_pos_app_left, pos2nat_left.
             unfold , get_env.
             generalize (pos2nat_prop p); intros.
             destruct (le_lt_dec n (pos2nat p)); try .
             rewrite nat2pos_pos2nat; auto.
          ++ intros p; rewrite vec_pos_app_right, pos2nat_right, vec_zero_spec.
             unfold , get_env.
             destruct (le_lt_dec n (n+pos2nat p)); try .
  Qed.


End ra_mm_comp.