#include <libecc/nn/nn_mul_public.h>
#include <libecc/nn/nn_logical.h>
#include <libecc/nn/nn_add.h>
#include <libecc/nn/nn.h>
#include "nn_div.h"
ATTRIBUTE_WARN_UNUSED_RET static int _nn_cmp_shift(nn_src_t in1, nn_src_t in2, u8 shift, int *cmp)
{
int ret, mask, tmp;
u8 i;
MUST_HAVE((in1->wlen >= (in2->wlen + shift)), ret, err);
MUST_HAVE((cmp != NULL), ret, err);
tmp = 0;
for (i = in2->wlen; i > 0; i--) {
mask = (!(tmp & 0x1));
tmp += ((in1->val[shift + i - 1] > in2->val[i - 1]) & mask);
tmp -= ((in1->val[shift + i - 1] < in2->val[i - 1]) & mask);
}
(*cmp) = tmp;
ret = 0;
err:
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int _nn_cnd_sub_shift(int cnd, nn_t out, nn_src_t in,
u8 shift, word_t *borrow)
{
word_t tmp, borrow1, borrow2, _borrow = WORD(0);
word_t mask = WORD_MASK_IFNOTZERO(cnd);
int ret;
u8 i;
MUST_HAVE((out->wlen >= (in->wlen + shift)), ret, err);
MUST_HAVE((borrow != NULL), ret, err);
for (i = 0; i < in->wlen; i++) {
tmp = (word_t)(out->val[shift + i] - (in->val[i] & mask));
borrow1 = (word_t)(tmp > out->val[shift + i]);
out->val[shift + i] = (word_t)(tmp - _borrow);
borrow2 = (word_t)(out->val[shift + i] > tmp);
_borrow = (word_t)(borrow1 | borrow2);
}
(*borrow) = _borrow;
ret = 0;
err:
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int _nn_submul_word_shift(nn_t out, nn_src_t in, word_t w, u8 shift,
word_t *borrow)
{
word_t _borrow = WORD(0), prod_high, prod_low, tmp;
int ret;
u8 i;
MUST_HAVE((out->wlen >= (in->wlen + shift)), ret, err);
MUST_HAVE((borrow != NULL), ret, err);
for (i = 0; i < in->wlen; i++) {
WORD_MUL(prod_high, prod_low, in->val[i], w);
prod_low = (word_t)(prod_low + _borrow);
prod_high = (word_t)(prod_high + (prod_low < _borrow));
tmp = (word_t)(out->val[shift + i] - prod_low);
_borrow = (word_t)(prod_high + (tmp > out->val[shift + i]));
out->val[shift + i] = tmp;
}
(*borrow) = _borrow;
ret = 0;
err:
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int _nn_divrem_normalized(nn_t q, nn_t r,
nn_src_t a, nn_src_t b, word_t v)
{
word_t borrow, qstar, qh, ql, rh, rl;
int _small, cmp, ret;
u8 i;
MUST_HAVE(!(b->wlen <= 0), ret, err);
MUST_HAVE(!(a->wlen <= b->wlen), ret, err);
MUST_HAVE(!(!((b->val[b->wlen - 1] >> (WORD_BITS - 1)) == WORD(1))), ret, err);
MUST_HAVE(!_nn_cmp_shift(a, b, (u8)(a->wlen - b->wlen), &cmp) && (cmp < 0), ret, err);
if (r != a) {
ret = nn_set_wlen(r, a->wlen); EG(ret, err);
ret = nn_copy(r, a); EG(ret, err);
}
ret = nn_set_wlen(q, (u8)(r->wlen - b->wlen)); EG(ret, err);
for (i = r->wlen; i > b->wlen; i--) {
u8 shift = (u8)(i - b->wlen - 1);
rh = r->val[i - 1];
rl = r->val[i - 2];
WORD_MUL(qh, ql, rl, v);
WORD_MUL(qstar, ql, rh, v);
qh = (word_t)(qh + ql);
qstar = (word_t)(qstar + (qh < ql));
qh = (word_t)(qh + rl);
rh = (word_t)(rh + (qh < rl));
qstar = (word_t)(qstar + rh);
ret = _nn_submul_word_shift(r, b, qstar, shift, &borrow); EG(ret, err);
MUST_HAVE(!(r->val[i - 1] < borrow), ret, err);
r->val[i - 1] = (word_t)(r->val[i - 1] - borrow);
ret = _nn_cmp_shift(r, b, shift, &cmp); EG(ret, err);
_small = ((!!(r->val[i - 1])) | (cmp >= 0));
ret = _nn_cnd_sub_shift(_small, r, b, shift, &borrow); EG(ret, err);
MUST_HAVE(!(r->val[i - 1] != borrow), ret, err);
r->val[i - 1] = (word_t)(r->val[i - 1] - borrow);
qstar = (word_t)(qstar + (word_t)_small);
q->val[shift] = qstar;
MUST_HAVE(!(r->val[r->wlen - 1] != WORD(0)), ret, err);
ret = _nn_cmp_shift(r, b, shift, &cmp); EG(ret, err);
MUST_HAVE(!(cmp >= 0), ret, err);
ret = nn_set_wlen(r, (u8)(r->wlen - 1)); EG(ret, err);
}
err:
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int _nn_divrem_normalized_aliased(nn_t q, nn_src_t a, nn_t b, word_t v)
{
int ret;
nn r;
r.magic = WORD(0);
ret = nn_init(&r, 0); EG(ret, err);
ret = _nn_divrem_normalized(q, &r, a, b, v); EG(ret, err);
ret = nn_copy(b, &r); EG(ret, err);
err:
nn_uninit(&r);
return ret;
}
int nn_divrem_normalized(nn_t q, nn_t r, nn_src_t a, nn_src_t b, word_t v)
{
int ret;
ret = nn_check_initialized(a); EG(ret, err);
ret = nn_check_initialized(q); EG(ret, err);
ret = nn_check_initialized(r); EG(ret, err);
MUST_HAVE((q != r) && (q != a) && (q != b), ret, err);
if (r == b) {
ret = _nn_divrem_normalized_aliased(q, a, r, v);
} else {
ret = nn_check_initialized(b); EG(ret, err);
ret = _nn_divrem_normalized(q, r, a, b, v);
}
err:
return ret;
}
int nn_mod_normalized(nn_t r, nn_src_t a, nn_src_t b, word_t v)
{
int ret;
nn q;
q.magic = WORD(0);
ret = nn_init(&q, 0); EG(ret, err);
ret = nn_divrem_normalized(&q, r, a, b, v);
err:
nn_uninit(&q);
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int _nn_divrem_unshifted(nn_t q, nn_t r, nn_src_t a, nn_src_t b_norm,
word_t v, bitcnt_t cnt)
{
nn a_shift;
u8 new_wlen, b_wlen;
int larger, ret, cmp;
word_t borrow;
a_shift.magic = WORD(0);
MUST_HAVE(((a->wlen + BIT_LEN_WORDS(cnt)) < NN_MAX_WORD_LEN), ret, err);
new_wlen = (u8)(a->wlen + (u8)BIT_LEN_WORDS(cnt));
b_wlen = b_norm->wlen;
if (new_wlen < b_wlen) {
ret = nn_copy(r, a); EG(ret, err);
ret = nn_zero(q);
goto err;
}
ret = nn_init(&a_shift, (u16)(new_wlen * WORD_BYTES)); EG(ret, err);
ret = nn_set_wlen(&a_shift, new_wlen); EG(ret, err);
ret = nn_lshift_fixedlen(&a_shift, a, cnt); EG(ret, err);
ret = nn_set_wlen(r, new_wlen); EG(ret, err);
if (new_wlen == b_wlen) {
ret = nn_cmp(&a_shift, b_norm, &cmp); EG(ret, err);
larger = (cmp >= 0);
ret = nn_cnd_sub(larger, r, &a_shift, b_norm); EG(ret, err);
MUST_HAVE(((!nn_cmp(r, b_norm, &cmp)) && (cmp < 0)), ret, err);
ret = nn_set_wlen(q, (u8)(new_wlen - b_wlen + 1)); EG(ret, err);
q->val[new_wlen - b_wlen] = (word_t) larger;
} else if (new_wlen > b_wlen) {
ret = _nn_cmp_shift(&a_shift, b_norm, (u8)(new_wlen - b_wlen), &cmp); EG(ret, err);
larger = (cmp >= 0);
ret = _nn_cnd_sub_shift(larger, &a_shift, b_norm, (u8)(new_wlen - b_wlen), &borrow); EG(ret, err);
MUST_HAVE(((!_nn_cmp_shift(&a_shift, b_norm, (u8)(new_wlen - b_wlen), &cmp)) && (cmp < 0)), ret, err);
ret = nn_divrem_normalized(q, r, &a_shift, b_norm, v); EG(ret, err);
ret = nn_set_wlen(q, (u8)(new_wlen - b_wlen + 1)); EG(ret, err);
q->val[new_wlen - b_wlen] = (word_t) larger;
}
ret = nn_rshift_fixedlen(r, r, cnt); EG(ret, err);
ret = nn_set_wlen(r, b_wlen);
err:
nn_uninit(&a_shift);
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int _nn_divrem_unshifted_aliased(nn_t q, nn_src_t a, nn_t b_norm,
word_t v, bitcnt_t cnt)
{
int ret;
nn r;
r.magic = WORD(0);
ret = nn_init(&r, 0); EG(ret, err);
ret = _nn_divrem_unshifted(q, &r, a, b_norm, v, cnt); EG(ret, err);
ret = nn_copy(b_norm, &r); EG(ret, err);
err:
nn_uninit(&r);
return ret;
}
int nn_divrem_unshifted(nn_t q, nn_t r, nn_src_t a, nn_src_t b,
word_t v, bitcnt_t cnt)
{
int ret;
ret = nn_check_initialized(a); EG(ret, err);
ret = nn_check_initialized(q); EG(ret, err);
ret = nn_check_initialized(r); EG(ret, err);
MUST_HAVE((q != r) && (q != a) && (q != b), ret, err);
if (r == b) {
ret = _nn_divrem_unshifted_aliased(q, a, r, v, cnt);
} else {
ret = nn_check_initialized(b); EG(ret, err);
ret = _nn_divrem_unshifted(q, r, a, b, v, cnt);
}
err:
return ret;
}
int nn_mod_unshifted(nn_t r, nn_src_t a, nn_src_t b, word_t v, bitcnt_t cnt)
{
nn q;
int ret;
q.magic = WORD(0);
ret = nn_init(&q, 0); EG(ret, err);
ret = nn_divrem_unshifted(&q, r, a, b, v, cnt);
err:
nn_uninit(&q);
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int _wcmp_22(word_t a[2], word_t b[2])
{
int mask, ret = 0;
ret += (a[1] > b[1]);
ret -= (a[1] < b[1]);
mask = !(ret & 0x1);
ret += ((a[0] > b[0]) & mask);
ret -= ((a[0] < b[0]) & mask);
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static word_t _wadd_22(word_t a[2], word_t b[2])
{
word_t carry;
a[0] = (word_t)(a[0] + b[0]);
carry = (word_t)(a[0] < b[0]);
a[1] = (word_t)(a[1] + carry);
carry = (word_t)(a[1] < carry);
a[1] = (word_t)(a[1] + b[1]);
carry = (word_t)(carry | (a[1] < b[1]));
return carry;
}
ATTRIBUTE_WARN_UNUSED_RET static word_t _wsub_22(word_t a[2], word_t b[2])
{
word_t borrow, tmp;
tmp = (word_t)(a[0] - b[0]);
borrow = (word_t)(tmp > a[0]);
a[0] = tmp;
tmp = (word_t)(a[1] - borrow);
borrow = (word_t)(tmp > a[1]);
a[1] = (word_t)(tmp - b[1]);
borrow = (word_t)(borrow | (a[1] > tmp));
return borrow;
}
#define WORD_CND_SUB_21(cnd, ah, al, b) do { \
word_t tmp, mask; \
mask = WORD_MASK_IFNOTZERO((cnd)); \
tmp = (word_t)((al) - ((b) & mask)); \
(ah) = (word_t)((ah) - (tmp > (al))); \
(al) = tmp; \
} while (0)
#define WORD_CND_SUB_22(cnd, ah, al, bh, bl) do { \
word_t tmp, mask; \
mask = WORD_MASK_IFNOTZERO((cnd)); \
tmp = (word_t)((al) - ((bl) & mask)); \
(ah) = (word_t)((ah) - (tmp > (al))); \
(al) = tmp; \
(ah) = (word_t)((ah) - ((bh) & mask)); \
} while (0)
ATTRIBUTE_WARN_UNUSED_RET static int _word_divrem(word_t *q, word_t *r, word_t ah, word_t al, word_t b)
{
word_t bh, bl, qh, ql, rm, rhl[2], phl[2];
int larger, ret;
u8 j;
MUST_HAVE((WRSHIFT((b), (WORD_BITS - 1)) == WORD(1)), ret, err);
bh = WRSHIFT((b), HWORD_BITS);
bl = WLSHIFT((b), HWORD_BITS);
rhl[1] = ah;
rhl[0] = al;
KNOWN_FACT(bh != 0, ret, err);
qh = (rhl[1] / bh);
qh = WORD_MIN(qh, HWORD_MASK);
WORD_MUL(phl[1], phl[0], qh, (b));
phl[1] = (WLSHIFT(phl[1], HWORD_BITS) |
WRSHIFT(phl[0], HWORD_BITS));
phl[0] = WLSHIFT(phl[0], HWORD_BITS);
for (j = 0; j < 2; j++) {
larger = (_wcmp_22(phl, rhl) > 0);
qh = (word_t)(qh - (word_t)larger);
WORD_CND_SUB_22(larger, phl[1], phl[0], bh, bl);
}
ret = (_wcmp_22(phl, rhl) > 0);
MUST_HAVE(!(ret), ret, err);
IGNORE_RET_VAL(_wsub_22(rhl, phl));
MUST_HAVE((WRSHIFT(rhl[1], HWORD_BITS) == 0), ret, err);
rm = (WLSHIFT(rhl[1], HWORD_BITS) |
WRSHIFT(rhl[0], HWORD_BITS));
ql = (rm / bh);
ql = WORD_MIN(ql, HWORD_MASK);
WORD_MUL(phl[1], phl[0], ql, (b));
for (j = 0; j < 2; j++) {
larger = (_wcmp_22(phl, rhl) > 0);
ql = (word_t) (ql - (word_t)larger);
WORD_CND_SUB_21(larger, phl[1], phl[0], (b));
}
ret = _wcmp_22(phl, rhl) > 0;
MUST_HAVE(!(ret), ret, err);
IGNORE_RET_VAL(_wsub_22(rhl, phl));
MUST_HAVE((rhl[1] == WORD(0)), ret, err);
MUST_HAVE(!(rhl[0] >= (b)), ret, err);
(*q) = (WLSHIFT(qh, HWORD_BITS) | ql);
(*r) = rhl[0];
MUST_HAVE(!((word_t) ((*q)*(b) + (*r)) != (al)), ret, err);
ret = 0;
err:
return ret;
}
int wreciprocal(word_t dh, word_t dl, word_t *reciprocal)
{
word_t q, carry, r[2], t[2];
int ret;
MUST_HAVE((reciprocal != NULL), ret, err);
if (((word_t)(dh + WORD(1)) == WORD(0)) &&
((word_t)(dl + WORD(1)) == WORD(0))) {
(*reciprocal) = WORD(0);
ret = 0;
goto err;
}
if ((word_t)(dh + WORD(1)) == WORD(0)) {
q = (word_t)(~dh);
r[1] = (word_t)(~dl);
} else {
t[1] = (word_t)(~dh);
t[0] = (word_t)(~dl);
ret = _word_divrem(&q, r+1, t[1], t[0],
(word_t)(dh + WORD(1))); EG(ret, err);
}
if ((word_t)(dl + WORD(1)) == WORD(0)) {
(*reciprocal) = q;
ret = 0;
goto err;
}
r[0] = WORD(0);
WORD_MUL(t[1], t[0], q, (word_t)~dl);
carry = _wadd_22(r, t);
t[0] = (word_t)(dl + WORD(1));
t[1] = dh;
while (carry || (_wcmp_22(r, t) >= 0)) {
q++;
carry = (word_t)(carry - _wsub_22(r, t));
}
(*reciprocal) = q;
ret = 0;
err:
return ret;
}
int nn_compute_div_coefs(nn_t p_normalized, word_t *p_shift,
word_t *p_reciprocal, nn_src_t p_in)
{
bitcnt_t p_rounded_bitlen, p_bitlen;
nn p, tmp_nn;
int ret;
p.magic = tmp_nn.magic = WORD(0);
ret = nn_check_initialized(p_in); EG(ret, err);
MUST_HAVE((p_shift != NULL), ret, err);
MUST_HAVE((p_reciprocal != NULL), ret, err);
MUST_HAVE((p_normalized != p_in), ret, err);
ret = nn_init(&p, 0); EG(ret, err);
ret = nn_copy(&p, p_in); EG(ret, err);
if (p.wlen < 2) {
ret = nn_set_wlen(&p, 2); EG(ret, err);
}
ret = nn_init(p_normalized, 0); EG(ret, err);
ret = nn_init(&tmp_nn, 0); EG(ret, err);
p_rounded_bitlen = (bitcnt_t)(WORD_BITS * p.wlen);
ret = nn_bitlen(&p, &p_bitlen); EG(ret, err);
(*p_shift) = (word_t)(p_rounded_bitlen - p_bitlen);
ret = nn_lshift(p_normalized, &p, (bitcnt_t)(*p_shift)); EG(ret, err);
MUST_HAVE((p_rounded_bitlen >= (2 * WORDSIZE)), ret, err);
ret = nn_rshift(&tmp_nn, p_normalized, (bitcnt_t)(p_rounded_bitlen - (2 * WORDSIZE))); EG(ret, err);
ret = wreciprocal(tmp_nn.val[1], tmp_nn.val[0], p_reciprocal);
err:
nn_uninit(&p);
nn_uninit(&tmp_nn);
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int _nn_divrem(nn_t q, nn_t r, nn_src_t a, nn_src_t b)
{
nn b_large, b_normalized;
bitcnt_t cnt;
word_t v;
nn_src_t ptr = b;
int ret, iszero;
b_large.magic = b_normalized.magic = WORD(0);
ret = nn_init(r, 0); EG(ret, err);
ret = nn_init(q, 0); EG(ret, err);
ret = nn_init(&b_large, 0); EG(ret, err);
MUST_HAVE(!nn_iszero(b, &iszero) && !iszero, ret, err);
if (b->wlen == 1) {
ret = nn_copy(&b_large, b); EG(ret, err);
ret = nn_set_wlen(&b_large, 2); EG(ret, err);
ptr = (nn_src_t) &b_large;
}
MUST_HAVE(!(ptr->wlen < 2), ret, err);
ret = nn_init(&b_normalized, (u16)((ptr->wlen) * WORD_BYTES)); EG(ret, err);
ret = nn_clz(ptr, &cnt); EG(ret, err);
ret = nn_lshift_fixedlen(&b_normalized, ptr, cnt); EG(ret, err);
ret = wreciprocal(b_normalized.val[ptr->wlen - 1],
b_normalized.val[ptr->wlen - 2],
&v); EG(ret, err);
ret = _nn_divrem_unshifted(q, r, a, &b_normalized, v, cnt);
err:
nn_uninit(&b_normalized);
nn_uninit(&b_large);
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int __nn_divrem_notrim_alias(nn_t q, nn_t r, nn_src_t a, nn_src_t b)
{
nn a_cpy, b_cpy;
int ret;
a_cpy.magic = b_cpy.magic = WORD(0);
ret = nn_init(&a_cpy, 0); EG(ret, err);
ret = nn_init(&b_cpy, 0); EG(ret, err);
ret = nn_copy(&a_cpy, a); EG(ret, err);
ret = nn_copy(&b_cpy, b); EG(ret, err);
ret = _nn_divrem(q, r, &a_cpy, &b_cpy);
err:
nn_uninit(&b_cpy);
nn_uninit(&a_cpy);
return ret;
}
int nn_divrem_notrim(nn_t q, nn_t r, nn_src_t a, nn_src_t b)
{
int ret;
ret = nn_check_initialized(a); EG(ret, err);
ret = nn_check_initialized(b); EG(ret, err);
MUST_HAVE(((q != NULL) && (r != NULL)), ret, err);
if ((a == q) || (a == r) || (b == q) || (b == r)) {
ret = __nn_divrem_notrim_alias(q, r, a, b);
} else {
ret = _nn_divrem(q, r, a, b);
}
err:
return ret;
}
int nn_divrem(nn_t q, nn_t r, nn_src_t a, nn_src_t b)
{
int ret;
ret = nn_divrem_notrim(q, r, a, b); EG(ret, err);
ret = nn_normalize(q); EG(ret, err);
ret = nn_normalize(r);
err:
return ret;
}
int nn_mod_notrim(nn_t r, nn_src_t a, nn_src_t b)
{
int ret;
nn q;
q.magic = WORD(0);
ret = nn_divrem_notrim(&q, r, a, b);
nn_uninit(&q);
return ret;
}
int nn_mod(nn_t r, nn_src_t a, nn_src_t b)
{
int ret;
nn q;
q.magic = WORD(0);
ret = nn_divrem(&q, r, a, b);
nn_uninit(&q);
return ret;
}
ATTRIBUTE_WARN_UNUSED_RET static int _nn_xgcd(nn_t g, nn_t u, nn_t v, nn_src_t a, nn_src_t b,
int *sign)
{
nn_t c, d, q, r, u1, v1, u2, v2;
nn scratch[8];
int ret, swap, iszero;
u8 i;
for (i = 0; i < 8; i++){
scratch[i].magic = WORD(0);
}
ret = nn_init(g, 0); EG(ret, err);
ret = nn_init(u, 0); EG(ret, err);
ret = nn_init(v, 0); EG(ret, err);
ret = nn_iszero(b, &iszero); EG(ret, err);
if (iszero) {
ret = nn_copy(g, a); EG(ret, err);
ret = nn_one(u); EG(ret, err);
ret = nn_zero(v); EG(ret, err);
(*sign) = 1;
goto err;
}
for (i = 0; i < 8; i++){
ret = nn_init(&scratch[i], 0); EG(ret, err);
}
u1 = &(scratch[0]);
v1 = &(scratch[1]);
u2 = &(scratch[2]);
v2 = &(scratch[3]);
ret = nn_one(u1); EG(ret, err);
ret = nn_zero(v1); EG(ret, err);
ret = nn_zero(u2); EG(ret, err);
ret = nn_one(v2); EG(ret, err);
c = &(scratch[4]);
d = &(scratch[5]);
ret = nn_copy(c, a); EG(ret, err);
ret = nn_copy(d, b); EG(ret, err);
q = &(scratch[6]);
r = &(scratch[7]);
swap = 0;
ret = nn_iszero(d, &iszero); EG(ret, err);
while (!iszero) {
ret = nn_divrem(q, r, c, d); EG(ret, err);
ret = nn_normalize(q); EG(ret, err);
ret = nn_normalize(r); EG(ret, err);
ret = nn_copy(c, r); EG(ret, err);
ret = nn_mul(r, q, u1); EG(ret, err);
ret = nn_normalize(r); EG(ret, err);
ret = nn_add(v1, v1, r); EG(ret, err);
ret = nn_mul(r, q, u2); EG(ret, err);
ret = nn_normalize(r); EG(ret, err);
ret = nn_add(v2, v2, r); EG(ret, err);
ret = nn_normalize(v1); EG(ret, err);
ret = nn_normalize(v2); EG(ret, err);
swap = 1;
ret = nn_iszero(c, &iszero); EG(ret, err);
if (iszero) {
break;
}
ret = nn_divrem(q, r, d, c); EG(ret, err);
ret = nn_normalize(q); EG(ret, err);
ret = nn_normalize(r); EG(ret, err);
ret = nn_copy(d, r); EG(ret, err);
ret = nn_mul(r, q, v1); EG(ret, err);
ret = nn_normalize(r); EG(ret, err);
ret = nn_add(u1, u1, r); EG(ret, err);
ret = nn_mul(r, q, v2); EG(ret, err);
ret = nn_normalize(r); EG(ret, err);
ret = nn_add(u2, u2, r); EG(ret, err);
ret = nn_normalize(u1); EG(ret, err);
ret = nn_normalize(u2); EG(ret, err);
swap = 0;
ret = nn_iszero(d, &iszero); EG(ret, err);
}
if (swap) {
ret = nn_copy(g, d); EG(ret, err);
ret = nn_copy(u, u2); EG(ret, err);
ret = nn_copy(v, u1); EG(ret, err);
} else {
ret = nn_copy(g, c); EG(ret, err);
ret = nn_copy(u, v2); EG(ret, err);
ret = nn_copy(v, v1); EG(ret, err);
}
(*sign) = swap ? -1 : 1;
ret = 0;
err:
for (i = 0; i < 8; i++){
nn_uninit(&scratch[i]);
}
if (ret){
nn_uninit(v);
nn_uninit(u);
nn_uninit(g);
}
return ret;
}
int nn_xgcd(nn_t g, nn_t u, nn_t v, nn_src_t a, nn_src_t b, int *sign)
{
nn a_cpy, b_cpy;
nn_src_t a_, b_;
int ret, cmp, _sign;
a_cpy.magic = b_cpy.magic = WORD(0);
ret = nn_check_initialized(a); EG(ret, err);
ret = nn_check_initialized(b); EG(ret, err);
MUST_HAVE((sign != NULL), ret, err);
ret = nn_init(&b_cpy, 0); EG(ret, err);
if ((g == a) || (u == a) || (v == a)){
ret = nn_copy(&a_cpy, a); EG(ret, err);
a_ = &a_cpy;
} else {
a_ = a;
}
if ((g == b) || (u == b) || (v == b)) {
ret = nn_copy(&b_cpy, b); EG(ret, err);
b_ = &b_cpy;
} else {
b_ = b;
}
ret = nn_cmp(a_, b_, &cmp); EG(ret, err);
if (cmp < 0) {
ret = _nn_xgcd(g, v, u, b_, a_, &_sign); EG(ret, err);
(*sign) = -(_sign);
} else {
ret = _nn_xgcd(g, u, v, a_, b_, &_sign); EG(ret, err);
(*sign) = _sign;
}
err:
nn_uninit(&b_cpy);
nn_uninit(&a_cpy);
return ret;
}
int nn_gcd(nn_t g, nn_src_t a, nn_src_t b, int *sign)
{
nn u, v;
int ret;
u.magic = v.magic = WORD(0);
ret = nn_xgcd(g, &u, &v, a, b, sign);
nn_uninit(&u);
nn_uninit(&v);
return ret;
}