diff --git a/lib/compiler/resinator/source_mapping.zig b/lib/compiler/resinator/source_mapping.zig index 74943bd565..1144340252 100644 --- a/lib/compiler/resinator/source_mapping.zig +++ b/lib/compiler/resinator/source_mapping.zig @@ -571,10 +571,7 @@ pub const SourceMappings = struct { // now subtract the span diff from the start line number of all of // the following nodes in order - var it = Sources.InorderIterator{ - .current = node, - .previous = node.children[0], - }; + var it = Sources.InorderIterator{ .current = node }; // skip past current, but store it var prev = it.next().?; while (it.next()) |inorder_node| { diff --git a/lib/std/treap.zig b/lib/std/treap.zig index 46a51c6d5e..2508306f33 100644 --- a/lib/std/treap.zig +++ b/lib/std/treap.zig @@ -51,26 +51,53 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { priority: usize, parent: ?*Node, children: [2]?*Node, + + pub fn next(node: *Node) ?*Node { + return nextOnDirection(node, 1); + } + pub fn prev(node: *Node) ?*Node { + return nextOnDirection(node, 0); + } }; + fn extremeInSubtreeOnDirection(node: *Node, direction: u1) *Node { + var cur = node; + while (cur.children[direction]) |next| cur = next; + return cur; + } + + fn nextOnDirection(node: *Node, direction: u1) ?*Node { + if (node.children[direction]) |child| { + return extremeInSubtreeOnDirection(child, direction ^ 1); + } + var cur = node; + // Traversing upward until we find `parent` to `cur` is NOT on + // `direction`, or equivalently, `cur` to `parent` IS on + // `direction` thus `parent` is the next. + while (true) { + if (cur.parent) |parent| { + // If `parent -> node` is NOT on `direction`, then + // `node -> parent` IS on `direction` + if (parent.children[direction] != cur) return parent; + cur = parent; + } else { + return null; + } + } + } + /// Returns the smallest Node by key in the treap if there is one. /// Use `getEntryForExisting()` to replace/remove this Node from the treap. pub fn getMin(self: Self) ?*Node { - var node = self.root; - while (node) |current| { - node = current.children[0] orelse break; - } - return node; + if (self.root) |root| return extremeInSubtreeOnDirection(root, 0); + return null; } /// Returns the largest Node by key in the treap if there is one. /// Use `getEntryForExisting()` to replace/remove this Node from the treap. pub fn getMax(self: Self) ?*Node { - var node = self.root; - while (node) |current| { - node = current.children[1] orelse break; - } - return node; + if (self.root) |root| return extremeInSubtreeOnDirection(root, 1); + return null; } /// Lookup the Entry for the given key in the treap. @@ -117,7 +144,8 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { removed, }, - /// Update's the Node at this Entry in the treap with the new node. + /// Update's the Node at this Entry in the treap with the new node (null for deleting). `new_node` + /// can have `undefind` content because the value will be initialized internally. pub fn set(self: *Entry, new_node: ?*Node) void { // Update the entry's node reference after updating the treap below. defer self.node = new_node; @@ -257,46 +285,26 @@ pub fn Treap(comptime Key: type, comptime compareFn: anytype) type { link.* = target; } + /// Usage example: + /// var iter = treap.inorderIterator(); + /// while (iter.next()) |node| { + /// ... + /// } pub const InorderIterator = struct { current: ?*Node, - previous: ?*Node = null, pub fn next(it: *InorderIterator) ?*Node { - while (true) { - if (it.current) |current| { - const previous = it.previous; - it.previous = current; - if (previous == current.parent) { - if (current.children[0]) |left_child| { - it.current = left_child; - } else { - if (current.children[1]) |right_child| { - it.current = right_child; - } else { - it.current = current.parent; - } - return current; - } - } else if (previous == current.children[0]) { - if (current.children[1]) |right_child| { - it.current = right_child; - } else { - it.current = current.parent; - } - return current; - } else { - std.debug.assert(previous == current.children[1]); - it.current = current.parent; - } - } else { - return null; - } - } + const current = it.current; + it.current = if (current) |cur| + cur.next() + else + null; + return current; } }; pub fn inorderIterator(self: *Self) InorderIterator { - return .{ .current = self.root }; + return .{ .current = self.getMin() }; } }; } @@ -447,3 +455,228 @@ test "insert, find, replace, remove" { try testing.expectEqual(entry.node, treap.getEntryFor(key).node); } } + +test "inorderIterator" { + var treap = TestTreap{}; + var nodes: [10]TestNode = undefined; + + // Build the tree. + var i: usize = 0; + while (i < 10) : (i += 1) { + const key = @as(u64, i); + var entry = treap.getEntryFor(key); + entry.set(&nodes[i]); + } + + // Test the iterator. + var iter = treap.inorderIterator(); + i = 0; + while (iter.next()) |node| { + const key = @as(u64, i); + try testing.expectEqual(key, node.key); + i += 1; + } +} + +test "getMin, getMax, simple" { + var treap = TestTreap{}; + var nodes: [3]TestNode = undefined; + + try testing.expectEqual(null, treap.getMin()); + try testing.expectEqual(null, treap.getMax()); + { // nodes[1] + var entry = treap.getEntryFor(1); + entry.set(&nodes[1]); + try testing.expectEqual(&nodes[1], treap.getMin()); + try testing.expectEqual(&nodes[1], treap.getMax()); + } + { // nodes[0] + var entry = treap.getEntryFor(0); + entry.set(&nodes[0]); + try testing.expectEqual(&nodes[0], treap.getMin()); + try testing.expectEqual(&nodes[1], treap.getMax()); + } + { // nodes[2] + var entry = treap.getEntryFor(2); + entry.set(&nodes[2]); + try testing.expectEqual(&nodes[0], treap.getMin()); + try testing.expectEqual(&nodes[2], treap.getMax()); + } +} + +test "getMin, getMax, random" { + var nodes: [100]TestNode = undefined; + var prng = std.Random.DefaultPrng.init(0xdeadbeef); + var iter = SliceIterRandomOrder(TestNode).init(&nodes, prng.random()); + + var treap = TestTreap{}; + var min: u64 = std.math.maxInt(u64); + var max: u64 = 0; + + try testing.expectEqual(null, treap.getMin()); + try testing.expectEqual(null, treap.getMax()); + + // Insert and check min/max after each insertion. + iter.reset(); + while (iter.next()) |node| { + const key = prng.random().int(u64); + + // Insert into `treap`. + var entry = treap.getEntryFor(key); + entry.set(node); + + if (key < min) min = key; + if (key > max) max = key; + + const min_node = treap.getMin().?; + try std.testing.expectEqual(null, min_node.prev()); + try std.testing.expectEqual(min, min_node.key); + + const max_node = treap.getMax().?; + try std.testing.expectEqual(null, max_node.next()); + try std.testing.expectEqual(max, max_node.key); + } +} + +test "node.{prev(),next()} with sequential insertion and deletion" { + // Insert order: 50, 0, 1, 2, ..., 49, 51, 52, ..., 99. + // Delete order: 0, 1, 2, ..., 49, 51, 52, ..., 99. + // Check 50's neighbors. + var treap = TestTreap{}; + var nodes: [100]TestNode = undefined; + { + var entry = treap.getEntryFor(50); + entry.set(&nodes[50]); + try testing.expectEqual(50, nodes[50].key); + try testing.expectEqual(null, nodes[50].prev()); + try testing.expectEqual(null, nodes[50].next()); + } + // Insert others. + var i: usize = 0; + while (i < 50) : (i += 1) { + const key = @as(u64, i); + const node = &nodes[i]; + var entry = treap.getEntryFor(key); + entry.set(node); + try testing.expectEqual(key, node.key); + try testing.expectEqual(node, nodes[50].prev()); + try testing.expectEqual(null, nodes[50].next()); + } + i = 51; + while (i < 100) : (i += 1) { + const key = @as(u64, i); + const node = &nodes[i]; + var entry = treap.getEntryFor(key); + entry.set(node); + try testing.expectEqual(key, node.key); + try testing.expectEqual(&nodes[49], nodes[50].prev()); + try testing.expectEqual(&nodes[51], nodes[50].next()); + } + // Remove others. + i = 0; + while (i < 49) : (i += 1) { + const key = @as(u64, i); + var entry = treap.getEntryFor(key); + entry.set(null); + try testing.expectEqual(&nodes[49], nodes[50].prev()); + try testing.expectEqual(&nodes[51], nodes[50].next()); + } + { // i = 49. + const key = @as(u64, i); + var entry = treap.getEntryFor(key); + entry.set(null); + try testing.expectEqual(null, nodes[50].prev()); + try testing.expectEqual(&nodes[51], nodes[50].next()); + } + i = 51; + while (i < 99) : (i += 1) { + const key = @as(u64, i); + var entry = treap.getEntryFor(key); + entry.set(null); + try testing.expectEqual(null, nodes[50].prev()); + try testing.expectEqual(&nodes[i + 1], nodes[50].next()); + } + { // i = 99. + const key = @as(u64, i); + var entry = treap.getEntryFor(key); + entry.set(null); + try testing.expectEqual(null, nodes[50].prev()); + try testing.expectEqual(null, nodes[50].next()); + } +} + +fn findFirstGreaterOrEqual(array: []u64, value: u64) usize { + var i: usize = 0; + while (i < array.len and array[i] < value) i += 1; + return i; +} + +fn testOrderedArrayAndTreapConsistency(array: []u64, treap: *TestTreap) !void { + var i: usize = 0; + while (i < array.len) : (i += 1) { + const value = array[i]; + + const entry = treap.getEntryFor(value); + try testing.expect(entry.node != null); + const node = entry.node.?; + try testing.expectEqual(value, node.key); + + if (i == 0) { + try testing.expectEqual(node.prev(), null); + } else { + try testing.expectEqual(node.prev(), treap.getEntryFor(array[i - 1]).node); + } + if (i + 1 == array.len) { + try testing.expectEqual(node.next(), null); + } else { + try testing.expectEqual(node.next(), treap.getEntryFor(array[i + 1]).node); + } + } +} + +test "node.{prev(),next()} with random data" { + var nodes: [100]TestNode = undefined; + var prng = std.Random.DefaultPrng.init(0xdeadbeef); + var iter = SliceIterRandomOrder(TestNode).init(&nodes, prng.random()); + + var treap = TestTreap{}; + // A slow, stupid but correct reference. Ordered. + var golden = std.ArrayList(u64).init(std.testing.allocator); + defer golden.deinit(); + + // Insert. + iter.reset(); + while (iter.next()) |node| { + const key = prng.random().int(u64); + + // Insert into `golden`. + const i = findFirstGreaterOrEqual(golden.items, key); + // Ensure not found. If found: `prng`'s fault. + try testing.expect(i == golden.items.len or golden.items[i] > key); + try golden.insert(i, key); + + // Insert into `treap`. + var entry = treap.getEntryFor(key); + entry.set(node); + + try testOrderedArrayAndTreapConsistency(golden.items, &treap); + } + + // Delete. + iter.reset(); + while (iter.next()) |node| { + const key = node.key; + + // Delete from `golden`. + const i = findFirstGreaterOrEqual(golden.items, key); + try testing.expect(i < golden.items.len); + _ = golden.orderedRemove(i); + + // Delete from `treap`. + var entry = treap.getEntryFor(key); + try testing.expect(entry.node != null); + entry.set(null); + + try testOrderedArrayAndTreapConsistency(golden.items, &treap); + } +}