/**
 * This file has no copyright assigned and is placed in the Public Domain.
 * This file is part of the mingw-w64 runtime package.
 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
 */

#include "math/cephes_emath.h"

#if NE == 10
/* 1.0E0 */
static const unsigned short __eone[NE] = {
  0x0000, 0x0000, 0x0000, 0x0000,
  0x0000, 0x0000, 0x0000, 0x0000, 0x8000, 0x3fff,
};
#else
static const unsigned short __eone[NE] = {
  0, 0000000,0000000,0000000,0100000,0x3fff,
};
#endif

#if NE == 10
static const unsigned short __etens[NTEN + 1][NE] =
{
  {0x6576, 0x4a92, 0x804a, 0x153f,
   0xc94c, 0x979a, 0x8a20, 0x5202, 0xc460, 0x7525,},	/* 10**4096 */
  {0x6a32, 0xce52, 0x329a, 0x28ce,
   0xa74d, 0x5de4, 0xc53d, 0x3b5d, 0x9e8b, 0x5a92,},	/* 10**2048 */
  {0x526c, 0x50ce, 0xf18b, 0x3d28,
   0x650d, 0x0c17, 0x8175, 0x7586, 0xc976, 0x4d48,},
  {0x9c66, 0x58f8, 0xbc50, 0x5c54,
   0xcc65, 0x91c6, 0xa60e, 0xa0ae, 0xe319, 0x46a3,},
  {0x851e, 0xeab7, 0x98fe, 0x901b,
   0xddbb, 0xde8d, 0x9df9, 0xebfb, 0xaa7e, 0x4351,},
  {0x0235, 0x0137, 0x36b1, 0x336c,
   0xc66f, 0x8cdf, 0x80e9, 0x47c9, 0x93ba, 0x41a8,},
  {0x50f8, 0x25fb, 0xc76b, 0x6b71,
   0x3cbf, 0xa6d5, 0xffcf, 0x1f49, 0xc278, 0x40d3,},
  {0x0000, 0x0000, 0x0000, 0x0000,
   0xf020, 0xb59d, 0x2b70, 0xada8, 0x9dc5, 0x4069,},
  {0x0000, 0x0000, 0x0000, 0x0000,
   0x0000, 0x0000, 0x0400, 0xc9bf, 0x8e1b, 0x4034,},
  {0x0000, 0x0000, 0x0000, 0x0000,
   0x0000, 0x0000, 0x0000, 0x2000, 0xbebc, 0x4019,},
  {0x0000, 0x0000, 0x0000, 0x0000,
   0x0000, 0x0000, 0x0000, 0x0000, 0x9c40, 0x400c,},
  {0x0000, 0x0000, 0x0000, 0x0000,
   0x0000, 0x0000, 0x0000, 0x0000, 0xc800, 0x4005,},
  {0x0000, 0x0000, 0x0000, 0x0000,
   0x0000, 0x0000, 0x0000, 0x0000, 0xa000, 0x4002,},	/* 10**1 */
};
#else
static const unsigned short __etens[NTEN+1][NE] = {
  {0xc94c,0x979a,0x8a20,0x5202,0xc460,0x7525,},/* 10**4096 */
  {0xa74d,0x5de4,0xc53d,0x3b5d,0x9e8b,0x5a92,},/* 10**2048 */
  {0x650d,0x0c17,0x8175,0x7586,0xc976,0x4d48,},
  {0xcc65,0x91c6,0xa60e,0xa0ae,0xe319,0x46a3,},
  {0xddbc,0xde8d,0x9df9,0xebfb,0xaa7e,0x4351,},
  {0xc66f,0x8cdf,0x80e9,0x47c9,0x93ba,0x41a8,},
  {0x3cbf,0xa6d5,0xffcf,0x1f49,0xc278,0x40d3,},
  {0xf020,0xb59d,0x2b70,0xada8,0x9dc5,0x4069,},
  {0x0000,0x0000,0x0400,0xc9bf,0x8e1b,0x4034,},
  {0x0000,0x0000,0x0000,0x2000,0xbebc,0x4019,},
  {0x0000,0x0000,0x0000,0x0000,0x9c40,0x400c,},
  {0x0000,0x0000,0x0000,0x0000,0xc800,0x4005,},
  {0x0000,0x0000,0x0000,0x0000,0xa000,0x4002,}, /* 10**1 */
};
#endif

int __asctoe64(const char * __restrict__ ss, short unsigned int * __restrict__ y)
{
  unsigned short yy[NI], xt[NI], tt[NI];
  int esign, decflg, nexp, expo, lost;
  int k, c;
  int valid_lead_string = 0;
  int have_non_zero_mant = 0;
  int prec = 0;
  /* int trail = 0; */
  int lexp;
  unsigned short nsign = 0;
  const unsigned short *p;
  char *sp,  *lstr;
  char *s;

  const char dec_sym = *(localeconv ()->decimal_point); 

  int lenldstr = 0;

  /* Copy the input string. */
  c = strlen (ss) + 2;
  lstr = (char *) malloc (c);
  s = (char *) ss;
  while( isspace ((int)(unsigned char)*s)) /* skip leading spaces */
    {
      ++s;
      ++lenldstr;
    }
  sp = lstr;
  for (k = 0; k < c; k++)
    {
      if ((*sp++ = *s++) == '\0')
	break;
    }
  *sp = '\0';
  s = lstr;

  if (*s == '-')
    {
      nsign = 0xffff;
      ++s;
    }
  else if (*s == '+')
    {
     ++s;
    }

  if (_strnicmp("INF", s , 3) == 0)
    {
      valid_lead_string = 1;
      s += 3;
      if ( _strnicmp ("INITY", s, 5) == 0)
	s += 5;
      __ecleaz(yy);
      yy[E] = 0x7fff;  /* infinity */
      goto aexit;
    }
  else if(_strnicmp ("NAN", s, 3) == 0)
    {
      valid_lead_string = 1;
      s += 3;
      __enan_NI16( yy );
      goto aexit;
    }

  /* FIXME: Handle case of strtold ("NAN(n_char_seq)",endptr)  */ 

  /*  Now get some digits.  */
  lost = 0;
  decflg = 0;
  nexp = 0;
  expo = 0;
  __ecleaz( yy );

  /* Ignore leading zeros */
  while (*s == '0')
    {
      valid_lead_string = 1;
      s++;
    }

nxtcom:

  k = *s - '0';
  if ((k >= 0) && (k <= 9))
    {
#if 0
/* The use of a special char as a flag for trailing zeroes causes problems when input
   actually contains the char  */
/* Identify and strip trailing zeros after the decimal point. */
      if ((trail == 0) && (decflg != 0))
	{
	  sp = s;
	  while ((*sp >= '0') && (*sp <= '9'))
	    ++sp;
	  --sp;
	  while (*sp == '0')
	    {
	      *sp-- = (char)-1;
	      trail++;
	    }
	  if( *s == (char)-1 )
	    goto donchr;
	}
#endif

/* If enough digits were given to more than fill up the yy register,
 * continuing until overflow into the high guard word yy[2]
 * guarantees that there will be a roundoff bit at the top
 * of the low guard word after normalization.
 */
      if (yy[2] == 0)
	{
	  if( decflg )
	    nexp += 1; /* count digits after decimal point */
	  __eshup1( yy );	/* multiply current number by 10 */
	  __emovz( yy, xt );
	  __eshup1( xt );
	  __eshup1( xt );
	  __eaddm( xt, yy );
	  __ecleaz( xt );
	  xt[NI-2] = (unsigned short )k;
	  __eaddm( xt, yy );
	}
      else
	{
	  /* Mark any lost non-zero digit.  */
	  lost |= k;
	  /* Count lost digits before the decimal point.  */
	  if (decflg == 0)
	    nexp -= 1;
	}
      have_non_zero_mant |= k;
      prec ++;
      /* goto donchr; */
    }
  else if (*s == dec_sym)
    {
      if( decflg )
        goto daldone;
      ++decflg;
    }
  else if ((*s == 'E') || (*s == 'e') )
    {
      if (prec || valid_lead_string)
	goto expnt;
      else
	goto daldone;
    }
#if 0
  else if (*s == (char)-1)
    goto donchr;
#endif
  else  /* an invalid char */
    goto daldone;

  /* donchr: */
  ++s;
  goto nxtcom;

/* Exponent interpretation */
expnt:

  esign = 1;
  expo = 0;
  /* Save position in case we need to fall back.  */
  sp = s;
  ++s;
  /* check for + or - */
  if (*s == '-')
    {
      esign = -1;
      ++s;
    }
  if (*s == '+')
    ++s;

  /* Check for valid exponent.  */
  if (!(*s >= '0' && *s <= '9'))
    {
      s = sp;
      goto daldone;
    }

  while ((*s >= '0') && (*s <= '9'))
    {
    /* Stop modifying exp if we are going to overflow anyway,
       but keep parsing the string. */	
      if (expo < 4978)
	{
	  expo *= 10;
	  expo += *s - '0';
	}
      s++;
    }

  if (esign < 0)
    expo = -expo;

  if (expo > 4977) /* maybe overflow */
    {
      __ecleaz(yy);
      if (have_non_zero_mant)
	yy[E] = 0x7fff;
      goto aexit;
    }
  else if (expo < -4977) /* underflow */
    {
      __ecleaz(yy);
      goto aexit;
    }

daldone:

  nexp = expo - nexp;

  /* Pad trailing zeros to minimize power of 10, per IEEE spec. */
  while ((nexp > 0) && (yy[2] == 0))
    {
      __emovz( yy, xt );
      __eshup1( xt );
      __eshup1( xt );
      __eaddm( yy, xt );
      __eshup1( xt );
      if (xt[2] != 0)
	break;
      nexp -= 1;
      __emovz( xt, yy );
    }
  if ((k = __enormlz(yy)) > NBITS)
    {
      __ecleaz(yy);
      goto aexit;
    }
  lexp = (EXONE - 1 + NBITS) - k;
  __emdnorm( yy, lost, 0, lexp, 64, NBITS );
  /* convert to external format */

  /* Multiply by 10**nexp.  If precision is 64 bits,
   * the maximum relative error incurred in forming 10**n
   * for 0 <= n <= 324 is 8.2e-20, at 10**180.
   * For 0 <= n <= 999, the peak relative error is 1.4e-19 at 10**947.
   * For 0 >= n >= -999, it is -1.55e-19 at 10**-435.
   */
  lexp = yy[E];
  if (nexp == 0)
    {
      k = 0;
      goto expdon;
    }
  esign = 1;
  if (nexp < 0)
    {
      nexp = -nexp;
      esign = -1;
      if (nexp > 4096)
	{ /* Punt.  Can't handle this without 2 divides. */
	  __emovi( __etens[0], tt );
	  lexp -= tt[E];
	  k = __edivm( tt, yy );
	  lexp += EXONE;
	  nexp -= 4096;
	}
    }
  p = &__etens[NTEN][0];
  __emov( __eone, xt );
  expo = 1;
  do
    {
      if (expo & nexp)
	__emul( p, xt, xt );
      p -= NE;
      expo = expo + expo;
    }
  while (expo <= MAXP);

  __emovi( xt, tt );
  if (esign < 0)
    {
      lexp -= tt[E];
      k = __edivm( tt, yy );
      lexp += EXONE;
    }
  else
    {
      lexp += tt[E];
      k = __emulm( tt, yy );
      lexp -= EXONE - 1;
    }

expdon:

  /* Round and convert directly to the destination type */

  __emdnorm( yy, k, 0, lexp, 64, 64 );

aexit:

  yy[0] = nsign;

  __toe64( yy, y );

  /* Check for overflow, undeflow  */
  if (have_non_zero_mant &&
      (*((long double*) y) == 0.0L || isinf (*((long double*) y)))) 
    errno = ERANGE;

  if (prec || valid_lead_string)
    return (lenldstr + (s - lstr));
  return 0;
}


long double strtold (const char * __restrict__ s, char ** __restrict__ se)
{
  int lenldstr;
  union
  {
    unsigned short int us[6];
    long double ld;
  } xx = {{0}};

  lenldstr =  __asctoe64( s, xx.us);
  if (se)
    *se = (char*)s + lenldstr;

  return xx.ld;
}

