11package com .thealgorithms .ciphers ;
22
3- import java .util .Arrays ;
43import java .util .HashSet ;
54import java .util .Set ;
65
76/**
87 * A Java implementation of Permutation Cipher.
98 * It is a type of transposition cipher in which the plaintext is divided into blocks
109 * and the characters within each block are rearranged according to a fixed permutation key.
11- *
10+ *
1211 * For example, with key {3, 1, 2} and plaintext "HELLO", the text is divided into blocks
1312 * of 3 characters: "HEL" and "LO" (with padding). The characters are then rearranged
1413 * according to the key positions.
15- *
14+ *
1615 * @author GitHub Copilot
1716 */
1817public class PermutationCipher {
19-
18+
2019 private static final char PADDING_CHAR = 'X' ;
21-
20+
2221 /**
2322 * Encrypts the given plaintext using the permutation cipher with the specified key.
24- *
23+ *
2524 * @param plaintext the text to encrypt
2625 * @param key the permutation key (array of integers representing positions)
2726 * @return the encrypted text
2827 * @throws IllegalArgumentException if the key is invalid
2928 */
3029 public String encrypt (String plaintext , int [] key ) {
3130 validateKey (key );
32-
31+
3332 if (plaintext == null || plaintext .isEmpty ()) {
3433 return plaintext ;
3534 }
36-
35+
3736 // Remove spaces and convert to uppercase for consistent processing
3837 String cleanText = plaintext .replaceAll ("\\ s+" , "" ).toUpperCase ();
39-
38+
4039 // Pad the text to make it divisible by key length
4140 String paddedText = padText (cleanText , key .length );
42-
41+
4342 StringBuilder encrypted = new StringBuilder ();
44-
43+
4544 // Process text in blocks of key length
4645 for (int i = 0 ; i < paddedText .length (); i += key .length ) {
4746 String block = paddedText .substring (i , Math .min (i + key .length , paddedText .length ()));
4847 encrypted .append (permuteBlock (block , key ));
4948 }
50-
49+
5150 return encrypted .toString ();
5251 }
53-
52+
5453 /**
5554 * Decrypts the given ciphertext using the permutation cipher with the specified key.
56- *
55+ *
5756 * @param ciphertext the text to decrypt
5857 * @param key the permutation key (array of integers representing positions)
5958 * @return the decrypted text
6059 * @throws IllegalArgumentException if the key is invalid
6160 */
6261 public String decrypt (String ciphertext , int [] key ) {
6362 validateKey (key );
64-
63+
6564 if (ciphertext == null || ciphertext .isEmpty ()) {
6665 return ciphertext ;
6766 }
68-
67+
6968 // Create the inverse permutation
7069 int [] inverseKey = createInverseKey (key );
71-
70+
7271 StringBuilder decrypted = new StringBuilder ();
73-
72+
7473 // Process text in blocks of key length
7574 for (int i = 0 ; i < ciphertext .length (); i += key .length ) {
7675 String block = ciphertext .substring (i , Math .min (i + key .length , ciphertext .length ()));
7776 decrypted .append (permuteBlock (block , inverseKey ));
7877 }
79-
78+
8079 // Remove padding characters from the end
8180 return removePadding (decrypted .toString ());
8281 }
83-
8482 /**
8583 * Validates that the permutation key is valid.
8684 * A valid key must contain all integers from 1 to n exactly once, where n is the key length.
87- *
85+ *
8886 * @param key the permutation key to validate
8987 * @throws IllegalArgumentException if the key is invalid
9088 */
9189 private void validateKey (int [] key ) {
9290 if (key == null || key .length == 0 ) {
9391 throw new IllegalArgumentException ("Key cannot be null or empty" );
9492 }
95-
93+
9694 Set <Integer > keySet = new HashSet <>();
9795 for (int position : key ) {
9896 if (position < 1 || position > key .length ) {
@@ -103,10 +101,10 @@ private void validateKey(int[] key) {
103101 }
104102 }
105103 }
106-
104+
107105 /**
108106 * Pads the text with padding characters to make its length divisible by the block size.
109- *
107+ *
110108 * @param text the text to pad
111109 * @param blockSize the size of each block
112110 * @return the padded text
@@ -116,19 +114,18 @@ private String padText(String text, int blockSize) {
116114 if (remainder == 0 ) {
117115 return text ;
118116 }
119-
117+
120118 int paddingNeeded = blockSize - remainder ;
121119 StringBuilder padded = new StringBuilder (text );
122120 for (int i = 0 ; i < paddingNeeded ; i ++) {
123121 padded .append (PADDING_CHAR );
124122 }
125-
123+
126124 return padded .toString ();
127125 }
128-
129126 /**
130127 * Applies the permutation to a single block of text.
131- *
128+ *
132129 * @param block the block to permute
133130 * @param key the permutation key
134131 * @return the permuted block
@@ -138,57 +135,57 @@ private String permuteBlock(String block, int[] key) {
138135 // Handle case where block is shorter than key (shouldn't happen with proper padding)
139136 block = padText (block , key .length );
140137 }
141-
138+
142139 char [] result = new char [key .length ];
143140 char [] blockChars = block .toCharArray ();
144-
141+
145142 for (int i = 0 ; i < key .length ; i ++) {
146143 // Key positions are 1-based, so subtract 1 for 0-based array indexing
147144 result [i ] = blockChars [key [i ] - 1 ];
148145 }
149-
146+
150147 return new String (result );
151148 }
152-
149+
153150 /**
154151 * Creates the inverse permutation key for decryption.
155- *
152+ *
156153 * @param key the original permutation key
157154 * @return the inverse key
158155 */
159156 private int [] createInverseKey (int [] key ) {
160157 int [] inverse = new int [key .length ];
161-
158+
162159 for (int i = 0 ; i < key .length ; i ++) {
163160 // The inverse key maps each position to where it should go
164161 inverse [key [i ] - 1 ] = i + 1 ;
165162 }
166-
163+
167164 return inverse ;
168165 }
169-
166+
170167 /**
171168 * Removes padding characters from the end of the decrypted text.
172- *
169+ *
173170 * @param text the text to remove padding from
174171 * @return the text without padding
175172 */
176173 private String removePadding (String text ) {
177174 if (text .isEmpty ()) {
178175 return text ;
179176 }
180-
177+
181178 int i = text .length () - 1 ;
182179 while (i >= 0 && text .charAt (i ) == PADDING_CHAR ) {
183180 i --;
184181 }
185-
182+
186183 return text .substring (0 , i + 1 );
187184 }
188-
185+
189186 /**
190187 * Gets the padding character used by this cipher.
191- *
188+ *
192189 * @return the padding character
193190 */
194191 public char getPaddingChar () {
0 commit comments