1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-31 04:57:45 +00:00

factor::numeric: Generalise modular inverse computation

This commit is contained in:
nicoo 2020-06-23 12:09:07 +02:00
parent bb01e67521
commit 43ee92c40f
4 changed files with 50 additions and 18 deletions

3
Cargo.lock generated
View file

@ -1,3 +1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]] [[package]]
name = "advapi32-sys" name = "advapi32-sys"
version = "0.2.0" version = "0.2.0"
@ -1313,6 +1315,7 @@ dependencies = [
name = "uu_factor" name = "uu_factor"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"uucore 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)", "uucore 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)",
"uucore_procs 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)", "uucore_procs 0.0.4 (git+https://github.com/uutils/uucore.git?branch=canary)",

View file

@ -14,7 +14,11 @@ edition = "2018"
[lib] [lib]
path = "src/factor.rs" path = "src/factor.rs"
[build-dependencies]
num-traits="0.2"
[dependencies] [dependencies]
num-traits = "0.2"
rand = "0.5" rand = "0.5"
uucore = { version="0.0.4", package="uucore", git="https://github.com/uutils/uucore.git", branch="canary" } 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" } uucore_procs = { version="0.0.4", package="uucore_procs", git="https://github.com/uutils/uucore.git", branch="canary" }

View file

@ -29,7 +29,7 @@ use miller_rabin::is_prime;
#[path = "src/numeric.rs"] #[path = "src/numeric.rs"]
mod numeric; mod numeric;
use numeric::inv_mod_u64; use numeric::modular_inverse;
mod sieve; mod sieve;
@ -57,7 +57,7 @@ fn main() {
let mut x = primes.next().unwrap(); let mut x = primes.next().unwrap();
for next in primes { for next in primes {
// format the table // 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 { if cols + outstr.len() > MAX_WIDTH {
write!(file, "\n {}", outstr).unwrap(); write!(file, "\n {}", outstr).unwrap();
cols = 4 + outstr.len(); cols = 4 + outstr.len();

View file

@ -6,6 +6,11 @@
// * For the full copyright and license information, please view the LICENSE file // * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code. // * that was distributed with this source code.
use num_traits::{
int::PrimInt,
ops::wrapping::{WrappingMul, WrappingSub},
};
use std::fmt::Debug;
use std::mem::swap; use std::mem::swap;
// This is incorrectly reported as dead code, // This is incorrectly reported as dead code,
@ -100,7 +105,7 @@ impl Arithmetic for Montgomery {
type I = u64; type I = u64;
fn new(n: u64) -> Self { fn new(n: u64) -> Self {
let a = inv_mod_u64(n).wrapping_neg(); let a = modular_inverse(n).wrapping_neg();
debug_assert_eq!(n.wrapping_mul(a), 1_u64.wrapping_neg()); debug_assert_eq!(n.wrapping_mul(a), 1_u64.wrapping_neg());
Montgomery { a, n } Montgomery { a, n }
} }
@ -172,35 +177,42 @@ impl Arithmetic for Montgomery {
} }
} }
pub(crate) trait Int: Debug + PrimInt + WrappingSub + WrappingMul {}
impl Int for u64 {}
impl Int for u32 {}
// extended Euclid algorithm // extended Euclid algorithm
// precondition: a is odd // precondition: a is odd
pub(crate) fn inv_mod_u64(a: u64) -> u64 { pub(crate) fn modular_inverse<T: Int>(a: T) -> T {
assert!(a % 2 == 1, "{} is not odd", a); let zero = T::zero();
let mut t = 0u64; let one = T::one();
let mut newt = 1u64; assert!(a % (one + one) == one, "{:?} is not odd", a);
let mut r = 0u64;
let mut t = zero;
let mut newt = one;
let mut r = zero;
let mut newr = a; let mut newr = a;
while newr != 0 { while newr != zero {
let quot = if r == 0 { let quot = if r == zero {
// special case when we're just starting out // special case when we're just starting out
// This works because we know that // This works because we know that
// a does not divide 2^64, so floor(2^64 / a) == floor((2^64-1) / a); // a does not divide 2^64, so floor(2^64 / a) == floor((2^64-1) / a);
std::u64::MAX T::max_value()
} else { } else {
r r
} / newr; } / newr;
let newtp = t.wrapping_sub(quot.wrapping_mul(newt)); let newtp = t.wrapping_sub(&quot.wrapping_mul(&newt));
t = newt; t = newt;
newt = newtp; newt = newtp;
let newrp = r.wrapping_sub(quot.wrapping_mul(newr)); let newrp = r.wrapping_sub(&quot.wrapping_mul(&newr));
r = newr; r = newr;
newr = newrp; newr = newrp;
} }
assert_eq!(r, 1); assert_eq!(r, one);
t t
} }
@ -208,12 +220,25 @@ pub(crate) fn inv_mod_u64(a: u64) -> u64 {
mod tests { mod tests {
use super::*; use super::*;
#[test] fn test_inverter<T: Int>() {
fn test_inverter() {
// All odd integers from 1 to 20 000 // All odd integers from 1 to 20 000
let mut test_values = (0..10_000u64).map(|i| 2 * i + 1); let one = T::from(1).unwrap();
let two = T::from(2).unwrap();
let mut test_values = (0..10_000)
.map(|i| T::from(i).unwrap())
.map(|i| two * i + one);
assert!(test_values.all(|x| x.wrapping_mul(inv_mod_u64(x)) == 1)); assert!(test_values.all(|x| x.wrapping_mul(&modular_inverse(x)) == one));
}
#[test]
fn test_inverter_u32() {
test_inverter::<u32>()
}
#[test]
fn test_inverter_u64() {
test_inverter::<u64>()
} }
#[test] #[test]