1//
2//  KTMutableMatrix
3//  KTMatrix collection class cluster
4//
5//  Extends the KTMatrix class cluster with mutator methods
6//
7//  Copyright (c) 2002 Chris Purcell. All rights reserved.
8//
9//  You may use this code for whatever purposes you wish.
10//  This code comes with no warranties, implied or otherwise.
11//  Using it may damage your data. It shouldn't, but save a copy first.
12//  That's a good idea anyway, actually.
13//
14
15#import "KTMutableMatrix.h"
16#import "KTPlaceholderMutableMatrix.h"
17#import "KTMutableMatrixSparseImp.h"
18#import "KTMutableMatrixDenseImp.h"
19#import "KTCuboidHash.h"
20
21@implementation KTMutableMatrix
22
23//// Allocators
24+ (id)alloc
25{
26    if ([self isEqual:[KTMutableMatrix class]])
27        return [KTPlaceholderMutableMatrix alloc];
28    else
29        return [super alloc];
30}
31+ (id)allocWithZone:(NSZone *)zone
32{
33    if ([self isEqual:[KTMutableMatrix class]])
34        return [KTPlaceholderMutableMatrix allocWithZone:zone];
35    else
36        return [super allocWithZone:zone];
37}
38
39//// Constructors
40+ (id)matrixWithMatrix:(KTMatrix *)matrix
41{
42    if (([[matrix locationHash] hashBound] == 0) ||
43        ([matrix count] < [[matrix locationHash] hashBound]/3))
44        return [KTMutableMatrixSparseImp matrixWithMatrix:matrix];
45    else
46        return [KTMutableMatrixDenseImp matrixWithMatrix:matrix];
47}
48+ (id)matrixWithLocationHash:(id<KTLocationHash>)hash;
49{
50    return [self matrixWithCapacity:0
51                       locationHash:hash];
52}
53+ (id)matrixWithLocationHash:(id<KTLocationHash>)hash
54                     objects:(NSArray *)objects
55                 atLocations:(NSArray *)loc1s
56                 byLocations:(NSArray *)loc2s
57{
58    if (([hash hashBound] == 0) || ([objects count] < [hash hashBound]/3))
59        return [KTMutableMatrixSparseImp matrixWithLocationHash:hash
60                                                        objects:objects
61                                                    atLocations:loc1s
62                                                    byLocations:loc2s];
63    else
64        return [KTMutableMatrixDenseImp matrixWithLocationHash:hash
65                                                       objects:objects
66                                                   atLocations:loc1s
67                                                   byLocations:loc2s];
68}
69+ (id)matrixWithCapacity:(unsigned)numItems
70          locationHash:(id<KTLocationHash>)hash
71{
72    if (([hash hashBound] == 0) || (numItems < [hash hashBound]/3))
73        return [KTMutableMatrixSparseImp matrixWithCapacity:numItems
74                                               locationHash:hash];
75    else
76        return [KTMutableMatrixDenseImp matrixWithCapacity:numItems
77                                              locationHash:hash];
78}
79+ (id)matrixWithCapacity:(unsigned)numItems
80        cuboidBoundArray:(NSArray *)bounds
81{
82    id<KTLocationHash> hash = [[KTCuboidHash allocWithZone:NULL]
83        initWithBounds:bounds];
84    id ret = [self matrixWithCapacity:numItems
85                       locationHash:hash];
86    [hash release];
87    return ret;
88}
89+ (id)matrixWithCapacity:(unsigned)numItems
90            cuboidBounds:(unsigned)bound1,...
91{
92    NSMutableArray *array
93    = [NSMutableArray arrayWithObject:[NSNumber numberWithInt:bound1]];
94    unsigned bound;
95    va_list args;
96    if (bound1 == 0)
97        {  // Creating a 0-dimensional array. Riiiight...
98        [array removeLastObject];
99        }
100    else
101        {
102        va_start(args, bound1);
103
104        while (bound = va_arg(args, unsigned))
105            [array addObject:[NSNumber numberWithInt:bound]];
106
107        va_end(args);
108        }
109    return [self matrixWithCapacity:numItems
110                   cuboidBoundArray:array];
111}
112+ (id)matrixWithCuboidBoundsObjectAtCoordinates:(unsigned)bound1,...
113{
114    KTCuboidHash *hash; // The hashing object to use
115    va_list args;       // To trawl through the arguments
116    id ret;             // The object being returned
117    id object;          // Used to store the object in the arguments
118
119    va_start(args, bound1);
120    hash = [[KTCuboidHash alloc] initWithBoundsList:bound1 :&args];
121    object = va_arg(args, id);
122
123    if ([self isEqual:[KTMutableMatrix class]])
124        {   // Can optimize away some inefficiencies
125        unsigned loc = [hash hashForCoordinatesList:&args];
126
127        if (([hash hashBound] != 0) && ([hash hashBound] <= 3))
128            ret = [KTMutableMatrixDenseImp alloc];
129        else
130            ret = [KTMutableMatrixSparseImp alloc];
131        ret = [[ret initWithLocationHash:hash
132                                  object:object
133                        atHashedLocation:loc] autorelease];
134        }
135    else
136        {
137        unsigned i, dimension = [hash dimension];
138        NSMutableArray *coord = [[NSMutableArray alloc] init];
139        NSNumber *number;
140
141        for (i = 0; i < dimension; i++)
142            {
143            number = [[NSNumber alloc] initWithInt:va_arg(args, unsigned)];
144            [coord addObject:number];
145            [number release];
146            }
147
148        ret = [self matrixWithLocationHash:hash
149                                    object:object
150                         atCoordinateArray:coord];
151        [coord release];
152        }
153
154    [hash release];
155    va_end(args);
156    return ret;
157}
158+ (id)matrixWithCuboidBoundsObjectsAtCoordinates:(unsigned)bound1,...
159{
160    KTCuboidHash *hash; // The hashing object to use
161    va_list args;       // To trawl through the arguments
162    id object;          // Used to store the objects in the arguments
163    id ret;             // The object being returned
164
165    // Read in data for the cuboid hash
166    va_start(args, bound1);
167    hash = [[KTCuboidHash alloc] initWithBoundsList:bound1 :&args];
168
169    if ([self isEqual:[KTMutableMatrix class]])
170        {   // Can optimize away some inefficiencies
171            // Need a mutable temporary to store all the objects
172        KTMutableMatrixSparseImp *temp;
173        // SELs/IMPs to speed up repeated method calls
174        SEL hashListSEL = @selector(hashForCoordinatesList:);
175        SEL setObjSEL = @selector(setObject:atHashedLocation:);
176        unsigned (*hashListIMP)(id, SEL, ...);
177        void (*setObjIMP)(id, SEL, ...);
178
179        // Allocate the temporary mutable matrix
180        temp = [[KTMutableMatrixSparseImp alloc] initWithCapacity:0
181                                                     locationHash:hash];
182
183        // Read in objects and coordinates
184        hashListIMP = (unsigned (*)(id, SEL, ...))
185            [hash methodForSelector:hashListSEL];
186        setObjIMP = (void (*)(id, SEL, ...))
187            [temp methodForSelector:setObjSEL];
188        while (object = va_arg(args, id))
189            setObjIMP(temp, setObjSEL, object,
190                      hashListIMP(hash,hashListSEL,&args));
191
192        // Make a mutable copy of the object
193        if (([hash hashBound] == 0) ||
194            ([temp count] < [hash hashBound]/3))
195            ret = [temp retain];
196        else
197            ret = [temp mutableCopyWithZone:NULL];
198        [temp release];
199        [ret autorelease];
200        }
201    else
202        {   // Version for derived classes
203        unsigned dimension = [hash dimension];
204        NSMutableArray *objects = [[NSMutableArray alloc] init];
205        NSMutableArray *coords  = [[NSMutableArray alloc] init];
206        NSNumber *number;
207        unsigned i;
208        NSMutableArray *coord;
209
210        // Fill a couple of arrays with the objects and coordinates
211        while (object = va_arg(args, id))
212            {
213            [objects addObject:object];
214            coord = [NSMutableArray array];
215            for (i = 0; i < dimension; i++)
216                {
217                number = [[NSNumber alloc] initWithInt:va_arg(args, unsigned)];
218                [coord addObject:number];
219                [number release];
220                }
221            [coords addObject:coord];
222            }
223
224        ret = [self matrixWithLocationHash:hash
225                                   objects:objects
226                        atCoordinateArrays:coords];
227        [objects release];
228        [coords release];
229        }
230
231    [hash release];
232    va_end(args);
233    return ret;
234}
235
236- (id)init
237{
238    return (self = [super init]);
239}
240- (id)initWithMatrix:(KTMatrix *)matrix
241{
242    [NSException raise:NSGenericException
243                format:
244        @"Must implement a complete subclass of KTMutableMatrix!"];
245    [self release];
246    return NULL;
247}
248- (id)initWithLocationHash:(id<KTLocationHash>)hash
249{ return (self = [self initWithCapacity:0 locationHash:hash]); }
250- (id)initWithCapacity:(unsigned)numItems
251          locationHash:(id<KTLocationHash>)hash
252{
253    [NSException raise:NSGenericException
254                format:
255        @"Must implement a complete subclass of KTMutableMatrix!"];
256    [self release];
257    return NULL;
258}
259- (id)initWithCapacity:(unsigned)numItems
260      cuboidBoundArray:(NSArray *)bounds;
261{
262    id<KTLocationHash> hash = [[KTCuboidHash allocWithZone:[self zone]]
263        initWithBounds:bounds];
264    self = [self initWithCapacity:numItems
265                     locationHash:hash];
266    [hash release];
267    return self;
268}
269- (id)initWithCapacity:(unsigned)numItems
270          cuboidBounds:(unsigned)bound1,...
271{
272    va_list args;
273    id<KTLocationHash> hash;
274
275    va_start(args, bound1);
276    hash = [[KTCuboidHash allocWithZone:[self zone]]
277                initWithBoundsList:bound1 :&args];
278    self = [self initWithCapacity:numItems locationHash:hash];
279    [hash release];
280    va_end(args);
281
282    return self;
283}
284
285//// Accessor methods
286- (id)objectAtLocation:(NSDictionary *)loc1
287            byLocation:(NSDictionary *)loc2;
288{
289    [NSException raise:NSGenericException
290                format:
291        @"Must implement a complete subclass of KTMutableMatrix!"];
292    return NULL;
293}
294- (id<KTLocationHash>)locationHash
295{
296    [NSException raise:NSGenericException
297                format:
298        @"Must implement a complete subclass of KTMutableMatrix!"];
299    return NULL;
300}
301
302//// Internal use accessors
303- (NSDictionary *)matrixData
304{
305    [NSException raise:NSGenericException
306                format:
307        @"Must implement a complete subclass of KTMutableMatrix!"];
308    return NULL;
309}
310
311//// Object methods
312- (NSEnumerator *)objectEnumerator
313{
314    [NSException raise:NSGenericException
315                format:
316        @"Must implement a complete subclass of KTMutableMatrix!"];
317    return NULL;
318}
319
320//// Mutator methods
321- (void)setMatrix:(KTMatrix *)matrix
322{
323    [NSException raise:NSGenericException
324                format:
325        @"Must implement a complete subclass of KTMutableMatrix!"];
326}
327- (void)setObject:(id)object
328       atLocation:(NSDictionary *)loc1;
329{
330    [self setObject:object
331         atLocation:loc1
332         byLocation:[NSDictionary dictionary]];
333}
334- (void)setObject:(id)object
335       atLocation:(NSDictionary *)loc1
336       byLocation:(NSDictionary *)loc2;
337{
338    [NSException raise:NSGenericException
339                format:
340        @"Must implement a complete subclass of KTMutableMatrix!"];
341}
342- (void)        setObject:(id)object
343        atCoordinateArray:(NSArray *)coords
344{
345    NSMutableDictionary *loc = [NSMutableDictionary dictionaryWithCapacity:
346        [coords count]];
347    unsigned axis = 0;
348
349    for (; axis < [coords count]; axis++)
350        [loc setObject:[coords objectAtIndex:axis]
351                forKey:[NSNumber numberWithInt:axis]];
352    [self setObject:object
353         atLocation:loc
354         byLocation:[NSDictionary dictionary]];
355    return;
356}
357- (void)    setObject:(id)object
358        atCoordinates:(unsigned)x,...
359{
360    NSMutableArray *array
361    = [NSMutableArray arrayWithObject:[NSNumber numberWithInt:x]];
362    unsigned axes = [self dimension];
363    va_list args;
364
365    va_start(args, x);
366
367    while ([array count] < axes)
368        [array addObject:[NSNumber numberWithInt:va_arg(args, unsigned)]];
369
370    va_end(args);
371
372    [self       setObject:object
373        atCoordinateArray:array];
374}
375- (void)setObjects:(NSArray *)objects
376       atLocations:(NSArray *)locs
377{
378    NSEnumerator *i = [objects objectEnumerator];
379    NSEnumerator *j = [locs objectEnumerator];
380    id object;
381    NSDictionary *loc1;
382    NSDictionary *loc2 = [[NSDictionary alloc] init];
383
384    if ([objects count] != [locs count])
385        [NSException raise:NSInvalidArgumentException
386                    format:
387            @"Objects and locations arrays not of equal length"];
388    while ((object = [i nextObject]) && (loc1 = [j nextObject]))
389        [self setObject:object
390             atLocation:loc1
391             byLocation:loc2];
392    [loc2 release];
393}
394- (void)setObjects:(NSArray *)objects
395       atLocations:(NSArray *)loc1s
396        byLocation:(NSDictionary *)loc2
397{
398    NSEnumerator *i = [objects objectEnumerator];
399    NSEnumerator *j = [loc1s objectEnumerator];
400    id object;
401    NSDictionary *loc1;
402
403    if ([objects count] != [loc1s count])
404        [NSException raise:NSInvalidArgumentException
405                    format:
406            @"Objects and locations arrays not of equal length"];
407    while ((object = [i nextObject]) && (loc1 = [j nextObject]))
408        [self setObject:object
409             atLocation:loc1
410             byLocation:loc2];
411}
412- (void)setObjects:(NSArray *)objects
413       atLocations:(NSArray *)loc1s
414       byLocations:(NSArray *)loc2s
415{
416    NSEnumerator *i = [objects objectEnumerator];
417    NSEnumerator *j = [loc1s objectEnumerator];
418    NSEnumerator *k = [loc2s objectEnumerator];
419    id object;
420    NSDictionary *loc1;
421    NSDictionary *loc2;
422
423    if ([objects count] != [loc1s count])
424        [NSException raise:NSInvalidArgumentException
425                    format:
426            @"Objects and locations arrays not of equal length"];
427    if ([objects count] != [loc1s count])
428        [NSException raise:NSInvalidArgumentException
429                    format:
430            @"Locations arrays not of equal length"];
431    while ((object = [i nextObject]) && (loc1 = [j nextObject]) &&
432           (loc2 = [k nextObject]))
433        [self setObject:object
434             atLocation:loc1
435             byLocation:loc2];
436}
437- (void)setObjectsAtLocations:(id)object1,...
438{
439    NSDictionary *empty     = [NSDictionary dictionary];
440    NSMutableArray *objects = [NSMutableArray array];
441    NSMutableArray *loc1s   = [NSMutableArray array];
442    NSMutableArray *loc2s   = [NSMutableArray array];
443    id object = object1;
444    va_list args;
445
446    if (object1 != 0)
447        {
448        va_start(args, object1);
449
450        do
451            {
452                [objects addObject:object];
453                [loc1s addObject:va_arg(args, id)];
454                [loc2s addObject:empty];
455            }
456        while (object = va_arg(args, id));
457
458        va_end(args);
459        }
460    [self setObjects:objects
461         atLocations:loc1s
462         byLocations:loc2s];
463}
464- (void)setObjectsAtLocationsByLocations:(id)object1,...
465{
466    NSMutableArray *objects = [NSMutableArray array];
467    NSMutableArray *loc1s   = [NSMutableArray array];
468    NSMutableArray *loc2s   = [NSMutableArray array];
469    id object = object1;
470    va_list args;
471
472    if (object1 != 0)
473        {
474        va_start(args, object1);
475
476        do
477            {
478                [objects addObject:object];
479                [loc1s addObject:va_arg(args, id)];
480                [loc2s addObject:va_arg(args, id)];
481            }
482        while (object = va_arg(args, id));
483
484        va_end(args);
485        }
486    [self setObjects:objects
487         atLocations:loc1s
488         byLocations:loc2s];
489}
490- (void)        setObjects:(NSArray *)objects
491        atCoordinateArrays:(NSArray *)coords
492{
493    NSArray *dims = [self axes];
494    NSMutableArray *loc1s = [[NSMutableArray alloc] initWithCapacity:
495        [coords count]];
496    NSMutableArray *loc2s = [[NSMutableArray alloc] initWithCapacity:
497        [coords count]];
498    NSMutableDictionary *loc;
499    NSDictionary *empty = [[NSDictionary alloc] init];
500
501    NSEnumerator *axes;
502    NSEnumerator *coordArrays = [coords objectEnumerator];
503    NSEnumerator *vals;
504    id axis;
505    NSArray *coord;
506
507    while ((coord = [coordArrays nextObject]))
508        {
509        if ([dims count] != [coord count])
510            [NSException raise:NSInvalidArgumentException
511                        format:
512                @"Dimensions of hash and number of coordinates not equal"];
513        axes = [dims objectEnumerator];
514        vals = [coord objectEnumerator];
515        loc  = [[NSMutableDictionary alloc] init];
516        while ((axis = [axes nextObject]))
517            [loc setObject:[vals nextObject]
518                    forKey:axis];
519        [loc1s addObject:loc];
520        [loc2s addObject:empty];
521        [loc release];
522        }
523    [self setObjects:objects
524         atLocations:loc1s
525         byLocations:loc2s];
526    [loc1s release];
527    [loc2s release];
528    [empty release];
529}
530- (void)setObjectsAtCoordinates:(id)object1,...
531{
532    NSMutableArray *objects = [NSMutableArray array];
533    NSMutableArray *coords  = [NSMutableArray array];
534    unsigned dimension = [self dimension];
535    id object = object1;
536    va_list args;
537
538    if (object1 != 0)
539        {
540        va_start(args, object1);
541
542        do
543            {
544                NSMutableArray *coord = [NSMutableArray arrayWithCapacity:
545                    dimension];
546                [objects addObject:object];
547                while ([coord count] < dimension)
548                    [coord addObject:[NSNumber numberWithInt:
549                        va_arg(args, unsigned)]];
550                [coords addObject:coord];
551            }
552        while (object = va_arg(args, id));
553
554        va_end(args);
555        }
556    [self       setObjects:objects
557        atCoordinateArrays:coords];
558}
559
560- (void)removeObjectAtLocation:(NSDictionary *)loc
561{
562    NSDictionary *empty = [[NSDictionary alloc] init];
563    [self removeObjectAtLocation:loc
564                      byLocation:empty];
565}
566- (void)removeObjectAtLocation:(NSDictionary *)loc1
567                    byLocation:(NSDictionary *)loc2
568{
569    [NSException raise:NSGenericException
570                format:
571        @"Must implement a complete subclass of KTMutableMatrix!"];
572}
573- (void)removeObjectAtCoordinateArray:(NSArray *)coords
574{
575    NSMutableDictionary *loc = [NSMutableDictionary dictionaryWithCapacity:
576        [coords count]];
577    unsigned axis = 0;
578    for (; axis < [coords count]; axis++)
579        [loc setObject:[coords objectAtIndex:axis]
580                forKey:[NSNumber numberWithInt:axis]];
581    [self removeObjectAtLocation:loc
582                      byLocation:[NSDictionary dictionary]];
583}
584- (void)removeObjectAtCoordinates:(unsigned)x,...
585{
586    NSMutableArray *array = [NSMutableArray arrayWithObject:
587        [NSNumber numberWithInt:x]];
588    unsigned axes = [self dimension];
589    va_list args;
590
591    va_start(args, x);
592
593    while ([array count] < axes)
594        [array addObject:[NSNumber numberWithInt:va_arg(args, unsigned)]];
595
596    va_end(args);
597
598    [self removeObjectAtCoordinateArray:array];
599}
600- (void)removeObjectsAtLocations:(NSArray *)locs
601{
602    NSDictionary *loc1;
603    NSDictionary *loc2 = [[NSDictionary alloc] init];
604    NSEnumerator *i = [locs objectEnumerator];
605    while ((loc1 = [i nextObject]))
606        [self removeObjectAtLocation:loc1
607                          byLocation:loc2];
608    [loc2 release];
609}
610- (void)removeObjectsAtLocations:(NSArray *)loc1s
611                      byLocation:(NSDictionary *)loc2
612{
613    NSDictionary *loc1;
614    NSEnumerator *i = [loc1s objectEnumerator];
615    while ((loc1 = [i nextObject]))
616        [self removeObjectAtLocation:loc1
617                          byLocation:loc2];
618}
619- (void)removeObjectsAtLocations:(NSArray *)loc1s
620                     byLocations:(NSArray *)loc2s
621{
622    NSDictionary *loc1, *loc2;
623    NSEnumerator *i = [loc1s objectEnumerator],
624        *j = [loc2s objectEnumerator];
625
626    if ([loc1s count] != [loc2s count])
627        [NSException raise:NSInvalidArgumentException
628                    format:
629@"Inputs to removeObjectsAtLocations:byLocations: not of equal count"];
630    while ((loc1 = [i nextObject]) && (loc2 = [j nextObject]))
631        [self removeObjectAtLocation:loc1
632                          byLocation:loc2];
633}
634- (void)removeObjectsAtCoordinateArrays:(NSArray *)coords
635{
636    NSArray *dims = [self axes];
637    NSMutableArray *loc1s = [[NSMutableArray alloc] initWithCapacity:
638        [coords count]];
639    NSMutableArray *loc2s = [[NSMutableArray alloc] initWithCapacity:
640        [coords count]];
641    NSMutableDictionary *loc;
642    NSDictionary *empty = [[NSDictionary alloc] init];
643
644    NSEnumerator *axes;
645    NSEnumerator *coordArrays = [coords objectEnumerator];
646    NSEnumerator *vals;
647    id axis;
648    NSArray *coord;
649
650    while ((coord = [coordArrays nextObject]))
651        {
652        if ([dims count] != [coord count])
653            [NSException raise:NSInvalidArgumentException
654                        format:
655                @"Dimensions of hash and number of coordinates not equal"];
656        axes = [dims objectEnumerator];
657        vals = [coord objectEnumerator];
658        loc  = [[NSMutableDictionary alloc] init];
659        while ((axis = [axes nextObject]))
660            [loc setObject:[vals nextObject]
661                    forKey:axis];
662        [loc1s addObject:loc];
663        [loc2s addObject:empty];
664        [loc release];
665        }
666    [self removeObjectsAtLocations:loc1s
667                       byLocations:loc2s];
668    [loc1s release];
669    [loc2s release];
670    [empty release];
671}
672
673- (void)removeAllObjects
674{
675    [NSException raise:NSGenericException
676                format:
677        @"Must implement a complete subclass of KTMutableMatrix!"];
678}
679
680
681@end
682