void _arb_poly_asin_series(arb_ptr g, arb_srcptr h, slong hlen, slong n, slong prec) { arb_t c; arb_init(c); arb_asin(c, h, prec); hlen = FLINT_MIN(hlen, n); if (hlen == 1) { _arb_vec_zero(g + 1, n - 1); } else { arb_ptr t, u; slong ulen; t = _arb_vec_init(n); u = _arb_vec_init(n); /* asin(h(x)) = integral(h'(x)/sqrt(1-h(x)^2)) */ ulen = FLINT_MIN(n, 2 * hlen - 1); _arb_poly_mullow(u, h, hlen, h, hlen, ulen, prec); arb_sub_ui(u, u, 1, prec); _arb_vec_neg(u, u, ulen); _arb_poly_rsqrt_series(t, u, ulen, n, prec); _arb_poly_derivative(u, h, hlen, prec); _arb_poly_mullow(g, t, n, u, hlen - 1, n, prec); _arb_poly_integral(g, g, n, prec); _arb_vec_clear(t, n); _arb_vec_clear(u, n); } arb_swap(g, c); arb_clear(c); }
void _arb_poly_inv_series(arb_ptr Qinv, arb_srcptr Q, slong Qlen, slong len, slong prec) { arb_inv(Qinv, Q, prec); if (Qlen == 1) { _arb_vec_zero(Qinv + 1, len - 1); } else if (len == 2) { arb_div(Qinv + 1, Qinv, Q, prec); arb_mul(Qinv + 1, Qinv + 1, Q + 1, prec); arb_neg(Qinv + 1, Qinv + 1); } else { slong Qnlen, Wlen, W2len; arb_ptr W; W = _arb_vec_init(len); NEWTON_INIT(1, len) NEWTON_LOOP(m, n) Qnlen = FLINT_MIN(Qlen, n); Wlen = FLINT_MIN(Qnlen + m - 1, n); W2len = Wlen - m; MULLOW(W, Q, Qnlen, Qinv, m, Wlen, prec); MULLOW(Qinv + m, Qinv, m, W + m, W2len, n - m, prec); _arb_vec_neg(Qinv + m, Qinv + m, n - m); NEWTON_END_LOOP NEWTON_END _arb_vec_clear(W, len); } }
void keiper_li_series(arb_ptr z, slong len, slong prec) { arb_ptr t, u, v; t = _arb_vec_init(len); u = _arb_vec_init(len); v = _arb_vec_init(len); /* -zeta(s) */ flint_printf("zeta: "); TIMEIT_ONCE_START arb_zero(t + 0); arb_one(t + 1); arb_one(u); _arb_poly_zeta_series(v, t, 2, u, 0, len, prec); _arb_vec_neg(v, v, len); TIMEIT_ONCE_STOP SHOW_MEMORY_USAGE /* logarithm */ flint_printf("log: "); TIMEIT_ONCE_START _arb_poly_log_series(t, v, len, len, prec); TIMEIT_ONCE_STOP /* add log(gamma(1+s/2)) */ flint_printf("gamma: "); TIMEIT_ONCE_START arb_one(u); arb_one(u + 1); arb_mul_2exp_si(u + 1, u + 1, -1); _arb_poly_lgamma_series(v, u, 2, len, prec); _arb_vec_add(t, t, v, len, prec); TIMEIT_ONCE_STOP /* subtract 0.5 s log(pi) */ arb_const_pi(u, prec); arb_log(u, u, prec); arb_mul_2exp_si(u, u, -1); arb_sub(t + 1, t + 1, u, prec); /* add log(1-s) */ arb_one(u); arb_set_si(u + 1, -1); _arb_poly_log_series(v, u, 2, len, prec); _arb_vec_add(t, t, v, len, prec); /* binomial transform */ flint_printf("binomial transform: "); TIMEIT_ONCE_START arb_set(z, t); _arb_vec_neg(t + 1, t + 1, len - 1); _arb_poly_binomial_transform(z + 1, t + 1, len - 1, len - 1, prec); TIMEIT_ONCE_STOP _arb_vec_clear(t, len); _arb_vec_clear(u, len); _arb_vec_clear(v, len); }
void _arb_poly_rgamma_series(arb_ptr res, arb_srcptr h, long hlen, long len, long prec) { int reflect; long i, rflen, r, n, wp; arb_ptr t, u, v; arb_struct f[2]; hlen = FLINT_MIN(hlen, len); wp = prec + FLINT_BIT_COUNT(prec); t = _arb_vec_init(len); u = _arb_vec_init(len); v = _arb_vec_init(len); arb_init(f); arb_init(f + 1); /* use zeta values at small integers */ if (arb_is_int(h) && (arf_cmpabs_ui(arb_midref(h), prec / 2) < 0)) { r = arf_get_si(arb_midref(h), ARF_RND_DOWN); _arb_poly_lgamma_series_at_one(u, len, wp); _arb_vec_neg(u, u, len); _arb_poly_exp_series(t, u, len, len, wp); if (r == 1) { _arb_vec_swap(v, t, len); } else if (r <= 0) { arb_set(f, h); arb_one(f + 1); rflen = FLINT_MIN(len, 2 - r); _arb_poly_rising_ui_series(u, f, FLINT_MIN(2, len), 1 - r, rflen, wp); _arb_poly_mullow(v, t, len, u, rflen, len, wp); } else { arb_one(f); arb_one(f + 1); rflen = FLINT_MIN(len, r); _arb_poly_rising_ui_series(v, f, FLINT_MIN(2, len), r - 1, rflen, wp); /* TODO: use div_series? */ _arb_poly_inv_series(u, v, rflen, len, wp); _arb_poly_mullow(v, t, len, u, len, len, wp); } } else { /* otherwise use Stirling series */ arb_gamma_stirling_choose_param(&reflect, &r, &n, h, 1, 0, wp); /* rgamma(h) = (gamma(1-h+r) sin(pi h)) / (rf(1-h, r) * pi), h = h0 + t*/ if (reflect) { /* u = gamma(r+1-h) */ arb_sub_ui(f, h, r + 1, wp); arb_neg(f, f); _arb_poly_gamma_stirling_eval(t, f, n, len, wp); _arb_poly_exp_series(u, t, len, len, wp); for (i = 1; i < len; i += 2) arb_neg(u + i, u + i); /* v = sin(pi x) */ arb_const_pi(f + 1, wp); arb_mul(f, h, f + 1, wp); _arb_poly_sin_series(v, f, 2, len, wp); _arb_poly_mullow(t, u, len, v, len, len, wp); /* rf(1-h,r) * pi */ if (r == 0) { arb_const_pi(u, wp); _arb_vec_scalar_div(v, t, len, u, wp); } else { arb_sub_ui(f, h, 1, wp); arb_neg(f, f); arb_set_si(f + 1, -1); rflen = FLINT_MIN(len, r + 1); _arb_poly_rising_ui_series(v, f, FLINT_MIN(2, len), r, rflen, wp); arb_const_pi(u, wp); _arb_vec_scalar_mul(v, v, rflen, u, wp); /* divide by rising factorial */ /* TODO: might better to use div_series, when it has a good basecase */ _arb_poly_inv_series(u, v, rflen, len, wp); _arb_poly_mullow(v, t, len, u, len, len, wp); } } else { /* rgamma(h) = rgamma(h+r) rf(h,r) */ if (r == 0) { arb_add_ui(f, h, r, wp); _arb_poly_gamma_stirling_eval(t, f, n, len, wp); _arb_vec_neg(t, t, len); _arb_poly_exp_series(v, t, len, len, wp); } else { arb_set(f, h); arb_one(f + 1); rflen = FLINT_MIN(len, r + 1); _arb_poly_rising_ui_series(t, f, FLINT_MIN(2, len), r, rflen, wp); arb_add_ui(f, h, r, wp); _arb_poly_gamma_stirling_eval(v, f, n, len, wp); _arb_vec_neg(v, v, len); _arb_poly_exp_series(u, v, len, len, wp); _arb_poly_mullow(v, u, len, t, rflen, len, wp); } } } /* compose with nonconstant part */ arb_zero(t); _arb_vec_set(t + 1, h + 1, hlen - 1); _arb_poly_compose_series(res, v, len, t, hlen, len, prec); arb_clear(f); arb_clear(f + 1); _arb_vec_clear(t, len); _arb_vec_clear(u, len); _arb_vec_clear(v, len); }
/* with inverse=1 simultaneously computes g = exp(-x) to length n with inverse=0 uses g as scratch space, computing g = exp(-x) only to length (n+1)/2 */ static void _arb_poly_exp_series_newton(arb_ptr f, arb_ptr g, arb_srcptr h, slong len, slong prec, int inverse, slong cutoff) { slong alloc; arb_ptr T, U, hprime; alloc = 3 * len; T = _arb_vec_init(alloc); U = T + len; hprime = U + len; _arb_poly_derivative(hprime, h, len, prec); arb_zero(hprime + len - 1); NEWTON_INIT(cutoff, len) /* f := exp(h) + O(x^m), g := exp(-h) + O(x^m2) */ NEWTON_BASECASE(n) _arb_poly_exp_series_basecase(f, h, n, n, prec); _arb_poly_inv_series(g, f, (n + 1) / 2, (n + 1) / 2, prec); NEWTON_END_BASECASE /* extend from length m to length n */ NEWTON_LOOP(m, n) slong m2 = (m + 1) / 2; slong l = m - 1; /* shifted for derivative */ /* g := exp(-h) + O(x^m) */ _arb_poly_mullow(T, f, m, g, m2, m, prec); _arb_poly_mullow(g + m2, g, m2, T + m2, m - m2, m - m2, prec); _arb_vec_neg(g + m2, g + m2, m - m2); /* U := h' + g (f' - f h') + O(x^(n-1)) Note: should replace h' by h' mod x^(m-1) */ _arb_vec_zero(f + m, n - m); _arb_poly_mullow(T, f, n, hprime, n, n, prec); /* should be mulmid */ _arb_poly_derivative(U, f, n, prec); arb_zero(U + n - 1); /* should skip low terms */ _arb_vec_sub(U + l, U + l, T + l, n - l, prec); _arb_poly_mullow(T + l, g, n - m, U + l, n - m, n - m, prec); _arb_vec_add(U + l, hprime + l, T + l, n - m, prec); /* f := f + f * (h - int U) + O(x^n) = exp(h) + O(x^n) */ _arb_poly_integral(U, U, n, prec); /* should skip low terms */ _arb_vec_sub(U + m, h + m, U + m, n - m, prec); _arb_poly_mullow(f + m, f, n - m, U + m, n - m, n - m, prec); /* g := exp(-h) + O(x^n) */ /* not needed if we only want exp(x) */ if (n == len && inverse) { _arb_poly_mullow(T, f, n, g, m, n, prec); _arb_poly_mullow(g + m, g, m, T + m, n - m, n - m, prec); _arb_vec_neg(g + m, g + m, n - m); } NEWTON_END_LOOP NEWTON_END _arb_vec_clear(T, alloc); }
void _arb_poly_inv_series(arb_ptr Qinv, arb_srcptr Q, slong Qlen, slong len, slong prec) { Qlen = FLINT_MIN(Qlen, len); arb_inv(Qinv, Q, prec); if (Qlen == 1) { _arb_vec_zero(Qinv + 1, len - 1); } else if (len == 2) { arb_mul(Qinv + 1, Qinv, Qinv, prec); arb_mul(Qinv + 1, Qinv + 1, Q + 1, prec); arb_neg(Qinv + 1, Qinv + 1); } else { slong i, j, blen; /* The basecase algorithm is faster for much larger Qlen or len than this, but unfortunately also much less numerically stable. */ if (Qlen == 2 || len <= 8) blen = len; else blen = FLINT_MIN(len, 4); for (i = 1; i < blen; i++) { arb_mul(Qinv + i, Q + 1, Qinv + i - 1, prec); for (j = 2; j < FLINT_MIN(i + 1, Qlen); j++) arb_addmul(Qinv + i, Q + j, Qinv + i - j, prec); if (!arb_is_one(Qinv)) arb_mul(Qinv + i, Qinv + i, Qinv, prec); arb_neg(Qinv + i, Qinv + i); } if (len > blen) { slong Qnlen, Wlen, W2len; arb_ptr W; W = _arb_vec_init(len); NEWTON_INIT(blen, len) NEWTON_LOOP(m, n) Qnlen = FLINT_MIN(Qlen, n); Wlen = FLINT_MIN(Qnlen + m - 1, n); W2len = Wlen - m; MULLOW(W, Q, Qnlen, Qinv, m, Wlen, prec); MULLOW(Qinv + m, Qinv, m, W + m, W2len, n - m, prec); _arb_vec_neg(Qinv + m, Qinv + m, n - m); NEWTON_END_LOOP NEWTON_END _arb_vec_clear(W, len); } } }
void _arb_poly_sin_cos_series_tangent(arb_ptr s, arb_ptr c, arb_srcptr h, slong hlen, slong len, slong prec, int times_pi) { arb_ptr t, u, v; arb_t s0, c0; hlen = FLINT_MIN(hlen, len); if (hlen == 1) { if (times_pi) arb_sin_cos_pi(s, c, h, prec); else arb_sin_cos(s, c, h, prec); _arb_vec_zero(s + 1, len - 1); _arb_vec_zero(c + 1, len - 1); return; } /* sin(x) = 2*tan(x/2)/(1+tan(x/2)^2) cos(x) = (1-tan(x/2)^2)/(1+tan(x/2)^2) */ arb_init(s0); arb_init(c0); t = _arb_vec_init(3 * len); u = t + len; v = u + len; /* sin, cos of h0 */ if (times_pi) arb_sin_cos_pi(s0, c0, h, prec); else arb_sin_cos(s0, c0, h, prec); /* t = tan((h-h0)/2) */ arb_zero(u); _arb_vec_scalar_mul_2exp_si(u + 1, h + 1, hlen - 1, -1); if (times_pi) { arb_const_pi(t, prec); _arb_vec_scalar_mul(u + 1, u + 1, hlen - 1, t, prec); } _arb_poly_tan_series(t, u, hlen, len, prec); /* v = 1 + t^2 */ _arb_poly_mullow(v, t, len, t, len, len, prec); arb_add_ui(v, v, 1, prec); /* u = 1/(1+t^2) */ _arb_poly_inv_series(u, v, len, len, prec); /* sine */ _arb_poly_mullow(s, t, len, u, len, len, prec); _arb_vec_scalar_mul_2exp_si(s, s, len, 1); /* cosine */ arb_sub_ui(v, v, 2, prec); _arb_vec_neg(v, v, len); _arb_poly_mullow(c, v, len, u, len, len, prec); /* sin(h0 + h1) = cos(h0) sin(h1) + sin(h0) cos(h1) cos(h0 + h1) = cos(h0) cos(h1) - sin(h0) sin(h1) */ if (!arb_is_zero(s0)) { _arb_vec_scalar_mul(t, s, len, c0, prec); _arb_vec_scalar_mul(u, c, len, s0, prec); _arb_vec_scalar_mul(v, s, len, s0, prec); _arb_vec_add(s, t, u, len, prec); _arb_vec_scalar_mul(t, c, len, c0, prec); _arb_vec_sub(c, t, v, len, prec); } _arb_vec_clear(t, 3 * len); arb_clear(s0); arb_clear(c0); }