/* Differential equation for F(a,b,c,y+z): (y+z)(y-1+z) F''(z) + ((y+z)(a+b+1) - c) F'(z) + a b F(z) = 0 Coefficients in the Taylor series are bounded by A * binomial(N+k, k) * nu^k using the Cauchy-Kovalevskaya majorant method. See J. van der Hoeven, "Fast evaluation of holonomic functions near and in regular singularities" */ static void bound(mag_t A, mag_t nu, mag_t N, const acb_t a, const acb_t b, const acb_t c, const acb_t y, const acb_t f0, const acb_t f1) { mag_t M0, M1, t, u; acb_t d; acb_init(d); mag_init(M0); mag_init(M1); mag_init(t); mag_init(u); /* nu = max(1/|y-1|, 1/|y|) = 1/min(|y-1|, |y|) */ acb_get_mag_lower(t, y); acb_sub_ui(d, y, 1, MAG_BITS); acb_get_mag_lower(u, d); mag_min(t, t, u); mag_one(u); mag_div(nu, u, t); /* M0 = 2 nu |ab| */ acb_get_mag(t, a); acb_get_mag(u, b); mag_mul(M0, t, u); mag_mul(M0, M0, nu); mag_mul_2exp_si(M0, M0, 1); /* M1 = 2 nu |(a+b+1)y-c| + 2|a+b+1| */ acb_add(d, a, b, MAG_BITS); acb_add_ui(d, d, 1, MAG_BITS); acb_get_mag(t, d); acb_mul(d, d, y, MAG_BITS); acb_sub(d, d, c, MAG_BITS); acb_get_mag(u, d); mag_mul(u, u, nu); mag_add(M1, t, u); mag_mul_2exp_si(M1, M1, 1); /* N = max(sqrt(2 M0), 2 M1) / nu */ mag_mul_2exp_si(M0, M0, 1); mag_sqrt(M0, M0); mag_mul_2exp_si(M1, M1, 1); mag_max(N, M0, M1); mag_div(N, N, nu); /* A = max(|f0|, |f1| / (nu (N+1)) */ acb_get_mag(t, f0); acb_get_mag(u, f1); mag_div(u, u, nu); mag_div(u, u, N); /* upper bound for dividing by N+1 */ mag_max(A, t, u); acb_clear(d); mag_clear(M0); mag_clear(M1); mag_clear(t); mag_clear(u); }
static void acb_rising_get_mag2_right(mag_t bound, const arb_t a, const arb_t b, ulong n) { mag_t t, u; ulong k; mag_init(t); mag_init(u); arb_get_mag(t, a); arb_get_mag(u, b); mag_mul(bound, t, t); mag_addmul(bound, u, u); mag_set(u, bound); mag_mul_2exp_si(t, t, 1); for (k = 1; k < n; k++) { mag_add_ui_2exp_si(u, u, 2 * k - 1, 0); mag_add(u, u, t); mag_mul(bound, bound, u); } mag_clear(t); mag_clear(u); }
void mag_exp_tail(mag_t z, const mag_t x, ulong N) { if (N == 0 || mag_is_inf(x)) { mag_exp(z, x); } else if (mag_is_zero(x)) { mag_zero(z); } else { mag_t t; mag_init(t); mag_set_ui_2exp_si(t, N, -1); /* bound by geometric series when N >= 2*x <=> N/2 >= x */ if (mag_cmp(t, x) >= 0) { /* 2 c^N / N! */ mag_pow_ui(t, x, N); mag_rfac_ui(z, N); mag_mul(z, z, t); mag_mul_2exp_si(z, z, 1); } else { mag_exp(z, x); } mag_clear(t); } }
static void arb_sqrt1pm1_tiny(arb_t r, const arb_t z, slong prec) { mag_t b, c; arb_t t; mag_init(b); mag_init(c); arb_init(t); /* if |z| < 1, then |(sqrt(1+z)-1) - (z/2-z^2/8)| <= |z|^3/(1-|z|)/16 */ arb_get_mag(b, z); mag_one(c); mag_sub_lower(c, c, b); mag_pow_ui(b, b, 3); mag_div(b, b, c); mag_mul_2exp_si(b, b, -4); arb_mul(t, z, z, prec); arb_mul_2exp_si(t, t, -2); arb_sub(r, z, t, prec); arb_mul_2exp_si(r, r, -1); if (mag_is_finite(b)) arb_add_error_mag(r, b); else arb_indeterminate(r); mag_clear(b); mag_clear(c); arb_clear(t); }
int main() { slong iter; flint_rand_t state; flint_printf("mul_2exp_si...."); fflush(stdout); flint_randinit(state); for (iter = 0; iter < 100000 * arb_test_multiplier(); iter++) { fmpr_t x, y, z; mag_t xb, yb; slong e; fmpr_init(x); fmpr_init(y); fmpr_init(z); mag_init(xb); mag_init(yb); mag_randtest_special(xb, state, 100); e = z_randtest(state); mag_get_fmpr(x, xb); mag_mul_2exp_si(yb, xb, e); fmpr_mul_2exp_si(y, x, e); mag_get_fmpr(z, yb); MAG_CHECK_BITS(yb) if (!fmpr_equal(z, y)) { flint_printf("FAIL\n\n"); flint_printf("x = "); fmpr_printd(x, 15); flint_printf("\n\n"); flint_printf("y = "); fmpr_printd(y, 15); flint_printf("\n\n"); flint_printf("z = "); fmpr_printd(z, 15); flint_printf("\n\n"); abort(); } fmpr_clear(x); fmpr_clear(y); fmpr_clear(z); mag_clear(xb); mag_clear(yb); } flint_randclear(state); flint_cleanup(); flint_printf("PASS\n"); return EXIT_SUCCESS; }
static void _acb_sqr_slow(acb_t z, const acb_t x, slong prec) { int inexact; mag_t am, bm, er, fr; mag_init_set_arf(am, arb_midref(a)); mag_init_set_arf(bm, arb_midref(b)); mag_init(er); mag_init(fr); mag_addmul(er, am, ar); mag_addmul(er, bm, br); mag_mul_2exp_si(er, er, 1); mag_addmul(er, ar, ar); mag_addmul(er, br, br); mag_addmul(fr, bm, ar); mag_addmul(fr, am, br); mag_addmul(fr, ar, br); mag_mul_2exp_si(fr, fr, 1); inexact = arf_complex_sqr(arb_midref(e), arb_midref(f), arb_midref(a), arb_midref(b), prec, ARB_RND); if (inexact & 1) arf_mag_add_ulp(arb_radref(e), er, arb_midref(e), prec); else mag_swap(arb_radref(e), er); if (inexact & 2) arf_mag_add_ulp(arb_radref(f), fr, arb_midref(f), prec); else mag_swap(arb_radref(f), fr); mag_clear(am); mag_clear(bm); mag_clear(er); mag_clear(fr); }
void acb_hypgeom_erf_propagated_error(mag_t re, mag_t im, const acb_t z) { mag_t x, y; mag_init(x); mag_init(y); /* |exp(-(x+y)^2)| = exp(y^2-x^2) */ arb_get_mag(y, acb_imagref(z)); mag_mul(y, y, y); arb_get_mag_lower(x, acb_realref(z)); mag_mul_lower(x, x, x); if (mag_cmp(y, x) >= 0) { mag_sub(re, y, x); mag_exp(re, re); } else { mag_sub_lower(re, x, y); mag_expinv(re, re); } /* Radius. */ mag_hypot(x, arb_radref(acb_realref(z)), arb_radref(acb_imagref(z))); mag_mul(re, re, x); /* 2/sqrt(pi) < 289/256 */ mag_mul_ui(re, re, 289); mag_mul_2exp_si(re, re, -8); if (arb_is_zero(acb_imagref(z))) { /* todo: could bound magnitude even for complex numbers */ mag_set_ui(y, 2); mag_min(re, re, y); mag_zero(im); } else if (arb_is_zero(acb_realref(z))) { mag_swap(im, re); mag_zero(re); } else { mag_set(im, re); } mag_clear(x); mag_clear(y); }
void _arb_sinc_derivative_bound(mag_t d, const arb_t x) { /* |f'(x)| < min(arb_get_mag(x), 1) / 2 */ mag_t r, one; mag_init(r); mag_init(one); arb_get_mag(r, x); mag_one(one); mag_min(d, r, one); mag_mul_2exp_si(d, d, -1); mag_clear(r); mag_clear(one); }
void arb_bernoulli_fmpz(arb_t res, const fmpz_t n, slong prec) { if (fmpz_cmp_ui(n, UWORD_MAX) <= 0) { if (fmpz_sgn(n) >= 0) arb_bernoulli_ui(res, fmpz_get_ui(n), prec); else arb_zero(res); } else if (fmpz_is_odd(n)) { arb_zero(res); } else { arb_t t; slong wp; arb_init(t); wp = prec + 2 * fmpz_bits(n); /* zeta(n) ~= 1 */ arf_one(arb_midref(res)); mag_one(arb_radref(res)); mag_mul_2exp_si(arb_radref(res), arb_radref(res), WORD_MIN); /* |B_n| = 2 * n! / (2*pi)^n * zeta(n) */ arb_gamma_fmpz(t, n, wp); arb_mul_fmpz(t, t, n, wp); arb_mul(res, res, t, wp); arb_const_pi(t, wp); arb_mul_2exp_si(t, t, 1); arb_pow_fmpz(t, t, n, wp); arb_div(res, res, t, prec); arb_mul_2exp_si(res, res, 1); if (fmpz_fdiv_ui(n, 4) == 0) arb_neg(res, res); arb_clear(t); } }
static void phase(acb_t res, const arb_t re, const arb_t im) { if (arb_is_nonnegative(re) || arb_is_negative(im)) { acb_one(res); } else if (arb_is_negative(re) && arb_is_nonnegative(im)) { acb_set_si(res, -3); } else { arb_zero(acb_imagref(res)); /* -1 +/- 2 */ arf_set_si(arb_midref(acb_realref(res)), -1); mag_one(arb_radref(acb_realref(res))); mag_mul_2exp_si(arb_radref(acb_realref(res)), arb_radref(acb_realref(res)), 1); } }
void acb_inv(acb_t res, const acb_t z, slong prec) { mag_t am, bm; slong hprec; #define a arb_midref(acb_realref(z)) #define b arb_midref(acb_imagref(z)) #define x arb_radref(acb_realref(z)) #define y arb_radref(acb_imagref(z)) /* choose precision for the floating-point approximation of a^2+b^2 so that the double rounding result in less than 2 ulp error; also use at least MAG_BITS bits since the value will be recycled for error bounds */ hprec = FLINT_MAX(prec + 3, MAG_BITS); if (arb_is_zero(acb_imagref(z))) { arb_inv(acb_realref(res), acb_realref(z), prec); arb_zero(acb_imagref(res)); return; } if (arb_is_zero(acb_realref(z))) { arb_inv(acb_imagref(res), acb_imagref(z), prec); arb_neg(acb_imagref(res), acb_imagref(res)); arb_zero(acb_realref(res)); return; } if (!acb_is_finite(z)) { acb_indeterminate(res); return; } if (mag_is_zero(x) && mag_is_zero(y)) { int inexact; arf_t a2b2; arf_init(a2b2); inexact = arf_sosq(a2b2, a, b, hprec, ARF_RND_DOWN); if (arf_is_special(a2b2)) { acb_indeterminate(res); } else { _arb_arf_div_rounded_den(acb_realref(res), a, a2b2, inexact, prec); _arb_arf_div_rounded_den(acb_imagref(res), b, a2b2, inexact, prec); arf_neg(arb_midref(acb_imagref(res)), arb_midref(acb_imagref(res))); } arf_clear(a2b2); return; } mag_init(am); mag_init(bm); /* first bound |a|-x, |b|-y */ arb_get_mag_lower(am, acb_realref(z)); arb_get_mag_lower(bm, acb_imagref(z)); if ((mag_is_zero(am) && mag_is_zero(bm))) { acb_indeterminate(res); } else { /* The propagated error in the real part is given exactly by (a+x')/((a+x')^2+(b+y'))^2 - a/(a^2+b^2) = P / Q, P = [(b^2-a^2) x' - a (x'^2+y'^2 + 2y'b)] Q = [(a^2+b^2)((a+x')^2+(b+y')^2)] where |x'| <= x and |y'| <= y, and analogously for the imaginary part. */ mag_t t, u, v, w; arf_t a2b2; int inexact; mag_init(t); mag_init(u); mag_init(v); mag_init(w); arf_init(a2b2); inexact = arf_sosq(a2b2, a, b, hprec, ARF_RND_DOWN); /* compute denominator */ /* t = (|a|-x)^2 + (|b|-x)^2 (lower bound) */ mag_mul_lower(t, am, am); mag_mul_lower(u, bm, bm); mag_add_lower(t, t, u); /* u = a^2 + b^2 (lower bound) */ arf_get_mag_lower(u, a2b2); /* t = ((|a|-x)^2 + (|b|-x)^2)(a^2 + b^2) (lower bound) */ mag_mul_lower(t, t, u); /* compute numerator */ /* real: |a^2-b^2| x + |a| ((x^2 + y^2) + 2 |b| y)) */ /* imag: |a^2-b^2| y + |b| ((x^2 + y^2) + 2 |a| x)) */ /* am, bm = upper bounds for a, b */ arf_get_mag(am, a); arf_get_mag(bm, b); /* v = x^2 + y^2 */ mag_mul(v, x, x); mag_addmul(v, y, y); /* u = |a| ((x^2 + y^2) + 2 |b| y) */ mag_mul_2exp_si(u, bm, 1); mag_mul(u, u, y); mag_add(u, u, v); mag_mul(u, u, am); /* v = |b| ((x^2 + y^2) + 2 |a| x) */ mag_mul_2exp_si(w, am, 1); mag_addmul(v, w, x); mag_mul(v, v, bm); /* w = |b^2 - a^2| (upper bound) */ if (arf_cmpabs(a, b) >= 0) mag_mul(w, am, am); else mag_mul(w, bm, bm); mag_addmul(u, w, x); mag_addmul(v, w, y); mag_div(arb_radref(acb_realref(res)), u, t); mag_div(arb_radref(acb_imagref(res)), v, t); _arb_arf_div_rounded_den_add_err(acb_realref(res), a, a2b2, inexact, prec); _arb_arf_div_rounded_den_add_err(acb_imagref(res), b, a2b2, inexact, prec); arf_neg(arb_midref(acb_imagref(res)), arb_midref(acb_imagref(res))); mag_clear(t); mag_clear(u); mag_clear(v); mag_clear(w); arf_clear(a2b2); } mag_clear(am); mag_clear(bm); #undef a #undef b #undef x #undef y }
void acb_hypgeom_2f1_continuation(acb_t res, acb_t res1, const acb_t a, const acb_t b, const acb_t c, const acb_t y, const acb_t z, const acb_t f0, const acb_t f1, long prec) { mag_t A, nu, N, w, err, err1, R, T, goal; acb_t x; long j, k; mag_init(A); mag_init(nu); mag_init(N); mag_init(err); mag_init(err1); mag_init(w); mag_init(R); mag_init(T); mag_init(goal); acb_init(x); bound(A, nu, N, a, b, c, y, f0, f1); acb_sub(x, z, y, prec); /* |T(k)| <= A * binomial(N+k, k) * nu^k * |x|^k */ acb_get_mag(w, x); mag_mul(w, w, nu); /* w = nu |x| */ mag_mul_2exp_si(goal, A, -prec-2); /* bound for T(0) */ mag_set(T, A); mag_inf(R); for (k = 1; k < 100 * prec; k++) { /* T(k) = T(k) * R(k), R(k) = (N+k)/k * w = (1 + N/k) w */ mag_div_ui(R, N, k); mag_add_ui(R, R, 1); mag_mul(R, R, w); /* T(k) */ mag_mul(T, T, R); if (mag_cmp(T, goal) <= 0 && mag_cmp_2exp_si(R, 0) < 0) break; } /* T(k) [1 + R + R^2 + R^3 + ...] */ mag_geom_series(err, R, 0); mag_mul(err, T, err); /* Now compute T, R for the derivative */ /* Coefficients are A * (k+1) * binomial(N+k+1, k+1) */ mag_add_ui(T, N, 1); mag_mul(T, T, A); mag_inf(R); for (j = 1; j <= k; j++) { mag_add_ui(R, N, k + 1); mag_div_ui(R, R, k); mag_mul(R, R, w); mag_mul(T, T, R); } mag_geom_series(err1, R, 0); mag_mul(err1, T, err1); if (mag_is_inf(err)) { acb_indeterminate(res); acb_indeterminate(res1); } else { evaluate_sum(res, res1, a, b, c, y, x, f0, f1, k, prec); acb_add_error_mag(res, err); acb_add_error_mag(res1, err1); } mag_clear(A); mag_clear(nu); mag_clear(N); mag_clear(err); mag_clear(err1); mag_clear(w); mag_clear(R); mag_clear(T); mag_clear(goal); acb_clear(x); }
/* derivatives: |8/sqrt(pi) sin(2z^2)|, |8/sqrt(pi) cos(2z^2)| <= 5 exp(4|xy|) */ void acb_hypgeom_fresnel_erf_error(acb_t res1, acb_t res2, const acb_t z, slong prec) { mag_t re; mag_t im; acb_t zmid; mag_init(re); mag_init(im); acb_init(zmid); if (arf_cmpabs_ui(arb_midref(acb_realref(z)), 1000) < 0 && arf_cmpabs_ui(arb_midref(acb_imagref(z)), 1000) < 0) { arb_get_mag(re, acb_realref(z)); arb_get_mag(im, acb_imagref(z)); mag_mul(re, re, im); mag_mul_2exp_si(re, re, 2); mag_exp(re, re); mag_mul_ui(re, re, 5); } else { arb_t t; arb_init(t); arb_mul(t, acb_realref(z), acb_imagref(z), prec); arb_abs(t, t); arb_mul_2exp_si(t, t, 2); arb_exp(t, t, prec); arb_get_mag(re, t); mag_mul_ui(re, re, 5); arb_clear(t); } mag_hypot(im, arb_radref(acb_realref(z)), arb_radref(acb_imagref(z))); mag_mul(re, re, im); if (arb_is_zero(acb_imagref(z))) { mag_set_ui(im, 8); /* For real x, |S(x)| < 4, |C(x)| < 4. */ mag_min(re, re, im); mag_zero(im); } else if (arb_is_zero(acb_realref(z))) { mag_set_ui(im, 8); mag_min(im, re, im); mag_zero(re); } else { mag_set(im, re); } arf_set(arb_midref(acb_realref(zmid)), arb_midref(acb_realref(z))); arf_set(arb_midref(acb_imagref(zmid)), arb_midref(acb_imagref(z))); acb_hypgeom_fresnel_erf(res1, res2, zmid, prec); if (res1 != NULL) { arb_add_error_mag(acb_realref(res1), re); arb_add_error_mag(acb_imagref(res1), im); } if (res2 != NULL) { arb_add_error_mag(acb_realref(res2), re); arb_add_error_mag(acb_imagref(res2), im); } mag_clear(re); mag_clear(im); acb_clear(zmid); }
void _arb_sin_cos_generic(arb_t s, arb_t c, const arf_t x, const mag_t xrad, slong prec) { int want_sin, want_cos; slong maglim; want_sin = (s != NULL); want_cos = (c != NULL); if (arf_is_zero(x) && mag_is_zero(xrad)) { if (want_sin) arb_zero(s); if (want_cos) arb_one(c); return; } if (!arf_is_finite(x) || !mag_is_finite(xrad)) { if (arf_is_nan(x)) { if (want_sin) arb_indeterminate(s); if (want_cos) arb_indeterminate(c); } else { if (want_sin) arb_zero_pm_one(s); if (want_cos) arb_zero_pm_one(c); } return; } maglim = FLINT_MAX(65536, 4 * prec); if (mag_cmp_2exp_si(xrad, -16) > 0 || arf_cmpabs_2exp_si(x, maglim) > 0) { _arb_sin_cos_wide(s, c, x, xrad, prec); return; } if (arf_cmpabs_2exp_si(x, -(prec/2) - 2) <= 0) { mag_t t, u, v; mag_init(t); mag_init(u); mag_init(v); arf_get_mag(t, x); mag_add(t, t, xrad); mag_mul(u, t, t); /* |sin(z)-z| <= z^3/6 */ if (want_sin) { arf_set(arb_midref(s), x); mag_set(arb_radref(s), xrad); arb_set_round(s, s, prec); mag_mul(v, u, t); mag_div_ui(v, v, 6); arb_add_error_mag(s, v); } /* |cos(z)-1| <= z^2/2 */ if (want_cos) { arf_one(arb_midref(c)); mag_mul_2exp_si(arb_radref(c), u, -1); } mag_clear(t); mag_clear(u); mag_clear(v); return; } if (mag_is_zero(xrad)) { arb_sin_cos_arf_generic(s, c, x, prec); } else { mag_t t; slong exp, radexp; mag_init_set(t, xrad); exp = arf_abs_bound_lt_2exp_si(x); radexp = MAG_EXP(xrad); if (radexp < MAG_MIN_LAGOM_EXP || radexp > MAG_MAX_LAGOM_EXP) radexp = MAG_MIN_LAGOM_EXP; if (want_cos && exp < -2) prec = FLINT_MIN(prec, 20 - FLINT_MAX(exp, radexp) - radexp); else prec = FLINT_MIN(prec, 20 - radexp); arb_sin_cos_arf_generic(s, c, x, prec); /* todo: could use quadratic bound */ if (want_sin) mag_add(arb_radref(s), arb_radref(s), t); if (want_cos) mag_add(arb_radref(c), arb_radref(c), t); mag_clear(t); } }
void _acb_lambertw(acb_t res, const acb_t z, const acb_t ez1, const fmpz_t k, int flags, slong prec) { slong goal, ebits, ebits2, ls, lt; const fmpz * expo; /* Estimated accuracy goal. */ /* todo: account for exponent bits and bits in k. */ goal = acb_rel_accuracy_bits(z); goal = FLINT_MAX(goal, 10); goal = FLINT_MIN(goal, prec); /* Handle tiny z directly. For k >= 2, |c_k| <= 4^k / 16. */ if (fmpz_is_zero(k) && arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), -goal / 2) < 0 && arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), -goal / 2) < 0) { mag_t err; mag_init(err); acb_get_mag(err, z); mag_mul_2exp_si(err, err, 2); acb_set(res, z); acb_submul(res, res, res, prec); mag_geom_series(err, err, 3); mag_mul_2exp_si(err, err, -4); acb_add_error_mag(res, err); mag_clear(err); return; } if (arf_cmpabs(arb_midref(acb_realref(z)), arb_midref(acb_imagref(z))) >= 0) expo = ARF_EXPREF(arb_midref(acb_realref(z))); else expo = ARF_EXPREF(arb_midref(acb_imagref(z))); ebits = fmpz_bits(expo); /* ebits ~= log2(|log(z) + 2 pi i k|) */ /* ebits2 ~= log2(log(log(z))) */ ebits = FLINT_MAX(ebits, fmpz_bits(k)); ebits = FLINT_MAX(ebits, 1) - 1; ebits2 = FLINT_BIT_COUNT(ebits); ebits2 = FLINT_MAX(ebits2, 1) - 1; /* We gain accuracy from the exponent when W ~ log - log log */ if (fmpz_sgn(expo) > 0 || (fmpz_sgn(expo) < 0 && !fmpz_is_zero(k))) { goal += ebits - ebits2; goal = FLINT_MAX(goal, 10); goal = FLINT_MIN(goal, prec); /* The asymptotic series with truncation L, M gives us about t - max(2+lt+L*(2+ls), M*(2+lt)) bits of accuracy where ls = -ebits, lt = ebits2 - ebits. */ ls = 2 - ebits; lt = 2 + ebits2 - ebits; if (ebits - FLINT_MAX(lt + 1*ls, 1*lt) > goal) { acb_lambertw_asymp(res, z, k, 1, 1, goal); acb_set_round(res, res, prec); return; } else if (ebits - FLINT_MAX(lt + 3*ls, 5*lt) > goal) { acb_lambertw_asymp(res, z, k, 3, 5, goal); acb_set_round(res, res, prec); return; } } /* Extremely close to the branch point at -1/e, use the series expansion directly. */ if (acb_lambertw_try_near_branch_point(res, z, ez1, k, flags, goal)) { acb_set_round(res, res, prec); return; } /* compute union of both sides */ if (acb_lambertw_branch_crossing(z, ez1, k)) { acb_t za, zb, eza1, ezb1; fmpz_t kk; acb_init(za); acb_init(zb); acb_init(eza1); acb_init(ezb1); fmpz_init(kk); fmpz_neg(kk, k); acb_set(za, z); acb_conj(zb, z); arb_nonnegative_part(acb_imagref(za), acb_imagref(za)); arb_nonnegative_part(acb_imagref(zb), acb_imagref(zb)); acb_set(eza1, ez1); acb_conj(ezb1, ez1); arb_nonnegative_part(acb_imagref(eza1), acb_imagref(eza1)); arb_nonnegative_part(acb_imagref(ezb1), acb_imagref(ezb1)); /* Check series expansion again, because now there is no crossing. */ if (!acb_lambertw_try_near_branch_point(res, za, eza1, k, flags, goal)) acb_lambertw_cleared_cut_fix_small(za, za, eza1, k, flags, goal); if (!acb_lambertw_try_near_branch_point(res, zb, ezb1, kk, flags, goal)) acb_lambertw_cleared_cut_fix_small(zb, zb, ezb1, kk, flags, goal); acb_conj(zb, zb); acb_union(res, za, zb, prec); acb_clear(za); acb_clear(zb); acb_clear(eza1); acb_clear(ezb1); fmpz_clear(kk); } else { acb_lambertw_cleared_cut_fix_small(res, z, ez1, k, flags, goal); acb_set_round(res, res, prec); } }
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_mat_exp(arb_mat_t B, const arb_mat_t A, slong prec) { slong i, j, dim, wp, N, q, r; mag_t norm, err; arb_mat_t T; dim = arb_mat_nrows(A); if (dim != arb_mat_ncols(A)) { flint_printf("arb_mat_exp: a square matrix is required!\n"); abort(); } if (dim == 0) { return; } else if (dim == 1) { arb_exp(arb_mat_entry(B, 0, 0), arb_mat_entry(A, 0, 0), prec); return; } wp = prec + 3 * FLINT_BIT_COUNT(prec); mag_init(norm); mag_init(err); arb_mat_init(T, dim, dim); arb_mat_bound_inf_norm(norm, A); if (mag_is_zero(norm)) { arb_mat_one(B); } else { q = pow(wp, 0.25); /* wanted magnitude */ if (mag_cmp_2exp_si(norm, 2 * wp) > 0) /* too big */ r = 2 * wp; else if (mag_cmp_2exp_si(norm, -q) < 0) /* tiny, no need to reduce */ r = 0; else r = FLINT_MAX(0, q + MAG_EXP(norm)); /* reduce to magnitude 2^(-r) */ arb_mat_scalar_mul_2exp_si(T, A, -r); mag_mul_2exp_si(norm, norm, -r); N = _arb_mat_exp_choose_N(norm, wp); mag_exp_tail(err, norm, N); _arb_mat_exp_taylor(B, T, N, wp); for (i = 0; i < dim; i++) for (j = 0; j < dim; j++) arb_add_error_mag(arb_mat_entry(B, i, j), err); for (i = 0; i < r; i++) { arb_mat_mul(T, B, B, wp); arb_mat_swap(T, B); } for (i = 0; i < dim; i++) for (j = 0; j < dim; j++) arb_set_round(arb_mat_entry(B, i, j), arb_mat_entry(B, i, j), prec); } mag_clear(norm); mag_clear(err); arb_mat_clear(T); }
void acb_modular_theta_const_sum(acb_t theta2, acb_t theta3, acb_t theta4, const acb_t q, long prec) { mag_t qmag, err; double log2q_approx; int is_real, is_real_or_imag; long N; mag_init(qmag); mag_init(err); acb_get_mag(qmag, q); log2q_approx = mag_get_log2_d_approx(qmag); is_real = arb_is_zero(acb_imagref(q)); is_real_or_imag = is_real || arb_is_zero(acb_realref(q)); if (log2q_approx >= 0.0) { N = 1; mag_inf(err); } else { N = 0; while (0.05 * N * N < prec) { if (log2q_approx * ((N+2)*(N+2)/4) < -prec - 2) break; N++; } N = (N+2)*(N+2)/4; mag_geom_series(err, qmag, N); mag_mul_2exp_si(err, err, 1); /* each term is taken twice */ if (mag_is_inf(err)) N = 1; } if (N < 1800) acb_modular_theta_const_sum_basecase(theta2, theta3, theta4, q, N, prec); else acb_modular_theta_const_sum_rs(theta2, theta3, theta4, q, N, prec); if (is_real_or_imag) arb_add_error_mag(acb_realref(theta2), err); else acb_add_error_mag(theta2, err); if (is_real) { arb_add_error_mag(acb_realref(theta3), err); arb_add_error_mag(acb_realref(theta4), err); } else { acb_add_error_mag(theta3, err); acb_add_error_mag(theta4, err); } mag_clear(qmag); mag_clear(err); }
/* computes the factors that are independent of n (all are upper bounds) */ void acb_hypgeom_u_asymp_bound_factors(int * R, mag_t alpha, mag_t nu, mag_t sigma, mag_t rho, mag_t zinv, const acb_t a, const acb_t b, const acb_t z) { mag_t r, u, zre, zim, zlo, sigma_prime; acb_t t; mag_init(r); mag_init(u); mag_init(zre); mag_init(zim); mag_init(zlo); mag_init(sigma_prime); acb_init(t); /* lower bounds for |re(z)|, |im(z)|, |z| */ arb_get_mag_lower(zre, acb_realref(z)); arb_get_mag_lower(zim, acb_imagref(z)); acb_get_mag_lower(zlo, z); /* todo: hypot */ /* upper bound for 1/|z| */ mag_one(u); mag_div(zinv, u, zlo); /* upper bound for r = |b - 2a| */ acb_mul_2exp_si(t, a, 1); acb_sub(t, b, t, MAG_BITS); acb_get_mag(r, t); /* determine region */ *R = 0; if (mag_cmp(zlo, r) >= 0) { int znonneg = arb_is_nonnegative(acb_realref(z)); if (znonneg && mag_cmp(zre, r) >= 0) { *R = 1; } else if (mag_cmp(zim, r) >= 0 || znonneg) { *R = 2; } else { mag_mul_2exp_si(u, r, 1); if (mag_cmp(zlo, u) >= 0) *R = 3; } } if (R == 0) { mag_inf(alpha); mag_inf(nu); mag_inf(sigma); mag_inf(rho); } else { /* sigma = |(b-2a)/z| */ mag_mul(sigma, r, zinv); /* nu = (1/2 + 1/2 sqrt(1-4 sigma^2))^(-1/2) <= 1 + 2 sigma^2 */ if (mag_cmp_2exp_si(sigma, -1) <= 0) { mag_mul(nu, sigma, sigma); mag_mul_2exp_si(nu, nu, 1); mag_one(u); mag_add(nu, nu, u); } else { mag_inf(nu); } /* modified sigma for alpha, beta, rho when in R3 */ if (*R == 3) mag_mul(sigma_prime, sigma, nu); else mag_set(sigma_prime, sigma); /* alpha = 1/(1-sigma') */ mag_one(alpha); mag_sub_lower(alpha, alpha, sigma_prime); mag_one(u); mag_div(alpha, u, alpha); /* rho = |2a^2-2ab+b|/2 + sigma'*(1+sigma'/4)/(1-sigma')^2 */ mag_mul_2exp_si(rho, sigma_prime, -2); mag_one(u); mag_add(rho, rho, u); mag_mul(rho, rho, sigma_prime); mag_mul(rho, rho, alpha); mag_mul(rho, rho, alpha); acb_sub(t, a, b, MAG_BITS); acb_mul(t, t, a, MAG_BITS); acb_mul_2exp_si(t, t, 1); acb_add(t, t, b, MAG_BITS); acb_get_mag(u, t); mag_mul_2exp_si(u, u, -1); mag_add(rho, rho, u); } mag_clear(r); mag_clear(u); mag_clear(zre); mag_clear(zim); mag_clear(zlo); mag_clear(sigma_prime); acb_clear(t); }
void acb_hypgeom_u_asymp(acb_t res, const acb_t a, const acb_t b, const acb_t z, slong n, slong prec) { acb_struct aa[3]; acb_t s, t, w, winv; int R, p, q, is_real, is_terminating; slong n_terminating; if (!acb_is_finite(a) || !acb_is_finite(b) || !acb_is_finite(z)) { acb_indeterminate(res); return; } acb_init(aa); acb_init(aa + 1); acb_init(aa + 2); acb_init(s); acb_init(t); acb_init(w); acb_init(winv); is_terminating = 0; n_terminating = WORD_MAX; /* special case, for incomplete gamma [todo: also when they happen to be exact and with difference 1...] */ if (a == b) { acb_set(aa, a); p = 1; q = 0; } else { acb_set(aa, a); acb_sub(aa + 1, a, b, prec); acb_add_ui(aa + 1, aa + 1, 1, prec); acb_one(aa + 2); p = 2; q = 1; } if (acb_is_nonpositive_int(aa)) { is_terminating = 1; if (arf_cmpabs_ui(arb_midref(acb_realref(aa)), prec) < 0) n_terminating = 1 - arf_get_si(arb_midref(acb_realref(aa)), ARF_RND_DOWN); } if (p == 2 && acb_is_nonpositive_int(aa + 1)) { is_terminating = 1; if (arf_cmpabs_ui(arb_midref(acb_realref(aa + 1)), n_terminating) < 0) n_terminating = 1 - arf_get_si(arb_midref(acb_realref(aa + 1)), ARF_RND_DOWN); } acb_neg(w, z); acb_inv(w, w, prec); acb_neg(winv, z); /* low degree polynomial -- no need to try to terminate sooner */ if (is_terminating && n_terminating < 8) { acb_hypgeom_pfq_sum_invz(s, t, aa, p, aa + p, q, w, winv, n_terminating, prec); acb_set(res, s); } else { mag_t C1, Cn, alpha, nu, sigma, rho, zinv, tmp, err; mag_init(C1); mag_init(Cn); mag_init(alpha); mag_init(nu); mag_init(sigma); mag_init(rho); mag_init(zinv); mag_init(tmp); mag_init(err); acb_hypgeom_u_asymp_bound_factors(&R, alpha, nu, sigma, rho, zinv, a, b, z); is_real = acb_is_real(a) && acb_is_real(b) && acb_is_real(z) && (is_terminating || arb_is_positive(acb_realref(z))); if (R == 0) { /* if R == 0, the error bound is infinite unless terminating */ if (is_terminating && n_terminating < prec) { acb_hypgeom_pfq_sum_invz(s, t, aa, p, aa + p, q, w, winv, n_terminating, prec); acb_set(res, s); } else { acb_indeterminate(res); } } else { /* C1 */ acb_hypgeom_mag_Cn(C1, R, nu, sigma, 1); /* err = 2 * alpha * exp(...) */ mag_mul(tmp, C1, rho); mag_mul(tmp, tmp, alpha); mag_mul(tmp, tmp, zinv); mag_mul_2exp_si(tmp, tmp, 1); mag_exp(err, tmp); mag_mul(err, err, alpha); mag_mul_2exp_si(err, err, 1); /* choose n automatically */ if (n < 0) { slong moreprec; /* take err into account when finding truncation point */ /* we should take Cn into account as well, but this depends on n which is to be determined; it's easier to look only at exp(...) which should be larger anyway */ if (mag_cmp_2exp_si(err, 10 * prec) > 0) moreprec = 10 * prec; else if (mag_cmp_2exp_si(err, 0) < 0) moreprec = 0; else moreprec = MAG_EXP(err); n = acb_hypgeom_pfq_choose_n_max(aa, p, aa + p, q, w, prec + moreprec, FLINT_MIN(WORD_MAX / 2, 50 + 10.0 * prec)); } acb_hypgeom_pfq_sum_invz(s, t, aa, p, aa + p, q, w, winv, n, prec); /* add error bound, if not terminating */ if (!(is_terminating && n == n_terminating)) { acb_hypgeom_mag_Cn(Cn, R, nu, sigma, n); mag_mul(err, err, Cn); /* nth term * factor */ acb_get_mag(tmp, t); mag_mul(err, err, tmp); if (is_real) arb_add_error_mag(acb_realref(s), err); else acb_add_error_mag(s, err); } acb_set(res, s); } mag_clear(C1); mag_clear(Cn); mag_clear(alpha); mag_clear(nu); mag_clear(sigma); mag_clear(rho); mag_clear(zinv); mag_clear(tmp); mag_clear(err); } acb_clear(aa); acb_clear(aa + 1); acb_clear(aa + 2); acb_clear(s); acb_clear(t); acb_clear(w); acb_clear(winv); }
void _arb_poly_mullow_block(arb_ptr z, arb_srcptr x, slong xlen, arb_srcptr y, slong ylen, slong n, slong prec) { slong xmlen, xrlen, ymlen, yrlen, i; fmpz *xz, *yz, *zz; fmpz *xe, *ye; slong *xblocks, *yblocks; int squaring; fmpz_t scale, t; xlen = FLINT_MIN(xlen, n); ylen = FLINT_MIN(ylen, n); squaring = (x == y) && (xlen == ylen); /* Strip trailing zeros */ xmlen = xrlen = xlen; while (xmlen > 0 && arf_is_zero(arb_midref(x + xmlen - 1))) xmlen--; while (xrlen > 0 && mag_is_zero(arb_radref(x + xrlen - 1))) xrlen--; if (squaring) { ymlen = xmlen; yrlen = xrlen; } else { ymlen = yrlen = ylen; while (ymlen > 0 && arf_is_zero(arb_midref(y + ymlen - 1))) ymlen--; while (yrlen > 0 && mag_is_zero(arb_radref(y + yrlen - 1))) yrlen--; } /* We don't know how to deal with infinities or NaNs */ if (!_arb_vec_is_finite(x, xlen) || (!squaring && !_arb_vec_is_finite(y, ylen))) { _arb_poly_mullow_classical(z, x, xlen, y, ylen, n, prec); return; } xlen = FLINT_MAX(xmlen, xrlen); ylen = FLINT_MAX(ymlen, yrlen); /* Start with the zero polynomial */ _arb_vec_zero(z, n); /* Nothing to do */ if (xlen == 0 || ylen == 0) return; n = FLINT_MIN(n, xlen + ylen - 1); fmpz_init(scale); fmpz_init(t); xz = _fmpz_vec_init(xlen); yz = _fmpz_vec_init(ylen); zz = _fmpz_vec_init(n); xe = _fmpz_vec_init(xlen); ye = _fmpz_vec_init(ylen); xblocks = flint_malloc(sizeof(slong) * (xlen + 1)); yblocks = flint_malloc(sizeof(slong) * (ylen + 1)); _arb_poly_get_scale(scale, x, xlen, y, ylen); /* Error propagation */ /* (xm + xr)*(ym + yr) = (xm*ym) + (xr*ym + xm*yr + xr*yr) = (xm*ym) + (xm*yr + xr*(ym + yr)) */ if (xrlen != 0 || yrlen != 0) { mag_ptr tmp; double *xdbl, *ydbl; tmp = _mag_vec_init(FLINT_MAX(xlen, ylen)); xdbl = flint_malloc(sizeof(double) * xlen); ydbl = flint_malloc(sizeof(double) * ylen); /* (xm + xr)^2 = (xm*ym) + (xr^2 + 2 xm xr) = (xm*ym) + xr*(2 xm + xr) */ if (squaring) { _mag_vec_get_fmpz_2exp_blocks(xz, xdbl, xe, xblocks, scale, x, NULL, xrlen); for (i = 0; i < xlen; i++) { arf_get_mag(tmp + i, arb_midref(x + i)); mag_mul_2exp_si(tmp + i, tmp + i, 1); mag_add(tmp + i, tmp + i, arb_radref(x + i)); } _mag_vec_get_fmpz_2exp_blocks(yz, ydbl, ye, yblocks, scale, NULL, tmp, xlen); _arb_poly_addmullow_rad(z, zz, xz, xdbl, xe, xblocks, xrlen, yz, ydbl, ye, yblocks, xlen, n); } else if (yrlen == 0) { /* xr * |ym| */ _mag_vec_get_fmpz_2exp_blocks(xz, xdbl, xe, xblocks, scale, x, NULL, xrlen); for (i = 0; i < ymlen; i++) arf_get_mag(tmp + i, arb_midref(y + i)); _mag_vec_get_fmpz_2exp_blocks(yz, ydbl, ye, yblocks, scale, NULL, tmp, ymlen); _arb_poly_addmullow_rad(z, zz, xz, xdbl, xe, xblocks, xrlen, yz, ydbl, ye, yblocks, ymlen, n); } else { /* |xm| * yr */ for (i = 0; i < xmlen; i++) arf_get_mag(tmp + i, arb_midref(x + i)); _mag_vec_get_fmpz_2exp_blocks(xz, xdbl, xe, xblocks, scale, NULL, tmp, xmlen); _mag_vec_get_fmpz_2exp_blocks(yz, ydbl, ye, yblocks, scale, y, NULL, yrlen); _arb_poly_addmullow_rad(z, zz, xz, xdbl, xe, xblocks, xmlen, yz, ydbl, ye, yblocks, yrlen, n); /* xr*(|ym| + yr) */ if (xrlen != 0) { _mag_vec_get_fmpz_2exp_blocks(xz, xdbl, xe, xblocks, scale, x, NULL, xrlen); for (i = 0; i < ylen; i++) arb_get_mag(tmp + i, y + i); _mag_vec_get_fmpz_2exp_blocks(yz, ydbl, ye, yblocks, scale, NULL, tmp, ylen); _arb_poly_addmullow_rad(z, zz, xz, xdbl, xe, xblocks, xrlen, yz, ydbl, ye, yblocks, ylen, n); } } _mag_vec_clear(tmp, FLINT_MAX(xlen, ylen)); flint_free(xdbl); flint_free(ydbl); } /* multiply midpoints */ if (xmlen != 0 && ymlen != 0) { _arb_vec_get_fmpz_2exp_blocks(xz, xe, xblocks, scale, x, xmlen, prec); if (squaring) { _arb_poly_addmullow_block(z, zz, xz, xe, xblocks, xmlen, xz, xe, xblocks, xmlen, n, prec, 1); } else { _arb_vec_get_fmpz_2exp_blocks(yz, ye, yblocks, scale, y, ymlen, prec); _arb_poly_addmullow_block(z, zz, xz, xe, xblocks, xmlen, yz, ye, yblocks, ymlen, n, prec, 0); } } /* Unscale. */ if (!fmpz_is_zero(scale)) { fmpz_zero(t); for (i = 0; i < n; i++) { arb_mul_2exp_fmpz(z + i, z + i, t); fmpz_add(t, t, scale); } } _fmpz_vec_clear(xz, xlen); _fmpz_vec_clear(yz, ylen); _fmpz_vec_clear(zz, n); _fmpz_vec_clear(xe, xlen); _fmpz_vec_clear(ye, ylen); flint_free(xblocks); flint_free(yblocks); fmpz_clear(scale); fmpz_clear(t); }
int main() { slong iter; flint_rand_t state; flint_printf("approx_dot...."); fflush(stdout); flint_randinit(state); for (iter = 0; iter < 100000 * arb_test_multiplier(); iter++) { arb_ptr x, y; arb_t s1, s2, z; slong i, len, prec, xbits, ybits, ebits; int initial, subtract, revx, revy; if (n_randint(state, 100) == 0) len = n_randint(state, 100); else if (n_randint(state, 10) == 0) len = n_randint(state, 10); else len = n_randint(state, 3); if (n_randint(state, 10) != 0 || len > 10) { prec = 2 + n_randint(state, 500); xbits = 2 + n_randint(state, 500); ybits = 2 + n_randint(state, 500); } else { prec = 2 + n_randint(state, 4000); xbits = 2 + n_randint(state, 4000); ybits = 2 + n_randint(state, 4000); } if (n_randint(state, 100) == 0) ebits = 2 + n_randint(state, 100); else ebits = 2 + n_randint(state, 10); initial = n_randint(state, 2); subtract = n_randint(state, 2); revx = n_randint(state, 2); revy = n_randint(state, 2); x = _arb_vec_init(len); y = _arb_vec_init(len); arb_init(s1); arb_init(s2); arb_init(z); switch (n_randint(state, 3)) { case 0: for (i = 0; i < len; i++) { arb_randtest(x + i, state, xbits, ebits); arb_randtest(y + i, state, ybits, ebits); } break; /* Test with cancellation */ case 1: for (i = 0; i < len; i++) { if (i <= len / 2) { arb_randtest(x + i, state, xbits, ebits); arb_randtest(y + i, state, ybits, ebits); } else { arb_neg(x + i, x + len - i - 1); arb_set(y + i, y + len - i - 1); } } break; default: for (i = 0; i < len; i++) { if (i <= len / 2) { arb_randtest(x + i, state, xbits, ebits); arb_randtest(y + i, state, ybits, ebits); } else { arb_neg_round(x + i, x + len - i - 1, 2 + n_randint(state, 500)); arb_set_round(y + i, y + len - i - 1, 2 + n_randint(state, 500)); } } break; } arb_randtest(s1, state, 200, 100); arb_randtest(s2, state, 200, 100); arb_randtest(z, state, xbits, ebits); arb_approx_dot(s1, initial ? z : NULL, subtract, revx ? (x + len - 1) : x, revx ? -1 : 1, revy ? (y + len - 1) : y, revy ? -1 : 1, len, prec); mag_zero(arb_radref(s1)); /* With the fast algorithm, we expect identical results when reversing the vectors. */ if (ebits <= 12) { arb_approx_dot(s2, initial ? z : NULL, subtract, !revx ? (x + len - 1) : x, !revx ? -1 : 1, !revy ? (y + len - 1) : y, !revy ? -1 : 1, len, prec); mag_zero(arb_radref(s2)); if (!arb_equal(s1, s2)) { flint_printf("FAIL (reversal)\n\n"); flint_printf("iter = %wd, len = %wd, prec = %wd, ebits = %wd\n\n", iter, len, prec, ebits); if (initial) { flint_printf("z = ", i); arb_printn(z, 100, ARB_STR_MORE); flint_printf(" (%wd)\n\n", arb_bits(z)); } for (i = 0; i < len; i++) { flint_printf("x[%wd] = ", i); arb_printn(x + i, 100, ARB_STR_MORE); flint_printf(" (%wd)\n", arb_bits(x + i)); flint_printf("y[%wd] = ", i); arb_printn(y + i, 100, ARB_STR_MORE); flint_printf(" (%wd)\n", arb_bits(y + i)); } flint_printf("\n\n"); flint_printf("s1 = "); arb_printn(s1, 100, ARB_STR_MORE); flint_printf("\n\n"); flint_printf("s2 = "); arb_printn(s2, 100, ARB_STR_MORE); flint_printf("\n\n"); flint_abort(); } } /* Verify that radii are ignored */ for (i = 0; i < len; i++) { arb_get_mid_arb(x + i, x + i); arb_get_mid_arb(y + i, y + i); } arb_get_mid_arb(z, z); arb_approx_dot(s2, initial ? z : NULL, subtract, revx ? (x + len - 1) : x, revx ? -1 : 1, revy ? (y + len - 1) : y, revy ? -1 : 1, len, prec); mag_zero(arb_radref(s2)); if (!arb_equal(s1, s2)) { flint_printf("FAIL (radii)\n\n"); flint_printf("iter = %wd, len = %wd, prec = %wd, ebits = %wd\n\n", iter, len, prec, ebits); if (initial) { flint_printf("z = ", i); arb_printn(z, 100, ARB_STR_MORE); flint_printf(" (%wd)\n\n", arb_bits(z)); } for (i = 0; i < len; i++) { flint_printf("x[%wd] = ", i); arb_printn(x + i, 100, ARB_STR_MORE); flint_printf(" (%wd)\n", arb_bits(x + i)); flint_printf("y[%wd] = ", i); arb_printn(y + i, 100, ARB_STR_MORE); flint_printf(" (%wd)\n", arb_bits(y + i)); } flint_printf("\n\n"); flint_printf("s1 = "); arb_printn(s1, 100, ARB_STR_MORE); flint_printf("\n\n"); flint_printf("s2 = "); arb_printn(s2, 100, ARB_STR_MORE); flint_printf("\n\n"); flint_abort(); } /* Compare with arb_dot */ arb_approx_dot(s2, initial ? z : NULL, subtract, revx ? (x + len - 1) : x, revx ? -1 : 1, revy ? (y + len - 1) : y, revy ? -1 : 1, len, prec); { mag_t err, xx, yy; mag_init(err); mag_init(xx); mag_init(yy); if (initial) arb_get_mag(err, z); for (i = 0; i < len; i++) { arb_get_mag(xx, revx ? x + len - 1 - i : x + i); arb_get_mag(yy, revx ? y + len - 1 - i : y + i); mag_addmul(err, xx, yy); } mag_mul_2exp_si(err, err, -prec + 2); arb_add_error_mag(s2, err); if (!arb_contains(s2, s1)) { flint_printf("FAIL (inclusion)\n\n"); flint_printf("iter = %wd, len = %wd, prec = %wd, ebits = %wd\n\n", iter, len, prec, ebits); if (initial) { flint_printf("z = ", i); arb_printn(z, 100, ARB_STR_MORE); flint_printf(" (%wd)\n\n", arb_bits(z)); } for (i = 0; i < len; i++) { flint_printf("x[%wd] = ", i); arb_printn(x + i, 100, ARB_STR_MORE); flint_printf(" (%wd)\n", arb_bits(x + i)); flint_printf("y[%wd] = ", i); arb_printn(y + i, 100, ARB_STR_MORE); flint_printf(" (%wd)\n", arb_bits(y + i)); } flint_printf("\n\n"); flint_printf("s1 = "); arb_printn(s1, 100, ARB_STR_MORE); flint_printf("\n\n"); flint_printf("s2 = "); arb_printn(s2, 100, ARB_STR_MORE); flint_printf("\n\n"); flint_abort(); } mag_clear(err); mag_clear(xx); mag_clear(yy); } arb_clear(s1); arb_clear(s2); arb_clear(z); _arb_vec_clear(x, len); _arb_vec_clear(y, len); } flint_randclear(state); flint_cleanup(); flint_printf("PASS\n"); return EXIT_SUCCESS; }