/****************************************************************************/
/*	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 <libm.h>
#include <blc_stdio.h>
#include <blc_float.h>

/****************************************************************************/
/*					Local definitions										*/
/****************************************************************************/

/*
*	See the comments at DblToAscii() for the explanation
*/

#define	DTOA_NAN_MSK	0x0020
#define	DTOA_INF_MSK	0x0040
#define	DTOA_ZRO_MSK	0x0080
#define	DTOA_NEG_MSK	0x0100
#define	DTOA_LEN_MSK	0x001f
#define	DTOA_EXP_SFT	16
#define	DTOA_EXP( x )	((x)>>DTOA_EXP_SFT)
#define	DTOA_LEN( x )	((x)&DTOA_LEN_MSK)
#define	DTOA_INF( x )	((x)&DTOA_INF_MSK)
#define	DTOA_NAN( x )	((x)&DTOA_NAN_MSK)
#define	DTOA_ZRO( x )	((x)&DTOA_ZRO_MSK)
#define	DTOA_NEG( x )	((x)&DTOA_NEG_MSK)

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

static	int		PrintStr( PRINTF *stat, int spec );
static	int		PrintFlt( PRINTF *stat, int spec );
static void		FormatA( PRINTF *stat, double numb, int upper );
static void		FormatF( PRINTF *stat, int pars, int upper );
static void		FormatE( PRINTF *stat, int pars, int upper );
static void		FormatG( PRINTF *stat, int pars, int upper );
static void		FormatS( PRINTF *stat, int pars, int upper );
static int		DblToAscii( double numb, char buff[ 20 ] );
static int		RoundStr( char *buff, int pars, int new );

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

/** @addtogroup stdio */
/**	@{ */

/**
*	Formatting core for all printf() type functions.
*
*	The function implements the formatted print function, as specified in the
*	C99 standard, with some exceptions (see Details). This is an internal
*	function, called by the actual API functions from the printf() family.
*	It is needed when you want to write your printf(), fprintf() etc. functions
*	that this library can not provide, since they require knowledge of your
*	system's handling of output devices.
*
*	@param	fmt		The format string.
*	@param	arg		The argument list (after the format string). Before you
*					call this function, you have to call va_start() and pass
*					the resulting va_list object in this argument.
*	@param	len		The maximum number of characters sent to the output. If you
*					want no limit, pass -1.
*	@param	fun		The function that outputs a number of characters.
*	@param	par		This is the first parameter that is passed to
*					<tt>(*fun)()</tt>.
*	@param	w2m		The address of the wcitomb() function. Since the actual
*					library function that is invoked when wcitomb() is called
*					depends on whether you used the <tt>-fshort-wchar</tt>
*					compiler switch and whether you have defined the
*					<tt>CONFIG_RFC3629_CONFORMANCE</tt> macro, print_flt()
*					can not know the actual function when the library is built.
*					If you pass NULL for this parameter, then the 'l' size
*					modifier for 's' and 'c' conversions will be ignored.
*
*	@return			The number of bytes written to the output or the number
*					of bytes that would have been written if the maximum number
*					of output characters were not limited. If the return value
*					is 0, then no output was generated. If the return value is
*					negative, it indicates an error code. If the number of
*					output characters were limited, then a return value greater
*					than the limit indicates that the output was truncated.
*
*	@warning
*	The print_int() and print_flt() functions both use a lot of stack. You
*	can expect a stack requirement of about 200 bytes.
*
*	@par Format string details:
*	The function reads the format string and copies it into the output
*	until a conversion specificator is found. The conversion specificator begins
*	with a '\%' character. If the next character is also a '\%' then a single
*	'\%' is sent to the output and no argument from the variable argument list
*	is consumed. Otherwise, the specificator has the following form:
*	<tt><br><br>
*	%[flags][width][.[precision]][length]conversion
*	</tt><br><br>
*	Apart from the conversion specifier all other fields are optional.
*	The details of the various fields are as follows:
*	<br><br><b>
*	Flag characters
*	</b><br>
*	The flag field is one or more characters from the following set:
*			<dl><dt><tt>
*	'+'		</tt></dt><dd>
*			For signed numeric conversions, a '+' symbol will be
*			placed in front of a non-negative result. Without this
*			flag only a negative sign is printed. For non-numeric
*			conversions the flag is ignored.
*			<br><br></dd><dt><tt>
*	' ' 	</tt></dt><dd>
*			For signed numeric conversions, non-negative numbers
*			will have a space in front of them, as a placeholder for
*			the signum. If both <tt>' '</tt> and '+' are given, '+' is used.
*			For non-numeric conversions the flag is ignored.
*			<br><br></dd><dt><tt>
*	'0' 	</tt></dt><dd>
*			For numeric conversions, the number will be zero padded
*			on the left (as opposed to space padded) to fill the
*			specified field width. If the '-' flag is also specified,
*			the '0' flag is ignored. For non-numeric conversions the
*			flag is ignored. For integer conversions if precision is
*			also specified, the flag is ignored.
*			<br><br></dd><dt><tt>
*	'-' 	</tt></dt><dd>
*			The converted object should be left-aligned (the default
*			is right aligned). The padding character is always space.
*			Only the 'n' conversion ignores this flag.
*			<br><br></dd><dt><tt>
*	'#'		</tt></dt><dd>
*			Use alternate output formatting. For 'x' or 'X' conversions
*			of a non-zero number the "0x" or "0X" strings will be
*			prepended to the result. For 'o' conversion a non-zero
*			result will be prefixed with a '0' character.
*			For float conversions the decimal point is always written and
*			for g and G conversions trailing zeroes are retained. In all
*			other cases the flag is ignored.
*			<br><br></dd></dl>
*	<b>
*	Field width
*	</b><br>
*	It is either a sequence of decimal digits or the single character of '*'.
*	A decimal digit sequence is interpreted as a decimal number. The '*'
*	character will fetch the next \b int argument from the argument list and
*	use that as the width. The value of the field width is the minimum number
*	of output characters generated by the conversion. Depending on the flags
*	and the conversion, the padding can be left or right, using spaces or
*	'0'-s (only on the left). If the field width is specified by the '*'
*	character and the value of the next argument is negative, then the
*	left align flag is set and the absoulte value of the field width will
*	be used.<br>
*	Note that the field width is the minimum number. If the result of the
*	conversion is longer than the specified field width, then the field
*	width will be ignored. Specifying a field width will never cause the
*	output being truncated.<br>
*	<i>The C99 standard also specifies the form of *m$ where m is a
*	sequence of decimal digits, meaning that the m-th argument on the
*	variable argument list should be used. This is not implemented and is
*	not recognised. In addition, the decimal character sequence is not
*	checked for overflow.</i>
*	<br><br><b>
*	Precision
*	</b><br>
*	The precision starts with a period, optionally followed by a sequence of
*	decimal digits or the single character of '*'. A decimal digit sequence
*	is interpreted as a decimal number. The '*' character will fetch the next
*	\b int argument from the argument list and use that value for the precision.
*	If negative value is obtained through '*' or the period is not followed by
*	a decimal digit or a '*', then the precision is set to 0.<br>
*	The interpretation of the precision depends on the actual conversion.
*	For integral conversions it is the minimum number of digits that should be
*	generated, if the number has less digits, then 0-s will be prepended to it.
*	For float conversions it is the minimum number of digits that follow the
*	decimal point. For non-numeric conversion the precision is ignored.<br>
*	<i>The C99 standard also specifies the form of *m$ where m is a
*	sequence of decimal digits, meaning that the m-th argument on the
*	variable argument list should be used. This is not implemented and is
*	not recognised. In addition, the decimal character sequence is not
*	checked for overflow.</i>
*	<br><br><b>
*	Length modifier
*	</b><br>
*	The interpretation of the next argument can be affected by the length
*	modifier. The exact interpretation of the length modifier depends on the
*	conversion and is detailed for each conversion specifier separately. The
*	following length modifiers are defined:
*			<dl><dt><tt>
*	"hh"	</tt></dt><dd>
*			Indicates \p char or \p unsigned \p char argument.
*			<br><br></dd><dt><tt>
*	"h"		</tt></dt><dd>
*			Indicates \p short or \p unsigned \p short argument.
*			<br><br></dd><dt><tt>
*	"l"		</tt></dt><dd>
*			Indicates \p long, \p unsigned \p long or \p wchar_t argument.
*			<br><br></dd><dt><tt>
*	"ll"	</tt></dt><dd>
*			Indicates <tt> long long </tt> or <tt> unsigned long long </tt>
*			or <tt>long double</tt> argument.
*			<br><br></dd><dt><tt>
*	"L"		</tt></dt><dd>
*			Indicates \p long \p double argument.<br>
*			<i>Recognised and a long double argument is fetched but it is
*			actually converted to a double and processed as such.</i>
*			<br><br></dd><dt><tt>
*	"j"		</tt></dt><dd>
*			Indicates \p intmax_t \p argument.<br>
*			<i>This implementation treats it as if it were an "ll".</i>
*			<br><br></dd><dt><tt>
*	"t"		</tt></dt><dd>
*			Indicates \p ptrdiff_t \p argument.<br>
*			<i>This implementation treats it as if it were an "l".</i>
*			<br><br></dd><dt><tt>
*	"z"		</tt></dt><dd>
*			Indicates \p size_t \p argument.<br>
*			<i>This implementation treats it as if it were an "l".</i>
*			<br><br></dd></dl>
*	<b>
*	Conversion specifiers
*	</b><br>
*	This single character field is compulsory. If it is missing, then the
*	function will skip the '\%' that started the conversion and print the
*	characters following it.
*			<dl><dt><tt>
*	'd'		</tt><tt>
*	'i'		</tt></dt><dd>
*			Fetches the next argument and converts it as a signed decimal
*			number. If the "l" or "ll" (or equivalent) length modifiers were
*			indicated, then the appropriate sized integer is fetched from
*			the argument list. The "h" and "hh" length modifiers are
*			ignored due to C's default integral promotion rules. If the
*			precision is 0 and the number fetched is zero, then no output is
*			generated, even in the presence of a '+' or '-' flag.
*			The '+', '-', '0' and ' ' flags are obeyed, the '#' flag has no
*			effect on the conversion. If precision is not specified, the
*			default value is 1.
*			<br><br></dd><dt><tt>
*	'u'		</tt></dt><dd>
*			Fetches the next argument and converts it as an unsigned decimal
*			number. If the "l" or "ll" (or equivalent) length modifier was
*			indicated, then the appropriate sized integer is fetched from
*			the argument list. If the "h" or "hh" length modifiers were
*			used, then the fetched number is masked to the range of
*			\p unsigned \p short or \p unsigned \p char, respectively.
*			If the precision is 0 and the number fetched is zero, then no
*			output is generated.
*			The '-' and '0' flags are obeyed, the '#', '+' and ' ' flags are
*			ignored. If precision is not specified, the default value is 1.
*			<br><br></dd><dt><tt>
*	'o'		</tt></dt><dd>
*			Fetches the next argument and converts it as an unsigned octal
*			number. If the "l" or "ll" (or equivalent) length modifier was
*			indicated, then the appropriate sized integer is fetched from
*			the argument list. If a	"h" or "hh" length modifier was used,
*			then the fetched number is masked to the range of \p unsigned
*			\p short or \p unsigned \p char, respectively.
*			If the precision is 0 and the number fetched is zero, then no
*			output is generated. If precision is not specified, the default
*			value is 1.
*			The '-' and '0' flags are obeyed, the '+' and ' ' flags are
*			ignored. If the '#' flag is set and the number is not zero, then
*			a '0' will be prepended to it to indicate an octal number.
*			<br><br></dd><dt><tt>
*	'x'		</tt><tt>
*	'X'		</tt></dt><dd>
*			Fetches the next argument and converts it as an unsigned hexadecimal
*			number. If the "l" or "ll" (or equivalent) length modifier was
*			indicated, then the appropriate sized integer is fetched from
*			the argument list. If one of the "h" or "hh" length modifiers
*			was used, then the fetched number is masked to the range of
*			\p unsigned \p short or \p unsigned \p char, respectively.
*			When the number is converted, for digits above 9 the letters
*			abcdef will be used for x conversion and ABCDEF for X conversion.
*			If the precision is 0 and the number fetched is zero, then no
*			output is generated. If precision is not specified, the default
*			value is 1.
*			The '-' and '0' flags are obeyed, the '+' and ' ' flags are
*			ignored. If the '#' flag is set and the number is not zero, then
*			a "0x" or "0X" (for x and X conversions, respectively) will be
*			prepended to the result.
*			<br><br></dd><dt><tt>
*	'p'		</tt></dt><dd>
*			Fetches the next \p void * argument and prints it as a hexadecimal
*			number. The length modifiers are ignored. If the pointer is NULL
*			then the string \p (nil) is printed, otherwise the behaviour is the
*			same as that of the %#...x conversion.
*			<br><br></dd><dt><tt>
*	'n'		</tt></dt><dd>
*			Fetches the next \p void * argument. Depending on the length
*			modifier, the pointer will be interpreted as <tt> char *, short *,
*			int *, long * </tt> or <tt> long long *</tt> (the default being
*			<tt>int *</tt>) and the number of output characters generated (but
*			not necessarily emitted) so far will be stored at the pointed
*			location. As a safety measure, the data is not stored if the
*			pointer is NULL. Flags, field width and precision are all ignored
*			and no output is generated.
*			<br><br></dd><dt><tt>
*	'E'		</tt><tt>
*	'e'		</tt></dt><dd>
*			Fetches the next double argument and prints it in the form of
*			[-]d.dddE-dd. If the conversion character is E, then the exponent
*			separator is 'E', for e format 'e' is used. The signum is written
*			if it is negative, or if the ' ' or '+' flags were given. If the
*			precision is specified, then it is the number of digits written
*			after the decimal point. If precision is not specified, the default
*			is 6. If the precision is 0, then the decimal point is omitted,
*			unless the '#' flag was specified. The decimal digit in front of
*			the decimal point is zero if and only if the number is zero.
*			Note that even in that case the result can be negative as IEEE-754
*			floats distingish between positive and negative zero.
*			The signum of the exponent is always printed and the exponent is
*			at least two digits. If the '0' flag was specified, then if the
*			field width is larger than the space needed for the number, the
*			result will be zero padded between the signum and the integer
*			decimal digit. If the number to convert is NaN, then the string
*			"nan" or "NAN" is printed (for e and E conversions, respectively),
*			all flags but the '-' as well as the precision are ignored but the
*			field width is respected. If the number to print is infinite, then
*			the strings inf or INF are printed. The zero padding flag and the
*			precision are ignored but the signum related and alignment flags
*			and the field width will still be used.
*			<br><br></dd><dt><tt>
*	'F'		</tt><tt>
*	'f'		</tt></dt><dd>
*			Fetches the next double argument and prints it in the format of
*			[-]dddddd.dddddd, printing as many digits as needed to represent
*			the number (note that this may lead to several hundred digits).
*			At least one integer digit is printed. The decimal point is printed
*			only if at least one fractional digit is present or if the '#' flag
*			was specified. The precision is the number of digits to be printed
*			after the decimal point; if not specified, 6 is assumed.
*			The treatment of the field width, flags and the handling of NaNs
*			and infinites is the same as with the E and e conversions.
*			<br><br></dd><dt><tt>
*	'G'		</tt><tt>
*	'g'		</tt></dt><dd>
*			Fetches the next double argument. If the decimal exponent is less
*			than -4 or larger than or equal to the precision, then the number
*			will be converted according to the E or e conversion, otherwise
*			according to f or F conversion. There is one difference, however:
*			After the number is rounded to the given precision, the trailing
*			0-s from the fractional part will be removed, unless the '#' flag
*			was specified. That is, if the number was 1.999E-9 and the precision
*			was set to 2, the number is first rounded to 2.00E-9, then the
*			trailing zeroes are removed, then since no fractional digits remain
*			the decimal point is removed and thus 2E-09 is printed.
*			<br><br></dd><dt><tt>
*	'A'		</tt><tt>
*	'a'		</tt></dt><dd>
*			Prints the double argument in hexadecimal form. The format is
*			[-]0Xh.hhhhP-d where h is a hex digit. After the signum first 0X
*			or 0x is printed (for A and a conversions, respectively), then a
*			single hexadecimal digit (actually, 1 or 0), the radix point and
*			then a sequence of hexadecimal digits. It is followed by the
*			exponent separator P or p (for A and a conversions, respecitvely),
*			then the binary exponent, in decimal form. The signum of the
*			binary exponent is always printed. The exponent is 1 to 4 decimal
*			digits.
*			If the precision is not given, then it will be taken as 13, since
*			that allows an IEEE-754 double to be printed in full precision.
*			The integer digit is 1 if the number was normalised, 0 otherwise.
*			The hex digits after the decimal point represent the IEEE-754
*			mantissa (which is 52 bits). The mantissa is printed as it is,
*			no normalisation or any other transformation is used on it.
*			In the hex digits A-F is used for A conversion and a-f for a
*			conversion. The exponent is the binary exponent, with the IEEE-754
*			bias removed. The only exception is the case of 0.0, where
*			the exponent is printed as 0 (even though it is actually set
*			to the maximum negative value in the double). If the number is
*			NaN or infinite, the same rules as with E and e conversion apply.
*			Note that if the precision is less than 13, the printed result
*			will <b>not</b> be rounded, it will simply be truncated.
*			<br><br></dd><dt><tt>
*	'c'		</tt></dt><dd>
*			Fetches the next int argument and prints it as a single character.
*			Alternatively, if the "l" length modifier was used, then a wchar_t
*			argument is fetched and converted to a UTF-8 sequence. The sequence
*			is then sent to the output. If the wchar_t argument is not a valid
*			Unicode character, then nothing is printed.<br>
*			The precision, the '#', '+', ' ' and '0' flags as well as any length
*			modifier apart from "l" are ignored.
*			<br><br></dd><dt><tt>
*	's'		</tt></dt><dd>
*			If no "l" length modifier was specified, then the next argument
*			is <tt>const char *</tt>. It is interpreted as a pointer to a
*			0-terminated C string. The string is copied to the output, not
*			including the terminating 0 character. If precision was specified,
*			then at most precision characters will be used from the string.<br>
*			If the "l" length modifier was present, then the argument is a
*			<tt>const wchar_t *</tt> and should point to an array of \p wchar_t
*			that is terminated by a wide NUL character. The elements of the
*			array are converted to UTF-8 and the result is sent to the output.
*			If an array element is not a valid Unicode character, then it is
*			simply ignored and skipped during the conversion. If precision
*			is specified, then it is taken as a limit of bytes generated by the
*			\p wchar_t to UTF-8 conversion. Note that the precision applies to
*			the <i>output</i> and not to the wide character array itself.
*			It is guaranteed that no partial UTF-8 sequence is sent out,
*			therefore it is possible that the actual number of output bytes
*			is less than the precision, even if the wide character array is
*			not exhausted.<br>
*			The '#', '+', ' ' and '0' flags as well as any length modifier
*			apart from "l" are ignored.
*			<br><br></dd></dl>
*
*	@par Usage example
*	@include printf.c
*/

int		print_flt( const char *fmt, va_list arg, size_t len,
					BLC_FMT_PUT fun, void **par, int (*w2m)(char*,wchar_t**) )
{
PRINTF	stat;

	stat.form = fmt;
	stat.args = arg;
	stat.outf = fun;
	stat.outp = par;
	stat.maxl = len;
	stat.wtom = w2m;
	stat.cchr = PrintStr;
	stat.cflt = PrintFlt;
	return( _blc_print( &stat ) );
}

/** @} */

/****************************************************************************/
/*					String conversion functions								*/
/****************************************************************************/

/*
*	Formats a character or a string, either char or wchar_t
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		PrintStr( PRINTF *stat, int spec )
{
int		c;
void	*s, *t;
int		n, l, i;

	if ( spec != 'c' && spec != 's' )

		return( 0 );

	if ( stat->size != SIZE_LONG || ! stat->wtom )

		return( _blc_print_str( stat, spec ) );

	// We have to do a wchar character or string print.	The character printing
	// is the same as the string printing, except that the length is 1.

	if ( spec == 'c' ) {

		stat->prec = -1;
		c = va_arg( stat->args, int );
		s = &c;
		t = s;
		n = 1;
		l = (*stat->wtom)( NULL, (void *) &t );

		if ( l <= 0 ) {

			n = 0;
			l = 0;
		}
	}
	else {

		s = va_arg( stat->args, void * );
		t = s;

		// First, we have to work out how long the string will be

		for ( n = l = 0 ;; n++ ) {

			i = (*stat->wtom)( NULL, (void *) &t );
			if ( i <= 0 ) break;
			if ( stat->prec >= 0 && l + i > stat->prec ) break;
			l += i;
		}
	}

	// Now 'n' is the number of wchars to convert 'l' is the number
	// of bytes that will result and 's' still points to the character

	if ( stat->fldw > l && ! ( stat->flag & FLAG_LEFT ) )

		_blc_print_spc( stat, stat->fldw - l );

	while( n-- )

		_blc_print_out(stat, stat->buff, (*stat->wtom)(stat->buff, (void*) &s));

	if ( stat->fldw > l && ( stat->flag & FLAG_LEFT ) )

		_blc_print_spc( stat, stat->fldw - l );

	return( 1 );
}

/****************************************************************************/
/*					Float conversion functions								*/
/****************************************************************************/

/*
*	Prints a float-point number
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		PrintFlt( PRINTF *stat, int spec )
{
int		upper;
int		pars;
double	number;

	upper = 0;

	switch ( spec ) {

		case 'A':
		case 'E':
		case 'F':
		case 'G':

			upper = 1;
			spec += 'a' - 'A';

		case 'a':
		case 'e':
		case 'f':
		case 'g':

			break;

		default:

			return( 0 );
	}

	if ( stat->size == SIZE_LDBL )

		number = (double) va_arg( stat->args, long double );
	else
		number = va_arg( stat->args, double );

	if ( spec == 'a' ) {

		// The 'a' specifier is a special case

		FormatA( stat, number, upper );
	}
	else {

		// Normal, decimal case. Convert the number.

		pars = DblToAscii( number, stat->buff );

		// Since the treatment of NaNs and infinitives is common,
		// we do that here.

		if ( DTOA_INF( pars ) | DTOA_NAN( pars ) ) {

			FormatS( stat, pars, upper );
		}
		else {

			switch ( spec ) {

				case 'e':	FormatE( stat, pars, upper ); break;
				case 'f':	FormatF( stat, pars, upper ); break;
				case 'g':	FormatG( stat, pars, upper ); break;
			}
		}
	}

	return( 1 );
}

/*
*	Converts a number using the 'f' format specifier
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The number is converted into the state's buffer, 'pars' is the
*	composit result of the dtoa().
*/

static void		FormatF( PRINTF *stat, int pars, int upper )
{
int		spc_befor, spc_after;
int		int_befor, int_digit, int_after;
int		fra_befor, fra_digit, fra_after;
char	chr_sign, chr_decp;
int		pow, len;

	(void) upper;

	if ( stat->prec < 0 ) stat->prec = 6;

	pow = DTOA_EXP( pars );
	len = DTOA_LEN( pars );

	chr_sign = 0;
	chr_decp = 0;

	int_befor = 0;
	spc_befor = 0;
	spc_after = 0;

	// First, work out the rounding condition. We have to round
	// if there are more fractional digits than the precision

	if ( len - pow - 1 > stat->prec ) {

		// We need to round. The number of characters remaining
		// should be the number of integer chars plus the precision.

		pars = RoundStr( stat->buff, pars, stat->prec + pow + 1 );
		pow = DTOA_EXP( pars );
		len = DTOA_LEN( pars );
	}

	// Work out how many integer characters to write

	if ( pow < 0 ) {

		// The number is smaller than 1, the integer part
		// consist of a single '0'.

		int_digit = 0;
		int_after = 1;
		fra_befor = -1 - pow;
		fra_digit = DTOA_LEN( pars );
	}
	else {

		// The number is >= 1 (or 0, actually).

		if ( pow + 1 >= len ) {

			int_digit = len;
			int_after = pow + 1 - len;
			fra_befor = 0;
			fra_digit = 0;
		}
		else {

			int_digit = pow + 1;
			int_after = 0;
			fra_befor = 0;
			fra_digit = len - pow - 1;
		}
	}

	// Check if we have to trimm the fractional part

	if ( fra_befor >= stat->prec ) {

		fra_befor = stat->prec;
		fra_digit = 0;
		fra_after = 0;
	}
	else if ( fra_befor + fra_digit > stat->prec ) {

		fra_digit = stat->prec - fra_befor;
		fra_after = 0;
	}
	else {

		fra_after = stat->prec - fra_befor - fra_digit;
	}

	// Now we have to calculate the entire length of the number
	// Since we don't need the exponent any more, we can use it.

	pow = int_digit + int_after + fra_befor + fra_digit + fra_after;

	if ( ( stat->flag & ( FLAG_PLUS | FLAG_BLNK ) ) | DTOA_NEG( pars ) ) {

		// There is a signum character

		if ( DTOA_NEG( pars ) ) {

			chr_sign = '-';
		}
		else if ( stat->flag & FLAG_PLUS ) {

			chr_sign = '+';
		}
		else {

			chr_sign = ' ';
		}

		len++;
	}

	if ( ( fra_befor + fra_digit + fra_after ) || ( stat->flag & FLAG_ALTF ) ) {

		// We have to write the decimal point

		chr_decp = '.';
		len++;
	}

	// Let's work out if we need to write leading 0-s

	if ( stat->fldw > pow ) {

		if ( stat->flag & FLAG_ZERO ) {

			int_befor = stat->fldw - pow;
		}
		else if ( stat->flag & FLAG_LEFT ) {

			spc_after = stat->fldw - pow;
		}
		else {

			spc_befor = stat->fldw - pow;
		}
	}

	// Now we can write the whole lot.

	(void) (
	( ! spc_befor || _blc_print_spc( stat, spc_befor ) ) &&
	( ! chr_sign  || _blc_print_out( stat, &chr_sign, 1 ) ) &&
	( ! int_befor || _blc_print_zro( stat, int_befor ) ) &&
	( ! int_digit || _blc_print_out( stat, stat->buff, int_digit ) ) &&
	( ! int_after || _blc_print_zro( stat, int_after ) ) &&
	( ! chr_decp  || _blc_print_out( stat, &chr_decp, 1 ) ) &&
	( ! fra_befor || _blc_print_zro( stat, fra_befor ) ) &&
	( ! fra_digit || _blc_print_out( stat, stat->buff+int_digit,fra_digit) ) &&
	( ! fra_after || _blc_print_zro( stat, fra_after ) ) &&
	( ! spc_after || _blc_print_spc( stat, spc_after ) )
	);
}

/*
*	Converts a number using the 'e' format specifier
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The number is converted into the state's buffer, 'pars' is the
*	composit result of the dtoa().
*/

static	void	FormatE( PRINTF *stat, int pars, int upper )
{
int		spc_befor, spc_after;
int		int_befor, exp_digit;
int		fra_digit, fra_after;
char	chr_sign, chr_decp;
int		tmp;
char	pow[ 6 ];

	if ( stat->prec < 0 ) stat->prec = 6;

	// Round the number

	pars = RoundStr( stat->buff, pars, stat->prec + 1 );

	if ( stat->prec == 0 ) {

		chr_decp = ( stat->flag & FLAG_ALTF ) ? '.' : 0;
	}
	else {

		chr_decp = '.';
	}

	// Work out the number of 0's after the number

	fra_digit = DTOA_LEN( pars ) - 1;
	fra_after = stat->prec - fra_digit;

	// Work out the exponent bit

	tmp = DTOA_EXP( pars );
	pow[ 0 ] = upper ? 'E' : 'e';
	pow[ 1 ] = tmp < 0 ? '-' : '+';
	if ( tmp < 0 ) tmp = -tmp;

	if ( tmp < 10 ) {

		pow[ 2 ] = '0';
		exp_digit = ultostr10( pow + 3, tmp ) + 3;
	}
	else {

		exp_digit = ultostr10( pow + 2, tmp ) + 2;
	}

	// Work out the signum bit

	chr_sign = 0;

	if ( DTOA_NEG( pars ) ) {

		chr_sign = '-';
	}
	else if ( stat->flag & FLAG_PLUS ) {

		chr_sign = '+';
	}
	else {

		chr_sign = ' ';
	}

	// Now we know the length of the number

	tmp  = 1;
	tmp += chr_sign ? 1 : 0;
	tmp += chr_decp ? 1 : 0;
	tmp += fra_digit + fra_after + exp_digit;

	spc_befor = 0;
	spc_after = 0;
	int_befor = 0;

	if ( tmp < stat->fldw ) {

		// We have to pad

		if ( stat->flag & FLAG_LEFT ) {

			spc_after = stat->fldw - tmp;
		}
		else if ( stat->flag & FLAG_ZERO ) {

			int_befor = stat->fldw - tmp;
		}
		else {

			spc_befor = stat->fldw - tmp;
		}
	}

	// Print the lot

	(void) (
	( ! spc_befor || _blc_print_spc( stat, spc_befor ) ) &&
	( ! chr_sign  || _blc_print_out( stat, &chr_sign, 1 ) ) &&
	( ! int_befor || _blc_print_zro( stat, int_befor ) ) &&
	( 				 _blc_print_out( stat, stat->buff, 1 ) ) &&
	( ! chr_decp  || _blc_print_out( stat, &chr_decp, 1 ) ) &&
	( ! fra_digit || _blc_print_out( stat, stat->buff + 1, fra_digit ) ) &&
	( ! fra_after || _blc_print_zro( stat, fra_after ) ) &&
	( 				 _blc_print_out( stat, pow, exp_digit ) ) &&
	( ! spc_after || _blc_print_spc( stat, spc_after ) )
	);
}

/*
*	Converts a number using the 'g' format specifier
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The number is converted into the state's buffer, 'pars' is the
*	composit result of the dtoa().
*/

static	void	FormatG( PRINTF *stat, int pars, int upper )
{
int		pow, len;

	if ( stat->prec < 0 ) stat->prec = 6;

	len = DTOA_LEN( pars );
	pow = DTOA_EXP( pars );

	if ( pow < -4 || pow >= stat->prec ) {

		// We have to do E conversion. That's the easy thing.

		if ( ! ( stat->flag & FLAG_ALTF ) ) {

			pars = RoundStr( stat->buff, pars, stat->prec + 1 );
			stat->prec = DTOA_LEN( pars ) - 1;
		}

		FormatE( stat, pars, upper );
	}
	else {

		if ( ! ( stat->flag & FLAG_ALTF ) ) {

			if ( len - pow - 1 > stat->prec ) {

				// Round the number first

				pars = RoundStr( stat->buff, pars, stat->prec + pow + 1 );
				pow = DTOA_EXP( pars );
				len = DTOA_LEN( pars );

				// Now set the precision

				stat->prec = len - pow - 1;
				if ( stat->prec < 0 ) stat->prec = 0;
			}
		}

		FormatF( stat, pars, upper );
	}
}

/*
*	Converts a number using the 'a' format specifier
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	void	FormatA( PRINTF *stat, double numb, int upper )
{
unsigned long long	man;
int					pow, sig;
int					exp_len, num_len, beg_len, sum_len;
int					zro_bef, zro_aft;
int					spc_bef, spc_aft;

	if ( stat->prec < 0 ) stat->prec = 13;

	man = _blm_dtoull( numb );
	sig = ( man >> 63 ) ? DTOA_NEG_MSK : 0;
	pow = ( ( man >> 52 ) & 2047 );
	man &= ( ( 1ull << 52 ) - 1 );

	if ( pow == 0x7ff ) {

		// The exponent indicates a special case

		if ( ! man ) {

			// Infinity (signed)

			FormatS( stat, sig | DTOA_INF_MSK, upper );
		}
		else {

			// NaN, NaN-s are not signed

			FormatS( stat, DTOA_NAN_MSK, upper );
		}

		return;
	}

	// Convert the mantissa to a hex number. This conversion guarantees
	// that the converted number will be 15 characters long

	ulltostr2n( stat->buff, man | ( 1ull << 56 ), 16, upper );
	stat->buff[ 0 ] = pow ? '1' : '0';
	stat->buff[ 1 ] = '.';

	// Now we have the h.hhhhhhhhhhhhh result. Work out the rest.
	// Check for the special case of 0, in which case the exponent
	// should not be unbiased.

	if ( pow || man ) pow -= 1023;

	if ( pow < 0 ) {

		stat->buff[ 16 ] = '-';
		pow = -pow;
	}
	else {

		stat->buff[ 16 ] = '+';
	}

	stat->buff[ 15 ] = upper ? 'P' : 'p';

	exp_len = ultostr10( stat->buff + 17, pow ) + 2;

	if ( stat->prec < 13 ) {

		num_len = stat->prec + 2;
		zro_aft = 0;
	}
	else {

		num_len = 15;
		zro_aft = stat->prec - 13;
	}

	if ( sig ) {

		stat->buff[ 21 ] = '-';
		stat->buff[ 22 ] = '0';
		stat->buff[ 23 ] = upper ? 'X' : 'x';
		beg_len = 3;
	}
	else if ( stat->flag & FLAG_PLUS ) {

		stat->buff[ 21 ] = '+';
		stat->buff[ 22 ] = '0';
		stat->buff[ 23 ] = upper ? 'X' : 'x';
		beg_len = 3;
	}
	else if ( stat->flag & FLAG_BLNK ) {

		stat->buff[ 21 ] = ' ';
		stat->buff[ 22 ] = '0';
		stat->buff[ 23 ] = upper ? 'X' : 'x';
		beg_len = 3;
	}
	else {

		stat->buff[ 21 ] = '0';
		stat->buff[ 22 ] = upper ? 'X' : 'x';
		beg_len = 2;
	}

	spc_bef = 0;
	spc_aft = 0;
	zro_bef = 0;
	sum_len = stat->fldw - num_len - exp_len - beg_len - zro_aft;

	if ( sum_len > 0 ) {

		if ( stat->flag & FLAG_ZERO )

			zro_bef = sum_len;

		else if ( stat->flag & FLAG_LEFT )

			spc_aft = sum_len;
		else
			spc_bef = sum_len;
	}

	(void) (
	( ! spc_bef || _blc_print_spc( stat, spc_bef ) ) &&
	( 			   _blc_print_out( stat, stat->buff + 21, beg_len ) ) &&
	( ! zro_bef || _blc_print_zro( stat, zro_bef ) ) &&
	( 			   _blc_print_out( stat, stat->buff, num_len ) ) &&
	( ! zro_aft || _blc_print_zro( stat, zro_aft ) ) &&
	( 			   _blc_print_out( stat, stat->buff + 15, exp_len ) ) &&
	( ! spc_aft || _blc_print_spc( stat, spc_aft ) )
	);
}

/*
*	Formats a NaN or an infinite for any float format
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	void	FormatS( PRINTF *stat, int parm, int upper )
{
const char	*str;
int			bef, len, aft;

	if ( DTOA_NAN( parm ) ) {

		str = upper ? "NAN" : "nan";
		len = 3;
	}
	else {

		if ( DTOA_NEG( parm ) ) {

			str = upper ? "-INF" : "-inf";
			len = 4;
		}
		else if ( stat->flag & FLAG_PLUS ) {

			str = upper ? "+INF" : "+inf";
			len = 4;
		}
		else if ( stat->flag & FLAG_BLNK ) {

			str = upper ? " INF" : " inf";
			len = 4;
		}
		else {

			str = upper ? "INF" : "inf";
			len = 3;
		}
	}

	bef = 0;
	aft = 0;

	if ( stat->fldw > len ) {

		if ( stat->flag & FLAG_LEFT )

			aft = stat->fldw - len;
		else
			bef = stat->fldw - len;
	}

	(void) (
	( ! bef || _blc_print_spc( stat, bef ) ) &&
	( 		   _blc_print_out( stat, str, len ) ) &&
	( ! aft || _blc_print_spc( stat, aft ) )
	);
}

/****************************************************************************/
/*					Double to string conversion functions					*/
/****************************************************************************/

/*
*	Rounds a number to the given number of digits
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The new length can *not* be 0!
*/

static int		RoundStr( char *buff, int pars, int new )
{
	if ( new < 1 ) new = 1;

	if ( new >= DTOA_LEN( pars ) ) return( pars );

	if ( buff[ new ] >= '5' ) {

		for ( --new ;; new-- ) {

			if ( buff[ new ] == '9' ) {

				if ( ! new ) {

					*buff = '1';
					pars += 1 << DTOA_EXP_SFT;
					break;
				}
			}
			else {

				buff[ new ]++;
				break;
			}
		}

		new++;
	}

	buff[ new ] = 0;
	return( ( pars & ~DTOA_LEN_MSK ) | new );
}

/*
*	Converts a double to string format
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	The conversion results in an at least 1 but not more than 18-character
*	string representing the decimal mantissa in d.dddd.. format, with the '.'
*	omitted. The string is terminated with a NUL. Note that this string is
*	at most 19 bytes, but the buffer *MUST* be at least 20 bytes long!
*
*	The conversion is not precise. Converting an IEEE double to string format
*	is by no means a trivial task. There is a significant amount of literature
*	exploring this subject. This function does not even attempt to be as clever
*	as the algorithms described in the literature or what you'd find in a good
*	printf()'s source code. Its aim is to be fairly precise but in the same
*	time to be reasonably small and fast. The returned result is fully
*	precise if the number to convert is an integer 1 <= N <= 1E17, for
*	these numbers are precisely representable in an IEEE-754 double. Numbers
*	between 1E17 and 1E19 are also converted with full precision, although
*	not all integers of that subset can be exactly represented in the IEEE
*	format. However, the conversion yields the number that the IEEE double
*	represented without any error. Any other number is converted to a string
*	using a method that is much more simple than the various methods described
*	in the literature that guarantee that you get the number that the IEEE
*	double actually represents (or a close one that satisfies some specific
*	criteria such as yielding the same double if converted back). The method
*	used here thus introduces an error, that represents itself in a relative
*	error that is within +/- 1E-17. This means that your 18-th digit can be
*	one larger or one smaller than the real thing (and, unfortunately, it
*	can propagate to the higher order digits too, if the 17th would be 0 or 9).
*	This happens in about 3.6% of all conversions; in the remaining 96.4%
*	the result matches the result given by a proper printf( "%.17e" ).
*
*	Furthermore, the decimal exponent, various flags and the length of the
*	result without trailing zeroes are returned packed into an int. The fields
*	can be extracted by passing the result to the following macros:
*
*		DTOA_NAN()	indicates that the number was NaN (pun not intended).
*					No other field is valid in the returned value and
*					nothing is written into the buffer.
*
*		DTOA_INF()	indicates that the number was infinite. Only the signum
*					is valid and nothing was written into into the buffer.
*
*		DTOA_ZRO()	indicates that the number was 0. The result is a single
*					digit of '0', length 1, exponent 0.
*
*		DTOA_NEG()	indicates that the number was negative. This bit is always
*					valid except when a NaN was converted.
*
*		DTOA_EXP()	yields the signed decimal exponent. In cases when no real
*					conversion takes place (NaN, inf, 0.0) this macro always
*					returns 0.
*
*		DTOA_LEN()	returns the length of the result with the trailing zeroes
*					removed. For the special cases it will return 0 (since
*					nothing was written into the buffer), for all other cases
*					it returns a number in [1,17]. This is the same number
*					that you'd get with an strlen( buff ).
*/

static	int		DblToAscii( double numb, char buff[ 20 ] )
{
int					bin, ten, len, sig;
unsigned long long	man;
unsigned int		acc[ 3 ];
const MULT			*mp;

	// The operation below does nothing but makes the compiler believe that
	// the 64 bits that represent a double actually represent an integer.
	// This allows us to manipulate the various bitfields within a IEEE-754
	// double directly.

	man = _blm_dtoull( numb );

	// Decompose the double. The IEEE-754 bit positions are simply hardcoded

	sig = ( man >> 63 ) ? DTOA_NEG_MSK : 0;
	bin = ( ( man >> 52 ) & 2047 );
	man &= ( ( 1ull << 52 ) - 1 );

	if ( bin == 0x7ff ) {

		// The exponent indicates a special case

		if ( ! man ) {

			// Infinity (signed)

			return( sig | DTOA_INF_MSK );
		}
		else {

			// NaN, NaN-s are not signed

			return( DTOA_NAN_MSK );
		}
	}

	if ( ! bin ) {

		// An outher special case

		if ( ! man ) {

			// The number is 0 (which is signed).

			buff[ 0 ] = '0';
			buff[ 1 ] = '\0';
			return( sig | DTOA_ZRO_MSK | 1 );
		}
		else {

			// Denormalised number, we must normalise it. It is a very
			// rare event, we don't really care about efficiency so
			// we simply shift one bit at a time.

			for ( ++bin ; ! ( (int) ( man >> 52 ) ) ; man <<= 1, bin-- );
		}
	}
	else {

		// Normalised number, insert the implicit "1." in front of the mantissa

		man |= 1ull << 52;
	}

	// Remove the bias from the exponent and shift the number up so
	// that the top bit is set. The current decimal exponent is 0.

	man <<= 11;
	bin -= 1023;
	ten  = 0;

	// Now the mantissa is in the form of 1.xxxxx000, with at least 11 bits
	// on the bottom being zero. We want the exponent to fall between 0
	// and 63. We may need to do some multiplications for that.

	if ( bin < 0 || bin > 63 ) {

		// Prepare for 96-bit multiplies to get the exponent into range.

		acc[ 0 ] = man >> 32;
		acc[ 1 ] = man;
		acc[ 2 ] = 0;

		// We will need 3 multiplications worst case (when the exponent is
		// is extremely large or extremely small), 1 or 2 multiplications
		// otherwise.

		for ( mp = _blc_float_pos ; bin < 0 ; mp++ ) {

			if ( bin + mp->binexp <= 63 ) {

				bin -= _blc_float_umul96x96( acc, mp->mant96 ) - 1;
				bin += mp->binexp;
				ten -= mp->decexp;
				mp--;
			}
		}

		for ( mp = _blc_float_neg ; bin > 63 ; mp++ ) {

			if ( bin + mp->binexp >= 0 ) {

				bin -= _blc_float_umul96x96( acc, mp->mant96 ) - 1;
				bin += mp->binexp;
				ten -= mp->decexp;
				mp--;
			}
		}

		// Convert back the mantissa from 96 bit format.

		man = ( (unsigned long long) acc[ 0 ] << 32 ) | acc[ 1 ];
	}

	// At this point the exponent is between 0 and 63, while 'ten' contains
	// the power-of-10 we had to apply in order to put the exponent into that
	// range and 'man' is the mantissa, still in 1.xxx format. Now convert
	// the integer part of the mantissa into a string, trailing zeroes omitted.

	len = ulltostr10( buff, man >> ( 63 - bin ) );
	if ( buff[ 0 ] == '0' ) len = 0;
	ten += len - 1;

	// Work out the fractional part of the mantissa

	if ( bin < 63 ) {

		man <<= bin + 1;
		man >>= 4;
	}
	else
		man = 0;

	// The fractional part is easy to convert, multiply by 10, get the
	// integer part, print it, retain the fractional part, do it again.
	// We generate no more than 20 characters all together.

	while ( man && len < 20 ) {

		man <<= 1;
		man  += man << 2;
		buff[ len++ ] = ( man >> 60 ) + '0';
		man &= ( 1ull << 60 ) - 1;
	}

	// Calculate the length of the result without trailing zeroes.
	// We know that the result is non-zero, therefore we also know
	// that this will terminate.

	while ( buff[ len - 1 ] == '0' ) len--;

	// If the final length is > 18, then we round it to 18 digits.

	if ( len == 20 ) {

		len = 19;
		if ( buff[ 19 ] >= '5' ) buff[ 18 ]++;
	}

	if ( len == 19 ) {

		len--;

		if ( buff[ 18 ] >= '5' ) {

			for ( bin = 17 ; bin >= 0 ; bin-- ) {

				if ( ++buff[ bin ] > '9' ) {

					if ( ! bin ) {

						len = 1;
						buff[ 0 ] = '1';
						ten++;
					}
					else {

						buff[ bin ] = '0';
						len--;
					}
				}
				else {

					break;
				}
			}
		}
	}

	// Terminate the buffer

	buff[ len ] = 0;

	// Now we return a 32-bit word containing all other information about
	// the number. The format of this number is explained by the macros that
	// decipher them.

	return( ( ten << 16 ) | sig | ( len + 1 - 1 ) );
}


