OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library priority_queue; | 5 library priority_queue; |
6 | 6 |
7 import 'dart:collection'; | 7 import 'dart:collection'; |
8 import 'dart:math'; | 8 import 'dart:math'; |
9 | 9 |
10 /** | 10 /** |
11 * A priority used for the priority queue. Subclasses only need to implement | 11 * A priority used for the priority queue. Subclasses only need to implement |
12 * the compareTo function. | 12 * the compareTo function. |
13 */ | 13 */ |
14 abstract class Priority implements Comparable { | 14 abstract class Priority implements Comparable { |
15 /** | 15 /** |
16 * Return < 0 if other is bigger, >0 if other is smaller, 0 if they are equal. | 16 * Return < 0 if other is bigger, >0 if other is smaller, 0 if they are equal. |
17 */ | 17 */ |
18 int compareTo(Priority other); | 18 int compareTo(Priority other); |
19 bool operator<(Priority other) => compareTo(other) < 0; | 19 bool operator <(Priority other) => compareTo(other) < 0; |
20 bool operator>(Priority other) => compareTo(other) > 0; | 20 bool operator >(Priority other) => compareTo(other) > 0; |
21 bool operator==(Priority other) => compareTo(other) == 0; | 21 bool operator ==(Priority other) => compareTo(other) == 0; |
22 } | 22 } |
23 | 23 |
24 /** | 24 /** |
25 * Priority based on integers. | 25 * Priority based on integers. |
26 */ | 26 */ |
27 class IntPriority extends Priority { | 27 class IntPriority extends Priority { |
28 int priority; | 28 int priority; |
29 IntPriority(int this.priority); | 29 IntPriority(int this.priority); |
30 | 30 |
31 int compareTo(IntPriority other) { | 31 int compareTo(IntPriority other) { |
32 return priority - other.priority; | 32 return priority - other.priority; |
33 } | 33 } |
| 34 |
34 String toString() => "$priority"; | 35 String toString() => "$priority"; |
35 } | 36 } |
36 | 37 |
37 /** | 38 /** |
38 * An element of a priority queue. The type is used restriction based | 39 * An element of a priority queue. The type is used restriction based |
39 * querying of the queues. | 40 * querying of the queues. |
40 */ | 41 */ |
41 abstract class TypedElement<V> { | 42 abstract class TypedElement<V> { |
42 bool typeEquals(var other); | 43 bool typeEquals(var other); |
43 } | 44 } |
44 | 45 |
45 class StringTypedElement<V> extends TypedElement{ | 46 class StringTypedElement<V> extends TypedElement { |
46 String type; | 47 String type; |
47 V value; | 48 V value; |
48 StringTypedElement(String this.type, V this.value); | 49 StringTypedElement(String this.type, V this.value); |
49 bool typeEquals(String otherType) => otherType == type; | 50 bool typeEquals(String otherType) => otherType == type; |
50 String toString() => "<Type: $type, Value: $value>"; | 51 String toString() => "<Type: $type, Value: $value>"; |
51 } | 52 } |
52 | 53 |
53 | |
54 /** | 54 /** |
55 * A priority node in a priority queue. A priority node contains all of the | 55 * A priority node in a priority queue. A priority node contains all of the |
56 * values for a given priority in a given queue. It is part of a linked | 56 * values for a given priority in a given queue. It is part of a linked |
57 * list of nodes, with prev and next pointers. | 57 * list of nodes, with prev and next pointers. |
58 */ | 58 */ |
59 class PriorityNode<N extends TypedElement, T extends Priority> { | 59 class PriorityNode<N extends TypedElement, T extends Priority> { |
60 T priority; | 60 T priority; |
61 Queue<N> values; | 61 Queue<N> values; |
62 PriorityNode prev; | 62 PriorityNode prev; |
63 PriorityNode next; | 63 PriorityNode next; |
64 PriorityNode(N initialNode, T this.priority) | 64 PriorityNode(N initialNode, T this.priority) : values = new Queue<N>() { |
65 : values = new Queue<N>() { | |
66 add(initialNode); | 65 add(initialNode); |
67 } | 66 } |
68 | 67 |
69 void add(N n) => values.add(n); | 68 void add(N n) => values.add(n); |
70 | 69 |
71 bool remove(N n) => values.remove(n); | 70 bool remove(N n) => values.remove(n); |
72 | 71 |
73 N removeFirst() => values.removeFirst(); | 72 N removeFirst() => values.removeFirst(); |
74 | 73 |
75 bool get isEmpty => values.isEmpty; | 74 bool get isEmpty => values.isEmpty; |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 for (var queue in restrictedQueues) { | 177 for (var queue in restrictedQueues) { |
179 if (queue.first.value == value) { | 178 if (queue.first.value == value) { |
180 queue.add(value, priority); | 179 queue.add(value, priority); |
181 } | 180 } |
182 } | 181 } |
183 mainQueue.add(value, priority); | 182 mainQueue.add(value, priority); |
184 } | 183 } |
185 | 184 |
186 bool get isEmpty => restrictedQueues.length + mainQueue.length == 0; | 185 bool get isEmpty => restrictedQueues.length + mainQueue.length == 0; |
187 | 186 |
188 int get length => restrictedQueues.fold(0, (v, e) => v + e.length) + | 187 int get length => |
189 mainQueue.length; | 188 restrictedQueues.fold(0, (v, e) => v + e.length) + mainQueue.length; |
190 | 189 |
191 PriorityQueue getRestricted(List<N> restrictions) { | 190 PriorityQueue getRestricted(List<N> restrictions) { |
192 var current = null; | 191 var current = null; |
193 // Find highest restricted priority. | 192 // Find highest restricted priority. |
194 for (var queue in restrictedQueues) { | 193 for (var queue in restrictedQueues) { |
195 if (!restrictions.any((e) => queue.head.first.typeEquals(e))) { | 194 if (!restrictions.any((e) => queue.head.first.typeEquals(e))) { |
196 if (current == null || queue.firstPriority > current.firstPriority) { | 195 if (current == null || queue.firstPriority > current.firstPriority) { |
197 current = queue; | 196 current = queue; |
198 } else if (current.firstPriority == queue.firstPriority) { | 197 } else if (current.firstPriority == queue.firstPriority) { |
199 current = queue.length > current.length ? queue : current; | 198 current = queue.length > current.length ? queue : current; |
200 } | 199 } |
201 } | 200 } |
202 } | 201 } |
203 return current; | 202 return current; |
204 } | 203 } |
205 | 204 |
206 N get first { | 205 N get first { |
207 if (isEmpty) throw "Trying to remove node from empty queue"; | 206 if (isEmpty) throw "Trying to remove node from empty queue"; |
208 var candidate = getRestricted([]); | 207 var candidate = getRestricted([]); |
209 if (candidate != null && | 208 if (candidate != null && |
210 (mainQueue.isEmpty || | 209 (mainQueue.isEmpty || |
211 mainQueue.firstPriority < candidate.firstPriority)) { | 210 mainQueue.firstPriority < candidate.firstPriority)) { |
212 return candidate.first; | 211 return candidate.first; |
213 } | 212 } |
214 return mainQueue.isEmpty ? null : mainQueue.first; | 213 return mainQueue.isEmpty ? null : mainQueue.first; |
215 } | 214 } |
216 | 215 |
217 /** | 216 /** |
218 * Returns the node that under the given set of restrictions. | 217 * Returns the node that under the given set of restrictions. |
219 * If the queue is empty this function throws. | 218 * If the queue is empty this function throws. |
220 * If the queue is not empty, but no node exists that adheres to the | 219 * If the queue is not empty, but no node exists that adheres to the |
221 * restrictions we return null. | 220 * restrictions we return null. |
222 */ | 221 */ |
223 N removeFirst({List restrictions: const []}) { | 222 N removeFirst({List restrictions: const []}) { |
224 if (isEmpty) throw "Trying to remove node from empty queue"; | 223 if (isEmpty) throw "Trying to remove node from empty queue"; |
225 var candidate = getRestricted(restrictions); | 224 var candidate = getRestricted(restrictions); |
226 | 225 |
227 if (candidate != null && | 226 if (candidate != null && |
228 (mainQueue.isEmpty || | 227 (mainQueue.isEmpty || |
229 mainQueue.firstPriority < candidate.firstPriority)) { | 228 mainQueue.firstPriority < candidate.firstPriority)) { |
230 var value = candidate.removeFirst(); | 229 var value = candidate.removeFirst(); |
231 if (candidate.isEmpty) restrictedQueues.remove(candidate); | 230 if (candidate.isEmpty) restrictedQueues.remove(candidate); |
232 return value; | 231 return value; |
233 } | 232 } |
234 while (!mainQueue.isEmpty) { | 233 while (!mainQueue.isEmpty) { |
235 var currentPriority = mainQueue.firstPriority; | 234 var currentPriority = mainQueue.firstPriority; |
236 var current = mainQueue.removeFirst(); | 235 var current = mainQueue.removeFirst(); |
237 if (!restrictions.any((e) => current.typeEquals(e))) { | 236 if (!restrictions.any((e) => current.typeEquals(e))) { |
238 return current; | 237 return current; |
239 } else { | 238 } else { |
240 var restrictedQueue = restrictedQueues | 239 var restrictedQueue = restrictedQueues.firstWhere( |
241 .firstWhere((e) => current.typeEquals(e.first.type), | 240 (e) => current.typeEquals(e.first.type), |
242 orElse: () => null); | 241 orElse: () => null); |
243 if (restrictedQueue == null) { | 242 if (restrictedQueue == null) { |
244 restrictedQueue = new PriorityQueue<N, P>(); | 243 restrictedQueue = new PriorityQueue<N, P>(); |
245 restrictedQueues.add(restrictedQueue); | 244 restrictedQueues.add(restrictedQueue); |
246 } | 245 } |
247 restrictedQueue.add(current, currentPriority); | 246 restrictedQueue.add(current, currentPriority); |
248 } | 247 } |
249 } | 248 } |
250 return null; | 249 return null; |
251 } | 250 } |
252 | 251 |
(...skipping 15 matching lines...) Expand all Loading... |
268 /// TEMPORARY TESTING AND PERFORMANCE | 267 /// TEMPORARY TESTING AND PERFORMANCE |
269 void main([args]) { | 268 void main([args]) { |
270 stress(new RestrictViewPriorityQueue<StringTypedElement, IntPriority>()); | 269 stress(new RestrictViewPriorityQueue<StringTypedElement, IntPriority>()); |
271 } | 270 } |
272 | 271 |
273 void stress(queue) { | 272 void stress(queue) { |
274 final int SIZE = 50000; | 273 final int SIZE = 50000; |
275 Random random = new Random(29); | 274 Random random = new Random(29); |
276 | 275 |
277 var priorities = [1, 2, 3, 16, 32, 42, 56, 57, 59, 90]; | 276 var priorities = [1, 2, 3, 16, 32, 42, 56, 57, 59, 90]; |
278 var values = [new StringTypedElement('safari', 'foo'), | 277 var values = [ |
279 new StringTypedElement('ie', 'bar'), | 278 new StringTypedElement('safari', 'foo'), |
280 new StringTypedElement('ff', 'foobar'), | 279 new StringTypedElement('ie', 'bar'), |
281 new StringTypedElement('dartium', 'barfoo'), | 280 new StringTypedElement('ff', 'foobar'), |
282 new StringTypedElement('chrome', 'hest'), | 281 new StringTypedElement('dartium', 'barfoo'), |
283 new StringTypedElement('drt', 'fisk')]; | 282 new StringTypedElement('chrome', 'hest'), |
| 283 new StringTypedElement('drt', 'fisk') |
| 284 ]; |
284 | 285 |
285 var restricted = ['safari', 'chrome']; | 286 var restricted = ['safari', 'chrome']; |
286 | 287 |
287 | |
288 void addRandom() { | 288 void addRandom() { |
289 queue.add(values[random.nextInt(values.length)], | 289 queue.add(values[random.nextInt(values.length)], |
290 new IntPriority(priorities[random.nextInt(priorities.length)])); | 290 new IntPriority(priorities[random.nextInt(priorities.length)])); |
291 } | 291 } |
292 | 292 |
293 var stopwatch = new Stopwatch()..start(); | 293 var stopwatch = new Stopwatch()..start(); |
294 while(queue.length < SIZE) { | 294 while (queue.length < SIZE) { |
295 addRandom(); | 295 addRandom(); |
296 } | 296 } |
297 | 297 |
298 stopwatch.stop(); | 298 stopwatch.stop(); |
299 print("Adding took: ${stopwatch.elapsedMilliseconds}"); | 299 print("Adding took: ${stopwatch.elapsedMilliseconds}"); |
300 print("Queue length: ${queue.length}"); | 300 print("Queue length: ${queue.length}"); |
301 | 301 |
302 stopwatch = new Stopwatch()..start(); | 302 stopwatch = new Stopwatch()..start(); |
303 while(queue.length > 0) { | 303 while (queue.length > 0) { |
304 queue.removeFirst(); | 304 queue.removeFirst(); |
305 } | 305 } |
306 stopwatch.stop(); | 306 stopwatch.stop(); |
307 print("Remowing took: ${stopwatch.elapsedMilliseconds}"); | 307 print("Remowing took: ${stopwatch.elapsedMilliseconds}"); |
308 print("Queue length: ${queue.length}"); | 308 print("Queue length: ${queue.length}"); |
309 | 309 |
310 | |
311 print("Restricted add/remove"); | 310 print("Restricted add/remove"); |
312 while(queue.length < SIZE) { | 311 while (queue.length < SIZE) { |
313 addRandom(); | 312 addRandom(); |
314 } | 313 } |
315 | 314 |
316 for (int i = 0; i < SIZE; i++) { | 315 for (int i = 0; i < SIZE; i++) { |
317 if (random.nextDouble() < 0.5) { | 316 if (random.nextDouble() < 0.5) { |
318 queue.removeFirst(restrictions: restricted); | 317 queue.removeFirst(restrictions: restricted); |
319 } else { | 318 } else { |
320 queue.removeFirst(); | 319 queue.removeFirst(); |
321 } | 320 } |
322 addRandom(); | 321 addRandom(); |
323 } | 322 } |
324 } | 323 } |
OLD | NEW |