Eigenmath/pollard.cpp

239 lines
2.9 KiB
C++

// Factor using the Pollard rho method
#include "stdafx.h"
#include "defs.h"
static unsigned int *n;
void
factor_number(void)
{
int h;
save();
p1 = pop();
// 0 or 1?
if (equaln(p1, 0) || equaln(p1, 1) || equaln(p1, -1)) {
push(p1);
restore();
return;
}
n = mcopy(p1->u.q.a);
h = tos;
factor_a();
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)
void
factor_a(void)
{
int k;
if (MSIGN(n) == -1) {
MSIGN(n) = 1;
push_integer(-1);
}
for (k = 0; k < 10000; k++) {
try_kth_prime(k);
// if n is 1 then we're done
if (MLENGTH(n) == 1 && n[0] == 1) {
mfree(n);
return;
}
}
factor_b();
}
void
try_kth_prime(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, hence 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);
}
// From TAOCP Vol. 2 by Knuth, p. 385 (Algorithm B)
int
factor_b(void)
{
int k, l;
unsigned int *g, *one, *t, *x, *xprime;
one = mint(1);
x = mint(5);
xprime = mint(2);
k = 1;
l = 1;
while (1) {
if (mprime(n)) {
push_factor(n, 1);
mfree(one);
mfree(x);
mfree(xprime);
return 0;
}
while (1) {
if (esc_flag) {
mfree(one);
mfree(n);
mfree(x);
mfree(xprime);
stop("esc");
}
// g = gcd(x' - x, n)
t = msub(xprime, x);
MSIGN(t) = 1;
g = mgcd(t, n);
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;
}
push_factor(g, 1);
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;
break;
}
}
}
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);
}
}