From Undecidability.TM Require Import ProgrammingTools.
From Undecidability Require Import BinNumbers.EncodeBinNumbers.
From Undecidability Require Import Code.CaseSum.
From Undecidability Require Import ArithPrelim.
From Coq Require Import BinNat.

Local Open Scope N_scope.


Definition WriteNumber (n : N) : pTM sigN^+ unit 1 := WriteValue n.

Definition WriteNumber_Rel (n : N) : pRel sigN^+ unit 1:=
   tin '(_, tout)
    isVoid tin[@]
    tout[@] n.

Definition WriteNumber_steps (n : N) : := 2 * Encode_N_size n + 3.

Lemma WriteNumber_Sem (n : N) : WriteNumber n c(WriteNumber_steps n) WriteNumber_Rel n.
Proof.
  eapply RealiseIn_monotone.
  { unfold WriteNumber. TM_Correct. }
  { setoid_rewrite Encode_N_hasSize. cbn. ring_simplify. reflexivity. }
  {
    intros tin ([], tout) H. hnf in H. intros Hright. modpon H. auto.
  }
Qed.



Definition Constr_N0 : pTM sigN^+ unit 1 := WriteNumber 0.

Definition Constr_N0_Rel : pRel sigN^+ unit 1:=
   tin '(_, tout)
    isVoid tin[@]
    tout[@] 0.

Definition Constr_N0_steps : := Eval cbn in WriteNumber_steps 0.

Lemma Constr_N0_Sem : Constr_N0 c(Constr_N0_steps) Constr_N0_Rel.
Proof.
  eapply RealiseIn_monotone.
  { unfold Constr_N0. apply WriteNumber_Sem. }
  { reflexivity. }
  { intros tin ([], tout) H. hnf in *. eauto. }
Qed.


Definition Constr_Npos : pTM sigN^+ unit 1 := Constr_Some _.

Definition Constr_Npos_Rel : pRel sigN^+ unit 1 :=
   tin '(_, tout)
     (x : positive),
      tin[@] x
      tout[@] Npos x.

Definition Constr_Npos_Sem : Constr_Npos c(Constr_Some_steps) Constr_Npos_Rel.
Proof.
  eapply RealiseIn_monotone.
  { unfold Constr_Npos. TM_Correct. }
  { reflexivity. }
  { intros tin ([], tout) H. cbn in *. intros x Hx. modpon H. auto. }
Qed.



Definition CaseN : pTM sigN^+ bool 1 := If (CaseOption _) (Return Nop true) (Return (WriteNumber 0) false).

Definition CaseN_Rel : pRel sigN^+ bool 1 :=
   tin '(yout, tout)
     (x : N),
      tin[@] x
      match yout, x with
      | true, Npos p tout[@] p
      | false, N0 tout[@] 0
      | _, _ False
      end.

Definition CaseN_steps : := 13.

Lemma CaseN_Sem : CaseN c(CaseN_steps) CaseN_Rel.
Proof.
  eapply RealiseIn_monotone.
  { unfold CaseN. TM_Correct.
    - apply WriteNumber_Sem. }
  { reflexivity. }
  {
    intros tin (yout, tout) H. intros x Hx. destruct H as [H|H]; TMSimp.
    - modpon H. destruct x as [ | p]; cbn in *; auto.
    - modpon H. destruct x as [ | p]; cbn in *; auto.
  }
Qed.



From Undecidability Require Import PosIncrementTM.

Definition Increment_N : pTM sigN^+ unit 1 := If (CaseOption _) (Increment _;; Constr_Some _) (WriteNumber 1).

Definition Increment_N_Rel : pRel sigN^+ unit 1 :=
   tin '(_, tout)
     (n : BinNums.N),
      tin[@] n
      tout[@] N.succ n.

Lemma Increment_N_Realise : Increment_N Increment_N_Rel.
Proof.
  eapply Realise_monotone.
  { unfold Increment_N. TM_Correct.
    - apply Increment_Realise.
    - eapply RealiseIn_Realise. apply WriteNumber_Sem. }
  {
    intros tin ([], tout) H. intros n Hn. destruct H as [H|H]; TMSimp.
    - modpon H. destruct n; cbn in *; auto.
      modpon ;[].
      modpon ;[]. eauto.
    - modpon H. destruct n; cbn in *; auto.
  }
Qed.



From Undecidability Require Import PosAddTM.

Definition : pTM sigN^+ unit 2 :=
  If (CaseOption _ @[||])
     (If (CaseOption _ @[||])
         (Add' _;; Constr_Some _@[||];; Constr_Some _@[||])
         (Constr_Some _@[||];; CopyValue _))
     (Constr_None _@[||]).

Definition : pRel sigN^+ unit 2 :=
   tin '(_, tout)
     (x y : N),
      tin[@] x
      tin[@] y
      x y
      tout[@] x
      tout[@] x + y.

Lemma Add'_N_Realise : .
Proof.
  eapply Realise_monotone.
  { unfold . TM_Correct.
    - apply Add'_Realise.
    }
  {
    intros tin ([], tout) H. intros x y Hx Hy Hle. destruct H as [H|H]; TMSimp.
    - modpon H. destruct x as [ | x]; cbn in *; auto.
      destruct ; TMSimp.
      + modpon . destruct y as [ | y]; cbn in *; auto.
        specialize ( x y).
        modpon ;[].
        simpl_tape in *; simpl_surject.
        modpon . modpon .
        split; eauto.
      + modpon . destruct y as [ | y]; cbn in *; auto.
        modpon .
        specialize ( (N.pos x)). modpon .
        split; auto.
    - modpon H. destruct x as [ | x]; cbn in *; auto. modpon . auto.
  }
Qed.


Definition Add_N : pTM sigN^+ unit 3 :=
  If (CaseOption _ @[||])
     (If (CaseOption _ @[||])
         (Add _;; Constr_Some _@[||];; Constr_Some _@[||];; Constr_Some _@[||])
         (Constr_Some _@[||];; Constr_None _@[||];; CopyValue _ @[|; |]))
     (Constr_None _@[||];; CopyValue _@[|; |]).

Definition Add_N_Rel : pRel sigN^+ unit 3 :=
   tin '(_, tout)
     (x y : N),
      tin[@] x
      tin[@] y
      isVoid tin[@]
      tout[@] x
      tout[@] y
      tout[@] x+y.

Lemma Add_N_Realise : Add_N Add_N_Rel.
Proof.
  eapply Realise_monotone.
  { unfold Add_N. TM_Correct.
    - apply Add_Realise. }
  {
    intros tin ([], tout) H. intros x y Hx Hy Hright. destruct H as [H|H]; TMSimp.
    - modpon H. destruct x as [ | x]; cbn in *; auto.
      destruct ; TMSimp.
      +
        rename into HCaseOption, into HAdd, into , into , into .
        modpon HCaseOption. destruct y as [ | y]; cbn in *; auto.
        specialize (HAdd x y). modpon HAdd; simpl_tape in *; simpl_surject; TMSimp; auto.
        modpon . modpon . modpon .
        repeat split; eauto.
      + modpon . destruct y as [ | y]; cbn in *; auto.
        modpon .
        modpon . modpon .
        TMSimp.
        repeat split; auto.
    - modpon H. destruct x as [ | x]; cbn in *; auto. modpon . modpon . auto.
  }
Qed.



From Undecidability Require Import PosMultTM.


Definition Mult_N : pTM sigN^+ unit 3 :=
  If (CaseN @[||])
     (If (CaseN @[||])
         (Mult _;; Constr_Npos @[||];; Constr_Npos @[||];; Constr_Npos @[||])
         (Constr_Npos @[||];; Constr_N0 @[||]))
     (Constr_N0 @[||]).

Definition Mult_N_Rel : pRel sigN^+ unit 3 :=
   tin '(yout, tout)
     (x y : N),
      tin[@] x
      tin[@] y
      isVoid tin[@]
      tout[@] x
      tout[@] y
      tout[@] x*y.

Lemma Mult_N_Realise : Mult_N Mult_N_Rel.
Proof.
  eapply Realise_monotone.
  { unfold Mult_N. TM_Correct.
    - eapply RealiseIn_Realise. apply CaseN_Sem.
    - eapply RealiseIn_Realise. apply CaseN_Sem.
    - apply Mult_Realise.
    - eapply RealiseIn_Realise. apply Constr_Npos_Sem.
    - eapply RealiseIn_Realise. apply Constr_Npos_Sem.
    - eapply RealiseIn_Realise. apply Constr_Npos_Sem.
    - eapply RealiseIn_Realise. apply Constr_Npos_Sem.
    - eapply RealiseIn_Realise. apply Constr_N0_Sem.
    - eapply RealiseIn_Realise. apply Constr_N0_Sem. }
  {
    intros tin ([], tout) H. intros x y Hx Hy Hright. destruct H as [H|H]; TMSimp.
    - modpon H. destruct x as [ | x]; cbn in *; auto. destruct as [?|?]; TMSimp.
      + modpon . destruct y as [ | y]; cbn in *; auto. simpl_tape in *; simpl_surject. TMSimp.
        modpon . modpon . modpon . repeat split; eauto.
      + modpon . destruct y as [ | y]; cbn in *; auto.
    - modpon H. destruct x as [ | x]; cbn in *; auto.
  }
Qed.



Ltac smpl_TM_NTM :=
  match goal with
  
  | [ |- WriteNumber _ _ ] eapply RealiseIn_Realise; apply WriteNumber_Sem
  | [ |- WriteNumber _ c(_) _ ] apply WriteNumber_Sem
  | [ |- (WriteNumber _) _ ] eapply RealiseIn_TerminatesIn; apply WriteNumber_Sem
  
  | [ |- Constr_N0 _ ] eapply RealiseIn_Realise; apply Constr_N0_Sem
  | [ |- Constr_N0 c(_) _ ] apply Constr_N0_Sem
  | [ |- (Constr_N0) _ ] eapply RealiseIn_TerminatesIn; apply Constr_N0_Sem
  
  | [ |- Constr_Npos _ ] eapply RealiseIn_Realise; apply Constr_Npos_Sem
  | [ |- Constr_Npos c(_) _ ] apply Constr_Npos_Sem
  | [ |- (Constr_Npos) _ ] eapply RealiseIn_TerminatesIn; apply Constr_Npos_Sem
  
  | [ |- CaseN _ ] eapply RealiseIn_Realise; apply CaseN_Sem
  | [ |- CaseN c(_) _ ] apply CaseN_Sem
  | [ |- (CaseN) _ ] eapply RealiseIn_TerminatesIn; apply CaseN_Sem
  
  | [ |- Increment_N _ ] apply Increment_N_Realise
  
  | [ |- _ ] apply Add'_N_Realise
  
  | [ |- Add_N _ ] apply Add_N_Realise
  
  | [ |- Mult_N _ ] apply Mult_N_Realise
  end.

Smpl Add smpl_TM_NTM : TM_Correct.