From d94ba36a208774cbfb88bf32bf96ed15c12c3dfd Mon Sep 17 00:00:00 2001 From: Sebastian Wilzbach Date: Fri, 17 Feb 2017 10:25:35 +0100 Subject: [PATCH] Replace overload constraints with static if --- std/algorithm/comparison.d | 129 +++++----- std/algorithm/iteration.d | 92 +++---- std/algorithm/mutation.d | 428 ++++++++++++++++--------------- std/algorithm/searching.d | 498 +++++++++++++++++++------------------ std/algorithm/sorting.d | 66 ++--- 5 files changed, 599 insertions(+), 614 deletions(-) diff --git a/std/algorithm/comparison.d b/std/algorithm/comparison.d index 4467334d30a..9d5050d7337 100644 --- a/std/algorithm/comparison.d +++ b/std/algorithm/comparison.d @@ -604,84 +604,85 @@ Returns: */ int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) -if (isInputRange!R1 && isInputRange!R2 && !(isSomeString!R1 && isSomeString!R2)) +if (isInputRange!R1 && isInputRange!R2 || isSomeString!R1 && isSomeString!R2) { - for (;; r1.popFront(), r2.popFront()) + static if (isInputRange!R1 && isInputRange!R2) { - if (r1.empty) return -cast(int)!r2.empty; - if (r2.empty) return !r1.empty; - auto a = r1.front, b = r2.front; - if (binaryFun!pred(a, b)) return -1; - if (binaryFun!pred(b, a)) return 1; + for (;; r1.popFront(), r2.popFront()) + { + if (r1.empty) return -cast(int)!r2.empty; + if (r2.empty) return !r1.empty; + auto a = r1.front, b = r2.front; + if (binaryFun!pred(a, b)) return -1; + if (binaryFun!pred(b, a)) return 1; + } } -} - -/// ditto -int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) if (isSomeString!R1 && isSomeString!R2) -{ - import core.stdc.string : memcmp; - import std.utf : decode; - - static if (is(typeof(pred) : string)) - enum isLessThan = pred == "a < b"; else - enum isLessThan = false; - - // For speed only - static int threeWay(size_t a, size_t b) { - static if (size_t.sizeof == int.sizeof && isLessThan) - return a - b; - else - return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0; - } - // For speed only - // @@@BUG@@@ overloading should be allowed for nested functions - static int threeWayInt(int a, int b) - { - static if (isLessThan) - return a - b; + import core.stdc.string : memcmp; + import std.utf : decode; + + static if (is(typeof(pred) : string)) + enum isLessThan = pred == "a < b"; else - return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0; - } + enum isLessThan = false; - static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof && isLessThan) - { - static if (typeof(r1[0]).sizeof == 1) + // For speed only + static int threeWay(size_t a, size_t b) { - immutable len = min(r1.length, r2.length); - immutable result = __ctfe ? - { - foreach (i; 0 .. len) - { - if (r1[i] != r2[i]) - return threeWayInt(r1[i], r2[i]); - } - return 0; - }() - : () @trusted { return memcmp(r1.ptr, r2.ptr, len); }(); - if (result) return result; + static if (size_t.sizeof == int.sizeof && isLessThan) + return a - b; + else + return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0; } - else + // For speed only + // @@@BUG@@@ overloading should be allowed for nested functions + static int threeWayInt(int a, int b) { - auto p1 = r1.ptr, p2 = r2.ptr, - pEnd = p1 + min(r1.length, r2.length); - for (; p1 != pEnd; ++p1, ++p2) + static if (isLessThan) + return a - b; + else + return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0; + } + + static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof && isLessThan) + { + static if (typeof(r1[0]).sizeof == 1) + { + immutable len = min(r1.length, r2.length); + immutable result = __ctfe ? + { + foreach (i; 0 .. len) + { + if (r1[i] != r2[i]) + return threeWayInt(r1[i], r2[i]); + } + return 0; + }() + : () @trusted { return memcmp(r1.ptr, r2.ptr, len); }(); + if (result) return result; + } + else { - if (*p1 != *p2) return threeWayInt(cast(int) *p1, cast(int) *p2); + auto p1 = r1.ptr, p2 = r2.ptr, + pEnd = p1 + min(r1.length, r2.length); + for (; p1 != pEnd; ++p1, ++p2) + { + if (*p1 != *p2) return threeWayInt(cast(int) *p1, cast(int) *p2); + } } + return threeWay(r1.length, r2.length); } - return threeWay(r1.length, r2.length); - } - else - { - for (size_t i1, i2;;) + else { - if (i1 == r1.length) return threeWay(i2, r2.length); - if (i2 == r2.length) return threeWay(r1.length, i1); - immutable c1 = decode(r1, i1), - c2 = decode(r2, i2); - if (c1 != c2) return threeWayInt(cast(int) c1, cast(int) c2); + for (size_t i1, i2;;) + { + if (i1 == r1.length) return threeWay(i2, r2.length); + if (i2 == r2.length) return threeWay(r1.length, i1); + immutable c1 = decode(r1, i1), + c2 = decode(r2, i2); + if (c1 != c2) return threeWayInt(cast(int) c1, cast(int) c2); + } } } } diff --git a/std/algorithm/iteration.d b/std/algorithm/iteration.d index 6985ebd9e21..42c8786f6d4 100644 --- a/std/algorithm/iteration.d +++ b/std/algorithm/iteration.d @@ -858,6 +858,7 @@ template each(alias pred = "a") import std.meta : AliasSeq; import std.traits : Parameters; +private: alias BinaryArgs = AliasSeq!(pred, "i", "a"); enum isRangeUnaryIterable(R) = @@ -886,67 +887,66 @@ template each(alias pred = "a") (!isForwardRange!R || isDynamicArray!R) && (isForeachUnaryIterable!R || isForeachBinaryIterable!R); +public: void each(Range)(Range r) - if (isRangeIterable!Range && !isForeachIterable!Range) + if (isRangeIterable!Range || + isForeachIterable!Range || + __traits(compiles, Parameters!(Parameters!(r.opApply))) || + __traits(compiles, typeof(range.front).length)) { - debug(each) pragma(msg, "Using while for ", Range.stringof); - static if (isRangeUnaryIterable!Range) + static if (isRangeIterable!Range) { - while (!r.empty) + debug(each) pragma(msg, "Using while for ", Range.stringof); + static if (isRangeUnaryIterable!Range) { - cast(void)unaryFun!pred(r.front); - r.popFront(); + while (!r.empty) + { + cast(void)unaryFun!pred(r.front); + r.popFront(); + } + } + else // if (isRangeBinaryIterable!Range) + { + size_t i = 0; + while (!r.empty) + { + cast(void)binaryFun!BinaryArgs(i, r.front); + r.popFront(); + i++; + } } } - else // if (isRangeBinaryIterable!Range) + else if (isForeachIterable!Iterable) { - size_t i = 0; - while (!r.empty) + debug(each) pragma(msg, "Using foreach for ", Iterable.stringof); + static if (isForeachUnaryIterable!Iterable) + { + foreach (ref e; r) + cast(void)unaryFun!pred(e); + } + else // if (isForeachBinaryIterable!Iterable) { - cast(void)binaryFun!BinaryArgs(i, r.front); - r.popFront(); - i++; + foreach (ref i, ref e; r) + cast(void)binaryFun!BinaryArgs(i, e); } } - } - - void each(Iterable)(auto ref Iterable r) - if (isForeachIterable!Iterable) - { - debug(each) pragma(msg, "Using foreach for ", Iterable.stringof); - static if (isForeachUnaryIterable!Iterable) + else static if (__traits(compiles, Parameters!(Parameters!(r.opApply)))) { - foreach (ref e; r) - cast(void)unaryFun!pred(e); + // opApply with >2 parameters. count the delegate args. + // only works if it is not templated (otherwise we cannot count the args) + auto dg(Parameters!(Parameters!(r.opApply)) params) { + pred(params); + return 0; // tells opApply to continue iteration + } + r.opApply(&dg); } - else // if (isForeachBinaryIterable!Iterable) + else static if (__traits(compiles, typeof(range.front).length)) { - foreach (ref i, ref e; r) - cast(void)binaryFun!BinaryArgs(i, e); + // range interface with >2 parameters. + for (auto r = range; !r.empty; r.popFront()) + pred(r.front.expand); } } - - // opApply with >2 parameters. count the delegate args. - // only works if it is not templated (otherwise we cannot count the args) - void each(Iterable)(auto ref Iterable r) - if (!isRangeIterable!Iterable && !isForeachIterable!Iterable && - __traits(compiles, Parameters!(Parameters!(r.opApply)))) - { - auto dg(Parameters!(Parameters!(r.opApply)) params) { - pred(params); - return 0; // tells opApply to continue iteration - } - r.opApply(&dg); - } - - // range interface with >2 parameters. - void each(Range)(Range range) - if (!isRangeIterable!Range && !isForeachIterable!Range && - __traits(compiles, typeof(range.front).length)) - { - for (auto r = range; !r.empty; r.popFront()) - pred(r.front.expand); - } } /// diff --git a/std/algorithm/mutation.d b/std/algorithm/mutation.d index 573bb13788b..dd191999cbb 100644 --- a/std/algorithm/mutation.d +++ b/std/algorithm/mutation.d @@ -363,57 +363,57 @@ See_Also: $(HTTP sgi.com/tech/stl/_copy.html, STL's _copy) */ TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) - if (areCopyCompatibleArrays!(SourceRange, TargetRange)) + if (isInputRange!SourceRange && ( + areCopyCompatibleArrays!(SourceRange, TargetRange) || + isOutputRange!(TargetRange, ElementType!SourceRange))) { - const tlen = target.length; - const slen = source.length; - assert(tlen >= slen, - "Cannot copy a source range into a smaller target range."); - - immutable overlaps = () @trusted { - return source.ptr < target.ptr + tlen && - target.ptr < source.ptr + slen; }(); - - if (overlaps) - { - foreach (idx; 0 .. slen) - target[idx] = source[idx]; - return target[slen .. tlen]; - } - else + static if (areCopyCompatibleArrays!(SourceRange, TargetRange)) { - // Array specialization. This uses optimized memory copying - // routines under the hood and is about 10-20x faster than the - // generic implementation. - target[0 .. slen] = source[]; - return target[slen .. $]; - } -} + const tlen = target.length; + const slen = source.length; + assert(tlen >= slen, + "Cannot copy a source range into a smaller target range."); -/// ditto -TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) - if (!areCopyCompatibleArrays!(SourceRange, TargetRange) && - isInputRange!SourceRange && - isOutputRange!(TargetRange, ElementType!SourceRange)) -{ - // Specialize for 2 random access ranges. - // Typically 2 random access ranges are faster iterated by common - // index than by x.popFront(), y.popFront() pair - static if (isRandomAccessRange!SourceRange && - hasLength!SourceRange && - hasSlicing!TargetRange && - isRandomAccessRange!TargetRange && - hasLength!TargetRange) - { - auto len = source.length; - foreach (idx; 0 .. len) - target[idx] = source[idx]; - return target[len .. target.length]; + immutable overlaps = () @trusted { + return source.ptr < target.ptr + tlen && + target.ptr < source.ptr + slen; }(); + + if (overlaps) + { + foreach (idx; 0 .. slen) + target[idx] = source[idx]; + return target[slen .. tlen]; + } + else + { + // Array specialization. This uses optimized memory copying + // routines under the hood and is about 10-20x faster than the + // generic implementation. + target[0 .. slen] = source[]; + return target[slen .. $]; + } } else { - put(target, source); - return target; + // Specialize for 2 random access ranges. + // Typically 2 random access ranges are faster iterated by common + // index than by x.popFront(), y.popFront() pair + static if (isRandomAccessRange!SourceRange && + hasLength!SourceRange && + hasSlicing!TargetRange && + isRandomAccessRange!TargetRange && + hasLength!TargetRange) + { + auto len = source.length; + foreach (idx; 0 .. len) + target[idx] = source[idx]; + return target[len .. target.length]; + } + else + { + put(target, source); + return target; + } } } @@ -776,59 +776,60 @@ See_Also: $(LREF uninitializeFill) */ void initializeAll(Range)(Range range) - if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range) + if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range || + is(Range == char[]) || is(Range == wchar[])) { - import core.stdc.string : memset, memcpy; - import std.traits : hasElaborateAssign, isDynamicArray; - - alias T = ElementType!Range; - static if (hasElaborateAssign!T) + static if (isInputRange!Range) { - import std.algorithm.internal : addressOf; - //Elaborate opAssign. Must go the memcpy road. - //We avoid calling emplace here, because our goal is to initialize to - //the static state of T.init, - //So we want to avoid any un-necassarilly CC'ing of T.init - auto p = typeid(T).initializer(); - if (p.ptr) + import core.stdc.string : memset, memcpy; + import std.traits : hasElaborateAssign, isDynamicArray; + + alias T = ElementType!Range; + static if (hasElaborateAssign!T) { - for ( ; !range.empty ; range.popFront() ) + import std.algorithm.internal : addressOf; + //Elaborate opAssign. Must go the memcpy road. + //We avoid calling emplace here, because our goal is to initialize to + //the static state of T.init, + //So we want to avoid any un-necassarilly CC'ing of T.init + auto p = typeid(T).initializer(); + if (p.ptr) { - static if (__traits(isStaticArray, T)) + for ( ; !range.empty ; range.popFront() ) { - // static array initializer only contains initialization - // for one element of the static array. - auto elemp = cast(void *)addressOf(range.front); - auto endp = elemp + T.sizeof; - while (elemp < endp) + static if (__traits(isStaticArray, T)) { - memcpy(elemp, p.ptr, p.length); - elemp += p.length; + // static array initializer only contains initialization + // for one element of the static array. + auto elemp = cast(void *)addressOf(range.front); + auto endp = elemp + T.sizeof; + while (elemp < endp) + { + memcpy(elemp, p.ptr, p.length); + elemp += p.length; + } + } + else + { + memcpy(addressOf(range.front), p.ptr, T.sizeof); } - } - else - { - memcpy(addressOf(range.front), p.ptr, T.sizeof); } } + else + static if (isDynamicArray!Range) + memset(range.ptr, 0, range.length * T.sizeof); + else + for ( ; !range.empty ; range.popFront() ) + memset(addressOf(range.front), 0, T.sizeof); } else - static if (isDynamicArray!Range) - memset(range.ptr, 0, range.length * T.sizeof); - else - for ( ; !range.empty ; range.popFront() ) - memset(addressOf(range.front), 0, T.sizeof); + fill(range, T.init); } else - fill(range, T.init); -} - -/// ditto -void initializeAll(Range)(Range range) - if (is(Range == char[]) || is(Range == wchar[])) -{ - alias T = ElementEncodingType!Range; - range[] = T.init; + { + alias T = ElementEncodingType!Range; + range[] = T.init; + } } /// @@ -1682,138 +1683,132 @@ Returns: Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) -if (s != SwapStrategy.stable - && isBidirectionalRange!Range +if (isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1) { - Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; - foreach (i, v; offset) + static if (s != SwapStrategy.stable) { - static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) + Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; + foreach (i, v; offset) { - blackouts[i].pos = v[0]; - blackouts[i].len = v[1] - v[0]; - } - else - { - static assert(is(typeof(v) : size_t), typeof(v).stringof); - blackouts[i].pos = v; - blackouts[i].len = 1; - } - static if (i > 0) - { - import std.exception : enforce; + static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) + { + blackouts[i].pos = v[0]; + blackouts[i].len = v[1] - v[0]; + } + else + { + static assert(is(typeof(v) : size_t), typeof(v).stringof); + blackouts[i].pos = v; + blackouts[i].len = 1; + } + static if (i > 0) + { + import std.exception : enforce; - enforce(blackouts[i - 1].pos + blackouts[i - 1].len - <= blackouts[i].pos, - "remove(): incorrect ordering of elements to remove"); + enforce(blackouts[i - 1].pos + blackouts[i - 1].len + <= blackouts[i].pos, + "remove(): incorrect ordering of elements to remove"); + } } - } - size_t left = 0, right = offset.length - 1; - auto tgt = range.save; - size_t tgtPos = 0; + size_t left = 0, right = offset.length - 1; + auto tgt = range.save; + size_t tgtPos = 0; - while (left <= right) - { - // Look for a blackout on the right - if (blackouts[right].pos + blackouts[right].len >= range.length) + while (left <= right) { - range.popBackExactly(blackouts[right].len); + // Look for a blackout on the right + if (blackouts[right].pos + blackouts[right].len >= range.length) + { + range.popBackExactly(blackouts[right].len); - // Since right is unsigned, we must check for this case, otherwise - // we might turn it into size_t.max and the loop condition will not - // fail when it should. - if (right > 0) + // Since right is unsigned, we must check for this case, otherwise + // we might turn it into size_t.max and the loop condition will not + // fail when it should. + if (right > 0) + { + --right; + continue; + } + else + break; + } + // Advance to next blackout on the left + assert(blackouts[left].pos >= tgtPos); + tgt.popFrontExactly(blackouts[left].pos - tgtPos); + tgtPos = blackouts[left].pos; + + // Number of elements to the right of blackouts[right] + immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len); + size_t toMove = void; + if (tailLen < blackouts[left].len) { - --right; - continue; + toMove = tailLen; + blackouts[left].pos += toMove; + blackouts[left].len -= toMove; } else - break; - } - // Advance to next blackout on the left - assert(blackouts[left].pos >= tgtPos); - tgt.popFrontExactly(blackouts[left].pos - tgtPos); - tgtPos = blackouts[left].pos; - - // Number of elements to the right of blackouts[right] - immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len); - size_t toMove = void; - if (tailLen < blackouts[left].len) - { - toMove = tailLen; - blackouts[left].pos += toMove; - blackouts[left].len -= toMove; - } - else - { - toMove = blackouts[left].len; - ++left; - } - tgtPos += toMove; - foreach (i; 0 .. toMove) - { - move(range.back, tgt.front); - range.popBack(); - tgt.popFront(); + { + toMove = blackouts[left].len; + ++left; + } + tgtPos += toMove; + foreach (i; 0 .. toMove) + { + move(range.back, tgt.front); + range.popBack(); + tgt.popFront(); + } } - } - - return range; -} -/// Ditto -Range remove -(SwapStrategy s = SwapStrategy.stable, Range, Offset...) -(Range range, Offset offset) -if (s == SwapStrategy.stable - && isBidirectionalRange!Range - && hasLvalueElements!Range - && Offset.length >= 1) -{ - auto result = range; - auto src = range, tgt = range; - size_t pos; - foreach (pass, i; offset) + return range; + } + else { - static if (is(typeof(i[0])) && is(typeof(i[1]))) + auto result = range; + auto src = range, tgt = range; + size_t pos; + foreach (pass, i; offset) { - auto from = i[0], delta = i[1] - i[0]; - } - else - { - auto from = i; - enum delta = 1; - } + static if (is(typeof(i[0])) && is(typeof(i[1]))) + { + auto from = i[0], delta = i[1] - i[0]; + } + else + { + auto from = i; + enum delta = 1; + } - static if (pass > 0) - { - import std.exception : enforce; - enforce(pos <= from, - "remove(): incorrect ordering of elements to remove"); + static if (pass > 0) + { + import std.exception : enforce; + enforce(pos <= from, + "remove(): incorrect ordering of elements to remove"); - for (; pos < from; ++pos, src.popFront(), tgt.popFront()) + for (; pos < from; ++pos, src.popFront(), tgt.popFront()) + { + move(src.front, tgt.front); + } + } + else { - move(src.front, tgt.front); + src.popFrontExactly(from); + tgt.popFrontExactly(from); + pos = from; } + // now skip source to the "to" position + src.popFrontExactly(delta); + result.popBackExactly(delta); + pos += delta; } - else - { - src.popFrontExactly(from); - tgt.popFrontExactly(from); - pos = from; - } - // now skip source to the "to" position - src.popFrontExactly(delta); - result.popBackExactly(delta); - pos += delta; + // leftover move + moveAll(src, tgt); + return result; } - // leftover move - moveAll(src, tgt); - return result; } /// @@ -2087,15 +2082,27 @@ See_Also: $(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view */ void reverse(Range)(Range r) -if (isBidirectionalRange!Range && !isRandomAccessRange!Range - && hasSwappableElements!Range) +if (isBidirectionalRange!Range && (hasSwappableElements!Range || isRandomAccessRange!Range)) { - while (!r.empty) + static if (isRandomAccessRange!Range) + { + //swapAt is in fact the only way to swap non lvalue ranges + immutable last = r.length-1; + immutable steps = r.length/2; + for (size_t i = 0; i < steps; i++) + { + r.swapAt(i, last-i); + } + } + else { - swap(r.front, r.back); - r.popFront(); - if (r.empty) break; - r.popBack(); + while (!r.empty) + { + swap(r.front, r.back); + r.popFront(); + if (r.empty) break; + r.popBack(); + } } } @@ -2107,17 +2114,12 @@ if (isBidirectionalRange!Range && !isRandomAccessRange!Range assert(arr == [ 3, 2, 1 ]); } -///ditto -void reverse(Range)(Range r) -if (isRandomAccessRange!Range && hasLength!Range) +/// +@safe unittest { - //swapAt is in fact the only way to swap non lvalue ranges - immutable last = r.length-1; - immutable steps = r.length/2; - for (size_t i = 0; i < steps; i++) - { - r.swapAt(i, last-i); - } + char[] arr = "hello\U00010143\u0100\U00010143".dup; + reverse(arr); + assert(arr == "\U00010143\u0100\U00010143olleh"); } @safe unittest @@ -2174,14 +2176,6 @@ if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable)) reverse(r); } -/// -@safe unittest -{ - char[] arr = "hello\U00010143\u0100\U00010143".dup; - reverse(arr); - assert(arr == "\U00010143\u0100\U00010143olleh"); -} - @safe unittest { void test(string a, string b) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 32c764e350b..4bdc0e2271b 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -483,43 +483,49 @@ if (isNarrowString!R1 && isInputRange!R2 && return result[0 .. i]; } -/// ditto -auto commonPrefix(R1, R2)(R1 r1, R2 r2) -if (isNarrowString!R1 && isInputRange!R2 && !isNarrowString!R2 && - is(typeof(r1.front == r2.front))) +/// +@safe unittest { - return commonPrefix!"a == b"(r1, r2); + assert(commonPrefix("hello, world", "hello, there") == "hello, "); } /// ditto auto commonPrefix(R1, R2)(R1 r1, R2 r2) -if (isNarrowString!R1 && isNarrowString!R2) +if (isNarrowString!R1 && (isNarrowString!R2 || + isInputRange!R2 && is(typeof(r1.front == r2.front)))) { - import std.algorithm.comparison : min; - - static if (ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof) + static if (isInputRange!R2) { - import std.utf : stride, UTFException; + return commonPrefix!"a == b"(r1, r2); + } + else + { + import std.algorithm.comparison : min; - immutable limit = min(r1.length, r2.length); - for (size_t i = 0; i < limit;) + static if (ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof) { - immutable codeLen = stride(r1, i); - size_t j = 0; + import std.utf : stride, UTFException; - for (; j < codeLen && i < limit; ++i, ++j) + immutable limit = min(r1.length, r2.length); + for (size_t i = 0; i < limit;) { - if (r1[i] != r2[i]) - return r1[0 .. i - j]; - } + immutable codeLen = stride(r1, i); + size_t j = 0; + + for (; j < codeLen && i < limit; ++i, ++j) + { + if (r1[i] != r2[i]) + return r1[0 .. i - j]; + } - if (i == limit && j < codeLen) - throw new UTFException("Invalid UTF-8 sequence", i); + if (i == limit && j < codeLen) + throw new UTFException("Invalid UTF-8 sequence", i); + } + return r1[0 .. limit]; } - return r1[0 .. limit]; + else + return commonPrefix!"a == b"(r1, r2); } - else - return commonPrefix!"a == b"(r1, r2); } @safe unittest @@ -1080,39 +1086,37 @@ if (isBidirectionalRange!Range && Needles.length > 1 && /// Ditto bool endsWith(alias pred = "a == b", R1, R2)(R1 doesThisEnd, R2 withThis) if (isBidirectionalRange!R1 && + (is(typeof(binaryFun!pred(doesThisEnd.back, withThis)) : bool) || isBidirectionalRange!R2 && - is(typeof(binaryFun!pred(doesThisEnd.back, withThis.back)) : bool)) + is(typeof(binaryFun!pred(doesThisEnd.back, withThis.back)) : bool))) { - alias haystack = doesThisEnd; - alias needle = withThis; + static if(!is(typeof(binaryFun!pred(doesThisEnd.back, withThis)) : bool)) + { + alias haystack = doesThisEnd; + alias needle = withThis; - static if (is(typeof(pred) : string)) - enum isDefaultPred = pred == "a == b"; - else - enum isDefaultPred = false; + static if (is(typeof(pred) : string)) + enum isDefaultPred = pred == "a == b"; + else + enum isDefaultPred = false; - static if (isDefaultPred && isArray!R1 && isArray!R2 && - is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2))) - { - if (haystack.length < needle.length) return false; + static if (isDefaultPred && isArray!R1 && isArray!R2 && + is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2))) + { + if (haystack.length < needle.length) return false; - return haystack[$ - needle.length .. $] == needle; + return haystack[$ - needle.length .. $] == needle; + } + else + { + import std.range : retro; + return startsWith!pred(retro(doesThisEnd), retro(withThis)); + } } else - { - import std.range : retro; - return startsWith!pred(retro(doesThisEnd), retro(withThis)); - } -} - -/// Ditto -bool endsWith(alias pred = "a == b", R, E)(R doesThisEnd, E withThis) -if (isBidirectionalRange!R && - is(typeof(binaryFun!pred(doesThisEnd.back, withThis)) : bool)) -{ - return doesThisEnd.empty - ? false - : binaryFun!pred(doesThisEnd.back, withThis); + return doesThisEnd.empty + ? false + : binaryFun!pred(doesThisEnd.back, withThis); } /// Ditto @@ -1767,26 +1771,207 @@ $(D haystack) advanced such that $(D needle) is a prefix of it (if no such position exists, returns $(D haystack) advanced to termination). */ R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle) -if (isForwardRange!R1 && isForwardRange!R2 - && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) - && !isRandomAccessRange!R1) -{ - static if (is(typeof(pred == "a == b")) && pred == "a == b" && isSomeString!R1 && isSomeString!R2 - && haystack[0].sizeof == needle[0].sizeof) +if (is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) && ( + (isForwardRange!R1 && isForwardRange!R2) || + (isRandomAccessRange!R1 && ( + isForwardRange!R2 || + hasLength!R1 && hasSlicing!R1 && isBidirectionalRange!R2)) + )) +{ + static if (!isRandomAccessRange!R1) { - // return cast(R1) find(representation(haystack), representation(needle)); - // Specialization for simple string search - alias Representation = - Select!(haystack[0].sizeof == 1, ubyte[], - Select!(haystack[0].sizeof == 2, ushort[], uint[])); - // Will use the array specialization - static TO force(TO, T)(T r) @trusted { return cast(TO)r; } - return force!R1(.find!(pred, Representation, Representation) - (force!Representation(haystack), force!Representation(needle))); + static if (is(typeof(pred == "a == b")) && pred == "a == b" && isSomeString!R1 && isSomeString!R2 + && haystack[0].sizeof == needle[0].sizeof) + { + // return cast(R1) find(representation(haystack), representation(needle)); + // Specialization for simple string search + alias Representation = + Select!(haystack[0].sizeof == 1, ubyte[], + Select!(haystack[0].sizeof == 2, ushort[], uint[])); + // Will use the array specialization + static TO force(TO, T)(T r) @trusted { return cast(TO)r; } + return force!R1(.find!(pred, Representation, Representation) + (force!Representation(haystack), force!Representation(needle))); + } + else + { + return simpleMindedFind!pred(haystack, needle); + } + } + else static if(hasLength!R1 && hasSlicing!R1 && isBidirectionalRange!R2) + { + if (needle.empty) return haystack; + + static if (hasLength!R2) + { + immutable needleLength = needle.length; + } + else + { + immutable needleLength = walkLength(needle.save); + } + if (needleLength > haystack.length) + { + return haystack[haystack.length .. haystack.length]; + } + // Optimization in case the ranges are both SortedRanges. + // Binary search can be used to find the first occurence + // of the first element of the needle in haystack. + // When it is found O(walklength(needle)) steps are performed. + // 8829 enhancement + import std.range : SortedRange; + import std.algorithm.comparison : mismatch; + static if (is(R1 == R2) + && is(R1 : SortedRange!TT, TT) + && pred == "a == b") + { + auto needleFirstElem = needle[0]; + auto partitions = haystack.trisect(needleFirstElem); + auto firstElemLen = partitions[1].length; + size_t count = 0; + + if (firstElemLen == 0) + return haystack[$ .. $]; + + while (needle.front() == needleFirstElem) + { + needle.popFront(); + ++count; + + if (count > firstElemLen) + return haystack[$ .. $]; + } + + auto m = mismatch(partitions[2], needle); + + if (m[1].empty) + return haystack[partitions[0].length + partitions[1].length - count .. $]; + } + else static if (isRandomAccessRange!R2) + { + immutable lastIndex = needleLength - 1; + auto last = needle[lastIndex]; + size_t j = lastIndex, skip = 0; + for (; j < haystack.length;) + { + if (!binaryFun!pred(haystack[j], last)) + { + ++j; + continue; + } + immutable k = j - lastIndex; + // last elements match + for (size_t i = 0;; ++i) + { + if (i == lastIndex) + return haystack[k .. haystack.length]; + if (!binaryFun!pred(haystack[k + i], needle[i])) + break; + } + if (skip == 0) + { + skip = 1; + while (skip < needleLength && needle[needleLength - 1 - skip] != needle[needleLength - 1]) + { + ++skip; + } + } + j += skip; + } + } + else + { + // @@@BUG@@@ + // auto needleBack = moveBack(needle); + // Stage 1: find the step + size_t step = 1; + auto needleBack = needle.back; + needle.popBack(); + for (auto i = needle.save; !i.empty && i.back != needleBack; + i.popBack(), ++step) + { + } + // Stage 2: linear find + size_t scout = needleLength - 1; + for (;;) + { + if (scout >= haystack.length) + break; + if (!binaryFun!pred(haystack[scout], needleBack)) + { + ++scout; + continue; + } + // Found a match with the last element in the needle + auto cand = haystack[scout + 1 - needleLength .. haystack.length]; + if (startsWith!pred(cand, needle)) + { + // found + return cand; + } + scout += step; + } + } + return haystack[haystack.length .. haystack.length]; } else { - return simpleMindedFind!pred(haystack, needle); + static if (!is(ElementType!R1 == ElementType!R2)) + { + return simpleMindedFind!pred(haystack, needle); + } + else + { + // Prepare the search with needle's first element + if (needle.empty) + return haystack; + + haystack = .find!pred(haystack, needle.front); + + static if (hasLength!R1 && hasLength!R2 && is(typeof(takeNone(haystack)) == R1)) + { + if (needle.length > haystack.length) + return takeNone(haystack); + } + else + { + if (haystack.empty) + return haystack; + } + + needle.popFront(); + size_t matchLen = 1; + + // Loop invariant: haystack[0 .. matchLen] matches everything in + // the initial needle that was popped out of needle. + for (;;) + { + // Extend matchLength as much as possible + for (;;) + { + import std.range : takeNone; + + if (needle.empty || haystack.empty) + return haystack; + + static if (hasLength!R1 && is(typeof(takeNone(haystack)) == R1)) + { + if (matchLen == haystack.length) + return takeNone(haystack); + } + + if (!binaryFun!pred(haystack[matchLen], needle.front)) + break; + + ++matchLen; + needle.popFront(); + } + + auto bestMatch = haystack[0 .. matchLen]; + haystack.popFront(); + haystack = .find!pred(haystack, bestMatch); + } + } } } @@ -1827,126 +2012,6 @@ if (isForwardRange!R1 && isForwardRange!R2 assert(equal(r, SList!int(2, 5, 7, 3)[])); } -/// ditto -R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle) -if (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && isBidirectionalRange!R2 - && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) -{ - if (needle.empty) return haystack; - - static if (hasLength!R2) - { - immutable needleLength = needle.length; - } - else - { - immutable needleLength = walkLength(needle.save); - } - if (needleLength > haystack.length) - { - return haystack[haystack.length .. haystack.length]; - } - // Optimization in case the ranges are both SortedRanges. - // Binary search can be used to find the first occurence - // of the first element of the needle in haystack. - // When it is found O(walklength(needle)) steps are performed. - // 8829 enhancement - import std.range : SortedRange; - import std.algorithm.comparison : mismatch; - static if (is(R1 == R2) - && is(R1 : SortedRange!TT, TT) - && pred == "a == b") - { - auto needleFirstElem = needle[0]; - auto partitions = haystack.trisect(needleFirstElem); - auto firstElemLen = partitions[1].length; - size_t count = 0; - - if (firstElemLen == 0) - return haystack[$ .. $]; - - while (needle.front() == needleFirstElem) - { - needle.popFront(); - ++count; - - if (count > firstElemLen) - return haystack[$ .. $]; - } - - auto m = mismatch(partitions[2], needle); - - if (m[1].empty) - return haystack[partitions[0].length + partitions[1].length - count .. $]; - } - else static if (isRandomAccessRange!R2) - { - immutable lastIndex = needleLength - 1; - auto last = needle[lastIndex]; - size_t j = lastIndex, skip = 0; - for (; j < haystack.length;) - { - if (!binaryFun!pred(haystack[j], last)) - { - ++j; - continue; - } - immutable k = j - lastIndex; - // last elements match - for (size_t i = 0;; ++i) - { - if (i == lastIndex) - return haystack[k .. haystack.length]; - if (!binaryFun!pred(haystack[k + i], needle[i])) - break; - } - if (skip == 0) - { - skip = 1; - while (skip < needleLength && needle[needleLength - 1 - skip] != needle[needleLength - 1]) - { - ++skip; - } - } - j += skip; - } - } - else - { - // @@@BUG@@@ - // auto needleBack = moveBack(needle); - // Stage 1: find the step - size_t step = 1; - auto needleBack = needle.back; - needle.popBack(); - for (auto i = needle.save; !i.empty && i.back != needleBack; - i.popBack(), ++step) - { - } - // Stage 2: linear find - size_t scout = needleLength - 1; - for (;;) - { - if (scout >= haystack.length) - break; - if (!binaryFun!pred(haystack[scout], needleBack)) - { - ++scout; - continue; - } - // Found a match with the last element in the needle - auto cand = haystack[scout + 1 - needleLength .. haystack.length]; - if (startsWith!pred(cand, needle)) - { - // found - return cand; - } - scout += step; - } - } - return haystack[haystack.length .. haystack.length]; -} - @safe unittest { import std.range; @@ -1991,69 +2056,6 @@ if (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && isBidirectionalRa //assert(find!"a == b"("abc", "bc").length == 2); } -/// ditto -R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle) -if (isRandomAccessRange!R1 && isForwardRange!R2 && !isBidirectionalRange!R2 && - is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) -{ - static if (!is(ElementType!R1 == ElementType!R2)) - { - return simpleMindedFind!pred(haystack, needle); - } - else - { - // Prepare the search with needle's first element - if (needle.empty) - return haystack; - - haystack = .find!pred(haystack, needle.front); - - static if (hasLength!R1 && hasLength!R2 && is(typeof(takeNone(haystack)) == R1)) - { - if (needle.length > haystack.length) - return takeNone(haystack); - } - else - { - if (haystack.empty) - return haystack; - } - - needle.popFront(); - size_t matchLen = 1; - - // Loop invariant: haystack[0 .. matchLen] matches everything in - // the initial needle that was popped out of needle. - for (;;) - { - // Extend matchLength as much as possible - for (;;) - { - import std.range : takeNone; - - if (needle.empty || haystack.empty) - return haystack; - - static if (hasLength!R1 && is(typeof(takeNone(haystack)) == R1)) - { - if (matchLen == haystack.length) - return takeNone(haystack); - } - - if (!binaryFun!pred(haystack[matchLen], needle.front)) - break; - - ++matchLen; - needle.popFront(); - } - - auto bestMatch = haystack[0 .. matchLen]; - haystack.popFront(); - haystack = .find!pred(haystack, bestMatch); - } - } -} - @safe unittest { import std.container : SList; diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index d27870a4a50..c98f2766997 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -3752,57 +3752,45 @@ void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, if (isRandomAccessRange!Range && isRandomAccessRange!RangeIndex && hasAssignableElements!RangeIndex && - isIntegral!(ElementType!(RangeIndex))) + isIntegral!(ElementType!(RangeIndex)) || is(ElementType!(RangeIndex) == ElementType!(Range)*)) { static assert(ss == SwapStrategy.unstable, "Stable swap strategy not implemented yet."); import std.container : BinaryHeap; - import std.exception : enforce; - if (index.empty) return; - enforce(ElementType!(RangeIndex).max >= index.length, - "Index type too small"); - bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b) - { - return binaryFun!(less)(r[a], r[b]); - } - auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); - foreach (i; 0 .. r.length) - { - heap.conditionalInsert(cast(ElementType!RangeIndex) i); - } - if (sorted == Yes.sortOutput) - { - while (!heap.empty) heap.removeFront(); - } -} -/// ditto -void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, - Range, RangeIndex) - (Range r, RangeIndex index, SortOutput sorted = No.sortOutput) - if (isRandomAccessRange!Range && - isRandomAccessRange!RangeIndex && - hasAssignableElements!RangeIndex && - is(ElementType!(RangeIndex) == ElementType!(Range)*)) -{ - static assert(ss == SwapStrategy.unstable, - "Stable swap strategy not implemented yet."); + static if (isIntegral!(ElementType!(RangeIndex))) + { + import std.exception : enforce; - import std.container : BinaryHeap; + enforce(ElementType!(RangeIndex).max >= index.length, + "Index type too small"); + bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b) + { + return binaryFun!(less)(r[a], r[b]); + } + auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); + foreach (i; 0 .. r.length) + { + heap.conditionalInsert(cast(ElementType!RangeIndex) i); + } - if (index.empty) return; - static bool indirectLess(const ElementType!(RangeIndex) a, - const ElementType!(RangeIndex) b) - { - return binaryFun!less(*a, *b); } - auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); - foreach (i; 0 .. r.length) + else { - heap.conditionalInsert(&r[i]); + static bool indirectLess(const ElementType!(RangeIndex) a, + const ElementType!(RangeIndex) b) + { + return binaryFun!less(*a, *b); + } + auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0); + foreach (i; 0 .. r.length) + { + heap.conditionalInsert(&r[i]); + } } + if (sorted == Yes.sortOutput) { while (!heap.empty) heap.removeFront();