#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "logger.h"
#include "bindata.h"
#include "files.h"

void bindata_add(t_bindata bdata,double value)
{
  if (value < bdata->min || value > bdata->max) return;

  bindata_fastadd(bdata,value);
}






t_bindata bindata_create(int nbins)
{
  t_bindata bdata = (t_bindata)malloc(sizeof(s_bindata));
  bdata->min = 0.0;
  bdata->max = 0.0;
  bdata->count = 0;
  bdata->nbins = nbins;
  bdata->isize = 0.0;
  bdata->bins = (int*)malloc((nbins+1)*sizeof(int));
  bindata_reset(bdata);
  return(bdata);
}

void bindata_reset(t_bindata bdata)
{
  int i;
  for (i=0; i<bdata->nbins+1; ++i)
    bdata->bins[i]=0;
}

t_bindata bindata_create(double min,double max, int nbins)
{
  t_bindata bdata = bindata_create(nbins);
  bdata->min = min;
  bdata->max = max;
  bdata->isize = (double)bdata->nbins/(bdata->max - bdata->min);

  return(bdata);
}

t_bindata bindata_create(double min,double max, double bin_resolution)
{
  return( bindata_create(min,max, (int)((max-min)/bin_resolution) ) );
}


t_bindata bindata_create_stdin(int nbins)
{
  double *buffer;
  int bsize,blen;
  FILE *f;

  f=stdin;
  
  bsize = 1024;
  blen = 0;

  buffer=(double*)malloc(bsize * sizeof(double));

  skipcomments(f);
 
  double min,max,val;
  if (fscanf(f,"%lf",&buffer[0])==EOF) { printf("error: no data\n"); return(0x00); }

  min = buffer[0]; max = min;
  blen=1;
  while (fscanf(f,"%lf",&buffer[blen]) != EOF){
    if (buffer[blen] < min) min = buffer[blen];
    if (buffer[blen] > max) max = buffer[blen];
    ++blen;
    if (blen > 10000000) break; // if we already used up 80 megabytes, then exit loop
                                // we should issue a warning, since values lower or higher
                                // wont be counted
    if (blen>=bsize){
      bsize=bsize*2;
      buffer=(double*)realloc(buffer,bsize*sizeof(double));
      ldieif(buffer==0x00,"not enough memory for buffer");
    }
  }

  if (!nbins) nbins = (int)sqrt((float)blen);

  t_bindata bdata1 = bindata_create(min,max,nbins);

  // add buffered values
  int i;
  for (i=0; i<blen; ++i)
    bindata_fastadd(bdata1,buffer[i]);
  
  // add unread values, if any
  while (fscanf(f,"%lf",&val) != EOF)
    bindata_add(bdata1,val);

  return(bdata1);
}

t_bindata bindata_create(char *filename,int nbins)
{
  FILE *f;
//  int r;

  if (!strcmp(filename,"-"))
    return(bindata_create_stdin(nbins));

  f=fopen(filename,"r");
  
  ldieif(f==0x00,"unable to open file\n");

  skipcomments(f);
 
  double min,max,val;
  if (fscanf(f,"%lf",&val)==EOF) { printf("error: no data\n"); return(0x00); }

  int n;

  n=1;
  min = val; max = val;
  while (fscanf(f,"%lf",&val) != EOF){
    if (val < min) min = val;
    if (val > max) max = val;
    ++n;
  }
#ifndef __MINGW32CE__
  rewind(f);
#endif

  if (!nbins) nbins = (int)sqrt((float)n);

  t_bindata bdata1 = bindata_create(min,max,nbins);
  
  while (fscanf(f,"%lf",&val) != EOF)
    bindata_fastadd(bdata1,val);

  fclose(f);

  return(bdata1);
}

t_bindata bindata_create(double *values, int nvalues, double bin_res)
{
  int i;

  double min,max;

  min = *values; max = *values;

  for (i=1; i<nvalues; ++i){
    if (min > values[i]) min = values[i];
    if (max < values[i]) max = values[i];
  }

  t_bindata bdata1 = bindata_create(min,max,bin_res);
  for (i=0; i<nvalues; ++i)
    bindata_fastadd(bdata1,values[i]);

  return(bdata1);
}

void logbindata_add(t_bindata bdata1,double *values,int nvalues)
{
  int i;

  for (i=0; i<nvalues; ++i)
    bindata_fastadd(bdata1,log10(values[i]));
}

void bindata_add(t_bindata bdata1,double *values,int nvalues)
{
  int i;

  for (i=0; i<nvalues; ++i)
    bindata_fastadd(bdata1,values[i]);
}

void logbindata_check(t_bindata bdata, double *values, int nvalues)
{
  int i;
  double tmp;

  bindata_reset(bdata);

  bdata->min = log10(*values); bdata->max = bdata->min;
  for (i=1; i<nvalues; ++i){
    tmp = log10(values[i]);
    if (bdata->min > tmp) bdata->min = tmp;
    if (bdata->max < tmp) bdata->max = tmp;
  }
  bdata->isize = (double)bdata->nbins/(bdata->max - bdata->min);
}

void bindata_check(t_bindata bdata, double *values, int nvalues)
{
  int i;

  bindata_reset(bdata);

  bdata->min = *values; bdata->max = *values;
  for (i=1; i<nvalues; ++i){
    if (bdata->min > values[i]) bdata->min = values[i];
    if (bdata->max < values[i]) bdata->max = values[i];
  }
  bdata->isize = (double)bdata->nbins/(bdata->max - bdata->min);
}

t_bindata bindata_create(double *values, int nvalues,int nbins)
{
  if (!nbins) nbins = (int)sqrt((float)nvalues);

  t_bindata bdata1 = bindata_create(nbins);
  bindata_reset(bdata1);

  bindata_check(bdata1,values,nvalues);

  bindata_add(bdata1,values,nvalues);

  return(bdata1);
}

double logbindata_mode(t_bindata bdata)
{
  double bsize;
  bsize = (bdata->max-bdata->min)/bdata->nbins;

  int mbin;
  double mval;

  mbin=0;
  mval=bdata->bins[0];

  double tmp;
  int i;

  for (i=1; i<bdata->nbins; ++i){
    tmp = (double)mval/pow(10.0,(double)i*bsize + bdata->min + 0.5*bsize);
    if (mval < tmp){
      mval = tmp;
      mbin = i;
    }
  }

  return(pow(10.0,(double)mbin*bsize + bdata->min + 0.5*bsize));
}

double bindata_mode(t_bindata bdata)
{
  double bsize;
  bsize = (bdata->max-bdata->min)/bdata->nbins;

  int mbin;
  int mval;


  mbin=0;
  mval=bdata->bins[0];

  int i;

  for (i=1; i<bdata->nbins; ++i){
    if (mval < bdata->bins[i]){
      mval = bdata->bins[i];
      mbin = i;
    }
  }

  return((double)mbin*bsize + bdata->min);  
}

void bindata_print(t_bindata bdata)
{
  int i;
  double bsize;

  bsize = ((double)bdata->max-bdata->min)/bdata->nbins;
  
  for (i=0; i<bdata->nbins-1; ++i)
    printf("%lG %i\n",(double)i*bsize + bdata->min, bdata->bins[i]);
  // last bin includes values that are <max (bin[bdata->nbin]) or ==max (bin[bdata->nbin+1])
  if (i<bdata->nbins)
    printf("%lG %i\n",(double)i*bsize + bdata->min, bdata->bins[i]+bdata->bins[i+1]);
}

void bindata_printnormal(t_bindata bdata)
{
  int i;
  double bsize;

  bsize = ((double)bdata->max-bdata->min)/bdata->nbins;
  int count;
  count=0;
  for (i=0; i<bdata->nbins+1; ++i)
    count+=bdata->bins[i];

  double icount=1.0/(double)count;
  
  for (i=0; i<bdata->nbins-1; ++i)
    printf("%lG %lG\n",(double)i*bsize + bdata->min, (double)bdata->bins[i]*icount);
  // last bin includes values that are <max (bin[bdata->nbin]) or ==max (bin[bdata->nbin+1])
  if (i<bdata->nbins)
    printf("%lG %lG\n",(double)i*bsize + bdata->min, (double)(bdata->bins[i]+bdata->bins[i+1])*icount);
}

void logbindata_print(t_bindata bdata)
{
  int i;
  double bsize;

  bsize = ((double)bdata->max-bdata->min)/bdata->nbins;
  
  for (i=0; i<bdata->nbins; ++i)
    printf("%lG %i\n",pow(10.0,(double)i*bsize + bdata->min + 0.5*bsize), bdata->bins[i]);
  if (i<bdata->nbins)
    printf("%lG %i\n",pow(10.0,(double)i*bsize + bdata->min + 0.5*bsize), bdata->bins[i]+bdata->bins[i+1]);
}



