From 3cbbe78bb21ef220b2ec93f6641dc44849462205 Mon Sep 17 00:00:00 2001 From: Walmir Silva Date: Fri, 28 Jun 2024 19:33:15 -0300 Subject: [PATCH] Add ArrayDeque and ArrayQueue Implementations with Comprehensive Unit Tests - Implemented the ArrayDeque and ArrayQueue classes in the KaririCode\DataStructure\Queue namespace. - Added comprehensive unit tests for the ArrayDeque and ArrayQueue classes in the KaririCode\DataStructure\Tests\Queue namespace. Classes: - KaririCode\DataStructure\Queue\ArrayDeque - KaririCode\DataStructure\Queue\ArrayQueue Tests: - KaririCode\DataStructure\Tests\Queue\ArrayDequeTest - KaririCode\DataStructure\Tests\Queue\ArrayQueueTest Fully covered features and tests: ArrayDeque: - Enqueue method: testEnqueueAddsElementToEndOfDeque - Dequeue method: testDequeueRemovesElementFromFrontOfDeque - Dequeue from empty deque: testDequeueFromEmptyDequeReturnsNull - Peek method: testPeekReturnsElementFromFrontWithoutRemovingIt - Peek from empty deque: testPeekFromEmptyDequeReturnsNull - Add first method: testAddFirstAddsElementToFrontOfDeque - Remove last method: testRemoveLastRemovesElementFromEndOfDeque - Remove last from empty deque: testRemoveLastFromEmptyDequeReturnsNull - Peek last method: testPeekLastReturnsElementFromEndWithoutRemovingIt - Peek last from empty deque: testPeekLastFromEmptyDequeReturnsNull - Is empty method: testIsEmptyReturnsTrueIfDequeIsEmpty - Size method: testSizeReturnsNumberOfElementsInDeque - Ensure capacity method: testEnsureCapacityDoublesCapacityWhenFull - Handling null values: testHandlingNullValuesCorrectly - Circular behavior: testCircularBehavior - Various data types: testDequeWithVariousDataTypes - Mixed operations: testDequeBehaviorAfterMixedOperations ArrayQueue: - Enqueue method: testEnqueueAddsElementToEndOfQueue - Dequeue method: testDequeueRemovesElementFromFrontOfQueue - Dequeue from empty queue: testDequeueFromEmptyQueueReturnsNull - Peek method: testPeekReturnsElementFromFrontWithoutRemovingIt - Peek from empty queue: testPeekFromEmptyQueueReturnsNull - Is empty method: testIsEmptyReturnsTrueIfQueueIsEmpty - Size method: testSizeReturnsNumberOfElementsInQueue - Ensure capacity method: testEnsureCapacityDoublesCapacityWhenFull - Handling null values: testHandlingNullValuesCorrectly - Various data types: testQueueWithVariousDataTypes - Mixed operations: testQueueBehaviorAfterMixedOperations --- src/Queue/ArrayDeque.php | 115 ++++++++++++++++++++++ src/Queue/ArrayQueue.php | 87 ++++++++++++++++ tests/Queue/ArrayDequeTest.php | 175 +++++++++++++++++++++++++++++++++ tests/Queue/ArrayQueueTest.php | 118 ++++++++++++++++++++++ 4 files changed, 495 insertions(+) create mode 100644 src/Queue/ArrayDeque.php create mode 100644 tests/Queue/ArrayDequeTest.php create mode 100644 tests/Queue/ArrayQueueTest.php diff --git a/src/Queue/ArrayDeque.php b/src/Queue/ArrayDeque.php new file mode 100644 index 0000000..f64a000 --- /dev/null +++ b/src/Queue/ArrayDeque.php @@ -0,0 +1,115 @@ + + */ +class ArrayDeque implements Queue +{ + private array $elements; + private int $front = 0; + private int $size = 0; + private int $capacity; + + public function __construct(int $initialCapacity = 16) + { + $this->capacity = $initialCapacity; + $this->elements = array_fill(0, $this->capacity, null); + } + + public function enqueue(mixed $element): void + { + $this->ensureCapacity(); + $index = ($this->front + $this->size) % $this->capacity; + $this->elements[$index] = $element; + ++$this->size; + } + + public function dequeue(): mixed + { + if ($this->isEmpty()) { + return null; + } + $element = $this->elements[$this->front]; + $this->elements[$this->front] = null; + $this->front = ($this->front + 1) % $this->capacity; + --$this->size; + + return $element; + } + + public function peek(): mixed + { + return $this->isEmpty() ? null : $this->elements[$this->front]; + } + + public function addFirst(mixed $element): void + { + $this->ensureCapacity(); + $this->front = ($this->front - 1 + $this->capacity) % $this->capacity; + $this->elements[$this->front] = $element; + ++$this->size; + } + + public function removeLast(): mixed + { + if ($this->isEmpty()) { + return null; + } + $index = ($this->front + $this->size - 1) % $this->capacity; + $element = $this->elements[$index]; + $this->elements[$index] = null; + --$this->size; + + return $element; + } + + public function peekLast(): mixed + { + if ($this->isEmpty()) { + return null; + } + $index = ($this->front + $this->size - 1) % $this->capacity; + + return $this->elements[$index]; + } + + public function isEmpty(): bool + { + return 0 === $this->size; + } + + public function size(): int + { + return $this->size; + } + + /** + * Ensures that the deque has enough capacity to add a new element. + */ + private function ensureCapacity(): void + { + if ($this->size === $this->capacity) { + $newCapacity = $this->capacity * 2; + $newElements = array_fill(0, $newCapacity, null); + for ($i = 0; $i < $this->size; ++$i) { + $newElements[$i] = $this->elements[($this->front + $i) % $this->capacity]; + } + $this->elements = $newElements; + $this->front = 0; + $this->capacity = $newCapacity; + } + } +} diff --git a/src/Queue/ArrayQueue.php b/src/Queue/ArrayQueue.php index e69de29..e5ece5e 100644 --- a/src/Queue/ArrayQueue.php +++ b/src/Queue/ArrayQueue.php @@ -0,0 +1,87 @@ + + * @license MIT + * + * @see https://kariricode.org/ + */ +class ArrayQueue implements Queue +{ + private array $elements; + private int $front = 0; + private int $size = 0; + private int $capacity; + + public function __construct(int $initialCapacity = 16) + { + $this->capacity = $initialCapacity; + $this->elements = array_fill(0, $this->capacity, null); + } + + public function enqueue(mixed $element): void + { + $this->ensureCapacity(); + $index = ($this->front + $this->size) % $this->capacity; + $this->elements[$index] = $element; + ++$this->size; + } + + public function dequeue(): mixed + { + if ($this->isEmpty()) { + return null; + } + $element = $this->elements[$this->front]; + $this->elements[$this->front] = null; + $this->front = ($this->front + 1) % $this->capacity; + --$this->size; + + return $element; + } + + public function peek(): mixed + { + return $this->isEmpty() ? null : $this->elements[$this->front]; + } + + public function isEmpty(): bool + { + return 0 === $this->size; + } + + public function size(): int + { + return $this->size; + } + + /** + * Ensures that the queue has enough capacity to add a new element. + */ + private function ensureCapacity(): void + { + if ($this->size === $this->capacity) { + $newCapacity = $this->capacity * 2; + $newElements = array_fill(0, $newCapacity, null); + for ($i = 0; $i < $this->size; ++$i) { + $newElements[$i] = $this->elements[($this->front + $i) % $this->capacity]; + } + $this->elements = $newElements; + $this->front = 0; + $this->capacity = $newCapacity; + } + } +} diff --git a/tests/Queue/ArrayDequeTest.php b/tests/Queue/ArrayDequeTest.php new file mode 100644 index 0000000..53c26cb --- /dev/null +++ b/tests/Queue/ArrayDequeTest.php @@ -0,0 +1,175 @@ +enqueue(1); + $this->assertSame(1, $deque->peek()); + } + + // Test dequeuing elements + public function testDequeueRemovesElementFromFrontOfDeque(): void + { + $deque = new ArrayDeque(); + $deque->enqueue(1); + $deque->enqueue(2); + $this->assertSame(1, $deque->dequeue()); + $this->assertSame(2, $deque->peek()); + } + + // Test dequeuing from an empty deque + public function testDequeueFromEmptyDequeReturnsNull(): void + { + $deque = new ArrayDeque(); + $this->assertNull($deque->dequeue()); + } + + // Test peeking elements + public function testPeekReturnsElementFromFrontWithoutRemovingIt(): void + { + $deque = new ArrayDeque(); + $deque->enqueue(1); + $this->assertSame(1, $deque->peek()); + $this->assertSame(1, $deque->peek()); + } + + // Test peeking from an empty deque + public function testPeekFromEmptyDequeReturnsNull(): void + { + $deque = new ArrayDeque(); + $this->assertNull($deque->peek()); + } + + // Test adding elements to the front + public function testAddFirstAddsElementToFrontOfDeque(): void + { + $deque = new ArrayDeque(); + $deque->enqueue(1); + $deque->addFirst(2); + $this->assertSame(2, $deque->peek()); + } + + // Test removing last elements + public function testRemoveLastRemovesElementFromEndOfDeque(): void + { + $deque = new ArrayDeque(); + $deque->enqueue(1); + $deque->enqueue(2); + $this->assertSame(2, $deque->removeLast()); + $this->assertSame(1, $deque->peekLast()); + } + + // Test removing last element from an empty deque + public function testRemoveLastFromEmptyDequeReturnsNull(): void + { + $deque = new ArrayDeque(); + $this->assertNull($deque->removeLast()); + } + + // Test peeking last elements + public function testPeekLastReturnsElementFromEndWithoutRemovingIt(): void + { + $deque = new ArrayDeque(); + $deque->enqueue(1); + $deque->enqueue(2); + $this->assertSame(2, $deque->peekLast()); + $this->assertSame(2, $deque->peekLast()); + } + + // Test peeking last from an empty deque + public function testPeekLastFromEmptyDequeReturnsNull(): void + { + $deque = new ArrayDeque(); + $this->assertNull($deque->peekLast()); + } + + // Test checking if deque is empty + public function testIsEmptyReturnsTrueIfDequeIsEmpty(): void + { + $deque = new ArrayDeque(); + $this->assertTrue($deque->isEmpty()); + $deque->enqueue(1); + $this->assertFalse($deque->isEmpty()); + } + + // Test getting the size of the deque + public function testSizeReturnsNumberOfElementsInDeque(): void + { + $deque = new ArrayDeque(); + $this->assertSame(0, $deque->size()); + $deque->enqueue(1); + $deque->enqueue(2); + $this->assertSame(2, $deque->size()); + } + + // Test ensuring capacity of deque + public function testEnsureCapacityDoublesCapacityWhenFull(): void + { + $deque = new ArrayDeque(2); + $deque->enqueue(1); + $deque->enqueue(2); + $deque->enqueue(3); // Should trigger capacity increase + $this->assertSame(3, $deque->size()); + } + + // Test handling null values in the deque + public function testHandlingNullValuesCorrectly(): void + { + $deque = new ArrayDeque(); + $deque->enqueue(null); + $this->assertSame(null, $deque->dequeue()); + } + + // Test circular nature of the deque + public function testCircularBehavior(): void + { + $deque = new ArrayDeque(3); + $deque->enqueue(1); + $deque->enqueue(2); + $deque->enqueue(3); + $deque->dequeue(); + $deque->enqueue(4); + $this->assertSame(2, $deque->dequeue()); + $this->assertSame(3, $deque->dequeue()); + $this->assertSame(4, $deque->dequeue()); + } + + // Test deque with various data types + public function testDequeWithVariousDataTypes(): void + { + $deque = new ArrayDeque(); + $deque->enqueue(123); + $deque->enqueue('string'); + $deque->enqueue([1, 2, 3]); + $deque->enqueue(new \stdClass()); + + $this->assertSame(123, $deque->dequeue()); + $this->assertSame('string', $deque->dequeue()); + $this->assertSame([1, 2, 3], $deque->dequeue()); + $this->assertInstanceOf(\stdClass::class, $deque->dequeue()); + } + + // Test deque behavior after mixed operations + public function testDequeBehaviorAfterMixedOperations(): void + { + $deque = new ArrayDeque(); + $deque->enqueue(1); + $deque->enqueue(2); + $deque->addFirst(0); + $deque->removeLast(); + $deque->enqueue(3); + $this->assertSame(0, $deque->dequeue()); + $this->assertSame(1, $deque->dequeue()); + $this->assertSame(3, $deque->peekLast()); + } +} diff --git a/tests/Queue/ArrayQueueTest.php b/tests/Queue/ArrayQueueTest.php new file mode 100644 index 0000000..536080b --- /dev/null +++ b/tests/Queue/ArrayQueueTest.php @@ -0,0 +1,118 @@ +enqueue(1); + $this->assertSame(1, $queue->peek()); + } + + // Test dequeuing elements + public function testDequeueRemovesElementFromFrontOfQueue(): void + { + $queue = new ArrayQueue(); + $queue->enqueue(1); + $queue->enqueue(2); + $this->assertSame(1, $queue->dequeue()); + $this->assertSame(2, $queue->peek()); + } + + // Test dequeuing from an empty queue + public function testDequeueFromEmptyQueueReturnsNull(): void + { + $queue = new ArrayQueue(); + $this->assertNull($queue->dequeue()); + } + + // Test peeking elements + public function testPeekReturnsElementFromFrontWithoutRemovingIt(): void + { + $queue = new ArrayQueue(); + $queue->enqueue(1); + $this->assertSame(1, $queue->peek()); + $this->assertSame(1, $queue->peek()); + } + + // Test peeking from an empty queue + public function testPeekFromEmptyQueueReturnsNull(): void + { + $queue = new ArrayQueue(); + $this->assertNull($queue->peek()); + } + + // Test checking if queue is empty + public function testIsEmptyReturnsTrueIfQueueIsEmpty(): void + { + $queue = new ArrayQueue(); + $this->assertTrue($queue->isEmpty()); + $queue->enqueue(1); + $this->assertFalse($queue->isEmpty()); + } + + // Test getting the size of the queue + public function testSizeReturnsNumberOfElementsInQueue(): void + { + $queue = new ArrayQueue(); + $this->assertSame(0, $queue->size()); + $queue->enqueue(1); + $queue->enqueue(2); + $this->assertSame(2, $queue->size()); + } + + // Test ensuring capacity of queue + public function testEnsureCapacityDoublesCapacityWhenFull(): void + { + $queue = new ArrayQueue(2); + $queue->enqueue(1); + $queue->enqueue(2); + $queue->enqueue(3); // Should trigger capacity increase + $this->assertSame(3, $queue->size()); + } + + // Test handling null values in the queue + public function testHandlingNullValuesCorrectly(): void + { + $queue = new ArrayQueue(); + $queue->enqueue(null); + $this->assertSame(null, $queue->dequeue()); + } + + // Test queue with various data types + public function testQueueWithVariousDataTypes(): void + { + $queue = new ArrayQueue(); + $queue->enqueue(123); + $queue->enqueue('string'); + $queue->enqueue([1, 2, 3]); + $queue->enqueue(new \stdClass()); + + $this->assertSame(123, $queue->dequeue()); + $this->assertSame('string', $queue->dequeue()); + $this->assertSame([1, 2, 3], $queue->dequeue()); + $this->assertInstanceOf(\stdClass::class, $queue->dequeue()); + } + + // Test queue behavior after mixed operations + public function testQueueBehaviorAfterMixedOperations(): void + { + $queue = new ArrayQueue(); + $queue->enqueue(1); + $queue->enqueue(2); + $queue->enqueue(3); + $queue->dequeue(); + $queue->enqueue(4); + $this->assertSame(2, $queue->dequeue()); + $this->assertSame(3, $queue->dequeue()); + $this->assertSame(4, $queue->peek()); + } +}