# Modular Exponentiation Notes
## What Is Modular Exponentiation?
Modular exponentiation is exponentiation performed over a modulus.
Simply, it is about calculating a power of a number, then taking the result modulo (remainder after division by) another number.
It’s written like this:
$$ b^e \pmod m $$
In that expression, we'd refer to $b$ as the base, to $e$ as the exponent, and to $m$ as the modulus.
---
### How not to implement modular exponentiation
A really bad approach to implementing modular exponentiation would be something like this: `a ** b % m`, which uses the definition quite literally.
The larger the exponent `b` is, the worse computing `a ** b` becomes.
It is very slow, uses a lot of memory, may cause overflows, and simply - is not efficient.
So instead, we use the **fast modular exponentiation** algorithm, also called **exponentiation by squaring**, which computes the same result efficiently.
---
## Example
Let's calculate `3^13 mod 7` using the Fast Exponentiation Algorithm.
First, write 13 in binary:
```
13 = 1101 (binary)
```
So:
```
3^13 = 3^(8 + 4 + 0 + 1)
= 3^8 * 3^4 * 3^1
```
Compute powers of 3 mod 7 by squaring each time:
```
3^1 = 3 → 3 mod 7 = 3
3^2 = 9 → 9 mod 7 = 2
3^4 = (3^2)^2 = 2^2 = 4 mod 7
3^8 = (3^4)^2 = 4^2 = 16 mod 7 = 2
```
Now multiply the powers mod 7:
```
3^13 mod 7 = 3^8 * 3^4 * 3^1 mod 7
= 2 * 4 * 3 = 24
= 24 mod 7 = 3
```
`3^13 mod 7 = 3`
---
## Python Implementation of Fast Modular Exponentiation
```python
def fast_mod_exp(base, exponent, modulus):
result = 1
base %= modulus # Handle case where base > modulus
while exponent > 0:
if exponent % 2 == 1:
# If the current bit is 1, multiply result by base
result = (result * base) % modulus
# Square the base
base = (base * base) % modulus
# Move to next bit
exponent = exponent // 2
return result
```
## Python's `pow()` and Sagemath's `power_mod`
Both [`pow(a, b, m)`](https://docs.python.org/3/library/functions.html#pow) in Python and [`power_mod(a, b, m)`](https://doc.sagemath.org/html/en/reference/rings_standard/sage/arith/misc.html#sage.arith.misc.power_mod) in SageMath are optimized for modular exponentiation and are fast and reliable.
You can use them to verify our previous exapmle calculation is correct in code:
```python
>>> pow(3, 13, 7)
3
```
```python
sage: power_mod(3, 13, 7)
3
```
---
Both `pow` and `power_mod` support negative exponents for calculating [modular multiplicative inverses](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse):
```python
>>> pow(2, -1, 7)
4
>>> (2 * 4) % 7
1
```
```python
sage: power_mod(2, -1, 7)
4
sage: (2 * 4) % 7
1
```
They are computed efficiently using the [Extended Euclidean Algorithm](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm).
---
Sage's `power_mod` function is even more powerful, as it can even handle symbols:
```python
sage: R.<x> = ZZ[]
sage: power_mod(3*x, 10, 7)
4*x^10
sage: power_mod(-3*x^2 + 4, 7, 2*x^3 - 5)
x^14 + x^8 + x^6 + x^3 + 962509*x^2 - 791910*x - 698281
```
Generally, it would also work as long as the base and the modulus are elements of a ring that implements the modulo operator %.