/*  File: msort.c
 *  Author: Jean Thierry-Mieg (mieg@ncbi.nlm.nih.gov)
 * Description:
 *      This file implements the classic sort/merge algorithm.
 *      
 *      This file is adapted from the GNU msort 
 *      written by Mike Haertel, September 1988.
 *      I got from him the idea of copying only
 *      (n - n2) bytes back in b, and added the
 *      already sorted case short cut.
 *      The rest of the code is standard.
 *	
 * Exported functions:
 *       mSort () with the same prototype as unix qsort()
 * HISTORY:
 * Last edited:
 * Created: Aug 6 2002 (mieg)
 *-------------------------------------------------------------------
 */

/* $Id: msort.c,v 1.3 2006/03/13 04:47:33 kent Exp $ */

#include "common.h"

static void mDoSortInt (int *b, int n , void *pf, 
			int (*cmp_r)(void *pf, const void *va, const void *vb)
			, int *buf)
/* Special case of sort where s == sizeof(int) */
{
  int *b0 = buf, *b1, *b2 ;
  int  n1, n2 ;

  n1 = n / 2 ; n2 = n - n1 ;
  b1 = b ; b2 = b + n1 ;

  if (n1 > 1) mDoSortInt (b1, n1, pf, cmp_r, buf);
  if (n2 > 1) mDoSortInt (b2, n2, pf, cmp_r, buf);

  if ( cmp_r (pf, b2 - 1, b2) <= 0)	/* Already sorted case */
    return ;

  while (n1 > 0 && n2 > 0)
    if ((*cmp_r) (pf, b1, b2) <= 0)
      { n1-- ; *b0++  = *b1++ ; }
    else
      { n2-- ; *b0++  = *b2++ ; }

  if (n1 > 0)
    memcpy (b0, b1, n1 * sizeof (int)) ;
  memcpy (b, buf, (n - n2) * sizeof (int)) ;
}

/***********************************************************/

static void mDoSort ( char *b, int n, int s , void *pf, 
		     int (*cmp_r)(void *pf, const void *va, const void *vb)
		     , char *buf)
/* More general case of sort where s != sizeof(int) */
{
  char *b0 = buf, *b1, *b2 ;
  int  n1, n2 ;

  n1 = n / 2 ; n2 = n - n1 ;
  b1 = b ; b2 = b + (n1 * s);

  if (n1 > 1) mDoSort (b1, n1, s, pf, cmp_r, buf);
  if (n2 > 1) mDoSort (b2, n2, s, pf, cmp_r, buf);

  if ( cmp_r (pf, b2 - s, b2) <= 0)	/* Already sorted case */
    return ;

  while (n1 > 0 && n2 > 0)
    {
      if ((*cmp_r) (pf, b1, b2) <= 0)
	{
	  memcpy (b0, b1, s) ;
	  n1-- ; b1 += s ; b0 += s ;
	}
      else
	{
	  memcpy (b0, b2, s) ;
	  n2-- ; b2 += s ; b0 += s ;
	}
    }

  if (n1 > 0)
    memcpy (b0, b1, n1 * s) ;
  memcpy (b, buf, (n - n2) * s) ;
}

/***********************************************************/

void _pf_cm_mSort_r (void *b, int n, int s, void *pf, 
	int (*cmp_r)(void *pf, const void *va, const void *vb))
/* Sort b, containing n items of size s using supplied comparison
 * function. */
{ 
  int size = n * s, s_of_i = sizeof (int) ;
  char  sBuf [4*1024] ;
  char *mBuf = 0, *buf ;

  if (n < 2)
    return ;

  /* If size is small, use the stack, else malloc */
  if (size < 4*1024)
    buf = sBuf ;
  else
    buf = mBuf = (char *) needLargeMem(size);

  if (s == s_of_i &&   /* aligned integers.  Use direct int pointers.  */
      (((char *)b - (char *) 0) % s_of_i) == 0)
    mDoSortInt ((int*)b, n, pf, cmp_r, (int*)buf) ;
  else
    mDoSort ((char *)b, n, s, pf, cmp_r, buf) ;

  if (mBuf) freeMem (mBuf) ;
}

