Oolite 1.91.0.7604-240417-a536cbe
Loading...
Searching...
No Matches
OOPriorityQueue.m
Go to the documentation of this file.
1/*
2
3OOPriorityQueue.m
4
5
6Copyright (C) 2007-2013 Jens Ayton
7
8Permission is hereby granted, free of charge, to any person obtaining a copy
9of this software and associated documentation files (the "Software"), to deal
10in the Software without restriction, including without limitation the rights
11to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12copies of the Software, and to permit persons to whom the Software is
13furnished to do so, subject to the following conditions:
14
15The above copyright notice and this permission notice shall be included in all
16copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24SOFTWARE.
25
26*/
27
28#include <assert.h>
29
30#import "OOPriorityQueue.h"
32
33
34/* Capacity grows by 50% each time. kMinCapacity must be at least 2 or Bad
35 Things will happen. Some examples of growth patterns based on kMinCapacity:
36 2: 2 3 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066...
37 4: 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599...
38 8: 8 12 18 27 40 60 90 135 202 303 454 681 1021 1531 2296...
39 16: 16 24 36 54 81 121 181 271 406 609 913 1369 2053 3079...
40 32: 32 48 72 108 162 243 364 546 819 1228 1842 2763 4144...
41
42 Special cases: when an OOPriorityQueue is copied, the copy's capacity is
43 set to the same as its count, which may be less than kMinCapacity. However,
44 when it is grown, it will be set to at least kMinCapacity (except in the
45 case of a reallocation failure, in which case an attempt will be made to
46 grow capacity by one element).
47*/
48enum
49{
50 kMinCapacity = 16
51};
52
53
54/* Functions to navigate the binary heap.
55 Using one-based indices, the following hold:
56 * The left child of a node, L(n), is 2n.
57 * The right child of a node, R(n), is 2n + 1.
58 * The parent of a node, P(n), is n/2 for n > 1.
59
60 Using zero-based indices, we must convert to and from one-based indices to
61 perform these calculations, giving:
62 * Lz(n) = L(n+1)-1 = (2(n+1))-1 = 2n+1
63 * Rz(n) = R(n+1)-1 = (2(n+1)+1)-1 = 2n+2
64 * Pz(n) = P(n+1)-1 = ((n+1)/2)-1
65*/
66
67
68OOINLINE NSUInteger PQLeftChild(NSUInteger n) INLINE_CONST_FUNC;
69OOINLINE NSUInteger PQRightChild(NSUInteger n) INLINE_CONST_FUNC;
70OOINLINE NSUInteger PQParent(NSUInteger n) INLINE_CONST_FUNC;
71
72OOINLINE NSUInteger PQLeftChild(NSUInteger n)
73{
74 return (n << 1) + 1;
75}
76
77OOINLINE NSUInteger PQRightChild(NSUInteger n)
78{
79 return (n << 1) + 2;
80}
81
82
83OOINLINE NSUInteger PQParent(NSUInteger n)
84{
85 return ((n + 1) >> 1) - 1;
86}
87
88
89typedef NSComparisonResult (*CompareIMP)(id self, SEL _cmd, id other);
90
91
92OOINLINE NSComparisonResult PQCompare(id a, id b, SEL comparator)
93{
94 CompareIMP compare = NULL;
95 NSComparisonResult result;
96
97 // This is equivalent to [a performSelector:comparator withObject:b], except the resulting value isn't an object.
98 compare = (CompareIMP)[a methodForSelector:comparator];
99 result = compare(a, comparator, b);
100 return result;
101}
102
103
104// Private priority queue methods.
105@interface OOPriorityQueue (Private)
106
107- (void) makeObjectsPerformSelector:(SEL)selector;
108
109- (void) bubbleUpFrom:(NSUInteger)i;
110- (void) bubbleDownFrom:(NSUInteger)i;
111
112- (void) growBuffer;
113- (void) shrinkBuffer;
114
115- (void)removeObjectAtIndex:(NSUInteger)i;
116
117#if OO_DEBUG
118- (void) appendDebugDataToString:(NSMutableString *)string index:(NSUInteger)i depth:(NSUInteger)depth;
119#endif
120
121@end
122
123
124// NSEnumerator subclass to pull objects from queue.
125@interface OOPriorityQueueEnumerator: NSEnumerator
126{
127@private
129}
130
131- (id) initWithPriorityQueue:(OOPriorityQueue *)queue;
132
133@end
134
135
136@implementation OOPriorityQueue
137
138+ (instancetype) queueWithComparator:(SEL)comparator
139{
140 return [[[self alloc] initWithComparator:comparator] autorelease];
141}
142
143
144- (id) initWithComparator:(SEL)comparator
145{
146 if (comparator == NULL)
147 {
148 [self release];
149 return nil;
150 }
151
152 self = [super init];
153 if (self != nil)
154 {
155 _comparator = comparator;
156 }
157
158 return self;
159}
160
161
162- (id) init
163{
164 return [self initWithComparator:@selector(compare:)];
165}
166
167
168- (void) dealloc
169{
170 [self makeObjectsPerformSelector:@selector(release)];
171 free(_heap);
172
173 [super dealloc];
174}
175
176
177- (NSString *) description
178{
179 return [NSString stringWithFormat:@"<%@ %p>{count=%lu, capacity=%lu}", [self class], self, _count, _capacity];
180}
181
182
183#if OO_DEBUG
184- (NSString *) debugDescription
185{
186 NSMutableString *result = nil;
187
188 result = [NSMutableString string];
189 [result appendFormat:@"<%@ %p> (count=%lu, capacity=%lu, comparator=%@)", [self class], self, _count, _capacity, NSStringFromSelector(_comparator)];
190
191 if (_count != 0)
192 {
193 [result appendString:@"\n{\n"];
194 [self appendDebugDataToString:result index:0 depth:0];
195 [result appendString:@"}"];
196 }
197 else
198 {
199 [result appendString:@" {}"];
200 }
201 return result;
202}
203#endif
204
205
206- (BOOL) isEqual:(id)object
207{
208 NSUInteger i;
209 OOPriorityQueue *selfCopy = nil, *otherCopy = nil;
210 BOOL identical = YES;
211
212 if (object == self) return YES;
213 if (![object isKindOfClass:[self class]]) return NO;
214
215 if (_count != [object count]) return NO;
216 if (_count == 0) return YES;
217
218 selfCopy = [self copy];
219 otherCopy = [object copy];
220 i = _count;
221 while (i--)
222 {
223 if (![[selfCopy nextObject] isEqual:[otherCopy nextObject]])
224 {
225 identical = NO;
226 break;
227 }
228 }
229 [selfCopy release];
230 [otherCopy release];
231
232 return identical;
233}
234
235
236- (NSUInteger) hash
237{
238 if (_count == 0) return NSNotFound;
239 return _count ^ [_heap[0] hash];
240}
241
242
243- (id) copyWithZone:(NSZone *)zone
244{
245 OOPriorityQueue *copy = nil;
246
247 copy = [[self class] allocWithZone:zone];
248 if (copy != nil)
249 {
250 copy->_comparator = _comparator;
251 copy->_count = _count;
252 copy->_capacity = _count;
253
254 copy->_heap = malloc(_count * sizeof(id));
255 if (copy->_heap != NULL)
256 {
257 memcpy(copy->_heap, _heap, _count * sizeof(id));
258 [copy makeObjectsPerformSelector:@selector(retain)];
259 }
260 else if (_count != 0)
261 {
262 [copy release];
263 copy = nil;
264 }
265 }
266
267 return copy;
268}
269
270
271- (void) addObject:(id)object
272{
273 NSUInteger i;
274
275 // Validate object
276 if (object == nil)
277 {
278 [NSException raise:NSInvalidArgumentException
279 format:@"Attempt to insert nil into OOPriorityQueue."];
280 }
281
282 if (![object respondsToSelector:_comparator])
283 {
284 [NSException raise:NSInvalidArgumentException
285 format:@"Attempt to insert object (%@) which does not support comparator %@ into OOPriorityQueue.",
286 object, NSStringFromSelector(_comparator)];
287 }
288
289 // Ensure there is sufficent space.
290 if (_count == _capacity) [self growBuffer];
291
292 // insert object at end of buffer.
293 i = _count++;
294 _heap[i] = object;
295
296 [self bubbleUpFrom:i];
297 [object retain];
298}
299
300
301- (void) removeObject:(id)object
302{
303 NSUInteger i;
304
305 /* Perform linear search for object (using comparator). A depth-first
306 search could skip leaves of lower priority, but I don't expect this to
307 be called very often.
308 */
309
310 if (object == nil) return;
311
312 for (i = 0; i < _count; ++i)
313 {
314 if (PQCompare(object, _heap[i], _comparator) == 0)
315 {
316 [self removeObjectAtIndex:i];
317 }
318 }
319}
320
321
322- (void) removeExactObject:(id)object
323{
324 NSUInteger i;
325
326 if (object == nil) return;
327
328 for (i = 0; i < _count; ++i)
329 {
330 if (object == _heap[i])
331 {
332 [self removeObjectAtIndex:i];
333 }
334 }
335}
336
337
338- (NSUInteger) count
339{
340 return _count;
341}
342
343
344- (id) nextObject
345{
346 id result = [self peekAtNextObject];
347 [self removeNextObject];
348 return result;
349}
350
351
352- (id) peekAtNextObject
353{
354 if (_count == 0) return nil;
355// return [[_heap[0] retain] autorelease];
356 return _heap[0];
357}
358
359
360- (void) removeNextObject
361{
362 [self removeObjectAtIndex:0];
363}
364
365
366- (void) addObjects:(id)collection
367{
368 id value = nil;
369
370 if ([collection respondsToSelector:@selector(objectEnumerator)]) collection = [collection objectEnumerator];
371 if (![collection respondsToSelector:@selector(nextObject)]) return;
372
373 while ((value = [collection nextObject])) [self addObject:value];
374}
375
376
377- (NSArray *) sortedObjects
378{
379 return [[self objectEnumerator] allObjects];
380}
381
382
383- (NSEnumerator *) objectEnumerator
384{
385 return [[[OOPriorityQueueEnumerator alloc] initWithPriorityQueue:self] autorelease];
386}
387
388@end
389
390
391@implementation OOPriorityQueue (Private)
392
393- (void) makeObjectsPerformSelector:(SEL)selector
394{
395 NSUInteger i;
396
397 if (selector == NULL) return;
398
399 for (i = 0; i != _count; ++i)
400 {
401 [_heap[i] performSelector:selector];
402 }
403}
404
405
406- (void) bubbleUpFrom:(NSUInteger)i
407{
408 NSUInteger pi;
409 id obj = nil, par = nil;
410
411 while (0 < i)
412 {
413 pi = PQParent(i);
414 obj = _heap[i];
415 par = _heap[pi];
416
417 if (PQCompare(obj, par, _comparator) < 0)
418 {
419 _heap[i] = par;
420 _heap[pi] = obj;
421 i = pi;
422 }
423 else break;
424 }
425}
426
427
428- (void) bubbleDownFrom:(NSUInteger)i
429{
430 NSUInteger end = _count - 1;
431 NSUInteger li, ri, next;
432 id obj = nil;
433
434 obj = _heap[i];
435 while (PQLeftChild(i) <= end)
436 {
437 li = PQLeftChild(i);
438 ri = PQRightChild(i);
439
440 // If left child has lower priority than right child, or there is only one child...
441 if (li == end || PQCompare(_heap[li], _heap[ri], _comparator) < 0)
442 {
443 next = li;
444 }
445 else
446 {
447 next = ri;
448 }
449
450 if (PQCompare(_heap[next], obj, _comparator) < 0)
451 {
452 // Exchange parent with lowest-priority child
453 _heap[i] = _heap[next];
454 _heap[next] = obj;
455 i = next;
456 }
457 else break;
458 }
459}
460
461
462- (void) growBuffer
463{
464 id *newBuffer = NULL;
465 NSUInteger newCapacity;
466
467 newCapacity = _capacity * 3 / 2;
468 if (newCapacity < kMinCapacity) newCapacity = kMinCapacity;
469
470 // Note: realloc(NULL, size) with non-zero size is equivalent to malloc(size), so this is OK starting from a NULL buffer.
471 newBuffer = realloc(_heap, newCapacity * sizeof(id));
472 if (newBuffer == NULL)
473 {
474 // Attempt to grow by just one waffer-thin slot.
475 newCapacity = _capacity + 1;
476 newBuffer = realloc(_heap, newCapacity * sizeof(id));
477
478 if (newBuffer == NULL)
479 {
480 // Failed to grow.
481 [NSException raise:NSMallocException
482 format:@"Could not expand capacity of OOPriorityQueue."];
483 }
484 }
485
486 _heap = newBuffer;
487 _capacity = newCapacity;
488
489 assert(_count < _capacity);
490}
491
492
493- (void)shrinkBuffer
494{
495 NSUInteger amountToRemove;
496 id *newBuffer = NULL;
497 NSUInteger newCapacity;
498
499 if (kMinCapacity < _capacity)
500 {
501 // Remove two thirds of free space, if at least three slots are free.
502 amountToRemove = (_capacity - _count) * 2 / 3;
503 if (2 < amountToRemove)
504 {
505 newCapacity = _capacity - amountToRemove;
506 newBuffer = realloc(_heap, newCapacity * sizeof(id));
507 if (newBuffer != NULL)
508 {
509 _heap = newBuffer;
510 _capacity = newCapacity;
511 }
512 }
513 }
514}
515
516
517- (void)removeObjectAtIndex:(NSUInteger)i
518{
519 id object = nil;
520
521 if (_count <= i) return;
522
523 object = _heap[i];
524 if (i < --_count)
525 {
526 // Overwrite object with last object in array
527 _heap[i] = _heap[_count];
528
529 // Push previously-last object down until tree is partially ordered.
530 [self bubbleDownFrom:i];
531 }
532 else
533 {
534 // Special case: removing last (or only) object. No bubbling needed.
535 }
536
537 [object autorelease];
538 if (_count * 2 <= _capacity) [self shrinkBuffer];
539}
540
541
542#if OO_DEBUG
543- (void) appendDebugDataToString:(NSMutableString *)string index:(NSUInteger)i depth:(NSUInteger)depth
544{
545 NSUInteger spaces;
546
547 if (_count <= i) return;
548
549 spaces = 2 + depth;
550 while (spaces--) [string appendString:@" "];
551 [string appendString:[_heap[i] description]];
552 [string appendString:@"\n"];
553
554 [self appendDebugDataToString:string index:PQLeftChild(i) depth:depth + 1];
555 [self appendDebugDataToString:string index:PQRightChild(i) depth:depth + 1];
556}
557#endif
558
559@end
560
561
562@implementation OOPriorityQueueEnumerator
563
564- (id) initWithPriorityQueue:(OOPriorityQueue *)queue
565{
566 if (queue == nil)
567 {
568 [self release];
569 return nil;
570 }
571
572 self = [super init];
573 if (self != nil)
574 {
575 _queue = [queue retain];
576 }
577 return self;
578}
579
580
581- (void) dealloc
582{
583 [_queue release];
584
585 [super dealloc];
586}
587
588
589- (id) nextObject
590{
591 id value = [_queue nextObject];
592 if (value == nil)
593 {
594 // Maintain enumerator semantics by ensuring we don't start returning new values after returning nil.
595 [_queue release];
596 _queue = nil;
597 }
598 return value;
599}
600
601@end
602
603
604#if DEBUG_GRAPHVIZ
605@implementation OOPriorityQueue (DebugGraphViz)
606
607
608static NSString *EscapedString(NSString *string)
609{
610 NSString *srcStrings[] =
611 {
612 //Note: backslash must be first.
613 @"\\", @"\"", @"\'", @"\r", @"\n", @"\t", nil
614 };
615 NSString *subStrings[] =
616 {
617 //Note: must be same order.
618 @"\\\\", @"\\\"", @"\\\'", @"\\r", @"\\n", @"\\t", nil
619 };
620
621 NSString **src = srcStrings, **sub = subStrings;
622 NSMutableString *mutable = nil;
623 NSString *result = nil;
624
625 mutable = [string mutableCopy];
626 while (*src != nil)
627 {
628 [mutable replaceOccurrencesOfString:*src++
629 withString:*sub++
630 options:0
631 range:NSMakeRange(0, [mutable length])];
632 }
633
634 if ([mutable length] == [string length])
635 {
636 result = string;
637 }
638 else
639 {
640 result = [[mutable copy] autorelease];
641 }
642 [mutable release];
643 return result;
644}
645
646
647- (NSString *) generateGraphViz
648{
649 NSMutableString *result = nil;
650 NSUInteger i;
651 id node = nil;
652 NSString *desc = nil;
653
654 result = [NSMutableString string];
655
656 // Header
657 [result appendString:
658 @"// OOPriorityQueue partially ordered tree dump\n\n"
659 "digraph heap\n"
660 "{\n"
661 "\tgraph [charset=\"UTF-8\", label=\"OOPriorityQueue heap dump\", labelloc=t, labeljust=l];\n"
662 "\tnode [shape=record];\n\t\n\t"];
663
664 // Nodes
665 for (i = 0; i < _count; ++i)
666 {
667 node = _heap[i];
668 desc = [node description];
669 if ([desc length] > 70)
670 {
671 desc = [[desc substringToIndex:64] stringByAppendingString:@"..."];
672 }
673
674 [result appendFormat:@"\tnode_%lu [label=\"<f0> | <f1> %@ | <f2>\"];\n", i, EscapedString(desc)];
675 }
676
677 // Arcs
678 for (i = 0; PQLeftChild(i) < _count; ++i)
679 {
680 [result appendFormat:@"\tnode_%lu:f0 -> node_%lu:f1;\n", i, PQLeftChild(i)];
681 if (PQRightChild(i) < _count) [result appendFormat:@"\tnode_%lu:f2 -> node_%lu:f1;\n", i, PQRightChild(i)];
682 }
683
684 [result appendString:@"}\n"];
685
686 return result;
687}
688
689
690- (void) writeGraphVizToURL:(NSURL *)url
691{
692 NSString *graphViz = nil;
693 NSData *data = nil;
694
695 graphViz = [self generateGraphViz];
696 data = [graphViz dataUsingEncoding:NSUTF8StringEncoding];
697
698 if (data != nil)
699 {
700 [data writeToURL:url atomically:YES];
701 }
702}
703
704
705- (void) writeGraphVizToPath:(NSString *)path
706{
707 [self writeGraphVizToURL:[NSURL fileURLWithPath:path]];
708}
709
710@end
711#endif
#define INLINE_CONST_FUNC
#define OOINLINE
return self
unsigned count
OOINLINE NSComparisonResult PQCompare(id a, id b, SEL comparator)
OOINLINE NSUInteger PQLeftChild(NSUInteger n) INLINE_CONST_FUNC
OOINLINE NSUInteger PQRightChild(NSUInteger n) INLINE_CONST_FUNC
OOINLINE NSUInteger PQParent(NSUInteger n) INLINE_CONST_FUNC
NSComparisonResult(* CompareIMP)(id self, SEL _cmd, id other)
@ kMinCapacity
return nil
OO_PQ_STRONG id * _heap
void makeObjectsPerformSelector:(SEL selector)
NSUInteger _capacity