summaryrefslogtreecommitdiff
path: root/src/tests/ecore_sheap.c
blob: 448be979187f5b4328a7dd0147a9c5e5f6d07b1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include <string.h>

#include "Ecore_Data.h"

#define HEAP_INCREMENT 4096

#define PARENT(i) (i / 2)
#define LEFT(i) (2 * i)
#define RIGHT(i) (2 * i + 1)

static void _ecore_sheap_heapify(Ecore_Sheap *heap, int i);
static void _ecore_sheap_update_data(Ecore_Sheap *heap);

/**
 * Allocate and initialize a new binary heap
 * @param compare The function for comparing keys, NULL for direct comparison
 * @param size    The number of elements to allow in the heap
 * @return A pointer to the newly allocated binary heap on success, NULL on
 * failure.
 */
EAPI Ecore_Sheap *
ecore_sheap_new(Ecore_Compare_Cb compare, int size)
{
   Ecore_Sheap *heap = NULL;

   heap = (Ecore_Sheap *)malloc(sizeof(Ecore_Sheap));
   if (!heap)
      return NULL;

   memset(heap, 0, sizeof(Ecore_Sheap));

   if (!ecore_sheap_init(heap, compare, size))
     {
        FREE(heap);
        return NULL;
     }

   return heap;
}

/**
 * Initialize a binary heap to default values
 * @param heap    The heap to initialize
 * @param compare The function for comparing keys, NULL for direct comparison
 * @param size    The number of elements to allow in the heap
 * @return        TRUE on success, FALSE on failure
 */
EAPI int
ecore_sheap_init(Ecore_Sheap *heap, Ecore_Compare_Cb compare, int size)
{
   CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);

   heap->space = size;
   if (!compare)
      heap->compare = ecore_direct_compare;
   else
      heap->compare = compare;

   heap->order = ECORE_SORT_MIN;

   heap->data = (void **)malloc(heap->space * sizeof(void *));
   if (!heap->data)
      return FALSE;

   memset(heap->data, 0, heap->space * sizeof(void *));

   return TRUE;
}

/**
 * Free up the memory used by the heap
 *
 * Frees the memory used by @a heap, calls the destroy function on each data
 * item if necessary.
 *
 * @param  heap The heap to be freed
 */
EAPI void
ecore_sheap_destroy(Ecore_Sheap *heap)
{
   int i;

   CHECK_PARAM_POINTER("heap", heap);

   /*
    * Free data in heap
    */
   if (heap->free_func)
      for (i = 0; i < heap->size; i++)
         heap->free_func(heap->data[i]);

   FREE(heap->data);

   FREE(heap);
}

/**
 * Set the function for freeing data.
 * @param  heap      The heap that will use this function when nodes are
 *                   destroyed.
 * @param  free_func The function that will free the key data.
 * @return @c TRUE on successful set, @c FALSE otherwise.
 */
EAPI int
ecore_sheap_free_cb_set(Ecore_Sheap *heap, Ecore_Free_Cb free_func)
{
   CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);

   heap->free_func = free_func;

   return TRUE;
}

/**
 * Insert new data into the heap.
 * @param  heap The heap to insert @a data.
 * @param  data The data to add to @a heap.
 * @return TRUE on success, NULL on failure. Increases the size of the heap if
 *         it becomes larger than available space.
 */
EAPI int
ecore_sheap_insert(Ecore_Sheap *heap, void *data)
{
   int i;
   void *temp;
   int parent;
   int position;

   CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);

   /*
    * Increase the size of the allocated data area if there isn't enough
    * space available to add this data
    */
   if (heap->size >= heap->space)
      return FALSE;

   heap->sorted = FALSE;

   /*
    * Place the data at the end of the heap initially. Then determine the
    * parent and position in the array of it's parent.
    */
   heap->data[heap->size] = data;
   position = heap->size;
   heap->size++;
   i = heap->size;
   parent = PARENT(i) - 1;

   /*
    * Check the order of the heap to decide where to place the inserted
    * data. The loop is placed inside the if statement to reduce the
    * number of branching decisions that must be predicted.
    */
   if (heap->order == ECORE_SORT_MIN)
      while ((position > 0) && heap->compare(heap->data[parent],
                                             heap->data[position]) > 0)
        {

           /*
            * Swap the data with it's parents to move it up in
            * the heap.
            */
           temp = heap->data[position];
           heap->data[position] = heap->data[parent];
           heap->data[parent] = temp;

           /*
            * Now determine the new position for the next
            * iteration of the loop, as well as it's parents
            * position.
            */
           i = PARENT(i);
           position = i - 1;
           parent = PARENT(i) - 1;
        }
   else
      while ((position > 0) && heap->compare(heap->data[parent],
                                             heap->data[position]) < 0)
        {

           /*
            * Swap the data with it's parents to move it up in
            * the heap.
            */
           temp = heap->data[position];
           heap->data[position] = heap->data[PARENT(i) - 1];
           heap->data[PARENT(i) - 1] = temp;

           /*
            * Now determine the new position for the next
            * iteration of the loop, as well as it's parents
            * position.
            */
           i = PARENT(i);
           position = i - 1;
           parent = PARENT(i) - 1;
        }

   return TRUE;
}

/**
 * Extract the item at the top of the heap
 * @param  heap The heap to remove the top item
 * @return The top item of the heap on success, NULL on failure.
 * @note   The extract function maintains the heap properties after the
 *         extract.
 */
EAPI void *
ecore_sheap_extract(Ecore_Sheap *heap)
{
   void *extreme;

   if (heap->size < 1)
      return NULL;

   heap->sorted = FALSE;

   extreme = heap->data[0];
   heap->size--;
   heap->data[0] = heap->data[heap->size];

   _ecore_sheap_heapify(heap, 1);

   return extreme;
}

/**
 * Examine the item at the top of the heap
 * @param  heap The heap to examine the top item
 * @return The top item of the heap on success, NULL on failure.
 * @note   The function does not alter the heap.
 */
EAPI void *
ecore_sheap_extreme(Ecore_Sheap *heap)
{
   if (heap->size < 1)
      return NULL;

   return heap->data[0];
}

/**
 * Change the value of the specified item in the heap
 * @param heap   The heap to search for the item to change
 * @param item   The item in the heap to change
 * @param newval The new value assigned to the item in the heap
 * @return       TRUE on success, FALSE on failure.
 * @note         The heap does not free the old data since it must be passed
 *               in, so the caller can perform the free if desired.
 */
EAPI int
ecore_sheap_change(Ecore_Sheap *heap, void *item, void *newval)
{
   int i;

   CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);

   for (i = 0; i < heap->size && heap->compare(heap->data[i], item); i++) ;

   if (i < heap->size)
      heap->data[i] = newval;
   else
      return FALSE;

   /*
    * FIXME: This is not the correct procedure when a change occurs.
    */
   _ecore_sheap_heapify(heap, 1);

   return TRUE;
}

/**
 * Change the comparison function for the heap
 * @param  heap    The heap to change comparison function
 * @param  compare The new function for comparing nodes
 * @return TRUE on success, FALSE on failure.
 *
 * The comparison function is changed to @compare and the heap is heapified
 * by the new comparison.
 */
EAPI int
ecore_sheap_compare_set(Ecore_Sheap *heap, Ecore_Compare_Cb compare)
{
   CHECK_PARAM_POINTER_RETURN("heap", heap, FALSE);

   if (!compare)
      heap->compare = ecore_direct_compare;
   else
      heap->compare = compare;

   _ecore_sheap_update_data(heap);

   return TRUE;
}

/**
 * Change the order of the heap
 * @param  heap  The heap to change the order
 * @param  order The new order of the heap
 *
 * Changes the heap order of @heap and re-heapifies the data to this new
 * order. The default order is a min heap.
 */
EAPI void
ecore_sheap_order_set(Ecore_Sheap *heap, char order)
{
   CHECK_PARAM_POINTER("heap", heap);

   heap->order = order;

   _ecore_sheap_update_data(heap);
}

/**
 * Sort the data in the heap
 * @param  heap The heap to be sorted
 *
 * Sorts the data in the heap into the order that is used for the heap's
 * data.
 */
EAPI void
ecore_sheap_sort(Ecore_Sheap *heap)
{
   int i = 0;
   void **new_data;

   CHECK_PARAM_POINTER("heap", heap);

   new_data = (void **)malloc(heap->size * sizeof(void *));

   /*
    * Extract the heap and insert into the new data array in order.
    */
   while (heap->size > 0)
      new_data[i++] = ecore_sheap_extract(heap);

   /*
    * Free the old data array and update the heap with the new data, also
    * mark as sorted.
    */
   FREE(heap->data);
   heap->data = new_data;
   heap->size = i;
   heap->sorted = TRUE;
}

/*
 * Access the item at the ith position in the heap
 * @param  heap The heap to access the internal data
 * @param  i    The index of the data within the heap
 * @return The data located at the ith position within @heap on success,
 *         NULL on failure.
 * @note   The data is guaranteed to be in sorted order.
 */
EAPI inline void *
ecore_sheap_item(Ecore_Sheap *heap, int i)
{
   if (i >= heap->size)
      return NULL;

   /*
    * Make sure the data is sorted so we return the correct value.
    */
   if (!heap->sorted)
      ecore_sheap_sort(heap);

   return heap->data[i];
}

/*
 * Regain the heap properties starting at position i
 * @param  heap The heap to regain heap properties
 * @param  i    The position to start heapifying
 */
static void
_ecore_sheap_heapify(Ecore_Sheap *heap, int i)
{
   int extreme;
   int left = LEFT(i);
   int right = RIGHT(i);

   if (heap->order == ECORE_SORT_MIN)
     {
        if (left <= heap->size && heap->compare(heap->data[left - 1],
                                                heap->data[i - 1]) < 0)
           extreme = left;
        else
           extreme = i;

        if (right <= heap->size && heap->compare(heap->data[right - 1],
                                                 heap->data[extreme - 1]) < 0)
           extreme = right;
     }
   else
     {
        if (left <= heap->size && heap->compare(heap->data[left - 1],
                                                heap->data[i - 1]) > 0)
           extreme = left;
        else
           extreme = i;

        if (right <= heap->size && heap->compare(heap->data[right - 1],
                                                 heap->data[extreme - 1]) > 0)
           extreme = right;
     }

   /*
    * If the data needs to be swapped down the heap, recurse on
    * heapifying it's new placement.
    */
   if (extreme != i)
     {
        void *temp;

        temp = heap->data[extreme - 1];
        heap->data[extreme - 1] = heap->data[i - 1];
        heap->data[i - 1] = temp;

        _ecore_sheap_heapify(heap, extreme);
     }
}

static void
_ecore_sheap_update_data(Ecore_Sheap *heap)
{
   int i, old_size;
   void **data;

   /*
    * Track the old values from the heap
    */
   old_size = heap->size;
   data = heap->data;

   heap->size = 0;
   heap->data = malloc(heap->space * sizeof(void *));

   for (i = 0; i < old_size; i++)
      ecore_sheap_insert(heap, data[i]);

   FREE(data);
}

int
ecore_direct_compare(const void *key1, const void *key2)
{
   unsigned long k1, k2;

   k1 = (unsigned long)key1;
   k2 = (unsigned long)key2;

   if (k1 > k2)
      return 1;

   if (k1 < k2)
      return -1;

   return 0;
}