From Coq Require Import List Lia.

From Undecidability.Shared.Libs.DLW
  Require Import Vec.pos Vec.vec Code.sss.

Require Import Undecidability.MinskyMachines.MM.

Set Implicit Arguments.

Halting problem for Minsky machines MM_HALTING



Notation INC := mm_inc.
Notation DEC := mm_dec.


Section Minsky_Machine.

  Variable (n : ).

  Definition mm_state := (*vec n)%type.

  Local Notation "e #> x" := (vec_pos e x).
  Local Notation "e [ v / x ]" := (vec_change e x v).


  Inductive mm_sss : mm_instr (pos n) mm_state mm_state Prop :=
    | in_mm_sss_inc : i x v, INC x // (i,v) -1> (1+i,v[(S (v#>x))/x])
    | in_mm_sss_dec_0 : i x k v, v#>x = O DEC x k // (i,v) -1> (k,v)
    | in_mm_sss_dec_1 : i x k v u, v#>x = S u DEC x k // (i,v) -1> (1+i,v[u/x])
  where "i // s -1> t" := (mm_sss i s t).

End Minsky_Machine.

Lemma eval_to_sss_compute n i (P : list (mm_instr (pos n))) c (v : vec n) c' v' :
  eval (i, P) (c, v) (c', v') sss_compute (@mm_sss _) (i,P) (c, v) (c', v').
Proof.
  generalize (i, P) as iP.
  generalize (c, v) as cv.
  generalize (c', v') as . clear.
  induction 1 as [ i P c v
                 | i P c v j c' v' H [k IH]
                 | i P c v j c' v' l H [k IH]
                 | i P c v j c' v' H [k IH] ].
  - exists 0. econstructor.
  - exists (1 + k).
    eapply nth_error_split in as ( & & & Hl).
    repeat econstructor.
    + f_equal. .
    + replace (1 + c) with (c + 1) by .
      now replace (S (vec_pos v j)) with (vec_pos v j + 1) by .
  - exists (1 + k).
    eapply nth_error_split in as ( & & & Hl).
    econstructor; [ | eauto].
    repeat econstructor.
    + f_equal. .
    + replace (c + 1) with (1 + c) by . econstructor. eauto.
  - exists (1 + k).
    eapply nth_error_split in as ( & & & Hl).
    econstructor; [ | eauto].
    repeat econstructor.
    * f_equal. .
    * eauto.
Qed.


Lemma eval_to_sss_out_code n i (P : list (mm_instr (pos n))) c (v : vec n) c' v' :
  eval (i, P) (c, v) (c', v') subcode.out_code (fst (c', v')) (i, P).
Proof.
  generalize (i, P) as iP.
  generalize (c, v) as cv.
  generalize (c', v') as . clear.
  induction 1 as [ i P c v
                 | i P c v j b c' v' H
                 | i P c v j c' v' l H
                 | i P c v j c' v' H ]; cbn in *.
  all: .
Qed.


Lemma sss_output_to_eval n i (P : list (mm_instr (pos n))) c (v : vec n) c' v' :
  sss_output (@mm_sss _) (i,P) (c, v) (c', v') eval (i, P) (c, v) (c', v').
Proof.
  generalize (i, P) as iP.
  generalize (c, v) as cv.
  generalize (c', v') as . clear.
  intros ? ? ? [[k ] ].
  induction as [cv | (c', v') (k & l & i & r & d & & & HH) H IH] in * |- *.
  - destruct iP as (i, P), cv as (c, v);
      try destruct as (c', v').
    econstructor. cbn in *. .
  - revert IH H .
    inversion_clear HH as [ | | ]; subst; intros IH' HHH IH % IH'; clear IH'.
    + eapply eval_mm_inc; [ | | ].
      * rewrite nth_error_app2; [ | ].
        now replace (k + length l - k - length l) with 0 by .
      * replace (k + length l + 1) with (1 + (k + length l)) by .
        now replace (vec_pos d x + 1) with (S (vec_pos d x)) by .
    + eapply eval_mm_dec_empty; [ | | eauto ..].
      rewrite nth_error_app2; [ | ].
      now replace (k + length l - k - length l) with 0 by .
    + eapply eval_mm_dec_S; [ | | eauto ..].
      * rewrite nth_error_app2; [ | ].
        now replace (k + length l - k - length l) with 0 by .
      * now replace (k + length l + 1) with (1 + (k + length l)) by .
Qed.


Theorem eval_iff n i (P : list (mm_instr (pos n))) c (v : vec n) c' v' :
  eval (i, P) (c, v) (c', v') sss_output (@mm_sss _) (i,P) (c, v) (c', v').
Proof.
  split.
  - intros H. split.
    + now eapply eval_to_sss_compute.
    + eapply eval_to_sss_out_code. eassumption.
  - intros H. now eapply sss_output_to_eval.
Qed.


Section MM_problems.

  Notation "P // s ~~> t" := (sss_output (@mm_sss _) P s t).
  Notation "P // s ↓" := (sss_terminates (@mm_sss _) P s).

  Definition MM_PROBLEM := { n : & { P : list (mm_instr (pos n)) & vec n } }.

  Definition MM_HALTS_ON_ZERO (P : MM_PROBLEM) :=
    match P with existT _ n (existT _ P v) (1,P) // (1,v) ~~> (0,vec_zero) end.

  Definition MM_HALTING (P : MM_PROBLEM) :=
    match P with existT _ n (existT _ P v) (1, P) // (1, v) end.

  Definition Halt_MM (P : MM_PROBLEM) :=
    match P with existT _ n (existT _ P v) c v', eval (1, P) (1, v) (c, v') end.

  Definition MM_2_PROBLEM := { P : list (mm_instr (pos 2)) & vec 2 }.

  Definition MM_2_HALTING (P : MM_2_PROBLEM) :=
    match P with existT _ P v (1, P) // (1, v) end.

End MM_problems.

Lemma Halt_MM_iff P :
  Halt_MM P MM_HALTING P.
Proof.
  destruct P as (n & P & v); cbn.
  split.
  - intros (c' & v' & H % eval_iff). eexists. eauto.
  - intros [(c', v') H % eval_iff]. eauto.
Qed.