/* Copyright (C) 2017 Fredrik Johansson This file is part of Arb. Arb is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ #include "arb_hypgeom.h" void arb_hypgeom_legendre_p_ui_rec(arb_t res, arb_t res_prime, ulong n, const arb_t x, slong prec) { slong wp; ulong k, den; mp_limb_t denlo, denhi; mpz_t p0, p1, xx, tt; fmpz_t fxx; int error; arb_t t, u, x2sub1; mag_t err1, err2, xrad; if (n > (UWORD(1) << (FLINT_BITS / 2 - 1))) { if (res != NULL) arb_indeterminate(res); if (res_prime != NULL) arb_indeterminate(res); return; } if (n == 0) { if (res != NULL) arb_one(res); if (res_prime != NULL) arb_zero(res_prime); return; } mag_init(xrad); arb_get_mag(xrad, x); /* error analysis assumes |x| < 1 */ if (mag_cmp_2exp_si(xrad, 0) >= 0) { arb_hypgeom_legendre_p_ui_one(res, res_prime, n, x, n + 1, prec); mag_clear(xrad); return; } mpz_init(p0); mpz_init(p1); mpz_init(xx); mpz_init(tt); fmpz_init(fxx); arb_init(t); arb_init(u); arb_init(x2sub1); mag_init(err1); mag_init(err2); wp = -arf_abs_bound_lt_2exp_si(arb_midref(x)); wp = FLINT_MAX(wp, 0); wp = FLINT_MIN(wp, prec); wp += prec + 2 * FLINT_BIT_COUNT(n + 2); /* (n+2)^2 >= 0.75(n+1)(n+2)+1 */ arb_mul(x2sub1, x, x, ARF_PREC_EXACT); arb_neg(x2sub1, x2sub1); arb_add_ui(x2sub1, x2sub1, 1, wp); error = arf_get_fmpz_fixed_si(fxx, arb_midref(x), -wp); fmpz_get_mpz(xx, fxx); mag_set(xrad, arb_radref(x)); if (error) mag_add_ui_2exp_si(xrad, xrad, 1, -wp); mpz_set_ui(p0, 1); mpz_mul_2exp(p0, p0, wp); mpz_set(p1, xx); den = 1; for (k = 1; k < n; k++) { mpz_mul(tt, p1, xx); mpz_tdiv_q_2exp(tt, tt, wp); flint_mpz_mul_ui(p0, p0, k*k); mpz_neg(p0, p0); flint_mpz_addmul_ui(p0, tt, 2 * k + 1); mpz_swap(p0, p1); umul_ppmm(denhi, denlo, den, k + 1); if (denhi != 0) { flint_mpz_tdiv_q_ui(p0, p0, den); flint_mpz_tdiv_q_ui(p1, p1, den); den = k + 1; } else { den = denlo; } } flint_mpz_tdiv_q_ui(p0, p0, den/n); flint_mpz_tdiv_q_ui(p1, p1, den); if (!mag_is_zero(xrad)) { arb_hypgeom_legendre_p_ui_deriv_bound(err1, err2, n, x, x2sub1); mag_mul(err1, err1, xrad); mag_mul(err2, err2, xrad); } arf_set_mpz(arb_midref(t), p1); arf_mul_2exp_si(arb_midref(t), arb_midref(t), -wp); mag_set_ui_2exp_si(arb_radref(t), (n + 1) * (n + 2), -wp); mag_add(arb_radref(t), arb_radref(t), err1); if (res_prime != NULL) { /* P' = n (P[n-1] - x P) / (1 - x^2) */ arf_set_mpz(arb_midref(u), p0); arf_mul_2exp_si(arb_midref(u), arb_midref(u), -wp); mag_set_ui_2exp_si(arb_radref(u), n * (n + 1), -wp); arb_submul(u, t, x, wp); arb_div(res_prime, u, x2sub1, wp); arb_mul_ui(res_prime, res_prime, n, prec); mag_add(arb_radref(res_prime), arb_radref(res_prime), err2); } if (res != NULL) /* done last since x may be aliased with res */ { arb_set_round(res, t, prec); } mpz_clear(p0); mpz_clear(p1); mpz_clear(xx); mpz_clear(tt); fmpz_clear(fxx); arb_clear(t); arb_clear(u); arb_clear(x2sub1); mag_clear(err1); mag_clear(err2); mag_clear(xrad); }