Inductive datMap(A B : Set)(f : A -> DistApproxTree B -> Prop) : DistApproxTree A -> DistApproxTree B  -> Prop :=
| datMap_leaf_None :
    datMap f (dat_leaf None) (dat_leaf None)
| datMap_leaf_Some : 
  forall a t,
    f a t ->
    datMap f (dat_leaf (Some a)) t
| datMap_internal : 
  forall t1a t2a t1b t2b,
    datMap f t1a t1b ->
    datMap f t2a t2b ->
    datMap f (dat_internal t1a t2a) (dat_internal t1b t2b).Inductive datMap(A B : Set)(f : A -> DistApproxTree B -> Prop) : DistApproxTree A -> DistApproxTree B  -> Prop :=
| datMap_leaf_None :
    datMap f (dat_leaf None) (dat_leaf None)
| datMap_leaf_Some : 
  forall a t,
    f a t ->
    datMap f (dat_leaf (Some a)) t
| datMap_internal : 
  forall t1a t2a t1b t2b,
    datMap f t1a t1b ->
    datMap f t2a t2b ->
    datMap f (dat_internal t1a t2a) (dat_internal t1b t2b).Inductive datMap(A B : Set)(f : A -> DistApproxTree B -> Prop) : DistApproxTree A -> DistApproxTree B  -> Prop :=
| datMap_leaf_None :
    datMap f (dat_leaf None) (dat_leaf None)
| datMap_leaf_Some : 
  forall a t,
    f a t ->
    datMap f (dat_leaf (Some a)) t
| datMap_internal : 
  forall t1a t2a t1b t2b,
    datMap f t1a t1b ->
    datMap f t2a t2b ->
    datMap f (dat_internal t1a t2a) (dat_internal t1b t2b).Inductive datMap(A B : Set)(f : A -> DistApproxTree B -> Prop) : DistApproxTree A -> DistApproxTree B  -> Prop :=
| datMap_leaf_None :
    datMap f (dat_leaf None) (dat_leaf None)
| datMap_leaf_Some : 
  forall a t,
    f a t ->
    datMap f (dat_leaf (Some a)) t
| datMap_internal : 
  forall t1a t2a t1b t2b,
    datMap f t1a t1b ->
    datMap f t2a t2b ->
    datMap f (dat_internal t1a t2a) (dat_internal t1b t2b).(* a variant of datMap that builds a tree of a maximum depth *)
Inductive datMap_depth(A B : Set)(f : nat -> A -> DistApproxTree B -> Prop) : nat -> DistApproxTree A -> DistApproxTree B -> Prop :=
| datMap_depth_leaf_None :
  forall n, 
    datMap_depth f n (dat_leaf None) (dat_leaf None)
| datMap_depth_leaf_Some :
  forall n a t,
    f n a t ->
    datMap_depth f n (dat_leaf (Some a)) t
| datMap_depth_internal :
  forall n t1a t2a t1b t2b,
    datMap_depth f n t1a t1b ->
    datMap_depth f n t2a t2b ->
    datMap_depth f (S n) (dat_internal t1a t2a) (dat_internal t1b t2b).(* a variant of datMap that builds a tree of a maximum depth *)
Inductive datMap_depth(A B : Set)(f : nat -> A -> DistApproxTree B -> Prop) : nat -> DistApproxTree A -> DistApproxTree B -> Prop :=
| datMap_depth_leaf_None :
  forall n, 
    datMap_depth f n (dat_leaf None) (dat_leaf None)
| datMap_depth_leaf_Some :
  forall n a t,
    f n a t ->
    datMap_depth f n (dat_leaf (Some a)) t
| datMap_depth_internal :
  forall n t1a t2a t1b t2b,
    datMap_depth f n t1a t1b ->
    datMap_depth f n t2a t2b ->
    datMap_depth f (S n) (dat_internal t1a t2a) (dat_internal t1b t2b).(* a variant of datMap that builds a tree of a maximum depth *)
Inductive datMap_depth(A B : Set)(f : nat -> A -> DistApproxTree B -> Prop) : nat -> DistApproxTree A -> DistApproxTree B -> Prop :=
| datMap_depth_leaf_None :
  forall n, 
    datMap_depth f n (dat_leaf None) (dat_leaf None)
| datMap_depth_leaf_Some :
  forall n a t,
    f n a t ->
    datMap_depth f n (dat_leaf (Some a)) t
| datMap_depth_internal :
  forall n t1a t2a t1b t2b,
    datMap_depth f n t1a t1b ->
    datMap_depth f n t2a t2b ->
    datMap_depth f (S n) (dat_internal t1a t2a) (dat_internal t1b t2b).(* a variant of datMap that builds a tree of a maximum depth *)
Inductive datMap_depth(A B : Set)(f : nat -> A -> DistApproxTree B -> Prop) : nat -> DistApproxTree A -> DistApproxTree B -> Prop :=
| datMap_depth_leaf_None :
  forall n, 
    datMap_depth f n (dat_leaf None) (dat_leaf None)
| datMap_depth_leaf_Some :
  forall n a t,
    f n a t ->
    datMap_depth f n (dat_leaf (Some a)) t
| datMap_depth_internal :
  forall n t1a t2a t1b t2b,
    datMap_depth f n t1a t1b ->
    datMap_depth f n t2a t2b ->
    datMap_depth f (S n) (dat_internal t1a t2a) (dat_internal t1b t2b).Definition dat_correct_bind(A B : Set)(c1 : Comp B)(c2 : B -> Comp A)(n : nat)(t : DistApproxTree A) :=
  exists tb, dat_correct c1 n tb /\
    (datMap_depth (fun n' b ta => (dat_correct (c2 b) n' ta)) n tb t).Definition dat_correct_bind2(A B : Set)(c1 : Comp B)(c2 : B -> Comp A)(n : nat)(t : DistApproxTree A) :=
  exists tb, dat_correct c1 n tb /\
    (datMap (fun b ta => (dat_correct (c2 b) n ta)) tb t).

(* Specifications: *)
Lemma lowDistApprox_le_1 : forall (A : Set)(c : Comp A) a n r, lowDistApprox c a n r -> r <= 1.
Lemma in_flattenPair_inv : forall (A B : Set)(ls : list (A * list B)) a b, In (a, b) (flattenPair ls) -> exists lsb, In b lsb /\ In (a , lsb) ls.
Lemma in_getSupport_bind_cp_fst : forall (A B : Set)(c1 : Comp B)(c2 : B -> Comp A) a0, In a0 (getSupport_bind_cp c1 c2) -> In (fst a0) (getSupport c1).
Lemma lowDistApprox_bind_evalDist_limit : forall (A B :Set)(c1 : Comp B)(c2 : B -> Comp A) a, well_formed_comp c1 -> (forall b, In b (getSupport c1) -> well_formed_comp (c2 b)) -> (forall (a : B), rat_inf_limit (lowDistApprox c1 a) (evalDist c1 a)) -> (forall (b : B) (a : A), In b (getSupport c1) -> rat_inf_limit (lowDistApprox (c2 b) a) (evalDist (c2 b) a)) -> rat_inf_limit (lowDistApprox_bind c1 c2 a) (evalDist (Bind c1 c2) a).
Lemma datMap_left_total : forall (A B : Set)(eqdb : eq_dec A)(t : DistApproxTree A)(f : A -> DistApproxTree B -> Prop), (forall a, In a (getTreeSupport eqdb t) -> exists b, f a b) -> exists t', datMap f t t'.
Lemma dat_exists_bind2 : forall (A B : Set)(c1 : Comp B)(c2 : B -> Comp A) n, well_formed_comp c1 -> (forall b, In b (getSupport c1) -> well_formed_comp (c2 b)) -> exists t, dat_correct_bind2 c1 c2 n t.