/*                        Copyright (c) 1988 Bellcore
**                            All Rights Reserved
**       Permission is granted to copy or use this program, EXCEPT that it
**       may not be sold for profit, the copyright notice must be reproduced
**       on copies, and credit should be given to Bellcore where it is due.
**       BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
*/


#ifndef lint
static char rcsid[]= "$Header: float.c,v 1.1 88/09/15 11:33:53 daniel Rel $";
#endif

#include <ctype.h>
#include "misc.h"
#include "floatrep.h"
#include "float.h"
#include "strings.h"

#define _F_GETEND(x)	(x + (strlen(x)-1)) 

/*
int floatcnt = 0;
*/
/*
**	routines to convert strings to our internal floating point form
**		isfloat just looks at the string
**		to see if a conversion is reasonable
**			it does look-ahead on when it sees an 'e' and such.
**		atocf actually does the conversion.
**	these two routines could probably be combined
*/

/*
**	test to see if the string can reasonably
**		be interpreted as floating point number
**	returns 0 if string can't be interpreted as a float
**	otherwise returns the number of digits that will be used in F_atof
*/
F_isfloat(str,need_decimal,allow_sign)
char *str;
int need_decimal;	/* if non-zero, require that a decimal point be present
				otherwise, accept strings like "123" */
int allow_sign;		/* if non-zero, allow + or - to set the sign */
{
	int man_length = 0;	/* length of the fractional part (mantissa) */
	int exp_length = 0;	/* length of the exponential part */
	int got_a_digit = 0;	/* flag to set if we ever see a digit */

	/*
	**	look for an optional leading sign marker
	*/
	if (allow_sign && ('+' == *str  || '-' == *str))
	{
		str++; man_length++;
	}
	/*
	**	count up the digits on the left hand side
	**		 of the decimal point
	*/
	while(isdigit(*str))
	{
		got_a_digit = 1;
		str++; man_length++;
	}

	/*
	**	check for a decimal point
	*/
	if ('.' == *str)
	{
		str++; man_length++;
	}
	else
	{
		if (need_decimal)
		{
			return(0);
		}
	}

	/*
	**	collect the digits on the right hand
	**		side of the decimal point
	*/
	while(isdigit(*str))
	{
		got_a_digit = 1;
		str++; man_length++;
	}

	if (!got_a_digit)
		return(0);

	/*
	**	now look ahead for an exponent
	*/
	if ('e' == *str ||
	    'E' == *str ||
	    'd' == *str ||
	    'D' == *str)
	{
		str++; exp_length++;
		if ('+' == *str  || '-' == *str)
		{
			str++; exp_length++;
		}

		if (!isdigit(*str))
		{
			/*
			**	look ahead went too far,
			**	so return just the length of the mantissa
			*/
			return(man_length);
		}

		while (isdigit(*str))
		{
			str++; exp_length++;
		}
	}
	return(man_length+exp_length);	/* return the total length */
}

/*
**	routine to convert a string to our internal
**	floating point representation
**
**		similar to atof()
*/
F_float
F_atof(str,allflag)
char *str;
int allflag;	/* require that exactly all the characters are used */
{
	char *beg = str; /* place holder for beginning of the string */ 
	char man[R_MANMAX];	/* temporary location to build the mantissa */
	int length = 0;	/* length of the mantissa so far */
	int got_a_digit = 0;	/* flag to set if we get a non-zero digit */ 
	int i;
	int resexp;

	F_float res;	/* where we build the result */

/*
floatcnt++;
*/
	res = R_makefloat();

	R_setsign(res,R_POSITIVE);

	resexp = 0;
	man[0] = '\0';

	/*
	**	check for leading sign
	*/
	if ('+' == *str)
	{
		/*
		**	sign should already be positive, see above in this
		**		routine, so just skip the plus sign
		*/
		str++;
	}
	else
	{
		if ('-' == *str)
		{
			R_setsign(res,R_NEGATIVE);
			str++;
		}
	}

	/*
	**	skip any leading zeros
	*/
	while('0' == *str)
	{
		str++;
	}

	/*
	**	now snarf up the digits on the left hand side
	**		of the decimal point
	*/
	while(isdigit(*str))
	{
		got_a_digit = 1;
		man[length++] = *str++;
		man[length] = '\0';
		resexp++;
	}

	/*
	**	skip the decimal point if there is one
	*/
	if ('.' == *str)
		str++;

	/*
	**	trim off any leading zeros (on the right hand side)
	**	if there were no digits in front of the decimal point.
	*/

	if (!got_a_digit)
	{
		while('0' == *str)
		{
			str++;
			resexp--;
		}
	}

	/*
	**	now snarf up the digits on the right hand side
	*/
	while(isdigit(*str))
	{
		man[length++] = *str++;
		man[length] = '\0';
	}

	if ('e' == *str ||
            'E' == *str ||
            'd' == *str ||
            'D' == *str )
	{
		str++;
		resexp += atoi(str);
	}

	if (allflag)
	{
		if ('+' == *str ||
		    '-' == *str)
		{
			str++;
		}
		while (isdigit(*str))
		{
			str++;
		}
		if ('\0' != *str)
		{
			(void) sprintf(Z_err_buf,
					"didn't use up all of %s in atocf",
					beg);
			Z_fatal(Z_err_buf);
		}
	}

	/*
	**	check for special case of all zeros in the mantissa
	*/
	for (i=0;i<length;i++)
	{
		if (man[i] != '0')
		{
			/*
			**	the mantissa is non-zero, so return it unchanged
			*/
			S_trimzeros(man);
			/*
			**	save a copy of the mantissa
			*/
			R_setfrac(res,man);
			R_setexp(res,resexp);
			return(res);
		}
	}

	/*
	**	the answer is 0, so . . .
	*/
	R_setzero(res);
	return(res);
}


/*
**	add s2 to s1
*/
static
void
_F_stradd(s1,s2)
char *s1,*s2;
{
	char *end1 = s1 + (strlen(s1)-1);
	char *end2 = s2 + (strlen(s2)-1);

	static char result[R_MANMAX];
	char *resptr = result+(R_MANMAX-1); /*point to the end of the array */
	int carry = 0;
	int tmp,val1,val2;

	*resptr-- = '\0';

	while ((end1 >= s1) ||  ( end2 >= s2))
	{
		if (end1 >= s1)
		{
			val1 = *end1 - '0';
			--end1;
		}
		else
		{
			val1 = 0;
		}

		if (end2 >= s2)
		{
			val2 = *end2 - '0';
			--end2;
		}
		else
		{
			val2 = 0;
		}

		tmp = val1 + val2 + carry;
		if (tmp > 9)
		{
			carry = 1;
			tmp -= 10;
		}
		else
		{
			carry = 0;
		}

		*resptr-- = tmp+'0';
	}
	if (carry)
	{
		*resptr =  '1';
	}
	else
	{
		resptr++;
	}
	(void) strcpy(s1,resptr);
	return;
}

/*
**	add zero(s) onto the end of a string
*/
static void
addzeros(ptr,count)
char *ptr;
int count;
{
	for(;count> 0;count--)
	{
		(void) strcat(ptr,"0");
	}
	return;
}

/*
**	subtract two mantissa strings
*/
F_float
F_floatsub(p1,p2)
F_float  p1,p2;
{
	static F_float result;
	static needinit = 1;
	static char man1[R_MANMAX],man2[R_MANMAX],diff[R_MANMAX];
	int exp1,exp2;
	char *diffptr,*big,*small;
	int man_cmp_val,i,borrow;

	if (needinit)
	{
		result = R_makefloat();
		needinit = 0;
	}

	man1[0] = '\0';
	man2[0] = '\0';

	exp1 = R_getexp(p1);
	exp2 = R_getexp(p2);

	/*
	**	line up the mantissas
	*/
	while (exp1 < exp2)
	{
		(void) strcat(man1,"0");
		exp1++;
	}

	while(exp1 > exp2)
	{
		(void) strcat(man2,"0");
		exp2++;
	}

	if (exp1 != exp2)	/* boiler plate assertion */
	{
		Z_fatal("mantissas didn't get lined up properly in floatsub");
	}

	(void) strcat(man1,R_getfrac(p1));
	(void) strcat(man2,R_getfrac(p2));
	
	/*
	**	now that the mantissa are aligned,
	**	if the strings are the same, return 0
	*/
	if((man_cmp_val = strcmp(man1,man2)) == 0)
	{
		R_setzero(result);
		return(result);
	}

	/*
	**	pad the shorter string with 0's
	**		when this loop finishes, both mantissas should
	**		have the same length
	*/
	if (strlen(man1)> strlen(man2))
	{
		addzeros(man2,strlen(man1)-strlen(man2));
	}
	else
	{
		if (strlen(man1)<strlen(man2))
		{
			addzeros(man1,strlen(man2)-strlen(man1));
		}
	}

	if (strlen(man1) != strlen(man2))	/* pure boilerplate */
	{
		Z_fatal("lengths not equal in F_floatsub");
	}

	if (man_cmp_val < 0)
	{
		big = man2;
		small = man1;
	}
	else
	{
		big = man1;
		small = man2;
	}

	/*
	**	find the difference between the mantissas
	*/
	for(i=(strlen(big)-1),borrow=0,diff[strlen(big)] = '\0';i>=0;i--)
	{
		char from;
		if (borrow)
		{
			if (big[i] == '0')
			{
				from = '9';
			}
			else
			{
				from = big[i]-1;
				borrow = 0;
			}
		}
		else
		{
			if(big[i]<small[i])
			{
				from = '9'+1;
				borrow = 1;
			}
			else
			{
				from = big[i];
			}
		}
		diff[i] = (from-small[i]) + '0';
	}

	/*
	** trim the leading zeros on the difference
	*/
	diffptr = diff;
	while('0' == *diffptr)
	{
		diffptr++;
		exp1--;
	}

	R_setexp(result,exp1); /* exponents are equal at the point */
	R_setfrac(result,diffptr);
	R_setsign(result,R_POSITIVE);
	return(result);
}

F_floatcmp(f1,f2)
F_float f1,f2;
{
	static char man1[R_MANMAX],man2[R_MANMAX];

	/*
	**		special case for zero
	*/
	if (R_zerofloat(f1))
	{
		if (R_zerofloat(f2))
		{
			return(0);
		}
		else
		{
			return(-1);
		}
	}
	else
	{
		if (R_zerofloat(f2))
		{
			return(1);
		}
	}

	/*
	**	to reach this point, both numbers must be non zeros
	*/
	if (R_getexp(f1) < R_getexp(f2))
	{
		return(-1);
	}

	if (R_getexp(f1) > R_getexp(f2))
	{
		return(1);
	}

	(void) strcpy(man1,R_getfrac(f1));
	S_trimzeros(man1);

	(void) strcpy(man2,R_getfrac(f2));
	S_trimzeros(man2);
	return(strcmp(man1,man2));
}

F_float
F_floatmul(f1,f2)
F_float f1,f2;
{
	static char prod[R_MANMAX];
	char *end;
	int count1 = 0;
	int count2 = 0;
	int tmp,len;
	char *end1;
	char *end2;
	static char man1[R_MANMAX],man2[R_MANMAX];
	char *bigman,*smallman;
	static F_float result;
	static int needinit = 1;

	if (needinit)
	{
		result = R_makefloat();
		needinit = 0;
	}
	/*
	**	special case for a zero result
	*/
	if (R_zerofloat(f1) || R_zerofloat(f2))
	{
		R_setzero(result);
		return(result);
	}

	(void) strcpy(man1,R_getfrac(f1));
	(void) strcpy(man2,R_getfrac(f2));

	end1 = _F_GETEND(man1);
	end2 = _F_GETEND(man2);

	/*
	**	decide which number will cause multiplication loop to go
	**	around the least
	*/
	while(end1 >= man1)
	{
		count1 += *end1 - '0';
		end1--;
	}

	while(end2 >= man2)
	{
		count2 += *end2 - '0';
		end2--;
	}


	if (count1 > count2)
	{
		bigman = man1;
		smallman = man2;
	}
	else
	{
		bigman = man2;
		smallman = man1;
	}
	S_trimzeros(bigman);
	S_trimzeros(smallman);
	len = strlen(bigman) +  strlen(smallman);

	end = _F_GETEND(smallman);
	(void) strcpy(prod,"0");

	/*
	**	multiplication by repeated addition
	*/
	while(end >= smallman)
	{
		for(tmp = 0;tmp<*end-'0';tmp++)
		{
			_F_stradd(prod,bigman);
		}
		addzeros(bigman,1);
		end--;
	}

	R_setfrac(result,prod);
	R_setexp(result,(((R_getexp(f1) + R_getexp(f2)) - len)+ strlen(prod)));

	if (R_getsign(f1) == R_getsign(f2))
	{
		R_setsign(result,R_POSITIVE);
	}
	else
	{
		R_setsign(result,R_NEGATIVE);
	}
	return(result);
}

_F_xor(x,y)
{
	return(((x) && !(y)) || (!(x) && (y)));
}
#define	_F_SAMESIGN(x,y)	_F_xor((x<0),(y<0))
#define _F_ABSADD(x,y)		(Z_ABS(x) + Z_ABS(y))

_F_ABSDIFF(x,y)
{
	if (Z_ABS(x) < Z_ABS(y))
	{
		return(Z_ABS(y) - Z_ABS(x));
	}
	else
	{
		return(Z_ABS(x) - Z_ABS(y));
	}
}
/*
**	add two floats without regard to sign
*/
F_float
F_floatmagadd(p1,p2)
F_float p1,p2;
{
	static F_float result;
	static int needinit = 1;

	static char  man1[R_MANMAX],man2[R_MANMAX];

	int digits;	/* count of the number of digits needed to represent the
				result */
	int resexp;	/* exponent of the result */
	int len;	/* length of the elements before adding */
	char *diffptr;

	if (needinit)
	{
		result = R_makefloat();
		needinit = 0;
	}
	(void) strcpy(man1,"");
	(void) strcpy(man2,"");

	/*
	**	find the difference in the exponents number of digits
	*/
	if( _F_SAMESIGN(R_getexp(p1),R_getexp(p2)))
	{
		digits =  _F_ABSDIFF(R_getexp(p1),R_getexp(p2));
	}
	else
	{
		digits = _F_ABSADD(R_getexp(p1),R_getexp(p2));
	}

	/*
	**	make sure that there is room to store the result
	*/
	if (digits>0)
	{ 
		if (R_getexp(p1) < R_getexp(p2))
		{
			/*
			**	leave room for terminator
			*/
			if (digits+strlen(R_getfrac(p1)) > (R_MANMAX-1))
			{
				(void) sprintf(Z_err_buf,
				   "numbers differ by too much in magnitude");
				Z_fatal(Z_err_buf);
			}
		}
		else
		{
			/*
			**	leave room for terminator
			*/
			if (digits+strlen(R_getfrac(p2)) > (R_MANMAX-1))
			{
				(void) sprintf(Z_err_buf,
				   "numbers differ by too much in magnitude");
				Z_fatal(Z_err_buf);
			}
		}
	}
	else
	{
		/*
		**	leave room for terminator and possible carry
		*/
		if (Z_MAX(strlen(R_getfrac(p1)),
			strlen(R_getfrac(p2))) > (R_MANMAX-2))
		{						
			(void) sprintf(Z_err_buf,
			   "numbers differ by too much in magnitude");
			Z_fatal(Z_err_buf);
		}
	}

	/*
	**	pad zeroes on the front of the smaller number
	*/
	if (R_getexp(p1) < R_getexp(p2))
	{

		addzeros(man1,digits);
		resexp = R_getexp(p2);
	}
	else
	{
		addzeros(man2,digits);
		resexp = R_getexp(p1);
	}
	(void) strcat(man1,R_getfrac(p1));
	(void) strcat(man2,R_getfrac(p2));

	len = Z_MAX(strlen(man1),strlen(man2));

	/*
	**	add the two values
	*/
	_F_stradd(man1,man2);

	/*
	**	adjust the exponent to account for a
	**		possible carry
	*/
	resexp += strlen(man1) - len;


	/*
	** trim the leading zeros on the sum
	*/
	diffptr = man1;
	while('0' == *diffptr)
	{
		diffptr++;
		resexp--;
	}

	R_setfrac(result,diffptr);
	R_setexp(result,resexp);
	R_setsign(result,R_POSITIVE);

	return(result);
}

/*
**	useful debugging routine. we don't call it in the release,
**	so it is commented out, but we'll leave it for future use
*/

/*
F_printfloat(fl)
F_float fl;
{
	(void) printf("fraction = :%s: exp = %d sign = %c\n",
			R_getfrac(fl),
			R_getexp(fl),
			((R_getsign(fl) == R_POSITIVE) ? '+': '-'));

}
*/
