Require Import List Relation_Operators Operators_Properties Lia.
Import ListNotations.

Require Import Undecidability.StringRewriting.Reductions.CM2_HALT_to_SSTS01.SR2ab.
Require Import Undecidability.StringRewriting.SSTS.

Require Undecidability.StringRewriting.Reductions.CM2_HALT_to_SSTS01.SR2ab_facts.
Require Import Undecidability.StringRewriting.Util.List_facts.

Require Import ssreflect ssrbool ssrfun.

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

Local Arguments rt_trans {A R x y z}.

Definition enc_pair '(x, y) : := (x + y) * (x + y) + y.

Lemma enc_pair_inj {xy x'y'} : enc_pair xy = enc_pair xy = .
Proof.
  move: xy [x y] [x' y'] /=. rewrite pair_equal_spec.
  have : x + y x' + y' x + y = x' + y' by .
  case; by nia.
Qed.


Opaque enc_pair.

Module Argument.
Section Reduction.
Context (srs : Srs) (a0: Symbol) (b0: Symbol) (Ha0b0: ).

Import SR2ab_facts (sym_eq_dec).

Definition enc (x: Symbol) : :=
  if sym_eq_dec x then 0
  else if sym_eq_dec x then 1
  else 2 + (
    match x with
    | (n, None) enc_pair (n, 0)
    | (n, Some m) enc_pair (n, 1 + m)
    end).

Definition ssts : Ssts := map ( '((a, b), (c, d)) ((enc a, enc b), (enc c, enc d))) srs.

Lemma sim_step {s t} : SR2ab.step srs s t SSTS.step ssts (map enc s) (map enc t).
Proof.
  case > ?. rewrite ?map_app /=.
  apply: step_intro. rewrite /ssts in_map_iff.
  eexists. by constructor; last by eassumption.
Qed.


Lemma enc_inj {a b} : enc a = enc b a = b.
Proof.
  rewrite /enc. move: (sym_eq_dec a ) (sym_eq_dec a ) (sym_eq_dec b ) (sym_eq_dec b ).
  move [] ? [] ? [] ? [] ? /=; try congruence.
  move: (a) (b) [? [?|]] [? [?|]] /= [] /enc_pair_inj []; by congruence.
Qed.


Lemma map_enc_inj {s t} : map enc s = map enc t s = t.
Proof.
  elim: s t; first by case.
  move ? ? IH [|? ?]; first done.
  by move /= [/enc_inj /IH ].
Qed.


Lemma inv_sim_step {s t'} : SSTS.step ssts (map enc s) t' t, t' = map enc t SR2ab.step srs s t.
Proof.
  move Hs': (map enc s) s' .
  case: Hs' u' v' a' b' c' d' H Hs.
  move: H. rewrite /ssts in_map_iff. move [[[a b]]] [c d] [[]] ? ? ? ?. subst.
  move H. exists (firstn (length u') s c :: d :: skipn (length u' + 2) s). constructor.
  - rewrite map_app /= map_firstn map_skipn Hs.
    rewrite firstn_app (ltac:() : length u' - length u' = 0) firstn_all.
    rewrite skipn_app (ltac:() : length u' + 2 - length u' = 2) skipn_all2 /=; first by .
    by rewrite app_nil_r.
  - move: H /SR2ab_facts.stepI. apply; last done.
    apply: map_enc_inj. rewrite Hs map_app /= map_firstn map_skipn Hs.
    rewrite firstn_app (ltac:() : length u' - length u' = 0) firstn_all.
    rewrite skipn_app (ltac:() : length u' + 2 - length u' = 2) skipn_all2 /=; first by .
    by rewrite app_nil_r.
Qed.


Lemma repeat_a0 {n} : repeat 0 n = map enc (repeat n).
Proof.
  elim: n; first done.
  move n /= . congr cons.
  rewrite /enc. by case: (sym_eq_dec ).
Qed.


Lemma repeat_b0 {n} : repeat 1 n = map enc (repeat n).
Proof using Ha0b0.
  elim: n; first done.
  move n /= . congr cons.
  rewrite /enc. case: (sym_eq_dec ); [done | by case: (sym_eq_dec )].
Qed.


Lemma transport : SR2ab (srs, , ) ssts.
Proof using Ha0b0.
  move [n Hn]. exists n. move: Hn. rewrite repeat_a0 repeat_b0.
  move: (repeat _) (repeat _) s t. elim.
  - move > ?. apply: rt_step. by apply: sim_step.
  - move *. by apply rt_refl.
  - move *. apply: rt_trans; by eassumption.
Qed.


Lemma inverse_transport : ssts SR2ab (srs, , ).
Proof using Ha0b0.
  move [n Hn]. exists n. move: Hn. rewrite repeat_a0 repeat_b0.
  move: (repeat _) (repeat _) s t.
  move Hs': (map enc s) s'. move Ht': (map enc t) t' /(@clos_rt_rt1n_iff _ _) .
  elim: s t Hs' Ht'.
  - move ? s t *. subst. have : s = t by apply: map_enc_inj.
    by apply rt_refl.
  - move > _ *. subst. move: /inv_sim_step [t] [? ?]. subst.
    apply /rt_trans; [apply: rt_step; by eassumption | by apply: ].
Qed.


End Reduction.
End Argument.

Require Import Undecidability.Synthetic.Definitions.

Theorem reduction : SR2ab .
Proof.
  exists ( '(srs, a0, b0)
    if SR2ab_facts.sym_eq_dec then [((0, 0), (1, 1))] else Argument.ssts srs ).
  move [[srs ] ]. constructor.
  - case: (SR2ab_facts.sym_eq_dec ).
    + move *. exists 1. rewrite /=. apply: rt_step.
      apply: (step_intro _ (u := [])). by left.
    + move H /=. by apply: Argument.transport.
  - case: (SR2ab_facts.sym_eq_dec ).
    + move /= _. exists 0. by apply: rt_refl.
    + move /= H. by apply: Argument.inverse_transport.
Qed.