From d876ac8018f31103b2840d368dc87f444b340787 Mon Sep 17 00:00:00 2001 From: Aram Ceballos Date: Fri, 9 Aug 2024 10:21:11 -0600 Subject: [PATCH 1/2] feat: add circular queue array data structure --- structure/circularqueue/circularqueue_test.go | 199 ++++++++++++++++++ structure/circularqueue/circularqueuearray.go | 96 +++++++++ 2 files changed, 295 insertions(+) create mode 100644 structure/circularqueue/circularqueue_test.go create mode 100644 structure/circularqueue/circularqueuearray.go diff --git a/structure/circularqueue/circularqueue_test.go b/structure/circularqueue/circularqueue_test.go new file mode 100644 index 000000000..c4d2c14df --- /dev/null +++ b/structure/circularqueue/circularqueue_test.go @@ -0,0 +1,199 @@ +package circularqueue + +import "testing" + +func TestCircularQueue(t *testing.T) { + t.Run("Size Check", func(t *testing.T) { + _, err := NewCircularQueue[int](-3) + if err == nil { + t.Errorf("Expected error, got nil") + } + + queue, _ := NewCircularQueue[int](5) + expectedSize := 5 + gotSize := queue.Size() + if gotSize != expectedSize { + t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize) + } + + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + queue.Enqueue(4) + queue.Enqueue(5) + + err = queue.Enqueue(6) + if err == nil { + t.Errorf("Expected error, got nil") + } + + expectedSize = 5 + gotSize = queue.Size() + if gotSize != expectedSize { + t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize) + } + + queue.Dequeue() + queue.Dequeue() + + err = queue.Enqueue(6) + if err != nil { + t.Errorf("Expected nil, got error: %v\n", err.Error()) + } + + expectedSize = 5 + gotSize = queue.Size() + if gotSize != expectedSize { + t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize) + } + }) + t.Run("Enqueue", func(t *testing.T) { + queue, _ := NewCircularQueue[int](10) + + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + + expected := 1 + got, err := queue.Peek() + if err != nil { + t.Error(err.Error()) + } + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) + t.Run("Dequeue", func(t *testing.T) { + queue, _ := NewCircularQueue[string](10) + + queue.Enqueue("one") + queue.Enqueue("two") + queue.Enqueue("three") + + expected := "one" + got, err := queue.Dequeue() + if err != nil { + t.Error(err.Error()) + } + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + + expected = "two" + got, err = queue.Peek() + if err != nil { + t.Error(err.Error()) + } + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) + t.Run("Circularity", func(t *testing.T) { + queue, _ := NewCircularQueue[int](10) + + queue.Enqueue(1) + queue.Enqueue(2) + queue.Enqueue(3) + queue.Dequeue() + queue.Dequeue() + queue.Enqueue(4) + queue.Enqueue(5) + queue.Dequeue() + + expected := 4 + got, err := queue.Peek() + if err != nil { + t.Error(err.Error()) + } + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) + t.Run("IsFull", func(t *testing.T) { + queue, _ := NewCircularQueue[bool](2) + queue.Enqueue(false) + queue.Enqueue(true) + + expected := true + got := queue.IsFull() + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + + queue.Dequeue() + queue.Dequeue() + + expected = false + got = queue.IsFull() + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) + t.Run("IsEmpty", func(t *testing.T) { + queue, _ := NewCircularQueue[float64](2) + + expected := true + got := queue.IsEmpty() + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + + queue.Enqueue(1.0) + + expected = false + got = queue.IsEmpty() + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + + }) + t.Run("Peak", func(t *testing.T) { + queue, _ := NewCircularQueue[rune](10) + + queue.Enqueue('a') + queue.Enqueue('b') + queue.Enqueue('c') + + expected := 'a' + got, err := queue.Peek() + if err != nil { + t.Error(err.Error()) + } + + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + }) +} + +// BenchmarkCircularQueue benchmarks the CircularQueue implementation. +func BenchmarkCircularQueue(b *testing.B) { + b.Run("Enqueue", func(b *testing.B) { + queue, _ := NewCircularQueue[int](1000) + for i := 0; i < b.N; i++ { + queue.Enqueue(i) + } + }) + + b.Run("Dequeue", func(b *testing.B) { + queue, _ := NewCircularQueue[int](1000) + for i := 0; i < 1000; i++ { + queue.Enqueue(i) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + queue.Dequeue() + } + }) + + b.Run("Peek", func(b *testing.B) { + queue, _ := NewCircularQueue[int](1000) + for i := 0; i < 1000; i++ { + queue.Enqueue(i) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + queue.Peek() + } + }) +} diff --git a/structure/circularqueue/circularqueuearray.go b/structure/circularqueue/circularqueuearray.go new file mode 100644 index 000000000..97447fa4a --- /dev/null +++ b/structure/circularqueue/circularqueuearray.go @@ -0,0 +1,96 @@ +// circularqueuearray.go +// description: Implementation of a circular queue data structure +// details: +// This file contains the implementation of a circular queue data structure +// using generics in Go. The circular queue supports basic operations such as +// enqueue, dequeue, peek, and checks for full and empty states. +// author(s): [Aram Ceballos](https://github.com/aramceballos) +// ref: https://www.programiz.com/dsa/circular-queue +// ref: https://en.wikipedia.org/wiki/Circular_buffer + +// Package queue provides an implementation of a circular queue data structure. +package circularqueue + +// errors package: Provides functions to create and manipulate error values +import ( + "errors" +) + +// CircularQueue represents a circular queue data structure. +type CircularQueue[T any] struct { + items []T + front int + rear int + size int +} + +// NewCircularQueue creates a new CircularQueue with the given size. +// Returns an error if the size is less than or equal to 0. +func NewCircularQueue[T any](size int) (*CircularQueue[T], error) { + if size <= 0 { + return nil, errors.New("size must be greater than 0") + } + return &CircularQueue[T]{ + items: make([]T, size), + front: -1, + rear: -1, + size: size, + }, nil +} + +// Enqueue adds an item to the rear of the queue. +// Returns an error if the queue is full. +func (cq *CircularQueue[T]) Enqueue(item T) error { + if cq.IsFull() { + return errors.New("queue is full") + } + if cq.IsEmpty() { + cq.front = 0 + } + cq.rear = (cq.rear + 1) % cq.size + cq.items[cq.rear] = item + return nil +} + +// Dequeue removes and returns the item from the front of the queue. +// Returns an error if the queue is empty. +func (cq *CircularQueue[T]) Dequeue() (T, error) { + if cq.IsEmpty() { + var zeroValue T + return zeroValue, errors.New("queue is empty") + } + retVal := cq.items[cq.front] + if cq.front == cq.rear { + cq.front = -1 + cq.rear = -1 + } else { + cq.front = (cq.front + 1) % cq.size + } + return retVal, nil +} + +// IsFull checks if the queue is full. +func (cq *CircularQueue[T]) IsFull() bool { + return (cq.front == 0 && cq.rear == cq.size-1) || cq.front == cq.rear+1 +} + +// IsEmpty checks if the queue is empty. +func (cq *CircularQueue[T]) IsEmpty() bool { + return cq.front == -1 && cq.rear == -1 +} + +// Peek returns the item at the front of the queue without removing it. +// Returns an error if the queue is empty. +func (cq *CircularQueue[T]) Peek() (T, error) { + if cq.IsEmpty() { + var zeroValue T + return zeroValue, errors.New("queue is empty") + } + + return cq.items[cq.front], nil +} + +// Size returns the size of the queue. +func (cq *CircularQueue[T]) Size() int { + return cq.size +} From 05a0a2a6632a015d6813d4c28ec82c92cadacd23 Mon Sep 17 00:00:00 2001 From: Aram Ceballos Date: Sat, 10 Aug 2024 19:03:00 -0700 Subject: [PATCH 2/2] test: add missing error handling --- structure/circularqueue/circularqueue_test.go | 136 +++++++++++++----- 1 file changed, 102 insertions(+), 34 deletions(-) diff --git a/structure/circularqueue/circularqueue_test.go b/structure/circularqueue/circularqueue_test.go index c4d2c14df..0820b0470 100644 --- a/structure/circularqueue/circularqueue_test.go +++ b/structure/circularqueue/circularqueue_test.go @@ -16,11 +16,21 @@ func TestCircularQueue(t *testing.T) { t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize) } - queue.Enqueue(1) - queue.Enqueue(2) - queue.Enqueue(3) - queue.Enqueue(4) - queue.Enqueue(5) + if err := queue.Enqueue(1); err != nil { + t.Error(err) + } + if err := queue.Enqueue(2); err != nil { + t.Error(err) + } + if err := queue.Enqueue(3); err != nil { + t.Error(err) + } + if err := queue.Enqueue(4); err != nil { + t.Error(err) + } + if err := queue.Enqueue(5); err != nil { + t.Error(err) + } err = queue.Enqueue(6) if err == nil { @@ -33,8 +43,12 @@ func TestCircularQueue(t *testing.T) { t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize) } - queue.Dequeue() - queue.Dequeue() + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } err = queue.Enqueue(6) if err != nil { @@ -50,9 +64,15 @@ func TestCircularQueue(t *testing.T) { t.Run("Enqueue", func(t *testing.T) { queue, _ := NewCircularQueue[int](10) - queue.Enqueue(1) - queue.Enqueue(2) - queue.Enqueue(3) + if err := queue.Enqueue(1); err != nil { + t.Error(err) + } + if err := queue.Enqueue(2); err != nil { + t.Error(err) + } + if err := queue.Enqueue(3); err != nil { + t.Error(err) + } expected := 1 got, err := queue.Peek() @@ -66,9 +86,15 @@ func TestCircularQueue(t *testing.T) { t.Run("Dequeue", func(t *testing.T) { queue, _ := NewCircularQueue[string](10) - queue.Enqueue("one") - queue.Enqueue("two") - queue.Enqueue("three") + if err := queue.Enqueue("one"); err != nil { + t.Error(err) + } + if err := queue.Enqueue("two"); err != nil { + t.Error(err) + } + if err := queue.Enqueue("three"); err != nil { + t.Error(err) + } expected := "one" got, err := queue.Dequeue() @@ -91,14 +117,30 @@ func TestCircularQueue(t *testing.T) { t.Run("Circularity", func(t *testing.T) { queue, _ := NewCircularQueue[int](10) - queue.Enqueue(1) - queue.Enqueue(2) - queue.Enqueue(3) - queue.Dequeue() - queue.Dequeue() - queue.Enqueue(4) - queue.Enqueue(5) - queue.Dequeue() + if err := queue.Enqueue(1); err != nil { + t.Error(err) + } + if err := queue.Enqueue(2); err != nil { + t.Error(err) + } + if err := queue.Enqueue(3); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + if err := queue.Enqueue(4); err != nil { + t.Error(err) + } + if err := queue.Enqueue(5); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } expected := 4 got, err := queue.Peek() @@ -111,8 +153,12 @@ func TestCircularQueue(t *testing.T) { }) t.Run("IsFull", func(t *testing.T) { queue, _ := NewCircularQueue[bool](2) - queue.Enqueue(false) - queue.Enqueue(true) + if err := queue.Enqueue(false); err != nil { + t.Error(err) + } + if err := queue.Enqueue(true); err != nil { + t.Error(err) + } expected := true got := queue.IsFull() @@ -120,8 +166,12 @@ func TestCircularQueue(t *testing.T) { t.Errorf("Expected: %v got: %v\n", expected, got) } - queue.Dequeue() - queue.Dequeue() + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } expected = false got = queue.IsFull() @@ -138,7 +188,9 @@ func TestCircularQueue(t *testing.T) { t.Errorf("Expected: %v got: %v\n", expected, got) } - queue.Enqueue(1.0) + if err := queue.Enqueue(1.0); err != nil { + t.Error(err) + } expected = false got = queue.IsEmpty() @@ -150,9 +202,15 @@ func TestCircularQueue(t *testing.T) { t.Run("Peak", func(t *testing.T) { queue, _ := NewCircularQueue[rune](10) - queue.Enqueue('a') - queue.Enqueue('b') - queue.Enqueue('c') + if err := queue.Enqueue('a'); err != nil { + t.Error(err) + } + if err := queue.Enqueue('b'); err != nil { + t.Error(err) + } + if err := queue.Enqueue('c'); err != nil { + t.Error(err) + } expected := 'a' got, err := queue.Peek() @@ -171,29 +229,39 @@ func BenchmarkCircularQueue(b *testing.B) { b.Run("Enqueue", func(b *testing.B) { queue, _ := NewCircularQueue[int](1000) for i := 0; i < b.N; i++ { - queue.Enqueue(i) + if err := queue.Enqueue(i); err != nil { + b.Error(err) + } } }) b.Run("Dequeue", func(b *testing.B) { queue, _ := NewCircularQueue[int](1000) for i := 0; i < 1000; i++ { - queue.Enqueue(i) + if err := queue.Enqueue(i); err != nil { + b.Error(err) + } } b.ResetTimer() for i := 0; i < b.N; i++ { - queue.Dequeue() + if _, err := queue.Dequeue(); err != nil { + b.Error(err) + } } }) b.Run("Peek", func(b *testing.B) { queue, _ := NewCircularQueue[int](1000) for i := 0; i < 1000; i++ { - queue.Enqueue(i) + if err := queue.Enqueue(i); err != nil { + b.Error(err) + } } b.ResetTimer() for i := 0; i < b.N; i++ { - queue.Peek() + if _, err := queue.Peek(); err != nil { + b.Error(err) + } } }) }