Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish i128 support #62

Merged
merged 40 commits into from
Jan 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
859ed05
feat: implement u128 and i128 operations for BigInt
dignifiedquire Jul 22, 2018
a1ea16a
first round of u64 BigDigit support
dignifiedquire Jul 21, 2018
48edbd4
some fixes
dignifiedquire Jul 21, 2018
9b75551
remove benchmark file
dignifiedquire Jul 21, 2018
a471ef5
start fixing after rebase
dignifiedquire Jul 22, 2018
4d9601a
fix mulassign impl
dignifiedquire Jul 23, 2018
bb3e3b1
alternative impl
dignifiedquire Jul 26, 2018
5cc40b7
use doublebigdigit
dignifiedquire Jul 26, 2018
e3babda
switch to faster add
dignifiedquire Jul 26, 2018
30cedd5
cleanup
dignifiedquire Jul 26, 2018
16180b5
fixup: rebase
dignifiedquire Nov 7, 2018
e02767d
mark serialization as unimplemented for now
dignifiedquire Nov 7, 2018
591e0f1
some cleanup
dignifiedquire Nov 7, 2018
e8a345f
Don't use field shorthands for older rustc
cuviper Aug 7, 2019
35512c9
Implement u64_digit serialization
cuviper Aug 7, 2019
3f261c3
Fix rand value stability for u64_digit
cuviper Aug 7, 2019
c1a8928
Add div_half for dividing by half-digit divisors
cuviper Aug 9, 2019
eb93dd5
cfg-gate u32_to/from_u128
cuviper Aug 9, 2019
a9b7800
Use half radix-bases when converting to strings
cuviper Aug 9, 2019
cc6f886
Automatically use u64_digit on 64-bit targets with has_i128
cuviper Aug 9, 2019
1717a5e
Fix the assumption of extern crate prelude
cuviper Aug 9, 2019
c41982d
Remove the unused get_limb
cuviper Aug 9, 2019
bbc1778
Document pub fn trailing_zeros
cuviper Aug 9, 2019
e831ef9
Implement Clone::clone_from
cuviper Aug 9, 2019
9a45c37
Remove pub fn assign_from_slice_native
cuviper Aug 9, 2019
33cead2
Use simpler constructors for BigInt
cuviper Aug 10, 2019
0560b75
Remove pub fn from_slice_native
cuviper Aug 10, 2019
938be52
Move BigUint::new_native to a free biguint_from_vec
cuviper Aug 10, 2019
e8e2f9d
cargo fmt
cuviper Aug 10, 2019
f8e4f13
Use biguint_from_vec in bigint_from_slice
cuviper Aug 10, 2019
f7747e5
Fix recursion in Rem<i64> for BigInt
cuviper Aug 10, 2019
2e55516
Implement Arbitrary with BigDigit
cuviper Aug 12, 2019
e42c0a9
nit: use a `1` literal directly
cuviper Sep 5, 2019
6fbea9a
Explain why we're manually implementing Clone
cuviper Sep 5, 2019
d6e8e13
use std::mem to call mem::swap
cuviper Sep 5, 2019
265c4fd
Comment build::write_radix_bases
cuviper Sep 5, 2019
bcdae48
Add i686 CI (without u64_digit)
cuviper Sep 5, 2019
0d8b039
Document the purpose of __add2
cuviper Sep 6, 2019
3c33bef
Merge branch 'master' (0.3.0-pre) into u128
cuviper Jan 13, 2020
69267c5
CI: test i686 natively
cuviper Jan 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ script:
- ./ci/test_full.sh
matrix:
include:
- name: "Rust: stable-i686"
rust: stable-i686-unknown-linux-gnu
addons:
apt:
packages:
- gcc-multilib
- name: "rustfmt"
rust: 1.31.0
before_script:
Expand Down
10 changes: 10 additions & 0 deletions benches/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ fn divide_2(b: &mut Bencher) {
divide_bench(b, 1 << 16, 1 << 12);
}

#[bench]
fn divide_big_little(b: &mut Bencher) {
divide_bench(b, 1 << 16, 1 << 4);
}

#[bench]
fn remainder_0(b: &mut Bencher) {
remainder_bench(b, 1 << 8, 1 << 6);
Expand All @@ -127,6 +132,11 @@ fn remainder_2(b: &mut Bencher) {
remainder_bench(b, 1 << 16, 1 << 12);
}

#[bench]
fn remainder_big_little(b: &mut Bencher) {
remainder_bench(b, 1 << 16, 1 << 4);
}

#[bench]
fn factorial_100(b: &mut Bencher) {
b.iter(|| factorial(100));
Expand Down
66 changes: 65 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,78 @@
extern crate autocfg;

use std::env;
use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::path::Path;

fn main() {
let ac = autocfg::new();

if ac.probe_type("i128") {
println!("cargo:rustc-cfg=has_i128");
autocfg::emit("has_i128");

let pointer_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH");
if pointer_width.as_ref().map(String::as_str) == Ok("64") {
autocfg::emit("u64_digit");
}
} else if env::var_os("CARGO_FEATURE_I128").is_some() {
panic!("i128 support was not detected!");
}

autocfg::rerun_path("build.rs");

write_radix_bases().unwrap();
}

/// Write tables of the greatest power of each radix for the given bit size. These are returned
/// from `biguint::get_radix_base` to batch the multiplication/division of radix conversions on
/// full `BigUint` values, operating on primitive integers as much as possible.
///
/// e.g. BASES_16[3] = (59049, 10) // 3¹⁰ fits in u16, but 3¹¹ is too big
/// BASES_32[3] = (3486784401, 20)
/// BASES_64[3] = (12157665459056928801, 40)
///
/// Powers of two are not included, just zeroed, as they're implemented with shifts.
#[allow(unknown_lints, bare_trait_objects)]
fn write_radix_bases() -> Result<(), Box<Error>> {
cuviper marked this conversation as resolved.
Show resolved Hide resolved
let out_dir = env::var("OUT_DIR")?;
let dest_path = Path::new(&out_dir).join("radix_bases.rs");
let mut f = File::create(&dest_path)?;

for &bits in &[16, 32, 64] {
let max = if bits < 64 {
(1 << bits) - 1
} else {
std::u64::MAX
};

writeln!(f, "#[deny(overflowing_literals)]")?;
writeln!(
f,
"pub static BASES_{bits}: [(u{bits}, usize); 257] = [",
bits = bits
)?;
for radix in 0u64..257 {
let (base, power) = if radix == 0 || radix.is_power_of_two() {
(0, 0)
} else {
let mut power = 1;
let mut base = radix;

while let Some(b) = base.checked_mul(radix) {
if b > max {
break;
}
base = b;
power += 1;
}
(base, power)
};
writeln!(f, " ({}, {}), // {}", base, power, radix)?;
}
writeln!(f, "];")?;
}

Ok(())
}
6 changes: 3 additions & 3 deletions ci/test_full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ set -ex
echo Testing num-bigint on rustc ${TRAVIS_RUST_VERSION}

FEATURES="serde"
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable|1.31.0|1.26.0|1.22.0)$ ]]; then
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable.*|1.31.0|1.26.0|1.22.0)$ ]]; then
FEATURES="$FEATURES rand"
fi
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable|1.31.0|1.26.0)$ ]]; then
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable.*|1.31.0|1.26.0)$ ]]; then
FEATURES="$FEATURES i128"
fi
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable|1.31.0)$ ]]; then
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable.*|1.31.0)$ ]]; then
FEATURES="$FEATURES quickcheck quickcheck_macros"
fi

Expand Down
90 changes: 65 additions & 25 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::mem;
use traits;
use traits::{One, Zero};

use biguint::biguint_from_vec;
use biguint::BigUint;

use bigint::BigInt;
Expand Down Expand Up @@ -68,29 +69,65 @@ fn div_wide(hi: BigDigit, lo: BigDigit, divisor: BigDigit) -> (BigDigit, BigDigi
((lhs / rhs) as BigDigit, (lhs % rhs) as BigDigit)
}

/// For small divisors, we can divide without promoting to `DoubleBigDigit` by
/// using half-size pieces of digit, like long-division.
#[inline]
fn div_half(rem: BigDigit, digit: BigDigit, divisor: BigDigit) -> (BigDigit, BigDigit) {
use big_digit::{HALF, HALF_BITS};
use integer::Integer;

debug_assert!(rem < divisor && divisor <= HALF);
let (hi, rem) = ((rem << HALF_BITS) | (digit >> HALF_BITS)).div_rem(&divisor);
let (lo, rem) = ((rem << HALF_BITS) | (digit & HALF)).div_rem(&divisor);
((hi << HALF_BITS) | lo, rem)
}

#[inline]
pub fn div_rem_digit(mut a: BigUint, b: BigDigit) -> (BigUint, BigDigit) {
let mut rem = 0;

for d in a.data.iter_mut().rev() {
let (q, r) = div_wide(rem, *d, b);
*d = q;
rem = r;
if b <= big_digit::HALF {
for d in a.data.iter_mut().rev() {
let (q, r) = div_half(rem, *d, b);
*d = q;
rem = r;
}
} else {
for d in a.data.iter_mut().rev() {
let (q, r) = div_wide(rem, *d, b);
*d = q;
rem = r;
}
}

(a.normalized(), rem)
}

#[inline]
pub fn rem_digit(a: &BigUint, b: BigDigit) -> BigDigit {
let mut rem: DoubleBigDigit = 0;
for &digit in a.data.iter().rev() {
rem = (rem << big_digit::BITS) + DoubleBigDigit::from(digit);
rem %= DoubleBigDigit::from(b);
let mut rem = 0;

if b <= big_digit::HALF {
for &digit in a.data.iter().rev() {
cuviper marked this conversation as resolved.
Show resolved Hide resolved
let (_, r) = div_half(rem, digit, b);
rem = r;
}
} else {
for &digit in a.data.iter().rev() {
cuviper marked this conversation as resolved.
Show resolved Hide resolved
let (_, r) = div_wide(rem, digit, b);
rem = r;
}
}

rem as BigDigit
rem
}

// Only for the Add impl:
/// Two argument addition of raw slices, `a += b`, returning the carry.
///
/// This is used when the data `Vec` might need to resize to push a non-zero carry, so we perform
/// the addition first hoping that it will fit.
///
/// The caller _must_ ensure that `a` is at least as long as `b`.
#[inline]
pub fn __add2(a: &mut [BigDigit], b: &[BigDigit]) -> BigDigit {
debug_assert!(a.len() >= b.len());
Expand Down Expand Up @@ -193,12 +230,12 @@ pub fn sub_sign(a: &[BigDigit], b: &[BigDigit]) -> (Sign, BigUint) {
Greater => {
let mut a = a.to_vec();
sub2(&mut a, b);
(Plus, BigUint::new(a))
(Plus, biguint_from_vec(a))
}
Less => {
let mut b = b.to_vec();
sub2(&mut b, a);
(Minus, BigUint::new(b))
(Minus, biguint_from_vec(b))
}
_ => (NoSign, Zero::zero()),
}
Expand All @@ -225,6 +262,10 @@ pub fn mac_digit(acc: &mut [BigDigit], b: &[BigDigit], c: BigDigit) {
}
}

fn bigint_from_slice(slice: &[BigDigit]) -> BigInt {
BigInt::from(biguint_from_vec(slice.to_vec()))
}

/// Three argument multiply accumulate:
/// acc += b * c
fn mac3(acc: &mut [BigDigit], b: &[BigDigit], c: &[BigDigit]) {
Expand Down Expand Up @@ -387,14 +428,14 @@ fn mac3(acc: &mut [BigDigit], b: &[BigDigit], c: &[BigDigit]) {
// in place of multiplications.
//
// x(t) = x2*t^2 + x1*t + x0
let x0 = BigInt::from_slice(Plus, &x[..x0_len]);
let x1 = BigInt::from_slice(Plus, &x[x0_len..x0_len + x1_len]);
let x2 = BigInt::from_slice(Plus, &x[x0_len + x1_len..]);
let x0 = bigint_from_slice(&x[..x0_len]);
let x1 = bigint_from_slice(&x[x0_len..x0_len + x1_len]);
let x2 = bigint_from_slice(&x[x0_len + x1_len..]);

// y(t) = y2*t^2 + y1*t + y0
let y0 = BigInt::from_slice(Plus, &y[..y0_len]);
let y1 = BigInt::from_slice(Plus, &y[y0_len..y0_len + y1_len]);
let y2 = BigInt::from_slice(Plus, &y[y0_len + y1_len..]);
let y0 = bigint_from_slice(&y[..y0_len]);
let y1 = bigint_from_slice(&y[y0_len..y0_len + y1_len]);
let y2 = bigint_from_slice(&y[y0_len + y1_len..]);

// Let w(t) = x(t) * y(t)
//
Expand Down Expand Up @@ -538,6 +579,7 @@ pub fn div_rem(mut u: BigUint, mut d: BigUint) -> (BigUint, BigUint) {
// want it to be the largest number we can efficiently divide by.
//
let shift = d.data.last().unwrap().leading_zeros() as usize;

let (q, r) = if shift == 0 {
// no need to clone d
div_rem_core(u, &d)
Expand Down Expand Up @@ -655,8 +697,7 @@ fn div_rem_core(mut a: BigUint, b: &BigUint) -> (BigUint, BigUint) {
let mut prod = b * &q0;

while cmp_slice(&prod.data[..], &a.data[j..]) == Greater {
let one: BigUint = One::one();
q0 -= one;
q0 -= 1u32;
prod -= b;
}

Expand Down Expand Up @@ -709,7 +750,7 @@ pub fn biguint_shl(n: Cow<BigUint>, bits: usize) -> BigUint {
}
}

BigUint::new(data)
biguint_from_vec(data)
}

#[inline]
Expand All @@ -736,7 +777,7 @@ pub fn biguint_shr(n: Cow<BigUint>, bits: usize) -> BigUint {
}
}

BigUint::new(data)
biguint_from_vec(data)
}

pub fn cmp_slice(a: &[BigDigit], b: &[BigDigit]) -> Ordering {
Expand Down Expand Up @@ -766,7 +807,6 @@ pub fn cmp_slice(a: &[BigDigit], b: &[BigDigit]) -> Ordering {
mod algorithm_tests {
use big_digit::BigDigit;
use traits::Num;
use Sign::Plus;
use {BigInt, BigUint};

#[test]
Expand All @@ -780,8 +820,8 @@ mod algorithm_tests {

let a = BigUint::from_str_radix("265252859812191058636308480000000", 10).unwrap();
let b = BigUint::from_str_radix("26525285981219105863630848000000", 10).unwrap();
let a_i = BigInt::from_biguint(Plus, a.clone());
let b_i = BigInt::from_biguint(Plus, b.clone());
let a_i = BigInt::from(a.clone());
let b_i = BigInt::from(b.clone());

assert_eq!(sub_sign_i(&a.data[..], &b.data[..]), &a_i - &b_i);
assert_eq!(sub_sign_i(&b.data[..], &a.data[..]), &b_i - &a_i);
Expand Down
Loading