Index: trunk/psLib/src/fileUtils/psLookupTable.c
===================================================================
--- trunk/psLib/src/fileUtils/psLookupTable.c	(revision 2195)
+++ trunk/psLib/src/fileUtils/psLookupTable.c	(revision 2302)
@@ -8,28 +8,654 @@
 *  @author Ross Harman, MHPCC
 *
-*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2004-10-26 00:36:50 $
+*  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2004-11-09 00:38:14 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 */
-
-#include "psLookupTable.c"
-
-
-psLookupTable* psLookupTableAlloc(psU64 numRows, psU64 numCols)
-{
-}
-
-psLookupTable* psLookupTableRead(char *fileName)
-{
-}
-
-psF64 psLookupTableInterpolate(psLookupTable *table, psF64 index, psMaskType *status)
-{
-}
-
-psVector* psLookupTableInterpolate(psLookupTable *table, psF64 index, psMaskType *status)
-{
-}
-
-#endif
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "psMemory.h"
+#include "psString.h"
+#include "psError.h"
+#include "psLookupTable.h"
+#include "psFileUtilsErrors.h"
+#include "psConstants.h"
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH 256
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
+
+static bool ignoreLine(char *inString);
+static char *cleanString(char *inString, int sLen);
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status);
+static psU8 parseU8(char *inString, psParseErrorType *status);
+static psS8 parseS8(char *inString, psParseErrorType *status);
+static psU16 parseU16(char *inString, psParseErrorType *status);
+static psS16 parseS16(char *inString, psParseErrorType *status);
+static psU32 parseU32(char *inString, psParseErrorType *status);
+static psS32 parseS32(char *inString, psParseErrorType *status);
+static psU64 parseU64(char *inString, psParseErrorType *status);
+static psS64 parseS64(char *inString, psParseErrorType *status);
+static psF32 parseF32(char *inString, psParseErrorType *status);
+static psF64 parseF64(char *inString, psParseErrorType *status);
+static void parseValue(psVector *vec, psU64 index, char* strValue, psParseErrorType *status);
+static void lookupTableFree(psLookupTable* table);
+
+/** Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+ *  must be null terminated. */
+static bool ignoreLine(char *inString)
+{
+    while(*inString!='\0' && *inString!='#') {
+        if(!isspace(*inString)) {
+            return false;
+        }
+        inString++;
+    }
+
+    return true;
+}
+
+
+/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+ *  terminated copy of the original input string. */
+static char *cleanString(char *inString, int sLen)
+{
+    char *ptrB = NULL;
+    char *ptrE = NULL;
+    char *cleaned = NULL;
+
+
+    ptrB = inString;
+
+    /* Skip over leading # or whitespace */
+    while (isspace(*ptrB) || *ptrB=='#') {
+        ptrB++;
+    }
+
+    /* Skip over trailing whitespace, null terminators, and # characters */
+    ptrE = inString + sLen;
+    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+        ptrE--;
+    }
+
+    // Length, sLen, does not include '\0'
+    sLen = ptrE - ptrB + 1;
+
+    // Adds '\0' to end of string and +1 to sLen
+    cleaned = psStringNCopy(ptrB, sLen);
+
+    return cleaned;
+}
+
+
+/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+ * the beginning of the string. Tokens are newly allocated null terminated strings. */
+static char* getToken(char **inString, char *delimiter, psParseErrorType *status)
+{
+    char *cleanToken = NULL;
+    int sLen = 0;
+
+
+    // Skip over leading whitespace
+    while(isspace(**inString)) {
+        (*inString)++;
+    }
+
+    // Length of token, not including delimiter
+    sLen = strcspn(*inString, delimiter);
+    if(sLen) {
+
+        // Create new, cleaned, and null terminated token
+        cleanToken = cleanString(*inString, sLen);
+
+        // Move to end of token
+        (*inString) += sLen;
+    } else if(**inString!='\0' && sLen==0) {
+        *status = PS_PARSE_ERROR_GENERAL;
+    }
+
+    return cleanToken;
+}
+
+/** Returns single parsed value as a psU8. The input string must be cleaned and null terminated. */
+static psU8 parseU8(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU8 value = 0.0;
+
+
+    value = (psU8)strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS8. The input string must be cleaned and null terminated. */
+static psS8 parseS8(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS8 value = 0.0;
+
+
+    value = (psS8)strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psU16. The input string must be cleaned and null terminated. */
+static psU16 parseU16(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU16 value = 0.0;
+
+
+    value = (psU16)strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS16. The input string must be cleaned and null terminated. */
+static psS16 parseS16(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS16 value = 0.0;
+
+
+    value = (psS16)strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psU32. The input string must be cleaned and null terminated. */
+static psU32 parseU32(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU32 value = 0.0;
+
+
+    value = (psU32)strtoul(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS32. The input string must be cleaned and null terminated. */
+static psS32 parseS32(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS32 value = 0.0;
+
+
+    value = (psS32)strtol(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psU64. The input string must be cleaned and null terminated. */
+static psU64 parseU64(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psU64 value = 0.0;
+
+
+    value = (psU64)strtoull(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psS64. The input string must be cleaned and null terminated. */
+static psS64 parseS64(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psS64 value = 0.0;
+
+
+    value = (psS64)strtoll(inString, &end, 0);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psF32. The input string must be cleaned and null terminated. */
+static psF32 parseF32(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psF32 value = 0.0;
+
+
+    value = (psF32)strtof(inString, &end);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a psF64. The input string must be cleaned and null terminated. */
+static psF64 parseF64(char *inString, psParseErrorType *status)
+{
+    char *end = NULL;
+    psF64 value = 0.0;
+
+
+    value = (psF64)strtod(inString, &end);
+    if(*end != '\0') {
+        *status = PS_PARSE_ERROR_VALUE;
+    } else if(inString==end) {
+        *status = PS_PARSE_ERROR_VALUE;
+    }
+
+    return value;
+}
+
+/** Returns single parsed value as a double precision number. The input string must be cleaned and null
+ * terminated. */
+static void parseValue(psVector *vec, psU64 index, char* strValue, psParseErrorType *status)
+{
+    psElemType type;
+
+
+    if(vec == NULL) {
+        *status = 1;
+        return;
+    }
+
+    type = vec->type.type;
+
+    switch(type) {
+    case PS_TYPE_U8:
+        vec->data.U8[index] = parseU8(strValue, status);
+        break;
+    case PS_TYPE_S8:
+        vec->data.S8[index] = parseS8(strValue, status);
+        break;
+    case PS_TYPE_U16:
+        vec->data.U16[index] = parseU16(strValue, status);
+        break;
+    case PS_TYPE_S16:
+        vec->data.S16[index] = parseS16(strValue, status);
+        break;
+    case PS_TYPE_U32:
+        vec->data.U32[index] = parseU32(strValue, status);
+        break;
+    case PS_TYPE_S32:
+        vec->data.S32[index] = parseS32(strValue, status);
+        break;
+    case PS_TYPE_U64:
+        vec->data.U64[index] = parseU64(strValue, status);
+        break;
+    case PS_TYPE_S64:
+        vec->data.S64[index] = parseS64(strValue, status);
+        break;
+    case PS_TYPE_F32:
+        vec->data.F32[index] = parseF32(strValue, status);
+        break;
+    case PS_TYPE_F64:
+        vec->data.F64[index] = parseF64(strValue, status);
+        break;
+    default:
+        *status = PS_PARSE_ERROR_TYPE;
+    }
+
+    return;
+}
+
+static psParseErrorType printError(psU64 lineCount, char* badText, psParseErrorType status)
+{
+    switch(status) {
+    case PS_PARSE_ERROR_VALUE:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_PARSE_VALUE, badText, lineCount);
+        break;
+    case PS_PARSE_ERROR_TYPE:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_PARSE_TYPE, badText, lineCount);
+        break;
+    case PS_PARSE_ERROR_GENERAL:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_PARSE_GENERAL, badText, lineCount);
+        break;
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_INVALID_TYPE, badText, lineCount);
+    }
+
+    return PS_LOOKUP_SUCCESS;
+}
+
+static void lookupTableFree(psLookupTable* table)
+{
+    if (table == NULL) {
+        return;
+    }
+
+    psFree(table->values);
+    psFree(table->index);
+    psFree((char*)table->fileName);
+}
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+psLookupTable* psLookupTableAlloc(const char *fileName, psF64 validFrom, psF64 validTo)
+{
+    psLookupTable *outTable = NULL;
+
+
+    // Can't read table if you don't know its name
+    PS_PTR_CHECK_NULL(fileName,NULL);
+
+    // Allocate lookup table
+    outTable = (psLookupTable*)psAlloc(sizeof(psLookupTable));
+
+    // Set deallocator
+    p_psMemSetDeallocator(outTable, (psFreeFcn)lookupTableFree);
+
+    // Allocate and set metadata item comment
+    outTable->fileName = psStringCopy(fileName);
+
+    // Number of table rows and columns. Automatically resized by table read.
+    outTable->numRows = 0;
+    outTable->numCols = 0;
+
+    // Valid ranges. Automatically set by table read if both zero.
+    outTable->validFrom = validFrom;
+    outTable->validTo = validTo;
+
+    // Vector of independent index values. Filled by table read.
+    outTable->index = NULL;
+
+    // Array of dependent table values corresponding to index values. Filled by table read.
+    outTable->values = NULL;
+
+    return outTable;
+}
+
+psLookupTable* psLookupTableRead(psLookupTable *table)
+{
+    bool typeLine = true;
+    char *line = NULL;
+    char *strType = NULL;
+    char *strValue = NULL;
+    char *linePtr = NULL;
+    psParseErrorType status = PS_PARSE_SUCCESS;
+    psU64 lineCount = 0;
+    psU64 numRows = 0;
+    psU64 numCols = 0;
+    psU64 failedLines = 0;
+    FILE *fp = NULL;
+    psElemType elemType;
+    psVector *indexVec = NULL;
+    psVector *valuesVec = NULL;
+    psArray *values = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(table,NULL);
+    PS_PTR_CHECK_NULL(table->fileName,NULL);
+    if((fp=fopen(table->fileName, "r")) == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_FILE_NOT_FOUND, table->fileName);
+        return NULL;
+    }
+
+    indexVec = table->index;
+    values = table->values;
+
+    // Create reusable line for continuous read
+    line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
+
+    // Loop through file to get numRows, numCols, and column data types
+    while(fgets(line, MAX_STRING_LENGTH, fp) != NULL) {
+
+        // Initialize variables for new line
+        linePtr = line;
+        lineCount++;
+
+        // If line is not a comment or blank, then extract data
+        if(!ignoreLine(linePtr)) {
+
+            if(typeLine == true) {
+
+                // Determine column types from first line in data file after comments
+                while((strType=getToken(&linePtr, " ", &status)) != NULL) {
+                    numCols++;
+                    typeLine = false;
+                    if(!strncmp(strType, "psU8", 4)) {
+                        elemType = PS_TYPE_U8;
+                    } else if(!strncmp(strType, "psS8", 4)) {
+                        elemType = PS_TYPE_S8;
+                    } else if(!strncmp(strType, "psU16", 5)) {
+                        elemType = PS_TYPE_U16;
+                    } else if(!strncmp(strType, "psS16", 5)) {
+                        elemType = PS_TYPE_S16;
+                    } else if(!strncmp(strType, "psU32", 5)) {
+                        elemType = PS_TYPE_U32;
+                    } else if(!strncmp(strType, "psS32", 5)) {
+                        elemType = PS_TYPE_S32;
+                    } else if(!strncmp(strType, "psU64", 5)) {
+                        elemType = PS_TYPE_U64;
+                    } else if(!strncmp(strType, "psS64", 5)) {
+                        elemType = PS_TYPE_S64;
+                    } else if(!strncmp(strType, "psF32", 5)) {
+                        elemType = PS_TYPE_F32;
+                    } else if(!strncmp(strType, "psF64", 5)) {
+                        elemType = PS_TYPE_F64;
+                    } else {
+                        status = PS_PARSE_ERROR_TYPE;
+                    }
+
+                    // Realloc number of columns as you go
+                    if(numCols == 1) {
+                        indexVec = psVectorAlloc(0, elemType);
+                    } else {
+                        values = psArrayRealloc(values, numCols-1);
+                        values->n = values->nalloc;
+                        values->data[numCols-2] = psVectorAlloc(0, elemType);
+                    }
+
+                    if(status) {
+                        status = printError(lineCount, strValue, status);
+                        failedLines++;
+                    }
+                    psFree(strType);
+                }
+            } else {
+
+                // Parse and add values to all columns
+                numRows++;
+                numCols = 0;
+                while((strValue=getToken(&linePtr, " ", &status)) != NULL) {
+                    numCols++;
+
+                    // Realloc number of rows as you go
+                    if(numCols == 1) {
+                        indexVec = psVectorRecycle(indexVec, numRows, indexVec->type.type);
+                        parseValue(indexVec, numRows-1, strValue, &status);
+                    } else {
+                        valuesVec = values->data[numCols-2];
+                        valuesVec = psVectorRecycle(valuesVec, numRows, valuesVec->type.type);
+                        parseValue(valuesVec, numRows-1, strValue, &status);
+                    }
+
+                    if(status) {
+                        status = printError(lineCount, strValue, status);
+                        failedLines++;
+                    }
+                    psFree(strValue);
+                } // end while
+            } // end else
+        } // if ignoreLine
+    } // end while
+
+    psFree(line);
+
+    // Set table for return
+    table->numRows = numRows;
+    table->numCols = numCols-1;
+    table->index = indexVec;
+    table->values = values;
+
+    fclose(fp);
+
+    return table;
+}
+
+psF64 psLookupTableInterpolate(psLookupTable *table, psF64 index, psU64 column, psLookupStatusType *status)
+{
+    psU64 hiIdx = 0;
+    psU64 loIdx = 0;
+    psU64 numRows = 0;
+    psU64 numCols = 0;
+    psF64 out = 0.0;
+    psF64 denom = 0.0;
+    psVector *indexVec = NULL;
+    psVector *valuesVec = NULL;
+    psArray *values = NULL;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(table,0.0);
+    indexVec = table->index;
+    values = table->values;
+    numRows = table->numRows;
+    numCols = table->numCols;
+    PS_PTR_CHECK_NULL(indexVec,0.0);
+    PS_PTR_CHECK_NULL(values,0.0);
+    PS_INT_CHECK_EQUALS(numRows, 0,0.0);
+    PS_INT_CHECK_EQUALS(numCols, 0,0.0);
+    PS_INT_CHECK_RANGE(column, 0, numCols-1, 0.0);
+
+
+    valuesVec = (psVector*)values->data[column];
+    PS_PTR_CHECK_NULL(indexVec,0.0);
+
+    if(index<indexVec->data.F64[0] || index<table->validFrom) {
+
+        // Index value past top of table
+        *status = PS_LOOKUP_PAST_TOP;
+        return valuesVec->data.F64[0];
+    } else if(index>indexVec->data.F64[numRows-1] || index>table->validTo) {
+
+        // Index value past bottom of table
+        *status = PS_LOOKUP_PAST_BOTTOM;
+        return valuesVec->data.F64[numRows-1];
+    } else {
+
+        // Interpolation
+        while(index > indexVec->data.F64[hiIdx]) {
+            hiIdx++;
+            if(hiIdx >= numRows) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_INTERPOLATE_HIGH, hiIdx);
+                return 0.0;
+            }
+        }
+
+        loIdx = hiIdx--;
+        if(loIdx < 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_INTERPOLATE_LOW, loIdx);
+            return 0.0;
+        }
+
+        denom = indexVec->data.F64[hiIdx] - indexVec->data.F64[loIdx];
+        if(fabs(denom) < FLT_EPSILON) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psLookupTable_DIVIDE_BY_ZERO);
+            return 0.0;
+        } else {
+            out = valuesVec->data.F64[loIdx]+(valuesVec->data.F64[hiIdx]-valuesVec->data.F64[loIdx])
+                  *(index-indexVec->data.F64[loIdx])/denom;
+        }
+    }
+
+    return out;
+}
+
+psVector* psLookupTableInterpolateAll(psLookupTable *table, psF64 index, psVector *stats)
+{
+    psU64 i = 0;
+    psU64 numCols = 0;
+    psVector *outVector = NULL;
+    psLookupStatusType status = PS_LOOKUP_SUCCESS;
+
+
+    // Error checks
+    PS_PTR_CHECK_NULL(table,NULL);
+    PS_PTR_CHECK_NULL(stats,NULL);
+    numCols = table->numCols;
+    PS_INT_CHECK_EQUALS(numCols, 0,NULL);
+
+    outVector = psVectorAlloc(numCols, PS_TYPE_F64);
+
+    // Fill vectors with results and status of results
+    for(i=0; i<numCols; i++) {
+        outVector->data.F64[i] = psLookupTableInterpolate(table, index, i, &status);
+        stats->data.U32[i] = status;
+    }
+
+    return outVector;
+}
+
