1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2026-01-15 01:31:08 +00:00
uutils-coreutils/src/factor/gen_table.rs
kwantam ff24d48e73 modify factor impl to eliminate overflow issue
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.
2015-05-08 00:06:35 -04:00

126 lines
3.4 KiB
Rust

/*
* This file is part of the uutils coreutils package.
*
* (c) kwantam <kwantam@gmail.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/
//! Generate a table of the multiplicative inverses of p_i mod 2^64
//! for the first 1027 odd primes (all 13 bit and smaller primes).
//! You can supply a commandline argument to override the default
//! value of 1027 for the number of entries in the table.
//!
//! 2 has no multiplicative inverse mode 2^64 because 2 | 2^64,
//! and in any case divisibility by two is trivial by checking the LSB.
use sieve::Sieve;
use std::env::args;
use std::num::Wrapping;
use std::u64::MAX as MAX_U64;
#[cfg(test)]
use numeric::is_prime;
#[cfg(test)]
mod numeric;
mod sieve;
// extended Euclid algorithm
// precondition: a does not divide 2^64
fn inv_mod_u64(a: u64) -> Option<u64> {
let mut t = 0u64;
let mut newt = 1u64;
let mut r = 0u64;
let mut newr = a;
while newr != 0 {
let quot = if r == 0 {
// special case when we're just starting out
// This works because we know that
// a does not divide 2^64, so floor(2^64 / a) == floor((2^64-1) / a);
MAX_U64
} else {
r
} / newr;
let (tp, Wrapping(newtp)) =
(newt, Wrapping(t) - (Wrapping(quot) * Wrapping(newt)));
t = tp;
newt = newtp;
let (rp, Wrapping(newrp)) =
(newr, Wrapping(r) - (Wrapping(quot) * Wrapping(newr)));
r = rp;
newr = newrp;
}
if r > 1 { // not invertible
return None;
}
Some(t)
}
#[cfg_attr(test, allow(dead_code))]
fn main() {
// By default, we print the multiplicative inverses mod 2^64 of the first 1k primes
let n = args().skip(1).next().unwrap_or("1027".to_string()).parse::<usize>().ok().unwrap_or(1027);
print!("{}", PREAMBLE);
let mut cols = 3;
// we want a total of n + 1 values
let mut primes = Sieve::new().take(n + 1);
// in each iteration of the for loop, we use the value yielded
// by the previous iteration. This leaves one value left at the
// end, which we call NEXT_PRIME.
let mut x = primes.next().unwrap();
for next in primes {
// format the table
let outstr = format!("({}, {}, {}),", x, inv_mod_u64(x).unwrap(), MAX_U64 / x);
if cols + outstr.len() > MAX_WIDTH {
print!("\n {}", outstr);
cols = 4 + outstr.len();
} else {
print!(" {}", outstr);
cols += 1 + outstr.len();
}
x = next;
}
print!("\n];\n\n#[allow(dead_code)]\npub const NEXT_PRIME: u64 = {};\n", x);
}
#[test]
fn test_generator_and_inverter() {
let num = 10000;
let invs = Sieve::new().map(|x| inv_mod_u64(x).unwrap());
assert!(Sieve::new().zip(invs).take(num).all(|(x, y)| {
let Wrapping(z) = Wrapping(x) * Wrapping(y);
is_prime(x) && z == 1
}));
}
const MAX_WIDTH: usize = 102;
const PREAMBLE: &'static str =
r##"/*
* This file is part of the uutils coreutils package.
*
* (c) kwantam <kwantam@gmail.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/
// *** NOTE: this file was automatically generated.
// Please do not edit by hand. Instead, modify and
// re-run src/factor/gen_tables.rs.
pub const P_INVS_U64: &'static [(u64, u64, u64)] = &[
"##;