Set Implicit Arguments.
Local Open Scope list_scope.
Section hF.
Variable b k c : nat.
Variable h : Bvector c -> Bvector b -> Bvector c.
Variable F : Bvector k -> list (Bvector b) -> Bvector b.
Definition hF key m :=
[k_Out, k_In] <-2 splitVector c k key;
h k_Out (F k_In m).
Variable A : OracleComp (list (Bvector b)) (Bvector c) bool.
Hypothesis A_wf : well_formed_oc A.
(* Step 0: inline and simplify *)
Definition G0 :=
k_in <-$ {0, 1}^k;
k_out <-$ {0, 1}^c;
[b, _] <-$2 A _ _ (fun (_ : unit) m => ret (h k_out (F k_in m), tt)) tt;
ret b.
(* Step 1: replace h with a random function *)
Definition G1 :=
k_in <-$ {0, 1}^k;
[b, _] <-$2 A _ _
(fun (s : list (Bvector b * Bvector c)) m =>
randomFunc ({0, 1}^c) _ s (F k_in m)) nil;
ret b.
(* Step 2: from cAU, identical random function inputs imply identical messages.  So this is equivalent to a random function with the domain of messages. *)
Definition G2 :=
[b, _] <-$2 A _ _
(fun (s : list (list (Bvector b) * Bvector c)) m =>
randomFunc ({0, 1}^c) _ s m) nil;
ret b.
Definition G0_1 :=
[k_out, k_in] <-$2 (
key <-$ {0, 1}^(c + k);
ret splitVector c k key);
[b, _] <-$2 A _ _ (fun (_ : unit) m => ret (h k_out (F k_in m), tt)) tt;
ret b.

(* Specifications: *)
Theorem G0_1_equiv :
Pr[PRF_G_A ({0, 1}^(c + k)) hF _ A] == Pr[G0_1].