Skip to content

Commit 49a14b5

Browse files
committed
bit and set_bit improvements
1 parent 8d6d388 commit 49a14b5

File tree

2 files changed

+71
-34
lines changed

2 files changed

+71
-34
lines changed

src/bigint.rs

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3258,15 +3258,22 @@ impl BigInt {
32583258
/// Returns whether the bit in position `bit` is set,
32593259
/// using the two's complement for negative numbers
32603260
pub fn bit(&self, bit: u64) -> bool {
3261-
// Let the binary representation of a number be
3262-
// ... 0 x 1 0 ... 0
3263-
// Then the two's complement is
3264-
// ... 1 !x 1 0 ... 0
3265-
// where !x is obtained from x by flipping each bit
3266-
if bit >= u64::from(big_digit::BITS) * self.len() as u64 {
3267-
self.is_negative()
3268-
} else if self.is_negative() && bit > self.data.trailing_zeros().unwrap() {
3269-
!self.data.bit(bit)
3261+
if self.is_negative() {
3262+
// Let the binary representation of a number be
3263+
// ... 0 x 1 0 ... 0
3264+
// Then the two's complement is
3265+
// ... 1 !x 1 0 ... 0
3266+
// where !x is obtained from x by flipping each bit
3267+
if bit >= u64::from(big_digit::BITS) * self.len() as u64 {
3268+
true
3269+
} else {
3270+
let trailing_zeros = self.data.trailing_zeros().unwrap();
3271+
match bit.cmp(&trailing_zeros) {
3272+
Ordering::Less => false,
3273+
Ordering::Equal => true,
3274+
Ordering::Greater => !self.data.bit(bit),
3275+
}
3276+
}
32703277
} else {
32713278
self.data.bit(bit)
32723279
}
@@ -3278,39 +3285,58 @@ impl BigInt {
32783285
match self.sign {
32793286
Sign::Plus => self.data.set_bit(bit, value),
32803287
Sign::NoSign => {
3281-
// clearing a bit for zero is a no-op
32823288
if value {
32833289
self.data.set_bit(bit, true);
32843290
self.sign = Sign::Plus;
3291+
} else {
3292+
// clearing a bit for zero is a no-op
32853293
}
32863294
}
32873295
Sign::Minus => {
32883296
let bits_per_digit = u64::from(big_digit::BITS);
3289-
// The first part of this `if` condition is not necessary but included because
3290-
// computing trailing_zeros can be avoided when the bit to set/clear is outside
3291-
// the represented digits
3292-
if bit >= bits_per_digit * self.len() as u64
3293-
|| bit > self.data.trailing_zeros().unwrap()
3294-
{
3295-
self.data.set_bit(bit, !value);
3297+
if bit >= bits_per_digit * self.len() as u64 {
3298+
if !value {
3299+
self.data.set_bit(bit, true);
3300+
}
32963301
} else {
3297-
// This is the general case that basically corresponds to what `bitor_neg_pos`
3298-
// (when setting bit) or `bitand_neg_pos` (when clearing bit) does, except there
3299-
// is no need to explicitly iterate over the digits of the right-hand side
3300-
let bit_index = (bit / bits_per_digit).to_usize().unwrap();
3301-
let bit_mask = (1 as BigDigit) << (bit % bits_per_digit);
3302-
let mut carry_in = 1;
3303-
let mut carry_out = 1;
3304-
for (index, digit) in self.digits_mut().iter_mut().enumerate() {
3302+
let trailing_zeros = self.data.trailing_zeros().unwrap();
3303+
if bit > trailing_zeros {
3304+
self.data.set_bit(bit, !value);
3305+
} else if bit < trailing_zeros && !value {
3306+
// bit is already cleared
3307+
} else if bit == trailing_zeros && value {
3308+
// bit is already set
3309+
} else {
3310+
// general case
3311+
let bit_index = (bit / bits_per_digit).to_usize().unwrap();
3312+
let bit_mask = (1 as BigDigit) << (bit % bits_per_digit);
3313+
let mut carry_in = 1;
3314+
let mut carry_out = 1;
3315+
let mut digit_iter = self.digits_mut().iter_mut().skip(bit_index);
3316+
3317+
let digit = digit_iter.next().unwrap();
33053318
let twos_in = negate_carry(*digit, &mut carry_in);
3306-
let twos_out = if index != bit_index {
3307-
twos_in
3308-
} else if value {
3319+
let twos_out = if value {
3320+
// set bit
33093321
twos_in | bit_mask
33103322
} else {
3323+
// clear bit
33113324
twos_in & !bit_mask
33123325
};
33133326
*digit = negate_carry(twos_out, &mut carry_out);
3327+
3328+
for digit in digit_iter {
3329+
if carry_in == 0 && carry_out == 0 {
3330+
// no more digits will change
3331+
break;
3332+
}
3333+
let twos = negate_carry(*digit, &mut carry_in);
3334+
*digit = negate_carry(twos, &mut carry_out);
3335+
}
3336+
3337+
if carry_out != 0 {
3338+
self.digits_mut().push(1 as BigDigit);
3339+
}
33143340
}
33153341
}
33163342
}

tests/bigint.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,12 +1311,12 @@ fn test_pow() {
13111311
#[test]
13121312
fn test_bit() {
13131313
// 12 = (1100)_2
1314-
assert!(!BigInt::from(12u8).bit(0));
1315-
assert!(!BigInt::from(12u8).bit(1));
1316-
assert!(BigInt::from(12u8).bit(2));
1317-
assert!(BigInt::from(12u8).bit(3));
1318-
assert!(!BigInt::from(12u8).bit(4));
1319-
assert!(!BigInt::from(12u8).bit(200));
1314+
assert!(!BigInt::from(0b1100u8).bit(0));
1315+
assert!(!BigInt::from(0b1100u8).bit(1));
1316+
assert!(BigInt::from(0b1100u8).bit(2));
1317+
assert!(BigInt::from(0b1100u8).bit(3));
1318+
assert!(!BigInt::from(0b1100u8).bit(4));
1319+
assert!(!BigInt::from(0b1100u8).bit(200));
13201320
// -12 = (...110100)_2
13211321
assert!(!BigInt::from(-12i8).bit(0));
13221322
assert!(!BigInt::from(-12i8).bit(1));
@@ -1365,4 +1365,15 @@ fn test_set_bit() {
13651365
x,
13661366
BigInt::from_biguint(Minus, (BigUint::one() << 200) - (BigUint::one() << 40))
13671367
);
1368+
1369+
x = BigInt::from_biguint(Minus, (BigUint::one() << 200) | (BigUint::one() << 100));
1370+
x.set_bit(100, false);
1371+
assert_eq!(
1372+
x,
1373+
BigInt::from_biguint(Minus, (BigUint::one() << 200) | (BigUint::one() << 101))
1374+
);
1375+
1376+
x = BigInt::from_biguint(Minus, (BigUint::one() << 63) | (BigUint::one() << 62));
1377+
x.set_bit(62, false);
1378+
assert_eq!(x, BigInt::from_biguint(Minus, BigUint::one() << 64));
13681379
}

0 commit comments

Comments
 (0)