Inductive datRepeat_depth (A : Set)(f : nat -> DistApproxTree A -> Prop)(P : A -> bool) : nat -> nat -> DistApproxTree A -> DistApproxTree A -> Prop :=
  | datRepeat_depth_leaf_None : forall n depth,
    datRepeat_depth f P n depth (dat_leaf None) (dat_leaf None)
  | datRepeat_depth_leaf_Some_yes : forall a n depth,
    P a = true ->
    datRepeat_depth f P n depth (dat_leaf (Some a)) (dat_leaf (Some a))
  | datRepeat_depth_leaf_Some_no : forall a depth,
    P a = false ->
    datRepeat_depth f P O depth (dat_leaf (Some a)) (dat_leaf None)
  | datRepeat_depth_leaf_Some_repeat : forall a t t' n depth,
    P a = false ->
    f depth t ->
    datRepeat_depth f P n depth t t' ->
    datRepeat_depth f P (S n) depth (dat_leaf (Some a)) t'
  | datRepeat_depth_internal : forall n depth t1 t2 t1' t2',
    datRepeat_depth f P n depth t1 t1' ->
    datRepeat_depth f P n depth t2 t2' ->
    datRepeat_depth f P n (S depth) (dat_internal t1 t2) (dat_internal t1' t2').Inductive datRepeat_depth (A : Set)(f : nat -> DistApproxTree A -> Prop)(P : A -> bool) : nat -> nat -> DistApproxTree A -> DistApproxTree A -> Prop :=
  | datRepeat_depth_leaf_None : forall n depth,
    datRepeat_depth f P n depth (dat_leaf None) (dat_leaf None)
  | datRepeat_depth_leaf_Some_yes : forall a n depth,
    P a = true ->
    datRepeat_depth f P n depth (dat_leaf (Some a)) (dat_leaf (Some a))
  | datRepeat_depth_leaf_Some_no : forall a depth,
    P a = false ->
    datRepeat_depth f P O depth (dat_leaf (Some a)) (dat_leaf None)
  | datRepeat_depth_leaf_Some_repeat : forall a t t' n depth,
    P a = false ->
    f depth t ->
    datRepeat_depth f P n depth t t' ->
    datRepeat_depth f P (S n) depth (dat_leaf (Some a)) t'
  | datRepeat_depth_internal : forall n depth t1 t2 t1' t2',
    datRepeat_depth f P n depth t1 t1' ->
    datRepeat_depth f P n depth t2 t2' ->
    datRepeat_depth f P n (S depth) (dat_internal t1 t2) (dat_internal t1' t2').Inductive datRepeat_depth (A : Set)(f : nat -> DistApproxTree A -> Prop)(P : A -> bool) : nat -> nat -> DistApproxTree A -> DistApproxTree A -> Prop :=
  | datRepeat_depth_leaf_None : forall n depth,
    datRepeat_depth f P n depth (dat_leaf None) (dat_leaf None)
  | datRepeat_depth_leaf_Some_yes : forall a n depth,
    P a = true ->
    datRepeat_depth f P n depth (dat_leaf (Some a)) (dat_leaf (Some a))
  | datRepeat_depth_leaf_Some_no : forall a depth,
    P a = false ->
    datRepeat_depth f P O depth (dat_leaf (Some a)) (dat_leaf None)
  | datRepeat_depth_leaf_Some_repeat : forall a t t' n depth,
    P a = false ->
    f depth t ->
    datRepeat_depth f P n depth t t' ->
    datRepeat_depth f P (S n) depth (dat_leaf (Some a)) t'
  | datRepeat_depth_internal : forall n depth t1 t2 t1' t2',
    datRepeat_depth f P n depth t1 t1' ->
    datRepeat_depth f P n depth t2 t2' ->
    datRepeat_depth f P n (S depth) (dat_internal t1 t2) (dat_internal t1' t2').Inductive datRepeat_depth (A : Set)(f : nat -> DistApproxTree A -> Prop)(P : A -> bool) : nat -> nat -> DistApproxTree A -> DistApproxTree A -> Prop :=
  | datRepeat_depth_leaf_None : forall n depth,
    datRepeat_depth f P n depth (dat_leaf None) (dat_leaf None)
  | datRepeat_depth_leaf_Some_yes : forall a n depth,
    P a = true ->
    datRepeat_depth f P n depth (dat_leaf (Some a)) (dat_leaf (Some a))
  | datRepeat_depth_leaf_Some_no : forall a depth,
    P a = false ->
    datRepeat_depth f P O depth (dat_leaf (Some a)) (dat_leaf None)
  | datRepeat_depth_leaf_Some_repeat : forall a t t' n depth,
    P a = false ->
    f depth t ->
    datRepeat_depth f P n depth t t' ->
    datRepeat_depth f P (S n) depth (dat_leaf (Some a)) t'
  | datRepeat_depth_internal : forall n depth t1 t2 t1' t2',
    datRepeat_depth f P n depth t1 t1' ->
    datRepeat_depth f P n depth t2 t2' ->
    datRepeat_depth f P n (S depth) (dat_internal t1 t2) (dat_internal t1' t2').Inductive datRepeat_depth (A : Set)(f : nat -> DistApproxTree A -> Prop)(P : A -> bool) : nat -> nat -> DistApproxTree A -> DistApproxTree A -> Prop :=
  | datRepeat_depth_leaf_None : forall n depth,
    datRepeat_depth f P n depth (dat_leaf None) (dat_leaf None)
  | datRepeat_depth_leaf_Some_yes : forall a n depth,
    P a = true ->
    datRepeat_depth f P n depth (dat_leaf (Some a)) (dat_leaf (Some a))
  | datRepeat_depth_leaf_Some_no : forall a depth,
    P a = false ->
    datRepeat_depth f P O depth (dat_leaf (Some a)) (dat_leaf None)
  | datRepeat_depth_leaf_Some_repeat : forall a t t' n depth,
    P a = false ->
    f depth t ->
    datRepeat_depth f P n depth t t' ->
    datRepeat_depth f P (S n) depth (dat_leaf (Some a)) t'
  | datRepeat_depth_internal : forall n depth t1 t2 t1' t2',
    datRepeat_depth f P n depth t1 t1' ->
    datRepeat_depth f P n depth t2 t2' ->
    datRepeat_depth f P n (S depth) (dat_internal t1 t2) (dat_internal t1' t2').Inductive datRepeat_depth (A : Set)(f : nat -> DistApproxTree A -> Prop)(P : A -> bool) : nat -> nat -> DistApproxTree A -> DistApproxTree A -> Prop :=
  | datRepeat_depth_leaf_None : forall n depth,
    datRepeat_depth f P n depth (dat_leaf None) (dat_leaf None)
  | datRepeat_depth_leaf_Some_yes : forall a n depth,
    P a = true ->
    datRepeat_depth f P n depth (dat_leaf (Some a)) (dat_leaf (Some a))
  | datRepeat_depth_leaf_Some_no : forall a depth,
    P a = false ->
    datRepeat_depth f P O depth (dat_leaf (Some a)) (dat_leaf None)
  | datRepeat_depth_leaf_Some_repeat : forall a t t' n depth,
    P a = false ->
    f depth t ->
    datRepeat_depth f P n depth t t' ->
    datRepeat_depth f P (S n) depth (dat_leaf (Some a)) t'
  | datRepeat_depth_internal : forall n depth t1 t2 t1' t2',
    datRepeat_depth f P n depth t1 t1' ->
    datRepeat_depth f P n depth t2 t2' ->
    datRepeat_depth f P n (S depth) (dat_internal t1 t2) (dat_internal t1' t2').Definition dat_correct_repeat(A : Set) (c : Comp A)(P : A -> bool)(n : nat)(t : DistApproxTree A) :=
exists t1 : DistApproxTree A,
  dat_correct c n t1 /\
  datRepeat_depth (dat_correct c) P n n t1 t.

(* Specifications: *)
Lemma datRepeat_depth_0 : forall n2 n1 (A : Set)(f : nat -> DistApproxTree A -> Prop) P t1 t2, datRepeat_depth f P n1 O t1 t2 -> n2 >= n1 -> f O (dat_leaf None) -> datRepeat_depth f P n2 O t1 t2.
Lemma dat_correct_repeat_same_h : forall depth repeats (A : Set)(c : Comp A) P ls t a t1, dat_correct_h (Repeat c P) ls depth t -> well_formed_comp c -> In a (filter P (getSupport c)) -> (forall a' n' ls', evalDet_steps (cs_more c (firstn n' ls)) (cs_done a' ls') -> ls' = nil) -> dat_correct_h c ls depth t1 -> repeats >= depth -> datRepeat_depth (dat_correct_h c nil) P repeats depth t1 t.
Lemma dat_correct_repeat_same: forall n (A : Set) (t : DistApproxTree A) (c : Comp A) (P : A -> bool), well_formed_comp c -> (exists a, In a (filter P (getSupport c))) -> dat_correct (Repeat c P) n t -> dat_correct_repeat c P n t.
Lemma datRepeat_depth_better: forall (n2 : nat) (A : Set) (t2 t1 t1' : DistApproxTree A)(rel : nat -> DistApproxTree A -> Prop) reps depth (P : A -> bool), datRepeat_depth rel P reps depth t1 t1' -> forall n1 (t2' : DistApproxTree A), reps >= n2 -> datRepeat (rel n1) P n2 t2 t2' -> dat_better t1 t2 -> depth >= n1 * n2 + datDepth t2 -> (forall (n1 n2: nat) (t3 t4 : DistApproxTree A), rel n1 t3 -> rel n2 t4 -> n1 >= n2 -> dat_better t3 t4) -> (forall a, rel 0%nat (dat_leaf (Some a)) -> P a = true) -> (forall n t, rel n t -> datDepth t <= n)%nat -> dat_better t1' t2'.
Lemma dat_better_repeat_sqrt : forall (A : Set) (c : Comp A)(P : A -> bool)(n : nat) (t1 t2 : DistApproxTree A), (exists a, In a (filter P (getSupport c))) -> dat_correct_repeat c P n t1 -> dat_correct_repeat2 c P (Nat.sqrt (div2 n)) t2 -> well_formed_comp c -> dat_better t1 t2.
Lemma lowDistApprox_repeat_sqrt_le : forall n (A : Set)(c : Comp A)(P : A -> bool) a v1 v2, well_formed_comp c -> (exists a, In a (filter P (getSupport c))) -> lowDistApprox (Repeat c P) a n v1 -> lowDistApprox_repeat c P a (Nat.sqrt (div2 n)) v2 -> n > 1 -> v2 <= v1.
Lemma datRepeat_better_depth: forall (A : Set) n2 (t2 t1 t1' : DistApproxTree A)(rel : nat -> DistApproxTree A -> Prop) reps depth (P : A -> bool), forall n1 (t2' : DistApproxTree A), datRepeat (rel n1) P n2 t2 t2' -> datRepeat_depth rel P reps depth t1 t1' -> dat_better t2 t1 -> n1 >= depth -> n2 >= reps -> (forall (n1 n2: nat) (t3 t4 : DistApproxTree A), rel n1 t3 -> rel n2 t4 -> n1 >= n2 -> dat_better t3 t4) -> (forall a, rel 0%nat (dat_leaf (Some a)) -> P a = true) -> (forall n t, rel n t -> datDepth t <= n)%nat -> dat_better t2' t1'.
Lemma dat_repeat_better: forall (A : Set) n (c : Comp A) (P : A -> bool) (t1 t2 : DistApproxTree A), (exists a : A, In a (filter P (getSupport c))) -> dat_correct_repeat c P n t1 -> dat_correct_repeat2 c P n t2 -> dat_better t2 t1.
Lemma lowDistApprox_le_repeat: forall (n : nat) (A : Set) (c : Comp A) (P : A -> bool)(a : A) (v1 v2 : Rat), well_formed_comp c -> (exists a0 : A, In a0 (filter P (getSupport c))) -> lowDistApprox (Repeat c P) a n v1 -> lowDistApprox_repeat c P a n v2 -> n > O -> v1 <= v2.
Lemma evalDet_step_done_support_singleton : forall (A : Set)(c : Comp A) a s, evalDet_step c nil = (cs_done a s) -> getSupport c = a :: nil.
Lemma lowDistApprox_low : forall (A : Set)(c : Comp A), well_formed_comp c -> forall n a r, lowDistApprox c a n r -> r <= evalDist c a.
Lemma lowDistApprox_repeat_scale_limit : forall (A : Set)(c : Comp A)(P : A -> bool), well_formed_comp c -> (exists a, In a (filter P (getSupport c))) -> (forall a, rat_inf_limit (lowDistApprox c a) (evalDist c a)) -> rat_inf_limit (fun n : nat => sumList_rel (fun i : nat => expRat_rel (sumList_rel (fun a0 : A => lowDistApprox c a0 n) (filter (fun a0 : A => negb (P a0)) (getSupport c))) i) (getNats O n)) (ratInverse (sumList (filter P (getSupport c)) (evalDist c))).
Lemma lowDistApprox_repeat_limit : forall (A : Set)(c : Comp A)(P : A -> bool) a, well_formed_comp c -> (exists b, In b (filter P (getSupport c))) -> (forall a, rat_inf_limit (lowDistApprox c a) (evalDist c a)) -> rat_inf_limit (lowDistApprox_repeat c P a) (evalDist (Repeat c P) a).
Lemma lowDistApprox_limit_repeat : forall (A : Set)(c : Comp A)(P : A -> bool) a, well_formed_comp c -> (exists a, In a (filter P (getSupport c))) -> (forall a', rat_inf_limit (lowDistApprox c a') (evalDist c a')) -> rat_inf_limit (lowDistApprox (Repeat c P) a) (evalDist (Repeat c P) a).
Theorem evalDet_evalDist_equiv : forall (A : Set)(c : Comp A), well_formed_comp c -> forall a, rat_inf_limit (lowDistApprox c a) (evalDist c a).
Lemma evalDet_equiv_impl_lowDistApprox_equiv : forall (A : Set)(c1 c2 : Comp A), evalDet_equiv c1 c2 -> forall a n r, lowDistApprox c1 a n r <-> lowDistApprox c2 a n r.
Theorem det_eq_impl_dist_sem_eq : forall (A : Set)(c1 c2 : Comp A), well_formed_comp c1 -> well_formed_comp c2 -> (* perhaps we can conclude this from the equivalence? *) evalDet_equiv c1 c2 -> dist_sem_eq c1 c2.