Open AaronKutch opened 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 ApInt
s 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.
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.
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.
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 apint
s 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.
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.
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.
Here is a brief compilation of problems I have seen:
assign
should be whatstrict_assign
isassign
orstrict_assign
and Robbepop dislikes the APIApInt
s from a bit slice of anotherApInt
(with current functions, I have to do cloning, shifting, truncating, extending, and sometimes another shifting)ApInt
s only.ApInt
.