/** @file  psVector.c
 *
 *  @brief Contains support for basic vector types
 *
 *  This file defines the basic type for a vector struct and functions useful
 *  in manupulating vectors.
 *
 *  @author Ross Harman, MHPCC
 *
 *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2004-07-15 23:52:33 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 */

/******************************************************************************/
/*  INCLUDE FILES                                                             */
/******************************************************************************/
#include <string.h>        // for memcpy
#include <stdlib.h>
#include <math.h>

#include "psMemory.h"
#include "psError.h"
#include "psVector.h"
#include "psLogMsg.h"
#include "psCompare.h"

/******************************************************************************/
/*  DEFINE STATEMENTS                                                         */
/******************************************************************************/

// None

/******************************************************************************/
/*  TYPE DEFINITIONS                                                          */
/******************************************************************************/

// None

/*****************************************************************************/
/*  GLOBAL VARIABLES                                                         */
/*****************************************************************************/

// None

/*****************************************************************************/
/*  FILE STATIC VARIABLES                                                    */
/*****************************************************************************/

// None

/*****************************************************************************/
/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
/*****************************************************************************/
static void vectorFree(psVector *restrict psVec);

/*****************************************************************************/
/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
/*****************************************************************************/
psVector* psVectorAlloc(unsigned int nalloc, psElemType elemType)
{
    psVector *psVec = NULL;
    int elementSize = 0;

    // Invalid nalloc
    if(nalloc < 1) {
        psError(__func__, "Invalid value for nalloc. nalloc: %d\n", nalloc);
        return NULL;
    }

    elementSize = PSELEMTYPE_SIZEOF(elemType);

    // Create vector struct
    psVec = (psVector *)psAlloc(sizeof(psVector));
    p_psMemSetDeallocator(psVec,(psFreeFcn)vectorFree);

    psVec->type.dimen = PS_DIMEN_VECTOR;
    psVec->type.type = elemType;
    psVec->nalloc = nalloc;
    psVec->n = nalloc;

    // Create vector data array
    psVec->data.V = psAlloc(nalloc*elementSize);

    return psVec;
}

psVector *psVectorRealloc(unsigned int nalloc, psVector *restrict in)
{
    int elementSize = 0;
    psElemType elemType;

    // Invalid nalloc
    if(nalloc < 1) {
        psError(__func__, "Invalid value for realloc (%d)\n", nalloc);
        return NULL;
    }

    if(in == NULL) {
        psError(__func__, "Null input vector\n");
        return NULL;
    } else if(in->nalloc != nalloc) {                    // No need to realloc to same size
        elemType = in->type.type;
        elementSize = PSELEMTYPE_SIZEOF(elemType);
        if(nalloc < in->n) {
            in->n = nalloc;
        }

        // Realloc after decrementation to avoid accessing freed array elements
        in->data.V = psRealloc(in->data.V,nalloc*elementSize);
        in->nalloc = nalloc;
    }

    return in;
}

psVector *psVectorRecycle(psVector *restrict in, psElemType type, unsigned int nalloc)
{
    psElemType elemType;

    if (in == NULL) {
        return psVectorAlloc(nalloc, type);
    }

    elemType = in->type.type;

    if (in->nalloc == nalloc && elemType == type) {
        // it is proper size/type already
        return in;
    }

    // Invalid nalloc
    if(nalloc < 1) {
        psError(__func__, "Invalid value for nalloc (%d)\n", nalloc);
        psFree(in);
        return NULL;
    }


    in->data.V = psRealloc(in->data.V,nalloc*PSELEMTYPE_SIZEOF(type));

    in->n = 0;
    in->type.type = type;
    in->nalloc = nalloc;

    return in;
}

psVector *psVectorSort(psVector *restrict outVector, const psVector *restrict inVector)
{
    int inN = 0;
    int outN = 0;
    int elSize = 0;
    void *inVec = NULL;
    void *outVec = NULL;
    psElemType inType = 0;

    if(inVector == NULL) {
        psError(__func__, " : Line %d - Null input vector\n", __LINE__);
        return outVector;
    }

    inType = inVector->type.type;
    inN = inVector->n;
    inVec = inVector->data.V;
    elSize = PSELEMTYPE_SIZEOF(inType);

    if(outVector == NULL) {
        outVector = psVectorAlloc(inN, inType);
        outVector->n = inVector->n;
    }

    outN = outVector->n;
    outVec = outVector->data.V;

    if(inN != outN) {
        psError(__func__, " : Line %d - Input and output vector sizes are not equal: in=%d out=%d\n", __LINE__,
                inN, outN);
        return outVector;
    }

    if(inN == 0) {
        psError(__func__, " : Line %d - No elements in use for input vector\n", __LINE__);
        return outVector;
    }

    if(outN == 0) {
        psError(__func__, " : Line %d - No elements in use for output vector\n", __LINE__);
        return outVector;
    }

    // Copy input vector values into output vector
    memcpy(outVec, inVec, elSize*outN);

    // Sort output vector
    switch(inType) {
    case PS_TYPE_U8:
        qsort(outVec, inN, elSize, psCompareU8);
        break;
    case PS_TYPE_U16:
        qsort(outVec, inN, elSize, psCompareU16);
        break;
    case PS_TYPE_U32:
        qsort(outVec, inN, elSize, psCompareU32);
        break;
    case PS_TYPE_U64:
        qsort(outVec, inN, elSize, psCompareU64);
        break;
    case PS_TYPE_S8:
        qsort(outVec, inN, elSize, psCompareS8);
        break;
    case PS_TYPE_S16:
        qsort(outVec, inN, elSize, psCompareS16);
        break;
    case PS_TYPE_S32:
        qsort(outVec, inN, elSize, psCompareS32);
        break;
    case PS_TYPE_S64:
        qsort(outVec, inN, elSize, psCompareS64);
        break;
    case PS_TYPE_F32:
        qsort(outVec, inN, elSize, psCompareF32);
        break;
    case PS_TYPE_F64:
        qsort(outVec, inN, elSize, psCompareF64);
        break;
    default:
        psError(__func__, " : Line %d - Invalid psType\n", __LINE__);
    }

    return outVector;
}

#define SORT_INDICES(TYPE)                                                                                   \
for(i=0; i<inN; i++) {                                                                                       \
    for(j=0; j<inN; j++) {                                                                                   \
        diff = fabs((double)tmpVector->data.TYPE[i] - inVec[j]);                                             \
        if(diff < FLT_EPSILON) {                                                                             \
            outVec[i] = j;                                                                                   \
            break;                                                                                           \
        }                                                                                                    \
    }                                                                                                        \
}

psVector *psVectorSortIndex(psVector *restrict outVector, const psVector *restrict inVector)
{
    int inN = 0;
    int outN = 0;
    int i = 0;
    int j = 0;
    float *inVec = NULL;
    int *outVec = NULL;
    double diff = 0.0f;
    psVector *tmpVector = NULL;
    psElemType inType = 0;

    if(inVector == NULL) {
        psError(__func__, " : Line %d - Null input vector\n", __LINE__);
        return outVector;
    }

    inN = inVector->n;
    inVec = inVector->data.V;
    inType = inVector->type.type;

    if(outVector == NULL) {
        outVector = psVectorAlloc(inN, PS_TYPE_U32);
        outVector->n = inN;
    }

    outN = outVector->n;
    outVec = outVector->data.V;

    if(inN != outN) {
        psError(__func__, " : Line %d - Input and output vector sizes are not equal: in=%d out=%d\n",
                __LINE__, inN, outN);
        return outVector;
    }

    tmpVector = psVectorAlloc(inN, inType);
    tmpVector->n = inN;
    tmpVector = psVectorSort(tmpVector, inVector);

    // Sort output vector
    switch(inType) {
    case PS_TYPE_U8:
        SORT_INDICES(U8);
        break;
    case PS_TYPE_U16:
        SORT_INDICES(U16);
        break;
    case PS_TYPE_U32:
        SORT_INDICES(U32);
        break;
    case PS_TYPE_U64:
        SORT_INDICES(U64);
        break;
    case PS_TYPE_S8:
        SORT_INDICES(S8);
        break;
    case PS_TYPE_S16:
        SORT_INDICES(S16);
        break;
    case PS_TYPE_S32:
        SORT_INDICES(S32);
        break;
    case PS_TYPE_S64:
        SORT_INDICES(S64);
        break;
    case PS_TYPE_F32:
        SORT_INDICES(F32);
        break;
    case PS_TYPE_F64:
        SORT_INDICES(F64);
        break;
    default:
        psError(__func__, " : Line %d - Invalid psType\n", __LINE__);
    }

    // Free temp memory
    psFree(tmpVector);

    return outVector;
}

static void vectorFree(psVector *restrict psVec)
{
    if (psVec == NULL) {
        return;
    }

    psFree(psVec->data.V);
}
