Theory PST_General

section ‹General Priority Search Trees›

theory PST_General
imports 
  "HOL-Data_Structures.Tree2"
  Prio_Map_Specs
begin

text ‹\noindent
  We show how to implement priority maps
  by augmented binary search trees. That is, the basic data structure is some 
  arbitrary binary search tree, e.g.\ a red-black tree, implementing the map 
  from @{typ 'a} to @{typ 'b} by storing pairs (k,p)› in each node. At this
  point we need to assume that the keys are also linearly ordered. To 
  implement getmin› efficiently we annotate/augment each node with another pair
  (k',p')›, the intended result of getmin› when applied to that subtree. The 
  specification of getmin› tells us that (k',p')› must be in that subtree and
  that p'› is the minimal priority in that subtree. Thus the annotation can be 
  computed by passing the (k',p')› with the minimal p'› up the tree. We will 
  now make this more precise for balanced binary trees in general.
  
  We assume that our trees are either leaves of the form @{term Leaf} or nodes 
  of the form @{term "Node l (kp, b) r"} where l› and r› are subtrees, kp› is 
  the contents of the node (a key-priority pair) and b› is some additional 
  balance information (e.g.\ colour, height, size, \dots). Augmented nodes are 
  of the form termNode l (kp, (b,kp')) r.
›

type_synonym ('k,'p,'c) pstree = "(('k×'p) × ('c × ('k × 'p))) tree"
 
text ‹ 
  The following invariant states that a node annotation is actually a minimal 
  key-priority pair for the node's subtree.
›

fun invpst :: "('k,'p::linorder,'c) pstree  bool" where
  "invpst Leaf = True"
| "invpst (Node l (x, _,mkp) r)  invpst l  invpst r
     is_min2 mkp (set (inorder l @ x # inorder r))"

text ‹The implementation of getmin› is trivial:›

fun pst_getmin where
"pst_getmin (Node _ (_, _,a) _) = a"

lemma pst_getmin_ismin: 
  "invpst t  tLeaf  is_min2 (pst_getmin t) (set_tree t)"
by (cases t rule: pst_getmin.cases) auto

  
text ‹  
  It remains to upgrade the existing map operations to work with augmented nodes.
  Therefore we now show how to transform any function definition on un-augmented 
  trees into one on trees augmented with (k',p')› pairs. A defining equation
  f pats = e› for the original type of nodes is transformed into an equation
  f pats' = e'› on the augmented type of nodes as follows:
   Every pattern @{term "Node l (kp, b) r"} in pats› and e› is replaced by
    @{term "Node l (kp, (b,DUMMY)) r"} to obtain pats'› and e2.
   To obtain e'›, every expression @{term "Node l (kp, b) r"} in e2 is 
    replaced by mkNode l kp b r› where:
›
  
definition "min2  λ(k,p) (k',p'). if pp' then (k,p) else (k',p')"

definition "min_kp a l r  case (l,r) of
  (Leaf,Leaf)  a
| (Leaf,Node _ (_, (_,kpr)) _)  min2 a kpr
| (Node _ (_, (_,kpl)) _,Leaf)  min2 a kpl
| (Node _ (_, (_,kpl)) _,Node _ (_, (_,kpr)) _)  min2 a (min2 kpl kpr)"

definition "mkNode c l a r  Node l (a, (c,min_kp a l r)) r"

text ‹  
  Note that this transformation does not affect the asymptotic complexity 
  of f›. Therefore the priority search tree operations have the same complexity 
  as the underlying search tree operations, i.e.\ typically logarithmic 
  (update›, delete›, lookup›) and constant time (empty›, is_empty›).
›

text ‹It is straightforward to show that @{const mkNode} preserves the invariant:› 
  
lemma is_min2_Empty[simp]: "¬is_min2 x {}"
by (auto simp: is_min2_def)

lemma is_min2_singleton[simp]: "is_min2 a {b}  b=a"
by (auto simp: is_min2_def)

lemma is_min2_insert:
  "is_min2 x (insert y ys) 
   (y=x  (zys. snd x  snd z))  (snd x  snd y  is_min2 x ys)"
by (auto simp: is_min2_def)

lemma is_min2_union:
  "is_min2 x (ys  zs) 
   (is_min2 x ys  (zzs. snd x  snd z)) 
     ((yys. snd x  snd y)  is_min2 x zs)"
by (auto simp: is_min2_def)

lemma is_min2_min2_insI: "is_min2 y ys  is_min2 (min2 x y) (insert x ys)"
by (auto simp: is_min2_def min2_def split: prod.split)

lemma is_min2_mergeI: 
  "is_min2 x xs  is_min2 y ys  is_min2 (min2 x y) (xs  ys)"
by (auto simp: is_min2_def min2_def split: prod.split)

theorem invpst_mkNode[simp]: "invpst (mkNode c l a r)  invpst l  invpst r"
apply (cases l rule: invpst.cases; 
       cases r rule: invpst.cases; 
       simp add: mkNode_def min_kp_def)
  subgoal using is_min2_min2_insI by blast
 subgoal by (auto intro!: is_min2_min2_insI simp: insert_commute)
subgoal by (smt Un_insert_left Un_insert_right is_min2_mergeI is_min2_min2_insI 
                sup_assoc)
done

end