import Base
import ./list.bend as List
# queue.bend: a FIFO queue as two lists, specified by the list it holds.
#
# import ./queue.bend as Q
#
# push conses onto rear; pop takes from front, and when front is empty the
# reversed rear becomes the new front. Queue is linear (Type), so each
# queue value is used once; then each element is reversed at most once and
# every operation costs O(1) on average.
#
# model(q) is the list q holds. push_model and pop_model state push and
# pop in terms of it.
type Queue<-A: Data> is Type:
Queue{front: List<&2, A>, rear: List<&2, A>}
def empty(-A: Data) -> Queue:
Queue{Nil{}, Nil{}}
def model(-A: Data, q: Queue) -> List<&2, A>:
match q:
case Queue{f, r}:
List.append(&2, A, f, List.reverse(&2, A, r))
def push(-A: Data, q: Queue, x: A) -> Queue:
match q:
case Queue{f, r}:
Queue{f, x <> r}
# front is empty; f is the reversed rear
def pop.rot(-A: Data, f: List<&2, A>) -> List.Step<&1, A, Queue>:
match f:
case Nil{}:
List.Stop{}
case h <> t:
List.Next{h, Queue{t, Nil{}}}
def pop(-A: Data, q: Queue) -> List.Step<&1, A, Queue>:
match q:
case Queue{Nil{}, r}:
pop.rot(A, List.reverse(&2, A, r))
case Queue{h <> t, r}:
List.Next{h, Queue{t, r}}
# pop's result with the remaining queue replaced by its model
def pop.model(-A: Data, s: List.Step<&1, A, Queue>) -> List.Step<&2, A, List<&2, A>>:
match s:
case List.Stop{}:
List.Stop{}
case List.Next{x, q}:
List.Next{x, model(A, q)}
# push appends x to the model.
law push_model:
for -A: Data
for q: Queue
for +x: A
{List.append(&2, A, model(A, q), [x]) == model(A, push(A, q, x)) : List<&2, A>}
def push_model(A, q, x):
match q:
case Queue{+f, +r}:
%List.reverse_go(A, r, [x]) : {List.append(&2, A, List.append(&2, A, f, List.reverse(&2, A, r)), [x]) == List.append(&2, A, f, _) : List<&2, A>}
Equal.sym(List<&2, A>,
List.append(&2, A, f, List.append(&2, A, List.reverse(&2, A, r), [x])),
List.append(&2, A, List.append(&2, A, f, List.reverse(&2, A, r)), [x]),
List.append_assoc(A, f, List.reverse(&2, A, r), [x]))
law pop_model.rot:
for -A: Data
for f: List<&2, A>
{List.uncons(A, f) == pop.model(A, pop.rot(A, f)) : List.Step<&2, A, List<&2, A>>}
def pop_model.rot(A, f):
match f:
case Nil{}:
{==}
case h <> t:
%List.append_nil(A, t) : {List.Next{h, t} == List.Next{h, _} : List.Step<&2, A, List<&2, A>>}
{==}
# pop returns the model's head and tail.
law pop_model:
for -A: Data
for q: Queue
{List.uncons(A, model(A, q)) == pop.model(A, pop(A, q)) : List.Step<&2, A, List<&2, A>>}
def pop_model(A, q):
match q:
case Queue{Nil{}, r}:
pop_model.rot(A, List.reverse(&2, A, r))
case Queue{h <> t, r}:
{==}