Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
43ee92c
factor::numeric: Generalise modular inverse computation
nicoonoclaste Jun 23, 2020
e68bb19
factor::numeric: Add a 32b Montgomery variant [WiP]
nicoonoclaste Jun 21, 2020
a440807
factor::miller_rabin: Use a specialized basis for 32b integers
nicoonoclaste Jun 21, 2020
4d28f48
factor: combine Montgomery and Montgomery32
Arcterus Jun 24, 2020
774feb0
factor::numeric: Generalise tests for Arithmetic trait
nicoonoclaste Jun 24, 2020
3f79be0
factor::numeric: Use debug_assert! for runtime assertions.
nicoonoclaste Jun 24, 2020
2824441
factor::numeric: Document when to remove OverflowingAdd trait
nicoonoclaste Jul 1, 2020
caa79a1
factor::numeric: Split Int and DoubleInt traits
nicoonoclaste Jul 1, 2020
19a8231
factor::numeric::Arithmetic: Rename associated type I to ModInt
nicoonoclaste Jul 1, 2020
53954ba
factor::numeric: Refactor away the use of {To,From}Primitives
nicoonoclaste Jul 1, 2020
f95f977
factor::numeric: Generate implementations of Int with a macro
nicoonoclaste Jul 1, 2020
b25c77c
factor::numeric: Generate implementations of DoubleInt with a macro
nicoonoclaste Jul 1, 2020
d2b43f4
factor::numeric::OverflowingAdd: Generate impls with a macro
nicoonoclaste Jul 2, 2020
3082903
factor::miller_rabin::is_prime: Fix bug
nicoonoclaste Jul 4, 2020
0a1200b
factor::miller_rabin: Add test for the largest 64b composite numbers
nicoonoclaste Jul 4, 2020
600268c
factor::miller_rabin::tests: Refactor
nicoonoclaste Jul 4, 2020
1e4d824
factor::miller_rabin: Add negative test over all small composites
nicoonoclaste Jul 4, 2020
d2fa0fe
factor::miller_rabin::tests: small_composites → small_semiprimes
nicoonoclaste Jul 4, 2020
4f08e28
factor::miller_rabin: Add property-based test
nicoonoclaste Jul 4, 2020
9b149a7
factor::miller_rabin: Hoist edge-cases (even, <2) out of test()
nicoonoclaste Jul 4, 2020
3d6fdff
factor::miller_rabin: Generalise tests to 32 and 64b Montgomery
nicoonoclaste Jul 4, 2020
cbcc760
factor::miller_rabin: Squash another bug! >:3
nicoonoclaste Jul 4, 2020
7a1b86c
factor::numeric::tests: Use a macro to instantiate every test
nicoonoclaste Jul 4, 2020
6256750
factor::miller_rabin: Use a macro to instantiate every test
nicoonoclaste Jul 4, 2020
6e228d3
Merge branches 'factor/faster/{centralise_logic, montgomery32}'
nicoonoclaste Jul 4, 2020
c6276b7
fixup! factor::numeric: Generalise modular inverse computation
nicoonoclaste Jul 9, 2020
96224f6
factor::numeric::DoubleInt: Clarify methods and associated types
nicoonoclaste Jul 21, 2020
141d813
factor::numeric::Int: Remove `from_u128` method
nicoonoclaste Jul 21, 2020
33a1c44
factor::numeric::DoubleInt: Document the DoubleWidth associated type
nicoonoclaste Jul 21, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions Cargo.lock

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

6 changes: 6 additions & 0 deletions src/uu/factor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@ keywords = ["coreutils", "uutils", "cross-platform", "cli", "utility"]
categories = ["command-line-utilities"]
edition = "2018"

[build-dependencies]
num-traits = "0.2" # used in src/numerics.rs, which is included by build.rs


[dependencies]
num-traits = "0.2"
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]
paste = "0.1.18"
quickcheck = "0.9.2"

[[bin]]
Expand Down
4 changes: 2 additions & 2 deletions src/uu/factor/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use miller_rabin::is_prime;

#[path = "src/numeric.rs"]
mod numeric;
use numeric::inv_mod_u64;
use numeric::modular_inverse;

mod sieve;

Expand Down Expand Up @@ -57,7 +57,7 @@ fn main() {
let mut x = primes.next().unwrap();
for next in primes {
// format the table
let outstr = format!("({}, {}, {}),", x, inv_mod_u64(x), std::u64::MAX / x);
let outstr = format!("({}, {}, {}),", x, modular_inverse(x), std::u64::MAX / x);
if cols + outstr.len() > MAX_WIDTH {
write!(file, "\n {}", outstr).unwrap();
cols = 4 + outstr.len();
Expand Down
17 changes: 12 additions & 5 deletions src/uu/factor/src/factor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,16 @@ impl fmt::Display for Factors {
}
}

fn _factor<A: Arithmetic>(num: u64, f: Factors) -> Factors {
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| {
// TODO: Optimise with 32 and 64b versions
_factor::<A>(n, f)
if n < (1 << 32) {
_factor::<Montgomery<u32>>(n, f)
} else {
_factor::<A>(n, f)
}
};

if num == 1 {
Expand Down Expand Up @@ -101,8 +105,11 @@ pub fn factor(mut n: u64) -> Factors {

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

// TODO: Optimise with 32 and 64b versions
_factor::<Montgomery>(n, factors)
if n < (1 << 32) {
_factor::<Montgomery<u32>>(n, factors)
} else {
_factor::<Montgomery<u64>>(n, factors)
}
}

#[cfg(test)]
Expand Down
136 changes: 107 additions & 29 deletions src/uu/factor/src/miller_rabin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,27 @@

use crate::numeric::*;

// 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
#[allow(clippy::unreadable_literal)]
const BASIS: [u64; 7] = [2, 325, 9375, 28178, 450775, 9780504, 1795265022];
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
#[allow(clippy::unreadable_literal)]
const BASIS: &'static [u64] = &[2, 325, 9375, 28178, 450775, 9780504, 1795265022];
}

impl Basis for Montgomery<u32> {
// 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
#[allow(clippy::unreadable_literal)]
const BASIS: &'static [u64] = &[
4230279247111683200,
14694767155120705706,
16641139526367750375,
];
}

#[derive(Eq, PartialEq)]
pub(crate) enum Result {
Expand All @@ -23,16 +40,12 @@ impl Result {
// 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>(m: A) -> Result {
pub(crate) fn test<A: Arithmetic + Basis>(m: A) -> Result {
use self::Result::*;

let n = m.modulus();
if n < 2 {
return Pseudoprime;
}
if n % 2 == 0 {
return if n == 2 { Prime } else { Composite(2) };
}
debug_assert!(n > 1);
debug_assert!(n % 2 != 0);

// n-1 = r 2ⁱ
let i = (n - 1).trailing_zeros();
Expand All @@ -41,10 +54,10 @@ pub(crate) fn test<A: Arithmetic>(m: A) -> Result {
let one = m.one();
let minus_one = m.minus_one();

for _a in BASIS.iter() {
for _a in A::BASIS.iter() {
let _a = _a % n;
if _a == 0 {
break;
continue;
}

let a = m.from_u64(_a);
Expand Down Expand Up @@ -87,48 +100,113 @@ pub(crate) fn test<A: Arithmetic>(m: A) -> Result {
// Used by build.rs' tests and debug assertions
#[allow(dead_code)]
pub(crate) fn is_prime(n: u64) -> bool {
if n % 2 == 0 {
if n < 2 {
false
} else if n % 2 == 0 {
n == 2
} else {
test::<Montgomery>(Montgomery::new(n)).is_prime()
test::<Montgomery<u64>>(Montgomery::new(n)).is_prime()
}
}

#[cfg(test)]
mod tests {
use super::is_prime;
use super::*;
use crate::numeric::{Arithmetic, Montgomery};
use quickcheck::quickcheck;
use std::iter;
const LARGEST_U64_PRIME: u64 = 0xFFFFFFFFFFFFFFC5;

fn primes() -> impl Iterator<Item = u64> {
iter::once(2).chain(odd_primes())
}

fn odd_primes() -> impl Iterator<Item = u64> {
use crate::table::{NEXT_PRIME, P_INVS_U64};
P_INVS_U64
.iter()
.map(|(p, _, _)| *p)
.chain(iter::once(NEXT_PRIME))
}

#[test]
fn largest_prime() {
assert!(is_prime(LARGEST_U64_PRIME));
}

#[test]
fn first_primes() {
use crate::table::{NEXT_PRIME, P_INVS_U64};
for (p, _, _) in P_INVS_U64.iter() {
assert!(is_prime(*p), "{} reported composite", p);
fn largest_composites() {
for i in LARGEST_U64_PRIME + 1..=u64::MAX {
assert!(!is_prime(i), "2⁶⁴ - {} reported prime", u64::MAX - i + 1);
}
assert!(is_prime(NEXT_PRIME));
}

#[test]
fn two() {
assert!(is_prime(2));
}

// TODO: Deduplicate with macro in numeric.rs
macro_rules! parametrized_check {
( $f:ident ) => {
paste::item! {
#[test]
fn [< $f _ u32 >]() {
$f::<Montgomery<u32>>()
}
#[test]
fn [< $f _ u64 >]() {
$f::<Montgomery<u64>>()
}
}
};
}

fn first_primes<A: Arithmetic + Basis>() {
for p in odd_primes() {
assert!(test(A::new(p)).is_prime(), "{} reported composite", p);
}
}
parametrized_check!(first_primes);

#[test]
fn one() {
assert!(!is_prime(1));
}
#[test]
fn zero() {
assert!(!is_prime(0));
}

fn first_composites<A: Arithmetic + Basis>() {
for (p, q) in primes().zip(odd_primes()) {
for i in p + 1..q {
assert!(!is_prime(i), "{} reported prime", i);
}
}
}
parametrized_check!(first_composites);

#[test]
fn issue_1556() {
// 10 425 511 = 2441 × 4271
assert!(!is_prime(10_425_511));
}

#[test]
fn small_composites() {
use crate::table::P_INVS_U64;

for i in 0..P_INVS_U64.len() {
let (p, _, _) = P_INVS_U64[i];
for (q, _, _) in &P_INVS_U64[0..i] {
fn small_semiprimes<A: Arithmetic + Basis>() {
for p in odd_primes() {
for q in odd_primes().take_while(|q| *q <= p) {
let n = p * q;
assert!(!is_prime(n), "{} = {} × {} reported prime", n, p, q);
let m = A::new(n);
assert!(!test(m).is_prime(), "{} = {} × {} reported prime", n, p, q);
}
}
}
parametrized_check!(small_semiprimes);

quickcheck! {
fn composites(i: u64, j: u64) -> bool {
i < 2 || j < 2 || !is_prime(i*j)
}
}
}
Loading