diff --git a/queues/arrayqueue/arrayqueue.go b/queues/arrayqueue/arrayqueue.go new file mode 100644 index 0000000..11bc995 --- /dev/null +++ b/queues/arrayqueue/arrayqueue.go @@ -0,0 +1,87 @@ +// Copyright (c) 2021, Aryan Ahadinia. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package arrayqueue implements a queue backed by a array-list. +// +// Structure is not thread safe. +// +// Reference: https://en.wikipedia.org/wiki/Queue_(abstract_data_type) +package arrayqueue + +import ( + "fmt" + "strings" + + "github.com/emirpasic/gods/lists/arraylist" + "github.com/emirpasic/gods/queues" +) + +func assertQueueImplementation() { + var _ queues.Queue = (*Queue)(nil) +} + +// Queue holds elements in an array-list +type Queue struct { + list *arraylist.List +} + +// New instantiates a new empty queue +func New() *Queue { + return &Queue{list: arraylist.New()} +} + +// Enqueue adds a value to the end of the queue +func (queue *Queue) Enqueue(value interface{}) { + queue.list.Add(value) +} + +// Dequeue removes first element of the queue and returns it, or nil if queue is empty. +// Second return parameter is true, unless the queue was empty and there was nothing to dequeue. +func (queue *Queue) Dequeue() (value interface{}, ok bool) { + value, ok = queue.list.Get(0) + queue.list.Remove(0) + return +} + +// Peek returns first element of the queue without removing it, or nil if queue is empty. +// Second return parameter is true, unless the queue was empty and there was nothing to peek. +func (queue *Queue) Peek() (value interface{}, ok bool) { + return queue.list.Get(0) +} + +// Empty returns true if queue does not contain any elements. +func (queue *Queue) Empty() bool { + return queue.list.Empty() +} + +// Size returns number of elements within the queue. +func (queue *Queue) Size() int { + return queue.list.Size() +} + +// Clear removes all elements from the queue. +func (queue *Queue) Clear() { + queue.list.Clear() +} + +// Values returns all elements in the queue (FIFO order). +func (queue *Queue) Values() []interface{} { + return queue.list.Values() +} + +// String returns a string representation of container +func (queue *Queue) String() string { + str := "ArrayQueue\n" + values := []string{} + for _, value := range queue.list.Values() { + values = append(values, fmt.Sprintf("%v", value)) + } + str += strings.Join(values, ", ") + return str +} + +// Check that the index is within bounds of the list +func (queue *Queue) withinRange(index int) bool { + return index >= 0 && index < queue.list.Size() +} diff --git a/queues/arrayqueue/arrayqueue_test.go b/queues/arrayqueue/arrayqueue_test.go new file mode 100644 index 0000000..d86a3f0 --- /dev/null +++ b/queues/arrayqueue/arrayqueue_test.go @@ -0,0 +1,278 @@ +// Copyright (c) 2021, Aryan Ahadinia. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package arrayqueue + +import ( + "fmt" + "testing" +) + +func TestQueueEnqueue(t *testing.T) { + queue := New() + if actualValue := queue.Empty(); actualValue != true { + t.Errorf("Got %v expected %v", actualValue, true) + } + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + if actualValue := queue.Values(); actualValue[0].(int) != 1 || actualValue[1].(int) != 2 || actualValue[2].(int) != 3 { + t.Errorf("Got %v expected %v", actualValue, "[1,2,3]") + } + if actualValue := queue.Empty(); actualValue != false { + t.Errorf("Got %v expected %v", actualValue, false) + } + if actualValue := queue.Size(); actualValue != 3 { + t.Errorf("Got %v expected %v", actualValue, 3) + } + if actualValue, ok := queue.Peek(); actualValue != 1 || !ok { + t.Errorf("Got %v expected %v", actualValue, 1) + } +} + +func TestQueuePeek(t *testing.T) { + queue := New() + if actualValue, ok := queue.Peek(); actualValue != nil || ok { + t.Errorf("Got %v expected %v", actualValue, nil) + } + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + if actualValue, ok := queue.Peek(); actualValue != 1 || !ok { + t.Errorf("Got %v expected %v", actualValue, 1) + } +} + +func TestQueueDequeue(t *testing.T) { + queue := New() + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + queue.Dequeue() + if actualValue, ok := queue.Peek(); actualValue != 2 || !ok { + t.Errorf("Got %v expected %v", actualValue, 2) + } + if actualValue, ok := queue.Dequeue(); actualValue != 2 || !ok { + t.Errorf("Got %v expected %v", actualValue, 2) + } + if actualValue, ok := queue.Dequeue(); actualValue != 3 || !ok { + t.Errorf("Got %v expected %v", actualValue, 3) + } + if actualValue, ok := queue.Dequeue(); actualValue != nil || ok { + t.Errorf("Got %v expected %v", actualValue, nil) + } + if actualValue := queue.Empty(); actualValue != true { + t.Errorf("Got %v expected %v", actualValue, true) + } + if actualValue := queue.Values(); len(actualValue) != 0 { + t.Errorf("Got %v expected %v", actualValue, "[]") + } +} + +func TestQueueIterator(t *testing.T) { + queue := New() + queue.Enqueue("a") + queue.Enqueue("b") + queue.Enqueue("c") + + // Iterator + it := queue.Iterator() + count := 0 + for it.Next() { + count++ + index := it.Index() + value := it.Value() + switch index { + case 0: + if actualValue, expectedValue := value, "a"; actualValue != expectedValue { + t.Errorf("Got %v expected %v", actualValue, expectedValue) + } + case 1: + if actualValue, expectedValue := value, "b"; actualValue != expectedValue { + t.Errorf("Got %v expected %v", actualValue, expectedValue) + } + case 2: + if actualValue, expectedValue := value, "c"; actualValue != expectedValue { + t.Errorf("Got %v expected %v", actualValue, expectedValue) + } + default: + t.Errorf("Too many") + } + if actualValue, expectedValue := index, count-1; actualValue != expectedValue { + t.Errorf("Got %v expected %v", actualValue, expectedValue) + } + } + if actualValue, expectedValue := count, 3; actualValue != expectedValue { + t.Errorf("Got %v expected %v", actualValue, expectedValue) + } + + queue.Clear() + it = queue.Iterator() + for it.Next() { + t.Errorf("Shouldn't iterate on empty queue") + } +} + +func TestQueueIteratorBegin(t *testing.T) { + queue := New() + it := queue.Iterator() + it.Begin() + queue.Enqueue("a") + queue.Enqueue("b") + queue.Enqueue("c") + for it.Next() { + } + it.Begin() + it.Next() + if index, value := it.Index(), it.Value(); index != 0 || value != "a" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "a") + } +} + +func TestQueueIteratorFirst(t *testing.T) { + queue := New() + it := queue.Iterator() + if actualValue, expectedValue := it.First(), false; actualValue != expectedValue { + t.Errorf("Got %v expected %v", actualValue, expectedValue) + } + queue.Enqueue("a") + queue.Enqueue("b") + queue.Enqueue("c") + if actualValue, expectedValue := it.First(), true; actualValue != expectedValue { + t.Errorf("Got %v expected %v", actualValue, expectedValue) + } + if index, value := it.Index(), it.Value(); index != 0 || value != "a" { + t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "a") + } +} + +func TestQueueSerialization(t *testing.T) { + queue := New() + queue.Enqueue("a") + queue.Enqueue("b") + queue.Enqueue("c") + + var err error + assert := func() { + if actualValue, expectedValue := fmt.Sprintf("%s%s%s", queue.Values()...), "abc"; actualValue != expectedValue { + t.Errorf("Got %v expected %v", actualValue, expectedValue) + } + if actualValue, expectedValue := queue.Size(), 3; actualValue != expectedValue { + t.Errorf("Got %v expected %v", actualValue, expectedValue) + } + if err != nil { + t.Errorf("Got error %v", err) + } + } + + assert() + + json, err := queue.ToJSON() + assert() + + err = queue.FromJSON(json) + assert() +} + +func benchmarkEnqueue(b *testing.B, queue *Queue, size int) { + for i := 0; i < b.N; i++ { + for n := 0; n < size; n++ { + queue.Enqueue(n) + } + } +} + +func benchmarkDequeue(b *testing.B, queue *Queue, size int) { + for i := 0; i < b.N; i++ { + for n := 0; n < size; n++ { + queue.Dequeue() + } + } +} + +func BenchmarkLinkedListQueueDequeue100(b *testing.B) { + b.StopTimer() + size := 100 + queue := New() + for n := 0; n < size; n++ { + queue.Enqueue(n) + } + b.StartTimer() + benchmarkDequeue(b, queue, size) +} + +func BenchmarkLinkedListQueueDequeue1000(b *testing.B) { + b.StopTimer() + size := 1000 + queue := New() + for n := 0; n < size; n++ { + queue.Enqueue(n) + } + b.StartTimer() + benchmarkDequeue(b, queue, size) +} + +func BenchmarkLinkedListQueueDequeue10000(b *testing.B) { + b.StopTimer() + size := 10000 + queue := New() + for n := 0; n < size; n++ { + queue.Enqueue(n) + } + b.StartTimer() + benchmarkDequeue(b, queue, size) +} + +func BenchmarkLinkedListQueueDequeue100000(b *testing.B) { + b.StopTimer() + size := 100000 + queue := New() + for n := 0; n < size; n++ { + queue.Enqueue(n) + } + b.StartTimer() + benchmarkDequeue(b, queue, size) +} + +func BenchmarkLinkedListQueueEnqueue100(b *testing.B) { + b.StopTimer() + size := 100 + stack := New() + b.StartTimer() + benchmarkEnqueue(b, stack, size) +} + +func BenchmarkLinkedListQueueEnqueue1000(b *testing.B) { + b.StopTimer() + size := 1000 + queue := New() + for n := 0; n < size; n++ { + queue.Enqueue(n) + } + b.StartTimer() + benchmarkEnqueue(b, queue, size) +} + +func BenchmarkLinkedListQueueEnqueue10000(b *testing.B) { + b.StopTimer() + size := 10000 + queue := New() + for n := 0; n < size; n++ { + queue.Enqueue(n) + } + b.StartTimer() + benchmarkEnqueue(b, queue, size) +} + +func BenchmarkLinkedListQueueEnqueue100000(b *testing.B) { + b.StopTimer() + size := 100000 + queue := New() + for n := 0; n < size; n++ { + queue.Enqueue(n) + } + b.StartTimer() + benchmarkEnqueue(b, queue, size) +} diff --git a/queues/arrayqueue/iterator.go b/queues/arrayqueue/iterator.go new file mode 100644 index 0000000..5e41319 --- /dev/null +++ b/queues/arrayqueue/iterator.go @@ -0,0 +1,84 @@ +// Copyright (c) 2021, Aryan Ahadinia. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package arrayqueue + +import "github.com/emirpasic/gods/containers" + +func assertIteratorImplementation() { + var _ containers.ReverseIteratorWithIndex = (*Iterator)(nil) +} + +// Iterator returns a stateful iterator whose values can be fetched by an index. +type Iterator struct { + queue *Queue + index int +} + +// Iterator returns a stateful iterator whose values can be fetched by an index. +func (queue *Queue) Iterator() Iterator { + return Iterator{queue: queue, index: -1} +} + +// Next moves the iterator to the next element and returns true if there was a next element in the container. +// If Next() returns true, then next element's index and value can be retrieved by Index() and Value(). +// If Next() was called for the first time, then it will point the iterator to the first element if it exists. +// Modifies the state of the iterator. +func (iterator *Iterator) Next() bool { + if iterator.index < iterator.queue.Size() { + iterator.index++ + } + return iterator.queue.withinRange(iterator.index) +} + +// Prev moves the iterator to the previous element and returns true if there was a previous element in the container. +// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) Prev() bool { + if iterator.index >= 0 { + iterator.index-- + } + return iterator.queue.withinRange(iterator.index) +} + +// Value returns the current element's value. +// Does not modify the state of the iterator. +func (iterator *Iterator) Value() interface{} { + value, _ := iterator.queue.list.Get(iterator.index) // in order (FIFO) + return value +} + +// Index returns the current element's index. +// Does not modify the state of the iterator. +func (iterator *Iterator) Index() int { + return iterator.index +} + +// Begin resets the iterator to its initial state (one-before-first) +// Call Next() to fetch the first element if any. +func (iterator *Iterator) Begin() { + iterator.index = -1 +} + +// End moves the iterator past the last element (one-past-the-end). +// Call Prev() to fetch the last element if any. +func (iterator *Iterator) End() { + iterator.index = iterator.queue.Size() +} + +// First moves the iterator to the first element and returns true if there was a first element in the container. +// If First() returns true, then first element's index and value can be retrieved by Index() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) First() bool { + iterator.Begin() + return iterator.Next() +} + +// Last moves the iterator to the last element and returns true if there was a last element in the container. +// If Last() returns true, then last element's index and value can be retrieved by Index() and Value(). +// Modifies the state of the iterator. +func (iterator *Iterator) Last() bool { + iterator.End() + return iterator.Prev() +} diff --git a/queues/arrayqueue/serialization.go b/queues/arrayqueue/serialization.go new file mode 100644 index 0000000..45d554f --- /dev/null +++ b/queues/arrayqueue/serialization.go @@ -0,0 +1,22 @@ +// Copyright (c) 2021, Aryan Ahadinia. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package arrayqueue + +import "github.com/emirpasic/gods/containers" + +func assertSerializationImplementation() { + var _ containers.JSONSerializer = (*Queue)(nil) + var _ containers.JSONDeserializer = (*Queue)(nil) +} + +// ToJSON outputs the JSON representation of the queue. +func (queue *Queue) ToJSON() ([]byte, error) { + return queue.list.ToJSON() +} + +// FromJSON populates the queue from the input JSON representation. +func (queue *Queue) FromJSON(data []byte) error { + return queue.list.FromJSON(data) +}