Section PWO_Seq.
Variable D1 R1 DO RO : Set.
Variable (PWO_P1 : ProcedureWithOracle D1 R1 DO RO).
Variable R2 : Set.
Variable (PWO_P2 : ProcedureWithOracle R1 R2 DO RO).
Hint Resolve (PWO_P1.(S_P_EqDec)) : typeclass_instances.
Hint Resolve (PWO_P2.(S_P_EqDec)) : typeclass_instances.
Hint Resolve (PWO_P1.(D_O_EqDec)) : typeclass_instances.
Hint Resolve (PWO_P2.(R_P_EqDec)) : typeclass_instances.
Definition S_P' := (nat * (PWO_P1.(S_P) + PWO_P2.(S_P))).
Definition PWO_Seq_P1 (d : D1) : Comp (DO * S_P') :=
[d, s] <-$2 PWO_P1.(P1) d;
ret (d, (PWO_P1.(q), inl s)).
intuition.
unfold PWO_Seq_P1.
wftac.
eapply P1_wf.
Qed.
Definition PWO_Seq_P2 (s : S_P') (r : RO)  : Comp (DO * S_P') :=
match (snd s) with
| inl s' =>
match (fst s) with
| O =>
r <-$ P3 s' r;
[d', s'] <-$2 PWO_P2.(P1) r;
ret (d', (O, inr s'))
| S q' =>
[d', s'] <-$2 P2 s' r;
ret (d', (q', inl s'))
end
| inr s' =>
[d', s'] <-$2 P2 s' r;
ret (d', (O, inr s'))
end.
intuition.
unfold PWO_Seq_P2.
destruct (snd x); destruct (fst x); wftac.
eapply P3_wf.
eapply P1_wf.
eapply P2_wf.
eapply P2_wf.
eapply P2_wf.
Qed.
Variable R2_def : R2.
Definition PWO_Seq_P3 (s : S_P') r : Comp R2 :=
match (snd s) return (Comp R2) with
| inl s' => ret R2_def
| inr s' => P3 s' r
end.
intuition.
unfold PWO_Seq_P3.
destruct (snd x); wftac.
eapply P3_wf.
Qed.
Definition PWO_Seq : ProcedureWithOracle D1 R2 DO RO.
econstructor.
eapply (S (PWO_P1.(q) + PWO_P2.(q)))%nat.
eapply PWO_Seq_P1_wf.
eapply PWO_Seq_P2_wf.
eapply PWO_Seq_P3_wf.
unfold S_P'.
intuition.
intuition.
eapply PWO_P1.(R_O_EqDec).
intuition.
Defined.
(* Theorem PWO_Seq_runP2 : forall n (S_O : Set) (s_O : S_O)(S_O_EqDec : EqDec S_O)(RO_EqDec : EqDec RO) m s_P d_O o x, evalDist ( [s_O, s_P, d_O] <-$3 runP2 (@P2 _ _ _ _ PWO_P1) o _ _ _ n s_O s_P d_O; [r_O, s_O] <-$2 o s_O d_O; r_P1 <-$ P3 s_P r_O; [d_O, s_P] <-$2 PWO_P2.(P1) r_P1; [s_O, s_P, d_O] <-$3 runP2 (@P2 _ _ _ _ PWO_P2) o _ _ _ m s_O s_P d_O; ret (s_O, (0%nat, inr s_P), d_O)) x == evalDist (runP2 (PWO_Seq_P2) o _ _ _ (S (n + m)) s_O (n, inl s_P) d_O) x.  induction n; intros.  rewrite plus_0_l. unfold runP2. rewrite compLoop_O. dist_ret_l. unfold setLet. rewrite compLoop_S. unfold runP2_once. inline_first. eapply evalDist_seq_eq. intuition. intros. destruct x0.  unfold PWO_Seq_P2. unfold snd, fst. inline_first. eapply evalDist_seq_eq. intuition. intros. inline_first. eapply evalDist_seq_eq. intuition. intros. destruct x1. dist_ret_r. unfold setLet. dist_ret_r.  symmetry. rewrite <- evalDist_right_ident. symmetry.  eapply comp_spec_eq_impl_eq. eapply comp_spec_seq; intuition. admit. admit. eapply (@compLoop_spec _ _ _ _ (fun (x : S_O * S_P PWO_P2 * DO) (y :  S_O * S_P' * DO) => snd x = snd y /\ fst (fst x) = fst (fst y) /\ (0, inr (snd (fst x))) = snd (fst y)))%nat; intros. intuition.   destruct x. destruct y. destruct p. destruct p0. simpl in H2. simpl in H5. simpl in H3. subst. comp_skip. admit.  eapply comp_spec_symm. eapply comp_spec_eq_trans.  specialize (@comp_spec_assoc _ _ _ _ (P2 (p:=PWO_P2) s2 a3) (fun z => [d', s']<-2 z; ret (d', (0, inr s'))) (fun z => [d0, s_P0]<-2 z; ret (b0, s_P0, d0))); intuition.   (* Ltac prog_inline_r := match goal with | [ |- comp_spec _ _ _ ] => eapply comp_spec_eq_trans_r; [idtac | eapply comp_spec_symm; eapply comp_spec_consequence; [ eapply comp_spec_assoc | intuition] ] end. *)
Ltac prog_inline_r :=
match goal with
| [ |- comp_spec _ _ _ ] =>
eapply comp_spec_eq_trans_r; [idtac | eapply comp_spec_symm;
eapply comp_spec_consequence; [
eapply comp_spec_assoc |
intuition] ]
end.
Show.
eapply comp_spec_eq_trans_r.
Focus 2.
eapply comp_spec_symm.
eapply comp_spec_consequence.
assert (EqDec DO).
admit.
specialize (@comp_spec_assoc _ _ _ _ (P2 (p:=PWO_P2) s2 a3) (fun z => [d', s']<-2 z; ret (d', (0, inr s')))); intros.
eapply H4.
eapply comp_spec_assoc.
Show.
prog_inline_r.
inline_first.
eapply comp_spec_seq.
admit.
admit.
eapply comp_spec_eq_refl.
intuition.
subst.
comp_skip.
unfold comp_spec_post; intros.
destruct H2.
destruct H5.
destruct y.
destruct x1.
simpl in H2.
simpl in H5.
destruct p0.
destruct p.
simpl in H5.
subst.
comp_simp.
inline_first.
eapply evalDist_seq.
intuition.
intuition.
intros.
subst.
comp_simp.
simpl in H6.
inversion H6; clear H6; subst.
inline_first.
eapply evalDist_seq.
intuition.
intuition.
intros.
subst.
comp_simp.
eapply H4.
simpl.
intuition.
eapply getSupport_In_Seq; eauto.
comp_simp.
eapply getSupport_In_Seq; eauto.
comp_simp.
simpl. intuition.
eapply getSupport_In_Seq; eauto.
comp_simp.
eapply getSupport_In_Seq; eauto.
eapply getSupport_In_Seq; eauto.
comp_simp.
simpl. intuition.
comp_simp.
simpl. intuition.
simpl.
intuition.
intuition.
comp_simp.
destruct r2.
destruct p.
intuition;
simpl in H5;
simpl in H2;
simpl in H7;
subst;
eapply eqRat_refl.
unfold runP2 in *.
rewrite compLoop_S.
eapply eqRat_trans.
eapply evalDist_seq_eq.
intros.
eapply compLoop_S.
intros.
eapply eqRat_refl.
inline_first.
eapply evalDist_seq_eq.
intuition.
intros.
comp_simp.
inline_first.
eapply evalDist_seq_eq.
intuition.
intros.
comp_simp.
rewrite Nat.add_succ_l.
eapply IHn.
Qed.

(* running the two PWOs in sequence is equivalent to running PWO_Seq *)
Theorem PWO_Seq_equiv :
forall (S_O : Set) o x d_P (s_O : S_O) e1 e2 e3,
evalDist ([s_O, r_P1] <-$2 PWO_P1 _ e1 o d_P s_O;
PWO_P2 _ e2 o r_P1 s_O) x ==
evalDist (PWO_Seq _ e3 o d_P s_O) x.