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 all 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
5 changes: 4 additions & 1 deletion ci/big_quickcheck/Cargo.toml
Expand Up @@ -7,9 +7,12 @@ edition = "2018"
[dependencies]
num-integer = "0.1.42"
num-traits = "0.2.11"
quickcheck = "0.9"
quickcheck_macros = "0.9"

[dependencies.quickcheck]
version = "0.9"
default-features = false

[dependencies.num-bigint]
features = ["quickcheck"]
path = "../.."
123 changes: 123 additions & 0 deletions src/bigint.rs
Expand Up @@ -3254,6 +3254,129 @@ impl BigInt {
pub fn trailing_zeros(&self) -> Option<u64> {
self.data.trailing_zeros()
}

/// Returns whether the bit in position `bit` is set,
/// using the two's complement for negative numbers
pub fn bit(&self, bit: u64) -> bool {
if self.is_negative() {
// 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 {
true
} else {
let trailing_zeros = self.data.trailing_zeros().unwrap();
match Ord::cmp(&bit, &trailing_zeros) {
Less => false,
Equal => true,
Greater => !self.data.bit(bit),
}
}
} else {
self.data.bit(bit)
}
}

/// Sets or clears the bit in the given position,
/// using the two's complement for negative numbers
///
/// Note that setting/clearing a bit (for positive/negative numbers,
/// respectively) greater than the current bit length, a reallocation
/// may be needed to store the new digits
pub fn set_bit(&mut self, bit: u64, value: bool) {
match self.sign {
Sign::Plus => self.data.set_bit(bit, value),
Sign::NoSign => {
if value {
self.data.set_bit(bit, true);
self.sign = Sign::Plus;
} else {
// Clearing a bit for zero is a no-op
}
}
Sign::Minus => {
let bits_per_digit = u64::from(big_digit::BITS);
if bit >= bits_per_digit * self.len() as u64 {
if !value {
self.data.set_bit(bit, true);
}
} else {
// If the Uint number is
// ... 0 x 1 0 ... 0
// then the two's complement is
// ... 1 !x 1 0 ... 0
// |-- bit at position 'trailing_zeros'
// where !x is obtained from x by flipping each bit
let trailing_zeros = self.data.trailing_zeros().unwrap();
if bit > trailing_zeros {
self.data.set_bit(bit, !value);
} else if bit == trailing_zeros && !value {
// Clearing the bit at position `trailing_zeros` is dealt with by doing
// similarly to what `bitand_neg_pos` does, except we start at digit
// `bit_index`. All digits below `bit_index` are guaranteed to be zero,
// so initially we have `carry_in` = `carry_out` = 1. Furthermore, we
// stop traversing the digits when there are no more carries.
let bit_index = (bit / bits_per_digit).to_usize().unwrap();
let bit_mask = (1 as BigDigit) << (bit % bits_per_digit);
let mut digit_iter = self.digits_mut().iter_mut().skip(bit_index);
let mut carry_in = 1;
let mut carry_out = 1;

let digit = digit_iter.next().unwrap();
let twos_in = negate_carry(*digit, &mut carry_in);
let twos_out = twos_in & !bit_mask;
*digit = negate_carry(twos_out, &mut carry_out);

for digit in digit_iter {
if carry_in == 0 && carry_out == 0 {
// Exit the loop since no more digits can change
break;
}
let twos = negate_carry(*digit, &mut carry_in);
*digit = negate_carry(twos, &mut carry_out);
}

if carry_out != 0 {
// All digits have been traversed and there is a carry
debug_assert_eq!(carry_in, 0);
self.digits_mut().push(1);
}
} else if bit < trailing_zeros && value {
// Flip each bit from position 'bit' to 'trailing_zeros', both inclusive
// ... 1 !x 1 0 ... 0 ... 0
// |-- bit at position 'bit'
// |-- bit at position 'trailing_zeros'
// bit_mask: 1 1 ... 1 0 .. 0
// This is done by xor'ing with the bit_mask
let index_lo = (bit / bits_per_digit).to_usize().unwrap();
let index_hi = (trailing_zeros / bits_per_digit).to_usize().unwrap();
let bit_mask_lo = big_digit::MAX << (bit % bits_per_digit);
let bit_mask_hi = big_digit::MAX
>> (bits_per_digit - 1 - (trailing_zeros % bits_per_digit));
let digits = self.digits_mut();

if index_lo == index_hi {
digits[index_lo] ^= bit_mask_lo & bit_mask_hi;
} else {
digits[index_lo] = bit_mask_lo;
for index in (index_lo + 1)..index_hi {
digits[index] = big_digit::MAX;
}
digits[index_hi] ^= bit_mask_hi;
}
} else {
// We end up here in two cases:
// bit == trailing_zeros && value: Bit is already set
// bit < trailing_zeros && !value: Bit is already cleared
}
}
}
}
// The top bit may have been cleared, so normalize
self.normalize();
}
}

impl_sum_iter_type!(BigInt);
Expand Down
37 changes: 37 additions & 0 deletions src/biguint.rs
Expand Up @@ -2725,6 +2725,43 @@ 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);
if let Some(digit_index) = (bit / bits_per_digit).to_usize() {
if let Some(digit) = self.data.get(digit_index) {
let bit_mask = (1 as BigDigit) << (bit % bits_per_digit);
return (digit & bit_mask) != 0;
}
}
false
}

/// Sets or clears the bit in the given position
///
/// Note that setting a bit greater than the current bit length, a reallocation may be needed
/// to store the new digits
pub fn set_bit(&mut self, bit: u64, value: bool) {
// Note: we're saturating `digit_index` and `new_len` -- any such case is guaranteed to
// fail allocation, and that's more consistent than adding our own overflow panics.
let bits_per_digit = u64::from(big_digit::BITS);
let digit_index = (bit / bits_per_digit)
.to_usize()
.unwrap_or(core::usize::MAX);
let bit_mask = (1 as BigDigit) << (bit % bits_per_digit);
if value {
if digit_index >= self.data.len() {
let new_len = digit_index.saturating_add(1);
self.data.resize(new_len, 0);
}
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
93 changes: 93 additions & 0 deletions tests/bigint.rs
Expand Up @@ -1307,3 +1307,96 @@ fn test_pow() {
check!(u64);
check!(usize);
}

#[test]
fn test_bit() {
// 12 = (1100)_2
assert!(!BigInt::from(0b1100u8).bit(0));
assert!(!BigInt::from(0b1100u8).bit(1));
assert!(BigInt::from(0b1100u8).bit(2));
assert!(BigInt::from(0b1100u8).bit(3));
assert!(!BigInt::from(0b1100u8).bit(4));
assert!(!BigInt::from(0b1100u8).bit(200));
assert!(!BigInt::from(0b1100u8).bit(u64::MAX));
// -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));
assert!(BigInt::from(-12i8).bit(u64::MAX));
}

#[test]
fn test_set_bit() {
let mut x: BigInt;

// zero
x = BigInt::zero();
x.set_bit(200, true);
assert_eq!(x, BigInt::one() << 200);
x = BigInt::zero();
x.set_bit(200, false);
assert_eq!(x, BigInt::zero());

// positive numbers
x = BigInt::from_biguint(Plus, BigUint::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());

// negative numbers
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() << 30);
x.set_bit(10, true);
assert_eq!(
x,
BigInt::from_biguint(Minus, (BigUint::one() << 30) - (BigUint::one() << 10))
);

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

x = BigInt::from_biguint(Minus, (BigUint::one() << 200) | (BigUint::one() << 100));
x.set_bit(100, false);
assert_eq!(
x,
BigInt::from_biguint(Minus, (BigUint::one() << 200) | (BigUint::one() << 101))
);

x = BigInt::from_biguint(Minus, (BigUint::one() << 63) | (BigUint::one() << 62));
x.set_bit(62, false);
assert_eq!(x, BigInt::from_biguint(Minus, BigUint::one() << 64));

x = BigInt::from_biguint(Minus, (BigUint::one() << 200) - BigUint::one());
x.set_bit(0, false);
assert_eq!(x, BigInt::from_biguint(Minus, BigUint::one() << 200));
}
26 changes: 26 additions & 0 deletions tests/biguint.rs
Expand Up @@ -1808,3 +1808,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());
}