defmodule Prime do
@moduledoc """
The main module you'll interact with.
This module provides two functions, `generate/1` and `test/1`,
where the former essentially depends on the latter. That is,
`generate/1` generates a prime by means of yielding a random
number using `Enum.random/1` and, if the number gained is proven
by `test/1` to be a prime, it returns the prime, and if not
it recursively invokes `generate/1` until `Enum.random/1` successfully
returns a prime.
[Prime number theorem](https://en.wikipedia.org/wiki/Prime_number_theorem)
asserts that there are approximately `n/ln(n)` prime numbers between
1 and n. Therefore we can assume the probability for the randomly chosen
number between 1 and n to be the prime number is `1/ln(n)`.
`ln(2^128)` approximately equals to 89. This means you'll only need
less than 100 attempts to find a prime number between 1 and `2^128`.
That's why it's just ok to make a recursion with no apparent base case.
Note, however, that the probablistic nature again reside in the `test/1`,
hence there is actually a little more than `1/ln(n)` steps to generate a prime.
See the doc of `Prime.Fermat.test/1` for details.
"""
alias Prime.MillerRabin
@doc """
Returns a random prime number of given bit size.
"""
@spec generate(bit_size :: pos_integer()) :: pos_integer()
def generate(bit_size) when is_number(bit_size) and 0 < bit_size do
inf = 2
sup = Bitwise.<<<(1, bit_size) - 1
x = Enum.random(inf..sup)
if test(x) do
x
else
generate(bit_size)
end
end
@doc """
Tests if the given number is a prime.
## Examples
iex> Prime.test(3)
true
iex> Prime.test(42)
false
iex> Prime.test(561)
false
iex> Prime.test(1_000_000_007)
true
"""
@spec test(pos_integer()) :: boolean()
def test(n) do
MillerRabin.test(n)
end
end