Require Import List Arith Lia.

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

From Undecidability.MinskyMachines.MMenv
  Require Import env mme_defs.

Set Implicit Arguments.

Set Default Proof Using "Type".

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

Local Notation "P // s > t" := (sss_compute (mm_sss_env eq_nat_dec) P s t).
Local Notation "P // s -+> t" := (sss_progress (mm_sss_env eq_nat_dec) P s t).
Local Notation "P // s -[ k ] t" := (sss_steps (mm_sss_env eq_nat_dec) P k s t).
Local Notation "P // s ↓" := (sss_terminates (mm_sss_env eq_nat_dec ) P s).

Local Notation " e ⇢ x " := (@get_env _ _ e x).
Local Notation " e ⦃ x ⇠ v ⦄ " := (@set_env _ _ eq_nat_dec e x v).
Local Notation "x '⋈' y" := ( i : , xi = yi :> ) (at level 70, no associativity).

Section mm_env_utils.

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

  Section mm_transfert.

    Variables (src dst zero : ).

    Hypothesis (Hsd : src dst) (Hsz : src zero) (Hdz : dst zero).

    Definition mm_transfert i := DEC src (3+i) :: INC dst :: DEC zero i :: nil.

    Fact mm_transfert_length i : length (mm_transfert i) = 3.
    Proof. reflexivity. Qed.

    Let mm_transfert_spec i e k x :
           esrc = k
         edst = x
         ezero = 0
         e', e' esrc0⦄⦃dstk+x
                    (i,mm_transfert i) // (i,e) -+> (3+i,e').
    Proof.
      unfold mm_transfert.
      revert e x.
      induction k as [ | k IHk ]; intros e x .
      + exists e; split.
        * intros j; dest j src; dest j dst.
        * mm env DEC zero with src (3+i).
          mm env stop; f_equal; auto.
      + destruct IHk with (e := esrck⦄⦃dst1+x) (x := 1+x)
          as (e' & & ); rew env.
        exists e'; split.
        * intros j; rewrite .
          dest j dst; try .
          dest j src.
        * mm env DEC S with src (3+i) k.
          mm env INC with dst.
          mm env DEC zero with zero i; rew env.
          rewrite .
          apply sss_progress_compute; auto.
    Qed.


    Fact mm_transfert_progress i e :
           edst = 0
         ezero = 0
         e', e' esrc0⦄⦃dst⇠(esrc)⦄
                   (i,mm_transfert i) // (i,e) -+> (3+i,e').
    Proof using Hsz Hsd Hdz.
      intros .
      destruct mm_transfert_spec with (e := e) (x := 0) (i := i) (k := esrc)
        as (e' & & ); auto.
      exists e'; split; auto.
      intros j; rewrite , plus_comm; auto.
    Qed.


  End mm_transfert.

  Hint Rewrite mm_transfert_length : length_db.

  Section mm_erase.

    Variables (dst zero : ).

    Hypothesis (Hdz : dst zero).

    Definition mm_erase i := DEC dst (2+i) :: DEC zero i :: nil.

    Fact mm_erase_length i : length (mm_erase i) = 2.
    Proof. reflexivity. Qed.

    Let mm_erase_spec i e x :
           edst = x
         ezero = 0
         e', e' edst0
                    (i,mm_erase i) // (i,e) -+> (2+i,e').
    Proof.
      unfold mm_erase.
      revert e.
      induction x as [ | x IHx ]; intros e .
      + exists e; split.
        * intros j; dest j dst.
        * mm env DEC zero with dst (2+i).
          mm env stop; f_equal; auto.
      + destruct IHx with (e := edstx)
          as (e' & & ); rew env.
        exists e'; split.
        * intros j; rewrite ; dest j dst.
        * mm env DEC S with dst (2+i) x.
          mm env DEC zero with zero i; rew env.
          apply sss_progress_compute; auto.
    Qed.


    Fact mm_erase_progress i e :
           ezero = 0
         e', e' edst0
                   (i,mm_erase i) // (i,e) -+> (2+i,e').
    Proof using Hdz.
      intros .
      destruct mm_erase_spec with (e := e) (i := i) (x := edst)
        as (e' & & ); auto.
      exists e'; split; auto.
    Qed.


  End mm_erase.

  Hint Rewrite mm_erase_length : length_db.

  Opaque mm_erase.

  Section mm_list_erase.

    Variable (zero : ).

    Fixpoint mm_list_erase ll i :=
      match ll with
        | nil nil
        | dst::ll mm_erase dst zero i mm_list_erase ll (2+i)
      end.

    Fact mm_list_erase_length ll i : length (mm_list_erase ll i) = 2*length ll.
    Proof.
      revert i; induction ll as [ | dst ll IH ]; simpl; intros i; rew length; auto.
      rewrite IH; ring.
    Qed.


    Fact mm_list_erase_compute ll i e :
            Forall ( x x zero) ll
          ezero = 0
          e', ( x, In x ll e'x = 0)
                     ( x, In x ll e'x = ex)
                     (i,mm_list_erase ll i) // (i,e) ->> (2*length ll+i,e').
    Proof.
      intros H; revert H i e.
      induction 1 as [ | dst ll ]; intros i e .
      + simpl; exists e; repeat split; try tauto; mm env stop.
      + destruct mm_erase_progress with (dst := dst) (zero := zero) (i := i) (e := e)
          as ( & & ); auto.
        destruct with (i := 2+i) (e := ) as ( & & & ).
        { rewrite ; rew env. }
        exists ; split; [ | split ].
        * intros x [ H | H ]; subst; auto.
          destruct in_dec with (1 := eq_nat_dec) (a := x) (l := ll) as [ | ]; auto.
          rewrite ; auto; rewrite ; rew env.
        * simpl; intros x Hx.
          rewrite , ; try tauto.
          dest x dst; destruct Hx; auto.
        * apply sss_compute_trans with (2+i,).
          - apply sss_progress_compute.
            simpl; revert ; apply subcode_sss_progress; auto.
          - replace (2*length (dst::ll)+i) with (2*length ll+(2+i)) by (rew length; ).
            revert ; simpl; apply subcode_sss_compute; auto.
    Qed.


  End mm_list_erase.

  Hint Rewrite mm_list_erase_length : length_db.

  Section mm_multi_erase.

    Variables (dst zero k : ).

    Definition mm_multi_erase := mm_list_erase zero (list_an dst k).

    Fact mm_multi_erase_length i : length (mm_multi_erase i) = 2*k.
    Proof. unfold mm_multi_erase; rew length; auto. Qed.

    Hypothesis (Hdz : dst+k zero).

    Fact mm_multi_erase_compute i e :
                    ezero = 0
       e', ( j, dst j < k+dst e'j = 0)
                  ( j, dst j < k+dst e'j = ej)
                  (i,mm_multi_erase i) // (i,e) ->> (2*k+i,e').
    Proof using Hdz.
      intros H; unfold mm_multi_erase.
      destruct mm_list_erase_compute
        with (zero := zero) (ll := list_an dst k) (i := i) (e := e)
        as (e' & & & ); auto.
      * rewrite Forall_forall; intros j; rewrite list_an_spec; intros; .
      * rew length in ; exists e'; msplit 2; auto.
        + intros; apply , list_an_spec; .
        + intros; apply ; rewrite list_an_spec; .
    Qed.


  End mm_multi_erase.

  Hint Rewrite mm_multi_erase_length : length_db.

  Section mm_dup.

    Variables (src dst tmp zero : ).

    Hypothesis (Hsd : src dst) (Hst : src tmp) (Hsz : src zero)
               (Hdt : dst tmp) (Hdz : dst zero)
               (Htz : tmp zero).

    Definition mm_dup i := DEC src (4+i) :: INC dst :: INC tmp :: DEC zero i :: nil.

    Fact mm_dup_length i : length (mm_dup i) = 4.
    Proof. reflexivity. Qed.

    Let mm_dup_spec i e k x y :
           esrc = k
         edst = x
         etmp = y
         ezero = 0
         e', e' esrc0⦄⦃dstk+x⦄⦃tmpk+y
                    (i,mm_dup i) // (i,e) -+> (4+i,e').
    Proof.
      unfold mm_dup.
      revert e x y.
      induction k as [ | k IHk ]; intros e x y .
      + exists e; split.
        * intros j; dest j src; dest j dst; dest j tmp.
        * mm env DEC zero with src (4+i).
          mm env stop; f_equal; auto.
      + destruct IHk with (e := esrck⦄⦃dst1+x⦄⦃tmp1+y) (x := 1+x) (y := 1+y)
          as (e' & & ); rew env.
        exists e'; split.
        * intros j; rewrite .
          dest j tmp; try .
          dest j dst; try .
          dest j src.
        * mm env DEC S with src (4+i) k.
          mm env INC with dst.
          mm env INC with tmp.
          mm env DEC zero with zero i; rew env.
          rewrite , .
          apply sss_progress_compute; auto.
    Qed.


    Fact mm_dup_progress i e :
           edst = 0
         etmp = 0
         ezero = 0
         e', e' esrc0⦄⦃dst⇠(esrc)⦄⦃tmp⇠(esrc)⦄
                   (i,mm_dup i) // (i,e) -+> (4+i,e').
    Proof using Htz Hsz Hst Hsd Hdz Hdt.
      intros .
      destruct mm_dup_spec with (e := e) (x := 0) (y := 0) (i := i) (k := esrc)
        as (e' & & ); auto.
      exists e'; split; auto.
      intros j; rewrite , plus_comm; auto.
    Qed.


  End mm_dup.

  Hint Rewrite mm_dup_length : length_db.

  Section mm_copy.

    Variables (src dst tmp zero : ).

    Hypothesis (Hsd : src dst) (Hst : src tmp) (Hsz : src zero)
               (Hdt : dst tmp) (Hdz : dst zero)
               (Htz : tmp zero).

    Definition mm_copy i := mm_erase dst zero i
                          mm_transfert src tmp zero (2+i)
                          mm_dup tmp src dst zero (5+i).

    Fact mm_copy_length i : length (mm_copy i) = 9.
    Proof. reflexivity. Qed.

    Fact mm_copy_progress i e :
           etmp = 0
         ezero = 0
         e', e' edst⇠(esrc)⦄
                   (i,mm_copy i) // (i,e) -+> (9+i,e').
    Proof using Htz Hsz Hst Hsd Hdz Hdt.
      intros .
      unfold mm_copy.
      destruct mm_erase_progress with (1 := Hdz) (i := i) (e := e)
        as ( & & ); auto.
      destruct mm_transfert_progress
        with (src := src) (dst := tmp) (zero := zero) (i := 2+i) (e := )
        as ( & & ); auto.
      1,2: rewrite ; rew env.
      destruct mm_dup_progress
        with (src := tmp) (dst := src) (tmp := dst) (zero := zero) (i := 5+i) (e := )
        as ( & & ); auto.
      1,2,3: rewrite ; rew env; rewrite ; rew env.
      exists ; split.
      * intros j.
        rewrite ; dest j dst.
        - rewrite ; rew env.
          rewrite ; rew env.
        - dest j src.
          + rewrite ; rew env.
            rewrite ; rew env.
          + dest j tmp.
            rewrite ; rew env.
            rewrite ; rew env.
      * apply sss_progress_trans with (2+i,).
        { revert ; apply subcode_sss_progress; auto. }
        apply sss_progress_trans with (5+i,).
        { revert ; apply subcode_sss_progress; auto. }
        { revert ; apply subcode_sss_progress; auto. }
    Qed.


  End mm_copy.

  Hint Rewrite mm_copy_length : length_db.

  Section mm_multi_copy.

    Variables (tmp zero : ).

    Fixpoint mm_multi_copy k src dst i :=
      match k with
        | 0 nil
        | S k mm_copy src dst tmp zero i mm_multi_copy k (S src) (S dst) (9+i)
      end.

    Fact mm_multi_copy_length k src dst i : length (mm_multi_copy k src dst i) = 9*k.
    Proof.
      revert src dst i; induction k as [ | k IHk ]; intros src dst i; simpl; auto.
      rew length; rewrite IHk; .
    Qed.


    Fact mm_multi_copy_compute k src dst i e :
            k+src dst
          k+dst tmp
          k+dst zero
          tmp zero
          etmp = 0
          ezero = 0
          e', ( j, j < k e'⇢(j+dst) = e⇢(j+src))
                     ( j, dst j < k+dst e'j = ej)
                     (i,mm_multi_copy k src dst i) // (i,e) ->> (9*k+i,e').
    Proof.
      revert src dst i e; induction k as [ | k IHk ]; intros src dst i e; intros .
      + exists e; split; [ | split ]; try (intros; ).
        mm env stop.
      + destruct (@mm_copy_progress src dst tmp zero) with (i := i) (e := e)
          as ( & & ); try .
        destruct (IHk (S src) (S dst) (9+i)) with (e := )
          as ( & & & ); try .
        { rewrite ; assert (dst tmp); try ; rew env. }
        { rewrite ; assert (dst zero); try ; rew env. }
        exists ; split; [ | split ].
        * intros [ | j ] Hj.
          - rewrite ; try ; simpl; rewrite ; rew env.
          - replace (S j + dst) with (j+S dst) by .
            replace (S j + src) with (j+S src) by .
            rewrite ; try .
            rewrite .
            assert (dst j+S src) by ; rew env.
        * intros j Hj.
          rewrite ; try .
          rewrite .
          assert (j dst) by ; rew env.
        * unfold mm_multi_copy; fold mm_multi_copy.
          apply sss_compute_trans with (9+i,).
          - apply sss_progress_compute.
            revert ; apply subcode_sss_progress; auto.
          - replace (9*S k+i) with (9*k+(9+i)) by .
            revert ; apply subcode_sss_compute.
            subcode_tac; rewrite app_nil_end; auto.
    Qed.


  End mm_multi_copy.

  Hint Rewrite mm_multi_copy_length : length_db.

  Section mm_set.

    Variables (dst zero : ) (Hdz : dst zero).

    Let mm_incs : list (mm_instr ) :=
      fix loop n := match n with
        | 0 nil
        | S n INC dst :: loop n
      end.

    Let mm_incs_length n : length (mm_incs n) = n.
    Proof. induction n; simpl; f_equal; auto. Qed.

    Let mm_incs_spec i e n :
       e', e' edst⇠(n+(edst))⦄
               (i,mm_incs n) // (i,e) ->> (n+i,e').
    Proof.
      revert i e; induction n as [ | n IHn ]; intros i e; simpl.
      + exists e; split.
        * intros j; dest j dst.
        * mm env stop.
      + destruct (IHn (1+i) (edst1+(edst))) as (e' & & ).
        exists e'; split.
        * intros j; rewrite ; dest j dst.
        * mm env INC with dst.
          replace (S (n+i)) with (n+(1+i)) by .
          revert ; apply subcode_sss_compute.
          subcode_tac; simpl; rewrite app_nil_end; auto.
    Qed.


    Definition mm_set n i := mm_erase dst zero i mm_incs n.

    Fact mm_set_length n i : length (mm_set n i) = 2+n.
    Proof.
      unfold mm_set; rew length; rewrite mm_incs_length; auto.
    Qed.


    Fact mm_set_progress n i e :
            ezero = 0
          e', e' edstn
                    (i,mm_set n i) // (i,e) -+> (2+n+i,e').
    Proof using Hdz.
      intros H; unfold mm_set.
      destruct (@mm_erase_progress _ _ Hdz i _ H) as ( & & ).
      destruct (mm_incs_spec (2+i) n) as ( & & ).
      exists ; split.
      + intros j; rewrite , .
        dest j dst; rewrite ; rew env.
      + apply sss_progress_compute_trans with (2+i,).
        * revert ; apply subcode_sss_progress; auto.
        * replace (2+n+i) with (n+(2+i)) by .
          revert ; apply subcode_sss_compute; auto.
    Qed.


  End mm_set.

End mm_env_utils.

Global Hint Rewrite mm_set_length mm_transfert_length mm_erase_length
             mm_list_erase_length mm_multi_erase_length
             mm_dup_length mm_copy_length mm_multi_copy_length
             mm_set_length : length_db.