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

factor: Split numeric.rs into multiple modules (#1567)

* factor::numeric: Start refactoring into multiple submodules

No change to the module's interface, but it should make it much easier to
keep the tests right next to the code they are related to.

Moreover, build.rs' dependency is now limited to numeric::{modular_inverse,
traits}, meaning that the rest of it can use build-time generated tables etc.

* factor::numeric: Move gcd (and its test) to a submodule

* factor::numeric: Move Montgomery arithmetic to its own module

Finally hollowed-out numeric.rs

* factor: Move numeric.rs to numeric/mod.rs

* factor::numeric: Fix an erroneous lint on obsolete Rust versions
This commit is contained in:
nicoo 2020-08-02 20:28:00 +02:00 committed by GitHub
parent 85e2e1d0ee
commit 1eabda91cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 262 additions and 204 deletions

View file

@ -27,9 +27,11 @@ use self::sieve::Sieve;
#[cfg(test)] #[cfg(test)]
use miller_rabin::is_prime; use miller_rabin::is_prime;
#[path = "src/numeric.rs"] #[path = "src/numeric/modular_inverse.rs"]
mod numeric; mod modular_inverse;
use numeric::modular_inverse; #[path = "src/numeric/traits.rs"]
mod traits;
use modular_inverse::modular_inverse;
mod sieve; mod sieve;

View file

@ -0,0 +1,67 @@
// * This file is part of the uutils coreutils package.
// *
// * (c) 2015 Wiktor Kuropatwa <wiktor.kuropatwa@gmail.com>
// * (c) 2020 nicoo <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.
use std::cmp::min;
use std::mem::swap;
pub fn gcd(mut n: u64, mut m: u64) -> u64 {
// Stein's binary GCD algorithm
// Base cases: gcd(n, 0) = gcd(0, n) = n
if n == 0 {
return m;
} else if m == 0 {
return n;
}
// Extract common factor-2: gcd(2ⁱ n, 2ⁱ m) = 2ⁱ gcd(n, m)
// and reducing until odd gcd(2ⁱ n, m) = gcd(n, m) if m is odd
let k = {
let k_n = n.trailing_zeros();
let k_m = m.trailing_zeros();
n >>= k_n;
m >>= k_m;
min(k_n, k_m)
};
loop {
// Invariant: n odd
debug_assert!(n % 2 == 1, "n = {} is even", n);
if n > m {
swap(&mut n, &mut m);
}
m -= n;
if m == 0 {
return n << k;
}
m >>= m.trailing_zeros();
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::quickcheck;
quickcheck! {
fn gcd(a: u64, b: u64) -> bool {
// Test against the Euclidean algorithm
let g = {
let (mut a, mut b) = (a, b);
while b > 0 {
a %= b;
swap(&mut a, &mut b);
}
a
};
super::gcd(a, b) == g
}
}
}

View file

@ -0,0 +1,17 @@
// * This file is part of the uutils coreutils package.
// *
// * (c) 2020 nicoo <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.
mod gcd;
pub use gcd::gcd;
mod traits;
mod modular_inverse;
pub(crate) use modular_inverse::modular_inverse;
mod montgomery;
pub(crate) use montgomery::{Arithmetic, Montgomery};

View file

@ -0,0 +1,62 @@
// * This file is part of the uutils coreutils package.
// *
// * (c) 2015 Wiktor Kuropatwa <wiktor.kuropatwa@gmail.com>
// * (c) 2020 nicoo <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.
use super::traits::Int;
// extended Euclid algorithm
// precondition: a is odd
pub(crate) fn modular_inverse<T: Int>(a: T) -> T {
let zero = T::zero();
let one = T::one();
debug_assert!(a % (one + one) == one, "{:?} is not odd", a);
let mut t = zero;
let mut newt = one;
let mut r = zero;
let mut newr = a;
while newr != zero {
let quot = if r == zero {
// 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);
T::max_value()
} else {
r
} / newr;
let newtp = t.wrapping_sub(&quot.wrapping_mul(&newt));
t = newt;
newt = newtp;
let newrp = r.wrapping_sub(&quot.wrapping_mul(&newr));
r = newr;
newr = newrp;
}
debug_assert_eq!(r, one);
t
}
#[cfg(test)]
mod tests {
use super::{super::traits::Int, *};
use crate::parametrized_check;
fn test_inverter<T: Int>() {
// All odd integers from 1 to 20 000
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(&modular_inverse(x)) == one));
}
parametrized_check!(test_inverter);
}

View file

@ -1,58 +1,14 @@
// * This file is part of the uutils coreutils package. // * This file is part of the uutils coreutils package.
// * // *
// * (c) 2015 Wiktor Kuropatwa <wiktor.kuropatwa@gmail.com> // * (c) 2020 Alex Lyon <arcterus@mail.com>
// * (c) 2020 nicoo <nicoo@debian.org> // * (c) 2020 nicoo <nicoo@debian.org>
// * // *
// * 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::{ use super::traits::{DoubleInt, Int, OverflowingAdd};
identities::{One, Zero}, use super::*;
int::PrimInt, use num_traits::identities::{One, Zero};
ops::wrapping::{WrappingMul, WrappingNeg, WrappingSub},
};
use std::cmp::min;
use std::fmt::{Debug, Display};
use std::mem::swap;
// This is incorrectly reported as dead code,
// presumably when included in build.rs.
#[allow(dead_code)]
pub fn gcd(mut n: u64, mut m: u64) -> u64 {
// Stein's binary GCD algorithm
// Base cases: gcd(n, 0) = gcd(0, n) = n
if n == 0 {
return m;
} else if m == 0 {
return n;
}
// Extract common factor-2: gcd(2ⁱ n, 2ⁱ m) = 2ⁱ gcd(n, m)
// and reducing until odd gcd(2ⁱ n, m) = gcd(n, m) if m is odd
let k = {
let k_n = n.trailing_zeros();
let k_m = m.trailing_zeros();
n >>= k_n;
m >>= k_m;
min(k_n, k_m)
};
loop {
// Invariant: n odd
debug_assert!(n % 2 == 1, "n = {} is even", n);
if n > m {
swap(&mut n, &mut m);
}
m -= n;
if m == 0 {
return n << k;
}
m >>= m.trailing_zeros();
}
}
pub(crate) trait Arithmetic: Copy + Sized { pub(crate) trait Arithmetic: Copy + Sized {
// The type of integers mod m, in some opaque representation // The type of integers mod m, in some opaque representation
@ -223,161 +179,10 @@ impl<T: DoubleInt> Arithmetic for Montgomery<T> {
} }
} }
// NOTE: Trait can be removed once num-traits adds a similar one;
// see https://github.com/rust-num/num-traits/issues/168
pub(crate) trait OverflowingAdd: Sized {
fn overflowing_add_(self, n: Self) -> (Self, bool);
}
macro_rules! overflowing {
($x:ty) => {
impl OverflowingAdd for $x {
fn overflowing_add_(self, n: Self) -> (Self, bool) {
self.overflowing_add(n)
}
}
};
}
overflowing!(u32);
overflowing!(u64);
overflowing!(u128);
pub(crate) trait Int:
Display + Debug + PrimInt + OverflowingAdd + WrappingNeg + WrappingSub + WrappingMul
{
fn as_u64(&self) -> u64;
fn from_u64(n: u64) -> Self;
#[cfg(debug_assertions)]
fn as_u128(&self) -> u128;
}
pub(crate) trait DoubleInt: Int {
/// An integer type with twice the width of `Self`.
/// In particular, multiplications (of `Int` values) can be performed in
/// `Self::DoubleWidth` without possibility of overflow.
type DoubleWidth: Int;
fn as_double_width(self) -> Self::DoubleWidth;
fn from_double_width(n: Self::DoubleWidth) -> Self;
}
macro_rules! int {
( $x:ty ) => {
impl Int for $x {
fn as_u64(&self) -> u64 {
*self as u64
}
fn from_u64(n: u64) -> Self {
n as _
}
#[cfg(debug_assertions)]
fn as_u128(&self) -> u128 {
*self as u128
}
}
};
}
macro_rules! double_int {
( $x:ty, $y:ty ) => {
int!($x);
impl DoubleInt for $x {
type DoubleWidth = $y;
fn as_double_width(self) -> $y {
self as _
}
fn from_double_width(n: $y) -> $x {
n as _
}
}
};
}
double_int!(u32, u64);
double_int!(u64, u128);
int!(u128);
// extended Euclid algorithm
// precondition: a is odd
pub(crate) fn modular_inverse<T: Int>(a: T) -> T {
let zero = T::zero();
let one = T::one();
debug_assert!(a % (one + one) == one, "{:?} is not odd", a);
let mut t = zero;
let mut newt = one;
let mut r = zero;
let mut newr = a;
while newr != zero {
let quot = if r == zero {
// 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);
T::max_value()
} else {
r
} / newr;
let newtp = t.wrapping_sub(&quot.wrapping_mul(&newt));
t = newt;
newt = newtp;
let newrp = r.wrapping_sub(&quot.wrapping_mul(&newr));
r = newr;
newr = newrp;
}
debug_assert_eq!(r, one);
t
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use quickcheck::quickcheck; use crate::parametrized_check;
quickcheck! {
fn gcd(a: u64, b: u64) -> bool {
// Test against the Euclidean algorithm
let g = {
let (mut a, mut b) = (a, b);
while b > 0 {
a %= b;
swap(&mut a, &mut b);
}
a
};
super::gcd(a, b) == g
}
}
macro_rules! parametrized_check {
( $f:ident ) => {
paste::item! {
#[test]
fn [< $f _ u32 >]() {
$f::<u32>()
}
#[test]
fn [< $f _ u64 >]() {
$f::<u64>()
}
}
};
}
fn test_inverter<T: Int>() {
// All odd integers from 1 to 20 000
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(&modular_inverse(x)) == one));
}
parametrized_check!(test_inverter);
fn test_add<A: DoubleInt>() { fn test_add<A: DoubleInt>() {
for n in 0..100 { for n in 0..100 {

View file

@ -0,0 +1,105 @@
// * This file is part of the uutils coreutils package.
// *
// * (c) 2020 Alex Lyon <arcterus@mail.com>
// * (c) 2020 nicoo <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.
use num_traits::{
int::PrimInt,
ops::wrapping::{WrappingMul, WrappingNeg, WrappingSub},
};
use std::fmt::{Debug, Display};
// NOTE: Trait can be removed once num-traits adds a similar one;
// see https://github.com/rust-num/num-traits/issues/168
pub(crate) trait OverflowingAdd: Sized {
fn overflowing_add_(self, n: Self) -> (Self, bool);
}
macro_rules! overflowing {
($x:ty) => {
impl OverflowingAdd for $x {
fn overflowing_add_(self, n: Self) -> (Self, bool) {
self.overflowing_add(n)
}
}
};
}
overflowing!(u32);
overflowing!(u64);
overflowing!(u128);
pub(crate) trait Int:
Display + Debug + PrimInt + OverflowingAdd + WrappingNeg + WrappingSub + WrappingMul
{
fn as_u64(&self) -> u64;
fn from_u64(n: u64) -> Self;
#[cfg(debug_assertions)]
fn as_u128(&self) -> u128;
}
pub(crate) trait DoubleInt: Int {
/// An integer type with twice the width of `Self`.
/// In particular, multiplications (of `Int` values) can be performed in
/// `Self::DoubleWidth` without possibility of overflow.
type DoubleWidth: Int;
fn as_double_width(self) -> Self::DoubleWidth;
fn from_double_width(n: Self::DoubleWidth) -> Self;
}
macro_rules! int {
( $x:ty ) => {
impl Int for $x {
fn as_u64(&self) -> u64 {
*self as u64
}
fn from_u64(n: u64) -> Self {
n as _
}
#[cfg(debug_assertions)]
fn as_u128(&self) -> u128 {
*self as u128
}
}
};
}
macro_rules! double_int {
( $x:ty, $y:ty ) => {
int!($x);
impl DoubleInt for $x {
type DoubleWidth = $y;
fn as_double_width(self) -> $y {
self as _
}
fn from_double_width(n: $y) -> $x {
n as _
}
}
};
}
double_int!(u32, u64);
double_int!(u64, u128);
int!(u128);
/// Helper macro for instantiating tests over u32 and u64
#[cfg(test)]
#[macro_export]
macro_rules! parametrized_check {
( $f:ident ) => {
paste::item! {
#[test]
fn [< $f _ u32 >]() {
$f::<u32>()
}
#[test]
fn [< $f _ u64 >]() {
$f::<u64>()
}
}
};
}