authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-29 16:01:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-29 16:01:51-07:00
log63685190da9415ff7d27080e0f33cbeab6a80156
tree06d7c48cd5f00f100c6340439bb8a968d885d5fa
parent56c5b665a1f93fd16a711986e25e4d7c5dfed163

move std.rb to the standard library orphanage

https://github.com/ziglang/std-lib-orphanage/ This code is not used by anything else in the standard library or by the compiler or any of its tools and therefore it's a great candidate to be maintained by a third party.

2 files changed, 0 insertions(+), 634 deletions(-)

lib/std/rb.zig deleted-633
...@@ -1,633 +0,0 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std");
7const assert = std.debug.assert;
8const testing = std.testing;
9const Order = std.math.Order;
10
11const Color = enum(u1) {
12 Black,
13 Red,
14};
15const Red = Color.Red;
16const Black = Color.Black;
17
18const ReplaceError = error{NotEqual};
19const SortError = error{NotUnique}; // The new comparison function results in duplicates.
20
21/// Insert this into your struct that you want to add to a red-black tree.
22/// Do not use a pointer. Turn the *rb.Node results of the functions in rb
23/// (after resolving optionals) to your structure using @fieldParentPtr(). Example:
24///
25/// const Number = struct {
26/// node: rb.Node,
27/// value: i32,
28/// };
29/// fn number(node: *rb.Node) Number {
30/// return @fieldParentPtr(Number, "node", node);
31/// }
32pub const Node = struct {
33 left: ?*Node,
34 right: ?*Node,
35
36 /// parent | color
37 parent_and_color: usize,
38
39 pub fn next(constnode: *Node) ?*Node {
40 var node = constnode;
41
42 if (node.right) |right| {
43 var n = right;
44 while (n.left) |left|
45 n = left;
46 return n;
47 }
48
49 while (true) {
50 var parent = node.getParent();
51 if (parent) |p| {
52 if (node != p.right)
53 return p;
54 node = p;
55 } else
56 return null;
57 }
58 }
59
60 pub fn prev(constnode: *Node) ?*Node {
61 var node = constnode;
62
63 if (node.left) |left| {
64 var n = left;
65 while (n.right) |right|
66 n = right;
67 return n;
68 }
69
70 while (true) {
71 var parent = node.getParent();
72 if (parent) |p| {
73 if (node != p.left)
74 return p;
75 node = p;
76 } else
77 return null;
78 }
79 }
80
81 pub fn isRoot(node: *Node) bool {
82 return node.getParent() == null;
83 }
84
85 fn isRed(node: *Node) bool {
86 return node.getColor() == Red;
87 }
88
89 fn isBlack(node: *Node) bool {
90 return node.getColor() == Black;
91 }
92
93 fn setParent(node: *Node, parent: ?*Node) void {
94 node.parent_and_color = @ptrToInt(parent) | (node.parent_and_color & 1);
95 }
96
97 fn getParent(node: *Node) ?*Node {
98 const mask: usize = 1;
99 comptime {
100 assert(@alignOf(*Node) >= 2);
101 }
102 const maybe_ptr = node.parent_and_color & ~mask;
103 return if (maybe_ptr == 0) null else @intToPtr(*Node, maybe_ptr);
104 }
105
106 fn setColor(node: *Node, color: Color) void {
107 const mask: usize = 1;
108 node.parent_and_color = (node.parent_and_color & ~mask) | @enumToInt(color);
109 }
110
111 fn getColor(node: *Node) Color {
112 return @intToEnum(Color, @intCast(u1, node.parent_and_color & 1));
113 }
114
115 fn setChild(node: *Node, child: ?*Node, is_left: bool) void {
116 if (is_left) {
117 node.left = child;
118 } else {
119 node.right = child;
120 }
121 }
122
123 fn getFirst(nodeconst: *Node) *Node {
124 var node = nodeconst;
125 while (node.left) |left| {
126 node = left;
127 }
128 return node;
129 }
130
131 fn getLast(nodeconst: *Node) *Node {
132 var node = nodeconst;
133 while (node.right) |right| {
134 node = right;
135 }
136 return node;
137 }
138};
139
140pub const Tree = struct {
141 root: ?*Node,
142 compareFn: fn (*Node, *Node, *Tree) Order,
143
144 /// Re-sorts a tree with a new compare function
145 pub fn sort(tree: *Tree, newCompareFn: fn (*Node, *Node, *Tree) Order) SortError!void {
146 var newTree = Tree.init(newCompareFn);
147 var node: *Node = undefined;
148 while (true) {
149 node = tree.first() orelse break;
150 tree.remove(node);
151 if (newTree.insert(node) != null) {
152 return error.NotUnique; // EEXISTS
153 }
154 }
155 tree.* = newTree;
156 }
157
158 /// If you have a need for a version that caches this, please file a bug.
159 pub fn first(tree: *Tree) ?*Node {
160 var node: *Node = tree.root orelse return null;
161
162 while (node.left) |left| {
163 node = left;
164 }
165
166 return node;
167 }
168
169 pub fn last(tree: *Tree) ?*Node {
170 var node: *Node = tree.root orelse return null;
171
172 while (node.right) |right| {
173 node = right;
174 }
175
176 return node;
177 }
178
179 /// Duplicate keys are not allowed. The item with the same key already in the
180 /// tree will be returned, and the item will not be inserted.
181 pub fn insert(tree: *Tree, node_const: *Node) ?*Node {
182 var node = node_const;
183 var maybe_key: ?*Node = undefined;
184 var maybe_parent: ?*Node = undefined;
185 var is_left: bool = undefined;
186
187 maybe_key = doLookup(node, tree, &maybe_parent, &is_left);
188 if (maybe_key) |key| {
189 return key;
190 }
191
192 node.left = null;
193 node.right = null;
194 node.setColor(Red);
195 node.setParent(maybe_parent);
196
197 if (maybe_parent) |parent| {
198 parent.setChild(node, is_left);
199 } else {
200 tree.root = node;
201 }
202
203 while (node.getParent()) |*parent| {
204 if (parent.*.isBlack())
205 break;
206 // the root is always black
207 var grandpa = parent.*.getParent() orelse unreachable;
208
209 if (parent.* == grandpa.left) {
210 var maybe_uncle = grandpa.right;
211
212 if (maybe_uncle) |uncle| {
213 if (uncle.isBlack())
214 break;
215
216 parent.*.setColor(Black);
217 uncle.setColor(Black);
218 grandpa.setColor(Red);
219 node = grandpa;
220 } else {
221 if (node == parent.*.right) {
222 rotateLeft(parent.*, tree);
223 node = parent.*;
224 parent.* = node.getParent().?; // Just rotated
225 }
226 parent.*.setColor(Black);
227 grandpa.setColor(Red);
228 rotateRight(grandpa, tree);
229 }
230 } else {
231 var maybe_uncle = grandpa.left;
232
233 if (maybe_uncle) |uncle| {
234 if (uncle.isBlack())
235 break;
236
237 parent.*.setColor(Black);
238 uncle.setColor(Black);
239 grandpa.setColor(Red);
240 node = grandpa;
241 } else {
242 if (node == parent.*.left) {
243 rotateRight(parent.*, tree);
244 node = parent.*;
245 parent.* = node.getParent().?; // Just rotated
246 }
247 parent.*.setColor(Black);
248 grandpa.setColor(Red);
249 rotateLeft(grandpa, tree);
250 }
251 }
252 }
253 // This was an insert, there is at least one node.
254 tree.root.?.setColor(Black);
255 return null;
256 }
257
258 /// lookup searches for the value of key, using binary search. It will
259 /// return a pointer to the node if it is there, otherwise it will return null.
260 /// Complexity guaranteed O(log n), where n is the number of nodes book-kept
261 /// by tree.
262 pub fn lookup(tree: *Tree, key: *Node) ?*Node {
263 var parent: ?*Node = undefined;
264 var is_left: bool = undefined;
265 return doLookup(key, tree, &parent, &is_left);
266 }
267
268 /// If node is not part of tree, behavior is undefined.
269 pub fn remove(tree: *Tree, nodeconst: *Node) void {
270 var node = nodeconst;
271 // as this has the same value as node, it is unsafe to access node after newnode
272 var newnode: ?*Node = nodeconst;
273 var maybe_parent: ?*Node = node.getParent();
274 var color: Color = undefined;
275 var next: *Node = undefined;
276
277 // This clause is to avoid optionals
278 if (node.left == null and node.right == null) {
279 if (maybe_parent) |parent| {
280 parent.setChild(null, parent.left == node);
281 } else
282 tree.root = null;
283 color = node.getColor();
284 newnode = null;
285 } else {
286 if (node.left == null) {
287 next = node.right.?; // Not both null as per above
288 } else if (node.right == null) {
289 next = node.left.?; // Not both null as per above
290 } else
291 next = node.right.?.getFirst(); // Just checked for null above
292
293 if (maybe_parent) |parent| {
294 parent.setChild(next, parent.left == node);
295 } else
296 tree.root = next;
297
298 if (node.left != null and node.right != null) {
299 const left = node.left.?;
300 const right = node.right.?;
301
302 color = next.getColor();
303 next.setColor(node.getColor());
304
305 next.left = left;
306 left.setParent(next);
307
308 if (next != right) {
309 var parent = next.getParent().?; // Was traversed via child node (right/left)
310 next.setParent(node.getParent());
311
312 newnode = next.right;
313 parent.left = node;
314
315 next.right = right;
316 right.setParent(next);
317 } else {
318 next.setParent(maybe_parent);
319 maybe_parent = next;
320 newnode = next.right;
321 }
322 } else {
323 color = node.getColor();
324 newnode = next;
325 }
326 }
327
328 if (newnode) |n|
329 n.setParent(maybe_parent);
330
331 if (color == Red)
332 return;
333 if (newnode) |n| {
334 n.setColor(Black);
335 return;
336 }
337
338 while (node == tree.root) {
339 // If not root, there must be parent
340 var parent = maybe_parent.?;
341 if (node == parent.left) {
342 var sibling = parent.right.?; // Same number of black nodes.
343
344 if (sibling.isRed()) {
345 sibling.setColor(Black);
346 parent.setColor(Red);
347 rotateLeft(parent, tree);
348 sibling = parent.right.?; // Just rotated
349 }
350 if ((if (sibling.left) |n| n.isBlack() else true) and
351 (if (sibling.right) |n| n.isBlack() else true))
352 {
353 sibling.setColor(Red);
354 node = parent;
355 maybe_parent = parent.getParent();
356 continue;
357 }
358 if (if (sibling.right) |n| n.isBlack() else true) {
359 sibling.left.?.setColor(Black); // Same number of black nodes.
360 sibling.setColor(Red);
361 rotateRight(sibling, tree);
362 sibling = parent.right.?; // Just rotated
363 }
364 sibling.setColor(parent.getColor());
365 parent.setColor(Black);
366 sibling.right.?.setColor(Black); // Same number of black nodes.
367 rotateLeft(parent, tree);
368 newnode = tree.root;
369 break;
370 } else {
371 var sibling = parent.left.?; // Same number of black nodes.
372
373 if (sibling.isRed()) {
374 sibling.setColor(Black);
375 parent.setColor(Red);
376 rotateRight(parent, tree);
377 sibling = parent.left.?; // Just rotated
378 }
379 if ((if (sibling.left) |n| n.isBlack() else true) and
380 (if (sibling.right) |n| n.isBlack() else true))
381 {
382 sibling.setColor(Red);
383 node = parent;
384 maybe_parent = parent.getParent();
385 continue;
386 }
387 if (if (sibling.left) |n| n.isBlack() else true) {
388 sibling.right.?.setColor(Black); // Same number of black nodes
389 sibling.setColor(Red);
390 rotateLeft(sibling, tree);
391 sibling = parent.left.?; // Just rotated
392 }
393 sibling.setColor(parent.getColor());
394 parent.setColor(Black);
395 sibling.left.?.setColor(Black); // Same number of black nodes
396 rotateRight(parent, tree);
397 newnode = tree.root;
398 break;
399 }
400
401 if (node.isRed())
402 break;
403 }
404
405 if (newnode) |n|
406 n.setColor(Black);
407 }
408
409 /// This is a shortcut to avoid removing and re-inserting an item with the same key.
410 pub fn replace(tree: *Tree, old: *Node, newconst: *Node) !void {
411 var new = newconst;
412
413 // I assume this can get optimized out if the caller already knows.
414 if (tree.compareFn(old, new, tree) != .eq) return ReplaceError.NotEqual;
415
416 if (old.getParent()) |parent| {
417 parent.setChild(new, parent.left == old);
418 } else
419 tree.root = new;
420
421 if (old.left) |left|
422 left.setParent(new);
423 if (old.right) |right|
424 right.setParent(new);
425
426 new.* = old.*;
427 }
428
429 pub fn init(f: fn (*Node, *Node, *Tree) Order) Tree {
430 return Tree{
431 .root = null,
432 .compareFn = f,
433 };
434 }
435};
436
437fn rotateLeft(node: *Node, tree: *Tree) void {
438 var p: *Node = node;
439 var q: *Node = node.right orelse unreachable;
440 var parent: *Node = undefined;
441
442 if (!p.isRoot()) {
443 parent = p.getParent().?;
444 if (parent.left == p) {
445 parent.left = q;
446 } else {
447 parent.right = q;
448 }
449 q.setParent(parent);
450 } else {
451 tree.root = q;
452 q.setParent(null);
453 }
454 p.setParent(q);
455
456 p.right = q.left;
457 if (p.right) |right| {
458 right.setParent(p);
459 }
460 q.left = p;
461}
462
463fn rotateRight(node: *Node, tree: *Tree) void {
464 var p: *Node = node;
465 var q: *Node = node.left orelse unreachable;
466 var parent: *Node = undefined;
467
468 if (!p.isRoot()) {
469 parent = p.getParent().?;
470 if (parent.left == p) {
471 parent.left = q;
472 } else {
473 parent.right = q;
474 }
475 q.setParent(parent);
476 } else {
477 tree.root = q;
478 q.setParent(null);
479 }
480 p.setParent(q);
481
482 p.left = q.right;
483 if (p.left) |left| {
484 left.setParent(p);
485 }
486 q.right = p;
487}
488
489fn doLookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node {
490 var maybe_node: ?*Node = tree.root;
491
492 pparent.* = null;
493 is_left.* = false;
494
495 while (maybe_node) |node| {
496 const res = tree.compareFn(node, key, tree);
497 if (res == .eq) {
498 return node;
499 }
500 pparent.* = node;
501 switch (res) {
502 .gt => {
503 is_left.* = true;
504 maybe_node = node.left;
505 },
506 .lt => {
507 is_left.* = false;
508 maybe_node = node.right;
509 },
510 .eq => unreachable, // handled above
511 }
512 }
513 return null;
514}
515
516const testNumber = struct {
517 node: Node,
518 value: usize,
519};
520
521fn testGetNumber(node: *Node) *testNumber {
522 return @fieldParentPtr(testNumber, "node", node);
523}
524
525fn testCompare(l: *Node, r: *Node, contextIgnored: *Tree) Order {
526 var left = testGetNumber(l);
527 var right = testGetNumber(r);
528
529 if (left.value < right.value) {
530 return .lt;
531 } else if (left.value == right.value) {
532 return .eq;
533 } else if (left.value > right.value) {
534 return .gt;
535 }
536 unreachable;
537}
538
539fn testCompareReverse(l: *Node, r: *Node, contextIgnored: *Tree) Order {
540 return testCompare(r, l, contextIgnored);
541}
542
543test "rb" {
544 if (@import("builtin").arch == .aarch64) {
545 // TODO https://github.com/ziglang/zig/issues/3288
546 return error.SkipZigTest;
547 }
548
549 var tree = Tree.init(testCompare);
550 var ns: [10]testNumber = undefined;
551 ns[0].value = 42;
552 ns[1].value = 41;
553 ns[2].value = 40;
554 ns[3].value = 39;
555 ns[4].value = 38;
556 ns[5].value = 39;
557 ns[6].value = 3453;
558 ns[7].value = 32345;
559 ns[8].value = 392345;
560 ns[9].value = 4;
561
562 var dup: testNumber = undefined;
563 dup.value = 32345;
564
565 _ = tree.insert(&ns[1].node);
566 _ = tree.insert(&ns[2].node);
567 _ = tree.insert(&ns[3].node);
568 _ = tree.insert(&ns[4].node);
569 _ = tree.insert(&ns[5].node);
570 _ = tree.insert(&ns[6].node);
571 _ = tree.insert(&ns[7].node);
572 _ = tree.insert(&ns[8].node);
573 _ = tree.insert(&ns[9].node);
574 tree.remove(&ns[3].node);
575 testing.expect(tree.insert(&dup.node) == &ns[7].node);
576 try tree.replace(&ns[7].node, &dup.node);
577
578 var num: *testNumber = undefined;
579 num = testGetNumber(tree.first().?);
580 while (num.node.next() != null) {
581 testing.expect(testGetNumber(num.node.next().?).value > num.value);
582 num = testGetNumber(num.node.next().?);
583 }
584}
585
586test "inserting and looking up" {
587 var tree = Tree.init(testCompare);
588 var number: testNumber = undefined;
589 number.value = 1000;
590 _ = tree.insert(&number.node);
591 var dup: testNumber = undefined;
592 //Assert that tuples with identical value fields finds the same pointer
593 dup.value = 1000;
594 assert(tree.lookup(&dup.node) == &number.node);
595 //Assert that tuples with identical values do not clobber when inserted.
596 _ = tree.insert(&dup.node);
597 assert(tree.lookup(&dup.node) == &number.node);
598 assert(tree.lookup(&number.node) != &dup.node);
599 assert(testGetNumber(tree.lookup(&dup.node).?).value == testGetNumber(&dup.node).value);
600 //Assert that if looking for a non-existing value, return null.
601 var non_existing_value: testNumber = undefined;
602 non_existing_value.value = 1234;
603 assert(tree.lookup(&non_existing_value.node) == null);
604}
605
606test "multiple inserts, followed by calling first and last" {
607 if (@import("builtin").arch == .aarch64) {
608 // TODO https://github.com/ziglang/zig/issues/3288
609 return error.SkipZigTest;
610 }
611 var tree = Tree.init(testCompare);
612 var zeroth: testNumber = undefined;
613 zeroth.value = 0;
614 var first: testNumber = undefined;
615 first.value = 1;
616 var second: testNumber = undefined;
617 second.value = 2;
618 var third: testNumber = undefined;
619 third.value = 3;
620 _ = tree.insert(&zeroth.node);
621 _ = tree.insert(&first.node);
622 _ = tree.insert(&second.node);
623 _ = tree.insert(&third.node);
624 assert(testGetNumber(tree.first().?).value == 0);
625 assert(testGetNumber(tree.last().?).value == 3);
626 var lookupNode: testNumber = undefined;
627 lookupNode.value = 3;
628 assert(tree.lookup(&lookupNode.node) == &third.node);
629 tree.sort(testCompareReverse) catch unreachable;
630 assert(testGetNumber(tree.first().?).value == 3);
631 assert(testGetNumber(tree.last().?).value == 0);
632 assert(tree.lookup(&lookupNode.node) == &third.node);
633}
lib/std/std.zig-1
...@@ -78,7 +78,6 @@ pub const packed_int_array = @import("packed_int_array.zig");...@@ -78,7 +78,6 @@ pub const packed_int_array = @import("packed_int_array.zig");
78pub const pdb = @import("pdb.zig");78pub const pdb = @import("pdb.zig");
79pub const process = @import("process.zig");79pub const process = @import("process.zig");
80pub const rand = @import("rand.zig");80pub const rand = @import("rand.zig");
81pub const rb = @import("rb.zig");
82pub const sort = @import("sort.zig");81pub const sort = @import("sort.zig");
83pub const ascii = @import("ascii.zig");82pub const ascii = @import("ascii.zig");
84pub const testing = @import("testing.zig");83pub const testing = @import("testing.zig");