#include "radix.h"
static ulong
_radix_divmod_bn_block(nn_ptr qb, nn_ptr R, nn_srcptr W, nn_srcptr b,
nn_srcptr binv, nn_ptr t, slong bn, const radix_t radix)
{
radix_mulmid(qb, W, bn, binv, bn, 0, bn, radix);
if (bn > 3 && LIMB_RADIX(radix) >= (ulong) bn)
{
ulong one = 1;
radix_mulmid(t, qb, bn, b, bn, bn - 3, 2 * bn, radix);
radix_sub(t, t, bn + 3, W + (bn - 3), 3, radix);
if (t[2] != 0)
radix_add(t + 3, t + 3, bn, &one, 1, radix);
return radix_sub(R, W + bn, bn, t + 3, bn, radix);
}
radix_mulmid(t, qb, bn, b, bn, 0, 2 * bn, radix);
return radix_sub(R, W + bn, bn, t + bn, bn, radix);
}
int
radix_divmod_bn_classical(nn_ptr q, nn_ptr rem, nn_srcptr a, slong an,
nn_srcptr b, slong bn, slong n, const radix_t radix)
{
nn_ptr binv, W, t, qlast;
slong blk, blocks, ext;
slong pend;
TMP_INIT;
FLINT_ASSERT(an >= 1);
FLINT_ASSERT(bn >= 1);
FLINT_ASSERT(n >= 1);
if (bn == 1)
return radix_divmod_bn_1(q, rem, a, an, b[0], n, radix);
TMP_START;
binv = TMP_ALLOC(bn * sizeof(ulong));
if (!radix_invmod_bn(binv, b, bn, bn, radix))
{
TMP_END;
return 0;
}
W = TMP_ALLOC((2 * bn) * sizeof(ulong));
t = TMP_ALLOC((2 * bn) * sizeof(ulong));
qlast = TMP_ALLOC(bn * sizeof(ulong));
blocks = (n + bn - 1) / bn;
ext = blocks * bn - n;
{
slong lo_avail = FLINT_MIN(an, bn);
flint_mpn_copyi(W, a, lo_avail);
flint_mpn_zero(W + lo_avail, 2 * bn - lo_avail);
if (an > bn)
flint_mpn_copyi(W + bn, a + bn, FLINT_MIN(an - bn, bn));
}
pend = 0;
for (blk = 0; blk < blocks; blk++)
{
slong qoff = blk * bn;
if (blk < blocks - 1)
{
ulong bw = _radix_divmod_bn_block(q + qoff, W, W, b, binv, t, bn, radix);
slong nextoff = (blk + 2) * bn;
if (nextoff < an)
{
slong nextavail = FLINT_MIN(bn, an - nextoff);
flint_mpn_copyi(W + bn, a + nextoff, nextavail);
flint_mpn_zero(W + bn + nextavail, bn - nextavail);
}
else
{
flint_mpn_zero(W + bn, bn);
}
{
ulong one = 1;
slong tot = pend + (slong) bw;
pend = 0;
while (tot > 0)
{
if (radix_sub(W + bn, W + bn, bn, &one, 1, radix))
pend++;
tot--;
}
}
}
else
{
slong qlen = n - qoff;
if (rem == NULL)
{
radix_mulmid(q + qoff, W, bn, binv, bn, 0, qlen, radix);
}
else
{
nn_ptr qb = (ext != 0) ? qlast : (q + qoff);
(void) _radix_divmod_bn_block(qb, W, W, b, binv, t, bn, radix);
if (ext != 0)
flint_mpn_copyi(q + qoff, qlast, qlen);
}
}
}
if (rem != NULL)
{
if (ext == 0)
{
flint_mpn_copyi(rem, W, bn);
}
else
{
nn_srcptr E = qlast + (bn - ext);
radix_mulmid(rem, b, bn, E, ext, 0, bn, radix);
radix_add(rem + ext, rem + ext, bn - ext, W, bn - ext, radix);
}
}
TMP_END;
return 1;
}