Repository: TheAlgorithms/Java
Pull request #5899 (2024-10-19 13:17:31)

refactor: Enhance docs, add more tests in `Vigenere`
<!--
Thank you for your contribution!
In order to reduce the number of notifications sent to the maintainers, please:
- create your PR as draft, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests,
- make sure that all of the CI checks pass,
- mark your PR as ready for review, cf. https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request#marking-a-pull-request-as-ready-for-review
-->

<!-- For completed items, change [ ] to [x] -->

- [x] I have read [CONTRIBUTING.md](https://github.com/TheAlgorithms/Java/blob/master/CONTRIBUTING.md).
- [x] This pull request is all my own work -- I have not plagiarized it.
- [x] All filenames are in PascalCase.
- [x] All functions and variable names follow Java naming conventions.
- [x] All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.
- [x] All new code is formatted with `clang-format -i --style=file path/to/your/file.java`

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5899?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 66.79%. Comparing base [(`e499d3b`)](https://app.codecov.io/gh/TheAlgorithms/Java/commit/e499d3b63e9cb3dc711ddd5d4a2fc3faba9b3d46?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) to head [(`b5ccc11`)](https://app.codecov.io/gh/TheAlgorithms/Java/commit/b5ccc117550f587fbdf188169d4f03342e0d7f90?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms).

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##             master    #5899      +/-   ##
============================================
- Coverage     66.80%   66.79%   -0.01%     
- Complexity     4501     4502       +1     
============================================
  Files           611      611              
  Lines         16958    16962       +4     
  Branches       3275     3275              
============================================
+ Hits          11328    11330       +2     
- Misses         5182     5183       +1     
- Partials        448      449       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5899?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms).


Patch:
diff --git a/src/main/java/com/thealgorithms/ciphers/Vigenere.java b/src/main/java/com/thealgorithms/ciphers/Vigenere.java
index 1702f1ab..0f117853 100644
--- a/src/main/java/com/thealgorithms/ciphers/Vigenere.java
+++ b/src/main/java/com/thealgorithms/ciphers/Vigenere.java
@@ -1,16 +1,54 @@
 package com.thealgorithms.ciphers;
 
 /**
- * A Java implementation of Vigenere Cipher.
+ * A Java implementation of the Vigenère Cipher.
+ *
+ * The Vigenère Cipher is a polyalphabetic substitution cipher that uses a
+ * keyword to shift letters in the plaintext by different amounts, depending
+ * on the corresponding character in the keyword. It wraps around the alphabet,
+ * ensuring the shifts are within 'A'-'Z' or 'a'-'z'.
+ *
+ * Non-alphabetic characters (like spaces, punctuation) are kept unchanged.
+ *
+ * Encryption Example:
+ * - Plaintext: "Hello World!"
+ * - Key: "suchsecret"
+ * - Encrypted Text: "Zynsg Yfvev!"
+ *
+ * Decryption Example:
+ * - Ciphertext: "Zynsg Yfvev!"
+ * - Key: "suchsecret"
+ * - Decrypted Text: "Hello World!"
+ *
+ * Wikipedia Reference:
+ * <a href="https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher">Vigenère Cipher - Wikipedia</a>
  *
  * @author straiffix
  * @author beingmartinbmc
  */
 public class Vigenere {
 
+    /**
+     * Encrypts a given message using the Vigenère Cipher with the specified key.
+     * Steps:
+     * 1. Iterate over each character in the message.
+     * 2. If the character is a letter, shift it by the corresponding character in the key.
+     * 3. Preserve the case of the letter.
+     * 4. Preserve non-alphabetic characters.
+     * 5. Move to the next character in the key (cyclic).
+     * 6. Return the encrypted message.
+     *
+     * @param message The plaintext message to encrypt.
+     * @param key The keyword used for encryption.
+     * @throws IllegalArgumentException if the key is empty.
+     * @return The encrypted message.
+     */
     public String encrypt(final String message, final String key) {
-        StringBuilder result = new StringBuilder();
+        if (key.isEmpty()) {
+            throw new IllegalArgumentException("Key cannot be empty.");
+        }
 
+        StringBuilder result = new StringBuilder();
         int j = 0;
         for (int i = 0; i < message.length(); i++) {
             char c = message.charAt(i);
@@ -20,17 +58,35 @@ public class Vigenere {
                 } else {
                     result.append((char) ((c + key.toLowerCase().charAt(j) - 2 * 'a') % 26 + 'a'));
                 }
+                j = ++j % key.length();
             } else {
                 result.append(c);
             }
-            j = ++j % key.length();
         }
         return result.toString();
     }
 
+    /**
+     * Decrypts a given message encrypted with the Vigenère Cipher using the specified key.
+     * Steps:
+     * 1. Iterate over each character in the message.
+     * 2. If the character is a letter, shift it back by the corresponding character in the key.
+     * 3. Preserve the case of the letter.
+     * 4. Preserve non-alphabetic characters.
+     * 5. Move to the next character in the key (cyclic).
+     * 6. Return the decrypted message.
+     *
+     * @param message The encrypted message to decrypt.
+     * @param key The keyword used for decryption.
+     * @throws IllegalArgumentException if the key is empty.
+     * @return The decrypted plaintext message.
+     */
     public String decrypt(final String message, final String key) {
-        StringBuilder result = new StringBuilder();
+        if (key.isEmpty()) {
+            throw new IllegalArgumentException("Key cannot be empty.");
+        }
 
+        StringBuilder result = new StringBuilder();
         int j = 0;
         for (int i = 0; i < message.length(); i++) {
             char c = message.charAt(i);
@@ -40,11 +96,10 @@ public class Vigenere {
                 } else {
                     result.append((char) ('z' - (25 - (c - key.toLowerCase().charAt(j))) % 26));
                 }
+                j = ++j % key.length();
             } else {
                 result.append(c);
             }
-
-            j = ++j % key.length();
         }
         return result.toString();
     }


Test patch:
diff --git a/src/test/java/com/thealgorithms/ciphers/VigenereTest.java b/src/test/java/com/thealgorithms/ciphers/VigenereTest.java
index c5935de9..7f94e573 100644
--- a/src/test/java/com/thealgorithms/ciphers/VigenereTest.java
+++ b/src/test/java/com/thealgorithms/ciphers/VigenereTest.java
@@ -1,6 +1,7 @@
 package com.thealgorithms.ciphers;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import org.junit.jupiter.api.Test;
 
@@ -9,28 +10,71 @@ class VigenereTest {
     Vigenere vigenere = new Vigenere();
 
     @Test
-    void vigenereEncryptTest() {
-        // given
+    void testVigenereEncryptDecrypt() {
         String text = "Hello World!";
         String key = "suchsecret";
 
-        // when
-        String cipherText = vigenere.encrypt(text, key);
+        String encryptedText = vigenere.encrypt(text, key);
+        String decryptedText = vigenere.decrypt(encryptedText, key);
 
-        // then
-        assertEquals("Zynsg Yfvev!", cipherText);
+        assertEquals("Zynsg Aqipw!", encryptedText);
+        assertEquals("Hello World!", decryptedText);
     }
 
     @Test
-    void vigenereDecryptTest() {
-        // given
-        String encryptedText = "Zynsg Yfvev!";
-        String key = "suchsecret";
+    void testWithEmptyMessage() {
+        String text = "";
+        String key = "anykey";
 
-        // when
+        String encryptedText = vigenere.encrypt(text, key);
         String decryptedText = vigenere.decrypt(encryptedText, key);
 
-        // then
-        assertEquals("Hello World!", decryptedText);
+        assertEquals("", encryptedText);
+        assertEquals("", decryptedText);
+    }
+
+    @Test
+    void testWithEmptyKey() {
+        String text = "This should remain the same";
+        String key = "";
+
+        assertThrows(IllegalArgumentException.class, () -> vigenere.encrypt(text, key));
+        assertThrows(IllegalArgumentException.class, () -> vigenere.decrypt(text, key));
+    }
+
+    @Test
+    void testWithNumbersInMessage() {
+        String text = "Vigenere123!";
+        String key = "cipher";
+
+        String encryptedText = vigenere.encrypt(text, key);
+        String decryptedText = vigenere.decrypt(encryptedText, key);
+
+        assertEquals("Xqvlrvtm123!", encryptedText);
+        assertEquals(text, decryptedText);
+    }
+
+    @Test
+    void testLongerKeyThanMessage() {
+        String text = "Short";
+        String key = "VeryLongSecretKey";
+
+        String encryptedText = vigenere.encrypt(text, key);
+        String decryptedText = vigenere.decrypt(encryptedText, key);
+
+        assertEquals("Nlfpe", encryptedText);
+        assertEquals(text, decryptedText);
+    }
+
+    @Test
+    void testUppercaseMessageAndKey() {
+        String text = "HELLO";
+        String key = "SECRET";
+
+        String encryptedText = vigenere.encrypt(text, key);
+        String decryptedText = vigenere.decrypt(encryptedText, key);
+
+        assertEquals("ZINCS", encryptedText);
+        assertEquals(text, decryptedText);
     }
 }