from pathlib import Path
import re, math, cmath
import mpmath as mp

HERE=Path(__file__).resolve().parent
PAPER=HERE/"CSM_RH_Paper_83_Principal_Character_Projectors_for_Shifted_Prime_Factor_States_v0.1_2026-09-09.md"
mp.mp.dps=60

def units(Q):
    return [r for r in range(Q) if math.gcd(r,Q)==1]

def rough_density(z_primes,a):
    num=1.0
    for p in z_primes:
        if a%p!=0:
            num*= (p-2)/(p-1)
    return num

def rough_density_bruteforce(primes,a):
    Q=1
    for p in primes: Q*=p
    U=units(Q)
    good=sum(1 for r in U if math.gcd(r+a,Q)==1)
    return good/len(U)

def check_roughness():
    cases=[([2,3,5],2),([2,3,5],6),([3,5,7],2),([3,5,7,11],30)]
    vals=[]
    for ps,a in cases:
        x=rough_density(ps,a)
        y=rough_density_bruteforce(ps,a)
        assert abs(x-y)<1e-14,(ps,a,x,y)
        vals.append((ps,a,x))
    return vals

def principal_L_logderivative(Q,s):
    # L(s,chi0)=zeta(s) prod_{p|Q}(1-p^-s)
    z=mp.zeta(s)
    zp=mp.diff(mp.zeta,s)
    val=-zp/z
    x=Q
    p=2
    facs=[]
    while p*p<=x:
        if x%p==0:
            facs.append(p)
            while x%p==0:x//=p
        p+=1
    if x>1:facs.append(x)
    for p in facs:
        val -= (mp.log(p)*p**(-s))/(1-p**(-s))
    return val

def check_principal_residue():
    rho=mp.zetazero(1)
    vals=[]
    for Q in [2,6,30]:
        for k in [6,8,10]:
            eps=mp.mpf(10)**(-k)
            r=eps*principal_L_logderivative(Q,rho+eps)
            vals.append((Q,k,complex(r)))
        assert abs(vals[-1][2]+1)<1e-6
    return vals[-6:]

def check_partition_rank_one():
    # Example Q=5, a=1, classes by shifted residue parity-like grouping.
    Q=5; a=1
    U=units(Q)
    # 3 classes partition all reduced r.
    gs=[]
    for j in range(3):
        gs.append({r:(1 if ((r+a)%Q)%3==j else 0) for r in U})
    pis=[sum(g[r] for r in U)/len(U) for g in gs]
    assert abs(sum(pis)-1)<1e-14
    # choose an orthogonal vector c dot pi=0
    c=[pis[1],-pis[0],0.0]
    dot=sum(ci*pi for ci,pi in zip(c,pis))
    assert abs(dot)<1e-14
    return pis,c,dot

def check_divisor_periodicity():
    lambdas={2:1.3,3:-0.7,5:0.2}
    Q=30
    def g(n):
        return sum(v for d,v in lambdas.items() if n%d==0)
    for n in range(1,100):
        assert abs(g(n)-g(n+Q))<1e-15
    return True

def check_source():
    s=PAPER.read_text(encoding="utf-8")
    for pat in [r"(?<!\\)\\\(",r"(?<!\\)\\\)",r"(?<!\\)\\\[",r"(?<!\\)\\\]"]:
        assert re.search(pat,s) is None
    assert s.count("$$")%2==0
    tmp=re.sub(r"\$\$.*?\$\$","",s,flags=re.S)
    assert len(re.findall(r"(?<!\\)\$",tmp))%2==0
    return True

if __name__=="__main__":
    print("roughness",check_roughness())
    print("principal_residue",check_principal_residue())
    print("partition_rank_one",check_partition_rank_one())
    print("divisor_periodicity",check_divisor_periodicity())
    print("source_delimiters",check_source())
    print("PASS")
