use crate::traits::ExactRoots;
use num_integer::{Integer, Roots};
use num_modular::{ModularCoreOps, ModularUnaryOps};
use num_traits::{CheckedAdd, CheckedMul, FromPrimitive, NumRef, RefNum};
use std::collections::BTreeMap;
pub fn trial_division<
I: Iterator<Item = u64>,
T: Integer + Clone + Roots + NumRef + FromPrimitive,
>(
primes: I,
target: T,
limit: Option<u64>,
) -> (BTreeMap<u64, usize>, Result<T, T>)
where
for<'r> &'r T: RefNum<T>,
{
let tsqrt: T = Roots::sqrt(&target) + T::one();
let limit = if let Some(l) = limit {
tsqrt.clone().min(T::from_u64(l).unwrap())
} else {
tsqrt.clone()
};
let mut residual = target;
let mut result = BTreeMap::new();
let mut factored = false;
for (p, pt) in primes.map(|p| (p, T::from_u64(p).unwrap())) {
if pt > tsqrt {
factored = true;
}
if pt > limit {
break;
}
while residual.is_multiple_of(&pt) {
residual = residual / &pt;
*result.entry(p).or_insert(0) += 1;
}
if residual.is_one() {
factored = true;
break;
}
}
if factored {
(result, Ok(residual))
} else {
(result, Err(residual))
}
}
pub fn pollard_rho<
T: Integer
+ FromPrimitive
+ NumRef
+ Clone
+ for<'r> ModularCoreOps<&'r T, &'r T, Output = T>
+ for<'r> ModularUnaryOps<&'r T, Output = T>,
>(
target: &T,
start: T,
offset: T,
max_iter: usize,
) -> (Option<T>, usize)
where
for<'r> &'r T: RefNum<T>,
{
let mut a = start.clone();
let mut b = start.clone();
let mut z = T::one() % target;
let (mut i, mut j) = (0usize, 1usize);
let mut s = start;
let mut backtrace = false;
while i < max_iter {
i += 1;
a = a.sqm(target).addm(&offset, target);
if a == b {
return (None, i);
}
let diff = if b > a { &b - &a } else { &a - &b }; z = z.mulm(&diff, target);
if z.is_zero() {
if backtrace {
return (None, i);
} else {
backtrace = true;
a = std::mem::replace(&mut s, T::one()); z = T::one() % target; continue;
}
}
if i == j || i & 127 == 0 || backtrace {
let d = z.gcd(target);
if !d.is_one() && &d != target {
return (Some(d), i);
}
s = a.clone();
}
if i == j {
b = a.clone();
j <<= 1;
}
}
(None, i)
}
pub fn squfof<T: Integer + NumRef + Clone + ExactRoots + std::fmt::Debug>(
target: &T,
mul_target: T,
max_iter: usize,
) -> (Option<T>, usize)
where
for<'r> &'r T: RefNum<T>,
{
assert!(
&mul_target.is_multiple_of(target),
"mul_target should be multiples of target"
);
let rd = Roots::sqrt(&mul_target);
#[inline]
fn rho<T: Integer + Clone + NumRef>(rd: &T, p: &T, q: &mut T, qm1: &mut T) -> T
where
for<'r> &'r T: RefNum<T>,
{
let b = (rd + p).div_floor(&*q);
let new_p = &b * &*q - p;
let new_q = if p > &new_p {
&*qm1 + b * (p - &new_p)
} else {
&*qm1 - b * (&new_p - p)
};
*qm1 = std::mem::replace(q, new_q);
new_p
}
let (mut p, mut q, mut qm1) = (rd.clone(), &mul_target - &rd * &rd, T::one());
if q.is_zero() {
return (Some(rd), 0);
}
for i in 1..max_iter {
p = rho(&rd, &p, &mut q, &mut qm1);
if i.is_odd() {
if let Some(rq) = q.sqrt_exact() {
let b = (&rd - &p) / &rq;
let mut u = b * &rq + &p;
let (mut v, mut vm1) = ((&mul_target - &u * &u) / &rq, rq);
loop {
let new_u = rho(&rd, &u, &mut v, &mut vm1);
if new_u == u {
break;
} else {
u = new_u;
}
}
let d = target.gcd(&u);
if d > T::one() && &d < target {
return (Some(d), i);
}
}
}
}
(None, max_iter)
}
pub const SQUFOF_MULTIPLIERS: [u16; 38] = [
3 * 5 * 7 * 11,
3 * 5 * 7,
3 * 5 * 7 * 11 * 13,
3 * 5 * 7 * 13,
3 * 5 * 7 * 11 * 17,
3 * 5 * 11,
3 * 5 * 7 * 17,
3 * 5,
3 * 5 * 7 * 11 * 19,
3 * 5 * 11 * 13,
3 * 5 * 7 * 19,
3 * 5 * 7 * 13 * 17,
3 * 5 * 13,
3 * 7 * 11,
3 * 7,
5 * 7 * 11,
3 * 7 * 13,
5 * 7,
3 * 5 * 17,
5 * 7 * 13,
3 * 5 * 19,
3 * 11,
3 * 7 * 17,
3,
3 * 11 * 13,
5 * 11,
3 * 7 * 19,
3 * 13,
5,
5 * 11 * 13,
5 * 7 * 19,
5 * 13,
7 * 11,
7,
3 * 17,
7 * 13,
11,
1,
];
pub fn one_line<T: Integer + NumRef + FromPrimitive + ExactRoots + CheckedAdd + CheckedMul>(
target: &T,
mul_target: T,
max_iter: usize,
) -> (Option<T>, usize)
where
for<'r> &'r T: RefNum<T>,
{
assert!(
&mul_target.is_multiple_of(target),
"mul_target should be multiples of target"
);
let mut ikn = mul_target.clone();
for i in 1..max_iter {
let s = ikn.sqrt() + T::one();
let s_squared = if let Some(result) = s.checked_mul(&s) {
result
} else {
return (None, i);
};
let m = s_squared - &ikn;
if let Some(t) = m.sqrt_exact() {
let g = target.gcd(&(s - t));
if !g.is_one() && &g != target {
return (Some(g), i);
}
}
ikn = if let Some(n) = ikn.checked_add(&mul_target) {
n
} else {
return (None, i);
}
}
(None, max_iter)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mint::SmallMint;
use num_modular::MontgomeryInt;
use rand::random;
#[test]
fn pollard_rho_test() {
assert_eq!(pollard_rho(&8051u16, 2, 1, 100).0, Some(97));
assert!(matches!(pollard_rho(&8051u16, random(), 1, 100).0, Some(i) if i == 97 || i == 83));
assert_eq!(pollard_rho(&455_459_u32, 2, 1, 100).0, Some(743));
for _ in 0..10 {
let target = random::<u16>() | 1;
let start = random::<u16>() % target;
let offset = random::<u16>() % target;
let expect = pollard_rho(&target, start, offset, 65536);
let mint_result = pollard_rho(
&SmallMint::from(target),
MontgomeryInt::new(start, &target).into(),
MontgomeryInt::new(offset, &target).into(),
65536,
);
assert_eq!(expect.0, mint_result.0.map(|v| v.value()));
}
}
#[test]
fn squfof_test() {
assert_eq!(squfof(&11111u32, 11111u32, 100).0, Some(41));
let cases: Vec<u64> = vec![
2501,
12851,
13289,
75301,
120_787,
967_009,
997_417,
7_091_569,
5_214_317,
20_834_839,
23_515_517,
33_409_583,
44_524_219,
13_290_059,
223_553_581,
2_027_651_281,
11_111_111_111,
100_895_598_169,
60_012_462_237_239,
287_129_523_414_791,
9_007_199_254_740_931,
11_111_111_111_111_111,
314_159_265_358_979_323,
384_307_168_202_281_507,
419_244_183_493_398_773,
658_812_288_346_769_681,
922_337_203_685_477_563,
1_000_000_000_000_000_127,
1_152_921_505_680_588_799,
1_537_228_672_809_128_917,
4_558_849,
];
for n in cases {
let d = squfof(&n, n, 40000)
.0
.or(squfof(&n, 3 * n, 40000).0)
.or(squfof(&n, 5 * n, 40000).0)
.or(squfof(&n, 7 * n, 40000).0)
.or(squfof(&n, 11 * n, 40000).0);
assert!(d.is_some(), "{}", n);
}
}
#[test]
fn one_line_test() {
assert_eq!(one_line(&11111u32, 11111u32, 100).0, Some(271));
}
#[test]
fn one_line_overflow() {
let n = u64::MAX / 4 + 1; let result = one_line(&n, n, 1000);
assert!(result.0.is_none());
}
#[test]
fn one_line_with_multiplier() {
let n = 11111u32;
let result = one_line(&n, n * 480, 100);
assert!(result.0.is_some());
let f = result.0.unwrap();
assert!(n % f == 0 && f > 1 && f < n);
}
#[test]
fn trial_division_no_limit() {
let primes: Vec<u64> = vec![2, 3, 5, 7, 11, 13];
let (factors, residual) = trial_division(primes.into_iter(), 2 * 3 * 5 * 7u64, None);
assert!(residual.is_ok());
assert_eq!(residual.unwrap(), 1);
assert_eq!(factors[&2], 1);
assert_eq!(factors[&3], 1);
assert_eq!(factors[&5], 1);
assert_eq!(factors[&7], 1);
}
#[test]
fn trial_division_residual_one() {
let primes: Vec<u64> = vec![2, 3, 5];
let (factors, residual) = trial_division(primes.into_iter(), 60u64, Some(100));
assert!(residual.is_ok());
assert_eq!(residual.unwrap(), 1);
assert_eq!(factors[&2], 2);
assert_eq!(factors[&3], 1);
assert_eq!(factors[&5], 1);
}
#[test]
fn trial_division_bound_exceeds_sqrt() {
let primes: Vec<u64> = vec![2, 3, 5, 7, 11, 13];
let (factors, residual) = trial_division(primes.into_iter(), 91u64, Some(100));
assert!(residual.is_ok());
assert_eq!(factors[&7], 1);
assert_eq!(residual.unwrap(), 13);
}
#[test]
fn trial_division_prime_target() {
let primes: Vec<u64> = vec![2, 3, 5, 7, 11];
let (factors, residual) = trial_division(primes.into_iter(), 97u64, Some(100));
assert!(residual.is_ok());
assert!(factors.is_empty());
assert_eq!(residual.unwrap(), 97);
}
#[test]
fn squfof_perfect_square() {
let result = squfof(&49u64, 49u64, 100);
assert_eq!(result.0, Some(7));
assert_eq!(result.1, 0); }
#[test]
fn squfof_perfect_square_large() {
let n = 10201u64; let result = squfof(&n, n, 100);
assert_eq!(result.0, Some(101));
assert_eq!(result.1, 0);
}
#[test]
fn pollard_rho_various_starts() {
let target = 8051u32;
for start in [1u32, 2, 3, 5, 7, 11, 13] {
for offset in [1u32, 2, 3, 5, 7] {
let (result, _) = pollard_rho(&target, start, offset, 10000);
if let Some(f) = result {
assert!(target % f == 0 && f > 1 && f < target);
}
}
}
}
#[test]
fn pollard_rho_loop_detection() {
let (result, _) = pollard_rho(&15u32, 0, 0, 100);
let _ = result;
}
#[test]
fn trial_division_limited() {
let primes: Vec<u64> = vec![2, 3, 5, 7, 11, 13];
let (factors, residual) = trial_division(primes.into_iter(), 1001u64, Some(10));
assert!(residual.is_err()); assert_eq!(factors[&7], 1);
assert_eq!(residual.unwrap_err(), 143);
}
}