From de07ca77e7310af23fa494defbf7cc235afdac68 Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Sun, 19 Jan 2020 22:05:38 +0400 Subject: [PATCH 1/4] rb: just use @include("std") we already have to use --override-std-dir when running std tests, and having it this way makes it much easier to run just the tests of this file. --- lib/std/rb.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/rb.zig b/lib/std/rb.zig index c41a269a27..0e87eb1eb6 100644 --- a/lib/std/rb.zig +++ b/lib/std/rb.zig @@ -1,4 +1,4 @@ -const std = @import("std.zig"); +const std = @import("std"); const assert = std.debug.assert; const testing = std.testing; const Order = std.math.Order; From e19008292222d37b3a08a4e6a9d41553ecfcfd7e Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Sun, 19 Jan 2020 22:08:05 +0400 Subject: [PATCH 2/4] rb: *breaking* make API thread-safe use @fieldParentPtr to access your context fields, which lie if you struct that contains a rb.Tree member (without a pointer). Also simplifies the init() function so rb.Tree can be initialized in a single line, without having to use "undefined". --- lib/std/rb.zig | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/std/rb.zig b/lib/std/rb.zig index 0e87eb1eb6..0fefa033a9 100644 --- a/lib/std/rb.zig +++ b/lib/std/rb.zig @@ -132,7 +132,7 @@ pub const Node = struct { pub const Tree = struct { root: ?*Node, - compareFn: fn (*Node, *Node) Order, + compareFn: fn (*Node, *Node, *Tree) Order, /// If you have a need for a version that caches this, please file a bug. pub fn first(tree: *Tree) ?*Node { @@ -389,7 +389,7 @@ pub const Tree = struct { var new = newconst; // I assume this can get optimized out if the caller already knows. - if (tree.compareFn(old, new) != .eq) return ReplaceError.NotEqual; + if (tree.compareFn(old, new, tree) != .eq) return ReplaceError.NotEqual; if (old.getParent()) |parent| { parent.setChild(new, parent.left == old); @@ -404,9 +404,11 @@ pub const Tree = struct { new.* = old.*; } - pub fn init(tree: *Tree, f: fn (*Node, *Node) Order) void { - tree.root = null; - tree.compareFn = f; + pub fn init(f: fn (*Node, *Node, *Tree) Order) Tree { + return Tree{ + .root = null, + .compareFn = f, + }; } }; @@ -469,7 +471,7 @@ fn doLookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node { is_left.* = false; while (maybe_node) |node| { - const res = tree.compareFn(node, key); + const res = tree.compareFn(node, key, tree); if (res == .eq) { return node; } @@ -498,7 +500,7 @@ fn testGetNumber(node: *Node) *testNumber { return @fieldParentPtr(testNumber, "node", node); } -fn testCompare(l: *Node, r: *Node) Order { +fn testCompare(l: *Node, r: *Node, contextIgnored: *Tree) Order { var left = testGetNumber(l); var right = testGetNumber(r); @@ -518,7 +520,7 @@ test "rb" { return error.SkipZigTest; } - var tree: Tree = undefined; + var tree = Tree.init(testCompare); var ns: [10]testNumber = undefined; ns[0].value = 42; ns[1].value = 41; @@ -534,7 +536,6 @@ test "rb" { var dup: testNumber = undefined; dup.value = 32345; - tree.init(testCompare); _ = tree.insert(&ns[1].node); _ = tree.insert(&ns[2].node); _ = tree.insert(&ns[3].node); @@ -557,8 +558,7 @@ test "rb" { } test "inserting and looking up" { - var tree: Tree = undefined; - tree.init(testCompare); + var tree = Tree.init(testCompare); var number: testNumber = undefined; number.value = 1000; _ = tree.insert(&number.node); @@ -582,8 +582,7 @@ test "multiple inserts, followed by calling first and last" { // TODO https://github.com/ziglang/zig/issues/3288 return error.SkipZigTest; } - var tree: Tree = undefined; - tree.init(testCompare); + var tree = Tree.init(testCompare); var zeroth: testNumber = undefined; zeroth.value = 0; var first: testNumber = undefined; From 4ab9678b9553d77bc0683ee69a8936524f6a7808 Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Sun, 19 Jan 2020 22:10:21 +0400 Subject: [PATCH 3/4] rb: add sort() that re-sorts tree with new compare function You can also specify the same compare function, but after updating the context that the function will use (connected to the rb.Tree) before. --- lib/std/rb.zig | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/std/rb.zig b/lib/std/rb.zig index 0fefa033a9..78e1e3aedd 100644 --- a/lib/std/rb.zig +++ b/lib/std/rb.zig @@ -134,6 +134,20 @@ pub const Tree = struct { root: ?*Node, compareFn: fn (*Node, *Node, *Tree) Order, + /// Re-sorts a tree with a new compare function + pub fn sort(tree: *Tree, newCompareFn: fn (*Node, *Node, *Tree) Order) !void { + var newTree = Tree.init(newCompareFn); + var node: *Node = undefined; + while (true) { + node = tree.first() orelse break; + tree.remove(node); + if (newTree.insert(node) != null) { + return error.NotUnique; // EEXISTS + } + } + tree.* = newTree; + } + /// If you have a need for a version that caches this, please file a bug. pub fn first(tree: *Tree) ?*Node { var node: *Node = tree.root orelse return null; @@ -244,6 +258,7 @@ pub const Tree = struct { return doLookup(key, tree, &parent, &is_left); } + /// If node is not part of tree, behavior is undefined. pub fn remove(tree: *Tree, nodeconst: *Node) void { var node = nodeconst; // as this has the same value as node, it is unsafe to access node after newnode @@ -514,6 +529,10 @@ fn testCompare(l: *Node, r: *Node, contextIgnored: *Tree) Order { unreachable; } +fn testCompareReverse(l: *Node, r: *Node, contextIgnored: *Tree) Order { + return testCompare(r, l, contextIgnored); +} + test "rb" { if (@import("builtin").arch == .aarch64) { // TODO https://github.com/ziglang/zig/issues/3288 @@ -600,4 +619,8 @@ test "multiple inserts, followed by calling first and last" { var lookupNode: testNumber = undefined; lookupNode.value = 3; assert(tree.lookup(&lookupNode.node) == &third.node); + tree.sort(testCompareReverse) catch unreachable; + assert(testGetNumber(tree.first().?).value == 3); + assert(testGetNumber(tree.last().?).value == 0); + assert(tree.lookup(&lookupNode.node) == &third.node); } From ad15a73240324fce93a659fbee9dab7be24dfbb0 Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Sun, 19 Jan 2020 23:35:56 +0400 Subject: [PATCH 4/4] rb: type Tree.sort with SortError --- lib/std/rb.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/rb.zig b/lib/std/rb.zig index 78e1e3aedd..d7840b75b0 100644 --- a/lib/std/rb.zig +++ b/lib/std/rb.zig @@ -11,6 +11,7 @@ const Red = Color.Red; const Black = Color.Black; const ReplaceError = error{NotEqual}; +const SortError = error{NotUnique}; // The new comparison function results in duplicates. /// Insert this into your struct that you want to add to a red-black tree. /// Do not use a pointer. Turn the *rb.Node results of the functions in rb @@ -135,7 +136,7 @@ pub const Tree = struct { compareFn: fn (*Node, *Node, *Tree) Order, /// Re-sorts a tree with a new compare function - pub fn sort(tree: *Tree, newCompareFn: fn (*Node, *Node, *Tree) Order) !void { + pub fn sort(tree: *Tree, newCompareFn: fn (*Node, *Node, *Tree) Order) SortError!void { var newTree = Tree.init(newCompareFn); var node: *Node = undefined; while (true) {