Section DistSingle_impl_Mult.
Variable A B : Set.
Variable A_EqDec : EqDec A.
Variable B_EqDec : EqDec B.
Variable c1 c2 : A -> Comp B.
Variable A_State : Set.
Variable A_State_EqDec : EqDec A_State.
Variable A1 : Comp (A * A_State).
Variable A2 : A_State -> list B -> Comp bool.
Hypothesis c1_wf : forall a, well_formed_comp (c1 a).
Hypothesis c2_wf : forall a, well_formed_comp (c2 a).
(* Produce a list of length n derived from c1, and c2.  The first i values come from c1, and the rest come from c2. *)
Definition computeHybrid(n i : nat) a : Comp (list B) :=
b1 <-$ compMap _ (fun _ => (c1 a)) (forNats i);
b2 <-$ compMap _ (fun _ => (c2 a)) (forNats (minus n i));
ret (b1 ++ b2).
Variable n : nat.
Hypothesis n_pos : n > 0.
induction n; destruct i; intuition; try omega.
unfold computeHybrid.
unfold minus.
fold minus.
rewrite <- minus_n_O.
unfold forNats.
fold forNats.
unfold compMap.
fold compMap.
inline_first.
comp_skip.
inline_first.
comp_simp.
comp_irr_l.
inline_first.
comp_skip.
simpl.
intuition.
unfold computeHybrid.
unfold minus.
fold minus.
unfold forNats.
fold forNats.
unfold compMap.
fold compMap.
inline_first.
do 2 comp_at comp_inline leftc 1%nat.
comp_swap_l.
comp_skip.
assert
(evalDist
(a1 <-$
(lsb' <-$
(b <-$ (c1 a);
lsb' <-$ compMap B_EqDec (fun _ : nat => (c1 a)) (forNats i);
ret b :: lsb'); ret x0 :: lsb');
a2 <-$ compMap B_EqDec (fun _ : nat => (c2 a)) (forNats (n0 - S i));
ret a1 ++ a2) x ==
evalDist
(ls <-$ (computeHybrid n0 (S i) a);
ret (x0 :: ls)) x).
unfold computeHybrid.
unfold forNats.
fold forNats.
unfold compMap.
fold compMap.
inline_first.
comp_skip.
inline_first.
comp_skip.
comp_simp.
inline_first.
comp_skip.
comp_simp.
simpl.
intuition.
rewrite H1.
clear H1.
eapply eqRat_trans.
2:{
comp_skip.
eapply eqRat_refl.
}
unfold computeHybrid.
inline_first.
comp_skip.
inline_first.
comp_skip.
inline_first.
comp_skip.
comp_simp.
simpl.
intuition.
Qed.
induction n; destruct i; intuition; try omega.
unfold computeHybrid.
unfold minus.
unfold forNats.
fold forNats.
unfold compMap.
fold compMap.
comp_simp.
inline_first.
comp_skip.
inline_first.
comp_irr_l.
inline_first.
comp_skip.
comp_simp.
simpl.
intuition.
unfold computeHybrid.
unfold minus.
fold minus.
unfold forNats.
fold forNats.
unfold compMap.
fold compMap.
inline_first.
do 2 comp_at comp_inline leftc 1%nat.
comp_swap_l.
comp_skip.
assert (evalDist
(a1 <-$
(lsb' <-$ compMap B_EqDec (fun _ : nat => (c1 a)) (forNats i);
ret x0 :: lsb');
a2 <-$ compMap B_EqDec (fun _ : nat => (c2 a)) (forNats (n0 - i));
ret a1 ++ a2) x ==
evalDist
(ls <-$
(a1 <-$ compMap B_EqDec (fun _ : nat => (c1 a)) (forNats i);
a2 <-$ compMap B_EqDec (fun _ : nat => (c2 a)) (forNats (n0 - i));
ret a1 ++ a2);
ret (x0 :: ls)) x).
inline_first.
comp_skip.
inline_first.
comp_skip.
comp_simp.
rewrite app_comm_cons.
intuition.
rewrite H1.
clear H1.
eapply eqRat_trans.
2:{
comp_skip.
eapply IHn0.
omega.
omega.
eapply eqRat_refl.
}
unfold computeHybrid.
inline_first.
comp_skip.
inline_first.
comp_skip.
inline_first.
comp_skip.
comp_simp.
simpl.
intuition.
Qed.
Definition B1 : Comp (A * (A * A_State)) :=
[a, s_A] <-$2 A1;
ret (a, (a, s_A)).
Definition B2(s : A * A_State)(b : B) : Comp bool :=
[a, s_A] <-2 s;
i <-$ [0 .. n);
rndHybrid <-$ computeHybrid n i a;
distHybrid <- listReplace rndHybrid i b b;
A2 s_A distHybrid.
Definition DistSingle_G1(c : A -> Comp B) :=
[a, s_A] <-$2 B1;
b <-$ (c a);
B2 s_A b.
unfold DistSingle_Adv, DistSingle_G1, DistSingle_G.
intuition.
Qed.
Definition DistSingle_G2(c : A -> Comp B) :=
[a, s_A] <-$2 A1;
i <-$ [ 0  .. n);
distHybrid <-$
(b <-$ c a;
rndHybrid <-$ computeHybrid n i a;
ret (listReplace rndHybrid i b b));
A2 s_A distHybrid.
intuition.
unfold DistSingle_G1, DistSingle_G2.
unfold B1, B2.
inline_first.
comp_skip.
comp_simp.
comp_swap_l.
comp_skip.
inline_first.
comp_skip.
comp_inline_r.
comp_skip.
comp_simp.
intuition.
Qed.
Definition DistSingle_G3_c2 :=
i <-$ [ 0  .. n);
[a, s_A] <-$2 A1;
distHybrid <-$ computeHybrid n i a;
A2 s_A distHybrid.
Definition DistSingle_G3_c1 :=
i <-$ [ 0  .. n);
[a, s_A] <-$2 A1;
distHybrid <-$ computeHybrid n (S i) a;
A2 s_A distHybrid.

Theorem DistSingle_G3_c2_equiv :
Pr[DistSingle_G2 c2] == Pr[DistSingle_G3_c2].