diff --git a/src/HOL/Data_Structures/Binomial_Heap.thy b/src/HOL/Data_Structures/Binomial_Heap.thy --- a/src/HOL/Data_Structures/Binomial_Heap.thy +++ b/src/HOL/Data_Structures/Binomial_Heap.thy @@ -1,647 +1,642 @@ (* Author: Peter Lammich Tobias Nipkow (tuning) *) section \Binomial Heap\ theory Binomial_Heap imports "HOL-Library.Pattern_Aliases" 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 trees = "'a tree list" subsubsection \Multiset of elements\ fun mset_tree :: "'a::linorder tree \ 'a multiset" where "mset_tree (Node _ a ts) = {#a#} + (\t\#mset ts. mset_tree t)" definition mset_trees :: "'a::linorder trees \ 'a multiset" where "mset_trees ts = (\t\#mset ts. mset_tree t)" lemma mset_tree_simp_alt[simp]: "mset_tree (Node r a ts) = {#a#} + mset_trees ts" unfolding mset_trees_def by auto declare mset_tree.simps[simp del] lemma mset_tree_nonempty[simp]: "mset_tree t \ {#}" by (cases t) auto lemma mset_trees_Nil[simp]: "mset_trees [] = {#}" by (auto simp: mset_trees_def) lemma mset_trees_Cons[simp]: "mset_trees (t#ts) = mset_tree t + mset_trees ts" by (auto simp: mset_trees_def) lemma mset_trees_empty_iff[simp]: "mset_trees ts = {#} \ ts=[]" by (auto simp: mset_trees_def) lemma root_in_mset[simp]: "root t \# mset_tree t" by (cases t) auto lemma mset_trees_rev_eq[simp]: "mset_trees (rev ts) = mset_trees ts" by (auto simp: mset_trees_def) subsubsection \Invariants\ text \Binomial tree\ fun btree :: "'a::linorder tree \ bool" where "btree (Node r x ts) \ (\t\set ts. btree t) \ map rank ts = rev [0..Heap invariant\ fun heap :: "'a::linorder tree \ bool" where "heap (Node _ x ts) \ (\t\set ts. heap t \ x \ root t)" definition "bheap t \ btree t \ heap t" text \Binomial Heap invariant\ definition "invar ts \ (\t\set ts. bheap t) \ (sorted_wrt (<) (map rank ts))" text \The children of a node are a valid heap\ lemma invar_children: "bheap (Node r v ts) \ invar (rev ts)" by (auto simp: bheap_def invar_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 x\<^sub>1 ts\<^sub>1 =: t\<^sub>1) (Node r' x\<^sub>2 ts\<^sub>2 =: t\<^sub>2) = (if x\<^sub>1\x\<^sub>2 then Node (r+1) x\<^sub>1 (t\<^sub>2#ts\<^sub>1) else Node (r+1) x\<^sub>2 (t\<^sub>1#ts\<^sub>2))" end lemma invar_link: assumes "bheap t\<^sub>1" assumes "bheap t\<^sub>2" assumes "rank t\<^sub>1 = rank t\<^sub>2" shows "bheap (link t\<^sub>1 t\<^sub>2)" using assms unfolding bheap_def by (cases "(t\<^sub>1, t\<^sub>2)" rule: link.cases) auto lemma rank_link[simp]: "rank (link t\<^sub>1 t\<^sub>2) = rank t\<^sub>1 + 1" by (cases "(t\<^sub>1, t\<^sub>2)" rule: link.cases) simp lemma mset_link[simp]: "mset_tree (link t\<^sub>1 t\<^sub>2) = mset_tree t\<^sub>1 + mset_tree t\<^sub>2" by (cases "(t\<^sub>1, t\<^sub>2)" rule: link.cases) simp subsubsection \\ins_tree\\ fun ins_tree :: "'a::linorder tree \ 'a trees \ 'a trees" where "ins_tree t [] = [t]" | "ins_tree t\<^sub>1 (t\<^sub>2#ts) = (if rank t\<^sub>1 < rank t\<^sub>2 then t\<^sub>1#t\<^sub>2#ts else ins_tree (link t\<^sub>1 t\<^sub>2) ts)" lemma bheap0[simp]: "bheap (Node 0 x [])" unfolding bheap_def by auto lemma invar_Cons[simp]: "invar (t#ts) \ bheap t \ invar ts \ (\t'\set ts. rank t < rank t')" by (auto simp: invar_def) lemma invar_ins_tree: assumes "bheap t" assumes "invar ts" assumes "\t'\set ts. rank t \ rank t'" shows "invar (ins_tree t ts)" using assms by (induction t ts rule: ins_tree.induct) (auto simp: invar_link less_eq_Suc_le[symmetric]) lemma mset_trees_ins_tree[simp]: "mset_trees (ins_tree t ts) = mset_tree t + mset_trees 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 t\<^sub>0 < rank t'" assumes "rank t\<^sub>0 < rank t" shows "rank t\<^sub>0 < 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 trees \ 'a trees" where "insert x ts = ins_tree (Node 0 x []) ts" lemma invar_insert[simp]: "invar t \ invar (insert x t)" by (auto intro!: invar_ins_tree simp: insert_def) lemma mset_trees_insert[simp]: "mset_trees (insert x t) = {#x#} + mset_trees t" by(auto simp: insert_def) subsubsection \\merge\\ context includes pattern_aliases begin fun merge :: "'a::linorder trees \ 'a trees \ 'a trees" where "merge ts\<^sub>1 [] = ts\<^sub>1" | "merge [] ts\<^sub>2 = ts\<^sub>2" | "merge (t\<^sub>1#ts\<^sub>1 =: h\<^sub>1) (t\<^sub>2#ts\<^sub>2 =: h\<^sub>2) = ( if rank t\<^sub>1 < rank t\<^sub>2 then t\<^sub>1 # merge ts\<^sub>1 h\<^sub>2 else if rank t\<^sub>2 < rank t\<^sub>1 then t\<^sub>2 # merge h\<^sub>1 ts\<^sub>2 else ins_tree (link t\<^sub>1 t\<^sub>2) (merge ts\<^sub>1 ts\<^sub>2) )" end lemma merge_simp2[simp]: "merge [] ts\<^sub>2 = ts\<^sub>2" by (cases ts\<^sub>2) auto lemma merge_rank_bound: assumes "t' \ set (merge ts\<^sub>1 ts\<^sub>2)" assumes "\t\<^sub>1\set ts\<^sub>1. rank t < rank t\<^sub>1" assumes "\t\<^sub>2\set ts\<^sub>2. rank t < rank t\<^sub>2" shows "rank t < rank t'" using assms by (induction ts\<^sub>1 ts\<^sub>2 arbitrary: t' rule: merge.induct) (auto split: if_splits simp: ins_tree_rank_bound) lemma invar_merge[simp]: assumes "invar ts\<^sub>1" assumes "invar ts\<^sub>2" shows "invar (merge ts\<^sub>1 ts\<^sub>2)" using assms by (induction ts\<^sub>1 ts\<^sub>2 rule: merge.induct) (auto 0 3 simp: Suc_le_eq intro!: invar_ins_tree invar_link elim!: merge_rank_bound) text \Longer, more explicit proof of @{thm [source] invar_merge}, to illustrate the application of the @{thm [source] merge_rank_bound} lemma.\ lemma assumes "invar ts\<^sub>1" assumes "invar ts\<^sub>2" shows "invar (merge ts\<^sub>1 ts\<^sub>2)" using assms proof (induction ts\<^sub>1 ts\<^sub>2 rule: merge.induct) case (3 t\<^sub>1 ts\<^sub>1 t\<^sub>2 ts\<^sub>2) \ \Invariants of the parts can be shown automatically\ from "3.prems" have [simp]: "bheap t\<^sub>1" "bheap t\<^sub>2" (*"invar (merge (t\<^sub>1#ts\<^sub>1) ts\<^sub>2)" "invar (merge ts\<^sub>1 (t\<^sub>2#ts\<^sub>2))" "invar (merge ts\<^sub>1 ts\<^sub>2)"*) by auto \ \These are the three cases of the @{const merge} function\ consider (LT) "rank t\<^sub>1 < rank t\<^sub>2" | (GT) "rank t\<^sub>1 > rank t\<^sub>2" | (EQ) "rank t\<^sub>1 = rank t\<^sub>2" using antisym_conv3 by blast then show ?case proof cases case LT \ \@{const merge} takes the first tree from the left heap\ then have "merge (t\<^sub>1 # ts\<^sub>1) (t\<^sub>2 # ts\<^sub>2) = t\<^sub>1 # merge ts\<^sub>1 (t\<^sub>2 # ts\<^sub>2)" by simp also have "invar \" proof (simp, intro conjI) \ \Invariant follows from induction hypothesis\ show "invar (merge ts\<^sub>1 (t\<^sub>2 # ts\<^sub>2))" using LT "3.IH" "3.prems" by simp \ \It remains to show that \t\<^sub>1\ has smallest rank.\ show "\t'\set (merge ts\<^sub>1 (t\<^sub>2 # ts\<^sub>2)). rank t\<^sub>1 < rank t'" \ \Which is done by auxiliary lemma @{thm [source] merge_rank_bound}\ using LT "3.prems" by (force elim!: merge_rank_bound) qed finally show ?thesis . next \ \@{const merge} takes the first tree from the right heap\ case GT \ \The proof is anaologous to the \LT\ case\ then show ?thesis using "3.prems" "3.IH" by (force elim!: merge_rank_bound) next case [simp]: EQ \ \@{const merge} links both first trees, and inserts them into the merged remaining heaps\ have "merge (t\<^sub>1 # ts\<^sub>1) (t\<^sub>2 # ts\<^sub>2) = ins_tree (link t\<^sub>1 t\<^sub>2) (merge ts\<^sub>1 ts\<^sub>2)" by simp also have "invar \" proof (intro invar_ins_tree invar_link) \ \Invariant of merged remaining heaps follows by IH\ show "invar (merge ts\<^sub>1 ts\<^sub>2)" using EQ "3.prems" "3.IH" by auto \ \For insertion, we have to show that the rank of the linked tree is \\\ the ranks in the merged remaining heaps\ show "\t'\set (merge ts\<^sub>1 ts\<^sub>2). rank (link t\<^sub>1 t\<^sub>2) \ rank t'" proof - \ \Which is, again, done with the help of @{thm [source] merge_rank_bound}\ have "rank (link t\<^sub>1 t\<^sub>2) = Suc (rank t\<^sub>2)" by simp thus ?thesis using "3.prems" by (auto simp: Suc_le_eq elim!: merge_rank_bound) qed qed simp_all finally show ?thesis . qed qed auto lemma mset_trees_merge[simp]: "mset_trees (merge ts\<^sub>1 ts\<^sub>2) = mset_trees ts\<^sub>1 + mset_trees ts\<^sub>2" by (induction ts\<^sub>1 ts\<^sub>2 rule: merge.induct) auto subsubsection \\get_min\\ fun get_min :: "'a::linorder trees \ 'a" where "get_min [t] = root t" | "get_min (t#ts) = min (root t) (get_min ts)" lemma bheap_root_min: assumes "bheap t" assumes "x \# mset_tree t" shows "root t \ x" using assms unfolding bheap_def by (induction t arbitrary: x rule: mset_tree.induct) (fastforce simp: mset_trees_def) lemma get_min_mset: assumes "ts\[]" assumes "invar ts" assumes "x \# mset_trees ts" shows "get_min ts \ x" using assms apply (induction ts arbitrary: x rule: get_min.induct) apply (auto simp: bheap_root_min min_def intro: order_trans; meson linear order_trans bheap_root_min )+ done lemma get_min_member: "ts\[] \ get_min ts \# mset_trees ts" by (induction ts rule: get_min.induct) (auto simp: min_def) lemma get_min: assumes "mset_trees ts \ {#}" assumes "invar ts" shows "get_min ts = Min_mset (mset_trees 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 trees \ 'a tree \ 'a trees" 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_get_min_rest: assumes "get_min_rest ts = (t',ts')" assumes "ts\[]" assumes "invar ts" shows "bheap t'" and "invar ts'" proof - have "bheap t' \ invar 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 "bheap t'" and "invar ts'" by auto qed subsubsection \\del_min\\ definition del_min :: "'a::linorder trees \ 'a::linorder trees" where "del_min ts = (case get_min_rest ts of (Node r x ts\<^sub>1, ts\<^sub>2) \ merge (rev ts\<^sub>1) ts\<^sub>2)" lemma invar_del_min[simp]: assumes "ts \ []" assumes "invar ts" shows "invar (del_min ts)" using assms unfolding del_min_def by (auto split: prod.split tree.split intro!: invar_merge invar_children dest: invar_get_min_rest ) lemma mset_trees_del_min: assumes "ts \ []" shows "mset_trees ts = mset_trees (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_trees_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 bheaps: 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_trees 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_trees_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) 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 "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..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.. = 2^r - 1" by (induction r) auto finally show ?case by (simp) qed lemma size_mset_tree: assumes "bheap t" shows "size (mset_tree t) = 2^rank t" using assms unfolding bheap_def by (simp add: size_mset_btree) text \The length of a binomial heap is bounded by the number of its elements\ lemma size_mset_trees: assumes "invar ts" shows "length ts \ log 2 (size (mset_trees ts) + 1)" proof - from \invar ts\ have ASC: "sorted_wrt (<) (map rank ts)" and TINV: "\t\set ts. bheap t" unfolding invar_def by auto have "(2::nat)^length ts = (\i\{0.. = (\i\[0.. (\t\ts. 2^rank t)" (is "_ \ ?T") using sorted_wrt_less_idx[OF ASC] by(simp add: sum_list_mono2) also have "?T + 1 \ (\t\ts. size (mset_tree t)) + 1" using TINV by (auto cong: map_cong simp: size_mset_tree) also have "\ = size (mset_trees ts) + 1" unfolding mset_trees_def by (induction ts) auto finally have "2^length ts \ size (mset_trees ts) + 1" by simp then show ?thesis using le_log2_of_power by blast 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" +[simp]: "T_link _ _ = 0" -text \This function is non-canonical: we omitted a \+1\ in the \else\-part, - to keep the following analysis simpler and more to the point. -\ fun T_ins_tree :: "'a::linorder tree \ 'a trees \ nat" where "T_ins_tree t [] = 1" -| "T_ins_tree t\<^sub>1 (t\<^sub>2 # ts) = ( - (if rank t\<^sub>1 < rank t\<^sub>2 then 1 - else T_link t\<^sub>1 t\<^sub>2 + T_ins_tree (link t\<^sub>1 t\<^sub>2) ts) - )" +| "T_ins_tree t\<^sub>1 (t\<^sub>2 # ts) = 1 + + (if rank t\<^sub>1 < rank t\<^sub>2 then 0 + else T_link t\<^sub>1 t\<^sub>2 + T_ins_tree (link t\<^sub>1 t\<^sub>2) ts)" definition T_insert :: "'a::linorder \ 'a trees \ nat" where -"T_insert x ts = T_ins_tree (Node 0 x []) ts + 1" +"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_trees ts) + 1) + 2" + shows "T_insert x ts \ log 2 (size (mset_trees ts) + 1) + 1" proof - - have "real (T_insert x ts) \ real (length ts) + 2" - unfolding T_insert_def using T_ins_tree_simple_bound - using of_nat_mono by fastforce + have "real (T_insert x ts) \ real (length ts) + 1" + unfolding T_insert_def using T_ins_tree_simple_bound + by (metis of_nat_1 of_nat_add of_nat_mono) also note size_mset_trees[OF \invar ts\] finally show ?thesis by simp qed subsubsection \\T_merge\\ context includes pattern_aliases begin fun T_merge :: "'a::linorder trees \ 'a trees \ nat" where "T_merge ts\<^sub>1 [] = 1" | "T_merge [] ts\<^sub>2 = 1" | "T_merge (t\<^sub>1#ts\<^sub>1 =: h\<^sub>1) (t\<^sub>2#ts\<^sub>2 =: h\<^sub>2) = 1 + ( if rank t\<^sub>1 < rank t\<^sub>2 then T_merge ts\<^sub>1 h\<^sub>2 else if rank t\<^sub>2 < rank t\<^sub>1 then T_merge h\<^sub>1 ts\<^sub>2 else T_ins_tree (link t\<^sub>1 t\<^sub>2) (merge ts\<^sub>1 ts\<^sub>2) + T_merge ts\<^sub>1 ts\<^sub>2 )" end 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: "T_merge ts\<^sub>1 ts\<^sub>2 + length (merge ts\<^sub>1 ts\<^sub>2) \ 2 * (length ts\<^sub>1 + length ts\<^sub>2) + 1" by (induction ts\<^sub>1 ts\<^sub>2 rule: T_merge.induct) (auto simp: T_ins_tree_length algebra_simps) text \Finally, we get the desired logarithmic bound\ lemma T_merge_bound: fixes ts\<^sub>1 ts\<^sub>2 defines "n\<^sub>1 \ size (mset_trees ts\<^sub>1)" defines "n\<^sub>2 \ size (mset_trees ts\<^sub>2)" assumes "invar ts\<^sub>1" "invar ts\<^sub>2" shows "T_merge ts\<^sub>1 ts\<^sub>2 \ 4*log 2 (n\<^sub>1 + n\<^sub>2 + 1) + 1" proof - note n_defs = assms(1,2) have "T_merge ts\<^sub>1 ts\<^sub>2 \ 2 * real (length ts\<^sub>1) + 2 * real (length ts\<^sub>2) + 1" using T_merge_length[of ts\<^sub>1 ts\<^sub>2] by simp also note size_mset_trees[OF \invar ts\<^sub>1\] also note size_mset_trees[OF \invar ts\<^sub>2\] finally have "T_merge ts\<^sub>1 ts\<^sub>2 \ 2 * log 2 (n\<^sub>1 + 1) + 2 * log 2 (n\<^sub>2 + 1) + 1" unfolding n_defs by (simp add: algebra_simps) also have "log 2 (n\<^sub>1 + 1) \ log 2 (n\<^sub>1 + n\<^sub>2 + 1)" unfolding n_defs by (simp add: algebra_simps) also have "log 2 (n\<^sub>2 + 1) \ log 2 (n\<^sub>1 + n\<^sub>2 + 1)" unfolding n_defs by (simp add: algebra_simps) finally show ?thesis by (simp add: algebra_simps) qed subsubsection \\T_get_min\\ fun T_get_min :: "'a::linorder trees \ 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_trees ts) + 1)" proof - have 1: "T_get_min ts = length ts" using assms T_get_min_estimate by auto also note size_mset_trees[OF \invar ts\] finally show ?thesis . qed subsubsection \\T_del_min\\ fun T_get_min_rest :: "'a::linorder trees \ 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: assumes "invar ts" assumes "ts\[]" shows "T_get_min_rest ts \ log 2 (size (mset_trees ts) + 1)" proof - have 1: "T_get_min_rest ts = length ts" using assms T_get_min_rest_estimate by auto also note size_mset_trees[OF \invar ts\] finally show ?thesis . qed 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 trees \ nat" where "T_del_min ts = T_get_min_rest ts + (case get_min_rest ts of (Node _ x ts\<^sub>1, ts\<^sub>2) - \ T_rev ts\<^sub>1 + T_merge (rev ts\<^sub>1) ts\<^sub>2 - ) + 1" + \ T_rev ts\<^sub>1 + T_merge (rev ts\<^sub>1) ts\<^sub>2)" lemma T_del_min_bound: fixes ts defines "n \ size (mset_trees ts)" assumes "invar ts" and "ts\[]" - shows "T_del_min ts \ 6 * log 2 (n+1) + 3" + shows "T_del_min ts \ 6 * log 2 (n+1) + 2" proof - obtain r x ts\<^sub>1 ts\<^sub>2 where GM: "get_min_rest ts = (Node r x ts\<^sub>1, ts\<^sub>2)" by (metis surj_pair tree.exhaust_sel) have I1: "invar (rev ts\<^sub>1)" and I2: "invar ts\<^sub>2" using invar_get_min_rest[OF GM \ts\[]\ \invar ts\] invar_children by auto define n\<^sub>1 where "n\<^sub>1 = size (mset_trees ts\<^sub>1)" define n\<^sub>2 where "n\<^sub>2 = size (mset_trees ts\<^sub>2)" have "n\<^sub>1 \ n" "n\<^sub>1 + n\<^sub>2 \ n" unfolding n_def n\<^sub>1_def n\<^sub>2_def using mset_get_min_rest[OF GM \ts\[]\] by (auto simp: mset_trees_def) - have "T_del_min ts = real (T_get_min_rest ts) + real (T_rev ts\<^sub>1) + real (T_merge (rev ts\<^sub>1) ts\<^sub>2) + 1" + have "T_del_min ts = real (T_get_min_rest ts) + real (T_rev ts\<^sub>1) + real (T_merge (rev ts\<^sub>1) ts\<^sub>2)" unfolding T_del_min_def GM by simp also have "T_get_min_rest ts \ log 2 (n+1)" using T_get_min_rest_bound[OF \invar ts\ \ts\[]\] unfolding n_def by simp also have "T_rev ts\<^sub>1 \ 1 + log 2 (n\<^sub>1 + 1)" unfolding T_rev_def n\<^sub>1_def using size_mset_trees[OF I1] by simp also have "T_merge (rev ts\<^sub>1) ts\<^sub>2 \ 4*log 2 (n\<^sub>1 + n\<^sub>2 + 1) + 1" unfolding n\<^sub>1_def n\<^sub>2_def using T_merge_bound[OF I1 I2] by (simp add: algebra_simps) - finally have "T_del_min ts \ log 2 (n+1) + log 2 (n\<^sub>1 + 1) + 4*log 2 (real (n\<^sub>1 + n\<^sub>2) + 1) + 3" + finally have "T_del_min ts \ log 2 (n+1) + log 2 (n\<^sub>1 + 1) + 4*log 2 (real (n\<^sub>1 + n\<^sub>2) + 1) + 2" by (simp add: algebra_simps) also note \n\<^sub>1 + n\<^sub>2 \ n\ also note \n\<^sub>1 \ n\ finally show ?thesis by (simp add: algebra_simps) qed end diff --git a/src/HOL/Data_Structures/Queue_2Lists.thy b/src/HOL/Data_Structures/Queue_2Lists.thy --- a/src/HOL/Data_Structures/Queue_2Lists.thy +++ b/src/HOL/Data_Structures/Queue_2Lists.thy @@ -1,88 +1,82 @@ (* Author: Tobias Nipkow *) section \Queue Implementation via 2 Lists\ theory Queue_2Lists imports Queue_Spec Reverse begin text \Definitions:\ type_synonym 'a queue = "'a list \ 'a list" fun norm :: "'a queue \ 'a queue" where "norm (fs,rs) = (if fs = [] then (itrev rs [], []) else (fs,rs))" fun enq :: "'a \ 'a queue \ 'a queue" where "enq a (fs,rs) = norm(fs, a # rs)" fun deq :: "'a queue \ 'a queue" where "deq (fs,rs) = (if fs = [] then (fs,rs) else norm(tl fs,rs))" fun first :: "'a queue \ 'a" where "first (a # fs,rs) = a" fun is_empty :: "'a queue \ bool" where "is_empty (fs,rs) = (fs = [])" fun list :: "'a queue \ 'a list" where "list (fs,rs) = fs @ rev rs" fun invar :: "'a queue \ bool" where "invar (fs,rs) = (fs = [] \ rs = [])" text \Implementation correctness:\ interpretation Queue where empty = "([],[])" and enq = enq and deq = deq and first = first and is_empty = is_empty and list = list and invar = invar proof (standard, goal_cases) case 1 show ?case by (simp) next case (2 q) thus ?case by(cases q) (simp) next case (3 q) thus ?case by(cases q) (simp add: itrev_Nil) next case (4 q) thus ?case by(cases q) (auto simp: neq_Nil_conv) next case (5 q) thus ?case by(cases q) (auto) next case 6 show ?case by(simp) next case (7 q) thus ?case by(cases q) (simp) next case (8 q) thus ?case by(cases q) (simp) qed text \Running times:\ fun T_norm :: "'a queue \ nat" where -"T_norm (fs,rs) = (if fs = [] then T_itrev rs [] else 0) + 1" +"T_norm (fs,rs) = (if fs = [] then T_itrev rs [] else 0)" fun T_enq :: "'a \ 'a queue \ nat" where -"T_enq a (fs,rs) = T_norm(fs, a # rs) + 1" +"T_enq a (fs,rs) = T_norm(fs, a # rs)" fun T_deq :: "'a queue \ nat" where -"T_deq (fs,rs) = (if fs = [] then 0 else T_norm(tl fs,rs)) + 1" - -fun T_first :: "'a queue \ nat" where -"T_first (a # fs,rs) = 1" - -fun T_is_empty :: "'a queue \ nat" where -"T_is_empty (fs,rs) = 1" +"T_deq (fs,rs) = (if fs = [] then 0 else T_norm(tl fs,rs))" text \Amortized running times:\ fun \ :: "'a queue \ nat" where "\(fs,rs) = length rs" -lemma a_enq: "T_enq a (fs,rs) + \(enq a (fs,rs)) - \(fs,rs) \ 4" +lemma a_enq: "T_enq a (fs,rs) + \(enq a (fs,rs)) - \(fs,rs) \ 2" by(auto simp: T_itrev) -lemma a_deq: "T_deq (fs,rs) + \(deq (fs,rs)) - \(fs,rs) \ 3" +lemma a_deq: "T_deq (fs,rs) + \(deq (fs,rs)) - \(fs,rs) \ 1" by(auto simp: T_itrev) end diff --git a/src/HOL/Data_Structures/Tree23_of_List.thy b/src/HOL/Data_Structures/Tree23_of_List.thy --- a/src/HOL/Data_Structures/Tree23_of_List.thy +++ b/src/HOL/Data_Structures/Tree23_of_List.thy @@ -1,168 +1,168 @@ (* Author: Tobias Nipkow *) section \2-3 Tree from List\ theory Tree23_of_List imports Tree23 begin text \Linear-time bottom up conversion of a list of items into a complete 2-3 tree whose inorder traversal yields the list of items.\ subsection "Code" text \Nonempty lists of 2-3 trees alternating with items, starting and ending with a 2-3 tree:\ datatype 'a tree23s = T "'a tree23" | TTs "'a tree23" "'a" "'a tree23s" abbreviation "not_T ts == (\t. ts \ T t)" fun len :: "'a tree23s \ nat" where "len (T _) = 1" | "len (TTs _ _ ts) = len ts + 1" fun trees :: "'a tree23s \ 'a tree23 set" where "trees (T t) = {t}" | "trees (TTs t a ts) = {t} \ trees ts" text \Join pairs of adjacent trees:\ fun join_adj :: "'a tree23s \ 'a tree23s" where "join_adj (TTs t1 a (T t2)) = T(Node2 t1 a t2)" | "join_adj (TTs t1 a (TTs t2 b (T t3))) = T(Node3 t1 a t2 b t3)" | "join_adj (TTs t1 a (TTs t2 b ts)) = TTs (Node2 t1 a t2) b (join_adj ts)" text \Towards termination of \join_all\:\ lemma len_ge2: "not_T ts \ len ts \ 2" by(cases ts rule: join_adj.cases) auto lemma [measure_function]: "is_measure len" by(rule is_measure_trivial) lemma len_join_adj_div2: "not_T ts \ len(join_adj ts) \ len ts div 2" by(induction ts rule: join_adj.induct) auto lemma len_join_adj1: "not_T ts \ len(join_adj ts) < len ts" using len_join_adj_div2[of ts] len_ge2[of ts] by simp corollary len_join_adj2[termination_simp]: "len(join_adj (TTs t a ts)) \ len ts" using len_join_adj1[of "TTs t a ts"] by simp fun join_all :: "'a tree23s \ 'a tree23" where "join_all (T t) = t" | "join_all ts = join_all (join_adj ts)" fun leaves :: "'a list \ 'a tree23s" where "leaves [] = T Leaf" | "leaves (a # as) = TTs Leaf a (leaves as)" definition tree23_of_list :: "'a list \ 'a tree23" where "tree23_of_list as = join_all(leaves as)" subsection \Functional correctness\ subsubsection \\inorder\:\ fun inorder2 :: "'a tree23s \ 'a list" where "inorder2 (T t) = inorder t" | "inorder2 (TTs t a ts) = inorder t @ a # inorder2 ts" lemma inorder2_join_adj: "not_T ts \ inorder2(join_adj ts) = inorder2 ts" by (induction ts rule: join_adj.induct) auto lemma inorder_join_all: "inorder (join_all ts) = inorder2 ts" proof (induction ts rule: join_all.induct) case 1 thus ?case by simp next case (2 t a ts) thus ?case using inorder2_join_adj[of "TTs t a ts"] by (simp add: le_imp_less_Suc) qed lemma inorder2_leaves: "inorder2(leaves as) = as" by(induction as) auto lemma inorder: "inorder(tree23_of_list as) = as" by(simp add: tree23_of_list_def inorder_join_all inorder2_leaves) subsubsection \Completeness:\ lemma complete_join_adj: "\t \ trees ts. complete t \ height t = n \ not_T ts \ \t \ trees (join_adj ts). complete t \ height t = Suc n" by (induction ts rule: join_adj.induct) auto lemma complete_join_all: "\t \ trees ts. complete t \ height t = n \ complete (join_all ts)" proof (induction ts arbitrary: n rule: join_all.induct) case 1 thus ?case by simp next case (2 t a ts) thus ?case apply simp using complete_join_adj[of "TTs t a ts" n, simplified] by blast qed lemma complete_leaves: "t \ trees (leaves as) \ complete t \ height t = 0" by (induction as) auto corollary complete: "complete(tree23_of_list as)" by(simp add: tree23_of_list_def complete_leaves complete_join_all[of _ 0]) subsection "Linear running time" fun T_join_adj :: "'a tree23s \ nat" where "T_join_adj (TTs t1 a (T t2)) = 1" | "T_join_adj (TTs t1 a (TTs t2 b (T t3))) = 1" | "T_join_adj (TTs t1 a (TTs t2 b ts)) = T_join_adj ts + 1" fun T_join_all :: "'a tree23s \ nat" where "T_join_all (T t) = 1" | "T_join_all ts = T_join_adj ts + T_join_all (join_adj ts) + 1" fun T_leaves :: "'a list \ nat" where "T_leaves [] = 1" | "T_leaves (a # as) = T_leaves as + 1" definition T_tree23_of_list :: "'a list \ nat" where -"T_tree23_of_list as = T_leaves as + T_join_all(leaves as) + 1" +"T_tree23_of_list as = T_leaves as + T_join_all(leaves as)" lemma T_join_adj: "not_T ts \ T_join_adj ts \ len ts div 2" by(induction ts rule: T_join_adj.induct) auto lemma len_ge_1: "len ts \ 1" by(cases ts) auto lemma T_join_all: "T_join_all ts \ 2 * len ts" proof(induction ts rule: join_all.induct) case 1 thus ?case by simp next case (2 t a ts) let ?ts = "TTs t a ts" have "T_join_all ?ts = T_join_adj ?ts + T_join_all (join_adj ?ts) + 1" by simp also have "\ \ len ?ts div 2 + T_join_all (join_adj ?ts) + 1" using T_join_adj[of ?ts] by simp also have "\ \ len ?ts div 2 + 2 * len (join_adj ?ts) + 1" using "2.IH" by simp also have "\ \ len ?ts div 2 + 2 * (len ?ts div 2) + 1" using len_join_adj_div2[of ?ts] by simp also have "\ \ 2 * len ?ts" using len_ge_1[of ?ts] by linarith finally show ?case . qed lemma T_leaves: "T_leaves as = length as + 1" by(induction as) auto lemma len_leaves: "len(leaves as) = length as + 1" by(induction as) auto -lemma T_tree23_of_list: "T_tree23_of_list as \ 3*(length as) + 4" +lemma T_tree23_of_list: "T_tree23_of_list as \ 3*(length as) + 3" using T_join_all[of "leaves as"] by(simp add: T_tree23_of_list_def T_leaves len_leaves) end