/*
 *  Copyright 2010 OpenSourceStewardshipFoundation.org
 *  Licensed under GNU General Public License version 2
 *
 * Author: seanhalle@yahoo.com
 *
 */
#include <stdio.h>
#include "Histogram.h"
#include "../vutilities.h"
#include "../vmalloc.h"


/*This Histogram Abstract Data Type has a number of bins plus a range of
 * values that the bins span, both chosen at creation.
 *
 *One creates a Histogram instance using the makeHistogram function, then
 * updates it with the addToHist function, and prints it out with the
 * printHist function.
 *
 *Note, the bin width is an integer, so the end of the range is adjusted
 * accordingly.  Use the bin-width to calculate the bin boundaries.
 */


Histogram *
makeHistogram( int32 numBins, int32 startOfRange, int32 endOfRange )

 {
   Histogram *hist;
   int32 i;


   hist = VMS__malloc( sizeof(Histogram) );
   hist->bins = VMS__malloc( numBins * sizeof(int32) );

   hist->numBins      = numBins;
   hist->binWidth     = (endOfRange - startOfRange) / numBins;
   hist->endOfRange   = startOfRange + hist->binWidth * numBins;
   hist->startOfRange = startOfRange;

   for( i = 0; i < hist->numBins; i++ )
    {
      hist->bins[ i ] = 0;
    }

   hist->name = NULL;
   return hist;
 }

inline void
makeHist_helper( Histogram *hist, int32 numBins,
                 int32 startOfRange, int32 binWidth, char *nameCopy )
 {
   hist->numBins      = numBins;
   hist->binWidth     = binWidth;
   hist->endOfRange   = startOfRange + hist->binWidth * numBins;
   hist->startOfRange = startOfRange;
   hist->name         = nameCopy;
   memset( hist->bins, 0, numBins * sizeof(int32) );
 }


Histogram *
makeFixedBinHist( int32 numBins, int32 startOfRange, int32 binWidth,
                  char *name )

 {
   Histogram *hist;

   hist = VMS__malloc( sizeof(Histogram) );
   hist->bins = VMS__malloc( numBins * sizeof(int32) );

   makeHist_helper( hist, numBins, startOfRange, binWidth,VMS__strDup(name));

   return hist;
 }

Histogram *
makeFixedBinHistExt( int32 numBins, int32 startOfRange, int32 binWidth,
                     char *name )

 {
   Histogram *hist;

   hist = malloc( sizeof(Histogram) );
   hist->bins = malloc( numBins * sizeof(int32) );

   makeHist_helper( hist, numBins, startOfRange, binWidth, strdup(name));

   return hist;
 }

void inline
addToHist( int32 value, Histogram *hist )
 {
   int32 binIdx;

   if( value < hist->startOfRange )
    { binIdx = 0;
    }
   else if( value > hist->endOfRange )
    { binIdx = hist->numBins - 1;
    }
   else
    {
      binIdx = (value - hist->startOfRange) / hist->binWidth;
    }

   hist->bins[ binIdx ] += 1;
 }

void inline
subFromHist( int32 value, Histogram *hist )
 {
   int32 binIdx;

   if( value < hist->startOfRange )
    { binIdx = 0;
    }
   else if( value > hist->endOfRange )
    { binIdx = hist->numBins - 1;
    }
   else
    {
      binIdx = (value - hist->startOfRange) / hist->binWidth;
    }

   hist->bins[ binIdx ] -= 1;
 }


/*Inline because use with RDTSC in innermost code so need ultra-fast
 */
void inline
addIntervalToHist( int32 startIntvl, int32 endIntvl, Histogram *hist )
 {
   int32 value;

   value = endIntvl - startIntvl;
   if( value < 0 || value > 10000000 ) return; //sanity check
   addToHist( value, hist );
 }

void inline
subIntervalFromHist( int32 startIntvl, int32 endIntvl, Histogram *hist )
 {
   int32 value;

   value = endIntvl - startIntvl;
   if( value < 0 || value > 10000000 ) return; //sanity check
   subFromHist( value, hist );
 }

void
printHist( Histogram *hist )
 {
   int32   binIdx, i, numBars, maxHeight, barValue, binStart, binEnd;
   float32 total, total2, binPercent, expectedValue1, expectedValue2;

   if( hist == NULL ) return;
   
      //do all except the top bin
   maxHeight = 0; total = 0.0; expectedValue1 = 0.0;
   for( i = 0; i < hist->numBins -1; i++ )
    {
      if( maxHeight < hist->bins[ i ] ) maxHeight = hist->bins[ i ];
      total += hist->bins[ i ];
      binStart = hist->startOfRange + hist->binWidth * i;
      expectedValue1 += hist->bins[ i ] * (binStart + hist->binWidth/2.0);
    }
      //copy and calc expected value minus the top bin
   expectedValue2  = expectedValue1;
   expectedValue2 /= total;
   total2          = total;
      //now do last iteration, to add the top bin
   if( maxHeight < hist->bins[ i ] ) maxHeight = hist->bins[ i ];
   total += hist->bins[ i ];
   binStart = hist->startOfRange + hist->binWidth * i;
   expectedValue1 += hist->bins[ i ] * (binStart + hist->binWidth/2.0);
   
   expectedValue1 /= total;
   
   barValue = maxHeight / 60;  //60 spaces across page for tallest bin

   printf( "histogram: " );
   if( hist->name != NULL ) printf( "%s\n", hist->name );
   else printf( "\n" );
   printf( "               num samples: %d | expected value: %3.2f \n",
                                               (int)total, expectedValue1 );
   printf( "minus top bin, num samples: %d | expected value: %3.2f \n",
                                               (int)total2, expectedValue2 );

   if( barValue == 0 ) { printf("error: bar val zero\n"); return; }
   for( binIdx = 0; binIdx < hist->numBins; binIdx++ )
    {
      binStart = hist->startOfRange + hist->binWidth * binIdx;
      binEnd = binStart + hist->binWidth - 1;
      binPercent = 100 * hist->bins[ binIdx ] / total;
      printf("bin range: %d - %d | %3.2f", binStart, binEnd, binPercent );

      numBars = hist->bins[ binIdx ] / barValue;
         //print one bin, height of bar is num dashes across page
      for( i = 0; i < numBars; i++ )
       {
         printf("-");
       }
      printf("\n");
    }
 }

void
freeHist( Histogram *hist )
 {
   VMS__free( hist->bins );
   VMS__free( hist->name );
   VMS__free( hist );
 }
void
freeHistExt( Histogram *hist )
 {
   free( hist->bins );
   free( hist->name );
   free( hist );
 }
