/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - This software is distributed in the hope that it will be - useful, but with NO WARRANTY OF ANY KIND. - No author or distributor accepts responsibility to anyone for the - consequences of using this software, or for whether it serves any - particular purpose or works at all, unless he or she says so in - writing. Everyone is granted permission to copy, modify and - redistribute this source code, for commercial or non-commercial - purposes, with the following restrictions: (1) the origin of this - source code must not be misrepresented; (2) modified versions must - be plainly marked as such; and (3) this notice may not be removed - or altered from any source or modified source distribution. *====================================================================*/ /* * numabasic.c * * Numa creation, destruction, copy, clone, etc. * NUMA *numaCreate() * NUMA *numaCreateFromIArray() * void *numaDestroy() * NUMA *numaCopy() * NUMA *numaClone() * l_int32 numaEmpty() * * Add/remove number (float or integer) * l_int32 numaAddNumber() * l_int32 numaExtendArray() * l_int32 numaInsertNumber() * l_int32 numaRemoveNumber() * l_int32 numaReplaceNumber() * * Numa accessors * l_int32 numaGetCount() * l_int32 numaSetCount() * l_int32 numaGetIValue() * l_int32 numaGetFValue() * l_int32 numaSetValue() * l_int32 numaShiftValue() * l_int32 *numaGetIArray() * l_float32 *numaGetFArray() * l_int32 numaGetRefcount() * l_int32 numaChangeRefcount() * l_int32 numaGetXParameters() * l_int32 numaSetXParameters() * l_int32 numaCopyXParameters() * * Serialize numa for I/O * l_int32 numaRead() * l_int32 numaReadStream() * l_int32 numaWrite() * l_int32 numaWriteStream() * * Numaa creation, destruction * NUMAA *numaaCreate() * void *numaaDestroy() * * Add Numa to Numaa * l_int32 numaaAddNuma() * l_int32 numaaExtendArray() * * Numaa accessors * l_int32 numaaGetCount() * l_int32 numaaGetNumberCount() * NUMA **numaaGetPtrArray() * NUMA *numaaGetNuma() * NUMA *numaaReplaceNuma() * l_int32 numaaAddNumber() * * Serialize numaa for I/O * l_int32 numaaRead() * l_int32 numaaReadStream() * l_int32 numaaWrite() * l_int32 numaaWriteStream() * * Numa2d creation, destruction * NUMA2D *numa2dCreate() * void *numa2dDestroy() * * Numa2d Accessors * l_int32 numa2dAddNumber() * l_int32 numa2dGetCount() * NUMA *numa2dGetNuma() * l_int32 numa2dGetFValue() * l_int32 numa2dGetIValue() * * NumaHash creation, destruction * NUMAHASH *numaHashCreate() * void *numaHashDestroy() * * NumaHash Accessors * NUMA *numaHashGetNuma() * void *numaHashAdd() * * (1) The numa is a struct, not an array. Always use the accessors * in this file, never the fields directly. * * (2) The number array holds l_float32 values. It can also * be used to store l_int32 values. * * (3) Storing and retrieving numbers: * * * to append a new number to the array, use numaAddNumber(). If * the number is an int, it will will automatically be converted * to l_float32 and stored. * * * to reset a value stored in the array, use numaSetValue(). * * * to increment or decrement a value stored in the array, * use numaShiftValue(). * * * to obtain a value from the array, use either numaGetIValue() * or numaGetFValue(), depending on whether you are retrieving * an integer or a float. This avoids doing an explicit cast, * such as * (a) return a l_float32 and cast it to an l_int32 * (b) cast the return directly to (l_float32 *) to * satisfy the function prototype, as in * numaGetFValue(na, index, (l_float32 *)&ival); [ugly!] * * (4) int <--> float conversions: * * Tradition dictates that type conversions go automatically from * l_int32 --> l_float32, even though it is possible to lose * precision for large integers, whereas you must cast (l_int32) * to go from l_float32 --> l_int32 because you're truncating * to the integer value. * * (5) As with other arrays in leptonica, the numa has both an allocated * size and a count of the stored numbers. When you add a number, it * goes on the end of the array, and causes a realloc if the array * is already filled. However, in situations where you want to * add numbers randomly into an array, such as when you build a * histogram, you must set the count of stored numbers in advance. * This is done with numaSetCount(). If you set a count larger * than the allocated array, it does a realloc to the size requested. * * (6) In situations where the data in a numa correspond to a function * y(x), the values can be either at equal spacings in x or at * arbitrary spacings. For the former, we can represent all x values * by two parameters: startx (corresponding to y[0]) and delx * for the change in x for adjacent values y[i] and y[i+1]. * startx and delx are initialized to 0.0 and 1.0, rsp. * For arbitrary spacings, we use a second numa, and the two * numas are typically denoted nay and nax. * * (7) The numa is also the basic struct used for histograms. Every numa * has startx and delx fields, initialized to 0.0 and 1.0, that can * be used to represent the "x" value for the location of the * first bin and the bin width, respectively. Accessors are the * numa*XParameters() functions. All functions that make numa * histograms must set these fields properly, and many functions * that use numa histograms rely on the correctness of these values. */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include "allheaders.h" static const l_int32 INITIAL_PTR_ARRAYSIZE = 50; /* n'importe quoi */ /*--------------------------------------------------------------------------* * Numa creation, destruction, copy, clone, etc. * *--------------------------------------------------------------------------*/ /*! * numaCreate() * * Input: size of number array to be alloc'd (0 for default) * Return: na, or null on error */ NUMA * numaCreate(l_int32 n) { NUMA *na; PROCNAME("numaCreate"); if (n <= 0) n = INITIAL_PTR_ARRAYSIZE; if ((na = (NUMA *)CALLOC(1, sizeof(NUMA))) == NULL) return (NUMA *)ERROR_PTR("na not made", procName, NULL); if ((na->array = (l_float32 *)CALLOC(n, sizeof(l_float32))) == NULL) return (NUMA *)ERROR_PTR("number array not made", procName, NULL); na->nalloc = n; na->n = 0; na->refcount = 1; na->startx = 0.0; na->delx = 1.0; return na; } /*! * numaCreateFromIArray() * * Input: int array * size (of the array) * Return: na, or null on error * * Notes: * (1) This just copies the data from the int array into the numa. * (2) The input array is NOT owned by the numa. */ NUMA * numaCreateFromIArray(l_int32 *array, l_int32 size) { l_int32 i; NUMA *na; PROCNAME("numaCreateFromIArray"); if (!array) return (NUMA *)ERROR_PTR("array not defined", procName, NULL); na = numaCreate(size); for (i = 0; i < size; i++) numaAddNumber(na, array[i]); return na; } /*! * numaDestroy() * * Input: &na (<to be nulled if it exists>) * Return: void * * Notes: * (1) Decrements the ref count and, if 0, destroys the numa. * (2) Always nulls the input ptr. */ void numaDestroy(NUMA **pna) { NUMA *na; PROCNAME("numaDestroy"); if (pna == NULL) { L_WARNING("ptr address is NULL", procName); return; } if ((na = *pna) == NULL) return; /* Decrement the ref count. If it is 0, destroy the numa. */ numaChangeRefcount(na, -1); if (numaGetRefcount(na) <= 0) { if (na->array) FREE(na->array); FREE(na); } *pna = NULL; return; } /*! * numaCopy() * * Input: na * Return: copy of numa, or null on error */ NUMA * numaCopy(NUMA *na) { l_int32 i; NUMA *cna; PROCNAME("numaCopy"); if (!na) return (NUMA *)ERROR_PTR("na not defined", procName, NULL); if ((cna = numaCreate(na->nalloc)) == NULL) return (NUMA *)ERROR_PTR("cna not made", procName, NULL); cna->startx = na->startx; cna->delx = na->delx; for (i = 0; i < na->n; i++) numaAddNumber(cna, na->array[i]); return cna; } /*! * numaClone() * * Input: na * Return: ptr to same numa, or null on error */ NUMA * numaClone(NUMA *na) { PROCNAME("numaClone"); if (!na) return (NUMA *)ERROR_PTR("na not defined", procName, NULL); numaChangeRefcount(na, 1); return na; } /*! * numaEmpty() * * Input: na * Return: 0 if OK; 1 on error * * Notes: * (1) This does not change the allocation of the array. * It just clears the number of stored numbers, so that * the array appears to be empty. */ l_int32 numaEmpty(NUMA *na) { PROCNAME("numaEmpty"); if (!na) return ERROR_INT("na not defined", procName, 1); na->n = 0; return 0; } /*--------------------------------------------------------------------------* * Number array: add number and extend array * *--------------------------------------------------------------------------*/ /*! * numaAddNumber() * * Input: na * val (float or int to be added; stored as a float) * Return: 0 if OK, 1 on error */ l_int32 numaAddNumber(NUMA *na, l_float32 val) { l_int32 n; PROCNAME("numaAddNumber"); if (!na) return ERROR_INT("na not defined", procName, 1); n = numaGetCount(na); if (n >= na->nalloc) numaExtendArray(na); na->array[n] = val; na->n++; return 0; } /*! * numaExtendArray() * * Input: na * Return: 0 if OK, 1 on error */ l_int32 numaExtendArray(NUMA *na) { PROCNAME("numaExtendArray"); if (!na) return ERROR_INT("na not defined", procName, 1); if ((na->array = (l_float32 *)reallocNew((void **)&na->array, sizeof(l_float32) * na->nalloc, 2 * sizeof(l_float32) * na->nalloc)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); na->nalloc *= 2; return 0; } /*! * numaInsertNumber() * * Input: na * index (location in na to insert new value) * val (float32 or integer to be added) * Return: 0 if OK, 1 on error * * Notes: * (1) This shifts na[i] --> na[i + 1] for all i >= index, * and then inserts val as na[index]. * (2) It should not be used repeatedly on large arrays, * because the function is O(n). * */ l_int32 numaInsertNumber(NUMA *na, l_int32 index, l_float32 val) { l_int32 i, n; PROCNAME("numaInsertNumber"); if (!na) return ERROR_INT("na not defined", procName, 1); n = numaGetCount(na); if (index < 0 || index > n) return ERROR_INT("index not in {0...n}", procName, 1); if (n >= na->nalloc) numaExtendArray(na); for (i = n; i > index; i--) na->array[i] = na->array[i - 1]; na->array[index] = val; na->n++; return 0; } /*! * numaRemoveNumber() * * Input: na * index (element to be removed) * Return: 0 if OK, 1 on error * * Notes: * (1) This shifts na[i] --> na[i - 1] for all i > index. * (2) It should not be used repeatedly on large arrays, * because the function is O(n). */ l_int32 numaRemoveNumber(NUMA *na, l_int32 index) { l_int32 i, n; PROCNAME("numaRemoveNumber"); if (!na) return ERROR_INT("na not defined", procName, 1); n = numaGetCount(na); if (index < 0 || index >= n) return ERROR_INT("index not in {0...n - 1}", procName, 1); for (i = index + 1; i < n; i++) na->array[i - 1] = na->array[i]; na->n--; return 0; } /*! * numaReplaceNumber() * * Input: na * index (element to be replaced) * val (new value to replace old one) * Return: 0 if OK, 1 on error */ l_int32 numaReplaceNumber(NUMA *na, l_int32 index, l_float32 val) { l_int32 n; PROCNAME("numaReplaceNumber"); if (!na) return ERROR_INT("na not defined", procName, 1); n = numaGetCount(na); if (index < 0 || index >= n) return ERROR_INT("index not in {0...n - 1}", procName, 1); na->array[index] = val; return 0; } /*----------------------------------------------------------------------* * Numa accessors * *----------------------------------------------------------------------*/ /*! * numaGetCount() * * Input: na * Return: count, or 0 if no numbers or on error */ l_int32 numaGetCount(NUMA *na) { PROCNAME("numaGetCount"); if (!na) return ERROR_INT("na not defined", procName, 0); return na->n; } /*! * numaSetCount() * * Input: na * newcount * Return: 0 if OK, 1 on error * * Notes: * (1) If newcount <= na->nalloc, this resets na->n. * Using newcount = 0 is equivalent to numaEmpty(). * (2) If newcount > na->nalloc, this causes a realloc * to a size na->nalloc = newcount. * (3) All the previously unused values in na are set to 0.0. */ l_int32 numaSetCount(NUMA *na, l_int32 newcount) { PROCNAME("numaSetCount"); if (!na) return ERROR_INT("na not defined", procName, 1); if (newcount > na->nalloc) { if ((na->array = (l_float32 *)reallocNew((void **)&na->array, sizeof(l_float32) * na->nalloc, sizeof(l_float32) * newcount)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); na->nalloc = newcount; } na->n = newcount; return 0; } /*! * numaGetFValue() * * Input: na * index (into numa) * &val (<return> float value; 0.0 on error) * Return: 0 if OK; 1 on error * * Notes: * (1) Caller may need to check the function return value to * decide if a 0.0 in the returned ival is valid. */ l_int32 numaGetFValue(NUMA *na, l_int32 index, l_float32 *pval) { PROCNAME("numaGetFValue"); if (!pval) return ERROR_INT("&val not defined", procName, 1); *pval = 0.0; if (!na) return ERROR_INT("na not defined", procName, 1); if (index < 0 || index >= na->n) return ERROR_INT("index not valid", procName, 1); *pval = na->array[index]; return 0; } /*! * numaGetIValue() * * Input: na * index (into numa) * &ival (<return> integer value; 0 on error) * Return: 0 if OK; 1 on error * * Notes: * (1) Caller may need to check the function return value to * decide if a 0 in the returned ival is valid. */ l_int32 numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival) { l_float32 val; PROCNAME("numaGetIValue"); if (!pival) return ERROR_INT("&ival not defined", procName, 1); *pival = 0; if (!na) return ERROR_INT("na not defined", procName, 1); if (index < 0 || index >= na->n) return ERROR_INT("index not valid", procName, 1); val = na->array[index]; *pival = (l_int32)(val + L_SIGN(val) * 0.5); return 0; } /*! * numaSetValue() * * Input: na * index (to element to be set) * val (to set element) * Return: 0 if OK; 1 on error */ l_int32 numaSetValue(NUMA *na, l_int32 index, l_float32 val) { PROCNAME("numaSetValue"); if (!na) return ERROR_INT("na not defined", procName, 1); if (index < 0 || index >= na->n) return ERROR_INT("index not valid", procName, 1); na->array[index] = val; return 0; } /*! * numaShiftValue() * * Input: na * index (to element to change relative to the current value) * diff (increment if diff > 0 or decrement if diff < 0) * Return: 0 if OK; 1 on error */ l_int32 numaShiftValue(NUMA *na, l_int32 index, l_float32 diff) { PROCNAME("numaShiftValue"); if (!na) return ERROR_INT("na not defined", procName, 1); if (index < 0 || index >= na->n) return ERROR_INT("index not valid", procName, 1); na->array[index] += diff; return 0; } /*! * numaGetIArray() * * Input: na * Return: a copy of the bare internal array, integerized * by rounding, or null on error * Notes: * (1) A copy of the array is always made, because we need to * generate an integer array from the bare float array. * The caller is responsible for freeing the array. * (2) The array size is determined by the number of stored numbers, * not by the size of the allocated array in the Numa. * (3) This function is provided to simplify calculations * using the bare internal array, rather than continually * calling accessors on the numa. It is typically used * on an array of size 256. */ l_int32 * numaGetIArray(NUMA *na) { l_int32 i, n, ival; l_int32 *array; PROCNAME("numaGetIArray"); if (!na) return (l_int32 *)ERROR_PTR("na not defined", procName, NULL); n = numaGetCount(na); if ((array = (l_int32 *)CALLOC(n, sizeof(l_int32))) == NULL) return (l_int32 *)ERROR_PTR("array not made", procName, NULL); for (i = 0; i < n; i++) { numaGetIValue(na, i, &ival); array[i] = ival; } return array; } /*! * numaGetFArray() * * Input: na * copyflag (L_NOCOPY or L_COPY) * Return: either the bare internal array or a copy of it, * or null on error * * Notes: * (1) If copyflag == L_COPY, it makes a copy which the caller * is responsible for freeing. Otherwise, it operates * directly on the bare array of the numa. * (2) Very important: for L_NOCOPY, any writes to the array * will be in the numa. Do not write beyond the size of * the count field, because it will not be accessable * from the numa! If necessary, be sure to set the count * the count field to a larger number (such as the alloc * size) BEFORE calling this function. */ l_float32 * numaGetFArray(NUMA *na, l_int32 copyflag) { l_int32 i, n; l_float32 *array; PROCNAME("numaGetFArray"); if (!na) return (l_float32 *)ERROR_PTR("na not defined", procName, NULL); if (copyflag == L_NOCOPY) array = na->array; else { /* copyflag == L_COPY */ n = numaGetCount(na); if ((array = (l_float32 *)CALLOC(n, sizeof(l_float32))) == NULL) return (l_float32 *)ERROR_PTR("array not made", procName, NULL); for (i = 0; i < n; i++) array[i] = na->array[i]; } return array; } /*! * numaGetRefCount() * * Input: na * Return: refcount, or UNDEF on error */ l_int32 numaGetRefcount(NUMA *na) { PROCNAME("numaGetRefcount"); if (!na) return ERROR_INT("na not defined", procName, UNDEF); return na->refcount; } /*! * numaChangeRefCount() * * Input: na * delta (change to be applied) * Return: 0 if OK, 1 on error */ l_int32 numaChangeRefcount(NUMA *na, l_int32 delta) { PROCNAME("numaChangeRefcount"); if (!na) return ERROR_INT("na not defined", procName, 1); na->refcount += delta; return 0; } /*! * numaGetXParameters() * * Input: na * &startx (<optional return> startx) * &delx (<optional return> delx) * Return: 0 if OK, 1 on error */ l_int32 numaGetXParameters(NUMA *na, l_float32 *pstartx, l_float32 *pdelx) { PROCNAME("numaGetXParameters"); if (!na) return ERROR_INT("na not defined", procName, 1); if (pstartx) *pstartx = na->startx; if (pdelx) *pdelx = na->delx; return 0; } /*! * numaSetXParameters() * * Input: na * startx (x value corresponding to na[0]) * delx (difference in x values for the situation where the * elements of na correspond to the evaulation of a * function at equal intervals of size @delx) * Return: 0 if OK, 1 on error */ l_int32 numaSetXParameters(NUMA *na, l_float32 startx, l_float32 delx) { PROCNAME("numaSetXParameters"); if (!na) return ERROR_INT("na not defined", procName, 1); na->startx = startx; na->delx = delx; return 0; } /*! * numaCopyXParameters() * * Input: nad (destination Numa) * nas (source Numa) * Return: 0 if OK, 1 on error */ l_int32 numaCopyXParameters(NUMA *nad, NUMA *nas) { l_float32 start, binsize; PROCNAME("numaCopyXParameters"); if (!nas || !nad) return ERROR_INT("nas and nad not both defined", procName, 1); numaGetXParameters(nas, &start, &binsize); numaSetXParameters(nad, start, binsize); return 0; } /*----------------------------------------------------------------------* * Serialize numa for I/O * *----------------------------------------------------------------------*/ /*! * numaRead() * * Input: filename * Return: na, or null on error */ NUMA * numaRead(const char *filename) { FILE *fp; NUMA *na; PROCNAME("numaRead"); if (!filename) return (NUMA *)ERROR_PTR("filename not defined", procName, NULL); if ((fp = fopenReadStream(filename)) == NULL) return (NUMA *)ERROR_PTR("stream not opened", procName, NULL); if ((na = numaReadStream(fp)) == NULL) { fclose(fp); return (NUMA *)ERROR_PTR("na not read", procName, NULL); } fclose(fp); return na; } /*! * numaReadStream() * * Input: stream * Return: numa, or null on error */ NUMA * numaReadStream(FILE *fp) { l_int32 i, n, index, ret, version; l_float32 val, startx, delx; NUMA *na; PROCNAME("numaReadStream"); if (!fp) return (NUMA *)ERROR_PTR("stream not defined", procName, NULL); ret = fscanf(fp, "\nNuma Version %d\n", &version); if (ret != 1) return (NUMA *)ERROR_PTR("not a numa file", procName, NULL); if (version != NUMA_VERSION_NUMBER) return (NUMA *)ERROR_PTR("invalid numa version", procName, NULL); fscanf(fp, "Number of numbers = %d\n", &n); if ((na = numaCreate(n)) == NULL) return (NUMA *)ERROR_PTR("na not made", procName, NULL); for (i = 0; i < n; i++) { if ((fscanf(fp, " [%d] = %f\n", &index, &val)) != 2) return (NUMA *)ERROR_PTR("bad input data", procName, NULL); numaAddNumber(na, val); } /* Optional data */ if ((fscanf(fp, "startx = %f, delx = %f\n", &startx, &delx)) == 2) numaSetXParameters(na, startx, delx); return na; } /*! * numaWrite() * * Input: filename, na * Return: 0 if OK, 1 on error */ l_int32 numaWrite(const char *filename, NUMA *na) { FILE *fp; PROCNAME("numaWrite"); if (!filename) return ERROR_INT("filename not defined", procName, 1); if (!na) return ERROR_INT("na not defined", procName, 1); if ((fp = fopen(filename, "w")) == NULL) return ERROR_INT("stream not opened", procName, 1); if (numaWriteStream(fp, na)) return ERROR_INT("na not written to stream", procName, 1); fclose(fp); return 0; } /*! * numaWriteStream() * * Input: stream, na * Return: 0 if OK, 1 on error */ l_int32 numaWriteStream(FILE *fp, NUMA *na) { l_int32 i, n; l_float32 startx, delx; PROCNAME("numaWriteStream"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!na) return ERROR_INT("na not defined", procName, 1); n = numaGetCount(na); fprintf(fp, "\nNuma Version %d\n", NUMA_VERSION_NUMBER); fprintf(fp, "Number of numbers = %d\n", n); for (i = 0; i < n; i++) fprintf(fp, " [%d] = %f\n", i, na->array[i]); fprintf(fp, "\n"); /* Optional data */ numaGetXParameters(na, &startx, &delx); if (startx != 0.0 || delx != 1.0) fprintf(fp, "startx = %f, delx = %f\n", startx, delx); return 0; } /*--------------------------------------------------------------------------* * Numaa creation, destruction * *--------------------------------------------------------------------------*/ /*! * numaaCreate() * * Input: size of numa ptr array to be alloc'd (0 for default) * Return: naa, or null on error * */ NUMAA * numaaCreate(l_int32 n) { NUMAA *naa; PROCNAME("numaaCreate"); if (n <= 0) n = INITIAL_PTR_ARRAYSIZE; if ((naa = (NUMAA *)CALLOC(1, sizeof(NUMAA))) == NULL) return (NUMAA *)ERROR_PTR("naa not made", procName, NULL); if ((naa->numa = (NUMA **)CALLOC(n, sizeof(NUMA *))) == NULL) return (NUMAA *)ERROR_PTR("numa ptr array not made", procName, NULL); naa->nalloc = n; naa->n = 0; return naa; } /*! * numaaDestroy() * * Input: &numaa <to be nulled if it exists> * Return: void */ void numaaDestroy(NUMAA **pnaa) { l_int32 i; NUMAA *naa; PROCNAME("numaaDestroy"); if (pnaa == NULL) { L_WARNING("ptr address is NULL!", procName); return; } if ((naa = *pnaa) == NULL) return; for (i = 0; i < naa->n; i++) numaDestroy(&naa->numa[i]); FREE(naa->numa); FREE(naa); *pnaa = NULL; return; } /*--------------------------------------------------------------------------* * Add Numa to Numaa * *--------------------------------------------------------------------------*/ /*! * numaaAddNuma() * * Input: naa * na (to be added) * copyflag (L_INSERT, L_COPY, L_CLONE) * Return: 0 if OK, 1 on error */ l_int32 numaaAddNuma(NUMAA *naa, NUMA *na, l_int32 copyflag) { l_int32 n; NUMA *nac; PROCNAME("numaaAddNuma"); if (!naa) return ERROR_INT("naa not defined", procName, 1); if (!na) return ERROR_INT("na not defined", procName, 1); if (copyflag == L_INSERT) nac = na; else if (copyflag == L_COPY) { if ((nac = numaCopy(na)) == NULL) return ERROR_INT("nac not made", procName, 1); } else if (copyflag == L_CLONE) nac = numaClone(na); else return ERROR_INT("invalid copyflag", procName, 1); n = numaaGetCount(naa); if (n >= naa->nalloc) numaaExtendArray(naa); naa->numa[n] = nac; naa->n++; return 0; } /*! * numaaExtendArray() * * Input: naa * Return: 0 if OK, 1 on error */ l_int32 numaaExtendArray(NUMAA *naa) { PROCNAME("numaaExtendArray"); if (!naa) return ERROR_INT("naa not defined", procName, 1); if ((naa->numa = (NUMA **)reallocNew((void **)&naa->numa, sizeof(NUMA *) * naa->nalloc, 2 * sizeof(NUMA *) * naa->nalloc)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); naa->nalloc *= 2; return 0; } /*----------------------------------------------------------------------* * Numaa accessors * *----------------------------------------------------------------------*/ /*! * numaaGetCount() * * Input: naa * Return: count (number of numa), or 0 if no numa or on error */ l_int32 numaaGetCount(NUMAA *naa) { PROCNAME("numaaGetCount"); if (!naa) return ERROR_INT("naa not defined", procName, 0); return naa->n; } /*! * numaaGetNumberCount() * * Input: naa * Return: count (number of numbers), or 0 if no numbers or on error */ l_int32 numaaGetNumberCount(NUMAA *naa) { NUMA *na; l_int32 n, sum, i; PROCNAME("numaaGetNumberCount"); if (!naa) return ERROR_INT("naa not defined", procName, 0); n = numaaGetCount(naa); for (sum = 0, i = 0; i < n; i++) { na = numaaGetNuma(naa, i, L_CLONE); sum += numaGetCount(na); numaDestroy(&na); } return sum; } /*! * numaaGetPtrArray() * * Input: naa * Return: the internal array of ptrs to Numa, or null on error * * Notes: * (1) This function is convenient for doing direct manipulation on * a fixed size array of Numas. To do this, it sets the count * to the full size of the allocated array of Numa ptrs. * The originating Numaa owns this array: DO NOT free it! * (2) Intended usage: * Numaa *naa = numaaCreate(n); * Numa **array = numaaGetPtrArray(naa); * ... [manipulate Numas directly on the array] * numaaDestroy(&naa); * (3) Cautions: * - Do not free this array; it is owned by tne Numaa. * - Do not call any functions on the Numaa, other than * numaaDestroy() when you're finished with the array. * Adding a Numa will force a resize, destroying the ptr array. * - Do not address the array outside its allocated size. * With the bare array, there are no protections. If the * allocated size is n, array[n] is an error. */ NUMA ** numaaGetPtrArray(NUMAA *naa) { PROCNAME("numaaGetPtrArray"); if (!naa) return (NUMA **)ERROR_PTR("naa not defined", procName, NULL); naa->n = naa->nalloc; return naa->numa; } /*! * numaaGetNuma() * * Input: naa * index (to the index-th numa) * accessflag (L_COPY or L_CLONE) * Return: numa, or null on error */ NUMA * numaaGetNuma(NUMAA *naa, l_int32 index, l_int32 accessflag) { PROCNAME("numaaGetNuma"); if (!naa) return (NUMA *)ERROR_PTR("naa not defined", procName, NULL); if (index < 0 || index >= naa->n) return (NUMA *)ERROR_PTR("index not valid", procName, NULL); if (accessflag == L_COPY) return numaCopy(naa->numa[index]); else if (accessflag == L_CLONE) return numaClone(naa->numa[index]); else return (NUMA *)ERROR_PTR("invalid accessflag", procName, NULL); } /*! * numaaReplaceNuma() * * Input: naa * index (to the index-th numa) * numa (insert and replace any existing one) * Return: 0 if OK, 1 on error * * Notes: * (1) Any existing numa is destroyed, and the input one * is inserted in its place. * (2) If the index is invalid, return 1 (error) */ l_int32 numaaReplaceNuma(NUMAA *naa, l_int32 index, NUMA *na) { l_int32 n; PROCNAME("numaaReplaceNuma"); if (!naa) return ERROR_INT("naa not defined", procName, 1); if (!na) return ERROR_INT("na not defined", procName, 1); n = numaaGetCount(naa); if (index < 0 || index >= n) return ERROR_INT("index not valid", procName, 1); numaDestroy(&naa->numa[index]); naa->numa[index] = na; return 0; } /*! * numaaAddNumber() * * Input: naa * index (of numa within numaa) * val (float or int to be added; stored as a float) * Return: 0 if OK, 1 on error * * Notes: * (1) Adds to an existing numa only. */ l_int32 numaaAddNumber(NUMAA *naa, l_int32 index, l_float32 val) { l_int32 n; NUMA *na; PROCNAME("numaaAddNumber"); if (!naa) return ERROR_INT("naa not defined", procName, 1); n = numaaGetCount(naa); if (index < 0 || index >= n) return ERROR_INT("invalid index in naa", procName, 1); na = numaaGetNuma(naa, index, L_CLONE); numaAddNumber(na, val); numaDestroy(&na); return 0; } /*----------------------------------------------------------------------* * Serialize numaa for I/O * *----------------------------------------------------------------------*/ /*! * numaaRead() * * Input: filename * Return: naa, or null on error */ NUMAA * numaaRead(const char *filename) { FILE *fp; NUMAA *naa; PROCNAME("numaaRead"); if (!filename) return (NUMAA *)ERROR_PTR("filename not defined", procName, NULL); if ((fp = fopenReadStream(filename)) == NULL) return (NUMAA *)ERROR_PTR("stream not opened", procName, NULL); if ((naa = numaaReadStream(fp)) == NULL) { fclose(fp); return (NUMAA *)ERROR_PTR("naa not read", procName, NULL); } fclose(fp); return naa; } /*! * numaaReadStream() * * Input: stream * Return: naa, or null on error */ NUMAA * numaaReadStream(FILE *fp) { l_int32 i, n, index, ret, version; NUMA *na; NUMAA *naa; PROCNAME("numaaReadStream"); if (!fp) return (NUMAA *)ERROR_PTR("stream not defined", procName, NULL); ret = fscanf(fp, "\nNumaa Version %d\n", &version); if (ret != 1) return (NUMAA *)ERROR_PTR("not a numa file", procName, NULL); if (version != NUMA_VERSION_NUMBER) return (NUMAA *)ERROR_PTR("invalid numaa version", procName, NULL); fscanf(fp, "Number of numa = %d\n\n", &n); if ((naa = numaaCreate(n)) == NULL) return (NUMAA *)ERROR_PTR("naa not made", procName, NULL); for (i = 0; i < n; i++) { fscanf(fp, "Numa[%d]:", &index); if ((na = numaReadStream(fp)) == NULL) return (NUMAA *)ERROR_PTR("na not made", procName, NULL); numaaAddNuma(naa, na, L_INSERT); } return naa; } /*! * numaaWrite() * * Input: filename, naa * Return: 0 if OK, 1 on error */ l_int32 numaaWrite(const char *filename, NUMAA *naa) { FILE *fp; PROCNAME("numaaWrite"); if (!filename) return ERROR_INT("filename not defined", procName, 1); if (!naa) return ERROR_INT("naa not defined", procName, 1); if ((fp = fopen(filename, "w")) == NULL) return ERROR_INT("stream not opened", procName, 1); if (numaaWriteStream(fp, naa)) return ERROR_INT("naa not written to stream", procName, 1); fclose(fp); return 0; } /*! * numaaWriteStream() * * Input: stream, naa * Return: 0 if OK, 1 on error */ l_int32 numaaWriteStream(FILE *fp, NUMAA *naa) { l_int32 i, n; NUMA *na; PROCNAME("numaaWriteStream"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!naa) return ERROR_INT("naa not defined", procName, 1); n = numaaGetCount(naa); fprintf(fp, "\nNumaa Version %d\n", NUMA_VERSION_NUMBER); fprintf(fp, "Number of numa = %d\n\n", n); for (i = 0; i < n; i++) { if ((na = numaaGetNuma(naa, i, L_CLONE)) == NULL) return ERROR_INT("na not found", procName, 1); fprintf(fp, "Numa[%d]:", i); numaWriteStream(fp, na); numaDestroy(&na); } return 0; } /*--------------------------------------------------------------------------* * Numa2d creation, destruction * *--------------------------------------------------------------------------*/ /*! * numa2dCreate() * * Input: nrows (of 2d array) * ncols (of 2d array) * initsize (initial size of each allocated numa) * Return: numa2d, or null on error * * Notes: * (1) The numa2d holds a doubly-indexed array of numa. * (2) The numa ptr array is initialized with all ptrs set to NULL. * (3) The numas are created only when a number is to be stored * at an index (i,j) for which a numa has not yet been made. */ NUMA2D * numa2dCreate(l_int32 nrows, l_int32 ncols, l_int32 initsize) { l_int32 i; NUMA2D *na2d; PROCNAME("numa2dCreate"); if (nrows <= 1 || ncols <= 1) return (NUMA2D *)ERROR_PTR("rows, cols not both >= 1", procName, NULL); if ((na2d = (NUMA2D *)CALLOC(1, sizeof(NUMA2D))) == NULL) return (NUMA2D *)ERROR_PTR("na2d not made", procName, NULL); na2d->nrows = nrows; na2d->ncols = ncols; na2d->initsize = initsize; /* Set up the 2D array */ if ((na2d->numa = (NUMA ***)CALLOC(nrows, sizeof(NUMA **))) == NULL) return (NUMA2D *)ERROR_PTR("numa row array not made", procName, NULL); for (i = 0; i < nrows; i++) { if ((na2d->numa[i] = (NUMA **)CALLOC(ncols, sizeof(NUMA *))) == NULL) return (NUMA2D *)ERROR_PTR("numa cols not made", procName, NULL); } return na2d; } /*! * numa2dDestroy() * * Input: &numa2d (<to be nulled if it exists>) * Return: void */ void numa2dDestroy(NUMA2D **pna2d) { l_int32 i, j; NUMA2D *na2d; PROCNAME("numa2dDestroy"); if (pna2d == NULL) { L_WARNING("ptr address is NULL!", procName); return; } if ((na2d = *pna2d) == NULL) return; for (i = 0; i < na2d->nrows; i++) { for (j = 0; j < na2d->ncols; j++) numaDestroy(&na2d->numa[i][j]); FREE(na2d->numa[i]); } FREE(na2d->numa); FREE(na2d); *pna2d = NULL; return; } /*--------------------------------------------------------------------------* * Numa2d accessors * *--------------------------------------------------------------------------*/ /*! * numa2dAddNumber() * * Input: na2d * row of 2d array * col of 2d array * val (float or int to be added; stored as a float) * Return: 0 if OK, 1 on error */ l_int32 numa2dAddNumber(NUMA2D *na2d, l_int32 row, l_int32 col, l_float32 val) { NUMA *na; PROCNAME("numa2dAddNumber"); if (!na2d) return ERROR_INT("na2d not defined", procName, 1); if (row < 0 || row >= na2d->nrows) return ERROR_INT("row out of bounds", procName, 1); if (col < 0 || col >= na2d->ncols) return ERROR_INT("col out of bounds", procName, 1); if ((na = na2d->numa[row][col]) == NULL) { na = numaCreate(na2d->initsize); na2d->numa[row][col] = na; } numaAddNumber(na, val); return 0; } /*! * numa2dGetCount() * * Input: na2d * row of 2d array * col of 2d array * Return: size of numa at [row][col], or 0 if the numa doesn't exist * or on error */ l_int32 numa2dGetCount(NUMA2D *na2d, l_int32 row, l_int32 col) { NUMA *na; PROCNAME("numa2dGetCount"); if (!na2d) return ERROR_INT("na2d not defined", procName, 0); if (row < 0 || row >= na2d->nrows) return ERROR_INT("row out of bounds", procName, 0); if (col < 0 || col >= na2d->ncols) return ERROR_INT("col out of bounds", procName, 0); if ((na = na2d->numa[row][col]) == NULL) return 0; else return na->n; } /*! * numa2dGetNuma() * * Input: na2d * row of 2d array * col of 2d array * Return: na (a clone of the numa if it exists) or null if it doesn't * * Notes: * (1) This does not give an error if the index is out of bounds. */ NUMA * numa2dGetNuma(NUMA2D *na2d, l_int32 row, l_int32 col) { NUMA *na; PROCNAME("numa2dGetNuma"); if (!na2d) return (NUMA *)ERROR_PTR("na2d not defined", procName, NULL); if (row < 0 || row >= na2d->nrows || col < 0 || col >= na2d->ncols) return NULL; if ((na = na2d->numa[row][col]) == NULL) return NULL; return numaClone(na); } /*! * numa2dGetFValue() * * Input: na2d * row of 2d array * col of 2d array * index (into numa) * &val (<return> float value) * Return: 0 if OK, 1 on error */ l_int32 numa2dGetFValue(NUMA2D *na2d, l_int32 row, l_int32 col, l_int32 index, l_float32 *pval) { NUMA *na; PROCNAME("numa2dGetFValue"); if (!na2d) return ERROR_INT("na2d not defined", procName, 1); if (!pval) return ERROR_INT("&val not defined", procName, 1); *pval = 0.0; if (row < 0 || row >= na2d->nrows) return ERROR_INT("row out of bounds", procName, 1); if (col < 0 || col >= na2d->ncols) return ERROR_INT("col out of bounds", procName, 1); if ((na = na2d->numa[row][col]) == NULL) return ERROR_INT("numa does not exist", procName, 1); return numaGetFValue(na, index, pval); } /*! * numa2dGetIValue() * * Input: na2d * row of 2d array * col of 2d array * index (into numa) * &val (<return> integer value) * Return: 0 if OK, 1 on error */ l_int32 numa2dGetIValue(NUMA2D *na2d, l_int32 row, l_int32 col, l_int32 index, l_int32 *pval) { NUMA *na; PROCNAME("numa2dGetIValue"); if (!na2d) return ERROR_INT("na2d not defined", procName, 1); if (!pval) return ERROR_INT("&val not defined", procName, 1); *pval = 0; if (row < 0 || row >= na2d->nrows) return ERROR_INT("row out of bounds", procName, 1); if (col < 0 || col >= na2d->ncols) return ERROR_INT("col out of bounds", procName, 1); if ((na = na2d->numa[row][col]) == NULL) return ERROR_INT("numa does not exist", procName, 1); return numaGetIValue(na, index, pval); } /*--------------------------------------------------------------------------* * Number array hash: Creation and destruction * *--------------------------------------------------------------------------*/ /*! * numaHashCreate() * * Input: nbuckets (the number of buckets in the hash table, * which should be prime.) * initsize (initial size of each allocated numa; 0 for default) * Return: ptr to new nahash, or null on error * * Note: actual numa are created only as required by numaHashAdd() */ NUMAHASH * numaHashCreate(l_int32 nbuckets, l_int32 initsize) { NUMAHASH *nahash; PROCNAME("numaHashCreate"); if (nbuckets <= 0) return (NUMAHASH *)ERROR_PTR("negative hash size", procName, NULL); if ((nahash = (NUMAHASH *)CALLOC(1, sizeof(NUMAHASH))) == NULL) return (NUMAHASH *)ERROR_PTR("nahash not made", procName, NULL); if ((nahash->numa = (NUMA **)CALLOC(nbuckets, sizeof(NUMA *))) == NULL) { FREE(nahash); return (NUMAHASH *)ERROR_PTR("numa ptr array not made", procName, NULL); } nahash->nbuckets = nbuckets; nahash->initsize = initsize; return nahash; } /*! * numaHashDestroy() * * Input: &nahash (<to be nulled, if it exists>) * Return: void */ void numaHashDestroy(NUMAHASH **pnahash) { NUMAHASH *nahash; l_int32 i; PROCNAME("numaHashDestroy"); if (pnahash == NULL) { L_WARNING("ptr address is NULL!", procName); return; } if ((nahash = *pnahash) == NULL) return; for (i = 0; i < nahash->nbuckets; i++) numaDestroy(&nahash->numa[i]); FREE(nahash->numa); FREE(nahash); *pnahash = NULL; } /*--------------------------------------------------------------------------* * Number array hash: Add elements and return numas *--------------------------------------------------------------------------*/ /*! * numaHashGetNuma() * * Input: nahash * key (key to be hashed into a bucket number) * Return: ptr to numa */ NUMA * numaHashGetNuma(NUMAHASH *nahash, l_uint32 key) { l_int32 bucket; NUMA *na; PROCNAME("numaHashGetNuma"); if (!nahash) return (NUMA *)ERROR_PTR("nahash not defined", procName, NULL); bucket = key % nahash->nbuckets; na = nahash->numa[bucket]; if (na) return numaClone(na); else return NULL; } /*! * numaHashAdd() * * Input: nahash * key (key to be hashed into a bucket number) * value (float value to be appended to the specific numa) * Return: 0 if OK; 1 on error */ l_int32 numaHashAdd(NUMAHASH *nahash, l_uint32 key, l_float32 value) { l_int32 bucket; NUMA *na; PROCNAME("numaHashAdd"); if (!nahash) return ERROR_INT("nahash not defined", procName, 1); if (key < 0) return ERROR_INT("key < 0", procName, 1); bucket = key % nahash->nbuckets; na = nahash->numa[bucket]; if (!na) { if ((na = numaCreate(nahash->initsize)) == NULL) return ERROR_INT("na not made", procName, 1); nahash->numa[bucket] = na; } numaAddNumber(na, value); return 0; }