#include <windows.h>
#include "mathcrt.h"
#include "MyMath.h"
#include "Math.h"

// Converts String to Int (Dec, Hex) or Float value
void StringToItem(char *&s, ExpressionItem *item, int options)
{
  item->type = IT_CONST | ITC_INT;
  __int64 &v=*((__int64*)&(item->param1));
  v = 0;

  // Check for right input
  if (!s) return;

  // String-value
  if ((((options & (STI_FLOAT | STI_INT)) == 0) || *s == '\'' || *s == '\"' || *s == '`' ||
      ((*s != '+') && (*s != '-') && ((*s < '0') || (*s > '9'))))
      && (options & STI_STRING))
  {
      // end of string char
      char eol = 0;
      if (*s == '\'' || *s == '\"' || *s == '`') eol = *s;
      else s--;

      item->type = IT_CONST | ITC_STRING;
      // allocate memory buffer for string
      char *sp;
      sp = *((char**)&(item->param1)) = AllocString();
      while (*(++s) && (*s != eol))
      {
        *(sp++) = *s;
      }
      if (*s == eol) s++;
      *sp = 0;
  } else
  {
      // strip leading spaces and tabs
      while ((*s == ' ') || (*s == '\t')) s++;
  // Hex-value
  if ((options & STI_INT) && *s == '0' && (s[1] == 'x' || s[1] == 'X'))
  {
    s++;
    while (*(s+1) == '0') *s++;
    for (;;)
    {
      int c=*(++s);
      if (c >= '0' && c <= '9') c-='0';
      else if (c >= 'a' && c <= 'f') c-='a'-10;
      else if (c >= 'A' && c <= 'F') c-='A'-10;
      else break;
      v<<=4;
      v+=c;
    }
  }
  // Dec-value, possible floating-point
  else
  {
    int sign=0, numsignif = 0;
    if (*s == '-') sign++; else s--;
    while (*(s+1) == '0') *s++;
    for (;;)
    {
      int c=*(++s) - '0'; numsignif++;
      if ((options & STI_FLOAT) &&
          ((c == ('e'-'0')) || (c==('E'-'0')) || (c==('.'-'0'))
          || (numsignif > 18)))
      {
          // Switch to floating point conversion rountine
          item->type = IT_CONST | ITC_FLOAT;
          double& d = *((double*)&(item->param1));
          d = (double) v;

          while ((c >= 0) && (c <= 9))
          {
              d = d*10.0 + (double) c;
              c=*(++s) - '0';
          }

          // sub-decimal part
          if (c == ('.'-'0'))
          {
            double pwr = 1.0, dec = 0.0;
            for (;;)
            {
              c=*(++s) - '0';
              if ((c < 0) || (c > 9)) break;
              dec = dec*10.0 + (double) c;
              pwr *= 10.0;
            }
            d += dec/pwr;
          }
          // exponental part
          if ((c == ('E'-'0')) || (c == ('e'-'0')))
          {
              int expc = 0, esign = 0;
              s++;
              // detect exponential sign
              if ((*s == '+') || (*s == '-'))
                  esign = (*s == '-');
              else s--;

              // detect exp value
              for (;;)
              {
                c=*(++s) - '0';
                if ((c < 0) || (c > 9)) break;
                expc = expc*10 + c;
              }

              if (expc >= DBL_MAX_EXP)
              {
                  d = HUGE_VAL;
                  expc = 0;
              }

              double pwr = 1;
              while (expc > 99) { pwr *= 1.0e100; expc -= 100; }
              while (expc > 9) { pwr *= 1.0e10; expc -= 10; }
              while (expc) { pwr *= 10.0; expc--; }
              if (esign) d /= pwr;
              else d *= pwr;
          }
          if (sign) d = -d;
          return;
      }
      if (c < 0 || c > 9) break;
      v*=10;
      v+=c;
    }
    if (sign) v = -v;
    if ((options & STI_FLOAT) && ((options & STI_INT) == 0))
    {
        double& d = *((double*)&(item->param1));
        d = (double) v;
        item->type = IT_CONST | ITC_FLOAT;
    }
  }
  }
}

void ItemToString(char *sbuf, ExpressionItem *item)
{
    if ((item == NULL) || ((item->type & ITEMTYPE) != IT_CONST))
    {
        *sbuf = 0;
        return;
    }

    switch (item->type & ITEMSUBTYPE)
    {
    case ITC_STRING:
        {
        char *ptr = *((char**)&(item->param1));
        while (*(sbuf++) = *(ptr++));
        }
        break;
    case ITC_ARRAY:
        {
            ArrayDesc *ad = (ArrayDesc *) item->param1;
            for (int index = 0; index < ad->count; index++)
                if ((ad->array[index]) &&
                    ((ad->array[index]->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST | ITC_INT)))
                    if ((*(sbuf++) = (char) *((__int64*)&(ad->array[index]->param1))) == 0)
                        break;
        }
        break;
    case ITC_FLOAT:
        FloatFormat(sbuf, *((double*)&(item->param1)), 6);
        break;
    case ITC_INT:
        itoa64(*((__int64*)&(item->param1)), sbuf);
        break;
    }
}

void itoa64(__int64 i, char *buffer)
{
    char buf[128], *b = buf;

    if (i < 0)
    {
        *(buffer++) = '-';
        i = -i;
    }
    if (i == 0) *(buffer++) = '0';
    else
    {
        while (i > 0)
        {
            *(b++) = '0' + ((char) (i%10));
            i /= 10;
        }
        while (b > buf) *(buffer++) = *(--b);
    }
    *buffer = 0;
}

#define _FLOAT_ROUND_ADJUST (double)5e-15
extern "C"
    int _floatp10(double *fnum, int *fsign, int prec);
extern "C"
    int _ftol(double num);

#define POS_INFINITY  "#INF"
#define NEG_INFINITY "-#INF"

void FloatFormatF(char *s, double value, int prec)
{
    int fpower, fsign, fdigit, fprec = 0, fzfill = 0;

    fpower = _floatp10(&value, &fsign, prec);
    if(fsign < 0) *s++ = '-';
    if(fpower < 0)
    {
        *s++ = '0';
        fpower++;
        fzfill++;
    } else {
        while(fpower >= 0)
        {
           if(fprec < 16)
           {
                fdigit = (int)value;
                *s++ = (char)((char)fdigit + (char)48);
                value -= (double)fdigit;
                value *= (double)10;
                value += _FLOAT_ROUND_ADJUST;
                fprec++;
           } else {
                *s++ = '0';
           }
           fpower--;
        }
        fpower = 0;
    }
    if(prec)
    {
            *s++ = '.';
            while(prec)
            {
              if(fzfill && fpower < 0)
              {
                *s++ = '0';
                fpower++;
              } else {
                if(fprec < 16)
                {
                  fdigit = (int)value;
                  *s++ = (unsigned char)((unsigned char)fdigit +
                                         (unsigned char)48);

                  value -= (double)fdigit;
                  value *= (double)10;
                  value += _FLOAT_ROUND_ADJUST;
                  fprec++;
                } else {
                  *s++ = '0';
                }
              }
              prec--;
            }
    }
    *s = '\0';
}

void FloatFormatE(char *s, double fnum, int options)
{
    int fpower, fsign, fdigit, fprec = 0, prec, fzfill = 0;
    double sfnum;

    prec = options & 0xF;

    sfnum = fnum;
    fpower = _floatp10(&sfnum, &fsign, -999);
    fpower = _floatp10(&fnum, &fsign, prec - fpower);
    if(fsign < 0) *s++ = '-';
    fdigit = (int)fnum;
    *s++ = (char)((char)fdigit + (char)48);
    fnum -= (double)fdigit;
    fnum *= (double)10;
    fnum += _FLOAT_ROUND_ADJUST;
    if(prec)
    {
        *s++ = '.';
        while(prec)
        {
            if(fprec < 16)
            {
                fdigit = (int)fnum;
                *s++ = (unsigned char)((unsigned char)fdigit +
                    (unsigned char)48);
                fnum -= (double)fdigit;
                fnum *= (double)10;
                fnum += _FLOAT_ROUND_ADJUST;
                fprec++;
            } else *s++ = '0';
            prec--;
        }
    }
    *s++ = ((options & FF_LEXP)?('E'):('e'));
    if(fpower >= 0)
    {
        *s++ = '+';
    } else {
        *s++ = '-';
        fpower = -fpower;
    }
    if(fpower < 10) *s++ = '0';
    itoa64(fpower, s);
}

void FloatFormat(char *s, double value, int options)
{
    int prec = options & 0xF;

    if(value == HUGE_VAL)
    {
        lstrcpy(s, POS_INFINITY);
        return;
    } else if(value == -HUGE_VAL) {
        lstrcpy(s, NEG_INFINITY);
        return;
    }

    if (options & FF_NOEXP) FloatFormatF(s, value, prec);
    else
    if (options & FF_EXP) FloatFormatE(s, value, options);
    else
    {
        double sfnum = value;
        int fsign, fpower;
        fpower = _floatp10(&sfnum, &fsign, -999);
        sfnum = value;
        fpower = _floatp10(&sfnum, &fsign, prec - fpower);

        if((value != 0.0) && ((fpower < -4) || (fpower >= prec)))
            FloatFormatE(s, value, options);
        else
        {
            prec -= (fpower + 1);
            if(prec <= 0) prec = 1;
            FloatFormatF(s, value, prec);
        }
    }
}

int lstrcmpn(char *s1, const char *s2, int chars)
{
    while ((chars > 0) && (*s1) && (*s2) && (*(s1) == *(s2))) chars--, s1++, s2++;
    if ((chars == 0) || (*s1 == *s2)) return 0;
    return (*s1 - *s2);
}