zenith_float_num/
modular.rs1use crate::ExactInt;
4use core::cmp::Ordering;
5
6pub const POLLARD_RHO_ITER_MAX: usize = 1_048_576;
8
9const POLLARD_BRENT_M: usize = 128;
11
12fn two() -> ExactInt {
13 ExactInt::from_u64(2)
14}
15
16fn abs_int(a: &ExactInt) -> ExactInt {
17 if a.is_negative() {
18 a.neg()
19 } else {
20 a.clone()
21 }
22}
23
24fn is_even(a: &ExactInt) -> bool {
25 a.low_word() & 1 == 0
26}
27
28fn rem_nonneg(a: &ExactInt, m: &ExactInt) -> Option<ExactInt> {
29 let mabs = abs_int(m);
30 if mabs.is_zero() {
31 return None;
32 }
33 let (_, r) = a.div_rem(&mabs)?;
34 if r.is_negative() {
35 Some(r.add(&mabs))
36 } else {
37 Some(r)
38 }
39}
40
41fn mul_mod(a: &ExactInt, b: &ExactInt, m: &ExactInt) -> Option<ExactInt> {
42 rem_nonneg(&a.mul(b), m)
43}
44
45fn add_mod(a: &ExactInt, b: &ExactInt, m: &ExactInt) -> Option<ExactInt> {
46 rem_nonneg(&a.add(b), m)
47}
48
49pub fn mod_pow(base: &ExactInt, exp: &ExactInt, modulus: &ExactInt) -> Option<ExactInt> {
52 if exp.is_negative() {
53 return None;
54 }
55 let m = abs_int(modulus);
56 if m.is_zero() {
57 return None;
58 }
59 if m.is_one() {
60 return Some(ExactInt::zero());
61 }
62 let mut acc = ExactInt::one();
63 let mut b = rem_nonneg(base, &m)?;
64 let mut e = exp.clone();
65 let two = two();
66 while !e.is_zero() {
67 if !is_even(&e) {
68 acc = mul_mod(&acc, &b, &m)?;
69 }
70 e = e.div_rem(&two)?.0;
71 if !e.is_zero() {
72 b = mul_mod(&b, &b, &m)?;
73 }
74 }
75 Some(acc)
76}
77
78fn egcd(a: &ExactInt, b: &ExactInt) -> (ExactInt, ExactInt, ExactInt) {
79 if b.is_zero() {
80 return (a.clone(), ExactInt::one(), ExactInt::zero());
81 }
82 let (q, r) = a.div_rem(b).unwrap_or((ExactInt::zero(), ExactInt::zero()));
83 let (g, x, y) = egcd(b, &r);
84 (g, y.clone(), x.sub(&q.mul(&y)))
86}
87
88pub fn mod_inv(a: &ExactInt, modulus: &ExactInt) -> Option<ExactInt> {
90 let m = abs_int(modulus);
91 if m.is_zero() || m.is_one() {
92 return None;
93 }
94 let aa = rem_nonneg(a, &m)?;
95 if aa.is_zero() {
96 return None;
97 }
98 let (g, x, _) = egcd(&aa, &m);
99 if !g.is_one() {
100 return None;
101 }
102 rem_nonneg(&x, &m)
103}
104
105pub fn miller_rabin(n: &ExactInt, witnesses: &[ExactInt]) -> bool {
110 let n = abs_int(n);
111 if n.cmp(&two()) == Ordering::Less {
112 return false;
113 }
114 if n == two() || n == ExactInt::from_u64(3) {
115 return true;
116 }
117 if is_even(&n) {
118 return false;
119 }
120 let one = ExactInt::one();
121 let n_minus = n.sub(&one);
122 let mut d = n_minus.clone();
123 let mut s = 0usize;
124 let two = two();
125 while !d.is_zero() && is_even(&d) {
126 d = d
127 .div_rem(&two)
128 .map(|(q, _)| q)
129 .unwrap_or_else(ExactInt::zero);
130 s += 1;
131 }
132 if s == 0 {
133 return false;
134 }
135 'wit: for a in witnesses {
136 let a = rem_nonneg(a, &n).unwrap_or_else(ExactInt::zero);
137 if a.is_zero() || a.is_one() {
138 continue;
139 }
140 let mut x = match mod_pow(&a, &d, &n) {
141 Some(v) => v,
142 None => return false,
143 };
144 if x.is_one() || x == n_minus {
145 continue;
146 }
147 for _ in 1..s {
148 x = match mul_mod(&x, &x, &n) {
149 Some(v) => v,
150 None => return false,
151 };
152 if x == n_minus {
153 continue 'wit;
154 }
155 if x.is_one() {
156 return false;
157 }
158 }
159 return false;
160 }
161 !witnesses.is_empty()
162}
163
164fn pollard_f(x: &ExactInt, c: &ExactInt, n: &ExactInt) -> Option<ExactInt> {
165 add_mod(&mul_mod(x, x, n)?, c, n)
166}
167
168fn brent_once(n: &ExactInt, c: &ExactInt, max_f: usize) -> Option<ExactInt> {
169 let mut y = ExactInt::zero();
170 let mut g = ExactInt::one();
171 let mut r = 1usize;
172 let mut q = ExactInt::one();
173 let mut f_used = 0usize;
174 let mut x = ExactInt::zero();
175 let mut ys = ExactInt::zero();
176 while g.is_one() {
177 x = y.clone();
178 for _ in 0..r {
179 y = pollard_f(&y, c, n)?;
180 f_used += 1;
181 if f_used >= max_f {
182 return None;
183 }
184 }
185 let mut k = 0usize;
186 while k < r && g.is_one() {
187 ys = y.clone();
188 let steps = POLLARD_BRENT_M.min(r - k);
189 for _ in 0..steps {
190 y = pollard_f(&y, c, n)?;
191 f_used += 1;
192 if f_used >= max_f {
193 return None;
194 }
195 let diff = abs_int(&x.sub(&y));
196 q = mul_mod(&q, &diff, n)?;
197 }
198 g = q.gcd(n);
199 k += steps;
200 }
201 if r > usize::MAX / 2 {
202 return None;
203 }
204 r = r.saturating_mul(2);
205 }
206 if g == *n {
207 loop {
208 ys = pollard_f(&ys, c, n)?;
209 f_used += 1;
210 if f_used >= max_f {
211 return None;
212 }
213 g = abs_int(&x.sub(&ys)).gcd(n);
214 if !g.is_one() {
215 break;
216 }
217 }
218 }
219 if g.is_one() || g == *n {
220 None
221 } else {
222 Some(g)
223 }
224}
225
226pub fn pollard_rho(n: &ExactInt) -> Option<ExactInt> {
228 let n = abs_int(n);
229 if n.cmp(&two()) != Ordering::Greater {
230 return None;
231 }
232 if is_even(&n) {
233 return Some(two());
234 }
235 for c in 1u64..=32 {
236 if let Some(f) = brent_once(&n, &ExactInt::from_u64(c), POLLARD_RHO_ITER_MAX) {
237 if !f.is_one() && f != n {
238 return Some(f);
239 }
240 }
241 }
242 None
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn modular_pow_inv_miller_rho() {
251 let two = ExactInt::from_u64(2);
252 let exp = ExactInt::from_u64(100);
253 let m = ExactInt::from_u64(1_000_000_007);
254 let got = mod_pow(&two, &exp, &m).expect("mod_pow");
255 assert_eq!(got, ExactInt::from_u64(976_371_285));
256
257 let inv = mod_inv(&ExactInt::from_u64(3), &ExactInt::from_u64(7)).expect("inv");
258 assert_eq!(inv, ExactInt::from_u64(5));
259 assert!(mod_inv(&ExactInt::from_u64(2), &ExactInt::from_u64(4)).is_none());
260
261 let mersenne = two.pow(31).sub(&ExactInt::one());
262 let wits: Vec<ExactInt> = [2u64, 3, 5, 7]
263 .into_iter()
264 .map(ExactInt::from_u64)
265 .collect();
266 assert!(miller_rabin(&mersenne, &wits));
267 assert!(!miller_rabin(&ExactInt::from_u64(9), &wits));
268
269 let n = ExactInt::from_u64(8051);
270 let f = pollard_rho(&n).expect("rho");
271 let other = n.div_rem(&f).expect("div").0;
272 let a = ExactInt::from_u64(83);
273 let b = ExactInt::from_u64(97);
274 assert!(
275 (f == a && other == b) || (f == b && other == a),
276 "factor {f:?}"
277 );
278 assert!(pollard_rho(&ExactInt::from_u64(31)).is_none());
279 assert!(mod_pow(&two, &ExactInt::from_i64(-1), &m).is_none());
280 }
281}