code wiki / (root) / nx_didyoumean.nx

nx_didyoumean.nx

buildroot/runtime/nx_didyoumean.nx

15204 B310 linesdepth 3pulls 4 transitivereach 72 importersview sourcekind library
docsdependenciesstructsconstsfunctions

about

nx_didyoumean.nx -- SEARCH RUNG F4: THE DID-YOU-MEAN LINE, BUILT FROM THE INDEX'S OWN TERM DICTIONARY (2026-09-18). WHAT IT DECIDES. For each eligible word of a query, one question: is the word RARE in the index while a one-edit neighbour of it is COMMON? When it is, the commonest such neighbour is offered in the word's place ("Did you mean ...?"), every other byte of the query kept. It ranks, filters and rewrites nothing: the typed query is still the one searched, and the line only offers the alternative. THE CANDIDATES ARE GENERATED, NOT SEARCHED FOR. The seg's zero-hit corrector (dss_correct_raw) walks the sorted run of dictionary terms sharing the word's first byte and runs a bounded Levenshtein per entry -- the right shape for a two-edit repair, and the reason it only runs when a query finds nothing: that run is a large slice of a multi-segment dictionary. This lib asks the converse question. It writes out every string exactly ONE Damerau edit from the word -- deletion, adjacent transposition, substitution, insertion over the tokenizer's letter class -- and asks the dictionary for each one's document frequency by EXACT lookup (ss_term_dcount, the sampled-window path the ranker's idf already reads). The work is a function of the word's LENGTH and never of the dictionary's size, which is what lets it run on queries that DO find results. EVERY BOUND IS DERIVED FROM THE RESOURCE IT DESCRIBES: - THE EDIT BOUND comes from the word's length: an edit is admitted only while it leaves a strict majority of the typed characters untouched (2 x edits < length). A two-letter word is never corrected; a word of three or more letters may take the one edit this lib generates. Two-edit repairs stay where they already live, in the zero-result fallback (dss_correct), unchanged. - RARE and COMMON come from the index's own document-frequency scale, the one the ranker weighs terms by (nx_intlog idf_q10). The widest idf the index can give any term is idf(N, 1). A word is RARE when its idf exceeds half that span; a candidate is COMMON when its idf does not. That is the midpoint of the log-df scale -- df below, or at or above, sqrt(N + 1) -- found without a square root and drawn from the LIVE document count N, so it moves with the corpus. dym_mid names the value by bisecting the predicate itself, so the announced number and the decision agree. - THE FIRST BYTE IS KEPT: the zero-hit corrector's documented choice, kept for the same determinism and because an edit at position 0 is where a short rare word most often lands on an unrelated common one. - THE WORK IS BOUNDED BY THE QUERY: at most `maxwords` words (the caller passes the ranker's own term cap), each costing at most dym_lookup_bound(length) exact lookups, every one of them counted in the stats the caller publishes. DOCUMENTED IMPRECISION -- the price of document frequency alone, with no query log and no context: - a rare real word one edit from a far commoner real word draws a suggestion. The typed word's results are still served, so this error costs one line of text and never a result. - a misspelling that is itself common (df at or above the midpoint) is taken at its word. - a slip in the FIRST letter is out of scope (uantum is not offered quantum), as it is for the incumbent corrector. license_tier: ORIGINAL No hw writes (Rule 26). Read-only on the index.

dependencies 2 imports · 1 importers

nx_seg_store.nx nx_intlog.nx nx_didyoumean.nx nx_docportal_search_seg.nx

imports: nx_seg_store.nxnx_intlog.nx

imported by: nx_docportal_search_seg.nx

structs

none

consts

40const DYM_LOW_A: i64 = 97 // 'a' -- the edit alphabet is the tokenizer's lowercase letter class, a..z
41const DYM_LOW_Z: i64 = 122 // 'z'
42const DYM_UP_A: i64 = 65 // 'A'
43const DYM_UP_Z: i64 = 90 // 'Z'
44const DYM_CASE: i64 = 32 // 'a' - 'A'
45const DYM_DIG_0: i64 = 48 // '0'
46const DYM_DIG_9: i64 = 57 // '9'
47const DYM_HIGH: i64 = 128 // a byte of a multi-byte UTF-8 sequence: a word holding one is left as typed
48const DYM_COLON: i64 = 58 // site:host, inurl:frag -- an operator name, or the value bound to it
49const DYM_DOT: i64 = 46 // host labels (reddit.com)
50const DYM_SLASH: i64 = 47 // paths and the r/<name> idiom
51const DYM_EDIT: i64 = 1 // the neighbourhood this lib generates is exactly one edit wide
52const DYM_KEEP: i64 = 1 // edits start at byte 1: the first byte of the word is kept
54const DYM_ST_DOCS: i64 = 0 // N, the live document count the scale is drawn from
55const DYM_ST_MID: i64 = 1 // the smallest COMMON df on that scale (dym_mid)
56const DYM_ST_WORDS: i64 = 2 // eligible words examined
57const DYM_ST_RARE: i64 = 3 // of them, rare
58const DYM_ST_LOOKUPS: i64 = 4 // one-edit candidates looked up
59const DYM_ST_HELD: i64 = 5 // of them, held by the dictionary (df > 0)
60const DYM_ST_FIXED: i64 = 6 // words replaced in the suggestion
61const DYM_ST_US: i64 = 7 // microseconds this answer cost
62const DYM_ST_TDF: i64 = 8 // df of the last rare word examined
63const DYM_ST_CDF: i64 = 9 // df of its commonest neighbour (0 = the dictionary holds none)
64const DYM_ST_ABSTAIN: i64 = 10 // 1 = the dictionary could not be read or the answer did not fit: nothing is offered
65const DYM_ST_MEMO: i64 = 11 // set by a caller that answered from a memo; this lib always writes 0
66const DYM_ST_SLOTS: i64 = 12 // the vector's size: the slots above

functions

68func dym_is_letter(c: i64) -> i64
73func dym_is_wordbyte(c: i64) -> i64
called by 1: dym_suggest calls 1: dym_is_letter
80func dym_is_joiner(c: i64) -> i64
called by 1: dym_suggest
86func dym_lower(c: i64) -> i64
called by 2: dym_emitdym_suggest
91func dym_letters() -> i64 { return DYM_LOW_Z - DYM_LOW_A + 1 }
called by 1: dym_lookup_bound
94func dym_edits_allowed(n: i64) -> i64
100func dym_lookup_bound(n: i64) -> i64
called by 1: main calls 2: dym_edits_alloweddym_letters
108func dym_rare(bign: i64, df: i64) -> i64
114func dym_common(bign: i64, df: i64) -> i64
called by 2: dym_suggestmain calls 1: dym_rare
121func dym_mid(bign: i64) -> i64
133func dym_consider(h: *i64, cand: *u8, cl: i64, bestdf: i64, best: *u8, st: *i64) -> i64
called by 1: dym_best_edit1 calls 1: ss_term_dcount
149func dym_best_edit1(h: *i64, t: *u8, n: i64, cand: *u8, best: *u8, st: *i64) -> i64
208func dym_emit(out: *u8, o: i64, cap: i64, src: *u8, n: i64) -> i64
called by 1: dym_suggest calls 1: dym_lower
219func dym_suggest(h: *i64, q: *u8, qn: i64, maxwords: i64, out: *u8, outcap: i64, st: *i64) -> i64