#include "niml_private.h"

/***************************************************************************/
/************************  NIML malloc stuff *******************************/
/***************************************************************************/

/*-------------------------------------------------------------------------*/
/** 25 Mar 2003: allow user to replace malloc, realloc, free functions    **/

static void * (*user_malloc)(size_t)         = NULL ;
static void * (*user_realloc)(void *,size_t) = NULL ;
static void   (*user_free)(void *)           = NULL ;
static int  use_userfunc                     = 0    ;
static int  ni_mall_used                     = 0    ;

/*-------------------------------------------------------------------------*/
/*! Allow user to replace malloc(), realloc(), and free() functions used
    in NI_malloc(), NI_realloc(), and NI_free().
      - um = replacement for malloc()
      - ur = replacement for realloc()
      - uf = replacement for free()
      - all 3 must be non-NULL
      - this function must be called BEFORE any call to NI_malloc() etc.
        takes place, or this function will fail
      - return value is 1 if the replacement is accepted, 0 if not
      - note that NI_malloc() always 0 fills the result, even the one
         returned by um()
      - RWCox - 25 Mar 2003 (VR Day)
---------------------------------------------------------------------------*/

int NI_malloc_replace( void *(*um)(size_t)        ,
                       void *(*ur)(void *,size_t) ,
                       void  (*uf)(void *)         ){

  if( ni_mall_used ||
      use_userfunc ||
      um == NULL   ||
      ur == NULL   ||
      uf == NULL     ) return 0 ;

  user_malloc  = um ;
  user_realloc = ur ;
  user_free    = uf ;
  use_userfunc = 1  ;
  return 1 ;
}

#if defined(NIML_OLD_MALLOC) || !defined(ALLOW_MCW_MALLOC)

/*--------------------------------------------------------------------------*/
/*! Allocate memory (actually uses calloc); calls exit() if it fails.
----------------------------------------------------------------------------*/

void * old_NI_malloc( size_t len )
{
   void *p ;
   if( use_userfunc ){
     p = user_malloc(len) ; if( p != NULL ) memset(p,0,len) ;
   } else {
     p = calloc(1,len) ;
   }
   if( p == NULL ){
     fprintf(stderr,"** ERROR: old_NI_malloc() fails. Aauugghh!\n") ;
     NI_sleep(333); exit(1);
   }
   ni_mall_used = 1 ; return p ;
}

/*--------------------------------------------------------------------------*/
/*! Free memory; NULL pointer is just ignored.
----------------------------------------------------------------------------*/

void NI_free( void *p )
{
   if( p != NULL ){
     if( use_userfunc ) user_free(p) ;
     else               free(p) ;
   }
   ni_mall_used = 1 ;
}

/*--------------------------------------------------------------------------*/
/*! Reallocate memory; calls exit() if it fails.
----------------------------------------------------------------------------*/

void * old_NI_realloc( void *p , size_t len )
{
   void *q ;

   if( use_userfunc ) q = user_realloc( p , len ) ;
   else               q = realloc( p , len ) ;
   if( q == NULL && len > 0 ){
     fprintf(stderr,"** ERROR: old_NI_realloc() fails. Ooooogg!\n");
     NI_sleep(333); exit(1);
   }
   ni_mall_used = 1 ; return q ;
}

/**** Fake routines with no meaning in this NI_malloc version ****/

char * NI_malloc_status          (void){ return "disabled"; }
void   NI_malloc_dump            (void){ return;      }
void   NI_malloc_enable_tracking (void){ return;      }
int    NI_malloc_tracking_enabled(void){ return 0;    }

/*****************************************************************************/
#else  /**  not NIML_OLD_MALLOC and ALLOW_MCW_MALLOC                   *******/
       /**  18 Nov 2002: keep track of mallocs, as in mcw_malloc.[ch]  *******/
/*****************************************************************************/

#define MAGIC  ((char) 0xd7)     /* goes in extra bytes at ends of blocks */
#define NEXTRA (2*sizeof(int))   /* number of extra bytes */

#undef  UINT
#define UINT unsigned int

/*-- struct to hold info about each malloc()-ed block --*/

typedef struct {
  void  *pmt ;   /* pointer to actually malloc-ed block
                    (user ptr is +NEXTRA bytes later)   */
  size_t psz ;   /* size of allocated block */
  char  *pfn ;   /* function name that called */
  int    pln ;   /* function line number */
  UINT   pss ;   /* serial number of this malloc */
} NI_mallitem ;

/** set hash table size (to a prime number, please) **/

#undef  SLOTS
#define SLOTS 1031

/** #define SLOTS 2053  **/
/** #define SLOTS 4099  **/
/** #define SLOTS 8191  **/
/** #define SLOTS 16381 **/
/** #define SLOTS 32003 **/

static NI_mallitem ** htab  = NULL ; /* table of lists */
static int *          nhtab = NULL ; /* size of each list */
static UINT          serial = 0    ; /* serial number of allocation */

#undef INLINE
#ifdef __GNUC__
# define INLINE inline
#else
# define INLINE /*nada*/
#endif

/*** prototypes for internal routines defined below ***/

static NI_mallitem * ptr_tracker( void * ) ;
static NI_mallitem * find_empty_slot( int ) ;
static void add_tracker( void * , size_t , char * , int ) ;
static void * malloc_track( size_t , char * , int ) ;
static void probe_track( NI_mallitem * , char *,int ) ;
static void * realloc_track( NI_mallitem *, size_t , char *, int  ) ;
static void * calloc_track( size_t , size_t , char * , int  ) ;
static void free_track( NI_mallitem * ) ;
static void qsort_intint( int, int *, int * ) ;

#undef malloc
#undef realloc
#undef calloc
#undef free

/*---------------------------------------------------------------*/
/*!  Compute a unique non-negative integer key from an address
-----------------------------------------------------------------*/

static INLINE UINT mallkey( char *fred )
{
   UINT q = (UINT)(intptr_t)fred ;

   q =   ((q & 0xf0f0f0f0) >> 4)   /* swap nibbles */
       | ((q & 0x0f0f0f0f) << 4) ;

   return q ;
}

/*----------------------------------------------------------------*/
/*! Find an address in the hash table;
    returns a pointer to the NI_mallitem that owns it (or NULL)
------------------------------------------------------------------*/

static NI_mallitem * ptr_tracker( void *fred )
{
   int jj,kk ;

   if( fred == NULL ) return NULL ;

   jj = mallkey((char *)fred) % SLOTS ;  /* hash table location */

   if( htab[jj] == NULL ) return NULL ;  /* nothing there */

   for( kk=0 ; kk < nhtab[jj] ; kk++ )   /* scan for match */
     if( htab[jj][kk].pmt == fred ) return (htab[jj]+kk) ;

   return NULL ; /* no match found */
}

/*-----------------------------------------------------------------*/
/*-- this macro finds an address from the user in the hash table --*/

#define shift_tracker(fff)  ptr_tracker( ((char *)(fff)) - NEXTRA )

/*-----------------------------------------------------------------*/
/*! Find an empty entry in the hash table list [jj] and return
   a pointer to it.  Will create the entry, if need be.
-------------------------------------------------------------------*/

static NI_mallitem * find_empty_slot( int jj )
{
   int kk ;

   if( htab[jj] == NULL ){                                    /* must make new list  */
     htab[jj] = (NI_mallitem *) malloc(sizeof(NI_mallitem)) ; /* of length 1 at [jj] */
    nhtab[jj] = 1 ;
     kk       = 0 ;
     htab[jj][0].pmt = NULL ;  /* mark as empty */
   } else {
     for( kk=nhtab[jj]-1 ; kk >= 0 ; kk-- )    /* scan (backwards) for NULL entry */
       if( htab[jj][kk].pmt == NULL ) break ;  /* found it? */

     if( kk < 0 ){                             /* must make list longer */
       kk = nhtab[jj] ; nhtab[jj]++ ;
       htab[jj] = (NI_mallitem *) realloc( htab[jj], sizeof(NI_mallitem)*nhtab[jj] ) ;
       htab[jj][kk].pmt = NULL ;  /* mark as empty */
     }
   }

   return (htab[jj]+kk) ;
}

/*----------------------------------------------------------------*/
/*! Add an entry to the hash table, given the
   address, the user's size, and the filename and line number.
------------------------------------------------------------------*/

static void add_tracker( void *fred , size_t n , char *fn , int ln )
{
   int jj ;
   NI_mallitem *ip ;

   if( fred == NULL ) return ;   /* bad news */

   jj = mallkey((char *)fred) % SLOTS ;  /* which hash list to use */
   ip = find_empty_slot(jj) ;            /* get an empty slot in this list */

   /* now put the data into the hash table */

   ip->pmt = fred ;
   ip->psz = n ;
   ip->pfn = fn ;
   ip->pln = ln ;
   ip->pss = ++serial ;

   return ;
}

/*-----------------------------------------------------------------*/
/*!  The tracking replacement for malloc().
-------------------------------------------------------------------*/

static void * malloc_track( size_t n , char *fn , int ln )
{
   char *fred ;
   size_t nn = n + 2*NEXTRA ;
   int ii ;

   fred = (char *)malloc(nn) ;
   if( fred == NULL ) return NULL ;  /* real bad news */

   /* mark overrun buffers */

   memset( fred           , MAGIC , NEXTRA ) ;
   memset( fred+(n+NEXTRA), MAGIC , NEXTRA ) ;

   ni_mall_used = 1 ;
   add_tracker(fred,n,fn,ln) ;      /* put in hash table */
   return (void *)(fred+NEXTRA) ;
}

/*-----------------------------------------------------------------*/
/*! Check an entry in the hash table for local overrun integrity.
-------------------------------------------------------------------*/

static void probe_track( NI_mallitem *ip , char *fn, int ln )
{
   int ii ;
   size_t n ;
   char *fred ;

   if( ip == NULL ) return ; /* error */
   fred = (char *) ip->pmt ; if( fred == NULL ) return ;
   n = ip->psz ;

   for( ii=0 ; ii < NEXTRA ; ii++ )
     if( fred[ii] != MAGIC ){
       fprintf(stderr,"*** NI_malloc pre-corruption!  "
                      "serial=%u size=%u source=%s line#=%d\n",
                      ip->pss,(unsigned int)ip->psz,ip->pfn,ip->pln ) ;
       if( fn != NULL ) fprintf(stderr,"   Caller=%s line#=%d\n",fn,ln) ;
       break ;
     }

   for( ii=0 ; ii < NEXTRA ; ii++ )
     if( fred[n+NEXTRA+ii] != MAGIC ){
       fprintf(stderr,"*** NI_malloc post-corruption!  "
                      "serial=%u size=%u source=%s line#=%d\n",
                      ip->pss,(unsigned int)ip->psz,ip->pfn,ip->pln ) ;
       if( fn != NULL ) fprintf(stderr,"   Caller=%s line#=%d\n",fn,ln) ;
       break ;
     }

   return ;
}

/*-------------------------------------------------------------------*/
/*! The tracking replacement for realloc().
---------------------------------------------------------------------*/

static void * realloc_track( NI_mallitem *ip, size_t n, char *fn, int ln )
{
   char *nfred , *cfred ;
   size_t nn = n + 2*NEXTRA ;
   int ii , cjj,njj , kk ;

   if( ip == NULL ) return NULL ;  /* should not happen */

   probe_track(ip,fn,ln) ;    /* check for integrity before reallocation */
   cfred = (char *)ip->pmt ;  /* old address */

   ni_mall_used = 1 ;
   nfred = (char *)realloc( (void *)cfred , nn ) ;
   if( nfred == NULL ) return NULL ;  /* this is bad - real bad */

   memset( nfred           , MAGIC , NEXTRA ) ;
   memset( nfred+(n+NEXTRA), MAGIC , NEXTRA ) ;

   cjj = mallkey(cfred) % SLOTS ;  /* hash table list for old */
   njj = mallkey(nfred) % SLOTS ;  /* and for new address */

   if( cjj == njj ){  /* can just update old hashtable entry */

     ip->pmt = nfred ;
     ip->psz = n ;
     ip->pfn = fn ;
     ip->pln = ln ;
     ip->pss = ++serial ;

   } else {           /* must move into a different list */

     add_tracker( nfred , n , fn , ln ) ;

     ip->pmt = NULL ; /* mark old entry as free */
   }

   return (void *)(nfred+NEXTRA) ;
}

/*-----------------------------------------------------------------*/
/*!  Tracking replacement for calloc().
-------------------------------------------------------------------*/

static void * calloc_track( size_t n , size_t m , char *fn , int ln )
{
   void *fred ;
   size_t nn = n*m ;

   fred = malloc_track(nn,fn,ln) ; if( fred == NULL ) return NULL ;
   memset( fred , 0 , nn ) ;
   return fred ;
}

/*-----------------------------------------------------------------*/
/*!  Tracking replacement for free().
-------------------------------------------------------------------*/

static void free_track( NI_mallitem *ip )
{
   char *cfred ;

   if( ip == NULL ) return ;
   cfred = (char *) ip->pmt ;
   if( cfred == NULL ) return ;

   probe_track(ip,NULL,0) ;  /* check for integrity before freeing */

   ni_mall_used = 1 ;
   free(cfred) ; ip->pmt = NULL ; return ;
}

/*-----------------------------------------------------------------*/
/*!  Return a status string about the situation.
     This is stored in a static buffer, so don't free it.
-------------------------------------------------------------------*/

static int use_tracking = 0 ;  /* is the tracking enabled? */

char * NI_malloc_status(void)
{
   static char buf[128] = "\0" ;
   int jj,kk , nptr=0 ; size_t nbyt=0 ;

   if( ! use_tracking ) return "not enabled" ;

   for( jj=0 ; jj < SLOTS ; jj++ ){
     for( kk=0 ; kk < nhtab[jj] ; kk++ ){
       if( htab[jj][kk].pmt != NULL ){
         probe_track( htab[jj]+kk , NULL,0 ) ; /* check for integrity */
         nptr++ ; nbyt += htab[jj][kk].psz ;
       }
     }
   }

   sprintf(buf,"chunks=%d bytes=%u",nptr,(UINT)nbyt) ;
   return buf ;
}

/*-----------------------------------------------------------------*/
/*!  Write a file with lots of info about the current status.
-------------------------------------------------------------------*/

void NI_malloc_dump(void)
{
   int ii,jj,kk ;
   char fname[32] , *str ;
   FILE *fp = NULL ;
   int nptr=0 ;
   int *ss , *jk ;

   if( ! use_tracking ) return ;

   /* find and open an output file */

   for( ii=1 ; ii < 1000 ; ii++ ){
     sprintf(fname,"NI_malldump.%03d",ii) ;
     if( NI_is_file(fname) ) continue ;
     fp = fopen( fname , "w" ) ;
     if( fp == NULL ){
       fprintf(stderr,"** Unable to open file %s for malloc table dump!\n",
               fname ) ;
       return ;
     }
     break ;
   }

   if( fp == NULL ){
     fprintf(stderr,"** Attempt to exceed 999 malloc table dump files!\n") ;
     return ;
   }

   /* count number of entries in the hash table */

   for( jj=0 ; jj < SLOTS ; jj++ ){
     for( kk=0 ; kk < nhtab[jj] ; kk++ ){
       if( htab[jj][kk].pmt != NULL ) nptr++ ;
     }
   }

   if( nptr < 1 ){
     fprintf(fp    ,"--- Nothing is malloc()-ed !? ---\n") ;
     fprintf(stderr,"--- Nothing is malloc()-ed !? ---\n") ;
     fclose(fp) ;
   }

   /* setup to sort by serial number */

   ss = (int *) malloc(sizeof(int)*nptr) ;  /* serial number */
   jk = (int *) malloc(sizeof(int)*nptr) ;  /* holds combination of jj and kk */

#define JBASE 32768  /* JBASE * SLOTS must be less than max int */

   /* scan table for non-NULL entries */

   for( ii=jj=0 ; jj < SLOTS ; jj++ ){
     for( kk=0 ; kk < nhtab[jj] ; kk++ ){
       if( htab[jj][kk].pmt != NULL ){
         ss[ii] = htab[jj][kk].pss ;   /* save serial number */
         jk[ii] = JBASE*jj + kk ;      /* save jj and kk */
         ii++ ;
       }
     }
   }

   qsort_intint( nptr , ss , jk ) ;  /* sort by ss, carrying jk along */

   /* now print table in serial number order */

   fprintf(fp, "MCW Malloc Table Dump:\n"
               "serial# size       source file          line# address    hash(j,k)\n"
               "------- ---------- -------------------- ----- ---------- ---------\n") ;

   for( ii=0 ; ii < nptr ; ii++ ){
     jj = jk[ii] / JBASE ;           /* retrieve jj and kk */
     kk = jk[ii] % JBASE ;
     if( htab[jj][kk].pmt != NULL ){
       fprintf(fp,"%7u %10u %-20.30s %5d %10p %5d %3d",
               htab[jj][kk].pss , (unsigned int)htab[jj][kk].psz ,
               htab[jj][kk].pfn , htab[jj][kk].pln , htab[jj][kk].pmt ,
               jj,kk ) ;
       fprintf(fp,"\n") ;
     }
     else
       fprintf(fp,"*** Error at ii=%d jj=%d kk=%d\n",ii,jj,kk) ;
   }

   free(ss) ; free(jk) ;

   /* and print out the summary line (to the file and screen) */

   str = NI_malloc_status() ;
   fprintf(fp,"----- Summary: %s\n",str) ;
   fclose(fp) ;

   fprintf(stderr,"** Malloc table dumped to file %s\n",fname) ;
   fprintf(stderr,"** Summary: %s\n",str) ;

   return ;
}

/*----------------------------------------------------------------*/
/*!  Turn on use of the tracking routines.
------------------------------------------------------------------*/

void NI_malloc_enable_tracking(void)       /* cannot be disabled */
{
   char *str ;

   if( use_userfunc ) return ;   /* 25 Mar 2003 */
   ni_mall_used = 1 ;

   if( use_tracking ) return ;   /* 05 Nov 2001 */

   str = getenv("AFNI_NO_MCW_MALLOC") ;
   if( str == NULL )
     str = getenv("NIML_MALLOC_DISABLE") ;

   use_tracking = 1 ;
   if( str!=NULL && ( *str=='y' || *str=='Y') ) use_tracking = 0 ;

   if( use_tracking && htab == NULL ){  /* initialize hash table */
     int jj ;
     htab  = (NI_mallitem **) malloc( SLOTS * sizeof(NI_mallitem *) ) ;
     nhtab = (int *)          malloc( SLOTS * sizeof(int) ) ;
     for( jj=0 ; jj < SLOTS ; jj++ ){
       htab[jj] = NULL ; nhtab[jj] = 0 ;
     }
   }

   return ;
}

/*---------------------------------------------------------------*/
/*! Lets the user check if the tracking routines are in use.
-----------------------------------------------------------------*/

int NI_malloc_tracking_enabled(void)
{
  return (use_tracking != 0) ;
}

/*--------------------------------------------------------------------------*/
/*! Allocate memory (actually uses calloc); calls exit() if it fails.
----------------------------------------------------------------------------*/

void * hidden_NI_malloc( size_t n , char *fnam , int lnum )
{
   void *p ;

        if( use_userfunc ){ p = user_malloc(n); if(p)memset(p,0,n); }
   else if( use_tracking )  p = calloc_track(1,n,fnam,lnum) ;
   else                     p = calloc(1,n) ;

   if( p == NULL ){
     fprintf(stderr,"** ERROR: NI_malloc() fails. Aauugghh!\n") ;
     NI_sleep(333); exit(1);
   }

#ifdef NIML_DEBUG
NI_dpr("hidden_NI_malloc: called from %s#%d\n",fnam,lnum) ;
#endif

   return p ;
}

/*--------------------------------------------------------------------------*/
/*! Reallocate memory; calls exit() if it fails.
----------------------------------------------------------------------------*/

void * hidden_NI_realloc( void *fred , size_t n , char *fnam , int lnum )
{
   NI_mallitem *ip ;
   void *q ;

   if( fred == NULL )
      return hidden_NI_malloc( n , fnam , lnum ) ;

   if( use_userfunc )
     q = user_realloc( fred , n ) ;
   else if( use_tracking && (ip=shift_tracker(fred)) != NULL )
     q = realloc_track( ip , n , fnam,lnum ) ;
   else
     q = realloc( fred , n ) ;

   if( q == NULL && n > 0 ){
      fprintf(stderr,"** ERROR: NI_realloc() fails. Ooooogg!\n");
      NI_sleep(333); exit(1);
   }

#ifdef NIML_DEBUG
NI_dpr("hidden_NI_realloc: called from %s#%d\n",fnam,lnum) ;
#endif

   return q ;
}

/*-----------------------------------------------------------------
    The actual replacement for free()
-------------------------------------------------------------------*/

void hidden_NI_free( void *fred , char *fnam , int lnum )
{
   NI_mallitem *ip ;

   if( fred == NULL ) return ;

   if( use_userfunc )                                          user_free(fred) ;
   else if( use_tracking && (ip=shift_tracker(fred)) != NULL ) free_track( ip ) ;
   else                                                        free( fred ) ;

#ifdef NIML_DEBUG
NI_dpr("hidden_NI_free: called from %s#%d\n",fnam,lnum) ;
#endif

}

/*------------------------------------------------*/
/*! Insertion_sort : sort an array of int + int.  */

static void isort_intint( int n , int * ar , int * iar )
{
   register int  j , p ;  /* array indices */
   register int   temp ;  /* a[j] holding place */
   register int  itemp ;
   register int * a = ar ;
   register int  * ia = iar ;

   if( n < 2 ) return ;

   for( j=1 ; j < n ; j++ ){

     if( a[j] < a[j-1] ){   /* out of order */
        p    = j ;
        temp = a[j] ; itemp = ia[j] ;

       do{
           a[p] =  a[p-1] ; /* at this point, a[p-1] > temp, so move it up */
          ia[p] = ia[p-1] ;
          p-- ;
        } while( p > 0 && temp < a[p-1] ) ;

        a[p] = temp ;       /* finally, put temp in its place */
       ia[p] = itemp ;
     }
   }
}

/*------------------------------------------------------*/
/*! Recursive part of quicksort (stack implementation). */

#undef  QS_STACK
#undef  QS_SWAPF
#undef  QS_SWAPI
#define QS_STACK  1024  /* stack size */
#define QS_SWAPF(x,y) ( temp=(x),(x)=(y),(y)= temp)
#define QS_SWAPI(i,j) (itemp=(i),(i)=(j),(j)=itemp)

static void qsrec_intint( int n , int * ar , int * iar , int cutoff )
{
   register int i , j ;        /* scanning indices */
   register int temp , pivot ; /* holding places */
   register int  itemp , ipivot ;
   register int * a = ar ;
   register int  * ia = iar ;

   int left , right , mst , stack[QS_STACK] , nnew ;

   /* return if too short (insertion sort will clean up) */

   if( cutoff < 3 ) cutoff = 3 ;
   if( n < cutoff ) return ;

   /* initialize stack to start with whole array */

   stack[0] = 0   ;
   stack[1] = n-1 ;
   mst      = 2   ;

   /* loop while the stack is nonempty */

   while( mst > 0 ){
      right = stack[--mst] ;  /* work on subarray from left -> right */
      left  = stack[--mst] ;

      i = ( left + right ) / 2 ;           /* middle of subarray */

      /* sort the left, middle, and right a[]'s */

      if( a[left] > a[i]     ){ QS_SWAPF(a[left] ,a[i]    ); QS_SWAPI(ia[left] ,ia[i]    ); }
      if( a[left] > a[right] ){ QS_SWAPF(a[left] ,a[right]); QS_SWAPI(ia[left] ,ia[right]); }
      if( a[i] > a[right]    ){ QS_SWAPF(a[right],a[i]    ); QS_SWAPI(ia[right],ia[i]    ); }

      pivot  = a[i] ;                        /* a[i] is the median-of-3 pivot! */
      a[i]   = a[right] ;
      ipivot = ia[i] ;
      ia[i]  = ia[right] ;

      i = left ;                            /* initialize scanning */
      j = right ;

      /*----- partition:  move elements bigger than pivot up and elements
                          smaller than pivot down, scanning in from ends -----*/

      do{
        for( ; a[++i] < pivot ; ) ;  /* scan i up,   until a[i] >= pivot */
        for( ; a[--j] > pivot ; ) ;  /* scan j down, until a[j] <= pivot */

        if( j <= i ) break ;         /* if j meets i, quit */

        QS_SWAPF( a[i] , a[j] ) ; QS_SWAPI( ia[i] , ia[j] ) ;
      } while( 1 ) ;

      /*----- at this point, the array is partitioned -----*/

      a[right]  = a[i] ;           /*restore the pivot*/
      a[i]      = pivot ;
      ia[right] = ia[i] ;
      ia[i]     = ipivot ;

      /*----- push subarrays [left..i-1] and [i+1..right] onto stack, if big -----*/

      nnew = 0 ;
      if( (i-left)  > cutoff ){ stack[mst++] = left ; stack[mst++] = i-1   ; nnew++ ; }
      if( (right-i) > cutoff ){ stack[mst++] = i+1  ; stack[mst++] = right ; nnew++ ; }

      /* if just added two subarrays to stack, make sure shorter one comes first */

      if( nnew == 2 && stack[mst-3] - stack[mst-4] > stack[mst-1] - stack[mst-2] ){
         QS_SWAPI( stack[mst-4] , stack[mst-2] ) ;
         QS_SWAPI( stack[mst-3] , stack[mst-1] ) ;
      }

   }  /* end of while stack is non-empty */

}

/*-----------------------------------------------------------------*/
/*! Sort an array partially recursively, and partially insertion.  */

#ifndef QS_CUTOFF
#define QS_CUTOFF 10
#endif

void qsort_intint( int n , int * a , int * ia )
{
   qsrec_intint( n , a , ia , QS_CUTOFF ) ;
   isort_intint( n , a , ia ) ;
   return ;
}

/*-----------------------------------------------------------------*/
/*! 17 Dec 2003: In case a true NI_free() call gets thru somehow.  */
/*-----------------------------------------------------------------*/

#ifdef NI_free
#undef NI_free
#endif
void NI_free( void *p )
{
  hidden_NI_free( p , (char *)"Nada" , 0 ) ;
}

#endif  /* NIML_OLD_MALLOC and ALLOW_MCW_MALLOC */
