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

#include "mrilib.h"

/******************* Routines to shift two rows at a time ********************/

/*---------------------------------------------------------------------------
   Set the interpolation method for shifting:
   input is one of MRI_NN, MRI_LINEAR, MRI_CUBIC, or MRI_FOURIER.
-----------------------------------------------------------------------------*/

typedef void (*shift_func)(int,int,float,float *,float,float *) ;
static  shift_func shifter      = fft_shift2 ;
static  int        shift_method = MRI_FOURIER ;

void SHIFT_set_method( int mode )
{
   shift_method = mode ;
   switch( mode ){
      default:          shift_method = MRI_FOURIER ;  /* fall thru */
      case MRI_FOURIER: shifter = fft_shift2   ; break ;

      case MRI_LINEAR:  shifter = lin_shift2   ; break ;
      case MRI_CUBIC:   shifter = cub_shift2   ; break ;
      case MRI_QUINTIC: shifter = quint_shift2 ; break ;  /* Nov 1998 */
      case MRI_HEPTIC:  shifter = hept_shift2  ; break ;  /* Nov 1998 */

      case MRI_NN:      shifter = nn_shift2    ; break ;  /* experimental */
      case MRI_TSSHIFT: shifter = ts_shift2    ; break ;  /* Dec 1999 */

      case MRI_WSINC5:  shifter = wsinc5_shift2 ; break ;  /* Aug 2019 */
      case MRI_WSINC9:  shifter = wsinc9_shift2 ; break ;  /* Aug 2019 */
   }
   return ;
}

int SHIFT_get_method( void ){ return shift_method ; }

/*--------------------------------------------------------------------------
   The main entry point - note that g can be NULL, but f cannot.
---------------------------------------------------------------------------*/

void SHIFT_two_rows( int n, int nup, float af, float * f, float ag, float * g )
{
   shifter( n,nup,af,f,ag,g ) ; return ;
}

/*--------------------------------------------------------------------------
   Shift 2 rows at a time with the FFT:
     n   = length of a row
     nup = length to use for FFTs (must be even)
     af  = shift for row f
     ag  = shift for row g
   Input and output arrays are f[n] and g[n].  (Note: g may be NULL.)
----------------------------------------------------------------------------*/

#define ZFILL
#define RECUR

void fft_shift2( int n, int nup, float af, float * f, float ag, float * g )
{
   static int nupold=0 , nuptop=0 ;
   static complex * row=NULL , * cf=NULL , * cg=NULL ;

   int ii , nby2=nup/2 , n21=nby2+1 ;
   complex fac , gac ;
   float sf , sg , dk ;
#ifdef RECUR
   complex csf , csg ;
#endif

ENTRY("fft_shift2") ;

   /* 15 Mar 2001: shift too big ==> return all zeros */

   if( (af < -n || af > n) && (ag < -n || ag > n) ){
      for( ii=0 ; ii < n ; ii++ ) f[ii] = g[ii] = 0.0 ;
      EXRETURN ;
   }

   /* make new memory for row storage? */

   if( nup > nuptop ){
      if( row != NULL ){ free(row) ; free(cf) ; free(cg) ; }
      row = (complex *) malloc( sizeof(complex) * nup ) ;
      cf  = (complex *) malloc( sizeof(complex) * n21 ) ;
      cg  = (complex *) malloc( sizeof(complex) * n21 ) ;
      nuptop = nup ;
   }

   /* FFT the pair of rows */

   if( g != NULL )
      for( ii=0 ; ii < n ; ii++ ){ row[ii].r = f[ii] ; row[ii].i = g[ii] ; }
   else
      for( ii=0 ; ii < n ; ii++ ){ row[ii].r = f[ii] ; row[ii].i = 0 ; }

#ifdef ZFILL
   for( ii=n ; ii < nup ; ii++ ){ row[ii].r = row[ii].i = 0.0 ; }
#else
   if( nup > n ){
      sf = 0.5 * (row[0].r + row[n-1].r) ; sg = 0.5 * (row[0].i + row[n-1].i) ;
      for( ii=n ; ii < nup ; ii++ ){ row[ii].r = sf ; row[ii].i = sg ; }
   }
#endif

   csfft_cox( -1 , nup , row ) ;

   /* untangle FFT coefficients from row into cf,cg */

   cf[0].r = 2.0 * row[0].r ; cf[0].i = 0.0 ;  /* twice too big */
   cg[0].r = 2.0 * row[0].i ; cg[0].i = 0.0 ;
   for( ii=1 ; ii < nby2 ; ii++ ){
      cf[ii].r =  row[ii].r + row[nup-ii].r ;
      cf[ii].i =  row[ii].i - row[nup-ii].i ;
      cg[ii].r =  row[ii].i + row[nup-ii].i ;
      cg[ii].i = -row[ii].r + row[nup-ii].r ;
   }
   cf[nby2].r = 2.0 * row[nby2].r ; cf[nby2].i = 0.0 ;
   cg[nby2].r = 2.0 * row[nby2].i ; cg[nby2].i = 0.0 ;

   /* phase shift both rows (cf,cg) */

   dk = (2.0*PI) / nup ;
   sf = -af * dk ; sg = -ag * dk ;

#ifdef RECUR
   csf = CEXPIT(sf) ; csg = CEXPIT(sg) ;
   fac.r = gac.r = 1.0 ;
   fac.i = gac.i = 0.0 ;
#endif

   for( ii=1 ; ii <= nby2 ; ii++ ){
#ifdef RECUR
      fac = CMULT( csf , fac ) ; cf[ii] = CMULT( fac , cf[ii] ) ;
      gac = CMULT( csg , gac ) ; cg[ii] = CMULT( gac , cg[ii] ) ;
#else
      fac = CEXPIT(ii*sf) ; cf[ii] = CMULT( fac , cf[ii] ) ;
      gac = CEXPIT(ii*sg) ; cg[ii] = CMULT( gac , cg[ii] ) ;
#endif
   }
   cf[nby2].i = 0.0 ; cg[nby2].i = 0.0 ;

   /* re-tangle the coefficients from 2 rows */

   row[0].r = cf[0].r ; row[0].i = cg[0].r ;
   for( ii=1 ; ii < nby2 ; ii++ ){
      row[ii].r     =  cf[ii].r - cg[ii].i ;
      row[ii].i     =  cf[ii].i + cg[ii].r ;
      row[nup-ii].r =  cf[ii].r + cg[ii].i ;
      row[nup-ii].i = -cf[ii].i + cg[ii].r ;
   }
   row[nby2].r = cf[nby2].r ;
   row[nby2].i = cg[nby2].r ;

   /* inverse FFT and store back in output arrays */

   csfft_cox( 1 , nup , row ) ;

   sf = 0.5 / nup ;              /* 0.5 to allow for twice too big above */

   if( g != NULL )
      for( ii=0; ii < n; ii++ ){ f[ii] = sf*row[ii].r; g[ii] = sf*row[ii].i; }
   else
      for( ii=0; ii < n; ii++ ){ f[ii] = sf*row[ii].r; }

   EXRETURN ;
}

/*--------------------------------------------------------------------------
  Some stuff needed for polynomial interpolation
----------------------------------------------------------------------------*/

static int    nlcbuf = 0 ;     /* workspace */
static float * lcbuf = NULL ;

   /* f[i], but inside the legal range */

#ifdef ZFILL
#  define FINS(i) ( ((i)<0 || (i)>=n) ? 0.0 : f[(i)] )
#else
#  define FINS(i) ( ((i)<0) ? f[0] : ((i)>=n) ? f[n-1] : f[(i)] )
#endif

#define SEPARATE_FINS

/*===========================================================================*/

/*---------------------------------------------------------------------------*/
/* Shift 1 row with weighted sinc interpolation. [Aug 2019] */
/*---------------------------------------------------------------------------*/

#undef  PIF
#define PIF 3.1415927f /* PI in float */

/* sinc function = sin(PI*x)/(PI*x) [N.B.: x will always be >= 0] */

#undef  sinc
#define sinc(x) ( ((x)>0.01f) ? sinf(PIF*(x))/(PIF*(x))     \
                              : 1.0f - 1.6449341f*(x)*(x) )

/* M3(x) = minimum sidelobe 3 term window
         = declines from 1 to 0 as x goes from 0 to 1 */

#undef  M3
#define M3(x) (0.4243801f+0.4973406f*cosf(PIF*(x))+0.0782793f*cosf(PIF*(x)*2.0f))

/* interpolating function = weighted sinc, for x in [0..5] */

#undef  wwsinc5
#define wwsinc5(x) ( (x <= 5.0f) ? (sinc(x)*M3(0.19999f*(x))) : 0.0f )

void wsinc5_shift( int n , float af , float *f )
{
   int   ii , ia , ix ;
   float aa , xx ;
   float wt_m4, wt_m3, wt_m2, wt_m1 , wt_00 , wt_p1, wt_p2, wt_p3, wt_p4, wt_p5 ;
   int ibot,itop ;

ENTRY("wsinc5_shift") ;

   if( fabsf(af) < 0.0001f ) EXRETURN ; /* little or nothing to do */

   af = -af ; ia = (int)af ; if( af < 0 ) ia-- ;
   if( ia <= -n || ia >= n ){
     for( ii=0 ; ii < n ; ii++ ) f[ii] = 0.0 ;
     EXRETURN ;
   }

   aa = af - ia ;                       /* between 0 and 1 */
   xx = aa+4.0f ; wt_m4 = wwsinc5(xx) ; /* arg to wwsinc5 is always positive */
   xx = aa+3.0f ; wt_m3 = wwsinc5(xx) ; /* and is distance from data point  */
   xx = aa+2.0f ; wt_m2 = wwsinc5(xx) ; /* to 'aa'; e.g., for m3 = -3,     */
   xx = aa+1.0f ; wt_m1 = wwsinc5(xx) ; /* distance is aa+3               */
   xx = aa      ; wt_00 = wwsinc5(xx) ;
   xx = 1.0f-aa ; wt_p1 = wwsinc5(xx) ;
   xx = 2.0f-aa ; wt_p2 = wwsinc5(xx) ;
   xx = 3.0f-aa ; wt_p3 = wwsinc5(xx) ;
   xx = 4.0f-aa ; wt_p4 = wwsinc5(xx) ;
   xx = 5.0f-aa ; wt_p5 = wwsinc5(xx) ;

   if( n > nlcbuf ){
      if( lcbuf != NULL ) free(lcbuf) ;
      lcbuf  = (float *) malloc( sizeof(float) * n ) ;
      nlcbuf = n ;
   }

   ibot = 5-ia ;   if( ibot < 0   ) ibot = 0 ;
   itop = n-6-ia ; if( itop > n-1 ) itop = n-1 ;

   for( ii=ibot ; ii <= itop ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m4 * f[ix-4] + wt_m3 * f[ix-3]
                 + wt_m2 * f[ix-2] + wt_m1 * f[ix-1] + wt_00 * f[ix]
                 + wt_p1 * f[ix+1] + wt_p2 * f[ix+2] + wt_p3 * f[ix+3]
                 + wt_p4 * f[ix+4] + wt_p5 * f[ix+5] ;
   }

   if( ibot > n ) ibot = n ;
   for( ii=0 ; ii < ibot ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m4 * FINS(ix-4) + wt_m3 * FINS(ix-3)
                 + wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3)
                 + wt_p4 * FINS(ix+4) + wt_p5 * FINS(ix+5) ;
   }

   if( itop < 0 ) itop = -1 ; /* 15 Mar 2001 */
   for( ii=itop+1 ; ii < n ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m4 * FINS(ix-4) + wt_m3 * FINS(ix-3)
                 + wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3)
                 + wt_p4 * FINS(ix+4) + wt_p5 * FINS(ix+5) ;
   }

   memcpy( f , lcbuf , sizeof(float)*n ) ;
   EXRETURN ;
}

void wsinc5_shift2( int n, int nup, float af, float * f, float ag, float * g )
{
                   wsinc5_shift( n , af , f ) ;
   if( g != NULL ) wsinc5_shift( n , ag , g ) ;
   return ;
}

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

#undef  wwsinc9
#define wwsinc9(x) ( (x <= 9.0f) ? (sinc(x)*M3(0.11111f*(x))) : 0.0f )

void wsinc9_shift( int n , float af , float *f )
{
   int   ii , ia , ix ;
   float aa , xx ;
   float wt_m4, wt_m3, wt_m2, wt_m1 , wt_00 , wt_p1, wt_p2, wt_p3, wt_p4, wt_p5 ;
   float wt_m5, wt_m6, wt_m7, wt_m8 , wt_p6, wt_p7, wt_p8, wt_p9 ;
   int ibot,itop ;

ENTRY("wsinc9_shift") ;

   if( fabsf(af) < 0.0001f ) EXRETURN ; /* little or nothing to do */

   af = -af ; ia = (int)af ; if( af < 0 ) ia-- ;
   if( ia <= -n || ia >= n ){
     for( ii=0 ; ii < n ; ii++ ) f[ii] = 0.0 ;
     EXRETURN ;
   }

   aa = af - ia ;                       /* between 0 and 1 */
   xx = aa+8.0f ; wt_m8 = wwsinc9(xx) ;
   xx = aa+7.0f ; wt_m7 = wwsinc9(xx) ;
   xx = aa+6.0f ; wt_m6 = wwsinc9(xx) ;
   xx = aa+5.0f ; wt_m5 = wwsinc9(xx) ;
   xx = aa+4.0f ; wt_m4 = wwsinc9(xx) ;
   xx = aa+3.0f ; wt_m3 = wwsinc9(xx) ;
   xx = aa+2.0f ; wt_m2 = wwsinc9(xx) ;
   xx = aa+1.0f ; wt_m1 = wwsinc9(xx) ;
   xx = aa      ; wt_00 = wwsinc9(xx) ;
   xx = 1.0f-aa ; wt_p1 = wwsinc9(xx) ;
   xx = 2.0f-aa ; wt_p2 = wwsinc9(xx) ;
   xx = 3.0f-aa ; wt_p3 = wwsinc9(xx) ;
   xx = 4.0f-aa ; wt_p4 = wwsinc9(xx) ;
   xx = 5.0f-aa ; wt_p5 = wwsinc9(xx) ;
   xx = 6.0f-aa ; wt_p6 = wwsinc9(xx) ;
   xx = 7.0f-aa ; wt_p7 = wwsinc9(xx) ;
   xx = 8.0f-aa ; wt_p8 = wwsinc9(xx) ;
   xx = 9.0f-aa ; wt_p9 = wwsinc9(xx) ;

   if( n > nlcbuf ){
      if( lcbuf != NULL ) free(lcbuf) ;
      lcbuf  = (float *) malloc( sizeof(float) * n ) ;
      nlcbuf = n ;
   }

   ibot = 9-ia ;    if( ibot < 0   ) ibot = 0 ;
   itop = n-10-ia ; if( itop > n-1 ) itop = n-1 ;

   for( ii=ibot ; ii <= itop ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m8 * f[ix-8] + wt_m7 * f[ix-7]
                 + wt_m6 * f[ix-6] + wt_m5 * f[ix-5]
                 + wt_m4 * f[ix-4] + wt_m3 * f[ix-3]
                 + wt_m2 * f[ix-2] + wt_m1 * f[ix-1] + wt_00 * f[ix]
                 + wt_p1 * f[ix+1] + wt_p2 * f[ix+2] + wt_p3 * f[ix+3]
                 + wt_p4 * f[ix+4] + wt_p5 * f[ix+5]
                 + wt_p6 * f[ix+6] + wt_p7 * f[ix+7] + wt_p8 * f[ix+8]
                 + wt_p9 * f[ix+9] ;
   }

   if( ibot > n ) ibot = n ;
   for( ii=0 ; ii < ibot ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m8 * FINS(ix-8) + wt_m7 * FINS(ix-7)
                 + wt_m6 * FINS(ix-6) + wt_m5 * FINS(ix-5)
                 + wt_m4 * FINS(ix-4) + wt_m3 * FINS(ix-3)
                 + wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3)
                 + wt_p4 * FINS(ix+4) + wt_p5 * FINS(ix+5)
                 + wt_p6 * FINS(ix+6) + wt_p7 * FINS(ix+7) + wt_p8 * FINS(ix+8)
                 + wt_p9 * FINS(ix+9) ;
   }

   if( itop < 0 ) itop = -1 ; /* 15 Mar 2001 */
   for( ii=itop+1 ; ii < n ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m8 * FINS(ix-8) + wt_m7 * FINS(ix-7)
                 + wt_m6 * FINS(ix-6) + wt_m5 * FINS(ix-5)
                 + wt_m4 * FINS(ix-4) + wt_m3 * FINS(ix-3)
                 + wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3)
                 + wt_p4 * FINS(ix+4) + wt_p5 * FINS(ix+5)
                 + wt_p6 * FINS(ix+6) + wt_p7 * FINS(ix+7) + wt_p8 * FINS(ix+8)
                 + wt_p9 * FINS(ix+9) ;
   }

   memcpy( f , lcbuf , sizeof(float)*n ) ;
   EXRETURN ;
}

void wsinc9_shift2( int n, int nup, float af, float * f, float ag, float * g )
{
                   wsinc9_shift( n , af , f ) ;
   if( g != NULL ) wsinc9_shift( n , ag , g ) ;
   return ;
}

/*---------------------------------------------------------------------------
  Shift 1 row with with heptic Lagrange polynomial interpolation [Nov 1998].
  Note that heptic interpolation is about the same as Hamming-weighted
  3-sidelobe sinc interpolation .
-----------------------------------------------------------------------------*/

   /* seventh order polynomials */

#define S_M3(x) (x*(x*x-1.0)*(x*x-4.0)*(x-3.0)*(4.0-x)*0.0001984126984)
#define S_M2(x) (x*(x*x-1.0)*(x-2.0)*(x*x-9.0)*(x-4.0)*0.001388888889)
#define S_M1(x) (x*(x-1.0)*(x*x-4.0)*(x*x-9.0)*(4.0-x)*0.004166666667)
#define S_00(x) ((x*x-1.0)*(x*x-4.0)*(x*x-9.0)*(x-4.0)*0.006944444444)
#define S_P1(x) (x*(x+1.0)*(x*x-4.0)*(x*x-9.0)*(4.0-x)*0.006944444444)
#define S_P2(x) (x*(x*x-1.0)*(x+2.0)*(x*x-9.0)*(x-4.0)*0.004166666667)
#define S_P3(x) (x*(x*x-1.0)*(x*x-4.0)*(x+3.0)*(4.0-x)*0.001388888889)
#define S_P4(x) (x*(x*x-1.0)*(x*x-4.0)*(x*x-9.0)*0.0001984126984)

void hept_shift( int n , float af , float * f )
{
   int   ii , ia , ix ;
   float  wt_m1,wt_00,wt_p1,wt_p2 , aa , wt_m2,wt_p3,wt_m3,wt_p4;
#ifdef SEPARATE_FINS
   int ibot,itop ;
#endif

ENTRY("hept_shift") ;

   af = -af ; ia = (int) af ; if( af < 0 ) ia-- ;  /* ia = floor */

   /* 15 Mar 2001: if shift is too large, return all zeros */

   if( ia <= -n || ia >= n ){
      for( ii=0 ; ii < n ; ii++ ) f[ii] = 0.0 ;
      EXRETURN ;
   }

   aa = af - ia ;
   wt_m1 = S_M1(aa) ; wt_00 = S_00(aa) ;
   wt_p1 = S_P1(aa) ; wt_p2 = S_P2(aa) ;
   wt_m2 = S_M2(aa) ; wt_p3 = S_P3(aa) ;
   wt_m3 = S_M3(aa) ; wt_p4 = S_P4(aa) ;

   if( n > nlcbuf ){
      if( lcbuf != NULL ) free(lcbuf) ;
      lcbuf  = (float *) malloc( sizeof(float) * n ) ;
      nlcbuf = n ;
   }

#ifdef SEPARATE_FINS
   ibot = 3-ia ;   if( ibot < 0   ) ibot = 0 ;
   itop = n-5-ia ; if( itop > n-1 ) itop = n-1 ;

   for( ii=ibot ; ii <= itop ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m2 * f[ix-2] + wt_m1 * f[ix-1] + wt_00 * f[ix]
                 + wt_p1 * f[ix+1] + wt_p2 * f[ix+2] + wt_p3 * f[ix+3]
                 + wt_m3 * f[ix-3] + wt_p4 * f[ix+4] ;
   }

   if( ibot > n ) ibot = n ; /* 15 Mar 2001 */
   for( ii=0 ; ii < ibot ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3)
                 + wt_m3 * FINS(ix-3) + wt_p4 * FINS(ix+4) ;
   }

   if( itop < 0 ) itop = -1 ; /* 15 Mar 2001 */
   for( ii=itop+1 ; ii < n ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3)
                 + wt_m3 * FINS(ix-3) + wt_p4 * FINS(ix+4) ;
   }
#else /* not SEPARATE_FINS */
   for( ii=0 ; ii < n ; ii++ ){
      ix = ii + ia ;
      if( ix > 1 && ix < n-3 )
         lcbuf[ii] =  wt_m2 * f[ix-2] + wt_m1 * f[ix-1] + wt_00 * f[ix]
                    + wt_p1 * f[ix+1] + wt_p2 * f[ix+2] + wt_p3 * f[ix+3]
                    + wt_m3 * f[ix-3] + wt_p4 * f[ix+4] ;
      else
         lcbuf[ii] =  wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                    + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3)
                    + wt_m3 * FINS(ix-3) + wt_p4 * FINS(ix+4) ;
   }
#endif /* SEPARATE_FINS */

   memcpy( f , lcbuf , sizeof(float)*n ) ;
   EXRETURN ;
}

void hept_shift2( int n, int nup, float af, float * f, float ag, float * g )
{
                   hept_shift( n , af , f ) ;
   if( g != NULL ) hept_shift( n , ag , g ) ;
   return ;
}

/*---------------------------------------------------------------------------
  Shift 1 row with with quintic Lagrange polynomial interpolation [Nov 1998].
  Note that quintic interpolation is about the same as Hamming-weighted
  2-sidelobe sinc interpolation .
-----------------------------------------------------------------------------*/

   /* quintic interpolation polynomials (Lagrange) */

#define Q_M2(x)  (x*(x*x-1.0)*(2.0-x)*(x-3.0)*0.008333333)
#define Q_M1(x)  (x*(x*x-4.0)*(x-1.0)*(x-3.0)*0.041666667)
#define Q_00(x)  ((x*x-4.0)*(x*x-1.0)*(3.0-x)*0.083333333)
#define Q_P1(x)  (x*(x*x-4.0)*(x+1.0)*(x-3.0)*0.083333333)
#define Q_P2(x)  (x*(x*x-1.0)*(x+2.0)*(3.0-x)*0.041666667)
#define Q_P3(x)  (x*(x*x-1.0)*(x*x-4.0)*0.008333333)

void quint_shift( int n , float af , float * f )
{
   int   ii , ia , ix ;
   float  wt_m1 , wt_00 , wt_p1 , wt_p2 , aa , wt_m2 , wt_p3 ;
#ifdef SEPARATE_FINS
   int ibot,itop ;
#endif

ENTRY("quint_shift") ;

   af = -af ; ia = (int) af ; if( af < 0 ) ia-- ;  /* ia = floor */

   /* 15 Mar 2001: if shift is too large, return all zeros */

   if( ia <= -n || ia >= n ){
      for( ii=0 ; ii < n ; ii++ ) f[ii] = 0.0 ;
      EXRETURN ;
   }

   aa = af - ia ;
   wt_m1 = Q_M1(aa) ; wt_00 = Q_00(aa) ;
   wt_p1 = Q_P1(aa) ; wt_p2 = Q_P2(aa) ;
   wt_m2 = Q_M2(aa) ; wt_p3 = Q_P3(aa) ;

   if( n > nlcbuf ){
      if( lcbuf != NULL ) free(lcbuf) ;
      lcbuf  = (float *) malloc( sizeof(float) * n ) ;
      nlcbuf = n ;
   }

#ifdef SEPARATE_FINS
   ibot = 2-ia ;   if( ibot < 0   ) ibot = 0 ;
   itop = n-4-ia ; if( itop > n-1 ) itop = n-1 ;

   for( ii=ibot ; ii <= itop ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m2 * f[ix-2] + wt_m1 * f[ix-1] + wt_00 * f[ix]
                 + wt_p1 * f[ix+1] + wt_p2 * f[ix+2] + wt_p3 * f[ix+3] ;
   }

   if( ibot > n ) ibot = n ; /* 15 Mar 2001 */
   for( ii=0 ; ii < ibot ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3) ;
   }

   if( itop < 0 ) itop = -1 ; /* 15 Mar 2001 */
   for( ii=itop+1 ; ii < n ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3) ;
   }
#else /* not SEPARATE_FINS */
   for( ii=0 ; ii < n ; ii++ ){
      ix = ii + ia ;
      if( ix > 1 && ix < n-3 )
         lcbuf[ii] =  wt_m2 * f[ix-2] + wt_m1 * f[ix-1] + wt_00 * f[ix]
                    + wt_p1 * f[ix+1] + wt_p2 * f[ix+2] + wt_p3 * f[ix+3] ;
      else
         lcbuf[ii] =  wt_m2 * FINS(ix-2) + wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                    + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) + wt_p3 * FINS(ix+3) ;
   }
#endif /* SEPARATE_FINS */

   memcpy( f , lcbuf , sizeof(float)*n ) ;
   EXRETURN ;
}

void quint_shift2( int n, int nup, float af, float * f, float ag, float * g )
{
                   quint_shift( n , af , f ) ;
   if( g != NULL ) quint_shift( n , ag , g ) ;
   return ;
}

/*---------------------------------------------------------------------------
  Shift 1 row with with cubic interpolation
-----------------------------------------------------------------------------*/

   /* cubic interpolation polynomials */

#define P_M1(x)  ((x)*(1.0-(x))*((x)-2.0)*0.1666667)
#define P_00(x)  (((x)+1.0)*((x)-1.0)*((x)-2.0)*0.5)
#define P_P1(x)  ((x)*((x)+1.0)*(2.0-(x))*0.5)
#define P_P2(x)  ((x)*((x)+1.0)*((x)-1.0)*0.1666667)

void cub_shift( int n , float af , float * f )
{
   int   ii , ia , ix ;
   float  wt_m1 , wt_00 , wt_p1 , wt_p2 , aa ;
#ifdef SEPARATE_FINS
   int ibot,itop ;
#endif

ENTRY("cub_shift") ;

   af = -af ; ia = (int) af ; if( af < 0 ) ia-- ;  /* ia = floor */

   /* 15 Mar 2001: if shift is too large, return all zeros */

   if( ia <= -n || ia >= n ){
      for( ii=0 ; ii < n ; ii++ ) f[ii] = 0.0 ;
      EXRETURN ;
   }

   aa = af - ia ;
   wt_m1 = P_M1(aa) ; wt_00 = P_00(aa) ;
   wt_p1 = P_P1(aa) ; wt_p2 = P_P2(aa) ;

   if( n > nlcbuf ){
      if( lcbuf != NULL ) free(lcbuf) ;
      lcbuf  = (float *) malloc( sizeof(float) * n ) ;
      nlcbuf = n ;
   }

#ifdef SEPARATE_FINS
   ibot = 1-ia ;   if( ibot < 0   ) ibot = 0 ;
   itop = n-3-ia ; if( itop > n-1 ) itop = n-1 ;

   for( ii=ibot ; ii <= itop ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m1 * f[ix-1] + wt_00 * f[ix]
                 + wt_p1 * f[ix+1] + wt_p2 * f[ix+2] ;
   }

   if( ibot > n ) ibot = n ; /* 15 Mar 2001 */
   for( ii=0 ; ii < ibot ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) ;
   }

   if( itop < 0 ) itop = -1 ; /* 15 Mar 2001 */
   for( ii=itop+1 ; ii < n ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                 + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) ;
   }
#else /* not SEPARATE_FINS */
   for( ii=0 ; ii < n ; ii++ ){
      ix = ii + ia ;
      if( ix > 0 && ix < n-2 )
         lcbuf[ii] =  wt_m1 * f[ix-1] + wt_00 * f[ix]
                    + wt_p1 * f[ix+1] + wt_p2 * f[ix+2] ;
      else
         lcbuf[ii] =  wt_m1 * FINS(ix-1) + wt_00 * FINS(ix)
                    + wt_p1 * FINS(ix+1) + wt_p2 * FINS(ix+2) ;
   }
#endif /* SEPARATE_FINS */

   memcpy( f , lcbuf , sizeof(float)*n ) ;
   EXRETURN ;
}

void cub_shift2( int n, int nup, float af, float * f, float ag, float * g )
{
                   cub_shift( n , af , f ) ;
   if( g != NULL ) cub_shift( n , ag , g ) ;
   return ;
}

/*---------------------------------------------------------------------------
   Shift one row with linear interpolation
-----------------------------------------------------------------------------*/

void lin_shift( int n , float af , float * f )
{
   int   ii , ia , ix ;
   float  wt_00 , wt_p1 , aa ;
#ifdef SEPARATE_FINS
   int ibot,itop ;
#endif

ENTRY("lin_shift") ;

   af = -af ; ia = (int) af ; if( af < 0 ) ia-- ;  /* ia = floor */
   aa = af - ia ;
   wt_00 = 1.0 - aa ; wt_p1 = aa ;  /* linear interpolation weights */

   /* 15 Mar 2001: if shift is too large, return all zeros */

   if( ia <= -n || ia >= n ){
      for( ii=0 ; ii < n ; ii++ ) f[ii] = 0.0 ;
      EXRETURN ;
   }

   if( n > nlcbuf ){
      if( lcbuf != NULL ) free(lcbuf) ;
      lcbuf  = (float *) malloc( sizeof(float) * n ) ;
      nlcbuf = n ;
   }

#ifdef SEPARATE_FINS
   ibot = -ia  ;   if( ibot < 0   ) ibot = 0 ;
   itop = n-2-ia ; if( itop > n-1 ) itop = n-1 ;

#if 0
if(PRINT_TRACING){
  char str[256]; sprintf(str,"n=%d ia=%d ibot=%d itop=%d",n,ia,ibot,itop); STATUS(str);
}
#endif

   for( ii=ibot ; ii <= itop ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_00 * f[ix] + wt_p1 * f[ix+1] ;
   }

   if( ibot > n ) ibot = n ; /* 15 Mar 2001 */
   for( ii=0 ; ii < ibot ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_00 * FINS(ix) + wt_p1 * FINS(ix+1) ;
   }

   if( itop < 0 ) itop = -1 ; /* 15 Mar 2001 */
   for( ii=itop+1 ; ii < n ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] =  wt_00 * FINS(ix) + wt_p1 * FINS(ix+1) ;
   }
#else
   for( ii=0 ; ii < n ; ii++ ){
      ix = ii + ia ;
      if( ix >= 0 && ix < n-1 )
         lcbuf[ii] =  wt_00 * f[ix] + wt_p1 * f[ix+1] ;
      else
         lcbuf[ii] =  wt_00 * FINS(ix) + wt_p1 * FINS(ix+1) ;
   }
#endif /* SEPARATE_FINS */

   memcpy( f , lcbuf , sizeof(float)*n ) ;
   EXRETURN ;
}

void lin_shift2( int n, int nup, float af, float * f, float ag, float * g )
{
                   lin_shift( n , af , f ) ;
   if( g != NULL ) lin_shift( n , ag , g ) ;
   return ;
}

/*--------------------------------------------------------------------------
  This is for experimental purposes only -- DO NOT USE!
----------------------------------------------------------------------------*/

void nn_shift( int n , float af , float * f )
{
   int   ii , ia , ix ;

ENTRY("nn_shift") ;

   af = -af ; ia = (int) af ; if( af < 0 ) ia-- ;  /* ia = floor */

   /* 15 Mar 2001: if shift is too large, return all zeros */

   if( ia <= -n || ia >= n ){
      for( ii=0 ; ii < n ; ii++ ) f[ii] = 0.0 ;
      EXRETURN ;
   }

   if( n > nlcbuf ){
      if( lcbuf != NULL ) free(lcbuf) ;
      lcbuf  = (float *) malloc( sizeof(float) * n ) ;
      nlcbuf = n ;
   }

   for( ii=0 ; ii < n ; ii++ ){
      ix = ii + ia ;
      lcbuf[ii] = FINS(ix) ;
   }

   memcpy( f , lcbuf , sizeof(float)*n ) ;
   EXRETURN ;
}

void nn_shift2( int n, int nup, float af, float * f, float ag, float * g )
{
                   nn_shift( n , af , f ) ;
   if( g != NULL ) nn_shift( n , ag , g ) ;
   return ;
}

/*---------------------------------------------------------------------------
   More experiments: two-step interpolation
-----------------------------------------------------------------------------*/

void ts_shift( int n , float af , float * f )
{
   register int ii , ia , ix ;
   float aa ;
   int ibot,itop ;

   af = -af ; ia = (int) af ; if( af < 0 ) ia-- ;  /* ia = floor */

   /* 15 Mar 2001: if shift is too large, return all zeros */

   if( ia <= -n || ia >= n ){
      for( ii=0 ; ii < n ; ii++ ) f[ii] = 0.0 ;
      EXRETURN ;
   }

   aa = af - ia ;

   if( n > nlcbuf ){
      if( lcbuf != NULL ) free(lcbuf) ;
      lcbuf  = (float *) malloc( sizeof(float) * n ) ;
      nlcbuf = n ;
   }

   ibot = -ia  ;   if( ibot < 0   ) ibot = 0 ;
   itop = n-2-ia ; if( itop > n-1 ) itop = n-1 ;

   if( aa < 0.30 ){
      memcpy( lcbuf+ibot, f+(ibot+ia)  , (itop+1-ibot)*sizeof(float) );
      for( ii=0 ; ii < ibot ; ii++ ){
         ix = ii + ia ; lcbuf[ii] = FINS(ix) ;
      }
      for( ii=itop+1 ; ii < n ; ii++ ){
         ix = ii + ia ; lcbuf[ii] = FINS(ix) ;
      }
   }

   else if( aa > 0.70 ){
      memcpy( lcbuf+ibot, f+(ibot+1+ia), (itop+1-ibot)*sizeof(float) );
      for( ii=0 ; ii < ibot ; ii++ ){
         ix = ii + ia ; lcbuf[ii] = FINS(ix+1) ;
      }
      for( ii=itop+1 ; ii < n ; ii++ ){
         ix = ii + ia ; lcbuf[ii] = FINS(ix+1) ;
      }

   } else {
      for( ii=ibot ; ii <= itop ; ii++ ){
         ix = ii + ia ; lcbuf[ii] =  0.5*( f[ix] + f[ix+1] ) ;
      }
      if( ibot > n ) ibot = n ; /* 15 Mar 2001 */
      for( ii=0 ; ii < ibot ; ii++ ){
         ix = ii + ia ; lcbuf[ii] =  0.5*( FINS(ix) + FINS(ix+1) ) ;
      }
      if( itop < 0 ) itop = -1 ; /* 15 Mar 2001 */
      for( ii=itop+1 ; ii < n ; ii++ ){
         ix = ii + ia ; lcbuf[ii] =  0.5*( FINS(ix) + FINS(ix+1) ) ;
      }
   }
   memcpy( f , lcbuf , sizeof(float)*n ) ;
   return ;
}

void ts_shift2( int n, int nup, float af, float * f, float ag, float * g )
{
                   ts_shift( n , af , f ) ;
   if( g != NULL ) ts_shift( n , ag , g ) ;
   return ;
}
