factor_one and friends - #30
Conversation
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.
|
Note that MPU |
| /* 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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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.
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.