Skip to content

Commit 2210400

Browse files
authored
Miller rabin (#13)
JaroPaska's prime factoring contributions: Miller-Rabin, Pollard's rho
1 parent ad5720e commit 2210400

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ target/
22
**/*.rs.bk
33
Cargo.lock
44
.idea/
5+
.vscode/
6+
*.code-workspace

src/math/mod.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,108 @@ pub fn canon_egcd(a: i64, b: i64, c: i64) -> Option<(i64, i64, i64)> {
2929
}
3030
}
3131

32+
fn pos_mod(n: i64, m: i64) -> i64 {
33+
if n < 0 {
34+
n + m
35+
} else {
36+
n
37+
}
38+
}
39+
40+
fn mod_mul(a: i64, b: i64, m: i64) -> i64 {
41+
pos_mod((a as i128 * b as i128 % m as i128) as i64, m)
42+
}
43+
44+
/// Assuming m >= 1 and exp >= 0, finds base ^ exp % m in logarithmic time
45+
fn mod_exp(mut base: i64, mut exp: i64, m: i64) -> i64 {
46+
assert!(m >= 1);
47+
assert!(exp >= 0);
48+
let mut ans = 1 % m;
49+
base = base % m;
50+
while exp > 0 {
51+
if exp % 2 == 1 {
52+
ans = mod_mul(ans, base, m);
53+
}
54+
base = mod_mul(base, base, m);
55+
exp /= 2;
56+
}
57+
pos_mod(ans, m)
58+
}
59+
60+
fn is_strong_probable_prime(n: i64, d: i64, r: i64, a: i64) -> bool {
61+
let mut x = mod_exp(a, d, n);
62+
if x == 1 || x == n - 1 {
63+
return true;
64+
}
65+
for _ in 0..(r - 1) {
66+
x = mod_mul(x, x, n);
67+
if x == n - 1 {
68+
return true;
69+
}
70+
}
71+
false
72+
}
73+
74+
const BASES: [i64; 12] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37];
75+
/// Assuming x >= 0, returns true if x is prime
76+
pub fn is_prime(n: i64) -> bool {
77+
assert!(n >= 0);
78+
match n {
79+
0 | 1 => return false,
80+
2 | 3 => return true,
81+
_ if n % 2 == 0 => return false,
82+
_ => {}
83+
};
84+
let r = (n - 1).trailing_zeros() as i64;
85+
let d = (n - 1) >> r;
86+
BASES
87+
.iter()
88+
.all(|&base| base > n - 2 || is_strong_probable_prime(n, d, r, base))
89+
}
90+
91+
fn pollard_rho(n: i64) -> i64 {
92+
for a in 1..n {
93+
let f = |x| pos_mod(mod_mul(x, x, n) + a, n);
94+
let mut x = 2;
95+
let mut y = 2;
96+
loop {
97+
x = f(x);
98+
y = f(f(y));
99+
let p = num::fast_gcd(x - y, n);
100+
if p > 1 {
101+
if p == n {
102+
break;
103+
} else {
104+
return p;
105+
}
106+
}
107+
}
108+
}
109+
panic!("No divisor found!");
110+
}
111+
112+
/// Assuming x >= 1, finds the prime factorization of n
113+
pub fn factorize(n: i64) -> Vec<i64> {
114+
assert!(n >= 1);
115+
let r = n.trailing_zeros();
116+
let mut factors = vec![2; r as usize];
117+
let mut stack = match n >> r {
118+
1 => Vec::new(),
119+
x => vec![x]
120+
};
121+
while let Some(top) = stack.pop() {
122+
if is_prime(top) {
123+
factors.push(top);
124+
} else {
125+
let div = pollard_rho(top);
126+
stack.push(div);
127+
stack.push(top / div);
128+
}
129+
}
130+
factors.sort_unstable();
131+
factors
132+
}
133+
32134
#[cfg(test)]
33135
mod test {
34136
use super::*;
@@ -44,4 +146,38 @@ mod test {
44146
assert_eq!(canon_egcd(a, b, d), Some((d, -2, 1)));
45147
assert_eq!(canon_egcd(b, a, d), Some((d, -1, 3)));
46148
}
149+
150+
#[test]
151+
fn test_modexp() {
152+
let m = 1_000_000_007;
153+
assert_eq!(mod_exp(0, 0, m), 1);
154+
assert_eq!(mod_exp(0, 1, m), 0);
155+
assert_eq!(mod_exp(0, 10, m), 0);
156+
assert_eq!(mod_exp(123, 456, m), 565291922);
157+
}
158+
159+
#[test]
160+
fn test_miller() {
161+
assert_eq!(is_prime(2), true);
162+
assert_eq!(is_prime(4), false);
163+
assert_eq!(is_prime(6), false);
164+
assert_eq!(is_prime(8), false);
165+
assert_eq!(is_prime(269), true);
166+
assert_eq!(is_prime(1000), false);
167+
assert_eq!(is_prime(1_000_000_007), true);
168+
assert_eq!(is_prime((1 << 61) - 1), true);
169+
assert_eq!(is_prime(7156857700403137441), false);
170+
}
171+
172+
#[test]
173+
fn test_pollard() {
174+
assert_eq!(factorize(1), Vec::new());
175+
assert_eq!(factorize(2), vec![2]);
176+
assert_eq!(factorize(4), vec![2, 2]);
177+
assert_eq!(factorize(12), vec![2, 2, 3]);
178+
assert_eq!(
179+
factorize(7156857700403137441),
180+
vec![11, 13, 17, 19, 29, 37, 41, 43, 61, 97, 109, 127]
181+
);
182+
}
47183
}

0 commit comments

Comments
 (0)