#include "acb.h"
#include "acb_hypgeom.h"
#include "acb_hypgeom/impl.h"
static int
_acb_hypgeom_legendre_q_single_valid(const acb_t z)
{
arb_t t;
int ok;
if (!arb_contains_zero(acb_imagref(z)))
return 1;
if (arb_is_positive(acb_imagref(z)))
return 1;
arb_init(t);
arb_one(t);
arb_neg(t, t);
ok = arb_lt(acb_realref(z), t);
arb_clear(t);
return ok;
}
void
_acb_hypgeom_legendre_q_double(acb_t res, const acb_t n, const acb_t m,
const acb_t z, slong prec)
{
acb_t t, u, v;
acb_init(t);
acb_init(u);
acb_init(v);
if (acb_is_int(m))
{
acb_sub_ui(t, z, 1, prec);
acb_mul_2exp_si(u, m, -1);
acb_pow(v, t, u, prec);
acb_neg(t, t);
acb_neg(u, u);
acb_pow(t, t, u, prec);
acb_mul(t, t, v, prec);
acb_hypgeom_legendre_q(u, n, m, z, 0, prec);
acb_mul(t, t, u, prec);
acb_mul_2exp_si(u, m, -1);
if (!acb_is_int(u))
acb_neg(t, t);
acb_sub_ui(u, z, 1, prec);
acb_sqrt(u, u, prec);
acb_sub_ui(v, z, 1, prec);
acb_neg(v, v);
acb_rsqrt(v, v, prec);
acb_mul(u, u, v, prec);
acb_hypgeom_legendre_p(v, n, m, z, 1, prec);
acb_mul(u, u, v, prec);
acb_const_pi(v, prec);
acb_mul(u, u, v, prec);
acb_mul_2exp_si(u, u, -1);
acb_sub(res, t, u, prec);
}
else
{
acb_sub(t, n, m, prec);
acb_add_ui(t, t, 1, prec);
acb_mul_2exp_si(u, m, 1);
acb_rising(t, t, u, prec);
acb_neg(u, m);
acb_hypgeom_legendre_p(u, n, u, z, 1, prec);
acb_mul(t, t, u, prec);
acb_hypgeom_legendre_p(u, n, m, z, 1, prec);
acb_sub(t, u, t, prec);
acb_exp_pi_i(u, m, prec);
acb_mul(t, t, u, prec);
acb_sin_pi(u, m, prec);
acb_div(t, t, u, prec);
acb_const_pi(u, prec);
acb_mul(t, t, u, prec);
acb_mul_2exp_si(t, t, -1);
acb_set(res, t);
}
acb_clear(t);
acb_clear(u);
acb_clear(v);
}
void
_acb_hypgeom_legendre_q_single(acb_t res, const acb_t n, const acb_t m,
const acb_t z, slong prec)
{
acb_t a, b, c, z2, t, u;
acb_init(a);
acb_init(b);
acb_init(c);
acb_init(z2);
acb_init(t);
acb_init(u);
if (!_acb_hypgeom_legendre_q_single_valid(z))
{
acb_indeterminate(res);
return;
}
acb_pow_si(z2, z, -2, prec);
acb_add(b, m, n, prec);
acb_add_ui(a, b, 1, prec);
acb_mul_2exp_si(a, a, -1);
acb_mul_2exp_si(b, b, -1);
acb_add_ui(b, b, 1, prec);
acb_set_ui(c, 3);
acb_mul_2exp_si(c, c, -1);
acb_add(c, c, n, prec);
acb_hypgeom_2f1(t, a, b, c, z2, 1, prec);
if (!acb_is_zero(m))
{
acb_add_ui(z2, z, 1, prec);
acb_mul_2exp_si(c, m, -1);
acb_pow(z2, z2, c, prec);
acb_mul(t, t, z2, prec);
acb_sub_ui(z2, z, 1, prec);
acb_mul_2exp_si(c, m, -1);
acb_pow(z2, z2, c, prec);
acb_mul(t, t, z2, prec);
acb_exp_pi_i(z2, m, prec);
acb_mul(t, t, z2, prec);
}
acb_set_ui(z2, 2);
acb_neg(c, n);
acb_pow(z2, z2, c, prec);
acb_mul(t, t, z2, prec);
acb_add(c, m, n, prec);
acb_add_ui(c, c, 1, prec);
acb_gamma(z2, c, prec);
acb_mul(t, t, z2, prec);
acb_neg(c, c);
acb_pow(z2, z, c, prec);
acb_mul(t, t, z2, prec);
acb_mul_2exp_si(t, t, -1);
arb_const_sqrt_pi(acb_realref(u), prec);
acb_mul_arb(t, t, acb_realref(u), prec);
acb_set(res, t);
acb_clear(a);
acb_clear(b);
acb_clear(c);
acb_clear(z2);
acb_clear(t);
acb_clear(u);
}
void
acb_hypgeom_legendre_q(acb_t res, const acb_t n, const acb_t m,
const acb_t z, int type, slong prec)
{
if (type == 0)
{
acb_t a, b, c, z2, mn, nm, t, u;
acb_init(a);
acb_init(b);
acb_init(c);
acb_init(z2);
acb_init(mn);
acb_init(nm);
acb_init(t);
acb_init(u);
acb_add(mn, m, n, prec);
acb_sub(nm, n, m, prec);
acb_mul(z2, z, z, prec);
acb_sub_ui(a, mn, 1, prec);
acb_neg(a, a);
acb_mul_2exp_si(a, a, -1);
acb_mul_2exp_si(b, nm, -1);
acb_add_ui(b, b, 1, prec);
acb_set_ui(c, 3);
acb_mul_2exp_si(c, c, -1);
acb_hypgeom_2f1(t, a, b, c, z2, 0, prec);
acb_neg(a, mn);
acb_mul_2exp_si(a, a, -1);
acb_add_ui(b, nm, 1, prec);
acb_mul_2exp_si(b, b, -1);
acb_one(c);
acb_mul_2exp_si(c, c, -1);
acb_hypgeom_2f1(u, a, b, c, z2, 0, prec);
acb_mul_2exp_si(a, mn, -1);
acb_sin_cos_pi(b, a, a, prec);
acb_mul_2exp_si(c, mn, -1);
acb_add_ui(c, c, 1, prec);
acb_gamma(c, c, prec);
acb_mul(a, a, c, prec);
acb_add_ui(c, nm, 1, prec);
acb_mul_2exp_si(c, c, -1);
acb_rgamma(c, c, prec);
acb_mul(a, a, c, prec);
acb_mul(a, a, z, prec);
acb_add_ui(c, mn, 1, prec);
acb_mul_2exp_si(c, c, -1);
acb_gamma(c, c, prec);
acb_mul(b, b, c, prec);
acb_mul_2exp_si(c, nm, -1);
acb_add_ui(c, c, 1, prec);
acb_rgamma(c, c, prec);
acb_mul(b, b, c, prec);
acb_mul_2exp_si(b, b, -1);
acb_mul(t, t, a, prec);
acb_mul(u, u, b, prec);
acb_sub(t, t, u, prec);
if (!acb_is_zero(m))
{
acb_sub_ui(u, z2, 1, prec);
acb_neg(u, u);
acb_neg(c, m);
acb_mul_2exp_si(c, c, -1);
acb_pow(u, u, c, prec);
acb_set_ui(c, 2);
acb_pow(c, c, m, prec);
acb_mul(u, u, c, prec);
acb_mul(t, t, u, prec);
}
arb_const_sqrt_pi(acb_realref(u), prec);
acb_mul_arb(t, t, acb_realref(u), prec);
acb_set(res, t);
acb_clear(a);
acb_clear(b);
acb_clear(c);
acb_clear(z2);
acb_clear(mn);
acb_clear(nm);
acb_clear(t);
acb_clear(u);
}
else if (type == 1)
{
if ((arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), -2) < 0 &&
arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), -2) < 0) ||
!_acb_hypgeom_legendre_q_single_valid(z))
{
_acb_hypgeom_legendre_q_double(res, n, m, z, prec);
}
else
{
_acb_hypgeom_legendre_q_single(res, n, m, z, prec);
}
}
else
{
flint_throw(FLINT_ERROR, "unsupported 'type' %d for legendre q\n", type);
}
}