#include "pch.h"

#include <Math/Int128.h>

#pragma warning(disable : 4073)
#pragma init_seg(lib)

const _int128  _I128_MIN( 0x8000000000000000, 0x0000000000000000);
const _int128  _I128_MAX( 0x7fffffffffffffff, 0xffffffffffffffff);
const _uint128 _UI128_MAX(0xffffffffffffffff, 0xffffffffffffffff);

// Conversion from _int128/_uint128 to string

// Number of digits that should be appended to the string for each loop.
// Also used as maximal number of digits to scan, that fits in 32 bit UINT
// digitCount[R] = floor(logR(_UI32_MAX+1))
// (index = radix = [2..36])
static const BYTE digitCount[37] = {
   0, 0,32,20,16,13,12,11
 ,10,10, 9, 9, 8, 8, 8, 8
 , 8, 7, 7, 7, 7, 7, 7, 7
 , 6, 6, 6, 6, 6, 6, 6, 6
 , 6, 6, 6, 6, 6
};

// Highest power of radix that fits in 32 bit (index = radix)
// For radix 2,4,8,16,32 special code is used which doesn't use this table.
// For all other values the element is used to get as many digits as possible
// by modulus and division. (powRadix[r] == pow(r,digitCount[r])
static const _uint128 powRadix[] = {
  0          //  not used
 ,0          //  not used
 ,0          //  not used
 ,0xcfd41b91 //  3486784401 =  3^20
 ,0          //  not used
 ,0x48c27395 //  1220703125 =  5^13
 ,0x81bf1000 //  2176782336 =  6^12
 ,0x75db9c97 //  1977326743 =  7^11
 ,0          //  not used
 ,0xcfd41b91 //  3486784401 =  9^10
 ,0x3b9aca00 //  1000000000 = 10^9
 ,0x8c8b6d2b //  2357947691 = 11^9
 ,0x19a10000 //   429981696 = 12^8
 ,0x309f1021 //   815730721 = 13^8
 ,0x57f6c100 //  1475789056 = 14^8
 ,0x98c29b81 //  2562890625 = 15^8
 ,0          //  not used
 ,0x18754571 //   410338673 = 17^7
 ,0x247dbc80 //   612220032 = 18^7
 ,0x3547667b //   893871739 = 19^7
 ,0x4c4b4000 //  1280000000 = 20^7
 ,0x6b5a6e1d //  1801088541 = 21^7
 ,0x94ace180 //  2494357888 = 22^7
 ,0xcaf18367 //  3404825447 = 23^7
 , 0xb640000 //   191102976 = 24^6
 , 0xe8d4a51 //   244140625 = 25^6
 ,0x1269ae40 //   308915776 = 26^6
 ,0x17179149 //   387420489 = 27^6
 ,0x1cb91000 //   481890304 = 28^6
 ,0x23744899 //   594823321 = 29^6
 ,0x2b73a840 //   729000000 = 30^6
 ,0x34e63b41 //   887503681 = 31^6
 ,0          //  not used
 ,0x4cfa3cc1 //  1291467969 = 33^6
 ,0x5c13d840 //  1544804416 = 34^6
 ,0x6d91b519 //  1838265625 = 35^6
 ,0x81bf1000 //  2176782336 = 36^6
};

#define ULTOSTR(v, str, radix)        \
{ if(sizeof(Ctype) == sizeof(char))   \
    _ultoa(v, (char*)str, radix);     \
  else                                \
    _ultow(v, (wchar_t*)str, radix);  \
}

#define STRLEN(str)      ((sizeof(Ctype)==sizeof(char))?strlen((char*)str):wcslen((wchar_t*)str))
#define STRCPY(dst, src) ((sizeof(Ctype)==sizeof(char))?(Ctype*)strcpy((char*)dst, (char*)src):(Ctype*)wcscpy((wchar_t*)dst, (wchar_t*)src))
#define STRREV(str)      ((sizeof(Ctype)==sizeof(char))?(Ctype*)_strrev((char*)str):(Ctype*)_wcsrev((wchar_t*)str))

template<class Int128Type, class Ctype> Ctype *int128toStr(Int128Type value, Ctype *str, UINT radix) {
  if((radix < 2) || (radix > 36)) {
    errno = EINVAL;
    str[0] = 0;
    return str;
  }
  errno = 0;
  bool setSign = false;
  if(value.isZero()) {
    str[0] = '0';
    str[1] = 0;
    return str;
  }

  Ctype *s = str;
  switch(radix) {
  case 2 :
  case 4 :
  case 16:
    { const UINT dc = digitCount[radix];
      for(int i = 3; i >= 0; i--) {
        if(value.s4.i[i]) {
          Ctype tmpStr[40];
          ULTOSTR(value.s4.i[i], tmpStr, radix);
          const size_t l = STRLEN(tmpStr);
          if(s != str) {
            for(size_t i = dc - l; i--;) *(s++) = '0'; // fill up with zeroes, if not leading digits
          }
          STRCPY(s, tmpStr);
          s += l;
        } else if(s != str) {
          for(size_t i = dc; i--;) *(s++) = '0'; // fill up with zeroes, if not leading digits
        }
      }
      *s = 0;
    }
    return str;
  case 8 : // Get 3 bits/digit giving 30 bits/loop, ie 10 digits/loop
  case 32: // Get 5 bits/digit giving 30 bits/loop too! which is 6 digits/loop
    { const UINT shft = (radix==32)?5:3;
      const UINT mask = (1 << shft) - 1;
      const UINT dpl  = 30 / shft;
      _uint128   v    = value;
      for(;;) {
        UINT v30 = v.s4.i[0] & ((1<<30) - 1);
        v >>= 30;
        UINT count;
        for(count = 0; v30; count++, v30 >>= shft) {
          *(s++) = radixLetter(v30 & mask);
        }
        if(v.isZero()) break;
        while(count++ < dpl) *(s++) = '0';
      }
    }
    break;
  case 10:
    if(value.isNegative()) {
      value = -value;
      setSign = true;
    }
    // NB continue case
  default:
    const UINT      dc      = digitCount[radix];
    const _uint128 &divisor = powRadix[radix];
    _uint128        v       = value;
    for(;;) {
      const UINT c = v % divisor;
      Ctype tmpStr[40];
      ULTOSTR(c, tmpStr, radix);
      STRCPY(s, STRREV(tmpStr));
      size_t l = STRLEN(tmpStr);
      s += l;
      v /= divisor;
      if(v) {
        while(l++ < dc) *(s++) = '0'; // append zeroes
      } else {
        break;
      }
    }
    if(setSign) *(s++) = '-';
    break;
  }
  *s = 0;
  return STRREV(str);
}

char *_i128toa(_int128 value, char *str, int radix) {
  return int128toStr<_int128, char>(value, str, radix);
}

wchar_t *_i128tow(_int128 value, wchar_t *str, int radix) {
  return int128toStr<_int128, wchar_t>(value, str, radix);
}

char*_ui128toa(_uint128 value, char *str, int radix) {
  return int128toStr<_uint128, char>(value, str, radix);
}

wchar_t *_ui128tow(_uint128 value, wchar_t *str, int radix) {
  return int128toStr<_uint128, wchar_t>(value, str, radix);
}

// Conversion from string to _int128/_uint128

static inline bool isRadixDigit(wchar_t ch, UINT radix, UINT &value) {
  if(!iswalnum(ch)) return false;
  const UINT v = iswdigit(ch) ? (ch - '0') : (ch - (iswupper(ch)?'A':'a') + 10);
  if(v >= radix) return false;
  value = v;
  return true;
}

template<class Int128Type, class Ctype, bool withSign> Int128Type strtoint128(const Ctype *s, Ctype **end, UINT radix, _locale_t locale) {
  if((s == NULL) || ((radix != 0) && ((radix < 2) || (radix > 36)))) {
    errno = EINVAL;
    return 0;
  }
  errno = 0;
  bool negative = false;
  bool gotDigit = false;
  while(iswspace(*s)) s++; // skip whitespace
  if(*s == '-') { // read optional sign
    s++;
    negative = true;
  } else if (*s == '+') {
    s++;
  }
  UINT digit;
  if(radix == 0) { // first determine if radix is 8,10 or 16
    if(*s == '0') {
      gotDigit = true;
      s++;
      if(end) *end = (Ctype*)s;
      if((*s == 'x') || (*s == 'X')) {
        radix = 16; s++;
      } else {
        radix = 8;
      }
      if(!isRadixDigit(*s, radix, digit)) { // we've scanned "0[x]",
        return 0;                           // if no more digits, then 0
      }
    } else if(iswdigit(*s)) {
      radix = 10;
    } else {
      return 0; // nothing recognized
    }
  }
  Int128Type result128;
  bool       overflow        = false;
  const UINT maxDigitCount32 = digitCount[radix];
  if(isRadixDigit(*(s++), radix, digit)) {
    gotDigit           = true;
    while((digit == 0) && isRadixDigit(*s, radix, digit)) s++; // skip leading zeroes
    bool firstChunk    = true;
    UINT result32      = digit;

    if((radix & -(int)radix) == radix) { // is radix 2,4,8,16 or 32
      const UINT maxBitCount   = withSign ? 127 : 128;
      UINT       totalBitCount;
      const UINT bitsPerDigit  = 32 / maxDigitCount32;
      const UINT maxBitCount32 = bitsPerDigit * maxDigitCount32;
      for(UINT bitCount32=bitsPerDigit;;result32=bitCount32=0) {
        while(isRadixDigit(*(s++), radix, digit)) {
          result32 <<= bitsPerDigit;
          result32 |= digit;
          if((bitCount32 += bitsPerDigit) == maxBitCount32) break;
        }
        if(firstChunk) {
          result128     = result32;
          firstChunk    = false;
          totalBitCount = bitCount32;
        } else if(bitCount32) {
          if((totalBitCount += bitCount32) > maxBitCount) {
            const BYTE mask = ~(((withSign&&!negative)?0x7f:0xff)>>(bitCount32&7));
            if(result128.s16.b[15-(bitCount32>>3)] & mask) {
              overflow = true;
              if(bitCount32 == maxBitCount32) {
                while(isRadixDigit(*(s++), radix, digit));
              }
              break;
            }
          }
          result128 <<= bitCount32;
          if(result32) result128 |= result32;
        } else {
          break;
        }
        if(bitCount32 < maxBitCount32) break;
      }
    } else {
      for(UINT digitCount32=1, p32=radix;; p32=1, result32=digitCount32=0) {
        while(isRadixDigit(*(s++), radix, digit)) {
          result32 *= radix;
          result32 += digit;
          p32      *= radix;
          if(++digitCount32 == maxDigitCount32) break;
        }
        if(firstChunk) {
          result128  = result32;
          firstChunk = false;
        } else if(digitCount32) {
          const UINT lastH = result128.s4.i[3];
          result128 *= p32;
          if(result32) result128 += result32;
          if(lastH && ((result128.s4.i[3]==0) || (withSign&&!negative&&result128.isNegative()))) {
            overflow = true;
            if(digitCount32 == maxDigitCount32) {
              while(isRadixDigit(*(s++), radix, digit));
            }
            break;
          }
        } else {
          break;
        }
        if(digitCount32 < maxDigitCount32) break;
      }
    }
  }
  if(!gotDigit) return 0;
  if(end) *end = (Ctype*)s-1;
  if(overflow) {
    errno = ERANGE;
    return withSign ? (negative ? _I128_MIN : _I128_MAX) : _UI128_MAX;
  }
  return negative ? -result128 : result128;
}

_int128 _strtoi128_l(const char *str, char **end, int radix, _locale_t locale) {
  return strtoint128<_int128 ,char   ,true >(str, end, radix, locale);
}

_uint128 _strtoui128_l(const char *str, char **end, int radix, _locale_t locale) {
  return strtoint128<_uint128,char   ,false>(str, end, radix, locale);
}

_int128 _wcstoi128_l(const wchar_t *str, wchar_t **end, int radix, _locale_t locale) {
  return strtoint128<_int128 ,wchar_t,true >(str, end, radix, locale);
}

_uint128 _wcstoui128_l(const wchar_t *str, wchar_t **end, int radix, _locale_t locale) {
  return strtoint128<_uint128,wchar_t,false>(str, end, radix, locale);
}
