/****************************************************************************/
/*	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												*/
/****************************************************************************/

/*
*	Conversion control table
*/

static const unsigned char conv64[ 35 ] = {

	0xbe,	0xe7,	0xde,	0x1a,	0x17,	0x15,	0x14,
	0x53, 	0x52,	0x51,	0x50,	0x50,	0x4f,	0x4f,
	0x4e, 	0x8e,	0x8e,	0x8e,	0x8d,	0x8d,	0x8d,
	0x8d, 	0x8c,	0x8c,	0x8c,	0x8c,	0x8c,	0x8c,
	0x8c,	0x8b,	0x8b,	0xcb,	0xcb,	0xcb,	0xcb
};

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

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

/**
*	Converts a 64-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.
*
*	@param	buff	Pointer to the buffer where the result is stored.
*					At worst case the buffer needs to be 65 bytes long.
*	@param	numb	The unsigned 64-bit 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	ulltostr( char *buff, unsigned long long numb, int base, int uppc )
{
unsigned long long	powr, mult;
unsigned int		ctrl, cntr;
unsigned int		temp, resd, mask;

	if ( base < 2 || base > 36 )

		return( 0 );

	if ( ! ( numb >> 32 ) )

		return( ultostr( buff, (unsigned long) numb, base, uppc ) );

	if ( base == 10 )

		return( ulltostr10( buff, numb ) );

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

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

	uppc = uppc ? 'A' - 10 : 'a' - 10;
	ctrl = conv64[ base - 2 ];
	temp = ctrl & 63;

	// Calculate the highest power of the base. For the special case
	// of 10 (being the most often used base) we have special rules.

	mult = umul32x32_64( base, base );

	if ( temp & 1 ) {

		powr = base;
		temp--;
		cntr = 1;
	}
	else {

		powr = 0;
		cntr = 0;
	}

	for ( mask = 2 ;; mask <<= 1 ) {

		if ( temp & mask ) {

			temp ^= mask;

			if ( cntr ) {

				powr = mul64x64_64( powr, mult );
			}
			else {

				powr = mult;
				cntr = 1;
			}
		}

		if ( temp )

			mult = mul64x64_64( mult, mult );
		else
			break;
	}

	// Now powr is the highest power that we can use. Process the top
	// part of the word. That way we create an at least 1 base digit
	// worth of space at the word's top.

	cntr = 0;

	if ( numb > powr ) {

		mult = mul64x64_64( powr, (unsigned long long) base );

		if ( numb >= mult ) {

			for ( cntr = 1 ; ! (mult & 0x8000000000000000ll) ; cntr <<= 1 )

				mult <<= 1;

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

				if ( numb >= mult ) {

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

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

	// Convert the rest of the word, the empty top bits being
	// used as workspace.

	temp = ctrl & 63;
	ctrl = base < 5 ? 1 : ( ctrl >> 6 ) + 2;

	for ( ;; ) {

		resd = 0;

		if ( numb >= powr ) {

			powr <<= ctrl;
			mask = 1 << ctrl;

			for ( ;; ) {

				if ( numb >= powr ) {

					numb -= powr;
					resd += mask;
				}

				if ( ( mask >>= 1 ) )

					powr >>= 1;
				else
					break;
			}
		}

		if ( resd || cntr ) {

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

		if ( temp-- )

			numb = mul64x64_64( numb, base );
		else
			break;
	}

	if ( ! cntr ) {

		*buff++ = '0';
		cntr = 1;
	}

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

/** @} */

