-
Notifications
You must be signed in to change notification settings - Fork 188
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
two's-complement logic operations on BigInt #26
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just moved the tests around in #27, which causes a conflict here. I suggest making a standalone tests/bigint_bitwise.rs
for your new tests.
I haven't looked too closely at the implementation details, but it seems pretty thorough AFAICT. Getting the tests absolutely right helps confidence here. Maybe some randomized i64
inputs would be a good idea for comparison too, in case there are edge cases you didn't think about.
src/tests/bigint.rs
Outdated
N, | ||
P(&'static [BigDigit]), | ||
M(&'static [BigDigit]), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest use V::*;
for readability, to get rid of the V::
prefix everywhere else.
src/tests/bigint.rs
Outdated
assert_eq!(a.clone() | &b, or, "{:x} | {:x}", a, b); | ||
assert_eq!(b.clone() | &a, or, "{:x} | {:x}", b, a); | ||
assert_eq!(a.clone() ^ &b, xor, "{:x} ^ {:x}", a, b); | ||
assert_eq!(b.clone() ^ &a, xor, "{:x} ^ {:x}", b, a); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to also have these tests compared to the primitive operators, probably just with i64
, at least for those where the values do fit. Just as a sanity check that your test setup is correct.
src/tests/bigint.rs
Outdated
let a = BigInt::from(a); | ||
let not = BigInt::from(not); | ||
assert_eq!(!a.clone(), not, "!{:x}", a); | ||
assert_eq!(!not.clone(), a, "!{:x}", not); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too, I think comparing to i64
is worthwhile.
src/bigint.rs
Outdated
fn not(mut self) -> BigInt { | ||
match self.sign { | ||
Sign::NoSign => { | ||
biguint::digits_mut(&mut self.data).push(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We support scalar arithmetic -- why not just self.data += 1
?
src/bigint.rs
Outdated
self.sign = Sign::Minus; | ||
} | ||
Sign::Minus => { | ||
sub2(biguint::digits_mut(&mut self.data), &[1]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.data -= 1
?
src/bigint.rs
Outdated
let carry = __add2(biguint::digits_mut(&mut self.data), &[1]); | ||
if carry != 0 { | ||
biguint::digits_mut(&mut self.data).push(carry); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.data += 1
?
src/bigint.rs
Outdated
|
||
// -1 ^ +ff = ...ffff ^ ...00ff = ...ff00 = +100 | ||
// -ff ^ +1 = ...ff01 ^ ...0001 = ...ff00 = +100 | ||
// answer is pos, has length of longest with a possible carry |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
answer is neg, but I think you got that right in practice.
99876ce
to
94de40d
Compare
I addressed your points in the first commit. In the second commit I added tests on |
I've added a third commit which makes the inline docs more helpful. I also made the use of the |
14a0976
to
d139cf4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor things can be simplified, then I'm happy. I'm impressed by how well the single abstraction of negate_carry
makes this viable!
Let's not forget the val/ref combinations too, but that can be done later, as you say.
src/bigint.rs
Outdated
self.assign_from_slice(Sign::NoSign, &[]); | ||
return; | ||
} | ||
(Sign::Plus, Sign::Plus) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this just use self.data &= other.data
?
src/bigint.rs
Outdated
self.assign_from_slice(other.sign, biguint::digits(&other.data)); | ||
return; | ||
} | ||
(Sign::Plus, Sign::Plus) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be self.data |= other.data
.
src/bigint.rs
Outdated
self.assign_from_slice(other.sign, biguint::digits(&other.data)); | ||
return; | ||
} | ||
(Sign::Plus, Sign::Plus) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be self.data ^= other.data
.
src/biguint.rs
Outdated
@@ -1686,6 +1686,24 @@ pub fn trailing_zeros(u: &BigUint) -> Option<usize> { | |||
.map(|(i, digit)| i * big_digit::BITS + digit.trailing_zeros() as usize) | |||
} | |||
|
|||
// needed since we cannot set visibility of `BigUint::data` to `pub(crate)` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should just move struct BigUint
to the crate root, and maybe its impl { fn normalize() }
too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand this point. I have done something in the new commit 3 (crate-visible IntDigits) that maybe addresses this, but I'm not sure if that's what you had in mind. Anyway, I think further changes here can be independent of this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant that if we moved the struct definition to the crate root, then everything could just access the data
field directly. The digits_mut
function especially was defeating any pretense of privacy within the crate anyway.
But now with your trait, I see how this is helpful to present a common interface for the macros, so this solution seems fine.
src/bigint.rs
Outdated
fn not(mut self) -> BigInt { | ||
match self.sign { | ||
Sign::NoSign | Sign::Plus => { | ||
self.data += 1 as BigDigit; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you got tripped up by this defaulting to 1i32
? Let's just use 1u32
instead of casting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You guessed right :)
269ade7
to
57c3a0e
Compare
Commit 1 is now the implementation and commit 2 is the tests. Commit 3 introduces a crate-visible Commit 4 implements ref/val forwarding for the bitwise ops of |
I took the small liberty of adding reference bors r+ |
26: two's-complement logic operations on BigInt r=cuviper a=tspiteri This commit adds the following impls, giving results equivalent to what would be expected with two's complement. * `impl Not for BigInt` * `impl BitAnd<&BigInt> for BigInt`, `impl BitAndAssign<&BigInt> for BigInt` * `impl BitOr<&BigInt> for BigInt`, `impl BitOrAssign<&BigInt> for BigInt` * `impl BitXor<&BigInt> for BigInt`, `impl BitXorAssign<&BigInt> for BigInt` I also added some tests. There is most probably some room for optimization, but the bitwise operations all operate in place using a single pass. I did not add other implementations such as `impl BitAnd<BigInt> for BigInt`, those can be done later. Edit: Fixes #13
Build succeeded |
This commit adds the following impls, giving results equivalent to what would be expected with two's complement.
impl Not for BigInt
impl BitAnd<&BigInt> for BigInt
,impl BitAndAssign<&BigInt> for BigInt
impl BitOr<&BigInt> for BigInt
,impl BitOrAssign<&BigInt> for BigInt
impl BitXor<&BigInt> for BigInt
,impl BitXorAssign<&BigInt> for BigInt
I also added some tests. There is most probably some room for optimization, but the bitwise operations all operate in place using a single pass.
I did not add other implementations such as
impl BitAnd<BigInt> for BigInt
, those can be done later.Edit: Fixes #13