#include "bee2/core/util.h"
#include "bee2/math/ww.h"
#include "bee2/math/zz.h"
void zzGCD(word d[], const word a[], size_t n, const word b[], size_t m,
void* stack)
{
register size_t s;
word* u = (word*)stack;
word* v = u + n;
ASSERT(wwIsDisjoint2(a, n, d, MIN2(n, m)));
ASSERT(wwIsDisjoint2(b, m, d, MIN2(n, m)));
ASSERT(!wwIsZero(a, n) && !wwIsZero(b, m));
wwSetZero(d, MIN2(n, m));
wwCopy(u, a, n);
wwCopy(v, b, m);
s = utilMin(2, wwLoZeroBits(u, n), wwLoZeroBits(v, m));
wwShLo(u, n, s);
n = wwWordSize(u, n);
wwShLo(v, m, s);
m = wwWordSize(v, m);
do
{
wwShLo(u, n, wwLoZeroBits(u, n));
n = wwWordSize(u, n);
wwShLo(v, m, wwLoZeroBits(v, m));
m = wwWordSize(v, m);
if (wwCmp2(u, n, v, m) > 0)
zzSubW2(u + m, n - m, zzSub2(u, v, m));
else
zzSubW2(v + n, m - n, zzSub2(v, u, n));
} while (!wwIsZero(v, m));
wwCopy(d, u, n);
wwShHi(d, W_OF_B(wwBitSize(d, m) + s), s);
CLEAN(s);
}
size_t zzGCD_deep(size_t n, size_t m)
{
return O_OF_W(n + m);
}
bool_t zzIsCoprime(const word a[], size_t n, const word b[], size_t m, void* stack)
{
word* d = (word*)stack;
stack = d + MIN2(n, m);
if (wwIsZero(a, n))
return wwIsW(b, m, 1);
if (wwIsZero(b, m))
return wwIsW(a, n, 1);
zzGCD(d, a, n, b, m, stack);
return wwIsW(d, MIN2(n, m), 1);
}
size_t zzIsCoprime_deep(size_t n, size_t m)
{
return O_OF_W(MIN2(n, m)) + zzGCD_deep(n, m);
}
void zzLCM(word d[], const word a[], size_t n, const word b[], size_t m,
void* stack)
{
word* prod = (word*)stack;
word* gcd = prod + n + m;
word* r = gcd + MIN2(n, m);
stack = r + MIN2(n, m);
ASSERT(wwIsDisjoint2(a, n, d, MAX2(n, m)));
ASSERT(wwIsDisjoint2(b, m, d, MAX2(n, m)));
ASSERT(!wwIsZero(a, n) && !wwIsZero(b, m));
wwSetZero(d, n + m);
n = wwWordSize(a, n);
m = wwWordSize(b, m);
zzMul(prod, a, n, b, m, stack);
zzGCD(gcd, a, n, b, m, stack);
if (n < m)
SWAP(n, m);
n += m;
m = wwWordSize(gcd, m);
zzDiv(d, r, prod, n, gcd, m, stack);
}
size_t zzLCM_deep(size_t n, size_t m)
{
return O_OF_W(n + m + MIN2(n, m) + MIN2(n, m)) +
utilMax(3,
zzMul_deep(n, m),
zzGCD_deep(n, m),
zzMod_deep(n + m, MIN2(n, m)));
}
void zzExGCD(word d[], word da[], word db[], const word a[], size_t n,
const word b[], size_t m, void* stack)
{
register size_t s;
register size_t nu, mv;
word* aa = (word*)stack;
word* bb = aa + n;
word* u = bb + m;
word* v = u + n;
word* da1 = v + m;
word* db1 = da1 + m;
ASSERT(wwIsDisjoint3(da, m, db, n, d, MIN2(n, m)));
ASSERT(wwIsDisjoint2(a, n, d, MIN2(n, m)));
ASSERT(wwIsDisjoint2(b, m, d, MIN2(n, m)));
ASSERT(wwIsDisjoint2(a, n, da, m));
ASSERT(wwIsDisjoint2(b, m, da, m));
ASSERT(wwIsDisjoint2(a, n, db, n));
ASSERT(wwIsDisjoint2(b, m, db, n));
ASSERT(!wwIsZero(a, n) && !wwIsZero(b, m));
wwSetZero(d, MIN2(n, m));
wwSetW(da, m, 1);
wwSetZero(db, n);
wwSetZero(da1, m);
wwSetW(db1, n, 1);
s = utilMin(2, wwLoZeroBits(a, n), wwLoZeroBits(b, m));
wwCopy(aa, a, n), wwShLo(aa, n, s), n = wwWordSize(aa, n);
wwCopy(bb, b, m), wwShLo(bb, m, s), m = wwWordSize(bb, m);
wwCopy(u, aa, n);
wwCopy(v, bb, m);
nu = n, mv = m;
do
{
for (; u[0] % 2 == 0; wwShLo(u, nu, 1))
if (da[0] % 2 == 0 && db[0] % 2 == 0)
{
wwShLo(da, m, 1);
wwShLo(db, n, 1);
}
else
{
ASSERT((da[0] + bb[0]) % 2 == 0);
ASSERT((db[0] + aa[0]) % 2 == 0);
wwShLoCarry(da, m, 1, zzAdd2(da, bb, m));
wwShLoCarry(db, n, 1, zzAdd2(db, aa, n));
}
for (; v[0] % 2 == 0; wwShLo(v, mv, 1))
if (da1[0] % 2 == 0 && db1[0] % 2 == 0)
{
wwShLo(da1, m, 1);
wwShLo(db1, n, 1);
}
else
{
ASSERT((da1[0] + bb[0]) % 2 == 0);
ASSERT((db1[0] + aa[0]) % 2 == 0);
wwShLoCarry(da1, m, 1, zzAdd2(da1, bb, m));
wwShLoCarry(db1, n, 1, zzAdd2(db1, aa, n));
}
nu = wwWordSize(u, nu);
mv = wwWordSize(v, mv);
if (wwCmp2(u, nu, v, mv) > 0)
{
zzSubW2(u + mv, nu - mv, zzSub2(u, v, mv));
if (zzAdd2(da, da1, m) || wwCmp(da, bb, m) >= 0)
zzSub2(da, bb, m);
if (zzAdd2(db, db1, n) || wwCmp(db, aa, n) >= 0)
zzSub2(db, aa, n);
}
else
{
zzSubW2(v + nu, mv - nu, zzSub2(v, u, nu));
if (zzAdd2(da1, da, m) || wwCmp(da1, bb, m) >= 0)
zzSub2(da1, bb, m);
if (zzAdd2(db1, db, n) || wwCmp(db1, aa, n) >= 0)
zzSub2(db1, aa, n);
}
} while (!wwIsZero(v, mv));
wwCopy(d, u, nu);
wwShHi(d, W_OF_B(wwBitSize(d, nu) + s), s);
CLEAN3(s, nu, mv);
}
size_t zzExGCD_deep(size_t n, size_t m)
{
return O_OF_W(3 * n + 3 * m);
}
void zzDivMod(word b[], const word divident[], const word a[],
const word mod[], size_t n, void* stack)
{
register size_t nu, nv;
word* u = (word*)stack;
word* v = u + n;
word* da = v + n;
word* da1 = da + n;
ASSERT(wwCmp(a, mod, n) < 0);
ASSERT(wwCmp(divident, mod, n) < 0);
ASSERT(wwIsDisjoint(b, mod, n));
ASSERT(zzIsOdd(mod, n) && mod[n - 1] != 0);
wwCopy(da, divident, n);
wwSetZero(da1, n);
wwCopy(u, a, n);
wwCopy(v, mod, n);
nu = wwWordSize(u, n);
nv = n;
while (!wwIsZero(v, nv))
{
for (; u[0] % 2 == 0; wwShLo(u, nu, 1))
if (da[0] % 2 == 0)
wwShLo(da, n, 1);
else
wwShLoCarry(da, n, 1, zzAdd2(da, mod, n));
for (; v[0] % 2 == 0; wwShLo(v, nv, 1))
if (da1[0] % 2 == 0)
wwShLo(da1, n, 1);
else
wwShLoCarry(da1, n, 1, zzAdd2(da1, mod, n));
nu = wwWordSize(u, nu);
nv = wwWordSize(v, nv);
if (wwCmp2(u, nu, v, nv) > 0)
{
zzSubW2(u + nv, nu - nv, zzSub2(u, v, nv));
if (zzAdd2(da, da1, n) || wwCmp(da, mod, n) >= 0)
zzSub2(da, mod, n);
}
else
{
zzSubW2(v + nu, nv - nu, zzSub2(v, u, nu));
if (zzAdd2(da1, da, n) || wwCmp(da1, mod, n) >= 0)
zzSub2(da1, mod, n);
}
}
EXPECT(wwIsW(u, nu, 1));
if (!wwIsW(u, nu, 1))
wwSetZero(b, n);
wwCopy(b, da, n);
CLEAN2(nu, nv);
}
size_t zzDivMod_deep(size_t n)
{
return O_OF_W(4 * n);
}
size_t zzAlmostInvMod(word b[], const word a[], const word mod[], size_t n,
void* stack)
{
register size_t k = 0;
size_t nu, nv;
word* u = (word*)stack;
word* v = u + n;
word* da0 = v + n;
word* da = da0 + n + 1;
ASSERT(!wwIsZero(a, n));
ASSERT(wwCmp(a, mod, n) < 0);
ASSERT(wwIsDisjoint(b, mod, n));
ASSERT(zzIsOdd(mod, n) && mod[n - 1] != 0);
wwSetW(da0, n + 1, 1);
wwSetZero(da, n + 1);
wwCopy(u, a, n);
wwCopy(v, mod, n);
nu = wwWordSize(u, n);
nv = n;
do
{
if (zzIsEven(v, nv))
{
wwShLo(v, nv, 1);
nv = wwWordSize(v, nv);
wwShHi(da0, n + 1, 1);
}
else if (zzIsEven(u, nu))
{
wwShLo(u, nu, 1);
nu = wwWordSize(u, nu);
wwShHi(da, n + 1, 1);
}
else if (wwCmp2(v, nv, u, nu) > 0)
{
ASSERT(nv >= nu);
zzSubW2(v + nu, nv - nu, zzSub2(v, u, nu));
wwShLo(v, nv, 1);
nv = wwWordSize(v, nv);
zzAdd2(da, da0, n + 1);
wwShHi(da0, n + 1, 1);
}
else
{
ASSERT(nu >= nv);
zzSubW2(u + nv, nu - nv, zzSub2(u, v, nv));
wwShLo(u, nu, 1);
nu = wwWordSize(u, nu);
zzAdd2(da0, da, n + 1);
wwShHi(da, n + 1, 1);
}
k = k + 1;
} while (!wwIsZero(u, nu));
EXPECT(wwIsW(v, nv, 1));
if (!wwIsW(v, nv, 1))
wwSetZero(b, n);
if (wwCmp2(da, n + 1, mod, n) >= 0)
da[n] -= zzSub2(da, mod, n);
ASSERT(wwCmp2(da, n + 1, mod, n) < 0);
zzNegMod(b, da, mod, n);
return k;
}
size_t zzAlmostInvMod_deep(size_t n)
{
return O_OF_W(4 * n + 2);
}