1//
2//  KTPlaceholderMutableMatrix
3//  KTMatrix collection class cluster
4//
5//  KTMutableMatrix class cluster initialisation subclass
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 "KTPlaceholderMutableMatrix.h"
16#import "KTCuboidHash.h"
17#import "KTMutableMatrixSparseImp.h"
18#import "KTMutableMatrixDenseImp.h"
19
20@implementation KTPlaceholderMutableMatrix
21
22- (id)init
23{
24    NSZone *zone = [self zone];
25    id<KTLocationHash> hash = [[KTCuboidHash allocWithZone:zone] init];
26    [self release];
27    self = [[KTMutableMatrixSparseImp allocWithZone:zone]
28        initWithCapacity:0 locationHash:hash];
29    [hash release];
30    return self;
31}
32- (id)initWithMatrix:(KTMatrix *)matrix
33{
34    NSZone *zone = [self zone];
35    [self release];
36    if (([[matrix locationHash] hashBound] == 0) ||
37        ([matrix count] < [[matrix locationHash] hashBound]/3))
38        self = [KTMutableMatrixSparseImp allocWithZone:zone];
39    else
40        self = [KTMutableMatrixDenseImp allocWithZone:zone];
41    [self initWithMatrix:matrix];
42    return self;
43}
44- (id)initWithLocationHash:(id<KTLocationHash>)hash
45                   objects:(NSArray *)objects
46               atLocations:(NSArray *)loc1s
47               byLocations:(NSArray *)loc2s
48{
49    NSZone *zone = [self zone];
50    [self release];
51    if (([hash hashBound] == 0) ||
52        ([objects count] < [hash hashBound]/3))
53        self = [KTMutableMatrixSparseImp allocWithZone:zone];
54    else
55        self = [KTMutableMatrixDenseImp allocWithZone:zone];
56    self = [self initWithLocationHash:hash
57                              objects:objects
58                          atLocations:loc1s
59                          byLocations:loc2s];
60    return self;
61}
62- (id)initWithCapacity:(unsigned)numItems
63          locationHash:(id<KTLocationHash>)hash
64{
65    NSZone *zone = [self zone];
66    [self release];
67    if (([hash hashBound] == 0) || (numItems < [hash hashBound]/3))
68        self = [KTMutableMatrixSparseImp allocWithZone:zone];
69    else
70        self = [KTMutableMatrixDenseImp allocWithZone:zone];
71    [self initWithCapacity:numItems
72              locationHash:hash];
73    return self;
74}
75
76//// Efficient implementations of other methods
77- (id)initWithCuboidBoundsObjectAtCoordinates:(unsigned)bound1,...
78{
79    NSZone *zone = [self zone];
80    KTCuboidHash *hash;
81    id object = nil;
82    va_list args;
83
84    [self release];
85
86    va_start(args, bound1);
87    hash = [[KTCuboidHash allocWithZone:zone]
88                initWithBoundsList:bound1 :&args];
89
90    object = va_arg(args, id);
91
92    if (([hash hashBound] != 0) && ([hash hashBound] <= 3))
93        self = [KTMutableMatrixDenseImp alloc];
94    else
95        self = [KTMutableMatrixSparseImp alloc];
96    self = [(id)self initWithLocationHash:hash
97                                   object:object
98                         atHashedLocation:[hash hashForCoordinatesList:&args]];
99
100    va_end(args);
101    [hash release];
102    return self;
103}
104- (id)initWithCuboidBoundsObjectsAtCoordinates:(unsigned)bound1,...
105{   // Can optimize away some inefficiencies
106    NSZone *zone = [self zone];
107    KTCuboidHash *hash; // The hashing object to use
108    va_list args;       // To trawl through the arguments
109    id object;          // Used to store the objects in the arguments
110
111    // Need a mutable temporary to store all the objects
112    KTMutableMatrixSparseImp *temp;
113    // SELs/IMPs to speed up repeated method calls
114    SEL hashListSEL = @selector(hashForCoordinatesList:);
115    SEL setObjSEL = @selector(setObject:atHashedLocation:);
116    unsigned (*hashListIMP)(id, SEL, ...);
117    void (*setObjIMP)(id, SEL, ...);
118
119    [self release];
120
121    // Read in data for the cuboid hash
122    va_start(args, bound1);
123    hash = [[KTCuboidHash allocWithZone:zone]
124                initWithBoundsList:bound1 :&args];
125
126    // Allocate the temporary mutable matrix
127    temp = [[KTMutableMatrixSparseImp allocWithZone:zone]
128                initWithCapacity:0
129                    locationHash:hash];
130
131    // Read in objects and coordinates
132    hashListIMP = (unsigned (*)(id, SEL, ...))
133        [hash methodForSelector:hashListSEL];
134    setObjIMP = (void (*)(id, SEL, ...))
135        [temp methodForSelector:setObjSEL];
136    while (object = va_arg(args, id))
137        setObjIMP(temp, setObjSEL, object,
138                  hashListIMP(hash,hashListSEL,&args));
139
140    // Make a mutable copy of the object if necessary
141    if (([hash hashBound] == 0) ||
142        ([temp count] < [hash hashBound]/3))
143        self = [temp retain];
144    else
145        self = [temp mutableCopyWithZone:zone];
146    [temp release];
147
148    [hash release];
149    va_end(args);
150    return self;
151}
152
153@end
154