/* $Id$ */
static	qsort1();
static	int (*qcompar)();
static	qexchange();
static	q3exchange();

qsort(base, nel, width, compar)
	char *base;
	int (*compar)();
{
	qcompar = compar;
	if (sizeof(int) < sizeof(char *)) {
		qsort1(base, base + (nel - 1) * (long) width, width);
	}
	else	qsort1(base, base + (nel - 1) * width, width);
}

static
qsort1(a1, a2, width)
	char *a1, *a2;
	register int width;
{
	register char *left, *right;
	register char *lefteq, *righteq;
	int cmp;

	for (;;) {
		if (a2 <= a1) return;
		left = a1;
		right = a2;
		if (sizeof(int) < sizeof(char *)) {
			lefteq = righteq = a1 + width *
					((((long)a2-(long)a1)+width)/(2*width));
		}
		else 	lefteq = righteq = a1 + width *
					(((a2-a1)+width)/(2*width));
		/*
		   Pick an element in the middle of the array.
		   We will collect the equals around it.
		   "lefteq" and "righteq" indicate the left and right
		   bounds of the equals respectively.
		   Smaller elements end up left of it, larger elements end
		   up right of it.
		*/
again:
		while (left < lefteq && (cmp = (*qcompar)(left, lefteq)) <= 0) {
			if (cmp < 0) {
				/* leave it where it is */
				left += width;
			}
			else {
				/* equal, so exchange with the element to
				   the left of the "equal"-interval.
				*/
				lefteq -= width;
				qexchange(left, lefteq, width);
			}
		}
		while (right > righteq) {
			if ((cmp = (*qcompar)(right, righteq)) < 0) {
				/* smaller, should go to left part
				*/
				if (left < lefteq) {
					/* yes, we had a larger one at the
					   left, so we can just exchange
					*/
					qexchange(left, right, width);
					left += width;
					right -= width;
					goto again;
				}
				/* no more room at the left part, so we
				   move the "equal-interval" one place to the
				   right, and the smaller element to the
				   left of it.
				   This is best expressed as a three-way
				   exchange.
				*/
				righteq += width;
				q3exchange(left, righteq, right, width);
				lefteq += width;
				left = lefteq;
			}
			else if (cmp == 0) {
				/* equal, so exchange with the element to
				   the right of the "equal-interval"
				*/
				righteq += width;
				qexchange(right, righteq, width);
			}
			else	/* just leave it */ right -= width;
		}
		if (left < lefteq) {
			/* larger element to the left, but no more room,
			   so move the "equal-interval" one place to the
			   left, and the larger element to the right
			   of it.
			*/
			lefteq -= width;
			q3exchange(right, lefteq, left, width);
			righteq -= width;
			right = righteq;
			goto again;
		}
		/* now sort the "smaller" part */
		qsort1(a1, lefteq - width, width);
		/* and now the larger, saving a subroutine call
		   because of the for(;;)
		*/
		a1 = righteq + width;
	}
	/*NOTREACHED*/
}

static
qexchange(p, q, n)
	register char *p, *q;
	register int n;
{
	register int c;

	while (n-- > 0) {
		c = *p;
		*p++ = *q;
		*q++ = c;
	}
}

static
q3exchange(p, q, r, n)
	register char *p, *q, *r;
	register int n;
{
	register int c;

	while (n-- > 0) {
		c = *p;
		*p++ = *r;
		*r++ = *q;
		*q++ = c;
	}
}
