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

Return the remainder of division by a primitive (unsigned) integer in the form of a primitive integer of that type #233

Open
JohnScience opened this issue Dec 31, 2021 · 1 comment

Comments

@JohnScience
Copy link

JohnScience commented Dec 31, 2021

I found myself writing this rather ugly function:

// TODO: decompose the function and maybe share the results with the library authors
#[cfg(any(test, feature = "num-bigint"))]
impl super::GetLastDigitAsU8 for num_bigint::BigUint {
    fn get_last_digit_as_u8(&self) -> u8 {
        const DEFAULT_RADIX: u8 = 10;
        // Unfortunately, the library doesn't offer a way to return the remainder
        // of the divisor's type even for primitive integers.
        let rem: Self = self % DEFAULT_RADIX;
        let rem: u32 = {
            let least_significant_digit: u32 = {
                // TODO: contact the library authors to make endianness explicit. Read more about endianness:
                // https://en.wikipedia.org/wiki/Endianness
                let mut le_iter_u32 = rem.iter_u32_digits();
                le_iter_u32.next()
                    // integers always have at least one digit
                    .unwrap()
            };
            least_significant_digit
        };
        let rem: u8 = rem.try_into()
            // that number is the remainder of division by 10 and therefore < 10 < 2^8
            .unwrap();
        rem
    }
}

As you can read from the comments, I would like to have an ability to check that num_bigint::U32Digits that is returned from num_bigint::BigUint::iter_u32_digits() with a debug_assert!().

One way how I can see it implemented is using Endianness enum with two variants, Big and Little. Then Endianness::Little can be the value for associated constant, let's name it ENDIANNESS, of num_bigint::U32Digits and num_bigint::U64Digits. There are also many such crates on crates.io.

The second thing I might want to see in the library is a set of functions (or a generic function relying on num_traits::int::PrimInt) that would allow to return the remainder of division by a primitive integer in the form of a primitive type instead of num_bigint::BigUint or num_bigint::BigInt.

The reason why library users (including me) might not want to have these functions in the library is possible desire to keep num-bigint as small as possible and then the solution would be to delegate the responsibility of implementing those functions to a separate library.

@JohnScience JohnScience changed the title Suggested way to return the remainder of division by a primitive (unsigned) integer Suggested way to return the remainder of division by a primitive (unsigned) integer in the form of a primitive integer of that type Dec 31, 2021
@JohnScience JohnScience changed the title Suggested way to return the remainder of division by a primitive (unsigned) integer in the form of a primitive integer of that type Return the remainder of division by a primitive (unsigned) integer in the form of a primitive integer of that type Dec 31, 2021
@cuviper
Copy link
Member

cuviper commented Feb 13, 2023

Both iter_u32_digits and iter_u64_digits are explicitly documented as being "ordered least significant digit first." That's roughly little-endian, although we're not talking about byte order -- the u32/u64 digits that come out will still be in the target's native endianness. If you're doing math like digit % RADIX, then native is what you want.

                le_iter_u32.next()
                    // integers always have at least one digit
                    .unwrap()

That comment is not true -- 0 is totally empty in BigInt and BigUint. I suggest using .unwrap_or(0) instead.

Regardless, the last u32 digit is not sufficient to determine the last radix-10 digit. Consider 232 = 4,294,967,296 -- the u32 digits (least-sig first) are [0, 1], but the last radix-10 digit is 6.

The second thing I might want to see in the library is a set of functions (or a generic function relying on num_traits::int::PrimInt) that would allow to return the remainder of division by a primitive integer in the form of a primitive type instead of num_bigint::BigUint or num_bigint::BigInt.

There's an old proposal in #88. I also considered changing Rem<{integer}> automatically narrow its Output, but I think when I experimented with that it had quite a large impact (but I don't fully remember now). For a smaller scope, we could probably just add rem_u32 and rem_u64 methods that would cover a lot of use-cases.

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