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
Facts Nat_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) : nat :=
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 (fs i, (a, b)).
Lemma fs_pos i : 0 < i -> fs i = ((i - 1) + a0 + b0).
Proof. case: i => [|?] /=; lia. Qed.
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 -> CM2.steps M n (0, (0, 0)) =
Some (n, (n, 0)).
Proof.
elim: n; first done.
move=> n + /= ? => ->; first by lia.
rewrite /M /CM2.step /= 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 -> CM2.steps M n (a0, (a0, 0)) =
Some (n + a0, (a0, n)).
Proof.
elim: n; first done.
move=> n + /= ? => ->; first by lia.
rewrite /M /CM2.step /= 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 : CM2.steps M (a0 + b0) (0, (0, 0)) =
Some (a0 + b0, (a0, b0)).
Proof.
have /steps_plus -> := init_M_a0 a0 ltac:(lia).
have ->: a0 + b0 = b0 + a0 by lia.
apply: init_M_b0. 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) = Some (encode_config y).
Proof.
case. move: x => [i [a b]] op.
move: (program_index_spec i) => [? ? H [/H] | ]; first done.
rewrite /CM2.step.
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, CM2.steps M n (encode_config x) = Some (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 rewrite Hnxy.
Qed.
Lemma M_to_P_step (x: mm2_config) (y: CM2.Config) :
CM2.step M (encode_config x) = Some y ->
exists z, y = encode_config z /\ mm2_step P x z.
Proof.
move: x y => [i [a b]] [i' [a' b']]. rewrite /CM2.step.
move: (program_index_spec i) => [{}i | {}i op] -> HiP /=; first done.
move=> H.
suff: exists z : mm2_config,
(i', (a', b')) = encode_config z /\ mm2_atom op (S i, (a, b)) z.
{ move=> [z [? ?]]. exists z. split; first done. by exists op. }
move: HiP => /mm2_instr_at_bounds ?.
move: op H => [||j|j] /=.
- move=> [???]. exists (S (S i), (S a, b)). split.
+ rewrite /=. congr (_, (_, _)); lia.
+ by apply: in_mm2s_inc_a.
- move=> [???]. exists (S (S i), (a, S b)). split.
+ rewrite /=. congr (_, (_, _)); lia.
+ by apply: in_mm2s_inc_b.
- move: a => [|a] [???].
+ exists (S (S i), (0, b)). split.
* rewrite /=. congr (_, (_, _)); lia.
* by apply: in_mm2s_dec_a0.
+ exists (j, (a, b)). split.
* rewrite /=. congr (_, (_, _)); lia.
* by apply: in_mm2s_dec_aS.
- move: b => [|b] [???].
+ exists (S (S i), (a, 0)). split.
* rewrite /=. congr (_, (_, _)); lia.
* by apply: in_mm2s_dec_b0.
+ exists (j, (a, b)). split.
* rewrite /=. congr (_, (_, _)); lia.
* by apply: in_mm2s_dec_bS.
Qed.
Lemma M_to_P (n: nat) (x: mm2_config) (y: CM2.Config) :
CM2.steps M n (encode_config x) = Some y ->
exists z, y = encode_config z /\ clos_refl_trans _ (mm2_step P) x z.
Proof.
elim: n x y.
{ move=> x y [] <-. exists x. split; [done|by apply: rt_refl]. }
move=> n IH x y /=. rewrite obind_oiter.
case Hx': (CM2.step M (encode_config x)) =>[x'|]; last by rewrite oiter_None.
move: Hx' => /M_to_P_step [y'] [->] Hxy'.
move=> /IH [z] [?] ?. exists z. split; first done.
apply: rt_trans; [apply rt_step|]; by eassumption.
Qed.
Lemma P_stop_iff_M_halting (x: mm2_config) :
mm2_stop P x <-> CM2.step M (encode_config x) = None.
Proof.
move: x => [i [a b]]. rewrite /CM2.step /=. 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.
+ by move: op a b => [||j|j] [|a] [|b] _.
Qed.
Lemma transport : MM2_HALTING (P, a0, b0) -> CM2.CM2_HALT M.
Proof.
move=> [z] [/P_to_M] [n /= Hn] /P_stop_iff_M_halting Hz.
exists ((a0 + b0) + (n + 1)).
by rewrite (steps_plus init_M) (steps_plus Hn).
Qed.
Lemma reflection: CM2.CM2_HALT M -> MM2_HALTING (P, a0, b0).
Proof.
move=> [n] /(steps_k_monotone ((a0 + b0) + n)) => /(_ ltac:(lia)).
rewrite (steps_plus init_M) -/(encode_config (1, (a0, b0))).
elim: n; first done.
move=> n /=.
case Hy: (CM2.steps M n (a0 + b0, (a0, b0))) => [y|]; first last.
{ move=> IH _. by apply: IH. }
move: Hy. rewrite -/(encode_config (1, (a0, b0))).
move=> /M_to_P [z] [->] Hz _ /P_stop_iff_M_halting ?.
by exists z.
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.