eigenmath/mmul.cpp

527 lines
7.9 KiB
C++

// Bignum multiplication and division
#include "stdafx.h"
#include "defs.h"
extern int ge(unsigned int *, unsigned int *, int);
static void mulf(unsigned int *, unsigned int *, int, unsigned int);
static void addf(unsigned int *, unsigned int *, int);
static void subf(unsigned int *, unsigned int *, int);
unsigned int *
mmul(unsigned int *a, unsigned int *b)
{
int alen, blen, i, n;
unsigned int *t, *x;
if (MZERO(a) || MZERO(b))
return mint(0);
if (MLENGTH(a) == 1 && a[0] == 1) {
t = mcopy(b);
MSIGN(t) *= MSIGN(a);
return t;
}
if (MLENGTH(b) == 1 && b[0] == 1) {
t = mcopy(a);
MSIGN(t) *= MSIGN(b);
return t;
}
alen = MLENGTH(a);
blen = MLENGTH(b);
n = alen + blen;
x = mnew(n);
t = mnew(alen + 1);
for (i = 0; i < n; i++)
x[i] = 0;
/* sum of partial products */
for (i = 0; i < blen; i++) {
mulf(t, a, alen, b[i]);
addf(x + i, t, alen + 1);
}
mfree(t);
/* length of product */
for (i = n - 1; i > 0; i--)
if (x[i])
break;
MLENGTH(x) = i + 1;
MSIGN(x) = MSIGN(a) * MSIGN(b);
return x;
}
unsigned int *
mdiv(unsigned int *a, unsigned int *b)
{
int alen, blen, i, n;
unsigned int c, *t, *x, *y;
unsigned long long jj, kk;
if (MZERO(b))
stop("divide by zero");
if (MZERO(a))
return mint(0);
alen = MLENGTH(a);
blen = MLENGTH(b);
n = alen - blen;
if (n < 0)
return mint(0);
x = mnew(alen + 1);
for (i = 0; i < alen; i++)
x[i] = a[i];
x[i] = 0;
y = mnew(n + 1);
t = mnew(blen + 1);
/* Add 1 here to round up in case the remaining words are non-zero. */
kk = (unsigned long long) b[blen - 1] + 1;
for (i = 0; i <= n; i++) {
y[n - i] = 0;
for (;;) {
/* estimate the partial quotient */
if (little_endian) {
((unsigned int *) &jj)[0] = x[alen - i - 1];
((unsigned int *) &jj)[1] = x[alen - i - 0];
} else {
((unsigned int *) &jj)[1] = x[alen - i - 1];
((unsigned int *) &jj)[0] = x[alen - i - 0];
}
c = (unsigned int) (jj / kk);
if (c == 0) {
if (ge(x + n - i, b, blen)) { /* see note 1 */
y[n - i]++;
subf(x + n - i, b, blen);
}
break;
}
y[n - i] += c;
mulf(t, b, blen, c);
subf(x + n - i, t, blen + 1);
}
}
mfree(t);
mfree(x);
/* length of quotient */
for (i = n; i > 0; i--)
if (y[i])
break;
if (i == 0 && y[0] == 0) {
mfree(y);
y = mint(0);
} else {
MLENGTH(y) = i + 1;
MSIGN(y) = MSIGN(a) * MSIGN(b);
}
return y;
}
// a = a + b
static void
addf(unsigned int *a, unsigned int *b, int len)
{
int i;
long long t = 0; /* can be signed or unsigned */
for (i = 0; i < len; i++) {
t += (long long) a[i] + b[i];
a[i] = (unsigned int) t;
t >>= 32;
}
}
// a = a - b
static void
subf(unsigned int *a, unsigned int *b, int len)
{
int i;
long long t = 0; /* must be signed */
for (i = 0; i < len; i++) {
t += (long long) a[i] - b[i];
a[i] = (unsigned int) t;
t >>= 32;
}
}
// a = b * c
// 0xffffffff + 0xffffffff * 0xffffffff == 0xffffffff00000000
static void
mulf(unsigned int *a, unsigned int *b, int len, unsigned int c)
{
int i;
unsigned long long t = 0; /* must be unsigned */
for (i = 0; i < len; i++) {
t += (unsigned long long) b[i] * c;
a[i] = (unsigned int) t;
t >>= 32;
}
a[i] = (unsigned int) t;
}
unsigned int *
mmod(unsigned int *a, unsigned int *b)
{
int alen, blen, i, n;
unsigned int c, *t, *x, *y;
unsigned long long jj, kk;
if (MZERO(b))
stop("divide by zero");
if (MZERO(a))
return mint(0);
alen = MLENGTH(a);
blen = MLENGTH(b);
n = alen - blen;
if (n < 0)
return mcopy(a);
x = mnew(alen + 1);
for (i = 0; i < alen; i++)
x[i] = a[i];
x[i] = 0;
y = mnew(n + 1);
t = mnew(blen + 1);
kk = (unsigned long long) b[blen - 1] + 1;
for (i = 0; i <= n; i++) {
y[n - i] = 0;
for (;;) {
/* estimate the partial quotient */
if (little_endian) {
((unsigned int *) &jj)[0] = x[alen - i - 1];
((unsigned int *) &jj)[1] = x[alen - i - 0];
} else {
((unsigned int *) &jj)[1] = x[alen - i - 1];
((unsigned int *) &jj)[0] = x[alen - i - 0];
}
c = (int) (jj / kk);
if (c == 0) {
if (ge(x + n - i, b, blen)) { /* see note 1 */
y[n - i]++;
subf(x + n - i, b, blen);
}
break;
}
y[n - i] += c;
mulf(t, b, blen, c);
subf(x + n - i, t, blen + 1);
}
}
mfree(t);
mfree(y);
/* length of remainder */
for (i = blen - 1; i > 0; i--)
if (x[i])
break;
if (i == 0 && x[0] == 0) {
mfree(x);
x = mint(0);
} else {
MLENGTH(x) = i + 1;
MSIGN(x) = MSIGN(a);
}
return x;
}
// return both quotient and remainder of a/b
void
mdivrem(unsigned int **q, unsigned int **r, unsigned int *a, unsigned int *b)
{
int alen, blen, i, n;
unsigned int c, *t, *x, *y;
unsigned long long jj, kk;
if (MZERO(b))
stop("divide by zero");
if (MZERO(a)) {
*q = mint(0);
*r = mint(0);
return;
}
alen = MLENGTH(a);
blen = MLENGTH(b);
n = alen - blen;
if (n < 0) {
*q = mint(0);
*r = mcopy(a);
return;
}
x = mnew(alen + 1);
for (i = 0; i < alen; i++)
x[i] = a[i];
x[i] = 0;
y = mnew(n + 1);
t = mnew(blen + 1);
kk = (unsigned long long) b[blen - 1] + 1;
for (i = 0; i <= n; i++) {
y[n - i] = 0;
for (;;) {
/* estimate the partial quotient */
if (little_endian) {
((unsigned int *) &jj)[0] = x[alen - i - 1];
((unsigned int *) &jj)[1] = x[alen - i - 0];
} else {
((unsigned int *) &jj)[1] = x[alen - i - 1];
((unsigned int *) &jj)[0] = x[alen - i - 0];
}
c = (int) (jj / kk);
if (c == 0) {
if (ge(x + n - i, b, blen)) { /* see note 1 */
y[n - i]++;
subf(x + n - i, b, blen);
}
break;
}
y[n - i] += c;
mulf(t, b, blen, c);
subf(x + n - i, t, blen + 1);
}
}
mfree(t);
/* length of quotient */
for (i = n; i > 0; i--)
if (y[i])
break;
if (i == 0 && y[0] == 0) {
mfree(y);
y = mint(0);
} else {
MLENGTH(y) = i + 1;
MSIGN(y) = MSIGN(a) * MSIGN(b);
}
/* length of remainder */
for (i = blen - 1; i > 0; i--)
if (x[i])
break;
if (i == 0 && x[0] == 0) {
mfree(x);
x = mint(0);
} else {
MLENGTH(x) = i + 1;
MSIGN(x) = MSIGN(a);
}
*q = y;
*r = x;
}
#if SELFTEST
// small integer tests
static void test_mmulf(int, int, int);
static void test_mdivf(int, int, int);
static void test_mmodf(int, int, int);
void
test_mmul(void)
{
int i, j, m;
logout("test mmul\n");
m = mtotal;
for (i = -100; i <= 100; i++)
for (j = -100; j <= 100; j++)
test_mmulf(i, j, i * j);
if (m != mtotal) {
logout("memory leak\n");
errout();
}
logout("ok\n");
}
static void
test_mmulf(int na, int nb, int nc)
{
unsigned int *a, *b, *c, *d;
a = mint(na);
b = mint(nb);
c = mint(nc);
d = mmul(a, b);
if (mcmp(c, d) == 0) {
mfree(a);
mfree(b);
mfree(c);
mfree(d);
return;
}
sprintf(logbuf, "%d %d %d %d\n", na, nb, nc, *d * MSIGN(d));
logout(logbuf);
errout();
}
void
test_mdiv(void)
{
int i, j, m;
logout("test mdiv\n");
m = mtotal;
for (i = -100; i <= 100; i++)
for (j = -100; j <= 100; j++)
if (j)
test_mdivf(i, j, i / j);
if (m != mtotal) {
logout("memory leak\n");
errout();
}
logout("ok\n");
}
static void
test_mdivf(int na, int nb, int nc)
{
unsigned int *a, *b, *c, *d;
a = mint(na);
b = mint(nb);
c = mint(nc);
d = mdiv(a, b);
if (mcmp(c, d) == 0) {
mfree(a);
mfree(b);
mfree(c);
mfree(d);
return;
}
sprintf(logbuf, "%d %d %d %d\n", na, nb, nc, *d * MSIGN(d));
logout(logbuf);
errout();
}
void
test_mmod(void)
{
int i, j, m;
logout("test mmod\n");
m = mtotal;
for (i = -100; i <= 100; i++)
for (j = -100; j <= 100; j++)
if (j)
test_mmodf(i, j, i % j);
if (m != mtotal) {
logout("memory leak\n");
errout();
}
logout("ok\n");
}
static void
test_mmodf(int na, int nb, int nc)
{
unsigned int *a, *b, *c, *d;
a = mint(na);
b = mint(nb);
c = mint(nc);
d = mmod(a, b);
if (mcmp(c, d) == 0) {
mfree(a);
mfree(b);
mfree(c);
mfree(d);
return;
}
sprintf(logbuf, "%d %d %d %d\n", na, nb, nc, *d * MSIGN(d));
logout(logbuf);
errout();
}
#endif