1 module d.gc.rbtree;
2 
3 struct Node(N, string NodeName = "node") {
4 private:
5 	alias Link = .Link!(N, NodeName);
6 	Link[2] childs;
7 
8 	@property
9 	ref Link left() {
10 		return childs[0];
11 	}
12 
13 	@property
14 	ref Link right() {
15 		return childs[1];
16 	}
17 }
18 
19 // TODO: when supported add default to use opCmp for compare.
20 struct RBTree(N, alias compare, string NodeName = "node") {
21 private:
22 	N* root;
23 
24 	alias Link = .Link!(N, NodeName);
25 	alias Node = .Node!(N, NodeName);
26 	alias Path = .Path!(N, NodeName);
27 
28 	static ref Node nodeData(N* n) {
29 		return .nodeData!NodeName(n);
30 	}
31 
32 public:
33 	@property
34 	bool empty() {
35 		return root is null;
36 	}
37 
38 	N* find(N* test) {
39 		auto n = root;
40 
41 		while (n !is null) {
42 			auto cmp = compare(test, n);
43 			// We have a perfect match.
44 			if (cmp == 0) {
45 				return n;
46 			}
47 
48 			n = nodeData(n).childs[cmp > 0].node;
49 		}
50 
51 		return null;
52 	}
53 
54 	/**
55 	 * Find the smallest item that is larger than the test.
56 	 */
57 	N* bestfit(N* test) {
58 		auto n = root;
59 
60 		N* bf = null;
61 		while (n !is null) {
62 			auto cmp = compare(test, n);
63 			// We have a perfect match.
64 			if (cmp == 0) {
65 				return n;
66 			}
67 
68 			if (cmp < 0) {
69 				bf = n;
70 			}
71 
72 			n = nodeData(n).childs[cmp > 0].node;
73 		}
74 
75 		return bf;
76 	}
77 
78 	void insert(N* n) {
79 		// rbtree's depth is ln(n) which is at most 8 * size_t.sizeof.
80 		// Each tree node that N.sizeof size, so we can remove ln(N.sizeof).
81 		// But a branch can be at most 2* longer than the shortest one.
82 		import d.gc.util;
83 		Path[16 * size_t.sizeof - lg2floor(N.sizeof)] path = void;
84 		auto stackp = &path[0]; // TODO: use .ptr when available.
85 
86 		// Let's make sure this is a child node.
87 		nodeData(n).left = Link(null, Color.Black);
88 		nodeData(n).right = Link(null, Color.Black);
89 
90 		// Root is always black.
91 		auto link = Link(root, Color.Black);
92 		while (!link.isLeaf()) {
93 			auto diff = compare(n, link.node);
94 			assert(diff != 0);
95 
96 			auto cmp = diff > 0;
97 			*stackp = Path(link, cmp);
98 
99 			stackp++;
100 			link = link.childs[cmp];
101 		}
102 
103 		// The tree only has a root.
104 		if (stackp is &path[0]) {
105 			root = n;
106 			return;
107 		}
108 
109 		// Inserted node is always red.
110 		*stackp = Path(Link(n, Color.Red), false);
111 		assert(stackp.isRed());
112 
113 		// Now we found an insertion point, let's fix the tree.
114 		for (stackp--; stackp !is (&path[0] - 1); stackp--) {
115 			link = stackp.link;
116 			auto cmp = stackp.cmp;
117 
118 			auto child = link.childs[cmp] = stackp[1].link;
119 			if (child.isBlack()) {
120 				break;
121 			}
122 
123 			if (link.isRed()) {
124 				continue;
125 			}
126 
127 			auto sibling = link.childs[!cmp];
128 			if (sibling.isRed()) {
129 				assert(link.isBlack());
130 				assert(link.left.isRed() && link.right.isRed());
131 
132 				/**
133 				 *     B          Br
134 				 *    / \   =>   / \
135 				 *   R   R      Rb  Rb
136 				 */
137 				link.left = link.left.getAs(Color.Black);
138 				link.right = link.right.getAs(Color.Black);
139 				*stackp = stackp.getWithLink(link.getAs(Color.Red));
140 				continue;
141 			}
142 
143 			auto line = child.childs[cmp];
144 			if (line.isBlack()) {
145 				if (child.childs[!cmp].isBlack()) {
146 					// Our red child has 2 black child, we are good.
147 					break;
148 				}
149 
150 				/**
151 				 * We transform The zigzag case into the line case.
152 				 *
153 				 *                 B
154 				 *     B          / \
155 				 *    / \        B   R
156 				 *   B   R   =>       \
157 				 *      / \            R
158 				 *     R   B            \
159 				 *                       B
160 				 */
161 				assert(child.childs[!cmp].isRed());
162 				child = child.rotate(cmp);
163 			}
164 
165 			/**
166 			 *     B            Rb
167 			 *    / \          / \
168 			 *   B   R   =>   Br  R
169 			 *      / \      / \
170 			 *     B   R    B   B
171 			 */
172 			link.childs[cmp] = child.getAs(Color.Black);
173 			link = link.getAs(Color.Red);
174 			*stackp = stackp.getWithLink(link.rotate(!cmp));
175 		}
176 
177 		root = path[0].node;
178 	}
179 
180 	void remove(N* n) {
181 		assert(n !is null);
182 		auto removed = extract(n);
183 		assert(n is removed);
184 	}
185 
186 	N* extractAny() {
187 		return extract(root);
188 	}
189 
190 	N* extract(N* n) {
191 		// rbtree's depth is ln(n) which is at most 8 * size_t.sizeof.
192 		// Each tree node that N.sizeof size, so we can remove ln(N.sizeof).
193 		// But a branch can be at most 2* longer than the shortest one.
194 		import d.gc.util;
195 		Path[16 * size_t.sizeof - lg2floor(N.sizeof)] path = void;
196 		auto stackp = &path[0]; // TODO: use .ptr when available.
197 
198 		// Root is always black.
199 		auto link = Link(root, Color.Black);
200 		auto rn = root;
201 		while (rn !is null) {
202 			auto diff = compare(n, rn);
203 
204 			// We found our node !
205 			if (diff == 0) {
206 				break;
207 			}
208 
209 			auto cmp = diff > 0;
210 			*stackp = Path(link, cmp);
211 
212 			stackp++;
213 			link = link.childs[cmp];
214 			rn = link.node;
215 		}
216 
217 		if (rn is null) {
218 			return null;
219 		}
220 
221 		// Now we look for a succesor.
222 		*stackp = Path(link, true);
223 		auto removep = stackp;
224 		auto removed = link;
225 
226 		/**
227 		 * We find a replacing node by going one to the right
228 		 * and then as far as possible to the left. That way
229 		 * we get the next node in the tree and its ordering
230 		 * will be valid.
231 		 */
232 		link = removed.right;
233 		while (!link.isLeaf()) {
234 			stackp++;
235 			*stackp = Path(link, false);
236 			link = link.left;
237 		}
238 
239 		link = stackp.link;
240 
241 		if (stackp is removep) {
242 			// The node we remove has no successor.
243 			*stackp = stackp.getWithLink(link.left);
244 		} else {
245 			/**
246 			 * Swap node to be deleted with its successor
247 			 * but not the color, so we keep tree color
248 			 * constraint in place.
249 			 */
250 			auto rcolor = removed.color;
251 
252 			removed = removed.getAs(link.color);
253 			*stackp = stackp.getWithLink(link.right);
254 
255 			link = link.getAs(rcolor);
256 			link.left = removed.left;
257 
258 			/**
259 			 * If the successor is the right child of the
260 			 * node we want to delete, this is incorrect.
261 			 * However, it doesn't matter, as it is going
262 			 * to be fixed during pruning.
263 			 */
264 			link.right = removed.right;
265 
266 			// NB: We don't clean the node to be removed.
267 			// We simply splice it out.
268 			*removep = removep.getWithLink(link);
269 		}
270 
271 		// If we are not at the root, fix the parent.
272 		if (removep !is &path[0]) {
273 			removep[-1].childs[removep[-1].cmp] = removep.link;
274 		}
275 
276 		// Removing red node require no fixup.
277 		if (removed.isRed()) {
278 			stackp[-1].childs[stackp[-1].cmp] = Link(null, Color.Black);
279 
280 			// Update root and exit
281 			root = path[0].node;
282 			return rn;
283 		}
284 
285 		for (stackp--; stackp !is (&path[0] - 1); stackp--) {
286 			link = stackp.link;
287 			auto cmp = stackp.cmp;
288 
289 			auto child = stackp[1].link;
290 			if (child.isRed()) {
291 				// If the double black is on a red node, recolor.
292 				link.childs[cmp] = child.getAs(Color.Black);
293 				break;
294 			}
295 
296 			link.childs[cmp] = child;
297 
298 			/**
299 			 * b = changed to black
300 			 * r = changed to red
301 			 * // = double black path
302 			 *
303 			 * We rotate and recolor to find ourselves in a case
304 			 * where sibling is black one level below. Because the
305 			 * new root will be red, zigzag case will bubble up
306 			 * with a red node, which is going to terminate.
307 			 *
308 			 *            Rb
309 			 *   B         \
310 			 *  / \\   =>   Br  <- new link
311 			 * R    B        \\
312 			 *                 B
313 			 */
314 			auto sibling = link.childs[!cmp];
315 			if (sibling.isRed()) {
316 				assert(link.isBlack());
317 
318 				link = link.getAs(Color.Red);
319 				auto parent = link.rotate(cmp);
320 				*stackp = stackp.getWithLink(parent.getAs(Color.Black));
321 
322 				// As we are going down one level, make sure we fix the parent.
323 				if (stackp !is &path[0]) {
324 					stackp[-1].childs[stackp[-1].cmp] = stackp.link;
325 				}
326 
327 				stackp++;
328 
329 				// Fake landing one level below.
330 				// NB: We don't need to fake cmp.
331 				*stackp = stackp.getWithLink(link);
332 				sibling = link.childs[!cmp];
333 			}
334 
335 			auto line = sibling.childs[!cmp];
336 			if (line.isRed()) {
337 				goto Line;
338 			}
339 
340 			if (sibling.childs[cmp].isBlack()) {
341 				/**
342 				 * b = changed to black
343 				 * r = changed to red
344 				 * // = double black path
345 				 *
346 				 * We recolor the sibling to push the double
347 				 * black one level up.
348 				 *
349 				 *     X           (X)
350 				 *    / \\         / \
351 				 *   B    B  =>   Br  B
352 				 *  / \          / \
353 				 * B   B        B   B
354 				 */
355 				link.childs[!cmp] = sibling.getAs(Color.Red);
356 				continue;
357 			}
358 
359 			/**
360 			 * b = changed to black
361 			 * r = changed to red
362 			 * // = double black path
363 			 *
364 			 * We rotate the zigzag to be in the line case.
365 			 *
366 			 *                   X
367 			 *     X            / \\
368 			 *    / \\         Rb   B
369 			 *   B    B  =>   /
370 			 *  / \          Br
371 			 * B   R        /
372 			 *             B
373 			 */
374 			line = sibling.getAs(Color.Red);
375 			sibling = line.rotate(!cmp);
376 			sibling = sibling.getAs(Color.Black);
377 			link.childs[!cmp] = sibling;
378 
379 		Line:
380 			/**
381 			 * b = changed to black
382 			 * x = changed to x's original color
383 			 * // = double black path
384 			 *
385 			 *     X           Bx
386 			 *    / \\        / \
387 			 *   B    B  =>  Rb  Xb
388 			 *  / \             / \
389 			 * R   Y           Y   B
390 			 */
391 			auto l = link.getAs(Color.Black);
392 			l = l.rotate(cmp);
393 			l.childs[!cmp] = line.getAs(Color.Black);
394 
395 			// If we are the root, we are done.
396 			if (stackp is &path[0]) {
397 				root = l.node;
398 				return rn;
399 			}
400 
401 			stackp[-1].childs[stackp[-1].cmp] = l.getAs(link.color);
402 			break;
403 		}
404 
405 		// Update root and exit
406 		root = path[0].node;
407 		return rn;
408 	}
409 
410 	void dump() {
411 		rb_print_tree!NodeName(root);
412 	}
413 }
414 
415 private:
416 //+
417 void rb_print_tree(string NodeName, N)(N* root) {
418 	Debug!(N, NodeName).print_tree(Link!(N, NodeName)(root, Color.Black), 0);
419 }
420 
421 // +/
422 
423 private:
424 
425 ref Node!(N, NodeName) nodeData(string NodeName, N)(N* n) {
426 	mixin("return n." ~ NodeName ~ ";");
427 }
428 
429 struct Link(N, string NodeName) {
430 	alias Node = .Node!(N, NodeName);
431 
432 	// This is effectively a tagged pointer, don't use as this.
433 	N* _child;
434 
435 	this(N* n, Color c) {
436 		assert(c == Color.Black || n !is null);
437 		_child = cast(N*) ((cast(size_t) n) | c);
438 	}
439 
440 	auto getAs(Color c) {
441 		assert(c == Color.Black || node !is null);
442 		return Link(node, c);
443 	}
444 
445 	@property
446 	N* node() {
447 		return cast(N*) ((cast(size_t) _child) & ~0x01);
448 	}
449 
450 	@property
451 	ref Node nodeData() {
452 		return .nodeData!NodeName(node);
453 	}
454 
455 	@property
456 	ref Link left() {
457 		return nodeData.left;
458 	}
459 
460 	@property
461 	ref Link right() {
462 		return nodeData.right;
463 	}
464 
465 	@property
466 	ref Link[2] childs() {
467 		return nodeData.childs;
468 	}
469 
470 	@property
471 	Color color() const {
472 		return cast(Color) ((cast(size_t) _child) & 0x01);
473 	}
474 
475 	bool isRed() const {
476 		return color == Color.Red;
477 	}
478 
479 	bool isBlack() const {
480 		return color == Color.Black;
481 	}
482 
483 	bool isLeaf() const {
484 		return _child is null;
485 	}
486 
487 	// Rotate the tree and return the new root.
488 	// The tree turn clockwize if cmp is true,
489 	// counterclockwize if it is false.
490 	auto rotate(bool cmp) {
491 		auto x = childs[!cmp];
492 		childs[!cmp] = x.childs[cmp];
493 		x.childs[cmp] = this;
494 		return x;
495 	}
496 
497 	// Rotate the tree and return the new root.
498 	auto rotateLeft() {
499 		auto r = right;
500 		right = r.left;
501 		r.left = this;
502 		return r;
503 	}
504 
505 	// Rotate the tree and return the new root.
506 	auto rotateRight() {
507 		auto l = left;
508 		left = l.right;
509 		l.right = this;
510 		return l;
511 	}
512 }
513 
514 enum Color : bool {
515 	Black = false,
516 	Red = true,
517 }
518 
519 struct Path(N, string NodeName) {
520 	alias Link = .Link!(N, NodeName);
521 	alias Node = .Node!(N, NodeName);
522 
523 	// This is effectively a tagged pointer, don't use as this.
524 	N* _child;
525 
526 	this(Link l, bool c) {
527 		_child = cast(N*) ((cast(size_t) l._child) | (c << 1));
528 	}
529 
530 	auto getWithLink(Link l) {
531 		return Path(l, cmp);
532 	}
533 
534 	auto getWithCmp(bool c) {
535 		return Path(link, c);
536 	}
537 
538 	@property
539 	Link link() {
540 		return Link(node, color);
541 	}
542 
543 	@property
544 	bool cmp() const {
545 		return !!((cast(size_t) _child) & 0x02);
546 	}
547 
548 	@property
549 	N* node() {
550 		return cast(N*) ((cast(size_t) _child) & ~0x03);
551 	}
552 
553 	@property
554 	ref Node nodeData() {
555 		return .nodeData!NodeName(node);
556 	}
557 
558 	@property
559 	ref Link left() {
560 		return nodeData.left;
561 	}
562 
563 	@property
564 	ref Link right() {
565 		return nodeData.right;
566 	}
567 
568 	@property
569 	ref Link[2] childs() {
570 		return nodeData.childs;
571 	}
572 
573 	@property
574 	Color color() const {
575 		return cast(Color) ((cast(size_t) _child) & 0x01);
576 	}
577 
578 	bool isRed() const {
579 		return color == Color.Red;
580 	}
581 
582 	bool isBlack() const {
583 		return color == Color.Black;
584 	}
585 }
586 
587 //+
588 template Debug(N, string NodeName) {
589 	void print_tree(Link!(N, NodeName) root, uint depth) {
590 		foreach (i; 0 .. depth) {
591 			import core.stdc.stdio;
592 			printf("\t".ptr);
593 		}
594 
595 		if (root.isBlack()) {
596 			import core.stdc.stdio;
597 			printf("B %p\n".ptr, root.node);
598 		} else {
599 			assert(root.isRed());
600 
601 			import core.stdc.stdio;
602 			printf("R %p\n".ptr, root.node);
603 		}
604 
605 		if (!root.isLeaf()) {
606 			print_tree(root.right, depth + 1);
607 			print_tree(root.left, depth + 1);
608 		}
609 	}
610 }
611 
612 // +/
613 
614 unittest rbtree {
615 	struct Stuff {
616 		Node!Stuff node;
617 		ulong value;
618 	}
619 
620 	static ptrdiff_t stuffCmp(Stuff* lhs, Stuff* rhs) {
621 		return (lhs.value == rhs.value)
622 			? (cast(ptrdiff_t) lhs) - (cast(ptrdiff_t) rhs)
623 			: (lhs.value - rhs.value);
624 	}
625 
626 	static ulong prand_next(ulong prev) {
627 		return (prev * 31415821 + 1) % 100_000_000;
628 	}
629 
630 	enum Items = 174762;
631 	Stuff[32][Items]* nodes;
632 
633 	Stuff* get_node(ulong tree, ulong node) {
634 		assert(node < 174762 && tree < 32);
635 		return &nodes[0][node][tree];
636 	}
637 
638 	// 128 Mb to ramble through.
639 	nodes = cast(Stuff[32][Items]*) __sd_gc_tl_malloc(128 * 1024 * 1024);
640 	ulong prand = 365307287;
641 
642 	foreach (i; 0 .. Items) {
643 		foreach (t; 0 .. 32) {
644 			prand = prand_next(prand);
645 			get_node(t, i).value = prand;
646 		}
647 	}
648 
649 	RBTree!(Stuff, stuffCmp)[32] trees;
650 
651 	foreach (i; 0 .. Items) {
652 		foreach (t; 0 .. 32) {
653 			trees[t].insert(get_node(t, i));
654 			// rb_print_tree(trees[t]);
655 		}
656 	}
657 
658 	foreach (i; 0 .. Items) {
659 		foreach (t; 0 .. 32) {
660 			trees[t].remove(get_node(t, i));
661 			// rb_print_tree(trees[t]);
662 		}
663 	}
664 }