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.

Lemma randomPerm_bad_preserves_wf : forall ls d ls' r,
well_formed_Bvector_perm ls ->
In (ls', r) (getSupport (randomPerm_bad (ls, d))) ->
well_formed_Bvector_perm ls'.