Current section

Files

Jump to
inplace lib adt queue.ex
Raw

lib/adt/queue.ex

defmodule InPlace.Queue do
alias InPlace.Array
## Circular queue
## The internal structure:
## `size` is located at `arr[capacity + 1]` and has the current queue size;
## `front_pointer` is located at `arr[capacity + 2]` and has a 0-based pointer to the current `front` element
## , that is: arr[arr[capacity + 2] + 1] = front
##
def new(capacity) when is_integer(capacity) and capacity > 0 do
ref = Array.new(capacity + 2, 0)
{capacity, ref}
end
def size({capacity, ref} = _queue) do
Array.get(ref, size_address(capacity))
end
defp size_address(capacity) do
capacity + 1
end
def empty?(queue) do
size(queue) == 0
end
def front({_capacity, ref} = queue) do
if !empty?(queue) do
get0(ref, front_pointer(queue))
end
end
def front_pointer({capacity, ref} = _queue) do
Array.get(ref, front_address(capacity))
end
defp front_address(capacity) do
capacity + 2
end
def rear({_capacity, ref} = queue) do
if !empty?(queue) do
pointer = rear_pointer(queue)
get0(ref, pointer)
end
end
def rear_pointer({capacity, _ref} = queue) do
rem(front_pointer(queue) + size(queue) - 1, capacity)
end
def enqueue({capacity, ref} = queue, element) do
current_size = size(queue)
if current_size == capacity, do: throw(:queue_over_capacity)
put0(ref, rem(front_pointer(queue) + current_size, capacity), element)
Array.put(ref, size_address(capacity), current_size + 1)
end
def dequeue({capacity, ref} = queue) do
case size(queue) do
0 ->
nil
current_size ->
pointer = front_pointer(queue)
Array.put(ref, size_address(capacity), current_size - 1)
Array.put(ref, front_address(capacity), rem(pointer + 1, capacity))
get0(ref, pointer)
end
end
def from_list(list) do
new(length(list))
|> tap(fn queue -> Enum.each(list, fn el -> enqueue(queue, el) end) end)
end
defp get0(arr, idx) do
Array.get(arr, idx + 1)
end
defp put0(arr, idx, value) do
Array.put(arr, idx + 1, value)
end
end