Re: isqrt tweak and factor(1) questions

David Sparks via busybox <[email protected]>
Newsgroups gmane.linux.busybox
Message-ID <cZfEHq-FiJrfCy6TExuGOkd4D7_kpPQ9LAPwp3fnQLHwTO8tDQGcIVbeisH9doqXVT6YFizR9srZbMy8wTVaQGnIl7UblVjkgZVcfQ0t_u0=@proton.me>
Thank you for the reply!

On Wednesday, February 4th, 2026 at 22:48, Denys Vlasenko <[email protected]> wrote:
> On Tue, Feb 3, 2026 at 3:52 PM David Sparks [email protected] wrote:

> I'll add a separate version just for "unsigned long".

I was thinking, if you want minimum code size, it's fine to share,
just only compile it for "unsigned long long" if needed, with some
CONFIG_ trickery.  CONFIG_LONGER_ISQRT depends on CONFIG_FACTOR,
and bingo, no code duplication.

> Looks good. I don't think it's worth going to asm() tricks
> to make gcc optimize better.
> (It's better to spend time to improve gcc to do it better for everyone
> ...but yes, it's also much harder).

I was just pleased because it's a *completely portable* asm() trick.

> For any even remotely taxing integer to factorize, unpack_wheel()
> takes insignificant time to execute by comparison.

I was thinking more about code size than time.  I agree it's trivial,
but it can still be improved.
 
>> It could
>> easily be merged with the trial division loop, or the wheel could be
>> generated algorithmically without the compressed table.
>
> Adding more code to trial division loop will cause additional
> register pressure (=> slower code).

I should have a look.  Are you worried about speed or size more
here?

If you want an unpacked wheel, I can generate it on the fly with
smaller code than the current packed table, and barely more time.

Basically, have a 3*11 = 33 and 5*7 = 35-bit shift register
(circular right shifts are simple with "x = x << 32 | x >> 1;")
with bits set whenever multiples of 3, 5, 7 or 11 appear.  Then
just step through the values up to 2310+13 = 2323 by 2, recording
the distances between values for which ((mod33 | mod 35) & 1) == 0.

(Which can be implemented branch-free using the "unconditional store
and conditional pointer increment" trick from BlockQuicksort.)

The initial steps <= 13 can be handled by initializing the shift
registers with contents which omit the first occurrence of those
values in the lsbits.

For 32-bit, 3 shift registers of 15, 7 and 11 bits are probably better
than doing 64-bit shifts, but I'll check code size.

(Q: On which platform do you generally run bloat-o-meter?)

> I didn't go for full-blown, "actually smart" code for this,
> since I'm not sure we have users.
> 
> However, if you absolutely itching to implement something
> bigger but faster (or capable of >64bits),

I'm not "absolutely itching", but if you actually *want* one,
it's a SMOP.  Mostly I want to know what your appetite for
size/speed tradeoffs is.

> please do submit a patch.
> It can always be under a CONFIG option if it's "too big"
> to be unconditional.
> 
> Frankly, a serious implementation for this would be rather different,
> and maybe better be a whole separate code, mostly unrelated
> (IOW: not an addition of an ifdef forest to what we have here).

Yes, trial division would be a lot more limited.  

>> I'm not sure what the desired tradeoff here is. Modulo-n arithmetic
>> requires widening multiply for intermediate values, which is both slow
>> and a lot of code to emulate if the platform doesn't have it. Maybe
>> I should have a look at the arbitrary-precision code in dc(1)?
> 
> That one is decimal-based (-> slow).
> 
> TLS code has some arbitrary-precision binary code.

Ooh!  I should hack on that!  I notice that it lacks __int128
support, and that could be a big speed boost.  (Compiler support
can be tested with #ifdef __SIZEOF_INT128__.)

> I don't think so.
> 
> factorize(N) calls isqrt_odd(N) before factorizing.
> isqrt_odd(N) will detect that N=p^2
> (by discovering that isqrt(N)^2 = N)
> and will factorize p once (will just print its factors twice).
> 
> Tell me what I'm missing.

I screwed up my example.  If N = p^3, *then* it'll divide by p,
notice that p^2 is square, and start trial dividing again.

The most wasteful case is N = p^2 * q, where q ~ sqrt(p), which
will trial divide up to q twice.  ("Worst case" is a bit more
difficult to define.)

_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.