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

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

/*
*	LAMBDA is a double representing 32/ln(2).
*	LINV_H and LINV_L are two doubles so that
*	- LINV_H has 7 trailing 0 bits in its mantissa, and
*	- LINV_H + LINV_L approximates ln(2)/32 to 99 bits of precision
*/

#define	LAMBDA	46.16624130844682903640485704954699031077
#define	LINV_H	(48775896626291.0/2251799813685248.0)
#define	LINV_L	(0.868819384119795758036378714112/2251799813685248.0)

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

/*
*	Coefficients for the Chebyshev approximation of the
*	exp( x ) - ( x + 1 ) function. The 0th and 1st coefficients
*	are 0, they are not stored.
*/

static const double poly[ 5 ] = {

	+4.99999999999991658284010168089572800909081706777215004e-01,
	+1.66666666666220604687718639946858445455291075631976128e-01,
	+4.16666675502474890101646358420151017298849183134734631e-02,
	+8.33333995577406280628421798156724165096420620102435350e-03,
	+1.36878925039409197897492662847687694416265458130510524e-03
};

/*
*	This table contains the 2^(i/32) values for the i : [0,31] range.
*	The first number + second number of each row gives you the actual
*	value for over 100 bit precision.
*/

static const double pows[ 32 ][ 2 ] = {

	{ 0x1.0000000000000p+0, 0x0.0000000000000p-52 },
	{ 0x1.059B0D3158574p+0, 0x0.3AE7C548EB68Cp-52 },
	{ 0x1.0B5586CF9890Fp+0, 0x0.6298B92B71842p-52 },
	{ 0x1.11301D0125B50p+0, 0x0.A4EBBF1AED931p-52 },
	{ 0x1.172B83C7D517Ap+0, 0x0.DCDF7C8C50EB1p-52 },
	{ 0x1.1D4873168B9AAp+0, 0x0.7805B8028990Fp-52 },
	{ 0x1.2387A6E756238p+0, 0x0.66C1FADB1C15Cp-52 },
	{ 0x1.29E9DF51FDEE1p+0, 0x0.2C25D15F5A24Ap-52 },
	{ 0x1.306FE0A31B715p+0, 0x0.2DE8D5A46305Cp-52 },
	{ 0x1.371A7373AA9CAp+0, 0x0.A7145502F4547p-52 },
	{ 0x1.3DEA64C123422p+0, 0x0.35B41223E13D7p-52 },
	{ 0x1.44E086061892Dp+0, 0x0.03136F409DF01p-52 },
	{ 0x1.4BFDAD5362A27p+0, 0x0.1D4397AFEC42Ep-52 },
	{ 0x1.5342B569D4F81p+0, 0x0.DF0A83C49D86Ap-52 },
	{ 0x1.5AB07DD485429p+0, 0x0.58C93015191EBp-52 },
	{ 0x1.6247EB03A5584p+0, 0x0.B1F0FA06FD2DAp-52 },
	{ 0x1.6A09E667F3BCCp+0, 0x0.908B2FB1366EAp-52 },
	{ 0x1.71F75E8EC5F73p+0, 0x0.DD2370F2EF0ACp-52 },
	{ 0x1.7A11473EB0186p+0, 0x0.D7D51023F6CDAp-52 },
	{ 0x1.82589994CCE12p+0, 0x0.8ACF88AFAB34Ap-52 },
	{ 0x1.8ACE5422AA0DBp+0, 0x0.5BA7C55A192C9p-52 },
	{ 0x1.93737B0CDC5E4p+0, 0x0.F4501C3F2540Ap-52 },
	{ 0x1.9C49182A3F090p+0, 0x0.1C7C46B071F2Bp-52 },
	{ 0x1.A5503B23E255Cp+0, 0x0.8B424491CAF87p-52 },
	{ 0x1.AE89F995AD3ADp+0, 0x0.5E8734D177320p-52 },
	{ 0x1.B7F76F2FB5E46p+0, 0x0.EAA7B081AB53Cp-52 },
	{ 0x1.C199BDD85529Cp+0, 0x0.2220CB12A091Bp-52 },
	{ 0x1.CB720DCEF9069p+0, 0x0.1503CBD1E949Dp-52 },
	{ 0x1.D5818DCFBA487p+0, 0x0.25DA05AEB66E0p-52 },
	{ 0x1.DFC97337B9B5Ep+0, 0x0.B968CAC39ED29p-52 },
	{ 0x1.EA4AFA2A490D9p+0, 0x0.858F73A18F5DBp-52 },
	{ 0x1.F50765B6E4540p+0, 0x0.674F84B762862p-52 }
};

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

/** @addtogroup func */
/** @{ */

/**
*	Power of two function
*
*	@param	x		The power to which 2 should be raised
*
*	@retval	NaN		The argument was NaN
*	@retval	+INF	The argument was +INF or the result would overflow
*	@retval	0.0		The argument was -INF or the result would underflow
*	@retval	2^x		Otherwise
*/

double	exp2( double x )
{
	return( exp( x * 0.6931471805599453094286904741849753008864 ) );
}

/**
*	Power of ten function
*
*	@param	x		The power to which 10 should be raised
*
*	@retval	NaN		The argument was NaN
*	@retval	+INF	The argument was +INF or the result would overflow
*	@retval	0.0		The argument was -INF or the result would underflow
*	@retval	10^x	Otherwise
*/

double	exp10( double x )
{
	return( exp( x * 2.302585092994045684036338861311321579706 ) );
}

/**
*	Inverse natural logarithm function.
*
*	@param	x		The power to which 'e' should be raised
*
*	@retval	NaN		The argument was NaN
*	@retval	+INF	The argument was +INF or the result would overflow
*	@retval	0.0		The argument was -INF or the result would underflow
*	@retval	e^x		Otherwise
*/

double	exp( double x )
{
int			n1, n2;
double		rp, rl, rr;

	// Standard sanity checks

	if ( isnan( x ) ) return( x );
	if ( isinf( x ) ) return( x > 0 ? x : 0 );

	// The algoritm has no error at zero, but this is faster
	// and is absolutely definitely positively sure to return
	// the precise value. Rule #3 of approximations: at well
	// know "round" (x,y) pairs always return the exact result.

	if ( x == 0 ) return( 1.0 );

	// Arguments that will generate a result outside of the range of
	// and IEEE-754 double can be dealt with in short order.

	if ( x > +709.7827128933839731 ) return( infinity() );
	if ( x < -745.1332191019411084 ) return( 0 );

	// The algorithm below is by Tang. The polynomial approximation of
	// exp(x) over a wider interval is not converging too fast. However,
	// in a narrow interval around zero, even a low order polynomial gives
	// pretty good results and is quite fast.
	// So the idea is to narrow the range down to [ -ln(2)/32, +ln(2)/32 ]
	// and do the calculations there. To keep the precision for the range
	// reduction and then the reconstruction, some numbers are stored as
	// two doubles, one much lager than the other and having a few trailing
	// 0 bits in its mantissa, so that the sum of the two doubles, using
	// extended precision, represents the number for almost 100 bits.
	// We will calculate 3 numbers, n1, n2 and rp, where rp is the range
	// reduced argument and n1 and n2 are integers so that:
	//
	// exp( x ) = 2^n1 * 2^(n2/32) * exp( rp )
	//
	// Then instead of exp( rp ) we will calculate a polynomial approximation
	// of P( rp ) := exp( rp ) - 1 - rp. Thus, the above equation is modified:
	//
	// exp( x ) = 2^n1 * 2^(n2/32) * ( 1 + P( rp ) + rp ) 		that is,
	//
	// exp( x ) = 2^n1 * ( 2^(n2/32) + 2^(n2/32) * ( P( rp ) + rp ) )
	//
	// In the next few statements we determine rp, n1 and n2. However, instead
	// of rp, we will have rl and rr representing rp in the extended precision
	// way explained above, rl being the larger part.

	n1 = round( x * LAMBDA );
	n2 = n1 & 31;
	n1 = n1 - n2;
	rl = x - ( n1 + n2 ) * LINV_H;
	rr = 0 - ( n1 + n2 ) * LINV_L;
	n1 = n1 >> 5;
	rp = rl + rr;

	// Now we evaluate the polynom that approximates exp( rp ) - rp - 1.
	// The polynom is only 6th order, so one would assume that it is a
	// rather bad approximation. Not so, the absolute approximation
	// error for the +/- ln(2)/64 range is within +/- 4E-18. The
	// polynom yields values close to 0, so the above absolute error
	// corresponds to a rather large relative error (the peak relative
	// error is around 3E-4, where the polynom yields 1.7E-16).
	// That would be unacceptable. However, we will add 1 to the polynom,
	// which will scale the final relative error to the same range as the
	// absolute error, that is, below the resolution of an IEEE-754 double.
	// Since we won't need rp any more, we can use that to store the result
	// of the polynomial approximation. The comments below, when they refer
	// to 'rp' mean the value *before* the evaluation (since it is still
	// available as the sum of rl and rr). The 0th and 1st coefficients of
	// the polynom are 0, so we don't pass them to the evaluation routine
	// but multiply with x^2 locally.

	rp = polynom( rp, poly, 4 ) * rp * rp;

	// We now add rp, so we have exp( rp ) - 1. However, since the result
	// of the polynom can be much smaller than rp itself, we do the addition
	// in two steps, first adding the lower part of the extended precision
	// representation of rp and then the higher part.

	rp += rr;
	rp += rl;

	// Now we have to work out the S = 2^(n2/32) coefficient.
	// Actually not much to work out, since it is precalculated and
	// stored in a table. However, the numbers are stored in the
	// extended precision format. The variables rl and rr will represent
	// the left (higher) and right (lower) parts.

	rl = pows[ n2 ][ 0 ];
	rr = pows[ n2 ][ 1 ];

	// Now we have to calculate the ( S + S * exp( rp ) ) bit.
	// We do the addition in two steps, adding the small part of S
	// first the large part second.

	rp *= rl + rr;
	rp += rr;
	rp += rl;

	// Finally, we have to multiply the current result with 2^n1.

	return( ldexp( rp, n1 ) );
}

/* @} */

