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 import std.range : ElementType, isInputRange;
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 	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(cast(Value) value, null);
70 			++_length;
71 			return true;
72 		}
73 		immutable bool r = root.insert(cast(Value) value, root);
74 		if (r)
75 			++_length;
76 		return r;
77 	}
78 
79 	/// ditto
80 	bool insert(R)(R r) if (isInputRange!R && is(ElementType!R == T))
81 	{
82 		immutable 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 	/**
142 	 * Returns: a range over the tree. Do not insert into the tree while
143 	 * iterating because you may iterate over the same value multiple times.
144 	 */
145 
146 	auto range(this This)()
147 	{
148 		return Range!(This)(cast(const(Node)*) root, RangeType.all, T.init);
149 	}
150 
151 	alias opSlice = range;
152 
153 	/**
154 	 * Returns: a range of elements which are less than value.
155 	 */
156 	auto lowerBound(this This)(inout T value)
157 	{
158 		return Range!(This)(cast(const(Node)*) root, RangeType.lower, value);
159 	}
160 
161 	/**
162 	 * Returns: a range of elements which are equivalent (though not necessarily
163 	 * equal) to value.
164 	 */
165 	auto equalRange(this This)(inout T value)
166 	{
167 		return Range!(This)(cast(const(Node)*) root, RangeType.equal, value);
168 	}
169 
170 	/**
171 	 * Returns: a range of elements which are greater than value.
172 	 */
173 	auto upperBound(this This)(inout T value)
174 	{
175 		return Range!(This)(cast(const(Node)*) root, RangeType.upper, value);
176 	}
177 
178 	/**
179 	 * Tree range
180 	 */
181 	static struct Range(ThisT)
182 	{
183 		@disable this();
184 
185 		/**
186 		 * Standard range operations
187 		 */
188 		ET front() const @property
189 		in
190 		{
191 			assert (!empty);
192 		}
193 		body
194 		{
195 			return cast(typeof(return)) current.values[index];
196 		}
197 
198 		/// ditto
199 		bool empty() const nothrow pure @property
200 		{
201 			return current is null;
202 		}
203 
204 		/// ditto
205 		void popFront()
206 		{
207 			_popFront();
208 			if (current is null)
209 				return;
210 			with (RangeType) final switch (type)
211 			{
212 			case upper:
213 			case all: break;
214 			case equal:
215 				if (_less(front(), val) || _less(val, front()))
216 					current = null;
217 				break;
218 			case lower:
219 				if (!_less(front(), val))
220 					current = null;
221 				break;
222 			}
223 		}
224 
225 	private:
226 
227 		alias ET = ContainerElementType!(ThisT, T);
228 
229 		void currentToLeftmost()
230 		{
231 			if (current is null)
232 				return;
233 			while (current.left !is null)
234 				current = current.left;
235 		}
236 
237 		void currentToLeastContaining(inout T val)
238 		{
239 			while (current !is null)
240 			{
241 				if (_less(val, current.values[0]))
242 					current = current.left;
243 				else if (current.isFull)
244 				{
245 					if (_less(current.values[$ - 1], cast(Value) val))
246 						current = current.right;
247 					else
248 						break;
249 				}
250 				else
251 					break;
252 			}
253 		}
254 
255 		this(const(Node)* n, RangeType type, inout T val)
256 		{
257 			current = n;
258 			this.type = type;
259 			this.val = val;
260 			with (RangeType) final switch(type)
261 			{
262 			case all:
263 				currentToLeftmost();
264 				break;
265 			case lower:
266 				currentToLeftmost();
267 				if (_less(val, front()))
268 					current = null;
269 				break;
270 			case equal:
271 				currentToLeastContaining(val);
272 				while (current !is null && _less(front(), val))
273 					_popFront();
274 				if (current is null || _less(front(), val) || _less(val, front()))
275 					current = null;
276 				break;
277 			case upper:
278 				currentToLeastContaining(val);
279 				while (current !is null && !_less(val, front()))
280 					_popFront();
281 				break;
282 			}
283 		}
284 
285 		void _popFront()
286 		in
287 		{
288 			assert (!empty);
289 		}
290 		body
291 		{
292 			index++;
293 			if (index >= nodeCapacity || current.isFree(index))
294 			{
295 				index = 0;
296 				if (current.right !is null)
297 				{
298 					current = current.right;
299 					while (current.left !is null)
300 						current = current.left;
301 				}
302 				else if (current.parent is null)
303 					current = null;
304 				else if (current.parent.left is current)
305 					current = current.parent;
306 				else
307 				{
308 					while (current.parent.right is current)
309 					{
310 						current = current.parent;
311 						if (current.parent is null)
312 						{
313 							current = null;
314 							return;
315 						}
316 					}
317 					current = current.parent;
318 				}
319 			}
320 		}
321 
322 		size_t index;
323 		const(Node)* current;
324 		RangeType type;
325 		const T val;
326 	}
327 
328 private:
329 
330 	import containers.internal.node : fatNodeCapacity, fullBits, shouldAddGCRange, shouldNullSlot;
331 	import containers.internal.element_type : ContainerElementType;
332     import std.algorithm : sort;
333 	import std.allocator: Mallocator, allocate, deallocate;
334 	import std.functional: binaryFun;
335 	import std.traits: isPointer, PointerTarget;
336 
337 	enum RangeType : ubyte {all, lower, equal, upper}
338 
339 	// If we're storing a struct that defines opCmp, don't compare pointers as
340 	// that is almost certainly not what the user intended.
341 	static if (is(typeof(less) == string ) && less == "a < b" && isPointer!T && __traits(hasMember, PointerTarget!T, "opCmp"))
342 		alias _less = binaryFun!"a.opCmp(*b) < 0";
343 	else
344 		alias _less = binaryFun!less;
345 
346 	static Node* allocateNode(Value value, Node* parent)
347 	out (result)
348 	{
349 		assert (result.left is null);
350 		assert (result.right is null);
351 	}
352 	body
353 	{
354 		import core.memory : GC;
355 		Node* n = allocate!Node(Mallocator.it);
356 		n.parent = parent;
357 		n.markUsed(0);
358 		n.values[0] = cast(Value) value;
359 		static if (supportGC && shouldAddGCRange!T)
360 			GC.addRange(n, Node.sizeof);
361 		return n;
362 	}
363 
364 	static void deallocateNode(ref Node* n)
365 	in
366 	{
367 		assert (n !is null);
368 	}
369 	body
370 	{
371 		import core.memory : GC;
372 		static if (supportGC && shouldAddGCRange!T)
373 			GC.removeRange(n);
374 		typeid(Node).destroy(n);
375 		deallocate!Node(Mallocator.it, n);
376 		n = null;
377 	}
378 
379 	static assert (nodeCapacity <= (typeof(Node.registry).sizeof * 8));
380 	static assert (Node.sizeof <= cacheLineSize);
381 	static struct Node
382 	{
383 		~this()
384 		{
385 			if (left !is null)
386 				deallocateNode(left);
387 			if (right !is null)
388 				deallocateNode(right);
389 		}
390 
391 		private size_t nextAvailableIndex() const nothrow pure
392 		{
393 			import core.bitop;
394 			return bsf(~(registry & fullBits!nodeCapacity));
395 		}
396 
397 		private void markUsed(size_t index) pure nothrow
398 		{
399 			registry |= (1 << index);
400 		}
401 
402 		private void markUnused(size_t index) pure nothrow
403 		{
404 			registry &= ~(1 << index);
405 			static if (shouldNullSlot!T)
406 				values[index] = null;
407 		}
408 
409 		private bool isFree(size_t index) const pure nothrow
410 		{
411 			return (registry & (1 << index)) == 0;
412 		}
413 
414 		private bool isFull() const pure nothrow
415 		{
416 			return (registry & fullBits!nodeCapacity) == fullBits!nodeCapacity;
417 		}
418 
419 		private bool isEmpty() const pure nothrow
420 		{
421 			return (registry & fullBits!nodeCapacity) == 0;
422 		}
423 
424 		bool contains(Value value) const
425 		{
426 			import std.range : assumeSorted;
427 			size_t i = nextAvailableIndex();
428 			if (_less(value, cast(Value) values[0]))
429 				return left !is null && left.contains(value);
430 			if (_less(values[i - 1], value))
431 				return right !is null && right.contains(value);
432 			return !assumeSorted!_less(values[0 .. i]).equalRange(value).empty;
433 		}
434 
435 		size_t calcHeight() nothrow pure
436 		{
437 			immutable size_t l = left !is null ? left.height() : 0;
438 			immutable size_t r = right !is null ? right.height() : 0;
439 			size_t h = 1 + (l > r ? l : r);
440 			registry &= fullBits!nodeCapacity;
441 			registry |= (h << (size_t.sizeof * 4));
442 			return h;
443 		}
444 
445 		size_t height() const nothrow pure
446 		{
447 			return registry >>> (size_t.sizeof * 4);
448 		}
449 
450 		int imbalanced() const nothrow pure
451 		{
452 			if (right !is null
453 				&& ((left is null && right.height() > 1)
454 				|| (left !is null && right.height() > left.height() + 1)))
455 				return 1;
456 			if (left !is null
457 				&& ((right is null && left.height() > 1)
458 				|| (right !is null && left.height() > right.height() + 1)))
459 				return -1;
460 			return 0;
461 		}
462 
463 		bool insert(T value, ref Node* root)
464 		in
465 		{
466 			static if (isPointer!T || is (T == class))
467 				assert (value !is null);
468 		}
469 		body
470 		{
471 			import std.algorithm : sort;
472 			import std.range : assumeSorted;
473 			if (!isFull())
474 			{
475 				immutable size_t index = nextAvailableIndex();
476 				static if (!allowDuplicates)
477 				{
478 					if (!assumeSorted!_less(values[0 .. index]).equalRange(
479 						cast(Value) value).empty)
480 					{
481 						return false;
482 					}
483 				}
484 				values[index] = cast(Value) value;
485 				markUsed(index);
486 				sort!_less(values[0 .. index + 1]);
487 				return true;
488 			}
489 			if (_less(value, values[0]))
490 			{
491 				if (left is null)
492 				{
493 					left = allocateNode(cast(Value) value, &this);
494 					calcHeight();
495 					return true;
496 				}
497 				bool b = left.insert(value, root);
498 				if (imbalanced() == -1)
499 					rotateRight(root);
500 				calcHeight();
501 				return b;
502 			}
503 			if (_less(values[$ - 1], cast(Value) value))
504 			{
505 				if (right is null)
506 				{
507 					right = allocateNode(value, &this);
508 					calcHeight();
509 					return true;
510 				}
511 				bool b = right.insert(value, root);
512 				if (imbalanced() == 1)
513 					rotateLeft(root);
514 				calcHeight();
515 				return b;
516 			}
517 			static if (!allowDuplicates)
518 				if (!assumeSorted!_less(values[]).equalRange(cast(Value) value).empty)
519 					return false;
520 			Value[nodeCapacity + 1] temp = void;
521 			temp[0 .. $ - 1] = values[];
522 			temp[$ - 1] = cast(Value) value;
523 			sort!_less(temp[]);
524 			if (right is null)
525 			{
526 				values[] = temp[0 .. $ - 1];
527 				right = allocateNode(temp[$ - 1], &this);
528 				return true;
529 			}
530 			if (left is null)
531 			{
532 				values[] = temp[1 .. $];
533 				left = allocateNode(temp[0], &this);
534 				return true;
535 			}
536 			if (right.height < left.height)
537 			{
538 				values[] = temp[0 .. $ - 1];
539 				bool b = right.insert(temp[$ - 1], root);
540 				if (imbalanced() == 1)
541 					rotateLeft(root);
542 				calcHeight();
543 				return b;
544 			}
545 			values[] = temp[1 .. $];
546 			bool b = left.insert(temp[0], root);
547 			if (imbalanced() == -1)
548 				rotateRight(root);
549 			calcHeight();
550 			return b;
551 		}
552 
553 		bool remove(Value value, ref Node* n, void delegate(T) cleanup = null)
554 		{
555 			import std.range : assumeSorted;
556 			assert (!isEmpty());
557 			if (isFull() && _less(value, values[0]))
558 			{
559 				bool r = left !is null && left.remove(value, left, cleanup);
560 				if (left.isEmpty())
561 					deallocateNode(left);
562 				return r;
563 			}
564 			if (isFull() && _less(values[$ - 1], value))
565 			{
566 				bool r = right !is null && right.remove(value, right, cleanup);
567 				if (right.isEmpty())
568 					deallocateNode(right);
569 				return r;
570 			}
571 			size_t i = nextAvailableIndex();
572 			auto sv = assumeSorted!_less(values[0 .. i]);
573 			auto tri = sv.trisect(value);
574 			if (tri[1].length == 0)
575 				return false;
576 			// Clean up removed item
577 			if (cleanup !is null)
578 				cleanup(tri[1][0]);
579 			immutable size_t l = tri[0].length;
580 			if (right is null && left is null)
581 			{
582 				Value[nodeCapacity - 1] temp;
583 				temp[0 .. l] = values[0 .. l];
584 				temp[l .. $] = values[l + 1 .. $];
585 				values[0 .. $ - 1] = temp[];
586 				markUnused(nextAvailableIndex() - 1);
587 			}
588 			else if (right !is null)
589 			{
590 				Value[nodeCapacity - 1] temp;
591 				temp[0 .. l] = values[0 .. l];
592 				temp[l .. $] = values[l + 1 .. $];
593 				values[0 .. $ - 1] = temp[];
594 				values[$ - 1] = right.removeSmallest();
595 				if (right.isEmpty())
596 					deallocateNode(right);
597 			}
598 			else if (left !is null)
599 			{
600 				Value[nodeCapacity - 1] temp;
601 				temp[0 .. l] = values[0 .. l];
602 				temp[l .. $] = values[l + 1 .. $];
603 				values[1 .. $] = temp[];
604 				values[0] = left.removeLargest();
605 				if (left.isEmpty())
606 					deallocateNode(left);
607 			}
608 			return true;
609 		}
610 
611 		Value removeSmallest()
612 		in
613 		{
614 			assert (!isEmpty());
615 		}
616 		body
617 		{
618 			if (left is null && right is null)
619 			{
620 				Value r = values[0];
621 				Value[nodeCapacity - 1] temp = void;
622 				temp[] = values[1 .. $];
623 				values[0 .. $ - 1] = temp[];
624 				markUnused(nextAvailableIndex() - 1);
625 				return r;
626 			}
627 			if (left !is null)
628 			{
629 				auto r = left.removeSmallest();
630 				if (left.isEmpty())
631 					deallocateNode(left);
632 				return r;
633 			}
634 			Value r = values[0];
635 			Value[nodeCapacity - 1] temp = void;
636 			temp[] = values[1 .. $];
637 			values[0 .. $ - 1] = temp[];
638 			values[$ - 1] = right.removeSmallest();
639 			if (right.isEmpty())
640 				deallocateNode(right);
641 			return r;
642 		}
643 
644 		Value removeLargest()
645 		in
646 		{
647 			assert (!isEmpty());
648 		}
649 		out (result)
650 		{
651 			static if (isPointer!T || is (T == class))
652 				assert (result !is null);
653 		}
654 		body
655 		{
656 			if (left is null && right is null)
657 			{
658 				size_t i = nextAvailableIndex() - 1;
659 				Value r = values[i];
660 				markUnused(i);
661 				return r;
662 			}
663 			if (right !is null)
664 			{
665 				auto r = right.removeLargest();
666 				if (right.isEmpty())
667 					deallocateNode(right);
668 				return r;
669 			}
670 			Value r = values[$ - 1];
671 			Value[nodeCapacity - 1] temp = void;
672 			temp[] = values[0 .. $ - 1];
673 			values[1 .. $] = temp[];
674 			values[0] = left.removeLargest();
675 			if (left.isEmpty())
676 				deallocateNode(left);
677 			return r;
678 		}
679 
680 		void rotateLeft(ref Node* root)
681 		{
682 			Node* newRoot = void;
683 			if (right.left !is null && right.right is null)
684 			{
685 				newRoot = right.left;
686 				newRoot.parent = this.parent;
687 				newRoot.left = &this;
688 				newRoot.left.parent = newRoot;
689 				newRoot.right = right;
690 				newRoot.right.parent = newRoot;
691 				newRoot.right.left = null;
692 				right = null;
693 				left = null;
694 			}
695 			else
696 			{
697 				newRoot = right;
698 				newRoot.parent = this.parent;
699 				right = newRoot.left;
700 				if (right !is null)
701 					right.parent = &this;
702 				newRoot.left = &this;
703 				this.parent = newRoot;
704 			}
705 			cleanup(newRoot, root);
706 		}
707 
708 		void rotateRight(ref Node* root)
709 		{
710 			Node* newRoot = void;
711 			if (left.right !is null && left.left is null)
712 			{
713 				newRoot = left.right;
714 				newRoot.parent = this.parent;
715 				newRoot.right = &this;
716 				newRoot.right.parent = newRoot;
717 				newRoot.left = left;
718 				newRoot.left.parent = newRoot;
719 				newRoot.left.right = null;
720 				left = null;
721 				right = null;
722 			}
723 			else
724 			{
725 				newRoot = left;
726 				newRoot.parent = this.parent;
727 				left = newRoot.right;
728 				if (left !is null)
729 					left.parent = &this;
730 				newRoot.right = &this;
731 				this.parent = newRoot;
732 			}
733 			cleanup(newRoot, root);
734 		}
735 
736 		void cleanup(Node* newRoot, ref Node* root)
737 		{
738 			if (newRoot.parent !is null)
739 			{
740 				if (newRoot.parent.right is &this)
741 					newRoot.parent.right = newRoot;
742 				else
743 					newRoot.parent.left = newRoot;
744 			}
745 			else
746 				root = newRoot;
747 			newRoot.fillFromChildren(root);
748 			if (newRoot.left !is null)
749 			{
750 				newRoot.left.fillFromChildren(root);
751 			}
752 			if (newRoot.right !is null)
753 			{
754 				newRoot.right.fillFromChildren(root);
755 			}
756 			if (newRoot.left !is null)
757 				newRoot.left.calcHeight();
758 			if (newRoot.right !is null)
759 				newRoot.right.calcHeight();
760 			newRoot.calcHeight();
761 		}
762 
763 		void fillFromChildren(ref Node* root)
764 		{
765 			while (!isFull())
766 			{
767 				if (left !is null)
768 				{
769 					insert(left.removeLargest(), root);
770 					if (left.isEmpty())
771 						deallocateNode(left);
772 				}
773 				else if (right !is null)
774 				{
775 					insert(right.removeSmallest(), root);
776 					if (right.isEmpty())
777 						deallocateNode(right);
778 				}
779 				else
780 					break;
781 			}
782 		}
783 
784 		debug(EMSI_CONTAINERS) invariant()
785 		{
786 			import std.string : format;
787 			assert (&this !is null);
788 			assert (left !is &this, "%x, %x".format(left, &this));
789 			assert (right !is &this, "%x, %x".format(right, &this));
790 			if (left !is null)
791 			{
792 				assert (left.left !is &this, "%s".format(values));
793 				assert (left.right !is &this, "%x, %x".format(left.right, &this));
794 				assert (left.parent is &this, "%x, %x, %x".format(left, left.parent, &this));
795 			}
796 			if (right !is null)
797 			{
798 				assert (right.left !is &this, "%s".format(values));
799 				assert (right.right !is &this, "%s".format(values));
800 				assert (right.parent is &this);
801 			}
802 		}
803 
804 		Node* left;
805 		Node* right;
806 		Node* parent;
807 
808 		Value[nodeCapacity] values;
809 		size_t registry = (cast(size_t) 1) << (size_t.sizeof * 4);
810 	}
811 
812 	size_t _length = 0;
813 	Node* root = null;
814 }
815 
816 unittest
817 {
818 	import core.memory : GC;
819 	import std.algorithm : equal, sort, map, filter;
820 	import std.array : array;
821 	import std.range : iota, walkLength, isInputRange;
822 	import std.string : format;
823 	import std.uuid : randomUUID;
824 
825 	{
826 		TTree!int kt;
827 		assert (kt.empty);
828 		foreach (i; 0 .. 200)
829 			assert (kt.insert(i));
830 		assert (!kt.empty);
831 		assert (kt.length == 200);
832 		assert (kt.contains(30));
833 	}
834 
835 	{
836 		TTree!int kt;
837 		assert (!kt.contains(5));
838 		kt.insert(2_000);
839 		assert (kt.contains(2_000));
840 		foreach_reverse (i; 0 .. 1_000)
841 		{
842 			assert (kt.insert(i));
843 		}
844 		assert (!kt.contains(100_000));
845 	}
846 
847 	{
848 		import std.random : uniform;
849 		TTree!int kt;
850 		foreach (i; 0 .. 1_000)
851 			kt.insert(uniform(0, 100_000));
852 	}
853 
854 	{
855 		TTree!int kt;
856 		kt.insert(10);
857 		assert (kt.length == 1);
858 		assert (!kt.insert(10));
859 		assert (kt.length == 1);
860 	}
861 
862 	{
863 		TTree!(int, true) kt;
864 		assert (kt.insert(1));
865 		assert (kt.length == 1);
866 		assert (kt.insert(1));
867 		assert (kt.length == 2);
868 		assert (kt.contains(1));
869 	}
870 
871 	{
872 		TTree!(int) kt;
873 		foreach (i; 0 .. 200)
874 			assert (kt.insert(i));
875 		assert (kt.length == 200);
876 		assert (kt.remove(79));
877 		assert (!kt.remove(79));
878 		assert (kt.length == 199);
879 	}
880 
881 	{
882 		string[] strs = [
883 			"2c381d2a-bacd-40db-b6d8-055b144c5ee6",
884 			"62104b50-e235-4c95-bcb9-a545e88e2d09",
885 			"828c8fc0-a392-4738-a49c-62e991fce090",
886 			"62e30465-79eb-446e-b34f-af5d7c491486",
887 			"93ec245b-60d2-4422-91ff-66a6d7e299fc",
888 			"c1d2f3d7-82cc-4d90-a2c5-9fba335f36cd",
889 			"c9d8d980-94eb-4941-b873-00d68021522f",
890 			"82dbc4df-cb3c-447a-9d73-cd6291a0ba02",
891 			"8d259231-6ab6-49e4-9bb6-fe097c4153ed",
892 			"f9f2d719-61e1-4f62-ae2c-bf2a24a13d5b"
893 		];
894 		TTree!string strings;
895 		foreach (i, s; strs)
896 			assert (strings.insert(s));
897 		sort(strs[]);
898 		assert (equal(strs, strings[]));
899 	}
900 
901 	foreach (x; 0 .. 1000)
902 	{
903 		TTree!string strings;
904 		string[] strs = iota(10).map!(a => randomUUID().toString()).array();
905 		foreach (i, s; strs)
906 			assert (strings.insert(s));
907 		assert (strings.length == strs.length);
908 		sort(strs);
909 		assert (equal(strs, strings[]));
910 	}
911 
912 	{
913 		TTree!string strings;
914 		strings.insert(["e", "f", "a", "b", "c", "d"]);
915 		assert (equal(strings[], ["a", "b", "c", "d", "e", "f"]));
916 	}
917 
918 	{
919 		TTree!(string, true) strings;
920 		assert (strings.insert("b"));
921 		assert (strings.insert("c"));
922 		assert (strings.insert("a"));
923 		assert (strings.insert("d"));
924 		assert (strings.insert("d"));
925 		assert (strings.length == 5);
926 		assert (equal(strings.equalRange("d"), ["d", "d"]));
927 		assert (equal(strings.lowerBound("d"), ["a", "b", "c"]));
928 		assert (equal(strings.upperBound("c"), ["d", "d"]));
929 	}
930 
931 	{
932 		static struct S
933 		{
934 			string x;
935 			int opCmp (ref const S other) const
936 			{
937 				if (x < other.x)
938 					return -1;
939 				if (x > other.x)
940 					return 1;
941 				return 0;
942 			}
943 		}
944 
945 		TTree!(S*, true) stringTree;
946 		auto one = S("offset");
947 		stringTree.insert(&one);
948 		auto two = S("object");
949 		assert (stringTree.equalRange(&two).empty);
950 		assert (!stringTree.equalRange(&one).empty);
951 		assert (stringTree[].front.x == "offset");
952 	}
953 
954 	{
955 		static struct TestStruct
956 		{
957 			int opCmp(ref const TestStruct other) const
958 			{
959 				return x < other.x ? -1 : (x > other.x ? 1 : 0);
960 			}
961 			int x;
962 			int y;
963 		}
964 		TTree!(TestStruct*, false) tsTree;
965 		static assert (isInputRange!(typeof(tsTree.range())));
966 		foreach (i; 0 .. 100)
967 		{
968 			assert(tsTree.insert(new TestStruct(i, i * 2)));
969 			version(graphviz_debugging)
970 			{
971 				File f = File("graph%04d.dot".format(i), "w");
972 				tsTree.print(f);
973 			}
974 		}
975 		assert (tsTree.length == 100);
976 		auto r = tsTree[];
977 		TestStruct* prev = r.front();
978 		r.popFront();
979 		while (!r.empty)
980 		{
981 			assert (r.front.x > prev.x, format("%s %s", prev.x, r.front.x));
982 			prev = r.front;
983 			r.popFront();
984 		}
985 		TestStruct a = TestStruct(30, 100);
986 		auto eqArray = array(tsTree.equalRange(&a));
987 		assert (eqArray.length == 1, format("%d", eqArray.length));
988 	}
989 
990 	{
991 		import std.algorithm : canFind;
992 		TTree!int ints;
993 		foreach (i; 0 .. 50)
994 			ints ~= i;
995 		assert (canFind(ints[], 20));
996 		assert (walkLength(ints[]) == 50);
997 		assert (walkLength(filter!(a => (a & 1) == 0)(ints[])) == 25);
998 	}
999 
1000 	{
1001 		TTree!int ints;
1002 		foreach (i; 0 .. 50)
1003 			ints ~=  i;
1004 		ints.remove(0);
1005 		assert (ints.length == 49);
1006 		foreach (i; 1 .. 12)
1007 			ints.remove(i);
1008 		assert (ints.length == 49 - 11);
1009 	}
1010 
1011 	{
1012 		const(TTree!(const(int))) getInts()
1013 		{
1014 			TTree!(const(int)) t;
1015 			t.insert(1);
1016 			t.insert(2);
1017 			t.insert(3);
1018 			return t;
1019 		}
1020 		auto t = getInts();
1021 		static assert (is (typeof(t[].front) == const(int)));
1022 		assert (equal(t[].filter!(a => a & 1), [1, 3]));
1023 	}
1024 }