#define DEBUG
/*      hsort - general purpose heapsort

       Author...
               Copyright (c) 1985  James R. Van Zandt

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

               Based on algorithms by Jon Bentley [Communications of the ACM v
               28 n 3 p 245 (Mar 85) and v 28 n 5 p 456 (May 85)], and the
               sort interface routines by Allen I.  Holub [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 the
               heap at the intermediate stages.  This is instructive if you
               want to see how the heap sort works.  #defining DEBUG2 will
               also print results of comparisons.

       Notes...
               This routine sorts N objects in worst-case time proportional to
               N*log(N).  The heapsort was discovered by J.  W.  J.  Williams
               [Communications of the ACM v 7 p 347-348 (1964)] and is
               discussed by D.  E.  Knuth [The Art of Computer Programming,
               Volume 3: Sorting and Searching, Addison-Wesley, Reading,
               Mass., 1973, section 5.2.3].

               This algorithm depends on a portion of an array having the
               "heap" property.  The array X has the property heap[L,U] if:

                       for all      L, i, and U
                       such that    2L <= i <= U
                       we have      X[i div 2] <= X[i]

               sift_down enlarges the heap: It accepts an array with heap[L+1,U]
               and returns an array with heap[L,U].
*/

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                  */
static char *Base;                      /* pointer to element [-1] of array             */
static char *a, *b, tmp;        /* temporaries for exchanges                    */

#ifdef DEBUG
int Exchanges=0, Comparisons=0;
#endif

/*--------------------------------------------------------------------------*/
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
       Comparisons++;
#endif
#ifdef DEBUG2
       register int rval;
       rval=strcmp(*s1p,*s2p);
       printf("   argvcmp(<%s><%s>) = %d\n",*s1p,*s2p,rval);
       return (rval);
#else
       return (strcmp(*s1p,*s2p));
#endif
}

hsort(base,nel,width,compare)
char *base;
int     nel,width;
int     (*compare)();
{
static int i,j,n,stop;
       /*      Perform a heap 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
       static int ii;
       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(1,nel,base);
       for ( ii=1 ; ii<=nel ; ii++ ) printf("[%d]     ",ii);
       printf("\n");
#endif
       Width=width;
       Comp=(compare==(PFI)0) ? &argvcmp : compare ;
       n=nel*Width;
       Base=base-Width;
       for (i=(n/Width/2)*Width; i>=Width; i-=Width) sift_down(i,n);
#ifdef DEBUG
       printf("Heap constructed\n");
       for ( ii=1 ; ii<=nel ; ii++ ) printf("[%d]     ",ii);
       printf("\n");
#endif
       stop=Width+Width;
       for (i=n; i>=stop; )
               {for (b=base, a=Base+i, j=Width ; j-- ; )
                       {tmp = *b; *b++ = *a; *a++ = tmp;
                       }
#ifdef DEBUG
               Exchanges++;
#endif
               sift_down(Width,i-=Width);
               }

#ifdef DEBUG
       printf("\nSort complete, list is:\n");
       ptext(1,nel,base);
       printf("%d comparisons and %d exchanges were performed \n",
       Comparisons,Exchanges);
#endif
}

/*---------------------------------------------------------------------------*/
static sift_down(L,U) int L,U;
/*      pre: heap(L+1,U)                */
{       int c,count;

#ifdef DEBUG
       int i1;
       i1=L;
#endif
       while(1)
               {c=L+L;
               if(c>U) break;
               if( (c+Width <= U) && ((*Comp)(Base+c+Width,Base+c)>0) ) c+= Width;
               if ((*Comp)(Base+L,Base+c)>=0) break;
               for(b=Base+L,a=Base+c,count=Width; count-- ; )
                       {tmp=*b; *b++ = *a; *a++ = tmp;
                       }
#ifdef DEBUG
               Exchanges++;
#endif
               L=c;
               }
#ifdef DEBUG
       ptext(i1/2,U/2,Base+Width);
#endif
/*      post: heap(L,U)                 */
}

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

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

       register int i;
       for (i=1; i<start; i++) {printf("        "); argv++;}
       for ( i=start ; i<=end ; i++ ) printf("%-8s",*argv++);
       printf("\n");
}

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

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

#endif