/*****************************************************************************
   Major portions of this software are copyrighted by the Medical College
   of Wisconsin, 1994-2000, and are released under the Gnu General Public
   License, Version 2.  See the file README.Copyright for details.
******************************************************************************/

#undef powerof2  /* needed for Mac OS X */

/*---------------------------------------------------------------------------*/
/*
  This file contains routines for performing wavelet analysis of
  time series data.

  File:    Wavelets.c
  Author:  B. Douglas Ward
  Date:    28 March 2000
*/

/*---------------------------------------------------------------------------*/
/*
  Include code for fast wavelet transforms.
*/

#include "Haar.c"
#include "Daubechies.c"


/*---------------------------------------------------------------------------*/
/*
   Print error message and stop.
*/

void WA_error (char * message)
{
  fprintf (stderr, "%s Error: %s \n", PROGRAM_NAME, message);
  exit(1);
}


/*---------------------------------------------------------------------------*/
/*
  Print time series data to screen.
*/

void ts_print (int npts, float * data)
{
  int i;


  for (i = 0;  i < npts;  i++)
    {
      printf ("%12.4f  ", data[i]);
      if (8*((i+1)/8) == i+1)  printf (" \n");
    }
      printf (" \n");

}


/*---------------------------------------------------------------------------*/
/*
  Write time series data to specified file.
*/

void ts_fprint (char * filename, int npts, float * data)
{
  int i;
  FILE * outfile = NULL;


  outfile = fopen (filename, "w");


  for (i = 0;  i < npts;  i++)
    {
      fprintf (outfile, "%f", data[i]);
      fprintf (outfile, " \n");
    }

  fclose (outfile);
}


/*---------------------------------------------------------------------------*/
/*
  Calculate integer power of 2.
*/

int powerof2 (int n)
{
  int i, j;

  j = 1;

  if (n > 0)
    for (i = 0;  i < n;  i++)
      j *= 2;
  else if (n < 0)
    j = 0;

  return (j);
}


/*---------------------------------------------------------------------------*/
/*
  Calculate integer log base 2.
*/

int my_log2 (int n)
{
  int i;
   
  i = floor (log(n)/log(2.0) + 1.0e-10);

  return (i);
}

/*---------------------------------------------------------------------------*/
/*
  Apply filter to wavelet coefficients.
*/

void FWT_1d_filter 
(
  float * filter,         /* array of filter coefficients */
  int N,                  /* log2(NPTS) */
  float * s               /* array of wavelet coefficients */ 
)

{
  int NPTS;        /* number of usable data points from input data */
  int ipts;        /* wavelet coefficient index */


  NPTS = powerof2 (N);

  for (ipts = 0;  ipts < NPTS;  ipts++)
    s[ipts] *= filter[ipts];

}


/*---------------------------------------------------------------------------*/
/*
  Set up array indicating which wavelet coefficients to set to zero.
*/

float * FWT_1d_stop_filter 
(
  int num_stop_filters,   /* number of wavelet stop filters */
  int * stop_band,        /* wavelet filter stop band */ 
  int * stop_mintr,       /* min. time for stop band */
  int * stop_maxtr,       /* max. time for stop band */
  int NFirst,             /* first image from input 3d+time dataset to use */
  int NPTS                /* number of usable data points from input data */
)

{
  int N;                       /* log2(NPTS) */
  int ipts;                    /* wavelet coefficient index */
  int band;                    /* frequency band */
  int mintr;                   /* min. value for time window (in TR) */
  int maxtr;                   /* max. value for time window (in TR) */
  int ifilter;                 /* filter index */
  float * stop_filter = NULL;  /* select wavelet coefficients to stop */


  N = my_log2(NPTS);
  stop_filter = (float *) malloc (sizeof(float) * NPTS);   MTEST (stop_filter);
  

  for (ipts = 0;  ipts < NPTS;  ipts++)
    {
      if (ipts == 0)
	{
	  band = -1;
	  mintr = 0;
	  maxtr = NPTS-1;
	}
      else
	{
	  band = my_log2(ipts);
	  mintr = (ipts - powerof2(band)) * powerof2(N-band);
	  maxtr = mintr + powerof2(N-band) - 1;
	}

      mintr += NFirst;
      maxtr += NFirst;

      stop_filter[ipts] = 1.0;
      for (ifilter = 0;  ifilter < num_stop_filters;  ifilter++)
	{
	  if (band == stop_band[ifilter])
	    {
	      if ((mintr >= stop_mintr[ifilter]) 
		  && (maxtr <= stop_maxtr[ifilter]))
		stop_filter[ipts] = 0.0;
	    }
	}

    }

  
  return (stop_filter);

}


/*---------------------------------------------------------------------------*/
/*
  Set up array indicating which wavelet coefficients to include in the model.
*/

float * FWT_1d_pass_filter
(
  int num_pass_filters,   /* number of wavelet pass filters */
  int * pass_band,        /* wavelet filter pass band */ 
  int * pass_mintr,       /* min. time for pass band */
  int * pass_maxtr,       /* max. time for pass band */
  int NFirst,             /* first image from input 3d+time dataset to use */
  int NPTS                /* number of usable data points from input data */
)

{
  int N;                       /* log2(NPTS) */
  int ipts;                    /* wavelet coefficient index */
  int band;                    /* frequency band */
  int mintr;                   /* min. value for time window (in TR) */
  int maxtr;                   /* max. value for time window (in TR) */
  int ifilter;                 /* filter index */
  float * pass_filter = NULL;  /* select wavelet coefficients to pass */


  N = my_log2 (NPTS);
  pass_filter = (float *) malloc (sizeof(float) * NPTS);   MTEST (pass_filter);
  

  for (ipts = 0;  ipts < NPTS;  ipts++)
    {
      if (ipts == 0)
	{
	  band = -1;
	  mintr = 0;
	  maxtr = NPTS-1;
	}
      else
	{
	  band = my_log2(ipts);
	  mintr = (ipts - powerof2(band)) * powerof2(N-band);
	  maxtr = mintr + powerof2(N-band) - 1;
	}

      mintr += NFirst;
      maxtr += NFirst;

      pass_filter[ipts] = 0.0;
      for (ifilter = 0;  ifilter < num_pass_filters;  ifilter++)
	{
	  if (band == pass_band[ifilter])
	    {
	      if ((mintr >= pass_mintr[ifilter]) 
		  && (maxtr <= pass_maxtr[ifilter]))
		pass_filter[ipts] = 1.0;
	    }
	}

    }

  
  return (pass_filter);

}


/*---------------------------------------------------------------------------*/
/*
  Calculate the error sum of squares.
*/

float calc_sse 
(
  int NPTS,         /* number of usable data points from input data */
  float * trueData,     /* actual time series data */
  float * est       /* estimated time series data */
)

{
  int ipt;          /* time point index */
  float diff;       /* difference between actual and estimated data */
  float sse;        /* estimation error sum of squares */

  sse = 0.0;
  for (ipt = 0;  ipt < NPTS;  ipt++)
    {
      diff = trueData[ipt] - est[ipt];
      sse += diff * diff;
    }
  
  return (sse);
}


/*---------------------------------------------------------------------------*/
/*
  Calculate the F-statistic for significance of the regression.
*/

float calc_freg
(
  int n,                      /* number of data points */
  int p,                      /* number of parameters in the full model */
  int q,                      /* number of parameters in the rdcd model */
  float ssef,                 /* error sum of squares from full model */
  float sser                  /* error sum of squares from reduced model */
)

{
  const float MAXF = 1000.0;         /* maximum value for F-statistic */
  const float EPSILON = 1.0e-2;      /* protection against divide by zero */
  float msef;                 /* mean square error for the full model */
  float msreg;                /* mean square due to the regression */
  float freg;                 /* F-statistic for the full regression model */


  /*----- Order of reduced model = order of full model ??? -----*/
  if (p <= q)   return (0.0);


  /*----- Calculate numerator and denominator of F-statistic -----*/
  msreg = (sser - ssef) / (p - q);    if (msreg < 0.0)  msreg = 0.0;
  msef   = ssef / (n - p);            if (msef  < 0.0)  msef  = 0.0;


  if (msef < EPSILON)
    freg = 0.0;
  else
    if (msreg > MAXF*msef)  freg = MAXF;
    else                    freg = msreg / msef;


  /*----- Limit range of values for F-statistic -----*/
  if (freg < 0.0)   freg = 0.0;
  if (freg > MAXF)  freg = MAXF;


  /*----- Return F-statistic for significance of the regression -----*/
  return (freg);

}


/*---------------------------------------------------------------------------*/
/*
  Calculate the coefficient of multiple determination R^2.
*/

float calc_rsqr 
(
  float ssef,                 /* error sum of squares from full model */
  float sser                  /* error sum of squares from reduced model */
)

{
  const float EPSILON = 1.0e-2;     /* protection against divide by zero */
  float rsqr;                       /* coeff. of multiple determination R^2  */


  /*----- coefficient of multiple determination R^2 -----*/
  if (sser < EPSILON)
    rsqr = 0.0;
  else
    rsqr = (sser - ssef) / sser;


  /*----- Limit range of values for R^2 -----*/
  if (rsqr < 0.0)   rsqr = 0.0;
  if (rsqr > 1.0)   rsqr = 1.0;


  /*----- Return coefficient of multiple determination R^2 -----*/
  return (rsqr);
}


/*---------------------------------------------------------------------------*/
/*
  Perform wavelet analysis on a single input time series.
*/

void wavelet_analysis 
(
  int wavelet_type,         /* indicates type of wavelet */   
  int f,                    /* number of parameters removed by the filter */
  float * stop_filter,      /* select wavelet coefs. to stop */
  int q,                    /* number of parameters in the baseline model */
  float * base_filter,      /* select wavelet coefs. for baseline */
  int p,                    /* number of parameters in the full model */
  float * full_filter,      /* select wavelet coefs. for full model */
  int NPTS,                 /* number of usable data points from input data */
  float * ts_array,         /* array of measured data for one voxel */

  float * coef,             /* full model wavelet coefficients */
  float * sse_base,         /* baseline model error sum of squares */
  float * sse_full,         /* full model error sum of squares */
  float * ffull,            /* full model F-statistic */
  float * rfull,            /* full model R^2 stats. */

  float * coefts,           /* filtered FWT coefficients */
  float * fitts,            /* filtered time series */
  float * sgnlts,           /* signal model fitted time series */
  float * errts             /* residual error time series */
)

{
  int N;                    /* log2(NPTS) */
  int it;                   /* time point index */
  int ip;                   /* full model parameter index */
  float * filtts = NULL;    /* stop filtered time series */
  float * basefwt = NULL;   /* baseline model FWT coefficients */
  float * basets = NULL;    /* baseline model time series */
  float * fullfwt = NULL;   /* full model FWT coefficients */
  float * fullts = NULL;    /* full model time series */

    
  /*----- Initialize local variables -----*/
  N = my_log2(NPTS);


  /*----- Allocate memory for arrays -----*/
  filtts     = (float *) malloc (sizeof(float) * NPTS);    MTEST (filtts);
  basefwt    = (float *) malloc (sizeof(float) * NPTS);    MTEST (basefwt);
  basets     = (float *) malloc (sizeof(float) * NPTS);    MTEST (basets);
  fullfwt    = (float *) malloc (sizeof(float) * NPTS);    MTEST (fullfwt);
  fullts     = (float *) malloc (sizeof(float) * NPTS);    MTEST (fullts);


  /*----- Forward Fast Wavelet Transform -----*/
  for (it = 0;  it < NPTS;  it++)
    coefts[it] = ts_array[it];
  switch (wavelet_type)
    {
    case WA_HAAR:
      Haar_forward_FWT_1d (N, coefts);
      break;

    case WA_DAUBECHIES:
      Daubechies_forward_FWT_1d (N, coefts);
      break;
    }


  /*----- Apply stop filter to wavelet transform coefficients -----*/
  FWT_1d_filter (stop_filter, N, coefts);


  /*----- Inverse Fast Wavelet Transform of filtered FWT -----*/
  for (it = 0;  it < NPTS;  it++)
    filtts[it] = coefts[it];
  switch (wavelet_type)
    {
    case WA_HAAR:
      Haar_inverse_FWT_1d (N, filtts);
      break;

    case WA_DAUBECHIES:
      Daubechies_inverse_FWT_1d (N, filtts);
      break;
    }


  /*----- Apply baseline pass filter to wavelet transform coefficients -----*/
  for (it = 0;  it < NPTS;  it++)
    basefwt[it] = coefts[it];
  FWT_1d_filter (base_filter, N, basefwt);


 /*----- Inverse Fast Wavelet Transform of baseline FWT -----*/
  for (it = 0;  it < NPTS;  it++)
    basets[it] = basefwt[it];
  switch (wavelet_type)
    {
    case WA_HAAR:
      Haar_inverse_FWT_1d (N, basets);
      break;

    case WA_DAUBECHIES:
      Daubechies_inverse_FWT_1d (N, basets);
      break;
    }


  /*----- Calculate error sum of squares (SSE) for baseline model -----*/
  *sse_base = calc_sse (NPTS, filtts, basets);


  /*----- Apply full model filter to wavelet transform coefficients -----*/
  for (it = 0;  it < NPTS;  it++)
    fullfwt[it] = coefts[it];
  FWT_1d_filter (full_filter, N, fullfwt);


  /*----- Save full model wavelet coefficients -----*/
  ip = 0;
  for (it = 0;  it < NPTS;  it++)
    if (full_filter[it] == 1.0)
      {
	coef[ip] = fullfwt[it];
	ip++;
	if (ip >= p)  break;
      }
      

 /*----- Inverse Fast Wavelet Transform of full model FWT -----*/
  for (it = 0;  it < NPTS;  it++)
    fullts[it] = fullfwt[it];
  switch (wavelet_type)
    {
    case WA_HAAR:
      Haar_inverse_FWT_1d (N, fullts);
      break;

    case WA_DAUBECHIES:
      Daubechies_inverse_FWT_1d (N, fullts);
      break;
    }


  /*----- Calculate error sum of squares (SSE) for signal model -----*/
  *sse_full = calc_sse (NPTS, filtts, fullts);


  /*----- Calculate coefficient of multiple determination R^2 -----*/
  *rfull = calc_rsqr (*sse_full, *sse_base);


  /*----- Calculate F-statistic for significance of the signal model -----*/
  *ffull = calc_freg (NPTS-f, p, q, *sse_full, *sse_base);


  /*----- Calculate residual errors -----*/
  for (it = 0;  it < NPTS;  it++)
    {
      if (p == 0)
	errts[it] = ts_array[it] - filtts[it];
      else
	errts[it] = filtts[it] - fullts[it];
    }


  /*----- Calculate "pure" signal time series -----*/
  for (it = 0;  it < NPTS;  it++)
    sgnlts[it] = fullts[it] - basets[it];


  /*----- Save the fitted time series -----*/
  for (it = 0;  it < NPTS;  it++)
    {
      if (p == 0)
	fitts[it] = filtts[it];
      else
	fitts[it] = fullts[it];
    }


  /*----- Release memory -----*/
  free (filtts);      filtts = NULL;
  free (basefwt);     basefwt = NULL;
  free (basets);      basets = NULL;
  free (fullfwt);     fullfwt = NULL;
  free (fullts);      fullts = NULL;


  return;

}


/*---------------------------------------------------------------------------*/
/*
  Convert F-value to p-value.  
  This routine was copied from: mri_stats.c - linked with libmri.a?
*/

#if 0
double fstat_t2p( double ff , double dofnum , double dofden )
{
   int which , status ;
   double p , q , f , dfn , dfd , bound ;

   which  = 1 ;
   p      = 0.0 ;
   q      = 0.0 ;
   f      = ff ;
   dfn    = dofnum ;
   dfd    = dofden ;

   cdff( &which , &p , &q , &f , &dfn , &dfd , &status , &bound ) ;

   if( status == 0 ) return q ;
   else              return 1.0 ;
}
#endif

/*---------------------------------------------------------------------------*/
/*
  Report the results of wavelet analysis for a single time series.
*/

static char lbuf[4096];   /* character string containing statistical summary */
static char sbuf[256];

void report_results 
(
  int N,                /* number of usable data points from input data */
  int NFirst,           /* first image from input 3d+time dataset to use */
  int f,                /* number of parameters removed by the filter */
  int p,                /* number of parameters in the full model */
  int q,                /* number of parameters in the baseline model */
  float * base_filter,  /* select wavelet coefs. for baseline */
  float * sgnl_filter,  /* select wavelet coefs. for full model */
  float * coef,         /* full model wavelet coefficients */
  float sse_base,       /* baseline model error sum of squares */
  float sse_full,       /* full model error sum of squares */
  float ffull,          /* full model F-statistic */
  float rfull,          /* full model R^2 stat. */
  char ** label         /* statistical summary for ouput display */
)

{
  int it;               /* time index */
  int icoef;            /* coefficient index */
  double pvalue;        /* p-value */
  

  /** 22 Apr 1997: create label if desired by AFNI         **/
  /** [This is in static storage, since AFNI will copy it] **/
  
  if( label != NULL ){  /* assemble this 1 line at a time from sbuf */
    
    lbuf[0] = '\0' ;   /* make this a 0 length string to start */
    
    /** for each reference, make a string into sbuf **/
    

  /*----- Display wavelet coefficients for full model -----*/
  icoef = 0;
  for (it = 0;  it < N;  it++)        
    {                      
      if (sgnl_filter[it])
	{                             
	  {
	    int band, mintr, maxtr;

	    if (it == 0)
	      {
		band = -1;
		mintr = 0;
		maxtr = N-1;
	      }
	    else
	      {
		band = my_log2(it);
		mintr = (it - powerof2(band)) * powerof2(my_log2(N)-band);
		maxtr = mintr + powerof2(my_log2(N)-band) - 1;
	      }
	    
	    mintr += NFirst;
	    maxtr += NFirst;
	    
	    if (base_filter[it])
	      sprintf (sbuf, "B(%2d)[%3d,%3d] = %f \n", 
		       band, mintr, maxtr, coef[icoef]);
	    else
	      sprintf (sbuf, "S(%2d)[%3d,%3d] = %f \n", 
		       band, mintr, maxtr, coef[icoef]);
	  }

	  strcat(lbuf,sbuf);

	  icoef++;
	}
	    
    }  /* End loop over Wavelet coefficients */


    /*----- Statistical results for baseline fit -----*/ 
    sprintf (sbuf, "\nBaseline: \n");
    strcat(lbuf,sbuf);
    sprintf (sbuf, "# params  = %4d \n", q);
    strcat (lbuf, sbuf);
    sprintf (sbuf, "SSE       = %10.3f \n", sse_base);
    strcat (lbuf, sbuf);
    sprintf (sbuf, "MSE       = %10.3f \n", sse_base/(N-f-q));
    strcat (lbuf, sbuf);
 

     /*----- Statistical results for full model -----*/
    sprintf (sbuf, "\nFull Model: \n");
    strcat (lbuf, sbuf);
    sprintf (sbuf, "# params  = %4d \n", p);
    strcat (lbuf, sbuf);
    sprintf (sbuf, "SSE       = %10.3f \n", sse_full);
    strcat (lbuf, sbuf);
    sprintf (sbuf, "MSE       = %10.3f \n", sse_full/(N-f-p));
    strcat (lbuf, sbuf);
    

     /*----- Summary statistics -----*/
    sprintf (sbuf, "\nSummary Statistics: \n");
    strcat (lbuf, sbuf);

    sprintf (sbuf, "R^2       = %10.3f \n", rfull);
    strcat (lbuf, sbuf);
    
    sprintf (sbuf, "F[%2d,%3d] = %10.3f \n", p-q, N-f-p, ffull);
    strcat (lbuf, sbuf);

    pvalue = fstat_t2p ( (double) ffull, (double) p-q, (double) N-f-p);
    sprintf (sbuf, "p-value   = %e  \n", pvalue);
    strcat (lbuf, sbuf);
    
    
    *label = lbuf ;  /* send address of lbuf back in what label points to */
  }
  
}


/*---------------------------------------------------------------------------*/







