Current section
Files
Jump to
Current section
Files
lib/primex.ex
defmodule Primex do
@moduledoc """
This is an educational exercise in refactoring someone else's
code. The original source comes from
http://rosettacode.org/wiki/Sieve_of_Eratosthenes#Elixir
"""
@typedoc """
`t:cis` is a user-defined type, which is a tuple of
an integer and a function that resolves to another `t:cis`
"""
@type cis :: {integer, (() -> cis)}
@typedoc """
`t:ciss` is a user-defined type, which is a tuple of
a `t:cis` (above) and a function that resolves to another `t:ciss`
"""
@type ciss :: {cis, (() -> ciss)}
@spec merge(cis, cis) :: cis
defp merge(xs, ys) do
{x, restxs} = xs
{y, restys} = ys
cond do
x < y -> {x, fn -> merge(restxs.(), ys) end}
y < x -> {y, fn -> merge(xs, restys.()) end}
true -> {x, fn -> merge(restxs.(), restys.()) end}
end
end
@spec stream_multiples_internal(integer, integer) :: cis
defp stream_multiples_internal(c, inc) do
{c, fn -> stream_multiples_internal(c + inc, inc) end}
end
@spec stream_multiples(integer) :: cis
defp stream_multiples(p) do
stream_multiples_internal(p * p, p + p)
end
@spec all_multiples(cis) :: ciss
defp all_multiples({p, restps}) do
{stream_multiples(p), fn -> all_multiples(restps.()) end}
end
@spec pairs(ciss) :: ciss
defp pairs({cs0, restcss0}) do
{cs1, restcss1} = restcss0.()
{merge(cs0, cs1), fn -> pairs(restcss1.()) end}
end
@spec composites(ciss) :: cis
defp composites({cs, restcss}) do
{c, restcs} = cs
{c, fn -> merge(restcs.(), composites(pairs(restcss.()))) end}
end
@spec remove_multiples(integer, cis) :: cis
defp remove_multiples(n, cmps) do
# n is a potential prime number
{c, restcs} = cmps
if n < c do
{n, fn -> remove_multiples(n + 2, cmps) end}
else
remove_multiples(n + 2, restcs.())
end
end
@spec odd_primes() :: cis
defp odd_primes() do
{3,
fn ->
{5,
fn ->
comp =
odd_primes()
|> all_multiples()
|> composites()
remove_multiples(7, comp)
end}
end}
end
@doc """
Get a Primes sieve stream
## Examples
iex> Primex.stream() |> Stream.take(25) |> Enum.to_list()
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
iex> Primex.stream() |> Stream.take_while(&(&1 < 100)) |> Enum.to_list()
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
"""
@spec stream() :: Enumerable.t()
def stream do
[2]
|> Stream.concat(
Stream.iterate(odd_primes(), fn {_, restps} -> restps.() end)
|> Stream.map(fn {p, _} -> p end)
)
end
@doc """
Get a stream of Primes under the given limit
## Examples
iex> Primex.under(100) |> Enum.to_list()
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
"""
@spec under(integer()) :: Enumerable.t()
def under(n) when is_integer(n) do
stream()
|> Stream.take_while(&(&1 < n))
end
end