def mpi_cos_sin(x, prec): a, b = x if a == b == fzero: return (fone, fone), (fzero, fzero) # Guaranteed to contain both -1 and 1 if (finf in x) or (fninf in x): return (fnone, fone), (fnone, fone) wp = prec + 20 ca, sa, na = cos_sin_quadrant(a, wp) cb, sb, nb = cos_sin_quadrant(b, wp) ca, cb = mpf_min_max([ca, cb]) sa, sb = mpf_min_max([sa, sb]) # Both functions are monotonic within one quadrant if na == nb: pass # Guaranteed to contain both -1 and 1 elif nb - na >= 4: return (fnone, fone), (fnone, fone) else: # cos has maximum between a and b if na // 4 != nb // 4: cb = fone # cos has minimum if (na - 2) // 4 != (nb - 2) // 4: ca = fnone # sin has maximum if (na - 1) // 4 != (nb - 1) // 4: sb = fone # sin has minimum if (na - 3) // 4 != (nb - 3) // 4: sa = fnone # Perturb to force interval rounding more = from_man_exp((MPZ_ONE << wp) + (MPZ_ONE << 10), -wp) less = from_man_exp((MPZ_ONE << wp) - (MPZ_ONE << 10), -wp) def finalize(v, rounding): if bool(v[0]) == (rounding == round_floor): p = more else: p = less v = mpf_mul(v, p, prec, rounding) sign, man, exp, bc = v if exp + bc >= 1: if sign: return fnone return fone return v ca = finalize(ca, round_floor) cb = finalize(cb, round_ceiling) sa = finalize(sa, round_floor) sb = finalize(sb, round_ceiling) return (ca, cb), (sa, sb)
def mpi_cos_sin(x, prec): a, b = x if a == b == fzero: return (fone, fone), (fzero, fzero) # Guaranteed to contain both -1 and 1 if (finf in x) or (fninf in x): return (fnone, fone), (fnone, fone) wp = prec + 20 ca, sa, na = cos_sin_quadrant(a, wp) cb, sb, nb = cos_sin_quadrant(b, wp) ca, cb = mpf_min_max([ca, cb]) sa, sb = mpf_min_max([sa, sb]) # Both functions are monotonic within one quadrant if na == nb: pass # Guaranteed to contain both -1 and 1 elif nb - na >= 4: return (fnone, fone), (fnone, fone) else: # cos has maximum between a and b if na//4 != nb//4: cb = fone # cos has minimum if (na-2)//4 != (nb-2)//4: ca = fnone # sin has maximum if (na-1)//4 != (nb-1)//4: sb = fone # sin has minimum if (na-3)//4 != (nb-3)//4: sa = fnone # Perturb to force interval rounding more = from_man_exp((MPZ_ONE<<wp) + (MPZ_ONE<<10), -wp) less = from_man_exp((MPZ_ONE<<wp) - (MPZ_ONE<<10), -wp) def finalize(v, rounding): if bool(v[0]) == (rounding == round_floor): p = more else: p = less v = mpf_mul(v, p, prec, rounding) sign, man, exp, bc = v if exp+bc >= 1: if sign: return fnone return fone return v ca = finalize(ca, round_floor) cb = finalize(cb, round_ceiling) sa = finalize(sa, round_floor) sb = finalize(sb, round_ceiling) return (ca,cb), (sa,sb)
def mpc_agm(a, b, prec, rnd=round_fast): """ Complex AGM. TODO: * check that convergence works as intended * optimize * select a nonarbitrary branch """ if mpc_is_infnan(a) or mpc_is_infnan(b): return fnan, fnan if mpc_zero in (a, b): return fzero, fzero if mpc_neg(a) == b: return fzero, fzero wp = prec+20 eps = mpf_shift(fone, -wp+10) while 1: a1 = mpc_shift(mpc_add(a, b, wp), -1) b1 = mpc_sqrt(mpc_mul(a, b, wp), wp) a, b = a1, b1 size = mpf_min_max([mpc_abs(a,10), mpc_abs(b,10)])[1] err = mpc_abs(mpc_sub(a, b, 10), 10) if size == fzero or mpf_lt(err, mpf_mul(eps, size)): return a
def mpc_agm(a, b, prec, rnd=round_fast): """ Complex AGM. TODO: * check that convergence works as intended * optimize * select a nonarbitrary branch """ if mpc_is_infnan(a) or mpc_is_infnan(b): return fnan, fnan if mpc_zero in (a, b): return fzero, fzero if mpc_neg(a) == b: return fzero, fzero wp = prec + 20 eps = mpf_shift(fone, -wp + 10) while 1: a1 = mpc_shift(mpc_add(a, b, wp), -1) b1 = mpc_sqrt(mpc_mul(a, b, wp), wp) a, b = a1, b1 size = mpf_min_max([mpc_abs(a, 10), mpc_abs(b, 10)])[1] err = mpc_abs(mpc_sub(a, b, 10), 10) if size == fzero or mpf_lt(err, mpf_mul(eps, size)): return a
def mpi_square(s, prec=0): sa, sb = s if mpf_ge(sa, fzero): a = mpf_mul(sa, sa, prec, round_floor) b = mpf_mul(sb, sb, prec, round_ceiling) elif mpf_le(sb, fzero): a = mpf_mul(sb, sb, prec, round_floor) b = mpf_mul(sa, sa, prec, round_ceiling) else: sa = mpf_neg(sa) sa, sb = mpf_min_max([sa, sb]) a = fzero b = mpf_mul(sb, sb, prec, round_ceiling) return a, b
def mpi_mul(s, t, prec=0): sa, sb = s ta, tb = t sas = mpf_sign(sa) sbs = mpf_sign(sb) tas = mpf_sign(ta) tbs = mpf_sign(tb) if sas == sbs == 0: # Should maybe be undefined if ta == fninf or tb == finf: return fninf, finf return fzero, fzero if tas == tbs == 0: # Should maybe be undefined if sa == fninf or sb == finf: return fninf, finf return fzero, fzero if sas >= 0: # positive * positive if tas >= 0: a = mpf_mul(sa, ta, prec, round_floor) b = mpf_mul(sb, tb, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # positive * negative elif tbs <= 0: a = mpf_mul(sb, ta, prec, round_floor) b = mpf_mul(sa, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # positive * both signs else: a = mpf_mul(sb, ta, prec, round_floor) b = mpf_mul(sb, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf elif sbs <= 0: # negative * positive if tas >= 0: a = mpf_mul(sa, tb, prec, round_floor) b = mpf_mul(sb, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # negative * negative elif tbs <= 0: a = mpf_mul(sb, tb, prec, round_floor) b = mpf_mul(sa, ta, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # negative * both signs else: a = mpf_mul(sa, tb, prec, round_floor) b = mpf_mul(sa, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf else: # General case: perform all cross-multiplications and compare # Since the multiplications can be done exactly, we need only # do 4 (instead of 8: two for each rounding mode) cases = [ mpf_mul(sa, ta), mpf_mul(sa, tb), mpf_mul(sb, ta), mpf_mul(sb, tb) ] if fnan in cases: a, b = (fninf, finf) else: a, b = mpf_min_max(cases) a = mpf_pos(a, prec, round_floor) b = mpf_pos(b, prec, round_ceiling) return a, b
def mpi_mul(s, t, prec=0): sa, sb = s ta, tb = t sas = mpf_sign(sa) sbs = mpf_sign(sb) tas = mpf_sign(ta) tbs = mpf_sign(tb) if sas == sbs == 0: # Should maybe be undefined if ta == fninf or tb == finf: return fninf, finf return fzero, fzero if tas == tbs == 0: # Should maybe be undefined if sa == fninf or sb == finf: return fninf, finf return fzero, fzero if sas >= 0: # positive * positive if tas >= 0: a = mpf_mul(sa, ta, prec, round_floor) b = mpf_mul(sb, tb, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # positive * negative elif tbs <= 0: a = mpf_mul(sb, ta, prec, round_floor) b = mpf_mul(sa, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # positive * both signs else: a = mpf_mul(sb, ta, prec, round_floor) b = mpf_mul(sb, tb, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf elif sbs <= 0: # negative * positive if tas >= 0: a = mpf_mul(sa, tb, prec, round_floor) b = mpf_mul(sb, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = fzero # negative * negative elif tbs <= 0: a = mpf_mul(sb, tb, prec, round_floor) b = mpf_mul(sa, ta, prec, round_ceiling) if a == fnan: a = fzero if b == fnan: b = finf # negative * both signs else: a = mpf_mul(sa, tb, prec, round_floor) b = mpf_mul(sa, ta, prec, round_ceiling) if a == fnan: a = fninf if b == fnan: b = finf else: # General case: perform all cross-multiplications and compare # Since the multiplications can be done exactly, we need only # do 4 (instead of 8: two for each rounding mode) cases = [mpf_mul(sa, ta), mpf_mul(sa, tb), mpf_mul(sb, ta), mpf_mul(sb, tb)] if fnan in cases: a, b = (fninf, finf) else: a, b = mpf_min_max(cases) a = mpf_pos(a, prec, round_floor) b = mpf_pos(b, prec, round_ceiling) return a, b