mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-08-01 21:47:46 +00:00
Merge pull request #1558 from nbraud/factor/faster/centralise_logic
factor: Refactor the factoring logic
This commit is contained in:
commit
1fb2e89e02
7 changed files with 149 additions and 121 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -1,3 +1,5 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "advapi32-sys"
|
||||
version = "0.2.0"
|
||||
|
@ -367,6 +369,15 @@ name = "either"
|
|||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.1"
|
||||
|
@ -670,6 +681,17 @@ name = "quick-error"
|
|||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.3.15"
|
||||
|
@ -1313,6 +1335,7 @@ dependencies = [
|
|||
name = "uu_factor"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uucore 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)",
|
||||
"uucore_procs 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)",
|
||||
|
@ -2193,6 +2216,7 @@ dependencies = [
|
|||
"checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a"
|
||||
"checksum dunce 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b2641c4a7c0c4101df53ea572bffdc561c146f6c2eb09e4df02bc4811e3feeb4"
|
||||
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82"
|
||||
"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b"
|
||||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
@ -2234,6 +2258,7 @@ dependencies = [
|
|||
"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
||||
"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||
"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
"checksum quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
|
||||
|
|
|
@ -11,14 +11,17 @@ keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
|
|||
categories = ["command-line-utilities"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
path = "src/factor.rs"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.5"
|
||||
uucore = { version="0.0.4", package="uucore", git="https://github.com/uutils/uucore.git", branch="canary" }
|
||||
uucore_procs = { version="0.0.4", package="uucore_procs", git="https://github.com/uutils/uucore.git", branch="canary" }
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "0.9.2"
|
||||
|
||||
[[bin]]
|
||||
name = "factor"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
path = "src/cli.rs"
|
||||
|
|
63
src/uu/factor/src/cli.rs
Normal file
63
src/uu/factor/src/cli.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
// * This file is part of the uutils coreutils package.
|
||||
// *
|
||||
// * (c) 2014 T. Jameson Little <t.jameson.little@gmail.com>
|
||||
// * (c) 2020 nicoo <nicoo@debian.org>
|
||||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE file
|
||||
// * that was distributed with this source code.
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use std::error::Error;
|
||||
use std::io::{self, stdin, stdout, BufRead, Write};
|
||||
|
||||
mod factor;
|
||||
pub(crate) use factor::*;
|
||||
|
||||
mod miller_rabin;
|
||||
mod numeric;
|
||||
mod rho;
|
||||
mod table;
|
||||
|
||||
static SYNTAX: &str = "[OPTION] [NUMBER]...";
|
||||
static SUMMARY: &str = "Print the prime factors of the given number(s).
|
||||
If none are specified, read from standard input.";
|
||||
static LONG_HELP: &str = "";
|
||||
|
||||
fn print_factors_str(num_str: &str, w: &mut impl io::Write) -> Result<(), Box<dyn Error>> {
|
||||
num_str
|
||||
.parse::<u64>()
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|x| writeln!(w, "{}:{}", x, factor(x)).map_err(|e| e.into()))
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let matches = app!(SYNTAX, SUMMARY, LONG_HELP).parse(args.collect_str());
|
||||
let stdout = stdout();
|
||||
let mut w = io::BufWriter::new(stdout.lock());
|
||||
|
||||
if matches.free.is_empty() {
|
||||
let stdin = stdin();
|
||||
|
||||
for line in stdin.lock().lines() {
|
||||
for number in line.unwrap().split_whitespace() {
|
||||
if let Err(e) = print_factors_str(number, &mut w) {
|
||||
show_warning!("{}: {}", number, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for number in &matches.free {
|
||||
if let Err(e) = print_factors_str(number, &mut w) {
|
||||
show_warning!("{}: {}", number, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = w.flush() {
|
||||
show_error!("{}", e);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
// * This file is part of the uutils coreutils package.
|
||||
// *
|
||||
// * (c) 2014 T. Jameson Little <t.jameson.little@gmail.com>
|
||||
// * (c) 2020 nicoo <nicoo@debian.org>
|
||||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE file
|
||||
|
@ -8,48 +7,30 @@
|
|||
|
||||
extern crate rand;
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io::{self, stdin, stdout, BufRead, Write};
|
||||
use std::ops;
|
||||
|
||||
mod miller_rabin;
|
||||
mod numeric;
|
||||
mod rho;
|
||||
mod table;
|
||||
use crate::numeric::{Arithmetic, Montgomery};
|
||||
use crate::{miller_rabin, rho, table};
|
||||
|
||||
static SYNTAX: &str = "[OPTION] [NUMBER]...";
|
||||
static SUMMARY: &str = "Print the prime factors of the given number(s).
|
||||
If none are specified, read from standard input.";
|
||||
static LONG_HELP: &str = "";
|
||||
|
||||
struct Factors {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Factors {
|
||||
f: BTreeMap<u64, u8>,
|
||||
}
|
||||
|
||||
impl Factors {
|
||||
fn one() -> Factors {
|
||||
pub fn one() -> Factors {
|
||||
Factors { f: BTreeMap::new() }
|
||||
}
|
||||
|
||||
fn prime(p: u64) -> Factors {
|
||||
debug_assert!(miller_rabin::is_prime(p));
|
||||
let mut f = Factors::one();
|
||||
f.push(p);
|
||||
f
|
||||
}
|
||||
|
||||
fn add(&mut self, prime: u64, exp: u8) {
|
||||
pub fn add(&mut self, prime: u64, exp: u8) {
|
||||
debug_assert!(miller_rabin::is_prime(prime));
|
||||
debug_assert!(exp > 0);
|
||||
let n = *self.f.get(&prime).unwrap_or(&0);
|
||||
self.f.insert(prime, exp + n);
|
||||
}
|
||||
|
||||
fn push(&mut self, prime: u64) {
|
||||
pub fn push(&mut self, prime: u64) {
|
||||
self.add(prime, 1)
|
||||
}
|
||||
|
||||
|
@ -61,14 +42,6 @@ impl Factors {
|
|||
}
|
||||
}
|
||||
|
||||
impl ops::MulAssign<Factors> for Factors {
|
||||
fn mul_assign(&mut self, other: Factors) {
|
||||
for (prime, exp) in &other.f {
|
||||
self.add(*prime, *exp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Factors {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for (p, exp) in self.f.iter() {
|
||||
|
@ -81,11 +54,38 @@ impl fmt::Display for Factors {
|
|||
}
|
||||
}
|
||||
|
||||
fn factor(mut n: u64) -> Factors {
|
||||
fn _factor<A: Arithmetic>(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| {
|
||||
// TODO: Optimise with 32 and 64b versions
|
||||
_factor::<A>(n, f)
|
||||
};
|
||||
|
||||
if num == 1 {
|
||||
return f;
|
||||
}
|
||||
|
||||
let n = A::new(num);
|
||||
let divisor = match miller_rabin::test::<A>(n) {
|
||||
Prime => {
|
||||
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 {
|
||||
let mut factors = Factors::one();
|
||||
|
||||
if n < 2 {
|
||||
factors.push(n);
|
||||
return factors;
|
||||
}
|
||||
|
||||
|
@ -99,56 +99,16 @@ fn factor(mut n: u64) -> Factors {
|
|||
return factors;
|
||||
}
|
||||
|
||||
let (f, n) = table::factor(n);
|
||||
factors *= f;
|
||||
let (factors, n) = table::factor(n, factors);
|
||||
|
||||
if n >= table::NEXT_PRIME {
|
||||
factors *= rho::factor(n);
|
||||
}
|
||||
|
||||
factors
|
||||
}
|
||||
|
||||
fn print_factors_str(num_str: &str, w: &mut impl io::Write) -> Result<(), Box<dyn Error>> {
|
||||
num_str
|
||||
.parse::<u64>()
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|x| writeln!(w, "{}:{}", x, factor(x)).map_err(|e| e.into()))
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let matches = app!(SYNTAX, SUMMARY, LONG_HELP).parse(args.collect_str());
|
||||
let stdout = stdout();
|
||||
let mut w = io::BufWriter::new(stdout.lock());
|
||||
|
||||
if matches.free.is_empty() {
|
||||
let stdin = stdin();
|
||||
|
||||
for line in stdin.lock().lines() {
|
||||
for number in line.unwrap().split_whitespace() {
|
||||
if let Err(e) = print_factors_str(number, &mut w) {
|
||||
show_warning!("{}: {}", number, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for number in &matches.free {
|
||||
if let Err(e) = print_factors_str(number, &mut w) {
|
||||
show_warning!("{}: {}", number, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = w.flush() {
|
||||
show_error!("{}", e);
|
||||
}
|
||||
|
||||
0
|
||||
// TODO: Optimise with 32 and 64b versions
|
||||
_factor::<Montgomery>(n, factors)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::factor;
|
||||
use quickcheck::quickcheck;
|
||||
|
||||
#[test]
|
||||
fn factor_recombines_small() {
|
||||
|
@ -176,4 +136,10 @@ mod tests {
|
|||
assert!(factor(pseudoprime).product() == pseudoprime);
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn factor_recombines(i: u64) -> bool {
|
||||
i == 0 || factor(i).product() == i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,10 +84,14 @@ pub(crate) fn test<A: Arithmetic>(m: A) -> Result {
|
|||
Prime
|
||||
}
|
||||
|
||||
// Used by build.rs' tests
|
||||
// Used by build.rs' tests and debug assertions
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn is_prime(n: u64) -> bool {
|
||||
test::<Montgomery>(Montgomery::new(n)).is_prime()
|
||||
if n % 2 == 0 {
|
||||
n == 2
|
||||
} else {
|
||||
test::<Montgomery>(Montgomery::new(n)).is_prime()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -11,11 +11,9 @@ use rand::rngs::SmallRng;
|
|||
use rand::{thread_rng, SeedableRng};
|
||||
use std::cmp::{max, min};
|
||||
|
||||
use crate::miller_rabin::Result::*;
|
||||
use crate::numeric::*;
|
||||
use crate::{miller_rabin, Factors};
|
||||
|
||||
fn find_divisor<A: Arithmetic>(n: A) -> u64 {
|
||||
pub(crate) fn find_divisor<A: Arithmetic>(n: A) -> u64 {
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
let mut rand = {
|
||||
let range = Uniform::new(1, n.modulus());
|
||||
|
@ -47,33 +45,3 @@ fn find_divisor<A: Arithmetic>(n: A) -> u64 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _factor<A: Arithmetic>(num: u64) -> Factors {
|
||||
// Shadow the name, so the recursion automatically goes from “Big” arithmetic to small.
|
||||
let _factor = |n| {
|
||||
// TODO: Optimise with 32 and 64b versions
|
||||
_factor::<A>(n)
|
||||
};
|
||||
|
||||
if num == 1 {
|
||||
return Factors::one();
|
||||
}
|
||||
|
||||
let n = A::new(num);
|
||||
let divisor = match miller_rabin::test::<A>(n) {
|
||||
Prime => {
|
||||
return Factors::prime(num);
|
||||
}
|
||||
|
||||
Composite(d) => d,
|
||||
Pseudoprime => find_divisor::<A>(n),
|
||||
};
|
||||
|
||||
let mut factors = _factor(divisor);
|
||||
factors *= _factor(num / divisor);
|
||||
factors
|
||||
}
|
||||
|
||||
pub(crate) fn factor(n: u64) -> Factors {
|
||||
_factor::<Montgomery>(n)
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@ use crate::Factors;
|
|||
|
||||
include!(concat!(env!("OUT_DIR"), "/prime_table.rs"));
|
||||
|
||||
pub(crate) fn factor(mut num: u64) -> (Factors, u64) {
|
||||
let mut factors = Factors::one();
|
||||
pub(crate) fn factor(mut num: u64, mut factors: Factors) -> (Factors, u64) {
|
||||
for &(prime, inv, ceil) in P_INVS_U64 {
|
||||
if num == 1 {
|
||||
break;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue