diff --git a/Cargo.lock b/Cargo.lock index 084334b..f924fec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "bnum" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" +dependencies = [ + "num-integer", + "num-traits", + "rand", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -31,18 +42,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", - "rand", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -101,7 +100,7 @@ dependencies = [ name = "rustic_factors" version = "0.1.0" dependencies = [ - "num-bigint", + "bnum", "num-integer", "num-traits", "rand", diff --git a/Cargo.toml b/Cargo.toml index 567d095..c0a4f38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -num-bigint = { version = "0.4.4", features = ["rand"] } +bnum = { version = "0.11.0", features = ["numtraits", "rand"] } num-integer = "0.1.46" num-traits = "0.2.18" rand = "0.8.5" diff --git a/src/algorithms/pollards_rho.rs b/src/algorithms/pollards_rho.rs index 6371d98..ebed269 100644 --- a/src/algorithms/pollards_rho.rs +++ b/src/algorithms/pollards_rho.rs @@ -4,23 +4,23 @@ use crate::orchestration; use crate::primality_test::MillerRabin; use crate::traits::Factorize; use crate::PrimeFactorization; -use num_bigint::BigInt; +use bnum::types::U512; use num_integer::Integer; pub struct PollardsRho; impl Factorize for PollardsRho { - fn factorize(n: &BigInt) -> BigInt { - let init = BigInt::from(2); - let psudorandom_fn = utils::generate_pseudorandom_fn(n); - let finished = move |x: &BigInt, y: &BigInt| (x - y).gcd(n) != BigInt::from(1); - let (tortoise, hare) = utils::floyds_cycle_detection(init, &psudorandom_fn, &finished); - (hare - tortoise).gcd(n) + fn factorize(n: &U512) -> U512 { + let init = U512::from(2u8); + let pseudorandom_fn = utils::generate_pseudorandom_fn(&n); + let finished = move |x: &U512, y: &U512| x.abs_diff(*y).gcd(&n) != U512::from(1u8); + let (tortoise, hare) = utils::floyds_cycle_detection(init, &pseudorandom_fn, &finished); + hare.abs_diff(tortoise).gcd(&n) } } impl PrimeFactorization for PollardsRho { - fn prime_factorization(n: &BigInt) -> Vec { + fn prime_factorization(n: &U512) -> Vec { orchestration::FactorizeRecursiveWith::::prime_factorization(n) } } diff --git a/src/algorithms/pollards_rho/utils.rs b/src/algorithms/pollards_rho/utils.rs index d1df971..c7b7cc0 100644 --- a/src/algorithms/pollards_rho/utils.rs +++ b/src/algorithms/pollards_rho/utils.rs @@ -1,10 +1,10 @@ -use num_bigint::{BigInt, RandBigInt}; -use num_traits::One; +use bnum::types::U512; +use rand::Rng; -pub fn floyds_cycle_detection(init: BigInt, next: &F, finished: &P) -> (BigInt, BigInt) +pub fn floyds_cycle_detection(init: U512, next: &F, finished: &P) -> (U512, U512) where - F: Fn(&BigInt) -> BigInt + ?Sized, - P: Fn(&BigInt, &BigInt) -> bool + ?Sized, + F: Fn(&U512) -> U512 + ?Sized, + P: Fn(&U512, &U512) -> bool + ?Sized, { let mut tortoise = init; let mut hare = next(&tortoise); @@ -15,11 +15,11 @@ where (tortoise, hare) } -pub fn generate_pseudorandom_fn(n: &'_ BigInt) -> impl Fn(&BigInt) -> BigInt + '_ { - let c = random_integer(&n); - move |x| (x.pow(2) + &c) % n +pub fn generate_pseudorandom_fn(n: &'_ U512) -> impl Fn(&U512) -> U512 + '_ { + let c = random_integer(n); + move |x| (x.pow(2) + c) % n } -fn random_integer(bound: &BigInt) -> BigInt { - rand::thread_rng().gen_bigint_range(&BigInt::one(), bound) +fn random_integer(bound: &U512) -> U512 { + rand::thread_rng().gen_range(U512::from(2u8)..*bound) } diff --git a/src/algorithms/trial_division.rs b/src/algorithms/trial_division.rs index 71249a0..1aa5ac1 100644 --- a/src/algorithms/trial_division.rs +++ b/src/algorithms/trial_division.rs @@ -1,20 +1,20 @@ use crate::PrimeFactorization; -use num_bigint::BigInt; +use bnum::types::U512; use num_integer::Integer; use num_traits::One; pub struct TrialDivision; impl PrimeFactorization for TrialDivision { - fn prime_factorization(n: &BigInt) -> Vec { - if n <= &BigInt::one() { + fn prime_factorization(n: &U512) -> Vec { + if n <= &U512::one() { return vec![n.clone()]; } trial_div(n.clone()) } } -fn trial_div(mut n: BigInt) -> Vec { +fn trial_div(mut n: U512) -> Vec { let mut factors = vec![]; let mut divisors = DivisorCandidates::new(); while let Some(d) = divisors.next() { @@ -32,31 +32,31 @@ fn trial_div(mut n: BigInt) -> Vec { factors } -fn is_still_undivided(n: &BigInt) -> bool { +fn is_still_undivided(n: &U512) -> bool { !n.is_one() } struct DivisorCandidates { - current: BigInt, + current: U512, } impl DivisorCandidates { fn new() -> Self { DivisorCandidates { - current: BigInt::from(2u8), + current: U512::from(2u8), } } } impl Iterator for DivisorCandidates { - type Item = BigInt; + type Item = U512; fn next(&mut self) -> Option { let output = self.current.clone(); - self.current = if self.current == BigInt::from(2u8) { - &self.current + 1u8 + self.current = if self.current == U512::from(2u8) { + &self.current + U512::from(1u8) } else { - &self.current + 2u8 + &self.current + U512::from(2u8) }; Some(output) } diff --git a/src/factorization.rs b/src/factorization.rs index 2d7d933..4ff1718 100644 --- a/src/factorization.rs +++ b/src/factorization.rs @@ -1,4 +1,4 @@ -use num_bigint::BigInt; +use bnum::types::U512; use std::collections::BTreeMap; use std::fmt; @@ -7,12 +7,12 @@ use crate::PrimeFactorization; static SUPERSCRIPTS: [&str; 10] = ["⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"]; pub struct Factorization<'a> { - number: &'a BigInt, - factors: Vec, + number: &'a U512, + factors: Vec, } impl<'a> Factorization<'a> { - pub fn new(n: &'a BigInt) -> Self { + pub fn new(n: &'a U512) -> Self { Factorization { number: n, factors: F::prime_factorization(n), @@ -29,7 +29,7 @@ impl<'a> Factorization<'a> { format!("{} = {}", self.number, display) } - fn frequencies(&self) -> BTreeMap<&BigInt, u128> { + fn frequencies(&self) -> BTreeMap<&U512, u128> { self.factors.iter().fold(BTreeMap::new(), |mut bmap, n| { *bmap.entry(n).or_insert(0) += 1; bmap @@ -43,7 +43,7 @@ impl fmt::Display for Factorization<'_> { } } -fn format_factor(base: &BigInt, exp: u128) -> String { +fn format_factor(base: &U512, exp: u128) -> String { fn format_exp(exp: u128) -> String { if exp <= 1 { return "".to_string(); @@ -63,17 +63,17 @@ mod tests { struct FakePrimeFactorizer; impl PrimeFactorization for FakePrimeFactorizer { - fn prime_factorization(n: &BigInt) -> Vec { - if n == &BigInt::from(36) { - return vec![2, 2, 3, 3].into_iter().map(BigInt::from).collect(); + fn prime_factorization(n: &U512) -> Vec { + if n == &U512::from(36u8) { + return vec![2u8, 2, 3, 3].into_iter().map(U512::from).collect(); } else { - return vec![2; 12].into_iter().map(BigInt::from).collect(); + return vec![2u8; 12].into_iter().map(U512::from).collect(); } } } fn check(n: u32, expected: &str) { - let n = BigInt::from(n); + let n = U512::from(n); let actual = Factorization::new::(&n); assert_eq!(format!("{actual}"), expected); } diff --git a/src/main.rs b/src/main.rs index 4c6945b..9b9d5da 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,6 @@ -use num_bigint::BigInt; +use bnum::types::U512; use rustic_factors::algorithms; use rustic_factors::Factorization; -use rustic_factors::primality_test::MillerRabin; -use rustic_factors::traits::PrimalityTest; use std::env; fn main() { @@ -17,11 +15,10 @@ fn run(args: Vec) -> Result<(), String> { return Err(format!("Usage: {} ", args[0])); } let method = &args[1]; - let n: BigInt = args[2] + let n: U512 = args[2] .parse() .map_err(|_| String::from("Please provide a valid positive integer"))?; match method.as_str() { - "miller_rabin" => println!("{}", MillerRabin::is_prime(&n)), "pollards_rho" => println!("{}", Factorization::new::(&n)), "trial_division" => println!("{}", Factorization::new::(&n)), _ => { diff --git a/src/orchestration/recursive.rs b/src/orchestration/recursive.rs index 7b25e8c..b2b9c35 100644 --- a/src/orchestration/recursive.rs +++ b/src/orchestration/recursive.rs @@ -1,5 +1,5 @@ use crate::traits::{Factorize, PrimalityTest, PrimeFactorization}; -use num_bigint::BigInt; +use bnum::types::U512; use num_integer::Integer; use num_traits::One; use std::marker::PhantomData; @@ -19,7 +19,7 @@ where Factorizer: Factorize, PrimeTester: PrimalityTest, { - fn prime_factorization(n: &BigInt) -> Vec { + fn prime_factorization(n: &U512) -> Vec { let max_successive_failures = 5; Self::new(max_successive_failures).recursive_factorization(n.clone()) } @@ -38,9 +38,9 @@ where } } - fn recursive_factorization(&self, mut n: BigInt) -> Vec { + fn recursive_factorization(&self, mut n: U512) -> Vec { let mut factors = vec![]; - let two = BigInt::from(2); + let two = U512::from(2u8); while n.is_even() { factors.push(two.clone()); n /= &two; @@ -49,14 +49,14 @@ where factors } - fn recursion_step(&self, n: BigInt, factors: &mut Vec, retried: usize) { + fn recursion_step(&self, n: U512, factors: &mut Vec, retried: usize) { if retried == self.max_successive_fails { panic![ "Failed to find factor after {0} succesive attempts", self.max_successive_fails ] } - if n <= BigInt::one() { + if n <= U512::one() { return; } match self.classify_factor(Factorizer::factorize(&n), &n) { @@ -72,7 +72,7 @@ where } } - fn classify_factor(&self, factor: BigInt, n: &BigInt) -> DivisorOfN { + fn classify_factor(&self, factor: U512, n: &U512) -> DivisorOfN { if PrimeTester::is_prime(&factor) { return DivisorOfN::Prime(factor); } @@ -84,9 +84,9 @@ where } enum DivisorOfN { - Prime(BigInt), - Composite(BigInt), - Trivial(BigInt), + Prime(U512), + Composite(U512), + Trivial(U512), } #[cfg(test)] @@ -98,23 +98,23 @@ mod tests { struct FakePrimeTester; impl PrimalityTest for FakePrimeTester { - fn is_prime(n: &BigInt) -> bool { - [2, 3, 5].contains(&n.try_into().unwrap()) + fn is_prime(n: &U512) -> bool { + [2, 3, 5].contains(&n.to_str_radix(10).parse().unwrap()) } } struct FakeFactorizer; impl Factorize for FakeFactorizer { - fn factorize(n: &BigInt) -> BigInt { + fn factorize(n: &U512) -> U512 { if n.is_even() { - return 2.into(); + return U512::from(2u8); } - if n % 3 == BigInt::zero() { - return 3.into(); + if n % U512::from(3u8) == U512::zero() { + return U512::from(3u8); } - if n % 5 == BigInt::zero() { - return 5.into(); + if n % U512::from(5u8) == U512::zero() { + return U512::from(5u8); } n.to_owned() } diff --git a/src/primality_test/miller_rabin.rs b/src/primality_test/miller_rabin.rs index 0ca1e90..59c2eed 100644 --- a/src/primality_test/miller_rabin.rs +++ b/src/primality_test/miller_rabin.rs @@ -3,15 +3,15 @@ mod utils; use self::composite_evidence::CompositeEvidence; use crate::traits::PrimalityTest; -use num_bigint::BigInt; +use bnum::types::U512; use num_integer::Integer; pub struct MillerRabin; impl PrimalityTest for MillerRabin { - fn is_prime(p: &BigInt) -> bool { - let two: BigInt = 2.into(); - if p == &two || p == &BigInt::from(3) { + fn is_prime(p: &U512) -> bool { + let two = U512::from(2u8); + if p == &two || p == &U512::from(3u8) { return true; } if p < &two || p.is_multiple_of(&two) { @@ -21,10 +21,10 @@ impl PrimalityTest for MillerRabin { } } -fn miller_rabin(p: &BigInt, trials: usize) -> bool { +fn miller_rabin(p: &U512, trials: usize) -> bool { let evidence = CompositeEvidence::new(p); let likely_prime = |witness| !evidence.witnessed_by(&witness); - utils::RandomIntegers::new(BigInt::from(2)..p - 1) + utils::RandomIntegers::new(U512::from(2u8)..p - U512::from(1u8)) .take(trials) .all(likely_prime) } @@ -40,15 +40,7 @@ mod tests { #[test] fn test_prime_numbers() { - let primes = [ - 3, - 5, - 7, - 11, - 104729, - 6700417, - 2u32.pow(31) - 1, - ]; + let primes = [3, 5, 7, 11, 104729, 6700417, 2u32.pow(31) - 1]; for prime in primes { check(prime, true); } diff --git a/src/primality_test/miller_rabin/composite_evidence.rs b/src/primality_test/miller_rabin/composite_evidence.rs index 1883816..8b98611 100644 --- a/src/primality_test/miller_rabin/composite_evidence.rs +++ b/src/primality_test/miller_rabin/composite_evidence.rs @@ -1,64 +1,81 @@ use super::utils; -use num_bigint::BigInt; +use bnum::types::U512; +use num_integer::Integer; use num_traits::One; pub struct CompositeEvidence<'a> { - n: &'a BigInt, + n: &'a U512, n_minus_1: Decomposed, } impl<'a> CompositeEvidence<'a> { - pub fn new(n: &'a BigInt) -> Self { - let n_minus_1 = Decomposed::new(n - 1u8); + pub fn new(n: &'a U512) -> Self { + let n_minus_1 = Decomposed::new(n - U512::from(1u8)); Self { n, n_minus_1 } } - pub fn witnessed_by(&self, witness: &BigInt) -> bool { + pub fn witnessed_by(&self, witness: &U512) -> bool { match self.raise_to_n_minus_1_mod_n(witness) { Ok(result) => fails_fermats_condition(result), Err(FoundNonTrivialSqrtOf1) => true, } } - fn raise_to_n_minus_1_mod_n(&self, base: &BigInt) -> ExponentiationResult { + fn raise_to_n_minus_1_mod_n(&self, base: &U512) -> ExponentiationResult { let odd_factor_in_exp = &self.n_minus_1.odd_factor; - let mut result = base.modpow(odd_factor_in_exp, &self.n); + let mut result = modpow(base, odd_factor_in_exp, &self.n); for _ in 0..self.n_minus_1.exponent_of_2 { if self.is_nontrivial_sqrt_of_1(&result) { return Err(FoundNonTrivialSqrtOf1); } - result = result.modpow(&BigInt::from(2u8), &self.n); + result = modpow(&result, &U512::from(2u8), &self.n); } Ok(RaisedToNMinus1ModN(result)) } - pub fn is_nontrivial_sqrt_of_1(&self, solution: &BigInt) -> bool { - let squared = solution.modpow(&BigInt::from(2u8), &self.n); - squared == BigInt::one() && solution != &BigInt::one() && solution != &(self.n - 1u8) + pub fn is_nontrivial_sqrt_of_1(&self, solution: &U512) -> bool { + let squared = modpow(solution, &U512::from(2u8), &self.n); + squared == U512::one() + && solution != &U512::one() + && solution != &(self.n - U512::from(1u8)) } } +fn modpow(base: &U512, exponent: &U512, modulus: &U512) -> U512 { + let mut result = U512::from(1u8); + let mut base = base % modulus; + let mut exp = exponent.clone(); + while exp > U512::from(0u8) { + if exp.is_odd() { + result = result * base % modulus; + } + base = base * base % modulus; + exp /= U512::from(2u8); + } + result +} + fn fails_fermats_condition(r: RaisedToNMinus1ModN) -> bool { !r.0.is_one() } type ExponentiationResult = Result; -struct RaisedToNMinus1ModN(BigInt); +struct RaisedToNMinus1ModN(U512); struct FoundNonTrivialSqrtOf1; struct Decomposed { exponent_of_2: u32, - odd_factor: BigInt, + odd_factor: U512, } impl Decomposed { /// Decomposes `number` into `exponent_of_2` and `odd_factor`, /// where `number = 2^exponent_of_2 * odd_factor`. - pub fn new(number: BigInt) -> Self { + pub fn new(number: U512) -> Self { let exponent_of_2 = utils::highest_power_of_2_divisor(&number); - let odd_factor = number / BigInt::from(2u8).pow(exponent_of_2); + let odd_factor = number / U512::from(2u8).pow(exponent_of_2); Self { exponent_of_2, odd_factor, diff --git a/src/primality_test/miller_rabin/utils.rs b/src/primality_test/miller_rabin/utils.rs index b200ccf..d0aa988 100644 --- a/src/primality_test/miller_rabin/utils.rs +++ b/src/primality_test/miller_rabin/utils.rs @@ -1,35 +1,32 @@ -use num_bigint::{BigInt, RandBigInt}; +use bnum::types::U512; use num_integer::Integer; +use rand::Rng; use std::ops::Range; pub struct RandomIntegers { - lo: BigInt, - hi: BigInt, + range: Range, } impl RandomIntegers { - pub fn new(range: Range) -> Self { - Self { - lo: range.start, - hi: range.end, - } + pub fn new(range: Range) -> Self { + Self { range } } } impl Iterator for RandomIntegers { - type Item = BigInt; + type Item = U512; fn next(&mut self) -> Option { - Some(rand::thread_rng().gen_bigint_range(&self.lo, &self.hi)) + Some(rand::thread_rng().gen_range(self.range.clone())) } } -pub fn highest_power_of_2_divisor(base: &BigInt) -> u32 { +pub fn highest_power_of_2_divisor(base: &U512) -> u32 { let mut exp = 0; let mut base = base.clone(); while base.is_even() { exp += 1; - base /= 2u8; + base /= U512::from(2u8); } exp } diff --git a/src/tests/utils.rs b/src/tests/utils.rs index 49d4ecb..d9cb231 100644 --- a/src/tests/utils.rs +++ b/src/tests/utils.rs @@ -1,21 +1,21 @@ #![allow(dead_code)] use crate::traits::{Factorize, PrimalityTest, PrimeFactorization}; -use num_bigint::BigInt; +use bnum::types::U512; pub(crate) fn check_factorization(n: u32, factors: &[u32]) { - let expected: Vec = factors.iter().map(|&d| BigInt::from(d)).collect(); - let mut actual = F::prime_factorization(&BigInt::from(n)); + let expected: Vec = factors.iter().map(|&d| U512::from(d)).collect(); + let mut actual = F::prime_factorization(&U512::from(n)); actual.sort_unstable(); assert_eq!(actual, expected, "Test failed for n = {}", n); } pub(crate) fn check_factor(n: u32, factor: u32) { - let expected = BigInt::from(factor); - let actual = F::factorize(&BigInt::from(n)); + let expected = U512::from(factor); + let actual = F::factorize(&U512::from(n)); assert_eq!(actual, expected, "Test failed for n = {}", n); } pub(crate) fn check_prime(p: u32, expected: bool) { - assert_eq!(P::is_prime(&BigInt::from(p)), expected); + assert_eq!(P::is_prime(&U512::from(p)), expected); } diff --git a/src/traits.rs b/src/traits.rs index 2a1950d..aacd624 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,13 +1,13 @@ -use num_bigint::BigInt; +use bnum::types::U512; pub trait PrimeFactorization { - fn prime_factorization(n: &BigInt) -> Vec; + fn prime_factorization(n: &U512) -> Vec; } pub trait Factorize { - fn factorize(n: &BigInt) -> BigInt; + fn factorize(n: &U512) -> U512; } pub trait PrimalityTest { - fn is_prime(p: &BigInt) -> bool; + fn is_prime(p: &U512) -> bool; }