From 81796cd533a04d352496770ef51950b51bbd432d Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Wed, 27 May 2026 13:55:56 +0900 Subject: [PATCH] =?UTF-8?q?lib/strconv:=20stof.ha=20port=20=E2=80=94=20Eis?= =?UTF-8?q?el-Lemire=20string=E2=86=92float=20(#106=20fold-4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stof64/stof32 (f64|f32 | invalid | overflow) via Eisel-Lemire fast-path (powers_of_ten[596][2]u64 + eisel_lemire 128-bit multiply) + decimal slow-path fallback (decimal.ww, fold-3). 16 fns + faithful powers_of_ten (byte-identical to Hare). u128 via pure-u64 64×64→128 (ftos_ryu.ha). Consumes &math.f64info (γ-cleanup), tagged-float-return (PREREQ-2 #157), 2D double-index (PREREQ-1 #156). 13 documented spelling-divergences (rule-9, each cites stof.ha): #155 (po10 double-index + per-field struct-copy), #161 (compound-assign explicit form), #144 (-0.0 via 1u64<<63), #158, #138, test-only #143/parsef64. Test 909 (DEC+hex+NaN/Inf/invalid/overflow, bit-exact, cstage ww run). Make test 185/185 incl 990-997 byte-id + combined_ww_fresh. Makefile: stof.ww added to w6c_ww/wwdump_ww deps (freshness, fold-3 precedent). Drew's strconv 5-fold plan 4/5. Followup #162 (wwstage lexer parsef64 1-ULP — could adopt stof64). --- Makefile | 10 +- lib/strconv/stof.ww | 700 +++++++++ lib/strconv/stof_data.ww | 611 +++++++- lib/strconv/test/stoftest.ww | 227 +++ selfhost/cmd/w6c/main.combined.ww | 1876 +++++++++++++++++++++-- selfhost/cmd/wwdump/main.combined.ww | 2052 +++++++++++++++++++++++--- selfhost/test/smoke.combined.ww | 2052 +++++++++++++++++++++++--- test/wcc/909_stof_run.c | 55 + 8 files changed, 6977 insertions(+), 606 deletions(-) create mode 100644 lib/strconv/stof.ww create mode 100644 lib/strconv/test/stoftest.ww create mode 100644 test/wcc/909_stof_run.c diff --git a/Makefile b/Makefile index 4f230446..14d75092 100644 --- a/Makefile +++ b/Makefile @@ -113,7 +113,7 @@ $(BIN)/wwdump_ww: selfhost/cmd/wwdump/main.ww \ selfhost/cmd/wcc/cgen.ww selfhost/cmd/wcc/cgenexpr.ww \ selfhost/cmd/wcc/cgenstmt.ww selfhost/cmd/wcc/cgenutil.ww \ selfhost/cmd/wcc/cgendecl.ww \ - lib/os/os.ww lib/rt/malloc.ww lib/time/time.ww lib/strconv/strconv.ww lib/strconv/stof_data.ww lib/strconv/decimal.ww lib/strings/strings.ww lib/bytes/bytes.ww lib/encoding/utf8/utf8.ww lib/ascii/ascii.ww lib/io/io.ww lib/memio/memio.ww lib/fmt/fmt.ww \ + lib/os/os.ww lib/rt/malloc.ww lib/time/time.ww lib/strconv/strconv.ww lib/strconv/stof_data.ww lib/strconv/decimal.ww lib/strconv/stof.ww lib/strings/strings.ww lib/bytes/bytes.ww lib/encoding/utf8/utf8.ww lib/ascii/ascii.ww lib/io/io.ww lib/memio/memio.ww lib/fmt/fmt.ww \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) @mkdir -p $(BIN)/wwdump_ww.d @@ -135,7 +135,7 @@ $(BIN)/w6c_ww: selfhost/cmd/w6c/main.ww \ selfhost/cmd/wcc/cgen.ww selfhost/cmd/wcc/cgenexpr.ww \ selfhost/cmd/wcc/cgenstmt.ww selfhost/cmd/wcc/cgenutil.ww \ selfhost/cmd/wcc/cgendecl.ww \ - lib/os/os.ww lib/rt/malloc.ww lib/time/time.ww lib/strconv/strconv.ww lib/strconv/stof_data.ww lib/strconv/decimal.ww lib/strings/strings.ww lib/bytes/bytes.ww lib/encoding/utf8/utf8.ww lib/ascii/ascii.ww lib/io/io.ww lib/memio/memio.ww lib/fmt/fmt.ww \ + lib/os/os.ww lib/rt/malloc.ww lib/time/time.ww lib/strconv/strconv.ww lib/strconv/stof_data.ww lib/strconv/decimal.ww lib/strconv/stof.ww lib/strings/strings.ww lib/bytes/bytes.ww lib/encoding/utf8/utf8.ww lib/ascii/ascii.ww lib/io/io.ww lib/memio/memio.ww lib/fmt/fmt.ww \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) @mkdir -p $(BIN)/w6c_ww.d @@ -322,7 +322,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_intdiv_signed \ $(BIN)/test_strings_run \ $(BIN)/test_hex_run $(BIN)/test_utf8_run $(BIN)/test_bytes_run \ - $(BIN)/test_decimal_run \ + $(BIN)/test_decimal_run $(BIN)/test_stof_run \ $(BIN)/test_memio_run $(BIN)/test_temp_run $(BIN)/test_getopt_run \ $(BIN)/test_base32_run $(BIN)/test_base64_run \ $(BIN)/test_adler32_run $(BIN)/test_crc16_run \ @@ -1046,6 +1046,10 @@ $(BIN)/test_decimal_run: test/wcc/922_decimal_run.c $(BIN)/ww $(BIN)/w6c \ $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< +$(BIN)/test_stof_run: test/wcc/909_stof_run.c $(BIN)/ww $(BIN)/w6c \ + $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) + $(CC) $(CFLAGS) -o $@ $< + $(BIN)/test_strings_run: test/wcc/966_strings_run.c $(BIN)/ww $(BIN)/w6c \ $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< diff --git a/lib/strconv/stof.ww b/lib/strconv/stof.ww new file mode 100644 index 00000000..71a8f973 --- /dev/null +++ b/lib/strconv/stof.ww @@ -0,0 +1,700 @@ +// strconv — string-to-float. Mirrors ref/hare/strconv/stof.ha +// (Hare in turn adapts Go): Eisel-Lemire fast path [1] with the +// Simple-Decimal-Conversion slow path [2] (decimal.ww) as fallback. +// [1]: https://nigeltao.github.io/blog/2020/eisel-lemire.html +// [2]: https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html +// +// The Eisel-Lemire fast path (`eisel_lemire` + the `powers_of_ten` +// table in stof_data.ww + the three call sites: floatbits's d.nd<=19 +// block, stof64/stof32's !truncated block) is a pure speed +// optimisation — it returns the same correctly-rounded value the +// decimal slow path (decimal_parse → floatbits) computes, or void to +// defer. Its prereqs landed: the 2D `[596][2]u64` static-init + +// double-index read (#156) and the tagged float-variant return-pack +// (#157, which the public `(f64|invalid|overflow)` return needs). +// +// Spelling divergences from Hare (mechanical, ww parser/cgen shape): +// - str scan index rides `i32` (ww `str.len: i32` + `invalid = !i32` +// payload), not Hare's `size`/`len(s)`. lib CLAUDE.md str-index note. +// - char literals kept faithful (`buf[i] == '.'`, `c - '0'`); probed +// byte-id + value-correct both stages. +// - Hare `?` error-propagation → nested statement-`match` with all- +// return arms + a `case void => void` continuation. ww's `?` +// lowering and a bound `match`-expression with mixed yield/return +// arms both diverge cs≠ww (the latter wwstage-checker-rejected); +// strconv.ww's stoi32 set the explicit-match precedent. +// - Hare `for (cond; afterthought)` 2-clause + `continue` → ww +// 2-clause `for (cond)` with the afterthought inlined at body end +// AND before each `continue` (ww has no empty-init 3-clause +// `for (; c; p)`; #138 post-skip is dodged since 2-clause has no +// post). decimal.ww set the inline-afterthought precedent. +// - Hare `if`/`switch`-expression yield → explicit if-statements + +// pre-bound scalar locals (ww has no expression-bodied if). +// - Hare fn-pointer-in-tuple + `switch yield` selecting the digit +// predicate in fast_parse → a `base==HEX` bool + an `isdigitbase` +// helper that branches to ascii.isdigit/isxdigit (no fn-ptr, no +// tuple, no switch). +// - struct-param field MUTATION (hex_to_bits mutates its by-value +// `p`) → copy p's fields to scalar locals at entry; ww miscompiles +// + diverges on writing a by-value struct param's fields (filed). +// - default arg dropped: Hare `b: base = base::DEC` → callers pass +// base explicitly (no lib fn ships a default arg; strconv.ww +// stoi64 precedent). The base param is normalised through a local +// `bb` (param reassignment avoided). +// - `math::NAN`/`math::INF` (f32) absent in ww math → materialised +// via f32frombits of the IEEE-754 f32 bit patterns (same honest +// construction as math/floats.ww's NAN_BITS/INF_BITS). +// - narrowing int→i32 assignments carry explicit casts (ww `int` is +// an 8B machine word; project_int_machine_word_derived_limits). +// - `r128`/`u128mul` live here (fold-4 is first consumer); fold-5 +// ftos (Ryū) shares them in-package. + +package strconv; + +import ascii; +import math; +import os; +import strings; + +// ref/hare/strconv/ftos_ryu.ha:12. 64×64→128 result halves. +type r128 = struct { + hi: u64, + lo: u64, +}; + +// ref/hare/strconv/ftos_ryu.ha:18. 64×64→128 via 32-bit decomposition +// (Hare's own "TODO: use 128-bit integers when implemented" — ww has +// no u128; the decomposition is the portable shape both stages agree +// on). Comma let-bindings split per decimal.ww divergence. +fn u128mul(a: u64, b: u64) r128 = { + let a0: u64 = (a: u32): u64; + let a1: u64 = a >> 32u64; + let b0: u64 = (b: u32): u64; + let b1: u64 = b >> 32u64; + let p00: u64 = a0 * b0; + let p01: u64 = a0 * b1; + let p10: u64 = a1 * b0; + let p11: u64 = a1 * b1; + let p00_lo: u64 = (p00: u32): u64; + let p00_hi: u64 = p00 >> 32u64; + let mid1: u64 = p10 + p00_hi; + let mid1_lo: u64 = (mid1: u32): u64; + let mid1_hi: u64 = mid1 >> 32u64; + let mid2: u64 = p01 + mid1_lo; + let mid2_lo: u64 = (mid2: u32): u64; + let mid2_hi: u64 = mid2 >> 32u64; + let r_hi: u64 = p11 + mid1_hi + mid2_hi; + let r_lo: u64 = (mid2_lo << 32u64) | p00_lo; + return r128 { hi = r_hi, lo = r_lo }; +}; + +// ref/hare/strconv/stof.ha:14. +fn todig(c: u8) u8 = { + if ('0' <= c && c <= '9') { return c - '0'; }; + if ('a' <= c && c <= 'f') { return c - 'a' + 10u8; }; + if ('A' <= c && c <= 'F') { return c - 'A' + 10u8; }; + abort("strconv.todig: unreachable"); + return 0u8; // unreachable; rt_abort is void-typed (path-cov) +}; + +@symbol("rt_abort") fn abort(msg: str) void; + +// ref/hare/strconv/stof.ha:25. +type fast_parsed_float = struct { + mantissa: u64, + exponent: i32, + negative: bool, + truncated: bool, +}; + +// Digit-class predicate selector for fast_parse — replaces Hare's +// fn-pointer-in-tuple (`&ascii::isdigit` / `&ascii::isxdigit`). +fn isdigitbase(c: rune, ishex: bool) bool = { + if (ishex) { return ascii.isxdigit(c); }; + return ascii.isdigit(c); +}; + +// ref/hare/strconv/stof.ha:32. +fn fast_parse(s: str, b: base) (fast_parsed_float | invalid) = { + let buf: []u8 = strings.toutf8(s); + let i: i32 = 0; + let neg: bool = false; + let trunc: bool = false; + if (buf[i] == '-') { + neg = true; + i += 1; + } else if (buf[i] == '+') { + i += 1; + }; + + let ishex: bool = (b == base.HEX); + let expchr: rune = 'e'; + let max_ndmant: int = 19; + if (ishex) { + expchr = 'p'; + max_ndmant = 16; + }; + let bnum: u64 = (b: i32): u64; + + let sawdot: bool = false; + let sawdigits: bool = false; + let nd: int = 0; + let ndmant: int = 0; + let dp: int = 0; + let mant: u64 = 0u64; + let exp: i32 = 0i32; + for (i < s.len) { + if (buf[i] == '.') { + if (sawdot) { return i: invalid; }; + sawdot = true; + dp = nd; + } else if (isdigitbase(buf[i]: rune, ishex)) { + sawdigits = true; + if (buf[i] == '0' && nd == 0) { + dp -= 1; + i += 1; + continue; + }; + nd += 1; + if (ndmant < max_ndmant) { + mant = mant * bnum + (todig(buf[i]): u64); + ndmant += 1; + } else if (buf[i] != '0') { + trunc = true; + }; + } else { + break; + }; + i += 1; + }; + if (!sawdigits) { return i: invalid; }; + if (!sawdot) { + dp = nd; + }; + if (b == base.HEX) { + dp *= 4; + ndmant *= 4; + }; + if (i < s.len && ascii.tolower(buf[i]: rune) == expchr) { + i += 1; + if (i >= s.len) { return i: invalid; }; + let expsign: int = 1; + if (buf[i] == '+') { + i += 1; + } else if (buf[i] == '-') { + expsign = -1; + i += 1; + }; + if (i >= s.len || !ascii.isdigit(buf[i]: rune)) { + return i: invalid; + }; + let e: int = 0; + for (i < s.len && ascii.isdigit(buf[i]: rune)) { + if (e < 10000) { + e = e * 10 + ((buf[i] - '0'): int); + }; + i += 1; + }; + dp += e * expsign; + } else if (b == base.HEX) { + return i: invalid; // hex floats must have an exponent + }; + if (i != s.len) { return i: invalid; }; + if (mant != 0u64) { + exp = (dp - ndmant): i32; + }; + return fast_parsed_float { + mantissa = mant, + exponent = exp, + negative = neg, + truncated = trunc, + }; +}; + +// ref/hare/strconv/stof.ha:115. Fills the slow-path decimal `d`. +fn decimal_parse(d: *decimal, s: str) (void | invalid) = { + let i: i32 = 0; + let buf: []u8 = strings.toutf8(s); + d.negative = false; + d.truncated = false; + if (buf[0] == '+') { + i += 1; + } else if (buf[0] == '-') { + d.negative = true; + i += 1; + }; + let sawdot: bool = false; + let sawdigits: bool = false; + for (i < s.len) { + if (buf[i] == '.') { + if (sawdot) { return i: invalid; }; + sawdot = true; + d.dp = (d.nd: i32); + } else if (ascii.isdigit(buf[i]: rune)) { + sawdigits = true; + if (buf[i] == '0' && d.nd == (0u64: size)) { + d.dp -= 1; + i += 1; + continue; + }; + if (d.nd < (len(d.digits): size)) { + d.digits[d.nd] = buf[i] - '0'; + d.nd += (1u64: size); + } else if (buf[i] != '0') { + d.truncated = true; + }; + } else { + break; + }; + i += 1; + }; + if (!sawdigits) { return i: invalid; }; + if (!sawdot) { + d.dp = (d.nd: i32); + }; + if (i < s.len && (buf[i] == 'e' || buf[i] == 'E')) { + i += 1; + if (i >= s.len) { return i: invalid; }; + let expsign: int = 1; + if (buf[i] == '+') { + i += 1; + } else if (buf[i] == '-') { + expsign = -1; + i += 1; + }; + if (i >= s.len || !ascii.isdigit(buf[i]: rune)) { + return i: invalid; + }; + let e: int = 0; + for (i < s.len && ascii.isdigit(buf[i]: rune)) { + if (e < 10000) { + e = e * 10 + ((buf[i] - '0'): int); + }; + i += 1; + }; + d.dp += (e * expsign): i32; + }; + if (i != s.len) { return i: invalid; }; + return; +}; + +// ref/hare/strconv/stof.ha:173. Count of leading zero bits in n>0. +fn leading_zeroes(n: u64) uint = { + os.assert(n > 0u64, "strconv.leading_zeroes: n == 0"); + let b: u64 = 0u64; + if ((n & 0xFFFFFFFF00000000u64) > 0u64) { + n >>= 32u64; + b |= 32u64; + }; + if ((n & 0xFFFF0000u64) > 0u64) { + n >>= 16u64; + b |= 16u64; + }; + if ((n & 0xFF00u64) > 0u64) { + n >>= 8u64; + b |= 8u64; + }; + if ((n & 0xF0u64) > 0u64) { + n >>= 4u64; + b |= 4u64; + }; + if ((n & 0xCu64) > 0u64) { + n >>= 2u64; + b |= 2u64; + }; + if ((n & 0x2u64) > 0u64) { + n >>= 1u64; + b |= 1u64; + }; + return ((63u64 - b): uint); +}; + +// ref/hare/strconv/stof.ha:203. Eisel-Lemire fast path: a correctly- +// rounded f64/f32 from (mantissa, exp10) when the 128-bit product is +// unambiguous, else void → caller falls to the decimal slow path. +// Divergences at-site: `mantissa <<= clz` (scalar-param mutate) → local +// `mnt`; whole-struct local reassign `x = merged` copies only the first +// word in cgen → per-field `x.hi = …; x.lo = …` (#155); `po10 = +// powers_of_ten[i]` row-bind → direct double-index (#155, A2); bitwise- +// vs-compare fully parenthesised; comma let-bindings split. +fn eisel_lemire( + mantissa: u64, + exp10: i32, + neg: bool, + f: *math.floatinfo, +) (u64 | void) = { + if (mantissa == 0u64 || exp10 > 288 || exp10 < -307) { + return; + }; + let idx: i32 = exp10 + 307; + let clz: uint = leading_zeroes(mantissa); + let mnt: u64 = mantissa << (clz: u64); + let shift: u64 = 64u64 - f.mantbits - 3u64; + let mask: u64 = (1u64 << shift) - 1u64; + // log(10)/log(2) ≈ 217706 / 65536; x / 65536 = x >> 16. + let exp: int = (217706 * (exp10: int)) >> 16; + let e2: u64 = ((exp + f.expbias + 64): u64) - (clz: u64); + let x: r128 = u128mul(mnt, powers_of_ten[idx][1]); + if ((x.hi & mask) == mask && (x.lo + mnt) < mnt) { + let y: r128 = u128mul(mnt, powers_of_ten[idx][0]); + let merged: r128 = r128 { hi = x.hi, lo = x.lo + y.hi }; + if (merged.lo < x.lo) { + // local-struct-field compound-assign drops the load in + // wwstage (sets =1, not +=1) — explicit form, byte-id. + merged.hi = merged.hi + 1u64; + }; + if ((merged.hi & mask) == mask && (merged.lo + 1u64) == 0u64 && + (y.lo + mnt) < mnt) { + return; + }; + x.hi = merged.hi; + x.lo = merged.lo; + }; + let msb: u64 = x.hi >> 63u64; + let mant: u64 = x.hi >> (msb + shift); + e2 -= 1u64 ^ msb; + if (x.lo == 0u64 && (x.hi & mask) == 0u64 && (mant & 3u64) == 1u64) { + return; + }; + mant += mant & 1u64; + mant >>= 1u64; + if ((mant >> (f.mantbits + 1u64)) > 0u64) { + mant >>= 1u64; + e2 += 1u64; + }; + if (e2 <= 0u64 || e2 >= (1u64 << f.expbits) - 1u64) { + return; + }; + return mkfloat(mant, (e2: uint), neg, f); +}; + +// ref/hare/strconv/stof.ha:247. Slow-path: decimal `d` → IEEE bits. +fn floatbits(d: *decimal, f: *math.floatinfo) (u64 | overflow) = { + let e: int = 0; + let m: u64 = 0u64; + let powtab: [19]i8 = [ + 0i8, 3i8, 6i8, 9i8, 13i8, 16i8, 19i8, 23i8, 26i8, 29i8, + 33i8, 36i8, 39i8, 43i8, 46i8, 49i8, 53i8, 56i8, 59i8, + ]; + if (d.nd == (0u64: size) || d.dp < -326) { + if (d.negative) { + return mkfloat(0u64, (0u32: uint), d.negative, f); + }; + return 0u64; + } else if (d.dp > 310) { + return overflow{}; + }; + if (d.nd <= (19u64: size)) { + let dmant: u64 = 0u64; + let i: size = (0u64: size); + for (i < d.nd) { + dmant = 10u64 * dmant + (d.digits[i]: u64); + i += (1u64: size); + }; + let exp10: i32 = d.dp - (d.nd: i32); + match (eisel_lemire(dmant, exp10, d.negative, f)) { + case let r: u64 => { return r; }; + case void => void; + }; + }; + for (d.dp > 0) { + let n: int = 0; + if ((d.dp: uint) >= (len(powtab): uint)) { + n = (maxshift: int); + } else { + n = (powtab[d.dp]: int); + }; + decimal_shift(d, -n); + e += n; + }; + for (d.dp <= 0) { + let n: int = 0; + if (d.dp == 0) { + if (d.digits[0] >= 5u8) { break; }; + if (d.digits[0] < 2u8) { n = 2; } else { n = 1; }; + } else if ((-d.dp) >= (len(powtab): i32)) { + n = (maxshift: int); + } else { + n = (powtab[-d.dp]: int); + }; + decimal_shift(d, n); + e -= n; + }; + e -= 1; + if (e <= -f.expbias + 1) { + let nn: int = -f.expbias - e + 1; + decimal_shift(d, -nn); + e += nn; + }; + if (e + f.expbias >= ((1u64 << f.expbits): int) - 1) { + return overflow{}; + }; + decimal_shift(d, (f.mantbits: int) + 1); + m = decimal_round(d); + if (m == (2u64 << f.mantbits)) { + m >>= 1u64; + e += 1; + if (e + f.expbias >= ((1u64 << f.expbits): int) - 1) { + return overflow{}; + }; + }; + if ((m & (1u64 << f.mantbits)) == 0u64) { + e = -f.expbias; + }; + return mkfloat(m, ((e + f.expbias): uint), d.negative, f); +}; + +// ref/hare/strconv/stof.ha:311. Assemble sign|exp|mantissa. +fn mkfloat(m: u64, e: uint, negative: bool, f: *math.floatinfo) u64 = { + let n: u64 = m & ((1u64 << f.mantbits) - 1u64); + n |= ((e: u64) & ((1u64 << f.expbits) - 1u64)) << f.mantbits; + if (negative) { + n |= 1u64 << (f.mantbits + f.expbits); + }; + return n; +}; + +// ref/hare/strconv/stof.ha:320. Exact f64 powers of ten 1e0..1e22 (all +// exactly representable; see stof64exact). +let f64pow10: [23]f64 = [ + 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, + 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, + 1.0e19, 1.0e20, 1.0e21, 1.0e22, +]; + +// ref/hare/strconv/stof.ha:326. +fn stof64exact(mant: u64, exp: i32, neg: bool) (f64 | void) = { + if (mant >> math.F64_MANTISSA_BITS != 0u64) { return; }; + let n: f64 = (mant: i64): f64; + if (neg) { + n = -n; + }; + if (exp == 0i32) { + return n; + }; + if (-22i32 <= exp && exp <= 22i32) { + if (exp >= 0i32) { + // f64 compound-assign mis-lowers in cgen — explicit + // form (strconv.ww f64tos precedent). + n = n * f64pow10[exp]; + } else { + n = n / f64pow10[-exp]; + }; + } else { + return; + }; + return n; +}; + +// ref/hare/strconv/stof.ha:345. Exact f32 powers of ten 1e0..1e10. +let f32pow10: [11]f32 = [ + 1.0e0f32, 1.0e1f32, 1.0e2f32, 1.0e3f32, 1.0e4f32, 1.0e5f32, 1.0e6f32, + 1.0e7f32, 1.0e8f32, 1.0e9f32, 1.0e10f32, +]; + +// ref/hare/strconv/stof.ha:349. +fn stof32exact(mant: u64, exp: i32, neg: bool) (f32 | void) = { + if (mant >> (math.F32_MANTISSA_BITS: u64) != 0u64) { return; }; + let n: f32 = (mant: i32): f32; + if (neg) { + n = -n; + }; + if (exp == 0i32) { + return n; + }; + if (-10i32 <= exp && exp <= 10i32) { + if (exp >= 0i32) { + // f32 compound-assign mis-lowers in cgen — explicit form. + n = n * f32pow10[exp]; + } else { + n = n / (f64pow10[-exp]: f32); + }; + } else { + return; + }; + return n; +}; + +// ref/hare/strconv/stof.ha:369. Adapted from Go's atofHex. The by-value +// `p` is mutated in Hare; ww copies its fields to scalar locals (struct +// param field-write miscompiles + diverges — filed). +fn hex_to_bits(p: fast_parsed_float, info: *math.floatinfo) (u64 | overflow) = { + let pmant: u64 = p.mantissa; + let pexp: i32 = p.exponent; + let pneg: bool = p.negative; + let ptrunc: bool = p.truncated; + let max_exp: int = ((1u64 << info.expbits): int) - info.expbias - 2; + let min_exp: int = -info.expbias + 1; + pexp += (info.mantbits: i32); + + // Shift left until a leading 1 bit followed by mantbits + 2 rounding. + for (pmant != 0u64 && pmant >> (info.mantbits + 2u64) == 0u64) { + pmant <<= 1u64; + pexp -= 1; + }; + if (ptrunc) { + pmant |= 1u64; + }; + // Too many bits: shift right (sticky-or the dropped bit). + for (pmant >> (3u64 + info.mantbits) != 0u64) { + pmant = (pmant >> 1u64) | (pmant & 1u64); + pexp += 1; + }; + // Denormalise if the exponent is small. + for (pmant > 1u64 && pexp < (min_exp: i32) - 2) { + pmant = (pmant >> 1u64) | (pmant & 1u64); + pexp += 1; + }; + // Round to even. + let round: u64 = pmant & 3u64; + pmant >>= 2u64; + round |= pmant & 1u64; + pexp += 2; + if (round == 3u64) { + pmant += 1u64; + if (pmant == 1u64 << (1u64 + info.mantbits)) { + pmant >>= 1u64; + pexp += 1; + }; + }; + // Denormal or zero. + if (pmant >> info.mantbits == 0u64) { + pexp = (-info.expbias): i32; + }; + if (pexp > (max_exp: i32)) { + return overflow{}; + }; + let bits: u64 = pmant & info.mantmask; + bits |= (((pexp + (info.expbias: i32)): u64) & info.expmask) << info.mantbits; + if (pneg) { + bits |= 1u64 << (info.mantbits + info.expbits); + }; + return bits; +}; + +// ref/hare/strconv/stof.ha:425. "nan"/"infinity"/±"infinity", +// case-insensitive. ww math has no f32 NAN/INF consts → f32frombits of +// the IEEE-754 f32 bit patterns (qNaN 0x7FC00000, ±Inf 0x7F800000 / +// 0xFF800000). +fn special(s: str) (f32 | void) = { + if (ascii.strcasecmp(s, "nan") == 0) { + return math.f32frombits(0x7FC00000u32); + } else if (ascii.strcasecmp(s, "infinity") == 0) { + return math.f32frombits(0x7F800000u32); + } else if (ascii.strcasecmp(s, "+infinity") == 0) { + return math.f32frombits(0x7F800000u32); + } else if (ascii.strcasecmp(s, "-infinity") == 0) { + return math.f32frombits(0xFF800000u32); + }; + return; +}; + +// ref/hare/strconv/stof.ha:445. Parse `s` as f64 (base DEC or HEX). See +// the module note: the EL fast path is HELD; the decimal fallback gives +// correct results meanwhile. +export fn stof64(s: str, b: base) (f64 | invalid | overflow) = { + let bb: base = b; + if (bb == base.DEFAULT) { + bb = base.DEC; + } else if (bb == base.HEX_LOWER) { + bb = base.HEX; + }; + os.assert(bb == base.DEC || bb == base.HEX, + "strconv.stof64: base must be DEC or HEX"); + + if (s.len == 0) { + return 0: invalid; + }; + + match (special(s)) { + case let f: f32 => { return (f: f64); }; + case void => void; + }; + + match (fast_parse(s, bb)) { + case let p: fast_parsed_float => { + if (bb == base.HEX) { + match (hex_to_bits(p, &math.f64info)) { + case let bits: u64 => { return math.f64frombits(bits); }; + case let eo: overflow => { return eo; }; + }; + } else if (!p.truncated) { + match (stof64exact(p.mantissa, p.exponent, p.negative)) { + case let n: f64 => { return n; }; + case void => void; + }; + match (eisel_lemire(p.mantissa, p.exponent, p.negative, + &math.f64info)) { + case let n: u64 => { return math.f64frombits(n); }; + case void => void; + }; + }; + let d = decimal { ... }; + match (decimal_parse(&d, s)) { + case let ei: invalid => { return ei; }; + case void => void; + }; + match (floatbits(&d, &math.f64info)) { + case let n: u64 => { return math.f64frombits(n); }; + case let eo: overflow => { return eo; }; + }; + }; + case let ei: invalid => { return ei; }; + }; + return 0: invalid; // unreachable (path-cov) +}; + +// ref/hare/strconv/stof.ha:491. Parse `s` as f32 (base DEC or HEX). +export fn stof32(s: str, b: base) (f32 | invalid | overflow) = { + let bb: base = b; + if (bb == base.DEFAULT) { + bb = base.DEC; + } else if (bb == base.HEX_LOWER) { + bb = base.HEX; + }; + os.assert(bb == base.DEC || bb == base.HEX, + "strconv.stof32: base must be DEC or HEX"); + + if (s.len == 0) { + return 0: invalid; + }; + + match (special(s)) { + case let f: f32 => { return f; }; + case void => void; + }; + + match (fast_parse(s, bb)) { + case let p: fast_parsed_float => { + if (bb == base.HEX) { + match (hex_to_bits(p, &math.f32info)) { + case let bits: u64 => { + return math.f32frombits(bits: u32); + }; + case let eo: overflow => { return eo; }; + }; + } else if (!p.truncated) { + match (stof32exact(p.mantissa, p.exponent, p.negative)) { + case let n: f32 => { return n; }; + case void => void; + }; + match (eisel_lemire(p.mantissa, p.exponent, p.negative, + &math.f32info)) { + case let n: u64 => { return math.f32frombits(n: u32); }; + case void => void; + }; + }; + let d = decimal { ... }; + match (decimal_parse(&d, s)) { + case let ei: invalid => { return ei; }; + case void => void; + }; + match (floatbits(&d, &math.f32info)) { + case let n: u64 => { return math.f32frombits(n: u32); }; + case let eo: overflow => { return eo; }; + }; + }; + case let ei: invalid => { return ei; }; + }; + return 0: invalid; // unreachable (path-cov) +}; diff --git a/lib/strconv/stof_data.ww b/lib/strconv/stof_data.ww index e81d5b24..1ce03e15 100644 --- a/lib/strconv/stof_data.ww +++ b/lib/strconv/stof_data.ww @@ -11,10 +11,12 @@ // both stages agree on (candidate #130). // // `powers_of_ten: [596][2]u64` (ref/hare/strconv/stof_data.ha:73) is the -// Eisel-Lemire fast-path table and is only consumed by stof.ha (a later -// fold); deferred to its consumer-driven port per CLAUDE.md rule 9 -// (Hare-fidelity is API-shaped, and module-level tables follow their -// caller's scope). +// Eisel-Lemire fast-path table (consumed by stof.ww's eisel_lemire); it +// lands here in fold-4 alongside its consumer, indexed `[exp10 + 307]` +// for exp10 in [-307, 288]. Faithful 2D `[596][2]u64` (the {hi,lo} pair +// IS the 128-bit truncated power-of-ten; rule-12, not flattened) — the +// 2D module-level static-init + double-index read it needs landed in +// #156 (cbeffea). See the table at the foot of this file. package strconv; @@ -93,3 +95,604 @@ let pow5_table: [0x051C]u8 = [ 7u8, 3u8, 7u8, 9u8, 8u8, 8u8, 4u8, 0u8, 3u8, 5u8, 4u8, 7u8, 2u8, 0u8, 5u8, 9u8, 6u8, 2u8, 2u8, 4u8, 0u8, 6u8, 9u8, 5u8, 9u8, 5u8, 3u8, 3u8, 6u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, ]; + +// ref/hare/strconv/stof_data.ha:73. Eisel-Lemire 128-bit power-of-ten +// table (see header note). 596 rows, {hi, lo} u64 pair per row. +let powers_of_ten: [596][2]u64 = [ + [0xA5D3B6D479F8E056u64, 0x8FD0C16206306BABu64], + [0x8F48A4899877186Cu64, 0xB3C4F1BA87BC8696u64], + [0x331ACDABFE94DE87u64, 0xE0B62E2929ABA83Cu64], + [0x9FF0C08B7F1D0B14u64, 0x8C71DCD9BA0B4925u64], + [0x07ECF0AE5EE44DD9u64, 0xAF8E5410288E1B6Fu64], + [0xC9E82CD9F69D6150u64, 0xDB71E91432B1A24Au64], + [0xBE311C083A225CD2u64, 0x892731AC9FAF056Eu64], + [0x6DBD630A48AAF406u64, 0xAB70FE17C79AC6CAu64], + [0x092CBBCCDAD5B108u64, 0xD64D3D9DB981787Du64], + [0x25BBF56008C58EA5u64, 0x85F0468293F0EB4Eu64], + [0xAF2AF2B80AF6F24Eu64, 0xA76C582338ED2621u64], + [0x1AF5AF660DB4AEE1u64, 0xD1476E2C07286FAAu64], + [0x50D98D9FC890ED4Du64, 0x82CCA4DB847945CAu64], + [0xE50FF107BAB528A0u64, 0xA37FCE126597973Cu64], + [0x1E53ED49A96272C8u64, 0xCC5FC196FEFD7D0Cu64], + [0x25E8E89C13BB0F7Au64, 0xFF77B1FCBEBCDC4Fu64], + [0x77B191618C54E9ACu64, 0x9FAACF3DF73609B1u64], + [0xD59DF5B9EF6A2417u64, 0xC795830D75038C1Du64], + [0x4B0573286B44AD1Du64, 0xF97AE3D0D2446F25u64], + [0x4EE367F9430AEC32u64, 0x9BECCE62836AC577u64], + [0x229C41F793CDA73Fu64, 0xC2E801FB244576D5u64], + [0x6B43527578C1110Fu64, 0xF3A20279ED56D48Au64], + [0x830A13896B78AAA9u64, 0x9845418C345644D6u64], + [0x23CC986BC656D553u64, 0xBE5691EF416BD60Cu64], + [0x2CBFBE86B7EC8AA8u64, 0xEDEC366B11C6CB8Fu64], + [0x7BF7D71432F3D6A9u64, 0x94B3A202EB1C3F39u64], + [0xDAF5CCD93FB0CC53u64, 0xB9E08A83A5E34F07u64], + [0xD1B3400F8F9CFF68u64, 0xE858AD248F5C22C9u64], + [0x23100809B9C21FA1u64, 0x91376C36D99995BEu64], + [0xABD40A0C2832A78Au64, 0xB58547448FFFFB2Du64], + [0x16C90C8F323F516Cu64, 0xE2E69915B3FFF9F9u64], + [0xAE3DA7D97F6792E3u64, 0x8DD01FAD907FFC3Bu64], + [0x99CD11CFDF41779Cu64, 0xB1442798F49FFB4Au64], + [0x40405643D711D583u64, 0xDD95317F31C7FA1Du64], + [0x482835EA666B2572u64, 0x8A7D3EEF7F1CFC52u64], + [0xDA3243650005EECFu64, 0xAD1C8EAB5EE43B66u64], + [0x90BED43E40076A82u64, 0xD863B256369D4A40u64], + [0x5A7744A6E804A291u64, 0x873E4F75E2224E68u64], + [0x711515D0A205CB36u64, 0xA90DE3535AAAE202u64], + [0x0D5A5B44CA873E03u64, 0xD3515C2831559A83u64], + [0xE858790AFE9486C2u64, 0x8412D9991ED58091u64], + [0x626E974DBE39A872u64, 0xA5178FFF668AE0B6u64], + [0xFB0A3D212DC8128Fu64, 0xCE5D73FF402D98E3u64], + [0x7CE66634BC9D0B99u64, 0x80FA687F881C7F8Eu64], + [0x1C1FFFC1EBC44E80u64, 0xA139029F6A239F72u64], + [0xA327FFB266B56220u64, 0xC987434744AC874Eu64], + [0x4BF1FF9F0062BAA8u64, 0xFBE9141915D7A922u64], + [0x6F773FC3603DB4A9u64, 0x9D71AC8FADA6C9B5u64], + [0xCB550FB4384D21D3u64, 0xC4CE17B399107C22u64], + [0x7E2A53A146606A48u64, 0xF6019DA07F549B2Bu64], + [0x2EDA7444CBFC426Du64, 0x99C102844F94E0FBu64], + [0xFA911155FEFB5308u64, 0xC0314325637A1939u64], + [0x793555AB7EBA27CAu64, 0xF03D93EEBC589F88u64], + [0x4BC1558B2F3458DEu64, 0x96267C7535B763B5u64], + [0x9EB1AAEDFB016F16u64, 0xBBB01B9283253CA2u64], + [0x465E15A979C1CADCu64, 0xEA9C227723EE8BCBu64], + [0x0BFACD89EC191EC9u64, 0x92A1958A7675175Fu64], + [0xCEF980EC671F667Bu64, 0xB749FAED14125D36u64], + [0x82B7E12780E7401Au64, 0xE51C79A85916F484u64], + [0xD1B2ECB8B0908810u64, 0x8F31CC0937AE58D2u64], + [0x861FA7E6DCB4AA15u64, 0xB2FE3F0B8599EF07u64], + [0x67A791E093E1D49Au64, 0xDFBDCECE67006AC9u64], + [0xE0C8BB2C5C6D24E0u64, 0x8BD6A141006042BDu64], + [0x58FAE9F773886E18u64, 0xAECC49914078536Du64], + [0xAF39A475506A899Eu64, 0xDA7F5BF590966848u64], + [0x6D8406C952429603u64, 0x888F99797A5E012Du64], + [0xC8E5087BA6D33B83u64, 0xAAB37FD7D8F58178u64], + [0xFB1E4A9A90880A64u64, 0xD5605FCDCF32E1D6u64], + [0x5CF2EEA09A55067Fu64, 0x855C3BE0A17FCD26u64], + [0xF42FAA48C0EA481Eu64, 0xA6B34AD8C9DFC06Fu64], + [0xF13B94DAF124DA26u64, 0xD0601D8EFC57B08Bu64], + [0x76C53D08D6B70858u64, 0x823C12795DB6CE57u64], + [0x54768C4B0C64CA6Eu64, 0xA2CB1717B52481EDu64], + [0xA9942F5DCF7DFD09u64, 0xCB7DDCDDA26DA268u64], + [0xD3F93B35435D7C4Cu64, 0xFE5D54150B090B02u64], + [0xC47BC5014A1A6DAFu64, 0x9EFA548D26E5A6E1u64], + [0x359AB6419CA1091Bu64, 0xC6B8E9B0709F109Au64], + [0xC30163D203C94B62u64, 0xF867241C8CC6D4C0u64], + [0x79E0DE63425DCF1Du64, 0x9B407691D7FC44F8u64], + [0x985915FC12F542E4u64, 0xC21094364DFB5636u64], + [0x3E6F5B7B17B2939Du64, 0xF294B943E17A2BC4u64], + [0xA705992CEECF9C42u64, 0x979CF3CA6CEC5B5Au64], + [0x50C6FF782A838353u64, 0xBD8430BD08277231u64], + [0xA4F8BF5635246428u64, 0xECE53CEC4A314EBDu64], + [0x871B7795E136BE99u64, 0x940F4613AE5ED136u64], + [0x28E2557B59846E3Fu64, 0xB913179899F68584u64], + [0x331AEADA2FE589CFu64, 0xE757DD7EC07426E5u64], + [0x3FF0D2C85DEF7621u64, 0x9096EA6F3848984Fu64], + [0x0FED077A756B53A9u64, 0xB4BCA50B065ABE63u64], + [0xD3E8495912C62894u64, 0xE1EBCE4DC7F16DFBu64], + [0x64712DD7ABBBD95Cu64, 0x8D3360F09CF6E4BDu64], + [0xBD8D794D96AACFB3u64, 0xB080392CC4349DECu64], + [0xECF0D7A0FC5583A0u64, 0xDCA04777F541C567u64], + [0xF41686C49DB57244u64, 0x89E42CAAF9491B60u64], + [0x311C2875C522CED5u64, 0xAC5D37D5B79B6239u64], + [0x7D633293366B828Bu64, 0xD77485CB25823AC7u64], + [0xAE5DFF9C02033197u64, 0x86A8D39EF77164BCu64], + [0xD9F57F830283FDFCu64, 0xA8530886B54DBDEBu64], + [0xD072DF63C324FD7Bu64, 0xD267CAA862A12D66u64], + [0x4247CB9E59F71E6Du64, 0x8380DEA93DA4BC60u64], + [0x52D9BE85F074E608u64, 0xA46116538D0DEB78u64], + [0x67902E276C921F8Bu64, 0xCD795BE870516656u64], + [0x00BA1CD8A3DB53B6u64, 0x806BD9714632DFF6u64], + [0x80E8A40ECCD228A4u64, 0xA086CFCD97BF97F3u64], + [0x6122CD128006B2CDu64, 0xC8A883C0FDAF7DF0u64], + [0x796B805720085F81u64, 0xFAD2A4B13D1B5D6Cu64], + [0xCBE3303674053BB0u64, 0x9CC3A6EEC6311A63u64], + [0xBEDBFC4411068A9Cu64, 0xC3F490AA77BD60FCu64], + [0xEE92FB5515482D44u64, 0xF4F1B4D515ACB93Bu64], + [0x751BDD152D4D1C4Au64, 0x991711052D8BF3C5u64], + [0xD262D45A78A0635Du64, 0xBF5CD54678EEF0B6u64], + [0x86FB897116C87C34u64, 0xEF340A98172AACE4u64], + [0xD45D35E6AE3D4DA0u64, 0x9580869F0E7AAC0Eu64], + [0x8974836059CCA109u64, 0xBAE0A846D2195712u64], + [0x2BD1A438703FC94Bu64, 0xE998D258869FACD7u64], + [0x7B6306A34627DDCFu64, 0x91FF83775423CC06u64], + [0x1A3BC84C17B1D542u64, 0xB67F6455292CBF08u64], + [0x20CABA5F1D9E4A93u64, 0xE41F3D6A7377EECAu64], + [0x547EB47B7282EE9Cu64, 0x8E938662882AF53Eu64], + [0xE99E619A4F23AA43u64, 0xB23867FB2A35B28Du64], + [0x6405FA00E2EC94D4u64, 0xDEC681F9F4C31F31u64], + [0xDE83BC408DD3DD04u64, 0x8B3C113C38F9F37Eu64], + [0x9624AB50B148D445u64, 0xAE0B158B4738705Eu64], + [0x3BADD624DD9B0957u64, 0xD98DDAEE19068C76u64], + [0xE54CA5D70A80E5D6u64, 0x87F8A8D4CFA417C9u64], + [0x5E9FCF4CCD211F4Cu64, 0xA9F6D30A038D1DBCu64], + [0x7647C3200069671Fu64, 0xD47487CC8470652Bu64], + [0x29ECD9F40041E073u64, 0x84C8D4DFD2C63F3Bu64], + [0xF468107100525890u64, 0xA5FB0A17C777CF09u64], + [0x7182148D4066EEB4u64, 0xCF79CC9DB955C2CCu64], + [0xC6F14CD848405530u64, 0x81AC1FE293D599BFu64], + [0xB8ADA00E5A506A7Cu64, 0xA21727DB38CB002Fu64], + [0xA6D90811F0E4851Cu64, 0xCA9CF1D206FDC03Bu64], + [0x908F4A166D1DA663u64, 0xFD442E4688BD304Au64], + [0x9A598E4E043287FEu64, 0x9E4A9CEC15763E2Eu64], + [0x40EFF1E1853F29FDu64, 0xC5DD44271AD3CDBAu64], + [0xD12BEE59E68EF47Cu64, 0xF7549530E188C128u64], + [0x82BB74F8301958CEu64, 0x9A94DD3E8CF578B9u64], + [0xE36A52363C1FAF01u64, 0xC13A148E3032D6E7u64], + [0xDC44E6C3CB279AC1u64, 0xF18899B1BC3F8CA1u64], + [0x29AB103A5EF8C0B9u64, 0x96F5600F15A7B7E5u64], + [0x7415D448F6B6F0E7u64, 0xBCB2B812DB11A5DEu64], + [0x111B495B3464AD21u64, 0xEBDF661791D60F56u64], + [0xCAB10DD900BEEC34u64, 0x936B9FCEBB25C995u64], + [0x3D5D514F40EEA742u64, 0xB84687C269EF3BFBu64], + [0x0CB4A5A3112A5112u64, 0xE65829B3046B0AFAu64], + [0x47F0E785EABA72ABu64, 0x8FF71A0FE2C2E6DCu64], + [0x59ED216765690F56u64, 0xB3F4E093DB73A093u64], + [0x306869C13EC3532Cu64, 0xE0F218B8D25088B8u64], + [0x1E414218C73A13FBu64, 0x8C974F7383725573u64], + [0xE5D1929EF90898FAu64, 0xAFBD2350644EEACFu64], + [0xDF45F746B74ABF39u64, 0xDBAC6C247D62A583u64], + [0x6B8BBA8C328EB783u64, 0x894BC396CE5DA772u64], + [0x066EA92F3F326564u64, 0xAB9EB47C81F5114Fu64], + [0xC80A537B0EFEFEBDu64, 0xD686619BA27255A2u64], + [0xBD06742CE95F5F36u64, 0x8613FD0145877585u64], + [0x2C48113823B73704u64, 0xA798FC4196E952E7u64], + [0xF75A15862CA504C5u64, 0xD17F3B51FCA3A7A0u64], + [0x9A984D73DBE722FBu64, 0x82EF85133DE648C4u64], + [0xC13E60D0D2E0EBBAu64, 0xA3AB66580D5FDAF5u64], + [0x318DF905079926A8u64, 0xCC963FEE10B7D1B3u64], + [0xFDF17746497F7052u64, 0xFFBBCFE994E5C61Fu64], + [0xFEB6EA8BEDEFA633u64, 0x9FD561F1FD0F9BD3u64], + [0xFE64A52EE96B8FC0u64, 0xC7CABA6E7C5382C8u64], + [0x3DFDCE7AA3C673B0u64, 0xF9BD690A1B68637Bu64], + [0x06BEA10CA65C084Eu64, 0x9C1661A651213E2Du64], + [0x486E494FCFF30A62u64, 0xC31BFA0FE5698DB8u64], + [0x5A89DBA3C3EFCCFAu64, 0xF3E2F893DEC3F126u64], + [0xF89629465A75E01Cu64, 0x986DDB5C6B3A76B7u64], + [0xF6BBB397F1135823u64, 0xBE89523386091465u64], + [0x746AA07DED582E2Cu64, 0xEE2BA6C0678B597Fu64], + [0xA8C2A44EB4571CDCu64, 0x94DB483840B717EFu64], + [0x92F34D62616CE413u64, 0xBA121A4650E4DDEBu64], + [0x77B020BAF9C81D17u64, 0xE896A0D7E51E1566u64], + [0x0ACE1474DC1D122Eu64, 0x915E2486EF32CD60u64], + [0x0D819992132456BAu64, 0xB5B5ADA8AAFF80B8u64], + [0x10E1FFF697ED6C69u64, 0xE3231912D5BF60E6u64], + [0xCA8D3FFA1EF463C1u64, 0x8DF5EFABC5979C8Fu64], + [0xBD308FF8A6B17CB2u64, 0xB1736B96B6FD83B3u64], + [0xAC7CB3F6D05DDBDEu64, 0xDDD0467C64BCE4A0u64], + [0x6BCDF07A423AA96Bu64, 0x8AA22C0DBEF60EE4u64], + [0x86C16C98D2C953C6u64, 0xAD4AB7112EB3929Du64], + [0xE871C7BF077BA8B7u64, 0xD89D64D57A607744u64], + [0x11471CD764AD4972u64, 0x87625F056C7C4A8Bu64], + [0xD598E40D3DD89BCFu64, 0xA93AF6C6C79B5D2Du64], + [0x4AFF1D108D4EC2C3u64, 0xD389B47879823479u64], + [0xCEDF722A585139BAu64, 0x843610CB4BF160CBu64], + [0xC2974EB4EE658828u64, 0xA54394FE1EEDB8FEu64], + [0x733D226229FEEA32u64, 0xCE947A3DA6A9273Eu64], + [0x0806357D5A3F525Fu64, 0x811CCC668829B887u64], + [0xCA07C2DCB0CF26F7u64, 0xA163FF802A3426A8u64], + [0xFC89B393DD02F0B5u64, 0xC9BCFF6034C13052u64], + [0xBBAC2078D443ACE2u64, 0xFC2C3F3841F17C67u64], + [0xD54B944B84AA4C0Du64, 0x9D9BA7832936EDC0u64], + [0x0A9E795E65D4DF11u64, 0xC5029163F384A931u64], + [0x4D4617B5FF4A16D5u64, 0xF64335BCF065D37Du64], + [0x504BCED1BF8E4E45u64, 0x99EA0196163FA42Eu64], + [0xE45EC2862F71E1D6u64, 0xC06481FB9BCF8D39u64], + [0x5D767327BB4E5A4Cu64, 0xF07DA27A82C37088u64], + [0x3A6A07F8D510F86Fu64, 0x964E858C91BA2655u64], + [0x890489F70A55368Bu64, 0xBBE226EFB628AFEAu64], + [0x2B45AC74CCEA842Eu64, 0xEADAB0ABA3B2DBE5u64], + [0x3B0B8BC90012929Du64, 0x92C8AE6B464FC96Fu64], + [0x09CE6EBB40173744u64, 0xB77ADA0617E3BBCBu64], + [0xCC420A6A101D0515u64, 0xE55990879DDCAABDu64], + [0x9FA946824A12232Du64, 0x8F57FA54C2A9EAB6u64], + [0x47939822DC96ABF9u64, 0xB32DF8E9F3546564u64], + [0x59787E2B93BC56F7u64, 0xDFF9772470297EBDu64], + [0x57EB4EDB3C55B65Au64, 0x8BFBEA76C619EF36u64], + [0xEDE622920B6B23F1u64, 0xAEFAE51477A06B03u64], + [0xE95FAB368E45ECEDu64, 0xDAB99E59958885C4u64], + [0x11DBCB0218EBB414u64, 0x88B402F7FD75539Bu64], + [0xD652BDC29F26A119u64, 0xAAE103B5FCD2A881u64], + [0x4BE76D3346F0495Fu64, 0xD59944A37C0752A2u64], + [0x6F70A4400C562DDBu64, 0x857FCAE62D8493A5u64], + [0xCB4CCD500F6BB952u64, 0xA6DFBD9FB8E5B88Eu64], + [0x7E2000A41346A7A7u64, 0xD097AD07A71F26B2u64], + [0x8ED400668C0C28C8u64, 0x825ECC24C873782Fu64], + [0x728900802F0F32FAu64, 0xA2F67F2DFA90563Bu64], + [0x4F2B40A03AD2FFB9u64, 0xCBB41EF979346BCAu64], + [0xE2F610C84987BFA8u64, 0xFEA126B7D78186BCu64], + [0x0DD9CA7D2DF4D7C9u64, 0x9F24B832E6B0F436u64], + [0x91503D1C79720DBBu64, 0xC6EDE63FA05D3143u64], + [0x75A44C6397CE912Au64, 0xF8A95FCF88747D94u64], + [0xC986AFBE3EE11ABAu64, 0x9B69DBE1B548CE7Cu64], + [0xFBE85BADCE996168u64, 0xC24452DA229B021Bu64], + [0xFAE27299423FB9C3u64, 0xF2D56790AB41C2A2u64], + [0xDCCD879FC967D41Au64, 0x97C560BA6B0919A5u64], + [0x5400E987BBC1C920u64, 0xBDB6B8E905CB600Fu64], + [0x290123E9AAB23B68u64, 0xED246723473E3813u64], + [0xF9A0B6720AAF6521u64, 0x9436C0760C86E30Bu64], + [0xF808E40E8D5B3E69u64, 0xB94470938FA89BCEu64], + [0xB60B1D1230B20E04u64, 0xE7958CB87392C2C2u64], + [0xB1C6F22B5E6F48C2u64, 0x90BD77F3483BB9B9u64], + [0x1E38AEB6360B1AF3u64, 0xB4ECD5F01A4AA828u64], + [0x25C6DA63C38DE1B0u64, 0xE2280B6C20DD5232u64], + [0x579C487E5A38AD0Eu64, 0x8D590723948A535Fu64], + [0x2D835A9DF0C6D851u64, 0xB0AF48EC79ACE837u64], + [0xF8E431456CF88E65u64, 0xDCDB1B2798182244u64], + [0x1B8E9ECB641B58FFu64, 0x8A08F0F8BF0F156Bu64], + [0xE272467E3D222F3Fu64, 0xAC8B2D36EED2DAC5u64], + [0x5B0ED81DCC6ABB0Fu64, 0xD7ADF884AA879177u64], + [0x98E947129FC2B4E9u64, 0x86CCBB52EA94BAEAu64], + [0x3F2398D747B36224u64, 0xA87FEA27A539E9A5u64], + [0x8EEC7F0D19A03AADu64, 0xD29FE4B18E88640Eu64], + [0x1953CF68300424ACu64, 0x83A3EEEEF9153E89u64], + [0x5FA8C3423C052DD7u64, 0xA48CEAAAB75A8E2Bu64], + [0x3792F412CB06794Du64, 0xCDB02555653131B6u64], + [0xE2BBD88BBEE40BD0u64, 0x808E17555F3EBF11u64], + [0x5B6ACEAEAE9D0EC4u64, 0xA0B19D2AB70E6ED6u64], + [0xF245825A5A445275u64, 0xC8DE047564D20A8Bu64], + [0xEED6E2F0F0D56712u64, 0xFB158592BE068D2Eu64], + [0x55464DD69685606Bu64, 0x9CED737BB6C4183Du64], + [0xAA97E14C3C26B886u64, 0xC428D05AA4751E4Cu64], + [0xD53DD99F4B3066A8u64, 0xF53304714D9265DFu64], + [0xE546A8038EFE4029u64, 0x993FE2C6D07B7FABu64], + [0xDE98520472BDD033u64, 0xBF8FDB78849A5F96u64], + [0x963E66858F6D4440u64, 0xEF73D256A5C0F77Cu64], + [0xDDE7001379A44AA8u64, 0x95A8637627989AADu64], + [0x5560C018580D5D52u64, 0xBB127C53B17EC159u64], + [0xAAB8F01E6E10B4A6u64, 0xE9D71B689DDE71AFu64], + [0xCAB3961304CA70E8u64, 0x9226712162AB070Du64], + [0x3D607B97C5FD0D22u64, 0xB6B00D69BB55C8D1u64], + [0x8CB89A7DB77C506Au64, 0xE45C10C42A2B3B05u64], + [0x77F3608E92ADB242u64, 0x8EB98A7A9A5B04E3u64], + [0x55F038B237591ED3u64, 0xB267ED1940F1C61Cu64], + [0x6B6C46DEC52F6688u64, 0xDF01E85F912E37A3u64], + [0x2323AC4B3B3DA015u64, 0x8B61313BBABCE2C6u64], + [0xABEC975E0A0D081Au64, 0xAE397D8AA96C1B77u64], + [0x96E7BD358C904A21u64, 0xD9C7DCED53C72255u64], + [0x7E50D64177DA2E54u64, 0x881CEA14545C7575u64], + [0xDDE50BD1D5D0B9E9u64, 0xAA242499697392D2u64], + [0x955E4EC64B44E864u64, 0xD4AD2DBFC3D07787u64], + [0xBD5AF13BEF0B113Eu64, 0x84EC3C97DA624AB4u64], + [0xECB1AD8AEACDD58Eu64, 0xA6274BBDD0FADD61u64], + [0x67DE18EDA5814AF2u64, 0xCFB11EAD453994BAu64], + [0x80EACF948770CED7u64, 0x81CEB32C4B43FCF4u64], + [0xA1258379A94D028Du64, 0xA2425FF75E14FC31u64], + [0x096EE45813A04330u64, 0xCAD2F7F5359A3B3Eu64], + [0x8BCA9D6E188853FCu64, 0xFD87B5F28300CA0Du64], + [0x775EA264CF55347Du64, 0x9E74D1B791E07E48u64], + [0x95364AFE032A819Du64, 0xC612062576589DDAu64], + [0x3A83DDBD83F52204u64, 0xF79687AED3EEC551u64], + [0xC4926A9672793542u64, 0x9ABE14CD44753B52u64], + [0x75B7053C0F178293u64, 0xC16D9A0095928A27u64], + [0x5324C68B12DD6338u64, 0xF1C90080BAF72CB1u64], + [0xD3F6FC16EBCA5E03u64, 0x971DA05074DA7BEEu64], + [0x88F4BB1CA6BCF584u64, 0xBCE5086492111AEAu64], + [0x2B31E9E3D06C32E5u64, 0xEC1E4A7DB69561A5u64], + [0x3AFF322E62439FCFu64, 0x9392EE8E921D5D07u64], + [0x09BEFEB9FAD487C2u64, 0xB877AA3236A4B449u64], + [0x4C2EBE687989A9B3u64, 0xE69594BEC44DE15Bu64], + [0x0F9D37014BF60A10u64, 0x901D7CF73AB0ACD9u64], + [0x538484C19EF38C94u64, 0xB424DC35095CD80Fu64], + [0x2865A5F206B06FB9u64, 0xE12E13424BB40E13u64], + [0xF93F87B7442E45D3u64, 0x8CBCCC096F5088CBu64], + [0xF78F69A51539D748u64, 0xAFEBFF0BCB24AAFEu64], + [0xB573440E5A884D1Bu64, 0xDBE6FECEBDEDD5BEu64], + [0x31680A88F8953030u64, 0x89705F4136B4A597u64], + [0xFDC20D2B36BA7C3Du64, 0xABCC77118461CEFCu64], + [0x3D32907604691B4Cu64, 0xD6BF94D5E57A42BCu64], + [0xA63F9A49C2C1B10Fu64, 0x8637BD05AF6C69B5u64], + [0x0FCF80DC33721D53u64, 0xA7C5AC471B478423u64], + [0xD3C36113404EA4A8u64, 0xD1B71758E219652Bu64], + [0x645A1CAC083126E9u64, 0x83126E978D4FDF3Bu64], + [0x3D70A3D70A3D70A3u64, 0xA3D70A3D70A3D70Au64], + [0xCCCCCCCCCCCCCCCCu64, 0xCCCCCCCCCCCCCCCCu64], + [0x0000000000000000u64, 0x8000000000000000u64], + [0x0000000000000000u64, 0xA000000000000000u64], + [0x0000000000000000u64, 0xC800000000000000u64], + [0x0000000000000000u64, 0xFA00000000000000u64], + [0x0000000000000000u64, 0x9C40000000000000u64], + [0x0000000000000000u64, 0xC350000000000000u64], + [0x0000000000000000u64, 0xF424000000000000u64], + [0x0000000000000000u64, 0x9896800000000000u64], + [0x0000000000000000u64, 0xBEBC200000000000u64], + [0x0000000000000000u64, 0xEE6B280000000000u64], + [0x0000000000000000u64, 0x9502F90000000000u64], + [0x0000000000000000u64, 0xBA43B74000000000u64], + [0x0000000000000000u64, 0xE8D4A51000000000u64], + [0x0000000000000000u64, 0x9184E72A00000000u64], + [0x0000000000000000u64, 0xB5E620F480000000u64], + [0x0000000000000000u64, 0xE35FA931A0000000u64], + [0x0000000000000000u64, 0x8E1BC9BF04000000u64], + [0x0000000000000000u64, 0xB1A2BC2EC5000000u64], + [0x0000000000000000u64, 0xDE0B6B3A76400000u64], + [0x0000000000000000u64, 0x8AC7230489E80000u64], + [0x0000000000000000u64, 0xAD78EBC5AC620000u64], + [0x0000000000000000u64, 0xD8D726B7177A8000u64], + [0x0000000000000000u64, 0x878678326EAC9000u64], + [0x0000000000000000u64, 0xA968163F0A57B400u64], + [0x0000000000000000u64, 0xD3C21BCECCEDA100u64], + [0x0000000000000000u64, 0x84595161401484A0u64], + [0x0000000000000000u64, 0xA56FA5B99019A5C8u64], + [0x0000000000000000u64, 0xCECB8F27F4200F3Au64], + [0x4000000000000000u64, 0x813F3978F8940984u64], + [0x5000000000000000u64, 0xA18F07D736B90BE5u64], + [0xA400000000000000u64, 0xC9F2C9CD04674EDEu64], + [0x4D00000000000000u64, 0xFC6F7C4045812296u64], + [0xF020000000000000u64, 0x9DC5ADA82B70B59Du64], + [0x6C28000000000000u64, 0xC5371912364CE305u64], + [0xC732000000000000u64, 0xF684DF56C3E01BC6u64], + [0x3C7F400000000000u64, 0x9A130B963A6C115Cu64], + [0x4B9F100000000000u64, 0xC097CE7BC90715B3u64], + [0x1E86D40000000000u64, 0xF0BDC21ABB48DB20u64], + [0x1314448000000000u64, 0x96769950B50D88F4u64], + [0x17D955A000000000u64, 0xBC143FA4E250EB31u64], + [0x5DCFAB0800000000u64, 0xEB194F8E1AE525FDu64], + [0x5AA1CAE500000000u64, 0x92EFD1B8D0CF37BEu64], + [0xF14A3D9E40000000u64, 0xB7ABC627050305ADu64], + [0x6D9CCD05D0000000u64, 0xE596B7B0C643C719u64], + [0xE4820023A2000000u64, 0x8F7E32CE7BEA5C6Fu64], + [0xDDA2802C8A800000u64, 0xB35DBF821AE4F38Bu64], + [0xD50B2037AD200000u64, 0xE0352F62A19E306Eu64], + [0x4526F422CC340000u64, 0x8C213D9DA502DE45u64], + [0x9670B12B7F410000u64, 0xAF298D050E4395D6u64], + [0x3C0CDD765F114000u64, 0xDAF3F04651D47B4Cu64], + [0xA5880A69FB6AC800u64, 0x88D8762BF324CD0Fu64], + [0x8EEA0D047A457A00u64, 0xAB0E93B6EFEE0053u64], + [0x72A4904598D6D880u64, 0xD5D238A4ABE98068u64], + [0x47A6DA2B7F864750u64, 0x85A36366EB71F041u64], + [0x999090B65F67D924u64, 0xA70C3C40A64E6C51u64], + [0xFFF4B4E3F741CF6Du64, 0xD0CF4B50CFE20765u64], + [0xBFF8F10E7A8921A4u64, 0x82818F1281ED449Fu64], + [0xAFF72D52192B6A0Du64, 0xA321F2D7226895C7u64], + [0x9BF4F8A69F764490u64, 0xCBEA6F8CEB02BB39u64], + [0x02F236D04753D5B4u64, 0xFEE50B7025C36A08u64], + [0x01D762422C946590u64, 0x9F4F2726179A2245u64], + [0x424D3AD2B7B97EF5u64, 0xC722F0EF9D80AAD6u64], + [0xD2E0898765A7DEB2u64, 0xF8EBAD2B84E0D58Bu64], + [0x63CC55F49F88EB2Fu64, 0x9B934C3B330C8577u64], + [0x3CBF6B71C76B25FBu64, 0xC2781F49FFCFA6D5u64], + [0x8BEF464E3945EF7Au64, 0xF316271C7FC3908Au64], + [0x97758BF0E3CBB5ACu64, 0x97EDD871CFDA3A56u64], + [0x3D52EEED1CBEA317u64, 0xBDE94E8E43D0C8ECu64], + [0x4CA7AAA863EE4BDDu64, 0xED63A231D4C4FB27u64], + [0x8FE8CAA93E74EF6Au64, 0x945E455F24FB1CF8u64], + [0xB3E2FD538E122B44u64, 0xB975D6B6EE39E436u64], + [0x60DBBCA87196B616u64, 0xE7D34C64A9C85D44u64], + [0xBC8955E946FE31CDu64, 0x90E40FBEEA1D3A4Au64], + [0x6BABAB6398BDBE41u64, 0xB51D13AEA4A488DDu64], + [0xC696963C7EED2DD1u64, 0xE264589A4DCDAB14u64], + [0xFC1E1DE5CF543CA2u64, 0x8D7EB76070A08AECu64], + [0x3B25A55F43294BCBu64, 0xB0DE65388CC8ADA8u64], + [0x49EF0EB713F39EBEu64, 0xDD15FE86AFFAD912u64], + [0x6E3569326C784337u64, 0x8A2DBF142DFCC7ABu64], + [0x49C2C37F07965404u64, 0xACB92ED9397BF996u64], + [0xDC33745EC97BE906u64, 0xD7E77A8F87DAF7FBu64], + [0x69A028BB3DED71A3u64, 0x86F0AC99B4E8DAFDu64], + [0xC40832EA0D68CE0Cu64, 0xA8ACD7C0222311BCu64], + [0xF50A3FA490C30190u64, 0xD2D80DB02AABD62Bu64], + [0x792667C6DA79E0FAu64, 0x83C7088E1AAB65DBu64], + [0x577001B891185938u64, 0xA4B8CAB1A1563F52u64], + [0xED4C0226B55E6F86u64, 0xCDE6FD5E09ABCF26u64], + [0x544F8158315B05B4u64, 0x80B05E5AC60B6178u64], + [0x696361AE3DB1C721u64, 0xA0DC75F1778E39D6u64], + [0x03BC3A19CD1E38E9u64, 0xC913936DD571C84Cu64], + [0x04AB48A04065C723u64, 0xFB5878494ACE3A5Fu64], + [0x62EB0D64283F9C76u64, 0x9D174B2DCEC0E47Bu64], + [0x3BA5D0BD324F8394u64, 0xC45D1DF942711D9Au64], + [0xCA8F44EC7EE36479u64, 0xF5746577930D6500u64], + [0x7E998B13CF4E1ECBu64, 0x9968BF6ABBE85F20u64], + [0x9E3FEDD8C321A67Eu64, 0xBFC2EF456AE276E8u64], + [0xC5CFE94EF3EA101Eu64, 0xEFB3AB16C59B14A2u64], + [0xBBA1F1D158724A12u64, 0x95D04AEE3B80ECE5u64], + [0x2A8A6E45AE8EDC97u64, 0xBB445DA9CA61281Fu64], + [0xF52D09D71A3293BDu64, 0xEA1575143CF97226u64], + [0x593C2626705F9C56u64, 0x924D692CA61BE758u64], + [0x6F8B2FB00C77836Cu64, 0xB6E0C377CFA2E12Eu64], + [0x0B6DFB9C0F956447u64, 0xE498F455C38B997Au64], + [0x4724BD4189BD5EACu64, 0x8EDF98B59A373FECu64], + [0x58EDEC91EC2CB657u64, 0xB2977EE300C50FE7u64], + [0x2F2967B66737E3EDu64, 0xDF3D5E9BC0F653E1u64], + [0xBD79E0D20082EE74u64, 0x8B865B215899F46Cu64], + [0xECD8590680A3AA11u64, 0xAE67F1E9AEC07187u64], + [0xE80E6F4820CC9495u64, 0xDA01EE641A708DE9u64], + [0x3109058D147FDCDDu64, 0x884134FE908658B2u64], + [0xBD4B46F0599FD415u64, 0xAA51823E34A7EEDEu64], + [0x6C9E18AC7007C91Au64, 0xD4E5E2CDC1D1EA96u64], + [0x03E2CF6BC604DDB0u64, 0x850FADC09923329Eu64], + [0x84DB8346B786151Cu64, 0xA6539930BF6BFF45u64], + [0xE612641865679A63u64, 0xCFE87F7CEF46FF16u64], + [0x4FCB7E8F3F60C07Eu64, 0x81F14FAE158C5F6Eu64], + [0xE3BE5E330F38F09Du64, 0xA26DA3999AEF7749u64], + [0x5CADF5BFD3072CC5u64, 0xCB090C8001AB551Cu64], + [0x73D9732FC7C8F7F6u64, 0xFDCB4FA002162A63u64], + [0x2867E7FDDCDD9AFAu64, 0x9E9F11C4014DDA7Eu64], + [0xB281E1FD541501B8u64, 0xC646D63501A1511Du64], + [0x1F225A7CA91A4226u64, 0xF7D88BC24209A565u64], + [0x3375788DE9B06958u64, 0x9AE757596946075Fu64], + [0x0052D6B1641C83AEu64, 0xC1A12D2FC3978937u64], + [0xC0678C5DBD23A49Au64, 0xF209787BB47D6B84u64], + [0xF840B7BA963646E0u64, 0x9745EB4D50CE6332u64], + [0xB650E5A93BC3D898u64, 0xBD176620A501FBFFu64], + [0xA3E51F138AB4CEBEu64, 0xEC5D3FA8CE427AFFu64], + [0xC66F336C36B10137u64, 0x93BA47C980E98CDFu64], + [0xB80B0047445D4184u64, 0xB8A8D9BBE123F017u64], + [0xA60DC059157491E5u64, 0xE6D3102AD96CEC1Du64], + [0x87C89837AD68DB2Fu64, 0x9043EA1AC7E41392u64], + [0x29BABE4598C311FBu64, 0xB454E4A179DD1877u64], + [0xF4296DD6FEF3D67Au64, 0xE16A1DC9D8545E94u64], + [0x1899E4A65F58660Cu64, 0x8CE2529E2734BB1Du64], + [0x5EC05DCFF72E7F8Fu64, 0xB01AE745B101E9E4u64], + [0x76707543F4FA1F73u64, 0xDC21A1171D42645Du64], + [0x6A06494A791C53A8u64, 0x899504AE72497EBAu64], + [0x0487DB9D17636892u64, 0xABFA45DA0EDBDE69u64], + [0x45A9D2845D3C42B6u64, 0xD6F8D7509292D603u64], + [0x0B8A2392BA45A9B2u64, 0x865B86925B9BC5C2u64], + [0x8E6CAC7768D7141Eu64, 0xA7F26836F282B732u64], + [0x3207D795430CD926u64, 0xD1EF0244AF2364FFu64], + [0x7F44E6BD49E807B8u64, 0x8335616AED761F1Fu64], + [0x5F16206C9C6209A6u64, 0xA402B9C5A8D3A6E7u64], + [0x36DBA887C37A8C0Fu64, 0xCD036837130890A1u64], + [0xC2494954DA2C9789u64, 0x802221226BE55A64u64], + [0xF2DB9BAA10B7BD6Cu64, 0xA02AA96B06DEB0FDu64], + [0x6F92829494E5ACC7u64, 0xC83553C5C8965D3Du64], + [0xCB772339BA1F17F9u64, 0xFA42A8B73ABBF48Cu64], + [0xFF2A760414536EFBu64, 0x9C69A97284B578D7u64], + [0xFEF5138519684ABAu64, 0xC38413CF25E2D70Du64], + [0x7EB258665FC25D69u64, 0xF46518C2EF5B8CD1u64], + [0xEF2F773FFBD97A61u64, 0x98BF2F79D5993802u64], + [0xAAFB550FFACFD8FAu64, 0xBEEEFB584AFF8603u64], + [0x95BA2A53F983CF38u64, 0xEEAABA2E5DBF6784u64], + [0xDD945A747BF26183u64, 0x952AB45CFA97A0B2u64], + [0x94F971119AEEF9E4u64, 0xBA756174393D88DFu64], + [0x7A37CD5601AAB85Du64, 0xE912B9D1478CEB17u64], + [0xAC62E055C10AB33Au64, 0x91ABB422CCB812EEu64], + [0x577B986B314D6009u64, 0xB616A12B7FE617AAu64], + [0xED5A7E85FDA0B80Bu64, 0xE39C49765FDF9D94u64], + [0x14588F13BE847307u64, 0x8E41ADE9FBEBC27Du64], + [0x596EB2D8AE258FC8u64, 0xB1D219647AE6B31Cu64], + [0x6FCA5F8ED9AEF3BBu64, 0xDE469FBD99A05FE3u64], + [0x25DE7BB9480D5854u64, 0x8AEC23D680043BEEu64], + [0xAF561AA79A10AE6Au64, 0xADA72CCC20054AE9u64], + [0x1B2BA1518094DA04u64, 0xD910F7FF28069DA4u64], + [0x90FB44D2F05D0842u64, 0x87AA9AFF79042286u64], + [0x353A1607AC744A53u64, 0xA99541BF57452B28u64], + [0x42889B8997915CE8u64, 0xD3FA922F2D1675F2u64], + [0x69956135FEBADA11u64, 0x847C9B5D7C2E09B7u64], + [0x43FAB9837E699095u64, 0xA59BC234DB398C25u64], + [0x94F967E45E03F4BBu64, 0xCF02B2C21207EF2Eu64], + [0x1D1BE0EEBAC278F5u64, 0x8161AFB94B44F57Du64], + [0x6462D92A69731732u64, 0xA1BA1BA79E1632DCu64], + [0x7D7B8F7503CFDCFEu64, 0xCA28A291859BBF93u64], + [0x5CDA735244C3D43Eu64, 0xFCB2CB35E702AF78u64], + [0x3A0888136AFA64A7u64, 0x9DEFBF01B061ADABu64], + [0x088AAA1845B8FDD0u64, 0xC56BAEC21C7A1916u64], + [0x8AAD549E57273D45u64, 0xF6C69A72A3989F5Bu64], + [0x36AC54E2F678864Bu64, 0x9A3C2087A63F6399u64], + [0x84576A1BB416A7DDu64, 0xC0CB28A98FCF3C7Fu64], + [0x656D44A2A11C51D5u64, 0xF0FDF2D3F3C30B9Fu64], + [0x9F644AE5A4B1B325u64, 0x969EB7C47859E743u64], + [0x873D5D9F0DDE1FEEu64, 0xBC4665B596706114u64], + [0xA90CB506D155A7EAu64, 0xEB57FF22FC0C7959u64], + [0x09A7F12442D588F2u64, 0x9316FF75DD87CBD8u64], + [0x0C11ED6D538AEB2Fu64, 0xB7DCBF5354E9BECEu64], + [0x8F1668C8A86DA5FAu64, 0xE5D3EF282A242E81u64], + [0xF96E017D694487BCu64, 0x8FA475791A569D10u64], + [0x37C981DCC395A9ACu64, 0xB38D92D760EC4455u64], + [0x85BBE253F47B1417u64, 0xE070F78D3927556Au64], + [0x93956D7478CCEC8Eu64, 0x8C469AB843B89562u64], + [0x387AC8D1970027B2u64, 0xAF58416654A6BABBu64], + [0x06997B05FCC0319Eu64, 0xDB2E51BFE9D0696Au64], + [0x441FECE3BDF81F03u64, 0x88FCF317F22241E2u64], + [0xD527E81CAD7626C3u64, 0xAB3C2FDDEEAAD25Au64], + [0x8A71E223D8D3B074u64, 0xD60B3BD56A5586F1u64], + [0xF6872D5667844E49u64, 0x85C7056562757456u64], + [0xB428F8AC016561DBu64, 0xA738C6BEBB12D16Cu64], + [0xE13336D701BEBA52u64, 0xD106F86E69D785C7u64], + [0xECC0024661173473u64, 0x82A45B450226B39Cu64], + [0x27F002D7F95D0190u64, 0xA34D721642B06084u64], + [0x31EC038DF7B441F4u64, 0xCC20CE9BD35C78A5u64], + [0x7E67047175A15271u64, 0xFF290242C83396CEu64], + [0x0F0062C6E984D386u64, 0x9F79A169BD203E41u64], + [0x52C07B78A3E60868u64, 0xC75809C42C684DD1u64], + [0xA7709A56CCDF8A82u64, 0xF92E0C3537826145u64], + [0x88A66076400BB691u64, 0x9BBCC7A142B17CCBu64], + [0x6ACFF893D00EA435u64, 0xC2ABF989935DDBFEu64], + [0x0583F6B8C4124D43u64, 0xF356F7EBF83552FEu64], + [0xC3727A337A8B704Au64, 0x98165AF37B2153DEu64], + [0x744F18C0592E4C5Cu64, 0xBE1BF1B059E9A8D6u64], + [0x1162DEF06F79DF73u64, 0xEDA2EE1C7064130Cu64], + [0x8ADDCB5645AC2BA8u64, 0x9485D4D1C63E8BE7u64], + [0x6D953E2BD7173692u64, 0xB9A74A0637CE2EE1u64], + [0xC8FA8DB6CCDD0437u64, 0xE8111C87C5C1BA99u64], + [0x1D9C9892400A22A2u64, 0x910AB1D4DB9914A0u64], + [0x2503BEB6D00CAB4Bu64, 0xB54D5E4A127F59C8u64], + [0x2E44AE64840FD61Du64, 0xE2A0B5DC971F303Au64], + [0x5CEAECFED289E5D2u64, 0x8DA471A9DE737E24u64], + [0x7425A83E872C5F47u64, 0xB10D8E1456105DADu64], + [0xD12F124E28F77719u64, 0xDD50F1996B947518u64], + [0x82BD6B70D99AAA6Fu64, 0x8A5296FFE33CC92Fu64], + [0x636CC64D1001550Bu64, 0xACE73CBFDC0BFB7Bu64], + [0x3C47F7E05401AA4Eu64, 0xD8210BEFD30EFA5Au64], + [0x65ACFAEC34810A71u64, 0x8714A775E3E95C78u64], + [0x7F1839A741A14D0Du64, 0xA8D9D1535CE3B396u64], + [0x1EDE48111209A050u64, 0xD31045A8341CA07Cu64], + [0x934AED0AAB460432u64, 0x83EA2B892091E44Du64], + [0xF81DA84D5617853Fu64, 0xA4E4B66B68B65D60u64], + [0x36251260AB9D668Eu64, 0xCE1DE40642E3F4B9u64], + [0xC1D72B7C6B426019u64, 0x80D2AE83E9CE78F3u64], + [0xB24CF65B8612F81Fu64, 0xA1075A24E4421730u64], + [0xDEE033F26797B627u64, 0xC94930AE1D529CFCu64], + [0x169840EF017DA3B1u64, 0xFB9B7CD9A4A7443Cu64], + [0x8E1F289560EE864Eu64, 0x9D412E0806E88AA5u64], + [0xF1A6F2BAB92A27E2u64, 0xC491798A08A2AD4Eu64], + [0xAE10AF696774B1DBu64, 0xF5B5D7EC8ACB58A2u64], + [0xACCA6DA1E0A8EF29u64, 0x9991A6F3D6BF1765u64], + [0x17FD090A58D32AF3u64, 0xBFF610B0CC6EDD3Fu64], + [0xDDFC4B4CEF07F5B0u64, 0xEFF394DCFF8A948Eu64], + [0x4ABDAF101564F98Eu64, 0x95F83D0A1FB69CD9u64], + [0x9D6D1AD41ABE37F1u64, 0xBB764C4CA7A4440Fu64], + [0x84C86189216DC5EDu64, 0xEA53DF5FD18D5513u64], + [0x32FD3CF5B4E49BB4u64, 0x92746B9BE2F8552Cu64], + [0x3FBC8C33221DC2A1u64, 0xB7118682DBB66A77u64], + [0x0FABAF3FEAA5334Au64, 0xE4D5E82392A40515u64], + [0x29CB4D87F2A7400Eu64, 0x8F05B1163BA6832Du64], + [0x743E20E9EF511012u64, 0xB2C71D5BCA9023F8u64], + [0x914DA9246B255416u64, 0xDF78E4B2BD342CF6u64], + [0x1AD089B6C2F7548Eu64, 0x8BAB8EEFB6409C1Au64], + [0xA184AC2473B529B1u64, 0xAE9672ABA3D0C320u64], + [0xC9E5D72D90A2741Eu64, 0xDA3C0F568CC4F3E8u64], + [0x7E2FA67C7A658892u64, 0x8865899617FB1871u64], + [0xDDBB901B98FEEAB7u64, 0xAA7EEBFB9DF9DE8Du64], + [0x552A74227F3EA565u64, 0xD51EA6FA85785631u64], + [0xD53A88958F87275Fu64, 0x8533285C936B35DEu64], + [0x8A892ABAF368F137u64, 0xA67FF273B8460356u64], + [0x2D2B7569B0432D85u64, 0xD01FEF10A657842Cu64], + [0x9C3B29620E29FC73u64, 0x8213F56A67F6B29Bu64], + [0x8349F3BA91B47B8Fu64, 0xA298F2C501F45F42u64], + [0x241C70A936219A73u64, 0xCB3F2F7642717713u64], + [0xED238CD383AA0110u64, 0xFE0EFB53D30DD4D7u64], + [0xF4363804324A40AAu64, 0x9EC95D1463E8A506u64], + [0xB143C6053EDCD0D5u64, 0xC67BB4597CE2CE48u64], + [0xDD94B7868E94050Au64, 0xF81AA16FDC1B81DAu64], + [0xCA7CF2B4191C8326u64, 0x9B10A4E5E9913128u64], + [0xFD1C2F611F63A3F0u64, 0xC1D4CE1F63F57D72u64], + [0xBC633B39673C8CECu64, 0xF24A01A73CF2DCCFu64], + [0xD5BE0503E085D813u64, 0x976E41088617CA01u64], + [0x4B2D8644D8A74E18u64, 0xBD49D14AA79DBC82u64], + [0xDDF8E7D60ED1219Eu64, 0xEC9C459D51852BA2u64], + [0xCABB90E5C942B503u64, 0x93E1AB8252F33B45u64], + [0x3D6A751F3B936243u64, 0xB8DA1662E7B00A17u64], + [0x0CC512670A783AD4u64, 0xE7109BFBA19C0C9Du64], + [0x27FB2B80668B24C5u64, 0x906A617D450187E2u64], + [0xB1F9F660802DEDF6u64, 0xB484F9DC9641E9DAu64], + [0x5E7873F8A0396973u64, 0xE1A63853BBD26451u64], + [0xDB0B487B6423E1E8u64, 0x8D07E33455637EB2u64], + [0x91CE1A9A3D2CDA62u64, 0xB049DC016ABC5E5Fu64], + [0x7641A140CC7810FBu64, 0xDC5C5301C56B75F7u64], + [0xA9E904C87FCB0A9Du64, 0x89B9B3E11B6329BAu64], + [0x546345FA9FBDCD44u64, 0xAC2820D9623BF429u64], + [0xA97C177947AD4095u64, 0xD732290FBACAF133u64], + [0x49ED8EABCCCC485Du64, 0x867F59A9D4BED6C0u64], + [0x5C68F256BFFF5A74u64, 0xA81F301449EE8C70u64], + [0x73832EEC6FFF3111u64, 0xD226FC195C6A2F8Cu64], +]; diff --git a/lib/strconv/test/stoftest.ww b/lib/strconv/test/stoftest.ww new file mode 100644 index 00000000..93e47670 --- /dev/null +++ b/lib/strconv/test/stoftest.ww @@ -0,0 +1,227 @@ +// stoftest — exercises lib/strconv/stof.ww (Hare stof.ha port). +// Run with `out/bin/ww run lib/strconv/test/stoftest.ww`. Same +// signalled-then-os.exit(signalled+10) pattern as decimaltest: a +// non-zero exit names the failing @test (1..N → exit 11..N+10). +// +// Ports ref/hare/strconv/stof.ha's @test vectors (stof64 / stof32 / +// stofhex). Comparisons are BIT-level (math.f64bits / f32bits) so a +// sign flip (-0.0 vs 0.0) or a 1-ulp miss fails the row rather than +// silently passing on IEEE `==`. NaN via math.isnan; ±Inf via +// f64frombits(INF_BITS) (ww math has no NAN/INF f32/f64 const — see +// math/floats.ww). Hex extremes (Hare's math::F64_MAX_NORMAL etc., +// absent in ww math) assert against the IEEE-754 bit patterns directly. +// +// Lives in lib/strconv/test/ so `import strconv` resolves to the +// DIRECTORY (full package: strconv.ww + decimal.ww + stof_data.ww + +// stof.ww), not the strconv.ww FILE — same rationale as decimaltest. + +package strconv; + +import strconv; +import os; +import math; + +let signalled: i32 = 0; +fn fail() void = { os.exit(signalled + 10); }; + +// ---- unwrap helpers (bit-exact value checks) ------------------------- + +fn chk64(s: str, b: base, want: f64) bool = { + match (stof64(s, b)) { + case let v: f64 => { return math.f64bits(v) == math.f64bits(want); }; + case let e: invalid => { return false; }; + case let e: overflow => { return false; }; + }; + return false; +}; + +fn chk32(s: str, b: base, want: f32) bool = { + match (stof32(s, b)) { + case let v: f32 => { return math.f32bits(v) == math.f32bits(want); }; + case let e: invalid => { return false; }; + case let e: overflow => { return false; }; + }; + return false; +}; + +fn ovf64(s: str, b: base) bool = { + match (stof64(s, b)) { + case let v: f64 => { return false; }; + case let e: invalid => { return false; }; + case let e: overflow => { return true; }; + }; + return false; +}; + +fn ovf32(s: str, b: base) bool = { + match (stof32(s, b)) { + case let v: f32 => { return false; }; + case let e: invalid => { return false; }; + case let e: overflow => { return true; }; + }; + return false; +}; + +// inv64 — the invalid byte-index, or -1 if not invalid. +fn inv64(s: str) i32 = { + match (stof64(s, base.DEC)) { + case let v: f64 => { return -1; }; + case let e: invalid => { return (e: i32); }; + case let e: overflow => { return -1; }; + }; + return -1; +}; + +fn inv32(s: str) i32 = { + match (stof32(s, base.DEC)) { + case let v: f32 => { return -1; }; + case let e: invalid => { return (e: i32); }; + case let e: overflow => { return -1; }; + }; + return -1; +}; + +fn nan64(s: str) bool = { + match (stof64(s, base.DEC)) { + case let v: f64 => { return math.isnan(v); }; + case let e: invalid => { return false; }; + case let e: overflow => { return false; }; + }; + return false; +}; + +fn nan32(s: str) bool = { + match (stof32(s, base.DEC)) { + case let v: f32 => { return math.isnan((v: f64)); }; + case let e: invalid => { return false; }; + case let e: overflow => { return false; }; + }; + return false; +}; + +// ---- stof64 ---------------------------------------------------------- +// ref/hare/strconv/stof.ha:530. + +@test fn stof64_dec() void = { + let inf: f64 = math.f64frombits(math.INF_BITS); + let ninf: f64 = math.f64frombits(0xFFF0000000000000u64); + let nzero: f64 = math.f64frombits(1u64 << 63u64); // -0.0 (1<<63 dodges the I64_MIN-literal emit bug #144) + let subn: f64 = math.f64frombits(0x0000000000000001u64); // 5e-324 + + if (!chk64("0", base.DEC, 0.0)) { fail(); }; + if (!chk64("200", base.DEC, 200.0)) { fail(); }; + if (!chk64("12345", base.DEC, 12345.0)) { fail(); }; + if (!chk64("+112233445566778899", base.DEC, 1.122334455667789e17)) { fail(); }; + if (!chk64("3.14", base.DEC, 3.14)) { fail(); }; + if (!chk64("2.99792458E+8", base.DEC, 299792458.0)) { fail(); }; + if (!chk64("6.022e23", base.DEC, 6.022e23)) { fail(); }; + if (!ovf64("1e310", base.DEC)) { fail(); }; + if (!chk64("9007199254740991", base.DEC, 9007199254740991.0)) { fail(); }; + if (!chk64("90071992547409915", base.DEC, 90071992547409920.0)) { fail(); }; + if (!chk64("90071992547409925", base.DEC, 90071992547409920.0)) { fail(); }; + if (!chk64("2.2250738585072014e-308", base.DEC, 2.2250738585072014e-308)) { fail(); }; + if (!chk64("-1e-324", base.DEC, nzero)) { fail(); }; + if (!chk64("5e-324", base.DEC, subn)) { fail(); }; + + if (inv64("") != 0) { fail(); }; + if (inv64("0ZO") != 1) { fail(); }; + if (inv64("1.23ezz") != 5) { fail(); }; + + if (!chk64("Infinity", base.DEC, inf)) { fail(); }; + if (!chk64("+Infinity", base.DEC, inf)) { fail(); }; + if (!chk64("-Infinity", base.DEC, ninf)) { fail(); }; + if (!chk64("infinity", base.DEC, inf)) { fail(); }; + if (!chk64("inFinIty", base.DEC, inf)) { fail(); }; + if (!chk64("-infinity", base.DEC, ninf)) { fail(); }; + if (!chk64("-infiNity", base.DEC, ninf)) { fail(); }; + if (!nan64("NaN")) { fail(); }; + if (!nan64("nan")) { fail(); }; + if (!nan64("naN")) { fail(); }; +}; + +// ---- stof32 ---------------------------------------------------------- +// ref/hare/strconv/stof.ha:560. + +@test fn stof32_dec() void = { + let inf: f32 = math.f32frombits(0x7F800000u32); + let ninf: f32 = math.f32frombits(0xFF800000u32); + let nzero: f32 = math.f32frombits(0x80000000u32); // -0.0 + let subn: f32 = math.f32frombits(0x00000001u32); // 1e-45 + + if (!chk32("0", base.DEC, 0.0f32)) { fail(); }; + if (!chk32("1e10", base.DEC, 1.0e10f32)) { fail(); }; + if (!chk32("299792458", base.DEC, 299792458.0f32)) { fail(); }; + if (!chk32("6.022e23", base.DEC, 6.022e23f32)) { fail(); }; + if (!ovf32("1e40", base.DEC)) { fail(); }; + if (!chk32("16777215", base.DEC, 16777215.0f32)) { fail(); }; + if (!chk32("167772155", base.DEC, 167772160.0f32)) { fail(); }; + if (!chk32("167772145", base.DEC, 167772140.0f32)) { fail(); }; + if (!chk32("6.62607015e-34", base.DEC, 6.62607015e-34f32)) { fail(); }; + if (!chk32("1.1754944e-38", base.DEC, 1.1754944e-38f32)) { fail(); }; + if (!chk32("-1e-50", base.DEC, nzero)) { fail(); }; + if (!chk32("1e-45", base.DEC, subn)) { fail(); }; + + if (inv32("") != 0) { fail(); }; + if (inv32("0ZO") != 1) { fail(); }; + if (inv32("1.23e-zz") != 6) { fail(); }; + + if (!chk32("Infinity", base.DEC, inf)) { fail(); }; + if (!chk32("+Infinity", base.DEC, inf)) { fail(); }; + if (!chk32("-Infinity", base.DEC, ninf)) { fail(); }; + if (!chk32("infinity", base.DEC, inf)) { fail(); }; + if (!chk32("inFinIty", base.DEC, inf)) { fail(); }; + if (!chk32("-infinity", base.DEC, ninf)) { fail(); }; + if (!chk32("-infiniTy", base.DEC, ninf)) { fail(); }; + if (!nan32("NaN")) { fail(); }; + if (!nan32("nan")) { fail(); }; + if (!nan32("naN")) { fail(); }; + if (!chk32("9.19100241453305036800e+20", base.DEC, 9.19100241453305036800e+20f32)) { fail(); }; +}; + +// ---- stofhex --------------------------------------------------------- +// ref/hare/strconv/stof.ha:590. Hex-float surface-form literals (0x1.fp-2, +// math::F64_MAX_NORMAL, …) are spelled as IEEE-754 bit patterns since ww +// has no hex-float literal lexer / no F*_MAX_NORMAL math consts. + +@test fn stof64_hex() void = { + if (!chk64("0p0", base.HEX, 0.0)) { fail(); }; + if (!chk64("1p0", base.HEX, 1.0)) { fail(); }; + if (!chk64("-1p0", base.HEX_LOWER, -1.0)) { fail(); }; + // 0x1.fp-2 = 1.9375 * 2^-2 = 0.484375 (exact). + if (!chk64("1.fp-2", base.HEX, 0.484375)) { fail(); }; + // F64_MAX_NORMAL. + if (!chk64("1.fffffffffffffp+1023", base.HEX, + math.f64frombits(0x7FEFFFFFFFFFFFFFu64))) { fail(); }; + // F64_MIN_NORMAL. + if (!chk64("1.0000000000000p-1022", base.HEX, + math.f64frombits(0x0010000000000000u64))) { fail(); }; + // F64_MIN_SUBNORMAL. + if (!chk64("0.0000000000001p-1022", base.HEX, + math.f64frombits(0x0000000000000001u64))) { fail(); }; + if (!ovf64("1p+1024", base.HEX)) { fail(); }; + if (!chk64("0.00000000000001p-1022", base.HEX, 0.0)) { fail(); }; +}; + +@test fn stof32_hex() void = { + if (!chk32("0p0", base.HEX, 0.0f32)) { fail(); }; + if (!chk32("1p0", base.HEX, 1.0f32)) { fail(); }; + if (!chk32("-1p0", base.HEX, -1.0f32)) { fail(); }; + if (!chk32("1.fp-2", base.HEX, 0.484375f32)) { fail(); }; + // F32_MAX_NORMAL. + if (!chk32("1.fffffd586b834p+127", base.HEX, + math.f32frombits(0x7F7FFFFFu32))) { fail(); }; + // F32_MIN_NORMAL. + if (!chk32("1.0p-126", base.HEX, math.f32frombits(0x00800000u32))) { fail(); }; + // F32_MIN_SUBNORMAL. + if (!chk32("1.6p-150", base.HEX, math.f32frombits(0x00000001u32))) { fail(); }; + if (!ovf32("1.0p+128", base.HEX)) { fail(); }; + if (!chk32("1.0p-151", base.HEX, 0.0f32)) { fail(); }; +}; + +export fn main() i32 = { + signalled = 1; stof64_dec(); + signalled = 2; stof32_dec(); + signalled = 3; stof64_hex(); + signalled = 4; stof32_hex(); + return 0; +}; diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 84396180..289ea873 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -3026,6 +3026,1134 @@ fn decimal_round(d: *decimal) u64 = { return n; }; +// ascii — rune-class predicates and case folding for the ASCII range. +// Matches Hare's ascii::isdigit family (rune-taking signature). Runes +// outside 0..127 always answer `false`. The lexer hot path uses these +// inline; they are expected to inline to a couple of compares. + +package ascii; + +export fn isdigit(c: rune) bool = { + if (c < 48) { return false; }; + if (c > 57) { return false; }; + return true; +}; + +export fn isupper(c: rune) bool = { + if (c < 65) { return false; }; + if (c > 90) { return false; }; + return true; +}; + +export fn islower(c: rune) bool = { + if (c < 97) { return false; }; + if (c > 122) { return false; }; + return true; +}; + +export fn isalpha(c: rune) bool = { + if (isupper(c)) { return true; }; + return islower(c); +}; + +export fn isalnum(c: rune) bool = { + if (isalpha(c)) { return true; }; + return isdigit(c); +}; + +// isspace — the C/Hare set: space, tab, NL, VT, FF, CR. +export fn isspace(c: rune) bool = { + if (c == 32) { return true; }; // ' ' + if (c == 9) { return true; }; // '\t' + if (c == 10) { return true; }; // '\n' + if (c == 11) { return true; }; // '\v' + if (c == 12) { return true; }; // '\f' + if (c == 13) { return true; }; // '\r' + return false; +}; + +export fn isxdigit(c: rune) bool = { + if (isdigit(c)) { return true; }; + if (c >= 65) { + if (c <= 70) { return true; }; // 'A'..'F' + }; + if (c >= 97) { + if (c <= 102) { return true; }; // 'a'..'f' + }; + return false; +}; + +// valid — `c` is in the 0..127 ASCII range. +export fn valid(c: rune) bool = { + if (c < 0) { return false; }; + if (c > 127) { return false; }; + return true; +}; + +// validstr — every byte in `s` is ASCII (0..127). +export fn validstr(s: str) bool = { + let i: i32 = 0; + for (i < s.len) { + // High-bit test rather than `> 127u8`; both cgens lower + // the bitwise form identically. The `> u8` form picks + // JA vs JG depending on signed/unsigned dispatch. + if ((s[i] & 128u8) != 0u8) { return false; }; + i += 1; + }; + return true; +}; + +// iscntrl — control chars: 0..31 and 127. +export fn iscntrl(c: rune) bool = { + if (c >= 0) { if (c <= 31) { return true; }; }; + if (c == 127) { return true; }; + return false; +}; + +// isblank — space and tab. +export fn isblank(c: rune) bool = { + if (c == 32) { return true; }; // ' ' + if (c == 9) { return true; }; // '\t' + return false; +}; + +// isprint — printable: space through '~'. +export fn isprint(c: rune) bool = { + if (c < 32) { return false; }; + if (c > 126) { return false; }; + return true; +}; + +// isgraph — printable, non-space. +export fn isgraph(c: rune) bool = { + if (c < 33) { return false; }; + if (c > 126) { return false; }; + return true; +}; + +// ispunct — printable, non-alnum, non-space. +export fn ispunct(c: rune) bool = { + if (!isgraph(c)) { return false; }; + if (isalnum(c)) { return false; }; + return true; +}; + +// tolower / toupper — fold ASCII case. Non-letters pass through. +export fn tolower(c: rune) rune = { + if (isupper(c)) { return c + 32; }; + return c; +}; + +export fn toupper(c: rune) rune = { + if (islower(c)) { return c - 32; }; + return c; +}; + +// strcasecmp — three-way ASCII case-insensitive compare. +export fn strcasecmp(a: str, b: str) i32 = { + let n: i32 = a.len; + if (b.len < n) { n = b.len; }; + let i: i32 = 0; + for (i < n) { + let ca: rune = tolower(a[i]: rune); + let cb: rune = tolower(b[i]: rune); + if (ca != cb) { return (ca - cb): i32; }; + i += 1; + }; + return a.len - b.len; +}; + +// floats — f64 classification, sign, bit-reinterpret core, and the f64 +// decompose half (subnormal-normalize + frexp). Ported from +// ref/hare/math/floats.ha (fold-1: classify/sign/bits; fold-2a: +// issubnormalf64/normalizef64/frexpf64; strconv-foundation fold-1a: +// F32 bit-layout + f32bits/f32frombits + floatinfo struct type; +// fold-1b: NAN_BITS/INF_BITS sentinels; γ-cleanup: f64info/f32info +// instances re-folded once #149 lowered &math.f64info). frexpf64's +// zero guard `n == 0f64` rides the #103 +// fix (no-decimal f64 literal now materialized into XMM) and its +// (f64, i64) tuple return rides the #105 fix (tuple f64-word read). +// The ldexp/modfrac/nextafter family stays deferred (need f64 DIVIDE). + +package math; + +// Returns the binary representation of the given f64. +// ref/hare/math/floats.ha:5. Parens around &n are load-bearing: ww's `:` +// cast binds tighter than unary `&`, so Hare's `*(&n: *u64)` would parse +// as `*(&(n: *u64))`; `(&n): *u64` reinterprets the address as intended. +export fn f64bits(n: f64) u64 = { + return *((&n): *u64); +}; + +// Returns the binary representation of the given f32. +// ref/hare/math/floats.ha:8 +export fn f32bits(n: f32) u32 = { + return *((&n): *u32); +}; + +// Returns f64 with the given binary representation. +// ref/hare/math/floats.ha:11 +export fn f64frombits(n: u64) f64 = { + return *((&n): *f64); +}; + +// Returns f32 with the given binary representation. +// ref/hare/math/floats.ha:14 +export fn f32frombits(n: u32) f32 = { + return *((&n): *f32); +}; + +// ref/hare/math/floats.ha:17,20,23 declare these as untyped int. ww has +// no untyped def (every def carries a type) and routes shift/bitwise +// through unify_arith, which rejects mixed operand types (cmd/wcc/ +// check.c:769). The bit-structure consts are used only as u64 shift +// amounts and mask widths, so they are typed u64 here — the closest +// stand-in for Hare's untyped-int adapt at those use sites. + +// The number of bits in the significand of the binary representation of f64. +export def F64_MANTISSA_BITS: u64 = 52; + +// The number of bits in the exponent of the binary representation of f64. +export def F64_EXPONENT_BITS: u64 = 11; + +// The bias of the exponent of the binary representation of f64. Subtract this +// from the exponent in the binary representation to get the actual exponent. +export def F64_EXPONENT_BIAS: u64 = 1023; + +// Mask with each bit of an f64's mantissa set. +// ref/hare/math/floats.ha:37 +export def F64_MANTISSA_MASK: u64 = (1 << F64_MANTISSA_BITS) - 1; + +// Mask with each bit of an f64's exponent set. +// ref/hare/math/floats.ha:40 +export def F64_EXPONENT_MASK: u64 = (1 << F64_EXPONENT_BITS) - 1; + +// The mask that gets an f64's sign. +// ref/hare/math/floats.ha:75 +def F64_SIGN_MASK: u64 = 1u64 << 63; + +// Mask that clears an f64's exponent field, keeping sign + mantissa. +// ref/hare/math/floats.ha:77. Hare hardcodes the 0x800FFFFFFFFFFFFF binary +// literal because its lexer can't const-fold the expression; ww's #88 +// def-const-fold can, so the readable form is kept. floats.ha:79's NOTE +// expression has an `0u64 &` upstream typo (it would yield 0); the value it +// documents is exactly ~(F64_EXPONENT_MASK << F64_MANTISSA_BITS). +def F64_EXP_REMOVAL_MASK: u64 = ~(F64_EXPONENT_MASK << F64_MANTISSA_BITS); + +// The f64 bit pattern whose exponent field evaluates to zero (0.5 scale). +// ref/hare/math/floats.ha:84 +def F64_EXP_ZERO: u64 = (F64_EXPONENT_BIAS - 1) << F64_MANTISSA_BITS; + +// F32 bit-structure constants. ref/hare/math/floats.ha:27,30,33 declare +// these as untyped int; ww has no untyped def, so they ride u32 (matching +// the u32 bit container, the same way the F64 family rides u64 — see +// the note above F64_MANTISSA_BITS). + +// The number of bits in the significand of the binary representation of f32. +// ref/hare/math/floats.ha:27 +export def F32_MANTISSA_BITS: u32 = 23u32; + +// The number of bits in the exponent of the binary representation of f32. +// ref/hare/math/floats.ha:30 +export def F32_EXPONENT_BITS: u32 = 8u32; + +// The bias of the exponent of the binary representation of f32. Subtract this +// from the exponent in the binary representation to get the actual exponent. +// ref/hare/math/floats.ha:33 +export def F32_EXPONENT_BIAS: u32 = 127u32; + +// Mask with each bit of an f32's mantissa set. +// ref/hare/math/floats.ha:43 +export def F32_MANTISSA_MASK: u32 = (1u32 << F32_MANTISSA_BITS) - 1u32; + +// Mask with each bit of an f32's exponent set. +// ref/hare/math/floats.ha:46 +export def F32_EXPONENT_MASK: u32 = (1u32 << F32_EXPONENT_BITS) - 1u32; + +// The mask that gets an f32's sign. +// ref/hare/math/floats.ha:87 +def F32_SIGN_MASK: u32 = 1u32 << 31; + +// Mask that clears an f32's exponent field, keeping sign + mantissa. +// ref/hare/math/floats.ha:92. Hare hardcodes the binary literal (its +// lexer can't const-fold the expression); ww's #88 def-const-fold can, +// so the readable form is kept (same call as F64_EXP_REMOVAL_MASK). +def F32_EXP_REMOVAL_MASK: u32 = ~(F32_EXPONENT_MASK << F32_MANTISSA_BITS); + +// The f32 bit pattern whose exponent field evaluates to zero (0.5 scale). +// ref/hare/math/floats.ha:95 +def F32_EXP_ZERO: u32 = (F32_EXPONENT_BIAS - 1u32) << F32_MANTISSA_BITS; + +// floatinfo — IEEE-754 shape parameters for a binary float type, passed +// to width-generic helpers in strconv (eisel_lemire, floatbits, hex_to_bits, +// mkfloat). ref/hare/math/floats.ha:101. Hare's `int` maps to ww's `int` +// (machine word, 8B; project_int_machine_word_derived_limits), so the +// expbias field stays `int` — that keeps the fold-4 stof port byte-for-byte +// against ref/hare/strconv/stof.ha:248,288 (`let e: int = 0` arithmetic +// against `f.expbias` of the same type, no cast at use site). +export type floatinfo = struct { + // Bits in significand. + mantbits: u64, + // Bits in exponent. + expbits: u64, + // Bias of exponent. + expbias: int, + // Mask for mantissa. + mantmask: u64, + // Mask for exponent. + expmask: u64, +}; + +// floatinfo instances for the f64 / f32 types, consumed by the +// width-generic strconv helpers via &math.f64info (cross-module +// address-of, lowered since #149). ref/hare/math/floats.ha:117,126. +// Hare spells the masks (1 << 52) - 1 / (1 << 23) - 1; the #129 A.2 +// struct-composite static-init path folds only bare-literal field +// initializers, not const-fold expressions, so the value-identical hex +// literals are used here (0xFFFFFFFFFFFFF == (1<<52)-1, 0x7FFFFF == +// (1<<23)-1 — same hex-literal style as the NAN_BITS/INF_BITS sentinels +// below). expbias rides `int` (the field type) with no suffix. +export def f64info: floatinfo = floatinfo { + mantbits = 52u64, + expbits = 11u64, + expbias = 1023, + mantmask = 0xFFFFFFFFFFFFFu64, + expmask = 0x7FFu64, +}; + +export def f32info: floatinfo = floatinfo { + mantbits = 23u64, + expbits = 8u64, + expbias = 127, + mantmask = 0x7FFFFFu64, + expmask = 0xFFu64, +}; + +// IEEE-754 quiet-NaN and positive-Infinity f64 bit sentinels. +// ref/hare/math/floats.ha:137,141. Hare exports `def NAN = 0.0/0.0;` and +// `def INF = 1.0/0.0;` (untyped float def-fold); ww's cgen doesn't lower +// `def: f64 = expr;` (the symbol comes out undefined at link time — see +// #129). Callers materialize the f64 sentinel via f64frombits(NAN_BITS) +// / f64frombits(INF_BITS); same bit-exact value, one extra reinterpret. +// 0x7FF8000000000000 is the IEEE-754 binary64 quiet-NaN (sign=0, exp= +// all-ones, mantissa MSB=1, rest=0); 0x7FF0000000000000 is +Infinity +// (sign=0, exp=all-ones, mantissa=0). Re-fold to `def NAN: f64 = ...` +// when #129 closes (γ-cleanup pattern per amalloc-drop precedent). +export def NAN_BITS: u64 = 0x7FF8000000000000u64; +export def INF_BITS: u64 = 0x7FF0000000000000u64; + +// Returns true if the given floating-point number is NaN. +// ref/hare/math/floats.ha:144 (Hare's expression body inlined into a +// block: ww has no expression-bodied fn form, only brace blocks). +export fn isnan(n: f64) bool = { + return n != n; +}; + +// Returns true if the given floating-point number is infinite. +// ref/hare/math/floats.ha:147 +export fn isinf(n: f64) bool = { + const bits = f64bits(n); + const mant = bits & F64_MANTISSA_MASK; + const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK; + return exp == F64_EXPONENT_MASK && mant == 0; +}; + +// Returns true if the given f64 is subnormal. +// ref/hare/math/floats.ha:179 +export fn issubnormalf64(n: f64) bool = { + const bits = f64bits(n); + const mant = bits & F64_MANTISSA_MASK; + const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK; + return exp == 0 && mant != 0; +}; + +// Returns the absolute value of f64 n. +// ref/hare/math/floats.ha:195 +export fn absf64(n: f64) f64 = { + if (isnan(n)) { + return n; + }; + return f64frombits(f64bits(n) & ~F64_SIGN_MASK); +}; + +// Returns 1 if x is positive and -1 if x is negative. Note that zero is also +// signed. +// ref/hare/math/floats.ha:212 +export fn signf64(x: f64) i64 = { + if (f64bits(x) & F64_SIGN_MASK == 0) { + return 1i64; + } else { + return -1i64; + }; +}; + +// Returns whether or not x is positive. +// ref/hare/math/floats.ha:231 +export fn ispositivef64(x: f64) bool = { + return signf64(x) == 1i64; +}; + +// Returns whether or not x is negative. +// ref/hare/math/floats.ha:237 +export fn isnegativef64(x: f64) bool = { + return signf64(x) == -1i64; +}; + +// Returns x, but with the sign of y. +// ref/hare/math/floats.ha:243 +export fn copysignf64(x: f64, y: f64) f64 = { + return f64frombits((f64bits(x) & ~F64_SIGN_MASK) | + (f64bits(y) & F64_SIGN_MASK)); +}; + +// Takes a potentially subnormal f64 n and returns a normal f64 normal_float +// and an exponent exp such that n == normal_float * 2^{exp}. +// ref/hare/math/floats.ha:256 +export fn normalizef64(n: f64) (f64, i64) = { + if (issubnormalf64(n)) { + const factor = 1i64 << (F64_MANTISSA_BITS: i64); + const normal_float = (n * (factor: f64)); + return (normal_float, -(F64_MANTISSA_BITS: i64)); + }; + return (n, 0); +}; + +// Breaks a f64 down into its mantissa and exponent. The mantissa will be +// between 0.5 and 1. +// ref/hare/math/floats.ha:278 +export fn frexpf64(n: f64) (f64, i64) = { + if (isnan(n) || isinf(n) || n == 0f64) { + return (n, 0); + }; + const normalized = normalizef64(n); + const normal_float = normalized.0; + const normalization_exp = normalized.1; + const bits = f64bits(normal_float); + const raw_exp: u64 = (bits >> F64_MANTISSA_BITS) & F64_EXPONENT_MASK; + const exp: i64 = normalization_exp + + (raw_exp: i64) - (F64_EXPONENT_BIAS: i64) + 1; + const mantissa: f64 = + f64frombits((bits & F64_EXP_REMOVAL_MASK) | F64_EXP_ZERO); + return (mantissa, exp); +}; + +// math — numeric helpers. Subset of Hare's math::; only the absolute- +// value pair for the signed integer types we currently care about. The +// return type is unsigned so that abs(I32_MIN) doesn't overflow. + +package math; + +export fn absi32(n: i32) u32 = { + if (n < 0) { return (-n): u32; }; + return n: u32; +}; + +export fn absi64(n: i64) u64 = { + if (n < 0) { return (-n): u64; }; + return n: u64; +}; + +// strconv — string-to-float. Mirrors ref/hare/strconv/stof.ha +// (Hare in turn adapts Go): Eisel-Lemire fast path [1] with the +// Simple-Decimal-Conversion slow path [2] (decimal.ww) as fallback. +// [1]: https://nigeltao.github.io/blog/2020/eisel-lemire.html +// [2]: https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html +// +// The Eisel-Lemire fast path (`eisel_lemire` + the `powers_of_ten` +// table in stof_data.ww + the three call sites: floatbits's d.nd<=19 +// block, stof64/stof32's !truncated block) is a pure speed +// optimisation — it returns the same correctly-rounded value the +// decimal slow path (decimal_parse → floatbits) computes, or void to +// defer. Its prereqs landed: the 2D `[596][2]u64` static-init + +// double-index read (#156) and the tagged float-variant return-pack +// (#157, which the public `(f64|invalid|overflow)` return needs). +// +// Spelling divergences from Hare (mechanical, ww parser/cgen shape): +// - str scan index rides `i32` (ww `str.len: i32` + `invalid = !i32` +// payload), not Hare's `size`/`len(s)`. lib CLAUDE.md str-index note. +// - char literals kept faithful (`buf[i] == '.'`, `c - '0'`); probed +// byte-id + value-correct both stages. +// - Hare `?` error-propagation → nested statement-`match` with all- +// return arms + a `case void => void` continuation. ww's `?` +// lowering and a bound `match`-expression with mixed yield/return +// arms both diverge cs≠ww (the latter wwstage-checker-rejected); +// strconv.ww's stoi32 set the explicit-match precedent. +// - Hare `for (cond; afterthought)` 2-clause + `continue` → ww +// 2-clause `for (cond)` with the afterthought inlined at body end +// AND before each `continue` (ww has no empty-init 3-clause +// `for (; c; p)`; #138 post-skip is dodged since 2-clause has no +// post). decimal.ww set the inline-afterthought precedent. +// - Hare `if`/`switch`-expression yield → explicit if-statements + +// pre-bound scalar locals (ww has no expression-bodied if). +// - Hare fn-pointer-in-tuple + `switch yield` selecting the digit +// predicate in fast_parse → a `base==HEX` bool + an `isdigitbase` +// helper that branches to ascii.isdigit/isxdigit (no fn-ptr, no +// tuple, no switch). +// - struct-param field MUTATION (hex_to_bits mutates its by-value +// `p`) → copy p's fields to scalar locals at entry; ww miscompiles +// + diverges on writing a by-value struct param's fields (filed). +// - default arg dropped: Hare `b: base = base::DEC` → callers pass +// base explicitly (no lib fn ships a default arg; strconv.ww +// stoi64 precedent). The base param is normalised through a local +// `bb` (param reassignment avoided). +// - `math::NAN`/`math::INF` (f32) absent in ww math → materialised +// via f32frombits of the IEEE-754 f32 bit patterns (same honest +// construction as math/floats.ww's NAN_BITS/INF_BITS). +// - narrowing int→i32 assignments carry explicit casts (ww `int` is +// an 8B machine word; project_int_machine_word_derived_limits). +// - `r128`/`u128mul` live here (fold-4 is first consumer); fold-5 +// ftos (Ryū) shares them in-package. + +package strconv; + +import ascii; +import math; +import os; +import strings; + +// ref/hare/strconv/ftos_ryu.ha:12. 64×64→128 result halves. +type r128 = struct { + hi: u64, + lo: u64, +}; + +// ref/hare/strconv/ftos_ryu.ha:18. 64×64→128 via 32-bit decomposition +// (Hare's own "TODO: use 128-bit integers when implemented" — ww has +// no u128; the decomposition is the portable shape both stages agree +// on). Comma let-bindings split per decimal.ww divergence. +fn u128mul(a: u64, b: u64) r128 = { + let a0: u64 = (a: u32): u64; + let a1: u64 = a >> 32u64; + let b0: u64 = (b: u32): u64; + let b1: u64 = b >> 32u64; + let p00: u64 = a0 * b0; + let p01: u64 = a0 * b1; + let p10: u64 = a1 * b0; + let p11: u64 = a1 * b1; + let p00_lo: u64 = (p00: u32): u64; + let p00_hi: u64 = p00 >> 32u64; + let mid1: u64 = p10 + p00_hi; + let mid1_lo: u64 = (mid1: u32): u64; + let mid1_hi: u64 = mid1 >> 32u64; + let mid2: u64 = p01 + mid1_lo; + let mid2_lo: u64 = (mid2: u32): u64; + let mid2_hi: u64 = mid2 >> 32u64; + let r_hi: u64 = p11 + mid1_hi + mid2_hi; + let r_lo: u64 = (mid2_lo << 32u64) | p00_lo; + return r128 { hi = r_hi, lo = r_lo }; +}; + +// ref/hare/strconv/stof.ha:14. +fn todig(c: u8) u8 = { + if ('0' <= c && c <= '9') { return c - '0'; }; + if ('a' <= c && c <= 'f') { return c - 'a' + 10u8; }; + if ('A' <= c && c <= 'F') { return c - 'A' + 10u8; }; + abort("strconv.todig: unreachable"); + return 0u8; // unreachable; rt_abort is void-typed (path-cov) +}; + +@symbol("rt_abort") fn abort(msg: str) void; + +// ref/hare/strconv/stof.ha:25. +type fast_parsed_float = struct { + mantissa: u64, + exponent: i32, + negative: bool, + truncated: bool, +}; + +// Digit-class predicate selector for fast_parse — replaces Hare's +// fn-pointer-in-tuple (`&ascii::isdigit` / `&ascii::isxdigit`). +fn isdigitbase(c: rune, ishex: bool) bool = { + if (ishex) { return ascii.isxdigit(c); }; + return ascii.isdigit(c); +}; + +// ref/hare/strconv/stof.ha:32. +fn fast_parse(s: str, b: base) (fast_parsed_float | invalid) = { + let buf: []u8 = strings.toutf8(s); + let i: i32 = 0; + let neg: bool = false; + let trunc: bool = false; + if (buf[i] == '-') { + neg = true; + i += 1; + } else if (buf[i] == '+') { + i += 1; + }; + + let ishex: bool = (b == base.HEX); + let expchr: rune = 'e'; + let max_ndmant: int = 19; + if (ishex) { + expchr = 'p'; + max_ndmant = 16; + }; + let bnum: u64 = (b: i32): u64; + + let sawdot: bool = false; + let sawdigits: bool = false; + let nd: int = 0; + let ndmant: int = 0; + let dp: int = 0; + let mant: u64 = 0u64; + let exp: i32 = 0i32; + for (i < s.len) { + if (buf[i] == '.') { + if (sawdot) { return i: invalid; }; + sawdot = true; + dp = nd; + } else if (isdigitbase(buf[i]: rune, ishex)) { + sawdigits = true; + if (buf[i] == '0' && nd == 0) { + dp -= 1; + i += 1; + continue; + }; + nd += 1; + if (ndmant < max_ndmant) { + mant = mant * bnum + (todig(buf[i]): u64); + ndmant += 1; + } else if (buf[i] != '0') { + trunc = true; + }; + } else { + break; + }; + i += 1; + }; + if (!sawdigits) { return i: invalid; }; + if (!sawdot) { + dp = nd; + }; + if (b == base.HEX) { + dp *= 4; + ndmant *= 4; + }; + if (i < s.len && ascii.tolower(buf[i]: rune) == expchr) { + i += 1; + if (i >= s.len) { return i: invalid; }; + let expsign: int = 1; + if (buf[i] == '+') { + i += 1; + } else if (buf[i] == '-') { + expsign = -1; + i += 1; + }; + if (i >= s.len || !ascii.isdigit(buf[i]: rune)) { + return i: invalid; + }; + let e: int = 0; + for (i < s.len && ascii.isdigit(buf[i]: rune)) { + if (e < 10000) { + e = e * 10 + ((buf[i] - '0'): int); + }; + i += 1; + }; + dp += e * expsign; + } else if (b == base.HEX) { + return i: invalid; // hex floats must have an exponent + }; + if (i != s.len) { return i: invalid; }; + if (mant != 0u64) { + exp = (dp - ndmant): i32; + }; + return fast_parsed_float { + mantissa = mant, + exponent = exp, + negative = neg, + truncated = trunc, + }; +}; + +// ref/hare/strconv/stof.ha:115. Fills the slow-path decimal `d`. +fn decimal_parse(d: *decimal, s: str) (void | invalid) = { + let i: i32 = 0; + let buf: []u8 = strings.toutf8(s); + d.negative = false; + d.truncated = false; + if (buf[0] == '+') { + i += 1; + } else if (buf[0] == '-') { + d.negative = true; + i += 1; + }; + let sawdot: bool = false; + let sawdigits: bool = false; + for (i < s.len) { + if (buf[i] == '.') { + if (sawdot) { return i: invalid; }; + sawdot = true; + d.dp = (d.nd: i32); + } else if (ascii.isdigit(buf[i]: rune)) { + sawdigits = true; + if (buf[i] == '0' && d.nd == (0u64: size)) { + d.dp -= 1; + i += 1; + continue; + }; + if (d.nd < (len(d.digits): size)) { + d.digits[d.nd] = buf[i] - '0'; + d.nd += (1u64: size); + } else if (buf[i] != '0') { + d.truncated = true; + }; + } else { + break; + }; + i += 1; + }; + if (!sawdigits) { return i: invalid; }; + if (!sawdot) { + d.dp = (d.nd: i32); + }; + if (i < s.len && (buf[i] == 'e' || buf[i] == 'E')) { + i += 1; + if (i >= s.len) { return i: invalid; }; + let expsign: int = 1; + if (buf[i] == '+') { + i += 1; + } else if (buf[i] == '-') { + expsign = -1; + i += 1; + }; + if (i >= s.len || !ascii.isdigit(buf[i]: rune)) { + return i: invalid; + }; + let e: int = 0; + for (i < s.len && ascii.isdigit(buf[i]: rune)) { + if (e < 10000) { + e = e * 10 + ((buf[i] - '0'): int); + }; + i += 1; + }; + d.dp += (e * expsign): i32; + }; + if (i != s.len) { return i: invalid; }; + return; +}; + +// ref/hare/strconv/stof.ha:173. Count of leading zero bits in n>0. +fn leading_zeroes(n: u64) uint = { + os.assert(n > 0u64, "strconv.leading_zeroes: n == 0"); + let b: u64 = 0u64; + if ((n & 0xFFFFFFFF00000000u64) > 0u64) { + n >>= 32u64; + b |= 32u64; + }; + if ((n & 0xFFFF0000u64) > 0u64) { + n >>= 16u64; + b |= 16u64; + }; + if ((n & 0xFF00u64) > 0u64) { + n >>= 8u64; + b |= 8u64; + }; + if ((n & 0xF0u64) > 0u64) { + n >>= 4u64; + b |= 4u64; + }; + if ((n & 0xCu64) > 0u64) { + n >>= 2u64; + b |= 2u64; + }; + if ((n & 0x2u64) > 0u64) { + n >>= 1u64; + b |= 1u64; + }; + return ((63u64 - b): uint); +}; + +// ref/hare/strconv/stof.ha:203. Eisel-Lemire fast path: a correctly- +// rounded f64/f32 from (mantissa, exp10) when the 128-bit product is +// unambiguous, else void → caller falls to the decimal slow path. +// Divergences at-site: `mantissa <<= clz` (scalar-param mutate) → local +// `mnt`; whole-struct local reassign `x = merged` copies only the first +// word in cgen → per-field `x.hi = …; x.lo = …` (#155); `po10 = +// powers_of_ten[i]` row-bind → direct double-index (#155, A2); bitwise- +// vs-compare fully parenthesised; comma let-bindings split. +fn eisel_lemire( + mantissa: u64, + exp10: i32, + neg: bool, + f: *math.floatinfo, +) (u64 | void) = { + if (mantissa == 0u64 || exp10 > 288 || exp10 < -307) { + return; + }; + let idx: i32 = exp10 + 307; + let clz: uint = leading_zeroes(mantissa); + let mnt: u64 = mantissa << (clz: u64); + let shift: u64 = 64u64 - f.mantbits - 3u64; + let mask: u64 = (1u64 << shift) - 1u64; + // log(10)/log(2) ≈ 217706 / 65536; x / 65536 = x >> 16. + let exp: int = (217706 * (exp10: int)) >> 16; + let e2: u64 = ((exp + f.expbias + 64): u64) - (clz: u64); + let x: r128 = u128mul(mnt, powers_of_ten[idx][1]); + if ((x.hi & mask) == mask && (x.lo + mnt) < mnt) { + let y: r128 = u128mul(mnt, powers_of_ten[idx][0]); + let merged: r128 = r128 { hi = x.hi, lo = x.lo + y.hi }; + if (merged.lo < x.lo) { + // local-struct-field compound-assign drops the load in + // wwstage (sets =1, not +=1) — explicit form, byte-id. + merged.hi = merged.hi + 1u64; + }; + if ((merged.hi & mask) == mask && (merged.lo + 1u64) == 0u64 && + (y.lo + mnt) < mnt) { + return; + }; + x.hi = merged.hi; + x.lo = merged.lo; + }; + let msb: u64 = x.hi >> 63u64; + let mant: u64 = x.hi >> (msb + shift); + e2 -= 1u64 ^ msb; + if (x.lo == 0u64 && (x.hi & mask) == 0u64 && (mant & 3u64) == 1u64) { + return; + }; + mant += mant & 1u64; + mant >>= 1u64; + if ((mant >> (f.mantbits + 1u64)) > 0u64) { + mant >>= 1u64; + e2 += 1u64; + }; + if (e2 <= 0u64 || e2 >= (1u64 << f.expbits) - 1u64) { + return; + }; + return mkfloat(mant, (e2: uint), neg, f); +}; + +// ref/hare/strconv/stof.ha:247. Slow-path: decimal `d` → IEEE bits. +fn floatbits(d: *decimal, f: *math.floatinfo) (u64 | overflow) = { + let e: int = 0; + let m: u64 = 0u64; + let powtab: [19]i8 = [ + 0i8, 3i8, 6i8, 9i8, 13i8, 16i8, 19i8, 23i8, 26i8, 29i8, + 33i8, 36i8, 39i8, 43i8, 46i8, 49i8, 53i8, 56i8, 59i8, + ]; + if (d.nd == (0u64: size) || d.dp < -326) { + if (d.negative) { + return mkfloat(0u64, (0u32: uint), d.negative, f); + }; + return 0u64; + } else if (d.dp > 310) { + return overflow{}; + }; + if (d.nd <= (19u64: size)) { + let dmant: u64 = 0u64; + let i: size = (0u64: size); + for (i < d.nd) { + dmant = 10u64 * dmant + (d.digits[i]: u64); + i += (1u64: size); + }; + let exp10: i32 = d.dp - (d.nd: i32); + match (eisel_lemire(dmant, exp10, d.negative, f)) { + case let r: u64 => { return r; }; + case void => void; + }; + }; + for (d.dp > 0) { + let n: int = 0; + if ((d.dp: uint) >= (len(powtab): uint)) { + n = (maxshift: int); + } else { + n = (powtab[d.dp]: int); + }; + decimal_shift(d, -n); + e += n; + }; + for (d.dp <= 0) { + let n: int = 0; + if (d.dp == 0) { + if (d.digits[0] >= 5u8) { break; }; + if (d.digits[0] < 2u8) { n = 2; } else { n = 1; }; + } else if ((-d.dp) >= (len(powtab): i32)) { + n = (maxshift: int); + } else { + n = (powtab[-d.dp]: int); + }; + decimal_shift(d, n); + e -= n; + }; + e -= 1; + if (e <= -f.expbias + 1) { + let nn: int = -f.expbias - e + 1; + decimal_shift(d, -nn); + e += nn; + }; + if (e + f.expbias >= ((1u64 << f.expbits): int) - 1) { + return overflow{}; + }; + decimal_shift(d, (f.mantbits: int) + 1); + m = decimal_round(d); + if (m == (2u64 << f.mantbits)) { + m >>= 1u64; + e += 1; + if (e + f.expbias >= ((1u64 << f.expbits): int) - 1) { + return overflow{}; + }; + }; + if ((m & (1u64 << f.mantbits)) == 0u64) { + e = -f.expbias; + }; + return mkfloat(m, ((e + f.expbias): uint), d.negative, f); +}; + +// ref/hare/strconv/stof.ha:311. Assemble sign|exp|mantissa. +fn mkfloat(m: u64, e: uint, negative: bool, f: *math.floatinfo) u64 = { + let n: u64 = m & ((1u64 << f.mantbits) - 1u64); + n |= ((e: u64) & ((1u64 << f.expbits) - 1u64)) << f.mantbits; + if (negative) { + n |= 1u64 << (f.mantbits + f.expbits); + }; + return n; +}; + +// ref/hare/strconv/stof.ha:320. Exact f64 powers of ten 1e0..1e22 (all +// exactly representable; see stof64exact). +let f64pow10: [23]f64 = [ + 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, + 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, + 1.0e19, 1.0e20, 1.0e21, 1.0e22, +]; + +// ref/hare/strconv/stof.ha:326. +fn stof64exact(mant: u64, exp: i32, neg: bool) (f64 | void) = { + if (mant >> math.F64_MANTISSA_BITS != 0u64) { return; }; + let n: f64 = (mant: i64): f64; + if (neg) { + n = -n; + }; + if (exp == 0i32) { + return n; + }; + if (-22i32 <= exp && exp <= 22i32) { + if (exp >= 0i32) { + // f64 compound-assign mis-lowers in cgen — explicit + // form (strconv.ww f64tos precedent). + n = n * f64pow10[exp]; + } else { + n = n / f64pow10[-exp]; + }; + } else { + return; + }; + return n; +}; + +// ref/hare/strconv/stof.ha:345. Exact f32 powers of ten 1e0..1e10. +let f32pow10: [11]f32 = [ + 1.0e0f32, 1.0e1f32, 1.0e2f32, 1.0e3f32, 1.0e4f32, 1.0e5f32, 1.0e6f32, + 1.0e7f32, 1.0e8f32, 1.0e9f32, 1.0e10f32, +]; + +// ref/hare/strconv/stof.ha:349. +fn stof32exact(mant: u64, exp: i32, neg: bool) (f32 | void) = { + if (mant >> (math.F32_MANTISSA_BITS: u64) != 0u64) { return; }; + let n: f32 = (mant: i32): f32; + if (neg) { + n = -n; + }; + if (exp == 0i32) { + return n; + }; + if (-10i32 <= exp && exp <= 10i32) { + if (exp >= 0i32) { + // f32 compound-assign mis-lowers in cgen — explicit form. + n = n * f32pow10[exp]; + } else { + n = n / (f64pow10[-exp]: f32); + }; + } else { + return; + }; + return n; +}; + +// ref/hare/strconv/stof.ha:369. Adapted from Go's atofHex. The by-value +// `p` is mutated in Hare; ww copies its fields to scalar locals (struct +// param field-write miscompiles + diverges — filed). +fn hex_to_bits(p: fast_parsed_float, info: *math.floatinfo) (u64 | overflow) = { + let pmant: u64 = p.mantissa; + let pexp: i32 = p.exponent; + let pneg: bool = p.negative; + let ptrunc: bool = p.truncated; + let max_exp: int = ((1u64 << info.expbits): int) - info.expbias - 2; + let min_exp: int = -info.expbias + 1; + pexp += (info.mantbits: i32); + + // Shift left until a leading 1 bit followed by mantbits + 2 rounding. + for (pmant != 0u64 && pmant >> (info.mantbits + 2u64) == 0u64) { + pmant <<= 1u64; + pexp -= 1; + }; + if (ptrunc) { + pmant |= 1u64; + }; + // Too many bits: shift right (sticky-or the dropped bit). + for (pmant >> (3u64 + info.mantbits) != 0u64) { + pmant = (pmant >> 1u64) | (pmant & 1u64); + pexp += 1; + }; + // Denormalise if the exponent is small. + for (pmant > 1u64 && pexp < (min_exp: i32) - 2) { + pmant = (pmant >> 1u64) | (pmant & 1u64); + pexp += 1; + }; + // Round to even. + let round: u64 = pmant & 3u64; + pmant >>= 2u64; + round |= pmant & 1u64; + pexp += 2; + if (round == 3u64) { + pmant += 1u64; + if (pmant == 1u64 << (1u64 + info.mantbits)) { + pmant >>= 1u64; + pexp += 1; + }; + }; + // Denormal or zero. + if (pmant >> info.mantbits == 0u64) { + pexp = (-info.expbias): i32; + }; + if (pexp > (max_exp: i32)) { + return overflow{}; + }; + let bits: u64 = pmant & info.mantmask; + bits |= (((pexp + (info.expbias: i32)): u64) & info.expmask) << info.mantbits; + if (pneg) { + bits |= 1u64 << (info.mantbits + info.expbits); + }; + return bits; +}; + +// ref/hare/strconv/stof.ha:425. "nan"/"infinity"/±"infinity", +// case-insensitive. ww math has no f32 NAN/INF consts → f32frombits of +// the IEEE-754 f32 bit patterns (qNaN 0x7FC00000, ±Inf 0x7F800000 / +// 0xFF800000). +fn special(s: str) (f32 | void) = { + if (ascii.strcasecmp(s, "nan") == 0) { + return math.f32frombits(0x7FC00000u32); + } else if (ascii.strcasecmp(s, "infinity") == 0) { + return math.f32frombits(0x7F800000u32); + } else if (ascii.strcasecmp(s, "+infinity") == 0) { + return math.f32frombits(0x7F800000u32); + } else if (ascii.strcasecmp(s, "-infinity") == 0) { + return math.f32frombits(0xFF800000u32); + }; + return; +}; + +// ref/hare/strconv/stof.ha:445. Parse `s` as f64 (base DEC or HEX). See +// the module note: the EL fast path is HELD; the decimal fallback gives +// correct results meanwhile. +export fn stof64(s: str, b: base) (f64 | invalid | overflow) = { + let bb: base = b; + if (bb == base.DEFAULT) { + bb = base.DEC; + } else if (bb == base.HEX_LOWER) { + bb = base.HEX; + }; + os.assert(bb == base.DEC || bb == base.HEX, + "strconv.stof64: base must be DEC or HEX"); + + if (s.len == 0) { + return 0: invalid; + }; + + match (special(s)) { + case let f: f32 => { return (f: f64); }; + case void => void; + }; + + match (fast_parse(s, bb)) { + case let p: fast_parsed_float => { + if (bb == base.HEX) { + match (hex_to_bits(p, &math.f64info)) { + case let bits: u64 => { return math.f64frombits(bits); }; + case let eo: overflow => { return eo; }; + }; + } else if (!p.truncated) { + match (stof64exact(p.mantissa, p.exponent, p.negative)) { + case let n: f64 => { return n; }; + case void => void; + }; + match (eisel_lemire(p.mantissa, p.exponent, p.negative, + &math.f64info)) { + case let n: u64 => { return math.f64frombits(n); }; + case void => void; + }; + }; + let d = decimal { ... }; + match (decimal_parse(&d, s)) { + case let ei: invalid => { return ei; }; + case void => void; + }; + match (floatbits(&d, &math.f64info)) { + case let n: u64 => { return math.f64frombits(n); }; + case let eo: overflow => { return eo; }; + }; + }; + case let ei: invalid => { return ei; }; + }; + return 0: invalid; // unreachable (path-cov) +}; + +// ref/hare/strconv/stof.ha:491. Parse `s` as f32 (base DEC or HEX). +export fn stof32(s: str, b: base) (f32 | invalid | overflow) = { + let bb: base = b; + if (bb == base.DEFAULT) { + bb = base.DEC; + } else if (bb == base.HEX_LOWER) { + bb = base.HEX; + }; + os.assert(bb == base.DEC || bb == base.HEX, + "strconv.stof32: base must be DEC or HEX"); + + if (s.len == 0) { + return 0: invalid; + }; + + match (special(s)) { + case let f: f32 => { return f; }; + case void => void; + }; + + match (fast_parse(s, bb)) { + case let p: fast_parsed_float => { + if (bb == base.HEX) { + match (hex_to_bits(p, &math.f32info)) { + case let bits: u64 => { + return math.f32frombits(bits: u32); + }; + case let eo: overflow => { return eo; }; + }; + } else if (!p.truncated) { + match (stof32exact(p.mantissa, p.exponent, p.negative)) { + case let n: f32 => { return n; }; + case void => void; + }; + match (eisel_lemire(p.mantissa, p.exponent, p.negative, + &math.f32info)) { + case let n: u64 => { return math.f32frombits(n: u32); }; + case void => void; + }; + }; + let d = decimal { ... }; + match (decimal_parse(&d, s)) { + case let ei: invalid => { return ei; }; + case void => void; + }; + match (floatbits(&d, &math.f32info)) { + case let n: u64 => { return math.f32frombits(n: u32); }; + case let eo: overflow => { return eo; }; + }; + }; + case let ei: invalid => { return ei; }; + }; + return 0: invalid; // unreachable (path-cov) +}; + // strconv — stof/ftos lookup tables. Mirrors ref/hare/strconv/stof_data.ha // byte-exact. Pure-data fold (strconv #106 fold-2, was fold-3 before drew // re-sequenced 2026-05-26): no logic, exercised transitively when fold-3's @@ -3039,10 +4167,12 @@ fn decimal_round(d: *decimal) u64 = { // both stages agree on (candidate #130). // // `powers_of_ten: [596][2]u64` (ref/hare/strconv/stof_data.ha:73) is the -// Eisel-Lemire fast-path table and is only consumed by stof.ha (a later -// fold); deferred to its consumer-driven port per CLAUDE.md rule 9 -// (Hare-fidelity is API-shaped, and module-level tables follow their -// caller's scope). +// Eisel-Lemire fast-path table (consumed by stof.ww's eisel_lemire); it +// lands here in fold-4 alongside its consumer, indexed `[exp10 + 307]` +// for exp10 in [-307, 288]. Faithful 2D `[596][2]u64` (the {hi,lo} pair +// IS the 128-bit truncated power-of-ten; rule-12, not flattened) — the +// 2D module-level static-init + double-index read it needs landed in +// #156 (cbeffea). See the table at the foot of this file. package strconv; @@ -3122,6 +4252,607 @@ let pow5_table: [0x051C]u8 = [ 9u8, 5u8, 3u8, 3u8, 6u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, ]; +// ref/hare/strconv/stof_data.ha:73. Eisel-Lemire 128-bit power-of-ten +// table (see header note). 596 rows, {hi, lo} u64 pair per row. +let powers_of_ten: [596][2]u64 = [ + [0xA5D3B6D479F8E056u64, 0x8FD0C16206306BABu64], + [0x8F48A4899877186Cu64, 0xB3C4F1BA87BC8696u64], + [0x331ACDABFE94DE87u64, 0xE0B62E2929ABA83Cu64], + [0x9FF0C08B7F1D0B14u64, 0x8C71DCD9BA0B4925u64], + [0x07ECF0AE5EE44DD9u64, 0xAF8E5410288E1B6Fu64], + [0xC9E82CD9F69D6150u64, 0xDB71E91432B1A24Au64], + [0xBE311C083A225CD2u64, 0x892731AC9FAF056Eu64], + [0x6DBD630A48AAF406u64, 0xAB70FE17C79AC6CAu64], + [0x092CBBCCDAD5B108u64, 0xD64D3D9DB981787Du64], + [0x25BBF56008C58EA5u64, 0x85F0468293F0EB4Eu64], + [0xAF2AF2B80AF6F24Eu64, 0xA76C582338ED2621u64], + [0x1AF5AF660DB4AEE1u64, 0xD1476E2C07286FAAu64], + [0x50D98D9FC890ED4Du64, 0x82CCA4DB847945CAu64], + [0xE50FF107BAB528A0u64, 0xA37FCE126597973Cu64], + [0x1E53ED49A96272C8u64, 0xCC5FC196FEFD7D0Cu64], + [0x25E8E89C13BB0F7Au64, 0xFF77B1FCBEBCDC4Fu64], + [0x77B191618C54E9ACu64, 0x9FAACF3DF73609B1u64], + [0xD59DF5B9EF6A2417u64, 0xC795830D75038C1Du64], + [0x4B0573286B44AD1Du64, 0xF97AE3D0D2446F25u64], + [0x4EE367F9430AEC32u64, 0x9BECCE62836AC577u64], + [0x229C41F793CDA73Fu64, 0xC2E801FB244576D5u64], + [0x6B43527578C1110Fu64, 0xF3A20279ED56D48Au64], + [0x830A13896B78AAA9u64, 0x9845418C345644D6u64], + [0x23CC986BC656D553u64, 0xBE5691EF416BD60Cu64], + [0x2CBFBE86B7EC8AA8u64, 0xEDEC366B11C6CB8Fu64], + [0x7BF7D71432F3D6A9u64, 0x94B3A202EB1C3F39u64], + [0xDAF5CCD93FB0CC53u64, 0xB9E08A83A5E34F07u64], + [0xD1B3400F8F9CFF68u64, 0xE858AD248F5C22C9u64], + [0x23100809B9C21FA1u64, 0x91376C36D99995BEu64], + [0xABD40A0C2832A78Au64, 0xB58547448FFFFB2Du64], + [0x16C90C8F323F516Cu64, 0xE2E69915B3FFF9F9u64], + [0xAE3DA7D97F6792E3u64, 0x8DD01FAD907FFC3Bu64], + [0x99CD11CFDF41779Cu64, 0xB1442798F49FFB4Au64], + [0x40405643D711D583u64, 0xDD95317F31C7FA1Du64], + [0x482835EA666B2572u64, 0x8A7D3EEF7F1CFC52u64], + [0xDA3243650005EECFu64, 0xAD1C8EAB5EE43B66u64], + [0x90BED43E40076A82u64, 0xD863B256369D4A40u64], + [0x5A7744A6E804A291u64, 0x873E4F75E2224E68u64], + [0x711515D0A205CB36u64, 0xA90DE3535AAAE202u64], + [0x0D5A5B44CA873E03u64, 0xD3515C2831559A83u64], + [0xE858790AFE9486C2u64, 0x8412D9991ED58091u64], + [0x626E974DBE39A872u64, 0xA5178FFF668AE0B6u64], + [0xFB0A3D212DC8128Fu64, 0xCE5D73FF402D98E3u64], + [0x7CE66634BC9D0B99u64, 0x80FA687F881C7F8Eu64], + [0x1C1FFFC1EBC44E80u64, 0xA139029F6A239F72u64], + [0xA327FFB266B56220u64, 0xC987434744AC874Eu64], + [0x4BF1FF9F0062BAA8u64, 0xFBE9141915D7A922u64], + [0x6F773FC3603DB4A9u64, 0x9D71AC8FADA6C9B5u64], + [0xCB550FB4384D21D3u64, 0xC4CE17B399107C22u64], + [0x7E2A53A146606A48u64, 0xF6019DA07F549B2Bu64], + [0x2EDA7444CBFC426Du64, 0x99C102844F94E0FBu64], + [0xFA911155FEFB5308u64, 0xC0314325637A1939u64], + [0x793555AB7EBA27CAu64, 0xF03D93EEBC589F88u64], + [0x4BC1558B2F3458DEu64, 0x96267C7535B763B5u64], + [0x9EB1AAEDFB016F16u64, 0xBBB01B9283253CA2u64], + [0x465E15A979C1CADCu64, 0xEA9C227723EE8BCBu64], + [0x0BFACD89EC191EC9u64, 0x92A1958A7675175Fu64], + [0xCEF980EC671F667Bu64, 0xB749FAED14125D36u64], + [0x82B7E12780E7401Au64, 0xE51C79A85916F484u64], + [0xD1B2ECB8B0908810u64, 0x8F31CC0937AE58D2u64], + [0x861FA7E6DCB4AA15u64, 0xB2FE3F0B8599EF07u64], + [0x67A791E093E1D49Au64, 0xDFBDCECE67006AC9u64], + [0xE0C8BB2C5C6D24E0u64, 0x8BD6A141006042BDu64], + [0x58FAE9F773886E18u64, 0xAECC49914078536Du64], + [0xAF39A475506A899Eu64, 0xDA7F5BF590966848u64], + [0x6D8406C952429603u64, 0x888F99797A5E012Du64], + [0xC8E5087BA6D33B83u64, 0xAAB37FD7D8F58178u64], + [0xFB1E4A9A90880A64u64, 0xD5605FCDCF32E1D6u64], + [0x5CF2EEA09A55067Fu64, 0x855C3BE0A17FCD26u64], + [0xF42FAA48C0EA481Eu64, 0xA6B34AD8C9DFC06Fu64], + [0xF13B94DAF124DA26u64, 0xD0601D8EFC57B08Bu64], + [0x76C53D08D6B70858u64, 0x823C12795DB6CE57u64], + [0x54768C4B0C64CA6Eu64, 0xA2CB1717B52481EDu64], + [0xA9942F5DCF7DFD09u64, 0xCB7DDCDDA26DA268u64], + [0xD3F93B35435D7C4Cu64, 0xFE5D54150B090B02u64], + [0xC47BC5014A1A6DAFu64, 0x9EFA548D26E5A6E1u64], + [0x359AB6419CA1091Bu64, 0xC6B8E9B0709F109Au64], + [0xC30163D203C94B62u64, 0xF867241C8CC6D4C0u64], + [0x79E0DE63425DCF1Du64, 0x9B407691D7FC44F8u64], + [0x985915FC12F542E4u64, 0xC21094364DFB5636u64], + [0x3E6F5B7B17B2939Du64, 0xF294B943E17A2BC4u64], + [0xA705992CEECF9C42u64, 0x979CF3CA6CEC5B5Au64], + [0x50C6FF782A838353u64, 0xBD8430BD08277231u64], + [0xA4F8BF5635246428u64, 0xECE53CEC4A314EBDu64], + [0x871B7795E136BE99u64, 0x940F4613AE5ED136u64], + [0x28E2557B59846E3Fu64, 0xB913179899F68584u64], + [0x331AEADA2FE589CFu64, 0xE757DD7EC07426E5u64], + [0x3FF0D2C85DEF7621u64, 0x9096EA6F3848984Fu64], + [0x0FED077A756B53A9u64, 0xB4BCA50B065ABE63u64], + [0xD3E8495912C62894u64, 0xE1EBCE4DC7F16DFBu64], + [0x64712DD7ABBBD95Cu64, 0x8D3360F09CF6E4BDu64], + [0xBD8D794D96AACFB3u64, 0xB080392CC4349DECu64], + [0xECF0D7A0FC5583A0u64, 0xDCA04777F541C567u64], + [0xF41686C49DB57244u64, 0x89E42CAAF9491B60u64], + [0x311C2875C522CED5u64, 0xAC5D37D5B79B6239u64], + [0x7D633293366B828Bu64, 0xD77485CB25823AC7u64], + [0xAE5DFF9C02033197u64, 0x86A8D39EF77164BCu64], + [0xD9F57F830283FDFCu64, 0xA8530886B54DBDEBu64], + [0xD072DF63C324FD7Bu64, 0xD267CAA862A12D66u64], + [0x4247CB9E59F71E6Du64, 0x8380DEA93DA4BC60u64], + [0x52D9BE85F074E608u64, 0xA46116538D0DEB78u64], + [0x67902E276C921F8Bu64, 0xCD795BE870516656u64], + [0x00BA1CD8A3DB53B6u64, 0x806BD9714632DFF6u64], + [0x80E8A40ECCD228A4u64, 0xA086CFCD97BF97F3u64], + [0x6122CD128006B2CDu64, 0xC8A883C0FDAF7DF0u64], + [0x796B805720085F81u64, 0xFAD2A4B13D1B5D6Cu64], + [0xCBE3303674053BB0u64, 0x9CC3A6EEC6311A63u64], + [0xBEDBFC4411068A9Cu64, 0xC3F490AA77BD60FCu64], + [0xEE92FB5515482D44u64, 0xF4F1B4D515ACB93Bu64], + [0x751BDD152D4D1C4Au64, 0x991711052D8BF3C5u64], + [0xD262D45A78A0635Du64, 0xBF5CD54678EEF0B6u64], + [0x86FB897116C87C34u64, 0xEF340A98172AACE4u64], + [0xD45D35E6AE3D4DA0u64, 0x9580869F0E7AAC0Eu64], + [0x8974836059CCA109u64, 0xBAE0A846D2195712u64], + [0x2BD1A438703FC94Bu64, 0xE998D258869FACD7u64], + [0x7B6306A34627DDCFu64, 0x91FF83775423CC06u64], + [0x1A3BC84C17B1D542u64, 0xB67F6455292CBF08u64], + [0x20CABA5F1D9E4A93u64, 0xE41F3D6A7377EECAu64], + [0x547EB47B7282EE9Cu64, 0x8E938662882AF53Eu64], + [0xE99E619A4F23AA43u64, 0xB23867FB2A35B28Du64], + [0x6405FA00E2EC94D4u64, 0xDEC681F9F4C31F31u64], + [0xDE83BC408DD3DD04u64, 0x8B3C113C38F9F37Eu64], + [0x9624AB50B148D445u64, 0xAE0B158B4738705Eu64], + [0x3BADD624DD9B0957u64, 0xD98DDAEE19068C76u64], + [0xE54CA5D70A80E5D6u64, 0x87F8A8D4CFA417C9u64], + [0x5E9FCF4CCD211F4Cu64, 0xA9F6D30A038D1DBCu64], + [0x7647C3200069671Fu64, 0xD47487CC8470652Bu64], + [0x29ECD9F40041E073u64, 0x84C8D4DFD2C63F3Bu64], + [0xF468107100525890u64, 0xA5FB0A17C777CF09u64], + [0x7182148D4066EEB4u64, 0xCF79CC9DB955C2CCu64], + [0xC6F14CD848405530u64, 0x81AC1FE293D599BFu64], + [0xB8ADA00E5A506A7Cu64, 0xA21727DB38CB002Fu64], + [0xA6D90811F0E4851Cu64, 0xCA9CF1D206FDC03Bu64], + [0x908F4A166D1DA663u64, 0xFD442E4688BD304Au64], + [0x9A598E4E043287FEu64, 0x9E4A9CEC15763E2Eu64], + [0x40EFF1E1853F29FDu64, 0xC5DD44271AD3CDBAu64], + [0xD12BEE59E68EF47Cu64, 0xF7549530E188C128u64], + [0x82BB74F8301958CEu64, 0x9A94DD3E8CF578B9u64], + [0xE36A52363C1FAF01u64, 0xC13A148E3032D6E7u64], + [0xDC44E6C3CB279AC1u64, 0xF18899B1BC3F8CA1u64], + [0x29AB103A5EF8C0B9u64, 0x96F5600F15A7B7E5u64], + [0x7415D448F6B6F0E7u64, 0xBCB2B812DB11A5DEu64], + [0x111B495B3464AD21u64, 0xEBDF661791D60F56u64], + [0xCAB10DD900BEEC34u64, 0x936B9FCEBB25C995u64], + [0x3D5D514F40EEA742u64, 0xB84687C269EF3BFBu64], + [0x0CB4A5A3112A5112u64, 0xE65829B3046B0AFAu64], + [0x47F0E785EABA72ABu64, 0x8FF71A0FE2C2E6DCu64], + [0x59ED216765690F56u64, 0xB3F4E093DB73A093u64], + [0x306869C13EC3532Cu64, 0xE0F218B8D25088B8u64], + [0x1E414218C73A13FBu64, 0x8C974F7383725573u64], + [0xE5D1929EF90898FAu64, 0xAFBD2350644EEACFu64], + [0xDF45F746B74ABF39u64, 0xDBAC6C247D62A583u64], + [0x6B8BBA8C328EB783u64, 0x894BC396CE5DA772u64], + [0x066EA92F3F326564u64, 0xAB9EB47C81F5114Fu64], + [0xC80A537B0EFEFEBDu64, 0xD686619BA27255A2u64], + [0xBD06742CE95F5F36u64, 0x8613FD0145877585u64], + [0x2C48113823B73704u64, 0xA798FC4196E952E7u64], + [0xF75A15862CA504C5u64, 0xD17F3B51FCA3A7A0u64], + [0x9A984D73DBE722FBu64, 0x82EF85133DE648C4u64], + [0xC13E60D0D2E0EBBAu64, 0xA3AB66580D5FDAF5u64], + [0x318DF905079926A8u64, 0xCC963FEE10B7D1B3u64], + [0xFDF17746497F7052u64, 0xFFBBCFE994E5C61Fu64], + [0xFEB6EA8BEDEFA633u64, 0x9FD561F1FD0F9BD3u64], + [0xFE64A52EE96B8FC0u64, 0xC7CABA6E7C5382C8u64], + [0x3DFDCE7AA3C673B0u64, 0xF9BD690A1B68637Bu64], + [0x06BEA10CA65C084Eu64, 0x9C1661A651213E2Du64], + [0x486E494FCFF30A62u64, 0xC31BFA0FE5698DB8u64], + [0x5A89DBA3C3EFCCFAu64, 0xF3E2F893DEC3F126u64], + [0xF89629465A75E01Cu64, 0x986DDB5C6B3A76B7u64], + [0xF6BBB397F1135823u64, 0xBE89523386091465u64], + [0x746AA07DED582E2Cu64, 0xEE2BA6C0678B597Fu64], + [0xA8C2A44EB4571CDCu64, 0x94DB483840B717EFu64], + [0x92F34D62616CE413u64, 0xBA121A4650E4DDEBu64], + [0x77B020BAF9C81D17u64, 0xE896A0D7E51E1566u64], + [0x0ACE1474DC1D122Eu64, 0x915E2486EF32CD60u64], + [0x0D819992132456BAu64, 0xB5B5ADA8AAFF80B8u64], + [0x10E1FFF697ED6C69u64, 0xE3231912D5BF60E6u64], + [0xCA8D3FFA1EF463C1u64, 0x8DF5EFABC5979C8Fu64], + [0xBD308FF8A6B17CB2u64, 0xB1736B96B6FD83B3u64], + [0xAC7CB3F6D05DDBDEu64, 0xDDD0467C64BCE4A0u64], + [0x6BCDF07A423AA96Bu64, 0x8AA22C0DBEF60EE4u64], + [0x86C16C98D2C953C6u64, 0xAD4AB7112EB3929Du64], + [0xE871C7BF077BA8B7u64, 0xD89D64D57A607744u64], + [0x11471CD764AD4972u64, 0x87625F056C7C4A8Bu64], + [0xD598E40D3DD89BCFu64, 0xA93AF6C6C79B5D2Du64], + [0x4AFF1D108D4EC2C3u64, 0xD389B47879823479u64], + [0xCEDF722A585139BAu64, 0x843610CB4BF160CBu64], + [0xC2974EB4EE658828u64, 0xA54394FE1EEDB8FEu64], + [0x733D226229FEEA32u64, 0xCE947A3DA6A9273Eu64], + [0x0806357D5A3F525Fu64, 0x811CCC668829B887u64], + [0xCA07C2DCB0CF26F7u64, 0xA163FF802A3426A8u64], + [0xFC89B393DD02F0B5u64, 0xC9BCFF6034C13052u64], + [0xBBAC2078D443ACE2u64, 0xFC2C3F3841F17C67u64], + [0xD54B944B84AA4C0Du64, 0x9D9BA7832936EDC0u64], + [0x0A9E795E65D4DF11u64, 0xC5029163F384A931u64], + [0x4D4617B5FF4A16D5u64, 0xF64335BCF065D37Du64], + [0x504BCED1BF8E4E45u64, 0x99EA0196163FA42Eu64], + [0xE45EC2862F71E1D6u64, 0xC06481FB9BCF8D39u64], + [0x5D767327BB4E5A4Cu64, 0xF07DA27A82C37088u64], + [0x3A6A07F8D510F86Fu64, 0x964E858C91BA2655u64], + [0x890489F70A55368Bu64, 0xBBE226EFB628AFEAu64], + [0x2B45AC74CCEA842Eu64, 0xEADAB0ABA3B2DBE5u64], + [0x3B0B8BC90012929Du64, 0x92C8AE6B464FC96Fu64], + [0x09CE6EBB40173744u64, 0xB77ADA0617E3BBCBu64], + [0xCC420A6A101D0515u64, 0xE55990879DDCAABDu64], + [0x9FA946824A12232Du64, 0x8F57FA54C2A9EAB6u64], + [0x47939822DC96ABF9u64, 0xB32DF8E9F3546564u64], + [0x59787E2B93BC56F7u64, 0xDFF9772470297EBDu64], + [0x57EB4EDB3C55B65Au64, 0x8BFBEA76C619EF36u64], + [0xEDE622920B6B23F1u64, 0xAEFAE51477A06B03u64], + [0xE95FAB368E45ECEDu64, 0xDAB99E59958885C4u64], + [0x11DBCB0218EBB414u64, 0x88B402F7FD75539Bu64], + [0xD652BDC29F26A119u64, 0xAAE103B5FCD2A881u64], + [0x4BE76D3346F0495Fu64, 0xD59944A37C0752A2u64], + [0x6F70A4400C562DDBu64, 0x857FCAE62D8493A5u64], + [0xCB4CCD500F6BB952u64, 0xA6DFBD9FB8E5B88Eu64], + [0x7E2000A41346A7A7u64, 0xD097AD07A71F26B2u64], + [0x8ED400668C0C28C8u64, 0x825ECC24C873782Fu64], + [0x728900802F0F32FAu64, 0xA2F67F2DFA90563Bu64], + [0x4F2B40A03AD2FFB9u64, 0xCBB41EF979346BCAu64], + [0xE2F610C84987BFA8u64, 0xFEA126B7D78186BCu64], + [0x0DD9CA7D2DF4D7C9u64, 0x9F24B832E6B0F436u64], + [0x91503D1C79720DBBu64, 0xC6EDE63FA05D3143u64], + [0x75A44C6397CE912Au64, 0xF8A95FCF88747D94u64], + [0xC986AFBE3EE11ABAu64, 0x9B69DBE1B548CE7Cu64], + [0xFBE85BADCE996168u64, 0xC24452DA229B021Bu64], + [0xFAE27299423FB9C3u64, 0xF2D56790AB41C2A2u64], + [0xDCCD879FC967D41Au64, 0x97C560BA6B0919A5u64], + [0x5400E987BBC1C920u64, 0xBDB6B8E905CB600Fu64], + [0x290123E9AAB23B68u64, 0xED246723473E3813u64], + [0xF9A0B6720AAF6521u64, 0x9436C0760C86E30Bu64], + [0xF808E40E8D5B3E69u64, 0xB94470938FA89BCEu64], + [0xB60B1D1230B20E04u64, 0xE7958CB87392C2C2u64], + [0xB1C6F22B5E6F48C2u64, 0x90BD77F3483BB9B9u64], + [0x1E38AEB6360B1AF3u64, 0xB4ECD5F01A4AA828u64], + [0x25C6DA63C38DE1B0u64, 0xE2280B6C20DD5232u64], + [0x579C487E5A38AD0Eu64, 0x8D590723948A535Fu64], + [0x2D835A9DF0C6D851u64, 0xB0AF48EC79ACE837u64], + [0xF8E431456CF88E65u64, 0xDCDB1B2798182244u64], + [0x1B8E9ECB641B58FFu64, 0x8A08F0F8BF0F156Bu64], + [0xE272467E3D222F3Fu64, 0xAC8B2D36EED2DAC5u64], + [0x5B0ED81DCC6ABB0Fu64, 0xD7ADF884AA879177u64], + [0x98E947129FC2B4E9u64, 0x86CCBB52EA94BAEAu64], + [0x3F2398D747B36224u64, 0xA87FEA27A539E9A5u64], + [0x8EEC7F0D19A03AADu64, 0xD29FE4B18E88640Eu64], + [0x1953CF68300424ACu64, 0x83A3EEEEF9153E89u64], + [0x5FA8C3423C052DD7u64, 0xA48CEAAAB75A8E2Bu64], + [0x3792F412CB06794Du64, 0xCDB02555653131B6u64], + [0xE2BBD88BBEE40BD0u64, 0x808E17555F3EBF11u64], + [0x5B6ACEAEAE9D0EC4u64, 0xA0B19D2AB70E6ED6u64], + [0xF245825A5A445275u64, 0xC8DE047564D20A8Bu64], + [0xEED6E2F0F0D56712u64, 0xFB158592BE068D2Eu64], + [0x55464DD69685606Bu64, 0x9CED737BB6C4183Du64], + [0xAA97E14C3C26B886u64, 0xC428D05AA4751E4Cu64], + [0xD53DD99F4B3066A8u64, 0xF53304714D9265DFu64], + [0xE546A8038EFE4029u64, 0x993FE2C6D07B7FABu64], + [0xDE98520472BDD033u64, 0xBF8FDB78849A5F96u64], + [0x963E66858F6D4440u64, 0xEF73D256A5C0F77Cu64], + [0xDDE7001379A44AA8u64, 0x95A8637627989AADu64], + [0x5560C018580D5D52u64, 0xBB127C53B17EC159u64], + [0xAAB8F01E6E10B4A6u64, 0xE9D71B689DDE71AFu64], + [0xCAB3961304CA70E8u64, 0x9226712162AB070Du64], + [0x3D607B97C5FD0D22u64, 0xB6B00D69BB55C8D1u64], + [0x8CB89A7DB77C506Au64, 0xE45C10C42A2B3B05u64], + [0x77F3608E92ADB242u64, 0x8EB98A7A9A5B04E3u64], + [0x55F038B237591ED3u64, 0xB267ED1940F1C61Cu64], + [0x6B6C46DEC52F6688u64, 0xDF01E85F912E37A3u64], + [0x2323AC4B3B3DA015u64, 0x8B61313BBABCE2C6u64], + [0xABEC975E0A0D081Au64, 0xAE397D8AA96C1B77u64], + [0x96E7BD358C904A21u64, 0xD9C7DCED53C72255u64], + [0x7E50D64177DA2E54u64, 0x881CEA14545C7575u64], + [0xDDE50BD1D5D0B9E9u64, 0xAA242499697392D2u64], + [0x955E4EC64B44E864u64, 0xD4AD2DBFC3D07787u64], + [0xBD5AF13BEF0B113Eu64, 0x84EC3C97DA624AB4u64], + [0xECB1AD8AEACDD58Eu64, 0xA6274BBDD0FADD61u64], + [0x67DE18EDA5814AF2u64, 0xCFB11EAD453994BAu64], + [0x80EACF948770CED7u64, 0x81CEB32C4B43FCF4u64], + [0xA1258379A94D028Du64, 0xA2425FF75E14FC31u64], + [0x096EE45813A04330u64, 0xCAD2F7F5359A3B3Eu64], + [0x8BCA9D6E188853FCu64, 0xFD87B5F28300CA0Du64], + [0x775EA264CF55347Du64, 0x9E74D1B791E07E48u64], + [0x95364AFE032A819Du64, 0xC612062576589DDAu64], + [0x3A83DDBD83F52204u64, 0xF79687AED3EEC551u64], + [0xC4926A9672793542u64, 0x9ABE14CD44753B52u64], + [0x75B7053C0F178293u64, 0xC16D9A0095928A27u64], + [0x5324C68B12DD6338u64, 0xF1C90080BAF72CB1u64], + [0xD3F6FC16EBCA5E03u64, 0x971DA05074DA7BEEu64], + [0x88F4BB1CA6BCF584u64, 0xBCE5086492111AEAu64], + [0x2B31E9E3D06C32E5u64, 0xEC1E4A7DB69561A5u64], + [0x3AFF322E62439FCFu64, 0x9392EE8E921D5D07u64], + [0x09BEFEB9FAD487C2u64, 0xB877AA3236A4B449u64], + [0x4C2EBE687989A9B3u64, 0xE69594BEC44DE15Bu64], + [0x0F9D37014BF60A10u64, 0x901D7CF73AB0ACD9u64], + [0x538484C19EF38C94u64, 0xB424DC35095CD80Fu64], + [0x2865A5F206B06FB9u64, 0xE12E13424BB40E13u64], + [0xF93F87B7442E45D3u64, 0x8CBCCC096F5088CBu64], + [0xF78F69A51539D748u64, 0xAFEBFF0BCB24AAFEu64], + [0xB573440E5A884D1Bu64, 0xDBE6FECEBDEDD5BEu64], + [0x31680A88F8953030u64, 0x89705F4136B4A597u64], + [0xFDC20D2B36BA7C3Du64, 0xABCC77118461CEFCu64], + [0x3D32907604691B4Cu64, 0xD6BF94D5E57A42BCu64], + [0xA63F9A49C2C1B10Fu64, 0x8637BD05AF6C69B5u64], + [0x0FCF80DC33721D53u64, 0xA7C5AC471B478423u64], + [0xD3C36113404EA4A8u64, 0xD1B71758E219652Bu64], + [0x645A1CAC083126E9u64, 0x83126E978D4FDF3Bu64], + [0x3D70A3D70A3D70A3u64, 0xA3D70A3D70A3D70Au64], + [0xCCCCCCCCCCCCCCCCu64, 0xCCCCCCCCCCCCCCCCu64], + [0x0000000000000000u64, 0x8000000000000000u64], + [0x0000000000000000u64, 0xA000000000000000u64], + [0x0000000000000000u64, 0xC800000000000000u64], + [0x0000000000000000u64, 0xFA00000000000000u64], + [0x0000000000000000u64, 0x9C40000000000000u64], + [0x0000000000000000u64, 0xC350000000000000u64], + [0x0000000000000000u64, 0xF424000000000000u64], + [0x0000000000000000u64, 0x9896800000000000u64], + [0x0000000000000000u64, 0xBEBC200000000000u64], + [0x0000000000000000u64, 0xEE6B280000000000u64], + [0x0000000000000000u64, 0x9502F90000000000u64], + [0x0000000000000000u64, 0xBA43B74000000000u64], + [0x0000000000000000u64, 0xE8D4A51000000000u64], + [0x0000000000000000u64, 0x9184E72A00000000u64], + [0x0000000000000000u64, 0xB5E620F480000000u64], + [0x0000000000000000u64, 0xE35FA931A0000000u64], + [0x0000000000000000u64, 0x8E1BC9BF04000000u64], + [0x0000000000000000u64, 0xB1A2BC2EC5000000u64], + [0x0000000000000000u64, 0xDE0B6B3A76400000u64], + [0x0000000000000000u64, 0x8AC7230489E80000u64], + [0x0000000000000000u64, 0xAD78EBC5AC620000u64], + [0x0000000000000000u64, 0xD8D726B7177A8000u64], + [0x0000000000000000u64, 0x878678326EAC9000u64], + [0x0000000000000000u64, 0xA968163F0A57B400u64], + [0x0000000000000000u64, 0xD3C21BCECCEDA100u64], + [0x0000000000000000u64, 0x84595161401484A0u64], + [0x0000000000000000u64, 0xA56FA5B99019A5C8u64], + [0x0000000000000000u64, 0xCECB8F27F4200F3Au64], + [0x4000000000000000u64, 0x813F3978F8940984u64], + [0x5000000000000000u64, 0xA18F07D736B90BE5u64], + [0xA400000000000000u64, 0xC9F2C9CD04674EDEu64], + [0x4D00000000000000u64, 0xFC6F7C4045812296u64], + [0xF020000000000000u64, 0x9DC5ADA82B70B59Du64], + [0x6C28000000000000u64, 0xC5371912364CE305u64], + [0xC732000000000000u64, 0xF684DF56C3E01BC6u64], + [0x3C7F400000000000u64, 0x9A130B963A6C115Cu64], + [0x4B9F100000000000u64, 0xC097CE7BC90715B3u64], + [0x1E86D40000000000u64, 0xF0BDC21ABB48DB20u64], + [0x1314448000000000u64, 0x96769950B50D88F4u64], + [0x17D955A000000000u64, 0xBC143FA4E250EB31u64], + [0x5DCFAB0800000000u64, 0xEB194F8E1AE525FDu64], + [0x5AA1CAE500000000u64, 0x92EFD1B8D0CF37BEu64], + [0xF14A3D9E40000000u64, 0xB7ABC627050305ADu64], + [0x6D9CCD05D0000000u64, 0xE596B7B0C643C719u64], + [0xE4820023A2000000u64, 0x8F7E32CE7BEA5C6Fu64], + [0xDDA2802C8A800000u64, 0xB35DBF821AE4F38Bu64], + [0xD50B2037AD200000u64, 0xE0352F62A19E306Eu64], + [0x4526F422CC340000u64, 0x8C213D9DA502DE45u64], + [0x9670B12B7F410000u64, 0xAF298D050E4395D6u64], + [0x3C0CDD765F114000u64, 0xDAF3F04651D47B4Cu64], + [0xA5880A69FB6AC800u64, 0x88D8762BF324CD0Fu64], + [0x8EEA0D047A457A00u64, 0xAB0E93B6EFEE0053u64], + [0x72A4904598D6D880u64, 0xD5D238A4ABE98068u64], + [0x47A6DA2B7F864750u64, 0x85A36366EB71F041u64], + [0x999090B65F67D924u64, 0xA70C3C40A64E6C51u64], + [0xFFF4B4E3F741CF6Du64, 0xD0CF4B50CFE20765u64], + [0xBFF8F10E7A8921A4u64, 0x82818F1281ED449Fu64], + [0xAFF72D52192B6A0Du64, 0xA321F2D7226895C7u64], + [0x9BF4F8A69F764490u64, 0xCBEA6F8CEB02BB39u64], + [0x02F236D04753D5B4u64, 0xFEE50B7025C36A08u64], + [0x01D762422C946590u64, 0x9F4F2726179A2245u64], + [0x424D3AD2B7B97EF5u64, 0xC722F0EF9D80AAD6u64], + [0xD2E0898765A7DEB2u64, 0xF8EBAD2B84E0D58Bu64], + [0x63CC55F49F88EB2Fu64, 0x9B934C3B330C8577u64], + [0x3CBF6B71C76B25FBu64, 0xC2781F49FFCFA6D5u64], + [0x8BEF464E3945EF7Au64, 0xF316271C7FC3908Au64], + [0x97758BF0E3CBB5ACu64, 0x97EDD871CFDA3A56u64], + [0x3D52EEED1CBEA317u64, 0xBDE94E8E43D0C8ECu64], + [0x4CA7AAA863EE4BDDu64, 0xED63A231D4C4FB27u64], + [0x8FE8CAA93E74EF6Au64, 0x945E455F24FB1CF8u64], + [0xB3E2FD538E122B44u64, 0xB975D6B6EE39E436u64], + [0x60DBBCA87196B616u64, 0xE7D34C64A9C85D44u64], + [0xBC8955E946FE31CDu64, 0x90E40FBEEA1D3A4Au64], + [0x6BABAB6398BDBE41u64, 0xB51D13AEA4A488DDu64], + [0xC696963C7EED2DD1u64, 0xE264589A4DCDAB14u64], + [0xFC1E1DE5CF543CA2u64, 0x8D7EB76070A08AECu64], + [0x3B25A55F43294BCBu64, 0xB0DE65388CC8ADA8u64], + [0x49EF0EB713F39EBEu64, 0xDD15FE86AFFAD912u64], + [0x6E3569326C784337u64, 0x8A2DBF142DFCC7ABu64], + [0x49C2C37F07965404u64, 0xACB92ED9397BF996u64], + [0xDC33745EC97BE906u64, 0xD7E77A8F87DAF7FBu64], + [0x69A028BB3DED71A3u64, 0x86F0AC99B4E8DAFDu64], + [0xC40832EA0D68CE0Cu64, 0xA8ACD7C0222311BCu64], + [0xF50A3FA490C30190u64, 0xD2D80DB02AABD62Bu64], + [0x792667C6DA79E0FAu64, 0x83C7088E1AAB65DBu64], + [0x577001B891185938u64, 0xA4B8CAB1A1563F52u64], + [0xED4C0226B55E6F86u64, 0xCDE6FD5E09ABCF26u64], + [0x544F8158315B05B4u64, 0x80B05E5AC60B6178u64], + [0x696361AE3DB1C721u64, 0xA0DC75F1778E39D6u64], + [0x03BC3A19CD1E38E9u64, 0xC913936DD571C84Cu64], + [0x04AB48A04065C723u64, 0xFB5878494ACE3A5Fu64], + [0x62EB0D64283F9C76u64, 0x9D174B2DCEC0E47Bu64], + [0x3BA5D0BD324F8394u64, 0xC45D1DF942711D9Au64], + [0xCA8F44EC7EE36479u64, 0xF5746577930D6500u64], + [0x7E998B13CF4E1ECBu64, 0x9968BF6ABBE85F20u64], + [0x9E3FEDD8C321A67Eu64, 0xBFC2EF456AE276E8u64], + [0xC5CFE94EF3EA101Eu64, 0xEFB3AB16C59B14A2u64], + [0xBBA1F1D158724A12u64, 0x95D04AEE3B80ECE5u64], + [0x2A8A6E45AE8EDC97u64, 0xBB445DA9CA61281Fu64], + [0xF52D09D71A3293BDu64, 0xEA1575143CF97226u64], + [0x593C2626705F9C56u64, 0x924D692CA61BE758u64], + [0x6F8B2FB00C77836Cu64, 0xB6E0C377CFA2E12Eu64], + [0x0B6DFB9C0F956447u64, 0xE498F455C38B997Au64], + [0x4724BD4189BD5EACu64, 0x8EDF98B59A373FECu64], + [0x58EDEC91EC2CB657u64, 0xB2977EE300C50FE7u64], + [0x2F2967B66737E3EDu64, 0xDF3D5E9BC0F653E1u64], + [0xBD79E0D20082EE74u64, 0x8B865B215899F46Cu64], + [0xECD8590680A3AA11u64, 0xAE67F1E9AEC07187u64], + [0xE80E6F4820CC9495u64, 0xDA01EE641A708DE9u64], + [0x3109058D147FDCDDu64, 0x884134FE908658B2u64], + [0xBD4B46F0599FD415u64, 0xAA51823E34A7EEDEu64], + [0x6C9E18AC7007C91Au64, 0xD4E5E2CDC1D1EA96u64], + [0x03E2CF6BC604DDB0u64, 0x850FADC09923329Eu64], + [0x84DB8346B786151Cu64, 0xA6539930BF6BFF45u64], + [0xE612641865679A63u64, 0xCFE87F7CEF46FF16u64], + [0x4FCB7E8F3F60C07Eu64, 0x81F14FAE158C5F6Eu64], + [0xE3BE5E330F38F09Du64, 0xA26DA3999AEF7749u64], + [0x5CADF5BFD3072CC5u64, 0xCB090C8001AB551Cu64], + [0x73D9732FC7C8F7F6u64, 0xFDCB4FA002162A63u64], + [0x2867E7FDDCDD9AFAu64, 0x9E9F11C4014DDA7Eu64], + [0xB281E1FD541501B8u64, 0xC646D63501A1511Du64], + [0x1F225A7CA91A4226u64, 0xF7D88BC24209A565u64], + [0x3375788DE9B06958u64, 0x9AE757596946075Fu64], + [0x0052D6B1641C83AEu64, 0xC1A12D2FC3978937u64], + [0xC0678C5DBD23A49Au64, 0xF209787BB47D6B84u64], + [0xF840B7BA963646E0u64, 0x9745EB4D50CE6332u64], + [0xB650E5A93BC3D898u64, 0xBD176620A501FBFFu64], + [0xA3E51F138AB4CEBEu64, 0xEC5D3FA8CE427AFFu64], + [0xC66F336C36B10137u64, 0x93BA47C980E98CDFu64], + [0xB80B0047445D4184u64, 0xB8A8D9BBE123F017u64], + [0xA60DC059157491E5u64, 0xE6D3102AD96CEC1Du64], + [0x87C89837AD68DB2Fu64, 0x9043EA1AC7E41392u64], + [0x29BABE4598C311FBu64, 0xB454E4A179DD1877u64], + [0xF4296DD6FEF3D67Au64, 0xE16A1DC9D8545E94u64], + [0x1899E4A65F58660Cu64, 0x8CE2529E2734BB1Du64], + [0x5EC05DCFF72E7F8Fu64, 0xB01AE745B101E9E4u64], + [0x76707543F4FA1F73u64, 0xDC21A1171D42645Du64], + [0x6A06494A791C53A8u64, 0x899504AE72497EBAu64], + [0x0487DB9D17636892u64, 0xABFA45DA0EDBDE69u64], + [0x45A9D2845D3C42B6u64, 0xD6F8D7509292D603u64], + [0x0B8A2392BA45A9B2u64, 0x865B86925B9BC5C2u64], + [0x8E6CAC7768D7141Eu64, 0xA7F26836F282B732u64], + [0x3207D795430CD926u64, 0xD1EF0244AF2364FFu64], + [0x7F44E6BD49E807B8u64, 0x8335616AED761F1Fu64], + [0x5F16206C9C6209A6u64, 0xA402B9C5A8D3A6E7u64], + [0x36DBA887C37A8C0Fu64, 0xCD036837130890A1u64], + [0xC2494954DA2C9789u64, 0x802221226BE55A64u64], + [0xF2DB9BAA10B7BD6Cu64, 0xA02AA96B06DEB0FDu64], + [0x6F92829494E5ACC7u64, 0xC83553C5C8965D3Du64], + [0xCB772339BA1F17F9u64, 0xFA42A8B73ABBF48Cu64], + [0xFF2A760414536EFBu64, 0x9C69A97284B578D7u64], + [0xFEF5138519684ABAu64, 0xC38413CF25E2D70Du64], + [0x7EB258665FC25D69u64, 0xF46518C2EF5B8CD1u64], + [0xEF2F773FFBD97A61u64, 0x98BF2F79D5993802u64], + [0xAAFB550FFACFD8FAu64, 0xBEEEFB584AFF8603u64], + [0x95BA2A53F983CF38u64, 0xEEAABA2E5DBF6784u64], + [0xDD945A747BF26183u64, 0x952AB45CFA97A0B2u64], + [0x94F971119AEEF9E4u64, 0xBA756174393D88DFu64], + [0x7A37CD5601AAB85Du64, 0xE912B9D1478CEB17u64], + [0xAC62E055C10AB33Au64, 0x91ABB422CCB812EEu64], + [0x577B986B314D6009u64, 0xB616A12B7FE617AAu64], + [0xED5A7E85FDA0B80Bu64, 0xE39C49765FDF9D94u64], + [0x14588F13BE847307u64, 0x8E41ADE9FBEBC27Du64], + [0x596EB2D8AE258FC8u64, 0xB1D219647AE6B31Cu64], + [0x6FCA5F8ED9AEF3BBu64, 0xDE469FBD99A05FE3u64], + [0x25DE7BB9480D5854u64, 0x8AEC23D680043BEEu64], + [0xAF561AA79A10AE6Au64, 0xADA72CCC20054AE9u64], + [0x1B2BA1518094DA04u64, 0xD910F7FF28069DA4u64], + [0x90FB44D2F05D0842u64, 0x87AA9AFF79042286u64], + [0x353A1607AC744A53u64, 0xA99541BF57452B28u64], + [0x42889B8997915CE8u64, 0xD3FA922F2D1675F2u64], + [0x69956135FEBADA11u64, 0x847C9B5D7C2E09B7u64], + [0x43FAB9837E699095u64, 0xA59BC234DB398C25u64], + [0x94F967E45E03F4BBu64, 0xCF02B2C21207EF2Eu64], + [0x1D1BE0EEBAC278F5u64, 0x8161AFB94B44F57Du64], + [0x6462D92A69731732u64, 0xA1BA1BA79E1632DCu64], + [0x7D7B8F7503CFDCFEu64, 0xCA28A291859BBF93u64], + [0x5CDA735244C3D43Eu64, 0xFCB2CB35E702AF78u64], + [0x3A0888136AFA64A7u64, 0x9DEFBF01B061ADABu64], + [0x088AAA1845B8FDD0u64, 0xC56BAEC21C7A1916u64], + [0x8AAD549E57273D45u64, 0xF6C69A72A3989F5Bu64], + [0x36AC54E2F678864Bu64, 0x9A3C2087A63F6399u64], + [0x84576A1BB416A7DDu64, 0xC0CB28A98FCF3C7Fu64], + [0x656D44A2A11C51D5u64, 0xF0FDF2D3F3C30B9Fu64], + [0x9F644AE5A4B1B325u64, 0x969EB7C47859E743u64], + [0x873D5D9F0DDE1FEEu64, 0xBC4665B596706114u64], + [0xA90CB506D155A7EAu64, 0xEB57FF22FC0C7959u64], + [0x09A7F12442D588F2u64, 0x9316FF75DD87CBD8u64], + [0x0C11ED6D538AEB2Fu64, 0xB7DCBF5354E9BECEu64], + [0x8F1668C8A86DA5FAu64, 0xE5D3EF282A242E81u64], + [0xF96E017D694487BCu64, 0x8FA475791A569D10u64], + [0x37C981DCC395A9ACu64, 0xB38D92D760EC4455u64], + [0x85BBE253F47B1417u64, 0xE070F78D3927556Au64], + [0x93956D7478CCEC8Eu64, 0x8C469AB843B89562u64], + [0x387AC8D1970027B2u64, 0xAF58416654A6BABBu64], + [0x06997B05FCC0319Eu64, 0xDB2E51BFE9D0696Au64], + [0x441FECE3BDF81F03u64, 0x88FCF317F22241E2u64], + [0xD527E81CAD7626C3u64, 0xAB3C2FDDEEAAD25Au64], + [0x8A71E223D8D3B074u64, 0xD60B3BD56A5586F1u64], + [0xF6872D5667844E49u64, 0x85C7056562757456u64], + [0xB428F8AC016561DBu64, 0xA738C6BEBB12D16Cu64], + [0xE13336D701BEBA52u64, 0xD106F86E69D785C7u64], + [0xECC0024661173473u64, 0x82A45B450226B39Cu64], + [0x27F002D7F95D0190u64, 0xA34D721642B06084u64], + [0x31EC038DF7B441F4u64, 0xCC20CE9BD35C78A5u64], + [0x7E67047175A15271u64, 0xFF290242C83396CEu64], + [0x0F0062C6E984D386u64, 0x9F79A169BD203E41u64], + [0x52C07B78A3E60868u64, 0xC75809C42C684DD1u64], + [0xA7709A56CCDF8A82u64, 0xF92E0C3537826145u64], + [0x88A66076400BB691u64, 0x9BBCC7A142B17CCBu64], + [0x6ACFF893D00EA435u64, 0xC2ABF989935DDBFEu64], + [0x0583F6B8C4124D43u64, 0xF356F7EBF83552FEu64], + [0xC3727A337A8B704Au64, 0x98165AF37B2153DEu64], + [0x744F18C0592E4C5Cu64, 0xBE1BF1B059E9A8D6u64], + [0x1162DEF06F79DF73u64, 0xEDA2EE1C7064130Cu64], + [0x8ADDCB5645AC2BA8u64, 0x9485D4D1C63E8BE7u64], + [0x6D953E2BD7173692u64, 0xB9A74A0637CE2EE1u64], + [0xC8FA8DB6CCDD0437u64, 0xE8111C87C5C1BA99u64], + [0x1D9C9892400A22A2u64, 0x910AB1D4DB9914A0u64], + [0x2503BEB6D00CAB4Bu64, 0xB54D5E4A127F59C8u64], + [0x2E44AE64840FD61Du64, 0xE2A0B5DC971F303Au64], + [0x5CEAECFED289E5D2u64, 0x8DA471A9DE737E24u64], + [0x7425A83E872C5F47u64, 0xB10D8E1456105DADu64], + [0xD12F124E28F77719u64, 0xDD50F1996B947518u64], + [0x82BD6B70D99AAA6Fu64, 0x8A5296FFE33CC92Fu64], + [0x636CC64D1001550Bu64, 0xACE73CBFDC0BFB7Bu64], + [0x3C47F7E05401AA4Eu64, 0xD8210BEFD30EFA5Au64], + [0x65ACFAEC34810A71u64, 0x8714A775E3E95C78u64], + [0x7F1839A741A14D0Du64, 0xA8D9D1535CE3B396u64], + [0x1EDE48111209A050u64, 0xD31045A8341CA07Cu64], + [0x934AED0AAB460432u64, 0x83EA2B892091E44Du64], + [0xF81DA84D5617853Fu64, 0xA4E4B66B68B65D60u64], + [0x36251260AB9D668Eu64, 0xCE1DE40642E3F4B9u64], + [0xC1D72B7C6B426019u64, 0x80D2AE83E9CE78F3u64], + [0xB24CF65B8612F81Fu64, 0xA1075A24E4421730u64], + [0xDEE033F26797B627u64, 0xC94930AE1D529CFCu64], + [0x169840EF017DA3B1u64, 0xFB9B7CD9A4A7443Cu64], + [0x8E1F289560EE864Eu64, 0x9D412E0806E88AA5u64], + [0xF1A6F2BAB92A27E2u64, 0xC491798A08A2AD4Eu64], + [0xAE10AF696774B1DBu64, 0xF5B5D7EC8ACB58A2u64], + [0xACCA6DA1E0A8EF29u64, 0x9991A6F3D6BF1765u64], + [0x17FD090A58D32AF3u64, 0xBFF610B0CC6EDD3Fu64], + [0xDDFC4B4CEF07F5B0u64, 0xEFF394DCFF8A948Eu64], + [0x4ABDAF101564F98Eu64, 0x95F83D0A1FB69CD9u64], + [0x9D6D1AD41ABE37F1u64, 0xBB764C4CA7A4440Fu64], + [0x84C86189216DC5EDu64, 0xEA53DF5FD18D5513u64], + [0x32FD3CF5B4E49BB4u64, 0x92746B9BE2F8552Cu64], + [0x3FBC8C33221DC2A1u64, 0xB7118682DBB66A77u64], + [0x0FABAF3FEAA5334Au64, 0xE4D5E82392A40515u64], + [0x29CB4D87F2A7400Eu64, 0x8F05B1163BA6832Du64], + [0x743E20E9EF511012u64, 0xB2C71D5BCA9023F8u64], + [0x914DA9246B255416u64, 0xDF78E4B2BD342CF6u64], + [0x1AD089B6C2F7548Eu64, 0x8BAB8EEFB6409C1Au64], + [0xA184AC2473B529B1u64, 0xAE9672ABA3D0C320u64], + [0xC9E5D72D90A2741Eu64, 0xDA3C0F568CC4F3E8u64], + [0x7E2FA67C7A658892u64, 0x8865899617FB1871u64], + [0xDDBB901B98FEEAB7u64, 0xAA7EEBFB9DF9DE8Du64], + [0x552A74227F3EA565u64, 0xD51EA6FA85785631u64], + [0xD53A88958F87275Fu64, 0x8533285C936B35DEu64], + [0x8A892ABAF368F137u64, 0xA67FF273B8460356u64], + [0x2D2B7569B0432D85u64, 0xD01FEF10A657842Cu64], + [0x9C3B29620E29FC73u64, 0x8213F56A67F6B29Bu64], + [0x8349F3BA91B47B8Fu64, 0xA298F2C501F45F42u64], + [0x241C70A936219A73u64, 0xCB3F2F7642717713u64], + [0xED238CD383AA0110u64, 0xFE0EFB53D30DD4D7u64], + [0xF4363804324A40AAu64, 0x9EC95D1463E8A506u64], + [0xB143C6053EDCD0D5u64, 0xC67BB4597CE2CE48u64], + [0xDD94B7868E94050Au64, 0xF81AA16FDC1B81DAu64], + [0xCA7CF2B4191C8326u64, 0x9B10A4E5E9913128u64], + [0xFD1C2F611F63A3F0u64, 0xC1D4CE1F63F57D72u64], + [0xBC633B39673C8CECu64, 0xF24A01A73CF2DCCFu64], + [0xD5BE0503E085D813u64, 0x976E41088617CA01u64], + [0x4B2D8644D8A74E18u64, 0xBD49D14AA79DBC82u64], + [0xDDF8E7D60ED1219Eu64, 0xEC9C459D51852BA2u64], + [0xCABB90E5C942B503u64, 0x93E1AB8252F33B45u64], + [0x3D6A751F3B936243u64, 0xB8DA1662E7B00A17u64], + [0x0CC512670A783AD4u64, 0xE7109BFBA19C0C9Du64], + [0x27FB2B80668B24C5u64, 0x906A617D450187E2u64], + [0xB1F9F660802DEDF6u64, 0xB484F9DC9641E9DAu64], + [0x5E7873F8A0396973u64, 0xE1A63853BBD26451u64], + [0xDB0B487B6423E1E8u64, 0x8D07E33455637EB2u64], + [0x91CE1A9A3D2CDA62u64, 0xB049DC016ABC5E5Fu64], + [0x7641A140CC7810FBu64, 0xDC5C5301C56B75F7u64], + [0xA9E904C87FCB0A9Du64, 0x89B9B3E11B6329BAu64], + [0x546345FA9FBDCD44u64, 0xAC2820D9623BF429u64], + [0xA97C177947AD4095u64, 0xD732290FBACAF133u64], + [0x49ED8EABCCCC485Du64, 0x867F59A9D4BED6C0u64], + [0x5C68F256BFFF5A74u64, 0xA81F301449EE8C70u64], + [0x73832EEC6FFF3111u64, 0xD226FC195C6A2F8Cu64], +]; + // strconv — number↔string conversions. // // Mirrors Hare's strconv:: surface. The *tos functions return a @@ -3904,143 +5635,6 @@ export fn tokprint(fd: i32, t: *tok) void = { fputcbyte(fd, 10u8); // '\n' }; -// ascii — rune-class predicates and case folding for the ASCII range. -// Matches Hare's ascii::isdigit family (rune-taking signature). Runes -// outside 0..127 always answer `false`. The lexer hot path uses these -// inline; they are expected to inline to a couple of compares. - -package ascii; - -export fn isdigit(c: rune) bool = { - if (c < 48) { return false; }; - if (c > 57) { return false; }; - return true; -}; - -export fn isupper(c: rune) bool = { - if (c < 65) { return false; }; - if (c > 90) { return false; }; - return true; -}; - -export fn islower(c: rune) bool = { - if (c < 97) { return false; }; - if (c > 122) { return false; }; - return true; -}; - -export fn isalpha(c: rune) bool = { - if (isupper(c)) { return true; }; - return islower(c); -}; - -export fn isalnum(c: rune) bool = { - if (isalpha(c)) { return true; }; - return isdigit(c); -}; - -// isspace — the C/Hare set: space, tab, NL, VT, FF, CR. -export fn isspace(c: rune) bool = { - if (c == 32) { return true; }; // ' ' - if (c == 9) { return true; }; // '\t' - if (c == 10) { return true; }; // '\n' - if (c == 11) { return true; }; // '\v' - if (c == 12) { return true; }; // '\f' - if (c == 13) { return true; }; // '\r' - return false; -}; - -export fn isxdigit(c: rune) bool = { - if (isdigit(c)) { return true; }; - if (c >= 65) { - if (c <= 70) { return true; }; // 'A'..'F' - }; - if (c >= 97) { - if (c <= 102) { return true; }; // 'a'..'f' - }; - return false; -}; - -// valid — `c` is in the 0..127 ASCII range. -export fn valid(c: rune) bool = { - if (c < 0) { return false; }; - if (c > 127) { return false; }; - return true; -}; - -// validstr — every byte in `s` is ASCII (0..127). -export fn validstr(s: str) bool = { - let i: i32 = 0; - for (i < s.len) { - // High-bit test rather than `> 127u8`; both cgens lower - // the bitwise form identically. The `> u8` form picks - // JA vs JG depending on signed/unsigned dispatch. - if ((s[i] & 128u8) != 0u8) { return false; }; - i += 1; - }; - return true; -}; - -// iscntrl — control chars: 0..31 and 127. -export fn iscntrl(c: rune) bool = { - if (c >= 0) { if (c <= 31) { return true; }; }; - if (c == 127) { return true; }; - return false; -}; - -// isblank — space and tab. -export fn isblank(c: rune) bool = { - if (c == 32) { return true; }; // ' ' - if (c == 9) { return true; }; // '\t' - return false; -}; - -// isprint — printable: space through '~'. -export fn isprint(c: rune) bool = { - if (c < 32) { return false; }; - if (c > 126) { return false; }; - return true; -}; - -// isgraph — printable, non-space. -export fn isgraph(c: rune) bool = { - if (c < 33) { return false; }; - if (c > 126) { return false; }; - return true; -}; - -// ispunct — printable, non-alnum, non-space. -export fn ispunct(c: rune) bool = { - if (!isgraph(c)) { return false; }; - if (isalnum(c)) { return false; }; - return true; -}; - -// tolower / toupper — fold ASCII case. Non-letters pass through. -export fn tolower(c: rune) rune = { - if (isupper(c)) { return c + 32; }; - return c; -}; - -export fn toupper(c: rune) rune = { - if (islower(c)) { return c - 32; }; - return c; -}; - -// strcasecmp — three-way ASCII case-insensitive compare. -export fn strcasecmp(a: str, b: str) i32 = { - let n: i32 = a.len; - if (b.len < n) { n = b.len; }; - let i: i32 = 0; - for (i < n) { - let ca: rune = tolower(a[i]: rune); - let cb: rune = tolower(b[i]: rune); - if (ca != cb) { return (ca - cb): i32; }; - i += 1; - }; - return a.len - b.len; -}; - // lib/ww/lex/lex.ww — port of cmd/wcc/lex.c. // // The DFA, the helpers, and the order of decisions all mirror the C diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 71f8dc46..f61626a1 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -1049,101 +1049,432 @@ fn decimal_round(d: *decimal) u64 = { return n; }; -// strconv — stof/ftos lookup tables. Mirrors ref/hare/strconv/stof_data.ha -// byte-exact. Pure-data fold (strconv #106 fold-2, was fold-3 before drew -// re-sequenced 2026-05-26): no logic, exercised transitively when fold-3's -// `leftshift_newdigits` lands (ref/hare/strconv/decimal.ha:35). -// -// ww uses module-level `let` for compile-time array data (ref/hare/strconv -// `const` has no ww keyword equivalent; lib/encoding/utf8/utf8.ww:48 sets -// the precedent with [2048]i8 dfa). Literal suffixes (`u16`, `u8`) are -// required because cstage rejects bare integer literals in `[N]u8`/`[N]u16` -// init while wwstage accepts them; the suffixed form is the only shape -// both stages agree on (candidate #130). -// -// `powers_of_ten: [596][2]u64` (ref/hare/strconv/stof_data.ha:73) is the -// Eisel-Lemire fast-path table and is only consumed by stof.ha (a later -// fold); deferred to its consumer-driven port per CLAUDE.md rule 9 -// (Hare-fidelity is API-shaped, and module-level tables follow their -// caller's scope). +// ascii — rune-class predicates and case folding for the ASCII range. +// Matches Hare's ascii::isdigit family (rune-taking signature). Runes +// outside 0..127 always answer `false`. The lexer hot path uses these +// inline; they are expected to inline to a couple of compares. -package strconv; +package ascii; -// ref/hare/strconv/stof_data.ha:4. Powers-of-five decimal-expansion -// metadata for `leftshift_newdigits` (decimal.ha:35). The top 5 bits -// of each entry are the new-digit-count `nn`; the low 11 bits index -// `pow5_table` for the digits themselves. -let left_shift_table: [65]u16 = [ - 0x0000u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16, 0x1817u16, - 0x181Du16, 0x2024u16, 0x202Bu16, 0x2033u16, 0x203Cu16, 0x2846u16, 0x2850u16, 0x285Bu16, 0x3067u16, - 0x3073u16, 0x3080u16, 0x388Eu16, 0x389Cu16, 0x38ABu16, 0x38BBu16, 0x40CCu16, 0x40DDu16, 0x40EFu16, - 0x4902u16, 0x4915u16, 0x4929u16, 0x513Eu16, 0x5153u16, 0x5169u16, 0x5180u16, 0x5998u16, 0x59B0u16, - 0x59C9u16, 0x61E3u16, 0x61FDu16, 0x6218u16, 0x6A34u16, 0x6A50u16, 0x6A6Du16, 0x6A8Bu16, 0x72AAu16, - 0x72C9u16, 0x72E9u16, 0x7B0Au16, 0x7B2Bu16, 0x7B4Du16, 0x8370u16, 0x8393u16, 0x83B7u16, 0x83DCu16, - 0x8C02u16, 0x8C28u16, 0x8C4Fu16, 0x9477u16, 0x949Fu16, 0x94C8u16, 0x9CF2u16, 0x051Cu16, 0x051Cu16, - 0x051Cu16, 0x051Cu16, -]; +export fn isdigit(c: rune) bool = { + if (c < 48) { return false; }; + if (c > 57) { return false; }; + return true; +}; -// ref/hare/strconv/stof_data.ha:15. Decimal digits of 5^k for k=1..60, -// concatenated. Indexed via `left_shift_table` (above); each shift k -// reads its `pow5_b - pow5_a` digits starting at `pow5_a`. -let pow5_table: [0x051C]u8 = [ - 5u8, 2u8, 5u8, 1u8, 2u8, 5u8, 6u8, 2u8, 5u8, 3u8, 1u8, 2u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 3u8, - 9u8, 0u8, 6u8, 2u8, 5u8, 1u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 9u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, 4u8, 8u8, 8u8, 2u8, 8u8, - 1u8, 2u8, 5u8, 2u8, 4u8, 4u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 1u8, 2u8, 2u8, 0u8, 7u8, 0u8, 3u8, 1u8, 2u8, 5u8, 6u8, 1u8, - 0u8, 3u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 3u8, 0u8, 5u8, 1u8, 7u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 1u8, 5u8, 2u8, 5u8, 8u8, - 7u8, 8u8, 9u8, 0u8, 6u8, 2u8, 5u8, 7u8, 6u8, 2u8, 9u8, 3u8, 9u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, 3u8, 8u8, 1u8, 4u8, 6u8, - 9u8, 7u8, 2u8, 6u8, 5u8, 6u8, 2u8, 5u8, 1u8, 9u8, 0u8, 7u8, 3u8, 4u8, 8u8, 6u8, 3u8, 2u8, 8u8, 1u8, 2u8, 5u8, 9u8, 5u8, - 3u8, 6u8, 7u8, 4u8, 3u8, 1u8, 6u8, 4u8, 0u8, 6u8, 2u8, 5u8, 4u8, 7u8, 6u8, 8u8, 3u8, 7u8, 1u8, 5u8, 8u8, 2u8, 0u8, 3u8, - 1u8, 2u8, 5u8, 2u8, 3u8, 8u8, 4u8, 1u8, 8u8, 5u8, 7u8, 9u8, 1u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 1u8, 1u8, 9u8, 2u8, 0u8, - 9u8, 2u8, 8u8, 9u8, 5u8, 5u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 5u8, 9u8, 6u8, 0u8, 4u8, 6u8, 4u8, 4u8, 7u8, 7u8, 5u8, 3u8, - 9u8, 0u8, 6u8, 2u8, 5u8, 2u8, 9u8, 8u8, 0u8, 2u8, 3u8, 2u8, 2u8, 3u8, 8u8, 7u8, 6u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 1u8, - 4u8, 9u8, 0u8, 1u8, 1u8, 6u8, 1u8, 1u8, 9u8, 3u8, 8u8, 4u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, 7u8, 4u8, 5u8, 0u8, 5u8, 8u8, - 0u8, 5u8, 9u8, 6u8, 9u8, 2u8, 3u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 3u8, 7u8, 2u8, 5u8, 2u8, 9u8, 0u8, 2u8, 9u8, 8u8, 4u8, - 6u8, 1u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 1u8, 8u8, 6u8, 2u8, 6u8, 4u8, 5u8, 1u8, 4u8, 9u8, 2u8, 3u8, 0u8, 9u8, 5u8, - 7u8, 0u8, 3u8, 1u8, 2u8, 5u8, 9u8, 3u8, 1u8, 3u8, 2u8, 2u8, 5u8, 7u8, 4u8, 6u8, 1u8, 5u8, 4u8, 7u8, 8u8, 5u8, 1u8, 5u8, - 6u8, 2u8, 5u8, 4u8, 6u8, 5u8, 6u8, 6u8, 1u8, 2u8, 8u8, 7u8, 3u8, 0u8, 7u8, 7u8, 3u8, 9u8, 2u8, 5u8, 7u8, 8u8, 1u8, 2u8, - 5u8, 2u8, 3u8, 2u8, 8u8, 3u8, 0u8, 6u8, 4u8, 3u8, 6u8, 5u8, 3u8, 8u8, 6u8, 9u8, 6u8, 2u8, 8u8, 9u8, 0u8, 6u8, 2u8, 5u8, - 1u8, 1u8, 6u8, 4u8, 1u8, 5u8, 3u8, 2u8, 1u8, 8u8, 2u8, 6u8, 9u8, 3u8, 4u8, 8u8, 1u8, 4u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, - 5u8, 8u8, 2u8, 0u8, 7u8, 6u8, 6u8, 0u8, 9u8, 1u8, 3u8, 4u8, 6u8, 7u8, 4u8, 0u8, 7u8, 2u8, 2u8, 6u8, 5u8, 6u8, 2u8, 5u8, - 2u8, 9u8, 1u8, 0u8, 3u8, 8u8, 3u8, 0u8, 4u8, 5u8, 6u8, 7u8, 3u8, 3u8, 7u8, 0u8, 3u8, 6u8, 1u8, 3u8, 2u8, 8u8, 1u8, 2u8, - 5u8, 1u8, 4u8, 5u8, 5u8, 1u8, 9u8, 1u8, 5u8, 2u8, 2u8, 8u8, 3u8, 6u8, 6u8, 8u8, 5u8, 1u8, 8u8, 0u8, 6u8, 6u8, 4u8, 0u8, - 6u8, 2u8, 5u8, 7u8, 2u8, 7u8, 5u8, 9u8, 5u8, 7u8, 6u8, 1u8, 4u8, 1u8, 8u8, 3u8, 4u8, 2u8, 5u8, 9u8, 0u8, 3u8, 3u8, 2u8, - 0u8, 3u8, 1u8, 2u8, 5u8, 3u8, 6u8, 3u8, 7u8, 9u8, 7u8, 8u8, 8u8, 0u8, 7u8, 0u8, 9u8, 1u8, 7u8, 1u8, 2u8, 9u8, 5u8, 1u8, - 6u8, 6u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 1u8, 8u8, 1u8, 8u8, 9u8, 8u8, 9u8, 4u8, 0u8, 3u8, 5u8, 4u8, 5u8, 8u8, 5u8, 6u8, - 4u8, 7u8, 5u8, 8u8, 3u8, 0u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 9u8, 0u8, 9u8, 4u8, 9u8, 4u8, 7u8, 0u8, 1u8, 7u8, 7u8, 2u8, - 9u8, 2u8, 8u8, 2u8, 3u8, 7u8, 9u8, 1u8, 5u8, 0u8, 3u8, 9u8, 0u8, 6u8, 2u8, 5u8, 4u8, 5u8, 4u8, 7u8, 4u8, 7u8, 3u8, 5u8, - 0u8, 8u8, 8u8, 6u8, 4u8, 6u8, 4u8, 1u8, 1u8, 8u8, 9u8, 5u8, 7u8, 5u8, 1u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 2u8, 2u8, 7u8, - 3u8, 7u8, 3u8, 6u8, 7u8, 5u8, 4u8, 4u8, 3u8, 2u8, 3u8, 2u8, 0u8, 5u8, 9u8, 4u8, 7u8, 8u8, 7u8, 5u8, 9u8, 7u8, 6u8, 5u8, - 6u8, 2u8, 5u8, 1u8, 1u8, 3u8, 6u8, 8u8, 6u8, 8u8, 3u8, 7u8, 7u8, 2u8, 1u8, 6u8, 1u8, 6u8, 0u8, 2u8, 9u8, 7u8, 3u8, 9u8, - 3u8, 7u8, 9u8, 8u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 5u8, 6u8, 8u8, 4u8, 3u8, 4u8, 1u8, 8u8, 8u8, 6u8, 0u8, 8u8, 0u8, 8u8, - 0u8, 1u8, 4u8, 8u8, 6u8, 9u8, 6u8, 8u8, 9u8, 9u8, 4u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 2u8, 8u8, 4u8, 2u8, 1u8, 7u8, 0u8, - 9u8, 4u8, 3u8, 0u8, 4u8, 0u8, 4u8, 0u8, 0u8, 7u8, 4u8, 3u8, 4u8, 8u8, 4u8, 4u8, 9u8, 7u8, 0u8, 7u8, 0u8, 3u8, 1u8, 2u8, - 5u8, 1u8, 4u8, 2u8, 1u8, 0u8, 8u8, 5u8, 4u8, 7u8, 1u8, 5u8, 2u8, 0u8, 2u8, 0u8, 0u8, 3u8, 7u8, 1u8, 7u8, 4u8, 2u8, 2u8, - 4u8, 8u8, 5u8, 3u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 7u8, 1u8, 0u8, 5u8, 4u8, 2u8, 7u8, 3u8, 5u8, 7u8, 6u8, 0u8, 1u8, 0u8, - 0u8, 1u8, 8u8, 5u8, 8u8, 7u8, 1u8, 1u8, 2u8, 4u8, 2u8, 6u8, 7u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 3u8, 5u8, 5u8, 2u8, 7u8, - 1u8, 3u8, 6u8, 7u8, 8u8, 8u8, 0u8, 0u8, 5u8, 0u8, 0u8, 9u8, 2u8, 9u8, 3u8, 5u8, 5u8, 6u8, 2u8, 1u8, 3u8, 3u8, 7u8, 8u8, - 9u8, 0u8, 6u8, 2u8, 5u8, 1u8, 7u8, 7u8, 6u8, 3u8, 5u8, 6u8, 8u8, 3u8, 9u8, 4u8, 0u8, 0u8, 2u8, 5u8, 0u8, 4u8, 6u8, 4u8, - 6u8, 7u8, 7u8, 8u8, 1u8, 0u8, 6u8, 6u8, 8u8, 9u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, 8u8, 8u8, 8u8, 1u8, 7u8, 8u8, 4u8, 1u8, - 9u8, 7u8, 0u8, 0u8, 1u8, 2u8, 5u8, 2u8, 3u8, 2u8, 3u8, 3u8, 8u8, 9u8, 0u8, 5u8, 3u8, 3u8, 4u8, 4u8, 7u8, 2u8, 6u8, 5u8, - 6u8, 2u8, 5u8, 4u8, 4u8, 4u8, 0u8, 8u8, 9u8, 2u8, 0u8, 9u8, 8u8, 5u8, 0u8, 0u8, 6u8, 2u8, 6u8, 1u8, 6u8, 1u8, 6u8, 9u8, - 4u8, 5u8, 2u8, 6u8, 6u8, 7u8, 2u8, 3u8, 6u8, 3u8, 2u8, 8u8, 1u8, 2u8, 5u8, 2u8, 2u8, 2u8, 0u8, 4u8, 4u8, 6u8, 0u8, 4u8, - 9u8, 2u8, 5u8, 0u8, 3u8, 1u8, 3u8, 0u8, 8u8, 0u8, 8u8, 4u8, 7u8, 2u8, 6u8, 3u8, 3u8, 3u8, 6u8, 1u8, 8u8, 1u8, 6u8, 4u8, - 0u8, 6u8, 2u8, 5u8, 1u8, 1u8, 1u8, 0u8, 2u8, 2u8, 3u8, 0u8, 2u8, 4u8, 6u8, 2u8, 5u8, 1u8, 5u8, 6u8, 5u8, 4u8, 0u8, 4u8, - 2u8, 3u8, 6u8, 3u8, 1u8, 6u8, 6u8, 8u8, 0u8, 9u8, 0u8, 8u8, 2u8, 0u8, 3u8, 1u8, 2u8, 5u8, 5u8, 5u8, 5u8, 1u8, 1u8, 1u8, - 5u8, 1u8, 2u8, 3u8, 1u8, 2u8, 5u8, 7u8, 8u8, 2u8, 7u8, 0u8, 2u8, 1u8, 1u8, 8u8, 1u8, 5u8, 8u8, 3u8, 4u8, 0u8, 4u8, 5u8, - 4u8, 1u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 2u8, 7u8, 7u8, 5u8, 5u8, 5u8, 7u8, 5u8, 6u8, 1u8, 5u8, 6u8, 2u8, 8u8, 9u8, 1u8, - 3u8, 5u8, 1u8, 0u8, 5u8, 9u8, 0u8, 7u8, 9u8, 1u8, 7u8, 0u8, 2u8, 2u8, 7u8, 0u8, 5u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 1u8, - 3u8, 8u8, 7u8, 7u8, 7u8, 8u8, 7u8, 8u8, 0u8, 7u8, 8u8, 1u8, 4u8, 4u8, 5u8, 6u8, 7u8, 5u8, 5u8, 2u8, 9u8, 5u8, 3u8, 9u8, - 5u8, 8u8, 5u8, 1u8, 1u8, 3u8, 5u8, 2u8, 5u8, 3u8, 9u8, 0u8, 6u8, 2u8, 5u8, 6u8, 9u8, 3u8, 8u8, 8u8, 9u8, 3u8, 9u8, 0u8, - 3u8, 9u8, 0u8, 7u8, 2u8, 2u8, 8u8, 3u8, 7u8, 7u8, 6u8, 4u8, 7u8, 6u8, 9u8, 7u8, 9u8, 2u8, 5u8, 5u8, 6u8, 7u8, 6u8, 2u8, - 6u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 3u8, 4u8, 6u8, 9u8, 4u8, 4u8, 6u8, 9u8, 5u8, 1u8, 9u8, 5u8, 3u8, 6u8, 1u8, 4u8, 1u8, - 8u8, 8u8, 8u8, 2u8, 3u8, 8u8, 4u8, 8u8, 9u8, 6u8, 2u8, 7u8, 8u8, 3u8, 8u8, 1u8, 3u8, 4u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, - 1u8, 7u8, 3u8, 4u8, 7u8, 2u8, 3u8, 4u8, 7u8, 5u8, 9u8, 7u8, 6u8, 8u8, 0u8, 7u8, 0u8, 9u8, 4u8, 4u8, 1u8, 1u8, 9u8, 2u8, - 4u8, 4u8, 8u8, 1u8, 3u8, 9u8, 1u8, 9u8, 0u8, 6u8, 7u8, 3u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 8u8, 6u8, 7u8, 3u8, 6u8, 1u8, - 7u8, 3u8, 7u8, 9u8, 8u8, 8u8, 4u8, 0u8, 3u8, 5u8, 4u8, 7u8, 2u8, 0u8, 5u8, 9u8, 6u8, 2u8, 2u8, 4u8, 0u8, 6u8, 9u8, 5u8, - 9u8, 5u8, 3u8, 3u8, 6u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, -]; +export fn isupper(c: rune) bool = { + if (c < 65) { return false; }; + if (c > 90) { return false; }; + return true; +}; + +export fn islower(c: rune) bool = { + if (c < 97) { return false; }; + if (c > 122) { return false; }; + return true; +}; + +export fn isalpha(c: rune) bool = { + if (isupper(c)) { return true; }; + return islower(c); +}; + +export fn isalnum(c: rune) bool = { + if (isalpha(c)) { return true; }; + return isdigit(c); +}; + +// isspace — the C/Hare set: space, tab, NL, VT, FF, CR. +export fn isspace(c: rune) bool = { + if (c == 32) { return true; }; // ' ' + if (c == 9) { return true; }; // '\t' + if (c == 10) { return true; }; // '\n' + if (c == 11) { return true; }; // '\v' + if (c == 12) { return true; }; // '\f' + if (c == 13) { return true; }; // '\r' + return false; +}; + +export fn isxdigit(c: rune) bool = { + if (isdigit(c)) { return true; }; + if (c >= 65) { + if (c <= 70) { return true; }; // 'A'..'F' + }; + if (c >= 97) { + if (c <= 102) { return true; }; // 'a'..'f' + }; + return false; +}; + +// valid — `c` is in the 0..127 ASCII range. +export fn valid(c: rune) bool = { + if (c < 0) { return false; }; + if (c > 127) { return false; }; + return true; +}; + +// validstr — every byte in `s` is ASCII (0..127). +export fn validstr(s: str) bool = { + let i: i32 = 0; + for (i < s.len) { + // High-bit test rather than `> 127u8`; both cgens lower + // the bitwise form identically. The `> u8` form picks + // JA vs JG depending on signed/unsigned dispatch. + if ((s[i] & 128u8) != 0u8) { return false; }; + i += 1; + }; + return true; +}; + +// iscntrl — control chars: 0..31 and 127. +export fn iscntrl(c: rune) bool = { + if (c >= 0) { if (c <= 31) { return true; }; }; + if (c == 127) { return true; }; + return false; +}; + +// isblank — space and tab. +export fn isblank(c: rune) bool = { + if (c == 32) { return true; }; // ' ' + if (c == 9) { return true; }; // '\t' + return false; +}; + +// isprint — printable: space through '~'. +export fn isprint(c: rune) bool = { + if (c < 32) { return false; }; + if (c > 126) { return false; }; + return true; +}; + +// isgraph — printable, non-space. +export fn isgraph(c: rune) bool = { + if (c < 33) { return false; }; + if (c > 126) { return false; }; + return true; +}; + +// ispunct — printable, non-alnum, non-space. +export fn ispunct(c: rune) bool = { + if (!isgraph(c)) { return false; }; + if (isalnum(c)) { return false; }; + return true; +}; + +// tolower / toupper — fold ASCII case. Non-letters pass through. +export fn tolower(c: rune) rune = { + if (isupper(c)) { return c + 32; }; + return c; +}; + +export fn toupper(c: rune) rune = { + if (islower(c)) { return c - 32; }; + return c; +}; + +// strcasecmp — three-way ASCII case-insensitive compare. +export fn strcasecmp(a: str, b: str) i32 = { + let n: i32 = a.len; + if (b.len < n) { n = b.len; }; + let i: i32 = 0; + for (i < n) { + let ca: rune = tolower(a[i]: rune); + let cb: rune = tolower(b[i]: rune); + if (ca != cb) { return (ca - cb): i32; }; + i += 1; + }; + return a.len - b.len; +}; + +// floats — f64 classification, sign, bit-reinterpret core, and the f64 +// decompose half (subnormal-normalize + frexp). Ported from +// ref/hare/math/floats.ha (fold-1: classify/sign/bits; fold-2a: +// issubnormalf64/normalizef64/frexpf64; strconv-foundation fold-1a: +// F32 bit-layout + f32bits/f32frombits + floatinfo struct type; +// fold-1b: NAN_BITS/INF_BITS sentinels; γ-cleanup: f64info/f32info +// instances re-folded once #149 lowered &math.f64info). frexpf64's +// zero guard `n == 0f64` rides the #103 +// fix (no-decimal f64 literal now materialized into XMM) and its +// (f64, i64) tuple return rides the #105 fix (tuple f64-word read). +// The ldexp/modfrac/nextafter family stays deferred (need f64 DIVIDE). + +package math; + +// Returns the binary representation of the given f64. +// ref/hare/math/floats.ha:5. Parens around &n are load-bearing: ww's `:` +// cast binds tighter than unary `&`, so Hare's `*(&n: *u64)` would parse +// as `*(&(n: *u64))`; `(&n): *u64` reinterprets the address as intended. +export fn f64bits(n: f64) u64 = { + return *((&n): *u64); +}; + +// Returns the binary representation of the given f32. +// ref/hare/math/floats.ha:8 +export fn f32bits(n: f32) u32 = { + return *((&n): *u32); +}; + +// Returns f64 with the given binary representation. +// ref/hare/math/floats.ha:11 +export fn f64frombits(n: u64) f64 = { + return *((&n): *f64); +}; + +// Returns f32 with the given binary representation. +// ref/hare/math/floats.ha:14 +export fn f32frombits(n: u32) f32 = { + return *((&n): *f32); +}; + +// ref/hare/math/floats.ha:17,20,23 declare these as untyped int. ww has +// no untyped def (every def carries a type) and routes shift/bitwise +// through unify_arith, which rejects mixed operand types (cmd/wcc/ +// check.c:769). The bit-structure consts are used only as u64 shift +// amounts and mask widths, so they are typed u64 here — the closest +// stand-in for Hare's untyped-int adapt at those use sites. + +// The number of bits in the significand of the binary representation of f64. +export def F64_MANTISSA_BITS: u64 = 52; + +// The number of bits in the exponent of the binary representation of f64. +export def F64_EXPONENT_BITS: u64 = 11; + +// The bias of the exponent of the binary representation of f64. Subtract this +// from the exponent in the binary representation to get the actual exponent. +export def F64_EXPONENT_BIAS: u64 = 1023; + +// Mask with each bit of an f64's mantissa set. +// ref/hare/math/floats.ha:37 +export def F64_MANTISSA_MASK: u64 = (1 << F64_MANTISSA_BITS) - 1; + +// Mask with each bit of an f64's exponent set. +// ref/hare/math/floats.ha:40 +export def F64_EXPONENT_MASK: u64 = (1 << F64_EXPONENT_BITS) - 1; + +// The mask that gets an f64's sign. +// ref/hare/math/floats.ha:75 +def F64_SIGN_MASK: u64 = 1u64 << 63; + +// Mask that clears an f64's exponent field, keeping sign + mantissa. +// ref/hare/math/floats.ha:77. Hare hardcodes the 0x800FFFFFFFFFFFFF binary +// literal because its lexer can't const-fold the expression; ww's #88 +// def-const-fold can, so the readable form is kept. floats.ha:79's NOTE +// expression has an `0u64 &` upstream typo (it would yield 0); the value it +// documents is exactly ~(F64_EXPONENT_MASK << F64_MANTISSA_BITS). +def F64_EXP_REMOVAL_MASK: u64 = ~(F64_EXPONENT_MASK << F64_MANTISSA_BITS); + +// The f64 bit pattern whose exponent field evaluates to zero (0.5 scale). +// ref/hare/math/floats.ha:84 +def F64_EXP_ZERO: u64 = (F64_EXPONENT_BIAS - 1) << F64_MANTISSA_BITS; + +// F32 bit-structure constants. ref/hare/math/floats.ha:27,30,33 declare +// these as untyped int; ww has no untyped def, so they ride u32 (matching +// the u32 bit container, the same way the F64 family rides u64 — see +// the note above F64_MANTISSA_BITS). + +// The number of bits in the significand of the binary representation of f32. +// ref/hare/math/floats.ha:27 +export def F32_MANTISSA_BITS: u32 = 23u32; + +// The number of bits in the exponent of the binary representation of f32. +// ref/hare/math/floats.ha:30 +export def F32_EXPONENT_BITS: u32 = 8u32; + +// The bias of the exponent of the binary representation of f32. Subtract this +// from the exponent in the binary representation to get the actual exponent. +// ref/hare/math/floats.ha:33 +export def F32_EXPONENT_BIAS: u32 = 127u32; + +// Mask with each bit of an f32's mantissa set. +// ref/hare/math/floats.ha:43 +export def F32_MANTISSA_MASK: u32 = (1u32 << F32_MANTISSA_BITS) - 1u32; + +// Mask with each bit of an f32's exponent set. +// ref/hare/math/floats.ha:46 +export def F32_EXPONENT_MASK: u32 = (1u32 << F32_EXPONENT_BITS) - 1u32; + +// The mask that gets an f32's sign. +// ref/hare/math/floats.ha:87 +def F32_SIGN_MASK: u32 = 1u32 << 31; + +// Mask that clears an f32's exponent field, keeping sign + mantissa. +// ref/hare/math/floats.ha:92. Hare hardcodes the binary literal (its +// lexer can't const-fold the expression); ww's #88 def-const-fold can, +// so the readable form is kept (same call as F64_EXP_REMOVAL_MASK). +def F32_EXP_REMOVAL_MASK: u32 = ~(F32_EXPONENT_MASK << F32_MANTISSA_BITS); + +// The f32 bit pattern whose exponent field evaluates to zero (0.5 scale). +// ref/hare/math/floats.ha:95 +def F32_EXP_ZERO: u32 = (F32_EXPONENT_BIAS - 1u32) << F32_MANTISSA_BITS; + +// floatinfo — IEEE-754 shape parameters for a binary float type, passed +// to width-generic helpers in strconv (eisel_lemire, floatbits, hex_to_bits, +// mkfloat). ref/hare/math/floats.ha:101. Hare's `int` maps to ww's `int` +// (machine word, 8B; project_int_machine_word_derived_limits), so the +// expbias field stays `int` — that keeps the fold-4 stof port byte-for-byte +// against ref/hare/strconv/stof.ha:248,288 (`let e: int = 0` arithmetic +// against `f.expbias` of the same type, no cast at use site). +export type floatinfo = struct { + // Bits in significand. + mantbits: u64, + // Bits in exponent. + expbits: u64, + // Bias of exponent. + expbias: int, + // Mask for mantissa. + mantmask: u64, + // Mask for exponent. + expmask: u64, +}; + +// floatinfo instances for the f64 / f32 types, consumed by the +// width-generic strconv helpers via &math.f64info (cross-module +// address-of, lowered since #149). ref/hare/math/floats.ha:117,126. +// Hare spells the masks (1 << 52) - 1 / (1 << 23) - 1; the #129 A.2 +// struct-composite static-init path folds only bare-literal field +// initializers, not const-fold expressions, so the value-identical hex +// literals are used here (0xFFFFFFFFFFFFF == (1<<52)-1, 0x7FFFFF == +// (1<<23)-1 — same hex-literal style as the NAN_BITS/INF_BITS sentinels +// below). expbias rides `int` (the field type) with no suffix. +export def f64info: floatinfo = floatinfo { + mantbits = 52u64, + expbits = 11u64, + expbias = 1023, + mantmask = 0xFFFFFFFFFFFFFu64, + expmask = 0x7FFu64, +}; + +export def f32info: floatinfo = floatinfo { + mantbits = 23u64, + expbits = 8u64, + expbias = 127, + mantmask = 0x7FFFFFu64, + expmask = 0xFFu64, +}; + +// IEEE-754 quiet-NaN and positive-Infinity f64 bit sentinels. +// ref/hare/math/floats.ha:137,141. Hare exports `def NAN = 0.0/0.0;` and +// `def INF = 1.0/0.0;` (untyped float def-fold); ww's cgen doesn't lower +// `def: f64 = expr;` (the symbol comes out undefined at link time — see +// #129). Callers materialize the f64 sentinel via f64frombits(NAN_BITS) +// / f64frombits(INF_BITS); same bit-exact value, one extra reinterpret. +// 0x7FF8000000000000 is the IEEE-754 binary64 quiet-NaN (sign=0, exp= +// all-ones, mantissa MSB=1, rest=0); 0x7FF0000000000000 is +Infinity +// (sign=0, exp=all-ones, mantissa=0). Re-fold to `def NAN: f64 = ...` +// when #129 closes (γ-cleanup pattern per amalloc-drop precedent). +export def NAN_BITS: u64 = 0x7FF8000000000000u64; +export def INF_BITS: u64 = 0x7FF0000000000000u64; + +// Returns true if the given floating-point number is NaN. +// ref/hare/math/floats.ha:144 (Hare's expression body inlined into a +// block: ww has no expression-bodied fn form, only brace blocks). +export fn isnan(n: f64) bool = { + return n != n; +}; + +// Returns true if the given floating-point number is infinite. +// ref/hare/math/floats.ha:147 +export fn isinf(n: f64) bool = { + const bits = f64bits(n); + const mant = bits & F64_MANTISSA_MASK; + const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK; + return exp == F64_EXPONENT_MASK && mant == 0; +}; + +// Returns true if the given f64 is subnormal. +// ref/hare/math/floats.ha:179 +export fn issubnormalf64(n: f64) bool = { + const bits = f64bits(n); + const mant = bits & F64_MANTISSA_MASK; + const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK; + return exp == 0 && mant != 0; +}; + +// Returns the absolute value of f64 n. +// ref/hare/math/floats.ha:195 +export fn absf64(n: f64) f64 = { + if (isnan(n)) { + return n; + }; + return f64frombits(f64bits(n) & ~F64_SIGN_MASK); +}; + +// Returns 1 if x is positive and -1 if x is negative. Note that zero is also +// signed. +// ref/hare/math/floats.ha:212 +export fn signf64(x: f64) i64 = { + if (f64bits(x) & F64_SIGN_MASK == 0) { + return 1i64; + } else { + return -1i64; + }; +}; + +// Returns whether or not x is positive. +// ref/hare/math/floats.ha:231 +export fn ispositivef64(x: f64) bool = { + return signf64(x) == 1i64; +}; + +// Returns whether or not x is negative. +// ref/hare/math/floats.ha:237 +export fn isnegativef64(x: f64) bool = { + return signf64(x) == -1i64; +}; + +// Returns x, but with the sign of y. +// ref/hare/math/floats.ha:243 +export fn copysignf64(x: f64, y: f64) f64 = { + return f64frombits((f64bits(x) & ~F64_SIGN_MASK) | + (f64bits(y) & F64_SIGN_MASK)); +}; + +// Takes a potentially subnormal f64 n and returns a normal f64 normal_float +// and an exponent exp such that n == normal_float * 2^{exp}. +// ref/hare/math/floats.ha:256 +export fn normalizef64(n: f64) (f64, i64) = { + if (issubnormalf64(n)) { + const factor = 1i64 << (F64_MANTISSA_BITS: i64); + const normal_float = (n * (factor: f64)); + return (normal_float, -(F64_MANTISSA_BITS: i64)); + }; + return (n, 0); +}; + +// Breaks a f64 down into its mantissa and exponent. The mantissa will be +// between 0.5 and 1. +// ref/hare/math/floats.ha:278 +export fn frexpf64(n: f64) (f64, i64) = { + if (isnan(n) || isinf(n) || n == 0f64) { + return (n, 0); + }; + const normalized = normalizef64(n); + const normal_float = normalized.0; + const normalization_exp = normalized.1; + const bits = f64bits(normal_float); + const raw_exp: u64 = (bits >> F64_MANTISSA_BITS) & F64_EXPONENT_MASK; + const exp: i64 = normalization_exp + + (raw_exp: i64) - (F64_EXPONENT_BIAS: i64) + 1; + const mantissa: f64 = + f64frombits((bits & F64_EXP_REMOVAL_MASK) | F64_EXP_ZERO); + return (mantissa, exp); +}; + +// math — numeric helpers. Subset of Hare's math::; only the absolute- +// value pair for the signed integer types we currently care about. The +// return type is unsigned so that abs(I32_MIN) doesn't overflow. + +package math; + +export fn absi32(n: i32) u32 = { + if (n < 0) { return (-n): u32; }; + return n: u32; +}; + +export fn absi64(n: i64) u64 = { + if (n < 0) { return (-n): u64; }; + return n: u64; +}; // types — integer limits. Mirrors Hare's types::limits (I8_MAX, …) // platform-fixed for amd64. Numeric helpers live in lib/math, matching @@ -3122,6 +3453,1406 @@ export fn rpad(s: str, p: rune, maxlen: i32) str = { return frombytes(buf); }; +// strconv — string-to-float. Mirrors ref/hare/strconv/stof.ha +// (Hare in turn adapts Go): Eisel-Lemire fast path [1] with the +// Simple-Decimal-Conversion slow path [2] (decimal.ww) as fallback. +// [1]: https://nigeltao.github.io/blog/2020/eisel-lemire.html +// [2]: https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html +// +// The Eisel-Lemire fast path (`eisel_lemire` + the `powers_of_ten` +// table in stof_data.ww + the three call sites: floatbits's d.nd<=19 +// block, stof64/stof32's !truncated block) is a pure speed +// optimisation — it returns the same correctly-rounded value the +// decimal slow path (decimal_parse → floatbits) computes, or void to +// defer. Its prereqs landed: the 2D `[596][2]u64` static-init + +// double-index read (#156) and the tagged float-variant return-pack +// (#157, which the public `(f64|invalid|overflow)` return needs). +// +// Spelling divergences from Hare (mechanical, ww parser/cgen shape): +// - str scan index rides `i32` (ww `str.len: i32` + `invalid = !i32` +// payload), not Hare's `size`/`len(s)`. lib CLAUDE.md str-index note. +// - char literals kept faithful (`buf[i] == '.'`, `c - '0'`); probed +// byte-id + value-correct both stages. +// - Hare `?` error-propagation → nested statement-`match` with all- +// return arms + a `case void => void` continuation. ww's `?` +// lowering and a bound `match`-expression with mixed yield/return +// arms both diverge cs≠ww (the latter wwstage-checker-rejected); +// strconv.ww's stoi32 set the explicit-match precedent. +// - Hare `for (cond; afterthought)` 2-clause + `continue` → ww +// 2-clause `for (cond)` with the afterthought inlined at body end +// AND before each `continue` (ww has no empty-init 3-clause +// `for (; c; p)`; #138 post-skip is dodged since 2-clause has no +// post). decimal.ww set the inline-afterthought precedent. +// - Hare `if`/`switch`-expression yield → explicit if-statements + +// pre-bound scalar locals (ww has no expression-bodied if). +// - Hare fn-pointer-in-tuple + `switch yield` selecting the digit +// predicate in fast_parse → a `base==HEX` bool + an `isdigitbase` +// helper that branches to ascii.isdigit/isxdigit (no fn-ptr, no +// tuple, no switch). +// - struct-param field MUTATION (hex_to_bits mutates its by-value +// `p`) → copy p's fields to scalar locals at entry; ww miscompiles +// + diverges on writing a by-value struct param's fields (filed). +// - default arg dropped: Hare `b: base = base::DEC` → callers pass +// base explicitly (no lib fn ships a default arg; strconv.ww +// stoi64 precedent). The base param is normalised through a local +// `bb` (param reassignment avoided). +// - `math::NAN`/`math::INF` (f32) absent in ww math → materialised +// via f32frombits of the IEEE-754 f32 bit patterns (same honest +// construction as math/floats.ww's NAN_BITS/INF_BITS). +// - narrowing int→i32 assignments carry explicit casts (ww `int` is +// an 8B machine word; project_int_machine_word_derived_limits). +// - `r128`/`u128mul` live here (fold-4 is first consumer); fold-5 +// ftos (Ryū) shares them in-package. + +package strconv; + +import ascii; +import math; +import os; +import strings; + +// ref/hare/strconv/ftos_ryu.ha:12. 64×64→128 result halves. +type r128 = struct { + hi: u64, + lo: u64, +}; + +// ref/hare/strconv/ftos_ryu.ha:18. 64×64→128 via 32-bit decomposition +// (Hare's own "TODO: use 128-bit integers when implemented" — ww has +// no u128; the decomposition is the portable shape both stages agree +// on). Comma let-bindings split per decimal.ww divergence. +fn u128mul(a: u64, b: u64) r128 = { + let a0: u64 = (a: u32): u64; + let a1: u64 = a >> 32u64; + let b0: u64 = (b: u32): u64; + let b1: u64 = b >> 32u64; + let p00: u64 = a0 * b0; + let p01: u64 = a0 * b1; + let p10: u64 = a1 * b0; + let p11: u64 = a1 * b1; + let p00_lo: u64 = (p00: u32): u64; + let p00_hi: u64 = p00 >> 32u64; + let mid1: u64 = p10 + p00_hi; + let mid1_lo: u64 = (mid1: u32): u64; + let mid1_hi: u64 = mid1 >> 32u64; + let mid2: u64 = p01 + mid1_lo; + let mid2_lo: u64 = (mid2: u32): u64; + let mid2_hi: u64 = mid2 >> 32u64; + let r_hi: u64 = p11 + mid1_hi + mid2_hi; + let r_lo: u64 = (mid2_lo << 32u64) | p00_lo; + return r128 { hi = r_hi, lo = r_lo }; +}; + +// ref/hare/strconv/stof.ha:14. +fn todig(c: u8) u8 = { + if ('0' <= c && c <= '9') { return c - '0'; }; + if ('a' <= c && c <= 'f') { return c - 'a' + 10u8; }; + if ('A' <= c && c <= 'F') { return c - 'A' + 10u8; }; + abort("strconv.todig: unreachable"); + return 0u8; // unreachable; rt_abort is void-typed (path-cov) +}; + +@symbol("rt_abort") fn abort(msg: str) void; + +// ref/hare/strconv/stof.ha:25. +type fast_parsed_float = struct { + mantissa: u64, + exponent: i32, + negative: bool, + truncated: bool, +}; + +// Digit-class predicate selector for fast_parse — replaces Hare's +// fn-pointer-in-tuple (`&ascii::isdigit` / `&ascii::isxdigit`). +fn isdigitbase(c: rune, ishex: bool) bool = { + if (ishex) { return ascii.isxdigit(c); }; + return ascii.isdigit(c); +}; + +// ref/hare/strconv/stof.ha:32. +fn fast_parse(s: str, b: base) (fast_parsed_float | invalid) = { + let buf: []u8 = strings.toutf8(s); + let i: i32 = 0; + let neg: bool = false; + let trunc: bool = false; + if (buf[i] == '-') { + neg = true; + i += 1; + } else if (buf[i] == '+') { + i += 1; + }; + + let ishex: bool = (b == base.HEX); + let expchr: rune = 'e'; + let max_ndmant: int = 19; + if (ishex) { + expchr = 'p'; + max_ndmant = 16; + }; + let bnum: u64 = (b: i32): u64; + + let sawdot: bool = false; + let sawdigits: bool = false; + let nd: int = 0; + let ndmant: int = 0; + let dp: int = 0; + let mant: u64 = 0u64; + let exp: i32 = 0i32; + for (i < s.len) { + if (buf[i] == '.') { + if (sawdot) { return i: invalid; }; + sawdot = true; + dp = nd; + } else if (isdigitbase(buf[i]: rune, ishex)) { + sawdigits = true; + if (buf[i] == '0' && nd == 0) { + dp -= 1; + i += 1; + continue; + }; + nd += 1; + if (ndmant < max_ndmant) { + mant = mant * bnum + (todig(buf[i]): u64); + ndmant += 1; + } else if (buf[i] != '0') { + trunc = true; + }; + } else { + break; + }; + i += 1; + }; + if (!sawdigits) { return i: invalid; }; + if (!sawdot) { + dp = nd; + }; + if (b == base.HEX) { + dp *= 4; + ndmant *= 4; + }; + if (i < s.len && ascii.tolower(buf[i]: rune) == expchr) { + i += 1; + if (i >= s.len) { return i: invalid; }; + let expsign: int = 1; + if (buf[i] == '+') { + i += 1; + } else if (buf[i] == '-') { + expsign = -1; + i += 1; + }; + if (i >= s.len || !ascii.isdigit(buf[i]: rune)) { + return i: invalid; + }; + let e: int = 0; + for (i < s.len && ascii.isdigit(buf[i]: rune)) { + if (e < 10000) { + e = e * 10 + ((buf[i] - '0'): int); + }; + i += 1; + }; + dp += e * expsign; + } else if (b == base.HEX) { + return i: invalid; // hex floats must have an exponent + }; + if (i != s.len) { return i: invalid; }; + if (mant != 0u64) { + exp = (dp - ndmant): i32; + }; + return fast_parsed_float { + mantissa = mant, + exponent = exp, + negative = neg, + truncated = trunc, + }; +}; + +// ref/hare/strconv/stof.ha:115. Fills the slow-path decimal `d`. +fn decimal_parse(d: *decimal, s: str) (void | invalid) = { + let i: i32 = 0; + let buf: []u8 = strings.toutf8(s); + d.negative = false; + d.truncated = false; + if (buf[0] == '+') { + i += 1; + } else if (buf[0] == '-') { + d.negative = true; + i += 1; + }; + let sawdot: bool = false; + let sawdigits: bool = false; + for (i < s.len) { + if (buf[i] == '.') { + if (sawdot) { return i: invalid; }; + sawdot = true; + d.dp = (d.nd: i32); + } else if (ascii.isdigit(buf[i]: rune)) { + sawdigits = true; + if (buf[i] == '0' && d.nd == (0u64: size)) { + d.dp -= 1; + i += 1; + continue; + }; + if (d.nd < (len(d.digits): size)) { + d.digits[d.nd] = buf[i] - '0'; + d.nd += (1u64: size); + } else if (buf[i] != '0') { + d.truncated = true; + }; + } else { + break; + }; + i += 1; + }; + if (!sawdigits) { return i: invalid; }; + if (!sawdot) { + d.dp = (d.nd: i32); + }; + if (i < s.len && (buf[i] == 'e' || buf[i] == 'E')) { + i += 1; + if (i >= s.len) { return i: invalid; }; + let expsign: int = 1; + if (buf[i] == '+') { + i += 1; + } else if (buf[i] == '-') { + expsign = -1; + i += 1; + }; + if (i >= s.len || !ascii.isdigit(buf[i]: rune)) { + return i: invalid; + }; + let e: int = 0; + for (i < s.len && ascii.isdigit(buf[i]: rune)) { + if (e < 10000) { + e = e * 10 + ((buf[i] - '0'): int); + }; + i += 1; + }; + d.dp += (e * expsign): i32; + }; + if (i != s.len) { return i: invalid; }; + return; +}; + +// ref/hare/strconv/stof.ha:173. Count of leading zero bits in n>0. +fn leading_zeroes(n: u64) uint = { + os.assert(n > 0u64, "strconv.leading_zeroes: n == 0"); + let b: u64 = 0u64; + if ((n & 0xFFFFFFFF00000000u64) > 0u64) { + n >>= 32u64; + b |= 32u64; + }; + if ((n & 0xFFFF0000u64) > 0u64) { + n >>= 16u64; + b |= 16u64; + }; + if ((n & 0xFF00u64) > 0u64) { + n >>= 8u64; + b |= 8u64; + }; + if ((n & 0xF0u64) > 0u64) { + n >>= 4u64; + b |= 4u64; + }; + if ((n & 0xCu64) > 0u64) { + n >>= 2u64; + b |= 2u64; + }; + if ((n & 0x2u64) > 0u64) { + n >>= 1u64; + b |= 1u64; + }; + return ((63u64 - b): uint); +}; + +// ref/hare/strconv/stof.ha:203. Eisel-Lemire fast path: a correctly- +// rounded f64/f32 from (mantissa, exp10) when the 128-bit product is +// unambiguous, else void → caller falls to the decimal slow path. +// Divergences at-site: `mantissa <<= clz` (scalar-param mutate) → local +// `mnt`; whole-struct local reassign `x = merged` copies only the first +// word in cgen → per-field `x.hi = …; x.lo = …` (#155); `po10 = +// powers_of_ten[i]` row-bind → direct double-index (#155, A2); bitwise- +// vs-compare fully parenthesised; comma let-bindings split. +fn eisel_lemire( + mantissa: u64, + exp10: i32, + neg: bool, + f: *math.floatinfo, +) (u64 | void) = { + if (mantissa == 0u64 || exp10 > 288 || exp10 < -307) { + return; + }; + let idx: i32 = exp10 + 307; + let clz: uint = leading_zeroes(mantissa); + let mnt: u64 = mantissa << (clz: u64); + let shift: u64 = 64u64 - f.mantbits - 3u64; + let mask: u64 = (1u64 << shift) - 1u64; + // log(10)/log(2) ≈ 217706 / 65536; x / 65536 = x >> 16. + let exp: int = (217706 * (exp10: int)) >> 16; + let e2: u64 = ((exp + f.expbias + 64): u64) - (clz: u64); + let x: r128 = u128mul(mnt, powers_of_ten[idx][1]); + if ((x.hi & mask) == mask && (x.lo + mnt) < mnt) { + let y: r128 = u128mul(mnt, powers_of_ten[idx][0]); + let merged: r128 = r128 { hi = x.hi, lo = x.lo + y.hi }; + if (merged.lo < x.lo) { + // local-struct-field compound-assign drops the load in + // wwstage (sets =1, not +=1) — explicit form, byte-id. + merged.hi = merged.hi + 1u64; + }; + if ((merged.hi & mask) == mask && (merged.lo + 1u64) == 0u64 && + (y.lo + mnt) < mnt) { + return; + }; + x.hi = merged.hi; + x.lo = merged.lo; + }; + let msb: u64 = x.hi >> 63u64; + let mant: u64 = x.hi >> (msb + shift); + e2 -= 1u64 ^ msb; + if (x.lo == 0u64 && (x.hi & mask) == 0u64 && (mant & 3u64) == 1u64) { + return; + }; + mant += mant & 1u64; + mant >>= 1u64; + if ((mant >> (f.mantbits + 1u64)) > 0u64) { + mant >>= 1u64; + e2 += 1u64; + }; + if (e2 <= 0u64 || e2 >= (1u64 << f.expbits) - 1u64) { + return; + }; + return mkfloat(mant, (e2: uint), neg, f); +}; + +// ref/hare/strconv/stof.ha:247. Slow-path: decimal `d` → IEEE bits. +fn floatbits(d: *decimal, f: *math.floatinfo) (u64 | overflow) = { + let e: int = 0; + let m: u64 = 0u64; + let powtab: [19]i8 = [ + 0i8, 3i8, 6i8, 9i8, 13i8, 16i8, 19i8, 23i8, 26i8, 29i8, + 33i8, 36i8, 39i8, 43i8, 46i8, 49i8, 53i8, 56i8, 59i8, + ]; + if (d.nd == (0u64: size) || d.dp < -326) { + if (d.negative) { + return mkfloat(0u64, (0u32: uint), d.negative, f); + }; + return 0u64; + } else if (d.dp > 310) { + return overflow{}; + }; + if (d.nd <= (19u64: size)) { + let dmant: u64 = 0u64; + let i: size = (0u64: size); + for (i < d.nd) { + dmant = 10u64 * dmant + (d.digits[i]: u64); + i += (1u64: size); + }; + let exp10: i32 = d.dp - (d.nd: i32); + match (eisel_lemire(dmant, exp10, d.negative, f)) { + case let r: u64 => { return r; }; + case void => void; + }; + }; + for (d.dp > 0) { + let n: int = 0; + if ((d.dp: uint) >= (len(powtab): uint)) { + n = (maxshift: int); + } else { + n = (powtab[d.dp]: int); + }; + decimal_shift(d, -n); + e += n; + }; + for (d.dp <= 0) { + let n: int = 0; + if (d.dp == 0) { + if (d.digits[0] >= 5u8) { break; }; + if (d.digits[0] < 2u8) { n = 2; } else { n = 1; }; + } else if ((-d.dp) >= (len(powtab): i32)) { + n = (maxshift: int); + } else { + n = (powtab[-d.dp]: int); + }; + decimal_shift(d, n); + e -= n; + }; + e -= 1; + if (e <= -f.expbias + 1) { + let nn: int = -f.expbias - e + 1; + decimal_shift(d, -nn); + e += nn; + }; + if (e + f.expbias >= ((1u64 << f.expbits): int) - 1) { + return overflow{}; + }; + decimal_shift(d, (f.mantbits: int) + 1); + m = decimal_round(d); + if (m == (2u64 << f.mantbits)) { + m >>= 1u64; + e += 1; + if (e + f.expbias >= ((1u64 << f.expbits): int) - 1) { + return overflow{}; + }; + }; + if ((m & (1u64 << f.mantbits)) == 0u64) { + e = -f.expbias; + }; + return mkfloat(m, ((e + f.expbias): uint), d.negative, f); +}; + +// ref/hare/strconv/stof.ha:311. Assemble sign|exp|mantissa. +fn mkfloat(m: u64, e: uint, negative: bool, f: *math.floatinfo) u64 = { + let n: u64 = m & ((1u64 << f.mantbits) - 1u64); + n |= ((e: u64) & ((1u64 << f.expbits) - 1u64)) << f.mantbits; + if (negative) { + n |= 1u64 << (f.mantbits + f.expbits); + }; + return n; +}; + +// ref/hare/strconv/stof.ha:320. Exact f64 powers of ten 1e0..1e22 (all +// exactly representable; see stof64exact). +let f64pow10: [23]f64 = [ + 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, + 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, + 1.0e19, 1.0e20, 1.0e21, 1.0e22, +]; + +// ref/hare/strconv/stof.ha:326. +fn stof64exact(mant: u64, exp: i32, neg: bool) (f64 | void) = { + if (mant >> math.F64_MANTISSA_BITS != 0u64) { return; }; + let n: f64 = (mant: i64): f64; + if (neg) { + n = -n; + }; + if (exp == 0i32) { + return n; + }; + if (-22i32 <= exp && exp <= 22i32) { + if (exp >= 0i32) { + // f64 compound-assign mis-lowers in cgen — explicit + // form (strconv.ww f64tos precedent). + n = n * f64pow10[exp]; + } else { + n = n / f64pow10[-exp]; + }; + } else { + return; + }; + return n; +}; + +// ref/hare/strconv/stof.ha:345. Exact f32 powers of ten 1e0..1e10. +let f32pow10: [11]f32 = [ + 1.0e0f32, 1.0e1f32, 1.0e2f32, 1.0e3f32, 1.0e4f32, 1.0e5f32, 1.0e6f32, + 1.0e7f32, 1.0e8f32, 1.0e9f32, 1.0e10f32, +]; + +// ref/hare/strconv/stof.ha:349. +fn stof32exact(mant: u64, exp: i32, neg: bool) (f32 | void) = { + if (mant >> (math.F32_MANTISSA_BITS: u64) != 0u64) { return; }; + let n: f32 = (mant: i32): f32; + if (neg) { + n = -n; + }; + if (exp == 0i32) { + return n; + }; + if (-10i32 <= exp && exp <= 10i32) { + if (exp >= 0i32) { + // f32 compound-assign mis-lowers in cgen — explicit form. + n = n * f32pow10[exp]; + } else { + n = n / (f64pow10[-exp]: f32); + }; + } else { + return; + }; + return n; +}; + +// ref/hare/strconv/stof.ha:369. Adapted from Go's atofHex. The by-value +// `p` is mutated in Hare; ww copies its fields to scalar locals (struct +// param field-write miscompiles + diverges — filed). +fn hex_to_bits(p: fast_parsed_float, info: *math.floatinfo) (u64 | overflow) = { + let pmant: u64 = p.mantissa; + let pexp: i32 = p.exponent; + let pneg: bool = p.negative; + let ptrunc: bool = p.truncated; + let max_exp: int = ((1u64 << info.expbits): int) - info.expbias - 2; + let min_exp: int = -info.expbias + 1; + pexp += (info.mantbits: i32); + + // Shift left until a leading 1 bit followed by mantbits + 2 rounding. + for (pmant != 0u64 && pmant >> (info.mantbits + 2u64) == 0u64) { + pmant <<= 1u64; + pexp -= 1; + }; + if (ptrunc) { + pmant |= 1u64; + }; + // Too many bits: shift right (sticky-or the dropped bit). + for (pmant >> (3u64 + info.mantbits) != 0u64) { + pmant = (pmant >> 1u64) | (pmant & 1u64); + pexp += 1; + }; + // Denormalise if the exponent is small. + for (pmant > 1u64 && pexp < (min_exp: i32) - 2) { + pmant = (pmant >> 1u64) | (pmant & 1u64); + pexp += 1; + }; + // Round to even. + let round: u64 = pmant & 3u64; + pmant >>= 2u64; + round |= pmant & 1u64; + pexp += 2; + if (round == 3u64) { + pmant += 1u64; + if (pmant == 1u64 << (1u64 + info.mantbits)) { + pmant >>= 1u64; + pexp += 1; + }; + }; + // Denormal or zero. + if (pmant >> info.mantbits == 0u64) { + pexp = (-info.expbias): i32; + }; + if (pexp > (max_exp: i32)) { + return overflow{}; + }; + let bits: u64 = pmant & info.mantmask; + bits |= (((pexp + (info.expbias: i32)): u64) & info.expmask) << info.mantbits; + if (pneg) { + bits |= 1u64 << (info.mantbits + info.expbits); + }; + return bits; +}; + +// ref/hare/strconv/stof.ha:425. "nan"/"infinity"/±"infinity", +// case-insensitive. ww math has no f32 NAN/INF consts → f32frombits of +// the IEEE-754 f32 bit patterns (qNaN 0x7FC00000, ±Inf 0x7F800000 / +// 0xFF800000). +fn special(s: str) (f32 | void) = { + if (ascii.strcasecmp(s, "nan") == 0) { + return math.f32frombits(0x7FC00000u32); + } else if (ascii.strcasecmp(s, "infinity") == 0) { + return math.f32frombits(0x7F800000u32); + } else if (ascii.strcasecmp(s, "+infinity") == 0) { + return math.f32frombits(0x7F800000u32); + } else if (ascii.strcasecmp(s, "-infinity") == 0) { + return math.f32frombits(0xFF800000u32); + }; + return; +}; + +// ref/hare/strconv/stof.ha:445. Parse `s` as f64 (base DEC or HEX). See +// the module note: the EL fast path is HELD; the decimal fallback gives +// correct results meanwhile. +export fn stof64(s: str, b: base) (f64 | invalid | overflow) = { + let bb: base = b; + if (bb == base.DEFAULT) { + bb = base.DEC; + } else if (bb == base.HEX_LOWER) { + bb = base.HEX; + }; + os.assert(bb == base.DEC || bb == base.HEX, + "strconv.stof64: base must be DEC or HEX"); + + if (s.len == 0) { + return 0: invalid; + }; + + match (special(s)) { + case let f: f32 => { return (f: f64); }; + case void => void; + }; + + match (fast_parse(s, bb)) { + case let p: fast_parsed_float => { + if (bb == base.HEX) { + match (hex_to_bits(p, &math.f64info)) { + case let bits: u64 => { return math.f64frombits(bits); }; + case let eo: overflow => { return eo; }; + }; + } else if (!p.truncated) { + match (stof64exact(p.mantissa, p.exponent, p.negative)) { + case let n: f64 => { return n; }; + case void => void; + }; + match (eisel_lemire(p.mantissa, p.exponent, p.negative, + &math.f64info)) { + case let n: u64 => { return math.f64frombits(n); }; + case void => void; + }; + }; + let d = decimal { ... }; + match (decimal_parse(&d, s)) { + case let ei: invalid => { return ei; }; + case void => void; + }; + match (floatbits(&d, &math.f64info)) { + case let n: u64 => { return math.f64frombits(n); }; + case let eo: overflow => { return eo; }; + }; + }; + case let ei: invalid => { return ei; }; + }; + return 0: invalid; // unreachable (path-cov) +}; + +// ref/hare/strconv/stof.ha:491. Parse `s` as f32 (base DEC or HEX). +export fn stof32(s: str, b: base) (f32 | invalid | overflow) = { + let bb: base = b; + if (bb == base.DEFAULT) { + bb = base.DEC; + } else if (bb == base.HEX_LOWER) { + bb = base.HEX; + }; + os.assert(bb == base.DEC || bb == base.HEX, + "strconv.stof32: base must be DEC or HEX"); + + if (s.len == 0) { + return 0: invalid; + }; + + match (special(s)) { + case let f: f32 => { return f; }; + case void => void; + }; + + match (fast_parse(s, bb)) { + case let p: fast_parsed_float => { + if (bb == base.HEX) { + match (hex_to_bits(p, &math.f32info)) { + case let bits: u64 => { + return math.f32frombits(bits: u32); + }; + case let eo: overflow => { return eo; }; + }; + } else if (!p.truncated) { + match (stof32exact(p.mantissa, p.exponent, p.negative)) { + case let n: f32 => { return n; }; + case void => void; + }; + match (eisel_lemire(p.mantissa, p.exponent, p.negative, + &math.f32info)) { + case let n: u64 => { return math.f32frombits(n: u32); }; + case void => void; + }; + }; + let d = decimal { ... }; + match (decimal_parse(&d, s)) { + case let ei: invalid => { return ei; }; + case void => void; + }; + match (floatbits(&d, &math.f32info)) { + case let n: u64 => { return math.f32frombits(n: u32); }; + case let eo: overflow => { return eo; }; + }; + }; + case let ei: invalid => { return ei; }; + }; + return 0: invalid; // unreachable (path-cov) +}; + +// strconv — stof/ftos lookup tables. Mirrors ref/hare/strconv/stof_data.ha +// byte-exact. Pure-data fold (strconv #106 fold-2, was fold-3 before drew +// re-sequenced 2026-05-26): no logic, exercised transitively when fold-3's +// `leftshift_newdigits` lands (ref/hare/strconv/decimal.ha:35). +// +// ww uses module-level `let` for compile-time array data (ref/hare/strconv +// `const` has no ww keyword equivalent; lib/encoding/utf8/utf8.ww:48 sets +// the precedent with [2048]i8 dfa). Literal suffixes (`u16`, `u8`) are +// required because cstage rejects bare integer literals in `[N]u8`/`[N]u16` +// init while wwstage accepts them; the suffixed form is the only shape +// both stages agree on (candidate #130). +// +// `powers_of_ten: [596][2]u64` (ref/hare/strconv/stof_data.ha:73) is the +// Eisel-Lemire fast-path table (consumed by stof.ww's eisel_lemire); it +// lands here in fold-4 alongside its consumer, indexed `[exp10 + 307]` +// for exp10 in [-307, 288]. Faithful 2D `[596][2]u64` (the {hi,lo} pair +// IS the 128-bit truncated power-of-ten; rule-12, not flattened) — the +// 2D module-level static-init + double-index read it needs landed in +// #156 (cbeffea). See the table at the foot of this file. + +package strconv; + +// ref/hare/strconv/stof_data.ha:4. Powers-of-five decimal-expansion +// metadata for `leftshift_newdigits` (decimal.ha:35). The top 5 bits +// of each entry are the new-digit-count `nn`; the low 11 bits index +// `pow5_table` for the digits themselves. +let left_shift_table: [65]u16 = [ + 0x0000u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16, 0x1817u16, + 0x181Du16, 0x2024u16, 0x202Bu16, 0x2033u16, 0x203Cu16, 0x2846u16, 0x2850u16, 0x285Bu16, 0x3067u16, + 0x3073u16, 0x3080u16, 0x388Eu16, 0x389Cu16, 0x38ABu16, 0x38BBu16, 0x40CCu16, 0x40DDu16, 0x40EFu16, + 0x4902u16, 0x4915u16, 0x4929u16, 0x513Eu16, 0x5153u16, 0x5169u16, 0x5180u16, 0x5998u16, 0x59B0u16, + 0x59C9u16, 0x61E3u16, 0x61FDu16, 0x6218u16, 0x6A34u16, 0x6A50u16, 0x6A6Du16, 0x6A8Bu16, 0x72AAu16, + 0x72C9u16, 0x72E9u16, 0x7B0Au16, 0x7B2Bu16, 0x7B4Du16, 0x8370u16, 0x8393u16, 0x83B7u16, 0x83DCu16, + 0x8C02u16, 0x8C28u16, 0x8C4Fu16, 0x9477u16, 0x949Fu16, 0x94C8u16, 0x9CF2u16, 0x051Cu16, 0x051Cu16, + 0x051Cu16, 0x051Cu16, +]; + +// ref/hare/strconv/stof_data.ha:15. Decimal digits of 5^k for k=1..60, +// concatenated. Indexed via `left_shift_table` (above); each shift k +// reads its `pow5_b - pow5_a` digits starting at `pow5_a`. +let pow5_table: [0x051C]u8 = [ + 5u8, 2u8, 5u8, 1u8, 2u8, 5u8, 6u8, 2u8, 5u8, 3u8, 1u8, 2u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 3u8, + 9u8, 0u8, 6u8, 2u8, 5u8, 1u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 9u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, 4u8, 8u8, 8u8, 2u8, 8u8, + 1u8, 2u8, 5u8, 2u8, 4u8, 4u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 1u8, 2u8, 2u8, 0u8, 7u8, 0u8, 3u8, 1u8, 2u8, 5u8, 6u8, 1u8, + 0u8, 3u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 3u8, 0u8, 5u8, 1u8, 7u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 1u8, 5u8, 2u8, 5u8, 8u8, + 7u8, 8u8, 9u8, 0u8, 6u8, 2u8, 5u8, 7u8, 6u8, 2u8, 9u8, 3u8, 9u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, 3u8, 8u8, 1u8, 4u8, 6u8, + 9u8, 7u8, 2u8, 6u8, 5u8, 6u8, 2u8, 5u8, 1u8, 9u8, 0u8, 7u8, 3u8, 4u8, 8u8, 6u8, 3u8, 2u8, 8u8, 1u8, 2u8, 5u8, 9u8, 5u8, + 3u8, 6u8, 7u8, 4u8, 3u8, 1u8, 6u8, 4u8, 0u8, 6u8, 2u8, 5u8, 4u8, 7u8, 6u8, 8u8, 3u8, 7u8, 1u8, 5u8, 8u8, 2u8, 0u8, 3u8, + 1u8, 2u8, 5u8, 2u8, 3u8, 8u8, 4u8, 1u8, 8u8, 5u8, 7u8, 9u8, 1u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 1u8, 1u8, 9u8, 2u8, 0u8, + 9u8, 2u8, 8u8, 9u8, 5u8, 5u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 5u8, 9u8, 6u8, 0u8, 4u8, 6u8, 4u8, 4u8, 7u8, 7u8, 5u8, 3u8, + 9u8, 0u8, 6u8, 2u8, 5u8, 2u8, 9u8, 8u8, 0u8, 2u8, 3u8, 2u8, 2u8, 3u8, 8u8, 7u8, 6u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 1u8, + 4u8, 9u8, 0u8, 1u8, 1u8, 6u8, 1u8, 1u8, 9u8, 3u8, 8u8, 4u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, 7u8, 4u8, 5u8, 0u8, 5u8, 8u8, + 0u8, 5u8, 9u8, 6u8, 9u8, 2u8, 3u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 3u8, 7u8, 2u8, 5u8, 2u8, 9u8, 0u8, 2u8, 9u8, 8u8, 4u8, + 6u8, 1u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 1u8, 8u8, 6u8, 2u8, 6u8, 4u8, 5u8, 1u8, 4u8, 9u8, 2u8, 3u8, 0u8, 9u8, 5u8, + 7u8, 0u8, 3u8, 1u8, 2u8, 5u8, 9u8, 3u8, 1u8, 3u8, 2u8, 2u8, 5u8, 7u8, 4u8, 6u8, 1u8, 5u8, 4u8, 7u8, 8u8, 5u8, 1u8, 5u8, + 6u8, 2u8, 5u8, 4u8, 6u8, 5u8, 6u8, 6u8, 1u8, 2u8, 8u8, 7u8, 3u8, 0u8, 7u8, 7u8, 3u8, 9u8, 2u8, 5u8, 7u8, 8u8, 1u8, 2u8, + 5u8, 2u8, 3u8, 2u8, 8u8, 3u8, 0u8, 6u8, 4u8, 3u8, 6u8, 5u8, 3u8, 8u8, 6u8, 9u8, 6u8, 2u8, 8u8, 9u8, 0u8, 6u8, 2u8, 5u8, + 1u8, 1u8, 6u8, 4u8, 1u8, 5u8, 3u8, 2u8, 1u8, 8u8, 2u8, 6u8, 9u8, 3u8, 4u8, 8u8, 1u8, 4u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, + 5u8, 8u8, 2u8, 0u8, 7u8, 6u8, 6u8, 0u8, 9u8, 1u8, 3u8, 4u8, 6u8, 7u8, 4u8, 0u8, 7u8, 2u8, 2u8, 6u8, 5u8, 6u8, 2u8, 5u8, + 2u8, 9u8, 1u8, 0u8, 3u8, 8u8, 3u8, 0u8, 4u8, 5u8, 6u8, 7u8, 3u8, 3u8, 7u8, 0u8, 3u8, 6u8, 1u8, 3u8, 2u8, 8u8, 1u8, 2u8, + 5u8, 1u8, 4u8, 5u8, 5u8, 1u8, 9u8, 1u8, 5u8, 2u8, 2u8, 8u8, 3u8, 6u8, 6u8, 8u8, 5u8, 1u8, 8u8, 0u8, 6u8, 6u8, 4u8, 0u8, + 6u8, 2u8, 5u8, 7u8, 2u8, 7u8, 5u8, 9u8, 5u8, 7u8, 6u8, 1u8, 4u8, 1u8, 8u8, 3u8, 4u8, 2u8, 5u8, 9u8, 0u8, 3u8, 3u8, 2u8, + 0u8, 3u8, 1u8, 2u8, 5u8, 3u8, 6u8, 3u8, 7u8, 9u8, 7u8, 8u8, 8u8, 0u8, 7u8, 0u8, 9u8, 1u8, 7u8, 1u8, 2u8, 9u8, 5u8, 1u8, + 6u8, 6u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 1u8, 8u8, 1u8, 8u8, 9u8, 8u8, 9u8, 4u8, 0u8, 3u8, 5u8, 4u8, 5u8, 8u8, 5u8, 6u8, + 4u8, 7u8, 5u8, 8u8, 3u8, 0u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 9u8, 0u8, 9u8, 4u8, 9u8, 4u8, 7u8, 0u8, 1u8, 7u8, 7u8, 2u8, + 9u8, 2u8, 8u8, 2u8, 3u8, 7u8, 9u8, 1u8, 5u8, 0u8, 3u8, 9u8, 0u8, 6u8, 2u8, 5u8, 4u8, 5u8, 4u8, 7u8, 4u8, 7u8, 3u8, 5u8, + 0u8, 8u8, 8u8, 6u8, 4u8, 6u8, 4u8, 1u8, 1u8, 8u8, 9u8, 5u8, 7u8, 5u8, 1u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 2u8, 2u8, 7u8, + 3u8, 7u8, 3u8, 6u8, 7u8, 5u8, 4u8, 4u8, 3u8, 2u8, 3u8, 2u8, 0u8, 5u8, 9u8, 4u8, 7u8, 8u8, 7u8, 5u8, 9u8, 7u8, 6u8, 5u8, + 6u8, 2u8, 5u8, 1u8, 1u8, 3u8, 6u8, 8u8, 6u8, 8u8, 3u8, 7u8, 7u8, 2u8, 1u8, 6u8, 1u8, 6u8, 0u8, 2u8, 9u8, 7u8, 3u8, 9u8, + 3u8, 7u8, 9u8, 8u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 5u8, 6u8, 8u8, 4u8, 3u8, 4u8, 1u8, 8u8, 8u8, 6u8, 0u8, 8u8, 0u8, 8u8, + 0u8, 1u8, 4u8, 8u8, 6u8, 9u8, 6u8, 8u8, 9u8, 9u8, 4u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 2u8, 8u8, 4u8, 2u8, 1u8, 7u8, 0u8, + 9u8, 4u8, 3u8, 0u8, 4u8, 0u8, 4u8, 0u8, 0u8, 7u8, 4u8, 3u8, 4u8, 8u8, 4u8, 4u8, 9u8, 7u8, 0u8, 7u8, 0u8, 3u8, 1u8, 2u8, + 5u8, 1u8, 4u8, 2u8, 1u8, 0u8, 8u8, 5u8, 4u8, 7u8, 1u8, 5u8, 2u8, 0u8, 2u8, 0u8, 0u8, 3u8, 7u8, 1u8, 7u8, 4u8, 2u8, 2u8, + 4u8, 8u8, 5u8, 3u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 7u8, 1u8, 0u8, 5u8, 4u8, 2u8, 7u8, 3u8, 5u8, 7u8, 6u8, 0u8, 1u8, 0u8, + 0u8, 1u8, 8u8, 5u8, 8u8, 7u8, 1u8, 1u8, 2u8, 4u8, 2u8, 6u8, 7u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 3u8, 5u8, 5u8, 2u8, 7u8, + 1u8, 3u8, 6u8, 7u8, 8u8, 8u8, 0u8, 0u8, 5u8, 0u8, 0u8, 9u8, 2u8, 9u8, 3u8, 5u8, 5u8, 6u8, 2u8, 1u8, 3u8, 3u8, 7u8, 8u8, + 9u8, 0u8, 6u8, 2u8, 5u8, 1u8, 7u8, 7u8, 6u8, 3u8, 5u8, 6u8, 8u8, 3u8, 9u8, 4u8, 0u8, 0u8, 2u8, 5u8, 0u8, 4u8, 6u8, 4u8, + 6u8, 7u8, 7u8, 8u8, 1u8, 0u8, 6u8, 6u8, 8u8, 9u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, 8u8, 8u8, 8u8, 1u8, 7u8, 8u8, 4u8, 1u8, + 9u8, 7u8, 0u8, 0u8, 1u8, 2u8, 5u8, 2u8, 3u8, 2u8, 3u8, 3u8, 8u8, 9u8, 0u8, 5u8, 3u8, 3u8, 4u8, 4u8, 7u8, 2u8, 6u8, 5u8, + 6u8, 2u8, 5u8, 4u8, 4u8, 4u8, 0u8, 8u8, 9u8, 2u8, 0u8, 9u8, 8u8, 5u8, 0u8, 0u8, 6u8, 2u8, 6u8, 1u8, 6u8, 1u8, 6u8, 9u8, + 4u8, 5u8, 2u8, 6u8, 6u8, 7u8, 2u8, 3u8, 6u8, 3u8, 2u8, 8u8, 1u8, 2u8, 5u8, 2u8, 2u8, 2u8, 0u8, 4u8, 4u8, 6u8, 0u8, 4u8, + 9u8, 2u8, 5u8, 0u8, 3u8, 1u8, 3u8, 0u8, 8u8, 0u8, 8u8, 4u8, 7u8, 2u8, 6u8, 3u8, 3u8, 3u8, 6u8, 1u8, 8u8, 1u8, 6u8, 4u8, + 0u8, 6u8, 2u8, 5u8, 1u8, 1u8, 1u8, 0u8, 2u8, 2u8, 3u8, 0u8, 2u8, 4u8, 6u8, 2u8, 5u8, 1u8, 5u8, 6u8, 5u8, 4u8, 0u8, 4u8, + 2u8, 3u8, 6u8, 3u8, 1u8, 6u8, 6u8, 8u8, 0u8, 9u8, 0u8, 8u8, 2u8, 0u8, 3u8, 1u8, 2u8, 5u8, 5u8, 5u8, 5u8, 1u8, 1u8, 1u8, + 5u8, 1u8, 2u8, 3u8, 1u8, 2u8, 5u8, 7u8, 8u8, 2u8, 7u8, 0u8, 2u8, 1u8, 1u8, 8u8, 1u8, 5u8, 8u8, 3u8, 4u8, 0u8, 4u8, 5u8, + 4u8, 1u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 2u8, 7u8, 7u8, 5u8, 5u8, 5u8, 7u8, 5u8, 6u8, 1u8, 5u8, 6u8, 2u8, 8u8, 9u8, 1u8, + 3u8, 5u8, 1u8, 0u8, 5u8, 9u8, 0u8, 7u8, 9u8, 1u8, 7u8, 0u8, 2u8, 2u8, 7u8, 0u8, 5u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 1u8, + 3u8, 8u8, 7u8, 7u8, 7u8, 8u8, 7u8, 8u8, 0u8, 7u8, 8u8, 1u8, 4u8, 4u8, 5u8, 6u8, 7u8, 5u8, 5u8, 2u8, 9u8, 5u8, 3u8, 9u8, + 5u8, 8u8, 5u8, 1u8, 1u8, 3u8, 5u8, 2u8, 5u8, 3u8, 9u8, 0u8, 6u8, 2u8, 5u8, 6u8, 9u8, 3u8, 8u8, 8u8, 9u8, 3u8, 9u8, 0u8, + 3u8, 9u8, 0u8, 7u8, 2u8, 2u8, 8u8, 3u8, 7u8, 7u8, 6u8, 4u8, 7u8, 6u8, 9u8, 7u8, 9u8, 2u8, 5u8, 5u8, 6u8, 7u8, 6u8, 2u8, + 6u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 3u8, 4u8, 6u8, 9u8, 4u8, 4u8, 6u8, 9u8, 5u8, 1u8, 9u8, 5u8, 3u8, 6u8, 1u8, 4u8, 1u8, + 8u8, 8u8, 8u8, 2u8, 3u8, 8u8, 4u8, 8u8, 9u8, 6u8, 2u8, 7u8, 8u8, 3u8, 8u8, 1u8, 3u8, 4u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, + 1u8, 7u8, 3u8, 4u8, 7u8, 2u8, 3u8, 4u8, 7u8, 5u8, 9u8, 7u8, 6u8, 8u8, 0u8, 7u8, 0u8, 9u8, 4u8, 4u8, 1u8, 1u8, 9u8, 2u8, + 4u8, 4u8, 8u8, 1u8, 3u8, 9u8, 1u8, 9u8, 0u8, 6u8, 7u8, 3u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 8u8, 6u8, 7u8, 3u8, 6u8, 1u8, + 7u8, 3u8, 7u8, 9u8, 8u8, 8u8, 4u8, 0u8, 3u8, 5u8, 4u8, 7u8, 2u8, 0u8, 5u8, 9u8, 6u8, 2u8, 2u8, 4u8, 0u8, 6u8, 9u8, 5u8, + 9u8, 5u8, 3u8, 3u8, 6u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, +]; + +// ref/hare/strconv/stof_data.ha:73. Eisel-Lemire 128-bit power-of-ten +// table (see header note). 596 rows, {hi, lo} u64 pair per row. +let powers_of_ten: [596][2]u64 = [ + [0xA5D3B6D479F8E056u64, 0x8FD0C16206306BABu64], + [0x8F48A4899877186Cu64, 0xB3C4F1BA87BC8696u64], + [0x331ACDABFE94DE87u64, 0xE0B62E2929ABA83Cu64], + [0x9FF0C08B7F1D0B14u64, 0x8C71DCD9BA0B4925u64], + [0x07ECF0AE5EE44DD9u64, 0xAF8E5410288E1B6Fu64], + [0xC9E82CD9F69D6150u64, 0xDB71E91432B1A24Au64], + [0xBE311C083A225CD2u64, 0x892731AC9FAF056Eu64], + [0x6DBD630A48AAF406u64, 0xAB70FE17C79AC6CAu64], + [0x092CBBCCDAD5B108u64, 0xD64D3D9DB981787Du64], + [0x25BBF56008C58EA5u64, 0x85F0468293F0EB4Eu64], + [0xAF2AF2B80AF6F24Eu64, 0xA76C582338ED2621u64], + [0x1AF5AF660DB4AEE1u64, 0xD1476E2C07286FAAu64], + [0x50D98D9FC890ED4Du64, 0x82CCA4DB847945CAu64], + [0xE50FF107BAB528A0u64, 0xA37FCE126597973Cu64], + [0x1E53ED49A96272C8u64, 0xCC5FC196FEFD7D0Cu64], + [0x25E8E89C13BB0F7Au64, 0xFF77B1FCBEBCDC4Fu64], + [0x77B191618C54E9ACu64, 0x9FAACF3DF73609B1u64], + [0xD59DF5B9EF6A2417u64, 0xC795830D75038C1Du64], + [0x4B0573286B44AD1Du64, 0xF97AE3D0D2446F25u64], + [0x4EE367F9430AEC32u64, 0x9BECCE62836AC577u64], + [0x229C41F793CDA73Fu64, 0xC2E801FB244576D5u64], + [0x6B43527578C1110Fu64, 0xF3A20279ED56D48Au64], + [0x830A13896B78AAA9u64, 0x9845418C345644D6u64], + [0x23CC986BC656D553u64, 0xBE5691EF416BD60Cu64], + [0x2CBFBE86B7EC8AA8u64, 0xEDEC366B11C6CB8Fu64], + [0x7BF7D71432F3D6A9u64, 0x94B3A202EB1C3F39u64], + [0xDAF5CCD93FB0CC53u64, 0xB9E08A83A5E34F07u64], + [0xD1B3400F8F9CFF68u64, 0xE858AD248F5C22C9u64], + [0x23100809B9C21FA1u64, 0x91376C36D99995BEu64], + [0xABD40A0C2832A78Au64, 0xB58547448FFFFB2Du64], + [0x16C90C8F323F516Cu64, 0xE2E69915B3FFF9F9u64], + [0xAE3DA7D97F6792E3u64, 0x8DD01FAD907FFC3Bu64], + [0x99CD11CFDF41779Cu64, 0xB1442798F49FFB4Au64], + [0x40405643D711D583u64, 0xDD95317F31C7FA1Du64], + [0x482835EA666B2572u64, 0x8A7D3EEF7F1CFC52u64], + [0xDA3243650005EECFu64, 0xAD1C8EAB5EE43B66u64], + [0x90BED43E40076A82u64, 0xD863B256369D4A40u64], + [0x5A7744A6E804A291u64, 0x873E4F75E2224E68u64], + [0x711515D0A205CB36u64, 0xA90DE3535AAAE202u64], + [0x0D5A5B44CA873E03u64, 0xD3515C2831559A83u64], + [0xE858790AFE9486C2u64, 0x8412D9991ED58091u64], + [0x626E974DBE39A872u64, 0xA5178FFF668AE0B6u64], + [0xFB0A3D212DC8128Fu64, 0xCE5D73FF402D98E3u64], + [0x7CE66634BC9D0B99u64, 0x80FA687F881C7F8Eu64], + [0x1C1FFFC1EBC44E80u64, 0xA139029F6A239F72u64], + [0xA327FFB266B56220u64, 0xC987434744AC874Eu64], + [0x4BF1FF9F0062BAA8u64, 0xFBE9141915D7A922u64], + [0x6F773FC3603DB4A9u64, 0x9D71AC8FADA6C9B5u64], + [0xCB550FB4384D21D3u64, 0xC4CE17B399107C22u64], + [0x7E2A53A146606A48u64, 0xF6019DA07F549B2Bu64], + [0x2EDA7444CBFC426Du64, 0x99C102844F94E0FBu64], + [0xFA911155FEFB5308u64, 0xC0314325637A1939u64], + [0x793555AB7EBA27CAu64, 0xF03D93EEBC589F88u64], + [0x4BC1558B2F3458DEu64, 0x96267C7535B763B5u64], + [0x9EB1AAEDFB016F16u64, 0xBBB01B9283253CA2u64], + [0x465E15A979C1CADCu64, 0xEA9C227723EE8BCBu64], + [0x0BFACD89EC191EC9u64, 0x92A1958A7675175Fu64], + [0xCEF980EC671F667Bu64, 0xB749FAED14125D36u64], + [0x82B7E12780E7401Au64, 0xE51C79A85916F484u64], + [0xD1B2ECB8B0908810u64, 0x8F31CC0937AE58D2u64], + [0x861FA7E6DCB4AA15u64, 0xB2FE3F0B8599EF07u64], + [0x67A791E093E1D49Au64, 0xDFBDCECE67006AC9u64], + [0xE0C8BB2C5C6D24E0u64, 0x8BD6A141006042BDu64], + [0x58FAE9F773886E18u64, 0xAECC49914078536Du64], + [0xAF39A475506A899Eu64, 0xDA7F5BF590966848u64], + [0x6D8406C952429603u64, 0x888F99797A5E012Du64], + [0xC8E5087BA6D33B83u64, 0xAAB37FD7D8F58178u64], + [0xFB1E4A9A90880A64u64, 0xD5605FCDCF32E1D6u64], + [0x5CF2EEA09A55067Fu64, 0x855C3BE0A17FCD26u64], + [0xF42FAA48C0EA481Eu64, 0xA6B34AD8C9DFC06Fu64], + [0xF13B94DAF124DA26u64, 0xD0601D8EFC57B08Bu64], + [0x76C53D08D6B70858u64, 0x823C12795DB6CE57u64], + [0x54768C4B0C64CA6Eu64, 0xA2CB1717B52481EDu64], + [0xA9942F5DCF7DFD09u64, 0xCB7DDCDDA26DA268u64], + [0xD3F93B35435D7C4Cu64, 0xFE5D54150B090B02u64], + [0xC47BC5014A1A6DAFu64, 0x9EFA548D26E5A6E1u64], + [0x359AB6419CA1091Bu64, 0xC6B8E9B0709F109Au64], + [0xC30163D203C94B62u64, 0xF867241C8CC6D4C0u64], + [0x79E0DE63425DCF1Du64, 0x9B407691D7FC44F8u64], + [0x985915FC12F542E4u64, 0xC21094364DFB5636u64], + [0x3E6F5B7B17B2939Du64, 0xF294B943E17A2BC4u64], + [0xA705992CEECF9C42u64, 0x979CF3CA6CEC5B5Au64], + [0x50C6FF782A838353u64, 0xBD8430BD08277231u64], + [0xA4F8BF5635246428u64, 0xECE53CEC4A314EBDu64], + [0x871B7795E136BE99u64, 0x940F4613AE5ED136u64], + [0x28E2557B59846E3Fu64, 0xB913179899F68584u64], + [0x331AEADA2FE589CFu64, 0xE757DD7EC07426E5u64], + [0x3FF0D2C85DEF7621u64, 0x9096EA6F3848984Fu64], + [0x0FED077A756B53A9u64, 0xB4BCA50B065ABE63u64], + [0xD3E8495912C62894u64, 0xE1EBCE4DC7F16DFBu64], + [0x64712DD7ABBBD95Cu64, 0x8D3360F09CF6E4BDu64], + [0xBD8D794D96AACFB3u64, 0xB080392CC4349DECu64], + [0xECF0D7A0FC5583A0u64, 0xDCA04777F541C567u64], + [0xF41686C49DB57244u64, 0x89E42CAAF9491B60u64], + [0x311C2875C522CED5u64, 0xAC5D37D5B79B6239u64], + [0x7D633293366B828Bu64, 0xD77485CB25823AC7u64], + [0xAE5DFF9C02033197u64, 0x86A8D39EF77164BCu64], + [0xD9F57F830283FDFCu64, 0xA8530886B54DBDEBu64], + [0xD072DF63C324FD7Bu64, 0xD267CAA862A12D66u64], + [0x4247CB9E59F71E6Du64, 0x8380DEA93DA4BC60u64], + [0x52D9BE85F074E608u64, 0xA46116538D0DEB78u64], + [0x67902E276C921F8Bu64, 0xCD795BE870516656u64], + [0x00BA1CD8A3DB53B6u64, 0x806BD9714632DFF6u64], + [0x80E8A40ECCD228A4u64, 0xA086CFCD97BF97F3u64], + [0x6122CD128006B2CDu64, 0xC8A883C0FDAF7DF0u64], + [0x796B805720085F81u64, 0xFAD2A4B13D1B5D6Cu64], + [0xCBE3303674053BB0u64, 0x9CC3A6EEC6311A63u64], + [0xBEDBFC4411068A9Cu64, 0xC3F490AA77BD60FCu64], + [0xEE92FB5515482D44u64, 0xF4F1B4D515ACB93Bu64], + [0x751BDD152D4D1C4Au64, 0x991711052D8BF3C5u64], + [0xD262D45A78A0635Du64, 0xBF5CD54678EEF0B6u64], + [0x86FB897116C87C34u64, 0xEF340A98172AACE4u64], + [0xD45D35E6AE3D4DA0u64, 0x9580869F0E7AAC0Eu64], + [0x8974836059CCA109u64, 0xBAE0A846D2195712u64], + [0x2BD1A438703FC94Bu64, 0xE998D258869FACD7u64], + [0x7B6306A34627DDCFu64, 0x91FF83775423CC06u64], + [0x1A3BC84C17B1D542u64, 0xB67F6455292CBF08u64], + [0x20CABA5F1D9E4A93u64, 0xE41F3D6A7377EECAu64], + [0x547EB47B7282EE9Cu64, 0x8E938662882AF53Eu64], + [0xE99E619A4F23AA43u64, 0xB23867FB2A35B28Du64], + [0x6405FA00E2EC94D4u64, 0xDEC681F9F4C31F31u64], + [0xDE83BC408DD3DD04u64, 0x8B3C113C38F9F37Eu64], + [0x9624AB50B148D445u64, 0xAE0B158B4738705Eu64], + [0x3BADD624DD9B0957u64, 0xD98DDAEE19068C76u64], + [0xE54CA5D70A80E5D6u64, 0x87F8A8D4CFA417C9u64], + [0x5E9FCF4CCD211F4Cu64, 0xA9F6D30A038D1DBCu64], + [0x7647C3200069671Fu64, 0xD47487CC8470652Bu64], + [0x29ECD9F40041E073u64, 0x84C8D4DFD2C63F3Bu64], + [0xF468107100525890u64, 0xA5FB0A17C777CF09u64], + [0x7182148D4066EEB4u64, 0xCF79CC9DB955C2CCu64], + [0xC6F14CD848405530u64, 0x81AC1FE293D599BFu64], + [0xB8ADA00E5A506A7Cu64, 0xA21727DB38CB002Fu64], + [0xA6D90811F0E4851Cu64, 0xCA9CF1D206FDC03Bu64], + [0x908F4A166D1DA663u64, 0xFD442E4688BD304Au64], + [0x9A598E4E043287FEu64, 0x9E4A9CEC15763E2Eu64], + [0x40EFF1E1853F29FDu64, 0xC5DD44271AD3CDBAu64], + [0xD12BEE59E68EF47Cu64, 0xF7549530E188C128u64], + [0x82BB74F8301958CEu64, 0x9A94DD3E8CF578B9u64], + [0xE36A52363C1FAF01u64, 0xC13A148E3032D6E7u64], + [0xDC44E6C3CB279AC1u64, 0xF18899B1BC3F8CA1u64], + [0x29AB103A5EF8C0B9u64, 0x96F5600F15A7B7E5u64], + [0x7415D448F6B6F0E7u64, 0xBCB2B812DB11A5DEu64], + [0x111B495B3464AD21u64, 0xEBDF661791D60F56u64], + [0xCAB10DD900BEEC34u64, 0x936B9FCEBB25C995u64], + [0x3D5D514F40EEA742u64, 0xB84687C269EF3BFBu64], + [0x0CB4A5A3112A5112u64, 0xE65829B3046B0AFAu64], + [0x47F0E785EABA72ABu64, 0x8FF71A0FE2C2E6DCu64], + [0x59ED216765690F56u64, 0xB3F4E093DB73A093u64], + [0x306869C13EC3532Cu64, 0xE0F218B8D25088B8u64], + [0x1E414218C73A13FBu64, 0x8C974F7383725573u64], + [0xE5D1929EF90898FAu64, 0xAFBD2350644EEACFu64], + [0xDF45F746B74ABF39u64, 0xDBAC6C247D62A583u64], + [0x6B8BBA8C328EB783u64, 0x894BC396CE5DA772u64], + [0x066EA92F3F326564u64, 0xAB9EB47C81F5114Fu64], + [0xC80A537B0EFEFEBDu64, 0xD686619BA27255A2u64], + [0xBD06742CE95F5F36u64, 0x8613FD0145877585u64], + [0x2C48113823B73704u64, 0xA798FC4196E952E7u64], + [0xF75A15862CA504C5u64, 0xD17F3B51FCA3A7A0u64], + [0x9A984D73DBE722FBu64, 0x82EF85133DE648C4u64], + [0xC13E60D0D2E0EBBAu64, 0xA3AB66580D5FDAF5u64], + [0x318DF905079926A8u64, 0xCC963FEE10B7D1B3u64], + [0xFDF17746497F7052u64, 0xFFBBCFE994E5C61Fu64], + [0xFEB6EA8BEDEFA633u64, 0x9FD561F1FD0F9BD3u64], + [0xFE64A52EE96B8FC0u64, 0xC7CABA6E7C5382C8u64], + [0x3DFDCE7AA3C673B0u64, 0xF9BD690A1B68637Bu64], + [0x06BEA10CA65C084Eu64, 0x9C1661A651213E2Du64], + [0x486E494FCFF30A62u64, 0xC31BFA0FE5698DB8u64], + [0x5A89DBA3C3EFCCFAu64, 0xF3E2F893DEC3F126u64], + [0xF89629465A75E01Cu64, 0x986DDB5C6B3A76B7u64], + [0xF6BBB397F1135823u64, 0xBE89523386091465u64], + [0x746AA07DED582E2Cu64, 0xEE2BA6C0678B597Fu64], + [0xA8C2A44EB4571CDCu64, 0x94DB483840B717EFu64], + [0x92F34D62616CE413u64, 0xBA121A4650E4DDEBu64], + [0x77B020BAF9C81D17u64, 0xE896A0D7E51E1566u64], + [0x0ACE1474DC1D122Eu64, 0x915E2486EF32CD60u64], + [0x0D819992132456BAu64, 0xB5B5ADA8AAFF80B8u64], + [0x10E1FFF697ED6C69u64, 0xE3231912D5BF60E6u64], + [0xCA8D3FFA1EF463C1u64, 0x8DF5EFABC5979C8Fu64], + [0xBD308FF8A6B17CB2u64, 0xB1736B96B6FD83B3u64], + [0xAC7CB3F6D05DDBDEu64, 0xDDD0467C64BCE4A0u64], + [0x6BCDF07A423AA96Bu64, 0x8AA22C0DBEF60EE4u64], + [0x86C16C98D2C953C6u64, 0xAD4AB7112EB3929Du64], + [0xE871C7BF077BA8B7u64, 0xD89D64D57A607744u64], + [0x11471CD764AD4972u64, 0x87625F056C7C4A8Bu64], + [0xD598E40D3DD89BCFu64, 0xA93AF6C6C79B5D2Du64], + [0x4AFF1D108D4EC2C3u64, 0xD389B47879823479u64], + [0xCEDF722A585139BAu64, 0x843610CB4BF160CBu64], + [0xC2974EB4EE658828u64, 0xA54394FE1EEDB8FEu64], + [0x733D226229FEEA32u64, 0xCE947A3DA6A9273Eu64], + [0x0806357D5A3F525Fu64, 0x811CCC668829B887u64], + [0xCA07C2DCB0CF26F7u64, 0xA163FF802A3426A8u64], + [0xFC89B393DD02F0B5u64, 0xC9BCFF6034C13052u64], + [0xBBAC2078D443ACE2u64, 0xFC2C3F3841F17C67u64], + [0xD54B944B84AA4C0Du64, 0x9D9BA7832936EDC0u64], + [0x0A9E795E65D4DF11u64, 0xC5029163F384A931u64], + [0x4D4617B5FF4A16D5u64, 0xF64335BCF065D37Du64], + [0x504BCED1BF8E4E45u64, 0x99EA0196163FA42Eu64], + [0xE45EC2862F71E1D6u64, 0xC06481FB9BCF8D39u64], + [0x5D767327BB4E5A4Cu64, 0xF07DA27A82C37088u64], + [0x3A6A07F8D510F86Fu64, 0x964E858C91BA2655u64], + [0x890489F70A55368Bu64, 0xBBE226EFB628AFEAu64], + [0x2B45AC74CCEA842Eu64, 0xEADAB0ABA3B2DBE5u64], + [0x3B0B8BC90012929Du64, 0x92C8AE6B464FC96Fu64], + [0x09CE6EBB40173744u64, 0xB77ADA0617E3BBCBu64], + [0xCC420A6A101D0515u64, 0xE55990879DDCAABDu64], + [0x9FA946824A12232Du64, 0x8F57FA54C2A9EAB6u64], + [0x47939822DC96ABF9u64, 0xB32DF8E9F3546564u64], + [0x59787E2B93BC56F7u64, 0xDFF9772470297EBDu64], + [0x57EB4EDB3C55B65Au64, 0x8BFBEA76C619EF36u64], + [0xEDE622920B6B23F1u64, 0xAEFAE51477A06B03u64], + [0xE95FAB368E45ECEDu64, 0xDAB99E59958885C4u64], + [0x11DBCB0218EBB414u64, 0x88B402F7FD75539Bu64], + [0xD652BDC29F26A119u64, 0xAAE103B5FCD2A881u64], + [0x4BE76D3346F0495Fu64, 0xD59944A37C0752A2u64], + [0x6F70A4400C562DDBu64, 0x857FCAE62D8493A5u64], + [0xCB4CCD500F6BB952u64, 0xA6DFBD9FB8E5B88Eu64], + [0x7E2000A41346A7A7u64, 0xD097AD07A71F26B2u64], + [0x8ED400668C0C28C8u64, 0x825ECC24C873782Fu64], + [0x728900802F0F32FAu64, 0xA2F67F2DFA90563Bu64], + [0x4F2B40A03AD2FFB9u64, 0xCBB41EF979346BCAu64], + [0xE2F610C84987BFA8u64, 0xFEA126B7D78186BCu64], + [0x0DD9CA7D2DF4D7C9u64, 0x9F24B832E6B0F436u64], + [0x91503D1C79720DBBu64, 0xC6EDE63FA05D3143u64], + [0x75A44C6397CE912Au64, 0xF8A95FCF88747D94u64], + [0xC986AFBE3EE11ABAu64, 0x9B69DBE1B548CE7Cu64], + [0xFBE85BADCE996168u64, 0xC24452DA229B021Bu64], + [0xFAE27299423FB9C3u64, 0xF2D56790AB41C2A2u64], + [0xDCCD879FC967D41Au64, 0x97C560BA6B0919A5u64], + [0x5400E987BBC1C920u64, 0xBDB6B8E905CB600Fu64], + [0x290123E9AAB23B68u64, 0xED246723473E3813u64], + [0xF9A0B6720AAF6521u64, 0x9436C0760C86E30Bu64], + [0xF808E40E8D5B3E69u64, 0xB94470938FA89BCEu64], + [0xB60B1D1230B20E04u64, 0xE7958CB87392C2C2u64], + [0xB1C6F22B5E6F48C2u64, 0x90BD77F3483BB9B9u64], + [0x1E38AEB6360B1AF3u64, 0xB4ECD5F01A4AA828u64], + [0x25C6DA63C38DE1B0u64, 0xE2280B6C20DD5232u64], + [0x579C487E5A38AD0Eu64, 0x8D590723948A535Fu64], + [0x2D835A9DF0C6D851u64, 0xB0AF48EC79ACE837u64], + [0xF8E431456CF88E65u64, 0xDCDB1B2798182244u64], + [0x1B8E9ECB641B58FFu64, 0x8A08F0F8BF0F156Bu64], + [0xE272467E3D222F3Fu64, 0xAC8B2D36EED2DAC5u64], + [0x5B0ED81DCC6ABB0Fu64, 0xD7ADF884AA879177u64], + [0x98E947129FC2B4E9u64, 0x86CCBB52EA94BAEAu64], + [0x3F2398D747B36224u64, 0xA87FEA27A539E9A5u64], + [0x8EEC7F0D19A03AADu64, 0xD29FE4B18E88640Eu64], + [0x1953CF68300424ACu64, 0x83A3EEEEF9153E89u64], + [0x5FA8C3423C052DD7u64, 0xA48CEAAAB75A8E2Bu64], + [0x3792F412CB06794Du64, 0xCDB02555653131B6u64], + [0xE2BBD88BBEE40BD0u64, 0x808E17555F3EBF11u64], + [0x5B6ACEAEAE9D0EC4u64, 0xA0B19D2AB70E6ED6u64], + [0xF245825A5A445275u64, 0xC8DE047564D20A8Bu64], + [0xEED6E2F0F0D56712u64, 0xFB158592BE068D2Eu64], + [0x55464DD69685606Bu64, 0x9CED737BB6C4183Du64], + [0xAA97E14C3C26B886u64, 0xC428D05AA4751E4Cu64], + [0xD53DD99F4B3066A8u64, 0xF53304714D9265DFu64], + [0xE546A8038EFE4029u64, 0x993FE2C6D07B7FABu64], + [0xDE98520472BDD033u64, 0xBF8FDB78849A5F96u64], + [0x963E66858F6D4440u64, 0xEF73D256A5C0F77Cu64], + [0xDDE7001379A44AA8u64, 0x95A8637627989AADu64], + [0x5560C018580D5D52u64, 0xBB127C53B17EC159u64], + [0xAAB8F01E6E10B4A6u64, 0xE9D71B689DDE71AFu64], + [0xCAB3961304CA70E8u64, 0x9226712162AB070Du64], + [0x3D607B97C5FD0D22u64, 0xB6B00D69BB55C8D1u64], + [0x8CB89A7DB77C506Au64, 0xE45C10C42A2B3B05u64], + [0x77F3608E92ADB242u64, 0x8EB98A7A9A5B04E3u64], + [0x55F038B237591ED3u64, 0xB267ED1940F1C61Cu64], + [0x6B6C46DEC52F6688u64, 0xDF01E85F912E37A3u64], + [0x2323AC4B3B3DA015u64, 0x8B61313BBABCE2C6u64], + [0xABEC975E0A0D081Au64, 0xAE397D8AA96C1B77u64], + [0x96E7BD358C904A21u64, 0xD9C7DCED53C72255u64], + [0x7E50D64177DA2E54u64, 0x881CEA14545C7575u64], + [0xDDE50BD1D5D0B9E9u64, 0xAA242499697392D2u64], + [0x955E4EC64B44E864u64, 0xD4AD2DBFC3D07787u64], + [0xBD5AF13BEF0B113Eu64, 0x84EC3C97DA624AB4u64], + [0xECB1AD8AEACDD58Eu64, 0xA6274BBDD0FADD61u64], + [0x67DE18EDA5814AF2u64, 0xCFB11EAD453994BAu64], + [0x80EACF948770CED7u64, 0x81CEB32C4B43FCF4u64], + [0xA1258379A94D028Du64, 0xA2425FF75E14FC31u64], + [0x096EE45813A04330u64, 0xCAD2F7F5359A3B3Eu64], + [0x8BCA9D6E188853FCu64, 0xFD87B5F28300CA0Du64], + [0x775EA264CF55347Du64, 0x9E74D1B791E07E48u64], + [0x95364AFE032A819Du64, 0xC612062576589DDAu64], + [0x3A83DDBD83F52204u64, 0xF79687AED3EEC551u64], + [0xC4926A9672793542u64, 0x9ABE14CD44753B52u64], + [0x75B7053C0F178293u64, 0xC16D9A0095928A27u64], + [0x5324C68B12DD6338u64, 0xF1C90080BAF72CB1u64], + [0xD3F6FC16EBCA5E03u64, 0x971DA05074DA7BEEu64], + [0x88F4BB1CA6BCF584u64, 0xBCE5086492111AEAu64], + [0x2B31E9E3D06C32E5u64, 0xEC1E4A7DB69561A5u64], + [0x3AFF322E62439FCFu64, 0x9392EE8E921D5D07u64], + [0x09BEFEB9FAD487C2u64, 0xB877AA3236A4B449u64], + [0x4C2EBE687989A9B3u64, 0xE69594BEC44DE15Bu64], + [0x0F9D37014BF60A10u64, 0x901D7CF73AB0ACD9u64], + [0x538484C19EF38C94u64, 0xB424DC35095CD80Fu64], + [0x2865A5F206B06FB9u64, 0xE12E13424BB40E13u64], + [0xF93F87B7442E45D3u64, 0x8CBCCC096F5088CBu64], + [0xF78F69A51539D748u64, 0xAFEBFF0BCB24AAFEu64], + [0xB573440E5A884D1Bu64, 0xDBE6FECEBDEDD5BEu64], + [0x31680A88F8953030u64, 0x89705F4136B4A597u64], + [0xFDC20D2B36BA7C3Du64, 0xABCC77118461CEFCu64], + [0x3D32907604691B4Cu64, 0xD6BF94D5E57A42BCu64], + [0xA63F9A49C2C1B10Fu64, 0x8637BD05AF6C69B5u64], + [0x0FCF80DC33721D53u64, 0xA7C5AC471B478423u64], + [0xD3C36113404EA4A8u64, 0xD1B71758E219652Bu64], + [0x645A1CAC083126E9u64, 0x83126E978D4FDF3Bu64], + [0x3D70A3D70A3D70A3u64, 0xA3D70A3D70A3D70Au64], + [0xCCCCCCCCCCCCCCCCu64, 0xCCCCCCCCCCCCCCCCu64], + [0x0000000000000000u64, 0x8000000000000000u64], + [0x0000000000000000u64, 0xA000000000000000u64], + [0x0000000000000000u64, 0xC800000000000000u64], + [0x0000000000000000u64, 0xFA00000000000000u64], + [0x0000000000000000u64, 0x9C40000000000000u64], + [0x0000000000000000u64, 0xC350000000000000u64], + [0x0000000000000000u64, 0xF424000000000000u64], + [0x0000000000000000u64, 0x9896800000000000u64], + [0x0000000000000000u64, 0xBEBC200000000000u64], + [0x0000000000000000u64, 0xEE6B280000000000u64], + [0x0000000000000000u64, 0x9502F90000000000u64], + [0x0000000000000000u64, 0xBA43B74000000000u64], + [0x0000000000000000u64, 0xE8D4A51000000000u64], + [0x0000000000000000u64, 0x9184E72A00000000u64], + [0x0000000000000000u64, 0xB5E620F480000000u64], + [0x0000000000000000u64, 0xE35FA931A0000000u64], + [0x0000000000000000u64, 0x8E1BC9BF04000000u64], + [0x0000000000000000u64, 0xB1A2BC2EC5000000u64], + [0x0000000000000000u64, 0xDE0B6B3A76400000u64], + [0x0000000000000000u64, 0x8AC7230489E80000u64], + [0x0000000000000000u64, 0xAD78EBC5AC620000u64], + [0x0000000000000000u64, 0xD8D726B7177A8000u64], + [0x0000000000000000u64, 0x878678326EAC9000u64], + [0x0000000000000000u64, 0xA968163F0A57B400u64], + [0x0000000000000000u64, 0xD3C21BCECCEDA100u64], + [0x0000000000000000u64, 0x84595161401484A0u64], + [0x0000000000000000u64, 0xA56FA5B99019A5C8u64], + [0x0000000000000000u64, 0xCECB8F27F4200F3Au64], + [0x4000000000000000u64, 0x813F3978F8940984u64], + [0x5000000000000000u64, 0xA18F07D736B90BE5u64], + [0xA400000000000000u64, 0xC9F2C9CD04674EDEu64], + [0x4D00000000000000u64, 0xFC6F7C4045812296u64], + [0xF020000000000000u64, 0x9DC5ADA82B70B59Du64], + [0x6C28000000000000u64, 0xC5371912364CE305u64], + [0xC732000000000000u64, 0xF684DF56C3E01BC6u64], + [0x3C7F400000000000u64, 0x9A130B963A6C115Cu64], + [0x4B9F100000000000u64, 0xC097CE7BC90715B3u64], + [0x1E86D40000000000u64, 0xF0BDC21ABB48DB20u64], + [0x1314448000000000u64, 0x96769950B50D88F4u64], + [0x17D955A000000000u64, 0xBC143FA4E250EB31u64], + [0x5DCFAB0800000000u64, 0xEB194F8E1AE525FDu64], + [0x5AA1CAE500000000u64, 0x92EFD1B8D0CF37BEu64], + [0xF14A3D9E40000000u64, 0xB7ABC627050305ADu64], + [0x6D9CCD05D0000000u64, 0xE596B7B0C643C719u64], + [0xE4820023A2000000u64, 0x8F7E32CE7BEA5C6Fu64], + [0xDDA2802C8A800000u64, 0xB35DBF821AE4F38Bu64], + [0xD50B2037AD200000u64, 0xE0352F62A19E306Eu64], + [0x4526F422CC340000u64, 0x8C213D9DA502DE45u64], + [0x9670B12B7F410000u64, 0xAF298D050E4395D6u64], + [0x3C0CDD765F114000u64, 0xDAF3F04651D47B4Cu64], + [0xA5880A69FB6AC800u64, 0x88D8762BF324CD0Fu64], + [0x8EEA0D047A457A00u64, 0xAB0E93B6EFEE0053u64], + [0x72A4904598D6D880u64, 0xD5D238A4ABE98068u64], + [0x47A6DA2B7F864750u64, 0x85A36366EB71F041u64], + [0x999090B65F67D924u64, 0xA70C3C40A64E6C51u64], + [0xFFF4B4E3F741CF6Du64, 0xD0CF4B50CFE20765u64], + [0xBFF8F10E7A8921A4u64, 0x82818F1281ED449Fu64], + [0xAFF72D52192B6A0Du64, 0xA321F2D7226895C7u64], + [0x9BF4F8A69F764490u64, 0xCBEA6F8CEB02BB39u64], + [0x02F236D04753D5B4u64, 0xFEE50B7025C36A08u64], + [0x01D762422C946590u64, 0x9F4F2726179A2245u64], + [0x424D3AD2B7B97EF5u64, 0xC722F0EF9D80AAD6u64], + [0xD2E0898765A7DEB2u64, 0xF8EBAD2B84E0D58Bu64], + [0x63CC55F49F88EB2Fu64, 0x9B934C3B330C8577u64], + [0x3CBF6B71C76B25FBu64, 0xC2781F49FFCFA6D5u64], + [0x8BEF464E3945EF7Au64, 0xF316271C7FC3908Au64], + [0x97758BF0E3CBB5ACu64, 0x97EDD871CFDA3A56u64], + [0x3D52EEED1CBEA317u64, 0xBDE94E8E43D0C8ECu64], + [0x4CA7AAA863EE4BDDu64, 0xED63A231D4C4FB27u64], + [0x8FE8CAA93E74EF6Au64, 0x945E455F24FB1CF8u64], + [0xB3E2FD538E122B44u64, 0xB975D6B6EE39E436u64], + [0x60DBBCA87196B616u64, 0xE7D34C64A9C85D44u64], + [0xBC8955E946FE31CDu64, 0x90E40FBEEA1D3A4Au64], + [0x6BABAB6398BDBE41u64, 0xB51D13AEA4A488DDu64], + [0xC696963C7EED2DD1u64, 0xE264589A4DCDAB14u64], + [0xFC1E1DE5CF543CA2u64, 0x8D7EB76070A08AECu64], + [0x3B25A55F43294BCBu64, 0xB0DE65388CC8ADA8u64], + [0x49EF0EB713F39EBEu64, 0xDD15FE86AFFAD912u64], + [0x6E3569326C784337u64, 0x8A2DBF142DFCC7ABu64], + [0x49C2C37F07965404u64, 0xACB92ED9397BF996u64], + [0xDC33745EC97BE906u64, 0xD7E77A8F87DAF7FBu64], + [0x69A028BB3DED71A3u64, 0x86F0AC99B4E8DAFDu64], + [0xC40832EA0D68CE0Cu64, 0xA8ACD7C0222311BCu64], + [0xF50A3FA490C30190u64, 0xD2D80DB02AABD62Bu64], + [0x792667C6DA79E0FAu64, 0x83C7088E1AAB65DBu64], + [0x577001B891185938u64, 0xA4B8CAB1A1563F52u64], + [0xED4C0226B55E6F86u64, 0xCDE6FD5E09ABCF26u64], + [0x544F8158315B05B4u64, 0x80B05E5AC60B6178u64], + [0x696361AE3DB1C721u64, 0xA0DC75F1778E39D6u64], + [0x03BC3A19CD1E38E9u64, 0xC913936DD571C84Cu64], + [0x04AB48A04065C723u64, 0xFB5878494ACE3A5Fu64], + [0x62EB0D64283F9C76u64, 0x9D174B2DCEC0E47Bu64], + [0x3BA5D0BD324F8394u64, 0xC45D1DF942711D9Au64], + [0xCA8F44EC7EE36479u64, 0xF5746577930D6500u64], + [0x7E998B13CF4E1ECBu64, 0x9968BF6ABBE85F20u64], + [0x9E3FEDD8C321A67Eu64, 0xBFC2EF456AE276E8u64], + [0xC5CFE94EF3EA101Eu64, 0xEFB3AB16C59B14A2u64], + [0xBBA1F1D158724A12u64, 0x95D04AEE3B80ECE5u64], + [0x2A8A6E45AE8EDC97u64, 0xBB445DA9CA61281Fu64], + [0xF52D09D71A3293BDu64, 0xEA1575143CF97226u64], + [0x593C2626705F9C56u64, 0x924D692CA61BE758u64], + [0x6F8B2FB00C77836Cu64, 0xB6E0C377CFA2E12Eu64], + [0x0B6DFB9C0F956447u64, 0xE498F455C38B997Au64], + [0x4724BD4189BD5EACu64, 0x8EDF98B59A373FECu64], + [0x58EDEC91EC2CB657u64, 0xB2977EE300C50FE7u64], + [0x2F2967B66737E3EDu64, 0xDF3D5E9BC0F653E1u64], + [0xBD79E0D20082EE74u64, 0x8B865B215899F46Cu64], + [0xECD8590680A3AA11u64, 0xAE67F1E9AEC07187u64], + [0xE80E6F4820CC9495u64, 0xDA01EE641A708DE9u64], + [0x3109058D147FDCDDu64, 0x884134FE908658B2u64], + [0xBD4B46F0599FD415u64, 0xAA51823E34A7EEDEu64], + [0x6C9E18AC7007C91Au64, 0xD4E5E2CDC1D1EA96u64], + [0x03E2CF6BC604DDB0u64, 0x850FADC09923329Eu64], + [0x84DB8346B786151Cu64, 0xA6539930BF6BFF45u64], + [0xE612641865679A63u64, 0xCFE87F7CEF46FF16u64], + [0x4FCB7E8F3F60C07Eu64, 0x81F14FAE158C5F6Eu64], + [0xE3BE5E330F38F09Du64, 0xA26DA3999AEF7749u64], + [0x5CADF5BFD3072CC5u64, 0xCB090C8001AB551Cu64], + [0x73D9732FC7C8F7F6u64, 0xFDCB4FA002162A63u64], + [0x2867E7FDDCDD9AFAu64, 0x9E9F11C4014DDA7Eu64], + [0xB281E1FD541501B8u64, 0xC646D63501A1511Du64], + [0x1F225A7CA91A4226u64, 0xF7D88BC24209A565u64], + [0x3375788DE9B06958u64, 0x9AE757596946075Fu64], + [0x0052D6B1641C83AEu64, 0xC1A12D2FC3978937u64], + [0xC0678C5DBD23A49Au64, 0xF209787BB47D6B84u64], + [0xF840B7BA963646E0u64, 0x9745EB4D50CE6332u64], + [0xB650E5A93BC3D898u64, 0xBD176620A501FBFFu64], + [0xA3E51F138AB4CEBEu64, 0xEC5D3FA8CE427AFFu64], + [0xC66F336C36B10137u64, 0x93BA47C980E98CDFu64], + [0xB80B0047445D4184u64, 0xB8A8D9BBE123F017u64], + [0xA60DC059157491E5u64, 0xE6D3102AD96CEC1Du64], + [0x87C89837AD68DB2Fu64, 0x9043EA1AC7E41392u64], + [0x29BABE4598C311FBu64, 0xB454E4A179DD1877u64], + [0xF4296DD6FEF3D67Au64, 0xE16A1DC9D8545E94u64], + [0x1899E4A65F58660Cu64, 0x8CE2529E2734BB1Du64], + [0x5EC05DCFF72E7F8Fu64, 0xB01AE745B101E9E4u64], + [0x76707543F4FA1F73u64, 0xDC21A1171D42645Du64], + [0x6A06494A791C53A8u64, 0x899504AE72497EBAu64], + [0x0487DB9D17636892u64, 0xABFA45DA0EDBDE69u64], + [0x45A9D2845D3C42B6u64, 0xD6F8D7509292D603u64], + [0x0B8A2392BA45A9B2u64, 0x865B86925B9BC5C2u64], + [0x8E6CAC7768D7141Eu64, 0xA7F26836F282B732u64], + [0x3207D795430CD926u64, 0xD1EF0244AF2364FFu64], + [0x7F44E6BD49E807B8u64, 0x8335616AED761F1Fu64], + [0x5F16206C9C6209A6u64, 0xA402B9C5A8D3A6E7u64], + [0x36DBA887C37A8C0Fu64, 0xCD036837130890A1u64], + [0xC2494954DA2C9789u64, 0x802221226BE55A64u64], + [0xF2DB9BAA10B7BD6Cu64, 0xA02AA96B06DEB0FDu64], + [0x6F92829494E5ACC7u64, 0xC83553C5C8965D3Du64], + [0xCB772339BA1F17F9u64, 0xFA42A8B73ABBF48Cu64], + [0xFF2A760414536EFBu64, 0x9C69A97284B578D7u64], + [0xFEF5138519684ABAu64, 0xC38413CF25E2D70Du64], + [0x7EB258665FC25D69u64, 0xF46518C2EF5B8CD1u64], + [0xEF2F773FFBD97A61u64, 0x98BF2F79D5993802u64], + [0xAAFB550FFACFD8FAu64, 0xBEEEFB584AFF8603u64], + [0x95BA2A53F983CF38u64, 0xEEAABA2E5DBF6784u64], + [0xDD945A747BF26183u64, 0x952AB45CFA97A0B2u64], + [0x94F971119AEEF9E4u64, 0xBA756174393D88DFu64], + [0x7A37CD5601AAB85Du64, 0xE912B9D1478CEB17u64], + [0xAC62E055C10AB33Au64, 0x91ABB422CCB812EEu64], + [0x577B986B314D6009u64, 0xB616A12B7FE617AAu64], + [0xED5A7E85FDA0B80Bu64, 0xE39C49765FDF9D94u64], + [0x14588F13BE847307u64, 0x8E41ADE9FBEBC27Du64], + [0x596EB2D8AE258FC8u64, 0xB1D219647AE6B31Cu64], + [0x6FCA5F8ED9AEF3BBu64, 0xDE469FBD99A05FE3u64], + [0x25DE7BB9480D5854u64, 0x8AEC23D680043BEEu64], + [0xAF561AA79A10AE6Au64, 0xADA72CCC20054AE9u64], + [0x1B2BA1518094DA04u64, 0xD910F7FF28069DA4u64], + [0x90FB44D2F05D0842u64, 0x87AA9AFF79042286u64], + [0x353A1607AC744A53u64, 0xA99541BF57452B28u64], + [0x42889B8997915CE8u64, 0xD3FA922F2D1675F2u64], + [0x69956135FEBADA11u64, 0x847C9B5D7C2E09B7u64], + [0x43FAB9837E699095u64, 0xA59BC234DB398C25u64], + [0x94F967E45E03F4BBu64, 0xCF02B2C21207EF2Eu64], + [0x1D1BE0EEBAC278F5u64, 0x8161AFB94B44F57Du64], + [0x6462D92A69731732u64, 0xA1BA1BA79E1632DCu64], + [0x7D7B8F7503CFDCFEu64, 0xCA28A291859BBF93u64], + [0x5CDA735244C3D43Eu64, 0xFCB2CB35E702AF78u64], + [0x3A0888136AFA64A7u64, 0x9DEFBF01B061ADABu64], + [0x088AAA1845B8FDD0u64, 0xC56BAEC21C7A1916u64], + [0x8AAD549E57273D45u64, 0xF6C69A72A3989F5Bu64], + [0x36AC54E2F678864Bu64, 0x9A3C2087A63F6399u64], + [0x84576A1BB416A7DDu64, 0xC0CB28A98FCF3C7Fu64], + [0x656D44A2A11C51D5u64, 0xF0FDF2D3F3C30B9Fu64], + [0x9F644AE5A4B1B325u64, 0x969EB7C47859E743u64], + [0x873D5D9F0DDE1FEEu64, 0xBC4665B596706114u64], + [0xA90CB506D155A7EAu64, 0xEB57FF22FC0C7959u64], + [0x09A7F12442D588F2u64, 0x9316FF75DD87CBD8u64], + [0x0C11ED6D538AEB2Fu64, 0xB7DCBF5354E9BECEu64], + [0x8F1668C8A86DA5FAu64, 0xE5D3EF282A242E81u64], + [0xF96E017D694487BCu64, 0x8FA475791A569D10u64], + [0x37C981DCC395A9ACu64, 0xB38D92D760EC4455u64], + [0x85BBE253F47B1417u64, 0xE070F78D3927556Au64], + [0x93956D7478CCEC8Eu64, 0x8C469AB843B89562u64], + [0x387AC8D1970027B2u64, 0xAF58416654A6BABBu64], + [0x06997B05FCC0319Eu64, 0xDB2E51BFE9D0696Au64], + [0x441FECE3BDF81F03u64, 0x88FCF317F22241E2u64], + [0xD527E81CAD7626C3u64, 0xAB3C2FDDEEAAD25Au64], + [0x8A71E223D8D3B074u64, 0xD60B3BD56A5586F1u64], + [0xF6872D5667844E49u64, 0x85C7056562757456u64], + [0xB428F8AC016561DBu64, 0xA738C6BEBB12D16Cu64], + [0xE13336D701BEBA52u64, 0xD106F86E69D785C7u64], + [0xECC0024661173473u64, 0x82A45B450226B39Cu64], + [0x27F002D7F95D0190u64, 0xA34D721642B06084u64], + [0x31EC038DF7B441F4u64, 0xCC20CE9BD35C78A5u64], + [0x7E67047175A15271u64, 0xFF290242C83396CEu64], + [0x0F0062C6E984D386u64, 0x9F79A169BD203E41u64], + [0x52C07B78A3E60868u64, 0xC75809C42C684DD1u64], + [0xA7709A56CCDF8A82u64, 0xF92E0C3537826145u64], + [0x88A66076400BB691u64, 0x9BBCC7A142B17CCBu64], + [0x6ACFF893D00EA435u64, 0xC2ABF989935DDBFEu64], + [0x0583F6B8C4124D43u64, 0xF356F7EBF83552FEu64], + [0xC3727A337A8B704Au64, 0x98165AF37B2153DEu64], + [0x744F18C0592E4C5Cu64, 0xBE1BF1B059E9A8D6u64], + [0x1162DEF06F79DF73u64, 0xEDA2EE1C7064130Cu64], + [0x8ADDCB5645AC2BA8u64, 0x9485D4D1C63E8BE7u64], + [0x6D953E2BD7173692u64, 0xB9A74A0637CE2EE1u64], + [0xC8FA8DB6CCDD0437u64, 0xE8111C87C5C1BA99u64], + [0x1D9C9892400A22A2u64, 0x910AB1D4DB9914A0u64], + [0x2503BEB6D00CAB4Bu64, 0xB54D5E4A127F59C8u64], + [0x2E44AE64840FD61Du64, 0xE2A0B5DC971F303Au64], + [0x5CEAECFED289E5D2u64, 0x8DA471A9DE737E24u64], + [0x7425A83E872C5F47u64, 0xB10D8E1456105DADu64], + [0xD12F124E28F77719u64, 0xDD50F1996B947518u64], + [0x82BD6B70D99AAA6Fu64, 0x8A5296FFE33CC92Fu64], + [0x636CC64D1001550Bu64, 0xACE73CBFDC0BFB7Bu64], + [0x3C47F7E05401AA4Eu64, 0xD8210BEFD30EFA5Au64], + [0x65ACFAEC34810A71u64, 0x8714A775E3E95C78u64], + [0x7F1839A741A14D0Du64, 0xA8D9D1535CE3B396u64], + [0x1EDE48111209A050u64, 0xD31045A8341CA07Cu64], + [0x934AED0AAB460432u64, 0x83EA2B892091E44Du64], + [0xF81DA84D5617853Fu64, 0xA4E4B66B68B65D60u64], + [0x36251260AB9D668Eu64, 0xCE1DE40642E3F4B9u64], + [0xC1D72B7C6B426019u64, 0x80D2AE83E9CE78F3u64], + [0xB24CF65B8612F81Fu64, 0xA1075A24E4421730u64], + [0xDEE033F26797B627u64, 0xC94930AE1D529CFCu64], + [0x169840EF017DA3B1u64, 0xFB9B7CD9A4A7443Cu64], + [0x8E1F289560EE864Eu64, 0x9D412E0806E88AA5u64], + [0xF1A6F2BAB92A27E2u64, 0xC491798A08A2AD4Eu64], + [0xAE10AF696774B1DBu64, 0xF5B5D7EC8ACB58A2u64], + [0xACCA6DA1E0A8EF29u64, 0x9991A6F3D6BF1765u64], + [0x17FD090A58D32AF3u64, 0xBFF610B0CC6EDD3Fu64], + [0xDDFC4B4CEF07F5B0u64, 0xEFF394DCFF8A948Eu64], + [0x4ABDAF101564F98Eu64, 0x95F83D0A1FB69CD9u64], + [0x9D6D1AD41ABE37F1u64, 0xBB764C4CA7A4440Fu64], + [0x84C86189216DC5EDu64, 0xEA53DF5FD18D5513u64], + [0x32FD3CF5B4E49BB4u64, 0x92746B9BE2F8552Cu64], + [0x3FBC8C33221DC2A1u64, 0xB7118682DBB66A77u64], + [0x0FABAF3FEAA5334Au64, 0xE4D5E82392A40515u64], + [0x29CB4D87F2A7400Eu64, 0x8F05B1163BA6832Du64], + [0x743E20E9EF511012u64, 0xB2C71D5BCA9023F8u64], + [0x914DA9246B255416u64, 0xDF78E4B2BD342CF6u64], + [0x1AD089B6C2F7548Eu64, 0x8BAB8EEFB6409C1Au64], + [0xA184AC2473B529B1u64, 0xAE9672ABA3D0C320u64], + [0xC9E5D72D90A2741Eu64, 0xDA3C0F568CC4F3E8u64], + [0x7E2FA67C7A658892u64, 0x8865899617FB1871u64], + [0xDDBB901B98FEEAB7u64, 0xAA7EEBFB9DF9DE8Du64], + [0x552A74227F3EA565u64, 0xD51EA6FA85785631u64], + [0xD53A88958F87275Fu64, 0x8533285C936B35DEu64], + [0x8A892ABAF368F137u64, 0xA67FF273B8460356u64], + [0x2D2B7569B0432D85u64, 0xD01FEF10A657842Cu64], + [0x9C3B29620E29FC73u64, 0x8213F56A67F6B29Bu64], + [0x8349F3BA91B47B8Fu64, 0xA298F2C501F45F42u64], + [0x241C70A936219A73u64, 0xCB3F2F7642717713u64], + [0xED238CD383AA0110u64, 0xFE0EFB53D30DD4D7u64], + [0xF4363804324A40AAu64, 0x9EC95D1463E8A506u64], + [0xB143C6053EDCD0D5u64, 0xC67BB4597CE2CE48u64], + [0xDD94B7868E94050Au64, 0xF81AA16FDC1B81DAu64], + [0xCA7CF2B4191C8326u64, 0x9B10A4E5E9913128u64], + [0xFD1C2F611F63A3F0u64, 0xC1D4CE1F63F57D72u64], + [0xBC633B39673C8CECu64, 0xF24A01A73CF2DCCFu64], + [0xD5BE0503E085D813u64, 0x976E41088617CA01u64], + [0x4B2D8644D8A74E18u64, 0xBD49D14AA79DBC82u64], + [0xDDF8E7D60ED1219Eu64, 0xEC9C459D51852BA2u64], + [0xCABB90E5C942B503u64, 0x93E1AB8252F33B45u64], + [0x3D6A751F3B936243u64, 0xB8DA1662E7B00A17u64], + [0x0CC512670A783AD4u64, 0xE7109BFBA19C0C9Du64], + [0x27FB2B80668B24C5u64, 0x906A617D450187E2u64], + [0xB1F9F660802DEDF6u64, 0xB484F9DC9641E9DAu64], + [0x5E7873F8A0396973u64, 0xE1A63853BBD26451u64], + [0xDB0B487B6423E1E8u64, 0x8D07E33455637EB2u64], + [0x91CE1A9A3D2CDA62u64, 0xB049DC016ABC5E5Fu64], + [0x7641A140CC7810FBu64, 0xDC5C5301C56B75F7u64], + [0xA9E904C87FCB0A9Du64, 0x89B9B3E11B6329BAu64], + [0x546345FA9FBDCD44u64, 0xAC2820D9623BF429u64], + [0xA97C177947AD4095u64, 0xD732290FBACAF133u64], + [0x49ED8EABCCCC485Du64, 0x867F59A9D4BED6C0u64], + [0x5C68F256BFFF5A74u64, 0xA81F301449EE8C70u64], + [0x73832EEC6FFF3111u64, 0xD226FC195C6A2F8Cu64], +]; + // strconv — number↔string conversions. // // Mirrors Hare's strconv:: surface. The *tos functions return a @@ -3904,143 +5635,6 @@ export fn tokprint(fd: i32, t: *tok) void = { fputcbyte(fd, 10u8); // '\n' }; -// ascii — rune-class predicates and case folding for the ASCII range. -// Matches Hare's ascii::isdigit family (rune-taking signature). Runes -// outside 0..127 always answer `false`. The lexer hot path uses these -// inline; they are expected to inline to a couple of compares. - -package ascii; - -export fn isdigit(c: rune) bool = { - if (c < 48) { return false; }; - if (c > 57) { return false; }; - return true; -}; - -export fn isupper(c: rune) bool = { - if (c < 65) { return false; }; - if (c > 90) { return false; }; - return true; -}; - -export fn islower(c: rune) bool = { - if (c < 97) { return false; }; - if (c > 122) { return false; }; - return true; -}; - -export fn isalpha(c: rune) bool = { - if (isupper(c)) { return true; }; - return islower(c); -}; - -export fn isalnum(c: rune) bool = { - if (isalpha(c)) { return true; }; - return isdigit(c); -}; - -// isspace — the C/Hare set: space, tab, NL, VT, FF, CR. -export fn isspace(c: rune) bool = { - if (c == 32) { return true; }; // ' ' - if (c == 9) { return true; }; // '\t' - if (c == 10) { return true; }; // '\n' - if (c == 11) { return true; }; // '\v' - if (c == 12) { return true; }; // '\f' - if (c == 13) { return true; }; // '\r' - return false; -}; - -export fn isxdigit(c: rune) bool = { - if (isdigit(c)) { return true; }; - if (c >= 65) { - if (c <= 70) { return true; }; // 'A'..'F' - }; - if (c >= 97) { - if (c <= 102) { return true; }; // 'a'..'f' - }; - return false; -}; - -// valid — `c` is in the 0..127 ASCII range. -export fn valid(c: rune) bool = { - if (c < 0) { return false; }; - if (c > 127) { return false; }; - return true; -}; - -// validstr — every byte in `s` is ASCII (0..127). -export fn validstr(s: str) bool = { - let i: i32 = 0; - for (i < s.len) { - // High-bit test rather than `> 127u8`; both cgens lower - // the bitwise form identically. The `> u8` form picks - // JA vs JG depending on signed/unsigned dispatch. - if ((s[i] & 128u8) != 0u8) { return false; }; - i += 1; - }; - return true; -}; - -// iscntrl — control chars: 0..31 and 127. -export fn iscntrl(c: rune) bool = { - if (c >= 0) { if (c <= 31) { return true; }; }; - if (c == 127) { return true; }; - return false; -}; - -// isblank — space and tab. -export fn isblank(c: rune) bool = { - if (c == 32) { return true; }; // ' ' - if (c == 9) { return true; }; // '\t' - return false; -}; - -// isprint — printable: space through '~'. -export fn isprint(c: rune) bool = { - if (c < 32) { return false; }; - if (c > 126) { return false; }; - return true; -}; - -// isgraph — printable, non-space. -export fn isgraph(c: rune) bool = { - if (c < 33) { return false; }; - if (c > 126) { return false; }; - return true; -}; - -// ispunct — printable, non-alnum, non-space. -export fn ispunct(c: rune) bool = { - if (!isgraph(c)) { return false; }; - if (isalnum(c)) { return false; }; - return true; -}; - -// tolower / toupper — fold ASCII case. Non-letters pass through. -export fn tolower(c: rune) rune = { - if (isupper(c)) { return c + 32; }; - return c; -}; - -export fn toupper(c: rune) rune = { - if (islower(c)) { return c - 32; }; - return c; -}; - -// strcasecmp — three-way ASCII case-insensitive compare. -export fn strcasecmp(a: str, b: str) i32 = { - let n: i32 = a.len; - if (b.len < n) { n = b.len; }; - let i: i32 = 0; - for (i < n) { - let ca: rune = tolower(a[i]: rune); - let cb: rune = tolower(b[i]: rune); - if (ca != cb) { return (ca - cb): i32; }; - i += 1; - }; - return a.len - b.len; -}; - // lib/ww/lex/lex.ww — port of cmd/wcc/lex.c. // // The DFA, the helpers, and the order of decisions all mirror the C diff --git a/selfhost/test/smoke.combined.ww b/selfhost/test/smoke.combined.ww index ed340db3..a87433a8 100644 --- a/selfhost/test/smoke.combined.ww +++ b/selfhost/test/smoke.combined.ww @@ -1049,101 +1049,432 @@ fn decimal_round(d: *decimal) u64 = { return n; }; -// strconv — stof/ftos lookup tables. Mirrors ref/hare/strconv/stof_data.ha -// byte-exact. Pure-data fold (strconv #106 fold-2, was fold-3 before drew -// re-sequenced 2026-05-26): no logic, exercised transitively when fold-3's -// `leftshift_newdigits` lands (ref/hare/strconv/decimal.ha:35). -// -// ww uses module-level `let` for compile-time array data (ref/hare/strconv -// `const` has no ww keyword equivalent; lib/encoding/utf8/utf8.ww:48 sets -// the precedent with [2048]i8 dfa). Literal suffixes (`u16`, `u8`) are -// required because cstage rejects bare integer literals in `[N]u8`/`[N]u16` -// init while wwstage accepts them; the suffixed form is the only shape -// both stages agree on (candidate #130). -// -// `powers_of_ten: [596][2]u64` (ref/hare/strconv/stof_data.ha:73) is the -// Eisel-Lemire fast-path table and is only consumed by stof.ha (a later -// fold); deferred to its consumer-driven port per CLAUDE.md rule 9 -// (Hare-fidelity is API-shaped, and module-level tables follow their -// caller's scope). +// ascii — rune-class predicates and case folding for the ASCII range. +// Matches Hare's ascii::isdigit family (rune-taking signature). Runes +// outside 0..127 always answer `false`. The lexer hot path uses these +// inline; they are expected to inline to a couple of compares. -package strconv; +package ascii; -// ref/hare/strconv/stof_data.ha:4. Powers-of-five decimal-expansion -// metadata for `leftshift_newdigits` (decimal.ha:35). The top 5 bits -// of each entry are the new-digit-count `nn`; the low 11 bits index -// `pow5_table` for the digits themselves. -let left_shift_table: [65]u16 = [ - 0x0000u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16, 0x1817u16, - 0x181Du16, 0x2024u16, 0x202Bu16, 0x2033u16, 0x203Cu16, 0x2846u16, 0x2850u16, 0x285Bu16, 0x3067u16, - 0x3073u16, 0x3080u16, 0x388Eu16, 0x389Cu16, 0x38ABu16, 0x38BBu16, 0x40CCu16, 0x40DDu16, 0x40EFu16, - 0x4902u16, 0x4915u16, 0x4929u16, 0x513Eu16, 0x5153u16, 0x5169u16, 0x5180u16, 0x5998u16, 0x59B0u16, - 0x59C9u16, 0x61E3u16, 0x61FDu16, 0x6218u16, 0x6A34u16, 0x6A50u16, 0x6A6Du16, 0x6A8Bu16, 0x72AAu16, - 0x72C9u16, 0x72E9u16, 0x7B0Au16, 0x7B2Bu16, 0x7B4Du16, 0x8370u16, 0x8393u16, 0x83B7u16, 0x83DCu16, - 0x8C02u16, 0x8C28u16, 0x8C4Fu16, 0x9477u16, 0x949Fu16, 0x94C8u16, 0x9CF2u16, 0x051Cu16, 0x051Cu16, - 0x051Cu16, 0x051Cu16, -]; +export fn isdigit(c: rune) bool = { + if (c < 48) { return false; }; + if (c > 57) { return false; }; + return true; +}; -// ref/hare/strconv/stof_data.ha:15. Decimal digits of 5^k for k=1..60, -// concatenated. Indexed via `left_shift_table` (above); each shift k -// reads its `pow5_b - pow5_a` digits starting at `pow5_a`. -let pow5_table: [0x051C]u8 = [ - 5u8, 2u8, 5u8, 1u8, 2u8, 5u8, 6u8, 2u8, 5u8, 3u8, 1u8, 2u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 3u8, - 9u8, 0u8, 6u8, 2u8, 5u8, 1u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 9u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, 4u8, 8u8, 8u8, 2u8, 8u8, - 1u8, 2u8, 5u8, 2u8, 4u8, 4u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 1u8, 2u8, 2u8, 0u8, 7u8, 0u8, 3u8, 1u8, 2u8, 5u8, 6u8, 1u8, - 0u8, 3u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 3u8, 0u8, 5u8, 1u8, 7u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 1u8, 5u8, 2u8, 5u8, 8u8, - 7u8, 8u8, 9u8, 0u8, 6u8, 2u8, 5u8, 7u8, 6u8, 2u8, 9u8, 3u8, 9u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, 3u8, 8u8, 1u8, 4u8, 6u8, - 9u8, 7u8, 2u8, 6u8, 5u8, 6u8, 2u8, 5u8, 1u8, 9u8, 0u8, 7u8, 3u8, 4u8, 8u8, 6u8, 3u8, 2u8, 8u8, 1u8, 2u8, 5u8, 9u8, 5u8, - 3u8, 6u8, 7u8, 4u8, 3u8, 1u8, 6u8, 4u8, 0u8, 6u8, 2u8, 5u8, 4u8, 7u8, 6u8, 8u8, 3u8, 7u8, 1u8, 5u8, 8u8, 2u8, 0u8, 3u8, - 1u8, 2u8, 5u8, 2u8, 3u8, 8u8, 4u8, 1u8, 8u8, 5u8, 7u8, 9u8, 1u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 1u8, 1u8, 9u8, 2u8, 0u8, - 9u8, 2u8, 8u8, 9u8, 5u8, 5u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 5u8, 9u8, 6u8, 0u8, 4u8, 6u8, 4u8, 4u8, 7u8, 7u8, 5u8, 3u8, - 9u8, 0u8, 6u8, 2u8, 5u8, 2u8, 9u8, 8u8, 0u8, 2u8, 3u8, 2u8, 2u8, 3u8, 8u8, 7u8, 6u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 1u8, - 4u8, 9u8, 0u8, 1u8, 1u8, 6u8, 1u8, 1u8, 9u8, 3u8, 8u8, 4u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, 7u8, 4u8, 5u8, 0u8, 5u8, 8u8, - 0u8, 5u8, 9u8, 6u8, 9u8, 2u8, 3u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 3u8, 7u8, 2u8, 5u8, 2u8, 9u8, 0u8, 2u8, 9u8, 8u8, 4u8, - 6u8, 1u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 1u8, 8u8, 6u8, 2u8, 6u8, 4u8, 5u8, 1u8, 4u8, 9u8, 2u8, 3u8, 0u8, 9u8, 5u8, - 7u8, 0u8, 3u8, 1u8, 2u8, 5u8, 9u8, 3u8, 1u8, 3u8, 2u8, 2u8, 5u8, 7u8, 4u8, 6u8, 1u8, 5u8, 4u8, 7u8, 8u8, 5u8, 1u8, 5u8, - 6u8, 2u8, 5u8, 4u8, 6u8, 5u8, 6u8, 6u8, 1u8, 2u8, 8u8, 7u8, 3u8, 0u8, 7u8, 7u8, 3u8, 9u8, 2u8, 5u8, 7u8, 8u8, 1u8, 2u8, - 5u8, 2u8, 3u8, 2u8, 8u8, 3u8, 0u8, 6u8, 4u8, 3u8, 6u8, 5u8, 3u8, 8u8, 6u8, 9u8, 6u8, 2u8, 8u8, 9u8, 0u8, 6u8, 2u8, 5u8, - 1u8, 1u8, 6u8, 4u8, 1u8, 5u8, 3u8, 2u8, 1u8, 8u8, 2u8, 6u8, 9u8, 3u8, 4u8, 8u8, 1u8, 4u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, - 5u8, 8u8, 2u8, 0u8, 7u8, 6u8, 6u8, 0u8, 9u8, 1u8, 3u8, 4u8, 6u8, 7u8, 4u8, 0u8, 7u8, 2u8, 2u8, 6u8, 5u8, 6u8, 2u8, 5u8, - 2u8, 9u8, 1u8, 0u8, 3u8, 8u8, 3u8, 0u8, 4u8, 5u8, 6u8, 7u8, 3u8, 3u8, 7u8, 0u8, 3u8, 6u8, 1u8, 3u8, 2u8, 8u8, 1u8, 2u8, - 5u8, 1u8, 4u8, 5u8, 5u8, 1u8, 9u8, 1u8, 5u8, 2u8, 2u8, 8u8, 3u8, 6u8, 6u8, 8u8, 5u8, 1u8, 8u8, 0u8, 6u8, 6u8, 4u8, 0u8, - 6u8, 2u8, 5u8, 7u8, 2u8, 7u8, 5u8, 9u8, 5u8, 7u8, 6u8, 1u8, 4u8, 1u8, 8u8, 3u8, 4u8, 2u8, 5u8, 9u8, 0u8, 3u8, 3u8, 2u8, - 0u8, 3u8, 1u8, 2u8, 5u8, 3u8, 6u8, 3u8, 7u8, 9u8, 7u8, 8u8, 8u8, 0u8, 7u8, 0u8, 9u8, 1u8, 7u8, 1u8, 2u8, 9u8, 5u8, 1u8, - 6u8, 6u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 1u8, 8u8, 1u8, 8u8, 9u8, 8u8, 9u8, 4u8, 0u8, 3u8, 5u8, 4u8, 5u8, 8u8, 5u8, 6u8, - 4u8, 7u8, 5u8, 8u8, 3u8, 0u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 9u8, 0u8, 9u8, 4u8, 9u8, 4u8, 7u8, 0u8, 1u8, 7u8, 7u8, 2u8, - 9u8, 2u8, 8u8, 2u8, 3u8, 7u8, 9u8, 1u8, 5u8, 0u8, 3u8, 9u8, 0u8, 6u8, 2u8, 5u8, 4u8, 5u8, 4u8, 7u8, 4u8, 7u8, 3u8, 5u8, - 0u8, 8u8, 8u8, 6u8, 4u8, 6u8, 4u8, 1u8, 1u8, 8u8, 9u8, 5u8, 7u8, 5u8, 1u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 2u8, 2u8, 7u8, - 3u8, 7u8, 3u8, 6u8, 7u8, 5u8, 4u8, 4u8, 3u8, 2u8, 3u8, 2u8, 0u8, 5u8, 9u8, 4u8, 7u8, 8u8, 7u8, 5u8, 9u8, 7u8, 6u8, 5u8, - 6u8, 2u8, 5u8, 1u8, 1u8, 3u8, 6u8, 8u8, 6u8, 8u8, 3u8, 7u8, 7u8, 2u8, 1u8, 6u8, 1u8, 6u8, 0u8, 2u8, 9u8, 7u8, 3u8, 9u8, - 3u8, 7u8, 9u8, 8u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 5u8, 6u8, 8u8, 4u8, 3u8, 4u8, 1u8, 8u8, 8u8, 6u8, 0u8, 8u8, 0u8, 8u8, - 0u8, 1u8, 4u8, 8u8, 6u8, 9u8, 6u8, 8u8, 9u8, 9u8, 4u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 2u8, 8u8, 4u8, 2u8, 1u8, 7u8, 0u8, - 9u8, 4u8, 3u8, 0u8, 4u8, 0u8, 4u8, 0u8, 0u8, 7u8, 4u8, 3u8, 4u8, 8u8, 4u8, 4u8, 9u8, 7u8, 0u8, 7u8, 0u8, 3u8, 1u8, 2u8, - 5u8, 1u8, 4u8, 2u8, 1u8, 0u8, 8u8, 5u8, 4u8, 7u8, 1u8, 5u8, 2u8, 0u8, 2u8, 0u8, 0u8, 3u8, 7u8, 1u8, 7u8, 4u8, 2u8, 2u8, - 4u8, 8u8, 5u8, 3u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 7u8, 1u8, 0u8, 5u8, 4u8, 2u8, 7u8, 3u8, 5u8, 7u8, 6u8, 0u8, 1u8, 0u8, - 0u8, 1u8, 8u8, 5u8, 8u8, 7u8, 1u8, 1u8, 2u8, 4u8, 2u8, 6u8, 7u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 3u8, 5u8, 5u8, 2u8, 7u8, - 1u8, 3u8, 6u8, 7u8, 8u8, 8u8, 0u8, 0u8, 5u8, 0u8, 0u8, 9u8, 2u8, 9u8, 3u8, 5u8, 5u8, 6u8, 2u8, 1u8, 3u8, 3u8, 7u8, 8u8, - 9u8, 0u8, 6u8, 2u8, 5u8, 1u8, 7u8, 7u8, 6u8, 3u8, 5u8, 6u8, 8u8, 3u8, 9u8, 4u8, 0u8, 0u8, 2u8, 5u8, 0u8, 4u8, 6u8, 4u8, - 6u8, 7u8, 7u8, 8u8, 1u8, 0u8, 6u8, 6u8, 8u8, 9u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, 8u8, 8u8, 8u8, 1u8, 7u8, 8u8, 4u8, 1u8, - 9u8, 7u8, 0u8, 0u8, 1u8, 2u8, 5u8, 2u8, 3u8, 2u8, 3u8, 3u8, 8u8, 9u8, 0u8, 5u8, 3u8, 3u8, 4u8, 4u8, 7u8, 2u8, 6u8, 5u8, - 6u8, 2u8, 5u8, 4u8, 4u8, 4u8, 0u8, 8u8, 9u8, 2u8, 0u8, 9u8, 8u8, 5u8, 0u8, 0u8, 6u8, 2u8, 6u8, 1u8, 6u8, 1u8, 6u8, 9u8, - 4u8, 5u8, 2u8, 6u8, 6u8, 7u8, 2u8, 3u8, 6u8, 3u8, 2u8, 8u8, 1u8, 2u8, 5u8, 2u8, 2u8, 2u8, 0u8, 4u8, 4u8, 6u8, 0u8, 4u8, - 9u8, 2u8, 5u8, 0u8, 3u8, 1u8, 3u8, 0u8, 8u8, 0u8, 8u8, 4u8, 7u8, 2u8, 6u8, 3u8, 3u8, 3u8, 6u8, 1u8, 8u8, 1u8, 6u8, 4u8, - 0u8, 6u8, 2u8, 5u8, 1u8, 1u8, 1u8, 0u8, 2u8, 2u8, 3u8, 0u8, 2u8, 4u8, 6u8, 2u8, 5u8, 1u8, 5u8, 6u8, 5u8, 4u8, 0u8, 4u8, - 2u8, 3u8, 6u8, 3u8, 1u8, 6u8, 6u8, 8u8, 0u8, 9u8, 0u8, 8u8, 2u8, 0u8, 3u8, 1u8, 2u8, 5u8, 5u8, 5u8, 5u8, 1u8, 1u8, 1u8, - 5u8, 1u8, 2u8, 3u8, 1u8, 2u8, 5u8, 7u8, 8u8, 2u8, 7u8, 0u8, 2u8, 1u8, 1u8, 8u8, 1u8, 5u8, 8u8, 3u8, 4u8, 0u8, 4u8, 5u8, - 4u8, 1u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 2u8, 7u8, 7u8, 5u8, 5u8, 5u8, 7u8, 5u8, 6u8, 1u8, 5u8, 6u8, 2u8, 8u8, 9u8, 1u8, - 3u8, 5u8, 1u8, 0u8, 5u8, 9u8, 0u8, 7u8, 9u8, 1u8, 7u8, 0u8, 2u8, 2u8, 7u8, 0u8, 5u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 1u8, - 3u8, 8u8, 7u8, 7u8, 7u8, 8u8, 7u8, 8u8, 0u8, 7u8, 8u8, 1u8, 4u8, 4u8, 5u8, 6u8, 7u8, 5u8, 5u8, 2u8, 9u8, 5u8, 3u8, 9u8, - 5u8, 8u8, 5u8, 1u8, 1u8, 3u8, 5u8, 2u8, 5u8, 3u8, 9u8, 0u8, 6u8, 2u8, 5u8, 6u8, 9u8, 3u8, 8u8, 8u8, 9u8, 3u8, 9u8, 0u8, - 3u8, 9u8, 0u8, 7u8, 2u8, 2u8, 8u8, 3u8, 7u8, 7u8, 6u8, 4u8, 7u8, 6u8, 9u8, 7u8, 9u8, 2u8, 5u8, 5u8, 6u8, 7u8, 6u8, 2u8, - 6u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 3u8, 4u8, 6u8, 9u8, 4u8, 4u8, 6u8, 9u8, 5u8, 1u8, 9u8, 5u8, 3u8, 6u8, 1u8, 4u8, 1u8, - 8u8, 8u8, 8u8, 2u8, 3u8, 8u8, 4u8, 8u8, 9u8, 6u8, 2u8, 7u8, 8u8, 3u8, 8u8, 1u8, 3u8, 4u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, - 1u8, 7u8, 3u8, 4u8, 7u8, 2u8, 3u8, 4u8, 7u8, 5u8, 9u8, 7u8, 6u8, 8u8, 0u8, 7u8, 0u8, 9u8, 4u8, 4u8, 1u8, 1u8, 9u8, 2u8, - 4u8, 4u8, 8u8, 1u8, 3u8, 9u8, 1u8, 9u8, 0u8, 6u8, 7u8, 3u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 8u8, 6u8, 7u8, 3u8, 6u8, 1u8, - 7u8, 3u8, 7u8, 9u8, 8u8, 8u8, 4u8, 0u8, 3u8, 5u8, 4u8, 7u8, 2u8, 0u8, 5u8, 9u8, 6u8, 2u8, 2u8, 4u8, 0u8, 6u8, 9u8, 5u8, - 9u8, 5u8, 3u8, 3u8, 6u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, -]; +export fn isupper(c: rune) bool = { + if (c < 65) { return false; }; + if (c > 90) { return false; }; + return true; +}; + +export fn islower(c: rune) bool = { + if (c < 97) { return false; }; + if (c > 122) { return false; }; + return true; +}; + +export fn isalpha(c: rune) bool = { + if (isupper(c)) { return true; }; + return islower(c); +}; + +export fn isalnum(c: rune) bool = { + if (isalpha(c)) { return true; }; + return isdigit(c); +}; + +// isspace — the C/Hare set: space, tab, NL, VT, FF, CR. +export fn isspace(c: rune) bool = { + if (c == 32) { return true; }; // ' ' + if (c == 9) { return true; }; // '\t' + if (c == 10) { return true; }; // '\n' + if (c == 11) { return true; }; // '\v' + if (c == 12) { return true; }; // '\f' + if (c == 13) { return true; }; // '\r' + return false; +}; + +export fn isxdigit(c: rune) bool = { + if (isdigit(c)) { return true; }; + if (c >= 65) { + if (c <= 70) { return true; }; // 'A'..'F' + }; + if (c >= 97) { + if (c <= 102) { return true; }; // 'a'..'f' + }; + return false; +}; + +// valid — `c` is in the 0..127 ASCII range. +export fn valid(c: rune) bool = { + if (c < 0) { return false; }; + if (c > 127) { return false; }; + return true; +}; + +// validstr — every byte in `s` is ASCII (0..127). +export fn validstr(s: str) bool = { + let i: i32 = 0; + for (i < s.len) { + // High-bit test rather than `> 127u8`; both cgens lower + // the bitwise form identically. The `> u8` form picks + // JA vs JG depending on signed/unsigned dispatch. + if ((s[i] & 128u8) != 0u8) { return false; }; + i += 1; + }; + return true; +}; + +// iscntrl — control chars: 0..31 and 127. +export fn iscntrl(c: rune) bool = { + if (c >= 0) { if (c <= 31) { return true; }; }; + if (c == 127) { return true; }; + return false; +}; + +// isblank — space and tab. +export fn isblank(c: rune) bool = { + if (c == 32) { return true; }; // ' ' + if (c == 9) { return true; }; // '\t' + return false; +}; + +// isprint — printable: space through '~'. +export fn isprint(c: rune) bool = { + if (c < 32) { return false; }; + if (c > 126) { return false; }; + return true; +}; + +// isgraph — printable, non-space. +export fn isgraph(c: rune) bool = { + if (c < 33) { return false; }; + if (c > 126) { return false; }; + return true; +}; + +// ispunct — printable, non-alnum, non-space. +export fn ispunct(c: rune) bool = { + if (!isgraph(c)) { return false; }; + if (isalnum(c)) { return false; }; + return true; +}; + +// tolower / toupper — fold ASCII case. Non-letters pass through. +export fn tolower(c: rune) rune = { + if (isupper(c)) { return c + 32; }; + return c; +}; + +export fn toupper(c: rune) rune = { + if (islower(c)) { return c - 32; }; + return c; +}; + +// strcasecmp — three-way ASCII case-insensitive compare. +export fn strcasecmp(a: str, b: str) i32 = { + let n: i32 = a.len; + if (b.len < n) { n = b.len; }; + let i: i32 = 0; + for (i < n) { + let ca: rune = tolower(a[i]: rune); + let cb: rune = tolower(b[i]: rune); + if (ca != cb) { return (ca - cb): i32; }; + i += 1; + }; + return a.len - b.len; +}; + +// floats — f64 classification, sign, bit-reinterpret core, and the f64 +// decompose half (subnormal-normalize + frexp). Ported from +// ref/hare/math/floats.ha (fold-1: classify/sign/bits; fold-2a: +// issubnormalf64/normalizef64/frexpf64; strconv-foundation fold-1a: +// F32 bit-layout + f32bits/f32frombits + floatinfo struct type; +// fold-1b: NAN_BITS/INF_BITS sentinels; γ-cleanup: f64info/f32info +// instances re-folded once #149 lowered &math.f64info). frexpf64's +// zero guard `n == 0f64` rides the #103 +// fix (no-decimal f64 literal now materialized into XMM) and its +// (f64, i64) tuple return rides the #105 fix (tuple f64-word read). +// The ldexp/modfrac/nextafter family stays deferred (need f64 DIVIDE). + +package math; + +// Returns the binary representation of the given f64. +// ref/hare/math/floats.ha:5. Parens around &n are load-bearing: ww's `:` +// cast binds tighter than unary `&`, so Hare's `*(&n: *u64)` would parse +// as `*(&(n: *u64))`; `(&n): *u64` reinterprets the address as intended. +export fn f64bits(n: f64) u64 = { + return *((&n): *u64); +}; + +// Returns the binary representation of the given f32. +// ref/hare/math/floats.ha:8 +export fn f32bits(n: f32) u32 = { + return *((&n): *u32); +}; + +// Returns f64 with the given binary representation. +// ref/hare/math/floats.ha:11 +export fn f64frombits(n: u64) f64 = { + return *((&n): *f64); +}; + +// Returns f32 with the given binary representation. +// ref/hare/math/floats.ha:14 +export fn f32frombits(n: u32) f32 = { + return *((&n): *f32); +}; + +// ref/hare/math/floats.ha:17,20,23 declare these as untyped int. ww has +// no untyped def (every def carries a type) and routes shift/bitwise +// through unify_arith, which rejects mixed operand types (cmd/wcc/ +// check.c:769). The bit-structure consts are used only as u64 shift +// amounts and mask widths, so they are typed u64 here — the closest +// stand-in for Hare's untyped-int adapt at those use sites. + +// The number of bits in the significand of the binary representation of f64. +export def F64_MANTISSA_BITS: u64 = 52; + +// The number of bits in the exponent of the binary representation of f64. +export def F64_EXPONENT_BITS: u64 = 11; + +// The bias of the exponent of the binary representation of f64. Subtract this +// from the exponent in the binary representation to get the actual exponent. +export def F64_EXPONENT_BIAS: u64 = 1023; + +// Mask with each bit of an f64's mantissa set. +// ref/hare/math/floats.ha:37 +export def F64_MANTISSA_MASK: u64 = (1 << F64_MANTISSA_BITS) - 1; + +// Mask with each bit of an f64's exponent set. +// ref/hare/math/floats.ha:40 +export def F64_EXPONENT_MASK: u64 = (1 << F64_EXPONENT_BITS) - 1; + +// The mask that gets an f64's sign. +// ref/hare/math/floats.ha:75 +def F64_SIGN_MASK: u64 = 1u64 << 63; + +// Mask that clears an f64's exponent field, keeping sign + mantissa. +// ref/hare/math/floats.ha:77. Hare hardcodes the 0x800FFFFFFFFFFFFF binary +// literal because its lexer can't const-fold the expression; ww's #88 +// def-const-fold can, so the readable form is kept. floats.ha:79's NOTE +// expression has an `0u64 &` upstream typo (it would yield 0); the value it +// documents is exactly ~(F64_EXPONENT_MASK << F64_MANTISSA_BITS). +def F64_EXP_REMOVAL_MASK: u64 = ~(F64_EXPONENT_MASK << F64_MANTISSA_BITS); + +// The f64 bit pattern whose exponent field evaluates to zero (0.5 scale). +// ref/hare/math/floats.ha:84 +def F64_EXP_ZERO: u64 = (F64_EXPONENT_BIAS - 1) << F64_MANTISSA_BITS; + +// F32 bit-structure constants. ref/hare/math/floats.ha:27,30,33 declare +// these as untyped int; ww has no untyped def, so they ride u32 (matching +// the u32 bit container, the same way the F64 family rides u64 — see +// the note above F64_MANTISSA_BITS). + +// The number of bits in the significand of the binary representation of f32. +// ref/hare/math/floats.ha:27 +export def F32_MANTISSA_BITS: u32 = 23u32; + +// The number of bits in the exponent of the binary representation of f32. +// ref/hare/math/floats.ha:30 +export def F32_EXPONENT_BITS: u32 = 8u32; + +// The bias of the exponent of the binary representation of f32. Subtract this +// from the exponent in the binary representation to get the actual exponent. +// ref/hare/math/floats.ha:33 +export def F32_EXPONENT_BIAS: u32 = 127u32; + +// Mask with each bit of an f32's mantissa set. +// ref/hare/math/floats.ha:43 +export def F32_MANTISSA_MASK: u32 = (1u32 << F32_MANTISSA_BITS) - 1u32; + +// Mask with each bit of an f32's exponent set. +// ref/hare/math/floats.ha:46 +export def F32_EXPONENT_MASK: u32 = (1u32 << F32_EXPONENT_BITS) - 1u32; + +// The mask that gets an f32's sign. +// ref/hare/math/floats.ha:87 +def F32_SIGN_MASK: u32 = 1u32 << 31; + +// Mask that clears an f32's exponent field, keeping sign + mantissa. +// ref/hare/math/floats.ha:92. Hare hardcodes the binary literal (its +// lexer can't const-fold the expression); ww's #88 def-const-fold can, +// so the readable form is kept (same call as F64_EXP_REMOVAL_MASK). +def F32_EXP_REMOVAL_MASK: u32 = ~(F32_EXPONENT_MASK << F32_MANTISSA_BITS); + +// The f32 bit pattern whose exponent field evaluates to zero (0.5 scale). +// ref/hare/math/floats.ha:95 +def F32_EXP_ZERO: u32 = (F32_EXPONENT_BIAS - 1u32) << F32_MANTISSA_BITS; + +// floatinfo — IEEE-754 shape parameters for a binary float type, passed +// to width-generic helpers in strconv (eisel_lemire, floatbits, hex_to_bits, +// mkfloat). ref/hare/math/floats.ha:101. Hare's `int` maps to ww's `int` +// (machine word, 8B; project_int_machine_word_derived_limits), so the +// expbias field stays `int` — that keeps the fold-4 stof port byte-for-byte +// against ref/hare/strconv/stof.ha:248,288 (`let e: int = 0` arithmetic +// against `f.expbias` of the same type, no cast at use site). +export type floatinfo = struct { + // Bits in significand. + mantbits: u64, + // Bits in exponent. + expbits: u64, + // Bias of exponent. + expbias: int, + // Mask for mantissa. + mantmask: u64, + // Mask for exponent. + expmask: u64, +}; + +// floatinfo instances for the f64 / f32 types, consumed by the +// width-generic strconv helpers via &math.f64info (cross-module +// address-of, lowered since #149). ref/hare/math/floats.ha:117,126. +// Hare spells the masks (1 << 52) - 1 / (1 << 23) - 1; the #129 A.2 +// struct-composite static-init path folds only bare-literal field +// initializers, not const-fold expressions, so the value-identical hex +// literals are used here (0xFFFFFFFFFFFFF == (1<<52)-1, 0x7FFFFF == +// (1<<23)-1 — same hex-literal style as the NAN_BITS/INF_BITS sentinels +// below). expbias rides `int` (the field type) with no suffix. +export def f64info: floatinfo = floatinfo { + mantbits = 52u64, + expbits = 11u64, + expbias = 1023, + mantmask = 0xFFFFFFFFFFFFFu64, + expmask = 0x7FFu64, +}; + +export def f32info: floatinfo = floatinfo { + mantbits = 23u64, + expbits = 8u64, + expbias = 127, + mantmask = 0x7FFFFFu64, + expmask = 0xFFu64, +}; + +// IEEE-754 quiet-NaN and positive-Infinity f64 bit sentinels. +// ref/hare/math/floats.ha:137,141. Hare exports `def NAN = 0.0/0.0;` and +// `def INF = 1.0/0.0;` (untyped float def-fold); ww's cgen doesn't lower +// `def: f64 = expr;` (the symbol comes out undefined at link time — see +// #129). Callers materialize the f64 sentinel via f64frombits(NAN_BITS) +// / f64frombits(INF_BITS); same bit-exact value, one extra reinterpret. +// 0x7FF8000000000000 is the IEEE-754 binary64 quiet-NaN (sign=0, exp= +// all-ones, mantissa MSB=1, rest=0); 0x7FF0000000000000 is +Infinity +// (sign=0, exp=all-ones, mantissa=0). Re-fold to `def NAN: f64 = ...` +// when #129 closes (γ-cleanup pattern per amalloc-drop precedent). +export def NAN_BITS: u64 = 0x7FF8000000000000u64; +export def INF_BITS: u64 = 0x7FF0000000000000u64; + +// Returns true if the given floating-point number is NaN. +// ref/hare/math/floats.ha:144 (Hare's expression body inlined into a +// block: ww has no expression-bodied fn form, only brace blocks). +export fn isnan(n: f64) bool = { + return n != n; +}; + +// Returns true if the given floating-point number is infinite. +// ref/hare/math/floats.ha:147 +export fn isinf(n: f64) bool = { + const bits = f64bits(n); + const mant = bits & F64_MANTISSA_MASK; + const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK; + return exp == F64_EXPONENT_MASK && mant == 0; +}; + +// Returns true if the given f64 is subnormal. +// ref/hare/math/floats.ha:179 +export fn issubnormalf64(n: f64) bool = { + const bits = f64bits(n); + const mant = bits & F64_MANTISSA_MASK; + const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK; + return exp == 0 && mant != 0; +}; + +// Returns the absolute value of f64 n. +// ref/hare/math/floats.ha:195 +export fn absf64(n: f64) f64 = { + if (isnan(n)) { + return n; + }; + return f64frombits(f64bits(n) & ~F64_SIGN_MASK); +}; + +// Returns 1 if x is positive and -1 if x is negative. Note that zero is also +// signed. +// ref/hare/math/floats.ha:212 +export fn signf64(x: f64) i64 = { + if (f64bits(x) & F64_SIGN_MASK == 0) { + return 1i64; + } else { + return -1i64; + }; +}; + +// Returns whether or not x is positive. +// ref/hare/math/floats.ha:231 +export fn ispositivef64(x: f64) bool = { + return signf64(x) == 1i64; +}; + +// Returns whether or not x is negative. +// ref/hare/math/floats.ha:237 +export fn isnegativef64(x: f64) bool = { + return signf64(x) == -1i64; +}; + +// Returns x, but with the sign of y. +// ref/hare/math/floats.ha:243 +export fn copysignf64(x: f64, y: f64) f64 = { + return f64frombits((f64bits(x) & ~F64_SIGN_MASK) | + (f64bits(y) & F64_SIGN_MASK)); +}; + +// Takes a potentially subnormal f64 n and returns a normal f64 normal_float +// and an exponent exp such that n == normal_float * 2^{exp}. +// ref/hare/math/floats.ha:256 +export fn normalizef64(n: f64) (f64, i64) = { + if (issubnormalf64(n)) { + const factor = 1i64 << (F64_MANTISSA_BITS: i64); + const normal_float = (n * (factor: f64)); + return (normal_float, -(F64_MANTISSA_BITS: i64)); + }; + return (n, 0); +}; + +// Breaks a f64 down into its mantissa and exponent. The mantissa will be +// between 0.5 and 1. +// ref/hare/math/floats.ha:278 +export fn frexpf64(n: f64) (f64, i64) = { + if (isnan(n) || isinf(n) || n == 0f64) { + return (n, 0); + }; + const normalized = normalizef64(n); + const normal_float = normalized.0; + const normalization_exp = normalized.1; + const bits = f64bits(normal_float); + const raw_exp: u64 = (bits >> F64_MANTISSA_BITS) & F64_EXPONENT_MASK; + const exp: i64 = normalization_exp + + (raw_exp: i64) - (F64_EXPONENT_BIAS: i64) + 1; + const mantissa: f64 = + f64frombits((bits & F64_EXP_REMOVAL_MASK) | F64_EXP_ZERO); + return (mantissa, exp); +}; + +// math — numeric helpers. Subset of Hare's math::; only the absolute- +// value pair for the signed integer types we currently care about. The +// return type is unsigned so that abs(I32_MIN) doesn't overflow. + +package math; + +export fn absi32(n: i32) u32 = { + if (n < 0) { return (-n): u32; }; + return n: u32; +}; + +export fn absi64(n: i64) u64 = { + if (n < 0) { return (-n): u64; }; + return n: u64; +}; // types — integer limits. Mirrors Hare's types::limits (I8_MAX, …) // platform-fixed for amd64. Numeric helpers live in lib/math, matching @@ -3122,6 +3453,1406 @@ export fn rpad(s: str, p: rune, maxlen: i32) str = { return frombytes(buf); }; +// strconv — string-to-float. Mirrors ref/hare/strconv/stof.ha +// (Hare in turn adapts Go): Eisel-Lemire fast path [1] with the +// Simple-Decimal-Conversion slow path [2] (decimal.ww) as fallback. +// [1]: https://nigeltao.github.io/blog/2020/eisel-lemire.html +// [2]: https://nigeltao.github.io/blog/2020/parse-number-f64-simple.html +// +// The Eisel-Lemire fast path (`eisel_lemire` + the `powers_of_ten` +// table in stof_data.ww + the three call sites: floatbits's d.nd<=19 +// block, stof64/stof32's !truncated block) is a pure speed +// optimisation — it returns the same correctly-rounded value the +// decimal slow path (decimal_parse → floatbits) computes, or void to +// defer. Its prereqs landed: the 2D `[596][2]u64` static-init + +// double-index read (#156) and the tagged float-variant return-pack +// (#157, which the public `(f64|invalid|overflow)` return needs). +// +// Spelling divergences from Hare (mechanical, ww parser/cgen shape): +// - str scan index rides `i32` (ww `str.len: i32` + `invalid = !i32` +// payload), not Hare's `size`/`len(s)`. lib CLAUDE.md str-index note. +// - char literals kept faithful (`buf[i] == '.'`, `c - '0'`); probed +// byte-id + value-correct both stages. +// - Hare `?` error-propagation → nested statement-`match` with all- +// return arms + a `case void => void` continuation. ww's `?` +// lowering and a bound `match`-expression with mixed yield/return +// arms both diverge cs≠ww (the latter wwstage-checker-rejected); +// strconv.ww's stoi32 set the explicit-match precedent. +// - Hare `for (cond; afterthought)` 2-clause + `continue` → ww +// 2-clause `for (cond)` with the afterthought inlined at body end +// AND before each `continue` (ww has no empty-init 3-clause +// `for (; c; p)`; #138 post-skip is dodged since 2-clause has no +// post). decimal.ww set the inline-afterthought precedent. +// - Hare `if`/`switch`-expression yield → explicit if-statements + +// pre-bound scalar locals (ww has no expression-bodied if). +// - Hare fn-pointer-in-tuple + `switch yield` selecting the digit +// predicate in fast_parse → a `base==HEX` bool + an `isdigitbase` +// helper that branches to ascii.isdigit/isxdigit (no fn-ptr, no +// tuple, no switch). +// - struct-param field MUTATION (hex_to_bits mutates its by-value +// `p`) → copy p's fields to scalar locals at entry; ww miscompiles +// + diverges on writing a by-value struct param's fields (filed). +// - default arg dropped: Hare `b: base = base::DEC` → callers pass +// base explicitly (no lib fn ships a default arg; strconv.ww +// stoi64 precedent). The base param is normalised through a local +// `bb` (param reassignment avoided). +// - `math::NAN`/`math::INF` (f32) absent in ww math → materialised +// via f32frombits of the IEEE-754 f32 bit patterns (same honest +// construction as math/floats.ww's NAN_BITS/INF_BITS). +// - narrowing int→i32 assignments carry explicit casts (ww `int` is +// an 8B machine word; project_int_machine_word_derived_limits). +// - `r128`/`u128mul` live here (fold-4 is first consumer); fold-5 +// ftos (Ryū) shares them in-package. + +package strconv; + +import ascii; +import math; +import os; +import strings; + +// ref/hare/strconv/ftos_ryu.ha:12. 64×64→128 result halves. +type r128 = struct { + hi: u64, + lo: u64, +}; + +// ref/hare/strconv/ftos_ryu.ha:18. 64×64→128 via 32-bit decomposition +// (Hare's own "TODO: use 128-bit integers when implemented" — ww has +// no u128; the decomposition is the portable shape both stages agree +// on). Comma let-bindings split per decimal.ww divergence. +fn u128mul(a: u64, b: u64) r128 = { + let a0: u64 = (a: u32): u64; + let a1: u64 = a >> 32u64; + let b0: u64 = (b: u32): u64; + let b1: u64 = b >> 32u64; + let p00: u64 = a0 * b0; + let p01: u64 = a0 * b1; + let p10: u64 = a1 * b0; + let p11: u64 = a1 * b1; + let p00_lo: u64 = (p00: u32): u64; + let p00_hi: u64 = p00 >> 32u64; + let mid1: u64 = p10 + p00_hi; + let mid1_lo: u64 = (mid1: u32): u64; + let mid1_hi: u64 = mid1 >> 32u64; + let mid2: u64 = p01 + mid1_lo; + let mid2_lo: u64 = (mid2: u32): u64; + let mid2_hi: u64 = mid2 >> 32u64; + let r_hi: u64 = p11 + mid1_hi + mid2_hi; + let r_lo: u64 = (mid2_lo << 32u64) | p00_lo; + return r128 { hi = r_hi, lo = r_lo }; +}; + +// ref/hare/strconv/stof.ha:14. +fn todig(c: u8) u8 = { + if ('0' <= c && c <= '9') { return c - '0'; }; + if ('a' <= c && c <= 'f') { return c - 'a' + 10u8; }; + if ('A' <= c && c <= 'F') { return c - 'A' + 10u8; }; + abort("strconv.todig: unreachable"); + return 0u8; // unreachable; rt_abort is void-typed (path-cov) +}; + +@symbol("rt_abort") fn abort(msg: str) void; + +// ref/hare/strconv/stof.ha:25. +type fast_parsed_float = struct { + mantissa: u64, + exponent: i32, + negative: bool, + truncated: bool, +}; + +// Digit-class predicate selector for fast_parse — replaces Hare's +// fn-pointer-in-tuple (`&ascii::isdigit` / `&ascii::isxdigit`). +fn isdigitbase(c: rune, ishex: bool) bool = { + if (ishex) { return ascii.isxdigit(c); }; + return ascii.isdigit(c); +}; + +// ref/hare/strconv/stof.ha:32. +fn fast_parse(s: str, b: base) (fast_parsed_float | invalid) = { + let buf: []u8 = strings.toutf8(s); + let i: i32 = 0; + let neg: bool = false; + let trunc: bool = false; + if (buf[i] == '-') { + neg = true; + i += 1; + } else if (buf[i] == '+') { + i += 1; + }; + + let ishex: bool = (b == base.HEX); + let expchr: rune = 'e'; + let max_ndmant: int = 19; + if (ishex) { + expchr = 'p'; + max_ndmant = 16; + }; + let bnum: u64 = (b: i32): u64; + + let sawdot: bool = false; + let sawdigits: bool = false; + let nd: int = 0; + let ndmant: int = 0; + let dp: int = 0; + let mant: u64 = 0u64; + let exp: i32 = 0i32; + for (i < s.len) { + if (buf[i] == '.') { + if (sawdot) { return i: invalid; }; + sawdot = true; + dp = nd; + } else if (isdigitbase(buf[i]: rune, ishex)) { + sawdigits = true; + if (buf[i] == '0' && nd == 0) { + dp -= 1; + i += 1; + continue; + }; + nd += 1; + if (ndmant < max_ndmant) { + mant = mant * bnum + (todig(buf[i]): u64); + ndmant += 1; + } else if (buf[i] != '0') { + trunc = true; + }; + } else { + break; + }; + i += 1; + }; + if (!sawdigits) { return i: invalid; }; + if (!sawdot) { + dp = nd; + }; + if (b == base.HEX) { + dp *= 4; + ndmant *= 4; + }; + if (i < s.len && ascii.tolower(buf[i]: rune) == expchr) { + i += 1; + if (i >= s.len) { return i: invalid; }; + let expsign: int = 1; + if (buf[i] == '+') { + i += 1; + } else if (buf[i] == '-') { + expsign = -1; + i += 1; + }; + if (i >= s.len || !ascii.isdigit(buf[i]: rune)) { + return i: invalid; + }; + let e: int = 0; + for (i < s.len && ascii.isdigit(buf[i]: rune)) { + if (e < 10000) { + e = e * 10 + ((buf[i] - '0'): int); + }; + i += 1; + }; + dp += e * expsign; + } else if (b == base.HEX) { + return i: invalid; // hex floats must have an exponent + }; + if (i != s.len) { return i: invalid; }; + if (mant != 0u64) { + exp = (dp - ndmant): i32; + }; + return fast_parsed_float { + mantissa = mant, + exponent = exp, + negative = neg, + truncated = trunc, + }; +}; + +// ref/hare/strconv/stof.ha:115. Fills the slow-path decimal `d`. +fn decimal_parse(d: *decimal, s: str) (void | invalid) = { + let i: i32 = 0; + let buf: []u8 = strings.toutf8(s); + d.negative = false; + d.truncated = false; + if (buf[0] == '+') { + i += 1; + } else if (buf[0] == '-') { + d.negative = true; + i += 1; + }; + let sawdot: bool = false; + let sawdigits: bool = false; + for (i < s.len) { + if (buf[i] == '.') { + if (sawdot) { return i: invalid; }; + sawdot = true; + d.dp = (d.nd: i32); + } else if (ascii.isdigit(buf[i]: rune)) { + sawdigits = true; + if (buf[i] == '0' && d.nd == (0u64: size)) { + d.dp -= 1; + i += 1; + continue; + }; + if (d.nd < (len(d.digits): size)) { + d.digits[d.nd] = buf[i] - '0'; + d.nd += (1u64: size); + } else if (buf[i] != '0') { + d.truncated = true; + }; + } else { + break; + }; + i += 1; + }; + if (!sawdigits) { return i: invalid; }; + if (!sawdot) { + d.dp = (d.nd: i32); + }; + if (i < s.len && (buf[i] == 'e' || buf[i] == 'E')) { + i += 1; + if (i >= s.len) { return i: invalid; }; + let expsign: int = 1; + if (buf[i] == '+') { + i += 1; + } else if (buf[i] == '-') { + expsign = -1; + i += 1; + }; + if (i >= s.len || !ascii.isdigit(buf[i]: rune)) { + return i: invalid; + }; + let e: int = 0; + for (i < s.len && ascii.isdigit(buf[i]: rune)) { + if (e < 10000) { + e = e * 10 + ((buf[i] - '0'): int); + }; + i += 1; + }; + d.dp += (e * expsign): i32; + }; + if (i != s.len) { return i: invalid; }; + return; +}; + +// ref/hare/strconv/stof.ha:173. Count of leading zero bits in n>0. +fn leading_zeroes(n: u64) uint = { + os.assert(n > 0u64, "strconv.leading_zeroes: n == 0"); + let b: u64 = 0u64; + if ((n & 0xFFFFFFFF00000000u64) > 0u64) { + n >>= 32u64; + b |= 32u64; + }; + if ((n & 0xFFFF0000u64) > 0u64) { + n >>= 16u64; + b |= 16u64; + }; + if ((n & 0xFF00u64) > 0u64) { + n >>= 8u64; + b |= 8u64; + }; + if ((n & 0xF0u64) > 0u64) { + n >>= 4u64; + b |= 4u64; + }; + if ((n & 0xCu64) > 0u64) { + n >>= 2u64; + b |= 2u64; + }; + if ((n & 0x2u64) > 0u64) { + n >>= 1u64; + b |= 1u64; + }; + return ((63u64 - b): uint); +}; + +// ref/hare/strconv/stof.ha:203. Eisel-Lemire fast path: a correctly- +// rounded f64/f32 from (mantissa, exp10) when the 128-bit product is +// unambiguous, else void → caller falls to the decimal slow path. +// Divergences at-site: `mantissa <<= clz` (scalar-param mutate) → local +// `mnt`; whole-struct local reassign `x = merged` copies only the first +// word in cgen → per-field `x.hi = …; x.lo = …` (#155); `po10 = +// powers_of_ten[i]` row-bind → direct double-index (#155, A2); bitwise- +// vs-compare fully parenthesised; comma let-bindings split. +fn eisel_lemire( + mantissa: u64, + exp10: i32, + neg: bool, + f: *math.floatinfo, +) (u64 | void) = { + if (mantissa == 0u64 || exp10 > 288 || exp10 < -307) { + return; + }; + let idx: i32 = exp10 + 307; + let clz: uint = leading_zeroes(mantissa); + let mnt: u64 = mantissa << (clz: u64); + let shift: u64 = 64u64 - f.mantbits - 3u64; + let mask: u64 = (1u64 << shift) - 1u64; + // log(10)/log(2) ≈ 217706 / 65536; x / 65536 = x >> 16. + let exp: int = (217706 * (exp10: int)) >> 16; + let e2: u64 = ((exp + f.expbias + 64): u64) - (clz: u64); + let x: r128 = u128mul(mnt, powers_of_ten[idx][1]); + if ((x.hi & mask) == mask && (x.lo + mnt) < mnt) { + let y: r128 = u128mul(mnt, powers_of_ten[idx][0]); + let merged: r128 = r128 { hi = x.hi, lo = x.lo + y.hi }; + if (merged.lo < x.lo) { + // local-struct-field compound-assign drops the load in + // wwstage (sets =1, not +=1) — explicit form, byte-id. + merged.hi = merged.hi + 1u64; + }; + if ((merged.hi & mask) == mask && (merged.lo + 1u64) == 0u64 && + (y.lo + mnt) < mnt) { + return; + }; + x.hi = merged.hi; + x.lo = merged.lo; + }; + let msb: u64 = x.hi >> 63u64; + let mant: u64 = x.hi >> (msb + shift); + e2 -= 1u64 ^ msb; + if (x.lo == 0u64 && (x.hi & mask) == 0u64 && (mant & 3u64) == 1u64) { + return; + }; + mant += mant & 1u64; + mant >>= 1u64; + if ((mant >> (f.mantbits + 1u64)) > 0u64) { + mant >>= 1u64; + e2 += 1u64; + }; + if (e2 <= 0u64 || e2 >= (1u64 << f.expbits) - 1u64) { + return; + }; + return mkfloat(mant, (e2: uint), neg, f); +}; + +// ref/hare/strconv/stof.ha:247. Slow-path: decimal `d` → IEEE bits. +fn floatbits(d: *decimal, f: *math.floatinfo) (u64 | overflow) = { + let e: int = 0; + let m: u64 = 0u64; + let powtab: [19]i8 = [ + 0i8, 3i8, 6i8, 9i8, 13i8, 16i8, 19i8, 23i8, 26i8, 29i8, + 33i8, 36i8, 39i8, 43i8, 46i8, 49i8, 53i8, 56i8, 59i8, + ]; + if (d.nd == (0u64: size) || d.dp < -326) { + if (d.negative) { + return mkfloat(0u64, (0u32: uint), d.negative, f); + }; + return 0u64; + } else if (d.dp > 310) { + return overflow{}; + }; + if (d.nd <= (19u64: size)) { + let dmant: u64 = 0u64; + let i: size = (0u64: size); + for (i < d.nd) { + dmant = 10u64 * dmant + (d.digits[i]: u64); + i += (1u64: size); + }; + let exp10: i32 = d.dp - (d.nd: i32); + match (eisel_lemire(dmant, exp10, d.negative, f)) { + case let r: u64 => { return r; }; + case void => void; + }; + }; + for (d.dp > 0) { + let n: int = 0; + if ((d.dp: uint) >= (len(powtab): uint)) { + n = (maxshift: int); + } else { + n = (powtab[d.dp]: int); + }; + decimal_shift(d, -n); + e += n; + }; + for (d.dp <= 0) { + let n: int = 0; + if (d.dp == 0) { + if (d.digits[0] >= 5u8) { break; }; + if (d.digits[0] < 2u8) { n = 2; } else { n = 1; }; + } else if ((-d.dp) >= (len(powtab): i32)) { + n = (maxshift: int); + } else { + n = (powtab[-d.dp]: int); + }; + decimal_shift(d, n); + e -= n; + }; + e -= 1; + if (e <= -f.expbias + 1) { + let nn: int = -f.expbias - e + 1; + decimal_shift(d, -nn); + e += nn; + }; + if (e + f.expbias >= ((1u64 << f.expbits): int) - 1) { + return overflow{}; + }; + decimal_shift(d, (f.mantbits: int) + 1); + m = decimal_round(d); + if (m == (2u64 << f.mantbits)) { + m >>= 1u64; + e += 1; + if (e + f.expbias >= ((1u64 << f.expbits): int) - 1) { + return overflow{}; + }; + }; + if ((m & (1u64 << f.mantbits)) == 0u64) { + e = -f.expbias; + }; + return mkfloat(m, ((e + f.expbias): uint), d.negative, f); +}; + +// ref/hare/strconv/stof.ha:311. Assemble sign|exp|mantissa. +fn mkfloat(m: u64, e: uint, negative: bool, f: *math.floatinfo) u64 = { + let n: u64 = m & ((1u64 << f.mantbits) - 1u64); + n |= ((e: u64) & ((1u64 << f.expbits) - 1u64)) << f.mantbits; + if (negative) { + n |= 1u64 << (f.mantbits + f.expbits); + }; + return n; +}; + +// ref/hare/strconv/stof.ha:320. Exact f64 powers of ten 1e0..1e22 (all +// exactly representable; see stof64exact). +let f64pow10: [23]f64 = [ + 1.0e0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, + 1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, + 1.0e19, 1.0e20, 1.0e21, 1.0e22, +]; + +// ref/hare/strconv/stof.ha:326. +fn stof64exact(mant: u64, exp: i32, neg: bool) (f64 | void) = { + if (mant >> math.F64_MANTISSA_BITS != 0u64) { return; }; + let n: f64 = (mant: i64): f64; + if (neg) { + n = -n; + }; + if (exp == 0i32) { + return n; + }; + if (-22i32 <= exp && exp <= 22i32) { + if (exp >= 0i32) { + // f64 compound-assign mis-lowers in cgen — explicit + // form (strconv.ww f64tos precedent). + n = n * f64pow10[exp]; + } else { + n = n / f64pow10[-exp]; + }; + } else { + return; + }; + return n; +}; + +// ref/hare/strconv/stof.ha:345. Exact f32 powers of ten 1e0..1e10. +let f32pow10: [11]f32 = [ + 1.0e0f32, 1.0e1f32, 1.0e2f32, 1.0e3f32, 1.0e4f32, 1.0e5f32, 1.0e6f32, + 1.0e7f32, 1.0e8f32, 1.0e9f32, 1.0e10f32, +]; + +// ref/hare/strconv/stof.ha:349. +fn stof32exact(mant: u64, exp: i32, neg: bool) (f32 | void) = { + if (mant >> (math.F32_MANTISSA_BITS: u64) != 0u64) { return; }; + let n: f32 = (mant: i32): f32; + if (neg) { + n = -n; + }; + if (exp == 0i32) { + return n; + }; + if (-10i32 <= exp && exp <= 10i32) { + if (exp >= 0i32) { + // f32 compound-assign mis-lowers in cgen — explicit form. + n = n * f32pow10[exp]; + } else { + n = n / (f64pow10[-exp]: f32); + }; + } else { + return; + }; + return n; +}; + +// ref/hare/strconv/stof.ha:369. Adapted from Go's atofHex. The by-value +// `p` is mutated in Hare; ww copies its fields to scalar locals (struct +// param field-write miscompiles + diverges — filed). +fn hex_to_bits(p: fast_parsed_float, info: *math.floatinfo) (u64 | overflow) = { + let pmant: u64 = p.mantissa; + let pexp: i32 = p.exponent; + let pneg: bool = p.negative; + let ptrunc: bool = p.truncated; + let max_exp: int = ((1u64 << info.expbits): int) - info.expbias - 2; + let min_exp: int = -info.expbias + 1; + pexp += (info.mantbits: i32); + + // Shift left until a leading 1 bit followed by mantbits + 2 rounding. + for (pmant != 0u64 && pmant >> (info.mantbits + 2u64) == 0u64) { + pmant <<= 1u64; + pexp -= 1; + }; + if (ptrunc) { + pmant |= 1u64; + }; + // Too many bits: shift right (sticky-or the dropped bit). + for (pmant >> (3u64 + info.mantbits) != 0u64) { + pmant = (pmant >> 1u64) | (pmant & 1u64); + pexp += 1; + }; + // Denormalise if the exponent is small. + for (pmant > 1u64 && pexp < (min_exp: i32) - 2) { + pmant = (pmant >> 1u64) | (pmant & 1u64); + pexp += 1; + }; + // Round to even. + let round: u64 = pmant & 3u64; + pmant >>= 2u64; + round |= pmant & 1u64; + pexp += 2; + if (round == 3u64) { + pmant += 1u64; + if (pmant == 1u64 << (1u64 + info.mantbits)) { + pmant >>= 1u64; + pexp += 1; + }; + }; + // Denormal or zero. + if (pmant >> info.mantbits == 0u64) { + pexp = (-info.expbias): i32; + }; + if (pexp > (max_exp: i32)) { + return overflow{}; + }; + let bits: u64 = pmant & info.mantmask; + bits |= (((pexp + (info.expbias: i32)): u64) & info.expmask) << info.mantbits; + if (pneg) { + bits |= 1u64 << (info.mantbits + info.expbits); + }; + return bits; +}; + +// ref/hare/strconv/stof.ha:425. "nan"/"infinity"/±"infinity", +// case-insensitive. ww math has no f32 NAN/INF consts → f32frombits of +// the IEEE-754 f32 bit patterns (qNaN 0x7FC00000, ±Inf 0x7F800000 / +// 0xFF800000). +fn special(s: str) (f32 | void) = { + if (ascii.strcasecmp(s, "nan") == 0) { + return math.f32frombits(0x7FC00000u32); + } else if (ascii.strcasecmp(s, "infinity") == 0) { + return math.f32frombits(0x7F800000u32); + } else if (ascii.strcasecmp(s, "+infinity") == 0) { + return math.f32frombits(0x7F800000u32); + } else if (ascii.strcasecmp(s, "-infinity") == 0) { + return math.f32frombits(0xFF800000u32); + }; + return; +}; + +// ref/hare/strconv/stof.ha:445. Parse `s` as f64 (base DEC or HEX). See +// the module note: the EL fast path is HELD; the decimal fallback gives +// correct results meanwhile. +export fn stof64(s: str, b: base) (f64 | invalid | overflow) = { + let bb: base = b; + if (bb == base.DEFAULT) { + bb = base.DEC; + } else if (bb == base.HEX_LOWER) { + bb = base.HEX; + }; + os.assert(bb == base.DEC || bb == base.HEX, + "strconv.stof64: base must be DEC or HEX"); + + if (s.len == 0) { + return 0: invalid; + }; + + match (special(s)) { + case let f: f32 => { return (f: f64); }; + case void => void; + }; + + match (fast_parse(s, bb)) { + case let p: fast_parsed_float => { + if (bb == base.HEX) { + match (hex_to_bits(p, &math.f64info)) { + case let bits: u64 => { return math.f64frombits(bits); }; + case let eo: overflow => { return eo; }; + }; + } else if (!p.truncated) { + match (stof64exact(p.mantissa, p.exponent, p.negative)) { + case let n: f64 => { return n; }; + case void => void; + }; + match (eisel_lemire(p.mantissa, p.exponent, p.negative, + &math.f64info)) { + case let n: u64 => { return math.f64frombits(n); }; + case void => void; + }; + }; + let d = decimal { ... }; + match (decimal_parse(&d, s)) { + case let ei: invalid => { return ei; }; + case void => void; + }; + match (floatbits(&d, &math.f64info)) { + case let n: u64 => { return math.f64frombits(n); }; + case let eo: overflow => { return eo; }; + }; + }; + case let ei: invalid => { return ei; }; + }; + return 0: invalid; // unreachable (path-cov) +}; + +// ref/hare/strconv/stof.ha:491. Parse `s` as f32 (base DEC or HEX). +export fn stof32(s: str, b: base) (f32 | invalid | overflow) = { + let bb: base = b; + if (bb == base.DEFAULT) { + bb = base.DEC; + } else if (bb == base.HEX_LOWER) { + bb = base.HEX; + }; + os.assert(bb == base.DEC || bb == base.HEX, + "strconv.stof32: base must be DEC or HEX"); + + if (s.len == 0) { + return 0: invalid; + }; + + match (special(s)) { + case let f: f32 => { return f; }; + case void => void; + }; + + match (fast_parse(s, bb)) { + case let p: fast_parsed_float => { + if (bb == base.HEX) { + match (hex_to_bits(p, &math.f32info)) { + case let bits: u64 => { + return math.f32frombits(bits: u32); + }; + case let eo: overflow => { return eo; }; + }; + } else if (!p.truncated) { + match (stof32exact(p.mantissa, p.exponent, p.negative)) { + case let n: f32 => { return n; }; + case void => void; + }; + match (eisel_lemire(p.mantissa, p.exponent, p.negative, + &math.f32info)) { + case let n: u64 => { return math.f32frombits(n: u32); }; + case void => void; + }; + }; + let d = decimal { ... }; + match (decimal_parse(&d, s)) { + case let ei: invalid => { return ei; }; + case void => void; + }; + match (floatbits(&d, &math.f32info)) { + case let n: u64 => { return math.f32frombits(n: u32); }; + case let eo: overflow => { return eo; }; + }; + }; + case let ei: invalid => { return ei; }; + }; + return 0: invalid; // unreachable (path-cov) +}; + +// strconv — stof/ftos lookup tables. Mirrors ref/hare/strconv/stof_data.ha +// byte-exact. Pure-data fold (strconv #106 fold-2, was fold-3 before drew +// re-sequenced 2026-05-26): no logic, exercised transitively when fold-3's +// `leftshift_newdigits` lands (ref/hare/strconv/decimal.ha:35). +// +// ww uses module-level `let` for compile-time array data (ref/hare/strconv +// `const` has no ww keyword equivalent; lib/encoding/utf8/utf8.ww:48 sets +// the precedent with [2048]i8 dfa). Literal suffixes (`u16`, `u8`) are +// required because cstage rejects bare integer literals in `[N]u8`/`[N]u16` +// init while wwstage accepts them; the suffixed form is the only shape +// both stages agree on (candidate #130). +// +// `powers_of_ten: [596][2]u64` (ref/hare/strconv/stof_data.ha:73) is the +// Eisel-Lemire fast-path table (consumed by stof.ww's eisel_lemire); it +// lands here in fold-4 alongside its consumer, indexed `[exp10 + 307]` +// for exp10 in [-307, 288]. Faithful 2D `[596][2]u64` (the {hi,lo} pair +// IS the 128-bit truncated power-of-ten; rule-12, not flattened) — the +// 2D module-level static-init + double-index read it needs landed in +// #156 (cbeffea). See the table at the foot of this file. + +package strconv; + +// ref/hare/strconv/stof_data.ha:4. Powers-of-five decimal-expansion +// metadata for `leftshift_newdigits` (decimal.ha:35). The top 5 bits +// of each entry are the new-digit-count `nn`; the low 11 bits index +// `pow5_table` for the digits themselves. +let left_shift_table: [65]u16 = [ + 0x0000u16, 0x0800u16, 0x0801u16, 0x0803u16, 0x1006u16, 0x1009u16, 0x100Du16, 0x1812u16, 0x1817u16, + 0x181Du16, 0x2024u16, 0x202Bu16, 0x2033u16, 0x203Cu16, 0x2846u16, 0x2850u16, 0x285Bu16, 0x3067u16, + 0x3073u16, 0x3080u16, 0x388Eu16, 0x389Cu16, 0x38ABu16, 0x38BBu16, 0x40CCu16, 0x40DDu16, 0x40EFu16, + 0x4902u16, 0x4915u16, 0x4929u16, 0x513Eu16, 0x5153u16, 0x5169u16, 0x5180u16, 0x5998u16, 0x59B0u16, + 0x59C9u16, 0x61E3u16, 0x61FDu16, 0x6218u16, 0x6A34u16, 0x6A50u16, 0x6A6Du16, 0x6A8Bu16, 0x72AAu16, + 0x72C9u16, 0x72E9u16, 0x7B0Au16, 0x7B2Bu16, 0x7B4Du16, 0x8370u16, 0x8393u16, 0x83B7u16, 0x83DCu16, + 0x8C02u16, 0x8C28u16, 0x8C4Fu16, 0x9477u16, 0x949Fu16, 0x94C8u16, 0x9CF2u16, 0x051Cu16, 0x051Cu16, + 0x051Cu16, 0x051Cu16, +]; + +// ref/hare/strconv/stof_data.ha:15. Decimal digits of 5^k for k=1..60, +// concatenated. Indexed via `left_shift_table` (above); each shift k +// reads its `pow5_b - pow5_a` digits starting at `pow5_a`. +let pow5_table: [0x051C]u8 = [ + 5u8, 2u8, 5u8, 1u8, 2u8, 5u8, 6u8, 2u8, 5u8, 3u8, 1u8, 2u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 3u8, + 9u8, 0u8, 6u8, 2u8, 5u8, 1u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 9u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, 4u8, 8u8, 8u8, 2u8, 8u8, + 1u8, 2u8, 5u8, 2u8, 4u8, 4u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 1u8, 2u8, 2u8, 0u8, 7u8, 0u8, 3u8, 1u8, 2u8, 5u8, 6u8, 1u8, + 0u8, 3u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 3u8, 0u8, 5u8, 1u8, 7u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 1u8, 5u8, 2u8, 5u8, 8u8, + 7u8, 8u8, 9u8, 0u8, 6u8, 2u8, 5u8, 7u8, 6u8, 2u8, 9u8, 3u8, 9u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, 3u8, 8u8, 1u8, 4u8, 6u8, + 9u8, 7u8, 2u8, 6u8, 5u8, 6u8, 2u8, 5u8, 1u8, 9u8, 0u8, 7u8, 3u8, 4u8, 8u8, 6u8, 3u8, 2u8, 8u8, 1u8, 2u8, 5u8, 9u8, 5u8, + 3u8, 6u8, 7u8, 4u8, 3u8, 1u8, 6u8, 4u8, 0u8, 6u8, 2u8, 5u8, 4u8, 7u8, 6u8, 8u8, 3u8, 7u8, 1u8, 5u8, 8u8, 2u8, 0u8, 3u8, + 1u8, 2u8, 5u8, 2u8, 3u8, 8u8, 4u8, 1u8, 8u8, 5u8, 7u8, 9u8, 1u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 1u8, 1u8, 9u8, 2u8, 0u8, + 9u8, 2u8, 8u8, 9u8, 5u8, 5u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 5u8, 9u8, 6u8, 0u8, 4u8, 6u8, 4u8, 4u8, 7u8, 7u8, 5u8, 3u8, + 9u8, 0u8, 6u8, 2u8, 5u8, 2u8, 9u8, 8u8, 0u8, 2u8, 3u8, 2u8, 2u8, 3u8, 8u8, 7u8, 6u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 1u8, + 4u8, 9u8, 0u8, 1u8, 1u8, 6u8, 1u8, 1u8, 9u8, 3u8, 8u8, 4u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, 7u8, 4u8, 5u8, 0u8, 5u8, 8u8, + 0u8, 5u8, 9u8, 6u8, 9u8, 2u8, 3u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 3u8, 7u8, 2u8, 5u8, 2u8, 9u8, 0u8, 2u8, 9u8, 8u8, 4u8, + 6u8, 1u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 1u8, 8u8, 6u8, 2u8, 6u8, 4u8, 5u8, 1u8, 4u8, 9u8, 2u8, 3u8, 0u8, 9u8, 5u8, + 7u8, 0u8, 3u8, 1u8, 2u8, 5u8, 9u8, 3u8, 1u8, 3u8, 2u8, 2u8, 5u8, 7u8, 4u8, 6u8, 1u8, 5u8, 4u8, 7u8, 8u8, 5u8, 1u8, 5u8, + 6u8, 2u8, 5u8, 4u8, 6u8, 5u8, 6u8, 6u8, 1u8, 2u8, 8u8, 7u8, 3u8, 0u8, 7u8, 7u8, 3u8, 9u8, 2u8, 5u8, 7u8, 8u8, 1u8, 2u8, + 5u8, 2u8, 3u8, 2u8, 8u8, 3u8, 0u8, 6u8, 4u8, 3u8, 6u8, 5u8, 3u8, 8u8, 6u8, 9u8, 6u8, 2u8, 8u8, 9u8, 0u8, 6u8, 2u8, 5u8, + 1u8, 1u8, 6u8, 4u8, 1u8, 5u8, 3u8, 2u8, 1u8, 8u8, 2u8, 6u8, 9u8, 3u8, 4u8, 8u8, 1u8, 4u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, + 5u8, 8u8, 2u8, 0u8, 7u8, 6u8, 6u8, 0u8, 9u8, 1u8, 3u8, 4u8, 6u8, 7u8, 4u8, 0u8, 7u8, 2u8, 2u8, 6u8, 5u8, 6u8, 2u8, 5u8, + 2u8, 9u8, 1u8, 0u8, 3u8, 8u8, 3u8, 0u8, 4u8, 5u8, 6u8, 7u8, 3u8, 3u8, 7u8, 0u8, 3u8, 6u8, 1u8, 3u8, 2u8, 8u8, 1u8, 2u8, + 5u8, 1u8, 4u8, 5u8, 5u8, 1u8, 9u8, 1u8, 5u8, 2u8, 2u8, 8u8, 3u8, 6u8, 6u8, 8u8, 5u8, 1u8, 8u8, 0u8, 6u8, 6u8, 4u8, 0u8, + 6u8, 2u8, 5u8, 7u8, 2u8, 7u8, 5u8, 9u8, 5u8, 7u8, 6u8, 1u8, 4u8, 1u8, 8u8, 3u8, 4u8, 2u8, 5u8, 9u8, 0u8, 3u8, 3u8, 2u8, + 0u8, 3u8, 1u8, 2u8, 5u8, 3u8, 6u8, 3u8, 7u8, 9u8, 7u8, 8u8, 8u8, 0u8, 7u8, 0u8, 9u8, 1u8, 7u8, 1u8, 2u8, 9u8, 5u8, 1u8, + 6u8, 6u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 1u8, 8u8, 1u8, 8u8, 9u8, 8u8, 9u8, 4u8, 0u8, 3u8, 5u8, 4u8, 5u8, 8u8, 5u8, 6u8, + 4u8, 7u8, 5u8, 8u8, 3u8, 0u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 9u8, 0u8, 9u8, 4u8, 9u8, 4u8, 7u8, 0u8, 1u8, 7u8, 7u8, 2u8, + 9u8, 2u8, 8u8, 2u8, 3u8, 7u8, 9u8, 1u8, 5u8, 0u8, 3u8, 9u8, 0u8, 6u8, 2u8, 5u8, 4u8, 5u8, 4u8, 7u8, 4u8, 7u8, 3u8, 5u8, + 0u8, 8u8, 8u8, 6u8, 4u8, 6u8, 4u8, 1u8, 1u8, 8u8, 9u8, 5u8, 7u8, 5u8, 1u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 2u8, 2u8, 7u8, + 3u8, 7u8, 3u8, 6u8, 7u8, 5u8, 4u8, 4u8, 3u8, 2u8, 3u8, 2u8, 0u8, 5u8, 9u8, 4u8, 7u8, 8u8, 7u8, 5u8, 9u8, 7u8, 6u8, 5u8, + 6u8, 2u8, 5u8, 1u8, 1u8, 3u8, 6u8, 8u8, 6u8, 8u8, 3u8, 7u8, 7u8, 2u8, 1u8, 6u8, 1u8, 6u8, 0u8, 2u8, 9u8, 7u8, 3u8, 9u8, + 3u8, 7u8, 9u8, 8u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 5u8, 6u8, 8u8, 4u8, 3u8, 4u8, 1u8, 8u8, 8u8, 6u8, 0u8, 8u8, 0u8, 8u8, + 0u8, 1u8, 4u8, 8u8, 6u8, 9u8, 6u8, 8u8, 9u8, 9u8, 4u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, 2u8, 8u8, 4u8, 2u8, 1u8, 7u8, 0u8, + 9u8, 4u8, 3u8, 0u8, 4u8, 0u8, 4u8, 0u8, 0u8, 7u8, 4u8, 3u8, 4u8, 8u8, 4u8, 4u8, 9u8, 7u8, 0u8, 7u8, 0u8, 3u8, 1u8, 2u8, + 5u8, 1u8, 4u8, 2u8, 1u8, 0u8, 8u8, 5u8, 4u8, 7u8, 1u8, 5u8, 2u8, 0u8, 2u8, 0u8, 0u8, 3u8, 7u8, 1u8, 7u8, 4u8, 2u8, 2u8, + 4u8, 8u8, 5u8, 3u8, 5u8, 1u8, 5u8, 6u8, 2u8, 5u8, 7u8, 1u8, 0u8, 5u8, 4u8, 2u8, 7u8, 3u8, 5u8, 7u8, 6u8, 0u8, 1u8, 0u8, + 0u8, 1u8, 8u8, 5u8, 8u8, 7u8, 1u8, 1u8, 2u8, 4u8, 2u8, 6u8, 7u8, 5u8, 7u8, 8u8, 1u8, 2u8, 5u8, 3u8, 5u8, 5u8, 2u8, 7u8, + 1u8, 3u8, 6u8, 7u8, 8u8, 8u8, 0u8, 0u8, 5u8, 0u8, 0u8, 9u8, 2u8, 9u8, 3u8, 5u8, 5u8, 6u8, 2u8, 1u8, 3u8, 3u8, 7u8, 8u8, + 9u8, 0u8, 6u8, 2u8, 5u8, 1u8, 7u8, 7u8, 6u8, 3u8, 5u8, 6u8, 8u8, 3u8, 9u8, 4u8, 0u8, 0u8, 2u8, 5u8, 0u8, 4u8, 6u8, 4u8, + 6u8, 7u8, 7u8, 8u8, 1u8, 0u8, 6u8, 6u8, 8u8, 9u8, 4u8, 5u8, 3u8, 1u8, 2u8, 5u8, 8u8, 8u8, 8u8, 1u8, 7u8, 8u8, 4u8, 1u8, + 9u8, 7u8, 0u8, 0u8, 1u8, 2u8, 5u8, 2u8, 3u8, 2u8, 3u8, 3u8, 8u8, 9u8, 0u8, 5u8, 3u8, 3u8, 4u8, 4u8, 7u8, 2u8, 6u8, 5u8, + 6u8, 2u8, 5u8, 4u8, 4u8, 4u8, 0u8, 8u8, 9u8, 2u8, 0u8, 9u8, 8u8, 5u8, 0u8, 0u8, 6u8, 2u8, 6u8, 1u8, 6u8, 1u8, 6u8, 9u8, + 4u8, 5u8, 2u8, 6u8, 6u8, 7u8, 2u8, 3u8, 6u8, 3u8, 2u8, 8u8, 1u8, 2u8, 5u8, 2u8, 2u8, 2u8, 0u8, 4u8, 4u8, 6u8, 0u8, 4u8, + 9u8, 2u8, 5u8, 0u8, 3u8, 1u8, 3u8, 0u8, 8u8, 0u8, 8u8, 4u8, 7u8, 2u8, 6u8, 3u8, 3u8, 3u8, 6u8, 1u8, 8u8, 1u8, 6u8, 4u8, + 0u8, 6u8, 2u8, 5u8, 1u8, 1u8, 1u8, 0u8, 2u8, 2u8, 3u8, 0u8, 2u8, 4u8, 6u8, 2u8, 5u8, 1u8, 5u8, 6u8, 5u8, 4u8, 0u8, 4u8, + 2u8, 3u8, 6u8, 3u8, 1u8, 6u8, 6u8, 8u8, 0u8, 9u8, 0u8, 8u8, 2u8, 0u8, 3u8, 1u8, 2u8, 5u8, 5u8, 5u8, 5u8, 1u8, 1u8, 1u8, + 5u8, 1u8, 2u8, 3u8, 1u8, 2u8, 5u8, 7u8, 8u8, 2u8, 7u8, 0u8, 2u8, 1u8, 1u8, 8u8, 1u8, 5u8, 8u8, 3u8, 4u8, 0u8, 4u8, 5u8, + 4u8, 1u8, 0u8, 1u8, 5u8, 6u8, 2u8, 5u8, 2u8, 7u8, 7u8, 5u8, 5u8, 5u8, 7u8, 5u8, 6u8, 1u8, 5u8, 6u8, 2u8, 8u8, 9u8, 1u8, + 3u8, 5u8, 1u8, 0u8, 5u8, 9u8, 0u8, 7u8, 9u8, 1u8, 7u8, 0u8, 2u8, 2u8, 7u8, 0u8, 5u8, 0u8, 7u8, 8u8, 1u8, 2u8, 5u8, 1u8, + 3u8, 8u8, 7u8, 7u8, 7u8, 8u8, 7u8, 8u8, 0u8, 7u8, 8u8, 1u8, 4u8, 4u8, 5u8, 6u8, 7u8, 5u8, 5u8, 2u8, 9u8, 5u8, 3u8, 9u8, + 5u8, 8u8, 5u8, 1u8, 1u8, 3u8, 5u8, 2u8, 5u8, 3u8, 9u8, 0u8, 6u8, 2u8, 5u8, 6u8, 9u8, 3u8, 8u8, 8u8, 9u8, 3u8, 9u8, 0u8, + 3u8, 9u8, 0u8, 7u8, 2u8, 2u8, 8u8, 3u8, 7u8, 7u8, 6u8, 4u8, 7u8, 6u8, 9u8, 7u8, 9u8, 2u8, 5u8, 5u8, 6u8, 7u8, 6u8, 2u8, + 6u8, 9u8, 5u8, 3u8, 1u8, 2u8, 5u8, 3u8, 4u8, 6u8, 9u8, 4u8, 4u8, 6u8, 9u8, 5u8, 1u8, 9u8, 5u8, 3u8, 6u8, 1u8, 4u8, 1u8, + 8u8, 8u8, 8u8, 2u8, 3u8, 8u8, 4u8, 8u8, 9u8, 6u8, 2u8, 7u8, 8u8, 3u8, 8u8, 1u8, 3u8, 4u8, 7u8, 6u8, 5u8, 6u8, 2u8, 5u8, + 1u8, 7u8, 3u8, 4u8, 7u8, 2u8, 3u8, 4u8, 7u8, 5u8, 9u8, 7u8, 6u8, 8u8, 0u8, 7u8, 0u8, 9u8, 4u8, 4u8, 1u8, 1u8, 9u8, 2u8, + 4u8, 4u8, 8u8, 1u8, 3u8, 9u8, 1u8, 9u8, 0u8, 6u8, 7u8, 3u8, 8u8, 2u8, 8u8, 1u8, 2u8, 5u8, 8u8, 6u8, 7u8, 3u8, 6u8, 1u8, + 7u8, 3u8, 7u8, 9u8, 8u8, 8u8, 4u8, 0u8, 3u8, 5u8, 4u8, 7u8, 2u8, 0u8, 5u8, 9u8, 6u8, 2u8, 2u8, 4u8, 0u8, 6u8, 9u8, 5u8, + 9u8, 5u8, 3u8, 3u8, 6u8, 9u8, 1u8, 4u8, 0u8, 6u8, 2u8, 5u8, +]; + +// ref/hare/strconv/stof_data.ha:73. Eisel-Lemire 128-bit power-of-ten +// table (see header note). 596 rows, {hi, lo} u64 pair per row. +let powers_of_ten: [596][2]u64 = [ + [0xA5D3B6D479F8E056u64, 0x8FD0C16206306BABu64], + [0x8F48A4899877186Cu64, 0xB3C4F1BA87BC8696u64], + [0x331ACDABFE94DE87u64, 0xE0B62E2929ABA83Cu64], + [0x9FF0C08B7F1D0B14u64, 0x8C71DCD9BA0B4925u64], + [0x07ECF0AE5EE44DD9u64, 0xAF8E5410288E1B6Fu64], + [0xC9E82CD9F69D6150u64, 0xDB71E91432B1A24Au64], + [0xBE311C083A225CD2u64, 0x892731AC9FAF056Eu64], + [0x6DBD630A48AAF406u64, 0xAB70FE17C79AC6CAu64], + [0x092CBBCCDAD5B108u64, 0xD64D3D9DB981787Du64], + [0x25BBF56008C58EA5u64, 0x85F0468293F0EB4Eu64], + [0xAF2AF2B80AF6F24Eu64, 0xA76C582338ED2621u64], + [0x1AF5AF660DB4AEE1u64, 0xD1476E2C07286FAAu64], + [0x50D98D9FC890ED4Du64, 0x82CCA4DB847945CAu64], + [0xE50FF107BAB528A0u64, 0xA37FCE126597973Cu64], + [0x1E53ED49A96272C8u64, 0xCC5FC196FEFD7D0Cu64], + [0x25E8E89C13BB0F7Au64, 0xFF77B1FCBEBCDC4Fu64], + [0x77B191618C54E9ACu64, 0x9FAACF3DF73609B1u64], + [0xD59DF5B9EF6A2417u64, 0xC795830D75038C1Du64], + [0x4B0573286B44AD1Du64, 0xF97AE3D0D2446F25u64], + [0x4EE367F9430AEC32u64, 0x9BECCE62836AC577u64], + [0x229C41F793CDA73Fu64, 0xC2E801FB244576D5u64], + [0x6B43527578C1110Fu64, 0xF3A20279ED56D48Au64], + [0x830A13896B78AAA9u64, 0x9845418C345644D6u64], + [0x23CC986BC656D553u64, 0xBE5691EF416BD60Cu64], + [0x2CBFBE86B7EC8AA8u64, 0xEDEC366B11C6CB8Fu64], + [0x7BF7D71432F3D6A9u64, 0x94B3A202EB1C3F39u64], + [0xDAF5CCD93FB0CC53u64, 0xB9E08A83A5E34F07u64], + [0xD1B3400F8F9CFF68u64, 0xE858AD248F5C22C9u64], + [0x23100809B9C21FA1u64, 0x91376C36D99995BEu64], + [0xABD40A0C2832A78Au64, 0xB58547448FFFFB2Du64], + [0x16C90C8F323F516Cu64, 0xE2E69915B3FFF9F9u64], + [0xAE3DA7D97F6792E3u64, 0x8DD01FAD907FFC3Bu64], + [0x99CD11CFDF41779Cu64, 0xB1442798F49FFB4Au64], + [0x40405643D711D583u64, 0xDD95317F31C7FA1Du64], + [0x482835EA666B2572u64, 0x8A7D3EEF7F1CFC52u64], + [0xDA3243650005EECFu64, 0xAD1C8EAB5EE43B66u64], + [0x90BED43E40076A82u64, 0xD863B256369D4A40u64], + [0x5A7744A6E804A291u64, 0x873E4F75E2224E68u64], + [0x711515D0A205CB36u64, 0xA90DE3535AAAE202u64], + [0x0D5A5B44CA873E03u64, 0xD3515C2831559A83u64], + [0xE858790AFE9486C2u64, 0x8412D9991ED58091u64], + [0x626E974DBE39A872u64, 0xA5178FFF668AE0B6u64], + [0xFB0A3D212DC8128Fu64, 0xCE5D73FF402D98E3u64], + [0x7CE66634BC9D0B99u64, 0x80FA687F881C7F8Eu64], + [0x1C1FFFC1EBC44E80u64, 0xA139029F6A239F72u64], + [0xA327FFB266B56220u64, 0xC987434744AC874Eu64], + [0x4BF1FF9F0062BAA8u64, 0xFBE9141915D7A922u64], + [0x6F773FC3603DB4A9u64, 0x9D71AC8FADA6C9B5u64], + [0xCB550FB4384D21D3u64, 0xC4CE17B399107C22u64], + [0x7E2A53A146606A48u64, 0xF6019DA07F549B2Bu64], + [0x2EDA7444CBFC426Du64, 0x99C102844F94E0FBu64], + [0xFA911155FEFB5308u64, 0xC0314325637A1939u64], + [0x793555AB7EBA27CAu64, 0xF03D93EEBC589F88u64], + [0x4BC1558B2F3458DEu64, 0x96267C7535B763B5u64], + [0x9EB1AAEDFB016F16u64, 0xBBB01B9283253CA2u64], + [0x465E15A979C1CADCu64, 0xEA9C227723EE8BCBu64], + [0x0BFACD89EC191EC9u64, 0x92A1958A7675175Fu64], + [0xCEF980EC671F667Bu64, 0xB749FAED14125D36u64], + [0x82B7E12780E7401Au64, 0xE51C79A85916F484u64], + [0xD1B2ECB8B0908810u64, 0x8F31CC0937AE58D2u64], + [0x861FA7E6DCB4AA15u64, 0xB2FE3F0B8599EF07u64], + [0x67A791E093E1D49Au64, 0xDFBDCECE67006AC9u64], + [0xE0C8BB2C5C6D24E0u64, 0x8BD6A141006042BDu64], + [0x58FAE9F773886E18u64, 0xAECC49914078536Du64], + [0xAF39A475506A899Eu64, 0xDA7F5BF590966848u64], + [0x6D8406C952429603u64, 0x888F99797A5E012Du64], + [0xC8E5087BA6D33B83u64, 0xAAB37FD7D8F58178u64], + [0xFB1E4A9A90880A64u64, 0xD5605FCDCF32E1D6u64], + [0x5CF2EEA09A55067Fu64, 0x855C3BE0A17FCD26u64], + [0xF42FAA48C0EA481Eu64, 0xA6B34AD8C9DFC06Fu64], + [0xF13B94DAF124DA26u64, 0xD0601D8EFC57B08Bu64], + [0x76C53D08D6B70858u64, 0x823C12795DB6CE57u64], + [0x54768C4B0C64CA6Eu64, 0xA2CB1717B52481EDu64], + [0xA9942F5DCF7DFD09u64, 0xCB7DDCDDA26DA268u64], + [0xD3F93B35435D7C4Cu64, 0xFE5D54150B090B02u64], + [0xC47BC5014A1A6DAFu64, 0x9EFA548D26E5A6E1u64], + [0x359AB6419CA1091Bu64, 0xC6B8E9B0709F109Au64], + [0xC30163D203C94B62u64, 0xF867241C8CC6D4C0u64], + [0x79E0DE63425DCF1Du64, 0x9B407691D7FC44F8u64], + [0x985915FC12F542E4u64, 0xC21094364DFB5636u64], + [0x3E6F5B7B17B2939Du64, 0xF294B943E17A2BC4u64], + [0xA705992CEECF9C42u64, 0x979CF3CA6CEC5B5Au64], + [0x50C6FF782A838353u64, 0xBD8430BD08277231u64], + [0xA4F8BF5635246428u64, 0xECE53CEC4A314EBDu64], + [0x871B7795E136BE99u64, 0x940F4613AE5ED136u64], + [0x28E2557B59846E3Fu64, 0xB913179899F68584u64], + [0x331AEADA2FE589CFu64, 0xE757DD7EC07426E5u64], + [0x3FF0D2C85DEF7621u64, 0x9096EA6F3848984Fu64], + [0x0FED077A756B53A9u64, 0xB4BCA50B065ABE63u64], + [0xD3E8495912C62894u64, 0xE1EBCE4DC7F16DFBu64], + [0x64712DD7ABBBD95Cu64, 0x8D3360F09CF6E4BDu64], + [0xBD8D794D96AACFB3u64, 0xB080392CC4349DECu64], + [0xECF0D7A0FC5583A0u64, 0xDCA04777F541C567u64], + [0xF41686C49DB57244u64, 0x89E42CAAF9491B60u64], + [0x311C2875C522CED5u64, 0xAC5D37D5B79B6239u64], + [0x7D633293366B828Bu64, 0xD77485CB25823AC7u64], + [0xAE5DFF9C02033197u64, 0x86A8D39EF77164BCu64], + [0xD9F57F830283FDFCu64, 0xA8530886B54DBDEBu64], + [0xD072DF63C324FD7Bu64, 0xD267CAA862A12D66u64], + [0x4247CB9E59F71E6Du64, 0x8380DEA93DA4BC60u64], + [0x52D9BE85F074E608u64, 0xA46116538D0DEB78u64], + [0x67902E276C921F8Bu64, 0xCD795BE870516656u64], + [0x00BA1CD8A3DB53B6u64, 0x806BD9714632DFF6u64], + [0x80E8A40ECCD228A4u64, 0xA086CFCD97BF97F3u64], + [0x6122CD128006B2CDu64, 0xC8A883C0FDAF7DF0u64], + [0x796B805720085F81u64, 0xFAD2A4B13D1B5D6Cu64], + [0xCBE3303674053BB0u64, 0x9CC3A6EEC6311A63u64], + [0xBEDBFC4411068A9Cu64, 0xC3F490AA77BD60FCu64], + [0xEE92FB5515482D44u64, 0xF4F1B4D515ACB93Bu64], + [0x751BDD152D4D1C4Au64, 0x991711052D8BF3C5u64], + [0xD262D45A78A0635Du64, 0xBF5CD54678EEF0B6u64], + [0x86FB897116C87C34u64, 0xEF340A98172AACE4u64], + [0xD45D35E6AE3D4DA0u64, 0x9580869F0E7AAC0Eu64], + [0x8974836059CCA109u64, 0xBAE0A846D2195712u64], + [0x2BD1A438703FC94Bu64, 0xE998D258869FACD7u64], + [0x7B6306A34627DDCFu64, 0x91FF83775423CC06u64], + [0x1A3BC84C17B1D542u64, 0xB67F6455292CBF08u64], + [0x20CABA5F1D9E4A93u64, 0xE41F3D6A7377EECAu64], + [0x547EB47B7282EE9Cu64, 0x8E938662882AF53Eu64], + [0xE99E619A4F23AA43u64, 0xB23867FB2A35B28Du64], + [0x6405FA00E2EC94D4u64, 0xDEC681F9F4C31F31u64], + [0xDE83BC408DD3DD04u64, 0x8B3C113C38F9F37Eu64], + [0x9624AB50B148D445u64, 0xAE0B158B4738705Eu64], + [0x3BADD624DD9B0957u64, 0xD98DDAEE19068C76u64], + [0xE54CA5D70A80E5D6u64, 0x87F8A8D4CFA417C9u64], + [0x5E9FCF4CCD211F4Cu64, 0xA9F6D30A038D1DBCu64], + [0x7647C3200069671Fu64, 0xD47487CC8470652Bu64], + [0x29ECD9F40041E073u64, 0x84C8D4DFD2C63F3Bu64], + [0xF468107100525890u64, 0xA5FB0A17C777CF09u64], + [0x7182148D4066EEB4u64, 0xCF79CC9DB955C2CCu64], + [0xC6F14CD848405530u64, 0x81AC1FE293D599BFu64], + [0xB8ADA00E5A506A7Cu64, 0xA21727DB38CB002Fu64], + [0xA6D90811F0E4851Cu64, 0xCA9CF1D206FDC03Bu64], + [0x908F4A166D1DA663u64, 0xFD442E4688BD304Au64], + [0x9A598E4E043287FEu64, 0x9E4A9CEC15763E2Eu64], + [0x40EFF1E1853F29FDu64, 0xC5DD44271AD3CDBAu64], + [0xD12BEE59E68EF47Cu64, 0xF7549530E188C128u64], + [0x82BB74F8301958CEu64, 0x9A94DD3E8CF578B9u64], + [0xE36A52363C1FAF01u64, 0xC13A148E3032D6E7u64], + [0xDC44E6C3CB279AC1u64, 0xF18899B1BC3F8CA1u64], + [0x29AB103A5EF8C0B9u64, 0x96F5600F15A7B7E5u64], + [0x7415D448F6B6F0E7u64, 0xBCB2B812DB11A5DEu64], + [0x111B495B3464AD21u64, 0xEBDF661791D60F56u64], + [0xCAB10DD900BEEC34u64, 0x936B9FCEBB25C995u64], + [0x3D5D514F40EEA742u64, 0xB84687C269EF3BFBu64], + [0x0CB4A5A3112A5112u64, 0xE65829B3046B0AFAu64], + [0x47F0E785EABA72ABu64, 0x8FF71A0FE2C2E6DCu64], + [0x59ED216765690F56u64, 0xB3F4E093DB73A093u64], + [0x306869C13EC3532Cu64, 0xE0F218B8D25088B8u64], + [0x1E414218C73A13FBu64, 0x8C974F7383725573u64], + [0xE5D1929EF90898FAu64, 0xAFBD2350644EEACFu64], + [0xDF45F746B74ABF39u64, 0xDBAC6C247D62A583u64], + [0x6B8BBA8C328EB783u64, 0x894BC396CE5DA772u64], + [0x066EA92F3F326564u64, 0xAB9EB47C81F5114Fu64], + [0xC80A537B0EFEFEBDu64, 0xD686619BA27255A2u64], + [0xBD06742CE95F5F36u64, 0x8613FD0145877585u64], + [0x2C48113823B73704u64, 0xA798FC4196E952E7u64], + [0xF75A15862CA504C5u64, 0xD17F3B51FCA3A7A0u64], + [0x9A984D73DBE722FBu64, 0x82EF85133DE648C4u64], + [0xC13E60D0D2E0EBBAu64, 0xA3AB66580D5FDAF5u64], + [0x318DF905079926A8u64, 0xCC963FEE10B7D1B3u64], + [0xFDF17746497F7052u64, 0xFFBBCFE994E5C61Fu64], + [0xFEB6EA8BEDEFA633u64, 0x9FD561F1FD0F9BD3u64], + [0xFE64A52EE96B8FC0u64, 0xC7CABA6E7C5382C8u64], + [0x3DFDCE7AA3C673B0u64, 0xF9BD690A1B68637Bu64], + [0x06BEA10CA65C084Eu64, 0x9C1661A651213E2Du64], + [0x486E494FCFF30A62u64, 0xC31BFA0FE5698DB8u64], + [0x5A89DBA3C3EFCCFAu64, 0xF3E2F893DEC3F126u64], + [0xF89629465A75E01Cu64, 0x986DDB5C6B3A76B7u64], + [0xF6BBB397F1135823u64, 0xBE89523386091465u64], + [0x746AA07DED582E2Cu64, 0xEE2BA6C0678B597Fu64], + [0xA8C2A44EB4571CDCu64, 0x94DB483840B717EFu64], + [0x92F34D62616CE413u64, 0xBA121A4650E4DDEBu64], + [0x77B020BAF9C81D17u64, 0xE896A0D7E51E1566u64], + [0x0ACE1474DC1D122Eu64, 0x915E2486EF32CD60u64], + [0x0D819992132456BAu64, 0xB5B5ADA8AAFF80B8u64], + [0x10E1FFF697ED6C69u64, 0xE3231912D5BF60E6u64], + [0xCA8D3FFA1EF463C1u64, 0x8DF5EFABC5979C8Fu64], + [0xBD308FF8A6B17CB2u64, 0xB1736B96B6FD83B3u64], + [0xAC7CB3F6D05DDBDEu64, 0xDDD0467C64BCE4A0u64], + [0x6BCDF07A423AA96Bu64, 0x8AA22C0DBEF60EE4u64], + [0x86C16C98D2C953C6u64, 0xAD4AB7112EB3929Du64], + [0xE871C7BF077BA8B7u64, 0xD89D64D57A607744u64], + [0x11471CD764AD4972u64, 0x87625F056C7C4A8Bu64], + [0xD598E40D3DD89BCFu64, 0xA93AF6C6C79B5D2Du64], + [0x4AFF1D108D4EC2C3u64, 0xD389B47879823479u64], + [0xCEDF722A585139BAu64, 0x843610CB4BF160CBu64], + [0xC2974EB4EE658828u64, 0xA54394FE1EEDB8FEu64], + [0x733D226229FEEA32u64, 0xCE947A3DA6A9273Eu64], + [0x0806357D5A3F525Fu64, 0x811CCC668829B887u64], + [0xCA07C2DCB0CF26F7u64, 0xA163FF802A3426A8u64], + [0xFC89B393DD02F0B5u64, 0xC9BCFF6034C13052u64], + [0xBBAC2078D443ACE2u64, 0xFC2C3F3841F17C67u64], + [0xD54B944B84AA4C0Du64, 0x9D9BA7832936EDC0u64], + [0x0A9E795E65D4DF11u64, 0xC5029163F384A931u64], + [0x4D4617B5FF4A16D5u64, 0xF64335BCF065D37Du64], + [0x504BCED1BF8E4E45u64, 0x99EA0196163FA42Eu64], + [0xE45EC2862F71E1D6u64, 0xC06481FB9BCF8D39u64], + [0x5D767327BB4E5A4Cu64, 0xF07DA27A82C37088u64], + [0x3A6A07F8D510F86Fu64, 0x964E858C91BA2655u64], + [0x890489F70A55368Bu64, 0xBBE226EFB628AFEAu64], + [0x2B45AC74CCEA842Eu64, 0xEADAB0ABA3B2DBE5u64], + [0x3B0B8BC90012929Du64, 0x92C8AE6B464FC96Fu64], + [0x09CE6EBB40173744u64, 0xB77ADA0617E3BBCBu64], + [0xCC420A6A101D0515u64, 0xE55990879DDCAABDu64], + [0x9FA946824A12232Du64, 0x8F57FA54C2A9EAB6u64], + [0x47939822DC96ABF9u64, 0xB32DF8E9F3546564u64], + [0x59787E2B93BC56F7u64, 0xDFF9772470297EBDu64], + [0x57EB4EDB3C55B65Au64, 0x8BFBEA76C619EF36u64], + [0xEDE622920B6B23F1u64, 0xAEFAE51477A06B03u64], + [0xE95FAB368E45ECEDu64, 0xDAB99E59958885C4u64], + [0x11DBCB0218EBB414u64, 0x88B402F7FD75539Bu64], + [0xD652BDC29F26A119u64, 0xAAE103B5FCD2A881u64], + [0x4BE76D3346F0495Fu64, 0xD59944A37C0752A2u64], + [0x6F70A4400C562DDBu64, 0x857FCAE62D8493A5u64], + [0xCB4CCD500F6BB952u64, 0xA6DFBD9FB8E5B88Eu64], + [0x7E2000A41346A7A7u64, 0xD097AD07A71F26B2u64], + [0x8ED400668C0C28C8u64, 0x825ECC24C873782Fu64], + [0x728900802F0F32FAu64, 0xA2F67F2DFA90563Bu64], + [0x4F2B40A03AD2FFB9u64, 0xCBB41EF979346BCAu64], + [0xE2F610C84987BFA8u64, 0xFEA126B7D78186BCu64], + [0x0DD9CA7D2DF4D7C9u64, 0x9F24B832E6B0F436u64], + [0x91503D1C79720DBBu64, 0xC6EDE63FA05D3143u64], + [0x75A44C6397CE912Au64, 0xF8A95FCF88747D94u64], + [0xC986AFBE3EE11ABAu64, 0x9B69DBE1B548CE7Cu64], + [0xFBE85BADCE996168u64, 0xC24452DA229B021Bu64], + [0xFAE27299423FB9C3u64, 0xF2D56790AB41C2A2u64], + [0xDCCD879FC967D41Au64, 0x97C560BA6B0919A5u64], + [0x5400E987BBC1C920u64, 0xBDB6B8E905CB600Fu64], + [0x290123E9AAB23B68u64, 0xED246723473E3813u64], + [0xF9A0B6720AAF6521u64, 0x9436C0760C86E30Bu64], + [0xF808E40E8D5B3E69u64, 0xB94470938FA89BCEu64], + [0xB60B1D1230B20E04u64, 0xE7958CB87392C2C2u64], + [0xB1C6F22B5E6F48C2u64, 0x90BD77F3483BB9B9u64], + [0x1E38AEB6360B1AF3u64, 0xB4ECD5F01A4AA828u64], + [0x25C6DA63C38DE1B0u64, 0xE2280B6C20DD5232u64], + [0x579C487E5A38AD0Eu64, 0x8D590723948A535Fu64], + [0x2D835A9DF0C6D851u64, 0xB0AF48EC79ACE837u64], + [0xF8E431456CF88E65u64, 0xDCDB1B2798182244u64], + [0x1B8E9ECB641B58FFu64, 0x8A08F0F8BF0F156Bu64], + [0xE272467E3D222F3Fu64, 0xAC8B2D36EED2DAC5u64], + [0x5B0ED81DCC6ABB0Fu64, 0xD7ADF884AA879177u64], + [0x98E947129FC2B4E9u64, 0x86CCBB52EA94BAEAu64], + [0x3F2398D747B36224u64, 0xA87FEA27A539E9A5u64], + [0x8EEC7F0D19A03AADu64, 0xD29FE4B18E88640Eu64], + [0x1953CF68300424ACu64, 0x83A3EEEEF9153E89u64], + [0x5FA8C3423C052DD7u64, 0xA48CEAAAB75A8E2Bu64], + [0x3792F412CB06794Du64, 0xCDB02555653131B6u64], + [0xE2BBD88BBEE40BD0u64, 0x808E17555F3EBF11u64], + [0x5B6ACEAEAE9D0EC4u64, 0xA0B19D2AB70E6ED6u64], + [0xF245825A5A445275u64, 0xC8DE047564D20A8Bu64], + [0xEED6E2F0F0D56712u64, 0xFB158592BE068D2Eu64], + [0x55464DD69685606Bu64, 0x9CED737BB6C4183Du64], + [0xAA97E14C3C26B886u64, 0xC428D05AA4751E4Cu64], + [0xD53DD99F4B3066A8u64, 0xF53304714D9265DFu64], + [0xE546A8038EFE4029u64, 0x993FE2C6D07B7FABu64], + [0xDE98520472BDD033u64, 0xBF8FDB78849A5F96u64], + [0x963E66858F6D4440u64, 0xEF73D256A5C0F77Cu64], + [0xDDE7001379A44AA8u64, 0x95A8637627989AADu64], + [0x5560C018580D5D52u64, 0xBB127C53B17EC159u64], + [0xAAB8F01E6E10B4A6u64, 0xE9D71B689DDE71AFu64], + [0xCAB3961304CA70E8u64, 0x9226712162AB070Du64], + [0x3D607B97C5FD0D22u64, 0xB6B00D69BB55C8D1u64], + [0x8CB89A7DB77C506Au64, 0xE45C10C42A2B3B05u64], + [0x77F3608E92ADB242u64, 0x8EB98A7A9A5B04E3u64], + [0x55F038B237591ED3u64, 0xB267ED1940F1C61Cu64], + [0x6B6C46DEC52F6688u64, 0xDF01E85F912E37A3u64], + [0x2323AC4B3B3DA015u64, 0x8B61313BBABCE2C6u64], + [0xABEC975E0A0D081Au64, 0xAE397D8AA96C1B77u64], + [0x96E7BD358C904A21u64, 0xD9C7DCED53C72255u64], + [0x7E50D64177DA2E54u64, 0x881CEA14545C7575u64], + [0xDDE50BD1D5D0B9E9u64, 0xAA242499697392D2u64], + [0x955E4EC64B44E864u64, 0xD4AD2DBFC3D07787u64], + [0xBD5AF13BEF0B113Eu64, 0x84EC3C97DA624AB4u64], + [0xECB1AD8AEACDD58Eu64, 0xA6274BBDD0FADD61u64], + [0x67DE18EDA5814AF2u64, 0xCFB11EAD453994BAu64], + [0x80EACF948770CED7u64, 0x81CEB32C4B43FCF4u64], + [0xA1258379A94D028Du64, 0xA2425FF75E14FC31u64], + [0x096EE45813A04330u64, 0xCAD2F7F5359A3B3Eu64], + [0x8BCA9D6E188853FCu64, 0xFD87B5F28300CA0Du64], + [0x775EA264CF55347Du64, 0x9E74D1B791E07E48u64], + [0x95364AFE032A819Du64, 0xC612062576589DDAu64], + [0x3A83DDBD83F52204u64, 0xF79687AED3EEC551u64], + [0xC4926A9672793542u64, 0x9ABE14CD44753B52u64], + [0x75B7053C0F178293u64, 0xC16D9A0095928A27u64], + [0x5324C68B12DD6338u64, 0xF1C90080BAF72CB1u64], + [0xD3F6FC16EBCA5E03u64, 0x971DA05074DA7BEEu64], + [0x88F4BB1CA6BCF584u64, 0xBCE5086492111AEAu64], + [0x2B31E9E3D06C32E5u64, 0xEC1E4A7DB69561A5u64], + [0x3AFF322E62439FCFu64, 0x9392EE8E921D5D07u64], + [0x09BEFEB9FAD487C2u64, 0xB877AA3236A4B449u64], + [0x4C2EBE687989A9B3u64, 0xE69594BEC44DE15Bu64], + [0x0F9D37014BF60A10u64, 0x901D7CF73AB0ACD9u64], + [0x538484C19EF38C94u64, 0xB424DC35095CD80Fu64], + [0x2865A5F206B06FB9u64, 0xE12E13424BB40E13u64], + [0xF93F87B7442E45D3u64, 0x8CBCCC096F5088CBu64], + [0xF78F69A51539D748u64, 0xAFEBFF0BCB24AAFEu64], + [0xB573440E5A884D1Bu64, 0xDBE6FECEBDEDD5BEu64], + [0x31680A88F8953030u64, 0x89705F4136B4A597u64], + [0xFDC20D2B36BA7C3Du64, 0xABCC77118461CEFCu64], + [0x3D32907604691B4Cu64, 0xD6BF94D5E57A42BCu64], + [0xA63F9A49C2C1B10Fu64, 0x8637BD05AF6C69B5u64], + [0x0FCF80DC33721D53u64, 0xA7C5AC471B478423u64], + [0xD3C36113404EA4A8u64, 0xD1B71758E219652Bu64], + [0x645A1CAC083126E9u64, 0x83126E978D4FDF3Bu64], + [0x3D70A3D70A3D70A3u64, 0xA3D70A3D70A3D70Au64], + [0xCCCCCCCCCCCCCCCCu64, 0xCCCCCCCCCCCCCCCCu64], + [0x0000000000000000u64, 0x8000000000000000u64], + [0x0000000000000000u64, 0xA000000000000000u64], + [0x0000000000000000u64, 0xC800000000000000u64], + [0x0000000000000000u64, 0xFA00000000000000u64], + [0x0000000000000000u64, 0x9C40000000000000u64], + [0x0000000000000000u64, 0xC350000000000000u64], + [0x0000000000000000u64, 0xF424000000000000u64], + [0x0000000000000000u64, 0x9896800000000000u64], + [0x0000000000000000u64, 0xBEBC200000000000u64], + [0x0000000000000000u64, 0xEE6B280000000000u64], + [0x0000000000000000u64, 0x9502F90000000000u64], + [0x0000000000000000u64, 0xBA43B74000000000u64], + [0x0000000000000000u64, 0xE8D4A51000000000u64], + [0x0000000000000000u64, 0x9184E72A00000000u64], + [0x0000000000000000u64, 0xB5E620F480000000u64], + [0x0000000000000000u64, 0xE35FA931A0000000u64], + [0x0000000000000000u64, 0x8E1BC9BF04000000u64], + [0x0000000000000000u64, 0xB1A2BC2EC5000000u64], + [0x0000000000000000u64, 0xDE0B6B3A76400000u64], + [0x0000000000000000u64, 0x8AC7230489E80000u64], + [0x0000000000000000u64, 0xAD78EBC5AC620000u64], + [0x0000000000000000u64, 0xD8D726B7177A8000u64], + [0x0000000000000000u64, 0x878678326EAC9000u64], + [0x0000000000000000u64, 0xA968163F0A57B400u64], + [0x0000000000000000u64, 0xD3C21BCECCEDA100u64], + [0x0000000000000000u64, 0x84595161401484A0u64], + [0x0000000000000000u64, 0xA56FA5B99019A5C8u64], + [0x0000000000000000u64, 0xCECB8F27F4200F3Au64], + [0x4000000000000000u64, 0x813F3978F8940984u64], + [0x5000000000000000u64, 0xA18F07D736B90BE5u64], + [0xA400000000000000u64, 0xC9F2C9CD04674EDEu64], + [0x4D00000000000000u64, 0xFC6F7C4045812296u64], + [0xF020000000000000u64, 0x9DC5ADA82B70B59Du64], + [0x6C28000000000000u64, 0xC5371912364CE305u64], + [0xC732000000000000u64, 0xF684DF56C3E01BC6u64], + [0x3C7F400000000000u64, 0x9A130B963A6C115Cu64], + [0x4B9F100000000000u64, 0xC097CE7BC90715B3u64], + [0x1E86D40000000000u64, 0xF0BDC21ABB48DB20u64], + [0x1314448000000000u64, 0x96769950B50D88F4u64], + [0x17D955A000000000u64, 0xBC143FA4E250EB31u64], + [0x5DCFAB0800000000u64, 0xEB194F8E1AE525FDu64], + [0x5AA1CAE500000000u64, 0x92EFD1B8D0CF37BEu64], + [0xF14A3D9E40000000u64, 0xB7ABC627050305ADu64], + [0x6D9CCD05D0000000u64, 0xE596B7B0C643C719u64], + [0xE4820023A2000000u64, 0x8F7E32CE7BEA5C6Fu64], + [0xDDA2802C8A800000u64, 0xB35DBF821AE4F38Bu64], + [0xD50B2037AD200000u64, 0xE0352F62A19E306Eu64], + [0x4526F422CC340000u64, 0x8C213D9DA502DE45u64], + [0x9670B12B7F410000u64, 0xAF298D050E4395D6u64], + [0x3C0CDD765F114000u64, 0xDAF3F04651D47B4Cu64], + [0xA5880A69FB6AC800u64, 0x88D8762BF324CD0Fu64], + [0x8EEA0D047A457A00u64, 0xAB0E93B6EFEE0053u64], + [0x72A4904598D6D880u64, 0xD5D238A4ABE98068u64], + [0x47A6DA2B7F864750u64, 0x85A36366EB71F041u64], + [0x999090B65F67D924u64, 0xA70C3C40A64E6C51u64], + [0xFFF4B4E3F741CF6Du64, 0xD0CF4B50CFE20765u64], + [0xBFF8F10E7A8921A4u64, 0x82818F1281ED449Fu64], + [0xAFF72D52192B6A0Du64, 0xA321F2D7226895C7u64], + [0x9BF4F8A69F764490u64, 0xCBEA6F8CEB02BB39u64], + [0x02F236D04753D5B4u64, 0xFEE50B7025C36A08u64], + [0x01D762422C946590u64, 0x9F4F2726179A2245u64], + [0x424D3AD2B7B97EF5u64, 0xC722F0EF9D80AAD6u64], + [0xD2E0898765A7DEB2u64, 0xF8EBAD2B84E0D58Bu64], + [0x63CC55F49F88EB2Fu64, 0x9B934C3B330C8577u64], + [0x3CBF6B71C76B25FBu64, 0xC2781F49FFCFA6D5u64], + [0x8BEF464E3945EF7Au64, 0xF316271C7FC3908Au64], + [0x97758BF0E3CBB5ACu64, 0x97EDD871CFDA3A56u64], + [0x3D52EEED1CBEA317u64, 0xBDE94E8E43D0C8ECu64], + [0x4CA7AAA863EE4BDDu64, 0xED63A231D4C4FB27u64], + [0x8FE8CAA93E74EF6Au64, 0x945E455F24FB1CF8u64], + [0xB3E2FD538E122B44u64, 0xB975D6B6EE39E436u64], + [0x60DBBCA87196B616u64, 0xE7D34C64A9C85D44u64], + [0xBC8955E946FE31CDu64, 0x90E40FBEEA1D3A4Au64], + [0x6BABAB6398BDBE41u64, 0xB51D13AEA4A488DDu64], + [0xC696963C7EED2DD1u64, 0xE264589A4DCDAB14u64], + [0xFC1E1DE5CF543CA2u64, 0x8D7EB76070A08AECu64], + [0x3B25A55F43294BCBu64, 0xB0DE65388CC8ADA8u64], + [0x49EF0EB713F39EBEu64, 0xDD15FE86AFFAD912u64], + [0x6E3569326C784337u64, 0x8A2DBF142DFCC7ABu64], + [0x49C2C37F07965404u64, 0xACB92ED9397BF996u64], + [0xDC33745EC97BE906u64, 0xD7E77A8F87DAF7FBu64], + [0x69A028BB3DED71A3u64, 0x86F0AC99B4E8DAFDu64], + [0xC40832EA0D68CE0Cu64, 0xA8ACD7C0222311BCu64], + [0xF50A3FA490C30190u64, 0xD2D80DB02AABD62Bu64], + [0x792667C6DA79E0FAu64, 0x83C7088E1AAB65DBu64], + [0x577001B891185938u64, 0xA4B8CAB1A1563F52u64], + [0xED4C0226B55E6F86u64, 0xCDE6FD5E09ABCF26u64], + [0x544F8158315B05B4u64, 0x80B05E5AC60B6178u64], + [0x696361AE3DB1C721u64, 0xA0DC75F1778E39D6u64], + [0x03BC3A19CD1E38E9u64, 0xC913936DD571C84Cu64], + [0x04AB48A04065C723u64, 0xFB5878494ACE3A5Fu64], + [0x62EB0D64283F9C76u64, 0x9D174B2DCEC0E47Bu64], + [0x3BA5D0BD324F8394u64, 0xC45D1DF942711D9Au64], + [0xCA8F44EC7EE36479u64, 0xF5746577930D6500u64], + [0x7E998B13CF4E1ECBu64, 0x9968BF6ABBE85F20u64], + [0x9E3FEDD8C321A67Eu64, 0xBFC2EF456AE276E8u64], + [0xC5CFE94EF3EA101Eu64, 0xEFB3AB16C59B14A2u64], + [0xBBA1F1D158724A12u64, 0x95D04AEE3B80ECE5u64], + [0x2A8A6E45AE8EDC97u64, 0xBB445DA9CA61281Fu64], + [0xF52D09D71A3293BDu64, 0xEA1575143CF97226u64], + [0x593C2626705F9C56u64, 0x924D692CA61BE758u64], + [0x6F8B2FB00C77836Cu64, 0xB6E0C377CFA2E12Eu64], + [0x0B6DFB9C0F956447u64, 0xE498F455C38B997Au64], + [0x4724BD4189BD5EACu64, 0x8EDF98B59A373FECu64], + [0x58EDEC91EC2CB657u64, 0xB2977EE300C50FE7u64], + [0x2F2967B66737E3EDu64, 0xDF3D5E9BC0F653E1u64], + [0xBD79E0D20082EE74u64, 0x8B865B215899F46Cu64], + [0xECD8590680A3AA11u64, 0xAE67F1E9AEC07187u64], + [0xE80E6F4820CC9495u64, 0xDA01EE641A708DE9u64], + [0x3109058D147FDCDDu64, 0x884134FE908658B2u64], + [0xBD4B46F0599FD415u64, 0xAA51823E34A7EEDEu64], + [0x6C9E18AC7007C91Au64, 0xD4E5E2CDC1D1EA96u64], + [0x03E2CF6BC604DDB0u64, 0x850FADC09923329Eu64], + [0x84DB8346B786151Cu64, 0xA6539930BF6BFF45u64], + [0xE612641865679A63u64, 0xCFE87F7CEF46FF16u64], + [0x4FCB7E8F3F60C07Eu64, 0x81F14FAE158C5F6Eu64], + [0xE3BE5E330F38F09Du64, 0xA26DA3999AEF7749u64], + [0x5CADF5BFD3072CC5u64, 0xCB090C8001AB551Cu64], + [0x73D9732FC7C8F7F6u64, 0xFDCB4FA002162A63u64], + [0x2867E7FDDCDD9AFAu64, 0x9E9F11C4014DDA7Eu64], + [0xB281E1FD541501B8u64, 0xC646D63501A1511Du64], + [0x1F225A7CA91A4226u64, 0xF7D88BC24209A565u64], + [0x3375788DE9B06958u64, 0x9AE757596946075Fu64], + [0x0052D6B1641C83AEu64, 0xC1A12D2FC3978937u64], + [0xC0678C5DBD23A49Au64, 0xF209787BB47D6B84u64], + [0xF840B7BA963646E0u64, 0x9745EB4D50CE6332u64], + [0xB650E5A93BC3D898u64, 0xBD176620A501FBFFu64], + [0xA3E51F138AB4CEBEu64, 0xEC5D3FA8CE427AFFu64], + [0xC66F336C36B10137u64, 0x93BA47C980E98CDFu64], + [0xB80B0047445D4184u64, 0xB8A8D9BBE123F017u64], + [0xA60DC059157491E5u64, 0xE6D3102AD96CEC1Du64], + [0x87C89837AD68DB2Fu64, 0x9043EA1AC7E41392u64], + [0x29BABE4598C311FBu64, 0xB454E4A179DD1877u64], + [0xF4296DD6FEF3D67Au64, 0xE16A1DC9D8545E94u64], + [0x1899E4A65F58660Cu64, 0x8CE2529E2734BB1Du64], + [0x5EC05DCFF72E7F8Fu64, 0xB01AE745B101E9E4u64], + [0x76707543F4FA1F73u64, 0xDC21A1171D42645Du64], + [0x6A06494A791C53A8u64, 0x899504AE72497EBAu64], + [0x0487DB9D17636892u64, 0xABFA45DA0EDBDE69u64], + [0x45A9D2845D3C42B6u64, 0xD6F8D7509292D603u64], + [0x0B8A2392BA45A9B2u64, 0x865B86925B9BC5C2u64], + [0x8E6CAC7768D7141Eu64, 0xA7F26836F282B732u64], + [0x3207D795430CD926u64, 0xD1EF0244AF2364FFu64], + [0x7F44E6BD49E807B8u64, 0x8335616AED761F1Fu64], + [0x5F16206C9C6209A6u64, 0xA402B9C5A8D3A6E7u64], + [0x36DBA887C37A8C0Fu64, 0xCD036837130890A1u64], + [0xC2494954DA2C9789u64, 0x802221226BE55A64u64], + [0xF2DB9BAA10B7BD6Cu64, 0xA02AA96B06DEB0FDu64], + [0x6F92829494E5ACC7u64, 0xC83553C5C8965D3Du64], + [0xCB772339BA1F17F9u64, 0xFA42A8B73ABBF48Cu64], + [0xFF2A760414536EFBu64, 0x9C69A97284B578D7u64], + [0xFEF5138519684ABAu64, 0xC38413CF25E2D70Du64], + [0x7EB258665FC25D69u64, 0xF46518C2EF5B8CD1u64], + [0xEF2F773FFBD97A61u64, 0x98BF2F79D5993802u64], + [0xAAFB550FFACFD8FAu64, 0xBEEEFB584AFF8603u64], + [0x95BA2A53F983CF38u64, 0xEEAABA2E5DBF6784u64], + [0xDD945A747BF26183u64, 0x952AB45CFA97A0B2u64], + [0x94F971119AEEF9E4u64, 0xBA756174393D88DFu64], + [0x7A37CD5601AAB85Du64, 0xE912B9D1478CEB17u64], + [0xAC62E055C10AB33Au64, 0x91ABB422CCB812EEu64], + [0x577B986B314D6009u64, 0xB616A12B7FE617AAu64], + [0xED5A7E85FDA0B80Bu64, 0xE39C49765FDF9D94u64], + [0x14588F13BE847307u64, 0x8E41ADE9FBEBC27Du64], + [0x596EB2D8AE258FC8u64, 0xB1D219647AE6B31Cu64], + [0x6FCA5F8ED9AEF3BBu64, 0xDE469FBD99A05FE3u64], + [0x25DE7BB9480D5854u64, 0x8AEC23D680043BEEu64], + [0xAF561AA79A10AE6Au64, 0xADA72CCC20054AE9u64], + [0x1B2BA1518094DA04u64, 0xD910F7FF28069DA4u64], + [0x90FB44D2F05D0842u64, 0x87AA9AFF79042286u64], + [0x353A1607AC744A53u64, 0xA99541BF57452B28u64], + [0x42889B8997915CE8u64, 0xD3FA922F2D1675F2u64], + [0x69956135FEBADA11u64, 0x847C9B5D7C2E09B7u64], + [0x43FAB9837E699095u64, 0xA59BC234DB398C25u64], + [0x94F967E45E03F4BBu64, 0xCF02B2C21207EF2Eu64], + [0x1D1BE0EEBAC278F5u64, 0x8161AFB94B44F57Du64], + [0x6462D92A69731732u64, 0xA1BA1BA79E1632DCu64], + [0x7D7B8F7503CFDCFEu64, 0xCA28A291859BBF93u64], + [0x5CDA735244C3D43Eu64, 0xFCB2CB35E702AF78u64], + [0x3A0888136AFA64A7u64, 0x9DEFBF01B061ADABu64], + [0x088AAA1845B8FDD0u64, 0xC56BAEC21C7A1916u64], + [0x8AAD549E57273D45u64, 0xF6C69A72A3989F5Bu64], + [0x36AC54E2F678864Bu64, 0x9A3C2087A63F6399u64], + [0x84576A1BB416A7DDu64, 0xC0CB28A98FCF3C7Fu64], + [0x656D44A2A11C51D5u64, 0xF0FDF2D3F3C30B9Fu64], + [0x9F644AE5A4B1B325u64, 0x969EB7C47859E743u64], + [0x873D5D9F0DDE1FEEu64, 0xBC4665B596706114u64], + [0xA90CB506D155A7EAu64, 0xEB57FF22FC0C7959u64], + [0x09A7F12442D588F2u64, 0x9316FF75DD87CBD8u64], + [0x0C11ED6D538AEB2Fu64, 0xB7DCBF5354E9BECEu64], + [0x8F1668C8A86DA5FAu64, 0xE5D3EF282A242E81u64], + [0xF96E017D694487BCu64, 0x8FA475791A569D10u64], + [0x37C981DCC395A9ACu64, 0xB38D92D760EC4455u64], + [0x85BBE253F47B1417u64, 0xE070F78D3927556Au64], + [0x93956D7478CCEC8Eu64, 0x8C469AB843B89562u64], + [0x387AC8D1970027B2u64, 0xAF58416654A6BABBu64], + [0x06997B05FCC0319Eu64, 0xDB2E51BFE9D0696Au64], + [0x441FECE3BDF81F03u64, 0x88FCF317F22241E2u64], + [0xD527E81CAD7626C3u64, 0xAB3C2FDDEEAAD25Au64], + [0x8A71E223D8D3B074u64, 0xD60B3BD56A5586F1u64], + [0xF6872D5667844E49u64, 0x85C7056562757456u64], + [0xB428F8AC016561DBu64, 0xA738C6BEBB12D16Cu64], + [0xE13336D701BEBA52u64, 0xD106F86E69D785C7u64], + [0xECC0024661173473u64, 0x82A45B450226B39Cu64], + [0x27F002D7F95D0190u64, 0xA34D721642B06084u64], + [0x31EC038DF7B441F4u64, 0xCC20CE9BD35C78A5u64], + [0x7E67047175A15271u64, 0xFF290242C83396CEu64], + [0x0F0062C6E984D386u64, 0x9F79A169BD203E41u64], + [0x52C07B78A3E60868u64, 0xC75809C42C684DD1u64], + [0xA7709A56CCDF8A82u64, 0xF92E0C3537826145u64], + [0x88A66076400BB691u64, 0x9BBCC7A142B17CCBu64], + [0x6ACFF893D00EA435u64, 0xC2ABF989935DDBFEu64], + [0x0583F6B8C4124D43u64, 0xF356F7EBF83552FEu64], + [0xC3727A337A8B704Au64, 0x98165AF37B2153DEu64], + [0x744F18C0592E4C5Cu64, 0xBE1BF1B059E9A8D6u64], + [0x1162DEF06F79DF73u64, 0xEDA2EE1C7064130Cu64], + [0x8ADDCB5645AC2BA8u64, 0x9485D4D1C63E8BE7u64], + [0x6D953E2BD7173692u64, 0xB9A74A0637CE2EE1u64], + [0xC8FA8DB6CCDD0437u64, 0xE8111C87C5C1BA99u64], + [0x1D9C9892400A22A2u64, 0x910AB1D4DB9914A0u64], + [0x2503BEB6D00CAB4Bu64, 0xB54D5E4A127F59C8u64], + [0x2E44AE64840FD61Du64, 0xE2A0B5DC971F303Au64], + [0x5CEAECFED289E5D2u64, 0x8DA471A9DE737E24u64], + [0x7425A83E872C5F47u64, 0xB10D8E1456105DADu64], + [0xD12F124E28F77719u64, 0xDD50F1996B947518u64], + [0x82BD6B70D99AAA6Fu64, 0x8A5296FFE33CC92Fu64], + [0x636CC64D1001550Bu64, 0xACE73CBFDC0BFB7Bu64], + [0x3C47F7E05401AA4Eu64, 0xD8210BEFD30EFA5Au64], + [0x65ACFAEC34810A71u64, 0x8714A775E3E95C78u64], + [0x7F1839A741A14D0Du64, 0xA8D9D1535CE3B396u64], + [0x1EDE48111209A050u64, 0xD31045A8341CA07Cu64], + [0x934AED0AAB460432u64, 0x83EA2B892091E44Du64], + [0xF81DA84D5617853Fu64, 0xA4E4B66B68B65D60u64], + [0x36251260AB9D668Eu64, 0xCE1DE40642E3F4B9u64], + [0xC1D72B7C6B426019u64, 0x80D2AE83E9CE78F3u64], + [0xB24CF65B8612F81Fu64, 0xA1075A24E4421730u64], + [0xDEE033F26797B627u64, 0xC94930AE1D529CFCu64], + [0x169840EF017DA3B1u64, 0xFB9B7CD9A4A7443Cu64], + [0x8E1F289560EE864Eu64, 0x9D412E0806E88AA5u64], + [0xF1A6F2BAB92A27E2u64, 0xC491798A08A2AD4Eu64], + [0xAE10AF696774B1DBu64, 0xF5B5D7EC8ACB58A2u64], + [0xACCA6DA1E0A8EF29u64, 0x9991A6F3D6BF1765u64], + [0x17FD090A58D32AF3u64, 0xBFF610B0CC6EDD3Fu64], + [0xDDFC4B4CEF07F5B0u64, 0xEFF394DCFF8A948Eu64], + [0x4ABDAF101564F98Eu64, 0x95F83D0A1FB69CD9u64], + [0x9D6D1AD41ABE37F1u64, 0xBB764C4CA7A4440Fu64], + [0x84C86189216DC5EDu64, 0xEA53DF5FD18D5513u64], + [0x32FD3CF5B4E49BB4u64, 0x92746B9BE2F8552Cu64], + [0x3FBC8C33221DC2A1u64, 0xB7118682DBB66A77u64], + [0x0FABAF3FEAA5334Au64, 0xE4D5E82392A40515u64], + [0x29CB4D87F2A7400Eu64, 0x8F05B1163BA6832Du64], + [0x743E20E9EF511012u64, 0xB2C71D5BCA9023F8u64], + [0x914DA9246B255416u64, 0xDF78E4B2BD342CF6u64], + [0x1AD089B6C2F7548Eu64, 0x8BAB8EEFB6409C1Au64], + [0xA184AC2473B529B1u64, 0xAE9672ABA3D0C320u64], + [0xC9E5D72D90A2741Eu64, 0xDA3C0F568CC4F3E8u64], + [0x7E2FA67C7A658892u64, 0x8865899617FB1871u64], + [0xDDBB901B98FEEAB7u64, 0xAA7EEBFB9DF9DE8Du64], + [0x552A74227F3EA565u64, 0xD51EA6FA85785631u64], + [0xD53A88958F87275Fu64, 0x8533285C936B35DEu64], + [0x8A892ABAF368F137u64, 0xA67FF273B8460356u64], + [0x2D2B7569B0432D85u64, 0xD01FEF10A657842Cu64], + [0x9C3B29620E29FC73u64, 0x8213F56A67F6B29Bu64], + [0x8349F3BA91B47B8Fu64, 0xA298F2C501F45F42u64], + [0x241C70A936219A73u64, 0xCB3F2F7642717713u64], + [0xED238CD383AA0110u64, 0xFE0EFB53D30DD4D7u64], + [0xF4363804324A40AAu64, 0x9EC95D1463E8A506u64], + [0xB143C6053EDCD0D5u64, 0xC67BB4597CE2CE48u64], + [0xDD94B7868E94050Au64, 0xF81AA16FDC1B81DAu64], + [0xCA7CF2B4191C8326u64, 0x9B10A4E5E9913128u64], + [0xFD1C2F611F63A3F0u64, 0xC1D4CE1F63F57D72u64], + [0xBC633B39673C8CECu64, 0xF24A01A73CF2DCCFu64], + [0xD5BE0503E085D813u64, 0x976E41088617CA01u64], + [0x4B2D8644D8A74E18u64, 0xBD49D14AA79DBC82u64], + [0xDDF8E7D60ED1219Eu64, 0xEC9C459D51852BA2u64], + [0xCABB90E5C942B503u64, 0x93E1AB8252F33B45u64], + [0x3D6A751F3B936243u64, 0xB8DA1662E7B00A17u64], + [0x0CC512670A783AD4u64, 0xE7109BFBA19C0C9Du64], + [0x27FB2B80668B24C5u64, 0x906A617D450187E2u64], + [0xB1F9F660802DEDF6u64, 0xB484F9DC9641E9DAu64], + [0x5E7873F8A0396973u64, 0xE1A63853BBD26451u64], + [0xDB0B487B6423E1E8u64, 0x8D07E33455637EB2u64], + [0x91CE1A9A3D2CDA62u64, 0xB049DC016ABC5E5Fu64], + [0x7641A140CC7810FBu64, 0xDC5C5301C56B75F7u64], + [0xA9E904C87FCB0A9Du64, 0x89B9B3E11B6329BAu64], + [0x546345FA9FBDCD44u64, 0xAC2820D9623BF429u64], + [0xA97C177947AD4095u64, 0xD732290FBACAF133u64], + [0x49ED8EABCCCC485Du64, 0x867F59A9D4BED6C0u64], + [0x5C68F256BFFF5A74u64, 0xA81F301449EE8C70u64], + [0x73832EEC6FFF3111u64, 0xD226FC195C6A2F8Cu64], +]; + // strconv — number↔string conversions. // // Mirrors Hare's strconv:: surface. The *tos functions return a @@ -3482,143 +5213,6 @@ export fn strerror(e: error) str = { return strings.dup(""); }; -// ascii — rune-class predicates and case folding for the ASCII range. -// Matches Hare's ascii::isdigit family (rune-taking signature). Runes -// outside 0..127 always answer `false`. The lexer hot path uses these -// inline; they are expected to inline to a couple of compares. - -package ascii; - -export fn isdigit(c: rune) bool = { - if (c < 48) { return false; }; - if (c > 57) { return false; }; - return true; -}; - -export fn isupper(c: rune) bool = { - if (c < 65) { return false; }; - if (c > 90) { return false; }; - return true; -}; - -export fn islower(c: rune) bool = { - if (c < 97) { return false; }; - if (c > 122) { return false; }; - return true; -}; - -export fn isalpha(c: rune) bool = { - if (isupper(c)) { return true; }; - return islower(c); -}; - -export fn isalnum(c: rune) bool = { - if (isalpha(c)) { return true; }; - return isdigit(c); -}; - -// isspace — the C/Hare set: space, tab, NL, VT, FF, CR. -export fn isspace(c: rune) bool = { - if (c == 32) { return true; }; // ' ' - if (c == 9) { return true; }; // '\t' - if (c == 10) { return true; }; // '\n' - if (c == 11) { return true; }; // '\v' - if (c == 12) { return true; }; // '\f' - if (c == 13) { return true; }; // '\r' - return false; -}; - -export fn isxdigit(c: rune) bool = { - if (isdigit(c)) { return true; }; - if (c >= 65) { - if (c <= 70) { return true; }; // 'A'..'F' - }; - if (c >= 97) { - if (c <= 102) { return true; }; // 'a'..'f' - }; - return false; -}; - -// valid — `c` is in the 0..127 ASCII range. -export fn valid(c: rune) bool = { - if (c < 0) { return false; }; - if (c > 127) { return false; }; - return true; -}; - -// validstr — every byte in `s` is ASCII (0..127). -export fn validstr(s: str) bool = { - let i: i32 = 0; - for (i < s.len) { - // High-bit test rather than `> 127u8`; both cgens lower - // the bitwise form identically. The `> u8` form picks - // JA vs JG depending on signed/unsigned dispatch. - if ((s[i] & 128u8) != 0u8) { return false; }; - i += 1; - }; - return true; -}; - -// iscntrl — control chars: 0..31 and 127. -export fn iscntrl(c: rune) bool = { - if (c >= 0) { if (c <= 31) { return true; }; }; - if (c == 127) { return true; }; - return false; -}; - -// isblank — space and tab. -export fn isblank(c: rune) bool = { - if (c == 32) { return true; }; // ' ' - if (c == 9) { return true; }; // '\t' - return false; -}; - -// isprint — printable: space through '~'. -export fn isprint(c: rune) bool = { - if (c < 32) { return false; }; - if (c > 126) { return false; }; - return true; -}; - -// isgraph — printable, non-space. -export fn isgraph(c: rune) bool = { - if (c < 33) { return false; }; - if (c > 126) { return false; }; - return true; -}; - -// ispunct — printable, non-alnum, non-space. -export fn ispunct(c: rune) bool = { - if (!isgraph(c)) { return false; }; - if (isalnum(c)) { return false; }; - return true; -}; - -// tolower / toupper — fold ASCII case. Non-letters pass through. -export fn tolower(c: rune) rune = { - if (isupper(c)) { return c + 32; }; - return c; -}; - -export fn toupper(c: rune) rune = { - if (islower(c)) { return c - 32; }; - return c; -}; - -// strcasecmp — three-way ASCII case-insensitive compare. -export fn strcasecmp(a: str, b: str) i32 = { - let n: i32 = a.len; - if (b.len < n) { n = b.len; }; - let i: i32 = 0; - for (i < n) { - let ca: rune = tolower(a[i]: rune); - let cb: rune = tolower(b[i]: rune); - if (ca != cb) { return (ca - cb): i32; }; - i += 1; - }; - return a.len - b.len; -}; - // selfhost/test/smoke.ww — end-to-end smoke for the selfhost path. // // Exercises the patterns the real ww-side compiler port will use: diff --git a/test/wcc/909_stof_run.c b/test/wcc/909_stof_run.c new file mode 100644 index 00000000..06674616 --- /dev/null +++ b/test/wcc/909_stof_run.c @@ -0,0 +1,55 @@ +/* + * 909_stof_run — execute the lib/strconv/test/stoftest fixture under the + * C-side `ww run` driver and assert exit 0. + * + * Same thin-wrapper shape as 922_decimal_run: stoftest.ww carries its own + * `export fn main()` that drives the @test fns (stof64/stof32 DEC + hex + * vectors, bit-exact) and signals which case failed via the exit code + * (signalled + 10, so cases 1..4 → exit 11..14). + * + * The fixture lives in lib/strconv/test/ (not lib/strconv/) so + * `import strconv` resolves to the lib/strconv DIRECTORY (full package: + * strconv.ww + decimal.ww + stof_data.ww + stof.ww), not the strconv.ww + * FILE — same rationale as 922_decimal_run. + */ +#include +#include +#include +#include + +static int +runwait(const char *cmd) +{ + int rc = system(cmd); + if (rc == -1) return -1; + if (WIFEXITED(rc)) return WEXITSTATUS(rc); + return 1; +} + +int +main(void) +{ + const char *bin = getenv("BIN"); + if (!bin) bin = "out/bin"; + char absbin[1024]; + if (bin[0] != '/') { + char cwd[1024]; + if (getcwd(cwd, sizeof cwd) == NULL) return 1; + snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); + bin = absbin; + } + char cwd[1024]; + if (getcwd(cwd, sizeof cwd) == NULL) return 1; + + const char *src = "lib/strconv/test/stoftest.ww"; + char path[1024], cmd[2048]; + snprintf(path, sizeof path, "%s/%s", cwd, src); + snprintf(cmd, sizeof cmd, "%s/ww run %s", bin, path); + int rc = runwait(cmd); + if (rc != 0) { + fprintf(stderr, "stof_run FAIL: %s exited %d\n", src, rc); + return 1; + } + printf("stof_run: %s ok\n", src); + return 0; +}