Require Coq.Bool.Bool.
(* assume that the tree has one node and 2 leaves, then the path is 1 bit long *)
(** / [ ID | PAYLOAD ] \ | [ ID | PAYLOAD ] | \ [ ID | PAYLOAD ] / /               \ /                 \ /                   \ / [ ID | PAYLOAD ] \   / [ ID | PAYLOAD ] \  ← | [ ID | PAYLOAD ] |   | [ ID | PAYLOAD ] |  ← A BUCKET = N BLOCKS (example: N=3) \ [ ID | PAYLOAD ] /   \ [ ID | PAYLOAD ] /  ← ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ **)
(* Z = 3 N = 3 addr space = Z * N = 3 * 3 path_len = 1 bit |acc_list| = Len = 5 Len_AP = 1 * 5 = 5 bits *)
(*TODO : define 2 random variables from a coin_flip, show that the probability of the conjuction of them is true is 1/4 *)
(* p <- coin_flip q <- coin_flip Pr[ p /\ q ] = 1/4 *)
(* Pr(A /\ B) = Pr(A) * Pr(B) *)
Definition coin_flip_two :=
p <- coin_flip ;;
q <- coin_flip ;;
mreturn (p, q).
Definition eval_dist {X} (mu : dist X) (f : X -> bool) : Q :=
sum_dist (filter_dist (dist_pmf mu) f).
Goal eval_dist coin_flip_two (fun '(p,q) => andb p q) = 1 / 4.
reflexivity.
Qed.
(* mapM :: Monad m => (a -> m b) -> t a -> m (t b) *)
Definition mapM {A B M} `{Monad M} (f : A -> M B) (l : list (M A))
:list (M B) :=
(List.map
(fun s => mbind s f) l).
Definition distr_eqv {X} d d' : Prop :=
forall (E : X -> bool),
Qeq (eval_dist d E) (eval_dist d' E).
(* sequence :: (Traversable t, Monad m) => t (m a) -> m (t a) *)
Fixpoint sequence {A M} `{Monad M} (l : list (M A)) : M (list A) :=
match l with
| [] => mreturn []
| h :: t =>
x <- h ;;
xs <- sequence t ;;
mreturn (x :: xs)
end.
(* Definition acc_list_1 {C : Config} (arg_list : list (block_id * operation)) (s : state) : *)
(*   list (dist path). *)
(*   pose (List.map (fun '(bid, op) => access bid op) arg_list). *)
(*   refine (List.map _ l). *)
(*   intros prm. *)
(*   specialize (prm s). *)
(*   pose (map fst prm). *)
(*   pose (map fst d). *)
(*   exact d0. *)
(* Defined. *)
Definition acc_dist_list {C : Config}
(arg_list : list (block_id * operation)) : Poram (list path) :=
let l := List.map (fun '(bid, op) => access bid op) arg_list in
let p := sequence l in
monad_map (List.map fst) p.
Definition get_dist_list_bool {C : Config}
(arg_list : list (block_id * operation))(s : state) : dist (list bool) :=
monad_map (@List.concat bool) (monad_map fst ((acc_dist_list arg_list) s)).

(* Specifications: *)
Lemma state_plift_map :
forall {X Y} (Pre Post: state -> Prop) (P : Y -> Prop)
(f : X -> Y) (m : Poram X),
state_plift Pre Post (fun x => P (f x)) m ->
state_plift Pre Post P (monad_map f m).
Lemma sequence_length :
forall {S A M} `{PredLift M} (Pre : S -> Prop)(l : list (StateT S M A)) ,
let n := List.length l in
state_plift Pre Pre (fun l' => List.length l' = n) (sequence l).