danlehmann / arbitrary-int

A modern and lightweight implementation of arbitrary integers for Rust
MIT License
30 stars 12 forks source link

Support remaining arithmetic operations #32

Open danlehmann opened 1 year ago

danlehmann commented 1 year ago

Basic types like u32 have plenty more operations, like:

And plenty more. All of those should be supported.

Some of that is already supported through num_traits, but a) not everyone might want that dependency and b) that is only a subset of the functionality.

pickx commented 1 year ago

as a starting point, here's a list of methods currently implemented for u32 but not UInt:

checked versions of existing operations

unchecked versions of existing operations

wrapping versions of existing operations

overflowing versions of existing operations

saturating versions of existing operations

carrying versions of existing operations

neg

rem

rem_euclid

add_signed

div_euclid

ilog

pow

next_multiple_of

power_of_two

other unimplemented operations

danlehmann commented 1 year ago

Thanks a lot for collecting those!

pickx commented 1 year ago

@danlehmann I can implement some of these if that's okay with you.

on that note, is there a reason why Mul and Div are not implemented? also, is there a rationale behind the trait bounds on the T for existing core::ops implementations, some of which are unnecessary for the implementation itself? for example, Shl<usize, Output = T> + Shr<usize, Output = T> + From<u8>?

danlehmann commented 1 year ago

Yes of course, happy to take patches for those!

I have been thinking about those a bit, so I wanted to write down my thoughts.

For many operations we can defer to the underlying type, e.g. wrapping_add should be straight-forward: just add and apply the MASK.

wrapping_xxx likely needs a two-step process though: 1. Do the wrapping_xxx instruction of the underlying type and b) do a try_new and - if error - consider that a wrap as well. But I think for some things we can do better:

danlehmann commented 1 year ago

on that note, is there a reason why Mul and Div are not implemented?

Just an oversight

also, is there a rationale behind the trait bounds on the T for existing core::ops implementations, some of which are unnecessary for the implementation itself? for example, Shl<usize, Output = T> + Shr<usize, Output = T> + From<u8>?

I put in whatever was needed at the time. At some point the Number trait simplified things somewhat though, so maybe some of those aren't needed any longer. If you find any unnecessary ones, removing should be safe from an api stability viewpoint

danlehmann commented 10 months ago

I implemented a good chunk of them: Mul, MulAssign, Div, DivAssign as well as all wrapping_xxx and saturating_xxx:

https://github.com/danlehmann/arbitrary-int/pull/39

For each instruction there are some interesting optimizations: E.g. u4 u4 can never overflow the underlying u8, where u5u5 can. I tried to implement those (which are compiler checked) whenever reasonable.

Will do overflowing_xxx and checked_xxx soon as well.

danlehmann commented 10 months ago

Skipping unchecked_xxx. They are all unstable for now.

In the patch I just referenced I also implemented checked_xxx and overflowing_xxx.

Once that's in, the list of missing operations should be a lot smaller

danlehmann commented 10 months ago

Landed https://github.com/danlehmann/arbitrary-int/pull/39.

This leaves the following:

unchecked versions of existing operations

carrying versions of existing operations

neg

rem

rem_euclid

add_signed

div_euclid

ilog

pow

next_multiple_of

power_of_two

other unimplemented operations