Repository: TheAlgorithms/Java
Pull request #5508 (2024-10-01 16:41:23)

Created DiffieHellman.java and MonoAlphabetic.java 
<!--
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
-->
Description

This PR implements the Diffie-Hellman Key Exchange and MonoAlphabetic Cipher algorithms. 

Key Changes:

1. Diffie-Hellman Key Exchange:
   - Added `DiffieHellman.java` under the `ciphers` directory.
   - The implementation allows two users (Person A and Person B) to securely generate a shared secret key over an insecure channel using large prime numbers and modular arithmetic.
   - The code prompts the users to input their secret numbers and performs the necessary calculations to exchange and compute the shared secret keys.

2. MonoAlphabetic Cipher:
   - Added `MonoAlphabetic.java` under the `ciphers` directory.
   - The implementation allows the user to either **encrypt** or **decrypt** data using a predefined substitution cipher, where each letter of the alphabet is replaced with a corresponding letter.
   - The user is prompted to enter the data, and the cipher is applied to return either the encrypted or decrypted result.

Both algorithms are utility classes, with private constructors to prevent instantiation and static methods to handle their respective functionalities.


<!-- 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/5508?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) Report
Attention: Patch coverage is `0%` with `20 lines` in your changes missing coverage. Please review.
> Project coverage is 52.22%. Comparing base [(`7f60d57`)](https://app.codecov.io/gh/TheAlgorithms/Java/commit/7f60d57504ab8456b72057735a0dc93595ad847f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) to head [(`835783d`)](https://app.codecov.io/gh/TheAlgorithms/Java/commit/835783dca068c013aa3aeb2b767bf964d4e06606?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms).

| [Files with missing lines](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5508?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms) | Patch % | Lines |
|---|---|---|
| [.../java/com/thealgorithms/ciphers/DiffieHellman.java](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5508?src=pr&el=tree&filepath=src%2Fmain%2Fjava%2Fcom%2Fthealgorithms%2Fciphers%2FDiffieHellman.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TheAlgorithms#diff-c3JjL21haW4vamF2YS9jb20vdGhlYWxnb3JpdGhtcy9jaXBoZXJzL0RpZmZpZUhlbGxtYW4uamF2YQ==) | 0.00% | [20 Missing :warning: ](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5508?src=pr&el=tree&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    #5508      +/-   ##
============================================
- Coverage     52.28%   52.22%   -0.07%     
- Complexity     3256     3257       +1     
============================================
  Files           525      526       +1     
  Lines         15185    15205      +20     
  Branches       2887     2887              
============================================
+ Hits           7940     7941       +1     
- Misses         6920     6940      +20     
+ Partials        325      324       -1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/TheAlgorithms/Java/pull/5508?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).

riti2601: I am currently unable to build my code due to the outdated pom.xml file. The dependencies required for the JUnit tests are missing, leading to compilation errors. To proceed with testing and building my changes effectively, I recommend updating the pom.xml file to include the necessary JUnit dependencies.

Please let me know if there's a preferred way to address this issue or if I should modify the code to remove the dependencies temporarily.

Thank you!
vil02: Did you try to place the tests in the [`.../src/test/java/com/thealgorithms`](https://github.com/TheAlgorithms/Java/tree/842ff5294f5e97dfd1e4c09f9d9e01a89ac16e32/src/test/java/com/thealgorithms) directory? cf. #5501.
riti2601: No, I'll try it. 
Thank you
riti2601: Hi @vil02,
I've addressed all the requested changes and updated the pull request accordingly. Could you please review the latest changes at your convenience and let me know if anything else is needed?
Thank you! 
siriak: @vil02 please review
riti2601: Hi @vil02, kindly review the changed code.
riti2601: Hello @vil02
I am participating in Hacktoberfest'25 and request that you kindly check the PR for further merging. 
Thank you


Patch:
diff --git a/src/main/java/com/thealgorithms/ciphers/DiffieHellman.java b/src/main/java/com/thealgorithms/ciphers/DiffieHellman.java
new file mode 100644
index 00000000..7470b40e
--- /dev/null
+++ b/src/main/java/com/thealgorithms/ciphers/DiffieHellman.java
@@ -0,0 +1,36 @@
+package com.thealgorithms.ciphers;
+
+import java.math.BigInteger;
+
+public final class DiffieHellman {
+
+    private final BigInteger base;
+    private final BigInteger secret;
+    private final BigInteger prime;
+
+    // Constructor to initialize base, secret, and prime
+    public DiffieHellman(BigInteger base, BigInteger secret, BigInteger prime) {
+        // Check for non-null and positive values
+        if (base == null || secret == null || prime == null || base.signum() <= 0 || secret.signum() <= 0 || prime.signum() <= 0) {
+            throw new IllegalArgumentException("Base, secret, and prime must be non-null and positive values.");
+        }
+        this.base = base;
+        this.secret = secret;
+        this.prime = prime;
+    }
+
+    // Method to calculate public value (g^x mod p)
+    public BigInteger calculatePublicValue() {
+        // Returns g^x mod p
+        return base.modPow(secret, prime);
+    }
+
+    // Method to calculate the shared secret key (otherPublic^secret mod p)
+    public BigInteger calculateSharedSecret(BigInteger otherPublicValue) {
+        if (otherPublicValue == null || otherPublicValue.signum() <= 0) {
+            throw new IllegalArgumentException("Other public value must be non-null and positive.");
+        }
+        // Returns b^x mod p or a^y mod p
+        return otherPublicValue.modPow(secret, prime);
+    }
+}
diff --git a/src/main/java/com/thealgorithms/ciphers/MonoAlphabetic.java b/src/main/java/com/thealgorithms/ciphers/MonoAlphabetic.java
new file mode 100644
index 00000000..1d5b7110
--- /dev/null
+++ b/src/main/java/com/thealgorithms/ciphers/MonoAlphabetic.java
@@ -0,0 +1,48 @@
+package com.thealgorithms.ciphers;
+
+public final class MonoAlphabetic {
+
+    private MonoAlphabetic() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    // Encryption method
+    public static String encrypt(String data, String key) {
+        if (!data.matches("[A-Z]+")) {
+            throw new IllegalArgumentException("Input data contains invalid characters. Only uppercase A-Z are allowed.");
+        }
+        StringBuilder sb = new StringBuilder();
+
+        // Encrypt each character
+        for (char c : data.toCharArray()) {
+            int idx = charToPos(c); // Get the index of the character
+            sb.append(key.charAt(idx)); // Map to the corresponding character in the key
+        }
+        return sb.toString();
+    }
+
+    // Decryption method
+    public static String decrypt(String data, String key) {
+        StringBuilder sb = new StringBuilder();
+
+        // Decrypt each character
+        for (char c : data.toCharArray()) {
+            int idx = key.indexOf(c); // Find the index of the character in the key
+            if (idx == -1) {
+                throw new IllegalArgumentException("Input data contains invalid characters.");
+            }
+            sb.append(posToChar(idx)); // Convert the index back to the original character
+        }
+        return sb.toString();
+    }
+
+    // Helper method: Convert a character to its position in the alphabet
+    private static int charToPos(char c) {
+        return c - 'A'; // Subtract 'A' to get position (0 for A, 1 for B, etc.)
+    }
+
+    // Helper method: Convert a position in the alphabet to a character
+    private static char posToChar(int pos) {
+        return (char) (pos + 'A'); // Add 'A' to convert position back to character
+    }
+}


Test patch:
diff --git a/src/test/java/com/thealgorithms/ciphers/DiffieHellmanTest.java b/src/test/java/com/thealgorithms/ciphers/DiffieHellmanTest.java
new file mode 100644
index 00000000..6255ad22
--- /dev/null
+++ b/src/test/java/com/thealgorithms/ciphers/DiffieHellmanTest.java
@@ -0,0 +1,38 @@
+package com.thealgorithms.ciphers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.math.BigInteger;
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class DiffieHellmanTest {
+
+    // Test for public value calculation using instance methods
+    @ParameterizedTest
+    @MethodSource("provideTestData")
+    public void testCalculatePublicValue(BigInteger base, BigInteger secret, BigInteger prime, BigInteger publicExpected, BigInteger sharedExpected) {
+        DiffieHellman dh = new DiffieHellman(base, secret, prime); // Create an instance of DiffieHellman
+        assertEquals(publicExpected, dh.calculatePublicValue()); // Call instance method
+    }
+
+    // Test for shared secret calculation using instance methods
+    @ParameterizedTest
+    @MethodSource("provideTestData")
+    public void testCalculateSharedSecret(BigInteger base, BigInteger secret, BigInteger prime, BigInteger publicExpected, BigInteger sharedExpected) {
+        DiffieHellman dh = new DiffieHellman(base, secret, prime); // Create an instance of DiffieHellman
+        assertEquals(sharedExpected, dh.calculateSharedSecret(publicExpected)); // Call instance method
+    }
+
+    // Provide test data for both public key and shared secret calculation
+    private static Stream<Arguments> provideTestData() {
+        return Stream.of(createTestArgs(5, 6, 23, 8, 13), createTestArgs(2, 5, 13, 6, 2));
+    }
+
+    // Helper method for arguments
+    private static Arguments createTestArgs(long base, long secret, long prime, long publicExpected, long sharedExpected) {
+        return Arguments.of(BigInteger.valueOf(base), BigInteger.valueOf(secret), BigInteger.valueOf(prime), BigInteger.valueOf(publicExpected), BigInteger.valueOf(sharedExpected));
+    }
+}
diff --git a/src/test/java/com/thealgorithms/ciphers/MonoAlphabeticTest.java b/src/test/java/com/thealgorithms/ciphers/MonoAlphabeticTest.java
new file mode 100644
index 00000000..b1a8c78c
--- /dev/null
+++ b/src/test/java/com/thealgorithms/ciphers/MonoAlphabeticTest.java
@@ -0,0 +1,29 @@
+package com.thealgorithms.ciphers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class MonoAlphabeticTest {
+
+    // Test for both encryption and decryption with different keys
+    @ParameterizedTest
+    @MethodSource("provideTestData")
+    public void testEncryptDecrypt(String plainText, String key, String encryptedText) {
+        // Test encryption
+        String actualEncrypted = MonoAlphabetic.encrypt(plainText, key);
+        assertEquals(encryptedText, actualEncrypted, "Encryption failed for input: " + plainText + " with key: " + key);
+
+        // Test decryption
+        String actualDecrypted = MonoAlphabetic.decrypt(encryptedText, key);
+        assertEquals(plainText, actualDecrypted, "Decryption failed for input: " + encryptedText + " with key: " + key);
+    }
+
+    // Provide test data for both encryption and decryption
+    private static Stream<Arguments> provideTestData() {
+        return Stream.of(Arguments.of("HELLO", "MNBVCXZLKJHGFDSAPOIUYTREWQ", "LCGGS"), Arguments.of("JAVA", "MNBVCXZLKJHGFDSAPOIUYTREWQ", "JMTM"), Arguments.of("HELLO", "QWERTYUIOPLKJHGFDSAZXCVBNM", "ITKKG"), Arguments.of("JAVA", "QWERTYUIOPLKJHGFDSAZXCVBNM", "PQCQ"));
+    }
+}