From Undecidability.TM Require Import Util.Prelim Code.CodeTM.
From Undecidability.TM Require Import Lifting.LiftAlphabet.



Local Hint Mode Retract - - : typeclass_instances.

Generalizable All Variables.

Section SurjectInject.
  Variable (sig : Type).
  Variable def : sig.
  Variable retr : Retract sig .

  Definition injectSymbols : list sig list := map Retr_f.
  Definition surjectSymbols : list list sig := map (surject Retr_g def).

  Lemma surject_inject (str : list sig) (str' : list ) :
    injectSymbols str = str'
    str = surjectSymbols str'.
  Proof.
    intros . induction str as [ | s str IH ]; cbn.
    - reflexivity.
    - unfold surject. retract_adjoint. f_equal. auto.
  Qed.


  Lemma inject_surject (str : list ) (str' : list sig) :
    ( t, t str s, Retr_g t = Some s)
    surjectSymbols str = str'
    str = injectSymbols str'.
  Proof.
    intros H . unfold injectSymbols, surjectSymbols. rewrite map_map. erewrite map_ext_in. symmetry. eapply map_id.
    intros t Ht. specialize (H _ Ht) as (s&Hs).
    erewrite retract_g_inv; eauto.
    unfold surject. rewrite Hs. reflexivity.
  Qed.


  Lemma surject_cons (str : list ) (str2 : list sig) (s : sig) :
    surjectSymbols str = s ::
     (t : ) (str' : list ),
      str = t :: str'
      surject Retr_g def t = s
      surjectSymbols str' = .
  Proof.
    destruct str as [ | t str ]; cbn in *; intros; inv H; eauto.
  Qed.


  Lemma surject_app (str : list ) (str2 str3 : list sig) :
    surjectSymbols str =
     (str' str'' : list ),
      str = str' str''
      surjectSymbols str' =
      surjectSymbols str'' = .
  Proof.
    revert str . induction as [ | s IH]; intros str H; cbn in *.
    - exists nil, str. cbn. auto.
    - pose proof surject_cons H as (t&str'&&&). cbn in *. specialize (IH _ _ ) as (Str&Str'&&&).
      inv H. repeat eexists. instantiate (1 := t :: Str). reflexivity. cbn. reflexivity.
  Qed.


End SurjectInject.

Corollary map_length_eq : (A B C : Type) (f : A C) (g : B C) (l1 : list A) (l2 : list B), map f = map g || = ||.
Proof.
  intros. erewrite map_length. symmetry. erewrite map_length. symmetry. rewrite H. reflexivity.
Qed.


Section MapCode.
  Variable sig : Type.
  Variable retr : Retract sig .

  Variable (sigX X : Type) (cX : codable sigX X) (I_x : Retract sigX sig) (I : Retract sig ).

  Global Instance Retract_plus : Retract (boundary + sig) (boundary + ) := Retract_sum _ _.
  Notation "'f''" := (@Retr_f (boundary+sig) (boundary+) Retract_plus).
  Notation "'g''" := (@Retr_g (boundary+sig) (boundary+) Retract_plus).

  Local Arguments Retr_f {X Y} (Retract).
  Local Arguments Retr_g {X Y} (Retract).

  Local Instance I_x' : Retract sigX := ComposeRetract _ _.

  Notation injectTape := (mapTape f').
  Notation surjectTape := (surjectTape g' (inl UNKNOWN)).


  Lemma surjectTape_injectTape t :
    surjectTape (injectTape t) = t.
  Proof.
    unfold surjectTape. unfold surject. simpl_tape.
    erewrite mapTape_ext. apply mapTape_id. intros a. retract_adjoint. reflexivity.
  Qed.


  Lemma contains_size_translate_sig (s : ) (x : X) (t : tape (boundary+sig)) :
    t ≃(;s) x (injectTape t) ≃(;s) x.
  Proof.
    split; intros (&HCode&Hs); subst; cbn in *; hnf.
    - repeat eexists. cbn. f_equal. rewrite map_app, !List.map_map. cbn. reflexivity.
      now simpl_list.
    - unfold injectTape in HCode.
      exists (surjectSymbols (inl UNKNOWN) _ ).
      apply mapTape_inv_midtape in HCode as (ls'&m'&rs'&&&&).
      rewrite map_map in .
      destruct m'; cbn in *; inv .
      split. f_equal.
      + unfold surjectSymbols. rewrite map_map. rewrite map_id at 1. eapply List.map_ext.
        intros [ | ]; cbn. reflexivity. unfold surject. cbn. retract_adjoint. reflexivity.
      + symmetry. eapply map_injective with (f := retract_sum_f id (Retr_f _)); eauto.
        { intros. eapply retract_f_injective; eauto. }
        now rewrite map_app, !map_map.
      + unfold surjectSymbols. rewrite map_length in Hs. simpl_list. .
  Qed.


  Corollary contains_translate_sig (x : X) (t : tape (boundary+sig)) :
    t x (injectTape t) x.
  Proof.
    split; intros.
    - eapply tape_contains_size_contains. apply contains_size_translate_sig.
      contains_ext.
    - eapply tape_contains_size_contains. apply contains_size_translate_sig. contains_ext.
  Qed.


  Lemma contains_size_translate_tau1 (x : X) (s : ) (t : tape (boundary+)) :
    t ≃(;s) x surjectTape t ≃(;s) x.
  Proof.
    intros (ls&HCode&Hs). cbn in *. subst. cbn. rewrite !map_map.
    repeat econstructor.
    - f_equal. cbn. rewrite map_app, !map_map. f_equal.
      eapply List.map_ext. intros. unfold surject. cbn. unfold retr_comp_f. cbn. retract_adjoint. reflexivity.
    - now simpl_list.
  Qed.


  Corollary contains_translate_tau1 (x : X) (t : tape (boundary+)) :
    t x surjectTape t x.
  Proof.
    intros. eapply tape_contains_size_contains. apply contains_size_translate_tau1. contains_ext.
  Qed.


  Lemma surject_inject_inr (x : boundary) (str : list (boundary+)) (code : list sig) :
    surjectSymbols (inl x) Retract_plus str = map inr code
     str' : list , str = map inr str' map (Retr_g I) str' = map Some code.
  Proof.
    revert x code. induction str as [ | s str' IH]; intros; cbn in *.
    - apply map_eq_nil' in H as . exists nil. cbn. tauto.
    - destruct code as [ | c code']; cbn in *; inv H.
      destruct s; cbn in *; inv .
      specialize (IH _ _ ) as (str''&&IH). rewrite IH.
      exists (t :: str''). cbn. split. auto. f_equal.
      unfold surject, retract_sum_g in . destruct (Retr_g I t) eqn:E; inv ; auto.
  Qed.


  Lemma in_encode_retract (x : X) :
     t' : , t' Encode_map cX _ x s' : sig, Retr_g I t' = Some s'.
  Proof. cbn. intros t' (?&&?) % in_map_iff. unfold retr_comp_f. cbn. retract_adjoint. eauto. Qed.

  Lemma contains_size_translate_tau2 (x : X) (s : ) (t : tape (boundary+)) :
    surjectTape t ≃(;s) x
    t ≃(;s) x.
  Proof.
    intros (&HCode&Hs). cbn in *.
    eapply mapTape_inv_midtape in HCode as (ls'&m'&rs'&&&&).
    repeat econstructor; cbn in *. f_equal.
    - unfold surject in . destruct m'; cbn in *. cbv [id] in *. now inv .
      destruct (Retr_g I t); inv .
    - symmetry in .
      change (surjectSymbols (inl UNKNOWN) Retract_plus rs' = map inr (Encode_map cX _ x) [inl STOP]) in .
      eapply surject_app in as (&&&&).
      eapply inject_surject in as ; eauto.
      eapply inject_surject in as ; eauto.
      + f_equal. unfold injectSymbols. cbn. rewrite !map_map. eapply List.map_ext. intros. cbn. reflexivity.
      + unfold surjectSymbols in . eapply map_eq_cons in as (t & ? & & ? & % map_eq_nil').
        unfold surject in H. destruct t; cbn in *; swap 1 2. destruct (Retr_g I t); inv H. inv H.
        intros [ | ]; intros [ | ]; try congruence; auto. inv H. eexists. cbn. reflexivity.
      + intros [ | ]; intros He; cbn; eauto.
        destruct (Retr_g I t) eqn:; cbn; eauto. exfalso.
        pose proof surject_inject_inr as (&&).
        apply in_map_iff in He as (?&HETmp&HE); inv HETmp.
        enough (t (Encode_map cX _ x)) as .
        {
          pose proof in_encode_retract as (?&?). congruence.
        }
        cbn in *.
        assert (None map (Retr_g I) ) as .
        {
          rewrite . eapply in_map_iff; eauto.
        }
        rewrite in . apply in_map_iff in as (?&?&?). congruence.
    - now rewrite map_length in Hs.
  Qed.


  Corollary contains_translate_tau2 (x : X) (t : tape (boundary+)) :
    surjectTape t x
    t x.
  Proof.
    intros. eapply tape_contains_size_contains. apply contains_size_translate_tau2. contains_ext.
  Qed.


  Corollary contains_size_translate_tau (x : X) (s : ) (t : tape (boundary+)) :
    surjectTape t ≃(;s) x t ≃(;s) x.
  Proof. split; auto using contains_size_translate_tau1, contains_size_translate_tau2. Qed.

  Corollary contains_translate_tau (x : X) (t : tape (boundary+)) :
    surjectTape t x t x.
  Proof. split; auto using contains_translate_tau1, contains_translate_tau2. Qed.

  Corollary contains_size_translate_eq (t1 t2 : tape (boundary+)) (s : ) (x : X) :
    surjectTape = surjectTape
     ≃(;s) x ≃(;s) x.
  Proof.
    intros HEq HEnc.
    eapply contains_size_translate_tau2; auto.
    rewrite HEq. now eapply contains_size_translate_tau1 in HEnc.
  Qed.


  Corollary contains_translate_eq (t1 t2 : tape (boundary+)) (x : X) :
    surjectTape = surjectTape
     x x.
  Proof.
    intros. eapply tape_contains_size_contains. eapply contains_size_translate_eq; eauto.
  Qed.


  Lemma surjectTape_isVoid_size (t : tape (boundary+)) (s : ) :
    isVoid_size t s isVoid_size (surjectTape t) s.
  Proof. unfold surjectTape. apply mapTape_isVoid_size. Qed.

  Lemma surjectTape_isVoid'_size (t : tape (boundary+)) (s : ) :
    isVoid_size (surjectTape t) s isVoid_size t s.
  Proof. unfold surjectTape. apply mapTape_isVoid_size. Qed.

  Lemma surjectTape_isVoid (t : tape (boundary+)) :
    isVoid t isVoid (surjectTape t).
  Proof. unfold surjectTape. apply mapTape_isVoid. Qed.

  Lemma surjectTape_isVoid' (t : tape (boundary+)) :
    isVoid (surjectTape t) isVoid t.
  Proof. unfold surjectTape. apply mapTape_isVoid. Qed.

End MapCode.

#[export] Hint Unfold surjectTape surjectTapes injectTape : tape.

Arguments Retract_plus : simpl never.
Arguments injectTape : simpl never.
Arguments surjectTape : simpl never.


Section ChangeAlphabet.
  Variable (sig : finType).
  Variable (n : ) (F : finType).
  Variable pM : pTM sig^+ F n.
  Variable (retr : Retract sig ).

  Definition ChangeAlphabet : pTM ^+ F n :=
    LiftAlphabet pM (Retract_plus retr) (inl UNKNOWN).

End ChangeAlphabet.

Notation "pM ⇑ Rmove" := (ChangeAlphabet pM Rmove) (at level 40, only parsing).

Ltac simpl_surject_step :=
  once lazymatch goal with
  
  | [ |- surjectTape _ _ ?t _ ] apply contains_translate_tau1
  | [ H : surjectTape _ _ ?t _ |- _ ] apply contains_translate_tau2 in H
  | [ |- surjectTape _ _ ?t ≃(;?n) _ ] apply contains_size_translate_tau1
  | [ H : surjectTape _ _ ?t ≃(;?n) _ |- _ ] apply contains_size_translate_tau2 in H
  
  | [ |- isVoid (surjectTape _ _ ?t) ] apply surjectTape_isVoid
  | [ H : isVoid (surjectTape _ _ ?t) |- _ ] apply surjectTape_isVoid' in H
  | [ |- isVoid_size (surjectTape _ _ ?t) ?n ] apply surjectTape_isVoid_size
  | [ H : isVoid_size (surjectTape _ _ ?t) ?n |- _ ] apply surjectTape_isVoid'_size in H
  end.

Ltac simpl_surject := repeat simpl_surject_step.


Ltac smpl_TM_ChangeAlphabet :=
  once lazymatch goal with
  | [ |- ChangeAlphabet ?pM ?retr _ ] apply LiftAlphabet_Realise
  | [ |- ChangeAlphabet ?pM ?retr c(_) _ ] apply LiftAlphabet_RealiseIn
  | [ |- (ChangeAlphabet ?pM ?retr) _ ] apply LiftAlphabet_TerminatesIn
  end.

Smpl Add smpl_TM_ChangeAlphabet : TM_Correct.