Skip to content

Commit 393337f

Browse files
authored
Add Tests for HillCipher (#5562)
1 parent 389d1d7 commit 393337f

File tree

2 files changed

+105
-144
lines changed

2 files changed

+105
-144
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,178 +1,103 @@
11
package com.thealgorithms.ciphers;
22

3-
import java.util.Scanner;
3+
public class HillCipher {
44

5-
/*
6-
* Java Implementation of Hill Cipher
7-
* Hill cipher is a polyalphabetic substitution cipher. Each letter is represented by a number
8-
* belonging to the set Z26 where A=0 , B=1, ..... Z=25. To encrypt a message, each block of n
9-
* letters (since matrix size is n x n) is multiplied by an invertible n × n matrix, against
10-
* modulus 26. To decrypt the message, each block is multiplied by the inverse of the matrix used
11-
* for encryption. The cipher key and plaintext/ciphertext are user inputs.
12-
* @author Ojasva Jain
13-
*/
14-
public final class HillCipher {
15-
private HillCipher() {
16-
}
17-
18-
static Scanner userInput = new Scanner(System.in);
19-
20-
/* Following function encrypts the message
21-
*/
22-
static void encrypt(String message) {
23-
message = message.toUpperCase();
24-
// Get key matrix
25-
System.out.println("Enter key matrix size");
26-
int matrixSize = userInput.nextInt();
27-
System.out.println("Enter Key/encryptionKey matrix ");
28-
int[][] keyMatrix = new int[matrixSize][matrixSize];
29-
for (int i = 0; i < matrixSize; i++) {
30-
for (int j = 0; j < matrixSize; j++) {
31-
keyMatrix[i][j] = userInput.nextInt();
32-
}
33-
}
34-
// check if det = 0
5+
// Encrypts the message using the key matrix
6+
public String encrypt(String message, int[][] keyMatrix) {
7+
message = message.toUpperCase().replaceAll("[^A-Z]", "");
8+
int matrixSize = keyMatrix.length;
359
validateDeterminant(keyMatrix, matrixSize);
3610

37-
int[][] messageVector = new int[matrixSize][1];
38-
String cipherText = "";
39-
int[][] cipherMatrix = new int[matrixSize][1];
40-
int j = 0;
41-
while (j < message.length()) {
11+
StringBuilder cipherText = new StringBuilder();
12+
int[] messageVector = new int[matrixSize];
13+
int[] cipherVector = new int[matrixSize];
14+
int index = 0;
15+
16+
while (index < message.length()) {
4217
for (int i = 0; i < matrixSize; i++) {
43-
if (j >= message.length()) {
44-
messageVector[i][0] = 23;
18+
if (index < message.length()) {
19+
messageVector[i] = message.charAt(index++) - 'A';
4520
} else {
46-
messageVector[i][0] = (message.charAt(j)) % 65;
21+
messageVector[i] = 'X' - 'A'; // Padding with 'X' if needed
4722
}
48-
System.out.println(messageVector[i][0]);
49-
j++;
5023
}
51-
int x;
52-
int i;
53-
for (i = 0; i < matrixSize; i++) {
54-
cipherMatrix[i][0] = 0;
5524

56-
for (x = 0; x < matrixSize; x++) {
57-
cipherMatrix[i][0] += keyMatrix[i][x] * messageVector[x][0];
25+
for (int i = 0; i < matrixSize; i++) {
26+
cipherVector[i] = 0;
27+
for (int j = 0; j < matrixSize; j++) {
28+
cipherVector[i] += keyMatrix[i][j] * messageVector[j];
5829
}
59-
System.out.println(cipherMatrix[i][0]);
60-
cipherMatrix[i][0] = cipherMatrix[i][0] % 26;
61-
}
62-
for (i = 0; i < matrixSize; i++) {
63-
cipherText += (char) (cipherMatrix[i][0] + 65);
30+
cipherVector[i] = cipherVector[i] % 26;
31+
cipherText.append((char) (cipherVector[i] + 'A'));
6432
}
6533
}
66-
System.out.println("Ciphertext: " + cipherText);
34+
35+
return cipherText.toString();
6736
}
6837

69-
// Following function decrypts a message
70-
static void decrypt(String message) {
71-
message = message.toUpperCase();
72-
// Get key matrix
73-
System.out.println("Enter key matrix size");
74-
int n = userInput.nextInt();
75-
System.out.println("Enter inverseKey/decryptionKey matrix ");
76-
int[][] keyMatrix = new int[n][n];
77-
for (int i = 0; i < n; i++) {
78-
for (int j = 0; j < n; j++) {
79-
keyMatrix[i][j] = userInput.nextInt();
80-
}
81-
}
82-
// check if det = 0
83-
validateDeterminant(keyMatrix, n);
38+
// Decrypts the message using the inverse key matrix
39+
public String decrypt(String message, int[][] inverseKeyMatrix) {
40+
message = message.toUpperCase().replaceAll("[^A-Z]", "");
41+
int matrixSize = inverseKeyMatrix.length;
42+
validateDeterminant(inverseKeyMatrix, matrixSize);
43+
44+
StringBuilder plainText = new StringBuilder();
45+
int[] messageVector = new int[matrixSize];
46+
int[] plainVector = new int[matrixSize];
47+
int index = 0;
8448

85-
// solving for the required plaintext message
86-
int[][] messageVector = new int[n][1];
87-
String plainText = "";
88-
int[][] plainMatrix = new int[n][1];
89-
int j = 0;
90-
while (j < message.length()) {
91-
for (int i = 0; i < n; i++) {
92-
if (j >= message.length()) {
93-
messageVector[i][0] = 23;
49+
while (index < message.length()) {
50+
for (int i = 0; i < matrixSize; i++) {
51+
if (index < message.length()) {
52+
messageVector[i] = message.charAt(index++) - 'A';
9453
} else {
95-
messageVector[i][0] = (message.charAt(j)) % 65;
54+
messageVector[i] = 'X' - 'A'; // Padding with 'X' if needed
9655
}
97-
System.out.println(messageVector[i][0]);
98-
j++;
9956
}
100-
int x;
101-
int i;
102-
for (i = 0; i < n; i++) {
103-
plainMatrix[i][0] = 0;
10457

105-
for (x = 0; x < n; x++) {
106-
plainMatrix[i][0] += keyMatrix[i][x] * messageVector[x][0];
58+
for (int i = 0; i < matrixSize; i++) {
59+
plainVector[i] = 0;
60+
for (int j = 0; j < matrixSize; j++) {
61+
plainVector[i] += inverseKeyMatrix[i][j] * messageVector[j];
10762
}
108-
109-
plainMatrix[i][0] = plainMatrix[i][0] % 26;
110-
}
111-
for (i = 0; i < n; i++) {
112-
plainText += (char) (plainMatrix[i][0] + 65);
63+
plainVector[i] = plainVector[i] % 26;
64+
plainText.append((char) (plainVector[i] + 'A'));
11365
}
11466
}
115-
System.out.println("Plaintext: " + plainText);
67+
68+
return plainText.toString();
11669
}
11770

118-
// Determinant calculator
119-
public static int determinant(int[][] a, int n) {
120-
int det = 0;
121-
int sign = 1;
122-
int p = 0;
123-
int q = 0;
71+
// Validates that the determinant of the key matrix is not zero modulo 26
72+
private void validateDeterminant(int[][] keyMatrix, int n) {
73+
int det = determinant(keyMatrix, n) % 26;
74+
if (det == 0) {
75+
throw new IllegalArgumentException("Invalid key matrix. Determinant is zero modulo 26.");
76+
}
77+
}
12478

79+
// Computes the determinant of a matrix recursively
80+
private int determinant(int[][] matrix, int n) {
81+
int det = 0;
12582
if (n == 1) {
126-
det = a[0][0];
127-
} else {
128-
int[][] b = new int[n - 1][n - 1];
129-
for (int x = 0; x < n; x++) {
130-
p = 0;
131-
q = 0;
132-
for (int i = 1; i < n; i++) {
133-
for (int j = 0; j < n; j++) {
134-
if (j != x) {
135-
b[p][q++] = a[i][j];
136-
if (q % (n - 1) == 0) {
137-
p++;
138-
q = 0;
139-
}
140-
}
83+
return matrix[0][0];
84+
}
85+
int sign = 1;
86+
int[][] subMatrix = new int[n - 1][n - 1];
87+
for (int x = 0; x < n; x++) {
88+
int subI = 0;
89+
for (int i = 1; i < n; i++) {
90+
int subJ = 0;
91+
for (int j = 0; j < n; j++) {
92+
if (j != x) {
93+
subMatrix[subI][subJ++] = matrix[i][j];
14194
}
14295
}
143-
det = det + a[0][x] * determinant(b, n - 1) * sign;
144-
sign = -sign;
96+
subI++;
14597
}
98+
det += sign * matrix[0][x] * determinant(subMatrix, n - 1);
99+
sign = -sign;
146100
}
147101
return det;
148102
}
149-
150-
// Function to implement Hill Cipher
151-
static void hillCipher(String message) {
152-
System.out.println("What do you want to process from the message?");
153-
System.out.println("Press 1: To Encrypt");
154-
System.out.println("Press 2: To Decrypt");
155-
short sc = userInput.nextShort();
156-
if (sc == 1) {
157-
encrypt(message);
158-
} else if (sc == 2) {
159-
decrypt(message);
160-
} else {
161-
System.out.println("Invalid input, program terminated.");
162-
}
163-
}
164-
165-
static void validateDeterminant(int[][] keyMatrix, int n) {
166-
if (determinant(keyMatrix, n) % 26 == 0) {
167-
System.out.println("Invalid key, as determinant = 0. Program Terminated");
168-
}
169-
}
170-
171-
// Driver code
172-
public static void main(String[] args) {
173-
// Get the message to be encrypted
174-
System.out.println("Enter message");
175-
String message = userInput.nextLine();
176-
hillCipher(message);
177-
}
178103
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.thealgorithms.ciphers;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
class HillCipherTest {
8+
9+
HillCipher hillCipher = new HillCipher();
10+
11+
@Test
12+
void hillCipherEncryptTest() {
13+
// given
14+
String message = "ACT"; // Plaintext message
15+
int[][] keyMatrix = {{6, 24, 1}, {13, 16, 10}, {20, 17, 15}}; // Encryption key matrix
16+
17+
// when
18+
String cipherText = hillCipher.encrypt(message, keyMatrix);
19+
20+
// then
21+
assertEquals("POH", cipherText);
22+
}
23+
24+
@Test
25+
void hillCipherDecryptTest() {
26+
// given
27+
String cipherText = "POH"; // Ciphertext message
28+
int[][] inverseKeyMatrix = {{8, 5, 10}, {21, 8, 21}, {21, 12, 8}}; // Decryption (inverse key) matrix
29+
30+
// when
31+
String plainText = hillCipher.decrypt(cipherText, inverseKeyMatrix);
32+
33+
// then
34+
assertEquals("ACT", plainText);
35+
}
36+
}

0 commit comments

Comments
 (0)