#include "bee2/core/util.h"
#include "bee2/core/word.h"
#include "bee2/math/ww.h"
#include "bee2/math/zz.h"
#include "zz_lcl.h"
void zzRed(word a[], const word mod[], size_t n, void* stack)
{
ASSERT(wwIsDisjoint2(a, 2 * n, mod, n));
zzMod(a, a, 2 * n, mod, n, stack);
}
size_t zzRed_deep(size_t n)
{
return zzMod_deep(2 * n, n);
}
void FAST(zzRedCrand)(word a[], const word mod[], size_t n, void* stack)
{
register word carry;
register dword prod;
ASSERT(wwIsDisjoint2(a, 2 * n, mod, n));
ASSERT(n >= 2 && mod[0] && wwIsRepW(mod + 1, n - 1, WORD_MAX));
carry = zzAddMulW(a, a + n, n, WORD_0 - mod[0]);
zzMul11(prod, carry, WORD_0 - mod[0]);
prod += a[0];
a[0] = (word)prod;
prod >>= B_PER_W;
if (zzAddW2(a + 1, n - 1, (word)prod) || wwCmp(a, mod, n) >= 0)
zzAddW2(a, n, WORD_0 - mod[0]);
CLEAN2(prod, carry);
}
void SAFE(zzRedCrand)(word a[], const word mod[], size_t n, void* stack)
{
register word carry;
register dword prod;
register word mask;
size_t i;
ASSERT(wwIsDisjoint2(a, 2 * n, mod, n));
ASSERT(n >= 2 && mod[0] && wwIsRepW(mod + 1, n - 1, WORD_MAX));
carry = zzAddMulW(a, a + n, n, WORD_0 - mod[0]);
zzMul11(prod, carry, WORD_0 - mod[0]);
prod += a[0];
a[0] = (word)prod;
prod >>= B_PER_W;
carry = (word)prod;
mask = wordLeq01(mod[0], a[0]);
for (i = 1; i < n; ++i)
{
a[i] += carry;
carry = wordLess01(a[i], carry);
mask &= wordEq01(mod[i], a[i]);
mask |= wordLess01(mod[i], a[i]);
}
mask |= carry;
mask = WORD_0 - mask;
mask &= (WORD_0 - mod[0]);
zzAddW2(a, n, mask);
CLEAN3(prod, carry, mask);
}
size_t zzRedCrand_deep(size_t n)
{
return 0;
}
void zzRedBarrStart(word barr_param[], const word mod[], size_t n,
void* stack)
{
word* divident = (word*)stack;
stack = divident + 2 * n + 1;
ASSERT(wwIsDisjoint2(barr_param, n + 2, mod, n));
ASSERT(n > 0 && mod[n - 1] != 0);
wwSetZero(divident, 2 * n);
divident[2 * n] = 1;
zzDiv(barr_param, divident, divident, 2 * n + 1, mod, n, stack);
}
size_t zzRedBarrStart_deep(size_t n)
{
return O_OF_W(2 * n + 1) + zzDiv_deep(2 * n + 1, n);
}
void FAST(zzRedBarr)(word a[], const word mod[], size_t n,
const word barr_param[], void* stack)
{
word* q = (word*)stack;
word* qm = q + (n + 1) + (n + 2);
stack = qm + (n + 2) + n;
ASSERT(wwIsDisjoint2(a, 2 * n, mod, n));
ASSERT(wwIsDisjoint2(a, 2 * n, barr_param, n + 2));
ASSERT(n > 0 && mod[n - 1] != 0);
zzMul(q, a + n - 1, n + 1, barr_param, n + 2, stack);
zzMul(qm, q + n + 1, n + 2, mod, n, stack);
zzSub2(a, qm, n + 1);
while (wwCmp2(a, n + 1, mod, n) >= 0)
a[n] -= zzSub2(a, mod, n);
}
void SAFE(zzRedBarr)(word a[], const word mod[], size_t n,
const word barr_param[], void* stack)
{
register word w;
size_t i;
word* q = (word*)stack;
word* qm = q + (n + 1) + (n + 2);
stack = qm + (n + 2) + n;
ASSERT(wwIsDisjoint2(a, 2 * n, mod, n));
ASSERT(wwIsDisjoint2(a, 2 * n, barr_param, n + 2));
ASSERT(n > 0 && mod[n - 1] != 0);
zzMul(q, a + n - 1, n + 1, barr_param, n + 2, stack);
zzMul(qm, q + n + 1, n + 2, mod, n, stack);
zzSub2(a, qm, n + 1);
for (i = 0, w = 1; i < n; ++i)
{
w &= wordEq01(mod[i], a[i]);
w |= wordLess01(mod[i], a[i]);
}
w |= a[n], w = WORD_0 - w;
a[n] -= zzSubAndW(a, mod, n, w);
for (i = 0, w = 1; i < n; ++i)
{
w &= wordEq01(mod[i], a[i]);
w |= wordLess01(mod[i], a[i]);
}
w |= a[n], w = WORD_0 - w;
zzSubAndW(a, mod, n, w);
CLEAN(w);
}
size_t zzRedBarr_deep(size_t n)
{
return O_OF_W(4 * n + 5) +
utilMax(2,
zzMul_deep(n + 1, n + 2),
zzMul_deep(n + 2, n));
}
void FAST(zzRedMont)(word a[], const word mod[], size_t n,
register word mont_param, void* stack)
{
register word carry = 0;
register word w;
size_t i;
ASSERT(wwIsDisjoint2(a, 2 * n, mod, n));
ASSERT(n > 0 && mod[n - 1] != 0 && mod[0] % 2);
ASSERT((word)(mod[0] * mont_param + 1) == 0);
for (i = 0; i < n; ++i)
{
zzMul11Lo(w, a[i], mont_param);
carry |= zzAddW2(a + i + n, n - i, zzAddMulW(a + i, mod, n, w));
}
ASSERT(wwIsZero(a, n));
wwCopy(a, a + n, n);
a[n] = carry;
if (wwCmp2(a, n + 1, mod, n) >= 0)
zzSub2(a, mod, n);
CLEAN2(carry, w);
}
void SAFE(zzRedMont)(word a[], const word mod[], size_t n,
register word mont_param, void* stack)
{
register word carry = 0;
register word w = 0;
size_t i;
ASSERT(wwIsDisjoint2(a, 2 * n, mod, n));
ASSERT(n > 0 && mod[n - 1] != 0 && mod[0] % 2);
ASSERT((word)(mod[0] * mont_param + 1) == 0);
for (i = 0; i < n; ++i)
{
zzMul11Lo(w, a[i], mont_param);
carry |= zzAddW2(a + i + n, n - i, zzAddMulW(a + i, mod, n, w));
}
ASSERT(wwIsZero(a, n));
for (i = 0; i < n; ++i)
{
a[i] = a[n + i];
w &= wordEq01(mod[i], a[i]);
w |= wordLess01(mod[i], a[i]);
}
w |= carry, w = WORD_0 - w;
zzSubAndW(a, mod, n, w);
CLEAN2(carry, w);
}
size_t zzRedMont_deep(size_t n)
{
return 0;
}
void FAST(zzRedCrandMont)(word a[], const word mod[], size_t n,
register word mont_param, void* stack)
{
register word carry = 0;
register word borrow = 0;
register dword prod;
register word w;
size_t i;
ASSERT(wwIsDisjoint2(a, 2 * n, mod, n));
ASSERT(n >= 2 && mod[0] % 2 && wwIsRepW(mod + 1, n - 1, WORD_MAX));
ASSERT((word)(mod[0] * mont_param + 1) == 0);
for (i = 0; i < n; ++i)
{
zzMul11Lo(w, a[i], mont_param);
zzMul11(prod, w, WORD_0 - mod[0]);
w += carry;
if (w >= carry)
a[i + n] += w, carry = a[i + n] < w;
w = (word)(prod >> B_PER_W);
w += borrow;
if (w >= borrow)
borrow = a[i + 1] < w, a[i + 1] -= w;
}
carry -= zzSubW2(a + n + 1, n - 1, borrow);
wwCopy(a, a + n, n);
a[n] = carry;
if (wwCmp2(a, n + 1, mod, n) >= 0)
zzSub2(a, mod, n);
CLEAN(prod), CLEAN3(carry, borrow, w);
}
void SAFE(zzRedCrandMont)(word a[], const word mod[], size_t n,
register word mont_param, void* stack)
{
register word carry = 0;
register word borrow = 0;
register word w = 0;
register dword prod;
size_t i;
ASSERT(wwIsDisjoint2(a, 2 * n, mod, n));
ASSERT(n >= 2 && mod[0] % 2 && wwIsRepW(mod + 1, n - 1, WORD_MAX));
ASSERT((word)(mod[0] * mont_param + 1) == 0);
for (i = 0; i < n; ++i)
{
zzMul11Lo(w, a[i], mont_param);
zzMul11(prod, w, WORD_0 - mod[0]);
w += carry;
carry = wordLess01(w, carry);
a[i + n] += w;
carry |= wordLess01(a[i + n], w);
w = (word)(prod >> B_PER_W);
w += borrow;
borrow = wordLess01(w, borrow);
borrow |= wordLess01(a[i + 1], w);
a[i + 1] -= w;
}
carry -= zzSubW2(a + n + 1, n - 1, borrow);
for (i = 0; i < n; ++i)
{
a[i] = a[n + i];
w &= wordEq01(mod[i], a[i]);
w |= wordLess01(mod[i], a[i]);
}
w |= carry, w = WORD_0 - w;
zzSubAndW(a, mod, n, w);
CLEAN(prod), CLEAN3(carry, borrow, w);
}
size_t zzRedCrandMont_deep(size_t n)
{
return 0;
}