(* 
  A predicate that associates an initial height, a list of
  height, a cover and a tree
*)
Inductive height_pred : nat -> list nat -> list (btree A) -> btree A -> Prop :=
  | height_pred_nil :
      forall (n : nat) (t : btree A), height_pred n (n :: nil) (t :: nil) t
  | height_pred_node :
      forall (n : nat) (ln1 ln2 : list nat) (t1 t2 : btree A)
        (l1 l2 : list (btree A)),
      height_pred (S n) ln1 l1 t1 ->
      height_pred (S n) ln2 l2 t2 ->
      height_pred n (ln1 ++ ln2) (l1 ++ l2) (node t1 t2).(* 
  A predicate that associates an initial height, a list of
  height, a cover and a tree
*)
Inductive height_pred : nat -> list nat -> list (btree A) -> btree A -> Prop :=
  | height_pred_nil :
      forall (n : nat) (t : btree A), height_pred n (n :: nil) (t :: nil) t
  | height_pred_node :
      forall (n : nat) (ln1 ln2 : list nat) (t1 t2 : btree A)
        (l1 l2 : list (btree A)),
      height_pred (S n) ln1 l1 t1 ->
      height_pred (S n) ln2 l2 t2 ->
      height_pred n (ln1 ++ ln2) (l1 ++ l2) (node t1 t2).(* 
  A predicate that associates an initial height, a list of
  height, a cover and a tree
*)
Inductive height_pred : nat -> list nat -> list (btree A) -> btree A -> Prop :=
  | height_pred_nil :
      forall (n : nat) (t : btree A), height_pred n (n :: nil) (t :: nil) t
  | height_pred_node :
      forall (n : nat) (ln1 ln2 : list nat) (t1 t2 : btree A)
        (l1 l2 : list (btree A)),
      height_pred (S n) ln1 l1 t1 ->
      height_pred (S n) ln2 l2 t2 ->
      height_pred n (ln1 ++ ln2) (l1 ++ l2) (node t1 t2).

(* Specifications: *)
Theorem height_pred_ordered_cover : forall (n : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> ordered_cover l t.
Theorem height_pred_not_nil1 : forall (n : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> ln <> nil.
Theorem height_pred_not_nil2 : forall (n : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> l <> nil.
Theorem height_pred_length : forall (n : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> length ln = length l.
Theorem height_pred_weight : forall (n : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> n * sum_leaves f t + weight_tree f t = prod2list f ln l.
Theorem ordered_cover_height_pred : forall (n : nat) (t : btree A) (l : list (btree A)), ordered_cover l t -> exists ln : list nat, height_pred n ln l t.
Theorem height_pred_larger : forall (n n1 : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> In n1 ln -> n <= n1.
Theorem height_pred_larger_strict : forall (n n1 : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> In n1 ln -> n < n1 \/ ln = n :: nil /\ l = t :: nil.
Theorem height_pred_larger_ex : forall (n : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> exists n1 : _, In n1 ln /\ n <= n1.
Lemma height_pred_disj_larger_aux : forall (n : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> forall ln1 ln2 a, ln = ln1 ++ a :: ln2 -> (forall n1 : nat, In n1 ln1 -> n1 < a) -> (forall n1 : nat, In n1 ln2 -> n1 <= a) -> (exists ln3 : _, ln2 = a :: ln3) \/ ln = n :: nil /\ l = t :: nil.
Theorem height_pred_disj_larger : forall (n a : nat) (ln1 ln2 : list nat) (t : btree A) (l : list (btree A)), height_pred n (ln1 ++ a :: ln2) l t -> (forall n1 : nat, In n1 ln1 -> n1 < a) -> (forall n1 : nat, In n1 ln2 -> n1 <= a) -> (exists ln3 : _, ln2 = a :: ln3) \/ (ln1 = nil /\ a = n /\ ln2 = nil) /\ l = t :: nil.
Lemma height_pred_disj_larger2_aux : forall (n : nat) (ln : list nat) (t : btree A) (l : list (btree A)), height_pred n ln l t -> forall ln1 ln2 a, ln = ln1 ++ a :: ln2 -> (exists n1 : _, In n1 ln1 /\ a <= n1) \/ (exists n1 : _, In n1 ln2 /\ a <= n1) \/ ln = n :: nil /\ l = t :: nil.
Theorem height_pred_disj_larger2 : forall (n a : nat) (ln1 ln2 : list nat) (t : btree A) (l : list (btree A)), height_pred n (ln1 ++ a :: ln2) l t -> (exists n1 : _, In n1 ln1 /\ a <= n1) \/ (exists n1 : _, In n1 ln2 /\ a <= n1) \/ (ln1 = nil /\ a = n /\ ln2 = nil) /\ l = t :: nil.
Theorem height_pred_shrink : forall (n a b : nat) (ln1 ln2 : list nat) (t t1 t2 : btree A) (l1 l2 : list (btree A)), height_pred n (ln1 ++ a :: b :: ln2) (l1 ++ t1 :: t2 :: l2) t -> (forall n1 : nat, In n1 ln1 -> n1 < a) -> (forall n1 : nat, In n1 (b :: ln2) -> n1 <= a) -> length ln1 = length l1 -> height_pred n (ln1 ++ pred a :: ln2) (l1 ++ node t1 t2 :: l2) t.
Theorem height_pred_compute_code : forall (n : nat) (t : btree A), height_pred n (map (fun x => length (snd x) + n) (compute_code t)) (map (fun x => leaf (fst x)) (compute_code t)) t.
Theorem weight_tree_compute : forall (m : list A) t, distinct_leaves t -> (forall a : A, f a = number_of_occurrences eqA_dec a m) -> length (encode eqA_dec (compute_code t) m) = weight_tree f t.