/*------------------------------------------------------------------------------
        FILE    :       DynArray.c
        CREATED :       97/12/02
        AUTHOR  :       Catalin Cocos
        CONTENTS:       dynamic pointer array with sorting capabilities
------------------------------------------------------------------------------*/

#include "StdInc.h"
#include "DynArray.h"

/*------------------------------------------------------------------------------
        DESC.   :       Creaton of a default array (allocation + default initialization)
        CREATED :       97/12/02        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
LDT_tdst_DSArray* LDT_DSAr_new()
{
        LDT_tdst_DSArray* pst_NewArray = malloc( sizeof(LDT_tdst_DSArray) ); /* allocate */
        LDT_DSAr_Init( pst_NewArray );
        return pst_NewArray;
}

/*------------------------------------------------------------------------------
        DESC.   :       default initialization of an array - returns 1 if successful,
                                0 on error;
        WARNING :       This function should be called only once and prior to the use of
                                any other array functions.
        CREATED :       97/12/02        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSAr_Init( LDT_tdst_DSArray* pst_Ar)
{
        if( !pst_Ar ) return 0; /* NULL argument */
        memset( pst_Ar, 0, sizeof( LDT_tdst_DSArray ) );
        return 1; /* success */
}

/*------------------------------------------------------------------------------
        DESC.   :       deletes an existing array
        WARNING :       this should only be called for dinamically allocated arrays.
        CREATED :       97/12/02        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_DSAr_delete( LDT_tdst_DSArray* pst_Ar )
{
        if( !pst_Ar ) return;
        LDT_DSAr_RemoveAll( pst_Ar );
        free( pst_Ar );
}


/*------------------------------------------------------------------------------
        DESC.   :       sets the array's size. - returns 1 if successful, 0 on error;
        CREATED :       97/12/02        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSAr_SetSize( LDT_tdst_DSArray* pst_Ar, int nNewSize )
{
        void** pNewData;

        if(nNewSize < 0) return 0; /* wrong size input */
        
        if (nNewSize == 0)
        {
                /* shrink to nothing */
                if(pst_Ar->pData)
				{
					free( pst_Ar->pData );
					pst_Ar->pData = NULL;
				}
                pst_Ar->nSize = pst_Ar->nMaxSize = 0;
        }
        else if (pst_Ar->pData == NULL)
        {
                int nNewMax = nNewSize;
                if( nNewSize < pst_Ar->nGrowBy ) nNewMax = pst_Ar->nGrowBy;
                /* create one with exact size */
                pst_Ar->pData = malloc( nNewMax * sizeof(void*) );      
                if(!pst_Ar->pData ) return 0; /* allocation failed */
                memset( pst_Ar->pData, 0, nNewMax * sizeof(void*) ); /* zero fill */
                pst_Ar->nSize = nNewSize;
                pst_Ar->nMaxSize = nNewMax;
        }
        else if (nNewSize <= pst_Ar->nMaxSize)
        {       
                /* it fits */
                if (nNewSize > pst_Ar->nSize)
                        /* initialize the new elements */
                        memset( &pst_Ar->pData[pst_Ar->nSize], 0,
                                        (nNewSize-pst_Ar->nSize) * sizeof(void*));
                pst_Ar->nSize = nNewSize;
        }
        else
        {
                /* Otherwise grow array */
                int nNewMax;
                if (nNewSize < pst_Ar->nMaxSize + pst_Ar->nGrowBy)
                        nNewMax = pst_Ar->nMaxSize + pst_Ar->nGrowBy;  /* granularity */
                else
                        nNewMax = nNewSize;  /* no slush */
                
                pNewData = realloc( pst_Ar->pData, nNewMax * sizeof(void*) );
                if( !pNewData ) return 0; /* reallocation failed */

                /* construct remaining elements */
                memset( &pNewData[pst_Ar->nSize], 0,
                                (nNewSize-pst_Ar->nSize) * sizeof(void*));
                
                pst_Ar->pData = pNewData;
                pst_Ar->nSize = nNewSize;
                pst_Ar->nMaxSize = nNewMax;
        }
        return 1; /* success */
}


/*------------------------------------------------------------------------------
        DESC.   :       frees unused memory
        CREATED :       97/12/02        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_DSAr_FreeExtra( LDT_tdst_DSArray* pst_Ar)
{
        void** pNewData;
        if (pst_Ar->nSize != pst_Ar->nMaxSize)
        {
                /* shrink to desired size */
                if( pst_Ar->nSize )
                {
                        pNewData = realloc( pst_Ar->pData, pst_Ar->nSize * sizeof(void*) );
                        if( pNewData )
                        {
                                pst_Ar->pData = pNewData;
                                pst_Ar->nMaxSize = pst_Ar->nSize;
                        }
                }
                else
                        LDT_DSAr_SetSize( pst_Ar, 0 ); /* clean-up */
        }
}


/*------------------------------------------------------------------------------
        DESC.   :       Appends an array element. returns the index of the appended
                                element, or -1 on error
        CREATED :       97/12/02        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSAr_Add( LDT_tdst_DSArray* pst_Ar, void* newElement)
{
    if(!LDT_DSAr_SetSize( pst_Ar, pst_Ar->nSize+1 )) return -1;
        pst_Ar->pData[pst_Ar->nSize-1] = newElement;
        return pst_Ar->nSize-1;
}


/*------------------------------------------------------------------------------
        DESC.   :       Inserts a new array element. Returns 1 on success, 0 on failure
        CREATED :       97/12/02        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSAr_InsertAt(LDT_tdst_DSArray* pst_Ar, int nIndex, void* newElement )
{
        if(nIndex < 0) return 0;
        
        if (nIndex >= pst_Ar->nSize)
        {
                /* adding after the end of the array */
                /* grow so nIndex is valid */
                if(!LDT_DSAr_SetSize( pst_Ar, nIndex + 1)) return 0;
        }
        else
        {
                /* inserting in the middle of the array */
                /* grow the array to new size */
                if(!LDT_DSAr_SetSize( pst_Ar, pst_Ar->nSize + 1)) return 0; 
                /* shift old data up to fill gap */
                memmove(pst_Ar->pData+nIndex+1, pst_Ar->pData+nIndex,
                        ( pst_Ar->nSize - nIndex - 1 ) * sizeof(void*));
        }
        /* insert new value in the gap */
        pst_Ar->pData[nIndex] = newElement;
        return 1;
}

/*------------------------------------------------------------------------------
        DESC.   :       removes an array element. Returns the element on success, NULL on failure
        CREATED :       97/12/02        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
void* LDT_DSAr_RemoveAt(LDT_tdst_DSArray* pst_Ar, int nIndex )
{
        int nMoveCount;
		void * pRemoved;
        if(nIndex < 0 || nIndex >= pst_Ar->nSize ) return NULL;
        
		pRemoved = pst_Ar->pData[nIndex];	/* the removed element */
		
		nMoveCount = pst_Ar->nSize - nIndex - 1;
		memcpy(pst_Ar->pData+nIndex, pst_Ar->pData+nIndex+1, nMoveCount*sizeof(void*));
        pst_Ar->nSize--;
        return pRemoved;
}

/*------------------------------------------------------------------------------
        DESC.   :       Inserts the elements of another array. Returns 1 on success,
                                0 on failure.
        CREATED :       97/12/02        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSAr_InsertArrayAt(LDT_tdst_DSArray* pst_Ar, int nIndex, LDT_tdst_DSArray* pNewArray)
{
        int nCount, nOldSize;
        if(pNewArray == NULL || nIndex < 0) return 0;
        nCount = pNewArray->nSize;
        if (nCount > 0)
        {
                if (nIndex >= pst_Ar->nSize)
                {
                        /* adding after the end of the array */
                        /* grow so nIndex is valid */
                        if(!LDT_DSAr_SetSize(pst_Ar, nIndex + nCount)) return 0;  
                }
                else
                {
                        /* inserting in the middle of the array */
                        nOldSize = pst_Ar->nSize;
                        /* grow it to new size */
                        if(!LDT_DSAr_SetSize( pst_Ar, nOldSize  + nCount)) return 0;  
                        /* shift old data up to fill gap */
                        memmove(pst_Ar->pData+nIndex+nCount, pst_Ar->pData+nIndex,
                                        (nOldSize-nIndex) * sizeof(void*));
                }
                memcpy( pst_Ar->pData + nIndex, pNewArray->pData, nCount * sizeof(void*));
        }
        return 1;
}


/*------------------------------------------------------------------------------
        DESC.   :       Sorts the array, using the comparison function
        CREATED :       97/12/03        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
void LDT_DSAr_QSort( LDT_tdst_DSArray* pst_Ar )
{
        if( pst_Ar->pfnCompare)
                qsort(  &pst_Ar->pData[0], pst_Ar->nSize, sizeof(void*),
                                (int (*)(const void *,const void *)) pst_Ar->pfnCompare );
}

/*------------------------------------------------------------------------------
        DESC.   :       Adds an element in the array, in sorted position.
                                Returns the index of the added element, -1 on failure
                                On failure,if the <<position>> parameter is not NULL, it will
                                contain the index at which the element(or an equivalent) was found.
        CREATED :       97/12/03        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSAr_SAdd( LDT_tdst_DSArray* pst_Ar, void* newElement, int AdmitDuplicates, int* position )
{
        int iidx, idx = LDT_DSAr_Index( pst_Ar, newElement, &iidx );
        if(position) *position = idx;
        if(idx>=0 && ( !AdmitDuplicates || (newElement == pst_Ar->pData[idx]) ))
                return -1;
        else return ( LDT_DSAr_InsertAt(pst_Ar, iidx, newElement )? iidx: -1 );
}


/*------------------------------------------------------------------------------
        DESC.   :       gets the index of a certain element. -1 if the element
                                (or an equivalent) is not found
        CREATED :       97/12/03        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSAr_Index( LDT_tdst_DSArray* pst_Ar, void* newElement, int *p_iidx )
{
        int hidx = pst_Ar->nSize-1, lidx = 0, cidx; /* , i, plus, minus; */
	
		assert(pst_Ar->pfnCompare); /* the comparison function must exist*/
        
		if(p_iidx) *p_iidx = 0;
        if(!newElement) return -1;
        if(hidx<0) return hidx;
        while(hidx > lidx)
                switch(pst_Ar->pfnCompare(&newElement,pst_Ar->pData+(cidx = (hidx+lidx)/2)))
                {
                case  0: hidx = lidx = cidx; break;
                case -1: hidx = cidx; break;
                default: lidx = cidx+1; break;
                }
        if(p_iidx) *p_iidx = lidx+1;
        switch(pst_Ar->pfnCompare( &newElement, pst_Ar->pData+lidx ))
        {
        case  0:
                /* for( i = 0, plus = pst_Ar->nSize-1-lidx, minus = lidx; plus>=0 || minus>=0; i++ )
                {
                        if(     plus>=i && 
                                (plus = pst_Ar->pfnCompare(&newElement,pst_Ar->pData+lidx+1)?-1:plus)>=0 &&
                                newElement == pst_Ar->pData[lidx+i])
                        {
                                lidx += i;
                                break;
                        }
                        if(     minus>=i &&
                                (minus = pst_Ar->pfnCompare(&newElement,pst_Ar->pData+lidx-1 )?-1:minus)>=0 &&
                                newElement == pst_Ar->pData[lidx+i])
                        {
                                lidx -= i;
                                break;
                        }
                } */
                if(p_iidx) *p_iidx = lidx+1;
                return lidx;
        case -1:
                if(p_iidx) (*p_iidx)--;
        default:
                return -1;
        }
}

/*------------------------------------------------------------------------------
        DESC.   :       gets the starting of an equivalence interval that includes the 
                                <<idx>> index
        CREATED :       97/12/03        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSAr_UpperBound( LDT_tdst_DSArray* pst_Ar, int idx)
{
        void** pCrt;
        int Midx = pst_Ar->nSize-1, lidx, hidx, cidx; /* the upper limit */
        if(idx<0 || idx>=Midx ) return Midx;          /* the item is out of bounds or the last in the array */
        pCrt = pst_Ar->pData + (lidx = hidx = idx);   /* this is the target */
        /* if the last element of the array is <=> with the target, return its index (no use for further search)*/
        if( !pst_Ar->pfnCompare( pCrt, pst_Ar->pData + Midx ) ) return Midx;
        /* find the interval bound */
        for(hidx ++; !pst_Ar->pfnCompare( pCrt,pst_Ar->pData + hidx );
                lidx = hidx, cidx = idx+(hidx-idx)*2, hidx = ( cidx<Midx? cidx: Midx ));
        hidx--;
        while(hidx > lidx)
                switch(pst_Ar->pfnCompare( pCrt, pst_Ar->pData +( cidx = (hidx+lidx+1)/2) ))
                {
                        case -1: hidx = cidx-1; break;
                        default: lidx = cidx; break;
                }
        return lidx;
}

/*------------------------------------------------------------------------------
        DESC.   :       gets the beginning of an equivalence interval that includes the 
                                <<idx>> index
        CREATED :       97/12/03        
        AUTHOR  :       Catalin Cocos
------------------------------------------------------------------------------*/
int LDT_DSAr_LowerBound( LDT_tdst_DSArray* pst_Ar, int idx)
{
        int lidx, hidx, cidx;
        void** pCrt;
        if(idx<=0 || idx>pst_Ar->nSize-1 ) return 0;   /* the item is out of bounds or the last in the array */
        pCrt = pst_Ar->pData + (lidx = hidx = idx);       /* this is the target */
        /* if the first element of the array is <=> with the target, return its index (no use for further search) */
        if(!pst_Ar->pfnCompare( pCrt, pst_Ar->pData )) return 0;
        /* find the interval bound */
        for(lidx--; !pst_Ar->pfnCompare( pCrt, pst_Ar->pData + lidx );
                hidx = lidx, cidx = idx-(idx-lidx)*2, lidx = (cidx>lidx? cidx: lidx) );
        lidx++;
        while(hidx > lidx)
                switch(pst_Ar->pfnCompare( pCrt, pst_Ar->pData + (cidx = (hidx+lidx)/2) ) )
                {
                        case 0: case -1: hidx = cidx; break;
                        default: lidx = cidx+1; break;
                }
        return lidx;
}
