/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Daniel Selsam

Simple queue implemented using two lists.
Note: this is only a temporary placeholder.
-/
prelude
import Init.Data.List

namespace Std

/--
A functional queue data structure, using two back-to-back lists.
If we think of the queue as having elements pushed on the front and
popped from the back, then the queue itself is effectively `eList ++ dList.reverse`.
-/
structure Queue (α : Type u) where
  /-- The enqueue list, which stores elements that have just been pushed
  (with the most recently enqueued elements at the head). -/
  eList : List α := []
  /-- The dequeue list, which buffers elements ready to be dequeued
  (with the head being the next item to be yielded by `dequeue?`). -/
  dList : List α := []

namespace Queue

variable {α : Type u}

/-- `O(1)`. The empty queue. -/
def empty : Queue α := {}

instance : EmptyCollection (Queue α) := ⟨.empty⟩
instance : Inhabited (Queue α) := ⟨∅⟩

/-- `O(1)`. Is the queue empty? -/
def isEmpty (q : Queue α) : Bool :=
  q.dList.isEmpty && q.eList.isEmpty

/-- `O(1)`. Push an element on the front of the queue. -/
def enqueue (v : α) (q : Queue α) : Queue α :=
  { q with eList := v::q.eList }

/-- `O(|vs|)`. Push a list of elements `vs` on the front of the queue. -/
def enqueueAll (vs : List α) (q : Queue α) : Queue α :=
  { q with eList := vs ++ q.eList }

/--
`O(1)` amortized, `O(n)` worst case. Pop an element from the back of the queue,
returning the element and the new queue.
-/
def dequeue? (q : Queue α) : Option (α × Queue α) :=
  match q.dList with
  | d::ds => some (d, { q with dList := ds })
  | []    =>
    match q.eList.reverse with
    | []    => none
    | d::ds => some (d, { eList := [], dList := ds })

def toArray (q : Queue α) : Array α :=
  q.dList.toArray ++ q.eList.toArray.reverse
