Merge pull request #6110 from cre4ture/feature/factor_fix_gnu_tests_with_num_prime

Feature/factor fix gnu tests with num prime
This commit is contained in:
Sylvestre Ledru 2024-04-21 12:49:51 +02:00 committed by GitHub
commit 0b78b77fb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 298 additions and 1690 deletions

101
Cargo.lock generated
View file

@ -8,6 +8,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "aho-corasick"
version = "1.0.4"
@ -162,6 +173,18 @@ version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "blake2b_simd"
version = "1.0.2"
@ -391,6 +414,7 @@ dependencies = [
"hex-literal",
"libc",
"nix",
"num-prime",
"once_cell",
"phf",
"phf_codegen",
@ -887,6 +911,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a889e633afd839fb5b04fe53adfd588cefe518e71ec8d3c929698c6daf2acd"
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures"
version = "0.3.28"
@ -1025,6 +1055,15 @@ dependencies = [
"crunchy",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.14.3"
@ -1241,6 +1280,15 @@ version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "lru"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a"
dependencies = [
"hashbrown 0.12.3",
]
[[package]]
name = "lscolors"
version = "0.16.0"
@ -1360,6 +1408,7 @@ dependencies = [
"autocfg",
"num-integer",
"num-traits",
"rand",
]
[[package]]
@ -1372,6 +1421,33 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-modular"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-prime"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f4e3bc495f6e95bc15a6c0c55ac00421504a5a43d09e3cc455d1fea7015581d"
dependencies = [
"bitvec",
"either",
"lru",
"num-bigint",
"num-integer",
"num-modular",
"num-traits",
"rand",
]
[[package]]
name = "num-traits"
version = "0.2.18"
@ -1431,7 +1507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
dependencies = [
"dlv-list",
"hashbrown",
"hashbrown 0.14.3",
]
[[package]]
@ -1628,6 +1704,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.8.5"
@ -2058,6 +2140,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tempfile"
version = "3.10.1"
@ -2478,6 +2566,8 @@ version = "0.0.26"
dependencies = [
"clap",
"coz",
"num-bigint",
"num-prime",
"num-traits",
"quickcheck",
"rand",
@ -3613,6 +3703,15 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]]
name = "xattr"
version = "1.3.1"

View file

@ -298,6 +298,7 @@ nix = { version = "0.28", default-features = false }
nom = "7.1.3"
notify = { version = "=6.0.1", features = ["macos_kqueue"] }
num-bigint = "0.4.4"
num-prime = "0.4.3"
num-traits = "0.2.18"
number_prefix = "0.4"
once_cell = "1.19.0"
@ -480,6 +481,7 @@ chrono = { workspace = true }
filetime = { workspace = true }
glob = { workspace = true }
libc = { workspace = true }
num-prime = { workspace = true }
pretty_assertions = "1"
rand = { workspace = true }
regex = { workspace = true }

View file

@ -101,6 +101,8 @@ skip = [
{ name = "terminal_size", version = "0.2.6" },
# filetime, parking_lot_core
{ name = "redox_syscall", version = "0.4.1" },
# num-prime, rust-ini
{ name = "hashbrown", version = "0.12.3" },
]
# spell-checker: enable

View file

@ -21,6 +21,8 @@ num-traits = { workspace = true }
rand = { workspace = true }
smallvec = { workspace = true }
uucore = { workspace = true }
num-bigint = { workspace = true }
num-prime = { workspace = true }
[dev-dependencies]
quickcheck = "1.0.3"
@ -30,4 +32,4 @@ name = "factor"
path = "src/main.rs"
[lib]
path = "src/cli.rs"
path = "src/factor.rs"

View file

@ -1,103 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//! Generate a table of the multiplicative inverses of `p_i` mod 2^64
//! for the first 1027 odd primes (all 13 bit and smaller primes).
//! You can supply a command line argument to override the default
//! value of 1027 for the number of entries in the table.
//!
//! 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.
#![cfg_attr(test, allow(dead_code))]
use std::env::{self, args};
use std::fs::File;
use std::io::Write;
use std::path::Path;
use self::sieve::Sieve;
#[cfg(test)]
use miller_rabin::is_prime;
#[path = "src/numeric/modular_inverse.rs"]
mod modular_inverse;
#[allow(unused_imports)] // imports there are used, but invisible from build.rs
#[path = "src/numeric/traits.rs"]
mod traits;
use modular_inverse::modular_inverse;
mod sieve;
#[cfg_attr(test, allow(dead_code))]
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let mut file = File::create(Path::new(&out_dir).join("prime_table.rs")).unwrap();
// By default, we print the multiplicative inverses mod 2^64 of the first 1k primes
const DEFAULT_SIZE: usize = 320;
let n = args()
.nth(1)
.and_then(|s| s.parse::<usize>().ok())
.unwrap_or(DEFAULT_SIZE);
write!(file, "{PREAMBLE}").unwrap();
let mut cols = 3;
// we want a total of n + 1 values
let mut primes = Sieve::odd_primes().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 output = format!("({}, {}, {}),", x, modular_inverse(x), std::u64::MAX / x);
if cols + output.len() > MAX_WIDTH {
write!(file, "\n {output}").unwrap();
cols = 4 + output.len();
} else {
write!(file, " {output}").unwrap();
cols += 1 + output.len();
}
x = next;
}
write!(
file,
"\n];\n\n#[allow(dead_code)]\npub const NEXT_PRIME: u64 = {x};\n"
)
.unwrap();
}
#[test]
fn test_generator_is_prime() {
assert_eq!(Sieve::odd_primes.take(10_000).all(is_prime));
}
#[test]
fn test_generator_10001() {
let prime_10001 = Sieve::primes().skip(10_000).next();
assert_eq!(prime_10001, Some(104_743));
}
const MAX_WIDTH: usize = 102;
const PREAMBLE: &str = r"/*
* This file is part of the uutils coreutils package.
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/
// *** NOTE: this file was automatically generated.
// Please do not edit by hand. Instead, modify and
// re-run src/factor/gen_tables.rs.
#[allow(clippy::unreadable_literal)]
pub const PRIME_INVERSIONS_U64: &[(u64, u64, u64)] = &[
";

View file

@ -1,215 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (ToDO) filts, minidx, minkey paridx
use std::iter::{Chain, Copied, Cycle};
use std::slice::Iter;
/// A lazy Sieve of Eratosthenes.
///
/// This is a reasonably efficient implementation based on
/// O'Neill, M. E. "[The Genuine Sieve of Eratosthenes.](http://dx.doi.org/10.1017%2FS0956796808007004)"
/// Journal of Functional Programming, Volume 19, Issue 1, 2009, pp. 95--106.
#[derive(Default)]
pub struct Sieve {
inner: Wheel,
filts: PrimeHeap,
}
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> {
for n in &mut self.inner {
let mut prime = true;
while let Some((next, inc)) = self.filts.peek() {
// need to keep checking the min element of the heap
// until we've found an element that's greater than n
if next > n {
break; // next heap element is bigger than n
}
if next == n {
// n == next, and is composite.
prime = false;
}
// Increment the element in the prime heap.
self.filts.replace((next + inc, inc));
}
if prime {
// this is a prime; add it to the heap
self.filts.insert(n);
return Some(n);
}
}
None
}
}
impl Sieve {
fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
#[inline]
pub fn primes() -> PrimeSieve {
INIT_PRIMES.iter().copied().chain(Self::new())
}
#[allow(dead_code)]
#[inline]
pub fn odd_primes() -> PrimeSieve {
INIT_PRIMES[1..].iter().copied().chain(Self::new())
}
}
pub type PrimeSieve = Chain<Copied<Iter<'static, u64>>, Sieve>;
/// An iterator that generates an infinite list of numbers that are
/// not divisible by any of 2, 3, 5, or 7.
struct Wheel {
next: u64,
increment: Cycle<Iter<'static, u64>>,
}
impl Iterator for Wheel {
type Item = u64;
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(1, None)
}
#[inline]
fn next(&mut self) -> Option<u64> {
let increment = self.increment.next().unwrap(); // infinite iterator, no check necessary
let ret = self.next;
self.next = ret + increment;
Some(ret)
}
}
impl Wheel {
#[inline]
fn new() -> Self {
Self {
next: 11u64,
increment: WHEEL_INCS.iter().cycle(),
}
}
}
impl Default for Wheel {
fn default() -> Self {
Self::new()
}
}
/// The increments of a wheel of circumference 210
/// (i.e., a wheel that skips all multiples of 2, 3, 5, 7)
const WHEEL_INCS: &[u64] = &[
2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6,
2, 6, 6, 4, 2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10,
];
const INIT_PRIMES: &[u64] = &[2, 3, 5, 7];
/// A min-heap of "infinite lists" of prime multiples, where a list is
/// represented as (head, increment).
#[derive(Debug, Default)]
struct PrimeHeap {
data: Vec<(u64, u64)>,
}
impl PrimeHeap {
fn peek(&self) -> Option<(u64, u64)> {
if let Some(&(x, y)) = self.data.first() {
Some((x, y))
} else {
None
}
}
fn insert(&mut self, next: u64) {
let mut idx = self.data.len();
let key = next * next;
let item = (key, next);
self.data.push(item);
loop {
// break if we've bubbled to the top
if idx == 0 {
break;
}
let paridx = (idx - 1) / 2;
let (k, _) = self.data[paridx];
if key < k {
// bubble up, found a smaller key
self.data.swap(idx, paridx);
idx = paridx;
} else {
// otherwise, parent is smaller, so we're done
break;
}
}
}
fn remove(&mut self) -> (u64, u64) {
let ret = self.data.swap_remove(0);
let mut idx = 0;
let len = self.data.len();
let (key, _) = self.data[0];
loop {
let child1 = 2 * idx + 1;
let child2 = 2 * idx + 2;
// no more children
if child1 >= len {
break;
}
// find lesser child
let (c1key, _) = self.data[child1];
let (minidx, minkey) = if child2 >= len {
(child1, c1key)
} else {
let (c2key, _) = self.data[child2];
if c1key < c2key {
(child1, c1key)
} else {
(child2, c2key)
}
};
if minkey < key {
self.data.swap(minidx, idx);
idx = minidx;
continue;
}
// smaller than both children, so done
break;
}
ret
}
/// More efficient than inserting and removing in two steps
/// because we save one traversal of the heap.
fn replace(&mut self, next: (u64, u64)) -> (u64, u64) {
self.data.push(next);
self.remove()
}
}

View file

@ -1,121 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use std::io::BufRead;
use std::io::{self, stdin, stdout, Write};
mod factor;
use clap::{crate_version, Arg, ArgAction, Command};
pub use factor::*;
use uucore::display::Quotable;
use uucore::error::{set_exit_code, FromIo, UResult};
use uucore::{format_usage, help_about, help_usage, show_error, show_warning};
mod miller_rabin;
pub mod numeric;
mod rho;
pub mod table;
const ABOUT: &str = help_about!("factor.md");
const USAGE: &str = help_usage!("factor.md");
mod options {
pub static EXPONENTS: &str = "exponents";
pub static HELP: &str = "help";
pub static NUMBER: &str = "NUMBER";
}
fn print_factors_str(
num_str: &str,
w: &mut io::BufWriter<impl io::Write>,
print_exponents: bool,
) -> io::Result<()> {
let x = match num_str.trim().parse::<u64>() {
Ok(x) => x,
Err(e) => {
// We return Ok() instead of Err(), because it's non-fatal and we should try the next
// number.
show_warning!("{}: {}", num_str.maybe_quote(), e);
set_exit_code(1);
return Ok(());
}
};
// If print_exponents is true, use the alternate format specifier {:#} from fmt to print the factors
// of x in the form of p^e.
if print_exponents {
writeln!(w, "{}:{:#}", x, factor(x))?;
} else {
writeln!(w, "{}:{}", x, factor(x))?;
}
w.flush()
}
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args)?;
// If matches find --exponents flag than variable print_exponents is true and p^e output format will be used.
let print_exponents = matches.get_flag(options::EXPONENTS);
let stdout = stdout();
// We use a smaller buffer here to pass a gnu test. 4KiB appears to be the default pipe size for bash.
let mut w = io::BufWriter::with_capacity(4 * 1024, stdout.lock());
if let Some(values) = matches.get_many::<String>(options::NUMBER) {
for number in values {
print_factors_str(number, &mut w, print_exponents)
.map_err_context(|| "write error".into())?;
}
} else {
let stdin = stdin();
let lines = stdin.lock().lines();
for line in lines {
match line {
Ok(line) => {
for number in line.split_whitespace() {
print_factors_str(number, &mut w, print_exponents)
.map_err_context(|| "write error".into())?;
}
}
Err(e) => {
set_exit_code(1);
show_error!("error reading input: {}", e);
return Ok(());
}
}
}
}
if let Err(e) = w.flush() {
show_error!("{}", e);
}
Ok(())
}
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(crate_version!())
.about(ABOUT)
.override_usage(format_usage(USAGE))
.infer_long_args(true)
.disable_help_flag(true)
.args_override_self(true)
.arg(Arg::new(options::NUMBER).action(ArgAction::Append))
.arg(
Arg::new(options::EXPONENTS)
.short('h')
.long(options::EXPONENTS)
.help("Print factors in the form p^e")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::HELP)
.long(options::HELP)
.help("Print help information.")
.action(ArgAction::Help),
)
}

View file

@ -3,302 +3,142 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
#![allow(clippy::items_after_test_module)]
use smallvec::SmallVec;
use std::cell::RefCell;
use std::fmt;
// spell-checker:ignore funcs
use crate::numeric::{Arithmetic, Montgomery};
use crate::{miller_rabin, rho, table};
use std::collections::BTreeMap;
use std::io::BufRead;
use std::io::{self, stdin, stdout, Write};
type Exponent = u8;
use clap::{crate_version, Arg, ArgAction, Command};
use num_bigint::BigUint;
use num_traits::FromPrimitive;
use uucore::display::Quotable;
use uucore::error::{set_exit_code, FromIo, UResult, USimpleError};
use uucore::{format_usage, help_about, help_usage, show_error, show_warning};
#[derive(Clone, Debug, Default)]
struct Decomposition(SmallVec<[(u64, Exponent); NUM_FACTORS_INLINE]>);
const ABOUT: &str = help_about!("factor.md");
const USAGE: &str = help_usage!("factor.md");
// spell-checker:ignore (names) ErdősKac * Erdős Kac
// The number of factors to inline directly into a `Decomposition` object.
// As a consequence of the ErdősKac theorem, the average number of prime factors
// of integers < 10²⁵ ≃ 2⁸³ is 4, so we can use a slightly higher value.
const NUM_FACTORS_INLINE: usize = 5;
impl Decomposition {
fn one() -> Self {
Self::default()
}
fn add(&mut self, factor: u64, exp: Exponent) {
debug_assert!(exp > 0);
if let Some((_, e)) = self.0.iter_mut().find(|(f, _)| *f == factor) {
*e += exp;
} else {
self.0.push((factor, exp));
}
}
#[cfg(test)]
fn product(&self) -> u64 {
self.0
.iter()
.fold(1, |acc, (p, exp)| acc * p.pow(*exp as u32))
}
fn get(&self, p: u64) -> Option<&(u64, u8)> {
self.0.iter().find(|(q, _)| *q == p)
}
mod options {
pub static EXPONENTS: &str = "exponents";
pub static HELP: &str = "help";
pub static NUMBER: &str = "NUMBER";
}
impl PartialEq for Decomposition {
fn eq(&self, other: &Self) -> bool {
for p in &self.0 {
if other.get(p.0) != Some(p) {
return false;
}
}
for p in &other.0 {
if self.get(p.0) != Some(p) {
return false;
}
}
true
}
}
impl Eq for Decomposition {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Factors(RefCell<Decomposition>);
impl Factors {
pub fn one() -> Self {
Self(RefCell::new(Decomposition::one()))
}
pub fn add(&mut self, prime: u64, exp: Exponent) {
debug_assert!(miller_rabin::is_prime(prime));
self.0.borrow_mut().add(prime, exp);
}
pub fn push(&mut self, prime: u64) {
self.add(prime, 1);
}
#[cfg(test)]
fn product(&self) -> u64 {
self.0.borrow().product()
}
}
impl fmt::Display for Factors {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let v = &mut (self.0).borrow_mut().0;
v.sort_unstable();
let include_exponents = f.alternate();
for (p, exp) in v {
if include_exponents && *exp > 1 {
write!(f, " {p}^{exp}")?;
} else {
for _ in 0..*exp {
write!(f, " {p}")?;
}
}
}
Ok(())
}
}
fn _factor<A: Arithmetic + miller_rabin::Basis>(num: u64, f: Factors) -> Factors {
use miller_rabin::Result::*;
// Shadow the name, so the recursion automatically goes from “Big” arithmetic to small.
let _factor = |n, f| {
if n < (1 << 32) {
_factor::<Montgomery<u32>>(n, f)
} else {
_factor::<A>(n, f)
}
fn print_factors_str(
num_str: &str,
w: &mut io::BufWriter<impl io::Write>,
print_exponents: bool,
) -> UResult<()> {
let rx = num_str.trim().parse::<num_bigint::BigUint>();
let Ok(x) = rx else {
// return Ok(). it's non-fatal and we should try the next number.
show_warning!("{}: {}", num_str.maybe_quote(), rx.unwrap_err());
set_exit_code(1);
return Ok(());
};
if num == 1 {
return f;
}
let n = A::new(num);
let divisor = match miller_rabin::test::<A>(n) {
Prime => {
#[cfg(feature = "coz")]
coz::progress!("factor found");
let mut r = f;
r.push(num);
return r;
}
Composite(d) => d,
Pseudoprime => rho::find_divisor::<A>(n),
};
let f = _factor(divisor, f);
_factor(num / divisor, f)
}
pub fn factor(mut n: u64) -> Factors {
#[cfg(feature = "coz")]
coz::begin!("factorization");
let mut factors = Factors::one();
if n < 2 {
return factors;
}
let n_zeros = n.trailing_zeros();
if n_zeros > 0 {
factors.add(2, n_zeros as Exponent);
n >>= n_zeros;
}
if n == 1 {
#[cfg(feature = "coz")]
coz::end!("factorization");
return factors;
}
table::factor(&mut n, &mut factors);
#[allow(clippy::let_and_return)]
let r = if n < (1 << 32) {
_factor::<Montgomery<u32>>(n, factors)
let (factorization, remaining) = if x > BigUint::from_u32(1).unwrap() {
num_prime::nt_funcs::factors(x.clone(), None)
} else {
_factor::<Montgomery<u64>>(n, factors)
(BTreeMap::new(), None)
};
#[cfg(feature = "coz")]
coz::end!("factorization");
if let Some(_remaining) = remaining {
return Err(USimpleError::new(
1,
"Factorization incomplete. Remainders exists.",
));
}
r
write_result(w, x, factorization, print_exponents).map_err_context(|| "write error".into())?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::{factor, Decomposition, Exponent, Factors};
use quickcheck::quickcheck;
use smallvec::smallvec;
use std::cell::RefCell;
#[test]
fn factor_2044854919485649() {
let f = Factors(RefCell::new(Decomposition(smallvec![
(503, 1),
(2423, 1),
(40961, 2)
])));
assert_eq!(factor(f.product()), f);
}
#[test]
fn factor_recombines_small() {
assert!((1..10_000)
.map(|i| 2 * i + 1)
.all(|i| factor(i).product() == i));
}
#[test]
fn factor_recombines_overflowing() {
assert!((0..250)
.map(|i| 2 * i + 2u64.pow(32) + 1)
.all(|i| factor(i).product() == i));
}
#[test]
fn factor_recombines_strong_pseudoprime() {
// This is a strong pseudoprime (wrt. miller_rabin::BASIS)
// and triggered a bug in rho::factor's code path handling
// miller_rabbin::Result::Composite
let pseudoprime = 17_179_869_183;
for _ in 0..20 {
// Repeat the test 20 times, as it only fails some fraction
// of the time.
assert!(factor(pseudoprime).product() == pseudoprime);
}
}
quickcheck! {
fn factor_recombines(i: u64) -> bool {
i == 0 || factor(i).product() == i
}
fn recombines_factors(f: Factors) -> () {
assert_eq!(factor(f.product()), f);
}
fn exponentiate_factors(f: Factors, e: Exponent) -> () {
if e == 0 { return; }
if let Some(fe) = f.product().checked_pow(e.into()) {
assert_eq!(factor(fe), f ^ e);
fn write_result(
w: &mut io::BufWriter<impl Write>,
x: BigUint,
factorization: BTreeMap<BigUint, usize>,
print_exponents: bool,
) -> io::Result<()> {
write!(w, "{x}:")?;
for (factor, n) in factorization {
if print_exponents {
if n > 1 {
write!(w, " {}^{}", factor, n)?;
} else {
write!(w, " {}", factor)?;
}
} else {
w.write_all(format!(" {}", factor).repeat(n).as_bytes())?;
}
}
writeln!(w)?;
w.flush()
}
#[cfg(test)]
use rand::{
distributions::{Distribution, Standard},
Rng,
};
#[cfg(test)]
impl Distribution<Factors> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Factors {
let mut f = Factors::one();
let mut g = 1u64;
let mut n = u64::MAX;
#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args)?;
// spell-checker:ignore (names) Adam Kalai * Kalai's
// Adam Kalai's algorithm for generating uniformly-distributed
// integers and their factorization.
//
// See Generating Random Factored Numbers, Easily, J. Cryptology (2003)
'attempt: loop {
while n > 1 {
n = rng.gen_range(1..n);
if miller_rabin::is_prime(n) {
if let Some(h) = g.checked_mul(n) {
f.push(n);
g = h;
} else {
// We are overflowing u64, retry
continue 'attempt;
// If matches find --exponents flag than variable print_exponents is true and p^e output format will be used.
let print_exponents = matches.get_flag(options::EXPONENTS);
let stdout = stdout();
// We use a smaller buffer here to pass a gnu test. 4KiB appears to be the default pipe size for bash.
let mut w = io::BufWriter::with_capacity(4 * 1024, stdout.lock());
if let Some(values) = matches.get_many::<String>(options::NUMBER) {
for number in values {
print_factors_str(number, &mut w, print_exponents)?;
}
} else {
let stdin = stdin();
let lines = stdin.lock().lines();
for line in lines {
match line {
Ok(line) => {
for number in line.split_whitespace() {
print_factors_str(number, &mut w, print_exponents)?;
}
}
Err(e) => {
set_exit_code(1);
show_error!("error reading input: {}", e);
return Ok(());
}
}
return f;
}
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for Factors {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
factor(u64::arbitrary(g))
if let Err(e) = w.flush() {
show_error!("{}", e);
}
Ok(())
}
#[cfg(test)]
impl std::ops::BitXor<Exponent> for Factors {
type Output = Self;
fn bitxor(self, rhs: Exponent) -> Self {
debug_assert_ne!(rhs, 0);
let mut r = Self::one();
#[allow(clippy::explicit_iter_loop)]
for (p, e) in self.0.borrow().0.iter() {
r.add(*p, rhs * e);
}
debug_assert_eq!(r.product(), self.product().pow(rhs.into()));
r
}
pub fn uu_app() -> Command {
Command::new(uucore::util_name())
.version(crate_version!())
.about(ABOUT)
.override_usage(format_usage(USAGE))
.infer_long_args(true)
.disable_help_flag(true)
.args_override_self(true)
.arg(Arg::new(options::NUMBER).action(ArgAction::Append))
.arg(
Arg::new(options::EXPONENTS)
.short('h')
.long(options::EXPONENTS)
.help("Print factors in the form p^e")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::HELP)
.long(options::HELP)
.help("Print help information.")
.action(ArgAction::Help),
)
}

View file

@ -1,222 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (URL) appspot
use crate::numeric::*;
pub(crate) trait Basis {
const BASIS: &'static [u64];
}
impl Basis for Montgomery<u64> {
// Small set of bases for the Miller-Rabin prime test, valid for all 64b integers;
// discovered by Jim Sinclair on 2011-04-20, see miller-rabin.appspot.com
const BASIS: &'static [u64] = &[2, 325, 9375, 28178, 450_775, 9_780_504, 1_795_265_022];
}
impl Basis for Montgomery<u32> {
// spell-checker:ignore (names) Steve Worley
// Small set of bases for the Miller-Rabin prime test, valid for all 32b integers;
// discovered by Steve Worley on 2013-05-27, see miller-rabin.appspot.com
const BASIS: &'static [u64] = &[
4_230_279_247_111_683_200,
14_694_767_155_120_705_706,
16_641_139_526_367_750_375,
];
}
#[derive(Eq, PartialEq)]
#[must_use = "Ignoring the output of a primality test."]
pub(crate) enum Result {
Prime,
Pseudoprime,
Composite(u64),
}
impl Result {
pub(crate) fn is_prime(&self) -> bool {
*self == Self::Prime
}
}
// Deterministic Miller-Rabin primality-checking algorithm, adapted to extract
// (some) dividers; it will fail to factor strong pseudoprimes.
#[allow(clippy::many_single_char_names)]
pub(crate) fn test<A: Arithmetic + Basis>(m: A) -> Result {
use self::Result::*;
let n = m.modulus();
debug_assert!(n > 1);
debug_assert!(n % 2 != 0);
// n-1 = r 2ⁱ
let i = (n - 1).trailing_zeros();
let r = (n - 1) >> i;
let one = m.one();
let minus_one = m.minus_one();
'witness: for _a in A::BASIS {
let _a = _a % n;
if _a == 0 {
continue;
}
let a = m.to_mod(_a);
// x = a^r mod n
let mut x = m.pow(a, r);
if x == one || x == minus_one {
continue;
}
for _ in 1..i {
let y = m.mul(x, x);
if y == one {
return Composite(gcd(m.to_u64(x) - 1, m.modulus()));
} else if y == minus_one {
// This basis element is not a witness of `n` being composite.
// Keep looking.
continue 'witness;
}
x = y;
}
return Pseudoprime;
}
Prime
}
// Used by build.rs' tests and debug assertions
#[allow(dead_code)]
pub(crate) fn is_prime(n: u64) -> bool {
if n < 2 {
false
} else if n % 2 == 0 {
n == 2
} else {
test::<Montgomery<u64>>(Montgomery::new(n)).is_prime()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::numeric::{traits::DoubleInt, Arithmetic, Montgomery};
use quickcheck::quickcheck;
use std::iter;
const LARGEST_U64_PRIME: u64 = 0xFFFF_FFFF_FFFF_FFC5;
fn primes() -> impl Iterator<Item = u64> {
iter::once(2).chain(odd_primes())
}
fn odd_primes() -> impl Iterator<Item = u64> {
use crate::table::{NEXT_PRIME, PRIME_INVERSIONS_U64};
PRIME_INVERSIONS_U64
.iter()
.map(|(p, _, _)| *p)
.chain(iter::once(NEXT_PRIME))
}
#[test]
fn largest_prime() {
assert!(is_prime(LARGEST_U64_PRIME));
}
#[test]
fn largest_composites() {
for i in LARGEST_U64_PRIME + 1..=u64::MAX {
assert!(!is_prime(i), "2⁶⁴ - {} reported prime", u64::MAX - i + 1);
}
}
#[test]
fn two() {
assert!(is_prime(2));
}
fn first_primes<A: DoubleInt>()
where
Montgomery<A>: Basis,
{
for p in odd_primes() {
assert!(
test(Montgomery::<A>::new(p)).is_prime(),
"{p} reported composite"
);
}
}
#[test]
fn first_primes_u32() {
first_primes::<u32>();
}
#[test]
fn first_primes_u64() {
first_primes::<u64>();
}
#[test]
fn one() {
assert!(!is_prime(1));
}
#[test]
fn zero() {
assert!(!is_prime(0));
}
#[test]
fn first_composites() {
for (p, q) in primes().zip(odd_primes()) {
for i in p + 1..q {
assert!(!is_prime(i), "{i} reported prime");
}
}
}
#[test]
fn issue_1556() {
// 10 425 511 = 2441 × 4271
assert!(!is_prime(10_425_511));
}
fn small_semiprimes<A: DoubleInt>()
where
Montgomery<A>: Basis,
{
for p in odd_primes() {
for q in odd_primes().take_while(|q| *q <= p) {
let n = p * q;
let m = Montgomery::<A>::new(n);
assert!(!test(m).is_prime(), "{n} = {p} × {q} reported prime");
}
}
}
#[test]
fn small_semiprimes_u32() {
small_semiprimes::<u32>();
}
#[test]
fn small_semiprimes_u64() {
small_semiprimes::<u64>();
}
quickcheck! {
fn composites(i: u64, j: u64) -> bool {
// TODO: #1559 factor n > 2^64 - 1
match i.checked_mul(j) {
Some(n) => i < 2 || j < 2 || !is_prime(n),
_ => true,
}
}
}
}

View file

@ -1,126 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (vars) kgcdab gcdac gcdbc
use std::cmp::min;
use std::mem::swap;
pub fn gcd(mut u: u64, mut v: u64) -> u64 {
// Stein's binary GCD algorithm
// Base cases: gcd(n, 0) = gcd(0, n) = n
if u == 0 {
return v;
} else if v == 0 {
return u;
}
// gcd(2ⁱ u, 2ʲ v) = 2ᵏ gcd(u, v) with u, v odd and k = min(i, j)
// 2ᵏ is the greatest power of two that divides both u and v
let k = {
let i = u.trailing_zeros();
let j = v.trailing_zeros();
u >>= i;
v >>= j;
min(i, j)
};
loop {
// Loop invariant: u and v are odd
debug_assert!(u % 2 == 1, "u = {u} is even");
debug_assert!(v % 2 == 1, "v = {v} is even");
// gcd(u, v) = gcd(|u - v|, min(u, v))
if u > v {
swap(&mut u, &mut v);
}
v -= u;
if v == 0 {
// Reached the base case; gcd is 2ᵏ u
return u << k;
}
// gcd(u, 2ʲ v) = gcd(u, v) as u is odd
v >>= v.trailing_zeros();
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::{quickcheck, TestResult};
quickcheck! {
fn euclidean(a: u64, b: u64) -> bool {
// Test against the Euclidean algorithm
let g = {
let (mut a, mut b) = (a, b);
while b > 0 {
a %= b;
swap(&mut a, &mut b);
}
a
};
gcd(a, b) == g
}
fn one(a: u64) -> bool {
gcd(1, a) == 1
}
fn zero(a: u64) -> bool {
gcd(0, a) == a
}
fn divisor(a: u64, b: u64) -> TestResult {
// Test that gcd(a, b) divides a and b, unless a == b == 0
if a == 0 && b == 0 { return TestResult::discard(); } // restrict test domain to !(a == b == 0)
let g = gcd(a, b);
TestResult::from_bool( g != 0 && a % g == 0 && b % g == 0 )
}
fn commutative(a: u64, b: u64) -> bool {
gcd(a, b) == gcd(b, a)
}
fn associative(a: u64, b: u64, c: u64) -> bool {
gcd(a, gcd(b, c)) == gcd(gcd(a, b), c)
}
fn scalar_multiplication(a: u64, b: u64, k: u64) -> bool {
// TODO: #1559 factor n > 2^64 - 1
match (k.checked_mul(a), k.checked_mul(b), k.checked_mul(gcd(a, b))) {
(Some(ka), Some(kb), Some(kgcdab)) => gcd(ka, kb) == kgcdab,
_ => true
}
}
fn multiplicative(a: u64, b: u64, c: u64) -> bool {
// TODO: #1559 factor n > 2^64 - 1
match (a.checked_mul(b), gcd(a, c).checked_mul(gcd(b, c))) {
(Some(ab), Some(gcdac_gcdbc)) => {
// gcd(ab, c) = gcd(a, c) gcd(b, c) when a and b coprime
gcd(a, b) != 1 || gcd(ab, c) == gcdac_gcdbc
},
_ => true,
}
}
fn linearity(a: u64, b: u64, k: u64) -> bool {
// TODO: #1559 factor n > 2^64 - 1
match k.checked_mul(b) {
Some(kb) => {
match a.checked_add(kb) {
Some(a_plus_kb) => gcd(a_plus_kb, b) == gcd(a, b),
_ => true,
}
}
_ => true,
}
}
}
}

View file

@ -1,15 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
mod gcd;
pub use gcd::gcd;
pub(crate) mod traits;
mod modular_inverse;
pub(crate) use modular_inverse::modular_inverse;
mod montgomery;
pub(crate) use montgomery::{Arithmetic, Montgomery};

View file

@ -1,84 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use super::traits::Int;
// extended Euclid algorithm
// precondition: a is odd
pub(crate) fn modular_inverse<T: Int>(a: T) -> T {
let zero = T::zero();
let one = T::one();
debug_assert!(a % (one + one) == one, "{a:?} is not odd");
let mut t = zero;
let mut new_t = one;
let mut r = zero;
let mut new_r = a;
while new_r != zero {
let quot = if r == zero {
// special case when we're just starting out
// This works because we know that
// a does not divide 2^64, so floor(2^64 / a) == floor((2^64-1) / a);
T::max_value()
} else {
r
} / new_r;
let new_tp = t.wrapping_sub(&quot.wrapping_mul(&new_t));
t = new_t;
new_t = new_tp;
let new_rp = r.wrapping_sub(&quot.wrapping_mul(&new_r));
r = new_r;
new_r = new_rp;
}
debug_assert_eq!(r, one);
t
}
#[cfg(test)]
mod tests {
use super::{super::traits::Int, *};
use quickcheck::quickcheck;
fn small_values<T: Int>() {
// All odd integers from 1 to 20 000
let one = T::from(1).unwrap();
let two = T::from(2).unwrap();
let mut test_values = (0..10_000)
.map(|i| T::from(i).unwrap())
.map(|i| two * i + one);
assert!(test_values.all(|x| x.wrapping_mul(&modular_inverse(x)) == one));
}
#[test]
fn small_values_u32() {
small_values::<u32>();
}
#[test]
fn small_values_u64() {
small_values::<u64>();
}
quickcheck! {
fn random_values_u32(n: u32) -> bool {
match 2_u32.checked_mul(n) {
Some(n) => modular_inverse(n + 1).wrapping_mul(n + 1) == 1,
_ => true,
}
}
fn random_values_u64(n: u64) -> bool {
match 2_u64.checked_mul(n) {
Some(n) => modular_inverse(n + 1).wrapping_mul(n + 1) == 1,
_ => true,
}
}
}
}

View file

@ -1,246 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use super::*;
use traits::{DoubleInt, Int, One, OverflowingAdd, Zero};
pub(crate) trait Arithmetic: Copy + Sized {
// The type of integers mod m, in some opaque representation
type ModInt: Copy + Sized + Eq;
fn new(m: u64) -> Self;
fn modulus(&self) -> u64;
fn to_mod(&self, n: u64) -> Self::ModInt;
fn to_u64(&self, n: Self::ModInt) -> u64;
fn add(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt;
fn mul(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt;
fn pow(&self, mut a: Self::ModInt, mut b: u64) -> Self::ModInt {
let (_a, _b) = (a, b);
let mut result = self.one();
while b > 0 {
if b & 1 != 0 {
result = self.mul(result, a);
}
a = self.mul(a, a);
b >>= 1;
}
// Check that r (reduced back to the usual representation) equals
// a^b % n, unless the latter computation overflows
debug_assert!(self
.to_u64(_a)
.checked_pow(_b as u32)
.map(|r| r % self.modulus() == self.to_u64(result))
.unwrap_or(true));
result
}
fn one(&self) -> Self::ModInt {
self.to_mod(1)
}
fn minus_one(&self) -> Self::ModInt {
self.to_mod(self.modulus() - 1)
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Montgomery<T: DoubleInt> {
a: T,
n: T,
}
impl<T: DoubleInt> Montgomery<T> {
/// computes x/R mod n efficiently
fn reduce(&self, x: T::DoubleWidth) -> T {
let t_bits = T::zero().count_zeros() as usize;
debug_assert!(x < (self.n.as_double_width()) << t_bits);
// TODO: optimize
let Self { a, n } = self;
let m = T::from_double_width(x).wrapping_mul(a);
let nm = (n.as_double_width()) * (m.as_double_width());
let (xnm, overflow) = x.overflowing_add(&nm); // x + n*m
debug_assert_eq!(
xnm % (T::DoubleWidth::one() << T::zero().count_zeros() as usize),
T::DoubleWidth::zero()
);
// (x + n*m) / R
// in case of overflow, this is (2¹²⁸ + xnm)/2⁶⁴ - n = xnm/2⁶⁴ + (2⁶⁴ - n)
let y = T::from_double_width(xnm >> t_bits)
+ if overflow {
n.wrapping_neg()
} else {
T::zero()
};
if y >= *n {
y - *n
} else {
y
}
}
}
impl<T: DoubleInt> Arithmetic for Montgomery<T> {
// Montgomery transform, R=2⁶⁴
// Provides fast arithmetic mod n (n odd, u64)
type ModInt = T;
fn new(n: u64) -> Self {
debug_assert!(T::zero().count_zeros() >= 64 || n < (1 << T::zero().count_zeros() as usize));
let n = T::from_u64(n);
let a = modular_inverse(n).wrapping_neg();
debug_assert_eq!(n.wrapping_mul(&a), T::one().wrapping_neg());
Self { a, n }
}
fn modulus(&self) -> u64 {
self.n.as_u64()
}
fn to_mod(&self, x: u64) -> Self::ModInt {
// TODO: optimize!
debug_assert!(x < self.n.as_u64());
let r = T::from_double_width(
((T::DoubleWidth::from_u64(x)) << T::zero().count_zeros() as usize)
% self.n.as_double_width(),
);
debug_assert_eq!(x, self.to_u64(r));
r
}
fn to_u64(&self, n: Self::ModInt) -> u64 {
self.reduce(n.as_double_width()).as_u64()
}
fn add(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt {
let (r, overflow) = a.overflowing_add(&b);
// In case of overflow, a+b = 2⁶⁴ + r = (2⁶⁴ - n) + r (working mod n)
let r = if overflow {
r + self.n.wrapping_neg()
} else {
r
};
// Normalize to [0; n[
let r = if r < self.n { r } else { r - self.n };
// Check that r (reduced back to the usual representation) equals
// a+b % n
#[cfg(debug_assertions)]
{
let a_r = self.to_u64(a) as u128;
let b_r = self.to_u64(b) as u128;
let r_r = self.to_u64(r);
let r_2 = ((a_r + b_r) % self.n.as_u128()) as u64;
debug_assert_eq!(
r_r, r_2,
"[{}] = {} ≠ {} = {} + {} = [{}] + [{}] mod {}; a = {}",
r, r_r, r_2, a_r, b_r, a, b, self.n, self.a
);
}
r
}
fn mul(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt {
let r = self.reduce(a.as_double_width() * b.as_double_width());
// Check that r (reduced back to the usual representation) equals
// a*b % n
#[cfg(debug_assertions)]
{
let a_r = self.to_u64(a) as u128;
let b_r = self.to_u64(b) as u128;
let r_r = self.to_u64(r);
let r_2: u64 = ((a_r * b_r) % self.n.as_u128()) as u64;
debug_assert_eq!(
r_r, r_2,
"[{}] = {} ≠ {} = {} * {} = [{}] * [{}] mod {}; a = {}",
r, r_r, r_2, a_r, b_r, a, b, self.n, self.a
);
}
r
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_add<A: DoubleInt>() {
for n in 0..100 {
let n = 2 * n + 1;
let m = Montgomery::<A>::new(n);
for x in 0..n {
let m_x = m.to_mod(x);
for y in 0..=x {
let m_y = m.to_mod(y);
println!("{n:?}, {x:?}, {y:?}");
assert_eq!((x + y) % n, m.to_u64(m.add(m_x, m_y)));
}
}
}
}
#[test]
fn add_u32() {
test_add::<u32>();
}
#[test]
fn add_u64() {
test_add::<u64>();
}
fn test_multiplication<A: DoubleInt>() {
for n in 0..100 {
let n = 2 * n + 1;
let m = Montgomery::<A>::new(n);
for x in 0..n {
let m_x = m.to_mod(x);
for y in 0..=x {
let m_y = m.to_mod(y);
assert_eq!((x * y) % n, m.to_u64(m.mul(m_x, m_y)));
}
}
}
}
#[test]
fn multiplication_u32() {
test_multiplication::<u32>();
}
#[test]
fn multiplication_u64() {
test_multiplication::<u64>();
}
fn test_roundtrip<A: DoubleInt>() {
for n in 0..100 {
let n = 2 * n + 1;
let m = Montgomery::<A>::new(n);
for x in 0..n {
let x_ = m.to_mod(x);
assert_eq!(x, m.to_u64(x_));
}
}
}
#[test]
fn roundtrip_u32() {
test_roundtrip::<u32>();
}
#[test]
fn roundtrip_u64() {
test_roundtrip::<u64>();
}
}

View file

@ -1,89 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
pub(crate) use num_traits::{
identities::{One, Zero},
ops::overflowing::OverflowingAdd,
};
use num_traits::{
int::PrimInt,
ops::wrapping::{WrappingMul, WrappingNeg, WrappingSub},
};
use std::fmt::{Debug, Display};
#[allow(dead_code)] // Rust doesn't recognize the use in the macro
pub(crate) trait Int:
Display + Debug + PrimInt + OverflowingAdd + WrappingNeg + WrappingSub + WrappingMul
{
fn as_u64(&self) -> u64;
fn from_u64(n: u64) -> Self;
#[cfg(debug_assertions)]
fn as_u128(&self) -> u128;
}
#[allow(dead_code)] // Rust doesn't recognize the use in the macro
pub(crate) trait DoubleInt: Int {
/// An integer type with twice the width of `Self`.
/// In particular, multiplications (of `Int` values) can be performed in
/// `Self::DoubleWidth` without possibility of overflow.
type DoubleWidth: Int;
fn as_double_width(self) -> Self::DoubleWidth;
fn from_double_width(n: Self::DoubleWidth) -> Self;
}
macro_rules! int {
( $x:ty ) => {
impl Int for $x {
fn as_u64(&self) -> u64 {
*self as u64
}
fn from_u64(n: u64) -> Self {
n as _
}
#[cfg(debug_assertions)]
fn as_u128(&self) -> u128 {
*self as u128
}
}
};
}
macro_rules! double_int {
( $x:ty, $y:ty ) => {
int!($x);
impl DoubleInt for $x {
type DoubleWidth = $y;
fn as_double_width(self) -> $y {
self as _
}
fn from_double_width(n: $y) -> Self {
n as _
}
}
};
}
double_int!(u32, u64);
double_int!(u64, u128);
int!(u128);
/// Helper macro for instantiating tests over u32 and u64
#[cfg(test)]
#[macro_export]
macro_rules! parametrized_check {
( $f:ident ) => {
paste::item! {
#[test]
fn [< $f _ u32 >]() {
$f::<u32>()
}
#[test]
fn [< $f _ u64 >]() {
$f::<u64>()
}
}
};
}

View file

@ -1,43 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use rand::distributions::{Distribution, Uniform};
use rand::rngs::SmallRng;
use rand::{thread_rng, SeedableRng};
use std::cmp::{max, min};
use crate::numeric::*;
pub(crate) fn find_divisor<A: Arithmetic>(input: A) -> u64 {
let mut rand = {
let range = Uniform::new(1, input.modulus());
let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
move || input.to_mod(range.sample(&mut rng))
};
let quadratic = |a, b| move |x| input.add(input.mul(a, input.mul(x, x)), b);
loop {
let f = quadratic(rand(), rand());
let mut x = rand();
let mut y = x;
loop {
x = f(x);
y = f(f(y));
let d = {
let _x = input.to_u64(x);
let _y = input.to_u64(y);
gcd(input.modulus(), max(_x, _y) - min(_x, _y))
};
if d == input.modulus() {
// Failure, retry with a different quadratic
break;
} else if d > 1 {
return d;
}
}
}
}

View file

@ -1,98 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker: ignore (ToDO) INVS
use crate::Factors;
include!(concat!(env!("OUT_DIR"), "/prime_table.rs"));
pub fn factor(num: &mut u64, factors: &mut Factors) {
for &(prime, inv, ceil) in PRIME_INVERSIONS_U64 {
if *num == 1 {
break;
}
// inv = prime^-1 mod 2^64
// ceil = floor((2^64-1) / prime)
// if (num * inv) mod 2^64 <= ceil, then prime divides num
// See https://math.stackexchange.com/questions/1251327/
// for a nice explanation.
let mut k = 0;
loop {
let x = num.wrapping_mul(inv);
// While prime divides num
if x <= ceil {
*num = x;
k += 1;
#[cfg(feature = "coz")]
coz::progress!("factor found");
} else {
if k > 0 {
factors.add(prime, k);
}
break;
}
}
}
}
pub const CHUNK_SIZE: usize = 8;
pub fn factor_chunk(n_s: &mut [u64; CHUNK_SIZE], f_s: &mut [Factors; CHUNK_SIZE]) {
for &(prime, inv, ceil) in PRIME_INVERSIONS_U64 {
if n_s[0] == 1 && n_s[1] == 1 && n_s[2] == 1 && n_s[3] == 1 {
break;
}
for (num, factors) in n_s.iter_mut().zip(f_s.iter_mut()) {
if *num == 1 {
continue;
}
let mut k = 0;
loop {
let x = num.wrapping_mul(inv);
// While prime divides num
if x <= ceil {
*num = x;
k += 1;
} else {
if k > 0 {
factors.add(prime, k);
}
break;
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Factors;
use quickcheck::quickcheck;
use rand::{rngs::SmallRng, Rng, SeedableRng};
quickcheck! {
fn chunk_vs_iter(seed: u64) -> () {
let mut rng = SmallRng::seed_from_u64(seed);
let mut n_c: [u64; CHUNK_SIZE] = rng.gen();
let mut f_c: [Factors; CHUNK_SIZE] = rng.gen();
let mut n_i = n_c;
let mut f_i = f_c.clone();
for (n, f) in n_i.iter_mut().zip(f_i.iter_mut()) {
factor(n, f);
}
factor_chunk(&mut n_c, &mut f_c);
assert_eq!(n_i, n_c);
assert_eq!(f_i, f_c);
}
}
}

View file

@ -17,10 +17,10 @@ array-init = "2.0.0"
criterion = "0.3"
rand = "0.8"
rand_chacha = "0.3.1"
num-bigint = "0.4.4"
num-prime = "0.4.3"
num-traits = "0.2.18"
[[bench]]
name = "gcd"
harness = false
[[bench]]
name = "table"

View file

@ -1,33 +0,0 @@
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use uu_factor::numeric;
fn gcd(c: &mut Criterion) {
let inputs = {
// Deterministic RNG; use an explicitly-named RNG to guarantee stability
use rand::{RngCore, SeedableRng};
use rand_chacha::ChaCha8Rng;
const SEED: u64 = 0xab4d_1dea_dead_cafe;
let mut rng = ChaCha8Rng::seed_from_u64(SEED);
std::iter::repeat_with(move || (rng.next_u64(), rng.next_u64()))
};
let mut group = c.benchmark_group("gcd");
for (n, m) in inputs.take(10) {
group.bench_with_input(
BenchmarkId::from_parameter(format!("{}_{}", n, m)),
&(n, m),
|b, &(n, m)| {
b.iter(|| numeric::gcd(n, m));
},
);
}
group.finish();
}
criterion_group!(benches, gcd);
criterion_main!(benches);

View file

@ -2,9 +2,11 @@
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore funcs
use array_init::array_init;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use uu_factor::{table::*, Factors};
fn table(c: &mut Criterion) {
#[cfg(target_os = "linux")]
@ -26,21 +28,10 @@ fn table(c: &mut Criterion) {
group.throughput(Throughput::Elements(INPUT_SIZE as _));
for a in inputs.take(10) {
let a_str = format!("{:?}", a);
group.bench_with_input(BenchmarkId::new("factor_chunk", &a_str), &a, |b, &a| {
b.iter(|| {
let mut n_s = a;
let mut f_s: [_; INPUT_SIZE] = array_init(|_| Factors::one());
for (n_s, f_s) in n_s.chunks_mut(CHUNK_SIZE).zip(f_s.chunks_mut(CHUNK_SIZE)) {
factor_chunk(n_s.try_into().unwrap(), f_s.try_into().unwrap());
}
});
});
group.bench_with_input(BenchmarkId::new("factor", &a_str), &a, |b, &a| {
b.iter(|| {
let mut n_s = a;
let mut f_s: [_; INPUT_SIZE] = array_init(|_| Factors::one());
for (n, f) in n_s.iter_mut().zip(f_s.iter_mut()) {
factor(n, f);
for n in a {
let _r = num_prime::nt_funcs::factors(n, None);
}
});
});

View file

@ -3,16 +3,12 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (methods) hexdigest
// spell-checker:ignore (methods) hexdigest funcs nprimes
use crate::common::util::TestScenario;
use std::time::{Duration, SystemTime};
#[path = "../../src/uu/factor/sieve.rs"]
mod sieve;
use self::sieve::Sieve;
use rand::distributions::{Distribution, Uniform};
use rand::{rngs::SmallRng, Rng, SeedableRng};
@ -155,7 +151,7 @@ fn test_cli_args() {
#[test]
fn test_random() {
let log_num_primes = f64::from(u32::try_from(NUM_PRIMES).unwrap()).log2().ceil();
let primes = Sieve::primes().take(NUM_PRIMES).collect::<Vec<u64>>();
let primes = num_prime::nt_funcs::nprimes(NUM_PRIMES);
let rng_seed = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
@ -1269,3 +1265,53 @@ const PRIMES50: &[u64] = &[
fn fails_on_directory() {
new_ucmd!().pipe_in(".").fails();
}
#[test]
fn succeeds_with_numbers_larger_than_u64() {
new_ucmd!()
.arg("158909489063877810457")
.succeeds()
.stdout_is("158909489063877810457: 3401347 3861211 12099721\n");
new_ucmd!()
.arg("222087527029934481871")
.succeeds()
.stdout_is("222087527029934481871: 15601 26449 111427 4830277\n");
new_ucmd!()
.arg("12847291069740315094892340035")
.succeeds()
.stdout_is(
"12847291069740315094892340035: \
5 4073 18899 522591721 63874247821\n",
);
}
#[test]
fn succeeds_with_numbers_larger_than_u128() {
new_ucmd!()
.arg("-h")
.arg("340282366920938463463374607431768211456")
.succeeds()
.stdout_is("340282366920938463463374607431768211456: 2^128\n");
new_ucmd!()
.arg("+170141183460469231731687303715884105729")
.succeeds()
.stdout_is(
"170141183460469231731687303715884105729: \
3 56713727820156410577229101238628035243\n",
);
}
#[test]
fn succeeds_with_numbers_larger_than_u256() {
new_ucmd!()
.arg("-h")
.arg(
"115792089237316195423570985008687907853\
269984665640564039457584007913129639936",
)
.succeeds()
.stdout_is(
"115792089237316195423570985008687907853\
269984665640564039457584007913129639936: 2^256\n",
);
}

View file

@ -221,7 +221,7 @@ grep -rlE '/usr/local/bin/\s?/usr/local/bin' init.cfg tests/* | xargs -r sed -Ei
# we should not regress our project just to match what GNU is going.
# So, do some changes on the fly
patch -N -r - -d "$path_GNU" -p 1 -i "`realpath \"$path_UUTILS/util/gnu-patches/tests_env_env-S.pl.patch\"`" || true
eval cat "$path_UUTILS/util/gnu-patches/*.patch" | patch -N -r - -d "$path_GNU" -p 1 -i - || true
sed -i -e "s|rm: cannot remove 'e/slink'|rm: cannot remove 'e'|g" tests/rm/fail-eacces.sh

View file

@ -0,0 +1,21 @@
diff --git a/tests/factor/factor.pl b/tests/factor/factor.pl
index 6e612e418..f19c06ca0 100755
--- a/tests/factor/factor.pl
+++ b/tests/factor/factor.pl
@@ -61,12 +61,13 @@ my @Tests =
# Map newer glibc diagnostic to expected.
# Also map OpenBSD 5.1's "unknown option" to expected "invalid option".
{ERR_SUBST => q!s/'1'/1/;s/unknown/invalid/!},
- {ERR => "$prog: invalid option -- 1\n"
- . "Try '$prog --help' for more information.\n"},
+ {ERR => "error: unexpected argument '-1' found\n\n"
+ . " tip: to pass '-1' as a value, use '-- -1'\n\n"
+ . "Usage: factor [OPTION]... [NUMBER]...\n"},
{EXIT => 1}],
['cont', 'a 4',
{OUT => "4: 2 2\n"},
- {ERR => "$prog: 'a' is not a valid positive integer\n"},
+ {ERR => "$prog: warning: a: invalid digit found in string\n"},
{EXIT => 1}],
['bug-2012-a', '465658903', {OUT => '15259 30517'}],
['bug-2012-b', '2242724851', {OUT => '33487 66973'}],