def mpf_asin(x, prec, rnd=round_fast): sign, man, exp, bc = x if bc+exp > 0 and x not in (fone, fnone): raise ComplexResult("asin(x) is real only for -1 <= x <= 1") flag_nr = True if prec < 1000 or exp+bc < -13: flag_nr = False else: ebc = exp + bc if ebc < -13: flag_nr = False elif ebc < -3: if prec < 3000: flag_nr = False if not flag_nr: # asin(x) = 2*atan(x/(1+sqrt(1-x**2))) wp = prec + 15 a = mpf_mul(x, x) b = mpf_add(fone, mpf_sqrt(mpf_sub(fone, a, wp), wp), wp) c = mpf_div(x, b, wp) return mpf_shift(mpf_atan(c, prec, rnd), 1) # use Newton's method extra = 10 extra_p = 10 prec2 = prec + extra r = math.asin(to_float(x)) r = from_float(r, 50, rnd) for p in giant_steps(50, prec2): wp = p + extra_p c, s = cos_sin(r, wp, rnd) tmp = mpf_sub(x, s, wp, rnd) tmp = mpf_div(tmp, c, wp, rnd) r = mpf_add(r, tmp, wp, rnd) sign, man, exp, bc = r return normalize(sign, man, exp, bc, prec, rnd)
def mpc_nthroot(z, n, prec, rnd=round_fast): """ Complex n-th root. Use Newton method as in the real case when it is faster, otherwise use z**(1/n) """ a, b = z if a[0] == 0 and b == fzero: re = mpf_nthroot(a, n, prec, rnd) return (re, fzero) if n < 2: if n == 0: return mpc_one if n == 1: return mpc_pos((a, b), prec, rnd) if n == -1: return mpc_div(mpc_one, (a, b), prec, rnd) inverse = mpc_nthroot((a, b), -n, prec + 5, reciprocal_rnd[rnd]) return mpc_div(mpc_one, inverse, prec, rnd) if n <= 20: prec2 = int(1.2 * (prec + 10)) asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b pf = mpc_abs((a, b), prec) if pf[-2] + pf[-1] > -10 and pf[-2] + pf[-1] < prec: af = to_fixed(a, prec2) bf = to_fixed(b, prec2) re, im = mpc_nthroot_fixed(af, bf, n, prec2) extra = 10 re = from_man_exp(re, -prec2 - extra, prec2, rnd) im = from_man_exp(im, -prec2 - extra, prec2, rnd) return re, im fn = from_int(n) prec2 = prec + 10 + 10 nth = mpf_rdiv_int(1, fn, prec2) re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd) re = normalize(re[0], re[1], re[2], re[3], prec, rnd) im = normalize(im[0], im[1], im[2], im[3], prec, rnd) return re, im
def mpc_nthroot(z, n, prec, rnd=round_fast): """ Complex n-th root. Use Newton method as in the real case when it is faster, otherwise use z**(1/n) """ a, b = z if a[0] == 0 and b == fzero: re = mpf_nthroot(a, n, prec, rnd) return (re, fzero) if n < 2: if n == 0: return mpc_one if n == 1: return mpc_pos((a, b), prec, rnd) if n == -1: return mpc_div(mpc_one, (a, b), prec, rnd) inverse = mpc_nthroot((a, b), -n, prec+5, reciprocal_rnd[rnd]) return mpc_div(mpc_one, inverse, prec, rnd) if n <= 20: prec2 = int(1.2 * (prec + 10)) asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b pf = mpc_abs((a,b), prec) if pf[-2] + pf[-1] > -10 and pf[-2] + pf[-1] < prec: af = to_fixed(a, prec2) bf = to_fixed(b, prec2) re, im = mpc_nthroot_fixed(af, bf, n, prec2) extra = 10 re = from_man_exp(re, -prec2-extra, prec2, rnd) im = from_man_exp(im, -prec2-extra, prec2, rnd) return re, im fn = from_int(n) prec2 = prec+10 + 10 nth = mpf_rdiv_int(1, fn, prec2) re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd) re = normalize(re[0], re[1], re[2], re[3], prec, rnd) im = normalize(im[0], im[1], im[2], im[3], prec, rnd) return re, im
def __new__(cls, val=fzero, **kwargs): """A new mpf can be created from a Python float, an int, a or a decimal string representing a number in floating-point format.""" prec, rounding = prec_rounding if kwargs: prec = kwargs.get('prec', prec) if 'dps' in kwargs: prec = dps_to_prec(kwargs['dps']) rounding = kwargs.get('rounding', rounding) if type(val) is cls: sign, man, exp, bc = val._mpf_ if (not man) and exp: return val return make_mpf(normalize(sign, man, exp, bc, prec, rounding)) elif type(val) is tuple: if len(val) == 2: return make_mpf(from_man_exp(val[0], val[1], prec, rounding)) if len(val) == 4: sign, man, exp, bc = val return make_mpf(normalize(sign, MP_BASE(man), exp, bc, prec, rounding)) raise ValueError else: return make_mpf(mpf_pos(mpf_convert_arg(val, prec, rounding), prec, rounding))
def mpf_exp(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if not exp: return fone if x == fninf: return fzero return x # Fast handling e**n. TODO: the best cutoff depends on both the # size of n and the precision. if prec > 600 and exp >= 0: return mpf_pow_int(mpf_e(prec + 10), (-1)**sign * (man << exp), prec, rnd) mag = bc + exp if mag < -prec - 10: return mpf_perturb(fone, sign, prec, rnd) # extra precision needs to be similar in magnitude to log_2(|x|) # for the modulo reduction, plus r for the error from squaring r times wp = prec + max(0, mag) if wp < 300: r = int(2 * wp**0.4) if mag < 0: r = max(1, r + mag) wp += r + 20 t = to_fixed(x, wp) # abs(x) > 1? if mag > 1: lg2 = ln2_fixed(wp) n, t = divmod(t, lg2) else: n = 0 man = exp_series(t, wp, r) else: r = int(0.7 * wp**0.5) if mag < 0: r = max(1, r + mag) wp += r + 20 t = to_fixed(x, wp) if mag > 1: lg2 = ln2_fixed(wp) n, t = divmod(t, lg2) else: n = 0 man = exp_series2(t, wp, r) bc = wp - 2 + bctable[int(man >> (wp - 2))] return normalize(0, man, int(-wp + n), bc, prec, rnd)
def mpf_exp(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if not exp: return fone if x == fninf: return fzero return x # Fast handling e**n. TODO: the best cutoff depends on both the # size of n and the precision. if prec > 600 and exp >= 0: return mpf_pow_int(mpf_e(prec+10), (-1)**sign *(man<<exp), prec, rnd) mag = bc+exp if mag < -prec-10: return mpf_perturb(fone, sign, prec, rnd) # extra precision needs to be similar in magnitude to log_2(|x|) # for the modulo reduction, plus r for the error from squaring r times wp = prec + max(0, mag) if wp < 300: r = int(2*wp**0.4) if mag < 0: r = max(1, r + mag) wp += r + 20 t = to_fixed(x, wp) # abs(x) > 1? if mag > 1: lg2 = ln2_fixed(wp) n, t = divmod(t, lg2) else: n = 0 man = exp_series(t, wp, r) else: r = int(0.7 * wp**0.5) if mag < 0: r = max(1, r + mag) wp += r + 20 t = to_fixed(x, wp) if mag > 1: lg2 = ln2_fixed(wp) n, t = divmod(t, lg2) else: n = 0 man = exp_series2(t, wp, r) bc = wp - 2 + bctable[int(man >> (wp - 2))] return normalize(0, man, int(-wp+n), bc, prec, rnd)
def mpf_exp(x, prec, rnd=round_fast): sign, man, exp, bc = x if not man: if not exp: return fone if x == fninf: return fzero return x mag = bc+exp # Fast handling e**n. TODO: the best cutoff depends on both the # size of n and the precision. if prec > 600 and exp >= 0: e = mpf_e(prec+10+int(1.45*mag)) return mpf_pow_int(e, (-1)**sign *(man<<exp), prec, rnd) if mag < -prec-10: return mpf_perturb(fone, sign, prec, rnd) # extra precision needs to be similar in magnitude to log_2(|x|) # for the modulo reduction, plus r for the error from squaring r times wp = prec + max(0, mag) if wp < 300: r = int(2*wp**0.4) if mag < 0: r = max(1, r + mag) wp += r + 20 t = to_fixed(x, wp) # abs(x) > 1? if mag > 1: lg2 = ln2_fixed(wp) n, t = divmod(t, lg2) else: n = 0 man = exp_series(t, wp, r) else: use_newton = False # put a bound on exp to avoid infinite recursion in exp_newton # TODO find a good bound if wp > LIM_EXP_SERIES2 and exp < 1000: if mag > 0: use_newton = True elif mag <= 0 and -mag <= ns_exp[-1]: i = bisect(ns_exp, -mag-1) if i < len(ns_exp): wp0 = precs_exp[i] if wp > wp0: use_newton = True if not use_newton: r = int(0.7 * wp**0.5) if mag < 0: r = max(1, r + mag) wp += r + 20 t = to_fixed(x, wp) if mag > 1: lg2 = ln2_fixed(wp) n, t = divmod(t, lg2) else: n = 0 man = exp_series2(t, wp, r) else: # if x is very small or very large use # exp(x + m) = exp(x) * e**m if mag > LIM_MAG: wp += mag*10 + 100 n = int(mag * math.log(2)) + 1 x = mpf_sub(x, from_int(n, wp), wp) elif mag <= 0: wp += -mag*10 + 100 if mag < 0: n = int(-mag * math.log(2)) + 1 x = mpf_add(x, from_int(n, wp), wp) res = exp_newton(x, wp) sign, man, exp, bc = res if mag < 0: t = mpf_pow_int(mpf_e(wp), n, wp) res = mpf_div(res, t, wp) sign, man, exp, bc = res if mag > LIM_MAG: t = mpf_pow_int(mpf_e(wp), n, wp) res = mpf_mul(res, t, wp) sign, man, exp, bc = res return normalize(sign, man, exp, bc, prec, rnd) bc = bitcount(man) return normalize(0, man, int(-wp+n), bc, prec, rnd)
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 mpf_nthroot(s, n, prec, rnd=round_fast): """nth-root of a positive number Use the Newton method when faster, otherwise use x**(1/n) """ sign, man, exp, bc = s if sign: raise ComplexResult("nth root of a negative number") if not man: if s == fnan: return fnan if s == fzero: if n > 0: return fzero if n == 0: return fone return finf # Infinity if not n: return fnan if n < 0: return fzero return finf flag_inverse = False if n < 2: if n == 0: return fone if n == 1: return mpf_pos(s, prec, rnd) if n == -1: return mpf_div(fone, s, prec, rnd) # n < 0 rnd = reciprocal_rnd[rnd] flag_inverse = True extra_inverse = 5 prec += extra_inverse n = -n if n > 20 and (n >= 20000 or prec < int(233 + 28.3 * n**0.62)): prec2 = prec + 10 fn = from_int(n) nth = mpf_rdiv_int(1, fn, prec2) r = mpf_pow(s, nth, prec2, rnd) s = normalize(r[0], r[1], r[2], r[3], prec, rnd) if flag_inverse: return mpf_div(fone, s, prec-extra_inverse, rnd) else: return s # Convert to a fixed-point number with prec2 bits. prec2 = prec + 2*n - (prec%n) # a few tests indicate that # for 10 < n < 10**4 a bit more precision is needed if n > 10: prec2 += prec2//10 prec2 = prec2 - prec2%n # Mantissa may have more bits than we need. Trim it down. shift = bc - prec2 # Adjust exponents to make prec2 and exp+shift multiples of n. sign1 = 0 es = exp+shift if es < 0: sign1 = 1 es = -es if sign1: shift += es%n else: shift -= es%n man = rshift(man, shift) extra = 10 exp1 = ((exp+shift-(n-1)*prec2)//n) - extra rnd_shift = 0 if flag_inverse: if rnd == 'u' or rnd == 'c': rnd_shift = 1 else: if rnd == 'd' or rnd == 'f': rnd_shift = 1 man = nthroot_fixed(man+rnd_shift, n, prec2, exp1) s = from_man_exp(man, exp1, prec, rnd) if flag_inverse: return mpf_div(fone, s, prec-extra_inverse, rnd) else: return s
def acos_asin(z, prec, rnd, n): """ complex acos for n = 0, asin for n = 1 The algorithm is described in T.E. Hull, T.F. Fairgrieve and P.T.P. Tang 'Implementing the Complex Arcsine and Arcosine Functions using Exception Handling', ACM Trans. on Math. Software Vol. 23 (1997), p299 The complex acos and asin can be defined as acos(z) = acos(beta) - I*sign(a)* log(alpha + sqrt(alpha**2 -1)) asin(z) = asin(beta) + I*sign(a)* log(alpha + sqrt(alpha**2 -1)) where z = a + I*b alpha = (1/2)*(r + s); beta = (1/2)*(r - s) = a/alpha r = sqrt((a+1)**2 + y**2); s = sqrt((a-1)**2 + y**2) These expressions are rewritten in different ways in different regions, delimited by two crossovers alpha_crossover and beta_crossover, and by abs(a) <= 1, in order to improve the numerical accuracy. """ a, b = z wp = prec + 10 # special cases with real argument if b == fzero: am = mpf_sub(fone, mpf_abs(a), wp) # case abs(a) <= 1 if not am[0]: if n == 0: return mpf_acos(a, prec, rnd), fzero else: return mpf_asin(a, prec, rnd), fzero # cases abs(a) > 1 else: # case a < -1 if a[0]: pi = mpf_pi(prec, rnd) c = mpf_acosh(mpf_neg(a), prec, rnd) if n == 0: return pi, mpf_neg(c) else: return mpf_neg(mpf_shift(pi, -1)), c # case a > 1 else: c = mpf_acosh(a, prec, rnd) if n == 0: return fzero, c else: pi = mpf_pi(prec, rnd) return mpf_shift(pi, -1), mpf_neg(c) asign = bsign = 0 if a[0]: a = mpf_neg(a) asign = 1 if b[0]: b = mpf_neg(b) bsign = 1 am = mpf_sub(fone, a, wp) ap = mpf_add(fone, a, wp) r = mpf_hypot(ap, b, wp) s = mpf_hypot(am, b, wp) alpha = mpf_shift(mpf_add(r, s, wp), -1) beta = mpf_div(a, alpha, wp) b2 = mpf_mul(b, b, wp) # case beta <= beta_crossover if not mpf_sub(beta_crossover, beta, wp)[0]: if n == 0: re = mpf_acos(beta, wp) else: re = mpf_asin(beta, wp) else: # to compute the real part in this region use the identity # asin(beta) = atan(beta/sqrt(1-beta**2)) # beta/sqrt(1-beta**2) = (alpha + a) * (alpha - a) # alpha + a is numerically accurate; alpha - a can have # cancellations leading to numerical inaccuracies, so rewrite # it in differente ways according to the region Ax = mpf_add(alpha, a, wp) # case a <= 1 if not am[0]: # c = b*b/(r + (a+1)); d = (s + (1-a)) # alpha - a = (1/2)*(c + d) # case n=0: re = atan(sqrt((1/2) * Ax * (c + d))/a) # case n=1: re = atan(a/sqrt((1/2) * Ax * (c + d))) c = mpf_div(b2, mpf_add(r, ap, wp), wp) d = mpf_add(s, am, wp) re = mpf_shift(mpf_mul(Ax, mpf_add(c, d, wp), wp), -1) if n == 0: re = mpf_atan(mpf_div(mpf_sqrt(re, wp), a, wp), wp) else: re = mpf_atan(mpf_div(a, mpf_sqrt(re, wp), wp), wp) else: # c = Ax/(r + (a+1)); d = Ax/(s - (1-a)) # alpha - a = (1/2)*(c + d) # case n = 0: re = atan(b*sqrt(c + d)/2/a) # case n = 1: re = atan(a/(b*sqrt(c + d)/2) c = mpf_div(Ax, mpf_add(r, ap, wp), wp) d = mpf_div(Ax, mpf_sub(s, am, wp), wp) re = mpf_shift(mpf_add(c, d, wp), -1) re = mpf_mul(b, mpf_sqrt(re, wp), wp) if n == 0: re = mpf_atan(mpf_div(re, a, wp), wp) else: re = mpf_atan(mpf_div(a, re, wp), wp) # to compute alpha + sqrt(alpha**2 - 1), if alpha <= alpha_crossover # replace it with 1 + Am1 + sqrt(Am1*(alpha+1))) # where Am1 = alpha -1 # if alpha <= alpha_crossover: if not mpf_sub(alpha_crossover, alpha, wp)[0]: c1 = mpf_div(b2, mpf_add(r, ap, wp), wp) # case a < 1 if mpf_neg(am)[0]: # Am1 = (1/2) * (b*b/(r + (a+1)) + b*b/(s + (1-a)) c2 = mpf_add(s, am, wp) c2 = mpf_div(b2, c2, wp) Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) else: # Am1 = (1/2) * (b*b/(r + (a+1)) + (s - (1-a))) c2 = mpf_sub(s, am, wp) Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) # im = log(1 + Am1 + sqrt(Am1*(alpha+1))) im = mpf_mul(Am1, mpf_add(alpha, fone, wp), wp) im = mpf_log(mpf_add(fone, mpf_add(Am1, mpf_sqrt(im, wp), wp), wp), wp) else: # im = log(alpha + sqrt(alpha*alpha - 1)) im = mpf_sqrt(mpf_sub(mpf_mul(alpha, alpha, wp), fone, wp), wp) im = mpf_log(mpf_add(alpha, im, wp), wp) if asign: if n == 0: re = mpf_sub(mpf_pi(wp), re, wp) else: re = mpf_neg(re) if not bsign and n == 0: im = mpf_neg(im) if bsign and n == 1: im = mpf_neg(im) re = normalize(re[0], re[1], re[2], re[3], prec, rnd) im = normalize(im[0], im[1], im[2], im[3], prec, rnd) return re, im
return (re, fzero) if n < 2: if n == 0: return mpc_one if n == 1: return mpc_pos((a, b), prec, rnd) if n == -1: return mpc_div(mpc_one, (a, b), prec, rnd) inverse = mpc_nthroot((a, b), -n, prec+5, reciprocal_rnd[rnd]) return mpc_div(mpc_one, inverse, prec, rnd) if n > 20: fn = from_int(n) prec2 = prec+10 nth = mpf_rdiv_int(1, fn, prec2) re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd) re = normalize(re[0], re[1], re[2], re[3], prec, rnd) im = normalize(im[0], im[1], im[2], im[3], prec, rnd) return re, im prec2 = int(1.2 * (prec + 10)) asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b af = to_fixed(a, prec2) bf = to_fixed(b, prec2) re, im = mpc_nthroot_fixed(af, bf, n, prec2) extra = 10 re = from_man_exp(re, -prec2-extra, prec2, rnd) im = from_man_exp(im, -prec2-extra, prec2, rnd) return re, im def mpc_cbrt((a, b), prec, rnd=round_fast): """
def mpf_log(x, prec, rnd=round_fast): """ Compute the natural logarithm of the mpf value x. If x is negative, ComplexResult is raised. """ sign, man, exp, bc = x #------------------------------------------------------------------ # Handle special values if not man: if x == fzero: return fninf if x == finf: return finf if x == fnan: return fnan if sign: raise ComplexResult("logarithm of a negative number") wp = prec + 20 #------------------------------------------------------------------ # Handle log(2^n) = log(n)*2. # Here we catch the only possible exact value, log(1) = 0 if man == 1: if not exp: return fzero return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) mag = exp+bc abs_mag = abs(mag) #------------------------------------------------------------------ # Handle x = 1+eps, where log(x) ~ x. We need to check for # cancellation when moving to fixed-point math and compensate # by increasing the precision. Note that abs_mag in (0, 1) <=> # 0.5 < x < 2 and x != 1 if abs_mag <= 1: # Calculate t = x-1 to measure distance from 1 in bits tsign = 1-abs_mag if tsign: tman = (MP_ONE<<bc) - man else: tman = man - (MP_ONE<<(bc-1)) tbc = bitcount(tman) cancellation = bc - tbc if cancellation > wp: t = normalize(tsign, tman, abs_mag-bc, tbc, tbc, 'n') return mpf_perturb(t, tsign, prec, rnd) else: wp += cancellation # TODO: if close enough to 1, we could use Taylor series # even in the AGM precision range, since the Taylor series # converges rapidly #------------------------------------------------------------------ # Another special case: # n*log(2) is a good enough approximation if abs_mag > 10000: if bitcount(abs_mag) > wp: return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) #------------------------------------------------------------------ # General case. # Perform argument reduction using log(x) = log(x*2^n) - n*log(2): # If we are in the Taylor precision range, choose magnitude 0 or 1. # If we are in the AGM precision range, choose magnitude -m for # some large m; benchmarking on one machine showed m = prec/20 to be # optimal between 1000 and 100,000 digits. if wp <= LOG_TAYLOR_PREC: m = log_taylor_cached(lshift(man, wp-bc), wp) if mag: m += mag*ln2_fixed(wp) else: optimal_mag = -wp//LOG_AGM_MAG_PREC_RATIO n = optimal_mag - mag x = mpf_shift(x, n) wp += (-optimal_mag) m = -log_agm(to_fixed(x, wp), wp) m -= n*ln2_fixed(wp) return from_man_exp(m, -wp, prec, rnd)
def f(prec, rnd=round_fast): wp = prec + 20 v = fixed(wp) if rnd in (round_up, round_ceiling): v += 1 return normalize(0, v, -wp, bitcount(v), prec, rnd)
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 = sqrt_fixed((1<<wp) - ((sin*sin)>>wp), wp) 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
return (re, fzero) if n < 2: if n == 0: return mpc_one if n == 1: return mpc_pos((a, b), prec, rnd) if n == -1: return mpc_div(mpc_one, (a, b), prec, rnd) inverse = mpc_nthroot((a, b), -n, prec + 5, reciprocal_rnd[rnd]) return mpc_div(mpc_one, inverse, prec, rnd) if n > 20: fn = from_int(n) prec2 = prec + 10 nth = mpf_rdiv_int(1, fn, prec2) re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd) re = normalize(re[0], re[1], re[2], re[3], prec, rnd) im = normalize(im[0], im[1], im[2], im[3], prec, rnd) return re, im prec2 = int(1.2 * (prec + 10)) asign, aman, aexp, abc = a bsign, bman, bexp, bbc = b af = to_fixed(a, prec2) bf = to_fixed(b, prec2) re, im = mpc_nthroot_fixed(af, bf, n, prec2) extra = 10 re = from_man_exp(re, -prec2 - extra, prec2, rnd) im = from_man_exp(im, -prec2 - extra, prec2, rnd) return re, im def mpc_cbrt((a, b), prec, rnd=round_fast):