/*****************************************************************************
    This routine must ensure that the psHistogram structure is correctly
    allocated and deallocated by the procedure psHistogramAllocGeneric() and
    that it can be populated correctly by psVectorHistogram().
 *****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <pslib.h>
#include "tap.h"
#include "pstap.h"
#define ERROR_TOLERANCE 0.001
#define NUM_DATA 20

psS32 genericTest(psS32 numBins, psS32 lower, psS32 higher)
{
    if (numBins > NUM_DATA) {
        diag("Configuration error: NUM_DATA must be larger than numBins");
        return(0);
    }

    {
        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 *myBounds = psVectorAlloc(numBins+1, PS_TYPE_F32);
        myBounds->n = myBounds->nalloc;
        for (int i=0;i<numBins+1;i++) {
            myBounds->data.F32[i] = lower + ((higher - lower) / (float) numBins) * (float) i;
        }

        psHistogram *myHist = psHistogramAllocGeneric(myBounds);
        ok(myHist->nums->n == numBins, "psHistogramAllocGeneric() set correct number of bins");
        ok(myHist->bounds->n == numBins+1, "sHistogramAllocGeneric() set correct number of bounds");

        bool errorFlag = false;
        for (int i=0;i<numBins;i++) {
            if (fabs(myHist->nums->data.F32[i]) > ERROR_TOLERANCE) {
                diag("ERROR: myHist->nums->data.F32[%d] not initialized to 0.\n", i);
                errorFlag = true;
            }
            myHist->nums->data.F32[i] = 0.0;
        }
        ok(!errorFlag, "psVectorHistogram() correctly initialized the bins");
        ok(myHist->minNum == 0, "psHistogramAlloc() initialized minNum to 0.0");
        ok(myHist->maxNum == 0, "psHistogramAlloc() initialized maxNum to 0.0");
        ok(myHist->uniform == false, "psHistogramAlloc() initialized ->uniform to false");

        myHist = psVectorHistogram( myHist, myData, NULL, NULL, 0 );
        for (int i=0;i<numBins;i++) {
            psF32 expected = ((float) NUM_DATA) / ((float) numBins);
            if (fabs(myHist->nums->data.F32[i] - expected) > 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], expected);
                errorFlag = true;
            }
        }
        ok(!errorFlag, "psVectorHistogram() correctly initialized the bins");

        psFree(myData);
        psFree(myHist);
        psFree(myBounds);
        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    }
    return (1);
}

psS32 main()
{
    psLogSetFormat("HLNM");
    //
    // We list pertinent psStats.c functions here for debugging ease.
    //
    psTraceSetLevel(".", 0);
    psTraceSetLevel(".psLib", 0);
    psTraceSetLevel("p_psVectorBinDisect", 0);
    psTraceSetLevel("psHistogramAlloc", 0);
    psTraceSetLevel("psHistogramAllocGeneric", 0);
    psTraceSetLevel("UpdateHistogramBins", 0);
    psTraceSetLevel("psVectorHistogram", 0);
    plan_tests(32);

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