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

Count big bits in u64 instead of usize #143

Merged
merged 2 commits into from
Apr 15, 2020
Merged
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions benches/bigint.rs
Original file line number Diff line number Diff line change
@@ -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);
@@ -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));
2 changes: 1 addition & 1 deletion benches/gcd.rs
Original file line number Diff line number Diff line change
@@ -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);
6 changes: 3 additions & 3 deletions benches/roots.rs
Original file line number Diff line number Diff line change
@@ -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);

@@ -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);

@@ -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);

4 changes: 2 additions & 2 deletions ci/big_rand/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
@@ -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);
}
}
8 changes: 4 additions & 4 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
@@ -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))
@@ -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
}

6 changes: 3 additions & 3 deletions src/bigint.rs
Original file line number Diff line number Diff line change
@@ -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
}
@@ -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()
}

@@ -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()
}
}
32 changes: 19 additions & 13 deletions src/bigrand.rs
Original file line number Diff line number Diff line change
@@ -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.
@@ -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 {
@@ -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);
}
@@ -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);
@@ -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 }
}
}
79 changes: 46 additions & 33 deletions src/biguint.rs
Original file line number Diff line number Diff line change
@@ -216,14 +216,14 @@ impl FromStr for BigUint {

// Convert from a power of two radix (bits == ilog2(radix)) where bits evenly divides
// BigDigit::BITS
fn from_bitwise_digits_le(v: &[u8], bits: usize) -> BigUint {
fn from_bitwise_digits_le(v: &[u8], bits: u8) -> BigUint {
debug_assert!(!v.is_empty() && bits <= 8 && big_digit::BITS % bits == 0);
debug_assert!(v.iter().all(|&c| BigDigit::from(c) < (1 << bits)));

let digits_per_big_digit = big_digit::BITS / bits;

let data = v
.chunks(digits_per_big_digit)
.chunks(digits_per_big_digit.into())
.map(|chunk| {
chunk
.iter()
@@ -237,11 +237,15 @@ fn from_bitwise_digits_le(v: &[u8], bits: usize) -> BigUint {

// Convert from a power of two radix (bits == ilog2(radix)) where bits doesn't evenly divide
// BigDigit::BITS
fn from_inexact_bitwise_digits_le(v: &[u8], bits: usize) -> BigUint {
fn from_inexact_bitwise_digits_le(v: &[u8], bits: u8) -> BigUint {
debug_assert!(!v.is_empty() && bits <= 8 && big_digit::BITS % bits != 0);
debug_assert!(v.iter().all(|&c| BigDigit::from(c) < (1 << bits)));

let big_digits = (v.len() * bits + big_digit::BITS - 1) / big_digit::BITS;
let big_digits = (v.len() as u64)
.saturating_mul(bits.into())
.div_ceil(&big_digit::BITS.into())
.to_usize()
.unwrap_or(core::usize::MAX);
let mut data = Vec::with_capacity(big_digits);

let mut d = 0;
@@ -1587,7 +1591,7 @@ impl Integer for BigUint {
#[inline]
fn gcd(&self, other: &Self) -> Self {
#[inline]
fn twos(x: &BigUint) -> usize {
fn twos(x: &BigUint) -> u64 {
x.trailing_zeros().unwrap_or(0)
}

@@ -1688,7 +1692,7 @@ impl Integer for BigUint {
}

#[inline]
fn fixpoint<F>(mut x: BigUint, max_bits: usize, f: F) -> BigUint
fn fixpoint<F>(mut x: BigUint, max_bits: u64, f: F) -> BigUint
where
F: Fn(&BigUint) -> BigUint,
{
@@ -1739,7 +1743,8 @@ impl Roots for BigUint {

// The root of non-zero values less than 2ⁿ can only be 1.
let bits = self.bits();
if bits <= n as usize {
let n64 = u64::from(n);
if bits <= n64 {
return BigUint::one();
}

@@ -1748,7 +1753,7 @@ impl Roots for BigUint {
return x.nth_root(n).into();
}

let max_bits = bits / n as usize + 1;
let max_bits = bits / n64 + 1;

#[cfg(feature = "std")]
let guess = if let Some(f) = self.to_f64() {
@@ -1757,11 +1762,10 @@ impl Roots for BigUint {
} else {
// Try to guess by scaling down such that it does fit in `f64`.
// With some (x * 2ⁿᵏ), its nth root ≈ (ⁿ√x * 2ᵏ)
let nsz = n as usize;
let extra_bits = bits - (f64::MAX_EXP as usize - 1);
let root_scale = (extra_bits + (nsz - 1)) / nsz;
let scale = root_scale * nsz;
if scale < bits && bits - scale > nsz {
let extra_bits = bits - (f64::MAX_EXP as u64 - 1);
let root_scale = extra_bits.div_ceil(&n64);
let scale = root_scale * n64;
if scale < bits && bits - scale > n64 {
(self >> scale).nth_root(n) << root_scale
} else {
BigUint::one() << max_bits
@@ -1792,7 +1796,7 @@ impl Roots for BigUint {
}

let bits = self.bits();
let max_bits = bits / 2 as usize + 1;
let max_bits = bits / 2 + 1;

#[cfg(feature = "std")]
let guess = if let Some(f) = self.to_f64() {
@@ -1801,7 +1805,7 @@ impl Roots for BigUint {
} else {
// Try to guess by scaling down such that it does fit in `f64`.
// With some (x * 2²ᵏ), its sqrt ≈ (√x * 2ᵏ)
let extra_bits = bits - (f64::MAX_EXP as usize - 1);
let extra_bits = bits - (f64::MAX_EXP as u64 - 1);
let root_scale = (extra_bits + 1) / 2;
let scale = root_scale * 2;
(self >> scale).sqrt() << root_scale
@@ -1828,7 +1832,7 @@ impl Roots for BigUint {
}

let bits = self.bits();
let max_bits = bits / 3 as usize + 1;
let max_bits = bits / 3 + 1;

#[cfg(feature = "std")]
let guess = if let Some(f) = self.to_f64() {
@@ -1837,7 +1841,7 @@ impl Roots for BigUint {
} else {
// Try to guess by scaling down such that it does fit in `f64`.
// With some (x * 2³ᵏ), its cbrt ≈ (∛x * 2ᵏ)
let extra_bits = bits - (f64::MAX_EXP as usize - 1);
let extra_bits = bits - (f64::MAX_EXP as u64 - 1);
let root_scale = (extra_bits + 2) / 3;
let scale = root_scale * 3;
(self >> scale).cbrt() << root_scale
@@ -1864,7 +1868,7 @@ fn high_bits_to_u64(v: &BigUint) -> u64 {
let mut ret_bits = 0;

for d in v.data.iter().rev() {
let digit_bits = (bits - 1) % big_digit::BITS + 1;
let digit_bits = (bits - 1) % u64::from(big_digit::BITS) + 1;
let bits_want = cmp::min(64 - ret_bits, digit_bits);

if bits_want != 64 {
@@ -1932,9 +1936,9 @@ impl ToPrimitive for BigUint {
#[inline]
fn to_f32(&self) -> Option<f32> {
let mantissa = high_bits_to_u64(self);
let exponent = self.bits() - fls(mantissa);
let exponent = self.bits() - u64::from(fls(mantissa));

if exponent > f32::MAX_EXP as usize {
if exponent > f32::MAX_EXP as u64 {
None
} else {
let ret = (mantissa as f32) * 2.0f32.powi(exponent as i32);
@@ -1949,9 +1953,9 @@ impl ToPrimitive for BigUint {
#[inline]
fn to_f64(&self) -> Option<f64> {
let mantissa = high_bits_to_u64(self);
let exponent = self.bits() - fls(mantissa);
let exponent = self.bits() - u64::from(fls(mantissa));

if exponent > f64::MAX_EXP as usize {
if exponent > f64::MAX_EXP as u64 {
None
} else {
let ret = (mantissa as f64) * 2.0f64.powi(exponent as i32);
@@ -2170,13 +2174,17 @@ impl_to_biguint!(f32, FromPrimitive::from_f32);
impl_to_biguint!(f64, FromPrimitive::from_f64);

// Extract bitwise digits that evenly divide BigDigit
fn to_bitwise_digits_le(u: &BigUint, bits: usize) -> Vec<u8> {
fn to_bitwise_digits_le(u: &BigUint, bits: u8) -> Vec<u8> {
debug_assert!(!u.is_zero() && bits <= 8 && big_digit::BITS % bits == 0);

let last_i = u.data.len() - 1;
let mask: BigDigit = (1 << bits) - 1;
let digits_per_big_digit = big_digit::BITS / bits;
let digits = (u.bits() + bits - 1) / bits;
let digits = u
.bits()
.div_ceil(&u64::from(bits))
.to_usize()
.unwrap_or(core::usize::MAX);
let mut res = Vec::with_capacity(digits);

for mut r in u.data[..last_i].iter().cloned() {
@@ -2196,11 +2204,15 @@ fn to_bitwise_digits_le(u: &BigUint, bits: usize) -> Vec<u8> {
}

// Extract bitwise digits that don't evenly divide BigDigit
fn to_inexact_bitwise_digits_le(u: &BigUint, bits: usize) -> Vec<u8> {
fn to_inexact_bitwise_digits_le(u: &BigUint, bits: u8) -> Vec<u8> {
debug_assert!(!u.is_zero() && bits <= 8 && big_digit::BITS % bits != 0);

let mask: BigDigit = (1 << bits) - 1;
let digits = (u.bits() + bits - 1) / bits;
let digits = u
.bits()
.div_ceil(&u64::from(bits))
.to_usize()
.unwrap_or(core::usize::MAX);
let mut res = Vec::with_capacity(digits);

let mut r = 0;
@@ -2659,12 +2671,12 @@ impl BigUint {

/// Determines the fewest bits necessary to express the `BigUint`.
#[inline]
pub fn bits(&self) -> usize {
pub fn bits(&self) -> u64 {
if self.is_zero() {
return 0;
}
let zeros = self.data.last().unwrap().leading_zeros();
self.data.len() * big_digit::BITS - zeros as usize
let zeros: u64 = self.data.last().unwrap().leading_zeros().into();
self.data.len() as u64 * u64::from(big_digit::BITS) - zeros
}

/// Strips off trailing zero bigdigits - comparisons require the last element in the vector to
@@ -2723,9 +2735,10 @@ impl BigUint {

/// 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> {
let i = self.data.iter().position(|&digit| digit != 0)?;
Some(i * big_digit::BITS + self.data[i].trailing_zeros() as usize)
let zeros: u64 = self.data[i].trailing_zeros().into();
Some(i as u64 * u64::from(big_digit::BITS) + zeros)
}
}

@@ -2745,7 +2758,7 @@ fn plain_modpow(base: &BigUint, exp_data: &[BigDigit], modulus: &BigUint) -> Big
}

let mut r = exp_data[i];
let mut b = 0usize;
let mut b = 0u8;
while r.is_even() {
base = &base * &base % modulus;
r >>= 1;
@@ -2984,7 +2997,7 @@ impl<'de> serde::Deserialize<'de> for BigUint {

/// Returns the greatest power of the radix for the given bit size
#[inline]
fn get_radix_base(radix: u32, bits: usize) -> (BigDigit, usize) {
fn get_radix_base(radix: u32, bits: u8) -> (BigDigit, usize) {
mod gen {
include! { concat!(env!("OUT_DIR"), "/radix_bases.rs") }
}
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -258,11 +258,11 @@ mod big_digit {

// `DoubleBigDigit` size dependent
#[cfg(not(u64_digit))]
pub(crate) const BITS: usize = 32;
pub(crate) const BITS: u8 = 32;
#[cfg(u64_digit)]
pub(crate) const BITS: usize = 64;
pub(crate) const BITS: u8 = 64;

pub(crate) const HALF_BITS: usize = BITS / 2;
pub(crate) const HALF_BITS: u8 = BITS / 2;
pub(crate) const HALF: BigDigit = (1 << HALF_BITS) - 1;

const LO_MASK: DoubleBigDigit = (1 << BITS) - 1;
2 changes: 1 addition & 1 deletion src/monty.rs
Original file line number Diff line number Diff line change
@@ -150,7 +150,7 @@ pub(crate) fn monty_modpow(x: &BigUint, y: &BigUint, m: &BigUint) -> BigUint {

// rr = 2**(2*_W*len(m)) mod m
let mut rr = BigUint::one();
rr = (rr.shl(2 * num_words * big_digit::BITS)) % m;
rr = (rr.shl(2 * num_words as u64 * u64::from(big_digit::BITS))) % m;
if rr.data.len() < num_words {
rr.data.resize(num_words, 0);
}