Skip to content

Add main, test for Rail Fence Cipher and update README.md #5761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
* [PlayfairCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/PlayfairCipher.java)
* [Polybius](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/Polybius.java)
* [ProductCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/ProductCipher.java)
* [RailFenceCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java)
* [RSA](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/RSA.java)
* [SimpleSubCipher](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/SimpleSubCipher.java)
* [Vigenere](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/ciphers/Vigenere.java)
Expand Down Expand Up @@ -729,6 +730,7 @@
* [HillCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/HillCipherTest.java)
* [PlayfairTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/PlayfairTest.java)
* [PolybiusTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/PolybiusTest.java)
* [RailFenceTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/RailFenceTest.java)
* [RSATest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/RSATest.java)
* [SimpleSubCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/SimpleSubCipherTest.java)
* [VigenereTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/VigenereTest.java)
Expand Down
147 changes: 147 additions & 0 deletions src/main/java/com/thealgorithms/ciphers/RailFenceCipher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package com.thealgorithms.ciphers;

import java.util.Arrays;

/**
* The rail fence cipher (also called a zigzag cipher) is a classical type of transposition cipher.
* It derives its name from the manner in which encryption is performed, in analogy to a fence built with horizontal rails.
* https://en.wikipedia.org/wiki/Rail_fence_cipher
* @author https://github.com/Krounosity
*/

public class RailFenceCipher {

// Encrypts the input string using the rail fence cipher method with the given number of rails.
public String encrypt(String str, int rails) {

// Base case of single rail or rails are more than the number of characters in the string
if (rails == 1 || rails >= str.length()) {
return str;
}

// Boolean flag to determine if the movement is downward or upward in the rail matrix.
boolean down = true;
// Create a 2D array to represent the rails (rows) and the length of the string (columns).
char[][] strRail = new char[rails][str.length()];

// Initialize all positions in the rail matrix with a placeholder character ('\n').
for (int i = 0; i < rails; i++) {
Arrays.fill(strRail[i], '\n');
}

int row = 0; // Start at the first row
int col = 0; // Start at the first column

int i = 0;

// Fill the rail matrix with characters from the string based on the rail pattern.
while (col < str.length()) {
// Change direction to down when at the first row.
if (row == 0) {
down = true;
}
// Change direction to up when at the last row.
else if (row == rails - 1) {
down = false;
}

// Place the character in the current position of the rail matrix.
strRail[row][col] = str.charAt(i);
col++; // Move to the next column.
// Move to the next row based on the direction.
if (down) {
row++;
} else {
row--;
}

i++;
}

// Construct the encrypted string by reading characters row by row.
StringBuilder encryptedString = new StringBuilder();
for (char[] chRow : strRail) {
for (char ch : chRow) {
if (ch != '\n') {
encryptedString.append(ch);
}
}
}
return encryptedString.toString();
}
// Decrypts the input string using the rail fence cipher method with the given number of rails.
public String decrypt(String str, int rails) {

// Base case of single rail or rails are more than the number of characters in the string
if (rails == 1 || rails >= str.length()) {
return str;
}
// Boolean flag to determine if the movement is downward or upward in the rail matrix.
boolean down = true;

// Create a 2D array to represent the rails (rows) and the length of the string (columns).
char[][] strRail = new char[rails][str.length()];

int row = 0; // Start at the first row
int col = 0; // Start at the first column

// Mark the pattern on the rail matrix using '*'.
while (col < str.length()) {
// Change direction to down when at the first row.
if (row == 0) {
down = true;
}
// Change direction to up when at the last row.
else if (row == rails - 1) {
down = false;
}

// Mark the current position in the rail matrix.
strRail[row][col] = '*';
col++; // Move to the next column.
// Move to the next row based on the direction.
if (down) {
row++;
} else {
row--;
}
}

int index = 0; // Index to track characters from the input string.
// Fill the rail matrix with characters from the input string based on the marked pattern.
for (int i = 0; i < rails; i++) {
for (int j = 0; j < str.length(); j++) {
if (strRail[i][j] == '*') {
strRail[i][j] = str.charAt(index++);
}
}
}

// Construct the decrypted string by following the zigzag pattern.
StringBuilder decryptedString = new StringBuilder();
row = 0; // Reset to the first row
col = 0; // Reset to the first column

while (col < str.length()) {
// Change direction to down when at the first row.
if (row == 0) {
down = true;
}
// Change direction to up when at the last row.
else if (row == rails - 1) {
down = false;
}
// Append the character from the rail matrix to the decrypted string.
decryptedString.append(strRail[row][col]);
col++; // Move to the next column.
// Move to the next row based on the direction.
if (down) {
row++;
} else {
row--;
}
}

return decryptedString.toString();
}
}
62 changes: 62 additions & 0 deletions src/test/java/com/thealgorithms/ciphers/RailFenceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.thealgorithms.ciphers;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

public class RailFenceTest {

@Test
void testEncryption() {
RailFenceCipher cipher = new RailFenceCipher();

String input = "We are discovered! Flee at once";
int rails = 3;
String encrypted = cipher.encrypt(input, rails);
assertEquals("Wrivdlaneaedsoee!Fe toc cr e e", encrypted);

String singleChar = "A";
int singleRail = 2;
String encryptedSingleChar = cipher.encrypt(singleChar, singleRail);
assertEquals("A", encryptedSingleChar);

String shortString = "Hello";
int moreRails = 10;
String encryptedShortString = cipher.encrypt(shortString, moreRails);
assertEquals("Hello", encryptedShortString);

String inputSingleRail = "Single line";
int singleRailOnly = 1;
String encryptedSingleRail = cipher.encrypt(inputSingleRail, singleRailOnly);
assertEquals("Single line", encryptedSingleRail);
}

@Test
void testDecryption() {
RailFenceCipher cipher = new RailFenceCipher();

// Scenario 1: Basic decryption with multiple rails
String encryptedInput = "Wrivdlaneaedsoee!Fe toc cr e e";
int rails = 3;
String decrypted = cipher.decrypt(encryptedInput, rails);
assertEquals("We are discovered! Flee at once", decrypted);

// Scenario 2: Single character string decryption
String encryptedSingleChar = "A";
int singleRail = 2; // More than 1 rail
String decryptedSingleChar = cipher.decrypt(encryptedSingleChar, singleRail);
assertEquals("A", decryptedSingleChar);

// Scenario 3: String length less than the number of rails
String encryptedShortString = "Hello";
int moreRails = 10; // More rails than characters
String decryptedShortString = cipher.decrypt(encryptedShortString, moreRails);
assertEquals("Hello", decryptedShortString);

// Scenario 4: Single rail decryption (output should be the same as input)
String encryptedSingleRail = "Single line";
int singleRailOnly = 1;
String decryptedSingleRail = cipher.decrypt(encryptedSingleRail, singleRailOnly);
assertEquals("Single line", decryptedSingleRail);
}
}