int main() { slong iter; flint_rand_t state; flint_printf("bell_sum_taylor...."); fflush(stdout); flint_randinit(state); for (iter = 0; iter < 1000 * arb_test_multiplier(); iter++) { arb_t s1, s2; fmpz_t a, b, n; slong 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)) { flint_printf("FAIL: overlap or accuracy\n\n"); flint_printf("prec = %wd\n\n", prec); flint_printf("n = "); fmpz_print(n); flint_printf("\n\n"); flint_printf("a = "); fmpz_print(a); flint_printf("\n\n"); flint_printf("b = "); fmpz_print(b); flint_printf("\n\n"); flint_printf("s1 = "); arb_printn(s1, 100, 0); flint_printf("\n\n"); flint_printf("s2 = "); arb_printn(s2, 100, 0); flint_printf("\n\n"); flint_abort(); } arb_clear(s1); arb_clear(s2); fmpz_clear(a); fmpz_clear(b); fmpz_clear(n); } flint_randclear(state); flint_cleanup(); flint_printf("PASS\n"); return EXIT_SUCCESS; }
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); }