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

Do something about assign and general ApInt construction #44

Open
AaronKutch opened this issue Jul 14, 2019 · 6 comments
Open

Do something about assign and general ApInt construction #44

AaronKutch opened this issue Jul 14, 2019 · 6 comments

Comments

@AaronKutch
Copy link
Contributor

Here is a brief compilation of problems I have seen:

  • perhaps assign should be what strict_assign is
  • I have never used assign or strict_assign and Robbepop dislikes the API
  • It is extremely painful to construct ApInts from a bit slice of another ApInt (with current functions, I have to do cloning, shifting, truncating, extending, and sometimes another shifting)
  • Often, inplace binary ops with one immutable and one mutable ApInt is all that is needed, but sometimes there is a need for two immutable inputs and a mutable output. However, we really do not want to greatly multiply the number of functions. At most, we should add the classical 2 input, 1 output functions to ApInts only.
  • Inplace ops are great for nearly 100% of use case operations involving carries, but commonly in bitwise ops we want to perform operations between specific bit slices and output that to a bit slice of a third ApInt.
  • Sometimes, we want to do bitwise ops between bit slices of the same ApInt.
@AaronKutch
Copy link
Contributor Author

Using the Rust tradition of immutable and mutable "slice" views into a data structure will not work if two of the bit slices are on the same ApInt. I propose introducing a three macros:

The first will accept a single ApInt as a mutable reference and up to three ranges, specifying the bit ranges of two inputs and one output for a binary operation. There will be a runtime check that the ranges are all equal in size, otherwise an error will be returned. Ranges are allowed to overlap (which is how inplace ops can happen) and maybe even allow for wrapping around the ends of the ApInt (e.g. 60..8 on a 64 bit ApInt means that the top 4 bits and 8 bottom bits are used. This will be great for circular bit buffers). If there are only two ranges supplied, then a unary operation is going to be applied. One range is a simple in place unary op.

The second accepting two mutable ApInts I will have to try to contruct myself and report back.

The third will use three ApInts and always needs three ranges specified, each corresponding to a separate ApInt. I probably

This will be a huge undertaking for me of course, and I do not plan to try this until most every other issue is fixed. In the meantime, maybe we should just introduce something purely for assignment of bits.

@AaronKutch
Copy link
Contributor Author

AaronKutch commented Aug 22, 2019

I need signed and unsigned resizing functions which check if the numeric value overflows (there are set bits that are cutoff for the unsigned resize, or there are unset bits that are cutoff for negative signed ApInts). The _extend functions are fine as they are. I think overflowing_unsigned_truncate (maybe _utruncate?) and overflowing_sign_resize functions would be the name for the new function.
Should we then rename the existing truncate to wrapping_truncate and so forth? I think some users may assume that the resizing functions are checking for overflow already, so this rename can be an improvement.

@AaronKutch
Copy link
Contributor Author

We can use the new IntoIterator<Item = T> by-value iteration soon, which I think can help with constructor ergonomics. Just putting this here to remind my future self about the change.

@AaronKutch
Copy link
Contributor Author

AaronKutch commented Dec 20, 2019

In university I learned about how Verilog has a curly brackets operator which manages concatenating arbitrary precision integers and arbitrary amounts of 1s and 0s together. I believe a rusty variation will be the perfect solution for this issue and can compactly create a constant apints as a side effect, fixing issues I have with BitWidth::try_from() being everywhere.
For creating constants, for example, the syntax could be like con!{-1234567i42} for a 42 bit integer with value -1234567 sign extended. Arbitrary Sign extentions, concatenations, and shiftings would look like con!{0u7, x[7..63], {x[63]}64} (argument order endianness reversed from Verilog). Unlike Verilog, it can also accept variable ranges and check at runtime whether the indexes are out of range.

Creating constants from decimal and checking for overflow requires deserialization functions and allocations at compile time, which is the reason why a procedural macro is needed.
The procedural macro which would generate a series of range checks and bit slice copies, and is many PR improvements away from being possible, unfortunately.
Even if we get the construction and serialization PRs needed for this, we will have circular dependency problems between the apint crate and the procedural macro crate if the procedural macro crate uses the needed functions from the apint crate and the apint crate wants to in turn use the procedural macro (unless we want to copy and maintain a lot of stuff on the procedural macro side).
It isn't absolutely needed inside the apint crate, but would be very useful If I could have an internal stopgap when implementing Karatsuba multiplication and fixing the sprawling mess of division macros.

In order to break this circle of PR dependencies and literal dependencies, I think I will make an second macro_rules macro that can only handle only the constant arguments 0 and 1 and cannot do nested repeats. The apint crate will implement this second function. The public procedural macro crate will not exist until later when we have the simpler design stabilized and the serialization functions all working. The procedural macro crate will depend upon the internal function and some deserialization functions (only a one way dependency edge to the apint crate), and will add compile time constant making on top of the internal function.

The problem is that the internal function will need to be public in order for the procedural macro crate to use it (using special feature flags will cause the apint to compile twice when someone uses both crates, which we want to avoid). Maybe we could intentionally make it public as a alternative to cons!{} that doesn't accept constants and nested repeats.

@AaronKutch
Copy link
Contributor Author

I implemented this in my awint system of crates https://docs.rs/awint_macros/0.6.0/awint_macros/ . @Robbepop should I close all my old issues on this repo? If there are no further plans of maintenance I will close my issues to clean up my open issues list and also remove the crate from the list of alternatives on num-bigint.

@Robbepop
Copy link
Owner

I do not have plans to continue maintenance of apint because I think there are better alternatives and it originally was a side projects for stevia which itself it abandoned. That said, I think keeping your issues online is useful information.

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