""" Typo generator for Trend Surfer / brand typos. No external APIs. """ from __future__ import annotations import string KEYBOARD_NEIGHBORS = { # simplified QWERTY adjacency (enough for useful typos) "q": "wa", "w": "qase", "e": "wsdr", "r": "edft", "t": "rfgy", "y": "tghu", "u": "yhji", "i": "ujko", "o": "iklp", "p": "ol", "a": "qwsz", "s": "awedxz", "d": "serfcx", "f": "drtgvc", "g": "ftyhbv", "h": "gyujbn", "j": "huikmn", "k": "jiolm", "l": "kop", "z": "asx", "x": "zsdc", "c": "xdfv", "v": "cfgb", "b": "vghn", "n": "bhjm", "m": "njk", } def _normalize_brand(brand: str) -> str: b = (brand or "").lower().strip() b = "".join(ch for ch in b if ch in string.ascii_lowercase) return b def generate_typos(brand: str, *, limit: int = 100) -> list[str]: b = _normalize_brand(brand) if len(b) < 2: return [] candidates: list[str] = [] seen = set() def _add(s: str): if s and s not in seen and s != b: seen.add(s) candidates.append(s) # 1) single deletion for i in range(len(b)): _add(b[:i] + b[i + 1 :]) if len(candidates) >= limit: return candidates # 2) single insertion (duplicate char) for i in range(len(b)): _add(b[:i] + b[i] + b[i:]) if len(candidates) >= limit: return candidates # 3) adjacent transposition for i in range(len(b) - 1): _add(b[:i] + b[i + 1] + b[i] + b[i + 2 :]) if len(candidates) >= limit: return candidates # 4) neighbor substitution for i, ch in enumerate(b): neigh = KEYBOARD_NEIGHBORS.get(ch, "") for n in neigh: _add(b[:i] + n + b[i + 1 :]) if len(candidates) >= limit: return candidates return candidates[:limit]