/* Author: Pate Williams (c) 1997 "Algorithm 3.4.3 (Distinct Degree Factorization). Given a squarefree polynomial A contained in Fp[X], this algorithm finds for each d the polynomial A[d] which is the product of the irreducible factors of A of degree d." -Henri Cohen- See "A Course in Computational Algebraic Number Theory" by Henri Cohen page 126. */ #include #include #include #include "lip.h" #define DEBUG #define POLY_SIZE 256 void error(char *message) { fprintf(stderr, "%s", message); exit(1); } verylong **allocate_very_matrix(long m, long n) { long i; verylong **matrix = calloc(m, sizeof(verylong *)); if (!matrix) error("Failure in allocate_very_matrix."); for (i = 0; i < m; i++) { matrix[i] = calloc(n, sizeof(verylong)); if (!matrix[i]) error("Failure in allocate_very_matrix."); } return matrix; } void free_very_matrix(verylong **zm, long m, long n) { long i, j; for (i = 0; i < m; i++) { for (j = 0; j < n; j++) zfree(&zm[i][j]); free(zm[i]); } free(zm); } void monic(long dA, long p, verylong *zA) { long i; verylong za = 0, zi = 0, zp = 0; zintoz(p, &zp); zinvmod(zA[dA], zp, &zi); zone(&zA[dA]); for (i = 0; i < dA; i++) { zmulmod(zA[i], zi, zp, &za); zcopy(za, &zA[i]); } zfree(&za); zfree(&zi); zfree(&zp); } void zpoly_mul(long m, long n, verylong *za, verylong *zb, verylong *zc, long *p) { long i, j, k; verylong zd = 0, zai = 0, zbk = 0, zsum = 0, zterm = 0; *p = m + n; for (k = 0; k <= *p; k++) { zzero(&zsum); for (i = 0; i <= k; i++) { j = k - i; if (i > m) zzero(&zai); else zcopy(za[i], &zai); if (j > n) zzero(&zbk); else zcopy(zb[j], &zbk); zmul(zai, zbk, &zterm); zcopy(zsum, &zd); zadd(zterm, zd, &zsum); } zcopy(zsum, &zc[k]); } zfree(&zd); zfree(&zai); zfree(&zbk); zfree(&zsum); zfree(&zterm); } void zspow(verylong za, long n, verylong *zb) { int c = zscompare(za, 0l); verylong zc = 0; if (c > 0) zsexp(za, n, zb); else if (c < 0) { zcopy(za, &zc); zabs(&zc); zsexp(zc, n, zb); if (n & 1) znegate(zb); } else zzero(zb); zfree(&zc); } void zpoly_div(long m, long n, verylong *zu, verylong *zv, verylong *zq, verylong *zr, long *p, long *s) { long j, jk, k, nk; verylong za = 0, zb = 0, zvn = 0; zcopy(zv[n], &zvn); for (j = 0; j <= m; j++) zcopy(zu[j], &zr[j]); if (m < n) { *p = 0, *s = m; zzero(&zq[0]); } else { *p = m - n, *s = n - 1; for (k = *p; k >= 0; k--) { nk = n + k; zspow(zvn, k, &za); zmul(zr[nk], za, &zq[k]); for (j = nk - 1; j >= 0; j--) { jk = j - k; if (jk >= 0) { zmul(zvn, zr[j], &za); zmul(zr[nk], zv[jk], &zb); zsub(za, zb, &zr[j]); } else { zcopy(zr[j], &za); zmul(zvn, za, &zr[j]); } } } while (*p > 0 && zscompare(zq[*p], 0l) == 0) *p = *p - 1; while (*s > 0 && zscompare(zr[*s], 0l) == 0) *s = *s - 1; } zfree(&za); zfree(&zb); zfree(&zvn); } void zpoly_pow(long degreeA, long degreem, verylong zn, verylong *zA, verylong *zm, verylong *zs, long *ds) { long dp, dq, dx = degreeA, i; verylong za = 0, zb = 0, zp[POLY_SIZE], zq[POLY_SIZE], zx[POLY_SIZE], zy[POLY_SIZE]; for (i = 0; i < POLY_SIZE; i++) zp[i] = zq[i] = zx[i] = zy[i] = 0; *ds = 0; zcopy(zn, &za); zone(&zs[0]); for (i = 0; i <= dx; i++) zcopy(zA[i], &zx[i]); while (zscompare(za, 0l) > 0) { if (zodd(za)) { /* s = (s * x) % m; */ zpoly_mul(*ds, dx, zs, zx, zp, &dp); zpoly_div(dp, degreem, zp, zm, zq, zs, &dq, ds); } zcopy(za, &zb); zrshift(zb, 1l, &za); if (zscompare(za, 0l) > 0) { /* x = (x * x) % m; */ for (i = 0; i <= dx; i++) zcopy(zx[i], &zy[i]); zpoly_mul(dx, dx, zx, zy, zp, &dp); zpoly_div(dp, degreem, zp, zm, zq, zx, &dq, &dx); } } zfree(&za); zfree(&zb); for (i = 0; i < POLY_SIZE; i++) { zfree(&zp[i]); zfree(&zq[i]); zfree(&zx[i]); zfree(&zy[i]); } } void zpoly_sub(long da, long db, verylong *za, verylong *zb, verylong *zc, long *dc) /* c = a - b */ { long i; verylong zz = 0; zzero(&zz); if (da >= db) { for (i = 0; i <= db; i++) zsub(za[i], zb[i], &zc[i]); for (i = db + 1; i <= da; i++) zcopy(za[i], &zc[i]); *dc = da; } else { for (i = 0; i <= da; i++) zsub(za[i], zb[i], &zc[i]); for (i = da + 1; i <= db; i++) zsub(zz, zb[i], &zc[i]); *dc = db; } zfree(&zz); } void zpoly_gcd(long degreeA, long degreeB, long p, verylong *zA, verylong *zB, verylong *za, long *da) { int nonzero = 0, zero; long db, dq, dr, i; verylong zc = 0, zp = 0; verylong zb[POLY_SIZE], zq[POLY_SIZE], zr[POLY_SIZE]; for (i = 0; i < POLY_SIZE; i++) zb[i] = zq[i] = zr[i] = 0; if (degreeA > degreeB) { *da = degreeA; db = degreeB; for (i = 0; i <= *da; i++) zcopy(zA[i], &za[i]); for (i = 0; i <= db; i++) zcopy(zB[i], &zb[i]); } else { *da = degreeB; db = degreeA; for (i = 0; i <= *da; i++) zcopy(zB[i], &za[i]); for (i = 0; i <= db; i++) zcopy(zA[i], &zb[i]); } for (i = 0; i <= db && !nonzero; i++) nonzero = zscompare(zb[i], 0l) != 0; while (nonzero) { zpoly_div(*da, db, za, zb, zq, zr, &dq, &dr); zintoz(p, &zp); for (i = 0; i <= dr; i++) { zcopy(zr[i], &zc); zmod(zc, zp, &zr[i]); } zero = 1; for (i = dr; i >= 0 && zero; i--) { zero = zscompare(zr[i], 0l) == 0; if (zero && dr > 0) dr--; } for (i = 0; i <= db; i++) zcopy(zb[i], &za[i]); *da = db; for (i = 0; i <= dr; i++) zcopy(zr[i], &zb[i]); db = dr; nonzero = 0; for (i = 0; i <= db && !nonzero; i++) nonzero = zscompare(zb[i], 0l) != 0; } zfree(&zc); zfree(&zp); for (i = 0; i < POLY_SIZE; i++) { zfree(&zb[i]); zfree(&zq[i]); zfree(&zr[i]); } } void zpoly_write(char *label, long dA, verylong *zA) { long i; printf("%s", label); for (i = dA; i >= 0; i--) { zwrite(zA[i]); printf(" "); } printf("\n"); } void distinct_degree(long dA, long p, verylong *zA, verylong **zB, long *dB, long *count) /* distinct degree factorization */ { int zero; long d = 0, d1, dP, dR, dV = dA, dW = 1, e, e2, i; verylong za = 0, zp = 0; verylong zP[POLY_SIZE] = {0}; verylong zR[POLY_SIZE] = {0}; verylong zV[POLY_SIZE] = {0}; verylong zW[POLY_SIZE] = {0}; *count = 0; zintoz(p, &zp); for (i = 0; i <= dA; i++) zcopy(zA[i], &zV[i]); zzero(&zW[0]); zone(&zW[1]); L2: e = dV; d1 = d + 1; e2 = e / 2; if (d1 > e2) { if (e > 0) { monic(dV, p, zV); for (i = 0; i <= dV; i++) zcopy(zV[i], &zB[*count][i]); dB[*count] = dV; *count = *count + 1; for (i = d + 1; i <= e; i++) { zone(&zB[*count][0]); dB[*count] = 0; *count = *count + 1; } } zfree(&za); zfree(&zp); for (i = 0; i < POLY_SIZE; i++) { zfree(&zP[i]); zfree(&zR[i]); zfree(&zV[i]); zfree(&zW[i]); } return; } d++; zpoly_pow(dW, dV, zp, zW, zV, zP, &dP); for (i = 0; i <= dP; i++) zintoz(zsmod(zP[i], p), &zW[i]); dW = dP; zsadd(zW[1], - 1l, &za); zcopy(za, &zW[1]); zpoly_gcd(dW, dV, p, zW, zV, zB[*count], &dB[*count]); zero = 1; for (i = 1; zero && i <= dB[*count]; i++) zero = zscompare(zB[*count][i], 0l) == 0; monic(dB[*count], p, zB[*count]); if (!zero || (zero && zscompare(zB[*count][0], 1l) != 0)) { zpoly_div(dV, dB[*count], zV, zB[*count], zP, zR, &dP, &dR); for (i = 0; i <= dP; i++) zintoz(zsmod(zP[i], p), &zV[i]); dV = dP; zsadd(zW[1], + 1l, &za); zcopy(za, &zW[1]); zpoly_div(dW, dV, zW, zV, zP, zR, &dP, &dR); for (i = 0; i <= dR; i++) zintoz(zsmod(zR[i], p), &zW[i]); dW = dR; } #ifdef DEBUG zpoly_write("W = ", dW, zW); zpoly_write("V = ", dV, zV); zpoly_write("A = ", dB[*count], zB[*count]); #endif *count = *count + 1; goto L2; } int main(void) { char answer[256]; long count, dA, dB[32], i, m = 32, p; verylong zA[POLY_SIZE] = {0}; verylong **zB = allocate_very_matrix(m, POLY_SIZE); do { printf("degree of polynomial = "); scanf("%d", &dA); printf("modulus = "); scanf("%d", &p); for (i = 0; i <= dA; i++) { printf("A[%ld] = ", i); zread(&zA[i]); } distinct_degree(dA, p, zA, zB, dB, &count); for (i = 0; i < count; i++) zpoly_write("factor = ", dB[i], zB[i]); printf("another polynomial (n or y)? "); scanf("%s", answer); } while (tolower(answer[0]) == 'y'); for (i = 0; i <= dA; i++) zfree(&zA[i]); free_very_matrix(zB, m, POLY_SIZE); return 0; }