-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
/
miller_rabin.cpp
186 lines (176 loc) · 5.72 KB
/
miller_rabin.cpp
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/**
* Copyright 2020 @author tjgurwara99
* @file
*
* A basic implementation of Miller-Rabin primality test.
*/
#include <cassert>
#include <iostream>
#include <random>
#include <vector>
/**
* Function to give a binary representation of a number in reverse order
* @param num integer number that we want to convert
* @return result vector of the number input in reverse binary
*/
template <typename T>
std::vector<T> reverse_binary(T num) {
std::vector<T> result;
T temp = num;
while (temp > 0) {
result.push_back(temp % 2);
temp = temp / 2;
}
return result;
}
/**
* Function for modular exponentiation.
* This function is an efficient modular exponentiation function.
* It can be used with any big integer library such as Boost multiprecision
* to give result any modular exponentiation problem relatively quickly.
* @param base number being raised to a power as integer
* @param rev_binary_exponent reverse binary of the power the base is being
* raised to
* @param mod modulo
* @return r the modular exponentiation of \f$a^{n} \equiv r \mod{m}\f$ where
* \f$n\f$ is the base 10 representation of rev_binary_exponent and \f$m = mod
* \f$ parameter.
*/
template <typename T>
T modular_exponentiation(T base, const std::vector<T> &rev_binary_exponent,
T mod) {
if (mod == 1)
return 0;
T b = 1;
if (rev_binary_exponent.size() == 0)
return b;
T A = base;
if (rev_binary_exponent[0] == 1)
b = base;
for (typename std::vector<T>::const_iterator it =
rev_binary_exponent.cbegin() + 1;
it != rev_binary_exponent.cend(); ++it) {
A = A * A % mod;
if (*it == 1)
b = A * b % mod;
}
return b;
}
/** Function for testing the conditions that are satisfied when a number is
* prime.
* @param d number such that \f$d \cdot 2^r = n - 1\f$ where \f$n = num\f$
* parameter and \f$r \geq 1\f$
* @param num number being tested for primality.
* @return 'false' if n is composite
* @return 'true' if n is (probably) prime.
*/
template <typename T>
bool miller_test(T d, T num) {
// random number seed
std::random_device rd_seed;
// random number generator
std::mt19937 gen(rd_seed());
// Uniformly distributed range [2, num - 2] for random numbers
std::uniform_int_distribution<> distribution(2, num - 2);
// Random number generated in the range [2, num -2].
T random = distribution(gen);
// vector for reverse binary of the power
std::vector<T> power = reverse_binary(d);
// x = random ^ d % num
T x = modular_exponentiation(random, power, num);
// miller conditions
if (x == 1 || x == num - 1) {
return true;
}
while (d != num - 1) {
x = (x * x) % num;
d *= 2;
if (x == 1) {
return false;
}
if (x == num - 1) {
return true;
}
}
return false;
}
/**
* Function that test (probabilistically) whether a given number is a prime
* based on the Miller-Rabin Primality Test.
* @param num number to be tested for primality.
* @param repeats number of repetitions for the test to increase probability of
* correct result.
* @return 'false' if num is composite
* @return 'true' if num is (probably) prime
*
* \detail
* First we check whether the num input is less than 4, if so we can determine
* whether this is a prime or composite by checking for 2 and 3.
* Next we check whether this num is odd (as all primes greater than 2 are odd).
* Next we write our num in the following format \f$num = 2^r \cdot d + 1\f$.
* After finding r and d for our input num, we use for loop repeat number of
* times inside which we check the miller conditions using the function
* miller_test. If miller_test returns false then the number is composite After
* the loop finishes completely without issuing a false return call, we can
* conclude that this number is probably prime.
*/
template <typename T>
bool miller_rabin_primality_test(T num, T repeats) {
if (num <= 4) {
// If num == 2 or num == 3 then prime
if (num == 2 || num == 3) {
return true;
} else {
return false;
}
}
// If num is even then not prime
if (num % 2 == 0) {
return false;
}
// Finding d and r in num = 2^r * d + 1
T d = num - 1, r = 0;
while (d % 2 == 0) {
d = d / 2;
r++;
}
for (T i = 0; i < repeats; ++i) {
if (!miller_test(d, num)) {
return false;
}
}
return true;
}
/**
* Functions for testing the miller_rabin_primality_test() function with some
* assert statements.
*/
void tests() {
// First test on 2
assert(((void)"2 is prime but function says otherwise.\n",
miller_rabin_primality_test(2, 1) == true));
std::cout << "First test passes." << std::endl;
// Second test on 5
assert(((void)"5 should be prime but the function says otherwise.\n",
miller_rabin_primality_test(5, 3) == true));
std::cout << "Second test passes." << std::endl;
// Third test on 23
assert(((void)"23 should be prime but the function says otherwise.\n",
miller_rabin_primality_test(23, 3) == true));
std::cout << "Third test passes." << std::endl;
// Fourth test on 16
assert(((void)"16 is not a prime but the function says otherwise.\n",
miller_rabin_primality_test(16, 3) == false));
std::cout << "Fourth test passes." << std::endl;
// Fifth test on 27
assert(((void)"27 is not a prime but the function says otherwise.\n",
miller_rabin_primality_test(27, 3) == false));
std::cout << "Fifth test passes." << std::endl;
}
/**
* Main function
*/
int main() {
tests();
return 0;
}