"""Session 23: bounded Snow Crash key probe; Python + NumPy, no network.
Candidate choices and transforms fixed before inspecting outputs.
Run from any directory. Saves all candidates, controls and phrase counts.
"""
import hashlib
import json
import re
from word_core import *

KEYS = {s: np.array([LET.index(ch) for ch in s])
        for s in ['NAMSHUB', 'ENKI', 'GISHUR']}
FORMS = [(1, -1), (1, 1), (-1, 1)]
PHRASES = ['informational hygiene', 'deep structures', 'neurolinguistic virus',
           'self fulfilling fiction', 'code is just a form of speech',
           'master of all the right commands', 'key words',
           'patterns that rule the universe', 'perfect execution', 'gis hur', 'nam shub']
TERMS = ['asherah', 'enki', 'enlil', 'eridu', 'babel', 'nam shub', 'hammurabi', 'inanna']

def probe(source):
    c = vals(source)
    lengths = [len(w['values']) for w in tokenize(source)]
    cum = np.cumsum(lengths)
    nw = int(np.sum(cum <= int(.6 * len(c))))
    cut = int(cum[nw - 1])
    policies = [('continuous', '')] + ([('page', '%')] if '%' in source else [])
    candidates = []
    for reset_name, reset in policies:
        ix = positions(source, reset=reset)[0]
        for name, original in KEYS.items():
            for direction, key in [('forward', original), ('reverse', original[::-1])]:
                stream = key[(ix[None, :] + np.arange(len(key))[:, None]) % len(key)]
                for a, b in FORMS:
                    d = (a * c[None, :] + b * stream) % 29
                    pre = FULL_MODEL.score(d[:, :cut], lengths[:nw])
                    suf = FULL_MODEL.score(d[:, cut:], lengths[nw:])
                    for phase in range(len(key)):
                        candidates.append(dict(key=name, direction=direction, phase=phase,
                            reset=reset_name, cipher_sign=a, key_sign=b,
                            prefix_score=float(pre[phase]), suffix_score=float(suf[phase]),
                            text=render(source, d[phase]), values=d[phase].tolist()))
    candidates.sort(key=lambda r:r['prefix_score'], reverse=True)
    return dict(runes=len(c), split_after_runes=cut, settings=len(candidates),
                candidates=candidates)

def main():
    source = '%'.join([lp[57], lp[57]])
    plain = vals(source)
    controls = []
    for name, key in KEYS.items():
        for reset_name, reset in [('continuous', ''), ('page', '%')]:
            for a, b in FORMS:
                ix = positions(source, reset=reset)[0]
                phase = 1
                stream = key[(ix+phase) % len(key)]
                cipher = (a * (plain - b*stream)) % 29
                r = probe(replace_values(source, cipher))
                exact = r['candidates'][0]['values'] == plain.tolist()
                assert exact, (name, reset_name, a, b)
                controls.append(dict(key=name, reset=reset_name, cipher_sign=a,
                    key_sign=b, phase=phase, rank_one_exact=exact))
    section = '%'.join(lp[:56]).split('$')[7]
    assert len(vals(lp[32])) == 121 and len(vals(section)) == 1589
    targets = {}
    for name, text in [('LP2_p32_tree', lp[32]), ('section_7', section)]:
        result = probe(text)
        for row in result['candidates']: row.pop('values')
        result['source_sha256'] = hashlib.sha256(text.encode()).hexdigest()
        targets[name] = result
    translation = (ROOT/'liber-primus__translation.txt').read_text()
    entries = [m.group(1) for line in translation.splitlines()
               if (m:=re.match(r'^\d+(?:\.\d+)+\s+(.+)$',line))]
    norm = lambda s:' '.join(re.findall('[a-z]+',s.lower()))
    normalized = [' '+norm(e)+' ' for e in entries]
    counts = {p:sum(e.count(' '+norm(p)+' ') for e in normalized) for p in PHRASES+TERMS}
    result = dict(keys={k:v.tolist() for k,v in KEYS.items()}, controls=controls,
        settings=sum(t['settings'] for t in targets.values()), targets=targets,
        phrase_audit=dict(entries=len(entries), counts=counts,
            translation_sha256=hashlib.sha256(translation.encode()).hexdigest()),
        transcription_sha256=hashlib.sha256((ROOT/'liber-primus__transcription--master.txt').read_bytes()).hexdigest(),
        limits=['Only three chosen keys; no inference that the novel specifies them.',
            'Zero-based GP; separate Latin-letter tokens; no alternate spellings or prime-valued keys.',
            'Three modular forms, both key directions, all phases; continuous or page reset only.',
            'Punctuation never advances key; no F exceptions, shifts, autokey or extra layers.',
            'First 60 percent rounded down to whole words ranks all settings; suffix cannot select.',
            'Suffix has been seen in earlier research; not a historically untouched holdout.',
            'Small LP1 English model; controls establish only recovery on artificial examples.',
            'All candidates saved; human review limited to leading ten per target.',
            'Phrase audit is a selected query list, not a full-novel comparison.'])
    (ROOT/'snow_key_results.json').write_text(json.dumps(result,indent=2,ensure_ascii=False)+'\n')
    print(json.dumps(dict(settings=result['settings'], controls=len(controls),
        targets={k:{x:v[x] for x in ['runes','split_after_runes','settings']} for k,v in targets.items()},
        phrase_audit=result['phrase_audit']),indent=2))

if __name__ == '__main__': main()
