Skip to content

Commit ae7e3b2

Browse files
committed
feat: add euler primality test
1 parent 7e29be3 commit ae7e3b2

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.thealgorithms.maths.Prime;
2+
3+
import java.math.BigInteger;
4+
import java.security.SecureRandom;
5+
6+
/**
7+
* The {@code EulerPseudoprime} class implements the Euler primality test.
8+
*
9+
* It is based on Euler’s criterion:
10+
* For an odd prime number {@code n} and any integer {@code a} coprime to {@code n}:
11+
* a^((n-1)/2) ≡ (a/n) (mod n)
12+
* where (a/n) is the Jacobi symbol.
13+
*
14+
* This algorithm is a stronger probabilistic test than Fermat’s test.
15+
* It may still incorrectly identify a composite as “probably prime” (Euler pseudoprime),
16+
* but such cases are rare.
17+
*/
18+
public class EulerPseudoprime {
19+
20+
private EulerPseudoprime() {
21+
}
22+
23+
private static final SecureRandom random = new SecureRandom();
24+
25+
/**
26+
* Performs the Euler primality test for a given number.
27+
*
28+
* @param n number to test (must be > 2 and odd)
29+
* @param trials number of random bases to test
30+
* @return {@code true} if {@code n} passes all Euler tests (probably prime),
31+
* {@code false} if composite.
32+
*/
33+
public static boolean isProbablePrime(BigInteger n, int trials) {
34+
if (n.compareTo(BigInteger.TWO) < 0) {
35+
return false;
36+
}
37+
if (n.equals(BigInteger.TWO) || n.equals(BigInteger.valueOf(3))) {
38+
return true;
39+
}
40+
if (n.mod(BigInteger.TWO).equals(BigInteger.ZERO)) {
41+
return false;
42+
}
43+
44+
for (int i = 0; i < trials; i++) {
45+
BigInteger a = uniformRandom(BigInteger.TWO, n.subtract(BigInteger.TWO));
46+
BigInteger jacobi = BigInteger.valueOf(jacobiSymbol(a, n));
47+
if (jacobi.equals(BigInteger.ZERO)) return false;
48+
49+
BigInteger exp = n.subtract(BigInteger.ONE).divide(BigInteger.TWO);
50+
BigInteger modExp = a.modPow(exp, n);
51+
52+
// Euler's criterion: a^((n-1)/2) ≡ (a/n) (mod n)
53+
if (!modExp.equals(jacobi.mod(n))) {
54+
return false; // definitely composite
55+
}
56+
}
57+
return true; // probably prime
58+
}
59+
60+
/**
61+
* Generates a random BigInteger between {@code min} and {@code max}, inclusive.
62+
*/
63+
private static BigInteger uniformRandom(BigInteger min, BigInteger max) {
64+
BigInteger result;
65+
do {
66+
result = new BigInteger(max.bitLength(), random);
67+
} while (result.compareTo(min) < 0 || result.compareTo(max) > 0);
68+
return result;
69+
}
70+
71+
/**
72+
* Computes the Jacobi symbol (a/n).
73+
* Assumes n is positive and odd.
74+
*/
75+
private static int jacobiSymbol(BigInteger a, BigInteger n) {
76+
if (n.signum() <= 0 || n.mod(BigInteger.TWO).equals(BigInteger.ZERO)) {
77+
throw new IllegalArgumentException("n must be positive and odd.");
78+
}
79+
80+
int result = 1;
81+
a = a.mod(n);
82+
83+
while (a.compareTo(BigInteger.ZERO) != 0) {
84+
while (a.mod(BigInteger.TWO).equals(BigInteger.ZERO)) {
85+
a = a.divide(BigInteger.TWO);
86+
BigInteger nMod8 = n.mod(BigInteger.valueOf(8));
87+
if (nMod8.equals(BigInteger.valueOf(3)) || nMod8.equals(BigInteger.valueOf(5))) {
88+
result = -result;
89+
}
90+
}
91+
92+
BigInteger temp = a;
93+
a = n;
94+
n = temp;
95+
96+
if (a.mod(BigInteger.valueOf(4)).equals(BigInteger.valueOf(3)) && n.mod(BigInteger.valueOf(4)).equals(BigInteger.valueOf(3))) {
97+
result = -result;
98+
}
99+
100+
a = a.mod(n);
101+
}
102+
103+
return n.equals(BigInteger.ONE) ? result : 0;
104+
}
105+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.thealgorithms.maths.prime;
2+
3+
import static org.junit.jupiter.api.Assertions.*;
4+
5+
import com.thealgorithms.maths.Prime.EulerPseudoprime;
6+
import java.math.BigInteger;
7+
import org.junit.jupiter.api.Test;
8+
9+
class EulerPseudoprimeTest {
10+
11+
@Test
12+
void testPrimeNumbers() {
13+
assertTrue(EulerPseudoprime.isProbablePrime(BigInteger.valueOf(7), 10));
14+
assertTrue(EulerPseudoprime.isProbablePrime(BigInteger.valueOf(13), 10));
15+
assertTrue(EulerPseudoprime.isProbablePrime(BigInteger.valueOf(101), 10));
16+
}
17+
18+
@Test
19+
void testCompositeNumbers() {
20+
assertFalse(EulerPseudoprime.isProbablePrime(BigInteger.valueOf(9), 10));
21+
assertFalse(EulerPseudoprime.isProbablePrime(BigInteger.valueOf(21), 10));
22+
assertFalse(EulerPseudoprime.isProbablePrime(BigInteger.valueOf(221), 10));
23+
}
24+
25+
@Test
26+
void testEvenNumbers() {
27+
assertFalse(EulerPseudoprime.isProbablePrime(BigInteger.valueOf(4), 10));
28+
assertFalse(EulerPseudoprime.isProbablePrime(BigInteger.valueOf(100), 10));
29+
}
30+
}

0 commit comments

Comments
 (0)