# HG changeset patch # User wenzelm # Date 1541513190 -3600 # Node ID 27423819534c89322337794d004adbdc71b7b9df # Parent c1fe9dcc274a2a8590ec17f8f48e0e8c7661dcbc# Parent 9f21381600e380ca1804cba6425e980e2573325b merged diff -r 9f21381600e3 -r 27423819534c src/HOL/Library/Comparator.thy --- a/src/HOL/Library/Comparator.thy Tue Nov 06 14:53:56 2018 +0100 +++ b/src/HOL/Library/Comparator.thy Tue Nov 06 15:06:30 2018 +0100 @@ -76,6 +76,24 @@ "asymp (\a b. cmp a b = Greater)" using irreflp_greater by (auto intro!: asympI dest: asym_greater) +lemma trans_equiv_less: + "cmp a c = Less" if "cmp a b = Equiv" and "cmp b c = Less" + using that + by (metis (full_types) comp.exhaust greater_iff_sym_less trans_equiv trans_less) + +lemma trans_less_equiv: + "cmp a c = Less" if "cmp a b = Less" and "cmp b c = Equiv" + using that + by (metis (full_types) comp.exhaust greater_iff_sym_less trans_equiv trans_less) + +lemma trans_equiv_greater: + "cmp a c = Greater" if "cmp a b = Equiv" and "cmp b c = Greater" + using that by (simp add: sym [of a b] greater_iff_sym_less trans_less_equiv) + +lemma trans_greater_equiv: + "cmp a c = Greater" if "cmp a b = Greater" and "cmp b c = Equiv" + using that by (simp add: sym [of b c] greater_iff_sym_less trans_equiv_less) + lemma transp_less: "transp (\a b. cmp a b = Less)" by (rule transpI) (fact trans_less) @@ -118,6 +136,26 @@ "transp (\a b. cmp a b \ Greater)" by (rule transpI) (fact trans_not_greater) +text \Substitution under equivalences\ + +lemma equiv_subst_left: + "cmp z y = comp \ cmp x y = comp" if "cmp z x = Equiv" for comp +proof - + from that have "cmp x z = Equiv" + by (simp add: sym) + with that show ?thesis + by (cases comp) (auto intro: trans_equiv trans_equiv_less trans_equiv_greater) +qed + +lemma equiv_subst_right: + "cmp x z = comp \ cmp x y = comp" if "cmp z y = Equiv" for comp +proof - + from that have "cmp y z = Equiv" + by (simp add: sym) + with that show ?thesis + by (cases comp) (auto intro: trans_equiv trans_less_equiv trans_greater_equiv) +qed + end typedef 'a comparator = "{cmp :: 'a \ 'a \ comp. comparator cmp}" diff -r 9f21381600e3 -r 27423819534c src/HOL/Library/Sorting_Algorithms.thy --- a/src/HOL/Library/Sorting_Algorithms.thy Tue Nov 06 14:53:56 2018 +0100 +++ b/src/HOL/Library/Sorting_Algorithms.thy Tue Nov 06 15:06:30 2018 +0100 @@ -6,11 +6,6 @@ imports Main Multiset Comparator begin -text \Prelude\ - -hide_const (open) sorted insort sort - - section \Stably sorted lists\ abbreviation (input) stable_segment :: "'a comparator \ 'a \ 'a list \ 'a list" @@ -19,17 +14,26 @@ fun sorted :: "'a comparator \ 'a list \ bool" where sorted_Nil: "sorted cmp [] \ True" | sorted_single: "sorted cmp [x] \ True" - | sorted_rec: "sorted cmp (y # x # xs) \ compare cmp x y \ Less \ sorted cmp (x # xs)" + | sorted_rec: "sorted cmp (y # x # xs) \ compare cmp y x \ Greater \ sorted cmp (x # xs)" lemma sorted_ConsI: "sorted cmp (x # xs)" if "sorted cmp xs" - and "\y ys. xs = y # ys \ compare cmp y x \ Less" + and "\y ys. xs = y # ys \ compare cmp x y \ Greater" + using that by (cases xs) simp_all + +lemma sorted_Cons_imp_sorted: + "sorted cmp xs" if "sorted cmp (x # xs)" using that by (cases xs) simp_all +lemma sorted_Cons_imp_not_less: + "compare cmp y x \ Greater" if "sorted cmp (y # xs)" + and "x \ set xs" + using that by (induction xs arbitrary: y) (auto dest: compare.trans_not_greater) + lemma sorted_induct [consumes 1, case_names Nil Cons, induct pred: sorted]: "P xs" if "sorted cmp xs" and "P []" and *: "\x xs. sorted cmp xs \ P xs - \ (\y. y \ set xs \ compare cmp y x \ Less) \ P (x # xs)" + \ (\y. y \ set xs \ compare cmp x y \ Greater) \ P (x # xs)" using \sorted cmp xs\ proof (induction xs) case Nil show ?case @@ -40,7 +44,7 @@ by (cases xs) simp_all moreover have "P xs" using \sorted cmp xs\ by (rule Cons.IH) - moreover have "compare cmp y x \ Less" if "y \ set xs" for y + moreover have "compare cmp x y \ Greater" if "y \ set xs" for y using that \sorted cmp (x # xs)\ proof (induction xs) case Nil then show ?case @@ -54,10 +58,10 @@ by simp next case (Cons w ws) - with Cons.prems have "compare cmp w z \ Less" "compare cmp z x \ Less" + with Cons.prems have "compare cmp z w \ Greater" "compare cmp x z \ Greater" by auto - then have "compare cmp w x \ Less" - by (auto dest: compare.trans_not_less) + then have "compare cmp x w \ Greater" + by (auto dest: compare.trans_not_greater) with Cons show ?thesis using Cons.prems Cons.IH by auto qed @@ -69,7 +73,7 @@ lemma sorted_induct_remove1 [consumes 1, case_names Nil minimum]: "P xs" if "sorted cmp xs" and "P []" and *: "\x xs. sorted cmp xs \ P (remove1 x xs) - \ x \ set xs \ hd (stable_segment cmp x xs) = x \ (\y. y \ set xs \ compare cmp y x \ Less) + \ x \ set xs \ hd (stable_segment cmp x xs) = x \ (\y. y \ set xs \ compare cmp x y \ Greater) \ P xs" using \sorted cmp xs\ proof (induction xs) case Nil @@ -80,7 +84,7 @@ then have "sorted cmp (x # xs)" by (simp add: sorted_ConsI) moreover note Cons.IH - moreover have "\y. compare cmp y x = Less \ y \ set xs \ False" + moreover have "\y. compare cmp x y = Greater \ y \ set xs \ False" using Cons.hyps by simp ultimately show ?case by (auto intro!: * [of "x # xs" x]) blast @@ -114,7 +118,7 @@ assume "remove1 x ys = z # zs" with \x \ y\ have "z \ set ys" using notin_set_remove1 [of z ys x] by auto - then show "compare cmp z y \ Less" + then show "compare cmp y z \ Greater" by (rule Cons.hyps(2)) qed with False show ?thesis @@ -123,6 +127,20 @@ qed qed +lemma sorted_stable_segment: + "sorted cmp (stable_segment cmp x xs)" +proof (induction xs) + case Nil + show ?case + by simp +next + case (Cons y ys) + then show ?case + by (auto intro!: sorted_ConsI simp add: filter_eq_Cons_iff compare.sym) + (auto dest: compare.trans_equiv simp add: compare.sym compare.greater_iff_sym_less) + +qed + primrec insort :: "'a comparator \ 'a \ 'a list \ 'a list" where "insort cmp y [] = [y]" | "insort cmp y (x # xs) = (if compare cmp y x \ Greater @@ -175,7 +193,7 @@ lemma insort_eq_ConsI: "insort cmp x xs = x # xs" - if "sorted cmp xs" "\y. y \ set xs \ compare cmp y x \ Less" + if "sorted cmp xs" "\y. y \ set xs \ compare cmp x y \ Greater" using that by (induction xs) (simp_all add: compare.greater_iff_sym_less) lemma remove1_insort_not_same_eq [simp]: @@ -188,14 +206,15 @@ next case (Cons z zs) show ?case - proof (cases "compare cmp z x = Less") + proof (cases "compare cmp x z = Greater") case True with Cons show ?thesis - by (simp add: compare.greater_iff_sym_less) + by simp next case False - have "compare cmp y x \ Less" if "y \ set zs" for y - using that False Cons.hyps by (auto dest: compare.trans_not_less) + then have "compare cmp x y \ Greater" if "y \ set zs" for y + using that Cons.hyps + by (auto dest: compare.trans_not_greater) with Cons show ?thesis by (simp add: insort_eq_ConsI) qed @@ -211,7 +230,7 @@ next case (Cons y ys) then have "compare cmp x y \ Less" - by auto + by (auto simp add: compare.greater_iff_sym_less) then consider "compare cmp x y = Greater" | "compare cmp x y = Equiv" by (cases "compare cmp x y") auto then show ?case proof cases @@ -227,6 +246,31 @@ qed qed +lemma sorted_append_iff: + "sorted cmp (xs @ ys) \ sorted cmp xs \ sorted cmp ys + \ (\x \ set xs. \y \ set ys. compare cmp x y \ Greater)" (is "?P \ ?R \ ?S \ ?Q") +proof + assume ?P + have ?R + using \?P\ by (induction xs) + (auto simp add: sorted_Cons_imp_not_less, + auto simp add: sorted_Cons_imp_sorted intro: sorted_ConsI) + moreover have ?S + using \?P\ by (induction xs) (auto dest: sorted_Cons_imp_sorted) + moreover have ?Q + using \?P\ by (induction xs) (auto simp add: sorted_Cons_imp_not_less, + simp add: sorted_Cons_imp_sorted) + ultimately show "?R \ ?S \ ?Q" + by simp +next + assume "?R \ ?S \ ?Q" + then have ?R ?S ?Q + by simp_all + then show ?P + by (induction xs) + (auto simp add: append_eq_Cons_conv intro!: sorted_ConsI) +qed + definition sort :: "'a comparator \ 'a list \ 'a list" where "sort cmp xs = foldr (insort cmp) xs []" @@ -299,7 +343,7 @@ case (minimum x xs) from \mset ys = mset xs\ have ys: "set ys = set xs" by (rule mset_eq_setD) - then have "compare cmp y x \ Less" if "y \ set ys" for y + then have "compare cmp x y \ Greater" if "y \ set ys" for y using that minimum.hyps by simp from minimum.prems have stable: "stable_segment cmp x ys = stable_segment cmp x xs" by simp @@ -318,4 +362,113 @@ qed qed +lemma filter_insort: + "filter P (insort cmp x xs) = insort cmp x (filter P xs)" + if "sorted cmp xs" and "P x" + using that by (induction xs) + (auto simp add: compare.trans_not_greater insort_eq_ConsI) + +lemma filter_insort_triv: + "filter P (insort cmp x xs) = filter P xs" + if "\ P x" + using that by (induction xs) simp_all + +lemma filter_sort: + "filter P (sort cmp xs) = sort cmp (filter P xs)" + by (induction xs) (auto simp add: filter_insort filter_insort_triv) + + +section \Alternative sorting algorithms\ + +subsection \Quicksort\ + +definition quicksort :: "'a comparator \ 'a list \ 'a list" + where quicksort_is_sort [simp]: "quicksort = sort" + +lemma sort_by_quicksort: + "sort = quicksort" + by simp + +lemma sort_by_quicksort_rec: + "sort cmp xs = sort cmp [x\xs. compare cmp x (xs ! (length xs div 2)) = Less] + @ stable_segment cmp (xs ! (length xs div 2)) xs + @ sort cmp [x\xs. compare cmp x (xs ! (length xs div 2)) = Greater]" (is "sort cmp ?lhs = ?rhs") +proof (rule sort_eqI) + show "mset ?lhs = mset ?rhs" + by (rule multiset_eqI) (auto simp add: compare.sym intro: comp.exhaust) +next + show "sorted cmp ?rhs" + by (auto simp add: sorted_append_iff sorted_stable_segment compare.equiv_subst_right dest: compare.trans_greater) +next + let ?pivot = "xs ! (length xs div 2)" + fix l + assume "l \ set xs" + have "compare cmp x ?pivot = comp \ compare cmp l x = Equiv + \ compare cmp l ?pivot = comp \ compare cmp l x = Equiv" for x comp + proof - + have "compare cmp x ?pivot = comp \ compare cmp l ?pivot = comp" + if "compare cmp l x = Equiv" + using that by (simp add: compare.equiv_subst_left compare.sym) + then show ?thesis by blast + qed + then show "stable_segment cmp l ?lhs = stable_segment cmp l ?rhs" + by (simp add: stable_sort compare.sym [of _ ?pivot]) + (cases "compare cmp l ?pivot", simp_all) +qed + +context +begin + +qualified definition partition :: "'a comparator \ 'a \ 'a list \ 'a list \ 'a list \ 'a list" + where "partition cmp pivot xs = + ([x \ xs. compare cmp x pivot = Less], stable_segment cmp pivot xs, [x \ xs. compare cmp x pivot = Greater])" + +qualified lemma partition_code [code]: + "partition cmp pivot [] = ([], [], [])" + "partition cmp pivot (x # xs) = + (let (lts, eqs, gts) = partition cmp pivot xs + in case compare cmp x pivot of + Less \ (x # lts, eqs, gts) + | Equiv \ (lts, x # eqs, gts) + | Greater \ (lts, eqs, x # gts))" + using comp.exhaust by (auto simp add: partition_def Let_def compare.sym [of _ pivot]) + +lemma quicksort_code [code]: + "quicksort cmp xs = + (case xs of + [] \ [] + | [x] \ xs + | [x, y] \ (if compare cmp x y \ Greater then xs else [y, x]) + | _ \ + let (lts, eqs, gts) = partition cmp (xs ! (length xs div 2)) xs + in quicksort cmp lts @ eqs @ quicksort cmp gts)" +proof (cases "length xs \ 3") + case False + then have "length xs \ 2" + by simp + then have "length xs = 0 \ length xs = 1 \ length xs = 2" + using le_neq_trans less_2_cases by auto + then consider "xs = []" | x where "xs = [x]" | x y where "xs = [x, y]" + by (auto simp add: length_Suc_conv numeral_2_eq_2) + then show ?thesis + by cases simp_all +next + case True + then obtain x y z zs where "xs = x # y # z # zs" + by (metis le_0_eq length_0_conv length_Cons list.exhaust not_less_eq_eq numeral_3_eq_3) + moreover have "quicksort cmp xs = + (let (lts, eqs, gts) = partition cmp (xs ! (length xs div 2)) xs + in quicksort cmp lts @ eqs @ quicksort cmp gts)" + using sort_by_quicksort_rec [of cmp xs] by (simp add: partition_def) + ultimately show ?thesis + by simp +qed + end + +text \Evaluation example\ + +value "let cmp = key abs (reversed default) + in quicksort cmp [65, 1705, -2322, 734, 4, (-17::int)]" + +end