Theory Indexed_PQueue_Impl

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

section ‹Implementation of the indexed priority queue›

theory Indexed_PQueue_Impl
  imports DynamicArray "../Functional/Indexed_PQueue"
begin

text ‹
  Imperative implementation of indexed priority queue. The data structure
  is also verified in cite"Refine_Imperative_HOL-AFP" by Peter Lammich.
›

datatype 'a indexed_pqueue =
  Indexed_PQueue (pqueue: "(nat × 'a) dynamic_array") (index: "nat option array")
setup add_simple_datatype "indexed_pqueue"

fun idx_pqueue :: "'a::heap idx_pqueue  'a indexed_pqueue  assn" where
  "idx_pqueue (xs, m) (Indexed_PQueue pq idx) = (dyn_array xs pq * idx a m)"
setup add_rewrite_ent_rule @{thm idx_pqueue.simps}

subsection ‹Basic operations›

definition idx_pqueue_empty :: "nat  'a::heap indexed_pqueue Heap" where
  "idx_pqueue_empty k = do {
    pq  dyn_array_new;
    idx  Array.new k None;
    return (Indexed_PQueue pq idx) }"

lemma idx_pqueue_empty_rule [hoare_triple]:
  "<emp>
   idx_pqueue_empty n
   <idx_pqueue ([], replicate n None)>" by auto2

definition idx_pqueue_nth :: "'a::heap indexed_pqueue  nat  (nat × 'a) Heap" where
  "idx_pqueue_nth p i = array_nth (pqueue p) i"

lemma idx_pqueue_nth_rule [hoare_triple]:
  "<idx_pqueue (xs, m) p * (i < length xs)>
   idx_pqueue_nth p i
   <λr. idx_pqueue (xs, m) p * (r = xs ! i)>" by auto2

definition idx_nth :: "'a::heap indexed_pqueue  nat  nat option Heap" where
  "idx_nth p i = Array.nth (index p) i"

lemma idx_nth_rule [hoare_triple]:
  "<idx_pqueue (xs, m) p * (i < length m)>
   idx_nth p i
   <λr. idx_pqueue (xs, m) p * (r = m ! i)>" by auto2

definition idx_pqueue_length :: "'a indexed_pqueue  nat Heap" where
  "idx_pqueue_length a = array_length (pqueue a)"

lemma idx_pqueue_length_rule [hoare_triple]:
  "<idx_pqueue (xs, m) p>
   idx_pqueue_length p
   <λr. idx_pqueue (xs, m) p * (r = length xs)>" by auto2

definition idx_pqueue_swap ::
  "'a::{heap,linorder} indexed_pqueue  nat  nat  unit Heap" where
  "idx_pqueue_swap p i j = do {
     pr_i  array_nth (pqueue p) i;
     pr_j  array_nth (pqueue p) j;
     Array.upd (fst pr_i) (Some j) (index p);
     Array.upd (fst pr_j) (Some i) (index p);
     array_swap (pqueue p) i j
   }"

lemma idx_pqueue_swap_rule [hoare_triple]:
  "i < length xs  j < length xs  index_of_pqueue (xs, m) 
   <idx_pqueue (xs, m) p>
   idx_pqueue_swap p i j
   <λ_. idx_pqueue (idx_pqueue_swap_fun (xs, m) i j) p>"
@proof @unfold "idx_pqueue_swap_fun (xs, m) i j" @qed

definition idx_pqueue_push :: "nat  'a::heap  'a indexed_pqueue  'a indexed_pqueue Heap" where
  "idx_pqueue_push k v p = do {
     len  array_length (pqueue p);
     d'  push_array (k, v) (pqueue p);
     Array.upd k (Some len) (index p);
     return (Indexed_PQueue d' (index p))
   }"

lemma idx_pqueue_push_rule [hoare_triple]:
  "k < length m  ¬has_key_alist xs k 
   <idx_pqueue (xs, m) p>
   idx_pqueue_push k v p
   <idx_pqueue (idx_pqueue_push_fun k v (xs, m))>t"
@proof @unfold "idx_pqueue_push_fun k v (xs, m)" @qed

definition idx_pqueue_pop :: "'a::heap indexed_pqueue  ((nat × 'a) × 'a indexed_pqueue) Heap" where
  "idx_pqueue_pop p = do {
     (x, d')  pop_array (pqueue p);
     Array.upd (fst x) None (index p);
     return (x, Indexed_PQueue d' (index p))
   }"

lemma idx_pqueue_pop_rule [hoare_triple]:
  "xs  []  index_of_pqueue (xs, m) 
   <idx_pqueue (xs, m) p>
   idx_pqueue_pop p
   <λ(x, r). idx_pqueue (idx_pqueue_pop_fun (xs, m)) r * (x = last xs)>"
@proof @unfold "idx_pqueue_pop_fun (xs, m)" @qed

definition idx_pqueue_array_upd :: "nat  'a  'a::heap dynamic_array  unit Heap" where
  "idx_pqueue_array_upd i x d = array_upd i x d"

lemma array_upd_idx_pqueue_rule [hoare_triple]:
  "i < length xs  k = fst (xs ! i) 
   <idx_pqueue (xs, m) p>
   idx_pqueue_array_upd i (k, v) (pqueue p)
   <λ_. idx_pqueue (list_update xs i (k, v), m) p>" by auto2

definition has_key_idx_pqueue :: "nat  'a::{heap,linorder} indexed_pqueue  bool Heap" where
  "has_key_idx_pqueue k p = do {
    i_opt  Array.nth (index p) k;
    return (i_opt  None) }"

lemma has_key_idx_pqueue_rule [hoare_triple]:
  "k < length m  index_of_pqueue (xs, m) 
   <idx_pqueue (xs, m) p>
   has_key_idx_pqueue k p
   <λr. idx_pqueue (xs, m) p * (r  has_key_alist xs k)>" by auto2

setup del_prfstep_thm @{thm idx_pqueue.simps}
setup del_simple_datatype "indexed_pqueue"

subsection ‹Bubble up and down›

partial_function (heap) idx_bubble_down :: "'a::{heap,linorder} indexed_pqueue  nat  unit Heap" where
  "idx_bubble_down a k = do {
    len  idx_pqueue_length a;
    (if s2 k < len then do {
      vk  idx_pqueue_nth a k;
      vs1k  idx_pqueue_nth a (s1 k);
      vs2k  idx_pqueue_nth a (s2 k);
      (if snd vs1k  snd vs2k then
         if snd vk > snd vs1k then
           do { idx_pqueue_swap a k (s1 k); idx_bubble_down a (s1 k) }
         else return ()
       else
         if snd vk > snd vs2k then
           do { idx_pqueue_swap a k (s2 k); idx_bubble_down a (s2 k) }
         else return ()) }
     else if s1 k < len then do {
       vk  idx_pqueue_nth a k;
       vs1k  idx_pqueue_nth a (s1 k);
       (if snd vk > snd vs1k then
          do { idx_pqueue_swap a k (s1 k); idx_bubble_down a (s1 k) }
        else return ()) }
     else return ()) }"

lemma idx_bubble_down_rule [hoare_triple]:
  "index_of_pqueue x 
   <idx_pqueue x a>
   idx_bubble_down a k
   <λ_. idx_pqueue (idx_bubble_down_fun x k) a>"
@proof @fun_induct "idx_bubble_down_fun x k" @with
  @subgoal "(x = (xs, m), k = k)"
  @unfold "idx_bubble_down_fun (xs, m) k"
  @case "s2 k < length xs" @with
    @case "snd (xs ! s1 k)  snd (xs ! s2 k)"
  @end
  @case "s1 k < length xs" @end
@qed

partial_function (heap) idx_bubble_up :: "'a::{heap,linorder} indexed_pqueue  nat  unit Heap" where
  "idx_bubble_up a k =
    (if k = 0 then return () else do {
       len  idx_pqueue_length a;
       (if k < len then do {
          vk  idx_pqueue_nth a k;
          vpk  idx_pqueue_nth a (par k);
          (if snd vk < snd vpk then
             do { idx_pqueue_swap a k (par k); idx_bubble_up a (par k) }
           else return ()) }
        else return ())})"

lemma idx_bubble_up_rule [hoare_triple]:
  "index_of_pqueue x 
   <idx_pqueue x a>
   idx_bubble_up a k
   <λ_. idx_pqueue (idx_bubble_up_fun x k) a>"
@proof @fun_induct "idx_bubble_up_fun x k" @with
  @subgoal "(x = (xs, m), k = k)"
  @unfold "idx_bubble_up_fun (xs, m) k" @end
@qed

subsection ‹Main operations›

definition delete_min_idx_pqueue :: "'a::{heap,linorder} indexed_pqueue  ((nat × 'a) × 'a indexed_pqueue) Heap" where
  "delete_min_idx_pqueue p = do {
     len  idx_pqueue_length p;
     if len = 0 then raise STR ''delete_min''
     else do {
       idx_pqueue_swap p 0 (len - 1);
       (x', r)  idx_pqueue_pop p;
       idx_bubble_down r 0;
       return (x', r)
     }
   }"

lemma delete_min_idx_pqueue_rule [hoare_triple]:
  "xs  []  index_of_pqueue (xs, m) 
   <idx_pqueue (xs, m) p>
   delete_min_idx_pqueue p
   <λ(x, r). idx_pqueue (snd (delete_min_idx_pqueue_fun (xs, m))) r *
       (x = fst (delete_min_idx_pqueue_fun (xs, m)))>"
@proof @unfold "delete_min_idx_pqueue_fun (xs, m)" @qed

definition insert_idx_pqueue :: "nat  'a::{heap,linorder}  'a indexed_pqueue  'a indexed_pqueue Heap" where
  "insert_idx_pqueue k v p = do {
     p'  idx_pqueue_push k v p;
     len  idx_pqueue_length p';
     idx_bubble_up p' (len - 1);
     return p'
   }"

lemma insert_idx_pqueue_rule [hoare_triple]:
  "k < length m  ¬has_key_alist xs k  index_of_pqueue (xs, m) 
   <idx_pqueue (xs, m) p>
   insert_idx_pqueue k v p
   <idx_pqueue (insert_idx_pqueue_fun k v (xs, m))>t"
@proof @unfold "insert_idx_pqueue_fun k v (xs, m)" @qed

definition update_idx_pqueue ::
  "nat  'a::{heap,linorder}  'a indexed_pqueue  'a indexed_pqueue Heap" where
  "update_idx_pqueue k v p = do {
    i_opt  idx_nth p k;
    case i_opt of
      None  insert_idx_pqueue k v p
    | Some i  do {
      x  idx_pqueue_nth p i;
      idx_pqueue_array_upd i (k, v) (pqueue p);
      (if snd x  v then do {idx_bubble_down p i; return p}
       else do {idx_bubble_up p i; return p}) }}"

lemma update_idx_pqueue_rule [hoare_triple]:
  "k < length m  index_of_pqueue (xs, m) 
   <idx_pqueue (xs, m) p>
   update_idx_pqueue k v p
   <idx_pqueue (update_idx_pqueue_fun k v (xs, m))>t"
@proof @unfold "update_idx_pqueue_fun k v (xs, m)" @qed

subsection ‹Outer interface›

text ‹Express Hoare triples for indexed priority queue operations in terms of
  the mapping represented by the queue.›
definition idx_pqueue_map :: "(nat, 'a::{heap,linorder}) map  nat  'a indexed_pqueue  assn" where
  "idx_pqueue_map M n p = (Axs m. idx_pqueue (xs, m) p *
      (index_of_pqueue (xs, m)) * (is_heap xs) * (M = map_of_alist xs) * (n = length m))"
setup add_rewrite_ent_rule @{thm idx_pqueue_map_def}

lemma heap_implies_hd_min2 [resolve]:
  "is_heap xs  xs  []  (map_of_alist xs)k = Some v  snd (hd xs)  v"
@proof
  @obtain i where "i < length xs" "xs ! i = (k, v)"
  @have "snd (hd xs)  snd (xs ! i)"
@qed

theorem idx_pqueue_empty_map [hoare_triple]:
  "<emp>
   idx_pqueue_empty n
   <idx_pqueue_map empty_map n>" by auto2

theorem delete_min_idx_pqueue_map [hoare_triple]:
  "<idx_pqueue_map M n p * (M  empty_map)>
   delete_min_idx_pqueue p
   <λ(x, r). idx_pqueue_map (delete_map (fst x) M) n r * (fst x < n) *
                (is_heap_min (fst x) M) * (Mfst x = Some (snd x))>" by auto2

theorem insert_idx_pqueue_map [hoare_triple]:
  "k < n  k  keys_of M 
   <idx_pqueue_map M n p>
   insert_idx_pqueue k v p
   <idx_pqueue_map (M {k  v}) n>t" by auto2

theorem has_key_idx_pqueue_map [hoare_triple]:
  "k < n 
   <idx_pqueue_map M n p>
   has_key_idx_pqueue k p
   <λr. idx_pqueue_map M n p * (r  k  keys_of M)>" by auto2

theorem update_idx_pqueue_map [hoare_triple]:
  "k < n 
   <idx_pqueue_map M n p>
   update_idx_pqueue k v p
   <idx_pqueue_map (M {k  v}) n>t" by auto2

setup del_prfstep_thm @{thm idx_pqueue_map_def}

end