-
Notifications
You must be signed in to change notification settings - Fork 19.7k
/
Copy pathSolovayStrassenPrimalityTest.java
133 lines (112 loc) · 5.08 KB
/
SolovayStrassenPrimalityTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package com.thealgorithms.maths;
import java.util.Random;
/**
* This class implements the Solovay-Strassen primality test,
* which is a probabilistic algorithm to determine whether a number is prime.
* The algorithm is based on properties of the Jacobi symbol and modular exponentiation.
*
* For more information, go to {@link https://en.wikipedia.org/wiki/Solovay%E2%80%93Strassen_primality_test}
*/
final class SolovayStrassenPrimalityTest {
private final Random random;
/**
* Constructs a SolovayStrassenPrimalityTest instance with a specified seed for randomness.
*
* @param seed the seed for generating random numbers
*/
private SolovayStrassenPrimalityTest(int seed) {
random = new Random(seed);
}
/**
* Factory method to create an instance of SolovayStrassenPrimalityTest.
*
* @param seed the seed for generating random numbers
* @return a new instance of SolovayStrassenPrimalityTest
*/
public static SolovayStrassenPrimalityTest getSolovayStrassenPrimalityTest(int seed) {
return new SolovayStrassenPrimalityTest(seed);
}
/**
* Calculates modular exponentiation using the method of exponentiation by squaring.
*
* @param base the base number
* @param exponent the exponent
* @param mod the modulus
* @return (base^exponent) mod mod
*/
private static long calculateModularExponentiation(long base, long exponent, long mod) {
long x = 1; // This will hold the result of (base^exponent) % mod
long y = base; // This holds the current base value being squared
while (exponent > 0) {
// If exponent is odd, multiply the current base (y) with x
if (exponent % 2 == 1) {
x = x * y % mod; // Update result with current base
}
// Square the base for the next iteration
y = y * y % mod; // Update base to be y^2
exponent = exponent / 2; // Halve the exponent for next iteration
}
return x % mod; // Return final result after all iterations
}
/**
* Computes the Jacobi symbol (a/n), which is a generalization of the Legendre symbol.
*
* @param a the numerator
* @param num the denominator (must be an odd positive integer)
* @return the Jacobi symbol value: 1, -1, or 0
*/
public int calculateJacobi(long a, long num) {
// Check if num is non-positive or even; Jacobi symbol is not defined in these cases
if (num <= 0 || num % 2 == 0) {
return 0;
}
a = a % num; // Reduce a modulo num to simplify calculations
int jacobi = 1; // Initialize Jacobi symbol value
while (a != 0) {
// While a is even, reduce it and adjust jacobi based on properties of num
while (a % 2 == 0) {
a /= 2; // Divide a by 2 until it becomes odd
long nMod8 = num % 8; // Get num modulo 8 to check conditions for jacobi adjustment
if (nMod8 == 3 || nMod8 == 5) {
jacobi = -jacobi; // Flip jacobi sign based on properties of num modulo 8
}
}
long temp = a; // Temporarily store value of a
a = num; // Set a to be num for next iteration
num = temp; // Set num to be previous value of a
// Adjust jacobi based on properties of both numbers when both are odd and congruent to 3 modulo 4
if (a % 4 == 3 && num % 4 == 3) {
jacobi = -jacobi; // Flip jacobi sign again based on congruences
}
a = a % num; // Reduce a modulo num for next iteration of Jacobi computation
}
return (num == 1) ? jacobi : 0; // If num reduces to 1, return jacobi value, otherwise return 0 (not defined)
}
/**
* Performs the Solovay-Strassen primality test on a given number.
*
* @param num the number to be tested for primality
* @param iterations the number of iterations to run for accuracy
* @return true if num is likely prime, false if it is composite
*/
public boolean solovayStrassen(long num, int iterations) {
if (num <= 1) {
return false; // Numbers <=1 are not prime by definition.
}
if (num <= 3) {
return true; // Numbers <=3 are prime.
}
for (int i = 0; i < iterations; i++) {
long r = Math.abs(random.nextLong() % (num - 1)) + 2; // Generate a non-negative random number.
long a = r % (num - 1) + 1; // Choose random 'a' in range [1, n-1].
long jacobi = (num + calculateJacobi(a, num)) % num;
// Calculate Jacobi symbol and adjust it modulo n.
long mod = calculateModularExponentiation(a, (num - 1) / 2, num);
// Calculate modular exponentiation: a^((n-1)/2) mod n.
if (jacobi == 0 || mod != jacobi) {
return false; // If Jacobi symbol is zero or doesn't match modular result, n is composite.
}
}
return true; // If no contradictions found after all iterations, n is likely prime.
}
}