-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSplay.dart
executable file
·305 lines (269 loc) · 8.25 KB
/
Splay.dart
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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// This benchmark is based on a JavaScript log processing module used
// by the V8 profiler to generate execution time profiles for runs of
// JavaScript applications, and it effectively measures how fast the
// JavaScript engine is at allocating nodes and reclaiming the memory
// used for old nodes. Because of the way splay trees work, the engine
// also has to deal with a lot of changes to the large tree object
// graph.
import "dart:math";
//import "../../common/dart/BenchmarkBase.dart";
void main() {
Splay.main();
}
class Splay {
//const Splay() : super("Splay");
// Configuration.
static final int kTreeSize = 8000;
static final int kTreeModifications = 80;
static final int kTreePayloadDepth = 5;
static SplayTree tree;
static Random rnd = new Random(12345);
// Insert new node with a unique key.
static num insertNewNode() {
num key;
do {
key = rnd.nextDouble();
} while (tree.find(key) != null);
Payload payload = Payload.generate(kTreePayloadDepth, key.toString());
tree.insert(key, payload);
return key;
}
static void mysetup() {
tree = new SplayTree();
for (int i = 0; i < kTreeSize; i++) insertNewNode();
}
static void tearDown() {
// Allow the garbage collector to reclaim the memory
// used by the splay tree no matter how we exit the
// tear down function.
List<num> keys = tree.exportKeys();
tree = null;
// Verify that the splay tree has the right size.
int length = keys.length;
if (length != kTreeSize) throw new Error("Splay tree has wrong size");
// Verify that the splay tree has sorted, unique keys.
for (int i = 0; i < length - 1; i++) {
if (keys[i] >= keys[i + 1]) throw new Error("Splay tree not sorted");
}
}
void warmup() {
exercise();
}
void exercise() {
// Replace a few nodes in the splay tree.
for (int i = 0; i < kTreeModifications; i++) {
num key = insertNewNode();
Node greatest = tree.findGreatestLessThan(key);
if (greatest == null) tree.remove(key);
else tree.remove(greatest.key);
}
}
static void main() {
mysetup();
new Splay().report();
tearDown();
}
}
class Leaf {
Leaf(String tag) {
string = "String for key $tag in leaf node";
array = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
}
String string;
List<num> array;
}
class Payload {
Payload(this.left, this.right);
var left, right;
static generate(depth, tag) {
if (depth == 0) return new Leaf(tag);
return new Payload(generate(depth - 1, tag),
generate(depth - 1, tag));
}
}
class Error implements Exception {
const Error(this.message);
final String message;
}
/**
* A splay tree is a self-balancing binary search tree with the additional
* property that recently accessed elements are quick to access again.
* It performs basic operations such as insertion, look-up and removal
* in O(log(n)) amortized time.
*/
class SplayTree {
SplayTree();
/**
* Inserts a node into the tree with the specified [key] and value if
* the tree does not already contain a node with the specified key. If
* the value is inserted, it becomes the root of the tree.
*/
void insert(num key, value) {
if (isEmpty) {
root = new Node(key, value);
return;
}
// Splay on the key to move the last node on the search path for
// the key to the root of the tree.
splay(key);
if (root.key == key) return;
Node node = new Node(key, value);
if (key > root.key) {
node.left = root;
node.right = root.right;
root.right = null;
} else {
node.right = root;
node.left = root.left;
root.left = null;
}
root = node;
}
/**
* Removes a node with the specified key from the tree if the tree
* contains a node with this key. The removed node is returned. If
* [key] is not found, an exception is thrown.
*/
Node remove(num key) {
if (isEmpty) throw new Error('Key not found: $key');
splay(key);
if (root.key != key) throw new Error('Key not found: $key');
Node removed = root;
if (root.left == null) {
root = root.right;
} else {
Node right = root.right;
root = root.left;
// Splay to make sure that the new root has an empty right child.
splay(key);
// Insert the original right child as the right child of the new
// root.
root.right = right;
}
return removed;
}
/**
* Returns the node having the specified [key] or null if the tree doesn't
* contain a node with the specified [key].
*/
Node find(num key) {
if (isEmpty) return null;
splay(key);
return root.key == key ? root : null;
}
/**
* Returns the Node having the maximum key value.
*/
Node findMax([Node start]) {
if (isEmpty) return null;
Node current = null == start ? root : start;
while (current.right != null) current = current.right;
return current;
}
/**
* Returns the Node having the maximum key value that
* is less than the specified [key].
*/
Node findGreatestLessThan(num key) {
if (isEmpty) return null;
// Splay on the key to move the node with the given key or the last
// node on the search path to the top of the tree.
splay(key);
// Now the result is either the root node or the greatest node in
// the left subtree.
if (root.key < key) return root;
if (root.left != null) return findMax(root.left);
return null;
}
/**
* Perform the splay operation for the given key. Moves the node with
* the given key to the top of the tree. If no node has the given
* key, the last node on the search path is moved to the top of the
* tree. This is the simplified top-down splaying algorithm from:
* "Self-adjusting Binary Search Trees" by Sleator and Tarjan
*/
void splay(num key) {
if (isEmpty) return;
// Create a dummy node. The use of the dummy node is a bit
// counter-intuitive: The right child of the dummy node will hold
// the L tree of the algorithm. The left child of the dummy node
// will hold the R tree of the algorithm. Using a dummy node, left
// and right will always be nodes and we avoid special cases.
final Node dummy = new Node(null, null);
Node left = dummy;
Node right = dummy;
Node current = root;
while (true) {
if (key < current.key) {
if (current.left == null) break;
if (key < current.left.key) {
// Rotate right.
Node tmp = current.left;
current.left = tmp.right;
tmp.right = current;
current = tmp;
if (current.left == null) break;
}
// Link right.
right.left = current;
right = current;
current = current.left;
} else if (key > current.key) {
if (current.right == null) break;
if (key > current.right.key) {
// Rotate left.
Node tmp = current.right;
current.right = tmp.left;
tmp.left = current;
current = tmp;
if (current.right == null) break;
}
// Link left.
left.right = current;
left = current;
current = current.right;
} else {
break;
}
}
// Assemble.
left.right = current.left;
right.left = current.right;
current.left = dummy.right;
current.right = dummy.left;
root = current;
}
/**
* Returns a list with all the keys of the tree.
*/
List<num> exportKeys() {
List<num> result = [];
if (!isEmpty) root.traverse((Node node) => result.add(node.key));
return result;
}
// Tells whether the tree is empty.
bool get isEmpty => null == root;
// Pointer to the root node of the tree.
Node root;
}
class Node {
Node(this.key, this.value);
final num key;
final Object value;
Node left, right;
/**
* Performs an ordered traversal of the subtree starting here.
*/
void traverse(void f(Node n)) {
Node current = this;
while (current != null) {
Node left = current.left;
if (left != null) left.traverse(f);
f(current);
current = current.right;
}
}
}