Пример #1
0
int
fmpz_mat_solve_cramer(fmpz_mat_t X, fmpz_t den,
                            const fmpz_mat_t A, const fmpz_mat_t B)
{
    long i, dim = fmpz_mat_nrows(A);

    if (dim == 0)
    {
        fmpz_one(den);
        return 1;
    }
    else if (dim == 1)
    {
        fmpz_set(den, fmpz_mat_entry(A, 0, 0));

        if (fmpz_is_zero(den))
            return 0;

        if (!fmpz_mat_is_empty(B))
            _fmpz_vec_set(X->rows[0], B->rows[0], fmpz_mat_ncols(B));
        return 1;
    }
    else if (dim == 2)
    {
        fmpz_t t, u;

        _fmpz_mat_det_cofactor_2x2(den, A->rows);

        if (fmpz_is_zero(den))
            return 0;

        fmpz_init(t);
        fmpz_init(u);

        for (i = 0; i < fmpz_mat_ncols(B); i++)
        {
            fmpz_mul   (t, fmpz_mat_entry(A, 1, 1), fmpz_mat_entry(B, 0, i));
            fmpz_submul(t, fmpz_mat_entry(A, 0, 1), fmpz_mat_entry(B, 1, i));
            fmpz_mul   (u, fmpz_mat_entry(A, 0, 0), fmpz_mat_entry(B, 1, i));
            fmpz_submul(u, fmpz_mat_entry(A, 1, 0), fmpz_mat_entry(B, 0, i));

            fmpz_swap(fmpz_mat_entry(X, 0, i), t);
            fmpz_swap(fmpz_mat_entry(X, 1, i), u);
        }

        fmpz_clear(t);
        fmpz_clear(u);

        return 1;
    }
    else if (dim == 3)
    {
        return _fmpz_mat_solve_cramer_3x3(X, den, A, B);
    }
    else
    {
        printf("Exception: fmpz_mat_solve_cramer: dim > 3 not implemented");
        abort();
    }
}
Пример #2
0
void _fmpz_mat_solve_cramer_2x2(fmpz * x, fmpz_t d,
    fmpz ** const a, const fmpz * b)
{
    fmpz_mul   (&x[0], &a[1][1], &b[0]);
    fmpz_submul(&x[0], &a[0][1], &b[1]);
    fmpz_mul   (&x[1], &a[0][0], &b[1]);
    fmpz_submul(&x[1], &a[1][0], &b[0]);
    _fmpz_mat_det_cofactor_2x2(d, a);
}
Пример #3
0
void
_fmpz_poly_newton_to_monomial(fmpz * poly, const fmpz * roots, slong n)
{
    slong i, j;

    for (i = n - 2; i >= 0; i--)
        for (j = i; j < n - 1; j++)
            fmpz_submul(poly + j, poly + j + 1, roots + i);
}
Пример #4
0
void _fmpz_ramanujan_tau(fmpz_t res, fmpz_factor_t factors)
{
    fmpz_poly_t poly;
    fmpz_t tau_p, p_11, next, this, prev;
    long k, r;
    ulong max_prime;

    max_prime = 1UL;
    for (k = 0; k < factors->length; k++)
    {
        /* TODO: handle overflow properly */
        max_prime = FLINT_MAX(max_prime, fmpz_get_ui(factors->p + k));
    }

    fmpz_poly_init(poly);
    fmpz_poly_ramanujan_tau(poly, max_prime + 1);

    fmpz_set_ui(res, 1);
    fmpz_init(tau_p);
    fmpz_init(p_11);
    fmpz_init(next);
    fmpz_init(this);
    fmpz_init(prev);

    for (k = 0; k < factors->length; k++)
    {
        ulong p = fmpz_get_ui(factors->p + k);

        fmpz_set(tau_p, poly->coeffs + p);
        fmpz_set_ui(p_11, p);
        fmpz_pow_ui(p_11, p_11, 11);
        fmpz_set_ui(prev, 1);
        fmpz_set(this, tau_p);

        for (r = 1; r < fmpz_get_ui(factors->exp + k); r++)
        {
            fmpz_mul(next, tau_p, this);
            fmpz_submul(next, p_11, prev);
            fmpz_set(prev, this);
            fmpz_set(this, next);
        }
        fmpz_mul(res, res, this);
    }

    fmpz_clear(tau_p);
    fmpz_clear(p_11);
    fmpz_clear(next);
    fmpz_clear(this);
    fmpz_clear(prev);
    fmpz_poly_clear(poly);
}
Пример #5
0
void _fmpz_mat_solve_cramer_3x3(fmpz * x, fmpz_t d,
    fmpz ** const a, const fmpz * b)
{
    fmpz_t t12, t13, t14, t15, t16, t17;

    fmpz_init(t12);
    fmpz_init(t13);
    fmpz_init(t14);
    fmpz_init(t15);
    fmpz_init(t16);
    fmpz_init(t17);

    fmpz_mul(t17, &a[1][0], &a[2][1]); fmpz_submul(t17, &a[1][1], &a[2][0]);
    fmpz_mul(t16, &a[1][2], &a[2][0]); fmpz_submul(t16, &a[1][0], &a[2][2]);
    fmpz_mul(t15, &a[1][1], &a[2][2]); fmpz_submul(t15, &a[1][2], &a[2][1]);
    fmpz_mul(t14, &a[2][0], &b[1]); fmpz_submul(t14, &a[1][0], &b[2]);
    fmpz_mul(t13, &a[2][1], &b[1]); fmpz_submul(t13, &a[1][1], &b[2]);
    fmpz_mul(t12, &a[2][2], &b[1]); fmpz_submul(t12, &a[1][2], &b[2]);

    fmpz_mul   (&x[0], t15, &b[0]);
    fmpz_addmul(&x[0], t13, &a[0][2]);
    fmpz_submul(&x[0], t12, &a[0][1]);

    fmpz_mul   (&x[1], t16, &b[0]);
    fmpz_addmul(&x[1], t12, &a[0][0]);
    fmpz_submul(&x[1], t14, &a[0][2]);

    fmpz_mul   (&x[2], t17, &b[0]);
    fmpz_addmul(&x[2], t14, &a[0][1]);
    fmpz_submul(&x[2], t13, &a[0][0]);

    fmpz_mul   (d, t15, &a[0][0]);
    fmpz_addmul(d, t16, &a[0][1]);
    fmpz_addmul(d, t17, &a[0][2]);

    fmpz_clear(t12);
    fmpz_clear(t13);
    fmpz_clear(t14);
    fmpz_clear(t15);
    fmpz_clear(t16);
    fmpz_clear(t17);
}
Пример #6
0
void
acb_quadratic_roots_fmpz(acb_t r1, acb_t r2,
    const fmpz_t a, const fmpz_t b, const fmpz_t c, slong prec)
{
    fmpz_t d;
    fmpz_init(d);

    /* d = b^2 - 4ac */
    fmpz_mul(d, a, c);
    fmpz_mul_2exp(d, d, 2);
    fmpz_submul(d, b, b);
    fmpz_neg(d, d);

    /* +/- sqrt(d) */
    acb_zero(r1);
    if (fmpz_sgn(d) >= 0)
    {
        arb_sqrt_fmpz(acb_realref(r1), d, prec + fmpz_bits(d) + 4);
    }
    else
    {
        fmpz_neg(d, d);
        arb_sqrt_fmpz(acb_imagref(r1), d, prec + fmpz_bits(d) + 4);
    }
    acb_neg(r2, r1);

    /* -b */
    acb_sub_fmpz(r1, r1, b, prec + 4);
    acb_sub_fmpz(r2, r2, b, prec + 4);

    /* divide by 2a */
    fmpz_mul_2exp(d, a, 1);
    acb_div_fmpz(r1, r1, d, prec);
    acb_div_fmpz(r2, r2, d, prec);

    fmpz_clear(d);
    return;
}
Пример #7
0
void
_fmpz_vec_scalar_submul_fmpz(fmpz * vec1, const fmpz * vec2, slong len2,
                             const fmpz_t x)
{
    fmpz c = *x;

    if (!COEFF_IS_MPZ(c))
    {
        if (c == 0)
            return;
        else if (c == 1)
            _fmpz_vec_sub(vec1, vec1, vec2, len2);
        else if (c == -1)
            _fmpz_vec_add(vec1, vec1, vec2, len2);
        else
            _fmpz_vec_scalar_submul_si(vec1, vec2, len2, c);
    }
    else
    {
        slong i;
        for (i = 0; i < len2; i++)
            fmpz_submul(vec1 + i, vec2 + i, x);
    }
}
Пример #8
0
int
_fmpz_mat_solve_cramer_3x3(fmpz_mat_t X, fmpz_t den,
    const fmpz_mat_t A, const fmpz_mat_t B)
{
    fmpz_t t15, t16, t17;
    int success;

    fmpz_init(t15);
    fmpz_init(t16);
    fmpz_init(t17);

    fmpz_mul(t17, AA(1,0), AA(2,1));
    fmpz_submul(t17, AA(1,1), AA(2,0));

    fmpz_mul(t16, AA(1,2), AA(2,0));
    fmpz_submul(t16, AA(1,0), AA(2,2));

    fmpz_mul(t15, AA(1,1), AA(2,2));
    fmpz_submul(t15, AA(1,2), AA(2,1));

    fmpz_mul   (den, t15, AA(0,0));
    fmpz_addmul(den, t16, AA(0,1));
    fmpz_addmul(den, t17, AA(0,2));

    success = !fmpz_is_zero(den);

    if (success)
    {
        fmpz_t t12, t13, t14, x0, x1, x2;
        long i, n = fmpz_mat_ncols(B);

        fmpz_init(t12);
        fmpz_init(t13);
        fmpz_init(t14);
        fmpz_init(x0);
        fmpz_init(x1);
        fmpz_init(x2);

        for (i = 0; i < n; i++)
        {
            fmpz_mul(t14, AA(2,0), BB(1,i));
            fmpz_submul(t14, AA(1,0), BB(2,i));

            fmpz_mul(t13, AA(2,1), BB(1,i));
            fmpz_submul(t13, AA(1,1), BB(2,i));

            fmpz_mul(t12, AA(2,2), BB(1,i));
            fmpz_submul(t12, AA(1,2), BB(2,i));

            fmpz_mul   (x0, t15, BB(0,i));
            fmpz_addmul(x0, t13, AA(0,2));
            fmpz_submul(x0, t12, AA(0,1));

            fmpz_mul   (x1, t16, BB(0,i));
            fmpz_addmul(x1, t12, AA(0,0));
            fmpz_submul(x1, t14, AA(0,2));

            fmpz_mul   (x2, t17, BB(0,i));
            fmpz_addmul(x2, t14, AA(0,1));
            fmpz_submul(x2, t13, AA(0,0));

            fmpz_swap(XX(0,i), x0);
            fmpz_swap(XX(1,i), x1);
            fmpz_swap(XX(2,i), x2);
        }

        fmpz_clear(t12);
        fmpz_clear(t13);
        fmpz_clear(t14);
        fmpz_clear(x0);
        fmpz_clear(x1);
        fmpz_clear(x2);
    }

    fmpz_clear(t15);
    fmpz_clear(t16);
    fmpz_clear(t17);

    return success;
}
static void
__ramanujan_even_common_denom(fmpz * num, fmpz * den, long start, long n)
{
    fmpz_t t, c, d, cden;
    long j, k, m, mcase;
    int prodsize;

    if (start >= n)
        return;

    fmpz_init(t);
    fmpz_init(c);
    fmpz_init(d);
    fmpz_init(cden);

    /* Common denominator */
    fmpz_primorial(cden, n + 1);

    start += start % 2;

    /* Convert initial values to common denominator */
    for (k = 0; k < start; k += 2)
    {
        fmpz_divexact(t, cden, den + k);
        fmpz_mul(num + k, num + k, t);
    }

    /* Ramanujan's recursive formula */
    for (m = start; m < n; m += 2)
    {
        mcase = m % 6;

        fmpz_mul_ui(num + m, cden, m + 3UL);
        fmpz_divexact_ui(num + m, num + m, 3UL);

        if (mcase == 4)
        {
            fmpz_neg(num + m, num + m);
            fmpz_divexact_ui(num + m, num + m, 2UL);
        }

        /* All factors are strictly smaller than m + 4; choose prodsize such
           that (m + 4)^prodsize fits in a signed long. */
        {
#if FLINT64
            if      (m < 1444L)       prodsize = 6;
            else if (m < 2097148L)    prodsize = 3;
            else if (m < 3037000495L) prodsize = 2;  /* not very likely... */
            else abort();
#else
            if      (m < 32L)    prodsize = 6;
            else if (m < 1286L)  prodsize = 3;
            else if (m < 46336L) prodsize = 2;
            else abort();
#endif
        }

        /* c = t = binomial(m+3, m) */
        fmpz_set_ui(t, m + 1UL);
        fmpz_mul_ui(t, t, m + 2UL);
        fmpz_mul_ui(t, t, m + 3UL);
        fmpz_divexact_ui(t, t, 6UL);
        fmpz_set(c, t);

        for (j = 6; j <= m; j += 6)
        {
            long r = m - j;

            /* c = binomial(m+3, m-j); */
            switch (prodsize)
            {
                case 2:
                fmpz_mul_ui(c, c, (r+6)*(r+5));
                fmpz_mul_ui(c, c, (r+4)*(r+3));
                fmpz_mul_ui(c, c, (r+2)*(r+1));
                fmpz_set_ui(d,    (j+0)*(j+3));
                fmpz_mul_ui(d, d, (j-2)*(j+2));
                fmpz_mul_ui(d, d, (j-1)*(j+1));
                fmpz_divexact(c, c, d);
                break;

                case 3:
                fmpz_mul_ui(c, c, (r+6)*(r+5)*(r+4));
                fmpz_mul_ui(c, c, (r+3)*(r+2)*(r+1));
                fmpz_set_ui(d,    (j+0)*(j+3)*(j-2));
                fmpz_mul_ui(d, d, (j+2)*(j-1)*(j+1));
                fmpz_divexact(c, c, d);
                break;

                case 6:
                fmpz_mul_ui(c, c,      (r+6)*(r+5)*(r+4)*(r+3)*(r+2)*(r+1));
                fmpz_divexact_ui(c, c, (j+0)*(j+3)*(j-2)*(j+2)*(j-1)*(j+1));
                break;
            }

            fmpz_submul(num + m, c, num + (m - j));
        }
        fmpz_divexact(num + m, num + m, t);
    }

    /* Convert to separate denominators */
    for (k = 0; k < n; k += 2)
    {
        bernoulli_number_denom(den + k, k);
        fmpz_divexact(t, cden, den + k);
        fmpz_divexact(num + k, num + k, t);
    }

    fmpz_clear(t);
    fmpz_clear(c);
    fmpz_clear(d);
    fmpz_clear(cden);
}
Пример #10
0
static 
void _fmpz_mod_mat_det(fmpz_t rop, const fmpz *M, slong n, const fmpz_t pN)
{
    fmpz *F;
    fmpz *a;
    fmpz *A;
    fmpz_t s;
    slong t, i, j, p, k;

    F = _fmpz_vec_init(n);
    a = _fmpz_vec_init((n-1) * n);
    A = _fmpz_vec_init(n);

    fmpz_init(s);

    fmpz_neg(F + 0, M + 0*n + 0);

    for (t = 1; t < n; t++)
    {
        for (i = 0; i <= t; i++)
            fmpz_set(a + 0*n + i, M + i*n + t);

        fmpz_set(A + 0, M + t*n + t);

        for (p = 1; p < t; p++)
        {
            for (i = 0; i <= t; i++)
            {
                fmpz_zero(s);
                for (j = 0; j <= t; j++)
                    fmpz_addmul(s, M + i*n + j, a + (p-1)*n + j);
                fmpz_mod(a + p*n + i, s, pN);
            }

            fmpz_set(A + p, a + p*n + t);
        }

        fmpz_zero(s);
        for (j = 0; j <= t; j++)
            fmpz_addmul(s, M + t*n + j, a + (t-1)*n + j);
        fmpz_mod(A + t, s, pN);

        for (p = 0; p <= t; p++)
        {
            fmpz_sub(F + p, F + p, A + p);
            for (k = 0; k < p; k++)
                fmpz_submul(F + p, A + k, F + (p-k-1));
            fmpz_mod(F + p, F + p, pN);
        }
    }

    /*
        Now [F{n-1}, F{n-2}, ..., F{0}, 1] is the 
        characteristic polynomial of the matrix M.
     */

    if (n % WORD(2) == 0)
    {
        fmpz_set(rop, F + (n-1));
    }
    else
    {
        fmpz_neg(rop, F + (n-1));
        fmpz_mod(rop, rop, pN);
    }

    _fmpz_vec_clear(F, n);
    _fmpz_vec_clear(a, (n-1)*n);
    _fmpz_vec_clear(A, n);
    fmpz_clear(s);
}
Пример #11
0
void _nf_elem_sub_qf(nf_elem_t a, const nf_elem_t b, 
                                   const nf_elem_t c, const nf_t nf, int can)
{
   fmpz_t d;

   const fmpz * const bnum = QNF_ELEM_NUMREF(b);
   const fmpz * const bden = QNF_ELEM_DENREF(b);
   
   const fmpz * const cnum = QNF_ELEM_NUMREF(c);
   const fmpz * const cden = QNF_ELEM_DENREF(c);
   
   fmpz * const anum = QNF_ELEM_NUMREF(a);
   fmpz * const aden = QNF_ELEM_DENREF(a);

   fmpz_init(d);
   fmpz_one(d);

   if (fmpz_equal(bden, cden))
   {
      fmpz_sub(anum, bnum, cnum);
      fmpz_sub(anum + 1, bnum + 1, cnum + 1);
      fmpz_sub(anum + 2, bnum + 2, cnum + 2);
      fmpz_set(aden, bden);

      if (can && !fmpz_is_one(aden))
      {
         fmpz_gcd(d, anum, anum + 1);
         fmpz_gcd(d, d, anum + 2);
         if (!fmpz_is_one(d))
         {
            fmpz_gcd(d, d, aden);

            if (!fmpz_is_one(d))
            {
               fmpz_divexact(anum, anum, d);
               fmpz_divexact(anum + 1, anum + 1, d);
               fmpz_divexact(anum + 2, anum + 2, d);
               fmpz_divexact(aden, aden, d);
            }
         }
      }

      fmpz_clear(d);

      return;
   }

   if (!fmpz_is_one(bden) && !fmpz_is_one(cden))
      fmpz_gcd(d, bden, cden);

   if (fmpz_is_one(d))
   {
      fmpz_mul(anum, bnum, cden);
      fmpz_mul(anum + 1, bnum + 1, cden);
      fmpz_mul(anum + 2, bnum + 2, cden);
      fmpz_submul(anum, cnum, bden);
      fmpz_submul(anum + 1, cnum + 1, bden);
      fmpz_submul(anum + 2, cnum + 2, bden);
      fmpz_mul(aden, bden, cden);
   } else
   {
      fmpz_t bden1;
      fmpz_t cden1;
      
      fmpz_init(bden1);
      fmpz_init(cden1);
      
      fmpz_divexact(bden1, bden, d);
      fmpz_divexact(cden1, cden, d);
        
      fmpz_mul(anum, bnum, cden1);
      fmpz_mul(anum + 1, bnum + 1, cden1);
      fmpz_mul(anum + 2, bnum + 2, cden1);
      fmpz_submul(anum, cnum, bden1);
      fmpz_submul(anum + 1, cnum + 1, bden1);
      fmpz_submul(anum + 2, cnum + 2, bden1);
      
      if (fmpz_is_zero(anum) && fmpz_is_zero(anum + 1) && fmpz_is_zero(anum + 2))
         fmpz_one(aden);
      else
      {
         if (can)
         {
            fmpz_t e;
            
            fmpz_init(e);
              
            fmpz_gcd(e, anum, anum + 1);
            fmpz_gcd(e, e, anum + 2);
            if (!fmpz_is_one(e))
               fmpz_gcd(e, e, d);
            
            if (fmpz_is_one(e))
               fmpz_mul(aden, bden, cden1);
            else
            {
                fmpz_divexact(anum, anum, e);
                fmpz_divexact(anum + 1, anum + 1, e);
                fmpz_divexact(anum + 2, anum + 2, e);
                fmpz_divexact(bden1, bden, e);
                fmpz_mul(aden, bden1, cden1);
            }
            
            fmpz_clear(e);
         } else
            fmpz_mul(aden, bden, cden1);
      }

      fmpz_clear(bden1);
      fmpz_clear(cden1);
   }

   fmpz_clear(d);
}
Пример #12
0
void
_fmpq_poly_interpolate_fmpz_vec(fmpz * poly, fmpz_t den,
                                    const fmpz * xs, const fmpz * ys, long n)
{
    fmpz *P, *Q, *w;
    fmpz_t t;

    long i, j;

    /* Constant */
    if (n == 1)
    {
        fmpz_set(poly, ys);
        fmpz_one(den);
        return;
    }

    /* Linear */
    if (n == 2)
    {
        fmpz_sub(den, xs, xs + 1);
        fmpz_sub(poly + 1, ys, ys + 1);
        fmpz_mul(poly, xs, ys + 1);
        fmpz_submul(poly, xs + 1, ys);
        return;
    }

    fmpz_init(t);

    P = _fmpz_vec_init(n + 1);
    Q = _fmpz_vec_init(n);
    w = _fmpz_vec_init(n);

    /* P = (x-x[0])*(x-x[1])*...*(x-x[n-1]) */
    _fmpz_poly_product_roots_fmpz_vec(P, xs, n);

    /* Weights */
    for (i = 0; i < n; i++)
    {
        fmpz_one(w + i);
        for (j = 0; j < n; j++)
        {
            if (i != j)
            {
                fmpz_sub(t, xs + i, xs + j);
                fmpz_mul(w + i, w + i, t);
            }
        }
    }

    _fmpz_vec_zero(poly, n);
    _fmpz_vec_lcm(den, w, n);

    for (i = 0; i < n; i++)
    {
        /* Q = P / (x - x[i]) */
        _fmpz_poly_div_root(Q, P, n + 1, xs + i);

        /* result += Q * weight(i) */
        fmpz_divexact(t, den, w + i);
        fmpz_mul(t, t, ys + i);
        _fmpz_vec_scalar_addmul_fmpz(poly, Q, n, t);
    }

    _fmpz_vec_clear(P, n + 1);
    _fmpz_vec_clear(Q, n);
    _fmpz_vec_clear(w, n);
    fmpz_clear(t);
}
Пример #13
0
int
_fmpz_poly_sqrt_classical(fmpz * res, const fmpz * poly, long len)
{
    long i, m;
    int result;

    /* the degree must be even */
    if (len % 2 == 0)
        return 0;

    /* valuation must be even, and then can be reduced to 0 */
    while (fmpz_is_zero(poly))
    {
        if (!fmpz_is_zero(poly + 1))
            return 0;

        fmpz_zero(res);
        poly += 2;
        len -= 2;
        res++;
    }

    /* check whether a square root exists modulo 2 */
    for (i = 1; i < len; i += 2)
        if (!fmpz_is_even(poly + i))
            return 0;

    /* check endpoints */
    if (!fmpz_is_square(poly) || (len > 1 && !fmpz_is_square(poly + len - 1)))
        return 0;

    /* square root of leading coefficient */
    m = (len + 1) / 2;
    fmpz_sqrt(res + m - 1, poly + len - 1);
    result = 1;

    /* do long divison style 'square root with remainder' from top to bottom */
    if (len > 1)
    {
        fmpz_t t, u;
        fmpz * r;

        fmpz_init(t);
        fmpz_init(u);
        r = _fmpz_vec_init(len);
        _fmpz_vec_set(r, poly, len);
        fmpz_mul_ui(u, res + m - 1, 2);

        for (i = 1; i < m; i++)
        {
            fmpz_fdiv_qr(res + m - i - 1, t, r + len - i - 1, u);
            if (!fmpz_is_zero(t))
            {
                result = 0;
                break;
            }

            fmpz_mul_si(t, res + m - i - 1, -2);
            _fmpz_vec_scalar_addmul_fmpz(r + len - 2*i, res + m - i, i - 1, t);
            fmpz_submul(r + len - 2*i - 1, res + m - i - 1, res + m - i - 1);
        }

        for (i = m; i < len && result; i++)
            if (!fmpz_is_zero(r + len - 1 - i))
                result = 0;

        _fmpz_vec_clear(r, len);
        fmpz_clear(t);
        fmpz_clear(u);
    }

    return result;
}
Пример #14
0
void acb_modular_transform(acb_t w, const psl2z_t g, const acb_t z, slong prec)
{
#define a (&g->a)
#define b (&g->b)
#define c (&g->c)
#define d (&g->d)
#define x acb_realref(z)
#define y acb_imagref(z)

    if (fmpz_is_zero(c))
    {
        /* (az+b)/d, where we must have a = d = 1 */
        acb_add_fmpz(w, z, b, prec);
    }
    else if (fmpz_is_zero(a))
    {
        /* b/(cz+d), where -bc = 1, c = 1 => -1/(z+d) */
        acb_add_fmpz(w, z, d, prec);
        acb_inv(w, w, prec);
        acb_neg(w, w);
    }
    else if (0)
    {
        acb_t t, u;

        acb_init(t);
        acb_init(u);

        acb_set_fmpz(t, b);
        acb_addmul_fmpz(t, z, a, prec);

        acb_set_fmpz(u, d);
        acb_addmul_fmpz(u, z, c, prec);

        acb_div(w, t, u, prec);

        acb_clear(t);
        acb_clear(u);
    }
    else
    {
        /* (az+b)/(cz+d) = (re+im*i)/den where

            re = bd + (bc+ad)x + ac(x^2+y^2)
            im = (ad-bc)y
            den = c^2(x^2+y^2) + 2cdx + d^2
        */

        fmpz_t t;
        arb_t re, im, den;

        arb_init(re);
        arb_init(im);
        arb_init(den);
        fmpz_init(t);

        arb_mul(im, x, x, prec);
        arb_addmul(im, y, y, prec);

        fmpz_mul(t, b, d);
        arb_set_fmpz(re, t);
        fmpz_mul(t, b, c);
        fmpz_addmul(t, a, d);
        arb_addmul_fmpz(re, x, t, prec);
        fmpz_mul(t, a, c);
        arb_addmul_fmpz(re, im, t, prec);

        fmpz_mul(t, d, d);
        arb_set_fmpz(den, t);
        fmpz_mul(t, c, d);
        fmpz_mul_2exp(t, t, 1);
        arb_addmul_fmpz(den, x, t, prec);
        fmpz_mul(t, c, c);
        arb_addmul_fmpz(den, im, t, prec);

        fmpz_mul(t, a, d);
        fmpz_submul(t, b, c);
        arb_mul_fmpz(im, y, t, prec);

        arb_div(acb_realref(w), re, den, prec);
        arb_div(acb_imagref(w), im, den, prec);

        arb_clear(re);
        arb_clear(im);
        arb_clear(den);
        fmpz_clear(t);
    }

#undef a
#undef b
#undef c
#undef d
#undef x
#undef y
}