"""Verify base60 bytes, then test them as a cyclic rune key.
This is a hypothesis test, not a claim the bytes are a key.
"""
import json,re,hashlib
from lp_core import *

ALPH='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx'
tokens=[]
for p in [49,50,51]:tokens+=re.findall(r'(?<![A-Za-z0-9])[0-4][0-9A-Za-x](?![A-Za-z0-9])',lp[p])
blob=bytes(60*ALPH.index(t[0])+ALPH.index(t[1]) for t in tokens)
reference=bytes.fromhex(re.findall(r'^[0-9A-F]{512}$',(ROOT/'byte-strings.txt').read_text(),re.M)[0])
assert len(blob)==256 and blob==reference
matrix=np.array(list(blob)).reshape(32,8)
keys={}
for transpose in (False,True):
    for revrow in (False,True):
        for revcol in (False,True):
            m=matrix.T if transpose else matrix
            if revrow:m=m[::-1,:]
            if revcol:m=m[:,::-1]
            keys[f'transpose_{transpose}_reverse_rows_{revrow}_reverse_cols_{revcol}']=m.ravel()%29

def sweep(text,label):
    c=vals(text);cut=int(0.6*len(c));out=[];count=0
    for resetname,reset in [('continuous',''),('page','%'),('paragraph','&$')]:
        for skip in (False,True):
            ix,_=positions(text,reset,skip_f=skip)
            for keyname,key in keys.items():
                shifted=key[(np.arange(256)[:,None]+ix[None,:])%256]
                for a in (1,-1):
                    for sign in (-1,1):
                        d=(a*c[None,:]+sign*shifted)%29
                        if skip:d[:,c==0]=0
                        prefix=scores(d[:,:cut]);j=int(np.argmax(prefix))
                        out.append({'target':label,'key_traversal':keyname,'reset':resetname,'skip_all_cipher_F':skip,'a':a,'sign':sign,'offset':j,'prefix_score':float(prefix[j]),'suffix_score':float(scores(d[j:j+1,cut:])[0]),'text':render(text,d[j])})
                        count+=256
    return sorted(out,key=lambda x:x['prefix_score'],reverse=True)[:5],count

if __name__=='__main__':
    # Synthetic independent positive control, using held-out known plaintext.
    text=lp[57];plain=vals(text);key=list(keys.values())[0]
    cipher=(plain+key[(np.arange(len(plain))+17)%256])%29
    it=iter(cipher);test=''.join(R[int(next(it))] if c in R else c for c in text)
    top,_=sweep(test,'synthetic_byte_key_control')
    assert top[0]['text']==render(text,plain)
    out={'blob_size':len(blob),'unique_byte_values':len(set(blob)),
         'sha256':hashlib.sha256(blob).hexdigest(),'archive_hex_match':True,
         'control_exact_recovery':True,'candidate_count':0,'sections':[],
         'limits':['Assumes the 256-byte block is a cyclic additive key modulo 29, which is unproven.','Eight rectangular matrix traversals, three reset rules, all cyclic offsets; no nonlinear key expansion tested.','Prefix-selected candidates, suffix not used for parameter selection.','Base60 master agrees exactly with separate hexadecimal listing; older decimal tables contain inconsistencies.']}
    for i,text in enumerate('%'.join(lp[:56]).split('$')):
        if len(vals(text))<60:continue
        top,n=sweep(text,f'marked_section_{i}');out['candidate_count']+=n
        out['sections'].append({'section_index':i,'top_prefix_candidates':top})
        print('Byte key section',i,'prefix',top[0]['prefix_score'],'suffix',top[0]['suffix_score'],flush=True)
    (ROOT/'byte_key_results.json').write_text(json.dumps(out,ensure_ascii=False,indent=2))
    (ROOT/'base60_verified.bin').write_bytes(blob)
    print('Byte key candidates',out['candidate_count'],flush=True)
