rust-lang / libs-team

The home of the library team
Apache License 2.0
110 stars 18 forks source link

ACP: `isqrt` for `NonZero<uN>` #391

Closed ivan-shrimp closed 3 weeks ago

ivan-shrimp commented 4 weeks ago

Proposal

Problem statement

Integer primitives now have an integer square root method isqrt. Positive integers always have a positive square root, and it would be natural to directly express this through NonZero.

Motivating examples or use cases

x.pow(2).isqrt() gives x for x: uN, and we can extend that to NonZero<uN>.

Solution sketch

// N = 8/16/32/64/128/size
impl NonZero<uN> {
    const fn isqrt(self) -> Self {
        // should behave as if:
        unsafe { NonZero::new_unchecked(self.get().isqrt()) }
    }
}

Alternatives

Links and related work

Motivation for primitive isqrt: https://github.com/rust-lang/rust/issues/89273 Tracking issue for isqrt: https://github.com/rust-lang/rust/issues/116226 Suggestion for this function: https://github.com/rust-lang/rust/issues/116226#issuecomment-2144225174 Implementation: https://github.com/rust-lang/rust/pull/126199

What happens now?

This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

Second, if there's a concrete solution:

kennytm commented 4 weeks ago

My motivation suggesting NonZero<uN>::isqrt() in https://github.com/rust-lang/rust/issues/116226#issuecomment-2144225174 was that, given a natural number $N > 0$, compute the "middle" value $M = \lfloor\sqrt N\rfloor > 0$ to decompose every number $i \in \mathbb ZN$ into $(a,b) \in \mathbb Z{\lceil N/M\rceil} \times \mathbb Z_M$ with $i = aM+b$. The $(a,b)$ pair is then feed through a Feistel network in order to perform something like format-preserving encryption.

With N: NonZero<u64> we can use NonZero::isqrt() to get M: NonZero<u64> so that (a, b) = (i / M, i % M) are panic-free.

scottmcm commented 3 weeks ago

(Disclaimer: I'm not libs-api) This seems entirely reasonable to me, under the same "it's helpful compared to .get().isqrt()" that gave us NonZero::count_ones and NonZero::ilog2.

pitaj commented 3 weeks ago

For the future, the following operations could have the same function signature fn(NonZero<T>) -> NonZero<T>

the8472 commented 3 weeks ago

We discussed this during today's libs-api meeting and adding isqrt was accepted. Additional methods should go in a separate ACP.