
















#include <algorithm>
#include <iomanip>
#include <iostream>
#include <chrono>
#include <vector>
using namespace std;
typedef long long ll;
typedef struct ecPoint
{
ll x, y;
} ECPOINT, *PECPOINT;
typedef struct ecPointOrder
{
ll order;
ECPOINT pt;
} ECPOINTORDER, * PECPOINTORDER;
int JACOBI(ll a, ll n)
{
int e = 0, s;
ll a1, b = a, m, n1;
if (a == 0) return 0;
if (a == 1) return 1;
while ((b & 1) == 0)
{
b >>= 1;
e++;
}
a1 = b;
m = n % 8;
if (!(e & 1)) s = 1;
else if (m == 1 || m == 7) s = +1;
else if (m == 3 || m == 5) s = -1;
if (n % 4 == 3 && a1 % 4 == 3) s = -s;
if (a1 != 1) n1 = n % a1; else n1 = 1;
return s * JACOBI(n1, a1);
}
ll ExpMod(ll x, ll b, ll n)
/* returns x ^ b mod n */
{
ll a = 1LL, s = x;
if (b == 0)
return 1LL;
while (b != 0) {
if (b & 1l) a = (a * s) % n;
b >>= 1;
if (b != 0) s = (s * s) % n;
}
if (a < 0)
a += n;
return a;
}
ll ExtendedEuclidean(ll b, ll n)
{
ll b0 = b, n0 = n, t = 1, t0 = 0, temp, q, r;
q = n0 / b0;
r = n0 - q * b0;
while (r > 0) {
temp = t0 - q * t;
if (temp >= 0) temp = temp % n;
else temp = n - (-temp % n);
t0 = t;
t = temp;
n0 = b0;
b0 = r;
q = n0 / b0;
r = n0 - q * b0;
}
if (b0 != 1) return 0;
else return t % n;
}
ll Weierstrass(ll a, ll b, ll x, ll p)
{
return ((((x * x) % p) * x) % p + (a * x) % p + b) % p;
}
ll EPoints(
bool print,
ll a, ll b, ll p,
vector<ECPOINT>& e)
/* returns the number of points on the elliptic
curve y ^ 2 = x ^ 3 + ax + b mod p */
{
ll count = 0, m = (p + 1) / 4, x, y;
ECPOINT pt{};
if (p % 4 == 3) {
for (x = 0; x < p; x++) {
y = Weierstrass(a, b, x, p);
if (JACOBI(y, p) != -1) {
y = ExpMod(y, m, p);
if (print)
{
cout << "(" << setw(2) << x << ", ";
cout << y << ") " << endl;
}
pt.x = x;
pt.y = y;
e.push_back(pt);
count++;
y = -y % p;
if (y < 0) y += p;
if (y != 0)
{
if (print)
{
cout << "(" << setw(2) << x << ", ";
cout << y << ") " << endl;
}
pt.x = x;
pt.y = y;
e.push_back(pt);
count++;
}
if (print && count % 5 == 0)
cout << endl;
}
}
if (print && count % 5 != 0)
cout << endl;
}
return count;
}
void Add(ll a, ll p, ECPOINT P,
ECPOINT Q, ECPOINT& R)
/* elliptic curve point partial addition */
{
ll i, lambda;
if (P.x == Q.x && P.y == 0 && Q.y == 0) {
R.x = 0;
R.y = 1;
return;
}
if (P.x == Q.x && P.y == p - Q.y) {
R.x = 0;
R.y = 1;
return;
}
if (P.x == 0 && P.y == 1) {
R = Q;
return;
}
if (Q.x == 0 && Q.y == 1) {
R = P;
return;
}
if (P.x != Q.x) {
i = Q.x - P.x;
if (i < 0) i += p;
i = ExtendedEuclidean(i, p);
lambda = ((Q.y - P.y) * i) % p;
}
else {
i = ExtendedEuclidean((2 * P.y) % p, p);
lambda = ((3 * P.x * P.x + a) * i) % p;
}
if (lambda < 0) lambda += p;
R.x = (lambda * lambda - P.x - Q.x) % p;
R.y = (lambda * (P.x - R.x) - P.y) % p;
if (R.x < 0) R.x += p;
if (R.y < 0) R.y += p;
}
void Multiply(
ll a, ll k, ll p,
ECPOINT P,
ECPOINT& R)
{
ECPOINT S;
R.x = 0;
R.y = 1;
S = P;
while (k != 0) {
if (k & 1) Add(a, p, R, S, R);
k >>= 1;
if (k != 0) Add(a, p, S, S, S);
}
}
ll Order(ll a, ll p, ECPOINT P)
{
ll order = 1;
ECPOINT Q = P, R{};
do {
order++;
Add(a, p, P, Q, R);
Q = R;
} while (R.x != 0 && R.y != 1);
return order;
}
const int PrimeSize = 10000000;
bool sieve[PrimeSize];
void PopulateSieve() {
// sieve of Eratosthenes
int c, inc, i, n = PrimeSize - 1;
for (i = 0; i < n; i++)
sieve[i] = false;
sieve[1] = false;
sieve[2] = true;
for (i = 3; i <= n; i++)
sieve[i] = (i & 1) == 1 ? true : false;
c = 3;
do {
i = c * c;
inc = c + c;
while (i <= n) {
sieve[i] = false;
i += inc;
}
c += 2;
while (!sieve[c])
c++;
} while (c * c <= n);
}
ll Partition(
vector<ECPOINTORDER>& a, ll n, ll lo, ll hi)
{
ll pivotIndex = lo + (hi - lo) / 2;
ECPOINTORDER po = a[(unsigned int)pivotIndex];
ECPOINTORDER x = po;
ECPOINTORDER t = x;
a[(unsigned int)pivotIndex] = a[(unsigned int)hi];
a[(unsigned int)hi] = t;
ll storeIndex = lo;
for (unsigned int i = (unsigned int)lo; i < (unsigned int)hi; i++)
{
if (a[i].order < x.order)
{
t = a[i];
a[i] = a[(unsigned int)storeIndex];
a[(unsigned int)(storeIndex++)] = t;
}
}
t = a[(unsigned int)storeIndex];
a[(unsigned int)storeIndex] = a[(unsigned int)hi];
a[(unsigned int)hi] = t;
return storeIndex;
}
static void DoQuickSort(
vector<ECPOINTORDER>& a, ll n, ll p, ll r)
{
if (p < r)
{
ll q = Partition(a, n, p, r);
DoQuickSort(a, n, p, q - 1);
DoQuickSort(a, n, q + 1, r);
}
}
void QuickSort(vector<ECPOINTORDER>& a, ll n)
{
DoQuickSort(a, n, 0, n - 1);
}
int main()
{
PopulateSieve();
while (true)
{
bool noncyclic = false;
int option;
ll a, b, p;
cout << "Enter 1 to continue or 0 to quit = ";
cin >> option;
if (option == 0)
break;
cout << "Weierstrass parameter a = ";
cin >> a;
cout << "Weierstrass parameter b = ";
cin >> b;
cout << "Enter the prime parameter p = ";
cin >> p;
if (!sieve[p])
{
cout << "p is not a prime number. " << endl;
continue;
}
if (p % 4 != 3)
{
cout << "p mod 4 must be 3 for this algorithm." << endl;
continue;
}
bool print = false, enumerate = false;
vector<ECPOINT> e;
vector<ECPOINTORDER> eOrder;
cout << "Enumerate points (0 for no or 1 for yes)? ";
cin >> option;
enumerate = option == 1;
auto time0 = chrono::high_resolution_clock::now();
ll count = EPoints(print, a, b, p, e);
auto time1 = chrono::high_resolution_clock::now();
auto elapsed = time1 - time0;
ll h = count + 1;
ll maxOrder = 0;
ll minOrder = h;
ll order = -1;
ECPOINT P{}, Q{};
if (enumerate)
cout << "The points (x, y) on E and their orders are:" << endl;
for (unsigned int i = 0; i < count; i++)
{
ECPOINTORDER ptOrder{};
order = Order(a, p, e[i]);
ptOrder.order = order;
ptOrder.pt = e[i];
eOrder.push_back(ptOrder);
if (order < h)
noncyclic = true;
if (order < minOrder) {
P = e[i];
minOrder = order;
}
if (order > maxOrder) {
Q = e[i];
maxOrder = order;
}
}
if (enumerate)
{
QuickSort(eOrder, count);
for (unsigned int i = 0; i < eOrder.size(); i++)
{
cout << "(" << eOrder[i].pt.x << ", ";
cout << eOrder[i].pt.y << ") ";
cout << eOrder[i].order << "\t";
if ((i + 1) % 5 == 0)
cout << endl;
}
if (count % 5 != 0)
cout << endl;
}
cout << "#E = " << h << endl;
cout << "Noncyclic = " << noncyclic << endl;
cout << "Minimum order = " << minOrder << endl;
cout << "Maximum order = " << maxOrder << endl;
cout << "(" << P.x << ", " << P.y << ")" << endl;
cout << "(" << Q.x << ", " << Q.y << ")" << endl;
long long runtime = chrono::duration_cast<chrono::milliseconds>(elapsed).count();
if (runtime != 0)
cout << "Point counting runtime in milliseconds = " << runtime << endl;
else
{
runtime = chrono::duration_cast<chrono::microseconds>(elapsed).count();
cout << "Point counting runtime in microseconds = " << runtime << endl;
}
}
return 0;
}

/*
Author: Pate Williams (c) 1997
Exercise
"5.6 The field GF(2 ^ 5) can be constructed as
Z_2[x]/(x ^ 5 + x ^ 2 + 1). Perform the following
computations in this field.
(a) Compute (x ^ 4 + x ^ 2) * (x ^ 3 + x + 1).
(b) Using the Extended Euclidean algorithm
compute (x ^ 3 + x ^ 2) ^ - 1.
(c) Using the square and multiply algorithm,
compute x ^ 25." -Douglas R. Stinson-
See "Cryptography: Theory and Practice" by
Douglas R. Stinson page 201.
*/
#include <math.h>
#include <stdio.h>
#define SIZE 32l
void poly_mul(long m, long n, long *a, long *b, long *c, long *p)
{
long ai, bj, i, j, k, sum;
*p = m + n;
for (k = 0; k <= *p; k++) {
sum = 0;
for (i = 0; i <= k; i++) {
j = k - i;
if (i > m) ai = 0; else ai = a[i];
if (j > n) bj = 0; else bj = b[j];
sum += ai * bj;
}
c[k] = sum;
}
}
void poly_div(long m, long n, long *u, long *v,
long *q, long *r, long *p, long *s)
{
long j, jk, k, nk, vn = v[n];
for (j = 0; j <= m; j++) r[j] = u[j];
if (m < n) {
*p = 0, *s = m;
q[0] = 0;
}
else {
*p = m - n, *s = n - 1;
for (k = *p; k >= 0; k--) {
nk = n + k;
q[k] = r[nk] * pow(vn, k);
for (j = nk - 1; j >= 0; j--) {
jk = j - k;
if (jk >= 0)
r[j] = vn * r[j] - r[nk] * v[j - k];
else
r[j] = vn * r[j];
}
}
while (*p > 0 && q[*p] == 0) *p = *p - 1;
while (*s > 0 && r[*s] == 0) *s = *s - 1;
}
}
void poly_exp_mod(long degreeA, long degreem, long n,
long modulus, long *A, long *m, long *s,
long *ds)
{
int zero;
long dp, dq, dx = degreeA, i;
long p[SIZE], q[SIZE], x[SIZE];
*ds = 0, s[0] = 1;
for (i = 0; i <= dx; i++) x[i] = A[i];
while (n > 0) {
if ((n & 1) == 1) {
/* s = (s * x) % m; */
poly_mul(*ds, dx, s, x, p, &dp);
poly_div(dp, degreem, p, m, q, s, &dq, ds);
for (i = 0; i <= *ds; i++) s[i] %= modulus;
zero = s[*ds] == 0, i = *ds;
while (i > 0 && zero) {
if (zero) *ds = *ds - 1;
zero = s[--i] == 0;
}
}
n >>= 1;
/* x = (x * x) % m; */
poly_mul(dx, dx, x, x, p, &dp);
poly_div(dp, degreem, p, m, q, x, &dq, &dx);
for (i = 0; i <= dx; i++) x[i] %= modulus;
zero = x[dx] == 0, i = dx;
while (i > 0 && zero) {
if (zero) dx--;
zero = x[--i] == 0;
}
}
}
void poly_copy(long db, long *a, long *b, long *da)
/* a = b */
{
long i;
*da = db;
for (i = 0; i <= db; i++) a[i] = b[i];
}
int poly_Extended_Euclidean(long db, long dn,
long *b, long *n,
long *t, long *dt)
{
int nonzero;
long db0, dn0, dq, dr, dt0 = 0, dtemp, du, i;
long b0[SIZE], n0[SIZE], q[SIZE];
long r[SIZE], t0[SIZE], temp[SIZE], u[SIZE];
*dt = 0;
poly_copy(dn, n0, n, &dn0);
poly_copy(db, b0, b, &db0);
t0[0] = 0;
t[0] = 1;
poly_div(dn0, db0, n0, b0, q, r, &dq, &dr);
nonzero = r[0] != 0;
for (i = 1; !nonzero && i <= dr; i++)
nonzero = r[i] != 0;
while (nonzero) {
poly_mul(dq, *dt, q, t, u, &du);
if (dt0 < du)
for (i = dt0 + 1; i <= du; i++) t0[i] = 0;
for (i = 0; i <= du; i++)
temp[i] = t0[i] - u[i];
dtemp = du;
poly_copy(*dt, t0, t, &dt0);
poly_copy(dtemp, t, temp, dt);
poly_copy(db0, n0, b0, &dn0);
poly_copy(dr, b0, r, &db0);
poly_div(dn0, db0, n0, b0, q, r, &dq, &dr);
nonzero = r[0] != 0;
for (i = 1; !nonzero && i <= dr; i++)
nonzero = r[i] != 0;
}
if (db0 != 0 && b0[0] != 1) return 0;
return 1;
}
void poly_mod(long da, long p, long *a, long *new_da)
{
int zero;
long i;
for (i = 0; i <= da; i++) {
a[i] %= p;
if (a[i] < 0) a[i] += p;
}
zero = a[da] == 0;
for (i = da - 1; zero && i >= 0; i--) {
da--;
zero = a[i] == 0;
}
*new_da = da;
}
void poly_write(char *label, long da, long *a)
{
long i;
printf("%s", label);
for (i = da; i >= 0; i--)
printf("%ld ", a[i]);
printf("\n");
}
int main(void)
{
long da = 4, db = 3, dc = 3, dd = 5;
long de, dq, dr, ds, p = 2;
long a[5] = { 0, 0, 1, 0, 1 };
long b[4] = { 1, 1, 0, 1 };
long c[4] = { 0, 0, 1, 1 };
long d[6] = { 1, 0, 1, 0, 0, 1 };
long e[SIZE], q[SIZE], r[SIZE], s[SIZE];
poly_write("A = ", da, a);
poly_write("B = ", db, b);
poly_write("C = ", dc, c);
poly_write("D = ", dd, d);
poly_mul(da, db, a, b, e, &de);
poly_mod(de, p, e, &de);
poly_div(de, dd, e, d, q, r, &dq, &dr);
poly_mod(dq, p, q, &dq);
poly_mod(dr, p, r, &dr);
poly_write("A * B mod 2 = ", de, e);
poly_write("A * B / D mod 2 = ", dq, q);
poly_write("A * B mod D, 2 = ", dr, r);
if (!poly_Extended_Euclidean(dc, dd, c, d, e, &de))
printf("*error*\in poly_Extended_Euclidean\n");
poly_mod(de, p, e, &de);
poly_write("C ^ - 1 mod D = ", de, e);
poly_mul(dc, de, c, e, s, &ds);
poly_mod(ds, p, s, &ds);
poly_div(ds, dd, s, d, q, r, &dq, &dr);
poly_mod(dr, p, r, &dr);
poly_write("C * C ^ - 1 mod D, 2 = ", dr, r);
dq = 1;
q[0] = 0;
q[1] = 1;
poly_exp_mod(dq, dd, 7, p, q, d, s, &ds);
poly_mod(ds, p, s, &ds);
poly_write("x ^ 7 mod D, 2 = ", ds, s);
poly_exp_mod(dq, dd, 9, p, q, d, s, &ds);
poly_mod(ds, p, s, &ds);
poly_write("x ^ 9 mod D, 2 = ", ds, s);
poly_exp_mod(dq, dd, 10, p, q, d, s, &ds);
poly_mod(ds, p, s, &ds);
poly_write("x ^ 10 mod D, 2 = ", ds, s);
poly_exp_mod(dq, dd, 25, p, q, d, s, &ds);
poly_mod(ds, p, s, &ds);
poly_write("x ^ 25 mod D, 2 = ", ds, s);
return 0;
}




As can be seen the C++ application appears to be much faster than the C# application. Also, in my humble opinion, C++ with header files and C++ source code files is much more elegant than C# with only class source code. I leave it to someone else to add C# interfaces to my class source code files.
A few years ago, I implemented the Shanks-Mestre elliptic curve point counting algorithm in C# using the BigInteger data structure and the algorithm found in Henri Cohen’s textbook A Course in Computational Algebraic Number Theory. I translated the C# application to C++ in August 21-22, 2023. The C++ code uses the long long 64-bit signed integer data type. Below are some results. I also implemented Schoof’s point counting algorithm in C#.












/*
Author: Pate Williams (c) 1997
"Algorithm 2.3.2 (Image of a Matrix). Given an
m by n matrix M = (m[i][i]) with 1 <= i <= m and
1 <= j <= n having coefficients in the field K,
this algorithm outputs a basis of the image of
M, i. e. vector space spanned by the columns of
M." -Henri Cohen- See "A Course in Computational
Algebraic Number Theory" by Henri Cohen pages
58-59. We specialize the code to the field Zp.
*/
#include <stdio.h>
#include <stdlib.h>
long** create_matrix(long m, long n)
{
long i, ** matrix = (long**)calloc(m, sizeof(long*));
if (!matrix) {
fprintf(stderr, "fatal error\ninsufficient memory\n");
fprintf(stderr, "from create_matrix\n");
exit(1);
}
for (i = 0; i < m; i++) {
matrix[i] = (long*)calloc(n, sizeof(long));
if (!matrix[i]) {
fprintf(stderr, "fatal error\ninsufficient memory\n");
fprintf(stderr, "from create_matrix\n");
exit(1);
}
}
return matrix;
}
void delete_matrix(long m, long** matrix)
{
long i;
for (i = 0; i < m; i++) free(matrix[i]);
free(matrix);
}
void Euclid_extended(long a, long b, long* u,
long* v, long* d)
{
long q, t1, t3, v1, v3;
*u = 1, * d = a;
if (b == 0) {
*v = 0;
return;
}
v1 = 0, v3 = b;
#ifdef DEBUG
printf("----------------------------------\n");
printf(" q t3 *u *d t1 v1 v3\n");
printf("----------------------------------\n");
#endif
while (v3 != 0) {
q = *d / v3;
t3 = *d - q * v3;
t1 = *u - q * v1;
*u = v1, * d = v3;
#ifdef DEBUG
printf("%4ld %4ld %4ld ", q, t3, *u);
printf("%4ld %4ld %4ld %4ld\n", *d, t1, v1, v3);
#endif
v1 = t1, v3 = t3;
}
*v = (*d - a * *u) / b;
#ifdef DEBUG
printf("----------------------------------\n");
#endif
}
long inv(long number, long modulus)
{
long d, u, v;
Euclid_extended(number, modulus, &u, &v, &d);
if (d == 1) return u;
return 0;
}
void image(long m, long n, long p,
long** M, long** X, long* r)
{
int found;
long D, i, j, k, s;
long* c = (long*)calloc(m, sizeof(long));
long* d = (long*)calloc(n, sizeof(long));
long** N = create_matrix(m, n);
if (!c || !d) {
fprintf(stderr, "fatal error\ninsufficient memory\n");
fprintf(stderr, "from kernel\n");
exit(1);
}
for (i = 0; i < m; i++) {
c[i] = -1;
for (j = 0; j < n; j++) N[i][j] = M[i][j];
}
*r = 0;
for (k = 0; k < n; k++) {
found = 0, j = 0;
while (!found && j < m) {
found = M[j][k] != 0 && c[j] == -1;
if (!found) j++;
}
if (found) {
D = p - inv(M[j][k], p);
M[j][k] = p - 1;
for (s = k + 1; s < n; s++)
M[j][s] = (D * M[j][s]) % p;
for (i = 0; i < m; i++) {
if (i != j) {
D = M[i][k];
M[i][k] = 0;
for (s = k + 1; s < n; s++) {
M[i][s] = (M[i][s] + D * M[j][s]) % p;
if (M[i][s] < 0) M[i][s] += p;
}
}
}
c[j] = k;
d[k] = j;
}
else {
*r = *r + 1;
d[k] = -1;
}
}
for (j = 0; j < m; j++) {
if (c[j] != -1) {
for (i = 0; i < n; i++) {
if (i < m) X[i][j] = N[i][c[j]];
else X[i][j] = 0;
}
}
}
delete_matrix(m, N);
free(c);
free(d);
}
void print_matrix(long m, long n, long** a)
{
long i, j;
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++)
printf("%2ld ", a[i][j]);
printf("\n");
}
}
int main(void)
{
long i, j, m = 8, n = 8, p = 13, r;
long a[8][8] = { {0, 0, 0, 0, 0, 0, 0, 0},
{2, 0, 7, 11, 10, 12, 5, 11},
{3, 6, 3, 3, 0, 4, 7, 2},
{4, 3, 6, 4, 1, 6, 2, 3},
{2, 11, 8, 8, 2, 1, 3, 11},
{6, 11, 8, 6, 2, 6, 10, 9},
{5, 11, 7, 10, 0, 11, 6, 12},
{3, 3, 12, 5, 0, 11, 9, 11} };
long** M = create_matrix(m, n);
long** X = create_matrix(n, n);
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
M[i][j] = a[j][i];
printf("the original matrix is as follows:\n");
print_matrix(m, n, M);
image(m, n, p, M, X, &r);
printf("the image of the matrix is as follows:\n");
print_matrix(n, n - r, X);
printf("the rank of the matrix is: %ld\n", n - r);
delete_matrix(m, M);
delete_matrix(n, X);
return 0;
}

/*
Author: Pate Williams (c) 1997
"Algorithm 2.3.5 (Inverse Image Matrix). Let M be
an m by n matrix and V be an m by r matrix, where
n <= m. This algorithm either outputs a message
saying that some column vector of V is not in the
image of M, or outputs an n by r matrix X such
that V = MX." -Henri Cohen- See "A Course in Com-
putational Algebraic Number Theory" by Henri
Cohen pages 60-61. We specialize to the field Q.
*/
#include <stdio.h>
#include <stdlib.h>
double** create_matrix(long m, long n)
{
double** matrix = (double**)calloc(m, sizeof(double*));
long i;
if (!matrix) {
fprintf(stderr, "fatal error\ninsufficient memory\n");
fprintf(stderr, "from create_matrix\n");
exit(1);
}
for (i = 0; i < m; i++) {
matrix[i] = (double*)calloc(n, sizeof(double));
if (!matrix[i]) {
fprintf(stderr, "fatal error\ninsufficient memory\n");
fprintf(stderr, "from create_matrix\n");
exit(1);
}
}
return matrix;
}
void delete_matrix(long m, double** matrix)
{
long i;
for (i = 0; i < m; i++) free(matrix[i]);
free(matrix);
}
void inverse_image_matrix(long m, long n, long r,
double** M, double** V,
double** X)
{
double ck, d, sum, t;
double** B = create_matrix(m, r);
int found;
long i, j, k, l;
for (i = 0; i < m; i++)
for (j = 0; j < r; j++)
B[i][j] = V[i][j];
for (j = 0; j < n; j++) {
found = 0, i = j;
while (!found && i < m) {
found = M[i][j] != 0;
if (!found) i++;
}
if (!found) {
fprintf(stderr, "fatal error\nnot linearly independent\n");
fprintf(stderr, "from inverse_image_matrix\n");
exit(1);
}
if (i > j) {
for (l = 0; l < n; l++)
t = M[i][l], M[i][l] = M[j][l], M[j][l] = t;
for (l = 0; l < r; l++)
t = B[i][l], B[i][l] = B[j][l], B[j][l] = t;
}
d = 1.0 / M[j][j];
for (k = j + 1; k < m; k++) {
ck = d * M[k][j];
for (l = j + 1; l < n; l++)
M[k][l] -= ck * M[j][l];
for (l = 0; l < r; l++)
B[k][l] -= ck * B[j][l];
}
}
for (i = n - 1; i >= 0; i--) {
for (k = 0; k < r; k++) {
sum = 0;
for (j = i + 1; j < n; j++)
sum += M[i][j] * X[j][k];
X[i][k] = (B[i][k] - sum) / M[i][i];
}
}
for (k = n + 1; k < m; k++) {
for (j = 0; j < r; j++) {
sum = 0;
for (i = 0; i < n; i++)
sum += M[k][i] * X[i][j];
if (sum != B[k][j]) {
fprintf(stderr, "fatal error\ncolumn not in image\n");
fprintf(stderr, "from inverse_image_matrix\n");
exit(1);
}
}
}
delete_matrix(m, B);
}
void matrix_multiply(long m, long n, long r,
double** a, double** b,
double** c)
/* c = a * b */
{
double sum;
long i, j, k;
for (i = 0; i < m; i++) {
for (j = 0; j < r; j++) {
sum = 0.0;
for (k = 0; k < n; k++)
sum += a[i][k] * b[k][j];
c[i][j] = sum;
}
}
}
void print_matrix(long m, long n, double** a)
{
long i, j;
for (i = 0; i < m; i++) {
for (j = 0; j < n; j++)
printf("%+10.6lf ", a[i][j]);
printf("\n");
}
}
int main(void)
{
long i, j, m = 4, n = 4, r = 4;
double** c = create_matrix(m, n);
double** M = create_matrix(m, n);
double** V = create_matrix(m, r);
double** X = create_matrix(n, r);
for (i = 0; i < m; i++) {
c[i][i] = M[i][i] = 2.0;
if (i > 0)
c[i][i - 1] = M[i][i - 1] = -1.0;
if (i < m - 1)
c[i][i + 1] = M[i][i + 1] = -1.0;
}
for (i = 0; i < m; i++)
for (j = 0; j < r; j++)
V[i][j] = i + j + 1;
printf("M is as follows:\n");
print_matrix(m, n, M);
printf("V is as follows:\n");
print_matrix(m, r, V);
inverse_image_matrix(m, n, r, M, V, X);
printf("X is as follows:\n");
print_matrix(n, r, X);
matrix_multiply(m, n, r, c, X, M);
printf("MX is as follows:\n");
print_matrix(m, r, M);
delete_matrix(m, c);
delete_matrix(m, M);
delete_matrix(m, V);
delete_matrix(n, X);
return 0;
}

A few years ago, I implemented the LLL lattice reduction algorithm found in two references: “Handbook of Applied Cryptography” Edited by Alfred J. Menezes et al and “A Course in Computational Algebraic Number Theory” by Henri Cohen. My new implementation in C# uses 64-bit integers and BigIntegers. Here are some results.




27-Decimal Digit Number 123456789012345678901234567


31-Digit Decimal Number 1234567890123456789012345678901 Pollard Failed


The same factorizations by the Classical Shor-Pollard- James Pate Williams, Jr. algorithm.
