Skip to content

Commit 3b665ed

Browse files
committed
fix: add mock tests for edge cases
1 parent 58fc874 commit 3b665ed

File tree

2 files changed

+67
-16
lines changed

2 files changed

+67
-16
lines changed

src/main/java/com/thealgorithms/maths/Prime/EulerPseudoprime.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ public static boolean isProbablePrime(BigInteger n, int trials) {
4545
for (int i = 0; i < trials; i++) {
4646
BigInteger a = uniformRandom(BigInteger.TWO, n.subtract(BigInteger.TWO));
4747
BigInteger jacobi = BigInteger.valueOf(jacobiSymbol(a, n));
48-
if (jacobi.equals(BigInteger.ZERO)) return false;
48+
if (jacobi.equals(BigInteger.ZERO)) {
49+
return false;
50+
}
4951

5052
BigInteger exp = n.subtract(BigInteger.ONE).divide(BigInteger.TWO);
5153
BigInteger modExp = a.modPow(exp, n);
@@ -58,22 +60,11 @@ public static boolean isProbablePrime(BigInteger n, int trials) {
5860
return true; // probably prime
5961
}
6062

61-
/**
62-
* Generates a random BigInteger between {@code min} and {@code max}, inclusive.
63-
*/
64-
private static BigInteger uniformRandom(BigInteger min, BigInteger max) {
65-
BigInteger result;
66-
do {
67-
result = new BigInteger(max.bitLength(), random);
68-
} while (result.compareTo(min) < 0 || result.compareTo(max) > 0);
69-
return result;
70-
}
71-
7263
/**
7364
* Computes the Jacobi symbol (a/n).
7465
* Assumes n is positive and odd.
7566
*/
76-
private static int jacobiSymbol(BigInteger a, BigInteger n) {
67+
public static int jacobiSymbol(BigInteger a, BigInteger n) {
7768
if (n.signum() <= 0 || n.mod(BigInteger.TWO).equals(BigInteger.ZERO)) {
7869
throw new IllegalArgumentException("n must be positive and odd.");
7970
}
@@ -103,4 +94,15 @@ private static int jacobiSymbol(BigInteger a, BigInteger n) {
10394

10495
return n.equals(BigInteger.ONE) ? result : 0;
10596
}
97+
98+
/**
99+
* Generates a random BigInteger between {@code min} and {@code max}, inclusive.
100+
*/
101+
private static BigInteger uniformRandom(BigInteger min, BigInteger max) {
102+
BigInteger result;
103+
do {
104+
result = new BigInteger(max.bitLength(), random);
105+
} while (result.compareTo(min) < 0 || result.compareTo(max) > 0);
106+
return result;
107+
}
106108
}

src/test/java/com/thealgorithms/maths/prime/EulerPseudoprimeTest.java

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package com.thealgorithms.maths.prime;
22

33
import static org.junit.jupiter.api.Assertions.*;
4+
import static org.mockito.ArgumentMatchers.any;
45

56
import com.thealgorithms.maths.Prime.EulerPseudoprime;
67
import java.math.BigInteger;
78
import org.junit.jupiter.api.Test;
9+
import org.mockito.MockedStatic;
10+
import org.mockito.Mockito;
811

912
class EulerPseudoprimeTest {
1013

@@ -75,8 +78,54 @@ void testUniformRandomRange() throws Exception {
7578
}
7679

7780
@Test
78-
void testCompositeWithZeroJacobiSymbol() {
79-
// Choose n = 9, a = 3 → jacobi(3,9) = 0 should make it return false early
80-
assertFalse(EulerPseudoprime.isProbablePrime(BigInteger.valueOf(9), 1));
81+
void testIsProbablePrimeWhenJacobiSymbolIsZero() {
82+
try (MockedStatic<EulerPseudoprime> mockedPrimality = Mockito.mockStatic(EulerPseudoprime.class, Mockito.CALLS_REAL_METHODS)) {
83+
84+
// Mock jacobiSymbol to return 0 to test the branch
85+
mockedPrimality.when(() -> EulerPseudoprime.jacobiSymbol(any(BigInteger.class), any(BigInteger.class))).thenReturn(0);
86+
87+
boolean result = EulerPseudoprime.isProbablePrime(BigInteger.valueOf(15), 1);
88+
89+
assertFalse(result);
90+
}
91+
}
92+
93+
@Test
94+
void testJacobiSymbolThrowsForEvenOrNonPositiveN() throws Exception {
95+
var method = EulerPseudoprime.class.getDeclaredMethod("jacobiSymbol", BigInteger.class, BigInteger.class);
96+
method.setAccessible(true);
97+
98+
// Helper lambda to unwrap InvocationTargetException
99+
Runnable invokeJacobi = () -> {
100+
try {
101+
method.invoke(null, BigInteger.valueOf(2), BigInteger.valueOf(8));
102+
} catch (Exception e) {
103+
// unwrap
104+
Throwable cause = e.getCause();
105+
if (cause instanceof IllegalArgumentException) {
106+
throw (IllegalArgumentException) cause;
107+
} else {
108+
throw new RuntimeException(e);
109+
}
110+
}
111+
};
112+
113+
// Now check that it actually throws
114+
assertThrows(IllegalArgumentException.class, invokeJacobi::run);
115+
116+
// Another case: non-positive n
117+
Runnable invokeJacobi2 = () -> {
118+
try {
119+
method.invoke(null, BigInteger.valueOf(5), BigInteger.valueOf(-3));
120+
} catch (Exception e) {
121+
Throwable cause = e.getCause();
122+
if (cause instanceof IllegalArgumentException) {
123+
throw (IllegalArgumentException) cause;
124+
} else {
125+
throw new RuntimeException(e);
126+
}
127+
}
128+
};
129+
assertThrows(IllegalArgumentException.class, invokeJacobi2::run);
81130
}
82131
}

0 commit comments

Comments
 (0)