| 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 |