diff --git a/structure/circularqueue/circularqueue_test.go b/structure/circularqueue/circularqueue_test.go new file mode 100644 index 000000000..0820b0470 --- /dev/null +++ b/structure/circularqueue/circularqueue_test.go @@ -0,0 +1,267 @@ +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) + } + + 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 { + t.Errorf("Expected error, got nil") + } + + expectedSize = 5 + gotSize = queue.Size() + if gotSize != expectedSize { + t.Errorf("Expected size: %v, got: %v\n", expectedSize, gotSize) + } + + 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 { + 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) + + 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() + 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) + + 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() + 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) + + 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() + 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) + 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() + if got != expected { + t.Errorf("Expected: %v got: %v\n", expected, got) + } + + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + if _, err := queue.Dequeue(); err != nil { + t.Error(err) + } + + 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) + } + + if err := queue.Enqueue(1.0); err != nil { + t.Error(err) + } + + 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) + + 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() + 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++ { + 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++ { + if err := queue.Enqueue(i); err != nil { + b.Error(err) + } + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + 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++ { + if err := queue.Enqueue(i); err != nil { + b.Error(err) + } + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := queue.Peek(); err != nil { + b.Error(err) + } + } + }) +} 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 +}