From Undecidability.TM Require Export Util.Prelim Util.TM_facts Code.Code.
From Undecidability.TM Require Export Lifting.Lifting.
From Undecidability.TM Require Export Combinators.Combinators.

(* * Value-Containing *)

(* ** Right tapes *)

(* Tape proposition that says that the pointer is on (but not off) the right-most symbol *)
Section isVoid.

  Definition isVoid (sig : Type) (t : tape sig) :=
     x rs, t = midtape rs x nil.

   Definition isVoid_size (sig : Type) (t : tape sig) (s : ) :=
     x rs, t = midtape rs x nil |rs| s.

   Lemma isVoid_size_isVoid (sig : Type) (t : tape sig) (s : ) :
     isVoid_size t s isVoid t.
   Proof. intros (x&rs&&_). hnf. eauto. Qed.

  Lemma isVoid_size_monotone (sig : Type) (t : tape sig) (s1 s2 : ) :
    isVoid_size t isVoid_size t .
  Proof. intros (x&rs&&Hr) Hs. exists x, rs. split. eauto. . Qed.


  Lemma mapTape_isVoid_size (sig tau : Type) (t : tape sig) (s : ) (f : sig ) :
    isVoid_size (mapTape f t) s isVoid_size t s.
  Proof.
    split.
    - intros (&&H&Hs). destruct t; cbn in *; inv H. rewrite map_length in Hs.
      apply map_eq_nil in as . hnf. eauto.
    - intros (&&&Hs). hnf. cbn. do 2 eexists; repeat split; eauto. now simpl_list.
  Qed.


  Lemma mapTape_isVoid (sig tau : Type) (t : tape sig) (f : sig ) :
    isVoid (mapTape f t) isVoid t.
  Proof.
    split.
    - intros (&&H). destruct t; cbn in *; inv H.
      apply map_eq_nil in as . hnf. eauto.
    - intros (&&). hnf. cbn. eauto.
  Qed.


  Lemma isVoid_right (sig : Type) (t : tape sig) :
    isVoid t right t = nil.
  Proof. now intros (x&rs&). Qed.

  Lemma isVoid_size_right (sig : Type) (t : tape sig) (s1 : ) :
    isVoid_size t right t = nil.
  Proof. eauto using isVoid_right, isVoid_size_isVoid. Qed.

  Lemma isVoid_size_left (sig : Type) (t : tape sig) (s1 : ) :
    isVoid_size t length (left t) .
  Proof. now intros (x&&&). Qed.

  Lemma isVoid_isVoid_size (sig : Type) (t : tape sig) :
    isVoid t isVoid_size t (| tape_local_l t|).
  Proof. intros (x&&). cbn. hnf. eauto. Qed.

  Lemma isVoid_size_sizeOfTape (sig : Type) (t : tape sig) (s : ) :
    isVoid_size t s
    sizeOfTape t 1 + s.
  Proof. intros [m (&&H)]. cbn. simpl_list; cbn. . Qed.

End isVoid.

Ltac isVoid_mono :=
  once lazymatch goal with
  | [ H : isVoid_size ?t ? |- isVoid_size ?t ? ]
    apply isVoid_size_monotone with (1 := H); eauto; simpl_comp; try
  | [ H : isVoid_size ?t ? |- isVoid ?t ]
    apply isVoid_size_isVoid with (1 := H)
  | [ H : isVoid ?t |- isVoid_size ?t ? ]
    eapply isVoid_size_monotone;
    [ apply (isVoid_isVoid_size H) | eauto; simpl_comp; try ]
  | [ H : isVoid ?t |- isVoid ?t ]
    apply H
  end.
#[export] Hint Extern 10 isVoid_mono : core.

(* We add these three symbols the alphabets of every machine. START is the first symbol of the encoding and STOP is always the right-most symbol. UNKNOWN is always ignored (it serves as the default symbol for the alphabet-lift, see ChangeAlphabet). *)
Inductive boundary : Type :=
| START : boundary
| STOP : boundary
| UNKNOWN : boundary.

(* Declare discreteness of boundary *)
Instance boundary_eq : eq_dec boundary.
Proof. unfold dec. decide equality. Defined. (* because definition *)

(* Declare finiteness of boundary *)
Instance boundary_fin : finTypeC (EqType boundary).
Proof. split with (enum := [START; STOP; UNKNOWN]). cbn. intros []; cbn; reflexivity. Defined. (* because definition *)

(* In this section, we define value-containment (≃). It is defined on tapes over arbitrary Types (even infinite types), not finType. *)
Section Fix_Sig.

  Variable (sig : Type).

  Notation "sig '^+'" := ((boundary + sig) % type) (at level 0) : type_scope.

  (* A tape t contains a value x, if t=midtape rs (inl START) (map inr (encode x) ++ [inl STOP]) for some rs : list (sig^+). This means, the pointer is on the start symbol, right to the pointer is the encoding of x, which is terminated by the stop symbol inl STOP. We write t x for tape t contains x. *)

  (* We also define a dual predicate for value-containment: reversed value containment. It is, however, only used internally. The difference is, that the pointer is on the stop symbol, instead of the start symbol. This predicate is useful for intermediate state of a machine, for example in the machine CopyValue, which first has to move the head to the stop symbol. We write t x for t reversedly contains x. *)

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

    Definition tape_contains (t: tape sig^+) (x : X) :=
       r1, t = midtape (inl START) (map inr (Encode_map _ _ x) [inl STOP]).

    Definition tape_contains_size (t: tape sig^+) (x : X) (s : ) :=
       r1, t = midtape (inl START) (map inr (Encode_map _ _ x) [inl STOP])
            length s.

    Definition tape_contains_rev (t: tape sig^+) (x : X) :=
       r1, t = midtape (map inr (rev (Encode_map _ _ x)) inl START :: ) (inl STOP) nil.

    Definition tape_contains_rev_size (t: tape sig^+) (x : X) (s : ) :=
       r1, t = midtape (map inr (rev (Encode_map _ _ x)) inl START :: ) (inl STOP) nil
            length s.

    Lemma tape_contains_rev_isVoid t x :
      tape_contains_rev t x
      isVoid t.
    Proof. intros (&). repeat econstructor. Qed.

    Lemma tape_contains_rev_size_isVoid t x s :
      tape_contains_rev_size t x s
      isVoid_size t (S (size x + s)).
    Proof.
      intros (&&Hs). hnf.
      do 2 eexists. repeat split. simpl_list. cbn. unfold size. simpl_list. .
    Qed.


    Lemma tape_contains_size_contains t x s :
      tape_contains_size t x s tape_contains t x.
    Proof. intros (&&). hnf; eauto. Qed.

    Lemma tape_contains_rev_size_contains t x s :
      tape_contains_rev_size t x s tape_contains_rev t x.
    Proof. intros (&&). hnf; eauto. Qed.

    Lemma tape_contains_contains_size t x :
      tape_contains t x tape_contains_size t x (length (left t)).
    Proof. intros (&). cbn. hnf. eexists. split. reflexivity. reflexivity. Qed.

    Lemma tape_contains_rev_contains_rev_size t x :
      tape_contains_rev t x tape_contains_rev_size t x (length (left t) - S (size x)).
    Proof.
      intros (&). cbn. hnf. eexists. split. reflexivity.
      apply Nat.eq_le_incl. simpl_list; cbn. unfold size. .
    Qed.


    Lemma tape_contains_size_sizeOfTape (t : tape (sig^+)) x s :
      tape_contains_size t x s
      sizeOfTape t 2 + s + size x.
    Proof. intros (rs&&H). cbn. simpl_list; cbn. simpl_list; cbn. unfold size. . Qed.

    Lemma tape_contains_rev_size_sizeOfTape (t : tape (sig^+)) x s :
      tape_contains_rev_size t x s
      sizeOfTape t 2 + s + size x.
    Proof. intros (rs&&H). cbn. simpl_list; cbn. simpl_list; cbn. unfold size. . Qed.

    Lemma sizeOfTape_tape_contains_size (t : tape (sig^+)) (x:X) s :
      tape_contains_size t x s
      size x sizeOfTape t.
    Proof. intros (rs&&H). cbn. simpl_list; cbn. simpl_list; cbn. unfold size. . Qed.

  End Tape_Contains.

  Arguments tape_contains {sigX X cX} (I t x) : simpl never.
  Arguments tape_contains_rev {sigX X cX} (I t x) : simpl never.
  Arguments tape_contains_size {sigX X cX} (I t x s) : simpl never.
  Arguments tape_contains_rev_size {sigX X cX} (I t x s) : simpl never.

  Notation "t ≃( I ) x" := (tape_contains I t x) (at level 70, no associativity).
  Notation "t ≃ x" := (t ≃(_) x) (at level 70, no associativity, only parsing).
  Notation "t ≃( I ';' s ) x" := (tape_contains_size I t x s) (at level 70, no associativity).
  Notation "t ≃( ';' s ) x" := (t ≃(_;s) x) (at level 70, only parsing).
  Notation "t ≂( I ) x" := (tape_contains_rev I t x) (at level 70, no associativity).
  Notation "t ≂ x" := (t ≃(_) x) (at level 70, no associativity, only parsing).
  Notation "t ≂( I ';' s ) x" := (tape_contains_rev_size I t x s) (at level 70, no associativity).
  Notation "t ≃( ';' s ) x" := (t ≃(_;s) x) (at level 70, no associativity, only parsing).

  Section Encodes_Ext.
    Variable (X Y sigX sigY : Type) (cX : codable sigX X) (cY : codable sigY Y) ( : Retract sigX sig) ( : Retract sigY sig).

    Lemma tape_contains_ext (t : tape (sig^+)) (x : X) (y : Y) :
      t ≃() x
      Encode_map _ x = Encode_map _ _ y
      t ≃() y.
    Proof. cbn. intros (&). repeat econstructor. cbn. do 2 f_equal. now rewrite H. Qed.

    Implicit Type x : X.
    Implicit Type y : Y.

    Lemma tape_contains_size_ext (t : tape (sig^+)) x y s1 s2 :
      t ≃(;) x
      Encode_map _ x = Encode_map _ _ y
      
      t ≃(;) y.
    Proof. cbn. intros (&&Hs) H. repeat econstructor. cbn. do 2 f_equal. now rewrite H. . Qed.

    Lemma tape_contains_rev_ext (t : tape (sig^+)) (x : X) (y : Y) :
      t ≃() x
      Encode_map _ x = Encode_map _ _ y
      t ≃() y.
    Proof. cbn. intros (&) H. repeat econstructor. cbn. do 2 f_equal. now rewrite H. Qed.

    Lemma tape_contains_rev_size_ext (t : tape (sig^+)) x y s1 s2 :
      t ≂(;) x
      Encode_map _ x = Encode_map _ _ y
      
      t ≂(;) y.
    Proof. cbn. intros (&&Hs) H. repeat econstructor. cbn. do 2 f_equal. now rewrite H. . Qed.

  End Encodes_Ext.

  (* Define tapes that contain a value or are right. *)
  Section InitTape.
    Variable (sigX X : Type) (cX : codable sigX X) (I : Retract sigX sig).

    Definition initValue (x : X) :=
      midtape nil (inl START) (map inr (Encode_map _ I x) [inl STOP]).

    Lemma initValue_contains_size (x : X) :
      initValue x ≃(;0) x.
    Proof. unfold initValue. repeat econstructor. Qed.

    Lemma initValue_contains (x : X) :
      initValue x x.
    Proof. repeat econstructor. Qed.

    Definition initRight : tape sig^+ := midtape nil (inl STOP) nil.

    Lemma initRight_isVoid_size : isVoid_size (initRight) 0.
    Proof. repeat econstructor. Qed.

    Lemma initRight_isVoid : isVoid initRight.
    Proof. repeat econstructor. Qed.

  End InitTape.

End Fix_Sig.

Arguments tape_contains {sig sigX X cX} (I t x) : simpl never.
Arguments tape_contains_rev {sig sigX X cX} (I t x) : simpl never.
Arguments tape_contains_size {sig sigX X cX} (I t x s) : simpl never.
Arguments tape_contains_rev_size {sig sigX X cX} (I t x s) : simpl never.

Notation "t ≃( cX ) x" := (tape_contains cX t x) (at level 70, no associativity, format "t ≃( cX ) x").
Notation "t ≃ x" := (t ≃(_) x) (at level 70, no associativity, only parsing).
Notation "t ≃( cX ';' s ) x" := (tape_contains_size cX t x s) (at level 70, no associativity, format "t ≃( cX ';' s ) x").
Notation "t ≃( ';' s ) x" := (t ≃(_;s) x) (at level 70, only parsing).

(*  is only used "internally"! *)
Notation "t ≂( cX ) x" := (tape_contains_rev cX t x) (at level 70, no associativity, format "t ≂( cX ) x").
Notation "t ≂ x" := (t ≂(_) x) (at level 70, no associativity, only parsing).
Notation "t ≂( cX ';' s ) x" := (tape_contains_rev_size cX t x s) (at level 70, no associativity, format "t ≂( cX ';' s ) x").
Notation "t ≂( ';' s ) x" := (t ≂(_;s) x) (at level 70, no associativity, only parsing).

(* The tactic contains_ext applys tape_contains_ext *)

Ltac contains_solve_le :=
  try now (cbn; solve []).

Local Ltac eUnify := ((is_evar || is_evar );unify ).

Ltac contains_ext :=
  once lazymatch goal with
  | [H : ?t ≃(?;?) ?x |- ?t ≃(?;?) ?y]
    apply tape_contains_size_ext with (1 := H); try eUnify ;simpl_comp; try reflexivity; try contains_solve_le
  | [H : ?t ≃(_;?) ?x |- ?t ≃(_) ?y]
    eapply tape_contains_size_contains; contains_ext
  | [H : ?t ≃(_) ?x |- ?t ≃(_;?) ?y]
    eapply tape_contains_contains_size; contains_ext
  | [H : ?t ≃(?) ?x |- ?t ≃(?) ?y]
    apply tape_contains_ext with (1 := H); try eUnify ; simpl_comp; try reflexivity
  (*  is only used "internally"! *)
  | [H : ?t ≂(_;?) ?x |- ?t ≂(_;?) ?y]
    apply tape_contains_rev_size_ext with (1 := H); simpl_comp; try reflexivity; contains_solve_le
  | [H : ?t ≂(_;?) ?x |- ?t ≂(_) ?y]
    eapply tape_contains_rev_size_contains; contains_ext
  | [H : ?t ≂(_) ?x |- ?t ≂(_;?) ?y]
    eapply tape_contains_rev_contains_rev_size; contains_ext
  | [H : ?t ≂(_) ?x |- ?t ≂(_) ?y]
    apply tape_contains_rev_ext with (1 := H); simpl_comp; try reflexivity
  end.

#[export] Hint Extern 10 contains_ext : core.

(* Because every machine is defined on an alphabet Σ^+, the notation adds the discreteness and finiteness constructors, to cast Σ^+ : finType. *)
Notation "sig '^+'" := (FinType (EqType (boundary + sig) % type)) (at level 0) : type_scope.

(* Size functions are functions of type Vector.t () n. They compute the memory usage (i.e. size of the left rest) on a tape, given its initial size value *)

(* Compose two size functions *)
Definition compSizeFun (n : ) (f1 f2 : Vector.t ( ) n) : Vector.t ( ) n :=
  tabulate ( i [@i] >> [@i]).

Notation "f >>> g" := (compSizeFun f g) (at level 40).

(* Get the size function of a tape *)
Notation "s '@>' i" := (s[@i]) (at level 10, only parsing).

(* You can write size_function values... @> , which is equivialent to (size_function values...)[@] , i.e. the evaluation of the size function size_function values... on tape 1. *)

(* Tapes-lift for size functions *)

Definition injectSizeFun {m n : } (f : Vector.t () m) (I : Vector.t (Fin.t n) m) : Vector.t () n :=
  LiftTapes.fill I (Vector.const id _) f.

Notation "f '@>>' I" := (injectSizeFun f I) (at level 41).

(* You can write  @>> >>> @>>> , which is equivialent to ( @>> ) >>> ( @>> ), i.e. we lift  with  and compose this with the lifting of . *)
(* FIXME: this doesn't work yet. Use parenthesis *)