Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

impl pollards_rho #1

Merged
merged 10 commits into from
Apr 2, 2024
3 changes: 3 additions & 0 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
pub mod pollards_rho;
pub mod trial_division;

pub use self::pollards_rho::PollardsRho;
pub use self::trial_division::TrialDivision;
108 changes: 108 additions & 0 deletions src/algorithms/pollards_rho.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
mod utils;

use crate::primality_test;
use crate::traits::PrimalityTest;
use crate::Factorize;

pub struct PollardsRho;
type PrimeTester = Box<dyn PrimalityTest>;

impl Factorize for PollardsRho {
fn factorize(&self, n: u128) -> Vec<u128> {
RecursivePollardsRho::new(n, Box::new(primality_test::MillerRabin))
.solve()
.factors
}
}

struct RecursivePollardsRho {
n: u128,
factors: Vec<u128>,
prime_tester: PrimeTester,
}

impl RecursivePollardsRho {
fn new(mut n: u128, prime_tester: PrimeTester) -> Self {
let mut factors = vec![];
while n % 2 == 0 {
factors.push(2);
n /= 2;
}
Self {
n,
factors,
prime_tester,
}
}

fn solve(mut self) -> Self {
self.recursively_factorize_n(self.n);
self
}

fn recursively_factorize_n(&mut self, n: u128) {
if n <= 1 {
return;
}
match self.get_divisor_with_pollards_rho(n) {
DivisorOfN::Trivial(_) => self.recursively_factorize_n(n),
DivisorOfN::Prime(p) => {
self.factors.push(p);
self.recursively_factorize_n(n / p)
}
DivisorOfN::Composite(d) => {
self.recursively_factorize_n(n / d);
self.recursively_factorize_n(d);
}
}
}

fn get_divisor_with_pollards_rho(&self, n: u128) -> DivisorOfN {
let d = pollards_rho(n);
if self.prime_tester.is_prime(d) {
return DivisorOfN::Prime(d);
}
if d == 1 || d == n {
return DivisorOfN::Trivial(d);
}
DivisorOfN::Composite(d)
}
}

fn pollards_rho(n: u128) -> u128 {
let init = 2;
let psudorandom_fn = utils::generate_psudeorandom_fn(n);
let finished = |x: u128, y: u128| utils::gcd(x.abs_diff(y), n) != 1;
let (tortoise, hare) = utils::floyds_cycle_detection(init, &psudorandom_fn, &finished);
utils::gcd(tortoise.abs_diff(hare), n)
}

enum DivisorOfN {
Prime(u128),
Composite(u128),
Trivial(u128),
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn composites() {
let test_cases = [(8051, vec![83, 97]), (15, vec![3, 5]), (4096, vec![2; 12])];
for (n, expected) in test_cases {
let mut actual = PollardsRho.factorize(n);
actual.sort_unstable();
assert_eq!(actual, expected);
}
}

#[test]
fn primes() {
let primes = [3, 5, 19, 29, 37, 7027, 13037];
for p in primes {
let actual = PollardsRho.factorize(p);
assert_eq!(actual, vec![p]);
}
}
}
32 changes: 32 additions & 0 deletions src/algorithms/pollards_rho/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use rand::Rng;
use std::ops::Range;

pub fn floyds_cycle_detection<F, P>(init: u128, next: &F, finished: &P) -> (u128, u128)
where
F: Fn(u128) -> u128 + ?Sized,
P: Fn(u128, u128) -> bool + ?Sized,
{
let mut tortoise = init;
let mut hare = next(tortoise);
while !finished(tortoise, hare) {
tortoise = next(tortoise);
hare = next(next(hare));
}
(tortoise, hare)
}

pub fn gcd(a: u128, b: u128) -> u128 {
if b == 0 {
return a;
}
gcd(b, a % b)
}

pub fn generate_psudeorandom_fn(n: u128) -> impl Fn(u128) -> u128 {
let c = random_integer(1..n);
move |x| (x * x + c) % n
}

fn random_integer(r: Range<u128>) -> u128 {
rand::thread_rng().gen_range(r)
}
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ fn main() {
.parse()
.expect("Please provide a valid positive integer");
match method.as_str() {
"pollards_rho" => println!("{}", Factorization::new(n, algorithms::PollardsRho)),
"trial_division" => println!("{}", Factorization::new(n, algorithms::TrialDivision)),
_ => eprintln!("Unknown algorithm. Available options: trial_division"),
_ => eprintln!("Unknown algorithm. Available options: pollards_rho, trial_division"),
}
}
3 changes: 2 additions & 1 deletion src/primality_test.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod miller_rabin;
mod miller_rabin;
pub use miller_rabin::MillerRabin;
Loading