back to table · edit · history · where entries came from · files · download
3070 bytes, as of the version from 2026-11-10 22:53 (current). Recorded here, not run.
"""T710: minimal polynomials of Bettin–Conrey cotangent sums.
Requires SageMath (tested with 10.9) and numberdb (tested with 0.1.10).
Install numberdb with sage -pip install numberdb.
sage -python generate.py # preview, no writes
sage -python generate.py --verify # compare all stored entries
sage -python generate.py --publish # explicitly write to the target
Configure your own API key and session attribution externally for writes.
For k >= 2 compute alpha_k=c_0(1/k) in Q(zeta_lcm(4,k)), using the
positive-angle embedding zeta_L=exp(2*pi*i/L). Clear denominators of its
monic Q-minimal polynomial, divide the coefficient gcd, and choose positive
leading coefficient. This does not rescale alpha_k. Default coverage is all
3 <= k <= 24; k=2 is the omitted zero. All arithmetic is exact, with no
floating input, decimal recognition or polynomial fitting.
Bettin and Conrey, Period functions and cotangent sums (2013), pp. 217,223:
https://doi.org/10.2140/ant.2013.7.215 .
The companion independent_check.py uses the finite Estermann double sum,
products over Galois embeddings, and 512-bit direct cotangent enclosures.
"""
import argparse
from functools import lru_cache
import numberdb.sage as numberdb
from sage.rings.integer_ring import ZZ
from sage.rings.rational_field import QQ
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.number_field.number_field import CyclotomicField
ZX = PolynomialRing(ZZ, 'x')
@lru_cache(maxsize=None)
def algebraic_sum(k):
k = ZZ(k)
if k < 2:
raise ValueError('k must be an integer at least 2')
L = k.lcm(4)
K = CyclotomicField(L)
z = K.gen()
w, ii = z**(L//k), z**(L//4)
return -sum((QQ(m)/k)*ii*(w**m+1)/(w**m-1)
for m in range(1, int(k)))
@lru_cache(maxsize=None)
def polynomial(k):
a = algebraic_sum(k)
q = a.minpoly()
p = ZX(q*q.denominator())
p = ZX(p/p.content())
if p.leading_coefficient() < 0:
p = -p
assert p(a) == 0 and p.is_irreducible()
assert p.content() == 1
assert all(type(c).__name__ == 'Integer' for c in p.list())
return p
class BettinConreyMinimalPolynomials(numberdb.Generator):
table = 'T710'
parameters = ('k',)
type = 'Z[]'
digits = 100
rigour = 'exact'
def enumerate(self, kmax=24):
for k in range(3, int(kmax)+1):
yield {'k': k}
def value(self, params, digits):
return polynomial(ZZ(params['k']))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
mode = parser.add_mutually_exclusive_group()
mode.add_argument('--publish', action='store_true')
mode.add_argument('--verify', action='store_true')
parser.add_argument('--kmax', type=int, default=24)
args = parser.parse_args()
g = BettinConreyMinimalPolynomials()
if args.publish:
print(g.publish(kmax=args.kmax, removing=False))
elif args.verify:
print(g.verify(sample=None, kmax=args.kmax))
else:
print(g.preview(kmax=args.kmax))