mirror of
https://github.com/vale981/arb
synced 2025-03-05 09:21:38 -05:00
356 lines
7.4 KiB
C
356 lines
7.4 KiB
C
/*
|
|
Copyright (C) 2012, 2022 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 "flint/thread_support.h"
|
|
#include "hypgeom.h"
|
|
|
|
static __inline__ void
|
|
fmpz_poly_evaluate_si(fmpz_t y, const fmpz_poly_t poly, slong x)
|
|
{
|
|
fmpz_set_si(y, x);
|
|
fmpz_poly_evaluate_fmpz(y, poly, y);
|
|
}
|
|
|
|
static void
|
|
bsplit_recursive_fmpz(fmpz_t P, fmpz_t Q, fmpz_t B, fmpz_t T,
|
|
const hypgeom_t hyp, slong a, slong b, int cont)
|
|
{
|
|
if (b - a == 1)
|
|
{
|
|
if (a == 0)
|
|
{
|
|
fmpz_one(P);
|
|
fmpz_one(Q);
|
|
}
|
|
else
|
|
{
|
|
fmpz_poly_evaluate_si(P, hyp->P, a);
|
|
fmpz_poly_evaluate_si(Q, hyp->Q, a);
|
|
}
|
|
|
|
fmpz_poly_evaluate_si(B, hyp->B, a);
|
|
fmpz_poly_evaluate_si(T, hyp->A, a);
|
|
fmpz_mul(T, T, P);
|
|
}
|
|
else
|
|
{
|
|
slong m;
|
|
fmpz_t P2, Q2, B2, T2;
|
|
|
|
m = (a + b) / 2;
|
|
|
|
fmpz_init(P2);
|
|
fmpz_init(Q2);
|
|
fmpz_init(B2);
|
|
fmpz_init(T2);
|
|
|
|
bsplit_recursive_fmpz(P, Q, B, T, hyp, a, m, 1);
|
|
bsplit_recursive_fmpz(P2, Q2, B2, T2, hyp, m, b, 1);
|
|
|
|
if (fmpz_is_one(B) && fmpz_is_one(B2))
|
|
{
|
|
fmpz_mul(T, T, Q2);
|
|
fmpz_addmul(T, P, T2);
|
|
}
|
|
else
|
|
{
|
|
fmpz_mul(T, T, B2);
|
|
fmpz_mul(T, T, Q2);
|
|
fmpz_mul(T2, T2, B);
|
|
fmpz_addmul(T, P, T2);
|
|
}
|
|
|
|
fmpz_mul(B, B, B2);
|
|
fmpz_mul(Q, Q, Q2);
|
|
if (cont)
|
|
fmpz_mul(P, P, P2);
|
|
|
|
fmpz_clear(P2);
|
|
fmpz_clear(Q2);
|
|
fmpz_clear(B2);
|
|
fmpz_clear(T2);
|
|
}
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
arb_struct P;
|
|
arb_struct Q;
|
|
arb_struct B;
|
|
arb_struct T;
|
|
slong a;
|
|
slong b;
|
|
}
|
|
bsplit_res_t;
|
|
|
|
typedef struct
|
|
{
|
|
const hypgeom_struct * hyp;
|
|
slong prec;
|
|
slong a;
|
|
slong b;
|
|
}
|
|
bsplit_args_t;
|
|
|
|
static void
|
|
bsplit_init(bsplit_res_t * x, void * args)
|
|
{
|
|
arb_init(&x->P);
|
|
arb_init(&x->Q);
|
|
arb_init(&x->B);
|
|
arb_init(&x->T);
|
|
}
|
|
|
|
static void
|
|
bsplit_clear(bsplit_res_t * x, void * args)
|
|
{
|
|
arb_clear(&x->P);
|
|
arb_clear(&x->Q);
|
|
arb_clear(&x->B);
|
|
arb_clear(&x->T);
|
|
}
|
|
|
|
static void
|
|
bsplit_basecase(bsplit_res_t * res, slong a, slong b, bsplit_args_t * args)
|
|
{
|
|
fmpz_t PP, QQ, BB, TT;
|
|
int cont;
|
|
|
|
fmpz_init(PP);
|
|
fmpz_init(QQ);
|
|
fmpz_init(BB);
|
|
fmpz_init(TT);
|
|
|
|
cont = (b != args->b);
|
|
|
|
bsplit_recursive_fmpz(PP, QQ, BB, TT, args->hyp, a, b, cont);
|
|
|
|
arb_set_fmpz(&res->P, PP);
|
|
arb_set_fmpz(&res->Q, QQ);
|
|
arb_set_fmpz(&res->B, BB);
|
|
arb_set_fmpz(&res->T, TT);
|
|
|
|
res->a = a;
|
|
res->b = b;
|
|
|
|
fmpz_clear(PP);
|
|
fmpz_clear(QQ);
|
|
fmpz_clear(BB);
|
|
fmpz_clear(TT);
|
|
}
|
|
|
|
/* res = left */
|
|
static void
|
|
bsplit_merge(bsplit_res_t * res, bsplit_res_t * left, bsplit_res_t * right, bsplit_args_t * args)
|
|
{
|
|
arb_ptr P = &res->P;
|
|
arb_ptr Q = &res->Q;
|
|
arb_ptr B = &res->B;
|
|
arb_ptr T = &res->T;
|
|
|
|
arb_ptr P2 = &right->P;
|
|
arb_ptr Q2 = &right->Q;
|
|
arb_ptr B2 = &right->B;
|
|
arb_ptr T2 = &right->T;
|
|
|
|
slong prec = args->prec;
|
|
|
|
slong b = right->b;
|
|
|
|
int cont = b != args->b;
|
|
|
|
if (res != left)
|
|
flint_abort();
|
|
|
|
if (arb_is_one(B) && arb_is_one(B2))
|
|
{
|
|
arb_mul(T, T, Q2, prec);
|
|
arb_addmul(T, P, T2, prec);
|
|
}
|
|
else
|
|
{
|
|
arb_mul(T, T, B2, prec);
|
|
arb_mul(T, T, Q2, prec);
|
|
arb_mul(T2, T2, B, prec);
|
|
arb_addmul(T, P, T2, prec);
|
|
}
|
|
|
|
arb_mul(B, B, B2, prec);
|
|
arb_mul(Q, Q, Q2, prec);
|
|
if (cont)
|
|
arb_mul(P, P, P2, prec);
|
|
|
|
res->b = right->b;
|
|
}
|
|
|
|
#define WANT_PARALLEL 1
|
|
|
|
static void
|
|
bsplit_recursive_arb(arb_t P, arb_t Q, arb_t B, arb_t T,
|
|
const hypgeom_t hyp, slong a, slong b, int cont, slong prec)
|
|
{
|
|
if (WANT_PARALLEL)
|
|
{
|
|
bsplit_res_t res;
|
|
bsplit_args_t args;
|
|
|
|
res.P = *P;
|
|
res.Q = *Q;
|
|
res.B = *B;
|
|
res.T = *T;
|
|
|
|
args.hyp = hyp;
|
|
args.prec = prec;
|
|
args.a = a;
|
|
args.b = b;
|
|
|
|
flint_parallel_binary_splitting(&res,
|
|
(bsplit_basecase_func_t) bsplit_basecase,
|
|
(bsplit_merge_func_t) bsplit_merge,
|
|
sizeof(bsplit_res_t),
|
|
(bsplit_init_func_t) bsplit_init,
|
|
(bsplit_clear_func_t) bsplit_clear,
|
|
&args, a, b, 4, -1, FLINT_PARALLEL_BSPLIT_LEFT_INPLACE);
|
|
|
|
*P = res.P;
|
|
*Q = res.Q;
|
|
*B = res.B;
|
|
*T = res.T;
|
|
}
|
|
else
|
|
{
|
|
if (b - a < 4)
|
|
{
|
|
fmpz_t PP, QQ, BB, TT;
|
|
|
|
fmpz_init(PP);
|
|
fmpz_init(QQ);
|
|
fmpz_init(BB);
|
|
fmpz_init(TT);
|
|
|
|
bsplit_recursive_fmpz(PP, QQ, BB, TT, hyp, a, b, cont);
|
|
|
|
arb_set_fmpz(P, PP);
|
|
arb_set_fmpz(Q, QQ);
|
|
arb_set_fmpz(B, BB);
|
|
arb_set_fmpz(T, TT);
|
|
|
|
fmpz_clear(PP);
|
|
fmpz_clear(QQ);
|
|
fmpz_clear(BB);
|
|
fmpz_clear(TT);
|
|
}
|
|
else
|
|
{
|
|
slong m;
|
|
arb_t P2, Q2, B2, T2;
|
|
|
|
m = (a + b) / 2;
|
|
|
|
arb_init(P2);
|
|
arb_init(Q2);
|
|
arb_init(B2);
|
|
arb_init(T2);
|
|
|
|
bsplit_recursive_arb(P, Q, B, T, hyp, a, m, 1, prec);
|
|
bsplit_recursive_arb(P2, Q2, B2, T2, hyp, m, b, 1, prec);
|
|
|
|
if (arb_is_one(B) && arb_is_one(B2))
|
|
{
|
|
arb_mul(T, T, Q2, prec);
|
|
arb_addmul(T, P, T2, prec);
|
|
}
|
|
else
|
|
{
|
|
arb_mul(T, T, B2, prec);
|
|
arb_mul(T, T, Q2, prec);
|
|
arb_mul(T2, T2, B, prec);
|
|
arb_addmul(T, P, T2, prec);
|
|
}
|
|
|
|
arb_mul(B, B, B2, prec);
|
|
arb_mul(Q, Q, Q2, prec);
|
|
if (cont)
|
|
arb_mul(P, P, P2, prec);
|
|
|
|
arb_clear(P2);
|
|
arb_clear(Q2);
|
|
arb_clear(B2);
|
|
arb_clear(T2);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
arb_hypgeom_sum(arb_t P, arb_t Q, const hypgeom_t hyp, slong n, slong prec)
|
|
{
|
|
if (n < 1)
|
|
{
|
|
arb_zero(P);
|
|
arb_one(Q);
|
|
}
|
|
else
|
|
{
|
|
arb_t B, T;
|
|
arb_init(B);
|
|
arb_init(T);
|
|
bsplit_recursive_arb(P, Q, B, T, hyp, 0, n, 0, prec);
|
|
if (!arb_is_one(B))
|
|
arb_mul(Q, Q, B, prec);
|
|
arb_swap(P, T);
|
|
arb_clear(B);
|
|
arb_clear(T);
|
|
}
|
|
}
|
|
|
|
void
|
|
arb_hypgeom_infsum(arb_t P, arb_t Q, hypgeom_t hyp, slong target_prec, slong prec)
|
|
{
|
|
mag_t err, z;
|
|
slong n;
|
|
|
|
mag_init(err);
|
|
mag_init(z);
|
|
|
|
mag_set_fmpz(z, hyp->P->coeffs + hyp->P->length - 1);
|
|
mag_div_fmpz(z, z, hyp->Q->coeffs + hyp->Q->length - 1);
|
|
|
|
if (!hyp->have_precomputed)
|
|
{
|
|
hypgeom_precompute(hyp);
|
|
hyp->have_precomputed = 1;
|
|
}
|
|
|
|
n = hypgeom_bound(err, hyp->r, hyp->boundC, hyp->boundD,
|
|
hyp->boundK, hyp->MK, z, target_prec);
|
|
|
|
arb_hypgeom_sum(P, Q, hyp, n, prec);
|
|
|
|
if (arf_sgn(arb_midref(Q)) < 0)
|
|
{
|
|
arb_neg(P, P);
|
|
arb_neg(Q, Q);
|
|
}
|
|
|
|
/* We have p/q = s + err i.e. (p + q*err)/q = s */
|
|
{
|
|
mag_t u;
|
|
mag_init(u);
|
|
arb_get_mag(u, Q);
|
|
mag_mul(u, u, err);
|
|
mag_add(arb_radref(P), arb_radref(P), u);
|
|
mag_clear(u);
|
|
}
|
|
|
|
mag_clear(z);
|
|
mag_clear(err);
|
|
}
|
|
|