(* We will use a tree to represent an approximation at a certain level.  These tree-based approximations are equivalent to the standard approximations above, but they make it easier to prove some properties. *)
Inductive DistApproxTree(A : Set) :=
| dat_leaf : option A -> DistApproxTree A
| dat_internal : DistApproxTree A -> DistApproxTree A -> DistApproxTree A.(* We will use a tree to represent an approximation at a certain level.  These tree-based approximations are equivalent to the standard approximations above, but they make it easier to prove some properties. *)
Inductive DistApproxTree(A : Set) :=
| dat_leaf : option A -> DistApproxTree A
| dat_internal : DistApproxTree A -> DistApproxTree A -> DistApproxTree A.(* We will use a tree to represent an approximation at a certain level.  These tree-based approximations are equivalent to the standard approximations above, but they make it easier to prove some properties. *)
Inductive DistApproxTree(A : Set) :=
| dat_leaf : option A -> DistApproxTree A
| dat_internal : DistApproxTree A -> DistApproxTree A -> DistApproxTree A.(* dat_correct is an inductive predicate that lets us conclude that some tree produces a correct approximation for some computation. *)
Inductive dat_correct_h(A : Set)(c : Comp A)(s : Blist) : nat -> DistApproxTree A -> Prop :=
    | dat_correct_h_leaf_Some :
      forall a n, 
        evalDet c s (ca_done a) ->
        dat_correct_h c s n (dat_leaf (Some a))
    | dat_correct_h_leaf_None :
        (forall a, ~ evalDet c s (ca_done a)) ->
        dat_correct_h c s 0 (dat_leaf None)
    | dat_correct_h_internal :
      forall t1 t2 n, 
        (forall a, ~ evalDet c s (ca_done a)) ->
        dat_correct_h c (s ++ (true :: nil)) n t1 ->
        dat_correct_h c (s ++ (false :: nil)) n t2 ->
        dat_correct_h c s (S n) (dat_internal t1 t2).(* dat_correct is an inductive predicate that lets us conclude that some tree produces a correct approximation for some computation. *)
Inductive dat_correct_h(A : Set)(c : Comp A)(s : Blist) : nat -> DistApproxTree A -> Prop :=
    | dat_correct_h_leaf_Some :
      forall a n, 
        evalDet c s (ca_done a) ->
        dat_correct_h c s n (dat_leaf (Some a))
    | dat_correct_h_leaf_None :
        (forall a, ~ evalDet c s (ca_done a)) ->
        dat_correct_h c s 0 (dat_leaf None)
    | dat_correct_h_internal :
      forall t1 t2 n, 
        (forall a, ~ evalDet c s (ca_done a)) ->
        dat_correct_h c (s ++ (true :: nil)) n t1 ->
        dat_correct_h c (s ++ (false :: nil)) n t2 ->
        dat_correct_h c s (S n) (dat_internal t1 t2).(* dat_correct is an inductive predicate that lets us conclude that some tree produces a correct approximation for some computation. *)
Inductive dat_correct_h(A : Set)(c : Comp A)(s : Blist) : nat -> DistApproxTree A -> Prop :=
    | dat_correct_h_leaf_Some :
      forall a n, 
        evalDet c s (ca_done a) ->
        dat_correct_h c s n (dat_leaf (Some a))
    | dat_correct_h_leaf_None :
        (forall a, ~ evalDet c s (ca_done a)) ->
        dat_correct_h c s 0 (dat_leaf None)
    | dat_correct_h_internal :
      forall t1 t2 n, 
        (forall a, ~ evalDet c s (ca_done a)) ->
        dat_correct_h c (s ++ (true :: nil)) n t1 ->
        dat_correct_h c (s ++ (false :: nil)) n t2 ->
        dat_correct_h c s (S n) (dat_internal t1 t2).(* dat_correct is an inductive predicate that lets us conclude that some tree produces a correct approximation for some computation. *)
Inductive dat_correct_h(A : Set)(c : Comp A)(s : Blist) : nat -> DistApproxTree A -> Prop :=
    | dat_correct_h_leaf_Some :
      forall a n, 
        evalDet c s (ca_done a) ->
        dat_correct_h c s n (dat_leaf (Some a))
    | dat_correct_h_leaf_None :
        (forall a, ~ evalDet c s (ca_done a)) ->
        dat_correct_h c s 0 (dat_leaf None)
    | dat_correct_h_internal :
      forall t1 t2 n, 
        (forall a, ~ evalDet c s (ca_done a)) ->
        dat_correct_h c (s ++ (true :: nil)) n t1 ->
        dat_correct_h c (s ++ (false :: nil)) n t2 ->
        dat_correct_h c s (S n) (dat_internal t1 t2).Definition dat_correct(A : Set)(c : Comp A)(n : nat) :=
    dat_correct_h c nil n.

(* Specifications: *)
Lemma dat_correct_func : forall (A : Set)(c : Comp A) ls n t1, dat_correct_h c ls n t1 -> forall t2, dat_correct_h c ls n t2 -> t1 = t2.