(*  Use of this source code is governed by the license described	* * in the LICENSE file at the root of the source tree.		*)
(* An axiomatization of expected polynomial time as a type class.  *)
Definition procedure_family(A : nat -> Type) := forall n, A n.
Definition efficiency_predicate := forall (A : nat -> Type), procedure_family A -> Prop.
(* We can abstract over arrow.  This would allow us to make sure that there is a model of the axioms below. *)
(* Is there a language with a "function space" and "efficiency" that satisfies these semantics.  Make this language! *)
(* Exercise: write the description of this class in a paper. *)
Class expected_poly_time_predicate(efficient : efficiency_predicate) := {
expected_poly_time_compose :
forall (A B C : nat -> Type)(f1 : forall n, A n -> B n)(f2 : forall n, A n -> B n -> C n),
efficient _ f1 ->
efficient _ f2 ->
efficient _ (fun n (a : A n) => f2 n a (f1 n a));
expected_poly_time_compose_0 :
forall (A B : nat -> Type)(f1 : forall n, A n)(f2 : forall n, A n -> B n),
efficient _ f1 ->
efficient _ f2 ->
efficient _ (fun n => f2 n (f1 n));
expected_poly_time_ident :
forall (A : nat -> Type),
efficient _ (fun n (a : A n) => a);
expected_poly_time_const :
forall (A B : nat -> Type)(b : forall n, B n),
efficient _ b ->
efficient _ (fun (n : nat)(_ : A n) => (b n));
expected_poly_time_closure_const :
forall (A : Type)(a : A)(B : nat -> Type)(f : forall n, A -> B n),
efficient _ f ->
efficient _ (fun (n : nat) => @f n a);
expected_poly_time_nil :
forall (A : nat -> Type),
efficient _ (fun n => @nil (A n));
expected_poly_time_cons :
forall (A : nat -> Type),
efficient _ (fun n (a : A n) ls => cons a ls);
expected_poly_time_pair :
forall (A : nat -> Type)(B : nat -> Type),
efficient _ (fun n => @pair (A n) (B n));
expected_poly_time_ret :
forall (A : nat -> Set)(eqd : forall n, eq_dec (A n)),
efficient _ (fun n (a : A n) => Ret (eqd n) a);
expected_poly_time_rnd :
forall f,
polynomial f ->
efficient _ (fun n => ({0, 1}^(f n)));
expected_poly_time_bind :
forall (A B : nat -> Set),
efficient _ (fun n => @Bind (A n) (B n));
expected_poly_time_vec_hd :
forall (A : nat -> Type) z,
efficient _ (fun n (v : Vector.t (A n) (S z)) => Vector.hd v);
expected_poly_time_pair_arg :
forall (A B C: nat -> Type)(f : forall (n : nat), A n -> B n -> C n),
efficient _ (fun (n : nat)(p : (A n * B n)) => f n (fst p) (snd p)) ->
efficient _ f;
expected_poly_time_unpair_arg :
forall (A B C: nat -> Type)(f : forall (n : nat), (A n * B n) -> C n),
efficient _ (fun (n : nat)(a : A n)(b : B n) => f n (a, b)) ->
efficient _ f;
(* The let rules are structured differently -- I should see if I can make them consistent *)
expected_poly_time_let_3_f :
forall (A B C D E: nat -> Type)(f1 : forall n, (A n) -> (B n * C n * D n))(f2 : forall n, (A n) -> B n -> C n -> D n -> E n),
efficient _ f1 ->
efficient _ f2 ->
efficient _ (fun n (a : A n) => [x1, x2, x3] <-3 (f1 n a); f2 n a x1 x2 x3);
expected_poly_time_let_2_f :
forall (A B C E: nat -> Type)(f1 : forall n, (A n) -> (B n * C n))(f2 : forall n, (A n) -> B n -> C n -> E n),
efficient _ f1 ->
efficient _ f2 ->
efficient _ (fun n (a : A n) => [x1, x2] <-2 (f1 n a); f2 n a x1 x2);
expected_poly_time_let_f :
forall (A : nat -> Type)(B : nat -> Set)(C : nat -> Type)(f1 : forall n, (A n) -> B n)(f2 : forall n, (A n) -> B n -> C n),
efficient _ f1 ->
efficient _ f2 ->
efficient _ (fun n (a : A n) => x <- (f1 n a); f2 n a x);
expected_poly_time_fst :
forall (A B : nat -> Type),
efficient _ (fun n (a : A n * B n) => fst a);
expected_poly_time_snd :
forall (A B : nat -> Type),
efficient _ (fun n (a : A n * B n) => snd a);
expected_poly_time_if :
forall (A : nat -> Type),
efficient _ (fun n (b : bool)(a1 a2 : A n) => if b then a1 else a2);
expected_poly_time_bvxor :
efficient _ BVxor;
expected_poly_time_eqb_bool :
efficient _ (fun n (x y : bool) => eqb x y)
}.
Section expected_poly_time_theory.
Context `{ept : expected_poly_time_predicate}.

Theorem expected_poly_time_compose_simp :
forall (A B C : nat -> Type)(f1 : forall n, A n -> B n)(f2 : forall n, B n -> C n),
efficient _ f1 ->
efficient _ f2 ->
efficient _ (fun n (a : A n) => f2 n (f1 n a)).