380 changes: 380 additions & 0 deletions arb/bell_fmpz.c
@@ -0,0 +1,380 @@
/*=============================================================================
This file is part of ARB.
ARB is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
ARB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ARB; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=============================================================================*/
/******************************************************************************
Copyright (C) 2015 Fredrik Johansson
******************************************************************************/

#include "arb.h"
#include "arith.h"
#include "double_extras.h"

/* \sum_{k=0}^{a-1} \frac{k^n}{k!} */
/* b = a * \frac{(a-1)^n}{(a-1)!} */
/* assumes n > 0, a >= 0 */
static void
lower_bound(mag_t bound, const fmpz_t a, const fmpz_t n)
{
arb_t t, u;
long wp;

if (fmpz_is_zero(a))
{
mag_zero(bound);
return;
}

wp = 10 + fmpz_bits(n);

arb_init(t);
arb_init(u);

/* decreasing condition: a * (a-1)^n < a^n */
arb_set_fmpz(t, a);
arb_pow_fmpz(t, t, n, wp);

arb_set_fmpz(u, a);
arb_sub_ui(u, u, 1, wp);
arb_pow_fmpz(u, u, n, wp);
arb_mul_fmpz(u, u, a, wp);

if (arb_lt(u, t))
{
arb_gamma_fmpz(t, a, wp);
arb_div(t, u, t, wp);
arb_get_mag(bound, t);
}
else
{
mag_inf(bound);
}

arb_clear(t);
arb_clear(u);
}

/*
b^n [ ((b+1)/b)^n ((b+2)/b)^n ]
--- [ 1 + ----------- + ----------- + .... ]
b! [ (b+1) (b+1)(b+2) ]
*/
static void
upper_bound(mag_t bound, const fmpz_t b, const fmpz_t n)
{
arb_t t, u;
long wp;

wp = 10 + 2 * fmpz_bits(n);

arb_init(t);
arb_init(u);

/* decreasing condition: (1+1/b)^n / (b+1) < 1 */
/* geometric series factor: 1 + t^2 + t^3 + ... = 1/(1-t) */
arb_one(t);
arb_div_fmpz(t, t, b, wp);
arb_add_ui(t, t, 1, wp);
arb_pow_fmpz(t, t, n, wp);
arb_set_fmpz(u, b);
arb_add_ui(u, u, 1, wp);
arb_div(t, t, u, wp);

arb_one(u);
arb_sub(u, u, t, wp);

if (arb_is_positive(u))
{
arb_set_fmpz(t, b);
arb_pow_fmpz(t, t, n, wp);
arb_div(t, t, u, wp);

arb_set_fmpz(u, b);
arb_add_ui(u, u, 1, wp);
arb_gamma(u, u, wp);

arb_div(t, t, u, wp);
arb_get_mag(bound, t);
}
else
{
mag_inf(bound);
}

arb_clear(t);
arb_clear(u);
}

/* approximate; need not give a correct bound, but
should be accurate so that we find near-optimal cutoffs
(we compute correct bounds elsewhere) */
static void
_arb_bell_mag(fmpz_t mmag, const fmpz_t n, const fmpz_t k)
{
if (fmpz_cmp_ui(k, 1) <= 0)
{
fmpz_set(mmag, k);
}
else if (fmpz_bits(n) < 50)
{
double dn, dk, z, u, lg;

dn = fmpz_get_d(n);
dk = fmpz_get_d(k);

z = dk + 1.0;
u = 1.0 / z;
lg = 0.91893853320467274178 + (z-0.5)*log(z) - z;
lg = lg + u * (0.08333333333333333 - 0.00277777777777777778 * (u * u)
+ 0.00079365079365079365079 * ((u * u) * (u * u)));
u = (dn * log(dk) - lg) * 1.4426950408889634074 + 1.0;
fmpz_set_d(mmag, u);
}
else
{
arb_t t, u;
arf_t bound;
long prec;

arb_init(t);
arb_init(u);
arf_init(bound);

prec = 10 + 1.1 * fmpz_bits(n);

arb_log_fmpz(t, k, prec);
arb_mul_fmpz(t, t, n, prec);

arb_set_fmpz(u, k);
arb_add_ui(u, u, 1, prec);
arb_lgamma(u, u, prec);

arb_sub(t, t, u, prec);

arb_const_log2(u, prec);
arb_div(t, t, u, prec);

arf_set_mag(bound, arb_radref(t));
arf_add(bound, arb_midref(t), bound, prec, ARF_RND_CEIL);
arf_get_fmpz(mmag, bound, ARF_RND_CEIL);

arb_clear(t);
arb_clear(u);
arf_clear(bound);
}
}

void
arb_bell_find_cutoffs(fmpz_t A, fmpz_t B, fmpz_t M, fmpz_t Mmag, const fmpz_t n, long prec)
{
fmpz_t a, amag, b, bmag, m, mmag, w, wmag, Amag, Bmag;

fmpz_init(a); fmpz_init(amag);
fmpz_init(b); fmpz_init(bmag);
fmpz_init(m); fmpz_init(mmag);
fmpz_init(w); fmpz_init(wmag);
fmpz_init(Amag); fmpz_init(Bmag);

if (fmpz_bits(n) < 53 && 0)
{
double dn = fmpz_get_d(n);

fmpz_set_d(M, dn / d_lambertw(dn));
_arb_bell_mag(Mmag, n, M);
}
else
{
/* do ternary search for M */
fmpz_zero(a);
fmpz_mul_ui(b, n, 2);
fmpz_zero(amag);
fmpz_zero(bmag);

while (_fmpz_sub_small(b, a) > 4)
{
fmpz_sub(m, b, a);
fmpz_tdiv_q_ui(m, m, 3);
fmpz_mul_2exp(w, m, 1);
fmpz_add(m, a, m);
fmpz_add(w, a, w);

_arb_bell_mag(mmag, n, m);
_arb_bell_mag(wmag, n, w);

if (fmpz_cmp(mmag, wmag) < 0)
{
fmpz_swap(a, m);
fmpz_swap(amag, mmag);
}
else
{
fmpz_swap(b, w);
fmpz_swap(bmag, wmag);
}
}

fmpz_set(M, a);
fmpz_set(Mmag, amag);
}

/* bisect for A */
fmpz_zero(a);
fmpz_zero(amag);
fmpz_set(b, M);
fmpz_set(bmag, Mmag);

while (_fmpz_sub_small(b, a) > 4)
{
fmpz_sub(m, b, a);
fmpz_tdiv_q_2exp(m, m, 1);
fmpz_add(m, a, m);

_arb_bell_mag(mmag, n, m);

/* mmag < Mmag - p */
if (_fmpz_sub_small(mmag, Mmag) < -prec)
{
fmpz_swap(a, m);
fmpz_swap(amag, mmag);
}
else
{
fmpz_swap(b, m);
fmpz_swap(bmag, mmag);
}
}

fmpz_set(A, a);
fmpz_set(Amag, amag);

/* bisect for B */
fmpz_set(a, M);
fmpz_set(amag, Mmag);
fmpz_mul_ui(b, n, 2);
fmpz_zero(bmag);

while (_fmpz_sub_small(b, a) > 4)
{
fmpz_sub(m, b, a);
fmpz_tdiv_q_2exp(m, m, 1);
fmpz_add(m, a, m);

_arb_bell_mag(mmag, n, m);

/* mmag < Mmag - p */
if (_fmpz_sub_small(mmag, Mmag) < -prec || fmpz_sgn(mmag) <= 0)
{
fmpz_swap(b, m);
fmpz_swap(bmag, mmag);
}
else
{
fmpz_swap(a, m);
fmpz_swap(amag, mmag);
}
}

fmpz_set(B, a);
fmpz_set(Bmag, amag);

fmpz_clear(a); fmpz_clear(amag);
fmpz_clear(b); fmpz_clear(bmag);
fmpz_clear(m); fmpz_clear(mmag);
fmpz_clear(w); fmpz_clear(wmag);
fmpz_clear(Amag); fmpz_clear(Bmag);
}

void
arb_bell_fmpz(arb_t res, const fmpz_t n, long prec)
{
fmpz_t a, b, m, mmag, c;
arb_t t;
mag_t bound;

if (fmpz_sgn(n) < 0)
{
arb_zero(res);
return;
}

if (fmpz_fits_si(n))
{
long nn = fmpz_get_si(n);

/* compute exactly if we would be computing at least half the bits
of the exact number */
if (nn < 50 || prec > 0.5 * nn * log(0.7*nn / log(nn)) * 1.442695041)
{
fmpz_init(a);
arith_bell_number(a, nn);
arb_set_round_fmpz(res, a, prec);
fmpz_clear(a);
return;
}
}

fmpz_init(a);
fmpz_init(b);
fmpz_init(m);
fmpz_init(mmag);
fmpz_init(c);
arb_init(t);
mag_init(bound);

arb_bell_find_cutoffs(a, b, m, mmag, n, 1.03 * prec + fmpz_bits(n) + 2);

/* cutoff: n > 2^12 * prec^2 */
fmpz_set_ui(c, prec);
fmpz_mul_ui(c, c, prec);
fmpz_mul_2exp(c, c, 12);

if (fmpz_cmp(n, c) > 0)
arb_bell_sum_taylor(res, n, a, b, mmag, prec + 2);
else
arb_bell_sum_bsplit(res, n, a, b, mmag, prec + 2);

lower_bound(bound, a, n);
arb_add_error_mag(res, bound);

upper_bound(bound, b, n);
arb_add_error_mag(res, bound);

arb_const_e(t, prec + 2);
arb_div(res, res, t, prec);

fmpz_clear(a);
fmpz_clear(b);
fmpz_clear(m);
fmpz_clear(mmag);
fmpz_clear(c);
arb_clear(t);
mag_clear(bound);
}

void
arb_bell_ui(arb_t res, ulong n, long prec)
{
fmpz_t t;
fmpz_init(t);
fmpz_set_ui(t, n);
arb_bell_fmpz(res, t, prec);
fmpz_clear(t);
}

126 changes: 126 additions & 0 deletions arb/bell_sum_bsplit.c
@@ -0,0 +1,126 @@
/*=============================================================================
This file is part of ARB.
ARB is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
ARB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ARB; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=============================================================================*/
/******************************************************************************
Copyright (C) 2015 Fredrik Johansson
******************************************************************************/

#include "arb.h"

static void
bsplit(arb_t P, arb_t Q, const fmpz_t n, const fmpz_t a, const fmpz_t b, long prec)
{
fmpz_t t;
fmpz_init(t);
fmpz_sub(t, b, a);

if (fmpz_sgn(t) <= 0)
{
arb_zero(P);
arb_one(Q);
}
else if (fmpz_cmp_ui(t, 20) < 0)
{
long steps, k;
arb_t u;
arb_init(u);

arb_zero(P);
arb_one(Q);

steps = fmpz_get_si(t);

for (k = steps - 1; k >= 0; k--)
{
fmpz_add_ui(t, a, k);

arb_set_round_fmpz(u, t, prec);
arb_pow_fmpz(u, u, n, prec);
arb_addmul(P, Q, u, prec);

if (!fmpz_is_zero(t))
arb_mul_fmpz(Q, Q, t, prec);
}

arb_clear(u);
}
else
{
fmpz_t m;
arb_t P1, Q2;

fmpz_init(m);
arb_init(P1);
arb_init(Q2);

fmpz_add(m, a, b);
fmpz_tdiv_q_2exp(m, m, 1);

bsplit(P1, Q, n, a, m, prec);
bsplit(P, Q2, n, m, b, prec);

arb_mul(Q, Q, Q2, prec);
arb_addmul(P, P1, Q2, prec);

fmpz_clear(m);
arb_clear(P1);
arb_clear(Q2);
}

fmpz_clear(t);
}

void
arb_bell_sum_bsplit(arb_t res, const fmpz_t n,
const fmpz_t a, const fmpz_t b, const fmpz_t mmag, long prec)
{
if (fmpz_cmp(a, b) >= 0)
{
arb_zero(res);
}
else
{
long wp;
arb_t P, Q;

wp = _fmpz_sub_small(b, a);
wp = FLINT_BIT_COUNT(FLINT_ABS(wp));
wp = prec + fmpz_bits(n) + fmpz_bits(a) + wp;

arb_init(P);
arb_init(Q);

bsplit(P, Q, n, a, b, wp);
arb_div(res, P, Q, wp);

if (!fmpz_is_zero(a))
{
arb_gamma_fmpz(P, a, wp);
arb_div(res, res, P, wp);
}

arb_set_round(res, res, prec);

arb_clear(P);
arb_clear(Q);
}
}

230 changes: 230 additions & 0 deletions arb/bell_sum_taylor.c
@@ -0,0 +1,230 @@
/*=============================================================================
This file is part of ARB.
ARB is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
ARB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ARB; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=============================================================================*/
/******************************************************************************
Copyright (C) 2015 Fredrik Johansson
******************************************************************************/

#include "arb.h"
#include "arb_poly.h"

/* tuning parameter */
#define RADIUS_BITS 3

void
_arb_bell_sum_taylor(arb_t res, const fmpz_t n,
const fmpz_t a, const fmpz_t b, const fmpz_t mmag, long tol)
{
fmpz_t m, r, R, tmp;
mag_t B, C, D, bound;
arb_t t, u;
long wp, k, N;

if (_fmpz_sub_small(b, a) < 5)
{
arb_bell_sum_bsplit(res, n, a, b, mmag, tol);
return;
}

fmpz_init(m);
fmpz_init(r);
fmpz_init(R);
fmpz_init(tmp);

/* r = max(m - a, b - m) */
/* m = a + (b - a) / 2 */
fmpz_sub(r, b, a);
fmpz_cdiv_q_2exp(r, r, 1);
fmpz_add(m, a, r);

fmpz_mul_2exp(R, r, RADIUS_BITS);

mag_init(B);
mag_init(C);
mag_init(D);
mag_init(bound);

arb_init(t);
arb_init(u);

if (fmpz_cmp(R, m) >= 0)
{
mag_inf(C);
mag_inf(D);
}
else
{
/* C = exp(R * |F'(m)| + (1/2) R^2 * (n/(m-R)^2 + 1/(m-R))) */
/* C = exp(R * (|F'(m)| + (1/2) R * (n/(m-R) + 1)/(m-R))) */
/* D = (1/2) R * (n/(m-R) + 1)/(m-R) */
fmpz_sub(tmp, m, R);
mag_set_fmpz(D, n);
mag_div_fmpz(D, D, tmp);
mag_one(C);
mag_add(D, D, C);
mag_div_fmpz(D, D, tmp);
mag_mul_fmpz(D, D, R);
mag_mul_2exp_si(D, D, -1);

/* C = |F'(m)| */
wp = 20 + 1.05 * fmpz_bits(n);
arb_set_fmpz(t, n);
arb_div_fmpz(t, t, m, wp);
fmpz_add_ui(tmp, m, 1);
arb_set_fmpz(u, tmp);
arb_digamma(u, u, wp);
arb_sub(t, t, u, wp);
arb_get_mag(C, t);

/* C = exp(R * (C + D)) */
mag_add(C, C, D);
mag_mul_fmpz(C, C, R);
mag_exp(C, C);
}

if (mag_cmp_2exp_si(C, tol / 4 + 2) > 0)
{
_arb_bell_sum_taylor(res, n, a, m, mmag, tol);
_arb_bell_sum_taylor(t, n, m, b, mmag, tol);
arb_add(res, res, t, 2 * tol);
}
else
{
arb_ptr mx, ser1, ser2, ser3;

/* D = T(m) */
wp = 20 + 1.05 * fmpz_bits(n);
arb_set_fmpz(t, m);
arb_pow_fmpz(t, t, n, wp);
fmpz_add_ui(tmp, m, 1);
arb_gamma_fmpz(u, tmp, wp);
arb_div(t, t, u, wp);
arb_get_mag(D, t);

/* error bound: (b-a) * C * D * B^N / (1 - B), B = r/R */
/* ((b-a) * C * D * 2) * 2^(-N*RADIUS_BITS) */

/* ((b-a) * C * D * 2) */
mag_mul(bound, C, D);
mag_mul_2exp_si(bound, bound, 1);
fmpz_sub(tmp, b, a);
mag_mul_fmpz(bound, bound, tmp);

/* N = (tol + log2((b-a)*C*D*2) - mmag) / RADIUS_BITS */
if (mmag == NULL)
{
/* estimate D ~= 2^mmag */
fmpz_add_ui(tmp, MAG_EXPREF(C), tol);
fmpz_cdiv_q_ui(tmp, tmp, RADIUS_BITS);
}
else
{
fmpz_sub(tmp, MAG_EXPREF(bound), mmag);
fmpz_add_ui(tmp, tmp, tol);
fmpz_cdiv_q_ui(tmp, tmp, RADIUS_BITS);
}

if (fmpz_cmp_ui(tmp, 5 * tol / 4) > 0)
N = 5 * tol / 4;
else if (fmpz_cmp_ui(tmp, 2) < 0)
N = 2;
else
N = fmpz_get_ui(tmp);

/* multiply by 2^(-N*RADIUS_BITS) */
mag_mul_2exp_si(bound, bound, -N * RADIUS_BITS);

mx = _arb_vec_init(2);
ser1 = _arb_vec_init(N);
ser2 = _arb_vec_init(N);
ser3 = _arb_vec_init(N);

/* estimate (this should work for moderate n and tol) */
wp = 1.1 * tol + 1.05 * fmpz_bits(n) + 5;

/* increase precision until convergence */
while (1)
{
/* (m+x)^n / gamma(m+1+x) */
arb_set_fmpz(mx, m);
arb_one(mx + 1);
_arb_poly_log_series(ser1, mx, 2, N, wp);
for (k = 0; k < N; k++)
arb_mul_fmpz(ser1 + k, ser1 + k, n, wp);
arb_add_ui(mx, mx, 1, wp);
_arb_poly_lgamma_series(ser2, mx, 2, N, wp);
_arb_vec_sub(ser1, ser1, ser2, N, wp);
_arb_poly_exp_series(ser3, ser1, N, N, wp);

/* t = a - m, u = b - m */
arb_set_fmpz(t, a);
arb_sub_fmpz(t, t, m, wp);
arb_set_fmpz(u, b);
arb_sub_fmpz(u, u, m, wp);
arb_power_sum_vec(ser1, t, u, N, wp);

arb_zero(res);
for (k = 0; k < N; k++)
arb_addmul(res, ser3 + k, ser1 + k, wp);

if (mmag != NULL)
{
if (_fmpz_sub_small(MAG_EXPREF(arb_radref(res)), mmag) <= -tol)
break;
}
else
{
if (arb_rel_accuracy_bits(res) >= tol)
break;
}

wp = 2 * wp;
}

/* add the series truncation bound */
arb_add_error_mag(res, bound);

_arb_vec_clear(mx, 2);
_arb_vec_clear(ser1, N);
_arb_vec_clear(ser2, N);
_arb_vec_clear(ser3, N);
}

mag_clear(B);
mag_clear(C);
mag_clear(D);
mag_clear(bound);
arb_clear(t);
arb_clear(u);

fmpz_clear(m);
fmpz_clear(r);
fmpz_clear(R);
fmpz_clear(tmp);
}

void
arb_bell_sum_taylor(arb_t res, const fmpz_t n,
const fmpz_t a, const fmpz_t b, const fmpz_t mmag, long prec)
{
_arb_bell_sum_taylor(res, n, a, b, mmag, prec + 5);
}

100 changes: 100 additions & 0 deletions arb/test/t-bell_fmpz.c
@@ -0,0 +1,100 @@
/*=============================================================================
This file is part of ARB.
ARB is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
ARB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ARB; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=============================================================================*/
/******************************************************************************
Copyright (C) 2015 Fredrik Johansson
******************************************************************************/

#include <stdio.h>
#include "arb.h"

int main()
{
long iter;
flint_rand_t state;

printf("bell_fmpz....");
fflush(stdout);

flint_randinit(state);

for (iter = 0; iter < 5000; iter++)
{
arb_t b1, b2;
fmpz_t n;
long prec1, prec2, acc1, acc2;

fmpz_init(n);
arb_init(b1);
arb_init(b2);

if (iter % 100 == 0)
{
fmpz_randtest(n, state, 1 + n_randint(state, 100));
fmpz_abs(n, n);
prec1 = 2 + n_randint(state, 200);
prec2 = 2 + n_randint(state, 200);
}
else
{
fmpz_randtest(n, state, 1 + n_randint(state, 20));
fmpz_abs(n, n);
prec1 = 2 + n_randint(state, 1000);
prec2 = 2 + n_randint(state, 1000);
}

arb_bell_fmpz(b1, n, prec1);
arb_bell_fmpz(b2, n, prec2);

if (!arb_overlaps(b1, b2))
{
printf("FAIL: overlap\n\n");
printf("n = "); fmpz_print(n); printf("\n\n");
printf("b1 = "); arb_printn(b1, 50, 0); printf("\n\n");
printf("b2 = "); arb_printn(b2, 50, 0); printf("\n\n");
abort();
}

acc1 = arb_rel_accuracy_bits(b1);
acc2 = arb_rel_accuracy_bits(b2);

if (acc1 < prec1 - 4 || acc2 < prec2 - 4)
{
printf("FAIL: poor accuracy\n\n");
printf("prec1 = %ld, acc1 = %ld\n", prec1, acc1);
printf("prec2 = %ld, acc2 = %ld\n", prec2, acc2);
printf("n = "); fmpz_print(n); printf("\n\n");
printf("b1 = "); arb_printn(b1, 50, 0); printf("\n\n");
printf("b2 = "); arb_printn(b2, 50, 0); printf("\n\n");
abort();
}

arb_clear(b1);
arb_clear(b2);
fmpz_clear(n);
}

flint_randclear(state);
flint_cleanup();
printf("PASS\n");
return EXIT_SUCCESS;
}

84 changes: 84 additions & 0 deletions arb/test/t-bell_sum_taylor.c
@@ -0,0 +1,84 @@
/*=============================================================================
This file is part of ARB.
ARB is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
ARB is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ARB; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
=============================================================================*/
/******************************************************************************
Copyright (C) 2015 Fredrik Johansson
******************************************************************************/

#include <stdio.h>
#include "arb.h"

int main()
{
long iter;
flint_rand_t state;

printf("bell_sum_taylor....");
fflush(stdout);

flint_randinit(state);

for (iter = 0; iter < 1000; iter++)
{
arb_t s1, s2;
fmpz_t a, b, n;
long prec;

arb_init(s1);
arb_init(s2);
fmpz_init(a);
fmpz_init(b);
fmpz_init(n);

prec = 2 + n_randint(state, 300);
fmpz_randtest_unsigned(n, state, 1 + n_randint(state, 100));
fmpz_randtest_unsigned(a, state, 1 + n_randint(state, 100));
fmpz_add_ui(b, a, n_randint(state, 100));

arb_bell_sum_bsplit(s1, n, a, b, NULL, prec);
arb_bell_sum_taylor(s2, n, a, b, NULL, prec);

if (!arb_overlaps(s1, s2) || (arb_rel_accuracy_bits(s1) < prec - 4)
|| (arb_rel_accuracy_bits(s2) < prec - 4))
{
printf("FAIL: overlap or accuracy\n\n");
printf("prec = %ld\n\n", prec);
printf("n = "); fmpz_print(n); printf("\n\n");
printf("a = "); fmpz_print(a); printf("\n\n");
printf("b = "); fmpz_print(b); printf("\n\n");
printf("s1 = "); arb_printn(s1, 100, 0); printf("\n\n");
printf("s2 = "); arb_printn(s2, 100, 0); printf("\n\n");
abort();
}

arb_clear(s1);
arb_clear(s2);
fmpz_clear(a);
fmpz_clear(b);
fmpz_clear(n);
}

flint_randclear(state);
flint_cleanup();
printf("PASS\n");
return EXIT_SUCCESS;
}

17 changes: 17 additions & 0 deletions doc/source/arb.rst
Expand Up @@ -1219,6 +1219,23 @@ Other special functions
`a = U_n(x), b = U_{n-1}(x)`.
Aliasing between *a*, *b* and *x* is not permitted.

.. function:: void arb_bell_sum_bsplit(arb_t res, const fmpz_t n, const fmpz_t a, const fmpz_t b, const fmpz_t mmag, long prec)

.. function:: void arb_bell_sum_taylor(arb_t res, const fmpz_t n, const fmpz_t a, const fmpz_t b, const fmpz_t mmag, long prec)

Helper functions for Bell numbers, evaluating the sum
`\sum_{k=a}^{b-1} k^n / k!`. If *mmag* is non-NULL, it may be used
to indicate that the target error tolerance should be
`2^{mmag - prec}`.

.. function:: void arb_bell_fmpz(arb_t res, const fmpz_t n, long prec)

.. function:: void arb_bell_ui(arb_t res, ulong n, long prec)

Sets *res* to the Bell number `B_n`. If the number is too large to
fit exactly in *prec* bits, a numerical approximation is computed
efficiently.

Internals for computing elementary functions
-------------------------------------------------------------------------------

Expand Down