"""Session 15: fixed literary-key probe. No network, no adaptive key expansion.
Run with Python + NumPy. Scores rank candidates; they do not verify solutions.
"""
import hashlib
import json
from word_core import *

# Frozen before examining unsolved outputs. These are exploratory candidates,
# not keys unambiguously specified by a literary reference.
KEY_TEXTS = ['SELFRELIANCE', 'TRUSTTHYSELF', 'INMOSTOUTMOST', 'ENERGY',
             'CIRCUMFERENCE', 'DIVINITY', 'ALLTHATLIVESISHOLY',
             'ALLWORDSARESACRED', 'LOVEISTHELAW', 'FIRFUMFERENFE']

def encode(text):
    text = text.replace('V', 'U').replace('C', 'K').replace('Q', 'K').replace('Z', 'S')
    table = dict((x, i) for i, x in enumerate(LET))
    tokens = sorted(table, key=len, reverse=True)
    out = []
    while text:
        token = next(t for t in tokens if text.startswith(t))
        out.append(table[token]); text = text[len(token):]
    return np.array(out, dtype=np.int64)

KEYS = {text: encode(text) for text in KEY_TEXTS}
FORMS = [(1, -1), (1, 1), (-1, 1)]

def probe(source):
    c = vals(source)
    lengths = [len(w['values']) for w in tokenize(source)]
    # Cut only between words; the suffix never chooses key/phase/reset/skip.
    cumulative = np.cumsum(lengths)
    nw = int(np.sum(cumulative <= int(.6 * len(c))))
    cut = int(cumulative[nw - 1])
    leaders = []; count = 0
    for reset_name, reset in [('section', ''), ('page', '%')]:
        for skip_f in [False, True]:
            ix = positions(source, reset=reset, skip_f=skip_f)[0]
            for name, key in KEYS.items():
                stream = key[(ix[None, :] + np.arange(len(key))[:, None]) % len(key)]
                for a, b in FORMS:
                    decoded = (a * c[None, :] + b * stream) % 29
                    if skip_f: decoded[:, c == 0] = 0
                    score = FULL_MODEL.score(decoded[:, :cut], lengths[:nw])
                    suffix = FULL_MODEL.score(decoded[:, cut:], lengths[nw:])
                    count += len(key)
                    # Every family contributes its best prefix candidate. This
                    # includes the global prefix winner, not necessarily top-10 globally.
                    j = int(np.argmax(score))
                    leaders.append(dict(key=name, phase=j, reset=reset_name,
                        skip_all_cipher_F=skip_f, cipher_sign=a, key_sign=b,
                        prefix_score=float(score[j]), suffix_score=float(suffix[j]),
                        text=render(source, decoded[j]), values=decoded[j].tolist()))
    leaders.sort(key=lambda x: x['prefix_score'], reverse=True)
    return dict(settings=count, runes=len(c), split_after_runes=cut,
                leaders=leaders[:10])

def main():
    # All three algebraic forms: withheld real plaintext, artificially encrypted.
    # Include a page boundary so the page-reset control is nontrivial.
    plain_source = '%'.join([lp[57], lp[57]])
    plain = vals(plain_source)
    controls = []
    for reset_name, reset in [('section', ''), ('page', '%')]:
        for a, b in FORMS:
            key = KEYS['SELFRELIANCE']; phase = 3
            ix = positions(plain_source, reset=reset)[0]
            stream = key[(ix + phase) % len(key)]
            cipher = (a * (plain - b * stream)) % 29
            result = probe(replace_values(plain_source, cipher))
            exact = result['leaders'][0]['values'] == plain.tolist()
            assert exact, (reset_name, a, b, result['leaders'][0])
            controls.append(dict(reset=reset_name, cipher_sign=a, key_sign=b,
                                 phase=phase, runes=len(plain), rank_one_exact=exact))
    out = dict(keys={k:v.tolist() for k,v in KEYS.items()}, controls=controls,
        mapping='Zero-based GP order from lp_core; greedy longest token; C/Q->K, V->U, Z->S. No cross-word ambiguities explored.',
        selection='First 60 percent rounded down to whole words; suffix reported unchanged.',
        limits=['Exploratory key selection, not a uniquely implied source-book cipher.',
                'No arbitrary shifts, key reversals, fitted keys, selective F subsets, or extra layers.',
                'All-cipher-F skip is only one crude exception policy; real selective-F policies are not covered.',
                'Controls test artificial ciphers and rank-one recovery, not unknown real encipherment.',
                'Two copies of LP2p57 used in controls; absent from model training.',
                'Suffix is unused for this run selection but has been inspected in previous sessions.',
                'Only retained family winners inspected; small model can misrank solutions.'],
        sections=[], candidate_settings=0)
    for i, source in enumerate('%'.join(lp[:56]).split('$')):
        if len(vals(source)) <= 100: continue
        result = probe(source)
        out['candidate_settings'] += result['settings']
        for row in result['leaders']: row.pop('values')
        out['sections'].append(dict(section=i, **result))
        top = result['leaders'][0]
        print(i, result['settings'], top['key'], round(top['prefix_score'],4),
              round(top['suffix_score'],4), top['text'][:110], flush=True)
    out['input_sha256'] = hashlib.sha256((ROOT/'liber-primus__transcription--master.txt').read_bytes()).hexdigest()
    (ROOT/'literary_key_results.json').write_text(json.dumps(out, indent=2, ensure_ascii=False))
    print('Controls', len(controls), 'all rank-one exact; settings',out['candidate_settings'])

if __name__ == '__main__': main()
