Esempio n. 1
0
File: add.c Progetto: isuruf/arb
void
_arb_poly_add(arb_ptr res, arb_srcptr poly1, slong len1,
    arb_srcptr poly2, slong len2, slong prec)
{
    slong i, min = FLINT_MIN(len1, len2);

    for (i = 0; i < min; i++)
        arb_add(res + i, poly1 + i, poly2 + i, prec);

    for (i = min; i < len1; i++)
        arb_set_round(res + i, poly1 + i, prec);

    for (i = min; i < len2; i++)
        arb_set_round(res + i, poly2 + i, prec);
}
Esempio n. 2
0
File: compose.c Progetto: isuruf/arb
void
_arb_poly_compose(arb_ptr res,
    arb_srcptr poly1, slong len1,
    arb_srcptr poly2, slong len2, slong prec)
{
    if (len1 == 1)
    {
        arb_set_round(res, poly1, prec);
    }
    else if (len2 == 1)
    {
        _arb_poly_evaluate(res, poly1, len1, poly2, prec);
    }
    else if (_arb_vec_is_zero(poly2 + 1, len2 - 2))
    {
        _arb_poly_compose_axnc(res, poly1, len1, poly2, poly2 + len2 - 1, len2 - 1, prec);
    }
    else if (len1 <= 7)
    {
        _arb_poly_compose_horner(res, poly1, len1, poly2, len2, prec);
    }
    else
    {
        _arb_poly_compose_divconquer(res, poly1, len1, poly2, len2, prec);
    }
}
Esempio n. 3
0
File: root_ui.c Progetto: isuruf/arb
void
arb_root_ui(arb_t res, const arb_t x, ulong k, slong prec)
{
    if (k == 0)
    {
        arb_indeterminate(res);
    }
    else if (k == 1)
    {
        arb_set_round(res, x, prec);
    }
    else if (k == 2)
    {
        arb_sqrt(res, x, prec);
    }
    else if (k == 4)
    {
        arb_sqrt(res, x, prec + 2);
        arb_sqrt(res, res, prec);
    }
    else
    {
        if (k > 50 || prec < (WORD(1) << ((k / 8) + 8)))
            arb_root_ui_exp(res, x, k, prec);
        else
            arb_root_ui_algebraic(res, x, k, prec);
    }
}
Esempio n. 4
0
void
arb_rising_fmpq_ui(arb_t y, const fmpq_t x, ulong n, long prec)
{
    if (n == 0)
    {
        arb_one(y);
    }
    else if (n == 1)
    {
        arb_set_fmpq(y, x, prec);
    }
    else
    {
        long wp;

        wp = ARF_PREC_ADD(prec, FLINT_BIT_COUNT(n));

        bsplit(y, fmpq_numref(x), fmpq_denref(x), 0, n, wp);

        if (fmpz_is_one(fmpq_denref(x)))
        {
            arb_set_round(y, y, prec);
        }
        else
        {
            arb_t t;
            arb_init(t);
            arb_set_fmpz(t, fmpq_denref(x));
            arb_pow_ui(t, t, n, wp);
            arb_div(y, y, t, prec);
            arb_clear(t);
        }
    }
}
Esempio n. 5
0
void
_arb_poly_evaluate_rectangular(arb_t y, arb_srcptr poly,
    long len, const arb_t x, long prec)
{
    long i, j, m, r;
    arb_ptr xs;
    arb_t s, t, c;

    if (len < 3)
    {
        if (len == 0)
        {
            arb_zero(y);
        }
        else if (len == 1)
        {
            arb_set_round(y, poly + 0, prec);
        }
        else if (len == 2)
        {
            arb_mul(y, x, poly + 1, prec);
            arb_add(y, y, poly + 0, prec);
        }
        return;
    }

    m = n_sqrt(len) + 1;
    r = (len + m - 1) / m;

    xs = _arb_vec_init(m + 1);
    arb_init(s);
    arb_init(t);
    arb_init(c);

    _arb_vec_set_powers(xs, x, m + 1, prec);

    arb_set(y, poly + (r - 1) * m);
    for (j = 1; (r - 1) * m + j < len; j++)
        arb_addmul(y, xs + j, poly + (r - 1) * m + j, prec);

    for (i = r - 2; i >= 0; i--)
    {
        arb_set(s, poly + i * m);
        for (j = 1; j < m; j++)
            arb_addmul(s, xs + j, poly + i * m + j, prec);

        arb_mul(y, y, xs + m, prec);
        arb_add(y, y, s, prec);
    }

    _arb_vec_clear(xs, m + 1);
    arb_clear(s);
    arb_clear(t);
    arb_clear(c);
}
Esempio n. 6
0
void
_arb_poly_compose_series(arb_ptr res, arb_srcptr poly1, slong len1,
                            arb_srcptr poly2, slong len2, slong n, slong prec)
{
    if (len2 == 1)
    {
        arb_set_round(res, poly1, prec);
        _arb_vec_zero(res + 1, n - 1);
    }
    else if (_arb_vec_is_zero(poly2 + 1, len2 - 2))  /* poly2 is a monomial */
    {
        slong i, j;
        arb_t t;

        arb_init(t);
        arb_set(t, poly2 + len2 - 1);
        arb_set_round(res, poly1, prec);

        for (i = 1, j = len2 - 1; i < len1 && j < n; i++, j += len2 - 1)
        {
            arb_mul(res + j, poly1 + i, t, prec);

            if (i + 1 < len1 && j + len2 - 1 < n)
                arb_mul(t, t, poly2 + len2 - 1, prec);
        }

        if (len2 != 2)
            for (i = 1; i < n; i++)
                if (i % (len2 - 1) != 0)
                    arb_zero(res + i);

        arb_clear(t);
    }
    else if (len1 < 6 || n < 6)
    {
        _arb_poly_compose_series_horner(res, poly1, len1, poly2, len2, n, prec);
    }
    else
    {
        _arb_poly_compose_series_brent_kung(res, poly1, len1, poly2, len2, n, prec);
    }
}
Esempio n. 7
0
/* atan(x) = x + eps, |eps| < x^3*/
void
arb_atan_eps(arb_t z, const arf_t x, slong prec)
{
    fmpz_t mag;
    fmpz_init(mag);
    fmpz_mul_ui(mag, ARF_EXPREF(x), 3);
    arb_set_arf(z, x);
    arb_set_round(z, z, prec);
    arb_add_error_2exp_fmpz(z, mag);
    fmpz_clear(mag);
}
Esempio n. 8
0
void
_arb_poly_evaluate_horner(arb_t y, arb_srcptr f, slong len,
                           const arb_t x, slong prec)
{
    if (len == 0)
    {
        arb_zero(y);
    }
    else if (len == 1 || arb_is_zero(x))
    {
        arb_set_round(y, f, prec);
    }
    else if (len == 2)
    {
        arb_mul(y, x, f + 1, prec);
        arb_add(y, y, f + 0, prec);
    }
    else
    {
        slong i = len - 1;
        arb_t t, u;

        arb_init(t);
        arb_init(u);
        arb_set(u, f + i);

        for (i = len - 2; i >= 0; i--)
        {
            arb_mul(t, u, x, prec);
            arb_add(u, f + i, t, prec);
        }

        arb_swap(y, u);

        arb_clear(t);
        arb_clear(u);
    }
}
/* TODO: BSGS can reduce to nv mul */
void
acb_dirichlet_arb_quadratic_powers(arb_ptr v, slong nv, const arb_t x, slong prec)
{
    slong i;
    arb_t dx, x2;
    arb_init(dx);
    arb_init(x2);
    arb_set(dx, x);
    arb_mul(x2, x, x, prec);
    for (i = 0; i < nv; i++)
    {
        if (i == 0)
            arb_one(v + i);
        else if (i == 1)
            arb_set_round(v + i, x, prec);
        else
        {
            arb_mul(dx, dx, x2, prec);
            arb_mul(v + i, v + i - 1, dx, prec);
        }
    }
    arb_clear(dx);
    arb_clear(x2);
}
Esempio n. 10
0
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);
    }
}
Esempio n. 11
0
File: exp.c Progetto: wbhart/arb
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);
}
Esempio n. 12
0
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;
}
Esempio n. 13
0
void
acb_sqrt(acb_t y, const acb_t x, slong prec)
{
    arb_t r, t, u;
    slong wp;

#define a acb_realref(x)
#define b acb_imagref(x)
#define c acb_realref(y)
#define d acb_imagref(y)

    if (arb_is_zero(b))
    {
        if (arb_is_nonnegative(a))
        {
            arb_sqrt(c, a, prec);
            arb_zero(d);
            return;
        }
        else if (arb_is_nonpositive(a))
        {
            arb_neg(d, a);
            arb_sqrt(d, d, prec);
            arb_zero(c);
            return;
        }
    }

    if (arb_is_zero(a))
    {
        if (arb_is_nonnegative(b))
        {
            arb_mul_2exp_si(c, b, -1);
            arb_sqrt(c, c, prec);
            arb_set(d, c);
            return;
        }
        else if (arb_is_nonpositive(b))
        {
            arb_mul_2exp_si(c, b, -1);
            arb_neg(c, c);
            arb_sqrt(c, c, prec);
            arb_neg(d, c);
            return;
        }
    }

    wp = prec + 4;

    arb_init(r);
    arb_init(t);
    arb_init(u);

    acb_abs(r, x, wp);
    arb_add(t, r, a, wp);

    if (arb_rel_accuracy_bits(t) > 8)
    {
        /* sqrt(a+bi) = sqrt((r+a)/2) + b/sqrt(2*(r+a))*i, r = |a+bi| */

        arb_mul_2exp_si(u, t, 1);
        arb_sqrt(u, u, wp);
        arb_div(d, b, u, prec);

        arb_set_round(c, u, prec);
        arb_mul_2exp_si(c, c, -1);
    }
    else
    {
        /*
            sqrt(a+bi) = sqrt((r+a)/2) + (b/|b|)*sqrt((r-a)/2)*i
                                         (sign)
        */

        arb_mul_2exp_si(t, t, -1);

        arb_sub(u, r, a, wp);
        arb_mul_2exp_si(u, u, -1);

        arb_sqrtpos(c, t, prec);

        if (arb_is_nonnegative(b))
        {
            arb_sqrtpos(d, u, prec);
        }
        else if (arb_is_nonpositive(b))
        {
            arb_sqrtpos(d, u, prec);
            arb_neg(d, d);
        }
        else
        {
            arb_sqrtpos(t, u, wp);
            arb_neg(u, t);
            arb_union(d, t, u, prec);
        }
    }

    arb_clear(r);
    arb_clear(t);
    arb_clear(u);

#undef a
#undef b
#undef c
#undef d
}
Esempio n. 14
0
void
arb_exp_arf_bb(arb_t z, const arf_t x, slong prec, int minus_one)
{
    slong k, iter, bits, r, mag, q, wp, N;
    slong argred_bits, start_bits;
    mp_bitcnt_t Qexp[1];
    int inexact;
    fmpz_t t, u, T, Q;
    arb_t w;

    if (arf_is_zero(x))
    {
        if (minus_one)
            arb_zero(z);
        else
            arb_one(z);
        return;
    }

    if (arf_is_special(x))
    {
        abort();
    }

    mag = arf_abs_bound_lt_2exp_si(x);

    /* We assume that this function only gets called with something
       reasonable as input (huge/tiny input will be handled by
       the main exp wrapper). */
    if (mag > 200 || mag < -2 * prec - 100)
    {
        flint_printf("arb_exp_arf_bb: unexpectedly large/small input\n");
        abort();
    }

    if (prec < 100000000)
    {
        argred_bits = 16;
        start_bits = 32;
    }
    else
    {
        argred_bits = 32;
        start_bits = 64;
    }

    /* Argument reduction: exp(x) -> exp(x/2^q). This improves efficiency
       of the first iteration in the bit-burst algorithm. */
    q = FLINT_MAX(0, mag + argred_bits);

    /* Determine working precision. */
    wp = prec + 10 + 2 * q + 2 * FLINT_BIT_COUNT(prec);
    if (minus_one && mag < 0)
        wp += (-mag);

    fmpz_init(t);
    fmpz_init(u);
    fmpz_init(Q);
    fmpz_init(T);
    arb_init(w);

    /* Convert x/2^q to a fixed-point number. */
    inexact = arf_get_fmpz_fixed_si(t, x, -wp + q);

    /* Aliasing of z and x is safe now that only use t. */
    /* Start with z = 1. */
    arb_one(z);

    /* Bit-burst loop. */
    for (iter = 0, bits = start_bits; !fmpz_is_zero(t);
        iter++, bits *= 2)
    {
        /* Extract bits. */
        r = FLINT_MIN(bits, wp);
        fmpz_tdiv_q_2exp(u, t, wp - r);

        /* Binary splitting (+1 fixed-point ulp truncation error). */
        mag = fmpz_bits(u) - r;
        N = bs_num_terms(mag, wp);

       _arb_exp_sum_bs_powtab(T, Q, Qexp, u, r, N);

        /* T = T / Q  (+1 fixed-point ulp error). */
        if (*Qexp >= wp)
        {
            fmpz_tdiv_q_2exp(T, T, *Qexp - wp);
            fmpz_tdiv_q(T, T, Q);
        }
        else
        {
            fmpz_mul_2exp(T, T, wp - *Qexp);
            fmpz_tdiv_q(T, T, Q);
        }

        /* T = 1 + T */
        fmpz_one(Q);
        fmpz_mul_2exp(Q, Q, wp);
        fmpz_add(T, T, Q);

        /* Now T = exp(u) with at most 2 fixed-point ulp error. */
        /* Set z = z * T. */
        arf_set_fmpz(arb_midref(w), T);
        arf_mul_2exp_si(arb_midref(w), arb_midref(w), -wp);
        mag_set_ui_2exp_si(arb_radref(w), 2, -wp);
        arb_mul(z, z, w, wp);

        /* Remove used bits. */
        fmpz_mul_2exp(u, u, wp - r);
        fmpz_sub(t, t, u);
    }

    /* We have exp(x + eps) - exp(x) < 2*eps (by assumption that the argument
       reduction is large enough). */
    if (inexact)
        arb_add_error_2exp_si(z, -wp + 1);

    fmpz_clear(t);
    fmpz_clear(u);
    fmpz_clear(Q);
    fmpz_clear(T);
    arb_clear(w);

    /* exp(x) = exp(x/2^q)^(2^q) */
    for (k = 0; k < q; k++)
        arb_mul(z, z, z, wp);

    if (minus_one)
        arb_sub_ui(z, z, 1, wp);

    arb_set_round(z, z, prec);
}