Example #1
0
File: erf.c Project: isuruf/arb
void
acb_hypgeom_erf(acb_t res, const acb_t z, slong prec)
{
    double x, y, absz2, logz;
    slong prec2;

    if (!acb_is_finite(z))
    {
        acb_indeterminate(res);
        return;
    }

    if (acb_is_zero(z))
    {
        acb_zero(res);
        return;
    }

    if ((arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), 0) < 0 &&
            arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), 0) < 0))
    {
        acb_hypgeom_erf_1f1a(res, z, prec);
        return;
    }

    if ((arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), 64) > 0 ||
            arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), 64) > 0))
    {
        acb_hypgeom_erf_asymp(res, z, prec, prec);
        return;
    }

    x = arf_get_d(arb_midref(acb_realref(z)), ARF_RND_DOWN);
    y = arf_get_d(arb_midref(acb_imagref(z)), ARF_RND_DOWN);

    absz2 = x * x + y * y;
    logz = 0.5 * log(absz2);

    if (logz - absz2 < -(prec + 8) * 0.69314718055994530942)
    {
        /* If the asymptotic term is small, we can
           compute with reduced precision */
        prec2 = FLINT_MIN(prec + 4 + (y*y - x*x - logz) * 1.4426950408889634074, (double) prec);
        prec2 = FLINT_MAX(8, prec2);
        prec2 = FLINT_MIN(prec2, prec);

        acb_hypgeom_erf_asymp(res, z, prec, prec2);
    }
    else if (arf_cmpabs(arb_midref(acb_imagref(z)), arb_midref(acb_realref(z))) > 0)
    {
        acb_hypgeom_erf_1f1a(res, z, prec);
    }
    else
    {
        acb_hypgeom_erf_1f1b(res, z, prec);
    }
}
Example #2
0
void
acb_lambertw_principal_d(acb_t res, const acb_t z)
{
    double za, zb, wa, wb, ewa, ewb, t, u, q, r;
    int k, maxk = 15;

    za = arf_get_d(arb_midref(acb_realref(z)), ARF_RND_DOWN);
    zb = arf_get_d(arb_midref(acb_imagref(z)), ARF_RND_DOWN);

    /* make sure we end up on the right branch */
    if (za < -0.367 && zb > -1e-20 && zb <= 0.0
                  && arf_sgn(arb_midref(acb_imagref(z))) < 0)
        zb = -1e-20;

    wa = za;
    wb = zb;

    if (fabs(wa) > 2.0 || fabs(wb) > 2.0)
    {
        t = atan2(wb, wa);
        wa = 0.5 * log(wa * wa + wb * wb);
        wb = t;
    }
    else if (fabs(wa) > 0.25 || fabs(wb) > 0.25)
    {
        /* We have W(z) ~= -1 + (2(ez+1))^(1/2) near the branch point.
           Changing the exponent to 1/4 gives a much worse local guess
           which however does the job on a larger domain. */
        wa *= 5.43656365691809;
        wb *= 5.43656365691809;
        wa += 2.0;
        t = atan2(wb, wa);
        r = pow(wa * wa + wb * wb, 0.125);
        wa = r * cos(0.25 * t);
        wb = r * sin(0.25 * t);
        wa -= 1.0;
    }

    for (k = 0; k < maxk; k++)
    {
        t = exp(wa);
        ewa = t * cos(wb);
        ewb = t * sin(wb);
        t = (ewa * wa - ewb * wb); q = t + ewa; t -= za;
        u = (ewb * wa + ewa * wb); r = u + ewb; u -= zb;
        ewa = q * t + r * u; ewb = q * u - r * t;
        r = 1.0 / (q * q + r * r);
        ewa *= r; ewb *= r;
        if ((ewa*ewa + ewb*ewb) < (wa*wa + wb*wb) * 1e-12)
            maxk = FLINT_MIN(maxk, k + 2);
        wa -= ewa; wb -= ewb;
    }

    acb_set_d_d(res, wa, wb);
}
Example #3
0
int
acb_hypgeom_2f1_choose(const acb_t z)
{
    double x, y;
    double mag[7];
    int i, pick;

    x = arf_get_d(arb_midref(acb_realref(z)), ARF_RND_DOWN);
    y = arf_get_d(arb_midref(acb_imagref(z)), ARF_RND_DOWN);

    x = FLINT_MAX(FLINT_MIN(x, 1e10), -1e10);
    y = FLINT_MAX(FLINT_MIN(y, 1e10), -1e10);

    mag[0] = x*x + y*y;  /* |z|^2 */
    mag[4] = (1.0-x)*(1.0-x) + y*y;              /* |1-z|^2 */

    if (mag[0] <= ALWAYS1)   return 0;

    mag[1] = mag[0] / FLINT_MAX(mag[4], 1e-10);  /* |z/(z-1)|^2 */

    if (mag[1] <= ALWAYS1)   return 1;

    if (mag[0] <= ALWAYS2 || mag[1] <= ALWAYS2)
        return mag[0] <= mag[1] ? 0 : 1;

    mag[2] = 1.0 / mag[0];                    /* |1/z|^2 */
    mag[3] = 1.0 / FLINT_MAX(mag[4], 1e-10);  /* 1/|1-z|^2 */
    mag[5] = mag[4] / mag[0];                 /* |1-1/z|^2 = |(1-z)/z|^2 */

    pick = 0;
    for (i = 1; i < 6; i++)
    {
        if (mag[i] < mag[pick])
            pick = i;
    }

    if (mag[pick] <= LIMIT)
        return pick;

    return 6;
}
Example #4
0
int
acb_hypgeom_u_use_asymp(const acb_t z, slong prec)
{
    double x, y;

    if ((arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), 0) < 0 &&
         arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), 0) < 0))
    {
        return 0;
    }

    if ((arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), 64) > 0 ||
         arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), 64) > 0))
    {
        return 1;
    }

    x = arf_get_d(arb_midref(acb_realref(z)), ARF_RND_DOWN);
    y = arf_get_d(arb_midref(acb_imagref(z)), ARF_RND_DOWN);

    return sqrt(x * x + y * y) > prec * 0.69314718055994530942;
}
Example #5
0
renf_elem_class::operator double() const noexcept
{
    if (nf == nullptr)
    {
        arb_t s;
        arb_init(s);
        arb_set_fmpq(s, b, 128);
        double ans = arf_get_d(arb_midref(s), ARF_RND_NEAR);
        arb_clear(s);
        return ans;
    }
    else
        return renf_elem_get_d(a, nf->renf_t(), ARF_RND_NEAR);
}
Example #6
0
/* this can be improved */
static int
use_recurrence(const acb_t n, const acb_t a, const acb_t b, slong prec)
{
    if (!acb_is_int(n) || !arb_is_nonnegative(acb_realref(n)))
        return 0;

    if (arf_cmpabs_ui(arb_midref(acb_realref(n)), prec) > 0)
        return 0;

    if (arb_is_nonnegative(acb_realref(a)) ||
        arf_get_d(arb_midref(acb_realref(a)), ARF_RND_DOWN) > -0.9)
        return 0;

    return 1;
}
Example #7
0
void arb_mat_print_sage_float(const arb_mat_t A) {
    int nrows = arb_mat_nrows(A);
    int ncols = arb_mat_ncols(A);
    printf("[");
    for(int j = 0; j < nrows; j++) {
        printf("[");
        for(int k = 0; k < ncols; k++) {
            double x = arf_get_d(arb_midref(arb_mat_entry(A, j, k)), ARF_RND_NEAR);
            printf("%e", x);
            if(k < nrows - 1)
                printf(", ");
        }
        printf("],\n");
    }
    printf("]\n");
}
Example #8
0
void
acb_hypgeom_airy(acb_t ai, acb_t aip, acb_t bi, acb_t bip, const acb_t z, slong prec)
{
    arf_srcptr re, im;
    double x, y, t, zmag, z15, term_est, airy_est, abstol;
    slong n, wp;

    if (!acb_is_finite(z))
    {
        if (ai != NULL) acb_indeterminate(ai);
        if (aip != NULL) acb_indeterminate(aip);
        if (bi != NULL) acb_indeterminate(bi);
        if (bip != NULL) acb_indeterminate(bip);
        return;
    }

    re = arb_midref(acb_realref(z));
    im = arb_midref(acb_imagref(z));
    wp = prec * 1.03 + 15;

    /* tiny input -- use direct method and pick n without underflowing */
    if (arf_cmpabs_2exp_si(re, -64) < 0 && arf_cmpabs_2exp_si(im, -64) < 0)
    {
        if (arf_cmpabs_2exp_si(re, -wp) < 0 && arf_cmpabs_2exp_si(im, -wp) < 0)
        {
            n = 1;  /* very tiny input */
        }
        else
        {
            if (arf_cmpabs(re, im) > 0)
                zmag = fmpz_get_d(ARF_EXPREF(re));
            else
                zmag = fmpz_get_d(ARF_EXPREF(im));
            zmag = (zmag + 1) * (1.0 / LOG2);
            n = wp / (-zmag) + 1;
        }

        acb_hypgeom_airy_direct(ai, aip, bi, bip, z, n, wp);
    }  /* huge input -- use asymptotics and pick n without overflowing */
    else if ((arf_cmpabs_2exp_si(re, 64) > 0 || arf_cmpabs_2exp_si(im, 64) > 0))
    {
        if (arf_cmpabs_2exp_si(re, prec) > 0 || arf_cmpabs_2exp_si(im, prec) > 0)
        {
            n = 1;   /* very huge input */
        }
        else
        {
            x = fmpz_get_d(ARF_EXPREF(re));
            y = fmpz_get_d(ARF_EXPREF(im));
            zmag = (FLINT_MAX(x, y) - 2) * (1.0 / LOG2);
            n = asymp_pick_terms(wp, zmag);
            n = FLINT_MAX(n, 1);
        }

        acb_hypgeom_airy_asymp(ai, aip, bi, bip, z, n, wp);
    }
    else /* moderate input */
    {
        x = arf_get_d(re, ARF_RND_DOWN);
        y = arf_get_d(im, ARF_RND_DOWN);

        zmag = sqrt(x * x + y * y);
        z15 = zmag * sqrt(zmag);

        if (zmag >= 4.0 && (n = asymp_pick_terms(wp, log(zmag))) != -1)
        {
            acb_hypgeom_airy_asymp(ai, aip, bi, bip, z, n, wp);
        }
        else if (zmag <= 1.5)
        {
            t = 3 * (wp * LOG2) / (2 * z15 * EXP1);
            t = (wp * LOG2) / (2 * d_lambertw(t));
            n = FLINT_MAX(t + 1, 2);
            acb_hypgeom_airy_direct(ai, aip, bi, bip, z, n, wp);
        }
        else
        {
            /* estimate largest term: log2(exp(2(z^3/9)^(1/2))) */
            term_est = 0.96179669392597560491 * z15;

            /* estimate the smaller of Ai and Bi */
            airy_est = estimate_airy(x, y, (ai != NULL || aip != NULL));

            /* estimate absolute tolerance and necessary working precision */
            abstol = airy_est - wp;
            wp = wp + term_est - airy_est;
            wp = FLINT_MAX(wp, 10);

            t = 3 * (-abstol * LOG2) / (2 * z15 * EXP1);
            t = (-abstol * LOG2) / (2 * d_lambertw(t));
            n = FLINT_MAX(t + 1, 2);

            if (acb_is_exact(z))
                acb_hypgeom_airy_direct(ai, aip, bi, bip, z, n, wp);
            else
                acb_hypgeom_airy_direct_prop(ai, aip, bi, bip, z, n, wp);
        }
    }

    if (ai != NULL) acb_set_round(ai, ai, prec);
    if (aip != NULL) acb_set_round(aip, aip, prec);
    if (bi != NULL) acb_set_round(bi, bi, prec);
    if (bip != NULL) acb_set_round(bip, bip, prec);
}
Example #9
0
void
acb_hypgeom_erf(acb_t res, const acb_t z, slong prec)
{
    double x, y, abs_z2, log_z, log_erf_z_asymp;
    slong prec2, wp;
    int more_imaginary;

    if (!acb_is_finite(z))
    {
        acb_indeterminate(res);
        return;
    }

    if (acb_is_zero(z))
    {
        acb_zero(res);
        return;
    }

    if ((arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), -64) < 0 &&
         arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), -64) < 0))
    {
        acb_hypgeom_erf_1f1(res, z, prec, prec, 1);
        return;
    }

    if ((arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), 64) > 0 ||
         arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), 64) > 0))
    {
        acb_hypgeom_erf_asymp(res, z, 0, prec, prec);
        return;
    }

    x = arf_get_d(arb_midref(acb_realref(z)), ARF_RND_DOWN);
    y = arf_get_d(arb_midref(acb_imagref(z)), ARF_RND_DOWN);

    abs_z2 = x * x + y * y;
    log_z = 0.5 * log(abs_z2);
    /* estimate of log(erf(z)), disregarding csgn term */
    log_erf_z_asymp = y*y - x*x - log_z;

    if (log_z - abs_z2 < -(prec + 8) * 0.69314718055994530942)
    {
        /* If the asymptotic term is small, we can
           compute with reduced precision. */
        prec2 = FLINT_MIN(prec + 4 + log_erf_z_asymp * 1.4426950408889634074, (double) prec);
        prec2 = FLINT_MAX(8, prec2);
        prec2 = FLINT_MIN(prec2, prec);

        acb_hypgeom_erf_asymp(res, z, 0, prec, prec2);
    }
    else
    {
        more_imaginary = arf_cmpabs(arb_midref(acb_imagref(z)),
                                    arb_midref(acb_realref(z))) > 0;

        /* Worst case: exp(|x|^2), computed: exp(x^2).
           (x^2+y^2) - (x^2-y^2) = 2y^2, etc. */
        if (more_imaginary)
            wp = prec + FLINT_MAX(2 * x * x, 0.0) * 1.4426950408889634074 + 5;
        else
            wp = prec + FLINT_MAX(2 * y * y, 0.0) * 1.4426950408889634074 + 5;

        acb_hypgeom_erf_1f1(res, z, prec, wp, more_imaginary);
    }
}
Example #10
0
File: m.c Project: argriffing/arb
void
acb_hypgeom_m_choose(int * asymp, int * kummer, slong * wp,
    const acb_t a, const acb_t b, const acb_t z, int regularized, slong prec)
{
    double x, y, t, cancellation;
    double input_accuracy, direct_accuracy, asymp_accuracy;
    slong m = WORD_MAX;
    slong n = WORD_MAX;

    if (acb_is_int(a) &&
            arf_cmpabs_2exp_si(arb_midref(acb_realref(a)), 30) < 0)
    {
        m = arf_get_si(arb_midref(acb_realref(a)), ARF_RND_DOWN);
    }

    if (acb_is_int(b) &&
            arf_cmpabs_2exp_si(arb_midref(acb_realref(b)), 30) < 0)
    {
        n = arf_get_si(arb_midref(acb_realref(b)), ARF_RND_DOWN);
    }

    *asymp = 0;
    *kummer = 0;
    *wp = prec;

    /* The 1F1 series terminates. */
    /* TODO: for large m, estimate extra precision here. */
    if (m <= 0 && m < n && m > -10 * prec && (n > 0 || !regularized))
    {
        *asymp = 0;
        return;
    }

    /* The 1F1 series terminates with the Kummer transform. */
    /* TODO: for large m, estimate extra precision here. */
    if (m >= 1 && n >= 1 && m < 0.1 * prec && n < 0.1 * prec && n <= m)
    {
        *asymp = 0;
        *kummer = 1;
        return;
    }

    input_accuracy = acb_rel_accuracy_bits(z);
    t = acb_rel_accuracy_bits(a);
    input_accuracy = FLINT_MIN(input_accuracy, t);
    t = acb_rel_accuracy_bits(b);
    input_accuracy = FLINT_MIN(input_accuracy, t);
    input_accuracy = FLINT_MAX(input_accuracy, 0.0);

    /* From here we ignore the values of a, b. Taking them into account is
       a possible future improvement... */

    /* Tiny |z|. */
    if ((arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), 2) < 0 &&
         arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), 2) < 0))
    {
        *asymp = 0;
        *wp = FLINT_MAX(2, FLINT_MIN(input_accuracy + 20, prec));
        return;
    }

    /* Huge |z|. */
    if ((arf_cmpabs_2exp_si(arb_midref(acb_realref(z)), 64) > 0 ||
         arf_cmpabs_2exp_si(arb_midref(acb_imagref(z)), 64) > 0))
    {
        *asymp = 1;
        *wp = FLINT_MAX(2, FLINT_MIN(input_accuracy + 20, prec));
        return;
    }

    x = arf_get_d(arb_midref(acb_realref(z)), ARF_RND_DOWN);
    y = arf_get_d(arb_midref(acb_imagref(z)), ARF_RND_DOWN);

    asymp_accuracy = sqrt(x * x + y * y) * 1.44269504088896 - 5.0;

    /* The Kummer transformation gives less cancellation with the 1F1 series. */
    if (x < 0.0)
    {
        *kummer = 1;
        x = -x;
    }

    if (asymp_accuracy >= prec)
    {
        *asymp = 1;
        *wp = FLINT_MAX(2, FLINT_MIN(input_accuracy + 20, prec));
        return;
    }

    cancellation = hypotmx(x, y) * 1.44269504088896;

    direct_accuracy = input_accuracy - cancellation;

    if (direct_accuracy > asymp_accuracy)
    {
        *asymp = 0;
        *wp = FLINT_MAX(2, FLINT_MIN(input_accuracy + 20, prec + cancellation));
    }
    else
    {
        *asymp = 1;
        *wp = FLINT_MAX(2, FLINT_MIN(input_accuracy + 20, prec));
    }
}
Example #11
0
double Lib_Arb_Get_D(ArbPtr x)
{
    return arf_get_d( arb_midref((arb_ptr) x), ARF_RND_DOWN);
}
Example #12
0
void
acb_gamma_stirling_eval(acb_t s, const acb_t z, long nterms, int digamma, long prec)
{
    acb_t t, logz, zinv, zinv2;
    arb_t b;
    mag_t err;

    long k, term_prec;
    double z_mag, term_mag;

    acb_init(t);
    acb_init(logz);
    acb_init(zinv);
    acb_init(zinv2);
    arb_init(b);

    acb_log(logz, z, prec);
    acb_inv(zinv, z, prec);

    nterms = FLINT_MAX(nterms, 1);

    acb_zero(s);
    if (nterms > 1)
    {
        acb_mul(zinv2, zinv, zinv, prec);

        z_mag = arf_get_d(arb_midref(acb_realref(logz)), ARF_RND_UP) * 1.44269504088896;

        for (k = nterms - 1; k >= 1; k--)
        {
            term_mag = bernoulli_bound_2exp_si(2 * k);
            term_mag -= (2 * k - 1) * z_mag;
            term_prec = prec + term_mag;
            term_prec = FLINT_MIN(term_prec, prec);
            term_prec = FLINT_MAX(term_prec, 10);

            arb_gamma_stirling_coeff(b, k, digamma, term_prec);

            if (prec > 2000)
            {
                acb_set_round(t, zinv2, term_prec);
                acb_mul(s, s, t, term_prec);
            }
            else
                acb_mul(s, s, zinv2, term_prec);

            arb_add(acb_realref(s), acb_realref(s), b, term_prec);
        }

        if (digamma)
            acb_mul(s, s, zinv2, prec);
        else
            acb_mul(s, s, zinv, prec);
    }

    /* remainder bound */
    mag_init(err);
    acb_gamma_stirling_bound(err, z, digamma ? 1 : 0, 1, nterms);
    mag_add(arb_radref(acb_realref(s)), arb_radref(acb_realref(s)), err);
    mag_add(arb_radref(acb_imagref(s)), arb_radref(acb_imagref(s)), err);
    mag_clear(err);

    if (digamma)
    {
        acb_neg(s, s);
        acb_mul_2exp_si(zinv, zinv, -1);
        acb_sub(s, s, zinv, prec);
        acb_add(s, s, logz, prec);
    }
    else
    {
        /* (z-0.5)*log(z) - z + log(2*pi)/2 */
        arb_one(b);
        arb_mul_2exp_si(b, b, -1);
        arb_set(acb_imagref(t), acb_imagref(z));
        arb_sub(acb_realref(t), acb_realref(z), b, prec);
        acb_mul(t, logz, t, prec);
        acb_add(s, s, t, prec);
        acb_sub(s, s, z, prec);
        arb_const_log_sqrt2pi(b, prec);
        arb_add(acb_realref(s), acb_realref(s), b, prec);
    }

    acb_clear(t);
    acb_clear(logz);
    acb_clear(zinv);
    acb_clear(zinv2);
    arb_clear(b);
}
Example #13
0
int main()
{
    slong iter;
    flint_rand_t state;

    flint_printf("get_d....");
    fflush(stdout);

    flint_randinit(state);

    /* test exact roundtrip */
    for (iter = 0; iter < 100000 * arb_test_multiplier(); iter++)
    {
        arf_t x, z;
        double y;
        arf_rnd_t rnd;

        arf_init(x);
        arf_init(z);

        switch (n_randint(state, 4))
        {
            case 0:  rnd = ARF_RND_DOWN; break;
            case 1:  rnd = ARF_RND_UP; break;
            case 2:  rnd = ARF_RND_FLOOR; break;
            case 3:  rnd = ARF_RND_CEIL; break;
            default: rnd = ARF_RND_NEAR; break;
        }

        arf_randtest_special(x, state, 53, 8);
        y = arf_get_d(x, rnd);
        arf_set_d(z, y);

        if (!arf_equal(x, z))
        {
            flint_printf("FAIL:\n\n");
            flint_printf("x = "); arf_print(x); flint_printf("\n\n");
            flint_printf("y = %.17g\n\n", y);
            flint_printf("z = "); arf_print(z); flint_printf("\n\n");
            abort();
        }

        arf_clear(x);
        arf_clear(z);
    }

    /* test rounding */
    for (iter = 0; iter < 100000 * arb_test_multiplier(); iter++)
    {
        arf_t x, z, w;
        arf_rnd_t rnd;
        double y;

        arf_init(x);
        arf_init(z);
        arf_init(w);

        arf_randtest_special(x, state, 300, 8);

        switch (n_randint(state, 4))
        {
            case 0:  rnd = ARF_RND_DOWN; break;
            case 1:  rnd = ARF_RND_UP; break;
            case 2:  rnd = ARF_RND_FLOOR; break;
            default: rnd = ARF_RND_CEIL; break;
        }

        y = arf_get_d(x, rnd);
        arf_set_d(w, y);

        arf_set_round(z, x, 53, rnd);

        if (!arf_equal(w, z))
        {
            flint_printf("FAIL:\n\n");
            flint_printf("x = "); arf_print(x); flint_printf("\n\n");
            flint_printf("y = %.17g\n\n", y);
            flint_printf("z = "); arf_print(z); flint_printf("\n\n");
            flint_printf("w = "); arf_print(w); flint_printf("\n\n");
            abort();
        }

        arf_clear(x);
        arf_clear(z);
        arf_clear(w);
    }

    /* compare with mpfr */
    for (iter = 0; iter < 100000 * arb_test_multiplier(); iter++)
    {
        arf_t x, r1, r2;
        arf_rnd_t rnd;
        mpfr_t t;
        double d1, d2;

        arf_init(x);
        arf_init(r1);
        arf_init(r2);
        mpfr_init2(t, 300);

        arf_randtest_special(x, state, 300, 20);
        arf_get_mpfr(t, x, MPFR_RNDD);

        switch (n_randint(state, 4))
        {
            case 0:  rnd = ARF_RND_DOWN; break;
            case 1:  rnd = ARF_RND_UP; break;
            case 2:  rnd = ARF_RND_FLOOR; break;
            case 3:  rnd = ARF_RND_CEIL; break;
            default: rnd = ARF_RND_NEAR; break;
        }

        d1 = arf_get_d(x, rnd);
        d2 = mpfr_get_d(t, rnd_to_mpfr(rnd));

        arf_set_d(r1, d1);
        arf_set_d(r2, d2);

        if (!arf_equal(r1, r2))
        {
            flint_printf("FAIL:\n\n");
            flint_printf("rnd = %i\n\n", rnd);
            flint_printf("x = "); arf_print(x); flint_printf("\n\n");
            flint_printf("d1 = %.17g\n\n", d1);
            flint_printf("d2 = %.17g\n\n", d2);
            flint_printf("r1 = "); arf_print(r1); flint_printf("\n\n");
            flint_printf("r2 = "); arf_print(r2); flint_printf("\n\n");
            abort();
        }

        arf_clear(x);
        arf_clear(r1);
        arf_clear(r2);
        mpfr_clear(t);
    }

    flint_randclear(state);
    flint_cleanup();
    flint_printf("PASS\n");
    return EXIT_SUCCESS;
}
int
acb_calc_integrate_taylor(acb_t res,
    acb_calc_func_t func, void * param,
    const acb_t a, const acb_t b,
    const arf_t inner_radius,
    const arf_t outer_radius,
    long accuracy_goal, long prec)
{
    long num_steps, step, N, bp;
    int result;

    acb_t delta, m, x, y1, y2, sum;
    acb_ptr taylor_poly;
    arf_t err;

    acb_init(delta);
    acb_init(m);
    acb_init(x);
    acb_init(y1);
    acb_init(y2);
    acb_init(sum);
    arf_init(err);

    acb_sub(delta, b, a, prec);

    /* precision used for bounds calculations */
    bp = MAG_BITS;

    /* compute the number of steps */
    {
        arf_t t;
        arf_init(t);
        acb_get_abs_ubound_arf(t, delta, bp);
        arf_div(t, t, inner_radius, bp, ARF_RND_UP);
        arf_mul_2exp_si(t, t, -1);
        num_steps = (long) (arf_get_d(t, ARF_RND_UP) + 1.0);
        /* make sure it's not something absurd */
        num_steps = FLINT_MIN(num_steps, 10 * prec);
        num_steps = FLINT_MAX(num_steps, 1);
        arf_clear(t);
    }

    result = ARB_CALC_SUCCESS;

    acb_zero(sum);

    for (step = 0; step < num_steps; step++)
    {
        /* midpoint of subinterval */
        acb_mul_ui(m, delta, 2 * step + 1, prec);
        acb_div_ui(m, m, 2 * num_steps, prec);
        acb_add(m, m, a, prec);

        if (arb_calc_verbose)
        {
            printf("integration point %ld/%ld: ", 2 * step + 1, 2 * num_steps);
            acb_printd(m, 15); printf("\n");
        }

        /* evaluate at +/- x */
        /* TODO: exactify m, and include error in x? */
        acb_div_ui(x, delta, 2 * num_steps, prec);

        /* compute bounds and number of terms to use */
        {
            arb_t cbound, xbound, rbound;
            arf_t C, D, R, X, T;
            double DD, TT, NN;

            arb_init(cbound);
            arb_init(xbound);
            arb_init(rbound);
            arf_init(C);
            arf_init(D);
            arf_init(R);
            arf_init(X);
            arf_init(T);

            /* R is the outer radius */
            arf_set(R, outer_radius);

            /* X = upper bound for |x| */
            acb_get_abs_ubound_arf(X, x, bp);
            arb_set_arf(xbound, X);

            /* Compute C(m,R). Important subtlety: due to rounding when
               computing m, we will in general be farther than R away from
               the integration path. But since acb_calc_cauchy_bound
               actually integrates over the area traced by a complex
               interval, it will catch any extra singularities (giving
               an infinite bound). */
            arb_set_arf(rbound, outer_radius);
            acb_calc_cauchy_bound(cbound, func, param, m, rbound, 8, bp);
            arf_set_mag(C, arb_radref(cbound));
            arf_add(C, arb_midref(cbound), C, bp, ARF_RND_UP);

            /* Sanity check: we need C < inf and R > X */
            if (arf_is_finite(C) && arf_cmp(R, X) > 0)
            {
                /* Compute upper bound for D = C * R * X / (R - X) */
                arf_mul(D, C, R, bp, ARF_RND_UP);
                arf_mul(D, D, X, bp, ARF_RND_UP);
                arf_sub(T, R, X, bp, ARF_RND_DOWN);
                arf_div(D, D, T, bp, ARF_RND_UP);

                /* Compute upper bound for T = (X / R) */
                arf_div(T, X, R, bp, ARF_RND_UP);

                /* Choose N */
                /* TODO: use arf arithmetic to avoid overflow */
                /* TODO: use relative accuracy (look at |f(m)|?) */
                DD = arf_get_d(D, ARF_RND_UP);
                TT = arf_get_d(T, ARF_RND_UP);
                NN = -(accuracy_goal * 0.69314718055994530942 + log(DD)) / log(TT);
                N = NN + 0.5;
                N = FLINT_MIN(N, 100 * prec);
                N = FLINT_MAX(N, 1);

                /* Tail bound: D / (N + 1) * T^N */
                {
                    mag_t TT;
                    mag_init(TT);
                    arf_get_mag(TT, T);
                    mag_pow_ui(TT, TT, N);
                    arf_set_mag(T, TT);
                    mag_clear(TT);
                }
                arf_mul(D, D, T, bp, ARF_RND_UP);
                arf_div_ui(err, D, N + 1, bp, ARF_RND_UP);
            }
            else
            {
                N = 1;
                arf_pos_inf(err);
                result = ARB_CALC_NO_CONVERGENCE;
            }

            if (arb_calc_verbose)
            {
                printf("N = %ld; bound: ", N); arf_printd(err, 15); printf("\n");
                printf("R: "); arf_printd(R, 15); printf("\n");
                printf("C: "); arf_printd(C, 15); printf("\n");
                printf("X: "); arf_printd(X, 15); printf("\n");
            }

            arb_clear(cbound);
            arb_clear(xbound);
            arb_clear(rbound);
            arf_clear(C);
            arf_clear(D);
            arf_clear(R);
            arf_clear(X);
            arf_clear(T);
        }

        /* evaluate Taylor polynomial */
        taylor_poly = _acb_vec_init(N + 1);
        func(taylor_poly, m, param, N, prec);
        _acb_poly_integral(taylor_poly, taylor_poly, N + 1, prec);
        _acb_poly_evaluate(y2, taylor_poly, N + 1, x, prec);
        acb_neg(x, x);
        _acb_poly_evaluate(y1, taylor_poly, N + 1, x, prec);
        acb_neg(x, x);

        /* add truncation error */
        arb_add_error_arf(acb_realref(y1), err);
        arb_add_error_arf(acb_imagref(y1), err);
        arb_add_error_arf(acb_realref(y2), err);
        arb_add_error_arf(acb_imagref(y2), err);

        acb_add(sum, sum, y2, prec);
        acb_sub(sum, sum, y1, prec);

        if (arb_calc_verbose)
        {
            printf("values:  ");
            acb_printd(y1, 15); printf("  ");
            acb_printd(y2, 15); printf("\n");
        }

        _acb_vec_clear(taylor_poly, N + 1);

        if (result == ARB_CALC_NO_CONVERGENCE)
            break;
    }

    acb_set(res, sum);

    acb_clear(delta);
    acb_clear(m);
    acb_clear(x);
    acb_clear(y1);
    acb_clear(y2);
    acb_clear(sum);
    arf_clear(err);

    return result;
}