/** @file  tst_psCoord02.c
*
*  @brief This file contains test code for:
*
*
*  @author GLG, MHPCC
*
*  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
*  @date $Date: 2005-11-23 01:03:18 $
*
*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
*/

#include "psTest.h"
#include "pslib_strict.h"

static psS32 test04( void );
static psS32 test05( void );
static psS32 test06( void );

testDescription tests[] = {
                              {test04, 0000, "psPlaneTransformCombine()", 0, false},
                              {test05, 0000, "psPlaneTransformFit()", 0, false},
                              {test06, 0000, "psPlaneTransformInvert()", 0, false},
                              {NULL}
                          };

#define PS_PERCENT_COMPARE(X, Y, PERCENT_FRACTION) (fabs((Y)-(X))/fabs(X) < (PERCENT_FRACTION))

//#define PS_GEN_RAN_FLOAT (-1.0 + (psF32) (rand() % 2))
#define PS_GEN_RAN_FLOAT (-0.5 + ((psF32)rand()) / ((psF32) RAND_MAX))
#define RANDOM_SEED 1995

psS32 main( psS32 argc, char* argv[] )
{
    psLogSetLevel( PS_LOG_INFO );
    psLogSetFormat("HLNM");
    psTraceSetLevel(".", 0);
    psTraceSetLevel("psPlaneTransformCombine", 0);
    psTraceSetLevel("multiplyDPoly2D", 0);
    srand(RANDOM_SEED);

    return ( ! runTestSuite( stderr, "psImage", tests, argc, argv ) );
}


/******************************************************************************
TEST04: The strategy behind test04() is to generate a pair of transforms t1
and t2 with a wide variety of sizes for the x/y components of the x/y
transforms, then set the coefficients of t1/t2 to arbitray values, then
generate a new transform t3 via psPlaneTransformCombine() function.  Then, for
several arbitrary input plane coordinates, we tarnsform them via the new
transform t3, as well as individually via t1 then t2, and verify that they
produce identical output coordinates.
 *****************************************************************************/
// These macros determine the number of x/y test points which will be x-formed.
#define TST04_X_MIN 0.0
#define TST04_X_MAX 10.0
#define TST04_Y_MIN 0.0
#define TST04_Y_MAX 10.0
#define TST04_NUM 5.0
// These macros define the max sizes of the various input transforms.
#define TST04_T1_X_X 5
#define TST04_T1_X_Y 3
#define TST04_T1_Y_X 3
#define TST04_T1_Y_Y 3
#define TST04_T2_X_X 3
#define TST04_T2_X_Y 3
#define TST04_T2_Y_X 3
#define TST04_T2_Y_Y 3
/******************************************************************************
tstTransforms(t1, t2, t3): this function generates a set of arbitrary x/y
coordinates, then transforms them into output coordinates via the t3
transform, as well as the t1 followed by the t2 transform.  It verifies the
output coordinates are identical and returns TRUE if so.  Otherwise, it prints
an error message and returns FALSE.
 *****************************************************************************/
bool tstTransforms(psPlaneTransform *t1, psPlaneTransform *t2, psPlaneTransform *t3)
{
    bool testStatus = true;
    psPlane *in = psPlaneAlloc();
    in->xErr = 0.0;
    in->yErr = 0.0;

    for (in->x = TST04_X_MIN ; in->x <= TST04_X_MAX ; in->x+= (TST04_X_MAX-TST04_X_MIN) / TST04_NUM) {
        for (in->y = TST04_Y_MIN ; in->y <= TST04_Y_MAX ; in->y+= (TST04_Y_MAX-TST04_Y_MIN) / TST04_NUM) {
            // Apply the t1/t2 transforms individually to the in coords.
            psPlane *mid = psPlaneTransformApply(NULL, t1, in);
            if (mid == NULL) {
                printf("TEST ERROR: intermediate psPlane coords are NULL.\n");
                psFree(in);
                return(false);
            }

            psPlane *outT1T2 = psPlaneTransformApply(NULL, t2, mid);
            if (outT1T2 == NULL) {
                printf("TEST ERROR: intermediate psPlane coords are NULL.\n");
                psFree(mid);
                return(false);
            }

            // Apply the t3 transforms individually to the in coords.
            psPlane *outT3 = psPlaneTransformApply(NULL, t3, in);
            if (outT3 == NULL) {
                printf("TEST ERROR: intermediate psPlane coords are NULL.\n");
                psFree(mid);
                psFree(outT1T2);
                return(false);
            }

            // Verify that the results are identical.
            if (!PS_PERCENT_COMPARE(outT3->x, outT1T2->x, 0.01)) {
                printf("TEST ERROR: x is %f, should be %f\n", outT3->x, outT1T2->x);
                testStatus = false;
            }
            // Verify that the results are identical.
            if (!PS_PERCENT_COMPARE(outT3->y, outT1T2->y, 0.01)) {
                printf("TEST ERROR: y is %f, should be %f\n", outT3->y, outT1T2->y);
                testStatus = false;
            }
            if (0) {
                if (fabs(outT3->x - outT1T2->x) > FLT_EPSILON) {
                    printf("TEST ERROR: x is %f, should be %f\n", outT3->x, outT1T2->x);
                    testStatus = false;
                    printf("(in->x, in->y) is (%f, %f)\n", in->x, in->y);
                    printf("(mid->x, mid->y) is (%f, %f)\n", mid->x, mid->y);
                    printf("(outT1T2->x, outT1T2->y) is (%f, %f)\n", outT1T2->x, outT1T2->y);
                    printf("(outT3->x, outT3->y) is (%f, %f)\n", outT3->x, outT3->y);
                    PS_POLY_PRINT_2D(t1->x);
                    PS_POLY_PRINT_2D(t1->y);
                    PS_POLY_PRINT_2D(t2->x);
                    PS_POLY_PRINT_2D(t2->y);
                    PS_POLY_PRINT_2D(t3->x);
                    PS_POLY_PRINT_2D(t3->y);
                }
                if (fabs(outT3->y - outT1T2->y) > FLT_EPSILON) {
                    printf("TEST ERROR: y is %f, should be %f\n", outT3->y, outT1T2->y);
                    testStatus = false;
                    printf("(in->x, in->y) is (%f, %f)\n", in->x, in->y);
                    printf("(mid->x, mid->y) is (%f, %f)\n", mid->x, mid->y);
                    printf("(outT1T2->x, outT1T2->y) is (%f, %f)\n", outT1T2->x, outT1T2->y);
                    printf("(outT3->x, outT3->y) is (%f, %f)\n", outT3->x, outT3->y);
                    PS_POLY_PRINT_2D(t1->x);
                    PS_POLY_PRINT_2D(t1->y);
                    PS_POLY_PRINT_2D(t2->x);
                    PS_POLY_PRINT_2D(t2->y);
                    PS_POLY_PRINT_2D(t3->x);
                    PS_POLY_PRINT_2D(t3->y);
                }
            }
            psFree(mid);
            psFree(outT1T2);
            psFree(outT3);
        }
    }

    psFree(in);
    return(testStatus);
}

/******************************************************************************
setCoeffs(t1, t2): this function sets the coefficients of the t1 and t2
transforms to somewhat arbitrary values.
 *****************************************************************************/
int setCoeffs(psPlaneTransform *t1, psPlaneTransform *t2)
{
    for (psS32 t1xx = 0 ; t1xx < t1->x->nX+1 ; t1xx++) {
        for (psS32 t1xy = 0 ; t1xy < t1->x->nY+1 ; t1xy++) {
            t1->x->coeff[t1xx][t1xy] = PS_GEN_RAN_FLOAT;
            //printf("%f\n", t1->x->coeff[t1xx][t1xy]);
        }
    }

    for (psS32 t1yx = 0 ; t1yx < t1->y->nX+1 ; t1yx++) {
        for (psS32 t1yy = 0 ; t1yy < t1->y->nY+1 ; t1yy++) {
            t1->y->coeff[t1yx][t1yy] = PS_GEN_RAN_FLOAT;
            //printf("%f\n", t1->y->coeff[t1yx][t1yy]);
        }
    }
    for (psS32 t2xx = 0 ; t2xx < t2->x->nX+1 ; t2xx++) {
        for (psS32 t2xy = 0 ; t2xy < t2->x->nY+1 ; t2xy++) {
            t2->x->coeff[t2xx][t2xy] = PS_GEN_RAN_FLOAT;
            //printf("%f\n", t2->x->coeff[t2xx][t2xy]);
        }
    }
    for (psS32 t2yx = 0 ; t2yx < t2->y->nX+1 ; t2yx++) {
        for (psS32 t2yy = 0 ; t2yy < t2->y->nY+1 ; t2yy++) {
            t2->y->coeff[t2yx][t2yy] = PS_GEN_RAN_FLOAT;
            //printf("%f\n", t2->y->coeff[t2yx][t2yy]);
        }
    }

    return(0);
}

/******************************************************************************
test04(): We test psPlaneTransformCombine() with a variety of input
transforms, as well as input coordinates, as well as erroneous input
parameters.
 *****************************************************************************/
psS32 test04( void )
{
    bool testStatus = true;

    // Create a variety of input transforms, then test them.
    //
    for (psS32 t1xx = 0 ; t1xx < TST04_T1_X_X ; t1xx++) {
        for (psS32 t1xy = 0 ; t1xy < TST04_T1_X_Y ; t1xy++) {
            for (psS32 t1yx = 0 ; t1yx < TST04_T1_Y_X ; t1yx++) {
                for (psS32 t1yy = 0 ; t1yy < TST04_T1_Y_Y ; t1yy++) {
                    for (psS32 t2xx = 0 ; t2xx < TST04_T2_X_X ; t2xx++) {
                        for (psS32 t2xy = 0 ; t2xy < TST04_T2_X_Y ; t2xy++) {
                            for (psS32 t2yx = 0 ; t2yx < TST04_T2_Y_X ; t2yx++) {
                                for (psS32 t2yy = 0 ; t2yy < TST04_T2_Y_Y ; t2yy++) {
                                    //                                    printf("(%d %d %d %d %d %d %d %d)\n", t1xx, t1xy, t1yx, t1yy, t2xx, t2xy, t2yx, t2yy);
                                    psPlaneTransform *trans1 = psPlaneTransformAlloc(PS_MAX(t1xx, t1yx), PS_MAX(t1xy, t1yy));
                                    psPlaneTransform *trans2 = psPlaneTransformAlloc(PS_MAX(t2xx, t2yx), PS_MAX(t2xy, t2yy));
                                    psPlaneTransform *trans3 = NULL;
                                    setCoeffs(trans1, trans2);
                                    trans3 = psPlaneTransformCombine(NULL, trans1,
                                                                     trans2, psRegionSet(NAN,NAN,NAN,NAN), 0);
                                    //XXX: the last two parameters are bogus.  Needs to change.  -rdd

                                    if (trans3 == NULL) {
                                        printf("TEST ERROR: psPlaneTransformCombine() returned NULL/\n");
                                        testStatus = false;
                                    } else {
                                        testStatus = tstTransforms(trans1, trans2, trans3);
                                    }
                                    psFree(trans1);
                                    psFree(trans2);
                                    psFree(trans3);
                                }
                            }
                        }
                    }
                }
            }
        }
    }


    //
    // Test erroneous input parameter combinations
    //
    psPlaneTransform *trans1 = psPlaneTransformAlloc(2, 2);
    psPlaneTransform *trans2 = psPlaneTransformAlloc(2, 2);
    psPlaneTransform *trans3 = NULL;

    printf("----------------------------------------------------------------------------------\n");
    printf("Calling psPlaneTransformCombine with NULL trans1.  Should generate error and return NULL.\n");
    trans3 = psPlaneTransformCombine(NULL, NULL, trans2, psRegionSet(0,0,0,0), 10);
    if (trans3 != NULL) {
        printf("TEST ERROR: psPlaneTransformCombine() returned a non-NULL psPlaneTransform.\n");
        testStatus = false;
        psFree(trans3);
    }

    printf("----------------------------------------------------------------------------------\n");
    printf("Calling psPlaneTransformCombine with NULL trans2.  Should generate error and return NULL.\n");
    trans3 = psPlaneTransformCombine(NULL, trans1, NULL, psRegionSet(0,0,0,0), 10);
    if (trans3 != NULL) {
        printf("TEST ERROR: psPlaneTransformCombine() returned a non-NULL psPlaneTransform.\n");
        testStatus = false;
        psFree(trans3);
    }

    psFree(trans1);
    psFree(trans2);
    return(!testStatus);
}


#define TST05_X_MIN 0.0
#define TST05_X_MAX 10.0
#define TST05_Y_MIN 0.0
#define TST05_Y_MAX 10.0
#define TST05_NUM_X 5.0
#define TST05_NUM_Y 5.0
#define TST05_X_POLY_ORDER 2
#define TST05_Y_POLY_ORDER 2
#define TST05_NUM_DATA (TST05_NUM_X * TST05_NUM_Y)
/******************************************************************************
XXX: This is only a rudimentary test of the psPlaneTransformFit() function.
It tests a few NULL input parameter conditions, and some simple linear
transformations.
 *****************************************************************************/
psS32 test05( void )
{
    bool testStatus = true;
    psPlaneTransform *trans = psPlaneTransformAlloc(TST05_X_POLY_ORDER, TST05_Y_POLY_ORDER);
    psPlaneTransform *transInit = psPlaneTransformAlloc(TST05_X_POLY_ORDER, TST05_Y_POLY_ORDER);
    psArray *src = psArrayAlloc((int) TST05_NUM_DATA);
    psArray *dst = psArrayAlloc((int) TST05_NUM_DATA);
    bool rc;

    //
    // We set an arbitrary non-linear transformation.
    //
    for (psS32 x = 0 ; x < TST05_X_POLY_ORDER ; x++) {
        for (psS32 y = 0 ; y < TST05_Y_POLY_ORDER ; y++) {
            transInit->x->coeff[x][y] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
            transInit->y->coeff[x][y] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
        }
    }

    // okay, for now, it's linear.
    transInit->x->coeff[1][1] = 0.1;
    transInit->y->coeff[1][1] = 0.1;

    /*
        //
        // We set an arbitrary linear transformation.
        //
        transInit->x->coeff[0][0] = 1.0;
        transInit->x->coeff[0][1] = 3.0;
        transInit->x->coeff[1][0] = 2.0;
        transInit->x->coeff[1][1] = 7.0;
        transInit->y->coeff[0][0] = 4.0;
        transInit->y->coeff[0][1] = 6.0;
        transInit->y->coeff[1][0] = 5.0;
        transInit->y->coeff[1][1] = 3.0;
    */
    //
    // We generate a grid of input data points in the x1,y1 plane, calculate the
    // corresponding values in the transformed plane, and set these to
    // the src and dst psVectors.
    //
    psS32 i = 0;
    for (psF32 x = TST05_X_MIN ; x < TST05_X_MAX ; x+= (TST05_X_MAX-TST05_X_MIN)/TST05_NUM_X) {
        for (psF32 y = TST05_Y_MIN ; y < TST05_Y_MAX ; y+= (TST05_Y_MAX-TST05_Y_MIN)/TST05_NUM_Y) {
            src->data[i] = (psPtr *) psPlaneAlloc();
            ((psPlane *) src->data[i])->x = x;
            ((psPlane *) src->data[i])->y = y;

            dst->data[i] = psPlaneTransformApply(NULL, transInit, ((psPlane *) src->data[i]));
            i++;
        }
    }

    printf("----------------------------------------------------------------------------------\n");
    printf("Calling psPlaneTransformFit with NULL trans.  Should generate error and return NULL.\n");
    rc = psPlaneTransformFit(NULL, src, dst, 100, 100.0);
    if (rc == true) {
        printf("TEST ERROR: psPlaneTransformFit() returned TRUE.\n");
        testStatus = false;
    }

    printf("----------------------------------------------------------------------------------\n");
    printf("Calling psPlaneTransformFit with NULL src psArray.  Should generate error and return NULL.\n");
    rc = psPlaneTransformFit(trans, NULL, dst, 100, 100.0);
    if (rc == true) {
        printf("TEST ERROR: psPlaneTransformFit() returned TRUE.\n");
        testStatus = false;
    }

    printf("----------------------------------------------------------------------------------\n");
    printf("Calling psPlaneTransformFit with NULL dst psArray.  Should generate error and return NULL.\n");
    rc = psPlaneTransformFit(trans, src, NULL, 100, 100.0);
    if (rc == true) {
        printf("TEST ERROR: psPlaneTransformFit() returned TRUE.\n");
        testStatus = false;
    }

    printf("----------------------------------------------------------------------------------\n");
    printf("Calling psPlaneTransformFit with acceptable data.\n");
    rc = psPlaneTransformFit(trans, src, dst, 100, 100.0);
    if (rc != true) {
        printf("TEST ERROR: psPlaneTransformFit() returned FALSE.\n");
        testStatus = false;
    }

    //
    // For the initial grid of input points, we transform them to output points with
    // the derived transformation, and verify that they are within 10%.
    //

    // XXX: We ignore endpoints.
    for (psS32 i = 1 ; i < src->n-1 ; i++) {
        psPlane *inData = (psPlane *) src->data[i];
        psPlane *outData = (psPlane *) dst->data[i];
        psPlane *outDataDeriv = psPlaneTransformApply(NULL, trans, inData);
        if (!PS_PERCENT_COMPARE(outDataDeriv->x, outData->x, 0.20) ||
                !PS_PERCENT_COMPARE(outDataDeriv->y, outData->y, 0.20)) {
            printf("TEST ERROR: the derived output coords (%d) were (%f, %f) should have been (%f, %f).\n",
                   i, outDataDeriv->x, outDataDeriv->y, outData->x, outData->y);
            testStatus = false;
        }
        psFree(outDataDeriv);
    }

    psFree(transInit);
    psFree(trans);
    psFree(src);
    psFree(dst);

    return(!testStatus);
}

#define NUM_TRANSFORMS 100
/******************************************************************************
This is only a rudimentary test of the psPlaneTransformInvert() function.
It tests:
    A few NULL input parameter conditions.
    Several random linear transformations.
XXX: Must test:
    Non-linear transformations.
  *****************************************************************************/
psS32 test06( void )
{
    bool testStatus = true;
    psPlaneTransform *trans = psPlaneTransformAlloc(1, 1);
    psPlaneTransform *transInverse = NULL;
    psRegion myRegion = psRegionSet(1.0, 5.0, 1.0, 5.0);

    trans->x->coeff[0][0] = 0.0;
    trans->x->coeff[0][1] = 1.0;
    trans->x->coeff[1][0] = 2.0;
    trans->x->coeff[1][1] = 3.0;
    trans->y->coeff[0][0] = 4.0;
    trans->y->coeff[0][1] = 5.0;
    trans->y->coeff[1][0] = 6.0;
    trans->y->coeff[1][1] = 7.0;

    printf("----------------------------------------------------------------------------------\n");
    printf("Calling psPlaneTransformInvert with NULL trans.  Should generate error and return NULL.\n");
    transInverse = psPlaneTransformInvert(NULL, NULL, myRegion, 10);
    if (transInverse != NULL) {
        printf("TEST ERROR: psPlaneTransformInvert() returned a non-NULL psPlaneTransform.\n");
        testStatus = false;
        psFree(transInverse);
    }

    printf("----------------------------------------------------------------------------------\n");
    printf("Calling psPlaneTransformInvert with zero nSamples.  Should generate error and return NULL.\n");
    transInverse = psPlaneTransformInvert(NULL, trans, myRegion, 0);
    if (transInverse != NULL) {
        printf("TEST ERROR: psPlaneTransformInvert() returned a non-NULL psPlaneTransform.\n");
        testStatus = false;
        psFree(transInverse);
    }

    printf("Calling psPlaneTransformInvert with acceptable linear transformations.\n");
    for (psS32 n = 0 ; n < NUM_TRANSFORMS ; n++) {
        if (n == 0) {
            // I ensure that we test the identity transformation since this was
            // giving us probs before.
            trans->x->coeff[0][0] = 0.0;
            trans->x->coeff[0][1] = 0.0;
            trans->x->coeff[1][0] = 1.0;
            trans->x->coeff[1][1] = 0.0;
            trans->y->coeff[0][0] = 0.0;
            trans->y->coeff[0][1] = 1.0;
            trans->y->coeff[1][0] = 0.0;
            trans->y->coeff[1][1] = 0.0;
        } else {
            // We create a random linear transformation and hope it is invertible.
            trans->x->coeff[0][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
            trans->x->coeff[0][1] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
            trans->x->coeff[1][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
            trans->x->coeff[1][1] = 0.0;
            trans->y->coeff[0][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
            trans->y->coeff[0][1] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
            trans->y->coeff[1][0] = (psF32)rand() / (psF32)RAND_MAX * 10.0f;
            trans->y->coeff[1][1] = 0.0;
        }

        transInverse = psPlaneTransformInvert(NULL, trans, myRegion, 10);
        if (transInverse == NULL) {
            printf("TEST ERROR: psPlaneTransformInvert() returned a NULL psPlaneTransform.\n");
            testStatus = false;
        }
        //
        // Transform the "in" coords to "mid" with psPlaneTransform "trans", then
        // transform "mid" to "out" with psPlaneTransform "transInverse".
        //
        // XXX: Loop on a few data points.
        //
        psPlane *in = psPlaneAlloc();
        in->x = 2.0;
        in->y = 3.0;
        psPlane *mid = psPlaneTransformApply(NULL, trans, in);
        psPlane *out = psPlaneTransformApply(NULL, transInverse, mid);

        if (((fabs(in->x - out->x) > FLT_EPSILON)) ||
                ((fabs(in->y - out->y) > FLT_EPSILON)) ||
                isnan(out->x) ||
                isnan(out->y)) {
            printf("TEST ERROR: in coords were (%f, %f), out coords were (%f, %f).\n",
                   in->x, in->y, out->x, out->y);
            testStatus = false;
        }

        psFree(in);
        psFree(mid);
        psFree(out);
        psFree(transInverse);
    }

    psFree(trans);

    fflush(stdout);

    return(!testStatus);
}


