factor: Derecursify and refactor

~7% slowdown, paves the way for upcoming improvements
This commit is contained in:
nicoo 2020-07-29 20:27:54 +02:00 committed by Roy Ivy III
parent 8643489096
commit 3743a3e1e7
2 changed files with 55 additions and 39 deletions

View file

@ -33,7 +33,14 @@ impl Decomposition {
} }
} }
#[cfg(test)] fn is_one(&self) -> bool {
self.0.is_empty()
}
fn pop(&mut self) -> Option<(u64, Exponent)> {
self.0.pop()
}
fn product(&self) -> u64 { fn product(&self) -> u64 {
self.0 self.0
.iter() .iter()
@ -77,11 +84,11 @@ impl Factors {
self.0.borrow_mut().add(prime, exp) self.0.borrow_mut().add(prime, exp)
} }
#[cfg(test)]
pub fn push(&mut self, prime: u64) { pub fn push(&mut self, prime: u64) {
self.add(prime, 1) self.add(prime, 1)
} }
#[cfg(test)]
fn product(&self) -> u64 { fn product(&self) -> u64 {
self.0.borrow().product() self.0.borrow().product()
} }
@ -102,62 +109,70 @@ impl fmt::Display for Factors {
} }
} }
fn _factor<A: Arithmetic + miller_rabin::Basis>(num: u64, f: Factors) -> Factors { fn _find_factor<A: Arithmetic + miller_rabin::Basis>(num: u64) -> Option<u64> {
use miller_rabin::Result::*; use miller_rabin::Result::*;
// Shadow the name, so the recursion automatically goes from “Big” arithmetic to small.
let _factor = |n, f| {
if n < (1 << 32) {
_factor::<Montgomery<u32>>(n, f)
} else {
_factor::<A>(n, f)
}
};
if num == 1 {
return f;
}
let n = A::new(num); let n = A::new(num);
let divisor = match miller_rabin::test::<A>(n) { match miller_rabin::test::<A>(n) {
Prime => { Prime => None,
let mut r = f; Composite(d) => Some(d),
r.push(num); Pseudoprime => Some(rho::find_divisor::<A>(n)),
return r; }
}
Composite(d) => d,
Pseudoprime => rho::find_divisor::<A>(n),
};
let f = _factor(divisor, f);
_factor(num / divisor, f)
} }
pub fn factor(mut n: u64) -> Factors { fn find_factor(num: u64) -> Option<u64> {
if num < (1 << 32) {
_find_factor::<Montgomery<u32>>(num)
} else {
_find_factor::<Montgomery<u64>>(num)
}
}
pub fn factor(num: u64) -> Factors {
let mut factors = Factors::one(); let mut factors = Factors::one();
if n < 2 { if num < 2 {
return factors; return factors;
} }
let z = n.trailing_zeros(); let mut n = num;
let z = num.trailing_zeros();
if z > 0 { if z > 0 {
factors.add(2, z as Exponent); factors.add(2, z as Exponent);
n >>= z; n >>= z;
} }
debug_assert_eq!(num, n * factors.product());
if n == 1 { if n == 1 {
return factors; return factors;
} }
let (factors, n) = table::factor(n, factors); table::factor(&mut n, &mut factors);
debug_assert_eq!(num, n * factors.product());
if n < (1 << 32) { if n == 1 {
_factor::<Montgomery<u32>>(n, factors) return factors;
} else {
_factor::<Montgomery<u64>>(n, factors)
} }
let mut dec = Decomposition::one();
dec.add(n, 1);
while !dec.is_one() {
// Check correctness invariant
debug_assert_eq!(num, factors.product() * dec.product());
let (f, e) = dec.pop().unwrap();
if let Some(d) = find_factor(f) {
dec.add(f / d, e);
dec.add(d, e);
} else {
// f is prime
factors.add(f, e);
}
}
factors
} }
#[cfg(test)] #[cfg(test)]

View file

@ -14,7 +14,8 @@ use crate::Factors;
include!(concat!(env!("OUT_DIR"), "/prime_table.rs")); include!(concat!(env!("OUT_DIR"), "/prime_table.rs"));
pub(crate) fn factor(mut num: u64, mut factors: Factors) -> (Factors, u64) { pub(crate) fn factor(n: &mut u64, factors: &mut Factors) {
let mut num = *n;
for &(prime, inv, ceil) in P_INVS_U64 { for &(prime, inv, ceil) in P_INVS_U64 {
if num == 1 { if num == 1 {
break; break;
@ -42,5 +43,5 @@ pub(crate) fn factor(mut num: u64, mut factors: Factors) -> (Factors, u64) {
} }
} }
(factors, num) *n = num;
} }