"""Authored bounded directed graph SCC partition and independent reachability oracle."""
from collections import deque
import itertools,json,platform
def solve(nodes,edges):
 if len(nodes)>8 or len(set(nodes))!=len(nodes) or len(edges)>64 or any(a not in nodes or b not in nodes for a,b in edges):raise ValueError('bounded graph')
 adj={x:[] for x in nodes};rev={x:[] for x in nodes}
 for a,b in edges:adj[a].append(b);rev[b].append(a)
 seen=set();post=[]
 def finish(v):
  seen.add(v)
  for u in rev[v]:
   if u not in seen:finish(u)
  post.append(v)
 for v in nodes:
  if v not in seen:finish(v)
 seen=set();groups=[]
 def collect(v,bag):
  seen.add(v);bag.append(v)
  for u in adj[v]:
   if u not in seen:collect(u,bag)
 for v in reversed(post):
  if v not in seen:bag=[];collect(v,bag);groups.append(sorted(bag))
 groups=sorted(groups);owner={v:i for i,g in enumerate(groups) for v in g};compressed=sorted({(owner[a],owner[b]) for a,b in edges if owner[a]!=owner[b]})
 reach={}
 for start in nodes:
  r={start};q=deque([start])
  while q:
   for u in adj[q.popleft()]:
    if u not in r:r.add(u);q.append(u)
  reach[start]=r
 checks=0
 for a,b in itertools.product(nodes,repeat=2):assert (owner[a]==owner[b])==(b in reach[a] and a in reach[b]);checks+=1
 assert sorted(v for g in groups for v in g)==sorted(nodes)
 indeg=[0]*len(groups)
 for a,b in compressed:indeg[b]+=1
 q=deque(i for i,d in enumerate(indeg) if d==0);order=[]
 while q:
  a=q.popleft();order.append(a)
  for u,v in compressed:
   if u==a:
    indeg[v]-=1
    if indeg[v]==0:q.append(v)
 assert len(order)==len(groups)
 return {'groups':groups,'condensation_edges':[list(e) for e in compressed],'component_topological_order':order,'pair_oracle_checks':checks,'singleton_self_loops':[a for a,b in edges if a==b]}
def demo():
 nodes=list('ABCDEU');edges=[('A','B'),('B','A'),('B','C'),('C','D'),('D','C'),('D','E')];main=solve(nodes,edges);assert main['groups']==[['A','B'],['C','D'],['E'],['U']]
 loop=solve(['X','U'],[('X','X')]);assert loop['groups']==[['U'],['X']]
 repeated=solve(nodes,edges+[('B','C')]);assert repeated['groups']==main['groups'] and repeated['condensation_edges']==main['condensation_edges']
 assert solve([],[])['groups']==[]
 possible=list(itertools.product('ABC',repeat=2));count=0
 for mask in range(1<<len(possible)):solve(list('ABC'),[e for i,e in enumerate(possible) if mask>>i&1]);count+=1
 return {'runtime':platform.python_version(),'nodes':nodes,'edges':[list(e) for e in edges],'main':main,'repeated_bridge_condensation_unchanged':True,'singleton_loop_and_isolate':loop,'all_three_vertex_directed_graphs_checked':count,'scope':'Bounded authored recursive SCC and BFS reachability oracle; numeric component labels normalized for display, not universal identity. No Java/NetworkX run or automatic cycle repair.'}
if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=True,indent=2))
