/** @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
*  @author Robert DeSonia, MHPCC
*  @author Joshua Hoblitt, University of Hawaii
*
*  @version $Revision: 1.94 $ $Name: not supported by cvs2svn $
*  @date $Date: 2007-02-08 02:59:36 $
*
*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <string.h>                        // for memcpy
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <unistd.h>

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

typedef struct
{
    union {
        psU8* U8;                      ///< Unsigned 8-bit integer data.
        psU16* U16;                    ///< Unsigned 16-bit integer data.
        psU32* U32;                    ///< Unsigned 32-bit integer data.
        psU64* U64;                    ///< Unsigned 64-bit integer data.
        psS8* S8;                      ///< Signed 8-bit integer data.
        psS16* S16;                    ///< Signed 16-bit integer data.
        psS32* S32;                    ///< Signed 32-bit integer data.
        psS64* S64;                    ///< Signed 64-bit integer data.
        psF32* F32;                    ///< Single-precision float data.
        psF64* F64;                    ///< Double-precision float data.
        psC32* C32;                    ///< Single-precision complex data.
        psC64* C64;                    ///< Double-precision complex data.
    } data;
    psU32 index;
}
indexedVector;

static void vectorFree(psVector* psVec);

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

    psFree(psVec->data.U8);
}

// FUNCTION IMPLEMENTATION - PUBLIC
bool psMemCheckVector(psPtr ptr)
{
    PS_ASSERT_PTR(ptr, false);
    return ( psMemGetDeallocator(ptr) == (psFreeFunc)vectorFree );
}


// Allocate a psVector; does not set vector->n (left to the caller)
static psVector *vectorAlloc(const char *file,
                             unsigned int lineno,
                             const char *func,
                             long nalloc, // Number of elements to allocate
                             psElemType type) // Type of elements
{
    int elementSize = PSELEMTYPE_SIZEOF(type); // Size, in bytes, of element
    if (elementSize < 1) {
        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
                _("Input psVector is an unsupported type (0x%x)."), type);
        return NULL;
    }

    // Create vector struct
    psVector *vector = (psVector*) p_psAlloc(file, lineno, func, sizeof(psVector));
    psMemSetDeallocator(vector, (psFreeFunc) vectorFree);

    vector->type.dimen = PS_DIMEN_VECTOR;
    vector->type.type = type;
    P_PSVECTOR_SET_NALLOC(vector, nalloc);

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

    return vector;
}

psVector* p_psVectorAlloc(const char *file,
                          unsigned int lineno,
                          const char *func,
                          long nalloc,
                          psElemType type)
{
    psVector *vector = vectorAlloc(file, lineno, func, nalloc, type);
    if (!vector) {
        return NULL;
    }
    vector->n = nalloc;
    return vector;
}

psVector* p_psVectorAllocEmpty(const char *file,
                               unsigned int lineno,
                               const char *func,
                               long nalloc,
                               psElemType type)
{
    psVector *vector = vectorAlloc(file, lineno, func, nalloc, type);
    if (!vector) {
        return NULL;
    }
    vector->n = 0;
    return vector;
}

psVector* psVectorRealloc(psVector* vector,
                          long nalloc)
{
    psS32 elementSize = 0;
    psElemType elemType;

    if (vector == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                _("psVectorRealloc must a given a non-NULL psVector to resize.  Desired datatype unknown."));
        return NULL;
    } else if (vector->nalloc != nalloc) {     // No need to realloc to same size
        elemType = vector->type.type;
        elementSize = PSELEMTYPE_SIZEOF(elemType);
        if (nalloc < vector->n) {
            vector->n = nalloc;
        }
        // Realloc after decrementation to avoid accessing freed array elements
        vector->data.U8 = psRealloc(vector->data.U8, nalloc * elementSize);
        P_PSVECTOR_SET_NALLOC(vector,nalloc);
    }

    return vector;
}

psVector* p_psVectorRecycle(const char *file,
                            unsigned int lineno,
                            const char *func,
                            psVector* vector,
                            long nalloc,
                            psElemType type)
{
    psS32 byteSize;

    if (vector == NULL) {
        return p_psVectorAlloc(file, lineno, func, nalloc, type);
    }

    if (vector->type.dimen !=  PS_DIMEN_VECTOR &&
            vector->type.dimen !=  PS_DIMEN_TRANSV) {
        psFree(vector);
        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
                _("The input psVector must have a vector dimension type."));
        return NULL;
    }

    byteSize = nalloc * PSELEMTYPE_SIZEOF(type);

    // need to increase data buffer?
    if (byteSize > vector->nalloc*PSELEMTYPE_SIZEOF(vector->type.type)) {
        vector->data.U8 = psRealloc(vector->data.U8, byteSize);
        P_PSVECTOR_SET_NALLOC(vector,nalloc);
    }

    vector->type.dimen = PS_DIMEN_VECTOR;
    vector->type.type = type;
    vector->n = nalloc;
    return vector;
}

psVector *psVectorExtend(psVector *vector,
                         long delta,
                         long nExtend)
{
    // can't handle a NULL vector (don't know the data type to allocate)
    if (vector == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                _("The input psVector can not be NULL."));
        return NULL;
    }

    // requirement on delta; if < 1, set to 10.
    if (delta < 1) {
        delta = 10;
    }

    // adjust the allocated size, if needed. (if nExtend < 1, this is will never happen)
    if (nExtend > 0) {
        unsigned int minAlloc = vector->n + nExtend + nExtend;
        if (vector->nalloc < minAlloc) {
            unsigned int nAlloc = delta + vector->nalloc;
            // make sure the delta is large enough hold twice the extended length.
            if (nAlloc < minAlloc) {
                nAlloc = minAlloc;
            }
            vector = psVectorRealloc(vector, nAlloc);
        }
    } else if (nExtend < -vector->n) {
        // For the case of a negative nExtend, need to check that we are not decreasing
        // vector beyond its own length (i.e., creating a negative length).
        nExtend = -vector->n;
    }

    // increment the length by the value specified
    vector->n += nExtend;

    return vector;
}

psVector* p_psVectorCopy(const char *file,
                       unsigned int lineno,
                       const char *func,
                       psVector* output,
                       const psVector* input,
                       psElemType type)
{
    if (input == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                _("The input psVector can not be NULL."));
        psFree(output);
        return NULL;
    }

    psS32 nElements = input->n;

    output = p_psVectorRecycle(file, lineno, func, output, nElements, type);
    if (nElements == 0) {
        //        psWarning("Warning: psVector was copied with 0 elements!\n");
        return output;
    }
    output->n = nElements;

    if (input->type.type == type) {
        // Can simply copy the bytes if the types are the same

        #define PSVECTOR_COPY_SAME_CASE(NAME) \
    case PS_TYPE_##NAME: \
        output->data.NAME = memcpy(output->data.NAME, input->data.NAME, \
                                   input->n * PSELEMTYPE_SIZEOF(PS_TYPE_##NAME)); \
        break;

        switch (type) {
            PSVECTOR_COPY_SAME_CASE(U8);
            PSVECTOR_COPY_SAME_CASE(U16);
            PSVECTOR_COPY_SAME_CASE(U32);
            PSVECTOR_COPY_SAME_CASE(U64);
            PSVECTOR_COPY_SAME_CASE(S8);
            PSVECTOR_COPY_SAME_CASE(S16);
            PSVECTOR_COPY_SAME_CASE(S32);
            PSVECTOR_COPY_SAME_CASE(S64);
            PSVECTOR_COPY_SAME_CASE(F32);
            PSVECTOR_COPY_SAME_CASE(F64);
            PSVECTOR_COPY_SAME_CASE(C32);
            PSVECTOR_COPY_SAME_CASE(C64);
        default: {
                char* typeStr;
                PS_TYPE_NAME(typeStr,type);
                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
                        //                        _("Input psVector is an unsupported type (0x%x)."), typeStr);
                        "Input psVector is an unsupported type.\n");
                psFree(output);
                return NULL;
            }
        }
        return output;
    }

    #define PSVECTOR_COPY(INTYPE,OUTTYPE) { \
        ps##INTYPE *inVec = input->data.INTYPE; \
        ps##OUTTYPE *outVec = output->data.OUTTYPE; \
        for (psS32 col=0;col<nElements;col++) { \
            *(outVec++) = *(inVec++); \
        } \
    }

    #define PSVECTOR_COPY_CASE(OUTTYPE) \
case PS_TYPE_##OUTTYPE: { \
        switch (input->type.type) { \
        case PS_TYPE_S8: \
            PSVECTOR_COPY(S8,OUTTYPE); \
            break; \
        case PS_TYPE_S16: \
            PSVECTOR_COPY(S16,OUTTYPE); \
            break; \
        case PS_TYPE_S32: \
            PSVECTOR_COPY(S32,OUTTYPE); \
            break; \
        case PS_TYPE_S64: \
            PSVECTOR_COPY(S64,OUTTYPE); \
            break; \
        case PS_TYPE_U8: \
            PSVECTOR_COPY(U8,OUTTYPE); \
            break; \
        case PS_TYPE_U16: \
            PSVECTOR_COPY(U16,OUTTYPE); \
            break; \
        case PS_TYPE_U32: \
            PSVECTOR_COPY(U32,OUTTYPE); \
            break; \
        case PS_TYPE_U64: \
            PSVECTOR_COPY(U64,OUTTYPE); \
            break; \
        case PS_TYPE_F32: \
            PSVECTOR_COPY(F32,OUTTYPE); \
            break; \
        case PS_TYPE_F64: \
            PSVECTOR_COPY(F64,OUTTYPE); \
            break; \
        case PS_TYPE_C32: \
            PSVECTOR_COPY(C32,OUTTYPE); \
            break; \
        case PS_TYPE_C64: \
            PSVECTOR_COPY(C64,OUTTYPE); \
            break; \
        default: { \
                char* typeStr; \
                PS_TYPE_NAME(typeStr,type); \
                psError(PS_ERR_BAD_PARAMETER_TYPE, true, \
                        _("Input psVector is an unsupported type (0x%s)."), \
                        typeStr); \
                psFree(output); \
            } \
        } \
        break; \
    }

    switch (type) {
        PSVECTOR_COPY_CASE(S8);
        PSVECTOR_COPY_CASE(S16);
        PSVECTOR_COPY_CASE(S32);
        PSVECTOR_COPY_CASE(S64);
        PSVECTOR_COPY_CASE(U8);
        PSVECTOR_COPY_CASE(U16);
        PSVECTOR_COPY_CASE(U32);
        PSVECTOR_COPY_CASE(U64);
        PSVECTOR_COPY_CASE(F32);
        PSVECTOR_COPY_CASE(F64);
        PSVECTOR_COPY_CASE(C32);
        PSVECTOR_COPY_CASE(C64);
    default: {
            char* typeStr;
            PS_TYPE_NAME(typeStr,type);
            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
                    _("Input psVector is an unsupported type (0x%s)."),
                    typeStr);
            psFree(output);
            output = NULL;
            break;
        }
    }
    return output;


}

// Heap sort of the array
// XXX since the key size is fixed (same number of bits) a Radix sort could be
// a big win here -- someone should benchmark this -JH
#define PSVECTOR_SORT_CASE(NAME) \
case PS_TYPE_##NAME: { \
    ps##NAME temp; \
    ps##NAME *value = outVector->data.NAME; \
    for (;;) { \
        if (l > 0) { \
            temp = value[--l]; \
        } else { \
            temp = value[ir]; \
            value[ir] = value[0]; \
            if (--ir == 0) { \
                value[0] = temp; \
                return outVector; \
            } \
        } \
        int i = l; \
        int j = (l << 1) + 1; \
        while (j <= ir) { \
            if (j < ir && value[j] < value[j+1]) \
                ++j; \
            if (temp < value[j]) { \
                value[i]=value[j]; \
                j += (i=j) + 1; \
            } else { \
                j = ir + 1; \
            } \
        } \
        value[i] = temp; \
    } \
} \
break;

psVector* psVectorSort(psVector* outVector,
                       const psVector* inVector)
{
    if (!inVector) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                _("psVectorSort can not sort a NULL psVector."));
        psFree(outVector);
        return NULL;
    }

    outVector = psVectorCopy(outVector, inVector, inVector->type.type);
    if (outVector == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                "Error in psVectorSort:  psVectorCopy returned NULL vector!\n");
        return NULL;
    }
    long N = outVector->n;                    // Number of elements
    if (N < 2) {
        return outVector;
    }
    long l = N >> 1;
    long ir = N - 1;
    switch (outVector->type.type) {
        PSVECTOR_SORT_CASE(U8);
        PSVECTOR_SORT_CASE(U16);
        PSVECTOR_SORT_CASE(U32);
        PSVECTOR_SORT_CASE(U64);
        PSVECTOR_SORT_CASE(S8);
        PSVECTOR_SORT_CASE(S16);
        PSVECTOR_SORT_CASE(S32);
        PSVECTOR_SORT_CASE(S64);
        PSVECTOR_SORT_CASE(F32);
        PSVECTOR_SORT_CASE(F64);
    default:
        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
                _("Input psVector is an unsupported type (0x%x)."),
                inVector->type.type);
        psFree(outVector);
        return NULL;
    }
    return outVector;
}

// Heap sort of the index array
#define PSVECTOR_SORT_INDEX_CASE(NAME) \
case PS_TYPE_##NAME: { \
    psS32 temp; \
    psS32 *index = out->data.S32; \
    ps##NAME *value = inVector->data.NAME; \
    for (;;) { \
        if (l > 0) { \
            temp = index[--l]; \
        } else { \
            temp = index[ir]; \
            index[ir] = index[0]; \
            if (--ir == 0) { \
                index[0] = temp; \
                return out; \
            } \
        } \
        long i = l; \
        long j = (l << 1) + 1; \
        while (j <= ir) { \
            if (j < ir && value[index[j]] < value[index[j+1]]) \
                ++j; \
            if (value[temp ] < value[index[j]]) { \
                index[i]=index[j]; \
                j += (i=j) + 1; \
            } else { \
                j = ir + 1; \
            } \
        } \
        index[i] = temp; \
    } \
} \
break;

psVector* psVectorSortIndex(psVector* outVector,
                            const psVector* inVector)
{
    if (inVector == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true,
                _("psVectorSort can not sort a NULL psVector."));
        psFree(outVector);
        return NULL;
    }

    psVector *out = psVectorRecycle(outVector, inVector->n, PS_TYPE_S32); // Vector for output
    out = psVectorCreate(out, 0, inVector->n, 1, PS_TYPE_S32);
    long N = out->n;                    // Number of elements
    if (N < 2) {
        return out;
    }
    long l = N >> 1;
    long ir = N - 1;
    switch (inVector->type.type) {
        PSVECTOR_SORT_INDEX_CASE(U8);
        PSVECTOR_SORT_INDEX_CASE(U16);
        PSVECTOR_SORT_INDEX_CASE(U32);
        PSVECTOR_SORT_INDEX_CASE(U64);
        PSVECTOR_SORT_INDEX_CASE(S8);
        PSVECTOR_SORT_INDEX_CASE(S16);
        PSVECTOR_SORT_INDEX_CASE(S32);
        PSVECTOR_SORT_INDEX_CASE(S64);
        PSVECTOR_SORT_INDEX_CASE(F32);
        PSVECTOR_SORT_INDEX_CASE(F64);
    default:
        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
                _("Input psVector is an unsupported type (0x%x)."),
                inVector->type.type);
        psFree(outVector);
        return NULL;
    }
    return out;
}

psString psVectorToString(const psVector* vector,
                          int maxLength)
{

    if (maxLength < 5) {
        return NULL;
    }

    psString str = psStringAlloc(maxLength + 1);

    if (vector == NULL) {
        snprintf(str,maxLength, "NULL");
        return str;
    }

    int size = vector->n;

    if (size == 0) {
        snprintf(str,maxLength, "[]");
        return str;
    }

    psString tempStr = psStringAlloc(maxLength+1);
    *str = '\0';
    bool full = false;

    #define APPEND_ELEMENTS_CASE(TYPE, NATIVE_TYPE, FORMAT) \
case PS_TYPE_##TYPE: \
    for (lcv=0; lcv < size && ! full; lcv++) { \
        snprintf(tempStr, maxLength, "%s" FORMAT, prefix, (NATIVE_TYPE) (vector->data.TYPE[lcv])); \
        strncat(str,tempStr,maxLength); \
        full = (strlen(str) > maxLength-2); \
        prefix = ","; \
    } \
    break;

    #define APPEND_ELEMENTS_CASE_COMPLEX(TYPE,CREAL,CIMAG) \
case PS_TYPE_##TYPE: \
    for (lcv=0; lcv < size && ! full; lcv++) { \
        snprintf(tempStr, maxLength, "%s%g%+gi", prefix, \
                 CREAL(vector->data.TYPE[lcv]), \
                 CIMAG(vector->data.TYPE[lcv])); \
        strncat(str,tempStr,maxLength); \
        full = (strlen(str) > maxLength-2); \
        prefix = ","; \
    } \
    break;

    int lcv;
    char* prefix = "[";
    switch(vector->type.type) {
        APPEND_ELEMENTS_CASE(S8,char,"%hd")
        APPEND_ELEMENTS_CASE(S16,short int,"%hd")
        APPEND_ELEMENTS_CASE(S32,int,"%d")
        APPEND_ELEMENTS_CASE(S64,long,"%ld")
        APPEND_ELEMENTS_CASE(U8,unsigned char,"%hu")
        APPEND_ELEMENTS_CASE(U16,unsigned short,"%hu")
        APPEND_ELEMENTS_CASE(U32,unsigned int, "%u")
        APPEND_ELEMENTS_CASE(U64,unsigned long,"%lu")
        APPEND_ELEMENTS_CASE(F32,double,"%g")
        APPEND_ELEMENTS_CASE(F64,double,"%g")
        APPEND_ELEMENTS_CASE_COMPLEX(C32,crealf,cimagf)
        APPEND_ELEMENTS_CASE_COMPLEX(C64,creal,cimag)
    default:
        snprintf(str,maxLength,"[...]");
        break;
    }

    if (full) {
        // couldn't all fit in given string length

        // remove elements until there is room for ",...]"
        while (strlen(str) > maxLength - 5) {
            char* lastComma = strrchr(str,',');
            if (lastComma == NULL) { // no comma, must be first number
                str[1] = '\0';
            } else {
                *lastComma = '\0';
            }
        }
        strncat(str,",...]",maxLength);
    } else {
        strncat(str,"]",maxLength);
    }

    psFree(tempStr);

    return str;
}

psF64 p_psVectorGetElementF64(const psVector* vector,
                              int position)
{
    if (vector == NULL) {
        return NAN;
    }
    if (position < 0 || position >= vector->n) {
        return NAN;
    }

    switch (vector->type.type) {
    case PS_TYPE_U8:
        return vector->data.U8[position];
        break;
    case PS_TYPE_U16:
        return vector->data.U16[position];
        break;
    case PS_TYPE_U32:
        return vector->data.U32[position];
        break;
    case PS_TYPE_U64:
        return vector->data.U64[position];
        break;
    case PS_TYPE_S8:
        return vector->data.S8[position];
        break;
    case PS_TYPE_S16:
        return vector->data.S16[position];
        break;
    case PS_TYPE_S32:
        return vector->data.S32[position];
        break;
    case PS_TYPE_S64:
        return vector->data.S64[position];
        break;
    case PS_TYPE_F32:
        return vector->data.F32[position];
        break;
    case PS_TYPE_F64:
        return vector->data.F64[position];
    default:
        return NAN;
    }
}


bool p_psVectorPrint (int fd,
                      const psVector *a,
                      char *name)
{
    char line[1024];

    sprintf (line, "vector: %s\n", name);
    write (fd, line, strlen(line));

    for (int i = 0; i < a[0].n; i++) {
        sprintf (line, "%f\n", p_psVectorGetElementF64(a, i));
        write (fd, line, strlen(line));
    }
    sprintf (line, "\n");
    write (fd, line, strlen(line));
    return (true);
}


bool psVectorInit(psVector *vector,
                  ...)
{
    va_list argp;
    psU8  vU8;
    psU16 vU16;
    psU32 vU32;
    psU64 vU64;
    psS8 vS8;
    psS16 vS16;
    psS32 vS32;
    psS64 vS64;
    psF32 vF32;
    psF64 vF64;
    psC32 vC32;
    psC64 vC64;

    unsigned int temp;
    int temp2;

    if (vector == NULL)
        return (false);

    va_start (argp, vector);

    switch (vector->type.type) {
    case PS_TYPE_BOOL: {
            // This is a little ugly, because psVector doesn't have a boolean type yet.
            int temp = va_arg(argp, int);
            bool value = temp;
            if (!value) {
                memset(vector->data.U8, 0, vector->n * sizeof(bool));
            } else {
                for (long i = 0; i < vector->n; i++) {
                    vector->data.U8[i] = value;
                }
            }
            return true;
        }
    case PS_TYPE_U8:
        temp = va_arg (argp, psU32);
        if ( temp >= PS_MIN_U8 && temp <= PS_MAX_U8 ) {
            vU8 = temp;
        } else {
            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error:  U8 Value out of Range.\n");
            return false;
        }
        // Can use memset for char-size values
        memset(vector->data.U8, vU8, vector->n * sizeof(psU8));
        return (true);
    case PS_TYPE_F32:
        vF32 = (psF32)va_arg (argp, psF64);
        if (vF32 == 0.0) {
            memset(vector->data.F32, 0, vector->n * sizeof(psF32));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.F32[iy] = vF32;
            }
        }
        return (true);
    case PS_TYPE_F64:
        vF64 = va_arg (argp, psF64);
        if (vF64 == 0.0) {
            memset(vector->data.F64, 0, vector->n * sizeof(psF64));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.F64[iy] = vF64;
            }
        }
        return (true);
    case PS_TYPE_U16:
        temp = va_arg (argp, psU32);
        if ( temp >= PS_MIN_U16 && temp <= PS_MAX_U16 )
            vU16 = temp;
        else {
            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error:  U16 Value out of Range.\n");
            return false;
        }
        if (vU16 == 0) {
            memset(vector->data.U16, 0, vector->n * sizeof(psU16));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.U16[iy] = vU16;
            }
        }
        return (true);
    case PS_TYPE_U32:
        vU32 = va_arg (argp, psU32);
        if (vU32 == 0) {
            memset(vector->data.U32, 0, vector->n * sizeof(psU32));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.U32[iy] = vU32;
            }
        }
        return (true);
    case PS_TYPE_U64:
        vU64 = va_arg (argp, psU64);
        if (vU64 == 0) {
            memset(vector->data.U64, 0, vector->n * sizeof(psU64));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.U64[iy] = vU64;
            }
        }
        return (true);
    case PS_TYPE_S8:
        temp2 = va_arg (argp, psS32);
        if ( temp2 >= PS_MIN_S8 && temp2 <= PS_MAX_S8 )
            vS8 = temp2;
        else {
            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error:  S8 Value out of Range.\n");
            return false;
        }
        // Can use memset for char-size values
        memset(vector->data.S8, vS8, vector->n * sizeof(psS8));
        return (true);
    case PS_TYPE_S16:
        temp2 = va_arg (argp, psS32);
        if ( temp2 >= PS_MIN_S16 && temp2 <= PS_MAX_S16 )
            vS16 = temp2;
        else {
            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Error:  S16 Value out of Range.\n");
            return false;
        }
        if (vS16 == 0) {
            memset(vector->data.S16, 0, vector->n * sizeof(psS16));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.S16[iy] = vS16;
            }
        }
        return (true);
    case PS_TYPE_S32:
        vS32 = va_arg (argp, psS32);
        if (vS32 == 0) {
            memset(vector->data.S32, 0, vector->n * sizeof(psS32));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.S32[iy] = vS32;
            }
        }
        return (true);
    case PS_TYPE_S64:
        vS64 = va_arg (argp, psS64);
        if (vS64 == 0) {
            memset(vector->data.S64, 0, vector->n * sizeof(psS64));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.S64[iy] = vS64;
            }
        }
        return (true);
    case PS_TYPE_C32:
        vC32 = va_arg (argp, psC32);
        if (vC32 == 0) {
            memset(vector->data.C32, 0, vector->n * sizeof(psC32));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.C32[iy] = vC32;
            }
        }
        return (true);
    case PS_TYPE_C64:
        vC64 = va_arg (argp, psC64);
        if (vC64 == 0) {
            memset(vector->data.C64, 0, vector->n * sizeof(psC64));
        } else {
            for (long iy = 0; iy < vector->n; iy++) {
                vector->data.C64[iy] = vC64;
            }
        }
        return (true);
    default:
        psError (PS_ERR_BAD_PARAMETER_TYPE, true, "datatype not defined in psImageInit\n");
        return (false);
    }
    return (false);
}

#define FUNC_MACRO_VECTOR_CREATE(TYPE) \
static psVector *vectorCreate##TYPE(const char *file, unsigned int lineno, const char *func, psVector *input, double lower, double upper, double delta) \
{ \
    \
    int nBin = (upper - lower) / delta; \
    psVector *vec = p_psVectorRecycle(file, lineno, func, input, nBin, PS_TYPE_##TYPE); \
    vec->n = nBin; \
    for (int i = 0; i < nBin; i++) \
    { \
        vec->data.TYPE[i] = lower + (i * delta); \
    } \
    return vec; \
} \

FUNC_MACRO_VECTOR_CREATE(S8)
FUNC_MACRO_VECTOR_CREATE(S16)
FUNC_MACRO_VECTOR_CREATE(S32)
FUNC_MACRO_VECTOR_CREATE(S64)
FUNC_MACRO_VECTOR_CREATE(U8)
FUNC_MACRO_VECTOR_CREATE(U16)
FUNC_MACRO_VECTOR_CREATE(U32)
FUNC_MACRO_VECTOR_CREATE(U64)
FUNC_MACRO_VECTOR_CREATE(F32)
FUNC_MACRO_VECTOR_CREATE(F64)

psVector *p_psVectorCreate(const char *file,
                           unsigned int lineno,
                           const char *func,
                           psVector *input,
                           double lower,
                           double upper,
                           double delta,
                           psElemType type)
{
    psVector *out = NULL;
    switch (type) {
    case PS_TYPE_S8:
        out = vectorCreateS8(file, lineno, func, input, lower,  upper, delta);
        break;
    case PS_TYPE_S16:
        out = vectorCreateS16(file, lineno, func, input, lower, upper, delta);
        break;
    case PS_TYPE_S32:
        out = vectorCreateS32(file, lineno, func, input, lower, upper, delta);
        break;
    case PS_TYPE_S64:
        out = vectorCreateS64(file, lineno, func, input, lower, upper, delta);
        break;
    case PS_TYPE_U8:
        out = vectorCreateU8(file, lineno, func, input, lower, upper, delta);
        break;
    case PS_TYPE_U16:
        out = vectorCreateU16(file, lineno, func, input, lower, upper, delta);
        break;
    case PS_TYPE_U32:
        out = vectorCreateU32(file, lineno, func, input, lower, upper, delta);
        break;
    case PS_TYPE_U64:
        out = vectorCreateU64(file, lineno, func, input, lower, upper, delta);
        break;
    case PS_TYPE_F32:
        out = vectorCreateF32(file, lineno, func, input, lower, upper, delta);
        break;
    case PS_TYPE_F64:
        out = vectorCreateF64(file, lineno, func, input, lower, upper, delta);
        break;
    default:
        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Invalid psType for Vector Create\n");
    }
    return (out);
}

bool psVectorSet(psVector *input,
                 long position,
                 double complex value)
{
    if (input == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psVector can not be NULL."));
        return false;
    }
    if (position > input->n) {
        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Number too large\n");
        return false;
    }
    if (position < 0)
        position += input->n;
    if (position < 0) {
        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Negative number too large\n");
        return false;
    }

    if (position == input->n) {
        if (position >= input->nalloc) {
            psError(PS_ERR_BAD_PARAMETER_NULL, true,
                    "Specified position, %ld, is greater than n+1 of the vector, %ld.",
                    position, input->nalloc);
            return false;
        }
        (*(psVector**)&input)->n++;
    }

    switch (input->type.type) {
    case PS_TYPE_U8:
        input->data.U8[position] = (psU8)value;
        break;
    case PS_TYPE_U16:
        input->data.U16[position] = (psU16)value;
        break;
    case PS_TYPE_U32:
        input->data.U32[position] = (psU32)value;
        break;
    case PS_TYPE_U64:
        input->data.U64[position] = (psU64)value;
        break;
    case PS_TYPE_S8:
        input->data.S8[position] = (psS8)value;
        break;
    case PS_TYPE_S16:
        input->data.S16[position] = (psS16)value;
        break;
    case PS_TYPE_S32:
        input->data.S32[position] = (psS32)value;
        break;
    case PS_TYPE_S64:
        input->data.S64[position] = (psS64)value;
        break;
    case PS_TYPE_F32:
        input->data.F32[position] = (psF32)value;
        break;
    case PS_TYPE_F64:
        input->data.F64[position] = (psF64)value;
        break;
    case PS_TYPE_C32:
        input->data.C32[position] = (psC32)value;
        break;
    case PS_TYPE_C64:
        input->data.C64[position] = (psC64)value;
        break;
    default:
        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Invalid psVector Data Type\n");
        return false;
    }

    return true;
}

double complex psVectorGet(const psVector *input,
                           long position)
{
    if (input == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psVector can not be NULL."));
        return NAN;
    }
    if (position >= input->n) {
        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Number too large\n");
        return NAN;
    }
    if(position < 0)
        position += input->n;
    if (position < 0) {
        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Invalid position.  Negative number too large\n");
        return NAN;
    }
    switch (input->type.type) {
    case PS_TYPE_U8:
        return input->data.U8[position];
        break;
    case PS_TYPE_U16:
        return input->data.U16[position];
        break;
    case PS_TYPE_U32:
        return input->data.U32[position];
        break;
    case PS_TYPE_U64:
        return input->data.U64[position];
        break;
    case PS_TYPE_S8:
        return input->data.S8[position];
        break;
    case PS_TYPE_S16:
        return input->data.S16[position];
        break;
    case PS_TYPE_S32:
        return input->data.S32[position];
        break;
    case PS_TYPE_S64:
        return input->data.S64[position];
        break;
    case PS_TYPE_F32:
        return input->data.F32[position];
        break;
    case PS_TYPE_F64:
        return input->data.F64[position];
        break;
    case PS_TYPE_C32:
        return input->data.C32[position];
        break;
    case PS_TYPE_C64:
        return input->data.C64[position];
        break;
    default:
        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Invalid psVector Data Type\n");
        return NAN;
    }

}

// count number of pixels with given mask value
long psVectorCountPixelMask (psVector *mask,
                             psMaskType value)
{
    long Npixels = 0;
    if (mask == NULL) {
        psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psVector can not be NULL."));
        Npixels = -1;
        return Npixels;
    }

    psElemType type;
    type = mask->type.type;

    if (type != PS_TYPE_MASK) {
        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
                "psVector type does not match the specified psMaskType!\n");
        return -1;
    }

    switch (type) {
    case PS_TYPE_U8:
    case PS_TYPE_U16:
        for (long i = 0; i < mask->n; i++) {
            if (mask->data.PS_TYPE_MASK_DATA[i] & value) {
                Npixels ++;
            }
        }
        break;
    case PS_TYPE_S8:
    case PS_TYPE_S16:
    case PS_TYPE_S32:
    case PS_TYPE_S64:
    case PS_TYPE_U32:
    case PS_TYPE_U64:
    case PS_TYPE_F32:
    case PS_TYPE_F64:
    case PS_TYPE_C32:
    case PS_TYPE_C64:
    default:
        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
                _("Input psVector is an unsupported type (0x%x)."), type);
        return -1;
    }
    return (Npixels);
}

long psVectorLength(const psVector *vector)
{
    if ( !psMemCheckVector((psVector*)vector) ) {
        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
                "Error:  Specified vector is not a valid psVector \n");
        return -1;
    }
    return (vector->n);
}


