mirror of
https://github.com/uutils/coreutils
synced 2024-11-15 09:27:21 +00:00
add test for factor
Add a test for `factor`. This commit also pulls factor's Sieve implementation into its own module so that the factor test can use it. Finally, slight refactoring for clarity in gen_table.rs.
This commit is contained in:
parent
cab4f8d570
commit
9a806346a9
5 changed files with 670 additions and 572 deletions
1
Makefile
1
Makefile
|
@ -161,6 +161,7 @@ TEST_PROGS := \
|
|||
cat \
|
||||
cp \
|
||||
env \
|
||||
factor \
|
||||
mkdir \
|
||||
mv \
|
||||
nl \
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
//! 2 has no multiplicative inverse mode 2^64 because 2 | 2^64,
|
||||
//! and in any case divisibility by two is trivial by checking the LSB.
|
||||
|
||||
use sieve::Sieve;
|
||||
use std::env::args;
|
||||
use std::iter::repeat;
|
||||
use std::num::Wrapping;
|
||||
use std::u64::MAX as MAX_U64;
|
||||
|
||||
|
@ -24,48 +24,7 @@ use numeric::is_prime;
|
|||
#[cfg(test)]
|
||||
mod numeric;
|
||||
|
||||
// A lazy Sieve of Eratosthenes
|
||||
// Not particularly efficient, but fine for generating a few thousand primes.
|
||||
struct Sieve {
|
||||
inner: Box<Iterator<Item=u64>>,
|
||||
filts: Vec<u64>,
|
||||
}
|
||||
|
||||
impl Iterator for Sieve {
|
||||
type Item = u64;
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.inner.size_hint()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<u64> {
|
||||
while let Some(n) = self.inner.next() {
|
||||
if self.filts.iter().all(|&x| n % x != 0) {
|
||||
self.filts.push(n);
|
||||
return Some(n);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Sieve {
|
||||
#[inline]
|
||||
pub fn new() -> Sieve {
|
||||
fn next(s: &mut u64, t: u64) -> Option<u64> {
|
||||
let ret = Some(*s);
|
||||
*s = *s + t;
|
||||
ret
|
||||
}
|
||||
let next = next;
|
||||
|
||||
let odds_by_3 = Box::new(repeat(2).scan(3, next)) as Box<Iterator<Item=u64>>;
|
||||
|
||||
Sieve { inner: odds_by_3, filts: Vec::new() }
|
||||
}
|
||||
}
|
||||
mod sieve;
|
||||
|
||||
// extended Euclid algorithm
|
||||
// precondition: a does not divide 2^64
|
||||
|
@ -105,16 +64,20 @@ fn inv_mod_u64(a: u64) -> Option<u64> {
|
|||
|
||||
#[cfg_attr(test, allow(dead_code))]
|
||||
fn main() {
|
||||
// By default, we print the multiplicative inverses mod 2^64 of the first 10k primes
|
||||
let n = args().skip(1).next().unwrap_or("10000".to_string()).parse::<usize>().ok().unwrap_or(10000);
|
||||
// By default, we print the multiplicative inverses mod 2^64 of the first 1k primes
|
||||
let n = args().skip(1).next().unwrap_or("1000".to_string()).parse::<usize>().ok().unwrap_or(1000);
|
||||
|
||||
print!("{}", PREAMBLE);
|
||||
let mut cols = 3;
|
||||
|
||||
let m = n;
|
||||
Sieve::new()
|
||||
.scan((0, 3), move |st, x| {
|
||||
let (count, mut cols) = *st;
|
||||
if count < m {
|
||||
// we want a total of n + 1 values
|
||||
let mut primes = Sieve::new().take(n + 1);
|
||||
|
||||
// in each iteration of the for loop, we use the value yielded
|
||||
// by the previous iteration. This leaves one value left at the
|
||||
// end, which we call NEXT_PRIME.
|
||||
let mut x = primes.next().unwrap();
|
||||
for next in primes {
|
||||
// format the table
|
||||
let outstr = format!("({}, {}, {}),", x, inv_mod_u64(x).unwrap(), MAX_U64 / x);
|
||||
if cols + outstr.len() > MAX_WIDTH {
|
||||
|
@ -125,18 +88,10 @@ fn main() {
|
|||
cols += 1 + outstr.len();
|
||||
}
|
||||
|
||||
*st = (count + 1, cols);
|
||||
Some(1)
|
||||
} else if count == m {
|
||||
// now we're done formatting the table, print NEXT_PRIME
|
||||
print!("\n];\n\npub const NEXT_PRIME: u64 = {};\n", x);
|
||||
|
||||
*st = (count + 1, cols);
|
||||
Some(1)
|
||||
} else {
|
||||
None
|
||||
x = next;
|
||||
}
|
||||
}).take(m + 1).count();
|
||||
|
||||
print!("\n];\n\npub const NEXT_PRIME: u64 = {};\n", x);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -150,7 +105,7 @@ fn test_generator_and_inverter() {
|
|||
}));
|
||||
}
|
||||
|
||||
const MAX_WIDTH: usize = 100;
|
||||
const MAX_WIDTH: usize = 102;
|
||||
const PREAMBLE: &'static str =
|
||||
r##"/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
|
|
53
src/factor/sieve.rs
Normal file
53
src/factor/sieve.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) kwantam <kwantam@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use std::iter::repeat;
|
||||
|
||||
// A lazy Sieve of Eratosthenes
|
||||
// Not particularly efficient, but fine for generating a few thousand primes.
|
||||
pub struct Sieve {
|
||||
inner: Box<Iterator<Item=u64>>,
|
||||
filts: Vec<u64>,
|
||||
}
|
||||
|
||||
impl Iterator for Sieve {
|
||||
type Item = u64;
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.inner.size_hint()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<u64> {
|
||||
while let Some(n) = self.inner.next() {
|
||||
if self.filts.iter().all(|&x| n % x != 0) {
|
||||
self.filts.push(n);
|
||||
return Some(n);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Sieve {
|
||||
#[inline]
|
||||
pub fn new() -> Sieve {
|
||||
fn next(s: &mut u64, t: u64) -> Option<u64> {
|
||||
let ret = Some(*s);
|
||||
*s = *s + t;
|
||||
ret
|
||||
}
|
||||
let next = next;
|
||||
|
||||
let odds_by_3 = Box::new(repeat(2).scan(3, next)) as Box<Iterator<Item=u64>>;
|
||||
|
||||
Sieve { inner: odds_by_3, filts: Vec::new() }
|
||||
}
|
||||
}
|
89
test/factor.rs
Normal file
89
test/factor.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) kwantam <kwantam@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
extern crate libc;
|
||||
extern crate rand;
|
||||
|
||||
use rand::{weak_rng, Rng};
|
||||
use sieve::Sieve;
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
#[path="../src/factor/sieve.rs"]
|
||||
mod sieve;
|
||||
|
||||
const NUM_PRIMES: usize = 10000;
|
||||
const LOG_PRIMES: f64 = 14.0; // ceil(log2(NUM_PRIMES))
|
||||
|
||||
const NUM_TESTS: usize = 1000;
|
||||
const PROGNAME: &'static str = "./factor";
|
||||
|
||||
#[test]
|
||||
fn test_random() {
|
||||
|
||||
let mut primes = Sieve::new().take(NUM_PRIMES - 1).collect::<Vec<u64>>();
|
||||
primes.push(2);
|
||||
let primes = primes;
|
||||
|
||||
let mut rng = weak_rng();
|
||||
let mut rand_gt = move |min: u64| {
|
||||
let mut product = 1u64;
|
||||
let mut factors = Vec::new();
|
||||
while product < min {
|
||||
// log distribution---higher probability for lower numbers
|
||||
let mut factor;
|
||||
loop {
|
||||
let next = rng.gen_range(0f64, LOG_PRIMES).exp2().floor() as usize;
|
||||
if next < NUM_PRIMES {
|
||||
factor = primes[next];
|
||||
break;
|
||||
}
|
||||
}
|
||||
let factor = factor;
|
||||
|
||||
match product.checked_mul(factor) {
|
||||
Some(p) => {
|
||||
product = p;
|
||||
factors.push(factor);
|
||||
},
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
|
||||
factors.sort();
|
||||
(product, factors)
|
||||
};
|
||||
|
||||
// build an input and expected output string from factor
|
||||
let mut instring = String::new();
|
||||
let mut outstring = String::new();
|
||||
for _ in 0..NUM_TESTS {
|
||||
let (product, factors) = rand_gt(1 << 63);
|
||||
instring.push_str(&(format!("{} ", product))[..]);
|
||||
|
||||
outstring.push_str(&(format!("{}:", product))[..]);
|
||||
for factor in factors.iter() {
|
||||
outstring.push_str(&(format!(" {}", factor))[..]);
|
||||
}
|
||||
outstring.push_str("\n");
|
||||
}
|
||||
|
||||
// now run factor
|
||||
let mut process = Command::new(PROGNAME)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()
|
||||
.unwrap_or_else(|e| panic!("{}", e));
|
||||
|
||||
process.stdin.take().unwrap_or_else(|| panic!("Could not take child process stdin"))
|
||||
.write_all(instring.as_bytes()).unwrap_or_else(|e| panic!("{}", e));
|
||||
|
||||
let output = process.wait_with_output().unwrap_or_else(|e| panic!("{}", e));
|
||||
assert_eq!(&output.stdout[..], outstring.as_bytes());
|
||||
}
|
Loading…
Reference in a new issue