/* Author: Pate Williams (c) 1997 "Algorithm 2.6.7 (Integral LLL Algorithm). Given a basis b[1], b[2],..., b[n] of a lattice (L, q) by its gram matrix which is assumed to have inte- gral coefficients, this algorithm transforms the vectors b[i] so that when the algorithm terminates, the b[i] form an LLL-reduced basis." -Henri Cohen- See "A Course in Computational Algebraic Number Theory" by Henri Cohen page 94. Also see "Handbook of Applied Cryptography by Alfred J. Menezes et al 3.108 Algorithm "Finding a delta-quality simulta- neous diophantine approximation" pages 121-122. */ #include #include #include #include "lip.h" void system_error(char error_message[]) { fprintf(stderr, "%s", error_message); exit(1); } verylong **allocate_very_matrix(long lr, long ur, long lc, long uc) { /* Allocates a real matrix of range [lr..ur][lc..uc]. */ long i; verylong **p = calloc((ur - lr + 1), sizeof(verylong *)); if (!p) system_error("Failure in allocate_very_matrix()."); p -= lr; for (i = lr; i <= ur; i++) { p[i]= calloc((uc - lc + 1), sizeof(verylong)); if (!p[i]) system_error("Failure in allocate_very_matrix()."); p[i] -= lc; } return p; } verylong *allocate_very_vector(long l, long u) { /* Allocates a verylong vector of range [l..u]. */ verylong *p; p = calloc((u - l + 1), sizeof(verylong)); if (!p) system_error("Failure in allocate_very_vector()."); return p - l; } void free_very_matrix(verylong **m, long lr, long ur, long lc, long uc) { /* Frees a verylong matrix of range [lr..ur][lc..uc]. */ long i, j; for (i = lr; i <= ur; i++) for (j = lc; j <= uc; j++) zfree(&m[i][j]); for (i = ur; i >= lr; i--) free((char *)(m[i] + lc)); free((char *) (m + lr)); } void free_very_vector(verylong *v, long l, long u) { /* Frees a verylong vector of range [l..u]. */ long i; for (i = l; i <= u; i++) zfree(&v[i]); free((char *)(v + l)); } void scalar(long n, verylong *za, verylong *zb, verylong *zs) { /* *s = inner_product(a, b) */ long i; verylong zt = 0, zu = 0; zzero(zs); for (i = 1; i <= n; i++) { zmul(za[i], zb[i], &zt); zadd(zt, *zs, &zu); zcopy(zu, zs); } zfree(&zt); zfree(&zu); } void RED(long k, long l, long n, verylong *zd, verylong **zb, verylong **zh, verylong **zl) { long i; verylong zq = 0, zr = 0, zs = 0, zt = 0; zlshift(zl[k][l], 1l, &zr); zcopy(zr, &zs); zabs(&zs); if (zcompare(zs, zd[l]) > 0) { zadd(zr, zd[l], &zs); zlshift(zd[l], 1l, &zr); zdiv(zs, zr, &zq, &zt); for (i = 1; i <= n; i++) { zmul(zq, zh[i][l], &zr); zsub(zh[i][k], zr, &zs); zcopy(zs, &zh[i][k]); zmul(zq, zb[l][i], &zr); zsub(zb[k][i], zr, &zs); zcopy(zs, &zb[k][i]); } zmul(zq, zd[l], &zr); zsub(zl[k][l], zr, &zs); zcopy(zs, &zl[k][l]); for (i = 1; i <= l - 1; i++) { zmul(zq, zl[l][i], &zr); zsub(zl[k][i], zr, &zs); zcopy(zs, &zl[k][i]); } } zfree(&zq); zfree(&zr); zfree(&zs); zfree(&zt); } void SWAP(long k, long k1, long kmax, long n, verylong *zd, verylong **zb, verylong **zh, verylong **zl) { long i, j; verylong zB = 0, zm = 0, zr = 0, zs = 0, zt = 0; verylong zu = 0; for (i = 1; i <= n; i++) { zcopy(zh[i][k], &zt); zcopy(zh[i][k1], &zh[i][k]); zcopy(zt, &zh[i][k1]); } for (j = 1; j <= n; j++) { zcopy(zb[k][j], &zt); zcopy(zb[k1][j], &zb[k][j]); zcopy(zt, &zb[k1][j]); } if (k > 2) { for (j = 1; j <= k - 2; j++) { zcopy(zl[k][j], &zt); zcopy(zl[k1][j], &zl[k][j]); zcopy(zt, &zl[k1][j]); } } zcopy(zl[k][k1], &zm); zmul(zd[k - 2], zd[k], &zr); zsq(zm, &zs); zadd(zr, zs, &zt); zdiv(zt, zd[k1], &zB, &zr); for (i = k + 1; i <= kmax; i++) { zcopy(zl[i][k], &zt); zmul(zd[k], zl[i][k1], &zr); zmul(zm, zt, &zs); zsub(zr, zs, &zu); zdiv(zu, zd[k1], &zl[i][k], &zr); zmul(zB, zt, &zr); zmul(zm, zl[i][k], &zs); zadd(zr, zs, &zu); zdiv(zu, zd[k], &zl[i][k1], &zr); } zcopy(zB, &zd[k1]); zfree(&zB); zfree(&zm); zfree(&zr); zfree(&zs); zfree(&zt); zfree(&zu); } void int_LLL(long n, verylong **zb, verylong **zh) { double x, y; long i, j, k = 2, k1, kmax = 1, l; verylong zr = 0, zs = 0, zt = 0, zu = 0; verylong *zB = allocate_very_vector(1, n); verylong *zd = allocate_very_vector(0, n); verylong **zl = allocate_very_matrix(1, n, 1, n); zone(&zd[0]); scalar(n, zb[1], zb[1], &zd[1]); for (i = 1; i <= n; i++) { for (j = 1; j <= n; j++) zzero(&zh[i][j]); zone(&zh[i][i]); } #ifdef DEBUG if (n <= 17) { printf("the basis to be reduced is:\n"); for (i = 1; i <= n; i++) { for (j = 1; j <= n; j++) { zwrite(zb[i][j]); printf(" "); } printf("\n"); } } #endif L2: if (k <= kmax) goto L3; kmax = k; for (j = 1; j <= k; j++) { scalar(n, zb[k], zb[j], &zu); for (i = 1; i <= j - 1; i++) { zmul(zd[i], zu, &zr); zmul(zl[k][i], zl[j][i], &zs); zsub(zr, zs, &zt); zdiv(zt, zd[i - 1], &zu, &zr); } if (j < k) zcopy(zu, &zl[k][j]); else if (j == k) { zcopy(zu, &zd[k]); if (zscompare(zd[k], 0l) == 0) system_error("Failure in int_LLL."); } } L3: k1 = k - 1; RED(k, k1, n, zd, zb, zh, zl); zmul(zd[k], zd[k - 2], &zr); zsq(zd[k1], &zs); zsq(zl[k][k1], &zt); x = zdoub(zr); y = 3.0 * zdoub(zs) / 4.0 - zdoub(zt); if (x < y) { SWAP(k, k1, kmax, n, zd, zb, zh, zl); k = max(2, k1); goto L3; } for (l = k - 2; l >= 1; l--) RED(k, l, n, zd, zb, zh, zl); if (++k <= n) goto L2; #ifdef DEBUG if (n <= 17) { printf("the LLL-reduced basis is:\n"); for (i = 1; i <= n; i++) { for (j = 1; j <= n; j++) { zwrite(zb[i][j]); printf(" "); } printf("\n"); } } #endif free_very_matrix(zl, 1, n, 1, n); free_very_vector(zB, 1, n); free_very_vector(zd, 0, n); zfree(&zr); zfree(&zs); zfree(&zt); zfree(&zu); } int simultaneous_diophantine(double delta, long n, verylong zQ, verylong *zP, verylong *zp, verylong *zq) { double P, Q, l; int equal, found; long i, j, n1 = n + 1; verylong zd = 0, zl = 0, zr = 0, zs = 0, zt = 0; verylong **zA = allocate_very_matrix(1, n1, 1, n1); verylong **zh = allocate_very_matrix(1, n1, 1, n1); Q = zdoub(zQ); zintoz(pow(Q, delta), &zl); l = 1.0 / zdoub(zl); zmul(zl, zQ, &zd); for (i = 1; i <= n; i++) zcopy(zd, &zA[i][i]); znegate(&zl); for (i = 1; i <= n; i++) zmul(zl, zq[i], &zA[n1][i]); zone(&zA[n1][n1]); int_LLL(n1, zA, zh); found = 0; for (j = 1; !found && j <= n1; j++) { zcopy(zA[j][n1], zP); if (zcompare(*zP, zQ) != 0) { for (i = 1; i <= n; i++) { zdiv(zA[j][i], zl, &zr, &zs); zmul(*zP, zq[i], &zt); zadd(zr, zt, &zs); zdiv(zs, zQ, &zp[i], &zr); } P = zdoub(*zP); #ifdef DEBUG if (n <= 16) { printf("p = "); zwrite(*zP); printf(" p[i] "); for (i = 1; i <= n; i++) { zwrite(zp[i]); printf(" "); } printf("\n"); } #endif if (zcompare(*zP, 0) != 0) { equal = 1; for (i = 1; equal && i <= n; i++) equal = fabs(P * zdoub(zq[i]) / Q - zdoub(zp[i])) <= l; } else equal = 0; found = equal; } } free_very_matrix(zA, 1, n1, 1, n1); free_very_matrix(zh, 1, n1, 1, n1); zfree(&zd); zfree(&zl); zfree(&zr); zfree(&zs); zfree(&zt); return found; } int main(void) { double delta = 0.15; long i, n = 16; verylong zP = 0, zQ = 0; verylong *zp = allocate_very_vector(1, n); verylong *zq = allocate_very_vector(1, n); zpstart2(); for (i = 1; i <= n; i++) zintoz(zpnext(), &zq[i]); zintoz(zpnext(), &zQ); printf("delta = %lf\n", delta); printf("n = %ld\n", n); if (n <= 16) { printf("q = "); zwriteln(zQ); printf("q[i] "); for (i = 1; i <= n; i++) { zwrite(zq[i]); printf(" "); } } printf("\n"); if (simultaneous_diophantine(delta, n, zQ, &zP, zp, zq)) { printf("p = "); zwriteln(zP); printf("p[i] "); for (i = 1; i <= n; i++) { zwrite(zp[i]); printf(" "); } } else printf("\nno simultaneous diophantine approximation\n"); free_very_vector(zp, 1, n); free_very_vector(zq, 1, n); return 0; }