423 lines
5.5 KiB
C++
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");
|
|
}
|