Merge branch 'jmdavis-red-black'

Conflicts:
	std/array.d
This commit is contained in:
Andrei Alexandrescu 2011-02-26 18:54:43 -06:00
commit db5b27dcd2
3 changed files with 423 additions and 1665 deletions

File diff suppressed because it is too large Load diff

View file

@ -4105,13 +4105,10 @@ struct RBNode(V)
* ignored on insertion. If duplicates are allowed, then new elements are
* inserted after all existing duplicate elements.
*/
struct RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false)
if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false)
if(is(typeof(binaryFun!less(T.init, T.init))))
{
static if(is(typeof(less) == string))
alias binaryFun!(less) _less;
else
alias less _less;
alias binaryFun!less _less;
// BUG: this must come first in the struct due to issue 2810
@ -4122,7 +4119,20 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
{
Node result;
static if(!allowDuplicates)
{
bool added = true;
scope(success)
{
if(added)
++_length;
}
}
else
{
scope(success)
++_length;
}
if(!_end.left)
{
_end.left = result = allocate(n);
@ -4204,11 +4214,6 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
}
return false;
}
private static RedBlackTree create(Elem[] elems...)
{
return RedBlackTree(elems);
}
}
else
{
@ -4224,9 +4229,11 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
private alias RBNode!Elem.Node Node;
private Node _end;
private size_t _length;
private void _setup()
{
assert(!_end); //Make sure that _setup isn't run more than once.
_end = allocate();
}
@ -4311,10 +4318,17 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
static if(doUnittest) unittest
{
auto ts = create(1, 2, 3, 4, 5);
auto ts = new RedBlackTree(1, 2, 3, 4, 5);
assert(ts.length == 5);
auto r = ts[];
assert(std.algorithm.equal(r, cast(T[])[1, 2, 3, 4, 5]));
assert(r.front == 1);
static if(less == "a < b")
auto vals = [1, 2, 3, 4, 5];
else
auto vals = [5, 4, 3, 2, 1];
assert(std.algorithm.equal(r, vals));
assert(r.front == vals.front);
assert(r.back != r.front);
auto oldfront = r.front;
auto oldback = r.back;
@ -4323,6 +4337,7 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
assert(r.front != r.back);
assert(r.front != oldfront);
assert(r.back != oldback);
assert(ts.length == 5);
}
// find a node based on an element value
@ -4372,27 +4387,37 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
return _end.left is null;
}
/++
Returns the number of elements in the container.
Complexity: $(BIGOH 1).
+/
@property size_t length()
{
return _length;
}
/**
* Duplicate this container. The resulting container contains a shallow
* copy of the elements.
*
* Complexity: $(BIGOH n)
*/
RedBlackTree dup()
@property RedBlackTree dup()
{
RedBlackTree result;
result._setup();
result._end = _end.dup();
return result;
return new RedBlackTree(_end.dup(), _length);
}
static if(doUnittest) unittest
{
auto ts = create(1, 2, 3, 4, 5);
auto ts2 = ts.dup();
auto ts = new RedBlackTree(1, 2, 3, 4, 5);
assert(ts.length == 5);
auto ts2 = ts.dup;
assert(ts2.length == 5);
assert(std.algorithm.equal(ts[], ts2[]));
ts2.insert(cast(Elem)6);
assert(!std.algorithm.equal(ts[], ts2[]));
assert(ts.length == 5 && ts2.length == 6);
}
/**
@ -4425,11 +4450,12 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
return _end.prev.value;
}
/**
* Check to see if an element exists in the container
*
* Complexity: $(BIGOH log(n))
*/
/++
$(D in) operator. Check to see if the given element exists in the
container.
Complexity: $(BIGOH log(n))
+/
bool opBinaryRight(string op)(Elem e) if (op == "in")
{
return _find(e) !is null;
@ -4437,19 +4463,28 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
static if(doUnittest) unittest
{
auto ts = create(1, 2, 3, 4, 5);
auto ts = new RedBlackTree(1, 2, 3, 4, 5);
assert(cast(Elem)3 in ts);
assert(cast(Elem)6 !in ts);
}
/**
* Clear the container of all elements
* Removes all elements from the container.
*
* Complexity: $(BIGOH 1)
*/
void clear()
{
_end.left = null;
_length = 0;
}
static if(doUnittest) unittest
{
auto ts = new RedBlackTree(1,2,3,4,5);
assert(ts.length == 5);
ts.clear();
assert(ts.empty && ts.length == 0);
}
/**
@ -4504,10 +4539,33 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
static if(doUnittest) unittest
{
auto ts = create(1,2,3,4,5);
assert(ts.stableInsert(cast(Elem[])[6, 7, 8, 9, 10]) == 5);
assert(ts.stableInsert(cast(Elem)11) == 1);
auto ts = new RedBlackTree(2,1,3,4,5,2,5);
static if(allowDuplicates)
{
assert(ts.length == 7);
assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 6);
assert(ts.length == 13);
assert(ts.stableInsert(cast(Elem)11) == 1 && ts.length == 14);
assert(ts.stableInsert(cast(Elem)7) == 1 && ts.length == 15);
static if(less == "a < b")
assert(ts.arrayEqual([1,2,2,3,4,5,5,6,7,7,8,8,9,10,11]));
else
assert(ts.arrayEqual([11,10,9,8,8,7,7,6,5,5,4,3,2,2,1]));
}
else
{
assert(ts.length == 5);
assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 5);
assert(ts.length == 10);
assert(ts.stableInsert(cast(Elem)11) == 1 && ts.length == 11);
assert(ts.stableInsert(cast(Elem)7) == 0 && ts.length == 11);
static if(less == "a < b")
assert(ts.arrayEqual([1,2,3,4,5,6,7,8,9,10,11]));
else
assert(ts.arrayEqual([11,10,9,8,7,6,5,4,3,2,1]));
}
}
/**
@ -4517,6 +4575,8 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
*/
Elem removeAny()
{
scope(success)
--_length;
auto n = _end.leftmost;
auto result = n.value;
n.remove(_end);
@ -4527,8 +4587,10 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
static if(doUnittest) unittest
{
auto ts = create(1,2,3,4,5);
auto ts = new RedBlackTree(1,2,3,4,5);
assert(ts.length == 5);
auto x = ts.removeAny();
assert(ts.length == 4);
Elem[] arr;
foreach(Elem i; 1..6)
if(i != x) arr ~= i;
@ -4542,6 +4604,8 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
*/
void removeFront()
{
scope(success)
--_length;
_end.leftmost.remove(_end);
version(RBDoChecks)
check();
@ -4554,6 +4618,8 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
*/
void removeBack()
{
scope(success)
--_length;
_end.prev.remove(_end);
version(RBDoChecks)
check();
@ -4561,19 +4627,29 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
static if(doUnittest) unittest
{
auto ts = create(1,2,3,4,5);
auto ts = new RedBlackTree(1,2,3,4,5);
assert(ts.length == 5);
ts.removeBack();
assert(ts.length == 4);
static if(less == "a < b")
assert(ts.arrayEqual([1,2,3,4]));
else
assert(ts.arrayEqual([2,3,4,5]));
ts.removeFront();
assert(ts.arrayEqual([2,3,4]));
assert(ts.arrayEqual([2,3,4]) && ts.length == 3);
}
/**
* Remove the given range from the container. Returns a range containing
* all the elements that were after the given range.
*
* Complexity: $(BIGOH m * log(n))
*/
/++
Removes the given range from the container.
Returns: A range containing all of the elements that were after the
given range.
Complexity: $(BIGOH m * log(n)) (where m is the number of elements in
the range)
+/
Range remove(Range r)
{
auto b = r._begin;
@ -4581,6 +4657,7 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
while(b !is e)
{
b = b.remove(_end);
--_length;
}
version(RBDoChecks)
check();
@ -4589,13 +4666,159 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
static if(doUnittest) unittest
{
auto ts = create(1,2,3,4,5);
auto ts = new RedBlackTree(1,2,3,4,5);
assert(ts.length == 5);
auto r = ts[];
r.popFront();
r.popBack();
assert(ts.length == 5);
auto r2 = ts.remove(r);
assert(ts.length == 2);
assert(ts.arrayEqual([1,5]));
static if(less == "a < b")
assert(std.algorithm.equal(r2, [5]));
else
assert(std.algorithm.equal(r2, [1]));
}
/++
Removes the given $(D Take!Range) from the container
Returns: A range containing all of the elements that were after the
given range.
Complexity: $(BIGOH m * log(n)) (where m is the number of elements in
the range)
+/
Range remove(Take!Range r)
{
auto b = r.original._begin;
while(!r.empty)
r.popFront(); // move take range to its last element
auto e = r.original._begin;
while(b != e)
{
b = b.remove(_end);
--_length;
}
return Range(e, _end);
}
static if(doUnittest) unittest
{
auto ts = new RedBlackTree(1,2,3,4,5);
auto r = ts[];
r.popFront();
assert(ts.length == 5);
auto r2 = ts.remove(take(r, 0));
static if(less == "a < b")
{
assert(std.algorithm.equal(r2, [2,3,4,5]));
auto r3 = ts.remove(take(r, 2));
assert(ts.arrayEqual([1,4,5]) && ts.length == 3);
assert(std.algorithm.equal(r3, [4,5]));
}
else
{
assert(std.algorithm.equal(r2, [4,3,2,1]));
auto r3 = ts.remove(take(r, 2));
assert(ts.arrayEqual([5,2,1]) && ts.length == 3);
assert(std.algorithm.equal(r3, [2,1]));
}
}
/++
Removes elements from the container that are equal to the given values
according to the less comparator. One element is removed for each value
given which is in the container. If $(D allowDuplicates) is true,
duplicates are removed only if duplicate values are given.
Returns: The number of elements removed.
Complexity: $(BIGOH m log(n)) (where m is the number of elements to remove)
Examples:
--------------------
auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7);
rbt.removeKey(1, 4, 7);
assert(std.algorithm.equal(rbt[], [0, 1, 1, 5]));
rbt.removeKey(1, 1, 0);
assert(std.algorithm.equal(rbt[], [5]));
--------------------
+/
size_t removeKey(U)(U[] elems...)
if(isImplicitlyConvertible!(U, Elem))
{
immutable lenBefore = length;
foreach(e; elems)
{
auto beg = _firstGreaterEqual(e);
if(beg is _end || _less(e, beg.value))
// no values are equal
continue;
beg.remove(_end);
--_length;
}
return lenBefore - length;
}
/++ Ditto +/
size_t removeKey(Stuff)(Stuff stuff)
if(isInputRange!Stuff &&
isImplicitlyConvertible!(ElementType!Stuff, Elem) &&
!is(Stuff == Elem[]))
{
//We use array in case stuff is a Range from this RedBlackTree - either
//directly or indirectly.
return removeKey(array(stuff));
}
static if(doUnittest) unittest
{
auto rbt = new RedBlackTree(5, 4, 3, 7, 2, 1, 7, 6, 2, 19, 45);
static if(allowDuplicates)
{
assert(rbt.length == 11);
assert(rbt.removeKey(cast(Elem)4) == 1 && rbt.length == 10);
assert(rbt.arrayEqual([1,2,2,3,5,6,7,7,19,45]) && rbt.length == 10);
assert(rbt.removeKey(cast(Elem)6, cast(Elem)2, cast(Elem)1) == 3);
assert(rbt.arrayEqual([2,3,5,7,7,19,45]) && rbt.length == 7);
assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 7);
assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 4);
static if(less == "a < b")
assert(std.algorithm.equal(rbt[], [7,7,19,45]));
else
assert(std.algorithm.equal(rbt[], [7,5,3,2]));
}
else
{
assert(rbt.length == 9);
assert(rbt.removeKey(cast(Elem)4) == 1 && rbt.length == 8);
assert(rbt.arrayEqual([1,2,3,5,6,7,19,45]));
assert(rbt.removeKey(cast(Elem)6, cast(Elem)2, cast(Elem)1) == 3);
assert(rbt.arrayEqual([3,5,7,19,45]) && rbt.length == 5);
assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 5);
assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 2);
static if(less == "a < b")
assert(std.algorithm.equal(rbt[], [19,45]));
else
assert(std.algorithm.equal(rbt[], [5,3]));
}
}
// find the first node where the value is > e
@ -4685,18 +4908,28 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
static if(doUnittest) unittest
{
auto ts = create(1, 2, 3, 4, 5);
auto r1 = ts.lowerBound(3);
assert(std.algorithm.equal(r1, [1,2]));
auto r2 = ts.upperBound(3);
assert(std.algorithm.equal(r2, [4,5]));
auto r3 = ts.equalRange(3);
assert(std.algorithm.equal(r3, [3]));
auto ts = new RedBlackTree(1, 2, 3, 4, 5);
auto rl = ts.lowerBound(3);
auto ru = ts.upperBound(3);
auto re = ts.equalRange(3);
static if(less == "a < b")
{
assert(std.algorithm.equal(rl, [1,2]));
assert(std.algorithm.equal(ru, [4,5]));
}
else
{
assert(std.algorithm.equal(rl, [5,4]));
assert(std.algorithm.equal(ru, [2,1]));
}
assert(std.algorithm.equal(re, [3]));
}
version(RBDoChecks)
{
/**
/*
* Print the tree. This prints a sideways view of the tree in ASCII form,
* with the number of indentations representing the level of the nodes.
* It does not print values, only the tree structure and color of nodes.
@ -4721,7 +4954,7 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
writeln();
}
/**
/*
* Check the tree for validity. This is called after every add or remove.
* This should only be enabled to debug the implementation of the RB Tree.
*/
@ -4777,6 +5010,11 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
}
}
/+
For the moment, using templatized contstructors doesn't seem to work
very well (likely due to bug# 436 and/or bug# 1528). The redBlackTree
helper function seems to do the job well enough though.
/**
* Constructor. Pass in an array of elements, or individual elements to
* initialize the tree with.
@ -4795,17 +5033,136 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string))
_setup();
stableInsert(stuff);
}
+/
/++ +/
this()
{
_setup();
}
/++
Constructor. Pass in an array of elements, or individual elements to
initialize the tree with.
+/
this(Elem[] elems...)
{
_setup();
stableInsert(elems);
}
private this(Node end, size_t length)
{
_end = end;
_length = length;
}
}
//Verify Example for removeKey.
unittest
{
auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7);
rbt.removeKey(1, 4, 7);
assert(std.algorithm.equal(rbt[], [0, 1, 1, 5]));
rbt.removeKey(1, 1, 0);
assert(std.algorithm.equal(rbt[], [5]));
}
unittest
{
RedBlackTree!uint rt1;
RedBlackTree!int rt2;
RedBlackTree!ushort rt3;
RedBlackTree!short rt4;
RedBlackTree!ubyte rt5;
RedBlackTree!byte rt6;
void test(T)()
{
auto rt1 = new RedBlackTree!(T, "a < b", false)();
auto rt2 = new RedBlackTree!(T, "a < b", true)();
auto rt3 = new RedBlackTree!(T, "a > b", false)();
auto rt4 = new RedBlackTree!(T, "a > b", true)();
}
test!long();
test!ulong();
test!int();
test!uint();
test!short();
test!ushort();
test!byte();
test!byte();
}
/++
Convenience function for creating a $(D RedBlackTree!E) from a list of
values.
Examples:
--------------------
auto rbt1 = redBlackTree(0, 1, 5, 7);
auto rbt2 = redBlackTree!string("hello", "world");
auto rbt3 = redBlackTree!true(0, 1, 5, 7, 5);
auto rbt4 = redBlackTree!"a > b"(0, 1, 5, 7);
auto rbt5 = redBlackTree!("a > b", true)(0.1, 1.3, 5.9, 7.2, 5.9);
--------------------
+/
auto redBlackTree(E)(E[] elems...)
{
return new RedBlackTree!E(elems);
}
/++ Ditto +/
auto redBlackTree(bool allowDuplicates, E)(E[] elems...)
{
return new RedBlackTree!(E, "a < b", allowDuplicates)(elems);
}
/++ Ditto +/
auto redBlackTree(alias less, E)(E[] elems...)
{
return new RedBlackTree!(E, less)(elems);
}
/++ Ditto +/
auto redBlackTree(alias less, bool allowDuplicates, E)(E[] elems...)
if(is(typeof(binaryFun!less(E.init, E.init))))
{
//We shouldn't need to instantiate less here, but for some reason,
//dmd can't handle it if we don't (even though the template which
//takes less but not allowDuplicates works just fine).
return new RedBlackTree!(E, binaryFun!less, allowDuplicates)(elems);
}
//Verify Examples.
unittest
{
auto rbt1 = redBlackTree(0, 1, 5, 7);
auto rbt2 = redBlackTree!string("hello", "world");
auto rbt3 = redBlackTree!true(0, 1, 5, 7, 5);
auto rbt4 = redBlackTree!"a > b"(0, 1, 5, 7);
auto rbt5 = redBlackTree!("a > b", true)(0.1, 1.3, 5.9, 7.2, 5.9);
}
//Combinations not in examples.
unittest
{
auto rbt1 = redBlackTree!(true, string)("hello", "hello");
auto rbt2 = redBlackTree!((a, b){return a < b;}, double)(5.1, 2.3);
auto rbt3 = redBlackTree!("a > b", true, string)("hello", "world");
}
unittest
{
auto rt1 = redBlackTree(5, 4, 3, 2, 1);
assert(rt1.length == 5);
assert(array(rt1[]) == [1, 2, 3, 4, 5]);
auto rt2 = redBlackTree!"a > b"(1.1, 2.1);
assert(rt2.length == 2);
assert(array(rt2[]) == [2.1, 1.1]);
auto rt3 = redBlackTree!true(5, 5, 4);
assert(rt3.length == 3);
assert(array(rt3[]) == [4, 5, 5]);
auto rt4 = redBlackTree!string("hello", "hello");
assert(rt4.length == 1);
assert(array(rt4[]) == ["hello"]);
}
version(unittest) struct UnittestMe {