From Undecidability Require Import TM.SBTM.
Require Import PeanoNat Lia List ssreflect ssrbool ssrfun.
Import ListNotations SBTMNotations.

Lemma iter_plus {X} (f : X X) (x : X) n m : Nat.iter (n + m) f x = Nat.iter m f (Nat.iter n f x).
Proof.
  elim: m; first by rewrite Nat.add_0_r.
  move m /= . by have : n + S m = S n + m by .
Qed.


Lemma oiter_None {X : Type} (f : X option X) k : Nat.iter k (obind f) None = None.
Proof. elim: k; [done | by move /= ? ]. Qed.

Lemma steps_plus {M} k1 k2 {x} :
  steps M ( + ) x = obind ( y steps M y) (steps M x).
Proof.
  rewrite /steps iter_plus /=.
  move: (Nat.iter _ (Some x)) [y|] /=; first done.
  apply: oiter_None.
Qed.


Lemma steps_None_mono {M x} k2 k1 : steps M x = None steps M x = None.
Proof.
  elim: x.
  { move [|]; [done|]. }
  move IH [|] x + ?; first done.
  rewrite (steps_plus 1 ) (steps_plus 1 ).
  move: (steps M 1 x) [y|]; last done.
  move /IH. apply. .
Qed.


Lemma steps_sync {M k1 x k2 y} :
  steps M (S ) x = None steps M (S ) x = Some y steps M y = None.
Proof.
  elim: x.
  { move x. rewrite (steps_plus 1 ). by move . }
  move IH x.
  rewrite (steps_plus 1 (S )) (steps_plus 1 ).
  move: (steps M 1 x) [z|]; last done.
  move: [|].
  { by move /= []. }
  move /IH H /H /(steps_None_mono (S )). apply. .
Qed.


Inductive almost_eq : list bool list bool Prop :=
  | almost_eq_cons a l1 l2 : almost_eq almost_eq (a :: ) (a :: )
  | almost_eq_false n1 n2 : almost_eq (repeat false ) (repeat false ).

Inductive almost_eq_tape : tape tape Prop :=
  | almost_eq_tape_intro a ls1 rs1 ls2 rs2 :
      almost_eq almost_eq
      almost_eq_tape (, a, ) (, a, ).

Lemma almost_eq_tape_step_Some M q q1 q2 t1 t'1 t2 t'2 :
  almost_eq_tape
  step M (q, ) = Some (, )
  step M (q, ) = Some (, )
   = almost_eq_tape .
Proof.
  have ? := almost_eq_false 0 0.
  have ? := almost_eq_false 0 _.
  have ? := almost_eq_false _ 0.
  case a ???? [] [????| ] [].
  - move ????. rewrite /step /=. case: (trans' M _); last done.
    move [[? ?] []] [] [] /=; by do ? constructor.
  - move . rewrite /step /=. case: (trans' M _); last done.
    move [[? ?] []] [] [] /=; first by do ? constructor.
    move: [|?] [|?] /=; by do ? constructor.
  - move ????. rewrite /step /=. case: (trans' M _); last done.
    move [[? ?] []] [] [] /=; last by do ? constructor.
    move: [|?] [|?] /=; by do ? constructor.
  - move . rewrite /step /=. case: (trans' M _); last done.
    move [[? ?] []] [] [] /=.
    + move: [|?] [|?] /=; by do ? constructor.
    + move: [|?] [|?] /=; by do ? constructor.
Qed.


Lemma almost_eq_tape_step_None M q t1 t2 :
  almost_eq_tape
  step M (q, ) = None
  step M (q, ) = None.
Proof.
  case a ???? [] [????| ] [] >.
  all: rewrite /step /=.
  all: case: (trans' M _); last done.
  all: by move [[? ?] ?].
Qed.


Lemma almost_eq_refl l : almost_eq l l.
Proof.
  elim: l >.
  - by apply: (almost_eq_false 0 0).
  - by apply: almost_eq_cons.
Qed.


Lemma almost_eq_sym l1 l2 : almost_eq almost_eq .
Proof. elim *; by constructor. Qed.

Lemma almost_eq_tape_sym t1 t2 : almost_eq_tape almost_eq_tape .
Proof. move [] > /almost_eq_sym ? /almost_eq_sym ?. by constructor. Qed.

Lemma almost_eq_tape_steps_None {M k q t1 t2} :
  almost_eq_tape
  (steps M k (q, ) = None steps M k (q, ) = None).
Proof.
  elim: k q ; first done.
  move k IH q . rewrite !(steps_plus 1 k) /=.
  case : (step M (q, )) [[ ]|];
    case : (step M (q, )) [[ ]|].
  - move: /almost_eq_tape_step_Some /[apply] /[apply].
    move [ /IH]. by apply.
  - move /almost_eq_tape_sym Ht2t1.
    by move: Ht2t1 /almost_eq_tape_step_None /[apply] .
  - move Ht1t2.
    by move: Ht1t2 /almost_eq_tape_step_None /[apply] .
  - done.
Qed.


Fixpoint seq_Vector (n : ) : Vector.t (Fin.t n) n :=
  match n return Vector.t (Fin.t n) n with
  | 0 Vector.nil (Fin.t 0)
  | S n' Vector.cons (Fin.t (S n')) (Fin.F1) n' (Vector.map (Fin.R 1) (seq_Vector n'))
  end.

Lemma seq_Vector_spec {n} (q : Fin.t n) :
  VectorDef.nth (seq_Vector n) q = q.
Proof.
  elim: q; first done.
  move {}n q IH /=.
  by rewrite (Vector.nth_map _ _ q q erefl) IH.
Qed.


Definition construct_trans {n : }
  (f : (Fin.t n) * bool option ((Fin.t n) * bool * direction)) :
  Vector.t (
    (option ((Fin.t n) * bool * direction)) *
    (option ((Fin.t n) * bool * direction))) n :=
  Vector.map ( q (f (q, true), f (q, false))) (seq_Vector n).

Lemma construct_trans_spec {n : }
  (f : (Fin.t n) * bool option ((Fin.t n) * bool * direction)) :
   x, trans' (Build_SBTM n (construct_trans f)) x = f x.
Proof.
  move /= [q a]. rewrite /trans' /construct_trans /=.
  rewrite (Vector.nth_map _ _ q q erefl) seq_Vector_spec.
  by case: a.
Qed.