arb/arb_hypgeom/legendre_p_ui_rec.c
2020-06-23 13:43:00 +02:00

149 lines
3.7 KiB
C

/*
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 <http://www.gnu.org/licenses/>.
*/
#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);
}