diff --git a/backlog/tasks/task-48 - Specializer-SQL-support-LIKE-alias-comma-join-wave-2.md b/backlog/tasks/task-48 - Specializer-SQL-support-LIKE-alias-comma-join-wave-2.md index 262931c..4dc9baa 100644 --- a/backlog/tasks/task-48 - Specializer-SQL-support-LIKE-alias-comma-join-wave-2.md +++ b/backlog/tasks/task-48 - Specializer-SQL-support-LIKE-alias-comma-join-wave-2.md @@ -1,10 +1,12 @@ --- id: TASK-48 -title: 'Specializer SQL support: LIKE, dynamic-table alias, comma-join rewrite (wave 2)' -status: In Progress +title: >- + Specializer SQL support: LIKE, dynamic-table alias, comma-join rewrite (wave + 2) +status: Done assignee: [] created_date: '2026-07-26 15:05' -updated_date: '2026-07-26 15:05' +updated_date: '2026-07-26 16:41' labels: [] milestone: m-7 dependencies: diff --git a/backlog/tasks/task-49 - Specializer-SQL-support-builtin-long-tail-similarity-string-subscripts-wave-3.md b/backlog/tasks/task-49 - Specializer-SQL-support-builtin-long-tail-similarity-string-subscripts-wave-3.md new file mode 100644 index 0000000..639f9db --- /dev/null +++ b/backlog/tasks/task-49 - Specializer-SQL-support-builtin-long-tail-similarity-string-subscripts-wave-3.md @@ -0,0 +1,48 @@ +--- +id: TASK-49 +title: >- + Specializer SQL support: builtin long tail + similarity + string subscripts + (wave 3) +status: In Progress +assignee: [] +created_date: '2026-07-26 16:45' +updated_date: '2026-07-26 17:44' +labels: [] +milestone: m-7 +dependencies: + - TASK-48 +documentation: + - docs/superpowers/specs/2026-07-25-sql-specializer-design.md + - docs/superpowers/specs/2026-07-26-wave1-builtin-pins.md +type: feature +ordinal: 43000 +--- + +## Description + + +Census-measured wave (2026-07-26 replay: 265/678 match, zero FAILs). Pure expression kernels riding the wave-1/2 machinery — pins fleet FIRST, shared semantic fn per op, both backends, differential + trap agreement, corpus zero-FAIL gate. Catalogue by measured first-blocker: SIMILARITY (39 cases): levenshtein/editdist3, damerau_levenshtein, jaccard, hamming/mismatches. STRING TAIL (~35): repeat, concat_ws, lpad/rpad, replace, reverse, translate, unicode/ord, strip_accents (oracle-extracted table, casemap playbook), ucase/lcase aliases, bit_length. STRING SUBSCRIPTS (39): array_extract/array_slice/list_slice on VARCHAR (the blocked sources are function/string/*, not lists). MATH TAIL (~10): add/subtract/multiply/divide aliases, mod, fmod, fdiv, nextafter. REJECT BY NAME: aggregates (sum/count/geomean), regexp_* (RE2, own wave), columns(), list-typed subscripts. + + +## Acceptance Criteria + +- [x] #1 Pins measured BEFORE implementation for all six families (similarity; pad/repeat/replace/translate/reverse/concat_ws; unicode/bit_length/case-aliases; VARCHAR subscripts; math aliases+fmod/fdiv/mod/nextafter; strip_accents oracle table) — JSON pins + spec addendum committed +- [x] #2 Every op is one shared semantic fn used verbatim by both backends; differential fuzz extended incl. trap agreement +- [x] #3 Aggregates and regexp family reject cleanly by name (no behavior change, messages recorded) +- [x] #4 Corpus replay: flips recorded here, zero FAILs, known-divergent list unchanged or justified +- [x] #5 mise gate-specializer green + + +## Implementation Notes + + +Pins committed (2622a14) BEFORE implementation. Fleet: 6 agents, 528k tokens, 300+ probes. Headline refutations: similarity is BYTE-based and damerau is the UNRESTRICTED DL variant (not OSA); fmod is FLOORED (divisor sign) while mod/% is C-fmod (dividend sign), // on doubles is PLAIN division with NULL-on-zero; rpad truncation keeps the PREFIX like lpad; hamming('','') is an ERROR; unicode('')=-1 but ascii('')=0 (sole ascii/unicode divergence); VARCHAR out-of-range subscripts give '' where LIST gives NULL and NULL is never an open bound; strip_accents = oracle map (4460 cps) + Hangul compose + context-dependent NUL truncation, DuckDB tables lag Unicode 16 by 57 cps. SCOPE CHANGE: reverse DESCOPED (measured grapheme-cluster/UAX-29 semantics incl. RI pairing = real segmentation machinery for 3 corpus cases; rejects by name, pins retained). concat/concat_ws restricted to VARCHAR args (DuckDB implicit float rendering not modeled). nextafter f64 only (FLOAT overload is f32; f32 columns already reject). + +IMPLEMENTATION COMPLETE. Corpus 265 -> 383 of 678, zero FAILs (deterministic, replayed twice); gate green (738 passed, 13 xfailed; cargo 129). New IR: StrOp2 += Levenshtein/Damerau/Jaccard/Hamming, StrOp3 (Replace/Translate), StrOp2i (Repeat/Extract), Spad, Sslice, Sord, StrOp1 += StripAccents, BinOp += Ffloordiv/Ffloormod/Fnextafter, ArithOp::IDiv (// operator + divide()); concat_ws/concat desugar onto Case/Or/Concat with the has-prior-non-null fold — no new IR. Shared semantic fns verbatim in both backends; 500-seed differential incl. trap agreement green. NULL-pre-empts-trap masking: Jaccard/Hamming mask BOTH operands to 'a' under the combined flag ('' is IN their trap domain — the Flogb pattern); Spad masks len->0. Overflow trap texts now DuckDB-verbatim with operand values (test-fleet finding, fixed in-wave; abs text measured too). f32 base tables classify clean-unsupported in the replay (nextafter's f32-grid was the wave's only FAIL class). 57 new oracle tests in 4 files (tests/test_duckdb_wave3_*.py), drafted by a 4-agent fleet, all green incl. NaN-bit assertions (fmod fff8 vs % 7ff8) and the data-dependent Insufficient-padding trap split. + + +## Final Summary + + +Wave 3 complete: 26 builtins across six measured families — similarity (byte-based levenshtein/editdist3, unrestricted damerau, byte-set jaccard, hamming/mismatches with verbatim trap texts), string builders (repeat, lpad/rpad with the data-dependent empty-pad trap, replace, translate, concat_ws/concat via a pure desugar), VARCHAR subscripts (array_extract/list_extract, array_slice/list_slice — '' out-of-range, NULL never an open bound), inspection (unicode/ord/ascii, bit_length, ucase/lcase), math tail (add/subtract/multiply/divide/mod aliases, the // operator, floor-pair fdiv/fmod, bit-exact nextafter), and strip_accents from an oracle-extracted 4460-codepoint table + Hangul jamo composition. reverse descoped by measurement (UAX-29 grapheme clusters for 3 corpus cases). Overflow trap texts brought to DuckDB's verbatim, operand-interpolated forms. Corpus 265 -> 383 of 678, zero FAILs; f32 base tables now classify honestly as clean-unsupported. Gate green: cargo 129, pytest 738 + 13 xfail, 500-seed cross-backend differential incl. trap agreement, 57 new oracle tests. + diff --git a/docs/superpowers/specs/2026-07-26-wave3-builtin-pins.md b/docs/superpowers/specs/2026-07-26-wave3-builtin-pins.md new file mode 100644 index 0000000..cb088b9 --- /dev/null +++ b/docs/superpowers/specs/2026-07-26-wave3-builtin-pins.md @@ -0,0 +1,178 @@ +# Wave-3 builtin pins — DuckDB 1.5.5, measured 2026-07-26 + +The implementation contract for TASK-49. Every claim was MEASURED through the +vectorized path (table columns, not literals) by a six-family probe fleet; the +full pin tables (300+ probes with exact reprs, float bit patterns, result +types, and verbatim error heads) are the JSON files in `pins-wave3/`. Literal +(constant-fold) spot checks matched the column path in every family unless +noted. Nothing here is inferred from documentation. + +## Similarity — levenshtein, editdist3, damerau_levenshtein, jaccard, hamming, mismatches + +- UNIT is raw UTF-8 **bytes** for all six — never codepoints or graphemes. + Witnesses: levenshtein('é','e')=2, jaccard('é','è')=1/3 (the two codepoints + share lead byte 0xC3 — codepoint-set semantics would give 0.0), + hamming('é','e') errors on byte-length mismatch while hamming('é','è')=1. +- Types: levenshtein/editdist3/damerau_levenshtein/hamming/mismatches → + BIGINT; jaccard → DOUBLE. All NULL-strict every arg. All case-sensitive. +- `editdist3` == `levenshtein` (200-pair sweep, 0 diffs). Plain Levenshtein, + empty strings fine: ('','')=0, ('','abc')=3. +- `damerau_levenshtein` is the **UNRESTRICTED** DL variant, transposition + cost 1 — NOT restricted OSA. Witness: ('ca','abc')=2 (OSA gives 3); + 300-pair sweep 0 diffs vs an unrestricted-DL reference, 3 diffs vs OSA. +- `jaccard` = |A∩B|/|A∪B| over **single-byte sets**, duplicates ignored: + ('ab','ba')=1.0, ('abc','abd')=0.5. Empty string on either side TRAPS: + "Invalid Input Error: Jaccard Function: An argument too short!". +- `hamming` == `mismatches` (identical values AND error texts — the errors + say "Mismatch Function" even for hamming). Byte-length mismatch traps + "…Strings must be of equal length!"; ANY empty input (both empty too) + traps "…Strings must be of length > 0!" — ('','') is an ERROR, not 0. +- Embedded NUL is an ordinary byte everywhere. + +## String builders — repeat, lpad, rpad, replace, translate, concat_ws, concat + +- `repeat(s,n)`: n≤0 → '' silently; NULL-strict; multi-byte safe. Huge n + deliberately unpinned (OOM risk) — corpus uses small n. +- `lpad/rpad(s,l,pad)`: l counts **codepoints**. Truncation (l < length(s)) + keeps the FIRST l codepoints for BOTH lpad and rpad (rpad('abcdef',3,'x') + = 'abc' — a naive rpad keeps the suffix; refuted). l≤0 → ''. pad cycles + left-to-right cut to (l−length(s)) codepoints, never splitting a + codepoint. pad='' traps "Invalid Input Error: Insufficient padding in + LPAD." (resp. RPAD) ONLY when growth is needed (l > length(s)); otherwise + the prefix returns without error — the trap is data-dependent. NULL-strict + all three args. +- `replace(s,from,to)`: empty needle is a strict NO-OP; leftmost + non-overlapping single pass, output not rescanned ('aaa','aa','b'→'ba'); + byte-sequence match; NULL-strict. +- `translate(s,from,to)`: per-**codepoint** map; from-chars beyond |to| are + DELETED; duplicate in from → FIRST wins; to-extras ignored; to='' deletes + every from-char; NULL-strict. +- `concat_ws(sep, args…)`: NULL args are SKIPPED with their separator; + NULL sep → NULL; all args NULL → '' (NOT NULL). Zero value-args is a + binder error. `concat(args…)` = same skip-NULL fold joined by '' — and is + NOT sugar for `||`, which stays NULL-strict. DuckDB implicitly casts + numeric/boolean VALUE args with its own float rendering ('1e+20', + '-0.0'); the SEPARATOR never casts. v0 restricts both functions to + VARCHAR args — non-VARCHAR args reject by name (we do not model DuckDB + float-to-string rendering). +- `reverse` is **DESCOPED**: measured to operate on UAX-29 grapheme + clusters (combining sequences attached, ZWJ emoji one unit, regional- + indicator pairs swap as units — real segmentation, not an approximation). + Full segmentation machinery for 3 corpus cases fails the cost test; + rejects by name citing grapheme semantics. Pins retained for a future + wave. + +## Inspection + case aliases — unicode, ord, ascii, bit_length, ucase, lcase + +- `unicode(s)`/`ord(s)`: FIRST codepoint as INTEGER; multi-char fine + ('abc'→97); `unicode('')` = **-1**. `ord` == `unicode` (exhaustive + 1,112,063-codepoint sweep + 210 strings, 0 diffs). +- `ascii(s)` == unicode EXCEPT `ascii('')` = **0** — the sole divergence, + and ascii does NOT restrict to ASCII (ascii('é')=233). +- `bit_length(s)` = 8 × strlen(s) exactly (bytes; BIGINT) — pure desugar. + `octet_length` does not exist in DuckDB 1.5.5 (binder error). +- `ucase`/`lcase` == `upper`/`lower`: exhaustive all-codepoint sweep both + directions, zero mismatches — pure aliases onto the existing casemap ops. + +## VARCHAR subscripts — array_extract, list_extract, array_slice, list_slice, s[i], s[a:b] + +- UNIT = **codepoints** (not graphemes: extract(2) of 'e'+U+0301 is the bare + combining mark). +- `array_extract(s,i)` (== `list_extract` == `s[i]`, 220-row sweeps): 1-based; + i=0 → ''; negative = from end (-1 last, resolution len+1+i with clamping); + out-of-range EITHER direction → **''** (the LIST overload gives NULL — do + not copy list semantics); NULL-strict; VARCHAR result. +- `array_slice(s,a,b)` (== `list_slice` == `s[a:b]`, 220-row sweeps): + both-ends-INCLUSIVE, 1-based; negative from-end (-1 = last char); a≤0 + clamps to start; b>len clamps to end; fully out-of-range or a>b → ''; + NULL bound → NULL (**NULL is NOT an open bound** — open bounds exist only + syntactically: s[:b] ≡ slice(1,b), s[a:] ≡ slice(a,-1), s[:] ≡ + slice(1,-1)). +- Step form REJECTS for every step value incl. 1: "Not implemented Error: + Slice with steps has not been implemented for string types, …". +- `substr(s,2,3)`='ell' vs `array_slice(s,2,3)`='el' — (start,LENGTH) vs + (begin,end) conventions; do not share lowering. + +## Math tail — add, subtract, multiply, divide, mod, fmod, fdiv, nextafter + +- `add/subtract/multiply/divide/mod` are EXACT aliases of `+ - * // %`: + same values, types, and byte-identical error texts (overflow traps incl. + INT64_MIN special cases) — pure frontend desugars onto existing ops. + Unary add(x)=x, subtract(x)=-x also exist. +- `divide`(int,int) = truncating INT division (7,2 → 3); on DOUBLE, `//` + and divide() are **plain division** (−7.5//2.0 = −3.75, NOT floor) with + divisor==0 → NULL. +- `mod`/`%` = truncated C-fmod (dividend's sign) on ints AND doubles; + INT x%0 → NULL (no trap); DOUBLE x%0.0 → **NaN 7ff8…** (a value, NOT + NULL — the fleet's decision line over-generalized from the int probes; + re-measured 2026-07-26, correction appended to pins-wave3/math_tail.json. + NaN-sign addendum (CI-discovered): the %-by-zero NaN comes from LIBM + fmod and its sign is PLATFORM-dependent — 7ff8 on Windows ucrt, fff8 on + Linux glibc; both engines use the platform libm so they agree with the + oracle per-platform, and the pin is bit AGREEMENT, not a constant. + fmod-by-zero is hardware-generated (0·inf under SSE) and stays fff8 + everywhere); mod(−7.5,2.5) = **−0.0**. +- `fmod`/`fdiv` are the FLOOR-division pair, always DOUBLE: fdiv = + floor(x/y) (±inf on zero divisor); fmod takes the **DIVISOR's** sign + (fmod(−7.5,2.5) = +0.0 where mod gives −0.0) and is computed as + x − floor(x/y)·y — so fmod(1.0, inf) = NaN (not 1.0 as C fmod); + fmod(x,0) = NaN. Computed NaNs surface as fff8… (negative quiet NaN) + where mod-by-zero NULLs and propagated NaNs stay 7ff8… — reproduce by + computing naturally on x86, verified by bit-exact pin tests. +- `nextafter` = C nextafter bit-exact, TOTAL (no traps): x==y returns y + (nextafter(0.0,−0.0) = −0.0); denormal/inf/NaN edges pinned by bits. Int + args promote to DOUBLE. The (FLOAT,FLOAT) overload returns FLOAT (f32 + nextafterf) — out of v0 scope (f32 columns already reject at binding). +- VARCHAR never implicitly casts into any of these (binder error) — matches + our no-implicit-cast rule. + +## strip_accents — oracle-extracted table + Hangul compose + +- NOT purely per-codepoint: per-cp map, THEN a canonical-compose pass whose + only observable effect is **Hangul jamo composition** (L+V→LV, LV+T→LVT, + including precomposed LV + T; formula 0xAC00+(L−0x1100)·588+(V−0x1161)·28 + +(T−0x11A7), verified over all 399 LV pairs + 200 LVT triples). Wrong-order + jamo do not compose. +- The per-cp map is ORACLE-EXTRACTED (full non-surrogate sweep): 4460 + changed codepoints; 2450 map to '' (Mn/Mc/Me marks — Indic vowel signs + are deleted, not just accents); every non-empty output is exactly ONE + codepoint. Includes accent-free rewrites (U+212A→'K', U+2126→Ω, + U+2000→U+2002). Compatibility decompositions NOT applied. +- DuckDB's tables lag Unicode 16 by 57 codepoints — the Rust table is + generated from the oracle map (scripts/gen_strip_accents.py, casemap/pow10 + playbook), NEVER from a host Unicode library. +- Row-local algorithm (validated 518/518 strings): all-ASCII input → + returned VERBATIM (embedded NULs preserved); otherwise truncate at the + first NUL (context-dependent NUL quirk — even a map-unchanged non-ASCII + char like an emoji triggers the truncating path), apply the map, compose + Hangul. Idempotent; total; NULL-strict. + +## Implementation addenda (found while building/testing) + +- Overflow trap texts are now DuckDB's verbatim, operand values included: + "Overflow in addition of INT64 (x + y)!" (sub/mul likewise), "Overflow + in division of x / y" for BOTH `//` and `%` on i64::MIN op −1 (no + trailing '!'), and "Overflow on abs(x)" (measured this wave). One + shared `overflow_msg`/`abs_overflow_msg` feeds both backends. +- hamming's two traps check EQUAL LENGTH first: hamming('', 'a') raises + the equal-length message; only ('','') reaches "length > 0". +- Documented laxity (same stance as wave-1 round digits): the engine + binds lpad/rpad with an i64 length column where DuckDB wants INTEGER; + constant integer lengths — the corpus shape — bind in both. +- concat/concat_ws literal -0.0 renders '0.0' in DuckDB (DECIMAL literal + path); the pinned '-0.0' is the DOUBLE column path, which we follow. +- Corpus replay: f32 base tables classify clean-unsupported — widening + to f64 is value-exact but every f32-GRID-sensitive op (nextafter ulp + steps, FLOAT→VARCHAR rendering) computes on the wrong grid (2 nextafter + cases were the wave's only FAILs before this rule; 3 sources, 5 cases). +- Unary add(x)=x / subtract(x)=-x exist in DuckDB but are unpinned — + they reject by arity until measured. + +## Catalogue rejections (do NOT ship) + +- Aggregates (`sum`, `count`, `geomean`) reject as aggregate-by-name. +- `regexp_matches`/`regexp_extract`/`regexp_full_match`: RE2 semantics, own + wave. +- `reverse`: grapheme-cluster semantics (above). +- Step slicing on VARCHAR, non-VARCHAR args to concat/concat_ws, + column-count-changing star macros (`columns(...)`). diff --git a/docs/superpowers/specs/pins-wave3/math_tail.json b/docs/superpowers/specs/pins-wave3/math_tail.json new file mode 100644 index 0000000..b98db7f --- /dev/null +++ b/docs/superpowers/specs/pins-wave3/math_tail.json @@ -0,0 +1,2092 @@ +{ + "family": "math_tail", + "duckdb_version": "1.5.5", + "decisions": [ + "add/subtract/multiply/divide/mod are exact aliases of + - * // % on the column path: same values, same result types, byte-identical error texts on every witness (basics, signs, overflow, zero divisor).", + "fdiv/fmod are NOT the function forms of / and %: they are a separate FLOOR-division pair. fdiv(x,y)=floor(x/y) as DOUBLE (fdiv(-7,2)=-4.0, fdiv(7,-3)=-3.0, fdiv(-7.5,2.0)=-4.0); fmod is floored mod taking the DIVISOR's sign (fmod(-7.5,2.0)=0.5, fmod(7.5,-2.0)=-0.5, fmod(-7,3)=2.0, fmod(7,-3)=-2.0).", + "mod/% on DOUBLE is C-fmod (truncated, dividend's sign): mod(-7.5,2.0)=-1.5, mod(7.5,-2.0)=1.5, mod(-7.5,2.5)=-0.0 (bits 8000000000000000), mod(-0.0,1.0)=-0.0. DuckDB's 'fmod' is NOT C fmod; DuckDB's 'mod' IS.", + "divide(int,int) = truncating integer division, result type = arg type (divide(7,2)=3 BIGINT, divide(-7,2)=-3, INTEGER cols stay INTEGER). divide(double,double) and // on DOUBLE = PLAIN division (NOT floor: -7.5//2.0=-3.75) except divisor==0 -> NULL.", + "Zero-divisor matrix (both int and double columns, no errors anywhere): / -> IEEE +-inf (7/0=inf, -7/0=-inf, 0.0/0.0=nan fff8000000000000); // and divide() and % and mod() -> NULL; fdiv -> +-inf; fmod -> nan fff8000000000000. NULL dividend with zero divisor -> NULL (no trap).", + "Result types: int op int -> same int width for + - * // divide % mod (typeof INTEGER stays INTEGER, incl. INT32-specific overflow error); / on ints -> DOUBLE always; fdiv/fmod/nextafter/pow/power/**/^ -> DOUBLE even for integer args; any mixed int/double -> DOUBLE and takes the double semantics (divide(7,2.0)=3.5 NOT 3).", + "Int overflow traps (vectorized and literal path identical text): 'Out of Range Error: Overflow in addition of INT64 (9223372036854775807 + 1)!' / 'Overflow in subtraction of INT64 (-9223372036854775808 - 1)!' / 'Overflow in multiplication of INT64 (9223372036854775807 * 2)!' / INT32 variant for INTEGER cols. INT64_MIN//-1, divide, %, mod all trap 'Out of Range Error: Overflow in division of -9223372036854775808 / -1' (no trailing '!', and % says 'division'). INT64_MIN / -1 via / is fine -> 9.223372036854776e+18 DOUBLE.", + "DOUBLE arithmetic is raw IEEE: 0.1+0.2 -> 3fd3333333333334; 1e308+1e308 -> inf (never errors); inf+1 -> inf; nan propagates as 7ff8000000000000; add(-0.0,0.0)=+0.0, add(-0.0,-0.0)=-0.0.", + "NaN bit patterns are part of the contract at the Python boundary: computed NaNs from fmod and 0.0/0.0 come out NEGATIVE quiet NaN fff8000000000000; mod/% by zero and propagated NaN inputs come out 7ff8000000000000. fmod(inf,1)=nan, fmod(1,inf)=nan, fmod(0,inf)=nan (all fff8...) -- NOT the C fmod results (C gives 1.0/0.0 for the latter two); consistent with x-floor(x/y)*y evaluated in IEEE.", + "nextafter matches C nextafter bit-exactly: 1.0->2.0 = 3ff0000000000001; 1.0->0.0 = 3fefffffffffffff; 0.0->-1 = 8000000000000001 (-5e-324); -0.0->1 = 0000000000000001; x==y returns y (0.0,-0.0 -> -0.0; -0.0,0.0 -> +0.0); max->inf = inf; -max->-inf = -inf; inf->0 = 7fefffffffffffff; inf->inf = inf; nan either arg -> nan 7ff8000000000000. No errors/traps at all. Int args promote to DOUBLE; FLOAT,FLOAT overload returns FLOAT (nextafterf: 1.0000001192092896).", + "NULL propagation is strict NULL-in->NULL-out for every function and operator in the family, each arg position and both; typeof is preserved (BIGINT ops stay BIGINT, fdiv/fmod/nextafter stay DOUBLE).", + "VARCHAR does NOT implicitly cast into +/add: bind-time BinderException 'No function matches the given name and argument types '+(VARCHAR, BIGINT)'' with full candidate list; string content (unicode, NUL) is irrelevant because binding fails before execution. '3'+1 errors too.", + "Identities: pow == power == ** == ^ (7^2=49.0 DOUBLE; ^ is POWER not xor; xor(7,2)=5 BIGINT is the bitwise fn). Unary add(x)=x and subtract(x)=-x exist.", + "Literal-path divergences to note: integer literals are INTEGER so divide(7,2) literal -> INTEGER 3 (BIGINT col path -> BIGINT); decimal literals are DECIMAL (typeof(1.0)=DECIMAL(2,1)) so 7.5 % 2.5 literal -> DECIMAL(2,1) Decimal('0.0') via the DECIMAL mod overload (DOUBLE col path -> DOUBLE 0.0), while 7.5//2.5, fdiv, fmod, nextafter on decimal literals cast to DOUBLE. Values/errors otherwise identical between fold and vectorized paths on all witnesses." + ], + "probes": [ + { + "sql": "SELECT a + b AS v, typeof(a + b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[9]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT add(a, b) AS v, typeof(add(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[9]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT a - b AS v, typeof(a - b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[5]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT subtract(a, b) AS v, typeof(subtract(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[5]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT a * b AS v, typeof(a * b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[14]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT multiply(a, b) AS v, typeof(multiply(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[14]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT a / b AS v, typeof(a / b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[3.5]", + "result_type": "DOUBLE", + "bits": [ + "400c000000000000" + ], + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT a // b AS v, typeof(a // b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[3]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT divide(a, b) AS v, typeof(divide(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[3]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT a % b AS v, typeof(a % b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[1]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT mod(a, b) AS v, typeof(mod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[1]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT fdiv(a, b) AS v, typeof(fdiv(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[3.0]", + "result_type": "DOUBLE", + "bits": [ + "4008000000000000" + ], + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT fmod(a, b) AS v, typeof(fmod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[1.0]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000000" + ], + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT pow(a, b) AS v, typeof(pow(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[49.0]", + "result_type": "DOUBLE", + "bits": [ + "4048800000000000" + ], + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT power(a, b) AS v, typeof(power(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[49.0]", + "result_type": "DOUBLE", + "bits": [ + "4048800000000000" + ], + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT a ** b AS v, typeof(a ** b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[49.0]", + "result_type": "DOUBLE", + "bits": [ + "4048800000000000" + ], + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT a ^ b AS v, typeof(a ^ b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[49.0]", + "result_type": "DOUBLE", + "bits": [ + "4048800000000000" + ], + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT xor(a, b) AS v, typeof(xor(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[5]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT add(a) AS v, typeof(add(a)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[7]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT subtract(a) AS v, typeof(subtract(a)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 2)]", + "result_repr": "[-7]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "bigint basics" + }, + { + "sql": "SELECT a + b AS v, typeof(a + b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(9223372036854775807, 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in addition of INT64 (9223372036854775807 + 1)!", + "note": "int64 add overflow, operator" + }, + { + "sql": "SELECT add(a, b) AS v, typeof(add(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(9223372036854775807, 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in addition of INT64 (9223372036854775807 + 1)!", + "note": "int64 add overflow, function" + }, + { + "sql": "SELECT a - b AS v, typeof(a - b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-9223372036854775808, 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in subtraction of INT64 (-9223372036854775808 - 1)!", + "note": "int64 sub overflow, operator" + }, + { + "sql": "SELECT subtract(a, b) AS v, typeof(subtract(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-9223372036854775808, 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in subtraction of INT64 (-9223372036854775808 - 1)!", + "note": "int64 sub overflow, function" + }, + { + "sql": "SELECT a * b AS v, typeof(a * b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(9223372036854775807, 2)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in multiplication of INT64 (9223372036854775807 * 2)!", + "note": "int64 mul overflow, operator" + }, + { + "sql": "SELECT multiply(a, b) AS v, typeof(multiply(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(9223372036854775807, 2)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in multiplication of INT64 (9223372036854775807 * 2)!", + "note": "int64 mul overflow, function" + }, + { + "sql": "SELECT a // b AS v, typeof(a // b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-9223372036854775808, -1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in division of -9223372036854775808 / -1", + "note": "INT64_MIN / -1 edge" + }, + { + "sql": "SELECT divide(a, b) AS v, typeof(divide(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-9223372036854775808, -1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in division of -9223372036854775808 / -1", + "note": "INT64_MIN / -1 edge" + }, + { + "sql": "SELECT a / b AS v, typeof(a / b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-9223372036854775808, -1)]", + "result_repr": "[9.223372036854776e+18]", + "result_type": "DOUBLE", + "bits": [ + "43e0000000000000" + ], + "error": null, + "note": "INT64_MIN / -1 edge" + }, + { + "sql": "SELECT a % b AS v, typeof(a % b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-9223372036854775808, -1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in division of -9223372036854775808 / -1", + "note": "INT64_MIN / -1 edge" + }, + { + "sql": "SELECT mod(a, b) AS v, typeof(mod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-9223372036854775808, -1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in division of -9223372036854775808 / -1", + "note": "INT64_MIN / -1 edge" + }, + { + "sql": "SELECT a // b AS v, typeof(a // b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 3)]", + "result_repr": "[-2]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "negative dividend" + }, + { + "sql": "SELECT divide(a, b) AS v, typeof(divide(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 3)]", + "result_repr": "[-2]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "negative dividend" + }, + { + "sql": "SELECT a % b AS v, typeof(a % b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 3)]", + "result_repr": "[-1]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "negative dividend" + }, + { + "sql": "SELECT mod(a, b) AS v, typeof(mod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 3)]", + "result_repr": "[-1]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "negative dividend" + }, + { + "sql": "SELECT fdiv(a, b) AS v, typeof(fdiv(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 3)]", + "result_repr": "[-3.0]", + "result_type": "DOUBLE", + "bits": [ + "c008000000000000" + ], + "error": null, + "note": "negative dividend" + }, + { + "sql": "SELECT fmod(a, b) AS v, typeof(fmod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 3)]", + "result_repr": "[2.0]", + "result_type": "DOUBLE", + "bits": [ + "4000000000000000" + ], + "error": null, + "note": "negative dividend" + }, + { + "sql": "SELECT a / b AS v, typeof(a / b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 3)]", + "result_repr": "[-2.3333333333333335]", + "result_type": "DOUBLE", + "bits": [ + "c002aaaaaaaaaaab" + ], + "error": null, + "note": "negative dividend" + }, + { + "sql": "SELECT a // b AS v, typeof(a // b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, -3)]", + "result_repr": "[-2]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "negative divisor" + }, + { + "sql": "SELECT divide(a, b) AS v, typeof(divide(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, -3)]", + "result_repr": "[-2]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "negative divisor" + }, + { + "sql": "SELECT a % b AS v, typeof(a % b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, -3)]", + "result_repr": "[1]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "negative divisor" + }, + { + "sql": "SELECT mod(a, b) AS v, typeof(mod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, -3)]", + "result_repr": "[1]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "negative divisor" + }, + { + "sql": "SELECT fdiv(a, b) AS v, typeof(fdiv(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, -3)]", + "result_repr": "[-3.0]", + "result_type": "DOUBLE", + "bits": [ + "c008000000000000" + ], + "error": null, + "note": "negative divisor" + }, + { + "sql": "SELECT fmod(a, b) AS v, typeof(fmod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, -3)]", + "result_repr": "[-2.0]", + "result_type": "DOUBLE", + "bits": [ + "c000000000000000" + ], + "error": null, + "note": "negative divisor" + }, + { + "sql": "SELECT a // b AS v, typeof(a // b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 2)]", + "result_repr": "[-3]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "trunc vs floor witness" + }, + { + "sql": "SELECT divide(a, b) AS v, typeof(divide(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 2)]", + "result_repr": "[-3]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "trunc vs floor witness" + }, + { + "sql": "SELECT fdiv(a, b) AS v, typeof(fdiv(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 2)]", + "result_repr": "[-4.0]", + "result_type": "DOUBLE", + "bits": [ + "c010000000000000" + ], + "error": null, + "note": "trunc vs floor witness" + }, + { + "sql": "SELECT a / b AS v, typeof(a / b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 0)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "int zero divisor" + }, + { + "sql": "SELECT a // b AS v, typeof(a // b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 0)]", + "result_repr": "[None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "int zero divisor" + }, + { + "sql": "SELECT divide(a, b) AS v, typeof(divide(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 0)]", + "result_repr": "[None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "int zero divisor" + }, + { + "sql": "SELECT a % b AS v, typeof(a % b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 0)]", + "result_repr": "[None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "int zero divisor" + }, + { + "sql": "SELECT mod(a, b) AS v, typeof(mod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 0)]", + "result_repr": "[None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "int zero divisor" + }, + { + "sql": "SELECT fdiv(a, b) AS v, typeof(fdiv(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 0)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "int zero divisor" + }, + { + "sql": "SELECT fmod(a, b) AS v, typeof(fmod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(7, 0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "int zero divisor" + }, + { + "sql": "SELECT add(a, b) AS v, typeof(add(a, b)) AS ty FROM t", + "input_repr": "t(a INTEGER, b INTEGER); rows=[(7, 2)]", + "result_repr": "[9]", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "int32 result type" + }, + { + "sql": "SELECT divide(a, b) AS v, typeof(divide(a, b)) AS ty FROM t", + "input_repr": "t(a INTEGER, b INTEGER); rows=[(7, 2)]", + "result_repr": "[3]", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "int32 divide type" + }, + { + "sql": "SELECT a / b AS v, typeof(a / b) AS ty FROM t", + "input_repr": "t(a INTEGER, b INTEGER); rows=[(7, 2)]", + "result_repr": "[3.5]", + "result_type": "DOUBLE", + "bits": [ + "400c000000000000" + ], + "error": null, + "note": "int32 / operator" + }, + { + "sql": "SELECT a + b AS v, typeof(a + b) AS ty FROM t", + "input_repr": "t(a INTEGER, b INTEGER); rows=[(2147483647, 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in addition of INT32 (2147483647 + 1)!", + "note": "int32 add overflow, operator" + }, + { + "sql": "SELECT add(a, b) AS v, typeof(add(a, b)) AS ty FROM t", + "input_repr": "t(a INTEGER, b INTEGER); rows=[(2147483647, 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in addition of INT32 (2147483647 + 1)!", + "note": "int32 add overflow, function" + }, + { + "sql": "SELECT x + y AS v, typeof(x + y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[9.0]", + "result_type": "DOUBLE", + "bits": [ + "4022000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT add(x, y) AS v, typeof(add(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[9.0]", + "result_type": "DOUBLE", + "bits": [ + "4022000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT x - y AS v, typeof(x - y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[5.0]", + "result_type": "DOUBLE", + "bits": [ + "4014000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT subtract(x, y) AS v, typeof(subtract(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[5.0]", + "result_type": "DOUBLE", + "bits": [ + "4014000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT x * y AS v, typeof(x * y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[14.0]", + "result_type": "DOUBLE", + "bits": [ + "402c000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT multiply(x, y) AS v, typeof(multiply(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[14.0]", + "result_type": "DOUBLE", + "bits": [ + "402c000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT x / y AS v, typeof(x / y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[3.5]", + "result_type": "DOUBLE", + "bits": [ + "400c000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT x // y AS v, typeof(x // y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[3.5]", + "result_type": "DOUBLE", + "bits": [ + "400c000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT divide(x, y) AS v, typeof(divide(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[3.5]", + "result_type": "DOUBLE", + "bits": [ + "400c000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT x % y AS v, typeof(x % y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[1.0]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT mod(x, y) AS v, typeof(mod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[1.0]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT fdiv(x, y) AS v, typeof(fdiv(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[3.0]", + "result_type": "DOUBLE", + "bits": [ + "4008000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.0, 2.0)]", + "result_repr": "[1.0]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000000" + ], + "error": null, + "note": "double basics" + }, + { + "sql": "SELECT fdiv(x, y) AS v, typeof(fdiv(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.5, 2.5)]", + "result_repr": "[3.0]", + "result_type": "DOUBLE", + "bits": [ + "4008000000000000" + ], + "error": null, + "note": "double fractional" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.5, 2.5)]", + "result_repr": "[0.0]", + "result_type": "DOUBLE", + "bits": [ + "0000000000000000" + ], + "error": null, + "note": "double fractional" + }, + { + "sql": "SELECT mod(x, y) AS v, typeof(mod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.5, 2.5)]", + "result_repr": "[0.0]", + "result_type": "DOUBLE", + "bits": [ + "0000000000000000" + ], + "error": null, + "note": "double fractional" + }, + { + "sql": "SELECT x // y AS v, typeof(x // y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.5, 2.5)]", + "result_repr": "[3.0]", + "result_type": "DOUBLE", + "bits": [ + "4008000000000000" + ], + "error": null, + "note": "double fractional" + }, + { + "sql": "SELECT divide(x, y) AS v, typeof(divide(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.5, 2.5)]", + "result_repr": "[3.0]", + "result_type": "DOUBLE", + "bits": [ + "4008000000000000" + ], + "error": null, + "note": "double fractional" + }, + { + "sql": "SELECT fdiv(x, y) AS v, typeof(fdiv(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-7.5, 2.0)]", + "result_repr": "[-4.0]", + "result_type": "DOUBLE", + "bits": [ + "c010000000000000" + ], + "error": null, + "note": "double negative dividend" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-7.5, 2.0)]", + "result_repr": "[0.5]", + "result_type": "DOUBLE", + "bits": [ + "3fe0000000000000" + ], + "error": null, + "note": "double negative dividend" + }, + { + "sql": "SELECT mod(x, y) AS v, typeof(mod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-7.5, 2.0)]", + "result_repr": "[-1.5]", + "result_type": "DOUBLE", + "bits": [ + "bff8000000000000" + ], + "error": null, + "note": "double negative dividend" + }, + { + "sql": "SELECT x % y AS v, typeof(x % y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-7.5, 2.0)]", + "result_repr": "[-1.5]", + "result_type": "DOUBLE", + "bits": [ + "bff8000000000000" + ], + "error": null, + "note": "double negative dividend" + }, + { + "sql": "SELECT x // y AS v, typeof(x // y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-7.5, 2.0)]", + "result_repr": "[-3.75]", + "result_type": "DOUBLE", + "bits": [ + "c00e000000000000" + ], + "error": null, + "note": "double negative dividend" + }, + { + "sql": "SELECT divide(x, y) AS v, typeof(divide(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-7.5, 2.0)]", + "result_repr": "[-3.75]", + "result_type": "DOUBLE", + "bits": [ + "c00e000000000000" + ], + "error": null, + "note": "double negative dividend" + }, + { + "sql": "SELECT fdiv(x, y) AS v, typeof(fdiv(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.5, -2.0)]", + "result_repr": "[-4.0]", + "result_type": "DOUBLE", + "bits": [ + "c010000000000000" + ], + "error": null, + "note": "double negative divisor" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.5, -2.0)]", + "result_repr": "[-0.5]", + "result_type": "DOUBLE", + "bits": [ + "bfe0000000000000" + ], + "error": null, + "note": "double negative divisor" + }, + { + "sql": "SELECT mod(x, y) AS v, typeof(mod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.5, -2.0)]", + "result_repr": "[1.5]", + "result_type": "DOUBLE", + "bits": [ + "3ff8000000000000" + ], + "error": null, + "note": "double negative divisor" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-7.5, 2.5)]", + "result_repr": "[0.0]", + "result_type": "DOUBLE", + "bits": [ + "0000000000000000" + ], + "error": null, + "note": "exact multiple, expect -0.0?" + }, + { + "sql": "SELECT mod(x, y) AS v, typeof(mod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-7.5, 2.5)]", + "result_repr": "[-0.0]", + "result_type": "DOUBLE", + "bits": [ + "8000000000000000" + ], + "error": null, + "note": "exact multiple, expect -0.0?" + }, + { + "sql": "SELECT x / y AS v, typeof(x / y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 0.0)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "double zero divisor" + }, + { + "sql": "SELECT divide(x, y) AS v, typeof(divide(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 0.0)]", + "result_repr": "[None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "double zero divisor" + }, + { + "sql": "SELECT x // y AS v, typeof(x // y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 0.0)]", + "result_repr": "[None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "double zero divisor" + }, + { + "sql": "SELECT fdiv(x, y) AS v, typeof(fdiv(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 0.0)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "double zero divisor" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 0.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "double zero divisor" + }, + { + "sql": "SELECT mod(x, y) AS v, typeof(mod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 0.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "7ff8000000000000" + ], + "error": null, + "note": "double zero divisor" + }, + { + "sql": "SELECT x % y AS v, typeof(x % y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 0.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "7ff8000000000000" + ], + "error": null, + "note": "double zero divisor" + }, + { + "sql": "SELECT x / y AS v, typeof(x / y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-1.0, 0.0)]", + "result_repr": "[-inf]", + "result_type": "DOUBLE", + "bits": [ + "fff0000000000000" + ], + "error": null, + "note": "double -1/0" + }, + { + "sql": "SELECT x / y AS v, typeof(x / y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(0.0, 0.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "double 0/0" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(0.0, 0.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "fmod(0,0)" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(inf, 1.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "fmod(inf,1)" + }, + { + "sql": "SELECT x + y AS v, typeof(x + y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(inf, 1.0)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "inf + 1" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, inf)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "fmod(1,inf)" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(nan, 1.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "7ff8000000000000" + ], + "error": null, + "note": "fmod(nan,1)" + }, + { + "sql": "SELECT x + y AS v, typeof(x + y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(nan, 1.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "7ff8000000000000" + ], + "error": null, + "note": "nan + 1" + }, + { + "sql": "SELECT add(x, y) AS v, typeof(add(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(nan, 1.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "7ff8000000000000" + ], + "error": null, + "note": "add(nan,1)" + }, + { + "sql": "SELECT x + y AS v, typeof(x + y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1e+308, 1e+308)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "double overflow -> inf or error?" + }, + { + "sql": "SELECT add(x, y) AS v, typeof(add(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1e+308, 1e+308)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "double overflow -> inf or error?" + }, + { + "sql": "SELECT x * y AS v, typeof(x * y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1e+308, 1e+308)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "double overflow -> inf or error?" + }, + { + "sql": "SELECT multiply(x, y) AS v, typeof(multiply(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1e+308, 1e+308)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "double overflow -> inf or error?" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-0.0, 1.0)]", + "result_repr": "[0.0]", + "result_type": "DOUBLE", + "bits": [ + "0000000000000000" + ], + "error": null, + "note": "-0.0 handling" + }, + { + "sql": "SELECT mod(x, y) AS v, typeof(mod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-0.0, 1.0)]", + "result_repr": "[-0.0]", + "result_type": "DOUBLE", + "bits": [ + "8000000000000000" + ], + "error": null, + "note": "-0.0 handling" + }, + { + "sql": "SELECT x % y AS v, typeof(x % y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-0.0, 1.0)]", + "result_repr": "[-0.0]", + "result_type": "DOUBLE", + "bits": [ + "8000000000000000" + ], + "error": null, + "note": "-0.0 handling" + }, + { + "sql": "SELECT add(x, y) AS v, typeof(add(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-0.0, 1.0)]", + "result_repr": "[1.0]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000000" + ], + "error": null, + "note": "-0.0 handling" + }, + { + "sql": "SELECT add(x, y) AS v, typeof(add(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-0.0, 0.0)]", + "result_repr": "[0.0]", + "result_type": "DOUBLE", + "bits": [ + "0000000000000000" + ], + "error": null, + "note": "-0 + 0" + }, + { + "sql": "SELECT add(x, y) AS v, typeof(add(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-0.0, -0.0)]", + "result_repr": "[-0.0]", + "result_type": "DOUBLE", + "bits": [ + "8000000000000000" + ], + "error": null, + "note": "-0 + -0" + }, + { + "sql": "SELECT x + y AS v, typeof(x + y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(0.1, 0.2)]", + "result_repr": "[0.30000000000000004]", + "result_type": "DOUBLE", + "bits": [ + "3fd3333333333334" + ], + "error": null, + "note": "raw IEEE check" + }, + { + "sql": "SELECT add(x, y) AS v, typeof(add(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(0.1, 0.2)]", + "result_repr": "[0.30000000000000004]", + "result_type": "DOUBLE", + "bits": [ + "3fd3333333333334" + ], + "error": null, + "note": "raw IEEE check fn" + }, + { + "sql": "SELECT a + x AS v, typeof(a + x) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.5)]", + "result_repr": "[9.5]", + "result_type": "DOUBLE", + "bits": [ + "4023000000000000" + ], + "error": null, + "note": "mixed bigint/double" + }, + { + "sql": "SELECT add(a, x) AS v, typeof(add(a, x)) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.5)]", + "result_repr": "[9.5]", + "result_type": "DOUBLE", + "bits": [ + "4023000000000000" + ], + "error": null, + "note": "mixed bigint/double" + }, + { + "sql": "SELECT divide(a, x) AS v, typeof(divide(a, x)) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.5)]", + "result_repr": "[2.8]", + "result_type": "DOUBLE", + "bits": [ + "4006666666666666" + ], + "error": null, + "note": "mixed bigint/double" + }, + { + "sql": "SELECT mod(a, x) AS v, typeof(mod(a, x)) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.5)]", + "result_repr": "[2.0]", + "result_type": "DOUBLE", + "bits": [ + "4000000000000000" + ], + "error": null, + "note": "mixed bigint/double" + }, + { + "sql": "SELECT a % x AS v, typeof(a % x) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.5)]", + "result_repr": "[2.0]", + "result_type": "DOUBLE", + "bits": [ + "4000000000000000" + ], + "error": null, + "note": "mixed bigint/double" + }, + { + "sql": "SELECT a // x AS v, typeof(a // x) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.5)]", + "result_repr": "[2.8]", + "result_type": "DOUBLE", + "bits": [ + "4006666666666666" + ], + "error": null, + "note": "mixed bigint/double" + }, + { + "sql": "SELECT fdiv(a, x) AS v, typeof(fdiv(a, x)) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.5)]", + "result_repr": "[2.0]", + "result_type": "DOUBLE", + "bits": [ + "4000000000000000" + ], + "error": null, + "note": "mixed bigint/double" + }, + { + "sql": "SELECT fmod(a, x) AS v, typeof(fmod(a, x)) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.5)]", + "result_repr": "[2.0]", + "result_type": "DOUBLE", + "bits": [ + "4000000000000000" + ], + "error": null, + "note": "mixed bigint/double" + }, + { + "sql": "SELECT divide(a, x) AS v, typeof(divide(a, x)) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.0)]", + "result_repr": "[3.5]", + "result_type": "DOUBLE", + "bits": [ + "400c000000000000" + ], + "error": null, + "note": "mixed divide: int or double semantics?" + }, + { + "sql": "SELECT a // x AS v, typeof(a // x) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 2.0)]", + "result_repr": "[3.5]", + "result_type": "DOUBLE", + "bits": [ + "400c000000000000" + ], + "error": null, + "note": "mixed // : int or double semantics?" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 2.0)]", + "result_repr": "[1.0000000000000002]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000001" + ], + "error": null, + "note": "up from 1.0" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 0.0)]", + "result_repr": "[0.9999999999999999]", + "result_type": "DOUBLE", + "bits": [ + "3fefffffffffffff" + ], + "error": null, + "note": "down from 1.0" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(0.0, -1.0)]", + "result_repr": "[-5e-324]", + "result_type": "DOUBLE", + "bits": [ + "8000000000000001" + ], + "error": null, + "note": "0 toward -1: smallest neg denormal?" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-0.0, 1.0)]", + "result_repr": "[5e-324]", + "result_type": "DOUBLE", + "bits": [ + "0000000000000001" + ], + "error": null, + "note": "-0 toward 1: smallest pos denormal?" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(0.0, 0.0)]", + "result_repr": "[0.0]", + "result_type": "DOUBLE", + "bits": [ + "0000000000000000" + ], + "error": null, + "note": "x==y" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, 1.0)]", + "result_repr": "[1.0]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000000" + ], + "error": null, + "note": "x==y at 1" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.7976931348623157e+308, inf)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "max toward inf" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(inf, 0.0)]", + "result_repr": "[1.7976931348623157e+308]", + "result_type": "DOUBLE", + "bits": [ + "7fefffffffffffff" + ], + "error": null, + "note": "inf toward 0" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(inf, inf)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "inf,inf" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(nan, 1.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "7ff8000000000000" + ], + "error": null, + "note": "nan first arg" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(1.0, nan)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "7ff8000000000000" + ], + "error": null, + "note": "nan second arg" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-0.0, 0.0)]", + "result_repr": "[0.0]", + "result_type": "DOUBLE", + "bits": [ + "0000000000000000" + ], + "error": null, + "note": "-0 toward +0" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(0.0, -0.0)]", + "result_repr": "[-0.0]", + "result_type": "DOUBLE", + "bits": [ + "8000000000000000" + ], + "error": null, + "note": "+0 toward -0" + }, + { + "sql": "SELECT nextafter(a, b) AS v, typeof(nextafter(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(1, 2)]", + "result_repr": "[1.0000000000000002]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000001" + ], + "error": null, + "note": "nextafter int args" + }, + { + "sql": "SELECT nextafter(f, g) AS v, typeof(nextafter(f, g)) AS ty FROM t", + "input_repr": "t(f FLOAT, g FLOAT); rows=[(1.0, 2.0)]", + "result_repr": "[1.0000001192092896]", + "result_type": "FLOAT", + "bits": [ + "3ff0000020000000" + ], + "error": null, + "note": "nextafter float32 args" + }, + { + "sql": "SELECT add(a, b) AS v, typeof(add(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT subtract(a, b) AS v, typeof(subtract(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT multiply(a, b) AS v, typeof(multiply(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT divide(a, b) AS v, typeof(divide(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT mod(a, b) AS v, typeof(mod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT fdiv(a, b) AS v, typeof(fdiv(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT fmod(a, b) AS v, typeof(fmod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT nextafter(a, b) AS v, typeof(nextafter(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT a + b AS v, typeof(a + b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT a / b AS v, typeof(a / b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT a // b AS v, typeof(a // b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT a % b AS v, typeof(a % b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 1), (1, None), (None, None)]", + "result_repr": "[None, None, None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL each arg + both" + }, + { + "sql": "SELECT divide(a, b) AS v, typeof(divide(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 0)]", + "result_repr": "[None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL dividend with zero divisor: NULL or trap?" + }, + { + "sql": "SELECT mod(a, b) AS v, typeof(mod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 0)]", + "result_repr": "[None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL dividend with zero divisor: NULL or trap?" + }, + { + "sql": "SELECT a // b AS v, typeof(a // b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 0)]", + "result_repr": "[None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL dividend with zero divisor: NULL or trap?" + }, + { + "sql": "SELECT a % b AS v, typeof(a % b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(None, 0)]", + "result_repr": "[None]", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL dividend with zero divisor: NULL or trap?" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(None, 0.0)]", + "result_repr": "[None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "NULL dividend double zero divisor" + }, + { + "sql": "SELECT x / y AS v, typeof(x / y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(None, 0.0)]", + "result_repr": "[None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "NULL dividend double zero divisor" + }, + { + "sql": "SELECT fdiv(x, y) AS v, typeof(fdiv(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(None, 0.0)]", + "result_repr": "[None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "NULL dividend double zero divisor" + }, + { + "sql": "SELECT v + b AS v, typeof(v + b) AS ty FROM t", + "input_repr": "t(v VARCHAR, b BIGINT); rows=[('3', 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "BinderException: Binder Error: No function matches the given name and argument types '+(VARCHAR, BIGINT)'. You might need to add explicit type casts.\n\tCandidate functions:\n\t+(TINYINT) -> TINYINT\n\t+(TINYINT, TINYINT) -> TINYINT\n\t+(SMALLINT) -> SMALLINT\n\t+(SMALLINT, SMALLINT) -> SMALLINT\n\t+(INTEGER) -> INTEGER\n\t+(INTEGER, INTEGER) -> INTEGER\n\t+(BIGINT) -> BIGINT\n\t+(BIGINT, BIGINT) -> BIGINT\n\t+(HUGEINT) -> HUGEINT\n\t+(HUGEINT, HUGEINT) -> HUGEINT\n\t+(FLOAT) -> FLOAT\n\t+(FLOAT, FLOAT) -> FLOAT\n\t+(DOUBLE) -> DOUBLE\n\t+(DOUBLE, DOUBLE) -> DOUBLE\n\t+(DECIMAL) -> DECIMAL\n\t+(DECIMAL, DECIMAL) -> DECIMAL\n\t+(UTINYINT) -> UTINYINT\n\t+(UTINYINT, UTINYINT) -> UTINYINT\n\t+(USMALLINT) -> USMALLINT\n\t+(USMALLINT, USMALLINT) -> USMALLINT\n\t+(UINTEGER) -> UINTEGER\n\t+(UINTEGER, UINTEGER) -> UINTEGER\n\t+(UBIGINT) -> UBIGINT\n\t+(UBIGINT, UBIGINT) -> UBIGINT\n\t+(UHUGEINT) -> UHUGEINT\n\t+(UHUGEINT, UHUGEINT) -> UHUGEINT\n\t+(DATE, INTEGER) -> DATE\n\t+(INTEGER, DATE) -> DATE\n\t+(INTERVAL, INTERVAL) -> INTERVAL\n\t+(DATE, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, DATE) -> TIMESTAMP\n\t+(TIME, INTERVAL) -> TIME\n\t+(INTERVAL, TIME) -> TIME\n\t+(TIMESTAMP, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, TIMESTAMP) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE\n\t+(INTERVAL, TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n\t+(TIME, DATE) -> TIMESTAMP\n\t+(DATE, TIME) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, DATE) -> TIMESTAMP WITH TIME ZONE\n\t+(DATE, TIME WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\t+([ANY[]...]) -> ANY[]\n\t+(BIGNUM, BIGNUM) -> BIGNUM\n\t+(TIMESTAMP WITH TIME ZONE, INTERVAL) -> TIMESTAMP WITH TIME ZONE\n\t+(INTERVAL, TIMESTAMP WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\n\nLINE 1: SELECT v + b AS v, typeof(v + b) AS ty FROM t\n ^", + "note": "varchar numeric implicit cast, operator" + }, + { + "sql": "SELECT add(v, b) AS v, typeof(add(v, b)) AS ty FROM t", + "input_repr": "t(v VARCHAR, b BIGINT); rows=[('3', 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "BinderException: Binder Error: No function matches the given name and argument types 'add(VARCHAR, BIGINT)'. You might need to add explicit type casts.\n\tCandidate functions:\n\tadd(TINYINT) -> TINYINT\n\tadd(TINYINT, TINYINT) -> TINYINT\n\tadd(SMALLINT) -> SMALLINT\n\tadd(SMALLINT, SMALLINT) -> SMALLINT\n\tadd(INTEGER) -> INTEGER\n\tadd(INTEGER, INTEGER) -> INTEGER\n\tadd(BIGINT) -> BIGINT\n\tadd(BIGINT, BIGINT) -> BIGINT\n\tadd(HUGEINT) -> HUGEINT\n\tadd(HUGEINT, HUGEINT) -> HUGEINT\n\tadd(FLOAT) -> FLOAT\n\tadd(FLOAT, FLOAT) -> FLOAT\n\tadd(DOUBLE) -> DOUBLE\n\tadd(DOUBLE, DOUBLE) -> DOUBLE\n\tadd(DECIMAL) -> DECIMAL\n\tadd(DECIMAL, DECIMAL) -> DECIMAL\n\tadd(UTINYINT) -> UTINYINT\n\tadd(UTINYINT, UTINYINT) -> UTINYINT\n\tadd(USMALLINT) -> USMALLINT\n\tadd(USMALLINT, USMALLINT) -> USMALLINT\n\tadd(UINTEGER) -> UINTEGER\n\tadd(UINTEGER, UINTEGER) -> UINTEGER\n\tadd(UBIGINT) -> UBIGINT\n\tadd(UBIGINT, UBIGINT) -> UBIGINT\n\tadd(UHUGEINT) -> UHUGEINT\n\tadd(UHUGEINT, UHUGEINT) -> UHUGEINT\n\tadd(DATE, INTEGER) -> DATE\n\tadd(INTEGER, DATE) -> DATE\n\tadd(INTERVAL, INTERVAL) -> INTERVAL\n\tadd(DATE, INTERVAL) -> TIMESTAMP\n\tadd(INTERVAL, DATE) -> TIMESTAMP\n\tadd(TIME, INTERVAL) -> TIME\n\tadd(INTERVAL, TIME) -> TIME\n\tadd(TIMESTAMP, INTERVAL) -> TIMESTAMP\n\tadd(INTERVAL, TIMESTAMP) -> TIMESTAMP\n\tadd(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE\n\tadd(INTERVAL, TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n\tadd(TIME, DATE) -> TIMESTAMP\n\tadd(DATE, TIME) -> TIMESTAMP\n\tadd(TIME WITH TIME ZONE, DATE) -> TIMESTAMP WITH TIME ZONE\n\tadd(DATE, TIME WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\tadd([ANY[]...]) -> ANY[]\n\tadd(BIGNUM, BIGNUM) -> BIGNUM\n\n\nLINE 1: SELECT add(v, b) AS v, typeof(add(v, b)) AS ty FROM t\n ^", + "note": "varchar numeric implicit cast, function" + }, + { + "sql": "SELECT v + b AS v, typeof(v + b) AS ty FROM t", + "input_repr": "t(v VARCHAR, b BIGINT); rows=[('\u00e9', 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "BinderException: Binder Error: No function matches the given name and argument types '+(VARCHAR, BIGINT)'. You might need to add explicit type casts.\n\tCandidate functions:\n\t+(TINYINT) -> TINYINT\n\t+(TINYINT, TINYINT) -> TINYINT\n\t+(SMALLINT) -> SMALLINT\n\t+(SMALLINT, SMALLINT) -> SMALLINT\n\t+(INTEGER) -> INTEGER\n\t+(INTEGER, INTEGER) -> INTEGER\n\t+(BIGINT) -> BIGINT\n\t+(BIGINT, BIGINT) -> BIGINT\n\t+(HUGEINT) -> HUGEINT\n\t+(HUGEINT, HUGEINT) -> HUGEINT\n\t+(FLOAT) -> FLOAT\n\t+(FLOAT, FLOAT) -> FLOAT\n\t+(DOUBLE) -> DOUBLE\n\t+(DOUBLE, DOUBLE) -> DOUBLE\n\t+(DECIMAL) -> DECIMAL\n\t+(DECIMAL, DECIMAL) -> DECIMAL\n\t+(UTINYINT) -> UTINYINT\n\t+(UTINYINT, UTINYINT) -> UTINYINT\n\t+(USMALLINT) -> USMALLINT\n\t+(USMALLINT, USMALLINT) -> USMALLINT\n\t+(UINTEGER) -> UINTEGER\n\t+(UINTEGER, UINTEGER) -> UINTEGER\n\t+(UBIGINT) -> UBIGINT\n\t+(UBIGINT, UBIGINT) -> UBIGINT\n\t+(UHUGEINT) -> UHUGEINT\n\t+(UHUGEINT, UHUGEINT) -> UHUGEINT\n\t+(DATE, INTEGER) -> DATE\n\t+(INTEGER, DATE) -> DATE\n\t+(INTERVAL, INTERVAL) -> INTERVAL\n\t+(DATE, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, DATE) -> TIMESTAMP\n\t+(TIME, INTERVAL) -> TIME\n\t+(INTERVAL, TIME) -> TIME\n\t+(TIMESTAMP, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, TIMESTAMP) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE\n\t+(INTERVAL, TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n\t+(TIME, DATE) -> TIMESTAMP\n\t+(DATE, TIME) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, DATE) -> TIMESTAMP WITH TIME ZONE\n\t+(DATE, TIME WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\t+([ANY[]...]) -> ANY[]\n\t+(BIGNUM, BIGNUM) -> BIGNUM\n\t+(TIMESTAMP WITH TIME ZONE, INTERVAL) -> TIMESTAMP WITH TIME ZONE\n\t+(INTERVAL, TIMESTAMP WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\n\nLINE 1: SELECT v + b AS v, typeof(v + b) AS ty FROM t\n ^", + "note": "e-acute" + }, + { + "sql": "SELECT add(v, b) AS v, typeof(add(v, b)) AS ty FROM t", + "input_repr": "t(v VARCHAR, b BIGINT); rows=[('e\u0301', 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "BinderException: Binder Error: No function matches the given name and argument types 'add(VARCHAR, BIGINT)'. You might need to add explicit type casts.\n\tCandidate functions:\n\tadd(TINYINT) -> TINYINT\n\tadd(TINYINT, TINYINT) -> TINYINT\n\tadd(SMALLINT) -> SMALLINT\n\tadd(SMALLINT, SMALLINT) -> SMALLINT\n\tadd(INTEGER) -> INTEGER\n\tadd(INTEGER, INTEGER) -> INTEGER\n\tadd(BIGINT) -> BIGINT\n\tadd(BIGINT, BIGINT) -> BIGINT\n\tadd(HUGEINT) -> HUGEINT\n\tadd(HUGEINT, HUGEINT) -> HUGEINT\n\tadd(FLOAT) -> FLOAT\n\tadd(FLOAT, FLOAT) -> FLOAT\n\tadd(DOUBLE) -> DOUBLE\n\tadd(DOUBLE, DOUBLE) -> DOUBLE\n\tadd(DECIMAL) -> DECIMAL\n\tadd(DECIMAL, DECIMAL) -> DECIMAL\n\tadd(UTINYINT) -> UTINYINT\n\tadd(UTINYINT, UTINYINT) -> UTINYINT\n\tadd(USMALLINT) -> USMALLINT\n\tadd(USMALLINT, USMALLINT) -> USMALLINT\n\tadd(UINTEGER) -> UINTEGER\n\tadd(UINTEGER, UINTEGER) -> UINTEGER\n\tadd(UBIGINT) -> UBIGINT\n\tadd(UBIGINT, UBIGINT) -> UBIGINT\n\tadd(UHUGEINT) -> UHUGEINT\n\tadd(UHUGEINT, UHUGEINT) -> UHUGEINT\n\tadd(DATE, INTEGER) -> DATE\n\tadd(INTEGER, DATE) -> DATE\n\tadd(INTERVAL, INTERVAL) -> INTERVAL\n\tadd(DATE, INTERVAL) -> TIMESTAMP\n\tadd(INTERVAL, DATE) -> TIMESTAMP\n\tadd(TIME, INTERVAL) -> TIME\n\tadd(INTERVAL, TIME) -> TIME\n\tadd(TIMESTAMP, INTERVAL) -> TIMESTAMP\n\tadd(INTERVAL, TIMESTAMP) -> TIMESTAMP\n\tadd(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE\n\tadd(INTERVAL, TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n\tadd(TIME, DATE) -> TIMESTAMP\n\tadd(DATE, TIME) -> TIMESTAMP\n\tadd(TIME WITH TIME ZONE, DATE) -> TIMESTAMP WITH TIME ZONE\n\tadd(DATE, TIME WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\tadd([ANY[]...]) -> ANY[]\n\tadd(BIGNUM, BIGNUM) -> BIGNUM\n\n\nLINE 1: SELECT add(v, b) AS v, typeof(add(v, b)) AS ty FROM t\n ^", + "note": "combining sequence" + }, + { + "sql": "SELECT v + b AS v, typeof(v + b) AS ty FROM t", + "input_repr": "t(v VARCHAR, b BIGINT); rows=[('\ud83d\ude00', 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "BinderException: Binder Error: No function matches the given name and argument types '+(VARCHAR, BIGINT)'. You might need to add explicit type casts.\n\tCandidate functions:\n\t+(TINYINT) -> TINYINT\n\t+(TINYINT, TINYINT) -> TINYINT\n\t+(SMALLINT) -> SMALLINT\n\t+(SMALLINT, SMALLINT) -> SMALLINT\n\t+(INTEGER) -> INTEGER\n\t+(INTEGER, INTEGER) -> INTEGER\n\t+(BIGINT) -> BIGINT\n\t+(BIGINT, BIGINT) -> BIGINT\n\t+(HUGEINT) -> HUGEINT\n\t+(HUGEINT, HUGEINT) -> HUGEINT\n\t+(FLOAT) -> FLOAT\n\t+(FLOAT, FLOAT) -> FLOAT\n\t+(DOUBLE) -> DOUBLE\n\t+(DOUBLE, DOUBLE) -> DOUBLE\n\t+(DECIMAL) -> DECIMAL\n\t+(DECIMAL, DECIMAL) -> DECIMAL\n\t+(UTINYINT) -> UTINYINT\n\t+(UTINYINT, UTINYINT) -> UTINYINT\n\t+(USMALLINT) -> USMALLINT\n\t+(USMALLINT, USMALLINT) -> USMALLINT\n\t+(UINTEGER) -> UINTEGER\n\t+(UINTEGER, UINTEGER) -> UINTEGER\n\t+(UBIGINT) -> UBIGINT\n\t+(UBIGINT, UBIGINT) -> UBIGINT\n\t+(UHUGEINT) -> UHUGEINT\n\t+(UHUGEINT, UHUGEINT) -> UHUGEINT\n\t+(DATE, INTEGER) -> DATE\n\t+(INTEGER, DATE) -> DATE\n\t+(INTERVAL, INTERVAL) -> INTERVAL\n\t+(DATE, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, DATE) -> TIMESTAMP\n\t+(TIME, INTERVAL) -> TIME\n\t+(INTERVAL, TIME) -> TIME\n\t+(TIMESTAMP, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, TIMESTAMP) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE\n\t+(INTERVAL, TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n\t+(TIME, DATE) -> TIMESTAMP\n\t+(DATE, TIME) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, DATE) -> TIMESTAMP WITH TIME ZONE\n\t+(DATE, TIME WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\t+([ANY[]...]) -> ANY[]\n\t+(BIGNUM, BIGNUM) -> BIGNUM\n\t+(TIMESTAMP WITH TIME ZONE, INTERVAL) -> TIMESTAMP WITH TIME ZONE\n\t+(INTERVAL, TIMESTAMP WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\n\nLINE 1: SELECT v + b AS v, typeof(v + b) AS ty FROM t\n ^", + "note": "emoji" + }, + { + "sql": "SELECT v + b AS v, typeof(v + b) AS ty FROM t", + "input_repr": "t(v VARCHAR, b BIGINT); rows=[('\u0130', 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "BinderException: Binder Error: No function matches the given name and argument types '+(VARCHAR, BIGINT)'. You might need to add explicit type casts.\n\tCandidate functions:\n\t+(TINYINT) -> TINYINT\n\t+(TINYINT, TINYINT) -> TINYINT\n\t+(SMALLINT) -> SMALLINT\n\t+(SMALLINT, SMALLINT) -> SMALLINT\n\t+(INTEGER) -> INTEGER\n\t+(INTEGER, INTEGER) -> INTEGER\n\t+(BIGINT) -> BIGINT\n\t+(BIGINT, BIGINT) -> BIGINT\n\t+(HUGEINT) -> HUGEINT\n\t+(HUGEINT, HUGEINT) -> HUGEINT\n\t+(FLOAT) -> FLOAT\n\t+(FLOAT, FLOAT) -> FLOAT\n\t+(DOUBLE) -> DOUBLE\n\t+(DOUBLE, DOUBLE) -> DOUBLE\n\t+(DECIMAL) -> DECIMAL\n\t+(DECIMAL, DECIMAL) -> DECIMAL\n\t+(UTINYINT) -> UTINYINT\n\t+(UTINYINT, UTINYINT) -> UTINYINT\n\t+(USMALLINT) -> USMALLINT\n\t+(USMALLINT, USMALLINT) -> USMALLINT\n\t+(UINTEGER) -> UINTEGER\n\t+(UINTEGER, UINTEGER) -> UINTEGER\n\t+(UBIGINT) -> UBIGINT\n\t+(UBIGINT, UBIGINT) -> UBIGINT\n\t+(UHUGEINT) -> UHUGEINT\n\t+(UHUGEINT, UHUGEINT) -> UHUGEINT\n\t+(DATE, INTEGER) -> DATE\n\t+(INTEGER, DATE) -> DATE\n\t+(INTERVAL, INTERVAL) -> INTERVAL\n\t+(DATE, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, DATE) -> TIMESTAMP\n\t+(TIME, INTERVAL) -> TIME\n\t+(INTERVAL, TIME) -> TIME\n\t+(TIMESTAMP, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, TIMESTAMP) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE\n\t+(INTERVAL, TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n\t+(TIME, DATE) -> TIMESTAMP\n\t+(DATE, TIME) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, DATE) -> TIMESTAMP WITH TIME ZONE\n\t+(DATE, TIME WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\t+([ANY[]...]) -> ANY[]\n\t+(BIGNUM, BIGNUM) -> BIGNUM\n\t+(TIMESTAMP WITH TIME ZONE, INTERVAL) -> TIMESTAMP WITH TIME ZONE\n\t+(INTERVAL, TIMESTAMP WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\n\nLINE 1: SELECT v + b AS v, typeof(v + b) AS ty FROM t\n ^", + "note": "U+0130" + }, + { + "sql": "SELECT v + b AS v, typeof(v + b) AS ty FROM t", + "input_repr": "t(v VARCHAR, b BIGINT); rows=[('\u212a', 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "BinderException: Binder Error: No function matches the given name and argument types '+(VARCHAR, BIGINT)'. You might need to add explicit type casts.\n\tCandidate functions:\n\t+(TINYINT) -> TINYINT\n\t+(TINYINT, TINYINT) -> TINYINT\n\t+(SMALLINT) -> SMALLINT\n\t+(SMALLINT, SMALLINT) -> SMALLINT\n\t+(INTEGER) -> INTEGER\n\t+(INTEGER, INTEGER) -> INTEGER\n\t+(BIGINT) -> BIGINT\n\t+(BIGINT, BIGINT) -> BIGINT\n\t+(HUGEINT) -> HUGEINT\n\t+(HUGEINT, HUGEINT) -> HUGEINT\n\t+(FLOAT) -> FLOAT\n\t+(FLOAT, FLOAT) -> FLOAT\n\t+(DOUBLE) -> DOUBLE\n\t+(DOUBLE, DOUBLE) -> DOUBLE\n\t+(DECIMAL) -> DECIMAL\n\t+(DECIMAL, DECIMAL) -> DECIMAL\n\t+(UTINYINT) -> UTINYINT\n\t+(UTINYINT, UTINYINT) -> UTINYINT\n\t+(USMALLINT) -> USMALLINT\n\t+(USMALLINT, USMALLINT) -> USMALLINT\n\t+(UINTEGER) -> UINTEGER\n\t+(UINTEGER, UINTEGER) -> UINTEGER\n\t+(UBIGINT) -> UBIGINT\n\t+(UBIGINT, UBIGINT) -> UBIGINT\n\t+(UHUGEINT) -> UHUGEINT\n\t+(UHUGEINT, UHUGEINT) -> UHUGEINT\n\t+(DATE, INTEGER) -> DATE\n\t+(INTEGER, DATE) -> DATE\n\t+(INTERVAL, INTERVAL) -> INTERVAL\n\t+(DATE, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, DATE) -> TIMESTAMP\n\t+(TIME, INTERVAL) -> TIME\n\t+(INTERVAL, TIME) -> TIME\n\t+(TIMESTAMP, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, TIMESTAMP) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE\n\t+(INTERVAL, TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n\t+(TIME, DATE) -> TIMESTAMP\n\t+(DATE, TIME) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, DATE) -> TIMESTAMP WITH TIME ZONE\n\t+(DATE, TIME WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\t+([ANY[]...]) -> ANY[]\n\t+(BIGNUM, BIGNUM) -> BIGNUM\n\t+(TIMESTAMP WITH TIME ZONE, INTERVAL) -> TIMESTAMP WITH TIME ZONE\n\t+(INTERVAL, TIMESTAMP WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\n\nLINE 1: SELECT v + b AS v, typeof(v + b) AS ty FROM t\n ^", + "note": "U+212A kelvin" + }, + { + "sql": "SELECT v + b AS v, typeof(v + b) AS ty FROM t", + "input_repr": "t(v VARCHAR, b BIGINT); rows=[('\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66', 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "BinderException: Binder Error: No function matches the given name and argument types '+(VARCHAR, BIGINT)'. You might need to add explicit type casts.\n\tCandidate functions:\n\t+(TINYINT) -> TINYINT\n\t+(TINYINT, TINYINT) -> TINYINT\n\t+(SMALLINT) -> SMALLINT\n\t+(SMALLINT, SMALLINT) -> SMALLINT\n\t+(INTEGER) -> INTEGER\n\t+(INTEGER, INTEGER) -> INTEGER\n\t+(BIGINT) -> BIGINT\n\t+(BIGINT, BIGINT) -> BIGINT\n\t+(HUGEINT) -> HUGEINT\n\t+(HUGEINT, HUGEINT) -> HUGEINT\n\t+(FLOAT) -> FLOAT\n\t+(FLOAT, FLOAT) -> FLOAT\n\t+(DOUBLE) -> DOUBLE\n\t+(DOUBLE, DOUBLE) -> DOUBLE\n\t+(DECIMAL) -> DECIMAL\n\t+(DECIMAL, DECIMAL) -> DECIMAL\n\t+(UTINYINT) -> UTINYINT\n\t+(UTINYINT, UTINYINT) -> UTINYINT\n\t+(USMALLINT) -> USMALLINT\n\t+(USMALLINT, USMALLINT) -> USMALLINT\n\t+(UINTEGER) -> UINTEGER\n\t+(UINTEGER, UINTEGER) -> UINTEGER\n\t+(UBIGINT) -> UBIGINT\n\t+(UBIGINT, UBIGINT) -> UBIGINT\n\t+(UHUGEINT) -> UHUGEINT\n\t+(UHUGEINT, UHUGEINT) -> UHUGEINT\n\t+(DATE, INTEGER) -> DATE\n\t+(INTEGER, DATE) -> DATE\n\t+(INTERVAL, INTERVAL) -> INTERVAL\n\t+(DATE, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, DATE) -> TIMESTAMP\n\t+(TIME, INTERVAL) -> TIME\n\t+(INTERVAL, TIME) -> TIME\n\t+(TIMESTAMP, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, TIMESTAMP) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE\n\t+(INTERVAL, TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n\t+(TIME, DATE) -> TIMESTAMP\n\t+(DATE, TIME) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, DATE) -> TIMESTAMP WITH TIME ZONE\n\t+(DATE, TIME WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\t+([ANY[]...]) -> ANY[]\n\t+(BIGNUM, BIGNUM) -> BIGNUM\n\t+(TIMESTAMP WITH TIME ZONE, INTERVAL) -> TIMESTAMP WITH TIME ZONE\n\t+(INTERVAL, TIMESTAMP WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\n\nLINE 1: SELECT v + b AS v, typeof(v + b) AS ty FROM t\n ^", + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT v + b AS v, typeof(v + b) AS ty FROM t", + "input_repr": "t(v VARCHAR, b BIGINT); rows=[('a\\x00b', 1)]", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "BinderException: Binder Error: No function matches the given name and argument types '+(VARCHAR, BIGINT)'. You might need to add explicit type casts.\n\tCandidate functions:\n\t+(TINYINT) -> TINYINT\n\t+(TINYINT, TINYINT) -> TINYINT\n\t+(SMALLINT) -> SMALLINT\n\t+(SMALLINT, SMALLINT) -> SMALLINT\n\t+(INTEGER) -> INTEGER\n\t+(INTEGER, INTEGER) -> INTEGER\n\t+(BIGINT) -> BIGINT\n\t+(BIGINT, BIGINT) -> BIGINT\n\t+(HUGEINT) -> HUGEINT\n\t+(HUGEINT, HUGEINT) -> HUGEINT\n\t+(FLOAT) -> FLOAT\n\t+(FLOAT, FLOAT) -> FLOAT\n\t+(DOUBLE) -> DOUBLE\n\t+(DOUBLE, DOUBLE) -> DOUBLE\n\t+(DECIMAL) -> DECIMAL\n\t+(DECIMAL, DECIMAL) -> DECIMAL\n\t+(UTINYINT) -> UTINYINT\n\t+(UTINYINT, UTINYINT) -> UTINYINT\n\t+(USMALLINT) -> USMALLINT\n\t+(USMALLINT, USMALLINT) -> USMALLINT\n\t+(UINTEGER) -> UINTEGER\n\t+(UINTEGER, UINTEGER) -> UINTEGER\n\t+(UBIGINT) -> UBIGINT\n\t+(UBIGINT, UBIGINT) -> UBIGINT\n\t+(UHUGEINT) -> UHUGEINT\n\t+(UHUGEINT, UHUGEINT) -> UHUGEINT\n\t+(DATE, INTEGER) -> DATE\n\t+(INTEGER, DATE) -> DATE\n\t+(INTERVAL, INTERVAL) -> INTERVAL\n\t+(DATE, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, DATE) -> TIMESTAMP\n\t+(TIME, INTERVAL) -> TIME\n\t+(INTERVAL, TIME) -> TIME\n\t+(TIMESTAMP, INTERVAL) -> TIMESTAMP\n\t+(INTERVAL, TIMESTAMP) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, INTERVAL) -> TIME WITH TIME ZONE\n\t+(INTERVAL, TIME WITH TIME ZONE) -> TIME WITH TIME ZONE\n\t+(TIME, DATE) -> TIMESTAMP\n\t+(DATE, TIME) -> TIMESTAMP\n\t+(TIME WITH TIME ZONE, DATE) -> TIMESTAMP WITH TIME ZONE\n\t+(DATE, TIME WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\t+([ANY[]...]) -> ANY[]\n\t+(BIGNUM, BIGNUM) -> BIGNUM\n\t+(TIMESTAMP WITH TIME ZONE, INTERVAL) -> TIMESTAMP WITH TIME ZONE\n\t+(INTERVAL, TIMESTAMP WITH TIME ZONE) -> TIMESTAMP WITH TIME ZONE\n\n\nLINE 1: SELECT v + b AS v, typeof(v + b) AS ty FROM t\n ^", + "note": "embedded NUL" + }, + { + "sql": "SELECT 7 / 2 AS v, typeof(7 / 2) AS ty", + "input_repr": "literal path", + "result_repr": "[3.5]", + "result_type": "DOUBLE", + "bits": [ + "400c000000000000" + ], + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT 7 // 2 AS v, typeof(7 // 2) AS ty", + "input_repr": "literal path", + "result_repr": "[3]", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT divide(7, 2) AS v, typeof(divide(7, 2)) AS ty", + "input_repr": "literal path", + "result_repr": "[3]", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT mod(-7, 3) AS v, typeof(mod(-7, 3)) AS ty", + "input_repr": "literal path", + "result_repr": "[-1]", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT fdiv(-7, 2) AS v, typeof(fdiv(-7, 2)) AS ty", + "input_repr": "literal path", + "result_repr": "[-4.0]", + "result_type": "DOUBLE", + "bits": [ + "c010000000000000" + ], + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT 9223372036854775807 + 1 AS v, typeof(9223372036854775807 + 1) AS ty", + "input_repr": "literal path", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in addition of INT64 (9223372036854775807 + 1)!", + "note": "literal overflow: fold-time error text?" + }, + { + "sql": "SELECT add(9223372036854775807, 1) AS v, typeof(add(9223372036854775807, 1)) AS ty", + "input_repr": "literal path", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "OutOfRangeException: Out of Range Error: Overflow in addition of INT64 (9223372036854775807 + 1)!", + "note": "literal overflow via function" + }, + { + "sql": "SELECT 1.0 AS v, typeof(1.0) AS ty", + "input_repr": "literal path", + "result_repr": "[Decimal('1.0')]", + "result_type": "DECIMAL(2,1)", + "bits": null, + "error": null, + "note": "decimal literal type" + }, + { + "sql": "SELECT fmod(1.0, 0.0) AS v, typeof(fmod(1.0, 0.0)) AS ty", + "input_repr": "literal path", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "literal fmod by zero (decimal literals!)" + }, + { + "sql": "SELECT fmod(1.0::DOUBLE, 0.0::DOUBLE) AS v, typeof(fmod(1.0::DOUBLE, 0.0::DOUBLE)) AS ty", + "input_repr": "literal path", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT 1.0 / 0.0 AS v, typeof(1.0 / 0.0) AS ty", + "input_repr": "literal path", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "decimal literal division by zero" + }, + { + "sql": "SELECT 1.0::DOUBLE / 0.0::DOUBLE AS v, typeof(1.0::DOUBLE / 0.0::DOUBLE) AS ty", + "input_repr": "literal path", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT 1 / 0 AS v, typeof(1 / 0) AS ty", + "input_repr": "literal path", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT 1 % 0 AS v, typeof(1 % 0) AS ty", + "input_repr": "literal path", + "result_repr": "[None]", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT nextafter(1.0, 2.0) AS v, typeof(nextafter(1.0, 2.0)) AS ty", + "input_repr": "literal path", + "result_repr": "[1.0000000000000002]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000001" + ], + "error": null, + "note": "decimal literals into nextafter" + }, + { + "sql": "SELECT nextafter(1.0::DOUBLE, 2.0::DOUBLE) AS v, typeof(nextafter(1.0::DOUBLE, 2.0::DOUBLE)) AS ty", + "input_repr": "literal path", + "result_repr": "[1.0000000000000002]", + "result_type": "DOUBLE", + "bits": [ + "3ff0000000000001" + ], + "error": null, + "note": "literal/constant-fold path" + }, + { + "sql": "SELECT fdiv(a, b) AS v, typeof(fdiv(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 0)]", + "result_repr": "[-inf]", + "result_type": "DOUBLE", + "bits": [ + "fff0000000000000" + ], + "error": null, + "note": "fdiv negative by int zero: -inf?" + }, + { + "sql": "SELECT fmod(a, b) AS v, typeof(fmod(a, b)) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "fmod negative by int zero: nan bits?" + }, + { + "sql": "SELECT a / b AS v, typeof(a / b) AS ty FROM t", + "input_repr": "t(a BIGINT, b BIGINT); rows=[(-7, 0)]", + "result_repr": "[-inf]", + "result_type": "DOUBLE", + "bits": [ + "fff0000000000000" + ], + "error": null, + "note": "int / operator negative by zero" + }, + { + "sql": "SELECT divide(x, y) AS v, typeof(divide(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(0.0, 0.0)]", + "result_repr": "[None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "divide double 0/0: NULL guard on divisor?" + }, + { + "sql": "SELECT fmod(x, y) AS v, typeof(fmod(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(0.0, inf)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "fff8000000000000" + ], + "error": null, + "note": "fmod(0, inf)" + }, + { + "sql": "SELECT x % y AS v, typeof(x % y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(7.5, -2.0)]", + "result_repr": "[1.5]", + "result_type": "DOUBLE", + "bits": [ + "3ff8000000000000" + ], + "error": null, + "note": "% parity with mod, negative divisor" + }, + { + "sql": "SELECT a % x AS v, typeof(a % x) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 0.0)]", + "result_repr": "[nan]", + "result_type": "DOUBLE", + "bits": [ + "7ff8000000000000" + ], + "error": null, + "note": "mixed % by double zero: nan or NULL?" + }, + { + "sql": "SELECT a / x AS v, typeof(a / x) AS ty FROM t", + "input_repr": "t(a BIGINT, x DOUBLE); rows=[(7, 0.0)]", + "result_repr": "[inf]", + "result_type": "DOUBLE", + "bits": [ + "7ff0000000000000" + ], + "error": null, + "note": "mixed / by double zero" + }, + { + "sql": "SELECT x // y AS v, typeof(x // y) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-1.0, 0.0)]", + "result_repr": "[None]", + "result_type": "DOUBLE", + "bits": null, + "error": null, + "note": "double // negative by zero: NULL?" + }, + { + "sql": "SELECT fdiv(x, y) AS v, typeof(fdiv(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-1.0, 0.0)]", + "result_repr": "[-inf]", + "result_type": "DOUBLE", + "bits": [ + "fff0000000000000" + ], + "error": null, + "note": "fdiv double negative by zero: -inf?" + }, + { + "sql": "SELECT nextafter(x, y) AS v, typeof(nextafter(x, y)) AS ty FROM t", + "input_repr": "t(x DOUBLE, y DOUBLE); rows=[(-1.7976931348623157e+308, -inf)]", + "result_repr": "[-inf]", + "result_type": "DOUBLE", + "bits": [ + "fff0000000000000" + ], + "error": null, + "note": "-max toward -inf" + }, + { + "sql": "SELECT 7.5 // 2.5 AS v, typeof(7.5 // 2.5) AS ty", + "input_repr": "literal path", + "result_repr": "[3.0]", + "result_type": "DOUBLE", + "bits": [ + "4008000000000000" + ], + "error": null, + "note": "decimal literals //: what overload?" + }, + { + "sql": "SELECT 7.5 % 2.5 AS v, typeof(7.5 % 2.5) AS ty", + "input_repr": "literal path", + "result_repr": "[Decimal('0.0')]", + "result_type": "DECIMAL(2,1)", + "bits": null, + "error": null, + "note": "decimal literals %" + }, + { + "sql": "SELECT fdiv(7.5, 2.5) AS v, typeof(fdiv(7.5, 2.5)) AS ty", + "input_repr": "literal path", + "result_repr": "[3.0]", + "result_type": "DOUBLE", + "bits": [ + "4008000000000000" + ], + "error": null, + "note": "decimal literals fdiv" + }, + { + "sql": "SELECT fmod(7.5, -2.0) AS v, typeof(fmod(7.5, -2.0)) AS ty", + "input_repr": "literal path", + "result_repr": "[-0.5]", + "result_type": "DOUBLE", + "bits": [ + "bfe0000000000000" + ], + "error": null, + "note": "decimal literals fmod sign" + } + ], + "corrections": [ + { + "date": "2026-07-26", + "refutes": "decision/surprise lines claiming DOUBLE % / mod() by zero -> NULL and 7ff8-vs-fff8 attribution to NULL cells", + "measured": "DOUBLE columns: x % 0.0 and mod(x, 0.0) -> NaN bits 7ff8000000000000 for x in {7.5, -7.5, 0.0} and y = -0.0 too; NEVER NULL. INT zero-divisor -> NULL stands. fmod-by-zero fff8 stands.", + "probes": [ + { + "sql": "SELECT x % y FROM d", + "input_repr": "d(x DOUBLE, y DOUBLE); rows=[(7.5,0.0),(-7.5,0.0),(0.0,0.0),(7.5,-0.0)]", + "result_repr": "[nan, nan, nan, nan]", + "bits": [ + "7ff8000000000000", + "7ff8000000000000", + "7ff8000000000000", + "7ff8000000000000" + ] + }, + { + "sql": "SELECT mod(x, y) FROM d", + "input_repr": "same", + "result_repr": "[nan, nan, nan, nan]", + "bits": [ + "7ff8000000000000", + "7ff8000000000000", + "7ff8000000000000", + "7ff8000000000000" + ] + } + ], + "note": "raw probes never covered this cell (only int rows + fmod(1.0,0.0)); the summary over-generalized" + } + ] +} \ No newline at end of file diff --git a/docs/superpowers/specs/pins-wave3/similarity.json b/docs/superpowers/specs/pins-wave3/similarity.json new file mode 100644 index 0000000..775c9e5 --- /dev/null +++ b/docs/superpowers/specs/pins-wave3/similarity.json @@ -0,0 +1,770 @@ +{ + "family": "similarity", + "duckdb_version": "1.5.5", + "decisions": [ + "Oracle: duckdb 1.5.5 (duckdb.__version__ probe).", + "UNIT: all six functions operate on raw UTF-8 BYTES, never codepoints or graphemes. Witnesses: levenshtein('\\u00e9','e')=2, levenshtein('\\U0001D11Ea','a')=4, levenshtein('e\\u0301','\\u00e9')=3, levenshtein(ZWJ-family,'man')=14, damerau_levenshtein('\\u00e9','e')=2, jaccard('\\u00e9','\\u00e8')=1/3 (shared 0xc3 byte), jaccard(U+1D11E,U+1D121)=0.6, hamming('\\u00e9','e') -> equal-length ERROR (byte lengths 2 vs 1) while hamming('\\u00e9','\\u00e8')=1.", + "RETURN TYPES: levenshtein/editdist3/damerau_levenshtein/hamming/mismatches -> BIGINT; jaccard -> DOUBLE (typeof probes).", + "NULL: any NULL argument (either or both positions) -> NULL for all six functions; typeof still resolves to BIGINT/DOUBLE.", + "CASE: all case-sensitive. levenshtein('A','a')=1, editdist3=1, damerau=1, hamming=1, jaccard('A','a')=0.0.", + "editdist3 IS levenshtein: 200-random-pair sweep 0 diffs; both equal plain-Levenshtein python reference on all 200; identical results on unicode witnesses ('\\u00e9','e')=2 and astral=4.", + "damerau_levenshtein is the UNRESTRICTED Damerau-Levenshtein (NOT OSA/restricted), transposition cost 1: ('ca','abc')=2 (OSA would be 3), ('ab','ba')=1, ('a cat','an act')=2; 300-pair sweep: 0 diffs vs unrestricted-DL reference, 3 diffs vs OSA, 5 vs plain lev; OSA-refuting pairs pinned individually: ('aca','aabbc')=3, ('cbbac','abcb')=3, ('ab','bcca')=3 (OSA says 4 for each).", + "jaccard: SET of single BYTES (not bigrams, not codepoints), duplicates ignored, |A&B|/|A|B|: ('ab','ba')=1.0, ('aab','ab')=1.0, ('abc','abd')=0.5 (bits 3fe0000000000000; bigram model would give 1/3).", + "jaccard EMPTY-STRING on either or both sides is an ERROR, not a value: 'Invalid Input Error: Jaccard Function: An argument too short!'", + "hamming IS mismatches (200 equal-length-pair sweep 0 diffs; identical error texts; error prefix is 'Mismatch Function' even when invoked as hamming).", + "hamming/mismatches: unequal BYTE length -> 'Invalid Input Error: Mismatch Function: Strings must be of equal length!'; ANY empty input including ('','') -> 'Invalid Input Error: Mismatch Function: Strings must be of length > 0!' (hamming('','') is an error, NOT 0).", + "levenshtein/editdist3/damerau_levenshtein accept empty strings: ('','')=0, ('','abc')=3, ('abc','')=3.", + "Embedded NUL (\\x00): insertable via parameter binding, fully preserved (length('a\\x00b')=3, strlen=3), treated as an ordinary byte: levenshtein('a\\x00b','ab')=1, hamming('a\\x00b','axb')=1, jaccard('a\\x00b','\\x00')=1/3.", + "Literal (constant-fold) path matched the column (vectorized) path on every spot-check, including error text: levenshtein('\\u00e9','e'), damerau('ca','abc'), jaccard('\\u00e9','\\u00e8'), jaccard('abc','abd'), hamming errors. No divergence found.", + "Incidental: octet_length(VARCHAR) does not exist in DuckDB 1.5.5 (Binder Error; only BLOB/BIT); use strlen() for byte length." + ], + "probes": [ + { + "sql": "duckdb.__version__", + "input_repr": null, + "result_repr": "'1.5.5'", + "result_type": null, + "bits": null, + "error": null, + "note": "oracle version" + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('A', 'a')", + "note": "case sensitivity", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('', '')", + "note": "both empty", + "result_repr": "0", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('', 'abc')", + "note": "left empty", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('abc', '')", + "note": "right empty", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('\u00e9', 'e')", + "note": "unit witness: codepoints=1, bytes=2", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('\ud834\udd1ea', 'a')", + "note": "astral unit witness: codepoints=1, bytes=4", + "result_repr": "4", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('e\u0301', '\u00e9')", + "note": "combining witness: codepoints=2, bytes=3, graphemes=1", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67', '\ud83d\udc68')", + "note": "ZWJ family vs man: codepoints=4, bytes=14, graphemes=1", + "result_repr": "14", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('\ud83d\ude00', '')", + "note": "emoji vs empty: codepoints=1, bytes=4", + "result_repr": "4", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('\u0130', 'I')", + "note": "U+0130 vs I: codepoints=1, bytes=2", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('\u212a', 'K')", + "note": "U+212A Kelvin vs K: codepoints=1, bytes=3", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "(None, 'abc')", + "note": "NULL left", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('abc', None)", + "note": "NULL right", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "(None, None)", + "note": "NULL both", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein(a,b), typeof(levenshtein(a,b)) FROM t", + "input_repr": "('a\\x00b', 'ab')", + "note": "embedded NUL: codepoint distance 1", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "INSERT 'a\\x00b'", + "input_repr": "'a\\x00b'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.\n\tCandidate functions:\n\toctet_length(BLOB) -> BIGINT\n\toctet_length(BIT) -> BIGINT\n\n\nLINE 1: SELECT length(a), octet_length(a) FROM t\n ^", + "note": "embedded NUL insert failed" + }, + { + "sql": "SELECT levenshtein('\u00e9','e'), typeof(levenshtein('\u00e9','e'))", + "input_repr": null, + "note": "literal path spot-check vs column path", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT levenshtein('A','a'), typeof(levenshtein('A','a'))", + "input_repr": null, + "note": "literal path spot-check", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT editdist3(a,b), typeof(editdist3(a,b)) FROM t", + "input_repr": "('A', 'a')", + "note": "editdist3 case", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT editdist3(a,b), typeof(editdist3(a,b)) FROM t", + "input_repr": "('\u00e9', 'e')", + "note": "editdist3 unit witness", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT editdist3(a,b), typeof(editdist3(a,b)) FROM t", + "input_repr": "(None, 'abc')", + "note": "NULL left", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT editdist3(a,b), typeof(editdist3(a,b)) FROM t", + "input_repr": "('abc', None)", + "note": "NULL right", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT editdist3(a,b), typeof(editdist3(a,b)) FROM t", + "input_repr": "('', '')", + "note": "both empty", + "result_repr": "0", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT a,b,levenshtein(a,b),editdist3(a,b) FROM sweep -- 200 random pairs alpha=abcd len 0..6 seed=42", + "input_repr": "200 pairs", + "result_repr": "editdist3!=levenshtein count=0; levenshtein!=python_ref count=0; examples=([], [])", + "result_type": null, + "bits": null, + "error": null, + "note": "alias-identity sweep + reference check" + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('ca', 'abc')", + "note": "THE variant witness: OSA(restricted)=3, unrestricted=2", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('ab', 'ba')", + "note": "transposition cost witness: 1 if transpositions cost 1, 2 if plain lev", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('A', 'a')", + "note": "case", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('', '')", + "note": "both empty", + "result_repr": "0", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('', 'abc')", + "note": "left empty", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('\u00e9', 'e')", + "note": "unit witness: codepoints=1, bytes=2", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('\ud834\udd1ea', 'a\ud834\udd1e')", + "note": "astral transposition: codepoints OSA=1, bytes OSA=2", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "(None, 'abc')", + "note": "NULL left", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('abc', None)", + "note": "NULL right", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein('ca','abc'), typeof(damerau_levenshtein('ca','abc'))", + "input_repr": null, + "note": "literal path spot-check", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT a,b,damerau_levenshtein(a,b) FROM sweep2 -- 300 random pairs alpha=abc len 0..6 seed cont.", + "input_repr": "300 pairs", + "result_repr": "diff_vs_OSA=3 diff_vs_unrestrictedDL=0 diff_vs_plain_lev=5; osa_ex=[('aca', 'aabbc', 3, 4), ('cbbac', 'abcb', 3, 4), ('ab', 'bcca', 3, 4)] dl_ex=[]", + "result_type": null, + "bits": null, + "error": null, + "note": "variant decision sweep" + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('ab', 'ba')", + "note": "char-set witness: 1.0 iff char-set element, 0.0 if bigram", + "result_repr": "1.0", + "result_type": "DOUBLE", + "bits": "3ff0000000000000", + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('aab', 'ab')", + "note": "duplicate handling: 1.0 iff set semantics", + "result_repr": "1.0", + "result_type": "DOUBLE", + "bits": "3ff0000000000000", + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('abc', 'abd')", + "note": "exact value: char-set=0.5, bigram=1/3", + "result_repr": "0.5", + "result_type": "DOUBLE", + "bits": "3fe0000000000000", + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('A', 'a')", + "note": "case: 0.0 iff case-sensitive", + "result_repr": "0.0", + "result_type": "DOUBLE", + "bits": "0000000000000000", + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('\u00e9', '\u00e8')", + "note": "byte-vs-codepoint: codepoint-sets disjoint=0.0, byte-sets share 0xc3 -> 1/3", + "result_repr": "0.3333333333333333", + "result_type": "DOUBLE", + "bits": "3fd5555555555555", + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('\ud834\udd1e', '\ud834\udd21')", + "note": "astral byte-overlap: codepoint=0.0, bytes share f0 9d 84 -> 0.6", + "result_repr": "0.6", + "result_type": "DOUBLE", + "bits": "3fe3333333333333", + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('a', 'a')", + "note": "identical singleton -> 1.0", + "result_repr": "1.0", + "result_type": "DOUBLE", + "bits": "3ff0000000000000", + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('', '')", + "note": "both empty: div-by-zero shape?", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Jaccard Function: An argument too short!" + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('a', '')", + "note": "right empty", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Jaccard Function: An argument too short!" + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('', 'abc')", + "note": "left empty", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Jaccard Function: An argument too short!" + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "(None, 'abc')", + "note": "NULL left", + "result_repr": "None", + "result_type": "DOUBLE", + "bits": null, + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('abc', None)", + "note": "NULL right", + "result_repr": "None", + "result_type": "DOUBLE", + "bits": null, + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "(None, None)", + "note": "NULL both", + "result_repr": "None", + "result_type": "DOUBLE", + "bits": null, + "error": null + }, + { + "sql": "SELECT jaccard(a,b), typeof(jaccard(a,b)) FROM t", + "input_repr": "('a\\x00b', '\\x00')", + "note": "embedded NUL as set element", + "result_repr": "0.3333333333333333", + "result_type": "DOUBLE", + "bits": "3fd5555555555555", + "error": null + }, + { + "sql": "SELECT jaccard('\u00e9','\u00e8'), typeof(jaccard('\u00e9','\u00e8'))", + "input_repr": null, + "note": "literal path spot-check", + "result_repr": "0.3333333333333333", + "result_type": "DOUBLE", + "bits": "3fd5555555555555", + "error": null + }, + { + "sql": "SELECT jaccard('abc','abd'), typeof(jaccard('abc','abd'))", + "input_repr": null, + "note": "literal path spot-check exact bits", + "result_repr": "0.5", + "result_type": "DOUBLE", + "bits": "3fe0000000000000", + "error": null + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('abc', 'abd')", + "note": "basic: 1", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('abc', 'ab')", + "note": "unequal length: exact error text", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of equal length!" + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('', '')", + "note": "both empty", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of length > 0!" + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('A', 'a')", + "note": "case", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('\u00e9', 'e')", + "note": "unit: codepoint-len equal (works->1), byte-len unequal (error)", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of equal length!" + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('\u0130', 'I')", + "note": "unit: U+0130 (2 bytes) vs I (1 byte)", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of equal length!" + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('e\u0301', '\u00e9')", + "note": "grapheme witness: grapheme-len 1v1, codepoint 2v1, bytes 3v2", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of equal length!" + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('\ud83d\ude00', 'a')", + "note": "emoji vs ascii: codepoint 1v1, bytes 4v1", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of equal length!" + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('a\\x00b', 'axb')", + "note": "embedded NUL equal length", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "(None, 'abc')", + "note": "NULL left", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('abc', None)", + "note": "NULL right", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT mismatches(a,b), typeof(mismatches(a,b)) FROM t", + "input_repr": "('abc', 'abd')", + "note": "basic", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT mismatches(a,b), typeof(mismatches(a,b)) FROM t", + "input_repr": "('abc', 'ab')", + "note": "unequal length: exact error text (same as hamming?)", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of equal length!" + }, + { + "sql": "SELECT mismatches(a,b), typeof(mismatches(a,b)) FROM t", + "input_repr": "('\u00e9', 'e')", + "note": "unit witness", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of equal length!" + }, + { + "sql": "SELECT mismatches(a,b), typeof(mismatches(a,b)) FROM t", + "input_repr": "(None, 'abc')", + "note": "NULL left", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT hamming('\u00e9','e')", + "input_repr": null, + "note": "literal path spot-check (error or 1?)", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of equal length!" + }, + { + "sql": "SELECT a,b,hamming(a,b),mismatches(a,b) FROM sweep3 -- 200 random equal-length pairs", + "input_repr": "200 pairs", + "result_repr": "hamming!=mismatches count=0; hamming!=python_ref count=0", + "result_type": null, + "bits": null, + "error": null, + "note": "alias-identity sweep" + }, + { + "sql": "python-side jaccard references", + "input_repr": null, + "result_repr": "{\"char('ab','ba')\": 1.0, \"char('aab','ab')\": 1.0, \"char('abc','abd')\": 0.5, \"bigram('abc','abd')\": 0.3333333333333333, \"bytes('\\\\xe9','\\\\xe8')\": 0.3333333333333333, 'bytes(gclef,gclef2)': 0.6}", + "result_type": null, + "bits": null, + "error": null, + "note": "reference values used in decisions" + }, + { + "sql": "SELECT length(a), strlen(a) FROM t -- a='a\\x00b'", + "input_repr": "'a\\x00b'", + "result_repr": "(3, 3)", + "result_type": null, + "bits": null, + "error": null, + "note": "NUL preserved? length=codepoints, strlen=bytes" + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('\u00e9', '\u00e8')", + "note": "both 2 bytes (c3a9 vs c3a8): byte-compare -> 1", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT hamming(a,b), typeof(hamming(a,b)) FROM t", + "input_repr": "('\u00e9\u00e9', '\u00e9\u00e9')", + "note": "identical multibyte -> 0", + "result_repr": "0", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT mismatches(a,b), typeof(mismatches(a,b)) FROM t", + "input_repr": "('', '')", + "note": "empty-empty error text for mismatches", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of length > 0!" + }, + { + "sql": "SELECT mismatches(a,b), typeof(mismatches(a,b)) FROM t", + "input_repr": "('\u00e9', '\u00e8')", + "note": "byte-compare -> 1", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT jaccard('','')", + "input_repr": null, + "note": "literal path: empty jaccard error text", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Jaccard Function: An argument too short!" + }, + { + "sql": "SELECT hamming('','')", + "input_repr": null, + "note": "literal path: empty hamming error text", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of length > 0!" + }, + { + "sql": "SELECT hamming('abc','ab')", + "input_repr": null, + "note": "literal path: unequal length error text", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Mismatch Function: Strings must be of equal length!" + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('aca', 'aabbc')", + "note": "OSA=4, unrestricted DL=3 -> refutes restricted variant", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('cbbac', 'abcb')", + "note": "OSA=4, unrestricted DL=3", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('ab', 'bcca')", + "note": "OSA=4, unrestricted DL=3", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT damerau_levenshtein(a,b), typeof(damerau_levenshtein(a,b)) FROM t", + "input_repr": "('a cat', 'an act')", + "note": "classic witness: OSA=3, unrestricted=2", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null + }, + { + "sql": "SELECT editdist3(a,b), typeof(editdist3(a,b)) FROM t", + "input_repr": "('\ud834\udd1ea', 'a')", + "note": "editdist3 astral: expect 4 like levenshtein", + "result_repr": "4", + "result_type": "BIGINT", + "bits": null, + "error": null + } + ] +} \ No newline at end of file diff --git a/docs/superpowers/specs/pins-wave3/string_builders.json b/docs/superpowers/specs/pins-wave3/string_builders.json new file mode 100644 index 0000000..389474b --- /dev/null +++ b/docs/superpowers/specs/pins-wave3/string_builders.json @@ -0,0 +1,1152 @@ +{ + "family": "string_builders", + "duckdb_version": "1.5.5", + "decisions": [ + "All family functions return VARCHAR (typeof pinned per probe); every literal spot-check (repeat, lpad, rpad, replace, translate, reverse, concat, concat_ws, lpad-empty-pad error) matched the column path.", + "repeat(s,n): n<=0 (probed 0 and -3) -> ''; NULL-strict in both args; unit is the whole string (multi-byte string repeated intact); embedded NUL repeats fine; huge n NOT probed (only n<=10 pinned).", + "lpad/rpad count CODEPOINTS, not bytes and not graphemes: lpad(e-acute,3,'x')='xx'+e-acute; combining seq e+U+0301 counts as 2 (lpad(seq,3,'x')='x'+seq).", + "lpad/rpad truncation (l < length(s)): BOTH keep the FIRST l codepoints (prefix) -- rpad('abcdef',3,'x')='abc', same as lpad; truncation slices through grapheme clusters at codepoint boundary (rpad(e+U+0301+'x',1,'p')='e').", + "lpad/rpad: negative l -> '' (silent, no error); l=0 -> ''; l=length(s) -> identity; NULL-strict in all three args.", + "pad='': raises 'Invalid Input Error: Insufficient padding in LPAD.' (resp. RPAD) ONLY when growth is needed (l > length(s)); l <= length(s) returns the truncated/identity prefix without error.", + "pad cycling: pad repeated left-to-right, truncated to (l - length(s)) codepoints, then prepended (lpad) or appended (rpad): lpad('a',6,'xyz')='xyzxya', rpad='axyzxy'; cut is at codepoint boundaries, multi-byte pad chars never split (lpad('a',4,e-acute+emoji)=e-acute+emoji+e-acute+'a').", + "replace: empty needle is a NO-OP (returns s unchanged, '' stays ''); leftmost non-overlapping single pass, output NOT rescanned ('aaa','aa','b'->'ba'; 'abab','ab','ba'->'baba'); case-sensitive; plain codepoint-sequence substring match (replacing base 'e' of a combining seq leaves the orphan mark); NULL-strict in all three args; NUL and ZWJ are ordinary needle chars.", + "reverse: GRAPHEME CLUSTERS (engine needs UAX-29 segmentation): combining seq stays attached, ZWJ family emoji stays one unit, flag RI-pair stays intact, and two adjacent flags swap as two units (US+FR -> FR+US) proving real RI pairing rather than an RI blob; NULL-strict; embedded NUL is its own cluster ('a\\x00b'->'b\\x00a').", + "translate: per-CODEPOINT map (astral chars and combining marks are single map units); from longer than to -> unmapped from-chars are DELETED; duplicate char in from -> FIRST occurrence wins ('aa' from='aa' to='xy' -> 'xx'); to longer than from -> extras ignored; from='' -> identity; to='' -> every from-char deleted; NULL-strict in all three args; NUL translatable.", + "concat_ws: NULL value-args are SKIPPED with their separator (',','x',NULL,'y' -> 'x,y'); NULL separator -> NULL; all value-args NULL (or a single NULL arg) -> '' (empty string, not NULL); zero value-args -> Binder Error (no concat_ws(VARCHAR) overload).", + "concat_ws value-args implicitly cast to VARCHAR: INTEGER 1->'1', BIGINT -7->'-7', DOUBLE 1.5->'1.5', 0.5->'0.5', 1e20->'1e+20', -0.0->'-0.0', BOOLEAN true->'true', FLOAT 1.5->'1.5', FLOAT 0.1->'0.1'; the SEPARATOR does NOT implicitly cast (INTEGER sep -> Binder Error).", + "concat: skips NULL args like concat_ws; all-NULL or single-NULL -> '' (never NULL); same implicit-cast rendering as concat_ws; variadic (1..n args). Contrast: the || operator IS NULL-strict ('x'||NULL -> NULL).", + "chr(0) works; VARCHAR stores embedded NUL; length('a\\x00b')=3 (NUL counts as a codepoint); repeat/lpad/replace/translate/reverse/concat/concat_ws all treat NUL as an ordinary character." + ], + "probes": [ + { + "name": "repeat basic", + "sql": "SELECT repeat(s, n), typeof(repeat(s, n)) FROM t", + "input_repr": "{'s': 'ab', 'n': 5}", + "result_repr": "'ababababab'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "repeat n=0", + "sql": "SELECT repeat(s, n), typeof(repeat(s, n)) FROM t", + "input_repr": "{'s': 'ab', 'n': 0}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "repeat n<0", + "sql": "SELECT repeat(s, n), typeof(repeat(s, n)) FROM t", + "input_repr": "{'s': 'ab', 'n': -3}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "repeat n NULL", + "sql": "SELECT repeat(s, n), typeof(repeat(s, n)) FROM t", + "input_repr": "{'s': 'ab', 'n': None}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "repeat s NULL", + "sql": "SELECT repeat(s, n), typeof(repeat(s, n)) FROM t", + "input_repr": "{'s': None, 'n': 2}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "repeat both NULL", + "sql": "SELECT repeat(s, n), typeof(repeat(s, n)) FROM t", + "input_repr": "{'s': None, 'n': None}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "repeat unicode unit", + "sql": "SELECT repeat(s, n), typeof(repeat(s, n)) FROM t", + "input_repr": "{'s': '\\xe9\\U0001f600', 'n': 3}", + "result_repr": "'\\xe9\\U0001f600\\xe9\\U0001f600\\xe9\\U0001f600'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "unit should be whole string" + }, + { + "name": "repeat n=10", + "sql": "SELECT repeat(s, n), typeof(repeat(s, n)) FROM t", + "input_repr": "{'s': 'abc', 'n': 10}", + "result_repr": "'abcabcabcabcabcabcabcabcabcabc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "huge n NOT probed (OOM risk); only sane n pinned" + }, + { + "name": "repeat embedded NUL", + "sql": "SELECT repeat(s, n), typeof(repeat(s, n)) FROM t", + "input_repr": "{'s': 'a\\x00b', 'n': 2}", + "result_repr": "'a\\x00ba\\x00b'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "repeat literal spotcheck", + "sql": "SELECT repeat('ab', 3), typeof(repeat('ab', 3))", + "input_repr": null, + "result_repr": "'ababab'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad basic", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 5, 'p': 'x'}", + "result_repr": "'xxabc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad basic", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 5, 'p': 'x'}", + "result_repr": "'abcxx'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad len unit: s multibyte", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': '\\xe9', 'l': 3, 'p': 'x'}", + "result_repr": "'xx\\xe9'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "'xx'+e-acute => codepoints; 'x'+e-acute => bytes" + }, + { + "name": "lpad pad multibyte", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'a', 'l': 3, 'p': '\\xe9'}", + "result_repr": "'\\xe9\\xe9a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "e-acute*2+'a' => codepoint counting" + }, + { + "name": "lpad combining len unit", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'e\\u0301', 'l': 3, 'p': 'x'}", + "result_repr": "'xe\\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "'x'+COMB => codepoints(2); 'xx'+COMB => graphemes(1)" + }, + { + "name": "lpad truncate side", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abcdef', 'l': 3, 'p': 'x'}", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad truncate side", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abcdef', 'l': 3, 'p': 'x'}", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad truncate combining", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'e\\u0301x', 'l': 1, 'p': 'p'}", + "result_repr": "'e'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "'e' => cuts inside cluster (codepoint); COMB => grapheme" + }, + { + "name": "lpad truncate unicode", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': '\\xe9abc', 'l': 2, 'p': 'x'}", + "result_repr": "'\\xe9a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad negative len", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': -1, 'p': 'x'}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad negative len", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': -1, 'p': 'x'}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad len=0", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 0, 'p': 'x'}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad len=len(s) identity", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 3, 'p': 'x'}", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad empty pad grow", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 5, 'p': ''}", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Insufficient padding in LPAD.", + "note": null + }, + { + "name": "rpad empty pad grow", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 5, 'p': ''}", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Insufficient padding in RPAD.", + "note": null + }, + { + "name": "lpad empty pad shrink", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abcdef', 'l': 3, 'p': ''}", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad pad cycle order", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'a', 'l': 6, 'p': 'xyz'}", + "result_repr": "'xyzxya'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad pad cycle order", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'a', 'l': 6, 'p': 'xyz'}", + "result_repr": "'axyzxy'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad multibyte pad cut", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'a', 'l': 4, 'p': '\\xe9\\U0001f600'}", + "result_repr": "'\\xe9\\U0001f600\\xe9a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "cut mid-cycle: codepoint boundary?" + }, + { + "name": "rpad multibyte pad cut", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'a', 'l': 4, 'p': '\\U0001f600\\xe9'}", + "result_repr": "'a\\U0001f600\\xe9\\U0001f600'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad s NULL", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': None, 'l': 5, 'p': 'x'}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad len NULL", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': None, 'p': 'x'}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad pad NULL", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 5, 'p': None}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad s NULL", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': None, 'l': 5, 'p': 'x'}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad len NULL", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': None, 'p': 'x'}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad pad NULL", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 5, 'p': None}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad NUL pad", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'ab', 'l': 4, 'p': '\\x00'}", + "result_repr": "'\\x00\\x00ab'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad literal empty pad spotcheck", + "sql": "SELECT lpad('abc', 5, ''), typeof(lpad('abc', 5, ''))", + "input_repr": null, + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Insufficient padding in LPAD.", + "note": null + }, + { + "name": "replace basic", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'hello', 'f': 'l', 't2': 'L'}", + "result_repr": "'heLLo'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace empty needle", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'abc', 'f': '', 't2': 'X'}", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace empty needle empty s", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': '', 'f': '', 't2': 'X'}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace overlap", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'aaa', 'f': 'aa', 't2': 'b'}", + "result_repr": "'ba'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace overlap2", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'aaaa', 'f': 'aa', 't2': 'b'}", + "result_repr": "'bb'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace case sens", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'AAA', 'f': 'a', 't2': 'b'}", + "result_repr": "'AAA'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace to empty", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'abcabc', 'f': 'b', 't2': ''}", + "result_repr": "'acac'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace unicode needle", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': '\\xe9\\xe9', 'f': '\\xe9', 't2': 'e'}", + "result_repr": "'ee'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace combining base", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'e\\u0301x', 'f': 'e', 't2': 'y'}", + "result_repr": "'y\\u0301x'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "codepoint-level? y+U+0301 expected if so" + }, + { + "name": "replace rescan?", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'abab', 'f': 'ab', 't2': 'ba'}", + "result_repr": "'baba'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "does output get rescanned?" + }, + { + "name": "replace s NULL", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': None, 'f': 'a', 't2': 'b'}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace from NULL", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'abc', 'f': None, 't2': 'b'}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace to NULL", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'abc', 'f': 'a', 't2': None}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace NUL needle", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': 'a\\x00b', 'f': '\\x00', 't2': 'X'}", + "result_repr": "'aXb'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace ZWJ needle", + "sql": "SELECT replace(s, f, t2), typeof(replace(s, f, t2)) FROM t", + "input_repr": "{'s': '\\U0001f468\\u200d\\U0001f469\\u200d\\U0001f467', 'f': '\\u200d', 't2': '-'}", + "result_repr": "'\\U0001f468-\\U0001f469-\\U0001f467'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ZWJ replace inside family emoji" + }, + { + "name": "reverse ascii", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': 'ab'}", + "result_repr": "'ba'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "reverse multibyte", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': '\\xe9a'}", + "result_repr": "'a\\xe9'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "reverse combining", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': 'e\\u0301x'}", + "result_repr": "'xe\\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "grapheme keeps e+U+0301 together" + }, + { + "name": "reverse ZWJ family", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': '\\U0001f468\\u200d\\U0001f469\\u200d\\U0001f467'}", + "result_repr": "'\\U0001f468\\u200d\\U0001f469\\u200d\\U0001f467'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "grapheme keeps whole family emoji intact" + }, + { + "name": "reverse flag", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': '\\U0001f1fa\\U0001f1f8a'}", + "result_repr": "'a\\U0001f1fa\\U0001f1f8'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "grapheme keeps both regional indicators together" + }, + { + "name": "reverse astral", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': '\\U00010348a'}", + "result_repr": "'a\\U00010348'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "reverse specials", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': '\\u0130\\u212az'}", + "result_repr": "'z\\u212a\\u0130'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "reverse NULL", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': None}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "reverse embedded NUL", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': 'a\\x00b'}", + "result_repr": "'b\\x00a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "reverse literal spotcheck", + "sql": "SELECT reverse('\u00e9a'), typeof(reverse('\u00e9a'))", + "input_repr": null, + "result_repr": "'a\\xe9'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "translate basic", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'abc', 'f': 'ab', 't2': 'xy'}", + "result_repr": "'xyc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "translate delete extras", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'abcb', 'f': 'abc', 't2': 'x'}", + "result_repr": "'x'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "from longer than to => delete?" + }, + { + "name": "translate dup in from", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'aa', 'f': 'aa', 't2': 'xy'}", + "result_repr": "'xx'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "first or last mapping wins?" + }, + { + "name": "translate to longer", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'ab', 'f': 'a', 't2': 'xy'}", + "result_repr": "'xb'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "extras in to ignored?" + }, + { + "name": "translate unicode", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': '\\xe9\\U0001f600z', 'f': '\\xe9\\U0001f600', 't2': 'ab'}", + "result_repr": "'abz'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "translate combining mark", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'e\\u0301', 'f': '\\u0301', 't2': 'x'}", + "result_repr": "'ex'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "per-codepoint => 'ex'" + }, + { + "name": "translate empty from", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'abc', 'f': '', 't2': 'xyz'}", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "translate empty to", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'abc', 'f': 'ab', 't2': ''}", + "result_repr": "'c'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "delete all mapped chars?" + }, + { + "name": "translate s NULL", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': None, 'f': 'a', 't2': 'b'}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "translate from NULL", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'abc', 'f': None, 't2': 'b'}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "translate to NULL", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'abc', 'f': 'a', 't2': None}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "translate NUL", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': 'a\\x00b', 'f': '\\x00', 't2': 'X'}", + "result_repr": "'aXb'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "translate astral map", + "sql": "SELECT translate(s, f, t2), typeof(translate(s, f, t2)) FROM t", + "input_repr": "{'s': '\\U00010348a', 'f': '\\U00010348', 't2': '\\U0001f600'}", + "result_repr": "'\\U0001f600a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws basic", + "sql": "SELECT concat_ws(sep, a, b), typeof(concat_ws(sep, a, b)) FROM t", + "input_repr": "{'sep': ',', 'a': 'x', 'b': 'y'}", + "result_repr": "'x,y'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws NULL arg mid", + "sql": "SELECT concat_ws(sep, a, b, c), typeof(concat_ws(sep, a, b, c)) FROM t", + "input_repr": "{'sep': ',', 'a': 'x', 'b': None, 'c': 'y'}", + "result_repr": "'x,y'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "skipped or strict?" + }, + { + "name": "concat_ws NULL first arg", + "sql": "SELECT concat_ws(sep, a, b), typeof(concat_ws(sep, a, b)) FROM t", + "input_repr": "{'sep': ',', 'a': None, 'b': 'y'}", + "result_repr": "'y'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws sep NULL", + "sql": "SELECT concat_ws(sep, a, b), typeof(concat_ws(sep, a, b)) FROM t", + "input_repr": "{'sep': None, 'a': 'x', 'b': 'y'}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws all args NULL", + "sql": "SELECT concat_ws(sep, a, b), typeof(concat_ws(sep, a, b)) FROM t", + "input_repr": "{'sep': ',', 'a': None, 'b': None}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws one arg", + "sql": "SELECT concat_ws(sep, a), typeof(concat_ws(sep, a)) FROM t", + "input_repr": "{'sep': ',', 'a': 'x'}", + "result_repr": "'x'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws one NULL arg", + "sql": "SELECT concat_ws(sep, a), typeof(concat_ws(sep, a)) FROM t", + "input_repr": "{'sep': ',', 'a': None}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws zero args", + "sql": "SELECT concat_ws(sep), typeof(concat_ws(sep)) FROM t", + "input_repr": "{'sep': ','}", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'concat_ws(VARCHAR)'. You might need to add explicit type casts.\n\tCandidate functions:\n\tconcat_ws(VARCHAR, ANY, [ANY...]) -> VARCHAR\n\n\nLINE 1: SELECT concat_ws(sep), typeof(concat_ws(sep)) FROM t\n ^", + "note": null + }, + { + "name": "concat_ws unicode sep", + "sql": "SELECT concat_ws(sep, a, b), typeof(concat_ws(sep, a, b)) FROM t", + "input_repr": "{'sep': '\\xe9', 'a': 'x', 'b': 'y'}", + "result_repr": "'x\\xe9y'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws NUL sep", + "sql": "SELECT concat_ws(sep, a, b), typeof(concat_ws(sep, a, b)) FROM t", + "input_repr": "{'sep': '\\x00', 'a': 'x', 'b': 'y'}", + "result_repr": "'x\\x00y'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws implicit casts", + "sql": "SELECT concat_ws(',', i, bg, d1, d2, d3, d4, bo), typeof(concat_ws(',', i, bg, d1, d2, d3, d4, bo)) FROM t", + "input_repr": "{'i': 1, 'bg': -7, 'd1': 1.5, 'd2': 0.5, 'd3': 1e+20, 'd4': -0.0, 'bo': True}", + "result_repr": "'1,-7,1.5,0.5,1e+20,-0.0,true'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "exact rendering of ints/doubles/bools" + }, + { + "name": "concat_ws float col", + "sql": "SELECT concat_ws(',', f1, f2), typeof(concat_ws(',', f1, f2)) FROM t", + "input_repr": "{'f1': 1.5, 'f2': 0.1}", + "result_repr": "'1.5,0.1'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat_ws literal spotcheck", + "sql": "SELECT concat_ws(',', 'x', NULL, 'y'), typeof(concat_ws(',', 'x', NULL, 'y'))", + "input_repr": null, + "result_repr": "'x,y'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat basic", + "sql": "SELECT concat(a, b), typeof(concat(a, b)) FROM t", + "input_repr": "{'a': 'x', 'b': 'y'}", + "result_repr": "'xy'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat NULL arg", + "sql": "SELECT concat(a, b), typeof(concat(a, b)) FROM t", + "input_repr": "{'a': 'x', 'b': None}", + "result_repr": "'x'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "skip or strict?" + }, + { + "name": "concat both NULL", + "sql": "SELECT concat(a, b), typeof(concat(a, b)) FROM t", + "input_repr": "{'a': None, 'b': None}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat 3 args", + "sql": "SELECT concat(a, b, c), typeof(concat(a, b, c)) FROM t", + "input_repr": "{'a': 'x', 'b': 'y', 'c': 'z'}", + "result_repr": "'xyz'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat implicit casts", + "sql": "SELECT concat(i, d1, bo), typeof(concat(i, d1, bo)) FROM t", + "input_repr": "{'i': 1, 'd1': 1.5, 'bo': True}", + "result_repr": "'11.5true'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat double 1e20/-0.0", + "sql": "SELECT concat(d3, ':', d4), typeof(concat(d3, ':', d4)) FROM t", + "input_repr": "{'d3': 1e+20, 'd4': -0.0}", + "result_repr": "'1e+20:-0.0'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat NUL", + "sql": "SELECT concat(a, b), typeof(concat(a, b)) FROM t", + "input_repr": "{'a': 'a\\x00', 'b': 'b'}", + "result_repr": "'a\\x00b'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "|| NULL strictness", + "sql": "SELECT a || b, typeof(a || b) FROM t", + "input_repr": "{'a': 'x', 'b': None}", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "contrast with concat" + }, + { + "name": "|| basic", + "sql": "SELECT a || b, typeof(a || b) FROM t", + "input_repr": "{'a': 'x', 'b': 'y'}", + "result_repr": "'xy'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat literal spotcheck", + "sql": "SELECT concat('x', NULL, 'y'), typeof(concat('x', NULL, 'y'))", + "input_repr": null, + "result_repr": "'xy'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "chr(0) literal", + "sql": "SELECT chr(0), typeof(chr(0))", + "input_repr": null, + "result_repr": "'\\x00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "NUL roundtrip", + "sql": "SELECT s, typeof(s) FROM t", + "input_repr": "{'s': 'a\\x00b'}", + "result_repr": "'a\\x00b'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "can VARCHAR hold NUL at all?" + }, + { + "name": "length of NUL string", + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "{'s': 'a\\x00b'}", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad empty pad exact len", + "sql": "SELECT lpad(s, l, p), typeof(lpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 3, 'p': ''}", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad empty pad exact len", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 3, 'p': ''}", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad empty pad grow by 1", + "sql": "SELECT rpad(s, l, p), typeof(rpad(s, l, p)) FROM t", + "input_repr": "{'s': 'abc', 'l': 4, 'p': ''}", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Insufficient padding in RPAD.", + "note": null + }, + { + "name": "reverse two flags", + "sql": "SELECT reverse(s), typeof(reverse(s)) FROM t", + "input_repr": "{'s': '\\U0001f1fa\\U0001f1f8\\U0001f1eb\\U0001f1f7'}", + "result_repr": "'\\U0001f1eb\\U0001f1f7\\U0001f1fa\\U0001f1f8'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "grapheme: FR then US; naive RI-blob: unchanged" + }, + { + "name": "concat_ws int sep", + "sql": "SELECT concat_ws(sepi, a, b), typeof(concat_ws(sepi, a, b)) FROM t", + "input_repr": "{'sepi': 7, 'a': 'x', 'b': 'y'}", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'concat_ws(INTEGER, VARCHAR, VARCHAR)'. You might need to add explicit type casts.\n\tCandidate functions:\n\tconcat_ws(VARCHAR, ANY, [ANY...]) -> VARCHAR\n\n\nLINE 1: SELECT concat_ws(sepi, a, b), typeof(concat_ws(sepi, a, b)) FROM t\n ^", + "note": null + }, + { + "name": "concat single arg", + "sql": "SELECT concat(a), typeof(concat(a)) FROM t", + "input_repr": "{'a': 'x'}", + "result_repr": "'x'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "concat single NULL", + "sql": "SELECT concat(a), typeof(concat(a)) FROM t", + "input_repr": "{'a': None}", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "lpad literal spotcheck", + "sql": "SELECT lpad('abc', 5, 'xy'), typeof(lpad('abc', 5, 'xy'))", + "input_repr": null, + "result_repr": "'xyabc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "rpad literal spotcheck", + "sql": "SELECT rpad('abc', 5, 'xy'), typeof(rpad('abc', 5, 'xy'))", + "input_repr": null, + "result_repr": "'abcxy'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "replace literal empty needle", + "sql": "SELECT replace('abc', '', 'X'), typeof(replace('abc', '', 'X'))", + "input_repr": null, + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + }, + { + "name": "translate literal spotcheck", + "sql": "SELECT translate('abcb', 'abc', 'x'), typeof(translate('abcb', 'abc', 'x'))", + "input_repr": null, + "result_repr": "'x'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": null + } + ] +} \ No newline at end of file diff --git a/docs/superpowers/specs/pins-wave3/string_inspection_case_aliases.json b/docs/superpowers/specs/pins-wave3/string_inspection_case_aliases.json new file mode 100644 index 0000000..8e2aef0 --- /dev/null +++ b/docs/superpowers/specs/pins-wave3/string_inspection_case_aliases.json @@ -0,0 +1,2239 @@ +{ + "family": "string_inspection_case_aliases", + "duckdb_version": "1.5.5", + "decisions": [ + "unicode(s)/ord(s)/ascii(s) return the FIRST codepoint of s as INTEGER; multi-char input is NOT an error ('abc'->97, astral-first '\\U0001F600abc' handled in random sweep).", + "unicode('') = -1 and ord('') = -1, but ascii('') = 0 \u2014 ascii differs from unicode ONLY on the empty string (confirmed as the sole mismatch shape across 210 random strings and the full single-codepoint sweep).", + "ascii() does NOT error on non-ASCII and does not clamp: ascii('\\xe9')=233, ascii(U+10FFFF)=1114111 \u2014 it is unicode() except for the empty-string case.", + "ord is alias-identical to unicode: 0 mismatches on exhaustive 1,112,063-codepoint sweep plus 210 random strings (incl '', NUL-only, NUL-leading, astral-first, combining-first, ZWJ).", + "NULL in -> NULL out for unicode/ord/ascii/bit_length/strlen/length/ucase/lcase/upper/lower; typeof unchanged.", + "No normalization anywhere: unicode('e'+U+0301)=101 (base char, first codepoint); lone U+0301 -> 769.", + "Embedded NUL (chr(0)) is insertable via parameter binding and treated as ordinary codepoint 0: unicode('\\x00abc')=0, strlen('a\\x00b')=3, ucase('a\\x00b')='A\\x00B'.", + "bit_length(s) = 8 * strlen(s) exactly \u2014 BYTES not codepoints: '\\xe9'->16, U+1F600->32, ''->0; 0 mismatches on full codepoint sweep + 210 random strings. Type BIGINT.", + "octet_length does NOT exist in DuckDB 1.5.5 \u2014 Binder Error; byte length is strlen (BIGINT).", + "strlen counts BYTES; length counts CODEPOINTS not graphemes (ZWJ family emoji: length=7, strlen=25, bit_length=200).", + "ucase/lcase are alias-identical to upper/lower: exhaustive sweep of ALL 1,112,063 valid non-surrogate codepoints 1..0x10FFFF -> zero mismatches for both; plus zero mismatches on 210 random multi-char strings. Type VARCHAR.", + "upper/lower use SIMPLE (one-to-one) case mapping: upper('\\xdf' sharp-s)='\\u1e9e' (NOT 'SS'); lower(U+0130)='i' (single codepoint, NOT 'i'+combining-dot like Python); length(upper(chr(c)))==1 for every single codepoint (0 exceptions, both directions).", + "upper changes exactly 1451 codepoints, lower changes exactly 1433 (of 1,112,063).", + "Casing can change BYTE length (sharp-s 2B->3B, U+212A 3B->1B; 28/210 random strings changed byte length) \u2014 no same-size buffer assumption.", + "Result types: unicode/ord/ascii -> INTEGER; bit_length/strlen/length -> BIGINT; ucase/lcase/upper/lower -> VARCHAR.", + "Literal (constant-fold) path spot-checks all matched the column path: unicode('')/-1, unicode('abc'), unicode(e-acute), ord('abc'), ascii('abc'), bit_length(e-acute), ucase(sharp-s), lcase(U+0130), unicode(NULL).", + "chr() domain context: chr(0) OK (NUL string); chr(55296) and chr(1114112) error 'Invalid Input Error: Invalid UTF8 Codepoint '; DuckDB's optimizer may reorder WHERE conjuncts so chr() can be evaluated on rows a preceding predicate was meant to exclude." + ], + "probes": [ + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'a'", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "''", + "result_repr": "-1", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "233", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "101", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "769", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "304", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "8490", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "128512", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "128104", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "119070", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "1114111", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "0", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "0", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'7'", + "result_repr": "55", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "' '", + "result_repr": "32", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT unicode(s), typeof(unicode(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "223", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'a'", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "''", + "result_repr": "-1", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "233", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "101", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "769", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "304", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "8490", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "128512", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "128104", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "119070", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "1114111", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "0", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "0", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'7'", + "result_repr": "55", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "' '", + "result_repr": "32", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT ord(s), typeof(ord(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "223", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'a'", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "''", + "result_repr": "0", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "233", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "101", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "769", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "304", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "8490", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "128512", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "128104", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "119070", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "1114111", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "0", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "0", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'7'", + "result_repr": "55", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "' '", + "result_repr": "32", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT ascii(s), typeof(ascii(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "223", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'a'", + "result_repr": "8", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "24", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "''", + "result_repr": "0", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "16", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "24", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "16", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "16", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "24", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "32", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "200", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "32", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "32", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "32", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "24", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "8", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'7'", + "result_repr": "8", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "' '", + "result_repr": "8", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT bit_length(s), typeof(bit_length(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "16", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'a'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "ascii a" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'abc'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "multi-char" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "''", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "empty" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "None", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "NULL" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "combining e + U+0301" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "U+212A kelvin" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "emoji U+1F600" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "astral U+1D11E" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "embedded NUL leading" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "embedded NUL middle" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "NUL only" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'7'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "digit" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "' '", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "space" + }, + { + "sql": "SELECT octet_length(s), typeof(octet_length(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Binder Error: No function matches the given name and argument types 'octet_length(VARCHAR)'. You might need to add explicit type casts.", + "note": "ss sharp" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'a'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "''", + "result_repr": "0", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "4", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "25", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "4", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "4", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "4", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'7'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "' '", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT strlen(s), typeof(strlen(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'a'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "''", + "result_repr": "0", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "2", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "7", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "4", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "3", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'7'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "' '", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT length(s), typeof(length(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "1", + "result_type": "BIGINT", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'a'", + "result_repr": "'A'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "'ABC'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "''", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "'\u00c9'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "'E\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "'\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "'\u0130'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "'\u212a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "'\ud83d\ude00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "'\ud834\udd1e'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "'\\U0010ffff'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "'\\x00ABC'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "'A\\x00B'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "'\\x00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'7'", + "result_repr": "'7'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "' '", + "result_repr": "' '", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT ucase(s), typeof(ucase(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "'\u1e9e'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'a'", + "result_repr": "'a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "''", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "'\u00e9'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "'e\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "'\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "'i'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "'k'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "'\ud83d\ude00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "'\ud834\udd1e'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "'\\U0010ffff'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "'\\x00abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "'a\\x00b'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "'\\x00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'7'", + "result_repr": "'7'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "' '", + "result_repr": "' '", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT lcase(s), typeof(lcase(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "'\u00df'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'a'", + "result_repr": "'A'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "'ABC'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "''", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "'\u00c9'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "'E\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "'\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "'\u0130'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "'\u212a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "'\ud83d\ude00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "'\ud834\udd1e'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "'\\U0010ffff'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "'\\x00ABC'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "'A\\x00B'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "'\\x00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'7'", + "result_repr": "'7'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "' '", + "result_repr": "' '", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT upper(s), typeof(upper(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "'\u1e9e'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'a'", + "result_repr": "'a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ascii a" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'abc'", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "multi-char" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "''", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "empty" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "None", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "NULL" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\u00e9'", + "result_repr": "'\u00e9'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "e-acute U+00E9" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'e\u0301'", + "result_repr": "'e\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "combining e + U+0301" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\u0301'", + "result_repr": "'\u0301'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "combining leading U+0301 alone" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\u0130'", + "result_repr": "'i'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+0130 dotted I" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\u212a'", + "result_repr": "'k'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+212A kelvin" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "result_repr": "'\ud83d\ude00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "emoji U+1F600" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ZWJ family emoji" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\ud834\udd1e'", + "result_repr": "'\ud834\udd1e'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "astral U+1D11E" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\\U0010ffff'", + "result_repr": "'\\U0010ffff'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "max cp U+10FFFF" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\\x00abc'", + "result_repr": "'\\x00abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "embedded NUL leading" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'a\\x00b'", + "result_repr": "'a\\x00b'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "embedded NUL middle" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\\x00'", + "result_repr": "'\\x00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "NUL only" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'7'", + "result_repr": "'7'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "digit" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "' '", + "result_repr": "' '", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "space" + }, + { + "sql": "SELECT lower(s), typeof(lower(s)) FROM t", + "input_repr": "'\u00df'", + "result_repr": "'\u00df'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "ss sharp" + }, + { + "sql": "SELECT unicode(''), typeof(unicode(''))", + "input_repr": "'' literal", + "result_repr": "-1", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "LITERAL PATH '' literal" + }, + { + "sql": "SELECT unicode('abc'), typeof(unicode('abc'))", + "input_repr": "'abc' literal", + "result_repr": "97", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "LITERAL PATH 'abc' literal" + }, + { + "sql": "SELECT unicode('\u00e9'), typeof(unicode('\u00e9'))", + "input_repr": "e-acute literal", + "result_repr": "233", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "LITERAL PATH e-acute literal" + }, + { + "sql": "SELECT ord('abc')", + "input_repr": "'abc' literal", + "result_repr": "97", + "result_type": null, + "bits": null, + "error": null, + "note": "LITERAL PATH 'abc' literal" + }, + { + "sql": "SELECT ascii('abc')", + "input_repr": "'abc' literal", + "result_repr": "97", + "result_type": null, + "bits": null, + "error": null, + "note": "LITERAL PATH 'abc' literal" + }, + { + "sql": "SELECT bit_length('\u00e9')", + "input_repr": "e-acute literal", + "result_repr": "16", + "result_type": null, + "bits": null, + "error": null, + "note": "LITERAL PATH e-acute literal" + }, + { + "sql": "SELECT ucase('\u00df')", + "input_repr": "sharp-s literal", + "result_repr": "'\u1e9e'", + "result_type": null, + "bits": null, + "error": null, + "note": "LITERAL PATH sharp-s literal" + }, + { + "sql": "SELECT lcase('\u0130')", + "input_repr": "U+0130 literal", + "result_repr": "'i'", + "result_type": null, + "bits": null, + "error": null, + "note": "LITERAL PATH U+0130 literal" + }, + { + "sql": "SELECT unicode(NULL), typeof(unicode(NULL))", + "input_repr": "NULL literal", + "result_repr": "None", + "result_type": "INTEGER", + "bits": null, + "error": null, + "note": "LITERAL PATH NULL literal" + }, + { + "sql": "SELECT count(*) FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343)", + "input_repr": "codepoints 1..0x10FFFF minus surrogates", + "result_repr": "[(1112063,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "sweep domain size (expect 1114111-2048=1112063)" + }, + { + "sql": "SELECT c FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE ucase(chr(c)) IS DISTINCT FROM upper(chr(c)) LIMIT 50", + "input_repr": "full sweep", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Invalid UTF8 Codepoint 55296", + "note": "ucase vs upper mismatch codepoints (expect none)" + }, + { + "sql": "SELECT c FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE lcase(chr(c)) IS DISTINCT FROM lower(chr(c)) LIMIT 50", + "input_repr": "full sweep", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Invalid UTF8 Codepoint 55296", + "note": "lcase vs lower mismatch codepoints (expect none)" + }, + { + "sql": "SELECT c FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE ord(chr(c)) IS DISTINCT FROM unicode(chr(c)) LIMIT 50", + "input_repr": "full sweep", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Invalid UTF8 Codepoint 55296", + "note": "ord vs unicode mismatch codepoints (expect none)" + }, + { + "sql": "SELECT c FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE ascii(chr(c)) IS DISTINCT FROM unicode(chr(c)) LIMIT 50", + "input_repr": "full sweep", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Invalid UTF8 Codepoint 55296", + "note": "ascii vs unicode mismatch codepoints, single-char (expect none)" + }, + { + "sql": "SELECT c FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE unicode(chr(c)) != c LIMIT 50", + "input_repr": "full sweep", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Invalid UTF8 Codepoint 55296", + "note": "unicode(chr(c)) round-trip mismatches (expect none)" + }, + { + "sql": "SELECT count(*) FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE upper(chr(c)) != chr(c)", + "input_repr": "full sweep", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Invalid UTF8 Codepoint 55296", + "note": "how many codepoints upper() actually changes" + }, + { + "sql": "SELECT count(*) FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE lower(chr(c)) != chr(c)", + "input_repr": "full sweep", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Invalid UTF8 Codepoint 55296", + "note": "how many codepoints lower() actually changes" + }, + { + "sql": "SELECT count(*) FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE length(upper(chr(c))) != 1", + "input_repr": "full sweep", + "result_repr": "[(0,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "codepoints where upper() output is not exactly 1 codepoint (full-fold witnesses)" + }, + { + "sql": "SELECT count(*) FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE length(lower(chr(c))) != 1", + "input_repr": "full sweep", + "result_repr": "[(0,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "codepoints where lower() output is not exactly 1 codepoint" + }, + { + "sql": "SELECT count(*) FROM (SELECT range::INTEGER AS c FROM range(1, 1114112) WHERE range < 55296 OR range > 57343) WHERE bit_length(chr(c)) != 8*strlen(chr(c))", + "input_repr": "full sweep", + "result_repr": "[(0,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "bit_length vs 8*strlen mismatches (expect 0)" + }, + { + "sql": "SELECT chr(c) FROM (SELECT 0 AS c) t2", + "input_repr": "chr(0) via column", + "result_repr": "[('\\x00',)]", + "result_type": null, + "bits": null, + "error": null, + "note": "chr(0) column path" + }, + { + "sql": "SELECT chr(c) FROM (SELECT 55296 AS c) t2", + "input_repr": "chr(0xD800) via column", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Invalid UTF8 Codepoint 55296", + "note": "chr on surrogate" + }, + { + "sql": "SELECT chr(c) FROM (SELECT 1114112 AS c) t2", + "input_repr": "chr(0x110000) via column", + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Invalid Input Error: Invalid UTF8 Codepoint 1114112", + "note": "chr beyond max" + }, + { + "sql": "SELECT count(*) FROM r", + "input_repr": "210 random strings", + "result_repr": "[(210,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "sweep table size" + }, + { + "sql": "SELECT s FROM r WHERE ord(s) IS DISTINCT FROM unicode(s) LIMIT 20", + "input_repr": "210 random strings incl edge shapes", + "result_repr": "[]", + "result_type": null, + "bits": null, + "error": null, + "note": "ord vs unicode mismatches on arbitrary strings (expect none)" + }, + { + "sql": "SELECT s, ascii(s), unicode(s) FROM r WHERE ascii(s) IS DISTINCT FROM unicode(s) LIMIT 20", + "input_repr": "210 random strings", + "result_repr": "[('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1), ('', 0, -1)]", + "result_type": null, + "bits": null, + "error": null, + "note": "ascii vs unicode mismatches on arbitrary strings (expect only empty string: 0 vs -1)" + }, + { + "sql": "SELECT s FROM r WHERE bit_length(s) IS DISTINCT FROM 8*strlen(s) LIMIT 20", + "input_repr": "210 random strings", + "result_repr": "[]", + "result_type": null, + "bits": null, + "error": null, + "note": "bit_length == 8*strlen on arbitrary strings (expect none)" + }, + { + "sql": "SELECT s FROM r WHERE ucase(s) IS DISTINCT FROM upper(s) OR lcase(s) IS DISTINCT FROM lower(s) LIMIT 20", + "input_repr": "210 random strings", + "result_repr": "[]", + "result_type": null, + "bits": null, + "error": null, + "note": "ucase/lcase alias check on multi-char strings (expect none)" + }, + { + "sql": "SELECT count(*) FROM r WHERE strlen(upper(s)) != strlen(s) OR strlen(lower(s)) != strlen(s)", + "input_repr": "210 random strings", + "result_repr": "[(28,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "strings where casing changes byte length" + }, + { + "sql": "SELECT count(*) FROM cp", + "input_repr": "materialized codepoint table", + "result_repr": "[(1112063,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "domain: 1..0x10FFFF minus surrogates = 1112063" + }, + { + "sql": "SELECT c FROM cp WHERE ucase(chr(c)) IS DISTINCT FROM upper(chr(c)) LIMIT 50", + "input_repr": "exhaustive sweep 1112063 cps", + "result_repr": "[]", + "result_type": null, + "bits": null, + "error": null, + "note": "ucase vs upper mismatches (expect [])" + }, + { + "sql": "SELECT c FROM cp WHERE lcase(chr(c)) IS DISTINCT FROM lower(chr(c)) LIMIT 50", + "input_repr": "exhaustive sweep 1112063 cps", + "result_repr": "[]", + "result_type": null, + "bits": null, + "error": null, + "note": "lcase vs lower mismatches (expect [])" + }, + { + "sql": "SELECT c FROM cp WHERE ord(chr(c)) IS DISTINCT FROM unicode(chr(c)) LIMIT 50", + "input_repr": "exhaustive sweep 1112063 cps", + "result_repr": "[]", + "result_type": null, + "bits": null, + "error": null, + "note": "ord vs unicode mismatches (expect [])" + }, + { + "sql": "SELECT c FROM cp WHERE ascii(chr(c)) IS DISTINCT FROM unicode(chr(c)) LIMIT 50", + "input_repr": "exhaustive sweep 1112063 cps", + "result_repr": "[]", + "result_type": null, + "bits": null, + "error": null, + "note": "ascii vs unicode mismatches single-char (expect [])" + }, + { + "sql": "SELECT c FROM cp WHERE unicode(chr(c)) != c LIMIT 50", + "input_repr": "exhaustive sweep 1112063 cps", + "result_repr": "[]", + "result_type": null, + "bits": null, + "error": null, + "note": "unicode(chr(c)) round-trip (expect [])" + }, + { + "sql": "SELECT count(*) FROM cp WHERE upper(chr(c)) != chr(c)", + "input_repr": "exhaustive sweep", + "result_repr": "[(1451,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "codepoints changed by upper()" + }, + { + "sql": "SELECT count(*) FROM cp WHERE lower(chr(c)) != chr(c)", + "input_repr": "exhaustive sweep", + "result_repr": "[(1433,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "codepoints changed by lower()" + } + ] +} \ No newline at end of file diff --git a/docs/superpowers/specs/pins-wave3/strip_accents.json b/docs/superpowers/specs/pins-wave3/strip_accents.json new file mode 100644 index 0000000..0df7d2f --- /dev/null +++ b/docs/superpowers/specs/pins-wave3/strip_accents.json @@ -0,0 +1,5222 @@ +{ + "family": "strip_accents", + "duckdb_version": "1.5.5", + "decisions": [ + "duckdb 1.5.5: strip_accents(VARCHAR) -> VARCHAR (typeof-confirmed); NULL -> NULL; '' -> ''; exactly 1 arg (else Binder Error); no implicit cast from INTEGER (Binder Error); no runtime error for any codepoint.", + "NOT purely per-codepoint: after per-cp mapping, canonical composition runs across codepoints. Only observed cross-cp effect is Hangul: L+V->LV, LV+T->LVT, and precomposed-LV-syllable + T -> LVT (U+AC00,U+11A8 -> U+AC01). Wrong-order/incomplete jamo (V+L, L+T, T alone, LVT+T) do NOT compose. Formula S=0xAC00+(L-0x1100)*588+(V-0x1161)*28+(T-0x11A7) verified for all 399 LV pairs and 200 LVT triples.", + "Full algorithm pin: if input is all-ASCII bytes -> return verbatim (even with embedded NULs). Else: take bytes up to first NUL (TRUNCATION QUIRK, see below), apply per-cp map, then Hangul-compose adjacent results. Validated on 518 random/targeted strings: 'per-cp map + NFC compose' model matched all except the NUL case; plain concat differed only on Hangul-composable inputs.", + "Per-cp map (oracle-extracted, exhaustive 0..0x10FFFF minus surrogates): 4460 changed codepoints, min U+00C0, max U+E01EF; 2450 map to empty string (marks: Mn 1984, Mc 453, Me 13); ALL non-empty outputs are exactly ONE codepoint; zero multi-codepoint outputs; zero NULL/error outputs. Full map embedded in pins JSON under 'map'.", + "Mapping = canonical decompose -> drop marks -> recompose, per utf8proc's OLDER-than-Unicode-16 tables: 57 cps diverge from a Python 3.14 unicodedata model (e.g. U+0897, U+10D69..U+10D6D, U+113B8.. left unchanged by duck). Generate the Rust table from the extracted map, never from host Unicode tables.", + "51 mark-category (Unicode 16) cps are NOT stripped (unknown to duck's tables) and are inert: 'a'+U+0897 -> unchanged; 'a'+U+0897+U+0301 -> 'a'+U+0897 (known mark stripped around it).", + "strip_accents changes characters with no accents: singleton canonical decompositions apply - U+212A->'K', U+212B->'A', U+2126->U+03A9, U+2000(Zs)->U+2002, U+2329(Ps)->U+3008; composition exclusions stay decomposed-then-recomposed per NFC (U+0958 -> U+0915, nukta stripped).", + "Compatibility decompositions NOT applied: U+1D400, U+FB01 (fi ligature), U+01C4 (DZ caron) unchanged.", + "Combining sequences: 'e'+U+0301 -> 'e'; mark-BEFORE-base U+0301+'e' -> 'e'; marks-only string -> ''; strip happens before Hangul compose (U+1112,U+1164,U+0301 -> U+D5AC).", + "Embedded NUL QUIRK: pure-ASCII strings pass through verbatim incl. NULs ('a\\x00b' -> 'a\\x00b'); the moment ANY non-ASCII byte is present anywhere (even one unchanged by the map, e.g. emoji), output truncates at the first NUL: 'a\\x00e-acute' -> 'a', 'e-acute\\x00b' -> 'e', '\\x00e-acute' -> '', 'a\\x00emoji' -> 'a'. Literal/const-fold path behaves identically.", + "Idempotent: strip_accents(strip_accents(x)) == strip_accents(x) for every codepoint (0 violations over full sweep).", + "Literal (constant-fold) path spot-checks match the column path ('e-acute', both NUL cases); no divergence observed.", + "Emoji, ZWJ sequences, astral letters, U+200D unchanged (absent from map); U+0130 -> 'I', U+00E9 -> 'e'." + ], + "probes": [ + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "None", + "note": "NULL input", + "result_repr": "None", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "''", + "note": "empty string", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'abc'", + "note": "plain ascii", + "result_repr": "'abc'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u00e9'", + "note": "e-acute U+00E9", + "result_repr": "'e'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u0130'", + "note": "LATIN CAPITAL I WITH DOT ABOVE U+0130", + "result_repr": "'I'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u212a'", + "note": "KELVIN SIGN U+212A", + "result_repr": "'K'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u212b'", + "note": "ANGSTROM SIGN U+212B (NFD singleton to A+ring)", + "result_repr": "'A'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u2126'", + "note": "OHM SIGN U+2126 (NFD singleton to omega, no mark)", + "result_repr": "'\u03a9'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'e\u0301'", + "note": "e + combining acute (decomposed)", + "result_repr": "'e'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u0301'", + "note": "combining acute alone", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u0301e'", + "note": "combining acute BEFORE base", + "result_repr": "'e'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'e\u0301\u0301'", + "note": "e + two combining acutes", + "result_repr": "'e'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\ud83d\ude00'", + "note": "emoji U+1F600", + "result_repr": "'\ud83d\ude00'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "note": "ZWJ family emoji", + "result_repr": "'\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\ud801\udc00'", + "note": "astral DESERET CAPITAL LONG I U+10400", + "result_repr": "'\ud801\udc00'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\ud835\udc00'", + "note": "MATH BOLD CAPITAL A U+1D400 (compat decomp only)", + "result_repr": "'\ud835\udc00'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'a\\x00b'", + "note": "embedded NUL via parameter", + "result_repr": "'a\\x00b'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\\x00'", + "note": "lone NUL via parameter", + "result_repr": "'\\x00'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'caf\u00e9 na\u00efve \u0130stanbul'", + "note": "mixed sentence", + "result_repr": "'cafe naive Istanbul'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u1112\u1161\u11ab'", + "note": "Hangul Jamo HIEUH+A+NIEUN (NFC would compose to U+D55C)", + "result_repr": "'\ud55c'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\ud55c'", + "note": "precomposed Hangul syllable U+D55C", + "result_repr": "'\ud55c'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\uac00'", + "note": "Hangul syllable U+AC00 GA", + "result_repr": "'\uac00'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u1100\u1161'", + "note": "Hangul Jamo L+V (NFC would compose to U+AC00)", + "result_repr": "'\uac00'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'A\u030a'", + "note": "A + combining ring (NFC would give U+00C5)", + "result_repr": "'A'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u00c5'", + "note": "precomposed A-ring U+00C5", + "result_repr": "'A'", + "result_type": "VARCHAR", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(NULL)", + "note": "literal NULL", + "result_repr": "(None,)", + "bits": null, + "error": null, + "result_type": null + }, + { + "sql": "SELECT strip_accents('\u00e9'), typeof(strip_accents('\u00e9'))", + "note": "literal path e-acute (constant fold)", + "result_repr": "('e', 'VARCHAR')", + "bits": null, + "error": null, + "result_type": null + }, + { + "sql": "SELECT typeof(strip_accents(s)) FROM (VALUES (NULL::VARCHAR)) t(s)", + "note": "typeof on NULL col", + "result_repr": "('VARCHAR',)", + "bits": null, + "error": null, + "result_type": null + }, + { + "sql": "SELECT strip_accents()", + "note": "no args", + "error": "Binder Error: No function matches the given name and argument types 'strip_accents()'. You might need to add explicit type casts.", + "bits": null, + "result_type": null + }, + { + "sql": "SELECT strip_accents('a','b')", + "note": "two args", + "error": "Binder Error: No function matches the given name and argument types 'strip_accents(STRING_LITERAL, STRING_LITERAL)'. You might need to add explicit type casts.", + "bits": null, + "result_type": null + }, + { + "sql": "SELECT strip_accents(123)", + "note": "int literal arg (implicit cast?)", + "error": "Binder Error: No function matches the given name and argument types 'strip_accents(INTEGER_LITERAL)'. You might need to add explicit type casts.", + "bits": null, + "result_type": null + }, + { + "sql": "SELECT strip_accents(i) FROM ti -- INTEGER col", + "error": "Binder Error: No function matches the given name and argument types 'strip_accents(INTEGER)'. You might need to add explicit type casts.", + "note": "int column arg", + "bits": null, + "result_type": null + }, + { + "sql": "SELECT chr(0)", + "note": "chr(0)", + "result_repr": "('\\x00',)", + "bits": null, + "error": null, + "result_type": null + }, + { + "sql": "SELECT chr(55296)", + "note": "chr(surrogate 0xD800)", + "error": "Invalid Input Error: Invalid UTF8 Codepoint 55296", + "bits": null, + "result_type": null + }, + { + "sql": "SELECT chr(1114112)", + "note": "chr(0x110000) out of range", + "error": "Invalid Input Error: Invalid UTF8 Codepoint 1114112", + "bits": null, + "result_type": null + }, + { + "sql": "SELECT strip_accents(chr(0))", + "note": "strip_accents(chr(0))", + "result_repr": "('\\x00',)", + "bits": null, + "error": null, + "result_type": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'a\\x00b'", + "result_repr": "'a\\x00b'", + "result_type": "VARCHAR", + "note": "ascii + NUL, no accent", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'a\\x00\u00e9'", + "result_repr": "'a'", + "result_type": "VARCHAR", + "note": "NUL then accented char", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u00e9\\x00b'", + "result_repr": "'e'", + "result_type": "VARCHAR", + "note": "accented char then NUL", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\\x00\u00e9'", + "result_repr": "''", + "result_type": "VARCHAR", + "note": "NUL first, then accented", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u00e9\\x00'", + "result_repr": "'e'", + "result_type": "VARCHAR", + "note": "accented then trailing NUL", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'a\u00e9\\x00b\u00e9'", + "result_repr": "'ae'", + "result_type": "VARCHAR", + "note": "accent before and after NUL", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'a\\x00\ud83d\ude00'", + "result_repr": "'a'", + "result_type": "VARCHAR", + "note": "NUL then unchanged non-ascii (emoji)", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\ud83d\ude00\\x00a'", + "result_repr": "'\ud83d\ude00'", + "result_type": "VARCHAR", + "note": "unchanged non-ascii then NUL", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'a\\x00\ud835\udc00'", + "result_repr": "'a'", + "result_type": "VARCHAR", + "note": "NUL then unchanged non-ascii (math bold A)", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'a\\x00\u00fc'", + "result_repr": "'a'", + "result_type": "VARCHAR", + "note": "NUL then u-umlaut (changed cp)", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u1100\\x00\u1161'", + "result_repr": "'\u1100'", + "result_type": "VARCHAR", + "note": "jamo NUL jamo", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u1100\u1161\\x00b'", + "result_repr": "'\uac00'", + "result_type": "VARCHAR", + "note": "composable jamo then NUL", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\\x00'", + "result_repr": "'\\x00'", + "result_type": "VARCHAR", + "note": "lone NUL", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\\x00\\x00'", + "result_repr": "'\\x00\\x00'", + "result_type": "VARCHAR", + "note": "two NULs", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'a\\x00b\\x00c'", + "result_repr": "'a\\x00b\\x00c'", + "result_type": "VARCHAR", + "note": "two NULs ascii only", + "bits": null, + "error": null + }, + { + "sql": "SELECT strip_accents('a' || chr(0) || chr(233))", + "input_repr": "'a\\x00\u00e9'", + "result_repr": "'a'", + "note": "literal/concat path of NUL truncation case", + "bits": null, + "error": null, + "result_type": null + }, + { + "sql": "SELECT strip_accents('a' || chr(0) || 'b')", + "input_repr": "'a\\x00b'", + "result_repr": "'a\\x00b'", + "note": "literal path, NUL, no accent", + "bits": null, + "error": null, + "result_type": null + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u1100\u1161'", + "result_repr": "'\uac00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x1100', '0x1161'] -> ['0xac00']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u1100\u1161\u11a8'", + "result_repr": "'\uac01'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x1100', '0x1161', '0x11a8'] -> ['0xac01']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\uac00\u11a8'", + "result_repr": "'\uac01'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0xac00', '0x11a8'] -> ['0xac01']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\ud7a3\u11a8'", + "result_repr": "'\ud7a3\u11a8'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0xd7a3', '0x11a8'] -> ['0xd7a3', '0x11a8']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u1161\u1100'", + "result_repr": "'\u1161\u1100'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x1161', '0x1100'] -> ['0x1161', '0x1100']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u1100\u1100\u1161'", + "result_repr": "'\u1100\uac00'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x1100', '0x1100', '0x1161'] -> ['0x1100', '0xac00']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u1100\u1161\u1161'", + "result_repr": "'\uac00\u1161'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x1100', '0x1161', '0x1161'] -> ['0xac00', '0x1161']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u11a8'", + "result_repr": "'\u11a8'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x11a8'] -> ['0x11a8']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u1100\u11a8'", + "result_repr": "'\u1100\u11a8'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x1100', '0x11a8'] -> ['0x1100', '0x11a8']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'e\u0301'", + "result_repr": "'e'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x65', '0x301'] -> ['0x65']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u00e9\u0301'", + "result_repr": "'e'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0xe9', '0x301'] -> ['0x65']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'a\u0316\u0301b'", + "result_repr": "'ab'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x61', '0x316', '0x301', '0x62'] -> ['0x61', '0x62']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u0301\u0301\u0301'", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x301', '0x301', '0x301'] -> []" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'a\\x00\u00e9'", + "result_repr": "'a'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x61', '0x0', '0xe9'] -> ['0x61']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u212b\u212b'", + "result_repr": "'AA'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x212b', '0x212b'] -> ['0x41', '0x41']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\ud83d\udc68\\u200d\ud83d\udc69'", + "result_repr": "'\ud83d\udc68\\u200d\ud83d\udc69'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x1f468', '0x200d', '0x1f469'] -> ['0x1f468', '0x200d', '0x1f469']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'\u1112\u1161\u11ab\u1112\u1161\u11ab'", + "result_repr": "'\ud55c\ud55c'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x1112', '0x1161', '0x11ab', '0x1112', '0x1161', '0x11ab'] -> ['0xd55c', '0xd55c']" + }, + { + "sql": "SELECT strip_accents(s) FROM t -- column path", + "input_repr": "'A\u030a\u11a8'", + "result_repr": "'A\u11a8'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "targeted context probe; cps in->out: ['0x41', '0x30a', '0x11a8'] -> ['0x41', '0x11a8']" + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'a\u0897'", + "result_repr": "'a\u0897'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "base + U+0897 ARABIC PEPET (mark unknown to duck's tables) - inert?" + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'a\u0897\u0301'", + "result_repr": "'a\u0897'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "base + unknown mark + known mark" + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\\u2000'", + "result_repr": "'\\u2002'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+2000 EN QUAD (Zs, singleton decomposition)" + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u2329'", + "result_repr": "'\u3008'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+2329 LEFT-POINTING ANGLE BRACKET (Ps)" + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\ufb01'", + "result_repr": "'\ufb01'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+FB01 LATIN SMALL LIGATURE FI (compat-only decomp)" + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u01c4'", + "result_repr": "'\u01c4'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+01C4 DZ WITH CARON (compat digraph)" + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u0958'", + "result_repr": "'\u0915'", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+0958 DEVANAGARI QA (composition exclusion, NFD to KA+nukta)" + }, + { + "sql": "SELECT strip_accents(s), typeof(strip_accents(s)) FROM t", + "input_repr": "'\u093e'", + "result_repr": "''", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "U+093E DEVANAGARI VOWEL SIGN AA (Mc spacing mark)" + }, + { + "sql": "SELECT c, strip_accents(chr(c)) FROM cps WHERE strip_accents(chr(c)) != chr(c) -- cps = 0..0x10FFFF minus surrogates", + "input_repr": "all 1112064 codepoints", + "result_repr": "4460 changed; min=U+00C0 max=U+E01EF; 2450 empty outputs; 0 multi-codepoint outputs; 0 NULL outputs", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "exhaustive vectorized sweep" + }, + { + "sql": "SELECT count(*) FROM cps WHERE strip_accents(strip_accents(chr(c))) != strip_accents(chr(c))", + "input_repr": "all swept codepoints", + "result_repr": "0", + "result_type": null, + "bits": null, + "error": null, + "note": "idempotence holds for every codepoint" + }, + { + "sql": "SELECT strip_accents(chr(l) || chr(v)) ... all 19x21 LV jamo pairs", + "input_repr": "399 LV pairs + 200 random LVT triples", + "result_repr": "all equal chr(0xAC00+(L-0x1100)*588+(V-0x1161)*28[+(T-0x11A7)])", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "standard Hangul composition formula holds exhaustively for LV, sampled for LVT" + }, + { + "sql": "518 random/targeted multi-char strings via column path", + "input_repr": "pool: changed cps, ascii, jamo L/V/T, marks, astral, ZWJ, NUL", + "result_repr": "model 'per-cp map then NFC-compose' matched 517/518; only miss = NUL truncation case; plain concat missed only 12 Hangul cases + NUL case", + "result_type": "VARCHAR", + "bits": null, + "error": null, + "note": "compositionality validation" + }, + { + "sql": "python-model comparison: NFC(dropMarks(NFD(ch))) with Python 3.14 unicodedata", + "input_repr": "all swept codepoints", + "result_repr": "57 mismatches, e.g. U+0897 ARABIC PEPET duck-unchanged vs py-stripped; TODHRI/GARAY/TULU-TIGALARI untouched by duck", + "result_type": null, + "bits": null, + "error": null, + "note": "duck's utf8proc Unicode tables are OLDER than Unicode 16 - use extracted map, not host tables. mismatch cps: [2199, 67017, 67044, 68969, 68970, 68971, 68972, 68973, 69372, 70531, 70533, 70542, 70545, 70584, 70585, 70586, 70587, 70588, 70589, 70590, 70591, 70592, 70594, 70597, 70599, 70600, 70601, 70602, 70604, 70605, 70606, 70607, 70608, 70610, 70625, 70626, 73562, 90398, 90399, 90400, 90401, 90402, 90403, 90404, 90405, 90406, 90407, 90408, 90409, 90410, 90411, 90412, 90413, 90414, 90415, 124398, 124399]" + } + ], + "map": { + "192": "A", + "193": "A", + "194": "A", + "195": "A", + "196": "A", + "197": "A", + "199": "C", + "200": "E", + "201": "E", + "202": "E", + "203": "E", + "204": "I", + "205": "I", + "206": "I", + "207": "I", + "209": "N", + "210": "O", + "211": "O", + "212": "O", + "213": "O", + "214": "O", + "217": "U", + "218": "U", + "219": "U", + "220": "U", + "221": "Y", + "224": "a", + "225": "a", + "226": "a", + "227": "a", + "228": "a", + "229": "a", + "231": "c", + "232": "e", + "233": "e", + "234": "e", + "235": "e", + "236": "i", + "237": "i", + "238": "i", + "239": "i", + "241": "n", + "242": "o", + "243": "o", + "244": "o", + "245": "o", + "246": "o", + "249": "u", + "250": "u", + "251": "u", + "252": "u", + "253": "y", + "255": "y", + "256": "A", + "257": "a", + "258": "A", + "259": "a", + "260": "A", + "261": "a", + "262": "C", + "263": "c", + "264": "C", + "265": "c", + "266": "C", + "267": "c", + "268": "C", + "269": "c", + "270": "D", + "271": "d", + "274": "E", + "275": "e", + "276": "E", + "277": "e", + "278": "E", + "279": "e", + "280": "E", + "281": "e", + "282": "E", + "283": "e", + "284": "G", + "285": "g", + "286": "G", + "287": "g", + "288": "G", + "289": "g", + "290": "G", + "291": "g", + "292": "H", + "293": "h", + "296": "I", + "297": "i", + "298": "I", + "299": "i", + "300": "I", + "301": "i", + "302": "I", + "303": "i", + "304": "I", + "308": "J", + "309": "j", + "310": "K", + "311": "k", + "313": "L", + "314": "l", + "315": "L", + "316": "l", + "317": "L", + "318": "l", + "323": "N", + "324": "n", + "325": "N", + "326": "n", + "327": "N", + "328": "n", + "332": "O", + "333": "o", + "334": "O", + "335": "o", + "336": "O", + "337": "o", + "340": "R", + "341": "r", + "342": "R", + "343": "r", + "344": "R", + "345": "r", + "346": "S", + "347": "s", + "348": "S", + "349": "s", + "350": "S", + "351": "s", + "352": "S", + "353": "s", + "354": "T", + "355": "t", + "356": "T", + "357": "t", + "360": "U", + "361": "u", + "362": "U", + "363": "u", + "364": "U", + "365": "u", + "366": "U", + "367": "u", + "368": "U", + "369": "u", + "370": "U", + "371": "u", + "372": "W", + "373": "w", + "374": "Y", + "375": "y", + "376": "Y", + "377": "Z", + "378": "z", + "379": "Z", + "380": "z", + "381": "Z", + "382": "z", + "416": "O", + "417": "o", + "431": "U", + "432": "u", + "461": "A", + "462": "a", + "463": "I", + "464": "i", + "465": "O", + "466": "o", + "467": "U", + "468": "u", + "469": "U", + "470": "u", + "471": "U", + "472": "u", + "473": "U", + "474": "u", + "475": "U", + "476": "u", + "478": "A", + "479": "a", + "480": "A", + "481": "a", + "482": "\u00c6", + "483": "\u00e6", + "486": "G", + "487": "g", + "488": "K", + "489": "k", + "490": "O", + "491": "o", + "492": "O", + "493": "o", + "494": "\u01b7", + "495": "\u0292", + "496": "j", + "500": "G", + "501": "g", + "504": "N", + "505": "n", + "506": "A", + "507": "a", + "508": "\u00c6", + "509": "\u00e6", + "510": "\u00d8", + "511": "\u00f8", + "512": "A", + "513": "a", + "514": "A", + "515": "a", + "516": "E", + "517": "e", + "518": "E", + "519": "e", + "520": "I", + "521": "i", + "522": "I", + "523": "i", + "524": "O", + "525": "o", + "526": "O", + "527": "o", + "528": "R", + "529": "r", + "530": "R", + "531": "r", + "532": "U", + "533": "u", + "534": "U", + "535": "u", + "536": "S", + "537": "s", + "538": "T", + "539": "t", + "542": "H", + "543": "h", + "550": "A", + "551": "a", + "552": "E", + "553": "e", + "554": "O", + "555": "o", + "556": "O", + "557": "o", + "558": "O", + "559": "o", + "560": "O", + "561": "o", + "562": "Y", + "563": "y", + "768": "", + "769": "", + "770": "", + "771": "", + "772": "", + "773": "", + "774": "", + "775": "", + "776": "", + "777": "", + "778": "", + "779": "", + "780": "", + "781": "", + "782": "", + "783": "", + "784": "", + "785": "", + "786": "", + "787": "", + "788": "", + "789": "", + "790": "", + "791": "", + "792": "", + "793": "", + "794": "", + "795": "", + "796": "", + "797": "", + "798": "", + "799": "", + "800": "", + "801": "", + "802": "", + "803": "", + "804": "", + "805": "", + "806": "", + "807": "", + "808": "", + "809": "", + "810": "", + "811": "", + "812": "", + "813": "", + "814": "", + "815": "", + "816": "", + "817": "", + "818": "", + "819": "", + "820": "", + "821": "", + "822": "", + "823": "", + "824": "", + "825": "", + "826": "", + "827": "", + "828": "", + "829": "", + "830": "", + "831": "", + "832": "", + "833": "", + "834": "", + "835": "", + "836": "", + "837": "", + "838": "", + "839": "", + "840": "", + "841": "", + "842": "", + "843": "", + "844": "", + "845": "", + "846": "", + "847": "", + "848": "", + "849": "", + "850": "", + "851": "", + "852": "", + "853": "", + "854": "", + "855": "", + "856": "", + "857": "", + "858": "", + "859": "", + "860": "", + "861": "", + "862": "", + "863": "", + "864": "", + "865": "", + "866": "", + "867": "", + "868": "", + "869": "", + "870": "", + "871": "", + "872": "", + "873": "", + "874": "", + "875": "", + "876": "", + "877": "", + "878": "", + "879": "", + "884": "\u02b9", + "894": ";", + "901": "\u00a8", + "902": "\u0391", + "903": "\u00b7", + "904": "\u0395", + "905": "\u0397", + "906": "\u0399", + "908": "\u039f", + "910": "\u03a5", + "911": "\u03a9", + "912": "\u03b9", + "938": "\u0399", + "939": "\u03a5", + "940": "\u03b1", + "941": "\u03b5", + "942": "\u03b7", + "943": "\u03b9", + "944": "\u03c5", + "970": "\u03b9", + "971": "\u03c5", + "972": "\u03bf", + "973": "\u03c5", + "974": "\u03c9", + "979": "\u03d2", + "980": "\u03d2", + "1024": "\u0415", + "1025": "\u0415", + "1027": "\u0413", + "1031": "\u0406", + "1036": "\u041a", + "1037": "\u0418", + "1038": "\u0423", + "1049": "\u0418", + "1081": "\u0438", + "1104": "\u0435", + "1105": "\u0435", + "1107": "\u0433", + "1111": "\u0456", + "1116": "\u043a", + "1117": "\u0438", + "1118": "\u0443", + "1142": "\u0474", + "1143": "\u0475", + "1155": "", + "1156": "", + "1157": "", + "1158": "", + "1159": "", + "1160": "", + "1161": "", + "1217": "\u0416", + "1218": "\u0436", + "1232": "\u0410", + "1233": "\u0430", + "1234": "\u0410", + "1235": "\u0430", + "1238": "\u0415", + "1239": "\u0435", + "1242": "\u04d8", + "1243": "\u04d9", + "1244": "\u0416", + "1245": "\u0436", + "1246": "\u0417", + "1247": "\u0437", + "1250": "\u0418", + "1251": "\u0438", + "1252": "\u0418", + "1253": "\u0438", + "1254": "\u041e", + "1255": "\u043e", + "1258": "\u04e8", + "1259": "\u04e9", + "1260": "\u042d", + "1261": "\u044d", + "1262": "\u0423", + "1263": "\u0443", + "1264": "\u0423", + "1265": "\u0443", + "1266": "\u0423", + "1267": "\u0443", + "1268": "\u0427", + "1269": "\u0447", + "1272": "\u042b", + "1273": "\u044b", + "1425": "", + "1426": "", + "1427": "", + "1428": "", + "1429": "", + "1430": "", + "1431": "", + "1432": "", + "1433": "", + "1434": "", + "1435": "", + "1436": "", + "1437": "", + "1438": "", + "1439": "", + "1440": "", + "1441": "", + "1442": "", + "1443": "", + "1444": "", + "1445": "", + "1446": "", + "1447": "", + "1448": "", + "1449": "", + "1450": "", + "1451": "", + "1452": "", + "1453": "", + "1454": "", + "1455": "", + "1456": "", + "1457": "", + "1458": "", + "1459": "", + "1460": "", + "1461": "", + "1462": "", + "1463": "", + "1464": "", + "1465": "", + "1466": "", + "1467": "", + "1468": "", + "1469": "", + "1471": "", + "1473": "", + "1474": "", + "1476": "", + "1477": "", + "1479": "", + "1552": "", + "1553": "", + "1554": "", + "1555": "", + "1556": "", + "1557": "", + "1558": "", + "1559": "", + "1560": "", + "1561": "", + "1562": "", + "1570": "\u0627", + "1571": "\u0627", + "1572": "\u0648", + "1573": "\u0627", + "1574": "\u064a", + "1611": "", + "1612": "", + "1613": "", + "1614": "", + "1615": "", + "1616": "", + "1617": "", + "1618": "", + "1619": "", + "1620": "", + "1621": "", + "1622": "", + "1623": "", + "1624": "", + "1625": "", + "1626": "", + "1627": "", + "1628": "", + "1629": "", + "1630": "", + "1631": "", + "1648": "", + "1728": "\u06d5", + "1730": "\u06c1", + "1747": "\u06d2", + "1750": "", + "1751": "", + "1752": "", + "1753": "", + "1754": "", + "1755": "", + "1756": "", + "1759": "", + "1760": "", + "1761": "", + "1762": "", + "1763": "", + "1764": "", + "1767": "", + "1768": "", + "1770": "", + "1771": "", + "1772": "", + "1773": "", + "1809": "", + "1840": "", + "1841": "", + "1842": "", + "1843": "", + "1844": "", + "1845": "", + "1846": "", + "1847": "", + "1848": "", + "1849": "", + "1850": "", + "1851": "", + "1852": "", + "1853": "", + "1854": "", + "1855": "", + "1856": "", + "1857": "", + "1858": "", + "1859": "", + "1860": "", + "1861": "", + "1862": "", + "1863": "", + "1864": "", + "1865": "", + "1866": "", + "1958": "", + "1959": "", + "1960": "", + "1961": "", + "1962": "", + "1963": "", + "1964": "", + "1965": "", + "1966": "", + "1967": "", + "1968": "", + "2027": "", + "2028": "", + "2029": "", + "2030": "", + "2031": "", + "2032": "", + "2033": "", + "2034": "", + "2035": "", + "2045": "", + "2070": "", + "2071": "", + "2072": "", + "2073": "", + "2075": "", + "2076": "", + "2077": "", + "2078": "", + "2079": "", + "2080": "", + "2081": "", + "2082": "", + "2083": "", + "2085": "", + "2086": "", + "2087": "", + "2089": "", + "2090": "", + "2091": "", + "2092": "", + "2093": "", + "2137": "", + "2138": "", + "2139": "", + "2200": "", + "2201": "", + "2202": "", + "2203": "", + "2204": "", + "2205": "", + "2206": "", + "2207": "", + "2250": "", + "2251": "", + "2252": "", + "2253": "", + "2254": "", + "2255": "", + "2256": "", + "2257": "", + "2258": "", + "2259": "", + "2260": "", + "2261": "", + "2262": "", + "2263": "", + "2264": "", + "2265": "", + "2266": "", + "2267": "", + "2268": "", + "2269": "", + "2270": "", + "2271": "", + "2272": "", + "2273": "", + "2275": "", + "2276": "", + "2277": "", + "2278": "", + "2279": "", + "2280": "", + "2281": "", + "2282": "", + "2283": "", + "2284": "", + "2285": "", + "2286": "", + "2287": "", + "2288": "", + "2289": "", + "2290": "", + "2291": "", + "2292": "", + "2293": "", + "2294": "", + "2295": "", + "2296": "", + "2297": "", + "2298": "", + "2299": "", + "2300": "", + "2301": "", + "2302": "", + "2303": "", + "2304": "", + "2305": "", + "2306": "", + "2307": "", + "2345": "\u0928", + "2353": "\u0930", + "2356": "\u0933", + "2362": "", + "2363": "", + "2364": "", + "2366": "", + "2367": "", + "2368": "", + "2369": "", + "2370": "", + "2371": "", + "2372": "", + "2373": "", + "2374": "", + "2375": "", + "2376": "", + "2377": "", + "2378": "", + "2379": "", + "2380": "", + "2381": "", + "2382": "", + "2383": "", + "2385": "", + "2386": "", + "2387": "", + "2388": "", + "2389": "", + "2390": "", + "2391": "", + "2392": "\u0915", + "2393": "\u0916", + "2394": "\u0917", + "2395": "\u091c", + "2396": "\u0921", + "2397": "\u0922", + "2398": "\u092b", + "2399": "\u092f", + "2402": "", + "2403": "", + "2433": "", + "2434": "", + "2435": "", + "2492": "", + "2494": "", + "2495": "", + "2496": "", + "2497": "", + "2498": "", + "2499": "", + "2500": "", + "2503": "", + "2504": "", + "2507": "", + "2508": "", + "2509": "", + "2519": "", + "2524": "\u09a1", + "2525": "\u09a2", + "2527": "\u09af", + "2530": "", + "2531": "", + "2558": "", + "2561": "", + "2562": "", + "2563": "", + "2611": "\u0a32", + "2614": "\u0a38", + "2620": "", + "2622": "", + "2623": "", + "2624": "", + "2625": "", + "2626": "", + "2631": "", + "2632": "", + "2635": "", + "2636": "", + "2637": "", + "2641": "", + "2649": "\u0a16", + "2650": "\u0a17", + "2651": "\u0a1c", + "2654": "\u0a2b", + "2672": "", + "2673": "", + "2677": "", + "2689": "", + "2690": "", + "2691": "", + "2748": "", + "2750": "", + "2751": "", + "2752": "", + "2753": "", + "2754": "", + "2755": "", + "2756": "", + "2757": "", + "2759": "", + "2760": "", + "2761": "", + "2763": "", + "2764": "", + "2765": "", + "2786": "", + "2787": "", + "2810": "", + "2811": "", + "2812": "", + "2813": "", + "2814": "", + "2815": "", + "2817": "", + "2818": "", + "2819": "", + "2876": "", + "2878": "", + "2879": "", + "2880": "", + "2881": "", + "2882": "", + "2883": "", + "2884": "", + "2887": "", + "2888": "", + "2891": "", + "2892": "", + "2893": "", + "2901": "", + "2902": "", + "2903": "", + "2908": "\u0b21", + "2909": "\u0b22", + "2914": "", + "2915": "", + "2946": "", + "2964": "\u0b92", + "3006": "", + "3007": "", + "3008": "", + "3009": "", + "3010": "", + "3014": "", + "3015": "", + "3016": "", + "3018": "", + "3019": "", + "3020": "", + "3021": "", + "3031": "", + "3072": "", + "3073": "", + "3074": "", + "3075": "", + "3076": "", + "3132": "", + "3134": "", + "3135": "", + "3136": "", + "3137": "", + "3138": "", + "3139": "", + "3140": "", + "3142": "", + "3143": "", + "3144": "", + "3146": "", + "3147": "", + "3148": "", + "3149": "", + "3157": "", + "3158": "", + "3170": "", + "3171": "", + "3201": "", + "3202": "", + "3203": "", + "3260": "", + "3262": "", + "3263": "", + "3264": "", + "3265": "", + "3266": "", + "3267": "", + "3268": "", + "3270": "", + "3271": "", + "3272": "", + "3274": "", + "3275": "", + "3276": "", + "3277": "", + "3285": "", + "3286": "", + "3298": "", + "3299": "", + "3315": "", + "3328": "", + "3329": "", + "3330": "", + "3331": "", + "3387": "", + "3388": "", + "3390": "", + "3391": "", + "3392": "", + "3393": "", + "3394": "", + "3395": "", + "3396": "", + "3398": "", + "3399": "", + "3400": "", + "3402": "", + "3403": "", + "3404": "", + "3405": "", + "3415": "", + "3426": "", + "3427": "", + "3457": "", + "3458": "", + "3459": "", + "3530": "", + "3535": "", + "3536": "", + "3537": "", + "3538": "", + "3539": "", + "3540": "", + "3542": "", + "3544": "", + "3545": "", + "3546": "", + "3547": "", + "3548": "", + "3549": "", + "3550": "", + "3551": "", + "3570": "", + "3571": "", + "3633": "", + "3636": "", + "3637": "", + "3638": "", + "3639": "", + "3640": "", + "3641": "", + "3642": "", + "3655": "", + "3656": "", + "3657": "", + "3658": "", + "3659": "", + "3660": "", + "3661": "", + "3662": "", + "3761": "", + "3764": "", + "3765": "", + "3766": "", + "3767": "", + "3768": "", + "3769": "", + "3770": "", + "3771": "", + "3772": "", + "3784": "", + "3785": "", + "3786": "", + "3787": "", + "3788": "", + "3789": "", + "3790": "", + "3864": "", + "3865": "", + "3893": "", + "3895": "", + "3897": "", + "3902": "", + "3903": "", + "3907": "\u0f42", + "3917": "\u0f4c", + "3922": "\u0f51", + "3927": "\u0f56", + "3932": "\u0f5b", + "3945": "\u0f40", + "3953": "", + "3954": "", + "3955": "", + "3956": "", + "3957": "", + "3958": "", + "3959": "", + "3960": "", + "3961": "", + "3962": "", + "3963": "", + "3964": "", + "3965": "", + "3966": "", + "3967": "", + "3968": "", + "3969": "", + "3970": "", + "3971": "", + "3972": "", + "3974": "", + "3975": "", + "3981": "", + "3982": "", + "3983": "", + "3984": "", + "3985": "", + "3986": "", + "3987": "", + "3988": "", + "3989": "", + "3990": "", + "3991": "", + "3993": "", + "3994": "", + "3995": "", + "3996": "", + "3997": "", + "3998": "", + "3999": "", + "4000": "", + "4001": "", + "4002": "", + "4003": "", + "4004": "", + "4005": "", + "4006": "", + "4007": "", + "4008": "", + "4009": "", + "4010": "", + "4011": "", + "4012": "", + "4013": "", + "4014": "", + "4015": "", + "4016": "", + "4017": "", + "4018": "", + "4019": "", + "4020": "", + "4021": "", + "4022": "", + "4023": "", + "4024": "", + "4025": "", + "4026": "", + "4027": "", + "4028": "", + "4038": "", + "4134": "\u1025", + "4139": "", + "4140": "", + "4141": "", + "4142": "", + "4143": "", + "4144": "", + "4145": "", + "4146": "", + "4147": "", + "4148": "", + "4149": "", + "4150": "", + "4151": "", + "4152": "", + "4153": "", + "4154": "", + "4155": "", + "4156": "", + "4157": "", + "4158": "", + "4182": "", + "4183": "", + "4184": "", + "4185": "", + "4190": "", + "4191": "", + "4192": "", + "4194": "", + "4195": "", + "4196": "", + "4199": "", + "4200": "", + "4201": "", + "4202": "", + "4203": "", + "4204": "", + "4205": "", + "4209": "", + "4210": "", + "4211": "", + "4212": "", + "4226": "", + "4227": "", + "4228": "", + "4229": "", + "4230": "", + "4231": "", + "4232": "", + "4233": "", + "4234": "", + "4235": "", + "4236": "", + "4237": "", + "4239": "", + "4250": "", + "4251": "", + "4252": "", + "4253": "", + "4957": "", + "4958": "", + "4959": "", + "5906": "", + "5907": "", + "5908": "", + "5909": "", + "5938": "", + "5939": "", + "5940": "", + "5970": "", + "5971": "", + "6002": "", + "6003": "", + "6068": "", + "6069": "", + "6070": "", + "6071": "", + "6072": "", + "6073": "", + "6074": "", + "6075": "", + "6076": "", + "6077": "", + "6078": "", + "6079": "", + "6080": "", + "6081": "", + "6082": "", + "6083": "", + "6084": "", + "6085": "", + "6086": "", + "6087": "", + "6088": "", + "6089": "", + "6090": "", + "6091": "", + "6092": "", + "6093": "", + "6094": "", + "6095": "", + "6096": "", + "6097": "", + "6098": "", + "6099": "", + "6109": "", + "6155": "", + "6156": "", + "6157": "", + "6159": "", + "6277": "", + "6278": "", + "6313": "", + "6432": "", + "6433": "", + "6434": "", + "6435": "", + "6436": "", + "6437": "", + "6438": "", + "6439": "", + "6440": "", + "6441": "", + "6442": "", + "6443": "", + "6448": "", + "6449": "", + "6450": "", + "6451": "", + "6452": "", + "6453": "", + "6454": "", + "6455": "", + "6456": "", + "6457": "", + "6458": "", + "6459": "", + "6679": "", + "6680": "", + "6681": "", + "6682": "", + "6683": "", + "6741": "", + "6742": "", + "6743": "", + "6744": "", + "6745": "", + "6746": "", + "6747": "", + "6748": "", + "6749": "", + "6750": "", + "6752": "", + "6753": "", + "6754": "", + "6755": "", + "6756": "", + "6757": "", + "6758": "", + "6759": "", + "6760": "", + "6761": "", + "6762": "", + "6763": "", + "6764": "", + "6765": "", + "6766": "", + "6767": "", + "6768": "", + "6769": "", + "6770": "", + "6771": "", + "6772": "", + "6773": "", + "6774": "", + "6775": "", + "6776": "", + "6777": "", + "6778": "", + "6779": "", + "6780": "", + "6783": "", + "6832": "", + "6833": "", + "6834": "", + "6835": "", + "6836": "", + "6837": "", + "6838": "", + "6839": "", + "6840": "", + "6841": "", + "6842": "", + "6843": "", + "6844": "", + "6845": "", + "6846": "", + "6847": "", + "6848": "", + "6849": "", + "6850": "", + "6851": "", + "6852": "", + "6853": "", + "6854": "", + "6855": "", + "6856": "", + "6857": "", + "6858": "", + "6859": "", + "6860": "", + "6861": "", + "6862": "", + "6912": "", + "6913": "", + "6914": "", + "6915": "", + "6916": "", + "6918": "\u1b05", + "6920": "\u1b07", + "6922": "\u1b09", + "6924": "\u1b0b", + "6926": "\u1b0d", + "6930": "\u1b11", + "6964": "", + "6965": "", + "6966": "", + "6967": "", + "6968": "", + "6969": "", + "6970": "", + "6971": "", + "6972": "", + "6973": "", + "6974": "", + "6975": "", + "6976": "", + "6977": "", + "6978": "", + "6979": "", + "6980": "", + "7019": "", + "7020": "", + "7021": "", + "7022": "", + "7023": "", + "7024": "", + "7025": "", + "7026": "", + "7027": "", + "7040": "", + "7041": "", + "7042": "", + "7073": "", + "7074": "", + "7075": "", + "7076": "", + "7077": "", + "7078": "", + "7079": "", + "7080": "", + "7081": "", + "7082": "", + "7083": "", + "7084": "", + "7085": "", + "7142": "", + "7143": "", + "7144": "", + "7145": "", + "7146": "", + "7147": "", + "7148": "", + "7149": "", + "7150": "", + "7151": "", + "7152": "", + "7153": "", + "7154": "", + "7155": "", + "7204": "", + "7205": "", + "7206": "", + "7207": "", + "7208": "", + "7209": "", + "7210": "", + "7211": "", + "7212": "", + "7213": "", + "7214": "", + "7215": "", + "7216": "", + "7217": "", + "7218": "", + "7219": "", + "7220": "", + "7221": "", + "7222": "", + "7223": "", + "7376": "", + "7377": "", + "7378": "", + "7380": "", + "7381": "", + "7382": "", + "7383": "", + "7384": "", + "7385": "", + "7386": "", + "7387": "", + "7388": "", + "7389": "", + "7390": "", + "7391": "", + "7392": "", + "7393": "", + "7394": "", + "7395": "", + "7396": "", + "7397": "", + "7398": "", + "7399": "", + "7400": "", + "7405": "", + "7412": "", + "7415": "", + "7416": "", + "7417": "", + "7616": "", + "7617": "", + "7618": "", + "7619": "", + "7620": "", + "7621": "", + "7622": "", + "7623": "", + "7624": "", + "7625": "", + "7626": "", + "7627": "", + "7628": "", + "7629": "", + "7630": "", + "7631": "", + "7632": "", + "7633": "", + "7634": "", + "7635": "", + "7636": "", + "7637": "", + "7638": "", + "7639": "", + "7640": "", + "7641": "", + "7642": "", + "7643": "", + "7644": "", + "7645": "", + "7646": "", + "7647": "", + "7648": "", + "7649": "", + "7650": "", + "7651": "", + "7652": "", + "7653": "", + "7654": "", + "7655": "", + "7656": "", + "7657": "", + "7658": "", + "7659": "", + "7660": "", + "7661": "", + "7662": "", + "7663": "", + "7664": "", + "7665": "", + "7666": "", + "7667": "", + "7668": "", + "7669": "", + "7670": "", + "7671": "", + "7672": "", + "7673": "", + "7674": "", + "7675": "", + "7676": "", + "7677": "", + "7678": "", + "7679": "", + "7680": "A", + "7681": "a", + "7682": "B", + "7683": "b", + "7684": "B", + "7685": "b", + "7686": "B", + "7687": "b", + "7688": "C", + "7689": "c", + "7690": "D", + "7691": "d", + "7692": "D", + "7693": "d", + "7694": "D", + "7695": "d", + "7696": "D", + "7697": "d", + "7698": "D", + "7699": "d", + "7700": "E", + "7701": "e", + "7702": "E", + "7703": "e", + "7704": "E", + "7705": "e", + "7706": "E", + "7707": "e", + "7708": "E", + "7709": "e", + "7710": "F", + "7711": "f", + "7712": "G", + "7713": "g", + "7714": "H", + "7715": "h", + "7716": "H", + "7717": "h", + "7718": "H", + "7719": "h", + "7720": "H", + "7721": "h", + "7722": "H", + "7723": "h", + "7724": "I", + "7725": "i", + "7726": "I", + "7727": "i", + "7728": "K", + "7729": "k", + "7730": "K", + "7731": "k", + "7732": "K", + "7733": "k", + "7734": "L", + "7735": "l", + "7736": "L", + "7737": "l", + "7738": "L", + "7739": "l", + "7740": "L", + "7741": "l", + "7742": "M", + "7743": "m", + "7744": "M", + "7745": "m", + "7746": "M", + "7747": "m", + "7748": "N", + "7749": "n", + "7750": "N", + "7751": "n", + "7752": "N", + "7753": "n", + "7754": "N", + "7755": "n", + "7756": "O", + "7757": "o", + "7758": "O", + "7759": "o", + "7760": "O", + "7761": "o", + "7762": "O", + "7763": "o", + "7764": "P", + "7765": "p", + "7766": "P", + "7767": "p", + "7768": "R", + "7769": "r", + "7770": "R", + "7771": "r", + "7772": "R", + "7773": "r", + "7774": "R", + "7775": "r", + "7776": "S", + "7777": "s", + "7778": "S", + "7779": "s", + "7780": "S", + "7781": "s", + "7782": "S", + "7783": "s", + "7784": "S", + "7785": "s", + "7786": "T", + "7787": "t", + "7788": "T", + "7789": "t", + "7790": "T", + "7791": "t", + "7792": "T", + "7793": "t", + "7794": "U", + "7795": "u", + "7796": "U", + "7797": "u", + "7798": "U", + "7799": "u", + "7800": "U", + "7801": "u", + "7802": "U", + "7803": "u", + "7804": "V", + "7805": "v", + "7806": "V", + "7807": "v", + "7808": "W", + "7809": "w", + "7810": "W", + "7811": "w", + "7812": "W", + "7813": "w", + "7814": "W", + "7815": "w", + "7816": "W", + "7817": "w", + "7818": "X", + "7819": "x", + "7820": "X", + "7821": "x", + "7822": "Y", + "7823": "y", + "7824": "Z", + "7825": "z", + "7826": "Z", + "7827": "z", + "7828": "Z", + "7829": "z", + "7830": "h", + "7831": "t", + "7832": "w", + "7833": "y", + "7835": "\u017f", + "7840": "A", + "7841": "a", + "7842": "A", + "7843": "a", + "7844": "A", + "7845": "a", + "7846": "A", + "7847": "a", + "7848": "A", + "7849": "a", + "7850": "A", + "7851": "a", + "7852": "A", + "7853": "a", + "7854": "A", + "7855": "a", + "7856": "A", + "7857": "a", + "7858": "A", + "7859": "a", + "7860": "A", + "7861": "a", + "7862": "A", + "7863": "a", + "7864": "E", + "7865": "e", + "7866": "E", + "7867": "e", + "7868": "E", + "7869": "e", + "7870": "E", + "7871": "e", + "7872": "E", + "7873": "e", + "7874": "E", + "7875": "e", + "7876": "E", + "7877": "e", + "7878": "E", + "7879": "e", + "7880": "I", + "7881": "i", + "7882": "I", + "7883": "i", + "7884": "O", + "7885": "o", + "7886": "O", + "7887": "o", + "7888": "O", + "7889": "o", + "7890": "O", + "7891": "o", + "7892": "O", + "7893": "o", + "7894": "O", + "7895": "o", + "7896": "O", + "7897": "o", + "7898": "O", + "7899": "o", + "7900": "O", + "7901": "o", + "7902": "O", + "7903": "o", + "7904": "O", + "7905": "o", + "7906": "O", + "7907": "o", + "7908": "U", + "7909": "u", + "7910": "U", + "7911": "u", + "7912": "U", + "7913": "u", + "7914": "U", + "7915": "u", + "7916": "U", + "7917": "u", + "7918": "U", + "7919": "u", + "7920": "U", + "7921": "u", + "7922": "Y", + "7923": "y", + "7924": "Y", + "7925": "y", + "7926": "Y", + "7927": "y", + "7928": "Y", + "7929": "y", + "7936": "\u03b1", + "7937": "\u03b1", + "7938": "\u03b1", + "7939": "\u03b1", + "7940": "\u03b1", + "7941": "\u03b1", + "7942": "\u03b1", + "7943": "\u03b1", + "7944": "\u0391", + "7945": "\u0391", + "7946": "\u0391", + "7947": "\u0391", + "7948": "\u0391", + "7949": "\u0391", + "7950": "\u0391", + "7951": "\u0391", + "7952": "\u03b5", + "7953": "\u03b5", + "7954": "\u03b5", + "7955": "\u03b5", + "7956": "\u03b5", + "7957": "\u03b5", + "7960": "\u0395", + "7961": "\u0395", + "7962": "\u0395", + "7963": "\u0395", + "7964": "\u0395", + "7965": "\u0395", + "7968": "\u03b7", + "7969": "\u03b7", + "7970": "\u03b7", + "7971": "\u03b7", + "7972": "\u03b7", + "7973": "\u03b7", + "7974": "\u03b7", + "7975": "\u03b7", + "7976": "\u0397", + "7977": "\u0397", + "7978": "\u0397", + "7979": "\u0397", + "7980": "\u0397", + "7981": "\u0397", + "7982": "\u0397", + "7983": "\u0397", + "7984": "\u03b9", + "7985": "\u03b9", + "7986": "\u03b9", + "7987": "\u03b9", + "7988": "\u03b9", + "7989": "\u03b9", + "7990": "\u03b9", + "7991": "\u03b9", + "7992": "\u0399", + "7993": "\u0399", + "7994": "\u0399", + "7995": "\u0399", + "7996": "\u0399", + "7997": "\u0399", + "7998": "\u0399", + "7999": "\u0399", + "8000": "\u03bf", + "8001": "\u03bf", + "8002": "\u03bf", + "8003": "\u03bf", + "8004": "\u03bf", + "8005": "\u03bf", + "8008": "\u039f", + "8009": "\u039f", + "8010": "\u039f", + "8011": "\u039f", + "8012": "\u039f", + "8013": "\u039f", + "8016": "\u03c5", + "8017": "\u03c5", + "8018": "\u03c5", + "8019": "\u03c5", + "8020": "\u03c5", + "8021": "\u03c5", + "8022": "\u03c5", + "8023": "\u03c5", + "8025": "\u03a5", + "8027": "\u03a5", + "8029": "\u03a5", + "8031": "\u03a5", + "8032": "\u03c9", + "8033": "\u03c9", + "8034": "\u03c9", + "8035": "\u03c9", + "8036": "\u03c9", + "8037": "\u03c9", + "8038": "\u03c9", + "8039": "\u03c9", + "8040": "\u03a9", + "8041": "\u03a9", + "8042": "\u03a9", + "8043": "\u03a9", + "8044": "\u03a9", + "8045": "\u03a9", + "8046": "\u03a9", + "8047": "\u03a9", + "8048": "\u03b1", + "8049": "\u03b1", + "8050": "\u03b5", + "8051": "\u03b5", + "8052": "\u03b7", + "8053": "\u03b7", + "8054": "\u03b9", + "8055": "\u03b9", + "8056": "\u03bf", + "8057": "\u03bf", + "8058": "\u03c5", + "8059": "\u03c5", + "8060": "\u03c9", + "8061": "\u03c9", + "8064": "\u03b1", + "8065": "\u03b1", + "8066": "\u03b1", + "8067": "\u03b1", + "8068": "\u03b1", + "8069": "\u03b1", + "8070": "\u03b1", + "8071": "\u03b1", + "8072": "\u0391", + "8073": "\u0391", + "8074": "\u0391", + "8075": "\u0391", + "8076": "\u0391", + "8077": "\u0391", + "8078": "\u0391", + "8079": "\u0391", + "8080": "\u03b7", + "8081": "\u03b7", + "8082": "\u03b7", + "8083": "\u03b7", + "8084": "\u03b7", + "8085": "\u03b7", + "8086": "\u03b7", + "8087": "\u03b7", + "8088": "\u0397", + "8089": "\u0397", + "8090": "\u0397", + "8091": "\u0397", + "8092": "\u0397", + "8093": "\u0397", + "8094": "\u0397", + "8095": "\u0397", + "8096": "\u03c9", + "8097": "\u03c9", + "8098": "\u03c9", + "8099": "\u03c9", + "8100": "\u03c9", + "8101": "\u03c9", + "8102": "\u03c9", + "8103": "\u03c9", + "8104": "\u03a9", + "8105": "\u03a9", + "8106": "\u03a9", + "8107": "\u03a9", + "8108": "\u03a9", + "8109": "\u03a9", + "8110": "\u03a9", + "8111": "\u03a9", + "8112": "\u03b1", + "8113": "\u03b1", + "8114": "\u03b1", + "8115": "\u03b1", + "8116": "\u03b1", + "8118": "\u03b1", + "8119": "\u03b1", + "8120": "\u0391", + "8121": "\u0391", + "8122": "\u0391", + "8123": "\u0391", + "8124": "\u0391", + "8126": "\u03b9", + "8129": "\u00a8", + "8130": "\u03b7", + "8131": "\u03b7", + "8132": "\u03b7", + "8134": "\u03b7", + "8135": "\u03b7", + "8136": "\u0395", + "8137": "\u0395", + "8138": "\u0397", + "8139": "\u0397", + "8140": "\u0397", + "8141": "\u1fbf", + "8142": "\u1fbf", + "8143": "\u1fbf", + "8144": "\u03b9", + "8145": "\u03b9", + "8146": "\u03b9", + "8147": "\u03b9", + "8150": "\u03b9", + "8151": "\u03b9", + "8152": "\u0399", + "8153": "\u0399", + "8154": "\u0399", + "8155": "\u0399", + "8157": "\u1ffe", + "8158": "\u1ffe", + "8159": "\u1ffe", + "8160": "\u03c5", + "8161": "\u03c5", + "8162": "\u03c5", + "8163": "\u03c5", + "8164": "\u03c1", + "8165": "\u03c1", + "8166": "\u03c5", + "8167": "\u03c5", + "8168": "\u03a5", + "8169": "\u03a5", + "8170": "\u03a5", + "8171": "\u03a5", + "8172": "\u03a1", + "8173": "\u00a8", + "8174": "\u00a8", + "8175": "`", + "8178": "\u03c9", + "8179": "\u03c9", + "8180": "\u03c9", + "8182": "\u03c9", + "8183": "\u03c9", + "8184": "\u039f", + "8185": "\u039f", + "8186": "\u03a9", + "8187": "\u03a9", + "8188": "\u03a9", + "8189": "\u00b4", + "8192": "\u2002", + "8193": "\u2003", + "8400": "", + "8401": "", + "8402": "", + "8403": "", + "8404": "", + "8405": "", + "8406": "", + "8407": "", + "8408": "", + "8409": "", + "8410": "", + "8411": "", + "8412": "", + "8413": "", + "8414": "", + "8415": "", + "8416": "", + "8417": "", + "8418": "", + "8419": "", + "8420": "", + "8421": "", + "8422": "", + "8423": "", + "8424": "", + "8425": "", + "8426": "", + "8427": "", + "8428": "", + "8429": "", + "8430": "", + "8431": "", + "8432": "", + "8486": "\u03a9", + "8490": "K", + "8491": "A", + "8602": "\u2190", + "8603": "\u2192", + "8622": "\u2194", + "8653": "\u21d0", + "8654": "\u21d4", + "8655": "\u21d2", + "8708": "\u2203", + "8713": "\u2208", + "8716": "\u220b", + "8740": "\u2223", + "8742": "\u2225", + "8769": "\u223c", + "8772": "\u2243", + "8775": "\u2245", + "8777": "\u2248", + "8800": "=", + "8802": "\u2261", + "8813": "\u224d", + "8814": "<", + "8815": ">", + "8816": "\u2264", + "8817": "\u2265", + "8820": "\u2272", + "8821": "\u2273", + "8824": "\u2276", + "8825": "\u2277", + "8832": "\u227a", + "8833": "\u227b", + "8836": "\u2282", + "8837": "\u2283", + "8840": "\u2286", + "8841": "\u2287", + "8876": "\u22a2", + "8877": "\u22a8", + "8878": "\u22a9", + "8879": "\u22ab", + "8928": "\u227c", + "8929": "\u227d", + "8930": "\u2291", + "8931": "\u2292", + "8938": "\u22b2", + "8939": "\u22b3", + "8940": "\u22b4", + "8941": "\u22b5", + "9001": "\u3008", + "9002": "\u3009", + "10972": "\u2add", + "11503": "", + "11504": "", + "11505": "", + "11647": "", + "11744": "", + "11745": "", + "11746": "", + "11747": "", + "11748": "", + "11749": "", + "11750": "", + "11751": "", + "11752": "", + "11753": "", + "11754": "", + "11755": "", + "11756": "", + "11757": "", + "11758": "", + "11759": "", + "11760": "", + "11761": "", + "11762": "", + "11763": "", + "11764": "", + "11765": "", + "11766": "", + "11767": "", + "11768": "", + "11769": "", + "11770": "", + "11771": "", + "11772": "", + "11773": "", + "11774": "", + "11775": "", + "12330": "", + "12331": "", + "12332": "", + "12333": "", + "12334": "", + "12335": "", + "12364": "\u304b", + "12366": "\u304d", + "12368": "\u304f", + "12370": "\u3051", + "12372": "\u3053", + "12374": "\u3055", + "12376": "\u3057", + "12378": "\u3059", + "12380": "\u305b", + "12382": "\u305d", + "12384": "\u305f", + "12386": "\u3061", + "12389": "\u3064", + "12391": "\u3066", + "12393": "\u3068", + "12400": "\u306f", + "12401": "\u306f", + "12403": "\u3072", + "12404": "\u3072", + "12406": "\u3075", + "12407": "\u3075", + "12409": "\u3078", + "12410": "\u3078", + "12412": "\u307b", + "12413": "\u307b", + "12436": "\u3046", + "12441": "", + "12442": "", + "12446": "\u309d", + "12460": "\u30ab", + "12462": "\u30ad", + "12464": "\u30af", + "12466": "\u30b1", + "12468": "\u30b3", + "12470": "\u30b5", + "12472": "\u30b7", + "12474": "\u30b9", + "12476": "\u30bb", + "12478": "\u30bd", + "12480": "\u30bf", + "12482": "\u30c1", + "12485": "\u30c4", + "12487": "\u30c6", + "12489": "\u30c8", + "12496": "\u30cf", + "12497": "\u30cf", + "12499": "\u30d2", + "12500": "\u30d2", + "12502": "\u30d5", + "12503": "\u30d5", + "12505": "\u30d8", + "12506": "\u30d8", + "12508": "\u30db", + "12509": "\u30db", + "12532": "\u30a6", + "12535": "\u30ef", + "12536": "\u30f0", + "12537": "\u30f1", + "12538": "\u30f2", + "12542": "\u30fd", + "42607": "", + "42608": "", + "42609": "", + "42610": "", + "42612": "", + "42613": "", + "42614": "", + "42615": "", + "42616": "", + "42617": "", + "42618": "", + "42619": "", + "42620": "", + "42621": "", + "42654": "", + "42655": "", + "42736": "", + "42737": "", + "43010": "", + "43014": "", + "43019": "", + "43043": "", + "43044": "", + "43045": "", + "43046": "", + "43047": "", + "43052": "", + "43136": "", + "43137": "", + "43188": "", + "43189": "", + "43190": "", + "43191": "", + "43192": "", + "43193": "", + "43194": "", + "43195": "", + "43196": "", + "43197": "", + "43198": "", + "43199": "", + "43200": "", + "43201": "", + "43202": "", + "43203": "", + "43204": "", + "43205": "", + "43232": "", + "43233": "", + "43234": "", + "43235": "", + "43236": "", + "43237": "", + "43238": "", + "43239": "", + "43240": "", + "43241": "", + "43242": "", + "43243": "", + "43244": "", + "43245": "", + "43246": "", + "43247": "", + "43248": "", + "43249": "", + "43263": "", + "43302": "", + "43303": "", + "43304": "", + "43305": "", + "43306": "", + "43307": "", + "43308": "", + "43309": "", + "43335": "", + "43336": "", + "43337": "", + "43338": "", + "43339": "", + "43340": "", + "43341": "", + "43342": "", + "43343": "", + "43344": "", + "43345": "", + "43346": "", + "43347": "", + "43392": "", + "43393": "", + "43394": "", + "43395": "", + "43443": "", + "43444": "", + "43445": "", + "43446": "", + "43447": "", + "43448": "", + "43449": "", + "43450": "", + "43451": "", + "43452": "", + "43453": "", + "43454": "", + "43455": "", + "43456": "", + "43493": "", + "43561": "", + "43562": "", + "43563": "", + "43564": "", + "43565": "", + "43566": "", + "43567": "", + "43568": "", + "43569": "", + "43570": "", + "43571": "", + "43572": "", + "43573": "", + "43574": "", + "43587": "", + "43596": "", + "43597": "", + "43643": "", + "43644": "", + "43645": "", + "43696": "", + "43698": "", + "43699": "", + "43700": "", + "43703": "", + "43704": "", + "43710": "", + "43711": "", + "43713": "", + "43755": "", + "43756": "", + "43757": "", + "43758": "", + "43759": "", + "43765": "", + "43766": "", + "44003": "", + "44004": "", + "44005": "", + "44006": "", + "44007": "", + "44008": "", + "44009": "", + "44010": "", + "44012": "", + "44013": "", + "63744": "\u8c48", + "63745": "\u66f4", + "63746": "\u8eca", + "63747": "\u8cc8", + "63748": "\u6ed1", + "63749": "\u4e32", + "63750": "\u53e5", + "63751": "\u9f9c", + "63752": "\u9f9c", + "63753": "\u5951", + "63754": "\u91d1", + "63755": "\u5587", + "63756": "\u5948", + "63757": "\u61f6", + "63758": "\u7669", + "63759": "\u7f85", + "63760": "\u863f", + "63761": "\u87ba", + "63762": "\u88f8", + "63763": "\u908f", + "63764": "\u6a02", + "63765": "\u6d1b", + "63766": "\u70d9", + "63767": "\u73de", + "63768": "\u843d", + "63769": "\u916a", + "63770": "\u99f1", + "63771": "\u4e82", + "63772": "\u5375", + "63773": "\u6b04", + "63774": "\u721b", + "63775": "\u862d", + "63776": "\u9e1e", + "63777": "\u5d50", + "63778": "\u6feb", + "63779": "\u85cd", + "63780": "\u8964", + "63781": "\u62c9", + "63782": "\u81d8", + "63783": "\u881f", + "63784": "\u5eca", + "63785": "\u6717", + "63786": "\u6d6a", + "63787": "\u72fc", + "63788": "\u90ce", + "63789": "\u4f86", + "63790": "\u51b7", + "63791": "\u52de", + "63792": "\u64c4", + "63793": "\u6ad3", + "63794": "\u7210", + "63795": "\u76e7", + "63796": "\u8001", + "63797": "\u8606", + "63798": "\u865c", + "63799": "\u8def", + "63800": "\u9732", + "63801": "\u9b6f", + "63802": "\u9dfa", + "63803": "\u788c", + "63804": "\u797f", + "63805": "\u7da0", + "63806": "\u83c9", + "63807": "\u9304", + "63808": "\u9e7f", + "63809": "\u8ad6", + "63810": "\u58df", + "63811": "\u5f04", + "63812": "\u7c60", + "63813": "\u807e", + "63814": "\u7262", + "63815": "\u78ca", + "63816": "\u8cc2", + "63817": "\u96f7", + "63818": "\u58d8", + "63819": "\u5c62", + "63820": "\u6a13", + "63821": "\u6dda", + "63822": "\u6f0f", + "63823": "\u7d2f", + "63824": "\u7e37", + "63825": "\u964b", + "63826": "\u52d2", + "63827": "\u808b", + "63828": "\u51dc", + "63829": "\u51cc", + "63830": "\u7a1c", + "63831": "\u7dbe", + "63832": "\u83f1", + "63833": "\u9675", + "63834": "\u8b80", + "63835": "\u62cf", + "63836": "\u6a02", + "63837": "\u8afe", + "63838": "\u4e39", + "63839": "\u5be7", + "63840": "\u6012", + "63841": "\u7387", + "63842": "\u7570", + "63843": "\u5317", + "63844": "\u78fb", + "63845": "\u4fbf", + "63846": "\u5fa9", + "63847": "\u4e0d", + "63848": "\u6ccc", + "63849": "\u6578", + "63850": "\u7d22", + "63851": "\u53c3", + "63852": "\u585e", + "63853": "\u7701", + "63854": "\u8449", + "63855": "\u8aaa", + "63856": "\u6bba", + "63857": "\u8fb0", + "63858": "\u6c88", + "63859": "\u62fe", + "63860": "\u82e5", + "63861": "\u63a0", + "63862": "\u7565", + "63863": "\u4eae", + "63864": "\u5169", + "63865": "\u51c9", + "63866": "\u6881", + "63867": "\u7ce7", + "63868": "\u826f", + "63869": "\u8ad2", + "63870": "\u91cf", + "63871": "\u52f5", + "63872": "\u5442", + "63873": "\u5973", + "63874": "\u5eec", + "63875": "\u65c5", + "63876": "\u6ffe", + "63877": "\u792a", + "63878": "\u95ad", + "63879": "\u9a6a", + "63880": "\u9e97", + "63881": "\u9ece", + "63882": "\u529b", + "63883": "\u66c6", + "63884": "\u6b77", + "63885": "\u8f62", + "63886": "\u5e74", + "63887": "\u6190", + "63888": "\u6200", + "63889": "\u649a", + "63890": "\u6f23", + "63891": "\u7149", + "63892": "\u7489", + "63893": "\u79ca", + "63894": "\u7df4", + "63895": "\u806f", + "63896": "\u8f26", + "63897": "\u84ee", + "63898": "\u9023", + "63899": "\u934a", + "63900": "\u5217", + "63901": "\u52a3", + "63902": "\u54bd", + "63903": "\u70c8", + "63904": "\u88c2", + "63905": "\u8aaa", + "63906": "\u5ec9", + "63907": "\u5ff5", + "63908": "\u637b", + "63909": "\u6bae", + "63910": "\u7c3e", + "63911": "\u7375", + "63912": "\u4ee4", + "63913": "\u56f9", + "63914": "\u5be7", + "63915": "\u5dba", + "63916": "\u601c", + "63917": "\u73b2", + "63918": "\u7469", + "63919": "\u7f9a", + "63920": "\u8046", + "63921": "\u9234", + "63922": "\u96f6", + "63923": "\u9748", + "63924": "\u9818", + "63925": "\u4f8b", + "63926": "\u79ae", + "63927": "\u91b4", + "63928": "\u96b8", + "63929": "\u60e1", + "63930": "\u4e86", + "63931": "\u50da", + "63932": "\u5bee", + "63933": "\u5c3f", + "63934": "\u6599", + "63935": "\u6a02", + "63936": "\u71ce", + "63937": "\u7642", + "63938": "\u84fc", + "63939": "\u907c", + "63940": "\u9f8d", + "63941": "\u6688", + "63942": "\u962e", + "63943": "\u5289", + "63944": "\u677b", + "63945": "\u67f3", + "63946": "\u6d41", + "63947": "\u6e9c", + "63948": "\u7409", + "63949": "\u7559", + "63950": "\u786b", + "63951": "\u7d10", + "63952": "\u985e", + "63953": "\u516d", + "63954": "\u622e", + "63955": "\u9678", + "63956": "\u502b", + "63957": "\u5d19", + "63958": "\u6dea", + "63959": "\u8f2a", + "63960": "\u5f8b", + "63961": "\u6144", + "63962": "\u6817", + "63963": "\u7387", + "63964": "\u9686", + "63965": "\u5229", + "63966": "\u540f", + "63967": "\u5c65", + "63968": "\u6613", + "63969": "\u674e", + "63970": "\u68a8", + "63971": "\u6ce5", + "63972": "\u7406", + "63973": "\u75e2", + "63974": "\u7f79", + "63975": "\u88cf", + "63976": "\u88e1", + "63977": "\u91cc", + "63978": "\u96e2", + "63979": "\u533f", + "63980": "\u6eba", + "63981": "\u541d", + "63982": "\u71d0", + "63983": "\u7498", + "63984": "\u85fa", + "63985": "\u96a3", + "63986": "\u9c57", + "63987": "\u9e9f", + "63988": "\u6797", + "63989": "\u6dcb", + "63990": "\u81e8", + "63991": "\u7acb", + "63992": "\u7b20", + "63993": "\u7c92", + "63994": "\u72c0", + "63995": "\u7099", + "63996": "\u8b58", + "63997": "\u4ec0", + "63998": "\u8336", + "63999": "\u523a", + "64000": "\u5207", + "64001": "\u5ea6", + "64002": "\u62d3", + "64003": "\u7cd6", + "64004": "\u5b85", + "64005": "\u6d1e", + "64006": "\u66b4", + "64007": "\u8f3b", + "64008": "\u884c", + "64009": "\u964d", + "64010": "\u898b", + "64011": "\u5ed3", + "64012": "\u5140", + "64013": "\u55c0", + "64016": "\u585a", + "64018": "\u6674", + "64021": "\u51de", + "64022": "\u732a", + "64023": "\u76ca", + "64024": "\u793c", + "64025": "\u795e", + "64026": "\u7965", + "64027": "\u798f", + "64028": "\u9756", + "64029": "\u7cbe", + "64030": "\u7fbd", + "64032": "\u8612", + "64034": "\u8af8", + "64037": "\u9038", + "64038": "\u90fd", + "64042": "\u98ef", + "64043": "\u98fc", + "64044": "\u9928", + "64045": "\u9db4", + "64046": "\u90de", + "64047": "\u96b7", + "64048": "\u4fae", + "64049": "\u50e7", + "64050": "\u514d", + "64051": "\u52c9", + "64052": "\u52e4", + "64053": "\u5351", + "64054": "\u559d", + "64055": "\u5606", + "64056": "\u5668", + "64057": "\u5840", + "64058": "\u58a8", + "64059": "\u5c64", + "64060": "\u5c6e", + "64061": "\u6094", + "64062": "\u6168", + "64063": "\u618e", + "64064": "\u61f2", + "64065": "\u654f", + "64066": "\u65e2", + "64067": "\u6691", + "64068": "\u6885", + "64069": "\u6d77", + "64070": "\u6e1a", + "64071": "\u6f22", + "64072": "\u716e", + "64073": "\u722b", + "64074": "\u7422", + "64075": "\u7891", + "64076": "\u793e", + "64077": "\u7949", + "64078": "\u7948", + "64079": "\u7950", + "64080": "\u7956", + "64081": "\u795d", + "64082": "\u798d", + "64083": "\u798e", + "64084": "\u7a40", + "64085": "\u7a81", + "64086": "\u7bc0", + "64087": "\u7df4", + "64088": "\u7e09", + "64089": "\u7e41", + "64090": "\u7f72", + "64091": "\u8005", + "64092": "\u81ed", + "64093": "\u8279", + "64094": "\u8279", + "64095": "\u8457", + "64096": "\u8910", + "64097": "\u8996", + "64098": "\u8b01", + "64099": "\u8b39", + "64100": "\u8cd3", + "64101": "\u8d08", + "64102": "\u8fb6", + "64103": "\u9038", + "64104": "\u96e3", + "64105": "\u97ff", + "64106": "\u983b", + "64107": "\u6075", + "64108": "\ud850\udeee", + "64109": "\u8218", + "64112": "\u4e26", + "64113": "\u51b5", + "64114": "\u5168", + "64115": "\u4f80", + "64116": "\u5145", + "64117": "\u5180", + "64118": "\u52c7", + "64119": "\u52fa", + "64120": "\u559d", + "64121": "\u5555", + "64122": "\u5599", + "64123": "\u55e2", + "64124": "\u585a", + "64125": "\u58b3", + "64126": "\u5944", + "64127": "\u5954", + "64128": "\u5a62", + "64129": "\u5b28", + "64130": "\u5ed2", + "64131": "\u5ed9", + "64132": "\u5f69", + "64133": "\u5fad", + "64134": "\u60d8", + "64135": "\u614e", + "64136": "\u6108", + "64137": "\u618e", + "64138": "\u6160", + "64139": "\u61f2", + "64140": "\u6234", + "64141": "\u63c4", + "64142": "\u641c", + "64143": "\u6452", + "64144": "\u6556", + "64145": "\u6674", + "64146": "\u6717", + "64147": "\u671b", + "64148": "\u6756", + "64149": "\u6b79", + "64150": "\u6bba", + "64151": "\u6d41", + "64152": "\u6edb", + "64153": "\u6ecb", + "64154": "\u6f22", + "64155": "\u701e", + "64156": "\u716e", + "64157": "\u77a7", + "64158": "\u7235", + "64159": "\u72af", + "64160": "\u732a", + "64161": "\u7471", + "64162": "\u7506", + "64163": "\u753b", + "64164": "\u761d", + "64165": "\u761f", + "64166": "\u76ca", + "64167": "\u76db", + "64168": "\u76f4", + "64169": "\u774a", + "64170": "\u7740", + "64171": "\u78cc", + "64172": "\u7ab1", + "64173": "\u7bc0", + "64174": "\u7c7b", + "64175": "\u7d5b", + "64176": "\u7df4", + "64177": "\u7f3e", + "64178": "\u8005", + "64179": "\u8352", + "64180": "\u83ef", + "64181": "\u8779", + "64182": "\u8941", + "64183": "\u8986", + "64184": "\u8996", + "64185": "\u8abf", + "64186": "\u8af8", + "64187": "\u8acb", + "64188": "\u8b01", + "64189": "\u8afe", + "64190": "\u8aed", + "64191": "\u8b39", + "64192": "\u8b8a", + "64193": "\u8d08", + "64194": "\u8f38", + "64195": "\u9072", + "64196": "\u9199", + "64197": "\u9276", + "64198": "\u967c", + "64199": "\u96e3", + "64200": "\u9756", + "64201": "\u97db", + "64202": "\u97ff", + "64203": "\u980b", + "64204": "\u983b", + "64205": "\u9b12", + "64206": "\u9f9c", + "64207": "\ud84a\udc4a", + "64208": "\ud84a\udc44", + "64209": "\ud84c\udfd5", + "64210": "\u3b9d", + "64211": "\u4018", + "64212": "\u4039", + "64213": "\ud854\ude49", + "64214": "\ud857\udcd0", + "64215": "\ud85f\uded3", + "64216": "\u9f43", + "64217": "\u9f8e", + "64285": "\u05d9", + "64286": "", + "64287": "\u05f2", + "64298": "\u05e9", + "64299": "\u05e9", + "64300": "\u05e9", + "64301": "\u05e9", + "64302": "\u05d0", + "64303": "\u05d0", + "64304": "\u05d0", + "64305": "\u05d1", + "64306": "\u05d2", + "64307": "\u05d3", + "64308": "\u05d4", + "64309": "\u05d5", + "64310": "\u05d6", + "64312": "\u05d8", + "64313": "\u05d9", + "64314": "\u05da", + "64315": "\u05db", + "64316": "\u05dc", + "64318": "\u05de", + "64320": "\u05e0", + "64321": "\u05e1", + "64323": "\u05e3", + "64324": "\u05e4", + "64326": "\u05e6", + "64327": "\u05e7", + "64328": "\u05e8", + "64329": "\u05e9", + "64330": "\u05ea", + "64331": "\u05d5", + "64332": "\u05d1", + "64333": "\u05db", + "64334": "\u05e4", + "65024": "", + "65025": "", + "65026": "", + "65027": "", + "65028": "", + "65029": "", + "65030": "", + "65031": "", + "65032": "", + "65033": "", + "65034": "", + "65035": "", + "65036": "", + "65037": "", + "65038": "", + "65039": "", + "65056": "", + "65057": "", + "65058": "", + "65059": "", + "65060": "", + "65061": "", + "65062": "", + "65063": "", + "65064": "", + "65065": "", + "65066": "", + "65067": "", + "65068": "", + "65069": "", + "65070": "", + "65071": "", + "66045": "", + "66272": "", + "66422": "", + "66423": "", + "66424": "", + "66425": "", + "66426": "", + "68097": "", + "68098": "", + "68099": "", + "68101": "", + "68102": "", + "68108": "", + "68109": "", + "68110": "", + "68111": "", + "68152": "", + "68153": "", + "68154": "", + "68159": "", + "68325": "", + "68326": "", + "68900": "", + "68901": "", + "68902": "", + "68903": "", + "69291": "", + "69292": "", + "69373": "", + "69374": "", + "69375": "", + "69446": "", + "69447": "", + "69448": "", + "69449": "", + "69450": "", + "69451": "", + "69452": "", + "69453": "", + "69454": "", + "69455": "", + "69456": "", + "69506": "", + "69507": "", + "69508": "", + "69509": "", + "69632": "", + "69633": "", + "69634": "", + "69688": "", + "69689": "", + "69690": "", + "69691": "", + "69692": "", + "69693": "", + "69694": "", + "69695": "", + "69696": "", + "69697": "", + "69698": "", + "69699": "", + "69700": "", + "69701": "", + "69702": "", + "69744": "", + "69747": "", + "69748": "", + "69759": "", + "69760": "", + "69761": "", + "69762": "", + "69786": "\ud804\udc99", + "69788": "\ud804\udc9b", + "69803": "\ud804\udca5", + "69808": "", + "69809": "", + "69810": "", + "69811": "", + "69812": "", + "69813": "", + "69814": "", + "69815": "", + "69816": "", + "69817": "", + "69818": "", + "69826": "", + "69888": "", + "69889": "", + "69890": "", + "69927": "", + "69928": "", + "69929": "", + "69930": "", + "69931": "", + "69932": "", + "69933": "", + "69934": "", + "69935": "", + "69936": "", + "69937": "", + "69938": "", + "69939": "", + "69940": "", + "69957": "", + "69958": "", + "70003": "", + "70016": "", + "70017": "", + "70018": "", + "70067": "", + "70068": "", + "70069": "", + "70070": "", + "70071": "", + "70072": "", + "70073": "", + "70074": "", + "70075": "", + "70076": "", + "70077": "", + "70078": "", + "70079": "", + "70080": "", + "70089": "", + "70090": "", + "70091": "", + "70092": "", + "70094": "", + "70095": "", + "70188": "", + "70189": "", + "70190": "", + "70191": "", + "70192": "", + "70193": "", + "70194": "", + "70195": "", + "70196": "", + "70197": "", + "70198": "", + "70199": "", + "70206": "", + "70209": "", + "70367": "", + "70368": "", + "70369": "", + "70370": "", + "70371": "", + "70372": "", + "70373": "", + "70374": "", + "70375": "", + "70376": "", + "70377": "", + "70378": "", + "70400": "", + "70401": "", + "70402": "", + "70403": "", + "70459": "", + "70460": "", + "70462": "", + "70463": "", + "70464": "", + "70465": "", + "70466": "", + "70467": "", + "70468": "", + "70471": "", + "70472": "", + "70475": "", + "70476": "", + "70477": "", + "70487": "", + "70498": "", + "70499": "", + "70502": "", + "70503": "", + "70504": "", + "70505": "", + "70506": "", + "70507": "", + "70508": "", + "70512": "", + "70513": "", + "70514": "", + "70515": "", + "70516": "", + "70709": "", + "70710": "", + "70711": "", + "70712": "", + "70713": "", + "70714": "", + "70715": "", + "70716": "", + "70717": "", + "70718": "", + "70719": "", + "70720": "", + "70721": "", + "70722": "", + "70723": "", + "70724": "", + "70725": "", + "70726": "", + "70750": "", + "70832": "", + "70833": "", + "70834": "", + "70835": "", + "70836": "", + "70837": "", + "70838": "", + "70839": "", + "70840": "", + "70841": "", + "70842": "", + "70843": "", + "70844": "", + "70845": "", + "70846": "", + "70847": "", + "70848": "", + "70849": "", + "70850": "", + "70851": "", + "71087": "", + "71088": "", + "71089": "", + "71090": "", + "71091": "", + "71092": "", + "71093": "", + "71096": "", + "71097": "", + "71098": "", + "71099": "", + "71100": "", + "71101": "", + "71102": "", + "71103": "", + "71104": "", + "71132": "", + "71133": "", + "71216": "", + "71217": "", + "71218": "", + "71219": "", + "71220": "", + "71221": "", + "71222": "", + "71223": "", + "71224": "", + "71225": "", + "71226": "", + "71227": "", + "71228": "", + "71229": "", + "71230": "", + "71231": "", + "71232": "", + "71339": "", + "71340": "", + "71341": "", + "71342": "", + "71343": "", + "71344": "", + "71345": "", + "71346": "", + "71347": "", + "71348": "", + "71349": "", + "71350": "", + "71351": "", + "71453": "", + "71454": "", + "71455": "", + "71456": "", + "71457": "", + "71458": "", + "71459": "", + "71460": "", + "71461": "", + "71462": "", + "71463": "", + "71464": "", + "71465": "", + "71466": "", + "71467": "", + "71724": "", + "71725": "", + "71726": "", + "71727": "", + "71728": "", + "71729": "", + "71730": "", + "71731": "", + "71732": "", + "71733": "", + "71734": "", + "71735": "", + "71736": "", + "71737": "", + "71738": "", + "71984": "", + "71985": "", + "71986": "", + "71987": "", + "71988": "", + "71989": "", + "71991": "", + "71992": "", + "71995": "", + "71996": "", + "71997": "", + "71998": "", + "72000": "", + "72002": "", + "72003": "", + "72145": "", + "72146": "", + "72147": "", + "72148": "", + "72149": "", + "72150": "", + "72151": "", + "72154": "", + "72155": "", + "72156": "", + "72157": "", + "72158": "", + "72159": "", + "72160": "", + "72164": "", + "72193": "", + "72194": "", + "72195": "", + "72196": "", + "72197": "", + "72198": "", + "72199": "", + "72200": "", + "72201": "", + "72202": "", + "72243": "", + "72244": "", + "72245": "", + "72246": "", + "72247": "", + "72248": "", + "72249": "", + "72251": "", + "72252": "", + "72253": "", + "72254": "", + "72263": "", + "72273": "", + "72274": "", + "72275": "", + "72276": "", + "72277": "", + "72278": "", + "72279": "", + "72280": "", + "72281": "", + "72282": "", + "72283": "", + "72330": "", + "72331": "", + "72332": "", + "72333": "", + "72334": "", + "72335": "", + "72336": "", + "72337": "", + "72338": "", + "72339": "", + "72340": "", + "72341": "", + "72342": "", + "72343": "", + "72344": "", + "72345": "", + "72751": "", + "72752": "", + "72753": "", + "72754": "", + "72755": "", + "72756": "", + "72757": "", + "72758": "", + "72760": "", + "72761": "", + "72762": "", + "72763": "", + "72764": "", + "72765": "", + "72766": "", + "72767": "", + "72850": "", + "72851": "", + "72852": "", + "72853": "", + "72854": "", + "72855": "", + "72856": "", + "72857": "", + "72858": "", + "72859": "", + "72860": "", + "72861": "", + "72862": "", + "72863": "", + "72864": "", + "72865": "", + "72866": "", + "72867": "", + "72868": "", + "72869": "", + "72870": "", + "72871": "", + "72873": "", + "72874": "", + "72875": "", + "72876": "", + "72877": "", + "72878": "", + "72879": "", + "72880": "", + "72881": "", + "72882": "", + "72883": "", + "72884": "", + "72885": "", + "72886": "", + "73009": "", + "73010": "", + "73011": "", + "73012": "", + "73013": "", + "73014": "", + "73018": "", + "73020": "", + "73021": "", + "73023": "", + "73024": "", + "73025": "", + "73026": "", + "73027": "", + "73028": "", + "73029": "", + "73031": "", + "73098": "", + "73099": "", + "73100": "", + "73101": "", + "73102": "", + "73104": "", + "73105": "", + "73107": "", + "73108": "", + "73109": "", + "73110": "", + "73111": "", + "73459": "", + "73460": "", + "73461": "", + "73462": "", + "73472": "", + "73473": "", + "73475": "", + "73524": "", + "73525": "", + "73526": "", + "73527": "", + "73528": "", + "73529": "", + "73530": "", + "73534": "", + "73535": "", + "73536": "", + "73537": "", + "73538": "", + "78912": "", + "78919": "", + "78920": "", + "78921": "", + "78922": "", + "78923": "", + "78924": "", + "78925": "", + "78926": "", + "78927": "", + "78928": "", + "78929": "", + "78930": "", + "78931": "", + "78932": "", + "78933": "", + "92912": "", + "92913": "", + "92914": "", + "92915": "", + "92916": "", + "92976": "", + "92977": "", + "92978": "", + "92979": "", + "92980": "", + "92981": "", + "92982": "", + "94031": "", + "94033": "", + "94034": "", + "94035": "", + "94036": "", + "94037": "", + "94038": "", + "94039": "", + "94040": "", + "94041": "", + "94042": "", + "94043": "", + "94044": "", + "94045": "", + "94046": "", + "94047": "", + "94048": "", + "94049": "", + "94050": "", + "94051": "", + "94052": "", + "94053": "", + "94054": "", + "94055": "", + "94056": "", + "94057": "", + "94058": "", + "94059": "", + "94060": "", + "94061": "", + "94062": "", + "94063": "", + "94064": "", + "94065": "", + "94066": "", + "94067": "", + "94068": "", + "94069": "", + "94070": "", + "94071": "", + "94072": "", + "94073": "", + "94074": "", + "94075": "", + "94076": "", + "94077": "", + "94078": "", + "94079": "", + "94080": "", + "94081": "", + "94082": "", + "94083": "", + "94084": "", + "94085": "", + "94086": "", + "94087": "", + "94095": "", + "94096": "", + "94097": "", + "94098": "", + "94180": "", + "94192": "", + "94193": "", + "113821": "", + "113822": "", + "118528": "", + "118529": "", + "118530": "", + "118531": "", + "118532": "", + "118533": "", + "118534": "", + "118535": "", + "118536": "", + "118537": "", + "118538": "", + "118539": "", + "118540": "", + "118541": "", + "118542": "", + "118543": "", + "118544": "", + "118545": "", + "118546": "", + "118547": "", + "118548": "", + "118549": "", + "118550": "", + "118551": "", + "118552": "", + "118553": "", + "118554": "", + "118555": "", + "118556": "", + "118557": "", + "118558": "", + "118559": "", + "118560": "", + "118561": "", + "118562": "", + "118563": "", + "118564": "", + "118565": "", + "118566": "", + "118567": "", + "118568": "", + "118569": "", + "118570": "", + "118571": "", + "118572": "", + "118573": "", + "118576": "", + "118577": "", + "118578": "", + "118579": "", + "118580": "", + "118581": "", + "118582": "", + "118583": "", + "118584": "", + "118585": "", + "118586": "", + "118587": "", + "118588": "", + "118589": "", + "118590": "", + "118591": "", + "118592": "", + "118593": "", + "118594": "", + "118595": "", + "118596": "", + "118597": "", + "118598": "", + "119134": "\ud834\udd57", + "119135": "\ud834\udd58", + "119136": "\ud834\udd58", + "119137": "\ud834\udd58", + "119138": "\ud834\udd58", + "119139": "\ud834\udd58", + "119140": "\ud834\udd58", + "119141": "", + "119142": "", + "119143": "", + "119144": "", + "119145": "", + "119149": "", + "119150": "", + "119151": "", + "119152": "", + "119153": "", + "119154": "", + "119163": "", + "119164": "", + "119165": "", + "119166": "", + "119167": "", + "119168": "", + "119169": "", + "119170": "", + "119173": "", + "119174": "", + "119175": "", + "119176": "", + "119177": "", + "119178": "", + "119179": "", + "119210": "", + "119211": "", + "119212": "", + "119213": "", + "119227": "\ud834\uddb9", + "119228": "\ud834\uddba", + "119229": "\ud834\uddb9", + "119230": "\ud834\uddba", + "119231": "\ud834\uddb9", + "119232": "\ud834\uddba", + "119362": "", + "119363": "", + "119364": "", + "121344": "", + "121345": "", + "121346": "", + "121347": "", + "121348": "", + "121349": "", + "121350": "", + "121351": "", + "121352": "", + "121353": "", + "121354": "", + "121355": "", + "121356": "", + "121357": "", + "121358": "", + "121359": "", + "121360": "", + "121361": "", + "121362": "", + "121363": "", + "121364": "", + "121365": "", + "121366": "", + "121367": "", + "121368": "", + "121369": "", + "121370": "", + "121371": "", + "121372": "", + "121373": "", + "121374": "", + "121375": "", + "121376": "", + "121377": "", + "121378": "", + "121379": "", + "121380": "", + "121381": "", + "121382": "", + "121383": "", + "121384": "", + "121385": "", + "121386": "", + "121387": "", + "121388": "", + "121389": "", + "121390": "", + "121391": "", + "121392": "", + "121393": "", + "121394": "", + "121395": "", + "121396": "", + "121397": "", + "121398": "", + "121403": "", + "121404": "", + "121405": "", + "121406": "", + "121407": "", + "121408": "", + "121409": "", + "121410": "", + "121411": "", + "121412": "", + "121413": "", + "121414": "", + "121415": "", + "121416": "", + "121417": "", + "121418": "", + "121419": "", + "121420": "", + "121421": "", + "121422": "", + "121423": "", + "121424": "", + "121425": "", + "121426": "", + "121427": "", + "121428": "", + "121429": "", + "121430": "", + "121431": "", + "121432": "", + "121433": "", + "121434": "", + "121435": "", + "121436": "", + "121437": "", + "121438": "", + "121439": "", + "121440": "", + "121441": "", + "121442": "", + "121443": "", + "121444": "", + "121445": "", + "121446": "", + "121447": "", + "121448": "", + "121449": "", + "121450": "", + "121451": "", + "121452": "", + "121461": "", + "121476": "", + "121499": "", + "121500": "", + "121501": "", + "121502": "", + "121503": "", + "121505": "", + "121506": "", + "121507": "", + "121508": "", + "121509": "", + "121510": "", + "121511": "", + "121512": "", + "121513": "", + "121514": "", + "121515": "", + "121516": "", + "121517": "", + "121518": "", + "121519": "", + "122880": "", + "122881": "", + "122882": "", + "122883": "", + "122884": "", + "122885": "", + "122886": "", + "122888": "", + "122889": "", + "122890": "", + "122891": "", + "122892": "", + "122893": "", + "122894": "", + "122895": "", + "122896": "", + "122897": "", + "122898": "", + "122899": "", + "122900": "", + "122901": "", + "122902": "", + "122903": "", + "122904": "", + "122907": "", + "122908": "", + "122909": "", + "122910": "", + "122911": "", + "122912": "", + "122913": "", + "122915": "", + "122916": "", + "122918": "", + "122919": "", + "122920": "", + "122921": "", + "122922": "", + "123023": "", + "123184": "", + "123185": "", + "123186": "", + "123187": "", + "123188": "", + "123189": "", + "123190": "", + "123566": "", + "123628": "", + "123629": "", + "123630": "", + "123631": "", + "124140": "", + "124141": "", + "124142": "", + "124143": "", + "125136": "", + "125137": "", + "125138": "", + "125139": "", + "125140": "", + "125141": "", + "125142": "", + "125252": "", + "125253": "", + "125254": "", + "125255": "", + "125256": "", + "125257": "", + "125258": "", + "194560": "\u4e3d", + "194561": "\u4e38", + "194562": "\u4e41", + "194563": "\ud840\udd22", + "194564": "\u4f60", + "194565": "\u4fae", + "194566": "\u4fbb", + "194567": "\u5002", + "194568": "\u507a", + "194569": "\u5099", + "194570": "\u50e7", + "194571": "\u50cf", + "194572": "\u349e", + "194573": "\ud841\ude3a", + "194574": "\u514d", + "194575": "\u5154", + "194576": "\u5164", + "194577": "\u5177", + "194578": "\ud841\udd1c", + "194579": "\u34b9", + "194580": "\u5167", + "194581": "\u518d", + "194582": "\ud841\udd4b", + "194583": "\u5197", + "194584": "\u51a4", + "194585": "\u4ecc", + "194586": "\u51ac", + "194587": "\u51b5", + "194588": "\ud864\udddf", + "194589": "\u51f5", + "194590": "\u5203", + "194591": "\u34df", + "194592": "\u523b", + "194593": "\u5246", + "194594": "\u5272", + "194595": "\u5277", + "194596": "\u3515", + "194597": "\u52c7", + "194598": "\u52c9", + "194599": "\u52e4", + "194600": "\u52fa", + "194601": "\u5305", + "194602": "\u5306", + "194603": "\u5317", + "194604": "\u5349", + "194605": "\u5351", + "194606": "\u535a", + "194607": "\u5373", + "194608": "\u537d", + "194609": "\u537f", + "194610": "\u537f", + "194611": "\u537f", + "194612": "\ud842\ude2c", + "194613": "\u7070", + "194614": "\u53ca", + "194615": "\u53df", + "194616": "\ud842\udf63", + "194617": "\u53eb", + "194618": "\u53f1", + "194619": "\u5406", + "194620": "\u549e", + "194621": "\u5438", + "194622": "\u5448", + "194623": "\u5468", + "194624": "\u54a2", + "194625": "\u54f6", + "194626": "\u5510", + "194627": "\u5553", + "194628": "\u5563", + "194629": "\u5584", + "194630": "\u5584", + "194631": "\u5599", + "194632": "\u55ab", + "194633": "\u55b3", + "194634": "\u55c2", + "194635": "\u5716", + "194636": "\u5606", + "194637": "\u5717", + "194638": "\u5651", + "194639": "\u5674", + "194640": "\u5207", + "194641": "\u58ee", + "194642": "\u57ce", + "194643": "\u57f4", + "194644": "\u580d", + "194645": "\u578b", + "194646": "\u5832", + "194647": "\u5831", + "194648": "\u58ac", + "194649": "\ud845\udce4", + "194650": "\u58f2", + "194651": "\u58f7", + "194652": "\u5906", + "194653": "\u591a", + "194654": "\u5922", + "194655": "\u5962", + "194656": "\ud845\udea8", + "194657": "\ud845\udeea", + "194658": "\u59ec", + "194659": "\u5a1b", + "194660": "\u5a27", + "194661": "\u59d8", + "194662": "\u5a66", + "194663": "\u36ee", + "194664": "\u36fc", + "194665": "\u5b08", + "194666": "\u5b3e", + "194667": "\u5b3e", + "194668": "\ud846\uddc8", + "194669": "\u5bc3", + "194670": "\u5bd8", + "194671": "\u5be7", + "194672": "\u5bf3", + "194673": "\ud846\udf18", + "194674": "\u5bff", + "194675": "\u5c06", + "194676": "\u5f53", + "194677": "\u5c22", + "194678": "\u3781", + "194679": "\u5c60", + "194680": "\u5c6e", + "194681": "\u5cc0", + "194682": "\u5c8d", + "194683": "\ud847\udde4", + "194684": "\u5d43", + "194685": "\ud847\udde6", + "194686": "\u5d6e", + "194687": "\u5d6b", + "194688": "\u5d7c", + "194689": "\u5de1", + "194690": "\u5de2", + "194691": "\u382f", + "194692": "\u5dfd", + "194693": "\u5e28", + "194694": "\u5e3d", + "194695": "\u5e69", + "194696": "\u3862", + "194697": "\ud848\udd83", + "194698": "\u387c", + "194699": "\u5eb0", + "194700": "\u5eb3", + "194701": "\u5eb6", + "194702": "\u5eca", + "194703": "\ud868\udf92", + "194704": "\u5efe", + "194705": "\ud848\udf31", + "194706": "\ud848\udf31", + "194707": "\u8201", + "194708": "\u5f22", + "194709": "\u5f22", + "194710": "\u38c7", + "194711": "\ud84c\udeb8", + "194712": "\ud858\uddda", + "194713": "\u5f62", + "194714": "\u5f6b", + "194715": "\u38e3", + "194716": "\u5f9a", + "194717": "\u5fcd", + "194718": "\u5fd7", + "194719": "\u5ff9", + "194720": "\u6081", + "194721": "\u393a", + "194722": "\u391c", + "194723": "\u6094", + "194724": "\ud849\uded4", + "194725": "\u60c7", + "194726": "\u6148", + "194727": "\u614c", + "194728": "\u614e", + "194729": "\u614c", + "194730": "\u617a", + "194731": "\u618e", + "194732": "\u61b2", + "194733": "\u61a4", + "194734": "\u61af", + "194735": "\u61de", + "194736": "\u61f2", + "194737": "\u61f6", + "194738": "\u6210", + "194739": "\u621b", + "194740": "\u625d", + "194741": "\u62b1", + "194742": "\u62d4", + "194743": "\u6350", + "194744": "\ud84a\udf0c", + "194745": "\u633d", + "194746": "\u62fc", + "194747": "\u6368", + "194748": "\u6383", + "194749": "\u63e4", + "194750": "\ud84a\udff1", + "194751": "\u6422", + "194752": "\u63c5", + "194753": "\u63a9", + "194754": "\u3a2e", + "194755": "\u6469", + "194756": "\u647e", + "194757": "\u649d", + "194758": "\u6477", + "194759": "\u3a6c", + "194760": "\u654f", + "194761": "\u656c", + "194762": "\ud84c\udc0a", + "194763": "\u65e3", + "194764": "\u66f8", + "194765": "\u6649", + "194766": "\u3b19", + "194767": "\u6691", + "194768": "\u3b08", + "194769": "\u3ae4", + "194770": "\u5192", + "194771": "\u5195", + "194772": "\u6700", + "194773": "\u669c", + "194774": "\u80ad", + "194775": "\u43d9", + "194776": "\u6717", + "194777": "\u671b", + "194778": "\u6721", + "194779": "\u675e", + "194780": "\u6753", + "194781": "\ud84c\udfc3", + "194782": "\u3b49", + "194783": "\u67fa", + "194784": "\u6785", + "194785": "\u6852", + "194786": "\u6885", + "194787": "\ud84d\udc6d", + "194788": "\u688e", + "194789": "\u681f", + "194790": "\u6914", + "194791": "\u3b9d", + "194792": "\u6942", + "194793": "\u69a3", + "194794": "\u69ea", + "194795": "\u6aa8", + "194796": "\ud84d\udea3", + "194797": "\u6adb", + "194798": "\u3c18", + "194799": "\u6b21", + "194800": "\ud84e\udca7", + "194801": "\u6b54", + "194802": "\u3c4e", + "194803": "\u6b72", + "194804": "\u6b9f", + "194805": "\u6bba", + "194806": "\u6bbb", + "194807": "\ud84e\ude8d", + "194808": "\ud847\udd0b", + "194809": "\ud84e\udefa", + "194810": "\u6c4e", + "194811": "\ud84f\udcbc", + "194812": "\u6cbf", + "194813": "\u6ccd", + "194814": "\u6c67", + "194815": "\u6d16", + "194816": "\u6d3e", + "194817": "\u6d77", + "194818": "\u6d41", + "194819": "\u6d69", + "194820": "\u6d78", + "194821": "\u6d85", + "194822": "\ud84f\udd1e", + "194823": "\u6d34", + "194824": "\u6e2f", + "194825": "\u6e6e", + "194826": "\u3d33", + "194827": "\u6ecb", + "194828": "\u6ec7", + "194829": "\ud84f\uded1", + "194830": "\u6df9", + "194831": "\u6f6e", + "194832": "\ud84f\udf5e", + "194833": "\ud84f\udf8e", + "194834": "\u6fc6", + "194835": "\u7039", + "194836": "\u701e", + "194837": "\u701b", + "194838": "\u3d96", + "194839": "\u704a", + "194840": "\u707d", + "194841": "\u7077", + "194842": "\u70ad", + "194843": "\ud841\udd25", + "194844": "\u7145", + "194845": "\ud850\ude63", + "194846": "\u719c", + "194847": "\ud850\udfab", + "194848": "\u7228", + "194849": "\u7235", + "194850": "\u7250", + "194851": "\ud851\ude08", + "194852": "\u7280", + "194853": "\u7295", + "194854": "\ud851\udf35", + "194855": "\ud852\udc14", + "194856": "\u737a", + "194857": "\u738b", + "194858": "\u3eac", + "194859": "\u73a5", + "194860": "\u3eb8", + "194861": "\u3eb8", + "194862": "\u7447", + "194863": "\u745c", + "194864": "\u7471", + "194865": "\u7485", + "194866": "\u74ca", + "194867": "\u3f1b", + "194868": "\u7524", + "194869": "\ud853\udc36", + "194870": "\u753e", + "194871": "\ud853\udc92", + "194872": "\u7570", + "194873": "\ud848\udd9f", + "194874": "\u7610", + "194875": "\ud853\udfa1", + "194876": "\ud853\udfb8", + "194877": "\ud854\udc44", + "194878": "\u3ffc", + "194879": "\u4008", + "194880": "\u76f4", + "194881": "\ud854\udcf3", + "194882": "\ud854\udcf2", + "194883": "\ud854\udd19", + "194884": "\ud854\udd33", + "194885": "\u771e", + "194886": "\u771f", + "194887": "\u771f", + "194888": "\u774a", + "194889": "\u4039", + "194890": "\u778b", + "194891": "\u4046", + "194892": "\u4096", + "194893": "\ud855\udc1d", + "194894": "\u784e", + "194895": "\u788c", + "194896": "\u78cc", + "194897": "\u40e3", + "194898": "\ud855\ude26", + "194899": "\u7956", + "194900": "\ud855\ude9a", + "194901": "\ud855\udec5", + "194902": "\u798f", + "194903": "\u79eb", + "194904": "\u412f", + "194905": "\u7a40", + "194906": "\u7a4a", + "194907": "\u7a4f", + "194908": "\ud856\udd7c", + "194909": "\ud856\udea7", + "194910": "\ud856\udea7", + "194911": "\u7aee", + "194912": "\u4202", + "194913": "\ud856\udfab", + "194914": "\u7bc6", + "194915": "\u7bc9", + "194916": "\u4227", + "194917": "\ud857\udc80", + "194918": "\u7cd2", + "194919": "\u42a0", + "194920": "\u7ce8", + "194921": "\u7ce3", + "194922": "\u7d00", + "194923": "\ud857\udf86", + "194924": "\u7d63", + "194925": "\u4301", + "194926": "\u7dc7", + "194927": "\u7e02", + "194928": "\u7e45", + "194929": "\u4334", + "194930": "\ud858\ude28", + "194931": "\ud858\ude47", + "194932": "\u4359", + "194933": "\ud858\uded9", + "194934": "\u7f7a", + "194935": "\ud858\udf3e", + "194936": "\u7f95", + "194937": "\u7ffa", + "194938": "\u8005", + "194939": "\ud859\udcda", + "194940": "\ud859\udd23", + "194941": "\u8060", + "194942": "\ud859\udda8", + "194943": "\u8070", + "194944": "\ud84c\udf5f", + "194945": "\u43d5", + "194946": "\u80b2", + "194947": "\u8103", + "194948": "\u440b", + "194949": "\u813e", + "194950": "\u5ab5", + "194951": "\ud859\udfa7", + "194952": "\ud859\udfb5", + "194953": "\ud84c\udf93", + "194954": "\ud84c\udf9c", + "194955": "\u8201", + "194956": "\u8204", + "194957": "\u8f9e", + "194958": "\u446b", + "194959": "\u8291", + "194960": "\u828b", + "194961": "\u829d", + "194962": "\u52b3", + "194963": "\u82b1", + "194964": "\u82b3", + "194965": "\u82bd", + "194966": "\u82e6", + "194967": "\ud85a\udf3c", + "194968": "\u82e5", + "194969": "\u831d", + "194970": "\u8363", + "194971": "\u83ad", + "194972": "\u8323", + "194973": "\u83bd", + "194974": "\u83e7", + "194975": "\u8457", + "194976": "\u8353", + "194977": "\u83ca", + "194978": "\u83cc", + "194979": "\u83dc", + "194980": "\ud85b\udc36", + "194981": "\ud85b\udd6b", + "194982": "\ud85b\udcd5", + "194983": "\u452b", + "194984": "\u84f1", + "194985": "\u84f3", + "194986": "\u8516", + "194987": "\ud85c\udfca", + "194988": "\u8564", + "194989": "\ud85b\udf2c", + "194990": "\u455d", + "194991": "\u4561", + "194992": "\ud85b\udfb1", + "194993": "\ud85c\udcd2", + "194994": "\u456b", + "194995": "\u8650", + "194996": "\u865c", + "194997": "\u8667", + "194998": "\u8669", + "194999": "\u86a9", + "195000": "\u8688", + "195001": "\u870e", + "195002": "\u86e2", + "195003": "\u8779", + "195004": "\u8728", + "195005": "\u876b", + "195006": "\u8786", + "195007": "\u45d7", + "195008": "\u87e1", + "195009": "\u8801", + "195010": "\u45f9", + "195011": "\u8860", + "195012": "\u8863", + "195013": "\ud85d\ude67", + "195014": "\u88d7", + "195015": "\u88de", + "195016": "\u4635", + "195017": "\u88fa", + "195018": "\u34bb", + "195019": "\ud85e\udcae", + "195020": "\ud85e\udd66", + "195021": "\u46be", + "195022": "\u46c7", + "195023": "\u8aa0", + "195024": "\u8aed", + "195025": "\u8b8a", + "195026": "\u8c55", + "195027": "\ud85f\udca8", + "195028": "\u8cab", + "195029": "\u8cc1", + "195030": "\u8d1b", + "195031": "\u8d77", + "195032": "\ud85f\udf2f", + "195033": "\ud842\udc04", + "195034": "\u8dcb", + "195035": "\u8dbc", + "195036": "\u8df0", + "195037": "\ud842\udcde", + "195038": "\u8ed4", + "195039": "\u8f38", + "195040": "\ud861\uddd2", + "195041": "\ud861\udded", + "195042": "\u9094", + "195043": "\u90f1", + "195044": "\u9111", + "195045": "\ud861\udf2e", + "195046": "\u911b", + "195047": "\u9238", + "195048": "\u92d7", + "195049": "\u92d8", + "195050": "\u927c", + "195051": "\u93f9", + "195052": "\u9415", + "195053": "\ud862\udffa", + "195054": "\u958b", + "195055": "\u4995", + "195056": "\u95b7", + "195057": "\ud863\udd77", + "195058": "\u49e6", + "195059": "\u96c3", + "195060": "\u5db2", + "195061": "\u9723", + "195062": "\ud864\udd45", + "195063": "\ud864\ude1a", + "195064": "\u4a6e", + "195065": "\u4a76", + "195066": "\u97e0", + "195067": "\ud865\udc0a", + "195068": "\u4ab2", + "195069": "\ud865\udc96", + "195070": "\u980b", + "195071": "\u980b", + "195072": "\u9829", + "195073": "\ud865\uddb6", + "195074": "\u98e2", + "195075": "\u4b33", + "195076": "\u9929", + "195077": "\u99a7", + "195078": "\u99c2", + "195079": "\u99fe", + "195080": "\u4bce", + "195081": "\ud866\udf30", + "195082": "\u9b12", + "195083": "\u9c40", + "195084": "\u9cfd", + "195085": "\u4cce", + "195086": "\u4ced", + "195087": "\u9d67", + "195088": "\ud868\udcce", + "195089": "\u4cf8", + "195090": "\ud868\udd05", + "195091": "\ud868\ude0e", + "195092": "\ud868\ude91", + "195093": "\u9ebb", + "195094": "\u4d56", + "195095": "\u9ef9", + "195096": "\u9efe", + "195097": "\u9f05", + "195098": "\u9f0f", + "195099": "\u9f16", + "195100": "\u9f3b", + "195101": "\ud869\ude00", + "917760": "", + "917761": "", + "917762": "", + "917763": "", + "917764": "", + "917765": "", + "917766": "", + "917767": "", + "917768": "", + "917769": "", + "917770": "", + "917771": "", + "917772": "", + "917773": "", + "917774": "", + "917775": "", + "917776": "", + "917777": "", + "917778": "", + "917779": "", + "917780": "", + "917781": "", + "917782": "", + "917783": "", + "917784": "", + "917785": "", + "917786": "", + "917787": "", + "917788": "", + "917789": "", + "917790": "", + "917791": "", + "917792": "", + "917793": "", + "917794": "", + "917795": "", + "917796": "", + "917797": "", + "917798": "", + "917799": "", + "917800": "", + "917801": "", + "917802": "", + "917803": "", + "917804": "", + "917805": "", + "917806": "", + "917807": "", + "917808": "", + "917809": "", + "917810": "", + "917811": "", + "917812": "", + "917813": "", + "917814": "", + "917815": "", + "917816": "", + "917817": "", + "917818": "", + "917819": "", + "917820": "", + "917821": "", + "917822": "", + "917823": "", + "917824": "", + "917825": "", + "917826": "", + "917827": "", + "917828": "", + "917829": "", + "917830": "", + "917831": "", + "917832": "", + "917833": "", + "917834": "", + "917835": "", + "917836": "", + "917837": "", + "917838": "", + "917839": "", + "917840": "", + "917841": "", + "917842": "", + "917843": "", + "917844": "", + "917845": "", + "917846": "", + "917847": "", + "917848": "", + "917849": "", + "917850": "", + "917851": "", + "917852": "", + "917853": "", + "917854": "", + "917855": "", + "917856": "", + "917857": "", + "917858": "", + "917859": "", + "917860": "", + "917861": "", + "917862": "", + "917863": "", + "917864": "", + "917865": "", + "917866": "", + "917867": "", + "917868": "", + "917869": "", + "917870": "", + "917871": "", + "917872": "", + "917873": "", + "917874": "", + "917875": "", + "917876": "", + "917877": "", + "917878": "", + "917879": "", + "917880": "", + "917881": "", + "917882": "", + "917883": "", + "917884": "", + "917885": "", + "917886": "", + "917887": "", + "917888": "", + "917889": "", + "917890": "", + "917891": "", + "917892": "", + "917893": "", + "917894": "", + "917895": "", + "917896": "", + "917897": "", + "917898": "", + "917899": "", + "917900": "", + "917901": "", + "917902": "", + "917903": "", + "917904": "", + "917905": "", + "917906": "", + "917907": "", + "917908": "", + "917909": "", + "917910": "", + "917911": "", + "917912": "", + "917913": "", + "917914": "", + "917915": "", + "917916": "", + "917917": "", + "917918": "", + "917919": "", + "917920": "", + "917921": "", + "917922": "", + "917923": "", + "917924": "", + "917925": "", + "917926": "", + "917927": "", + "917928": "", + "917929": "", + "917930": "", + "917931": "", + "917932": "", + "917933": "", + "917934": "", + "917935": "", + "917936": "", + "917937": "", + "917938": "", + "917939": "", + "917940": "", + "917941": "", + "917942": "", + "917943": "", + "917944": "", + "917945": "", + "917946": "", + "917947": "", + "917948": "", + "917949": "", + "917950": "", + "917951": "", + "917952": "", + "917953": "", + "917954": "", + "917955": "", + "917956": "", + "917957": "", + "917958": "", + "917959": "", + "917960": "", + "917961": "", + "917962": "", + "917963": "", + "917964": "", + "917965": "", + "917966": "", + "917967": "", + "917968": "", + "917969": "", + "917970": "", + "917971": "", + "917972": "", + "917973": "", + "917974": "", + "917975": "", + "917976": "", + "917977": "", + "917978": "", + "917979": "", + "917980": "", + "917981": "", + "917982": "", + "917983": "", + "917984": "", + "917985": "", + "917986": "", + "917987": "", + "917988": "", + "917989": "", + "917990": "", + "917991": "", + "917992": "", + "917993": "", + "917994": "", + "917995": "", + "917996": "", + "917997": "", + "917998": "", + "917999": "" + } +} \ No newline at end of file diff --git a/docs/superpowers/specs/pins-wave3/varchar_subscripts.json b/docs/superpowers/specs/pins-wave3/varchar_subscripts.json new file mode 100644 index 0000000..dc18551 --- /dev/null +++ b/docs/superpowers/specs/pins-wave3/varchar_subscripts.json @@ -0,0 +1,310 @@ +{ + "family": "varchar_subscripts", + "duckdb_version": "1.5.5", + "decisions": [ + "duckdb 1.5.5; all pins via vectorized column path (CREATE TABLE + SELECT fn(col) FROM t); literal path spot-checked identical on every spot check.", + "UNIT = Unicode CODEPOINTS for both array_extract and array_slice on VARCHAR. Witnesses: '\u00e9a' extract(1)='\u00e9' (not a byte); 'e'+U+0301 splits: extract(1)='e', extract(2)=U+0301 alone (NOT graphemes); '\ud83d\ude00a' extract(1)='\ud83d\ude00', extract(2)='a' (not UTF-16); ZWJ family emoji has length()=7 and extract(1)='\ud83d\udc68', extract(2)=ZWJ, extract(7)='\ud83d\udc66', extract(8)=''.", + "array_extract(str,i): 1-based. i=0 -> '' (empty string). Negative i = from end: -1 last char, -len first char ('hello': -5->'h', -6->''). Out-of-range in EITHER direction -> '' (empty string, NOT NULL). NULL str or NULL i -> NULL. Empty-string input -> '' for every i. Result type VARCHAR.", + "CONTRAST: array_extract on LIST out-of-range returns NULL (l=[10,20,30]: l[5] is NULL, l[0] is NULL); on VARCHAR out-of-range returns ''. Do not copy list semantics to strings.", + "s[i] bracket on a VARCHAR column IS NOT DISTINCT FROM array_extract(s,i) on all 18 edge rows and a 220-row random sweep (count of mismatches = 0). list_extract(s,2) also works on VARCHAR and returned 'e'.", + "array_slice(str,a,b): 1-based, INCLUSIVE on BOTH ends: (2,4)->'ell', (2,2)->'e', (5,5)->'o'. Negative = from end with -1 = last char: (-3,-1)->'llo', (1,-1)->'hello', (2,-2)->'ell'. begin<=0 clamps to start: (0,3)->'hel', (-10,10)->'hello', (-5,2)->'he'. end>len clamps to len: (4,100)->'lo'. Entirely out of range either side -> '': (100,200)->'', (-100,-50)->'', (6,6)->''. a>b after resolution -> '': (3,2)->'', (1,0)->''. Result type VARCHAR.", + "NULL is NOT an open bound: array_slice with NULL a, NULL b, or both -> NULL; NULL string -> NULL. (Refutes Python-style None-as-open assumption.)", + "list_slice(s,a,b) IS NOT DISTINCT FROM array_slice(s,a,b) on all 24 edge rows AND 220 random (s,a,b) incl NULL bounds and unicode: 0 mismatches. Same for bracket s[a:b] with column bounds: 0 mismatches. All three are one function.", + "Open bracket forms (column path): s[:3]='hel' == array_slice(s,1,3); s[2:]='ello' == array_slice(s,2,-1); s[:]='hello' == array_slice(s,1,-1) (IS NOT DISTINCT FROM probes all true); s[:-2]='hell'; s[-2:]='lo'; s[0:3]='hel'; s[0:0]=''.", + "Step slicing is NOT supported on VARCHAR: s[1:5:2], s[1:5:1], s[5:1:-1], list_slice(s,1,5,2), array_slice(s,1,5,2) ALL raise the same Not implemented Error (first line: 'Not implemented Error: Slice with steps has not been implemented for string types, you can consider rewriting your query as follows:'). Even step=1 errors.", + "Embedded NUL: 'ab\\x00cd' inserts fine via parameter; length()=5 counts it; array_extract(s,3)='\\x00'; array_slice(s,2,4)='b\\x00c'; bracket forms identical.", + "substr differs: substr(s,2,3)='ell' (start,LENGTH) vs array_slice(s,2,3)='el' (begin,end inclusive) on 'hello'.", + "Unicode slices confirm codepoint-inclusive bounds: ('\u00e9ab',1,2)->'\u00e9a'; ('e'+U+0301+'ab',1,1)->'e' (strips combining mark); (2,3)->U+0301+'a'; (family,1,3)->'\ud83d\udc68'+ZWJ+'\ud83d\udc69'; ('\ud83d\ude00ab',1,2)->'\ud83d\ude00a'; ('\u00e9ab',-2,-1)->'ab'.", + "List overload exists and is a different function surface: array_extract(INTEGER[] col, 2) -> 20 with typeof INTEGER (VARCHAR path always returns VARCHAR)." + ], + "probes": [ + { + "sql": "SELECT i, array_extract(s, i) FROM hi ORDER BY id", + "input_repr": "[(1, 'hello', -7), (2, 'hello', -6), (3, 'hello', -5), (4, 'hello', -3), (5, 'hello', -1), (6, 'hello', 0), (7, 'hello', 1), (8, 'hello', 2), (9, 'hello', 3), (10, 'hello', 5), (11, 'hello', 6), (12, 'hello', 7), (13, 'hello', None), (14, None, 2), (15, None, None), (16, '', 1), (17, '', -1), (18, '', 0)]", + "result_repr": "[(-7, ''), (-6, ''), (-5, 'h'), (-3, 'l'), (-1, 'o'), (0, ''), (1, 'h'), (2, 'e'), (3, 'l'), (5, 'o'), (6, ''), (7, ''), (None, None), (2, None), (None, None), (1, ''), (-1, ''), (0, '')]", + "result_type": null, + "bits": null, + "error": null, + "note": "rows: s='hello' ids1-13, NULL s ids14-15, '' ids16-18" + }, + { + "sql": "SELECT typeof(array_extract(s, 2)), typeof(s[2]) FROM strs", + "input_repr": null, + "result_repr": "[('VARCHAR', 'VARCHAR')]", + "result_type": "[('VARCHAR', 'VARCHAR')]", + "bits": null, + "error": null, + "note": null + }, + { + "sql": "SELECT i, s[i], s[i] IS NOT DISTINCT FROM array_extract(s, i) FROM hi ORDER BY id", + "input_repr": null, + "result_repr": "[(-7, '', True), (-6, '', True), (-5, 'h', True), (-3, 'l', True), (-1, 'o', True), (0, '', True), (1, 'h', True), (2, 'e', True), (3, 'l', True), (5, 'o', True), (6, '', True), (7, '', True), (None, None, True), (2, None, True), (None, None, True), (1, '', True), (-1, '', True), (0, '', True)]", + "result_type": null, + "bits": null, + "error": null, + "note": "bracket single with column index vs array_extract" + }, + { + "sql": "SELECT s[2], s[-1], s[0], s[9] FROM strs", + "input_repr": null, + "result_repr": "[('e', 'o', '', '')]", + "result_type": null, + "bits": null, + "error": null, + "note": "bracket single, literal indexes, column string" + }, + { + "sql": "SELECT list_extract(s, 2) FROM strs", + "input_repr": null, + "result_repr": "[('e',)]", + "result_type": null, + "bits": null, + "error": null, + "note": "list_extract alias on VARCHAR?" + }, + { + "sql": "SELECT id, length(s), i, array_extract(s, i) FROM uni ORDER BY id", + "input_repr": "[(1, '\u00e9a', 1), (2, '\u00e9a', 2), (3, '\u00e9a', 3), (4, 'e\u0301a', 1), (5, 'e\u0301a', 2), (6, 'e\u0301a', 3), (7, '\ud83d\ude00a', 1), (8, '\ud83d\ude00a', 2), (9, '\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66', 1), (10, '\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66', 2), (11, '\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66', 7), (12, '\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66', 8), (13, '\u0130\u212aa', 1), (14, '\u0130\u212aa', 2)]", + "result_repr": "[(1, 2, 1, '\u00e9'), (2, 2, 2, 'a'), (3, 2, 3, ''), (4, 3, 1, 'e'), (5, 3, 2, '\u0301'), (6, 3, 3, 'a'), (7, 2, 1, '\ud83d\ude00'), (8, 2, 2, 'a'), (9, 7, 1, '\ud83d\udc68'), (10, 7, 2, '\\u200d'), (11, 7, 7, '\ud83d\udc66'), (12, 7, 8, ''), (13, 3, 1, '\u0130'), (14, 3, 2, '\u212a')]", + "result_type": null, + "bits": null, + "error": null, + "note": "witnesses: e-acute(U+00E9), e+combining U+0301, U+1F600 astral, ZWJ family(7cp), U+0130, U+212A" + }, + { + "sql": "SELECT id, array_extract(s, i) IS NOT DISTINCT FROM s[i] FROM uni ORDER BY id", + "input_repr": null, + "result_repr": "[(1, True), (2, True), (3, True), (4, True), (5, True), (6, True), (7, True), (8, True), (9, True), (10, True), (11, True), (12, True), (13, True), (14, True)]", + "result_type": null, + "bits": null, + "error": null, + "note": null + }, + { + "sql": "INSERT INTO nul VALUES (1, ?) -- param 'ab\\x00cd'", + "input_repr": null, + "result_repr": null, + "result_type": null, + "bits": null, + "error": null, + "note": "embedded NUL insert via param: succeeded" + }, + { + "sql": "SELECT length(s), array_extract(s, 3), array_slice(s, 2, 4), s[3], s[2:4] FROM nul", + "input_repr": "'ab\\x00cd'", + "result_repr": "[(5, '\\x00', 'b\\x00c', '\\x00', 'b\\x00c')]", + "result_type": null, + "bits": null, + "error": null, + "note": "embedded NUL subscripts" + }, + { + "sql": "SELECT id, a, b, array_slice(s, a, b) FROM sl ORDER BY id", + "input_repr": "[(1, 'hello', 2, 4), (2, 'hello', 1, 5), (3, 'hello', 0, 3), (4, 'hello', 2, 2), (5, 'hello', 3, 2), (6, 'hello', -3, -1), (7, 'hello', -1, -1), (8, 'hello', 1, -1), (9, 'hello', -10, 10), (10, 'hello', 4, 100), (11, 'hello', 100, 200), (12, 'hello', -100, -50), (13, 'hello', None, 3), (14, 'hello', 2, None), (15, 'hello', None, None), (16, None, 2, 4), (17, 'hello', 0, 0), (18, 'hello', 1, 0), (19, 'hello', -5, 2), (20, '', 1, 3), (21, 'hello', 2, -2), (22, 'hello', -2, 5), (23, 'hello', 5, 5), (24, 'hello', 6, 6)]", + "result_repr": "[(1, 2, 4, 'ell'), (2, 1, 5, 'hello'), (3, 0, 3, 'hel'), (4, 2, 2, 'e'), (5, 3, 2, ''), (6, -3, -1, 'llo'), (7, -1, -1, 'o'), (8, 1, -1, 'hello'), (9, -10, 10, 'hello'), (10, 4, 100, 'lo'), (11, 100, 200, ''), (12, -100, -50, ''), (13, None, 3, None), (14, 2, None, None), (15, None, None, None), (16, 2, 4, None), (17, 0, 0, ''), (18, 1, 0, ''), (19, -5, 2, 'he'), (20, 1, 3, ''), (21, 2, -2, 'ell'), (22, -2, 5, 'lo'), (23, 5, 5, 'o'), (24, 6, 6, '')]", + "result_type": null, + "bits": null, + "error": null, + "note": null + }, + { + "sql": "SELECT typeof(array_slice(s, a, b)) FROM sl WHERE id=1", + "input_repr": null, + "result_repr": "[('VARCHAR',)]", + "result_type": "[('VARCHAR',)]", + "bits": null, + "error": null, + "note": null + }, + { + "sql": "SELECT id, array_slice(s, a, b) IS NOT DISTINCT FROM list_slice(s, a, b) FROM sl ORDER BY id", + "input_repr": null, + "result_repr": "[(1, True), (2, True), (3, True), (4, True), (5, True), (6, True), (7, True), (8, True), (9, True), (10, True), (11, True), (12, True), (13, True), (14, True), (15, True), (16, True), (17, True), (18, True), (19, True), (20, True), (21, True), (22, True), (23, True), (24, True)]", + "result_type": null, + "bits": null, + "error": null, + "note": "list_slice alias on all edge rows" + }, + { + "sql": "SELECT id, array_slice(s, a, b) IS NOT DISTINCT FROM s[a:b] FROM sl ORDER BY id", + "input_repr": null, + "result_repr": "[(1, True), (2, True), (3, True), (4, True), (5, True), (6, True), (7, True), (8, True), (9, True), (10, True), (11, True), (12, True), (13, True), (14, True), (15, True), (16, True), (17, True), (18, True), (19, True), (20, True), (21, True), (22, True), (23, True), (24, True)]", + "result_type": null, + "bits": null, + "error": null, + "note": "bracket slice with column bounds vs array_slice" + }, + { + "sql": "SELECT s[2:4], s[:3], s[2:], s[:], s[-3:-1], s[:-2], s[-2:], s[0:3], s[0:0] FROM strs", + "input_repr": null, + "result_repr": "[('ell', 'hel', 'ello', 'hello', 'llo', 'hell', 'lo', 'hel', '')]", + "result_type": null, + "bits": null, + "error": null, + "note": "open bracket forms on 'hello' column" + }, + { + "sql": "SELECT s[:3] IS NOT DISTINCT FROM array_slice(s, 1, 3), s[2:] IS NOT DISTINCT FROM array_slice(s, 2, -1), s[:] IS NOT DISTINCT FROM array_slice(s, 1, -1) FROM strs", + "input_repr": null, + "result_repr": "[(True, True, True)]", + "result_type": null, + "bits": null, + "error": null, + "note": "candidate desugar equivalences" + }, + { + "sql": "SELECT s[1:5:2] FROM strs", + "input_repr": null, + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Not implemented Error: Slice with steps has not been implemented for string types, you can consider rewriting your query as follows:\n SELECT array_to_string((str_split(string, '')[begin:end:step], '');", + "note": "step bracket on VARCHAR" + }, + { + "sql": "SELECT s[1:5:1] FROM strs", + "input_repr": null, + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Not implemented Error: Slice with steps has not been implemented for string types, you can consider rewriting your query as follows:\n SELECT array_to_string((str_split(string, '')[begin:end:step], '');", + "note": "step=1 bracket on VARCHAR" + }, + { + "sql": "SELECT s[5:1:-1] FROM strs", + "input_repr": null, + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Not implemented Error: Slice with steps has not been implemented for string types, you can consider rewriting your query as follows:\n SELECT array_to_string((str_split(string, '')[begin:end:step], '');", + "note": "negative step bracket on VARCHAR" + }, + { + "sql": "SELECT s[:':2'] FROM strs", + "input_repr": null, + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Conversion Error: Could not convert string ':2' to INT64\n\nLINE 1: SELECT s[:':2'] FROM strs\n ^", + "note": "nonsense guard, ignore" + }, + { + "sql": "SELECT list_slice(s, 1, 5, 2) FROM strs", + "input_repr": null, + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Not implemented Error: Slice with steps has not been implemented for string types, you can consider rewriting your query as follows:\n SELECT array_to_string((str_split(string, '')[begin:end:step], '');", + "note": "4-arg list_slice on VARCHAR" + }, + { + "sql": "SELECT array_slice(s, 1, 5, 2) FROM strs", + "input_repr": null, + "result_repr": null, + "result_type": null, + "bits": null, + "error": "Not implemented Error: Slice with steps has not been implemented for string types, you can consider rewriting your query as follows:\n SELECT array_to_string((str_split(string, '')[begin:end:step], '');", + "note": "4-arg array_slice on VARCHAR" + }, + { + "sql": "SELECT id, array_slice(s, a, b) FROM usl ORDER BY id", + "input_repr": "[(1, '\u00e9ab', 1, 2), (2, 'e\u0301ab', 1, 1), (3, 'e\u0301ab', 2, 3), (4, '\ud83d\udc68\\u200d\ud83d\udc69\\u200d\ud83d\udc67\\u200d\ud83d\udc66', 1, 3), (5, '\ud83d\ude00ab', 1, 2), (6, '\u00e9ab', -2, -1)]", + "result_repr": "[(1, '\u00e9a'), (2, 'e'), (3, '\u0301a'), (4, '\ud83d\udc68\\u200d\ud83d\udc69'), (5, '\ud83d\ude00a'), (6, 'ab')]", + "result_type": null, + "bits": null, + "error": null, + "note": "unicode slice witnesses" + }, + { + "sql": "SELECT count(*) FROM rnd WHERE array_slice(s,a,b) IS DISTINCT FROM list_slice(s,a,b)", + "input_repr": null, + "result_repr": "[(0,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "220-row random sweep incl NULL bounds + unicode; expect 0" + }, + { + "sql": "SELECT count(*) FROM rnd WHERE array_slice(s,a,b) IS DISTINCT FROM s[a:b]", + "input_repr": null, + "result_repr": "[(0,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "bracket sweep; expect 0" + }, + { + "sql": "SELECT count(*) FROM rnd WHERE array_extract(s,a) IS DISTINCT FROM s[a]", + "input_repr": null, + "result_repr": "[(0,)]", + "result_type": null, + "bits": null, + "error": null, + "note": "extract bracket sweep; expect 0" + }, + { + "sql": "SELECT id, s, a, b, array_slice(s,a,b), list_slice(s,a,b) FROM rnd WHERE array_slice(s,a,b) IS DISTINCT FROM list_slice(s,a,b) LIMIT 5", + "input_repr": null, + "result_repr": "[]", + "result_type": null, + "bits": null, + "error": null, + "note": "mismatch examples if any" + }, + { + "sql": "SELECT substr(s, 2, 3), array_slice(s, 2, 3) FROM strs", + "input_repr": null, + "result_repr": "[('ell', 'el')]", + "result_type": null, + "bits": null, + "error": null, + "note": "substr(start,LENGTH) vs array_slice(begin,end)" + }, + { + "sql": "SELECT array_extract(l, 2), typeof(array_extract(l, 2)), typeof(l) FROM lt", + "input_repr": null, + "result_repr": "[(20, 'INTEGER', 'INTEGER[]')]", + "result_type": null, + "bits": null, + "error": null, + "note": "list-typed overload exists; out of scope otherwise" + }, + { + "sql": "SELECT array_extract('hello', 2), 'hello'[2], array_slice('hello', 2, 4), 'hello'[2:4]", + "input_repr": null, + "result_repr": "[('e', 'e', 'ell', 'ell')]", + "result_type": null, + "bits": null, + "error": null, + "note": "literal path spot check" + }, + { + "sql": "SELECT '\u00e9a'[1], array_extract('\u00e9a', 1)", + "input_repr": null, + "result_repr": "[('\u00e9', '\u00e9')]", + "result_type": null, + "bits": null, + "error": null, + "note": "literal unicode" + }, + { + "sql": "SELECT 'hello'[-1], array_extract('hello', 0), array_slice('hello', 3, 2)", + "input_repr": null, + "result_repr": "[('o', '', '')]", + "result_type": null, + "bits": null, + "error": null, + "note": "literal path edge spot check" + }, + { + "sql": "SELECT array_extract(l, 5), array_extract(l, 0), array_extract(l, -1) FROM lt", + "input_repr": "l=[10,20,30]", + "result_repr": "[(None, None, 30)]", + "result_type": null, + "bits": null, + "error": null, + "note": "LIST overload contrast: out-of-range on list -> NULL? (string gives '')" + } + ] +} \ No newline at end of file diff --git a/scripts/gen_strip_accents.py b/scripts/gen_strip_accents.py new file mode 100644 index 0000000..bb56d09 --- /dev/null +++ b/scripts/gen_strip_accents.py @@ -0,0 +1,162 @@ +"""Generate src/specializer/exec/strip_accents.rs by measuring DuckDB. + +Why this file exists +-------------------- +DuckDB's strip_accents() is utf8proc NFD-decompose + drop-marks + recompose, +with tables from whatever utf8proc vintage this duckdb build ships — which +lags Unicode 16 by dozens of codepoints. Any host Unicode library (Rust +crates, Python unicodedata, ICU) is therefore the wrong spec. Measurement +(pins-wave3/strip_accents.json) shows the observable behavior is exactly: + + 1. all-ASCII input -> returned verbatim (embedded NULs preserved); + 2. otherwise truncate at the first NUL, then + 3. map each codepoint through a fixed table (4460 changed entries: 2450 + marks delete to '', the rest map 1:1 — never multi-codepoint), then + 4. compose adjacent Hangul jamo (L+V -> LV, LV+T -> LVT, formulaic). + +This script sweeps every non-surrogate codepoint through the vectorized +path, extracts the changed entries, verifies the 4-step model end-to-end +against duckdb on random compositions, and freezes the table. The oracle +census in tests/test_duckdb_interpreter.py is the ongoing authority. + +Regenerate with `uv run python scripts/gen_strip_accents.py` after bumping +the duckdb dependency, then run the gate. +""" + +from __future__ import annotations + +import random +import sys +from pathlib import Path + +import duckdb +import pyarrow as pa + +REPO = Path(__file__).resolve().parent.parent +OUT = REPO / "src" / "specializer" / "exec" / "strip_accents.rs" + +S_BASE, L_BASE, V_BASE, T_BASE = 0xAC00, 0x1100, 0x1161, 0x11A7 +V_COUNT, T_COUNT, S_COUNT = 21, 28, 11172 + + +def hangul_compose(cps: list[int]) -> list[int]: + out: list[int] = [] + for c in cps: + if out: + p = out[-1] + if 0x1100 <= p <= 0x1112 and 0x1161 <= c <= 0x1175: + out[-1] = ( + S_BASE + (p - L_BASE) * (V_COUNT * T_COUNT) + (c - V_BASE) * T_COUNT + ) + continue + if ( + S_BASE <= p < S_BASE + S_COUNT + and (p - S_BASE) % T_COUNT == 0 + and 0x11A8 <= c <= 0x11C2 + ): + out[-1] = p + (c - T_BASE) + continue + out.append(c) + return out + + +def model(s: str, table: dict[int, str]) -> str: + if all(ord(c) < 0x80 for c in s): + return s + nul = s.find("\x00") + if nul >= 0: + s = s[:nul] + mapped = [table.get(ord(c), c) for c in s] + cps = [ord(c) for m in mapped for c in m] + return "".join(chr(c) for c in hangul_compose(cps)) + + +def main() -> int: + con = duckdb.connect() + cps = [cp for cp in range(1, 0x110000) if not (0xD800 <= cp <= 0xDFFF)] + con.register("cps_arrow", pa.table({"cp": cps, "s": [chr(c) for c in cps]})) + con.execute("CREATE TABLE cps AS SELECT * FROM cps_arrow") + rows = con.execute("SELECT cp, s, strip_accents(s) FROM cps ORDER BY cp").fetchall() + + table: dict[int, str] = {} + for cp, s, out in rows: + if out != s: + assert len(out) <= 1, f"multi-codepoint output at U+{cp:04X}: {out!r}" + table[cp] = out + + # Witnesses from the pins: fail loudly if the oracle moved. + assert table[0x00E9] == "e" and table[0x212A] == "K" and table[0x0301] == "" + + # End-to-end model check: random multi-char strings incl. jamo, + # marks, NULs, astral chars — the model must match duckdb exactly. + rng = random.Random(49) # noqa: S311 -- deterministic probe seed, not crypto + pool = ( + [rng.choice(cps) for _ in range(400)] + + list(range(0x1100, 0x1113)) + + list(range(0x1161, 0x1176)) + + list(range(0x11A8, 0x11C3)) + + [0x0301, 0x0300, 0xE9, 0x41, 0x62, 0x1F600, 0xAC00, 0x212A, 0] + ) + probes = [ + "".join(chr(rng.choice(pool)) for _ in range(rng.randrange(1, 12))) + for _ in range(600) + ] + con.register("probes_arrow", pa.table({"s": probes})) + got = con.execute("SELECT strip_accents(s) FROM probes_arrow").fetchall() + bad = [ + (p, g[0], model(p, table)) + for p, g in zip(probes, got, strict=True) + if g[0] != model(p, table) + ] + assert not bad, ( + f"model diverges from duckdb on {len(bad)} probes, first: {bad[0]!r}" + ) + + deleted = sorted(cp for cp, m in table.items() if m == "") + mapped = sorted((cp, m) for cp, m in table.items() if m != "") + + def fmt_deleted() -> str: + body = "\n".join(f" '\\u{{{cp:04X}}}'," for cp in deleted) + return f"pub(super) const STRIP_DELETED: &[char] = &[\n{body}\n];\n" + + def fmt_mapped() -> str: + body = "\n".join( + f" ('\\u{{{cp:04X}}}', '\\u{{{ord(m):04X}}}')," for cp, m in mapped + ) + return f"pub(super) const STRIP_MAPPED: &[(char, char)] = &[\n{body}\n];\n" + + OUT.write_text( + f"""//! DuckDB's strip_accents() per-codepoint map, as measured — see +//! scripts/gen_strip_accents.py for the model (ASCII fast path, NUL +//! truncation, this map, then Hangul jamo composition) and why a host +//! Unicode library would be the wrong spec (utf8proc version skew). +//! +//! GENERATED by `uv run python scripts/gen_strip_accents.py` against +//! duckdb-python {duckdb.__version__}; regenerate after a duckdb bump. The oracle +//! census in tests/test_duckdb_interpreter.py is the authority. + +/// Per-codepoint result: `None` = unchanged, `Some(None)` = deleted, +/// `Some(Some(m))` = mapped 1:1. +pub(super) fn strip_map(c: char) -> Option> {{ + if (c as u32) < 0x80 {{ + return None; + }} + if STRIP_DELETED.binary_search(&c).is_ok() {{ + return Some(None); + }} + if let Ok(i) = STRIP_MAPPED.binary_search_by_key(&c, |e| e.0) {{ + return Some(Some(STRIP_MAPPED[i].1)); + }} + None +}} + +{fmt_deleted()} +{fmt_mapped()}""", + encoding="utf-8", + ) + print(f"wrote {OUT}: {len(deleted)} deleted + {len(mapped)} mapped entries") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/specializer/exec/cranelift.rs b/src/specializer/exec/cranelift.rs index 98f095c..6f792a8 100644 --- a/src/specializer/exec/cranelift.rs +++ b/src/specializer/exec/cranelift.rs @@ -149,24 +149,25 @@ extern "C" fn h_const_str(p: *mut Cx, ptr: i64, len: i64) -> i64 { } macro_rules! checked_bin { - ($name:ident, $method:ident, $msg:literal) => { + ($name:ident, $method:ident, $op:expr) => { extern "C" fn $name(p: *mut Cx, a: i64, b: i64) -> i64 { match a.$method(b) { Some(v) => v, None => { - unsafe { cx(p) }.set_trap(format!($msg)); + // Same DuckDB-verbatim text as the interpreter arm. + unsafe { cx(p) }.set_trap(interp::overflow_msg($op, a, b)); 0 } } } }; } -checked_bin!(h_iadd, checked_add, "i64 overflow in iadd"); -checked_bin!(h_isub, checked_sub, "i64 overflow in isub"); -checked_bin!(h_imul, checked_mul, "i64 overflow in imul"); +checked_bin!(h_iadd, checked_add, BinOp::Iadd); +checked_bin!(h_isub, checked_sub, BinOp::Isub); +checked_bin!(h_imul, checked_mul, BinOp::Imul); macro_rules! checked_div { - ($name:ident, $method:ident, $opname:literal) => { + ($name:ident, $method:ident, $op:expr, $opname:literal) => { extern "C" fn $name(p: *mut Cx, a: i64, b: i64) -> i64 { if b == 0 { unsafe { cx(p) }.set_trap(format!("division by zero in {}", $opname)); @@ -175,15 +176,15 @@ macro_rules! checked_div { match a.$method(b) { Some(v) => v, None => { - unsafe { cx(p) }.set_trap(format!("i64 overflow in {}", $opname)); + unsafe { cx(p) }.set_trap(interp::overflow_msg($op, a, b)); 0 } } } }; } -checked_div!(h_idiv, checked_div, "idiv"); -checked_div!(h_irem, checked_rem, "irem"); +checked_div!(h_idiv, checked_div, BinOp::Idiv, "idiv"); +checked_div!(h_irem, checked_rem, BinOp::Irem, "irem"); extern "C" fn h_frem(a: f64, b: f64) -> f64 { a % b @@ -322,7 +323,7 @@ extern "C" fn h_iabs(p: *mut Cx, a: i64) -> i64 { match a.checked_abs() { Some(v) => v, None => { - unsafe { cx(p) }.set_trap("i64 overflow in iabs".to_string()); + unsafe { cx(p) }.set_trap(interp::abs_overflow_msg(a)); 0 } } @@ -506,6 +507,174 @@ extern "C" fn h_ssubstr( (ao as usize + rng.start) as i64 } +// Wave-3 helpers: every one delegates to the interpreter's shared +// semantic fn (pins spec 2026-07-26 wave 3) — the backends cannot drift. + +extern "C" fn h_ssim(p: *mut Cx, which: i64, ao: i64, al: i64, bo: i64, bl: i64) -> i64 { + let c = unsafe { cx(p) }; + let arena = unsafe { &*c.arena }; + let a = arena.get(span(ao, al)).as_bytes(); + let b = arena.get(span(bo, bl)).as_bytes(); + match which { + 0 => interp::duck_levenshtein(a, b), + 1 => interp::duck_damerau(a, b), + _ => match interp::duck_hamming(a, b) { + Ok(v) => v, + Err(t) => { + c.set_trap(t.0); + 0 + } + }, + } +} + +extern "C" fn h_sjaccard(p: *mut Cx, ao: i64, al: i64, bo: i64, bl: i64) -> f64 { + let c = unsafe { cx(p) }; + let arena = unsafe { &*c.arena }; + let a = arena.get(span(ao, al)).as_bytes(); + let b = arena.get(span(bo, bl)).as_bytes(); + match interp::duck_jaccard(a, b) { + Ok(v) => v, + Err(t) => { + c.set_trap(t.0); + 0.0 + } + } +} + +extern "C" fn h_str3( + p: *mut Cx, + which: i64, + ao: i64, + al: i64, + bo: i64, + bl: i64, + co: i64, + cl: i64, + len_out: *mut i64, +) -> i64 { + let c = unsafe { cx(p) }; + let arena = unsafe { &mut *c.arena }; + let out = { + let s = arena.get(span(ao, al)); + let x = arena.get(span(bo, bl)); + let y = arena.get(span(co, cl)); + match which { + 0 => interp::duck_replace(s, x, y), + _ => interp::duck_translate(s, x, y), + } + }; + let r = arena.push_str(&out); + unsafe { *len_out = r.len as i64 }; + r.off as i64 +} + +extern "C" fn h_srepeat(p: *mut Cx, ao: i64, al: i64, n: i64, len_out: *mut i64) -> i64 { + let c = unsafe { cx(p) }; + let arena = unsafe { &mut *c.arena }; + let out = { + let s = arena.get(span(ao, al)); + interp::duck_repeat(s, n) + }; + match out { + Ok(s) => { + let r = arena.push_str(&s); + unsafe { *len_out = r.len as i64 }; + r.off as i64 + } + Err(t) => { + c.set_trap(t.0); + unsafe { *len_out = 0 }; + 0 + } + } +} + +extern "C" fn h_sextract(p: *mut Cx, ao: i64, al: i64, i: i64, len_out: *mut i64) -> i64 { + let c = unsafe { cx(p) }; + let arena = unsafe { &*c.arena }; + let rng = interp::extract_window(arena.get(span(ao, al)), i); + // The extracted char is a subview of the input span — no copy. + unsafe { *len_out = (rng.end - rng.start) as i64 }; + (ao as usize + rng.start) as i64 +} + +extern "C" fn h_spad( + p: *mut Cx, + left: i64, + ao: i64, + al: i64, + l: i64, + po: i64, + pl: i64, + len_out: *mut i64, +) -> i64 { + let c = unsafe { cx(p) }; + let arena = unsafe { &mut *c.arena }; + let out = { + let s = arena.get(span(ao, al)); + let pad = arena.get(span(po, pl)); + interp::duck_pad(left != 0, s, l, pad) + }; + match out { + Ok(s) => { + let r = arena.push_str(&s); + unsafe { *len_out = r.len as i64 }; + r.off as i64 + } + Err(t) => { + c.set_trap(t.0); + unsafe { *len_out = 0 }; + 0 + } + } +} + +extern "C" fn h_sslice(p: *mut Cx, ao: i64, al: i64, lo: i64, hi: i64, len_out: *mut i64) -> i64 { + let c = unsafe { cx(p) }; + let arena = unsafe { &*c.arena }; + let rng = interp::slice_window(arena.get(span(ao, al)), lo, hi); + // The slice is a subview of the input span — no copy. + unsafe { *len_out = (rng.end - rng.start) as i64 }; + (ao as usize + rng.start) as i64 +} + +extern "C" fn h_sord(p: *mut Cx, empty_zero: i64, ao: i64, al: i64) -> i64 { + let c = unsafe { cx(p) }; + let arena = unsafe { &*c.arena }; + interp::duck_ord(arena.get(span(ao, al)), empty_zero != 0) +} + +extern "C" fn h_sstrip(p: *mut Cx, ao: i64, al: i64, len_out: *mut i64) -> i64 { + let c = unsafe { cx(p) }; + let arena = unsafe { &mut *c.arena }; + let out = interp::duck_strip_accents(arena.get(span(ao, al))); + match out { + None => { + // ASCII fast path: the input span verbatim. + unsafe { *len_out = al }; + ao + } + Some(s) => { + let r = arena.push_str(&s); + unsafe { *len_out = r.len as i64 }; + r.off as i64 + } + } +} + +extern "C" fn h_ffloordiv(x: f64, y: f64) -> f64 { + interp::duck_fdiv(x, y) +} + +extern "C" fn h_ffloormod(x: f64, y: f64) -> f64 { + interp::duck_fmod(x, y) +} + +extern "C" fn h_fnextafter(x: f64, y: f64) -> f64 { + interp::duck_nextafter(x, y) +} + macro_rules! store_h { ($name:ident, $variant:ident, $ty:ty, $conv:expr) => { extern "C" fn $name(p: *mut Cx, col: i64, valid: u8, val: $ty) { @@ -1011,6 +1180,9 @@ fn translate_inst( BinOp::Frem => call_h(b, module, "h_frem", &[x, y]).unwrap(), BinOp::Fpow => call_h(b, module, "h_fpow", &[x, y]).unwrap(), BinOp::Flogb => call_h(b, module, "h_flogb", &[cxp, x, y]).unwrap(), + BinOp::Ffloordiv => call_h(b, module, "h_ffloordiv", &[x, y]).unwrap(), + BinOp::Ffloormod => call_h(b, module, "h_ffloormod", &[x, y]).unwrap(), + BinOp::Fnextafter => call_h(b, module, "h_fnextafter", &[x, y]).unwrap(), BinOp::And => b.ins().band(x, y), BinOp::Or => b.ins().bor(x, y), BinOp::Xor => b.ins().bxor(x, y), @@ -1159,6 +1331,26 @@ fn translate_inst( let (bo, bl) = vals[&rhs.0].str2(); let v = match op { StrOp2::Find => call_h(b, module, "h_sfind", &[cxp, ao, al, bo, bl]).unwrap(), + StrOp2::Levenshtein | StrOp2::Damerau | StrOp2::Hamming => { + let which = icon( + b, + match op { + StrOp2::Levenshtein => 0, + StrOp2::Damerau => 1, + _ => 2, + }, + ); + let v = call_h(b, module, "h_ssim", &[cxp, which, ao, al, bo, bl]).unwrap(); + if matches!(op, StrOp2::Hamming) { + trap_check(b); + } + v + } + StrOp2::Jaccard => { + let v = call_h(b, module, "h_sjaccard", &[cxp, ao, al, bo, bl]).unwrap(); + trap_check(b); + v + } pred => { let which = icon( b, @@ -1173,6 +1365,72 @@ fn translate_inst( }; vals.insert(dst.0, V::S(v)); } + Inst::Str3 { op, dst, a, b: x, c: y } => { + let (ao, al) = vals[&a.0].str2(); + let (bo, bl) = vals[&x.0].str2(); + let (co, cl) = vals[&y.0].str2(); + let which = icon(b, matches!(op, super::super::ir::StrOp3::Translate) as i64); + let lp = b.ins().stack_addr(types::I64, slot_out, 0); + let off = call_h( + b, + module, + "h_str3", + &[cxp, which, ao, al, bo, bl, co, cl, lp], + ) + .unwrap(); + let len = b.ins().stack_load(types::I64, slot_out, 0); + vals.insert(dst.0, V::Str(off, len)); + } + Inst::Str2i { op, dst, a, n } => { + let (ao, al) = vals[&a.0].str2(); + let nv = vals[&n.0].s(); + let lp = b.ins().stack_addr(types::I64, slot_out, 0); + let (name, traps) = match op { + super::super::ir::StrOp2i::Repeat => ("h_srepeat", true), + super::super::ir::StrOp2i::Extract => ("h_sextract", false), + }; + let off = call_h(b, module, name, &[cxp, ao, al, nv, lp]).unwrap(); + if traps { + trap_check(b); + } + let len = b.ins().stack_load(types::I64, slot_out, 0); + vals.insert(dst.0, V::Str(off, len)); + } + Inst::Spad { + left, + dst, + a, + len, + pad, + } => { + let (ao, al) = vals[&a.0].str2(); + let lv = vals[&len.0].s(); + let (po, pl) = vals[&pad.0].str2(); + let leftv = icon(b, *left as i64); + let lp = b.ins().stack_addr(types::I64, slot_out, 0); + let off = call_h(b, module, "h_spad", &[cxp, leftv, ao, al, lv, po, pl, lp]).unwrap(); + trap_check(b); + let lnew = b.ins().stack_load(types::I64, slot_out, 0); + vals.insert(dst.0, V::Str(off, lnew)); + } + Inst::Sslice { dst, a, lo, hi } => { + let (ao, al) = vals[&a.0].str2(); + let (lov, hiv) = (vals[&lo.0].s(), vals[&hi.0].s()); + let lp = b.ins().stack_addr(types::I64, slot_out, 0); + let off = call_h(b, module, "h_sslice", &[cxp, ao, al, lov, hiv, lp]).unwrap(); + let len = b.ins().stack_load(types::I64, slot_out, 0); + vals.insert(dst.0, V::Str(off, len)); + } + Inst::Sord { + empty_zero, + dst, + a, + } => { + let (ao, al) = vals[&a.0].str2(); + let ez = icon(b, *empty_zero as i64); + let v = call_h(b, module, "h_sord", &[cxp, ez, ao, al]).unwrap(); + vals.insert(dst.0, V::S(v)); + } Inst::SLen { bytes, dst, a } => { let (ao, al) = vals[&a.0].str2(); let bv = icon(b, *bytes as i64); @@ -1189,9 +1447,16 @@ fn translate_inst( } Inst::Str1 { op, dst, a } => { let (o, l) = vals[&a.0].str2(); - let up = icon(b, matches!(op, StrOp1::Upper) as i64); let lp = b.ins().stack_addr(types::I64, slot_out, 0); - let off = call_h(b, module, "h_scase", &[cxp, o, l, up, lp]).unwrap(); + let off = match op { + StrOp1::StripAccents => { + call_h(b, module, "h_sstrip", &[cxp, o, l, lp]).unwrap() + } + _ => { + let up = icon(b, matches!(op, StrOp1::Upper) as i64); + call_h(b, module, "h_scase", &[cxp, o, l, up, lp]).unwrap() + } + }; let len = b.ins().stack_load(types::I64, slot_out, 0); vals.insert(dst.0, V::Str(off, len)); } @@ -1546,6 +1811,18 @@ const HELPERS: &[(&str, *const u8)] = &[ ("h_round2f", h_round2f as *const u8), ("h_round2i", h_round2i as *const u8), ("h_slike", h_slike as *const u8), + ("h_ssim", h_ssim as *const u8), + ("h_sjaccard", h_sjaccard as *const u8), + ("h_str3", h_str3 as *const u8), + ("h_srepeat", h_srepeat as *const u8), + ("h_sextract", h_sextract as *const u8), + ("h_spad", h_spad as *const u8), + ("h_sslice", h_sslice as *const u8), + ("h_sord", h_sord as *const u8), + ("h_sstrip", h_sstrip as *const u8), + ("h_ffloordiv", h_ffloordiv as *const u8), + ("h_ffloormod", h_ffloormod as *const u8), + ("h_fnextafter", h_fnextafter as *const u8), ]; fn helper_sig(name: &str, sig: &mut cranelift_codegen::ir::Signature, ptr: types::Type) { @@ -1570,6 +1847,16 @@ fn helper_sig(name: &str, sig: &mut cranelift_codegen::ir::Signature, ptr: types "h_round2f" => (&[F64, I64, I64], Some(F64)), "h_round2i" => (&[I64, I64, I64], Some(I64)), "h_slike" => (&[ptr, I64, I64, I64, I64, I64, I64, I64, I64], Some(I8)), + "h_ssim" => (&[ptr, I64, I64, I64, I64, I64], Some(I64)), + "h_sjaccard" => (&[ptr, I64, I64, I64, I64], Some(F64)), + "h_str3" => (&[ptr, I64, I64, I64, I64, I64, I64, I64, I64], Some(I64)), + "h_srepeat" => (&[ptr, I64, I64, I64, I64], Some(I64)), + "h_sextract" => (&[ptr, I64, I64, I64, I64], Some(I64)), + "h_spad" => (&[ptr, I64, I64, I64, I64, I64, I64, I64], Some(I64)), + "h_sslice" => (&[ptr, I64, I64, I64, I64, I64], Some(I64)), + "h_sord" => (&[ptr, I64, I64, I64], Some(I64)), + "h_sstrip" => (&[ptr, I64, I64, I64], Some(I64)), + "h_ffloordiv" | "h_ffloormod" | "h_fnextafter" => (&[F64, F64], Some(F64)), "h_iabs" => (&[ptr, I64], Some(I64)), "h_fcmp" => (&[F64, F64, I64], Some(I8)), "h_scmp" => (&[ptr, I64, I64, I64, I64, I64], Some(I8)), diff --git a/src/specializer/exec/interp.rs b/src/specializer/exec/interp.rs index 8e97b01..d49c1e3 100644 --- a/src/specializer/exec/interp.rs +++ b/src/specializer/exec/interp.rs @@ -928,6 +928,341 @@ pub(super) fn like_escape_of(esc: &str) -> Result, Trap> { } } +// ------------------------------------------ wave-3 builtins (TASK-49) -- +// One shared fn per op, used verbatim by BOTH backends. Pins: +// docs/superpowers/specs/2026-07-26-wave3-builtin-pins.md — all +// similarity ops are raw UTF-8 BYTE-based; error texts are DuckDB's own. + +/// levenshtein == editdist3: classic two-row DP over bytes. +pub(super) fn duck_levenshtein(a: &[u8], b: &[u8]) -> i64 { + if a.is_empty() || b.is_empty() { + return (a.len() + b.len()) as i64; + } + let mut prev: Vec = (0..=b.len()).collect(); + let mut cur = vec![0usize; b.len() + 1]; + for (i, &ac) in a.iter().enumerate() { + cur[0] = i + 1; + for (j, &bc) in b.iter().enumerate() { + let sub = prev[j] + usize::from(ac != bc); + cur[j + 1] = sub.min(prev[j + 1] + 1).min(cur[j] + 1); + } + std::mem::swap(&mut prev, &mut cur); + } + prev[b.len()] as i64 +} + +/// damerau_levenshtein: the UNRESTRICTED DL variant (NOT OSA — witness +/// ('ca','abc') = 2), transposition cost 1, over bytes. Full matrix plus +/// a last-occurrence table, as the unrestricted recurrence requires. +// ponytail: O(n*m) memory; corpus strings are short — stream it if huge +// inputs ever matter. +pub(super) fn duck_damerau(a: &[u8], b: &[u8]) -> i64 { + let (n, m) = (a.len(), b.len()); + if n == 0 || m == 0 { + return (n + m) as i64; + } + let w = m + 2; + let inf = n + m; + let mut d = vec![0usize; (n + 2) * w]; + d[0] = inf; + for i in 0..=n { + d[(i + 1) * w] = inf; + d[(i + 1) * w + 1] = i; + } + for j in 0..=m { + d[j + 1] = inf; + d[w + j + 1] = j; + } + let mut last_a = [0usize; 256]; // last row where each byte occurred in a + for i in 1..=n { + let mut last_b = 0usize; // last column where a[i-1] matched in b + for j in 1..=m { + let (i1, j1) = (last_a[b[j - 1] as usize], last_b); + let cost = usize::from(a[i - 1] != b[j - 1]); + if cost == 0 { + last_b = j; + } + let sub = d[i * w + j] + cost; + let ins = d[(i + 1) * w + j] + 1; + let del = d[i * w + j + 1] + 1; + let trans = d[i1 * w + j1] + (i - i1 - 1) + 1 + (j - j1 - 1); + d[(i + 1) * w + j + 1] = sub.min(ins).min(del).min(trans); + } + last_a[a[i - 1] as usize] = i; + } + d[(n + 1) * w + m + 1] as i64 +} + +/// jaccard: |A∩B|/|A∪B| over single-BYTE sets; empty either side traps. +pub(super) fn duck_jaccard(a: &[u8], b: &[u8]) -> Result { + if a.is_empty() || b.is_empty() { + return Err(Trap("Jaccard Function: An argument too short!".into())); + } + let (mut in_a, mut in_b) = ([false; 256], [false; 256]); + for &c in a { + in_a[c as usize] = true; + } + for &c in b { + in_b[c as usize] = true; + } + let (mut inter, mut union) = (0i64, 0i64); + for i in 0..256 { + inter += i64::from(in_a[i] && in_b[i]); + union += i64::from(in_a[i] || in_b[i]); + } + Ok(inter as f64 / union as f64) +} + +/// hamming == mismatches: byte-wise; empty inputs and length mismatch +/// trap — the messages say "Mismatch Function" even for hamming +/// (measured; the error text leaks the shared implementation). +pub(super) fn duck_hamming(a: &[u8], b: &[u8]) -> Result { + if a.is_empty() || b.is_empty() { + return Err(Trap( + "Mismatch Function: Strings must be of length > 0!".into(), + )); + } + if a.len() != b.len() { + return Err(Trap( + "Mismatch Function: Strings must be of equal length!".into(), + )); + } + Ok(a.iter().zip(b).filter(|(x, y)| x != y).count() as i64) +} + +/// Engine guard for string-building ops: DuckDB errors on absurd result +/// sizes too (exact threshold/message unpinned — huge-n was deliberately +/// not probed); 1 GiB keeps the engine alive without shadowing any pin. +const STR_BUILD_CAP: u64 = 1 << 30; + +/// repeat(s, n): n <= 0 -> '' silently. +pub(super) fn duck_repeat(s: &str, n: i64) -> Result { + if n <= 0 { + return Ok(String::new()); + } + match (s.len() as u64).checked_mul(n as u64) { + Some(sz) if sz <= STR_BUILD_CAP => Ok(s.repeat(n as usize)), + _ => Err(Trap("string builder result exceeds 1 GiB".into())), + } +} + +/// lpad/rpad(s, l, pad): l counts CODEPOINTS; truncation keeps the FIRST +/// l codepoints for BOTH sides; the pad cycles cut at codepoint +/// boundaries; empty pad traps ONLY when growth is needed. +pub(super) fn duck_pad(left: bool, s: &str, l: i64, pad: &str) -> Result { + if l <= 0 { + return Ok(String::new()); + } + let l = l as usize; + let n = s.chars().count(); + if l <= n { + return Ok(s.chars().take(l).collect()); + } + if pad.is_empty() { + return Err(Trap(format!( + "Insufficient padding in {}.", + if left { "LPAD" } else { "RPAD" } + ))); + } + if l as u64 > STR_BUILD_CAP / 4 { + return Err(Trap("string builder result exceeds 1 GiB".into())); + } + let fill: String = pad.chars().cycle().take(l - n).collect(); + Ok(if left { + fill + s + } else { + let mut out = s.to_string(); + out.push_str(&fill); + out + }) +} + +/// replace(s, from, to): empty needle is a strict NO-OP; leftmost +/// non-overlapping single pass (Rust `str::replace` is exactly that). +pub(super) fn duck_replace(s: &str, from: &str, to: &str) -> String { + if from.is_empty() { + return s.to_string(); + } + s.replace(from, to) +} + +/// translate(s, from, to): per-codepoint map, FIRST occurrence in `from` +/// wins, from-chars beyond |to| are deleted. +pub(super) fn duck_translate(s: &str, from: &str, to: &str) -> String { + let mut out = String::with_capacity(s.len()); + for c in s.chars() { + match from.chars().position(|f| f == c) { + None => out.push(c), + Some(i) => { + if let Some(t) = to.chars().nth(i) { + out.push(t); + } + } + } + } + out +} + +/// array_extract/list_extract/s[i] on VARCHAR: 1-based codepoint, +/// negative resolves len+1+i, out-of-range/0 -> '' (NOT NULL). Returns +/// the byte range so callers can subview instead of copying. +pub(super) fn extract_window(s: &str, i: i64) -> std::ops::Range { + let n = s.chars().count() as i64; + let pos = if i < 0 { (n + 1).saturating_add(i) } else { i }; + if pos < 1 || pos > n { + return 0..0; + } + let skip = (pos - 1) as usize; + let b0 = s + .char_indices() + .nth(skip) + .map(|(i, _)| i) + .unwrap_or(s.len()); + let b1 = s[b0..] + .chars() + .next() + .map(|c| b0 + c.len_utf8()) + .unwrap_or(s.len()); + b0..b1 +} + +/// array_slice/list_slice/s[a:b] on VARCHAR: 1-based, both-ends-INCLUSIVE +/// codepoints, negative from-end (-1 = last), lo <= 0 clamps to start, +/// hi > len clamps to end, reversed/out-of-range -> ''. Byte range out. +pub(super) fn slice_window(s: &str, lo: i64, hi: i64) -> std::ops::Range { + let n = s.chars().count() as i64; + let rlo = (if lo < 0 { + (n + 1).saturating_add(lo) + } else { + lo + }) + .max(1); + let rhi = (if hi < 0 { + (n + 1).saturating_add(hi) + } else { + hi + }) + .min(n); + if rlo > rhi { + return 0..0; + } + let skip = (rlo - 1) as usize; + let take = (rhi - rlo + 1) as usize; + let b0 = s + .char_indices() + .nth(skip) + .map(|(i, _)| i) + .unwrap_or(s.len()); + let b1 = s[b0..] + .char_indices() + .nth(take) + .map(|(i, _)| b0 + i) + .unwrap_or(s.len()); + b0..b1 +} + +/// unicode/ord ('' -> -1) and ascii ('' -> 0 — the sole divergence): +/// first codepoint as i64. +pub(super) fn duck_ord(s: &str, empty_zero: bool) -> i64 { + match s.chars().next() { + Some(c) => c as i64, + None => { + if empty_zero { + 0 + } else { + -1 + } + } + } +} + +/// strip_accents: all-ASCII passes VERBATIM (NULs preserved); otherwise +/// truncate at the first NUL (the measured context-dependent quirk), +/// per-codepoint oracle map, then Hangul jamo composition. TOTAL. +pub(super) fn duck_strip_accents(s: &str) -> Option { + if s.is_ascii() { + return None; // caller keeps the input span — no copy + } + let cut = s.find('\0').map(|i| &s[..i]).unwrap_or(s); + let mut out = String::with_capacity(cut.len()); + for c in cut.chars() { + let mapped = match super::strip_accents::strip_map(c) { + None => Some(c), + Some(m) => m, + }; + let Some(mc) = mapped else { continue }; + // Hangul compose against the last emitted codepoint: L+V -> LV, + // LV+T -> LVT (formulaic; wrong-order jamo never compose). + if let Some(p) = out.chars().next_back() { + let (pu, cu) = (p as u32, mc as u32); + if (0x1100..=0x1112).contains(&pu) && (0x1161..=0x1175).contains(&cu) { + out.pop(); + let lv = 0xAC00 + (pu - 0x1100) * 588 + (cu - 0x1161) * 28; + out.push(char::from_u32(lv).expect("Hangul LV in range")); + continue; + } + if (0xAC00..0xAC00 + 11172).contains(&pu) + && (pu - 0xAC00) % 28 == 0 + && (0x11A8..=0x11C2).contains(&cu) + { + out.pop(); + let lvt = pu + (cu - 0x11A7); + out.push(char::from_u32(lvt).expect("Hangul LVT in range")); + continue; + } + } + out.push(mc); + } + Some(out) +} + +/// SQL fdiv(x, y) = floor(x / y) — TOTAL, ±inf on zero divisor. +pub(super) fn duck_fdiv(x: f64, y: f64) -> f64 { + (x / y).floor() +} + +/// SQL fmod(x, y) = x − floor(x/y)·y — FLOORED (divisor's sign), NaN on +/// zero or infinite divisor. NOT C fmod (that is SQL `%`). +pub(super) fn duck_fmod(x: f64, y: f64) -> f64 { + x - (x / y).floor() * y +} + +/// C nextafter, bit-exact; x == y returns y (incl. signed zeros). +pub(super) fn duck_nextafter(x: f64, y: f64) -> f64 { + if x.is_nan() || y.is_nan() { + return f64::NAN; + } + if x == y { + return y; + } + if x == 0.0 { + // toward y from zero: the smallest denormal with y's direction. + return f64::from_bits(1).copysign(y - x); + } + let bits = x.to_bits(); + let up = (y > x) == (x > 0.0); // move away from zero? + f64::from_bits(if up { bits + 1 } else { bits - 1 }) +} + +/// DuckDB's int-overflow trap texts, verbatim (wave-3 pins: measured via +/// both the operators and their function aliases). Division covers `//` +/// AND `%` on i64::MIN op -1 — DuckDB's own % message says "division", +/// and only add/sub/mul carry the trailing '!'. +pub(super) fn overflow_msg(op: BinOp, x: i64, y: i64) -> String { + match op { + BinOp::Iadd => format!("Overflow in addition of INT64 ({x} + {y})!"), + BinOp::Isub => format!("Overflow in subtraction of INT64 ({x} - {y})!"), + BinOp::Imul => format!("Overflow in multiplication of INT64 ({x} * {y})!"), + _ => format!("Overflow in division of {x} / {y}"), + } +} + +/// DuckDB's abs(i64::MIN) trap text, verbatim (measured 2026-07-26 — no +/// trailing '!', unlike the binary-op overflow family). +pub(super) fn abs_overflow_msg(x: i64) -> String { + format!("Overflow on abs({x})") +} + /// Wave-1 string search (pins: 1-based CODEPOINT positions, empty needle /// matches everything, byte-wise comparison, zero unicode intelligence). pub(super) fn str_find(s: &str, n: &str) -> i64 { @@ -945,7 +1280,7 @@ pub(super) fn str_pred(op: StrOp2, s: &str, n: &str) -> bool { StrOp2::Contains => s.contains(n), StrOp2::Starts => s.starts_with(n), StrOp2::Ends => s.ends_with(n), - StrOp2::Find => unreachable!("find returns i64, handled separately"), + _ => unreachable!("non-predicate str2 ops have dedicated arms"), } } @@ -992,7 +1327,7 @@ fn compile_inst(p: &Program, inst: &Inst, slots: &HashMap) -> InstFn { ctx.regs[dst] = RegVal::I64(v); Ok(()) } - None => Err(Trap(format!("i64 overflow in {}", op.name()))), + None => Err(Trap(overflow_msg(op, x, y))), } }), BinOp::Idiv | BinOp::Irem => Box::new(move |ctx| { @@ -1009,7 +1344,7 @@ fn compile_inst(p: &Program, inst: &Inst, slots: &HashMap) -> InstFn { ctx.regs[dst] = RegVal::I64(v); Ok(()) } - None => Err(Trap(format!("i64 overflow in {}", op.name()))), + None => Err(Trap(overflow_msg(op, x, y))), } }), BinOp::Fadd @@ -1017,7 +1352,10 @@ fn compile_inst(p: &Program, inst: &Inst, slots: &HashMap) -> InstFn { | BinOp::Fmul | BinOp::Fdiv | BinOp::Frem - | BinOp::Fpow => Box::new(move |ctx| { + | BinOp::Fpow + | BinOp::Ffloordiv + | BinOp::Ffloormod + | BinOp::Fnextafter => Box::new(move |ctx| { let (x, y) = (as_f64(ctx.regs[a]), as_f64(ctx.regs[b])); let v = match op { BinOp::Fadd => x + y, @@ -1025,6 +1363,9 @@ fn compile_inst(p: &Program, inst: &Inst, slots: &HashMap) -> InstFn { BinOp::Fmul => x * y, BinOp::Fdiv => x / y, BinOp::Fpow => duck_pow(x, y)?, + BinOp::Ffloordiv => duck_fdiv(x, y), + BinOp::Ffloormod => duck_fmod(x, y), + BinOp::Fnextafter => duck_nextafter(x, y), _ => x % y, }; ctx.regs[dst] = RegVal::F64(v); @@ -1234,6 +1575,30 @@ fn compile_inst(p: &Program, inst: &Inst, slots: &HashMap) -> InstFn { ctx.regs[dst] = RegVal::I64(str_find(s, n)); Ok(()) }), + StrOp2::Levenshtein => Box::new(move |ctx| { + let s = ctx.arena.get(as_str(ctx.regs[a])).as_bytes(); + let n = ctx.arena.get(as_str(ctx.regs[b])).as_bytes(); + ctx.regs[dst] = RegVal::I64(duck_levenshtein(s, n)); + Ok(()) + }), + StrOp2::Damerau => Box::new(move |ctx| { + let s = ctx.arena.get(as_str(ctx.regs[a])).as_bytes(); + let n = ctx.arena.get(as_str(ctx.regs[b])).as_bytes(); + ctx.regs[dst] = RegVal::I64(duck_damerau(s, n)); + Ok(()) + }), + StrOp2::Jaccard => Box::new(move |ctx| { + let s = ctx.arena.get(as_str(ctx.regs[a])).as_bytes(); + let n = ctx.arena.get(as_str(ctx.regs[b])).as_bytes(); + ctx.regs[dst] = RegVal::F64(duck_jaccard(s, n)?); + Ok(()) + }), + StrOp2::Hamming => Box::new(move |ctx| { + let s = ctx.arena.get(as_str(ctx.regs[a])).as_bytes(); + let n = ctx.arena.get(as_str(ctx.regs[b])).as_bytes(); + ctx.regs[dst] = RegVal::I64(duck_hamming(s, n)?); + Ok(()) + }), op => Box::new(move |ctx| { let s = ctx.arena.get(as_str(ctx.regs[a])); let n = ctx.arena.get(as_str(ctx.regs[b])); @@ -1242,6 +1607,93 @@ fn compile_inst(p: &Program, inst: &Inst, slots: &HashMap) -> InstFn { }), } } + Inst::Str3 { op, dst, a, b, c } => { + let (dst, a, b, c) = (sl(slots, dst), sl(slots, a), sl(slots, b), sl(slots, c)); + Box::new(move |ctx| { + let out = { + let s = ctx.arena.get(as_str(ctx.regs[a])); + let x = ctx.arena.get(as_str(ctx.regs[b])); + let y = ctx.arena.get(as_str(ctx.regs[c])); + match op { + ir::StrOp3::Replace => duck_replace(s, x, y), + ir::StrOp3::Translate => duck_translate(s, x, y), + } + }; + ctx.regs[dst] = RegVal::Str(ctx.arena.push_str(&out)); + Ok(()) + }) + } + Inst::Str2i { op, dst, a, n } => { + let (dst, a, n) = (sl(slots, dst), sl(slots, a), sl(slots, n)); + match op { + ir::StrOp2i::Repeat => Box::new(move |ctx| { + let out = duck_repeat( + ctx.arena.get(as_str(ctx.regs[a])), + as_i64(ctx.regs[n]), + )?; + ctx.regs[dst] = RegVal::Str(ctx.arena.push_str(&out)); + Ok(()) + }), + ir::StrOp2i::Extract => Box::new(move |ctx| { + let sref = as_str(ctx.regs[a]); + let rng = extract_window(ctx.arena.get(sref), as_i64(ctx.regs[n])); + // The extracted char is a subview of the input span. + ctx.regs[dst] = RegVal::Str(StrRef { + off: sref.off + rng.start, + len: rng.end - rng.start, + }); + Ok(()) + }), + } + } + Inst::Spad { + left, + dst, + a, + len, + pad, + } => { + let (dst, a, len, pad) = (sl(slots, dst), sl(slots, a), sl(slots, len), sl(slots, pad)); + Box::new(move |ctx| { + let out = duck_pad( + left, + ctx.arena.get(as_str(ctx.regs[a])), + as_i64(ctx.regs[len]), + ctx.arena.get(as_str(ctx.regs[pad])), + )?; + ctx.regs[dst] = RegVal::Str(ctx.arena.push_str(&out)); + Ok(()) + }) + } + Inst::Sslice { dst, a, lo, hi } => { + let (dst, a, lo, hi) = (sl(slots, dst), sl(slots, a), sl(slots, lo), sl(slots, hi)); + Box::new(move |ctx| { + let sref = as_str(ctx.regs[a]); + let rng = slice_window( + ctx.arena.get(sref), + as_i64(ctx.regs[lo]), + as_i64(ctx.regs[hi]), + ); + // The slice is a subview of the input span — no copy. + ctx.regs[dst] = RegVal::Str(StrRef { + off: sref.off + rng.start, + len: rng.end - rng.start, + }); + Ok(()) + }) + } + Inst::Sord { + empty_zero, + dst, + a, + } => { + let (dst, a) = (sl(slots, dst), sl(slots, a)); + Box::new(move |ctx| { + let s = ctx.arena.get(as_str(ctx.regs[a])); + ctx.regs[dst] = RegVal::I64(duck_ord(s, empty_zero)); + Ok(()) + }) + } Inst::SLen { bytes, dst, a } => { let (dst, a) = (sl(slots, dst), sl(slots, a)); Box::new(move |ctx| { @@ -1265,16 +1717,29 @@ fn compile_inst(p: &Program, inst: &Inst, slots: &HashMap) -> InstFn { } Inst::Str1 { op, dst, a } => { let (dst, a) = (sl(slots, dst), sl(slots, a)); - // DuckDB uses SIMPLE (1:1) case maps; casemap.rs carries the - // measured exception table over Rust's full maps. - let map: fn(char) -> char = match op { - StrOp1::Upper => super::casemap::simple_upper, - StrOp1::Lower => super::casemap::simple_lower, - }; - Box::new(move |ctx| { - ctx.regs[dst] = RegVal::Str(ctx.arena.case_map(as_str(ctx.regs[a]), map)); - Ok(()) - }) + match op { + // DuckDB uses SIMPLE (1:1) case maps; casemap.rs carries + // the measured exception table over Rust's full maps. + StrOp1::Upper | StrOp1::Lower => { + let map: fn(char) -> char = match op { + StrOp1::Upper => super::casemap::simple_upper, + _ => super::casemap::simple_lower, + }; + Box::new(move |ctx| { + ctx.regs[dst] = RegVal::Str(ctx.arena.case_map(as_str(ctx.regs[a]), map)); + Ok(()) + }) + } + StrOp1::StripAccents => Box::new(move |ctx| { + let sref = as_str(ctx.regs[a]); + let out = duck_strip_accents(ctx.arena.get(sref)); + ctx.regs[dst] = match out { + None => RegVal::Str(sref), // ASCII fast path: verbatim + Some(s) => RegVal::Str(ctx.arena.push_str(&s)), + }; + Ok(()) + }), + } } Inst::Strim { side, @@ -1334,12 +1799,15 @@ fn compile_inst(p: &Program, inst: &Inst, slots: &HashMap) -> InstFn { Inst::Num1 { op, dst, a } => { let (dst, a) = (sl(slots, dst), sl(slots, a)); match op { - NumOp1::Iabs => Box::new(move |ctx| match as_i64(ctx.regs[a]).checked_abs() { - Some(v) => { - ctx.regs[dst] = RegVal::I64(v); - Ok(()) + NumOp1::Iabs => Box::new(move |ctx| { + let x = as_i64(ctx.regs[a]); + match x.checked_abs() { + Some(v) => { + ctx.regs[dst] = RegVal::I64(v); + Ok(()) + } + None => Err(Trap(abs_overflow_msg(x))), } - None => Err(Trap("i64 overflow in iabs".to_string())), }), NumOp1::Fabs => Box::new(move |ctx| { ctx.regs[dst] = RegVal::F64(as_f64(ctx.regs[a]).abs()); diff --git a/src/specializer/exec/mod.rs b/src/specializer/exec/mod.rs index 84099c5..b06baa5 100644 --- a/src/specializer/exec/mod.rs +++ b/src/specializer/exec/mod.rs @@ -14,6 +14,7 @@ pub mod casemap; pub mod cranelift; pub mod interp; mod pow10; +mod strip_accents; #[cfg(test)] mod tests; diff --git a/src/specializer/exec/strip_accents.rs b/src/specializer/exec/strip_accents.rs new file mode 100644 index 0000000..a24cc75 --- /dev/null +++ b/src/specializer/exec/strip_accents.rs @@ -0,0 +1,4489 @@ +//! DuckDB's strip_accents() per-codepoint map, as measured — see +//! scripts/gen_strip_accents.py for the model (ASCII fast path, NUL +//! truncation, this map, then Hangul jamo composition) and why a host +//! Unicode library would be the wrong spec (utf8proc version skew). +//! +//! GENERATED by `uv run python scripts/gen_strip_accents.py` against +//! duckdb-python 1.5.5; regenerate after a duckdb bump. The oracle +//! census in tests/test_duckdb_interpreter.py is the authority. + +/// Per-codepoint result: `None` = unchanged, `Some(None)` = deleted, +/// `Some(Some(m))` = mapped 1:1. +pub(super) fn strip_map(c: char) -> Option> { + if (c as u32) < 0x80 { + return None; + } + if STRIP_DELETED.binary_search(&c).is_ok() { + return Some(None); + } + if let Ok(i) = STRIP_MAPPED.binary_search_by_key(&c, |e| e.0) { + return Some(Some(STRIP_MAPPED[i].1)); + } + None +} + +pub(super) const STRIP_DELETED: &[char] = &[ + '\u{0300}', + '\u{0301}', + '\u{0302}', + '\u{0303}', + '\u{0304}', + '\u{0305}', + '\u{0306}', + '\u{0307}', + '\u{0308}', + '\u{0309}', + '\u{030A}', + '\u{030B}', + '\u{030C}', + '\u{030D}', + '\u{030E}', + '\u{030F}', + '\u{0310}', + '\u{0311}', + '\u{0312}', + '\u{0313}', + '\u{0314}', + '\u{0315}', + '\u{0316}', + '\u{0317}', + '\u{0318}', + '\u{0319}', + '\u{031A}', + '\u{031B}', + '\u{031C}', + '\u{031D}', + '\u{031E}', + '\u{031F}', + '\u{0320}', + '\u{0321}', + '\u{0322}', + '\u{0323}', + '\u{0324}', + '\u{0325}', + '\u{0326}', + '\u{0327}', + '\u{0328}', + '\u{0329}', + '\u{032A}', + '\u{032B}', + '\u{032C}', + '\u{032D}', + '\u{032E}', + '\u{032F}', + '\u{0330}', + '\u{0331}', + '\u{0332}', + '\u{0333}', + '\u{0334}', + '\u{0335}', + '\u{0336}', + '\u{0337}', + '\u{0338}', + '\u{0339}', + '\u{033A}', + '\u{033B}', + '\u{033C}', + '\u{033D}', + '\u{033E}', + '\u{033F}', + '\u{0340}', + '\u{0341}', + '\u{0342}', + '\u{0343}', + '\u{0344}', + '\u{0345}', + '\u{0346}', + '\u{0347}', + '\u{0348}', + '\u{0349}', + '\u{034A}', + '\u{034B}', + '\u{034C}', + '\u{034D}', + '\u{034E}', + '\u{034F}', + '\u{0350}', + '\u{0351}', + '\u{0352}', + '\u{0353}', + '\u{0354}', + '\u{0355}', + '\u{0356}', + '\u{0357}', + '\u{0358}', + '\u{0359}', + '\u{035A}', + '\u{035B}', + '\u{035C}', + '\u{035D}', + '\u{035E}', + '\u{035F}', + '\u{0360}', + '\u{0361}', + '\u{0362}', + '\u{0363}', + '\u{0364}', + '\u{0365}', + '\u{0366}', + '\u{0367}', + '\u{0368}', + '\u{0369}', + '\u{036A}', + '\u{036B}', + '\u{036C}', + '\u{036D}', + '\u{036E}', + '\u{036F}', + '\u{0483}', + '\u{0484}', + '\u{0485}', + '\u{0486}', + '\u{0487}', + '\u{0488}', + '\u{0489}', + '\u{0591}', + '\u{0592}', + '\u{0593}', + '\u{0594}', + '\u{0595}', + '\u{0596}', + '\u{0597}', + '\u{0598}', + '\u{0599}', + '\u{059A}', + '\u{059B}', + '\u{059C}', + '\u{059D}', + '\u{059E}', + '\u{059F}', + '\u{05A0}', + '\u{05A1}', + '\u{05A2}', + '\u{05A3}', + '\u{05A4}', + '\u{05A5}', + '\u{05A6}', + '\u{05A7}', + '\u{05A8}', + '\u{05A9}', + '\u{05AA}', + '\u{05AB}', + '\u{05AC}', + '\u{05AD}', + '\u{05AE}', + '\u{05AF}', + '\u{05B0}', + '\u{05B1}', + '\u{05B2}', + '\u{05B3}', + '\u{05B4}', + '\u{05B5}', + '\u{05B6}', + '\u{05B7}', + '\u{05B8}', + '\u{05B9}', + '\u{05BA}', + '\u{05BB}', + '\u{05BC}', + '\u{05BD}', + '\u{05BF}', + '\u{05C1}', + '\u{05C2}', + '\u{05C4}', + '\u{05C5}', + '\u{05C7}', + '\u{0610}', + '\u{0611}', + '\u{0612}', + '\u{0613}', + '\u{0614}', + '\u{0615}', + '\u{0616}', + '\u{0617}', + '\u{0618}', + '\u{0619}', + '\u{061A}', + '\u{064B}', + '\u{064C}', + '\u{064D}', + '\u{064E}', + '\u{064F}', + '\u{0650}', + '\u{0651}', + '\u{0652}', + '\u{0653}', + '\u{0654}', + '\u{0655}', + '\u{0656}', + '\u{0657}', + '\u{0658}', + '\u{0659}', + '\u{065A}', + '\u{065B}', + '\u{065C}', + '\u{065D}', + '\u{065E}', + '\u{065F}', + '\u{0670}', + '\u{06D6}', + '\u{06D7}', + '\u{06D8}', + '\u{06D9}', + '\u{06DA}', + '\u{06DB}', + '\u{06DC}', + '\u{06DF}', + '\u{06E0}', + '\u{06E1}', + '\u{06E2}', + '\u{06E3}', + '\u{06E4}', + '\u{06E7}', + '\u{06E8}', + '\u{06EA}', + '\u{06EB}', + '\u{06EC}', + '\u{06ED}', + '\u{0711}', + '\u{0730}', + '\u{0731}', + '\u{0732}', + '\u{0733}', + '\u{0734}', + '\u{0735}', + '\u{0736}', + '\u{0737}', + '\u{0738}', + '\u{0739}', + '\u{073A}', + '\u{073B}', + '\u{073C}', + '\u{073D}', + '\u{073E}', + '\u{073F}', + '\u{0740}', + '\u{0741}', + '\u{0742}', + '\u{0743}', + '\u{0744}', + '\u{0745}', + '\u{0746}', + '\u{0747}', + '\u{0748}', + '\u{0749}', + '\u{074A}', + '\u{07A6}', + '\u{07A7}', + '\u{07A8}', + '\u{07A9}', + '\u{07AA}', + '\u{07AB}', + '\u{07AC}', + '\u{07AD}', + '\u{07AE}', + '\u{07AF}', + '\u{07B0}', + '\u{07EB}', + '\u{07EC}', + '\u{07ED}', + '\u{07EE}', + '\u{07EF}', + '\u{07F0}', + '\u{07F1}', + '\u{07F2}', + '\u{07F3}', + '\u{07FD}', + '\u{0816}', + '\u{0817}', + '\u{0818}', + '\u{0819}', + '\u{081B}', + '\u{081C}', + '\u{081D}', + '\u{081E}', + '\u{081F}', + '\u{0820}', + '\u{0821}', + '\u{0822}', + '\u{0823}', + '\u{0825}', + '\u{0826}', + '\u{0827}', + '\u{0829}', + '\u{082A}', + '\u{082B}', + '\u{082C}', + '\u{082D}', + '\u{0859}', + '\u{085A}', + '\u{085B}', + '\u{0898}', + '\u{0899}', + '\u{089A}', + '\u{089B}', + '\u{089C}', + '\u{089D}', + '\u{089E}', + '\u{089F}', + '\u{08CA}', + '\u{08CB}', + '\u{08CC}', + '\u{08CD}', + '\u{08CE}', + '\u{08CF}', + '\u{08D0}', + '\u{08D1}', + '\u{08D2}', + '\u{08D3}', + '\u{08D4}', + '\u{08D5}', + '\u{08D6}', + '\u{08D7}', + '\u{08D8}', + '\u{08D9}', + '\u{08DA}', + '\u{08DB}', + '\u{08DC}', + '\u{08DD}', + '\u{08DE}', + '\u{08DF}', + '\u{08E0}', + '\u{08E1}', + '\u{08E3}', + '\u{08E4}', + '\u{08E5}', + '\u{08E6}', + '\u{08E7}', + '\u{08E8}', + '\u{08E9}', + '\u{08EA}', + '\u{08EB}', + '\u{08EC}', + '\u{08ED}', + '\u{08EE}', + '\u{08EF}', + '\u{08F0}', + '\u{08F1}', + '\u{08F2}', + '\u{08F3}', + '\u{08F4}', + '\u{08F5}', + '\u{08F6}', + '\u{08F7}', + '\u{08F8}', + '\u{08F9}', + '\u{08FA}', + '\u{08FB}', + '\u{08FC}', + '\u{08FD}', + '\u{08FE}', + '\u{08FF}', + '\u{0900}', + '\u{0901}', + '\u{0902}', + '\u{0903}', + '\u{093A}', + '\u{093B}', + '\u{093C}', + '\u{093E}', + '\u{093F}', + '\u{0940}', + '\u{0941}', + '\u{0942}', + '\u{0943}', + '\u{0944}', + '\u{0945}', + '\u{0946}', + '\u{0947}', + '\u{0948}', + '\u{0949}', + '\u{094A}', + '\u{094B}', + '\u{094C}', + '\u{094D}', + '\u{094E}', + '\u{094F}', + '\u{0951}', + '\u{0952}', + '\u{0953}', + '\u{0954}', + '\u{0955}', + '\u{0956}', + '\u{0957}', + '\u{0962}', + '\u{0963}', + '\u{0981}', + '\u{0982}', + '\u{0983}', + '\u{09BC}', + '\u{09BE}', + '\u{09BF}', + '\u{09C0}', + '\u{09C1}', + '\u{09C2}', + '\u{09C3}', + '\u{09C4}', + '\u{09C7}', + '\u{09C8}', + '\u{09CB}', + '\u{09CC}', + '\u{09CD}', + '\u{09D7}', + '\u{09E2}', + '\u{09E3}', + '\u{09FE}', + '\u{0A01}', + '\u{0A02}', + '\u{0A03}', + '\u{0A3C}', + '\u{0A3E}', + '\u{0A3F}', + '\u{0A40}', + '\u{0A41}', + '\u{0A42}', + '\u{0A47}', + '\u{0A48}', + '\u{0A4B}', + '\u{0A4C}', + '\u{0A4D}', + '\u{0A51}', + '\u{0A70}', + '\u{0A71}', + '\u{0A75}', + '\u{0A81}', + '\u{0A82}', + '\u{0A83}', + '\u{0ABC}', + '\u{0ABE}', + '\u{0ABF}', + '\u{0AC0}', + '\u{0AC1}', + '\u{0AC2}', + '\u{0AC3}', + '\u{0AC4}', + '\u{0AC5}', + '\u{0AC7}', + '\u{0AC8}', + '\u{0AC9}', + '\u{0ACB}', + '\u{0ACC}', + '\u{0ACD}', + '\u{0AE2}', + '\u{0AE3}', + '\u{0AFA}', + '\u{0AFB}', + '\u{0AFC}', + '\u{0AFD}', + '\u{0AFE}', + '\u{0AFF}', + '\u{0B01}', + '\u{0B02}', + '\u{0B03}', + '\u{0B3C}', + '\u{0B3E}', + '\u{0B3F}', + '\u{0B40}', + '\u{0B41}', + '\u{0B42}', + '\u{0B43}', + '\u{0B44}', + '\u{0B47}', + '\u{0B48}', + '\u{0B4B}', + '\u{0B4C}', + '\u{0B4D}', + '\u{0B55}', + '\u{0B56}', + '\u{0B57}', + '\u{0B62}', + '\u{0B63}', + '\u{0B82}', + '\u{0BBE}', + '\u{0BBF}', + '\u{0BC0}', + '\u{0BC1}', + '\u{0BC2}', + '\u{0BC6}', + '\u{0BC7}', + '\u{0BC8}', + '\u{0BCA}', + '\u{0BCB}', + '\u{0BCC}', + '\u{0BCD}', + '\u{0BD7}', + '\u{0C00}', + '\u{0C01}', + '\u{0C02}', + '\u{0C03}', + '\u{0C04}', + '\u{0C3C}', + '\u{0C3E}', + '\u{0C3F}', + '\u{0C40}', + '\u{0C41}', + '\u{0C42}', + '\u{0C43}', + '\u{0C44}', + '\u{0C46}', + '\u{0C47}', + '\u{0C48}', + '\u{0C4A}', + '\u{0C4B}', + '\u{0C4C}', + '\u{0C4D}', + '\u{0C55}', + '\u{0C56}', + '\u{0C62}', + '\u{0C63}', + '\u{0C81}', + '\u{0C82}', + '\u{0C83}', + '\u{0CBC}', + '\u{0CBE}', + '\u{0CBF}', + '\u{0CC0}', + '\u{0CC1}', + '\u{0CC2}', + '\u{0CC3}', + '\u{0CC4}', + '\u{0CC6}', + '\u{0CC7}', + '\u{0CC8}', + '\u{0CCA}', + '\u{0CCB}', + '\u{0CCC}', + '\u{0CCD}', + '\u{0CD5}', + '\u{0CD6}', + '\u{0CE2}', + '\u{0CE3}', + '\u{0CF3}', + '\u{0D00}', + '\u{0D01}', + '\u{0D02}', + '\u{0D03}', + '\u{0D3B}', + '\u{0D3C}', + '\u{0D3E}', + '\u{0D3F}', + '\u{0D40}', + '\u{0D41}', + '\u{0D42}', + '\u{0D43}', + '\u{0D44}', + '\u{0D46}', + '\u{0D47}', + '\u{0D48}', + '\u{0D4A}', + '\u{0D4B}', + '\u{0D4C}', + '\u{0D4D}', + '\u{0D57}', + '\u{0D62}', + '\u{0D63}', + '\u{0D81}', + '\u{0D82}', + '\u{0D83}', + '\u{0DCA}', + '\u{0DCF}', + '\u{0DD0}', + '\u{0DD1}', + '\u{0DD2}', + '\u{0DD3}', + '\u{0DD4}', + '\u{0DD6}', + '\u{0DD8}', + '\u{0DD9}', + '\u{0DDA}', + '\u{0DDB}', + '\u{0DDC}', + '\u{0DDD}', + '\u{0DDE}', + '\u{0DDF}', + '\u{0DF2}', + '\u{0DF3}', + '\u{0E31}', + '\u{0E34}', + '\u{0E35}', + '\u{0E36}', + '\u{0E37}', + '\u{0E38}', + '\u{0E39}', + '\u{0E3A}', + '\u{0E47}', + '\u{0E48}', + '\u{0E49}', + '\u{0E4A}', + '\u{0E4B}', + '\u{0E4C}', + '\u{0E4D}', + '\u{0E4E}', + '\u{0EB1}', + '\u{0EB4}', + '\u{0EB5}', + '\u{0EB6}', + '\u{0EB7}', + '\u{0EB8}', + '\u{0EB9}', + '\u{0EBA}', + '\u{0EBB}', + '\u{0EBC}', + '\u{0EC8}', + '\u{0EC9}', + '\u{0ECA}', + '\u{0ECB}', + '\u{0ECC}', + '\u{0ECD}', + '\u{0ECE}', + '\u{0F18}', + '\u{0F19}', + '\u{0F35}', + '\u{0F37}', + '\u{0F39}', + '\u{0F3E}', + '\u{0F3F}', + '\u{0F71}', + '\u{0F72}', + '\u{0F73}', + '\u{0F74}', + '\u{0F75}', + '\u{0F76}', + '\u{0F77}', + '\u{0F78}', + '\u{0F79}', + '\u{0F7A}', + '\u{0F7B}', + '\u{0F7C}', + '\u{0F7D}', + '\u{0F7E}', + '\u{0F7F}', + '\u{0F80}', + '\u{0F81}', + '\u{0F82}', + '\u{0F83}', + '\u{0F84}', + '\u{0F86}', + '\u{0F87}', + '\u{0F8D}', + '\u{0F8E}', + '\u{0F8F}', + '\u{0F90}', + '\u{0F91}', + '\u{0F92}', + '\u{0F93}', + '\u{0F94}', + '\u{0F95}', + '\u{0F96}', + '\u{0F97}', + '\u{0F99}', + '\u{0F9A}', + '\u{0F9B}', + '\u{0F9C}', + '\u{0F9D}', + '\u{0F9E}', + '\u{0F9F}', + '\u{0FA0}', + '\u{0FA1}', + '\u{0FA2}', + '\u{0FA3}', + '\u{0FA4}', + '\u{0FA5}', + '\u{0FA6}', + '\u{0FA7}', + '\u{0FA8}', + '\u{0FA9}', + '\u{0FAA}', + '\u{0FAB}', + '\u{0FAC}', + '\u{0FAD}', + '\u{0FAE}', + '\u{0FAF}', + '\u{0FB0}', + '\u{0FB1}', + '\u{0FB2}', + '\u{0FB3}', + '\u{0FB4}', + '\u{0FB5}', + '\u{0FB6}', + '\u{0FB7}', + '\u{0FB8}', + '\u{0FB9}', + '\u{0FBA}', + '\u{0FBB}', + '\u{0FBC}', + '\u{0FC6}', + '\u{102B}', + '\u{102C}', + '\u{102D}', + '\u{102E}', + '\u{102F}', + '\u{1030}', + '\u{1031}', + '\u{1032}', + '\u{1033}', + '\u{1034}', + '\u{1035}', + '\u{1036}', + '\u{1037}', + '\u{1038}', + '\u{1039}', + '\u{103A}', + '\u{103B}', + '\u{103C}', + '\u{103D}', + '\u{103E}', + '\u{1056}', + '\u{1057}', + '\u{1058}', + '\u{1059}', + '\u{105E}', + '\u{105F}', + '\u{1060}', + '\u{1062}', + '\u{1063}', + '\u{1064}', + '\u{1067}', + '\u{1068}', + '\u{1069}', + '\u{106A}', + '\u{106B}', + '\u{106C}', + '\u{106D}', + '\u{1071}', + '\u{1072}', + '\u{1073}', + '\u{1074}', + '\u{1082}', + '\u{1083}', + '\u{1084}', + '\u{1085}', + '\u{1086}', + '\u{1087}', + '\u{1088}', + '\u{1089}', + '\u{108A}', + '\u{108B}', + '\u{108C}', + '\u{108D}', + '\u{108F}', + '\u{109A}', + '\u{109B}', + '\u{109C}', + '\u{109D}', + '\u{135D}', + '\u{135E}', + '\u{135F}', + '\u{1712}', + '\u{1713}', + '\u{1714}', + '\u{1715}', + '\u{1732}', + '\u{1733}', + '\u{1734}', + '\u{1752}', + '\u{1753}', + '\u{1772}', + '\u{1773}', + '\u{17B4}', + '\u{17B5}', + '\u{17B6}', + '\u{17B7}', + '\u{17B8}', + '\u{17B9}', + '\u{17BA}', + '\u{17BB}', + '\u{17BC}', + '\u{17BD}', + '\u{17BE}', + '\u{17BF}', + '\u{17C0}', + '\u{17C1}', + '\u{17C2}', + '\u{17C3}', + '\u{17C4}', + '\u{17C5}', + '\u{17C6}', + '\u{17C7}', + '\u{17C8}', + '\u{17C9}', + '\u{17CA}', + '\u{17CB}', + '\u{17CC}', + '\u{17CD}', + '\u{17CE}', + '\u{17CF}', + '\u{17D0}', + '\u{17D1}', + '\u{17D2}', + '\u{17D3}', + '\u{17DD}', + '\u{180B}', + '\u{180C}', + '\u{180D}', + '\u{180F}', + '\u{1885}', + '\u{1886}', + '\u{18A9}', + '\u{1920}', + '\u{1921}', + '\u{1922}', + '\u{1923}', + '\u{1924}', + '\u{1925}', + '\u{1926}', + '\u{1927}', + '\u{1928}', + '\u{1929}', + '\u{192A}', + '\u{192B}', + '\u{1930}', + '\u{1931}', + '\u{1932}', + '\u{1933}', + '\u{1934}', + '\u{1935}', + '\u{1936}', + '\u{1937}', + '\u{1938}', + '\u{1939}', + '\u{193A}', + '\u{193B}', + '\u{1A17}', + '\u{1A18}', + '\u{1A19}', + '\u{1A1A}', + '\u{1A1B}', + '\u{1A55}', + '\u{1A56}', + '\u{1A57}', + '\u{1A58}', + '\u{1A59}', + '\u{1A5A}', + '\u{1A5B}', + '\u{1A5C}', + '\u{1A5D}', + '\u{1A5E}', + '\u{1A60}', + '\u{1A61}', + '\u{1A62}', + '\u{1A63}', + '\u{1A64}', + '\u{1A65}', + '\u{1A66}', + '\u{1A67}', + '\u{1A68}', + '\u{1A69}', + '\u{1A6A}', + '\u{1A6B}', + '\u{1A6C}', + '\u{1A6D}', + '\u{1A6E}', + '\u{1A6F}', + '\u{1A70}', + '\u{1A71}', + '\u{1A72}', + '\u{1A73}', + '\u{1A74}', + '\u{1A75}', + '\u{1A76}', + '\u{1A77}', + '\u{1A78}', + '\u{1A79}', + '\u{1A7A}', + '\u{1A7B}', + '\u{1A7C}', + '\u{1A7F}', + '\u{1AB0}', + '\u{1AB1}', + '\u{1AB2}', + '\u{1AB3}', + '\u{1AB4}', + '\u{1AB5}', + '\u{1AB6}', + '\u{1AB7}', + '\u{1AB8}', + '\u{1AB9}', + '\u{1ABA}', + '\u{1ABB}', + '\u{1ABC}', + '\u{1ABD}', + '\u{1ABE}', + '\u{1ABF}', + '\u{1AC0}', + '\u{1AC1}', + '\u{1AC2}', + '\u{1AC3}', + '\u{1AC4}', + '\u{1AC5}', + '\u{1AC6}', + '\u{1AC7}', + '\u{1AC8}', + '\u{1AC9}', + '\u{1ACA}', + '\u{1ACB}', + '\u{1ACC}', + '\u{1ACD}', + '\u{1ACE}', + '\u{1B00}', + '\u{1B01}', + '\u{1B02}', + '\u{1B03}', + '\u{1B04}', + '\u{1B34}', + '\u{1B35}', + '\u{1B36}', + '\u{1B37}', + '\u{1B38}', + '\u{1B39}', + '\u{1B3A}', + '\u{1B3B}', + '\u{1B3C}', + '\u{1B3D}', + '\u{1B3E}', + '\u{1B3F}', + '\u{1B40}', + '\u{1B41}', + '\u{1B42}', + '\u{1B43}', + '\u{1B44}', + '\u{1B6B}', + '\u{1B6C}', + '\u{1B6D}', + '\u{1B6E}', + '\u{1B6F}', + '\u{1B70}', + '\u{1B71}', + '\u{1B72}', + '\u{1B73}', + '\u{1B80}', + '\u{1B81}', + '\u{1B82}', + '\u{1BA1}', + '\u{1BA2}', + '\u{1BA3}', + '\u{1BA4}', + '\u{1BA5}', + '\u{1BA6}', + '\u{1BA7}', + '\u{1BA8}', + '\u{1BA9}', + '\u{1BAA}', + '\u{1BAB}', + '\u{1BAC}', + '\u{1BAD}', + '\u{1BE6}', + '\u{1BE7}', + '\u{1BE8}', + '\u{1BE9}', + '\u{1BEA}', + '\u{1BEB}', + '\u{1BEC}', + '\u{1BED}', + '\u{1BEE}', + '\u{1BEF}', + '\u{1BF0}', + '\u{1BF1}', + '\u{1BF2}', + '\u{1BF3}', + '\u{1C24}', + '\u{1C25}', + '\u{1C26}', + '\u{1C27}', + '\u{1C28}', + '\u{1C29}', + '\u{1C2A}', + '\u{1C2B}', + '\u{1C2C}', + '\u{1C2D}', + '\u{1C2E}', + '\u{1C2F}', + '\u{1C30}', + '\u{1C31}', + '\u{1C32}', + '\u{1C33}', + '\u{1C34}', + '\u{1C35}', + '\u{1C36}', + '\u{1C37}', + '\u{1CD0}', + '\u{1CD1}', + '\u{1CD2}', + '\u{1CD4}', + '\u{1CD5}', + '\u{1CD6}', + '\u{1CD7}', + '\u{1CD8}', + '\u{1CD9}', + '\u{1CDA}', + '\u{1CDB}', + '\u{1CDC}', + '\u{1CDD}', + '\u{1CDE}', + '\u{1CDF}', + '\u{1CE0}', + '\u{1CE1}', + '\u{1CE2}', + '\u{1CE3}', + '\u{1CE4}', + '\u{1CE5}', + '\u{1CE6}', + '\u{1CE7}', + '\u{1CE8}', + '\u{1CED}', + '\u{1CF4}', + '\u{1CF7}', + '\u{1CF8}', + '\u{1CF9}', + '\u{1DC0}', + '\u{1DC1}', + '\u{1DC2}', + '\u{1DC3}', + '\u{1DC4}', + '\u{1DC5}', + '\u{1DC6}', + '\u{1DC7}', + '\u{1DC8}', + '\u{1DC9}', + '\u{1DCA}', + '\u{1DCB}', + '\u{1DCC}', + '\u{1DCD}', + '\u{1DCE}', + '\u{1DCF}', + '\u{1DD0}', + '\u{1DD1}', + '\u{1DD2}', + '\u{1DD3}', + '\u{1DD4}', + '\u{1DD5}', + '\u{1DD6}', + '\u{1DD7}', + '\u{1DD8}', + '\u{1DD9}', + '\u{1DDA}', + '\u{1DDB}', + '\u{1DDC}', + '\u{1DDD}', + '\u{1DDE}', + '\u{1DDF}', + '\u{1DE0}', + '\u{1DE1}', + '\u{1DE2}', + '\u{1DE3}', + '\u{1DE4}', + '\u{1DE5}', + '\u{1DE6}', + '\u{1DE7}', + '\u{1DE8}', + '\u{1DE9}', + '\u{1DEA}', + '\u{1DEB}', + '\u{1DEC}', + '\u{1DED}', + '\u{1DEE}', + '\u{1DEF}', + '\u{1DF0}', + '\u{1DF1}', + '\u{1DF2}', + '\u{1DF3}', + '\u{1DF4}', + '\u{1DF5}', + '\u{1DF6}', + '\u{1DF7}', + '\u{1DF8}', + '\u{1DF9}', + '\u{1DFA}', + '\u{1DFB}', + '\u{1DFC}', + '\u{1DFD}', + '\u{1DFE}', + '\u{1DFF}', + '\u{20D0}', + '\u{20D1}', + '\u{20D2}', + '\u{20D3}', + '\u{20D4}', + '\u{20D5}', + '\u{20D6}', + '\u{20D7}', + '\u{20D8}', + '\u{20D9}', + '\u{20DA}', + '\u{20DB}', + '\u{20DC}', + '\u{20DD}', + '\u{20DE}', + '\u{20DF}', + '\u{20E0}', + '\u{20E1}', + '\u{20E2}', + '\u{20E3}', + '\u{20E4}', + '\u{20E5}', + '\u{20E6}', + '\u{20E7}', + '\u{20E8}', + '\u{20E9}', + '\u{20EA}', + '\u{20EB}', + '\u{20EC}', + '\u{20ED}', + '\u{20EE}', + '\u{20EF}', + '\u{20F0}', + '\u{2CEF}', + '\u{2CF0}', + '\u{2CF1}', + '\u{2D7F}', + '\u{2DE0}', + '\u{2DE1}', + '\u{2DE2}', + '\u{2DE3}', + '\u{2DE4}', + '\u{2DE5}', + '\u{2DE6}', + '\u{2DE7}', + '\u{2DE8}', + '\u{2DE9}', + '\u{2DEA}', + '\u{2DEB}', + '\u{2DEC}', + '\u{2DED}', + '\u{2DEE}', + '\u{2DEF}', + '\u{2DF0}', + '\u{2DF1}', + '\u{2DF2}', + '\u{2DF3}', + '\u{2DF4}', + '\u{2DF5}', + '\u{2DF6}', + '\u{2DF7}', + '\u{2DF8}', + '\u{2DF9}', + '\u{2DFA}', + '\u{2DFB}', + '\u{2DFC}', + '\u{2DFD}', + '\u{2DFE}', + '\u{2DFF}', + '\u{302A}', + '\u{302B}', + '\u{302C}', + '\u{302D}', + '\u{302E}', + '\u{302F}', + '\u{3099}', + '\u{309A}', + '\u{A66F}', + '\u{A670}', + '\u{A671}', + '\u{A672}', + '\u{A674}', + '\u{A675}', + '\u{A676}', + '\u{A677}', + '\u{A678}', + '\u{A679}', + '\u{A67A}', + '\u{A67B}', + '\u{A67C}', + '\u{A67D}', + '\u{A69E}', + '\u{A69F}', + '\u{A6F0}', + '\u{A6F1}', + '\u{A802}', + '\u{A806}', + '\u{A80B}', + '\u{A823}', + '\u{A824}', + '\u{A825}', + '\u{A826}', + '\u{A827}', + '\u{A82C}', + '\u{A880}', + '\u{A881}', + '\u{A8B4}', + '\u{A8B5}', + '\u{A8B6}', + '\u{A8B7}', + '\u{A8B8}', + '\u{A8B9}', + '\u{A8BA}', + '\u{A8BB}', + '\u{A8BC}', + '\u{A8BD}', + '\u{A8BE}', + '\u{A8BF}', + '\u{A8C0}', + '\u{A8C1}', + '\u{A8C2}', + '\u{A8C3}', + '\u{A8C4}', + '\u{A8C5}', + '\u{A8E0}', + '\u{A8E1}', + '\u{A8E2}', + '\u{A8E3}', + '\u{A8E4}', + '\u{A8E5}', + '\u{A8E6}', + '\u{A8E7}', + '\u{A8E8}', + '\u{A8E9}', + '\u{A8EA}', + '\u{A8EB}', + '\u{A8EC}', + '\u{A8ED}', + '\u{A8EE}', + '\u{A8EF}', + '\u{A8F0}', + '\u{A8F1}', + '\u{A8FF}', + '\u{A926}', + '\u{A927}', + '\u{A928}', + '\u{A929}', + '\u{A92A}', + '\u{A92B}', + '\u{A92C}', + '\u{A92D}', + '\u{A947}', + '\u{A948}', + '\u{A949}', + '\u{A94A}', + '\u{A94B}', + '\u{A94C}', + '\u{A94D}', + '\u{A94E}', + '\u{A94F}', + '\u{A950}', + '\u{A951}', + '\u{A952}', + '\u{A953}', + '\u{A980}', + '\u{A981}', + '\u{A982}', + '\u{A983}', + '\u{A9B3}', + '\u{A9B4}', + '\u{A9B5}', + '\u{A9B6}', + '\u{A9B7}', + '\u{A9B8}', + '\u{A9B9}', + '\u{A9BA}', + '\u{A9BB}', + '\u{A9BC}', + '\u{A9BD}', + '\u{A9BE}', + '\u{A9BF}', + '\u{A9C0}', + '\u{A9E5}', + '\u{AA29}', + '\u{AA2A}', + '\u{AA2B}', + '\u{AA2C}', + '\u{AA2D}', + '\u{AA2E}', + '\u{AA2F}', + '\u{AA30}', + '\u{AA31}', + '\u{AA32}', + '\u{AA33}', + '\u{AA34}', + '\u{AA35}', + '\u{AA36}', + '\u{AA43}', + '\u{AA4C}', + '\u{AA4D}', + '\u{AA7B}', + '\u{AA7C}', + '\u{AA7D}', + '\u{AAB0}', + '\u{AAB2}', + '\u{AAB3}', + '\u{AAB4}', + '\u{AAB7}', + '\u{AAB8}', + '\u{AABE}', + '\u{AABF}', + '\u{AAC1}', + '\u{AAEB}', + '\u{AAEC}', + '\u{AAED}', + '\u{AAEE}', + '\u{AAEF}', + '\u{AAF5}', + '\u{AAF6}', + '\u{ABE3}', + '\u{ABE4}', + '\u{ABE5}', + '\u{ABE6}', + '\u{ABE7}', + '\u{ABE8}', + '\u{ABE9}', + '\u{ABEA}', + '\u{ABEC}', + '\u{ABED}', + '\u{FB1E}', + '\u{FE00}', + '\u{FE01}', + '\u{FE02}', + '\u{FE03}', + '\u{FE04}', + '\u{FE05}', + '\u{FE06}', + '\u{FE07}', + '\u{FE08}', + '\u{FE09}', + '\u{FE0A}', + '\u{FE0B}', + '\u{FE0C}', + '\u{FE0D}', + '\u{FE0E}', + '\u{FE0F}', + '\u{FE20}', + '\u{FE21}', + '\u{FE22}', + '\u{FE23}', + '\u{FE24}', + '\u{FE25}', + '\u{FE26}', + '\u{FE27}', + '\u{FE28}', + '\u{FE29}', + '\u{FE2A}', + '\u{FE2B}', + '\u{FE2C}', + '\u{FE2D}', + '\u{FE2E}', + '\u{FE2F}', + '\u{101FD}', + '\u{102E0}', + '\u{10376}', + '\u{10377}', + '\u{10378}', + '\u{10379}', + '\u{1037A}', + '\u{10A01}', + '\u{10A02}', + '\u{10A03}', + '\u{10A05}', + '\u{10A06}', + '\u{10A0C}', + '\u{10A0D}', + '\u{10A0E}', + '\u{10A0F}', + '\u{10A38}', + '\u{10A39}', + '\u{10A3A}', + '\u{10A3F}', + '\u{10AE5}', + '\u{10AE6}', + '\u{10D24}', + '\u{10D25}', + '\u{10D26}', + '\u{10D27}', + '\u{10EAB}', + '\u{10EAC}', + '\u{10EFD}', + '\u{10EFE}', + '\u{10EFF}', + '\u{10F46}', + '\u{10F47}', + '\u{10F48}', + '\u{10F49}', + '\u{10F4A}', + '\u{10F4B}', + '\u{10F4C}', + '\u{10F4D}', + '\u{10F4E}', + '\u{10F4F}', + '\u{10F50}', + '\u{10F82}', + '\u{10F83}', + '\u{10F84}', + '\u{10F85}', + '\u{11000}', + '\u{11001}', + '\u{11002}', + '\u{11038}', + '\u{11039}', + '\u{1103A}', + '\u{1103B}', + '\u{1103C}', + '\u{1103D}', + '\u{1103E}', + '\u{1103F}', + '\u{11040}', + '\u{11041}', + '\u{11042}', + '\u{11043}', + '\u{11044}', + '\u{11045}', + '\u{11046}', + '\u{11070}', + '\u{11073}', + '\u{11074}', + '\u{1107F}', + '\u{11080}', + '\u{11081}', + '\u{11082}', + '\u{110B0}', + '\u{110B1}', + '\u{110B2}', + '\u{110B3}', + '\u{110B4}', + '\u{110B5}', + '\u{110B6}', + '\u{110B7}', + '\u{110B8}', + '\u{110B9}', + '\u{110BA}', + '\u{110C2}', + '\u{11100}', + '\u{11101}', + '\u{11102}', + '\u{11127}', + '\u{11128}', + '\u{11129}', + '\u{1112A}', + '\u{1112B}', + '\u{1112C}', + '\u{1112D}', + '\u{1112E}', + '\u{1112F}', + '\u{11130}', + '\u{11131}', + '\u{11132}', + '\u{11133}', + '\u{11134}', + '\u{11145}', + '\u{11146}', + '\u{11173}', + '\u{11180}', + '\u{11181}', + '\u{11182}', + '\u{111B3}', + '\u{111B4}', + '\u{111B5}', + '\u{111B6}', + '\u{111B7}', + '\u{111B8}', + '\u{111B9}', + '\u{111BA}', + '\u{111BB}', + '\u{111BC}', + '\u{111BD}', + '\u{111BE}', + '\u{111BF}', + '\u{111C0}', + '\u{111C9}', + '\u{111CA}', + '\u{111CB}', + '\u{111CC}', + '\u{111CE}', + '\u{111CF}', + '\u{1122C}', + '\u{1122D}', + '\u{1122E}', + '\u{1122F}', + '\u{11230}', + '\u{11231}', + '\u{11232}', + '\u{11233}', + '\u{11234}', + '\u{11235}', + '\u{11236}', + '\u{11237}', + '\u{1123E}', + '\u{11241}', + '\u{112DF}', + '\u{112E0}', + '\u{112E1}', + '\u{112E2}', + '\u{112E3}', + '\u{112E4}', + '\u{112E5}', + '\u{112E6}', + '\u{112E7}', + '\u{112E8}', + '\u{112E9}', + '\u{112EA}', + '\u{11300}', + '\u{11301}', + '\u{11302}', + '\u{11303}', + '\u{1133B}', + '\u{1133C}', + '\u{1133E}', + '\u{1133F}', + '\u{11340}', + '\u{11341}', + '\u{11342}', + '\u{11343}', + '\u{11344}', + '\u{11347}', + '\u{11348}', + '\u{1134B}', + '\u{1134C}', + '\u{1134D}', + '\u{11357}', + '\u{11362}', + '\u{11363}', + '\u{11366}', + '\u{11367}', + '\u{11368}', + '\u{11369}', + '\u{1136A}', + '\u{1136B}', + '\u{1136C}', + '\u{11370}', + '\u{11371}', + '\u{11372}', + '\u{11373}', + '\u{11374}', + '\u{11435}', + '\u{11436}', + '\u{11437}', + '\u{11438}', + '\u{11439}', + '\u{1143A}', + '\u{1143B}', + '\u{1143C}', + '\u{1143D}', + '\u{1143E}', + '\u{1143F}', + '\u{11440}', + '\u{11441}', + '\u{11442}', + '\u{11443}', + '\u{11444}', + '\u{11445}', + '\u{11446}', + '\u{1145E}', + '\u{114B0}', + '\u{114B1}', + '\u{114B2}', + '\u{114B3}', + '\u{114B4}', + '\u{114B5}', + '\u{114B6}', + '\u{114B7}', + '\u{114B8}', + '\u{114B9}', + '\u{114BA}', + '\u{114BB}', + '\u{114BC}', + '\u{114BD}', + '\u{114BE}', + '\u{114BF}', + '\u{114C0}', + '\u{114C1}', + '\u{114C2}', + '\u{114C3}', + '\u{115AF}', + '\u{115B0}', + '\u{115B1}', + '\u{115B2}', + '\u{115B3}', + '\u{115B4}', + '\u{115B5}', + '\u{115B8}', + '\u{115B9}', + '\u{115BA}', + '\u{115BB}', + '\u{115BC}', + '\u{115BD}', + '\u{115BE}', + '\u{115BF}', + '\u{115C0}', + '\u{115DC}', + '\u{115DD}', + '\u{11630}', + '\u{11631}', + '\u{11632}', + '\u{11633}', + '\u{11634}', + '\u{11635}', + '\u{11636}', + '\u{11637}', + '\u{11638}', + '\u{11639}', + '\u{1163A}', + '\u{1163B}', + '\u{1163C}', + '\u{1163D}', + '\u{1163E}', + '\u{1163F}', + '\u{11640}', + '\u{116AB}', + '\u{116AC}', + '\u{116AD}', + '\u{116AE}', + '\u{116AF}', + '\u{116B0}', + '\u{116B1}', + '\u{116B2}', + '\u{116B3}', + '\u{116B4}', + '\u{116B5}', + '\u{116B6}', + '\u{116B7}', + '\u{1171D}', + '\u{1171E}', + '\u{1171F}', + '\u{11720}', + '\u{11721}', + '\u{11722}', + '\u{11723}', + '\u{11724}', + '\u{11725}', + '\u{11726}', + '\u{11727}', + '\u{11728}', + '\u{11729}', + '\u{1172A}', + '\u{1172B}', + '\u{1182C}', + '\u{1182D}', + '\u{1182E}', + '\u{1182F}', + '\u{11830}', + '\u{11831}', + '\u{11832}', + '\u{11833}', + '\u{11834}', + '\u{11835}', + '\u{11836}', + '\u{11837}', + '\u{11838}', + '\u{11839}', + '\u{1183A}', + '\u{11930}', + '\u{11931}', + '\u{11932}', + '\u{11933}', + '\u{11934}', + '\u{11935}', + '\u{11937}', + '\u{11938}', + '\u{1193B}', + '\u{1193C}', + '\u{1193D}', + '\u{1193E}', + '\u{11940}', + '\u{11942}', + '\u{11943}', + '\u{119D1}', + '\u{119D2}', + '\u{119D3}', + '\u{119D4}', + '\u{119D5}', + '\u{119D6}', + '\u{119D7}', + '\u{119DA}', + '\u{119DB}', + '\u{119DC}', + '\u{119DD}', + '\u{119DE}', + '\u{119DF}', + '\u{119E0}', + '\u{119E4}', + '\u{11A01}', + '\u{11A02}', + '\u{11A03}', + '\u{11A04}', + '\u{11A05}', + '\u{11A06}', + '\u{11A07}', + '\u{11A08}', + '\u{11A09}', + '\u{11A0A}', + '\u{11A33}', + '\u{11A34}', + '\u{11A35}', + '\u{11A36}', + '\u{11A37}', + '\u{11A38}', + '\u{11A39}', + '\u{11A3B}', + '\u{11A3C}', + '\u{11A3D}', + '\u{11A3E}', + '\u{11A47}', + '\u{11A51}', + '\u{11A52}', + '\u{11A53}', + '\u{11A54}', + '\u{11A55}', + '\u{11A56}', + '\u{11A57}', + '\u{11A58}', + '\u{11A59}', + '\u{11A5A}', + '\u{11A5B}', + '\u{11A8A}', + '\u{11A8B}', + '\u{11A8C}', + '\u{11A8D}', + '\u{11A8E}', + '\u{11A8F}', + '\u{11A90}', + '\u{11A91}', + '\u{11A92}', + '\u{11A93}', + '\u{11A94}', + '\u{11A95}', + '\u{11A96}', + '\u{11A97}', + '\u{11A98}', + '\u{11A99}', + '\u{11C2F}', + '\u{11C30}', + '\u{11C31}', + '\u{11C32}', + '\u{11C33}', + '\u{11C34}', + '\u{11C35}', + '\u{11C36}', + '\u{11C38}', + '\u{11C39}', + '\u{11C3A}', + '\u{11C3B}', + '\u{11C3C}', + '\u{11C3D}', + '\u{11C3E}', + '\u{11C3F}', + '\u{11C92}', + '\u{11C93}', + '\u{11C94}', + '\u{11C95}', + '\u{11C96}', + '\u{11C97}', + '\u{11C98}', + '\u{11C99}', + '\u{11C9A}', + '\u{11C9B}', + '\u{11C9C}', + '\u{11C9D}', + '\u{11C9E}', + '\u{11C9F}', + '\u{11CA0}', + '\u{11CA1}', + '\u{11CA2}', + '\u{11CA3}', + '\u{11CA4}', + '\u{11CA5}', + '\u{11CA6}', + '\u{11CA7}', + '\u{11CA9}', + '\u{11CAA}', + '\u{11CAB}', + '\u{11CAC}', + '\u{11CAD}', + '\u{11CAE}', + '\u{11CAF}', + '\u{11CB0}', + '\u{11CB1}', + '\u{11CB2}', + '\u{11CB3}', + '\u{11CB4}', + '\u{11CB5}', + '\u{11CB6}', + '\u{11D31}', + '\u{11D32}', + '\u{11D33}', + '\u{11D34}', + '\u{11D35}', + '\u{11D36}', + '\u{11D3A}', + '\u{11D3C}', + '\u{11D3D}', + '\u{11D3F}', + '\u{11D40}', + '\u{11D41}', + '\u{11D42}', + '\u{11D43}', + '\u{11D44}', + '\u{11D45}', + '\u{11D47}', + '\u{11D8A}', + '\u{11D8B}', + '\u{11D8C}', + '\u{11D8D}', + '\u{11D8E}', + '\u{11D90}', + '\u{11D91}', + '\u{11D93}', + '\u{11D94}', + '\u{11D95}', + '\u{11D96}', + '\u{11D97}', + '\u{11EF3}', + '\u{11EF4}', + '\u{11EF5}', + '\u{11EF6}', + '\u{11F00}', + '\u{11F01}', + '\u{11F03}', + '\u{11F34}', + '\u{11F35}', + '\u{11F36}', + '\u{11F37}', + '\u{11F38}', + '\u{11F39}', + '\u{11F3A}', + '\u{11F3E}', + '\u{11F3F}', + '\u{11F40}', + '\u{11F41}', + '\u{11F42}', + '\u{13440}', + '\u{13447}', + '\u{13448}', + '\u{13449}', + '\u{1344A}', + '\u{1344B}', + '\u{1344C}', + '\u{1344D}', + '\u{1344E}', + '\u{1344F}', + '\u{13450}', + '\u{13451}', + '\u{13452}', + '\u{13453}', + '\u{13454}', + '\u{13455}', + '\u{16AF0}', + '\u{16AF1}', + '\u{16AF2}', + '\u{16AF3}', + '\u{16AF4}', + '\u{16B30}', + '\u{16B31}', + '\u{16B32}', + '\u{16B33}', + '\u{16B34}', + '\u{16B35}', + '\u{16B36}', + '\u{16F4F}', + '\u{16F51}', + '\u{16F52}', + '\u{16F53}', + '\u{16F54}', + '\u{16F55}', + '\u{16F56}', + '\u{16F57}', + '\u{16F58}', + '\u{16F59}', + '\u{16F5A}', + '\u{16F5B}', + '\u{16F5C}', + '\u{16F5D}', + '\u{16F5E}', + '\u{16F5F}', + '\u{16F60}', + '\u{16F61}', + '\u{16F62}', + '\u{16F63}', + '\u{16F64}', + '\u{16F65}', + '\u{16F66}', + '\u{16F67}', + '\u{16F68}', + '\u{16F69}', + '\u{16F6A}', + '\u{16F6B}', + '\u{16F6C}', + '\u{16F6D}', + '\u{16F6E}', + '\u{16F6F}', + '\u{16F70}', + '\u{16F71}', + '\u{16F72}', + '\u{16F73}', + '\u{16F74}', + '\u{16F75}', + '\u{16F76}', + '\u{16F77}', + '\u{16F78}', + '\u{16F79}', + '\u{16F7A}', + '\u{16F7B}', + '\u{16F7C}', + '\u{16F7D}', + '\u{16F7E}', + '\u{16F7F}', + '\u{16F80}', + '\u{16F81}', + '\u{16F82}', + '\u{16F83}', + '\u{16F84}', + '\u{16F85}', + '\u{16F86}', + '\u{16F87}', + '\u{16F8F}', + '\u{16F90}', + '\u{16F91}', + '\u{16F92}', + '\u{16FE4}', + '\u{16FF0}', + '\u{16FF1}', + '\u{1BC9D}', + '\u{1BC9E}', + '\u{1CF00}', + '\u{1CF01}', + '\u{1CF02}', + '\u{1CF03}', + '\u{1CF04}', + '\u{1CF05}', + '\u{1CF06}', + '\u{1CF07}', + '\u{1CF08}', + '\u{1CF09}', + '\u{1CF0A}', + '\u{1CF0B}', + '\u{1CF0C}', + '\u{1CF0D}', + '\u{1CF0E}', + '\u{1CF0F}', + '\u{1CF10}', + '\u{1CF11}', + '\u{1CF12}', + '\u{1CF13}', + '\u{1CF14}', + '\u{1CF15}', + '\u{1CF16}', + '\u{1CF17}', + '\u{1CF18}', + '\u{1CF19}', + '\u{1CF1A}', + '\u{1CF1B}', + '\u{1CF1C}', + '\u{1CF1D}', + '\u{1CF1E}', + '\u{1CF1F}', + '\u{1CF20}', + '\u{1CF21}', + '\u{1CF22}', + '\u{1CF23}', + '\u{1CF24}', + '\u{1CF25}', + '\u{1CF26}', + '\u{1CF27}', + '\u{1CF28}', + '\u{1CF29}', + '\u{1CF2A}', + '\u{1CF2B}', + '\u{1CF2C}', + '\u{1CF2D}', + '\u{1CF30}', + '\u{1CF31}', + '\u{1CF32}', + '\u{1CF33}', + '\u{1CF34}', + '\u{1CF35}', + '\u{1CF36}', + '\u{1CF37}', + '\u{1CF38}', + '\u{1CF39}', + '\u{1CF3A}', + '\u{1CF3B}', + '\u{1CF3C}', + '\u{1CF3D}', + '\u{1CF3E}', + '\u{1CF3F}', + '\u{1CF40}', + '\u{1CF41}', + '\u{1CF42}', + '\u{1CF43}', + '\u{1CF44}', + '\u{1CF45}', + '\u{1CF46}', + '\u{1D165}', + '\u{1D166}', + '\u{1D167}', + '\u{1D168}', + '\u{1D169}', + '\u{1D16D}', + '\u{1D16E}', + '\u{1D16F}', + '\u{1D170}', + '\u{1D171}', + '\u{1D172}', + '\u{1D17B}', + '\u{1D17C}', + '\u{1D17D}', + '\u{1D17E}', + '\u{1D17F}', + '\u{1D180}', + '\u{1D181}', + '\u{1D182}', + '\u{1D185}', + '\u{1D186}', + '\u{1D187}', + '\u{1D188}', + '\u{1D189}', + '\u{1D18A}', + '\u{1D18B}', + '\u{1D1AA}', + '\u{1D1AB}', + '\u{1D1AC}', + '\u{1D1AD}', + '\u{1D242}', + '\u{1D243}', + '\u{1D244}', + '\u{1DA00}', + '\u{1DA01}', + '\u{1DA02}', + '\u{1DA03}', + '\u{1DA04}', + '\u{1DA05}', + '\u{1DA06}', + '\u{1DA07}', + '\u{1DA08}', + '\u{1DA09}', + '\u{1DA0A}', + '\u{1DA0B}', + '\u{1DA0C}', + '\u{1DA0D}', + '\u{1DA0E}', + '\u{1DA0F}', + '\u{1DA10}', + '\u{1DA11}', + '\u{1DA12}', + '\u{1DA13}', + '\u{1DA14}', + '\u{1DA15}', + '\u{1DA16}', + '\u{1DA17}', + '\u{1DA18}', + '\u{1DA19}', + '\u{1DA1A}', + '\u{1DA1B}', + '\u{1DA1C}', + '\u{1DA1D}', + '\u{1DA1E}', + '\u{1DA1F}', + '\u{1DA20}', + '\u{1DA21}', + '\u{1DA22}', + '\u{1DA23}', + '\u{1DA24}', + '\u{1DA25}', + '\u{1DA26}', + '\u{1DA27}', + '\u{1DA28}', + '\u{1DA29}', + '\u{1DA2A}', + '\u{1DA2B}', + '\u{1DA2C}', + '\u{1DA2D}', + '\u{1DA2E}', + '\u{1DA2F}', + '\u{1DA30}', + '\u{1DA31}', + '\u{1DA32}', + '\u{1DA33}', + '\u{1DA34}', + '\u{1DA35}', + '\u{1DA36}', + '\u{1DA3B}', + '\u{1DA3C}', + '\u{1DA3D}', + '\u{1DA3E}', + '\u{1DA3F}', + '\u{1DA40}', + '\u{1DA41}', + '\u{1DA42}', + '\u{1DA43}', + '\u{1DA44}', + '\u{1DA45}', + '\u{1DA46}', + '\u{1DA47}', + '\u{1DA48}', + '\u{1DA49}', + '\u{1DA4A}', + '\u{1DA4B}', + '\u{1DA4C}', + '\u{1DA4D}', + '\u{1DA4E}', + '\u{1DA4F}', + '\u{1DA50}', + '\u{1DA51}', + '\u{1DA52}', + '\u{1DA53}', + '\u{1DA54}', + '\u{1DA55}', + '\u{1DA56}', + '\u{1DA57}', + '\u{1DA58}', + '\u{1DA59}', + '\u{1DA5A}', + '\u{1DA5B}', + '\u{1DA5C}', + '\u{1DA5D}', + '\u{1DA5E}', + '\u{1DA5F}', + '\u{1DA60}', + '\u{1DA61}', + '\u{1DA62}', + '\u{1DA63}', + '\u{1DA64}', + '\u{1DA65}', + '\u{1DA66}', + '\u{1DA67}', + '\u{1DA68}', + '\u{1DA69}', + '\u{1DA6A}', + '\u{1DA6B}', + '\u{1DA6C}', + '\u{1DA75}', + '\u{1DA84}', + '\u{1DA9B}', + '\u{1DA9C}', + '\u{1DA9D}', + '\u{1DA9E}', + '\u{1DA9F}', + '\u{1DAA1}', + '\u{1DAA2}', + '\u{1DAA3}', + '\u{1DAA4}', + '\u{1DAA5}', + '\u{1DAA6}', + '\u{1DAA7}', + '\u{1DAA8}', + '\u{1DAA9}', + '\u{1DAAA}', + '\u{1DAAB}', + '\u{1DAAC}', + '\u{1DAAD}', + '\u{1DAAE}', + '\u{1DAAF}', + '\u{1E000}', + '\u{1E001}', + '\u{1E002}', + '\u{1E003}', + '\u{1E004}', + '\u{1E005}', + '\u{1E006}', + '\u{1E008}', + '\u{1E009}', + '\u{1E00A}', + '\u{1E00B}', + '\u{1E00C}', + '\u{1E00D}', + '\u{1E00E}', + '\u{1E00F}', + '\u{1E010}', + '\u{1E011}', + '\u{1E012}', + '\u{1E013}', + '\u{1E014}', + '\u{1E015}', + '\u{1E016}', + '\u{1E017}', + '\u{1E018}', + '\u{1E01B}', + '\u{1E01C}', + '\u{1E01D}', + '\u{1E01E}', + '\u{1E01F}', + '\u{1E020}', + '\u{1E021}', + '\u{1E023}', + '\u{1E024}', + '\u{1E026}', + '\u{1E027}', + '\u{1E028}', + '\u{1E029}', + '\u{1E02A}', + '\u{1E08F}', + '\u{1E130}', + '\u{1E131}', + '\u{1E132}', + '\u{1E133}', + '\u{1E134}', + '\u{1E135}', + '\u{1E136}', + '\u{1E2AE}', + '\u{1E2EC}', + '\u{1E2ED}', + '\u{1E2EE}', + '\u{1E2EF}', + '\u{1E4EC}', + '\u{1E4ED}', + '\u{1E4EE}', + '\u{1E4EF}', + '\u{1E8D0}', + '\u{1E8D1}', + '\u{1E8D2}', + '\u{1E8D3}', + '\u{1E8D4}', + '\u{1E8D5}', + '\u{1E8D6}', + '\u{1E944}', + '\u{1E945}', + '\u{1E946}', + '\u{1E947}', + '\u{1E948}', + '\u{1E949}', + '\u{1E94A}', + '\u{E0100}', + '\u{E0101}', + '\u{E0102}', + '\u{E0103}', + '\u{E0104}', + '\u{E0105}', + '\u{E0106}', + '\u{E0107}', + '\u{E0108}', + '\u{E0109}', + '\u{E010A}', + '\u{E010B}', + '\u{E010C}', + '\u{E010D}', + '\u{E010E}', + '\u{E010F}', + '\u{E0110}', + '\u{E0111}', + '\u{E0112}', + '\u{E0113}', + '\u{E0114}', + '\u{E0115}', + '\u{E0116}', + '\u{E0117}', + '\u{E0118}', + '\u{E0119}', + '\u{E011A}', + '\u{E011B}', + '\u{E011C}', + '\u{E011D}', + '\u{E011E}', + '\u{E011F}', + '\u{E0120}', + '\u{E0121}', + '\u{E0122}', + '\u{E0123}', + '\u{E0124}', + '\u{E0125}', + '\u{E0126}', + '\u{E0127}', + '\u{E0128}', + '\u{E0129}', + '\u{E012A}', + '\u{E012B}', + '\u{E012C}', + '\u{E012D}', + '\u{E012E}', + '\u{E012F}', + '\u{E0130}', + '\u{E0131}', + '\u{E0132}', + '\u{E0133}', + '\u{E0134}', + '\u{E0135}', + '\u{E0136}', + '\u{E0137}', + '\u{E0138}', + '\u{E0139}', + '\u{E013A}', + '\u{E013B}', + '\u{E013C}', + '\u{E013D}', + '\u{E013E}', + '\u{E013F}', + '\u{E0140}', + '\u{E0141}', + '\u{E0142}', + '\u{E0143}', + '\u{E0144}', + '\u{E0145}', + '\u{E0146}', + '\u{E0147}', + '\u{E0148}', + '\u{E0149}', + '\u{E014A}', + '\u{E014B}', + '\u{E014C}', + '\u{E014D}', + '\u{E014E}', + '\u{E014F}', + '\u{E0150}', + '\u{E0151}', + '\u{E0152}', + '\u{E0153}', + '\u{E0154}', + '\u{E0155}', + '\u{E0156}', + '\u{E0157}', + '\u{E0158}', + '\u{E0159}', + '\u{E015A}', + '\u{E015B}', + '\u{E015C}', + '\u{E015D}', + '\u{E015E}', + '\u{E015F}', + '\u{E0160}', + '\u{E0161}', + '\u{E0162}', + '\u{E0163}', + '\u{E0164}', + '\u{E0165}', + '\u{E0166}', + '\u{E0167}', + '\u{E0168}', + '\u{E0169}', + '\u{E016A}', + '\u{E016B}', + '\u{E016C}', + '\u{E016D}', + '\u{E016E}', + '\u{E016F}', + '\u{E0170}', + '\u{E0171}', + '\u{E0172}', + '\u{E0173}', + '\u{E0174}', + '\u{E0175}', + '\u{E0176}', + '\u{E0177}', + '\u{E0178}', + '\u{E0179}', + '\u{E017A}', + '\u{E017B}', + '\u{E017C}', + '\u{E017D}', + '\u{E017E}', + '\u{E017F}', + '\u{E0180}', + '\u{E0181}', + '\u{E0182}', + '\u{E0183}', + '\u{E0184}', + '\u{E0185}', + '\u{E0186}', + '\u{E0187}', + '\u{E0188}', + '\u{E0189}', + '\u{E018A}', + '\u{E018B}', + '\u{E018C}', + '\u{E018D}', + '\u{E018E}', + '\u{E018F}', + '\u{E0190}', + '\u{E0191}', + '\u{E0192}', + '\u{E0193}', + '\u{E0194}', + '\u{E0195}', + '\u{E0196}', + '\u{E0197}', + '\u{E0198}', + '\u{E0199}', + '\u{E019A}', + '\u{E019B}', + '\u{E019C}', + '\u{E019D}', + '\u{E019E}', + '\u{E019F}', + '\u{E01A0}', + '\u{E01A1}', + '\u{E01A2}', + '\u{E01A3}', + '\u{E01A4}', + '\u{E01A5}', + '\u{E01A6}', + '\u{E01A7}', + '\u{E01A8}', + '\u{E01A9}', + '\u{E01AA}', + '\u{E01AB}', + '\u{E01AC}', + '\u{E01AD}', + '\u{E01AE}', + '\u{E01AF}', + '\u{E01B0}', + '\u{E01B1}', + '\u{E01B2}', + '\u{E01B3}', + '\u{E01B4}', + '\u{E01B5}', + '\u{E01B6}', + '\u{E01B7}', + '\u{E01B8}', + '\u{E01B9}', + '\u{E01BA}', + '\u{E01BB}', + '\u{E01BC}', + '\u{E01BD}', + '\u{E01BE}', + '\u{E01BF}', + '\u{E01C0}', + '\u{E01C1}', + '\u{E01C2}', + '\u{E01C3}', + '\u{E01C4}', + '\u{E01C5}', + '\u{E01C6}', + '\u{E01C7}', + '\u{E01C8}', + '\u{E01C9}', + '\u{E01CA}', + '\u{E01CB}', + '\u{E01CC}', + '\u{E01CD}', + '\u{E01CE}', + '\u{E01CF}', + '\u{E01D0}', + '\u{E01D1}', + '\u{E01D2}', + '\u{E01D3}', + '\u{E01D4}', + '\u{E01D5}', + '\u{E01D6}', + '\u{E01D7}', + '\u{E01D8}', + '\u{E01D9}', + '\u{E01DA}', + '\u{E01DB}', + '\u{E01DC}', + '\u{E01DD}', + '\u{E01DE}', + '\u{E01DF}', + '\u{E01E0}', + '\u{E01E1}', + '\u{E01E2}', + '\u{E01E3}', + '\u{E01E4}', + '\u{E01E5}', + '\u{E01E6}', + '\u{E01E7}', + '\u{E01E8}', + '\u{E01E9}', + '\u{E01EA}', + '\u{E01EB}', + '\u{E01EC}', + '\u{E01ED}', + '\u{E01EE}', + '\u{E01EF}', +]; + +pub(super) const STRIP_MAPPED: &[(char, char)] = &[ + ('\u{00C0}', '\u{0041}'), + ('\u{00C1}', '\u{0041}'), + ('\u{00C2}', '\u{0041}'), + ('\u{00C3}', '\u{0041}'), + ('\u{00C4}', '\u{0041}'), + ('\u{00C5}', '\u{0041}'), + ('\u{00C7}', '\u{0043}'), + ('\u{00C8}', '\u{0045}'), + ('\u{00C9}', '\u{0045}'), + ('\u{00CA}', '\u{0045}'), + ('\u{00CB}', '\u{0045}'), + ('\u{00CC}', '\u{0049}'), + ('\u{00CD}', '\u{0049}'), + ('\u{00CE}', '\u{0049}'), + ('\u{00CF}', '\u{0049}'), + ('\u{00D1}', '\u{004E}'), + ('\u{00D2}', '\u{004F}'), + ('\u{00D3}', '\u{004F}'), + ('\u{00D4}', '\u{004F}'), + ('\u{00D5}', '\u{004F}'), + ('\u{00D6}', '\u{004F}'), + ('\u{00D9}', '\u{0055}'), + ('\u{00DA}', '\u{0055}'), + ('\u{00DB}', '\u{0055}'), + ('\u{00DC}', '\u{0055}'), + ('\u{00DD}', '\u{0059}'), + ('\u{00E0}', '\u{0061}'), + ('\u{00E1}', '\u{0061}'), + ('\u{00E2}', '\u{0061}'), + ('\u{00E3}', '\u{0061}'), + ('\u{00E4}', '\u{0061}'), + ('\u{00E5}', '\u{0061}'), + ('\u{00E7}', '\u{0063}'), + ('\u{00E8}', '\u{0065}'), + ('\u{00E9}', '\u{0065}'), + ('\u{00EA}', '\u{0065}'), + ('\u{00EB}', '\u{0065}'), + ('\u{00EC}', '\u{0069}'), + ('\u{00ED}', '\u{0069}'), + ('\u{00EE}', '\u{0069}'), + ('\u{00EF}', '\u{0069}'), + ('\u{00F1}', '\u{006E}'), + ('\u{00F2}', '\u{006F}'), + ('\u{00F3}', '\u{006F}'), + ('\u{00F4}', '\u{006F}'), + ('\u{00F5}', '\u{006F}'), + ('\u{00F6}', '\u{006F}'), + ('\u{00F9}', '\u{0075}'), + ('\u{00FA}', '\u{0075}'), + ('\u{00FB}', '\u{0075}'), + ('\u{00FC}', '\u{0075}'), + ('\u{00FD}', '\u{0079}'), + ('\u{00FF}', '\u{0079}'), + ('\u{0100}', '\u{0041}'), + ('\u{0101}', '\u{0061}'), + ('\u{0102}', '\u{0041}'), + ('\u{0103}', '\u{0061}'), + ('\u{0104}', '\u{0041}'), + ('\u{0105}', '\u{0061}'), + ('\u{0106}', '\u{0043}'), + ('\u{0107}', '\u{0063}'), + ('\u{0108}', '\u{0043}'), + ('\u{0109}', '\u{0063}'), + ('\u{010A}', '\u{0043}'), + ('\u{010B}', '\u{0063}'), + ('\u{010C}', '\u{0043}'), + ('\u{010D}', '\u{0063}'), + ('\u{010E}', '\u{0044}'), + ('\u{010F}', '\u{0064}'), + ('\u{0112}', '\u{0045}'), + ('\u{0113}', '\u{0065}'), + ('\u{0114}', '\u{0045}'), + ('\u{0115}', '\u{0065}'), + ('\u{0116}', '\u{0045}'), + ('\u{0117}', '\u{0065}'), + ('\u{0118}', '\u{0045}'), + ('\u{0119}', '\u{0065}'), + ('\u{011A}', '\u{0045}'), + ('\u{011B}', '\u{0065}'), + ('\u{011C}', '\u{0047}'), + ('\u{011D}', '\u{0067}'), + ('\u{011E}', '\u{0047}'), + ('\u{011F}', '\u{0067}'), + ('\u{0120}', '\u{0047}'), + ('\u{0121}', '\u{0067}'), + ('\u{0122}', '\u{0047}'), + ('\u{0123}', '\u{0067}'), + ('\u{0124}', '\u{0048}'), + ('\u{0125}', '\u{0068}'), + ('\u{0128}', '\u{0049}'), + ('\u{0129}', '\u{0069}'), + ('\u{012A}', '\u{0049}'), + ('\u{012B}', '\u{0069}'), + ('\u{012C}', '\u{0049}'), + ('\u{012D}', '\u{0069}'), + ('\u{012E}', '\u{0049}'), + ('\u{012F}', '\u{0069}'), + ('\u{0130}', '\u{0049}'), + ('\u{0134}', '\u{004A}'), + ('\u{0135}', '\u{006A}'), + ('\u{0136}', '\u{004B}'), + ('\u{0137}', '\u{006B}'), + ('\u{0139}', '\u{004C}'), + ('\u{013A}', '\u{006C}'), + ('\u{013B}', '\u{004C}'), + ('\u{013C}', '\u{006C}'), + ('\u{013D}', '\u{004C}'), + ('\u{013E}', '\u{006C}'), + ('\u{0143}', '\u{004E}'), + ('\u{0144}', '\u{006E}'), + ('\u{0145}', '\u{004E}'), + ('\u{0146}', '\u{006E}'), + ('\u{0147}', '\u{004E}'), + ('\u{0148}', '\u{006E}'), + ('\u{014C}', '\u{004F}'), + ('\u{014D}', '\u{006F}'), + ('\u{014E}', '\u{004F}'), + ('\u{014F}', '\u{006F}'), + ('\u{0150}', '\u{004F}'), + ('\u{0151}', '\u{006F}'), + ('\u{0154}', '\u{0052}'), + ('\u{0155}', '\u{0072}'), + ('\u{0156}', '\u{0052}'), + ('\u{0157}', '\u{0072}'), + ('\u{0158}', '\u{0052}'), + ('\u{0159}', '\u{0072}'), + ('\u{015A}', '\u{0053}'), + ('\u{015B}', '\u{0073}'), + ('\u{015C}', '\u{0053}'), + ('\u{015D}', '\u{0073}'), + ('\u{015E}', '\u{0053}'), + ('\u{015F}', '\u{0073}'), + ('\u{0160}', '\u{0053}'), + ('\u{0161}', '\u{0073}'), + ('\u{0162}', '\u{0054}'), + ('\u{0163}', '\u{0074}'), + ('\u{0164}', '\u{0054}'), + ('\u{0165}', '\u{0074}'), + ('\u{0168}', '\u{0055}'), + ('\u{0169}', '\u{0075}'), + ('\u{016A}', '\u{0055}'), + ('\u{016B}', '\u{0075}'), + ('\u{016C}', '\u{0055}'), + ('\u{016D}', '\u{0075}'), + ('\u{016E}', '\u{0055}'), + ('\u{016F}', '\u{0075}'), + ('\u{0170}', '\u{0055}'), + ('\u{0171}', '\u{0075}'), + ('\u{0172}', '\u{0055}'), + ('\u{0173}', '\u{0075}'), + ('\u{0174}', '\u{0057}'), + ('\u{0175}', '\u{0077}'), + ('\u{0176}', '\u{0059}'), + ('\u{0177}', '\u{0079}'), + ('\u{0178}', '\u{0059}'), + ('\u{0179}', '\u{005A}'), + ('\u{017A}', '\u{007A}'), + ('\u{017B}', '\u{005A}'), + ('\u{017C}', '\u{007A}'), + ('\u{017D}', '\u{005A}'), + ('\u{017E}', '\u{007A}'), + ('\u{01A0}', '\u{004F}'), + ('\u{01A1}', '\u{006F}'), + ('\u{01AF}', '\u{0055}'), + ('\u{01B0}', '\u{0075}'), + ('\u{01CD}', '\u{0041}'), + ('\u{01CE}', '\u{0061}'), + ('\u{01CF}', '\u{0049}'), + ('\u{01D0}', '\u{0069}'), + ('\u{01D1}', '\u{004F}'), + ('\u{01D2}', '\u{006F}'), + ('\u{01D3}', '\u{0055}'), + ('\u{01D4}', '\u{0075}'), + ('\u{01D5}', '\u{0055}'), + ('\u{01D6}', '\u{0075}'), + ('\u{01D7}', '\u{0055}'), + ('\u{01D8}', '\u{0075}'), + ('\u{01D9}', '\u{0055}'), + ('\u{01DA}', '\u{0075}'), + ('\u{01DB}', '\u{0055}'), + ('\u{01DC}', '\u{0075}'), + ('\u{01DE}', '\u{0041}'), + ('\u{01DF}', '\u{0061}'), + ('\u{01E0}', '\u{0041}'), + ('\u{01E1}', '\u{0061}'), + ('\u{01E2}', '\u{00C6}'), + ('\u{01E3}', '\u{00E6}'), + ('\u{01E6}', '\u{0047}'), + ('\u{01E7}', '\u{0067}'), + ('\u{01E8}', '\u{004B}'), + ('\u{01E9}', '\u{006B}'), + ('\u{01EA}', '\u{004F}'), + ('\u{01EB}', '\u{006F}'), + ('\u{01EC}', '\u{004F}'), + ('\u{01ED}', '\u{006F}'), + ('\u{01EE}', '\u{01B7}'), + ('\u{01EF}', '\u{0292}'), + ('\u{01F0}', '\u{006A}'), + ('\u{01F4}', '\u{0047}'), + ('\u{01F5}', '\u{0067}'), + ('\u{01F8}', '\u{004E}'), + ('\u{01F9}', '\u{006E}'), + ('\u{01FA}', '\u{0041}'), + ('\u{01FB}', '\u{0061}'), + ('\u{01FC}', '\u{00C6}'), + ('\u{01FD}', '\u{00E6}'), + ('\u{01FE}', '\u{00D8}'), + ('\u{01FF}', '\u{00F8}'), + ('\u{0200}', '\u{0041}'), + ('\u{0201}', '\u{0061}'), + ('\u{0202}', '\u{0041}'), + ('\u{0203}', '\u{0061}'), + ('\u{0204}', '\u{0045}'), + ('\u{0205}', '\u{0065}'), + ('\u{0206}', '\u{0045}'), + ('\u{0207}', '\u{0065}'), + ('\u{0208}', '\u{0049}'), + ('\u{0209}', '\u{0069}'), + ('\u{020A}', '\u{0049}'), + ('\u{020B}', '\u{0069}'), + ('\u{020C}', '\u{004F}'), + ('\u{020D}', '\u{006F}'), + ('\u{020E}', '\u{004F}'), + ('\u{020F}', '\u{006F}'), + ('\u{0210}', '\u{0052}'), + ('\u{0211}', '\u{0072}'), + ('\u{0212}', '\u{0052}'), + ('\u{0213}', '\u{0072}'), + ('\u{0214}', '\u{0055}'), + ('\u{0215}', '\u{0075}'), + ('\u{0216}', '\u{0055}'), + ('\u{0217}', '\u{0075}'), + ('\u{0218}', '\u{0053}'), + ('\u{0219}', '\u{0073}'), + ('\u{021A}', '\u{0054}'), + ('\u{021B}', '\u{0074}'), + ('\u{021E}', '\u{0048}'), + ('\u{021F}', '\u{0068}'), + ('\u{0226}', '\u{0041}'), + ('\u{0227}', '\u{0061}'), + ('\u{0228}', '\u{0045}'), + ('\u{0229}', '\u{0065}'), + ('\u{022A}', '\u{004F}'), + ('\u{022B}', '\u{006F}'), + ('\u{022C}', '\u{004F}'), + ('\u{022D}', '\u{006F}'), + ('\u{022E}', '\u{004F}'), + ('\u{022F}', '\u{006F}'), + ('\u{0230}', '\u{004F}'), + ('\u{0231}', '\u{006F}'), + ('\u{0232}', '\u{0059}'), + ('\u{0233}', '\u{0079}'), + ('\u{0374}', '\u{02B9}'), + ('\u{037E}', '\u{003B}'), + ('\u{0385}', '\u{00A8}'), + ('\u{0386}', '\u{0391}'), + ('\u{0387}', '\u{00B7}'), + ('\u{0388}', '\u{0395}'), + ('\u{0389}', '\u{0397}'), + ('\u{038A}', '\u{0399}'), + ('\u{038C}', '\u{039F}'), + ('\u{038E}', '\u{03A5}'), + ('\u{038F}', '\u{03A9}'), + ('\u{0390}', '\u{03B9}'), + ('\u{03AA}', '\u{0399}'), + ('\u{03AB}', '\u{03A5}'), + ('\u{03AC}', '\u{03B1}'), + ('\u{03AD}', '\u{03B5}'), + ('\u{03AE}', '\u{03B7}'), + ('\u{03AF}', '\u{03B9}'), + ('\u{03B0}', '\u{03C5}'), + ('\u{03CA}', '\u{03B9}'), + ('\u{03CB}', '\u{03C5}'), + ('\u{03CC}', '\u{03BF}'), + ('\u{03CD}', '\u{03C5}'), + ('\u{03CE}', '\u{03C9}'), + ('\u{03D3}', '\u{03D2}'), + ('\u{03D4}', '\u{03D2}'), + ('\u{0400}', '\u{0415}'), + ('\u{0401}', '\u{0415}'), + ('\u{0403}', '\u{0413}'), + ('\u{0407}', '\u{0406}'), + ('\u{040C}', '\u{041A}'), + ('\u{040D}', '\u{0418}'), + ('\u{040E}', '\u{0423}'), + ('\u{0419}', '\u{0418}'), + ('\u{0439}', '\u{0438}'), + ('\u{0450}', '\u{0435}'), + ('\u{0451}', '\u{0435}'), + ('\u{0453}', '\u{0433}'), + ('\u{0457}', '\u{0456}'), + ('\u{045C}', '\u{043A}'), + ('\u{045D}', '\u{0438}'), + ('\u{045E}', '\u{0443}'), + ('\u{0476}', '\u{0474}'), + ('\u{0477}', '\u{0475}'), + ('\u{04C1}', '\u{0416}'), + ('\u{04C2}', '\u{0436}'), + ('\u{04D0}', '\u{0410}'), + ('\u{04D1}', '\u{0430}'), + ('\u{04D2}', '\u{0410}'), + ('\u{04D3}', '\u{0430}'), + ('\u{04D6}', '\u{0415}'), + ('\u{04D7}', '\u{0435}'), + ('\u{04DA}', '\u{04D8}'), + ('\u{04DB}', '\u{04D9}'), + ('\u{04DC}', '\u{0416}'), + ('\u{04DD}', '\u{0436}'), + ('\u{04DE}', '\u{0417}'), + ('\u{04DF}', '\u{0437}'), + ('\u{04E2}', '\u{0418}'), + ('\u{04E3}', '\u{0438}'), + ('\u{04E4}', '\u{0418}'), + ('\u{04E5}', '\u{0438}'), + ('\u{04E6}', '\u{041E}'), + ('\u{04E7}', '\u{043E}'), + ('\u{04EA}', '\u{04E8}'), + ('\u{04EB}', '\u{04E9}'), + ('\u{04EC}', '\u{042D}'), + ('\u{04ED}', '\u{044D}'), + ('\u{04EE}', '\u{0423}'), + ('\u{04EF}', '\u{0443}'), + ('\u{04F0}', '\u{0423}'), + ('\u{04F1}', '\u{0443}'), + ('\u{04F2}', '\u{0423}'), + ('\u{04F3}', '\u{0443}'), + ('\u{04F4}', '\u{0427}'), + ('\u{04F5}', '\u{0447}'), + ('\u{04F8}', '\u{042B}'), + ('\u{04F9}', '\u{044B}'), + ('\u{0622}', '\u{0627}'), + ('\u{0623}', '\u{0627}'), + ('\u{0624}', '\u{0648}'), + ('\u{0625}', '\u{0627}'), + ('\u{0626}', '\u{064A}'), + ('\u{06C0}', '\u{06D5}'), + ('\u{06C2}', '\u{06C1}'), + ('\u{06D3}', '\u{06D2}'), + ('\u{0929}', '\u{0928}'), + ('\u{0931}', '\u{0930}'), + ('\u{0934}', '\u{0933}'), + ('\u{0958}', '\u{0915}'), + ('\u{0959}', '\u{0916}'), + ('\u{095A}', '\u{0917}'), + ('\u{095B}', '\u{091C}'), + ('\u{095C}', '\u{0921}'), + ('\u{095D}', '\u{0922}'), + ('\u{095E}', '\u{092B}'), + ('\u{095F}', '\u{092F}'), + ('\u{09DC}', '\u{09A1}'), + ('\u{09DD}', '\u{09A2}'), + ('\u{09DF}', '\u{09AF}'), + ('\u{0A33}', '\u{0A32}'), + ('\u{0A36}', '\u{0A38}'), + ('\u{0A59}', '\u{0A16}'), + ('\u{0A5A}', '\u{0A17}'), + ('\u{0A5B}', '\u{0A1C}'), + ('\u{0A5E}', '\u{0A2B}'), + ('\u{0B5C}', '\u{0B21}'), + ('\u{0B5D}', '\u{0B22}'), + ('\u{0B94}', '\u{0B92}'), + ('\u{0F43}', '\u{0F42}'), + ('\u{0F4D}', '\u{0F4C}'), + ('\u{0F52}', '\u{0F51}'), + ('\u{0F57}', '\u{0F56}'), + ('\u{0F5C}', '\u{0F5B}'), + ('\u{0F69}', '\u{0F40}'), + ('\u{1026}', '\u{1025}'), + ('\u{1B06}', '\u{1B05}'), + ('\u{1B08}', '\u{1B07}'), + ('\u{1B0A}', '\u{1B09}'), + ('\u{1B0C}', '\u{1B0B}'), + ('\u{1B0E}', '\u{1B0D}'), + ('\u{1B12}', '\u{1B11}'), + ('\u{1E00}', '\u{0041}'), + ('\u{1E01}', '\u{0061}'), + ('\u{1E02}', '\u{0042}'), + ('\u{1E03}', '\u{0062}'), + ('\u{1E04}', '\u{0042}'), + ('\u{1E05}', '\u{0062}'), + ('\u{1E06}', '\u{0042}'), + ('\u{1E07}', '\u{0062}'), + ('\u{1E08}', '\u{0043}'), + ('\u{1E09}', '\u{0063}'), + ('\u{1E0A}', '\u{0044}'), + ('\u{1E0B}', '\u{0064}'), + ('\u{1E0C}', '\u{0044}'), + ('\u{1E0D}', '\u{0064}'), + ('\u{1E0E}', '\u{0044}'), + ('\u{1E0F}', '\u{0064}'), + ('\u{1E10}', '\u{0044}'), + ('\u{1E11}', '\u{0064}'), + ('\u{1E12}', '\u{0044}'), + ('\u{1E13}', '\u{0064}'), + ('\u{1E14}', '\u{0045}'), + ('\u{1E15}', '\u{0065}'), + ('\u{1E16}', '\u{0045}'), + ('\u{1E17}', '\u{0065}'), + ('\u{1E18}', '\u{0045}'), + ('\u{1E19}', '\u{0065}'), + ('\u{1E1A}', '\u{0045}'), + ('\u{1E1B}', '\u{0065}'), + ('\u{1E1C}', '\u{0045}'), + ('\u{1E1D}', '\u{0065}'), + ('\u{1E1E}', '\u{0046}'), + ('\u{1E1F}', '\u{0066}'), + ('\u{1E20}', '\u{0047}'), + ('\u{1E21}', '\u{0067}'), + ('\u{1E22}', '\u{0048}'), + ('\u{1E23}', '\u{0068}'), + ('\u{1E24}', '\u{0048}'), + ('\u{1E25}', '\u{0068}'), + ('\u{1E26}', '\u{0048}'), + ('\u{1E27}', '\u{0068}'), + ('\u{1E28}', '\u{0048}'), + ('\u{1E29}', '\u{0068}'), + ('\u{1E2A}', '\u{0048}'), + ('\u{1E2B}', '\u{0068}'), + ('\u{1E2C}', '\u{0049}'), + ('\u{1E2D}', '\u{0069}'), + ('\u{1E2E}', '\u{0049}'), + ('\u{1E2F}', '\u{0069}'), + ('\u{1E30}', '\u{004B}'), + ('\u{1E31}', '\u{006B}'), + ('\u{1E32}', '\u{004B}'), + ('\u{1E33}', '\u{006B}'), + ('\u{1E34}', '\u{004B}'), + ('\u{1E35}', '\u{006B}'), + ('\u{1E36}', '\u{004C}'), + ('\u{1E37}', '\u{006C}'), + ('\u{1E38}', '\u{004C}'), + ('\u{1E39}', '\u{006C}'), + ('\u{1E3A}', '\u{004C}'), + ('\u{1E3B}', '\u{006C}'), + ('\u{1E3C}', '\u{004C}'), + ('\u{1E3D}', '\u{006C}'), + ('\u{1E3E}', '\u{004D}'), + ('\u{1E3F}', '\u{006D}'), + ('\u{1E40}', '\u{004D}'), + ('\u{1E41}', '\u{006D}'), + ('\u{1E42}', '\u{004D}'), + ('\u{1E43}', '\u{006D}'), + ('\u{1E44}', '\u{004E}'), + ('\u{1E45}', '\u{006E}'), + ('\u{1E46}', '\u{004E}'), + ('\u{1E47}', '\u{006E}'), + ('\u{1E48}', '\u{004E}'), + ('\u{1E49}', '\u{006E}'), + ('\u{1E4A}', '\u{004E}'), + ('\u{1E4B}', '\u{006E}'), + ('\u{1E4C}', '\u{004F}'), + ('\u{1E4D}', '\u{006F}'), + ('\u{1E4E}', '\u{004F}'), + ('\u{1E4F}', '\u{006F}'), + ('\u{1E50}', '\u{004F}'), + ('\u{1E51}', '\u{006F}'), + ('\u{1E52}', '\u{004F}'), + ('\u{1E53}', '\u{006F}'), + ('\u{1E54}', '\u{0050}'), + ('\u{1E55}', '\u{0070}'), + ('\u{1E56}', '\u{0050}'), + ('\u{1E57}', '\u{0070}'), + ('\u{1E58}', '\u{0052}'), + ('\u{1E59}', '\u{0072}'), + ('\u{1E5A}', '\u{0052}'), + ('\u{1E5B}', '\u{0072}'), + ('\u{1E5C}', '\u{0052}'), + ('\u{1E5D}', '\u{0072}'), + ('\u{1E5E}', '\u{0052}'), + ('\u{1E5F}', '\u{0072}'), + ('\u{1E60}', '\u{0053}'), + ('\u{1E61}', '\u{0073}'), + ('\u{1E62}', '\u{0053}'), + ('\u{1E63}', '\u{0073}'), + ('\u{1E64}', '\u{0053}'), + ('\u{1E65}', '\u{0073}'), + ('\u{1E66}', '\u{0053}'), + ('\u{1E67}', '\u{0073}'), + ('\u{1E68}', '\u{0053}'), + ('\u{1E69}', '\u{0073}'), + ('\u{1E6A}', '\u{0054}'), + ('\u{1E6B}', '\u{0074}'), + ('\u{1E6C}', '\u{0054}'), + ('\u{1E6D}', '\u{0074}'), + ('\u{1E6E}', '\u{0054}'), + ('\u{1E6F}', '\u{0074}'), + ('\u{1E70}', '\u{0054}'), + ('\u{1E71}', '\u{0074}'), + ('\u{1E72}', '\u{0055}'), + ('\u{1E73}', '\u{0075}'), + ('\u{1E74}', '\u{0055}'), + ('\u{1E75}', '\u{0075}'), + ('\u{1E76}', '\u{0055}'), + ('\u{1E77}', '\u{0075}'), + ('\u{1E78}', '\u{0055}'), + ('\u{1E79}', '\u{0075}'), + ('\u{1E7A}', '\u{0055}'), + ('\u{1E7B}', '\u{0075}'), + ('\u{1E7C}', '\u{0056}'), + ('\u{1E7D}', '\u{0076}'), + ('\u{1E7E}', '\u{0056}'), + ('\u{1E7F}', '\u{0076}'), + ('\u{1E80}', '\u{0057}'), + ('\u{1E81}', '\u{0077}'), + ('\u{1E82}', '\u{0057}'), + ('\u{1E83}', '\u{0077}'), + ('\u{1E84}', '\u{0057}'), + ('\u{1E85}', '\u{0077}'), + ('\u{1E86}', '\u{0057}'), + ('\u{1E87}', '\u{0077}'), + ('\u{1E88}', '\u{0057}'), + ('\u{1E89}', '\u{0077}'), + ('\u{1E8A}', '\u{0058}'), + ('\u{1E8B}', '\u{0078}'), + ('\u{1E8C}', '\u{0058}'), + ('\u{1E8D}', '\u{0078}'), + ('\u{1E8E}', '\u{0059}'), + ('\u{1E8F}', '\u{0079}'), + ('\u{1E90}', '\u{005A}'), + ('\u{1E91}', '\u{007A}'), + ('\u{1E92}', '\u{005A}'), + ('\u{1E93}', '\u{007A}'), + ('\u{1E94}', '\u{005A}'), + ('\u{1E95}', '\u{007A}'), + ('\u{1E96}', '\u{0068}'), + ('\u{1E97}', '\u{0074}'), + ('\u{1E98}', '\u{0077}'), + ('\u{1E99}', '\u{0079}'), + ('\u{1E9B}', '\u{017F}'), + ('\u{1EA0}', '\u{0041}'), + ('\u{1EA1}', '\u{0061}'), + ('\u{1EA2}', '\u{0041}'), + ('\u{1EA3}', '\u{0061}'), + ('\u{1EA4}', '\u{0041}'), + ('\u{1EA5}', '\u{0061}'), + ('\u{1EA6}', '\u{0041}'), + ('\u{1EA7}', '\u{0061}'), + ('\u{1EA8}', '\u{0041}'), + ('\u{1EA9}', '\u{0061}'), + ('\u{1EAA}', '\u{0041}'), + ('\u{1EAB}', '\u{0061}'), + ('\u{1EAC}', '\u{0041}'), + ('\u{1EAD}', '\u{0061}'), + ('\u{1EAE}', '\u{0041}'), + ('\u{1EAF}', '\u{0061}'), + ('\u{1EB0}', '\u{0041}'), + ('\u{1EB1}', '\u{0061}'), + ('\u{1EB2}', '\u{0041}'), + ('\u{1EB3}', '\u{0061}'), + ('\u{1EB4}', '\u{0041}'), + ('\u{1EB5}', '\u{0061}'), + ('\u{1EB6}', '\u{0041}'), + ('\u{1EB7}', '\u{0061}'), + ('\u{1EB8}', '\u{0045}'), + ('\u{1EB9}', '\u{0065}'), + ('\u{1EBA}', '\u{0045}'), + ('\u{1EBB}', '\u{0065}'), + ('\u{1EBC}', '\u{0045}'), + ('\u{1EBD}', '\u{0065}'), + ('\u{1EBE}', '\u{0045}'), + ('\u{1EBF}', '\u{0065}'), + ('\u{1EC0}', '\u{0045}'), + ('\u{1EC1}', '\u{0065}'), + ('\u{1EC2}', '\u{0045}'), + ('\u{1EC3}', '\u{0065}'), + ('\u{1EC4}', '\u{0045}'), + ('\u{1EC5}', '\u{0065}'), + ('\u{1EC6}', '\u{0045}'), + ('\u{1EC7}', '\u{0065}'), + ('\u{1EC8}', '\u{0049}'), + ('\u{1EC9}', '\u{0069}'), + ('\u{1ECA}', '\u{0049}'), + ('\u{1ECB}', '\u{0069}'), + ('\u{1ECC}', '\u{004F}'), + ('\u{1ECD}', '\u{006F}'), + ('\u{1ECE}', '\u{004F}'), + ('\u{1ECF}', '\u{006F}'), + ('\u{1ED0}', '\u{004F}'), + ('\u{1ED1}', '\u{006F}'), + ('\u{1ED2}', '\u{004F}'), + ('\u{1ED3}', '\u{006F}'), + ('\u{1ED4}', '\u{004F}'), + ('\u{1ED5}', '\u{006F}'), + ('\u{1ED6}', '\u{004F}'), + ('\u{1ED7}', '\u{006F}'), + ('\u{1ED8}', '\u{004F}'), + ('\u{1ED9}', '\u{006F}'), + ('\u{1EDA}', '\u{004F}'), + ('\u{1EDB}', '\u{006F}'), + ('\u{1EDC}', '\u{004F}'), + ('\u{1EDD}', '\u{006F}'), + ('\u{1EDE}', '\u{004F}'), + ('\u{1EDF}', '\u{006F}'), + ('\u{1EE0}', '\u{004F}'), + ('\u{1EE1}', '\u{006F}'), + ('\u{1EE2}', '\u{004F}'), + ('\u{1EE3}', '\u{006F}'), + ('\u{1EE4}', '\u{0055}'), + ('\u{1EE5}', '\u{0075}'), + ('\u{1EE6}', '\u{0055}'), + ('\u{1EE7}', '\u{0075}'), + ('\u{1EE8}', '\u{0055}'), + ('\u{1EE9}', '\u{0075}'), + ('\u{1EEA}', '\u{0055}'), + ('\u{1EEB}', '\u{0075}'), + ('\u{1EEC}', '\u{0055}'), + ('\u{1EED}', '\u{0075}'), + ('\u{1EEE}', '\u{0055}'), + ('\u{1EEF}', '\u{0075}'), + ('\u{1EF0}', '\u{0055}'), + ('\u{1EF1}', '\u{0075}'), + ('\u{1EF2}', '\u{0059}'), + ('\u{1EF3}', '\u{0079}'), + ('\u{1EF4}', '\u{0059}'), + ('\u{1EF5}', '\u{0079}'), + ('\u{1EF6}', '\u{0059}'), + ('\u{1EF7}', '\u{0079}'), + ('\u{1EF8}', '\u{0059}'), + ('\u{1EF9}', '\u{0079}'), + ('\u{1F00}', '\u{03B1}'), + ('\u{1F01}', '\u{03B1}'), + ('\u{1F02}', '\u{03B1}'), + ('\u{1F03}', '\u{03B1}'), + ('\u{1F04}', '\u{03B1}'), + ('\u{1F05}', '\u{03B1}'), + ('\u{1F06}', '\u{03B1}'), + ('\u{1F07}', '\u{03B1}'), + ('\u{1F08}', '\u{0391}'), + ('\u{1F09}', '\u{0391}'), + ('\u{1F0A}', '\u{0391}'), + ('\u{1F0B}', '\u{0391}'), + ('\u{1F0C}', '\u{0391}'), + ('\u{1F0D}', '\u{0391}'), + ('\u{1F0E}', '\u{0391}'), + ('\u{1F0F}', '\u{0391}'), + ('\u{1F10}', '\u{03B5}'), + ('\u{1F11}', '\u{03B5}'), + ('\u{1F12}', '\u{03B5}'), + ('\u{1F13}', '\u{03B5}'), + ('\u{1F14}', '\u{03B5}'), + ('\u{1F15}', '\u{03B5}'), + ('\u{1F18}', '\u{0395}'), + ('\u{1F19}', '\u{0395}'), + ('\u{1F1A}', '\u{0395}'), + ('\u{1F1B}', '\u{0395}'), + ('\u{1F1C}', '\u{0395}'), + ('\u{1F1D}', '\u{0395}'), + ('\u{1F20}', '\u{03B7}'), + ('\u{1F21}', '\u{03B7}'), + ('\u{1F22}', '\u{03B7}'), + ('\u{1F23}', '\u{03B7}'), + ('\u{1F24}', '\u{03B7}'), + ('\u{1F25}', '\u{03B7}'), + ('\u{1F26}', '\u{03B7}'), + ('\u{1F27}', '\u{03B7}'), + ('\u{1F28}', '\u{0397}'), + ('\u{1F29}', '\u{0397}'), + ('\u{1F2A}', '\u{0397}'), + ('\u{1F2B}', '\u{0397}'), + ('\u{1F2C}', '\u{0397}'), + ('\u{1F2D}', '\u{0397}'), + ('\u{1F2E}', '\u{0397}'), + ('\u{1F2F}', '\u{0397}'), + ('\u{1F30}', '\u{03B9}'), + ('\u{1F31}', '\u{03B9}'), + ('\u{1F32}', '\u{03B9}'), + ('\u{1F33}', '\u{03B9}'), + ('\u{1F34}', '\u{03B9}'), + ('\u{1F35}', '\u{03B9}'), + ('\u{1F36}', '\u{03B9}'), + ('\u{1F37}', '\u{03B9}'), + ('\u{1F38}', '\u{0399}'), + ('\u{1F39}', '\u{0399}'), + ('\u{1F3A}', '\u{0399}'), + ('\u{1F3B}', '\u{0399}'), + ('\u{1F3C}', '\u{0399}'), + ('\u{1F3D}', '\u{0399}'), + ('\u{1F3E}', '\u{0399}'), + ('\u{1F3F}', '\u{0399}'), + ('\u{1F40}', '\u{03BF}'), + ('\u{1F41}', '\u{03BF}'), + ('\u{1F42}', '\u{03BF}'), + ('\u{1F43}', '\u{03BF}'), + ('\u{1F44}', '\u{03BF}'), + ('\u{1F45}', '\u{03BF}'), + ('\u{1F48}', '\u{039F}'), + ('\u{1F49}', '\u{039F}'), + ('\u{1F4A}', '\u{039F}'), + ('\u{1F4B}', '\u{039F}'), + ('\u{1F4C}', '\u{039F}'), + ('\u{1F4D}', '\u{039F}'), + ('\u{1F50}', '\u{03C5}'), + ('\u{1F51}', '\u{03C5}'), + ('\u{1F52}', '\u{03C5}'), + ('\u{1F53}', '\u{03C5}'), + ('\u{1F54}', '\u{03C5}'), + ('\u{1F55}', '\u{03C5}'), + ('\u{1F56}', '\u{03C5}'), + ('\u{1F57}', '\u{03C5}'), + ('\u{1F59}', '\u{03A5}'), + ('\u{1F5B}', '\u{03A5}'), + ('\u{1F5D}', '\u{03A5}'), + ('\u{1F5F}', '\u{03A5}'), + ('\u{1F60}', '\u{03C9}'), + ('\u{1F61}', '\u{03C9}'), + ('\u{1F62}', '\u{03C9}'), + ('\u{1F63}', '\u{03C9}'), + ('\u{1F64}', '\u{03C9}'), + ('\u{1F65}', '\u{03C9}'), + ('\u{1F66}', '\u{03C9}'), + ('\u{1F67}', '\u{03C9}'), + ('\u{1F68}', '\u{03A9}'), + ('\u{1F69}', '\u{03A9}'), + ('\u{1F6A}', '\u{03A9}'), + ('\u{1F6B}', '\u{03A9}'), + ('\u{1F6C}', '\u{03A9}'), + ('\u{1F6D}', '\u{03A9}'), + ('\u{1F6E}', '\u{03A9}'), + ('\u{1F6F}', '\u{03A9}'), + ('\u{1F70}', '\u{03B1}'), + ('\u{1F71}', '\u{03B1}'), + ('\u{1F72}', '\u{03B5}'), + ('\u{1F73}', '\u{03B5}'), + ('\u{1F74}', '\u{03B7}'), + ('\u{1F75}', '\u{03B7}'), + ('\u{1F76}', '\u{03B9}'), + ('\u{1F77}', '\u{03B9}'), + ('\u{1F78}', '\u{03BF}'), + ('\u{1F79}', '\u{03BF}'), + ('\u{1F7A}', '\u{03C5}'), + ('\u{1F7B}', '\u{03C5}'), + ('\u{1F7C}', '\u{03C9}'), + ('\u{1F7D}', '\u{03C9}'), + ('\u{1F80}', '\u{03B1}'), + ('\u{1F81}', '\u{03B1}'), + ('\u{1F82}', '\u{03B1}'), + ('\u{1F83}', '\u{03B1}'), + ('\u{1F84}', '\u{03B1}'), + ('\u{1F85}', '\u{03B1}'), + ('\u{1F86}', '\u{03B1}'), + ('\u{1F87}', '\u{03B1}'), + ('\u{1F88}', '\u{0391}'), + ('\u{1F89}', '\u{0391}'), + ('\u{1F8A}', '\u{0391}'), + ('\u{1F8B}', '\u{0391}'), + ('\u{1F8C}', '\u{0391}'), + ('\u{1F8D}', '\u{0391}'), + ('\u{1F8E}', '\u{0391}'), + ('\u{1F8F}', '\u{0391}'), + ('\u{1F90}', '\u{03B7}'), + ('\u{1F91}', '\u{03B7}'), + ('\u{1F92}', '\u{03B7}'), + ('\u{1F93}', '\u{03B7}'), + ('\u{1F94}', '\u{03B7}'), + ('\u{1F95}', '\u{03B7}'), + ('\u{1F96}', '\u{03B7}'), + ('\u{1F97}', '\u{03B7}'), + ('\u{1F98}', '\u{0397}'), + ('\u{1F99}', '\u{0397}'), + ('\u{1F9A}', '\u{0397}'), + ('\u{1F9B}', '\u{0397}'), + ('\u{1F9C}', '\u{0397}'), + ('\u{1F9D}', '\u{0397}'), + ('\u{1F9E}', '\u{0397}'), + ('\u{1F9F}', '\u{0397}'), + ('\u{1FA0}', '\u{03C9}'), + ('\u{1FA1}', '\u{03C9}'), + ('\u{1FA2}', '\u{03C9}'), + ('\u{1FA3}', '\u{03C9}'), + ('\u{1FA4}', '\u{03C9}'), + ('\u{1FA5}', '\u{03C9}'), + ('\u{1FA6}', '\u{03C9}'), + ('\u{1FA7}', '\u{03C9}'), + ('\u{1FA8}', '\u{03A9}'), + ('\u{1FA9}', '\u{03A9}'), + ('\u{1FAA}', '\u{03A9}'), + ('\u{1FAB}', '\u{03A9}'), + ('\u{1FAC}', '\u{03A9}'), + ('\u{1FAD}', '\u{03A9}'), + ('\u{1FAE}', '\u{03A9}'), + ('\u{1FAF}', '\u{03A9}'), + ('\u{1FB0}', '\u{03B1}'), + ('\u{1FB1}', '\u{03B1}'), + ('\u{1FB2}', '\u{03B1}'), + ('\u{1FB3}', '\u{03B1}'), + ('\u{1FB4}', '\u{03B1}'), + ('\u{1FB6}', '\u{03B1}'), + ('\u{1FB7}', '\u{03B1}'), + ('\u{1FB8}', '\u{0391}'), + ('\u{1FB9}', '\u{0391}'), + ('\u{1FBA}', '\u{0391}'), + ('\u{1FBB}', '\u{0391}'), + ('\u{1FBC}', '\u{0391}'), + ('\u{1FBE}', '\u{03B9}'), + ('\u{1FC1}', '\u{00A8}'), + ('\u{1FC2}', '\u{03B7}'), + ('\u{1FC3}', '\u{03B7}'), + ('\u{1FC4}', '\u{03B7}'), + ('\u{1FC6}', '\u{03B7}'), + ('\u{1FC7}', '\u{03B7}'), + ('\u{1FC8}', '\u{0395}'), + ('\u{1FC9}', '\u{0395}'), + ('\u{1FCA}', '\u{0397}'), + ('\u{1FCB}', '\u{0397}'), + ('\u{1FCC}', '\u{0397}'), + ('\u{1FCD}', '\u{1FBF}'), + ('\u{1FCE}', '\u{1FBF}'), + ('\u{1FCF}', '\u{1FBF}'), + ('\u{1FD0}', '\u{03B9}'), + ('\u{1FD1}', '\u{03B9}'), + ('\u{1FD2}', '\u{03B9}'), + ('\u{1FD3}', '\u{03B9}'), + ('\u{1FD6}', '\u{03B9}'), + ('\u{1FD7}', '\u{03B9}'), + ('\u{1FD8}', '\u{0399}'), + ('\u{1FD9}', '\u{0399}'), + ('\u{1FDA}', '\u{0399}'), + ('\u{1FDB}', '\u{0399}'), + ('\u{1FDD}', '\u{1FFE}'), + ('\u{1FDE}', '\u{1FFE}'), + ('\u{1FDF}', '\u{1FFE}'), + ('\u{1FE0}', '\u{03C5}'), + ('\u{1FE1}', '\u{03C5}'), + ('\u{1FE2}', '\u{03C5}'), + ('\u{1FE3}', '\u{03C5}'), + ('\u{1FE4}', '\u{03C1}'), + ('\u{1FE5}', '\u{03C1}'), + ('\u{1FE6}', '\u{03C5}'), + ('\u{1FE7}', '\u{03C5}'), + ('\u{1FE8}', '\u{03A5}'), + ('\u{1FE9}', '\u{03A5}'), + ('\u{1FEA}', '\u{03A5}'), + ('\u{1FEB}', '\u{03A5}'), + ('\u{1FEC}', '\u{03A1}'), + ('\u{1FED}', '\u{00A8}'), + ('\u{1FEE}', '\u{00A8}'), + ('\u{1FEF}', '\u{0060}'), + ('\u{1FF2}', '\u{03C9}'), + ('\u{1FF3}', '\u{03C9}'), + ('\u{1FF4}', '\u{03C9}'), + ('\u{1FF6}', '\u{03C9}'), + ('\u{1FF7}', '\u{03C9}'), + ('\u{1FF8}', '\u{039F}'), + ('\u{1FF9}', '\u{039F}'), + ('\u{1FFA}', '\u{03A9}'), + ('\u{1FFB}', '\u{03A9}'), + ('\u{1FFC}', '\u{03A9}'), + ('\u{1FFD}', '\u{00B4}'), + ('\u{2000}', '\u{2002}'), + ('\u{2001}', '\u{2003}'), + ('\u{2126}', '\u{03A9}'), + ('\u{212A}', '\u{004B}'), + ('\u{212B}', '\u{0041}'), + ('\u{219A}', '\u{2190}'), + ('\u{219B}', '\u{2192}'), + ('\u{21AE}', '\u{2194}'), + ('\u{21CD}', '\u{21D0}'), + ('\u{21CE}', '\u{21D4}'), + ('\u{21CF}', '\u{21D2}'), + ('\u{2204}', '\u{2203}'), + ('\u{2209}', '\u{2208}'), + ('\u{220C}', '\u{220B}'), + ('\u{2224}', '\u{2223}'), + ('\u{2226}', '\u{2225}'), + ('\u{2241}', '\u{223C}'), + ('\u{2244}', '\u{2243}'), + ('\u{2247}', '\u{2245}'), + ('\u{2249}', '\u{2248}'), + ('\u{2260}', '\u{003D}'), + ('\u{2262}', '\u{2261}'), + ('\u{226D}', '\u{224D}'), + ('\u{226E}', '\u{003C}'), + ('\u{226F}', '\u{003E}'), + ('\u{2270}', '\u{2264}'), + ('\u{2271}', '\u{2265}'), + ('\u{2274}', '\u{2272}'), + ('\u{2275}', '\u{2273}'), + ('\u{2278}', '\u{2276}'), + ('\u{2279}', '\u{2277}'), + ('\u{2280}', '\u{227A}'), + ('\u{2281}', '\u{227B}'), + ('\u{2284}', '\u{2282}'), + ('\u{2285}', '\u{2283}'), + ('\u{2288}', '\u{2286}'), + ('\u{2289}', '\u{2287}'), + ('\u{22AC}', '\u{22A2}'), + ('\u{22AD}', '\u{22A8}'), + ('\u{22AE}', '\u{22A9}'), + ('\u{22AF}', '\u{22AB}'), + ('\u{22E0}', '\u{227C}'), + ('\u{22E1}', '\u{227D}'), + ('\u{22E2}', '\u{2291}'), + ('\u{22E3}', '\u{2292}'), + ('\u{22EA}', '\u{22B2}'), + ('\u{22EB}', '\u{22B3}'), + ('\u{22EC}', '\u{22B4}'), + ('\u{22ED}', '\u{22B5}'), + ('\u{2329}', '\u{3008}'), + ('\u{232A}', '\u{3009}'), + ('\u{2ADC}', '\u{2ADD}'), + ('\u{304C}', '\u{304B}'), + ('\u{304E}', '\u{304D}'), + ('\u{3050}', '\u{304F}'), + ('\u{3052}', '\u{3051}'), + ('\u{3054}', '\u{3053}'), + ('\u{3056}', '\u{3055}'), + ('\u{3058}', '\u{3057}'), + ('\u{305A}', '\u{3059}'), + ('\u{305C}', '\u{305B}'), + ('\u{305E}', '\u{305D}'), + ('\u{3060}', '\u{305F}'), + ('\u{3062}', '\u{3061}'), + ('\u{3065}', '\u{3064}'), + ('\u{3067}', '\u{3066}'), + ('\u{3069}', '\u{3068}'), + ('\u{3070}', '\u{306F}'), + ('\u{3071}', '\u{306F}'), + ('\u{3073}', '\u{3072}'), + ('\u{3074}', '\u{3072}'), + ('\u{3076}', '\u{3075}'), + ('\u{3077}', '\u{3075}'), + ('\u{3079}', '\u{3078}'), + ('\u{307A}', '\u{3078}'), + ('\u{307C}', '\u{307B}'), + ('\u{307D}', '\u{307B}'), + ('\u{3094}', '\u{3046}'), + ('\u{309E}', '\u{309D}'), + ('\u{30AC}', '\u{30AB}'), + ('\u{30AE}', '\u{30AD}'), + ('\u{30B0}', '\u{30AF}'), + ('\u{30B2}', '\u{30B1}'), + ('\u{30B4}', '\u{30B3}'), + ('\u{30B6}', '\u{30B5}'), + ('\u{30B8}', '\u{30B7}'), + ('\u{30BA}', '\u{30B9}'), + ('\u{30BC}', '\u{30BB}'), + ('\u{30BE}', '\u{30BD}'), + ('\u{30C0}', '\u{30BF}'), + ('\u{30C2}', '\u{30C1}'), + ('\u{30C5}', '\u{30C4}'), + ('\u{30C7}', '\u{30C6}'), + ('\u{30C9}', '\u{30C8}'), + ('\u{30D0}', '\u{30CF}'), + ('\u{30D1}', '\u{30CF}'), + ('\u{30D3}', '\u{30D2}'), + ('\u{30D4}', '\u{30D2}'), + ('\u{30D6}', '\u{30D5}'), + ('\u{30D7}', '\u{30D5}'), + ('\u{30D9}', '\u{30D8}'), + ('\u{30DA}', '\u{30D8}'), + ('\u{30DC}', '\u{30DB}'), + ('\u{30DD}', '\u{30DB}'), + ('\u{30F4}', '\u{30A6}'), + ('\u{30F7}', '\u{30EF}'), + ('\u{30F8}', '\u{30F0}'), + ('\u{30F9}', '\u{30F1}'), + ('\u{30FA}', '\u{30F2}'), + ('\u{30FE}', '\u{30FD}'), + ('\u{F900}', '\u{8C48}'), + ('\u{F901}', '\u{66F4}'), + ('\u{F902}', '\u{8ECA}'), + ('\u{F903}', '\u{8CC8}'), + ('\u{F904}', '\u{6ED1}'), + ('\u{F905}', '\u{4E32}'), + ('\u{F906}', '\u{53E5}'), + ('\u{F907}', '\u{9F9C}'), + ('\u{F908}', '\u{9F9C}'), + ('\u{F909}', '\u{5951}'), + ('\u{F90A}', '\u{91D1}'), + ('\u{F90B}', '\u{5587}'), + ('\u{F90C}', '\u{5948}'), + ('\u{F90D}', '\u{61F6}'), + ('\u{F90E}', '\u{7669}'), + ('\u{F90F}', '\u{7F85}'), + ('\u{F910}', '\u{863F}'), + ('\u{F911}', '\u{87BA}'), + ('\u{F912}', '\u{88F8}'), + ('\u{F913}', '\u{908F}'), + ('\u{F914}', '\u{6A02}'), + ('\u{F915}', '\u{6D1B}'), + ('\u{F916}', '\u{70D9}'), + ('\u{F917}', '\u{73DE}'), + ('\u{F918}', '\u{843D}'), + ('\u{F919}', '\u{916A}'), + ('\u{F91A}', '\u{99F1}'), + ('\u{F91B}', '\u{4E82}'), + ('\u{F91C}', '\u{5375}'), + ('\u{F91D}', '\u{6B04}'), + ('\u{F91E}', '\u{721B}'), + ('\u{F91F}', '\u{862D}'), + ('\u{F920}', '\u{9E1E}'), + ('\u{F921}', '\u{5D50}'), + ('\u{F922}', '\u{6FEB}'), + ('\u{F923}', '\u{85CD}'), + ('\u{F924}', '\u{8964}'), + ('\u{F925}', '\u{62C9}'), + ('\u{F926}', '\u{81D8}'), + ('\u{F927}', '\u{881F}'), + ('\u{F928}', '\u{5ECA}'), + ('\u{F929}', '\u{6717}'), + ('\u{F92A}', '\u{6D6A}'), + ('\u{F92B}', '\u{72FC}'), + ('\u{F92C}', '\u{90CE}'), + ('\u{F92D}', '\u{4F86}'), + ('\u{F92E}', '\u{51B7}'), + ('\u{F92F}', '\u{52DE}'), + ('\u{F930}', '\u{64C4}'), + ('\u{F931}', '\u{6AD3}'), + ('\u{F932}', '\u{7210}'), + ('\u{F933}', '\u{76E7}'), + ('\u{F934}', '\u{8001}'), + ('\u{F935}', '\u{8606}'), + ('\u{F936}', '\u{865C}'), + ('\u{F937}', '\u{8DEF}'), + ('\u{F938}', '\u{9732}'), + ('\u{F939}', '\u{9B6F}'), + ('\u{F93A}', '\u{9DFA}'), + ('\u{F93B}', '\u{788C}'), + ('\u{F93C}', '\u{797F}'), + ('\u{F93D}', '\u{7DA0}'), + ('\u{F93E}', '\u{83C9}'), + ('\u{F93F}', '\u{9304}'), + ('\u{F940}', '\u{9E7F}'), + ('\u{F941}', '\u{8AD6}'), + ('\u{F942}', '\u{58DF}'), + ('\u{F943}', '\u{5F04}'), + ('\u{F944}', '\u{7C60}'), + ('\u{F945}', '\u{807E}'), + ('\u{F946}', '\u{7262}'), + ('\u{F947}', '\u{78CA}'), + ('\u{F948}', '\u{8CC2}'), + ('\u{F949}', '\u{96F7}'), + ('\u{F94A}', '\u{58D8}'), + ('\u{F94B}', '\u{5C62}'), + ('\u{F94C}', '\u{6A13}'), + ('\u{F94D}', '\u{6DDA}'), + ('\u{F94E}', '\u{6F0F}'), + ('\u{F94F}', '\u{7D2F}'), + ('\u{F950}', '\u{7E37}'), + ('\u{F951}', '\u{964B}'), + ('\u{F952}', '\u{52D2}'), + ('\u{F953}', '\u{808B}'), + ('\u{F954}', '\u{51DC}'), + ('\u{F955}', '\u{51CC}'), + ('\u{F956}', '\u{7A1C}'), + ('\u{F957}', '\u{7DBE}'), + ('\u{F958}', '\u{83F1}'), + ('\u{F959}', '\u{9675}'), + ('\u{F95A}', '\u{8B80}'), + ('\u{F95B}', '\u{62CF}'), + ('\u{F95C}', '\u{6A02}'), + ('\u{F95D}', '\u{8AFE}'), + ('\u{F95E}', '\u{4E39}'), + ('\u{F95F}', '\u{5BE7}'), + ('\u{F960}', '\u{6012}'), + ('\u{F961}', '\u{7387}'), + ('\u{F962}', '\u{7570}'), + ('\u{F963}', '\u{5317}'), + ('\u{F964}', '\u{78FB}'), + ('\u{F965}', '\u{4FBF}'), + ('\u{F966}', '\u{5FA9}'), + ('\u{F967}', '\u{4E0D}'), + ('\u{F968}', '\u{6CCC}'), + ('\u{F969}', '\u{6578}'), + ('\u{F96A}', '\u{7D22}'), + ('\u{F96B}', '\u{53C3}'), + ('\u{F96C}', '\u{585E}'), + ('\u{F96D}', '\u{7701}'), + ('\u{F96E}', '\u{8449}'), + ('\u{F96F}', '\u{8AAA}'), + ('\u{F970}', '\u{6BBA}'), + ('\u{F971}', '\u{8FB0}'), + ('\u{F972}', '\u{6C88}'), + ('\u{F973}', '\u{62FE}'), + ('\u{F974}', '\u{82E5}'), + ('\u{F975}', '\u{63A0}'), + ('\u{F976}', '\u{7565}'), + ('\u{F977}', '\u{4EAE}'), + ('\u{F978}', '\u{5169}'), + ('\u{F979}', '\u{51C9}'), + ('\u{F97A}', '\u{6881}'), + ('\u{F97B}', '\u{7CE7}'), + ('\u{F97C}', '\u{826F}'), + ('\u{F97D}', '\u{8AD2}'), + ('\u{F97E}', '\u{91CF}'), + ('\u{F97F}', '\u{52F5}'), + ('\u{F980}', '\u{5442}'), + ('\u{F981}', '\u{5973}'), + ('\u{F982}', '\u{5EEC}'), + ('\u{F983}', '\u{65C5}'), + ('\u{F984}', '\u{6FFE}'), + ('\u{F985}', '\u{792A}'), + ('\u{F986}', '\u{95AD}'), + ('\u{F987}', '\u{9A6A}'), + ('\u{F988}', '\u{9E97}'), + ('\u{F989}', '\u{9ECE}'), + ('\u{F98A}', '\u{529B}'), + ('\u{F98B}', '\u{66C6}'), + ('\u{F98C}', '\u{6B77}'), + ('\u{F98D}', '\u{8F62}'), + ('\u{F98E}', '\u{5E74}'), + ('\u{F98F}', '\u{6190}'), + ('\u{F990}', '\u{6200}'), + ('\u{F991}', '\u{649A}'), + ('\u{F992}', '\u{6F23}'), + ('\u{F993}', '\u{7149}'), + ('\u{F994}', '\u{7489}'), + ('\u{F995}', '\u{79CA}'), + ('\u{F996}', '\u{7DF4}'), + ('\u{F997}', '\u{806F}'), + ('\u{F998}', '\u{8F26}'), + ('\u{F999}', '\u{84EE}'), + ('\u{F99A}', '\u{9023}'), + ('\u{F99B}', '\u{934A}'), + ('\u{F99C}', '\u{5217}'), + ('\u{F99D}', '\u{52A3}'), + ('\u{F99E}', '\u{54BD}'), + ('\u{F99F}', '\u{70C8}'), + ('\u{F9A0}', '\u{88C2}'), + ('\u{F9A1}', '\u{8AAA}'), + ('\u{F9A2}', '\u{5EC9}'), + ('\u{F9A3}', '\u{5FF5}'), + ('\u{F9A4}', '\u{637B}'), + ('\u{F9A5}', '\u{6BAE}'), + ('\u{F9A6}', '\u{7C3E}'), + ('\u{F9A7}', '\u{7375}'), + ('\u{F9A8}', '\u{4EE4}'), + ('\u{F9A9}', '\u{56F9}'), + ('\u{F9AA}', '\u{5BE7}'), + ('\u{F9AB}', '\u{5DBA}'), + ('\u{F9AC}', '\u{601C}'), + ('\u{F9AD}', '\u{73B2}'), + ('\u{F9AE}', '\u{7469}'), + ('\u{F9AF}', '\u{7F9A}'), + ('\u{F9B0}', '\u{8046}'), + ('\u{F9B1}', '\u{9234}'), + ('\u{F9B2}', '\u{96F6}'), + ('\u{F9B3}', '\u{9748}'), + ('\u{F9B4}', '\u{9818}'), + ('\u{F9B5}', '\u{4F8B}'), + ('\u{F9B6}', '\u{79AE}'), + ('\u{F9B7}', '\u{91B4}'), + ('\u{F9B8}', '\u{96B8}'), + ('\u{F9B9}', '\u{60E1}'), + ('\u{F9BA}', '\u{4E86}'), + ('\u{F9BB}', '\u{50DA}'), + ('\u{F9BC}', '\u{5BEE}'), + ('\u{F9BD}', '\u{5C3F}'), + ('\u{F9BE}', '\u{6599}'), + ('\u{F9BF}', '\u{6A02}'), + ('\u{F9C0}', '\u{71CE}'), + ('\u{F9C1}', '\u{7642}'), + ('\u{F9C2}', '\u{84FC}'), + ('\u{F9C3}', '\u{907C}'), + ('\u{F9C4}', '\u{9F8D}'), + ('\u{F9C5}', '\u{6688}'), + ('\u{F9C6}', '\u{962E}'), + ('\u{F9C7}', '\u{5289}'), + ('\u{F9C8}', '\u{677B}'), + ('\u{F9C9}', '\u{67F3}'), + ('\u{F9CA}', '\u{6D41}'), + ('\u{F9CB}', '\u{6E9C}'), + ('\u{F9CC}', '\u{7409}'), + ('\u{F9CD}', '\u{7559}'), + ('\u{F9CE}', '\u{786B}'), + ('\u{F9CF}', '\u{7D10}'), + ('\u{F9D0}', '\u{985E}'), + ('\u{F9D1}', '\u{516D}'), + ('\u{F9D2}', '\u{622E}'), + ('\u{F9D3}', '\u{9678}'), + ('\u{F9D4}', '\u{502B}'), + ('\u{F9D5}', '\u{5D19}'), + ('\u{F9D6}', '\u{6DEA}'), + ('\u{F9D7}', '\u{8F2A}'), + ('\u{F9D8}', '\u{5F8B}'), + ('\u{F9D9}', '\u{6144}'), + ('\u{F9DA}', '\u{6817}'), + ('\u{F9DB}', '\u{7387}'), + ('\u{F9DC}', '\u{9686}'), + ('\u{F9DD}', '\u{5229}'), + ('\u{F9DE}', '\u{540F}'), + ('\u{F9DF}', '\u{5C65}'), + ('\u{F9E0}', '\u{6613}'), + ('\u{F9E1}', '\u{674E}'), + ('\u{F9E2}', '\u{68A8}'), + ('\u{F9E3}', '\u{6CE5}'), + ('\u{F9E4}', '\u{7406}'), + ('\u{F9E5}', '\u{75E2}'), + ('\u{F9E6}', '\u{7F79}'), + ('\u{F9E7}', '\u{88CF}'), + ('\u{F9E8}', '\u{88E1}'), + ('\u{F9E9}', '\u{91CC}'), + ('\u{F9EA}', '\u{96E2}'), + ('\u{F9EB}', '\u{533F}'), + ('\u{F9EC}', '\u{6EBA}'), + ('\u{F9ED}', '\u{541D}'), + ('\u{F9EE}', '\u{71D0}'), + ('\u{F9EF}', '\u{7498}'), + ('\u{F9F0}', '\u{85FA}'), + ('\u{F9F1}', '\u{96A3}'), + ('\u{F9F2}', '\u{9C57}'), + ('\u{F9F3}', '\u{9E9F}'), + ('\u{F9F4}', '\u{6797}'), + ('\u{F9F5}', '\u{6DCB}'), + ('\u{F9F6}', '\u{81E8}'), + ('\u{F9F7}', '\u{7ACB}'), + ('\u{F9F8}', '\u{7B20}'), + ('\u{F9F9}', '\u{7C92}'), + ('\u{F9FA}', '\u{72C0}'), + ('\u{F9FB}', '\u{7099}'), + ('\u{F9FC}', '\u{8B58}'), + ('\u{F9FD}', '\u{4EC0}'), + ('\u{F9FE}', '\u{8336}'), + ('\u{F9FF}', '\u{523A}'), + ('\u{FA00}', '\u{5207}'), + ('\u{FA01}', '\u{5EA6}'), + ('\u{FA02}', '\u{62D3}'), + ('\u{FA03}', '\u{7CD6}'), + ('\u{FA04}', '\u{5B85}'), + ('\u{FA05}', '\u{6D1E}'), + ('\u{FA06}', '\u{66B4}'), + ('\u{FA07}', '\u{8F3B}'), + ('\u{FA08}', '\u{884C}'), + ('\u{FA09}', '\u{964D}'), + ('\u{FA0A}', '\u{898B}'), + ('\u{FA0B}', '\u{5ED3}'), + ('\u{FA0C}', '\u{5140}'), + ('\u{FA0D}', '\u{55C0}'), + ('\u{FA10}', '\u{585A}'), + ('\u{FA12}', '\u{6674}'), + ('\u{FA15}', '\u{51DE}'), + ('\u{FA16}', '\u{732A}'), + ('\u{FA17}', '\u{76CA}'), + ('\u{FA18}', '\u{793C}'), + ('\u{FA19}', '\u{795E}'), + ('\u{FA1A}', '\u{7965}'), + ('\u{FA1B}', '\u{798F}'), + ('\u{FA1C}', '\u{9756}'), + ('\u{FA1D}', '\u{7CBE}'), + ('\u{FA1E}', '\u{7FBD}'), + ('\u{FA20}', '\u{8612}'), + ('\u{FA22}', '\u{8AF8}'), + ('\u{FA25}', '\u{9038}'), + ('\u{FA26}', '\u{90FD}'), + ('\u{FA2A}', '\u{98EF}'), + ('\u{FA2B}', '\u{98FC}'), + ('\u{FA2C}', '\u{9928}'), + ('\u{FA2D}', '\u{9DB4}'), + ('\u{FA2E}', '\u{90DE}'), + ('\u{FA2F}', '\u{96B7}'), + ('\u{FA30}', '\u{4FAE}'), + ('\u{FA31}', '\u{50E7}'), + ('\u{FA32}', '\u{514D}'), + ('\u{FA33}', '\u{52C9}'), + ('\u{FA34}', '\u{52E4}'), + ('\u{FA35}', '\u{5351}'), + ('\u{FA36}', '\u{559D}'), + ('\u{FA37}', '\u{5606}'), + ('\u{FA38}', '\u{5668}'), + ('\u{FA39}', '\u{5840}'), + ('\u{FA3A}', '\u{58A8}'), + ('\u{FA3B}', '\u{5C64}'), + ('\u{FA3C}', '\u{5C6E}'), + ('\u{FA3D}', '\u{6094}'), + ('\u{FA3E}', '\u{6168}'), + ('\u{FA3F}', '\u{618E}'), + ('\u{FA40}', '\u{61F2}'), + ('\u{FA41}', '\u{654F}'), + ('\u{FA42}', '\u{65E2}'), + ('\u{FA43}', '\u{6691}'), + ('\u{FA44}', '\u{6885}'), + ('\u{FA45}', '\u{6D77}'), + ('\u{FA46}', '\u{6E1A}'), + ('\u{FA47}', '\u{6F22}'), + ('\u{FA48}', '\u{716E}'), + ('\u{FA49}', '\u{722B}'), + ('\u{FA4A}', '\u{7422}'), + ('\u{FA4B}', '\u{7891}'), + ('\u{FA4C}', '\u{793E}'), + ('\u{FA4D}', '\u{7949}'), + ('\u{FA4E}', '\u{7948}'), + ('\u{FA4F}', '\u{7950}'), + ('\u{FA50}', '\u{7956}'), + ('\u{FA51}', '\u{795D}'), + ('\u{FA52}', '\u{798D}'), + ('\u{FA53}', '\u{798E}'), + ('\u{FA54}', '\u{7A40}'), + ('\u{FA55}', '\u{7A81}'), + ('\u{FA56}', '\u{7BC0}'), + ('\u{FA57}', '\u{7DF4}'), + ('\u{FA58}', '\u{7E09}'), + ('\u{FA59}', '\u{7E41}'), + ('\u{FA5A}', '\u{7F72}'), + ('\u{FA5B}', '\u{8005}'), + ('\u{FA5C}', '\u{81ED}'), + ('\u{FA5D}', '\u{8279}'), + ('\u{FA5E}', '\u{8279}'), + ('\u{FA5F}', '\u{8457}'), + ('\u{FA60}', '\u{8910}'), + ('\u{FA61}', '\u{8996}'), + ('\u{FA62}', '\u{8B01}'), + ('\u{FA63}', '\u{8B39}'), + ('\u{FA64}', '\u{8CD3}'), + ('\u{FA65}', '\u{8D08}'), + ('\u{FA66}', '\u{8FB6}'), + ('\u{FA67}', '\u{9038}'), + ('\u{FA68}', '\u{96E3}'), + ('\u{FA69}', '\u{97FF}'), + ('\u{FA6A}', '\u{983B}'), + ('\u{FA6B}', '\u{6075}'), + ('\u{FA6C}', '\u{242EE}'), + ('\u{FA6D}', '\u{8218}'), + ('\u{FA70}', '\u{4E26}'), + ('\u{FA71}', '\u{51B5}'), + ('\u{FA72}', '\u{5168}'), + ('\u{FA73}', '\u{4F80}'), + ('\u{FA74}', '\u{5145}'), + ('\u{FA75}', '\u{5180}'), + ('\u{FA76}', '\u{52C7}'), + ('\u{FA77}', '\u{52FA}'), + ('\u{FA78}', '\u{559D}'), + ('\u{FA79}', '\u{5555}'), + ('\u{FA7A}', '\u{5599}'), + ('\u{FA7B}', '\u{55E2}'), + ('\u{FA7C}', '\u{585A}'), + ('\u{FA7D}', '\u{58B3}'), + ('\u{FA7E}', '\u{5944}'), + ('\u{FA7F}', '\u{5954}'), + ('\u{FA80}', '\u{5A62}'), + ('\u{FA81}', '\u{5B28}'), + ('\u{FA82}', '\u{5ED2}'), + ('\u{FA83}', '\u{5ED9}'), + ('\u{FA84}', '\u{5F69}'), + ('\u{FA85}', '\u{5FAD}'), + ('\u{FA86}', '\u{60D8}'), + ('\u{FA87}', '\u{614E}'), + ('\u{FA88}', '\u{6108}'), + ('\u{FA89}', '\u{618E}'), + ('\u{FA8A}', '\u{6160}'), + ('\u{FA8B}', '\u{61F2}'), + ('\u{FA8C}', '\u{6234}'), + ('\u{FA8D}', '\u{63C4}'), + ('\u{FA8E}', '\u{641C}'), + ('\u{FA8F}', '\u{6452}'), + ('\u{FA90}', '\u{6556}'), + ('\u{FA91}', '\u{6674}'), + ('\u{FA92}', '\u{6717}'), + ('\u{FA93}', '\u{671B}'), + ('\u{FA94}', '\u{6756}'), + ('\u{FA95}', '\u{6B79}'), + ('\u{FA96}', '\u{6BBA}'), + ('\u{FA97}', '\u{6D41}'), + ('\u{FA98}', '\u{6EDB}'), + ('\u{FA99}', '\u{6ECB}'), + ('\u{FA9A}', '\u{6F22}'), + ('\u{FA9B}', '\u{701E}'), + ('\u{FA9C}', '\u{716E}'), + ('\u{FA9D}', '\u{77A7}'), + ('\u{FA9E}', '\u{7235}'), + ('\u{FA9F}', '\u{72AF}'), + ('\u{FAA0}', '\u{732A}'), + ('\u{FAA1}', '\u{7471}'), + ('\u{FAA2}', '\u{7506}'), + ('\u{FAA3}', '\u{753B}'), + ('\u{FAA4}', '\u{761D}'), + ('\u{FAA5}', '\u{761F}'), + ('\u{FAA6}', '\u{76CA}'), + ('\u{FAA7}', '\u{76DB}'), + ('\u{FAA8}', '\u{76F4}'), + ('\u{FAA9}', '\u{774A}'), + ('\u{FAAA}', '\u{7740}'), + ('\u{FAAB}', '\u{78CC}'), + ('\u{FAAC}', '\u{7AB1}'), + ('\u{FAAD}', '\u{7BC0}'), + ('\u{FAAE}', '\u{7C7B}'), + ('\u{FAAF}', '\u{7D5B}'), + ('\u{FAB0}', '\u{7DF4}'), + ('\u{FAB1}', '\u{7F3E}'), + ('\u{FAB2}', '\u{8005}'), + ('\u{FAB3}', '\u{8352}'), + ('\u{FAB4}', '\u{83EF}'), + ('\u{FAB5}', '\u{8779}'), + ('\u{FAB6}', '\u{8941}'), + ('\u{FAB7}', '\u{8986}'), + ('\u{FAB8}', '\u{8996}'), + ('\u{FAB9}', '\u{8ABF}'), + ('\u{FABA}', '\u{8AF8}'), + ('\u{FABB}', '\u{8ACB}'), + ('\u{FABC}', '\u{8B01}'), + ('\u{FABD}', '\u{8AFE}'), + ('\u{FABE}', '\u{8AED}'), + ('\u{FABF}', '\u{8B39}'), + ('\u{FAC0}', '\u{8B8A}'), + ('\u{FAC1}', '\u{8D08}'), + ('\u{FAC2}', '\u{8F38}'), + ('\u{FAC3}', '\u{9072}'), + ('\u{FAC4}', '\u{9199}'), + ('\u{FAC5}', '\u{9276}'), + ('\u{FAC6}', '\u{967C}'), + ('\u{FAC7}', '\u{96E3}'), + ('\u{FAC8}', '\u{9756}'), + ('\u{FAC9}', '\u{97DB}'), + ('\u{FACA}', '\u{97FF}'), + ('\u{FACB}', '\u{980B}'), + ('\u{FACC}', '\u{983B}'), + ('\u{FACD}', '\u{9B12}'), + ('\u{FACE}', '\u{9F9C}'), + ('\u{FACF}', '\u{2284A}'), + ('\u{FAD0}', '\u{22844}'), + ('\u{FAD1}', '\u{233D5}'), + ('\u{FAD2}', '\u{3B9D}'), + ('\u{FAD3}', '\u{4018}'), + ('\u{FAD4}', '\u{4039}'), + ('\u{FAD5}', '\u{25249}'), + ('\u{FAD6}', '\u{25CD0}'), + ('\u{FAD7}', '\u{27ED3}'), + ('\u{FAD8}', '\u{9F43}'), + ('\u{FAD9}', '\u{9F8E}'), + ('\u{FB1D}', '\u{05D9}'), + ('\u{FB1F}', '\u{05F2}'), + ('\u{FB2A}', '\u{05E9}'), + ('\u{FB2B}', '\u{05E9}'), + ('\u{FB2C}', '\u{05E9}'), + ('\u{FB2D}', '\u{05E9}'), + ('\u{FB2E}', '\u{05D0}'), + ('\u{FB2F}', '\u{05D0}'), + ('\u{FB30}', '\u{05D0}'), + ('\u{FB31}', '\u{05D1}'), + ('\u{FB32}', '\u{05D2}'), + ('\u{FB33}', '\u{05D3}'), + ('\u{FB34}', '\u{05D4}'), + ('\u{FB35}', '\u{05D5}'), + ('\u{FB36}', '\u{05D6}'), + ('\u{FB38}', '\u{05D8}'), + ('\u{FB39}', '\u{05D9}'), + ('\u{FB3A}', '\u{05DA}'), + ('\u{FB3B}', '\u{05DB}'), + ('\u{FB3C}', '\u{05DC}'), + ('\u{FB3E}', '\u{05DE}'), + ('\u{FB40}', '\u{05E0}'), + ('\u{FB41}', '\u{05E1}'), + ('\u{FB43}', '\u{05E3}'), + ('\u{FB44}', '\u{05E4}'), + ('\u{FB46}', '\u{05E6}'), + ('\u{FB47}', '\u{05E7}'), + ('\u{FB48}', '\u{05E8}'), + ('\u{FB49}', '\u{05E9}'), + ('\u{FB4A}', '\u{05EA}'), + ('\u{FB4B}', '\u{05D5}'), + ('\u{FB4C}', '\u{05D1}'), + ('\u{FB4D}', '\u{05DB}'), + ('\u{FB4E}', '\u{05E4}'), + ('\u{1109A}', '\u{11099}'), + ('\u{1109C}', '\u{1109B}'), + ('\u{110AB}', '\u{110A5}'), + ('\u{1D15E}', '\u{1D157}'), + ('\u{1D15F}', '\u{1D158}'), + ('\u{1D160}', '\u{1D158}'), + ('\u{1D161}', '\u{1D158}'), + ('\u{1D162}', '\u{1D158}'), + ('\u{1D163}', '\u{1D158}'), + ('\u{1D164}', '\u{1D158}'), + ('\u{1D1BB}', '\u{1D1B9}'), + ('\u{1D1BC}', '\u{1D1BA}'), + ('\u{1D1BD}', '\u{1D1B9}'), + ('\u{1D1BE}', '\u{1D1BA}'), + ('\u{1D1BF}', '\u{1D1B9}'), + ('\u{1D1C0}', '\u{1D1BA}'), + ('\u{2F800}', '\u{4E3D}'), + ('\u{2F801}', '\u{4E38}'), + ('\u{2F802}', '\u{4E41}'), + ('\u{2F803}', '\u{20122}'), + ('\u{2F804}', '\u{4F60}'), + ('\u{2F805}', '\u{4FAE}'), + ('\u{2F806}', '\u{4FBB}'), + ('\u{2F807}', '\u{5002}'), + ('\u{2F808}', '\u{507A}'), + ('\u{2F809}', '\u{5099}'), + ('\u{2F80A}', '\u{50E7}'), + ('\u{2F80B}', '\u{50CF}'), + ('\u{2F80C}', '\u{349E}'), + ('\u{2F80D}', '\u{2063A}'), + ('\u{2F80E}', '\u{514D}'), + ('\u{2F80F}', '\u{5154}'), + ('\u{2F810}', '\u{5164}'), + ('\u{2F811}', '\u{5177}'), + ('\u{2F812}', '\u{2051C}'), + ('\u{2F813}', '\u{34B9}'), + ('\u{2F814}', '\u{5167}'), + ('\u{2F815}', '\u{518D}'), + ('\u{2F816}', '\u{2054B}'), + ('\u{2F817}', '\u{5197}'), + ('\u{2F818}', '\u{51A4}'), + ('\u{2F819}', '\u{4ECC}'), + ('\u{2F81A}', '\u{51AC}'), + ('\u{2F81B}', '\u{51B5}'), + ('\u{2F81C}', '\u{291DF}'), + ('\u{2F81D}', '\u{51F5}'), + ('\u{2F81E}', '\u{5203}'), + ('\u{2F81F}', '\u{34DF}'), + ('\u{2F820}', '\u{523B}'), + ('\u{2F821}', '\u{5246}'), + ('\u{2F822}', '\u{5272}'), + ('\u{2F823}', '\u{5277}'), + ('\u{2F824}', '\u{3515}'), + ('\u{2F825}', '\u{52C7}'), + ('\u{2F826}', '\u{52C9}'), + ('\u{2F827}', '\u{52E4}'), + ('\u{2F828}', '\u{52FA}'), + ('\u{2F829}', '\u{5305}'), + ('\u{2F82A}', '\u{5306}'), + ('\u{2F82B}', '\u{5317}'), + ('\u{2F82C}', '\u{5349}'), + ('\u{2F82D}', '\u{5351}'), + ('\u{2F82E}', '\u{535A}'), + ('\u{2F82F}', '\u{5373}'), + ('\u{2F830}', '\u{537D}'), + ('\u{2F831}', '\u{537F}'), + ('\u{2F832}', '\u{537F}'), + ('\u{2F833}', '\u{537F}'), + ('\u{2F834}', '\u{20A2C}'), + ('\u{2F835}', '\u{7070}'), + ('\u{2F836}', '\u{53CA}'), + ('\u{2F837}', '\u{53DF}'), + ('\u{2F838}', '\u{20B63}'), + ('\u{2F839}', '\u{53EB}'), + ('\u{2F83A}', '\u{53F1}'), + ('\u{2F83B}', '\u{5406}'), + ('\u{2F83C}', '\u{549E}'), + ('\u{2F83D}', '\u{5438}'), + ('\u{2F83E}', '\u{5448}'), + ('\u{2F83F}', '\u{5468}'), + ('\u{2F840}', '\u{54A2}'), + ('\u{2F841}', '\u{54F6}'), + ('\u{2F842}', '\u{5510}'), + ('\u{2F843}', '\u{5553}'), + ('\u{2F844}', '\u{5563}'), + ('\u{2F845}', '\u{5584}'), + ('\u{2F846}', '\u{5584}'), + ('\u{2F847}', '\u{5599}'), + ('\u{2F848}', '\u{55AB}'), + ('\u{2F849}', '\u{55B3}'), + ('\u{2F84A}', '\u{55C2}'), + ('\u{2F84B}', '\u{5716}'), + ('\u{2F84C}', '\u{5606}'), + ('\u{2F84D}', '\u{5717}'), + ('\u{2F84E}', '\u{5651}'), + ('\u{2F84F}', '\u{5674}'), + ('\u{2F850}', '\u{5207}'), + ('\u{2F851}', '\u{58EE}'), + ('\u{2F852}', '\u{57CE}'), + ('\u{2F853}', '\u{57F4}'), + ('\u{2F854}', '\u{580D}'), + ('\u{2F855}', '\u{578B}'), + ('\u{2F856}', '\u{5832}'), + ('\u{2F857}', '\u{5831}'), + ('\u{2F858}', '\u{58AC}'), + ('\u{2F859}', '\u{214E4}'), + ('\u{2F85A}', '\u{58F2}'), + ('\u{2F85B}', '\u{58F7}'), + ('\u{2F85C}', '\u{5906}'), + ('\u{2F85D}', '\u{591A}'), + ('\u{2F85E}', '\u{5922}'), + ('\u{2F85F}', '\u{5962}'), + ('\u{2F860}', '\u{216A8}'), + ('\u{2F861}', '\u{216EA}'), + ('\u{2F862}', '\u{59EC}'), + ('\u{2F863}', '\u{5A1B}'), + ('\u{2F864}', '\u{5A27}'), + ('\u{2F865}', '\u{59D8}'), + ('\u{2F866}', '\u{5A66}'), + ('\u{2F867}', '\u{36EE}'), + ('\u{2F868}', '\u{36FC}'), + ('\u{2F869}', '\u{5B08}'), + ('\u{2F86A}', '\u{5B3E}'), + ('\u{2F86B}', '\u{5B3E}'), + ('\u{2F86C}', '\u{219C8}'), + ('\u{2F86D}', '\u{5BC3}'), + ('\u{2F86E}', '\u{5BD8}'), + ('\u{2F86F}', '\u{5BE7}'), + ('\u{2F870}', '\u{5BF3}'), + ('\u{2F871}', '\u{21B18}'), + ('\u{2F872}', '\u{5BFF}'), + ('\u{2F873}', '\u{5C06}'), + ('\u{2F874}', '\u{5F53}'), + ('\u{2F875}', '\u{5C22}'), + ('\u{2F876}', '\u{3781}'), + ('\u{2F877}', '\u{5C60}'), + ('\u{2F878}', '\u{5C6E}'), + ('\u{2F879}', '\u{5CC0}'), + ('\u{2F87A}', '\u{5C8D}'), + ('\u{2F87B}', '\u{21DE4}'), + ('\u{2F87C}', '\u{5D43}'), + ('\u{2F87D}', '\u{21DE6}'), + ('\u{2F87E}', '\u{5D6E}'), + ('\u{2F87F}', '\u{5D6B}'), + ('\u{2F880}', '\u{5D7C}'), + ('\u{2F881}', '\u{5DE1}'), + ('\u{2F882}', '\u{5DE2}'), + ('\u{2F883}', '\u{382F}'), + ('\u{2F884}', '\u{5DFD}'), + ('\u{2F885}', '\u{5E28}'), + ('\u{2F886}', '\u{5E3D}'), + ('\u{2F887}', '\u{5E69}'), + ('\u{2F888}', '\u{3862}'), + ('\u{2F889}', '\u{22183}'), + ('\u{2F88A}', '\u{387C}'), + ('\u{2F88B}', '\u{5EB0}'), + ('\u{2F88C}', '\u{5EB3}'), + ('\u{2F88D}', '\u{5EB6}'), + ('\u{2F88E}', '\u{5ECA}'), + ('\u{2F88F}', '\u{2A392}'), + ('\u{2F890}', '\u{5EFE}'), + ('\u{2F891}', '\u{22331}'), + ('\u{2F892}', '\u{22331}'), + ('\u{2F893}', '\u{8201}'), + ('\u{2F894}', '\u{5F22}'), + ('\u{2F895}', '\u{5F22}'), + ('\u{2F896}', '\u{38C7}'), + ('\u{2F897}', '\u{232B8}'), + ('\u{2F898}', '\u{261DA}'), + ('\u{2F899}', '\u{5F62}'), + ('\u{2F89A}', '\u{5F6B}'), + ('\u{2F89B}', '\u{38E3}'), + ('\u{2F89C}', '\u{5F9A}'), + ('\u{2F89D}', '\u{5FCD}'), + ('\u{2F89E}', '\u{5FD7}'), + ('\u{2F89F}', '\u{5FF9}'), + ('\u{2F8A0}', '\u{6081}'), + ('\u{2F8A1}', '\u{393A}'), + ('\u{2F8A2}', '\u{391C}'), + ('\u{2F8A3}', '\u{6094}'), + ('\u{2F8A4}', '\u{226D4}'), + ('\u{2F8A5}', '\u{60C7}'), + ('\u{2F8A6}', '\u{6148}'), + ('\u{2F8A7}', '\u{614C}'), + ('\u{2F8A8}', '\u{614E}'), + ('\u{2F8A9}', '\u{614C}'), + ('\u{2F8AA}', '\u{617A}'), + ('\u{2F8AB}', '\u{618E}'), + ('\u{2F8AC}', '\u{61B2}'), + ('\u{2F8AD}', '\u{61A4}'), + ('\u{2F8AE}', '\u{61AF}'), + ('\u{2F8AF}', '\u{61DE}'), + ('\u{2F8B0}', '\u{61F2}'), + ('\u{2F8B1}', '\u{61F6}'), + ('\u{2F8B2}', '\u{6210}'), + ('\u{2F8B3}', '\u{621B}'), + ('\u{2F8B4}', '\u{625D}'), + ('\u{2F8B5}', '\u{62B1}'), + ('\u{2F8B6}', '\u{62D4}'), + ('\u{2F8B7}', '\u{6350}'), + ('\u{2F8B8}', '\u{22B0C}'), + ('\u{2F8B9}', '\u{633D}'), + ('\u{2F8BA}', '\u{62FC}'), + ('\u{2F8BB}', '\u{6368}'), + ('\u{2F8BC}', '\u{6383}'), + ('\u{2F8BD}', '\u{63E4}'), + ('\u{2F8BE}', '\u{22BF1}'), + ('\u{2F8BF}', '\u{6422}'), + ('\u{2F8C0}', '\u{63C5}'), + ('\u{2F8C1}', '\u{63A9}'), + ('\u{2F8C2}', '\u{3A2E}'), + ('\u{2F8C3}', '\u{6469}'), + ('\u{2F8C4}', '\u{647E}'), + ('\u{2F8C5}', '\u{649D}'), + ('\u{2F8C6}', '\u{6477}'), + ('\u{2F8C7}', '\u{3A6C}'), + ('\u{2F8C8}', '\u{654F}'), + ('\u{2F8C9}', '\u{656C}'), + ('\u{2F8CA}', '\u{2300A}'), + ('\u{2F8CB}', '\u{65E3}'), + ('\u{2F8CC}', '\u{66F8}'), + ('\u{2F8CD}', '\u{6649}'), + ('\u{2F8CE}', '\u{3B19}'), + ('\u{2F8CF}', '\u{6691}'), + ('\u{2F8D0}', '\u{3B08}'), + ('\u{2F8D1}', '\u{3AE4}'), + ('\u{2F8D2}', '\u{5192}'), + ('\u{2F8D3}', '\u{5195}'), + ('\u{2F8D4}', '\u{6700}'), + ('\u{2F8D5}', '\u{669C}'), + ('\u{2F8D6}', '\u{80AD}'), + ('\u{2F8D7}', '\u{43D9}'), + ('\u{2F8D8}', '\u{6717}'), + ('\u{2F8D9}', '\u{671B}'), + ('\u{2F8DA}', '\u{6721}'), + ('\u{2F8DB}', '\u{675E}'), + ('\u{2F8DC}', '\u{6753}'), + ('\u{2F8DD}', '\u{233C3}'), + ('\u{2F8DE}', '\u{3B49}'), + ('\u{2F8DF}', '\u{67FA}'), + ('\u{2F8E0}', '\u{6785}'), + ('\u{2F8E1}', '\u{6852}'), + ('\u{2F8E2}', '\u{6885}'), + ('\u{2F8E3}', '\u{2346D}'), + ('\u{2F8E4}', '\u{688E}'), + ('\u{2F8E5}', '\u{681F}'), + ('\u{2F8E6}', '\u{6914}'), + ('\u{2F8E7}', '\u{3B9D}'), + ('\u{2F8E8}', '\u{6942}'), + ('\u{2F8E9}', '\u{69A3}'), + ('\u{2F8EA}', '\u{69EA}'), + ('\u{2F8EB}', '\u{6AA8}'), + ('\u{2F8EC}', '\u{236A3}'), + ('\u{2F8ED}', '\u{6ADB}'), + ('\u{2F8EE}', '\u{3C18}'), + ('\u{2F8EF}', '\u{6B21}'), + ('\u{2F8F0}', '\u{238A7}'), + ('\u{2F8F1}', '\u{6B54}'), + ('\u{2F8F2}', '\u{3C4E}'), + ('\u{2F8F3}', '\u{6B72}'), + ('\u{2F8F4}', '\u{6B9F}'), + ('\u{2F8F5}', '\u{6BBA}'), + ('\u{2F8F6}', '\u{6BBB}'), + ('\u{2F8F7}', '\u{23A8D}'), + ('\u{2F8F8}', '\u{21D0B}'), + ('\u{2F8F9}', '\u{23AFA}'), + ('\u{2F8FA}', '\u{6C4E}'), + ('\u{2F8FB}', '\u{23CBC}'), + ('\u{2F8FC}', '\u{6CBF}'), + ('\u{2F8FD}', '\u{6CCD}'), + ('\u{2F8FE}', '\u{6C67}'), + ('\u{2F8FF}', '\u{6D16}'), + ('\u{2F900}', '\u{6D3E}'), + ('\u{2F901}', '\u{6D77}'), + ('\u{2F902}', '\u{6D41}'), + ('\u{2F903}', '\u{6D69}'), + ('\u{2F904}', '\u{6D78}'), + ('\u{2F905}', '\u{6D85}'), + ('\u{2F906}', '\u{23D1E}'), + ('\u{2F907}', '\u{6D34}'), + ('\u{2F908}', '\u{6E2F}'), + ('\u{2F909}', '\u{6E6E}'), + ('\u{2F90A}', '\u{3D33}'), + ('\u{2F90B}', '\u{6ECB}'), + ('\u{2F90C}', '\u{6EC7}'), + ('\u{2F90D}', '\u{23ED1}'), + ('\u{2F90E}', '\u{6DF9}'), + ('\u{2F90F}', '\u{6F6E}'), + ('\u{2F910}', '\u{23F5E}'), + ('\u{2F911}', '\u{23F8E}'), + ('\u{2F912}', '\u{6FC6}'), + ('\u{2F913}', '\u{7039}'), + ('\u{2F914}', '\u{701E}'), + ('\u{2F915}', '\u{701B}'), + ('\u{2F916}', '\u{3D96}'), + ('\u{2F917}', '\u{704A}'), + ('\u{2F918}', '\u{707D}'), + ('\u{2F919}', '\u{7077}'), + ('\u{2F91A}', '\u{70AD}'), + ('\u{2F91B}', '\u{20525}'), + ('\u{2F91C}', '\u{7145}'), + ('\u{2F91D}', '\u{24263}'), + ('\u{2F91E}', '\u{719C}'), + ('\u{2F91F}', '\u{243AB}'), + ('\u{2F920}', '\u{7228}'), + ('\u{2F921}', '\u{7235}'), + ('\u{2F922}', '\u{7250}'), + ('\u{2F923}', '\u{24608}'), + ('\u{2F924}', '\u{7280}'), + ('\u{2F925}', '\u{7295}'), + ('\u{2F926}', '\u{24735}'), + ('\u{2F927}', '\u{24814}'), + ('\u{2F928}', '\u{737A}'), + ('\u{2F929}', '\u{738B}'), + ('\u{2F92A}', '\u{3EAC}'), + ('\u{2F92B}', '\u{73A5}'), + ('\u{2F92C}', '\u{3EB8}'), + ('\u{2F92D}', '\u{3EB8}'), + ('\u{2F92E}', '\u{7447}'), + ('\u{2F92F}', '\u{745C}'), + ('\u{2F930}', '\u{7471}'), + ('\u{2F931}', '\u{7485}'), + ('\u{2F932}', '\u{74CA}'), + ('\u{2F933}', '\u{3F1B}'), + ('\u{2F934}', '\u{7524}'), + ('\u{2F935}', '\u{24C36}'), + ('\u{2F936}', '\u{753E}'), + ('\u{2F937}', '\u{24C92}'), + ('\u{2F938}', '\u{7570}'), + ('\u{2F939}', '\u{2219F}'), + ('\u{2F93A}', '\u{7610}'), + ('\u{2F93B}', '\u{24FA1}'), + ('\u{2F93C}', '\u{24FB8}'), + ('\u{2F93D}', '\u{25044}'), + ('\u{2F93E}', '\u{3FFC}'), + ('\u{2F93F}', '\u{4008}'), + ('\u{2F940}', '\u{76F4}'), + ('\u{2F941}', '\u{250F3}'), + ('\u{2F942}', '\u{250F2}'), + ('\u{2F943}', '\u{25119}'), + ('\u{2F944}', '\u{25133}'), + ('\u{2F945}', '\u{771E}'), + ('\u{2F946}', '\u{771F}'), + ('\u{2F947}', '\u{771F}'), + ('\u{2F948}', '\u{774A}'), + ('\u{2F949}', '\u{4039}'), + ('\u{2F94A}', '\u{778B}'), + ('\u{2F94B}', '\u{4046}'), + ('\u{2F94C}', '\u{4096}'), + ('\u{2F94D}', '\u{2541D}'), + ('\u{2F94E}', '\u{784E}'), + ('\u{2F94F}', '\u{788C}'), + ('\u{2F950}', '\u{78CC}'), + ('\u{2F951}', '\u{40E3}'), + ('\u{2F952}', '\u{25626}'), + ('\u{2F953}', '\u{7956}'), + ('\u{2F954}', '\u{2569A}'), + ('\u{2F955}', '\u{256C5}'), + ('\u{2F956}', '\u{798F}'), + ('\u{2F957}', '\u{79EB}'), + ('\u{2F958}', '\u{412F}'), + ('\u{2F959}', '\u{7A40}'), + ('\u{2F95A}', '\u{7A4A}'), + ('\u{2F95B}', '\u{7A4F}'), + ('\u{2F95C}', '\u{2597C}'), + ('\u{2F95D}', '\u{25AA7}'), + ('\u{2F95E}', '\u{25AA7}'), + ('\u{2F95F}', '\u{7AEE}'), + ('\u{2F960}', '\u{4202}'), + ('\u{2F961}', '\u{25BAB}'), + ('\u{2F962}', '\u{7BC6}'), + ('\u{2F963}', '\u{7BC9}'), + ('\u{2F964}', '\u{4227}'), + ('\u{2F965}', '\u{25C80}'), + ('\u{2F966}', '\u{7CD2}'), + ('\u{2F967}', '\u{42A0}'), + ('\u{2F968}', '\u{7CE8}'), + ('\u{2F969}', '\u{7CE3}'), + ('\u{2F96A}', '\u{7D00}'), + ('\u{2F96B}', '\u{25F86}'), + ('\u{2F96C}', '\u{7D63}'), + ('\u{2F96D}', '\u{4301}'), + ('\u{2F96E}', '\u{7DC7}'), + ('\u{2F96F}', '\u{7E02}'), + ('\u{2F970}', '\u{7E45}'), + ('\u{2F971}', '\u{4334}'), + ('\u{2F972}', '\u{26228}'), + ('\u{2F973}', '\u{26247}'), + ('\u{2F974}', '\u{4359}'), + ('\u{2F975}', '\u{262D9}'), + ('\u{2F976}', '\u{7F7A}'), + ('\u{2F977}', '\u{2633E}'), + ('\u{2F978}', '\u{7F95}'), + ('\u{2F979}', '\u{7FFA}'), + ('\u{2F97A}', '\u{8005}'), + ('\u{2F97B}', '\u{264DA}'), + ('\u{2F97C}', '\u{26523}'), + ('\u{2F97D}', '\u{8060}'), + ('\u{2F97E}', '\u{265A8}'), + ('\u{2F97F}', '\u{8070}'), + ('\u{2F980}', '\u{2335F}'), + ('\u{2F981}', '\u{43D5}'), + ('\u{2F982}', '\u{80B2}'), + ('\u{2F983}', '\u{8103}'), + ('\u{2F984}', '\u{440B}'), + ('\u{2F985}', '\u{813E}'), + ('\u{2F986}', '\u{5AB5}'), + ('\u{2F987}', '\u{267A7}'), + ('\u{2F988}', '\u{267B5}'), + ('\u{2F989}', '\u{23393}'), + ('\u{2F98A}', '\u{2339C}'), + ('\u{2F98B}', '\u{8201}'), + ('\u{2F98C}', '\u{8204}'), + ('\u{2F98D}', '\u{8F9E}'), + ('\u{2F98E}', '\u{446B}'), + ('\u{2F98F}', '\u{8291}'), + ('\u{2F990}', '\u{828B}'), + ('\u{2F991}', '\u{829D}'), + ('\u{2F992}', '\u{52B3}'), + ('\u{2F993}', '\u{82B1}'), + ('\u{2F994}', '\u{82B3}'), + ('\u{2F995}', '\u{82BD}'), + ('\u{2F996}', '\u{82E6}'), + ('\u{2F997}', '\u{26B3C}'), + ('\u{2F998}', '\u{82E5}'), + ('\u{2F999}', '\u{831D}'), + ('\u{2F99A}', '\u{8363}'), + ('\u{2F99B}', '\u{83AD}'), + ('\u{2F99C}', '\u{8323}'), + ('\u{2F99D}', '\u{83BD}'), + ('\u{2F99E}', '\u{83E7}'), + ('\u{2F99F}', '\u{8457}'), + ('\u{2F9A0}', '\u{8353}'), + ('\u{2F9A1}', '\u{83CA}'), + ('\u{2F9A2}', '\u{83CC}'), + ('\u{2F9A3}', '\u{83DC}'), + ('\u{2F9A4}', '\u{26C36}'), + ('\u{2F9A5}', '\u{26D6B}'), + ('\u{2F9A6}', '\u{26CD5}'), + ('\u{2F9A7}', '\u{452B}'), + ('\u{2F9A8}', '\u{84F1}'), + ('\u{2F9A9}', '\u{84F3}'), + ('\u{2F9AA}', '\u{8516}'), + ('\u{2F9AB}', '\u{273CA}'), + ('\u{2F9AC}', '\u{8564}'), + ('\u{2F9AD}', '\u{26F2C}'), + ('\u{2F9AE}', '\u{455D}'), + ('\u{2F9AF}', '\u{4561}'), + ('\u{2F9B0}', '\u{26FB1}'), + ('\u{2F9B1}', '\u{270D2}'), + ('\u{2F9B2}', '\u{456B}'), + ('\u{2F9B3}', '\u{8650}'), + ('\u{2F9B4}', '\u{865C}'), + ('\u{2F9B5}', '\u{8667}'), + ('\u{2F9B6}', '\u{8669}'), + ('\u{2F9B7}', '\u{86A9}'), + ('\u{2F9B8}', '\u{8688}'), + ('\u{2F9B9}', '\u{870E}'), + ('\u{2F9BA}', '\u{86E2}'), + ('\u{2F9BB}', '\u{8779}'), + ('\u{2F9BC}', '\u{8728}'), + ('\u{2F9BD}', '\u{876B}'), + ('\u{2F9BE}', '\u{8786}'), + ('\u{2F9BF}', '\u{45D7}'), + ('\u{2F9C0}', '\u{87E1}'), + ('\u{2F9C1}', '\u{8801}'), + ('\u{2F9C2}', '\u{45F9}'), + ('\u{2F9C3}', '\u{8860}'), + ('\u{2F9C4}', '\u{8863}'), + ('\u{2F9C5}', '\u{27667}'), + ('\u{2F9C6}', '\u{88D7}'), + ('\u{2F9C7}', '\u{88DE}'), + ('\u{2F9C8}', '\u{4635}'), + ('\u{2F9C9}', '\u{88FA}'), + ('\u{2F9CA}', '\u{34BB}'), + ('\u{2F9CB}', '\u{278AE}'), + ('\u{2F9CC}', '\u{27966}'), + ('\u{2F9CD}', '\u{46BE}'), + ('\u{2F9CE}', '\u{46C7}'), + ('\u{2F9CF}', '\u{8AA0}'), + ('\u{2F9D0}', '\u{8AED}'), + ('\u{2F9D1}', '\u{8B8A}'), + ('\u{2F9D2}', '\u{8C55}'), + ('\u{2F9D3}', '\u{27CA8}'), + ('\u{2F9D4}', '\u{8CAB}'), + ('\u{2F9D5}', '\u{8CC1}'), + ('\u{2F9D6}', '\u{8D1B}'), + ('\u{2F9D7}', '\u{8D77}'), + ('\u{2F9D8}', '\u{27F2F}'), + ('\u{2F9D9}', '\u{20804}'), + ('\u{2F9DA}', '\u{8DCB}'), + ('\u{2F9DB}', '\u{8DBC}'), + ('\u{2F9DC}', '\u{8DF0}'), + ('\u{2F9DD}', '\u{208DE}'), + ('\u{2F9DE}', '\u{8ED4}'), + ('\u{2F9DF}', '\u{8F38}'), + ('\u{2F9E0}', '\u{285D2}'), + ('\u{2F9E1}', '\u{285ED}'), + ('\u{2F9E2}', '\u{9094}'), + ('\u{2F9E3}', '\u{90F1}'), + ('\u{2F9E4}', '\u{9111}'), + ('\u{2F9E5}', '\u{2872E}'), + ('\u{2F9E6}', '\u{911B}'), + ('\u{2F9E7}', '\u{9238}'), + ('\u{2F9E8}', '\u{92D7}'), + ('\u{2F9E9}', '\u{92D8}'), + ('\u{2F9EA}', '\u{927C}'), + ('\u{2F9EB}', '\u{93F9}'), + ('\u{2F9EC}', '\u{9415}'), + ('\u{2F9ED}', '\u{28BFA}'), + ('\u{2F9EE}', '\u{958B}'), + ('\u{2F9EF}', '\u{4995}'), + ('\u{2F9F0}', '\u{95B7}'), + ('\u{2F9F1}', '\u{28D77}'), + ('\u{2F9F2}', '\u{49E6}'), + ('\u{2F9F3}', '\u{96C3}'), + ('\u{2F9F4}', '\u{5DB2}'), + ('\u{2F9F5}', '\u{9723}'), + ('\u{2F9F6}', '\u{29145}'), + ('\u{2F9F7}', '\u{2921A}'), + ('\u{2F9F8}', '\u{4A6E}'), + ('\u{2F9F9}', '\u{4A76}'), + ('\u{2F9FA}', '\u{97E0}'), + ('\u{2F9FB}', '\u{2940A}'), + ('\u{2F9FC}', '\u{4AB2}'), + ('\u{2F9FD}', '\u{29496}'), + ('\u{2F9FE}', '\u{980B}'), + ('\u{2F9FF}', '\u{980B}'), + ('\u{2FA00}', '\u{9829}'), + ('\u{2FA01}', '\u{295B6}'), + ('\u{2FA02}', '\u{98E2}'), + ('\u{2FA03}', '\u{4B33}'), + ('\u{2FA04}', '\u{9929}'), + ('\u{2FA05}', '\u{99A7}'), + ('\u{2FA06}', '\u{99C2}'), + ('\u{2FA07}', '\u{99FE}'), + ('\u{2FA08}', '\u{4BCE}'), + ('\u{2FA09}', '\u{29B30}'), + ('\u{2FA0A}', '\u{9B12}'), + ('\u{2FA0B}', '\u{9C40}'), + ('\u{2FA0C}', '\u{9CFD}'), + ('\u{2FA0D}', '\u{4CCE}'), + ('\u{2FA0E}', '\u{4CED}'), + ('\u{2FA0F}', '\u{9D67}'), + ('\u{2FA10}', '\u{2A0CE}'), + ('\u{2FA11}', '\u{4CF8}'), + ('\u{2FA12}', '\u{2A105}'), + ('\u{2FA13}', '\u{2A20E}'), + ('\u{2FA14}', '\u{2A291}'), + ('\u{2FA15}', '\u{9EBB}'), + ('\u{2FA16}', '\u{4D56}'), + ('\u{2FA17}', '\u{9EF9}'), + ('\u{2FA18}', '\u{9EFE}'), + ('\u{2FA19}', '\u{9F05}'), + ('\u{2FA1A}', '\u{9F0F}'), + ('\u{2FA1B}', '\u{9F16}'), + ('\u{2FA1C}', '\u{9F3B}'), + ('\u{2FA1D}', '\u{2A600}'), +]; diff --git a/src/specializer/exec/tests.rs b/src/specializer/exec/tests.rs index 4cbe6b8..45a98d4 100644 --- a/src/specializer/exec/tests.rs +++ b/src/specializer/exec/tests.rs @@ -514,18 +514,21 @@ fn eval1(body: &str, out_col: &str) -> Result>, Trap> { #[test] fn pin_integer_overflow_and_division_traps() { + // Overflow texts are DuckDB's own, verbatim with operand values + // (wave-3 pins); division-by-zero texts are internal (unreachable + // through SQL — the frontend CASE guard yields NULL first). for (expr, needle) in [ ( " %a = const.i64 9223372036854775807\n %b = const.i64 1\n %r = iadd %a, %b", - "overflow in iadd", + "Overflow in addition of INT64 (9223372036854775807 + 1)!", ), ( " %a = const.i64 -9223372036854775808\n %b = const.i64 1\n %r = isub %a, %b", - "overflow in isub", + "Overflow in subtraction of INT64 (-9223372036854775808 - 1)!", ), ( " %a = const.i64 4611686018427387904\n %b = const.i64 4\n %r = imul %a, %b", - "overflow in imul", + "Overflow in multiplication of INT64 (4611686018427387904 * 4)!", ), ( " %a = const.i64 1\n %b = const.i64 0\n %r = idiv %a, %b", @@ -537,11 +540,11 @@ fn pin_integer_overflow_and_division_traps() { ), ( " %a = const.i64 -9223372036854775808\n %b = const.i64 -1\n %r = idiv %a, %b", - "overflow in idiv", + "Overflow in division of -9223372036854775808 / -1", ), ( " %a = const.i64 -9223372036854775808\n %b = const.i64 -1\n %r = irem %a, %b", - "overflow in irem", + "Overflow in division of -9223372036854775808 / -1", ), ] { let body = format!("{expr}\n store out.o, %r"); @@ -608,7 +611,7 @@ fn pin_frem_is_ieee_and_new_unaries() { fn pin_iabs_min_traps() { let body = " %m = const.i64 -9223372036854775808\n %a = iabs %m\n store out.o, %a"; let err = eval1(body, "o: i64").unwrap_err(); - assert!(err.0.contains("overflow"), "got '{}'", err.0); + assert!(err.0.contains("Overflow on abs"), "got '{}'", err.0); } #[test] diff --git a/src/specializer/fold.rs b/src/specializer/fold.rs index 8b422b9..67e40ec 100644 --- a/src/specializer/fold.rs +++ b/src/specializer/fold.rs @@ -113,6 +113,60 @@ pub fn fold(e: SExpr) -> SExpr { b: Box::new(b), }) } + // Wave-3 string ops: fold children only, same policy as Str2 — + // the ops stay runtime so constant trap rows keep their timing. + SKind::Str3 { op, a, b, c } => { + let a = fold(*a); + let b = fold(*b); + let c = fold(*c); + e(SKind::Str3 { + op, + a: Box::new(a), + b: Box::new(b), + c: Box::new(c), + }) + } + SKind::Str2i { op, a, n } => { + let a = fold(*a); + let n = fold(*n); + e(SKind::Str2i { + op, + a: Box::new(a), + n: Box::new(n), + }) + } + SKind::Spad { left, a, len, pad } => { + let a = fold(*a); + let len = fold(*len); + let pad = fold(*pad); + e(SKind::Spad { + left, + a: Box::new(a), + len: Box::new(len), + pad: Box::new(pad), + }) + } + SKind::Sslice { a, lo, hi } => { + let a = fold(*a); + let lo = fold(*lo); + let hi = fold(*hi); + e(SKind::Sslice { + a: Box::new(a), + lo: Box::new(lo), + hi: Box::new(hi), + }) + } + SKind::Sord { empty_zero, a } => { + let a = fold(*a); + e(SKind::Sord { + empty_zero, + a: Box::new(a), + }) + } + SKind::StripAccents(a) => { + let a = fold(*a); + e(SKind::StripAccents(Box::new(a))) + } SKind::Not(inner) => { let inner = fold(*inner); match as_const(&inner) { @@ -244,6 +298,9 @@ fn arith(op: ArithOp, a: &Lit, b: &Lit) -> Option { ArithOp::Sub => x.checked_sub(*y).map(Lit::I64), ArithOp::Mul => x.checked_mul(*y).map(Lit::I64), ArithOp::Rem => x.checked_rem(*y).map(Lit::I64), + // Zero/MIN//-1 stay unfolded; the frontend's CASE guard turns + // the zero row into NULL at runtime, never reaching the fold. + ArithOp::IDiv => x.checked_div(*y).map(Lit::I64), ArithOp::Div => unreachable!("/ is promoted to f64 by the frontend"), }, (Lit::F64(x), Lit::F64(y)) => Some(Lit::F64(match op { @@ -251,6 +308,10 @@ fn arith(op: ArithOp, a: &Lit, b: &Lit) -> Option { ArithOp::Sub => x - y, ArithOp::Mul => x * y, ArithOp::Div => x / y, + // `//` on doubles is plain division (wave-3 pins); the zero- + // divisor NULL comes from the frontend's CASE guard, which is + // never folded — this arm only sees the guarded default. + ArithOp::IDiv => x / y, // IEEE, exactly as exec/interp.rs: x % 0.0 is NaN, never traps. ArithOp::Rem => x % y, })), diff --git a/src/specializer/frontend.rs b/src/specializer/frontend.rs index 4c64861..918fb71 100644 --- a/src/specializer/frontend.rs +++ b/src/specializer/frontend.rs @@ -28,7 +28,7 @@ use sqlparser::dialect::DuckDbDialect; use sqlparser::parser::Parser; use super::fold::fold; -use super::ir::{BinOp, CmpPred, Col, Lit, NumOp1, StrOp2, TrimSide, Ty}; +use super::ir::{BinOp, CmpPred, Col, Lit, NumOp1, StrOp2, StrOp2i, StrOp3, TrimSide, Ty}; use super::plan::{ArithOp, JoinKind, JoinSpec, Rel, SExpr, SKind, StaticTable}; #[derive(Debug, PartialEq, Eq)] @@ -1042,6 +1042,7 @@ impl Binder<'_> { BinaryOperator::Minus => self.arith(ArithOp::Sub, a, b), BinaryOperator::Multiply => self.arith(ArithOp::Mul, a, b), BinaryOperator::Divide => self.arith(ArithOp::Div, a, b), + BinaryOperator::DuckIntegerDivide => self.arith(ArithOp::IDiv, a, b), BinaryOperator::Modulo => self.arith(ArithOp::Rem, a, b), BinaryOperator::Eq => self.cmp(CmpPred::Eq, a, b), BinaryOperator::NotEq => self.cmp(CmpPred::Ne, a, b), @@ -1224,17 +1225,26 @@ impl Binder<'_> { fn arith(&self, op: ArithOp, a: SExpr, b: SExpr) -> Result { let (a, b, ty) = numeric_promote(op, a, b)?; let nullable = a.nullable || b.nullable; - // DuckDB pin (2026-07-26): integer % by zero is NULL, not an error — + // DuckDB pins (2026-07-26, waves 1+3): integer % by zero is NULL, + // and `//`/divide() by zero is NULL on BOTH ints and doubles — // guard with a CASE unless the divisor is a provably non-zero - // literal. The irem trap stays reachable only for MIN % -1, where - // DuckDB traps too. Float % is IEEE (x % 0.0 = NaN), no guard. - if op == ArithOp::Rem - && ty == Ty::I64 - && !matches!(b.kind, SKind::Lit(Lit::I64(n)) if n != 0) - { + // literal. The idiv/irem traps stay reachable only for MIN op -1, + // where DuckDB traps too. Float % is IEEE (x % 0.0 = NaN), no guard. + let needs_guard = match (op, ty) { + (ArithOp::Rem, Ty::I64) => true, + (ArithOp::IDiv, _) => true, + _ => false, + }; + let nonzero_lit = matches!(b.kind, SKind::Lit(Lit::I64(n)) if n != 0) + || matches!(b.kind, SKind::Lit(Lit::F64(x)) if x != 0.0); + if needs_guard && !nonzero_lit { let zero = SExpr { - kind: SKind::Lit(Lit::I64(0)), - ty: Ty::I64, + kind: SKind::Lit(if ty == Ty::F64 { + Lit::F64(0.0) + } else { + Lit::I64(0) + }), + ty, nullable: false, }; // The guard must fire for a NULL divisor too: `b = 0` alone is @@ -1272,10 +1282,10 @@ impl Binder<'_> { }; return Ok(SExpr { kind: SKind::Case { - arms: vec![(cond, null_of(Ty::I64))], + arms: vec![(cond, null_of(ty))], default: Some(Box::new(rem)), }, - ty: Ty::I64, + ty, nullable: true, }); } @@ -1341,7 +1351,9 @@ impl Binder<'_> { } } match name.as_str() { - "upper" | "lower" => { + // ucase/lcase are alias-identical to upper/lower (wave-3 pins: + // exhaustive all-codepoint sweep, zero mismatches). + "upper" | "lower" | "ucase" | "lcase" => { let [arg] = args[..] else { return Err(PrepareError::Bind(format!( "{name} takes exactly 1 argument" @@ -1360,7 +1372,7 @@ impl Binder<'_> { let nullable = inner.nullable; Ok(SExpr { kind: SKind::StrCase { - upper: name == "upper", + upper: matches!(name.as_str(), "upper" | "ucase"), a: Box::new(inner), }, ty: Ty::Str, @@ -1757,6 +1769,476 @@ impl Binder<'_> { } } } + // Wave-3 similarity: all raw UTF-8 BYTE-based (measured); + // editdist3 == levenshtein and mismatches == hamming exactly. + "levenshtein" | "editdist3" | "damerau_levenshtein" | "jaccard" | "hamming" + | "mismatches" => { + let op = match name.as_str() { + "levenshtein" | "editdist3" => StrOp2::Levenshtein, + "damerau_levenshtein" => StrOp2::Damerau, + "jaccard" => StrOp2::Jaccard, + _ => StrOp2::Hamming, + }; + let [a, b] = args[..] else { + return Err(PrepareError::Bind(format!( + "{name} takes exactly 2 arguments" + ))); + }; + self.str2(&name, op, a, b) + } + "repeat" | "array_extract" | "list_extract" => { + let op = if name == "repeat" { + StrOp2i::Repeat + } else { + StrOp2i::Extract + }; + let [s, n] = args[..] else { + return Err(PrepareError::Bind(format!( + "{name} takes exactly 2 arguments" + ))); + }; + let (bs, bn) = (self.expr_or_null(s)?, self.expr_or_null(n)?); + let (Some(bs), Some(bn)) = (bs, bn) else { + return Ok(null_of(Ty::Str)); + }; + if bs.ty != Ty::Str { + if name == "repeat" { + // No implicit numeric->VARCHAR cast (measured). + return Err(PrepareError::Bind(format!( + "no function matches repeat({})", + bs.ty.name() + ))); + } + // The LIST overload has different out-of-range semantics + // (NULL, not '') — only the VARCHAR path ships in v0. + return Err(unsup(format!( + "{name} on {} (only VARCHAR subscripts in v0)", + bs.ty.name() + ))); + } + if bn.ty != Ty::I64 { + return Err(PrepareError::Bind(format!( + "no function matches {name}(str, {})", + bn.ty.name() + ))); + } + let nullable = bs.nullable || bn.nullable; + Ok(SExpr { + kind: SKind::Str2i { + op, + a: Box::new(bs), + n: Box::new(bn), + }, + ty: Ty::Str, + nullable, + }) + } + "array_slice" | "list_slice" => { + if args.len() == 4 { + // DuckDB rejects step slicing on VARCHAR for EVERY step + // value, including 1 (measured). + return Err(unsup( + "slice with step (DuckDB: not implemented for string types)", + )); + } + let [s, lo, hi] = args[..] else { + return Err(PrepareError::Bind(format!( + "{name} takes exactly 3 arguments" + ))); + }; + let (bs, blo, bhi) = ( + self.expr_or_null(s)?, + self.expr_or_null(lo)?, + self.expr_or_null(hi)?, + ); + let (Some(bs), Some(blo), Some(bhi)) = (bs, blo, bhi) else { + // NULL is NOT an open bound (measured): any NULL -> NULL. + return Ok(null_of(Ty::Str)); + }; + if bs.ty != Ty::Str { + return Err(unsup(format!( + "{name} on {} (only VARCHAR subscripts in v0)", + bs.ty.name() + ))); + } + for e in [&blo, &bhi] { + if e.ty != Ty::I64 { + return Err(PrepareError::Bind(format!( + "no function matches {name}(str, {}, {})", + blo.ty.name(), + bhi.ty.name() + ))); + } + } + let nullable = bs.nullable || blo.nullable || bhi.nullable; + Ok(SExpr { + kind: SKind::Sslice { + a: Box::new(bs), + lo: Box::new(blo), + hi: Box::new(bhi), + }, + ty: Ty::Str, + nullable, + }) + } + "lpad" | "rpad" => { + let [s, l, pad] = args[..] else { + return Err(PrepareError::Bind(format!( + "{name} takes exactly 3 arguments" + ))); + }; + let (bs, bl, bp) = ( + self.expr_or_null(s)?, + self.expr_or_null(l)?, + self.expr_or_null(pad)?, + ); + let (Some(bs), Some(bl), Some(bp)) = (bs, bl, bp) else { + return Ok(null_of(Ty::Str)); + }; + if bs.ty != Ty::Str || bp.ty != Ty::Str || bl.ty != Ty::I64 { + return Err(PrepareError::Bind(format!( + "no function matches {name}({}, {}, {})", + bs.ty.name(), + bl.ty.name(), + bp.ty.name() + ))); + } + let nullable = bs.nullable || bl.nullable || bp.nullable; + Ok(SExpr { + kind: SKind::Spad { + left: name == "lpad", + a: Box::new(bs), + len: Box::new(bl), + pad: Box::new(bp), + }, + ty: Ty::Str, + nullable, + }) + } + "replace" | "translate" => { + let op = if name == "replace" { + StrOp3::Replace + } else { + StrOp3::Translate + }; + let [s, x, y] = args[..] else { + return Err(PrepareError::Bind(format!( + "{name} takes exactly 3 arguments" + ))); + }; + let (bs, bx, by) = ( + self.expr_or_null(s)?, + self.expr_or_null(x)?, + self.expr_or_null(y)?, + ); + let (Some(bs), Some(bx), Some(by)) = (bs, bx, by) else { + return Ok(null_of(Ty::Str)); + }; + for e in [&bs, &bx, &by] { + if e.ty != Ty::Str { + return Err(PrepareError::Bind(format!( + "no function matches {name}({})", + e.ty.name() + ))); + } + } + let nullable = bs.nullable || bx.nullable || by.nullable; + Ok(SExpr { + kind: SKind::Str3 { + op, + a: Box::new(bs), + b: Box::new(bx), + c: Box::new(by), + }, + ty: Ty::Str, + nullable, + }) + } + // unicode('') = ord('') = -1, but ascii('') = 0 — the measured + // sole divergence; all return the FIRST codepoint otherwise. + "unicode" | "ord" | "ascii" => { + let [arg] = args[..] else { + return Err(PrepareError::Bind(format!( + "{name} takes exactly 1 argument" + ))); + }; + let Some(inner) = self.expr_or_null(arg)? else { + return Ok(null_of(Ty::I64)); + }; + if inner.ty != Ty::Str { + return Err(PrepareError::Bind(format!( + "no function matches {name}({})", + inner.ty.name() + ))); + } + let nullable = inner.nullable; + Ok(SExpr { + kind: SKind::Sord { + empty_zero: name == "ascii", + a: Box::new(inner), + }, + ty: Ty::I64, + nullable, + }) + } + // bit_length = 8 * strlen exactly (measured) — pure desugar. + "bit_length" => { + let [arg] = args[..] else { + return Err(PrepareError::Bind( + "bit_length takes exactly 1 argument".to_string(), + )); + }; + let Some(inner) = self.expr_or_null(arg)? else { + return Ok(null_of(Ty::I64)); + }; + if inner.ty != Ty::Str { + return Err(PrepareError::Bind(format!( + "no function matches bit_length({})", + inner.ty.name() + ))); + } + let nullable = inner.nullable; + let slen = SExpr { + kind: SKind::SLen { + bytes: true, + a: Box::new(inner), + }, + ty: Ty::I64, + nullable, + }; + let eight = SExpr { + kind: SKind::Lit(Lit::I64(8)), + ty: Ty::I64, + nullable: false, + }; + self.arith(ArithOp::Mul, eight, slen) + } + "strip_accents" => { + let [arg] = args[..] else { + return Err(PrepareError::Bind( + "strip_accents takes exactly 1 argument".to_string(), + )); + }; + let Some(inner) = self.expr_or_null(arg)? else { + return Ok(null_of(Ty::Str)); + }; + if inner.ty != Ty::Str { + return Err(PrepareError::Bind(format!( + "no function matches strip_accents({})", + inner.ty.name() + ))); + } + let nullable = inner.nullable; + Ok(SExpr { + kind: SKind::StripAccents(Box::new(inner)), + ty: Ty::Str, + nullable, + }) + } + // concat_ws: NULL args are SKIPPED with their separator; NULL + // sep -> NULL; all-args-NULL -> '' (measured). Desugars onto + // Case/Or/Concat — the separator appears before arg i iff some + // earlier arg was non-NULL. + "concat_ws" => { + if args.len() < 2 { + return Err(PrepareError::Bind( + "concat_ws needs a separator and at least 1 argument".to_string(), + )); + } + let sep = match self.expr_or_null(args[0])? { + // NULL separator -> NULL result, regardless of args. + None => return Ok(null_of(Ty::Str)), + Some(e) => e, + }; + if sep.ty != Ty::Str { + // The separator does NOT implicitly cast (measured — + // unlike the value args). + return Err(PrepareError::Bind(format!( + "no function matches concat_ws({}, ...)", + sep.ty.name() + ))); + } + let is_null = |e: &SExpr| SExpr { + kind: SKind::IsNull { + negated: false, + inner: Box::new(e.clone()), + }, + ty: Ty::I1, + nullable: false, + }; + let sconcat = |a: SExpr, b: SExpr| SExpr { + kind: SKind::Concat { + a: Box::new(a), + b: Box::new(b), + }, + ty: Ty::Str, + nullable: false, + }; + // The body only evaluates when sep is non-NULL (the outer + // CASE guards it), so pieces use a provably-non-null view + // of the separator — the concat() precedent shape. + let sep_body = if sep.nullable { + SExpr { + kind: SKind::Case { + arms: vec![(is_null(&sep), lit_str(""))], + default: Some(Box::new(sep.clone())), + }, + ty: Ty::Str, + nullable: false, + } + } else { + sep.clone() + }; + // prior_nullable: IS-NOT-NULL exprs of earlier nullable + // args; prior_sure: an earlier arg is provably non-NULL. + let mut prior_nullable: Vec = Vec::new(); + let mut prior_sure = false; + let mut acc: Option = None; + for arg in &args[1..] { + let Some(e) = self.expr_or_null(arg)? else { + continue; // literal NULL: skipped entirely + }; + let e = to_varchar(e); + let joined = sconcat(sep_body.clone(), e.clone()); + let with_sep = if prior_sure { + joined + } else if prior_nullable.is_empty() { + e.clone() + } else { + let mut it = prior_nullable.iter(); + let mut some_prior = SExpr { + kind: SKind::IsNull { + negated: true, + inner: Box::new(it.next().expect("non-empty").clone()), + }, + ty: Ty::I1, + nullable: false, + }; + for p in it { + let not_null = SExpr { + kind: SKind::IsNull { + negated: true, + inner: Box::new(p.clone()), + }, + ty: Ty::I1, + nullable: false, + }; + some_prior = SExpr { + kind: SKind::Or { + a: Box::new(some_prior), + b: Box::new(not_null), + }, + ty: Ty::I1, + nullable: false, + }; + } + SExpr { + kind: SKind::Case { + arms: vec![(some_prior, joined)], + default: Some(Box::new(e.clone())), + }, + ty: Ty::Str, + nullable: false, + } + }; + let piece = if e.nullable { + SExpr { + kind: SKind::Case { + arms: vec![(is_null(&e), lit_str(""))], + default: Some(Box::new(with_sep)), + }, + ty: Ty::Str, + nullable: false, + } + } else { + with_sep + }; + acc = Some(match acc { + None => piece, + Some(p) => sconcat(p, piece), + }); + if e.nullable { + prior_nullable.push(e); + } else { + prior_sure = true; + } + } + let body = acc.unwrap_or_else(|| lit_str("")); + if !sep.nullable { + return Ok(body); + } + // NULL separator -> NULL result (measured), even though + // every piece is individually total. + Ok(SExpr { + kind: SKind::Case { + arms: vec![(is_null(&sep), null_of(Ty::Str))], + default: Some(Box::new(body)), + }, + ty: Ty::Str, + nullable: true, + }) + } + // Wave-3 math tail: add/subtract/multiply/divide/mod are EXACT + // aliases of + - * // % (measured: same values, types, and + // error texts); fdiv/fmod are the FLOOR pair (always DOUBLE); + // nextafter is C nextafter, total. + "add" | "subtract" | "multiply" | "divide" | "mod" => { + let [x, y] = args[..] else { + return Err(unsup(format!("{name} with {} arguments", args.len()))); + }; + let op = match name.as_str() { + "add" => ArithOp::Add, + "subtract" => ArithOp::Sub, + "multiply" => ArithOp::Mul, + "divide" => ArithOp::IDiv, + _ => ArithOp::Rem, + }; + let (bx, by) = (self.expr_or_null(x)?, self.expr_or_null(y)?); + let (bx, by) = match (bx, by) { + (Some(a), Some(b)) => (a, b), + (Some(a), None) => { + let n = null_of(a.ty); + (a, n) + } + (None, Some(b)) => { + let n = null_of(b.ty); + (n, b) + } + (None, None) => (null_of(Ty::I64), null_of(Ty::I64)), + }; + self.arith(op, bx, by) + } + "fdiv" => match args[..] { + [x, y] => self.math2("fdiv", BinOp::Ffloordiv, x, y), + _ => Err(PrepareError::Bind( + "fdiv takes exactly 2 arguments".to_string(), + )), + }, + "fmod" => match args[..] { + [x, y] => self.math2("fmod", BinOp::Ffloormod, x, y), + _ => Err(PrepareError::Bind( + "fmod takes exactly 2 arguments".to_string(), + )), + }, + "nextafter" => match args[..] { + [x, y] => self.math2("nextafter", BinOp::Fnextafter, x, y), + _ => Err(PrepareError::Bind( + "nextafter takes exactly 2 arguments".to_string(), + )), + }, + // Named rejects (wave-3 AC #3): each states WHY, not just what. + "sum" | "count" | "avg" | "min" | "max" | "geomean" | "product" | "string_agg" + | "first" | "last" | "any_value" => Err(unsup(format!( + "aggregate function {name} (no aggregation in v0)" + ))), + "regexp_matches" | "regexp_extract" | "regexp_full_match" | "regexp_replace" + | "regexp_split_to_array" => Err(unsup(format!( + "function {name} (RE2 regex semantics, not in v0)" + ))), + "reverse" => Err(unsup( + "function reverse (grapheme-cluster semantics — measured UAX-29 \ + incl. regional-indicator pairing — not modeled in v0)", + )), _ => Err(unsup(format!( "function {} (not in the v0 catalogue)", f.name diff --git a/src/specializer/ir/gen.rs b/src/specializer/ir/gen.rs index b10b25b..e9b8df4 100644 --- a/src/specializer/ir/gen.rs +++ b/src/specializer/ir/gen.rs @@ -10,7 +10,7 @@ use super::{ BinOp, Block, BlockId, Builder, CmpPred, Col, ColTy, Inst, Lit, NumOp1, Program, RoundMode, - StaticTy, StrOp1, StrOp2, Term, TrimSide, Ty, Value, + StaticTy, StrOp1, StrOp2, StrOp2i, StrOp3, Term, TrimSide, Ty, Value, }; pub struct Rng(u64); @@ -218,11 +218,22 @@ fn load_all( } } +/// A small fresh i64 const in [-9, 9] — positions/counts for string ops, +/// keeping generated programs executable (range guards, alloc caps). +fn small(rng: &mut Rng, b: &mut Builder, insts: &mut Vec) -> Value { + let dst = b.fresh(); + insts.push(Inst::Const { + dst, + lit: Lit::I64(rng.below(19) as i64 - 9), + }); + dst +} + /// A few random compute instructions over whatever is in scope. fn compute(rng: &mut Rng, b: &mut Builder, scope: &mut Scope, insts: &mut Vec) { let n = rng.below(7); for _ in 0..n { - match rng.below(13) { + match rng.below(14) { 0 => { let ops = [ BinOp::Iadd, @@ -234,6 +245,9 @@ fn compute(rng: &mut Rng, b: &mut Builder, scope: &mut Scope, insts: &mut Vec { - let op = if rng.chance(50) { - StrOp1::Upper - } else { - StrOp1::Lower + let op = match rng.below(3) { + 0 => StrOp1::Upper, + 1 => StrOp1::Lower, + _ => StrOp1::StripAccents, }; let a = ensure(rng, b, scope, insts, Ty::Str); let dst = b.fresh(); @@ -351,14 +365,6 @@ fn compute(rng: &mut Rng, b: &mut Builder, scope: &mut Scope, insts: &mut Vec| { - let dst = b.fresh(); - insts.push(Inst::Const { - dst, - lit: Lit::I64(rng.below(19) as i64 - 9), - }); - dst - }; let start = small(rng, b, insts); scope.add(start, Ty::I64); let len = if rng.chance(50) { @@ -447,13 +453,99 @@ fn compute(rng: &mut Rng, b: &mut Builder, scope: &mut Scope, insts: &mut Vec { + let a = ensure(rng, b, scope, insts, Ty::Str); + match rng.below(5) { + 0 => { + let op = if rng.chance(50) { + StrOp3::Replace + } else { + StrOp3::Translate + }; + let x = ensure(rng, b, scope, insts, Ty::Str); + let y = ensure(rng, b, scope, insts, Ty::Str); + let dst = b.fresh(); + insts.push(Inst::Str3 { + op, + dst, + a, + b: x, + c: y, + }); + scope.add(dst, Ty::Str); + } + 1 => { + // Repeat with a SMALL fresh count: an arbitrary + // in-scope i64 would build multi-GiB strings. + let op = if rng.chance(50) { + StrOp2i::Repeat + } else { + StrOp2i::Extract + }; + let n = small(rng, b, insts); + scope.add(n, Ty::I64); + let dst = b.fresh(); + insts.push(Inst::Str2i { op, dst, a, n }); + scope.add(dst, Ty::Str); + } + 2 => { + // Spad traps on empty pad + growth — low weight via + // small len keeps most programs running while the + // trap row stays reachable for agreement checks. + let len = small(rng, b, insts); + scope.add(len, Ty::I64); + let pad = ensure(rng, b, scope, insts, Ty::Str); + let dst = b.fresh(); + insts.push(Inst::Spad { + left: rng.chance(50), + dst, + a, + len, + pad, + }); + scope.add(dst, Ty::Str); + } + 3 => { + let lo = small(rng, b, insts); + scope.add(lo, Ty::I64); + let hi = small(rng, b, insts); + scope.add(hi, Ty::I64); + let dst = b.fresh(); + insts.push(Inst::Sslice { dst, a, lo, hi }); + scope.add(dst, Ty::Str); + } + _ => { + let dst = b.fresh(); + insts.push(Inst::Sord { + empty_zero: rng.chance(50), + dst, + a, + }); + scope.add(dst, Ty::I64); + } + } + } _ => { let (from, mk): (Ty, fn(Value, Value) -> Inst) = if rng.chance(50) { (Ty::I64, |dst, a| Inst::Itos { dst, a }) diff --git a/src/specializer/ir/mod.rs b/src/specializer/ir/mod.rs index 829b45f..affaeba 100644 --- a/src/specializer/ir/mod.rs +++ b/src/specializer/ir/mod.rs @@ -247,6 +247,15 @@ pub enum BinOp { /// Traps: base checked FIRST (zero / negative / base==1 each with their /// own DuckDB message), then x (zero / negative). Flogb, + /// SQL fdiv(x, y) = floor(x / y) — TOTAL, ±inf on zero divisor + /// (wave-3 pins). NOT the `//` operator, which is plain division. + Ffloordiv, + /// SQL fmod(x, y) = x − floor(x/y)·y — FLOORED mod, divisor's sign; + /// NaN on zero or infinite divisor (wave-3 pins). NOT C fmod — that + /// is `Frem` (SQL `%`/mod on doubles). + Ffloormod, + /// C nextafter, bit-exact, TOTAL; x == y returns y (wave-3 pins). + Fnextafter, And, Or, Xor, @@ -265,7 +274,10 @@ impl BinOp { | BinOp::Fdiv | BinOp::Frem | BinOp::Fpow - | BinOp::Flogb => (Ty::F64, Ty::F64), + | BinOp::Flogb + | BinOp::Ffloordiv + | BinOp::Ffloormod + | BinOp::Fnextafter => (Ty::F64, Ty::F64), BinOp::And | BinOp::Or | BinOp::Xor => (Ty::I1, Ty::I1), } } @@ -284,6 +296,9 @@ impl BinOp { BinOp::Frem => "frem", BinOp::Fpow => "fpow", BinOp::Flogb => "flogb", + BinOp::Ffloordiv => "ffloordiv", + BinOp::Ffloormod => "ffloormod", + BinOp::Fnextafter => "fnextafter", BinOp::And => "and", BinOp::Or => "or", BinOp::Xor => "xor", @@ -326,6 +341,9 @@ pub enum RoundMode { pub enum StrOp1 { Upper, Lower, + /// strip_accents: oracle-extracted per-codepoint map + Hangul jamo + /// composition + the measured NUL quirk (wave-3 pins). TOTAL. + StripAccents, } impl StrOp1 { @@ -333,13 +351,16 @@ impl StrOp1 { match self { StrOp1::Upper => "supper", StrOp1::Lower => "slower", + StrOp1::StripAccents => "sstrip", } } } -/// Wave-1 string search: str × str, TOTAL (no traps), NULL-strict via -/// lanes. Positions are 1-based CODEPOINT indices; an empty needle -/// matches everything (pins spec 2026-07-26). +/// Two-string ops, NULL-strict via lanes. The wave-1 search ops are TOTAL +/// with 1-based CODEPOINT positions and empty-needle-matches-everything; +/// the wave-3 similarity ops are raw UTF-8 BYTE-based (all of them — +/// measured), and `Jaccard`/`Hamming` TRAP (empty inputs / byte-length +/// mismatch, DuckDB messages verbatim). #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum StrOp2 { /// instr/strpos/position: 1-based codepoint index, 0 = not found. @@ -347,12 +368,24 @@ pub enum StrOp2 { Contains, Starts, Ends, + /// levenshtein == editdist3: plain byte-level edit distance. + Levenshtein, + /// damerau_levenshtein: the UNRESTRICTED DL variant (NOT OSA — + /// witness ('ca','abc') = 2), transposition cost 1, bytes. + Damerau, + /// jaccard: |A∩B|/|A∪B| over single-BYTE sets, duplicates ignored; + /// traps on an empty string either side. + Jaccard, + /// hamming == mismatches: byte-wise; traps on byte-length mismatch + /// and on ANY empty input (('','') is an error, not 0). + Hamming, } impl StrOp2 { pub fn result_ty(self) -> Ty { match self { - StrOp2::Find => Ty::I64, + StrOp2::Find | StrOp2::Levenshtein | StrOp2::Damerau | StrOp2::Hamming => Ty::I64, + StrOp2::Jaccard => Ty::F64, _ => Ty::I1, } } @@ -363,6 +396,50 @@ impl StrOp2 { StrOp2::Contains => "scontains", StrOp2::Starts => "sstarts", StrOp2::Ends => "sends", + StrOp2::Levenshtein => "slevenshtein", + StrOp2::Damerau => "sdamerau", + StrOp2::Jaccard => "sjaccard", + StrOp2::Hamming => "shamming", + } + } +} + +/// Three-string ops (Str × Str × Str -> Str), TOTAL, NULL-strict via lanes. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum StrOp3 { + /// replace(s, from, to): leftmost non-overlapping single pass, output + /// never rescanned; empty needle is a strict no-op (wave-3 pins). + Replace, + /// translate(s, from, to): per-CODEPOINT map; from-chars beyond |to| + /// are deleted; duplicate in `from` -> FIRST wins (wave-3 pins). + Translate, +} + +impl StrOp3 { + pub fn name(self) -> &'static str { + match self { + StrOp3::Replace => "sreplace", + StrOp3::Translate => "stranslate", + } + } +} + +/// (Str × I64 -> Str) ops, TOTAL, NULL-strict via lanes. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum StrOp2i { + /// repeat(s, n): n <= 0 -> '' silently (wave-3 pins). + Repeat, + /// array_extract/list_extract/s[i] on VARCHAR: 1-based CODEPOINT, + /// negative from the end (len+1+i), out-of-range/0 -> '' — NOT NULL + /// (the LIST overload differs; wave-3 pins). + Extract, +} + +impl StrOp2i { + pub fn name(self) -> &'static str { + match self { + StrOp2i::Repeat => "srepeat", + StrOp2i::Extract => "sextract", } } } @@ -504,13 +581,56 @@ pub enum Inst { a: Value, b: Value, }, - /// Wave-1 string search — `a` is the haystack, `b` the needle. + /// Wave-1 string search / wave-3 similarity — `a` is the haystack + /// (resp. first argument), `b` the needle (second argument). Str2 { op: StrOp2, dst: Value, a: Value, b: Value, }, + /// `sreplace` / `stranslate` — replace(a, b, c) / translate(a, b, c). + Str3 { + op: StrOp3, + dst: Value, + a: Value, + b: Value, + c: Value, + }, + /// `srepeat` / `sextract` — (a: Str, n: I64) -> Str. + Str2i { + op: StrOp2i, + dst: Value, + a: Value, + n: Value, + }, + /// `spad.left` / `spad.right` — lpad/rpad(a, len, pad): len counts + /// CODEPOINTS; truncation keeps the FIRST len codepoints for BOTH + /// sides; TRAPS ("Insufficient padding in LPAD/RPAD.") only when + /// pad is empty AND growth is needed (data-dependent; wave-3 pins). + Spad { + left: bool, + dst: Value, + a: Value, + len: Value, + pad: Value, + }, + /// `sslice` — array_slice/list_slice/s[a:b] on VARCHAR: 1-based, + /// both-ends-INCLUSIVE codepoint slice, negative from-end, clamping, + /// out-of-range/reversed -> ''. TOTAL (wave-3 pins). + Sslice { + dst: Value, + a: Value, + lo: Value, + hi: Value, + }, + /// `sord` / `sord.ascii` — first codepoint as i64; '' -> -1 (unicode/ + /// ord) or 0 (ascii — the measured sole divergence). TOTAL. + Sord { + empty_zero: bool, + dst: Value, + a: Value, + }, /// String length: codepoints (`slenc`) or UTF-8 bytes (`slenb`). SLen { bytes: bool, @@ -675,6 +795,11 @@ impl Inst { | Inst::Sconcat { dst, .. } | Inst::Str1 { dst, .. } | Inst::Str2 { dst, .. } + | Inst::Str3 { dst, .. } + | Inst::Str2i { dst, .. } + | Inst::Spad { dst, .. } + | Inst::Sslice { dst, .. } + | Inst::Sord { dst, .. } | Inst::SLen { dst, .. } | Inst::Round2f { dst, .. } | Inst::Round2i { dst, .. } @@ -731,6 +856,7 @@ impl Inst { | Inst::Ftoi { dst, a, .. } | Inst::Str1 { dst, a, .. } | Inst::SLen { dst, a, .. } + | Inst::Sord { dst, a, .. } | Inst::Num1 { dst, a, .. } | Inst::Not { dst, a } => { *dst = m(*dst); @@ -742,6 +868,7 @@ impl Inst { dst, a, chars: b, .. } | Inst::Str2 { dst, a, b, .. } + | Inst::Str2i { dst, a, n: b, .. } | Inst::Round2f { dst, a, n: b, .. } | Inst::Round2i { dst, a, n: b, .. } | Inst::Sconcat { dst, a, b } => { @@ -749,6 +876,25 @@ impl Inst { *a = m(*a); *b = m(*b); } + Inst::Str3 { dst, a, b, c, .. } + | Inst::Spad { + dst, + a, + len: b, + pad: c, + .. + } + | Inst::Sslice { + dst, + a, + lo: b, + hi: c, + } => { + *dst = m(*dst); + *a = m(*a); + *b = m(*b); + *c = m(*c); + } Inst::Slike { dst, a, p, esc, .. } => { *dst = m(*dst); *a = m(*a); diff --git a/src/specializer/ir/parse.rs b/src/specializer/ir/parse.rs index ab2dff5..3e010fd 100644 --- a/src/specializer/ir/parse.rs +++ b/src/specializer/ir/parse.rs @@ -11,7 +11,7 @@ use std::collections::HashMap; use super::{ BinOp, Block, BlockId, CmpPred, Col, ColTy, Inst, Lit, NumOp1, Program, RoundMode, StaticTy, - StrOp1, StrOp2, Term, TrimSide, Ty, Value, + StrOp1, StrOp2, StrOp2i, StrOp3, Term, TrimSide, Ty, Value, }; #[derive(Debug)] @@ -857,7 +857,8 @@ impl Parser { Inst::Const { dst: def!(0), lit } } "iadd" | "isub" | "imul" | "idiv" | "irem" | "fadd" | "fsub" | "fmul" | "fdiv" - | "frem" | "fpow" | "flogb" | "and" | "or" | "xor" => { + | "frem" | "fpow" | "flogb" | "ffloordiv" | "ffloormod" | "fnextafter" | "and" + | "or" | "xor" => { want_dsts(1, self)?; let op = match opcode.as_str() { "iadd" => BinOp::Iadd, @@ -872,6 +873,9 @@ impl Parser { "frem" => BinOp::Frem, "fpow" => BinOp::Fpow, "flogb" => BinOp::Flogb, + "ffloordiv" => BinOp::Ffloordiv, + "ffloormod" => BinOp::Ffloormod, + "fnextafter" => BinOp::Fnextafter, "and" => BinOp::And, "or" => BinOp::Or, _ => BinOp::Xor, @@ -972,12 +976,17 @@ impl Parser { Inst::StofOpt { flag, dst, a } } } - "sfind" | "scontains" | "sstarts" | "sends" => { + "sfind" | "scontains" | "sstarts" | "sends" | "slevenshtein" | "sdamerau" + | "sjaccard" | "shamming" => { want_dsts(1, self)?; let op = match opcode.as_str() { "sfind" => StrOp2::Find, "scontains" => StrOp2::Contains, "sstarts" => StrOp2::Starts, + "slevenshtein" => StrOp2::Levenshtein, + "sdamerau" => StrOp2::Damerau, + "sjaccard" => StrOp2::Jaccard, + "shamming" => StrOp2::Hamming, _ => StrOp2::Ends, }; let a = self.use_value()?; @@ -1047,12 +1056,12 @@ impl Parser { let b = self.use_value()?; Inst::Sconcat { dst: def!(0), a, b } } - "supper" | "slower" => { + "supper" | "slower" | "sstrip" => { want_dsts(1, self)?; - let op = if opcode == "supper" { - StrOp1::Upper - } else { - StrOp1::Lower + let op = match opcode.as_str() { + "supper" => StrOp1::Upper, + "slower" => StrOp1::Lower, + _ => StrOp1::StripAccents, }; let a = self.use_value()?; Inst::Str1 { @@ -1061,6 +1070,81 @@ impl Parser { a, } } + "sreplace" | "stranslate" => { + want_dsts(1, self)?; + let op = if opcode == "sreplace" { + StrOp3::Replace + } else { + StrOp3::Translate + }; + let a = self.use_value()?; + self.expect(Tok::Comma)?; + let b = self.use_value()?; + self.expect(Tok::Comma)?; + let c = self.use_value()?; + Inst::Str3 { + op, + dst: def!(0), + a, + b, + c, + } + } + "srepeat" | "sextract" => { + want_dsts(1, self)?; + let op = if opcode == "srepeat" { + StrOp2i::Repeat + } else { + StrOp2i::Extract + }; + let a = self.use_value()?; + self.expect(Tok::Comma)?; + let n = self.use_value()?; + Inst::Str2i { + op, + dst: def!(0), + a, + n, + } + } + "spad.left" | "spad.right" => { + want_dsts(1, self)?; + let a = self.use_value()?; + self.expect(Tok::Comma)?; + let len = self.use_value()?; + self.expect(Tok::Comma)?; + let pad = self.use_value()?; + Inst::Spad { + left: opcode == "spad.left", + dst: def!(0), + a, + len, + pad, + } + } + "sslice" => { + want_dsts(1, self)?; + let a = self.use_value()?; + self.expect(Tok::Comma)?; + let lo = self.use_value()?; + self.expect(Tok::Comma)?; + let hi = self.use_value()?; + Inst::Sslice { + dst: def!(0), + a, + lo, + hi, + } + } + "sord" | "sord.ascii" => { + want_dsts(1, self)?; + let a = self.use_value()?; + Inst::Sord { + empty_zero: opcode == "sord.ascii", + dst: def!(0), + a, + } + } "strim.both" | "strim.lead" | "strim.trail" => { want_dsts(1, self)?; let side = match opcode.as_str() { diff --git a/src/specializer/ir/print.rs b/src/specializer/ir/print.rs index 780a453..0a9d2ae 100644 --- a/src/specializer/ir/print.rs +++ b/src/specializer/ir/print.rs @@ -127,6 +127,23 @@ fn print_inst(s: &mut String, p: &Program, inst: &Inst) { Inst::Str2 { op, a, b, .. } => { let _ = write!(s, "{} {}, {}", op.name(), val(*a), val(*b)); } + Inst::Str3 { op, a, b, c, .. } => { + let _ = write!(s, "{} {}, {}, {}", op.name(), val(*a), val(*b), val(*c)); + } + Inst::Str2i { op, a, n, .. } => { + let _ = write!(s, "{} {}, {}", op.name(), val(*a), val(*n)); + } + Inst::Spad { left, a, len, pad, .. } => { + let op = if *left { "spad.left" } else { "spad.right" }; + let _ = write!(s, "{} {}, {}, {}", op, val(*a), val(*len), val(*pad)); + } + Inst::Sslice { a, lo, hi, .. } => { + let _ = write!(s, "sslice {}, {}, {}", val(*a), val(*lo), val(*hi)); + } + Inst::Sord { empty_zero, a, .. } => { + let op = if *empty_zero { "sord.ascii" } else { "sord" }; + let _ = write!(s, "{} {}", op, val(*a)); + } Inst::Round2f { trunc, a, n, .. } => { let op = if *trunc { "ftrunc2" } else { "fround2" }; let _ = write!(s, "{} {}, {}", op, val(*a), val(*n)); diff --git a/src/specializer/ir/verify.rs b/src/specializer/ir/verify.rs index 8d2bb9a..7582c99 100644 --- a/src/specializer/ir/verify.rs +++ b/src/specializer/ir/verify.rs @@ -180,13 +180,17 @@ fn dst_types(p: &Program, inst: &Inst) -> Vec<(Value, Ty)> { | Inst::Ftos { dst, .. } | Inst::Sconcat { dst, .. } | Inst::Str1 { dst, .. } + | Inst::Str3 { dst, .. } + | Inst::Str2i { dst, .. } + | Inst::Spad { dst, .. } + | Inst::Sslice { dst, .. } | Inst::Strim { dst, .. } | Inst::Ssubstr { dst, .. } => { vec![(*dst, Ty::Str)] } Inst::Num1 { op, dst, .. } => vec![(*dst, op.sig())], Inst::Str2 { op, dst, .. } => vec![(*dst, op.result_ty())], - Inst::SLen { dst, .. } => vec![(*dst, Ty::I64)], + Inst::SLen { dst, .. } | Inst::Sord { dst, .. } => vec![(*dst, Ty::I64)], Inst::Slike { dst, .. } => vec![(*dst, Ty::I1)], Inst::Round2f { dst, .. } => vec![(*dst, Ty::F64)], Inst::Round2i { dst, .. } => vec![(*dst, Ty::I64)], @@ -364,9 +368,28 @@ fn check_block( want(&in_scope, def_types, *a, Ty::Str, "operand", bi, i, errs); want(&in_scope, def_types, *rhs, Ty::Str, "operand", bi, i, errs); } - Inst::Str1 { a, .. } | Inst::SLen { a, .. } => { + Inst::Str1 { a, .. } | Inst::SLen { a, .. } | Inst::Sord { a, .. } => { want(&in_scope, def_types, *a, Ty::Str, "operand", bi, i, errs) } + Inst::Str3 { a, b, c, .. } => { + want(&in_scope, def_types, *a, Ty::Str, "operand", bi, i, errs); + want(&in_scope, def_types, *b, Ty::Str, "operand", bi, i, errs); + want(&in_scope, def_types, *c, Ty::Str, "operand", bi, i, errs); + } + Inst::Str2i { a, n, .. } => { + want(&in_scope, def_types, *a, Ty::Str, "operand", bi, i, errs); + want(&in_scope, def_types, *n, Ty::I64, "operand", bi, i, errs); + } + Inst::Spad { a, len, pad, .. } => { + want(&in_scope, def_types, *a, Ty::Str, "operand", bi, i, errs); + want(&in_scope, def_types, *len, Ty::I64, "operand", bi, i, errs); + want(&in_scope, def_types, *pad, Ty::Str, "operand", bi, i, errs); + } + Inst::Sslice { a, lo, hi, .. } => { + want(&in_scope, def_types, *a, Ty::Str, "operand", bi, i, errs); + want(&in_scope, def_types, *lo, Ty::I64, "operand", bi, i, errs); + want(&in_scope, def_types, *hi, Ty::I64, "operand", bi, i, errs); + } Inst::Slike { a, p, esc, .. } => { want(&in_scope, def_types, *a, Ty::Str, "operand", bi, i, errs); want(&in_scope, def_types, *p, Ty::Str, "operand", bi, i, errs); diff --git a/src/specializer/lower.rs b/src/specializer/lower.rs index 0d57b77..ef0fcab 100644 --- a/src/specializer/lower.rs +++ b/src/specializer/lower.rs @@ -29,7 +29,7 @@ use std::collections::HashMap; use super::frontend::PrepareError; use super::ir::{ BinOp, Block, BlockId, Builder, CmpPred, Col, Inst, Lit, NumOp1, Program, StaticTy, StrOp1, - Term, Ty, Value, + StrOp2, Term, Ty, Value, }; use super::plan::{ArithOp, JoinKind, JoinSpec, Rel, SExpr, SKind, StaticTable}; @@ -444,10 +444,14 @@ impl<'a> FB<'a> { (ArithOp::Sub, Ty::I64) => BinOp::Isub, (ArithOp::Mul, Ty::I64) => BinOp::Imul, (ArithOp::Rem, Ty::I64) => BinOp::Irem, + (ArithOp::IDiv, Ty::I64) => BinOp::Idiv, (ArithOp::Add, Ty::F64) => BinOp::Fadd, (ArithOp::Sub, Ty::F64) => BinOp::Fsub, (ArithOp::Mul, Ty::F64) => BinOp::Fmul, (ArithOp::Div, Ty::F64) => BinOp::Fdiv, + // `//` on doubles is PLAIN division (wave-3 pins); the + // zero-divisor NULL guard is the frontend's CASE wrap. + (ArithOp::IDiv, Ty::F64) => BinOp::Fdiv, (ArithOp::Rem, Ty::F64) => BinOp::Frem, (op, ty) => { return Err(PrepareError::Internal(format!( @@ -714,15 +718,144 @@ impl<'a> FB<'a> { live.push((la, Ty::Str)); let lb = self.emit(b, live)?; let (la, _) = live.pop().expect("pushed above"); + let flag = self.combine_flags(la.flag, lb.flag); + // Jaccard/Hamming trap on empty inputs / length mismatch — + // and the "" mask default IS in the trap domain. NULL + // pre-empts the checks (jaccard(NULL, '') is NULL, not an + // error), so BOTH payloads mask to "a" under the COMBINED + // flag, the Flogb pattern. + let (av, bv) = match op { + StrOp2::Jaccard | StrOp2::Hamming => { + let a = Lane { flag, val: la.val }; + let b = Lane { flag, val: lb.val }; + ( + self.masked_to(a, Lit::Str("a".into())), + self.masked_to(b, Lit::Str("a".into())), + ) + } + _ => (la.val, lb.val), + }; let dst = self.fresh(); self.inst(Inst::Str2 { + op: *op, + dst, + a: av, + b: bv, + }); + Ok(Lane { flag, val: dst }) + } + SKind::Str3 { op, a, b, c } => { + let la = self.emit(a, live)?; + live.push((la, Ty::Str)); + let lb = self.emit(b, live)?; + live.push((lb, Ty::Str)); + let lc = self.emit(c, live)?; + let (lb, _) = live.pop().expect("pushed above"); + let (la, _) = live.pop().expect("pushed above"); + let dst = self.fresh(); + self.inst(Inst::Str3 { op: *op, dst, a: la.val, b: lb.val, + c: lc.val, }); + let f = self.combine_flags(la.flag, lb.flag); Ok(Lane { - flag: self.combine_flags(la.flag, lb.flag), + flag: self.combine_flags(f, lc.flag), + val: dst, + }) + } + SKind::Str2i { op, a, n } => { + let la = self.emit(a, live)?; + live.push((la, Ty::Str)); + let ln = self.emit(n, live)?; + let (la, _) = live.pop().expect("pushed above"); + // Total under masked defaults: repeat("", any n) allocates + // nothing (the cap guard sees 0 bytes) and extract is total. + let dst = self.fresh(); + self.inst(Inst::Str2i { + op: *op, + dst, + a: la.val, + n: ln.val, + }); + Ok(Lane { + flag: self.combine_flags(la.flag, ln.flag), + val: dst, + }) + } + SKind::Spad { left, a, len, pad } => { + let la = self.emit(a, live)?; + live.push((la, Ty::Str)); + let ll = self.emit(len, live)?; + live.push((ll, Ty::I64)); + let lp = self.emit(pad, live)?; + let (ll, _) = live.pop().expect("pushed above"); + let (la, _) = live.pop().expect("pushed above"); + // The empty-pad trap fires only when growth is needed; a + // VALID len with a NULL pad must not trap (NULL pre-empts). + // Mask len to 0 under the COMBINED flag — len 0 returns '' + // before the pad is ever examined. + let flag = { + let f = self.combine_flags(la.flag, ll.flag); + self.combine_flags(f, lp.flag) + }; + let len_v = self.masked_to(Lane { flag, val: ll.val }, Lit::I64(0)); + let dst = self.fresh(); + self.inst(Inst::Spad { + left: *left, + dst, + a: la.val, + len: len_v, + pad: lp.val, + }); + Ok(Lane { flag, val: dst }) + } + SKind::Sslice { a, lo, hi } => { + let la = self.emit(a, live)?; + live.push((la, Ty::Str)); + let llo = self.emit(lo, live)?; + live.push((llo, Ty::I64)); + let lhi = self.emit(hi, live)?; + let (llo, _) = live.pop().expect("pushed above"); + let (la, _) = live.pop().expect("pushed above"); + let dst = self.fresh(); + self.inst(Inst::Sslice { + dst, + a: la.val, + lo: llo.val, + hi: lhi.val, + }); + let f = self.combine_flags(la.flag, llo.flag); + Ok(Lane { + flag: self.combine_flags(f, lhi.flag), + val: dst, + }) + } + SKind::Sord { empty_zero, a } => { + let l = self.emit(a, live)?; + let dst = self.fresh(); + self.inst(Inst::Sord { + empty_zero: *empty_zero, + dst, + a: l.val, + }); + Ok(Lane { + flag: l.flag, + val: dst, + }) + } + SKind::StripAccents(a) => { + let l = self.emit(a, live)?; + let dst = self.fresh(); + self.inst(Inst::Str1 { + op: StrOp1::StripAccents, + dst, + a: l.val, + }); + Ok(Lane { + flag: l.flag, val: dst, }) } diff --git a/src/specializer/plan.rs b/src/specializer/plan.rs index 1010cc3..456959e 100644 --- a/src/specializer/plan.rs +++ b/src/specializer/plan.rs @@ -192,22 +192,62 @@ pub enum SKind { op: super::ir::NumOp1, a: Box, }, - /// Wave-1 f64 binary math: Fpow (total) and Flogb(base, x) (trapping). + /// Wave-1 f64 binary math: Fpow (total) and Flogb(base, x) (trapping); + /// wave-3 adds Ffloordiv/Ffloormod/Fnextafter (all total). MathF2 { op: super::ir::BinOp, a: Box, b: Box, }, + /// replace/translate — Str × Str × Str -> Str, total, NULL-propagating. + Str3 { + op: super::ir::StrOp3, + a: Box, + b: Box, + c: Box, + }, + /// repeat / VARCHAR array_extract — (Str, I64) -> Str, total. + Str2i { + op: super::ir::StrOp2i, + a: Box, + n: Box, + }, + /// lpad/rpad — traps only on empty pad + needed growth, so lowering + /// masks ALL operands under a combined flag (NULL pre-empts the trap). + Spad { + left: bool, + a: Box, + len: Box, + pad: Box, + }, + /// VARCHAR array_slice/list_slice — total, NULL-propagating (a NULL + /// bound is NULL, never an open bound). + Sslice { + a: Box, + lo: Box, + hi: Box, + }, + /// unicode/ord ('' -> -1) / ascii ('' -> 0) — Str -> I64, total. + Sord { + empty_zero: bool, + a: Box, + }, + /// strip_accents — Str -> Str, total (oracle table + Hangul compose). + StripAccents(Box), } /// SQL-level arithmetic. `Div` is DuckDB's `/` — ALWAYS float division /// (measured: `5/2 = 2.5 DOUBLE`); the frontend promotes both sides to f64. -/// Integer `%` stays integral (measured: `5%2 -> INTEGER`). +/// Integer `%` stays integral (measured: `5%2 -> INTEGER`). `IDiv` is +/// DuckDB's `//` / divide(): truncating division on ints, PLAIN division +/// on doubles (NOT floor — measured -7.5//2.0 = -3.75), zero divisor -> +/// NULL on both (the frontend wraps the CASE guard). #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum ArithOp { Add, Sub, Mul, Div, + IDiv, Rem, } diff --git a/src/specializer/tests.rs b/src/specializer/tests.rs index 5adeca3..6fde49d 100644 --- a/src/specializer/tests.rs +++ b/src/specializer/tests.rs @@ -142,7 +142,7 @@ fn integer_overflow_traps_like_duckdb() { ) .unwrap_err(); assert!( - err.contains("overflow"), + err.contains("Overflow"), "expected an overflow trap, got: {err}" ); } @@ -479,9 +479,12 @@ fn unsupported_constructs_are_named_cleanly() { ("SELECT a FROM __THIS__ JOIN t USING (a)", "USING"), // Bare aggregates parse as plain function calls; they reject via the // function arm until the catalogue distinguishes aggregation. - ("SELECT sum(a) FROM __THIS__", "function sum"), + ("SELECT sum(a) FROM __THIS__", "aggregate function sum"), ("SELECT a FROM __THIS__ GROUP BY a", "aggregation"), - ("SELECT levenshtein('x', 'y') FROM __THIS__", "function"), + // Wave-3 named rejects: regex family, grapheme-based reverse. + ("SELECT regexp_matches('x', 'y') FROM __THIS__", "RE2"), + ("SELECT reverse('abc') FROM __THIS__", "grapheme"), + ("SELECT jaro_similarity('x', 'y') FROM __THIS__", "function"), // Star now expands; the still-unsupported star forms reject by name. ("SELECT * REPLACE (a + 1 AS a) FROM __THIS__", "REPLACE"), ("SELECT COLUMNS('a') FROM __THIS__", "function COLUMNS"), @@ -859,7 +862,7 @@ fn abs_min_traps_like_duckdb() { batch(1, vec![c_i64(&[Some(i64::MIN)])]), ) .unwrap_err(); - assert!(err.contains("overflow"), "got: {err}"); + assert!(err.contains("Overflow"), "got: {err}"); } #[test] @@ -1006,7 +1009,7 @@ fn traps_never_fire_under_a_null_flag() { let got = run_sql(sql, &schema, batch(1, vec![c_i64(&[None])])).unwrap(); assert_eq!(got, rows(&[&["NULL"]])); let err = run_sql(sql, &schema, batch(1, vec![c_i64(&[Some(1)])])).unwrap_err(); - assert!(err.contains("overflow"), "got: {err}"); + assert!(err.contains("Overflow"), "got: {err}"); } #[test] diff --git a/tests/test_corpus_replay.py b/tests/test_corpus_replay.py index 296de99..34ebbaf 100644 --- a/tests/test_corpus_replay.py +++ b/tests/test_corpus_replay.py @@ -107,6 +107,15 @@ def _replay(case: dict) -> tuple[str, str]: t: con.execute(f'SELECT * FROM "{s}"."{t}"').to_arrow_table() for s, t in named } + # f32 base tables cannot be emulated by the f64-only engine: widening + # is value-exact, but every f32-GRID-sensitive op (nextafter's ulp + # steps, FLOAT->VARCHAR shortest-round-trip, FLOAT rounding) computes + # on the wrong grid. Comparisons happen to survive; the blanket rule + # is the defensible one (wave-3: 3 sources, 5 cases). + for t in arrow.values(): + if any(pa.types.is_float32(f.type) for f in t.schema): + return "unsupported", "f32 base-table column (engine is f64-only)" + # Row model from the driving table's schema; unmappable column types are # the engine's own clean rejection (it sees the same field as unsupported). fields = {} diff --git a/tests/test_duckdb_wave3_builders.py b/tests/test_duckdb_wave3_builders.py new file mode 100644 index 0000000..4134f50 --- /dev/null +++ b/tests/test_duckdb_wave3_builders.py @@ -0,0 +1,283 @@ +"""Wave-3 string-builder builtins vs the duckdb oracle. + +Pins: docs/superpowers/specs/2026-07-26-wave3-builtin-pins.md +Family: repeat, lpad, rpad, replace, translate, concat_ws, ucase, lcase. + +Everything runs through `duck_check` (both engines, repr-multiset compare) +on columns of __THIS__ — literals only where the pin is about literals +(constant lengths for the pad-trap split). +""" + +from __future__ import annotations + +import pytest +from test_duckdb_interpreter import duck_check + +# ---------------------------------------------------------------- repeat + + +def test_repeat_zero_negative_and_multibyte(): + # n=0 and n<0 both yield '' silently; multi-byte codepoints survive. + duck_check( + "SELECT repeat(s, n) AS r FROM __THIS__", + {"s": "str", "n": "int"}, + [ + {"s": "ab", "n": 0}, + {"s": "ab", "n": -3}, + {"s": "é☃", "n": 2}, + {"s": "ab", "n": 3}, + ], + ) + + +def test_repeat_null_strict(): + duck_check( + "SELECT repeat(s, n) AS r FROM __THIS__", + {"s": "str?", "n": "int?"}, + [ + {"s": None, "n": 2}, + {"s": "ab", "n": None}, + {"s": None, "n": None}, + {"s": "ab", "n": 2}, + ], + ) + + +# ------------------------------------------------------------- lpad/rpad + + +def test_lpad_counts_codepoints_not_bytes(): + # 'éé' is 2 codepoints / 4 UTF-8 bytes: lpad to 4 must ADD two pads. + duck_check( + "SELECT lpad(s, 4, 'x') AS r FROM __THIS__", + {"s": "str"}, + [{"s": "éé"}, {"s": "ab"}], + ) + + +def test_pad_truncation_keeps_first_codepoints(): + # Headline: rpad('abcdef',3,'x')='abc' — truncation keeps the FIRST l + # codepoints for BOTH directions. Plus l<=0 -> '' and l=length identity. + # DuckDB only binds lpad(VARCHAR, INTEGER, VARCHAR): cast the BIGINT col. + duck_check( + "SELECT lpad(s, CAST(l AS INTEGER), 'x') AS lp," + " rpad(s, CAST(l AS INTEGER), 'x') AS rp FROM __THIS__", + {"s": "str", "l": "int"}, + [ + {"s": "abcdef", "l": 3}, + {"s": "éab☃", "l": 2}, + {"s": "abc", "l": 0}, + {"s": "abc", "l": -2}, + {"s": "abc", "l": 3}, + ], + ) + + +def test_pad_cycling_and_multibyte_pad_cut(): + # lpad('a',6,'xyz')='xyzxya', rpad='axyzxy'; multi-byte pad is cut at a + # codepoint boundary: lpad('a',3,'é☃')='é☃a', rpad='aé☃'. + duck_check( + "SELECT lpad(s, CAST(l AS INTEGER), p) AS lp," + " rpad(s, CAST(l AS INTEGER), p) AS rp FROM __THIS__", + {"s": "str", "l": "int", "p": "str"}, + [ + {"s": "a", "l": 6, "p": "xyz"}, + {"s": "a", "l": 3, "p": "é☃"}, + ], + ) + + +def test_pad_null_strict_all_three_args(): + duck_check( + "SELECT lpad(s, CAST(l AS INTEGER), p) AS lp," + " rpad(s, CAST(l AS INTEGER), p) AS rp FROM __THIS__", + {"s": "str?", "l": "int?", "p": "str?"}, + [ + {"s": None, "l": 3, "p": "x"}, + {"s": "a", "l": None, "p": "x"}, + {"s": "a", "l": 3, "p": None}, + {"s": "a", "l": 3, "p": "x"}, + ], + ) + + +def test_pad_empty_pad_shrink_is_silent(): + # Empty pad is fine as long as no growth is needed: lpad('abc',2,'')='ab'. + duck_check( + "SELECT lpad(s, 2, p) AS lp, rpad(s, 2, p) AS rp FROM __THIS__", + {"s": "str", "p": "str"}, + [{"s": "abc", "p": ""}], + ) + + +def test_lpad_empty_pad_growth_traps(): + # Same arguments except l now requires growth: DATA-DEPENDENT trap. + with pytest.raises(Exception, match="Insufficient padding in LPAD"): + duck_check( + "SELECT lpad(s, 5, p) AS r FROM __THIS__", + {"s": "str", "p": "str"}, + [{"s": "abc", "p": ""}], + ) + + +def test_rpad_empty_pad_growth_traps(): + with pytest.raises(Exception, match="Insufficient padding in RPAD"): + duck_check( + "SELECT rpad(s, 5, p) AS r FROM __THIS__", + {"s": "str", "p": "str"}, + [{"s": "abc", "p": ""}], + ) + + +def test_pad_empty_trap_where_guard_is_lazy(): + # The would-trap row ('abc' needs growth) is filtered out / branched away: + # neither engine may evaluate the pad on it. + duck_check( + "SELECT lpad(s, 5, p) AS r FROM __THIS__ WHERE length(s) >= 5", + {"s": "str", "p": "str"}, + [{"s": "abcdef", "p": ""}, {"s": "abc", "p": ""}], + ) + duck_check( + "SELECT CASE WHEN length(s) >= 5 THEN rpad(s, 5, p)" + " ELSE NULL END AS r FROM __THIS__", + {"s": "str", "p": "str"}, + [{"s": "abcdef", "p": ""}, {"s": "abc", "p": ""}], + ) + + +def test_pad_null_pad_preempts_empty_trap(): + # CRITICAL masking pin: lpad('abc',5,NULL) is NULL, NOT the padding trap — + # NULL-strictness wins over the insufficient-padding check. + duck_check( + "SELECT lpad(s, 5, p) AS lp, rpad(s, 5, p) AS rp FROM __THIS__", + {"s": "str", "p": "str?"}, + [{"s": "abc", "p": None}], + ) + + +# --------------------------------------------------------------- replace + + +def test_replace_single_pass_and_empty_needle(): + duck_check( + "SELECT replace(s, n, r) AS o FROM __THIS__", + {"s": "str", "n": "str", "r": "str"}, + [ + {"s": "abc", "n": "", "r": "x"}, # empty needle: strict no-op + {"s": "aaa", "n": "aa", "r": "b"}, # leftmost non-overlap: 'ba' + {"s": "abab", "n": "ab", "r": "ba"}, # single pass: 'baba' + {"s": "ABC", "n": "b", "r": "x"}, # case-sensitive: unchanged + ], + ) + + +def test_replace_null_strict(): + duck_check( + "SELECT replace(s, n, r) AS o FROM __THIS__", + {"s": "str?", "n": "str?", "r": "str?"}, + [ + {"s": None, "n": "a", "r": "b"}, + {"s": "a", "n": None, "r": "b"}, + {"s": "a", "n": "a", "r": None}, + {"s": "aa", "n": "a", "r": "b"}, + ], + ) + + +# ------------------------------------------------------------- translate + + +def test_translate_per_codepoint_semantics(): + duck_check( + "SELECT translate(s, f, t) AS o FROM __THIS__", + {"s": "str", "f": "str", "t": "str"}, + [ + # from longer than to: unmatched from-chars are DELETED -> 'x' + {"s": "abcb", "f": "abc", "t": "x"}, + # duplicate in from: FIRST mapping wins (a->x, not a->y) + {"s": "abc", "f": "aa", "t": "xy"}, + # to='': every from-char deleted + {"s": "abca", "f": "a", "t": ""}, + # from='': identity + {"s": "abc", "f": "", "t": "xyz"}, + # per-CODEPOINT, not per-byte, on multi-byte chars + {"s": "éa☃", "f": "é☃", "t": "xy"}, + ], + ) + + +def test_translate_null_strict(): + duck_check( + "SELECT translate(s, f, t) AS o FROM __THIS__", + {"s": "str?", "f": "str?", "t": "str?"}, + [ + {"s": None, "f": "a", "t": "b"}, + {"s": "a", "f": None, "t": "b"}, + {"s": "a", "f": "a", "t": None}, + {"s": "ab", "f": "a", "t": "b"}, + ], + ) + + +# ------------------------------------------------------------- concat_ws + + +def test_concat_ws_null_skipping_and_null_separator(): + # NULL value args are SKIPPED with their separator; NULL separator (from a + # nullable COLUMN) nulls the whole result; ALL values NULL -> '' not NULL. + duck_check( + "SELECT concat_ws(sep, a, b, c) AS o FROM __THIS__", + {"sep": "str?", "a": "str?", "b": "str?", "c": "str?"}, + [ + {"sep": ",", "a": "x", "b": None, "c": "y"}, + {"sep": ",", "a": None, "b": None, "c": None}, + {"sep": None, "a": "x", "b": "y", "c": "z"}, + {"sep": "-", "a": "x", "b": "y", "c": "z"}, + ], + ) + + +def test_concat_ws_value_args_implicitly_cast(): + # int renders '7'; double renders like DuckDB varchar-casts doubles + # (1.5 / -0.0 keeps its sign / 1e20 -> '1e+20'); bool -> 'true'/'false'. + duck_check( + "SELECT concat_ws(',', i, d, b) AS o FROM __THIS__", + {"i": "int", "d": "float", "b": "bool"}, + [ + {"i": 7, "d": 1.5, "b": True}, + {"i": 0, "d": -0.0, "b": False}, + {"i": -3, "d": 1e20, "b": True}, + ], + ) + + +def test_concat_ws_separator_does_not_implicitly_cast(): + # An int separator is a BIND error in DuckDB; the engine must reject the + # query at build time, not coerce. + with pytest.raises(ValueError, match="no function matches concat_ws"): + duck_check( + "SELECT concat_ws(i, s, s) AS o FROM __THIS__", + {"i": "int", "s": "str"}, + [{"i": 5, "s": "a"}], + ) + + +# ------------------------------------------------------------ ucase/lcase + + +def test_ucase_lcase_are_upper_lower_on_unicode(): + # Pair the alias with its canonical spelling in ONE query over a sweep + # incl. 'ß' (uppercases to U+1E9E), U+0130 'İ', U+212A KELVIN SIGN. + duck_check( + "SELECT ucase(s) AS u1, upper(s) AS u2," + " lcase(s) AS l1, lower(s) AS l2 FROM __THIS__", + {"s": "str"}, + [ + {"s": "ß"}, + {"s": "İ"}, + {"s": "K"}, + {"s": "Straße"}, + {"s": "aBcÉ é"}, + {"s": ""}, + ], + ) diff --git a/tests/test_duckdb_wave3_mathtail.py b/tests/test_duckdb_wave3_mathtail.py new file mode 100644 index 0000000..c95919d --- /dev/null +++ b/tests/test_duckdb_wave3_mathtail.py @@ -0,0 +1,268 @@ +"""Wave-3 math tail — oracle pins vs DuckDB 1.5.5. + +Family: add/subtract/multiply/divide/mod aliases, the // operator, fdiv, +fmod, nextafter. Measured pins live in +docs/superpowers/specs/2026-07-26-wave3-builtin-pins.md ("Math tail"). +Every case below was probed through the vectorized path (table columns). +""" + +from __future__ import annotations + +import re +import struct + +import duckdb +import pytest +from pydantic import create_model +from test_duckdb_interpreter import duck_check + +from sql_transform._interpreter import DuckDBInferFn + +NAN = float("nan") +INF = float("inf") +I64MAX = 9223372036854775807 +I64MIN = -9223372036854775808 + + +# --------------------------------------------------- alias == operator: +# add/subtract/multiply/divide/mod are pure frontend desugars of + - * // %. +# Pairing each alias with its operator in ONE query makes any divergence +# between the two lowerings show up as an intra-row mismatch vs the oracle. + + +def test_int_aliases_pair_with_operators(): + # (7,2)/(−7,2) witness divide()'s TRUNCATING int division (3 / −3, + # BIGINT — repr int); mixed-sign rows witness mod's dividend sign + # (mod(−7,3) = −1, mod(7,−3) = 1). NOTE: DuckDB also has unary + # add(x)=x / subtract(x)=−x; the engine rejects those cleanly + # ("unsupported: add with 1 arguments") — out of the pinned surface. + duck_check( + "SELECT add(a, b) AS s1, a + b AS s2, subtract(a, b) AS d1, a - b AS d2," + " multiply(a, b) AS m1, a * b AS m2, divide(a, b) AS q1, a // b AS q2," + " mod(a, b) AS r1, a % b AS r2 FROM __THIS__", + {"a": "int", "b": "int"}, + [ + {"a": 7, "b": 2}, + {"a": -7, "b": 2}, + {"a": 7, "b": -2}, + {"a": -7, "b": -2}, + {"a": -7, "b": 3}, + {"a": 7, "b": -3}, + {"a": 0, "b": 3}, + {"a": 123456789, "b": 1000}, + ], + ) + + +def test_double_aliases_pair_with_operators(): + # // on DOUBLE is PLAIN division: −7.5 // 2.0 = −3.75 (NOT floor). + # mod keeps the dividend's sign on doubles too: mod(−7.5, 2.0) = −1.5. + duck_check( + "SELECT add(x, y) AS s1, x + y AS s2, subtract(x, y) AS d1, x - y AS d2," + " multiply(x, y) AS m1, x * y AS m2, divide(x, y) AS q1, x // y AS q2," + " mod(x, y) AS r1, x % y AS r2 FROM __THIS__", + {"x": "float", "y": "float"}, + [ + {"x": -7.5, "y": 2.0}, + {"x": 7.5, "y": -2.0}, + {"x": -7.5, "y": 2.5}, + {"x": 7.5, "y": 2.5}, + {"x": 1.5, "y": 3.0}, + {"x": -0.5, "y": 0.25}, + ], + ) + + +# ------------------------------------------------------- zero divisors: +# ints: // divide % mod all NULL; / promotes to DOUBLE and stays IEEE. +# doubles: // divide NULL, but % mod are NaN VALUES (not NULL) — the +# repr-based compare keeps 'nan' and None apart, so the row is a real pin. + + +def test_int_zero_divisor_nulls_and_ieee_slash(): + duck_check( + "SELECT a // b AS q, divide(a, b) AS q2, a % b AS r, mod(a, b) AS r2," + " a / b AS f FROM __THIS__", + {"a": "int", "b": "int"}, + [{"a": 7, "b": 0}, {"a": -7, "b": 0}, {"a": 0, "b": 0}, {"a": 7, "b": 3}], + ) + + +def test_double_zero_divisor_null_vs_nan_vs_inf(): + # CRITICAL row: x % 0.0 -> NaN value while x // 0.0 -> NULL; / -> ±inf. + duck_check( + "SELECT x // y AS q, divide(x, y) AS q2, x % y AS r, mod(x, y) AS r2," + " x / y AS f FROM __THIS__", + {"x": "float", "y": "float"}, + [{"x": 7.5, "y": 0.0}, {"x": -7.5, "y": 0.0}, {"x": 0.0, "y": 0.0}], + ) + + +# ------------------------------------------------------ overflow traps: +# byte-identical error texts on both lowerings, and WHERE/CASE guards +# genuinely prevent evaluation of the trapping row. + + +def test_add_overflow_traps_operator_and_alias(): + # Both engines trap on i64::MAX + 1 through both lowerings, with + # DuckDB's own text verbatim (values interpolated, trailing '!'). + for expr in ["a + b", "add(a, b)"]: + with pytest.raises( + Exception, + match=re.escape("Overflow in addition of INT64 (9223372036854775807 + 1)!"), + ): + duck_check( + f"SELECT {expr} AS s FROM __THIS__", + {"a": "int", "b": "int"}, + [{"a": I64MAX, "b": 1}], + ) + + +def test_int64_min_division_overflow_traps(): + # INT64_MIN // −1 and mod(INT64_MIN, −1) both trap in both engines with + # the DIVISION text for both — DuckDB's own % message says 'division', + # and unlike add/sub/mul it carries no trailing '!'. + for expr in ["a // b", "mod(a, b)"]: + with pytest.raises( + Exception, + match=re.escape("Overflow in division of -9223372036854775808 / -1"), + ): + duck_check( + f"SELECT {expr} AS q FROM __THIS__", + {"a": "int", "b": "int"}, + [{"a": I64MIN, "b": -1}], + ) + + +def test_overflow_guarded_rows_do_not_trap(): + duck_check( + "SELECT a + b AS s, add(a, b) AS s2 FROM __THIS__ WHERE a < 100", + {"a": "int", "b": "int"}, + [{"a": 1, "b": 2}, {"a": I64MAX, "b": 1}], + ) + duck_check( + "SELECT CASE WHEN b <> -1 THEN a // b ELSE NULL END AS q FROM __THIS__", + {"a": "int", "b": "int"}, + [{"a": 7, "b": 2}, {"a": I64MIN, "b": -1}], + ) + + +# --------------------------------------------------- mod vs fmod signs: +# mod/% = truncated (dividend's sign); fmod = floor pair (DIVISOR's sign). +# Signed-zero split: mod(−7.5, 2.5) = −0.0 but fmod(−7.5, 2.5) = +0.0 — +# repr distinguishes, so one row pins both. + + +def test_mod_dividend_sign_vs_fmod_divisor_sign(): + duck_check( + "SELECT mod(x, y) AS m, x % y AS p, fmod(x, y) AS f FROM __THIS__", + {"x": "float", "y": "float"}, + [ + {"x": -7.5, "y": 2.5}, # m = p = -0.0, f = 0.0 + {"x": -7.5, "y": 2.0}, # m = -1.5, f = 0.5 + {"x": 7.5, "y": -2.0}, # m = 1.5, f = -0.5 + ], + ) + + +# --------------------------------------------------------- fdiv / fmod: +# the FLOOR-division pair, ALWAYS DOUBLE even on BIGINT inputs (repr float), +# so INT64_MIN fdiv −1 is 9.223372036854776e18 — no trap. + + +def test_fdiv_fmod_bigint_inputs_yield_double(): + duck_check( + "SELECT fdiv(a, b) AS q, fmod(a, b) AS r FROM __THIS__", + {"a": "int", "b": "int"}, + [ + {"a": -7, "b": 2}, # q = -4.0 (floor, not truncate) + {"a": 7, "b": -3}, # q = -3.0 + {"a": -7, "b": 3}, # r = 2.0 (divisor's sign) + {"a": 7, "b": 0}, # q = inf, r = nan + {"a": -7, "b": 0}, # q = -inf + {"a": I64MIN, "b": -1}, # q = 9.223372036854776e18, NO trap + ], + ) + + +def test_fdiv_fmod_double_edges(): + # fmod(1.0, inf) = NaN refutes the C-fmod reading (C gives 1.0): DuckDB + # computes x − floor(x/y)·y, and floor(0)·inf is NaN. + duck_check( + "SELECT fdiv(x, y) AS q, fmod(x, y) AS r FROM __THIS__", + {"x": "float", "y": "float"}, + [ + {"x": -7.5, "y": 2.0}, # q = -4.0, r = 0.5 + {"x": 7.5, "y": -2.0}, # q = -4.0, r = -0.5 + {"x": -7.5, "y": 2.5}, # q = -3.0, r = +0.0 + {"x": 7.5, "y": 0.0}, # q = inf, r = nan + {"x": -7.5, "y": 0.0}, # q = -inf, r = nan + {"x": 1.0, "y": INF}, # q = 0.0, r = nan (C fmod would say 1.0) + {"x": 0.0, "y": 0.0}, # q = nan, r = nan + ], + ) + + +def test_computed_nan_bits_match_oracle(): + # repr collapses every NaN to 'nan', so pin the BITS manually. fmod's + # NaN comes from hardware arithmetic (0*inf under SSE) and is fff8… + # on every x86 platform. The %-by-zero NaN comes from LIBM fmod and + # its SIGN is platform-dependent (Windows ucrt 7ff8…, Linux glibc + # fff8… — CI-discovered, the cbrt situation again): both engines use + # the platform libm, so the pin is ENGINE == ORACLE bit agreement, + # not a constant. + def bits(v: float) -> str: + return format(struct.unpack(" 1/3. DOUBLE output (repr + # in the harness would catch an int 1 vs float 1.0 drift). + duck_check( + "SELECT jaccard(a, b) AS j FROM __THIS__", + AB, + [ + {"a": "ab", "b": "ba"}, # 1.0 + {"a": "aab", "b": "ab"}, # 1.0 + {"a": "abc", "b": "abd"}, # 0.5 + {"a": "é", "b": "è"}, # 1/3 + {"a": "a", "b": "A"}, # 0.0: case-sensitive + ], + ) + + +def test_mismatches_is_hamming_pairwise(): + # Byte unit again: 'é' vs 'è' differ in one of two bytes. + duck_check( + "SELECT hamming(a, b) AS h, mismatches(a, b) AS m FROM __THIS__", + AB, + [ + {"a": "é", "b": "è"}, # 1 + {"a": "abcd", "b": "abcd"}, # 0 + {"a": "abcd", "b": "azcx"}, # 2 + {"a": "A", "b": "a"}, # 1: case-sensitive + ], + ) + + +def test_hamming_byte_length_mismatch_traps(): + # 'é' is 2 bytes vs 1 -> trap, and the message says Mismatch. + with pytest.raises( + Exception, match="Mismatch Function: Strings must be of equal length!" + ): + duck_check( + "SELECT hamming(a, b) AS h FROM __THIS__", + AB, + [{"a": "é", "b": "e"}], + ) + + +def test_hamming_both_empty_traps_length_gt_zero(): + with pytest.raises( + Exception, match="Mismatch Function: Strings must be of length > 0!" + ): + duck_check( + "SELECT hamming(a, b) AS h FROM __THIS__", + AB, + [{"a": "", "b": ""}], + ) + + +def test_jaccard_empty_either_side_traps(): + for row in [{"a": "", "b": "a"}, {"a": "a", "b": ""}]: + with pytest.raises(Exception, match="Jaccard Function: An argument too short!"): + duck_check( + "SELECT jaccard(a, b) AS j FROM __THIS__", + AB, + [row], + ) + + +def test_null_pre_empts_every_trap(): + # NULL-strict on every argument: these exact rows would trap if the + # traps ran before the NULL check — they must yield NULL instead. + duck_check( + "SELECT jaccard(a, b) AS j, hamming(a, b) AS h," + " levenshtein(a, b) AS lv, damerau_levenshtein(a, b) AS dl" + " FROM __THIS__", + {"a": "str?", "b": "str?"}, + [ + {"a": None, "b": ""}, # jaccard(NULL,'') / hamming(NULL,'') + {"a": "", "b": None}, + {"a": None, "b": None}, + ], + ) + + +def test_where_guard_masks_trap_rows(): + # Filtered rows must never evaluate the trapping calls. + duck_check( + "SELECT hamming(a, b) AS h, jaccard(a, b) AS j FROM __THIS__ WHERE ok", + {"a": "str", "b": "str", "ok": "bool"}, + [ + {"a": "ab", "b": "xy", "ok": True}, + {"a": "é", "b": "e", "ok": False}, # hamming length trap + {"a": "", "b": "", "ok": False}, # both functions trap + ], + ) + + +def test_embedded_nul_is_an_ordinary_byte(): + duck_check( + "SELECT levenshtein(a, b) AS lv, hamming(a, c) AS h FROM __THIS__", + {"a": "str", "b": "str", "c": "str"}, + [{"a": "a\x00b", "b": "ab", "c": "axb"}], + ) diff --git a/tests/test_duckdb_wave3_subscripts.py b/tests/test_duckdb_wave3_subscripts.py new file mode 100644 index 0000000..f8038e1 --- /dev/null +++ b/tests/test_duckdb_wave3_subscripts.py @@ -0,0 +1,212 @@ +"""Wave-3 builtin pins: VARCHAR subscripts, codepoint probes, strip_accents. + +Pins measured against duckdb 1.5.5 per +docs/superpowers/specs/2026-07-26-wave3-builtin-pins.md: +- array_extract/list_extract on VARCHAR: 1-based CODEPOINT indexing, + 0 / out-of-range -> '' (empty string, not NULL), negative from end, + NULL-strict, list_extract alias-identical. +- array_slice/list_slice on VARCHAR: both-ends-inclusive 1-based, negative + from end, clamping at both ends, reversed/out-of-range -> '', NULL bound + -> NULL (not an open bound), alias-identical, splits combining sequences. + Step slice does not prepare (build-time reject). +- unicode/ord/ascii: first codepoint; '' -> -1 for unicode/ord but 0 for + ascii (the sole divergence); NULL-strict. +- bit_length = 8 * strlen (UTF-8 bytes). +- strip_accents: utf8proc-style accent removal — decomposes-and-drops + combining marks, rewrites accent-free lookalikes (U+212A -> 'K'), + composes Hangul jamo, deletes Indic vowel signs; NUL truncates output + only when the input has non-ASCII characters; idempotent; NULL-strict. + +No runtime-trapping op in this family (out-of-range yields ''), so the +lazy-guard convention has nothing to witness here. +""" + +from __future__ import annotations + +import duckdb +import pytest +from test_duckdb_interpreter import duck_check + + +def test_array_extract_codepoints_negative_and_alias(): + # 1-based codepoint indexing on 'éa' plus an astral char; negative + # indexes count from the end; i=0, -len-1 and len+1 all yield ''. + # list_extract must be alias-identical (paired column). + duck_check( + "SELECT s, i, array_extract(s, i) AS e, list_extract(s, i) AS l FROM __THIS__", + {"s": "str", "i": "int"}, + [ + {"s": "éa", "i": 1}, + {"s": "éa", "i": 2}, + {"s": "éa", "i": 0}, + {"s": "éa", "i": -1}, + {"s": "éa", "i": -2}, + {"s": "éa", "i": -3}, + {"s": "éa", "i": 3}, + {"s": "a\U0001f600b", "i": 2}, + {"s": "a\U0001f600b", "i": -2}, + ], + ) + + +def test_array_extract_out_of_range_is_empty_string_not_null(): + # The '' rows asserted explicitly: IS NOT NULL must be TRUE both ways. + duck_check( + "SELECT array_extract(s, 99) AS hi, array_extract(s, -99) AS lo, " + "array_extract(s, 99) IS NOT NULL AS hi_nn, " + "array_extract(s, -99) IS NOT NULL AS lo_nn FROM __THIS__", + {"s": "str"}, + [{"s": "éa"}], + ) + + +def test_array_extract_null_strict(): + duck_check( + "SELECT array_extract(s, i) AS e, list_extract(s, i) AS l FROM __THIS__", + {"s": "str?", "i": "int?"}, + [{"s": None, "i": 1}, {"s": "abc", "i": None}], + ) + + +def test_array_slice_inclusive_clamping_and_alias(): + # Both-ends-inclusive 1-based; negative from end; a<=0 clamps to start; + # b>len clamps to end; reversed / fully out-of-range -> ''. + duck_check( + "SELECT a, b, array_slice(s, a, b) AS r, list_slice(s, a, b) AS r2 " + "FROM __THIS__", + {"s": "str", "a": "int", "b": "int"}, + [ + {"s": "hello", "a": 2, "b": 4}, # 'ell' + {"s": "hello", "a": -3, "b": -1}, # 'llo' + {"s": "hello", "a": 2, "b": -2}, # 'ell' + {"s": "hello", "a": 0, "b": 3}, # 'hel' + {"s": "hello", "a": 4, "b": 100}, # 'lo' + {"s": "hello", "a": 4, "b": 2}, # '' + {"s": "hello", "a": 10, "b": 20}, # '' + ], + ) + + +def test_array_slice_null_bound_is_null_not_open(): + # A NULL bound nulls the result — it is NOT treated as an open bound. + duck_check( + "SELECT array_slice(s, a, b) AS r, " + "array_slice(s, a, b) IS NULL AS r_null FROM __THIS__", + {"s": "str", "a": "int?", "b": "int?"}, + [ + {"s": "hello", "a": None, "b": 3}, + {"s": "hello", "a": 2, "b": None}, + ], + ) + + +def test_array_slice_and_extract_split_combining_sequence(): + # Codepoint semantics split 'e'+U+0301: the mark travels alone. + duck_check( + "SELECT array_slice(s, 1, 1) AS head, array_slice(s, 2, 3) AS tail, " + "array_extract(s, 2) AS mark FROM __THIS__", + {"s": "str"}, + [{"s": "éx"}], + ) + + +def test_array_slice_with_step_fails_to_build(): + with pytest.raises(ValueError, match="step"): + duck_check( + "SELECT array_slice(s, 1, 3, 2) AS r FROM __THIS__", + {"s": "str"}, + [{"s": "hello"}], + ) + + +def test_unicode_ord_ascii_family_on_same_rows(): + # unicode/ord: first codepoint, '' -> -1. ascii: identical except + # '' -> 0 — the sole divergence. NULL-strict for all three. + duck_check( + "SELECT unicode(s) AS u, ord(s) AS o, ascii(s) AS a FROM __THIS__", + {"s": "str?"}, + [ + {"s": ""}, + {"s": "abc"}, + {"s": "\U0001f600x"}, + {"s": "é"}, + {"s": None}, + ], + ) + + +def test_bit_length_is_eight_times_strlen(): + # Witnesses: 'é' -> 16, astral -> 32, '' -> 0; plus an in-query sweep + # asserting bit_length(s) = 8 * strlen(s) on every row. + duck_check( + "SELECT bit_length(s) AS b, " + "bit_length(s) = 8 * strlen(s) AS agrees FROM __THIS__", + {"s": "str"}, + [ + {"s": "é"}, + {"s": "\U0001f600"}, + {"s": ""}, + {"s": "abc"}, + {"s": "é"}, + {"s": "aé\U0001f600"}, + ], + ) + + +def test_strip_accents_witnesses_and_idempotence(): + # 'é' -> 'e'; U+212A KELVIN SIGN -> 'K' (accent-free rewrite); + # combining mark deleted (attached and lone); Indic vowel sign + # deleted; Hangul jamo compose to a syllable; NULL-strict. + # The idem column pins idempotence on every row. + duck_check( + "SELECT strip_accents(s) AS r, " + "strip_accents(strip_accents(s)) = strip_accents(s) AS idem " + "FROM __THIS__", + {"s": "str?"}, + [ + {"s": "é"}, + {"s": "K"}, + {"s": "é"}, + {"s": "́"}, + {"s": "가"}, # -> U+AC00 + {"s": "का"}, # vowel sign deleted -> U+0915 + {"s": "ा"}, # lone vowel sign -> '' + {"s": None}, + ], + ) + + +def test_strip_accents_nul_context_quirk(): + # ASCII-only input passes NUL through verbatim; any non-ASCII char in + # the input flips to the utf8proc path, which truncates at the NUL. + duck_check( + "SELECT strip_accents(s) AS r FROM __THIS__", + {"s": "str"}, + [ + {"s": "a\x00b"}, # unchanged: 'a\x00b' + {"s": "a\x00é"}, # truncated: 'a' + ], + ) + + +def test_strip_accents_census_sample(): + # Deterministic sample (~300) of every codepoint strip_accents changes + # in planes 0-1, extracted from the oracle itself, replayed through + # both engines as single-char rows. + con = duckdb.connect() + cps = [ + r[0] + for r in con.execute( + "SELECT i FROM generate_series(1, 131071) t(i) " + "WHERE i NOT BETWEEN 55296 AND 57343 " + "AND strip_accents(chr(i::INTEGER)) != chr(i::INTEGER) " + "ORDER BY i" + ).fetchall() + ] + assert len(cps) > 1000 # the map is big; a tiny census means a bad probe + sample = cps[:: max(1, len(cps) // 300)] + duck_check( + "SELECT s, strip_accents(s) AS r FROM __THIS__", + {"s": "str"}, + [{"s": chr(c)} for c in sample], + )