Skip to content
Merged
Show file tree
Hide file tree
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
6 changes: 1 addition & 5 deletions src/uint/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod add;
mod add_mod;
mod bit_and;
mod bits;
mod cmp;
pub(crate) mod encoding;
mod modular;
Expand Down Expand Up @@ -150,11 +151,6 @@ impl BoxedUint {
self.limbs.len()
}

/// Get the precision of this [`BoxedUint`] in bits.
pub fn bits(&self) -> usize {
self.limbs.len() * Limb::BITS
}

/// Perform a carry chain-like operation over the limbs of the inputs,
/// constructing a result from the returned limbs and carry which is
/// widened to the same width as the widest input.
Expand Down
49 changes: 49 additions & 0 deletions src/uint/boxed/bits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! Bit manipulation functions.

use crate::{BoxedUint, Limb, Zero};
use subtle::{ConditionallySelectable, ConstantTimeEq};

impl BoxedUint {
/// Calculate the number of bits needed to represent this number, i.e. the index of the highest
/// set bit.
///
/// Use [`BoxedUint::bits_precision`] to get the total capacity of this integer.
pub fn bits(&self) -> usize {
// Use `u32` because `subtle` can't select on `usize` and it matches what `core` uses for
// the return value of `leading_zeros`
let mut leading_zeros = 0u32;
let mut n = 0u32;

for limb in self.limbs.iter().rev() {
n.conditional_assign(&(n + 1), !limb.is_zero() | !n.ct_eq(&0));

// Set `leading_zeros` for the first nonzero limb we encounter
leading_zeros.conditional_assign(&(limb.leading_zeros() as u32), n.ct_eq(&1));
}

Limb::BITS * (n as usize) - (leading_zeros as usize)
}

/// Get the precision of this [`BoxedUint`] in bits.
pub fn bits_precision(&self) -> usize {
self.limbs.len() * Limb::BITS
}
}

#[cfg(test)]
mod tests {
use super::BoxedUint;
use hex_literal::hex;

#[test]
fn bits() {
assert_eq!(0, BoxedUint::zero().bits());
assert_eq!(128, BoxedUint::max(128).bits());

let n1 = BoxedUint::from_be_slice(&hex!("000000000029ffffffffffffffffffff"), 128).unwrap();
assert_eq!(86, n1.bits());

let n2 = BoxedUint::from_be_slice(&hex!("00000000004000000000000000000000"), 128).unwrap();
assert_eq!(87, n2.bits());
}
}
2 changes: 1 addition & 1 deletion tests/boxed_uint_proptests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ proptest! {
match Option::<BoxedUint>::from(a.checked_add(&b)) {
Some(actual) => prop_assert_eq!(expected, to_biguint(&actual)),
None => {
let max = BoxedUint::max(a.bits());
let max = BoxedUint::max(a.bits_precision());
prop_assert!(expected > to_biguint(&max));
}
}
Expand Down