/*
 *     double strtod (str, endptr);
 *     const char *str;
 *     char **endptr;
 *		if !NULL, on return, points to char in str where conv. stopped
 *
 *     double atof (str)
 *     const char *str;
 *
 * recognizes:
 [spaces] [sign] digits [ [.] [ [digits] [ [e|E|d|D] [space|sign] [int][F|L]]]]
 *
 * returns:
 *	the number
 *		on overflow: HUGE_VAL and errno = ERANGE
 *		on underflow: -HUGE_VAL and errno = ERANGE
 *
 * bugs:
 *   naive over/underflow detection
 *
 *	++jrb bammi@dsrgsun.ces.cwru.edu
 *
 * 07/29/89:
 *	ok, before you beat me over the head, here is a hopefully
 *	much improved one, with proper regard for rounding/precision etc
 *	(had to read up on everything i did'nt want to know in K. V2)
 *	the old naive coding is at the end bracketed by ifdef __OLD__.
 *	modeled after peter housels posting on comp.os.minix.
 *	thanks peter!
 *		++jrb
 */
#if !(defined(unix) || defined(minix))
#include <stddef.h>
#include <stdlib.h>
#include <float.h>
#endif
#include <errno.h>
#include <assert.h>
#include <math.h>

#ifdef minix
#include "lib.h"
#endif

#ifndef _COMPILER_H
#include <compiler.h>
#endif

extern int errno;
#if (defined(unix) || defined(minix))
#define NULL 	((void *)0)
#endif

#define Ise(c)		((c == 'e') || (c == 'E') || (c == 'd') || (c == 'D'))
#define Isdigit(c)	((c <= '9') && (c >= '0'))
#define Isspace(c)	((c == ' ') || (c == '\t'))
#define Issign(c)	((c == '-') || (c == '+'))
#define IsValidTrail(c) ((c == 'F') || (c == 'L'))
#define Val(c)		((c - '0'))

#define MAXDOUBLE	DBL_MAX
#define MINDOUBLE	DBL_MIN

#define MAXF  1.797693134862316
#define MINF  2.225073858507201
#define MAXE  308
#define MINE  (-308)

/* another alias for ieee double */
struct ldouble {
    unsigned long hi, lo;
};

static int __ten_mul __PROTO((double *acc, int digit));
static double __adjust __PROTO((double *acc, int dexp, int sign));
static double __ten_pow __PROTO((double r, int e));

/*
 * mul 64 bit accumulator by 10 and add digit
 * algorithm:
 *	10x = 2( 4x + x ) == ( x<<2 + x) << 1
 *	result = 10x + digit
 */
static int __ten_mul(acc, digit)
double *acc;
int digit;
{
    register unsigned long d0, d1, d2, d3;
    register          short i;
    
    d2 = d0 = ((struct ldouble *)acc)->hi;
    d3 = d1 = ((struct ldouble *)acc)->lo;

    /* check possibility of overflow */
/*    if( (d0 & 0x0FFF0000L) >= 0x0ccc0000L ) */
/*    if( (d0 & 0x70000000L) != 0 ) */
    if( (d0 & 0xF0000000L) != 0 )
	/* report overflow somehow */
	return 1;
    
    /* 10acc == 2(4acc + acc) */
    for(i = 0; i < 2; i++)
    {  /* 4acc == ((acc) << 2) */
	asm volatile("	lsll	#1,%1;
 			roxll	#1,%0"	/* shift L 64 bit acc 1bit */
	    : "=d" (d0), "=d" (d1)
	    : "0"  (d0), "1"  (d1) );
    }

    /* 4acc + acc */
    asm volatile(" addl    %2,%0" : "=d" (d1) : "0" (d1), "d" (d3));
    asm volatile(" addxl   %2,%0" : "=d" (d0) : "0" (d0), "d" (d2));

    /* (4acc + acc) << 1 */
    asm volatile("  lsll    #1,%1;
 		    roxll   #1,%0"	/* shift L 64 bit acc 1bit */
	    : "=d" (d0), "=d" (d1)
	    : "0"  (d0), "1"  (d1) );

    /* add in digit */
    d2 = 0;
    d3 = digit;
    asm volatile(" addl    %2,%0" : "=d" (d1) : "0" (d1), "d" (d3));
    asm volatile(" addxl   %2,%0" : "=d" (d0) : "0" (d0), "d" (d2));


    /* stuff result back into acc */
    ((struct ldouble *)acc)->hi = d0;
    ((struct ldouble *)acc)->lo = d1;

    return 0;	/* no overflow */
}

#include "flonum.h"

static double __adjust(acc, dexp, sign)
double *acc;	/* the 64 bit accumulator */
int     dexp;	/* decimal exponent       */
int	sign;	/* sign flag		  */
{
    register unsigned long  d0, d1, d2, d3;
    register          short i;
    register 	      short bexp = 0; /* binary exponent */
    unsigned short tmp[4];
    double r;

#ifdef __STDC__
    double __normdf( double d, int exp, int sign, int rbits );
    double ldexp(double d, int exp);
#else
    extern double __normdf();
    extern double ldexp();
#endif    
    d0 = ((struct ldouble *)acc)->hi;
    d1 = ((struct ldouble *)acc)->lo;
    while(dexp != 0)
    {	/* something to do */
	if(dexp > 0)
	{ /* need to scale up by mul */
	    while(d0 > 429496729 ) /* 2**31 / 5 */
	    {	/* possibility of overflow, div/2 */
		asm volatile(" lsrl	#1,%1;
 			       roxrl	#1,%0"
		    : "=d" (d1), "=d" (d0)
		    : "0"  (d1), "1"  (d0));
		bexp++;
	    }
	    /* acc * 10 == 2(4acc + acc) */
	    d2 = d0;
	    d3 = d1;
	    for(i = 0; i < 2; i++)
	    {  /* 4acc == ((acc) << 2) */
		asm volatile("	lsll	#1,%1;
 				roxll	#1,%0"	/* shift L 64 bit acc 1bit */
			     : "=d" (d0), "=d" (d1)
			     : "0"  (d0), "1"  (d1) );
	    }

	    /* 4acc + acc */
	    asm volatile(" addl    %2,%0" : "=d" (d1) : "0" (d1), "d" (d3));
	    asm volatile(" addxl   %2,%0" : "=d" (d0) : "0" (d0), "d" (d2));

	    /* (4acc + acc) << 1 */
	    bexp++;	/* increment exponent to effectively acc * 10 */
	    dexp--;
	}
	else /* (dexp < 0) */
	{ /* scale down by 10 */
	    while((d0 & 0xC0000000L) == 0)
	    {	/* preserve prec by ensuring upper bits are set before div */
		asm volatile(" lsll  #1,%1;
 			       roxll #1,%0" /* shift L to move bits up */
		    : "=d" (d0), "=d" (d1)
		    : "0"  (d0), "1"  (d1) );
		bexp--;	/* compensate for the shift */
	    }
	    /* acc/10 = acc/5/2 */
	    *((unsigned long *)&tmp[0]) = d0;
	    *((unsigned long *)&tmp[2]) = d1;
	    d2 = (unsigned long)tmp[0];
	    asm volatile(" divu #5,%0" : "=d" (d2) : "0" (d2));
	    tmp[0] = (unsigned short)d2;	/* the quotient only */
	    for(i = 1; i < 4; i++)
	    {
		d2 = (d2 & 0xFFFF0000L) | (unsigned long)tmp[i]; /* REM|next */
		asm volatile(" divu #5,%0" : "=d" (d2) : "0" (d2));
		tmp[i] = (unsigned short)d2;
	    }
	    d0 = *((unsigned long *)&tmp[0]);
	    d1 = *((unsigned long *)&tmp[2]);
	    /* acc/2 */
	    bexp--;	/* div/2 taken care of by decrementing binary exp */
	    dexp++;
	}
    }
    
    /* stuff the result back into acc */
    ((struct ldouble *)acc)->hi = d0;
    ((struct ldouble *)acc)->lo = d1;

    /* normalize it */
    r = __normdf( *acc, ((0x03ff - 1) + 53), (sign)? -1 : 0, 0 );
    /* now shove in the binary exponent */
    return ldexp(r, bexp);
}

/* flags */
#define SIGN	0x01
#define ESIGN	0x02
#define DECP	0x04
#define CONVF	0x08

double strtod (s, endptr)
const register char *s;
char **endptr;
{
    double         accum = 0.0;
    register short flags = 0;
    register short texp  = 0;
    register short e     = 0;
/*    const register char *start = s;  */
    double	   zero = 0.0;

    assert ((s != NULL));
    
    if(endptr != NULL) *endptr = (char *)s;
    while(Isspace(*s)) s++;
    if(*s == '\0')
    {	/* just leading spaces */
	return zero;
    }
    
    
    if(Issign(*s))
    {
	if(*s == '-') flags = SIGN;
	if(*++s == '\0')
	{   /* "+|-" : should be an error ? */
	    return zero;
	}
    }
    
    do {
	if (Isdigit(*s))
	{
	    flags |= CONVF;
	    if( __ten_mul(&accum, Val(*s)) ) texp++;
	    if(flags & DECP) texp--;
	}
	else if(*s == '.')
	{
	    if (flags & DECP)  /* second decimal point */
		break;
	    flags |= DECP;
	}
	else
	    break;
	s++;
    } while (1);

    if(Ise(*s))
    {
	if(*++s != '\0') /* skip e|E|d|D */
	{  /* ! ([s]xxx[.[yyy]]e)  */
    
	    while(Isspace(*s)) s++; /* Ansi allows spaces after e */
	    if(*s != '\0')
	    { /*  ! ([s]xxx[.[yyy]]e[space])  */
    
		if(Issign(*s))
		    if(*s++ == '-') flags |= ESIGN;
    
		if(*s != '\0')
		{ /*  ! ([s]xxx[.[yyy]]e[s])  -- error?? */

		    for(; Isdigit(*s); s++)
			e = (((e << 2) + e) << 1) + Val(*s);

		    if(IsValidTrail(*s)) s++;
		    
		    /* dont care what comes after this */
		    if(flags & ESIGN)
			texp -= e;
		    else
			texp += e;
		}
	    }
	}
    }

    if((endptr != NULL) && (flags & CONVF))
	*endptr = (char *) s;
    if(accum == zero) return zero;
    
    return __adjust(&accum, (int)texp, (int)(flags & SIGN));
}

double atof(s)
const char *s;
{
#ifdef __OLD__
    extern double strtod();
#endif
    return strtod(s, (char **)NULL);
}

#ifdef TEST
#ifdef __MSHORT__
#error "please run this test in 32 bit int mode"
#endif

#define NTEST 10000L

#ifdef unix
#ifdef __MSHORT__
#define	RAND_MAX	(0x7FFF)	/* maximum value from rand() */
#else
#define	RAND_MAX	(0x7FFFFFFFL)	/* maximum value from rand() */
#endif
#endif

main()
{
    
    double expected, result, e, max_abs_err;
    char buf[128];
    register long i, errs;
    register int s;
#ifdef __STDC__
    double atof(const char *);
    int rand(void);
#else
    extern double atof();
    extern int rand();
#endif

#if 0
    expected = atof("3.14159265358979e23");
    expected = atof("3.141");
    expected = atof(".31415"); 
    printf("%f\n\n", expected);
    expected = atof("3.1415"); 
    printf("%f\n\n", expected);
    expected = atof("31.415"); 
    printf("%f\n\n", expected);
    expected = atof("314.15"); 
    printf("%f\n\n", expected);

    expected = atof(".31415"); 
    printf("%f\n\n", expected);
    expected = atof(".031415"); 
    printf("%f\n\n", expected);
    expected = atof(".0031415"); 
    printf("%f\n\n", expected);
    expected = atof(".00031415"); 
    printf("%f\n\n", expected);
    expected = atof(".000031415"); 
    printf("%f\n\n", expected);

    expected = atof("-3.1415e-9"); 
    printf("%20.15e\n\n", expected);

    expected = atof("+3.1415e+009"); 
    printf("%20.15e\n\n", expected);
#endif

    expected = atof("+3.123456789123456789"); 
    printf("%30.25e\n\n", expected);

    expected = atof(".000003123456789123456789"); 
    printf("%30.25e\n\n", expected);

    expected = atof("3.1234567891234567890000000000"); 
    printf("%30.25e\n\n", expected);

    expected = atof("9.22337999999999999999999999999999999999999999"); 
    printf("%47.45e\n\n", expected);

    expected = atof("1.0000000000000000000"); 
    printf("%25.19e\n\n", expected);

    expected = atof("1.00000000000000000000"); 
    printf("%26.20e\n\n", expected);

    expected = atof("1.000000000000000000000"); 
    printf("%27.21e\n\n", expected);

    expected = atof("1.000000000000000000000000"); 
    printf("%30.24e\n\n", expected);

 
#if 0
    expected = atof("1.7e+308");
    if(errno != 0)
    {
	printf("%d\n", errno);
    }
    else    printf("1.7e308 OK %g\n", expected);
    expected = atof("1.797693e308");	/* anything gt looses */
    if(errno != 0)
    {
	printf("%d\n", errno);
    }
    else    printf("Max OK %g\n", expected);
    expected = atof("2.225073858507201E-307");
    if(errno != 0)
    {
	printf("%d\n", errno, expected);
    }
    else    printf("Min OK %g\n", expected);
#endif
    
    max_abs_err = 0.0;
    for(errs = 0, i = 0; i < NTEST; i++)
    {
	expected = (double)(s = rand()) / (double)rand();
	if(s > (RAND_MAX >> 1)) expected = -expected;
	sprintf(buf, "%.14e", expected);
	result = atof(buf);
	e = (expected == 0.0) ? result : (result - expected)/expected;
	if(e < 0) e = (-e);
	if(e > 1.0e-6) 
	{
	    errs++; printf("%.14e %s %.14e (%.14e)\n", expected, buf, result, e);
	}
	if (e > max_abs_err) max_abs_err = e;
    }
    printf("%ld Error(s), Max abs err %.14e\n", errs, max_abs_err);
}
#endif /* TEST */

/* old naive coding */
#ifdef __OLD__
static double __ten_pow(r, e)
double r;
register int e;
{
    if(e < 0)
	for(; e < 0; e++) r /= 10.0;
    else
	for(; e > 0; --e) r *= 10.0;
    return r;
}

#define RET(X) 	{goto ret;}

double strtod (s, endptr)
const register char *s;
char **endptr;
{
    double f = 0.1, r = 0.0, accum = 0.0;
    register int  e = 0, esign = 0, sign = 0;
    register int texp = 0, countexp;
    
    assert ((s != NULL));
    
    while(Isspace(*s)) s++;
    if(*s == '\0') RET(r);	/* just spaces */
    
    if(Issign(*s))
    {
	if(*s == '-') sign = 1;
	s++;
	if(*s == '\0') RET(r); /* "+|-" : should be an error ? */
    }
    countexp = 0;
    while(Isdigit(*s))
    {
	if(!countexp && (*s != '0')) countexp = 1;
	accum = (accum * 10.0) + Val(*s);
	/* should check for overflow here somehow */
	s++; 
	if(countexp) texp++;
    }
    r = (sign ? (-accum) : accum);
    if(*s == '\0') RET(r);  /* [s]xxx */
    
    countexp = (texp == 0);
    if(*s == '.')
    {
	s++;
	if(*s == '\0') RET(r); /* [s]xxx. */
	if(!Ise(*s))
	{
	    while(Isdigit(*s))
	    {
		if(countexp && (*s == '0')) --texp;
		if(countexp && (*s != '0')) countexp = 0;
		accum = accum + (Val(*s) * f);
		f = f / 10.0;
		/* overflow (w + f) ? */
		s++;
	    }
	    r = (sign ? (-accum) : accum);
	    if(*s == '\0') RET(r); /* [s]xxx.yyy  */
	}
    }
    if(!Ise(*s)) RET(r);	/* [s]xxx[.[yyy]]  */
    
    s++; /* skip e */
    if(*s == '\0') RET(r); /* [s]xxx[.[yyy]]e  */
    
    while(Isspace(*s)) s++;
    if(*s == '\0') RET(r); /* [s]xxx[.[yyy]]e[space]  */
    
    if(Issign(*s))
    {
	if(*s == '-') esign = 1;
	s++;
	if(*s == '\0') RET(r); /* [s]xxx[.[yyy]]e[s]  */
    }
    
    while(Isdigit(*s))
    {
	e = (e * 10) + Val(*s);
	s++;
    }
    /* dont care what comes after this */
    if(esign) e = (-e);
    texp += e;
    
    /* overflow ? */ /* FIXME */
    if( texp > MAXE)
    {
	if( ! ((texp == (MAXE+1)) && (accum <= MAXF)))
	{
	    errno = ERANGE;
	    r = ((sign) ? -HUGE_VAL : HUGE_VAL);
	    RET(r);
	}
    }
    
    /* underflow -- in reality occurs before this */ /* FIXME */
    if(texp < MINE)
    {
	errno = ERANGE;
	r = ((sign) ? -HUGE_VAL : HUGE_VAL);
	RET(r);
    }
    r = __ten_pow(r, e);
    /* fall through */
    
    /* all returns end up here, with return value in r */
  ret:
    if(endptr != NULL)
	*endptr = s;
    return r;
}
#endif /* __OLD__ */
