Revision 24d2dc18e53205337a4bb9bc2dd1f9faa17798e3 authored by Lionel Muller on 07 October 2011, 09:58:47 UTC, committed by Lionel Muller on 07 October 2011, 09:58:47 UTC
git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/cado-nfs/trunk@957 3eaf19af-ecc0-40f6-b43f-672aa0c71c71
1 parent 171f670
modul_poly.c
/* arithmetic on polynomials over Z/pZ, with coefficients represented by
the same type as in mod_ul.[ch] ; most presumably an unsigned long */
#include "cado.h"
#include <stdio.h>
#include <stdlib.h>
#include "mod_ul.h"
#include "modul_poly.h"
#include "gmp_aux.h"
/* allocate an array of d coefficients, and initialize it */
static residueul_t*
alloc_long_array (int d)
{
residueul_t *f;
f = (residueul_t*) malloc (d * sizeof (residueul_t));
if (f == NULL)
{
fprintf (stderr, "Error, not enough memory\n");
exit (1);
}
return f;
}
/* reallocate an array to d coefficients */
static residueul_t*
realloc_long_array (residueul_t *f, int d)
{
f = (residueul_t*) realloc (f, d * sizeof (residueul_t));
if (f == NULL)
{
fprintf (stderr, "Error, not enough memory\n");
exit (1);
}
return f;
}
/* initialize a polynomial with maximal degree d */
void
modul_poly_init (modul_poly_t f, int d)
{
ASSERT (d >= 0);
f->degree = -1; /* initialize to 0 */
f->coeff = alloc_long_array (d + 1);
f->alloc = d + 1;
}
/* clear a polynomial */
void
modul_poly_clear (modul_poly_t f)
{
free (f->coeff);
f->alloc = 0;
}
/* realloc f to (at least) n coefficients */
void
modul_poly_realloc (modul_poly_t f, int n)
{
if (f->alloc < n)
{
f->coeff = realloc_long_array (f->coeff, n);
f->alloc = n;
}
}
/* f <- g */
void
modul_poly_set (modul_poly_t f, const modul_poly_t g, modulusul_t p)
{
int i, d = g->degree;
modul_poly_realloc (f, d + 1);
f->degree = d;
for (i = 0; i <= d; i++)
modul_set(f->coeff[i], g->coeff[i], p);
}
/* f <- f/lc(f) mod p */
void
modul_poly_make_monic (modul_poly_t f, modulusul_t p)
{
int d = f->degree, i;
residueul_t ilc;
if (modul_is1(f->coeff[d],p))
return;
modul_inv(ilc, f->coeff[d], p);
for (i = 0; i < d; i++)
modul_mul(f->coeff[i], f->coeff[i], ilc, p);
modul_set1(f->coeff[d], p);
}
/* fp <- f/lc(f) mod p. Return degree of fp (-1 if fp=0). */
int
modul_poly_set_mod (modul_poly_t fp, mpz_t *f, int d, modulusul_t p)
{
d = modul_poly_set_mod_raw (fp, f, d, p);
modul_poly_make_monic (fp, p);
return d;
}
/* fp <- f mod p. Return degree of fp (-1 if fp=0). */
int
modul_poly_set_mod_raw (modul_poly_t fp, mpz_t *f, int d, modulusul_t p)
{
int i;
while (d >= 0 && mpz_divisible_ui_p (f[d], modul_getmod_ul (p)))
d --;
ASSERT (d >= 0); /* f is 0 mod p: should not happen in the CADO-NFS context
since otherwise p would divide N, indeed f(m)=N */
modul_poly_realloc (fp, d + 1);
fp->degree = d;
for (i = 0; i <= d; i++)
modul_set_ul (fp->coeff[i], mpz_fdiv_ui (f[i], modul_getmod_ul (p)), p);
return d;
}
/* f <- a*x+b, a <> 0 */
void
modul_poly_set_linear (modul_poly_t f, residueul_t a, residueul_t b, modulusul_t p)
{
modul_poly_realloc (f, 2);
f->degree = 1;
modul_set(f->coeff[1], a, p);
modul_set(f->coeff[0], b, p);
}
/* swap f and g */
void
modul_poly_swap (modul_poly_t f, modul_poly_t g)
{
int i;
residueul_t *t;
i = f->alloc;
f->alloc = g->alloc;
g->alloc = i;
i = f->degree;
f->degree = g->degree;
g->degree = i;
t = f->coeff;
f->coeff = g->coeff;
g->coeff = t;
}
/* h <- f*g mod p */
void
modul_poly_mul(modul_poly_t h, const modul_poly_t f, const modul_poly_t g, modulusul_t p) {
int df = f->degree;
int dg = g->degree;
int i, j;
modul_poly_t res;
residueul_t aux;
modul_poly_init(res, df+dg);
for (i = 0; i <= df+dg; ++i)
modul_set0(res->coeff[i], p);
for (i = 0; i <= df; ++i)
for (j = 0; j <= dg; ++j) {
modul_mul(aux, f->coeff[i], g->coeff[j], p);
modul_add(res->coeff[i+j], res->coeff[i+j], aux, p);
}
res->degree=df+dg;
modul_poly_set(h, res, p);
modul_poly_clear(res);
}
/* h <- g^2 mod p, g and h must differ */
void
modul_poly_sqr (modul_poly_t h, const modul_poly_t g, modulusul_t p)
{
int i, j, dg = g->degree;
residueul_t *gc, *hc;
residueul_t aux;
ASSERT (dg >= -1);
if (dg == -1) /* g is zero */
{
h->degree = -1;
return;
}
modul_poly_realloc (h, 2 * dg + 1);
gc = g->coeff;
hc = h->coeff;
for (i = 0; i <= dg; i++)
for (j = i + 1; j <= dg; j++)
if (i == 0 || j == dg)
modul_mul(hc[i + j], gc[i], gc[j], p);
else {
modul_mul(aux, gc[i], gc[j], p);
modul_add(hc[i + j], hc[i + j], aux, p);
}
for (i = 1; i < 2 * dg; i++)
modul_add(hc[i], hc[i], hc[i], p);
modul_mul(hc[0], gc[0], gc[0], p);
modul_mul(hc[2*dg], gc[dg], gc[dg], p);
for (i = 1; i < dg; i++) {
modul_mul(aux, gc[i], gc[i], p);
modul_add(hc[2*i], hc[2*i], aux, p);
}
h->degree = 2 * dg;
}
/* normalize h so that h->coeff[deg(h)] <> 0 */
static void
modul_poly_normalize (modul_poly_t h, modulusul_t p)
{
int dh = h->degree;
while (dh >= 0 && modul_is0(h->coeff[dh],p))
dh --;
h->degree = dh;
}
/* h <- (x+a)*h mod p */
void
modul_poly_mul_x (modul_poly_t h, residueul_t a, modulusul_t p)
{
int i, d = h->degree;
residueul_t *hc;
residueul_t aux;
modul_poly_realloc (h, d + 2); /* (x+a)*h has degree d+1, thus d+2 coeffs */
hc = h->coeff;
modul_set(hc[d + 1], hc[d], p);
for (i = d - 1; i >= 0; i--) {
modul_mul(aux, a, hc[i+1], p);
modul_add(hc[i+1], aux, hc[i], p);
}
modul_mul(hc[0], hc[0], a, p);
h->degree = d + 1;
}
/* h <- g - x mod p */
void
modul_poly_sub_x (modul_poly_t h, const modul_poly_t g, modulusul_t p)
{
int i, d = g->degree;
/* if g has degree d >= 2, then g-x has degree d too;
otherwise g-x has degree <= 1 */
modul_poly_realloc (h, ((d < 2) ? 1 : d) + 1);
for (i = 0; i <= d; i++)
modul_set(h->coeff[i], g->coeff[i], p);
for (i = d + 1; i <= 1; i++)
modul_set0(h->coeff[i], p);
modul_sub_ul(h->coeff[1], h->coeff[1], 1, p);
h->degree = (d < 1) ? 1 : d;
modul_poly_normalize (h, p);
}
/* h <- g - 1 mod p */
void
modul_poly_sub_1 (modul_poly_t h, const modul_poly_t g, modulusul_t p)
{
int i, d = g->degree;
/* g-1 has degree d if d >= 1, and degree 0 otherwise */
modul_poly_realloc (h, ((d < 1) ? 0 : d) + 1);
for (i = 0; i <= d; i++)
modul_set(h->coeff[i], g->coeff[i], p);
for (i = d + 1; i <= 0; i++)
modul_set0(h->coeff[i], p);
modul_sub_ul(h->coeff[0], h->coeff[0], 1, p);
h->degree = (d < 0) ? 0 : d;
modul_poly_normalize (h, p);
}
/* h <- rem(h, f) mod p, f not necessarily monic */
void
modul_poly_div_r (modul_poly_t h, const modul_poly_t f, modulusul_t p)
{
int i, d = f->degree, dh = h->degree, monic;
residueul_t *hc = h->coeff, t;
residueul_t aux;
modul_set1(t, p);
monic = modul_is1(f->coeff[d], p);
if (!monic)
modul_inv(t, f->coeff[d], p); /* 1/f[d] mod p */
while (dh >= d)
{
/* subtract h[dh]/f[d]*x^(dh-d)*f from h */
if (!monic)
modul_mul(hc[dh], hc[dh], t, p);
for (i = 0; i < d; i++) {
modul_mul(aux, hc[dh], f->coeff[i], p);
modul_sub(hc[dh - d + i], hc[dh - d + i], aux, p);
}
do {
dh --;
} while (dh >= 0 && modul_is0(hc[dh],p));
h->degree = dh;
}
}
/* q <- divexact(h, f) mod p, f not necessarily monic. Clobbers h. */
void
modul_poly_divexact (modul_poly_t q, modul_poly_t h, const modul_poly_t f, modulusul_t p)
{
int i, d = f->degree, dh = h->degree, monic;
residueul_t *hc = h->coeff, t;
residueul_t aux;
modul_set1(t, p);
ASSERT (d >= 0);
ASSERT (dh >= 0);
ASSERT (dh >= d);
modul_poly_realloc (q, dh + 1 - d);
q->degree = dh - d;
monic = modul_is1(f->coeff[d], p);
if (!monic)
modul_inv(t, f->coeff[d], p);
while (dh >= d)
{
/* subtract h[dh]/f[d]*x^(dh-d)*f from h */
if (!monic)
modul_mul(hc[dh], hc[dh], t, p);
modul_set(q->coeff[dh - d], hc[dh], p);
/* we only need to update the coefficients of degree >= d of h,
i.e., we want i >= 2d - dh */
for (i = (2 * d > dh) ? 2 * d - dh : 0; i < d; i++) {
modul_mul(aux, hc[dh], f->coeff[i], p);
modul_sub(hc[dh - d + i], hc[dh - d + i], aux, p);
}
dh --;
}
modul_poly_normalize (q, p);
}
/* fp <- gcd (fp, g), clobbers g */
void
modul_poly_gcd (modul_poly_t fp, modul_poly_t g, modulusul_t p)
{
while (g->degree >= 0)
{
modul_poly_div_r (fp, g, p);
// modul_poly_mod_ui (fp, fp, p);
/* now deg(fp) < deg(g): swap f and g */
modul_poly_swap (fp, g);
}
}
void
modul_poly_out (FILE *fp, modul_poly_t f, modulusul_t p)
{
if (f->degree < 0)
fprintf (fp, "0\n");
else
{
int i;
for (i = 0; i <= f->degree; i++)
if (!modul_is0(f->coeff[i],p))
gmp_fprintf (fp, "+%Nu*x^%d", f->coeff[i], MODUL_SIZE, i);
fprintf (fp, ";\n");
}
}
/* returns f(x) mod p */
void modul_poly_eval (residueul_t r, modul_poly_t f, residueul_t x, modulusul_t p)
{
int i, d = f->degree;
residueul_t aux;
ASSERT (d >= 0);
modul_set(r, f->coeff[d], p);
for (i = d - 1; i >= 0; i--) {
modul_mul(aux, r, x, p);
modul_add(r, aux, f->coeff[i], p);
}
}
/* Return the number n of roots of f mod p using a naive algorithm.
If r is not NULL, put the roots in r[0], ..., r[n-1]. */
int
modul_poly_roots_naive (residueul_t *r, modul_poly_t f, modulusul_t p)
{
int n = 0;
residueul_t x;
modul_set0(x, p);
for ( ; !modul_finished(x, p) ; modul_next(x, p) ) {
residueul_t v;
modul_poly_eval (v, f, x, p);
if (modul_is0(v, p))
{
if (r != NULL)
modul_set(r[n], x, p);
n ++;
}
}
return n;
}
/* g <- (x+a)^e mod (fp, p), using auxiliary polynomial h */
void
modul_poly_powmod_ui (modul_poly_t g, modul_poly_t fp, modul_poly_t h, residueul_t a,
unsigned long e, modulusul_t p)
{
int k = nbits (e);
modul_poly_make_monic (fp, p);
residueul_t one;
modul_set1(one, p);
/* initialize g to x */
modul_poly_set_linear (g, one, a, p);
ASSERT (e > 0);
for (k -= 2; k >= 0; k--)
{
modul_poly_sqr (h, g, p); /* h <- g^2 */
if (e & (1UL << k))
modul_poly_mul_x (h, a, p); /* h <- x*h */
modul_poly_div_r (h, fp, p); /* h -> rem(h, fp) */
modul_poly_set (g, h, p); /* g <- h */
}
}
/* g <- g^e mod (fp, p), using auxiliary polynomial h */
void
modul_poly_general_powmod_ui (modul_poly_t g, modul_poly_t fp, modul_poly_t h,
unsigned long e, modulusul_t p)
{
int k = nbits (e);
modul_poly_t g_sav;
modul_poly_init(g_sav, g->degree);
modul_poly_set(g_sav, g, p);
modul_poly_make_monic (fp, p);
ASSERT (e > 0);
for (k -= 2; k >= 0; k--)
{
modul_poly_sqr (h, g, p); /* h <- g^2 */
modul_poly_div_r (h, fp, p); /* h -> rem(h, fp) */
modul_poly_set (g, h, p); /* g <- h */
if (e & (1UL << k))
modul_poly_mul (h, h, g_sav, p); /* h <- g_sav*h */
modul_poly_div_r (h, fp, p); /* h -> rem(h, fp) */
modul_poly_set (g, h, p); /* g <- h */
}
modul_poly_clear(g_sav);
}
/* Assuming f is a (squarefree) product of linear factors mod p, splits it
and put the corresponding roots mod p in r[].
Return number of roots found (should be degree of f).
Assumes p is odd, and deg(f) >= 1.
*/
int
modul_poly_cantor_zassenhaus (residueul_t *r, modul_poly_t f, modulusul_t p, int depth)
{
residueul_t a;
modul_poly_t q, h, ff;
int d = f->degree, dq, n, m;
ASSERT (p[0] & 1);
ASSERT (d >= 1);
if (d == 1) /* easy case: linear factor x + a ==> root -a mod p */
{
residueul_t aux;
modul_neg(aux, f->coeff[1], p);
modul_inv(a, aux, p);
modul_mul(r[0], a, f->coeff[0], p);
return 1;
}
/* if f has degree d, then q,h may have up to degree 2d-1 in the powering
algorithm */
modul_poly_init (q, 2 * d - 1);
modul_poly_init (h, 2 * d - 1);
modul_poly_init (ff, d);
modul_set_ul(a, lrand48(), p);
for (;;)
{
modul_poly_powmod_ui (q, f, h, a, (modul_getmod_ul(p) - 1) / 2, p);
modul_poly_sub_1 (q, q, p);
modul_poly_set (h, f, p);
modul_poly_gcd (q, h, p);
dq = q->degree;
ASSERT (dq >= 0);
if (0 < dq && dq < d)
{
n = modul_poly_cantor_zassenhaus (r, q, p, depth + 1);
ASSERT (n == dq);
modul_poly_set (ff, f, p); /* modul_poly_divexact clobbers its 2nd arg */
modul_poly_divexact (h, ff, q, p);
m = modul_poly_cantor_zassenhaus (r + n, h, p, depth + 1);
ASSERT (m == h->degree);
n += m;
break;
}
modul_next(a, p);
if (modul_finished(a, p))
modul_set0(a, p);
}
modul_poly_clear (q);
modul_poly_clear (h);
modul_poly_clear (ff);
return n;
}
typedef int (*sortfunc_t) (const void *, const void *);
int coeff_cmp(
const unsigned long * a,
const unsigned long * b)
{
if (*a < *b) return -1;
if (*b < *a) return 1;
return 0;
}
#define ROOTS_MOD_THRESHOLD 43 /* if only the number of roots is needed */
#define ROOTS_MOD_THRESHOLD2 67 /* if the roots are needed too */
/* The following function returns the number of roots of f(x) mod p,
where f has degree d.
If r is not NULL, put the n roots of f(x) mod p in r[0]...r[n-1].
For p <= ROOTS_MOD_THRESHOLD, determine the number of roots of f mod p
by evaluating f on 0, 1, ..., p-1. In theory, this threshold should
depend on the degree of f. The above thresholds seem to be optimal
on a Pentium M. We must have ROOTS_MOD_THRESHOLD2 >= 2.
The following ideas are from Emmanuel Thome:
FIXME1: instead of first computing f1 = gcd(x^p-x, f) which is the
product of linear factors, and then splitting f1, we can directly
compute f1 = gcd(x^((p-1)/2)-1, f) and f2 = gcd(x^((p-1)/2)+1, f),
assuming the roots 0 has been taken out.
FIXME2: in the EDF, instead of dividing out f by gcd((x+a)^((p-1)/2)-1, f),
we can simply compute gcd((x+a)^((p-1)/2)+1, f).
FIXME3: instead of computing (x+a)^((p-1)/2), we can translate f by -a,
and keep x^((p-1)/2) unchanged. [But then why not translate x^((p-1)/2),
which has lower degree? Warning: if f has root -a, we might miss it.]
*/
int
modul_poly_roots(residueul_t *r, mpz_t *f, int d, modulusul_t p)
{
modul_poly_t fp, g, h;
int df;
/* the number of roots is the degree of gcd(x^p-x,f) */
modul_poly_init (fp, d);
d = modul_poly_set_mod (fp, f, d, p);
/* d is the degree of fp (-1 if fp=0) */
if (d == 0)
{
df = 0;
goto clear_fp;
}
ASSERT (d > 0);
if ((r == NULL && p[0] <= ROOTS_MOD_THRESHOLD) ||
(r != NULL && p[0] <= ROOTS_MOD_THRESHOLD2))
{
df = modul_poly_roots_naive (r, fp, p);
goto clear_fp;
}
modul_poly_init (g, 2 * d - 1);
modul_poly_init (h, 2 * d - 1);
/* we first compute x^p mod fp; since fp has degree d, all operations can
be done with polynomials of degree < 2d: a square give at most degree
2d-2, and multiplication by x gives 2d-1. */
/* g <- x^p mod fp */
residueul_t zero;
modul_init(zero,p);
modul_poly_powmod_ui (g, fp, h, zero, p[0], p);
modul_clear(zero,p);
/* subtract x */
modul_poly_sub_x (g, g, p);
/* fp <- gcd (fp, g) */
modul_poly_gcd (fp, g, p);
/* now fp contains gcd(x^p-x, f) */
df = fp->degree;
ASSERT (df >= 0);
/* If r is NULL, then we're not interested in finding the exact roots.
* Only their number is important, and it's easily read as the degree
* of the gcd.
*/
if (r != NULL && df > 0)
{
int n MAYBE_UNUSED = modul_poly_cantor_zassenhaus (r, fp, p, 0);
ASSERT (n == df);
}
modul_poly_clear (g);
modul_poly_clear (h);
clear_fp:
modul_poly_clear (fp);
if (r && df)
qsort(r, df, sizeof(unsigned long), (sortfunc_t) &coeff_cmp);
return df;
}
int
modul_poly_roots_ulong (unsigned long *r, mpz_t *f, int d, modulusul_t p)
{
residueul_t * pr;
int i, n;
pr = malloc(d * sizeof(residueul_t));
n = modul_poly_roots(pr,f,d,p);
for(i = 0 ; i < n ; i++) {
r[i] = modul_getmod_ul (pr[i]);
}
free(pr);
return n;
}
int
modul_poly_roots_int64 (int64_t *r, mpz_t *f, int d, modulusul_t p)
{
residueul_t *pr;
int i, n;
pr = malloc(d * sizeof(residueul_t));
n = modul_poly_roots(pr,f,d,p);
for(i = 0 ; i < n ; i++) {
r[i] = modul_getmod_ul (pr[i]);
}
free(pr);
return n;
}
int modul_poly_is_irreducible(modul_poly_t fp, modulusul_t p)
{
modul_poly_t g, gmx, h;
int d, i;
modul_poly_make_monic (fp, p);
d = fp->degree;
if (d == 0)
return 1;
ASSERT (d > 0);
modul_poly_init (g, 2 * d - 1);
modul_poly_init (gmx, 2 * d - 1);
modul_poly_init (h, 2 * d - 1);
for (i = 1; 2*i <= d; ++i) {
/* we first compute x^(p^i) mod fp; since fp has degree d, all operations can
be done with polynomials of degree < 2d: a square give at most degree
2d-2, and multiplication by x gives 2d-1. */
if (i == 1) {
/* g <- x^p mod fp */
modul_poly_powmod_ui (g, fp, h, 0, p[0], p);
} else {
/* g <- g^p mod fp */
modul_poly_general_powmod_ui (g, fp, h, p[0], p);
}
/* subtract x */
modul_poly_sub_x (gmx, g, p);
/* h <- gcd (fp, x^(p^i)-x) */
modul_poly_set(h, fp, p);
modul_poly_gcd (h, gmx, p);
if (h->degree > 0)
return 0;
}
modul_poly_clear (g);
modul_poly_clear (gmx);
modul_poly_clear (h);
return 1;
}
Computing file changes ...