/* * heap.c - * * Routines to create and manipulate heaps. * * ********************************************************************* * * Copyright (C) 1985, 1990 Regents of the University of California. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. The University of California * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #ifndef lint static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/utils/heap.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $"; #endif /* not lint */ #include #include #include "utils/magic.h" #include "utils/geometry.h" #include "utils/heap.h" #include "utils/utils.h" #include "utils/malloc.h" /* C99 compat */ #include "textio/textio.h" #define heapLeft(used,i) ((used) < ( (i)<<1) ? 0 : (i)+(i) ) #define heapRight(used,i) ((used) < (((i)<<1)+1) ? 0 : (i)+(i)+1) #define KEY_LESS_COND(t, l, i1, i2, stmt) \ switch (t) { \ case HE_INT: if (l[i1].he_int < l[i2].he_int) stmt; \ break; \ case HE_DLONG: if ((l[i1].he_dlong < l[i2].he_dlong)) stmt; \ break; \ case HE_DOUBLE: if (l[i1].he_double < l[i2].he_double) stmt; \ break; \ case HE_FLOAT: if (l[i1].he_float < l[i2].he_float) stmt; \ break; \ } #define KEY_LE_COND(t, l, i1, i2, stmt) \ switch (t) { \ case HE_INT: if (l[i1].he_int <= l[i2].he_int) stmt; \ break; \ case HE_DLONG: if ((l[i1].he_dlong <=l[i2].he_dlong)) stmt; \ break; \ case HE_DOUBLE: if (l[i1].he_double <= l[i2].he_double) stmt; \ break; \ case HE_FLOAT: if (l[i1].he_float <= l[i2].he_float) stmt; \ break; \ } #define KEY_GREATER_COND(t, l, i1, i2, stmt) \ switch (t) { \ case HE_INT: if (l[i1].he_int > l[i2].he_int) stmt; \ break; \ case HE_DLONG:if(l[i1].he_dlong > l[i2].he_dlong) stmt; \ break; \ case HE_DOUBLE: if (l[i1].he_double > l[i2].he_double) stmt; \ break; \ case HE_FLOAT: if (l[i1].he_float > l[i2].he_float) stmt; \ break; \ } #define KEY_GE_COND(t, l, i1, i2, stmt) \ switch (t) { \ case HE_INT: if (l[i1].he_int >= l[i2].he_int) stmt; \ break; \ case HE_DLONG: if ((l[i1].he_dlong >= l[i2].he_dlong)) stmt; \ break; \ case HE_DOUBLE: if (l[i1].he_double >= l[i2].he_double) stmt; \ break; \ case HE_FLOAT: if (l[i1].he_float >= l[i2].he_float) stmt; \ break; \ } /* Forward declarations */ extern void heapify(); /* * ---------------------------------------------------------------------------- * * HeapInit -- * HeapInitType -- * * Initialize a heap. The first form is a heap with integer keys; the * second allows specification of the type of key to use. * * Note that with this addressing scheme it is necessary to allocate * 2**n + 1 locations for the heap block, location 0 being unused. * * Results: * None. * * Side effects: * Allocates storage for the heap. Initializes the fields in the heap * struct. Memory is allocated. * * ---------------------------------------------------------------------------- */ void HeapInit( Heap *heap, /* Pointer to a heap struct */ int size, /* Initial size of heap */ int descending, /* TRUE if largest on top */ int stringIds) /* TRUE if entry id's are strings */ { HeapInitType(heap, size, descending, stringIds, HE_INT); } void HeapInitType( Heap *heap, /* Pointer to a heap struct */ int size, /* Initial size of heap */ int descending, /* TRUE if largest on top */ int stringIds, /* TRUE if entry id's are strings */ int keyType) /* Type of keys to use */ { if (size < 0) size = -size; heap->he_size = 2; while (size > heap->he_size) heap->he_size <<= 1; heap->he_used = 0; heap->he_built = 0; heap->he_big = descending; heap->he_stringId = stringIds; /* * Add 2 to the size because location 0 is used as a temporary, * and location (2**n) + 1 is the last leaf */ switch (heap->he_keyType = keyType) { case HE_INT: case HE_DLONG: case HE_FLOAT: case HE_DOUBLE: break; default: TxError("Unsupported key type: %d\n", keyType); break; } heap->he_list = (HeapEntry *) mallocMagic((unsigned) ((heap->he_size+2) * sizeof (HeapEntry))); ASSERT(heap->he_list != NULL, "Malloc failed in HeapInit"); } /* * ---------------------------------------------------------------------------- * * HeapKill -- * * Deallocate all storage associated with the heap. * * Results: * None. * * Side effects: * Storage is freed. Fields in the heap record are reset. If func is * not NULL then call it with each heap element before freeing the heap * entry. * * ---------------------------------------------------------------------------- */ void HeapKill( Heap *heap, /* The heap being killed */ cb_heap_kill_t func) /* Some function to free each id element */ { int i; /* * Free used locations except 0, since that location is reserved to pass * back values. */ if (func) for (i = 1; i <= heap->he_used; i++) (*func) (heap, i); /** @invoke cb_heap_kill_t */ freeMagic((char *) heap->he_list); heap->he_list = NULL; } /* * ---------------------------------------------------------------------------- * * HeapFreeIdFunc -- * * Supplied function to HeapKill. Frees the referenced entry id if it * is a string, otherwise do nothing. * * Results: * None. * * Side effects: * Memory is freed. * * ---------------------------------------------------------------------------- */ void HeapFreeIdFunc( Heap *heap, /* The heap in which the free is performed */ int i) /* The index of the entry whose id gets freed */ { if (heap->he_stringId) freeMagic(heap->he_list[i].he_id); } /* * ---------------------------------------------------------------------------- * * HeapRemoveTop -- * Delete the top element from the heap. * * Results: * Pointer to the removed heap element, which is the same as the entry * pointer passed to the function from outside, or NULL if no entries * remain. * * Side effects: * If the heap has been touched, restore the heap property before removing * the top element. * * ---------------------------------------------------------------------------- */ HeapEntry * HeapRemoveTop( Heap *heap, /* The heap from which the top is removed */ HeapEntry *entry) /* Return the value in this struct */ { int i; if (heap->he_used == 0) return (HeapEntry *) NULL; if (!heap->he_built) for (i = heap->he_used; i > 0; i--) heapify(heap, i); heap->he_built = heap->he_used; *entry = heap->he_list[1]; heap->he_list[1] = heap->he_list[heap->he_used]; heap->he_used--; heapify(heap, 1); return entry; } /* * ---------------------------------------------------------------------------- * * HeapLookAtTop -- * * Return pointer to top element, but don't remove it. * * Results: * Pointer to the top heap element, or NULL if heap is empty. * * Side effects: * If the heap has been touched, restore the heap property before * returning the top element. * * ---------------------------------------------------------------------------- */ HeapEntry * HeapLookAtTop( Heap *heap) { int i; if (heap->he_used == 0) return (HeapEntry *) NULL; if (!heap->he_built) for (i = heap->he_used; i > 0; i--) heapify(heap, i); heap->he_built = heap->he_used; return &heap->he_list[1]; } /* * ---------------------------------------------------------------------------- * * heapify -- * * Restore the heap property to the heap, where the root has changed. * * Results: * None. * * Side effects: * Things get shuffled around in the heap. Location 0 is trashed. * * ---------------------------------------------------------------------------- */ void heapify( Heap *heap, int root) { HeapEntry *list = heap->he_list; int x, r, used = heap->he_used; int keyType = heap->he_keyType; if (heap->he_big) { /* Biggest one on the top of the heap */ while (1) { if ((x = heapLeft(used, root)) == 0) break; if ((r = heapRight(used, root))) KEY_LESS_COND(keyType, list, x, r, x = r); KEY_LE_COND(keyType, list, x, root, return); *list = list[root]; list[root] = list[x]; list[x] = *list; root = x; } } else { /* Smallest one on the top of the heap */ while (1) { if ((x = heapLeft(used, root)) == 0) break; if ((r = heapRight(used, root))) KEY_GREATER_COND(keyType, list, x, r, x = r); KEY_GE_COND(keyType, list, x, root, return); *list = list[root]; list[root] = list[x]; list[x] = *list; root = x; } } } /* * ---------------------------------------------------------------------------- * * HeapAdd -- * * Add an item to the bottom of the heap. Restore the heap structure * by propagating the item upwards. * * Results: * None. * * Side effects: * Things get shuffled around in the heap. * Free the old block and allocate a larger block if necessary. * * Warning: * Something awful may happen if the id value was declared a string in * HeapInit and you provide something else. * * Note: * Because throwing pointers to indeterminate types (int, float, etc.) * into the second argument produces results with unititialized fields, * use of HeapAdd() is now deprecated, in favor of the determinate-type * versions HeapAddInt, etc. (see below). These routines also protect * against writing any data type into a heap initialized as a different * data type. * * ---------------------------------------------------------------------------- */ void HeapAdd( Heap *heap, union heUnion *pKey, char *id) { HeapEntry *list = heap->he_list; int i, cmp, keyType; keyType = heap->he_keyType; if (heap->he_used == heap->he_size) { HeapEntry *new; /* Need to recopy to a larger area */ new = (HeapEntry *) mallocMagic((unsigned) ((2 * heap->he_size + 2) * sizeof (HeapEntry))); bcopy((char *) list, (char *) new, (heap->he_size + 2) * sizeof (HeapEntry)); freeMagic((char *) heap->he_list); heap->he_list = list = new; heap->he_size *= 2; } i = ++heap->he_used; list[i].he_union = *pKey; if (heap->he_stringId) list[i].he_id = StrDup((char **) NULL, id); else list[i].he_id = id; if (heap->he_built) { if (heap->he_big) { /* Biggest on the top */ while (1) { /* If odd then new entry is the right half of a pair */ cmp = i; if ((i & 1) && (i != 1)) KEY_LE_COND(keyType, list, i, i-1, cmp = i-1); /* Find parent. If 0 then at the root so quit */ if ((i >>= 1) == 0) return; KEY_LE_COND(keyType, list, cmp, i, return); list[0] = list[cmp]; list[cmp] = list[i]; list[i] = list[0]; heapify(heap, cmp); } } else { /* Smallest on top */ while (1) { /* If odd then new entry is the right half of a pair */ cmp = i; if ((i & 1) && (i != 1)) KEY_GE_COND(keyType, list, i, i-1, cmp = i-1); /* Find parent. If 0 then at the root so quit */ if ((i >>= 1) == 0) return; KEY_GE_COND(keyType, list, cmp, i, return); list[0] = list[cmp]; list[cmp] = list[i]; list[i] = list[0]; heapify(heap, cmp); } } } } /* * ---------------------------------------------------------------------------- * HeapAddInt, HeapAddDLong, HeapAddFloat, HeapAddDouble -- * * Type-determinate versions of HeapAdd(). Note that unlike HeapAdd(), * the second argument is the value, not a pointer to the value. * * ---------------------------------------------------------------------------- */ void HeapAddInt( Heap *heap, int data, char *id) { union heUnion pKey; ASSERT(heap->he_keyType == HE_INT, "Attempt to add type int to non-int heap."); pKey.hu_int = data; HeapAdd(heap, &pKey, id); } void HeapAddDLong( Heap *heap, dlong data, char *id) { union heUnion pKey; ASSERT(heap->he_keyType == HE_DLONG, "Attempt to add type double long to non-double long heap."); pKey.hu_dlong = data; HeapAdd(heap, &pKey, id); } void HeapAddFloat( Heap *heap, float data, char *id) { union heUnion pKey; ASSERT(heap->he_keyType == HE_FLOAT, "Attempt to add type float to non-float heap."); pKey.hu_float = data; HeapAdd(heap, &pKey, id); } void HeapAddDouble( Heap *heap, double data, char *id) { union heUnion pKey; ASSERT(heap->he_keyType == HE_DOUBLE, "Attempt to add type double to non-double heap."); pKey.hu_double = data; HeapAdd(heap, &pKey, id); } /* * ---------------------------------------------------------------------------- * * HeapDump -- * Dump the contents of the heap for debugging purposes. * * Results: * None. * * Side effects: * None. * * ---------------------------------------------------------------------------- */ void HeapDump( Heap *heap) { int i; if (heap->he_big) printf("Heap with biggest on the top\n"); else printf("Heap with smallest on the top\n"); for (i = 1; i <= heap->he_used; i++) { printf("[%d]: Key ", i); switch (heap->he_keyType) { case HE_INT: printf("%d", heap->he_list[i].he_int); break; case HE_FLOAT: printf("%f", heap->he_list[i].he_float); break; case HE_DOUBLE: printf("%f", heap->he_list[i].he_double); break; case HE_DLONG: printf("%"DLONG_PREFIX"d", heap->he_list[i].he_dlong); break; } if (heap->he_stringId == TRUE) printf("//id %s; ", heap->he_list[i].he_id); else printf("//id %p; ", heap->he_list[i].he_id); } printf("\n"); }