Repository: TheAlgorithms/Java
Pull request #5631 (2024-10-07 17:14:04)

Added ADFGVXCipher.java
<!--
Thank you for your contribution!
In order to reduce the number of notifications sent to the maintainers, please:
- create your PR as draft, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests,
- make sure that all of the CI checks pass,
- mark your PR as ready for review, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review
-->

<!-- For completed items, change [ ] to [x] -->

- [x] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Java/blob/master/CONTRIBUTING.md).
- [x] This pull request is all my own work -- I have not plagiarized it.
- [x] All filenames are in PascalCase.
- [x] All functions and variable names follow Java naming conventions.
- [x] All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.
- [x] All new code is formatted with `clang-format -i --style=file path/to/your/file.java`

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5631?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) Report
Attention: Patch coverage is `0%` with `54 lines` in your changes missing coverage. Please review.
> Please [upload](https://docs.codecov.com/docs/codecov-uploader) report for BASE (`master@676d451`). [Learn more](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms#section-missing-base-commit) about missing BASE report.

| [Files with missing lines](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5631?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) | Patch % | Lines |
|---|---|---|
| [...n/java/com/thealgorithms/ciphers/ADFGVXCipher.java](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5631?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fcom%2Fthealgorithms%2Fciphers%2FADFGVXCipher.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms#diff-c3JjL21haW4vamF2YS9jb20vdGhlYWxnb3JpdGhtcy9jaXBoZXJzL0FERkdWWENpcGhlci5qYXZh) | 0.00% | [54 Missing :warning: ](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5631?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##             master    #5631   +/-   ##
=========================================
  Coverage          ?   57.70%           
  Complexity        ?     3646           
=========================================
  Files             ?      546           
  Lines             ?    15726           
  Branches          ?     2999           
=========================================
  Hits              ?     9075           
  Misses            ?     6270           
  Partials          ?      381           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5631?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms).


Patch:
diff --git a/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java b/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java
new file mode 100644
index 00000000..3e62d6a2
--- /dev/null
+++ b/src/main/java/com/thealgorithms/ciphers/ADFGVXCipher.java
@@ -0,0 +1,123 @@
+package com.thealgorithms.ciphers;
+
+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
+ *
+ * @author bennybebo
+ */
+public class ADFGVXCipher {
+
+    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'}};
+    private static final Map<String, Character> POLYBIUS_MAP = new HashMap<>();
+    private static final Map<Character, String> REVERSE_POLYBIUS_MAP = new HashMap<>();
+
+    static {
+        for (int i = 0; i < POLYBIUS_SQUARE.length; i++) {
+            for (int j = 0; j < POLYBIUS_SQUARE[i].length; j++) {
+                String key = "" + POLYBIUS_LETTERS[i] + POLYBIUS_LETTERS[j];
+                POLYBIUS_MAP.put(key, POLYBIUS_SQUARE[i][j]);
+                REVERSE_POLYBIUS_MAP.put(POLYBIUS_SQUARE[i][j], key);
+            }
+        }
+    }
+
+    // Encrypts the plaintext using the ADFGVX cipher
+    public String encrypt(String plaintext, String key) {
+        plaintext = plaintext.toUpperCase().replaceAll("[^A-Z0-9]", "");
+        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
+    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);
+            plaintext.append(POLYBIUS_MAP.get(pair));
+        }
+
+        return plaintext.toString();
+    }
+
+    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
+        }
+
+        // Fill the table row by row
+        for (int i = 0; i < text.length(); i++) {
+            table[i / key.length()][i % key.length()] = text.charAt(i);
+        }
+
+        // Read columns based on the alphabetical order of the key
+        StringBuilder ciphertext = new StringBuilder();
+        char[] sortedKey = key.toCharArray();
+        Arrays.sort(sortedKey);
+
+        for (char keyChar : sortedKey) {
+            int column = key.indexOf(keyChar);
+            for (char[] row : table) {
+                if (row[column] != '_') {
+                    ciphertext.append(row[column]);
+                }
+            }
+        }
+
+        return ciphertext.toString();
+    }
+
+    private String reverseColumnarTransposition(String ciphertext, String key) {
+        int numRows = (int) Math.ceil((double) ciphertext.length() / key.length());
+        char[][] table = new char[numRows][key.length()];
+
+        char[] sortedKey = key.toCharArray();
+        Arrays.sort(sortedKey);
+
+        int index = 0;
+        // Fill the table column by column according to the sorted key order
+        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
+                }
+            }
+        }
+
+        // Read the table row by row to get the fractionated text
+        StringBuilder fractionatedText = new StringBuilder();
+        for (char[] row : table) {
+            for (char cell : row) {
+                if (cell != '_') {
+                    fractionatedText.append(cell);
+                }
+            }
+        }
+
+        return fractionatedText.toString();
+    }
+}


Test patch:
diff --git a/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java b/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java
new file mode 100644
index 00000000..a1fc4fd9
--- /dev/null
+++ b/src/test/java/com/thealgorithms/ciphers/ADFGVXCipherTest.java
@@ -0,0 +1,36 @@
+package com.thealgorithms.ciphers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class ADFGVXCipherTest {
+
+    ADFGVXCipher adfgvxCipher = new ADFGVXCipher();
+
+    @Test
+    void adfgvxCipherEncryptTest() {
+        // given
+        String message = "attack at 1200am"; // Plaintext message
+        String keyword = "PRIVACY";
+
+        // when
+        String cipherText = adfgvxCipher.encrypt(message, keyword);
+
+        // then
+        assertEquals("DGDDDAGDDGAFADDFDADVDVFAADVX", cipherText);
+    }
+
+    @Test
+    void adfgvxCipherDecryptTest() {
+        // given
+        String cipherText = "DGDDDAGDDGAFADDFDADVDVFAADVX"; // Ciphertext message
+        String keyword = "PRIVACY";
+
+        // when
+        String plainText = adfgvxCipher.decrypt(cipherText, keyword);
+
+        // then
+        assertEquals("ATTACKAT1200AM", plainText);
+    }
+}