/*****************************************************************************
   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 MAIN

#include "afni.h"

/*-----------------------------------------------------------------------------
   Register a function to be called from the "FIM+" menu.
     menu_name = label for menu
     nbrik     = number of values returned by user_func
     user_func = function to call for each data time series
     user_data = extra data to go to user_func

   void user_func( int n, float *ts, void *user_data, int nbrik, void *v )

     n         = length of time series
     ts[]      = time series data
     user_data = whatever you want (same as above)
     nbrik     = same as above
     val       = (float *) v = output array of values
     val[]     = user_func should fill val[0..nbrik-1] with values to save
                 (presumably computed from ts[])

   user_func should not destroy the data in ts[], since it will be reused
   if more than one fimfunc is used at a time.

   Before the first call with time series data, user_func will be called like so:
     user_func( n,NULL,user_data,nbrik, FIMdata *fd ) ;
   where FIMdata is the type
     typedef struct {
        MRI_IMAGE *ref_ts , *ort_ts ;
        int nvox , ignore , polort ;
     } FIMdata ;
   and fd is used to pass in the parameters set by the user:
      fd->ref_ts = float image of reference ("ideal") time series
                   fd->ref_ts->nx = length of data
                   fd->ref_ts->ny = number of ref time series
                   MRI_FLOAT_PTR(fd->ref_ts) = pointer to ref timeseries data
      fd->ort_ts = similarly for the orts (but this may be NULL)
      fd->nvox   = number of voxels that will be passed in
                 = number of "normal" calls to user_data
      fd->ignore = ignore setting (but ignored data is included in ts[])
      fd->polort = polort setting (but no detrending is done to ts[])
   N.B.: FIMdata is typedef-ed in afni.h.

   After the last (nvox-th) time series is processed, user_func will be called
   one last time, with the form
     user_func( -k,NULL,user_data,nbrik, dset ) ;
   where dset is a pointer to the dataset (THD_3dim_dataset *) that has
   just been constructed.  The value k (negative of the first argument) will
   be the index of the first sub-brick in the dataset - it contains the data
   from val[0].  If desired, you can use this final call to set sub-brick
   parameters for sub-bricks k through k+nbrik-1 (for val[0] .. val[nbrik-1]).

   Both special calls have the second argument NULL, which is how user_func
   can distinguish them from a normal call with timeseries data in ts[].

   The initialization call will have the first argument be the number of
   points in the timeseries to come ('n') - this will be positive.  The
   final call will have the first argument be the negative of the sub-brick
   index of the first sub-brick created by this routine.  This value will
   be non-positive (will be 0 or less), which is how the initialization
   and final calls can be distinguished.  In each of these cases, the
   last argument must be properly cast to the correct pointer type before
   being used.

   A sample fimfunc for the Spearman rank correlation, spearman_fimfunc(),
   is given at the end of this file.
--------------------------------------------------------------------------------*/

void AFNI_register_fimfunc( char *menu_name , int nbrik ,
                            generic_func *user_func , void *user_data )
{
   MCW_function_list *rlist = &(GLOBAL_library.registered_fim) ;
   int num = rlist->num ;

ENTRY("AFNI_register_fimfunc") ;

   if( menu_name == NULL || menu_name[0] == '\0' ||
       nbrik <= 0        || user_func == NULL      ) EXRETURN ; /* bad inputs! */

   if( num == sizeof(int) ) EXRETURN ;  /* too many already! */

   if( num == 0 ){
     rlist->flags=NULL; rlist->labels=NULL; rlist->funcs=NULL;
     rlist->func_data=NULL; rlist->func_code=NULL; rlist->func_init=NULL;
   }

   rlist->flags = (int *) XtRealloc( (char *)rlist->flags, sizeof(int)*(num+1) ) ;

   rlist->labels = (char **) XtRealloc( (char *)rlist->labels ,
                                        sizeof(char *)*(num+1) ) ;

   rlist->funcs = (generic_func **) XtRealloc( (char *)rlist->funcs ,
                                               sizeof(generic_func *)*(num+1) ) ;

   rlist->func_data = (void **) XtRealloc( (char *)rlist->func_data ,
                                           sizeof(void *)*(num+1) ) ;

   rlist->func_code = (int *) XtRealloc( (char *)rlist->func_code, sizeof(int)*(num+1) ) ;

   rlist->func_init = (generic_func **) XtRealloc( (char *)rlist->func_init ,
                                                   sizeof(generic_func *)*(num+1) ) ;

   rlist->flags[num]     = nbrik ;
   rlist->labels[num]    = XtNewString(menu_name) ;
   rlist->funcs[num]     = user_func ;
   rlist->func_data[num] = user_data ;
   rlist->func_code[num] = FUNC_FIM  ;
   rlist->func_init[num] = NULL ;

   rlist->num = num+1 ;
   EXRETURN ;
}

/*******************************************************************************
  Code for the Spearman rank and quandrant correlation fimfuncs
********************************************************************************/

static float spearman_rank_manycorr( int n , float *x ,
                                     int nr, float *rv, float **r )
{
   register int ii ;
   register float ss ;
   int jj ;
   float bb , xv ;

   if( nr == 1 ) return spearman_rank_corr( n,x,rv[0],r[0] ) ;

   xv = spearman_rank_prepare( n , x ) ; if( xv <= 0.0 ) return 0.0 ;

   for( jj=0,bb=0.0 ; jj < nr ; jj++ ){
      for( ii=0,ss=0.0 ; ii < n ; ii++ ) ss += x[ii] * r[jj][ii] ;
      ss /= sqrt(rv[jj]*xv) ;
      if( fabs(ss) > fabs(bb) ) bb = ss ;
   }

   return bb ;
}

static float quadrant_manycorr( int n , float *x ,
                                int nr, float *rv, float **r )
{
   register int ii ;
   register float ss ;
   int jj ;
   float bb , xv ;

   if( nr == 1 ) return quadrant_corr( n,x,rv[0],r[0] ) ;

   xv = quadrant_corr_prepare( n , x ) ; if( xv <= 0.0 ) return 0.0 ;

   for( jj=0,bb=0.0 ; jj < nr ; jj++ ){
      for( ii=0,ss=0.0 ; ii < n ; ii++ ) ss += x[ii] * r[jj][ii] ;
      ss /= sqrt(rv[jj]*xv) ;
      if( fabs(ss) > fabs(bb) ) bb = ss ;
   }

   return bb ;
}

/*--------------------------------------------------------------------------
  A sample fimfunc to compute the Spearman rank correlation
----------------------------------------------------------------------------*/

void spearman_fimfunc( int n, float *ts, void *ud, int nb, void *val )
{
   static float *tsc=NULL , *refc=NULL , *refv=NULL , **rr ;
   static int   *keep=NULL ;
   static int    ntsc , nref , nkeep , polort,ignore , ncall;

   int ii , kk ;
   float *v ;

   /*--- handle special cases ---*/

   if( ts == NULL ){

      /*--- the initialization call ---*/

      if( n > 0 ){

         FIMdata *fd = (FIMdata *) val ;

         polort = fd->polort ;  /* not used here */
         ignore = fd->ignore ;
         ncall  = 0 ;           /* how many times we have been called */

         /* make workspace for copy of ts data when it arrives */

         ntsc = n ;
         if( tsc != NULL ) free(tsc) ;
         tsc = (float *) malloc(sizeof(float)*ntsc) ;

         /* make copy of ref data */

         nref = fd->ref_ts->ny ;
         if( refc != NULL ) free(refc) ;
         if( refv != NULL ) free(refv) ;
         if( keep != NULL ) free(keep) ;
         if( rr   != NULL ) free(rr) ;
         refc = (float *) malloc(sizeof(float)*ntsc*nref) ;  /* copy of ref   */
         refv = (float *) malloc(sizeof(float)*nref) ;      /* rank variances */
         keep = (int *)   malloc(sizeof(int)*ntsc) ;       /* keeper indices  */
         rr   = (float **)malloc(sizeof(float *)*nref) ;  /* convenience ptrs */

         for( kk=0 ; kk < nref ; kk++ ){
            rr[kk] = refc+kk*ntsc ;                       /* compute ptr */
            memcpy( rr[kk] ,                              /* copy data  */
                    MRI_FLOAT_PTR(fd->ref_ts) + kk*fd->ref_ts->nx ,
                    sizeof(float)*ntsc  ) ;
         }

         /* mark those places we will keep (where ref is OK) */

         for( nkeep=0,ii=ignore ; ii < ntsc ; ii++ ){ /* for each time point */
            for( kk=0 ; kk < nref ; kk++ )            /* check each ref     */
               if( rr[kk][ii] >= WAY_BIG ) break ;

            if( kk == nref ) keep[nkeep++] = ii ;     /* mark if all are OK */
         }

         /* compress ref, eliminating non-keepers */

         if( nkeep < ntsc ){
            for( ii=0 ; ii < nkeep ; ii++ ){
               for( kk=0 ; kk < nref ; kk++ )
                  rr[kk][ii] = rr[kk][keep[ii]] ;  /* copy backwards */
            }
         }

         /* prepare each ref vector for rank processing */

         for( kk=0 ; kk < nref ; kk++ )
            refv[kk] = spearman_rank_prepare( nkeep , rr[kk] ) ;

#if 0
fprintf(stderr,"spearman_fimfunc: initialize ntsc=%d nkeep=%d nref=%d\n",
        ntsc,nkeep,nref);
#endif

         return ;

      /*--- the ending call ---*/

      } else {
         THD_3dim_dataset *dset = (THD_3dim_dataset *) val ;
         int kb = -n ;

         free(tsc)  ; tsc  = NULL ;  /* free workspaces */
         free(refc) ; refc = NULL ;
         free(refv) ; refv = NULL ;
         free(keep) ; keep = NULL ;
         free(rr)   ; rr   = NULL ;
         rank_order_float(0,NULL) ;

         /* edit sub-brick statistical parameters and name */

         EDIT_BRICK_TO_FICO( dset , kb , nkeep , (nref==1)?1:2 , 1 ) ;
         EDIT_BRICK_LABEL( dset , kb , "Spearman CC" ) ;

#if 0
fprintf(stderr,"spearman_fimfunc: finalize with kb=%d\n",kb);
#endif

         return ;
      }
   }

   /*--- the normal case [with data] ---*/

   ncall++ ;
#if 0
if(ncall%1000==0) fprintf(stderr,"spearman_fimfunc: ncall=%d\n",ncall);
#endif

   /* copy input timeseries data (since we will mangle it) */

   if( nkeep < ntsc )
      for( ii=0 ; ii < nkeep ; ii++ ) tsc[ii] = ts[keep[ii]]; /* the hard way */
   else
      memcpy(tsc,ts,sizeof(float)*ntsc) ;                     /* the easy way */

   /* compute our single result */

   v    = (float *) val ;
   v[0] = spearman_rank_manycorr( nkeep , tsc , nref , refv , rr ) ;

   return ;
}

/*--------------------------------------------------------------------------
  A sample fimfunc to compute the quadrant correlation
----------------------------------------------------------------------------*/

void quadrant_fimfunc( int n, float *ts, void *ud, int nb, void *val )
{
   static float *tsc=NULL , *refc=NULL , *refv=NULL , **rr ;
   static int   *keep=NULL ;
   static int    ntsc , nref , nkeep , polort,ignore , ncall;

   int ii , kk ;
   float *v ;

   /*--- handle special cases ---*/

   if( ts == NULL ){

      /*--- the initialization call ---*/

      if( n > 0 ){

         FIMdata *fd = (FIMdata *) val ;

         polort = fd->polort ;  /* not used here */
         ignore = fd->ignore ;
         ncall  = 0 ;           /* how many times we have been called */

         /* make workspace for copy of ts data when it arrives */

         ntsc = n ;
         if( tsc != NULL ) free(tsc) ;
         tsc = (float *) malloc(sizeof(float)*ntsc) ;

         /* make copy of ref data */

         nref = fd->ref_ts->ny ;
         if( refc != NULL ) free(refc) ;
         if( refv != NULL ) free(refv) ;
         if( keep != NULL ) free(keep) ;
         if( rr   != NULL ) free(rr) ;
         refc = (float *) malloc(sizeof(float)*ntsc*nref) ;  /* copy of ref   */
         refv = (float *) malloc(sizeof(float)*nref) ;      /* rank variances */
         keep = (int *)   malloc(sizeof(int)*ntsc) ;       /* keeper indices  */
         rr   = (float **)malloc(sizeof(float *)*nref) ;  /* convenience ptrs */

         for( kk=0 ; kk < nref ; kk++ ){
            rr[kk] = refc+kk*ntsc ;                       /* compute ptr */
            memcpy( rr[kk] ,                              /* copy data  */
                    MRI_FLOAT_PTR(fd->ref_ts) + kk*fd->ref_ts->nx ,
                    sizeof(float)*ntsc  ) ;
         }

         /* mark those places we will keep (where ref is OK) */

         for( nkeep=0,ii=ignore ; ii < ntsc ; ii++ ){ /* for each time point */
            for( kk=0 ; kk < nref ; kk++ )            /* check each ref     */
               if( rr[kk][ii] >= WAY_BIG ) break ;

            if( kk == nref ) keep[nkeep++] = ii ;     /* mark if all are OK */
         }

         /* compress ref, eliminating non-keepers */

         if( nkeep < ntsc ){
            for( ii=0 ; ii < nkeep ; ii++ ){
               for( kk=0 ; kk < nref ; kk++ )
                  rr[kk][ii] = rr[kk][keep[ii]] ;  /* copy backwards */
            }
         }

         /* prepare each ref vector for rank processing */

         for( kk=0 ; kk < nref ; kk++ )
            refv[kk] = quadrant_corr_prepare( nkeep , rr[kk] ) ;

#if 0
fprintf(stderr,"quadrant_fimfunc: initialize ntsc=%d nkeep=%d nref=%d\n",
        ntsc,nkeep,nref);
#endif

         return ;

      /*--- the ending call ---*/

      } else {
         THD_3dim_dataset *dset = (THD_3dim_dataset *) val ;
         int kb = -n ;

         free(tsc)  ; tsc  = NULL ;  /* free workspaces */
         free(refc) ; refc = NULL ;
         free(refv) ; refv = NULL ;
         free(keep) ; keep = NULL ;
         free(rr)   ; rr   = NULL ;
         rank_order_float(0,NULL) ;

         /* edit sub-brick statistical parameters and name */

         EDIT_BRICK_TO_FICO( dset , kb , nkeep , (nref==1)?1:2 , 1 ) ;
         EDIT_BRICK_LABEL( dset , kb , "Quadrant CC" ) ;

#if 0
fprintf(stderr,"quadrant_fimfunc: finalize with kb=%d\n",kb);
#endif

         return ;
      }
   }

   /*--- the normal case [with data] ---*/

   ncall++ ;
#if 0
if(ncall%1000==0) fprintf(stderr,"quadrant_fimfunc: ncall=%d\n",ncall);
#endif

   /* copy input timeseries data (since we will mangle it) */

   if( nkeep < ntsc )
      for( ii=0 ; ii < nkeep ; ii++ ) tsc[ii] = ts[keep[ii]]; /* the hard way */
   else
      memcpy(tsc,ts,sizeof(float)*ntsc) ;                     /* the easy way */

   /* compute our single result */

   v    = (float *) val ;
   v[0] = quadrant_manycorr( nkeep , tsc , nref , refv , rr ) ;

   return ;
}
