From 9ff1aa9d550556812ef12a26a3d3e9b55e63b95b Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 6 Mar 2026 07:50:03 +0900 Subject: [PATCH] fp_uscale.c: replace fmt_fp.c and readfloat.c with uscale algorithm Replace separate float formatting (fmt_fp.c) and parsing (readfloat.c) implementations with a unified fp_uscale.c using 128-bit unrounded scaling. Both mrb_format_float() and mrb_read_float() now share a single pow10 table and uscale() primitive for decimal/binary conversion. This fixes subnormal parsing accuracy (old code returned 0.0 for the smallest subnormals) and corrects %.2f rounding for values like 12345.125. Table size grows from ~5KB to ~11KB in .rodata. Co-authored-by: Claude --- lib/mruby/amalgam.rb | 3 +- src/fmt_fp.c | 380 ----------- src/fp_uscale.c | 1359 ++++++++++++++++++++++++++++++++++++++++ src/readfloat.c | 229 ------- tools/gen_pow10_tab.rb | 68 ++ 5 files changed, 1428 insertions(+), 611 deletions(-) delete mode 100644 src/fmt_fp.c create mode 100644 src/fp_uscale.c delete mode 100644 src/readfloat.c create mode 100755 tools/gen_pow10_tab.rb diff --git a/lib/mruby/amalgam.rb b/lib/mruby/amalgam.rb index 125f84657..0aec03c75 100644 --- a/lib/mruby/amalgam.rb +++ b/lib/mruby/amalgam.rb @@ -41,7 +41,7 @@ module MRuby allocf.c readnum.c readint.c - readfloat.c + fp_uscale.c state.c symbol.c class.c @@ -66,7 +66,6 @@ module MRuby cdump.c codedump.c print.c - fmt_fp.c debug.c etc.c version.c diff --git a/src/fmt_fp.c b/src/fmt_fp.c deleted file mode 100644 index 173cc3b58..000000000 --- a/src/fmt_fp.c +++ /dev/null @@ -1,380 +0,0 @@ -#include -#include - -#ifndef MRB_NO_FLOAT -/*********************************************************************** - - Routine for converting a single-precision - floating-point number into a string. - - The code in this function was inspired from Fred Bayer's pdouble.c. - Since pdouble.c was released as Public Domain, I'm releasing this - code as public domain as well. - - Dave Hylands - - The original code can be found in https://github.com/dhylands/format-float -***********************************************************************/ - -/*********************************************************************** - - I modified the routine for mruby: - - * support `double` - * support `#` (alt_form) modifier - - My modifications in this file are also placed in the public domain. - - Matz (Yukihiro Matsumoto) - -***********************************************************************/ - -#include - -#ifdef MRB_USE_FLOAT32 - -// 1 sign bit, 8 exponent bits, and 23 mantissa bits. -// exponent values 0 and 255 are reserved, exponent can be 1 to 254. -// exponent is stored with a bias of 127. -// The min and max floats are on the order of 1x10^37 and 1x10^-37 - -#define FLT_DECEXP 32 -#define FLT_ROUND_TO_ONE 0.9999995F -#define FLT_MIN_BUF_SIZE 6 // -9e+99 - -#else - -// 1 sign bit, 11 exponent bits, and 52 mantissa bits. - -#define FLT_DECEXP 256 -#define FLT_ROUND_TO_ONE 0.999999999995 -#define FLT_MIN_BUF_SIZE 7 // -9e+199 - -#endif /* MRB_USE_FLOAT32 */ - -static const mrb_float g_pos_pow[] = { -#ifndef MRB_USE_FLOAT32 - 1e256, 1e128, 1e64, -#endif - 1e32, 1e16, 1e8, 1e4, 1e2, 1e1 -}; -static const mrb_float g_neg_pow[] = { -#ifndef MRB_USE_FLOAT32 - 1e-256, 1e-128, 1e-64, -#endif - 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1 -}; - -/* - * mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) - * - * fmt: should be one of 'e', 'E', 'f', 'F', 'g', or 'G'. (|0x80 for '#') - * prec: is the precision (as specified in printf) - * sign: should be '\0', '+', or ' ' ('\0' is the normal one - only print - * a sign if ```f``` is negative. Anything else is printed as the - * sign character for positive numbers. - */ - -int -mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) { - char *s = buf; - int buf_remaining = (int)buf_size - 1; - int alt_form = 0; - - if ((uint8_t)fmt & 0x80) { - fmt &= 0x7f; /* turn off alt_form flag */ - alt_form = 1; - } - if (buf_size <= FLT_MIN_BUF_SIZE) { - // Smallest exp notion is -9e+99 (-9e+199) which is 6 (7) chars plus terminating - // null. - - if (buf_size >= 2) { - *s++ = '?'; - } - if (buf_size >= 1) { - *s++ = '\0'; - } - return buf_size >= 2; - } - if (signbit(f)) { - *s++ = '-'; - f = -f; - } - else if (sign) { - *s++ = sign; - } - buf_remaining -= (int)(s - buf); // Adjust for sign - - { - char uc = fmt & 0x20; - if (isinf(f)) { - *s++ = 'I' ^ uc; - *s++ = 'N' ^ uc; - *s++ = 'F' ^ uc; - goto ret; - } - else if (isnan(f)) { - *s++ = 'N' ^ uc; - *s++ = 'A' ^ uc; - *s++ = 'N' ^ uc; - ret: - *s = '\0'; - return (int)(s - buf); - } - } - - if (prec < 0) { - prec = 6; - } - char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt - fmt |= 0x20; // Force fmt to be lowercase - char org_fmt = fmt; - if (fmt == 'g' && prec == 0) { - prec = 1; - } - int e, e1; - int dec = 0; - char e_sign = '\0'; - int num_digits = 0; - const mrb_float *pos_pow = g_pos_pow; - const mrb_float *neg_pow = g_neg_pow; - - if (f == 0.0) { - e = 0; - if (fmt == 'e') { - e_sign = '+'; - } - else if (fmt == 'f') { - num_digits = prec + 1; - } - } - else if (f < 1.0) { // f < 1.0 - char first_dig = '0'; - if (f >= FLT_ROUND_TO_ONE) { - first_dig = '1'; - } - - // Build negative exponent - for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) { - if (*neg_pow > f) { - e += e1; - f *= *pos_pow; - } - } - char e_sign_char = '-'; - if (f < 1.0) { - if (f >= FLT_ROUND_TO_ONE) { - f = 1.0; - if (e == 0) { - e_sign_char = '+'; - } - } - else { - e++; - f *= 10.0; - } - } - - // If the user specified 'g' format, and e is <= 4, then we'll switch - // to the fixed format ('f') - - if (fmt == 'f' || (fmt == 'g' && e <= 4)) { - fmt = 'f'; - dec = -1; - *s++ = first_dig; - - if (org_fmt == 'g') { - prec += (e - 1); - } - // truncate precision to prevent buffer overflow - if (prec + 2 > buf_remaining) { - prec = buf_remaining - 2; - } - num_digits = prec; - if (num_digits || alt_form) { - *s++ = '.'; - while (--e && num_digits) { - *s++ = '0'; - num_digits--; - } - } - } - else { - // For e & g formats, we'll be printing the exponent, so set the - // sign. - e_sign = e_sign_char; - dec = 0; - - if (prec > (buf_remaining - FLT_MIN_BUF_SIZE)) { - prec = buf_remaining - FLT_MIN_BUF_SIZE; - if (fmt == 'g') { - prec++; - } - } - } - } - else { - // Build positive exponent - for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) { - if (*pos_pow <= f) { - e += e1; - f *= *neg_pow; - } - } - // correct for FP rounding errors in the power-of-10 loop - // (e.g. x87 extended precision can leave f >= 10.0) - if (f >= 10.0) { - f *= 0.1; - e++; - } - - // If the user specified fixed format (fmt == 'f') and e makes the - // number too big to fit into the available buffer, then we'll - // switch to the 'e' format. - - if (fmt == 'f') { - if (e >= buf_remaining) { - fmt = 'e'; - } - else if ((e + prec + 2) > buf_remaining) { - prec = buf_remaining - e - 2; - if (prec < 0) { - // This means no decimal point, so we can add one back - // for the decimal. - prec++; - } - } - } - if (fmt == 'e' && prec > (buf_remaining - 6)) { - prec = buf_remaining - 6; - } - // If the user specified 'g' format, and e is < prec, then we'll switch - // to the fixed format. - - if (fmt == 'g' && e < prec) { - fmt = 'f'; - prec -= (e + 1); - } - if (fmt == 'f') { - dec = e; - num_digits = prec + e + 1; - } - else { - e_sign = '+'; - } - } - if (prec < 0) { - // This can happen when the prec is trimmed to prevent buffer overflow - prec = 0; - } - - // We now have f as a floating-point number between >= 1 and < 10 - // (or equal to zero), and e contains the absolute value of the power of - // 10 exponent, and (dec + 1) == the number of digits before the decimal. - - // For e, prec is # digits after the decimal - // For f, prec is # digits after the decimal - // For g, prec is the max number of significant digits - // - // For e & g there will be a single digit before the decimal - // for f there will be e digits before the decimal - - if (fmt == 'e') { - num_digits = prec + 1; - if (prec == 0) prec = 1; - } - else if (fmt == 'g') { - num_digits = prec; - } - - // Print the digits of the mantissa - for (int i = 0; i < num_digits; i++,dec--) { - int8_t d = (int8_t)f; - if (d > 9) d = 9; - if (d < 0) d = 0; - *s++ = '0' + d; - if (dec == 0 && (prec > 0 || alt_form)) { - *s++ = '.'; - } - f -= (mrb_float)d; - f *= 10.0; - } - - // Round - if (f >= 5.0) { - char *rs = s; - rs--; - while (1) { - if (*rs == '.') { - rs--; - continue; - } - if (*rs < '0' || *rs > '9') { - // + or - - rs++; // So we sit on the digit to the right of the sign - break; - } - if (*rs < '9') { - (*rs)++; - break; - } - *rs = '0'; - if (rs == buf) { - break; - } - rs--; - } - if (*rs == '0') { - // We need to insert a 1 - if (fmt != 'f' && rs[1] == '.') { - // We're going to round 9.99 to 10.00 - // Move the decimal point - rs[0] = '.'; - rs[1] = '0'; - if (e_sign == '-') { - e--; - } - else { - e++; - } - } - s++; - char *ss = s; - while (ss > rs) { - *ss = ss[-1]; - ss--; - } - *rs = '1'; - if (f < 1.0 && fmt == 'f') { - // We rounded up to 1.0 - prec--; - } - } - } - - if (org_fmt == 'g' && prec > 0 && !alt_form) { - // Remove trailing zeros and a trailing decimal point - while (s[-1] == '0') { - s--; - } - if (s[-1] == '.') { - s--; - } - } - // Append the exponent - if (e_sign) { - *s++ = e_char; - *s++ = e_sign; - if (e >= 100) { - *s++ = '0' + (e / 100); - e %= 100; - } - *s++ = '0' + (e / 10); - *s++ = '0' + (e % 10); - } - - *s = '\0'; - return (int)(s - buf); -} -#endif diff --git a/src/fp_uscale.c b/src/fp_uscale.c new file mode 100644 index 000000000..0ade7bc89 --- /dev/null +++ b/src/fp_uscale.c @@ -0,0 +1,1359 @@ +/* +** fp_uscale.c - Unrounded Scaling float conversion +** +** Unified float formatting and parsing using the uscale algorithm. +** Based on Russ Cox's "Unrounded Scaling" approach. +** See https://research.swtch.com/fp +** +** Replaces fmt_fp.c (formatting) and readfloat.c (parsing). +*/ + +#include + +#ifndef MRB_NO_FLOAT + +#include +#include +#include + +/* ======== Platform support ======== */ + +#if defined(__SIZEOF_INT128__) +static inline void mul64(uint64_t a, uint64_t b, uint64_t *hi, uint64_t *lo) +{ + __uint128_t r = (__uint128_t)a * b; + *hi = (uint64_t)(r >> 64); + *lo = (uint64_t)r; +} +#elif defined(_MSC_VER) && defined(_M_X64) +#include +static inline void mul64(uint64_t a, uint64_t b, uint64_t *hi, uint64_t *lo) +{ + *lo = _umul128(a, b, hi); +} +#else +static inline void mul64(uint64_t a, uint64_t b, uint64_t *hi, uint64_t *lo) +{ + uint64_t a_lo = (uint32_t)a, a_hi = a >> 32; + uint64_t b_lo = (uint32_t)b, b_hi = b >> 32; + uint64_t p0 = a_lo * b_lo; + uint64_t p1 = a_lo * b_hi; + uint64_t p2 = a_hi * b_lo; + uint64_t p3 = a_hi * b_hi; + uint64_t mid = (p0 >> 32) + (uint32_t)p1 + (uint32_t)p2; + *lo = (p0 & 0xFFFFFFFFULL) | (mid << 32); + *hi = p3 + (p1 >> 32) + (p2 >> 32) + (mid >> 32); +} +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define clz64(x) __builtin_clzll(x) +#elif defined(_MSC_VER) && defined(_M_X64) +static inline int clz64(uint64_t x) +{ + unsigned long idx; + _BitScanReverse64(&idx, x); + return 63 - (int)idx; +} +#else +static inline int clz64(uint64_t x) +{ + int n = 0; + if (x <= 0x00000000FFFFFFFFULL) { n += 32; x <<= 32; } + if (x <= 0x0000FFFFFFFFFFFFULL) { n += 16; x <<= 16; } + if (x <= 0x00FFFFFFFFFFFFFFULL) { n += 8; x <<= 8; } + if (x <= 0x0FFFFFFFFFFFFFFFULL) { n += 4; x <<= 4; } + if (x <= 0x3FFFFFFFFFFFFFFFULL) { n += 2; x <<= 2; } + if (x <= 0x7FFFFFFFFFFFFFFFULL) { n += 1; } + return n; +} +#endif + +#define bits_len64(x) (64 - clz64(x)) + +/* ======== pow10 table ======== */ + +typedef struct { + uint64_t hi; + uint64_t lo; +} pow10_entry; + +#define POW10_MIN (-343) +#define POW10_MAX 341 +#define POW10_TAB_SIZE (POW10_MAX - POW10_MIN + 1) + +/* generated by tools/gen_pow10_tab.rb */ +static const pow10_entry pow10_tab[POW10_TAB_SIZE] = { + {0xbf29dcaba82fdeafULL, 0x8bcd1178c77f03ccULL}, + {0xeef453d6923bd65bULL, 0xeec055d6f95ec4c0ULL}, + {0x9558b4661b6565f9ULL, 0xb53835a65bdb3af8ULL}, + {0xbaaee17fa23ebf77ULL, 0xa286430ff2d209b6ULL}, + {0xe95a99df8ace6f54ULL, 0x0b27d3d3ef868c23ULL}, + {0x91d8a02bb6c10595ULL, 0x86f8e46475b41796ULL}, + {0xb64ec836a47146faULL, 0x68b71d7d93211d7bULL}, + {0xe3e27a444d8d98b8ULL, 0x02e4e4dcf7e964daULL}, + {0x8e6d8c6ab0787f73ULL, 0x01cf0f0a1af1df08ULL}, + {0xb208ef855c969f50ULL, 0x4242d2cca1ae56caULL}, + {0xde8b2b66b3bc4724ULL, 0x52d3877fca19ec7dULL}, + {0x8b16fb203055ac77ULL, 0xb3c434afde5033ceULL}, + {0xaddcb9e83c6b1794ULL, 0x20b541dbd5e440c2ULL}, + {0xd953e8624b85dd79ULL, 0x28e29252cb5d50f2ULL}, + {0x87d4713d6f33aa6cULL, 0x798d9b73bf1a5297ULL}, + {0xa9c98d8ccb009507ULL, 0x97f10250aee0e73dULL}, + {0xd43bf0effdc0ba49ULL, 0xfded42e4da99210dULL}, + {0x84a57695fe98746eULL, 0xfeb449cf089fb4a8ULL}, + {0xa5ced43b7e3e9189ULL, 0xbe615c42cac7a1d2ULL}, + {0xcf42894a5dce35ebULL, 0xadf9b3537d798a46ULL}, + {0x818995ce7aa0e1b3ULL, 0x8cbc10142e6bf66cULL}, + {0xa1ebfb4219491a20ULL, 0xefeb14193a06f407ULL}, + {0xca66fa129f9b60a7ULL, 0x2be5d91f8888b109ULL}, + {0xfd00b897478238d1ULL, 0x76df4f676aaadd4bULL}, + {0x9e20735e8cb16383ULL, 0xaa4b91a0a2aaca4fULL}, + {0xc5a890362fddbc63ULL, 0x14de7608cb557ce2ULL}, + {0xf712b443bbd52b7cULL, 0x5a16138afe2adc1bULL}, + {0x9a6bb0aa55653b2eULL, 0xb84dcc36dedac991ULL}, + {0xc1069cd4eabe89f9ULL, 0x66613f4496917bf5ULL}, + {0xf148440a256e2c77ULL, 0x3ff98f15bc35daf2ULL}, + {0x96cd2a865764dbcbULL, 0xc7fbf96d95a1a8d7ULL}, + {0xbc807527ed3e12bdULL, 0x39faf7c8fb0a130dULL}, + {0xeba09271e88d976cULL, 0x0879b5bb39cc97d1ULL}, + {0x93445b8731587ea4ULL, 0x854c1195041fdee2ULL}, + {0xb8157268fdae9e4dULL, 0xa69f15fa4527d69bULL}, + {0xe61acf033d1a45e0ULL, 0x9046db78d671cc42ULL}, + {0x8fd0c16206306bacULL, 0x5a2c492b86071fa9ULL}, + {0xb3c4f1ba87bc8697ULL, 0x70b75b766788e793ULL}, + {0xe0b62e2929aba83dULL, 0xcce53254016b2178ULL}, + {0x8c71dcd9ba0b4926ULL, 0x600f3f7480e2f4ebULL}, + {0xaf8e5410288e1b70ULL, 0xf8130f51a11bb226ULL}, + {0xdb71e91432b1a24bULL, 0x3617d32609629eafULL}, + {0x892731ac9faf056fULL, 0x41cee3f7c5dda32dULL}, + {0xab70fe17c79ac6cbULL, 0x92429cf5b7550bf9ULL}, + {0xd64d3d9db981787eULL, 0xf6d34433252a4ef7ULL}, + {0x85f0468293f0eb4fULL, 0xda440a9ff73a715aULL}, + {0xa76c582338ed2622ULL, 0x50d50d47f5090db1ULL}, + {0xd1476e2c07286fabULL, 0xe50a5099f24b511eULL}, + {0x82cca4db847945cbULL, 0xaf267260376f12b2ULL}, + {0xa37fce126597973dULL, 0x1af00ef8454ad75fULL}, + {0xcc5fc196fefd7d0dULL, 0xe1ac12b6569d8d37ULL}, + {0xff77b1fcbebcdc50ULL, 0xda171763ec44f085ULL}, + {0x9faacf3df73609b2ULL, 0x884e6e9e73ab1653ULL}, + {0xc795830d75038c1eULL, 0x2a620a461095dbe8ULL}, + {0xf97ae3d0d2446f26ULL, 0xb4fa8cd794bb52e2ULL}, + {0x9becce62836ac578ULL, 0xb11c9806bcf513cdULL}, + {0xc2e801fb244576d6ULL, 0xdd63be086c3258c0ULL}, + {0xf3a20279ed56d48bULL, 0x94bcad8a873eeef0ULL}, + {0x9845418c345644d7ULL, 0x7cf5ec7694875556ULL}, + {0xbe5691ef416bd60dULL, 0xdc33679439a92aacULL}, + {0xedec366b11c6cb90ULL, 0xd340417948137557ULL}, + {0x94b3a202eb1c3f3aULL, 0x840828ebcd0c2956ULL}, + {0xb9e08a83a5e34f08ULL, 0x250a3326c04f33acULL}, + {0xe858ad248f5c22caULL, 0x2e4cbff070630097ULL}, + {0x91376c36d99995bfULL, 0xdceff7f6463de05eULL}, + {0xb58547448ffffb2eULL, 0x542bf5f3d7cd5875ULL}, + {0xe2e69915b3fff9faULL, 0xe936f370cdc0ae93ULL}, + {0x8dd01fad907ffc3cULL, 0x51c2582680986d1cULL}, + {0xb1442798f49ffb4bULL, 0x6632ee3020be8863ULL}, + {0xdd95317f31c7fa1eULL, 0xbfbfa9bc28ee2a7cULL}, + {0x8a7d3eef7f1cfc53ULL, 0xb7d7ca159994da8dULL}, + {0xad1c8eab5ee43b67ULL, 0x25cdbc9afffa1130ULL}, + {0xd863b256369d4a41ULL, 0x6f412bc1bff8957dULL}, + {0x873e4f75e2224e69ULL, 0xa588bb5917fb5d6eULL}, + {0xa90de3535aaae203ULL, 0x8eeaea2f5dfa34c9ULL}, + {0xd3515c2831559a84ULL, 0xf2a5a4bb3578c1fcULL}, + {0x8412d9991ed58092ULL, 0x17a786f5016b793dULL}, + {0xa5178fff668ae0b7ULL, 0x9d9168b241c6578dULL}, + {0xce5d73ff402d98e4ULL, 0x04f5c2ded237ed70ULL}, + {0x80fa687f881c7f8fULL, 0x831999cb4362f466ULL}, + {0xa139029f6a239f73ULL, 0xe3e0003e143bb17fULL}, + {0xc987434744ac874fULL, 0x5cd8004d994a9ddfULL}, + {0xfbe9141915d7a923ULL, 0xb40e0060ff9d4557ULL}, + {0x9d71ac8fada6c9b6ULL, 0x9088c03c9fc24b56ULL}, + {0xc4ce17b399107c23ULL, 0x34aaf04bc7b2de2cULL}, + {0xf6019da07f549b2cULL, 0x81d5ac5eb99f95b7ULL}, + {0x99c102844f94e0fcULL, 0xd1258bbb3403bd92ULL}, + {0xc0314325637a193aULL, 0x056eeeaa0104acf7ULL}, + {0xf03d93eebc589f89ULL, 0x86caaa548145d835ULL}, + {0x96267c7535b763b6ULL, 0xb43eaa74d0cba721ULL}, + {0xbbb01b9283253ca3ULL, 0x614e551204fe90e9ULL}, + {0xea9c227723ee8bccULL, 0xb9a1ea56863e3523ULL}, + {0x92a1958a76751760ULL, 0xf405327613e6e136ULL}, + {0xb749faed14125d37ULL, 0x31067f1398e09984ULL}, + {0xe51c79a85916f485ULL, 0x7d481ed87f18bfe5ULL}, + {0x8f31cc0937ae58d3ULL, 0x2e4d13474f6f77efULL}, + {0xb2fe3f0b8599ef08ULL, 0x79e05819234b55eaULL}, + {0xdfbdcece67006acaULL, 0x98586e1f6c1e2b65ULL}, + {0x8bd6a141006042beULL, 0x1f3744d3a392db1fULL}, + {0xaecc49914078536eULL, 0xa70516088c7791e7ULL}, + {0xda7f5bf590966849ULL, 0x50c65b8aaf957661ULL}, + {0x888f99797a5e012eULL, 0x927bf936adbd69fcULL}, + {0xaab37fd7d8f58179ULL, 0x371af784592cc47cULL}, + {0xd5605fcdcf32e1d7ULL, 0x04e1b5656f77f59bULL}, + {0x855c3be0a17fcd27ULL, 0xa30d115f65aaf980ULL}, + {0xa6b34ad8c9dfc070ULL, 0x0bd055b73f15b7e1ULL}, + {0xd0601d8efc57b08cULL, 0x0ec46b250edb25d9ULL}, + {0x823c12795db6ce58ULL, 0x893ac2f72948f7a7ULL}, + {0xa2cb1717b52481eeULL, 0xab8973b4f39b3591ULL}, + {0xcb7ddcdda26da269ULL, 0x566bd0a2308202f6ULL}, + {0xfe5d54150b090b03ULL, 0x2c06c4cabca283b3ULL}, + {0x9efa548d26e5a6e2ULL, 0x3b843afeb5e59250ULL}, + {0xc6b8e9b0709f109bULL, 0xca6549be635ef6e4ULL}, + {0xf867241c8cc6d4c1ULL, 0x3cfe9c2dfc36b49dULL}, + {0x9b407691d7fc44f9ULL, 0x861f219cbda230e2ULL}, + {0xc21094364dfb5637ULL, 0x67a6ea03ed0abd1bULL}, + {0xf294b943e17a2bc5ULL, 0xc190a484e84d6c62ULL}, + {0x979cf3ca6cec5b5bULL, 0x58fa66d3113063bdULL}, + {0xbd8430bd08277232ULL, 0xaf390087d57c7cacULL}, + {0xece53cec4a314ebeULL, 0x5b0740a9cadb9bd7ULL}, + {0x940f4613ae5ed137ULL, 0x78e4886a1ec94166ULL}, + {0xb913179899f68585ULL, 0xd71daa84a67b91c0ULL}, + {0xe757dd7ec07426e6ULL, 0xcce51525d01a7630ULL}, + {0x9096ea6f38489850ULL, 0xc00f2d37a21089deULL}, + {0xb4bca50b065abe64ULL, 0xf012f8858a94ac56ULL}, + {0xe1ebce4dc7f16dfcULL, 0x2c17b6a6ed39d76bULL}, + {0x8d3360f09cf6e4beULL, 0x9b8ed228544426a3ULL}, + {0xb080392cc4349dedULL, 0x427286b26955304cULL}, + {0xdca04777f541c568ULL, 0x130f285f03aa7c5fULL}, + {0x89e42caaf9491b61ULL, 0x0be9793b624a8dbbULL}, + {0xac5d37d5b79b623aULL, 0xcee3d78a3add312aULL}, + {0xd77485cb25823ac8ULL, 0x829ccd6cc9947d74ULL}, + {0x86a8d39ef77164bdULL, 0x51a20063fdfcce68ULL}, + {0xa8530886b54dbdecULL, 0x260a807cfd7c0203ULL}, + {0xd267caa862a12d67ULL, 0x2f8d209c3cdb0284ULL}, + {0x8380dea93da4bc61ULL, 0xbdb83461a608e192ULL}, + {0xa46116538d0deb79ULL, 0xad26417a0f8b19f7ULL}, + {0xcd795be870516657ULL, 0x986fd1d8936de074ULL}, + {0x806bd9714632dff7ULL, 0xff45e3275c24ac49ULL}, + {0xa086cfcd97bf97f4ULL, 0x7f175bf1332dd75bULL}, + {0xc8a883c0fdaf7df1ULL, 0x9edd32ed7ff94d32ULL}, + {0xfad2a4b13d1b5d6dULL, 0x86947fa8dff7a07eULL}, + {0x9cc3a6eec6311a64ULL, 0x341ccfc98bfac44fULL}, + {0xc3f490aa77bd60fdULL, 0x412403bbeef97563ULL}, + {0xf4f1b4d515acb93cULL, 0x116d04aaeab7d2bbULL}, + {0x991711052d8bf3c6ULL, 0x8ae422ead2b2e3b5ULL}, + {0xbf5cd54678eef0b7ULL, 0x2d9d2ba5875f9ca2ULL}, + {0xef340a98172aace5ULL, 0x7904768ee93783cbULL}, + {0x9580869f0e7aac0fULL, 0x2ba2ca1951c2b25fULL}, + {0xbae0a846d2195713ULL, 0x768b7c9fa6335ef6ULL}, + {0xe998d258869facd8ULL, 0xd42e5bc78fc036b4ULL}, + {0x91ff83775423cc07ULL, 0x849cf95cb9d82230ULL}, + {0xb67f6455292cbf09ULL, 0xe5c437b3e84e2abdULL}, + {0xe41f3d6a7377eecbULL, 0xdf3545a0e261b56cULL}, + {0x8e938662882af53fULL, 0xab814b848d7d1163ULL}, + {0xb23867fb2a35b28eULL, 0x16619e65b0dc55bcULL}, + {0xdec681f9f4c31f32ULL, 0x9bfa05ff1d136b2bULL}, + {0x8b3c113c38f9f37fULL, 0x217c43bf722c22fbULL}, + {0xae0b158b4738705fULL, 0x69db54af4eb72bbaULL}, + {0xd98ddaee19068c77ULL, 0xc45229db2264f6a8ULL}, + {0x87f8a8d4cfa417caULL, 0x1ab35a28f57f1a29ULL}, + {0xa9f6d30a038d1dbdULL, 0xa16030b332dee0b3ULL}, + {0xd47487cc8470652cULL, 0x89b83cdfff9698e0ULL}, + {0x84c8d4dfd2c63f3cULL, 0xd613260bffbe1f8cULL}, + {0xa5fb0a17c777cf0aULL, 0x0b97ef8effada76fULL}, + {0xcf79cc9db955c2cdULL, 0x8e7deb72bf99114bULL}, + {0x81ac1fe293d599c0ULL, 0x390eb327b7bfaacfULL}, + {0xa21727db38cb0030ULL, 0x47525ff1a5af9583ULL}, + {0xca9cf1d206fdc03cULL, 0x5926f7ee0f1b7ae3ULL}, + {0xfd442e4688bd304bULL, 0x6f70b5e992e2599cULL}, + {0x9e4a9cec15763e2fULL, 0x65a671b1fbcd7801ULL}, + {0xc5dd44271ad3cdbbULL, 0xbf100e1e7ac0d602ULL}, + {0xf7549530e188c129ULL, 0x2ed411a619710b83ULL}, + {0x9a94dd3e8cf578baULL, 0x7d448b07cfe6a731ULL}, + {0xc13a148e3032d6e8ULL, 0x1c95adc9c3e050feULL}, + {0xf18899b1bc3f8ca2ULL, 0x23bb193c34d8653eULL}, + {0x96f5600f15a7b7e6ULL, 0xd654efc5a1073f46ULL}, + {0xbcb2b812db11a5dfULL, 0x8bea2bb709490f18ULL}, + {0xebdf661791d60f57ULL, 0xeee4b6a4cb9b52deULL}, + {0x936b9fcebb25c996ULL, 0x354ef226ff4113cbULL}, + {0xb84687c269ef3bfcULL, 0xc2a2aeb0bf1158bdULL}, + {0xe65829b3046b0afbULL, 0xf34b5a5ceed5aeedULL}, + {0x8ff71a0fe2c2e6ddULL, 0xb80f187a15458d54ULL}, + {0xb3f4e093db73a094ULL, 0xa612de989a96f0a9ULL}, + {0xe0f218b8d25088b9ULL, 0xcf97963ec13cacd3ULL}, + {0x8c974f7383725574ULL, 0xe1bebde738c5ec04ULL}, + {0xafbd2350644eead0ULL, 0x1a2e6d6106f76705ULL}, + {0xdbac6c247d62a584ULL, 0x20ba08b948b540c6ULL}, + {0x894bc396ce5da773ULL, 0x94744573cd71487cULL}, + {0xab9eb47c81f51150ULL, 0xf99156d0c0cd9a9bULL}, + {0xd686619ba27255a3ULL, 0x37f5ac84f1010142ULL}, + {0x8613fd0145877586ULL, 0x42f98bd316a0a0c9ULL}, + {0xa798fc4196e952e8ULL, 0xd3b7eec7dc48c8fbULL}, + {0xd17f3b51fca3a7a1ULL, 0x08a5ea79d35afb3aULL}, + {0x82ef85133de648c5ULL, 0x6567b28c2418dd04ULL}, + {0xa3ab66580d5fdaf6ULL, 0x3ec19f2f2d1f1445ULL}, + {0xcc963fee10b7d1b4ULL, 0xce7206faf866d957ULL}, + {0xffbbcfe994e5c620ULL, 0x020e88b9b6808fadULL}, + {0x9fd561f1fd0f9bd4ULL, 0x01491574121059ccULL}, + {0xc7caba6e7c5382c9ULL, 0x019b5ad11694703fULL}, + {0xf9bd690a1b68637cULL, 0xc20231855c398c4fULL}, + {0x9c1661a651213e2eULL, 0xf9415ef359a3f7b1ULL}, + {0xc31bfa0fe5698db9ULL, 0xb791b6b0300cf59dULL}, + {0xf3e2f893dec3f127ULL, 0xa576245c3c103305ULL}, + {0x986ddb5c6b3a76b8ULL, 0x0769d6b9a58a1fe3ULL}, + {0xbe89523386091466ULL, 0x09444c680eeca7dcULL}, + {0xee2ba6c0678b5980ULL, 0x8b955f8212a7d1d3ULL}, + {0x94db483840b717f0ULL, 0x573d5bb14ba8e323ULL}, + {0xba121a4650e4ddecULL, 0x6d0cb29d9e931becULL}, + {0xe896a0d7e51e1567ULL, 0x884fdf450637e2e8ULL}, + {0x915e2486ef32cd61ULL, 0xf531eb8b23e2edd1ULL}, + {0xb5b5ada8aaff80b9ULL, 0xf27e666decdba945ULL}, + {0xe3231912d5bf60e7ULL, 0xef1e000968129396ULL}, + {0x8df5efabc5979c90ULL, 0x3572c005e10b9c3eULL}, + {0xb1736b96b6fd83b4ULL, 0x42cf7007594e834dULL}, + {0xddd0467c64bce4a1ULL, 0x53834c092fa22421ULL}, + {0x8aa22c0dbef60ee5ULL, 0x94320f85bdc55694ULL}, + {0xad4ab7112eb3929eULL, 0x793e93672d36ac39ULL}, + {0xd89d64d57a607745ULL, 0x178e3840f8845748ULL}, + {0x87625f056c7c4a8cULL, 0xeeb8e3289b52b68dULL}, + {0xa93af6c6c79b5d2eULL, 0x2a671bf2c2276430ULL}, + {0xd389b4787982347aULL, 0xb500e2ef72b13d3cULL}, + {0x843610cb4bf160ccULL, 0x31208dd5a7aec645ULL}, + {0xa54394fe1eedb8ffULL, 0x3d68b14b119a77d7ULL}, + {0xce947a3da6a9273fULL, 0x8cc2dd9dd60115cdULL}, + {0x811ccc668829b888ULL, 0xf7f9ca82a5c0ada0ULL}, + {0xa163ff802a3426a9ULL, 0x35f83d234f30d908ULL}, + {0xc9bcff6034c13053ULL, 0x03764c6c22fd0f4aULL}, + {0xfc2c3f3841f17c68ULL, 0x4453df872bbc531dULL}, + {0x9d9ba7832936edc1ULL, 0x2ab46bb47b55b3f2ULL}, + {0xc5029163f384a932ULL, 0xf56186a19a2b20eeULL}, + {0xf64335bcf065d37eULL, 0xb2b9e84a00b5e92aULL}, + {0x99ea0196163fa42fULL, 0xafb4312e4071b1baULL}, + {0xc06481fb9bcf8d3aULL, 0x1ba13d79d08e1e29ULL}, + {0xf07da27a82c37089ULL, 0xa2898cd844b1a5b3ULL}, + {0x964e858c91ba2656ULL, 0xc595f8072aef0790ULL}, + {0xbbe226efb628afebULL, 0x76fb7608f5aac974ULL}, + {0xeadab0aba3b2dbe6ULL, 0xd4ba538b33157bd1ULL}, + {0x92c8ae6b464fc970ULL, 0xc4f47436ffed6d62ULL}, + {0xb77ada0617e3bbccULL, 0xf6319144bfe8c8bbULL}, + {0xe55990879ddcaabeULL, 0x33bdf595efe2faeaULL}, + {0x8f57fa54c2a9eab7ULL, 0x6056b97db5eddcd2ULL}, + {0xb32df8e9f3546565ULL, 0xb86c67dd23695406ULL}, + {0xdff9772470297ebeULL, 0xa68781d46c43a908ULL}, + {0x8bfbea76c619ef37ULL, 0xa814b124c3aa49a5ULL}, + {0xaefae51477a06b04ULL, 0x1219dd6df494dc0eULL}, + {0xdab99e59958885c5ULL, 0x16a054c971ba1312ULL}, + {0x88b402f7fd75539cULL, 0xee2434fde7144bebULL}, + {0xaae103b5fcd2a882ULL, 0x29ad423d60d95ee6ULL}, + {0xd59944a37c0752a3ULL, 0xb41892ccb90fb6a0ULL}, + {0x857fcae62d8493a6ULL, 0x908f5bbff3a9d224ULL}, + {0xa6dfbd9fb8e5b88fULL, 0x34b332aff09446adULL}, + {0xd097ad07a71f26b3ULL, 0x81dfff5becb95858ULL}, + {0x825ecc24c8737830ULL, 0x712bff9973f3d737ULL}, + {0xa2f67f2dfa90563cULL, 0x8d76ff7fd0f0cd05ULL}, + {0xcbb41ef979346bcbULL, 0xb0d4bf5fc52d0046ULL}, + {0xfea126b7d78186bdULL, 0x1d09ef37b6784057ULL}, + {0x9f24b832e6b0f437ULL, 0xf2263582d20b2836ULL}, + {0xc6ede63fa05d3144ULL, 0x6eafc2e3868df244ULL}, + {0xf8a95fcf88747d95ULL, 0x8a5bb39c68316ed5ULL}, + {0x9b69dbe1b548ce7dULL, 0x36795041c11ee545ULL}, + {0xc24452da229b021cULL, 0x0417a45231669e97ULL}, + {0xf2d56790ab41c2a3ULL, 0x051d8d66bdc0463cULL}, + {0x97c560ba6b0919a6ULL, 0x2332786036982be5ULL}, + {0xbdb6b8e905cb6010ULL, 0xabff1678443e36dfULL}, + {0xed246723473e3814ULL, 0xd6fedc16554dc497ULL}, + {0x9436c0760c86e30cULL, 0x065f498df5509adeULL}, + {0xb94470938fa89bcfULL, 0x07f71bf172a4c196ULL}, + {0xe7958cb87392c2c3ULL, 0x49f4e2edcf4df1fbULL}, + {0x90bd77f3483bb9baULL, 0x4e390dd4a190b73dULL}, + {0xb4ecd5f01a4aa829ULL, 0xe1c75149c9f4e50cULL}, + {0xe2280b6c20dd5233ULL, 0xda39259c3c721e4fULL}, + {0x8d590723948a5360ULL, 0xa863b781a5c752f1ULL}, + {0xb0af48ec79ace838ULL, 0xd27ca5620f3927aeULL}, + {0xdcdb1b2798182245ULL, 0x071bceba9307719aULL}, + {0x8a08f0f8bf0f156cULL, 0xe47161349be4a700ULL}, + {0xac8b2d36eed2dac6ULL, 0x1d8db981c2ddd0c0ULL}, + {0xd7adf884aa879178ULL, 0xa4f127e2339544f0ULL}, + {0x86ccbb52ea94baebULL, 0x6716b8ed603d4b16ULL}, + {0xa87fea27a539e9a6ULL, 0xc0dc6728b84c9ddbULL}, + {0xd29fe4b18e88640fULL, 0x711380f2e65fc552ULL}, + {0x83a3eeeef9153e8aULL, 0xe6ac3097cffbdb53ULL}, + {0xa48ceaaab75a8e2cULL, 0xa0573cbdc3fad228ULL}, + {0xcdb02555653131b7ULL, 0xc86d0bed34f986b2ULL}, + {0x808e17555f3ebf12ULL, 0x1d442774411bf42fULL}, + {0xa0b19d2ab70e6ed7ULL, 0xa49531515162f13bULL}, + {0xc8de047564d20a8cULL, 0x0dba7da5a5bbad8aULL}, + {0xfb158592be068d2fULL, 0x11291d0f0f2a98edULL}, + {0x9ced737bb6c4183eULL, 0xaab9b229697a9f94ULL}, + {0xc428d05aa4751e4dULL, 0x55681eb3c3d94779ULL}, + {0xf53304714d9265e0ULL, 0x2ac22660b4cf9957ULL}, + {0x993fe2c6d07b7facULL, 0x1ab957fc7101bfd6ULL}, + {0xbf8fdb78849a5f97ULL, 0x2167adfb8d422fccULL}, + {0xef73d256a5c0f77dULL, 0x69c1997a7092bbbfULL}, + {0x95a8637627989aaeULL, 0x2218ffec865bb557ULL}, + {0xbb127c53b17ec15aULL, 0xaa9f3fe7a7f2a2adULL}, + {0xe9d71b689dde71b0ULL, 0x55470fe191ef4b59ULL}, + {0x9226712162ab070eULL, 0x354c69ecfb358f17ULL}, + {0xb6b00d69bb55c8d2ULL, 0xc29f84683a02f2ddULL}, + {0xe45c10c42a2b3b06ULL, 0x734765824883af95ULL}, + {0x8eb98a7a9a5b04e4ULL, 0x880c9f716d524dbdULL}, + {0xb267ed1940f1c61dULL, 0xaa0fc74dc8a6e12cULL}, + {0xdf01e85f912e37a4ULL, 0x9493b9213ad09977ULL}, + {0x8b61313bbabce2c7ULL, 0xdcdc53b4c4c25feaULL}, + {0xae397d8aa96c1b78ULL, 0x541368a1f5f2f7e5ULL}, + {0xd9c7dced53c72256ULL, 0x691842ca736fb5deULL}, + {0x881cea14545c7576ULL, 0x81af29be8825d1abULL}, + {0xaa242499697392d3ULL, 0x221af42e2a2f4616ULL}, + {0xd4ad2dbfc3d07788ULL, 0x6aa1b139b4bb179bULL}, + {0x84ec3c97da624ab5ULL, 0x42a50ec410f4eec1ULL}, + {0xa6274bbdd0fadd62ULL, 0x134e527515322a71ULL}, + {0xcfb11ead453994bbULL, 0x9821e7125a7eb50dULL}, + {0x81ceb32c4b43fcf5ULL, 0x7f15306b788f3128ULL}, + {0xa2425ff75e14fc32ULL, 0x5eda7c8656b2fd72ULL}, + {0xcad2f7f5359a3b3fULL, 0xf6911ba7ec5fbccfULL}, + {0xfd87b5f28300ca0eULL, 0x74356291e777ac03ULL}, + {0x9e74d1b791e07e49ULL, 0x88a15d9b30aacb82ULL}, + {0xc612062576589ddbULL, 0x6ac9b501fcd57e62ULL}, + {0xf79687aed3eec552ULL, 0xc57c22427c0addfbULL}, + {0x9abe14cd44753b53ULL, 0x3b6d95698d86cabdULL}, + {0xc16d9a0095928a28ULL, 0x8a48fac3f0e87d6cULL}, + {0xf1c90080baf72cb2ULL, 0xacdb3974ed229cc7ULL}, + {0x971da05074da7befULL, 0x2c0903e91435a1fcULL}, + {0xbce5086492111aebULL, 0x770b44e359430a7bULL}, + {0xec1e4a7db69561a6ULL, 0xd4ce161c2f93cd1aULL}, + {0x9392ee8e921d5d08ULL, 0xc500cdd19dbc6030ULL}, + {0xb877aa3236a4b44aULL, 0xf6410146052b783dULL}, + {0xe69594bec44de15cULL, 0xb3d141978676564cULL}, + {0x901d7cf73ab0acdaULL, 0xf062c8feb409f5efULL}, + {0xb424dc35095cd810ULL, 0xac7b7b3e610c736bULL}, + {0xe12e13424bb40e14ULL, 0xd79a5a0df94f9046ULL}, + {0x8cbccc096f5088ccULL, 0x06c07848bbd1ba2cULL}, + {0xafebff0bcb24aaffULL, 0x0870965aeac628b7ULL}, + {0xdbe6fecebdedd5bfULL, 0x4a8cbbf1a577b2e4ULL}, + {0x89705f4136b4a598ULL, 0xce97f577076acfcfULL}, + {0xabcc77118461cefdULL, 0x023df2d4c94583c2ULL}, + {0xd6bf94d5e57a42bdULL, 0xc2cd6f89fb96e4b3ULL}, + {0x8637bd05af6c69b6ULL, 0x59c065b63d3e4ef0ULL}, + {0xa7c5ac471b478424ULL, 0xf0307f23cc8de2acULL}, + {0xd1b71758e219652cULL, 0x2c3c9eecbfb15b57ULL}, + {0x83126e978d4fdf3cULL, 0x9ba5e353f7ced916ULL}, + {0xa3d70a3d70a3d70bULL, 0xc28f5c28f5c28f5cULL}, + {0xcccccccccccccccdULL, 0x3333333333333333ULL}, + {0x8000000000000000ULL, 0x0000000000000000ULL}, + {0xa000000000000000ULL, 0x0000000000000000ULL}, + {0xc800000000000000ULL, 0x0000000000000000ULL}, + {0xfa00000000000000ULL, 0x0000000000000000ULL}, + {0x9c40000000000000ULL, 0x0000000000000000ULL}, + {0xc350000000000000ULL, 0x0000000000000000ULL}, + {0xf424000000000000ULL, 0x0000000000000000ULL}, + {0x9896800000000000ULL, 0x0000000000000000ULL}, + {0xbebc200000000000ULL, 0x0000000000000000ULL}, + {0xee6b280000000000ULL, 0x0000000000000000ULL}, + {0x9502f90000000000ULL, 0x0000000000000000ULL}, + {0xba43b74000000000ULL, 0x0000000000000000ULL}, + {0xe8d4a51000000000ULL, 0x0000000000000000ULL}, + {0x9184e72a00000000ULL, 0x0000000000000000ULL}, + {0xb5e620f480000000ULL, 0x0000000000000000ULL}, + {0xe35fa931a0000000ULL, 0x0000000000000000ULL}, + {0x8e1bc9bf04000000ULL, 0x0000000000000000ULL}, + {0xb1a2bc2ec5000000ULL, 0x0000000000000000ULL}, + {0xde0b6b3a76400000ULL, 0x0000000000000000ULL}, + {0x8ac7230489e80000ULL, 0x0000000000000000ULL}, + {0xad78ebc5ac620000ULL, 0x0000000000000000ULL}, + {0xd8d726b7177a8000ULL, 0x0000000000000000ULL}, + {0x878678326eac9000ULL, 0x0000000000000000ULL}, + {0xa968163f0a57b400ULL, 0x0000000000000000ULL}, + {0xd3c21bcecceda100ULL, 0x0000000000000000ULL}, + {0x84595161401484a0ULL, 0x0000000000000000ULL}, + {0xa56fa5b99019a5c8ULL, 0x0000000000000000ULL}, + {0xcecb8f27f4200f3aULL, 0x0000000000000000ULL}, + {0x813f3978f8940985ULL, 0xc000000000000000ULL}, + {0xa18f07d736b90be6ULL, 0xb000000000000000ULL}, + {0xc9f2c9cd04674edfULL, 0x5c00000000000000ULL}, + {0xfc6f7c4045812297ULL, 0xb300000000000000ULL}, + {0x9dc5ada82b70b59eULL, 0x0fe0000000000000ULL}, + {0xc5371912364ce306ULL, 0x93d8000000000000ULL}, + {0xf684df56c3e01bc7ULL, 0x38ce000000000000ULL}, + {0x9a130b963a6c115dULL, 0xc380c00000000000ULL}, + {0xc097ce7bc90715b4ULL, 0xb460f00000000000ULL}, + {0xf0bdc21abb48db21ULL, 0xe1792c0000000000ULL}, + {0x96769950b50d88f5ULL, 0xecebbb8000000000ULL}, + {0xbc143fa4e250eb32ULL, 0xe826aa6000000000ULL}, + {0xeb194f8e1ae525feULL, 0xa23054f800000000ULL}, + {0x92efd1b8d0cf37bfULL, 0xa55e351b00000000ULL}, + {0xb7abc627050305aeULL, 0x0eb5c261c0000000ULL}, + {0xe596b7b0c643c71aULL, 0x926332fa30000000ULL}, + {0x8f7e32ce7bea5c70ULL, 0x1b7dffdc5e000000ULL}, + {0xb35dbf821ae4f38cULL, 0x225d7fd375800000ULL}, + {0xe0352f62a19e306fULL, 0x2af4dfc852e00000ULL}, + {0x8c213d9da502de46ULL, 0xbad90bdd33cc0000ULL}, + {0xaf298d050e4395d7ULL, 0x698f4ed480bf0000ULL}, + {0xdaf3f04651d47b4dULL, 0xc3f32289a0eec000ULL}, + {0x88d8762bf324cd10ULL, 0x5a77f59604953800ULL}, + {0xab0e93b6efee0054ULL, 0x7115f2fb85ba8600ULL}, + {0xd5d238a4abe98069ULL, 0x8d5b6fba67292780ULL}, + {0x85a36366eb71f042ULL, 0xb85925d48079b8b0ULL}, + {0xa70c3c40a64e6c52ULL, 0x666f6f49a09826dcULL}, + {0xd0cf4b50cfe20766ULL, 0x000b4b1c08be3093ULL}, + {0x82818f1281ed44a0ULL, 0x40070ef18576de5bULL}, + {0xa321f2d7226895c8ULL, 0x5008d2ade6d495f2ULL}, + {0xcbea6f8ceb02bb3aULL, 0x640b07596089bb6fULL}, + {0xfee50b7025c36a09ULL, 0xfd0dc92fb8ac2a4bULL}, + {0x9f4f2726179a2246ULL, 0xfe289dbdd36b9a6fULL}, + {0xc722f0ef9d80aad7ULL, 0xbdb2c52d4846810aULL}, + {0xf8ebad2b84e0d58cULL, 0x2d1f76789a58214dULL}, + {0x9b934c3b330c8578ULL, 0x9c33aa0b607714d0ULL}, + {0xc2781f49ffcfa6d6ULL, 0xc340948e3894da04ULL}, + {0xf316271c7fc3908bULL, 0x7410b9b1c6ba1085ULL}, + {0x97edd871cfda3a57ULL, 0x688a740f1c344a53ULL}, + {0xbde94e8e43d0c8edULL, 0xc2ad1112e3415ce8ULL}, + {0xed63a231d4c4fb28ULL, 0xb35855579c11b422ULL}, + {0x945e455f24fb1cf9ULL, 0x70173556c18b1095ULL}, + {0xb975d6b6ee39e437ULL, 0x4c1d02ac71edd4bbULL}, + {0xe7d34c64a9c85d45ULL, 0x9f2443578e6949e9ULL}, + {0x90e40fbeea1d3a4bULL, 0x4376aa16b901ce32ULL}, + {0xb51d13aea4a488deULL, 0x9454549c674241beULL}, + {0xe264589a4dcdab15ULL, 0x396969c38112d22eULL}, + {0x8d7eb76070a08aedULL, 0x03e1e21a30abc35dULL}, + {0xb0de65388cc8ada9ULL, 0xc4da5aa0bcd6b434ULL}, + {0xdd15fe86affad913ULL, 0xb610f148ec0c6141ULL}, + {0x8a2dbf142dfcc7acULL, 0x91ca96cd9387bcc8ULL}, + {0xacb92ed9397bf997ULL, 0xb63d3c80f869abfbULL}, + {0xd7e77a8f87daf7fcULL, 0x23cc8ba1368416f9ULL}, + {0x86f0ac99b4e8dafeULL, 0x965fd744c2128e5cULL}, + {0xa8acd7c0222311bdULL, 0x3bf7cd15f29731f3ULL}, + {0xd2d80db02aabd62cULL, 0x0af5c05b6f3cfe6fULL}, + {0x83c7088e1aab65dcULL, 0x86d9983925861f05ULL}, + {0xa4b8cab1a1563f53ULL, 0xa88ffe476ee7a6c7ULL}, + {0xcde6fd5e09abcf27ULL, 0x12b3fdd94aa19079ULL}, + {0x80b05e5ac60b6179ULL, 0xabb07ea7cea4fa4bULL}, + {0xa0dc75f1778e39d7ULL, 0x969c9e51c24e38deULL}, + {0xc913936dd571c84dULL, 0xfc43c5e632e1c716ULL}, + {0xfb5878494ace3a60ULL, 0xfb54b75fbf9a38dcULL}, + {0x9d174b2dcec0e47cULL, 0x9d14f29bd7c06389ULL}, + {0xc45d1df942711d9bULL, 0xc45a2f42cdb07c6bULL}, + {0xf5746577930d6501ULL, 0x3570bb13811c9b86ULL}, + {0x9968bf6abbe85f21ULL, 0x816674ec30b1e134ULL}, + {0xbfc2ef456ae276e9ULL, 0x61c012273cde5981ULL}, + {0xefb3ab16c59b14a3ULL, 0x3a3016b10c15efe1ULL}, + {0x95d04aee3b80ece6ULL, 0x445e0e2ea78db5edULL}, + {0xbb445da9ca612820ULL, 0xd57591ba51712368ULL}, + {0xea1575143cf97227ULL, 0x0ad2f628e5cd6c42ULL}, + {0x924d692ca61be759ULL, 0xa6c3d9d98fa063a9ULL}, + {0xb6e0c377cfa2e12fULL, 0x9074d04ff3887c93ULL}, + {0xe498f455c38b997bULL, 0xf4920463f06a9bb8ULL}, + {0x8edf98b59a373fedULL, 0xb8db42be7642a153ULL}, + {0xb2977ee300c50fe8ULL, 0xa712136e13d349a8ULL}, + {0xdf3d5e9bc0f653e2ULL, 0xd0d6984998c81c12ULL}, + {0x8b865b215899f46dULL, 0x42861f2dff7d118bULL}, + {0xae67f1e9aec07188ULL, 0x1327a6f97f5c55eeULL}, + {0xda01ee641a708deaULL, 0x17f190b7df336b6aULL}, + {0x884134fe908658b3ULL, 0xcef6fa72eb802322ULL}, + {0xaa51823e34a7eedfULL, 0x42b4b90fa6602beaULL}, + {0xd4e5e2cdc1d1ea97ULL, 0x9361e7538ff836e5ULL}, + {0x850fadc09923329fULL, 0xfc1d309439fb224fULL}, + {0xa6539930bf6bff46ULL, 0x7b247cb94879eae3ULL}, + {0xcfe87f7cef46ff17ULL, 0x19ed9be79a98659cULL}, + {0x81f14fae158c5f6fULL, 0xb0348170c09f3f81ULL}, + {0xa26da3999aef774aULL, 0x1c41a1ccf0c70f62ULL}, + {0xcb090c8001ab551dULL, 0xa3520a402cf8d33aULL}, + {0xfdcb4fa002162a64ULL, 0x8c268cd038370809ULL}, + {0x9e9f11c4014dda7fULL, 0xd798180223226505ULL}, + {0xc646d63501a1511eULL, 0x4d7e1e02abeafe47ULL}, + {0xf7d88bc24209a566ULL, 0xe0dda58356e5bdd9ULL}, + {0x9ae7575969460760ULL, 0xcc8a8772164f96a7ULL}, + {0xc1a12d2fc3978938ULL, 0xffad294e9be37c51ULL}, + {0xf209787bb47d6b85ULL, 0x3f9873a242dc5b65ULL}, + {0x9745eb4d50ce6333ULL, 0x07bf484569c9b91fULL}, + {0xbd176620a501fc00ULL, 0x49af1a56c43c2767ULL}, + {0xec5d3fa8ce427b00ULL, 0x5c1ae0ec754b3141ULL}, + {0x93ba47c980e98ce0ULL, 0x3990cc93c94efec8ULL}, + {0xb8a8d9bbe123f018ULL, 0x47f4ffb8bba2be7bULL}, + {0xe6d3102ad96cec1eULL, 0x59f23fa6ea8b6e1aULL}, + {0x9043ea1ac7e41393ULL, 0x783767c8529724d0ULL}, + {0xb454e4a179dd1878ULL, 0xd64541ba673cee04ULL}, + {0xe16a1dc9d8545e95ULL, 0x0bd69229010c2985ULL}, + {0x8ce2529e2734bb1eULL, 0xe7661b59a0a799f3ULL}, + {0xb01ae745b101e9e5ULL, 0xa13fa23008d18070ULL}, + {0xdc21a1171d42645eULL, 0x898f8abc0b05e08cULL}, + {0x899504ae72497ebbULL, 0x95f9b6b586e3ac57ULL}, + {0xabfa45da0edbde6aULL, 0xfb782462e89c976dULL}, + {0xd6f8d7509292d604ULL, 0xba562d7ba2c3bd49ULL}, + {0x865b86925b9bc5c3ULL, 0xf475dc6d45ba564dULL}, + {0xa7f26836f282b733ULL, 0x719353889728ebe1ULL}, + {0xd1ef0244af236500ULL, 0xcdf8286abcf326d9ULL}, + {0x8335616aed761f20ULL, 0x80bb1942b617f847ULL}, + {0xa402b9c5a8d3a6e8ULL, 0xa0e9df93639df659ULL}, + {0xcd036837130890a2ULL, 0xc92457783c8573f0ULL}, + {0x802221226be55a65ULL, 0x3db6b6ab25d36876ULL}, + {0xa02aa96b06deb0feULL, 0x0d246455ef484293ULL}, + {0xc83553c5c8965d3eULL, 0x906d7d6b6b1a5338ULL}, + {0xfa42a8b73abbf48dULL, 0x3488dcc645e0e806ULL}, + {0x9c69a97284b578d8ULL, 0x00d589fbebac9104ULL}, + {0xc38413cf25e2d70eULL, 0x010aec7ae697b545ULL}, + {0xf46518c2ef5b8cd2ULL, 0x814da799a03da296ULL}, + {0x98bf2f79d5993803ULL, 0x10d088c00426859eULL}, + {0xbeeefb584aff8604ULL, 0x5504aaf005302705ULL}, + {0xeeaaba2e5dbf6785ULL, 0x6a45d5ac067c30c7ULL}, + {0x952ab45cfa97a0b3ULL, 0x226ba58b840d9e7cULL}, + {0xba756174393d88e0ULL, 0x6b068eee6511061bULL}, + {0xe912b9d1478ceb18ULL, 0x85c832a9fe5547a2ULL}, + {0x91abb422ccb812efULL, 0x539d1faa3ef54cc5ULL}, + {0xb616a12b7fe617abULL, 0xa8846794ceb29ff6ULL}, + {0xe39c49765fdf9d95ULL, 0x12a5817a025f47f4ULL}, + {0x8e41ade9fbebc27eULL, 0xeba770ec417b8cf8ULL}, + {0xb1d219647ae6b31dULL, 0xa6914d2751da7037ULL}, + {0xde469fbd99a05fe4ULL, 0x9035a07126510c44ULL}, + {0x8aec23d680043befULL, 0xda218446b7f2a7abULL}, + {0xada72ccc20054aeaULL, 0x50a9e55865ef5195ULL}, + {0xd910f7ff28069da5ULL, 0xe4d45eae7f6b25fbULL}, + {0x87aa9aff79042287ULL, 0x6f04bb2d0fa2f7bdULL}, + {0xa99541bf57452b29ULL, 0xcac5e9f8538bb5acULL}, + {0xd3fa922f2d1675f3ULL, 0xbd776476686ea317ULL}, + {0x847c9b5d7c2e09b8ULL, 0x966a9eca014525eeULL}, + {0xa59bc234db398c26ULL, 0xbc05467c81966f6aULL}, + {0xcf02b2c21207ef2fULL, 0x6b06981ba1fc0b44ULL}, + {0x8161afb94b44f57eULL, 0xe2e41f11453d870aULL}, + {0xa1ba1ba79e1632ddULL, 0x9b9d26d5968ce8cdULL}, + {0xca28a291859bbf94ULL, 0x8284708afc302301ULL}, + {0xfcb2cb35e702af79ULL, 0xa3258cadbb3c2bc1ULL}, + {0x9defbf01b061adacULL, 0xc5f777ec95059b58ULL}, + {0xc56baec21c7a1917ULL, 0xf77555e7ba47022fULL}, + {0xf6c69a72a3989f5cULL, 0x7552ab61a8d8c2baULL}, + {0x9a3c2087a63f639aULL, 0xc953ab1d098779b4ULL}, + {0xc0cb28a98fcf3c80ULL, 0x7ba895e44be95822ULL}, + {0xf0fdf2d3f3c30ba0ULL, 0x9a92bb5d5ee3ae2aULL}, + {0x969eb7c47859e744ULL, 0x609bb51a5b4e4cdaULL}, + {0xbc4665b596706115ULL, 0x78c2a260f221e011ULL}, + {0xeb57ff22fc0c795aULL, 0x56f34af92eaa5815ULL}, + {0x9316ff75dd87cbd9ULL, 0xf6580edbbd2a770dULL}, + {0xb7dcbf5354e9becfULL, 0xf3ee1292ac7514d0ULL}, + {0xe5d3ef282a242e82ULL, 0x70e9973757925a05ULL}, + {0x8fa475791a569d11ULL, 0x0691fe8296bb7843ULL}, + {0xb38d92d760ec4456ULL, 0xc8367e233c6a5653ULL}, + {0xe070f78d3927556bULL, 0x7a441dac0b84ebe8ULL}, + {0x8c469ab843b89563ULL, 0x6c6a928b87331371ULL}, + {0xaf58416654a6babcULL, 0xc785372e68ffd84dULL}, + {0xdb2e51bfe9d0696bULL, 0xf96684fa033fce61ULL}, + {0x88fcf317f22241e3ULL, 0xbbe0131c4207e0fcULL}, + {0xab3c2fddeeaad25bULL, 0x2ad817e35289d93cULL}, + {0xd60b3bd56a5586f2ULL, 0x758e1ddc272c4f8bULL}, + {0x85c7056562757457ULL, 0x0978d2a9987bb1b6ULL}, + {0xa738c6bebb12d16dULL, 0x4bd70753fe9a9e24ULL}, + {0xd106f86e69d785c8ULL, 0x1eccc928fe4145adULL}, + {0x82a45b450226b39dULL, 0x133ffdb99ee8cb8cULL}, + {0xa34d721642b06085ULL, 0xd80ffd2806a2fe6fULL}, + {0xcc20ce9bd35c78a6ULL, 0xce13fc72084bbe0bULL}, + {0xff290242c83396cfULL, 0x8198fb8e8a5ead8eULL}, + {0x9f79a169bd203e42ULL, 0xf0ff9d39167b2c79ULL}, + {0xc75809c42c684dd2ULL, 0xad3f84875c19f797ULL}, + {0xf92e0c3537826146ULL, 0x588f65a93320757dULL}, + {0x9bbcc7a142b17cccULL, 0x77599f89bff4496eULL}, + {0xc2abf989935ddbffULL, 0x9530076c2ff15bcaULL}, + {0xf356f7ebf83552ffULL, 0xfa7c09473bedb2bcULL}, + {0x98165af37b2153dfULL, 0x3c8d85cc85748fb5ULL}, + {0xbe1bf1b059e9a8d7ULL, 0x8bb0e73fa6d1b3a3ULL}, + {0xeda2ee1c7064130dULL, 0xee9d210f9086208cULL}, + {0x9485d4d1c63e8be8ULL, 0x752234a9ba53d457ULL}, + {0xb9a74a0637ce2ee2ULL, 0x926ac1d428e8c96dULL}, + {0xe8111c87c5c1ba9aULL, 0x370572493322fbc8ULL}, + {0x910ab1d4db9914a1ULL, 0xe263676dbff5dd5dULL}, + {0xb54d5e4a127f59c9ULL, 0xdafc41492ff354b4ULL}, + {0xe2a0b5dc971f303bULL, 0xd1bb519b7bf029e2ULL}, + {0x8da471a9de737e25ULL, 0xa31513012d761a2dULL}, + {0xb10d8e1456105daeULL, 0x8bda57c178d3a0b8ULL}, + {0xdd50f1996b947519ULL, 0x2ed0edb1d70888e6ULL}, + {0x8a5296ffe33cc930ULL, 0x7d42948f26655590ULL}, + {0xace73cbfdc0bfb7cULL, 0x9c9339b2effeaaf4ULL}, + {0xd8210befd30efa5bULL, 0xc3b8081fabfe55b1ULL}, + {0x8714a775e3e95c79ULL, 0x9a530513cb7ef58eULL}, + {0xa8d9d1535ce3b397ULL, 0x80e7c658be5eb2f2ULL}, + {0xd31045a8341ca07dULL, 0xe121b7eeedf65fafULL}, + {0x83ea2b892091e44eULL, 0x6cb512f554b9fbcdULL}, + {0xa4e4b66b68b65d61ULL, 0x07e257b2a9e87ac0ULL}, + {0xce1de40642e3f4baULL, 0xc9daed9f54629971ULL}, + {0x80d2ae83e9ce78f4ULL, 0x3e28d48394bd9fe6ULL}, + {0xa1075a24e4421731ULL, 0x4db309a479ed07e0ULL}, + {0xc94930ae1d529cfdULL, 0x211fcc0d986849d8ULL}, + {0xfb9b7cd9a4a7443dULL, 0xe967bf10fe825c4eULL}, + {0x9d412e0806e88aa6ULL, 0x71e0d76a9f1179b1ULL}, + {0xc491798a08a2ad4fULL, 0x0e590d4546d5d81dULL}, + {0xf5b5d7ec8acb58a3ULL, 0x51ef5096988b4e24ULL}, + {0x9991a6f3d6bf1766ULL, 0x5335925e1f5710d6ULL}, + {0xbff610b0cc6edd40ULL, 0xe802f6f5a72cd50cULL}, + {0xeff394dcff8a948fULL, 0x2203b4b310f80a4fULL}, + {0x95f83d0a1fb69cdaULL, 0xb54250efea9b0671ULL}, + {0xbb764c4ca7a44410ULL, 0x6292e52be541c80eULL}, + {0xea53df5fd18d5514ULL, 0x7b379e76de923a12ULL}, + {0x92746b9be2f8552dULL, 0xcd02c30a4b1b644bULL}, + {0xb7118682dbb66a78ULL, 0xc04373ccdde23d5eULL}, + {0xe4d5e82392a40516ULL, 0xf05450c0155accb5ULL}, + {0x8f05b1163ba6832eULL, 0xd634b2780d58bff1ULL}, + {0xb2c71d5bca9023f9ULL, 0x8bc1df1610aeefedULL}, + {0xdf78e4b2bd342cf7ULL, 0x6eb256db94daabe9ULL}, + {0x8bab8eefb6409c1bULL, 0xe52f76493d08ab71ULL}, + {0xae9672aba3d0c321ULL, 0x5e7b53db8c4ad64eULL}, + {0xda3c0f568cc4f3e9ULL, 0x361a28d26f5d8be1ULL}, + {0x8865899617fb1872ULL, 0x81d05983859a776dULL}, + {0xaa7eebfb9df9de8eULL, 0x22446fe467011548ULL}, + {0xd51ea6fa85785632ULL, 0xaad58bdd80c15a9aULL}, + {0x8533285c936b35dfULL, 0x2ac5776a7078d8a0ULL}, + {0xa67ff273b8460357ULL, 0x7576d5450c970ec8ULL}, + {0xd01fef10a657842dULL, 0xd2d48a964fbcd27aULL}, + {0x8213f56a67f6b29cULL, 0x63c4d69df1d6038cULL}, + {0xa298f2c501f45f43ULL, 0x7cb60c456e4b8470ULL}, + {0xcb3f2f7642717714ULL, 0xdbe38f56c9de658cULL}, + {0xfe0efb53d30dd4d8ULL, 0x12dc732c7c55feefULL}, + {0x9ec95d1463e8a507ULL, 0x0bc9c7fbcdb5bf55ULL}, + {0xc67bb4597ce2ce49ULL, 0x4ebc39fac1232f2aULL}, + {0xf81aa16fdc1b81dbULL, 0x226b4879716bfaf5ULL}, + {0x9b10a4e5e9913129ULL, 0x35830d4be6e37cd9ULL}, + {0xc1d4ce1f63f57d73ULL, 0x02e3d09ee09c5c0fULL}, + {0xf24a01a73cf2dcd0ULL, 0x439cc4c698c37313ULL}, + {0x976e41088617ca02ULL, 0x2a41fafc1f7a27ecULL}, + {0xbd49d14aa79dbc83ULL, 0xb4d279bb2758b1e7ULL}, + {0xec9c459d51852ba3ULL, 0x22071829f12ede61ULL}, + {0x93e1ab8252f33b46ULL, 0x35446f1a36bd4afcULL}, + {0xb8da1662e7b00a18ULL, 0xc2958ae0c46c9dbcULL}, + {0xe7109bfba19c0c9eULL, 0xf33aed98f587c52bULL}, + {0x906a617d450187e3ULL, 0xd804d47f9974db3aULL}, + {0xb484f9dc9641e9dbULL, 0x4e06099f7fd21209ULL}, + {0xe1a63853bbd26452ULL, 0xa1878c075fc6968cULL}, + {0x8d07e33455637eb3ULL, 0x24f4b7849bdc1e17ULL}, + {0xb049dc016abc5e60ULL, 0x6e31e565c2d3259dULL}, + {0xdc5c5301c56b75f8ULL, 0x89be5ebf3387ef04ULL}, + {0x89b9b3e11b6329bbULL, 0x5616fb378034f562ULL}, + {0xac2820d9623bf42aULL, 0xab9cba05604232bbULL}, + {0xd732290fbacaf134ULL, 0x5683e886b852bf6aULL}, + {0x867f59a9d4bed6c1ULL, 0xb61271543333b7a2ULL}, + {0xa81f301449ee8c71ULL, 0xa3970da94000a58bULL}, + {0xd226fc195c6a2f8dULL, 0x8c7cd1139000ceeeULL}, + {0x83585d8fd9c25db8ULL, 0x37ce02ac3a008154ULL}, + {0xa42e74f3d032f526ULL, 0x45c183574880a1aaULL}, + {0xcd3a1230c43fb270ULL, 0xd731e42d1aa0ca14ULL}, + {0x80444b5e7aa7cf86ULL, 0x867f2e9c30a47e4cULL}, + {0xa0555e361951c367ULL, 0x281efa433ccd9de0ULL}, + {0xc86ab5c39fa63441ULL, 0x7226b8d40c010558ULL}, + {0xfa856334878fc151ULL, 0x4eb067090f0146aeULL}, + {0x9c935e00d4b9d8d3ULL, 0x912e4065a960cc2cULL}, + {0xc3b8358109e84f08ULL, 0xf579d07f13b8ff37ULL}, + {0xf4a642e14c6262c9ULL, 0x32d8449ed8a73f05ULL}, + {0x98e7e9cccfbd7dbeULL, 0x7fc72ae347688763ULL}, + {0xbf21e44003acdd2dULL, 0x1fb8f59c1942a93cULL}, + {0xeeea5d5004981479ULL, 0xe7a733031f93538bULL}, + {0x95527a5202df0cccULL, 0xf0c87fe1f3bc1437ULL}, + {0xbaa718e68396cffeULL, 0x2cfa9fda70ab1945ULL}, + {0xe950df20247c83feULL, 0xb83947d10cd5df96ULL}, + {0x91d28b7416cdd27fULL, 0xb323cce2a805abbeULL}, + {0xb6472e511c81471eULL, 0x1fecc01b520716adULL}, + {0xe3d8f9e563a198e6ULL, 0xa7e7f0222688dc59ULL}, + {0x8e679c2f5e44ff90ULL, 0xa8f0f615581589b7ULL}, + {0xb201833b35d63f74ULL, 0xd32d339aae1aec25ULL}, + {0xde81e40a034bcf50ULL, 0x07f8808159a1a72eULL}, + {0x8b112e86420f6192ULL, 0x04fb5050d805087dULL}, + {0xadd57a27d29339f7ULL, 0x863a24650e064a9cULL}, + {0xd94ad8b1c7380875ULL, 0xe7c8ad7e5187dd43ULL}, + {0x87cec76f1c830549ULL, 0x70dd6c6ef2f4ea4aULL}, + {0xa9c2794ae3a3c69bULL, 0x4d14c78aafb224ddULL}, + {0xd433179d9c8cb842ULL, 0xa059f96d5b9eae14ULL}, + {0x849feec281d7f329ULL, 0x24383be459432cccULL}, + {0xa5c7ea73224deff4ULL, 0xed464add6f93f7ffULL}, + {0xcf39e50feae16bf0ULL, 0x2897dd94cb78f5ffULL}, + {0x81842f29f2cce376ULL, 0x195eea7cff2b99bfULL}, + {0xa1e53af46f801c54ULL, 0x9fb6a51c3ef6802fULL}, + {0xca5e89b18b602369ULL, 0xc7a44e634eb4203bULL}, + {0xfcf62c1dee382c43ULL, 0xb98d61fc2261284aULL}, + {0x9e19db92b4e31baaULL, 0x93f85d3d957cb92eULL}, + {0xc5a05277621be294ULL, 0x38f6748cfadbe77aULL}, + {0xf70867153aa2db39ULL, 0x473411b03992e158ULL}, + {0x9a65406d44a5c904ULL, 0x8c808b0e23fbccd7ULL}, + {0xc0fe908895cf3b45ULL, 0xafa0add1acfac00dULL}, + {0xf13e34aabb430a16ULL, 0x9b88d94618397010ULL}, + {0x96c6e0eab509e64eULL, 0xa13587cbcf23e60aULL}, + {0xbc789925624c5fe1ULL, 0x4982e9bec2ecdf8dULL}, + {0xeb96bf6ebadf77d9ULL, 0x1be3a42e73a81770ULL}, + {0x933e37a534cbaae8ULL, 0x716e469d08490ea6ULL}, + {0xb80dc58e81fe95a2ULL, 0x8dc9d8444a5b524fULL}, + {0xe61136f2227e3b0aULL, 0x313c4e555cf226e3ULL}, + {0x8fcac257558ee4e7ULL, 0xdec5b0f55a17584eULL}, + {0xb3bd72ed2af29e20ULL, 0x56771d32b09d2e62ULL}, + {0xe0accfa875af45a8ULL, 0x6c14e47f5cc479faULL}, + {0x8c6c01c9498d8b89ULL, 0x438d0ecf99facc3cULL}, + {0xaf87023b9bf0ee6bULL, 0x1470528380797f4bULL}, + {0xdb68c2ca82ed2a06ULL, 0x598c67246097df1eULL}, +}; + +/* ======== Uscale core ======== */ + +static inline int log10_pow2(int x) +{ + return (x * 78913) >> 18; +} + +static inline int log2_pow10(int x) +{ + return (x * 108853) >> 15; +} + +typedef struct { + pow10_entry pm; + int s; +} scaler; + +static inline scaler prescale(int e, int p, int lp) +{ + scaler c; + c.pm = pow10_tab[p - POW10_MIN]; + c.s = -(e + lp + 3); + return c; +} + +typedef uint64_t unrounded; + +static inline uint64_t ur_floor(unrounded u) { return (u + 0) >> 2; } +static inline uint64_t ur_round(unrounded u) { return (u + 1 + ((u >> 2) & 1)) >> 2; } +static inline uint64_t ur_ceil(unrounded u) { return (u + 3) >> 2; } + +static inline unrounded ur_div(unrounded u, uint64_t d) +{ + return (u / d) | (u & 1) | (u % d != 0 ? 1 : 0); +} + +static inline unrounded ur_nudge(unrounded u, int delta) +{ + return u + delta; +} + +static unrounded uscale(uint64_t x, scaler c) +{ + uint64_t hi, mid, mid2, lo_unused; + mul64(x, c.pm.hi, &hi, &mid); + + uint64_t sticky = 1; + uint64_t mask = (1ULL << (c.s & 63)) - 1; + + if ((hi & mask) == 0) { + mul64(x, c.pm.lo, &mid2, &lo_unused); + sticky = (mid - mid2) > 1 ? 1 : 0; + hi -= (mid < mid2) ? 1 : 0; + } + + return (hi >> c.s) | sticky; +} + +static void unpack64(double f, uint64_t *m, int *e) +{ + const int shift = 64 - 53; + const int min_exp = -(1074 + shift); + union { double d; uint64_t u; } u; + u.d = f; + uint64_t bits = u.u; + int exp; + + *m = (1ULL << 63) | ((bits & ((1ULL << 52) - 1)) << shift); + exp = (int)((bits >> 52) & ((1 << shift) - 1)); + + if (exp == 0) { + int s; + *m &= ~(1ULL << 63); + *e = min_exp; + s = clz64(*m); + *m <<= s; + *e -= s; + } + else { + *e = (exp - 1) + min_exp; + } +} + +static double pack64(uint64_t m, int e) +{ + union { double d; uint64_t u; } u; + if ((m & (1ULL << 52)) == 0) { + /* subnormal */ + u.u = m; + } + else { + int biased = 1075 + e; + if (biased >= 2047) { + /* exponent overflow -> infinity */ + u.u = 0x7FF0000000000000ULL; + } + else { + u.u = (m & ~(1ULL << 52)) | ((uint64_t)biased << 52); + } + } + return u.d; +} + +/* ======== Algorithm helpers ======== */ + +static const uint64_t uint64_pow10[20] = { + 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, + 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL, 1000000000ULL, + 10000000000ULL, 100000000000ULL, 1000000000000ULL, 10000000000000ULL, + 100000000000000ULL, 1000000000000000ULL, 10000000000000000ULL, + 100000000000000000ULL, 1000000000000000000ULL, 10000000000000000000ULL, +}; + +static int count_digits(uint64_t d) +{ + int nd = log10_pow2(bits_len64(d)); + return nd + (d >= uint64_pow10[nd] ? 1 : 0); +} + +static void fixed_width(double f, int n, uint64_t *d, int *p) +{ + uint64_t m; + int e; + unrounded u; + + unpack64(f, &m, &e); + *p = n - 1 - log10_pow2(e + 63); + u = uscale(m, prescale(e, *p, log2_pow10(*p))); + *d = ur_round(u); + + if (*d >= uint64_pow10[n]) { + *d = ur_round(ur_div(u, 10)); + (*p)--; + } + *p = -(*p); +} + +static double parse_decimal(uint64_t d, int p) +{ + int b, lp, e; + unrounded u; + uint64_t m; + + if (d == 0) return 0.0; + + b = bits_len64(d); + lp = log2_pow10(p); + e = 53 - b - lp; + if (e > 1074) e = 1074; + + u = uscale(d << (64 - b), prescale(e - (64 - b), p, lp)); + m = ur_round(u); + + if (m >= (1ULL << 53)) { + u = (u >> 1) | (u & 1); + m = ur_round(u); + e--; + } + + return pack64(m, -e); +} + +/* ======== Formatting helpers ======== */ + +static const char i2a[] = + "00010203040506070809" + "10111213141516171819" + "20212223242526272829" + "30313233343536373839" + "40414243444546474849" + "50515253545556575859" + "60616263646566676869" + "70717273747576777879" + "80818283848586878889" + "90919293949596979899"; + +static void format_base10(char *buf, int nd, uint64_t u) +{ + int idx; + while (nd >= 2) { + nd -= 2; + idx = (int)(u % 100) * 2; + u /= 100; + buf[nd] = i2a[idx]; + buf[nd + 1] = i2a[idx + 1]; + } + if (nd > 0) { + buf[0] = '0' + (char)u; + } +} + +static int +emit_exp(char *s, char e_char, int exp) +{ + char *p = s; + *p++ = e_char; + if (exp < 0) { + *p++ = '-'; + exp = -exp; + } + else { + *p++ = '+'; + } + if (exp >= 100) { + *p++ = '0' + exp / 100; + exp %= 100; + } + *p++ = '0' + exp / 10; + *p++ = '0' + exp % 10; + return (int)(p - s); +} + +/* ======== mrb_format_float ======== */ + +#ifdef MRB_USE_FLOAT32 +#define FLT_MIN_BUF_SIZE 6 +#else +#define FLT_MIN_BUF_SIZE 7 +#endif + +int +mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) +{ + char *s = buf; + int buf_remaining = (int)buf_size - 1; + int alt_form = 0; + char e_char; + + if ((uint8_t)fmt & 0x80) { + fmt &= 0x7f; + alt_form = 1; + } + if (buf_size <= (size_t)FLT_MIN_BUF_SIZE) { + if (buf_size >= 2) *s++ = '?'; + if (buf_size >= 1) *s = '\0'; + return buf_size >= 2; + } + if (signbit(f)) { + *s++ = '-'; + f = -f; + } + else if (sign) { + *s++ = sign; + } + buf_remaining -= (int)(s - buf); + + { + char uc = fmt & 0x20; + if (isinf(f)) { + *s++ = 'I' ^ uc; + *s++ = 'N' ^ uc; + *s++ = 'F' ^ uc; + goto done; + } + if (isnan(f)) { + *s++ = 'N' ^ uc; + *s++ = 'A' ^ uc; + *s++ = 'N' ^ uc; + goto done; + } + } + + if (prec < 0) prec = 6; + e_char = 'E' | (fmt & 0x20); + fmt |= 0x20; + if (fmt == 'g' && prec == 0) prec = 1; + + if (f == 0.0) { + /* zero */ + if (fmt == 'e') { + *s++ = '0'; + if (prec > 0 || alt_form) { + int i; + if (prec > buf_remaining - 5) prec = buf_remaining - 5; + *s++ = '.'; + for (i = 0; i < prec; i++) *s++ = '0'; + } + s += emit_exp(s, e_char, 0); + } + else if (fmt == 'f') { + int i; + if (prec > buf_remaining - 2) prec = buf_remaining - 2; + *s++ = '0'; + if (prec > 0 || alt_form) { + *s++ = '.'; + for (i = 0; i < prec; i++) *s++ = '0'; + } + } + else { /* g */ + *s++ = '0'; + if (alt_form && prec > 1) { + int i; + *s++ = '.'; + for (i = 1; i < prec; i++) *s++ = '0'; + } + } + } + else { + /* nonzero finite */ + uint64_t d; + int p, nd, exp; + char digs[20]; + + if (fmt == 'g') { + /* g/G format */ + int fprec; + fixed_width((double)f, prec, &d, &p); + nd = count_digits(d); + exp = p + nd - 1; + + if (exp < -4 || exp >= prec) { + /* use e format with prec-1 fractional digits */ + fprec = prec - 1; + format_base10(digs, nd, d); + *s++ = digs[0]; + if (fprec > 0 || alt_form) { + int i, end = fprec < nd - 1 ? fprec : nd - 1; + *s++ = '.'; + for (i = 0; i < end; i++) *s++ = digs[1 + i]; + for (; i < fprec; i++) *s++ = '0'; + } + if (!alt_form) { + /* strip trailing zeros */ + while (s > buf && s[-1] == '0') s--; + if (s > buf && s[-1] == '.') s--; + } + s += emit_exp(s, e_char, exp); + } + else { + /* use f format with prec-(exp+1) fractional digits */ + int i; + fprec = prec - (exp + 1); + format_base10(digs, nd, d); + /* integer part */ + if (exp < 0) { + *s++ = '0'; + } + else { + for (i = 0; i <= exp && i < nd; i++) *s++ = digs[i]; + for (; i <= exp; i++) *s++ = '0'; + } + if (fprec > 0 || alt_form) { + int frac_avail; + *s++ = '.'; + if (exp < 0) { + int zeros = -(exp + 1); + for (i = 0; i < zeros && i < fprec; i++) *s++ = '0'; + frac_avail = fprec - zeros; + for (i = 0; i < frac_avail && i < nd; i++) *s++ = digs[i]; + for (; i < frac_avail; i++) *s++ = '0'; + } + else { + frac_avail = nd - exp - 1; + if (frac_avail < 0) frac_avail = 0; + for (i = 0; i < frac_avail && i < fprec; i++) *s++ = digs[exp + 1 + i]; + for (; i < fprec; i++) *s++ = '0'; + } + } + if (!alt_form && fprec > 0) { + while (s[-1] == '0') s--; + if (s[-1] == '.') s--; + } + } + } + else if (fmt == 'e') { + /* e/E format */ + int n = prec + 1; + int i; + if (n > 18) n = 18; + if (n < 1) n = 1; + fixed_width((double)f, n, &d, &p); + nd = count_digits(d); + exp = p + nd - 1; + format_base10(digs, nd, d); + *s++ = digs[0]; + if (prec > 0 || alt_form) { + *s++ = '.'; + for (i = 1; i < nd; i++) *s++ = digs[i]; + for (i = nd - 1; i < prec; i++) *s++ = '0'; + } + s += emit_exp(s, e_char, exp); + } + else { + /* f/F format */ + int exp_est, i; + union { double d; uint64_t u; } uf; + uf.d = (double)f; + exp_est = log10_pow2((int)((uf.u >> 52) & 0x7FF) - 1023); + + if (exp_est >= buf_remaining) { + /* too big for f, use e */ + int n = prec + 1; + if (n > 18) n = 18; + if (n < 1) n = 1; + fixed_width((double)f, n, &d, &p); + nd = count_digits(d); + exp = p + nd - 1; + format_base10(digs, nd, d); + *s++ = digs[0]; + if (prec > 0 || alt_form) { + *s++ = '.'; + for (i = 1; i < nd; i++) *s++ = digs[i]; + for (i = nd - 1; i < prec; i++) *s++ = '0'; + } + s += emit_exp(s, e_char, exp); + } + else { + if ((exp_est + prec + 2) > buf_remaining) { + prec = buf_remaining - exp_est - 2; + if (prec < 0) prec = 0; + } + + if (exp_est + prec <= 17) { + /* Direct uscale: compute round(f * 10^prec) */ + uint64_t m; + int e, lp; + unrounded u; + unpack64((double)f, &m, &e); + lp = log2_pow10(prec); + u = uscale(m, prescale(e, prec, lp)); + d = ur_round(u); + nd = count_digits(d); + exp = nd - prec - 1; + } + else { + /* Large number: use fixed_width, pad with zeros */ + int n = 18; + fixed_width((double)f, n, &d, &p); + nd = count_digits(d); + exp = p + nd - 1; + } + format_base10(digs, nd, d); + + if (exp >= 0) { + for (i = 0; i < nd && i <= exp; i++) *s++ = digs[i]; + for (; i <= exp; i++) *s++ = '0'; + if (prec > 0 || alt_form) { + int frac_avail = nd - exp - 1; + if (frac_avail < 0) frac_avail = 0; + *s++ = '.'; + for (i = 0; i < frac_avail && i < prec; i++) *s++ = digs[exp + 1 + i]; + for (; i < prec; i++) *s++ = '0'; + } + } + else { + int zeros, frac_avail; + *s++ = '0'; + if (prec > 0 || alt_form) { + *s++ = '.'; + zeros = -(exp + 1); + for (i = 0; i < zeros && i < prec; i++) *s++ = '0'; + frac_avail = prec - zeros; + for (i = 0; i < frac_avail && i < nd; i++) *s++ = digs[i]; + for (; i < frac_avail; i++) *s++ = '0'; + } + } + } + } + } + +done: + *s = '\0'; + return (int)(s - buf); +} + +/* ======== mrb_read_float ======== */ + +MRB_API mrb_bool +mrb_read_float(const char *str, char **endp, double *fp) +{ + const char *p = str; + const char *a = p; + uint64_t d = 0; + int nd = 0; + int dp = 0; + int trunc = 0; + int sign = 1; + int any_digits = 0; + + while (ISSPACE((unsigned char)*p)) p++; + + if (*p == '-') { sign = -1; p++; } + else if (*p == '+') p++; + + /* skip leading zeros */ + while (*p == '0') { p++; any_digits = 1; } + + /* integer part */ + while (ISDIGIT(*p)) { + if (nd < 19) { + d = d * 10 + (*p - '0'); + nd++; + } + else { + trunc++; + } + any_digits = 1; + a = ++p; + } + + /* fractional part */ + if (*p == '.') { + p++; + if (nd == 0) { + while (*p == '0') { dp++; p++; any_digits = 1; } + } + while (ISDIGIT(*p)) { + if (nd < 19) { + d = d * 10 + (*p - '0'); + nd++; + dp++; + } + any_digits = 1; + p++; + } + a = p; + } + + if (!any_digits) { + if (endp) *endp = (char*)str; + *fp = 0.0; + return FALSE; + } + + /* exponent */ + if ((*p | 32) == 'e') { + int e = 0; + int exp_sign = 1; + p++; + if (*p == '-') { exp_sign = -1; p++; } + else if (*p == '+') p++; + + if (!ISDIGIT(*p)) goto done; + + while (ISDIGIT(*p)) { + if (e < 10000) e = e * 10 + (*p - '0'); + p++; + } + + { + int final_p = e * exp_sign + trunc - dp; + double res; + if (d == 0) { + res = 0.0; + } + else if (final_p > 308) { + res = HUGE_VAL; + } + else if (final_p < -342 - nd) { + res = 0.0; + } + else { + res = parse_decimal(d, final_p); + } + if (sign < 0) res = -res; + *fp = res; + } + a = p; + goto done; + } + + { + int final_p = trunc - dp; + double res; + if (d == 0) { + res = 0.0; + } + else if (final_p > 308) { + res = HUGE_VAL; + } + else if (final_p < -342 - nd) { + res = 0.0; + } + else { + res = parse_decimal(d, final_p); + } + if (sign < 0) res = -res; + *fp = res; + } + +done: + if (endp) *endp = (char*)a; + return TRUE; +} + +#endif /* MRB_NO_FLOAT */ diff --git a/src/readfloat.c b/src/readfloat.c deleted file mode 100644 index cb23d7f71..000000000 --- a/src/readfloat.c +++ /dev/null @@ -1,229 +0,0 @@ -#include - -#ifndef MRB_NO_FLOAT - -#include -#include -#include - -// Powers of 10 lookup table for better performance and accuracy -static const double pow10_positive[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, - 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, - 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, - 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, - 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, - 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, - 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, - 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, - 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, - 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, - 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, - 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, - 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, - 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, - 1e160, 1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, 1e169, - 1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, 1e179, - 1e180, 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, 1e188, 1e189, - 1e190, 1e191, 1e192, 1e193, 1e194, 1e195, 1e196, 1e197, 1e198, 1e199, - 1e200, 1e201, 1e202, 1e203, 1e204, 1e205, 1e206, 1e207, 1e208, 1e209, - 1e210, 1e211, 1e212, 1e213, 1e214, 1e215, 1e216, 1e217, 1e218, 1e219, - 1e220, 1e221, 1e222, 1e223, 1e224, 1e225, 1e226, 1e227, 1e228, 1e229, - 1e230, 1e231, 1e232, 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239, - 1e240, 1e241, 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249, - 1e250, 1e251, 1e252, 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259, - 1e260, 1e261, 1e262, 1e263, 1e264, 1e265, 1e266, 1e267, 1e268, 1e269, - 1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, 1e277, 1e278, 1e279, - 1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286, 1e287, 1e288, 1e289, - 1e290, 1e291, 1e292, 1e293, 1e294, 1e295, 1e296, 1e297, 1e298, 1e299, - 1e300, 1e301, 1e302, 1e303, 1e304, 1e305, 1e306, 1e307, 1e308 -}; - -static const double pow10_negative[] = { - 1e0, 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, - 1e-60, 1e-61, 1e-62, 1e-63, 1e-64, 1e-65, 1e-66, 1e-67, 1e-68, 1e-69, - 1e-70, 1e-71, 1e-72, 1e-73, 1e-74, 1e-75, 1e-76, 1e-77, 1e-78, 1e-79, - 1e-80, 1e-81, 1e-82, 1e-83, 1e-84, 1e-85, 1e-86, 1e-87, 1e-88, 1e-89, - 1e-90, 1e-91, 1e-92, 1e-93, 1e-94, 1e-95, 1e-96, 1e-97, 1e-98, 1e-99, - 1e-100, 1e-101, 1e-102, 1e-103, 1e-104, 1e-105, 1e-106, 1e-107, 1e-108, 1e-109, - 1e-110, 1e-111, 1e-112, 1e-113, 1e-114, 1e-115, 1e-116, 1e-117, 1e-118, 1e-119, - 1e-120, 1e-121, 1e-122, 1e-123, 1e-124, 1e-125, 1e-126, 1e-127, 1e-128, 1e-129, - 1e-130, 1e-131, 1e-132, 1e-133, 1e-134, 1e-135, 1e-136, 1e-137, 1e-138, 1e-139, - 1e-140, 1e-141, 1e-142, 1e-143, 1e-144, 1e-145, 1e-146, 1e-147, 1e-148, 1e-149, - 1e-150, 1e-151, 1e-152, 1e-153, 1e-154, 1e-155, 1e-156, 1e-157, 1e-158, 1e-159, - 1e-160, 1e-161, 1e-162, 1e-163, 1e-164, 1e-165, 1e-166, 1e-167, 1e-168, 1e-169, - 1e-170, 1e-171, 1e-172, 1e-173, 1e-174, 1e-175, 1e-176, 1e-177, 1e-178, 1e-179, - 1e-180, 1e-181, 1e-182, 1e-183, 1e-184, 1e-185, 1e-186, 1e-187, 1e-188, 1e-189, - 1e-190, 1e-191, 1e-192, 1e-193, 1e-194, 1e-195, 1e-196, 1e-197, 1e-198, 1e-199, - 1e-200, 1e-201, 1e-202, 1e-203, 1e-204, 1e-205, 1e-206, 1e-207, 1e-208, 1e-209, - 1e-210, 1e-211, 1e-212, 1e-213, 1e-214, 1e-215, 1e-216, 1e-217, 1e-218, 1e-219, - 1e-220, 1e-221, 1e-222, 1e-223, 1e-224, 1e-225, 1e-226, 1e-227, 1e-228, 1e-229, - 1e-230, 1e-231, 1e-232, 1e-233, 1e-234, 1e-235, 1e-236, 1e-237, 1e-238, 1e-239, - 1e-240, 1e-241, 1e-242, 1e-243, 1e-244, 1e-245, 1e-246, 1e-247, 1e-248, 1e-249, - 1e-250, 1e-251, 1e-252, 1e-253, 1e-254, 1e-255, 1e-256, 1e-257, 1e-258, 1e-259, - 1e-260, 1e-261, 1e-262, 1e-263, 1e-264, 1e-265, 1e-266, 1e-267, 1e-268, 1e-269, - 1e-270, 1e-271, 1e-272, 1e-273, 1e-274, 1e-275, 1e-276, 1e-277, 1e-278, 1e-279, - 1e-280, 1e-281, 1e-282, 1e-283, 1e-284, 1e-285, 1e-286, 1e-287, 1e-288, 1e-289, - 1e-290, 1e-291, 1e-292, 1e-293, 1e-294, 1e-295, 1e-296, 1e-297, 1e-298, 1e-299, - 1e-300, 1e-301, 1e-302, 1e-303, 1e-304, 1e-305, 1e-306, 1e-307, 1e-308, 1e-309, - 1e-310, 1e-311, 1e-312, 1e-313, 1e-314, 1e-315, 1e-316, 1e-317, 1e-318, 1e-319, - 1e-320, 1e-321, 1e-322, 1e-323 -}; - -#define POW10_POSITIVE_SIZE (sizeof(pow10_positive) / sizeof(pow10_positive[0])) -#define POW10_NEGATIVE_SIZE (sizeof(pow10_negative) / sizeof(pow10_negative[0])) - -static double -mrb_pow10(int exp) -{ - if (exp >= 0) { - if (exp < (int)POW10_POSITIVE_SIZE) { - return pow10_positive[exp]; - } - return HUGE_VAL; - } - else { - exp = -exp; - if (exp < (int)POW10_NEGATIVE_SIZE) { - return pow10_negative[exp]; - } - return 0.0; - } -} - -/* -** Parses a string representation of a floating-point number. -** -** @param str The input string to parse. -** @param endp A pointer to a char* that will be updated to point to the -** character in str after the last character used in the -** conversion. Can be NULL. -** @param fp A pointer to a double that will be set to the parsed -** floating-point number. -** @return TRUE if a float is successfully parsed, FALSE otherwise. -*/ -MRB_API mrb_bool -mrb_read_float(const char *str, char **endp, double *fp) -{ - const char *p = str; - const char *a = p; - uint64_t int_part = 0; - uint64_t frac_part = 0; - int frac_digits = 0; - int sign = 1; - int digits = 0; - int overflow = 0; - - // Skip whitespace - while (ISSPACE((unsigned char)*p)) p++; - - // Handle sign - if (*p == '-') { sign = -1; p++; } - else if (*p == '+') p++; - - // Parse integer part using integer arithmetic for better accuracy - while (ISDIGIT(*p)) { - if (int_part > (UINT64_MAX - 9) / 10) { - overflow = 1; - // Continue parsing to find the end - while (ISDIGIT(*p)) p++; - break; - } - int_part = int_part * 10 + (*p - '0'); - digits++; - a = ++p; - } - - // Parse fractional part - if (*p == '.') { - p++; - while (ISDIGIT(*p) && frac_digits < 18) { // Limit precision to avoid overflow - frac_part = frac_part * 10 + (*p - '0'); - frac_digits++; - digits++; - p++; - } - // Skip remaining fractional digits if any - while (ISDIGIT(*p)) p++; - a = p; - } - - // If no digits were found, return FALSE - if (digits == 0) { - if (endp) *endp = (char*)str; - *fp = 0.0; - return FALSE; - } - - double res; - if (overflow) { - // For overflow case, fall back to floating point parsing - res = (double)int_part; - } - else { - // Divide by the exact 10^n (exact for n <= 22) rather than multiplying - // by the inexact 10^-n, so the fraction is correctly rounded. - res = (double)int_part; - if (frac_digits > 0) { - res += (double)frac_part / mrb_pow10(frac_digits); - } - } - - // Handle exponent - if ((*p | 32) == 'e') { - int e = 0; - int exp_sign = 1; - p++; - if (*p == '-') { exp_sign = -1; p++; } - else if (*p == '+') p++; - - // If no digits follow 'e', ignore the exponent part - if (!ISDIGIT(*p)) goto done; - - while (ISDIGIT(*p)) { - if (e < 10000) { // Prevent integer overflow - e = e * 10 + (*p - '0'); - } - p++; - } - e *= exp_sign; - - // Apply exponent directly - let mrb_pow10 handle overflow - // Large exponents will return HUGE_VAL (infinity) or 0.0 as appropriate - - res *= mrb_pow10(e); - a = p; - } - - // Apply sign - res *= sign; - - // Set endp - done: - if (endp) *endp = (char*)a; - *fp = res; - - // strtod(3) stores ERANGE to errno for overflow/underflow - // mruby does not require those checks -#if 0 - // Check for underflow after applying the exponent - if (res != 0.0 && fabs(res) < DBL_MIN) { - return FALSE; - } - - // Check if the result is infinity or NaN - if (isinf(res) || isnan(res)) { - return FALSE; - } -#endif - return TRUE; -} - -#endif diff --git a/tools/gen_pow10_tab.rb b/tools/gen_pow10_tab.rb new file mode 100755 index 000000000..07cec1885 --- /dev/null +++ b/tools/gen_pow10_tab.rb @@ -0,0 +1,68 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Generate pow10 table for mruby unrounded scaling (fp_uscale.c) +# +# Uses exact integer arithmetic only. +# For each p in [-343, 341], computes (hi, lo) such that: +# 10^p ~= (hi * 2^64 - lo) * 2^pe +# where pe = floor(p * log2(10)) - 127 +# +# The 128-bit value pm = hi * 2^64 - lo is in [2^127, 2^128). + +POW10_MIN = -343 +POW10_MAX = 341 + +def generate_entry(p) + if p >= 0 + val = 10**p + bit_len = val.bit_length + pe = bit_len - 128 + if pe >= 0 + mask = (1 << pe) - 1 + pm = (val >> pe) + ((val & mask) != 0 ? 1 : 0) + else + pm = val << (-pe) + end + else + abs_p = -p + denom = 10**abs_p + # pe = floor(p * log2(10)) - 127 + # Ruby's integer division of negative numbers does floor division + pe_est = (p * 108853 >> 15) - 127 + + numerator = 1 << (-pe_est) + pm = (numerator + denom - 1) / denom + + # Adjust pe if pm is out of range [2^127, 2^128) + while pm >= (1 << 128) + pe_est += 1 + numerator = 1 << (-pe_est) + pm = (numerator + denom - 1) / denom + end + while pm < (1 << 127) + pe_est -= 1 + numerator = 1 << (-pe_est) + pm = (numerator + denom - 1) / denom + end + end + + raise "pm out of range for p=#{p}: #{pm.bit_length}" unless pm.bit_length == 128 + + hi = (pm >> 64) + ((pm & ((1 << 64) - 1)) != 0 ? 1 : 0) + lo = (hi << 64) - pm + + raise "hi out of range for p=#{p}" unless hi >= (1 << 63) && hi < (1 << 64) + + { hi: hi, lo: lo } +end + +def main + entries = (POW10_MIN..POW10_MAX).map { |p| [p, generate_entry(p)] } + + entries.each do |p, e| + printf(" {0x%016xULL, 0x%016xULL},\n", e[:hi], e[:lo]) + end +end + +main