#include "bee2/core/util.h"
#include "bee2/core/word.h"
#include "bee2/math/ww.h"
#include "bee2/math/zz.h"
#include "zz_lcl.h"
word zzMulW(word b[], const word a[], size_t n, register word w)
{
register word carry = 0;
register dword prod;
size_t i;
ASSERT(wwIsSameOrDisjoint(a, b, n));
for (i = 0; i < n; ++i)
{
zzMul11(prod, w, a[i]);
prod += carry;
b[i] = (word)prod;
carry = (word)(prod >> B_PER_W);
}
CLEAN2(prod, w);
return carry;
}
word zzAddMulW(word b[], const word a[], size_t n, register word w)
{
register word carry = 0;
register dword prod;
size_t i;
ASSERT(wwIsSameOrDisjoint(a, b, n));
for (i = 0; i < n; ++i)
{
zzMul11(prod, w, a[i]);
prod += carry;
prod += b[i];
b[i] = (word)prod;
carry = (word)(prod >> B_PER_W);
}
CLEAN2(prod, w);
return carry;
}
word zzSubMulW(word b[], const word a[], size_t n, register word w)
{
register word borrow = 0;
register dword prod;
size_t i;
ASSERT(wwIsSameOrDisjoint(a, b, n));
for (i = 0; i < n; ++i)
{
zzMul11(prod, w, a[i]);
prod = (dword)0 - prod;
prod += b[i];
prod -= borrow;
b[i] = (word)prod;
borrow = WORD_0 - (word)(prod >> B_PER_W);
}
CLEAN2(prod, w);
return borrow;
}
void zzMul(word c[], const word a[], size_t n, const word b[], size_t m,
void* stack)
{
register word carry = 0;
register dword prod;
size_t i, j;
ASSERT(wwIsDisjoint2(a, n, c, n + m));
ASSERT(wwIsDisjoint2(b, m, c, n + m));
wwSetZero(c, n + m);
for (i = 0; i < n; ++i)
{
for (j = 0; j < m; ++j)
{
zzMul11(prod, a[i], b[j]);
prod += carry;
prod += c[i + j];
c[i + j] = (word)prod;
carry = (word)(prod >> B_PER_W);
}
c[i + j] = carry;
carry = 0;
}
CLEAN(prod);
}
size_t zzMul_deep(size_t n, size_t m)
{
return 0;
}
void zzSqr(word b[], const word a[], size_t n, void* stack)
{
register word carry = 0;
register word carry1;
register dword prod;
size_t i, j;
ASSERT(wwIsDisjoint2(a, n, b, n + n));
wwSetZero(b, n + n);
for (i = 0; i < n; ++i)
{
for (j = i + 1; j < n; ++j)
{
zzMul11(prod, a[i], a[j]);
prod += carry;
prod += b[i + j];
b[i + j] = (word)prod;
carry = (word)(prod >> B_PER_W);
}
b[i + j] = carry;
carry = 0;
}
for (i = 0; i < n + n; ++i)
{
carry1 = b[i] >> (B_PER_W - 1);
b[i] = (b[i] << 1) | carry;
carry = carry1;
}
for (i = 0; i < n; ++i)
{
zzMul11(prod, a[i], a[i]);
prod += carry;
prod += b[i + i];
b[i + i] = (word)prod;
prod >>= B_PER_W;
prod += b[i + i + 1];
b[i + i + 1] = (word)prod;
carry = (word)(prod >> B_PER_W);
}
CLEAN3(prod, carry, carry1);
}
size_t zzSqr_deep(size_t n)
{
return 0;
}
word zzDivW(word q[], const word a[], size_t n, register word w)
{
register word r = 0;
register dword divisor;
ASSERT(w > 0);
ASSERT(wwIsSameOrDisjoint(a, q, n));
while (n--)
{
divisor = r;
divisor <<= B_PER_W;
divisor |= a[n];
q[n] = (word)(divisor / w);
r = (word)(divisor % w);
}
CLEAN2(divisor, w);
return r;
}
word zzModW(const word a[], size_t n, register word w)
{
register word r = 0;
register dword divisor;
ASSERT(w > 0);
ASSERT(wwIsValid(a, n));
while (n--)
{
divisor = r;
divisor <<= B_PER_W;
divisor |= a[n];
r = (word)(divisor % w);
}
CLEAN2(divisor, w);
return r;
}
word zzModW2(const word a[], size_t n, register word w)
{
register word r0 = 0;
register dword r1 = 0;
register word b;
ASSERT(w > 0);
ASSERT(w <= WORD_BIT_HALF);
ASSERT(wwIsValid(a, n));
b = (WORD_MAX - w + 1) % w;
while (n--)
{
r1 *= b;
r1 += r0;
r1 *= b;
r1 += a[n];
r0 = (word)r1;
r1 >>= B_PER_W;
}
#ifdef SAFE_FAST
while (r1 != 0)
{
r1 *= b;
r1 += r0 % w;
r0 = (word)r1;
r1 >>= B_PER_W;
}
r0 %= w;
#else
r1 *= b;
r1 += r0 % w;
r0 = (word)r1;
r1 >>= B_PER_W;
r1 *= b;
r1 += r0 % w;
r0 = (word)r1 % w;
#endif
CLEAN3(r1, b, w);
return r0;
}
void zzDiv(word q[], word r[], const word a[], size_t n, const word b[],
size_t m, void* stack)
{
register dword qhat;
register size_t shift;
register word t;
size_t i;
word* divident;
word* divisor;
word* mul;
ASSERT(n >= m);
ASSERT(wwIsValid(a, n) && wwIsValid(b, m));
ASSERT(m > 0 && b[m - 1] > 0);
ASSERT(wwIsDisjoint2(q, n + 1 - m, r, m));
ASSERT(a == r || wwIsDisjoint2(a, n, r, m));
if (wwCmp2(a, n, b, m) < 0)
{
wwSetZero(q, n - m + 1);
wwCopy(r, a, m);
return;
}
if (m == 1)
{
r[0] = zzDivW(q, a, n, b[0]);
return;
}
divident = (word*)stack;
divisor = divident + n + 1;
mul = divisor + m;
wwCopy(divident, a, n);
divident[n] = 0;
wwCopy(divisor, b, m);
shift = wordCLZ(b[m - 1]);
wwShHi(divident, n + 1, shift);
wwShHi(divisor, m, shift);
for (i = n; i >= m; --i)
{
if (divident[i] == divisor[m - 1])
q[i - m] = WORD_MAX;
else
{
qhat = divident[i];
qhat <<= B_PER_W;
qhat |= divident[i - 1];
qhat /= divisor[m - 1];
q[i - m] = (word)qhat;
}
wwCopy(mul, divisor + m - 2, 2);
mul[2] = zzMulW(mul, mul, 2, q[i - m]);
while (wwCmp2(mul, 3, divident + i - 2, 3) > 0)
{
q[i - m]--;
mul[2] -= zzSub2(mul, divisor + m - 2, 2);
}
t = zzSubMulW(divident + i - m, divisor, m, q[i - m]);
divident[i] -= t;
if (divident[i] > (word)~t)
{
q[i - m]--;
divident[i] += zzAdd2(divident + i - m, divisor, m);
}
}
wwShLo(divident, n + 1, shift);
wwCopy(r, divident, m);
CLEAN3(t, shift, qhat);
}
size_t zzDiv_deep(size_t n, size_t m)
{
return O_OF_W(n + m + 4);
}
void zzMod(word r[], const word a[], size_t n, const word b[], size_t m, void* stack)
{
register dword qhat;
register size_t shift;
register word t;
size_t i;
word* divident;
word* divisor;
word* mul;
ASSERT(wwIsValid(a, n) && wwIsValid(b, m));
ASSERT(m > 0 && b[m - 1] > 0);
ASSERT(a == r || wwIsDisjoint2(a, n, r, m));
if (wwCmp2(a, n, b, m) < 0)
{
if (n < m)
wwSetZero(r + n, m - n), m = n;
wwCopy(r, a, m);
return;
}
if (m == 1)
{
r[0] = zzModW(a, n, b[0]);
return;
}
divident = (word*)stack;
divisor = divident + n + 1;
mul = divisor + m;
wwCopy(divident, a, n);
divident[n] = 0;
wwCopy(divisor, b, m);
shift = wordCLZ(b[m - 1]);
wwShHi(divident, n + 1, shift);
wwShHi(divisor, m, shift);
for (i = n; i >= m; --i)
{
if (divident[i] == divisor[m - 1])
t = WORD_MAX;
else
{
qhat = divident[i];
qhat <<= B_PER_W;
qhat |= divident[i - 1];
qhat /= divisor[m - 1];
t = (word)qhat;
}
wwCopy(mul, divisor + m - 2, 2);
mul[2] = zzMulW(mul, mul, 2, t);
while (wwCmp2(mul, 3, divident + i - 2, 3) > 0)
{
t--;
mul[2] -= zzSub2(mul, divisor + m - 2, 2);
}
t = zzSubMulW(divident + i - m, divisor, m, t);
divident[i] -= t;
if (divident[i] > (word)~t)
divident[i] += zzAdd2(divident + i - m, divisor, m);
}
wwShLo(divident, n + 1, shift);
wwCopy(r, divident, m);
CLEAN3(t, shift, qhat);
}
size_t zzMod_deep(size_t n, size_t m)
{
return O_OF_W(n + m + 4);
}