1 /**
2  * T-Tree.
3  * Copyright: © 2014 Economic Modeling Specialists, Intl.
4  * Authors: Brian Schott
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6  */
7 
8 module containers.ttree;
9 
10 //version(graphviz_debugging) import std.stdio;
11 
12 /**
13  * Implements a binary search tree with multiple items per tree node.
14  *
15  * T-tree Nodes are (by default) sized to fit within a 64-byte
16  * cache line. The number of items stored per node can be read from the
17  * nodeCapacity field. Each node has 0, 1, or 2 children. Each node has between
18  * 1 and $(B nodeCapacity) items, or it has $(B nodeCapacity) items and 0 or
19  * more children.
20  * Params:
21  *     T = the element type
22  *     allowDuplicates = if true, duplicate values will be allowed in the tree
23  *     less = the comparitor function to use
24  *     cacheLineSize = Nodes will be sized to fit within this number of bytes.
25  *     supportGC = true if the container should support holding references to
26  *         GC-allocated memory.
27  * See_also: $(LINK http://en.wikipedia.org/wiki/T-tree)
28  */
29 struct TTree(T, bool allowDuplicates = false, alias less = "a < b",
30 	bool supportGC = true, size_t cacheLineSize = 64)
31 {
32 	this(this) @disable;
33 
34 	~this()
35 	{
36 		if (root is null)
37 			return;
38 		deallocateNode(root);
39 	}
40 
41 	private import containers.internal.storage_type : ContainerStorageType;
42 
43 	private enum size_t nodeCapacity = fatNodeCapacity!(T.sizeof, 3, size_t, cacheLineSize);
44 	static assert (nodeCapacity <= (size_t.sizeof * 4), "cannot fit height info and registry in size_t");
45 
46 	debug(EMSI_CONTAINERS) invariant()
47 	{
48 		assert (root is null || _length != 0);
49 	}
50 
51 
52 	alias Value = ContainerStorageType!T;
53 
54 	/// $(B tree ~= item) operator overload.
55 	void opOpAssign(string op)(T value) if (op == "~")
56 	{
57 		insert(value);
58 	}
59 
60 	/**
61 	 * Returns true if any values were added. This is not a stable insert. You
62 	 * will get strange results if you insert into a tree while iterating over
63 	 * it.
64 	 */
65 	bool insert(T value)
66 	{
67 		if (root is null)
68 		{
69 			root = allocateNode(value, null);
70 			++_length;
71 			return true;
72 		}
73 		immutable bool r = root.insert(value, root);
74 		if (r)
75 			++_length;
76 		return r;
77 	}
78 
79 	/// ditto
80 	bool insert(R)(R r)
81 	{
82 		bool retVal = false;
83 		while (!r.empty)
84 		{
85 			retVal = insert(r.front()) || retVal;
86 			r.popFront();
87 		}
88 		return retVal;
89 	}
90 
91 	/// ditto
92 	bool insert(T[] values)
93 	{
94 		bool retVal = false;
95 		foreach (ref v; values)
96 			retVal = insert(v) || retVal;
97 		return retVal;
98 	}
99 
100 	/**
101 	 * Removes a value from the tree.
102 	 * Params:
103 	 *     value = a value equal to the one to be removed
104 	 *     cleanup = a function that should be run on the removed item
105 	 * Retuns: true if any value was removed
106 	 */
107 	bool remove(T value, void delegate(T) cleanup = null)
108 	{
109 		bool removed = root !is null && root.remove(cast(Value) value, root, cleanup);
110 		if (removed)
111 			--_length;
112 		if (_length == 0)
113 			deallocateNode(root);
114 		return removed;
115 	}
116 
117 	/**
118 	 * Returns true if the tree _conains the given value
119 	 */
120 	bool contains(T value) const
121 	{
122 		return root !is null && root.contains(value);
123 	}
124 
125 	/**
126 	 * Returns: the number of elements in the tree.
127 	 */
128 	size_t length() const nothrow pure @property
129 	{
130 		return _length;
131 	}
132 
133 	/**
134 	 * Returns: true if the tree is empty.
135 	 */
136 	bool empty() const nothrow pure @property
137 	{
138 		return _length == 0;
139 	}
140 
141 	version(graphviz_debugging) void print(File f)
142 	{
143 		f.writeln("digraph g {");
144 		root.print(f);
145 		f.writeln("}");
146 	}
147 
148 	/**
149 	 * Returns: a range over the tree. Do not insert into the tree while
150 	 * iterating because you may iterate over the same value multiple times.
151 	 */
152 	Range opSlice() const
153 	{
154 		return Range(root, Range.type.all, T.init);
155 	}
156 
157 	/**
158 	 * Returns: a range of elements which are less than value.
159 	 */
160 	Range lowerBound(inout T value) const
161 	{
162 		return Range(root, Range.Type.lower, value);
163 	}
164 
165 	/**
166 	 * Returns: a range of elements which are equivalent (though not necessarily
167 	 * equal) to value.
168 	 */
169 	Range equalRange(inout T value) const
170 	{
171 		return Range(root, Range.Type.equal, value);
172 	}
173 
174 	/**
175 	 * Returns: a range of elements which are greater than value.
176 	 */
177 	Range upperBound(inout T value) const
178 	{
179 		return Range(root, Range.Type.upper, value);
180 	}
181 
182 	/**
183 	 * Tree range
184 	 */
185 	static struct Range
186 	{
187 		@disable this();
188 
189 		/**
190 		 * Standard range operations
191 		 */
192 		T front() const @property
193 		in
194 		{
195 			assert (!empty);
196 		}
197 		body
198 		{
199 			return cast(typeof(return)) current.values[index];
200 		}
201 
202 		/// ditto
203 		bool empty() const nothrow pure @property
204 		{
205 			return current is null;
206 		}
207 
208 		/// ditto
209 		void popFront()
210 		{
211 			_popFront();
212 			if (current is null)
213 				return;
214 			final switch (type)
215 			{
216 			case Type.upper:
217 			case Type.all: break;
218 			case Type.equal:
219 				if (_less(front(), val) || _less(val, front()))
220 					current = null;
221 				break;
222 			case Type.lower:
223 				if (!_less(front(), val))
224 					current = null;
225 				break;
226 			}
227 		}
228 
229 		/// ditto
230 		Range save() @property
231 		{
232 			return this;
233 		}
234 
235 	private:
236 
237 		enum Type : ubyte {all, lower, equal, upper}
238 
239 		void currentToLeftmost()
240 		{
241 			if (current is null)
242 				return;
243 			while (current.left !is null)
244 				current = current.left;
245 		}
246 
247 		void currentToLeastContaining(inout T val)
248 		{
249 			while (current !is null)
250 			{
251 				if (_less(val, current.values[0]))
252 					current = current.left;
253 				else if (current.isFull)
254 				{
255 					if (_less(current.values[$ - 1], cast(Value) val))
256 						current = current.right;
257 					else
258 						break;
259 				}
260 				else
261 					break;
262 			}
263 		}
264 
265 		this(inout(Node)* n, Type type, inout T val)
266 		{
267 			current = n;
268 			this.type = type;
269 			this.val = val;
270 			final switch(type)
271 			{
272 			case Type.all:
273 				currentToLeftmost();
274 				break;
275 			case Type.lower:
276 				currentToLeftmost();
277 				if (_less(val, front()))
278 					current = null;
279 				break;
280 			case Type.equal:
281 				currentToLeastContaining(val);
282 				while (current !is null && _less(front(), val))
283 					_popFront();
284 				if (current is null || _less(front(), val) || _less(val, front()))
285 					current = null;
286 				break;
287 			case Type.upper:
288 				currentToLeastContaining(val);
289 				while (current !is null && !_less(val, front()))
290 					_popFront();
291 				break;
292 			}
293 		}
294 
295 		void _popFront()
296 		in
297 		{
298 			assert (!empty);
299 		}
300 		body
301 		{
302 			index++;
303 			if (index >= nodeCapacity || current.isFree(index))
304 			{
305 				index = 0;
306 				if (current.right !is null)
307 				{
308 					current = current.right;
309 					while (current.left !is null)
310 						current = current.left;
311 				}
312 				else if (current.parent is null)
313 					current = null;
314 				else if (current.parent.left is current)
315 					current = current.parent;
316 				else
317 				{
318 					while (current.parent.right is current)
319 					{
320 						current = current.parent;
321 						if (current.parent is null)
322 						{
323 							current = null;
324 							return;
325 						}
326 					}
327 					current = current.parent;
328 				}
329 			}
330 		}
331 
332 		size_t index;
333 		const(Node)* current;
334 		Type type;
335 		const T val;
336 	}
337 
338 private:
339 
340 	import containers.internal.node : fatNodeCapacity, fullBits, shouldAddGCRange, shouldNullSlot;
341     import std.algorithm : sort;
342 	import std.allocator: Mallocator, allocate, deallocate;
343 	import std.functional: binaryFun;
344 	import std.traits: isPointer, PointerTarget;
345 
346 	// If we're storing a struct that defines opCmp, don't compare pointers as
347 	// that is almost certainly not what the user intended.
348 	static if (is(typeof(less) == string ) && less == "a < b" && isPointer!T && __traits(hasMember, PointerTarget!T, "opCmp"))
349 		alias _less = binaryFun!"a.opCmp(*b) < 0";
350 	else
351 		alias _less = binaryFun!less;
352 
353 	static Node* allocateNode(ref T value, Node* parent)
354 	out (result)
355 	{
356 		assert (result.left is null);
357 		assert (result.right is null);
358 	}
359 	body
360 	{
361 		import core.memory : GC;
362 		Node* n = allocate!Node(Mallocator.it);
363 		n.parent = parent;
364 		n.markUsed(0);
365 		n.values[0] = cast(Value) value;
366 		static if (supportGC && shouldAddGCRange!T)
367 			GC.addRange(n, Node.sizeof);
368 		return n;
369 	}
370 
371 	static void deallocateNode(ref Node* n)
372 	in
373 	{
374 		assert (n !is null);
375 	}
376 	body
377 	{
378 		import core.memory : GC;
379 		static if (supportGC && shouldAddGCRange!T)
380 			GC.removeRange(n);
381 		typeid(Node).destroy(n);
382 		deallocate!Node(Mallocator.it, n);
383 		n = null;
384 	}
385 
386 	static assert (nodeCapacity <= (typeof(Node.registry).sizeof * 8));
387 	static assert (Node.sizeof <= cacheLineSize);
388 	static struct Node
389 	{
390 		~this()
391 		{
392 			if (left !is null)
393 				deallocateNode(left);
394 			if (right !is null)
395 				deallocateNode(right);
396 		}
397 
398 		private size_t nextAvailableIndex() const nothrow pure
399 		{
400 			import core.bitop : bsf;
401 			immutable i = ~(registry & fullBits!nodeCapacity);
402 			assert(i != 0);
403 			return bsf(i);
404 		}
405 
406 		private void markUsed(size_t index) pure nothrow
407 		{
408 			registry |= (1 << index);
409 		}
410 
411 		private void markUnused(size_t index) pure nothrow
412 		{
413 			registry &= ~(1 << index);
414 			static if (shouldNullSlot!T)
415 				values[index] = null;
416 		}
417 
418 		private bool isFree(size_t index) const pure nothrow
419 		{
420 			return (registry & (1 << index)) == 0;
421 		}
422 
423 		private bool isFull() const pure nothrow
424 		{
425 			return (registry & fullBits!nodeCapacity) == fullBits!nodeCapacity;
426 		}
427 
428 		private bool isEmpty() const pure nothrow
429 		{
430 			return (registry & fullBits!nodeCapacity) == 0;
431 		}
432 
433 		bool contains(Value value) const
434 		{
435 			import std.range;
436 			size_t i = nextAvailableIndex();
437 			if (_less(value, cast(Value) values[0]))
438 				return left !is null && left.contains(value);
439 			if (_less(values[i - 1], value))
440 				return right !is null && right.contains(value);
441 			return !assumeSorted!_less(values[0 .. i]).equalRange(value).empty;
442 		}
443 
444 		size_t calcHeight() nothrow pure
445 		{
446 			size_t l = left !is null ? left.height() : 0;
447 			size_t r = right !is null ? right.height() : 0;
448 			size_t h = 1 + (l > r ? l : r);
449 			registry &= fullBits!nodeCapacity;
450 			registry |= (h << (size_t.sizeof * 4));
451 			return h;
452 		}
453 
454 		size_t height() const nothrow pure
455 		{
456 			return registry >>> (size_t.sizeof * 4);
457 		}
458 
459 		int imbalanced() const nothrow pure
460 		{
461 			if (right !is null
462 				&& ((left is null && right.height() > 1)
463 				|| (left !is null && right.height() > left.height() + 1)))
464 				return 1;
465 			if (left !is null
466 				&& ((right is null && left.height() > 1)
467 				|| (right !is null && left.height() > right.height() + 1)))
468 				return -1;
469 			return 0;
470 		}
471 
472 		bool insert(T value, ref Node* root)
473 		in
474 		{
475 			static if (isPointer!T || is (T == class))
476 				assert (value !is null);
477 		}
478 		body
479 		{
480 			import std.algorithm : sort;
481 			import std.range;
482 			if (!isFull())
483 			{
484 				immutable size_t index = nextAvailableIndex();
485 				static if (!allowDuplicates)
486 				{
487 					if (!assumeSorted!_less(values[0 .. index]).equalRange(
488 						cast(Value) value).empty)
489 					{
490 						return false;
491 					}
492 				}
493 				values[index] = cast(Value) value;
494 				markUsed(index);
495 				sort!_less(values[0 .. index + 1]);
496 				return true;
497 			}
498 			if (_less(value, values[0]))
499 			{
500 				if (left is null)
501 				{
502 					left = allocateNode(value, &this);
503 					calcHeight();
504 					return true;
505 				}
506 				bool b = left.insert(value, root);
507 				if (imbalanced() == -1)
508 					rotateRight(root);
509 				calcHeight();
510 				return b;
511 			}
512 			if (_less(values[$ - 1], cast(Value) value))
513 			{
514 				if (right is null)
515 				{
516 					right = allocateNode(value, &this);
517 					calcHeight();
518 					return true;
519 				}
520 				bool b = right.insert(value, root);
521 				if (imbalanced() == 1)
522 					rotateLeft(root);
523 				calcHeight();
524 				return b;
525 			}
526 			static if (!allowDuplicates)
527 				if (!assumeSorted!_less(values[]).equalRange(cast(Value) value).empty)
528 					return false;
529 			Value[nodeCapacity + 1] temp = void;
530 			temp[0 .. $ - 1] = values[];
531 			temp[$ - 1] = cast(Value) value;
532 			sort!_less(temp[]);
533 			if (right is null)
534 			{
535 				values[] = temp[0 .. $ - 1];
536 				right = allocateNode(temp[$ - 1], &this);
537 				return true;
538 			}
539 			if (left is null)
540 			{
541 				values[] = temp[1 .. $];
542 				left = allocateNode(temp[0], &this);
543 				return true;
544 			}
545 			if (right.height < left.height)
546 			{
547 				values[] = temp[0 .. $ - 1];
548 				bool b = right.insert(temp[$ - 1], root);
549 				if (imbalanced() == 1)
550 					rotateLeft(root);
551 				calcHeight();
552 				return b;
553 			}
554 			values[] = temp[1 .. $];
555 			bool b = left.insert(temp[0], root);
556 			if (imbalanced() == -1)
557 				rotateRight(root);
558 			calcHeight();
559 			return b;
560 		}
561 
562 		bool remove(Value value, ref Node* n, void delegate(T) cleanup = null)
563 		{
564 			import std.range;
565 			assert (!isEmpty());
566 			if (isFull() && _less(value, values[0]))
567 			{
568 				bool r = left !is null && left.remove(value, left, cleanup);
569 				if (left.isEmpty())
570 					deallocateNode(left);
571 				return r;
572 			}
573 			if (isFull() && _less(values[$ - 1], value))
574 			{
575 				bool r = right !is null && right.remove(value, right, cleanup);
576 				if (right.isEmpty())
577 					deallocateNode(right);
578 				return r;
579 			}
580 			size_t i = nextAvailableIndex();
581 			auto sv = assumeSorted!_less(values[0 .. i]);
582 			auto tri = sv.trisect(value);
583 			if (tri[1].length == 0)
584 				return false;
585 			// Clean up removed item
586 			if (cleanup !is null)
587 				cleanup(tri[1][0]);
588 			immutable size_t l = tri[0].length;
589 			if (right is null && left is null)
590 			{
591 				Value[nodeCapacity - 1] temp;
592 				temp[0 .. l] = values[0 .. l];
593 				temp[l .. $] = values[l + 1 .. $];
594 				values[0 .. $ - 1] = temp[];
595 				markUnused(nextAvailableIndex() - 1);
596 			}
597 			else if (right !is null)
598 			{
599 				Value[nodeCapacity - 1] temp;
600 				temp[0 .. l] = values[0 .. l];
601 				temp[l .. $] = values[l + 1 .. $];
602 				values[0 .. $ - 1] = temp[];
603 				values[$ - 1] = right.removeSmallest();
604 				if (right.isEmpty())
605 					deallocateNode(right);
606 			}
607 			else if (left !is null)
608 			{
609 				Value[nodeCapacity - 1] temp;
610 				temp[0 .. l] = values[0 .. l];
611 				temp[l .. $] = values[l + 1 .. $];
612 				values[1 .. $] = temp[];
613 				values[0] = left.removeLargest();
614 				if (left.isEmpty())
615 					deallocateNode(left);
616 			}
617 			return true;
618 		}
619 
620 		Value removeSmallest()
621 		in
622 		{
623 			assert (!isEmpty());
624 		}
625 		body
626 		{
627 			if (left is null && right is null)
628 			{
629 				Value r = values[0];
630 				Value[nodeCapacity - 1] temp = void;
631 				temp[] = values[1 .. $];
632 				values[0 .. $ - 1] = temp[];
633 				markUnused(nextAvailableIndex() - 1);
634 				return r;
635 			}
636 			if (left !is null)
637 			{
638 				auto r = left.removeSmallest();
639 				if (left.isEmpty())
640 					deallocateNode(left);
641 				return r;
642 			}
643 			Value r = values[0];
644 			Value[nodeCapacity - 1] temp = void;
645 			temp[] = values[1 .. $];
646 			values[0 .. $ - 1] = temp[];
647 			values[$ - 1] = right.removeSmallest();
648 			if (right.isEmpty())
649 				deallocateNode(right);
650 			return r;
651 		}
652 
653 		Value removeLargest()
654 		in
655 		{
656 			assert (!isEmpty());
657 		}
658 		out (result)
659 		{
660 			static if (isPointer!T || is (T == class))
661 				assert (result !is null);
662 		}
663 		body
664 		{
665 			if (left is null && right is null)
666 			{
667 				size_t i = nextAvailableIndex() - 1;
668 				Value r = values[i];
669 				markUnused(i);
670 				return r;
671 			}
672 			if (right !is null)
673 			{
674 				auto r = right.removeLargest();
675 				if (right.isEmpty())
676 					deallocateNode(right);
677 				return r;
678 			}
679 			Value r = values[$ - 1];
680 			Value[nodeCapacity - 1] temp = void;
681 			temp[] = values[0 .. $ - 1];
682 			values[1 .. $] = temp[];
683 			values[0] = left.removeLargest();
684 			if (left.isEmpty())
685 				deallocateNode(left);
686 			return r;
687 		}
688 
689 		void rotateLeft(ref Node* root)
690 		{
691 			Node* newRoot = void;
692 			if (right.left !is null && right.right is null)
693 			{
694 				newRoot = right.left;
695 				newRoot.parent = this.parent;
696 				newRoot.left = &this;
697 				newRoot.left.parent = newRoot;
698 				newRoot.right = right;
699 				newRoot.right.parent = newRoot;
700 				newRoot.right.left = null;
701 				right = null;
702 				left = null;
703 			}
704 			else
705 			{
706 				newRoot = right;
707 				newRoot.parent = this.parent;
708 				right = newRoot.left;
709 				if (right !is null)
710 					right.parent = &this;
711 				newRoot.left = &this;
712 				this.parent = newRoot;
713 			}
714 			cleanup(newRoot, root);
715 		}
716 
717 		void rotateRight(ref Node* root)
718 		{
719 			Node* newRoot = void;
720 			if (left.right !is null && left.left is null)
721 			{
722 				newRoot = left.right;
723 				newRoot.parent = this.parent;
724 				newRoot.right = &this;
725 				newRoot.right.parent = newRoot;
726 				newRoot.left = left;
727 				newRoot.left.parent = newRoot;
728 				newRoot.left.right = null;
729 				left = null;
730 				right = null;
731 			}
732 			else
733 			{
734 				newRoot = left;
735 				newRoot.parent = this.parent;
736 				left = newRoot.right;
737 				if (left !is null)
738 					left.parent = &this;
739 				newRoot.right = &this;
740 				this.parent = newRoot;
741 			}
742 			cleanup(newRoot, root);
743 		}
744 
745 		void cleanup(Node* newRoot, ref Node* root)
746 		{
747 			if (newRoot.parent !is null)
748 			{
749 				if (newRoot.parent.right is &this)
750 					newRoot.parent.right = newRoot;
751 				else
752 					newRoot.parent.left = newRoot;
753 			}
754 			else
755 				root = newRoot;
756 			newRoot.fillFromChildren(root);
757 			if (newRoot.left !is null)
758 			{
759 				newRoot.left.fillFromChildren(root);
760 			}
761 			if (newRoot.right !is null)
762 			{
763 				newRoot.right.fillFromChildren(root);
764 			}
765 			if (newRoot.left !is null)
766 				newRoot.left.calcHeight();
767 			if (newRoot.right !is null)
768 				newRoot.right.calcHeight();
769 			newRoot.calcHeight();
770 		}
771 
772 		void fillFromChildren(ref Node* root)
773 		{
774 			while (!isFull())
775 			{
776 				if (left !is null)
777 				{
778 					insert(left.removeLargest(), root);
779 					if (left.isEmpty())
780 						deallocateNode(left);
781 				}
782 				else if (right !is null)
783 				{
784 					insert(right.removeSmallest(), root);
785 					if (right.isEmpty())
786 						deallocateNode(right);
787 				}
788 				else
789 					break;
790 			}
791 		}
792 
793 		version(graphviz_debugging) void print(File f)
794 		{
795 			f.writef("\"%016x\"[shape=record, label=\"<f0>%d|", &this, height());
796 			f.write("<f1>|");
797 			foreach (i, v; values)
798 			{
799 				if (isFree(i))
800 					f.write("<f> |");
801 				else
802 					f.writef("<f> %s|", v);
803 			}
804 			f.write("<f2>\"];");
805 			if (left !is null)
806 			{
807 				f.writefln("\"%016x\":f1 -> \"%016x\";", &this, left);
808 				left.print(f);
809 			}
810 			if (right !is null)
811 			{
812 				f.writefln("\"%016x\":f2 -> \"%016x\";", &this, right);
813 				right.print(f);
814 			}
815 			if (parent !is null)
816 			{
817 				f.writefln("\"%016x\" -> \"%016x\";", &this, parent);
818 			}
819 		}
820 
821 		debug(EMSI_CONTAINERS) invariant()
822 		{
823 			import std.string : format;
824 			assert (&this !is null);
825 			assert (left !is &this, "%x, %x".format(left, &this));
826 			assert (right !is &this, "%x, %x".format(right, &this));
827 			if (left !is null)
828 			{
829 				assert (left.left !is &this, "%s".format(values));
830 				assert (left.right !is &this, "%x, %x".format(left.right, &this));
831 				assert (left.parent is &this, "%x, %x, %x".format(left, left.parent, &this));
832 			}
833 			if (right !is null)
834 			{
835 				assert (right.left !is &this, "%s".format(values));
836 				assert (right.right !is &this, "%s".format(values));
837 				assert (right.parent is &this);
838 			}
839 		}
840 
841 //		version(graphviz_debugging) void print(File f)
842 //		{
843 //			f.writef("\"%016x\"[shape=record, label=\"", &this);
844 //			f.write("<f1>|");
845 //			foreach (i, v; values)
846 //			{
847 //				if (isFree(i))
848 //					f.write("<f> |");
849 //				else
850 //					f.writef("<f> %s|", v);
851 //			}
852 //			f.write("<f2>\"];");
853 //			if (left !is null)
854 //			{
855 //				f.writefln("\"%016x\":f1 -> \"%016x\";", &this, left);
856 //				left.print(f);
857 //			}
858 //			if (right !is null)
859 //			{
860 //				f.writefln("\"%016x\":f2 -> \"%016x\";", &this, right);
861 //				right.print(f);
862 //			}
863 //		}
864 
865 		Node* left;
866 		Node* right;
867 		Node* parent;
868 
869 		Value[nodeCapacity] values;
870 		size_t registry = (cast(size_t) 1) << (size_t.sizeof * 4);
871 	}
872 
873 	size_t _length = 0;
874 	Node* root = null;
875 }
876 
877 unittest
878 {
879 	import core.memory : GC;
880 	import std.algorithm : equal, sort, map, filter;
881 	import std.array : array;
882 	import std.range : iota, walkLength, isForwardRange;
883 	import std.string : format;
884 	import std.uuid : randomUUID;
885 
886 	{
887 		TTree!int kt;
888 		assert (kt.empty);
889 		foreach (i; 0 .. 200)
890 		{
891 			assert (kt.insert(i));
892 //			version(graphviz_debugging)
893 //			{
894 //				File f = File("graph%04d.dot".format(i), "w");
895 //				kt.print(f);
896 //			}
897 		}
898 		assert (!kt.empty);
899 		assert (kt.length == 200);
900 		assert (kt.contains(30));
901 //		writeln(__LINE__, " passed");
902 	}
903 
904 	{
905 		TTree!int kt;
906 		assert (!kt.contains(5));
907 		kt.insert(2_000);
908 		assert (kt.contains(2_000));
909 		foreach_reverse (i; 0 .. 1_000)
910 		{
911 			assert (kt.insert(i));
912 		}
913 		assert (!kt.contains(100_000));
914 //		writeln(__LINE__, " passed");
915 	}
916 
917 	{
918 		import std.random;
919 		TTree!int kt;
920 		foreach (i; 0 .. 1_000)
921 		{
922 			kt.insert(uniform(0, 100_000));
923 		}
924 //		writeln(__LINE__, " passed");
925 	}
926 
927 	{
928 		TTree!int kt;
929 		kt.insert(10);
930 		assert (kt.length == 1);
931 		assert (!kt.insert(10));
932 		assert (kt.length == 1);
933 //		writeln(__LINE__, " passed");
934 	}
935 
936 	{
937 		TTree!(int, true) kt;
938 		assert (kt.insert(1));
939 		assert (kt.length == 1);
940 		assert (kt.insert(1));
941 		assert (kt.length == 2);
942 		assert (kt.contains(1));
943 //		writeln(__LINE__, " passed");
944 	}
945 
946 	{
947 		TTree!(int) kt;
948 		foreach (i; 0 .. 200)
949 		{
950 			assert (kt.insert(i));
951 //			version(graphviz_debugging)
952 //			{
953 //				File f = File("graph%04d.dot".format(i), "w");
954 //				kt.print(f);
955 //			}
956 		}
957 		assert (kt.length == 200);
958 		assert (kt.remove(79));
959 		assert (!kt.remove(79));
960 //		version(graphviz_debugging)
961 //		{
962 //			File f = File("graph%04d.dot".format(999), "w");
963 //			kt.print(f);
964 //		}
965 		assert (kt.length == 199);
966 //		writeln(__LINE__, " passed");
967 	}
968 
969 	{
970 		string[] strs = [
971 			"2c381d2a-bacd-40db-b6d8-055b144c5ee6",
972 			"62104b50-e235-4c95-bcb9-a545e88e2d09",
973 			"828c8fc0-a392-4738-a49c-62e991fce090",
974 			"62e30465-79eb-446e-b34f-af5d7c491486",
975 			"93ec245b-60d2-4422-91ff-66a6d7e299fc",
976 			"c1d2f3d7-82cc-4d90-a2c5-9fba335f36cd",
977 			"c9d8d980-94eb-4941-b873-00d68021522f",
978 			"82dbc4df-cb3c-447a-9d73-cd6291a0ba02",
979 			"8d259231-6ab6-49e4-9bb6-fe097c4153ed",
980 			"f9f2d719-61e1-4f62-ae2c-bf2a24a13d5b"
981 		];
982 		TTree!string strings;
983 		foreach (i, s; strs)
984 		{
985 			assert (strings.insert(s));
986 //			version(graphviz_debugging)
987 //			{
988 //				File f = File("graph%04d.dot".format(i), "w");
989 //				strings.print(f);
990 //			}
991 		}
992 		sort(strs[]);
993 //		writeln(strings[]);
994 		assert (equal(strs, strings[]));
995 //		writeln(__LINE__, " passed");
996 	}
997 
998 	foreach (x; 0 .. 1000)
999 	{
1000 		TTree!string strings;
1001 		string[] strs = iota(10).map!(a => randomUUID().toString()).array();
1002 		foreach (i, s; strs)
1003 		{
1004 			assert (strings.insert(s));
1005 //			version(graphviz_debugging)
1006 //			{
1007 //				File f = File("graph%04d.dot".format(i), "w");
1008 //				strings.print(f);
1009 //			}
1010 		}
1011 		assert (strings.length == strs.length);
1012 //		version(graphviz_debugging)
1013 //		{
1014 //			File f = File("graph%04d.dot".format(1000), "w");
1015 //			strings.print(f);
1016 //		}
1017 		sort(strs);
1018 //		writeln(strings[]);
1019 		assert (equal(strs, strings[]));
1020 //		writeln(__LINE__, " passed");
1021 	}
1022 
1023 	{
1024 		TTree!string strings;
1025 		strings.insert(["e", "f", "a", "b", "c", "d"]);
1026 //		writeln(strings[]);
1027 		assert (equal(strings[], ["a", "b", "c", "d", "e", "f"]));
1028 //		writeln(__LINE__, " passed");
1029 	}
1030 
1031 	{
1032 		TTree!(string, true) strings;
1033 		assert (strings.insert("b"));
1034 		assert (strings.insert("c"));
1035 		assert (strings.insert("a"));
1036 		assert (strings.insert("d"));
1037 		assert (strings.insert("d"));
1038 		assert (strings.length == 5);
1039 		assert (equal(strings.equalRange("d"), ["d", "d"]));
1040 		assert (equal(strings.lowerBound("d"), ["a", "b", "c"]));
1041 		assert (equal(strings.upperBound("c"), ["d", "d"]));
1042 //		writeln(__LINE__, " passed");
1043 	}
1044 
1045 	{
1046 //		import std.stdio;
1047 		static struct S
1048 		{
1049 			string x;
1050 			int opCmp (ref const S other) const
1051 			{
1052 				if (x < other.x)
1053 					return -1;
1054 				if (x > other.x)
1055 					return 1;
1056 				return 0;
1057 			}
1058 		}
1059 
1060 		TTree!(S*, true) stringTree;
1061 		auto one = S("offset");
1062 		stringTree.insert(&one);
1063 		auto two = S("object");
1064 		assert (stringTree.equalRange(&two).empty);
1065 		assert (!stringTree.equalRange(&one).empty);
1066 		assert (stringTree[].front.x == "offset");
1067 //		writeln(__LINE__, " passed");
1068 	}
1069 
1070 	{
1071 		static struct TestStruct
1072 		{
1073 			int opCmp(ref const TestStruct other) const
1074 			{
1075 				return x < other.x ? -1 : (x > other.x ? 1 : 0);
1076 			}
1077 			int x;
1078 			int y;
1079 		}
1080 		TTree!(TestStruct*, false) tsTree;
1081 		static assert (isForwardRange!(typeof(tsTree).Range));
1082 		foreach (i; 0 .. 100)
1083 		{
1084 			assert(tsTree.insert(new TestStruct(i, i * 2)));
1085 			version(graphviz_debugging)
1086 			{
1087 				File f = File("graph%04d.dot".format(i), "w");
1088 				tsTree.print(f);
1089 			}
1090 		}
1091 		assert (tsTree.length == 100);
1092 		auto r = tsTree[];
1093 		TestStruct* prev = r.front();
1094 		r.popFront();
1095 		while (!r.empty)
1096 		{
1097 			assert (r.front.x > prev.x, format("%s %s", prev.x, r.front.x));
1098 			prev = r.front;
1099 			r.popFront();
1100 		}
1101 		TestStruct a = TestStruct(30, 100);
1102 		auto eqArray = array(tsTree.equalRange(&a));
1103 		assert (eqArray.length == 1, format("%d", eqArray.length));
1104 //		writeln(__LINE__, " passed");
1105 	}
1106 
1107 	{
1108 		import std.algorithm : canFind;
1109 		TTree!int ints;
1110 		foreach (i; 0 .. 50)
1111 			ints ~= i;
1112 		assert (canFind(ints[], 20));
1113 		assert (walkLength(ints[]) == 50);
1114 		assert (walkLength(filter!(a => (a & 1) == 0)(ints[])) == 25);
1115 //		writeln(__LINE__, " passed");
1116 	}
1117 
1118 	{
1119 		TTree!int ints;
1120 		foreach (i; 0 .. 50)
1121 			ints ~=  i;
1122 		ints.remove(0);
1123 		assert (ints.length == 49);
1124 		foreach (i; 1 .. 12)
1125 		{
1126 //			try
1127 				ints.remove(i);
1128 //			catch (Error)
1129 //				writeln("Failed on ", i);
1130 		}
1131 		assert (ints.length == 49 - 11);
1132 	}
1133 
1134 	// This test ensures that the container works with const elements
1135 	{
1136 		const(TTree!(const(int))) getInts()
1137 		{
1138 			TTree!(const(int)) t;
1139 			t.insert(1);
1140 			t.insert(2);
1141 			t.insert(3);
1142 			return t;
1143 		}
1144 		auto t = getInts();
1145 		static assert (is (typeof(t[].front) == const(int)));
1146 		assert (equal(t[].filter!(a => a & 1), [1, 3]));
1147 	}
1148 }