#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
