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

Enhancements for parity with primitives #140

Open
3 tasks
Kixiron opened this issue Mar 8, 2020 · 4 comments
Open
3 tasks

Enhancements for parity with primitives #140

Kixiron opened this issue Mar 8, 2020 · 4 comments

Comments

@Kixiron
Copy link

Kixiron commented Mar 8, 2020

For parity with primitives and other standard types, the following features would be amazing

  • core::ops::Not and std::ops::Not for BigUint
  • Checked operations for BigUint, e.g. checked_add
  • BigInt/BigUint with_capacity plus a method to determine the current int's capacity
@Kixiron
Copy link
Author

Kixiron commented Mar 8, 2020

Additionally, none of the checked_ operation variants are implemented for BigUint

@Kixiron Kixiron changed the title ops::Not is not implemented for BigUint Enhancements for parity with primatives Mar 8, 2020
@Kixiron Kixiron changed the title Enhancements for parity with primatives Enhancements for parity with primitives Mar 8, 2020
@cuviper
Copy link
Member

cuviper commented Mar 9, 2020

core::ops::Not and std::ops::Not for BigUint

Note, those traits are one and the same, defined in core and re-exported in std.

What would you want this to do with leading zeros? For a fixed-width size, that's no problem, just make them all ones, but BigUint is semantically infinite. We do have Not for BigInt because we can use negative values as if it were 2's complement, with an infinite prefix of ones. There's prior art for this in Python bitwise operators, where ~x is -x - 1.

Another option is to ignore leading zeros and just flip up to the most-significant bit, but that means the round trip !!x won't return to the same value. We could also flip the bits of whatever big-digits are currently allocated, but that exposes the internal digit size which may be different on some targets. We also have an invariant to never have 0 in the most-significant digit, but that would again break the !!x round trip if we trim that.

Checked operations for BigUint, e.g. checked_add

It does implement the trait CheckedAdd etc., but it just doesn't have the same inherent methods that you can use without importing the trait, like BigInt does. They're mostly useless except for checked_div dividing by 0, although I guess checked_sub also avoids negative cases for BigUint. For addition and multiplication, the only reason to have these is for generic code taking T: CheckedAdd, which doesn't need the inherent method at all.

BigInt/BigUint with_capacity plus a method to determine the current int's capacity

This doesn't fit the subject of "parity with primitives"...

I have thought about adding capacity methods, sort of related to #98. I would want this to be either in bits or bytes though, not exposing the internal digit size.

@Kixiron
Copy link
Author

Kixiron commented Mar 14, 2020

Sorry it took me so long to respond, I've been busy.

What would you want this to do with leading zeros?

I honestly didn't think this would have been as complex of a task as it turned out to be, but (forgive me if this is just plain stupid) couldn't iterating over the internal digits and applying Not to them suffice?
Ex.

for digit in self.data.iter_mut() {
    *digit = !*digit;
}

It does implement the trait CheckedAdd etc.

I'm sorry, I didn't see those!

This doesn't fit the subject of "parity with primitives"...

Forgive me, I was trying to make a title that fit the more general scope of the issue

I have thought about adding capacity methods [..] not exposing the internal digit size.

However it's exposed is fine, as long as there's a way to pre-allocate another Big{U}Int

Thank you for taking the time to respond!

@cuviper
Copy link
Member

cuviper commented Mar 18, 2020

couldn't iterating over the internal digits and applying Not to them suffice?

In the current master branch, the internal digit size varies by target for efficient computation, u32 on 32-bit targets and u64 on 64-bit, and this is considered an implementation detail that may yet change. But even as-is, that means !BigUint::one() as you suggest would return 0xfffffffe on 32-bit targets and 0xfffffffffffffffe on 64-bit. That seems bad.

We also don't keep any leading zeros, so BigUint::zero() is completely empty. Thus, if we just applied Not to all digits, !BigUint::zero() would still be zero!

For the round-trip problem, take a concrete example like let x = (BigUint::one() << 256) - 1u32 -- in binary that's 256 ones, and in digits that will be a size-dependent number of 0xff...ff digits. If we Not this digit-by-digit, they would all turn zero, and then for our no-leading-zeros invariant we'd trim it to an empty vector. If we Not it again, there are no digits left to change, so it remains zero. That means we have x != !!x, which would be pretty surprising.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants