From 74b0534bb1971066c0bbb8dd8a9afcfc9d4905e3 Mon Sep 17 00:00:00 2001 From: mgstabrani Date: Thu, 10 Sep 2026 21:50:19 +0700 Subject: [PATCH 1/2] data_structure: add priority queue implementation --- data_structures/priority_queue.jule | 94 ++++++++++++++++++++++++ data_structures/priority_queue_test.jule | 75 +++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 data_structures/priority_queue.jule create mode 100644 data_structures/priority_queue_test.jule diff --git a/data_structures/priority_queue.jule b/data_structures/priority_queue.jule new file mode 100644 index 0000000..7a39ddb --- /dev/null +++ b/data_structures/priority_queue.jule @@ -0,0 +1,94 @@ +// Implementation of a generic priority queue backed by a binary min-heap. +// Reference: https://en.wikipedia.org/wiki/Priority_queue + +// PriorityQueue is a generic priority queue. Items are ordered by their +// natural ordering, and the smallest item is always at the front of the +// queue (min-heap). +struct PriorityQueue[T: ordered] { + items: []T +} + +impl PriorityQueue { + // Push adds an item to the queue. + fn Push(mut *self, mut item: T) { + self.items = append(self.items, item) + self.siftUp(len(self.items) - 1) + } + + // Pop removes and returns the smallest item in the queue. + // The second return value reports whether an item was removed; + // it is false when the queue is empty. + fn Pop(mut *self): (item: T, ok: bool) { + if len(self.items) == 0 { + return + } + item = self.items[0] + ok = true + + last := len(self.items) - 1 + self.items[0] = self.items[last] + self.items = self.items[:last] + if len(self.items) > 0 { + self.siftDown(0) + } + return + } + + // Peek returns the smallest item in the queue without removing it. + // The second return value reports whether the queue has an item; + // it is false when the queue is empty. + fn Peek(*self): (item: T, ok: bool) { + if len(self.items) == 0 { + return + } + item = self.items[0] + ok = true + return + } + + // Len returns the number of items in the queue. + fn Len(*self): int { + return len(self.items) + } + + // Empty reports whether the queue has no items. + fn Empty(*self): bool { + return len(self.items) == 0 + } + + // siftUp restores the heap property by moving the item at index i + // up towards the root while it is smaller than its parent. + fn siftUp(mut *self, mut i: int) { + for i > 0 { + parent := (i - 1) / 2 + if self.items[i] < self.items[parent] { + self.items[i], self.items[parent] = self.items[parent], self.items[i] + i = parent + } else { + break + } + } + } + + // siftDown restores the heap property by moving the item at index i + // down towards the leaves while it is bigger than one of its children. + fn siftDown(mut *self, mut i: int) { + n := len(self.items) + for { + left := 2*i + 1 + right := 2*i + 2 + mut smallest := i + if left < n && self.items[left] < self.items[smallest] { + smallest = left + } + if right < n && self.items[right] < self.items[smallest] { + smallest = right + } + if smallest == i { + break + } + self.items[i], self.items[smallest] = self.items[smallest], self.items[i] + i = smallest + } + } +} diff --git a/data_structures/priority_queue_test.jule b/data_structures/priority_queue_test.jule new file mode 100644 index 0000000..6904f83 --- /dev/null +++ b/data_structures/priority_queue_test.jule @@ -0,0 +1,75 @@ +#build test + +use "std/slices" +use "std/testing" + +#test +fn testPriorityQueuePushPop(t: &testing::T) { + mut pq := PriorityQueue[int]{} + t.Assert(pq.Empty(), "new priority queue should be empty") + t.Assert(pq.Len() == 0, "new priority queue should have length 0") + + pq.Push(5) + pq.Push(1) + pq.Push(3) + pq.Push(2) + pq.Push(4) + t.Assert(!pq.Empty(), "priority queue with items should not be empty") + t.Assert(pq.Len() == 5, "priority queue should have length 5") + + mut got := make([]int, 0, 5) + for !pq.Empty() { + v, ok := pq.Pop() + t.Assert(ok, "pop should succeed while queue is not empty") + got = append(got, v) + } + t.Assert(slices::Equal(got, [1, 2, 3, 4, 5]), "pop should always return the smallest remaining item") + t.Assert(pq.Empty(), "priority queue should be empty after popping all items") +} + +#test +fn testPriorityQueuePopEmpty(t: &testing::T) { + mut pq := PriorityQueue[int]{} + _, ok := pq.Pop() + t.Assert(!ok, "pop on empty priority queue should report ok == false") +} + +#test +fn testPriorityQueuePeek(t: &testing::T) { + mut pq := PriorityQueue[int]{} + _, ok := pq.Peek() + t.Assert(!ok, "peek on empty priority queue should report ok == false") + + pq.Push(10) + pq.Push(3) + pq.Push(7) + v, ok2 := pq.Peek() + t.Assert(ok2 && v == 3, "peek should return the smallest item (3)") + t.Assert(pq.Len() == 3, "peek should not remove the item") +} + +#test +fn testPriorityQueueDuplicates(t: &testing::T) { + mut pq := PriorityQueue[int]{} + pq.Push(2) + pq.Push(2) + pq.Push(1) + pq.Push(1) + + mut got := make([]int, 0, 4) + for !pq.Empty() { + v, _ := pq.Pop() + got = append(got, v) + } + t.Assert(slices::Equal(got, [1, 1, 2, 2]), "duplicate items should still pop in sorted order") +} + +#test +fn testPriorityQueueGenericString(t: &testing::T) { + mut pq := PriorityQueue[string]{} + pq.Push("banana") + pq.Push("apple") + pq.Push("cherry") + v, ok := pq.Pop() + t.Assert(ok && v == "apple", "generic string priority queue should pop \"apple\" first") +} From 9cadd1e6f12bd6df0258c980b3811ef09286ce31 Mon Sep 17 00:00:00 2001 From: "Mgs. Tabrani" Date: Sat, 12 Sep 2026 22:44:08 +0700 Subject: [PATCH 2/2] fix: mutability to the Peek function Co-authored-by: Mertcan D. <54983926+mertcandav@users.noreply.github.com> --- data_structures/priority_queue.jule | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/priority_queue.jule b/data_structures/priority_queue.jule index 7a39ddb..031e51e 100644 --- a/data_structures/priority_queue.jule +++ b/data_structures/priority_queue.jule @@ -37,7 +37,7 @@ impl PriorityQueue { // Peek returns the smallest item in the queue without removing it. // The second return value reports whether the queue has an item; // it is false when the queue is empty. - fn Peek(*self): (item: T, ok: bool) { + fn Peek(mut *self): (item: T, ok: bool) { if len(self.items) == 0 { return }