1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-08-04 23:17:46 +00:00

factor: Refactor (eheh) around a Factors datatype

It is clearer to see what is going on, as opposed to passing around an
unmarked `Vec<u64>`, and there is a single place to add invariants checks.

This is also a more compact memory representation: each prime factor is
represented only once, with an additional byte for multiplicity.  The
performance impact is however not significant.
This commit is contained in:
nicoo 2020-05-24 11:12:39 +02:00
parent 272b66aac8
commit d9095a2539

View file

@ -23,9 +23,12 @@ use rand::distributions::{Distribution, Uniform};
use rand::rngs::SmallRng;
use rand::{thread_rng, SeedableRng};
use std::cmp::{max, min};
use std::collections::HashMap;
use std::fmt;
use std::io::{stdin, BufRead};
use std::mem::swap;
use std::num::Wrapping;
use std::ops;
mod numeric;
@ -36,14 +39,6 @@ static SUMMARY: &str = "Print the prime factors of the given number(s).
If none are specified, read from standard input.";
static LONG_HELP: &str = "";
fn rho_pollard_pseudorandom_function(x: u64, a: u64, b: u64, num: u64) -> u64 {
if num < 1 << 63 {
(sm_mul(a, sm_mul(x, x, num), num) + b) % num
} else {
big_add(big_mul(a, big_mul(x, x, num), num), b, num)
}
}
fn gcd(mut a: u64, mut b: u64) -> u64 {
while b > 0 {
a %= b;
@ -52,6 +47,58 @@ fn gcd(mut a: u64, mut b: u64) -> u64 {
a
}
struct Factors {
f: HashMap<u64, u8>,
}
impl Factors {
fn new() -> Factors {
Factors { f: HashMap::new() }
}
fn add(&mut self, prime: u64, exp: u8) {
assert!(exp > 0);
self.f.insert(prime, exp + self.f.get(&prime).unwrap_or(&0));
}
fn push(&mut self, prime: u64) {
self.add(prime, 1)
}
}
impl ops::MulAssign<Factors> for Factors {
fn mul_assign(&mut self, other: Factors) {
for (prime, exp) in &other.f {
self.add(*prime, *exp);
}
}
}
impl fmt::Display for Factors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Use a representation with efficient in-order iteration
let mut primes: Vec<&u64> = self.f.keys().collect();
primes.sort();
for p in primes {
for _ in 0..self.f[&p] {
write!(f, " {}", p)?
}
}
Ok(())
}
}
fn rho_pollard_pseudorandom_function(x: u64, a: u64, b: u64, num: u64) -> u64 {
if num < 1 << 63 {
(sm_mul(a, sm_mul(x, x, num), num) + b) % num
} else {
big_add(big_mul(a, big_mul(x, x, num), num), b, num)
}
}
fn rho_pollard_find_divisor(num: u64) -> u64 {
#![allow(clippy::many_single_char_names)]
let range = Uniform::new(1, num);
@ -78,31 +125,39 @@ fn rho_pollard_find_divisor(num: u64) -> u64 {
}
}
fn rho_pollard_factor(num: u64, factors: &mut Vec<u64>) {
fn rho_pollard_factor(num: u64, factors: &mut Factors) {
if is_prime(num) {
factors.push(num);
return;
}
let divisor = rho_pollard_find_divisor(num);
rho_pollard_factor(divisor, factors);
rho_pollard_factor(num / divisor, factors);
}
fn table_division(mut num: u64, factors: &mut Vec<u64>) {
fn table_division(mut num: u64) -> Factors {
let mut factors = Factors::new();
if num < 2 {
return;
factors.push(num);
return factors
}
while num % 2 == 0 {
num /= 2;
factors.push(2);
}
if num == 1 {
return;
return factors;
}
if is_prime(num) {
factors.push(num);
return;
return factors;
}
for &(prime, inv, ceil) in P_INVS_U64 {
if num == 1 {
break;
@ -120,7 +175,7 @@ fn table_division(mut num: u64, factors: &mut Vec<u64>) {
factors.push(prime);
if is_prime(num) {
factors.push(num);
return;
return factors;
}
} else {
break;
@ -136,21 +191,13 @@ fn table_division(mut num: u64, factors: &mut Vec<u64>) {
//trial_division_slow(num, factors);
//} else if num > 1 {
// number is still greater than 1, but not so big that we have to worry
rho_pollard_factor(num, factors);
rho_pollard_factor(num, &mut factors);
factors
//}
}
fn print_factors(num: u64) {
print!("{}:", num);
let mut factors = Vec::new();
// we always start with table division, and go from there
table_division(num, &mut factors);
factors.sort();
for fac in &factors {
print!(" {}", fac);
}
print!("{}:{}", num, table_division(num));
println!();
}