mirror of http://192.168.1.51:8099/lmh188/twain3.0
546 lines
16 KiB
C
546 lines
16 KiB
C
/*====================================================================*
|
|
- 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
|
|
* <pre>
|
|
*
|
|
* 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()
|
|
*
|
|
* Heap operations
|
|
* l_int32 lheapSwapUp()
|
|
* l_int32 lheapSwapDown()
|
|
* l_int32 lheapSort()
|
|
* l_int32 lheapSortStrictOrder()
|
|
*
|
|
* Accessors
|
|
* l_int32 lheapGetCount()
|
|
*
|
|
* 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. ------------------------------
|
|
* </pre>
|
|
*/
|
|
|
|
#include <string.h>
|
|
#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 function */
|
|
static l_int32 lheapExtendArray(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
|
|
*
|
|
* <pre>
|
|
* 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.
|
|
* </pre>
|
|
*/
|
|
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;
|
|
|
|
return;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* Accessors *
|
|
*--------------------------------------------------------------------------*/
|
|
/*!
|
|
* \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)
|
|
lheapExtendArray(lh);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \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;
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* 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
|
|
*
|
|
* <pre>
|
|
* 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.
|
|
* </pre>
|
|
*/
|
|
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
|
|
*
|
|
* <pre>
|
|
* 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).
|
|
* </pre>
|
|
*/
|
|
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;
|
|
}
|
|
|
|
|
|
/*!
|
|
* \brief lheapSort()
|
|
*
|
|
* \param[in] lh heap, with internal array
|
|
* \return 0 if OK, 1 on error
|
|
*
|
|
* <pre>
|
|
* 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.
|
|
* </pre>
|
|
*/
|
|
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
|
|
*
|
|
* <pre>
|
|
* 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.
|
|
* </pre>
|
|
*/
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
* 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;
|
|
}
|