1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

factor::numeric: Replace lose functions with an Arithmetic trait

This commit is contained in:
nicoo 2020-05-24 18:16:21 +02:00
parent 29eb8fd77b
commit 30fd6a0309
3 changed files with 113 additions and 95 deletions

View file

@ -20,7 +20,7 @@ impl Result {
// Deterministic Miller-Rabin primality-checking algorithm, adapted to extract // Deterministic Miller-Rabin primality-checking algorithm, adapted to extract
// (some) dividers; it will fail to factor strong pseudoprimes. // (some) dividers; it will fail to factor strong pseudoprimes.
pub(crate) fn test(n: u64) -> Result { pub(crate) fn test<A: Arithmetic>(n: u64) -> Result {
use self::Result::*; use self::Result::*;
if n < 2 { if n < 2 {
@ -32,28 +32,22 @@ pub(crate) fn test(n: u64) -> Result {
let r = (n - 1) >> (n - 1).trailing_zeros(); let r = (n - 1) >> (n - 1).trailing_zeros();
let mul = if n < 1 << 63 {
sm_mul as fn(u64, u64, u64) -> u64
} else {
big_mul as fn(u64, u64, u64) -> u64
};
for a in BASIS.iter() { for a in BASIS.iter() {
let mut x = a % n; let mut x = a % n;
if x == 0 { if x == 0 {
break; break;
} }
if pow(x, n - 1, n, mul) != 1 { if A::pow(x, n - 1, n) != 1 {
return Pseudoprime; return Pseudoprime;
} }
x = pow(x, r, n, mul); x = A::pow(x, r, n);
if x == 1 || x == n - 1 { if x == 1 || x == n - 1 {
break; break;
} }
loop { loop {
let y = mul(x, x, n); let y = A::mul(x, x, n);
if y == 1 { if y == 1 {
return Composite(gcd(x - 1, n)); return Composite(gcd(x - 1, n));
} }
@ -72,5 +66,10 @@ pub(crate) fn test(n: u64) -> Result {
// Used by build.rs' tests // Used by build.rs' tests
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn is_prime(n: u64) -> bool { pub(crate) fn is_prime(n: u64) -> bool {
test(n).is_prime() if n < 1 << 63 {
test::<Small>(n)
} else {
test::<Big>(n)
}
.is_prime()
} }

View file

@ -21,7 +21,27 @@ pub fn gcd(mut a: u64, mut b: u64) -> u64 {
a a
} }
pub fn big_add(a: u64, b: u64, m: u64) -> u64 { pub(crate) trait Arithmetic {
fn add(a: u64, b: u64, modulus: u64) -> u64;
fn mul(a: u64, b: u64, modulus: u64) -> u64;
fn pow(mut a: u64, mut b: u64, m: u64) -> u64 {
let mut result = 1;
while b > 0 {
if b & 1 != 0 {
result = Self::mul(result, a, m);
}
a = Self::mul(a, a, m);
b >>= 1;
}
result
}
}
pub(crate) struct Big {}
impl Arithmetic for Big {
fn add(a: u64, b: u64, m: u64) -> u64 {
let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1); let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1);
let msb_mod_m = msb_mod_m % m; let msb_mod_m = msb_mod_m % m;
@ -33,23 +53,9 @@ pub fn big_add(a: u64, b: u64, m: u64) -> u64 {
} }
} }
// computes (a + b) % m using the russian peasant algorithm
// CAUTION: Will overflow if m >= 2^63
pub fn sm_mul(mut a: u64, mut b: u64, m: u64) -> u64 {
let mut result = 0;
while b > 0 {
if b & 1 != 0 {
result = (result + a) % m;
}
a = (a << 1) % m;
b >>= 1;
}
result
}
// computes (a + b) % m using the russian peasant algorithm // computes (a + b) % m using the russian peasant algorithm
// Only necessary when m >= 2^63; otherwise, just wastes time. // Only necessary when m >= 2^63; otherwise, just wastes time.
pub fn big_mul(mut a: u64, mut b: u64, m: u64) -> u64 { fn mul(mut a: u64, mut b: u64, m: u64) -> u64 {
// precompute 2^64 mod m, since we expect to wrap // precompute 2^64 mod m, since we expect to wrap
let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1); let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1);
let msb_mod_m = msb_mod_m % m; let msb_mod_m = msb_mod_m % m;
@ -76,16 +82,26 @@ pub fn big_mul(mut a: u64, mut b: u64, m: u64) -> u64 {
} }
result result
} }
}
// computes a.pow(b) % m pub(crate) struct Small {}
pub(crate) fn pow(mut a: u64, mut b: u64, m: u64, mul: fn(u64, u64, u64) -> u64) -> u64 {
let mut result = 1; impl Arithmetic for Small {
// computes (a + b) % m using the russian peasant algorithm
// CAUTION: Will overflow if m >= 2^63
fn mul(mut a: u64, mut b: u64, m: u64) -> u64 {
let mut result = 0;
while b > 0 { while b > 0 {
if b & 1 != 0 { if b & 1 != 0 {
result = mul(result, a, m); result = (result + a) % m;
} }
a = mul(a, a, m); a = (a << 1) % m;
b >>= 1; b >>= 1;
} }
result result
} }
fn add(a: u64, b: u64, m: u64) -> u64 {
(a + b) % m
}
}

View file

@ -6,39 +6,34 @@ use rand::rngs::SmallRng;
use rand::{thread_rng, SeedableRng}; use rand::{thread_rng, SeedableRng};
use std::cmp::{max, min}; use std::cmp::{max, min};
fn pseudorandom_function(x: u64, a: u64, b: u64, num: u64) -> u64 { fn find_divisor<A: Arithmetic>(n: u64) -> u64 {
if num < 1 << 63 {
(sm_mul(a, sm_mul(x, x, num), num) + b) % num
} else {
big_add(big_mul(a, big_mul(x, x, num), num), b, num)
}
}
fn find_divisor(num: u64) -> u64 {
#![allow(clippy::many_single_char_names)] #![allow(clippy::many_single_char_names)]
let range = Uniform::new(1, num); let mut rand = {
let range = Uniform::new(1, n);
let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap(); let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
let mut x = range.sample(&mut rng); move || range.sample(&mut rng)
let mut y = x; };
let mut a = range.sample(&mut rng);
let mut b = range.sample(&mut rng); let quadratic = |a, b| move |x| A::add(A::mul(a, A::mul(x, x, n), n), b, n);
loop { loop {
x = pseudorandom_function(x, a, b, num); let f = quadratic(rand(), rand());
y = pseudorandom_function(y, a, b, num); let mut x = rand();
y = pseudorandom_function(y, a, b, num); let mut y = x;
let d = gcd(num, max(x, y) - min(x, y));
if d == num { loop {
// Failure, retry with different function x = f(x);
x = range.sample(&mut rng); y = f(f(y));
y = x; let d = gcd(n, max(x, y) - min(x, y));
a = range.sample(&mut rng); if d == n {
b = range.sample(&mut rng); // Failure, retry with a different quadratic
break;
} else if d > 1 { } else if d > 1 {
return d; return d;
} }
} }
} }
}
pub(crate) fn factor(mut num: u64) -> Factors { pub(crate) fn factor(mut num: u64) -> Factors {
let mut factors = Factors::new(); let mut factors = Factors::new();
@ -46,7 +41,11 @@ pub(crate) fn factor(mut num: u64) -> Factors {
return factors; return factors;
} }
match miller_rabin::test(num) { match if num < 1 << 63 {
miller_rabin::test::<Small>(num)
} else {
miller_rabin::test::<Big>(num)
} {
Prime => { Prime => {
factors.push(num); factors.push(num);
return factors; return factors;
@ -60,7 +59,11 @@ pub(crate) fn factor(mut num: u64) -> Factors {
Pseudoprime => {} Pseudoprime => {}
}; };
let divisor = find_divisor(num); let divisor = if num < 1 << 63 {
find_divisor::<Small>(num)
} else {
find_divisor::<Big>(num)
};
factors *= factor(divisor); factors *= factor(divisor);
factors *= factor(num / divisor); factors *= factor(num / divisor);
factors factors