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
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
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
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
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
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
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
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
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)
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)