/*
** read nifti-1 or nifti-2 to vista format
**
** G.Lohmann, MPI-KYB, 2016
*/
#include <viaio/Vlib.h>
#include <viaio/VImage.h>
#include <viaio/mu.h>
#include <viaio/option.h>

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

#include <gsl/gsl_math.h>

#include <nifti/nifti2.h>
#include <nifti/nifti1_io.h>

#define MIN_HEADER_SIZE 348
#define NII_HEADER_SIZE 352

#define ABS(x) ((x) > 0 ? (x) : -(x))

/*extern int isinff(float x);
  extern int isnanf(float x);
*/
extern char *VReadGzippedData(char *filename,size_t *len);
extern char *VReadUnzippedData(char *filename,VBoolean nofail,size_t *size);
extern char *VReadDataContainer(char *filename,VBoolean nofail,size_t *size);
extern FILE *VReadInputFile (char *filename,VBoolean nofail);
extern int  CheckGzip(char *filename);

extern void VByteSwapNiftiHeader(nifti_1_header *hdr);
extern void VByteSwapNifti2Header(nifti_2_header *hdr);
extern void VByteSwapData(char *data,size_t ndata,size_t nsize);
extern float strtof(const char *str, char **endptr);

int NiftiVersion(char *databuffer,int *swap)
{
  int *k = (int *)(&databuffer[0]);
  int type = *k;
  *swap = 0;
  if (type == 348) return 1;
  else if (type == 540) return 2;
  else {
    nifti_swap_4bytes(1,k);
    type = *k;
    *swap = 1;
    if (type == 348) return 1;
    else if (type == 540) return 2;
    else return 0;
  }
  return 0;
}

/* read repetition time from description field instead of pixdim  */
float TRfromString(const char *str)
{
  float f_tr   = -1;    /* return value on failure */
  float factor = 1000;  /* expect seconds as default */
  char* c_tr;

  if( ( c_tr = strstr( str, "TR=" ) ) || ( c_tr = strstr( str, "TR =" ) ) ) {
      while( *c_tr++ != '=' )
    ;

      while( *c_tr && isspace( *c_tr ) )
	c_tr++;

      f_tr = strtof( c_tr, &c_tr );

      if( f_tr > 0.0 ) {
	while( *c_tr && isspace( *c_tr ) )
	  c_tr++;

	if( strncasecmp( c_tr, "ms", 2 ) == 0 )
	  factor = 1;
      }
  }
  return f_tr * factor;
}

/* set dimension in geo info */
void VUpdateGeoinfo(VAttrList geolist,int dimtype,VLong tr)
{
  double *D = VGetGeoDim(geolist,NULL);
  D[0] = (double)dimtype;
  VSetGeoDim(geolist,D);

  double *E = VGetGeoPixdim(geolist,NULL);
  if (tr > 0) {
    E[4] = (double)tr;
    VSetGeoPixdim(geolist,E);
  }
}

/*
 *  VIniImage
 *
 *  Allocate Image structure and fill voxel values with databuffer.
 *  Returns a pointer to the image if successful, zero otherwise.
 *  Useful for converting from 4D nifti to list of 3D vista images
 */
VImage VIniImage (int nbands, int nrows, int ncolumns, VRepnKind pixel_repn, char *databuffer)
{
  size_t row_size = ncolumns * VRepnSize (pixel_repn);
  size_t row_index_size = nbands * nrows * sizeof (char *);
  size_t band_index_size = nbands * sizeof (char **);
  size_t pixel_size=0;
  VImage image;
  int band=0, row=0;
  char *p = NULL;

  /* Check parameters: */
  if (nbands < 1) VError("VIniImage: Invalid number of bands: %d", (int) nbands);
  if (nrows < 1)  VError ("VIniImage: Invalid number of rows: %d", (int) nrows);
  if (ncolumns < 1) VError ("VIniImage: Invalid number of columns: %d",(int) ncolumns);

#define AlignUp(v, b) ((((v) + (b) - 1) / (b)) * (b))

  /* Initialize VImage data struct */
  pixel_size = VRepnSize (pixel_repn);
  p = VMalloc(AlignUp (sizeof (VImageRec) + row_index_size + band_index_size, pixel_size));

  image = (VImage) p;
  image->nbands = nbands;
  image->nrows  = nrows;
  image->ncolumns = ncolumns;
  image->flags    = VImageSingleAlloc;
  image->pixel_repn = pixel_repn;
  image->attributes = VCreateAttrList ();
  image->band_index = (VPointer **) (p += sizeof (VImageRec));
  image->row_index  = (VPointer *) (p += band_index_size);
  image->data = (VPointer) AlignUp((long) (databuffer+4), pixel_size);

  image->nframes = nbands;
  image->nviewpoints = image->ncolors = image->ncomponents = 1;

  /* Initialize the indices: */
  for (band = 0; band < nbands; band++)
    image->band_index[band] = image->row_index + band * nrows;
  for (row = 0, p = image->data; row < nbands * nrows; row++, p += row_size)
    image->row_index[row] = p;

  return image;

#undef AlignUp
}

#define xgetval(data,index,Datatype) \
{ \
  Datatype *k = (Datatype *)(&data[index]); \
  u = (float)(*k); \
}

float VGetValue(char *data,size_t index,int datatype)
{
  float u=0;
  switch(datatype) {
  case DT_BINARY:
    xgetval(data,index,VBit);
    break;
  case DT_UNSIGNED_CHAR:
    xgetval(data,index,VUByte);
    break;
  case DT_SIGNED_SHORT:
    xgetval(data,index,short);
    break;
  case DT_SIGNED_INT:
    xgetval(data,index,int);
    break;
  case DT_FLOAT:
    xgetval(data,index,float);
    break;
  case DT_DOUBLE:
    xgetval(data,index,double);
    break;
  case DT_INT8:
    xgetval(data,index,VSByte);
    break;
  case DT_UINT16:
    xgetval(data,index,unsigned short);
    break;
  case DT_UINT32:
    xgetval(data,index,unsigned int);
    break;
  case DT_INT64:
    xgetval(data,index,long);
    break;
  case DT_UINT64:
    xgetval(data,index,unsigned long);
    break;
  default:
    VError(" unknown datatype %d",datatype);
  }
  if (isinf(u) || isnan(u))
    u = 0;
  return u;
}

void VCleanData(VImage src)
{
  int b,r,c;
  double u=0;
  for (b=0; b<VImageNBands(src); b++) {
    for (r=0; r<VImageNRows(src); r++) {
      for (c=0; c<VImageNColumns(src); c++) {
	u = VGetPixel(src,b,r,c);
	if (isinf(u) || isnan(u)) {
	  u = 0;
	  VSetPixel(src,b,r,c,u);
	}
      }
    }
  }
}

/* get image statistics for re-scaling parameters */
void VDataStats(char *data,size_t ndata,size_t nsize,int datatype,float *xmin,float *xmax)
{
  size_t i,n;
  float u=0;
  float zmin = VRepnMaxValue(VFloatRepn);
  float zmax = VRepnMinValue(VFloatRepn);

  n=0;
  for (i=0; i<ndata; i+= nsize) {
    u = VGetValue(data,i,datatype);
    if (fabs(u) < TINY) continue;
    if (u < zmin) zmin = u;
    if (u > zmax) zmax = u;
    n++;
  }
  if (n < 1) VError(" no non-zero data points found");

  *xmin = (zmin+TINY);
  *xmax = (zmax-TINY);
}

/* list of 3D images */
void Nii2Vista3DList(char *data,size_t nsize,size_t nslices,size_t nrows,size_t ncols,size_t nt,
		     VRepnKind dst_repn,int datatype,float scl_slope,float scl_inter,
		     VString voxelstr,VLong tr,VAttrList out_list)
{
  int slice,row,col;
  float u=0;
  size_t i;
  size_t add=0;
  size_t nrnc = nrows*ncols;
  size_t npix = nrnc*nslices;
  size_t ndata = nt * npix * nsize;

  if (nsize == 1) add=4;
  if (nsize == 2) add=2;
  if (nsize == 4) add=1;

  VImage *dst = (VImage *) VCalloc(nt,sizeof(VImage));
  if (nt > 1) VAppendAttr(out_list,"nimages",NULL,VLongRepn,(VLong) nt);

  for (i=0; i<nt; i++) {

    /* rescale if needed */
    if (fabs(scl_slope) > 0 || fabs(scl_inter) > 0) {

      VImage tmp = VCreateImage(nslices,nrows,ncols,dst_repn);
      VSetAttr(VImageAttrList(tmp),"voxel",NULL,VStringRepn,voxelstr);
      if (tr > 0) VSetAttr(VImageAttrList(tmp),"repetition_time",NULL,VLongRepn,(VLong)tr);
      for (slice=0; slice<nslices; slice++) {
	for (row=0; row<nrows; row++) {
	  for (col=0; col<ncols; col++) {
	    const size_t src_index = (col + row*ncols + slice*nrnc + i*npix)*nsize;
	    if (src_index >= ndata || src_index < 0) continue;
	    u = VGetValue(data,src_index,datatype);
	    u = scl_slope*u + scl_inter;
	    VSetPixel(tmp,slice,row,col,(double)u);
	  }
	}
      }
      VAppendAttr(out_list,"image",NULL,VImageRepn,tmp);
    }

    /* otherwise just copy */
    else {
      const size_t index = (i*npix-add)*nsize;
      dst[i] = VIniImage(nslices,nrows,ncols,dst_repn,&data[index]);
      VSetAttr(VImageAttrList(dst[i]),"voxel",NULL,VStringRepn,voxelstr);
      if (tr > 0) VSetAttr(VImageAttrList(dst[i]),"repetition_time",NULL,VLongRepn,(VLong)tr);
      VAppendAttr(out_list,"image",NULL,VImageRepn,dst[i]);
      VCleanData(dst[i]);
    }
  }
}

/* 4D time series data */
void Nii2Vista4D(char *data,size_t nsize,size_t nslices,size_t nrows,size_t ncols,size_t nt,
		 VRepnKind dst_repn,int datatype,VBoolean do_scaling,float scl_slope,float scl_inter,
		 VString voxelstr,double *slicetime,VLong tr,VAttrList out_list)
{
  size_t slice,row,col,ti;
  size_t nrnc = nrows*ncols;
  size_t npix = nrnc*nslices;
  size_t ndata = nt * npix * nsize;
  size_t add=0;
  float u=0;

  /* rescale to 16bit integer if needed */
  float xmin=0,xmax=0,umin=0,umax=0;
  if (do_scaling && dst_repn != VShortRepn) {
    dst_repn = VShortRepn;
    VDataStats(data,ndata,nsize,datatype,&xmin,&xmax);
    fprintf(stderr," data range: [%f, %f]\n",xmin,xmax);
    umin = 0;
    umax = VRepnMaxValue(VShortRepn);
  }

  VImage *dst = (VImage *) VCalloc(nslices,sizeof(VImage));

  for (slice=0; slice<nslices; slice++) {
    dst[slice] = VCreateImage(nt,nrows,ncols,dst_repn);
    if (!dst[slice]) VError(" err allocating image");
    VFillImage(dst[slice],VAllBands,0);

    VSetAttr(VImageAttrList(dst[slice]),"voxel",NULL,VStringRepn,voxelstr);
    if (tr > 0) VSetAttr(VImageAttrList(dst[slice]),"repetition_time",NULL,VLongRepn,(VLong)tr);
    if (slicetime != NULL)
      VSetAttr(VImageAttrList(dst[slice]),"slice_time",NULL,VShortRepn,(VShort)slicetime[slice]);

    for (ti=0; ti<nt; ti++) {
      for (row=0; row<nrows; row++) {
	for (col=0; col<ncols; col++) {
	  const size_t src_index = (col + row*ncols + slice*nrnc + ti*npix + add)*nsize;
	  if (src_index >= ndata || src_index < 0) continue;

	  if (!do_scaling) {
	    u = VGetValue(data,src_index,datatype);
	    if (fabs(scl_slope) > 0 || fabs(scl_inter) > 0) {
	      u = scl_slope*u + scl_inter;
	    }
	    if (VPixelRepn(dst[slice]) == VShortRepn) {
	      VPixel(dst[slice],ti,row,col,VShort) = u;
	    }
	    else if (VPixelRepn(dst[slice]) == VFloatRepn) {
	      VPixel(dst[slice],ti,row,col,VFloat) = u;
	    }
	    else {
	      VSetPixel(dst[slice],ti,row,col,(double)u);
	    }
	  }
	  else {
	    u = VGetValue(data,src_index,datatype);
	    u = umax * (u-xmin)/(xmax-xmin);
	    if (u < umin) u = umin;
	    if (u > umax) u = umax;
	    VPixel(dst[slice],ti,row,col,VShort) = (VShort)u;
	  }
	}
      }
    }
    VCleanData(dst[slice]);
    VAppendAttr(out_list,"image",NULL,VImageRepn,dst[slice]);
  }
}

/* copy nifti header infos to geolist in vista header */
double *VGetNiftiHeader(VAttrList geolist,nifti_2_header hdr,VLong tr)
{

  /* units in lipsia: mm and sec */
  char xyzt = hdr.xyzt_units;
  int spaceunits = XYZT_TO_SPACE(xyzt);
  int timeunits  = XYZT_TO_TIME(xyzt);
  double xscale  = 1.0;
  double tscale  = 1.0;

  if (spaceunits == NIFTI_UNITS_MICRON) xscale=1000.0;
  if (timeunits == NIFTI_UNITS_SEC) tscale=1000.0;

  /* dim info */
  VSetAttr(geolist,"dim_info",NULL,VShortRepn,(VShort)hdr.dim_info);

  /* dim */
  /* hdr.dim[0]==3 means dim=3,  hdr.dim[0]==4 means dim=4 (timeseries) */
  float *E = VCalloc(8,sizeof(float));
  VAttrList elist = VCreateAttrList();
  VBundle ebundle = VCreateBundle ("bundle",elist,8*sizeof(float),(VPointer)E);
  VSetAttr(geolist,"dim",NULL,VBundleRepn,ebundle);
  int i;
  for (i=0; i<8; i++) E[i] = hdr.dim[i];
  for (i=5; i<8; i++) E[i] = 0;

  /* pixdim */
  float *D = VCalloc(8,sizeof(float));
  for (i=0; i<8; i++) D[i] = hdr.pixdim[i];
  for (i=1; i<=3; i++) D[i] *= xscale;
  D[4] *= tscale;
  if (tr > 0) D[4] = (double)tr;

  /* override if TR info is in description field */
  float xtr = TRfromString(hdr.descrip);
  if (xtr > 0) {
    D[4] = (double)(xtr*tscale);
    fprintf(stderr," reading TR from description field, TR= %.4f ms\n",D[4]);
  }
  if (fabs(D[0]) < 0.0001) D[0] = 1.0;  /* if not specified, assume pixdim[0] = 1 */

  for (i=5; i<8; i++) D[i] = 0;
  VAttrList dlist = VCreateAttrList();
  VBundle dbundle = VCreateBundle ("bundle",dlist,8*sizeof(float),(VPointer)D);
  VSetAttr(geolist,"pixdim",NULL,VBundleRepn,dbundle);

  /* sform */
  VImage sform = VCreateImage(1,4,4,VFloatRepn);
  VFillImage (sform,VAllBands,0);
  int j;
  for (j=0; j<4; j++) {
    VPixel(sform,0,0,j,VFloat) = hdr.srow_x[j];
    VPixel(sform,0,1,j,VFloat) = hdr.srow_y[j];
    VPixel(sform,0,2,j,VFloat) = hdr.srow_z[j];
  }
  VSetAttr(geolist,"sform_code",NULL,VShortRepn,(VShort)hdr.sform_code);
  VSetAttr(geolist,"sform",NULL,VImageRepn,sform);

  /* qform */
  size_t dim=6;
  float *Q = VCalloc(dim,sizeof(float));
  VAttrList qlist = VCreateAttrList();
  VBundle qbundle = VCreateBundle ("bundle",qlist,dim*sizeof(float),(VPointer)Q);
  Q[0] = hdr.quatern_b;
  Q[1] = hdr.quatern_c;
  Q[2] = hdr.quatern_d;
  Q[3] = hdr.qoffset_x;
  Q[4] = hdr.qoffset_y;
  Q[5] = hdr.qoffset_z;

  VShort qform_code = hdr.qform_code;
  if (hdr.sform_code==0 && qform_code==0) qform_code = 1;    /* if both codes are unspecified, assume scanner coord */
  VSetAttr(geolist,"qform_code",NULL,VShortRepn,(VShort)qform_code);
  VSetAttr(geolist,"qform",NULL,VBundleRepn,qbundle);

  /* MRI encoding directions */
  int freq_dim=0,phase_dim=0,slice_dim=0;
  if (hdr.dim_info != 0) {
    freq_dim  = DIM_INFO_TO_FREQ_DIM (hdr.dim_info );
    phase_dim = DIM_INFO_TO_PHASE_DIM(hdr.dim_info );
    slice_dim = DIM_INFO_TO_SLICE_DIM(hdr.dim_info );
    VSetAttr(geolist,"freq_dim",NULL,VShortRepn,(VShort)freq_dim);
    VSetAttr(geolist,"phase_dim",NULL,VShortRepn,(VShort)phase_dim);
    VSetAttr(geolist,"slice_dim",NULL,VShortRepn,(VShort)slice_dim);
  }
  if (slice_dim == 0) return NULL;

  /* slicetiming information */
  int slice_start = hdr.slice_start;
  int slice_end = hdr.slice_end;
  int slice_code = hdr.slice_code;
  double slice_duration = hdr.slice_duration;
  if (slice_code == 0 || slice_duration < 1.0e-10) return NULL;
  fprintf(stderr," slice duration: %.2f ms\n", slice_duration);
  VSetAttr(geolist,"slice_start",NULL,VShortRepn,(VShort)slice_start);
  VSetAttr(geolist,"slice_end",NULL,VShortRepn,(VShort)slice_end);
  VSetAttr(geolist,"slice_code",NULL,VShortRepn,(VShort)slice_code);
  VSetAttr(geolist,"slice_duration",NULL,VFloatRepn,(VFloat)slice_duration);

  int nslices = (int)E[3];
  double *slicetimes = (double *) VCalloc(nslices,sizeof(double));
  VGetSlicetimes(slice_start,slice_end,slice_code,slice_duration,slicetimes);
  return slicetimes;
}

VAttrList Nifti1_to_Vista(char *databuffer,VLong tr,VBoolean attrtype,VBoolean do_scaling,VBoolean *ok)
{
  /* read header */
  nifti_1_header hdr1;
  nifti_2_header hdr;
  int i=0,swap=0;
  size_t header_size=0;

  int nifti_version = NiftiVersion(databuffer,&swap);
  if (nifti_version < 1) VError(" unknown nifti version");
  /* fprintf(stderr," nifti version: %d\n",nifti_version); */

  if (nifti_version == 1) {  /* nifti-1 */
    header_size = 348;
    memcpy(&hdr1,databuffer,header_size);
    swap = NIFTI_NEEDS_SWAP(hdr1);
    if (swap == 1) VByteSwapNiftiHeader(&hdr1);

    hdr.datatype = hdr1.datatype;
    for (i=0; i<8; i++) hdr.dim[i]=hdr1.dim[i];
    for (i=0; i<8; i++) hdr.pixdim[i]=hdr1.pixdim[i];
    hdr.bitpix=hdr1.bitpix;
    hdr.vox_offset=hdr1.vox_offset;
    hdr.intent_p1 = hdr1.intent_p1;
    hdr.intent_p2 = hdr1.intent_p2;
    hdr.intent_p3 = hdr1.intent_p3;
    hdr.scl_slope = hdr1.scl_slope;
    hdr.scl_inter = hdr1.scl_inter;
    hdr.slice_duration = hdr1.slice_duration;
    hdr.slice_start = hdr1.slice_start;
    hdr.slice_end = hdr1.slice_end;
    for (i=0; i<80; i++) hdr.descrip[i] = hdr1.descrip[i];
    hdr.qform_code = hdr1.qform_code;
    hdr.sform_code = hdr1.sform_code;
    hdr.quatern_b = hdr1.quatern_b;
    hdr.quatern_c = hdr1.quatern_c;
    hdr.quatern_d = hdr1.quatern_d;
    hdr.qoffset_x = hdr1.qoffset_x;
    hdr.qoffset_y = hdr1.qoffset_y;
    hdr.qoffset_z = hdr1.qoffset_z;
    for (i=0; i<4; i++) hdr.srow_x[i]=hdr1.srow_x[i];
    for (i=0; i<4; i++) hdr.srow_y[i]=hdr1.srow_y[i];
    for (i=0; i<4; i++) hdr.srow_z[i]=hdr1.srow_z[i];
    hdr.xyzt_units = hdr1.xyzt_units;
    hdr.dim_info = hdr1.dim_info;
  }
  else if (nifti_version == 2) {  /* nifti-2 */
    header_size = 540;
    memcpy(&hdr,databuffer,header_size);
    if (swap) {
      VByteSwapNifti2Header(&hdr);
    }
  }
  else {
    VError(" not a nifti file");
  }

  /* get data type */
  VRepnKind dst_repn = DT_UNKNOWN;
  int datatype = (int)hdr.datatype;

  switch(datatype) {
  case DT_UNKNOWN:
    VError(" unknown data type");
    break;
  case DT_BINARY:
    dst_repn = VBitRepn;
    break;
  case DT_UNSIGNED_CHAR:
    dst_repn = VUByteRepn;
    break;
  case DT_SIGNED_SHORT:
    dst_repn = VShortRepn;
    break;
  case DT_SIGNED_INT:
    dst_repn = VIntegerRepn;
    break;
  case DT_FLOAT:
    dst_repn = VFloatRepn;
    break;
  case DT_DOUBLE:
    dst_repn = VDoubleRepn;
    break;
  case DT_INT8:
    dst_repn = VSByteRepn;
    break;
  case DT_UINT16:
    dst_repn = VUShortRepn;
    break;
  case DT_UINT32:
    dst_repn = VUIntegerRepn;
    break;
  case DT_INT64:
    dst_repn = VLongRepn;
    break;
  case DT_UINT64:
    dst_repn = VULongRepn;
    break;
  default:
    VError(" unknown data type %d",datatype);
  }

  /* get scaling parameters */
  double scl_slope = (double)hdr.scl_slope;
  double scl_inter = (double)hdr.scl_inter;
  double tiny=1.0e-6;
  if (fabs(scl_slope) < tiny && fabs(scl_inter) < tiny) {
    scl_slope = 1.0;
    scl_inter = 0;
  }
  else if (fabs(scl_slope-1.0) < tiny && fabs(scl_inter) < tiny) {
    scl_slope = 1.0;
    scl_inter = 0;
  }
  else {
    dst_repn = VFloatRepn;
    do_scaling = FALSE;  /* no automatic scaling */
  }

  /* number of values stored at each time point */
  if (hdr.dim[5] > 1) VError("data type not supported, dim[5]= %d\n",hdr.dim[5]);

  /* image size */
  /* size_t dimtype = (size_t)hdr.dim[0]; */
  size_t ncols   = (size_t)hdr.dim[1];
  size_t nrows   = (size_t)hdr.dim[2];
  size_t nslices = (size_t)hdr.dim[3];
  size_t nt      = (size_t)hdr.dim[4];

  /* fill data container */
  size_t bytesize = 8;
  if (dst_repn == VBitRepn) bytesize = 1;
  size_t nsize   = hdr.bitpix/bytesize;
  size_t npixels = nslices * nrows * ncols;
  size_t ndata   = nt * npixels * nsize;
  size_t vox_offset = (size_t)hdr.vox_offset;
  size_t startdata = MIN_HEADER_SIZE;
  if (vox_offset > 0) startdata = vox_offset;
  char *data = &databuffer[startdata];

  /* byte swap image data, if needed */
  if (swap == 1) {
    VByteSwapData(data,ndata,nsize);
  }

  /* repetition time (may be wrong in some cases) */
  char xyzt = hdr.xyzt_units;
  int tcode = XYZT_TO_TIME(xyzt);
  float factor = 1.0;
  float xtr=0;
  if (tcode == NIFTI_UNITS_MSEC) factor = 1.0;
  if (tcode == NIFTI_UNITS_SEC) factor = 1000.0;

  if (nt > 1)  {
    if (tr == 0) tr = (short)(factor*hdr.pixdim[4]);

    /* override if TR info is in description field */
    xtr = TRfromString(hdr.descrip);
    if (xtr > 0) {
      tr = (short) (xtr*factor);
      fprintf(stderr," reading TR from description field, TR= %ld ms\n",tr);
    }
    /* fprintf(stderr," nt=%ld,  TR= %ld milliseconds\n",nt,tr); */
    if (tr < 0.1) VWarning(" implausible TR (%d ms), use 'vnifti' for data conversion to specify TR on the command line",tr);
  }

  /* voxel reso */
  int blen=512;
  char *voxelstr = (char *) VCalloc(blen,sizeof(char));
  memset(voxelstr,0,blen);
  sprintf(voxelstr,"%f %f %f",hdr.pixdim[1],hdr.pixdim[2],hdr.pixdim[3]);

  /* geometry information */
  VAttrList geolist = VCreateAttrList();
  double *slicetime = VGetNiftiHeader(geolist,hdr,tr);

  /* read nii image into vista attrlist */
  VAttrList out_list = VCreateAttrList();
  VAppendAttr(out_list,"geoinfo",NULL,VAttrListRepn,geolist);

  (*ok) = FALSE;
  if (nt <= 1) {      /* output one 3D image */
    Nii2Vista3DList(data,nsize,nslices,nrows,ncols,nt,dst_repn,datatype,scl_slope,scl_inter,voxelstr,tr,out_list);
    VUpdateGeoinfo(geolist,(int)3,tr);
  }
  else if (attrtype == FALSE) {        /* output list of 3D images */
    Nii2Vista3DList(data,nsize,nslices,nrows,ncols,nt,dst_repn,datatype,scl_slope,scl_inter,voxelstr,tr,out_list);
    VUpdateGeoinfo(geolist,(int)3,tr);
  }
  else if (attrtype == TRUE && nt > 1) {   /* output one 4D image */
    Nii2Vista4D(data,nsize,nslices,nrows,ncols,nt,dst_repn,datatype,do_scaling,scl_slope,scl_inter,voxelstr,slicetime,tr,out_list);
    VUpdateGeoinfo(geolist,(int)4,tr);
    (*ok) = TRUE;
  }

  return out_list;
}


/*
    KGSX: Biomolecular Kino-geometric Sampling and Fitting of Experimental Data
    Yao et al, Proteins. 2012 Jan;80(1):25-43
    e-mail: latombe@cs.stanford.edu, vdbedem@slac.stanford.edu, julie.bernauer@inria.fr

        Copyright (C) 2011-2013 Stanford University

        Permission is hereby granted, free of charge, to any person obtaining a copy of
        this software and associated documentation files (the "Software"), to deal in
        the Software without restriction, including without limitation the rights to
        use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
        of the Software, and to permit persons to whom the Software is furnished to do
        so, subject to the following conditions:

        This entire text, including the above copyright notice and this permission notice
        shall be included in all copies or substantial portions of the Software.

        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
        OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
        FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
        IN THE SOFTWARE.
*/

#ifndef KGS_RELATIVEMSDDIRECTION_H
#define KGS_RELATIVEMSDDIRECTION_H

#include <gsl/gsl_vector.h>

#include <tuple>
#include <vector>

#include "core/graph/KinTree.h"
#include "Direction.h"
#include "Selection.h"

class RelativeMSDDirection: public Direction {
 public:
  RelativeMSDDirection( std::vector< std::tuple<Atom*, Atom*, double> > &relativeDistances);

 protected:
  void computeGradient(Configuration* conf, Configuration* target, gsl_vector* ret);

 private:
  std::vector< std::tuple<Atom*, Atom*, double> > &m_relativeDistances;
};

#endif //KGS_RELATIVEMSDDIRECTION_H


/* This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.  You should have received
   a copy of the GNU General Public License along with this program;
   if not, write to the Free Foundation, Inc., 59 Temple Place, Suite
   330, Boston, MA 02111-1307 USA

   Original implementation was copyright (C) 1997 Makoto Matsumoto and
   Takuji Nishimura. Coded by Takuji Nishimura, considering the
   suggestions by Topher Cooper and Marc Rieffel in July-Aug. 1997, "A
   C-program for MT19937: Integer version (1998/4/6)"

   This implementation copyright (C) 1998 Brian Gough. I reorganized
   the code to use the module framework of GSL.  The license on this
   implementation was changed from LGPL to GPL, following paragraph 3
   of the LGPL, version 2.

   The seeding procedure has been updated to match the 10/99 release
   of MT19937.

   The original code included the comment: "When you use this, send an
   email to: matumoto@math.keio.ac.jp with an appropriate reference to
   your work".

   Makoto Matsumoto has a web page with more information about the
   generator, http://www.math.keio.ac.jp/~matumoto/emt.html.

   The paper below has details of the algorithm.

   From: Makoto Matsumoto and Takuji Nishimura, "Mersenne Twister: A
   623-dimensionally equidistributerd uniform pseudorandom number
   generator". ACM Transactions on Modeling and Computer Simulation,
   Vol. 8, No. 1 (Jan. 1998), Pages 3-30

   You can obtain the paper directly from Makoto Matsumoto's web page.

   The period of this generator is 2^{19937} - 1.

*/

#include <config.h>
#include <stdlib.h>
#include <gsl/gsl_rng.h>

static inline unsigned long int mt_get (void *vstate);
static double mt_get_double (void *vstate);
static void mt_set (void *state, unsigned long int s);

#define N 624	/* Period parameters */
#define M 397

/* most significant w-r bits */
static const unsigned long UPPER_MASK = 0x80000000UL;

/* least significant r bits */
static const unsigned long LOWER_MASK = 0x7fffffffUL;

typedef struct
  {
    unsigned long mt[N];
    int mti;
  }
mt_state_t;

static inline unsigned long
mt_get (void *vstate)
{
  mt_state_t *state = (mt_state_t *) vstate;

  unsigned long k ;
  unsigned long int *const mt = state->mt;

#define MAGIC(y) (((y)&0x1) ? 0x9908b0dfUL : 0)

  if (state->mti >= N)
    {	/* generate N words at one time */
      int kk;

      for (kk = 0; kk < N - M; kk++)
	{
	  unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
	  mt[kk] = mt[kk + M] ^ (y >> 1) ^ MAGIC(y);
	}
      for (; kk < N - 1; kk++)
	{
	  unsigned long y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
	  mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ MAGIC(y);
	}

      {
	unsigned long y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
	mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ MAGIC(y);
      }

      state->mti = 0;
    }

  /* Tempering */

  k = mt[state->mti];
  k ^= (k >> 11);
  k ^= (k << 7) & 0x9d2c5680UL;
  k ^= (k << 15) & 0xefc60000UL;
  k ^= (k >> 18);

  state->mti++;

  return k;
}

static double
mt_get_double (void * vstate)
{
  return mt_get (vstate) / 4294967296.0 ;
}

static void
mt_set (void *vstate, unsigned long int s)
{
  mt_state_t *state = (mt_state_t *) vstate;
  int i;

  if (s == 0)
    s = 4357;	/* the default seed is 4357 */

  /* This is the October 1999 version of the seeding procedure. It
     was updated by the original developers to avoid the periodicity
     in the simple congruence originally used.

     Note that an ANSI-C unsigned long integer arithmetic is
     automatically modulo 2^32 (or a higher power of two), so we can
     safely ignore overflow. */

#define LCG(x) ((69069 * x) + 1) &0xffffffffUL

  for (i = 0; i < N; i++)
    {
      state->mt[i] = s & 0xffff0000UL;
      s = LCG(s);
      state->mt[i] |= (s &0xffff0000UL) >> 16;
      s = LCG(s);
    }

  state->mti = i;
}

/* This is the original version of the seeding procedure, no longer
   used but available for compatibility with the original MT19937. */

static void
mt_1998_set (void *vstate, unsigned long int s)
{
  mt_state_t *state = (mt_state_t *) vstate;
  int i;

  if (s == 0)
    s = 4357;	/* the default seed is 4357 */

  state->mt[0] = s & 0xffffffffUL;

#define LCG1998(n) ((69069 * n) & 0xffffffffUL)

  for (i = 1; i < N; i++)
    state->mt[i] = LCG1998 (state->mt[i - 1]);

  state->mti = i;
}

static const gsl_rng_type mt_type =
{"mt19937",			/* name */
 0xffffffffUL,			/* RAND_MAX  */
 0,			        /* RAND_MIN  */
 sizeof (mt_state_t),
 &mt_set,
 &mt_get,
 &mt_get_double};

static const gsl_rng_type mt_1998_type =
{"mt19937_1998",		/* name */
 0xffffffffUL,			/* RAND_MAX  */
 0,			        /* RAND_MIN  */
 sizeof (mt_state_t),
 &mt_1998_set,
 &mt_get,
 &mt_get_double};

const gsl_rng_type *gsl_rng_mt19937 = &mt_type;
const gsl_rng_type *gsl_rng_mt19937_1998 = &mt_1998_type;

/* MT19937 is the default generator, so define that here too */

const gsl_rng_type *gsl_rng_default = &mt_type;
unsigned long int gsl_rng_default_seed = 0;


/*	$Id$ */
/*
 * Copyright (c) 2014 Kristaps Dzonsons <kristaps@kcons.eu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_multifit.h>
#include <gsl/gsl_histogram.h>
#include <kplot.h>

#include "extern.h"

#define	BUFSZ 1024

/*
 * All possible input tokens, including some which are "virtual" tokens
 * in that they don't map to an exact character representation.
 */
enum	token {
	TOKEN_PAREN_OPEN,
	TOKEN_PAREN_CLOSE,
	TOKEN_ADD,
	TOKEN_SUB,
	TOKEN_MUL,
	TOKEN_DIV,
	TOKEN_EXP,
	TOKEN_END,
	TOKEN_NUMBER,
	TOKEN_SQRT,
	TOKEN_EXPF,
	TOKEN_P1,
	TOKEN_PN,
	TOKEN_N,
	TOKEN_ERROR,
	TOKEN_SKIP,
	TOKEN_POSITIVE,
	TOKEN_NEGATIVE,
	TOK__MAX
};

enum	arity {
	ARITY_NONE = 0,
	ARITY_ONE,
	ARITY_TWO
};

enum	assoc {
	ASSOC_NONE = 0,
	ASSOC_L,
	ASSOC_R,
};

struct	tok {
	char		 key; /* input identifier */
	int		 prec; /* precedence (if applicable) */
	int		 oper; /* is operator? */
	int		 func; /* is function? */
	enum assoc	 assoc; /* associativity */
	enum arity	 arity; /* n-ary-ess */
	enum htype	 map; /* map to htype (if applicable) */
};

static	const struct tok toks[TOK__MAX] = {
	{ '(',  -1, 0, 0, ASSOC_NONE, ARITY_NONE, HNODE__MAX }, /* TOKEN_PAREN_OPEN */
	{ ')',  -1, 0, 0, ASSOC_NONE, ARITY_NONE, HNODE__MAX }, /* TOKEN_PAREN_CLOSE */
	{ '+',  2, 1, 0, ASSOC_L, ARITY_TWO, HNODE_ADD }, /* TOKEN_ADD */
	{ '-',  2, 1, 0, ASSOC_L, ARITY_TWO, HNODE_SUB }, /* TOKEN_SUB */
	{ '*',  3, 1, 0, ASSOC_L, ARITY_TWO, HNODE_MUL }, /* TOKEN_MUL */
	{ '/',  3, 1, 0, ASSOC_L, ARITY_TWO, HNODE_DIV }, /* TOKEN_DIV */
	{ '^',  5, 1, 0, ASSOC_R, ARITY_TWO, HNODE_EXP }, /* TOKEN_EXP */
	{ '\0', -1, 0, 0, ASSOC_NONE, ARITY_NONE, HNODE__MAX }, /* TOKEN_END */
	{ '\0', -1, 0, 0, ASSOC_NONE, ARITY_NONE, HNODE_NUMBER }, /* TOKEN_NUMBER */
	{ '\0', -1, 0, 1, ASSOC_NONE, ARITY_NONE, HNODE_SQRT }, /* TOKEN_SQRT */
	{ '\0', -1, 0, 1, ASSOC_NONE, ARITY_NONE, HNODE_EXPF }, /* TOKEN_EXPF */
	{ 'x',  -1, 0, 0, ASSOC_NONE, ARITY_NONE, HNODE_P1 }, /* TOKEN_P1 */
	{ 'X',  -1, 0, 0, ASSOC_NONE, ARITY_NONE, HNODE_PN }, /* TOKEN_PN */
	{ 'n',  -1, 0, 0, ASSOC_NONE, ARITY_NONE, HNODE_N }, /* TOKEN_N */
	{ '\0', -1, 0, 0, ASSOC_NONE, ARITY_NONE, HNODE__MAX }, /* TOKEN_ERROR */
	{ ' ',  -1, 0, 0, ASSOC_NONE, ARITY_NONE, HNODE__MAX }, /* TOKEN_SKIP */
	{ '+',  4, 1, 0, ASSOC_R, ARITY_ONE, HNODE_POSITIVE }, /* TOKEN_POSITIVE */
	{ '-',  4, 1, 0, ASSOC_R, ARITY_ONE, HNODE_NEGATIVE }, /* TOKEN_NEGATIVE */
};

#if 0
static void
hnode_print(struct hnode **p)
{
	struct hnode	**pp;

	if (NULL == p)
		return;

	for (pp = p; NULL != *pp; pp++) {
		switch ((*pp)->type) {
		case (HNODE_VAR):
			putchar('v');
			break;
		case (HNODE_P1):
			putchar('x');
			break;
		case (HNODE_P2):
			putchar('y');
			break;
		case (HNODE_NUMBER):
			printf("%g", (*pp)->real);
			break;
		case (HNODE_SQRT):
			printf("sqrt");
			break;
		case (HNODE_ADD):
			putchar('+');
			break;
		case (HNODE_EXP):
			putchar('^');
			break;
		case (HNODE_SUB):
			putchar('-');
			break;
		case (HNODE_MUL):
			putchar('*');
			break;
		case (HNODE_DIV):
			putchar('/');
			break;
		case (HNODE_POSITIVE):
			printf("+'");
			break;
		case (HNODE_NEGATIVE):
			printf("-'");
			break;
		default:
			abort();
		}
		putchar(' ');
	}
	putchar('\n');
}
#endif

/*
 * Check if the current token (which has ARITY_ONE, we assume) is in
 * fact unary.
 * We do this by checking the previous token: if it's a binary operator
 * or an open parenthesis, then we're a unary operator.
 */
static int
check_unary(enum token lasttok)
{
	switch (lasttok) {
	case (TOK__MAX):
		/* FALLTHROUGH */
	case (TOKEN_PAREN_OPEN):
		/* FALLTHROUGH */
	case (TOKEN_ADD):
		/* FALLTHROUGH */
	case (TOKEN_SUB):
		/* FALLTHROUGH */
	case (TOKEN_MUL):
		/* FALLTHROUGH */
	case (TOKEN_DIV):
		/* FALLTHROUGH */
	case (TOKEN_EXP):
		return(1);
	default:
		break;
	}
	return(0);
}

/*
 * Check if the current token (which has ARITY_TWO, we assume) is in
 * fact binary.
 * We do this by checking the previous token: if it's an operand or a
 * close-paren (i.e., an operand), then we're a binary operator.
 */
static int
check_binary(enum token lasttok)
{
	switch (lasttok) {
	case (TOKEN_PAREN_CLOSE):
		/* FALLTHROUGH */
	case (TOKEN_NUMBER):
		/* FALLTHROUGH */
	case (TOKEN_P1):
		/* FALLTHROUGH */
	case (TOKEN_PN):
		/* FALLTHROUGH */
	case (TOKEN_N):
		return(1);
	default:
		break;
	}
	return(0);
}

/*
 * Extra a token from input.
 * If TOKEN_ERROR, something bad has happened in the attempt to do so.
 * Otherwise, this returns a valid token (possibly end of input).
 */
static enum token
tokenise(const char **v, char *buf, enum token lasttok)
{
	size_t		i, sz;

	/* Short-circuit this case. */
	if ('\0' == **v)
		return(TOKEN_END);

	/* Look for token in our predefined inputs. */
	for (i = 0; i < TOK__MAX; i++)
		if ('\0' != toks[i].key && **v == toks[i].key) {
			switch (toks[i].arity) {
			case (ARITY_NONE):
				(*v)++;
				return((enum token)i);
			case (ARITY_ONE):
				if ( ! check_unary(lasttok))
					continue;
				break;
			case (ARITY_TWO):
				if ( ! check_binary(lasttok))
					continue;
				break;
			}
			(*v)++;
			return((enum token)i);
		}

	/* See if we're a real number or identifier. */
	if (isdigit((int)**v) || '.' == **v) {
		sz = 0;
		for ( ; isdigit((int)**v) || '.' == **v; (*v)++) {
			assert(sz < BUFSZ);
			buf[sz++] = **v;
		}
		buf[sz] = '\0';
		return(TOKEN_NUMBER);
	} else if (isalpha((int)**v)) {
		sz = 0;
		for ( ; isalpha((int)**v); (*v)++) {
			assert(sz < BUFSZ);
			buf[sz++] = **v;
		}
		buf[sz] = '\0';
		if (0 == strcmp(buf, "sqrt"))
			return(TOKEN_SQRT);
		else if (0 == strcmp(buf, "exp"))
			return(TOKEN_EXPF);
	}

	/* Eh... */
	return(TOKEN_ERROR);
}

/*
 * Queue something onto the output RPN queue.
 */
static void
enqueue(struct hnode ***q, size_t *qsz, enum token tok)
{

	*q = realloc(*q, ++(*qsz) * sizeof(struct hnode *));
	(*q)[*qsz - 1] = calloc(1, sizeof(struct hnode));
	(*q)[*qsz - 1]->type = toks[tok].map;
	assert(HNODE__MAX != toks[tok].map);
}

/*
 * Make sure that all operators and functions are matched with
 * arguments.
 * The Shunting-Yard algorithm itself doesn't do this, so we need to do
 * it now.
 */
static int
check(struct hnode **p)
{
	struct hnode	**pp;
	size_t		  ssz;

	for (ssz = 0, pp = p; NULL != *pp; pp++)
		switch ((*pp)->type) {
		case (HNODE_P1):
			/* FALLTHROUGH */
		case (HNODE_PN):
			/* FALLTHROUGH */
		case (HNODE_N):
			/* FALLTHROUGH */
		case (HNODE_NUMBER):
			ssz++;
			break;
		case (HNODE_POSITIVE):
			/* FALLTHROUGH */
		case (HNODE_NEGATIVE):
			/* FALLTHROUGH */
		case (HNODE_SQRT):
			/* FALLTHROUGH */
		case (HNODE_EXPF):
			if (0 == ssz)
				return(0);
			break;
		case (HNODE_ADD):
			/* FALLTHROUGH */
		case (HNODE_SUB):
			/* FALLTHROUGH */
		case (HNODE_MUL):
			/* FALLTHROUGH */
		case (HNODE_DIV):
			/* FALLTHROUGH */
		case (HNODE_EXP):
			if (ssz < 2)
				return(0);
			ssz--;
			break;
		default:
			abort();
		}

	return(1 == ssz);
}

/*
 * Dijkstra's Shunting-Yard algorithm for converting an infix-order
 * expression to prefix order.
 * This returns a NULL-terminated list of expressions to evaluate in
 * post-fix order.
 */
struct hnode **
hnode_parse(const char **v)
{
	enum token	  tok, lasttok;
	enum token	  stack[STACKSZ];
	char		  buf[BUFSZ + 1];
	struct hnode	**q;
	int		  found;
	size_t		  i, qsz, ssz;

	q = NULL;
	qsz = ssz = 0;
	lasttok = TOK__MAX;

	while (TOKEN_END != (tok = tokenise(v, buf, lasttok))) {
		switch (tok) {
		case (TOKEN_ERROR):
			goto err;
		case (TOKEN_SKIP):
			break;
		case (TOKEN_P1):
			/* FALLTHROUGH */
		case (TOKEN_PN):
			/* FALLTHROUGH */
		case (TOKEN_N):
			enqueue(&q, &qsz, tok);
			break;
		case (TOKEN_NUMBER):
			enqueue(&q, &qsz, tok);
			q[qsz - 1]->real = atof(buf);
			break;
		case (TOKEN_SQRT):
			/* FALLTHROUGH */
		case (TOKEN_EXPF):
			/* FALLTHROUGH */
		case (TOKEN_PAREN_OPEN):
			assert(ssz < STACKSZ);
			stack[ssz++] = tok;
			break;
		case (TOKEN_PAREN_CLOSE):
			assert(ssz > 0);
			found = 0;
			do {
				if (TOKEN_PAREN_OPEN == stack[--ssz])
					found = 1;
				else
					enqueue(&q, &qsz, stack[ssz]);
			} while ( ! found && ssz > 0);
			assert(found);
			if (ssz > 0 && toks[stack[ssz - 1]].func)
				enqueue(&q, &qsz, stack[--ssz]);
			break;
		case (TOKEN_POSITIVE):
			/* FALLTHROUGH */
		case (TOKEN_NEGATIVE):
			/* FALLTHROUGH */
		case (TOKEN_SUB):
			/* FALLTHROUGH */
		case (TOKEN_MUL):
			/* FALLTHROUGH */
		case (TOKEN_DIV):
			/* FALLTHROUGH */
		case (TOKEN_EXP):
			/* FALLTHROUGH */
		case (TOKEN_ADD):
			assert(toks[tok].prec >= 0);
			assert(ASSOC_NONE != toks[tok].assoc);
			while (ssz > 0 && toks[stack[ssz - 1]].oper) {
				assert(toks[stack[ssz - 1]].prec >= 0);
				assert(ASSOC_NONE !=
					toks[stack[ssz - 1]].assoc);
				if ((ASSOC_L == toks[tok].assoc
					 && toks[tok].prec ==
					 toks[stack[ssz - 1]].prec) ||
					 (toks[tok].prec <
					  toks[stack[ssz - 1]].prec))
					enqueue(&q, &qsz, stack[--ssz]);
				else
					break;
			}
			assert(ssz < STACKSZ);
			stack[ssz++] = tok;
			break;
		default:
			abort();
		}
		if (TOKEN_SKIP != tok)
			lasttok = tok;
	}

	while (ssz > 0) {
		--ssz;
		if (TOKEN_PAREN_OPEN == stack[ssz])
			goto err;
		enqueue(&q, &qsz, stack[ssz]);
	}

	q = realloc(q, ++qsz * sizeof(struct hnode *));
	q[qsz - 1] = NULL;

	if ( ! check(q))
		goto err;

#if 0
	hnode_print(q);
#endif
	return(q);
err:
	for (i = 0; i < qsz; i++)
		free(q[i]);
	free(q);
	return(NULL);
}

struct hnode **
hnode_copy(struct hnode **p)
{
	struct hnode	**pp;
	size_t		  sz;

	for (sz = 0, pp = p; NULL != *pp; pp++)
		sz++;

	pp = calloc(sz + 1, sizeof(struct hnode *));
	for (sz = 0; NULL != *p; p++, sz++) {
		pp[sz] = calloc(1, sizeof(struct hnode));
		pp[sz]->type = (*p)->type;
		pp[sz]->real = (*p)->real;
	}

	return(pp);
}

void
hnode_free(struct hnode **p)
{
	struct hnode	**pp;

	if (NULL == p)
		return;

	for (pp = p; NULL != *pp; pp++)
		free(*pp);

	free(p);
}

/*
 * Execute a function in prefix order given variables x (given player's
 * strategy), y (other player's strategy), and var (given player's
 * morality index).
 */
double
hnode_exec(const struct hnode *const *p, double x, double X, size_t n)
{
	double		  stack[STACKSZ];
	const struct hnode *const *pp;
	size_t		  ssz;
	double		  val;

	for (ssz = 0, pp = p; NULL != *pp; pp++)
		switch ((*pp)->type) {
		case (HNODE_P1):
			assert(ssz < STACKSZ);
			stack[ssz++] = x;
			break;
		case (HNODE_PN):
			assert(ssz < STACKSZ);
			stack[ssz++] = X;
			break;
		case (HNODE_N):
			assert(ssz < STACKSZ);
			stack[ssz++] = (double)n;
			break;
		case (HNODE_NUMBER):
			assert(ssz < STACKSZ);
			stack[ssz++] = (*pp)->real;
			break;
		case (HNODE_SQRT):
			assert(ssz > 0);
			val = sqrt(stack[--ssz]);
			stack[ssz++] = val;
			break;
		case (HNODE_EXPF):
			assert(ssz > 0);
			val = exp(stack[--ssz]);
			stack[ssz++] = val;
			break;
		case (HNODE_ADD):
			assert(ssz > 1);
			val = stack[ssz - 2] + stack[ssz - 1];
			ssz -= 2;
			stack[ssz++] = val;
			break;
		case (HNODE_SUB):
			assert(ssz > 1);
			val = stack[ssz - 2] - stack[ssz - 1];
			ssz -= 2;
			stack[ssz++] = val;
			break;
		case (HNODE_MUL):
			assert(ssz > 1);
			val = stack[ssz - 2] * stack[ssz - 1];
			ssz -= 2;
			stack[ssz++] = val;
			break;
		case (HNODE_DIV):
			assert(ssz > 1);
			val = stack[ssz - 2] / stack[ssz - 1];
			ssz -= 2;
			stack[ssz++] = val;
			break;
		case (HNODE_EXP):
			assert(ssz > 1);
			val = pow(stack[ssz - 2], stack[ssz - 1]);
			ssz -= 2;
			stack[ssz++] = val;
			break;
		case (HNODE_POSITIVE):
			assert(ssz > 0);
			break;
		case (HNODE_NEGATIVE):
			assert(ssz > 0);
			val = -(stack[--ssz]);
			stack[ssz++] = val;
			break;
		default:
			abort();
		}

	assert(1 == ssz);
	return(stack[0]);
}

static void
hnode_test_expect(double x, double X,
	double n, const char *expf, double vexp)
{
	struct hnode	 **exp;
	double		   v;
	const char	  *expfp;

	expfp = expf;
	exp = hnode_parse((const char **)&expfp);
	assert(NULL != exp);
	v = hnode_exec
		((const struct hnode *const *)exp, x, X, n);
	g_debug("pi(x=%g, X=%g, n=%g) = %s = %g (want %g)",
		x, X, n, expf, v, vexp);
	hnode_free(exp);
}

void
hnode_test(void)
{
	double	x, X, n;

	x = 10.0;
	X = 20.0;
	n = 2.0;
	hnode_test_expect(x, X, n,
		"(1 - exp(-X)) - x",
		(1.0 - exp(-X)) - x);
	hnode_test_expect(x, X, n,
		"sqrt(1 / n * X) - 0.5 * x^2",
		sqrt(1.0 / n * X) - 0.5 * pow(x, 2.0));
	hnode_test_expect(x, X, n,
		"x - (X - x) * x - x^2",
		x - (X - x) * x - pow(x, 2.0));
	hnode_test_expect(x, X, n,
		"x * (1 / X) - x",
		x * (1.0 / X) - x);
}


/**
 * \file TypedBaseFilter.h
 */

#ifndef ATK_CORE_TYPEDBASEFILTER_H
#define ATK_CORE_TYPEDBASEFILTER_H

#include <ATK/Core/BaseFilter.h>
#include <ATK/Core/TypeTraits.h>

#include <memory>
#include <vector>

#include <boost/align/aligned_allocator.hpp>

#include <gsl/gsl>

namespace ATK
{
  const gsl::index ALIGNMENT = 32;

  /// Interface for output filters
  template<typename DataType>
  class ATK_CORE_EXPORT OutputArrayInterface
  {
  public:
    virtual ~OutputArrayInterface() = default;

    /**
     * @brief Returns an array with the processed output
     * @param port is the port that the next plugin listens to
     */
    virtual DataType* get_output_array(gsl::index port) const = 0;
    /**
     * Returns the size of the output arrays (usually the last size processed)
     */
    virtual gsl::index get_output_array_size() const = 0;
  };

  /// Base class for typed filters, contains arrays
  template<typename DataType_, typename DataType__ = DataType_>
  class ATK_CORE_EXPORT TypedBaseFilter : public BaseFilter, public OutputArrayInterface<DataType__>
  {
  protected:
    /// Simplify parent calls
    using Parent = BaseFilter;
  public:
    /// To be used by inherited APIs
    using DataType = DataType_;
    /// To be used by inherited APIs
    using DataTypeInput = DataType_;
    /// To be used by inherited APIs
    using DataTypeOutput = DataType__;
    /// To be used for filters that require aligned data
    using AlignedVector = std::vector<DataTypeInput, boost::alignment::aligned_allocator<DataTypeInput, ALIGNMENT> >;
    /// To be used for filters that require aligned data for output data
    using AlignedOutVector = std::vector<DataTypeOutput, boost::alignment::aligned_allocator<DataTypeOutput, ALIGNMENT> >;
    /// To be used for filters that required aligned data for parameters (like EQ)
    using AlignedScalarVector = std::vector<typename TypeTraits<DataType>::Scalar, boost::alignment::aligned_allocator<typename TypeTraits<DataType>::Scalar, ALIGNMENT> >;

    /// Base constructor for filters with actual data
    TypedBaseFilter(gsl::index nb_input_ports, gsl::index nb_output_ports);
    /// Move constructor
    TypedBaseFilter(TypedBaseFilter&& other);
    /// Destructor
    ~TypedBaseFilter() override = default;

    TypedBaseFilter(const TypedBaseFilter&) = delete;
    TypedBaseFilter& operator=(const TypedBaseFilter&) = delete;

    /**
     * @brief Returns an array with the processed output
     * @param port is the port that the next plugin listens to
     */
    DataType__* get_output_array(gsl::index port) const final;
    gsl::index get_output_array_size() const final;

    void set_nb_input_ports(gsl::index nb_ports) override;
    void set_nb_output_ports(gsl::index nb_ports) override;

    void full_setup() override;

    /// Connects this filter input to another's output
    void set_input_port(gsl::index input_port, gsl::not_null<BaseFilter*> filter, gsl::index output_port) final;
    void set_input_port(gsl::index input_port, BaseFilter& filter, gsl::index output_port) final;

  private:
    int get_type() const override;
  protected:
    /// This implementation does nothing
    void process_impl(gsl::index size) const override;
    /// Prepares the filter by retrieving the inputs arrays
    void prepare_process(gsl::index size) final;
    /// Prepares the filter by resizing the outputs arrays
    void prepare_outputs(gsl::index size) final;

    /// Used to convert other filter outputs to DataType*
    void convert_inputs(gsl::index size);

    /// Input arrays with the input delay, owned here
    std::vector<AlignedVector> converted_inputs_delay;
    /// Input arrays, starting from t=0 (without input delay)
    std::vector<DataTypeInput*> converted_inputs;
    /// Current size of the input arrays, without delay
    std::vector<gsl::index> converted_inputs_size;
    /// Current input delay
    std::vector<gsl::index> converted_in_delays;
    /// Pointer to the output interface of the connected filters
    std::vector<OutputArrayInterface<DataType_>*> direct_filters;

    /// Output arrays with the output delay, owned here
    std::vector<AlignedOutVector> outputs_delay;
    /// Output arrays, starting from t=0 (without output delay)
    std::vector<DataTypeOutput*> outputs;
    /// Current size of the output arrays, without delay
    std::vector<gsl::index> outputs_size;
    /// Current output delay
    std::vector<gsl::index> out_delays;

    /// A vector containing the default values for the input arrays
    AlignedVector default_input;
    /// A vector containing the default values for the output arrays
    AlignedOutVector default_output;
  };
}

#endif


/*
 * Copyright 2014 RWTH Aachen University. All rights reserved.
 *
 * Licensed under the RWTH LM License (the "License");
 * you may not use this file except in compliance with the License.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#pragma once
#include <gsl/gsl_cblas.h>
#include <cassert>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <numeric>

typedef double Real;

inline void FastZero(const int size, float x[]) {
  std::fill(x, x + size, 0.0f);
}

inline void FastZero(const int size, double x[]) {
  std::fill(x, x + size, 0.0);
}

inline void FastCopy(const float source[],
                     const int size,
                     float destination[]) {
  memcpy(destination, source, size * sizeof(float));
}

inline void FastCopy(const double source[],
                     const int size,
                     double destination[]) {
  memcpy(destination, source, size * sizeof(double));
}

inline float FastMax(const float x[], const int size) {
  return *std::max_element(x, x + size);
}

inline double FastMax(const double x[], const int size) {
  return *std::max_element(x, x + size);
}

inline float FastComputeSum(const float x[], const int size) {
  return std::accumulate(x, x + size, 0.0f);
}

inline double FastComputeSum(const double x[], const int size) {
  return std::accumulate(x, x + size, 0.0);
}

inline void FastAddConstant(const float source[],
                            const int size,
                            const float value,
                            float destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const float x) { return x + value; });
}

inline void FastAddConstant(const double source[],
                            const int size,
                            const double value,
                            double destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const double x) { return x + value; });
}

inline void FastSubtractConstant(const float source[],
                                 const int size,
                                 const float value,
                                 float destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const double x) { return x - value; });
}

inline void FastSubtractConstant(const double source[],
                                 const int size,
                                 const double value,
                                 double destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const double x) { return x - value; });
}

inline void FastReverseSubtractConstant(
    const float source[],
    const int size,
    const float value,
    float destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const float x) { return value - x; });
}

inline void FastReverseSubtractConstant(
    const double source[],
    const int size,
    const double value,
    double destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const double x) { return value - x; });
}

inline void FastMultiplyByConstant(const float source[],
                                   const int size,
                                   const float value,
                                   float destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const float x) { return value * x; });
}

inline void FastMultiplyByConstant(const double source[],
                                   const int size,
                                   const double value,
                                   double destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const double x) { return value * x; });
}

inline void FastDivideByConstant(const float source[],
                                 const int size,
                                 const float value,
                                 float destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const float x) { return x / value; });
}

inline void FastDivideByConstant(const double source[],
                                 const int size,
                                 const double value,
                                 double destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [value](const double x) { return x / value; });
}

inline void FastInvert(const float source[],
                       const int size,
                       float destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [](const double x) { return 1.0f / x; });
}

inline void FastInvert(const double source[],
                       const int size,
                       double destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [](const double x) { return 1.0 / x; });
}

inline void FastAdd(const float a[],
                    const int size,
                    const float b[],
                    float c[]) {
  std::transform(a,
                 a + size,
                 b,
                 c,
                 [](const float x, const float y) { return x + y; });
}

inline void FastAdd(const double a[],
                    const int size,
                    const double b[],
                    double c[]) {
  std::transform(a,
                 a + size,
                 b,
                 c,
                 [](const double x, const double y) { return x + y; });
}

inline void FastMultiplyByConstantAdd(const float alpha,
                                      const float a[],
                                      const int size,
                                      float b[]) {
  cblas_saxpy(size, alpha, a, 1, b, 1);
}

inline void FastMultiplyByConstantAdd(const double alpha,
                                      const double a[],
                                      const int size,
                                      double b[]) {
  cblas_daxpy(size, alpha, a, 1, b, 1);
}

inline void FastSub(const float a[],
                    const int size,
                    const float b[],
                    float c[]) {
  std::transform(a,
                 a + size,
                 b,
                 c,
                 [](const float x, const float y) { return x - y; });
}

inline void FastSub(const double a[],
                    const int size,
                    const double b[],
                    double c[]) {
  std::transform(a,
                 a + size,
                 b,
                 c,
                 [](const double x, const double y) { return x - y; });
}

inline void FastMultiply(const float a[],
                         const int size,
                         const float b[],
                         float c[]) {
  std::transform(a,
                 a + size,
                 b,
                 c,
                 [](const float x, const float y) { return x * y; });
}

inline void FastMultiply(const double a[],
                         const int size,
                         const double b[],
                         double c[]) {
  std::transform(a,
                 a + size,
                 b,
                 c,
                 [](const double x, const double y) { return x * y; });
}

inline void FastMultiplyAdd(const float a[],
                            const int size,
                            const float b[],
                            float c[]) {
  for (int i = 0; i < size; ++i)
    c[i] += a[i] * b[i];
}

inline void FastMultiplyAdd(const double a[],
                            const int size,
                            const double b[],
                            double c[]) {
  for (int i = 0; i < size; ++i)
    c[i] += a[i] * b[i];
}

inline void FastTanh(const float source[],
                     const int size,
                     float destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [](const float x) { return std::tanh(x); });
}

inline void FastTanh(const double source[],
                     const int size,
                     double destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [](const double x) { return std::tanh(x); });
}

inline void FastExponential(const float source[],
                            const int size,
                            float destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [](const float x) { return std::exp(x); });
}

inline void FastExponential(const double source[],
                            const int size,
                            double destination[]) {
  std::transform(source,
                 source + size,
                 destination,
                 [](const double x) { return std::exp(x); });
}

inline void FastMatrixVectorMultiply(const float a[],
                                     const bool transpose_a,
                                     const int rows_a,
                                     const int columns_a,
                                     const float x[],
                                     float y[]) {
  cblas_sgemv(CblasColMajor,
              transpose_a ? CblasTrans : CblasNoTrans,
              rows_a,
              columns_a,
              1.0f,
              a,
              rows_a,
              x,
              1,
              1.0f,
              y,
              1);
}

inline void FastMatrixVectorMultiply(const double a[],
                                     const bool transpose_a,
                                     const int rows_a,
                                     const int columns_a,
                                     const double x[],
                                     double y[]) {
  cblas_dgemv(CblasColMajor,
              transpose_a ? CblasTrans : CblasNoTrans,
              rows_a,
              columns_a,
              1.0,
              a,
              rows_a,
              x,
              1,
              1.0,
              y,
              1);
}

inline void FastOuterProduct(const float alpha,
                             const float x[],
                             const int size_x,
                             const float y[],
                             const int size_y,
                             float a[]) {
  cblas_sger(CblasColMajor,
             size_x,
             size_y,
             alpha,
             x,
             1,
             y,
             1,
             a,
             size_x);
}

inline void FastOuterProduct(const double alpha,
                             const double x[],
                             const int size_x,
                             const double y[],
                             const int size_y,
                             double a[]) {
  cblas_dger(CblasColMajor,
             size_x,
             size_y,
             alpha,
             x,
             1,
             y,
             1,
             a,
             size_x);
}

inline void FastMatrixMatrixMultiply(
      const float alpha,
      const float a[],
      const bool transpose_a,
      const int rows_a,
      const int columns_a,
      const float b[],
      const bool transpose_b,
      const int columns_b,
      float c[]) {
  cblas_sgemm(CblasColMajor,
              transpose_a ? CblasTrans : CblasNoTrans,
              transpose_b ? CblasTrans : CblasNoTrans,
              rows_a,
              columns_b,
              columns_a,
              alpha,
              a,
              transpose_a ? columns_a : rows_a,
              b,
              transpose_b ? columns_b : columns_a,
              1.0f,
              c,
              rows_a);
}

inline void FastMatrixMatrixMultiply(
      const double alpha,
      const double a[],
      const bool transpose_a,
      const int rows_a,  // m
      const int columns_a,  // k
      const double b[],
      const bool transpose_b,
      const int columns_b,  // n
      double c[]) {
  cblas_dgemm(CblasColMajor,
              transpose_a ? CblasTrans : CblasNoTrans,
              transpose_b ? CblasTrans : CblasNoTrans,
              rows_a,  // m
              columns_b,  // n
              columns_a,  // k
              alpha,
              a,
              transpose_a ? columns_a : rows_a,
              b,
              transpose_b ? columns_b : columns_a,
              1.0,
              c,
              rows_a);
}

inline Real *FastMalloc(const int size) {
  Real *result = new Real[size];
  assert(result != nullptr || size == 0);
  return result;
}

inline void FastFree(Real *x) {
  delete [] x;
}


/****************************************************************
 *
 * Copyright (C) Max Planck Institute
 * for Biological Cybernetics, Tuebingen, Germany
 *
 * Author: Gabriele Lohmann, 2015
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *****************************************************************/

/*
** trial average using spline interpolation
**
** G.Lohmann, May 2014
*/
#include <viaio/Vlib.h>
#include <viaio/VImage.h>
#include <viaio/mu.h>
#include <viaio/option.h>

#include <gsl/gsl_cblas.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_spline.h>

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

#define ABS(x) ((x) > 0 ? (x) : -(x))
#define SQR(x) ((x)*(x))

#define LEN     10000   /* buffer length        */
#define NTRIALS 10000   /* max number of trials */

typedef struct SpointStruct {
  VShort x;
  VShort y;
  VShort z;
} SPoint;

typedef struct TrialStruct {
  int   id;
  float onset;
  float duration;
  float height;
} Trial;

Trial trial[NTRIALS];
int ntrials=0;
int nevents=0;

int test_ascii(int val)
{
  if (val >= 'a' && val <= 'z') return 1;
  if (val >= 'A' && val <= 'Z') return 1;
  if (val >= '0' && val <= '9') return 1;
  if (val ==  ' ') return 1;
  if (val == '\0') return 1;
  if (val == '\n') return 1;
  if (val == '\r') return 1;
  if (val == '\t') return 1;
  if (val == '\v') return 1;
  return 0;
}

/* parse design file */
void ReadDesign(VString designfile)
{
  FILE *fp=NULL;
  int  i,j,k,id;
  char buf[LEN];
  float onset=0,duration=0,height=0;

  fp = fopen(designfile,"r");
  if (!fp) VError(" error opening design file %s",designfile);

  i = ntrials = nevents = 0;
  while (!feof(fp)) {
    for (j=0; j<LEN; j++) buf[j] = '\0';
    if (fgets(buf,LEN,fp) == NULL) break;
    if (strlen(buf) < 2) continue;
    if (buf[0] == '%' || buf[0] == '#') continue;
    if (! test_ascii((int)buf[0])) VError(" input file must be a text file");

    /* remove non-alphanumeric characters */
    for (j=0; j<strlen(buf); j++) {
      k = (int)buf[j];
      if (!isgraph(k) && buf[j] != '\n' && buf[j] != '\r' && buf[j] != '\0') {
	buf[j] = ' ';
      }
      if (buf[j] == '\v') buf[j] = ' '; /* remove tabs */
      if (buf[j] == '\t') buf[j] = ' ';
    }

    if (sscanf(buf,"%d %f %f %f",&id,&onset,&duration,&height) != 4)
      VError(" line %d: illegal input format",i+1);

    if (duration < 0.5 && duration >= -0.0001) duration = 0.5;
    trial[i].id       = id;
    trial[i].onset    = onset;
    trial[i].duration = duration;
    trial[i].height   = height;
    i++;
    if (i > NTRIALS) VError(" too many trials %d",i);

    if (id > nevents) nevents = id;
  }
  fclose(fp);

  ntrials = i;
}

int main (int argc,char *argv[])
{
  static VString  designfile = "";
  static VShort   cond_id = 1;
  static VShort   trial_id = 0;
  static VDouble  temporal_resolution = 1.0;
  static VDouble  start = 0;
  static VDouble  length = 20;
  static VOptionDescRec  options[] = {
    {"design", VStringRepn, 1, & designfile, VRequiredOpt, NULL,"Design file (ascii)" },
    {"cond", VShortRepn, 1, & cond_id, VOptionalOpt, NULL,"Id of experimental condition"},
    {"trial", VShortRepn, 1, & trial_id, VOptionalOpt, NULL,"Id of trial (starts at 0)"},
    {"resolution", VDoubleRepn, 1, & temporal_resolution, VOptionalOpt, NULL," output temporal resolution in secs"},
    {"start", VDoubleRepn, 1, & start, VOptionalOpt, NULL, "start relative to beginning of trial in secs"},
    {"length", VDoubleRepn, 1, & length, VOptionalOpt, NULL, "trial length in seconds"},
  };
  FILE *in_file=NULL,*out_file=NULL;
  VAttrList list=NULL;
  VAttrListPosn posn;
  int i,j,k,slice,row,col;
  int nslices=0,nrows=0,ncols=0,ntimesteps=0;
  double t=0;
  char *prg = GetLipsiaName("vcuttrials");
  fprintf (stderr, "%s\n", prg);

  /*  parse command line */
  VParseFilterCmd (VNumber (options),options,argc,argv,&in_file,&out_file);

  /* get image dimensions, read functional data */
  if (! (list = VReadFile (in_file, NULL))) exit (1);
  fclose(in_file);

  nslices = 0;
  VImage tmp=NULL;
  for (VFirstAttr (list, & posn); VAttrExists (& posn); VNextAttr (& posn)) {
    if (VGetAttrRepn (& posn) != VImageRepn) continue;
    VGetAttrValue (& posn, NULL,VImageRepn, & tmp);
    /* if (VPixelRepn(tmp) != VShortRepn) continue; */
    nslices++;
  }
  if (nslices < 1) VError(" no slices");

  VAttrList geoinfo = VGetGeoInfo(list);
  VImage *src = (VImage *) VCalloc(nslices,sizeof(VImage));
  i = ntimesteps = nrows = ncols = 0;
  for (VFirstAttr (list, & posn); VAttrExists (& posn); VNextAttr (& posn)) {
    if (VGetAttrRepn (& posn) != VImageRepn) continue;
    VGetAttrValue (& posn, NULL,VImageRepn, & src[i]);
    /* if (VPixelRepn(src[i]) != VShortRepn) continue; */
    if (VImageNBands(src[i]) > ntimesteps) ntimesteps = VImageNBands(src[i]);
    if (VImageNRows(src[i])  > nrows)  nrows = VImageNRows(src[i]);
    if (VImageNColumns(src[i]) > ncols) ncols = VImageNColumns(src[i]);
    i++;
  }
  fprintf(stderr," nslices: %d, nrows: %d, ncols: %d,  ntimesteps: %d\n",nslices,nrows,ncols,ntimesteps);

  /* read repetition time */
  double tr = 0;
  if (VGetAttr (VImageAttrList (src[0]), "repetition_time", NULL,
		VDoubleRepn, (VPointer) & tr) != VAttrFound) {
    VError(" attribute 'repetition_time' missing");
  }
  tr /= 1000.0;
  double experiment_duration = tr * (double)ntimesteps;

  /* read design file */
  ReadDesign(designfile);

  /* select trial id */
  int tid=0,j0=-1;
  for (j=0; j<ntrials; j++) {
    if (trial[j].id == cond_id && tid == trial_id) {
      j0 = j;
      break;
    }
    if (trial[j].id == cond_id) tid++;
  }
  if (j0 < 0) VError("trial not found");
  fprintf(stderr," trial onset: %f\n",trial[j0].onset);

  /* adjust start time */
  trial[j0].onset += start;
  if (trial[j0].onset < 0) {
    VWarning(" negative onset, reset to zero");
    trial[j0].onset = 0;
  }
  if (trial[j0].onset + length >= experiment_duration) {
    VWarning(" experiment_duration exceeded",experiment_duration);
    length = experiment_duration - trial[j0].onset-0.5;
    VWarning(" parameter '-length' set to %f\n",length);
  }

  int nt = (int)(length/temporal_resolution + 0.5);
  fprintf(stderr," number of output timesteps: %d\n",nt);

  /* ini output data structs  */
  VImage *dest = (VImage *) VCalloc(nslices, sizeof(VImage));
  VAttrList out_list = VCreateAttrList();
  if (geoinfo != NULL) VSetGeoInfo(geoinfo,out_list);
  for (slice=0; slice<nslices; slice++) {
    dest[slice] = VCreateImage(nt,nrows,ncols,VPixelRepn(src[slice]));
    VFillImage(dest[slice],VAllBands,0);
    VCopyImageAttrs (src[slice], dest[slice]);
    VAppendAttr(out_list,"image",NULL,VImageRepn,dest[slice]);
  }

  /* spline interpolation  */
  double tstep = temporal_resolution;
  double *xx = (double *) VCalloc(ntimesteps,sizeof(double));
  double *yy = (double *) VCalloc(ntimesteps,sizeof(double));

  gsl_interp_accel *acc = gsl_interp_accel_alloc ();
  gsl_spline *spline = gsl_spline_alloc (gsl_interp_cspline, ntimesteps);

  for (slice=0; slice<nslices; slice++) {
    for (row=0; row<nrows; row++) {
      for (col=0; col<ncols; col++) {

	for (k=0; k<ntimesteps; k++) {
	  xx[k] = tr*((double)k);
	  yy[k] = (double)VGetPixel(src[slice],k,row,col);
	}
	gsl_spline_init (spline, xx, yy, ntimesteps);

	k=0;
	for (t=0; t<length; t += tstep) {
	  if (k >= nt) break;
	  double xi = trial[j].onset + t;
	  double yi = 0;
	  if (xi < experiment_duration && xi >= 0) {
	    if (gsl_spline_eval_e (spline,xi,acc,&yi) == GSL_EDOM) continue;
	  }
	  VSetPixel(dest[slice],k,row,col,yi);
	  k++;
	}
      }
    }
  }

  /* write to disk */
  if (! VWriteFile (out_file, out_list)) exit (1);
  fprintf (stderr, "%s: done.\n", argv[0]);
  exit(0);
}


#pragma once

#include "BoundingRegion.h"
#include "Ellipsoid.h"
#include "S2CellID.h"

#include <CesiumGeometry/CullingResult.h>
#include <CesiumGeometry/Plane.h>

#include <glm/vec3.hpp>
#include <gsl/span>

#include <array>
#include <string_view>

namespace CesiumGeospatial {

/**
 * A tile bounding volume specified as an S2 cell token with minimum and maximum
 * heights. The bounding volume is a k DOP. A k-DOP is the Boolean intersection
 * of extents along k directions.
 *
 * @param cellID The S2 cell ID.
 * @param minimumHeight The minimum height of the bounding volume.
 * @param maximumHeight The maximum height of the bounding volume.
 * @param ellipsoid The ellipsoid.
 */
class CESIUMGEOSPATIAL_API S2CellBoundingVolume final {
public:
  S2CellBoundingVolume(
      const S2CellID& cellID,
      double minimumHeight,
      double maximumHeight,
      const Ellipsoid& ellipsoid = Ellipsoid::WGS84);

  /**
   * @brief Gets this bounding volume's cell ID.
   */
  const S2CellID& getCellID() const { return this->_cellID; }

  /**
   * @brief Gets the minimum height of the cell.
   */
  double getMinimumHeight() const noexcept { return this->_minimumHeight; }

  /**
   * @brief Gets the maximum height of the cell.
   */
  double getMaximumHeight() const noexcept { return this->_maximumHeight; }

  /**
   * @brief Gets the center of this bounding volume in ellipsoid-fixed (ECEF)
   * coordinates.
   */
  glm::dvec3 getCenter() const noexcept;

  /**
   * @brief Gets the either corners of the bounding volume, in ellipsoid-fixed
   * (ECEF) coordinates.
   *
   * @return An array of positions with a `size()` of 8.
   */
  gsl::span<const glm::dvec3> getVertices() const noexcept;

  /**
   * @brief Determines on which side of a plane the bounding volume is located.
   *
   * @param plane The plane to test against.
   * @return The {@link CesiumGeometry::CullingResult}
   *  * `Inside` if the entire region is on the side of the plane the normal is
   * pointing.
   *  * `Outside` if the entire region is on the opposite side.
   *  * `Intersecting` if the region intersects the plane.
   */
  CesiumGeometry::CullingResult
  intersectPlane(const CesiumGeometry::Plane& plane) const noexcept;

  /**
   * @brief Computes the distance squared from a given position to the closest
   * point on this bounding volume. The position must be expressed in
   * ellipsoid-centered (ECEF) coordinates.
   *
   * @param position The position
   * @return The estimated distance squared from the bounding box to the point.
   *
   * @snippet TestOrientedBoundingBox.cpp distanceSquaredTo
   */
  double
  computeDistanceSquaredToPosition(const glm::dvec3& position) const noexcept;

  /**
   * @brief Gets the six planes that bound the volume.
   *
   * @return An array of planes with a `size()` of 6.
   */
  gsl::span<const CesiumGeometry::Plane> getBoundingPlanes() const noexcept;

  /**
   * @brief Computes the bounding begion that best fits this S2 cell volume.
   *
   * @return The bounding region.
   */
  BoundingRegion computeBoundingRegion() const noexcept;

private:
  S2CellID _cellID;
  double _minimumHeight;
  double _maximumHeight;
  glm::dvec3 _center;
  std::array<CesiumGeometry::Plane, 6> _boundingPlanes;
  std::array<glm::dvec3, 8> _vertices;
};

} // namespace CesiumGeospatial


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include "Utils\buffer.h"
#include "MageSettings.h"           // for CameraIdentity
#include "ORBDescriptor.h"

#include "Memory\allocators.h"
#include "arcana\utils\serialization\serializable.h"

#include <opencv2\core\core.hpp>
#include <stdint.h>
#include <atomic>

#include <gsl\span>

namespace mage
{
    using ImageAllocator = memory::std_allocator<void*, block_splitting_allocation_strategy>;

    template<typename AllocT>
    struct ImageData : mira::serializable<ImageData<AllocT>>
    {
        ImageData(CameraIdentity cameraIdentity, size_t maxFeatures, float pyramidScale, size_t numLevels, float imageBorder, const AllocT& allocator)
            :   m_cameraIdentity{ cameraIdentity },
                m_featureCount{ 0 },
                m_pyramidScale { pyramidScale },
                m_numLevels { numLevels },
                m_imageBorder { imageBorder },
                m_published{ false },
                m_maxFeatures{ maxFeatures },
                m_keypointBuffer{ maxFeatures, allocator },
                m_descriptorBuffer{ maxFeatures, allocator }
        {}

        template<typename StreamT, typename = mira::is_stream_t<StreamT>>
        ImageData(CameraIdentity cameraIdentity, size_t maxFeatures, float pyramidScale, size_t numLevels, float imageBorder, const AllocT& allocator, StreamT& stream)
            : ImageData{ cameraIdentity, imageWidth, imageHeight, maxFeatures, pyramidScale, allocator }
        {
            deserialize(stream);
        }

        static constexpr auto members()
        {
            return declare_members(
                &ImageData::m_cameraIdentity,
                &ImageData::m_featureCount,
                &ImageData::m_pyramidScale,
                &ImageData::m_numLevels,
                &ImageData::m_imageBorder,
                &ImageData::m_published,
                &ImageData::m_maxFeatures,
                &ImageData::m_keypointBuffer,
                &ImageData::m_descriptorBuffer
            );
        }

        /*
            Inserts the keypoints into the image data, and returns how many
            items it copied. Because we only support a fixed size, we
            only copy items if we have space.
        */
        size_t Insert(gsl::span<const cv::KeyPoint> keypoints)
        {
            auto toCopy = std::min<int>(gsl::narrow_cast<int>(keypoints.size()), gsl::narrow_cast<int>(m_maxFeatures - m_featureCount));
            m_featureCount += m_keypointBuffer.copy(keypoints.data(), keypoints.data() + toCopy, m_featureCount);
            return toCopy;
        }

        size_t Insert(const cv::KeyPoint& kp)
        {
            if (m_featureCount == m_maxFeatures)
                return 0;

            m_keypointBuffer[m_featureCount] = kp;
            m_featureCount++;
            return 1;
        }

        size_t Insert(const cv::KeyPoint* beg, const cv::KeyPoint* end)
        {
            auto toCopy = std::min<int>(gsl::narrow_cast<int>(end - beg), gsl::narrow_cast<int>(m_maxFeatures - m_featureCount));
            m_featureCount += m_keypointBuffer.copy(beg, beg + toCopy, m_featureCount);
            return toCopy;
        }

        size_t GetMaxFeatures() const
        {
            return m_maxFeatures;
        }

        size_t GetFeatureCount() const
        {
            return m_featureCount;
        }

        float GetPyramidScale() const
        {
            return m_pyramidScale;
        }

        size_t GetNumLevels() const
        {
            return m_numLevels;
        }

        float GetImageBorder() const
        {
            return m_imageBorder;
        }

        CameraIdentity GetCameraIdentity() const
        {
            return m_cameraIdentity;
        }

        const cv::KeyPoint& GetKeypoint(size_t idx) const
        {
            return const_cast<ImageData*>(this)->GetKeypoint(idx);
        }

        cv::KeyPoint& GetKeypoint(size_t idx)
        {
            assert(0 <= idx && idx < m_featureCount);
            return m_keypointBuffer[idx];
        }

        gsl::span<const cv::KeyPoint> GetKeypoints() const
        {
            return { m_keypointBuffer.begin(), m_keypointBuffer.begin() + m_featureCount };
        }

        gsl::span<cv::KeyPoint> GetKeypoints()
        {
            return { m_keypointBuffer.begin(), m_keypointBuffer.begin() + m_featureCount };
        }

        const ORBDescriptor& GetDescriptor(size_t idx) const
        {
            return const_cast<ImageData*>(this)->GetDescriptor(idx);
        }

        ORBDescriptor& GetDescriptor(size_t idx)
        {
            assert(0 <= idx && idx < m_featureCount);
            return m_descriptorBuffer[idx];
        }

        gsl::span<const ORBDescriptor> GetDescriptors() const
        {
            return const_cast<ImageData*>(this)->GetDescriptors();
        }

        gsl::span<ORBDescriptor> GetDescriptors()
        {
            return { m_descriptorBuffer.begin(), m_descriptorBuffer.begin() + m_featureCount };
        }

        void SetDescriptors(gsl::span<const ORBDescriptor> values)
        {
            assert(m_featureCount == values.size());
            m_descriptorBuffer.copy(values.begin(), values.end());
        }

        void ZeroOutDescriptors()
        {
            m_descriptorBuffer.fill(0);
        }

        /*
            Returns whether or not this image was ever published to the map
        */
        bool IsPublished() const
        {
            return m_published;
        }

        void MarkAsPublished() const
        {
            m_published = true;
        }

        template<typename DerivedT>
        static size_t AllocationSizeInBytes(size_t maxFeatures)
        {
            return sizeof(DerivedT) + maxFeatures * sizeof(cv::KeyPoint) + maxFeatures * sizeof(ORBDescriptor);
        }

    private:
        template<typename T>
        using rebind_allocator = typename std::allocator_traits<AllocT>::template rebind_alloc<T>;

        size_t m_featureCount{};
        mutable std::atomic<bool> m_published{ false };

        const size_t m_maxFeatures{};
        const float m_pyramidScale{};
        const size_t m_numLevels{}; //TODO: change numlevels to be reasonable with image resolution
        const float m_imageBorder{};
        const CameraIdentity m_cameraIdentity{};

        buffer<cv::KeyPoint, rebind_allocator<cv::KeyPoint>> m_keypointBuffer{};
        buffer<ORBDescriptor, rebind_allocator<ORBDescriptor>> m_descriptorBuffer{};
    };
}


/* permutation/gsl_permutation.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004 Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_PERMUTATION_H__
#define __GSL_PERMUTATION_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

struct gsl_permutation_struct
{
  size_t size;
  size_t *data;
};

typedef struct gsl_permutation_struct gsl_permutation;

GSL_EXPORT gsl_permutation *gsl_permutation_alloc (const size_t n);
GSL_EXPORT gsl_permutation *gsl_permutation_calloc (const size_t n);
GSL_EXPORT void gsl_permutation_init (gsl_permutation * p);
GSL_EXPORT void gsl_permutation_free (gsl_permutation * p);
GSL_EXPORT int gsl_permutation_memcpy (gsl_permutation * dest, const gsl_permutation * src);

GSL_EXPORT int gsl_permutation_fread (FILE * stream, gsl_permutation * p);
GSL_EXPORT int gsl_permutation_fwrite (FILE * stream, const gsl_permutation * p);
GSL_EXPORT int gsl_permutation_fscanf (FILE * stream, gsl_permutation * p);
GSL_EXPORT int gsl_permutation_fprintf (FILE * stream, const gsl_permutation * p, const char *format);

GSL_EXPORT size_t gsl_permutation_size (const gsl_permutation * p);
GSL_EXPORT size_t * gsl_permutation_data (const gsl_permutation * p);

GSL_EXPORT size_t gsl_permutation_get (const gsl_permutation * p, const size_t i);
GSL_EXPORT int gsl_permutation_swap (gsl_permutation * p, const size_t i, const size_t j);

GSL_EXPORT int gsl_permutation_valid (gsl_permutation * p);
GSL_EXPORT void gsl_permutation_reverse (gsl_permutation * p);
GSL_EXPORT int gsl_permutation_inverse (gsl_permutation * inv, const gsl_permutation * p);
GSL_EXPORT int gsl_permutation_next (gsl_permutation * p);
GSL_EXPORT int gsl_permutation_prev (gsl_permutation * p);
GSL_EXPORT int gsl_permutation_mul (gsl_permutation * p, const gsl_permutation * pa, const gsl_permutation * pb);

GSL_EXPORT int gsl_permutation_linear_to_canonical (gsl_permutation * q, const gsl_permutation * p);
GSL_EXPORT int gsl_permutation_canonical_to_linear (gsl_permutation * p, const gsl_permutation * q);

GSL_EXPORT size_t gsl_permutation_inversions (const gsl_permutation * p);
GSL_EXPORT size_t gsl_permutation_linear_cycles (const gsl_permutation * p);
GSL_EXPORT size_t gsl_permutation_canonical_cycles (const gsl_permutation * q);

#ifdef HAVE_INLINE

extern inline
size_t
gsl_permutation_get (const gsl_permutation * p, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= p->size)
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return p->data[i];
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_PERMUTATION_H__ */


#include <stdio.h>
#include <string.h>
#include <gsl/gsl_statistics_double.h>
#include "tz_error.h"
#include "tz_constant.h"
#include "tz_image_lib.h"
#include "tz_objdetect.h"
#include "tz_imatrix.h"
#include "tz_stack_math.h"
#include "tz_stack_bwdist.h"
#include "tz_stack_bwmorph.h"
#include "tz_voxel_linked_list.h"
#include "tz_stack_sampling.h"
#include "tz_stack_draw.h"
#include "tz_voxel_graphics.h"

INIT_EXCEPTION_MAIN(e)

int main(int argc, char* argv[])
{
  char neuron_name[100];
  if (argc == 1) {
    strcpy(neuron_name, "fly_neuron");
  } else {
    strcpy(neuron_name, argv[1]);
  }

  char file_path[100];

  sprintf(file_path, "../data/%s.tif", neuron_name);
  Stack *canvas = Read_Stack(file_path);

  Translate_Stack(canvas, COLOR, 1);
  Print_Stack_Info(canvas);

  sprintf(file_path, "../data/%s/grow_bundle.tif", neuron_name);
  Stack *bundle = Read_Stack(file_path);
  Stack *bundle_boundary = Stack_Perimeter(bundle, NULL, 4);
  Kill_Stack(bundle);

  sprintf(file_path, "../data/%s/bundle_seed.tif", neuron_name);
  Stack *seed = Read_Stack(file_path);

  Rgb_Color color;
  Set_Color(&color, 0, 255, 0);
  Stack_Label_Bwc(canvas, bundle_boundary, color);
  Set_Color(&color, 255, 0, 0);
  Stack_Label_Bwc(canvas, seed, color);

  sprintf(file_path, "../data/%s/bundle_label.tif", neuron_name);
  Write_Stack(file_path, canvas);

  Kill_Stack(canvas);
  Kill_Stack(bundle_boundary);
  Kill_Stack(seed);

  return 0;
}


/**
 * @file binauralprocessing.h
 * @brief Binaural processing
 * @author Kenichi Kumatani
 */
#ifndef BINAURALPROCESSING_H
#define BINAURALPROCESSING_H

#include <stdio.h>
#include <assert.h>
#include <float.h>

#include <gsl/gsl_block.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_fft_complex.h>
#include <common/refcount.h>
#include "common/jexception.h"

#include "stream/stream.h"
#include "postfilter/spectralsubtraction.h"
#include "beamformer/spectralinfoarray.h"
#include "beamformer/beamformer.h"

class BinaryMaskFilter : public VectorComplexFeatureStream {
public:
  BinaryMaskFilter( unsigned chanX, VectorComplexFeatureStreamPtr &srcL, VectorComplexFeatureStreamPtr &srcR,
		       unsigned M, float threshold, float alpha,
		       float dEta = 0.01, const String& nm = "BinaryMaskFilter" );
  ~BinaryMaskFilter();
  virtual const gsl_vector_complex* next(int frame_no = -5);
  virtual void reset();
  void set_threshold( float threshold ){ threshold_ = threshold; }
  void set_thresholds( const gsl_vector *thresholds );
  double threshold(){ return threshold_; }
  gsl_vector *thresholds(){ return threshold_per_freq_; }

#ifdef ENABLE_LEGACY_BTK_API
  void setThreshold( float threshold ){ set_threshold(threshold); }
  void setThresholds( const gsl_vector *thresholds ){ set_thresholds(thresholds); }
  double getThreshold(){ return threshold(); }
  gsl_vector *getThresholds(){ return thresholds(); }
#endif

protected:
  VectorComplexFeatureStreamPtr srcL_; /* left channel */
  VectorComplexFeatureStreamPtr srcR_; /* right channel */
  unsigned          chanX_;  /* want to extract the index of a channel */
  gsl_vector_float *prevMu_; /* binary mask at a previous frame */
  float             alpha_;  /* forgetting factor */
  float             dEta_;        /* flooring value */
  float             threshold_;   /* threshold for the ITD */
  gsl_vector        *threshold_per_freq_;
};

typedef Inherit<BinaryMaskFilter, VectorComplexFeatureStreamPtr> BinaryMaskFilterPtr;

/**
   @class Implementation of binary masking based on C. Kim's Interspeech2010 paper
   @brief binary mask two inputs based of the threshold of the interaural time delay (ITD)
   @usage
 */
class KimBinaryMaskFilter : public BinaryMaskFilter {
public:
  KimBinaryMaskFilter( unsigned chanX, VectorComplexFeatureStreamPtr &srcL, VectorComplexFeatureStreamPtr &srcR,
		       unsigned M, float threshold, float alpha,
		       float dEta = 0.01, float dPowerCoeff = 1/15.0, const String& nm = "KimBinaryMaskFilter" );
  ~KimBinaryMaskFilter();
  virtual const gsl_vector_complex* next(int frame_no = -5);
  virtual void reset();

  virtual const gsl_vector_complex* masking1( const gsl_vector_complex* ad_X_L, const gsl_vector_complex* ad_X_R, float threshold );

protected:
  float             dpower_coeff_; /* power law non-linearity */
};

typedef Inherit<KimBinaryMaskFilter, BinaryMaskFilterPtr> KimBinaryMaskFilterPtr;

/**
   @class Implementation of estimating the threshold for C. Kim's ITD-based binary masking
   @brief binary mask two inputs based of the threshold of the interaural time delay (ITD)
   @usage
 */
class KimITDThresholdEstimator : public KimBinaryMaskFilter {
public:
  KimITDThresholdEstimator( VectorComplexFeatureStreamPtr &srcL, VectorComplexFeatureStreamPtr &srcR, unsigned M, float minThreshold = 0, float maxThreshold = 0, float width = 0.02, float minFreq= -1, float maxFreq=-1, int sampleRate=-1, float dEta = 0.01, float dPowerCoeff = 1/15.0, const String& nm = "KimITDThresholdEstimator" );
  ~KimITDThresholdEstimator();
  virtual const gsl_vector_complex* next(int frame_no = -5);
  virtual void   reset();
  virtual double calc_threshold();
  const gsl_vector* cost_function();

#ifdef ENABLE_LEGACY_BTK_API
  virtual double calcThreshold(){ return calc_threshold(); }
  const gsl_vector* getCostFunction(){ return cost_function(); }
#endif

protected:
  virtual void accumStats1( const gsl_vector_complex* ad_X_L, const gsl_vector_complex* ad_X_R );

  /* for restricting the search space */
  float min_threshold_;
  float max_threshold_;
  float width_;
  unsigned min_fbinX_;
  unsigned max_fbinX_;
  /* work space */
  double *cost_func_values_;
  double *sigma_T_;
  double *sigma_I_;
  double *mean_T_;
  double *mean_I_;
  unsigned int nCand_;
  unsigned int nSamples_;
  gsl_vector *buffer_; /* for returning values from the python */
  bool cost_func_computed_;
};

typedef Inherit<KimITDThresholdEstimator, KimBinaryMaskFilterPtr> KimITDThresholdEstimatorPtr;

/**
	@class
 */
class IIDBinaryMaskFilter : public BinaryMaskFilter {
public:
	IIDBinaryMaskFilter( unsigned chanX, VectorComplexFeatureStreamPtr &srcL, VectorComplexFeatureStreamPtr &srcR,
						unsigned M, float threshold, float alpha,
						float dEta = 0.01, const String& nm = "IIDBinaryMaskFilter" );
	~IIDBinaryMaskFilter();
	virtual const gsl_vector_complex* next(int frame_no = -5);
	virtual void reset();

	virtual const gsl_vector_complex* masking1( const gsl_vector_complex* ad_X_L, const gsl_vector_complex* ad_X_R, float threshold );
};

typedef Inherit<IIDBinaryMaskFilter, BinaryMaskFilterPtr> IIDBinaryMaskFilterPtr;

/**
 @class binary masking based on a difference between magnitudes of two beamformers' outputs.
 @brief set zero to beamformer's output with the smaller magnitude over frequency bins
 */
class IIDThresholdEstimator : public KimITDThresholdEstimator {
public:
	IIDThresholdEstimator( VectorComplexFeatureStreamPtr &srcL, VectorComplexFeatureStreamPtr &srcR, unsigned M,
							 float minThreshold = 0, float maxThreshold = 0, float width = 0.02,
							 float minFreq= -1, float maxFreq=-1, int sampleRate=-1, float dEta = 0.01, float dPowerCoeff = 0.5, const String& nm = "IIDThresholdEstimator" );
	~IIDThresholdEstimator();
	virtual const gsl_vector_complex* next(int frame_no = -5);
	virtual void reset();
	virtual double calc_threshold();

#ifdef ENABLE_LEGACY_BTK_API
	virtual double calcThreshold(){ return calc_threshold(); }
#endif

protected:
        virtual void accumStats1( const gsl_vector_complex* ad_X_L, const gsl_vector_complex* ad_X_R );

private:
        double *_Y4_T;
        double *_Y4_I;
        double _beta;
};

typedef Inherit<IIDThresholdEstimator, KimITDThresholdEstimatorPtr> IIDThresholdEstimatorPtr;

/**
 @class binary masking based on a difference between magnitudes of two beamformers' outputs at each frequency bin.
 @brief set zero to beamformer's output with the smaller magnitude at each frequency bin
 */
class FDIIDThresholdEstimator : public BinaryMaskFilter {
public:
  FDIIDThresholdEstimator( VectorComplexFeatureStreamPtr &srcL, VectorComplexFeatureStreamPtr &srcR, unsigned M,
			   float minThreshold = 0, float maxThreshold = 0, float width = 1000,
			   float dEta = 0.01, float dPowerCoeff = 1/15.0, const String& nm = "FDIIDThresholdEstimator" );
  ~FDIIDThresholdEstimator();
  virtual const gsl_vector_complex* next(int frame_no = -5);
  virtual void   reset();
  virtual double calc_threshold();
  const gsl_vector* cost_function(unsigned freqX);

#ifdef ENABLE_LEGACY_BTK_API
  virtual double calcThreshold(){ return calc_threshold(); }
  const gsl_vector* getCostFunction( unsigned freqX ){ return cost_function(freqX); }
#endif

protected:
  virtual void accumStats1( const gsl_vector_complex* ad_X_L, const gsl_vector_complex* ad_X_R );

  /* for restricting the search space */
  float min_threshold_;
  float max_threshold_;
  float width_;
  float dpower_coeff_;

  /* work space */
  double **cost_func_values_;
  double **_Y4;
  double **_sigma;
  double **_mean;
  double _beta;
  unsigned int nCand_;
  unsigned int nSamples_;
  gsl_vector *buffer_; /* for returning values from the python */
  bool cost_func_computed_;
};

typedef Inherit<FDIIDThresholdEstimator, BinaryMaskFilterPtr> FDIIDThresholdEstimatorPtr;

#endif