Skip to content

factor_one and friends - #30

Open
hvds wants to merge 6 commits into
danaj:masterfrom
hvds:factor_state
Open

factor_one and friends#30
hvds wants to merge 6 commits into
danaj:masterfrom
hvds:factor_state

Conversation

@hvds

@hvds hvds commented May 28, 2022

Copy link
Copy Markdown

Hi Dana, we've discussed this from time to time, I finally got round to having a go at implementing it - moving the core of factor() out to a new function factor_one() that returns one factor at a time. This will add a slight overhead for the additional calls, and management of the factor_state structure; but will give direct benefits for some of the functions that call factor() (is_semiprime, is_smooth, is_rough, is_powerful), and I think offers opportunities for further improvement.

  • add_factor now also takes an exponent, the two macros are removed;
  • fs_init, fs_close added to help with management of the factor_state structure;
  • removed the secondary power_factor() check; not sure how important that is, but can work on bringing it back if needed
  • the primary power_factor check no longer recurses, just sets a multiplier for exponents;
  • bonus function is_tau(n, k) returns true if tau(n) == k (should it be called has_divisors?);
  • handling 32-bit overflow on khi*khi needs more thought: maybe tlim should become mpz_t, or maybe khi should be reduced on 32-bit targets;
  • is_tau() needs more tests.

I'm not in a rush for this, I think it'd be a good idea to release what's currently in the repo before merging this, and it needs some polish in any case. In particular, I don't know which FS_LARGE tests are worth repeating after a factor is found, and which should be skipped - that could be handled either by adding more FS states or, say, by having a bitmask of tests to try (probably also copied with the pending factors onto the tofac_stack).

I'd welcome your thoughts.

hvds added 5 commits May 28, 2022 15:29
The new function returns one prime/exponent pair for each call,
using a new factor_state structure to keep track. To aid this, add_factor()
gets an additional parameter for the exponent.
is_semiprime, is_smooth, is_rough, is_powerful can all short-circuit
based on a single p^e factor.
@hvds

hvds commented May 29, 2022

Copy link
Copy Markdown
Author

Note that MPU t/26-issquarefree.t found the missing mpz_init when factorizing 815373060690029363516051578884163974. That appears to be the only test case across MPU and MPUG that reaches that point (finding a composite factor directly).

Comment thread factor.c
Comment on lines +420 to +425
/* TODO: What to do with composites we can't factor?
* Push them as "C#####" ?
* For now, just push them as if we factored.
*/
if (fs->log) gmp_printf("gave up on %Zd\n", fs->n);
goto found_factor;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record, Claude found that my local code equivalent to this chunk was not doing what the comment claimed, but effectively trying again without removing any factor resulting in an infinite loop. I chose there to croak instead at this point, which I think is more honest than returning a composite.
I anticipate this old PR has the same flaw, but have not checked to confirm.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I'll look into it. I know I wasn't entirely sure what to do with these cases. The factor interface doesn't have a return value other than the list of factors, undef, or croak. No way to say "here are the factors" vs "here's what I managed to find but I couldn't complete everything". The user would have to run primality tests on the returns which is obnoxious. We could do something like return composites as negative values, but that's strange.

I think an exported factor_one gives us more room. undef is a reasonable thing, as it says we couldn't succeed, and in this case there isn't any extra information we have to give them (that is, this says there were no prime factors we were able to find). It does mean we lose out on it splitting into multiple composites, but that's a much rarer situation than factor(n) which could have easily pulled out a number of prime factors before finally failing on the last composite.

Another way we could get factor_one is by using a factor iterator, which has the advantage of being able to store state. In the case where honestly only one factor is desired, I think that would be done like "$F = factor_iter($n)->next_factor;" which would create the iterator, ask for one factor, then it is destroyed as it goes out of scope. That would be nice -- I don't know if the actual interface would include an explicit create + destroy/free or not.

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

Successfully merging this pull request may close these issues.

2 participants