/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *====================================================================*/ /*! * \file heap.c *
 *
 *      Create/Destroy L_Heap
 *          L_HEAP         *lheapCreate()
 *          void            lheapDestroy()
 *
 *      Operations to add/remove to/from the heap
 *          l_int32         lheapAdd()
 *          static l_int32  lheapExtendArray()
 *          void           *lheapRemove()
 *
 *      Other accessors
 *          l_int32         lheapGetCount()
 *          void           *lheapGetElement()
 *
 *      Heap sort
 *          l_int32         lheapSort()
 *          l_int32         lheapSortStrictOrder()
 *
 *      Low-level heap operations
 *          static l_int32  lheapSwapUp()
 *          static l_int32  lheapSwapDown()
 *
 *      Debug output
 *          l_int32         lheapPrint()
 *
 *    The L_Heap is useful to implement a priority queue, that is sorted
 *    on a key in each element of the heap.  The heap is an array
 *    of nearly arbitrary structs, with a l_float32 the first field.
 *    This field is the key on which the heap is sorted.
 *
 *    Internally, we keep track of the heap size, n.  The item at the
 *    root of the heap is at the head of the array.  Items are removed
 *    from the head of the array and added to the end of the array.
 *    When an item is removed from the head, the item at the end
 *    of the array is moved to the head.  When items are either
 *    added or removed, it is usually necessary to swap array items
 *    to restore the heap order.  It is guaranteed that the number
 *    of swaps does not exceed log(n).
 *
 *    --------------------------  N.B.  ------------------------------
 *    The items on the heap (or, equivalently, in the array) are cast
 *    to void*.  Their key is a l_float32, and it is REQUIRED that the
 *    key be the first field in the struct.  That allows us to get the
 *    key by simply dereferencing the struct.  Alternatively, we could
 *    choose (but don't) to pass an application-specific comparison
 *    function into the heap operation functions.
 *    --------------------------  N.B.  ------------------------------
 * 
*/ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include "allheaders.h" /* Bounds on initial array size */ static const l_uint32 MaxPtrArraySize = 100000; static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */ #define SWAP_ITEMS(i, j) { void *tempitem = lh->array[(i)]; \ lh->array[(i)] = lh->array[(j)]; \ lh->array[(j)] = tempitem; } /* Static functions */ static l_int32 lheapExtendArray(L_HEAP *lh); static l_ok lheapSwapUp(L_HEAP *lh, l_int32 index); static l_ok lheapSwapDown(L_HEAP *lh); /*--------------------------------------------------------------------------* * L_Heap create/destroy * *--------------------------------------------------------------------------*/ /*! * \brief lheapCreate() * * \param[in] n size of ptr array to be alloc'd; use 0 for default * \param[in] direction L_SORT_INCREASING, L_SORT_DECREASING * \return lheap, or NULL on error */ L_HEAP * lheapCreate(l_int32 n, l_int32 direction) { L_HEAP *lh; PROCNAME("lheapCreate"); if (n < InitialPtrArraySize || n > MaxPtrArraySize) n = InitialPtrArraySize; /* Allocate ptr array and initialize counters. */ lh = (L_HEAP *)LEPT_CALLOC(1, sizeof(L_HEAP)); if ((lh->array = (void **)LEPT_CALLOC(n, sizeof(void *))) == NULL) { lheapDestroy(&lh, FALSE); return (L_HEAP *)ERROR_PTR("ptr array not made", procName, NULL); } lh->nalloc = n; lh->n = 0; lh->direction = direction; return lh; } /*! * \brief lheapDestroy() * * \param[in,out] plh will be set to null before returning * \param[in] freeflag TRUE to free each remaining struct in the array * \return void * *
 * Notes:
 *      (1) Use %freeflag == TRUE when the items in the array can be
 *          simply destroyed using free.  If those items require their
 *          own destroy function, they must be destroyed before
 *          calling this function, and then this function is called
 *          with %freeflag == FALSE.
 *      (2) To destroy the lheap, we destroy the ptr array, then
 *          the lheap, and then null the contents of the input ptr.
 * 
*/ void lheapDestroy(L_HEAP **plh, l_int32 freeflag) { l_int32 i; L_HEAP *lh; PROCNAME("lheapDestroy"); if (plh == NULL) { L_WARNING("ptr address is NULL\n", procName); return; } if ((lh = *plh) == NULL) return; if (freeflag) { /* free each struct in the array */ for (i = 0; i < lh->n; i++) LEPT_FREE(lh->array[i]); } else if (lh->n > 0) { /* freeflag == FALSE but elements exist on array */ L_WARNING("memory leak of %d items in lheap!\n", procName, lh->n); } if (lh->array) LEPT_FREE(lh->array); LEPT_FREE(lh); *plh = NULL; } /*--------------------------------------------------------------------------* * Operations to add/remove to/from the heap * *--------------------------------------------------------------------------*/ /*! * \brief lheapAdd() * * \param[in] lh heap * \param[in] item to be added to the tail of the heap * \return 0 if OK, 1 on error */ l_ok lheapAdd(L_HEAP *lh, void *item) { PROCNAME("lheapAdd"); if (!lh) return ERROR_INT("lh not defined", procName, 1); if (!item) return ERROR_INT("item not defined", procName, 1); /* If necessary, expand the allocated array by a factor of 2 */ if (lh->n >= lh->nalloc) { if (lheapExtendArray(lh)) return ERROR_INT("extension failed", procName, 1); } /* Add the item */ lh->array[lh->n] = item; lh->n++; /* Restore the heap */ lheapSwapUp(lh, lh->n - 1); return 0; } /*! * \brief lheapExtendArray() * * \param[in] lh heap * \return 0 if OK, 1 on error */ static l_int32 lheapExtendArray(L_HEAP *lh) { PROCNAME("lheapExtendArray"); if (!lh) return ERROR_INT("lh not defined", procName, 1); if ((lh->array = (void **)reallocNew((void **)&lh->array, sizeof(void *) * lh->nalloc, 2 * sizeof(void *) * lh->nalloc)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); lh->nalloc = 2 * lh->nalloc; return 0; } /*! * \brief lheapRemove() * * \param[in] lh heap * \return ptr to item popped from the root of the heap, * or NULL if the heap is empty or on error */ void * lheapRemove(L_HEAP *lh) { void *item; PROCNAME("lheapRemove"); if (!lh) return (void *)ERROR_PTR("lh not defined", procName, NULL); if (lh->n == 0) return NULL; item = lh->array[0]; lh->array[0] = lh->array[lh->n - 1]; /* move last to the head */ lh->array[lh->n - 1] = NULL; /* set ptr to null */ lh->n--; lheapSwapDown(lh); /* restore the heap */ return item; } /*--------------------------------------------------------------------------* * Other accessors * *--------------------------------------------------------------------------*/ /*! * \brief lheapGetCount() * * \param[in] lh heap * \return count, or 0 on error */ l_int32 lheapGetCount(L_HEAP *lh) { PROCNAME("lheapGetCount"); if (!lh) return ERROR_INT("lh not defined", procName, 0); return lh->n; } /*! * \brief lheapGetElement() * * \param[in] lh heap * \param[in] index into the internal heap array * \return ptr to the element at array[index], or NULL on error * *
 * Notes:
 *      (1) This is useful for retrieving an arbitrary element in the
 *          heap array without disturbing the heap.  It allows all the
 *          elements on the heap to be queried in linear time; for
 *          example, to find the min or max of some value.
 *      (2) Tbe retrieved element is owned by the heap.  Do not destroy it.
 * 
*/ void * lheapGetElement(L_HEAP *lh, l_int32 index) { PROCNAME("lheapGetElement"); if (!lh) return ERROR_PTR("lh not defined", procName, NULL); if (index < 0 || index >= lh->n) return ERROR_PTR("invalid index", procName, NULL); return (void *)lh->array[index]; } /*--------------------------------------------------------------------------* * Heap sort * *--------------------------------------------------------------------------*/ /*! * \brief lheapSort() * * \param[in] lh heap, with internal array * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This sorts an array into heap order.  If the heap is already
 *          in heap order for the direction given, this has no effect.
 * 
*/ l_ok lheapSort(L_HEAP *lh) { l_int32 i; PROCNAME("lheapSort"); if (!lh) return ERROR_INT("lh not defined", procName, 1); for (i = 0; i < lh->n; i++) lheapSwapUp(lh, i); return 0; } /*! * \brief lheapSortStrictOrder() * * \param[in] lh heap, with internal array * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This sorts a heap into strict order.
 *      (2) For each element, starting at the end of the array and
 *          working forward, the element is swapped with the head
 *          element and then allowed to swap down onto a heap of
 *          size reduced by one.  The result is that the heap is
 *          reversed but in strict order.  The array elements are
 *          then reversed to put it in the original order.
 * 
*/ l_ok lheapSortStrictOrder(L_HEAP *lh) { l_int32 i, index, size; PROCNAME("lheapSortStrictOrder"); if (!lh) return ERROR_INT("lh not defined", procName, 1); /* Start from a sorted heap */ lheapSort(lh); size = lh->n; /* save the actual size */ for (i = 0; i < size; i++) { index = size - i; SWAP_ITEMS(0, index - 1); lh->n--; /* reduce the apparent heap size by 1 */ lheapSwapDown(lh); } lh->n = size; /* restore the size */ for (i = 0; i < size / 2; i++) /* reverse */ SWAP_ITEMS(i, size - i - 1); return 0; } /*--------------------------------------------------------------------------* * Low-level heap operations * *--------------------------------------------------------------------------*/ /*! * \brief lheapSwapUp() * * \param[in] lh heap * \param[in] index of array corresponding to node to be swapped up * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This is called after a new item is put on the heap, at the
 *          bottom of a complete tree.
 *      (2) To regain the heap order, we let it bubble up,
 *          iteratively swapping with its parent, until it either
 *          reaches the root of the heap or it finds a parent that
 *          is in the correct position already vis-a-vis the child.
 * 
*/ static l_ok lheapSwapUp(L_HEAP *lh, l_int32 index) { l_int32 ip; /* index to heap for parent; 1 larger than array index */ l_int32 ic; /* index into heap for child */ l_float32 valp, valc; PROCNAME("lheapSwapUp"); if (!lh) return ERROR_INT("lh not defined", procName, 1); if (index < 0 || index >= lh->n) return ERROR_INT("invalid index", procName, 1); ic = index + 1; /* index into heap: add 1 to array index */ if (lh->direction == L_SORT_INCREASING) { while (1) { if (ic == 1) /* root of heap */ break; ip = ic / 2; valc = *(l_float32 *)(lh->array[ic - 1]); valp = *(l_float32 *)(lh->array[ip - 1]); if (valp <= valc) break; SWAP_ITEMS(ip - 1, ic - 1); ic = ip; } } else { /* lh->direction == L_SORT_DECREASING */ while (1) { if (ic == 1) /* root of heap */ break; ip = ic / 2; valc = *(l_float32 *)(lh->array[ic - 1]); valp = *(l_float32 *)(lh->array[ip - 1]); if (valp >= valc) break; SWAP_ITEMS(ip - 1, ic - 1); ic = ip; } } return 0; } /*! * \brief lheapSwapDown() * * \param[in] lh heap * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This is called after an item has been popped off the
 *          root of the heap, and the last item in the heap has
 *          been placed at the root.
 *      (2) To regain the heap order, we let it bubble down,
 *          iteratively swapping with one of its children.  For a
 *          decreasing sort, it swaps with the largest child; for
 *          an increasing sort, the smallest.  This continues until
 *          it either reaches the lowest level in the heap, or the
 *          parent finds that neither child should swap with it
 *          (e.g., for a decreasing heap, the parent is larger
 *          than or equal to both children).
 * 
*/ static l_ok lheapSwapDown(L_HEAP *lh) { l_int32 ip; /* index to heap for parent; 1 larger than array index */ l_int32 icr, icl; /* index into heap for left/right children */ l_float32 valp, valcl, valcr; PROCNAME("lheapSwapDown"); if (!lh) return ERROR_INT("lh not defined", procName, 1); if (lheapGetCount(lh) < 1) return 0; ip = 1; /* index into top of heap: corresponds to array[0] */ if (lh->direction == L_SORT_INCREASING) { while (1) { icl = 2 * ip; if (icl > lh->n) break; valp = *(l_float32 *)(lh->array[ip - 1]); valcl = *(l_float32 *)(lh->array[icl - 1]); icr = icl + 1; if (icr > lh->n) { /* only a left child; no iters below */ if (valp > valcl) SWAP_ITEMS(ip - 1, icl - 1); break; } else { /* both children exist; swap with the smallest if bigger */ valcr = *(l_float32 *)(lh->array[icr - 1]); if (valp <= valcl && valp <= valcr) /* smaller than both */ break; if (valcl <= valcr) { /* left smaller; swap */ SWAP_ITEMS(ip - 1, icl - 1); ip = icl; } else { /* right smaller; swap */ SWAP_ITEMS(ip - 1, icr - 1); ip = icr; } } } } else { /* lh->direction == L_SORT_DECREASING */ while (1) { icl = 2 * ip; if (icl > lh->n) break; valp = *(l_float32 *)(lh->array[ip - 1]); valcl = *(l_float32 *)(lh->array[icl - 1]); icr = icl + 1; if (icr > lh->n) { /* only a left child; no iters below */ if (valp < valcl) SWAP_ITEMS(ip - 1, icl - 1); break; } else { /* both children exist; swap with the biggest if smaller */ valcr = *(l_float32 *)(lh->array[icr - 1]); if (valp >= valcl && valp >= valcr) /* bigger than both */ break; if (valcl >= valcr) { /* left bigger; swap */ SWAP_ITEMS(ip - 1, icl - 1); ip = icl; } else { /* right bigger; swap */ SWAP_ITEMS(ip - 1, icr - 1); ip = icr; } } } } return 0; } /*---------------------------------------------------------------------* * Debug output * *---------------------------------------------------------------------*/ /*! * \brief lheapPrint() * * \param[in] fp file stream * \param[in] lh heap * \return 0 if OK; 1 on error */ l_ok lheapPrint(FILE *fp, L_HEAP *lh) { l_int32 i; PROCNAME("lheapPrint"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!lh) return ERROR_INT("lh not defined", procName, 1); fprintf(fp, "\n L_Heap: nalloc = %d, n = %d, array = %p\n", lh->nalloc, lh->n, lh->array); for (i = 0; i < lh->n; i++) fprintf(fp, "keyval[%d] = %f\n", i, *(l_float32 *)lh->array[i]); return 0; }