Require Import List.
Import ListNotations.

Require Import Undecidability.PCP.PCP.
Require Import Undecidability.PCP.Util.Facts.
Require Import Undecidability.PCP.Util.PCP_facts.
Require Import Undecidability.StringRewriting.SR.
Require Import Undecidability.StringRewriting.Util.Definitions.

Require Import Undecidability.Synthetic.Definitions.

Import RuleNotation.

Set Default Proof Using "Type".

Section SR_to_MPCP.
  Local Definition string := string .
  Variable R : stack .
  Variables : string.

  Notation "x ≻ y" := (rew R x y) (at level 70).
  Notation "x ≻* y" := (rewt R x y) (at level 70).

  Notation Sigma := ( sym R).

  Definition dollar := fresh Sigma.
  Notation "$" := dollar.
  Definition hash := fresh (dollar :: Sigma).
  Notation "#" := hash.

  Lemma sep_doll : $ #.
  Proof.
    intros H. symmetry in H. now eapply fresh_spec in H.
  Qed.


  Lemma doll_sig a : a Sigma $ a.
  Proof.
    now eapply fresh_spec.
  Qed.


  Lemma hash_sig a : a Sigma # a.
  Proof.
    intros; eapply fresh_spec. eauto.
  Qed.


  Definition d := [$] / ($ :: [#]).
  Definition e := ( [#;$]) / [$].
  Definition P := [d ; e] R [ ([#] / [#] )] map ( a [a] / [a]) Sigma.

  Lemma P_inv c :
    c P
    c = d c = e c R c = ( [#] / [#] ) a, a Sigma c = [a] / [a].
  Proof.
    cbn. intros. firstorder. eapply in_app_iff in H. firstorder.
    eapply in_map_iff in H. firstorder.
  Qed.


  Lemma cards_subset x1 : Sigma cards P.
  Proof.
    induction .
    - firstorder.
    - intros. intros [] [].
      + inv . unfold P. eauto 15.
      + eapply ; firstorder.
  Qed.


  Lemma rew_subset u v : u / v R u / v P.
  Proof.
    unfold P. intros. eauto.
  Qed.


  Fact SR_MPCP x :
    x Sigma x ≻* A, A P A = x [#] A.
  Proof.
    intros. revert H. pattern x; refine (rewt_induct _ _ ).
    - exists [e]. cbn. firstorder. now simpl_list.
    - clear x . intros. inv H. destruct as (A & ? & ?).
      pose (app_incl_l ). pose (app_incl_R (app_incl_R )).
      repeat eapply incl_app; try assumption. eauto.
      exists (cards [(u / v)] cards [ [#] / [#] ] A). split.
      + repeat (eapply incl_app); try assumption; unfold P.
        * eapply cards_subset. eapply app_incl_l. eassumption.
        * eapply incl_appr. eapply incl_appl. now apply ListAutomation.incl_sing.
        * eapply cards_subset. eapply app_incl_R. eapply app_incl_R. eassumption.
        * eauto.
      + simpl_list. cbn. rewrite . now simpl_list.
  Qed.


  Fact MPCP_SR x y A :
    A P x Sigma y Sigma
     A = x [#] y A
    y x ≻* .
  Proof.
    intros ?. revert x y. induction A; intros.
    - exfalso. destruct x; inv .
    - assert (a P) as [ | [ | [ | [ | (? & ? & )]]]] % P_inv by (now apply H).
      + exfalso. cbn -[fresh] in . destruct x. inv . destruct (sep_doll ).
        inversion . edestruct doll_sig; eauto.
      + cbn -[fresh] in *.
        autorewrite with list in *. cbn -[fresh] in *.
        eapply list_prefix_inv in as [ ?].
        destruct y.
        * reflexivity.
        * exfalso. inversion . edestruct doll_sig; eauto.
        * intros E. edestruct hash_sig; eauto.
        * intros E. edestruct hash_sig; eauto.
      + destruct a as (u, v).
        cbn -[fresh] in .
        edestruct (split_inv ) as (x' & ).
        intros E. edestruct hash_sig. unfold . rewrite !in_app_iff. do 2 right. now eapply sym_word_l; eauto. reflexivity.
        econstructor. eapply rewB. now eassumption.
        enough ( (y v) x' ≻* ) by now autorewrite with list in *.
        eapply IHA; autorewrite with list in *.
        * eauto.
        * eapply app_incl_R. eassumption.
        * eapply incl_app; eauto.
        * now eapply app_inv_head in .
      + cbn -[fresh] in . destruct x; [inv |].
        * simpl_list. change y with ([] y). eapply IHA; eauto using cons_incl.
        * inversion . edestruct hash_sig; eauto.
      + cbn -[fresh] in *. rename into a. destruct x.
        * inversion . edestruct hash_sig; eauto.
        * inv .
          replace (y n :: x) with ((y [n]) x) by now simpl_list.
          eapply IHA.
          ** eapply cons_incl. eassumption.
          ** eapply cons_incl. eassumption.
          ** eapply incl_app. eassumption. now eapply ListAutomation.incl_sing.
          ** simpl_list. eassumption.
  Qed.


  Corollary SR_MPCP_cor :
     ≻* A, A P (d :: A) = (d :: A).
  Proof.
    split.
    - intros ?. cbn in H. eapply SR_MPCP in H as (A & ? & ?).
      exists A. firstorder. cbn. rewrite . cbn. now simpl_list.
      eapply incl_appl. apply incl_refl.
    - intros (A & ? & ?). eapply MPCP_SR with (y := []) (A := A); try trivial.
      eapply incl_appl. now apply incl_refl.
      inv . rewrite . now simpl_list.
  Qed.


End SR_to_MPCP.

Theorem reduction :
  SR MPCP.
Proof.
  exists ( '(R, x, y) (d R x y, P R x y)). intros [[R x] y].
  unfold SR. rewrite SR_MPCP_cor. unfold MPCP. unfold d.
  firstorder trivial.
Qed.