/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss of reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include <stdlib.h>

/****************************************************************************/
/*					Local prototypes										*/
/****************************************************************************/

static	void	HeapSink( void *base, size_t nmemb, size_t size,
						  int (*comp)( const void *, const void *), int this );
static	void	HeapSwap( void *base, size_t size, int this, int that );

/****************************************************************************/
/*					Public API functions									*/
/****************************************************************************/

/** @addtogroup stdlib */
/**	@{ */

/**
*	Sort an array.
*
*	The routine sorts an array of identical but otherwise arbitrary
*	comparable objects into ascending order.
*
*	@param	base	Pointer to the array of the objects to sort
*
*	@param	nmemb	The number of objects in the array.
*
*	@param	size	The size of each object in the array, as returned by the
*					\p sizeof operator.
*
*	@param	comp	A pointer to a function that receives pointers to two
*					array members and returns less than 0, 0 or greater than
*					0 if the first argument is, respectively, less than, equal
*					to or greater than the second argument.
*	@note
*		Despite being called \p qsort(), this function actually implements
*		heapsort. Although quicksort is slightly faster, it has worse
*		worst-case performance than heapsort and it also needs extra storage,
*		that heapsort does not. In an embedded system worst-case behaviour is
*		a rather important issue and usually you can't just allocate an
*		unknown amount of temporary memory. Since the C99 standard does not
*		specify that the qsort() function must actually implement quicksort,
*		it seemed prudent to choose a sort algorithm that behaves well in those
*		respects. Regardless of whether your array is random, sorted, or sorted
*		in opposite direction you can expect around \p nmemb * log2(\p nmemb)
*		swaps of array elements and up to twice as many comapares.
*/

void	qsort( void *base, size_t nmemb, size_t size,
			   int (*comp)( const void *, const void * ) )
{
int		this;

	if ( nmemb > 1 ) {

		for ( this = ( nmemb - 2 ) / 2 ; this >= 0 ; this-- ) {

			HeapSink( base, nmemb, size, comp, this );
		}

		while ( nmemb-- > 1 ) {

			HeapSwap( base, size, 0, nmemb );
			HeapSink( base, nmemb, size, comp, 0 );
		}
	}
}

/** @} */

/****************************************************************************/
/*					Local functions											*/
/****************************************************************************/

/*
*	Sink the top element of a subtree to form a heap
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The function assumes that the two subtrees are already heaps and it is
*	only the root that can be erroneous.
*/

static	void	HeapSink( void *base, size_t nmemb, size_t size,
						  int (*comp)( const void *, const void * ), int this )
{
int		that, test;

	that = 2 * this + 1;

	while ( that < (int) nmemb ) {

		test = that + 1;

		if ( test < (int) nmemb &&
			 (*comp)( (char *) base + test * size,
					  (char *) base + that * size ) >= 0 ) that = test;

		if ( (*comp)( (char *) base + this * size,
					  (char *) base + that * size ) >= 0 ) break;

		HeapSwap( base, size, this, that );
		this = that;
		that = 2 * this + 1;
	}
}

/*
*	Swaps two elements of the array
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	void	HeapSwap( void *base, size_t size, int this, int that )
{
unsigned char	*a, *b;
unsigned int	*x, *y;
unsigned int	temp;

	a = (unsigned char *) base + this * size;
	b = (unsigned char *) base + that * size;

	if ( ! ( (int) a & ( sizeof( int ) - 1 ) ) &&
		 ! ( (int) b & ( sizeof( int ) - 1 ) ) ) {

		x = (unsigned int *) a;
		y = (unsigned int *) b;

		while ( size >= sizeof( int ) ) {

			temp = *x;
			*x++ = *y;
			*y++ = temp;
			size -= sizeof( int );
		}

		a = (unsigned char *) x;
		b = (unsigned char *) y;
	}

	while ( size-- ) {

		temp = *a;
		*a++ = *b;
		*b++ =  temp;
	}
}

