Robbepop / apint

Arbitrary precision integers library.
Other
27 stars 4 forks source link

Do something about `assign` and general ApInt construction #44

Open AaronKutch opened 5 years ago

AaronKutch commented 5 years ago

Here is a brief compilation of problems I have seen:

AaronKutch commented 5 years ago

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 commented 5 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 2 years ago

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 commented 2 years ago

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.