1use logicaffeine_base::numeric::{BigInt, Rational};
19
20const MR_BASES: &[u64] = &[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37];
23
24fn one() -> BigInt {
25 BigInt::from_i64(1)
26}
27
28fn two() -> BigInt {
29 BigInt::from_i64(2)
30}
31
32fn rem(a: &BigInt, m: &BigInt) -> BigInt {
34 a.div_rem(m).expect("modulus is nonzero").1
35}
36
37pub fn gcd(a: &BigInt, b: &BigInt) -> BigInt {
39 let (mut a, mut b) = (a.abs(), b.abs());
40 while !b.is_zero() {
41 let r = rem(&a, &b);
42 a = b;
43 b = r;
44 }
45 a
46}
47
48pub fn mod_inverse(a: &BigInt, m: &BigInt) -> Option<BigInt> {
50 let (mut old_r, mut r) = (rem(a, m), m.clone());
51 let (mut old_s, mut s) = (one(), BigInt::zero());
52 while !r.is_zero() {
53 let (q, rr) = old_r.div_rem(&r).expect("nonzero");
54 old_r = r;
55 r = rr;
56 let ns = old_s.sub(&q.mul(&s));
57 old_s = s;
58 s = ns;
59 }
60 if old_r != one() {
61 return None;
62 }
63 let mut res = rem(&old_s, m);
64 if res.is_negative() {
65 res = res.add(m);
66 }
67 Some(res)
68}
69
70pub fn modpow(base: &BigInt, exp: &BigInt, m: &BigInt) -> BigInt {
73 if *m == one() {
74 return BigInt::zero();
75 }
76 let two = two();
77 let mut result = one();
78 let mut b = rem(base, m);
79 let mut e = exp.clone();
80 while !e.is_zero() {
81 let (q, bit) = e.div_rem(&two).expect("nonzero");
82 if !bit.is_zero() {
83 result = rem(&result.mul(&b), m);
84 }
85 b = rem(&b.mul(&b), m);
86 e = q;
87 }
88 result
89}
90
91pub fn isqrt(n: &BigInt) -> BigInt {
95 if n.is_zero() || n.is_negative() {
96 return BigInt::zero();
97 }
98 let two = two();
99 let approx = n.to_f64().sqrt();
100 let mut x = if approx.is_finite() && approx >= 1.0 {
101 BigInt::parse_decimal(&format!("{approx:.0}")).unwrap_or_else(one)
102 } else {
103 one()
104 };
105 if x.is_zero() {
106 x = one();
107 }
108 while x.mul(&x) < *n {
109 x = x.mul(&two); }
111 loop {
112 let (q, _) = n.div_rem(&x).expect("nonzero");
113 let y = x.add(&q).div_rem(&two).expect("nonzero").0;
114 if y >= x {
115 return x;
116 }
117 x = y;
118 }
119}
120
121pub fn is_probable_prime(n: &BigInt) -> bool {
123 let (one, two) = (one(), two());
124 if *n < two {
125 return false;
126 }
127 if *n == two {
128 return true;
129 }
130 if !n.is_odd() {
131 return false;
132 }
133 let n1 = n.sub(&one);
135 let mut d = n1.clone();
136 let mut s = 0u32;
137 while !d.is_odd() {
138 d = d.div_rem(&two).expect("nonzero").0;
139 s += 1;
140 }
141 'base: for &a in MR_BASES {
142 let a = BigInt::from_u64(a);
143 if rem(&a, n).is_zero() {
144 continue; }
146 let mut x = modpow(&a, &d, n);
147 if x == one || x == n1 {
148 continue;
149 }
150 for _ in 0..s.saturating_sub(1) {
151 x = rem(&x.mul(&x), n);
152 if x == n1 {
153 continue 'base;
154 }
155 }
156 return false;
157 }
158 true
159}
160
161pub fn next_prime(start: &BigInt) -> BigInt {
163 let (one, two) = (one(), two());
164 if *start <= two {
165 return two;
166 }
167 let mut c = if start.is_odd() { start.clone() } else { start.add(&one) };
168 loop {
169 if is_probable_prime(&c) {
170 return c;
171 }
172 c = c.add(&two);
173 }
174}
175
176pub fn verify_factorization(n: &BigInt, p: &BigInt, q: &BigInt) -> bool {
178 let one = one();
179 p.mul(q) == *n && *p > one && *q > one && *p < *n && *q < *n
180}
181
182fn split(n: &BigInt, d: &BigInt) -> Option<(BigInt, BigInt)> {
183 let one = one();
184 if *d > one && *d < *n {
185 let (q, r) = n.div_rem(d).expect("nonzero");
186 if r.is_zero() {
187 return Some((d.clone(), q));
188 }
189 }
190 None
191}
192
193pub fn trial_division(n: &BigInt, limit: u64) -> Option<(BigInt, BigInt)> {
195 let mut d = 2u64;
196 while d <= limit {
197 let bd = BigInt::from_u64(d);
198 if bd.mul(&bd) > *n {
199 break;
200 }
201 if let Some(f) = split(n, &bd) {
202 return Some(f);
203 }
204 d += if d == 2 { 1 } else { 2 };
205 }
206 None
207}
208
209pub fn fermat(n: &BigInt, max_iters: u64) -> Option<(BigInt, BigInt)> {
213 if !n.is_odd() {
214 return split(n, &two());
215 }
216 let one = one();
217 let mut a = isqrt(n);
218 if a.mul(&a) < *n {
219 a = a.add(&one);
220 }
221 for _ in 0..max_iters {
222 let b2 = a.mul(&a).sub(n);
223 let b = isqrt(&b2);
224 if b.mul(&b) == b2 {
225 let p = a.sub(&b);
226 let q = a.add(&b);
227 if verify_factorization(n, &p, &q) {
228 return Some((p, q));
229 }
230 }
231 a = a.add(&one);
232 }
233 None
234}
235
236pub fn pollard_rho(n: &BigInt, max_iters: u64) -> Option<(BigInt, BigInt)> {
239 let (one, two) = (one(), two());
240 if !n.is_odd() {
241 return split(n, &two);
242 }
243 let f = |x: &BigInt| rem(&x.mul(x).add(&one), n);
244 let (mut x, mut y, mut d) = (two.clone(), two.clone(), one.clone());
245 let mut iters = 0u64;
246 while d == one && iters < max_iters {
247 x = f(&x);
248 y = f(&f(&y));
249 d = gcd(&x.sub(&y).abs(), n);
250 iters += 1;
251 }
252 split(n, &d)
253}
254
255pub fn pollard_p_minus_1(n: &BigInt, bound: u64) -> Option<(BigInt, BigInt)> {
259 let one = one();
260 let mut a = two();
261 for k in 2..=bound {
262 a = modpow(&a, &BigInt::from_u64(k), n);
263 if k % 16 == 0 || k == bound {
264 if let Some(f) = split(n, &gcd(&a.sub(&one), n)) {
265 return Some(f);
266 }
267 }
268 }
269 split(n, &gcd(&a.sub(&one), n))
270}
271
272pub fn wiener(e: &BigInt, n: &BigInt) -> Option<(BigInt, BigInt)> {
277 let (one, two, four) = (one(), two(), BigInt::from_i64(4));
278 let (mut num, mut den) = (e.clone(), n.clone());
279 let (mut h2, mut h1) = (BigInt::zero(), one.clone());
281 let (mut k2, mut k1) = (one.clone(), BigInt::zero());
282 for _ in 0..2000 {
283 if den.is_zero() {
284 break;
285 }
286 let (a, r) = num.div_rem(&den).expect("nonzero");
287 let h = a.mul(&h1).add(&h2); let k = a.mul(&k1).add(&k2); if !h.is_zero() {
290 let (phi, prem) = e.mul(&k).sub(&one).div_rem(&h).expect("nonzero");
291 if prem.is_zero() {
292 let s = n.sub(&phi).add(&one); let disc = s.mul(&s).sub(&four.mul(n));
295 if !disc.is_negative() {
296 let sq = isqrt(&disc);
297 if sq.mul(&sq) == disc {
298 let p = s.sub(&sq).div_rem(&two).expect("nonzero").0;
299 let q = s.add(&sq).div_rem(&two).expect("nonzero").0;
300 if verify_factorization(n, &p, &q) {
301 return Some((p, q));
302 }
303 }
304 }
305 }
306 }
307 num = den;
308 den = r;
309 (h2, h1) = (h1, h);
310 (k2, k1) = (k1, k);
311 }
312 None
313}
314
315fn pos_rem(a: &BigInt, m: &BigInt) -> BigInt {
325 let r = rem(a, m);
326 if r.is_negative() {
327 r.add(m)
328 } else {
329 r
330 }
331}
332
333pub fn crt(residues: &[BigInt], moduli: &[BigInt]) -> Option<BigInt> {
336 if residues.is_empty() || residues.len() != moduli.len() {
337 return None;
338 }
339 let mut x = pos_rem(&residues[0], &moduli[0]);
340 let mut m = moduli[0].clone();
341 for i in 1..moduli.len() {
342 let mi = &moduli[i];
343 let inv = mod_inverse(&pos_rem(&m, mi), mi)?;
344 let diff = pos_rem(&residues[i].sub(&x), mi);
345 let t = pos_rem(&diff.mul(&inv), mi);
346 x = x.add(&m.mul(&t));
347 m = m.mul(mi);
348 x = pos_rem(&x, &m);
349 }
350 Some(x)
351}
352
353pub fn integer_nth_root(x: &BigInt, n: u32) -> BigInt {
356 if n == 0 || x.is_negative() {
357 return BigInt::zero();
358 }
359 if n == 1 || x.is_zero() {
360 return x.clone();
361 }
362 let (one, two) = (one(), two());
363 let nn = BigInt::from_u64(n as u64);
364 let nm1 = BigInt::from_u64((n - 1) as u64);
365 let approx = x.to_f64().powf(1.0 / n as f64);
366 let mut r = if approx.is_finite() && approx >= 1.0 {
367 BigInt::parse_decimal(&format!("{approx:.0}")).unwrap_or_else(|| one.clone())
368 } else {
369 one.clone()
370 };
371 if r.is_zero() {
372 r = one.clone();
373 }
374 while r.pow(n) < *x {
375 r = r.mul(&two); }
377 loop {
378 let (q, _) = x.div_rem(&r.pow(n - 1)).expect("nonzero");
379 let next = nm1.mul(&r).add(&q).div_rem(&nn).expect("nonzero").0;
380 if next >= r {
381 break;
382 }
383 r = next;
384 }
385 while r.pow(n) > *x {
386 r = r.sub(&one);
387 }
388 while r.add(&one).pow(n) <= *x {
389 r = r.add(&one);
390 }
391 r
392}
393
394pub fn hastad_broadcast_attack(e: u32, ciphertexts: &[BigInt], moduli: &[BigInt]) -> Option<BigInt> {
399 if (ciphertexts.len() as u32) < e {
400 return None;
401 }
402 let c = crt(ciphertexts, moduli)?;
403 let m = integer_nth_root(&c, e);
404 (m.pow(e) == c).then_some(m)
405}
406
407fn ext_gcd(a: &BigInt, b: &BigInt) -> (BigInt, BigInt, BigInt) {
411 let (mut old_r, mut r) = (a.clone(), b.clone());
412 let (mut old_s, mut s) = (one(), BigInt::zero());
413 let (mut old_t, mut t) = (BigInt::zero(), one());
414 while !r.is_zero() {
415 let (q, _) = old_r.div_rem(&r).expect("nonzero");
416 let nr = old_r.sub(&q.mul(&r));
417 old_r = r;
418 r = nr;
419 let ns = old_s.sub(&q.mul(&s));
420 old_s = s;
421 s = ns;
422 let nt = old_t.sub(&q.mul(&t));
423 old_t = t;
424 t = nt;
425 }
426 (old_r, old_s, old_t)
427}
428
429pub fn common_modulus_attack(e1: &BigInt, e2: &BigInt, c1: &BigInt, c2: &BigInt, n: &BigInt) -> Option<BigInt> {
433 let (g, a, b) = ext_gcd(e1, e2);
434 if g != one() {
435 return None;
436 }
437 let t1 = if a.is_negative() {
438 modpow(&mod_inverse(c1, n)?, &a.negated(), n)
439 } else {
440 modpow(c1, &a, n)
441 };
442 let t2 = if b.is_negative() {
443 modpow(&mod_inverse(c2, n)?, &b.negated(), n)
444 } else {
445 modpow(c2, &b, n)
446 };
447 Some(rem(&t1.mul(&t2), n))
448}
449
450pub fn low_exponent_message(c: &BigInt, e: u32) -> Option<BigInt> {
453 let m = integer_nth_root(c, e);
454 (m.pow(e) == *c).then_some(m)
455}
456
457fn poly_norm_mod(p: &[BigInt], n: &BigInt) -> Vec<BigInt> {
458 let mut v: Vec<BigInt> = p.iter().map(|c| pos_rem(c, n)).collect();
459 while v.len() > 1 && v.last().is_some_and(|c| c.is_zero()) {
460 v.pop();
461 }
462 if v.is_empty() {
463 v.push(BigInt::zero());
464 }
465 v
466}
467
468fn poly_mul_mod(a: &[BigInt], b: &[BigInt], n: &BigInt) -> Vec<BigInt> {
469 let mut out = vec![BigInt::zero(); a.len() + b.len() - 1];
470 for (i, ai) in a.iter().enumerate() {
471 for (j, bj) in b.iter().enumerate() {
472 out[i + j] = pos_rem(&out[i + j].add(&ai.mul(bj)), n);
473 }
474 }
475 poly_norm_mod(&out, n)
476}
477
478fn poly_rem_mod(a: &[BigInt], b: &[BigInt], n: &BigInt) -> Option<Vec<BigInt>> {
481 let b = poly_norm_mod(b, n);
482 let db = b.len() - 1;
483 if b.iter().all(|c| c.is_zero()) {
484 return None;
485 }
486 let lead_inv = mod_inverse(&b[db], n)?;
487 let mut r = poly_norm_mod(a, n);
488 while r.len() > db {
489 let dr = r.len() - 1;
490 let factor = pos_rem(&r[dr].mul(&lead_inv), n);
491 let shift = dr - db;
492 for i in 0..b.len() {
493 r[shift + i] = pos_rem(&r[shift + i].sub(&factor.mul(&b[i])), n);
494 }
495 r = poly_norm_mod(&r, n);
496 }
497 Some(r)
498}
499
500fn poly_gcd_mod(a: &[BigInt], b: &[BigInt], n: &BigInt) -> Option<Vec<BigInt>> {
502 let mut a = poly_norm_mod(a, n);
503 let mut b = poly_norm_mod(b, n);
504 while !(b.len() == 1 && b[0].is_zero()) {
505 let r = poly_rem_mod(&a, &b, n)?;
506 a = b;
507 b = r;
508 }
509 Some(a)
510}
511
512pub fn franklin_reiter_attack(e: u32, r: &BigInt, c1: &BigInt, c2: &BigInt, n: &BigInt) -> Option<BigInt> {
518 let mut g1 = vec![BigInt::zero(); (e + 1) as usize];
519 g1[0] = pos_rem(&c1.negated(), n);
520 g1[e as usize] = one();
521
522 let xr = vec![pos_rem(r, n), one()];
523 let mut g2 = vec![one()];
524 for _ in 0..e {
525 g2 = poly_mul_mod(&g2, &xr, n);
526 }
527 g2[0] = pos_rem(&g2[0].sub(c2), n);
528
529 let g = poly_gcd_mod(&g1, &g2, n)?;
530 if g.len() == 2 {
531 let m = pos_rem(&g[0].negated().mul(&mod_inverse(&g[1], n)?), n);
532 if modpow(&m, &BigInt::from_u64(e as u64), n) == pos_rem(c1, n) {
533 return Some(m);
534 }
535 }
536 None
537}
538
539fn poly_mul(a: &[BigInt], b: &[BigInt]) -> Vec<BigInt> {
544 if a.is_empty() || b.is_empty() {
545 return Vec::new();
546 }
547 let mut out = vec![BigInt::zero(); a.len() + b.len() - 1];
548 for (i, ai) in a.iter().enumerate() {
549 for (j, bj) in b.iter().enumerate() {
550 out[i + j] = out[i + j].add(&ai.mul(bj));
551 }
552 }
553 out
554}
555
556fn poly_pow(base: &[BigInt], e: usize) -> Vec<BigInt> {
557 let mut acc = vec![one()];
558 for _ in 0..e {
559 acc = poly_mul(&acc, base);
560 }
561 acc
562}
563
564fn poly_eval(coeffs: &[BigInt], x: &BigInt) -> BigInt {
565 coeffs.iter().rev().fold(BigInt::zero(), |acc, c| acc.mul(x).add(c))
566}
567
568fn poly_integer_root(coeffs: &[BigInt], bound: &BigInt) -> Option<BigInt> {
573 let maxc = coeffs.iter().map(|c| c.abs()).max()?;
574 if maxc.is_zero() {
575 return None;
576 }
577 if bound.to_f64() <= 4_200_000.0 {
581 let mut x = BigInt::zero();
582 let one = one();
583 while x <= *bound {
584 if poly_eval(coeffs, &x).is_zero() {
585 return Some(x);
586 }
587 x = x.add(&one);
588 }
589 return None;
590 }
591 let cf: Vec<f64> =
592 coeffs.iter().map(|c| Rational::new(c.clone(), maxc.clone()).map(|r| r.to_f64()).unwrap_or(0.0)).collect();
593 let eval = |x: f64| cf.iter().rev().fold(0.0, |acc, &c| acc * x + c);
594 let deriv = |x: f64| (1..cf.len()).rev().fold(0.0, |acc, k| acc * x + cf[k] * k as f64);
595 let bound_f = bound.to_f64();
596
597 let mut seeds: Vec<f64> = Vec::new();
598 let mut p = 1.0;
599 while p <= bound_f {
600 seeds.push(p);
601 p *= 1.5;
602 }
603 for i in 0..=64 {
604 seeds.push(bound_f * i as f64 / 64.0);
605 }
606
607 let mut seen: Vec<BigInt> = Vec::new();
608 for &s in &seeds {
609 let mut x = s;
610 for _ in 0..200 {
611 let d = deriv(x);
612 if d.abs() < 1e-15 {
613 break;
614 }
615 let nx = x - eval(x) / d;
616 if !nx.is_finite() {
617 break;
618 }
619 if (nx - x).abs() < 0.4 {
620 x = nx;
621 break;
622 }
623 x = nx;
624 }
625 if x.is_finite() && x >= -1.0 && x <= bound_f + 1.0 {
626 if let Some(cand) = BigInt::parse_decimal(&format!("{:.0}", x.max(0.0))) {
627 if !seen.contains(&cand) {
628 if cand <= *bound && poly_eval(coeffs, &cand).is_zero() {
629 return Some(cand);
630 }
631 seen.push(cand);
632 }
633 }
634 }
635 }
636 None
637}
638
639fn poly_derivative(p: &[BigInt]) -> Vec<BigInt> {
640 (1..p.len()).map(|k| p[k].mul(&BigInt::from_i64(k as i64))).collect()
641}
642
643fn round_div_factor(a: &BigInt, b: &BigInt) -> BigInt {
645 let (q, r) = a.div_rem(b).expect("nonzero");
646 if r.is_zero() {
647 return q;
648 }
649 if BigInt::from_i64(2).mul(&r.abs()) > b.abs() {
650 if a.is_negative() ^ b.is_negative() {
651 q.sub(&one())
652 } else {
653 q.add(&one())
654 }
655 } else {
656 q
657 }
658}
659
660fn integer_roots_of(res: &[BigInt], bound: &BigInt) -> Vec<BigInt> {
665 let deriv = poly_derivative(res);
666 let mut out: Vec<BigInt> = Vec::new();
667 for seed in real_root_candidates(res, bound) {
668 let mut x = seed;
669 for _ in 0..80 {
670 let fx = poly_eval(res, &x);
671 if fx.is_zero() {
672 break;
673 }
674 let dfx = poly_eval(&deriv, &x);
675 if dfx.is_zero() {
676 break;
677 }
678 let step = round_div_factor(&fx, &dfx);
679 if step.is_zero() {
680 break;
681 }
682 x = x.sub(&step);
683 }
684 if poly_eval(res, &x).is_zero() && x.abs() <= *bound && !out.contains(&x) {
685 out.push(x);
686 }
687 }
688 out
689}
690
691pub fn coppersmith_factor_high_bits(n: &BigInt, p_high: &BigInt, unknown_bits: u32) -> Option<(BigInt, BigInt)> {
698 let two = two();
699 let mut x_bound = one();
700 for _ in 0..unknown_bits {
701 x_bound = x_bound.mul(&two);
702 }
703 let (m, t) = (4usize, 4usize);
706 let deg = m + t;
707 let f = vec![p_high.clone(), one()]; let mut polys: Vec<Vec<BigInt>> = Vec::new();
711 for i in 0..=m {
712 let scale = n.pow((m - i) as u32);
713 polys.push(poly_pow(&f, i).iter().map(|c| c.mul(&scale)).collect());
714 }
715 let fm = poly_pow(&f, m);
716 for j in 1..=t {
717 let mut p = vec![BigInt::zero(); j];
718 p.extend_from_slice(&fm);
719 polys.push(p);
720 }
721
722 let mut xpow = vec![one()];
724 for k in 1..=deg {
725 xpow.push(xpow[k - 1].mul(&x_bound));
726 }
727 let rows: Vec<Vec<BigInt>> = polys
728 .iter()
729 .map(|p| (0..=deg).map(|k| p.get(k).cloned().unwrap_or_else(BigInt::zero).mul(&xpow[k])).collect())
730 .collect();
731
732 let reduced = crate::lattice::lll_reduce_bigint_fp(&rows);
733 for row in &reduced {
734 let mut h = Vec::with_capacity(deg + 1);
736 let mut ok = true;
737 for k in 0..=deg {
738 let (q, r) = row[k].div_rem(&xpow[k]).expect("nonzero");
739 if !r.is_zero() {
740 ok = false;
741 break;
742 }
743 h.push(q);
744 }
745 if !ok || h.iter().all(|c| c.is_zero()) {
746 continue;
747 }
748 if let Some(x0) = poly_integer_root(&h, &x_bound) {
749 if let Some(f) = split(n, &p_high.add(&x0)) {
750 return Some(f);
751 }
752 }
753 }
754 None
755}
756
757pub fn rsa_private_exponent(e: &BigInt, p: &BigInt, q: &BigInt) -> Option<BigInt> {
761 let one = one();
762 mod_inverse(e, &p.sub(&one).mul(&q.sub(&one)))
763}
764
765pub fn factor_via_private_exponent(n: &BigInt, e: &BigInt, d: &BigInt) -> Option<(BigInt, BigInt)> {
772 let (one, two) = (one(), two());
773 let n1 = n.sub(&one);
774 let k = e.mul(d).sub(&one);
775 if k.is_zero() || k.is_negative() {
776 return None;
777 }
778 let mut t = k;
780 let mut s = 0u32;
781 while !t.is_odd() {
782 t = t.div_rem(&two).expect("nonzero").0;
783 s += 1;
784 }
785 for &gb in MR_BASES {
786 let g = BigInt::from_u64(gb);
787 let shared = gcd(&g, n);
788 if shared != one {
789 if let Some(f) = split(n, &shared) {
790 return Some(f);
791 }
792 continue;
793 }
794 let mut x = modpow(&g, &t, n);
795 if x == one || x == n1 {
796 continue;
797 }
798 for _ in 0..s {
799 let y = modpow(&x, &two, n);
800 if y == one {
801 if let Some(f) = split(n, &gcd(&x.sub(&one), n)) {
803 return Some(f);
804 }
805 break;
806 }
807 if y == n1 {
808 break; }
810 x = y;
811 }
812 }
813 None
814}
815
816pub fn batch_gcd(moduli: &[BigInt]) -> Vec<(usize, usize, BigInt)> {
819 let one = one();
820 let mut hits = Vec::new();
821 for i in 0..moduli.len() {
822 for j in (i + 1)..moduli.len() {
823 let g = gcd(&moduli[i], &moduli[j]);
824 if g > one {
825 hits.push((i, j, g));
826 }
827 }
828 }
829 hits
830}
831
832fn gf2_dependencies(rows: &[Vec<bool>], ncols: usize) -> Vec<Vec<usize>> {
847 let m = rows.len();
848 let mut mat: Vec<(Vec<bool>, Vec<bool>)> = rows
849 .iter()
850 .enumerate()
851 .map(|(i, r)| {
852 let mut tag = vec![false; m];
853 tag[i] = true;
854 (r.clone(), tag)
855 })
856 .collect();
857 let mut pivot = 0;
858 for col in 0..ncols {
859 let Some(pr) = (pivot..m).find(|&i| mat[i].0[col]) else {
860 continue;
861 };
862 mat.swap(pivot, pr);
863 let (prow, ptag) = (mat[pivot].0.clone(), mat[pivot].1.clone());
864 for i in 0..m {
865 if i != pivot && mat[i].0[col] {
866 for c in 0..ncols {
867 mat[i].0[c] ^= prow[c];
868 }
869 for c in 0..m {
870 mat[i].1[c] ^= ptag[c];
871 }
872 }
873 }
874 pivot += 1;
875 }
876 mat.iter()
877 .filter(|(prim, _)| prim.iter().all(|&b| !b))
878 .map(|(_, tag)| (0..m).filter(|&i| tag[i]).collect())
879 .filter(|s: &Vec<usize>| !s.is_empty())
880 .collect()
881}
882
883pub fn dixon_factor(n: &BigInt, base: &[u64], tries: usize) -> Option<(BigInt, BigInt)> {
889 let one = one();
890 let need = base.len() + 5;
891 let mut rels: Vec<(BigInt, Vec<u64>)> = Vec::new();
892 let mut r = isqrt(n).add(&one);
893 let mut steps = 0;
894 while rels.len() < need && steps < tries {
895 let mut s = rem(&r.mul(&r), n);
896 let mut exps = vec![0u64; base.len()];
897 for (bi, &b) in base.iter().enumerate() {
898 let bb = BigInt::from_u64(b);
899 while let Some((q, rr)) = s.div_rem(&bb) {
900 if rr.is_zero() {
901 s = q;
902 exps[bi] += 1;
903 } else {
904 break;
905 }
906 }
907 }
908 if s == one {
909 rels.push((r.clone(), exps));
910 }
911 r = r.add(&one);
912 steps += 1;
913 }
914 if rels.len() < 2 {
915 return None;
916 }
917 let rows: Vec<Vec<bool>> = rels.iter().map(|(_, e)| e.iter().map(|&x| x & 1 == 1).collect()).collect();
918 for dep in gf2_dependencies(&rows, base.len()) {
919 let mut x = one.clone();
920 let mut total = vec![0u64; base.len()];
921 for &i in &dep {
922 x = rem(&x.mul(&rels[i].0), n);
923 for (k, &e) in rels[i].1.iter().enumerate() {
924 total[k] += e;
925 }
926 }
927 let mut y = one.clone();
928 for (k, &e) in total.iter().enumerate() {
929 y = rem(&y.mul(&modpow(&BigInt::from_u64(base[k]), &BigInt::from_u64(e / 2), n)), n);
930 }
931 let diff = if x >= y { x.sub(&y) } else { x.add(n).sub(&y) };
932 for cand in [gcd(&diff, n), gcd(&x.add(&y), n)] {
933 if let Some(f) = split(n, &cand) {
934 return Some(f);
935 }
936 }
937 }
938 None
939}
940
941fn is_small_prime(p: u64) -> bool {
951 if p < 2 {
952 return false;
953 }
954 let mut d = 2u64;
955 while d * d <= p {
956 if p % d == 0 {
957 return false;
958 }
959 d += 1;
960 }
961 true
962}
963
964fn bigint_mod_u64(n: &BigInt, p: u64) -> u64 {
965 n.div_rem(&BigInt::from_u64(p)).expect("nonzero").1.to_i64().unwrap_or(0) as u64
966}
967
968fn modular_sqrt_u64(a: u64, p: u64) -> Option<u64> {
971 let a = a % p;
972 (0..p).find(|&r| (r as u128 * r as u128 % p as u128) as u64 == a)
973}
974
975pub fn quadratic_sieve(n: &BigInt, b: u64, m_interval: usize) -> Option<(BigInt, BigInt)> {
979 let one = one();
980 let a = isqrt(n).add(&one);
981
982 let mut base: Vec<u64> = Vec::new();
984 let mut roots: Vec<(u64, u64)> = Vec::new();
985 for p in 2..=b {
986 if !is_small_prime(p) {
987 continue;
988 }
989 let np = bigint_mod_u64(n, p);
990 if p == 2 {
991 base.push(2);
992 roots.push((np % 2, np % 2));
993 } else if let Some(r) = modular_sqrt_u64(np, p) {
994 base.push(p);
995 roots.push((r, p - r));
996 }
997 }
998 if base.len() < 3 {
999 return None;
1000 }
1001
1002 let mut sieve = vec![0f64; m_interval];
1004 for (bi, &p) in base.iter().enumerate() {
1005 let am = bigint_mod_u64(&a, p) as i64;
1006 let (r1, r2) = roots[bi];
1007 let both = [r1, r2];
1008 let candidates: &[u64] = if p == 2 { &both[..1] } else { &both[..] };
1009 for &root in candidates {
1010 let mut x = ((root as i64 - am).rem_euclid(p as i64)) as usize;
1011 while x < m_interval {
1012 sieve[x] += (p as f64).ln();
1013 x += p as usize;
1014 }
1015 }
1016 }
1017
1018 let mut rels: Vec<(BigInt, Vec<u64>)> = Vec::new();
1020 for x in 0..m_interval {
1021 let ax = a.add(&BigInt::from_u64(x as u64));
1022 let q = ax.mul(&ax).sub(n);
1023 if q.is_zero() {
1024 continue;
1025 }
1026 if sieve[x] < 0.5 * q.to_f64().ln() {
1029 continue;
1030 }
1031 let mut qq = q;
1032 let mut exps = vec![0u64; base.len()];
1033 for (bi, &p) in base.iter().enumerate() {
1034 let bp = BigInt::from_u64(p);
1035 while let Some((quo, rr)) = qq.div_rem(&bp) {
1036 if rr.is_zero() {
1037 qq = quo;
1038 exps[bi] += 1;
1039 } else {
1040 break;
1041 }
1042 }
1043 }
1044 if qq == one {
1045 rels.push((ax, exps));
1046 if rels.len() > base.len() + 5 {
1047 break;
1048 }
1049 }
1050 }
1051 if rels.len() < 2 {
1052 return None;
1053 }
1054
1055 let rows: Vec<Vec<bool>> = rels.iter().map(|(_, e)| e.iter().map(|&x| x & 1 == 1).collect()).collect();
1056 for dep in gf2_dependencies(&rows, base.len()) {
1057 let mut x = one.clone();
1058 let mut total = vec![0u64; base.len()];
1059 for &i in &dep {
1060 x = rem(&x.mul(&rels[i].0), n);
1061 for (k, &e) in rels[i].1.iter().enumerate() {
1062 total[k] += e;
1063 }
1064 }
1065 let mut y = one.clone();
1066 for (k, &e) in total.iter().enumerate() {
1067 y = rem(&y.mul(&modpow(&BigInt::from_u64(base[k]), &BigInt::from_u64(e / 2), n)), n);
1068 }
1069 let diff = if x >= y { x.sub(&y) } else { x.add(n).sub(&y) };
1070 for cand in [gcd(&diff, n), gcd(&x.add(&y), n)] {
1071 if let Some(f) = split(n, &cand) {
1072 return Some(f);
1073 }
1074 }
1075 }
1076 None
1077}
1078
1079type BPoly = std::collections::BTreeMap<(u32, u32), BigInt>;
1083
1084fn poly_deg(p: &[BigInt]) -> usize {
1086 let mut d = p.len();
1087 while d > 0 && p[d - 1].is_zero() {
1088 d -= 1;
1089 }
1090 d.saturating_sub(1)
1091}
1092
1093fn det_bareiss(mut a: Vec<Vec<BigInt>>) -> BigInt {
1095 let n = a.len();
1096 if n == 0 {
1097 return one();
1098 }
1099 let mut prev = one();
1100 let mut neg = false;
1101 for k in 0..n - 1 {
1102 if a[k][k].is_zero() {
1103 match (k + 1..n).find(|&i| !a[i][k].is_zero()) {
1104 Some(p) => {
1105 a.swap(k, p);
1106 neg = !neg;
1107 }
1108 None => return BigInt::zero(),
1109 }
1110 }
1111 for i in k + 1..n {
1112 for j in k + 1..n {
1113 let num = a[i][j].mul(&a[k][k]).sub(&a[i][k].mul(&a[k][j]));
1114 a[i][j] = num.div_rem(&prev).expect("Bareiss division is exact").0;
1115 }
1116 }
1117 prev = a[k][k].clone();
1118 }
1119 if neg {
1120 a[n - 1][n - 1].negated()
1121 } else {
1122 a[n - 1][n - 1].clone()
1123 }
1124}
1125
1126fn univariate_resultant(a: &[BigInt], b: &[BigInt]) -> BigInt {
1128 let (da, db) = (poly_deg(a), poly_deg(b));
1129 if a.iter().all(|c| c.is_zero()) || b.iter().all(|c| c.is_zero()) {
1130 return BigInt::zero();
1131 }
1132 if da == 0 {
1133 return a[0].pow(db as u32);
1134 }
1135 if db == 0 {
1136 return b[0].pow(da as u32);
1137 }
1138 let n = da + db;
1139 let mut syl = vec![vec![BigInt::zero(); n]; n];
1140 for r in 0..db {
1141 for c in 0..=da {
1142 syl[r][r + c] = a[da - c].clone();
1143 }
1144 }
1145 for r in 0..da {
1146 for c in 0..=db {
1147 syl[db + r][r + c] = b[db - c].clone();
1148 }
1149 }
1150 det_bareiss(syl)
1151}
1152
1153fn eval_bivar_at_y(g: &BPoly, t: &BigInt) -> Vec<BigInt> {
1155 let max_i = g.keys().map(|&(i, _)| i).max().unwrap_or(0) as usize;
1156 let mut uni = vec![BigInt::zero(); max_i + 1];
1157 for (&(i, j), c) in g {
1158 uni[i as usize] = uni[i as usize].add(&c.mul(&t.pow(j)));
1159 }
1160 uni
1161}
1162
1163fn lagrange_interpolate(points: &[(BigInt, BigInt)]) -> Vec<BigInt> {
1165 let n = points.len();
1166 let mut acc = vec![Rational::zero(); n];
1167 for (i, (xi, yi)) in points.iter().enumerate() {
1168 let mut basis = vec![Rational::zero(); n];
1170 basis[0] = Rational::from_bigint(yi.clone());
1171 let mut denom = Rational::from_i64(1);
1172 let mut deg = 0;
1173 for (m, (xm, _)) in points.iter().enumerate() {
1174 if m == i {
1175 continue;
1176 }
1177 for d in (0..=deg).rev() {
1179 let shifted = basis[d].clone();
1180 basis[d + 1] = basis[d + 1].add(&shifted);
1181 basis[d] = basis[d].mul(&Rational::from_bigint(xm.negated()));
1182 }
1183 deg += 1;
1184 denom = denom.mul(&Rational::from_bigint(xi.sub(xm)));
1185 }
1186 let inv = denom.recip().expect("distinct nodes");
1187 for d in 0..n {
1188 acc[d] = acc[d].add(&basis[d].mul(&inv));
1189 }
1190 }
1191 acc.iter().map(|r| r.round()).collect()
1192}
1193
1194fn bivariate_resultant_x(g1: &BPoly, g2: &BPoly) -> Vec<BigInt> {
1198 let dx = |g: &BPoly| g.keys().map(|&(i, _)| i).max().unwrap_or(0) as usize;
1199 let dy = |g: &BPoly| g.keys().map(|&(_, j)| j).max().unwrap_or(0) as usize;
1200 let degree = dx(g1) * dy(g2) + dx(g2) * dy(g1);
1201 let points: Vec<(BigInt, BigInt)> = (0..=degree as i64)
1202 .map(|t| {
1203 let ty = BigInt::from_i64(t);
1204 let r = univariate_resultant(&eval_bivar_at_y(g1, &ty), &eval_bivar_at_y(g2, &ty));
1205 (ty, r)
1206 })
1207 .collect();
1208 lagrange_interpolate(&points)
1209}
1210
1211fn bpoly_mul(a: &BPoly, b: &BPoly) -> BPoly {
1212 let mut out = BPoly::new();
1213 for (&(i1, j1), c1) in a {
1214 for (&(i2, j2), c2) in b {
1215 let e = out.entry((i1 + i2, j1 + j2)).or_insert_with(BigInt::zero);
1216 *e = e.add(&c1.mul(c2));
1217 }
1218 }
1219 out.retain(|_, c| !c.is_zero());
1220 out
1221}
1222
1223fn bpoly_pow(base: &BPoly, e: u32) -> BPoly {
1224 let mut acc = BPoly::new();
1225 acc.insert((0, 0), one());
1226 for _ in 0..e {
1227 acc = bpoly_mul(&acc, base);
1228 }
1229 acc
1230}
1231
1232fn bpoly_eval(g: &BPoly, x: &BigInt, y: &BigInt) -> BigInt {
1233 g.iter().fold(BigInt::zero(), |acc, (&(i, j), c)| acc.add(&c.mul(&x.pow(i)).mul(&y.pow(j))))
1234}
1235
1236fn bpoly_subst_x(g: &BPoly, xv: &BigInt) -> Vec<BigInt> {
1239 let max_j = g.keys().map(|&(_, j)| j).max().unwrap_or(0) as usize;
1240 let mut uni = vec![BigInt::zero(); max_j + 1];
1241 for (&(i, j), c) in g {
1242 uni[j as usize] = uni[j as usize].add(&c.mul(&xv.pow(i)));
1243 }
1244 uni
1245}
1246
1247fn bd_reduced_polys(n: &BigInt, e: &BigInt, m: usize, t: usize, x_bound: &BigInt) -> (Vec<BPoly>, BigInt) {
1250 let one = one();
1251 let y_bound = isqrt(n).mul(&BigInt::from_i64(3));
1252
1253 let mut f = BPoly::new();
1254 f.insert((1, 1), one.clone());
1255 f.insert((1, 0), n.add(&one));
1256 f.insert((0, 0), one.clone());
1257
1258 let mut shifts: Vec<BPoly> = Vec::new();
1259 for k in 0..=m {
1260 let fke: BPoly = {
1261 let scale = e.pow((m - k) as u32);
1262 bpoly_pow(&f, k as u32).into_iter().map(|(key, c)| (key, c.mul(&scale))).collect()
1263 };
1264 for i in 0..=(m - k) {
1265 shifts.push(fke.iter().map(|(&(a, b), c)| ((a + i as u32, b), c.clone())).collect());
1266 }
1267 for j in 1..=t {
1268 shifts.push(fke.iter().map(|(&(a, b), c)| ((a, b + j as u32), c.clone())).collect());
1269 }
1270 }
1271
1272 let mut monos: std::collections::BTreeSet<(u32, u32)> = std::collections::BTreeSet::new();
1273 for s in &shifts {
1274 monos.extend(s.keys());
1275 }
1276 let monos: Vec<(u32, u32)> = monos.into_iter().collect();
1277 let scale_of: Vec<BigInt> = monos.iter().map(|&(i, j)| x_bound.pow(i).mul(&y_bound.pow(j))).collect();
1278 let col: std::collections::HashMap<(u32, u32), usize> =
1279 monos.iter().enumerate().map(|(i, &k)| (k, i)).collect();
1280 let rows: Vec<Vec<BigInt>> = shifts
1281 .iter()
1282 .map(|s| {
1283 let mut row = vec![BigInt::zero(); monos.len()];
1284 for (&key, c) in s {
1285 let ci = col[&key];
1286 row[ci] = c.mul(&scale_of[ci]);
1287 }
1288 row
1289 })
1290 .collect();
1291
1292 let reduced = crate::lattice::lll_reduce_bigint_fp(&rows);
1293 let polys: Vec<BPoly> = reduced
1294 .iter()
1295 .map(|row| {
1296 let mut g = BPoly::new();
1297 for (ci, &key) in monos.iter().enumerate() {
1298 if !row[ci].is_zero() {
1299 let (q, r) = row[ci].div_rem(&scale_of[ci]).expect("nonzero");
1300 if r.is_zero() {
1301 g.insert(key, q);
1302 }
1303 }
1304 }
1305 g
1306 })
1307 .filter(|g| !g.is_empty())
1308 .collect();
1309 (polys, y_bound)
1310}
1311
1312fn real_root_candidates(res: &[BigInt], bound: &BigInt) -> Vec<BigInt> {
1315 let maxc = res.iter().map(|c| c.abs()).max().filter(|c| !c.is_zero());
1316 let Some(maxc) = maxc else {
1317 return Vec::new();
1318 };
1319 let cf: Vec<f64> =
1320 res.iter().map(|c| Rational::new(c.clone(), maxc.clone()).map(|r| r.to_f64()).unwrap_or(0.0)).collect();
1321 let eval = |x: f64| cf.iter().rev().fold(0.0, |acc, &c| acc * x + c);
1322 let deriv = |x: f64| (1..cf.len()).rev().fold(0.0, |acc, k| acc * x + cf[k] * k as f64);
1323 let bf = bound.to_f64();
1324 let mut seeds: Vec<f64> = Vec::new();
1325 let mut p = 1.0;
1326 while p <= bf {
1327 seeds.push(p);
1328 seeds.push(-p);
1329 p *= 1.3;
1330 }
1331 let mut out: Vec<BigInt> = Vec::new();
1332 for &s in &seeds {
1333 let mut x = s;
1334 for _ in 0..200 {
1335 let d = deriv(x);
1336 if d.abs() < 1e-18 || !x.is_finite() {
1337 break;
1338 }
1339 let nx = x - eval(x) / d;
1340 if !nx.is_finite() {
1341 break;
1342 }
1343 if (nx - x).abs() < 0.4 {
1344 x = nx;
1345 break;
1346 }
1347 x = nx;
1348 }
1349 if x.is_finite() && x.abs() <= bf * 1.01 {
1350 if let Some(c) = BigInt::parse_decimal(&format!("{:.0}", x.abs())) {
1351 let cand = if x < 0.0 { c.negated() } else { c };
1352 if !out.contains(&cand) {
1353 out.push(cand);
1354 }
1355 }
1356 }
1357 }
1358 out
1359}
1360
1361pub fn boneh_durfee(n: &BigInt, e: &BigInt, m: usize, t: usize, x_bound: &BigInt) -> Option<(BigInt, BigInt)> {
1368 let (polys, y_bound) = bd_reduced_polys(n, e, m, t, x_bound);
1369
1370 let x_lim = x_bound.to_i64().unwrap_or(0).max(0);
1374 let four_n = BigInt::from_i64(4).mul(n);
1375 let two = two();
1376 for g in polys.iter().take(6) {
1377 for ki in 1..=x_lim {
1378 let k = BigInt::from_i64(ki);
1379 let gy = bpoly_subst_x(g, &k);
1380 if gy.iter().all(|c| c.is_zero()) {
1381 continue;
1382 }
1383 for y0 in integer_roots_of(&gy, &y_bound) {
1384 let s = y0.negated();
1385 if s.is_negative() || s.is_zero() {
1386 continue;
1387 }
1388 let disc = s.mul(&s).sub(&four_n);
1389 if disc.is_negative() {
1390 continue;
1391 }
1392 let sq = isqrt(&disc);
1393 if sq.mul(&sq) == disc {
1394 let p = s.sub(&sq).div_rem(&two).expect("nonzero").0;
1395 let q = s.add(&sq).div_rem(&two).expect("nonzero").0;
1396 if verify_factorization(n, &p, &q) {
1397 return Some((p, q));
1398 }
1399 }
1400 }
1401 }
1402 }
1403 None
1404}
1405
1406#[derive(Clone, Copy, Debug)]
1409pub struct StructuralBudget {
1410 pub trial_limit: u64,
1411 pub fermat_iters: u64,
1412 pub pminus1_bound: u64,
1413 pub rho_iters: u64,
1414}
1415
1416impl Default for StructuralBudget {
1417 fn default() -> Self {
1422 Self { trial_limit: 1_000, fermat_iters: 3_000, pminus1_bound: 500, rho_iters: 5_000 }
1423 }
1424}
1425
1426#[derive(Clone, Debug, PartialEq, Eq)]
1428pub struct StructuralWitness {
1429 pub p: BigInt,
1430 pub q: BigInt,
1431 pub method: &'static str,
1432}
1433
1434pub fn structural_factor(n: &BigInt, budget: StructuralBudget) -> Option<StructuralWitness> {
1438 let mk = |(p, q): (BigInt, BigInt), method: &'static str| StructuralWitness { p, q, method };
1439 if let Some(f) = trial_division(n, budget.trial_limit) {
1440 return Some(mk(f, "trial division (small factor)"));
1441 }
1442 if let Some(f) = fermat(n, budget.fermat_iters) {
1443 return Some(mk(f, "Fermat (close primes)"));
1444 }
1445 if let Some(f) = pollard_p_minus_1(n, budget.pminus1_bound) {
1446 return Some(mk(f, "Pollard p−1 (smooth p−1)"));
1447 }
1448 if let Some(f) = pollard_rho(n, budget.rho_iters) {
1449 return Some(mk(f, "Pollard rho"));
1450 }
1451 None
1452}
1453
1454#[cfg(test)]
1455mod tests {
1456 use super::*;
1457
1458 fn big(s: &str) -> BigInt {
1459 BigInt::parse_decimal(s).expect("valid decimal")
1460 }
1461
1462 #[test]
1463 fn primality_and_next_prime_agree_with_known_values() {
1464 assert!(is_probable_prime(&BigInt::from_i64(2)));
1465 assert!(is_probable_prime(&BigInt::from_i64(1_000_003)));
1466 assert!(!is_probable_prime(&BigInt::from_i64(1_000_000)));
1467 assert!(!is_probable_prime(&big("1000000000000000000000000000000"))); assert_eq!(next_prime(&BigInt::from_i64(1_000_000)), BigInt::from_i64(1_000_003));
1469 }
1470
1471 #[test]
1472 fn isqrt_is_exact() {
1473 assert_eq!(isqrt(&BigInt::from_i64(0)), BigInt::from_i64(0));
1474 assert_eq!(isqrt(&BigInt::from_i64(15)), BigInt::from_i64(3));
1475 assert_eq!(isqrt(&BigInt::from_i64(16)), BigInt::from_i64(4));
1476 let big_sq = big("1000000000000000000000000000000"); assert_eq!(isqrt(&big_sq), big("1000000000000000"));
1478 }
1479
1480 #[test]
1481 fn trial_division_catches_a_small_factor() {
1482 let p = next_prime(&big("1000000000000000000000000000000"));
1483 let n = BigInt::from_i64(3).mul(&p);
1484 let (a, b) = trial_division(&n, 100).expect("the small factor 3 is found");
1485 assert!(verify_factorization(&n, &a, &b));
1486 assert!(a == BigInt::from_i64(3) || b == BigInt::from_i64(3));
1487 }
1488
1489 #[test]
1490 fn fermat_crushes_close_primes() {
1491 let p = next_prime(&big("1000000000000000000000000000000"));
1492 let q = next_prime(&p.add(&BigInt::from_i64(2)));
1493 let n = p.mul(&q);
1494 let (a, b) = fermat(&n, 100_000).expect("adjacent primes fall to Fermat");
1495 assert!(verify_factorization(&n, &a, &b));
1496 }
1497
1498 #[test]
1499 fn pollard_rho_factors_a_moderate_semiprime() {
1500 let p = next_prime(&big("1000000007"));
1501 let q = next_prime(&big("2000000011"));
1502 let n = p.mul(&q);
1503 let (a, b) = pollard_rho(&n, 500_000).expect("rho factors a ~60-bit semiprime");
1504 assert!(verify_factorization(&n, &a, &b));
1505 }
1506
1507 #[test]
1508 fn pollard_p_minus_1_crushes_a_smooth_prime() {
1509 let mut smooth = one();
1511 for &pr in &[2u64, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37] {
1512 smooth = smooth.mul(&BigInt::from_u64(pr));
1513 }
1514 let mut p = smooth.add(&one());
1515 let mut k = 1u64;
1516 while !is_probable_prime(&p) {
1517 k += 1;
1518 assert!(k <= 40, "a smooth prime is found with a small (≤40-smooth) multiplier");
1519 p = smooth.mul(&BigInt::from_u64(k)).add(&one());
1520 }
1521 let q = next_prime(&big("999999999999999999999999999999"));
1522 let n = p.mul(&q);
1523 let (a, b) = pollard_p_minus_1(&n, 40).expect("a smooth p−1 falls to Pollard p−1");
1524 assert!(verify_factorization(&n, &a, &b));
1525 }
1526
1527 #[test]
1528 fn wiener_crushes_a_small_private_exponent() {
1529 let p = next_prime(&big("1000000007"));
1530 let q = next_prime(&big("3000000019"));
1531 let n = p.mul(&q);
1532 let phi = p.sub(&one()).mul(&q.sub(&one()));
1533 let d = BigInt::from_i64(7919); let e = mod_inverse(&d, &phi).expect("d is coprime to φ");
1535 let (a, b) = wiener(&e, &n).expect("a small private exponent falls to Wiener");
1536 assert!(verify_factorization(&n, &a, &b));
1537 }
1538
1539 #[test]
1540 fn batch_gcd_crushes_a_shared_prime() {
1541 let p = next_prime(&big("1000000000000000000000000000000"));
1542 let q1 = next_prime(&big("3000000000000000000000000000000"));
1543 let q2 = next_prime(&big("7000000000000000000000000000000"));
1544 let (n1, n2) = (p.mul(&q1), p.mul(&q2));
1545 let hits = batch_gcd(&[n1, n2]);
1546 assert_eq!(hits.len(), 1, "the shared prime is detected");
1547 assert_eq!(hits[0].2, p, "and it is exactly p");
1548 }
1549
1550 #[test]
1551 fn structural_factor_orchestrates_the_arsenal() {
1552 let p = next_prime(&big("1000000000000000000000000000000"));
1554 let q = next_prime(&p.add(&BigInt::from_i64(2)));
1555 let n = p.mul(&q);
1556 let w = structural_factor(&n, StructuralBudget::default()).expect("weakness found");
1557 assert!(verify_factorization(&n, &w.p, &w.q));
1558 assert_eq!(w.method, "Fermat (close primes)");
1559 }
1560
1561 #[test]
1562 fn a_soundly_generated_modulus_resists_the_entire_structural_arsenal() {
1563 let p = next_prime(&big("1000000000000000000000000000057"));
1569 let q = next_prime(&big("9000000000000000000000000000000"));
1570 let n = p.mul(&q);
1571 assert!(
1572 structural_factor(&n, StructuralBudget::default()).is_none(),
1573 "a sound modulus has no structural shortcut — the ceiling stands"
1574 );
1575 }
1576
1577 #[test]
1578 fn resultant_machinery_eliminates_a_variable() {
1579 let det = det_bareiss(vec![
1581 vec![BigInt::from_i64(1), BigInt::from_i64(2)],
1582 vec![BigInt::from_i64(3), BigInt::from_i64(4)],
1583 ]);
1584 assert_eq!(det, BigInt::from_i64(-2));
1585
1586 let mut g1 = BPoly::new();
1589 g1.insert((1, 0), BigInt::from_i64(1));
1590 g1.insert((0, 1), BigInt::from_i64(-1));
1591 let mut g2 = BPoly::new();
1592 g2.insert((1, 0), BigInt::from_i64(1));
1593 g2.insert((0, 1), BigInt::from_i64(1));
1594 g2.insert((0, 0), BigInt::from_i64(-2));
1595 let res = bivariate_resultant_x(&g1, &g2);
1596 assert!(poly_eval(&res, &BigInt::from_i64(1)).is_zero(), "resultant vanishes at the shared y = 1");
1597 assert!(!poly_eval(&res, &BigInt::from_i64(0)).is_zero(), "and is nonzero away from it");
1598 }
1599
1600 #[test]
1601 #[ignore] fn boneh_durfee_breaks_small_d() {
1603 let p = next_prime(&big("1000003"));
1604 let q = next_prime(&big("2000003"));
1605 let n = p.mul(&q);
1606 let phi = p.sub(&one()).mul(&q.sub(&one()));
1607 let d = BigInt::from_i64(1423); let e = mod_inverse(&d, &phi).unwrap();
1609 assert!(wiener(&e, &n).is_none(), "beyond Wiener (control)");
1610 let x_bound = BigInt::from_i64(1 << 11);
1611 let (a, b) = boneh_durfee(&n, &e, 8, 3, &x_bound).expect("Boneh-Durfee breaks small-d");
1612 eprintln!("SMALL-d BROKEN: {a:?} · {b:?}");
1613 assert!(verify_factorization(&n, &a, &b), "the recovered factors check out");
1614 }
1615
1616 #[test]
1617 fn boneh_durfee_lattice_lifts_the_modular_root_to_the_integers() {
1618 let p = next_prime(&big("100003"));
1627 let q = next_prime(&big("200003"));
1628 let n = p.mul(&q);
1629 let phi = p.sub(&one()).mul(&q.sub(&one()));
1630 let d = BigInt::from_i64(43);
1631 let e = mod_inverse(&d, &phi).unwrap();
1632 let k = e.mul(&d).sub(&one()).div_rem(&phi).unwrap().0;
1633 let s = p.add(&q);
1634 let (polys, _) = bd_reduced_polys(&n, &e, 4, 2, &BigInt::from_i64(1 << 8));
1635 let vanishing = polys.iter().filter(|g| g.len() > 1 && bpoly_eval(g, &k, &s.negated()).is_zero()).count();
1636 assert!(vanishing >= 5, "the reduced lattice lifts the modular root to integer roots, got {vanishing}");
1637 }
1638
1639 #[test]
1640 fn quadratic_sieve_factors_a_larger_semiprime_by_sieving() {
1641 let p = next_prime(&big("100000"));
1644 let q = next_prime(&big("100050"));
1645 let n = p.mul(&q);
1646 let (a, b) = quadratic_sieve(&n, 500, 50_000).expect("the quadratic sieve factors N");
1647 assert!(verify_factorization(&n, &a, &b), "sieving + a GF(2) dependency split N");
1648 }
1649
1650 #[test]
1651 fn dixon_breaks_a_semiprime_with_our_gf2_symmetry_breaking() {
1652 let n = BigInt::from_i64(179).mul(&BigInt::from_i64(257));
1655 let base = [2u64, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31];
1656 let (a, b) = dixon_factor(&n, &base, 50_000).expect("Dixon factors via a GF(2) dependency");
1657 assert!(verify_factorization(&n, &a, &b), "the congruence of squares splits N");
1658 }
1659
1660 #[test]
1661 fn crt_and_nth_root_are_correct() {
1662 let x = crt(
1664 &[BigInt::from_i64(2), BigInt::from_i64(3), BigInt::from_i64(2)],
1665 &[BigInt::from_i64(3), BigInt::from_i64(5), BigInt::from_i64(7)],
1666 )
1667 .unwrap();
1668 assert_eq!(x, BigInt::from_i64(23));
1669 assert_eq!(integer_nth_root(&BigInt::from_i64(27), 3), BigInt::from_i64(3));
1670 assert_eq!(integer_nth_root(&big("1000000000000000000"), 3), BigInt::from_i64(1000000));
1671 assert_eq!(integer_nth_root(&BigInt::from_i64(3125), 5), BigInt::from_i64(5));
1672 assert_eq!(integer_nth_root(&BigInt::from_i64(26), 3), BigInt::from_i64(2), "floor cbrt(26) = 2");
1673 }
1674
1675 #[test]
1676 fn hastad_broadcast_recovers_the_message() {
1677 let e = 3u32;
1679 let m = big("123456789");
1680 let moduli: Vec<BigInt> = vec![
1681 next_prime(&big("100000007")).mul(&next_prime(&big("200000033"))),
1682 next_prime(&big("300000007")).mul(&next_prime(&big("400000009"))),
1683 next_prime(&big("500000003")).mul(&next_prime(&big("600000011"))),
1684 ];
1685 let e_big = BigInt::from_u64(e as u64);
1686 let ciphertexts: Vec<BigInt> = moduli.iter().map(|n| modpow(&m, &e_big, n)).collect();
1687 let recovered = hastad_broadcast_attack(e, &ciphertexts, &moduli).expect("Håstad recovers m");
1688 assert_eq!(recovered, m, "the broadcast plaintext falls out of the ciphertexts alone");
1689 }
1690
1691 #[test]
1692 fn common_modulus_recovers_the_message() {
1693 let n = next_prime(&big("1000000007")).mul(&next_prime(&big("2000000011")));
1695 let m = big("31415926535");
1696 let (e1, e2) = (BigInt::from_i64(65537), BigInt::from_i64(3));
1697 let c1 = modpow(&m, &e1, &n);
1698 let c2 = modpow(&m, &e2, &n);
1699 let recovered = common_modulus_attack(&e1, &e2, &c1, &c2, &n).expect("common modulus recovers m");
1700 assert_eq!(recovered, m, "a shared modulus + coprime exponents leaks the message via Bézout");
1701 }
1702
1703 #[test]
1704 fn franklin_reiter_recovers_related_messages() {
1705 let n = next_prime(&big("1000000007")).mul(&next_prime(&big("2000000011")));
1707 let e = 3u32;
1708 let e_big = BigInt::from_u64(e as u64);
1709 let m = big("424242424242");
1710 let r = big("31337");
1711 let c1 = modpow(&m, &e_big, &n);
1712 let c2 = modpow(&m.add(&r), &e_big, &n);
1713 let recovered = franklin_reiter_attack(e, &r, &c1, &c2, &n).expect("Franklin-Reiter recovers m");
1714 assert_eq!(recovered, m, "the related pair leaks the plaintext via polynomial GCD");
1715 }
1716
1717 #[test]
1718 fn low_exponent_no_padding_recovers_small_message() {
1719 let n = next_prime(&big("100000000000000003")).mul(&next_prime(&big("200000000000000003")));
1721 let m = big("123456");
1722 let c = modpow(&m, &BigInt::from_i64(3), &n);
1723 let recovered = low_exponent_message(&c, 3).expect("cube root recovers the small message");
1724 assert_eq!(recovered, m, "no padding under a small exponent is just an integer root");
1725 }
1726
1727 #[test]
1728 fn coppersmith_factors_from_known_high_bits() {
1729 let p = next_prime(&big("18446744073709551629"));
1733 let q = next_prime(&big("36893488147419103237"));
1734 let n = p.mul(&q);
1735 let unknown_bits = 20u32;
1736 let mut mask = one();
1737 for _ in 0..unknown_bits {
1738 mask = mask.mul(&two());
1739 }
1740 let (p_div, _) = p.div_rem(&mask).expect("nonzero");
1741 let p_high = p_div.mul(&mask); let (a, b) = coppersmith_factor_high_bits(&n, &p_high, unknown_bits).expect("Coppersmith recovers p");
1743 assert!(verify_factorization(&n, &a, &b), "the recovered factorization checks out");
1744 }
1745
1746 #[test]
1747 fn rsa_breaks_end_to_end_when_the_modulus_factors() {
1748 let p = next_prime(&big("1000000000000000000000000000057"));
1750 let q = next_prime(&p.add(&BigInt::from_i64(100)));
1751 let n = p.mul(&q);
1752 let e = BigInt::from_i64(65537);
1753 let d = rsa_private_exponent(&e, &p, &q).expect("e is coprime to φ");
1754 let m = big("123456789987654321");
1755 let c = modpow(&m, &e, &n);
1756 assert_eq!(modpow(&c, &d, &n), m, "RSA encryption round-trips");
1757
1758 let w = structural_factor(&n, StructuralBudget::default()).expect("close primes factor");
1761 let d_broken = rsa_private_exponent(&e, &w.p, &w.q).expect("recover d from the factors");
1762 assert_eq!(modpow(&c, &d_broken, &n), m, "the recovered key decrypts — RSA broken end to end");
1763 }
1764
1765 #[test]
1766 fn recovering_the_private_key_is_equivalent_to_factoring() {
1767 let p = next_prime(&big("1000000000000000000000000000057"));
1769 let q = next_prime(&big("9000000000000000000000000000000"));
1770 let n = p.mul(&q);
1771 let e = BigInt::from_i64(65537);
1772 let d = rsa_private_exponent(&e, &p, &q).expect("e is coprime to φ");
1773
1774 assert!(
1776 structural_factor(&n, StructuralBudget::default()).is_none(),
1777 "the sound modulus resists every structural attack"
1778 );
1779
1780 let (fp, fq) = factor_via_private_exponent(&n, &e, &d).expect("the private exponent factors N");
1785 assert!(verify_factorization(&n, &fp, &fq), "recovering d recovers the factorization");
1786 }
1787}