diff --git a/DIRECTORY.md b/DIRECTORY.md index 4b98173a4..d8b1045b5 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -637,6 +637,7 @@ * [A5KeyStreamGeneratorTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/a5/A5KeyStreamGeneratorTest.java) * [LFSRTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/a5/LFSRTest.java) * [AESEncryptionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/AESEncryptionTest.java) + * [AffineCipherTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/AffineCipherTest.java) * [AutokeyTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/AutokeyTest.java) * [BlowfishTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/BlowfishTest.java) * [CaesarTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/ciphers/CaesarTest.java) diff --git a/src/main/java/com/thealgorithms/ciphers/AffineCipher.java b/src/main/java/com/thealgorithms/ciphers/AffineCipher.java index bcf3a5b01..636323b63 100644 --- a/src/main/java/com/thealgorithms/ciphers/AffineCipher.java +++ b/src/main/java/com/thealgorithms/ciphers/AffineCipher.java @@ -1,5 +1,23 @@ package com.thealgorithms.ciphers; +/** + * The AffineCipher class implements the Affine cipher, a type of monoalphabetic substitution cipher. + * It encrypts and decrypts messages using a linear transformation defined by the formula: + * + * E(x) = (a * x + b) mod m + * D(y) = a^-1 * (y - b) mod m + * + * where: + * - E(x) is the encrypted character, + * - D(y) is the decrypted character, + * - a is the multiplicative key (must be coprime to m), + * - b is the additive key, + * - x is the index of the plaintext character, + * - y is the index of the ciphertext character, + * - m is the size of the alphabet (26 for the English alphabet). + * + * The class provides methods for encrypting and decrypting messages, as well as a main method to demonstrate its usage. + */ final class AffineCipher { private AffineCipher() { } @@ -8,16 +26,22 @@ final class AffineCipher { static int a = 17; static int b = 20; + /** + * Encrypts a message using the Affine cipher. + * + * @param msg the plaintext message as a character array + * @return the encrypted ciphertext + */ static String encryptMessage(char[] msg) { - /// Cipher Text initially empty + // Cipher Text initially empty String cipher = ""; for (int i = 0; i < msg.length; i++) { // Avoid space to be encrypted - /* applying encryption formula ( a x + b ) mod m + /* applying encryption formula ( a * x + b ) mod m {here x is msg[i] and m is 26} and added 'A' to - bring it in range of ascii alphabet[ 65-90 | A-Z ] */ + bring it in the range of ASCII alphabet [65-90 | A-Z] */ if (msg[i] != ' ') { - cipher = cipher + (char) ((((a * (msg[i] - 'A')) + b) % 26) + 'A'); + cipher += (char) ((((a * (msg[i] - 'A')) + b) % 26) + 'A'); } else { // else simply append space character cipher += msg[i]; } @@ -25,28 +49,33 @@ final class AffineCipher { return cipher; } + /** + * Decrypts a ciphertext using the Affine cipher. + * + * @param cipher the ciphertext to decrypt + * @return the decrypted plaintext message + */ static String decryptCipher(String cipher) { String msg = ""; int aInv = 0; - int flag = 0; + int flag; - // Find a^-1 (the multiplicative inverse of a - // in the group of integers modulo m.) + // Find a^-1 (the multiplicative inverse of a in the group of integers modulo m.) for (int i = 0; i < 26; i++) { flag = (a * i) % 26; - // Check if (a*i)%26 == 1, + // Check if (a * i) % 26 == 1, // then i will be the multiplicative inverse of a if (flag == 1) { aInv = i; } } for (int i = 0; i < cipher.length(); i++) { - /*Applying decryption formula a^-1 ( x - b ) mod m + /* Applying decryption formula a^-1 * (x - b) mod m {here x is cipher[i] and m is 26} and added 'A' - to bring it in range of ASCII alphabet[ 65-90 | A-Z ] */ + to bring it in the range of ASCII alphabet [65-90 | A-Z] */ if (cipher.charAt(i) != ' ') { - msg = msg + (char) (((aInv * ((cipher.charAt(i) + 'A' - b)) % 26)) + 'A'); + msg += (char) (((aInv * ((cipher.charAt(i) - 'A') - b + 26)) % 26) + 'A'); } else { // else simply append space character msg += cipher.charAt(i); } diff --git a/src/test/java/com/thealgorithms/ciphers/AffineCipherTest.java b/src/test/java/com/thealgorithms/ciphers/AffineCipherTest.java new file mode 100644 index 000000000..b1a2ce593 --- /dev/null +++ b/src/test/java/com/thealgorithms/ciphers/AffineCipherTest.java @@ -0,0 +1,39 @@ +package com.thealgorithms.ciphers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class AffineCipherTest { + + @Test + public void testEncryptMessage() { + String plaintext = "AFFINE CIPHER"; + char[] msg = plaintext.toCharArray(); + String expectedCiphertext = "UBBAHK CAPJKX"; // Expected ciphertext after encryption + + String actualCiphertext = AffineCipher.encryptMessage(msg); + assertEquals(expectedCiphertext, actualCiphertext, "The encryption result should match the expected ciphertext."); + } + + @Test + public void testEncryptDecrypt() { + String plaintext = "HELLO WORLD"; + char[] msg = plaintext.toCharArray(); + + String ciphertext = AffineCipher.encryptMessage(msg); + String decryptedText = AffineCipher.decryptCipher(ciphertext); + + assertEquals(plaintext, decryptedText, "Decrypted text should match the original plaintext."); + } + + @Test + public void testSpacesHandledInEncryption() { + String plaintext = "HELLO WORLD"; + char[] msg = plaintext.toCharArray(); + String expectedCiphertext = "JKZZY EYXZT"; + + String actualCiphertext = AffineCipher.encryptMessage(msg); + assertEquals(expectedCiphertext, actualCiphertext, "The encryption should handle spaces correctly."); + } +}