#define DEBUG
/*      qsort - general purpose quicksort

       Author...
               Copyright (c) 1984  Allen I. Holub

               All rights reserved.
               This program may be copied for personal, non-profit use only.

               Published in Dr. Dobb's Journal #102 (Apr 85).

       Usage...

               Including a #define for DEBUG will make this file a stand-alone
               program which sorts argv and prints the result, along with all
               intermediate stages.  This is pretty instructive if you want to
               see how the quick sort works.

*/

#ifdef DEBUG

static int      Lev=0;          /* recursion level */
static int      Maxlev=0;       /* maximum recursion level */
int Comparisons=0, Exchanges=0;
#endif

typedef int (*PFI)();   /* pointer to a function returning int */
static PFI Comp;                /* pointer to comparison routine */
static int Width;               /* width of an object in bytes */

/*---------------------------------------------------------------------------*/
int argvcmp(s1p,s2p) char **s1p,**s2p;
{
       /*      Comparison routine for sorting an argv like list of pointers
               to strings.  Just remove one level of indirection and call
               strcmp to do the comparison                                                                             */

#ifdef DEBUG
       register int rval;
       rval=strcmp(*s1p,*s2p);
       printf("level %d: argvcmp(<%s><%s>) = %d\n",Lev,*s1p,*s2p,rval);
       Comparisons++;
       return (rval);
#else
       return (strcmp(*s1p,*s2p));
#endif
}

qsort(base,nel,width,compare)
char *base;
int     nel,width;
int     (*compare)();
{
       /*      Perform a quick sort on an array starting at base.  The
               array is nel elements large and width is the size of a single
               element in bytes.  Compare is a pointer to a comparison routine
               which will be passed pointers to two elements of the array.  It
               should return a negative number if the left-most argument is
               less than the rightmost, 0 if the two arguments are equal, a
               positive number if the left argument is greater than the right.
               (That is, it acts like a "subtract" operator.) If compare is 0
               then the default comparison routine, argvcmp (which sorts an
               argv-like array of pointers to strings), is used.
       */

#ifdef DEBUG
       printf("Sorting %d element array of %d byte elements at 0x%x\n",
               nel,width,base);
       printf("Comparison routine at 0x%x. Unsorted list:\n",compare);
       ptext(nel,base);
#endif
       Width=width;
       Comp=(compare==(PFI)0) ? &argvcmp : compare ;
       if(nel>1)
               rqsort(base,base+((nel-1)*width));
#ifdef DEBUG
       printf("\nSort complete, list is:\n");
       ptext(nel,base);
       printf("Maximum recursion level = %d\n",Maxlev);
       printf("%d comparisons and %d exchanges were performed \n",
       Comparisons, Exchanges);
#endif
}

/*---------------------------------------------------------------------------*/
static rqsort(low,high)
register char *low,*high;
{
       /*      Workhorse function called by the access routine, qsort().
               Not externally accessible.                                                                              */

       char *pivot,*base;
       static char *a,*b;      /*      Used for exchanges, will not need to retain     */
       static int tmp,i;       /*      their values during the recursion so they       */
                                               /*      can be static                                                           */

#ifdef DEBUG
       printf("New pass, recursion level %d\n",Lev);
       if (Lev>Maxlev) Maxlev=Lev;
#endif
       base=low;               /* remember base address of array       */
       pivot=high;             /* partition off the pivot                      */
       high -= Width;

       do
               {while( low<high && (*Comp)(low,pivot) <= 0)  low += Width;
               while( low<high && (*Comp)(high,pivot) >= 0 ) high -= Width;
               if( low < high )        /* exchange low & high */
                       {
#ifdef DEBUG
                       printf("lev %d: exchanging high: <%s> & low: <%s>\n",
                               Lev,*((char **)high), *((char **)low));
#endif
                       for ( b=low,a=high,i=Width ; --i>=0 ; )
                               {tmp = *b;      /* Exchange *low and *high */
                               *b++ = *a;
                               *a++ = tmp;
#ifdef DEBUG
       Exchanges++;
#endif
                               }
                       }
               } while ( low<high );
#ifdef DEBUG
               printf("level %d: Exchanging pivot:<%s>  & low:<%s>\n",
                       Lev, *((char **)pivot), *((char**)low) );
#endif
               if( low < pivot && (*Comp)(low, pivot) > 0 )
                       for ( b=low,a=pivot,i=Width ; --i >=0 ; )
                               {tmp=*b ;       /* Exchange *low and *pivot */
                               *b++=*a;
                               *a++=tmp;
#ifdef DEBUG
       Exchanges++;
#endif
                               }
#ifdef DEBUG
               printf("\nDone with pass, partially sorted list =\n");
               ptext( ((pivot - base)/Width) + 1,base );
               printf("\n");
               ++Lev;
#endif
               low += Width;
               if( high-base < pivot-low )
                       {if( low < pivot ) rqsort( low , pivot );
                       if(base < high ) rqsort( base , high );
                       }
               else
                       {if( base < high ) rqsort( base , high );
                       if( low < pivot ) rqsort( low , pivot );
                       }
#ifdef DEBUG
               --Lev;
#endif
}

/*---------------------------------------------------------------------------*/
/*              Test routine for qsort, compiled if DEBUG is #defined                           */

#ifdef DEBUG
static ptext( argc, argv)
int argc;
char **argv;
{
       /*      Print out argv, one element per line    */

       register int i;
       for ( i=1 ; --argc>=0 ; i++ ) printf("%2d: %s\n",i,*argv++);
}

main(argc,argv) int argc; char **argv;
{
       /*      Test routine for qsort.  Sorts argv (less the first element).   */

       qsort(++argv,--argc,sizeof(PFI),0);
}

#endif