/*
  An alternative implementation of "strtod()" that is both
  simplier, and thread-safe.

  From mit-threads as bundled with MySQL 3.22

  SQL:2003 specifies a number as

<signed numeric literal> ::= [ <sign> ] <unsigned numeric literal>

<unsigned numeric literal> ::=
                    <exact numeric literal>
                  | <approximate numeric literal>

<exact numeric literal> ::=
                    <unsigned integer> [ <period> [ <unsigned integer> ] ]
                  | <period> <unsigned integer>

<approximate numeric literal> ::= <mantissa> E <exponent>

<mantissa> ::= <exact numeric literal>

<exponent> ::= <signed integer>

  So do we.

 */

#include "my_base.h"
#include "m_ctype.h"

static double scaler10[] = {
  1.0, 1e10, 1e20, 1e30, 1e40, 1e50, 1e60, 1e70, 1e80, 1e90
};
static double scaler1[] = {
  1.0, 10.0, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9
};

// let's use a static array for not to accumulate the error
static double pastpoint[] = {
  1e-1,  1e-2,  1e-3,  1e-4,  1e-5,  1e-6,  1e-7,  1e-8,  1e-9,
  1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18,  1e-19,
  1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28,  1e-29,
  1e-30, 1e-31, 1e-32, 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38,  1e-39,
  1e-40, 1e-41, 1e-42, 1e-43, 1e-44, 1e-45, 1e-46, 1e-47, 1e-48,  1e-49,
  1e-50, 1e-51, 1e-52, 1e-53, 1e-54, 1e-55, 1e-56, 1e-57, 1e-58,  1e-59,
};

double my_strtod(const char *str, char **end)
{
  double result= 0.0;
  int negative, ndigits;
  const char *old_str;

  while (my_isspace(&my_charset_latin1, *str))
    str++;

  if ((negative= (*str == '-')) || *str=='+')
    str++;

  old_str= str;
  while (my_isdigit (&my_charset_latin1, *str))
  {
    result= result*10.0 + (*str - '0');
    str++;
  }
  ndigits= str-old_str;

  if (*str == '.')
  {
    int n= 0;
    str++;
    old_str= str;
    while (my_isdigit (&my_charset_latin1, *str))
    {
      if (n < sizeof(pastpoint)/sizeof(pastpoint[0]))
      {
        result+= pastpoint[n] * (*str - '0');
        n++;
      }
      str++;
    }
    ndigits+= str-old_str;
    if (!ndigits) str--;
  }
  if (ndigits && (*str=='e' || *str=='E'))
  {
    int exp= 0;
    int neg= 0;
    const char *old_str= str++;

    if ((neg= (*str == '-')) || *str == '+')
      str++;

    if (!my_isdigit (&my_charset_latin1, *str))
      str= old_str;
    else
    {
      double scaler= 1.0;
      while (my_isdigit (&my_charset_latin1, *str))
      {
        exp= exp*10 + *str - '0';
        str++;
      }
      if (exp >= 1000)
      {
        if (neg)
          result= 0.0;
        else
          result= DBL_MAX;
        goto done;
      }
      while (exp >= 100)
      {
        scaler*= 1.0e100;
        exp-= 100;
      }
      scaler*= scaler10[exp/10]*scaler1[exp%10];
      if (neg)
        result/= scaler;
      else
        result*= scaler;
    }
  }

done:
  if (end)
    *end = (char *)str;

  if (isinf(result))
    result=DBL_MAX;

  return negative ? -result : result;
}

double my_atof(const char *nptr)
{
  return (strtod(nptr, 0));
}