#include "bee2/core/util.h"
#include "bee2/core/word.h"
#include "bee2/math/ww.h"
#include "bee2/math/zz.h"
bool_t zzIsEven(const word a[], size_t n)
{
ASSERT(wwIsValid(a, n));
return n == 0 || a[0] % 2 == 0;
}
bool_t zzIsOdd(const word a[], size_t n)
{
ASSERT(wwIsValid(a, n));
return n > 0 && a[0] % 2 == 1;
}
void zzAddAndW(word b[], const word a[], size_t n, register word w)
{
register word carry = 0;
register word prod;
size_t i;
ASSERT(wwIsSameOrDisjoint(a, b, n));
for (i = 0; i < n; ++i)
{
prod = w & a[i];
prod += carry;
carry = wordLess01(prod, carry);
b[i] += prod;
carry |= wordLess01(b[i], prod);
}
CLEAN3(prod, w, carry);
}
word zzSubAndW(word b[], const word a[], size_t n, register word w)
{
register word borrow = 0;
register word prod;
size_t i;
ASSERT(wwIsSameOrDisjoint(a, b, n));
for (i = 0; i < n; ++i)
{
prod = w & a[i];
prod += borrow;
borrow = wordLess01(prod, borrow);
borrow |= wordLess01(b[i], prod);
b[i] -= prod;
}
CLEAN2(prod, w);
return borrow;
}
int zzJacobi(const word a[], size_t n, const word b[], size_t m, void* stack)
{
register int t = 1;
register size_t s;
word* u = (word*)stack;
word* v = u + n;
stack = v + m;
ASSERT(wwIsValid(a, n));
ASSERT(zzIsOdd(b, m));
wwCopy(v, b, m);
m = wwWordSize(v, m);
zzMod(u, a, n, v, m, stack);
n = wwWordSize(u, m);
while (wwCmpW(v, m, 1) > 0)
{
if (wwIsZero(u, n))
{
t = 0;
break;
}
if (wwIsW(u, n, 1))
break;
s = wwLoZeroBits(u, n);
if (s % 2 && ((v[0] & 7) == 3 || (v[0] & 7) == 5))
t = -t;
wwShLo(u, n, s);
n = wwWordSize(u, n);
if ((u[0] & 3) == 3 && (v[0] & 3) == 3)
t = -t;
zzMod(v, v, m, u, n, stack);
m = wwWordSize(v, n);
wwSwap(u, v, n);
s = m, m = n, n = s;
}
CLEAN(s);
return t;
}
size_t zzJacobi_deep(size_t n, size_t m)
{
return O_OF_W(n + m) +
utilMax(2,
zzMod_deep(n, m),
zzMod_deep(m, n));
}
bool_t zzSqrt(word b[], const word a[], size_t n, void* stack)
{
register int cmp;
size_t m = (n + 1) / 2;
word* t = (word*)stack;
word* r = t + m + 1;
stack = r + m;
ASSERT(wwIsDisjoint2(a, n, b, m));
if ((n = wwWordSize(a, n)) == 0)
{
wwSetZero(b, m);
return TRUE;
}
wwSetZero(t, m + 1);
wwSetBit(t, (wwBitSize(a, n) + 1) / 2, 1);
zzSubW2(t, m + 1, 1);
ASSERT(t[m] == 0);
while (1)
{
wwCopy(b, t, m);
m = wwWordSize(b, m);
zzDiv(t, r, a, n, b, m, stack);
if (n - m == m && t[m] > 0)
return FALSE;
cmp = wwCmp(b, t, m);
if (cmp == 0)
return wwIsZero(r, m);
if (cmp < 0)
break;
t[m] = zzAdd2(t, b, m);
wwShLo(t, m + 1, 1);
ASSERT(t[m] == 0);
}
return FALSE;
}
size_t zzSqrt_deep(size_t n)
{
const size_t m = (n + 1) / 2;
return m + 1 + m + zzDiv_deep(n, m);
}