defmodule Multitool.Numbers.Primes do
@moduledoc """
Provides operations for working with prime numbers
A number is prime if it has only two factors, itself and one
"""
@moduledoc since: "0.1.0"
def prime?(n) when n <= 3, do: n > 1
@doc """
Checks if the given number is a prime number.
Returns `true` if the number is prime, `false` otherwise.
Numbers less than two always return `false`
## Parameters
n: Integer to check for primality
## Examples
iex> prime?(-1)
false
iex> prime?(1)
false
iex> prime?(3)
true
iex> prime?(9800)
false
"""
@doc since: "0.1.0"
def prime?(n) do
cond do
rem(n, 2) == 0 ->
false
rem(n, 3) == 0 ->
false
true ->
Stream.unfold(5, &increment_six_or_terminate(&1, n))
|> Enum.all?(&(rem(n, &1) != 0 && rem(n, &1 + 2) != 0))
end
end
defp increment_six_or_terminate(x, n) do
if x * x > n do
nil
else
{x, x + 6}
end
end
@doc """
Generates a list of `n` prime numbers, starting at the first prime number, two.
An empty list is returned when `n` is less than one
## Parameters
n: The number of prime numbers to generate
## Examples
iex> primes(-1)
[]
iex> primes(1)
[2]
iex> primes(10)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
"""
@doc since: "0.1.0"
def primes(n) do
cond do
n < 1 -> []
n == 1 -> [2]
n == 2 -> [2, 3]
n == 3 -> [2, 3, 5]
true -> primes() |> Enum.take(n)
end
end
@doc """
Generates a Stream of prime numbers, starting at the first prime number, two.
This operation will produce prime numbers until a terminating condition is reached
## Examples
iex> primes() |> Enum.take(0)
[]
iex> primes() |> Enum.take(1)
[2]
iex> primes() |> Enum.take(10)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
"""
@doc since: "1.2.0"
def primes(), do: Stream.iterate(2, &(&1 + 1)) |> Stream.filter(&prime?/1)
def primes_before(n) when is_integer(n) and n <= 2, do: []
@doc """
Returns a list of prime numbers that occur before the given integer `n`
An empty list is returned when n is less than three
## Parameters
n: An integer to retrieve preceding primes of
## Examples
iex> primes_before(3)
[2]
iex> primes_before(2)
[]
iex> primes_before(10)
[7, 5, 3, 2]
"""
@doc since: "1.3.0"
def primes_before(n) when is_integer(n) do
Stream.unfold(n - 1, fn x ->
if x < 2 do
nil
else
{x, x - 1}
end
end)
|> Stream.filter(&prime?/1)
|> Enum.to_list()
end
def primes_before(c, _n) when is_integer(c) and c <= 2, do: []
@doc """
Returns a list of `n` primes that occur before, and not including, `c`
## Parameters
c: Integer. The number to generate primes before
n: Integer. The number of primes to generate
## Examples
iex> primes_before(5, 2)
[3, 2]
iex> primes_before(2)
[]
"""
@doc since: "1.3.0"
def primes_before(c, n) when is_integer(n) and is_integer(c),
do: primes_before(c) |> Enum.take(n)
def previous_prime(c) when is_integer(c) and c < 3, do: nil
def previous_prime(c) when is_integer(c), do: primes_before(c, 1) |> hd()
@doc """
Returns a Stream that returns prime numbers that occur after the given number
This operation will produce prime numbers until a terminating condition is reached
## Parameters
n: An integer to generate primes after
## Examples
iex> primes_after(2) |> Enum.take(1)
[3]
iex> primes_after(0) |> Enum.take(2)
[2, 3]
"""
@doc since: "1.3.0"
def primes_after(n) when is_integer(n) do
start =
if n < 1 do
1
else
n + 1
end
Stream.iterate(start, &(&1 + 1))
|> Stream.filter(&prime?/1)
end
@doc """
Returns a list of `n` primes that occur after, and not including, `c`
## Parameters
c: Integer. The number to generate primes after
n: Integer. The number of primes to generate
## Examples
iex> primes_after(2, 1)
[3]
iex> primes_after(0, 2)
[2, 3]
"""
@doc since: "1.3.0"
def primes_after(c, n) when is_integer(n) and is_integer(c), do: primes_after(c) |> Enum.take(n)
@doc """
Returns the next prime number to follow `c`
## Parameters
c: Integer. The number to get the next prime after
## Examples
iex> next_prime(0)
2
iex> next_prime(59651)
59659
"""
@doc since: "1.3.0"
def next_prime(c), do: primes_after(c, 1) |> hd()
@doc """
Returns the nth prime number, where the first prime number is two.
Returns `nil` when `n` is less than one
## Parameters
n: The nth prime number to retrieve
## Examples
iex> nth_prime(3)
5
iex> nth_prime(-1)
nil
iex> nth_prime(98764)
1282201
"""
@doc since: "0.1.0"
def nth_prime(n) do
cond do
n < 1 ->
nil
n == 1 ->
2
n == 2 ->
3
n == 3 ->
5
true ->
Stream.iterate(2, &(&1 + 1))
|> Stream.filter(&prime?/1)
|> Stream.drop(n - 1)
|> Enum.take(1)
|> hd()
end
end
end