Current section
Files
Jump to
Current section
Files
lib/exqueue.ex
defmodule RTQueue do
@moduledoc """
An elixir realtime queue implement.
"""
@typedoc """
The state type in the RTQueue module stands for the reverse state of the queue.
"""
@type state :: :Empty
| {:Reverse, non_neg_integer, list, list, list, list}
| {:Concat, non_neg_integer, list, list}
| {:Done, list}
@typedoc """
The q type stands for the realtime data for a RTQueue.
"""
@type q :: {list, non_neg_integer, state, list, non_neg_integer}
defstruct realtime: {[], 0, :Empty, [], 0}
@typedoc """
The t type stands for the RTQueue.
"""
@type t :: %RTQueue{realtime: q}
@doc """
Return an empty queue.
"""
@spec new() :: t
def new(), do: %RTQueue{realtime: {[], 0, :Empty, [], 0}}
@doc """
Return true when the queue is empty, false when the queue is not empty.
"""
@spec empty?(t) :: boolean
def empty?(queue) do
{_, lenf, _, _, _} = queue.realtime
lenf == 0
end
@spec next(state) :: state
defp next({:Reverse, n, [x | f], fp, [y | r], rp}) do
{:Reverse, (n + 1), f, [x | fp], r, [y | rp]}
end
defp next({:Reverse, n, [], fp, [y], rp}) do
{:Concat, n, fp, [y | rp]}
end
defp next({:Concat, 0, _, acc}) do
{:Done, acc}
end
defp next({:Concat, n, [x | fp], acc}) do
{:Concat, (n - 1), fp, [x | acc]}
end
defp next(s), do: s
@spec abort(state) :: state
defp abort({:Concat, 0, _, [_ | acc]}) do
{:Done, acc}
end
defp abort({:Concat, n, fp, acc}) do
{:Concat, (n - 1), fp, acc}
end
defp abort({:Reverse, n, f, fp, r, rp}) do
{:Reverse, (n - 1), f, fp, r, rp}
end
defp abort(s), do: s
@spec step(list, non_neg_integer, state, list, non_neg_integer) :: t
defp step(f, lenf, s, r, lenr) do
sp =
if Enum.empty?(f) do
s |> next() |> next()
else
s |> next()
end
case sp do
{:Done, fp} -> %RTQueue{realtime: {fp, lenf, :Empty, r, lenr}}
sp -> %RTQueue{realtime: {f, lenf, sp, r, lenr}}
end
end
@spec balance(list, non_neg_integer, state, list, non_neg_integer) :: t
defp balance(f, lenf, s, r, lenr) do
cond do
lenr <= lenf -> step(f, lenf, s, r, lenr)
true -> step(f, lenf + lenr, {:Reverse, 0, f, [], r, []}, [], 0)
end
end
@doc """
Push an element to the back of a queue.
"""
@spec push(t, any) :: t
def push(queue, x) do
{f, lenf, s, r, lenr} = queue.realtime
balance(f, lenf, s, [x | r], (lenr + 1))
end
@doc """
Pop the front element of a queue.
"""
@spec pop(t) :: t
def pop(queue) do
{[_ | f], lenf, s, r, lenr} = queue.realtime
balance(f, lenf - 1, abort(s), r, lenr)
end
@doc """
Get the front element of a queue.
"""
@spec front(t) :: any
def front(queue) do
{[x | _], _, _, _, _} = queue.realtime
x
end
@doc """
Return the size of a queue.
"""
@spec size(t) :: non_neg_integer
def size(queue) do
{_, lenf, _, _, lenr} = queue.realtime
lenf + lenr
end
defimpl Inspect do
def inspect(queue, _opts \\ []) do
case RTQueue.empty?(queue) do
false -> Inspect.Algebra.concat([
"#RTQueue<[",
"size: " <> to_string(RTQueue.size(queue)),
", front: " <> Kernel.inspect(RTQueue.front(queue)),
"]>"
])
true -> "Empty #RTQueue"
end
end
end
end
defmodule FQueue do
@moduledoc """
An elixir queue implement using fingertree.
"""
require FList.FTree
defstruct tree: :Empty
@type t :: %FQueue{tree: FList.FTree.t}
@doc """
Return an empty queue.
"""
@spec new() :: t
def new(), do: %FQueue{tree: :Empty}
@doc """
Return the size of a queue.
"""
@spec size(t) :: non_neg_integer
def size(queue), do: FList.FTree.sizeT(queue.tree)
@doc """
Return true when the queue is empty, false when the queue is not empty.
"""
@spec empty?(t) :: boolean
def empty?(queue), do: queue.tree == :Empty
@doc """
Get the front element of a queue.
"""
@spec front(t) :: any
def front(queue), do: FList.FTree.head(queue.tree)
@doc """
Get the back element of a queue.
"""
@spec back(t) :: any
def back(queue), do: FList.FTree.last(queue.tree)
@doc """
Push an element to the back of a queue.
"""
@spec push(t, any) :: t
def push(queue, element) do
%FQueue{queue | tree: queue.tree |> FList.FTree.snoc(element)}
end
@doc """
Pop the front element of a queue.
"""
@spec pop(t) :: t
def pop(queue) do
%FQueue{queue | tree: queue.tree |> FList.FTree.tail()}
end
defimpl Inspect do
def inspect(queue, _opts \\ []) do
case FQueue.empty?(queue) do
false -> Inspect.Algebra.concat([
"#FQueue<[",
"size: " <> to_string(FQueue.size(queue)),
", front: " <> Kernel.inspect(FQueue.front(queue)),
"]>"
])
true -> "Empty #FQueue"
end
end
end
end
defmodule LQueue do
@moduledoc """
An elixir lazy queue implement using Stream.
"""
defstruct stream: %Stream{Stream.__struct__ | enum: []}, size: 0
@type t :: %LQueue{stream: Stream.t}
@doc """
Return an empty queue in default.
"""
@spec new(Stream.t, non_neg_integer) :: t
def new(s \\ nil, len \\ 0) do
case s do
nil -> %LQueue{stream: Stream.concat([]), size: len}
s -> %LQueue{stream: s, size: len}
end
end
@doc """
Push an element to the back of a queue.
"""
@spec push(t, any) :: t
def push(queue, ele) do
Stream.concat(queue.stream, [ele]) |> new(queue.size + 1)
end
@doc """
Get the front element of a queue.
"""
@spec front(t) :: any
def front(queue) do
cond do
queue.size > 0 -> Enum.fetch!(queue.stream, 0)
true -> :error
end
end
@doc """
Pop the front element of a queue.
"""
@spec pop(t) :: t
def pop(queue, num \\ 1) do
cond do
queue.size >= num -> Stream.drop(queue.stream, num) |> new(queue.size - num)
true -> :error
end
end
@doc """
Return true when the queue is empty, false when the queue is not empty.
"""
@spec empty?(t) :: boolean
def empty?(queue) do
queue.size == 0
end
@doc """
Return the size of a queue.
"""
@spec size(t) :: non_neg_integer
def size(queue) do
queue.size
end
defimpl Inspect do
def inspect(queue, _opts \\ []) do
case LQueue.empty?(queue) do
false -> Inspect.Algebra.concat([
"#LQueue<[",
"size: " <> to_string(LQueue.size(queue)),
", front: " <> Kernel.inspect(LQueue.front(queue)),
"]>"
])
true -> "Empty #LQueue"
end
end
end
end