This site is developed to XHTML and CSS2 W3C standards. If you see this paragraph, your browser does not support those standards and you need to upgrade. Visit WaSP for a variety of options.

php pastebin - collaborative irc debugging view php source

Paste #96

Posted by: Pi
Posted on: 2026-11-03 08:20:40
Age: 4 days ago
Views: 6
import sys
import time
import numpy as np
from math import isqrt, log2, log10, ceil

SH = 96


def primes(m):
    s = np.ones(m + 1, dtype=bool)
    s[:2] = False
    for i in range(2, isqrt(m) + 1):
        if s[i]:
            s[i * i::i] = False
    return np.nonzero(s)[0].astype(np.int64)


def setup_euler(n, g):
    N = ceil((n + g + 5) * log2(10)) + 5
    pr = primes(2 * N + 1)
    pr = pr[(pr != 2) & (pr != 5)]
    big = pr * pr > 2 * N + pr
    return N, pr[~big], pr[big]


def adic_sum_euler(pr, n, N):
    w = len(pr)
    if w == 0:
        return 0
    Ea = np.array([max(1, sum(1 for j in range(1, 40) if int(a) ** j <= 2 * N + 1)) for a in pr], dtype=np.int64)
    qs = [int(a) ** int(e) for a, e in zip(pr, Ea)]
    assert max(qs) ** 2 < 2 ** 63
    q = np.array(qs, dtype=np.int64)
    Em = int(Ea.max())
    pt = np.ones((w, Em + 1), dtype=np.int64)
    for t in range(1, Em + 1):
        pt[:, t] = pt[:, t - 1] * pr % q
    c0 = np.array([2 * pow(10, n, qq) % qq for qq in qs], dtype=np.int64)
    num = np.ones(w, dtype=np.int64)
    den = num.copy()
    v = np.zeros(w, dtype=np.int64)
    A = np.zeros(w, dtype=np.int64)
    B = num.copy()
    ar = np.arange(w)
    for k in range(1, N + 1):
        m1 = (k % pr == 0)
        m2 = ((2 * k + 1) % pr == 0)
        if m1.any() or m2.any():
            kv = np.full(w, k, dtype=np.int64)
            sv = np.full(w, 2 * k + 1, dtype=np.int64)
            for i in np.nonzero(m1)[0]:
                a = int(pr[i])
                x = k
                while x % a == 0:
                    x //= a
                    v[i] += 1
                kv[i] = x
            for i in np.nonzero(m2)[0]:
                a = int(pr[i])
                x = 2 * k + 1
                while x % a == 0:
                    x //= a
                    v[i] -= 1
                sv[i] = x
            num = num * (kv % q) % q
            den = den * (sv % q) % q
        else:
            num = num * k % q
            den = den * (2 * k + 1) % q
        neg = v < 0
        if neg.any():
            e = np.where(neg, -v, 1)
            T = num * (c0 * pt[ar, Ea - e] % q) % q
            A = np.where(neg, (A * den + B * T) % q, A)
            B = np.where(neg, B * den % q, B)
    tot = 0
    for i in range(w):
        qq = qs[i]
        r = int(A[i]) * pow(int(B[i]), -1, qq) % qq
        tot += (r << SH) // qq
    return tot


def block_weights(a, M):
    num = 1
    den = 1
    fs = []
    for m in range(M + 1):
        fs.append(num * pow(den, -1, a) % a)
        num = num * (m + 1) % a
        den = den * (2 * m + 3) % a
    return sum(fs[:M]) % a, fs[M]


def euler_blocks(pr, n, N):
    if len(pr) == 0:
        return 0
    M = (N + 1) // pr
    J = N - M * pr
    h = (pr - 1) // 2
    act = J >= h
    L = np.where(act, pr - 1 - J, 0)
    order = np.argsort(-L, kind='stable')
    pa = pr[order]
    La = L[order]
    aa_act = act[order]
    Lmax = int(La[0])
    A = np.zeros(len(pa), dtype=np.int64)
    P = np.ones_like(A)
    Q = P.copy()
    cnt = len(La) - np.searchsorted(La[::-1], np.arange(Lmax), side='right')
    for i in range(Lmax):
        c = int(cnt[i])
        m = pa[:c]
        A[:c] = (A[:c] + P[:c]) * (i + 1) % m
        P[:c] = P[:c] * (2 * i + 1) % m
        Q[:c] = Q[:c] * (i + 1) % m
    tot = 0
    for idx in range(len(pa)):
        a = int(pa[idx])
        sg = -1 if ((a - 1) // 2) & 1 else 1
        F, fm = block_weights(a, (N + 1) // a)
        t = sg * F
        if aa_act[idx]:
            Pv = int(A[idx]) * pow(int(Q[idx]), -1, a) % a if La[idx] > 0 else 0
            t += fm * (sg - Pv)
        c = 2 * pow(10, n, a) * t % a
        tot += (c << SH) // a
    return tot


def digits_euler(n, g=14):
    N, small, big = setup_euler(n, g)
    tot = adic_sum_euler(small, n, N) + euler_blocks(big, n, N)
    return finish(tot, g)




R = log10(13.5)


def setup_g(n, g):
    N = ceil((n + g + 6) / R) + 3
    pr = primes(3 * N + 2)
    pr = pr[(pr != 2) & (pr != 5)]
    big = pr * pr > 3 * N + 2 * pr
    return N, pr[~big], pr[big]


def adic_sum_g(pr, n, N):
    w = len(pr)
    if w == 0:
        return 0
    Ea = np.array([max(1, sum(1 for j in range(1, 40) if int(a) ** j <= 3 * N + 2)) for a in pr], dtype=np.int64)
    qs = [int(a) ** int(e) for a, e in zip(pr, Ea)]
    assert max(qs) ** 2 < 2 ** 63
    q = np.array(qs, dtype=np.int64)
    Em = int(Ea.max())
    pt = np.ones((w, Em + 1), dtype=np.int64)
    for t in range(1, Em + 1):
        pt[:, t] = pt[:, t - 1] * pr % q
    c0 = np.array([pow(10, n, qq) for qq in qs], dtype=np.int64)
    num = np.ones(w, dtype=np.int64)
    den = num.copy()
    v = np.zeros(w, dtype=np.int64)
    A = np.zeros(w, dtype=np.int64)
    B = num.copy()
    ar = np.arange(w)

    def strip(x, mask, sign):
        out = np.full(w, x, dtype=np.int64)
        for i in np.nonzero(mask)[0]:
            a = int(pr[i])
            y = x
            while y % a == 0:
                y //= a
                v[i] += sign
            out[i] = y
        return out

    for k in range(1, N + 1):
        fs = [k, 2 * k - 1]
        ds = [3, 3 * k - 1, 3 * k - 2]
        v0 = v.copy()
        for x in fs:
            mk = (x % pr == 0)
            if mk.any():
                num = num * (strip(x, mk, 1) % q) % q
            else:
                num = num * x % q
        for x in ds:
            mk = (x % pr == 0)
            if mk.any():
                den = den * (strip(x, mk, -1) % q) % q
            else:
                den = den * x % q
        y = 50 * k - 6
        mk = (y % pr == 0)
        vt = v.copy()
        if mk.any():
            sv = np.full(w, y, dtype=np.int64)
            ext = np.zeros(w, dtype=np.int64)
            for i in np.nonzero(mk)[0]:
                a = int(pr[i]); z = y
                while z % a == 0:
                    z //= a; ext[i] += 1
                sv[i] = z
            vt = v + ext
            yv = sv % q
        else:
            yv = y % q
        neg = vt < 0
        if neg.any():
            e = np.where(neg, -vt, 1)
            T = num * yv % q * (c0 * pt[ar, Ea - e] % q) % q
            A = np.where(neg, (A * den + B * T) % q, A)
            B = np.where(neg, B * den % q, B)
    tot = 0
    for i in range(w):
        qq = qs[i]
        r = int(A[i]) * pow(int(B[i]), -1, qq) % qq
        tot += (r << SH) // qq
    return tot


def suffix_sums(pa, lens, kind):
    n_ = len(pa)
    out = np.zeros(n_, dtype=np.int64)
    if n_ == 0 or lens.max() <= 0:
        return out
    order = np.argsort(-lens, kind='stable')
    a = pa[order]
    L = lens[order]
    if kind == 1:
        h = (a - 1) // 2
        top = h.copy()
        P = np.array([(-int(x) * pow(2, -int(x), int(y))) % int(y) for x, y in zip(h, a)], dtype=np.int64)
    else:
        top = a - 1
        P = np.full(n_, 2, dtype=np.int64)
    Q = np.ones(n_, dtype=np.int64)
    A = np.zeros(n_, dtype=np.int64)
    Lm = int(L[0])
    cnt = n_ - np.searchsorted(L[::-1], np.arange(Lm), side='right')
    for s in range(Lm):
        c = int(cnt[s])
        m = a[:c]
        j = top[:c] - s
        A[:c] = (A[:c] + (50 * j - 6) % m * P[:c]) % m
        if kind == 1:
            d = 3 * j - m
            nu = (d % m) * ((d - 1) % m) % m * ((d - 2) % m) % m
            de = j * j % m * (2 * j - 1) % m
        else:
            d = 3 * j - 2 * m
            e2 = 2 * j - m
            nu = 2 * (d % m) % m * ((d - 1) % m) % m * ((d - 2) % m) % m
            de = j * (e2 % m) % m * ((e2 - 1) % m) % m
        P[:c] = P[:c] * nu % m
        Q[:c] = Q[:c] * de % m
        A[:c] = A[:c] * de % m
    res = np.zeros(n_, dtype=np.int64)
    for i in range(n_):
        mm = int(a[i])
        res[order[i]] = int(A[i]) * pow(int(Q[i]), -1, mm) % mm if L[i] > 0 else 0
    return res


THR = 150000


def gosper_blocks(pr, n, N):
    if len(pr) == 0:
        return 0
    M = (N + 1) // pr
    J = N - M * pr
    h = (pr - 1) // 2
    lo1 = (pr + 2) // 3
    lo2 = (2 * pr + 2) // 3
    len1 = np.where((J >= lo1) & (J < h), h - J, 0)
    len2 = np.where(J >= lo2, pr - 1 - J, 0)
    S1 = suffix_sums(pr, np.where(len1 > THR, 0, len1), 1)
    S2 = suffix_sums(pr, np.where(len2 > THR, 0, len2), 2)
    for i in np.nonzero(len1 > THR)[0]:
        S1[i] = tail_bgs(int(pr[i]), 1, int(len1[i]))
    for i in np.nonzero(len2 > THR)[0]:
        S2[i] = tail_bgs(int(pr[i]), 2, int(len2[i]))
    tot = 0
    for idx in range(len(pr)):
        a = int(pr[idx]); Mi = int(M[idx]); Ji = int(J[idx]); hi = int(h[idx])
        sg = -1 if hi & 1 else 1
        i2 = pow(2, -1, a)
        w1n, w1d, w2n, w2d, p2 = 1, 1, 1, 2, 1
        F = 0
        for m in range(Mi + 1):
            u1 = w1n * pow(w1d, -1, a) % a * p2 % a
            u2 = w2n * pow(w2d, -1, a) % a * p2 % a
            if m < Mi:
                F += u1 + u2
            w1n = w1n * (m + 1) * (2 * m + 1) * (2 * m + 2) % a
            w1d = w1d * (3 * m + 2) * (3 * m + 3) * (3 * m + 4) % a
            w2n = w2n * (m + 1) * (2 * m + 2) * (2 * m + 3) % a
            w2d = w2d * (3 * m + 3) * (3 * m + 4) * (3 * m + 5) % a
            p2 = p2 * i2 % a
        H1 = 0 if Ji < lo1[idx] else (-2 * sg if Ji >= hi else (-2 * sg - int(S1[idx])))
        H2 = 0 if Ji < lo2[idx] else (-2 * sg - int(S2[idx]))
        c = pow(10, n, a) * (2 * sg * F - u1 * H1 - u2 * H2) % a
        tot += (c << SH) // a
    return tot



def conv_mod(a, b, p):
    n = len(a) + len(b) - 1
    sz = 1 << (n - 1).bit_length()
    a0 = a & 32767; a1 = a >> 15
    b0 = b & 32767; b1 = b >> 15
    fa0 = np.fft.rfft(a0.astype(np.float64), sz); fa1 = np.fft.rfft(a1.astype(np.float64), sz)
    fb0 = np.fft.rfft(b0.astype(np.float64), sz); fb1 = np.fft.rfft(b1.astype(np.float64), sz)
    r00 = np.rint(np.fft.irfft(fa0 * fb0, sz)[:n]).astype(np.int64) % p
    r01 = np.rint(np.fft.irfft(fa0 * fb1 + fa1 * fb0, sz)[:n]).astype(np.int64) % p
    r11 = np.rint(np.fft.irfft(fa1 * fb1, sz)[:n]).astype(np.int64) % p
    return (r00 + (r01 << 15) % p + ((r11 << 15) % p << 15)) % p


def prefix_prod(x, p):
    x = x.copy()
    s = 1
    while s < len(x):
        x[s:] = x[s:] * x[:-s] % p
        s <<= 1
    return x


def batch_inv(x, p):
    pre = prefix_prod(x, p)
    inv_all = pow(int(pre[-1]), -1, p)
    suf = prefix_prod(x[::-1].copy(), p)[::-1]
    out = np.empty_like(x)
    out[0] = inv_all * (suf[1] if len(x) > 1 else 1) % p
    out[1:-1] = pre[:-2] * suf[2:] % p * inv_all % p if len(x) > 2 else out[1:-1]
    if len(x) > 1:
        out[-1] = pre[-2] * inv_all % p
    return out


class Ctx:
    def __init__(self, K, p):
        self.K = K
        self.p = p
        f = [1] * (K + 1)
        for i in range(1, K + 1):
            f[i] = f[i - 1] * i % p
        inv = [1] * (K + 1)
        inv[K] = pow(f[K], -1, p)
        for i in range(K, 0, -1):
            inv[i - 1] = inv[i] * i % p
        self.delta = np.array([(inv[k] * inv[K - k]) % p * (1 if (K - k) % 2 == 0 else p - 1) % p for k in range(K + 1)], dtype=np.int64)


def shift(vals, a, ctx):
    K, p = ctx.K, ctx.p
    t = np.arange(-K, K + 1, dtype=np.int64)
    base = (a + t) % p
    if (base == 0).any():
        return None
    e = batch_inv(base, p)
    c = vals * ctx.delta % p
    full = conv_mod(c, e, p)[K:2 * K + 1]
    i = np.arange(K + 1)
    pre0 = 1
    for l in range(K + 1):
        pre0 = pre0 * ((a - l) % p) % p
    ratio_num = (a + i[:-1] + 1) % p
    ratio_den_inv = e[i[:-1]]
    steps = ratio_num * ratio_den_inv % p
    prefix = np.concatenate([[pre0], steps])
    pre = prefix_prod(prefix, p)
    return pre * full % p


def make_steps(a, kind):
    if kind == 1:
        def f(j):
            d = 3 * j - a
            num = (d % a) * ((d - 1) % a) % a * ((d - 2) % a) % a
            den = (j % a) * (j % a) % a * ((2 * j - 1) % a) % a
            return den, (50 * j - 6) % a * den % a, num
        h = (a - 1) // 2
        P0 = (-h * pow(2, -h, a)) % a
        top = h
    else:
        def f(j):
            d = 3 * j - 2 * a
            e2 = 2 * j - a
            num = 2 * (d % a) % a * ((d - 1) % a) % a * ((d - 2) % a) % a
            den = (j % a) * (e2 % a) % a * ((e2 - 1) % a) % a
            return den, (50 * j - 6) % a * den % a, num
        P0 = 2
        top = a - 1
    return f, P0, top


def comp(t1, t2, p):
    D1, B1, N1 = t1
    D2, B2, N2 = t2
    return D1 * D2 % p, (D2 * B1 + B2 * N1) % p, N1 * N2 % p


def tail_bgs(a, kind, L):
    f, P0, top = make_steps(a, kind)
    v = max(2, isqrt(L // 3) + 1)
    while True:
        res = _bgs(a, kind, L, v, f, P0, top)
        if res is not None:
            return res
        v += 1


def _bgs(a, kind, L, v, f, P0, top):
    p = a
    bits = bin(v)[3:]
    d = 1
    K = 4
    x = np.arange(K + 1, dtype=np.int64)
    G = tuple(np.asarray(t, dtype=np.int64) for t in f(top - v * x))
    for bit in bits:
        Kd = 3 * d + 1
        K2 = 6 * d + 1
        ctx = Ctx(Kd, p)
        delta = d * pow(v, -1, p) % p
        comps_first = []
        comps_second = []
        for c in range(3):
            vals = G[c] % p
            ext = shift(vals, Kd + 1, ctx)
            if ext is None:
                return None
            first = np.concatenate([vals, ext])[:K2 + 1]
            sh = []
            for m in range(3):
                r = shift(vals, (delta + m * (Kd + 1)) % p, ctx)
                if r is None:
                    return None
                sh.append(r)
            second = np.concatenate(sh)[:K2 + 1]
            comps_first.append(first)
            comps_second.append(second)
        G = comp(tuple(comps_first), tuple(comps_second), p)
        d *= 2
        if bit == '1':
            Kc = 3 * d + 1
            ctx = Ctx(Kc, p)
            ext = []
            for c in range(3):
                e = shift(G[c] % p, Kc + 1, ctx)
                if e is None:
                    return None
                ext.append(np.concatenate([G[c] % p, e])[:Kc + 4])
            xs = np.arange(Kc + 4, dtype=np.int64)
            st = f((top - v * xs - d) % p)
            G = comp(tuple(ext), tuple(np.asarray(t, dtype=np.int64) for t in st), p)
            d += 1
    w = L // v
    tot = (1, 0, 1)
    for xblk in range(w):
        tot = comp(tot, (int(G[0][xblk]), int(G[1][xblk]), int(G[2][xblk])), p)
    Dt, Bt, Nt = tot
    A, P, Q = Bt * P0 % p, Nt * P0 % p, Dt
    for s in range(v * w, L):
        D, B, N = f(top - s)
        A = (D * A + B * P) % p
        P = P * N % p
        Q = Q * D % p
    return A * pow(Q, -1, p) % p


def finish(tot, g):
    fr = tot & ((1 << SH) - 1)
    return str(fr * 10 ** g >> SH).zfill(g)


def digits(n, g=14, mode='gosper'):
    if mode == 'euler':
        return digits_euler(n, g)
    assert n >= 400
    N, small, big = setup_g(n, g)
    tot = adic_sum_g(small, n, N)
    tot += gosper_blocks(big, n, N) if mode == 'gosper' else adic_sum_g(big, n, N)
    return finish(tot, g)


if __name__ == "__main__":
    n = int(sys.argv[1])
    g = int(sys.argv[2]) if len(sys.argv) > 2 else 14
    mode = sys.argv[3] if len(sys.argv) > 3 else 'gosper'
    if len(sys.argv) > 4:
        THR = int(sys.argv[4])
    t = time.time()
    d = digits(n, g, mode)
    print(d[:g - 4], round(time.time() - t, 2), "s")

Download raw | Create new paste

© BitByByte, 2025.