Require Import List Lia Relations.Relation_Operators Relations.Operators_Properties.
Import ListNotations.
Require Import Undecidability.MinskyMachines.MM2.
Require Undecidability.CounterMachines.CM2.
From Undecidability.CounterMachines.Util Require Import
Nat_facts List_facts MM2_facts CM2_facts.
Require Import ssreflect ssrbool ssrfun.
Set Default Proof Using "Type".
Set Default Goal Selector "!".
Module Argument.
Section MM2_CM2.
Variable (P: list mm2_instr). Variables (a0 b0: nat).
Definition mm2_config : Set := (nat*(nat*nat)).
Definition fs (i: nat) : CM2.State :=
if i is S i then i + a0 + b0 else (length P) + a0 + b0.
Definition encode_instruction (mmi: mm2_instr) : CM2.Instruction :=
match mmi with
| mm2_inc_a => CM2.inc false
| mm2_inc_b => CM2.inc true
| mm2_dec_a j => CM2.dec false (fs j)
| mm2_dec_b j => CM2.dec true (fs j)
end.
Definition M : list CM2.Instruction :=
(repeat (CM2.inc false) a0) ++ (repeat (CM2.inc true) b0) ++ map encode_instruction P.
Definition encode_config (x: mm2_config) : CM2.Config :=
let: (i, (a, b)) := x in {| CM2.state := fs i; CM2.value1 := a; CM2.value2 := b |}.
Lemma seek_M {i M'} : nth_error (repeat (CM2.inc false) a0 ++ repeat (CM2.inc true) b0 ++ M') (i + a0 + b0) = nth_error M' i.
Proof.
rewrite nth_error_app2; [rewrite ?repeat_length; by lia |].
rewrite nth_error_app2; [rewrite ?repeat_length; by lia |].
congr @nth_error. rewrite ?repeat_length. by lia.
Qed.
Lemma init_M_a0 (n: nat) : n <= a0 -> Nat.iter n (CM2.step M) {| CM2.state := 0; CM2.value1 := 0; CM2.value2 := 0 |} =
{| CM2.state := n; CM2.value1 := n; CM2.value2 := 0 |}.
Proof.
elim: n; first done.
move=> n + /= ? => ->; first by lia.
rewrite /= /M nth_error_app1 ?repeat_length; first by lia.
rewrite (nth_error_nth' _ (CM2.inc false)) ?repeat_length ?nth_repeat; [by lia | done].
Qed.
Lemma init_M_b0 (n: nat) : n <= b0 -> Nat.iter n (CM2.step M) {| CM2.state := a0; CM2.value1 := a0; CM2.value2 := 0 |} =
{| CM2.state := n + a0; CM2.value1 := a0; CM2.value2 := n |}.
Proof.
elim: n; first done.
move=> n + /= ? => ->; first by lia.
rewrite /= /M nth_error_app2 ?repeat_length; first by lia.
rewrite nth_error_app1 ?repeat_length; first by lia.
rewrite (nth_error_nth' _ (CM2.inc true)) ?repeat_length ?nth_repeat; [by lia | done].
Qed.
Lemma init_M : Nat.iter (a0 + b0) (CM2.step M) {| CM2.state := 0; CM2.value1 := 0; CM2.value2 := 0 |} =
{| CM2.state := a0 + b0; CM2.value1 := a0; CM2.value2 := b0 |}.
Proof.
rewrite iter_plus ?init_M_a0 ?init_M_b0; f_equal; by lia.
Qed.
Lemma length_M : length M = a0 + b0 + length P.
Proof.
rewrite /M ?app_length ?repeat_length ?map_length. by lia.
Qed.
Inductive program_index : nat -> Prop :=
| seek_M_None {i} : nth_error M (fs i) = None -> (forall op, not (mm2_instr_at op i P)) -> program_index i
| seek_M_Some {i op} : nth_error M (fs (1+i)) = Some (encode_instruction op) ->
mm2_instr_at op (1+i) P -> program_index (1+i).
Lemma program_index_spec (i: nat) : program_index i.
Proof.
move Ht: (nth_error M (fs i)) => t. case: t Ht.
- move: i => [|i] op.
+ move=> ?. have /nth_error_Some : nth_error M (fs 0) <> None by congruence.
rewrite length_M /=. by lia.
+ move=> H. have /nth_error_None : nth_error M (fs (S i)) <> None by congruence.
rewrite length_M /= => ?. have /mm2_mmi_lookup [op' HP] : i < length P by lia.
move=> [:Hop']. apply: (@seek_M_Some i op'); last (abstract: Hop').
* rewrite /M HP seek_M nth_error_map nth_error_app2 ?firstn_length; first by lia.
by have ->: i - Nat.min i (length P) = 0 by lia.
* do 2 eexists. constructor; [by eassumption | rewrite firstn_length; by lia].
- rewrite /M. move=> Hi. apply: seek_M_None; first done.
move=> op [l] [r] [HP ?]. subst i. move: HP Hi => ->.
by rewrite seek_M nth_error_map nth_error_app2 ?PeanoNat.Nat.sub_diag.
Qed.
Lemma P_to_M_step {x y: mm2_config} :
mm2_step P x y -> CM2.step M (encode_config x) = encode_config y.
Proof.
case. move: x => [i [a b]] op.
move: (program_index_spec i) => [? ? H [/H] | ]; first done.
move=> {}i op' /= -> Hop' [/(mm2_instr_at_unique Hop') <- H]. by inversion H.
Qed.
Lemma P_to_M (x y: mm2_config) :
clos_refl_trans _ (mm2_step P) x y ->
exists n, Nat.iter n (CM2.step M) (encode_config x) = encode_config y.
Proof.
move /clos_rt_rtn1_iff. elim; first by (exists 0).
move=> {}y z /P_to_M_step Hyz _ [n Hnxy].
exists (1+n) => /=. by congruence.
Qed.
Lemma M_to_P_step (x: mm2_config) :
CM2.halting M (encode_config x) \/ exists y, CM2.step M (encode_config x) = encode_config y /\ mm2_step P x y.
Proof.
move: x => [i [a b]]. rewrite /CM2.halting /=.
move: (program_index_spec i) => [{}i | {}i op] -> HiP; [by left | right].
have [y Hy] := @mm2_progress op (1 + i, (a, b)).
exists y. constructor; [by inversion Hy | by exists op].
Qed.
Lemma M_to_P (n: nat) (x: mm2_config) :
exists y, Nat.iter n (CM2.step M) (encode_config x) = encode_config y /\ clos_refl_trans _ (mm2_step P) x y.
Proof.
elim: n x; first by (move=> x; exists x; constructor; [done | by apply: rt_refl]).
move=> n + x. move: (M_to_P_step x) => [Hx _| [y [HMxy HPxy]]].
- exists x. constructor; last by apply: rt_refl.
elim: n; [done | by move=> n /= ->].
- move=> /(_ y) => [[z [? ?]]]. exists z. constructor.
+ have ->: S n = 1 + n by lia. by rewrite iter_plus /= HMxy.
+ apply: rt_trans; [apply: rt_step | eassumption]; done.
Qed.
Lemma P_stop_iff_M_halting (x: mm2_config) :
mm2_stop P x <-> CM2.halting M (encode_config x).
Proof.
move: x => [i [a b]]. rewrite /CM2.halting /=. constructor.
- move: (program_index_spec i) => [{}i | {}i op] ->; first done.
have [y Hy] := @mm2_progress op (1 + i, (a, b)).
move=> ? /(_ y) + /ltac:(exfalso). apply. by exists op.
- move: (program_index_spec i) => [{}i | {}i op] ->.
+ move=> + _ y [] op [? ?] => /(_ op). by apply.
+ move: op a b => [||j|j] [|a] [|b] _ /= []; by lia.
Qed.
Lemma transport : MM2_HALTING (P, a0, b0) -> CM2.CM2_HALT M.
Proof.
move=> [z] [/P_to_M] [n +] /P_stop_iff_M_halting => <- ?.
exists ((a0 + b0) + n). by rewrite iter_plus init_M.
Qed.
Lemma reflection: CM2.CM2_HALT M -> MM2_HALTING (P, a0, b0).
Proof.
move=> [n] /(halting_monotone n ((a0 + b0) + n)) => /(_ ltac:(lia)).
rewrite iter_plus init_M -/(encode_config (1, (a0, b0))).
have [y [-> ?]] := M_to_P n (1, (a0, b0)).
move=> ?. exists y. constructor; first done.
by apply /P_stop_iff_M_halting.
Qed.
End MM2_CM2.
End Argument.
Require Import Undecidability.Synthetic.Definitions.
Theorem reduction : MM2_HALTING ⪯ CM2.CM2_HALT.
Proof.
exists (fun '(P, a0, b0) => Argument.M P a0 b0).
move => [[P a0] b0]. constructor.
- exact (Argument.transport P a0 b0).
- exact (Argument.reflection P a0 b0).
Qed.