/*****************************************************************************
    This routine must ensure that the psVectorFitPolynomial1D works correctly.
 
    We use Chebyshev polynomials here.
 *****************************************************************************/
#include <stdio.h>
#include "pslib_strict.h"
#include "psTest.h"
#include "psMemory.h"
#include "psVector.h"
#include "psImage.h"
#include "psMinimize.h"
#include <math.h>
#define NUM_DATA 21
#define POLY_ORDER 10
#define A 2.0
#define B 3.0
#define C 2.0
#define D 4.0
#define E 5.0
#define ERROR_TOLERANCE 0.10
#define IGNORE (ERROR_TOLERANCE * NUM_DATA)

double setData(double x)
{
    return(A + (B * x) + (C * x * x) + (D * x * x * x) + (E * x * x * x * x));
}

psS32 t00()
{
    psPolynomial1D *myPoly = NULL;
    psVector *x = NULL;
    psVector *y = NULL;
    psVector *yErr = NULL;
    psS32 i = 0;
    psS32 currentId = psMemGetId();
    psS32 testStatus = true;
    psS32 memLeaks = 0;

    myPoly = psPolynomial1DAlloc(POLY_ORDER, PS_POLYNOMIAL_CHEB);
    x = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
    y = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
    yErr = psVectorAlloc(NUM_DATA, PS_TYPE_F64);

    for (i=0;i<NUM_DATA;i++) {
        x->data.F64[i] = (double) i;
        y->data.F64[i] = setData(x->data.F64[i]);
        yErr->data.F64[i] = 1.0;
    }
    p_psNormalizeVectorRangeF64(x, -1.0, 1.0);

    printPositiveTestHeader(stdout,
                            "psMinimize functions",
                            "psVectorFitPolynomial1D(): CHEB, equal errors in yErr");

    psVectorFitPolynomial1D(myPoly, NULL, 0, y, yErr, x);

    //  Remove for testing since print out differs from platforms
    //    for (i=0;i<POLY_ORDER+1;i++) {
    //        printf("Polynomial coefficient %d is %0.1f\n", i, myPoly->coeff[i]);
    //    }

    // We don't test the first or last few data items.
    for (i=IGNORE;i<NUM_DATA-IGNORE;i++) {
        double expectData = y->data.F64[i];
        double actualData = psPolynomial1DEval(
                                myPoly,
                                x->data.F64[i]
                            );
        if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
            printf("ERROR: Fitted data %d: (%.1f %.1f), expected was (%.1f)\n",
                   i, x->data.F64[i], actualData, expectData);
            testStatus = false;
        }
        //      Remove for testing since print out differs from platforms
        //        } else {
        //            printf("Fitted data %d: (%.1f %.1f), expected was (%.1f)\n",
        //                   i, x->data.F64[i], actualData, expectData);
        //        }
    }

    psMemCheckCorruption(1);
    psFree(myPoly);
    psFree(x);
    psFree(y);
    psFree(yErr);
    psMemCheckCorruption(1);
    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
    if (0 != memLeaks) {
        psAbort(__func__,"Memory Leaks! (%d leaks)", memLeaks);
    }

    printFooter(stdout,
                "psMinimize functions",
                "psVectorFitPolynomial1D(): CHEB, equal errors in yErr",
                testStatus);
    return (!testStatus);
}


psS32 t01()
{
    psPolynomial1D *myPoly = NULL;
    psVector *x = NULL;
    psVector *y = NULL;
    psS32 i = 0;
    psS32 currentId = psMemGetId();
    psS32 testStatus = true;
    psS32 memLeaks = 0;

    myPoly = psPolynomial1DAlloc(POLY_ORDER, PS_POLYNOMIAL_CHEB);
    x = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
    y = psVectorAlloc(NUM_DATA, PS_TYPE_F64);

    for (i=0;i<NUM_DATA;i++) {
        x->data.F64[i] = (double) i;
        y->data.F64[i] = setData(x->data.F64[i]);
    }
    p_psNormalizeVectorRangeF64(x, -1.0, 1.0);

    printPositiveTestHeader(stdout,
                            "psMinimize functions",
                            "psVectorFitPolynomial1D(): CHEB, yErr is NULL");

    psVectorFitPolynomial1D(myPoly, NULL, 0, y, NULL, x);

    // We don't test the first or last few data items.
    for (i=IGNORE;i<NUM_DATA-IGNORE;i++) {
        double expectData = y->data.F64[i];
        double actualData = psPolynomial1DEval(
                                myPoly,
                                x->data.F64[i]
                            );
        if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
            printf("ERROR: Fitted data %d: (%.1f %.1f), expected was (%.1f)\n",
                   i, x->data.F64[i], actualData, expectData);
            testStatus = false;
        }
        //      Remove for testing since print out differs from platforms
        //        } else {
        //            printf("Fitted data %d: (%.1f %.1f), expected was (%.1f)\n",
        //                   i, x->data.F64[i], actualData, expectData);
        //        }
    }

    psMemCheckCorruption(1);
    psFree(myPoly);
    psFree(x);
    psFree(y);
    psMemCheckCorruption(1);
    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
    if (0 != memLeaks) {
        psAbort(__func__,"Memory Leaks! (%d leaks)", memLeaks);
    }

    printFooter(stdout,
                "psMinimize functions",
                "psVectorFitPolynomial1D(): CHEB, yErr is NULL",
                testStatus);
    return (!testStatus);
}


psS32 t02()
{
    psPolynomial1D *myPoly = NULL;
    psVector *x = NULL;
    psVector *y = NULL;
    psS32 i = 0;
    psS32 currentId = psMemGetId();
    psS32 testStatus = true;
    psS32 memLeaks = 0;

    myPoly = psPolynomial1DAlloc(POLY_ORDER, PS_POLYNOMIAL_CHEB);
    x = psVectorAlloc(NUM_DATA, PS_TYPE_F64);
    y = psVectorAlloc(NUM_DATA, PS_TYPE_F64);

    for (i=0;i<NUM_DATA;i++) {
        x->data.F64[i] = (double) i;
        y->data.F64[i] = setData(x->data.F64[i]);
    }
    p_psNormalizeVectorRangeF64(x, -1.0, 1.0);

    printPositiveTestHeader(stdout,
                            "psMinimize functions",
                            "psVectorFitPolynomial1D(): CHEB, x, yErr is NULL");

    psVectorFitPolynomial1D(myPoly, NULL, 0, y, NULL, NULL);
    //    psVectorFitPolynomial1D(myPoly, x, y, NULL);

    // We don't test the first or last few data items.
    for (i=IGNORE;i<NUM_DATA-IGNORE;i++) {
        double expectData = y->data.F64[i];
        double actualData = psPolynomial1DEval(myPoly, x->data.F64[i]);
        if (fabs(actualData-expectData) > fabs(ERROR_TOLERANCE * expectData)) {
            printf("ERROR: Fitted data %d: (%.2f %.2f), expected was (%.2f)\n",
                   i, x->data.F64[i], actualData, expectData);
            testStatus = false;
        }
        //      Remove for testing since print out differs from platforms
        //        } else {
        //            printf("Fitted data %d: (%.2f %.2f), expected was (%.2f)\n",
        //                   i, x->data.F64[i], actualData, expectData);
        //        }
    }

    psMemCheckCorruption(1);
    psFree(myPoly);
    psFree(x);
    psFree(y);
    psMemCheckCorruption(1);
    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
    if (0 != memLeaks) {
        psAbort(__func__,"Memory Leaks! (%d leaks)", memLeaks);
    }

    printFooter(stdout,
                "psMinimize functions",
                "psVectorFitPolynomial1D(): CHEB, x, yErr is NULL",
                testStatus);
    return (!testStatus);
}

psS32 t03()
{
    psPolynomial1D *myPoly = NULL;
    psS32 currentId = psMemGetId();
    psS32 testStatus = true;
    psS32 memLeaks = 0;

    printPositiveTestHeader(stdout,
                            "psMinimize functions",
                            "psVectorFitPolynomial1D(): CHEB, yErr is NULL");

    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error for null arguments.");
    psVectorFitPolynomial1D(myPoly, NULL, 0, NULL, NULL, NULL);
    if (myPoly != NULL) {
        printf("ERROR: psVectorFitPolynomial1D() returned a non-NULL polynomial.\n");
        testStatus = false;
    }

    psMemCheckCorruption(1);
    memLeaks = psMemCheckLeaks(currentId,NULL,stderr,false);
    if (0 != memLeaks) {
        psAbort(__func__,"Memory Leaks! (%d leaks)", memLeaks);
    }
    printFooter(stdout,
                "psMinimize functions",
                "psVectorFitPolynomial1D(): CHEB, yErr is NULL",
                testStatus);
    return (!testStatus);
}

psS32 main()
{
    psLogSetFormat("HLNM");
    t00();
    t01();
    t02();
    t03();
}
