mirror of
https://github.com/uutils/coreutils.git
synced 2025-12-23 08:47:37 +00:00
This change does the following: 1. Updates the arithmetic functions in `src/factor/numeric.rs` to correctly handle all cases up to 2^64. When numbers are larger than 2^63, we fall back to slightly slower routines that check for and handle overflow. 2. Since the arithmetic functions will now not overflow, we no longer need the safety net trial division implementation. We now always use Pollard's rho after eliminating small (<=13 bit) primes. 3. Slight tweak in `src/factor/gen_table.rs` to generate the first 1027 primes, which means we test every prime of 13 or fewer bits before going into Pollard's rho. Includes corresponding update in `src/factor/prime_table.rs` and the Makefile to reflect this. 4. Add a new test that generates random numbers with exclusively large (14 to 50 bit) prime factors. This exercises the possible overflow paths. 5. Add another new test that checks the `is_prime()` function against a few dozen 64-bit primes. Again this is to exercise possible overflow paths.
132 lines
3.3 KiB
Rust
132 lines
3.3 KiB
Rust
/*
|
|
* This file is part of the uutils coreutils package.
|
|
*
|
|
* (c) Wiktor Kuropatwa <wiktor.kuropatwa@gmail.com>
|
|
* (c) kwantam <kwantam@gmail.com>
|
|
* 20150507 added big_ routines to prevent overflow when num > 2^63
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE file
|
|
* that was distributed with this source code.
|
|
*/
|
|
|
|
use std::u64::MAX as MAX_U64;
|
|
use std::num::Wrapping;
|
|
|
|
pub fn big_add(a: u64, b: u64, m: u64) -> u64 {
|
|
let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1);
|
|
let msb_mod_m = msb_mod_m % m;
|
|
|
|
let Wrapping(res) = Wrapping(a) + Wrapping(b);
|
|
let res = if b <= MAX_U64 - a {
|
|
res
|
|
} else {
|
|
(res + msb_mod_m) % m
|
|
};
|
|
|
|
res
|
|
}
|
|
|
|
// 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
|
|
// Only necessary when m >= 2^63; otherwise, just wastes time.
|
|
pub fn big_mul(mut a: u64, mut b: u64, m: u64) -> u64 {
|
|
// precompute 2^64 mod m, since we expect to wrap
|
|
let Wrapping(msb_mod_m) = Wrapping(MAX_U64) - Wrapping(m) + Wrapping(1);
|
|
let msb_mod_m = msb_mod_m % m;
|
|
|
|
let mut result = 0;
|
|
while b > 0 {
|
|
if b & 1 != 0 {
|
|
let Wrapping(next_res) = Wrapping(result) + Wrapping(a);
|
|
let next_res = next_res % m;
|
|
result = if result <= MAX_U64 - a {
|
|
next_res
|
|
} else {
|
|
(next_res + msb_mod_m) % m
|
|
};
|
|
}
|
|
let Wrapping(next_a) = Wrapping(a) << 1;
|
|
let next_a = next_a % m;
|
|
a = if a < 1 << 63 {
|
|
next_a
|
|
} else {
|
|
(next_a + msb_mod_m) % m
|
|
};
|
|
b >>= 1;
|
|
}
|
|
result
|
|
}
|
|
|
|
// computes a.pow(b) % m
|
|
fn pow(mut a: u64, mut b: u64, m: u64, mul: fn(u64, u64, u64) -> u64) -> u64 {
|
|
let mut result = 1;
|
|
while b > 0 {
|
|
if b & 1 != 0 {
|
|
result = mul(result, a, m);
|
|
}
|
|
a = mul(a, a, m);
|
|
b >>= 1;
|
|
}
|
|
result
|
|
}
|
|
|
|
fn witness(mut a: u64, exponent: u64, m: u64) -> bool {
|
|
if a == 0 {
|
|
return false;
|
|
}
|
|
|
|
let mul = if m < 1 << 63 {
|
|
sm_mul as fn(u64, u64, u64) -> u64
|
|
} else {
|
|
big_mul as fn(u64, u64, u64) -> u64
|
|
};
|
|
|
|
if pow(a, m-1, m, mul) != 1 {
|
|
return true;
|
|
}
|
|
a = pow(a, exponent, m, mul);
|
|
if a == 1 {
|
|
return false;
|
|
}
|
|
loop {
|
|
if a == 1 {
|
|
return true;
|
|
}
|
|
if a == m-1 {
|
|
return false;
|
|
}
|
|
a = mul(a, a, m);
|
|
}
|
|
}
|
|
|
|
// uses deterministic (i.e., fixed witness set) Miller-Rabin test
|
|
pub fn is_prime(num: u64) -> bool {
|
|
if num < 2 {
|
|
return false;
|
|
}
|
|
if num % 2 == 0 {
|
|
return num == 2;
|
|
}
|
|
let mut exponent = num - 1;
|
|
while exponent & 1 == 0 {
|
|
exponent >>= 1;
|
|
}
|
|
|
|
// These witnesses detect all composites up to at least 2^64.
|
|
// Discovered by Jim Sinclair, according to http://miller-rabin.appspot.com
|
|
let witnesses = [2, 325, 9375, 28178, 450775, 9780504, 1795265022];
|
|
! witnesses.iter().any(|&wit| witness(wit % num, exponent, num))
|
|
}
|