(*  Use of this source code is governed by the license described	* * in the LICENSE file at the root of the source tree.		*)
(* This file contains an earlier version of state-related definitions.  The theory from this file should be moved to Procedure.v *)
Set Implicit Arguments.
(* Require Import ProgramLogic_old.   Definition stateful_comp_spec (S D R: Set)(inv : S -> S -> Prop)(pre : (S * D) -> (S * D) -> Prop)(post : (S * R) -> (S * R) -> Prop)(c1 c2 : (S * D) -> Comp (S * R)):= comp_spec_old (fun a1 a2 => pre a1 a2 /\ inv (fst a1) (fst a2)) (fun b1 b2 => post b1 b2 /\ inv (fst b1) (fst b2)) c1 c2 .   Section CompLoop.  Variable D R State : Set. Hypothesis S_EqDec : EqDec State.  Fixpoint compLoop (c : (State * D) -> Comp (State * R))(count : nat)(s : State)(d : D) : Comp State := match count with | 0 => ret s | S count' => [s', _] <-$2 c (s, d); compLoop c count' s' d end.  Lemma compLoop_wf : forall (o : (State * D) -> Comp (State * R)) count (s : State) (d : D), (forall x y, well_formed_comp (o (x, y))) -> well_formed_comp (compLoop o count s d).  induction count; intuition; unfold compLoop; wftac.  Qed.   Lemma compLoop_inv : forall (c : (State * D) -> Comp (State * R)) (inv : State -> Prop) d, (forall s s' r,  In (s', r) (getSupport (c (s, d))) -> inv s -> inv s') -> forall count s s', inv s -> In s' (getSupport (compLoop c count s d)) -> inv s'.  induction count; intuition; simpl in *. intuition; subst. trivial.  eapply in_getUnique_if in H1. apply in_flatten in H1. destruct H1. intuition. apply in_map_iff in H2. destruct H2. intuition. subst. destruct x0. eapply IHcount. eapply H. eauto. trivial. trivial.  Qed.  End CompLoop.  Class AdversaryWithOracle_concrete (D_A R_A D_O R_O : Set) (S_A : Set) (A1 : D_A -> Comp (S_A * D_O)) (A2 : S_A -> R_O -> Comp (S_A * D_O)) (A3 : S_A -> Comp R_A) := {  S_A_EqDec : EqDec S_A;  A1_wf : forall x, well_formed_comp (A1 x); A2_wf : forall x y, well_formed_comp (A2 x y); A3_wf : forall x, well_formed_comp (A3 x)  }.  Definition StatefulProc (S D R : Set) := S * D -> Comp (S * R).  Section AdversaryWithOracle_concrete.  Context `{A : AdversaryWithOracle_concrete }. Variable S_O : Set. (*  Definition Oracle := StatefulProc S_O D_O R_O. *)
Instance eqd_S_A : EqDec S_A.
eapply S_A_EqDec.
Defined.
Hypothesis R_A_EqDec : EqDec R_A.
Hypothesis D_A_EqDec : EqDec D_A.
Hypothesis S_O_EqDec : EqDec S_O.
Hypothesis D_O_EqDec : EqDec D_O.
Hypothesis R_O_EqDec : EqDec R_O.
Fixpoint loopA (oracleF : StatefulProc S_O D_O R_O)(count : nat)(s_A : S_A)(s_O : S_O)(d_O : D_O) : Comp (S_A * S_O) :=
match count with
| 0 => ret (s_A, s_O)
| S count' =>
[s_O', r_O] <-$2 oracleF (s_O, d_O);
[s_A', d_O'] <-$2 (A2 s_A r_O);
loopA oracleF count' s_A' s_O' d_O'
end.
induction count; intuition; simpl in *; wftac.
eapply A2_wf.
Qed.
intuition.
eapply (loopA_wf_inv oracleF (fun x => True)); intuition.
Qed.
Definition runA oracleF (count : nat)(s_O : S_O)(d_A : D_A) :=
p <-$ A1 d_A;
p' <-$ loopA oracleF count (fst p) s_O (snd p);
r <-$ A3 (fst p');
ret (r, (snd p')).
induction count; intuition;
unfold loopA, compLoop.
comp_simp.
eauto.
fold loopA.
inline_first.
eapply H; intuition.
inline_first.
comp_irr_l.
eapply A2_wf.
comp_simp.
eapply IHcount; intuition.
Qed.
intuition.
unfold runA.
inline_first.
comp_irr_l.
eapply A1_wf.
inline_first.
eapply (loopA_eq_compLoop H); intuition.
inline_first.
comp_irr_l; wftac.
eapply A3_wf.
Qed.
induction count; intuition.
comp_ute.
unfold loopA.
fold loopA.
inline_first.
eapply H0;
intuition.
inline_first.
simpl in H5.
subst.
comp_simp.
inline_first.
comp_skip.
simpl.
intuition.
Qed.
induction count; intuition.
comp_ute.
eapply add_proper.
comp_ute.
eapply mult_proper; comp_ute.
unfold loopA.
fold loopA.
inline_first.
eapply H1; intuition.
inline_first.
simpl in H6.
subst.
comp_simp.
inline_first.
comp_skip.
simpl; intuition.
eapply IHcount; intuition.
Qed.
(* Lemma loopA_spec : forall (A : Set)(inv : S_O -> S_O -> Prop)(o1 o2 : Oracle)(count : nat)(s_O1 s_O2 : S_O)(s_A : S_A)(d_O : D_O) f1 f2 (x : A), inv s_O1 s_O2 -> stateful_comp_spec inv (fun p1 p2 => snd p1 = snd p2) (fun p1 p2 => snd p1 = snd p2) o1 o2 -> (forall r1 r2 o1 o2, inv o1 o2 -> eq r1 r2 ->evalDist (f1 (r1, o1)) x <= evalDist (f2 (r2, o2)) x) -> evalDist (p <-$ loopA o1 count s_A s_O1 d_O; f1 p) x <= evalDist (p <-$ loopA o2 count s_A s_O2 d_O; f2 p) x.  induction count; intuition. comp_ute. eapply leRat_trans. Focus 2. eapply eqRat_impl_leRat. eapply ratAdd_0_l. repeat pairInv. eapply H1; intuition.  unfold loopA. fold loopA.  eapply leRat_trans. eapply eqRat_impl_leRat. comp_inline O O. eapply eqRat_refl.  eapply leRat_trans. Focus 2. eapply eqRat_impl_leRat. comp_inline 1%nat O. eapply eqRat_refl.  eapply H0; intuition.  simpl in H3. subst.  eapply leRat_trans. eapply eqRat_impl_leRat. comp_inline O O. eapply eqRat_refl.  comp_simp. eapply leRat_trans. Focus 2. eapply eqRat_impl_leRat. comp_inline 1%nat O. eapply eqRat_refl.  eapply evalDist_Bind_id_le; intuition. simpl. intuition. Qed. *)
induction count; intuition.
comp_ute.
repeat pairInv.
eapply add_proper.
eapply refl.
intuition.
eapply mult_proper.
eapply refl;intuition.
eapply H4; eauto.
unfold loopA.  fold loopA.
inline_first.
eapply H0;
intuition.
inline_first.
comp_irr_l.
eapply A2_wf.
comp_simp.
inline_first.
comp_irr_r.
eapply A2_wf.
comp_simp.
eapply IHcount; intuition; eauto.
Qed.
intuition.
eapply loopA_spec_irr; intuition; eauto.
eauto.
eauto.
Qed.

Lemma loopA_spec_2 :
forall rel (A : Set)(pre1 pre2 : (S_O * D_O) -> (S_O * D_O) -> Prop) post1 post2 post3 (o1 o2 : StatefulProc S_O D_O R_O),
SpecRel rel ->
comp_spec_old pre1 (fun p1 p2 => (post1 p1 p2) \/ (post2 p1 p2)) o1 o2 ->
comp_spec_old pre2 post3 o1 o2 ->
(forall s1 s2 r1 r2, post1 (s1, r1) (s2, r2) -> r1 = r2) ->
(forall s1 s2 r1 r2 d, post1 (s1, r1) (s2, r2) -> pre1 (s1, d) (s2, d)) ->
(forall s1 s2 r1 r2 d1 d2, post2 (s1, r1) (s2, r2) -> pre2 (s1, d1) (s2, d2)) ->
forall (count : nat)(s_O1 s_O2 : S_O)(s_A1 s_A2 : S_A)(d_O1 d_O2 : D_O) f1 f2 (x : A),
(pre1 (s_O1, d_O1) (s_O2, d_O2) /\ s_A1 = s_A2) \/ pre2 (s_O1, d_O1) (s_O2, d_O2) ->
(forall s_O1' s_O2' r1 r2 d1 d2 s_A1' s_A2' s_A1'' s_A2'',
post3 (s_O1', r1) (s_O2', r2) ->
In (s_A1'', d1) (getSupport (A2 s_A1' r1)) ->
In (s_A2'', d2) (getSupport (A2 s_A2' r2)) ->
pre2 (s_O1', d1) (s_O2', d2)) ->
(forall s_O1' s_O2' d_O1' d_O2' s_A',
pre1 (s_O1', d_O1') (s_O2', d_O2') ->
rel (evalDist (f1 (s_A', s_O1')) x) (evalDist (f2 (s_A', s_O2')) x)) ->
(forall s_O1' s_O2' d_O1' d_O2' s_A1' s_A2',
pre2 (s_O1', d_O1') (s_O2', d_O2') ->
rel (evalDist (f1 (s_A1', s_O1')) x) (evalDist (f2 (s_A2', s_O2')) x)) ->
rel (evalDist (p <-$ loopA o1 count s_A1 s_O1 d_O1; f1 p) x) (evalDist (p <-$ loopA o2 count s_A2 s_O2 d_O2; f2 p) x).