use alloc::vec::Vec;
use crate::nat::Nat;
fn mod_u64(n: &Nat, p: u64) -> u64 {
let mut r: u128 = 0;
for &limb in n.as_limbs().iter().rev() {
r = ((r << 64) | limb as u128) % p as u128;
}
r as u64
}
fn sqrt_mod_u64(a: u64, p: u64) -> Option<u64> {
let a = a % p;
if a == 0 {
return Some(0);
}
if p == 2 {
return Some(a & 1);
}
if pow_mod_u64(a, (p - 1) / 2, p) != 1 {
return None; }
if p % 4 == 3 {
return Some(pow_mod_u64(a, (p + 1) / 4, p));
}
let mut q = p - 1;
let mut s = 0u32;
while q & 1 == 0 {
q >>= 1;
s += 1;
}
let mut z = 2u64;
while pow_mod_u64(z, (p - 1) / 2, p) != p - 1 {
z += 1;
}
let mut m = s;
let mut c = pow_mod_u64(z, q, p);
let mut t = pow_mod_u64(a, q, p);
let mut r = pow_mod_u64(a, q.div_ceil(2), p);
while t != 1 {
let mut i = 0u32;
let mut t2 = t;
while t2 != 1 {
t2 = mul_mod_u64(t2, t2, p);
i += 1;
}
let b = pow_mod_u64(c, 1u64 << (m - i - 1), p);
m = i;
c = mul_mod_u64(b, b, p);
t = mul_mod_u64(t, c, p);
r = mul_mod_u64(r, b, p);
}
Some(r)
}
#[inline]
fn mul_mod_u64(a: u64, b: u64, p: u64) -> u64 {
((a as u128 * b as u128) % p as u128) as u64
}
fn pow_mod_u64(mut base: u64, mut exp: u64, p: u64) -> u64 {
let mut r = 1u64;
base %= p;
while exp > 0 {
if exp & 1 == 1 {
r = mul_mod_u64(r, base, p);
}
base = mul_mod_u64(base, base, p);
exp >>= 1;
}
r
}
fn primes_up_to(limit: u64) -> Vec<u64> {
if limit < 2 {
return Vec::new();
}
let n = limit as usize + 1;
let mut sieve = alloc::vec![true; n];
sieve[0] = false;
sieve[1] = false;
let mut i = 2usize;
while i * i < n {
if sieve[i] {
let mut j = i * i;
while j < n {
sieve[j] = false;
j += i;
}
}
i += 1;
}
(2..n).filter(|&i| sieve[i]).map(|i| i as u64).collect()
}
struct FbPrime {
p: u64,
logp: u8,
root1: u64,
root2: u64,
}
struct Relation {
base: Nat,
exps: Vec<(usize, u32)>,
parity: Vec<u64>,
}
struct Params {
bound: u64,
m: u64,
fudge: u32,
}
fn params_for(digits: usize) -> Params {
let (bound, m): (u64, u64) = match digits {
0..=24 => (3_000, 300_000),
25..=29 => (10_000, 4_000_000),
30..=34 => (25_000, 10_000_000),
35..=39 => (45_000, 20_000_000),
40..=42 => (75_000, 40_000_000),
43..=45 => (120_000, 70_000_000),
_ => (250_000, 120_000_000),
};
let fudge = (64 - bound.leading_zeros()) + 2;
Params { bound, m, fudge }
}
fn build_factor_base(n: &Nat, bound: u64) -> Vec<FbPrime> {
let mut fb = Vec::new();
for p in primes_up_to(bound) {
let a = mod_u64(n, p);
if p == 2 {
fb.push(FbPrime {
p: 2,
logp: 1,
root1: 1,
root2: 1,
});
continue;
}
if a == 0 {
fb.push(FbPrime {
p,
logp: 64 - p.leading_zeros() as u8 - 1,
root1: 0,
root2: p,
});
continue;
}
if let Some(r) = sqrt_mod_u64(a, p) {
fb.push(FbPrime {
p,
logp: (64 - p.leading_zeros() - 1) as u8,
root1: r,
root2: p - r,
});
}
}
fb
}
fn collect_relations(
n: &Nat,
a: &Nat,
fb: &[FbPrime],
params: &Params,
m: u64,
want: usize,
) -> Vec<Relation> {
let width = (2 * m + 1) as usize;
let mut logs = alloc::vec![0u8; width];
for fp in fb {
if fp.p == 2 {
let a_par = mod_u64(a, 2) as i64;
let mut i = (1 - a_par - m as i64).rem_euclid(2) as usize;
while i < width {
logs[i] = logs[i].saturating_add(1);
i += 2;
}
continue;
}
let a_mod = mod_u64(a, fp.p) as i64;
let single = fp.root1 == fp.root2;
for (k, &root) in [fp.root1, fp.root2].iter().enumerate() {
if k == 1 && single {
break; }
let start = (root as i64 - a_mod + m as i64).rem_euclid(fp.p as i64) as usize;
let mut i = start;
while i < width {
logs[i] = logs[i].saturating_add(fp.logp);
i += fp.p as usize;
}
}
}
let half_log2n = (n.bit_len() / 2) as u32;
let mut relations = Vec::new();
#[allow(clippy::needless_range_loop)] for i in 0..width {
let x = i as i64 - m as i64;
let xlog = if x == 0 {
0
} else {
63 - (x.unsigned_abs()).leading_zeros()
};
let target = half_log2n.saturating_add(xlog);
if (logs[i] as u32) + params.fudge < target {
continue;
}
if let Some(rel) = try_relation(n, a, fb, x) {
relations.push(rel);
if relations.len() >= want {
break;
}
}
}
relations
}
fn try_relation(n: &Nat, a: &Nat, fb: &[FbPrime], x: i64) -> Option<Relation> {
let base = if x >= 0 {
a.add(&Nat::from_u64(x as u64))
} else {
a.checked_sub(&Nat::from_u64((-x) as u64))?
};
let sq = base.square();
let (mut mag, neg) = if sq >= *n {
(sq.checked_sub(n).unwrap(), false)
} else {
(n.checked_sub(&sq).unwrap(), true)
};
if mag.is_zero() {
return None;
}
let mut exps: Vec<(usize, u32)> = Vec::new();
if neg {
exps.push((0, 1)); }
for (idx, fp) in fb.iter().enumerate().skip(1) {
let pn = Nat::from_u64(fp.p);
let mut e = 0u32;
loop {
let (q, r) = mag.div_rem(&pn).unwrap();
if !r.is_zero() {
break;
}
mag = q;
e += 1;
}
if e > 0 {
exps.push((idx, e));
}
if mag.is_one() {
break;
}
}
if !mag.is_one() {
return None; }
let words = fb.len().div_ceil(64);
let mut parity = alloc::vec![0u64; words];
for &(idx, e) in &exps {
if e & 1 == 1 {
parity[idx / 64] ^= 1u64 << (idx % 64);
}
}
Some(Relation { base, exps, parity })
}
fn find_dependencies(relations: &[Relation], cols: usize) -> Vec<Vec<usize>> {
let m = relations.len();
let pword = cols.div_ceil(64);
let hword = m.div_ceil(64);
let mut par: Vec<Vec<u64>> = relations.iter().map(|r| r.parity.clone()).collect();
for row in &mut par {
row.resize(pword, 0);
}
let mut hist: Vec<Vec<u64>> = (0..m)
.map(|i| {
let mut h = alloc::vec![0u64; hword];
h[i / 64] |= 1u64 << (i % 64);
h
})
.collect();
let mut pivot_row: Vec<Option<usize>> = alloc::vec![None; cols];
let mut deps = Vec::new();
for i in 0..m {
for c in 0..cols {
if par[i][c / 64] & (1u64 << (c % 64)) == 0 {
continue;
}
if let Some(pr) = pivot_row[c] {
let (lo, hi) = par.split_at_mut(i);
for (a, b) in hi[0].iter_mut().zip(&lo[pr]) {
*a ^= *b;
}
let (lo, hi) = hist.split_at_mut(i);
for (a, b) in hi[0].iter_mut().zip(&lo[pr]) {
*a ^= *b;
}
}
}
let mut pivot = None;
for c in 0..cols {
if par[i][c / 64] & (1u64 << (c % 64)) != 0 {
pivot = Some(c);
break;
}
}
match pivot {
Some(c) => pivot_row[c] = Some(i),
None => {
let subset: Vec<usize> = (0..m)
.filter(|&j| hist[i][j / 64] & (1u64 << (j % 64)) != 0)
.collect();
if !subset.is_empty() {
deps.push(subset);
}
}
}
}
deps
}
fn factor_from_dependencies(
n: &Nat,
fb: &[FbPrime],
relations: &[Relation],
deps: &[Vec<usize>],
) -> Option<Nat> {
for subset in deps {
let mut x = Nat::one();
let mut total: Vec<u32> = alloc::vec![0u32; fb.len()];
for &i in subset {
let r = &relations[i];
x = r.base.mul(&x).div_rem(n).unwrap().1;
for &(idx, e) in &r.exps {
total[idx] += e;
}
}
let mut y = Nat::one();
for (idx, &e) in total.iter().enumerate().skip(1) {
if e == 0 {
continue;
}
debug_assert_eq!(e & 1, 0, "dependency exponent must be even");
let pe = Nat::from_u64(fb[idx].p).modpow(&Nat::from_u64((e / 2) as u64), n);
y = y.mul(&pe).div_rem(n).unwrap().1;
}
let diff = if x >= y {
x.checked_sub(&y).unwrap()
} else {
n.checked_sub(&y.checked_sub(&x).unwrap()).unwrap()
};
if !diff.is_zero() {
let g = diff.gcd(n);
if !g.is_one() && &g != n {
return Some(g);
}
}
let sum = x.add(&y).div_rem(n).unwrap().1;
if !sum.is_zero() {
let g = sum.gcd(n);
if !g.is_one() && &g != n {
return Some(g);
}
}
}
None
}
pub(crate) fn qs_factor(n: &Nat) -> Option<Nat> {
let digits = (n.bit_len() * 30103 / 100000 + 1) as usize;
let params = params_for(digits);
let fb = build_factor_base(n, params.bound);
if fb.len() < 3 {
return None;
}
let root = n.isqrt();
let a = if root.square() == *n {
return Some(root); } else {
root.add(&Nat::one())
};
let want = fb.len() + 16 + fb.len() / 20;
const M_CAP: u64 = 300_000_000;
let mut m = params.m.min(M_CAP);
let mut relations = collect_relations(n, &a, &fb, ¶ms, m, want);
while relations.len() < want && m < M_CAP {
m = m.saturating_mul(2).min(M_CAP);
relations = collect_relations(n, &a, &fb, ¶ms, m, want);
}
if relations.len() <= fb.len() {
return None; }
let deps = find_dependencies(&relations, fb.len());
factor_from_dependencies(n, &fb, &relations, &deps)
}
#[cfg(test)]
mod tests {
use super::*;
fn prime_at_least(start: u64) -> Nat {
let mut c = start | 1;
loop {
let v = Nat::from_u64(c);
if v.is_prime_bpsw() {
return v;
}
c += 2;
}
}
fn assert_splits(p: &Nat, q: &Nat) {
let composite = p.mul(q);
let f = qs_factor(&composite).expect("QS finds a factor");
assert!(f == *p || f == *q, "factor {f:?} is one of the primes");
let (cof, r) = composite.div_rem(&f).unwrap();
assert!(r.is_zero());
assert!(cof == *p || cof == *q);
}
#[test]
fn splits_balanced_semiprimes() {
assert_splits(
&prime_at_least(3_000_000_019),
&prime_at_least(4_000_000_007),
);
assert_splits(
&prime_at_least(5_000_000_000_021),
&prime_at_least(9_000_000_000_011),
);
}
}