diff --git a/src/Containers-Queue-Tests/CTQueueTest.class.st b/src/Containers-Queue-Tests/CTQueueTest.class.st index d2b909b..6d1149e 100644 --- a/src/Containers-Queue-Tests/CTQueueTest.class.st +++ b/src/Containers-Queue-Tests/CTQueueTest.class.st @@ -1,8 +1,6 @@ " I'm a simple queue i.e., first in first out structure. I support basic collection protocol and in addition enqueue and dequeue as in Scala. - - " Class { #name : #CTQueueTest, @@ -25,6 +23,31 @@ CTQueueTest >> testAdd [ self assert: (queue includes: 2) ] +{ #category : #tests } +CTQueueTest >> testAddAll [ + "Ensure queueAll adds multiple elements at once." + | queue | + queue := self queueClass new. + queue addAll: #(10 20 30 40). + self assert: queue remove equals: 10. + self assert: queue remove equals: 20. + self assert: queue remove equals: 30. + self assert: queue remove equals: 40. +] + +{ #category : #tests } +CTQueueTest >> testAddAllLargeCollection [ + "Test adding a large collection via addAll." + | queue collection size | + queue := self queueClass new. + size := 50000. + collection := (1 to: size) asArray. + queue addAll: collection. + self assert: queue size equals: size. + 1 to: size do: [ :i | self assert: queue remove equals: i ]. + self assert: queue isEmpty. +] + { #category : #tests } CTQueueTest >> testAddGarantyFIFOOrder [ "Ensure elements are dequeued in FIFO order." @@ -40,37 +63,66 @@ CTQueueTest >> testAddGarantyFIFOOrder [ ] { #category : #tests } -CTQueueTest >> testEmptyQueue [ - self assert: self queueClass new isEmpty +CTQueueTest >> testAddNilElement [ + "Test adding a nil element to the queue." + | queue | + queue := self queueClass new. + queue add: nil. + self assert: queue size equals: 1. + self assert: queue peek isNil. + self assert: queue remove isNil. + self assert: queue isEmpty. ] { #category : #tests } -CTQueueTest >> testQueue [ - self assert: self queueClass new isEmpty +CTQueueTest >> testDoIteration [ + "Test iterating over the queue with do: in FIFO order." + | queue collected | + queue := self queueClass new. + queue addAll: #(10 20 30 40). + collected := OrderedCollection new. + queue do: [ :each | collected add: each ]. + self assert: collected asArray equals: #(10 20 30 40). + self assert: queue size equals: 4. "Iteration should not modify the queue" ] { #category : #tests } -CTQueueTest >> testQueueGarantyFIFOOrder [ +CTQueueTest >> testEmptyQueue [ self assert: self queueClass new isEmpty ] { #category : #tests } -CTQueueTest >> testAddAll [ - "Ensure queueAll adds multiple elements at once." +CTQueueTest >> testEmptyQueueRemove [ | queue | queue := self queueClass new. - queue addAll: #(10 20 30 40). - self assert: queue remove equals: 10. - self assert: queue remove equals: 20. - self assert: queue remove equals: 30. - self assert: queue remove equals: 40. + self assert: queue remove isNil. ] { #category : #tests } -CTQueueTest >> testEmptyQueueRemove [ +CTQueueTest >> testIncludes [ + "Test the includes: method for existing and non-existing elements." | queue | queue := self queueClass new. - self assert: queue remove isNil. + queue addAll: #(5 10 15). + self assert: (queue includes: 10). + self deny: (queue includes: 20). + self assert: queue size equals: 3. "includes: should not modify the queue" +] + +{ #category : #tests } +CTQueueTest >> testInterleavedAddRemove [ + "Test interleaved add and remove operations to ensure FIFO order and correctness." + | queue | + queue := self queueClass new. + queue add: 1. + queue add: 2. + self assert: queue remove equals: 1. + queue add: 3. + self assert: queue remove equals: 2. + queue add: 4. + self assert: queue remove equals: 3. + self assert: queue remove equals: 4. + self assert: queue isEmpty. ] { #category : #tests } @@ -85,6 +137,31 @@ CTQueueTest >> testIsEmpty [ self assert: queue isEmpty. ] +{ #category : #tests } +CTQueueTest >> testLargeQueuePerformance [ + "Verify FIFO behavior and performance with a large queue." + | queue size startTime endTime duration maxDuration | + queue := self queueClass new. + size := 100000. + + "Measure time to add elements" + startTime := DateAndTime now. + 1 to: size do: [ :i | queue add: i ]. + endTime := DateAndTime now. + duration := endTime - startTime. + maxDuration := 5 seconds. "Expect adding 100,000 elements to take less than 5 seconds" + self assert: duration < maxDuration description: 'Adding elements took too long'. + + "Measure time to remove elements and verify FIFO order" + startTime := DateAndTime now. + 1 to: size do: [ :i | self assert: queue remove equals: i ]. + endTime := DateAndTime now. + duration := endTime - startTime. + self assert: duration < maxDuration description: 'Removing elements took too long'. + + self assert: queue isEmpty. +] + { #category : #tests } CTQueueTest >> testPeek [ "Ensure peek returns the first element without removing it." @@ -108,6 +185,16 @@ CTQueueTest >> testPoll [ self assert: queue poll isNil. ] +{ #category : #tests } +CTQueueTest >> testQueue [ + self assert: self queueClass new isEmpty +] + +{ #category : #tests } +CTQueueTest >> testQueueGarantyFIFOOrder [ + self assert: self queueClass new isEmpty +] + { #category : #tests } CTQueueTest >> testRemove [ "Ensure remove behaves correctly, returning nil when empty." @@ -121,10 +208,44 @@ CTQueueTest >> testRemove [ ] { #category : #tests } -CTQueueTest >> testRemoveIfNone[ -"Ensure dequeueIfNone works correctly." +CTQueueTest >> testRemoveIfNone [ + "Ensure removeIfNone works correctly." | queue result | queue := self queueClass new. result := queue removeIfNone: [ 'fallback' ]. self assert: result equals: 'fallback'. +] + +{ #category : #tests } +CTQueueTest >> testSizeAccuracy [ + "Test that the size method accurately reflects the number of elements." + | queue | + queue := self queueClass new. + self assert: queue size equals: 0. + queue add: 1. + self assert: queue size equals: 1. + queue addAll: #(2 3 4). + self assert: queue size equals: 4. + queue remove. + self assert: queue size equals: 3. + queue removeIfNone: [ nil ]. + self assert: queue size equals: 2. +] + +{ #category : #tests } +CTQueueTest >> testStressAddRemove [ + "Stress test with many add and remove operations." + | queue iterations | + queue := self queueClass new. + iterations := 10000. + 1 to: iterations do: [ :i | + queue add: i. + self assert: queue size equals: 1. + self assert: queue remove equals: i. + self assert: queue isEmpty. + ]. + "Add multiple elements and remove them" + queue addAll: (1 to: 1000). + 1 to: 1000 do: [ :i | self assert: queue remove equals: i ]. + self assert: queue isEmpty. ] \ No newline at end of file diff --git a/src/Containers-Queue/CTQueue.class.st b/src/Containers-Queue/CTQueue.class.st index e3aeefb..2baf9bb 100644 --- a/src/Containers-Queue/CTQueue.class.st +++ b/src/Containers-Queue/CTQueue.class.st @@ -1,85 +1,111 @@ " -I'm a simple FIFO queue i.e., first in first out structure. I support basic collection protocol and in addition enqueue and dequeue as in Scala. -My basic support of collection API should be reviewd and probably improved (should check atomic queue protocol). - - +I'm a simple FIFO queue i.e., first in first out structure. I support basic collection protocol with efficient O(1) add and remove operations using a singly linked list. " Class { #name : #CTQueue, #superclass : #Object, #instVars : [ - 'elements' + 'head', + 'tail', + 'size' ], #category : #'Containers-Queue' } { #category : #adding } CTQueue >> add: anElement [ - "Add an element to the receiver. Note that the addition makes sure that when iterating over the receiver added first element are accessed first." - - elements addLast: anElement. + "Add an element to the end of the queue (FIFO order)." + | newNode | + newNode := CTQueueNode new value: anElement. + head ifNil: [ + head := newNode. + tail := newNode. + ] ifNotNil: [ + tail next: newNode. + tail := newNode. + ]. + size := size + 1. ^ anElement ] { #category : #adding } CTQueue >> addAll: aCollection [ - "Add the elements contained in the argument to the receiver. Note that the addition makes sure that when iterating over the receiver added first element are accessed first." - - elements addAllLast: aCollection. + "Add all elements from aCollection to the end of the queue." + aCollection do: [ :each | self add: each ]. ^ aCollection ] { #category : #iterating } CTQueue >> do: aBlock [ - "iterates the elements of the receiver starting first by first added elements." - - elements do: aBlock + "Iterate over elements in FIFO order." + | current | + current := head. + [ current notNil ] whileTrue: [ + aBlock value: current value. + current := current next. + ] ] { #category : #testing } CTQueue >> includes: anElement [ - - ^ elements includes: anElement + "Check if anElement exists in the queue." + | current | + current := head. + [ current notNil ] whileTrue: [ + current value = anElement ifTrue: [ ^ true ]. + current := current next. + ]. + ^ false ] { #category : #initialization } CTQueue >> initialize [ + "Initialize an empty queue." super initialize. - elements := OrderedCollection new. + head := nil. + tail := nil. + size := 0. ] { #category : #testing } CTQueue >> isEmpty [ - - ^ elements isEmpty + "Return true if the queue is empty." + ^ head isNil ] -{ #category : #removing } -CTQueue >> remove [ - "Return the older element of the receiver.." - - ^ elements ifEmpty: [ nil ] ifNotEmpty: [ elements removeFirst ]. +{ #category : #accessing } +CTQueue >> peek [ + "Return the front element without removing it, or nil if empty." + ^ head ifNil: [ nil ] ifNotNil: [ head value ] ] { #category : #removing } -CTQueue >> removeIfNone: aBlock [ - "Return the older element of the receiver.." - elements ifEmpty: [ ^ aBlock value ]. - ^ elements removeFirst +CTQueue >> poll [ + "Return and remove the front element, or nil if empty." + ^ self remove ] { #category : #removing } -CTQueue >> poll [ - "Returns and removes the front element, or nil if empty." - ^ elements ifEmpty: [ nil ] ifNotEmpty: [ elements removeFirst ]. +CTQueue >> remove [ + "Return and remove the oldest element, or nil if empty." + | value | + head ifNil: [ ^ nil ]. + value := head value. + head := head next. + head ifNil: [ tail := nil ]. + size := size - 1. + ^ value ] -{ #category : #accessing } -CTQueue >> peek [ - ^ elements ifEmpty: [ nil ] ifNotEmpty: [ elements first ]. +{ #category : #removing } +CTQueue >> removeIfNone: aBlock [ + "Return and remove the oldest element, or evaluate aBlock if empty." + head ifNil: [ ^ aBlock value ]. + ^ self remove ] { #category : #accessing } CTQueue >> size [ - ^ elements size + "Return the number of elements in the queue." + ^ size ] \ No newline at end of file diff --git a/src/Containers-Queue/CTQueueNode.class.st b/src/Containers-Queue/CTQueueNode.class.st new file mode 100644 index 0000000..430c3b2 --- /dev/null +++ b/src/Containers-Queue/CTQueueNode.class.st @@ -0,0 +1,29 @@ +Class { + #name : #CTQueueNode, + #superclass : #Object, + #instVars : [ + 'value', + 'next' + ], + #category : #'Containers-Queue' +} + +{ #category : #accessing } +CTQueueNode >> next [ + ^ next +] + +{ #category : #accessing } +CTQueueNode >> next: aNode [ + next := aNode +] + +{ #category : #accessing } +CTQueueNode >> value [ + ^ value +] + +{ #category : #accessing } +CTQueueNode >> value: anObject [ + value := anObject +] \ No newline at end of file