(* Use of this source code is governed by the license described	* * in the LICENSE file at the root of the source tree.		*)
Set Implicit Arguments.
Local Open Scope list_scope.
Inductive in_oc_supp : forall (A B C : Set), C -> OracleComp A B C -> Prop :=
| in_oc_supp_Bind :
forall (A B C C' : Set)(c : OracleComp A B C')(f : C' -> OracleComp A B C) x y,
in_oc_supp x c ->
in_oc_supp y (f x) ->
in_oc_supp y (OC_Bind c f)
| in_oc_supp_Query :
forall (A B : Set)(a : A)(b : B),
in_oc_supp b (OC_Query B a)
| in_oc_supp_Ret :
forall (A B C : Set)(c : Comp C) x,
In x (getSupport c) ->
in_oc_supp x (OC_Ret A B c)
| in_oc_supp_Run :
forall (A A' B B' C S : Set)(eqds : EqDec S)(eqda : EqDec A)(eqdb : EqDec B)
(c : OracleComp A B C)(oc : S -> A -> OracleComp A' B' (B * S)) s x s',
in_oc_supp x c ->
in_oc_supp (x, s') (OC_Run _ _ _ c oc s).
induction c; intuition; repeat simp_in_support.
econstructor.
Local Opaque getSupport.
simpl in *.
repeat simp_in_support.
destruct x.
simpl.
destruct p.
simpl.
econstructor.
eapply (@IHc _ _ (S * S0)%type).
eapply H1.
simpl in *.
repeat simp_in_support.
econstructor.
intuition.
assert (EqDec C).
eapply oc_EqDec.
eapply c.
intuition.
assert (B * S).
eapply comp_base_exists.
eapply o0.
trivial.
intuition.
intuition.
intuition.
assert (EqDec (B * S)).
eapply comp_EqDec.
eapply o0; trivial.
eapply EqDec_pair_l.
eapply H2.
trivial.
simpl in *.
repeat simp_in_support.
destruct x0.
econstructor.
eapply (@H0 _ S).
eapply H2.
eapply (@H _ _ _ S).
eapply H3.
Grab Existential Variables.
eapply EqDec_pair_l.
eauto.
trivial.
Qed.
Local Open Scope nat_scope.
Inductive queries_at_most' : forall (A B C : Set), OracleComp A B C -> nat -> Prop :=
| qam_Bind :
forall (A B C C' : Set)(eqdc' : EqDec C')(c : OracleComp A B C')(f : C' -> OracleComp A B C) q1 q2,
queries_at_most' c q1 ->
(forall c',
in_oc_supp c' c ->
queries_at_most' (f c') q2) ->
queries_at_most' (OC_Bind c f) (q1 + q2)
| qam_Query :
forall (A B : Set)(a : A),
queries_at_most' (OC_Query B a) 1
| qam_Ret :
forall (A B C : Set)(c : Comp C),
queries_at_most' (OC_Ret A B c) 0
| qam_Run :
forall (A A' B B' C S : Set)(eqds : EqDec S)(eqda : EqDec A)(eqdb : EqDec B)
(c : OracleComp A B C)(oc : S -> A -> OracleComp A' B' (B * S)) s q1 q2,
queries_at_most' c q1 ->
(forall s a, queries_at_most' (oc s a) q2) ->
queries_at_most' (OC_Run _ _ _ c oc s) (q1 * q2)
| qam_le :
forall (A B C : Set)(c : OracleComp A B C) q1 q2,
queries_at_most' c q1 ->
q1 <= q2 ->
queries_at_most' c q2.
Opaque getSupport.
induction 1; intuition; simpl in *.
(* Bind case *)
repeat simp_in_support.
destruct x.
rewrite mult_plus_distr_r.
pose proof H4.
eapply (IHqueries_at_most' _ count) in H4.
eapply (H1 _ _ _ count) in H5.
rewrite H5.
rewrite plus_comm.
rewrite <- (plus_assoc).
rewrite (plus_comm (q2 * n)).
rewrite plus_assoc.
eapply plus_le_compat.
eauto.
eapply le_refl.
intuition.
intuition.
(* query case *)
rewrite plus_0_r.
eapply H; eauto.
(* ret case *)
Transparent getSupport.
repeat simp_in_support.
intuition.
(* run case *)
repeat simp_in_support.
destruct x.
simpl in *.
specialize (IHqueries_at_most' (S * S0)%type (fun p =>  count (snd p)) _
(fun (x : S * S0) (y : A) =>
p <-$ (oc (fst x) y) S0 eqds0 o (snd x);
ret (fst (fst p), (snd (fst p), snd p)))
(s, s0) (q2 * n)
).
eapply le_trans.
eapply IHqueries_at_most'.
intuition.
simpl.
repeat simp_in_support.
destruct x.
simpl in *.
eapply H1.
eauto.
eauto.
eauto.
simpl.
rewrite mult_assoc.
intuition.
(* trans case *)
eapply le_trans.
eapply IHqueries_at_most'.
eauto.
eauto.
eapply plus_le_compat; intuition.
eapply mult_le_compat; intuition.
Grab Existential Variables.
eapply in_oc_supp_complete; eauto.
Qed.
(* Definition countOracle{A B State : Set}{eqdb : EqDec B}{eqds : EqDec State}(defB : B)(o : State -> A -> Comp (B * State)) p a := [s, n] <-2 p; match n with | 0 => ret (defB, p) | S n' => [b', s'] <-$2 o s a; ret (b', (s', n')) end.  Theorem countOracle_equiv : forall (A B C : Set)(defB : B)(eqdb : EqDec B)(eqdc : EqDec C)(c : OracleComp A B C) n, queries_at_most' c n -> forall (S : Set)(eqd : EqDec S)(o : S -> A -> Comp (B * S))(s : S), comp_spec (fun a b => fst a = fst b /\ snd a = fst (snd b)) (c _ _ o s) (c _ _ (countOracle defB o) (s, n)).  induction 1; intuition; simpl in *.  admit. admit. admit.  comp_skip. admit. admit. assert (EqDec C). eapply EqDec_pair_l. eauto. trivial.  assert (B * S). eapply oc_base_exists. eapply oc  specialize (@IHqueries_at_most' eqdb0 H2 defB (S * S0)%type). eapply H1. eapply oc_comp_spec_eq.  Qed.  *)
Local Open Scope rat_scope.
intuition.
simpl in *.
rewrite (sumList_partition evta).
eapply ratAdd_leRat_compat.
assert ( sumList (getSupport c)
(fun a : A => evalDist c a * Pr  [f a ] * (if evta a then 1 else 0)) <=
sumList (getSupport c)
(fun b : A =>
evalDist c b * (if EqDec_dec bool_EqDec (evta b) true then 1 else 0)) ).
eapply sumList_le; intuition.
destruct (EqDec_dec bool_EqDec (evta a) true ).
rewrite e.
eapply ratMult_leRat_compat; intuition.
eapply leRat_trans.
eapply ratMult_leRat_compat.
eapply leRat_refl.
eapply evalDist_le_1.
rewrite ratMult_1_r.
intuition.
destruct (evta a); intuition.
repeat rewrite ratMult_0_r.
eapply rat0_le_all.
rewrite H1.
eapply H.
assert ( sumList (getSupport c)
(fun a : A => evalDist c a * Pr  [f a ] * (if evta a then 0 else 1)) <=
sumList (getSupport c)
(fun a : A => k2 * (evalDist c a * (if evta a then 0 else 1)))).
eapply sumList_le.
intuition.
case_eq (evta a); intuition.
eapply eqRat_impl_leRat.
repeat rewrite ratMult_0_r.
intuition.
rewrite H0.
eapply eqRat_impl_leRat.
repeat rewrite ratMult_1_r.
eapply ratMult_comm.
trivial.
trivial.
rewrite H1.
rewrite sumList_factor_constant_l.
eapply leRat_trans.
2:{
eapply eqRat_impl_leRat.
eapply ratMult_1_r.
}
eapply ratMult_leRat_compat; intuition.
assert (sumList (getSupport c)
(fun a : A => evalDist c a * (if evta a then 0 else 1)) <=
sumList (getSupport c)
(fun a : A => evalDist c a)).
eapply sumList_le.
intuition.
eapply leRat_trans.
2:{
eapply eqRat_impl_leRat.
eapply ratMult_1_r.
}
eapply ratMult_leRat_compat; intuition.
destruct (evta a); intuition.
eapply rat0_le_all.
rewrite H2.
eapply evalDist_sum_le_1.
Qed.

Theorem oc_eventProb :
forall (A B C : Set)(c : OracleComp A B C) n,
queries_at_most' c n ->
forall
(S : Set)(eqds : EqDec S)(o : S -> A -> Comp (B * S))
(count : S -> nat)(evt : S -> bool)(s : S)(k : nat -> Rat) i,
(forall (n1 n2 : nat), (n1 <= n2)%nat -> (k n1 <= k n2)) ->
evt s = false ->
(forall s a, evt s = false -> Pr[p <-$ o s a; ret (evt (snd p))] <= (k (i + (count s))%nat)) ->
(forall s s' a b, In (b, s') (getSupport (o s a)) ->
count s' <= i + (count s))%nat ->
Pr[p <-$ c _ _ o s; ret (evt (snd p))] <= (n / 1) * (k (i * n + (count s))%nat).