1
1
package com .thealgorithms .ciphers ;
2
2
3
- import java .util .Arrays ;
4
3
import java .util .HashSet ;
5
4
import java .util .Set ;
6
5
7
6
/**
8
7
* A Java implementation of Permutation Cipher.
9
8
* It is a type of transposition cipher in which the plaintext is divided into blocks
10
9
* and the characters within each block are rearranged according to a fixed permutation key.
11
- *
10
+ *
12
11
* For example, with key {3, 1, 2} and plaintext "HELLO", the text is divided into blocks
13
12
* of 3 characters: "HEL" and "LO" (with padding). The characters are then rearranged
14
13
* according to the key positions.
15
- *
14
+ *
16
15
* @author GitHub Copilot
17
16
*/
18
17
public class PermutationCipher {
19
-
18
+
20
19
private static final char PADDING_CHAR = 'X' ;
21
-
20
+
22
21
/**
23
22
* Encrypts the given plaintext using the permutation cipher with the specified key.
24
- *
23
+ *
25
24
* @param plaintext the text to encrypt
26
25
* @param key the permutation key (array of integers representing positions)
27
26
* @return the encrypted text
28
27
* @throws IllegalArgumentException if the key is invalid
29
28
*/
30
29
public String encrypt (String plaintext , int [] key ) {
31
30
validateKey (key );
32
-
31
+
33
32
if (plaintext == null || plaintext .isEmpty ()) {
34
33
return plaintext ;
35
34
}
36
-
35
+
37
36
// Remove spaces and convert to uppercase for consistent processing
38
37
String cleanText = plaintext .replaceAll ("\\ s+" , "" ).toUpperCase ();
39
-
38
+
40
39
// Pad the text to make it divisible by key length
41
40
String paddedText = padText (cleanText , key .length );
42
-
41
+
43
42
StringBuilder encrypted = new StringBuilder ();
44
-
43
+
45
44
// Process text in blocks of key length
46
45
for (int i = 0 ; i < paddedText .length (); i += key .length ) {
47
46
String block = paddedText .substring (i , Math .min (i + key .length , paddedText .length ()));
48
47
encrypted .append (permuteBlock (block , key ));
49
48
}
50
-
49
+
51
50
return encrypted .toString ();
52
51
}
53
-
52
+
54
53
/**
55
54
* Decrypts the given ciphertext using the permutation cipher with the specified key.
56
- *
55
+ *
57
56
* @param ciphertext the text to decrypt
58
57
* @param key the permutation key (array of integers representing positions)
59
58
* @return the decrypted text
60
59
* @throws IllegalArgumentException if the key is invalid
61
60
*/
62
61
public String decrypt (String ciphertext , int [] key ) {
63
62
validateKey (key );
64
-
63
+
65
64
if (ciphertext == null || ciphertext .isEmpty ()) {
66
65
return ciphertext ;
67
66
}
68
-
67
+
69
68
// Create the inverse permutation
70
69
int [] inverseKey = createInverseKey (key );
71
-
70
+
72
71
StringBuilder decrypted = new StringBuilder ();
73
-
72
+
74
73
// Process text in blocks of key length
75
74
for (int i = 0 ; i < ciphertext .length (); i += key .length ) {
76
75
String block = ciphertext .substring (i , Math .min (i + key .length , ciphertext .length ()));
77
76
decrypted .append (permuteBlock (block , inverseKey ));
78
77
}
79
-
78
+
80
79
// Remove padding characters from the end
81
80
return removePadding (decrypted .toString ());
82
81
}
83
-
84
82
/**
85
83
* Validates that the permutation key is valid.
86
84
* A valid key must contain all integers from 1 to n exactly once, where n is the key length.
87
- *
85
+ *
88
86
* @param key the permutation key to validate
89
87
* @throws IllegalArgumentException if the key is invalid
90
88
*/
91
89
private void validateKey (int [] key ) {
92
90
if (key == null || key .length == 0 ) {
93
91
throw new IllegalArgumentException ("Key cannot be null or empty" );
94
92
}
95
-
93
+
96
94
Set <Integer > keySet = new HashSet <>();
97
95
for (int position : key ) {
98
96
if (position < 1 || position > key .length ) {
@@ -103,10 +101,10 @@ private void validateKey(int[] key) {
103
101
}
104
102
}
105
103
}
106
-
104
+
107
105
/**
108
106
* Pads the text with padding characters to make its length divisible by the block size.
109
- *
107
+ *
110
108
* @param text the text to pad
111
109
* @param blockSize the size of each block
112
110
* @return the padded text
@@ -116,19 +114,18 @@ private String padText(String text, int blockSize) {
116
114
if (remainder == 0 ) {
117
115
return text ;
118
116
}
119
-
117
+
120
118
int paddingNeeded = blockSize - remainder ;
121
119
StringBuilder padded = new StringBuilder (text );
122
120
for (int i = 0 ; i < paddingNeeded ; i ++) {
123
121
padded .append (PADDING_CHAR );
124
122
}
125
-
123
+
126
124
return padded .toString ();
127
125
}
128
-
129
126
/**
130
127
* Applies the permutation to a single block of text.
131
- *
128
+ *
132
129
* @param block the block to permute
133
130
* @param key the permutation key
134
131
* @return the permuted block
@@ -138,57 +135,57 @@ private String permuteBlock(String block, int[] key) {
138
135
// Handle case where block is shorter than key (shouldn't happen with proper padding)
139
136
block = padText (block , key .length );
140
137
}
141
-
138
+
142
139
char [] result = new char [key .length ];
143
140
char [] blockChars = block .toCharArray ();
144
-
141
+
145
142
for (int i = 0 ; i < key .length ; i ++) {
146
143
// Key positions are 1-based, so subtract 1 for 0-based array indexing
147
144
result [i ] = blockChars [key [i ] - 1 ];
148
145
}
149
-
146
+
150
147
return new String (result );
151
148
}
152
-
149
+
153
150
/**
154
151
* Creates the inverse permutation key for decryption.
155
- *
152
+ *
156
153
* @param key the original permutation key
157
154
* @return the inverse key
158
155
*/
159
156
private int [] createInverseKey (int [] key ) {
160
157
int [] inverse = new int [key .length ];
161
-
158
+
162
159
for (int i = 0 ; i < key .length ; i ++) {
163
160
// The inverse key maps each position to where it should go
164
161
inverse [key [i ] - 1 ] = i + 1 ;
165
162
}
166
-
163
+
167
164
return inverse ;
168
165
}
169
-
166
+
170
167
/**
171
168
* Removes padding characters from the end of the decrypted text.
172
- *
169
+ *
173
170
* @param text the text to remove padding from
174
171
* @return the text without padding
175
172
*/
176
173
private String removePadding (String text ) {
177
174
if (text .isEmpty ()) {
178
175
return text ;
179
176
}
180
-
177
+
181
178
int i = text .length () - 1 ;
182
179
while (i >= 0 && text .charAt (i ) == PADDING_CHAR ) {
183
180
i --;
184
181
}
185
-
182
+
186
183
return text .substring (0 , i + 1 );
187
184
}
188
-
185
+
189
186
/**
190
187
* Gets the padding character used by this cipher.
191
- *
188
+ *
192
189
* @return the padding character
193
190
*/
194
191
public char getPaddingChar () {
0 commit comments