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

Get/set n-th bit of BigUint and BigInt #183

Merged
merged 18 commits into from Feb 24, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/bigint.rs
Expand Up @@ -3254,6 +3254,70 @@ impl BigInt {
pub fn trailing_zeros(&self) -> Option<u64> {
self.data.trailing_zeros()
}

/// Returns whether the bit in position `bit` is set,
/// uses the two's complement for negative numbers
janmarthedal marked this conversation as resolved.
Show resolved Hide resolved
pub fn bit(&self, bit: u64) -> bool {
// Let the binary representation of a number be
// ... 0 x 1 0 ... 0
// Then the two's complement is
// ... 1 !x 1 0 ... 0
// where !x is obtained from x by flipping each bit
if bit >= u64::from(big_digit::BITS) * self.len() as u64 {
self.is_negative()
} else if self.is_negative() && bit > self.data.trailing_zeros().unwrap() {
!self.data.bit(bit)
} else {
self.data.bit(bit)
}
}

/// Sets or clears the bit in the given position,
/// uses the two's complement for negative numbers
janmarthedal marked this conversation as resolved.
Show resolved Hide resolved
pub fn set_bit(&mut self, bit: u64, value: bool) {
match self.sign {
Sign::Plus => self.data.set_bit(bit, value),
Sign::NoSign => {
// clearing a bit for zero is a no-op
if value {
self.data.set_bit(bit, true);
self.sign = Sign::Plus;
}
}
Sign::Minus => {
let bits_per_digit = u64::from(big_digit::BITS);
// The first part of this `if` condition is not necessary but included because
// computing trailing_zeros can be avoided when the bit to set/clear is outside
// the represented digits
if bit >= bits_per_digit * self.len() as u64
|| bit > self.data.trailing_zeros().unwrap()
{
self.data.set_bit(bit, !value);
} else {
// This is the general case that basically corresponds to what `bitor_neg_pos`
// (when setting bit) or `bitand_neg_pos` (when clearing bit) does, except there
// is no need to explicitly iterate over the digits of the right-hand side
janmarthedal marked this conversation as resolved.
Show resolved Hide resolved
let bit_index = (bit / bits_per_digit) as usize;
let bit_mask = (1 as BigDigit) << (bit % bits_per_digit);
let mut carry_in = 1;
let mut carry_out = 1;
for (index, digit) in self.digits_mut().iter_mut().enumerate() {
let twos_in = negate_carry(*digit, &mut carry_in);
let twos_out = if index != bit_index {
twos_in
} else if value {
twos_in | bit_mask
} else {
twos_in & !bit_mask
};
*digit = negate_carry(twos_out, &mut carry_out);
}
}
}
}
// the top bit may have been cleared, so normalize
self.normalize();
}
}

impl_sum_iter_type!(BigInt);
Expand Down
25 changes: 25 additions & 0 deletions src/biguint.rs
Expand Up @@ -2717,6 +2717,31 @@ impl BigUint {
pub fn count_ones(&self) -> u64 {
self.data.iter().map(|&d| u64::from(d.count_ones())).sum()
}

/// Returns whether the bit in the given position is set
pub fn bit(&self, bit: u64) -> bool {
let bits_per_digit = u64::from(big_digit::BITS);
let digit_index = (bit / bits_per_digit) as usize;
janmarthedal marked this conversation as resolved.
Show resolved Hide resolved
digit_index < self.data.len()
&& (self.data[digit_index] & ((1 as BigDigit) << (bit % bits_per_digit))) != 0
}

/// Sets or clears the bit in the given position
pub fn set_bit(&mut self, bit: u64, value: bool) {
let bits_per_digit = u64::from(big_digit::BITS);
let digit_index = (bit / bits_per_digit) as usize;
janmarthedal marked this conversation as resolved.
Show resolved Hide resolved
let bit_mask = (1 as BigDigit) << (bit % bits_per_digit);
if value {
if digit_index >= self.data.len() {
self.data.resize(digit_index + 1, 0);
janmarthedal marked this conversation as resolved.
Show resolved Hide resolved
}
self.data[digit_index] |= bit_mask;
} else if digit_index < self.data.len() {
self.data[digit_index] &= !bit_mask;
// the top bit may have been cleared, so normalize
self.normalize();
}
}
}

fn plain_modpow(base: &BigUint, exp_data: &[BigDigit], modulus: &BigUint) -> BigUint {
Expand Down
59 changes: 59 additions & 0 deletions tests/bigint.rs
Expand Up @@ -1307,3 +1307,62 @@ fn test_pow() {
check!(u64);
check!(usize);
}

#[test]
fn test_bit() {
// 12 = (1100)_2
assert!(!BigInt::from(12u8).bit(0));
assert!(!BigInt::from(12u8).bit(1));
assert!(BigInt::from(12u8).bit(2));
assert!(BigInt::from(12u8).bit(3));
assert!(!BigInt::from(12u8).bit(4));
assert!(!BigInt::from(12u8).bit(200));
// -12 = (...110100)_2
assert!(!BigInt::from(-12i8).bit(0));
assert!(!BigInt::from(-12i8).bit(1));
assert!(BigInt::from(-12i8).bit(2));
assert!(!BigInt::from(-12i8).bit(3));
assert!(BigInt::from(-12i8).bit(4));
assert!(BigInt::from(-12i8).bit(200));
}

#[test]
fn test_set_bit() {
let mut x = BigInt::zero();
x.set_bit(200, true);
assert_eq!(x, BigInt::one() << 200);
x.set_bit(10, true);
x.set_bit(200, false);
assert_eq!(x, BigInt::one() << 10);
x.set_bit(10, false);
x.set_bit(5, false);
assert_eq!(x, BigInt::zero());

x = BigInt::from(-12i8);
x.set_bit(200, true);
assert_eq!(x, BigInt::from(-12i8));
x.set_bit(200, false);
assert_eq!(
x,
BigInt::from_biguint(Minus, BigUint::from(12u8) | (BigUint::one() << 200))
);
x.set_bit(6, false);
assert_eq!(
x,
BigInt::from_biguint(Minus, BigUint::from(76u8) | (BigUint::one() << 200))
);
x.set_bit(6, true);
assert_eq!(
x,
BigInt::from_biguint(Minus, BigUint::from(12u8) | (BigUint::one() << 200))
);
x.set_bit(200, true);
assert_eq!(x, BigInt::from(-12i8));

x = BigInt::from_biguint(Minus, BigUint::one() << 200);
x.set_bit(40, true);
assert_eq!(
x,
BigInt::from_biguint(Minus, (BigUint::one() << 200) - (BigUint::one() << 40))
);
}
26 changes: 26 additions & 0 deletions tests/biguint.rs
Expand Up @@ -1806,3 +1806,29 @@ fn test_count_ones() {
let x: BigUint = (BigUint::from(3u8) << 128) | BigUint::from(3u8);
assert_eq!(x.count_ones(), 4);
}

#[test]
fn test_bit() {
assert!(!BigUint::from(0u8).bit(0));
assert!(!BigUint::from(0u8).bit(100));
assert!(!BigUint::from(42u8).bit(4));
assert!(BigUint::from(42u8).bit(5));
let x: BigUint = (BigUint::from(3u8) << 128) | BigUint::from(3u8);
assert!(x.bit(129));
assert!(!x.bit(130));
}

#[test]
fn test_set_bit() {
let mut x = BigUint::from(3u8);
x.set_bit(128, true);
x.set_bit(129, true);
assert_eq!(x, (BigUint::from(3u8) << 128) | BigUint::from(3u8));
x.set_bit(0, false);
x.set_bit(128, false);
x.set_bit(130, false);
assert_eq!(x, (BigUint::from(2u8) << 128) | BigUint::from(2u8));
x.set_bit(129, false);
x.set_bit(1, false);
assert_eq!(x, BigUint::zero());
}