eigenmath/pollard.cpp

423 lines
5.5 KiB
C++

// Factor using the Pollard rho method
#include "stdafx.h"
#include "defs.h"
extern int esc_flag;
static void factor_a(unsigned int *);
static void try_kth_prime(unsigned int **, int);
static void push_factor(unsigned int *, int);
static int factor_b(unsigned int *);
void
factor_number(void)
{
int h;
unsigned int *n;
save();
p1 = pop();
n = p1->u.q.a;
if (MLENGTH(n) == 1 && (n[0] == 0 || n[0] == 1)) {
push(p1);
restore();
return;
}
h = tos;
factor_a(n);
if (tos - h > 1) {
list(tos - h);
push_symbol(MULTIPLY);
swap();
cons();
}
restore();
}
// factor using table look-up, then switch to rho method if necessary
// From TAOCP Vol. 2 by Knuth, p. 380 (Algorithm A)
static void
factor_a(unsigned int *n)
{
int k;
n = mcopy(n);
if (MSIGN(n) == -1) {
MSIGN(n) = 1;
push_integer(-1);
}
for (k = 0; k < 10000; k++) {
try_kth_prime(&n, k);
// if n is 1 then we're done
if (MLENGTH(n) == 1 && n[0] == 1) {
mfree(n);
return;
}
}
factor_b(n);
}
static void
try_kth_prime(unsigned int **n, int k)
{
int count;
unsigned int *d, *q, *r;
d = mint(primetab[k]);
count = 0;
while (1) {
// if n is 1 then we're done
if (MLENGTH(*n) == 1 && (*n)[0] == 1) {
if (count)
push_factor(d, count);
else
mfree(d);
return;
}
mdivrem(&q, &r, *n, d);
// continue looping while remainder is zero
if (MLENGTH(r) == 1 && r[0] == 0) {
count++;
mfree(r);
mfree(*n);
*n = q;
} else {
mfree(r);
break;
}
}
if (count)
push_factor(d, count);
// q = n / d
// if q < d then n < d^2 so n is prime
if (mcmp(q, d) == -1) {
push_factor(*n, 1);
*n = mint(1);
}
if (count == 0)
mfree(d);
mfree(q);
}
static void
push_factor(unsigned int *d, int count)
{
p1 = alloc();
p1->k = NUM;
p1->u.q.a = d;
p1->u.q.b = mint(1);
push(p1);
if (count > 1) {
push_symbol(POWER);
swap();
p1 = alloc();
p1->k = NUM;
p1->u.q.a = mint(count);
p1->u.q.b = mint(1);
push(p1);
list(3);
}
}
// From TAOCP Vol. 2 by Knuth, p. 385 (Algorithm B)
static int
factor_b(unsigned int *n)
{
int k, l;
unsigned int *g, *one, *t, *x, *xprime;
unsigned int count;
count = 0;
one = mint(1);
x = mint(5);
xprime = mint(2);
k = 1;
l = 1;
while (1) {
if (mprime(n)) {
p1 = alloc();
p1->k = NUM;
p1->u.q.a = n;
p1->u.q.b = mint(1);
push(p1);
mfree(one);
mfree(x);
mfree(xprime);
return 0;
}
while (1) {
if (esc_flag) {
mfree(one);
mfree(x);
mfree(xprime);
stop("esc");
}
// g = gcd(x' - x, n)
t = msub(xprime, x);
MSIGN(t) = 1;
g = mgcd(t, n);
//printf("x=%d x'=%d t=%d n=%d g=%d\n", x[0], xprime[0], t[0], n[0], g[0]);
mfree(t);
if (MEQUAL(g, 1)) {
mfree(g);
if (--k == 0) {
mfree(xprime);
xprime = mcopy(x);
l *= 2;
k = l;
}
// x = (x ^ 2 + 1) mod n
t = mmul(x, x);
mfree(x);
x = madd(t, one);
mfree(t);
t = mmod(x, n);
mfree(x);
x = t;
continue;
}
p1 = alloc();
p1->k = NUM;
p1->u.q.a = g;
p1->u.q.b = mint(1);
push(p1);
if (mcmp(g, n) == 0) {
mfree(one);
mfree(n);
mfree(x);
mfree(xprime);
return -1;
}
// n = n / g
t = mdiv(n, g);
mfree(n);
n = t;
// x = x mod n
t = mmod(x, n);
mfree(x);
x = t;
// xprime = xprime mod n
t = mmod(xprime, n);
mfree(xprime);
xprime = t;
//printf("n=%d x=%d x'=%d\n", n[0], x[0], xprime[0]);
break;
}
}
}
#if 0
// old method
// starts to get very slow around factor(26!)
// n is freed
static void
factor_integer_f(unsigned int *n)
{
unsigned int *x, *y;
if (mprime(n)) {
p1 = alloc();
p1->k = NUM;
p1->u.q.a = n;
p1->u.q.b = mint(1);
push(p1);
return;
}
x = mp_factor(n);
if (MEQUAL(x, 1)) {
mfree(x);
p1 = alloc();
p1->k = NUM;
p1->u.q.a = n;
p1->u.q.b = mint(1);
push(p1);
return;
}
y = mdiv(n, x);
mfree(n);
factor_integer_f(x);
factor_integer_f(y);
}
#endif
#if 0
void
test_factor_timing(void)
{
int i;
unsigned int t;
struct tms tms;
p1 = alloc();
p1->k = NUM;
p1->u.q.a = mint(1);
p1->u.q.b = mint(1);
t = times(&tms);
for (i = 0; i < 10000; i++) {
tos = 0;
p1->u.q.a[0] = random();
push(p1);
factor_number();
}
t = times(&tms) - t;
printf("%d.%02d seconds\n", t / 100, t % 100);
for (i = 0; i < 10000; i++) {
tos = 0;
p1->u.q.a[0] = random();
push(p1);
factor_number();
if (tos > 1) {
list(tos);
push_symbol(MULTIPLY);
swap();
cons();
}
eval();
p2 = pop();
if (mcmp(p1->u.q.a, p2->u.q.a) != 0) {
printf("failed %u %u\n", p1->u.q.a[0], p2->u.q.a[0]);
Exit(1);
}
}
Exit(1);
}
#endif
void
test_factor_integer_f(int len, int count)
{
int i, j;
unsigned int *x;
x = mnew(len);
MLENGTH(x) = len;
MSIGN(x) = 1;
p1 = alloc();
p1->k = NUM;
p1->u.q.a = x;
p1->u.q.b = mint(1);
for (i = 0; i < count; i++) {
for (j = 0; j < len; j++)
x[j] = rand();
tos = 0;
push(p1);
factor_number();
eval();
p2 = pop();
if (mcmp(p1->u.q.a, p2->u.q.a) != 0) {
sprintf(logbuf, "failed %u %u\n", p1->u.q.a[0], p2->u.q.a[0]);
logout(logbuf);
errout();
}
}
}
void
test_factor_integer(void)
{
int n;
logout("testing factor_integer\n");
gc();
n = mtotal;
test_factor_integer_f(1, 1000);
test_factor_integer_f(2, 100);
test_factor_integer_f(3, 10);
// check for memory leak
p1 = nil;
p2 = nil;
gc();
if (mtotal != n) {
sprintf(logbuf, "memory leak %d %d\n", n, mtotal);
logout(logbuf);
errout();
}
logout("ok\n");
}