mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 12:07:46 +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 \
|
cat \
|
||||||
cp \
|
cp \
|
||||||
env \
|
env \
|
||||||
|
factor \
|
||||||
mkdir \
|
mkdir \
|
||||||
mv \
|
mv \
|
||||||
nl \
|
nl \
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
//! 2 has no multiplicative inverse mode 2^64 because 2 | 2^64,
|
//! 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.
|
//! and in any case divisibility by two is trivial by checking the LSB.
|
||||||
|
|
||||||
|
use sieve::Sieve;
|
||||||
use std::env::args;
|
use std::env::args;
|
||||||
use std::iter::repeat;
|
|
||||||
use std::num::Wrapping;
|
use std::num::Wrapping;
|
||||||
use std::u64::MAX as MAX_U64;
|
use std::u64::MAX as MAX_U64;
|
||||||
|
|
||||||
|
@ -24,48 +24,7 @@ use numeric::is_prime;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod numeric;
|
mod numeric;
|
||||||
|
|
||||||
// A lazy Sieve of Eratosthenes
|
mod sieve;
|
||||||
// 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() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// extended Euclid algorithm
|
// extended Euclid algorithm
|
||||||
// precondition: a does not divide 2^64
|
// precondition: a does not divide 2^64
|
||||||
|
@ -105,38 +64,34 @@ fn inv_mod_u64(a: u64) -> Option<u64> {
|
||||||
|
|
||||||
#[cfg_attr(test, allow(dead_code))]
|
#[cfg_attr(test, allow(dead_code))]
|
||||||
fn main() {
|
fn main() {
|
||||||
// By default, we print the multiplicative inverses mod 2^64 of the first 10k primes
|
// By default, we print the multiplicative inverses mod 2^64 of the first 1k primes
|
||||||
let n = args().skip(1).next().unwrap_or("10000".to_string()).parse::<usize>().ok().unwrap_or(10000);
|
let n = args().skip(1).next().unwrap_or("1000".to_string()).parse::<usize>().ok().unwrap_or(1000);
|
||||||
|
|
||||||
print!("{}", PREAMBLE);
|
print!("{}", PREAMBLE);
|
||||||
|
let mut cols = 3;
|
||||||
|
|
||||||
let m = n;
|
// we want a total of n + 1 values
|
||||||
Sieve::new()
|
let mut primes = Sieve::new().take(n + 1);
|
||||||
.scan((0, 3), move |st, x| {
|
|
||||||
let (count, mut cols) = *st;
|
|
||||||
if count < m {
|
|
||||||
// format the table
|
|
||||||
let outstr = format!("({}, {}, {}),", x, inv_mod_u64(x).unwrap(), MAX_U64 / x);
|
|
||||||
if cols + outstr.len() > MAX_WIDTH {
|
|
||||||
print!("\n {}", outstr);
|
|
||||||
cols = 4 + outstr.len();
|
|
||||||
} else {
|
|
||||||
print!(" {}", outstr);
|
|
||||||
cols += 1 + outstr.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
*st = (count + 1, cols);
|
// in each iteration of the for loop, we use the value yielded
|
||||||
Some(1)
|
// by the previous iteration. This leaves one value left at the
|
||||||
} else if count == m {
|
// end, which we call NEXT_PRIME.
|
||||||
// now we're done formatting the table, print NEXT_PRIME
|
let mut x = primes.next().unwrap();
|
||||||
print!("\n];\n\npub const NEXT_PRIME: u64 = {};\n", x);
|
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 {
|
||||||
|
print!("\n {}", outstr);
|
||||||
|
cols = 4 + outstr.len();
|
||||||
|
} else {
|
||||||
|
print!(" {}", outstr);
|
||||||
|
cols += 1 + outstr.len();
|
||||||
|
}
|
||||||
|
|
||||||
*st = (count + 1, cols);
|
x = next;
|
||||||
Some(1)
|
}
|
||||||
} else {
|
|
||||||
None
|
print!("\n];\n\npub const NEXT_PRIME: u64 = {};\n", x);
|
||||||
}
|
|
||||||
}).take(m + 1).count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -150,7 +105,7 @@ fn test_generator_and_inverter() {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_WIDTH: usize = 100;
|
const MAX_WIDTH: usize = 102;
|
||||||
const PREAMBLE: &'static str =
|
const PREAMBLE: &'static str =
|
||||||
r##"/*
|
r##"/*
|
||||||
* This file is part of the uutils coreutils package.
|
* This file is part of the uutils coreutils package.
|
||||||
|
|
File diff suppressed because it is too large
Load diff
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…
Add table
Add a link
Reference in a new issue