Skip to content

Commit b35f98a

Browse files
authored
Add Rail Fence Cipher (TheAlgorithms#5761)
1 parent 3e10f8f commit b35f98a

File tree

3 files changed

+211
-0
lines changed

3 files changed

+211
-0
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
* [PlayfairCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/PlayfairCipher.java)
7474
* [Polybius](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/Polybius.java)
7575
* [ProductCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/ProductCipher.java)
76+
* [RailFenceCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java)
7677
* [RSA](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/RSA.java)
7778
* [SimpleSubCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/SimpleSubCipher.java)
7879
* [Vigenere](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/Vigenere.java)
@@ -729,6 +730,7 @@
729730
* [HillCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/HillCipherTest.java)
730731
* [PlayfairTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/PlayfairTest.java)
731732
* [PolybiusTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/PolybiusTest.java)
733+
* [RailFenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/RailFenceTest.java)
732734
* [RSATest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/RSATest.java)
733735
* [SimpleSubCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/SimpleSubCipherTest.java)
734736
* [VigenereTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/VigenereTest.java)
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package com.thealgorithms.ciphers;
2+
3+
import java.util.Arrays;
4+
5+
/**
6+
* The rail fence cipher (also called a zigzag cipher) is a classical type of transposition cipher.
7+
* It derives its name from the manner in which encryption is performed, in analogy to a fence built with horizontal rails.
8+
* https://en.wikipedia.org/wiki/Rail_fence_cipher
9+
* @author https://github.com/Krounosity
10+
*/
11+
12+
public class RailFenceCipher {
13+
14+
// Encrypts the input string using the rail fence cipher method with the given number of rails.
15+
public String encrypt(String str, int rails) {
16+
17+
// Base case of single rail or rails are more than the number of characters in the string
18+
if (rails == 1 || rails >= str.length()) {
19+
return str;
20+
}
21+
22+
// Boolean flag to determine if the movement is downward or upward in the rail matrix.
23+
boolean down = true;
24+
// Create a 2D array to represent the rails (rows) and the length of the string (columns).
25+
char[][] strRail = new char[rails][str.length()];
26+
27+
// Initialize all positions in the rail matrix with a placeholder character ('\n').
28+
for (int i = 0; i < rails; i++) {
29+
Arrays.fill(strRail[i], '\n');
30+
}
31+
32+
int row = 0; // Start at the first row
33+
int col = 0; // Start at the first column
34+
35+
int i = 0;
36+
37+
// Fill the rail matrix with characters from the string based on the rail pattern.
38+
while (col < str.length()) {
39+
// Change direction to down when at the first row.
40+
if (row == 0) {
41+
down = true;
42+
}
43+
// Change direction to up when at the last row.
44+
else if (row == rails - 1) {
45+
down = false;
46+
}
47+
48+
// Place the character in the current position of the rail matrix.
49+
strRail[row][col] = str.charAt(i);
50+
col++; // Move to the next column.
51+
// Move to the next row based on the direction.
52+
if (down) {
53+
row++;
54+
} else {
55+
row--;
56+
}
57+
58+
i++;
59+
}
60+
61+
// Construct the encrypted string by reading characters row by row.
62+
StringBuilder encryptedString = new StringBuilder();
63+
for (char[] chRow : strRail) {
64+
for (char ch : chRow) {
65+
if (ch != '\n') {
66+
encryptedString.append(ch);
67+
}
68+
}
69+
}
70+
return encryptedString.toString();
71+
}
72+
// Decrypts the input string using the rail fence cipher method with the given number of rails.
73+
public String decrypt(String str, int rails) {
74+
75+
// Base case of single rail or rails are more than the number of characters in the string
76+
if (rails == 1 || rails >= str.length()) {
77+
return str;
78+
}
79+
// Boolean flag to determine if the movement is downward or upward in the rail matrix.
80+
boolean down = true;
81+
82+
// Create a 2D array to represent the rails (rows) and the length of the string (columns).
83+
char[][] strRail = new char[rails][str.length()];
84+
85+
int row = 0; // Start at the first row
86+
int col = 0; // Start at the first column
87+
88+
// Mark the pattern on the rail matrix using '*'.
89+
while (col < str.length()) {
90+
// Change direction to down when at the first row.
91+
if (row == 0) {
92+
down = true;
93+
}
94+
// Change direction to up when at the last row.
95+
else if (row == rails - 1) {
96+
down = false;
97+
}
98+
99+
// Mark the current position in the rail matrix.
100+
strRail[row][col] = '*';
101+
col++; // Move to the next column.
102+
// Move to the next row based on the direction.
103+
if (down) {
104+
row++;
105+
} else {
106+
row--;
107+
}
108+
}
109+
110+
int index = 0; // Index to track characters from the input string.
111+
// Fill the rail matrix with characters from the input string based on the marked pattern.
112+
for (int i = 0; i < rails; i++) {
113+
for (int j = 0; j < str.length(); j++) {
114+
if (strRail[i][j] == '*') {
115+
strRail[i][j] = str.charAt(index++);
116+
}
117+
}
118+
}
119+
120+
// Construct the decrypted string by following the zigzag pattern.
121+
StringBuilder decryptedString = new StringBuilder();
122+
row = 0; // Reset to the first row
123+
col = 0; // Reset to the first column
124+
125+
while (col < str.length()) {
126+
// Change direction to down when at the first row.
127+
if (row == 0) {
128+
down = true;
129+
}
130+
// Change direction to up when at the last row.
131+
else if (row == rails - 1) {
132+
down = false;
133+
}
134+
// Append the character from the rail matrix to the decrypted string.
135+
decryptedString.append(strRail[row][col]);
136+
col++; // Move to the next column.
137+
// Move to the next row based on the direction.
138+
if (down) {
139+
row++;
140+
} else {
141+
row--;
142+
}
143+
}
144+
145+
return decryptedString.toString();
146+
}
147+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.thealgorithms.ciphers;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
public class RailFenceTest {
8+
9+
@Test
10+
void testEncryption() {
11+
RailFenceCipher cipher = new RailFenceCipher();
12+
13+
String input = "We are discovered! Flee at once";
14+
int rails = 3;
15+
String encrypted = cipher.encrypt(input, rails);
16+
assertEquals("Wrivdlaneaedsoee!Fe toc cr e e", encrypted);
17+
18+
String singleChar = "A";
19+
int singleRail = 2;
20+
String encryptedSingleChar = cipher.encrypt(singleChar, singleRail);
21+
assertEquals("A", encryptedSingleChar);
22+
23+
String shortString = "Hello";
24+
int moreRails = 10;
25+
String encryptedShortString = cipher.encrypt(shortString, moreRails);
26+
assertEquals("Hello", encryptedShortString);
27+
28+
String inputSingleRail = "Single line";
29+
int singleRailOnly = 1;
30+
String encryptedSingleRail = cipher.encrypt(inputSingleRail, singleRailOnly);
31+
assertEquals("Single line", encryptedSingleRail);
32+
}
33+
34+
@Test
35+
void testDecryption() {
36+
RailFenceCipher cipher = new RailFenceCipher();
37+
38+
// Scenario 1: Basic decryption with multiple rails
39+
String encryptedInput = "Wrivdlaneaedsoee!Fe toc cr e e";
40+
int rails = 3;
41+
String decrypted = cipher.decrypt(encryptedInput, rails);
42+
assertEquals("We are discovered! Flee at once", decrypted);
43+
44+
// Scenario 2: Single character string decryption
45+
String encryptedSingleChar = "A";
46+
int singleRail = 2; // More than 1 rail
47+
String decryptedSingleChar = cipher.decrypt(encryptedSingleChar, singleRail);
48+
assertEquals("A", decryptedSingleChar);
49+
50+
// Scenario 3: String length less than the number of rails
51+
String encryptedShortString = "Hello";
52+
int moreRails = 10; // More rails than characters
53+
String decryptedShortString = cipher.decrypt(encryptedShortString, moreRails);
54+
assertEquals("Hello", decryptedShortString);
55+
56+
// Scenario 4: Single rail decryption (output should be the same as input)
57+
String encryptedSingleRail = "Single line";
58+
int singleRailOnly = 1;
59+
String decryptedSingleRail = cipher.decrypt(encryptedSingleRail, singleRailOnly);
60+
assertEquals("Single line", decryptedSingleRail);
61+
}
62+
}

0 commit comments

Comments
 (0)