Theory Binomial_Heap

theory Binomial_Heap
imports Base_FDS Complex_Main Priority_Queue_Specs
(* Author: Peter Lammich
           Tobias Nipkow (tuning)
*)

section ‹Binomial Heap›

theory Binomial_Heap
imports
  Base_FDS
  Complex_Main
  Priority_Queue_Specs
begin

text ‹
  We formalize the binomial heap presentation from Okasaki's book.
  We show the functional correctness and complexity of all operations.

  The presentation is engineered for simplicity, and most
  proofs are straightforward and automatic.
›

subsection ‹Binomial Tree and Heap Datatype›

datatype 'a tree = Node (rank: nat) (root: 'a) (children: "'a tree list")

type_synonym 'a heap = "'a tree list"

subsubsection ‹Multiset of elements›

fun mset_tree :: "'a::linorder tree ⇒ 'a multiset" where
  "mset_tree (Node _ a c) = {#a#} + (∑t∈#mset c. mset_tree t)"

definition mset_heap :: "'a::linorder heap ⇒ 'a multiset" where
  "mset_heap c = (∑t∈#mset c. mset_tree t)"

lemma mset_tree_simp_alt[simp]:
  "mset_tree (Node r a c) = {#a#} + mset_heap c"
  unfolding mset_heap_def by auto
declare mset_tree.simps[simp del]

lemma mset_tree_nonempty[simp]: "mset_tree t ≠ {#}"
by (cases t) auto

lemma mset_heap_Nil[simp]:
  "mset_heap [] = {#}"
by (auto simp: mset_heap_def)

lemma mset_heap_Cons[simp]: "mset_heap (t#ts) = mset_tree t + mset_heap ts"
by (auto simp: mset_heap_def)

lemma mset_heap_empty_iff[simp]: "mset_heap ts = {#} ⟷ ts=[]"
by (auto simp: mset_heap_def)

lemma root_in_mset[simp]: "root t ∈# mset_tree t"
by (cases t) auto

lemma mset_heap_rev_eq[simp]: "mset_heap (rev ts) = mset_heap ts"
by (auto simp: mset_heap_def)

subsubsection ‹Invariants›

text ‹Binomial invariant›
fun invar_btree :: "'a::linorder tree ⇒ bool" where
"invar_btree (Node r x ts) ⟷
   (∀t∈set ts. invar_btree t) ∧ map rank ts = rev [0..<r]"

definition invar_bheap :: "'a::linorder heap ⇒ bool" where
"invar_bheap ts
  ⟷ (∀t∈set ts. invar_btree t) ∧ (sorted_wrt (<) (map rank ts))"

text ‹Ordering (heap) invariant›
fun invar_otree :: "'a::linorder tree ⇒ bool" where
"invar_otree (Node _ x ts) ⟷ (∀t∈set ts. invar_otree t ∧ x ≤ root t)"

definition invar_oheap :: "'a::linorder heap ⇒ bool" where
"invar_oheap ts ⟷ (∀t∈set ts. invar_otree t)"

definition invar :: "'a::linorder heap ⇒ bool" where
"invar ts ⟷ invar_bheap ts ∧ invar_oheap ts"

text ‹The children of a node are a valid heap›
lemma invar_oheap_children:
  "invar_otree (Node r v ts) ⟹ invar_oheap (rev ts)"
by (auto simp: invar_oheap_def)

lemma invar_bheap_children:
  "invar_btree (Node r v ts) ⟹ invar_bheap (rev ts)"
by (auto simp: invar_bheap_def rev_map[symmetric])


subsection ‹Operations and Their Functional Correctness›

subsubsection ‹‹link››

context
includes pattern_aliases
begin

fun link :: "('a::linorder) tree ⇒ 'a tree ⇒ 'a tree" where
  "link (Node r x1 ts1 =: t1) (Node r' x2 ts2 =: t2) =
    (if x1≤x2 then Node (r+1) x1 (t2#ts1) else Node (r+1) x2 (t1#ts2))"

end

lemma invar_btree_link:
  assumes "invar_btree t1"
  assumes "invar_btree t2"
  assumes "rank t1 = rank t2"
  shows "invar_btree (link t1 t2)"
using assms
by (cases "(t1, t2)" rule: link.cases) simp

lemma invar_link_otree:
  assumes "invar_otree t1"
  assumes "invar_otree t2"
  shows "invar_otree (link t1 t2)"
using assms
by (cases "(t1, t2)" rule: link.cases) auto

lemma rank_link[simp]: "rank (link t1 t2) = rank t1 + 1"
by (cases "(t1, t2)" rule: link.cases) simp

lemma mset_link[simp]: "mset_tree (link t1 t2) = mset_tree t1 + mset_tree t2"
by (cases "(t1, t2)" rule: link.cases) simp

subsubsection ‹‹ins_tree››

fun ins_tree :: "'a::linorder tree ⇒ 'a heap ⇒ 'a heap" where
  "ins_tree t [] = [t]"
| "ins_tree t1 (t2#ts) =
  (if rank t1 < rank t2 then t1#t2#ts else ins_tree (link t1 t2) ts)"

lemma invar_bheap_Cons[simp]:
  "invar_bheap (t#ts)
  ⟷ invar_btree t ∧ invar_bheap ts ∧ (∀t'∈set ts. rank t < rank t')"
by (auto simp: invar_bheap_def)

lemma invar_btree_ins_tree:
  assumes "invar_btree t"
  assumes "invar_bheap ts"
  assumes "∀t'∈set ts. rank t ≤ rank t'"
  shows "invar_bheap (ins_tree t ts)"
using assms
by (induction t ts rule: ins_tree.induct) (auto simp: invar_btree_link less_eq_Suc_le[symmetric])

lemma invar_oheap_Cons[simp]:
  "invar_oheap (t#ts) ⟷ invar_otree t ∧ invar_oheap ts"
by (auto simp: invar_oheap_def)

lemma invar_oheap_ins_tree:
  assumes "invar_otree t"
  assumes "invar_oheap ts"
  shows "invar_oheap (ins_tree t ts)"
using assms
by (induction t ts rule: ins_tree.induct) (auto simp: invar_link_otree)

lemma mset_heap_ins_tree[simp]:
  "mset_heap (ins_tree t ts) = mset_tree t + mset_heap ts"
by (induction t ts rule: ins_tree.induct) auto

lemma ins_tree_rank_bound:
  assumes "t' ∈ set (ins_tree t ts)"
  assumes "∀t'∈set ts. rank t0 < rank t'"
  assumes "rank t0 < rank t"
  shows "rank t0 < rank t'"
using assms
by (induction t ts rule: ins_tree.induct) (auto split: if_splits)

subsubsection ‹‹insert››

hide_const (open) insert

definition insert :: "'a::linorder ⇒ 'a heap ⇒ 'a heap" where
"insert x ts = ins_tree (Node 0 x []) ts"

lemma invar_insert[simp]: "invar t ⟹ invar (insert x t)"
by (auto intro!: invar_btree_ins_tree simp: invar_oheap_ins_tree insert_def invar_def)

lemma mset_heap_insert[simp]: "mset_heap (insert x t) = {#x#} + mset_heap t"
by(auto simp: insert_def)

subsubsection ‹‹merge››

fun merge :: "'a::linorder heap ⇒ 'a heap ⇒ 'a heap" where
  "merge ts1 [] = ts1"
| "merge [] ts2 = ts2"
| "merge (t1#ts1) (t2#ts2) = (
    if rank t1 < rank t2 then t1 # merge ts1 (t2#ts2) else
    if rank t2 < rank t1 then t2 # merge (t1#ts1) ts2
    else ins_tree (link t1 t2) (merge ts1 ts2)
  )"

lemma merge_simp2[simp]: "merge [] ts2 = ts2"
by (cases ts2) auto

lemma merge_rank_bound:
  assumes "t' ∈ set (merge ts1 ts2)"
  assumes "∀t'∈set ts1. rank t < rank t'"
  assumes "∀t'∈set ts2. rank t < rank t'"
  shows "rank t < rank t'"
using assms
by (induction ts1 ts2 arbitrary: t' rule: merge.induct)
   (auto split: if_splits simp: ins_tree_rank_bound)

lemma invar_bheap_merge:
  assumes "invar_bheap ts1"
  assumes "invar_bheap ts2"
  shows "invar_bheap (merge ts1 ts2)"
  using assms
proof (induction ts1 ts2 rule: merge.induct)
  case (3 t1 ts1 t2 ts2)

  from "3.prems" have [simp]: "invar_btree t1" "invar_btree t2"
    by auto

  consider (LT) "rank t1 < rank t2"
         | (GT) "rank t1 > rank t2"
         | (EQ) "rank t1 = rank t2"
    using antisym_conv3 by blast
  then show ?case proof cases
    case LT
    then show ?thesis using 3
      by (force elim!: merge_rank_bound)
  next
    case GT
    then show ?thesis using 3
      by (force elim!: merge_rank_bound)
  next
    case [simp]: EQ

    from "3.IH"(3) "3.prems" have [simp]: "invar_bheap (merge ts1 ts2)"
      by auto

    have "rank t2 < rank t'" if "t' ∈ set (merge ts1 ts2)" for t'
      using that
      apply (rule merge_rank_bound)
      using "3.prems" by auto
    with EQ show ?thesis
      by (auto simp: Suc_le_eq invar_btree_ins_tree invar_btree_link)
  qed
qed simp_all

lemma invar_oheap_merge:
  assumes "invar_oheap ts1"
  assumes "invar_oheap ts2"
  shows "invar_oheap (merge ts1 ts2)"
using assms
by (induction ts1 ts2 rule: merge.induct)
   (auto simp: invar_oheap_ins_tree invar_link_otree)

lemma invar_merge[simp]: "⟦ invar ts1; invar ts2 ⟧ ⟹ invar (merge ts1 ts2)"
by (auto simp: invar_def invar_bheap_merge invar_oheap_merge)

lemma mset_heap_merge[simp]:
  "mset_heap (merge ts1 ts2) = mset_heap ts1 + mset_heap ts2"
by (induction ts1 ts2 rule: merge.induct) auto

subsubsection ‹‹get_min››

fun get_min :: "'a::linorder heap ⇒ 'a" where
  "get_min [t] = root t"
| "get_min (t#ts) = min (root t) (get_min ts)"

lemma invar_otree_root_min:
  assumes "invar_otree t"
  assumes "x ∈# mset_tree t"
  shows "root t ≤ x"
using assms
by (induction t arbitrary: x rule: mset_tree.induct) (fastforce simp: mset_heap_def)

lemma get_min_mset_aux:
  assumes "ts≠[]"
  assumes "invar_oheap ts"
  assumes "x ∈# mset_heap ts"
  shows "get_min ts ≤ x"
  using assms
apply (induction ts arbitrary: x rule: get_min.induct)
apply (auto
      simp: invar_otree_root_min min_def intro: order_trans;
      meson linear order_trans invar_otree_root_min
      )+
done

lemma get_min_mset:
  assumes "ts≠[]"
  assumes "invar ts"
  assumes "x ∈# mset_heap ts"
  shows "get_min ts ≤ x"
using assms by (auto simp: invar_def get_min_mset_aux)

lemma get_min_member:
  "ts≠[] ⟹ get_min ts ∈# mset_heap ts"
by (induction ts rule: get_min.induct) (auto simp: min_def)

lemma get_min:
  assumes "mset_heap ts ≠ {#}"
  assumes "invar ts"
  shows "get_min ts = Min_mset (mset_heap ts)"
using assms get_min_member get_min_mset
by (auto simp: eq_Min_iff)

subsubsection ‹‹get_min_rest››

fun get_min_rest :: "'a::linorder heap ⇒ 'a tree × 'a heap" where
  "get_min_rest [t] = (t,[])"
| "get_min_rest (t#ts) = (let (t',ts') = get_min_rest ts
                     in if root t ≤ root t' then (t,ts) else (t',t#ts'))"

lemma get_min_rest_get_min_same_root:
  assumes "ts≠[]"
  assumes "get_min_rest ts = (t',ts')"
  shows "root t' = get_min ts"
using assms
by (induction ts arbitrary: t' ts' rule: get_min.induct) (auto simp: min_def split: prod.splits)

lemma mset_get_min_rest:
  assumes "get_min_rest ts = (t',ts')"
  assumes "ts≠[]"
  shows "mset ts = {#t'#} + mset ts'"
using assms
by (induction ts arbitrary: t' ts' rule: get_min.induct) (auto split: prod.splits if_splits)

lemma set_get_min_rest:
  assumes "get_min_rest ts = (t', ts')"
  assumes "ts≠[]"
  shows "set ts = Set.insert t' (set ts')"
using mset_get_min_rest[OF assms, THEN arg_cong[where f=set_mset]]
by auto

lemma invar_bheap_get_min_rest:
  assumes "get_min_rest ts = (t',ts')"
  assumes "ts≠[]"
  assumes "invar_bheap ts"
  shows "invar_btree t'" and "invar_bheap ts'"
proof -
  have "invar_btree t' ∧ invar_bheap ts'"
    using assms
    proof (induction ts arbitrary: t' ts' rule: get_min.induct)
      case (2 t v va)
      then show ?case
        apply (clarsimp split: prod.splits if_splits)
        apply (drule set_get_min_rest; fastforce)
        done
    qed auto
  thus "invar_btree t'" and "invar_bheap ts'" by auto
qed

lemma invar_oheap_get_min_rest:
  assumes "get_min_rest ts = (t',ts')"
  assumes "ts≠[]"
  assumes "invar_oheap ts"
  shows "invar_otree t'" and "invar_oheap ts'"
using assms
by (induction ts arbitrary: t' ts' rule: get_min.induct) (auto split: prod.splits if_splits)

subsubsection ‹‹del_min››

definition del_min :: "'a::linorder heap ⇒ 'a::linorder heap" where
"del_min ts = (case get_min_rest ts of
   (Node r x ts1, ts2) ⇒ merge (rev ts1) ts2)"

lemma invar_del_min[simp]:
  assumes "ts ≠ []"
  assumes "invar ts"
  shows "invar (del_min ts)"
using assms
unfolding invar_def del_min_def
by (auto
      split: prod.split tree.split
      intro!: invar_bheap_merge invar_oheap_merge
      dest: invar_bheap_get_min_rest invar_oheap_get_min_rest
      intro!: invar_oheap_children invar_bheap_children
    )

lemma mset_heap_del_min:
  assumes "ts ≠ []"
  shows "mset_heap ts = mset_heap (del_min ts) + {# get_min ts #}"
using assms
unfolding del_min_def
apply (clarsimp split: tree.split prod.split)
apply (frule (1) get_min_rest_get_min_same_root)
apply (frule (1) mset_get_min_rest)
apply (auto simp: mset_heap_def)
done


subsubsection ‹Instantiating the Priority Queue Locale›

text ‹Last step of functional correctness proof: combine all the above lemmas
to show that binomial heaps satisfy the specification of priority queues with merge.›

interpretation binheap: Priority_Queue_Merge
  where empty = "[]" and is_empty = "(=) []" and insert = insert
  and get_min = get_min and del_min = del_min and merge = merge
  and invar = invar and mset = mset_heap
proof (unfold_locales, goal_cases)
  case 1 thus ?case by simp
next
  case 2 thus ?case by auto
next
  case 3 thus ?case by auto
next
  case (4 q)
  thus ?case using mset_heap_del_min[of q] get_min[OF _ ‹invar q›]
    by (auto simp: union_single_eq_diff)
next
  case (5 q) thus ?case using get_min[of q] by auto
next
  case 6 thus ?case by (auto simp add: invar_def invar_bheap_def invar_oheap_def)
next
  case 7 thus ?case by simp
next
  case 8 thus ?case by simp
next
  case 9 thus ?case by simp
next
  case 10 thus ?case by simp
qed


subsection ‹Complexity›

text ‹The size of a binomial tree is determined by its rank›
lemma size_mset_btree:
  assumes "invar_btree t"
  shows "size (mset_tree t) = 2^rank t"
  using assms
proof (induction t)
  case (Node r v ts)
  hence IH: "size (mset_tree t) = 2^rank t" if "t ∈ set ts" for t
    using that by auto

  from Node have COMPL: "map rank ts = rev [0..<r]" by auto

  have "size (mset_heap ts) = (∑t←ts. size (mset_tree t))"
    by (induction ts) auto
  also have "… = (∑t←ts. 2^rank t)" using IH
    by (auto cong: map_cong)
  also have "… = (∑r←map rank ts. 2^r)"
    by (induction ts) auto
  also have "… = (∑i∈{0..<r}. 2^i)"
    unfolding COMPL
    by (auto simp: rev_map[symmetric] interv_sum_list_conv_sum_set_nat)
  also have "… = 2^r - 1"
    by (induction r) auto
  finally show ?case
    by (simp)
qed

text ‹The length of a binomial heap is bounded by the number of its elements›
lemma size_mset_bheap:
  assumes "invar_bheap ts"
  shows "2^length ts ≤ size (mset_heap ts) + 1"
proof -
  from ‹invar_bheap ts› have
    ASC: "sorted_wrt (<) (map rank ts)" and
    TINV: "∀t∈set ts. invar_btree t"
    unfolding invar_bheap_def by auto

  have "(2::nat)^length ts = (∑i∈{0..<length ts}. 2^i) + 1"
    by (simp add: sum_power2)
  also have "… ≤ (∑t←ts. 2^rank t) + 1"
    using sorted_wrt_less_sum_mono_lowerbound[OF _ ASC, of "(^) (2::nat)"]
    using power_increasing[where a="2::nat"]
    by (auto simp: o_def)
  also have "… = (∑t←ts. size (mset_tree t)) + 1" using TINV
    by (auto cong: map_cong simp: size_mset_btree)
  also have "… = size (mset_heap ts) + 1"
    unfolding mset_heap_def by (induction ts) auto
  finally show ?thesis .
qed

subsubsection ‹Timing Functions›

text ‹
  We define timing functions for each operation, and provide
  estimations of their complexity.
›
definition t_link :: "'a::linorder tree ⇒ 'a tree ⇒ nat" where
[simp]: "t_link _ _ = 1"

fun t_ins_tree :: "'a::linorder tree ⇒ 'a heap ⇒ nat" where
  "t_ins_tree t [] = 1"
| "t_ins_tree t1 (t2 # rest) = (
    (if rank t1 < rank t2 then 1
     else t_link t1 t2 + t_ins_tree (link t1 t2) rest)
  )"

definition t_insert :: "'a::linorder ⇒ 'a heap ⇒ nat" where
"t_insert x ts = t_ins_tree (Node 0 x []) ts"

lemma t_ins_tree_simple_bound: "t_ins_tree t ts ≤ length ts + 1"
by (induction t ts rule: t_ins_tree.induct) auto

subsubsection ‹‹t_insert››

lemma t_insert_bound:
  assumes "invar ts"
  shows "t_insert x ts ≤ log 2 (size (mset_heap ts) + 1) + 1"
proof -

  have 1: "t_insert x ts ≤ length ts + 1"
    unfolding t_insert_def by (rule t_ins_tree_simple_bound)
  also have "… ≤ log 2 (2 * (size (mset_heap ts) + 1))"
  proof -
    from size_mset_bheap[of ts] assms
    have "2 ^ length ts ≤ size (mset_heap ts) + 1"
      unfolding invar_def by auto
    hence "2 ^ (length ts + 1) ≤ 2 * (size (mset_heap ts) + 1)" by auto
    thus ?thesis using le_log2_of_power by blast
  qed
  finally show ?thesis
    by (simp only: log_mult of_nat_mult) auto
qed

subsubsection ‹‹t_merge››

fun t_merge :: "'a::linorder heap ⇒ 'a heap ⇒ nat" where
  "t_merge ts1 [] = 1"
| "t_merge [] ts2 = 1"
| "t_merge (t1#ts1) (t2#ts2) = 1 + (
    if rank t1 < rank t2 then t_merge ts1 (t2#ts2)
    else if rank t2 < rank t1 then t_merge (t1#ts1) ts2
    else t_ins_tree (link t1 t2) (merge ts1 ts2) + t_merge ts1 ts2
  )"

text ‹A crucial idea is to estimate the time in correlation with the
  result length, as each carry reduces the length of the result.›

lemma t_ins_tree_length:
  "t_ins_tree t ts + length (ins_tree t ts) = 2 + length ts"
by (induction t ts rule: ins_tree.induct) auto

lemma t_merge_length:
  "length (merge ts1 ts2) + t_merge ts1 ts2 ≤ 2 * (length ts1 + length ts2) + 1"
by (induction ts1 ts2 rule: t_merge.induct)
   (auto simp: t_ins_tree_length algebra_simps)

text ‹Finally, we get the desired logarithmic bound›
lemma t_merge_bound_aux:
  fixes ts1 ts2
  defines "n1 ≡ size (mset_heap ts1)"
  defines "n2 ≡ size (mset_heap ts2)"
  assumes BINVARS: "invar_bheap ts1" "invar_bheap ts2"
  shows "t_merge ts1 ts2 ≤ 4*log 2 (n1 + n2 + 1) + 2"
proof -
  define n where "n = n1 + n2"

  from t_merge_length[of ts1 ts2]
  have "t_merge ts1 ts2 ≤ 2 * (length ts1 + length ts2) + 1" by auto
  hence "(2::nat)^t_merge ts1 ts2 ≤ 2^(2 * (length ts1 + length ts2) + 1)"
    by (rule power_increasing) auto
  also have "… = 2*(2^length ts1)2*(2^length ts2)2"
    by (auto simp: algebra_simps power_add power_mult)
  also note BINVARS(1)[THEN size_mset_bheap]
  also note BINVARS(2)[THEN size_mset_bheap]
  finally have "2 ^ t_merge ts1 ts2 ≤ 2 * (n1 + 1)2 * (n2 + 1)2"
    by (auto simp: power2_nat_le_eq_le n1_def n2_def)
  from le_log2_of_power[OF this] have "t_merge ts1 ts2 ≤ log 2 …"
    by simp
  also have "… = log 2 2 + 2*log 2 (n1 + 1) + 2*log 2 (n2 + 1)"
    by (simp add: log_mult log_nat_power)
  also have "n2 ≤ n" by (auto simp: n_def)
  finally have "t_merge ts1 ts2 ≤ log 2 2 + 2*log 2 (n1 + 1) + 2*log 2 (n + 1)"
    by auto
  also have "n1 ≤ n" by (auto simp: n_def)
  finally have "t_merge ts1 ts2 ≤ log 2 2 + 4*log 2 (n + 1)"
    by auto
  also have "log 2 2 ≤ 2" by auto
  finally have "t_merge ts1 ts2 ≤ 4*log 2 (n + 1) + 2" by auto
  thus ?thesis unfolding n_def by (auto simp: algebra_simps)
qed

lemma t_merge_bound:
  fixes ts1 ts2
  defines "n1 ≡ size (mset_heap ts1)"
  defines "n2 ≡ size (mset_heap ts2)"
  assumes "invar ts1" "invar ts2"
  shows "t_merge ts1 ts2 ≤ 4*log 2 (n1 + n2 + 1) + 2"
using assms t_merge_bound_aux unfolding invar_def by blast

subsubsection ‹‹t_get_min››

fun t_get_min :: "'a::linorder heap ⇒ nat" where
  "t_get_min [t] = 1"
| "t_get_min (t#ts) = 1 + t_get_min ts"

lemma t_get_min_estimate: "ts≠[] ⟹ t_get_min ts = length ts"
by (induction ts rule: t_get_min.induct) auto

lemma t_get_min_bound:
  assumes "invar ts"
  assumes "ts≠[]"
  shows "t_get_min ts ≤ log 2 (size (mset_heap ts) + 1)"
proof -
  have 1: "t_get_min ts = length ts" using assms t_get_min_estimate by auto
  also have "… ≤ log 2 (size (mset_heap ts) + 1)"
  proof -
    from size_mset_bheap[of ts] assms have "2 ^ length ts ≤ size (mset_heap ts) + 1"
      unfolding invar_def by auto
    thus ?thesis using le_log2_of_power by blast
  qed
  finally show ?thesis by auto
qed

subsubsection ‹‹t_del_min››

fun t_get_min_rest :: "'a::linorder heap ⇒ nat" where
  "t_get_min_rest [t] = 1"
| "t_get_min_rest (t#ts) = 1 + t_get_min_rest ts"

lemma t_get_min_rest_estimate: "ts≠[] ⟹ t_get_min_rest ts = length ts"
  by (induction ts rule: t_get_min_rest.induct) auto

lemma t_get_min_rest_bound_aux:
  assumes "invar_bheap ts"
  assumes "ts≠[]"
  shows "t_get_min_rest ts ≤ log 2 (size (mset_heap ts) + 1)"
proof -
  have 1: "t_get_min_rest ts = length ts" using assms t_get_min_rest_estimate by auto
  also have "… ≤ log 2 (size (mset_heap ts) + 1)"
  proof -
    from size_mset_bheap[of ts] assms have "2 ^ length ts ≤ size (mset_heap ts) + 1"
      by auto
    thus ?thesis using le_log2_of_power by blast
  qed
  finally show ?thesis by auto
qed

lemma t_get_min_rest_bound:
  assumes "invar ts"
  assumes "ts≠[]"
  shows "t_get_min_rest ts ≤ log 2 (size (mset_heap ts) + 1)"
using assms t_get_min_rest_bound_aux unfolding invar_def by blast

text‹Note that although the definition of function @{const rev} has quadratic complexity,
it can and is implemented (via suitable code lemmas) as a linear time function.
Thus the following definition is justified:›

definition "t_rev xs = length xs + 1"

definition t_del_min :: "'a::linorder heap ⇒ nat" where
  "t_del_min ts = t_get_min_rest ts + (case get_min_rest ts of (Node _ x ts1, ts2)
                    ⇒ t_rev ts1 + t_merge (rev ts1) ts2
  )"

lemma t_rev_ts1_bound_aux:
  fixes ts
  defines "n ≡ size (mset_heap ts)"
  assumes BINVAR: "invar_bheap (rev ts)"
  shows "t_rev ts ≤ 1 + log 2 (n+1)"
proof -
  have "t_rev ts = length ts + 1" by (auto simp: t_rev_def)
  hence "2^t_rev ts = 2*2^length ts" by auto
  also have "… ≤ 2*n+2" using size_mset_bheap[OF BINVAR] by (auto simp: n_def)
  finally have "2 ^ t_rev ts ≤ 2 * n + 2" .
  from le_log2_of_power[OF this] have "t_rev ts ≤ log 2 (2 * (n + 1))"
    by auto
  also have "… = 1 + log 2 (n+1)"
    by (simp only: of_nat_mult log_mult) auto
  finally show ?thesis by (auto simp: algebra_simps)
qed

lemma t_del_min_bound_aux:
  fixes ts
  defines "n ≡ size (mset_heap ts)"
  assumes BINVAR: "invar_bheap ts"
  assumes "ts≠[]"
  shows "t_del_min ts ≤ 6 * log 2 (n+1) + 3"
proof -
  obtain r x ts1 ts2 where GM: "get_min_rest ts = (Node r x ts1, ts2)"
    by (metis surj_pair tree.exhaust_sel)

  note BINVAR' = invar_bheap_get_min_rest[OF GM ‹ts≠[]› BINVAR]
  hence BINVAR1: "invar_bheap (rev ts1)" by (blast intro: invar_bheap_children)

  define n1 where "n1 = size (mset_heap ts1)"
  define n2 where "n2 = size (mset_heap ts2)"

  have t_rev_ts1_bound: "t_rev ts1 ≤ 1 + log 2 (n+1)"
  proof -
    note t_rev_ts1_bound_aux[OF BINVAR1, simplified, folded n1_def]
    also have "n1 ≤ n"
      unfolding n1_def n_def
      using mset_get_min_rest[OF GM ‹ts≠[]›]
      by (auto simp: mset_heap_def)
    finally show ?thesis by (auto simp: algebra_simps)
  qed

  have "t_del_min ts = t_get_min_rest ts + t_rev ts1 + t_merge (rev ts1) ts2"
    unfolding t_del_min_def by (simp add: GM)
  also have "… ≤ log 2 (n+1) + t_rev ts1 + t_merge (rev ts1) ts2"
    using t_get_min_rest_bound_aux[OF assms(2-)] by (auto simp: n_def)
  also have "… ≤ 2*log 2 (n+1) + t_merge (rev ts1) ts2 + 1"
    using t_rev_ts1_bound by auto
  also have "… ≤ 2*log 2 (n+1) + 4 * log 2 (n1 + n2 + 1) + 3"
    using t_merge_bound_aux[OF ‹invar_bheap (rev ts1)› ‹invar_bheap ts2]
    by (auto simp: n1_def n2_def algebra_simps)
  also have "n1 + n2 ≤ n"
    unfolding n1_def n2_def n_def
    using mset_get_min_rest[OF GM ‹ts≠[]›]
    by (auto simp: mset_heap_def)
  finally have "t_del_min ts ≤ 6 * log 2 (n+1) + 3"
    by auto
  thus ?thesis by (simp add: algebra_simps)
qed

lemma t_del_min_bound:
  fixes ts
  defines "n ≡ size (mset_heap ts)"
  assumes "invar ts"
  assumes "ts≠[]"
  shows "t_del_min ts ≤ 6 * log 2 (n+1) + 3"
using assms t_del_min_bound_aux unfolding invar_def by blast

end