Skip to content

Fix power of Integer with modulus 0#41695

Merged
vbraun merged 1 commit intosagemath:developfrom
MajoranaOedipus:fix_Integer_pow
Mar 22, 2026
Merged

Fix power of Integer with modulus 0#41695
vbraun merged 1 commit intosagemath:developfrom
MajoranaOedipus:fix_Integer_pow

Conversation

@MajoranaOedipus
Copy link
Copy Markdown
Contributor

When calling Integer.__pow__ with modulus 0, Mod(left, modulus) is an Integer, instead of IntegerMod_int, hence no (Mod(left, modulus) ** right).lift method or calls the wrong lift method, e.g.

pow(ZZ(-1), QQ((1, 2)), 0)  # => x, a polynomial, since it calls I.lift
pow(ZZ(3), 2, 0)  # => AttributeError: 'sage.rings.integer.Integer' object has no attribute 'lift'

I add a guard against modulus == 0 so that pow(n, ext, 0) would be the same as pow(n, ext). A related doctest is added.

Fixes #41692

📝 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.
  • I have updated the documentation and checked the documentation preview.

⌛ Dependencies

@github-actions
Copy link
Copy Markdown

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

@yyyyx4
Copy link
Copy Markdown
Member

yyyyx4 commented Feb 26, 2026

For what it's worth, Python throws a ValueError for pow(a,e,0). If you want zero to mean "no modular reduction", then a**e is the standard way to get that. I currently don't see a value in supporting this weird syntax and would therefore argue that a ValueError is the way to go.

@MajoranaOedipus
Copy link
Copy Markdown
Contributor Author

MajoranaOedipus commented Feb 26, 2026

I think raising a ValueError is also fine.

We can also raise an ZeroDivisionError, or we can call with:

if modulus == 0:
    return pow(left, right) % modulus

so that we might get consistent with (left**right)%modulus?
For example, when right or modulus is a RealNumber, the result is NaN instead.
This makes sense because for inexact calculus.
Other result types might raise their own error message.

Also there are cases when m in n % m can be a non-integer, for example when m is a RealNumber.
There was some efforts in standardizing the behaviour (#21747) although they got lost in the river of time.
m in n % m can also be a polynomial, although I doubt people would ever write pow(n, expt, m_poly) (maybe they forget to coerce n to zero-degree polynomial?).
If we like to take care of that, we can expect a TypeError in Mod(left, modulus) ** right, and fall back to (left ** right) % modulus.

To me, that Mod(n, 0) is an Integer is a surprise to me. But this is consistent with Zmod(0) == ZZ.

So ... we can do

  1. raise ZeroDivisionError or ValueError directly
  2. Let (left ** right) % modulus do the job (for zero modulus and maybe non-integer modulus?)
  3. Make pow(left, right, 0) the same as pow(left, right)

What do you think is better?

@cxzhong
Copy link
Copy Markdown
Contributor

cxzhong commented Mar 2, 2026

LGTM left this to @yyyyx4

vbraun pushed a commit to vbraun/sage that referenced this pull request Mar 6, 2026
sagemathgh-41695: Fix power of `Integer` with modulus `0`
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->
When calling `Integer.__pow__` with modulus `0`, `Mod(left, modulus)` is
an Integer, instead of `IntegerMod_int`, hence no `(Mod(left, modulus)
** right).lift` method or calls the wrong `lift` method, e.g.
```Python
pow(ZZ(-1), QQ((1, 2)), 0)  # => x, a polynomial, since it calls I.lift
pow(ZZ(3), 2, 0)  # => AttributeError: 'sage.rings.integer.Integer'
object has no attribute 'lift'
```

I add a guard against `modulus == 0` so that `pow(n, ext, 0)` would be
the same as `pow(n, ext)`. A related doctest is added.

Fixes sagemath#41692


### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [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.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
    
URL: sagemath#41695
Reported by: Majorana Oedipus
Reviewer(s): Chenxin Zhong
vbraun pushed a commit to vbraun/sage that referenced this pull request Mar 15, 2026
sagemathgh-41695: Fix power of `Integer` with modulus `0`
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->
When calling `Integer.__pow__` with modulus `0`, `Mod(left, modulus)` is
an Integer, instead of `IntegerMod_int`, hence no `(Mod(left, modulus)
** right).lift` method or calls the wrong `lift` method, e.g.
```Python
pow(ZZ(-1), QQ((1, 2)), 0)  # => x, a polynomial, since it calls I.lift
pow(ZZ(3), 2, 0)  # => AttributeError: 'sage.rings.integer.Integer'
object has no attribute 'lift'
```

I add a guard against `modulus == 0` so that `pow(n, ext, 0)` would be
the same as `pow(n, ext)`. A related doctest is added.

Fixes sagemath#41692


### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [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.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
    
URL: sagemath#41695
Reported by: Majorana Oedipus
Reviewer(s): Chenxin Zhong
vbraun pushed a commit to vbraun/sage that referenced this pull request Mar 18, 2026
sagemathgh-41695: Fix power of `Integer` with modulus `0`
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->
When calling `Integer.__pow__` with modulus `0`, `Mod(left, modulus)` is
an Integer, instead of `IntegerMod_int`, hence no `(Mod(left, modulus)
** right).lift` method or calls the wrong `lift` method, e.g.
```Python
pow(ZZ(-1), QQ((1, 2)), 0)  # => x, a polynomial, since it calls I.lift
pow(ZZ(3), 2, 0)  # => AttributeError: 'sage.rings.integer.Integer'
object has no attribute 'lift'
```

I add a guard against `modulus == 0` so that `pow(n, ext, 0)` would be
the same as `pow(n, ext)`. A related doctest is added.

Fixes sagemath#41692


### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [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.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
    
URL: sagemath#41695
Reported by: Majorana Oedipus
Reviewer(s): Chenxin Zhong
vbraun pushed a commit to vbraun/sage that referenced this pull request Mar 21, 2026
sagemathgh-41695: Fix power of `Integer` with modulus `0`
    
<!-- ^ Please provide a concise and informative title. -->
<!-- ^ Don't put issue numbers in the title, do this in the PR
description below. -->
<!-- ^ For example, instead of "Fixes sagemath#12345" use "Introduce new method
to calculate 1 + 2". -->
<!-- v Describe your changes below in detail. -->
<!-- v Why is this change required? What problem does it solve? -->
<!-- v If this PR resolves an open issue, please link to it here. For
example, "Fixes sagemath#12345". -->
When calling `Integer.__pow__` with modulus `0`, `Mod(left, modulus)` is
an Integer, instead of `IntegerMod_int`, hence no `(Mod(left, modulus)
** right).lift` method or calls the wrong `lift` method, e.g.
```Python
pow(ZZ(-1), QQ((1, 2)), 0)  # => x, a polynomial, since it calls I.lift
pow(ZZ(3), 2, 0)  # => AttributeError: 'sage.rings.integer.Integer'
object has no attribute 'lift'
```

I add a guard against `modulus == 0` so that `pow(n, ext, 0)` would be
the same as `pow(n, ext)`. A related doctest is added.

Fixes sagemath#41692


### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [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.
- [ ] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

<!-- List all open PRs that this PR logically depends on. For example,
-->
<!-- - sagemath#12345: short description why this is a dependency -->
<!-- - sagemath#34567: ... -->
    
URL: sagemath#41695
Reported by: Majorana Oedipus
Reviewer(s): Chenxin Zhong
@vbraun vbraun merged commit d7fe2e0 into sagemath:develop Mar 22, 2026
28 checks passed
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.

Unexpected result of pow(ZZ(-1), QQ((1, 2)), 0)

4 participants