/*  histogram.cc

    Mark Woolrich, Matthew Webster and Emma Robinson, FMRIB Image Analysis Group

    Copyright (C) 1999-2000 University of Oxford  */

/*  CCOPYRIGHT  */

#include "miscmaths.h"
#include "histogram.h"

using namespace std;
using namespace NEWMAT;

namespace MISCMATHS {

  float Histogram::getPercentile(float perc)
  {
    if(histogram.Nrows()==0) generate();

    generateCDF();
    float percentile=getValue(1);

    for (int i=2;i<=CDF.Nrows();i++){
      if(CDF(i)>perc){
	double diff=(CDF(i)-perc)/(CDF(i)-CDF(i-1));
	percentile=getValue(i-1)+diff*(getValue(i)-getValue(i-1));
	break;
      }

    }
    return percentile;
  }

  void Histogram::generate()
  {
    Tracer ts("Histogram::generate");

    int size = sourceData.Nrows();

    if(calcRange)
      {
	// calculate range automatically
	histMin=histMax=sourceData(1);
	for(int i=1; i<=size; i++)
	  {
	    if (sourceData(i)>histMax)
	      histMax=sourceData(i);
	    if (sourceData(i)<histMin)
	      histMin=sourceData(i);
	  }
      }

  // zero histogram
    histogram.ReSize(bins);
    histogram=0;

    // create histogram; the MIN is so that the maximum value falls in the
    // last valid bin, not the (last+1) bin
    for(int i=1; i<=size; i++)
      {
      histogram(getBin(sourceData(i)))++;
      }

    datapoints=size;
  }

  void Histogram::generate(ColumnVector exclude)
  {
    Tracer ts("Histogram::generate");
    int size = sourceData.Nrows();
    if(calcRange)
      {
	// calculate range automatically
	histMin=histMax=sourceData(1);
	for(int i=1; i<=size; i++)
	  {
	    if (sourceData(i)>histMax)
	      histMax=sourceData(i);
	    if (sourceData(i)<histMin)
	      histMin=sourceData(i);
	  }
      }


    histogram.ReSize(bins);
    histogram=0;

    datapoints=size;

    for(int i=1; i<=size; i++)
      {
	if(exclude(i)>0){
	  histogram(getBin(sourceData(i)))++;
	}else datapoints--;

      }


  }


  void Histogram::smooth()
    {
      Tracer ts("Histogram::smooth");

      ColumnVector newhist=histogram;

      // smooth in i direction
      newhist=0;
      ColumnVector kernel(3);
      // corresponds to Gaussian with sigma=0.8 voxels
      //       kernel(1)=0.5;
      //       kernel(2)=0.2283;
      //       kernel(3)=0.0219;
      // corresponds to Gaussian with sigma=0.6 voxels
      //       kernel(1)=0.6638;
      //       kernel(2)=0.1655;
      //       kernel(3)=0.0026;

      //gauss(0.5,5,1)
      kernel(1)=0.7866;
      kernel(2)=0.1065;
      kernel(3)=0.0003;

      for(int i=1; i<=bins; i++)
	  {
	    float val=0.5*histogram(i);
	    float norm=kernel(1);

	    if(i>1)
	      {
		val+=kernel(2)*(histogram(i-1));
		norm+=kernel(2);
	      }
	    if(i>2)
	      {
		val+=kernel(3)*(histogram(i-2));
		norm+=kernel(3);
	      }
	    if(i<bins)
	      {
		val+=kernel(2)*(histogram(i+1));
		norm+=kernel(2);
	      }
	    if(i<bins-1)
	      {
		val+=kernel(3)*(histogram(i+2));
		norm+=kernel(3);
	      }
	    val/=norm;

	    newhist(i)=val;
	  }

      histogram=newhist;

    }

  int Histogram::integrate(float value1, float value2) const
    {
      int upperLimit = getBin(value2);
      int sum = 0;

      for(int i = getBin(value1)+1; i< upperLimit; i++)
	{
	  sum += (int)histogram(i);
	}
      return sum;
    }

  float Histogram::mode() const
    {
      int maxbin = 0;
      int maxnum = 0;

      for(int i = 1; i< bins; i++)
	{
	  if((int)histogram(i) > maxnum) {
	    maxnum = (int)histogram(i);
	    maxbin = i;
	  }
	}

      return getValue(maxbin);
    }



  void Histogram::match(Histogram &target){

    int bin, newbin;
    double cdfval, val,dist;
    ColumnVector CDF_ref;
    ColumnVector olddata;
    ColumnVector histnew(bins);
    vector<double> rangein,rangetarg;

    CDF_ref=target.getCDF();
    olddata=sourceData;
    histnew=0; dist=0;
    if(exclusion.Nrows()==0){cout << " Histogram::match no excl " << endl; exclusion.ReSize(sourceData.Nrows()); exclusion=1;}

    float binwidthtarget=(target.getHistMax() - target.getHistMin())/target.getNumBins();


    for (int i=1;i<=sourceData.Nrows();i++){

      if(exclusion(i)>0){

	bin=getBin(sourceData(i));

	cdfval=CDF(bin);
	newbin=1;

	val=sourceData(i)-histMin;

	if(bin==target.getNumBins()){
	  newbin=bin;
	}else if( (cdfval- CDF_ref(bin)) > 1e-20){


	  for (int j=bin;j<target.getNumBins();j++){

	    if((cdfval - CDF_ref(j)) > 1e-20 && (cdfval - CDF_ref(j+1)) <= 1e-20 ){
	      newbin=j+1;
	      dist=(cdfval-CDF_ref(j))/(CDF_ref(j+1)-CDF_ref(j)); // find distance between current bins as proportion of bin width
	     break;
	    }
	  }

	}
	else{

	  //search backwards



	  int j=bin-1;
	  while (j>0){

	    //  if(sourceData(i)<1.2 && sourceData(i)>1)
	    if((cdfval - CDF_ref(1)) < -1e-20 &&  fabs(CDF_ref(bin) - CDF_ref(1)) < 1e-20){newbin=j+1; break; }
	    else if ((cdfval - CDF_ref(1)) > -1e-20 &&  fabs(CDF_ref(bin) - CDF_ref(1)) < 1e-20){newbin=j+1; break; }
	    else if((cdfval - CDF_ref(j)) > 1e-20 && (cdfval - CDF_ref(j+1)) <= 1e-20 ){
	      newbin=j+1;


	      dist=(cdfval-CDF_ref(j))/(CDF_ref(j+1)-CDF_ref(j)); // find distance between current bins as proportion of bin width
	      break;
	    }
	    j--;
	  }
	  //search forwards
	}



	sourceData(i)=target.getHistMin() + (newbin-1)*binwidthtarget+dist*binwidthtarget;

	histnew(newbin)++;

	if(sourceData(i) < target.getHistMin()) sourceData(i) = target.getHistMin();
	if(sourceData(i) > target.getHistMax()) sourceData(i) = target.getHistMax();

      }
    }
  }
}