/** @file tst_pmNonLinear.c
 *
 *  @brief Contains the tests for pmNonLinear.c:
 *
 * test00: This code will create a simple polynomial, and call
 * pmNonLinearityPolynomial() for a variety of image sizes [(1, 1), (1,
 * N), (N, 1), (N, N)].  
 *
 * test01: This code will create simple table lookup vectors, and call
 * pmNonLinearityPolynomial() for a variety of image sizes [(1, 1), (1,
 * N), (N, 1), (N, N)].  
 *
 * test02, test03: This code tests the functions with various unallowable
 * input parameters (NULLS) and incorrect vector sizes.
 *
 *  @author GLG, MHPCC
 *
 *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2005-09-28 20:42:52 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 */

#include "psTest.h"
#include "pslib.h"
#include "pmNonLinear.h"
static int test00(void);
static int test01(void);
static int test02(void);
static int test03(void);
testDescription tests[] = {
                              {test00, 000, "pmNonLinearityPolynomial", true, false},
                              {test01, 000, "pmNonLinearityLookup", true, false},
                              {test02, 000, "pmNonLinearityPolynomial(): error/warning conditions", true, false},
                              {test03, 000, "pmNonLinearityLookup(): error/warning conditions", true, false},
                              {NULL}
                          };


int main(int argc, char* argv[])
{
    psLogSetFormat("HLNM");
    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
}

#define NUM_ROWS 8
#define NUM_COLS 8
int doNonLinearityPolynomialTest(int numCols, int numRows)
{
    int i;
    int j;
    float actual;
    float expect;
    int testStatus = true;
    psImage *myImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    pmReadout *myReadout = pmReadoutAlloc(NULL);
    myReadout->image = myImage;
    psPolynomial1D *myPoly = psPolynomial1DAlloc(2, PS_POLYNOMIAL_ORD);
    myPoly->coeff[1] = 1.0;

    printPositiveTestHeader(stdout, "pmNonLinear", "doNonLinearityPolynomialTest");

    for (i=0;i<numRows;i++) {
        for (j=0;j<numCols;j++) {
            myReadout->image->data.F32[i][j] = (float) (i + j);
        }
    }

    myReadout = pmNonLinearityPolynomial(myReadout, myPoly);
    for (i=0;i<numRows;i++) {
        for (j=0;j<numCols;j++) {
            expect = psPolynomial1DEval(myPoly, (float) (i + j));
            actual = myReadout->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }


    psFree(myReadout);
    psFree(myPoly);
    printFooter(stdout, "pmNonLinear", "doNonLinearityPolynomialTest", true);
    return(testStatus);
}

int test00( void )
{
    int testStatus = 0;

    testStatus |= doNonLinearityPolynomialTest(1, 1);
    testStatus |= doNonLinearityPolynomialTest(NUM_COLS, 1);
    testStatus |= doNonLinearityPolynomialTest(1, NUM_ROWS);
    testStatus |= doNonLinearityPolynomialTest(NUM_COLS, NUM_ROWS);

    return(testStatus);
}

int doNonLinearityLookupTest(int numCols, int numRows)
{
    int i;
    int j;
    float actual;
    float expect;
    int testStatus = true;
    int tableSize = PS_MAX(numCols, numRows)*2;
    psImage *myImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    pmReadout *myReadout = pmReadoutAlloc(NULL);
    myReadout->image = myImage;
    psVector *in = psVectorAlloc(tableSize, PS_TYPE_F32);
    psVector *out = psVectorAlloc(tableSize, PS_TYPE_F32);

    printPositiveTestHeader(stdout, "pmNonLinear", "doNonLinearityLookupTest");
    for (i=0;i<numRows;i++) {
        for (j=0;j<numCols;j++) {
            myReadout->image->data.F32[i][j] = (float) (i + j);
        }
    }

    for (i=0;i<tableSize;i++) {
        in->data.F32[i] = (float) i;
        out->data.F32[i] = (float) (2 * i);
    }

    myReadout = pmNonLinearityLookup(myReadout, in, out);
    for (i=0;i<numRows;i++) {
        for (j=0;j<numCols;j++) {
            expect = (float) (2 * (i + j));
            actual = myReadout->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }


    psFree(myReadout);
    psFree(in);
    psFree(out);
    printFooter(stdout, "pmNonLinear", "doNonLinearityLookupTest", true);
    return(testStatus);
}

int test01( void )
{
    int testStatus = 0;

    testStatus |= doNonLinearityLookupTest(1, 1);
    testStatus |= doNonLinearityLookupTest(NUM_COLS, 1);
    testStatus |= doNonLinearityLookupTest(1, NUM_ROWS);
    testStatus |= doNonLinearityLookupTest (NUM_COLS, NUM_ROWS);

    return(testStatus);
}

int test02( void )
{
    int i;
    int j;
    int testStatus = true;
    psImage *myImage = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
    pmReadout *myReadout = pmReadoutAlloc(NULL);
    pmReadout *rc = NULL;
    myReadout->image = myImage;
    psPolynomial1D *myPoly = psPolynomial1DAlloc(2, PS_POLYNOMIAL_ORD);
    myPoly->coeff[1] = 1.0;

    printPositiveTestHeader(stdout, "pmNonLinear", "Testing bad input parameter conditions.");
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            myReadout->image->data.F32[i][j] = (float) (i + j);
        }
    }

    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityPolynomial() with NULL input readout.  Should generate error, return NULL.\n");
    rc = pmNonLinearityPolynomial(NULL, myPoly);
    if (rc != NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
        testStatus = false;
    }

    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityPolynomial() with NULL input readout->image.  Should generate error, return NULL.\n");
    psImage *tmpImage = myReadout->image;
    myReadout->image = NULL;
    rc = pmNonLinearityPolynomial(myReadout, myPoly);
    if (rc != NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
        testStatus = false;
    }
    myReadout->image = tmpImage;

    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityPolynomial() with NULL polynomial.  Should generate error, return NULL.\n");
    rc = pmNonLinearityPolynomial(myReadout, NULL);
    if (rc != NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
        testStatus = false;
    }

    psFree(myReadout);
    psFree(myPoly);
    return(testStatus);
}


int test03Init(pmReadout *myReadout)
{
    for (psS32 i=0;i<NUM_ROWS;i++) {
        for (psS32 j=0;j<NUM_COLS;j++) {
            myReadout->image->data.F32[i][j] = (float) (i + j);
        }
    }
    return(0);
}

int test03()
{
    int i;
    int j;
    int testStatus = true;
    int tableSize = PS_MAX(NUM_COLS, NUM_ROWS)*3;
    psImage *myImage = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
    pmReadout *myReadout = pmReadoutAlloc(NULL);
    pmReadout *rc = NULL;
    myReadout->image = myImage;
    psVector *in = psVectorAlloc(tableSize, PS_TYPE_F32);
    psVector *inOne = psVectorAlloc(1, PS_TYPE_F32);
    psVector *inSmall = psVectorAlloc(tableSize-1, PS_TYPE_F32);
    psVector *inBig = psVectorAlloc(tableSize+1, PS_TYPE_F32);
    psVector *out = psVectorAlloc(tableSize, PS_TYPE_F32);
    psVector *outOne = psVectorAlloc(1, PS_TYPE_F32);
    psVector *outSmall = psVectorAlloc(tableSize-1, PS_TYPE_F32);
    psVector *outBig = psVectorAlloc(tableSize+1, PS_TYPE_F32);

    test03Init(myReadout);
    for (i=0;i<tableSize;i++) {
        in->data.F32[i] = (float) i;
        out->data.F32[i] = (float) (2 * i);
        inBig->data.F32[i] = (float) i;
        outBig->data.F32[i] = (float) (2 * i);
        if (i < tableSize-1) {
            inSmall->data.F32[i] = (float) i;
            outSmall->data.F32[i] = (float) (2 * i);
        }
    }
    inBig->data.F32[tableSize] = (float) tableSize;
    outBig->data.F32[tableSize] = (float) (2 * tableSize);
    inOne->data.F32[0] = 0.0;
    outOne->data.F32[0] = 0.0;

    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with NULL input pmReadout.  Should generate error, return NULL.\n");
    rc = pmNonLinearityLookup(NULL, in, out);
    if (rc != NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
        testStatus = false;
    }

    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with NULL input pmReadout->image.  Should generate error, return NULL.\n");
    psImage *tmpImage = myReadout->image;
    myReadout->image = NULL;
    rc = pmNonLinearityLookup(myReadout, in, out);
    if (rc != NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
        testStatus = false;
    }
    myReadout->image = tmpImage;

    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with NULL inFlux psVector.  Should generate error, return NULL.\n");
    rc = pmNonLinearityLookup(myReadout, NULL, out);
    if (rc != NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
        testStatus = false;
    }

    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with NULL outFlux psVector.  Should generate error, return NULL.\n");
    rc = pmNonLinearityLookup(myReadout, in, NULL);
    if (rc != NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
        testStatus = false;
    }

    test03Init(myReadout);
    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
    rc = pmNonLinearityLookup(myReadout, in, outBig);
    if (rc == NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) (2 * (i + j));
            psF32 actual = rc->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }


    test03Init(myReadout);
    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
    rc = pmNonLinearityLookup(myReadout, in, outSmall);
    if (rc == NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) (2 * (i + j));
            psF32 actual = rc->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }

    test03Init(myReadout);
    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
    rc = pmNonLinearityLookup(myReadout, inSmall, out);
    if (rc == NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) (2 * (i + j));
            psF32 actual = rc->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }

    test03Init(myReadout);
    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
    rc = pmNonLinearityLookup(myReadout, inBig, out);
    if (rc == NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) (2 * (i + j));
            psF32 actual = rc->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }

    test03Init(myReadout);
    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
    rc = pmNonLinearityLookup(myReadout, inSmall, outBig);
    if (rc == NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) (2 * (i + j));
            psF32 actual = rc->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }

    test03Init(myReadout);
    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
    rc = pmNonLinearityLookup(myReadout, inBig, outSmall);
    if (rc == NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) (2 * (i + j));
            psF32 actual = rc->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }

    test03Init(myReadout);
    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with inFlux psVector size 1.  Should generate error, return original pmReadout.\n");
    rc = pmNonLinearityLookup(myReadout, inOne, out);
    if (rc != myReadout) {
        printf("TEST ERROR: pmNonLinearityPolynomial() did not return the original pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) ((i + j));
            psF32 actual = rc->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }

    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with one pixels outside inFlux range.  Should generate warnings.\n");
    test03Init(myReadout);
    myReadout->image->data.F32[0][0] = -1;
    rc = pmNonLinearityLookup(myReadout, in, out);
    if (rc == NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) (2 * (i + j));
            psF32 actual = rc->image->data.F32[i][j];
            if(i==0 && j==0) {
                if(actual != 0.0) {
                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n",i,j,actual,0.0);
                }
            } else {
                if (FLT_EPSILON < fabs(expect - actual)) {
                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                    testStatus = false;
                }
            }
        }
    }

    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with one pixels outside inFlux range.  Should generate warnings.\n");
    test03Init(myReadout);
    myReadout->image->data.F32[NUM_ROWS-1][NUM_COLS-1] = 100;
    rc = pmNonLinearityLookup(myReadout, in, out);
    if (rc == NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) (2 * (i + j));
            psF32 actual = rc->image->data.F32[i][j];
            if(i==(NUM_ROWS-1) && j==(NUM_COLS-1)) {
                if(actual != (tableSize-1)*2) {
                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n",i,j,actual,(tableSize-1)*2.0);
                }
            } else {
                if (FLT_EPSILON < fabs(expect - actual)) {
                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                    testStatus = false;
                }
            }
        }
    }

    test03Init(myReadout);
    printf("------------------------------------------------------------\n");
    printf("Calling pmNonLinearityLookup() with image values not in vector.\n");
    myReadout->image->data.F32[0][0] = 0.5;
    rc = pmNonLinearityLookup(myReadout, in, out);
    if (rc == NULL) {
        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
        testStatus = false;
    }
    for (i=0;i<NUM_ROWS;i++) {
        for (j=0;j<NUM_COLS;j++) {
            psF32 expect = (float) (2 * (i + j));
            if(i==0 && j==0) {
                expect = 1.0;
            }
            psF32 actual = rc->image->data.F32[i][j];
            if (FLT_EPSILON < fabs(expect - actual)) {
                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                testStatus = false;
            }
        }
    }

    psFree(myReadout);
    psFree(in);
    psFree(inOne);
    psFree(inSmall);
    psFree(inBig);
    psFree(out);
    psFree(outOne);
    psFree(outSmall);
    psFree(outBig);

    printFooter(stdout, "pmNonLinear", "Testing bad input parameter conditions.", true);
    return(testStatus);
}
