Skip to content

Commit

Permalink
Create a test framework for PrimeFactorization (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
hesampakdaman authored Apr 13, 2024
1 parent 3bbb35d commit bce31de
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 55 deletions.
33 changes: 16 additions & 17 deletions src/algorithms/fermats_factorization_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,29 +34,28 @@ fn is_perfect_square(n: &U512) -> bool {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::utils::check_factorization;

fn check(n: u32, factors: &[u32]) {
check_factorization::<FermatsFactorizationMethod>(n, factors);
}
use crate::test_framework::prime_factorization::CheckTestBuilder;

#[test]
fn composites() {
let test_cases = [
(5959, vec![59, 101]),
(12345, vec![3, 5, 823]),
(102030, vec![2, 3, 5, 19, 179]),
];
for (n, expected) in test_cases {
check(n, &expected);
}
CheckTestBuilder::new()
.case(5959, &[59, 101])
.case(12345, &[3, 5, 823])
.case(102030, &[2, 3, 5, 19, 179])
.build::<FermatsFactorizationMethod>()
.check_cases()
}

#[test]
fn primes() {
let primes = [409, 881, 1021, 4001, 5003, 9001];
for p in primes {
check(p, &[p]);
}
CheckTestBuilder::new()
.case(409, &[409])
.case(881, &[881])
.case(1021, &[1021])
.case(4001, &[4001])
.case(5003, &[5003])
.case(9001, &[9001])
.build::<FermatsFactorizationMethod>()
.check_cases()
}
}
31 changes: 20 additions & 11 deletions src/algorithms/pollards_rho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,34 @@ impl Factorize for PollardsRho {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::utils::check_factorization;
use crate::test_framework::prime_factorization::CheckTestBuilder;

fn check(n: u32, factors: &[u32]) {
check_factorization::<PollardsRho>(n, factors);
#[test]
fn default() {
CheckTestBuilder::default()
.build::<PollardsRho>()
.check_cases()
}

#[test]
fn composites() {
let test_cases = [(8051, vec![83, 97]), (15, vec![3, 5]), (4096, vec![2; 12])];
for (n, expected) in test_cases {
check(n, &expected);
}
CheckTestBuilder::new()
.case(8051, &[83, 97])
.case(15, &[3, 5])
.case(4096, &[2; 12])
.build::<PollardsRho>()
.check_cases()
}

#[test]
fn primes() {
let primes = [3, 5, 19, 29, 37];
for p in primes {
check(p, &[p])
}
CheckTestBuilder::new()
.case(3, &[3])
.case(5, &[5])
.case(19, &[19])
.case(29, &[29])
.case(37, &[37])
.build::<PollardsRho>()
.check_cases()
}
}
20 changes: 11 additions & 9 deletions src/algorithms/trial_division.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,21 @@ impl Iterator for DivisorCandidates {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::utils::check_factorization;

fn check(n: u32, factors: &[u32]) {
check_factorization::<TrialDivision>(n, factors);
}
use crate::test_framework::prime_factorization::CheckTestBuilder;

#[test]
fn factorize_prime() {
check(13, &[13]);
fn prime() {
CheckTestBuilder::new()
.case(13, &[13])
.build::<TrialDivision>()
.check_cases()
}

#[test]
fn factorize_composite() {
check(12, &[2, 2, 3]);
fn composite() {
CheckTestBuilder::new()
.case(12, &[2, 2, 3])
.build::<TrialDivision>()
.check_cases()
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ pub use traits::PrimeFactorization;

#[cfg(test)]
pub(crate) mod tests;

#[cfg(test)]
pub(crate) mod test_framework;
31 changes: 21 additions & 10 deletions src/orchestration/recursive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ enum DivisorOfN {
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::utils::check_factorization;
use crate::test_framework::prime_factorization::CheckTestBuilder;
use num_traits::Zero;

struct FakePrimeTester;
Expand Down Expand Up @@ -123,33 +123,44 @@ mod tests {

type MyTestOrchestrator = RecursivePrimeFactorization<FakeFactorizer, FakePrimeTester>;

fn check(n: u32, factors: &[u32]) {
check_factorization::<MyTestOrchestrator>(n, factors)
}

#[test]
fn single_prime() {
check(3, &[3])
CheckTestBuilder::new()
.case(3, &[3])
.build::<MyTestOrchestrator>()
.check_cases()
}

#[test]
fn composite_power_of_2() {
check(8, &[2; 3])
CheckTestBuilder::new()
.case(8, &[2; 3])
.build::<MyTestOrchestrator>()
.check_cases()
}

#[test]
fn odd_composite() {
check(15, &[3, 5])
CheckTestBuilder::new()
.case(15, &[3, 5])
.build::<MyTestOrchestrator>()
.check_cases()
}

#[test]
fn even_composite() {
check(30, &[2, 3, 5])
CheckTestBuilder::new()
.case(30, &[2, 3, 5])
.build::<MyTestOrchestrator>()
.check_cases()
}

#[test]
#[should_panic]
fn fails_to_find_factor() {
check(49, &[7, 7])
CheckTestBuilder::new()
.case(49, &[7; 2])
.build::<MyTestOrchestrator>()
.check_cases()
}
}
1 change: 1 addition & 0 deletions src/test_framework.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod prime_factorization;
4 changes: 4 additions & 0 deletions src/test_framework/prime_factorization.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod builder;
mod check_test;

pub use self::builder::CheckTestBuilder;
43 changes: 43 additions & 0 deletions src/test_framework/prime_factorization/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use super::check_test::CheckTest;
use crate::traits::PrimeFactorization;
use bnum::types::U512;

type Factors = Vec<U512>;
type TestCase = (U512, Factors);
pub struct CheckTestBuilder {
cases: Vec<TestCase>,
}

impl CheckTestBuilder {
pub fn new() -> Self {
Self { cases: Vec::new() }
}

pub fn case(mut self, n: u128, factors: &[u128]) -> Self {
let n_u512 = U512::from(n);
let factors_u512 = factors.iter().map(|&f| U512::from(f)).collect();
self.cases.push((n_u512, factors_u512));
self
}

pub fn build<F: PrimeFactorization>(self) -> CheckTest<F> {
CheckTest::<F>::new(self.cases)
}
}

impl Default for CheckTestBuilder {
fn default() -> Self {
Self::new()
.case(2, &[2])
.case(3, &[3])
.case(60, &[2, 2, 3, 5])
.case(101, &[101])
.case(121, &[11, 11])
.case(143, &[11, 13])
.case(221, &[13, 17])
.case(396, &[2, 2, 3, 3, 11])
.case(729, &[3; 6])
.case(510, &[2, 3, 5, 17])
.case(897, &[3, 13, 23])
}
}
31 changes: 31 additions & 0 deletions src/test_framework/prime_factorization/check_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::traits::PrimeFactorization;
use bnum::types::U512;
use std::marker::PhantomData;

type TestCase = (U512, Vec<U512>);

pub struct CheckTest<F: PrimeFactorization> {
cases: Vec<TestCase>,
_maker: PhantomData<F>,
}

impl<F: PrimeFactorization> CheckTest<F> {
pub fn new(cases: Vec<TestCase>) -> Self {
Self {
cases,
_maker: PhantomData,
}
}

pub fn check_cases(self) {
for (n, factors) in &self.cases {
Self::check(n, &factors);
}
}

pub fn check(n: &U512, expected: &[U512]) {
let mut actual = F::prime_factorization(&n);
actual.sort_unstable();
assert_eq!(actual, expected, "Test failed for n = {}", n);
}
}
9 changes: 1 addition & 8 deletions src/tests/utils.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
use crate::traits::{PrimalityTest, PrimeFactorization};
use crate::traits::PrimalityTest;
use bnum::types::U512;

pub(crate) fn check_factorization<F: PrimeFactorization>(n: u32, factors: &[u32]) {
let expected: Vec<U512> = 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_prime<P: PrimalityTest>(p: u32, expected: bool) {
assert_eq!(
P::is_prime(&U512::from(p)),
Expand Down

0 comments on commit bce31de

Please sign in to comment.