This commit is contained in:
goldenMetteyya 2019-04-27 03:02:56 +03:00
commit a977d93720
12 changed files with 710 additions and 579 deletions

View File

@ -7,7 +7,7 @@ keywords = ["mathematics", "numerics", "bignum"]
categories = [ "algorithms", "data-structures", "science" ]
license = "MIT/Apache-2.0"
name = "num-bigint-dig"
repository = "https://github.com/dignifiedquier/num-bigint"
repository = "https://github.com/dignifiedquire/num-bigint"
version = "0.3.1-alpha.0"
readme = "README.md"
build = "build.rs"
@ -21,6 +21,8 @@ harness = false
name = "bench_main"
required-features = ["prime"]
[dependencies]
[dependencies.smallvec]
version = "0.6.7"
default-features = false
@ -37,22 +39,22 @@ default-features = false
version = "0.1.37"
default-features = false
[dependencies.rand]
optional = true
version = "0.5"
version = "0.6"
default-features = false
features = ["std"]
[dependencies.zeroize]
version = "0.6"
optional = true
[dependencies.serde]
optional = true
version = "1.0"
default-features = false
features = ["std"]
[dev-dependencies.serde_test]
version = "1.0"
[dependencies.lazy_static]
version = "1.2.0"
@ -62,10 +64,16 @@ version = "1.2.7"
[dev-dependencies]
criterion = "0.2"
rand_chacha = "0.1"
rand_xorshift = "0.1"
rand_isaac = "0.1"
[dev-dependencies.serde_test]
version = "1.0"
[features]
default = ["std", "i128", "u64_digit"]
i128 = ["num-integer/i128", "num-traits/i128"]
std = ["num-integer/std", "num-traits/std", "smallvec/std"]
std = ["num-integer/std", "num-traits/std", "smallvec/std", "rand/std"]
u64_digit = []
prime = ["rand"]
prime = ["rand"]
nightly = ["zeroize/nightly", "rand/nightly"]

View File

@ -15,7 +15,7 @@ Add this to your `Cargo.toml`:
```toml
[dependencies]
num-bigint-dig = "0.2"
num-bigint-dig = ^0.4"
```
and this to your crate root:

View File

@ -22,7 +22,7 @@ pub fn cmp_slice(a: &[BigDigit], b: &[BigDigit]) -> Ordering {
return Greater;
}
}
return Equal;
Equal
}
#[cfg(test)]

View File

@ -11,11 +11,14 @@ use crate::biguint::{BigUint, IntDigits};
/// Uses the lehemer algorithm.
/// Based on https://github.com/golang/go/blob/master/src/math/big/int.go#L612
/// If `extended` is set, the Bezout coefficients are calculated, otherwise they are `None`.
/// If x or y are not nil, GCD sets their value such that z = a*x + b*y.
/// If either a or b is <= 0, GCD sets z = x = y = 0.
pub fn extended_gcd(
a_in: Cow<BigUint>,
b_in: Cow<BigUint>,
extended: bool,
) -> (BigInt, Option<BigInt>, Option<BigInt>) {
if a_in.is_zero() && b_in.is_zero() {
if extended {
return (b_in.to_bigint().unwrap(), Some(0.into()), Some(0.into()));
@ -39,6 +42,7 @@ pub fn extended_gcd(
return (a_in.to_bigint().unwrap(), None, None);
}
}
let a_in = a_in.to_bigint().unwrap();
let b_in = b_in.to_bigint().unwrap();
@ -56,14 +60,17 @@ pub fn extended_gcd(
std::mem::swap(&mut ua, &mut ub);
}
// temp variables for multiprecision update
let mut q: BigInt = 0.into();
let mut r: BigInt = 0.into();
let mut s: BigInt = 0.into();
let mut t: BigInt = 0.into();
// loop invariant A >= B
while b.len() > 1 {
// Attempt to calculate in single-precision using leading words of a and b.
let (u0, u1, v0, v1, even) = lehmer_simulate(&a, &b);
// multiprecision step
if v0 != 0 {
// Simulate the effect of the single-precision steps using cosequences.
@ -174,6 +181,8 @@ pub fn extended_gcd(
(a, ua, y)
}
/// Attempts to simulate several Euclidean update steps using leading digits of `a` and `b`.
/// It returns `u0`, `u1`, `v0`, `v1` such that `a` and `b` can be updated as:
/// a = u0 * a + v0 * b
@ -210,9 +219,14 @@ fn lehmer_simulate(a: &BigInt, b: &BigInt) -> (BigDigit, BigDigit, BigDigit, Big
0
};
// odd, even tracking
// Since we are calculating with full words to avoid overflow,
// we use 'even' to track the sign of the cosequences.
// For even iterations: u0, v1 >= 0 && u1, v0 <= 0
// For odd iterations: u0, v1 <= 0 && u1, v0 >= 0
// The first iteration starts with k=1 (odd).
let mut even = false;
// variables to track the cosequences
let mut u0 = 0;
let mut u1 = 1;
let mut u2 = 0;
@ -221,7 +235,10 @@ fn lehmer_simulate(a: &BigInt, b: &BigInt) -> (BigDigit, BigDigit, BigDigit, Big
let mut v1 = 0;
let mut v2 = 1;
// Calculate the quotient and cosequences using Collins' stoppting condition.
// Calculate the quotient and cosequences using Collins' stopping condition.
// Note that overflow of a Word is not possible when computing the remainder
// sequence and cosequences since the cosequence size is bounded by the input size.
// See section 4.2 of Jebelean for details.
while a2 >= v2 && a1.wrapping_sub(a2) >= v1 + v2 {
let q = a1 / a2;
let r = a1 % a2;
@ -245,6 +262,13 @@ fn lehmer_simulate(a: &BigInt, b: &BigInt) -> (BigDigit, BigDigit, BigDigit, Big
(u0, u1, v0, v1, even)
}
// lehmerUpdate updates the inputs A and B such that:
// A = u0*A + v0*B
// B = u1*A + v1*B
// where the signs of u0, u1, v0, v1 are given by even
// For even == true: u0, v1 >= 0 && u1, v0 <= 0
// For even == false: u0, v1 <= 0 && u1, v0 >= 0
// q, r, s, t are temporary variables to avoid allocations in the multiplication
fn lehmer_update(
a: &mut BigInt,
b: &mut BigInt,
@ -258,13 +282,26 @@ fn lehmer_update(
v1: BigDigit,
even: bool,
) {
t.data.set_digit(u0);
s.data.set_digit(v0);
//We handle to edge case that s or t is a zero and make sure that we dont have negative zero.
if even {
t.sign = Plus;
s.sign = Minus
if s.data.is_zero() {
s.sign = Plus;
} else {
s.sign = Minus;
}
} else {
t.sign = Minus;
// t.sign = Minus;
if t.data.is_zero() {
t.sign = Plus;
} else {
t.sign = Minus;
}
s.sign = Plus;
}
@ -288,6 +325,8 @@ fn lehmer_update(
*b = r + q;
}
// euclidUpdate performs a single step of the Euclidean GCD algorithm
// if extended is true, it also updates the cosequence Ua, Ub
fn euclid_udpate(
a: &mut BigInt,
b: &mut BigInt,

View File

@ -297,16 +297,17 @@ fn toom3(acc: &mut [BigDigit], x: &[BigDigit], y: &[BigDigit]) {
//
// Evaluate at w(t) where t is our given base to get the result.
add2(acc, r0.digits());
add2(acc, (comp1 << BITS * 1 * i).digits());
add2(acc, (comp2 << BITS * 2 * i).digits());
add2(acc, (comp3 << BITS * 3 * i).digits());
add2(acc, (r4 << BITS * 4 * i).digits());
add2(acc, (comp1 << (BITS * 1 * i)).digits());
add2(acc, (comp2 << (BITS * 2 * i)).digits());
add2(acc, (comp3 << (BITS * 3 * i)).digits());
add2(acc, (r4 << (BITS * 4 * i)).digits());
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "u64_digit")]
#[test]
fn test_mac3_regression() {
let b: Vec<BigDigit> = vec![

View File

@ -1,3 +1,5 @@
#![allow(clippy::many_single_char_names)]
mod add;
mod bits;
mod cmp;

View File

@ -1,15 +1,16 @@
#![allow(clippy::suspicious_arithmetic_impl)]
#[allow(deprecated, unused_imports)]
use std::borrow::Cow;
use std::cmp::Ordering::{self, Equal, Greater, Less};
use std::default::Default;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
use std::mem;
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
};
use std::str::{self, FromStr};
use std::{fmt, mem};
#[cfg(has_i128)]
use std::{i128, u128};
use std::{i64, u64};
@ -17,6 +18,9 @@ use std::{i64, u64};
#[cfg(feature = "serde")]
use serde;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
use integer::{Integer, Roots};
use num_traits::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive, Num, One, Pow, Signed,
@ -47,6 +51,14 @@ pub enum Sign {
Plus,
}
#[cfg(feature = "zeroize")]
impl Zeroize for Sign {
fn zeroize(&mut self) {
// TODO: Figure out how to better clear the sign.
*self = Sign::NoSign;
}
}
impl Neg for Sign {
type Output = Sign;
@ -113,7 +125,8 @@ impl<'de> serde::Deserialize<'de> for Sign {
}
/// A big signed integer type.
#[derive(Clone, Debug, Hash)]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
pub struct BigInt {
pub(crate) sign: Sign,
pub(crate) data: BigUint,
@ -144,6 +157,13 @@ impl PartialEq for BigInt {
impl Eq for BigInt {}
impl Hash for BigInt {
fn hash<H: Hasher>(&self, state: &mut H) {
self.sign.hash(state);
self.data.hash(state);
}
}
impl PartialOrd for BigInt {
#[inline]
fn partial_cmp(&self, other: &BigInt) -> Option<Ordering> {
@ -824,9 +844,7 @@ impl Signed for BigInt {
fn powsign<T: Integer>(sign: Sign, other: &T) -> Sign {
if other.is_zero() {
Plus
} else if sign != Minus {
sign
} else if other.is_odd() {
} else if sign != Minus || other.is_odd() {
sign
} else {
-sign
@ -2147,21 +2165,21 @@ impl<'a> Neg for &'a BigInt {
impl CheckedAdd for BigInt {
#[inline]
fn checked_add(&self, v: &BigInt) -> Option<BigInt> {
return Some(self.add(v));
Some(self.add(v))
}
}
impl CheckedSub for BigInt {
#[inline]
fn checked_sub(&self, v: &BigInt) -> Option<BigInt> {
return Some(self.sub(v));
Some(self.sub(v))
}
}
impl CheckedMul for BigInt {
#[inline]
fn checked_mul(&self, v: &BigInt) -> Option<BigInt> {
return Some(self.mul(v));
Some(self.mul(v))
}
}
@ -2169,9 +2187,10 @@ impl CheckedDiv for BigInt {
#[inline]
fn checked_div(&self, v: &BigInt) -> Option<BigInt> {
if v.is_zero() {
return None;
None
} else {
Some(self.div(v))
}
return Some(self.div(v));
}
}
@ -2245,7 +2264,7 @@ impl Integer for BigInt {
/// Deprecated, use `is_multiple_of` instead.
#[inline]
fn divides(&self, other: &BigInt) -> bool {
return self.is_multiple_of(other);
self.is_multiple_of(other)
}
/// Returns `true` if the number is a multiple of `other`.
@ -2698,10 +2717,7 @@ impl BigInt {
sign = NoSign;
}
BigInt {
sign: sign,
data: data,
}
BigInt { sign, data }
}
/// Creates and initializes a `BigInt`.
@ -3060,25 +3076,26 @@ impl BigInt {
#[inline]
pub fn checked_add(&self, v: &BigInt) -> Option<BigInt> {
return Some(self.add(v));
Some(self.add(v))
}
#[inline]
pub fn checked_sub(&self, v: &BigInt) -> Option<BigInt> {
return Some(self.sub(v));
Some(self.sub(v))
}
#[inline]
pub fn checked_mul(&self, v: &BigInt) -> Option<BigInt> {
return Some(self.mul(v));
Some(self.mul(v))
}
#[inline]
pub fn checked_div(&self, v: &BigInt) -> Option<BigInt> {
if v.is_zero() {
return None;
None
} else {
Some(self.div(v))
}
return Some(self.div(v));
}
/// Returns `(self ^ exponent) mod modulus`

View File

@ -1,6 +1,6 @@
//! Randomization of big integers
use rand::distributions::uniform::{SampleUniform, UniformSampler};
use rand::distributions::uniform::{SampleBorrow, SampleUniform, UniformSampler};
use rand::prelude::*;
use rand::AsByteSliceMut;
use rand::Rng;
@ -128,18 +128,29 @@ impl UniformSampler for UniformBigUint {
type X = BigUint;
#[inline]
fn new(low: Self::X, high: Self::X) -> Self {
fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = low_b.borrow();
let high = high_b.borrow();
assert!(low < high);
UniformBigUint {
len: high - &low,
base: low,
len: high - low,
base: low.clone(),
}
}
#[inline]
fn new_inclusive(low: Self::X, high: Self::X) -> Self {
assert!(low <= high);
Self::new(low, high + 1u32)
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
Self::new(low_b, high_b.borrow() + 1u32)
}
#[inline]
@ -148,8 +159,15 @@ impl UniformSampler for UniformBigUint {
}
#[inline]
fn sample_single<R: Rng + ?Sized>(low: Self::X, high: Self::X, rng: &mut R) -> Self::X {
rng.gen_biguint_range(&low, &high)
fn sample_single<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Self::X
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = low_b.borrow();
let high = high_b.borrow();
rng.gen_biguint_range(low, high)
}
}
@ -168,16 +186,31 @@ impl UniformSampler for UniformBigInt {
type X = BigInt;
#[inline]
fn new(low: Self::X, high: Self::X) -> Self {
#[inline]
fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = low_b.borrow();
let high = high_b.borrow();
assert!(low < high);
UniformBigInt {
len: into_magnitude(high - &low),
base: low,
len: into_magnitude(high - low),
base: low.clone(),
}
}
#[inline]
fn new_inclusive(low: Self::X, high: Self::X) -> Self {
fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = low_b.borrow();
let high = high_b.borrow();
assert!(low <= high);
Self::new(low, high + 1u32)
}
@ -188,8 +221,15 @@ impl UniformSampler for UniformBigInt {
}
#[inline]
fn sample_single<R: Rng + ?Sized>(low: Self::X, high: Self::X, rng: &mut R) -> Self::X {
rng.gen_bigint_range(&low, &high)
fn sample_single<R: Rng + ?Sized, B1, B2>(low_b: B1, high_b: B2, rng: &mut R) -> Self::X
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = low_b.borrow();
let high = high_b.borrow();
rng.gen_bigint_range(low, high)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -86,10 +86,19 @@
#[cfg(feature = "rand")]
extern crate rand;
#[cfg(all(test, feature = "rand"))]
extern crate rand_chacha;
#[cfg(all(test, feature = "rand"))]
extern crate rand_isaac;
#[cfg(all(test, feature = "rand"))]
extern crate rand_xorshift;
#[cfg(feature = "serde")]
extern crate serde;
#[cfg(feature = "zeroize")]
extern crate zeroize;
#[macro_use]
extern crate smallvec;

View File

@ -1,3 +1,5 @@
#![allow(clippy::many_single_char_names)]
use num_traits::{One, Zero};
use std::ops::Shl;

View File

@ -3,6 +3,9 @@
extern crate num_bigint_dig as num_bigint;
extern crate num_traits;
extern crate rand;
extern crate rand_chacha;
extern crate rand_isaac;
extern crate rand_xorshift;
mod biguint {
use num_bigint::{BigUint, RandBigInt, RandomBits};
@ -135,7 +138,7 @@ mod biguint {
#[test]
fn test_chacha_value_stability() {
use rand::prng::ChaChaRng;
use rand_chacha::ChaChaRng;
seeded_value_stability::<ChaChaRng>(EXPECTED_CHACHA);
}
@ -170,7 +173,7 @@ mod biguint {
];
#[test]
fn test_isaac_value_stability() {
use rand::prng::IsaacRng;
use rand_isaac::IsaacRng;
seeded_value_stability::<IsaacRng>(EXPECTED_ISAAC);
}
@ -204,7 +207,7 @@ mod biguint {
#[test]
fn test_xorshift_value_stability() {
use rand::prng::XorShiftRng;
use rand_xorshift::XorShiftRng;
seeded_value_stability::<XorShiftRng>(EXPECTED_XOR);
}
}
@ -344,7 +347,7 @@ mod bigint {
#[test]
fn test_chacha_value_stability() {
use rand::prng::ChaChaRng;
use rand_chacha::ChaChaRng;
seeded_value_stability::<ChaChaRng>(EXPECTED_CHACHA);
}
@ -379,7 +382,7 @@ mod bigint {
#[test]
fn test_isaac_value_stability() {
use rand::prng::IsaacRng;
use rand_isaac::IsaacRng;
seeded_value_stability::<IsaacRng>(EXPECTED_ISAAC);
}
@ -415,7 +418,7 @@ mod bigint {
#[test]
fn test_xorshift_value_stability() {
use rand::prng::XorShiftRng;
use rand_xorshift::XorShiftRng;
seeded_value_stability::<XorShiftRng>(EXPECTED_XOR);
}
}