Packages
A fast and efficient Elixir library for calculating modular multiplicative inverses.
Current section
Files
Jump to
Current section
Files
lib/modular_inverse.ex
defmodule ModularInverse do
@moduledoc """
A library for calculating modular multiplicative inverses in Elixir.
The modular multiplicative inverse of an integer `a` modulo `m` is an integer `x`
such that `(a * x) mod m = 1`. This inverse exists if and only if `a` and `m`
are coprime (i.e., their greatest common divisor is 1).
## Examples
iex> ModularInverse.of(3, 11)
4
iex> rem(3 * 4, 11)
1
iex> ModularInverse.of(7, 13)
2
iex> rem(7 * 2, 13)
1
## Error Cases
When the modular inverse does not exist (i.e., when `a` and `m` are not coprime),
the function raises a `RuntimeError`:
iex> ModularInverse.of(4, 8)
** (RuntimeError) The inverse of the given arguments does not exist, since they are not mutually prime.
## Algorithm
This implementation uses the Extended Euclidean Algorithm to find the modular inverse.
The algorithm efficiently computes the greatest common divisor and the Bézout coefficients,
which are used to construct the modular inverse.
## Performance
The time complexity is O(log min(a, m)), making it efficient for large numbers.
"""
@doc """
Calculates the modular multiplicative inverse of `a` modulo `m`.
Returns the smallest non-negative integer `x` such that `(a * x) mod m = 1`.
## Parameters
- `a` - The integer for which to find the modular inverse
- `m` - The modulus (must be positive)
## Returns
- `non_neg_integer()` - The modular multiplicative inverse of `a` modulo `m`
## Raises
- `RuntimeError` - When the modular inverse does not exist (when `a` and `m` are not coprime)
## Examples
iex> ModularInverse.of(3, 11)
4
iex> ModularInverse.of(7, 13)
2
iex> ModularInverse.of(1, 5)
1
## Mathematical Background
The modular multiplicative inverse exists if and only if `a` and `m` are coprime.
When it exists, it is unique modulo `m`. The function returns the smallest
non-negative representative of this inverse.
For example, with `a = 3` and `m = 11`:
- The inverse is `4` because `(3 * 4) mod 11 = 12 mod 11 = 1`
- Other valid inverses would be `4 + 11k` for any integer `k`, but `4` is the smallest non-negative one.
"""
@spec of(integer(), integer()) :: non_neg_integer()
def of(a, m) do
case BinaryExtendedGcd.of(a, m) do
{1, x, _} ->
case rem(x, m) do
v when v < 0 -> v + m
v -> v
end
_ ->
raise RuntimeError,
"The inverse of the given arguments does not exist, since they are not mutually prime."
end
end
end