Section PRP_PRF.
Variable n : nat. (* bit vector length *)
Hint Resolve getUnusedR_wf : wftac.
Hint Resolve well_formed_perm_1 : wftac.
Definition getUnusedBvector := @getUnusedR (Bvector n) _ _ (Rnd n).
Definition well_formed_Bvector_perm := @well_formed_perm (Bvector n) _ _ _ (Rnd n).
Definition randomPerm_Bvector := @randomPerm (Bvector n) _ _ _ (Rnd n).
intuition.
eapply getUnusedR_wf; wftac.
simpl.
eapply in_getAllBvectors.
Qed.
intuition.
eapply well_formed_perm_1 in H.
destruct H.
intuition.
unfold getUnusedBvector.
eapply getUnusedR_wf; wftac.
eauto.
Qed.
Hint Resolve getUnusedBvector_wf : wftac.
(* A random permutation in which the loop that gets an unused value from the range is unrolled one step.  This is useful when comparing to a random function. *)
Definition randomPerm_unroll(p : (list (Bvector n * Bvector n)) * Bvector n) : Comp (list (Bvector n * Bvector n) * Bvector n) :=
let (f, d) := p in
match f#d with
| None => (r <-$ (r0 <-$ {0, 1}^n; if (negb (inRange _ f r0)) then ret r0 else getUnusedBvector f) ; ret ((d, r) :: f, r))
| Some r => ret (f, r)
end.
intuition.
unfold randomPerm_unroll.
case_eq (ls#b0); intuition; wftac.
edestruct H; eauto.
eapply getUnusedBvector_wf_h.
eauto.
Qed.
(* A random permutation in which the loop that gets an unused value form the range is unrolled one step and the permutation keeps track of whether the first attempt located a value that is already in the range.  This is useful when comparing to a random function. *)
Definition randomPerm_bad(p : (list (Bvector n * Bvector n)) *  Bvector n) : Comp (list (Bvector n * Bvector n) * Bvector n) :=
let (f, d) := p in
match f#d with
| None =>
r0 <-$ {0,1} ^ n;
r <-$ if (negb (inRange _ f r0)) then ret r0 else getUnusedBvector f;
ret (if (inRange _ f r0) then ((d, r) :: (d, r0) :: f) else ((d, r) :: f), r)
| Some r => ret (f, r)
end.
Fixpoint checkBad(f : list (Bvector n * Bvector n)) : bool :=
match f with
| nil => false
| (d, r) :: f' =>
orb (inRange _ f' r) (checkBad f')
end.
intuition.
unfold randomPerm_bad.
case_eq (ls#d); intuition.
wftac.
wftac.
Qed.
Hint Resolve randomPerm_bad_wf : wftac.
intuition.
unfold randomPerm_Bvector, randomPerm, randomPerm_unroll.
case_eq (s#d); intuition.
unfold getUnusedR.
comp_skip.
eapply repeat_unroll_eq; wftac.
eapply well_formed_perm_1 in H; eauto.
destruct H.
exists x0.
eapply filter_In; intuition.
destruct (inRange _ s x0); intuition.
Qed.
intuition.
unfold randomPerm_unroll in *.
case_eq (ls#d); intuition.
rewrite H1 in H0.
simpl in *.
intuition.
inversion H2; clear H2; subst.
trivial.
rewrite H1 in H0.
apply getSupport_Bind_In in H0.
destruct H0.
intuition.
apply getSupport_Bind_In in H2.
destruct H2.
intuition.
simpl in H3.
intuition.
inversion H0; clear H0; subst.
case_eq (inRange _ ls x0); intuition;
rewrite H0 in H4.
simpl in *.
apply filter_In in H4.
intuition.
eapply well_formed_perm_cons; eauto.
destruct (inRange _ ls r); intuition.
simpl in *.
intuition. subst.
eapply well_formed_perm_cons; eauto.
Qed.
intuition.
destruct p.
eapply randomPerm_unroll_preserves_wf; eauto.
Qed.
intuition.
unfold randomPerm_bad in *.
case_eq (ls#d); intuition;
rewrite H1 in H0.
simp_in_support.
trivial.
simp_in_support.
case_eq (inRange _ ls x); intuition;
rewrite H0 in H3.
simp_in_support.
simpl in *.
intuition.
inversion H3; clear H3; subst.
apply filter_In in H4.
intuition.
unfold well_formed_Bvector_perm, well_formed_perm in *.
intuition.
destruct (in_dec (EqDec_dec _) d lsd).
specialize (H6 d).
intuition.
simpl in *.
rewrite eqbBvector_complete in H7.
discriminate.
edestruct (H (d :: lsd)).
econstructor; trivial.
intuition.
simpl in H7.
intuition. subst.
trivial.
specialize (H6 d0).
intuition.
simpl in H7.
destruct (eqbBvector d0 d).
discriminate.
trivial.
intuition.
destruct (in_dec (EqDec_dec _) r x0).
exists (removeFirst (EqDec_dec _) x0 r).
intuition.
simpl.
case_eq (eqbBvector r0 r); intuition.
apply eqbBvector_sound in H11.
subst.
exfalso.
eapply removeFirst_NoDup_not_in; eauto.
case_eq (eqbBvector r0 x); intuition.
apply eqbBvector_sound in H12.
subst.
assert (inRange  _ ls x = false).
eapply H8.
eapply removeFirst_in_iff.
eauto.
congruence.
eapply H8.
eapply removeFirst_in_iff.
eauto.
simpl.
eapply in_getAllBvectors.
eapply removeFirst_NoDup.
trivial.
eapply eq_add_S.
simpl in H10.
rewrite H10.
symmetry.
eapply removeFirst_length.
trivial.
destruct x0.
simpl in *.
omega.
exists x0.
intuition.
simpl.
simpl in n1.
intuition.
case_eq (eqbBvector r0 r); intuition.
apply eqbBvector_sound in H13.
subst.
intuition.
case_eq (eqbBvector r0 x); intuition.
apply eqbBvector_sound in H14.
subst.
assert (inRange _ ls x = false).
eapply H8.
simpl.
right.
trivial.
congruence.
eapply H8.
simpl.
intuition.
simpl.
eapply in_getAllBvectors.
inversion H7; clear H7; subst.
trivial.
repeat simp_in_support.
eapply well_formed_perm_cons; trivial.
simpl in *.
discriminate.
Qed.
intuition.
destruct p.
eapply randomPerm_bad_preserves_wf; eauto.
Qed.
Definition Bvector_perms_eq := @perms_eq (Bvector n) _ _ _ (Rnd n).
(* Lemma randomPerm_unroll_bad_inv : comp_spec (fun p1 p2 => (snd p1) = (snd p2) /\ Bvector_perms_eq (fst p1) (fst p2)) (fun p1 p2 => (snd p1) = (snd p2) /\ Bvector_perms_eq (fst p1) (fst p2)) randomPerm_unroll randomPerm_bad.  unfold comp_spec.  intuition. simpl in H1. subst. unfold randomPerm_unroll, randomPerm_bad. unfold Bvector_perms_eq, perms_eq, funcs_eq in *. intuition. destruct d2. unfold snd. simpl in H3.  rewrite H3. case_eq (l # b); intuition.  comp_simp. eapply H0; intuition.  unfold randomPerm_unroll. simpl. rewrite H3. rewrite H4. simpl. intuition.  unfold randomPerm_bad. rewrite H4. simpl. intuition.  inline_first. comp_skip. inline_first.  unfold fst, snd in H5. rewrite H5. case_eq (inRange _ l r2); intuition; unfold negb. comp_skip. eapply getUnusedR_eq; intuition.  comp_simp. eapply H0; intuition; unfold fst. eapply well_formed_perm_cons; trivial. rewrite H3. trivial. eapply getUnusedR_not_inRange. rewrite H3. eauto. eauto.  eapply well_formed_perm_cons_2; intuition. simpl. destruct (eqbBvector d b); intuition. simpl. case_eq (eqbBvector r r0); intuition. case_eq (eqbBvector r r2); intuition. apply eqbBvector_sound in H12. subst. rewrite H5. trivial.  unfold snd.  eapply getSupport_In_evalDist. intuition. eapply getSupport_In_evalDist. eapply (@inSupportRandomPerm (Bvector n) (Bvector n)). eauto. rewrite H3. eauto. rewrite randomPerm_eq_unroll. trivial. simpl in H1. trivial.  Theorem inSupportRandomPerm_bad : forall a b r1 r2, In r1 (getSupport (getUnusedBvector a)) -> inRange _ a r2  = true -> a # b = None -> In ((b, r1) :: (b, r2) :: a, r1) (getSupport (randomPerm_bad (a, b))).  intuition. unfold randomPerm_bad. rewrite H1. eapply getSupport_In_Seq. simpl. eapply (in_getAllBvectors r2). eapply getSupport_In_Seq. rewrite H0. unfold negb. eauto. rewrite H0. simpl. intuition. Qed.  eapply inSupportRandomPerm_bad; eauto.  comp_simp.  eapply H0; intuition; unfold fst. eapply well_formed_perm_cons; intuition. rewrite H3. trivial. rewrite H5. trivial. eapply well_formed_perm_cons; eauto. simpl. rewrite H3. trivial. simpl. rewrite H5. trivial.  unfold snd. eapply getSupport_In_evalDist. intuition. eapply getSupport_In_evalDist. eapply (@inSupportRandomPerm (Bvector n) (Bvector n)).  simpl. eapply filter_In. split. eapply H8.  instantiate (1:=a). rewrite H5. rewrite H6. trivial. rewrite H3. eauto.  rewrite randomPerm_eq_unroll. trivial. simpl in H1. trivial.  Theorem inSupportRandomPerm_bad_1 : forall a b r1, In r1 (getSupport (getUnusedBvector a)) -> a # b = None -> In ((b, r1) :: a, r1) (getSupport (randomPerm_bad (a, b))).  intuition. unfold randomPerm_bad. rewrite H0. eapply getSupport_In_Seq. simpl. eapply (in_getAllBvectors r1). eapply getSupport_In_Seq. erewrite getUnusedR_not_inRange. simpl. intuition. eauto. eauto. erewrite getUnusedR_not_inRange. simpl. intuition. eauto. eauto. Qed.  eapply inSupportRandomPerm_bad_1; eauto. simpl. eapply filter_In. split. eapply in_getAllBvectors. rewrite H6. trivial.  Qed. *)

Lemma filter_eq : forall (A : Set)(ls : list A)(P1 P2 : A -> bool),
(forall a, In a ls -> P1 a = P2 a) ->
filter P1 ls = filter P2 ls.