Skip to content

Conversation

@fchapoton
Copy link
Contributor

Fix #40846

📝 Checklist

  • The title is concise and informative.
  • The description explains in detail what this PR is about.
  • I have linked a relevant issue or discussion.
  • I have created tests covering the changes.

@fchapoton fchapoton changed the title fixing a bug fixing a bug in perfect_power Sep 18, 2025
@vincentmacri
Copy link
Member

You beat me to it!

Looks good, just add a sentence before the new test saying Test small powers of even numbers that are not a power of 2 (see :issue:`40846`).

@vincentmacri
Copy link
Member

You beat me to it!

Looks good, just add a sentence before the new test saying Test small powers of even numbers that are not a power of 2 (see :issue:`40846`).

Not too sure about how the docstring formatting works, this might require changing TESTS:: to TESTS: and adding a description to the previous tests which test powers of two.

@github-actions
Copy link

github-actions bot commented Sep 18, 2025

Documentation preview for this PR (built with commit 5649932; changes) is ready! 🎉
This preview will update shortly after each push to this PR.

if mpz_popcount(self.value) == 1:
return smallInteger(2), smallInteger(mpz_sizeinbase(self.value, 2) - 1)
if n < 1000:
elif n < 1000:
Copy link
Contributor

Choose a reason for hiding this comment

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

wouldn't it be better to test against len(_small_primes_table) (or store this in a constant)?

Copy link
Member

@vincentmacri vincentmacri Sep 18, 2025

Choose a reason for hiding this comment

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

wouldn't it be better to test against len(_small_primes_table) (or store this in a constant)?

_small_primes_table is kind of weird. It's not a list or table of primes themselves. Rather, _small_primes_table[n] is 1 if $2n + 1$ is prime, and 0 if $2n + 1$ is not prime (so _small_primes_table[0] = 0 because 1 is not prime, and _small_primes_table[1] = 1 because 3 is prime).

Copy link
Contributor

Choose a reason for hiding this comment

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

This and similar logic is being employed in three different places in the code, ie, check if value < 1000 and is odd and if so, invoke _small_primes_table[value >> 1]. We should store this 1000 value in a constant.

@mantepse
Copy link
Contributor

I think that 36.perfect_power() should also be a test. I don't see right now why the PR would fix it.

@vincentmacri
Copy link
Member

vincentmacri commented Sep 18, 2025

I think that 36.perfect_power() should also be a test.

Sure.

I don't see right now why the PR would fix it.

With the PR 36 will fail all the checks for the faster version and reach the pari fallback (which works). The bug was caused by 36 reaching the case for small odd primes.

if not (n & 1): checks if n is even. If so, if mpz_popcount(self.value) == 1: checks if n is a power of 2.

Previously 36 passed the first check and failed the second, then it checked if it was less than 1000 and if so whether $2 \lfloor {\frac{n}{2}} \rfloor + 1$ prime. If $n$ is odd then this is expression is equal to n. The return statement operates under this assumption. But if you plug in $n = 36$ you get $37$ which is prime. It then thinks that n is prime and the way you write a prime as a power is just itself to the power of 1 hence the bug.

@whoami730
Copy link
Contributor

Looks good, just add a sentence before the new test saying Test small powers of even numbers that are not a power of 2 (see :issue:`40846`).
+1.

@fchapoton
Copy link
Contributor Author

comment added. Other changes left for other PRs.

@whoami730
Copy link
Contributor

whoami730 commented Sep 19, 2025

Other changes left for other PRs.

Let's add a TODO then to indicate what changes need to be made?

@fchapoton
Copy link
Contributor Author

we should really quickly set this one to "positive review" ; the rest is not urgent

@mantepse
Copy link
Contributor

ok for me

@vincentmacri
Copy link
Member

Thank you for the quick fix!

@vincentmacri
Copy link
Member

Not needed for this PR, but since we were talking about it, some food for thought on _small_primes_table if someone wants to work on cleaning it up:

  • Why is _small_primes_table a list of int? Surely a tuple of bool would be more memory-efficient. (Or better yet bits, but I think working with bits is awkward unless there's some type for it that I'm not aware of.) And if it's more memory efficient then we could probably make it bigger (go up to 10000 instead of 1000 for example).
  • Does Cython support a way to define a constant/macro that is computed at compile-time? _small_primes_table would be more readable if it were defined as _small_primes_table = tuple(is_prime(2 * n + 1) for n in range(500)), but this module is supposed to be efficient so we should only do that if it can be computed at compile-time.

vbraun pushed a commit to vbraun/sage that referenced this pull request Sep 19, 2025
Fix sagemath#40846

### 📝 Checklist

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.

URL: sagemath#40850
Reported by: Frédéric Chapoton
Reviewer(s): Martin Rubey, Sahil Jain, Vincent Macri
@vbraun vbraun merged commit 9c1da99 into sagemath:develop Sep 21, 2025
22 of 24 checks passed
@fchapoton fchapoton deleted the fixbug_perfect_power branch September 21, 2025 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perfect_power() gives wrong result

5 participants