/* Copyright (C) 2014 Fredrik Johansson This file is part of Arb. Arb is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ #include "arb.h" #define TMP_ALLOC_LIMBS(__n) TMP_ALLOC((__n) * sizeof(mp_limb_t)) /* Compute wn-limb fixed-point number w, a number of ulps error, and an exponent q such that x = (w + xi * error) - q * log(2) where 0 <= w < log(2) and |xi| <= 1. Returns 0 if unsuccessful (high enough precision for log(2) to be available) and 1 if successful. Assumes nonspecial x; its exponent must not be an mpz. Error analysis: We first set n = |x| - e1 d = log(2) - e2 where 0 <= e1, e2 <= 1 ulp are the errors resulting from truncating to fixed-point numbers (note that the value of log(2) is correctly rounded). Next, we compute q, r such that 0 <= r < d and q*d + r = n (this is just integer division with remainder, done exactly). The real number we want to approximate is r_exact = |x| - q * log(2). The approximation r satisfies |r_exact - r| = |(|x| - q * log(2)) - (n - q * d)| = ||x| - q * log(2) - (|x| - e1) + q * (log(2) - e2)| = |e1 - q * e2| <= (q + 1) ulp. We select the working precision so that (q + 1) ulp is at most 1 ulp in the target precision. It is sufficient to use tn extra limbs where (q + 1) <= 2^(FLINT_BITS * tn). Note that q + 1 <= n / d + 1 <= |x| * 1.5 + 1 < 2^(exp+2). So it is sufficient to choose tn = ceil((exp+2)/FLINT_BITS). Now we round the result to the final precision: w = r - e3. This can add 1 more ulp of error, so the error may be 2 ulp. Finally, if x < 0, we correct the sign by setting,x = log(2) - x, q = -(q+1). This adds 1 more ulp (from the approximation of log(2)), for a total of 3 ulp. */ int _arb_get_mpn_fixed_mod_log2(mp_ptr w, fmpz_t q, mp_limb_t * error, const arf_t x, mp_size_t wn) { mp_srcptr xp; mp_size_t xn; int negative; slong exp; ARF_GET_MPN_READONLY(xp, xn, x); exp = ARF_EXP(x); negative = ARF_SGNBIT(x); if (exp <= -1) { /* todo: just zero top */ flint_mpn_zero(w, wn); *error = _arf_get_integer_mpn(w, xp, xn, exp + wn * FLINT_BITS); if (!negative) { fmpz_zero(q); } else { if (wn > ARB_LOG_TAB2_LIMBS) return 0; mpn_sub_n(w, arb_log_log2_tab + ARB_LOG_TAB2_LIMBS - wn, w, wn); *error += 1; /* log(2) has 1 ulp error */ fmpz_set_si(q, -1); } return 1; /* success */ } else { mp_ptr qp, rp, np; mp_srcptr dp; mp_size_t qn, rn, nn, dn, tn, alloc; TMP_INIT; tn = ((exp + 2) + FLINT_BITS - 1) / FLINT_BITS; dn = wn + tn; /* denominator */ nn = wn + 2 * tn; /* numerator */ qn = nn - dn + 1; /* quotient */ rn = dn; /* remainder */ if (dn > ARB_LOG_TAB2_LIMBS) return 0; TMP_START; alloc = qn + rn + nn; qp = TMP_ALLOC_LIMBS(alloc); rp = qp + qn; np = rp + rn; dp = arb_log_log2_tab + ARB_LOG_TAB2_LIMBS - dn; /* todo: prove that zeroing is unnecessary */ flint_mpn_zero(np, nn); _arf_get_integer_mpn(np, xp, xn, exp + dn * FLINT_BITS); mpn_tdiv_qr(qp, rp, 0, np, nn, dp, dn); if (!negative) { flint_mpn_copyi(w, rp + tn, wn); *error = 2; } else { if (mpn_add_1(qp, qp, qn, 1)) { /* I believe this cannot happen (should prove it) */ flint_printf("mod log(2): unexpected carry\n"); flint_abort(); } mpn_sub_n(w, dp + tn, rp + tn, wn); *error = 3; } /* read the exponent */ while (qn > 1 && qp[qn-1] == 0) qn--; if (qn == 1) { if (!negative) fmpz_set_ui(q, qp[0]); else fmpz_neg_ui(q, qp[0]); } else { fmpz_set_mpn_large(q, qp, qn, negative); } TMP_END; return 1; } }