aboutsummaryrefslogtreecommitdiffhomepage
path: root/hol-light/TacticRecording/dltree.ml
blob: e97ecd0beeba8cf6dde663d0f63a536f7718bd25 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
(* ========================================================================== *)
(* DYNAMIC LOOKUP TREES (HOL Zero)                                            *)
(* - Library support for data storage in dynamic indexed binary trees         *)
(*                                                                            *)
(* By Mark Adams                                                              *)
(* Copyright (c) Proof Technologies Ltd, 2008-2011                            *)
(* ========================================================================== *)


module Dltree : Dltree_sig = struct


(* This module provides library support for operations on dynamic lookup      *)
(* trees - self-balancing binary trees that store information on nodes        *)
(* ordered according to an index under '(<)'-comparison.                      *)

(* This is implemented as Andersson trees (also called AA trees), that stay   *)
(* balanced within a factor of 2 - i.e. the maximum distance from root to     *)
(* leaf is no more than twice the minimum distance.  This has a fairly simple *)
(* implementation and yet is one of the most efficient forms of self-         *)
(* balancing trees.                                                           *)


(* dltree datatype *)

(* The 'dltree' datatype is a binary lookup tree datatype, where an index and *)
(* item are held at each node, and leaves hold no information.  Comparison    *)
(* between indexes is done using the polymorphic '(<)' total order relation.  *)

(* An integer is also held at each node and is used to keep the tree balanced *)
(* as items are inserted/removed.  This integer represents the distance from  *)
(* the node to the left-most leaf descendant of the node.  Thus every left-   *)
(* branch node holds a value 1 less than its parent, and a node with a leaf   *)
(* as its left branch holds value 1.  Every right-branch holds an integer     *)
(* either equal to its parent's or 1 less, and must be strictly less than its *)
(* grandparent's.  Thus the integer at a node also represents an upper bound  *)
(* of half the distance from the node to its rightmost leaf descendant, and   *)
(* thus an upper bound of half the distance from the node to any descendant.  *)

(* The tree is kept balanced by adjusting nodes' integers and left and right  *)
(* branches as the tree is updated.  This is done by the 'skew' and 'split'   *)
(* operations.  The number of nodes that need to be considered in this        *)
(* process is at most O(log n).  Note that reference types are used for a     *)
(* node's branches, to save on unnecessary garbage collection that would      *)
(* otherwise result in rebuilding all ancestor nodes of all adjusted nodes    *)
(* each time a tree is updated.                                               *)

type ('a,'b) dltree0 =
   Node of (int * ('a * 'b) * ('a,'b) dltree * ('a,'b) dltree)
 | Leaf

and ('a,'b) dltree = ('a,'b) dltree0 ref;;


(* dltree_empty : unit -> ('a,'b) dltree                                      *)
(*                                                                            *)
(* Returns a fresh empty dltree.                                              *)

let dltree_empty () = ref Leaf;;


(* dltree_reempty : ('a,'b) dltree -> unit                                    *)
(*                                                                            *)
(* Empties a given dltree.                                                    *)

let dltree_reempty tr = (tr := Leaf);;


(* dltree_elems : ('a,'b) dltree -> ('a * 'b) list                            *)
(*                                                                            *)
(* This converts the information held in a given lookup tree into an index-   *)
(* ordered association list.                                                  *)

let rec dltree_elems0 tr0 xys0 =
  match !tr0 with
    Node (_,xy0,tr1,tr2) -> dltree_elems0 tr1 (xy0::(dltree_elems0 tr2 xys0))
  | Leaf                 -> xys0;;

let dltree_elems tr = dltree_elems0 tr [];;


(* Node destructors *)

let dest_node tr0 =
  match !tr0 with
    Node info -> info
  | Leaf      -> failwith "dest_node: ?";;

let level tr0 =
  match !tr0 with
    Node (l,_,_,_) -> l
  | Leaf           -> 0;;

let left_branch tr0 =
  match !tr0 with
    Node (_,_,tr1,_) -> tr1
  | Leaf             -> failwith "left_branch: No left branch";;

let right_branch tr0 =
  match !tr0 with
    Node (_,_,_,tr2) -> tr2
  | Leaf             -> failwith "right_branch: No right branch";;

let rec leftmost_elem x tr0 =
  match !tr0 with
    Node (_,x0,tr1,_) -> leftmost_elem x0 tr1
  | Leaf              -> x;;

let rec rightmost_elem x tr0 =
  match !tr0 with
    Node (_,x0,_,tr2) -> rightmost_elem x0 tr2
  | Leaf              -> x;;


(* Tests *)

let is_leaf tr0 =
  match !tr0 with
    Leaf -> true
  | _    -> false;;

let is_node tr0 =
  match !tr0 with
    Node _ -> true
  | _      -> false;;


(* skew *)

let skew tr0 =
  if (is_leaf tr0) or (is_leaf (left_branch tr0))
    then ()
  else if (level (left_branch tr0) = level tr0)
    then let (l0,xy0,tr1,tr2) = dest_node tr0 in
         let (l1,xy1,tr11,tr12) = dest_node tr1 in
         (tr0 := Node (l1, xy1, tr11, ref (Node (l0,xy0,tr12,tr2))))
    else ();;


(* split *)

let split tr0 =
  if (is_leaf tr0) or (is_leaf (right_branch tr0))
    then ()
  else if (level (right_branch (right_branch tr0)) = level tr0)
    then let (l0,xy0,tr1,tr2) = dest_node tr0 in
         let (l2,xy2,tr21,tr22) = dest_node tr2 in
         (tr0 := Node (l2 + 1, xy2, ref (Node (l0,xy0,tr1,tr21)), tr22))
    else ();;


(* dltree_insert : 'a * 'b -> ('a,'b) dltree -> unit                          *)
(*                                                                            *)
(* This inserts the supplied single indexed item into a given lookup tree.    *)
(* Fails if the tree already contains an entry for the supplied index.        *)

let rec dltree_insert ((x,_) as xy) tr0 =
  match !tr0 with
    Node (_,(x0,_),tr1,tr2)
       -> ((if (x < x0)
              then (* Put into left branch *)
                   dltree_insert xy tr1
            else if (x0 < x)
              then (* Put into right branch *)
                   dltree_insert xy tr2
              else (* Element already in tree *)
                   failwith "dltree_insert: Already in tree");
           (* Rebalance from the node *)
           skew tr0;
           split tr0)
  | Leaf
       -> (* Put element here *)
          (tr0 := Node (1, xy, ref Leaf, ref Leaf));;


(* dltree_remove : 'a -> ('a,'b) dltree -> unit                               *)
(*                                                                            *)
(* This removes the entry at the supplied index in a given lookup tree.       *)
(* Fails if the tree does not contain an entry for the supplied index.        *)

let decrease_level tr0 =
  let (l0,xy0,tr1,tr2) = dest_node tr0 in
  let n = 1 + min (level tr1) (level tr2) in
  if (n < l0) && (is_node tr2)
    then (tr0 := Node (n,xy0,tr1,tr2);
          if (is_node tr2)
            then let (l2,xy2,tr21,tr22) = dest_node tr2 in
                 if (n < level tr2)
                   then (tr2 := Node (n,xy2,tr21,tr22))
                   else ()
            else ())
    else ();;

let rec dltree_remove x tr0 =
  match !tr0 with
    Node (l0,((x0,_) as xy0),tr1,tr2)
       -> ((if (x < x0)
              then (* Element should be in left branch *)
                   dltree_remove x tr1
            else if (x0 < x)
              then (* Element should be in right branch *)
                   dltree_remove x tr2
              else (* Node holds element to be removed *)
                   if (is_leaf tr1) && (is_leaf tr2)
                     then (tr0 := Leaf)
                   else if (is_leaf tr1)
                     then let (x2,_) as xy2 = leftmost_elem xy0 tr2 in
                          (dltree_remove x2 tr2;
                           tr0 := Node (l0,xy2,tr1,tr2))
                     else let (x1,_) as xy1 = rightmost_elem xy0 tr1 in
                          (dltree_remove x1 tr1;
                           tr0 := Node (l0,xy1,tr1,tr2)));
           (if (is_node tr0)
              then (decrease_level tr0;
                    skew tr0;
                    skew tr2;
                    (if (is_node tr2) then skew (right_branch tr2)
                                      else ());
                    split tr0;
                    split tr2)
              else ()))
  | Leaf
       -> (* Element not in tree *)
          failwith "dltree_remove: Not in tree";;


(* dltree_elem : 'a -> ('a,'b) dltree -> 'a * 'b                              *)
(*                                                                            *)
(* This returns the index and item held at the supplied index in a given      *)
(* lookup tree.  Fails if the tree has no entry for the supplied index.       *)

let rec dltree_elem x0 tr =
  match !tr with
    Node (_, ((x,_) as xy), tr1, tr2)
         -> if (x0 < x)
              then dltree_elem x0 tr1
            else if (x < x0)
              then dltree_elem x0 tr2
              else xy
  | Leaf -> failwith "dltree_elem: Not in tree";;


(* dltree_lookup : 'a -> ('a,'b) dltree -> 'b                                 *)
(*                                                                            *)
(* This returns the item held at the supplied index in a given lookup tree.   *)

let rec dltree_lookup x0 tr =
  let (_,y) = try  dltree_elem x0 tr
              with Failure _ -> failwith "dltree_lookup: Not in tree" in
  y;;


(* dltree_mem : 'a -> ('a,'b) dltree -> bool                                  *)
(*                                                                            *)
(* This returns "true" iff the supplied index occurs in a given lookup tree.  *)

let rec dltree_mem x0 tr =
  match !tr with
    Node (_,(x,_),tr1,tr2)
         -> if (x0 < x)
              then dltree_mem x0 tr1
            else if (x < x0)
              then dltree_mem x0 tr2
              else true
  | Leaf -> false;;


end;;