use std::cmp::Ordering;
use std::hint::assert_unchecked;
use std::num::NonZeroU64;
use gcd::Gcd;
use crate::integer::factorization::prime::primes::SMALL_ODD_PRIMES;
use crate::integer::factorization::start;
use crate::Prime;
pub fn factorize<
const NR_SMALL_PRIMES: usize,
const TRIAL_DIVISION_LIMIT: u64,
const RHO_BASE_LIMIT: u64,
const KEEP_RESIDUAL: bool,
>(value: NonZeroU64) -> (Vec<(u64, u32)>, u64) {
let mut x = value.get();
let mut factors = Vec::with_capacity(15);
let two_powers = x.trailing_zeros();
if two_powers > 0 {
x >>= two_powers;
factors.push((2, two_powers));
}
'odd: {
for divisor in &SMALL_ODD_PRIMES[..NR_SMALL_PRIMES] {
let divisor = *divisor as u64;
unsafe { assert_unchecked(divisor != 0); }
let mut counter = 0;
while x.is_multiple_of(divisor) {
x /= divisor;
counter += 1;
}
if counter > 0 {
factors.push((divisor, counter));
}
if x == 1 {
break 'odd;
}
}
if TRIAL_DIVISION_LIMIT != 0 {
let start = start(NR_SMALL_PRIMES) as u64;
x = trial_division::<TRIAL_DIVISION_LIMIT>(x, start, &mut factors);
if x == 1 {
break 'odd;
}
}
if KEEP_RESIDUAL
&& x.is_prime() {
factors.push((x, 1));
x = 1;
break 'odd;
}
if RHO_BASE_LIMIT != 0 {
x = pollards_rho::<RHO_BASE_LIMIT>(x, &mut factors);
}
}
(factors, x)
}
fn trial_division<const END: u64>(mut x: u64, start: u64, factors: &mut Vec<(u64, u32)>) -> u64 {
let mut divisor = start;
let mut is_prime = x.is_prime();
while x > 1 && divisor < END && !is_prime {
debug_assert!(
divisor as u128 * divisor as u128 <= x as u128,
"a composite `x` always has a factor at or below its square root",
);
unsafe {
assert_unchecked(divisor != 0);
}
let mut counter = 0;
while x.is_multiple_of(divisor) {
x /= divisor;
counter += 1;
}
if counter > 0 {
factors.push((divisor, counter));
is_prime = x.is_prime();
}
divisor += 2;
}
x
}
fn pollards_rho<const BASE_LIMIT: u64>(x: u64, factors: &mut Vec<(u64, u32)>) -> u64 {
debug_assert_ne!(x, 0);
if x == 1 {
return 1;
}
let mut unsorted_factors = Vec::new();
let mut residual = 1;
rho_loop::<BASE_LIMIT>(x, &mut unsorted_factors, &mut residual);
unsorted_factors.sort_unstable();
let mut iter = unsorted_factors.into_iter();
if let Some(first) = iter.next() {
let mut factor = first;
let mut counter = 1;
for new_factor in iter {
if new_factor == factor {
counter += 1;
} else {
factors.push((factor, counter));
factor = new_factor;
counter = 1;
}
}
factors.push((factor, counter));
}
residual
}
fn rho_loop<const LIMIT: u64>(mut x: u64, factors: &mut Vec<u64>, residual: &mut u64) {
debug_assert_ne!(x, 0);
let mut e = 2;
while x > 1 {
if x.is_prime() {
factors.push(x);
return;
}
if e >= LIMIT {
*residual *= x;
return;
}
match rho(x, e) {
None | Some(1) => {
e += 1;
}
Some(factor) => {
if factor.is_prime() {
factors.push(factor);
} else {
rho_loop::<LIMIT>(factor, factors, residual);
}
x /= factor;
}
}
}
}
const MAX_STEPS: u64 = 1 << 24;
pub fn rho(value: u64, entropy: u64) -> Option<u64> {
debug_assert_ne!(value, 0);
debug_assert_ne!(value, 1);
debug_assert_ne!(value, 2);
let entropy = entropy.wrapping_mul(value);
let c = entropy & 0xff;
let u = (entropy & 0x7f).max(1);
let mut r: u64 = 1;
let mut q: u64 = 1;
let mut y: u64 = entropy & 0xf;
let mut factor = 1;
let mut y_old = 0;
let mut x = 0;
let value_wide = value as u128;
let f = |x: u64| (((x as u128) * (x as u128) + c as u128) % value_wide) as u64;
let mul_mod = |a: u64, b: u64| (((a as u128) * (b as u128)) % value_wide) as u64;
while factor == 1 {
x = y;
for _ in 0..r {
y = f(y);
}
let mut k = 0;
while k < r && factor == 1 {
y_old = y;
for _ in 0..u64::min(u, r - k) {
y = f(y);
if x > y {
q = mul_mod(q, x - y);
} else {
q = mul_mod(q, y - x);
}
}
factor = Gcd::gcd(q, value);
k += u;
}
if r > MAX_STEPS {
return None;
}
r *= 2;
}
let mut steps_left = r;
while factor == value || factor <= 1 {
if steps_left == 0 {
return None;
}
steps_left -= 1;
y_old = f(y_old);
match x.cmp(&y_old) {
Ordering::Less => factor = Gcd::gcd(y_old - x, value),
Ordering::Equal => {
return None;
}
Ordering::Greater => factor = Gcd::gcd(x - y_old, value),
}
}
Some(factor)
}
#[cfg(test)]
mod test {
use std::num::NonZeroU64;
use crate::integer::factorization::size_64::{factorize, rho};
use crate::Prime;
#[test]
fn test_composite() {
let composite = NonZeroU64::new(2_u64.pow(36) - 7).unwrap();
assert_eq!(
factorize::<256, 10_000, 20, true>(composite),
(vec![(3, 1), (22906492243, 1)], 1),
);
let composite = NonZeroU64::new(2_u64.pow(60) - 95).unwrap();
assert_eq!(
factorize::<256, 10_000, 20, true>(composite),
(vec![(3457, 1), (6203, 1), (53764867411, 1)], 1),
);
let composite = NonZeroU64::new(2_u64.pow(53) - 113).unwrap();
assert_eq!(
factorize::<256, 10_000, 20, true>(composite),
(vec![(3, 2), (43, 1), (642739, 1), (36211303, 1)], 1),
);
}
fn assert_describes(value: u64, factors: &[(u64, u32)], residual: u64) {
let mut total = residual;
for &(factor, power) in factors {
assert!(factor > 1, "{factor} is not a factor of {value}");
for _ in 0..power {
total = total.checked_mul(factor).expect("at most the original value");
}
}
assert_eq!(total, value, "factors {factors:?} and residual {residual} of {value}");
}
#[test]
fn test_residual_is_the_cofactor() {
let value = NonZeroU64::new(2_u64.pow(36) - 5).unwrap();
assert_eq!(factorize::<256, 0, 0, false>(value), (vec![], value.get()));
assert_eq!(factorize::<256, 0, 0, true>(value), (vec![(value.get(), 1)], 1));
let value = NonZeroU64::new(1_000_003 * 1_000_033).unwrap();
assert_eq!(factorize::<256, 0, 0, true>(value), (vec![], value.get()));
}
#[test]
fn test_pollards_rho() {
let values = [
1_000_003 * 1_000_003,
1_000_003 * 1_000_033,
2_u64.pow(53) - 113,
(2_u64.pow(31) - 1) * (2_u64.pow(31) - 19),
3 * 5 * 7 * (1_000_003 * 1_000_033),
];
for value in values {
let (factors, residual) = factorize::<256, 0, 100, true>(NonZeroU64::new(value).unwrap());
assert_describes(value, &factors, residual);
assert_eq!(residual, 1, "{value} was not factorized completely");
for &(factor, _) in &factors {
assert!(factor.is_prime(), "{factor} of {value} is not prime");
}
}
assert_eq!(
factorize::<256, 0, 100, true>(NonZeroU64::new(1_000_003 * 1_000_003).unwrap()),
(vec![(1_000_003, 2)], 1),
);
}
#[test]
fn test_pollards_rho_semiprimes() {
let mut state = 0x2545_f491_4f6c_dd1d_u64;
let mut next_prime_in = |below: u64| loop {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let candidate = (state % below) | 1;
if candidate > 1_000_000 && candidate.is_prime() {
break candidate;
}
};
for bound in [2_u64.pow(24), 2_u64.pow(28), 2_u64.pow(31)] {
for _ in 0..4 {
let left = next_prime_in(bound);
let right = next_prime_in(bound);
let value = left * right;
let (factors, residual) = factorize::<256, 0, 100, true>(NonZeroU64::new(value).unwrap());
assert_describes(value, &factors, residual);
assert_eq!(residual, 1, "{value} = {left} * {right} was not factorized completely");
let expected = if left == right {
vec![(left, 2)]
} else {
let (small, large) = (u64::min(left, right), u64::max(left, right));
vec![(small, 1), (large, 1)]
};
assert_eq!(factors, expected);
}
}
}
#[test]
fn test_rho_divisors() {
let values = [
1_000_003 * 1_000_003,
1_000_003 * 1_000_033,
23_274_416_678_917,
(2_u64.pow(31) - 1) * (2_u64.pow(31) - 19),
];
for value in values {
for entropy in 2..20 {
if let Some(divisor) = rho(value, entropy) {
assert!(divisor > 1 && divisor < value, "{divisor} is not a proper divisor of {value}");
assert_eq!(value % divisor, 0, "{divisor} does not divide {value}");
}
}
}
}
#[test]
fn test_rho_zero_step_size() {
let value: u64 = 23_274_416_678_917;
let entropy: u64 = 128;
debug_assert_eq!(entropy.wrapping_mul(value) & 0x7f_u64, 0, "this entropy has no step size");
if let Some(divisor) = rho(value, entropy) {
assert_eq!(value % divisor, 0);
}
}
}