totient_constant.py

9.5 kB · python · 338 lines

1from decimal import Decimal, getcontext2from fractions import Fraction3from math import comb, gcd, isqrt45getcontext().prec = 5067BOUNDS = [50, 200, 800, 1200, 2000, 3200, 12800, 51200, 102400]8SET_BOUNDS = [50, 200, 800]9PUBLISHED = {10    "Q(i)": {50: 672, 200: 10608, 800: 168088},11    "Q(sqrt -3)": {50: 630, 200: 9606, 800: 151020, 1200: 337026, 2000: 945486, 3200: 2419950},12}13FIELDS = [14    {"name": "Q(i)", "ram": 2, "mod": 4, "w": 4, "disc": 4, "D": -4},15    {"name": "Q(sqrt -3)", "ram": 3, "mod": 3, "w": 6, "disc": 3, "D": -3},16]17DISCS = [-4, -3, -20, -23]18EULER_M = 6019EULER_J = 102021def bernoulli(m):22    b = [Fraction(0)] * (m + 1)23    b[0] = Fraction(1)24    for n in range(1, m + 1):25        s = sum(comb(n + 1, k) * b[k] for k in range(n))26        b[n] = -s / (n + 1)27    return b2829def dec(f):30    return Decimal(f.numerator) / Decimal(f.denominator)3132def hurwitz2(a, bern):33    base = dec(a)34    s = sum(1 / ((base + k) * (base + k)) for k in range(EULER_M))35    x = base + EULER_M36    s += 1 / x + 1 / (2 * x * x)37    for j in range(1, EULER_J + 1):38        s += dec(bern[2 * j]) / x ** (2 * j + 1)39    return s4041def arctan_inv(n):42    total = Decimal(0)43    term = Decimal(1) / Decimal(n)44    sq = Decimal(n) * Decimal(n)45    k = 046    while True:47        t = term / (2 * k + 1)48        if t < Decimal(10) ** -46:49            return total50        total += t if k % 2 == 0 else -t51        term /= sq52        k += 15354def dpi():55    return 16 * arctan_inv(5) - 4 * arctan_inv(239)5657def jacobi(a, n):58    a %= n59    r = 160    while a:61        while a % 2 == 0:62            a //= 263            if n % 8 in (3, 5):64                r = -r65        a, n = n, a66        if a % 4 == 3 and n % 4 == 3:67            r = -r68        a %= n69    return r if n == 1 else 07071def kron(d, n):72    if n == 0:73        return 074    r = 175    while n % 2 == 0:76        if d % 2 == 0:77            return 078        n //= 279        r *= 1 if d % 8 in (1, 7) else -180    return r * jacobi(d % n, n) if n > 1 else r8182def units(d):83    return 4 if d == -4 else (6 if d == -3 else 2)8485def class_number(d):86    q = -d87    return units(d) * -sum(kron(d, a) * a for a in range(1, q)) // (2 * q)8889def lvalue(d, bern):90    q = -d91    s = sum(kron(d, a) * hurwitz2(Fraction(a, q), bern) for a in range(1, q))92    return s / (q * q)9394def spf_sieve(n):95    spf = list(range(n + 1))96    for p in range(2, isqrt(n) + 1):97        if spf[p] == p:98            for m in range(p * p, n + 1, p):99                if spf[m] == m:100                    spf[m] = p101    return spf102103def factor(n, spf):104    out = []105    while n > 1:106        p = spf[n]107        e = 0108        while n % p == 0:109            n //= p110            e += 1111        out.append((p, e))112    return out113114def kind(fld, p):115    if p == fld["ram"]:116        return "r"117    return "s" if p % fld["mod"] == 1 else "i"118119def mulc(fld, u, v):120    x, y = u121    c, d = v122    if fld["ram"] == 2:123        return (x * c - y * d, x * d + y * c)124    return (x * c - y * d, x * d + y * c - y * d)125126def conjc(fld, z):127    a, b = z128    return (a, -b) if fld["ram"] == 2 else (a - b, -b)129130def nrm(fld, z):131    a, b = z132    return a * a + b * b if fld["ram"] == 2 else a * a - a * b + b * b133134def classes(fld, m):135    if fld["ram"] == 2:136        return [(a, b) for a in range(1, isqrt(m) + 1) for b in range(isqrt(m - a * a) + 1)]137    r = isqrt(4 * m // 3) + 1138    out = []139    for a in range(-r, r + 1):140        for b in range(-r, r + 1):141            n = a * a - a * b + b * b142            if 0 < n <= m:143                z = (a, b)144                best = z145                for _ in range(3):146                    z = (-z[1], z[0] - z[1])147                    best = min(best, z, (-z[0], -z[1]))148                if best == (a, b):149                    out.append((a, b))150    return out151152def totient(fld, z, n, spf):153    v = n154    a, b = z155    for p, e in factor(n, spf):156        k = kind(fld, p)157        if k == "r":158            v = v // p * (p - 1)159        elif k == "i":160            v = v // (p * p) * (p * p - 1)161        else:162            v = v // p * (p - 1)163            if a % p == 0 and b % p == 0:164                v = v // p * (p - 1)165    return v166167def mobius(fld, z, n, spf):168    v = 1169    a, b = z170    for p, e in factor(n, spf):171        k = kind(fld, p)172        if k == "r":173            if e != 1:174                return 0175            v = -v176        elif k == "i":177            if e != 2:178                return 0179            v = -v180        else:181            if e == 1:182                v = -v183            elif e == 2 and a % p == 0 and b % p == 0:184                pass185            else:186                return 0187    return v188189def tables(fld, m, spf):190    cls = classes(fld, m)191    phi = [0] * (m + 1)192    nsum = [0] * (m + 1)193    mob = [0] * (m + 1)194    for z in cls:195        n = nrm(fld, z)196        phi[n] += totient(fld, z, n, spf)197        nsum[n] += n198        mob[n] += mobius(fld, z, n, spf)199    for n in range(1, m + 1):200        phi[n] += phi[n - 1]201        nsum[n] += nsum[n - 1]202    return cls, phi, nsum, mob203204def norm_arrays(d, m):205    chi = [kron(d, n) for n in range(m + 1)]206    ideals = [0] * (m + 1)207    for k in range(1, m + 1):208        if chi[k]:209            for n in range(k, m + 1, k):210                ideals[n] += chi[k]211    prime = [True] * (m + 1)212    prime[0] = prime[1] = False213    for k in range(2, isqrt(m) + 1):214        if prime[k]:215            for n in range(k * k, m + 1, k):216                prime[n] = False217    mu = [1] * (m + 1)218    sq = [True] * (m + 1)219    for k in range(2, m + 1):220        if prime[k]:221            for n in range(k, m + 1, k):222                mu[n] = -mu[n]223            for n in range(k * k, m + 1, k * k):224                sq[n] = False225    mk = [0] * (m + 1)226    for k in range(1, m + 1):227        if sq[k] and mu[k]:228            for j in range(1, m // k + 1):229                if sq[j] and chi[j]:230                    mk[k * j] += mu[k] * mu[j] * chi[j]231    tt = [0] * (m + 1)232    for n in range(1, m + 1):233        tt[n] = tt[n - 1] + n * ideals[n]234    return mk, tt235236def norm_totient_sum(n, mk, tt):237    return sum(mk[k] * tt[n // k] for k in range(1, n + 1) if mk[k])238239def totient_sum(phi, m):240    return phi[m]241242def convolution_sum(m, nsum, mob):243    return sum(mob[n] * nsum[m // n] for n in range(1, m + 1) if mob[n])244245def hnf(r1, r2):246    a1, b1 = r1247    a2, b2 = r2248    x, y, d = 1, 0, a1249    u, v, e = 0, 1, a2250    while e:251        q = d // e252        d, e = e, d - q * e253        x, u = u, x - q * u254        y, v = v, y - q * v255    if d < 0:256        d, x, y = -d, -x, -y257    return d, x * b1 + y * b2, abs((a2 // d) * b1 - (a1 // d) * b2)258259def farey_set(fld, m):260    seen = set()261    for z in classes(fld, m):262        n = nrm(fld, z)263        r1 = mulc(fld, (1, 0), z)264        r2 = mulc(fld, (0, 1), z)265        h11, h12, h22 = hnf(r1, r2)266        cj = conjc(fld, z)267        for x in range(h11):268            for y in range(h22):269                u, v = mulc(fld, (x, y), cj)270                u %= n271                v %= n272                g = gcd(gcd(u, v), n)273                seen.add((u // g, v // g, n // g))274    return len(seen)275276def d12(x):277    return str(+x.quantize(Decimal("1.000000000000")))278279def main():280    bern = bernoulli(2 * EULER_J)281    pi = dpi()282    z2 = pi * pi / 6283    lv = {d: lvalue(d, bern) for d in DISCS}284    print("pi", d12(pi))285    print("zeta(2)", d12(z2))286    print("L(2, chi_-4) Catalan", d12(lv[-4]))287    print("L(2, chi_-3)", d12(lv[-3]))288    const = {}289    for fld in FIELDS:290        dk = fld["disc"]291        zk = z2 * lv[fld["D"]]292        rho = 2 * pi / (fld["w"] * Decimal(dk).sqrt())293        c = rho / (2 * zk)294        const[fld["name"]] = c295        print("field", fld["name"], "w", fld["w"], "|D|", dk)296        print("  zeta_K(2)", d12(zk))297        print("  rho_K", d12(rho))298        print("  c = rho_K/(2 zeta_K(2))", d12(c))299        print("  Sayous c_K = pi/(sqrt|D| zeta_K(2))", d12(pi / (Decimal(dk).sqrt() * zk)), "= w c", d12(fld["w"] * c))300    spf = spf_sieve(max(BOUNDS))301    byclass = {}302    for fld in FIELDS:303        name = fld["name"]304        c = const[name]305        cls, phi, nsum, mob = tables(fld, max(BOUNDS), spf)306        byclass[fld["D"]] = phi307        print("field", name, "classes to", max(BOUNDS), len(cls))308        for m in BOUNDS:309            s = totient_sum(phi, m)310            dn = Decimal(m)311            main_term = c * dn * dn312            ratio = Decimal(s) / main_term313            dev = Decimal(s) - main_term314            mark = PUBLISHED[name].get(m)315            tag = "published " + str(mark) + " match " + str(mark == s) if mark else "new"316            print("  N", m, "count", s, "count/(c N^2)", d12(ratio), "dev/N^1.5", d12(dev / (dn * dn.sqrt())), "dev/(N ln N)", d12(dev / (dn * dn.ln())), tag)317        bad = [m for m in BOUNDS if convolution_sum(m, nsum, mob) != totient_sum(phi, m)]318        print("  convolution identity mismatches", len(bad))319        for m in SET_BOUNDS:320            k = farey_set(fld, m)321            print("  Farey set size at N", m, k, "equals totient sum", k == totient_sum(phi, m))322    mx = max(BOUNDS)323    for d in DISCS:324        h = class_number(d)325        w = units(d)326        zk = z2 * lv[d]327        rho = 2 * pi * h / (w * Decimal(-d).sqrt())328        c = rho / (2 * zk)329        mk, tt = norm_arrays(d, mx)330        print("discriminant", d, "h", h, "w", w, "L(2, chi_D)", d12(lv[d]), "rho_K", d12(rho), "c", d12(c))331        for m in BOUNDS:332            s = norm_totient_sum(m, mk, tt)333            dn = Decimal(m)334            main_term = c * dn * dn335            tail = " class route " + str(s == totient_sum(byclass[d], m)) if d in byclass else ""336            print("  N", m, "count", s, "count/(c N^2)", d12(s / main_term), "dev/N^1.5", d12((s - main_term) / (dn * dn.sqrt())) + tail)337338main()