lib/data_structures/priority_queue.ex

defmodule PriorityQueue do
  @moduledoc """
  Documentation for `PriorityQueue` data structure implemented on general balanced trees by Arne Andersson
  These have no storage overhead compared to unbalanced binary trees, and
  their performance is better than AVL trees.

  More about GB trees on [Arne Andersson work](http://user.it.uu.se/~hansh/arne1.pdf)
  """

  @doc """
  Create a new, empty qeue

  ## Examples

      iex> PriorityQueue.new
      {0, nil}

  """
  @spec new :: :gb_trees.tree(any, any)
  def new, do: :gb_trees.empty

  @doc """
  Check if priority queue is empty

  ## Examples

      iex> PriorityQueue.new |> PriorityQueue.empty?
      true

  """
  @spec empty?(:gb_trees.tree(any, any)) :: boolean
  def empty?(queue), do: :gb_trees.is_empty(queue)

  @doc """
  Add a new element with priority to queue

  ## Examples

      iex> PriorityQueue.new |> PriorityQueue.insert("Do homework", 4)
      {1, {4, "Do homework", nil, nil}}

  """
  @spec insert(:gb_trees.tree(any, any), any, any) :: :gb_trees.tree(any, any)
  def insert(queue, element, priority), do: :gb_trees.enter(priority, element, queue)

  @doc """
  Remove element from queue with the higest priority

  ## Examples

      iex> PriorityQueue.new |> PriorityQueue.insert("Do homework", 4) |> PriorityQueue.top
      {0, nil}

  """
  @spec top(:gb_trees.tree(any, any)) :: :gb_trees.tree(any, any)
  def top(queue) do
    {_priority, _element, new_queue} = :gb_trees.take_smallest(queue)
    new_queue
  end

  @doc """
  Return number of elements in queue

  ## Examples

      iex> PriorityQueue.new |> PriorityQueue.insert("Do homework", 4) |> PriorityQueue.size
      1

  """
  @spec size(:gb_trees.tree(any, any)) :: non_neg_integer
  def size(queue), do: :gb_trees.size(queue)

  @doc """
  Checkout if queue is empty

  ## Examples

      iex> PriorityQueue.new |> PriorityQueue.insert("Do homework", 4) |> PriorityQueue.is_empty
      false

      iex> PriorityQueue.new |> PriorityQueue.is_empty
      true

  """
  @spec is_empty(:gb_trees.tree(any, any)) :: boolean
  def is_empty(queue), do: if :gb_trees.size(queue) > 0, do: false, else: true

  @doc """
  Checkout if queue is empty

  ## Examples

      iex> PriorityQueue.new
      iex> |> PriorityQueue.insert("Do4", 4)
      iex> |> PriorityQueue.insert("Do1", 1)
      iex> |> PriorityQueue.to_list
      [{1, "Do1"}, {4, "Do4"}]

  """
  @spec to_list(:gb_trees.tree(any, any)) :: [{any, any}]
  def to_list(queue), do: :gb_trees.to_list(queue)

  @doc """
  Return peek element of queue

  ## Examples

      iex> PriorityQueue.new |> PriorityQueue.insert("Do homework", 4) |> PriorityQueue.peek
      "Do homework"

  """
  @spec peek(:gb_trees.tree(any, any)) :: any
  def peek(queue) do
    {_priority, element, _new_queue} = :gb_trees.take_smallest(queue)
    element
  end

end