/****************************************************************************/
/*	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>
#include <misc/multiply.h>
#include <blc_stdlib.h>

/****************************************************************************/
/*					Local data												*/
/****************************************************************************/

static const unsigned char conv32[ 35 ] = {

	0x3f,	0x33,	0x5e,	0x4c,	0x4b,	0x4a,	0x7e,
	0x69,	0x68,	0x68,	0x67,	0x67,	0x67,	0x67,
	0x9c,	0x86,	0x86,	0x86,	0x86,	0x86,	0x86,
	0x86,	0x85,	0x85,	0x85,	0x85,	0x85,	0x85,
	0x85,	0x85,	0xbe,	0xa5,	0xa5,	0xa5,	0xa5
};

/****************************************************************************/
/*					User API functions										*/
/****************************************************************************/

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

/**
*	Converts a 32-bit unsigned integer to ASCII with an arbitrary radix.
*
*	The number will be converted without leading zeroes or spaces and the
*	result is terminated by a 0 byte. If the radix is larger than 10, then
*	the case of letters used for digits over 9 is controlled by the \p uppc
*	argument.
*
*	@param	buff	Pointer to the buffer where the result is stored.
*					At worst case the buffer needs to be 33 bytes long.
*	@param	numb	The unsigned integer to convert
*	@param	base	The conversion base, [2..36]
*	@param	uppc	A flag indicating that for digits above 9 uppercase
*					letters should be used.
*
*	@return			The length of the final string, not counting the
*					terminating 0. If the base is not within the
*					2..36 range, no conversion takes place and 0 is
*					returned.
*
*	@note	This function is not a standard C library function.
*/

int		ultostr( char *buff, unsigned long numb, int base, int uppc )
{
unsigned long	ctrl, powr, cntr;
unsigned long	temp, resd, mask;

	if ( base < 2 || base > 36 ) return( 0 );

	if ( ! numb ) {

		buff[ 0 ] = '0';
		buff[ 1 ] = '\0';
		return( 1 );
	}

	if ( base == 10 )

		return( ultostr10( buff, numb ) );

	if ( ! ( base & ( base-1 ) ) )

		return( ultostr2n( buff, numb, base, uppc ) );

	ctrl = conv32[ base - 2 ];
	uppc = uppc ? 'A' - 10 : 'a' - 10;
	cntr = ctrl & 31;

	for ( temp = 1, powr = base ; temp < cntr ; temp++ ) {

		if ( numb <= powr )

			break;
		else
			powr *= base;
	}

	if ( temp == cntr ) {

		cntr = powr * base;

		if ( numb >= cntr ) {

			for ( mask=1 ; ! ( cntr & 0x80000000 ) ; mask <<= 1, cntr <<= 1 );

			for ( resd = 0 ; mask ; mask >>= 1, cntr >>= 1 ) {

				if ( numb >= cntr ) {

					numb -= cntr;
					resd += mask;
				}
			}

			*buff++ = resd + ( resd > 9 ? uppc : '0' );
			cntr = 1;
		}
		else {

			cntr = 0;
		}
	}
	else {

		cntr = 0;
	}

	ctrl >>= 5;

	for ( temp++ ; temp-- ; numb *= base ) {

		resd = 0;

		if ( numb >= powr ) {

			powr <<= ctrl + 1;

			for ( mask = 1 << ctrl ; mask ; mask >>= 1 ) {

				powr >>= 1;

				if ( numb >= powr ) {

					numb -= powr;
					resd += mask;
				}
			}
		}

		if ( cntr || resd ) {

			*buff++ = resd + ( resd > 9 ? uppc : '0' );
			cntr++;
		}
	}

	*buff = '\0';
	return( cntr );
}

/** @} */

