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