checks.py

back to table · edit · history · where entries came from · files · download

7585 bytes, as of the version from 2026-11-02 02:40 (current). Recorded here, not run.

"""Independent checks for Zeta polynomials of Fermat curves -- numberdb.org/T666.

Run: sage -python checks.py
Use --stored to check authenticated database values as well. For agents/sage.sh,
NUMBERDB_KEY_FROM_STDIN=1 reads the key and also selects stored checks.

The alternative polynomial uses additive Gauss sums in Q(zeta_(Np)), a
separately selected primitive root, the identity
J(a,b)=tau(a)tau(b)tau(-a-b)/(chi^(a+b)(-1)*p), and explicit coefficient
convolution. It shares no character-sum or multiplication routine with the
generator. Direct projective counts use only finite-field arithmetic. Counts
through degree two test the first two power sums, not every coefficient;
the full independent Gaussian computation tests all coefficients.
"""
import os
import sys
import json
import hashlib
import platform
from collections import Counter
from pathlib import Path
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.number_field.number_field import CyclotomicField
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.env import SAGE_VERSION
from generate import FermatZeta, character_sums
from check import exactness, measure, agrees_with, names_its_rings

R = PolynomialRing(ZZ, 'T')
T = R.gen()


def gaussian_polynomial(N, p):
    """Entire polynomial from Gauss sums, without evaluating Jacobi sums."""
    K = CyclotomicField(N*p, 'w')
    w = K.gen()
    # Select the largest primitive root; generator.py uses the smallest.
    for h in range(p-1, 1, -1):
        elements = [pow(h, k, p) for k in range(p-1)]
        if len(set(elements)) == p-1:
            break
    taus = {}
    for a in range(1, N):
        taus[a] = sum((w**((p*a*k + N*x) % (N*p))
                       for k,x in enumerate(elements)), K(0))
    coefficients = [K(1)]
    for a in range(1, N):
        for b in range(1, N):
            c = (a+b) % N
            if c == 0:
                continue
            sign = (-1)**(c*((p-1)//N))
            J = taus[a]*taus[b]*taus[N-c]*QQ(sign)/QQ(p)
            updated = [K(0)] * (len(coefficients)+1)
            for k, coefficient in enumerate(coefficients):
                updated[k] += coefficient
                updated[k+1] += coefficient*J
            coefficients = updated
    return R([ZZ(c) for c in coefficients])


def point_count_prime(N, p):
    frequencies = Counter(pow(x,N,p) for x in range(p))
    affine = sum(count*frequencies.get((1-u) % p,0) for u,count in frequencies.items())
    infinity = sum(pow(x,N,p) == p-1 for x in range(p))
    return affine+infinity


def point_count_square(N, p):
    """Fp2 = Fp[u]/(u^2-d), with d the smallest quadratic nonresidue."""
    d = next(d for d in range(2,p) if pow(d,(p-1)//2,p) == p-1)
    def mul(x,y):
        a,b=x; c,e=y
        return ((a*c+d*b*e) % p, (a*e+b*c) % p)
    def power(x,n):
        out=(1,0)
        while n:
            if n&1: out=mul(out,x)
            x=mul(x,x); n//=2
        return out
    frequencies=Counter(power((a,b),N) for a in range(p) for b in range(p))
    affine=sum(count*frequencies.get(((1-a) % p,(-b) % p),0)
               for (a,b),count in frequencies.items())
    infinity=frequencies.get((p-1,0),0)
    return affine+infinity


def elliptic_count(p):
    # The standard Fermat cubic birational model v^2=u^3-432.
    squares=Counter(y*y % p for y in range(p))
    return 1+sum(squares.get((x*x*x-432) % p,0) for x in range(p))


def structural_checks(N,p,P):
    genus=(N-1)*(N-2)//2
    assert P.degree()==2*genus and P[0]==1 and P[2*genus]==p**genus
    for k in range(genus+1):
        assert P[2*genus-k] == p**(genus-k)*P[k], (N,p,'reciprocity',k)
    K, sums=character_sums(N,p)
    z=K.gen()
    for (a,b),J in sums.items():
        assert J*sums[N-a,N-b]==p
        assert J==sums[b,a]
    n1=point_count_prime(N,p)
    n2=point_count_square(N,p)
    assert n1==p+1+P[1], (N,p,'point count Fp')
    assert n2==p*p+1-P[1]**2+2*P[2], (N,p,'point count Fp2')
    if N==3: assert n1==elliptic_count(p)
    return {'N':N,'p':p,'genus':genus,'points_Fp':n1,'points_Fp2':n2,'coefficients':[int(c) for c in P]}


def run(stored=False):
    # Nonzero controls precede use of both independent methods.
    assert point_count_prime(1,7)==8
    assert point_count_square(1,7)==50
    assert point_count_prime(2,7)==8
    assert point_count_square(2,7)==50
    assert point_count_prime(3,7)==9
    assert point_count_square(3,7)==63
    assert gaussian_polynomial(3,7)==1+T+7*T*T
    assert gaussian_polynomial(4,5)==(1-2*T+5*T*T)*(1+2*T+5*T*T)**2
    print('CONTROLS: projective lines, conics, cubic (3,7), quartic (4,5): passed',flush=True)
    generator=FermatZeta()
    values={(int(a['N']),int(a['p'])):generator.value(a,100) for a in generator.enumerate()}
    assert not exactness(values)
    assert not names_its_rings(str(Path(__file__).with_name('generate.py')))
    # Enumerate primes independently of generate.py.
    selected={(N,p) for N in range(3,9) for p in range(2,102)
              if all(p%d for d in range(2,p)) and (p-1)%N==0}
    assert set(values)==selected
    alternate={}
    evidence=[]
    for (N,p),P in values.items():
        evidence.append(structural_checks(N,p,P))
        alternate[N,p]=gaussian_polynomial(N,p)
        assert alternate[N,p]==P, (N,p,'Gaussian polynomial')
        print('CHECKED',N,p,'degree',P.degree(),'characters',len(str(P)),flush=True)
    assert not agrees_with(values,lambda key:alternate[key])
    for N,p in values:
        for d in range(3,N):
            if N%d==0:
                assert values[N,p] % values[d,p] == 0
    readback=[]
    if stored:
        doc=numberdb.table('T666')
        block=doc['Numbers']
        if isinstance(block,list):
            records=block
        else:
            records=[]
            for N,group in block.items():
                group=group.get('numbers',group)
                for p,entry in group.items():
                    records.append({'params':{'N':N,'p':p}, 'number':entry['number'] if isinstance(entry,dict) else entry})
        assert [(str(r['params']['N']),str(r['params']['p'])) for r in records] == [(str(N),str(p)) for N,p in values]
        for entry in records:
            params=entry['params']
            key=(int(params['N']),int(params['p']))
            P=R(entry['number'])
            assert key in alternate and P==alternate[key]
            readback.append(structural_checks(*key,P))
        assert len(readback)==len(values)
        verification=generator.verify(sample=None)
        print(verification)
        assert verification.ok
    report={'table':'T666','rows':len(values),'measurement':measure(values),
            'controls':'Passed lines, conics, cubic and quartic exact nonzero controls',
            'full_gauss_sum_polynomial_comparisons':len(values),
            'point_count_prime_and_square_comparisons':len(values),
            'stored_rows_checked':len(readback),'records':evidence,
            'versions':{'python':platform.python_version(),'sage':SAGE_VERSION,'numberdb':numberdb.__version__},
            'skill_manifest':'agents/runs/20261102T021426Z-build-skills/manifest.json',
            'skill_sha256':'a2b28d9953264d7b0ca62663785c2952190c5be6d6d3597fd695d9a54d606278'}
    print('EVIDENCE_JSON '+json.dumps(report,sort_keys=True),flush=True)


if __name__=='__main__':
    stored='--stored' in sys.argv
    if os.environ.get('NUMBERDB_KEY_FROM_STDIN')=='1':
        os.environ['NUMBERDB_API_KEY']=sys.stdin.read().strip(); stored=True
    run(stored=stored)