From Undecidability Require Export TM.Util.TM_facts.
Require Import Undecidability.Shared.Libs.PSL.FiniteTypes.DepPairs EqdepFacts.

Section StateWhile.

  Variable n : .
  Variable sig : finType.

  Variable : finType.
  Variable pM : pTM sig ( + ) n.

  Definition StateWhile_states : Type := { l : & state ( (pM l)) }.

  Definition liftState {l : } (q : state ( (pM l))) : StateWhile_states := ltac:(eexists; apply q).
  Definition lift {l : } (c : mconfig sig (state ( (pM l))) n) : mconfig sig (FinType(EqType StateWhile_states)) n :=
    {|
      cstate := liftState (cstate c);
      ctapes := ctapes c;
    |}.

  Definition StateWhile_trans : StateWhile_states * Vector.t (option sig) n StateWhile_states * Vector.t (option sig * move) n :=
     '(q, s)
      if halt ( q)
      then match (pM ( q)) ( q) with
           | inl (liftState (start ( (pM ))), nop_action)
           | inr _ (q, nop_action)
           end
      else let (q', a) := trans ( q, s) in (liftState q', a).

  Definition StateWhile_halt : StateWhile_states bool :=
     q halt ( q) &&
               match (pM ( q)) ( q) with
               | inl _ false
               | inr true
               end.

  Definition StateWhileTM (l1 : ) : TM sig n :=
    {|
      start := liftState (start ( (pM )));
      trans := StateWhile_trans;
      halt := StateWhile_halt;
    |}.


  Hypothesis (defF : inhabitedC ).
  Definition StateWhile_part : StateWhile_states :=
     q match (pM ( q)) ( q) with
          | inl _ default
          | inr
          end.

  Definition StateWhile (l1 : ) : pTM sig n :=
    (StateWhileTM ; StateWhile_part).

  Local Arguments loopM {_ _} _ _ _.
  Local Arguments halt {_ _} _ _.
  Local Arguments step {_ _} _ _.


  Lemma step_comp (l : ) (c : mconfig sig (state ( (pM l))) n) :
    haltConf c = false
    step (StateWhileTM l) (lift c) = lift (step ( (pM l)) c).
  Proof.
    intros HHalt. unfold haltConf in HHalt. unfold lift.
    destruct c as [q t]. cbn in *.
    cbv [step]. cbn. rewrite HHalt.
    destruct (trans (q, current_chars t)) as [q' a]. cbn. reflexivity.
  Qed.


  Lemma halt_comp (l : ) (c : mconfig sig (state ( (pM l))) n) :
    haltConf (M := (pM l)) c = false
    haltConf (M := StateWhileTM l) (lift c) = false.
  Proof.
    intros HHalt. cbn in *.
    destruct c as [q t]. cbn in *.
    apply andb_false_iff. cbn. now left.
  Qed.


  Lemma halt_comp' (l : ) (c : mconfig sig (state ( (pM l))) n) :
    haltConf (M := StateWhileTM l) (lift c) = haltConf (M := (pM l)) c.
  Proof.
    cbn in *. destruct c as [q t]. cbn in *. unfold StateWhile_halt, haltConf. cbn.
  Abort.


  Lemma StateWhile_trans_repeat (l l_ : ) (c : mconfig sig (state ( (pM l))) n) (l' : ) :
    haltConf (M := (pM l)) c = true
     (pM l) (cstate c) = inl l'
    step (StateWhileTM l_) (lift c) = lift (initc ( (pM l')) (ctapes c)).
  Proof.
    intros HHalt HRepeat. unfold haltConf in HHalt.
    destruct c as [q t]; cbn in *.
    unfold step. cbn -[doAct_multi] in *. rewrite HHalt, HRepeat. unfold initc, lift. cbn. f_equal. apply doAct_nop.
  Qed.


  Lemma startState_irrelevant k l l' c1 c2 :
    loopM (StateWhileTM l ) k = Some
    loopM (StateWhileTM l') k = Some .
  Proof. auto. Qed.

  Lemma startState_irrelevant' k l l' c1 :
    loopM (StateWhileTM l) k = loopM (StateWhileTM l') k.
  Proof. reflexivity. Qed.


  Definition lifth l : mconfig sig (state (StateWhileTM l)) n bool.
  Proof.
    intros ((l'&q)&t).
    decide (l=l') as [_ | ].
    - eapply (halt _ q).
    - apply true.
  Defined.


  Lemma lifth_comp l (c : mconfig sig (state (StateWhileTM l)) n) :
    lifth c = false haltConf c = false.
  Proof. destruct c as ((l'&q)&t). cbn. decide (l=l') as [| _]; intros H; auto. unfold StateWhile_halt. cbn. now rewrite H. Qed.


  Lemma lifth_comp' l (c : mconfig sig (state ( (pM l))) n) :
    @lifth l (lift c) = haltConf c.
  Proof. unfold haltConf. destruct c as (q,t). cbn. decide (l=l); tauto. Qed.


  Lemma StateWhile_split_repeat k l l1 c2 c3 :
    loop (step (StateWhileTM l)) (haltConf (M:=StateWhileTM l)) (lift ) k = Some
    haltConf = true
     (pM l) (cstate ) = inl
     k',
      loop (step (StateWhileTM l)) (haltConf (M:=StateWhileTM l)) (lift (initc ( (pM )) (ctapes ))) k' = Some
      k = S k'.
  Proof.
    intros HLoop H E. unfold haltConf in H.
    destruct k as [ |k']; cbn in *; unfold StateWhile_halt in *; cbn in *.
    - rewrite H, E in HLoop. cbn in *. congruence.
    - rewrite H, E in HLoop. cbn in *. exists k'. repeat split; auto.
      rewrite HLoop. f_equal. symmetry. apply StateWhile_trans_repeat; auto.
  Qed.


  Lemma StateWhile_split_break k l l2 c2 c3 :
    loop (step (StateWhileTM l)) (haltConf (M:=StateWhileTM l)) (lift ) k = Some
    haltConf = true
     (pM l) (cstate ) = inr
     = lift .
  Proof. intros HLoop H E. eapply loop_eq_0 in HLoop; auto. unfold haltConf in *. cbn in *. unfold StateWhile_halt in *. cbn in *. now rewrite H, E. Qed.

  Lemma StateWhile_split k l (c1 : mconfig sig (state ( (pM l))) n) (c3 : mconfig sig (FinType (EqType StateWhile_states)) n) :
    loopM (StateWhileTM l) (lift ) k = Some
     (c2 : mconfig sig (state ( (pM l))) n),
      match (pM l) (cstate ) with
      | inl
         (k1 k2 : ),
        loopM ( (pM l)) = Some
        loopM (StateWhileTM l) (lift (initc ( (pM )) (ctapes ))) = Some
        1 + + k
      | inr loopM ( (pM l)) k = Some = lift
      end.
  Proof.
    intros HLoop. unfold loopM in *.
    apply loop_split with (h := @lifth l) in HLoop as (&&&&&Hk).
    - apply loop_unlift with (f := step ( (pM l))) (h := haltConf (M := (pM l))) in as (&&).
      + exists . destruct ( (pM l) (cstate )) as [|] eqn:E.
        * exists . eapply StateWhile_split_repeat in as (&&). exists . repeat split. all: eauto.
          -- .
          -- now apply loop_fulfills in .
        * split. eapply loop_monotone. apply . . eapply StateWhile_split_break; eauto. now apply loop_fulfills in .
      + apply lifth_comp'.
      + apply step_comp.
    - apply lifth_comp.
  Qed.


  Lemma StateWhile_merge_repeat k1 k2 l l1 (c1 : mconfig sig (state ( (pM l))) n) (c2 : mconfig sig (state ( (pM l))) n) c3 :
    loopM ( (pM l)) = Some
    haltConf = true
     (pM l) (cstate ) = inl
    loopM (StateWhileTM l) (lift (initc ( (pM )) (ctapes ))) = Some
    loopM (StateWhileTM l) (lift ) ( + (1 + )) = Some .
  Proof.
    intros HHalt HL . unfold loopM in *.
    apply loop_merge with (f := step (StateWhileTM l)) (h := @lifth l) ( := lift ).
    - apply lifth_comp.
    - eapply loop_lift. 3: apply . 2: eauto.
      + apply lifth_comp'.
      + apply step_comp.
    - cbn [plus].
      rewrite loop_step.
      + now rewrite StateWhile_trans_repeat with (l' := ).
      + cbn; unfold StateWhile_halt; cbn. rewrite HL. apply andb_false_r.
  Qed.



  Lemma StateWhile_merge_break k l l2 (c1 : mconfig sig (state ( (pM l))) n) (c2 : mconfig sig (state ( (pM l))) n) :
    loopM ( (pM l)) k = Some
    haltConf = true
     (pM l) (cstate ) = inr
    loopM (StateWhileTM l) (lift ) k = Some (lift ).
  Proof.
    intros HLoop HHalt HL. unfold loopM in *.
    replace k with (k + 0) by .
    apply loop_merge with (f := step (StateWhileTM l)) (h := @lifth l) ( := lift ).
    - apply lifth_comp.
    - eapply loop_lift with (lift := lift) (f' := step (StateWhileTM l)) (h' := @lifth l) in HLoop; auto.
      + apply lifth_comp'.
      + apply step_comp.
    - cbn. unfold StateWhile_halt; cbn. unfold haltConf in *. rewrite HHalt, HL. cbn. reflexivity.
  Qed.



  Variable R : pRel sig ( + ) n.

  Inductive StateWhile_Rel : pRel sig n :=
  | StateWhile_Rel_loop :
       l t l1 t' l' t'',
        R l t (inl , t')
        StateWhile_Rel t' (l', t'')
        StateWhile_Rel l t (l', t'')
  | StateWhile_Rel_break :
       l t l2 t',
        R l t (inr , t')
        StateWhile_Rel l t (, t').

  Lemma lift_init l tin :
    initc (StateWhileTM l) tin = lift (initc ( (pM l)) tin).
  Proof. reflexivity. Qed.

  Lemma StateWhile_Realise l :
    ( l, pM l R l)
    StateWhile l StateWhile_Rel l.
  Proof.
    intros HRel. hnf in HRel; hnf. intros t k; revert t l. apply complete_induction with (x := k); clear k; intros k IH. intros tin l HLoop.
    unfold loopM in HLoop. cbn in *. rewrite lift_init in HLoop. apply StateWhile_split in HLoop as (&HLoop).
    destruct ( (pM l) (cstate )) as [|] eqn:E.
    - destruct HLoop as (&&&&Hk).
      apply HRel in . rewrite E in . rewrite lift_init in .
      eapply startState_irrelevant in . specialize IH with (2 := ). spec_assert IH by .
      econstructor 1; eauto.
    - destruct HLoop as (HLoop&).
      apply HRel in HLoop. rewrite E in *.
      constructor 2; auto. cbn. unfold StateWhile_part. cbn. now rewrite E.
  Qed.



  Section StateWhile_TerminatesIn.
    Variable (T T' : tRel sig n).

    Lemma StateWhile_TerminatesIn_ind l :
      ( l, pM l R l)
      ( l, (pM l) T' l)
      ( l (tin : tapes sig n) (k : ),
          T l tin k
           k1,
            T' l tin
            ( tmid l2, R l tin (inr , tmid) k)
            ( tmid l1, R l tin (inl , tmid) k2, T tmid 1 + + k))
      StateWhileTM l T l.
    Proof.
      intros Realise_M Term_M Hyp tin k. revert tin l. apply complete_induction with (x:=k); clear k; intros k IH tin l.
      intros (&&&) % Hyp.
      pose proof (Term_M _ _ _ ) as (oconf&Hloop).
      specialize (Realise_M _ _ _ _ Hloop).
      destruct ( (pM l) (cstate oconf)) as [ | ] eqn:.
      - specialize with (1 := Realise_M) as (&&Hi).
        specialize (IH ltac:() _ _ ) as (&).
        exists . apply loop_monotone with ( := + (1 + )). 2: .
        cbn -[plus]. rewrite lift_init.
        refine (StateWhile_merge_repeat Hloop _ _ ); auto.
        unfold loopM in *; now apply loop_fulfills in Hloop.
      - specialize with (1 := Realise_M).
        exists (lift oconf). eapply loop_monotone; eauto.
        refine (StateWhile_merge_break ( := ) Hloop _ _); auto.
        unfold loopM in *; now apply loop_fulfills in Hloop.
    Qed.


  End StateWhile_TerminatesIn.

  Section StateWhile_TerminatesIn_coind.
    Variable (T : tRel sig n).

    CoInductive StateWhile_T : tRel sig n :=
    | StateWhile_T_intro l t k k1 :
        T l t
        ( t' l1,
            R l t (inl , t')
             k2, StateWhile_T t' 1 + + k)
        ( tmid l2,
            R l t (inr , tmid) k)
        StateWhile_T l t k.

    Lemma StateWhile_TerminatesIn l :
      ( l, pM l R l)
      ( l, (pM l) T l)
      StateWhileTM l StateWhile_T l.
    Proof.
      intros HRel HTerm. eapply StateWhile_TerminatesIn_ind; eauto.
      intros l' tin k' HCoInd. destruct HCoInd; eauto.
    Qed.


  End StateWhile_TerminatesIn_coind.

  Lemma StartState_irrelevant (y y' : ) (k : ) (iconf : mconfig sig (FinType(EqType StateWhile_states)) n) :
    loopM ( (StateWhile y')) iconf k = loopM ( (StateWhile y )) iconf k.
  Proof. auto. Qed.

End StateWhile.

Arguments StateWhile : simpl never.
Arguments StateWhile {n sig } pM {defF} .


Section StateWhileInduction.
  Variable (sig : finType) (n : ) ( : finType).

  Variable : pRel sig (+) n.
  Variable : pRel sig n.

  Lemma StateWhileInduction :
    ( tin l yout tout (HLastStep: l tin (inr yout, tout)), l tin (yout, tout))
    ( tin l l' tmid tout yout
       (HStar : l tin (inl l', tmid)) (HLastStep : l' tmid (yout, tout)), l tin (yout, tout))
    ( l, StateWhile_Rel l <<=2 ( l)).
  Proof. intros . intros tin tout. induction 1; eauto. Qed.

End StateWhileInduction.

Section WhileCoInduction.
  Variable (sig : finType) (n : ) ( : finType).

  Variable R : pRel sig ( + ) n.
  Variable T T' : tRel sig n.

  Lemma StateWhileCoInduction :
    ( l (tin : tapes sig n) (k : ) (HT : T l tin k),
         k1,
          T' l tin
           (ymid : + ) tmid,
            R l tin (ymid, tmid)
            match ymid with
            | inl k2, T tmid 1 + + k
            | inr _ k
            end)
    ( l, T l <<=2 StateWhile_T R T' l).
  Proof.
    intros. revert l. cofix IH. intros l tin k HT. specialize H with (1 := HT) as (&&). econstructor; eauto.
    - intros tmid ymid HR. specialize ( (inl ymid) tmid HR) as (&&Hk); cbn in *.
      exists . split. 2: . now apply IH.
    - intros tmid HR. now specialize ( (inr ) tmid HR).
  Qed.


End WhileCoInduction.