1
+ package com .thealgorithms .ciphers ;
2
+
3
+ import java .util .Arrays ;
4
+ import java .util .HashMap ;
5
+ import java .util .Map ;
6
+ /**
7
+ * The ADFGVX cipher is a historically significant cipher used by
8
+ * the German Army during World War I. It is a fractionating transposition
9
+ * cipher that combines a Polybius square substitution with a columnar
10
+ * transposition. It's named after the six letters (A, D, F, G, V, X)
11
+ * that it uses in its substitution process.
12
+ * https://en.wikipedia.org/wiki/ADFGVX_cipher
13
+ *
14
+ * @author bennybebo
15
+ */
16
+ public class ADFGVXCipher {
17
+
18
+ private static final char [] POLYBIUS_LETTERS = {'A' , 'D' , 'F' , 'G' , 'V' , 'X' };
19
+ private static final char [][] POLYBIUS_SQUARE = {
20
+ {'P' , 'H' , '0' , 'Q' , 'G' , '6' },
21
+ {'4' , 'M' , 'E' , 'A' , '1' , 'Y' },
22
+ {'L' , '2' , 'N' , 'O' , 'F' , 'D' },
23
+ {'X' , 'K' , 'R' , '3' , 'C' , 'V' },
24
+ {'S' , '5' , 'Z' , 'W' , '7' , 'B' },
25
+ {'J' , '9' , 'U' , 'T' , 'I' , '8' }
26
+ };
27
+ private static final Map <String , Character > POLYBIUS_MAP = new HashMap <>();
28
+ private static final Map <Character , String > REVERSE_POLYBIUS_MAP = new HashMap <>();
29
+
30
+ static {
31
+ for (int i = 0 ; i < POLYBIUS_SQUARE .length ; i ++) {
32
+ for (int j = 0 ; j < POLYBIUS_SQUARE [i ].length ; j ++) {
33
+ String key = "" + POLYBIUS_LETTERS [i ] + POLYBIUS_LETTERS [j ];
34
+ POLYBIUS_MAP .put (key , POLYBIUS_SQUARE [i ][j ]);
35
+ REVERSE_POLYBIUS_MAP .put (POLYBIUS_SQUARE [i ][j ], key );
36
+ }
37
+ }
38
+ }
39
+
40
+ // Encrypts the plaintext using the ADFGVX cipher
41
+ public String encrypt (String plaintext , String key ) {
42
+ plaintext = plaintext .toUpperCase ().replaceAll ("[^A-Z0-9]" , "" );
43
+ StringBuilder fractionatedText = new StringBuilder ();
44
+
45
+ // Step 1: Polybius square substitution
46
+ for (char c : plaintext .toCharArray ()) {
47
+ fractionatedText .append (REVERSE_POLYBIUS_MAP .get (c ));
48
+ }
49
+
50
+ // Step 2: Columnar transposition
51
+ return columnarTransposition (fractionatedText .toString (), key );
52
+ }
53
+
54
+ // Decrypts the ciphertext using the ADFGVX cipher
55
+ public String decrypt (String ciphertext , String key ) {
56
+ // Step 1: Reverse the columnar transposition
57
+ String fractionatedText = reverseColumnarTransposition (ciphertext , key );
58
+
59
+ // Step 2: Polybius square substitution
60
+ StringBuilder plaintext = new StringBuilder ();
61
+ for (int i = 0 ; i < fractionatedText .length (); i += 2 ) {
62
+ String pair = fractionatedText .substring (i , i + 2 );
63
+ plaintext .append (POLYBIUS_MAP .get (pair ));
64
+ }
65
+
66
+ return plaintext .toString ();
67
+ }
68
+
69
+ private String columnarTransposition (String text , String key ) {
70
+ int numRows = (int ) Math .ceil ((double ) text .length () / key .length ());
71
+ char [][] table = new char [numRows ][key .length ()];
72
+ for (char [] row : table ) {
73
+ Arrays .fill (row , '_' ); // Fill with underscores to handle empty cells
74
+ }
75
+
76
+ // Fill the table row by row
77
+ for (int i = 0 ; i < text .length (); i ++) {
78
+ table [i / key .length ()][i % key .length ()] = text .charAt (i );
79
+ }
80
+
81
+ // Read columns based on the alphabetical order of the key
82
+ StringBuilder ciphertext = new StringBuilder ();
83
+ char [] sortedKey = key .toCharArray ();
84
+ Arrays .sort (sortedKey );
85
+
86
+ for (char keyChar : sortedKey ) {
87
+ int column = key .indexOf (keyChar );
88
+ for (char [] row : table ) {
89
+ if (row [column ] != '_' ) {
90
+ ciphertext .append (row [column ]);
91
+ }
92
+ }
93
+ }
94
+
95
+ return ciphertext .toString ();
96
+ }
97
+
98
+ private String reverseColumnarTransposition (String ciphertext , String key ) {
99
+ int numRows = (int ) Math .ceil ((double ) ciphertext .length () / key .length ());
100
+ char [][] table = new char [numRows ][key .length ()];
101
+
102
+ char [] sortedKey = key .toCharArray ();
103
+ Arrays .sort (sortedKey );
104
+
105
+ int index = 0 ;
106
+ // Fill the table column by column according to the sorted key order
107
+ for (char keyChar : sortedKey ) {
108
+ int column = key .indexOf (keyChar );
109
+ for (int row = 0 ; row < numRows ; row ++) {
110
+ if (index < ciphertext .length ()) {
111
+ table [row ][column ] = ciphertext .charAt (index ++);
112
+ } else {
113
+ table [row ][column ] = '_' ; // Fill empty cells with an underscore
114
+ }
115
+ }
116
+ }
117
+
118
+ // Read the table row by row to get the fractionated text
119
+ StringBuilder fractionatedText = new StringBuilder ();
120
+ for (char [] row : table ) {
121
+ for (char cell : row ) {
122
+ if (cell != '_' ) {
123
+ fractionatedText .append (cell );
124
+ }
125
+ }
126
+ }
127
+
128
+ return fractionatedText .toString ();
129
+ }
130
+ }
0 commit comments