Require Import List.
Import ListNotations.

Require Import Undecidability.StringRewriting.PCSnf.
Require Import Undecidability.StringRewriting.SR.
Require Import Undecidability.StringRewriting.Util.Definitions.
Require Import Undecidability.Shared.ListAutomation.

Set Default Proof Using "Type".

Lemma derv_trans X R x y z :
    @derv X R x y derv R y z derv R x z.
Proof.
  induction 1.
  - eauto.
  - intros. econstructor; eauto.
Qed.


Section fix_R.

    Variable R : SRS .

    Variables : list .
    Definition Σ := sym R.

    Notation "#" := (fresh Σ).

    Definition R' := R map ( a ([a], [a])) (# :: Σ).

    Lemma copy u v :
      incl u Σ
      derv R' (u v) (v u).
    Proof.
      intros Hu. induction u in v, Hu |- *; cbn.
      - simpl_list. constructor.
      - replace (a :: u v) with ([a] (u v)) by reflexivity.
        replace (v rev u [a]) with ((v rev u) [a]) by now simpl_list.
        econstructor. constructor. eapply in_app_iff. right. eapply in_map_iff. eauto.
        simpl_list.
        replace (v a :: u) with ((v [a]) u) by now simpl_list.
        eapply IHu. eauto.
    Qed.


    Lemma correct1 x y :
      incl x Σ incl y Σ
      SR (R, x, y) PCSnf (R', x [#], y [#]).
    Proof.
      intros Hx Hy. cbn. induction 1 as [ | x' y'].
      - constructor.
      - inversion H as [ x y u v ]; subst x' y'. simpl_list.
        eapply derv_trans. 1: eapply copy; eauto.
        simpl_list. econstructor. { econstructor. eapply in_app_iff. eauto. }
        simpl_list. eapply derv_trans. 1: eapply copy; eauto.
        simpl_list. econstructor. { econstructor. eapply in_app_iff. eauto. }
        eapply IHrewt; [ | eauto]. eapply incl_app; [ eauto | ]. eapply incl_app; [ | eauto ].
        unfold Σ. eauto.
    Qed.


    Lemma use_fresh {Σ} x : incl x Σ In (fresh Σ) x.
    Proof.
      now intros H ? % H % fresh_spec.
    Qed.


    Hint Unfold Σ : core.

    Lemma correct2 x1 x2 y1 y2 :
      incl Σ incl Σ incl Σ incl Σ
      PCSnf (R', [#] , [#] ) SR (R, , ).
    Proof.
      intros .
      remember ( [#] ) as x.
      remember ( [#] ) as y.
      induction 1 in , , , , Heqx, Heqy, , , , ; subst.
      - eapply list_prefix_inv in Heqy as [ ]. econstructor. unfold Σ in *.
        + now intros ? % % fresh_spec.
        + now intros ? % % fresh_spec.
      - inversion H as [x u v [ | (a & Ha & ) % in_map_iff] % in_app_iff ]; subst y.
        + assert (In # x) as Hx. {
            assert (In # ( # :: )) as Hf by eauto.
            rewrite in Hf. eapply in_app_iff in Hf as [ | ]; [ | now eauto].
            eapply sym_word_l in ; eauto. exfalso.
            eapply fresh_spec with (l := Σ). 2:reflexivity. unfold Σ at 2. eauto.
          }
          eapply in_split in Hx as (x1_ & x2_ & ).
          replace (u x1_ # :: x2_) with ((u x1_) [#] x2_) in by now simpl_list.
          eapply list_prefix_inv in as ( & ).
          * econstructor. econstructor. eauto.
            replace ( v x1_) with (( v) x1_) by now simpl_list.
            eapply IHderv.
            -- eapply incl_app. eauto. unfold Σ. eauto.
            -- etransitivity; eauto.
            -- eauto.
            -- eauto.
            -- now simpl_list.
            -- now simpl_list.
          * eapply use_fresh. eapply incl_app. unfold Σ. eauto. eapply list_prefix_inv'' in as [ ].
            2: eapply use_fresh; eauto. 2: eapply use_fresh; eauto.
            etransitivity; eauto.
          * eapply use_fresh. eauto.
        + inversion Ha; subst; clear Ha. destruct as [ | ].
          * eapply list_prefix_inv with (x := []) in as [ ]; [ | firstorder | now eapply use_fresh].
            simpl_list. cbn in H.
            eapply (IHderv []); [ eauto .. | now simpl_list ].
          * assert (In # x) as Hx. {
              assert (In # ( # :: )) as Hf by eauto.
              rewrite in Hf. eapply in_app_iff in Hf as [ | ]; [ | now eauto].
              exfalso. destruct as [ | []]. eapply fresh_spec with (l := Σ); eauto.
            }
            eapply in_split in Hx as (x1_ & x2_ & ).
            replace ([a] x1_ # :: x2_) with (([a] x1_) [#] x2_) in by now simpl_list.
            eapply list_prefix_inv in as ( & ).
            -- replace ( [a] x1_) with (( [a]) x1_) by now simpl_list.
               eapply IHderv.
               ++ eapply incl_app. eauto. eauto.
               ++ etransitivity; eauto.
               ++ eauto.
               ++ eauto.
               ++ now simpl_list.
               ++ now simpl_list.
            -- eapply use_fresh. eapply incl_app. eauto. eapply list_prefix_inv'' in as [ ].
               2: eapply use_fresh; eauto. 2: eapply use_fresh; eauto.
               etransitivity; eauto.
            -- eapply use_fresh. eauto.
    Qed.


End fix_R.

Require Import Undecidability.Synthetic.Definitions.

Lemma reduction :
  SR PCSnf.
Proof.
  exists ( '(R,x,y) (R' R x y, x fresh (Σ R x y), y fresh (Σ R x y))).
  intros [[R x] y]. split.
  - eapply correct1; unfold Σ; eauto.
  - intros. eapply correct2 with ( := x) ( := y) ( := []) ( := []).
    1-4: unfold Σ; eauto. now simpl_list.
Qed.