I have implemented the algorithm according to this paper it's working well, but for certain tests, it doesn't get the shortest path,
here's the pseudo-code
Initialize
For t=1 to iteration number do
For k=1 to l do
Repeat until ant k has completed a tour
Select the city j to be visited next
With probability pij given by Eq. (1)
Calculate Lk
Update the trail levels according to Eqs. (2-4).
End
here's the code
import java.io.*;
import java.util.*;
import static java.lang.Math.*;
public class test2 {
private InputReader cin;
private PrintWriter cout;
double pheromones[][];
double distances[][];
double visibility[][];
static int n;
City[] city;
Ant[] ant;
int m;
int T;
double alpha = 1; // pheromone importance
double beta = 2; // visibility importance
double evaporation = 0.1;
double Q = 100.0;
static class City {
double x, y;
int id;
public City(double x, double y, int id) {
this.x = x;
this.y = y;
this.id = id;
}
}
static class Ant {
int whereAmI;
boolean[] visited;
double tourDistance;
LinkedList<Integer> citiesVisitedInOrder;
int cityEdges[][];
Ant(int whereAmI) {
this.whereAmI = whereAmI;
visited = new boolean[n + 1];
cityEdges = new int[n + 1][n + 1];
reset();
}
void reset() {
Arrays.fill(visited, false);
visited[whereAmI] = true;
for (int i = 1; i <= n; i++)
Arrays.fill(cityEdges[i], 0);
tourDistance = 0;
citiesVisitedInOrder = new LinkedList<>();
citiesVisitedInOrder.addLast(whereAmI);
}
}
//the actual algorithm iteration
/*
Initialize
For t=1 to iteration number do
For k=1 to l do
Repeat until ant k has completed a tour
Select the city j to be visited next
With probability pij given by Eq. (1)
Calculate Lk
Update the trail levels according to Eqs. (2-4).
End
*/
private void solve() {
n = cin.readInt();
initializeParameter();
//the main loop
for (int t = 0; t < T; t++) {
for (int i = 0; i < m; i++) {//for each ant
Ant current = ant[i];
for (int j = 0; j < n; j++) {//for each city
int currentAntsCity = current.whereAmI;
double highestProbability = 0;
int cityId = 1;
double sumNotiation = calculateSum(current.visited, currentAntsCity);
//traverse all non-visited cities and choose the best
boolean good = false;
for (int c = 1; c <= n; c++) {//remove the equal
if (!current.visited[c]) {
double prop = (pow(pheromones[currentAntsCity][c], alpha) * pow(visibility[currentAntsCity][c], beta))
/ sumNotiation;
if (prop >= highestProbability) {
highestProbability = prop;
cityId = c;
good = true;
}
}
}
if (good) {
current.tourDistance += distances[currentAntsCity][cityId];
current.cityEdges[currentAntsCity][cityId] = current.cityEdges[cityId][currentAntsCity] = 1;
current.citiesVisitedInOrder.addLast(cityId);
current.whereAmI = cityId;
current.visited[cityId] = true;
}
}//after n iteration i ant completes a tour
current.tourDistance += distances[current.citiesVisitedInOrder.getFirst()][current.citiesVisitedInOrder.getLast()];
}//update
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
double deltaPhermons = 0;
for (int a = 0; a < m; a++) {
if (ant[a].cityEdges[i][j] != 0) {
deltaPhermons += Q / ant[a].tourDistance;
}
}
pheromones[i][j] = pheromones[j][i] = pheromones[i][j] * evaporation + deltaPhermons;
pheromones[i][i] = 0;
}
}
if (t == T - 1)
break;
//reset everything
for (int i = 0; i < m; i++) {
ant[i].reset();
}
}
//get the best ant route
double minDistance = Double.MAX_VALUE;
LinkedList<Integer> minRout = new LinkedList<>();
for (Ant ant : ant) {
if (ant.tourDistance < minDistance) {
minDistance = ant.tourDistance;
minRout = ant.citiesVisitedInOrder;
}
}
cout.println(minDistance);
for (int element : minRout)
cout.print(element + " ");
}
private double calculateSum(boolean[] visited, int currentAntsCity) {
//traverse all non-visited cities
double ans = 0.0;
for (int c = 1; c <= n; c++) {
if (!visited[c]) {
ans +=
pow(pheromones[currentAntsCity][c], alpha) *
pow(visibility[currentAntsCity][c], beta);
}
}
return ans;
}
private void initializeParameter() {
m = 2 * n;
T = 4 * m;
city = new City[n + 1];
pheromones = new double[n + 1][n + 1];
distances = new double[n + 1][n + 1];
visibility = new double[n + 1][n + 1];
//read cities coordinates
for (int i = 1; i <= n; i++) {
city[i] = new City(cin.readDouble(), cin.readDouble(), i);
}
//initialize distances
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
distances[i][j] = distances[j][i] = sqrt(pow(city[i].x -
city[j].x, 2.0) + pow(city[i].y -
city[j].y, 2.0));
}
}
//initialize the pheromones
double pheromones0 = 1.0 / (double) n;
for (int i = 1; i <= n; i++) {
Arrays.fill(pheromones[i], pheromones0);
pheromones[i][i] = 0;
}
//initialize the visibility
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
visibility[i][j] = visibility[j][i] = 1.0 / distances[i][j];
}
}
//initialize the ants
ant = new Ant[m];
Random rand = new Random(); //instance of random class for
for (int i = 0; i < m; i++) {
int random_int = rand.nextInt(n) + 1;
ant[i] = new Ant(random_int);
}
}
public static void main(String args[]) {
new test2().run();
}
private void run() {
// cin = new InputReader(System.in);
// cout = new PrintWriter(System.out);
try {
cin = new InputReader(new FileInputStream("input.txt"));
cout = new PrintWriter(new FileOutputStream("output.txt"));
} catch (FileNotFoundException e) {
//System.out.println(e.toString());
}
solve();
cout.close();
}
//just for faster reading from a file
public static class InputReader {
private InputStream stream;
private byte[] buf = new byte[1024];
private int curChar, numChars;
public InputReader(InputStream stream) {
this.stream = stream;
}
public int read() {
if (numChars == -1)
throw new InputMismatchException();
if (curChar >= numChars) {
curChar = 0;
try {
numChars = stream.read(buf);
} catch (IOException e) {
throw new InputMismatchException();
}
if (numChars <= 0)
return -1;
}
return buf[curChar++];
}
public int readInt() {
int c = read();
while (isSpaceChar(c))
c = read();
int sgn = 1;
if (c == '-') {
sgn = -1;
c = read();
}
int res = 0;
do {
if (c < '0' || c > '9')
throw new InputMismatchException();
res *= 10;
res += c - '0';
c = read();
} while (!isSpaceChar(c));
return res * sgn;
}
public double readDouble() {
int c = read();
while (isSpaceChar(c))
c = read();
int sgn = 1;
if (c == '-') {
sgn = -1;
c = read();
}
double res = 0;
while (!isSpaceChar(c) && c != '.') {
if (c == 'e' || c == 'E')
return res * pow(10, readInt());
if (c < '0' || c > '9')
throw new InputMismatchException();
res *= 10;
res += c - '0';
c = read();
}
if (c == '.') {
c = read();
double m = 1;
while (!isSpaceChar(c)) {
if (c == 'e' || c == 'E')
return res * pow(10, readInt());
if (c < '0' || c > '9')
throw new InputMismatchException();
m /= 10;
res += (c - '0') * m;
c = read();
}
}
return res * sgn;
}
private boolean isSpaceChar(int c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
}
}
}
the test case
10
-15 89
-5 -49
-35 -18
7 49
-95 -68
85 -39
53 -1
69 -99
-74 8
-52 -35
the right answer:
615.11811789868988853
1 9 5 10 3 2 8 6 7 4
my coes's output:
685.2134200307595
5 9 10 3 2 8 6 7 4 1
as you can notice I am not getting the shortest path, I believe that the mistake is somewhere in the constant, and the probability comparing!
the formula I have implemented
and the update formulas
how can I improve the algorithm accuracy? or maybe there's something wrong in my implementation!
I found the solution, Changing the Update functions after each complete tour for all ants, fixed the problem:
#part 1
//the elaboration presses after a complete tour for all ants
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
pheromones[i][j] *= evaporation;
if (pheromones[i][j] < 1.0 / (double) n)//notice it the phermones can't be less than the starting value`
pheromones[i][j] = 1.0 / (double) n;
pheromones[i][i] = 0;
}
}
#part 2
//update the phermonses
for (int i = 0; i < m; i++) {
for (int j = i + 1; j <= n; j++) {
int from = ant[i].rout[0];
int to = ant[i].rout[n - 1];
pheromones[from][to] += Q / ant[i].tourDistance;
pheromones[to][from] = pheromones[from][to];
}
}
source HERE
strangely enough the algorithm can work without the elaboration presses i.e. #part1.
Anyway here's the complete code with little changes
import java.io.*;
import java.util.*;
import static java.lang.Math.*;
public class test2 {
private InputReader cin;
private PrintWriter cout;
double[][] arr;
double[][] pheromones;
double[][] distances;
double[][] visibility;
static int n;
Ant[] ant;
int m;
int T;
double alpha = 1; // pheromone importance
double beta = 3; // visibility importance
double evaporation = 0.9;
double Q = 40.0;
double pheromones0;
static class Ant {
int whereAmI;
boolean[] visited;
double tourDistance;
private int ctr; //counter for the cites thought which the ant pass
private int[] route;
Ant(int whereAmI) {
this.whereAmI = whereAmI;
reset();
}
void reset() {
ctr = 0;
route = new int[n];
visited = new boolean[n + 1];
visited[whereAmI] = true;
tourDistance = 0;
addCityToTheRoute(whereAmI);
}
void addCityToTheRoute(int cityId) {
route[ctr++] = cityId;
}
}
private void solve() {
n = cin.readInt();
initializeParameter();
double mi = Double.MAX_VALUE;
int[] cityRoute = new int[n];
//the main loop
for (int t = 0; t < T; t++) {
for (int i = 0; i < m; i++) {//for each ant
for (int j = 0; j < n; j++) {//for each city
double highestProbability = 0;
int cityId = 1;
double sumNotiation = calculateSum(ant[i].visited, ant[i].whereAmI);
//traverse all non-visited cities and choose the best
boolean good = false;
for (int c = 1; c <= n; c++) {//remove the equal
if (!ant[i].visited[c]) {
double prop = (pow(pheromones[ant[i].whereAmI][c], alpha) * pow(visibility[ant[i].whereAmI][c], beta))
/ sumNotiation;
if (prop >= highestProbability) {
highestProbability = prop;
cityId = c;
good = true;
}
}
}
if (good) {
ant[i].tourDistance += distances[ant[i].whereAmI][cityId];
ant[i].addCityToTheRoute(cityId);
ant[i].whereAmI = cityId;
ant[i].visited[cityId] = true;
}
}//after n iteration i ant completes a tour
ant[i].tourDistance += distances[ant[i].route[0]][ant[i].route[n - 1]];//add the distance from the last city to the first city
//while k ant finishes its tour take the best ant's route so far
if (ant[i].tourDistance < mi) {
mi = ant[i].tourDistance;
cityRoute = ant[i].route;
}
}
//update
//evaporatePheromones
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
pheromones[i][j] *= evaporation;
if (pheromones[i][j] < pheromones0)
pheromones[i][j] = pheromones0;
pheromones[i][i] = 0;
}
}
//updatePheromones
for (int i = 0; i < m; i++) {
for (int j = i + 1; j <= n; j++) {
int from = ant[i].route[0];
int to = ant[i].route[n - 1];
pheromones[from][to] += Q / ant[i].tourDistance;
pheromones[to][from] = pheromones[from][to];
}
}
if (t == T - 1)
break;
//reset everything
for (int i = 0; i < m; i++) {
ant[i].reset();
}
}
//print the route with the distance
cout.println(mi);
for (int element : cityRoute)
cout.print(element + " ");
}
private double calculateSum(boolean[] visited, int currentAntsCity) {
//traverse all non-visited cities
double ans = 0.0;
for (int c = 1; c <= n; c++) {
if (!visited[c]) {
ans +=
pow(pheromones[currentAntsCity][c], alpha) *
pow(visibility[currentAntsCity][c], beta);
}
}
return ans;
}
private void initializeParameter() {
m = 20 * n;
T = 20;
pheromones = new double[n + 1][n + 1];
distances = new double[n + 1][n + 1];
visibility = new double[n + 1][n + 1];
//read cities coordinates
arr = new double[n + 1][2];
for (int i = 1; i <= n; i++) {
double x = cin.readDouble();
double y = cin.readDouble();
arr[i][0] = x;
arr[i][1] = y;
}
//initialize distances
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
distances[i][j] = distances[j][i] = sqrt(pow(arr[i][0] -
arr[j][0], 2.0) + pow(arr[i][1] -
arr[j][1], 2.0));
}
}
//initialize the pheromones
pheromones0 = 1.0 / (double) n;
for (int i = 1; i <= n; i++) {
Arrays.fill(pheromones[i], pheromones0);
pheromones[i][i] = 0;
}
//initialize the visibility
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
visibility[i][j] = visibility[j][i] = 1.0 / distances[i][j];
}
}
//initialize the ants
ant = new Ant[m];
Random rand = new Random(); //instance of random class for
for (int i = 0; i < m; i++) {
int random_int = rand.nextInt(n) + 1;
ant[i] = new Ant(random_int);
}
}
public static void main(String args[]) {
new test2().run();
}
private void run() {
// cin = new InputReader(System.in);
// cout = new PrintWriter(System.out);
try {
cin = new InputReader(new FileInputStream("input.txt"));
cout = new PrintWriter(new FileOutputStream("output.txt"));
} catch (FileNotFoundException e) {
//System.out.println(e.toString());
}
solve();
cout.close();
}
//just for faster reading from a file
public static class InputReader {
private InputStream stream;
private byte[] buf = new byte[1024];
private int curChar, numChars;
public InputReader(InputStream stream) {
this.stream = stream;
}
public int read() {
if (numChars == -1)
throw new InputMismatchException();
if (curChar >= numChars) {
curChar = 0;
try {
numChars = stream.read(buf);
} catch (IOException e) {
throw new InputMismatchException();
}
if (numChars <= 0)
return -1;
}
return buf[curChar++];
}
public int readInt() {
int c = read();
while (isSpaceChar(c))
c = read();
int sgn = 1;
if (c == '-') {
sgn = -1;
c = read();
}
int res = 0;
do {
if (c < '0' || c > '9')
throw new InputMismatchException();
res *= 10;
res += c - '0';
c = read();
} while (!isSpaceChar(c));
return res * sgn;
}
public double readDouble() {
int c = read();
while (isSpaceChar(c))
c = read();
int sgn = 1;
if (c == '-') {
sgn = -1;
c = read();
}
double res = 0;
while (!isSpaceChar(c) && c != '.') {
if (c == 'e' || c == 'E')
return res * pow(10, readInt());
if (c < '0' || c > '9')
throw new InputMismatchException();
res *= 10;
res += c - '0';
c = read();
}
if (c == '.') {
c = read();
double m = 1;
while (!isSpaceChar(c)) {
if (c == 'e' || c == 'E')
return res * pow(10, readInt());
if (c < '0' || c > '9')
throw new InputMismatchException();
m /= 10;
res += (c - '0') * m;
c = read();
}
}
return res * sgn;
}
private boolean isSpaceChar(int c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
}
}
}
Here is javascript code named Barrett. It can do RSA encryption and I put it into code snippet:
/*
* RSA, a suite of routines for performing RSA public-key computations in JavaScript.
* Copyright 1998-2005 David Shapiro.
* Dave Shapiro
* dave#ohdave.com
* changed by Fuchun, 2010-05-06
* fcrpg2005#gmail.com
*/
(function($w) {
if(typeof $w.RSAUtils === 'undefined')
var RSAUtils = $w.RSAUtils = {};
var biRadixBase = 2;
var biRadixBits = 16;
var bitsPerDigit = biRadixBits;
var biRadix = 1 << 16; // = 2^16 = 65536
var biHalfRadix = biRadix >>> 1;
var biRadixSquared = biRadix * biRadix;
var maxDigitVal = biRadix - 1;
var maxInteger = 9999999999999998;
//maxDigits:
//Change this to accommodate your largest number size. Use setMaxDigits()
//to change it!
//
//In general, if you're working with numbers of size N bits, you'll need 2*N
//bits of storage. Each digit holds 16 bits. So, a 1024-bit key will need
//
//1024 * 2 / 16 = 128 digits of storage.
//
var maxDigits;
var ZERO_ARRAY;
var bigZero, bigOne;
var BigInt = $w.BigInt = function(flag) {
if (typeof flag == "boolean" && flag == true) {
this.digits = null;
} else {
this.digits = ZERO_ARRAY.slice(0);
}
this.isNeg = false;
};
RSAUtils.setMaxDigits = function(value) {
maxDigits = value;
ZERO_ARRAY = new Array(maxDigits);
for (var iza = 0; iza < ZERO_ARRAY.length; iza++) ZERO_ARRAY[iza] = 0;
bigZero = new BigInt();
bigOne = new BigInt();
bigOne.digits[0] = 1;
};
RSAUtils.setMaxDigits(20);
//The maximum number of digits in base 10 you can convert to an
//integer without JavaScript throwing up on you.
var dpl10 = 15;
RSAUtils.biFromNumber = function(i) {
var result = new BigInt();
result.isNeg = i < 0;
i = Math.abs(i);
var j = 0;
while (i > 0) {
result.digits[j++] = i & maxDigitVal;
i = Math.floor(i / biRadix);
}
return result;
};
//lr10 = 10 ^ dpl10
var lr10 = RSAUtils.biFromNumber(1000000000000000);
RSAUtils.biFromDecimal = function(s) {
var isNeg = s.charAt(0) == '-';
var i = isNeg ? 1 : 0;
var result;
// Skip leading zeros.
while (i < s.length && s.charAt(i) == '0') ++i;
if (i == s.length) {
result = new BigInt();
}
else {
var digitCount = s.length - i;
var fgl = digitCount % dpl10;
if (fgl == 0) fgl = dpl10;
result = RSAUtils.biFromNumber(Number(s.substr(i, fgl)));
i += fgl;
while (i < s.length) {
result = RSAUtils.biAdd(RSAUtils.biMultiply(result, lr10),
RSAUtils.biFromNumber(Number(s.substr(i, dpl10))));
i += dpl10;
}
result.isNeg = isNeg;
}
return result;
};
RSAUtils.biCopy = function(bi) {
var result = new BigInt(true);
result.digits = bi.digits.slice(0);
result.isNeg = bi.isNeg;
return result;
};
RSAUtils.reverseStr = function(s) {
var result = "";
for (var i = s.length - 1; i > -1; --i) {
result += s.charAt(i);
}
return result;
};
var hexatrigesimalToChar = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z'
];
RSAUtils.biToString = function(x, radix) { // 2 <= radix <= 36
var b = new BigInt();
b.digits[0] = radix;
var qr = RSAUtils.biDivideModulo(x, b);
var result = hexatrigesimalToChar[qr[1].digits[0]];
while (RSAUtils.biCompare(qr[0], bigZero) == 1) {
qr = RSAUtils.biDivideModulo(qr[0], b);
digit = qr[1].digits[0];
result += hexatrigesimalToChar[qr[1].digits[0]];
}
return (x.isNeg ? "-" : "") + RSAUtils.reverseStr(result);
};
RSAUtils.biToDecimal = function(x) {
var b = new BigInt();
b.digits[0] = 10;
var qr = RSAUtils.biDivideModulo(x, b);
var result = String(qr[1].digits[0]);
while (RSAUtils.biCompare(qr[0], bigZero) == 1) {
qr = RSAUtils.biDivideModulo(qr[0], b);
result += String(qr[1].digits[0]);
}
return (x.isNeg ? "-" : "") + RSAUtils.reverseStr(result);
};
var hexToChar = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'];
RSAUtils.digitToHex = function(n) {
var mask = 0xf;
var result = "";
for (i = 0; i < 4; ++i) {
result += hexToChar[n & mask];
n >>>= 4;
}
return RSAUtils.reverseStr(result);
};
RSAUtils.biToHex = function(x) {
var result = "";
var n = RSAUtils.biHighIndex(x);
for (var i = RSAUtils.biHighIndex(x); i > -1; --i) {
result += RSAUtils.digitToHex(x.digits[i]);
}
return result;
};
RSAUtils.charToHex = function(c) {
var ZERO = 48;
var NINE = ZERO + 9;
var littleA = 97;
var littleZ = littleA + 25;
var bigA = 65;
var bigZ = 65 + 25;
var result;
if (c >= ZERO && c <= NINE) {
result = c - ZERO;
} else if (c >= bigA && c <= bigZ) {
result = 10 + c - bigA;
} else if (c >= littleA && c <= littleZ) {
result = 10 + c - littleA;
} else {
result = 0;
}
return result;
};
RSAUtils.hexToDigit = function(s) {
var result = 0;
var sl = Math.min(s.length, 4);
for (var i = 0; i < sl; ++i) {
result <<= 4;
result |= RSAUtils.charToHex(s.charCodeAt(i));
}
return result;
};
RSAUtils.biFromHex = function(s) {
var result = new BigInt();
var sl = s.length;
for (var i = sl, j = 0; i > 0; i -= 4, ++j) {
result.digits[j] = RSAUtils.hexToDigit(s.substr(Math.max(i - 4, 0), Math.min(i, 4)));
}
return result;
};
RSAUtils.biFromString = function(s, radix) {
var isNeg = s.charAt(0) == '-';
var istop = isNeg ? 1 : 0;
var result = new BigInt();
var place = new BigInt();
place.digits[0] = 1; // radix^0
for (var i = s.length - 1; i >= istop; i--) {
var c = s.charCodeAt(i);
var digit = RSAUtils.charToHex(c);
var biDigit = RSAUtils.biMultiplyDigit(place, digit);
result = RSAUtils.biAdd(result, biDigit);
place = RSAUtils.biMultiplyDigit(place, radix);
}
result.isNeg = isNeg;
return result;
};
RSAUtils.biDump = function(b) {
return (b.isNeg ? "-" : "") + b.digits.join(" ");
};
RSAUtils.biAdd = function(x, y) {
var result;
if (x.isNeg != y.isNeg) {
y.isNeg = !y.isNeg;
result = RSAUtils.biSubtract(x, y);
y.isNeg = !y.isNeg;
}
else {
result = new BigInt();
var c = 0;
var n;
for (var i = 0; i < x.digits.length; ++i) {
n = x.digits[i] + y.digits[i] + c;
result.digits[i] = n % biRadix;
c = Number(n >= biRadix);
}
result.isNeg = x.isNeg;
}
return result;
};
RSAUtils.biSubtract = function(x, y) {
var result;
if (x.isNeg != y.isNeg) {
y.isNeg = !y.isNeg;
result = RSAUtils.biAdd(x, y);
y.isNeg = !y.isNeg;
} else {
result = new BigInt();
var n, c;
c = 0;
for (var i = 0; i < x.digits.length; ++i) {
n = x.digits[i] - y.digits[i] + c;
result.digits[i] = n % biRadix;
// Stupid non-conforming modulus operation.
if (result.digits[i] < 0) result.digits[i] += biRadix;
c = 0 - Number(n < 0);
}
// Fix up the negative sign, if any.
if (c == -1) {
c = 0;
for (var i = 0; i < x.digits.length; ++i) {
n = 0 - result.digits[i] + c;
result.digits[i] = n % biRadix;
// Stupid non-conforming modulus operation.
if (result.digits[i] < 0) result.digits[i] += biRadix;
c = 0 - Number(n < 0);
}
// Result is opposite sign of arguments.
result.isNeg = !x.isNeg;
} else {
// Result is same sign.
result.isNeg = x.isNeg;
}
}
return result;
};
RSAUtils.biHighIndex = function(x) {
var result = x.digits.length - 1;
while (result > 0 && x.digits[result] == 0) --result;
return result;
};
RSAUtils.biNumBits = function(x) {
var n = RSAUtils.biHighIndex(x);
var d = x.digits[n];
var m = (n + 1) * bitsPerDigit;
var result;
for (result = m; result > m - bitsPerDigit; --result) {
if ((d & 0x8000) != 0) break;
d <<= 1;
}
return result;
};
RSAUtils.biMultiply = function(x, y) {
var result = new BigInt();
var c;
var n = RSAUtils.biHighIndex(x);
var t = RSAUtils.biHighIndex(y);
var u, uv, k;
for (var i = 0; i <= t; ++i) {
c = 0;
k = i;
for (j = 0; j <= n; ++j, ++k) {
uv = result.digits[k] + x.digits[j] * y.digits[i] + c;
result.digits[k] = uv & maxDigitVal;
c = uv >>> biRadixBits;
//c = Math.floor(uv / biRadix);
}
result.digits[i + n + 1] = c;
}
// Someone give me a logical xor, please.
result.isNeg = x.isNeg != y.isNeg;
return result;
};
RSAUtils.biMultiplyDigit = function(x, y) {
var n, c, uv;
result = new BigInt();
n = RSAUtils.biHighIndex(x);
c = 0;
for (var j = 0; j <= n; ++j) {
uv = result.digits[j] + x.digits[j] * y + c;
result.digits[j] = uv & maxDigitVal;
c = uv >>> biRadixBits;
//c = Math.floor(uv / biRadix);
}
result.digits[1 + n] = c;
return result;
};
RSAUtils.arrayCopy = function(src, srcStart, dest, destStart, n) {
var m = Math.min(srcStart + n, src.length);
for (var i = srcStart, j = destStart; i < m; ++i, ++j) {
dest[j] = src[i];
}
};
var highBitMasks = [0x0000, 0x8000, 0xC000, 0xE000, 0xF000, 0xF800,
0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0,
0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF];
RSAUtils.biShiftLeft = function(x, n) {
var digitCount = Math.floor(n / bitsPerDigit);
var result = new BigInt();
RSAUtils.arrayCopy(x.digits, 0, result.digits, digitCount,
result.digits.length - digitCount);
var bits = n % bitsPerDigit;
var rightBits = bitsPerDigit - bits;
for (var i = result.digits.length - 1, i1 = i - 1; i > 0; --i, --i1) {
result.digits[i] = ((result.digits[i] << bits) & maxDigitVal) |
((result.digits[i1] & highBitMasks[bits]) >>>
(rightBits));
}
result.digits[0] = ((result.digits[i] << bits) & maxDigitVal);
result.isNeg = x.isNeg;
return result;
};
var lowBitMasks = [0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F,
0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF,
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF];
RSAUtils.biShiftRight = function(x, n) {
var digitCount = Math.floor(n / bitsPerDigit);
var result = new BigInt();
RSAUtils.arrayCopy(x.digits, digitCount, result.digits, 0,
x.digits.length - digitCount);
var bits = n % bitsPerDigit;
var leftBits = bitsPerDigit - bits;
for (var i = 0, i1 = i + 1; i < result.digits.length - 1; ++i, ++i1) {
result.digits[i] = (result.digits[i] >>> bits) |
((result.digits[i1] & lowBitMasks[bits]) << leftBits);
}
result.digits[result.digits.length - 1] >>>= bits;
result.isNeg = x.isNeg;
return result;
};
RSAUtils.biMultiplyByRadixPower = function(x, n) {
var result = new BigInt();
RSAUtils.arrayCopy(x.digits, 0, result.digits, n, result.digits.length - n);
return result;
};
RSAUtils.biDivideByRadixPower = function(x, n) {
var result = new BigInt();
RSAUtils.arrayCopy(x.digits, n, result.digits, 0, result.digits.length - n);
return result;
};
RSAUtils.biModuloByRadixPower = function(x, n) {
var result = new BigInt();
RSAUtils.arrayCopy(x.digits, 0, result.digits, 0, n);
return result;
};
RSAUtils.biCompare = function(x, y) {
if (x.isNeg != y.isNeg) {
return 1 - 2 * Number(x.isNeg);
}
for (var i = x.digits.length - 1; i >= 0; --i) {
if (x.digits[i] != y.digits[i]) {
if (x.isNeg) {
return 1 - 2 * Number(x.digits[i] > y.digits[i]);
} else {
return 1 - 2 * Number(x.digits[i] < y.digits[i]);
}
}
}
return 0;
};
RSAUtils.biDivideModulo = function(x, y) {
var nb = RSAUtils.biNumBits(x);
var tb = RSAUtils.biNumBits(y);
var origYIsNeg = y.isNeg;
var q, r;
if (nb < tb) {
// |x| < |y|
if (x.isNeg) {
q = RSAUtils.biCopy(bigOne);
q.isNeg = !y.isNeg;
x.isNeg = false;
y.isNeg = false;
r = biSubtract(y, x);
// Restore signs, 'cause they're references.
x.isNeg = true;
y.isNeg = origYIsNeg;
} else {
q = new BigInt();
r = RSAUtils.biCopy(x);
}
return [q, r];
}
q = new BigInt();
r = x;
// Normalize Y.
var t = Math.ceil(tb / bitsPerDigit) - 1;
var lambda = 0;
while (y.digits[t] < biHalfRadix) {
y = RSAUtils.biShiftLeft(y, 1);
++lambda;
++tb;
t = Math.ceil(tb / bitsPerDigit) - 1;
}
// Shift r over to keep the quotient constant. We'll shift the
// remainder back at the end.
r = RSAUtils.biShiftLeft(r, lambda);
nb += lambda; // Update the bit count for x.
var n = Math.ceil(nb / bitsPerDigit) - 1;
var b = RSAUtils.biMultiplyByRadixPower(y, n - t);
while (RSAUtils.biCompare(r, b) != -1) {
++q.digits[n - t];
r = RSAUtils.biSubtract(r, b);
}
for (var i = n; i > t; --i) {
var ri = (i >= r.digits.length) ? 0 : r.digits[i];
var ri1 = (i - 1 >= r.digits.length) ? 0 : r.digits[i - 1];
var ri2 = (i - 2 >= r.digits.length) ? 0 : r.digits[i - 2];
var yt = (t >= y.digits.length) ? 0 : y.digits[t];
var yt1 = (t - 1 >= y.digits.length) ? 0 : y.digits[t - 1];
if (ri == yt) {
q.digits[i - t - 1] = maxDigitVal;
} else {
q.digits[i - t - 1] = Math.floor((ri * biRadix + ri1) / yt);
}
var c1 = q.digits[i - t - 1] * ((yt * biRadix) + yt1);
var c2 = (ri * biRadixSquared) + ((ri1 * biRadix) + ri2);
while (c1 > c2) {
--q.digits[i - t - 1];
c1 = q.digits[i - t - 1] * ((yt * biRadix) | yt1);
c2 = (ri * biRadix * biRadix) + ((ri1 * biRadix) + ri2);
}
b = RSAUtils.biMultiplyByRadixPower(y, i - t - 1);
r = RSAUtils.biSubtract(r, RSAUtils.biMultiplyDigit(b, q.digits[i - t - 1]));
if (r.isNeg) {
r = RSAUtils.biAdd(r, b);
--q.digits[i - t - 1];
}
}
r = RSAUtils.biShiftRight(r, lambda);
// Fiddle with the signs and stuff to make sure that 0 <= r < y.
q.isNeg = x.isNeg != origYIsNeg;
if (x.isNeg) {
if (origYIsNeg) {
q = RSAUtils.biAdd(q, bigOne);
} else {
q = RSAUtils.biSubtract(q, bigOne);
}
y = RSAUtils.biShiftRight(y, lambda);
r = RSAUtils.biSubtract(y, r);
}
// Check for the unbelievably stupid degenerate case of r == -0.
if (r.digits[0] == 0 && RSAUtils.biHighIndex(r) == 0) r.isNeg = false;
return [q, r];
};
RSAUtils.biDivide = function(x, y) {
return RSAUtils.biDivideModulo(x, y)[0];
};
RSAUtils.biModulo = function(x, y) {
return RSAUtils.biDivideModulo(x, y)[1];
};
RSAUtils.biMultiplyMod = function(x, y, m) {
return RSAUtils.biModulo(RSAUtils.biMultiply(x, y), m);
};
RSAUtils.biPow = function(x, y) {
var result = bigOne;
var a = x;
while (true) {
if ((y & 1) != 0) result = RSAUtils.biMultiply(result, a);
y >>= 1;
if (y == 0) break;
a = RSAUtils.biMultiply(a, a);
}
return result;
};
RSAUtils.biPowMod = function(x, y, m) {
var result = bigOne;
var a = x;
var k = y;
while (true) {
if ((k.digits[0] & 1) != 0) result = RSAUtils.biMultiplyMod(result, a, m);
k = RSAUtils.biShiftRight(k, 1);
if (k.digits[0] == 0 && RSAUtils.biHighIndex(k) == 0) break;
a = RSAUtils.biMultiplyMod(a, a, m);
}
return result;
};
$w.BarrettMu = function(m) {
this.modulus = RSAUtils.biCopy(m);
this.k = RSAUtils.biHighIndex(this.modulus) + 1;
var b2k = new BigInt();
b2k.digits[2 * this.k] = 1; // b2k = b^(2k)
this.mu = RSAUtils.biDivide(b2k, this.modulus);
this.bkplus1 = new BigInt();
this.bkplus1.digits[this.k + 1] = 1; // bkplus1 = b^(k+1)
this.modulo = BarrettMu_modulo;
this.multiplyMod = BarrettMu_multiplyMod;
this.powMod = BarrettMu_powMod;
};
function BarrettMu_modulo(x) {
var $dmath = RSAUtils;
var q1 = $dmath.biDivideByRadixPower(x, this.k - 1);
var q2 = $dmath.biMultiply(q1, this.mu);
var q3 = $dmath.biDivideByRadixPower(q2, this.k + 1);
var r1 = $dmath.biModuloByRadixPower(x, this.k + 1);
var r2term = $dmath.biMultiply(q3, this.modulus);
var r2 = $dmath.biModuloByRadixPower(r2term, this.k + 1);
var r = $dmath.biSubtract(r1, r2);
if (r.isNeg) {
r = $dmath.biAdd(r, this.bkplus1);
}
var rgtem = $dmath.biCompare(r, this.modulus) >= 0;
while (rgtem) {
r = $dmath.biSubtract(r, this.modulus);
rgtem = $dmath.biCompare(r, this.modulus) >= 0;
}
return r;
}
function BarrettMu_multiplyMod(x, y) {
/*
x = this.modulo(x);
y = this.modulo(y);
*/
var xy = RSAUtils.biMultiply(x, y);
return this.modulo(xy);
}
function BarrettMu_powMod(x, y) {
var result = new BigInt();
result.digits[0] = 1;
var a = x;
var k = y;
while (true) {
if ((k.digits[0] & 1) != 0) result = this.multiplyMod(result, a);
k = RSAUtils.biShiftRight(k, 1);
if (k.digits[0] == 0 && RSAUtils.biHighIndex(k) == 0) break;
a = this.multiplyMod(a, a);
}
return result;
}
var RSAKeyPair = function(encryptionExponent, decryptionExponent, modulus) {
var $dmath = RSAUtils;
this.e = $dmath.biFromHex(encryptionExponent);
this.d = $dmath.biFromHex(decryptionExponent);
this.m = $dmath.biFromHex(modulus);
// We can do two bytes per digit, so
// chunkSize = 2 * (number of digits in modulus - 1).
// Since biHighIndex returns the high index, not the number of digits, 1 has
// already been subtracted.
this.chunkSize = 2 * $dmath.biHighIndex(this.m);
this.radix = 16;
this.barrett = new $w.BarrettMu(this.m);
};
RSAUtils.getKeyPair = function(encryptionExponent, decryptionExponent, modulus) {
return new RSAKeyPair(encryptionExponent, decryptionExponent, modulus);
};
if(typeof $w.twoDigit === 'undefined') {
$w.twoDigit = function(n) {
return (n < 10 ? "0" : "") + String(n);
};
}
// Altered by Rob Saunders (rob#robsaunders.net). New routine pads the
// string after it has been converted to an array. This fixes an
// incompatibility with Flash MX's ActionScript.
RSAUtils.encryptedString = function(key, s) {
var a = [];
var sl = s.length;
var i = 0;
while (i < sl) {
a[i] = s.charCodeAt(i);
i++;
}
while (a.length % key.chunkSize != 0) {
a[i++] = 0;
}
var al = a.length;
var result = "";
var j, k, block;
for (i = 0; i < al; i += key.chunkSize) {
block = new BigInt();
j = 0;
for (k = i; k < i + key.chunkSize; ++j) {
block.digits[j] = a[k++];
block.digits[j] += a[k++] << 8;
}
var crypt = key.barrett.powMod(block, key.e);
var text = key.radix == 16 ? RSAUtils.biToHex(crypt) : RSAUtils.biToString(crypt, key.radix);
result += text + " ";
}
return result.substring(0, result.length - 1); // Remove last space.
};
RSAUtils.decryptedString = function(key, s) {
var blocks = s.split(" ");
var result = "";
var i, j, block;
for (i = 0; i < blocks.length; ++i) {
var bi;
if (key.radix == 16) {
bi = RSAUtils.biFromHex(blocks[i]);
}
else {
bi = RSAUtils.biFromString(blocks[i], key.radix);
}
block = key.barrett.powMod(bi, key.d);
for (j = 0; j <= RSAUtils.biHighIndex(block); ++j) {
result += String.fromCharCode(block.digits[j] & 255,
block.digits[j] >> 8);
}
}
// Remove trailing null, if any.
if (result.charCodeAt(result.length - 1) == 0) {
result = result.substring(0, result.length - 1);
}
return result;
};
RSAUtils.setMaxDigits(130);
})(window);
I can do RSA encryption like this:
var publickey = RSAUtils.getKeyPair("10001","","B3C61EBBA4659C4CE3639287EE871F1F48F7930EA977991C7AFE3CC442FEA49643212E7D570C853F368065CC57A2014666DA8AE7D493FD47D171C0D894EEE3ED7F99F6798B7FFD7B5873227038AD23E3197631A8CB642213B9F27D4901AB0D92BFA27542AE890855396ED92775255C977F5C302F1E7ED4B1E369C12CB6B1822F");
RSAUtils.encryptedString(publickey, "123456");
And result will be :
89ee5aa0c0e0901226c7641c0fd726c78a9c693bb908e610dcbcde262aba1d079e90827f70518dde395fdaec2bc3730d88368ca66d4164189a99059115afd72f3d0fa54dcd1b5e655264f8549978a56de8cb6f7b840b89cadb4364fd2783ba653b6efc6dd8aa98c2f470e6c0985b3b3421dce3f266843b6d7bb8918ded16a80d
I read the source code and reverted the hex string into byte array. But it is different from the java code's result! Here is the java code:
public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
cipher.init(Cipher.ENCRYPT_MODE, pk);
int blockSize = cipher.getBlockSize();
int outputSize = cipher.getOutputSize(data.length);
int leavedSize = data.length % blockSize;
int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
byte[] raw = new byte[outputSize * blocksSize];
int i = 0;
while (data.length - i * blockSize > 0) {
if (data.length - i * blockSize > blockSize) {
cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
} else {
cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
}
i++;
}
return raw;
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
I think the public key is very easy to create:
modulus is B3C61EBBA4659C4CE3639287EE871F1F48F7930EA977991C7AFE3CC442FEA49643212E7D570C853F368065CC57A2014666DA8AE7D493FD47D171C0D894EEE3ED7F99F6798B7FFD7B5873227038AD23E3197631A8CB642213B9F27D4901AB0D92BFA27542AE890855396ED92775255C977F5C302F1E7ED4B1E369C12CB6B1822F
publicExponent is 10001
Can anyone point out my mistake or make the result to the same? Many thanks!
Java/BouncyCastle and Barrett are using different paddings, so you cannot encrypt something in one and decrypt it in the other. The encryption result will also look differently.
The problem is that Barrett's RSAUtils is doing a zero byte padding:
while (a.length % key.chunkSize != 0) {
a[i++] = 0;
}
as seen in RSAUtils.encryptedString(). Such a thing is not recommended and might not even work half the time. Java and BouncyCastle don't even provide such a padding option for RSA. A proper padding must be used. JSBN for example does support PKCS#1 v1.5 padding. So, your Java code should use a fully qualified Cipher instance string:
Cipher.getInstance("RSA/ECB/PKCS1Padding", new org.bouncycastle.jce.provider.BouncyCastleProvider());
It is also not a good idea to use RSA for many "blocks" by splitting the data into chunks. It is vulnerable to block reordering and is really slow when you have much data (because RSA is slow). Use Hybrid Encryption instead where the actual data is encrypted with a fast symmetric cipher like AES and the randomly generated key for it is encrypted with a single RSA block.
I am trying to implement the rectangular/lattice multiplication in Java. For those who don't know, this is a short tutorial.
I tried some methods wherein I used a single array to store multiplication of two digits and sigma-append zeroes to it. Once all the numbers are multiplied, I pick two elements from the array and then add the sigma value and fetch two more numbers and again perform the same thing until all the numbers are fetched.
The logic works fine but I am not able to find the exact number of zeroes that I should maintain, since for every different set of numbers (4 digits * 3 digits) I get different number of zeroes.
Could somebody please help?
I liked the tutorial, very neat. So I wanted to implement it, but not do your project work. So I came up with a lousy, quick and dirty implementation, violating many of the design rules I practice myself. I used arrays to save the digit by digit multiplication results, and pretty much followed what the tutorial said. I never have had to count the number of 0's, and I am not sure what is sigma-appending, so I cannot answer that. Lastly, there is a bug in the code which shows up when the 2 numbers have a different count of digits. Here is the source code - feel free to edit and use any part. I think an easy fix would be to prepend 0's to the smaller number to make the digit count the same for the 2 numbers, and not to display the corresponding row/columns. More bookkeeping, but thats up to you.
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Lattice extends JPanel implements ActionListener {
protected Font axisFont, rectFont, carrFont;
protected Color boxColor = new Color (25, 143, 103), gridColor = new Color (78, 23, 211),
diagColor = new Color (93, 192, 85), fontColor = new Color (23, 187, 98),
carrColor = new Color (162, 34, 19);
protected int nDigitP, nDigitQ, dSize = 60,
m1, m2, lastCarry, iResult[],
xDigits[], yDigits[], prodTL[][], prodBR[][];
public Lattice (int p, int q, Font font) {
nDigitP = (int) Math.ceil (Math.log10 (p)); xDigits = new int[nDigitP];
nDigitQ = (int) Math.ceil (Math.log10 (q)); yDigits = new int[nDigitQ];
prodTL = new int[nDigitP][nDigitQ]; prodBR = new int[nDigitP][nDigitQ];
m1 = p; m2 = q; // To display in report
int np = p, nq = q, size = font.getSize(); // Save the digits in array
for (int i = 0 ; i < nDigitP ; i++) {
xDigits[i] = np % 10;
np /= 10;
}
for (int i = 0 ; i < nDigitQ ; i++) {
yDigits[i] = nq % 10;
nq /= 10;
}
for (int i = 0 ; i < nDigitP ; i++) { // Cell products as upper/lower matrix
for (int j = 0 ; j < nDigitQ ; j++) {
int prod = xDigits[i] * yDigits[j];
prodTL[i][j] = prod / 10;
prodBR[i][j] = prod % 10;
}}
axisFont = font.deriveFont (Font.PLAIN, size+8.0f);
rectFont = font.deriveFont (Font.PLAIN, size+4.0f);
carrFont = font.deriveFont (Font.PLAIN);
setPreferredSize (new Dimension ((nDigitP+2)*dSize, (nDigitQ+2)*dSize));
}
public void paint (Graphics g) {
int w = getWidth(), h = getHeight();
Graphics2D g2 = (Graphics2D) g; // To make diagonal lines smooth
g2.setPaint (Color.white);
g2.fillRect (0,0,w,h);
int dx = (int) Math.round (w/(2.0+nDigitP)), // Grid spacing to position
dy = (int) Math.round (h/(2.0+nDigitQ)); // the lines and the digits
g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint (RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.setFont (axisFont);
FontMetrics fm = g2.getFontMetrics();
for (int i = 0 ; i < nDigitP ; i++) { // Grid || Y-axis and labels on axis
int px = w - (i+1)*dx;
g2.setPaint (gridColor);
if (i > 0)
g2.drawLine (px, dy, px, h-dy);
String str = /*i + */"" + xDigits[i];
int strw = fm.stringWidth (str);
g2.setPaint (fontColor);
g2.drawString (str, px-dx/2-strw/2, 4*dy/5);
}
for (int i = 0 ; i < nDigitQ ; i++) { // Grid || X-axis and labels on axis
int py = h - (i+1)*dy;
g2.setPaint (gridColor);
if (i > 0)
g2.drawLine (dx, py, w-dx, py);
String str = /*i + */"" + yDigits[i];
int strw = fm.stringWidth (str);
g2.setPaint (fontColor);
g2.drawString (str, w-dx+2*dx/5-strw/2, py-dy/2+10);
}
g2.setFont (rectFont);
fm = g2.getFontMetrics(); // Upper/Lower traingular product matrix
for (int i = 0 ; i < nDigitP ; i++) {
for (int j = 0 ; j < nDigitQ ; j++) {
int px = w - (i+1)*dx;
int py = h - (j+1)*dy;
String strT = "" + prodTL[i][j];
int strw = fm.stringWidth (strT);
g2.drawString (strT, px-3*dx/4-strw/2, py-3*dy/4+5);
String strB = "" + prodBR[i][j];
strw = fm.stringWidth (strB);
g2.drawString (strB, px-dx/4-strw/2, py-dy/4+5);
}}
g2.setFont (axisFont);
fm = g2.getFontMetrics();
int carry = 0;
Vector cVector = new Vector(), iVector = new Vector();
for (int k = 0 ; k < 2*Math.max (nDigitP, nDigitQ) ; k++) {
int dSum = carry, i = k/2, j = k/2;
//System.out.println ("k="+k);
if ((k % 2) == 0) { // even k
if (k/2 < nDigitP && k/2 < nDigitQ)
dSum += prodBR[k/2][k/2];
// go right and top
for (int c = 0 ; c < k ; c++) {
if (--i < 0)
break;
if (i < nDigitP && j < nDigitQ)
dSum += prodTL[i][j];
//System.out.println (" >> TL (i,j) = (" + i+","+j+")");
if (++j == nDigitQ)
break;
if (i < nDigitP && j < nDigitQ)
dSum += prodBR[i][j];
//System.out.println (" >> BR (i,j) = (" + i+","+j+")");
}
// go bottom and left
i = k/2; j = k/2;
for (int c = 0 ; c < k ; c++) {
if (--j < 0)
break;
if (i < nDigitP && j < nDigitQ)
dSum += prodTL[i][j];
//System.out.println (" >> TL (i,j) = (" + i+","+j+")");
if (++i == nDigitP)
break;
if (i < nDigitP && j < nDigitQ)
dSum += prodBR[i][j];
//System.out.println (" >> BR (i,j) = (" + i+","+j+")");
}} else { // odd k
if (k/2 < nDigitP && k/2 < nDigitQ)
dSum += prodTL[k/2][k/2];
// go top and right
for (int c = 0 ; c < k ; c++) {
if (++j == nDigitQ)
break;
if (i < nDigitP && j < nDigitQ)
dSum += prodBR[i][j];
//System.out.println (" >> BR (i,j) = (" + i+","+j+")");
if (--i < 0)
break;
if (i < nDigitP && j < nDigitQ)
dSum += prodTL[i][j];
//System.out.println (" >> TL (i,j) = (" + i+","+j+")");
}
i = k/2; j = k/2;
// go left and bottom
for (int c = 0 ; c < k ; c++) {
if (++i == nDigitP)
break;
if (i < nDigitP && j < nDigitQ)
dSum += prodBR[i][j];
//System.out.println (" >> BR (i,j) = (" + i+","+j+")");
if (--j < 0)
break;
if (i < nDigitP && j < nDigitQ)
dSum += prodTL[i][j];
//System.out.println (" >> TL (i,j) = (" + i+","+j+")");
}}
int digit = dSum % 10; carry = dSum / 10;
cVector.addElement (new Integer (carry));
iVector.addElement (new Integer (digit));
String strD = "" + digit;
int strw = fm.stringWidth (strD);
if (k < nDigitP) {
int px = w - (k+1)*dx - 4*dx/5, py = h-dy + fm.getHeight();
g2.drawString (strD, px-strw/2, py);
} else {
int px = dx - 12, py = h - (k-nDigitP+1)*dy - dy/4;
g2.drawString (strD, px-strw/2, py+5);
}} // End k-loop
g2.setPaint (diagColor);
for (int i = 0 ; i < nDigitP ; i++) {
int xt = (i+1) * dx,
yb = (i+2) * dy;
g2.drawLine (xt, dy, 0, yb);
}
for (int i = 0 ; i < nDigitQ ; i++) {
int xb = (i + nDigitP - nDigitQ) * dx,
yr = (i+1) * dy;
g2.drawLine (w-dx, yr, xb, h);
}
// System.out.println ("carry Vector has " + cVector.size() + " elements");
g2.setFont (carrFont);
g2.setPaint (carrColor);
fm = g2.getFontMetrics();
for (int k = 0 ; k < 2*Math.max (nDigitP, nDigitQ) ; k++) {
carry = ((Integer) cVector.elementAt (k)).intValue();
lastCarry = carry; // To display
if (carry == 0)
continue;
String strC = "" + carry;
int strw = fm.stringWidth (strC),
px = w-dx-5-strw/2, // Const X while going Up
py = dy + fm.getHeight(); // Const Y while going Left
if (k < (nDigitQ-1))
py = h-(k+3)*dy + dy/5 + fm.getHeight();
else
px = w - (k-nDigitQ+2) * dx - dx/2 - strw/2;
g2.drawString (strC, px, py);
}
int n = iVector.size(); // Save the vector content to display later
iResult = new int[n];
for (int i = 0 ; i < n ; i++)
iResult[i] = ((Integer) iVector.elementAt (n-i-1)).intValue();
g2.setPaint (boxColor); g2.drawRect (dx, dy, w-2*dx, h-2*dy);
}
private void displayResults () {
StringBuffer sb = new StringBuffer ("Lattice: " + m1 + " \u00D7 " + m2 + " = " +
((lastCarry == 0) ? "" : (""+lastCarry)));
for (int k = 0 ; k < iResult.length ; k++)
sb.append ("" + iResult[k]);
// System.out.println (sb.toString());
JOptionPane.showMessageDialog (null, sb.toString(), "Lattice Multiplier",
JOptionPane.INFORMATION_MESSAGE);
}
public JPanel getButtonPanel () {
JPanel bp = new JPanel(new GridLayout (1,bNames.length));
for (int i = 0 ; i < bNames.length ; i++) {
JButton b = new JButton (bNames[i]);
b.addActionListener (this);
bp.add (b);
}
return bp;
}
private final static String[] bNames = {"Close", "Result"};
public void actionPerformed (ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals (bNames[0])) System.exit (0);
else if (cmd.equals (bNames[1])) displayResults();
}
public static void main (String[] args) {
JTextField tf1 = new JTextField (), tf2 = new JTextField();
JPanel num2m = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets (2,2,2,2);
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
gbc.gridy = GridBagConstraints.RELATIVE;
gbc.anchor = GridBagConstraints.EAST;
JLabel
label = new JLabel ("Multiplicand", JLabel.TRAILING); num2m.add (label, gbc);
label = new JLabel ("Multiplier", JLabel.TRAILING); num2m.add (label, gbc);
gbc.gridx++;
gbc.weightx = 1.0f; num2m.add (tf1, gbc); num2m.add (tf2, gbc);
JFrame lf = new JFrame ("Lattice Multiplication");
if (JOptionPane.showConfirmDialog (lf, num2m, "Enter numbers to multiply",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE ) == JOptionPane.OK_OPTION) {
try {
int m = Integer.parseInt (tf1.getText()), n = Integer.parseInt (tf2.getText());
Lattice lattice = new Lattice (m, n, label.getFont());
lf.add (lattice.getButtonPanel(), "South");
lf.add (lattice, "Center");
lf.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
lf.pack();
lf.setVisible (true);
} catch (NumberFormatException nex) {
JOptionPane.showMessageDialog (lf, "Invalid numbers to multiply",
"Lattice Multiplier Error", JOptionPane.ERROR_MESSAGE);
System.exit (1);
}} else { System.exit (2);
}}}
this works for any set of numbers..2 digit multiplicand with 4 digit multiplier..etc..
if(numberM.length()!=numberN.length()){
int mLen = numberM.length();
int nLen = numberN.length();
if(numberM.length()>numberN.length()){
for(int i=0;i<mLen-nLen;i++){
numberN = "0" + numberN;
}
}
else
{
for(int i=0;i<nLen-mLen;i++){
numberM = "0" + numberM;
}
}
}
int result[] = new int[numberN.length()+numberM.length()];
String numberRN = new StringBuffer(numberN).reverse().toString();
String numberRM = new StringBuffer(numberM).reverse().toString();
//reversing the number
int n[] = new int[numberN.length()];
int m[] = new int[numberM.length()];
int size_of_array = 0;
for(int i=0;i<numberN.length();i++){
n[i] = Integer.parseInt((new Character(numberRN.charAt(i))).toString());
m[i] = Integer.parseInt((new Character(numberRM.charAt(i))).toString());
}
//System.out.println("Numbers are:");
//displayArray(n,"n");
//displayArray(m,"m");
size_of_array = (m.length*2)*2;
int soa = size_of_array;
int tempArray[] = new int[size_of_array*m.length];
//System.out.println("Size of tempArray ="+tempArray.length);
//placing the numbers in a single array
int oddValue =3;
int index = 0;
tempArray[index++] = 0;
for(int i=0;i<m.length;i++){
for(int j=0;j<n.length;j++){
//System.out.println("n["+j+"]="+n[j]+" and m["+i+"]="+m[i]);
tempArray[index++] = (n[j] * m[i]) % 10;
tempArray[index] = (n[j] * m[i]) / 10;
//System.out.println("tempArray["+(index-1)+"]="+tempArray[index-1]+" tempArray["+(index)+"]="+tempArray[index]);
index++;
}
//System.out.println("index before appending zero="+index);
size_of_array=(i+1)*soa;
index = size_of_array;
//System.out.println("index after appending zero="+index);
//index+=i+oddArray[i];
index+=i+oddValue;
oddValue++;
//System.out.println("After move index="+index);
}
//System.out.println("tempArray full");
//displayArray(tempArray,"tempArray");
//adding the numbers and also dealing with carry
int count=0;int ind = 0;int res = 0;int carry =0;int tempInd = 0;
for(int i=0;i<m.length*2;i++){
tempInd = ind;
for(int k=0;k<m.length;k++){
//System.out.println(tempArray[ind]+"---"+tempArray[ind+1]);
res = tempArray[ind] + tempArray[ind+1] + res + carry;
ind = ind + soa ;
carry = 0;
//System.out.println("res="+res+" ind="+ind+" soa="+soa);
}
//System.out.println("----------------");
result[count++] = res %10;
carry = res /10;
res = 0;
ind = tempInd+2;
}
//displayArray(result,"result");
displayResult(result,sign);
}
private static void displayResult(int[] result,String sign) {
System.out.print("The result is "+sign);
for(int i=result.length-1;i>=0;i--){
System.out.print(result[i]);
}
}
static void displayArray(int tempArray[],String name){
for(int k =0;k<tempArray.length;k++)
System.out.print(" "+name+"["+k+"]-"+tempArray[k]);
System.out.println("");
}