#include "radix.h"
static void
_radix2_mask(nn_ptr a, slong d, slong alimbs, slong e)
{
slong full = d / e, r = d % e, i;
if (r)
{
a[full] &= (UWORD(1) << r) - 1;
for (i = full + 1; i < alimbs; i++)
a[i] = 0;
}
else
{
for (i = full; i < alimbs; i++)
a[i] = 0;
}
}
static void
_radix_halve_odd(nn_ptr W, nn_srcptr V, slong k, const radix_t radix)
{
ulong B = LIMB_RADIX(radix);
ulong Bhalf = B >> 1;
ulong rem, par;
slong i;
par = 0;
for (i = 0; i < k; i++)
par ^= V[i];
par &= 1;
rem = par;
for (i = k - 1; i >= 0; i--)
{
ulong v = V[i];
if (rem)
{
W[i] = Bhalf + (v >> 1) + (v & 1);
rem = 1 - (v & 1);
}
else
{
W[i] = v >> 1;
rem = v & 1;
}
}
}
static ulong
n_rsqrtmod_bn(ulong x, const radix_t radix)
{
nmod_t mod = radix->B;
ulong p = DIGIT_RADIX(radix);
slong e = radix->exp;
ulong y, t;
if (p == 2)
{
slong iter;
if ((x & 7) != 1)
return 0;
y = 1;
for (iter = 0; iter < 2 * e; iter++)
{
t = nmod_mul(nmod_mul(y, y, mod), x, mod);
if (t == 1)
break;
t = nmod_sub(t, 1, mod);
t >>= 1;
y = nmod_sub(y, nmod_mul(y, t, mod), mod);
}
return y;
}
else
{
ulong inv2, s;
slong d;
s = n_sqrtmod(nmod_set_ui(x, radix->b), p);
if (s == 0)
return 0;
if (s > (p - 1) / 2)
s = p - s;
y = n_invmod(s, p);
if (e == 1)
return y;
inv2 = (mod.n + 1) / 2;
for (d = 1; d < e; d *= 2)
{
t = nmod_mul(nmod_mul(y, y, mod), x, mod);
t = nmod_sub(t, 1, mod);
t = nmod_mul(t, inv2, mod);
y = nmod_sub(y, nmod_mul(y, t, mod), mod);
}
return y;
}
}
static void
_radix2_rsqrtmod_bn(nn_ptr res, nn_srcptr x, slong xn, slong n, const radix_t radix)
{
slong e = radix->exp;
slong D = e * n;
slong ed[2 * FLINT_BITS];
slong L, k;
ulong one = 1;
nn_ptr y2, w, t;
TMP_INIT;
FLINT_ASSERT(e >= 3);
for (k = 1; k < n; k++)
res[k] = 0;
ed[0] = D;
L = 0;
while (ed[L] > e)
{
ed[L + 1] = (ed[L] + 3) / 2;
L++;
}
TMP_START;
y2 = TMP_ALLOC((n + 1) * sizeof(ulong));
w = TMP_ALLOC((n + 1) * sizeof(ulong));
t = TMP_ALLOC((n + 1) * sizeof(ulong));
for (k = L - 1; k >= 0; k--)
{
slong nd = ed[k];
slong hd = nd + 1;
slong nl = (nd + e - 1) / e;
slong hl = (hd + e - 1) / e;
radix_mulmid(y2, res, hl, res, hl, 0, hl, radix);
_radix2_mask(y2, hd, hl, e);
radix_mulmid(w, x, FLINT_MIN(xn, hl), y2, hl, 0, hl, radix);
_radix2_mask(w, hd, hl, e);
radix_sub(w, w, hl, &one, 1, radix);
_radix2_mask(w, hd, hl, e);
radix_rshift_digits(w, w, hl, 1, radix);
radix_mulmid(t, res, nl, w, nl, 0, nl, radix);
_radix2_mask(t, nd, nl, e);
radix_sub(res, res, nl, t, nl, radix);
_radix2_mask(res, nd, nl, e);
}
TMP_END;
}
int
radix_rsqrtmod_bn(nn_ptr res, nn_srcptr x, slong xn, slong n, const radix_t radix)
{
slong i, m;
slong a[FLINT_BITS];
nn_ptr t, hbuf, scr;
TMP_INIT;
FLINT_ASSERT(xn >= 1);
FLINT_ASSERT(n >= 1);
res[0] = n_rsqrtmod_bn(x[0], radix);
if (res[0] == 0)
return 0;
if (n == 1)
return 1;
if (DIGIT_RADIX(radix) == 2)
{
TMP_START;
t = TMP_ALLOC((n + 1) * sizeof(ulong));
t[0] = res[0];
t[n] = 0;
_radix2_rsqrtmod_bn(t, x, xn, n, radix);
flint_mpn_copyi(res, t, n);
TMP_END;
return 1;
}
a[i = 0] = n;
while (a[i] > 1)
{
a[i + 1] = (a[i] + 1) / 2;
i++;
}
TMP_START;
t = TMP_ALLOC(n * sizeof(ulong));
hbuf = TMP_ALLOC(n * sizeof(ulong));
scr = TMP_ALLOC(n * sizeof(ulong));
for (i--; i >= 0; i--)
{
slong nn = a[i];
slong nm;
ulong one = 1;
m = a[i + 1];
nm = nn - m;
radix_mulmid(t, res, m, res, m, 0, nn, radix);
_radix_mulhigh_known_low(hbuf, x, FLINT_MIN(xn, nn), t, nn,
&one, 1, m, nn, scr, radix);
radix_mulmid(t, res, m, hbuf, nm, 0, nm, radix);
_radix_halve_odd(t, t, nm, radix);
radix_neg(res + m, t, nm, radix);
}
TMP_END;
return 1;
}