authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-20 07:11:57+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-20 07:11:57+01:00
log6a9510c0eb85434f75730f512f4d9e4bed5cfcb3
tree2cf5214166726ecabf1218a67d58a2b2f5ca1ef5
parent14d06330e0b85dbf9dbd2023d58d2af75a150bcc
parent5140455264347f8bf869edae71faa8848e92dabd

Merge pull request 'Make `std.PriorityDequeue` an unmanaged container' (#31273) from saurabh/zig:unmanaged-priority-dequeue into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31273

1 files changed, 383 insertions(+), 324 deletions(-)

lib/std/priority_dequeue.zig+383-324
...@@ -21,39 +21,44 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar...@@ -21,39 +21,44 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar
2121
22 items: []T,22 items: []T,
23 len: usize,23 len: usize,
24 allocator: Allocator,
25 context: Context,24 context: Context,
2625
27 /// Initialize and return a new priority dequeue.26 /// A priority dequeue containing no elements.
28 pub fn init(allocator: Allocator, context: Context) Self {27 pub const empty: Self = .{
28 .items = &.{},
29 .len = 0,
30 .context = undefined,
31 };
32
33 /// Initialize and return a new priority dequeue with context.
34 pub fn initContext(context: Context) Self {
29 return Self{35 return Self{
30 .items = &[_]T{},36 .items = &.{},
31 .len = 0,37 .len = 0,
32 .allocator = allocator,
33 .context = context,38 .context = context,
34 };39 };
35 }40 }
3641
37 /// Free memory used by the dequeue.42 /// Free memory used by the dequeue.
38 pub fn deinit(self: Self) void {43 pub fn deinit(self: Self, allocator: Allocator) void {
39 self.allocator.free(self.items);44 allocator.free(self.items);
40 }45 }
4146
42 /// Insert a new element, maintaining priority.47 /// Insert a new element, maintaining priority.
43 pub fn add(self: *Self, elem: T) !void {48 pub fn push(self: *Self, allocator: Allocator, elem: T) !void {
44 try self.ensureUnusedCapacity(1);49 try self.ensureUnusedCapacity(allocator, 1);
45 addUnchecked(self, elem);50 pushUnchecked(self, elem);
46 }51 }
4752
48 /// Add each element in `items` to the dequeue.53 /// Add each element in `items` to the dequeue.
49 pub fn addSlice(self: *Self, items: []const T) !void {54 pub fn pushSlice(self: *Self, allocator: Allocator, items: []const T) !void {
50 try self.ensureUnusedCapacity(items.len);55 try self.ensureUnusedCapacity(allocator, items.len);
51 for (items) |e| {56 for (items) |e| {
52 self.addUnchecked(e);57 self.pushUnchecked(e);
53 }58 }
54 }59 }
5560
56 fn addUnchecked(self: *Self, elem: T) void {61 fn pushUnchecked(self: *Self, elem: T) void {
57 self.items[self.len] = elem;62 self.items[self.len] = elem;
5863
59 if (self.len > 0) {64 if (self.len > 0) {
...@@ -151,34 +156,20 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar...@@ -151,34 +156,20 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar
151 return self.bestItemAtIndices(1, 2, .gt).index;156 return self.bestItemAtIndices(1, 2, .gt).index;
152 }157 }
153158
154 /// Pop the smallest element from the dequeue. Returns159 /// Remove and return the smallest element from the dequeue, or `null` if empty
155 /// `null` if empty.160 pub fn popMin(self: *Self) ?T {
156 pub fn removeMinOrNull(self: *Self) ?T {161 return if (self.len > 0) self.popIndex(0) else null;
157 return if (self.len > 0) self.removeMin() else null;
158 }
159
160 /// Remove and return the smallest element from the
161 /// dequeue.
162 pub fn removeMin(self: *Self) T {
163 return self.removeIndex(0);
164 }
165
166 /// Pop the largest element from the dequeue. Returns
167 /// `null` if empty.
168 pub fn removeMaxOrNull(self: *Self) ?T {
169 return if (self.len > 0) self.removeMax() else null;
170 }162 }
171163
172 /// Remove and return the largest element from the164 /// Remove and return the largest element from the dequeue, or `null` if empty
173 /// dequeue.165 pub fn popMax(self: *Self) ?T {
174 pub fn removeMax(self: *Self) T {166 return if (self.len > 0) self.popIndex(self.maxIndex().?) else null;
175 return self.removeIndex(self.maxIndex().?);
176 }167 }
177168
178 /// Remove and return element at index. Indices are in the169 /// Remove and return element at index. Indices are in the
179 /// same order as iterator, which is not necessarily priority170 /// same order as iterator, which is not necessarily priority
180 /// order.171 /// order.
181 pub fn removeIndex(self: *Self, index: usize) T {172 pub fn popIndex(self: *Self, index: usize) T {
182 assert(self.len > index);173 assert(self.len > index);
183 const item = self.items[index];174 const item = self.items[index];
184 const last = self.items[self.len - 1];175 const last = self.items[self.len - 1];
...@@ -333,14 +324,12 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar...@@ -333,14 +324,12 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar
333 return self.items.len;324 return self.items.len;
334 }325 }
335326
336 /// Dequeue takes ownership of the passed in slice. The slice must have been327 /// Dequeue takes ownership of the passed in slice. The slice must be de-initialize
337 /// allocated with `allocator`.328 /// with `deinit`.
338 /// De-initialize with `deinit`.329 pub fn fromOwnedSlice(items: []T, context: Context) Self {
339 pub fn fromOwnedSlice(allocator: Allocator, items: []T, context: Context) Self {
340 var queue = Self{330 var queue = Self{
341 .items = items,331 .items = items,
342 .len = items.len,332 .len = items.len,
343 .allocator = allocator,
344 .context = context,333 .context = context,
345 };334 };
346335
...@@ -356,29 +345,29 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar...@@ -356,29 +345,29 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar
356 }345 }
357346
358 /// Ensure that the dequeue can fit at least `new_capacity` items.347 /// Ensure that the dequeue can fit at least `new_capacity` items.
359 pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) !void {348 pub fn ensureTotalCapacity(self: *Self, allocator: Allocator, new_capacity: usize) !void {
360 var better_capacity = self.capacity();349 var better_capacity = self.capacity();
361 if (better_capacity >= new_capacity) return;350 if (better_capacity >= new_capacity) return;
362 while (true) {351 while (true) {
363 better_capacity += better_capacity / 2 + 8;352 better_capacity += better_capacity / 2 + 8;
364 if (better_capacity >= new_capacity) break;353 if (better_capacity >= new_capacity) break;
365 }354 }
366 self.items = try self.allocator.realloc(self.items, better_capacity);355 self.items = try allocator.realloc(self.items, better_capacity);
367 }356 }
368357
369 /// Ensure that the dequeue can fit at least `additional_count` **more** items.358 /// Ensure that the dequeue can fit at least `additional_count` **more** items.
370 pub fn ensureUnusedCapacity(self: *Self, additional_count: usize) !void {359 pub fn ensureUnusedCapacity(self: *Self, allocator: Allocator, additional_count: usize) !void {
371 return self.ensureTotalCapacity(self.len + additional_count);360 return self.ensureTotalCapacity(allocator, self.len + additional_count);
372 }361 }
373362
374 /// Reduce allocated capacity to `new_len`.363 /// Reduce allocated capacity to `new_len`.
375 pub fn shrinkAndFree(self: *Self, new_len: usize) void {364 pub fn shrinkAndFree(self: *Self, allocator: Allocator, new_len: usize) void {
376 assert(new_len <= self.items.len);365 assert(new_len <= self.items.len);
377366
378 // Cannot shrink to smaller than the current queue size without invalidating the heap property367 // Cannot shrink to smaller than the current queue size without invalidating the heap property
379 assert(new_len >= self.len);368 assert(new_len >= self.len);
380369
381 self.items = self.allocator.realloc(self.items[0..], new_len) catch |e| switch (e) {370 self.items = allocator.realloc(self.items[0..], new_len) catch |e| switch (e) {
382 error.OutOfMemory => { // no problem, capacity is still correct then.371 error.OutOfMemory => { // no problem, capacity is still correct then.
383 self.items.len = new_len;372 self.items.len = new_len;
384 return;373 return;
...@@ -395,8 +384,8 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar...@@ -395,8 +384,8 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar
395 }384 }
396 return error.ElementNotFound;385 return error.ElementNotFound;
397 };386 };
398 _ = self.removeIndex(old_index);387 _ = self.popIndex(old_index);
399 self.addUnchecked(new_elem);388 self.pushUnchecked(new_elem);
400 }389 }
401390
402 pub const Iterator = struct {391 pub const Iterator = struct {
...@@ -460,33 +449,40 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar...@@ -460,33 +449,40 @@ pub fn PriorityDequeue(comptime T: type, comptime Context: type, comptime compar
460 };449 };
461}450}
462451
452/// If a min heap is constructed from slice `{5, 8, 2, 9, 7, 1, 4, 4}` using this
453/// method, then the elements will be in order: {1, 2, 4, 4, 5, 7, 8, 9}
463fn lessThanComparison(context: void, a: u32, b: u32) Order {454fn lessThanComparison(context: void, a: u32, b: u32) Order {
464 _ = context;455 _ = context;
465 return std.math.order(a, b);456 return std.math.order(a, b);
466}457}
467458
468const PDQ = PriorityDequeue(u32, void, lessThanComparison);459/// Elements with lower priority will be removed first
469460const MinHeap = PriorityDequeue(u32, void, lessThanComparison);
470test "add and remove min" {461
471 var queue = PDQ.init(testing.allocator, {});462test "push and pop min in min heap" {
472 defer queue.deinit();463 const gpa = std.testing.allocator;
473464
474 try queue.add(54);465 var queue: MinHeap = .empty;
475 try queue.add(12);466 defer queue.deinit(gpa);
476 try queue.add(7);467
477 try queue.add(23);468 try queue.push(gpa, 54);
478 try queue.add(25);469 try queue.push(gpa, 12);
479 try queue.add(13);470 try queue.push(gpa, 7);
480471 try queue.push(gpa, 23);
481 try expectEqual(@as(u32, 7), queue.removeMin());472 try queue.push(gpa, 25);
482 try expectEqual(@as(u32, 12), queue.removeMin());473 try queue.push(gpa, 13);
483 try expectEqual(@as(u32, 13), queue.removeMin());474
484 try expectEqual(@as(u32, 23), queue.removeMin());475 try expectEqual(@as(u32, 7), queue.popMin());
485 try expectEqual(@as(u32, 25), queue.removeMin());476 try expectEqual(@as(u32, 12), queue.popMin());
486 try expectEqual(@as(u32, 54), queue.removeMin());477 try expectEqual(@as(u32, 13), queue.popMin());
478 try expectEqual(@as(u32, 23), queue.popMin());
479 try expectEqual(@as(u32, 25), queue.popMin());
480 try expectEqual(@as(u32, 54), queue.popMin());
487}481}
488482
489test "add and remove min structs" {483test "push and pop min structs" {
484 const gpa = std.testing.allocator;
485
490 const S = struct {486 const S = struct {
491 size: u32,487 size: u32,
492 };488 };
...@@ -495,310 +491,360 @@ test "add and remove min structs" {...@@ -495,310 +491,360 @@ test "add and remove min structs" {
495 _ = context;491 _ = context;
496 return std.math.order(a.size, b.size);492 return std.math.order(a.size, b.size);
497 }493 }
498 }.order).init(testing.allocator, {});494 }.order).initContext({});
499 defer queue.deinit();495 defer queue.deinit(gpa);
500496
501 try queue.add(.{ .size = 54 });497 try queue.push(gpa, .{ .size = 54 });
502 try queue.add(.{ .size = 12 });498 try queue.push(gpa, .{ .size = 12 });
503 try queue.add(.{ .size = 7 });499 try queue.push(gpa, .{ .size = 7 });
504 try queue.add(.{ .size = 23 });500 try queue.push(gpa, .{ .size = 23 });
505 try queue.add(.{ .size = 25 });501 try queue.push(gpa, .{ .size = 25 });
506 try queue.add(.{ .size = 13 });502 try queue.push(gpa, .{ .size = 13 });
507503
508 try expectEqual(@as(u32, 7), queue.removeMin().size);504 try expectEqual(@as(u32, 7), queue.popMin().?.size);
509 try expectEqual(@as(u32, 12), queue.removeMin().size);505 try expectEqual(@as(u32, 12), queue.popMin().?.size);
510 try expectEqual(@as(u32, 13), queue.removeMin().size);506 try expectEqual(@as(u32, 13), queue.popMin().?.size);
511 try expectEqual(@as(u32, 23), queue.removeMin().size);507 try expectEqual(@as(u32, 23), queue.popMin().?.size);
512 try expectEqual(@as(u32, 25), queue.removeMin().size);508 try expectEqual(@as(u32, 25), queue.popMin().?.size);
513 try expectEqual(@as(u32, 54), queue.removeMin().size);509 try expectEqual(@as(u32, 54), queue.popMin().?.size);
514}510}
515511
516test "add and remove max" {512test "push and pop max in min heap" {
517 var queue = PDQ.init(testing.allocator, {});513 const gpa = std.testing.allocator;
518 defer queue.deinit();514
519515 var queue: MinHeap = .empty;
520 try queue.add(54);516 defer queue.deinit(gpa);
521 try queue.add(12);517
522 try queue.add(7);518 try queue.push(gpa, 54);
523 try queue.add(23);519 try queue.push(gpa, 12);
524 try queue.add(25);520 try queue.push(gpa, 7);
525 try queue.add(13);521 try queue.push(gpa, 23);
526522 try queue.push(gpa, 25);
527 try expectEqual(@as(u32, 54), queue.removeMax());523 try queue.push(gpa, 13);
528 try expectEqual(@as(u32, 25), queue.removeMax());524
529 try expectEqual(@as(u32, 23), queue.removeMax());525 try expectEqual(@as(u32, 54), queue.popMax());
530 try expectEqual(@as(u32, 13), queue.removeMax());526 try expectEqual(@as(u32, 25), queue.popMax());
531 try expectEqual(@as(u32, 12), queue.removeMax());527 try expectEqual(@as(u32, 23), queue.popMax());
532 try expectEqual(@as(u32, 7), queue.removeMax());528 try expectEqual(@as(u32, 13), queue.popMax());
529 try expectEqual(@as(u32, 12), queue.popMax());
530 try expectEqual(@as(u32, 7), queue.popMax());
533}531}
534532
535test "add and remove same min" {533test "push and pop same min in min heap" {
536 var queue = PDQ.init(testing.allocator, {});534 const gpa = std.testing.allocator;
537 defer queue.deinit();535
538536 var queue: MinHeap = .empty;
539 try queue.add(1);537 defer queue.deinit(gpa);
540 try queue.add(1);538
541 try queue.add(2);539 try queue.push(gpa, 1);
542 try queue.add(2);540 try queue.push(gpa, 1);
543 try queue.add(1);541 try queue.push(gpa, 2);
544 try queue.add(1);542 try queue.push(gpa, 2);
545543 try queue.push(gpa, 1);
546 try expectEqual(@as(u32, 1), queue.removeMin());544 try queue.push(gpa, 1);
547 try expectEqual(@as(u32, 1), queue.removeMin());545
548 try expectEqual(@as(u32, 1), queue.removeMin());546 try expectEqual(@as(u32, 1), queue.popMin());
549 try expectEqual(@as(u32, 1), queue.removeMin());547 try expectEqual(@as(u32, 1), queue.popMin());
550 try expectEqual(@as(u32, 2), queue.removeMin());548 try expectEqual(@as(u32, 1), queue.popMin());
551 try expectEqual(@as(u32, 2), queue.removeMin());549 try expectEqual(@as(u32, 1), queue.popMin());
550 try expectEqual(@as(u32, 2), queue.popMin());
551 try expectEqual(@as(u32, 2), queue.popMin());
552}552}
553553
554test "add and remove same max" {554test "push and pop same max in min heap" {
555 var queue = PDQ.init(testing.allocator, {});555 const gpa = std.testing.allocator;
556 defer queue.deinit();556
557557 var queue: MinHeap = .empty;
558 try queue.add(1);558 defer queue.deinit(gpa);
559 try queue.add(1);559
560 try queue.add(2);560 try queue.push(gpa, 1);
561 try queue.add(2);561 try queue.push(gpa, 1);
562 try queue.add(1);562 try queue.push(gpa, 2);
563 try queue.add(1);563 try queue.push(gpa, 2);
564564 try queue.push(gpa, 1);
565 try expectEqual(@as(u32, 2), queue.removeMax());565 try queue.push(gpa, 1);
566 try expectEqual(@as(u32, 2), queue.removeMax());566
567 try expectEqual(@as(u32, 1), queue.removeMax());567 try expectEqual(@as(u32, 2), queue.popMax());
568 try expectEqual(@as(u32, 1), queue.removeMax());568 try expectEqual(@as(u32, 2), queue.popMax());
569 try expectEqual(@as(u32, 1), queue.removeMax());569 try expectEqual(@as(u32, 1), queue.popMax());
570 try expectEqual(@as(u32, 1), queue.removeMax());570 try expectEqual(@as(u32, 1), queue.popMax());
571 try expectEqual(@as(u32, 1), queue.popMax());
572 try expectEqual(@as(u32, 1), queue.popMax());
571}573}
572574
573test "removeOrNull empty" {575test "pop empty in min heap" {
574 var queue = PDQ.init(testing.allocator, {});576 const gpa = std.testing.allocator;
575 defer queue.deinit();577
578 var queue: MinHeap = .empty;
579 defer queue.deinit(gpa);
576580
577 try expect(queue.removeMinOrNull() == null);581 try expect(queue.popMin() == null);
578 try expect(queue.removeMaxOrNull() == null);582 try expect(queue.popMax() == null);
579}583}
580584
581test "edge case 3 elements" {585test "edge case 3 elements popMin in min heap" {
582 var queue = PDQ.init(testing.allocator, {});586 const gpa = std.testing.allocator;
583 defer queue.deinit();587
588 var queue: MinHeap = .empty;
589 defer queue.deinit(gpa);
584590
585 try queue.add(9);591 try queue.push(gpa, 9);
586 try queue.add(3);592 try queue.push(gpa, 3);
587 try queue.add(2);593 try queue.push(gpa, 2);
588594
589 try expectEqual(@as(u32, 2), queue.removeMin());595 try expectEqual(@as(u32, 2), queue.popMin());
590 try expectEqual(@as(u32, 3), queue.removeMin());596 try expectEqual(@as(u32, 3), queue.popMin());
591 try expectEqual(@as(u32, 9), queue.removeMin());597 try expectEqual(@as(u32, 9), queue.popMin());
592}598}
593599
594test "edge case 3 elements max" {600test "edge case 3 elements popmax in min heap" {
595 var queue = PDQ.init(testing.allocator, {});601 const gpa = std.testing.allocator;
596 defer queue.deinit();
597602
598 try queue.add(9);603 var queue: MinHeap = .empty;
599 try queue.add(3);604 defer queue.deinit(gpa);
600 try queue.add(2);
601605
602 try expectEqual(@as(u32, 9), queue.removeMax());606 try queue.push(gpa, 9);
603 try expectEqual(@as(u32, 3), queue.removeMax());607 try queue.push(gpa, 3);
604 try expectEqual(@as(u32, 2), queue.removeMax());608 try queue.push(gpa, 2);
609
610 try expectEqual(@as(u32, 9), queue.popMax());
611 try expectEqual(@as(u32, 3), queue.popMax());
612 try expectEqual(@as(u32, 2), queue.popMax());
605}613}
606614
607test "peekMin" {615test "peekMin in min heap" {
608 var queue = PDQ.init(testing.allocator, {});616 const gpa = std.testing.allocator;
609 defer queue.deinit();617
618 var queue: MinHeap = .empty;
619 defer queue.deinit(gpa);
610620
611 try expect(queue.peekMin() == null);621 try expect(queue.peekMin() == null);
612622
613 try queue.add(9);623 try queue.push(gpa, 9);
614 try queue.add(3);624 try queue.push(gpa, 3);
615 try queue.add(2);625 try queue.push(gpa, 2);
616626
617 try expect(queue.peekMin().? == 2);627 try expect(queue.peekMin().? == 2);
618 try expect(queue.peekMin().? == 2);628 try expect(queue.peekMin().? == 2);
619}629}
620630
621test "peekMax" {631test "peekMax in min heap" {
622 var queue = PDQ.init(testing.allocator, {});632 const gpa = std.testing.allocator;
623 defer queue.deinit();633
634 var queue: MinHeap = .empty;
635 defer queue.deinit(gpa);
624636
625 try expect(queue.peekMin() == null);637 try expect(queue.peekMin() == null);
626638
627 try queue.add(9);639 try queue.push(gpa, 9);
628 try queue.add(3);640 try queue.push(gpa, 3);
629 try queue.add(2);641 try queue.push(gpa, 2);
630642
631 try expect(queue.peekMax().? == 9);643 try expect(queue.peekMax().? == 9);
632 try expect(queue.peekMax().? == 9);644 try expect(queue.peekMax().? == 9);
633}645}
634646
635test "sift up with odd indices, removeMin" {647test "sift up with odd indices and popMin in min heap" {
636 var queue = PDQ.init(testing.allocator, {});648 const gpa = std.testing.allocator;
637 defer queue.deinit();649
650 var queue: MinHeap = .empty;
651 defer queue.deinit(gpa);
652
638 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };653 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
639 for (items) |e| {654 for (items) |e| {
640 try queue.add(e);655 try queue.push(gpa, e);
641 }656 }
642657
643 const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 };658 const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 };
644 for (sorted_items) |e| {659 for (sorted_items) |e| {
645 try expectEqual(e, queue.removeMin());660 try expectEqual(e, queue.popMin());
646 }661 }
647}662}
648663
649test "sift up with odd indices, removeMax" {664test "sift up with odd indices and popMax in min heap" {
650 var queue = PDQ.init(testing.allocator, {});665 const gpa = std.testing.allocator;
651 defer queue.deinit();666
667 var queue: MinHeap = .empty;
668 defer queue.deinit(gpa);
669
652 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };670 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
653 for (items) |e| {671 for (items) |e| {
654 try queue.add(e);672 try queue.push(gpa, e);
655 }673 }
656674
657 const sorted_items = [_]u32{ 25, 24, 24, 22, 21, 16, 15, 15, 14, 13, 12, 11, 7, 7, 6, 5, 2, 1 };675 const sorted_items = [_]u32{ 25, 24, 24, 22, 21, 16, 15, 15, 14, 13, 12, 11, 7, 7, 6, 5, 2, 1 };
658 for (sorted_items) |e| {676 for (sorted_items) |e| {
659 try expectEqual(e, queue.removeMax());677 try expectEqual(e, queue.popMax());
660 }678 }
661}679}
662680
663test "addSlice min" {681test "pushSlice in min heap and popMin" {
664 var queue = PDQ.init(testing.allocator, {});682 const gpa = std.testing.allocator;
665 defer queue.deinit();683
684 var queue: MinHeap = .empty;
685 defer queue.deinit(gpa);
686
666 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };687 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
667 try queue.addSlice(items[0..]);688 try queue.pushSlice(gpa, items[0..]);
668689
669 const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 };690 const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 };
670 for (sorted_items) |e| {691 for (sorted_items) |e| {
671 try expectEqual(e, queue.removeMin());692 try expectEqual(e, queue.popMin());
672 }693 }
673}694}
674695
675test "addSlice max" {696test "pushSlice in min heap and popMax" {
676 var queue = PDQ.init(testing.allocator, {});697 const gpa = std.testing.allocator;
677 defer queue.deinit();698
699 var queue: MinHeap = .empty;
700 defer queue.deinit(gpa);
701
678 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };702 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
679 try queue.addSlice(items[0..]);703 try queue.pushSlice(gpa, items[0..]);
680704
681 const sorted_items = [_]u32{ 25, 24, 24, 22, 21, 16, 15, 15, 14, 13, 12, 11, 7, 7, 6, 5, 2, 1 };705 const sorted_items = [_]u32{ 25, 24, 24, 22, 21, 16, 15, 15, 14, 13, 12, 11, 7, 7, 6, 5, 2, 1 };
682 for (sorted_items) |e| {706 for (sorted_items) |e| {
683 try expectEqual(e, queue.removeMax());707 try expectEqual(e, queue.popMax());
684 }708 }
685}709}
686710
687test "fromOwnedSlice trivial case 0" {711test "fromOwnedSlice trivial case 0 min heap" {
712 const gpa = std.testing.allocator;
713
688 const items = [0]u32{};714 const items = [0]u32{};
689 const queue_items = try testing.allocator.dupe(u32, &items);715 const queue_items = try gpa.dupe(u32, &items);
690 var queue = PDQ.fromOwnedSlice(testing.allocator, queue_items[0..], {});716
691 defer queue.deinit();717 var queue: MinHeap = .fromOwnedSlice(queue_items[0..], {});
718 defer queue.deinit(gpa);
719
692 try expectEqual(@as(usize, 0), queue.len);720 try expectEqual(@as(usize, 0), queue.len);
693 try expect(queue.removeMinOrNull() == null);721 try expect(queue.popMin() == null);
694}722}
695723
696test "fromOwnedSlice trivial case 1" {724test "fromOwnedSlice trivial case 1 min heap" {
725 const gpa = std.testing.allocator;
726
697 const items = [1]u32{1};727 const items = [1]u32{1};
698 const queue_items = try testing.allocator.dupe(u32, &items);728 const queue_items = try gpa.dupe(u32, &items);
699 var queue = PDQ.fromOwnedSlice(testing.allocator, queue_items[0..], {});729
700 defer queue.deinit();730 var queue: MinHeap = .fromOwnedSlice(queue_items[0..], {});
731 defer queue.deinit(gpa);
701732
702 try expectEqual(@as(usize, 1), queue.len);733 try expectEqual(@as(usize, 1), queue.len);
703 try expectEqual(items[0], queue.removeMin());734 try expectEqual(items[0], queue.popMin());
704 try expect(queue.removeMinOrNull() == null);735 try expect(queue.popMin() == null);
705}736}
706737
707test "fromOwnedSlice" {738test "fromOwnedSlice min heap" {
739 const gpa = std.testing.allocator;
740
708 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };741 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
709 const queue_items = try testing.allocator.dupe(u32, items[0..]);742 const queue_items = try gpa.dupe(u32, items[0..]);
710 var queue = PDQ.fromOwnedSlice(testing.allocator, queue_items[0..], {});743
711 defer queue.deinit();744 var queue: MinHeap = .fromOwnedSlice(queue_items[0..], {});
745 defer queue.deinit(gpa);
712746
713 const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 };747 const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 };
714 for (sorted_items) |e| {748 for (sorted_items) |e| {
715 try expectEqual(e, queue.removeMin());749 try expectEqual(e, queue.popMin());
716 }750 }
717}751}
718752
719test "update min queue" {753test "update and popMin in min heap" {
720 var queue = PDQ.init(testing.allocator, {});754 const gpa = std.testing.allocator;
721 defer queue.deinit();
722755
723 try queue.add(55);756 var queue: MinHeap = .empty;
724 try queue.add(44);757 defer queue.deinit(gpa);
725 try queue.add(11);758
759 try queue.push(gpa, 55);
760 try queue.push(gpa, 44);
761 try queue.push(gpa, 11);
726 try queue.update(55, 5);762 try queue.update(55, 5);
727 try queue.update(44, 4);763 try queue.update(44, 4);
728 try queue.update(11, 1);764 try queue.update(11, 1);
729 try expectEqual(@as(u32, 1), queue.removeMin());765 try expectEqual(@as(u32, 1), queue.popMin());
730 try expectEqual(@as(u32, 4), queue.removeMin());766 try expectEqual(@as(u32, 4), queue.popMin());
731 try expectEqual(@as(u32, 5), queue.removeMin());767 try expectEqual(@as(u32, 5), queue.popMin());
732}768}
733769
734test "update same min queue" {770test "update same element and popMin in min heap" {
735 var queue = PDQ.init(testing.allocator, {});771 const gpa = std.testing.allocator;
736 defer queue.deinit();772
773 var queue: MinHeap = .empty;
774 defer queue.deinit(gpa);
737775
738 try queue.add(1);776 try queue.push(gpa, 1);
739 try queue.add(1);777 try queue.push(gpa, 1);
740 try queue.add(2);778 try queue.push(gpa, 2);
741 try queue.add(2);779 try queue.push(gpa, 2);
742 try queue.update(1, 5);780 try queue.update(1, 5);
743 try queue.update(2, 4);781 try queue.update(2, 4);
744 try expectEqual(@as(u32, 1), queue.removeMin());782 try expectEqual(@as(u32, 1), queue.popMin());
745 try expectEqual(@as(u32, 2), queue.removeMin());783 try expectEqual(@as(u32, 2), queue.popMin());
746 try expectEqual(@as(u32, 4), queue.removeMin());784 try expectEqual(@as(u32, 4), queue.popMin());
747 try expectEqual(@as(u32, 5), queue.removeMin());785 try expectEqual(@as(u32, 5), queue.popMin());
748}786}
749787
750test "update max queue" {788test "update and popMax in min heap" {
751 var queue = PDQ.init(testing.allocator, {});789 const gpa = std.testing.allocator;
752 defer queue.deinit();790
791 var queue: MinHeap = .empty;
792 defer queue.deinit(gpa);
753793
754 try queue.add(55);794 try queue.push(gpa, 55);
755 try queue.add(44);795 try queue.push(gpa, 44);
756 try queue.add(11);796 try queue.push(gpa, 11);
757 try queue.update(55, 5);797 try queue.update(55, 5);
758 try queue.update(44, 1);798 try queue.update(44, 1);
759 try queue.update(11, 4);799 try queue.update(11, 4);
760800
761 try expectEqual(@as(u32, 5), queue.removeMax());801 try expectEqual(@as(u32, 5), queue.popMax());
762 try expectEqual(@as(u32, 4), queue.removeMax());802 try expectEqual(@as(u32, 4), queue.popMax());
763 try expectEqual(@as(u32, 1), queue.removeMax());803 try expectEqual(@as(u32, 1), queue.popMax());
764}804}
765805
766test "update same max queue" {806test "update same element and popMax in min heap" {
767 var queue = PDQ.init(testing.allocator, {});807 const gpa = std.testing.allocator;
768 defer queue.deinit();
769808
770 try queue.add(1);809 var queue: MinHeap = .empty;
771 try queue.add(1);810 defer queue.deinit(gpa);
772 try queue.add(2);811
773 try queue.add(2);812 try queue.push(gpa, 1);
813 try queue.push(gpa, 1);
814 try queue.push(gpa, 2);
815 try queue.push(gpa, 2);
774 try queue.update(1, 5);816 try queue.update(1, 5);
775 try queue.update(2, 4);817 try queue.update(2, 4);
776 try expectEqual(@as(u32, 5), queue.removeMax());818 try expectEqual(@as(u32, 5), queue.popMax());
777 try expectEqual(@as(u32, 4), queue.removeMax());819 try expectEqual(@as(u32, 4), queue.popMax());
778 try expectEqual(@as(u32, 2), queue.removeMax());820 try expectEqual(@as(u32, 2), queue.popMax());
779 try expectEqual(@as(u32, 1), queue.removeMax());821 try expectEqual(@as(u32, 1), queue.popMax());
780}822}
781823
782test "update after remove" {824test "update after pop in min heap" {
783 var queue = PDQ.init(testing.allocator, {});825 const gpa = std.testing.allocator;
784 defer queue.deinit();826
827 var queue: MinHeap = .empty;
828 defer queue.deinit(gpa);
785829
786 try queue.add(1);830 try queue.push(gpa, 1);
787 try expectEqual(@as(u32, 1), queue.removeMin());831 try expectEqual(@as(u32, 1), queue.popMin());
788 try expectError(error.ElementNotFound, queue.update(1, 1));832 try expectError(error.ElementNotFound, queue.update(1, 1));
789}833}
790834
791test "iterator" {835test "min heap iterator" {
792 var queue = PDQ.init(testing.allocator, {});836 const gpa = std.testing.allocator;
837
838 var queue: MinHeap = .empty;
793 var map = std.AutoHashMap(u32, void).init(testing.allocator);839 var map = std.AutoHashMap(u32, void).init(testing.allocator);
794 defer {840 defer {
795 queue.deinit();841 queue.deinit(gpa);
796 map.deinit();842 map.deinit();
797 }843 }
798844
799 const items = [_]u32{ 54, 12, 7, 23, 25, 13 };845 const items = [_]u32{ 54, 12, 7, 23, 25, 13 };
800 for (items) |e| {846 for (items) |e| {
801 _ = try queue.add(e);847 _ = try queue.push(gpa, e);
802 _ = try map.put(e, {});848 _ = try map.put(e, {});
803 }849 }
804850
...@@ -810,13 +856,15 @@ test "iterator" {...@@ -810,13 +856,15 @@ test "iterator" {
810 try expectEqual(@as(usize, 0), map.count());856 try expectEqual(@as(usize, 0), map.count());
811}857}
812858
813test "remove at index" {859test "pop at index in min heap" {
814 var queue = PDQ.init(testing.allocator, {});860 const gpa = std.testing.allocator;
815 defer queue.deinit();
816861
817 try queue.add(3);862 var queue: MinHeap = .empty;
818 try queue.add(2);863 defer queue.deinit(gpa);
819 try queue.add(1);864
865 try queue.push(gpa, 3);
866 try queue.push(gpa, 2);
867 try queue.push(gpa, 1);
820868
821 var it = queue.iterator();869 var it = queue.iterator();
822 var elem = it.next();870 var elem = it.next();
...@@ -827,42 +875,46 @@ test "remove at index" {...@@ -827,42 +875,46 @@ test "remove at index" {
827 idx += 1;875 idx += 1;
828 } else unreachable;876 } else unreachable;
829877
830 try expectEqual(queue.removeIndex(two_idx), 2);878 try expectEqual(queue.popIndex(two_idx), 2);
831 try expectEqual(queue.removeMin(), 1);879 try expectEqual(queue.popMin(), 1);
832 try expectEqual(queue.removeMin(), 3);880 try expectEqual(queue.popMin(), 3);
833 try expectEqual(queue.removeMinOrNull(), null);881 try expectEqual(queue.popMin(), null);
834}882}
835883
836test "iterator while empty" {884test "min heap iterator while empty" {
837 var queue = PDQ.init(testing.allocator, {});885 const gpa = std.testing.allocator;
838 defer queue.deinit();886
887 var queue: MinHeap = .empty;
888 defer queue.deinit(gpa);
839889
840 var it = queue.iterator();890 var it = queue.iterator();
841891
842 try expectEqual(it.next(), null);892 try expectEqual(it.next(), null);
843}893}
844894
845test "shrinkAndFree" {895test "min heap shrinkAndFree" {
846 var queue = PDQ.init(testing.allocator, {});896 const gpa = std.testing.allocator;
847 defer queue.deinit();897
898 var queue: MinHeap = .empty;
899 defer queue.deinit(gpa);
848900
849 try queue.ensureTotalCapacity(4);901 try queue.ensureTotalCapacity(gpa, 4);
850 try expect(queue.capacity() >= 4);902 try expect(queue.capacity() >= 4);
851903
852 try queue.add(1);904 try queue.push(gpa, 1);
853 try queue.add(2);905 try queue.push(gpa, 2);
854 try queue.add(3);906 try queue.push(gpa, 3);
855 try expect(queue.capacity() >= 4);907 try expect(queue.capacity() >= 4);
856 try expectEqual(@as(usize, 3), queue.len);908 try expectEqual(@as(usize, 3), queue.len);
857909
858 queue.shrinkAndFree(3);910 queue.shrinkAndFree(gpa, 3);
859 try expectEqual(@as(usize, 3), queue.capacity());911 try expectEqual(@as(usize, 3), queue.capacity());
860 try expectEqual(@as(usize, 3), queue.len);912 try expectEqual(@as(usize, 3), queue.len);
861913
862 try expectEqual(@as(u32, 3), queue.removeMax());914 try expectEqual(@as(u32, 3), queue.popMax());
863 try expectEqual(@as(u32, 2), queue.removeMax());915 try expectEqual(@as(u32, 2), queue.popMax());
864 try expectEqual(@as(u32, 1), queue.removeMax());916 try expectEqual(@as(u32, 1), queue.popMax());
865 try expect(queue.removeMaxOrNull() == null);917 try expect(queue.popMax() == null);
866}918}
867919
868test "fuzz testing min" {920test "fuzz testing min" {
...@@ -879,14 +931,15 @@ test "fuzz testing min" {...@@ -879,14 +931,15 @@ test "fuzz testing min" {
879}931}
880932
881fn fuzzTestMin(rng: std.Random, comptime queue_size: usize) !void {933fn fuzzTestMin(rng: std.Random, comptime queue_size: usize) !void {
882 const allocator = testing.allocator;934 const gpa = std.testing.allocator;
883 const items = try generateRandomSlice(allocator, rng, queue_size);
884935
885 var queue = PDQ.fromOwnedSlice(allocator, items, {});936 const items = try generateRandomSlice(gpa, rng, queue_size);
886 defer queue.deinit();937
938 var queue: MinHeap = .fromOwnedSlice(items, {});
939 defer queue.deinit(gpa);
887940
888 var last_removed: ?u32 = null;941 var last_removed: ?u32 = null;
889 while (queue.removeMinOrNull()) |next| {942 while (queue.popMin()) |next| {
890 if (last_removed) |last| {943 if (last_removed) |last| {
891 try expect(last <= next);944 try expect(last <= next);
892 }945 }
...@@ -908,14 +961,15 @@ test "fuzz testing max" {...@@ -908,14 +961,15 @@ test "fuzz testing max" {
908}961}
909962
910fn fuzzTestMax(rng: std.Random, queue_size: usize) !void {963fn fuzzTestMax(rng: std.Random, queue_size: usize) !void {
911 const allocator = testing.allocator;964 const gpa = std.testing.allocator;
912 const items = try generateRandomSlice(allocator, rng, queue_size);965
966 const items = try generateRandomSlice(gpa, rng, queue_size);
913967
914 var queue = PDQ.fromOwnedSlice(testing.allocator, items, {});968 var queue: MinHeap = .fromOwnedSlice(items, {});
915 defer queue.deinit();969 defer queue.deinit(gpa);
916970
917 var last_removed: ?u32 = null;971 var last_removed: ?u32 = null;
918 while (queue.removeMaxOrNull()) |next| {972 while (queue.popMax()) |next| {
919 if (last_removed) |last| {973 if (last_removed) |last| {
920 try expect(last >= next);974 try expect(last >= next);
921 }975 }
...@@ -937,24 +991,25 @@ test "fuzz testing min and max" {...@@ -937,24 +991,25 @@ test "fuzz testing min and max" {
937}991}
938992
939fn fuzzTestMinMax(rng: std.Random, queue_size: usize) !void {993fn fuzzTestMinMax(rng: std.Random, queue_size: usize) !void {
940 const allocator = testing.allocator;994 const gpa = std.testing.allocator;
941 const items = try generateRandomSlice(allocator, rng, queue_size);995
996 const items = try generateRandomSlice(gpa, rng, queue_size);
942997
943 var queue = PDQ.fromOwnedSlice(allocator, items, {});998 var queue: MinHeap = .fromOwnedSlice(items, {});
944 defer queue.deinit();999 defer queue.deinit(gpa);
9451000
946 var last_min: ?u32 = null;1001 var last_min: ?u32 = null;
947 var last_max: ?u32 = null;1002 var last_max: ?u32 = null;
948 var i: usize = 0;1003 var i: usize = 0;
949 while (i < queue_size) : (i += 1) {1004 while (i < queue_size) : (i += 1) {
950 if (i % 2 == 0) {1005 if (i % 2 == 0) {
951 const next = queue.removeMin();1006 const next = queue.popMin().?;
952 if (last_min) |last| {1007 if (last_min) |last| {
953 try expect(last <= next);1008 try expect(last <= next);
954 }1009 }
955 last_min = next;1010 last_min = next;
956 } else {1011 } else {
957 const next = queue.removeMax();1012 const next = queue.popMax().?;
958 if (last_max) |last| {1013 if (last_max) |last| {
959 try expect(last >= next);1014 try expect(last >= next);
960 }1015 }
...@@ -980,48 +1035,52 @@ fn contextLessThanComparison(context: []const u32, a: usize, b: usize) Order {...@@ -980,48 +1035,52 @@ fn contextLessThanComparison(context: []const u32, a: usize, b: usize) Order {
980 return std.math.order(context[a], context[b]);1035 return std.math.order(context[a], context[b]);
981}1036}
9821037
983const CPDQ = PriorityDequeue(usize, []const u32, contextLessThanComparison);1038const MinHeapWithContext = PriorityDequeue(usize, []const u32, contextLessThanComparison);
1039
1040test "push and pop" {
1041 const gpa = std.testing.allocator;
9841042
985test "add and remove" {
986 const context = [_]u32{ 5, 3, 4, 2, 2, 8, 0 };1043 const context = [_]u32{ 5, 3, 4, 2, 2, 8, 0 };
9871044
988 var queue = CPDQ.init(testing.allocator, context[0..]);1045 var queue: MinHeapWithContext = .initContext(context[0..]);
989 defer queue.deinit();1046 defer queue.deinit(gpa);
9901047
991 try queue.add(0);1048 try queue.push(gpa, 0);
992 try queue.add(1);1049 try queue.push(gpa, 1);
993 try queue.add(2);1050 try queue.push(gpa, 2);
994 try queue.add(3);1051 try queue.push(gpa, 3);
995 try queue.add(4);1052 try queue.push(gpa, 4);
996 try queue.add(5);1053 try queue.push(gpa, 5);
997 try queue.add(6);1054 try queue.push(gpa, 6);
998 try expectEqual(@as(usize, 6), queue.removeMin());1055 try expectEqual(@as(usize, 6), queue.popMin());
999 try expectEqual(@as(usize, 5), queue.removeMax());1056 try expectEqual(@as(usize, 5), queue.popMax());
1000 try expectEqual(@as(usize, 3), queue.removeMin());1057 try expectEqual(@as(usize, 3), queue.popMin());
1001 try expectEqual(@as(usize, 0), queue.removeMax());1058 try expectEqual(@as(usize, 0), queue.popMax());
1002 try expectEqual(@as(usize, 4), queue.removeMin());1059 try expectEqual(@as(usize, 4), queue.popMin());
1003 try expectEqual(@as(usize, 2), queue.removeMax());1060 try expectEqual(@as(usize, 2), queue.popMax());
1004 try expectEqual(@as(usize, 1), queue.removeMin());1061 try expectEqual(@as(usize, 1), queue.popMin());
1005}1062}
10061063
1007var all_cmps_unique = true;1064var all_cmps_unique = true;
10081065
1009test "don't compare a value to a copy of itself" {1066test "don't compare a value to a copy of itself" {
1067 const gpa = std.testing.allocator;
1068
1010 var depq = PriorityDequeue(u32, void, struct {1069 var depq = PriorityDequeue(u32, void, struct {
1011 fn uniqueLessThan(_: void, a: u32, b: u32) Order {1070 fn uniqueLessThan(_: void, a: u32, b: u32) Order {
1012 all_cmps_unique = all_cmps_unique and (a != b);1071 all_cmps_unique = all_cmps_unique and (a != b);
1013 return std.math.order(a, b);1072 return std.math.order(a, b);
1014 }1073 }
1015 }.uniqueLessThan).init(testing.allocator, {});1074 }.uniqueLessThan).initContext({});
1016 defer depq.deinit();1075 defer depq.deinit(gpa);
10171076
1018 try depq.add(1);1077 try depq.push(gpa, 1);
1019 try depq.add(2);1078 try depq.push(gpa, 2);
1020 try depq.add(3);1079 try depq.push(gpa, 3);
1021 try depq.add(4);1080 try depq.push(gpa, 4);
1022 try depq.add(5);1081 try depq.push(gpa, 5);
1023 try depq.add(6);1082 try depq.push(gpa, 6);
10241083
1025 _ = depq.removeIndex(2);1084 _ = depq.popIndex(2);
1026 try expectEqual(all_cmps_unique, true);1085 try expectEqual(all_cmps_unique, true);
1027}1086}