Example #1
0
def phi_fixed(prec):
    """
    Computes the golden ratio, (1+sqrt(5))/2
    """
    prec += 10
    a = isqrt_fast(MP_FIVE<<(2*prec)) + (MP_ONE << prec)
    return a >> 11
Example #2
0
def log_taylor(x, prec, r=0):
    """
    Fixed-point calculation of log(x). It is assumed that x is close
    enough to 1 for the Taylor series to converge quickly. Convergence
    can be improved by specifying r > 0 to compute
    log(x^(1/2^r))*2^r, at the cost of performing r square roots.

    The caller must provide sufficient guard bits.
    """
    for i in xrange(r):
        x = isqrt_fast(x<<prec)
    one = MP_ONE << prec
    v = ((x-one)<<prec)//(x+one)
    sign = v < 0
    if sign:
        v = -v
    v2 = (v*v) >> prec
    v4 = (v2*v2) >> prec
    s0 = v
    s1 = v//3
    v = (v*v4) >> prec
    k = 5
    while v:
        s0 += v // k
        k += 2
        s1 += v // k
        v = (v*v4) >> prec
        k += 2
    s1 = (s1*v2) >> prec
    s = (s0+s1) << (1+r)
    if sign:
        return -s
    return s
Example #3
0
def exp_series2(x, prec, r):
    x >>= r
    sign = 0
    if x < 0:
        sign = 1
        x = -x
    x2 = (x*x) >> prec
    if prec < 1500:
        s1 = a = x
        k = 3
        while a:
            a = ((a * x2) >> prec) // (k*(k-1))
            s1 += a
            k += 2
    else:
        # use Smith's method:
        # reduce the number of multiplication summing concurrently J series
        # J=4
        # Sinh(x) =
        #   (x + x^9/9! + ...) + x^2 * (x/3! + x^9/11! + ...) +
        #   x^4 * (x/5! + x^9/13! + ...) + x^6 * (x/7! + x^9/15! + ...)
        J = 4
        ax = [MP_ONE << prec, x2]
        px = x2
        asum = [x, x//6]
        fact = 6
        k = 4
        for j in range(2, J):
            px = (px * x2) >> prec
            ax.append(px)
            fact *= k*(k+1)
            asum.append(x//fact)
            k += 2
        lx = (ax[-1]*x2) >> prec
        p = asum[-1]
        while p:
            p = (p * lx) >> prec
            for j in range(J):
                p = p//(k*(k+1))
                asum[j] += p
                k += 2
        s1 = 0
        for i in range(1, J):
            s1 += ax[i]*asum[i]
        s1 = asum[0] + (s1 >> prec)
    c1 = isqrt_fast((s1*s1) + (MP_ONE<<(2*prec)))
    if sign:
        s = c1 - s1
    else:
        s = c1 + s1
    # Calculate s**(2**r) by repeated squaring
    while r:
        s = (s*s) >> prec
        r -= 1
    return s
Example #4
0
def agm_fixed(a, b, prec):
    """
    Fixed-point computation of agm(a,b), assuming
    a, b both close to unit magnitude.
    """
    i = 0
    while 1:
        anew = (a+b)>>1
        if i > 4 and abs(a-anew) < 8:
            return a
        b = isqrt_fast(a*b)
        a = anew
        i += 1
    return a
Example #5
0
def pi_fixed(prec, verbose=False, verbose_base=None):
    """
    Compute floor(pi * 2**prec) as a big integer.

    This is done using Chudnovsky's series (see comments in
    libelefun.py for details).
    """
    # The Chudnovsky series gives 14.18 digits per term
    N = int(prec/3.3219280948/14.181647462 + 2)
    if verbose:
        print "binary splitting with N =", N
    g, p, q = bs_chudnovsky(0, N, 0, verbose)
    sqrtC = isqrt_fast(CHUD_C<<(2*prec))
    v = p*CHUD_C*sqrtC//((q+CHUD_A*p)*CHUD_D)
    return v
Example #6
0
def expi_series(x, prec, r):
    x >>= r
    one = MP_ONE << prec
    x2 = (x*x) >> prec
    s = x
    a = x
    k = 2
    while a:
        a = ((a * x2) >> prec) // (-k*(k+1))
        s += a
        k += 2
    c = isqrt_fast((MP_ONE<<(2*prec)) - (s*s))
    # Calculate (c + j*s)**(2**r) by repeated squaring
    for j in range(r):
        c, s =  (c*c-s*s) >> prec, (2*c*s ) >> prec
    return c, s
Example #7
0
def log_agm(x, prec):
    """
    Fixed-point computation of -log(x) = log(1/x), suitable
    for large precision. It is required that 0 < x < 1. The
    algorithm used is the Sasaki-Kanada formula

        -log(x) = pi/agm(theta2(x)^2,theta3(x)^2). [1]

    For faster convergence in the theta functions, x should
    be chosen closer to 0.

    Guard bits must be added by the caller.

    HYPOTHESIS: if x = 2^(-n), n bits need to be added to
    account for the truncation to a fixed-point number,
    and this is the only significant cancellation error.

    The number of bits lost to roundoff is small and can be
    considered constant.

    [1] Richard P. Brent, "Fast Algorithms for High-Precision
        Computation of Elementary Functions (extended abstract)",
        http://wwwmaths.anu.edu.au/~brent/pd/RNC7-Brent.pdf

    """
    x2 = (x*x) >> prec
    # Compute jtheta2(x)**2
    s = a = b = x2
    while a:
        b = (b*x2) >> prec
        a = (a*b) >> prec
        s += a
    s += (MP_ONE<<prec)
    s = (s*s)>>(prec-2)
    s = (s*isqrt_fast(x<<prec))>>prec
    # Compute jtheta3(x)**2
    t = a = b = x
    while a:
        b = (b*x2) >> prec
        a = (a*b) >> prec
        t += a
    t = (MP_ONE<<prec) + (t<<1)
    t = (t*t)>>prec
    # Final formula
    p = agm_fixed(s, t, prec)
    return (pi_fixed(prec) << prec) // p
Example #8
0
def calc_cos_sin(which, y, swaps, prec, cos_rnd, sin_rnd):
    """
    Simultaneous computation of cos and sin (internal function).
    """
    y, wp = y
    swap_cos_sin, cos_sign, sin_sign = swaps

    if swap_cos_sin:
        which_compute = -which
    else:
        which_compute = which

    # XXX: assumes no swaps
    if not y:
        return fone, fzero

    # Tiny nonzero argument
    if wp > prec*2 + 30:
        y = from_man_exp(y, -wp)

        if swap_cos_sin:
            cos_rnd, sin_rnd = sin_rnd, cos_rnd
            cos_sign, sin_sign = sin_sign, cos_sign

        if cos_sign: cos = mpf_perturb(fnone, 0, prec, cos_rnd)
        else:        cos = mpf_perturb(fone, 1, prec, cos_rnd)
        if sin_sign: sin = mpf_perturb(mpf_neg(y), 0, prec, sin_rnd)
        else:        sin = mpf_perturb(y, 1, prec, sin_rnd)

        if swap_cos_sin:
            cos, sin = sin, cos
        return cos, sin

    # Use standard Taylor series
    if prec < 600:
        if which_compute == 0:
            sin = sin_taylor(y, wp)
            # only need to evaluate one of the series
            cos = isqrt_fast((MP_ONE<<(2*wp)) - sin*sin)
        elif which_compute == 1:
            sin = 0
            cos = cos_taylor(y, wp)
        elif which_compute == -1:
            sin = sin_taylor(y, wp)
            cos = 0
    # Use exp(i*x) with Brent's trick
    else:
        r = int(0.137 * prec**0.579)
        ep = r+20
        cos, sin = expi_series(y<<ep, wp+ep, r)
        cos >>= ep
        sin >>= ep

    if swap_cos_sin:
        cos, sin = sin, cos

    if cos_rnd is not round_nearest:
        # Round and set correct signs
        # XXX: this logic needs a second look
        ONE = MP_ONE << wp
        if cos_sign:
            cos += (-1)**(cos_rnd in (round_ceiling, round_down))
            cos = min(ONE, cos)
        else:
            cos += (-1)**(cos_rnd in (round_ceiling, round_up))
            cos = min(ONE, cos)
        if sin_sign:
            sin += (-1)**(sin_rnd in (round_ceiling, round_down))
            sin = min(ONE, sin)
        else:
            sin += (-1)**(sin_rnd in (round_ceiling, round_up))
            sin = min(ONE, sin)

    if which != -1:
        cos = normalize(cos_sign, cos, -wp, bitcount(cos), prec, cos_rnd)
    if which != 1:
        sin = normalize(sin_sign, sin, -wp, bitcount(sin), prec, sin_rnd)

    return cos, sin
Example #9
0
def exponential_series(x, prec, type=0):
    """
    Taylor series for cosh/sinh or cos/sin.

    type = 0 -- returns exp(x)  (slightly faster than cosh+sinh)
    type = 1 -- returns (cosh(x), sinh(x))
    type = 2 -- returns (cos(x), sin(x))
    """
    if x < 0:
        x = -x
        sign = 1
    else:
        sign = 0
    r = int(0.5 * prec**0.5)
    xmag = bitcount(x) - prec
    r = max(0, xmag + r)
    extra = 10 + 2 * max(r, -xmag)
    wp = prec + extra
    x <<= (extra - r)
    one = MPZ_ONE << wp
    alt = (type == 2)
    if prec < EXP_SERIES_U_CUTOFF:
        x2 = a = (x * x) >> wp
        x4 = (x2 * x2) >> wp
        s0 = s1 = MPZ_ZERO
        k = 2
        while a:
            a //= (k - 1) * k
            s0 += a
            k += 2
            a //= (k - 1) * k
            s1 += a
            k += 2
            a = (a * x4) >> wp
        s1 = (x2 * s1) >> wp
        if alt:
            c = s1 - s0 + one
        else:
            c = s1 + s0 + one
    else:
        u = int(0.3 * prec**0.35)
        x2 = a = (x * x) >> wp
        xpowers = [one, x2]
        for i in xrange(1, u):
            xpowers.append((xpowers[-1] * x2) >> wp)
        sums = [MPZ_ZERO] * u
        k = 2
        while a:
            for i in xrange(u):
                a //= (k - 1) * k
                if alt and k & 2: sums[i] -= a
                else: sums[i] += a
                k += 2
            a = (a * xpowers[-1]) >> wp
        for i in xrange(1, u):
            sums[i] = (sums[i] * xpowers[i]) >> wp
        c = sum(sums) + one
    if type == 0:
        s = isqrt_fast(c * c - (one << wp))
        if sign:
            v = c - s
        else:
            v = c + s
        for i in xrange(r):
            v = (v * v) >> wp
        return v >> extra
    else:
        # Repeatedly apply the double-angle formula
        # cosh(2*x) = 2*cosh(x)^2 - 1
        # cos(2*x) = 2*cos(x)^2 - 1
        pshift = wp - 1
        for i in xrange(r):
            c = ((c * c) >> pshift) - one
        # With the abs, this is the same for sinh and sin
        s = isqrt_fast(abs((one << wp) - c * c))
        if sign:
            s = -s
        return (c >> extra), (s >> extra)
Example #10
0
def exponential_series(x, prec, type=0):
    """
    Taylor series for cosh/sinh or cos/sin.

    type = 0 -- returns exp(x)  (slightly faster than cosh+sinh)
    type = 1 -- returns (cosh(x), sinh(x))
    type = 2 -- returns (cos(x), sin(x))
    """
    if x < 0:
        x = -x
        sign = 1
    else:
        sign = 0
    r = int(0.5 * prec ** 0.5)
    xmag = bitcount(x) - prec
    r = max(0, xmag + r)
    extra = 10 + 2 * max(r, -xmag)
    wp = prec + extra
    x <<= extra - r
    one = MPZ_ONE << wp
    alt = type == 2
    if prec < EXP_SERIES_U_CUTOFF:
        x2 = a = (x * x) >> wp
        x4 = (x2 * x2) >> wp
        s0 = s1 = MPZ_ZERO
        k = 2
        while a:
            a //= (k - 1) * k
            s0 += a
            k += 2
            a //= (k - 1) * k
            s1 += a
            k += 2
            a = (a * x4) >> wp
        s1 = (x2 * s1) >> wp
        if alt:
            c = s1 - s0 + one
        else:
            c = s1 + s0 + one
    else:
        u = int(0.3 * prec ** 0.35)
        x2 = a = (x * x) >> wp
        xpowers = [one, x2]
        for i in xrange(1, u):
            xpowers.append((xpowers[-1] * x2) >> wp)
        sums = [MPZ_ZERO] * u
        k = 2
        while a:
            for i in xrange(u):
                a //= (k - 1) * k
                if alt and k & 2:
                    sums[i] -= a
                else:
                    sums[i] += a
                k += 2
            a = (a * xpowers[-1]) >> wp
        for i in xrange(1, u):
            sums[i] = (sums[i] * xpowers[i]) >> wp
        c = sum(sums) + one
    if type == 0:
        s = isqrt_fast(c * c - (one << wp))
        if sign:
            v = c - s
        else:
            v = c + s
        for i in xrange(r):
            v = (v * v) >> wp
        return v >> extra
    else:
        # Repeatedly apply the double-angle formula
        # cosh(2*x) = 2*cosh(x)^2 - 1
        # cos(2*x) = 2*cos(x)^2 - 1
        pshift = wp - 1
        for i in xrange(r):
            c = ((c * c) >> pshift) - one
        # With the abs, this is the same for sinh and sin
        s = isqrt_fast(abs((one << wp) - c * c))
        if sign:
            s = -s
        return (c >> extra), (s >> extra)