/*====================================================================* - 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 dnabasic.c *
 *
 *      Dna creation, destruction, copy, clone, etc.
 *          L_DNA       *l_dnaCreate()
 *          L_DNA       *l_dnaCreateFromIArray()
 *          L_DNA       *l_dnaCreateFromDArray()
 *          L_DNA       *l_dnaMakeSequence()
 *          void        *l_dnaDestroy()
 *          L_DNA       *l_dnaCopy()
 *          L_DNA       *l_dnaClone()
 *          l_int32      l_dnaEmpty()
 *
 *      Dna: add/remove number and extend array
 *          l_int32      l_dnaAddNumber()
 *          static l_int32  l_dnaExtendArray()
 *          l_int32      l_dnaInsertNumber()
 *          l_int32      l_dnaRemoveNumber()
 *          l_int32      l_dnaReplaceNumber()
 *
 *      Dna accessors
 *          l_int32      l_dnaGetCount()
 *          l_int32      l_dnaSetCount()
 *          l_int32      l_dnaGetIValue()
 *          l_int32      l_dnaGetDValue()
 *          l_int32      l_dnaSetValue()
 *          l_int32      l_dnaShiftValue()
 *          l_int32     *l_dnaGetIArray()
 *          l_float64   *l_dnaGetDArray()
 *          l_int32      l_dnaGetRefcount()
 *          l_int32      l_dnaChangeRefcount()
 *          l_int32      l_dnaGetParameters()
 *          l_int32      l_dnaSetParameters()
 *          l_int32      l_dnaCopyParameters()
 *
 *      Serialize Dna for I/O
 *          L_DNA       *l_dnaRead()
 *          L_DNA       *l_dnaReadStream()
 *          l_int32      l_dnaWrite()
 *          l_int32      l_dnaWriteStream()
 *
 *      Dnaa creation, destruction
 *          L_DNAA      *l_dnaaCreate()
 *          L_DNAA      *l_dnaaCreateFull()
 *          l_int32      l_dnaaTruncate()
 *          void        *l_dnaaDestroy()
 *
 *      Add Dna to Dnaa
 *          l_int32      l_dnaaAddDna()
 *          static l_int32  l_dnaaExtendArray()
 *
 *      Dnaa accessors
 *          l_int32      l_dnaaGetCount()
 *          l_int32      l_dnaaGetDnaCount()
 *          l_int32      l_dnaaGetNumberCount()
 *          L_DNA       *l_dnaaGetDna()
 *          L_DNA       *l_dnaaReplaceDna()
 *          l_int32      l_dnaaGetValue()
 *          l_int32      l_dnaaAddNumber()
 *
 *      Serialize Dnaa for I/O
 *          L_DNAA      *l_dnaaRead()
 *          L_DNAA      *l_dnaaReadStream()
 *          l_int32      l_dnaaWrite()
 *          l_int32      l_dnaaWriteStream()
 *
 *    (1) The Dna is a struct holding an array of doubles.  It can also
 *        be used to store l_int32 values, up to the full precision
 *        of int32.  Always use it whenever integers larger than a
 *        few million need to be stored.
 *
 *    (2) Always use the accessors in this file, never the fields directly.
 *
 *    (3) Storing and retrieving numbers:
 *
 *       * to append a new number to the array, use l_dnaAddNumber().  If
 *         the number is an int, it will will automatically be converted
 *         to l_float64 and stored.
 *
 *       * to reset a value stored in the array, use l_dnaSetValue().
 *
 *       * to increment or decrement a value stored in the array,
 *         use l_dnaShiftValue().
 *
 *       * to obtain a value from the array, use either l_dnaGetIValue()
 *         or l_dnaGetDValue(), depending on whether you are retrieving
 *         an integer or a float64.  This avoids doing an explicit cast,
 *         such as
 *           (a) return a l_float64 and cast it to an l_int32
 *           (b) cast the return directly to (l_float64 *) to
 *               satisfy the function prototype, as in
 *                 l_dnaGetDValue(da, index, (l_float64 *)&ival);   [ugly!]
 *
 *    (4) int <--> double conversions:
 *
 *        Conversions go automatically from l_int32 --> l_float64,
 *        without loss of precision.  You must cast (l_int32)
 *        to go from l_float64 --> l_int32 because you're truncating
 *        to the integer value.
 *
 *    (5) As with other arrays in leptonica, the l_dna 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 l_dnaSetCount().  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 l_dna 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 l_dna, and the two
 *        l_dnas are typically denoted dnay and dnax.
 * 
*/ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include #include "allheaders.h" /* Bounds on initial array size */ static const l_uint32 MaxDoubleArraySize = 100000000; /* for dna */ static const l_uint32 MaxPtrArraySize = 1000000; /* for dnaa */ static const l_int32 InitialArraySize = 50; /*!< n'importe quoi */ /* Static functions */ static l_int32 l_dnaExtendArray(L_DNA *da); static l_int32 l_dnaaExtendArray(L_DNAA *daa); /*--------------------------------------------------------------------------* * Dna creation, destruction, copy, clone, etc. * *--------------------------------------------------------------------------*/ /*! * \brief l_dnaCreate() * * \param[in] n size of number array to be alloc'd; 0 for default * \return da, or NULL on error */ L_DNA * l_dnaCreate(l_int32 n) { L_DNA *da; PROCNAME("l_dnaCreate"); if (n <= 0 || n > MaxDoubleArraySize) n = InitialArraySize; da = (L_DNA *)LEPT_CALLOC(1, sizeof(L_DNA)); if ((da->array = (l_float64 *)LEPT_CALLOC(n, sizeof(l_float64))) == NULL) { l_dnaDestroy(&da); return (L_DNA *)ERROR_PTR("double array not made", procName, NULL); } da->nalloc = n; da->n = 0; da->refcount = 1; da->startx = 0.0; da->delx = 1.0; return da; } /*! * \brief l_dnaCreateFromIArray() * * \param[in] iarray integer array * \param[in] size of the array * \return da, or NULL on error * *
 * Notes:
 *      (1) We can't insert this int array into the l_dna, because a l_dna
 *          takes a double array.  So this just copies the data from the
 *          input array into the l_dna.  The input array continues to be
 *          owned by the caller.
 * 
*/ L_DNA * l_dnaCreateFromIArray(l_int32 *iarray, l_int32 size) { l_int32 i; L_DNA *da; PROCNAME("l_dnaCreateFromIArray"); if (!iarray) return (L_DNA *)ERROR_PTR("iarray not defined", procName, NULL); if (size <= 0) return (L_DNA *)ERROR_PTR("size must be > 0", procName, NULL); da = l_dnaCreate(size); for (i = 0; i < size; i++) l_dnaAddNumber(da, iarray[i]); return da; } /*! * \brief l_dnaCreateFromDArray() * * \param[in] darray float * \param[in] size of the array * \param[in] copyflag L_INSERT or L_COPY * \return da, or NULL on error * *
 * Notes:
 *      (1) With L_INSERT, ownership of the input array is transferred
 *          to the returned l_dna, and all %size elements are considered
 *          to be valid.
 * 
*/ L_DNA * l_dnaCreateFromDArray(l_float64 *darray, l_int32 size, l_int32 copyflag) { l_int32 i; L_DNA *da; PROCNAME("l_dnaCreateFromDArray"); if (!darray) return (L_DNA *)ERROR_PTR("darray not defined", procName, NULL); if (size <= 0) return (L_DNA *)ERROR_PTR("size must be > 0", procName, NULL); if (copyflag != L_INSERT && copyflag != L_COPY) return (L_DNA *)ERROR_PTR("invalid copyflag", procName, NULL); da = l_dnaCreate(size); if (copyflag == L_INSERT) { if (da->array) LEPT_FREE(da->array); da->array = darray; da->n = size; } else { /* just copy the contents */ for (i = 0; i < size; i++) l_dnaAddNumber(da, darray[i]); } return da; } /*! * \brief l_dnaMakeSequence() * * \param[in] startval * \param[in] increment * \param[in] size of sequence * \return l_dna of sequence of evenly spaced values, or NULL on error */ L_DNA * l_dnaMakeSequence(l_float64 startval, l_float64 increment, l_int32 size) { l_int32 i; l_float64 val; L_DNA *da; PROCNAME("l_dnaMakeSequence"); if ((da = l_dnaCreate(size)) == NULL) return (L_DNA *)ERROR_PTR("da not made", procName, NULL); for (i = 0; i < size; i++) { val = startval + i * increment; l_dnaAddNumber(da, val); } return da; } /*! * \brief l_dnaDestroy() * * \param[in,out] pda will be set to null before returning * \return void * *
 * Notes:
 *      (1) Decrements the ref count and, if 0, destroys the l_dna.
 *      (2) Always nulls the input ptr.
 * 
*/ void l_dnaDestroy(L_DNA **pda) { L_DNA *da; PROCNAME("l_dnaDestroy"); if (pda == NULL) { L_WARNING("ptr address is NULL\n", procName); return; } if ((da = *pda) == NULL) return; /* Decrement the ref count. If it is 0, destroy the l_dna. */ l_dnaChangeRefcount(da, -1); if (l_dnaGetRefcount(da) <= 0) { if (da->array) LEPT_FREE(da->array); LEPT_FREE(da); } *pda = NULL; } /*! * \brief l_dnaCopy() * * \param[in] da * \return copy of da, or NULL on error * *
 * Notes:
 *      (1) This removes unused ptrs above da->n.
 * 
*/ L_DNA * l_dnaCopy(L_DNA *da) { l_int32 i; L_DNA *dac; PROCNAME("l_dnaCopy"); if (!da) return (L_DNA *)ERROR_PTR("da not defined", procName, NULL); if ((dac = l_dnaCreate(da->n)) == NULL) return (L_DNA *)ERROR_PTR("dac not made", procName, NULL); dac->startx = da->startx; dac->delx = da->delx; for (i = 0; i < da->n; i++) l_dnaAddNumber(dac, da->array[i]); return dac; } /*! * \brief l_dnaClone() * * \param[in] da * \return ptr to same da, or NULL on error */ L_DNA * l_dnaClone(L_DNA *da) { PROCNAME("l_dnaClone"); if (!da) return (L_DNA *)ERROR_PTR("da not defined", procName, NULL); l_dnaChangeRefcount(da, 1); return da; } /*! * \brief l_dnaEmpty() * * \param[in] da * \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_ok l_dnaEmpty(L_DNA *da) { PROCNAME("l_dnaEmpty"); if (!da) return ERROR_INT("da not defined", procName, 1); da->n = 0; return 0; } /*--------------------------------------------------------------------------* * Dna: add/remove number and extend array * *--------------------------------------------------------------------------*/ /*! * \brief l_dnaAddNumber() * * \param[in] da * \param[in] val float or int to be added; stored as a float * \return 0 if OK, 1 on error */ l_ok l_dnaAddNumber(L_DNA *da, l_float64 val) { l_int32 n; PROCNAME("l_dnaAddNumber"); if (!da) return ERROR_INT("da not defined", procName, 1); n = l_dnaGetCount(da); if (n >= da->nalloc) { if (l_dnaExtendArray(da)) return ERROR_INT("extension failed", procName, 1); } da->array[n] = val; da->n++; return 0; } /*! * \brief l_dnaExtendArray() * * \param[in] da * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) The max number of doubles is 100M.
 * 
*/ static l_int32 l_dnaExtendArray(L_DNA *da) { size_t oldsize, newsize; PROCNAME("l_dnaExtendArray"); if (!da) return ERROR_INT("da not defined", procName, 1); if (da->nalloc > MaxDoubleArraySize) /* belt & suspenders */ return ERROR_INT("da has too many ptrs", procName, 1); oldsize = da->nalloc * sizeof(l_float64); newsize = 2 * oldsize; if (newsize > 8 * MaxDoubleArraySize) return ERROR_INT("newsize > 800 MB; too large", procName, 1); if ((da->array = (l_float64 *)reallocNew((void **)&da->array, oldsize, newsize)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); da->nalloc *= 2; return 0; } /*! * \brief l_dnaInsertNumber() * * \param[in] da * \param[in] index location in da to insert new value * \param[in] val float64 or integer to be added * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This shifts da[i] --> da[i + 1] for all i >= %index,
 *          and then inserts %val as da[%index].
 *      (2) It should not be used repeatedly on large arrays,
 *          because the function is O(n).
 *
 * 
*/ l_ok l_dnaInsertNumber(L_DNA *da, l_int32 index, l_float64 val) { l_int32 i, n; PROCNAME("l_dnaInsertNumber"); if (!da) return ERROR_INT("da not defined", procName, 1); n = l_dnaGetCount(da); if (index < 0 || index > n) { L_ERROR("index %d not in [0,...,%d]\n", procName, index, n); return 1; } if (n >= da->nalloc) { if (l_dnaExtendArray(da)) return ERROR_INT("extension failed", procName, 1); } for (i = n; i > index; i--) da->array[i] = da->array[i - 1]; da->array[index] = val; da->n++; return 0; } /*! * \brief l_dnaRemoveNumber() * * \param[in] da * \param[in] index element to be removed * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This shifts da[i] --> da[i - 1] for all i > %index.
 *      (2) It should not be used repeatedly on large arrays,
 *          because the function is O(n).
 * 
*/ l_ok l_dnaRemoveNumber(L_DNA *da, l_int32 index) { l_int32 i, n; PROCNAME("l_dnaRemoveNumber"); if (!da) return ERROR_INT("da not defined", procName, 1); n = l_dnaGetCount(da); if (index < 0 || index >= n) { L_ERROR("index %d not in [0,...,%d]\n", procName, index, n - 1); return 1; } for (i = index + 1; i < n; i++) da->array[i - 1] = da->array[i]; da->n--; return 0; } /*! * \brief l_dnaReplaceNumber() * * \param[in] da * \param[in] index element to be replaced * \param[in] val new value to replace old one * \return 0 if OK, 1 on error */ l_ok l_dnaReplaceNumber(L_DNA *da, l_int32 index, l_float64 val) { l_int32 n; PROCNAME("l_dnaReplaceNumber"); if (!da) return ERROR_INT("da not defined", procName, 1); n = l_dnaGetCount(da); if (index < 0 || index >= n) { L_ERROR("index %d not in [0,...,%d]\n", procName, index, n - 1); return 1; } da->array[index] = val; return 0; } /*----------------------------------------------------------------------* * Dna accessors * *----------------------------------------------------------------------*/ /*! * \brief l_dnaGetCount() * * \param[in] da * \return count, or 0 if no numbers or on error */ l_int32 l_dnaGetCount(L_DNA *da) { PROCNAME("l_dnaGetCount"); if (!da) return ERROR_INT("da not defined", procName, 0); return da->n; } /*! * \brief l_dnaSetCount() * * \param[in] da * \param[in] newcount * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) If %newcount <= da->nalloc, this resets da->n.
 *          Using %newcount = 0 is equivalent to l_dnaEmpty().
 *      (2) If %newcount > da->nalloc, this causes a realloc
 *          to a size da->nalloc = %newcount.
 *      (3) All the previously unused values in da are set to 0.0.
 * 
*/ l_ok l_dnaSetCount(L_DNA *da, l_int32 newcount) { PROCNAME("l_dnaSetCount"); if (!da) return ERROR_INT("da not defined", procName, 1); if (newcount > da->nalloc) { if ((da->array = (l_float64 *)reallocNew((void **)&da->array, sizeof(l_float64) * da->nalloc, sizeof(l_float64) * newcount)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); da->nalloc = newcount; } da->n = newcount; return 0; } /*! * \brief l_dnaGetDValue() * * \param[in] da * \param[in] index into l_dna * \param[out] pval double 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_ok l_dnaGetDValue(L_DNA *da, l_int32 index, l_float64 *pval) { PROCNAME("l_dnaGetDValue"); if (!pval) return ERROR_INT("&val not defined", procName, 1); *pval = 0.0; if (!da) return ERROR_INT("da not defined", procName, 1); if (index < 0 || index >= da->n) return ERROR_INT("index not valid", procName, 1); *pval = da->array[index]; return 0; } /*! * \brief l_dnaGetIValue() * * \param[in] da * \param[in] index into l_dna * \param[out] pival 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_ok l_dnaGetIValue(L_DNA *da, l_int32 index, l_int32 *pival) { l_float64 val; PROCNAME("l_dnaGetIValue"); if (!pival) return ERROR_INT("&ival not defined", procName, 1); *pival = 0; if (!da) return ERROR_INT("da not defined", procName, 1); if (index < 0 || index >= da->n) return ERROR_INT("index not valid", procName, 1); val = da->array[index]; *pival = (l_int32)(val + L_SIGN(val) * 0.5); return 0; } /*! * \brief l_dnaSetValue() * * \param[in] da * \param[in] index to element to be set * \param[in] val to set element * \return 0 if OK; 1 on error */ l_ok l_dnaSetValue(L_DNA *da, l_int32 index, l_float64 val) { PROCNAME("l_dnaSetValue"); if (!da) return ERROR_INT("da not defined", procName, 1); if (index < 0 || index >= da->n) return ERROR_INT("index not valid", procName, 1); da->array[index] = val; return 0; } /*! * \brief l_dnaShiftValue() * * \param[in] da * \param[in] index to element to change relative to the current value * \param[in] diff increment if diff > 0 or decrement if diff < 0 * \return 0 if OK; 1 on error */ l_ok l_dnaShiftValue(L_DNA *da, l_int32 index, l_float64 diff) { PROCNAME("l_dnaShiftValue"); if (!da) return ERROR_INT("da not defined", procName, 1); if (index < 0 || index >= da->n) return ERROR_INT("index not valid", procName, 1); da->array[index] += diff; return 0; } /*! * \brief l_dnaGetIArray() * * \param[in] da * \return a copy of the bare internal array, integerized * by rounding, or NULL on error *
 * Notes:
 *      (1) A copy of the array is made, because we need to
 *          generate an integer array from the bare double 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 l_dna.
 *      (3) This function is provided to simplify calculations
 *          using the bare internal array, rather than continually
 *          calling accessors on the l_dna.  It is typically used
 *          on an array of size 256.
 * 
*/ l_int32 * l_dnaGetIArray(L_DNA *da) { l_int32 i, n, ival; l_int32 *array; PROCNAME("l_dnaGetIArray"); if (!da) return (l_int32 *)ERROR_PTR("da not defined", procName, NULL); n = l_dnaGetCount(da); if ((array = (l_int32 *)LEPT_CALLOC(n, sizeof(l_int32))) == NULL) return (l_int32 *)ERROR_PTR("array not made", procName, NULL); for (i = 0; i < n; i++) { l_dnaGetIValue(da, i, &ival); array[i] = ival; } return array; } /*! * \brief l_dnaGetDArray() * * \param[in] da * \param[in] 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 l_dna.
 *      (2) Very important: for L_NOCOPY, any writes to the array
 *          will be in the l_dna.  Do not write beyond the size of
 *          the count field, because it will not be accessible
 *          from the l_dna!  If necessary, be sure to set the count
 *          field to a larger number (such as the alloc size)
 *          BEFORE calling this function.  Creating with l_dnaMakeConstant()
 *          is another way to insure full initialization.
 * 
*/ l_float64 * l_dnaGetDArray(L_DNA *da, l_int32 copyflag) { l_int32 i, n; l_float64 *array; PROCNAME("l_dnaGetDArray"); if (!da) return (l_float64 *)ERROR_PTR("da not defined", procName, NULL); if (copyflag == L_NOCOPY) { array = da->array; } else { /* copyflag == L_COPY */ n = l_dnaGetCount(da); if ((array = (l_float64 *)LEPT_CALLOC(n, sizeof(l_float64))) == NULL) return (l_float64 *)ERROR_PTR("array not made", procName, NULL); for (i = 0; i < n; i++) array[i] = da->array[i]; } return array; } /*! * \brief l_dnaGetRefCount() * * \param[in] da * \return refcount, or UNDEF on error */ l_int32 l_dnaGetRefcount(L_DNA *da) { PROCNAME("l_dnaGetRefcount"); if (!da) return ERROR_INT("da not defined", procName, UNDEF); return da->refcount; } /*! * \brief l_dnaChangeRefCount() * * \param[in] da * \param[in] delta change to be applied * \return 0 if OK, 1 on error */ l_ok l_dnaChangeRefcount(L_DNA *da, l_int32 delta) { PROCNAME("l_dnaChangeRefcount"); if (!da) return ERROR_INT("da not defined", procName, 1); da->refcount += delta; return 0; } /*! * \brief l_dnaGetParameters() * * \param[in] da * \param[out] pstartx [optional] startx * \param[out] pdelx [optional] delx * \return 0 if OK, 1 on error */ l_ok l_dnaGetParameters(L_DNA *da, l_float64 *pstartx, l_float64 *pdelx) { PROCNAME("l_dnaGetParameters"); if (pstartx) *pstartx = 0.0; if (pdelx) *pdelx = 1.0; if (!pstartx && !pdelx) return ERROR_INT("neither &startx nor &delx are defined", procName, 1); if (!da) return ERROR_INT("da not defined", procName, 1); if (pstartx) *pstartx = da->startx; if (pdelx) *pdelx = da->delx; return 0; } /*! * \brief l_dnaSetParameters() * * \param[in] da * \param[in] startx x value corresponding to da[0] * \param[in] delx difference in x values for the situation where the * elements of da correspond to the evaulation of a * function at equal intervals of size %delx * \return 0 if OK, 1 on error */ l_ok l_dnaSetParameters(L_DNA *da, l_float64 startx, l_float64 delx) { PROCNAME("l_dnaSetParameters"); if (!da) return ERROR_INT("da not defined", procName, 1); da->startx = startx; da->delx = delx; return 0; } /*! * \brief l_dnaCopyParameters() * * \param[in] dad destination DNuma * \param[in] das source DNuma * \return 0 if OK, 1 on error */ l_ok l_dnaCopyParameters(L_DNA *dad, L_DNA *das) { l_float64 start, binsize; PROCNAME("l_dnaCopyParameters"); if (!das || !dad) return ERROR_INT("das and dad not both defined", procName, 1); l_dnaGetParameters(das, &start, &binsize); l_dnaSetParameters(dad, start, binsize); return 0; } /*----------------------------------------------------------------------* * Serialize Dna for I/O * *----------------------------------------------------------------------*/ /*! * \brief l_dnaRead() * * \param[in] filename * \return da, or NULL on error */ L_DNA * l_dnaRead(const char *filename) { FILE *fp; L_DNA *da; PROCNAME("l_dnaRead"); if (!filename) return (L_DNA *)ERROR_PTR("filename not defined", procName, NULL); if ((fp = fopenReadStream(filename)) == NULL) return (L_DNA *)ERROR_PTR("stream not opened", procName, NULL); da = l_dnaReadStream(fp); fclose(fp); if (!da) return (L_DNA *)ERROR_PTR("da not read", procName, NULL); return da; } /*! * \brief l_dnaReadStream() * * \param[in] fp file stream * \return da, or NULL on error * *
 * Notes:
 *      (1) fscanf takes %lf to read a double; fprintf takes %f to write it.
 *      (2) It is OK for the dna to be empty.
 * 
*/ L_DNA * l_dnaReadStream(FILE *fp) { l_int32 i, n, index, ret, version; l_float64 val, startx, delx; L_DNA *da; PROCNAME("l_dnaReadStream"); if (!fp) return (L_DNA *)ERROR_PTR("stream not defined", procName, NULL); ret = fscanf(fp, "\nL_Dna Version %d\n", &version); if (ret != 1) return (L_DNA *)ERROR_PTR("not a l_dna file", procName, NULL); if (version != DNA_VERSION_NUMBER) return (L_DNA *)ERROR_PTR("invalid l_dna version", procName, NULL); if (fscanf(fp, "Number of numbers = %d\n", &n) != 1) return (L_DNA *)ERROR_PTR("invalid number of numbers", procName, NULL); if (n < 0) return (L_DNA *)ERROR_PTR("num doubles < 0", procName, NULL); if (n > MaxDoubleArraySize) return (L_DNA *)ERROR_PTR("too many doubles", procName, NULL); if (n == 0) L_INFO("the dna is empty\n", procName); if ((da = l_dnaCreate(n)) == NULL) return (L_DNA *)ERROR_PTR("da not made", procName, NULL); for (i = 0; i < n; i++) { if (fscanf(fp, " [%d] = %lf\n", &index, &val) != 2) { l_dnaDestroy(&da); return (L_DNA *)ERROR_PTR("bad input data", procName, NULL); } l_dnaAddNumber(da, val); } /* Optional data */ if (fscanf(fp, "startx = %lf, delx = %lf\n", &startx, &delx) == 2) l_dnaSetParameters(da, startx, delx); return da; } /*! * \brief l_dnaWrite() * * \param[in] filename * \param[in] da * \return 0 if OK, 1 on error */ l_ok l_dnaWrite(const char *filename, L_DNA *da) { l_int32 ret; FILE *fp; PROCNAME("l_dnaWrite"); if (!filename) return ERROR_INT("filename not defined", procName, 1); if (!da) return ERROR_INT("da not defined", procName, 1); if ((fp = fopenWriteStream(filename, "w")) == NULL) return ERROR_INT("stream not opened", procName, 1); ret = l_dnaWriteStream(fp, da); fclose(fp); if (ret) return ERROR_INT("da not written to stream", procName, 1); return 0; } /*! * \brief l_dnaWriteStream() * * \param[in] fp file stream * \param[in] da * \return 0 if OK, 1 on error */ l_ok l_dnaWriteStream(FILE *fp, L_DNA *da) { l_int32 i, n; l_float64 startx, delx; PROCNAME("l_dnaWriteStream"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!da) return ERROR_INT("da not defined", procName, 1); n = l_dnaGetCount(da); fprintf(fp, "\nL_Dna Version %d\n", DNA_VERSION_NUMBER); fprintf(fp, "Number of numbers = %d\n", n); for (i = 0; i < n; i++) fprintf(fp, " [%d] = %f\n", i, da->array[i]); fprintf(fp, "\n"); /* Optional data */ l_dnaGetParameters(da, &startx, &delx); if (startx != 0.0 || delx != 1.0) fprintf(fp, "startx = %f, delx = %f\n", startx, delx); return 0; } /*--------------------------------------------------------------------------* * Dnaa creation, destruction * *--------------------------------------------------------------------------*/ /*! * \brief l_dnaaCreate() * * \param[in] n size of l_dna ptr array to be alloc'd 0 for default * \return daa, or NULL on error * */ L_DNAA * l_dnaaCreate(l_int32 n) { L_DNAA *daa; PROCNAME("l_dnaaCreate"); if (n <= 0 || n > MaxPtrArraySize) n = InitialArraySize; daa = (L_DNAA *)LEPT_CALLOC(1, sizeof(L_DNAA)); if ((daa->dna = (L_DNA **)LEPT_CALLOC(n, sizeof(L_DNA *))) == NULL) { l_dnaaDestroy(&daa); return (L_DNAA *)ERROR_PTR("l_dna ptr array not made", procName, NULL); } daa->nalloc = n; daa->n = 0; return daa; } /*! * \brief l_dnaaCreateFull() * * \param[in] nptr size of dna ptr array to be alloc'd * \param[in] n size of individual dna arrays to be alloc'd 0 for default * \return daa, or NULL on error * *
 * Notes:
 *      (1) This allocates a dnaa and fills the array with allocated dnas.
 *          In use, after calling this function, use
 *              l_dnaaAddNumber(dnaa, index, val);
 *          to add val to the index-th dna in dnaa.
 * 
*/ L_DNAA * l_dnaaCreateFull(l_int32 nptr, l_int32 n) { l_int32 i; L_DNAA *daa; L_DNA *da; daa = l_dnaaCreate(nptr); for (i = 0; i < nptr; i++) { da = l_dnaCreate(n); l_dnaaAddDna(daa, da, L_INSERT); } return daa; } /*! * \brief l_dnaaTruncate() * * \param[in] daa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This identifies the largest index containing a dna that
 *          has any numbers within it, destroys all dna beyond that
 *          index, and resets the count.
 * 
*/ l_ok l_dnaaTruncate(L_DNAA *daa) { l_int32 i, n, nn; L_DNA *da; PROCNAME("l_dnaaTruncate"); if (!daa) return ERROR_INT("daa not defined", procName, 1); n = l_dnaaGetCount(daa); for (i = n - 1; i >= 0; i--) { da = l_dnaaGetDna(daa, i, L_CLONE); if (!da) continue; nn = l_dnaGetCount(da); l_dnaDestroy(&da); /* the clone */ if (nn == 0) l_dnaDestroy(&daa->dna[i]); else break; } daa->n = i + 1; return 0; } /*! * \brief l_dnaaDestroy() * * \param[in,out] pdaa will be set to null before returning * \return void */ void l_dnaaDestroy(L_DNAA **pdaa) { l_int32 i; L_DNAA *daa; PROCNAME("l_dnaaDestroy"); if (pdaa == NULL) { L_WARNING("ptr address is NULL!\n", procName); return; } if ((daa = *pdaa) == NULL) return; for (i = 0; i < daa->n; i++) l_dnaDestroy(&daa->dna[i]); LEPT_FREE(daa->dna); LEPT_FREE(daa); *pdaa = NULL; } /*--------------------------------------------------------------------------* * Add Dna to Dnaa * *--------------------------------------------------------------------------*/ /*! * \brief l_dnaaAddDna() * * \param[in] daa * \param[in] da to be added * \param[in] copyflag L_INSERT, L_COPY, L_CLONE * \return 0 if OK, 1 on error */ l_ok l_dnaaAddDna(L_DNAA *daa, L_DNA *da, l_int32 copyflag) { l_int32 n; L_DNA *dac; PROCNAME("l_dnaaAddDna"); if (!daa) return ERROR_INT("daa not defined", procName, 1); if (!da) return ERROR_INT("da not defined", procName, 1); if (copyflag == L_INSERT) { dac = da; } else if (copyflag == L_COPY) { if ((dac = l_dnaCopy(da)) == NULL) return ERROR_INT("dac not made", procName, 1); } else if (copyflag == L_CLONE) { dac = l_dnaClone(da); } else { return ERROR_INT("invalid copyflag", procName, 1); } n = l_dnaaGetCount(daa); if (n >= daa->nalloc) { if (l_dnaaExtendArray(daa)) { if (copyflag != L_INSERT) l_dnaDestroy(&dac); return ERROR_INT("extension failed", procName, 1); } } daa->dna[n] = dac; daa->n++; return 0; } /*! * \brief l_dnaaExtendArray() * * \param[in] daa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) Doubles the number of dna ptrs.
 *      (2) The max size of the dna array is 1M ptrs.
 * 
*/ static l_int32 l_dnaaExtendArray(L_DNAA *daa) { size_t oldsize, newsize; PROCNAME("l_dnaaExtendArray"); if (!daa) return ERROR_INT("daa not defined", procName, 1); if (daa->nalloc > MaxPtrArraySize) /* belt & suspenders */ return ERROR_INT("daa has too many ptrs", procName, 1); oldsize = daa->nalloc * sizeof(L_DNA *); newsize = 2 * oldsize; if (newsize > 8 * MaxPtrArraySize) return ERROR_INT("newsize > 8 MB; too large", procName, 1); if ((daa->dna = (L_DNA **)reallocNew((void **)&daa->dna, oldsize, newsize)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); daa->nalloc *= 2; return 0; } /*----------------------------------------------------------------------* * DNumaa accessors * *----------------------------------------------------------------------*/ /*! * \brief l_dnaaGetCount() * * \param[in] daa * \return count number of l_dna, or 0 if no l_dna or on error */ l_int32 l_dnaaGetCount(L_DNAA *daa) { PROCNAME("l_dnaaGetCount"); if (!daa) return ERROR_INT("daa not defined", procName, 0); return daa->n; } /*! * \brief l_dnaaGetDnaCount() * * \param[in] daa * \param[in] index of l_dna in daa * \return count of numbers in the referenced l_dna, or 0 on error. */ l_int32 l_dnaaGetDnaCount(L_DNAA *daa, l_int32 index) { PROCNAME("l_dnaaGetDnaCount"); if (!daa) return ERROR_INT("daa not defined", procName, 0); if (index < 0 || index >= daa->n) return ERROR_INT("invalid index into daa", procName, 0); return l_dnaGetCount(daa->dna[index]); } /*! * \brief l_dnaaGetNumberCount() * * \param[in] daa * \return count total number of numbers in the l_dnaa, * or 0 if no numbers or on error */ l_int32 l_dnaaGetNumberCount(L_DNAA *daa) { L_DNA *da; l_int32 n, sum, i; PROCNAME("l_dnaaGetNumberCount"); if (!daa) return ERROR_INT("daa not defined", procName, 0); n = l_dnaaGetCount(daa); for (sum = 0, i = 0; i < n; i++) { da = l_dnaaGetDna(daa, i, L_CLONE); sum += l_dnaGetCount(da); l_dnaDestroy(&da); } return sum; } /*! * \brief l_dnaaGetDna() * * \param[in] daa * \param[in] index to the index-th l_dna * \param[in] accessflag L_COPY or L_CLONE * \return l_dna, or NULL on error */ L_DNA * l_dnaaGetDna(L_DNAA *daa, l_int32 index, l_int32 accessflag) { PROCNAME("l_dnaaGetDna"); if (!daa) return (L_DNA *)ERROR_PTR("daa not defined", procName, NULL); if (index < 0 || index >= daa->n) return (L_DNA *)ERROR_PTR("index not valid", procName, NULL); if (accessflag == L_COPY) return l_dnaCopy(daa->dna[index]); else if (accessflag == L_CLONE) return l_dnaClone(daa->dna[index]); else return (L_DNA *)ERROR_PTR("invalid accessflag", procName, NULL); } /*! * \brief l_dnaaReplaceDna() * * \param[in] daa * \param[in] index to the index-th l_dna * \param[in] da insert and replace any existing one * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) Any existing l_dna is destroyed, and the input one
 *          is inserted in its place.
 *      (2) If %index is invalid, return 1 (error)
 * 
*/ l_ok l_dnaaReplaceDna(L_DNAA *daa, l_int32 index, L_DNA *da) { l_int32 n; PROCNAME("l_dnaaReplaceDna"); if (!daa) return ERROR_INT("daa not defined", procName, 1); if (!da) return ERROR_INT("da not defined", procName, 1); n = l_dnaaGetCount(daa); if (index < 0 || index >= n) return ERROR_INT("index not valid", procName, 1); l_dnaDestroy(&daa->dna[index]); daa->dna[index] = da; return 0; } /*! * \brief l_dnaaGetValue() * * \param[in] daa * \param[in] i index of l_dna within l_dnaa * \param[in] j index into l_dna * \param[out] pval double value * \return 0 if OK, 1 on error */ l_ok l_dnaaGetValue(L_DNAA *daa, l_int32 i, l_int32 j, l_float64 *pval) { l_int32 n; L_DNA *da; PROCNAME("l_dnaaGetValue"); if (!pval) return ERROR_INT("&val not defined", procName, 1); *pval = 0.0; if (!daa) return ERROR_INT("daa not defined", procName, 1); n = l_dnaaGetCount(daa); if (i < 0 || i >= n) return ERROR_INT("invalid index into daa", procName, 1); da = daa->dna[i]; if (j < 0 || j >= da->n) return ERROR_INT("invalid index into da", procName, 1); *pval = da->array[j]; return 0; } /*! * \brief l_dnaaAddNumber() * * \param[in] daa * \param[in] index of l_dna within l_dnaa * \param[in] val number to be added; stored as a double * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) Adds to an existing l_dna only.
 * 
*/ l_ok l_dnaaAddNumber(L_DNAA *daa, l_int32 index, l_float64 val) { l_int32 n; L_DNA *da; PROCNAME("l_dnaaAddNumber"); if (!daa) return ERROR_INT("daa not defined", procName, 1); n = l_dnaaGetCount(daa); if (index < 0 || index >= n) return ERROR_INT("invalid index in daa", procName, 1); da = l_dnaaGetDna(daa, index, L_CLONE); l_dnaAddNumber(da, val); l_dnaDestroy(&da); return 0; } /*----------------------------------------------------------------------* * Serialize Dna for I/O * *----------------------------------------------------------------------*/ /*! * \brief l_dnaaRead() * * \param[in] filename * \return daa, or NULL on error */ L_DNAA * l_dnaaRead(const char *filename) { FILE *fp; L_DNAA *daa; PROCNAME("l_dnaaRead"); if (!filename) return (L_DNAA *)ERROR_PTR("filename not defined", procName, NULL); if ((fp = fopenReadStream(filename)) == NULL) return (L_DNAA *)ERROR_PTR("stream not opened", procName, NULL); daa = l_dnaaReadStream(fp); fclose(fp); if (!daa) return (L_DNAA *)ERROR_PTR("daa not read", procName, NULL); return daa; } /*! * \brief l_dnaaReadStream() * * \param[in] fp file stream * \return daa, or NULL on error * *
 * Notes:
 *      (1) It is OK for the dnaa to be empty.
 * 
*/ L_DNAA * l_dnaaReadStream(FILE *fp) { l_int32 i, n, index, ret, version; L_DNA *da; L_DNAA *daa; PROCNAME("l_dnaaReadStream"); if (!fp) return (L_DNAA *)ERROR_PTR("stream not defined", procName, NULL); ret = fscanf(fp, "\nL_Dnaa Version %d\n", &version); if (ret != 1) return (L_DNAA *)ERROR_PTR("not a l_dna file", procName, NULL); if (version != DNA_VERSION_NUMBER) return (L_DNAA *)ERROR_PTR("invalid l_dnaa version", procName, NULL); if (fscanf(fp, "Number of L_Dna = %d\n\n", &n) != 1) return (L_DNAA *)ERROR_PTR("invalid number of l_dna", procName, NULL); if (n < 0) return (L_DNAA *)ERROR_PTR("num l_dna <= 0", procName, NULL); if (n > MaxPtrArraySize) return (L_DNAA *)ERROR_PTR("too many l_dna", procName, NULL); if (n == 0) L_INFO("the dnaa is empty\n", procName); if ((daa = l_dnaaCreate(n)) == NULL) return (L_DNAA *)ERROR_PTR("daa not made", procName, NULL); for (i = 0; i < n; i++) { if (fscanf(fp, "L_Dna[%d]:", &index) != 1) { l_dnaaDestroy(&daa); return (L_DNAA *)ERROR_PTR("invalid l_dna header", procName, NULL); } if ((da = l_dnaReadStream(fp)) == NULL) { l_dnaaDestroy(&daa); return (L_DNAA *)ERROR_PTR("da not made", procName, NULL); } l_dnaaAddDna(daa, da, L_INSERT); } return daa; } /*! * \brief l_dnaaWrite() * * \param[in] filename * \param[in] daa * \return 0 if OK, 1 on error */ l_ok l_dnaaWrite(const char *filename, L_DNAA *daa) { l_int32 ret; FILE *fp; PROCNAME("l_dnaaWrite"); if (!filename) return ERROR_INT("filename not defined", procName, 1); if (!daa) return ERROR_INT("daa not defined", procName, 1); if ((fp = fopenWriteStream(filename, "w")) == NULL) return ERROR_INT("stream not opened", procName, 1); ret = l_dnaaWriteStream(fp, daa); fclose(fp); if (ret) return ERROR_INT("daa not written to stream", procName, 1); return 0; } /*! * \brief l_dnaaWriteStream() * * \param[in] fp file stream * \param[in] daa * \return 0 if OK, 1 on error */ l_ok l_dnaaWriteStream(FILE *fp, L_DNAA *daa) { l_int32 i, n; L_DNA *da; PROCNAME("l_dnaaWriteStream"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!daa) return ERROR_INT("daa not defined", procName, 1); n = l_dnaaGetCount(daa); fprintf(fp, "\nL_Dnaa Version %d\n", DNA_VERSION_NUMBER); fprintf(fp, "Number of L_Dna = %d\n\n", n); for (i = 0; i < n; i++) { if ((da = l_dnaaGetDna(daa, i, L_CLONE)) == NULL) return ERROR_INT("da not found", procName, 1); fprintf(fp, "L_Dna[%d]:", i); l_dnaWriteStream(fp, da); l_dnaDestroy(&da); } return 0; }