diff --git a/std/array.d b/std/array.d deleted file mode 100644 index c0f466c37..000000000 --- a/std/array.d +++ /dev/null @@ -1,1599 +0,0 @@ -// Written in the D programming language. -/** -Functions and types that manipulate built-in arrays. - -Copyright: Copyright Andrei Alexandrescu 2008-. - -License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). - -Authors: $(WEB erdani.org, Andrei Alexandrescu) - -Source: $(PHOBOSSRC std/_array.d) -*/ -module std.array; - -import core.memory; -import std.algorithm, std.conv, std.ctype, std.encoding, std.exception, - std.intrinsic, std.range, std.string, std.traits, std.typecons, std.utf; -import std.c.string : memcpy; -version(unittest) import std.stdio, std.typetuple; - -/** -Returns a newly-allocated dynamic array consisting of a copy of the -input range, static array, dynamic array, or class or struct with an -$(D opApply) function $(D r). Note that narrow strings are handled as -a special case in an overload. - -Example: - ----- -auto a = array([1, 2, 3, 4, 5][]); -assert(a == [ 1, 2, 3, 4, 5 ]); ----- - */ -ForeachType!Range[] array(Range)(Range r) -if (isIterable!Range && !isNarrowString!Range) -{ - alias ForeachType!Range E; - static if (hasLength!Range) - { - if(r.length == 0) return null; - - // Determines whether the GC should scan the array. - auto blkInfo = (typeid(E).flags & 1) ? - cast(GC.BlkAttr) 0 : - GC.BlkAttr.NO_SCAN; - - auto result = (cast(E*) enforce(GC.malloc(r.length * E.sizeof, blkInfo), - text("Out of memory while allocating an array of ", r.length, - " objects of type ", E.stringof)))[0 .. r.length]; - size_t i = 0; - foreach (e; r) - { - // hacky - static if (is(typeof(e.opAssign(e)))) - { - // this should be in-place construction - auto voidArr = (cast(void*) (result.ptr + i))[0..E.sizeof]; - emplace!E(voidArr, e); - } - else - { - result[i] = e; - } - i++; - } - return result; - } - else - { - auto a = appender!(E[])(); - foreach (e; r) - { - a.put(e); - } - return a.data; - } -} - -/** -Convert a narrow string to an array type that fully supports random access. -This is handled as a special case and always returns a $(D dchar[]), -$(D const(dchar)[]), or $(D immutable(dchar)[]) depending on the constness of -the input. -*/ -ElementType!String[] array(String)(String str) if (isNarrowString!String) -{ - return to!(typeof(return))(str); -} - -unittest -{ - static struct TestArray { int x; string toString() { return .to!string(x); } } - - static struct OpAssign - { - uint num; - this(uint num) { this.num = num; } - - // Templating opAssign to make sure the bugs with opAssign being - // templated are fixed. - void opAssign(T)(T rhs) { this.num = rhs.num; } - } - - static struct OpApply - { - int opApply(int delegate(ref int) dg) - { - int res; - foreach(i; 0..10) - { - res = dg(i); - if(res) break; - } - - return res; - } - } - - auto a = array([1, 2, 3, 4, 5][]); - //writeln(a); - assert(a == [ 1, 2, 3, 4, 5 ]); - - auto b = array([TestArray(1), TestArray(2)][]); - //writeln(b); - - class C - { - int x; - this(int y) { x = y; } - override string toString() { return .to!string(x); } - } - auto c = array([new C(1), new C(2)][]); - //writeln(c); - - auto d = array([1., 2.2, 3][]); - assert(is(typeof(d) == double[])); - //writeln(d); - - auto e = [OpAssign(1), OpAssign(2)]; - auto f = array(e); - assert(e == f); - - assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]); - assert(array("ABC") == "ABC"d); - assert(array("ABC".dup) == "ABC"d.dup); -} - -/** -Implements the range interface primitive $(D empty) for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, $(D array.empty) is -equivalent to $(D empty(array)). - -Example: ----- -auto a = [ 1, 2, 3 ]; -assert(!a.empty); -assert(a[3 .. $].empty); ----- - */ - -@property bool empty(T)(in T[] a) @safe pure nothrow -{ - return !a.length; -} - -unittest -{ - auto a = [ 1, 2, 3 ]; - assert(!a.empty); - assert(a[3 .. $].empty); -} - -/** -Implements the range interface primitive $(D save) for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, $(D array.save) is -equivalent to $(D save(array)). The function does not duplicate the -content of the array, it simply returns its argument. - -Example: ----- -auto a = [ 1, 2, 3 ]; -auto b = a.save; -assert(b is a); ----- - */ - -@property T[] save(T)(T[] a) @safe pure nothrow -{ - return a; -} - -/** -Implements the range interface primitive $(D popFront) for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, $(D array.popFront) is -equivalent to $(D popFront(array)). For $(GLOSSARY narrow strings), -$(D popFront) automaticaly advances to the next $(GLOSSARY code -point). - -Example: ----- -int[] a = [ 1, 2, 3 ]; -a.popFront(); -assert(a == [ 2, 3 ]); ----- -*/ - -void popFront(A)(ref A a) -if (!isNarrowString!A && isDynamicArray!A && isMutable!A && !is(A == void[])) -{ - assert(a.length, "Attempting to popFront() past the end of an array of " - ~ typeof(a[0]).stringof); - a = a[1 .. $]; -} - -unittest -{ - auto a = [ 1, 2, 3 ]; - a.popFront(); - assert(a == [ 2, 3 ]); - static assert(!__traits(compiles, popFront!(immutable int[]))); - static assert(!__traits(compiles, popFront!(void[]))); -} - -// Specialization for narrow strings. The necessity of -// !isStaticArray!A suggests a compiler @@@BUG@@@. -void popFront(A)(ref A a) -if (isNarrowString!A && isMutable!A && !isStaticArray!A) -{ - assert(a.length, "Attempting to popFront() past the end of an array of " - ~ typeof(a[0]).stringof); - a = a[std.utf.stride(a, 0) .. $]; -} - -unittest -{ - string s1 = "\xC2\xA9hello"; - s1.popFront(); - assert(s1 == "hello"); - wstring s2 = "\xC2\xA9hello"; - s2.popFront(); - assert(s2 == "hello"); - string s3 = "\u20AC100"; - //write(s3, '\n'); - - static assert(!__traits(compiles, popFront!(immutable string))); -} - -/** -Implements the range interface primitive $(D popBack) for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, $(D array.popBack) is -equivalent to $(D popBack(array)). For $(GLOSSARY narrow strings), $(D -popFront) automaticaly eliminates the last $(GLOSSARY code point). - - -Example: ----- -int[] a = [ 1, 2, 3 ]; -a.popBack(); -assert(a == [ 1, 2 ]); ----- -*/ - -void popBack(A)(ref A a) -if (isDynamicArray!A && !isNarrowString!A && isMutable!A && !is(A == void[])) -{ - assert(a.length); - a = a[0 .. $ - 1]; -} - -unittest -{ - auto a = [ 1, 2, 3 ]; - a.popBack(); - assert(a == [ 1, 2 ]); - static assert(!__traits(compiles, popBack!(immutable int[]))); - static assert(!__traits(compiles, popBack!(void[]))); -} - -// Specialization for arrays of char -@trusted void popBack(A)(ref A a) -if (is(A : const(char)[]) && isMutable!A) -{ - immutable n = a.length; - const p = a.ptr + n; - if (n >= 1 && (p[-1] & 0b1100_0000) != 0b1000_0000) - { - a = a[0 .. n - 1]; - } - else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000) - { - a = a[0 .. n - 2]; - } - else if (n >= 3 && (p[-3] & 0b1100_0000) != 0b1000_0000) - { - a = a[0 .. n - 3]; - } - else if (n >= 4 && (p[-4] & 0b1100_0000) != 0b1000_0000) - { - a = a[0 .. n - 4]; - } - else - { - throw new UtfException("Invalid UTF character at end of string"); - } -} - -unittest -{ - string s = "hello\xE2\x89\xA0"; - s.popBack(); - assert(s == "hello", s); - string s3 = "\xE2\x89\xA0"; - auto c = s3.back; - assert(c == cast(dchar)'\u2260'); - s3.popBack(); - assert(s3 == ""); - - static assert(!__traits(compiles, popBack!(immutable char[]))); -} - -// Specialization for arrays of wchar -@trusted void popBack(A)(ref A a) -if (is(A : const(wchar)[]) && isMutable!A) -{ - assert(a.length); - if (a.length <= 1) // this is technically == but costs nothing and is safer - { - a = a[0 .. 0]; - return; - } - // We can go commando from here on, we're safe; length is > 1 - immutable c = a.ptr[a.length - 2]; - a = a.ptr[0 .. a.length - 1 - (c >= 0xD800 && c <= 0xDBFF)]; -} - -unittest -{ - wstring s = "hello\xE2\x89\xA0"; - s.popBack(); - assert(s == "hello"); - - static assert(!__traits(compiles, popBack!(immutable wchar[]))); -} - -/** -Implements the range interface primitive $(D front) for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, $(D array.front) is -equivalent to $(D front(array)). For $(GLOSSARY narrow strings), $(D -front) automaticaly returns the first $(GLOSSARY code point) as a $(D -dchar). - - -Example: ----- -int[] a = [ 1, 2, 3 ]; -assert(a.front == 1); ----- -*/ -ref T front(T)(T[] a) -if (!isNarrowString!(T[]) && !is(T[] == void[])) -{ - assert(a.length, "Attempting to fetch the front of an empty array"); - return a[0]; -} - -dchar front(A)(A a) if (isNarrowString!A) -{ - assert(a.length, "Attempting to fetch the front of an empty array"); - size_t i = 0; - return decode(a, i); -} - -unittest -{ - auto a = [ 1, 2 ]; - a.front = 4; - assert(a.front == 4); - assert(a == [ 4, 2 ]); -} - -/** -Implements the range interface primitive $(D back) for built-in -arrays. Due to the fact that nonmember functions can be called with -the first argument using the dot notation, $(D array.back) is -equivalent to $(D back(array)). For $(GLOSSARY narrow strings), $(D -back) automaticaly returns the last $(GLOSSARY code point) as a $(D -dchar). - -Example: ----- -int[] a = [ 1, 2, 3 ]; -assert(a.back == 3); ----- -*/ -ref T back(T)(T[] a) if (!isNarrowString!(T[])) -{ - assert(a.length, "Attempting to fetch the back of an empty array"); - return a[$ - 1]; -} - -unittest -{ - int[] a = [ 1, 2, 3 ]; - assert(a.back == 3); - a.back += 4; - assert(a.back == 7); -} - -// Specialization for strings -dchar back(A)(A a) -if (isDynamicArray!A && isNarrowString!A) -{ - auto n = a.length; - const p = a.ptr + n; - if (n >= 1 && (p[-1] & 0b1100_0000) != 0b1000_0000) - { - --n; - } - else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000) - { - n -= 2; - } - else if (n >= 3 && (p[-3] & 0b1100_0000) != 0b1000_0000) - { - n -= 3; - } - else if (n >= 4 && (p[-4] & 0b1100_0000) != 0b1000_0000) - { - n -= 4; - } - else - { - throw new UtfException(a.length - ? "Invalid UTF character at end of string" - : "Attempting to fetch the back of an empty array"); - } - return decode(a, n); -} - -// overlap -/* -Returns the overlapping portion, if any, of two arrays. Unlike $(D -equal), $(D overlap) only compares the pointers in the ranges, not the -values referred by them. If $(D r1) and $(D r2) have an overlapping -slice, returns that slice. Otherwise, returns the null slice. - -Example: ----- -int[] a = [ 10, 11, 12, 13, 14 ]; -int[] b = a[1 .. 3]; -assert(overlap(a, b) == [ 11, 12 ]); -b = b.dup; -// overlap disappears even though the content is the same -assert(overlap(a, b).empty); ----- -*/ -T[] overlap(T)(T[] r1, T[] r2) @trusted pure nothrow -{ - static T* max(T* a, T* b) nothrow { return a > b ? a : b; } - static T* min(T* a, T* b) nothrow { return a < b ? a : b; } - auto b = max(r1.ptr, r2.ptr); - auto e = min(r1.ptr + r1.length, r2.ptr + r2.length); - return b < e ? b[0 .. e - b] : null; -} - -unittest -{ - int[] a = [ 10, 11, 12, 13, 14 ]; - int[] b = a[1 .. 3]; - a[1] = 100; - assert(overlap(a, b) == [ 100, 12 ]); - - assert(overlap(a, a[0 .. 2]) is a[0 .. 2]); - assert(overlap(a, a[3 .. 5]) is a[3 .. 5]); - assert(overlap(a[0 .. 2], a) is a[0 .. 2]); - assert(overlap(a[3 .. 5], a) is a[3 .. 5]); - - assert(overlap(a, b.dup).empty); -} - -/** -Inserts $(D stuff) (which must be an input range or a single item) in -$(D array) at position $(D pos). - -Example: ---- -int[] a = [ 1, 2, 3, 4 ]; -a.insert(2, [ 1, 2 ]); -assert(a == [ 1, 2, 1, 2, 3, 4 ]); ---- - */ -void insert(T, Range)(ref T[] array, size_t pos, Range stuff) -if (isInputRange!Range && is(ElementEncodingType!Range : T)) -{ - static if (hasLength!Range) - { - immutable - delta = stuff.length, - oldLength = array.length, - newLength = oldLength + delta; - - // Reallocate the array to make space for new content - array = (cast(T*) core.memory.GC.realloc(array.ptr, - newLength * array[0].sizeof))[0 .. newLength]; - assert(array.length == newLength); - - // Move data in pos .. pos + stuff.length to the end of the array - foreach_reverse (i; pos .. oldLength) - { - // This will be guaranteed to not throw - move(array[i], array[i + delta]); - } - - // Copy stuff into array - copy(stuff, array[pos .. pos + stuff.length]); - } - else - { - auto app = appender!(T[])(); - app.put(array[0 .. pos]); - app.put(stuff); - app.put(array[pos .. $]); - array = app.data; - } -} - -/// Ditto -void insert(T)(ref T[] array, size_t pos, T stuff) -{ - return insert(array, pos, (&stuff)[0 .. 1]); -} - -unittest -{ - int[] a = ([1, 4, 5]).dup; - insert(a, 1u, [2, 3]); - assert(a == [1, 2, 3, 4, 5]); - insert(a, 1u, 99); - assert(a == [1, 99, 2, 3, 4, 5]); -} - -// @@@ TODO: document this -pure bool sameHead(T)(in T[] lhs, in T[] rhs) -{ - return lhs.ptr == rhs.ptr; -} - -/******************************************** -Returns an array that consists of $(D s) (which must be an input -range) repeated $(D n) times. This function allocates, fills, and -returns a new array. For a lazy version, refer to $(XREF -range,repeat). - */ -S replicate(S)(S s, size_t n) if (isDynamicArray!S) -{ - // Optimization for return join(std.range.repeat(s, n)); - if (n == 0) - return S.init; - if (n == 1) - return s; - auto r = new Unqual!(typeof(s[0]))[n * s.length]; - if (s.length == 1) - r[] = s[0]; - else - { - immutable len = s.length, nlen = n * len; - for (size_t i = 0; i < nlen; i += len) - { - r[i .. i + len] = s[]; - } - } - return cast(S) r; -} - -ElementType!S[] replicate(S)(S s, size_t n) -if (isInputRange!S && !isDynamicArray!S) -{ - return join(std.range.repeat(s, n)); -} - -unittest -{ - debug(std_array) printf("array.repeat.unittest\n"); - - foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])) - { - S s; - - s = replicate(to!S("1234"), 0); - assert(s is null); - s = replicate(to!S("1234"), 1); - assert(cmp(s, "1234") == 0); - s = replicate(to!S("1234"), 2); - assert(cmp(s, "12341234") == 0); - s = replicate(to!S("1"), 4); - assert(cmp(s, "1111") == 0); - s = replicate(cast(S) null, 4); - assert(s is null); - } - - int[] a = [ 1, 2, 3 ]; - assert(replicate(a, 3) == [1, 2, 3, 1, 2, 3, 1, 2, 3]); -} - -/************************************** -Split the string $(D s) into an array of words, using whitespace as -delimiter. Runs of whitespace are merged together (no empty words are -produced). - */ - -S[] split(S)(S s) if (isSomeString!S) -{ - size_t istart; - bool inword = false; - S[] result; - - foreach (i; 0 .. s.length) - { - switch (s[i]) - { - case ' ': case '\t': case '\f': case '\r': case '\n': case '\v': - if (inword) - { - result ~= s[istart .. i]; - inword = false; - } - break; - default: - if (!inword) - { - istart = i; - inword = true; - } - break; - } - } - if (inword) - result ~= s[istart .. $]; - return result; -} - -unittest -{ - foreach (S; TypeTuple!(string, wstring, dstring)) - { - debug(string) printf("string.split1\n"); - S s = " \t\npeter paul\tjerry \n"; - assert(equal(split(s), [ to!S("peter"), to!S("paul"), to!S("jerry") ])); - } -} - -/** -Splits a string by whitespace. - -Example: - ----- -auto a = " a bcd ef gh "; -assert(equal(splitter(a), ["", "a", "bcd", "ef", "gh"][])); ----- - */ -auto splitter(String)(String s) if (isSomeString!String) -{ - return std.algorithm.splitter!isspace(s); -} - -unittest -{ - auto a = " a bcd ef gh "; - assert(equal(splitter(a), ["", "a", "bcd", "ef", "gh"][])); - a = ""; - assert(splitter(a).empty); -} - -/************************************** - * Splits $(D s) into an array, using $(D delim) as the delimiter. - */ -Unqual!(S1)[] split(S1, S2)(S1 s, S2 delim) -if (isForwardRange!(Unqual!S1) && isForwardRange!S2) -{ - Unqual!S1 us = s; - auto app = appender!(Unqual!(S1)[])(); - foreach (word; std.algorithm.splitter(us, delim)) - { - app.put(word); - } - return app.data; -} - -unittest -{ - debug(std_array) printf("array.split\n"); - foreach (S; TypeTuple!(string, wstring, dstring, - immutable(string), immutable(wstring), immutable(dstring), - char[], wchar[], dchar[], - const(char)[], const(wchar)[], const(dchar)[])) - { - S s = to!S(",peter,paul,jerry,"); - int i; - - auto words = split(s, ","); - assert(words.length == 5, text(words.length)); - i = cmp(words[0], ""); - assert(i == 0); - i = cmp(words[1], "peter"); - assert(i == 0); - i = cmp(words[2], "paul"); - assert(i == 0); - i = cmp(words[3], "jerry"); - assert(i == 0); - i = cmp(words[4], ""); - assert(i == 0); - - auto s1 = s[0 .. s.length - 1]; // lop off trailing ',' - words = split(s1, ","); - assert(words.length == 4); - i = cmp(words[3], "jerry"); - assert(i == 0); - - auto s2 = s1[1 .. s1.length]; // lop off leading ',' - words = split(s2, ","); - assert(words.length == 3); - i = cmp(words[0], "peter"); - assert(i == 0); - - auto s3 = to!S(",,peter,,paul,,jerry,,"); - - words = split(s3, ",,"); - //printf("words.length = %d\n", words.length); - assert(words.length == 5); - i = cmp(words[0], ""); - assert(i == 0); - i = cmp(words[1], "peter"); - assert(i == 0); - i = cmp(words[2], "paul"); - assert(i == 0); - i = cmp(words[3], "jerry"); - assert(i == 0); - i = cmp(words[4], ""); - assert(i == 0); - - auto s4 = s3[0 .. s3.length - 2]; // lop off trailing ',,' - words = split(s4, ",,"); - assert(words.length == 4); - i = cmp(words[3], "jerry"); - assert(i == 0); - - auto s5 = s4[2 .. s4.length]; // lop off leading ',,' - words = split(s5, ",,"); - assert(words.length == 3); - i = cmp(words[0], "peter"); - assert(i == 0); - } -} - -/******************************************** - * Concatenate all the ranges in $(D ror) together into one array; - * use $(D sep) as the separator if present, otherwise none. - */ -ElementEncodingType!(ElementType!RoR)[] -join(RoR, R)(RoR ror, R sep) -if (isInputRange!RoR && isInputRange!(ElementType!RoR) && isForwardRange!R) -{ - if (ror.empty) return typeof(return).init; - auto iter = joiner(ror, sep); - static if (isForwardRange!RoR && hasLength!RoR - && (hasLength!(ElementType!RoR) || isSomeString!(ElementType!RoR)) - && hasLength!R) - { - immutable resultLen = reduce!"a + b.length"(cast(size_t) 0, ror.save) - + sep.length * (ror.length - 1); - auto result = new ElementEncodingType!(ElementType!RoR)[resultLen]; - copy(iter, result); - return result; - } - else - { - return copy(iter, appender!(typeof(return))).data; - } -} - -/// Ditto -ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) -if (isInputRange!RoR && isInputRange!(ElementType!RoR)) -{ - auto iter = joiner(ror); - static if (hasLength!RoR && hasLength!(ElementType!RoR)) - { - immutable resultLen = reduce!"a + b.length"(cast(size_t) 0, ror.save); - auto result = new Unqual!(ElementEncodingType!(ElementType!RoR))[resultLen]; - copy(iter, result); - return cast(typeof(return)) result; - } - else - { - return copy(iter, appender!(typeof(return))).data; - } -} - -unittest -{ - debug(std_array) printf("array.join.unittest\n"); - - string word1 = "peter"; - string word2 = "paul"; - string word3 = "jerry"; - string[3] words; - string r; - int i; - - words[0] = word1; - words[1] = word2; - words[2] = word3; - r = join(words[], ","); - i = cmp(r, "peter,paul,jerry"); - assert(i == 0, text(i)); - - assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]); - assert(join([[1, 2], [41, 42]]) == [1, 2, 41, 42]); -} - -/** -Replaces elements from $(D array) with indices ranging from $(D from) -(inclusive) to $(D to) (exclusive) with the range $(D stuff). Expands -or shrinks the array as needed. - -Example: ---- -int[] a = [ 1, 2, 3, 4 ]; -a.replace(1, 3, [ 9, 9, 9 ]); -assert(a == [ 1, 9, 9, 9, 4 ]); ---- - */ -void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff) -if (isDynamicArray!Range && is(ElementType!Range : T)) -{ - if (overlap(array, stuff)) - { - // use slower/conservative method - array = array[0 .. from] ~ stuff ~ array[to .. $]; - } - else if (stuff.length <= to - from) - { - // replacement reduces length - // BUG 2128 - //immutable stuffEnd = from + stuff.length; - auto stuffEnd = from + stuff.length; - array[from .. stuffEnd] = stuff; - array = remove(array, tuple(stuffEnd, to)); - } - else - { - // replacement increases length - // @@@TODO@@@: optimize this - immutable replaceLen = to - from; - array[from .. to] = stuff[0 .. replaceLen]; - insert(array, to, stuff[replaceLen .. $]); - } -} - -unittest -{ - int[] a = [1, 4, 5]; - replace(a, 1u, 2u, [2, 3, 4]); - assert(a == [1, 2, 3, 4, 5]); - replace(a, 1u, 2u, cast(int[])[]); - assert(a == [1, 3, 4, 5]); -} - -/******************************************** -Replace occurrences of $(D from) with $(D to) in $(D a). Returns a new -array without changing the contents of $(D subject). - */ -R1 replace(R1, R2, R3)(R1 subject, R2 from, R3 to) -if (isDynamicArray!R1 && isForwardRange!R2 && isForwardRange!R3 - && (hasLength!R3 || isSomeString!R3)) -{ - if (from.empty) return subject; - auto app = appender!R1(); - - for (;;) - { - auto balance = std.algorithm.find(subject, from.save); - if (balance.empty) - { - if (app.data.empty) return subject; - app.put(subject); - break; - } - app.put(subject[0 .. subject.length - balance.length]); - app.put(to.save); - subject = balance[from.length .. $]; - } - - return app.data; -} - -unittest -{ - debug(string) printf("array.replace.unittest\n"); - - alias TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[]) - TestTypes; - - foreach (S; TestTypes) - { - auto s = to!S("This is a foo foo list"); - auto from = to!S("foo"); - auto into = to!S("silly"); - S r; - int i; - - r = replace(s, from, into); - i = cmp(r, "This is a silly silly list"); - assert(i == 0); - - r = replace(s, to!S(""), into); - i = cmp(r, "This is a foo foo list"); - assert(i == 0); - - assert(replace(r, to!S("won't find this"), to!S("whatever")) is r); - } -} - -/******************************************** -Replaces the first occurrence of $(D from) with $(D to) in $(D -a). Returns a new array without changing the contents of $(D subject). - */ -R1 replaceFirst(R1, R2, R3)(R1 subject, R2 from, R3 to) -if (isDynamicArray!R1 && isForwardRange!R2 && isInputRange!R3) -{ - if (from.empty) return subject; - auto balance = std.algorithm.find(subject, from.save); - if (balance.empty) return subject; - auto app = appender!R1(); - app.put(subject[0 .. subject.length - balance.length]); - app.put(to.save); - subject = balance[from.length .. $]; - - return app.data; -} - -unittest -{ - debug(string) printf("array.replaceFirst.unittest\n"); - - alias TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[]) - TestTypes; - - foreach (S; TestTypes) - { - auto s = to!S("This is a foo foo list"); - auto from = to!S("foo"); - auto into = to!S("silly"); - S r; - int i; - - r = replace(s, from, into); - i = cmp(r, "This is a silly silly list"); - assert(i == 0); - - r = replace(s, to!S(""), into); - i = cmp(r, "This is a foo foo list"); - assert(i == 0); - - assert(replace(r, to!S("won't find this"), to!S("whatever")) is r); - } -} - -/***************************** -Return an array that is $(D s) with $(D slice) replaced by $(D -replacement[]). - */ - -T[] replaceSlice(T)(T[] s, in T[] slice, in T[] replacement) -in -{ - // Verify that slice[] really is a slice of s[] - assert(overlap(s, slice) is slice); -} -body -{ - auto result = new Unqual!(typeof(s[0]))[ - s.length - slice.length + replacement.length]; - immutable so = slice.ptr - s.ptr; - result[0 .. so] = s[0 .. so]; - result[so .. so + replacement.length] = replacement; - result[so + replacement.length .. result.length] = - s[so + slice.length .. s.length]; - - return cast(T[]) result; -} - -unittest -{ - debug(std_array) printf("array.replaceSlice.unittest\n"); - - string s = "hello"; - string slice = s[2 .. 4]; - - auto r = replaceSlice(s, slice, "bar"); - int i; - i = cmp(r, "hebaro"); - assert(i == 0); -} - -/** -Implements an output range that appends data to an array. This is -recommended over $(D a ~= data) when appending many elements because it is more -efficient. - -Example: ----- -auto app = appender!string(); -string b = "abcdefg"; -foreach (char c; b) app.put(c); -assert(app.data == "abcdefg"); - -int[] a = [ 1, 2 ]; -auto app2 = appender(a); -app2.put(3); -app2.put([ 4, 5, 6 ]); -assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]); ----- - */ - -struct Appender(A : T[], T) -{ - private struct Data - { - size_t capacity; - Unqual!(T)[] arr; - } - - private Data* _data; - -/** -Construct an appender with a given array. Note that this does not copy the -data. If the array has a larger capacity as determined by arr.capacity, -it will be used by the appender. After initializing an appender on an array, -appending to the original array will reallocate. -*/ - this(T[] arr) - { - // initialize to a given array. - _data = new Data; - _data.arr = cast(Unqual!(T)[])arr; - - // We want to use up as much of the block the array is in as possible. - // if we consume all the block that we can, then array appending is - // safe WRT built-in append, and we can use the entire block. - auto cap = arr.capacity; - if(cap > arr.length) - arr.length = cap; - // we assume no reallocation occurred - assert(arr.ptr is _data.arr.ptr); - _data.capacity = arr.length; - } - -/** -Reserve at least newCapacity elements for appending. Note that more elements -may be reserved than requested. If newCapacity < capacity, then nothing is -done. -*/ - void reserve(size_t newCapacity) - { - if(!_data) - _data = new Data; - if(_data.capacity < newCapacity) - { - // need to increase capacity - immutable len = _data.arr.length; - immutable growsize = (newCapacity - len) * T.sizeof; - auto u = GC.extend(_data.arr.ptr, growsize, growsize); - if(u) - { - // extend worked, update the capacity - _data.capacity = u / T.sizeof; - } - else - { - // didn't work, must reallocate - auto bi = GC.qalloc(newCapacity * T.sizeof, - (typeid(T[]).next.flags & 1) ? 0 : GC.BlkAttr.NO_SCAN); - _data.capacity = bi.size / T.sizeof; - if(len) - memcpy(bi.base, _data.arr.ptr, len * T.sizeof); - _data.arr = (cast(Unqual!(T)*)bi.base)[0..len]; - // leave the old data, for safety reasons - } - } - } - -/** -Returns the capacity of the array (the maximum number of elements the -managed array can accommodate before triggering a reallocation). If any -appending will reallocate, capacity returns 0. - */ - @property size_t capacity() - { - return _data ? _data.capacity : 0; - } - -/** -Returns the managed array. - */ - @property T[] data() - { - return cast(typeof(return))(_data ? _data.arr : null); - } - - // ensure we can add nelems elements, resizing as necessary - private void ensureAddable(size_t nelems) - { - if(!_data) - _data = new Data; - immutable len = _data.arr.length; - immutable reqlen = len + nelems; - if (reqlen > _data.capacity) - { - // Time to reallocate. - // We need to almost duplicate what's in druntime, except we - // have better access to the capacity field. - auto newlen = newCapacity(reqlen); - // first, try extending the current block - auto u = GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof); - if(u) - { - // extend worked, update the capacity - _data.capacity = u / T.sizeof; - } - else - { - // didn't work, must reallocate - auto bi = GC.qalloc(newlen * T.sizeof, - (typeid(T[]).next.flags & 1) ? 0 : GC.BlkAttr.NO_SCAN); - _data.capacity = bi.size / T.sizeof; - if(len) - memcpy(bi.base, _data.arr.ptr, len * T.sizeof); - _data.arr = (cast(Unqual!(T)*)bi.base)[0..len]; - // leave the old data, for safety reasons - } - } - } - - private static size_t newCapacity(size_t newlength) - { - long mult = 100 + (1000L) / (bsr(newlength * T.sizeof) + 1); - // limit to doubling the length, we don't want to grow too much - if(mult > 200) - mult = 200; - auto newext = cast(size_t)((newlength * mult + 99) / 100); - return newext > newlength ? newext : newlength; - } - -/** -Appends one item to the managed array. - */ - void put(U)(U item) if (isImplicitlyConvertible!(U, T) || - isSomeChar!T && isSomeChar!U) - { - static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof) - { - // must do some transcoding around here - Unqual!T[T.sizeof == 1 ? 4 : 2] encoded; - auto len = std.utf.encode(encoded, item); - put(encoded[0 .. len]); - } - else - { - ensureAddable(1); - immutable len = _data.arr.length; - _data.arr.ptr[len] = cast(Unqual!T)item; - _data.arr = _data.arr.ptr[0 .. len + 1]; - } - } - - // Const fixing hack. - void put(Range)(Range items) - if(isInputRange!(Unqual!Range) && !isInputRange!Range) { - alias put!(Unqual!Range) p; - p(items); - } - -/** -Appends an entire range to the managed array. - */ - void put(Range)(Range items) if (isInputRange!Range - && is(typeof(Appender.init.put(items.front)))) - { - // note, we disable this branch for appending one type of char to - // another because we can't trust the length portion. - static if (!(isSomeChar!T && isSomeChar!(ElementType!Range) && - !is(Range == Unqual!(T)[])) && - is(typeof(items.length) == size_t)) - { - // optimization -- if this type is something other than a string, - // and we are adding exactly one element, call the version for one - // element. - static if(!isSomeChar!T) - { - if(items.length == 1) - { - put(items.front); - return; - } - } - - // make sure we have enough space, then add the items - ensureAddable(items.length); - immutable len = _data.arr.length; - immutable newlen = len + items.length; - _data.arr = _data.arr.ptr[0..newlen]; - static if(is(typeof(_data.arr[] = items))) - { - _data.arr.ptr[len..newlen] = items; - } - else - { - for(size_t i = len; !items.empty; items.popFront(), ++i) - _data.arr.ptr[i] = cast(Unqual!T)items.front; - } - } - else - { - //pragma(msg, Range.stringof); - // Generic input range - for (; !items.empty; items.popFront()) - { - put(items.front); - } - } - } - - // only allow overwriting data on non-immutable and non-const data - static if(!is(T == immutable) && !is(T == const)) - { -/** -Clears the managed array. -*/ - void clear() - { - if (_data) - { - _data.arr = _data.arr.ptr[0..0]; - } - } - -/** -Shrinks the managed array to the given length. Passing in a length that's -greater than the current array length throws an enforce exception. -*/ - void shrinkTo(size_t newlength) - { - if(_data) - { - enforce(newlength <= _data.arr.length); - _data.arr = _data.arr.ptr[0..newlength]; - } - else - enforce(newlength == 0); - } - } -} - -/** -An appender that can update an array in-place. It forwards all calls to an -underlying appender implementation. Any calls made to the appender also update -the pointer to the original array passed in. -*/ -struct RefAppender(A : T[], T) -{ - private - { - Appender!(A, T) impl; - T[] *arr; - } - -/** -Construct a ref appender with a given array reference. This does not copy the -data. If the array has a larger capacity as determined by arr.capacity, it -will be used by the appender. $(D RefAppender) assumes that arr is a non-null value. - -Note, do not use builtin appending (i.e. ~=) on the original array passed in -until you are done with the appender, because calls to the appender override -those appends. -*/ - this(T[] *arr) - { - impl = Appender!(A, T)(*arr); - this.arr = arr; - } - - auto opDispatch(string fn, Args...)(Args args) if (is(typeof(mixin("impl." ~ fn ~ "(args)")))) - { - // we do it this way because we can't cache a void return - scope(exit) *this.arr = impl.data; - mixin("return impl." ~ fn ~ "(args);"); - } - -/** -Returns the capacity of the array (the maximum number of elements the -managed array can accommodate before triggering a reallocation). If any -appending will reallocate, capacity returns 0. - */ - @property size_t capacity() - { - return impl.capacity; - } - -/** -Returns the managed array. - */ - @property T[] data() - { - return impl.data; - } -} - -/** -Convenience function that returns an $(D Appender!(A)) object -initialized with $(D array). - */ -Appender!(E[]) appender(A : E[], E)(A array = null) -{ - return Appender!(E[])(array); -} - -unittest -{ - auto app = appender!(char[])(); - string b = "abcdefg"; - foreach (char c; b) app.put(c); - assert(app.data == "abcdefg"); - - int[] a = [ 1, 2 ]; - auto app2 = appender(a); - assert(app2.data == [ 1, 2 ]); - app2.put(3); - app2.put([ 4, 5, 6 ][]); - assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]); -} - -/** -Convenience function that returns a $(D RefAppender!(A)) object -initialized with $(D array). Don't use null for the array pointer, use the -other version of appender instead. - */ -RefAppender!(E[]) appender(A : E[]*, E)(A array) -{ - return RefAppender!(E[])(array); -} - -unittest -{ - auto arr = new char[0]; - auto app = appender(&arr); - string b = "abcdefg"; - foreach (char c; b) app.put(c); - assert(app.data == "abcdefg"); - assert(arr == "abcdefg"); - - int[] a = [ 1, 2 ]; - auto app2 = appender(&a); - assert(app2.data == [ 1, 2 ]); - assert(a == [ 1, 2 ]); - app2.put(3); - app2.put([ 4, 5, 6 ][]); - assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]); - assert(a == [ 1, 2, 3, 4, 5, 6 ]); -} - -/* -A simple slice type only holding pointers to the beginning and the end -of an array. Experimental duplication of the built-in slice - do not -use yet. - */ -struct SimpleSlice(T) -{ - private T * _b, _e; - - this(U...)(U values) - { - _b = cast(T*) core.memory.GC.malloc(U.length * T.sizeof); - _e = _b + U.length; - foreach (i, Unused; U) _b[i] = values[i]; - } - - void opAssign(R)(R anotherSlice) - { - static if (is(typeof(*_b = anotherSlice))) - { - // assign all elements to a value - foreach (p; _b .. _e) - { - *p = anotherSlice; - } - } - else - { - // assign another slice to this - enforce(anotherSlice.length == length); - auto p = _b; - foreach (p; _b .. _e) - { - *p = anotherSlice.front; - anotherSlice.popFront; - } - } - } - -/** - Range primitives. - */ - bool empty() const - { - assert(_b <= _e); - return _b == _e; - } - -/// Ditto - ref T front() - { - assert(!empty); - return *_b; - } - -/// Ditto - void popFront() - { - assert(!empty); - ++_b; - } - -/// Ditto - ref T back() - { - assert(!empty); - return _e[-1]; - } - -/// Ditto - void popBack() - { - assert(!empty); - --_e; - } - -/// Ditto - T opIndex(size_t n) - { - assert(n < length); - return _b[n]; - } - -/// Ditto - const(T) opIndex(size_t n) const - { - assert(n < length); - return _b[n]; - } - -/// Ditto - void opIndexAssign(T value, size_t n) - { - assert(n < length); - _b[n] = value; - } - -/// Ditto - SimpleSliceLvalue!T opSlice() - { - typeof(return) result = void; - result._b = _b; - result._e = _e; - return result; - } - -/// Ditto - SimpleSliceLvalue!T opSlice(size_t x, size_t y) - { - enforce(x <= y && y <= length); - typeof(return) result = { _b + x, _b + y }; - return result; - } - - @property - { - /// Returns the length of the slice. - size_t length() const - { - return _e - _b; - } - - /** - Sets the length of the slice. Newly added elements will be filled with - $(D T.init). - */ - void length(size_t newLength) - { - immutable oldLength = length; - _b = cast(T*) core.memory.GC.realloc(_b, newLength * T.sizeof); - _e = _b + newLength; - this[oldLength .. $] = T.init; - } - } - -/// Concatenation. - SimpleSlice opCat(R)(R another) - { - immutable newLen = length + another.length; - typeof(return) result = void; - result._b = cast(T*) - core.memory.GC.malloc(newLen * T.sizeof); - result._e = result._b + newLen; - result[0 .. this.length] = this; - result[this.length .. result.length] = another; - return result; - } - -/// Concatenation with rebinding. - void opCatAssign(R)(R another) - { - auto newThis = this ~ another; - move(newThis, this); - } -} - -// Support for mass assignment -struct SimpleSliceLvalue(T) -{ - private SimpleSlice!T _s; - alias _s this; - - void opAssign(R)(R anotherSlice) - { - static if (is(typeof(*_b = anotherSlice))) - { - // assign all elements to a value - foreach (p; _b .. _e) - { - *p = anotherSlice; - } - } - else - { - // assign another slice to this - enforce(anotherSlice.length == length); - auto p = _b; - foreach (p; _b .. _e) - { - *p = anotherSlice.front; - anotherSlice.popFront; - } - } - } -} - -unittest -{ - // SimpleSlice!(int) s; - - // s = SimpleSlice!(int)(4, 5, 6); - // assert(equal(s, [4, 5, 6][])); - // assert(s.length == 3); - // assert(s[0] == 4); - // assert(s[1] == 5); - // assert(s[2] == 6); - - // assert(s[] == s); - // assert(s[0 .. s.length] == s); - // assert(equal(s[0 .. s.length - 1], [4, 5][])); - - // auto s1 = s ~ s[0 .. 1]; - // assert(equal(s1, [4, 5, 6, 4][])); - - // assert(s1[3] == 4); - // s1[3] = 42; - // assert(s1[3] == 42); - - // const s2 = s; - // assert(s2.length == 3); - // assert(!s2.empty); - // assert(s2[0] == s[0]); - - // s[0 .. 2] = 10; - // assert(equal(s, [10, 10, 6][])); - - // s ~= [ 5, 9 ][]; - // assert(equal(s, [10, 10, 6, 5, 9][])); - - // s.length = 7; - // assert(equal(s, [10, 10, 6, 5, 9, 0, 0][])); -} diff --git a/std/container.d b/std/container.d index 7d1fc902a..68c0d3f48 100755 --- a/std/container.d +++ b/std/container.d @@ -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 { @@ -4223,10 +4228,12 @@ if (is(typeof(less(T.init, T.init)) == bool) || is(typeof(less) == string)) // used for convenience private alias RBNode!Elem.Node Node; - private Node _end; + 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); - assert(ts.arrayEqual([1,2,3,4,5,6,7,8,9,10,11])); + 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.arrayEqual([1,2,3,4])); + 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])); - assert(std.algorithm.equal(r2, [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 { diff --git a/win32.mak b/win32.mak index a6a20f8fe..0b1319299 100644 --- a/win32.mak +++ b/win32.mak @@ -113,8 +113,8 @@ SRCS_1 = std\math.d std\stdio.d std\dateparse.d std\date.d std\datetime.d \ std\intrinsic.d \ std\process.d \ std\system.d \ - std\encoding.d - + std\encoding.d + SRCS_2 = std\variant.d \ std\stream.d std\socket.d std\socketstream.d \ std\perf.d std\container.d std\conv.d \ @@ -287,7 +287,7 @@ SRC_STD_C_FREEBSD= std\c\freebsd\socket.d SRC_STD_INTERNAL_MATH= std\internal\math\biguintcore.d \ std\internal\math\biguintnoasm.d std\internal\math\biguintx86.d \ std\internal\math\gammafunction.d std\internal\math\errorfunction.d - + SRC_ETC= @@ -547,7 +547,7 @@ biguintnoasm.obj : std\internal\math\biguintnoasm.d biguintx86.obj : std\internal\math\biguintx86.d $(DMD) -c $(DFLAGS) std\internal\math\biguintx86.d - + gammafunction.obj : std\internal\math\gammafunction.d $(DMD) -c $(DFLAGS) std\internal\math\gammafunction.d @@ -928,8 +928,8 @@ install: $(CP) $(DOCS) $(DIR)\html\d\phobos svn: - $(CP) win32.mak posix.mak $(STDDOC) $(SVN)\ - $(CP) $(SRC) $(SVN)\ + $(CP) win32.mak posix.mak $(STDDOC) $(SVN)\ + $(CP) $(SRC) $(SVN)\ $(CP) $(SRC_STD) $(SVN)\std $(CP) $(SRC_STD_C) $(SVN)\std\c $(CP) $(SRC_STD_WIN) $(SVN)\std\windows