Skip to content

Commit

Permalink
Merge #143
Browse files Browse the repository at this point in the history
143: Count big bits in u64 instead of usize r=cuviper a=cuviper

A 32-bit target _could_ create a giant `BigUint` over 2²⁹ bytes = 512MB,
not that this would be computationally useful, but that value would have
more than `usize::MAX` bits. To avoid that possibility of overflow, we
can better represent bit counts in `u64`.

For 64-bit targets, a `BigUint` over 2⁶¹ bytes is not realistic.

This changes the public API in the return values of `bits()` and
`trailing_zeros()`, as well as the `bit_size` parameter of `RandBigInt`
and `RandomBits`. Trying to randomize an absurd size will naturally
cause a capacity overflow, just as if you'd made a huge `Vec`.

Co-authored-by: Josh Stone <cuviper@gmail.com>
  • Loading branch information
bors[bot] and cuviper committed Apr 14, 2020
2 parents 5b679e4 + 6d25cab commit ee2b80b
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 67 deletions.
8 changes: 4 additions & 4 deletions benches/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ fn get_rng() -> StdRng {
SeedableRng::from_seed(seed)
}

fn multiply_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
fn multiply_bench(b: &mut Bencher, xbits: u64, ybits: u64) {
let mut rng = get_rng();
let x = rng.gen_bigint(xbits);
let y = rng.gen_bigint(ybits);

b.iter(|| &x * &y);
}

fn divide_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
fn divide_bench(b: &mut Bencher, xbits: u64, ybits: u64) {
let mut rng = get_rng();
let x = rng.gen_bigint(xbits);
let y = rng.gen_bigint(ybits);

b.iter(|| &x / &y);
}

fn remainder_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
fn remainder_bench(b: &mut Bencher, xbits: u64, ybits: u64) {
let mut rng = get_rng();
let x = rng.gen_bigint(xbits);
let y = rng.gen_bigint(ybits);
Expand Down Expand Up @@ -245,7 +245,7 @@ fn from_str_radix_36(b: &mut Bencher) {
from_str_radix_bench(b, 36);
}

fn rand_bench(b: &mut Bencher, bits: usize) {
fn rand_bench(b: &mut Bencher, bits: u64) {
let mut rng = get_rng();

b.iter(|| rng.gen_bigint(bits));
Expand Down
2 changes: 1 addition & 1 deletion benches/gcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn get_rng() -> StdRng {
SeedableRng::from_seed(seed)
}

fn bench(b: &mut Bencher, bits: usize, gcd: fn(&BigUint, &BigUint) -> BigUint) {
fn bench(b: &mut Bencher, bits: u64, gcd: fn(&BigUint, &BigUint) -> BigUint) {
let mut rng = get_rng();
let x = rng.gen_biguint(bits);
let y = rng.gen_biguint(bits);
Expand Down
6 changes: 3 additions & 3 deletions benches/roots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn check(x: &BigUint, n: u32) {
assert_eq!((&hi - 1u32).nth_root(n), root);
}

fn bench_sqrt(b: &mut Bencher, bits: usize) {
fn bench_sqrt(b: &mut Bencher, bits: u64) {
let x = get_rng().gen_biguint(bits);
eprintln!("bench_sqrt({})", x);

Expand Down Expand Up @@ -71,7 +71,7 @@ fn big4k_sqrt(b: &mut Bencher) {
bench_sqrt(b, 4096);
}

fn bench_cbrt(b: &mut Bencher, bits: usize) {
fn bench_cbrt(b: &mut Bencher, bits: u64) {
let x = get_rng().gen_biguint(bits);
eprintln!("bench_cbrt({})", x);

Expand Down Expand Up @@ -99,7 +99,7 @@ fn big4k_cbrt(b: &mut Bencher) {
bench_cbrt(b, 4096);
}

fn bench_nth_root(b: &mut Bencher, bits: usize, n: u32) {
fn bench_nth_root(b: &mut Bencher, bits: u64, n: u32) {
let x = get_rng().gen_biguint(bits);
eprintln!("bench_{}th_root({})", n, x);

Expand Down
4 changes: 2 additions & 2 deletions ci/big_rand/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ mod biguint {
let mut rng = R::from_seed(seed);
for (i, &s) in expected.iter().enumerate() {
let n: BigUint = s.parse().unwrap();
let r = rng.gen_biguint((1 << i) + i);
let r = rng.gen_biguint((1 << i) + i as u64);
assert_eq!(n, r);
}
}
Expand Down Expand Up @@ -302,7 +302,7 @@ mod bigint {
let mut rng = R::from_seed(seed);
for (i, &s) in expected.iter().enumerate() {
let n: BigInt = s.parse().unwrap();
let r = rng.gen_bigint((1 << i) + i);
let r = rng.gen_bigint((1 << i) + i as u64);
assert_eq!(n, r);
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ fn mac3(acc: &mut [BigDigit], b: &[BigDigit], c: &[BigDigit]) {
// Recomposition. The coefficients of the polynomial are now known.
//
// Evaluate at w(t) where t is our given base to get the result.
let bits = big_digit::BITS * i;
let bits = u64::from(big_digit::BITS) * i as u64;
let result = r0
+ (comp1 << bits)
+ (comp2 << (2 * bits))
Expand Down Expand Up @@ -720,11 +720,11 @@ fn div_rem_core(mut a: BigUint, b: &BigUint) -> (BigUint, BigUint) {

/// Find last set bit
/// fls(0) == 0, fls(u32::MAX) == 32
pub(crate) fn fls<T: PrimInt>(v: T) -> usize {
mem::size_of::<T>() * 8 - v.leading_zeros() as usize
pub(crate) fn fls<T: PrimInt>(v: T) -> u8 {
mem::size_of::<T>() as u8 * 8 - v.leading_zeros() as u8
}

pub(crate) fn ilog2<T: PrimInt>(v: T) -> usize {
pub(crate) fn ilog2<T: PrimInt>(v: T) -> u8 {
fls(v) - 1
}

Expand Down
6 changes: 3 additions & 3 deletions src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,7 @@ impl_shift! { i8, i16, i32, i64, i128, isize }
fn shr_round_down<T: PrimInt>(i: &BigInt, shift: T) -> bool {
if i.is_negative() {
let zeros = i.trailing_zeros().expect("negative values are non-zero");
shift > T::zero() && shift.to_usize().map(|shift| zeros < shift).unwrap_or(true)
shift > T::zero() && shift.to_u64().map(|shift| zeros < shift).unwrap_or(true)
} else {
false
}
Expand Down Expand Up @@ -3195,7 +3195,7 @@ impl BigInt {
/// Determines the fewest bits necessary to express the `BigInt`,
/// not including the sign.
#[inline]
pub fn bits(&self) -> usize {
pub fn bits(&self) -> u64 {
self.data.bits()
}

Expand Down Expand Up @@ -3290,7 +3290,7 @@ impl BigInt {

/// Returns the number of least-significant bits that are zero,
/// or `None` if the entire number is zero.
pub fn trailing_zeros(&self) -> Option<usize> {
pub fn trailing_zeros(&self) -> Option<u64> {
self.data.trailing_zeros()
}
}
Expand Down
32 changes: 19 additions & 13 deletions src/bigrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ use crate::bigint::{into_magnitude, magnitude};
use crate::biguint::biguint_from_vec;

use num_integer::Integer;
use num_traits::Zero;
use num_traits::{ToPrimitive, Zero};

/// A trait for sampling random big integers.
///
/// The `rand` feature must be enabled to use this. See crate-level documentation for details.
pub trait RandBigInt {
/// Generate a random `BigUint` of the given bit size.
fn gen_biguint(&mut self, bit_size: usize) -> BigUint;
fn gen_biguint(&mut self, bit_size: u64) -> BigUint;

/// Generate a random BigInt of the given bit size.
fn gen_bigint(&mut self, bit_size: usize) -> BigInt;
fn gen_bigint(&mut self, bit_size: u64) -> BigInt;

/// Generate a random `BigUint` less than the given bound. Fails
/// when the bound is zero.
Expand All @@ -38,7 +38,7 @@ pub trait RandBigInt {
fn gen_bigint_range(&mut self, lbound: &BigInt, ubound: &BigInt) -> BigInt;
}

fn gen_bits<R: Rng + ?Sized>(rng: &mut R, data: &mut [u32], rem: usize) {
fn gen_bits<R: Rng + ?Sized>(rng: &mut R, data: &mut [u32], rem: u64) {
// `fill` is faster than many `gen::<u32>` calls
rng.fill(data);
if rem > 0 {
Expand All @@ -49,25 +49,31 @@ fn gen_bits<R: Rng + ?Sized>(rng: &mut R, data: &mut [u32], rem: usize) {

impl<R: Rng + ?Sized> RandBigInt for R {
#[cfg(not(u64_digit))]
fn gen_biguint(&mut self, bit_size: usize) -> BigUint {
fn gen_biguint(&mut self, bit_size: u64) -> BigUint {
let (digits, rem) = bit_size.div_rem(&32);
let mut data = vec![0u32; digits + (rem > 0) as usize];
let len = (digits + (rem > 0) as u64)
.to_usize()
.expect("capacity overflow");
let mut data = vec![0u32; len];
gen_bits(self, &mut data, rem);
biguint_from_vec(data)
}

#[cfg(u64_digit)]
fn gen_biguint(&mut self, bit_size: usize) -> BigUint {
fn gen_biguint(&mut self, bit_size: u64) -> BigUint {
use core::slice;

let (digits, rem) = bit_size.div_rem(&32);
let len = (digits + (rem > 0) as u64)
.to_usize()
.expect("capacity overflow");
let native_digits = bit_size.div_ceil(&64);
let mut data = vec![0u64; native_digits];
let native_len = native_digits.to_usize().expect("capacity overflow");
let mut data = vec![0u64; native_len];
unsafe {
// Generate bits in a `&mut [u32]` slice for value stability
let ptr = data.as_mut_ptr() as *mut u32;
let len = digits + (rem > 0) as usize;
debug_assert!(native_digits * 2 >= len);
debug_assert!(native_len * 2 >= len);
let data = slice::from_raw_parts_mut(ptr, len);
gen_bits(self, data, rem);
}
Expand All @@ -79,7 +85,7 @@ impl<R: Rng + ?Sized> RandBigInt for R {
biguint_from_vec(data)
}

fn gen_bigint(&mut self, bit_size: usize) -> BigInt {
fn gen_bigint(&mut self, bit_size: u64) -> BigInt {
loop {
// Generate a random BigUint...
let biguint = self.gen_biguint(bit_size);
Expand Down Expand Up @@ -253,12 +259,12 @@ impl SampleUniform for BigInt {
/// The `rand` feature must be enabled to use this. See crate-level documentation for details.
#[derive(Clone, Copy, Debug)]
pub struct RandomBits {
bits: usize,
bits: u64,
}

impl RandomBits {
#[inline]
pub fn new(bits: usize) -> RandomBits {
pub fn new(bits: u64) -> RandomBits {
RandomBits { bits }
}
}
Expand Down

0 comments on commit ee2b80b

Please sign in to comment.