Skip to content

Commit

Permalink
Merge pull request #1558 from nbraud/factor/faster/centralise_logic
Browse files Browse the repository at this point in the history
factor: Refactor the factoring logic
  • Loading branch information
sylvestre committed Jul 15, 2020
2 parents bb01e67 + 4cfe754 commit 1fb2e89
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 121 deletions.
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions src/uu/factor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 63 additions & 0 deletions src/uu/factor/src/cli.rs
Original file line number Diff line number Diff line change
@@ -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
}
128 changes: 47 additions & 81 deletions src/uu/factor/src/factor.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,36 @@
// * 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.

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;

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 = "";
use crate::numeric::{Arithmetic, Montgomery};
use crate::{miller_rabin, rho, table};

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)
}

Expand All @@ -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() {
Expand All @@ -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;
}

Expand All @@ -99,56 +99,16 @@ fn factor(mut n: u64) -> Factors {
return factors;
}

let (f, n) = table::factor(n);
factors *= f;

if n >= table::NEXT_PRIME {
factors *= rho::factor(n);
}
let (factors, n) = table::factor(n, factors);

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() {
Expand Down Expand Up @@ -176,4 +136,10 @@ mod tests {
assert!(factor(pseudoprime).product() == pseudoprime);
}
}

quickcheck! {
fn factor_recombines(i: u64) -> bool {
i == 0 || factor(i).product() == i
}
}
}
8 changes: 6 additions & 2 deletions src/uu/factor/src/miller_rabin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
Loading

0 comments on commit 1fb2e89

Please sign in to comment.