Theory Quicksort

(*
  File: Quicksort.thy
  Author: Bohua Zhan
*)

section ‹Quicksort›

theory Quicksort
  imports Arrays_Ex
begin

text ‹
  Functional version of quicksort.

  Implementation of quicksort is largely based on theory
  Imperative\_Quicksort in HOL/Imperative\_HOL/ex in the Isabelle
  library.
›

subsection ‹Outer remains›
  
definition outer_remains :: "'a list  'a list  nat  nat  bool" where [rewrite]:
  "outer_remains xs xs' l r  (length xs = length xs'  (i. i < l  r < i  xs ! i = xs' ! i))"

lemma outer_remains_length [forward]:
  "outer_remains xs xs' l r  length xs = length xs'" by auto2

lemma outer_remains_eq [rewrite_back]:
  "outer_remains xs xs' l r  i < l  xs ! i = xs' ! i"
  "outer_remains xs xs' l r  r < i  xs ! i = xs' ! i" by auto2+

lemma outer_remains_sublist [backward2]:
  "outer_remains xs xs' l r  i < l  take i xs = take i xs'"
  "outer_remains xs xs' l r  r < i  drop i xs = drop i xs'"
  "i  j  j  length xs  outer_remains xs xs' l r  j  l  sublist i j xs = sublist i j xs'"
  "i  j  j  length xs  outer_remains xs xs' l r  i > r  sublist i j xs = sublist i j xs'" by auto2+
setup del_prfstep_thm_eqforward @{thm outer_remains_def}

subsection ‹part1 function›  

function part1 :: "('a::linorder) list  nat  nat  'a  (nat × 'a list)" where
  "part1 xs l r a = (
     if r  l then (r, xs)
     else if xs ! l  a then part1 xs (l + 1) r a
     else part1 (list_swap xs l r) l (r - 1) a)"
  by auto
  termination by (relation "measure (λ(_,l,r,_). r - l)") auto
setup register_wellform_data ("part1 xs l r a", ["r < length xs"])
setup add_prfstep_check_req ("part1 xs l r a", "r < length xs")

lemma part1_basic:
  "r < length xs  l  r  (rs, xs') = part1 xs l r a 
   outer_remains xs xs' l r  mset xs' = mset xs  l  rs  rs  r"
@proof @fun_induct "part1 xs l r a" @unfold "part1 xs l r a" @qed
setup add_forward_prfstep_cond @{thm part1_basic} [with_term "part1 ?xs ?l ?r ?a"]

lemma part1_partitions1 [backward]:
  "r < length xs  (rs, xs') = part1 xs l r a  l  i  i < rs  xs' ! i  a"
@proof @fun_induct "part1 xs l r a" @unfold "part1 xs l r a" @qed

lemma part1_partitions2 [backward]:
  "r < length xs  (rs, xs') = part1 xs l r a  rs < i  i  r  xs' ! i  a"
@proof @fun_induct "part1 xs l r a" @unfold "part1 xs l r a" @qed

subsection ‹Paritition function›

definition partition :: "('a::linorder list)  nat  nat  (nat × 'a list)" where [rewrite]:
  "partition xs l r = (
    let p = xs ! r;
      (m, xs') = part1 xs l (r - 1) p;
      m' = if xs' ! m  p then m + 1 else m
    in
      (m', list_swap xs' m' r))"
setup register_wellform_data ("partition xs l r", ["l < r", "r < length xs"])

lemma partition_basic:
  "l < r  r < length xs  (rs, xs') = partition xs l r 
   outer_remains xs xs' l r  mset xs' = mset xs  l  rs  rs  r" by auto2
setup add_forward_prfstep_cond @{thm partition_basic} [with_term "partition ?xs ?l ?r"]
  
lemma partition_partitions1 [forward]:
  "l < r  r < length xs  (rs, xs') = partition xs l r 
   x  set (sublist l rs xs')  x  xs' ! rs"
@proof @obtain i where "i  l" "i < rs" "x = xs' ! i" @qed

lemma partition_partitions2 [forward]:
  "l < r  r < length xs  (rs, xs'') = partition xs l r 
   x  set (sublist (rs + 1) (r + 1) xs'')  x  xs'' ! rs"
@proof
  @obtain i where "i  rs + 1" "i < r + 1" "x = xs'' ! i"
  @let "p = xs ! r"
  @let "m = fst (part1 xs l (r - 1) p)"
  @let "xs' = snd (part1 xs l (r - 1) p)"
  @case "xs' ! m  p"
@qed
setup del_prfstep_thm @{thm partition_def}

lemma quicksort_term1:
  "¬r  l  ¬ length xs  r  x = partition xs l r  (p, xs1) = x  p - Suc l < r - l"
@proof @have "fst (partition xs l r) - l - 1 < r - l" @qed

lemma quicksort_term2:
  "¬r  l  ¬ length xs  r  x = partition xs l r  (p, xs2) = x  r - Suc p < r - l"
@proof @have "r - fst (partition xs l r) - 1 < r - l" @qed

subsection ‹Quicksort function›

function quicksort :: "('a::linorder) list  nat  nat  'a list" where
  "quicksort xs l r = (
    if l  r then xs
    else if r  length xs then xs
    else let
      (p, xs1) = partition xs l r;
      xs2 = quicksort xs1 l (p - 1)
    in
      quicksort xs2 (p + 1) r)"
  by auto termination apply (relation "measure (λ(a, l, r). (r - l))") 
  by (auto simp add: quicksort_term1 quicksort_term2)

lemma quicksort_basic [rewrite_arg]:
  "mset (quicksort xs l r) = mset xs  outer_remains xs (quicksort xs l r) l r"
@proof @fun_induct "quicksort xs l r" @unfold "quicksort xs l r" @qed

lemma quicksort_trivial1 [rewrite]:
  "l  r  quicksort xs l r = xs"
@proof @unfold "quicksort xs l r" @qed

lemma quicksort_trivial2 [rewrite]:
  "r  length xs  quicksort xs l r = xs"
@proof @unfold "quicksort xs l r" @qed

lemma quicksort_permutes [resolve]:
  "xs' = quicksort xs l r  set (sublist l (r + 1) xs') = set (sublist l (r + 1) xs)"
@proof
  @case "l  r" @case "r  length xs"
  @have "xs = take l xs @ sublist l (r + 1) xs @ drop (r + 1) xs"
  @have "xs' = take l xs' @ sublist l (r + 1) xs' @ drop (r + 1) xs'"
  @have "take l xs = take l xs'"
  @have "drop (r + 1) xs = drop (r + 1) xs'"
@qed

lemma quicksort_sorts [forward_arg]:
  "r < length xs  sorted (sublist l (r + 1) (quicksort xs l r))"
@proof @fun_induct "quicksort xs l r"
  @case "l  r" @with @case "l = r" @end
  @case "r  length xs"
  @let "p = fst (partition xs l r)"
  @let "xs1 = snd (partition xs l r)"
  @let "xs2 = quicksort xs1 l (p - 1)"
  @let "xs3 = quicksort xs2 (p + 1) r"
  @have "sorted (sublist l (r + 1) xs3)" @with
    @have "l  p" @have "p + 1  r + 1" @have "r + 1  length xs3"
    @have "sublist l p xs2 = sublist l p xs3"
    @have "set (sublist l p xs1) = set (sublist l p xs2)"
    @have "sublist (p + 1) (r + 1) xs1 = sublist (p + 1) (r + 1) xs2"
    @have "set (sublist (p + 1) (r + 1) xs2) = set (sublist (p + 1) (r + 1) xs3)"
    @have "xset (sublist l p xs3). x  xs3 ! p"
    @have "xset (sublist (p + 1) (r + 1) xs3). x  xs3 ! p"
    @have "sorted (sublist l p xs3)"
    @have "sorted (sublist (p + 1) (r + 1) xs3)"
    @have "sublist l (r + 1) xs3 = sublist l p xs3 @ (xs3 ! p) # sublist (p + 1) (r + 1) xs3"
  @end
  @unfold "quicksort xs l r"
@qed

text ‹Main result: correctness of functional quicksort.›
theorem quicksort_sorts_all [rewrite]:
  "xs  []  quicksort xs 0 (length xs - 1) = sort xs"
@proof
  @let "xs' = quicksort xs 0 (length xs - 1)"
  @have "sublist 0 (length xs - 1 + 1) xs' = xs'"
@qed

end