From Undecidability Require StringRewriting.SR.
From Undecidability Require StringRewriting.Util.Definitions.
Module SR_facts := StringRewriting.Util.Definitions.
From Undecidability Require Import TM.SBTM TM.Util.SBTM_facts.
From Undecidability Require Import StringRewriting.Reductions.SBTM_HALT_to_SR.

Require Import PeanoNat Lia.

Require Import List ssreflect ssrbool ssrfun.
Import ListNotations SBTMNotations.

Set Default Proof Using "Type".
Set Default Goal Selector "!".

Section Construction.
  Context (M : SBTM).

  #[local] Notation "⦇" := 0.
  #[local] Notation "⦈" := 1.
  #[local] Notation "# a" := (encode_symbol a) (at level 1).
  #[local] Notation encode_state q := (encode_state M q).
  #[local] Notation encode_config q t := (encode_config M q t).
  #[local] Notation encode_rule qa := (encode_rule M qa).

  #[local] Notation srs := (srs M).

  Lemma simulation q t k :
    steps M k (q, t) = None ->
    SR.rewt (srs ++ map SR.swap srs) (encode_config q t) [; ].
  Proof.
    elim: k q t; first done.
    move=> k IH q t.
    rewrite (steps_plus 1 k). case E: (steps M 1 _) => [[q' t']|].
    - move: E => /simulation_step ?.
      move=> /IH. apply: SR.rewS. apply /SR_facts.rew_app_inv.
      by left.
    - move: E => /simulation_halt /SR_facts.rewt_subset + _. apply.
      apply: incl_appl. by apply: incl_refl.
  Qed.

  Lemma rew_swap {X} {R : SR.SRS X} {x y} : SR.rew (map SR.swap R) x y -> SR.rew R y x.
  Proof.
    move ER': (map _ _) => R' H.
    case: H ER' => > + ?. subst R'.
    by move=> /in_map_iff [[??]] [[]] <- <- /SR.rewB.
  Qed.

  Lemma app_left_marker u ls : u ++ [] = :: rev (map encode_symbol ls) -> u = [] /\ ls = [].
  Proof.
    move: u ls => [|c u] [|l ls].
    - done.
    - by move=> /(@app_inj_tail nat _ (_ :: _)) [].
    - by case: u.
    - move=> /(@app_inj_tail nat _ (_ :: _)) []. by case: l.
  Qed.

  Lemma left_marker_nth q t n : not (nth_error (encode_config q t) (S n) = Some ).
  Proof.
    move: t => [[ls a] rs] /=. elim /rev_ind: ls n.
    - move=> [|[|n]] /=; [by case: a|done|].
      elim: rs n. { by move=> [|[|?]]. }
      move=> r rs IH [|n]; first by case: r.
      by apply: IH.
    - move=> l ls IH [|n]; rewrite map_app rev_app_distr; first by case: l.
      by apply: IH.
  Qed.

  Lemma inverse_simulation_back_step q t x q' t' :
    SR.rew (map SR.swap srs) (encode_config q t) x ->
    step M (q, t) = Some (q', t') ->
    exists q'' t'', x = encode_config q'' t'' /\ step M (q'', t'') = Some (q, t).
  Proof.
    have reassoc (n m : nat) (u v : list nat) : u ++ (n :: m :: v) = (u ++ [n]) ++ m :: v.
    { by rewrite -app_assoc. }
    move=> /rew_swap. move Ey: (encode_config q t) => y Hxy.
    case: Hxy Ey => u v > /In_srsI [].
    - move=> > _ H. exfalso. apply: (left_marker_nth q t (length u)).
      rewrite H. rewrite nth_error_app2; first by lia.
      by have ->: S (length u) - length u = 1 by lia.
    - move: t => [[??]?] > E b /encode_config_eq_app [->] [->] [-> ->].
      by rewrite /step E.
    - move: t => [[??]?] > E b /encode_config_eq_app [->] [->] [-> ->].
      by rewrite /step E.
    - move: t => [[ls ?] rs] q'' a ? a' E H _. move: H E. rewrite !(reassoc ).
      move=> /encode_config_eq_app [->] [->] [/app_left_marker] [-> ->].
      move: rs => [|r rs]. { by case: a'. }
      move=> [] /encode_symbol_inj -> -> E.
      exists q'', ([], a, rs). split; first done.
      by rewrite /step E.
    - move: t => [[ls ?] rs] q'' a ? a' E b H _. move: H E.
      move=> /encode_config_eq_app [->] [->] [->].
      move: rs => [|r rs]. { by case: a'. }
      move=> [] /encode_symbol_inj -> -> E.
      exists q'', (b :: ls, a, rs). split; first by rewrite /= -!app_assoc.
      by rewrite /step E.
    - move: t => [[ls ?] rs] q'' a ? a' E H _. move: H E. rewrite !(reassoc #a').
      move=> /encode_config_eq_app [->] [->] [].
      move: ls => [|l ls]. { move=> /(@app_inj_tail nat _ []) []. by case: a'. }
      move=> /= /(@app_inj_tail nat _ (_ :: _)) [-> /encode_symbol_inj ->].
      move: rs => [|r rs]; last by case: r.
      move=> [->] E.
      exists q'', (ls, a, []). split; first done.
      by rewrite /step E.
    - move: t => [[ls ?] rs] q'' a ? a' E b H _. move: H E. rewrite !(reassoc #a').
      move=> /encode_config_eq_app [->] [->] [+ ->].
      move: ls => [|l ls]. { move=> /(@app_inj_tail nat _ []) []. by case: a'. } cbn.
      move=> /(@app_inj_tail nat _ (_ :: _)) [-> /encode_symbol_inj ->] E.
      exists q'', (ls, a, b :: rs). split; first done.
      by rewrite /step E.
  Qed.

  Lemma inverse_simulation q t :
    SR.rewt (srs ++ map SR.swap srs) (encode_config q t) [; ] ->
    exists k, steps M k (q, t) = None.
  Proof.
    move Eu: (encode_config q t) => u.
    move Ev: ([; ]) => v Huv.
    elim: Huv q t Eu Ev. { by move=> ?? [[??]?] <-. }
    move=> x y z + _ + q t ??. subst x z.
    case E: (step M (q, t)) => [[q' t']|]; first last.
    { move=> *. by exists 1. }
    move=> /SR_facts.rew_app_inv [].
    { move: (E) => /inverse_simulation_step /[apply] ->.
      move=> /(_ _ _ erefl erefl) [k Hk].
      exists (1+k). by rewrite steps_plus /= E. }
    move: (E) => /inverse_simulation_back_step /[apply] [[q'']] [t''] [-> H''].
    move=> /(_ _ _ erefl erefl) [[|k] Hk]; first done.
    exists k. move: Hk. by rewrite (steps_plus 1 k) /= H''.
  Qed.

End Construction.

Require Import Undecidability.Synthetic.Definitions.

Theorem reduction :
  SBTM_HALT SR.TSR.
Proof.
  exists ((fun '(existT _ M (q, t)) => (srs M, encode_config M q t, [1; 0]))).
  move=> [M [q t]] /=. split.
  - by move=> [?] /simulation.
  - by move=> /inverse_simulation.
Qed.