From 8a8a1aa8c9e2efa923a4507322c2b41288952aa0 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sat, 19 Oct 2024 18:29:16 +0530 Subject: [PATCH 1/4] feat: Add `ADFGVXCipher` new algorithm with Junit tests --- .../thealgorithms/ciphers/ADFGVXCipher.java | 82 ++++++++++++++----- .../ciphers/ADFGVXCipherTest.java | 44 ++++++---- 2 files changed, 89 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java b/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java index 3e62d6a26dcb..61ca3a90ad94 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,19 @@ 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) Arrays.fill(row, '_'); // Fill empty cells with underscores - // 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 +123,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 +138,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); } } From 144af5083012340bbfcb9dd1097593a65d5aeb1e Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sat, 19 Oct 2024 18:32:19 +0530 Subject: [PATCH 2/4] Fix --- src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java b/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java index 61ca3a90ad94..d915858f9e6f 100644 --- a/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java +++ b/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java @@ -99,7 +99,9 @@ public String decrypt(String ciphertext, String key) { 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 empty cells with underscores + for (char[] row : table) { // Fill empty cells with underscores + Arrays.fill(row, '_'); + } // Populate the table row by row for (int i = 0; i < text.length(); i++) { From 9e836d35d5d8c0daa2c618100746e39eccece197 Mon Sep 17 00:00:00 2001 From: Hardvan Date: Sat, 26 Oct 2024 09:53:53 +0000 Subject: [PATCH 3/4] Update directory --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 10922d761952..0074f212551b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -320,6 +320,7 @@ * [BresenhamLine](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/BresenhamLine.java) * [ConvexHull](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/ConvexHull.java) * [GrahamScan](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/GrahamScan.java) + * [MidpointEllipse](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java) * [Point](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/Point.java) * graph * [StronglyConnectedComponentOptimized](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java) @@ -967,6 +968,7 @@ * [BresenhamLineTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java) * [ConvexHullTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/ConvexHullTest.java) * [GrahamScanTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/GrahamScanTest.java) + * [MidpointEllipseTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java) * graph * [StronglyConnectedComponentOptimizedTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/graph/StronglyConnectedComponentOptimizedTest.java) * greedyalgorithms From 7966f0afd12f9681c00142f9dfbf7f55062763e3 Mon Sep 17 00:00:00 2001 From: siriak Date: Sat, 26 Oct 2024 11:56:44 +0000 Subject: [PATCH 4/4] Update directory --- DIRECTORY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 0074f212551b..2aaa29460f8b 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -320,6 +320,7 @@ * [BresenhamLine](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/BresenhamLine.java) * [ConvexHull](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/ConvexHull.java) * [GrahamScan](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/GrahamScan.java) + * [MidpointCircle](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/MidpointCircle.java) * [MidpointEllipse](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/MidpointEllipse.java) * [Point](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/geometry/Point.java) * graph @@ -361,6 +362,7 @@ * [Average](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Average.java) * [BinaryPow](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/BinaryPow.java) * [BinomialCoefficient](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/BinomialCoefficient.java) + * [CatalanNumbers](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CatalanNumbers.java) * [Ceil](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Ceil.java) * [ChineseRemainderTheorem](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/ChineseRemainderTheorem.java) * [CircularConvolutionFFT](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CircularConvolutionFFT.java) @@ -968,6 +970,7 @@ * [BresenhamLineTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/BresenhamLineTest.java) * [ConvexHullTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/ConvexHullTest.java) * [GrahamScanTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/GrahamScanTest.java) + * [MidpointCircleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/MidpointCircleTest.java) * [MidpointEllipseTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/geometry/MidpointEllipseTest.java) * graph * [StronglyConnectedComponentOptimizedTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/graph/StronglyConnectedComponentOptimizedTest.java) @@ -1005,6 +1008,7 @@ * [AverageTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/AverageTest.java) * [BinaryPowTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/BinaryPowTest.java) * [BinomialCoefficientTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/BinomialCoefficientTest.java) + * [CatalanNumbersTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CatalanNumbersTest.java) * [CeilTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CeilTest.java) * [ChineseRemainderTheoremTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ChineseRemainderTheoremTest.java) * [CollatzConjectureTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CollatzConjectureTest.java)