gh-37441: Bind to FLINT/NTL API for polynomial modular exponentiation (new version)
This is a rejuvenation of the pull request by @remyoudompheng
https://github.com/sagemath/sage/pull/35320.
All I have done is rebase with develop and modify a few lines to fit
with the current structuring of SageMath. I have also added a few
additional docstrings.
As an example of the performance gain:
### Flint polynomials over prime fields
Old timings:
About 5x faster for flint using `nmod_poly`
```py
sage: R.<x> = GF(5)[]
sage: %time pow(x+1, 5**100, x^5 + 4*x + 3)
CPU times: user 551 µs, sys: 1e+03 ns, total: 552 µs
Wall time: 555 µs
x + 1
```
New timings:
```py
sage: R.<x> = GF(5)[]
sage: %time pow(x+1, 5**100, x^5 + 4*x + 3)
CPU times: user 175 µs, sys: 1e+03 ns, total: 176 µs
Wall time: 179 µs
x + 1
```
### NTL polynomials over prime fields
About 1000x faster for NTL $\textrm{GF}(p)$
Old timings:
```py
sage: set_random_seed(0)
....: p = 2**255 - 19
....: R.<x> = F[]
....: f = R.random_element(degree=12)
....: g = R.random_element(degree=12)
....: %timeit pow(f, 2**33, g)
92.3 ms ± 947 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
sage: %timeit pow(f, 2**64, g)
1.29 s ± 35.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
```
New timings:
```py
sage: set_random_seed(0)
....: p = 2**255 - 19
....: R.<x> = F[]
....: f = R.random_element(degree=12)
....: g = R.random_element(degree=12)
....: %timeit pow(f, 2**33, g)
651 µs ± 28.9 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
sage: %timeit pow(f, 2**64, g)
1.17 ms ± 10.7 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops
each)
```
### NTL polynomials over extension fields
About 10x faster for NTL polynomial rings over $\textrm{GF}(p^k)$
Old timings:
```py
sage: set_random_seed(0)
....:
....: p = 2**255 - 19
....: F.<a> = GF(p^3)
....: q = F.order()
....: R.<x> = F[]
....:
....: f = R.random_element(degree=12)
....: g = R.random_element(degree=12)
....:
....: %time _ = pow(f, q, g)
....:
....: p = 29
....: F.<a> = GF(p^100)
....: q = F.order()
....: R.<x> = F[]
....:
....: f = R.random_element(degree=12)
....: g = R.random_element(degree=12)
....:
....: %time _ = pow(f, q, g)
CPU times: user 409 ms, sys: 1.45 ms, total: 410 ms
Wall time: 411 ms
CPU times: user 14.6 s, sys: 202 ms, total: 14.8 s
Wall time: 14.9 s
```
New timings:
```py
sage: set_random_seed(0)
....:
....: p = 2**255 - 19
....: F.<a> = GF(p^3)
....: q = F.order()
....: R.<x> = F[]
....:
....: f = R.random_element(degree=12)
....: g = R.random_element(degree=12)
....:
....: %time _ = pow(f, q, g)
....:
....: p = 29
....: F.<a> = GF(p^100)
....: q = F.order()
....: R.<x> = F[]
....:
....: f = R.random_element(degree=12)
....: g = R.random_element(degree=12)
....:
....: %time _ = pow(f, q, g)
CPU times: user 371 ms, sys: 761 µs, total: 371 ms
Wall time: 371 ms
CPU times: user 1.54 s, sys: 10.5 ms, total: 1.55 s
Wall time: 1.55 s
```
Closes https://github.com/sagemath/sage/pull/35320
URL: https://github.com/sagemath/sage/pull/37441
Reported by: Giacomo Pope
Reviewer(s): Giacomo Pope, Travis Scrimshaw