/* int	_ExpDec80Bit(pDst,pSrc)
 *
 * ARGUMENT
 *	DEC	*pDst, *pSrc;
 *
 * DESCRIPTION
 *	Calculates e^pSrc, stores result in pDst.
 *
 * SIDE	EFFECTS
 *	None.
 *
 * RETURNS
 *	GM_SUCCESS if calculation is successful,
 *   otherwise the error code (which could be underflow)
 *
 * ALGORITHM
 *	pSrc = a * ln 2	+ b			|b| < (ln 2) / 2
 *	e^pSrc = (2^a) * (e^b)
 *  Using the Taylor series,
 *	e^b = 1	+ b + (b^2)/2! + (b^3)/3! + ...
 *
 * AUTHOR
 *  Jared Levy		April 7, 1987
 *   Copyright (C) 1987-1990 Greenleaf Software	Inc.  All rights reserved.
 *
 * MODIFICATIONS
 *
 */

#include <stdio.h>
#include "gm.h"
#include "gmsystem.h"

int	_ExpDec80Bit(pDst,pSrc)
DEC	*pDst, *pSrc;
{
	int	a, i;
	DEC	da, dpow2, db, dt, dfact, dterm;
	DEC	*pow2, *b, *fact, *pt, *term;

	if (CompareDecimal(pSrc,&decMaxExp) == 1)  {
		return(GM_OVERFLOW);
		}

	if (CompareDecimal(pSrc,&decMinExp) == -1) {
		_MacDZero(pDst);
		return(GM_UNDERFLOW);
		}

	/* calculate a = pSrc *	(1/(ln 2))  */
	(void) _MulDec80Bit(&da, pSrc, &decReciprocalOfLn2);

	(void) _ScaleDec80Bit(&da,&da,0);
	a = ConvDecimalToInt(&da);

	/* calculate b = pSrc -	a * ln 2  */
	b=&db;
	if (a == 0)  {
		_MacDCopy(b,pSrc);
	}
	else  {
		(void) _MulDec80Bit(&dt, &da, &decLn2);
		(void) _SubDec80Bit(b, pSrc, &dt);
	}

	/* calculate pSrc = (2^a) * (e^b)  */
	fact = &dfact;
	pt = &decOne;
	term = &dterm;
	_MacDCopy(fact,pt);
	_MacDCopy(pDst,pt);
	_AddDec80Bit(pDst, pDst, b);
	_MacDCopy(term,	b);

	do {
			/* calulate next term */
		fact->dc.sl[0]++;
		(void) _MulDec80Bit(term, b, term);
		(void) _DivRndDec80Bit(term, term, fact, 23);
			/* add term to series */
		(void) _AddDec80Bit(pDst, pDst,	term);
		/* calc	next factorial term in divisor */
		} while	(!(_MacIsDecZ(term)));

	if (a>0)  {
		pow2 = &dpow2;
/*
 * I used to use the _MacDZero statement here, but Lattice C 6.0
 * pointed out that it had a single redundant line in the
 * macro, so I expanded	it and remove the redundant line.
 *		_MacDZero(pow2);
 */
		pow2->dc.attr=0;
		pow2->ls.lsl[0]=0;
		pow2->ls.lsl[1]=0;
		pow2->dc.msd=0;

		pow2->dc.id = 0;
		pow2->dc.sl[a/16] = 1 << (a % 16);
		i =_MulDec80Bit(pDst, pDst, pow2);
		return(i);
		}

	while (a<0)  {
		_HalveUnsArr(pDst->dc.sl, 5);
		a++;
		}

	if (_MacIsDecZ(pDst))
		return(GM_UNDERFLOW);

	return(GM_SUCCESS);
}