| ... | ... | @@ -8,26 +8,28 @@ const expectEqual = testing.expectEqual; |
| 8 | 8 | const expectError = testing.expectError; |
| 9 | 9 | |
| 10 | 10 | /// Priority queue for storing generic data. Initialize with `init`. |
| 11 | | /// Provide `compareFn` that returns `Order.lt` when its first |
| 12 | | /// argument should get popped before its second argument, |
| 11 | /// Provide `compareFn` that returns `Order.lt` when its second |
| 12 | /// argument should get popped before its third argument, |
| 13 | 13 | /// `Order.eq` if the arguments are of equal priority, or `Order.gt` |
| 14 | | /// if the second argument should be popped first. |
| 14 | /// if the third argument should be popped first. |
| 15 | 15 | /// For example, to make `pop` return the smallest number, provide |
| 16 | | /// `fn lessThan(a: T, b: T) Order { return std.math.order(a, b); }` |
| 17 | | pub fn PriorityQueue(comptime T: type, comptime compareFn: fn (a: T, b: T) Order) type { |
| 16 | /// `fn lessThan(context: void, a: T, b: T) Order { _ = context; return std.math.order(a, b); }` |
| 17 | pub fn PriorityQueue(comptime T: type, comptime Context: type, comptime compareFn: fn (context: Context, a: T, b: T) Order) type { |
| 18 | 18 | return struct { |
| 19 | 19 | const Self = @This(); |
| 20 | 20 | |
| 21 | 21 | items: []T, |
| 22 | 22 | len: usize, |
| 23 | 23 | allocator: Allocator, |
| 24 | context: Context, |
| 24 | 25 | |
| 25 | 26 | /// Initialize and return a priority queue. |
| 26 | | pub fn init(allocator: Allocator) Self { |
| 27 | pub fn init(allocator: Allocator, context: Context) Self { |
| 27 | 28 | return Self{ |
| 28 | 29 | .items = &[_]T{}, |
| 29 | 30 | .len = 0, |
| 30 | 31 | .allocator = allocator, |
| 32 | .context = context, |
| 31 | 33 | }; |
| 32 | 34 | } |
| 33 | 35 | |
| ... | ... | @@ -55,7 +57,7 @@ pub fn PriorityQueue(comptime T: type, comptime compareFn: fn (a: T, b: T) Order |
| 55 | 57 | const child = self.items[child_index]; |
| 56 | 58 | const parent = self.items[parent_index]; |
| 57 | 59 | |
| 58 | | if (compareFn(child, parent) != .lt) break; |
| 60 | if (compareFn(self.context, child, parent) != .lt) break; |
| 59 | 61 | |
| 60 | 62 | self.items[parent_index] = child; |
| 61 | 63 | self.items[child_index] = parent; |
| ... | ... | @@ -127,14 +129,14 @@ pub fn PriorityQueue(comptime T: type, comptime compareFn: fn (a: T, b: T) Order |
| 127 | 129 | var smallest = self.items[index]; |
| 128 | 130 | |
| 129 | 131 | if (left) |e| { |
| 130 | | if (compareFn(e, smallest) == .lt) { |
| 132 | if (compareFn(self.context, e, smallest) == .lt) { |
| 131 | 133 | smallest_index = left_index; |
| 132 | 134 | smallest = e; |
| 133 | 135 | } |
| 134 | 136 | } |
| 135 | 137 | |
| 136 | 138 | if (right) |e| { |
| 137 | | if (compareFn(e, smallest) == .lt) { |
| 139 | if (compareFn(self.context, e, smallest) == .lt) { |
| 138 | 140 | smallest_index = right_index; |
| 139 | 141 | smallest = e; |
| 140 | 142 | } |
| ... | ... | @@ -153,11 +155,12 @@ pub fn PriorityQueue(comptime T: type, comptime compareFn: fn (a: T, b: T) Order |
| 153 | 155 | /// PriorityQueue takes ownership of the passed in slice. The slice must have been |
| 154 | 156 | /// allocated with `allocator`. |
| 155 | 157 | /// Deinitialize with `deinit`. |
| 156 | | pub fn fromOwnedSlice(allocator: Allocator, items: []T) Self { |
| 158 | pub fn fromOwnedSlice(allocator: Allocator, items: []T, context: Context) Self { |
| 157 | 159 | var queue = Self{ |
| 158 | 160 | .items = items, |
| 159 | 161 | .len = items.len, |
| 160 | 162 | .allocator = allocator, |
| 163 | .context = context, |
| 161 | 164 | }; |
| 162 | 165 | |
| 163 | 166 | if (queue.len <= 1) return queue; |
| ... | ... | @@ -207,7 +210,7 @@ pub fn PriorityQueue(comptime T: type, comptime compareFn: fn (a: T, b: T) Order |
| 207 | 210 | var update_index: usize = std.mem.indexOfScalar(T, self.items[0..self.len], elem) orelse return error.ElementNotFound; |
| 208 | 211 | const old_elem: T = self.items[update_index]; |
| 209 | 212 | self.items[update_index] = new_elem; |
| 210 | | switch (compareFn(new_elem, old_elem)) { |
| 213 | switch (compareFn(self.context, new_elem, old_elem)) { |
| 211 | 214 | .lt => siftUp(self, update_index), |
| 212 | 215 | .gt => siftDown(self, update_index), |
| 213 | 216 | .eq => {}, // Nothing to do as the items have equal priority |
| ... | ... | @@ -215,7 +218,7 @@ pub fn PriorityQueue(comptime T: type, comptime compareFn: fn (a: T, b: T) Order |
| 215 | 218 | } |
| 216 | 219 | |
| 217 | 220 | pub const Iterator = struct { |
| 218 | | queue: *PriorityQueue(T, compareFn), |
| 221 | queue: *PriorityQueue(T, Context, compareFn), |
| 219 | 222 | count: usize, |
| 220 | 223 | |
| 221 | 224 | pub fn next(it: *Iterator) ?T { |
| ... | ... | @@ -258,19 +261,20 @@ pub fn PriorityQueue(comptime T: type, comptime compareFn: fn (a: T, b: T) Order |
| 258 | 261 | }; |
| 259 | 262 | } |
| 260 | 263 | |
| 261 | | fn lessThan(a: u32, b: u32) Order { |
| 264 | fn lessThan(context: void, a: u32, b: u32) Order { |
| 265 | _ = context; |
| 262 | 266 | return std.math.order(a, b); |
| 263 | 267 | } |
| 264 | 268 | |
| 265 | | fn greaterThan(a: u32, b: u32) Order { |
| 266 | | return lessThan(a, b).invert(); |
| 269 | fn greaterThan(context: void, a: u32, b: u32) Order { |
| 270 | return lessThan(context, a, b).invert(); |
| 267 | 271 | } |
| 268 | 272 | |
| 269 | | const PQlt = PriorityQueue(u32, lessThan); |
| 270 | | const PQgt = PriorityQueue(u32, greaterThan); |
| 273 | const PQlt = PriorityQueue(u32, void, lessThan); |
| 274 | const PQgt = PriorityQueue(u32, void, greaterThan); |
| 271 | 275 | |
| 272 | 276 | test "std.PriorityQueue: add and remove min heap" { |
| 273 | | var queue = PQlt.init(testing.allocator); |
| 277 | var queue = PQlt.init(testing.allocator, {}); |
| 274 | 278 | defer queue.deinit(); |
| 275 | 279 | |
| 276 | 280 | try queue.add(54); |
| ... | ... | @@ -288,7 +292,7 @@ test "std.PriorityQueue: add and remove min heap" { |
| 288 | 292 | } |
| 289 | 293 | |
| 290 | 294 | test "std.PriorityQueue: add and remove same min heap" { |
| 291 | | var queue = PQlt.init(testing.allocator); |
| 295 | var queue = PQlt.init(testing.allocator, {}); |
| 292 | 296 | defer queue.deinit(); |
| 293 | 297 | |
| 294 | 298 | try queue.add(1); |
| ... | ... | @@ -306,14 +310,14 @@ test "std.PriorityQueue: add and remove same min heap" { |
| 306 | 310 | } |
| 307 | 311 | |
| 308 | 312 | test "std.PriorityQueue: removeOrNull on empty" { |
| 309 | | var queue = PQlt.init(testing.allocator); |
| 313 | var queue = PQlt.init(testing.allocator, {}); |
| 310 | 314 | defer queue.deinit(); |
| 311 | 315 | |
| 312 | 316 | try expect(queue.removeOrNull() == null); |
| 313 | 317 | } |
| 314 | 318 | |
| 315 | 319 | test "std.PriorityQueue: edge case 3 elements" { |
| 316 | | var queue = PQlt.init(testing.allocator); |
| 320 | var queue = PQlt.init(testing.allocator, {}); |
| 317 | 321 | defer queue.deinit(); |
| 318 | 322 | |
| 319 | 323 | try queue.add(9); |
| ... | ... | @@ -325,7 +329,7 @@ test "std.PriorityQueue: edge case 3 elements" { |
| 325 | 329 | } |
| 326 | 330 | |
| 327 | 331 | test "std.PriorityQueue: peek" { |
| 328 | | var queue = PQlt.init(testing.allocator); |
| 332 | var queue = PQlt.init(testing.allocator, {}); |
| 329 | 333 | defer queue.deinit(); |
| 330 | 334 | |
| 331 | 335 | try expect(queue.peek() == null); |
| ... | ... | @@ -337,7 +341,7 @@ test "std.PriorityQueue: peek" { |
| 337 | 341 | } |
| 338 | 342 | |
| 339 | 343 | test "std.PriorityQueue: sift up with odd indices" { |
| 340 | | var queue = PQlt.init(testing.allocator); |
| 344 | var queue = PQlt.init(testing.allocator, {}); |
| 341 | 345 | defer queue.deinit(); |
| 342 | 346 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; |
| 343 | 347 | for (items) |e| { |
| ... | ... | @@ -351,7 +355,7 @@ test "std.PriorityQueue: sift up with odd indices" { |
| 351 | 355 | } |
| 352 | 356 | |
| 353 | 357 | test "std.PriorityQueue: addSlice" { |
| 354 | | var queue = PQlt.init(testing.allocator); |
| 358 | var queue = PQlt.init(testing.allocator, {}); |
| 355 | 359 | defer queue.deinit(); |
| 356 | 360 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; |
| 357 | 361 | try queue.addSlice(items[0..]); |
| ... | ... | @@ -365,7 +369,7 @@ test "std.PriorityQueue: addSlice" { |
| 365 | 369 | test "std.PriorityQueue: fromOwnedSlice trivial case 0" { |
| 366 | 370 | const items = [0]u32{}; |
| 367 | 371 | const queue_items = try testing.allocator.dupe(u32, &items); |
| 368 | | var queue = PQlt.fromOwnedSlice(testing.allocator, queue_items[0..]); |
| 372 | var queue = PQlt.fromOwnedSlice(testing.allocator, queue_items[0..], {}); |
| 369 | 373 | defer queue.deinit(); |
| 370 | 374 | try expectEqual(@as(usize, 0), queue.len); |
| 371 | 375 | try expect(queue.removeOrNull() == null); |
| ... | ... | @@ -374,7 +378,7 @@ test "std.PriorityQueue: fromOwnedSlice trivial case 0" { |
| 374 | 378 | test "std.PriorityQueue: fromOwnedSlice trivial case 1" { |
| 375 | 379 | const items = [1]u32{1}; |
| 376 | 380 | const queue_items = try testing.allocator.dupe(u32, &items); |
| 377 | | var queue = PQlt.fromOwnedSlice(testing.allocator, queue_items[0..]); |
| 381 | var queue = PQlt.fromOwnedSlice(testing.allocator, queue_items[0..], {}); |
| 378 | 382 | defer queue.deinit(); |
| 379 | 383 | |
| 380 | 384 | try expectEqual(@as(usize, 1), queue.len); |
| ... | ... | @@ -385,7 +389,7 @@ test "std.PriorityQueue: fromOwnedSlice trivial case 1" { |
| 385 | 389 | test "std.PriorityQueue: fromOwnedSlice" { |
| 386 | 390 | const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; |
| 387 | 391 | const heap_items = try testing.allocator.dupe(u32, items[0..]); |
| 388 | | var queue = PQlt.fromOwnedSlice(testing.allocator, heap_items[0..]); |
| 392 | var queue = PQlt.fromOwnedSlice(testing.allocator, heap_items[0..], {}); |
| 389 | 393 | defer queue.deinit(); |
| 390 | 394 | |
| 391 | 395 | const sorted_items = [_]u32{ 1, 2, 5, 6, 7, 7, 11, 12, 13, 14, 15, 15, 16, 21, 22, 24, 24, 25 }; |
| ... | ... | @@ -395,7 +399,7 @@ test "std.PriorityQueue: fromOwnedSlice" { |
| 395 | 399 | } |
| 396 | 400 | |
| 397 | 401 | test "std.PriorityQueue: add and remove max heap" { |
| 398 | | var queue = PQgt.init(testing.allocator); |
| 402 | var queue = PQgt.init(testing.allocator, {}); |
| 399 | 403 | defer queue.deinit(); |
| 400 | 404 | |
| 401 | 405 | try queue.add(54); |
| ... | ... | @@ -413,7 +417,7 @@ test "std.PriorityQueue: add and remove max heap" { |
| 413 | 417 | } |
| 414 | 418 | |
| 415 | 419 | test "std.PriorityQueue: add and remove same max heap" { |
| 416 | | var queue = PQgt.init(testing.allocator); |
| 420 | var queue = PQgt.init(testing.allocator, {}); |
| 417 | 421 | defer queue.deinit(); |
| 418 | 422 | |
| 419 | 423 | try queue.add(1); |
| ... | ... | @@ -431,7 +435,7 @@ test "std.PriorityQueue: add and remove same max heap" { |
| 431 | 435 | } |
| 432 | 436 | |
| 433 | 437 | test "std.PriorityQueue: iterator" { |
| 434 | | var queue = PQlt.init(testing.allocator); |
| 438 | var queue = PQlt.init(testing.allocator, {}); |
| 435 | 439 | var map = std.AutoHashMap(u32, void).init(testing.allocator); |
| 436 | 440 | defer { |
| 437 | 441 | queue.deinit(); |
| ... | ... | @@ -453,7 +457,7 @@ test "std.PriorityQueue: iterator" { |
| 453 | 457 | } |
| 454 | 458 | |
| 455 | 459 | test "std.PriorityQueue: remove at index" { |
| 456 | | var queue = PQlt.init(testing.allocator); |
| 460 | var queue = PQlt.init(testing.allocator, {}); |
| 457 | 461 | defer queue.deinit(); |
| 458 | 462 | |
| 459 | 463 | try queue.add(3); |
| ... | ... | @@ -476,7 +480,7 @@ test "std.PriorityQueue: remove at index" { |
| 476 | 480 | } |
| 477 | 481 | |
| 478 | 482 | test "std.PriorityQueue: iterator while empty" { |
| 479 | | var queue = PQlt.init(testing.allocator); |
| 483 | var queue = PQlt.init(testing.allocator, {}); |
| 480 | 484 | defer queue.deinit(); |
| 481 | 485 | |
| 482 | 486 | var it = queue.iterator(); |
| ... | ... | @@ -485,7 +489,7 @@ test "std.PriorityQueue: iterator while empty" { |
| 485 | 489 | } |
| 486 | 490 | |
| 487 | 491 | test "std.PriorityQueue: shrinkAndFree" { |
| 488 | | var queue = PQlt.init(testing.allocator); |
| 492 | var queue = PQlt.init(testing.allocator, {}); |
| 489 | 493 | defer queue.deinit(); |
| 490 | 494 | |
| 491 | 495 | try queue.ensureTotalCapacity(4); |
| ... | ... | @@ -508,7 +512,7 @@ test "std.PriorityQueue: shrinkAndFree" { |
| 508 | 512 | } |
| 509 | 513 | |
| 510 | 514 | test "std.PriorityQueue: update min heap" { |
| 511 | | var queue = PQlt.init(testing.allocator); |
| 515 | var queue = PQlt.init(testing.allocator, {}); |
| 512 | 516 | defer queue.deinit(); |
| 513 | 517 | |
| 514 | 518 | try queue.add(55); |
| ... | ... | @@ -523,7 +527,7 @@ test "std.PriorityQueue: update min heap" { |
| 523 | 527 | } |
| 524 | 528 | |
| 525 | 529 | test "std.PriorityQueue: update same min heap" { |
| 526 | | var queue = PQlt.init(testing.allocator); |
| 530 | var queue = PQlt.init(testing.allocator, {}); |
| 527 | 531 | defer queue.deinit(); |
| 528 | 532 | |
| 529 | 533 | try queue.add(1); |
| ... | ... | @@ -539,7 +543,7 @@ test "std.PriorityQueue: update same min heap" { |
| 539 | 543 | } |
| 540 | 544 | |
| 541 | 545 | test "std.PriorityQueue: update max heap" { |
| 542 | | var queue = PQgt.init(testing.allocator); |
| 546 | var queue = PQgt.init(testing.allocator, {}); |
| 543 | 547 | defer queue.deinit(); |
| 544 | 548 | |
| 545 | 549 | try queue.add(55); |
| ... | ... | @@ -554,7 +558,7 @@ test "std.PriorityQueue: update max heap" { |
| 554 | 558 | } |
| 555 | 559 | |
| 556 | 560 | test "std.PriorityQueue: update same max heap" { |
| 557 | | var queue = PQgt.init(testing.allocator); |
| 561 | var queue = PQgt.init(testing.allocator, {}); |
| 558 | 562 | defer queue.deinit(); |
| 559 | 563 | |
| 560 | 564 | try queue.add(1); |
| ... | ... | @@ -568,3 +572,31 @@ test "std.PriorityQueue: update same max heap" { |
| 568 | 572 | try expectEqual(@as(u32, 2), queue.remove()); |
| 569 | 573 | try expectEqual(@as(u32, 1), queue.remove()); |
| 570 | 574 | } |
| 575 | |
| 576 | fn contextLessThan(context: []const u32, a: usize, b: usize) Order { |
| 577 | return std.math.order(context[a], context[b]); |
| 578 | } |
| 579 | |
| 580 | const CPQlt = PriorityQueue(usize, []const u32, contextLessThan); |
| 581 | |
| 582 | test "std.PriorityQueue: add and remove min heap with contextful comparator" { |
| 583 | const context = [_]u32{ 5, 3, 4, 2, 2, 8, 0 }; |
| 584 | |
| 585 | var queue = CPQlt.init(testing.allocator, context[0..]); |
| 586 | defer queue.deinit(); |
| 587 | |
| 588 | try queue.add(0); |
| 589 | try queue.add(1); |
| 590 | try queue.add(2); |
| 591 | try queue.add(3); |
| 592 | try queue.add(4); |
| 593 | try queue.add(5); |
| 594 | try queue.add(6); |
| 595 | try expectEqual(@as(usize, 6), queue.remove()); |
| 596 | try expectEqual(@as(usize, 4), queue.remove()); |
| 597 | try expectEqual(@as(usize, 3), queue.remove()); |
| 598 | try expectEqual(@as(usize, 1), queue.remove()); |
| 599 | try expectEqual(@as(usize, 2), queue.remove()); |
| 600 | try expectEqual(@as(usize, 0), queue.remove()); |
| 601 | try expectEqual(@as(usize, 5), queue.remove()); |
| 602 | } |