/*****************************************************************************
   This routine must ensure that the psHistogram structure is correctly
   populated by the procedure psGetArrayHistogram().
 
*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <pslib.h>
#include "tap.h"
#include "pstap.h"
#define ERROR_TOLERANCE 0.001
#define NUM_DATA 10000

psS32 genericTest(psS32 numBins, psS32 lower, psS32 higher)
{
    if ((numBins%2) != 0) {
        diag("Configuration error: must run this test with even number of bins");
        return(0);
    }
    if ((NUM_DATA%numBins) != 0) {
        diag("Configuration error: NUM_DATA must be an integer multiple of numBins");
        return(0);
    }

    // Allocate the psHistogram structure, ensure that psVectorHistogram() sets
    // the data correctly when called with a NULL mask.
    {
        psMemId id = psMemGetId();
        psVector *myData = psVectorAlloc( NUM_DATA, PS_TYPE_F32 );
        myData->n = myData->nalloc;
        for (int i = 0;i < NUM_DATA;i++)
        {
            myData->data.F32[ i ] = lower + ( ( higher - lower ) / ( float ) NUM_DATA ) * ( float ) i;
        }

        psHistogram *myHist = psHistogramAlloc(lower, higher, numBins );
        myHist = psVectorHistogram( myHist, myData, NULL, NULL, 0 );
        ok(myHist != NULL, "psVectorHistogram() returned a non-NULL psHistogram");
        skip_start(myHist == NULL, 1, "Skipping tests because psVectorHistogram() returned NULL");

        bool errorFlag = false;
        for (int i = 0;i < numBins;i++)
        {
            if (fabs(myHist->nums->data.F32[i] - (NUM_DATA/numBins)) > ERROR_TOLERANCE) {
                diag("Bin number %d bounds: (%.2f - %.2f) data (%.2f) should be (%.2f)\n",
                     i,
                     myHist->bounds->data.F32[i],
                     myHist->bounds->data.F32[i + 1],
                     myHist->nums->data.F32[i], (float) (NUM_DATA/numBins));
                errorFlag = true;
            }
        }
        ok(!errorFlag, "psVectorHistogram() correctly set the histogram");
        skip_end();
        psFree(myHist);
        psFree(myData);
        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    }

    // Allocate the psHistogram structure, ensure that psVectorHistogram() sets
    // the data correctly when called with a mask.
    {
        psMemId id = psMemGetId();
        psVector *myData = psVectorAlloc( NUM_DATA, PS_TYPE_F32 );
        myData->n = myData->nalloc;
        for (int i = 0;i < NUM_DATA;i++)
        {
            myData->data.F32[ i ] = lower + ( ( higher - lower ) / ( float ) NUM_DATA ) * ( float ) i;
        }

        psVector *myMask = psVectorAlloc( NUM_DATA, PS_TYPE_U8 );
        myMask->n = myMask->nalloc;
        for (int i = 0;i < NUM_DATA;i++)
        {
            if ( i >= ( NUM_DATA / 2 ) ) {
                myMask->data.U8[ i ] = 1;
            } else {
                myMask->data.U8[ i ] = 0;
            }
        }
        psHistogram *myHist = psHistogramAlloc(lower, higher, numBins );
        myHist = psVectorHistogram(myHist, myData, NULL, myMask, 1);
        ok(myHist != NULL, "psVectorHistogram() returned a non-NULL psHistogram");
        skip_start(myHist == NULL, 1, "Skipping tests because psVectorHistogram() returned NULL");

        bool errorFlag = false;
        for (int i = 0;i < numBins;i++)
        {
            if (i < numBins/2) {
                if (fabs(myHist->nums->data.F32[i] - (NUM_DATA/numBins)) > ERROR_TOLERANCE) {
                    diag("Bin number %d bounds: (%6.3f - %6.3f) data (%f) should be (%.2f)\n", i,
                         myHist->bounds->data.F32[ i ],
                         myHist->bounds->data.F32[ i + 1 ],
                         myHist->nums->data.F32[ i ], (float) (NUM_DATA/numBins) );
                    errorFlag = true;
                }
            } else {
                if (fabs(myHist->nums->data.F32[i]) > ERROR_TOLERANCE) {
                    diag("Bin number %d bounds: (%6.3f - %6.3f) data (%f) should be (%.2f)\n", i,
                         myHist->bounds->data.F32[ i ],
                         myHist->bounds->data.F32[ i + 1 ],
                         myHist->nums->data.F32[ i ], 0.0);
                    errorFlag = true;
                }
            }
        }
        ok(!errorFlag, "psVectorHistogram() correctly set the histogram");
        skip_end();
        psFree(myHist);
        psFree(myMask);
        psFree(myData);
        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    }

    // Verify the return value is null and program execution doesn't stop,
    // if input parameter myHist is null.
    {
        psMemId id = psMemGetId();
        psVector *myData = psVectorAlloc( NUM_DATA, PS_TYPE_F32 );
        myData->n = myData->nalloc;
        for (int i = 0;i < NUM_DATA;i++)
        {
            myData->data.F32[ i ] = lower + ( ( higher - lower ) / ( float ) NUM_DATA ) * ( float ) i;
        }
        ok(NULL == psVectorHistogram( NULL, myData, NULL, NULL, 0 ), "psVectorHistogram() returned NULL with a NULL psHistogram input");
        psFree( myData );
        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    }


    // Verify the return value is the same as the input parameter myHist and
    // program execution doesn't stop, if the input parameter myArray is
    // null.
    {
        psMemId id = psMemGetId();
        psHistogram *myHist = psHistogramAlloc(lower, higher, numBins );
        ok(NULL != psVectorHistogram( myHist, NULL, NULL, NULL, 0 ), "psVectorHistogram() returns non-NULL with a NULL input array");
        psFree(myHist);
        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    }

    // Verify the return value is the same as the input parameter myHist and
    // program execution doesn't stop, if the input parameter myArray has no
    // elements.
    // XXX: This code segment is commented out because psVectorAlloc returns
    // NULL if called with an N element data.
    {
        psMemId id = psMemGetId();
        psVector *myData = psVectorAlloc(0, PS_TYPE_F32);
        psHistogram *myHist = psHistogramAlloc(lower, higher, numBins);
        myHist = psVectorHistogram(myHist, NULL, NULL, NULL, 0);
        ok(NULL != psVectorHistogram( myHist, NULL, NULL, NULL, 0 ), "psVectorHistogram() returns non-NULL with an empty input array");
        psFree(myHist);
        psFree(myData);
        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    }

    return(1);
}

psS32 main()
{
    psLogSetFormat("HLNM");
    plan_tests(48);

    genericTest(2, 1, 3);
    genericTest(10, 1, 11);
    genericTest(20, 1, 21);
    genericTest(500, 1, 501);
}
