From 9e13329d54a41c4c294ecfad8e056a3bf30d810b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 15:45:04 +0200 Subject: [PATCH 01/22] Improve comment --- std/traits.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/traits.d b/std/traits.d index a9ddb1af446..2eb53cc9706 100644 --- a/std/traits.d +++ b/std/traits.d @@ -5544,7 +5544,7 @@ unittest } /** - * Returns true if T is an instance of the template S. + Returns true if $(D T) is an instance of the template $(D T). */ enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...); From 990f5d935c04a4b5586361d95da0729243668704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 16:35:38 +0200 Subject: [PATCH 02/22] First try at isSortedRange --- std/range/package.d | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/std/range/package.d b/std/range/package.d index c89727f76a0..8a9973357a3 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -7655,6 +7655,43 @@ unittest assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]); } +/** + Returns true if $(D T) is an instance of the template $(D T) with template + parameters $(D Ps). +*/ +template isSortedRange(T, alias pred = "a < b") +{ + import std.traits : TemplateArgsOf; + import std.functional : binaryFun; + + static if (isSomeString!(typeof(pred))) + { + alias predFun = binaryFun!pred; + } + else + { + alias predFun = pred; + } + + enum isSortedRange = (is(T == SortedRange!Args, Args...) && + is(typeof(predFun) == + typeof(binaryFun!(TemplateArgsOf!T[1])))); +} + +/// +unittest +{ + import std.functional : binaryFun; + + alias R = int[]; + enum pred = "a < b"; + + alias SR = SortedRange!(R, pred); + static assert(isSortedRange!(SR, pred)); + + static assert(isSortedRange!(SR, binaryFun!pred)); +} + /** $(D SortedRange) could accept ranges weaker than random-access, but it is unable to provide interesting functionality for them. Therefore, From a435e4a877991e400c07090a2d3572c827c61f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 16:44:45 +0200 Subject: [PATCH 03/22] First working version of isSortedRange --- std/range/package.d | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 8a9973357a3..2f81bddfdd8 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -7662,20 +7662,37 @@ unittest template isSortedRange(T, alias pred = "a < b") { import std.traits : TemplateArgsOf; - import std.functional : binaryFun; - static if (isSomeString!(typeof(pred))) + static if (TemplateArgsOf!T.length == 2) { - alias predFun = binaryFun!pred; + import std.functional : binaryFun; + + alias predArg = TemplateArgsOf!T[1]; + static if (isSomeString!(typeof(pred))) + { + alias predFun = binaryFun!pred; + } + else + { + alias predFun = pred; + } + + static if (isSomeString!(typeof(predArg))) + { + alias predArgFun = binaryFun!predArg; + } + else + { + alias predArgFun = predArg; + } + + enum isSortedRange = (is(T == SortedRange!Args, Args...) && + is(typeof(predFun) == typeof(predArgFun))); } else { - alias predFun = pred; + enum isSortedRange = false; } - - enum isSortedRange = (is(T == SortedRange!Args, Args...) && - is(typeof(predFun) == - typeof(binaryFun!(TemplateArgsOf!T[1])))); } /// @@ -7688,8 +7705,11 @@ unittest alias SR = SortedRange!(R, pred); static assert(isSortedRange!(SR, pred)); - static assert(isSortedRange!(SR, binaryFun!pred)); + + alias SR2 = SortedRange!(R, binaryFun!pred); + static assert(isSortedRange!(SR2, pred)); + static assert(isSortedRange!(SR2, binaryFun!pred)); } /** From a0c62d91bb6da1b4a0fe163b6604a4d64ba04443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 16:47:45 +0200 Subject: [PATCH 04/22] Add alias fun in unittest --- std/range/package.d | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index 2f81bddfdd8..b9b97f9e3a5 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -7702,14 +7702,15 @@ unittest alias R = int[]; enum pred = "a < b"; + alias fun = binaryFun!pred; alias SR = SortedRange!(R, pred); static assert(isSortedRange!(SR, pred)); - static assert(isSortedRange!(SR, binaryFun!pred)); + static assert(isSortedRange!(SR, fun)); alias SR2 = SortedRange!(R, binaryFun!pred); static assert(isSortedRange!(SR2, pred)); - static assert(isSortedRange!(SR2, binaryFun!pred)); + static assert(isSortedRange!(SR2, fun)); } /** From 79ae662971b22f7f5c5bef8513dffc0108045f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 17:18:24 +0200 Subject: [PATCH 05/22] Move isSortedRange to primitives --- std/range/package.d | 58 ------------------------------------- std/range/primitives.d | 66 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 58 deletions(-) diff --git a/std/range/package.d b/std/range/package.d index b9b97f9e3a5..c89727f76a0 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -7655,64 +7655,6 @@ unittest assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]); } -/** - Returns true if $(D T) is an instance of the template $(D T) with template - parameters $(D Ps). -*/ -template isSortedRange(T, alias pred = "a < b") -{ - import std.traits : TemplateArgsOf; - - static if (TemplateArgsOf!T.length == 2) - { - import std.functional : binaryFun; - - alias predArg = TemplateArgsOf!T[1]; - static if (isSomeString!(typeof(pred))) - { - alias predFun = binaryFun!pred; - } - else - { - alias predFun = pred; - } - - static if (isSomeString!(typeof(predArg))) - { - alias predArgFun = binaryFun!predArg; - } - else - { - alias predArgFun = predArg; - } - - enum isSortedRange = (is(T == SortedRange!Args, Args...) && - is(typeof(predFun) == typeof(predArgFun))); - } - else - { - enum isSortedRange = false; - } -} - -/// -unittest -{ - import std.functional : binaryFun; - - alias R = int[]; - enum pred = "a < b"; - alias fun = binaryFun!pred; - - alias SR = SortedRange!(R, pred); - static assert(isSortedRange!(SR, pred)); - static assert(isSortedRange!(SR, fun)); - - alias SR2 = SortedRange!(R, binaryFun!pred); - static assert(isSortedRange!(SR2, pred)); - static assert(isSortedRange!(SR2, fun)); -} - /** $(D SortedRange) could accept ranges weaker than random-access, but it is unable to provide interesting functionality for them. Therefore, diff --git a/std/range/primitives.d b/std/range/primitives.d index aff94f9b2d6..0a3fcd3446b 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -31,6 +31,10 @@ $(BOOKTABLE , bidirectional _range that also supports the array subscripting operation via the primitive $(D opIndex). )) + $(TR $(TD $(D $(LREF isSortedRange))) + $(TD Tests if something is a $(I sorted _range) sorted on a specific + predicate function $(D pred). + )) ) It also provides number of templates that test for various _range capabilities: @@ -993,6 +997,68 @@ unittest static assert( isRandomAccessRange!(inout(int)[])); } +/** + Returns true if $(D T) is a Range Sorted on predicate $(D pred). + + Currently checks if $(D T) is an instance of $(D SortedRange). +*/ +template isSortedRange(T, alias pred = "a < b") +{ + import std.traits : TemplateArgsOf; + + static if (TemplateArgsOf!T.length == 2) + { + import std.functional : binaryFun; + + alias predArg = TemplateArgsOf!T[1]; + static if (isSomeString!(typeof(pred))) + { + alias predFun = binaryFun!pred; + } + else + { + alias predFun = pred; + } + + static if (isSomeString!(typeof(predArg))) + { + alias predArgFun = binaryFun!predArg; + } + else + { + alias predArgFun = predArg; + } + + import std.range: SortedRange; + enum isSortedRange = (is(T == SortedRange!Args, Args...) && + is(typeof(predFun) == typeof(predArgFun))); + } + else + { + enum isSortedRange = false; + } +} + +/// +unittest +{ + import std.functional : binaryFun; + + alias R = int[]; + enum pred = "a < b"; + alias fun = binaryFun!pred; + + import std.range: SortedRange; + + alias SR = SortedRange!(R, pred); + static assert(isSortedRange!(SR, pred)); + static assert(isSortedRange!(SR, fun)); + + alias SR2 = SortedRange!(R, binaryFun!pred); + static assert(isSortedRange!(SR2, pred)); + static assert(isSortedRange!(SR2, fun)); +} + @safe unittest { // Test fix for bug 6935. From 95e8d5516f04ee86f29fd748aa60b13d4d4d6fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 17:38:51 +0200 Subject: [PATCH 06/22] Add failing use of isSortedRange in sort() --- std/algorithm/sorting.d | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 7012d01eb06..52b6bb9a3dc 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -967,24 +967,32 @@ sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Stable sorting uses TimSort, which needs to copy elements into a buffer, requiring assignable elements. +/ { - import std.range : assumeSorted; - alias lessFun = binaryFun!(less); - alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun - static if (is(LessRet == bool)) + import std.range.primitives: isSortedRange; + static if (isSortedRange!(Range, less)) { - static if (ss == SwapStrategy.unstable) - quickSortImpl!(lessFun)(r, r.length); - else //use Tim Sort for semistable & stable - TimSortImpl!(lessFun, Range).sort(r, null); - - enum maxLen = 8; - assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof); + return r; } else { - static assert(false, "Invalid predicate passed to sort: " ~ less.stringof); + import std.range : assumeSorted; + alias lessFun = binaryFun!(less); + alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun + static if (is(LessRet == bool)) + { + static if (ss == SwapStrategy.unstable) + quickSortImpl!(lessFun)(r, r.length); + else //use Tim Sort for semistable & stable + TimSortImpl!(lessFun, Range).sort(r, null); + + enum maxLen = 8; + assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof); + } + else + { + static assert(false, "Invalid predicate passed to sort: " ~ less.stringof); + } + return assumeSorted!less(r); } - return assumeSorted!less(r); } /// @@ -2859,4 +2867,3 @@ shapes. Here's a non-trivial example: } assert(n == 60); } - From 513c68099c0b9ddf18e3f79ee1693948773b42de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 18:01:36 +0200 Subject: [PATCH 07/22] Make isSortedRange more robust --- std/range/primitives.d | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/std/range/primitives.d b/std/range/primitives.d index 0a3fcd3446b..2eed6f9b4aa 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1005,8 +1005,10 @@ unittest template isSortedRange(T, alias pred = "a < b") { import std.traits : TemplateArgsOf; + import std.range: SortedRange; - static if (TemplateArgsOf!T.length == 2) + static if (is(T == SortedRange!Args, Args...) && + TemplateArgsOf!T.length == 2) { import std.functional : binaryFun; @@ -1029,9 +1031,7 @@ template isSortedRange(T, alias pred = "a < b") alias predArgFun = predArg; } - import std.range: SortedRange; - enum isSortedRange = (is(T == SortedRange!Args, Args...) && - is(typeof(predFun) == typeof(predArgFun))); + enum isSortedRange = is(typeof(predFun) == typeof(predArgFun)); } else { @@ -1057,6 +1057,10 @@ unittest alias SR2 = SortedRange!(R, binaryFun!pred); static assert(isSortedRange!(SR2, pred)); static assert(isSortedRange!(SR2, fun)); + + alias IR = int[]; + static assert(!isSortedRange!(IR, pred)); + static assert(!isSortedRange!(IR, fun)); } @safe unittest From ac8967120fe11acda7896d8d9140d88345ccbec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 18:05:52 +0200 Subject: [PATCH 08/22] Add comment --- std/algorithm/sorting.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 52b6bb9a3dc..76c8996d1cc 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -954,7 +954,7 @@ See_Also: */ SortedRange!(Range, less) sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, - Range)(Range r) + Range)(Range r) if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || hasAssignableElements!Range)) || (ss != SwapStrategy.unstable && hasAssignableElements!Range)) && @@ -970,7 +970,7 @@ sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, import std.range.primitives: isSortedRange; static if (isSortedRange!(Range, less)) { - return r; + return r; // already sorted, so just return $(D r) as is } else { From 07063e0ca6765a87fdf8a373490e7414a961119e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 18:11:49 +0200 Subject: [PATCH 09/22] Add specialization of isSorted --- std/algorithm/sorting.d | 59 +++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 76c8996d1cc..90269651dfe 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -131,39 +131,45 @@ less). */ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) { - if (r.empty) return true; - - static if (isRandomAccessRange!Range && hasLength!Range) + static if (isSortedRange!(Range, less)) { - immutable limit = r.length - 1; - foreach (i; 0 .. limit) - { - if (!binaryFun!less(r[i + 1], r[i])) continue; - assert( - !binaryFun!less(r[i], r[i + 1]), - "Predicate for isSorted is not antisymmetric. Both" ~ - " pred(a, b) and pred(b, a) are true for certain values."); - return false; - } + return true; } else { - auto ahead = r; - ahead.popFront(); - size_t i; - - for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i) + if (r.empty) return true; + static if (isRandomAccessRange!Range && hasLength!Range) { - if (!binaryFun!less(ahead.front, r.front)) continue; - // Check for antisymmetric predicate - assert( - !binaryFun!less(r.front, ahead.front), - "Predicate for isSorted is not antisymmetric. Both" ~ - " pred(a, b) and pred(b, a) are true for certain values."); - return false; + immutable limit = r.length - 1; + foreach (i; 0 .. limit) + { + if (!binaryFun!less(r[i + 1], r[i])) continue; + assert( + !binaryFun!less(r[i], r[i + 1]), + "Predicate for isSorted is not antisymmetric. Both" ~ + " pred(a, b) and pred(b, a) are true for certain values."); + return false; + } } + else + { + auto ahead = r; + ahead.popFront(); + size_t i; + + for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i) + { + if (!binaryFun!less(ahead.front, r.front)) continue; + // Check for antisymmetric predicate + assert( + !binaryFun!less(r.front, ahead.front), + "Predicate for isSorted is not antisymmetric. Both" ~ + " pred(a, b) and pred(b, a) are true for certain values."); + return false; + } + } + return true; } - return true; } /// @@ -175,6 +181,7 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) assert(isSorted(arr)); sort!("a > b")(arr); assert(isSorted!("a > b")(arr)); + assert(isSorted(sort(arr))); } @safe unittest From 06701745e4fb3c31f88653ee75d497859954feaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Fri, 7 Aug 2015 18:13:10 +0200 Subject: [PATCH 10/22] Remove import std.range.primitives: isSortedRange; --- std/algorithm/sorting.d | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/std/algorithm/sorting.d b/std/algorithm/sorting.d index 90269651dfe..c2c46fb958b 100644 --- a/std/algorithm/sorting.d +++ b/std/algorithm/sorting.d @@ -973,8 +973,7 @@ sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, swaps using assignment. Stable sorting uses TimSort, which needs to copy elements into a buffer, requiring assignable elements. +/ -{ - import std.range.primitives: isSortedRange; + { static if (isSortedRange!(Range, less)) { return r; // already sorted, so just return $(D r) as is From 144c686850b819e6cc0a0b6f25551bd34b4fb278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Sat, 8 Aug 2015 11:02:52 +0200 Subject: [PATCH 11/22] Support minPos --- std/algorithm/searching.d | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index fcb9b4fa6e1..4c64e3d7b71 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -2777,23 +2777,30 @@ smallest element and with the same ending as $(D range). The function can actually be used for finding the maximum or any other ordering predicate (that's why $(D maxPos) is not provided). */ -Range minPos(alias pred = "a < b", Range)(Range range) +auto minPos(alias pred = "a < b", Range)(Range range) if (isForwardRange!Range && !isInfinite!Range && is(typeof(binaryFun!pred(range.front, range.front)))) { if (range.empty) return range; - auto result = range.save; - - for (range.popFront(); !range.empty; range.popFront()) + static if (isSortedRange!(Range, pred)) + { + return range; + } + else { - //Note: Unlike minCount, we do not care to find equivalence, so a single pred call is enough - if (binaryFun!pred(range.front, result.front)) + auto result = range.save; + + for (range.popFront(); !range.empty; range.popFront()) { - // change the min - result = range.save; + //Note: Unlike minCount, we do not care to find equivalence, so a single pred call is enough + if (binaryFun!pred(range.front, result.front)) + { + // change the min + result = range.save; + } } + return result; } - return result; } /// @@ -2804,6 +2811,11 @@ Range minPos(alias pred = "a < b", Range)(Range range) assert(minPos(a) == [ 1, 2, 4, 1, 1, 2 ]); // Maximum is 4 and first occurs in position 2 assert(minPos!("a > b")(a) == [ 4, 1, 2, 4, 1, 1, 2 ]); + + // Test SortedRange as input + import std.algorithm.sorting : sort; + import std.algorithm : equal; + assert(equal(minPos(a.sort()), [ 1, 1, 1, 2, 2, 2, 3, 4, 4 ])); } @safe unittest @@ -3494,4 +3506,3 @@ unittest // Issue 13124 auto s = "hello how\nare you"; s.until!(c => c.among!('\n', '\r')); } - From cb2c335643925fc90e56c5ea87e054c3b3ecfc78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Sat, 8 Aug 2015 14:40:31 +0200 Subject: [PATCH 12/22] Bugfix --- std/range/primitives.d | 52 ++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/std/range/primitives.d b/std/range/primitives.d index 2eed6f9b4aa..567895c2a34 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1005,7 +1005,7 @@ unittest template isSortedRange(T, alias pred = "a < b") { import std.traits : TemplateArgsOf; - import std.range: SortedRange; + import std.range : SortedRange; static if (is(T == SortedRange!Args, Args...) && TemplateArgsOf!T.length == 2) @@ -1013,25 +1013,39 @@ template isSortedRange(T, alias pred = "a < b") import std.functional : binaryFun; alias predArg = TemplateArgsOf!T[1]; - static if (isSomeString!(typeof(pred))) - { - alias predFun = binaryFun!pred; - } - else - { - alias predFun = pred; - } - static if (isSomeString!(typeof(predArg))) + static if (isSomeString!(typeof(pred)) && + isSomeString!(typeof(predArg))) { - alias predArgFun = binaryFun!predArg; + /* + TODO Remove this when we find a way to + - distinguish binaryFun!"a < b" from binaryFun!"a > b" + - equate binaryFun!"a < b" from binaryFun!"a Date: Sat, 8 Aug 2015 14:45:01 +0200 Subject: [PATCH 13/22] Add minElement and maxElement --- std/algorithm/searching.d | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index 4c64e3d7b71..b6430ee7cae 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -2864,6 +2864,76 @@ unittest assert(minPos!("a[0] < b[0]")(b) == [ [2, 4], [4], [4] ]); } +import std.algorithm.comparison : min, max; + +/** Returns: Minimum Element in $(D range) or $(D unit) if $(D range) is empty. +*/ +auto minElement(alias F = min, R)(R range, + ElementType!R unit = ElementType!R.max) + if (isInputRange!R) +{ + import std.range.primitives : isSortedRange; + static if (isSortedRange!(R, "a < b")) + { + import std.range.primitives : front; + return range.empty ? unit : range.front; + } + else static if (isSortedRange!(R, "a > b") && + isBidirectionalRange!R) + { + import std.range.primitives : back; + return range.empty ? unit : range.back; + } + else + { + import std.algorithm.iteration : reduce; + return reduce!F(unit, range); + } +} + +@safe pure nothrow unittest +{ + import std.algorithm.sorting : sort, assumeSorted; + auto x = [2, 4, 1, 3]; + assert(x.minElement == 1); + assert(x.sort!"a < b".minElement == 1); + assert(x.sort!"a > b".minElement == 1); +} + +/** Returns: Maximum Element in $(D range) or $(D unit) if $(D range) is empty. + */ +auto maxElement(alias F = max, R)(R range, + ElementType!R unit = ElementType!R.min) + if (isInputRange!R) +{ + import std.range.primitives : isSortedRange; + static if (isSortedRange!(R, "a > b")) + { + import std.range.primitives : front; + return range.empty ? unit : range.front; + } + else static if (isSortedRange!(R, "a < b") && + isBidirectionalRange!R) + { + import std.range.primitives : back; + return range.empty ? unit : range.back; + } + else + { + import std.algorithm.iteration : reduce; + return reduce!F(unit, range); + } +} + +@safe pure nothrow unittest +{ + import std.algorithm.sorting : sort, assumeSorted; + auto x = [2, 4, 1, 3]; + assert(x.maxElement == 4); + assert(x.sort!"a < b".maxElement == 4); + assert(x.sort!"a > b".maxElement == 4); +} + /** Skip over the initial portion of the first given range that matches the second range, or do nothing if there is no match. From 9f3e5551d13096d1387ed833e035ce9387416f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Sat, 8 Aug 2015 14:47:22 +0200 Subject: [PATCH 14/22] Move empty check in minPos to else clauses --- std/algorithm/searching.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index b6430ee7cae..f7374bff9c9 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -2781,13 +2781,13 @@ auto minPos(alias pred = "a < b", Range)(Range range) if (isForwardRange!Range && !isInfinite!Range && is(typeof(binaryFun!pred(range.front, range.front)))) { - if (range.empty) return range; static if (isSortedRange!(Range, pred)) { return range; } else { + if (range.empty) return range; auto result = range.save; for (range.popFront(); !range.empty; range.popFront()) From 88ac1b0fd8722efe49ec39edcf85786317a5de9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Thu, 13 Aug 2015 18:16:15 +0200 Subject: [PATCH 15/22] Add space --- std/algorithm/searching.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index f7374bff9c9..c442b4d9e0c 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -2867,7 +2867,7 @@ unittest import std.algorithm.comparison : min, max; /** Returns: Minimum Element in $(D range) or $(D unit) if $(D range) is empty. -*/ + */ auto minElement(alias F = min, R)(R range, ElementType!R unit = ElementType!R.max) if (isInputRange!R) From 88f586d54f04bcd2030f9043c303180d3b482203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Mon, 17 Aug 2015 16:40:13 +0200 Subject: [PATCH 16/22] Only calculate TemplateArgsOf!T once --- std/range/primitives.d | 70 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/std/range/primitives.d b/std/range/primitives.d index 567895c2a34..c837b460a88 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1004,47 +1004,53 @@ unittest */ template isSortedRange(T, alias pred = "a < b") { - import std.traits : TemplateArgsOf; import std.range : SortedRange; - - static if (is(T == SortedRange!Args, Args...) && - TemplateArgsOf!T.length == 2) + static if (is(T == SortedRange!Args, Args...)) { - import std.functional : binaryFun; - - alias predArg = TemplateArgsOf!T[1]; - - static if (isSomeString!(typeof(pred)) && - isSomeString!(typeof(predArg))) + import std.traits : TemplateArgsOf; + alias TArgs = TemplateArgsOf!T; + static if (TArgs.length == 2) { - /* - TODO Remove this when we find a way to - - distinguish binaryFun!"a < b" from binaryFun!"a > b" - - equate binaryFun!"a < b" from binaryFun!"a b" + - equate binaryFun!"a < b" from binaryFun!"a Date: Mon, 17 Aug 2015 16:52:09 +0200 Subject: [PATCH 17/22] Use __traits(isSame, ) --- std/range/primitives.d | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/std/range/primitives.d b/std/range/primitives.d index c837b460a88..e34fd3cd788 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1001,6 +1001,8 @@ unittest Returns true if $(D T) is a Range Sorted on predicate $(D pred). Currently checks if $(D T) is an instance of $(D SortedRange). + + See also: http://forum.dlang.org/post/mqskge$2968$1@digitalmars.com */ template isSortedRange(T, alias pred = "a < b") { @@ -1045,7 +1047,7 @@ template isSortedRange(T, alias pred = "a < b") alias predArgFun = predArg; } - enum isSortedRange = is(typeof(predFun) == typeof(predArgFun)); + enum isSortedRange = __traits(isSame, predFun, predArgFun); } } else From d6d9c1fbab87dade813a78af43522d534d834ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Mon, 17 Aug 2015 17:15:48 +0200 Subject: [PATCH 18/22] Fix bug in isSortedRange --- std/range/primitives.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/std/range/primitives.d b/std/range/primitives.d index e34fd3cd788..5f89a214aa7 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1072,7 +1072,7 @@ unittest alias fun = binaryFun!pred; enum rpred = "a > b"; - alias rfun = binaryFun!pred; + alias rfun = binaryFun!rpred; import std.range : SortedRange; @@ -1086,7 +1086,7 @@ unittest alias SR_ = SortedRange!(R, pred); static assert(!isSortedRange!(SR_, rpred)); - // static assert(!isSortedRange!(SR_, rfun)); + static assert(!isSortedRange!(SR_, rfun)); alias IR = int[]; static assert(!isSortedRange!(IR, pred)); From 681d476b186ad9d1bb742a7b4b6ad423cc1e4220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Tue, 18 Aug 2015 12:26:37 +0200 Subject: [PATCH 19/22] Robuster isSortedRange --- std/range/primitives.d | 95 ++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 32 deletions(-) diff --git a/std/range/primitives.d b/std/range/primitives.d index 5f89a214aa7..c58129cffbf 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -997,6 +997,52 @@ unittest static assert( isRandomAccessRange!(inout(int)[])); } +auto standardizePredicatePrefix(S)(S s) if (isSomeString!S) +{ + if (s.length >= 2) + { + if (s[0 .. 2] == "a ") + { + return "a" ~ s[2 .. $]; + } + } + return s; +} + +auto standardizePredicateSuffix(S)(S s) if (isSomeString!S) +{ + if (s.length >= 2) + { + if (s[2 .. $] == " b") + { + return s[0 .. $ - 2] ~ "b"; + } + } + return s; +} + +auto standardizePredicate(S)(S s) if (isSomeString!S) +{ + return standardizePredicateSuffix(standardizePredicatePrefix(s)); +} + +unittest +{ + static assert(standardizePredicate("a < b") == "a b") == "a>b"); +} + +import std.functional : binaryFun; + +enum binaryFunString(alias pred : binaryFun!T, T ...) = T[0]; + +unittest +{ + enum pred = "a+b"; + alias x = binaryFun!pred; + static assert(binaryFunString!x == pred); +} + /** Returns true if $(D T) is a Range Sorted on predicate $(D pred). @@ -1014,41 +1060,26 @@ template isSortedRange(T, alias pred = "a < b") static if (TArgs.length == 2) { alias predArg = TArgs[1]; - - static if (isSomeString!(typeof(pred)) && - isSomeString!(typeof(predArg))) + static if (isSomeString!(typeof(pred))) { - /* - TODO Remove this when we find a way to - - distinguish binaryFun!"a < b" from binaryFun!"a > b" - - equate binaryFun!"a < b" from binaryFun!"a b"; import std.range : SortedRange; From f6b9df74fa572cca73a34cf7d12147184a74c69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Tue, 18 Aug 2015 12:38:58 +0200 Subject: [PATCH 20/22] More general standardizePredicate --- std/range/primitives.d | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/std/range/primitives.d b/std/range/primitives.d index c58129cffbf..f3b61c5635a 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1001,10 +1001,15 @@ auto standardizePredicatePrefix(S)(S s) if (isSomeString!S) { if (s.length >= 2) { - if (s[0 .. 2] == "a ") - { - return "a" ~ s[2 .. $]; - } + if (s[0] != 'a') + return s; + + size_t wsCount = 0; + while (1 + wsCount < s.length && + s[1 + wsCount] == ' ') + ++wsCount; + + return 'a' ~ s[1 + wsCount .. $]; } return s; } @@ -1013,10 +1018,15 @@ auto standardizePredicateSuffix(S)(S s) if (isSomeString!S) { if (s.length >= 2) { - if (s[2 .. $] == " b") - { - return s[0 .. $ - 2] ~ "b"; - } + if (s[$ - 1] != 'b') + return s; + + size_t wsCount = 0; + while (1 + wsCount < s.length && + s[$ - 2 - wsCount] == ' ') + ++wsCount; + + return s[0 .. $ - 1 - wsCount] ~ 'b'; } return s; } @@ -1029,6 +1039,8 @@ auto standardizePredicate(S)(S s) if (isSomeString!S) unittest { static assert(standardizePredicate("a < b") == "a b") == "a>b"); } @@ -1066,7 +1078,7 @@ template isSortedRange(T, alias pred = "a < b") } else { - alias predString = binaryFunString!(pred); + alias predString = binaryFunString!pred; } static if (isSomeString!(typeof(predArg))) @@ -1075,7 +1087,7 @@ template isSortedRange(T, alias pred = "a < b") } else { - alias predArgString = binaryFunString!(predArg); + alias predArgString = binaryFunString!predArg; } enum isSortedRange = (standardizePredicate(predString) == From 2812b99eefc0eb86d2b6d04d07d998dafb52b93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Tue, 18 Aug 2015 12:40:13 +0200 Subject: [PATCH 21/22] Better naming comments for standardizePredicate* --- std/range/primitives.d | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/std/range/primitives.d b/std/range/primitives.d index f3b61c5635a..43ce0ae63f4 100644 --- a/std/range/primitives.d +++ b/std/range/primitives.d @@ -1004,12 +1004,12 @@ auto standardizePredicatePrefix(S)(S s) if (isSomeString!S) if (s[0] != 'a') return s; - size_t wsCount = 0; - while (1 + wsCount < s.length && - s[1 + wsCount] == ' ') - ++wsCount; + size_t n = 0; // whitespace count + while (1 + n < s.length && + s[1 + n] == ' ') + ++n; - return 'a' ~ s[1 + wsCount .. $]; + return 'a' ~ s[1 + n .. $]; } return s; } @@ -1021,12 +1021,12 @@ auto standardizePredicateSuffix(S)(S s) if (isSomeString!S) if (s[$ - 1] != 'b') return s; - size_t wsCount = 0; - while (1 + wsCount < s.length && - s[$ - 2 - wsCount] == ' ') - ++wsCount; + size_t n = 0; // whitespace count + while (1 + n < s.length && + s[$ - 2 - n] == ' ') + ++n; - return s[0 .. $ - 1 - wsCount] ~ 'b'; + return s[0 .. $ - 1 - n] ~ 'b'; } return s; } From b7bf452ca52c2a529e79a830eee97310233e3a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Nordl=C3=B6w?= Date: Thu, 3 Sep 2015 09:33:12 +0200 Subject: [PATCH 22/22] Change return type of minPos from auto to Range --- std/algorithm/searching.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index c442b4d9e0c..ed43ad1cb4a 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -2777,7 +2777,7 @@ smallest element and with the same ending as $(D range). The function can actually be used for finding the maximum or any other ordering predicate (that's why $(D maxPos) is not provided). */ -auto minPos(alias pred = "a < b", Range)(Range range) +Range minPos(alias pred = "a < b", Range)(Range range) if (isForwardRange!Range && !isInfinite!Range && is(typeof(binaryFun!pred(range.front, range.front)))) {