1 |
|
|
/* |
2 |
|
|
Teem: Tools to process and visualize scientific data and images . |
3 |
|
|
Copyright (C) 2010 Thomas Schultz |
4 |
|
|
|
5 |
|
|
This library is free software; you can redistribute it and/or |
6 |
|
|
modify it under the terms of the GNU Lesser General Public License |
7 |
|
|
(LGPL) as published by the Free Software Foundation; either |
8 |
|
|
version 2.1 of the License, or (at your option) any later version. |
9 |
|
|
The terms of redistributing and/or modifying this software also |
10 |
|
|
include exceptions to the LGPL that facilitate static linking. |
11 |
|
|
|
12 |
|
|
This library is distributed in the hope that it will be useful, |
13 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
|
|
Lesser General Public License for more details. |
16 |
|
|
|
17 |
|
|
You should have received a copy of the GNU Lesser General Public License |
18 |
|
|
along with this library; if not, write to Free Software Foundation, Inc., |
19 |
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
#include "air.h" |
23 |
|
|
#include <string.h> |
24 |
|
|
|
25 |
|
|
/* internal helper routines */ |
26 |
|
|
/* restore heap property by pulling a single node up the tree */ |
27 |
|
|
static void upheap(airHeap *h, unsigned int i) { |
28 |
|
|
while (i) { |
29 |
|
|
unsigned int parent = (i-1)/2; |
30 |
|
|
if (h->key[h->idx[parent]] > h->key[h->idx[i]]) { /* swap */ |
31 |
|
|
unsigned int tmp = h->idx[parent]; |
32 |
|
|
h->idx[parent] = h->idx[i]; |
33 |
|
|
h->idx[i] = tmp; |
34 |
|
|
h->invidx[h->idx[i]] = i; |
35 |
|
|
h->invidx[h->idx[parent]] = parent; |
36 |
|
|
i = parent; |
37 |
|
|
} else |
38 |
|
|
i = 0; /* break loop */ |
39 |
|
|
} |
40 |
|
|
} |
41 |
|
|
|
42 |
|
|
/* restore heap property by pushing a single node down the tree */ |
43 |
|
|
static void downheap (airHeap *h, unsigned int i) { |
44 |
|
|
unsigned int len = h->key_a->len; |
45 |
|
|
while (1) { |
46 |
|
|
unsigned int left = 2*i+1; |
47 |
|
|
unsigned int right = 2*i+2; |
48 |
|
|
unsigned int minidx = 0; |
49 |
|
|
double minval = 0; |
50 |
|
|
if (left>=len) |
51 |
|
|
return; |
52 |
|
|
if ((right>=len) || |
53 |
|
|
(h->key[h->idx[left]] < h->key[h->idx[right]])) { |
54 |
|
|
minidx = left; |
55 |
|
|
minval = h->key[h->idx[left]]; |
56 |
|
|
} else { |
57 |
|
|
minidx = right; |
58 |
|
|
minval = h->key[h->idx[right]]; |
59 |
|
|
} |
60 |
|
|
if (h->key[h->idx[i]] > minval) { /* swap */ |
61 |
|
|
unsigned int tmp = h->idx[i]; |
62 |
|
|
h->idx[i] = h->idx[minidx]; |
63 |
|
|
h->idx[minidx] = tmp; |
64 |
|
|
h->invidx[h->idx[i]] = i; |
65 |
|
|
h->invidx[h->idx[minidx]] = minidx; |
66 |
|
|
i = minidx; |
67 |
|
|
} else |
68 |
|
|
return; |
69 |
|
|
} |
70 |
|
|
} |
71 |
|
|
|
72 |
|
|
/* Restore heap property "from scratch" (without any prior assumptions). |
73 |
|
|
* Works in a bottom-up manner, which is more efficient than top-down */ |
74 |
|
|
static void heapify (airHeap *h) { |
75 |
|
|
unsigned int len = h->key_a->len; |
76 |
|
|
int maxi = len/2-1, i; /* above maxi, downheap has no effect */ |
77 |
|
|
for (i=maxi; i>=0; i--) { |
78 |
|
|
downheap(h, i); |
79 |
|
|
} |
80 |
|
|
} |
81 |
|
|
|
82 |
|
|
/* Tries to increase or decrease the length of h. returns 0 upon success. */ |
83 |
|
|
static int heapLenIncr (airHeap *h, int delta) { |
84 |
|
|
unsigned int oldlen=h->key_a->len; |
85 |
|
|
unsigned int newlen=oldlen+delta; |
86 |
|
|
if (delta==0) |
87 |
|
|
return 0; |
88 |
|
|
airArrayLenIncr(h->key_a, delta); |
89 |
|
|
if (h->data_a!=NULL) airArrayLenIncr(h->data_a, delta); |
90 |
|
|
airArrayLenIncr(h->idx_a, delta); |
91 |
|
|
airArrayLenIncr(h->invidx_a, delta); |
92 |
|
|
if (h->key_a->len<newlen || (h->data_a!=NULL && h->data_a->len<newlen) || |
93 |
|
|
h->idx_a->len<newlen || h->invidx_a->len<newlen) { |
94 |
|
|
/* Error. Try to undo changes and return error code. */ |
95 |
|
|
if (h->key_a->len>oldlen) airArrayLenSet(h->key_a, oldlen); |
96 |
|
|
if (h->data_a!=NULL && h->data_a->len>oldlen) |
97 |
|
|
airArrayLenSet(h->data_a, oldlen); |
98 |
|
|
if (h->idx_a->len>oldlen) airArrayLenSet(h->idx_a, oldlen); |
99 |
|
|
if (h->invidx_a->len>oldlen) airArrayLenSet(h->invidx_a, oldlen); |
100 |
|
|
return 1; |
101 |
|
|
} |
102 |
|
|
return 0; |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
/* Creates a new heap, returning NULL upon error. If additional data |
106 |
|
|
* is to be stored with each node, dataUnit needs to be set to the |
107 |
|
|
* number of data bytes needed per element. incr is used for dynamic |
108 |
|
|
* memory allocation (an additional number of incr elements are |
109 |
|
|
* allocated each time the heap grows past its current capacity). |
110 |
|
|
*/ |
111 |
|
|
airHeap *airHeapNew(size_t dataUnit, unsigned int incr) { |
112 |
|
|
airHeap *h; |
113 |
|
|
airPtrPtrUnion appu; |
114 |
|
|
h = AIR_CALLOC(1, airHeap); |
115 |
|
|
if (h==NULL) { |
116 |
|
|
return NULL; |
117 |
|
|
} |
118 |
|
|
|
119 |
|
|
appu.d = &h->key; |
120 |
|
|
h->key_a = airArrayNew(appu.v, NULL, sizeof(double), incr); |
121 |
|
|
if (dataUnit>0) { /* data is optional */ |
122 |
|
|
h->data_a = airArrayNew(&h->data, NULL, dataUnit, incr); |
123 |
|
|
} |
124 |
|
|
appu.ui = &h->idx; |
125 |
|
|
h->idx_a = airArrayNew(appu.v, NULL, sizeof(unsigned int), incr); |
126 |
|
|
appu.ui = &h->invidx; |
127 |
|
|
h->invidx_a = airArrayNew(appu.v, NULL, sizeof(unsigned int), incr); |
128 |
|
|
|
129 |
|
|
if (h->key_a==NULL || (dataUnit>0 && h->data_a==NULL) || h->idx_a==NULL || |
130 |
|
|
h->invidx_a==NULL) { /* allocation failed (partly) */ |
131 |
|
|
airHeapNix(h); |
132 |
|
|
return NULL; |
133 |
|
|
} |
134 |
|
|
return h; |
135 |
|
|
} |
136 |
|
|
|
137 |
|
|
/* Same as airHeapNew, but initializes the new heap with the keys from key |
138 |
|
|
* and the optional data from data. If data is non-NULL, it needs to |
139 |
|
|
* have the same length as key, or this function will fail. The incr |
140 |
|
|
* field of the heap is copied from key (rather than data). |
141 |
|
|
*/ |
142 |
|
|
airHeap *airHeapFromArray(const airArray *key, const airArray *data) { |
143 |
|
|
airHeap *h; |
144 |
|
|
unsigned int i; |
145 |
|
|
if (key==NULL || (data!=NULL && data->len!=key->len)) |
146 |
|
|
return NULL; /* unusable input */ |
147 |
|
|
h = airHeapNew((data==NULL)?0:data->unit, key->incr); |
148 |
|
|
if (heapLenIncr (h, key->len)) { /* could not allocate memory */ |
149 |
|
|
airHeapNix(h); |
150 |
|
|
return NULL; |
151 |
|
|
} |
152 |
|
|
memcpy(h->key, key->data, key->len*sizeof(double)); |
153 |
|
|
if (h->data_a!=NULL) memcpy(h->data, data->data, data->len*data->unit); |
154 |
|
|
for (i=0; i<key->len; i++) { /* initialize indices to identity */ |
155 |
|
|
h->idx[i]=i; |
156 |
|
|
h->invidx[i]=i; |
157 |
|
|
} |
158 |
|
|
heapify(h); |
159 |
|
|
return h; |
160 |
|
|
} |
161 |
|
|
|
162 |
|
|
/* Frees all memory associated with the heap and its data */ |
163 |
|
|
airHeap *airHeapNix(airHeap *h) { |
164 |
|
|
if (h!=NULL) { |
165 |
|
|
airArrayNuke(h->key_a); |
166 |
|
|
if (h->data_a!=NULL) airArrayNuke(h->data_a); |
167 |
|
|
airArrayNuke(h->idx_a); |
168 |
|
|
airArrayNuke(h->invidx_a); |
169 |
|
|
free(h); |
170 |
|
|
} |
171 |
|
|
return NULL; |
172 |
|
|
} |
173 |
|
|
|
174 |
|
|
/* Returns the number of elements that are currently in heap h |
175 |
|
|
* (or 0 if h==NULL) */ |
176 |
|
|
unsigned int airHeapLength(const airHeap *h) { |
177 |
|
|
if (h!=NULL) { |
178 |
|
|
return h->key_a->len; |
179 |
|
|
} else |
180 |
|
|
return 0; |
181 |
|
|
} |
182 |
|
|
|
183 |
|
|
/* Inserts a key into the heap. data is copied over (deep copy) when the |
184 |
|
|
* heap was initialized to hold additional data. Otherwise, it is ignored. |
185 |
|
|
* |
186 |
|
|
* Returns the new number of elements in h. |
187 |
|
|
* Upon error, this can be the same as the old length. */ |
188 |
|
|
unsigned int airHeapInsert(airHeap *h, double key, const void *data) { |
189 |
|
|
unsigned int oldlen; |
190 |
|
|
if (h==NULL) return 0; |
191 |
|
|
oldlen = h->key_a->len; |
192 |
|
|
/* make space for the new element */ |
193 |
|
|
if (heapLenIncr(h, 1)) |
194 |
|
|
return oldlen; |
195 |
|
|
h->key[oldlen]=key; |
196 |
|
|
if (h->data_a!=NULL && data!=NULL) { |
197 |
|
|
memcpy((char*)h->data_a->data+oldlen*h->data_a->unit, data, |
198 |
|
|
h->data_a->unit); |
199 |
|
|
} |
200 |
|
|
h->idx[oldlen]=oldlen; |
201 |
|
|
h->invidx[oldlen]=oldlen; |
202 |
|
|
upheap(h,oldlen); /* restore the heap property */ |
203 |
|
|
return oldlen+1; |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
/* Merges the second heap into the first. Returns the new length of first, |
207 |
|
|
* or zero upon error. */ |
208 |
|
|
unsigned int airHeapMerge(airHeap *first, const airHeap *second) { |
209 |
|
|
unsigned int first_len, second_len, i; |
210 |
|
|
if (first==NULL || second==NULL) |
211 |
|
|
return 0; |
212 |
|
|
if ((first->data_a==NULL) ^ (second->data_a==NULL)) |
213 |
|
|
return 0; /* incompatible data */ |
214 |
|
|
if (first->data_a!=NULL && |
215 |
|
|
(first->data_a->unit!=second->data_a->unit)) |
216 |
|
|
return 0; /* incompatible data */ |
217 |
|
|
/* make sufficient space in first */ |
218 |
|
|
first_len = first->key_a->len; |
219 |
|
|
second_len = second->key_a->len; |
220 |
|
|
if (heapLenIncr(first, second_len)) |
221 |
|
|
return 0; |
222 |
|
|
/* concatenate and heapify */ |
223 |
|
|
memcpy(first->key+first_len, second->key, second_len*sizeof(double)); |
224 |
|
|
if (first->data_a!=NULL) |
225 |
|
|
memcpy((char*)first->data_a->data+first_len*first->data_a->unit, |
226 |
|
|
second->data_a->data,second_len*second->data_a->unit); |
227 |
|
|
for (i=0; i<second_len; i++) { |
228 |
|
|
first->idx[first_len+i] = first_len+second->idx[i]; |
229 |
|
|
first->invidx[first->idx[first_len+i]]=first_len+i; |
230 |
|
|
} |
231 |
|
|
heapify(first); |
232 |
|
|
return first_len+second_len; |
233 |
|
|
} |
234 |
|
|
|
235 |
|
|
/* Returns the key of the front element in the heap and optionally copies the |
236 |
|
|
* associated data to *data. Does not modify the heap. */ |
237 |
|
|
double airHeapFrontPeek(const airHeap *h, void *data) { |
238 |
|
|
if (h==NULL || h->key_a->len==0) |
239 |
|
|
return 0.0; |
240 |
|
|
if (data!=NULL && h->data_a!=NULL) |
241 |
|
|
memcpy(data, (char*)h->data_a->data+h->idx[0]*h->data_a->unit, |
242 |
|
|
h->data_a->unit); |
243 |
|
|
return h->key[h->idx[0]]; |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
/* Same as airHeapFrontPeek, but additionally removes the front element. */ |
247 |
|
|
double airHeapFrontPop(airHeap *h, void *data) { |
248 |
|
|
double retval = airHeapFrontPeek(h, data); |
249 |
|
|
if (h!=NULL && h->key_a->len>0) { |
250 |
|
|
airHeapRemove(h, h->idx[0]); |
251 |
|
|
} |
252 |
|
|
return retval; |
253 |
|
|
} |
254 |
|
|
|
255 |
|
|
/* Assigns a new key (and optionally, new data) to the front element and |
256 |
|
|
* re-sorts the heap if necessary. */ |
257 |
|
|
int airHeapFrontUpdate(airHeap *h, double newKey, const void *newData) { |
258 |
|
|
if (h==NULL || h->key_a->len==0) |
259 |
|
|
return 1; |
260 |
|
|
if (newData!=NULL && h->data_a!=NULL) |
261 |
|
|
memcpy((char*)h->data_a->data+h->idx[0]*h->data_a->unit, newData, |
262 |
|
|
h->data_a->unit); |
263 |
|
|
h->key[h->idx[0]]=newKey; |
264 |
|
|
downheap(h, 0); |
265 |
|
|
return 0; |
266 |
|
|
} |
267 |
|
|
|
268 |
|
|
/* The following functions provide advanced functionality and should |
269 |
|
|
* not be required in most applications. */ |
270 |
|
|
|
271 |
|
|
/* airHeapFind returns 0 if an element is found whose data is bitwise |
272 |
|
|
* equal to the given data, and sets *ai to the index of this |
273 |
|
|
* element. If more than one element matches the data, an arbitrary |
274 |
|
|
* one of them is returned. The index can be used with airHeapRemove |
275 |
|
|
* or airHeapUpdate, but will become invalid as soon as any element is |
276 |
|
|
* removed from the heap. */ |
277 |
|
|
int airHeapFind(const airHeap *h, unsigned int *ai, const void *data) { |
278 |
|
|
unsigned int i; |
279 |
|
|
if (h==NULL || ai==NULL || data==NULL || h->data_a==NULL) |
280 |
|
|
return 1; |
281 |
|
|
for (i=0; i<h->key_a->len; i++) { |
282 |
|
|
if (!memcmp((char*)h->data_a->data+i*h->data_a->unit, data, |
283 |
|
|
h->data_a->unit)) { |
284 |
|
|
*ai = i; |
285 |
|
|
return 0; |
286 |
|
|
} |
287 |
|
|
} |
288 |
|
|
return 1; |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
/* Removes element ai from the heap, returns 0 upon success. */ |
292 |
|
|
int airHeapRemove(airHeap *h, unsigned int ai) { |
293 |
|
|
unsigned int old_invidx_ai; |
294 |
|
|
if (h==NULL || h->key_a->len<=ai) |
295 |
|
|
return 1; |
296 |
|
|
/* in the tree, replace ai with last element */ |
297 |
|
|
old_invidx_ai=h->invidx[ai]; |
298 |
|
|
h->idx[h->invidx[ai]]=h->idx[h->key_a->len-1]; |
299 |
|
|
h->invidx[h->idx[h->key_a->len-1]]=h->invidx[ai]; |
300 |
|
|
/* remove ai - copy last element over, then drop it */ |
301 |
|
|
if (ai!=h->key_a->len-1) { |
302 |
|
|
h->key[ai]=h->key[h->key_a->len-1]; |
303 |
|
|
if (h->data_a!=NULL) { |
304 |
|
|
memcpy((char*)h->data_a->data+ai*h->data_a->unit, |
305 |
|
|
(char*)h->data_a->data+(h->key_a->len-1)*h->data_a->unit, |
306 |
|
|
h->data_a->unit); |
307 |
|
|
} |
308 |
|
|
h->idx[h->invidx[h->key_a->len-1]]=ai; |
309 |
|
|
h->invidx[ai]=h->invidx[h->key_a->len-1]; |
310 |
|
|
} |
311 |
|
|
heapLenIncr(h, -1); |
312 |
|
|
/* push moved element downheap */ |
313 |
|
|
if (old_invidx_ai<h->key_a->len) |
314 |
|
|
downheap(h, old_invidx_ai); |
315 |
|
|
return 0; |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
/* Changes the key (and optional data) of the element ai, re-sorting |
319 |
|
|
* the heap if necessary. Returns 0 upon success. */ |
320 |
|
|
int airHeapUpdate(airHeap *h, unsigned int ai, double newKey, |
321 |
|
|
const void *newData) { |
322 |
|
|
double oldkey; |
323 |
|
|
if (h==NULL || h->key_a->len<=ai) |
324 |
|
|
return 1; |
325 |
|
|
oldkey = h->key[ai]; |
326 |
|
|
/* replace key and data */ |
327 |
|
|
h->key[ai] = newKey; |
328 |
|
|
if (h->data_a!=NULL && newData!=NULL) { |
329 |
|
|
memcpy((char*)h->data_a->data+ai*h->data_a->unit, |
330 |
|
|
newData, h->data_a->unit); |
331 |
|
|
} |
332 |
|
|
if (oldkey<newKey) downheap(h, h->invidx[ai]); |
333 |
|
|
else upheap(h, h->invidx[ai]); |
334 |
|
|
return 0; |
335 |
|
|
} |