HyPre.sstep.MultiStep


From Coq Require Export
  Program.Equality
  Relations.Relations.

From HyPre Require Export
  ListExtra
  NatExtra
  MapExtra
  Tac.

From HyPre Require Export
  ApplyLf.

Import ListNotations.

Fixpoint multi_step {St : Type} (step : St -> St) (s : St) (n : nat) : list St
  := match n with
     | 0 => [s]
     | S n => multi_step step (step s) n ++ [s]
     end.

Fixpoint multi_until {St : Type} (b : St -> bool) (step : St -> St) (s : St) (n : nat) : St
  := match n with
     | 0 => s
     | S n => if b s then s else multi_until b step (step s) n
     end.

Fixpoint multi_step_until_opt {St : Type} (b : St -> bool) (step : St -> St) (s : St) (n : nat) : option (list St)
  := match n with
     | 0 => None
     | S n => if b s then Some [s] else match multi_step_until_opt b step (step s) n with
                                       | Some x => Some (x ++ [s])
                                       | None => None
                                       end
     end.

Definition multi_step_hd {St : Type} (step : St -> St) (s : St) (n : nat) : St
  := hd s (multi_step step s n).

Fixpoint multi_eval {L : Type}
  (e : L -> list L -> nat -> list L) (b : L -> nat) acc (n : nat)
  : list L
  := match n with
     | 0 => acc
     | S n => match acc with
             | [] => []
             | s :: acc' => multi_eval e b (e s acc' (b s)) n
             end
     end.

Lemma multi_step_len {St : Type} (step : St -> St) (s : St) (n : nat)
  : length (multi_step step s n) = S n.
Proof.
  revert s.
  induction n;intros;cbn.
  - reflexivity.
  - rewrite app_length.
    rewrite IHn.
    cbn.
    lia.
Qed.

Lemma multi_step_app {St : Type} (step : St -> St) (s : St) (n m : nat)
  : multi_step step s (S n + m) = multi_step step (step (multi_step_hd step s m)) n ++ multi_step step s m.
Proof.
  revert s n.
  induction m;intros;cbn.
  - setoid_rewrite <-plus_n_O.
    reflexivity.
  - replace (n + S m) with (S n + m) by lia.
    rewrite IHm.
    unfold multi_step_hd.
    cbn.
    rewrite app_assoc.
    f_equal.
    f_equal.
    f_equal.
    f_equal.
    destruct (multi_step step (step s) m) eqn:E;cbn.
    + exfalso.
      eapply f_equal with (f:=@length _) in E.
      rewrite multi_step_len in E.
      cbn in E.
      congruence.
    + reflexivity.
Qed.

Lemma multi_step_hd_app {St : Type} (step : St -> St) (s s' s'' : St) (n : nat) x
  : hd s' (multi_step step s n ++ x) = hd s'' (multi_step step s n).
Proof.
  revert s s' s'' x.
  induction n;cbn;eauto;intros.
  setoid_rewrite IHn.
  rewrite <-app_assoc.
  setoid_rewrite IHn.
  reflexivity.
  Unshelve.
  exact s.
Qed.

Lemma multi_step_hd_step {St : Type} (step : St -> St) (s : St) (n : nat)
  : multi_step_hd step s (S n) = step (multi_step_hd step s n).
Proof.
  unfold multi_step_hd.
  cbn.
  setoid_rewrite multi_step_hd_app with (s'':=s).
  revert s.
  induction n;cbn;intros.
  - reflexivity.
  - setoid_rewrite multi_step_hd_app.
    rewrite <-IHn.
    reflexivity.
Qed.

(* TODO clean-up & instantiate this lemma with b = length of imp program *)
Lemma multi_step_apply_lf_progress {L L' Y: Type}
  (step : L -> L) (lf : L -> Y -> list L' * Y) (b : nat)
  (s : L) (i : Y) (n : nat)
  (Hb : forall s' i n, multi_step_hd step s n = s' -> 0 < length (apply_lf lf i (multi_step step s' b))) (* b depends on s *)
  : length (apply_lf lf i (multi_step step s (n * S b))) >= n.
Proof.
  revert i.
  induction n;intros.
  - cbn.
    lia.
  - replace (S n * S b) with (S b + n * S b) by lia.
    rewrite multi_step_app.
    rewrite apply_lf_app.
    rewrite app_length.
    specialize (IHn i).
    match goal with |- length (apply_lf _ ?y (_ _ ?s2 _)) + _ >= _
                    => specialize (Hb s2 y (S (n * S b)))
    end.
    cbn in Hb.
    exploit Hb.
    eapply multi_step_hd_step.
    lia.
Qed.

Lemma multi_step_S {X : Type} (step : X -> X) (x : X) n
  : multi_step step x (S n) = (multi_step step (step x) n) ++ [x].
Proof.
  cbn.
  reflexivity.
Qed.

Lemma multi_step_hd_S {X : Type} (step : X -> X) (x y y' : X) n
  : hd y (multi_step step (step x) n) = hd y' (multi_step step x (S n)).
Proof.
  revert x y y'.
  induction n;intros;cbn.
  - reflexivity.
  - setoid_rewrite multi_step_hd_app.
    rewrite <-multi_step_S.
    setoid_rewrite multi_step_hd_app.
    eapply IHn.
    Unshelve.
    all: exact x.
Qed.

Lemma multi_step_S2 {X : Type} (step : X -> X) (x : X) n
  : multi_step step x (S n) = step (multi_step_hd step x n) :: multi_step step x n.
Proof.
  revert x.
  induction n;intros.
  - cbn;reflexivity.
  - cbn.
    rewrite app_comm_cons.
    f_equal.
    rewrite <-multi_step_S.
    rewrite IHn.
    f_equal.
    f_equal.
    eapply multi_step_hd_S.
Qed.

Lemma multi_step_apply_lf_progress_step {L L' Y: Type}
  (step : L -> L) (lf : L -> Y -> list L' * Y)
  (s : L) (i : Y) (n : nat)
  (Hb : forall i s, 0 < length (apply_lf lf i ([step s])))
  : length (apply_lf lf i (multi_step step s n)) >= n.
Proof.
  revert i.
  induction n;intros.
  - cbn.
    lia.
  - setoid_rewrite multi_step_S2.
    rewrite cons_app.
    rewrite cons_app.
    rewrite apply_lf_app.
    rewrite app_length.
    cbn.
    specialize (IHn i).
    match goal with
      |- | _ _ ?i' ([step ?s']) | + _ >= _ => specialize (Hb i' s')
    end.
    lia.
Qed.

Fail Lemma multi_step_apply_lf_progress_dep {L L' Y: Type}
  (step : L -> L) (lf : L -> Y -> list L' * Y) (b : L -> nat)
  (s : L) (i : Y) (n : nat)
  (Hb : forall s, length (apply_lf lf i (multi_step step s n)) < length (apply_lf lf i (multi_step step s (b s))))
  : length (apply_lf lf i (multi_step step s (fold_right 0 n))) >= n.
(*TODO remove:
 I might want to revisit the multi_eval approach:
     If have a progress lemma with a budget dependent on the current state,
     I could just use stmt_len as budget. IS that REALLY easier?
     maybe just scrap all of it and write inductive loop semantics *)


Lemma multi_step_apply_lf_progress' {L L' Y: Type}
  (step : L -> L) (lf : L -> list L') (b : nat)
  (s : L) (n : nat)
  (Hb : forall s' n, multi_step_hd step s n = s' -> 0 < length (apply_lf' lf (multi_step step s' b))) (* b depends on s *)
  : length (apply_lf' lf (multi_step step s (n * S b))) >= n.
Proof.
  unfold apply_lf' in *.
  eapply multi_step_apply_lf_progress.
  intros;eauto.
  destruct i.
  eapply Hb;eauto.
Qed.

(* this ensures that we can construct arbitrarily long traces on the leakage.
     afterwards the idea is, that we can make sure it is not too long by using lastn *)

(* so stst_sem would use a combination of multi_eval_lenlastn & length
     similar to imp_trace *)

(* in our example b would be the distance to the next loop header
      ^-- NO, much simpler: just the length of the program
     (since we're allowed to overshoot, we may ignore other possible leaks in between *)

(* come to think of it, it is probably easier to write one evaluator that does a loop iteration
     in one step *)


Inductive multi {X : Type} (R : relation X) : relation X :=
| multi_refl x : multi R x x
| multi_trans : forall (x y z : X),
    R y z ->
    multi R x y ->
    multi R x z.

(* TODO would it be simpler to use multi_trace ? *)
Inductive multi_trace {X : Type} (R : relation X) : list X -> Prop :=
| multi_nil : multi_trace R []
| multiT_refl : forall (x : X), multi_trace R [x]
| multiT_step : forall (x y : X) (yt : list X),
    R y x
    -> multi_trace R (y :: yt)
    -> multi_trace R (x :: y :: yt).

Inductive multi_trace_from {X : Type} (R : relation X) (z : X) : list X -> Prop :=
| multifT_refl : multi_trace_from R z [z]
| multifT_step : forall (x y : X) (yt : list X),
    R y x
    -> multi_trace_from R z (y :: yt)
    -> multi_trace_from R z (x :: y :: yt).

Lemma multi_trace_multi_step {X : Type} (step : X -> X) (x : X) (xt : list X)
  : multi_trace_from (fun a b => step a = b) x xt -> multi_step step x (length xt - 1) = xt.
Proof.
  intros H.
  dependent induction H.
  - cbn.
    reflexivity.
  - rename IHmulti_trace_from into Q.
    cbn in *.
    rewrite Nat.sub_0_r in Q.
    rewrite <-multi_step_S.
    rewrite multi_step_S2.
    f_equal.
    2: eapply Q.
    rewrite <-H.
    unfold multi_step_hd.
    rewrite Q.
    cbn.
    reflexivity.
Qed.

Lemma multi_step_multi_trace {X : Type} (step : X -> X) (x : X) (xt : list X)
  : multi_step step x (length xt - 1) = xt -> multi_trace_from (fun a b => step a = b) x xt.
Proof.
  dependent induction xt;cbn;intros.
  - congruence.
  - rewrite PeanoNat.Nat.sub_0_r in H.
    destruct xt;cbn in H.
    + inversion H.
      econstructor.
    + cbn in IHxt.
      rewrite <-multi_step_S in H.
      rewrite multi_step_S2 in H.
      econstructor.
      * inversion H.
        unfold multi_step_hd.
        rewrite H2.
        cbn.
        reflexivity.
      * eapply IHxt.
        rewrite PeanoNat.Nat.sub_0_r.
        inversion H.
        reflexivity.
Qed.

Lemma multi_step_multi_trace' {X : Type} (step : X -> X) (x y : X) (xt : list X)
  : multi_step step x (length xt) = y :: xt -> multi_trace_from (fun a b => step a = b) x (y :: xt).
Proof.
  intros.
  eapply multi_step_multi_trace.
  cbn.
  replace (|xt| - 0) with (|xt|) by lia.
  assumption.
Qed.

From Coq Require Import
  Classes.RelationClasses
  Classes.Morphisms.

Global Instance multi_step_Proper {X : Type}
  : Proper ((fun st1 st2 => forall (x : X), st1 x = st2 x) ==> eq ==> eq ==> eq) multi_step.
Proof.
  unfold Proper, respectful.
  intros.
  subst y0 y1.
  revert x0.
  induction x1;intros;cbn.
  - reflexivity.
  - setoid_rewrite IHx1.
    rewrite H.
    reflexivity.
Qed.

From Coq Require Import Program.Basics.

Global Instance multi_trace_from_Proper {X : Type}
  : Proper ((fun (R1 R2 : relation X) => forall x y, R1 x y -> R2 x y) ==> eq ==> eq ==> impl) multi_trace_from.
Proof.
  unfold Proper, respectful.
  intros.
  subst y0 y1.
  unfold impl.
  intros.
  dependent induction H0.
  - econstructor.
  - econstructor.
    + eapply H;eauto.
    + eauto.
Qed.

Lemma multi_until_S {X : Type} (step : X -> X) b (x : X) n
  : b (multi_until b step x n) = true
    -> b (multi_until b step x (S n)) = true.
Proof.
  revert x.
  induction n;intros.
  - cbn in H.
    cbn.
    destruct (b x) eqn:E;[ | congruence].
    eauto.
  - cbn.
    cbn in H.
    destruct (b x) eqn:E;eauto.
Qed.

Lemma multi_until_le {X : Type} (step : X -> X) b (x : X) n m
  : n <= m -> b (multi_until b step x n) = true -> b (multi_until b step x m) = true.
Proof.
  intros.
  induction H.
  - assumption.
  - eapply multi_until_S;eauto.
Qed.

Definition multi_step' {X : Type} (step : X -> X) (x : X) (n : nat)
  := match n with
     | 0 => []
     | S n => multi_step step x n
     end.