From 8d9f40cc65a5dba2eff17307c70ffe02fca5e1bc Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Wed, 30 Dec 2020 13:32:07 +0100 Subject: [PATCH 01/17] Add methods bit and set_bit for BigUint --- src/biguint.rs | 25 +++++++++++++++++++++++++ tests/biguint.rs | 23 +++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/biguint.rs b/src/biguint.rs index 432ff849..28d2caf2 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -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; + 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; + 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); + } + self.data[digit_index] |= bit_mask; + } else { + if digit_index < self.data.len() { + self.data[digit_index] &= !bit_mask; + } + } + } } fn plain_modpow(base: &BigUint, exp_data: &[BigDigit], modulus: &BigUint) -> BigUint { diff --git a/tests/biguint.rs b/tests/biguint.rs index cc274954..53a2b4a1 100644 --- a/tests/biguint.rs +++ b/tests/biguint.rs @@ -1806,3 +1806,26 @@ 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)); +} From 107b415bc2ca2a403b987e415eb1e62c668ad8bf Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Wed, 30 Dec 2020 14:04:35 +0100 Subject: [PATCH 02/17] Add method bit for BigInt --- src/bigint.rs | 11 +++++++++++ tests/bigint.rs | 16 ++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/bigint.rs b/src/bigint.rs index caa6d0eb..0e868c5b 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3254,6 +3254,17 @@ impl BigInt { pub fn trailing_zeros(&self) -> Option { self.data.trailing_zeros() } + + /// Returns whether the bit in position `bit` is set, + /// uses the two's complement for negative numbers + pub fn bit(&self, bit: u64) -> bool { + let b = self.data.bit(bit); + if self.is_negative() && bit > self.data.trailing_zeros().unwrap() { + !b + } else { + b + } + } } impl_sum_iter_type!(BigInt); diff --git a/tests/bigint.rs b/tests/bigint.rs index 292796ef..af1b0d03 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -1307,3 +1307,19 @@ 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)); + // -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)); +} From 58a816305688ad8e385e1ff993d2675eb53e236f Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Thu, 31 Dec 2020 09:45:41 +0100 Subject: [PATCH 03/17] Code done, more tests pending --- src/bigint.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/biguint.rs | 2 ++ 2 files changed, 48 insertions(+) diff --git a/src/bigint.rs b/src/bigint.rs index 0e868c5b..6fbc8007 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3258,6 +3258,11 @@ impl BigInt { /// Returns whether the bit in position `bit` is set, /// uses the two's complement for negative numbers pub fn bit(&self, bit: u64) -> bool { + // Let the binary representation of a number be + // x 1 0 ... 0 + // Then the two's complement is + // !x 1 0 ... 0 + // where !x is obtained from x by flipping each bit let b = self.data.bit(bit); if self.is_negative() && bit > self.data.trailing_zeros().unwrap() { !b @@ -3265,6 +3270,47 @@ impl BigInt { b } } + + 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; + } + } + Sign::Minus => { + let bits_per_digit = u64::from(big_digit::BITS); + if bit < bits_per_digit * self.len() as u64 { + let digit_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 (i, d) in self.digits_mut().iter_mut().enumerate() { + let twos_in = negate_carry(*d, &mut carry_in); + let twos_out = if i != digit_index { + // leave as-is + twos_in + } else if value { + // set bit + twos_in | bit_mask + } else { + // clear bit + twos_in & !bit_mask + }; + *d = negate_carry(twos_out, &mut carry_out); + } + } else { + if !value { + self.data.set_bit(bit, true); + } + } + } + } + // the top bit may have been cleared, so normalize + self.normalize(); + } } impl_sum_iter_type!(BigInt); diff --git a/src/biguint.rs b/src/biguint.rs index 28d2caf2..7173a3d6 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -2739,6 +2739,8 @@ impl BigUint { } else { if digit_index < self.data.len() { self.data[digit_index] &= !bit_mask; + // the top bit may have been cleared, so normalize + self.normalize(); } } } From 152bf6559fc69760eb10e47cca3717f9a85b0ea4 Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Thu, 31 Dec 2020 11:25:16 +0100 Subject: [PATCH 04/17] More tests and comments --- src/bigint.rs | 24 ++++++++++++++---------- tests/bigint.rs | 27 +++++++++++++++++++++++++++ tests/biguint.rs | 3 +++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/bigint.rs b/src/bigint.rs index 6fbc8007..18d717c8 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3259,9 +3259,9 @@ impl BigInt { /// uses the two's complement for negative numbers pub fn bit(&self, bit: u64) -> bool { // Let the binary representation of a number be - // x 1 0 ... 0 + // ... 0 x 1 0 ... 0 // Then the two's complement is - // !x 1 0 ... 0 + // ... 1 !x 1 0 ... 0 // where !x is obtained from x by flipping each bit let b = self.data.bit(bit); if self.is_negative() && bit > self.data.trailing_zeros().unwrap() { @@ -3275,6 +3275,7 @@ impl BigInt { 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; @@ -3283,25 +3284,28 @@ impl BigInt { Sign::Minus => { let bits_per_digit = u64::from(big_digit::BITS); if bit < bits_per_digit * self.len() as u64 { - let digit_index = (bit / bits_per_digit) as usize; + // This implementation corresponds to what the function `bitand_neg_pos` does when + // value=false and what `bitor_neg_pos` does when value=true, except there is no + // need to explicitly iterate over the digits of the right-hand side + 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 (i, d) in self.digits_mut().iter_mut().enumerate() { - let twos_in = negate_carry(*d, &mut carry_in); - let twos_out = if i != digit_index { - // leave as-is + 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 { - // set bit twos_in | bit_mask } else { - // clear bit twos_in & !bit_mask }; - *d = negate_carry(twos_out, &mut carry_out); + *digit = negate_carry(twos_out, &mut carry_out); } } else { + // The bit to set/clear is outside the represented digits, and thus more significant + // than the most significant bit of the current number. This corresponds to setting + // the bit to the negated value (no-op for value=true) if !value { self.data.set_bit(bit, true); } diff --git a/tests/bigint.rs b/tests/bigint.rs index af1b0d03..15d9ea6c 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -1316,10 +1316,37 @@ fn test_bit() { 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)); } diff --git a/tests/biguint.rs b/tests/biguint.rs index 53a2b4a1..5ff924d3 100644 --- a/tests/biguint.rs +++ b/tests/biguint.rs @@ -1828,4 +1828,7 @@ fn test_set_bit() { 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()); } From 8b6a1bbfdedca21532db8b69d77162b61e15b30d Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Fri, 1 Jan 2021 10:49:46 +0100 Subject: [PATCH 05/17] Add documentation to BitInt::set_bit --- src/bigint.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bigint.rs b/src/bigint.rs index 18d717c8..4d3d9cbd 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3271,6 +3271,8 @@ impl BigInt { } } + /// Sets or clears the bit in the given position, + /// uses the two's complement for negative numbers pub fn set_bit(&mut self, bit: u64, value: bool) { match self.sign { Sign::Plus => self.data.set_bit(bit, value), From f7199bdcfba21c1a94bab209d66b8e0f9447dc1c Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Fri, 1 Jan 2021 11:50:53 +0100 Subject: [PATCH 06/17] Check special case for set_bit for negative number --- src/bigint.rs | 22 +++++++++++----------- tests/bigint.rs | 4 ++++ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/bigint.rs b/src/bigint.rs index 4d3d9cbd..45933b7b 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3285,10 +3285,17 @@ impl BigInt { } Sign::Minus => { let bits_per_digit = u64::from(big_digit::BITS); - if bit < bits_per_digit * self.len() as u64 { - // This implementation corresponds to what the function `bitand_neg_pos` does when - // value=false and what `bitor_neg_pos` does when value=true, except there is no - // need to explicitly iterate over the digits of the right-hand side + // 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 let bit_index = (bit / bits_per_digit) as usize; let bit_mask = (1 as BigDigit) << (bit % bits_per_digit); let mut carry_in = 1; @@ -3304,13 +3311,6 @@ impl BigInt { }; *digit = negate_carry(twos_out, &mut carry_out); } - } else { - // The bit to set/clear is outside the represented digits, and thus more significant - // than the most significant bit of the current number. This corresponds to setting - // the bit to the negated value (no-op for value=true) - if !value { - self.data.set_bit(bit, true); - } } } } diff --git a/tests/bigint.rs b/tests/bigint.rs index 15d9ea6c..b96b4929 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -1349,4 +1349,8 @@ fn test_set_bit() { 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))); } From f0c3374b387d7ee6b249537b468c61695ece9b5d Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Fri, 1 Jan 2021 12:10:09 +0100 Subject: [PATCH 07/17] Check special case in BigInt::bit --- src/bigint.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/bigint.rs b/src/bigint.rs index 45933b7b..315881c7 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3263,11 +3263,12 @@ impl BigInt { // Then the two's complement is // ... 1 !x 1 0 ... 0 // where !x is obtained from x by flipping each bit - let b = self.data.bit(bit); - if self.is_negative() && bit > self.data.trailing_zeros().unwrap() { - !b + 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 { - b + self.data.bit(bit) } } From 39b89cbb5ce520092de93536f57e2858da1943b6 Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Fri, 1 Jan 2021 14:43:28 +0100 Subject: [PATCH 08/17] Fix some formatting --- src/biguint.rs | 10 ++++------ tests/bigint.rs | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/biguint.rs b/src/biguint.rs index 7173a3d6..6a3588f1 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -2736,12 +2736,10 @@ impl BigUint { self.data.resize(digit_index + 1, 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(); - } + } else if digit_index < self.data.len() { + self.data[digit_index] &= !bit_mask; + // the top bit may have been cleared, so normalize + self.normalize(); } } } diff --git a/tests/bigint.rs b/tests/bigint.rs index b96b4929..cf504409 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -1342,15 +1342,27 @@ fn test_set_bit() { 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))); + 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))); + 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))); + 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))); + assert_eq!( + x, + BigInt::from_biguint(Minus, (BigUint::one() << 200) - (BigUint::one() << 40)) + ); } From 8d6d388741c617bb480a79bbb942b67fd25c8925 Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Sat, 16 Jan 2021 16:37:42 +0100 Subject: [PATCH 09/17] Some conversion and overflow checks --- src/bigint.rs | 6 +++--- src/biguint.rs | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/bigint.rs b/src/bigint.rs index 315881c7..42f239a3 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3256,7 +3256,7 @@ impl BigInt { } /// Returns whether the bit in position `bit` is set, - /// uses the two's complement for negative numbers + /// using the two's complement for negative numbers pub fn bit(&self, bit: u64) -> bool { // Let the binary representation of a number be // ... 0 x 1 0 ... 0 @@ -3273,7 +3273,7 @@ impl BigInt { } /// Sets or clears the bit in the given position, - /// uses the two's complement for negative numbers + /// using the two's complement for negative numbers pub fn set_bit(&mut self, bit: u64, value: bool) { match self.sign { Sign::Plus => self.data.set_bit(bit, value), @@ -3297,7 +3297,7 @@ impl BigInt { // 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 - let bit_index = (bit / bits_per_digit) as usize; + let bit_index = (bit / bits_per_digit).to_usize().unwrap(); let bit_mask = (1 as BigDigit) << (bit % bits_per_digit); let mut carry_in = 1; let mut carry_out = 1; diff --git a/src/biguint.rs b/src/biguint.rs index 0790ff23..773cba52 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -2729,19 +2729,23 @@ impl BigUint { /// 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; + let digit_index = (bit / bits_per_digit).to_usize().unwrap(); 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 + /// + /// 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) { let bits_per_digit = u64::from(big_digit::BITS); - let digit_index = (bit / bits_per_digit) as usize; + let digit_index = (bit / bits_per_digit).to_usize().unwrap(); 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); + let new_len = digit_index.checked_add(1).unwrap(); + self.data.resize(new_len, 0); } self.data[digit_index] |= bit_mask; } else if digit_index < self.data.len() { From 49a14b569454ef5e2b38594442810ba6231ed7b5 Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Sun, 17 Jan 2021 09:14:27 +0100 Subject: [PATCH 10/17] bit and set_bit improvements --- src/bigint.rs | 82 ++++++++++++++++++++++++++++++++----------------- tests/bigint.rs | 23 ++++++++++---- 2 files changed, 71 insertions(+), 34 deletions(-) diff --git a/src/bigint.rs b/src/bigint.rs index 42f239a3..834e94f5 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3258,15 +3258,22 @@ impl BigInt { /// Returns whether the bit in position `bit` is set, /// using the two's complement for negative numbers 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) + 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 bit.cmp(&trailing_zeros) { + Ordering::Less => false, + Ordering::Equal => true, + Ordering::Greater => !self.data.bit(bit), + } + } } else { self.data.bit(bit) } @@ -3278,39 +3285,58 @@ impl BigInt { 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; + } else { + // clearing a bit for zero is a no-op } } 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); + if bit >= bits_per_digit * self.len() as u64 { + if !value { + self.data.set_bit(bit, true); + } } 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 - let bit_index = (bit / bits_per_digit).to_usize().unwrap(); - 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 trailing_zeros = self.data.trailing_zeros().unwrap(); + if bit > trailing_zeros { + self.data.set_bit(bit, !value); + } else if bit < trailing_zeros && !value { + // bit is already cleared + } else if bit == trailing_zeros && value { + // bit is already set + } else { + // general case + let bit_index = (bit / bits_per_digit).to_usize().unwrap(); + let bit_mask = (1 as BigDigit) << (bit % bits_per_digit); + let mut carry_in = 1; + let mut carry_out = 1; + let mut digit_iter = self.digits_mut().iter_mut().skip(bit_index); + + let digit = digit_iter.next().unwrap(); let twos_in = negate_carry(*digit, &mut carry_in); - let twos_out = if index != bit_index { - twos_in - } else if value { + let twos_out = if value { + // set bit twos_in | bit_mask } else { + // clear bit twos_in & !bit_mask }; *digit = negate_carry(twos_out, &mut carry_out); + + for digit in digit_iter { + if carry_in == 0 && carry_out == 0 { + // no more digits will change + break; + } + let twos = negate_carry(*digit, &mut carry_in); + *digit = negate_carry(twos, &mut carry_out); + } + + if carry_out != 0 { + self.digits_mut().push(1 as BigDigit); + } } } } diff --git a/tests/bigint.rs b/tests/bigint.rs index cf504409..8de4f4a8 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -1311,12 +1311,12 @@ fn test_pow() { #[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)); + 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)); // -12 = (...110100)_2 assert!(!BigInt::from(-12i8).bit(0)); assert!(!BigInt::from(-12i8).bit(1)); @@ -1365,4 +1365,15 @@ fn test_set_bit() { 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)); } From 30ec1f8212aa4c6c5f7f1d91441bfcf42576732e Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Sun, 17 Jan 2021 16:13:28 +0100 Subject: [PATCH 11/17] Better handling of set_bit for negative numbers --- src/bigint.rs | 118 ++++++++++++++++++++++++++++++++---------------- tests/bigint.rs | 23 +++++++++- 2 files changed, 100 insertions(+), 41 deletions(-) diff --git a/src/bigint.rs b/src/bigint.rs index 834e94f5..5db434cc 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3268,10 +3268,10 @@ impl BigInt { true } else { let trailing_zeros = self.data.trailing_zeros().unwrap(); - match bit.cmp(&trailing_zeros) { - Ordering::Less => false, - Ordering::Equal => true, - Ordering::Greater => !self.data.bit(bit), + match Ord::cmp(&bit, &trailing_zeros) { + Less => false, + Equal => true, + Greater => !self.data.bit(bit), } } } else { @@ -3289,7 +3289,7 @@ impl BigInt { self.data.set_bit(bit, true); self.sign = Sign::Plus; } else { - // clearing a bit for zero is a no-op + // Clearing a bit for zero is a no-op } } Sign::Minus => { @@ -3299,49 +3299,87 @@ impl BigInt { 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 { - // bit is already cleared - } else if bit == trailing_zeros && value { - // bit is already set - } else { - // general case - let bit_index = (bit / bits_per_digit).to_usize().unwrap(); - let bit_mask = (1 as BigDigit) << (bit % bits_per_digit); - let mut carry_in = 1; - let mut carry_out = 1; - let mut digit_iter = self.digits_mut().iter_mut().skip(bit_index); - - let digit = digit_iter.next().unwrap(); - let twos_in = negate_carry(*digit, &mut carry_in); - let twos_out = if value { - // set bit - twos_in | bit_mask - } else { - // clear bit - twos_in & !bit_mask - }; - *digit = negate_carry(twos_out, &mut carry_out); - - for digit in digit_iter { - if carry_in == 0 && carry_out == 0 { - // no more digits will change - break; + match Ord::cmp(&bit, &trailing_zeros) { + Less => { + if value { + // We need to 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 + // We do this 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 = BigDigit::MAX << (bit % bits_per_digit); + let bit_mask_hi = BigDigit::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] = BigDigit::MAX; + } + digits[index_hi] ^= bit_mask_hi; + } + } else { + // Bit is already cleared } - let twos = negate_carry(*digit, &mut carry_in); - *digit = negate_carry(twos, &mut carry_out); } - - if carry_out != 0 { - self.digits_mut().push(1 as BigDigit); + Equal => { + if value { + // Bit is already set + } else { + // Clearing the bit at position `trailing_zeros` is the only non-trivial + // case and 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 must have + // `carry_in` = `carry_out` = 1 + 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); + } + } + } + Greater => { + self.data.set_bit(bit, !value); } } } } } - // the top bit may have been cleared, so normalize + // The top bit may have been cleared, so normalize self.normalize(); } } diff --git a/tests/bigint.rs b/tests/bigint.rs index 8de4f4a8..7d2678c3 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -1328,9 +1328,18 @@ fn test_bit() { #[test] fn test_set_bit() { - let mut x = BigInt::zero(); + 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); @@ -1338,6 +1347,7 @@ fn test_set_bit() { 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)); @@ -1359,6 +1369,13 @@ fn test_set_bit() { 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!( @@ -1376,4 +1393,8 @@ fn test_set_bit() { 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)); } From 223bbc37781cf3b0cf18755f7b3345a582beaa16 Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Mon, 18 Jan 2021 14:48:44 +0100 Subject: [PATCH 12/17] Minor restructuring of BigInt::set_bit --- src/bigint.rs | 121 +++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 65 deletions(-) diff --git a/src/bigint.rs b/src/bigint.rs index 5db434cc..a95f8af0 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3306,75 +3306,66 @@ impl BigInt { // |-- bit at position 'trailing_zeros' // where !x is obtained from x by flipping each bit let trailing_zeros = self.data.trailing_zeros().unwrap(); - match Ord::cmp(&bit, &trailing_zeros) { - Less => { - if value { - // We need to 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 - // We do this 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 = BigDigit::MAX << (bit % bits_per_digit); - let bit_mask_hi = BigDigit::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] = BigDigit::MAX; - } - digits[index_hi] ^= bit_mask_hi; - } - } else { - // Bit is already cleared + 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. + 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); } - Equal => { - if value { - // Bit is already set - } else { - // Clearing the bit at position `trailing_zeros` is the only non-trivial - // case and 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 must have - // `carry_in` = `carry_out` = 1 - 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); - } - } + + 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); } - Greater => { - self.data.set_bit(bit, !value); + } 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 + // We do this 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 } } } From df4ef9d3b5f05032dc48347e76a378b97400038d Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Mon, 18 Jan 2021 15:06:43 +0100 Subject: [PATCH 13/17] Fix formatting (doh) --- src/bigint.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bigint.rs b/src/bigint.rs index a95f8af0..a027676b 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3346,8 +3346,7 @@ impl BigInt { // bit_mask: 1 1 ... 1 0 .. 0 // We do this 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 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)); From d95583c6ea97d809be571dfa3141bc5918a0f99d Mon Sep 17 00:00:00 2001 From: Jan Marthedal Rasmussen Date: Tue, 19 Jan 2021 13:52:27 +0100 Subject: [PATCH 14/17] Some more comments --- src/bigint.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/bigint.rs b/src/bigint.rs index a027676b..82d43db1 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -3281,6 +3281,10 @@ impl BigInt { /// 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), @@ -3312,7 +3316,8 @@ impl BigInt { // 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. + // 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); @@ -3344,7 +3349,7 @@ impl BigInt { // |-- bit at position 'bit' // |-- bit at position 'trailing_zeros' // bit_mask: 1 1 ... 1 0 .. 0 - // We do this by xor'ing with the bit_mask + // 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); @@ -3355,7 +3360,7 @@ impl BigInt { if index_lo == index_hi { digits[index_lo] ^= bit_mask_lo & bit_mask_hi; } else { - digits[index_lo] ^= bit_mask_lo; + digits[index_lo] = bit_mask_lo; for index in (index_lo + 1)..index_hi { digits[index] = big_digit::MAX; } @@ -3363,8 +3368,8 @@ impl BigInt { } } else { // We end up here in two cases: - // * bit == trailing_zeros && value: Bit is already set - // * bit < trailing_zeros && !value: Bit is already cleared + // bit == trailing_zeros && value: Bit is already set + // bit < trailing_zeros && !value: Bit is already cleared } } } From 5aaf3698444f3d10bc743488ded3d96599b65ec7 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 24 Feb 2021 12:05:11 -0800 Subject: [PATCH 15/17] Let BigUint::bit work to the extreme u64::MAX 32-bit targets would fail to convert that to a `usize` digit index, but we can reasonably say that all bits are false at that point. --- src/biguint.rs | 10 +++++++--- tests/bigint.rs | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/biguint.rs b/src/biguint.rs index 773cba52..5c9a0dbe 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -2729,9 +2729,13 @@ impl BigUint { /// 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).to_usize().unwrap(); - digit_index < self.data.len() - && (self.data[digit_index] & ((1 as BigDigit) << (bit % bits_per_digit))) != 0 + 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 diff --git a/tests/bigint.rs b/tests/bigint.rs index 7d2678c3..8eff5ba7 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -1317,6 +1317,7 @@ fn test_bit() { 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)); @@ -1324,6 +1325,7 @@ fn test_bit() { 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] From d1e854edb3c6d1d8ec7abe5cc9c4eed9c7a1f921 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 24 Feb 2021 12:15:48 -0800 Subject: [PATCH 16/17] Saturate the conversions in BitUint::set_bit --- src/biguint.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/biguint.rs b/src/biguint.rs index 5c9a0dbe..88365aa3 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -2743,12 +2743,16 @@ impl BigUint { /// 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(); + 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.checked_add(1).unwrap(); + let new_len = digit_index.saturating_add(1); self.data.resize(new_len, 0); } self.data[digit_index] |= bit_mask; From 69f654e4b6b9d2e9adbd124042c8c396f7328e7c Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 24 Feb 2021 12:58:50 -0800 Subject: [PATCH 17/17] tighten ci/big_quickcheck dependencies --- ci/big_quickcheck/Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ci/big_quickcheck/Cargo.toml b/ci/big_quickcheck/Cargo.toml index d061757f..c58f6d47 100644 --- a/ci/big_quickcheck/Cargo.toml +++ b/ci/big_quickcheck/Cargo.toml @@ -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 = "../.."