diff --git a/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java b/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java
index 3e62d6a26dcb..d915858f9e6f 100644
--- a/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java
+++ b/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java
@@ -3,23 +3,38 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+
/**
- * The ADFGVX cipher is a historically significant cipher used by
- * the German Army during World War I. It is a fractionating transposition
- * cipher that combines a Polybius square substitution with a columnar
- * transposition. It's named after the six letters (A, D, F, G, V, X)
- * that it uses in its substitution process.
- * https://en.wikipedia.org/wiki/ADFGVX_cipher
+ * The ADFGVX cipher is a fractionating transposition cipher that was used by
+ * the German Army during World War I. It combines a **Polybius square substitution**
+ * with a **columnar transposition** to enhance encryption strength.
+ *
+ * The name "ADFGVX" refers to the six letters (A, D, F, G, V, X) used as row and
+ * column labels in the Polybius square. This cipher was designed to secure
+ * communication and create complex, hard-to-break ciphertexts.
+ *
+ * Learn more: ADFGVX Cipher - Wikipedia.
+ *
+ * Example usage:
+ *
+ * ADFGVXCipher cipher = new ADFGVXCipher();
+ * String encrypted = cipher.encrypt("attack at 1200am", "PRIVACY");
+ * String decrypted = cipher.decrypt(encrypted, "PRIVACY");
+ *
*
* @author bennybebo
*/
public class ADFGVXCipher {
+ // Constants used in the Polybius square
private static final char[] POLYBIUS_LETTERS = {'A', 'D', 'F', 'G', 'V', 'X'};
private static final char[][] POLYBIUS_SQUARE = {{'N', 'A', '1', 'C', '3', 'H'}, {'8', 'T', 'B', '2', 'O', 'M'}, {'E', '5', 'W', 'R', 'P', 'D'}, {'4', 'F', '6', 'G', '7', 'I'}, {'9', 'J', '0', 'K', 'L', 'Q'}, {'S', 'U', 'V', 'X', 'Y', 'Z'}};
+
+ // Maps for fast substitution lookups
private static final Map POLYBIUS_MAP = new HashMap<>();
private static final Map REVERSE_POLYBIUS_MAP = new HashMap<>();
+ // Static block to initialize the lookup tables from the Polybius square
static {
for (int i = 0; i < POLYBIUS_SQUARE.length; i++) {
for (int j = 0; j < POLYBIUS_SQUARE[i].length; j++) {
@@ -30,26 +45,41 @@ public class ADFGVXCipher {
}
}
- // Encrypts the plaintext using the ADFGVX cipher
+ /**
+ * Encrypts a given plaintext using the ADFGVX cipher with the provided keyword.
+ * Steps:
+ * 1. Substitute each letter in the plaintext with a pair of ADFGVX letters.
+ * 2. Perform a columnar transposition on the fractionated text using the keyword.
+ *
+ * @param plaintext The message to be encrypted (can contain letters and digits).
+ * @param key The keyword for columnar transposition.
+ * @return The encrypted message as ciphertext.
+ */
public String encrypt(String plaintext, String key) {
- plaintext = plaintext.toUpperCase().replaceAll("[^A-Z0-9]", "");
+ plaintext = plaintext.toUpperCase().replaceAll("[^A-Z0-9]", ""); // Sanitize input
StringBuilder fractionatedText = new StringBuilder();
- // Step 1: Polybius square substitution
for (char c : plaintext.toCharArray()) {
fractionatedText.append(REVERSE_POLYBIUS_MAP.get(c));
}
- // Step 2: Columnar transposition
return columnarTransposition(fractionatedText.toString(), key);
}
- // Decrypts the ciphertext using the ADFGVX cipher
+ /**
+ * Decrypts a given ciphertext using the ADFGVX cipher with the provided keyword.
+ * Steps:
+ * 1. Reverse the columnar transposition performed during encryption.
+ * 2. Substitute each pair of ADFGVX letters with the corresponding plaintext letter.
+ * The resulting text is the decrypted message.
+ *
+ * @param ciphertext The encrypted message.
+ * @param key The keyword used during encryption.
+ * @return The decrypted plaintext message.
+ */
public String decrypt(String ciphertext, String key) {
- // Step 1: Reverse the columnar transposition
String fractionatedText = reverseColumnarTransposition(ciphertext, key);
- // Step 2: Polybius square substitution
StringBuilder plaintext = new StringBuilder();
for (int i = 0; i < fractionatedText.length(); i += 2) {
String pair = fractionatedText.substring(i, i + 2);
@@ -59,14 +89,21 @@ public String decrypt(String ciphertext, String key) {
return plaintext.toString();
}
+ /**
+ * Helper method: Performs columnar transposition during encryption
+ *
+ * @param text The fractionated text to be transposed
+ * @param key The keyword for columnar transposition
+ * @return The transposed text
+ */
private String columnarTransposition(String text, String key) {
int numRows = (int) Math.ceil((double) text.length() / key.length());
char[][] table = new char[numRows][key.length()];
- for (char[] row : table) {
- Arrays.fill(row, '_'); // Fill with underscores to handle empty cells
+ for (char[] row : table) { // Fill empty cells with underscores
+ Arrays.fill(row, '_');
}
- // Fill the table row by row
+ // Populate the table row by row
for (int i = 0; i < text.length(); i++) {
table[i / key.length()][i % key.length()] = text.charAt(i);
}
@@ -88,6 +125,13 @@ private String columnarTransposition(String text, String key) {
return ciphertext.toString();
}
+ /**
+ * Helper method: Reverses the columnar transposition during decryption
+ *
+ * @param ciphertext The transposed text to be reversed
+ * @param key The keyword used during encryption
+ * @return The reversed text
+ */
private String reverseColumnarTransposition(String ciphertext, String key) {
int numRows = (int) Math.ceil((double) ciphertext.length() / key.length());
char[][] table = new char[numRows][key.length()];
@@ -96,19 +140,19 @@ private String reverseColumnarTransposition(String ciphertext, String key) {
Arrays.sort(sortedKey);
int index = 0;
- // Fill the table column by column according to the sorted key order
+ // Populate the table column by column according to the sorted key
for (char keyChar : sortedKey) {
int column = key.indexOf(keyChar);
for (int row = 0; row < numRows; row++) {
if (index < ciphertext.length()) {
table[row][column] = ciphertext.charAt(index++);
} else {
- table[row][column] = '_'; // Fill empty cells with an underscore
+ table[row][column] = '_';
}
}
}
- // Read the table row by row to get the fractionated text
+ // Read the table row by row to reconstruct the fractionated text
StringBuilder fractionatedText = new StringBuilder();
for (char[] row : table) {
for (char cell : row) {
diff --git a/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java b/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java
index a1fc4fd9ebe5..4db856e40b84 100644
--- a/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java
+++ b/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java
@@ -6,31 +6,41 @@
class ADFGVXCipherTest {
- ADFGVXCipher adfgvxCipher = new ADFGVXCipher();
+ private final ADFGVXCipher adfgvxCipher = new ADFGVXCipher();
@Test
- void adfgvxCipherEncryptTest() {
- // given
- String message = "attack at 1200am"; // Plaintext message
- String keyword = "PRIVACY";
+ void testEncrypt() {
+ String message = "attack at 1200am";
+ String key = "PRIVACY";
- // when
- String cipherText = adfgvxCipher.encrypt(message, keyword);
+ String encrypted = adfgvxCipher.encrypt(message, key);
+ assertEquals("DGDDDAGDDGAFADDFDADVDVFAADVX", encrypted);
+ }
+
+ @Test
+ void testDecrypt() {
+ String encrypted = "DGDDDAGDDGAFADDFDADVDVFAADVX";
+ String key = "PRIVACY";
- // then
- assertEquals("DGDDDAGDDGAFADDFDADVDVFAADVX", cipherText);
+ String decrypted = adfgvxCipher.decrypt(encrypted, key);
+ assertEquals("ATTACKAT1200AM", decrypted);
}
@Test
- void adfgvxCipherDecryptTest() {
- // given
- String cipherText = "DGDDDAGDDGAFADDFDADVDVFAADVX"; // Ciphertext message
- String keyword = "PRIVACY";
+ void testEmptyInput() {
+ String encrypted = adfgvxCipher.encrypt("", "PRIVACY");
+ String decrypted = adfgvxCipher.decrypt("", "PRIVACY");
+ assertEquals("", encrypted);
+ assertEquals("", decrypted);
+ }
- // when
- String plainText = adfgvxCipher.decrypt(cipherText, keyword);
+ @Test
+ void testShortKey() {
+ String message = "TESTING";
+ String key = "A";
- // then
- assertEquals("ATTACKAT1200AM", plainText);
+ String encrypted = adfgvxCipher.encrypt(message, key);
+ String decrypted = adfgvxCipher.decrypt(encrypted, key);
+ assertEquals("TESTING", decrypted);
}
}