diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index 363bd16a0f9..08c88417f97 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -115,14 +115,20 @@ if (!allSatisfy!(isForwardRange, R1, R2) || else static if (isInputRange!R1 && isForwardRange!R2 && !isInfinite!R2) { import std.range : zip, repeat; - return joiner(map!((ElementType!R1 a) => zip(repeat(a), range2.save)) - (range1)); + import std.array : join; + auto pairs = map!((ElementType!R1 a) => zip(repeat(a), range2.save)) + (range1); + static if (!isInfinite!R1) return join(pairs); + else return joiner(pairs); } else static if (isInputRange!R2 && isForwardRange!R1 && !isInfinite!R1) { import std.range : zip, repeat; - return joiner(map!((ElementType!R2 a) => zip(range1.save, repeat(a))) - (range2)); + import std.array : join; + auto pairs = map!((ElementType!R2 a) => zip(range1.save, repeat(a))) + (range2); + static if (!isInfinite!R2) return join(pairs); + else return joiner(pairs); } else static assert(0, "cartesianProduct involving finite ranges must "~ "have at least one finite forward range"); @@ -160,6 +166,22 @@ if (!allSatisfy!(isForwardRange, R1, R2) || { assert(canFind(BC, tuple(n[0], n[1]))); } + assert(BC.length == 9); +} + +@safe unittest +{ + auto A = [ 1, 2, 3 ]; + auto B = [ 4, 5, 6 ]; + auto AB = cartesianProduct(A, B); + auto len = AB.length; + + assert(len == 9); + for (auto i = 0; i < len; i++) + { + assert(AB.length == len - i); + AB.popFront(); + } } @safe unittest @@ -250,6 +272,7 @@ if (!allSatisfy!(isForwardRange, R1, R2) || } // And therefore, by set comprehension, XY == Expected + assert(XY.length == Expected.length); } @safe unittest @@ -335,6 +358,32 @@ if (!allSatisfy!(isForwardRange, R1, R2) || assert(equal(map!"[a[0],a[1]]"(NB.take(10)), [[0, 1], [0, 2], [0, 3], [1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1]])); + // Test fifth case: (finite) input range with finite forward range + static struct InpFiniteRangeWrapper + { + int[] data; + @property int front() { return data[0]; } + @property bool empty() { return data.length == 0; } + @property void popFront() { data = data[1 .. $]; } + } + + auto X = InpFiniteRangeWrapper([0, 1, 2]); + auto XB = cartesianProduct(X, B); + foreach (n; [[0, 1], [0, 2], [0, 3], [1, 1], + [1, 2], [1, 3], [2, 1], [2, 2], [2, 3]]) + { + assert(canFind(XB, tuple(n[0], n[1]))); + } + assert(XB.length == 9); + auto Y = InpFiniteRangeWrapper([0, 1, 2]); + auto BY = cartesianProduct(B, Y); + foreach (n; [[1, 0], [2, 0], [3, 0], [1, 1], + [2, 1], [3, 1], [1, 2], [2, 2], [3, 2]]) + { + assert(canFind(BY, tuple(n[0], n[1]))); + } + assert(BY.length == 9); + // General finite range case auto C = [ 4, 5, 6 ]; auto BC = cartesianProduct(B, C); @@ -344,6 +393,7 @@ if (!allSatisfy!(isForwardRange, R1, R2) || { assert(canFind(BC, tuple(n[0], n[1]))); } + assert(BC.length == 9); } // https://issues.dlang.org/show_bug.cgi?id=13091 @@ -371,17 +421,26 @@ if (ranges.length >= 2 && { RR ranges; RR current; - bool empty = true; + size_t length = 0; this(RR _ranges) { + length = 1; ranges = _ranges; - empty = false; foreach (i, r; ranges) { current[i] = r.save; - if (current[i].empty) - empty = true; + static if (__traits(hasMember, current[i], "length")) + { + length *= current[i].length; + } + else + { + size_t count = 0; + for (auto tmp = r.save; !tmp.empty; tmp.popFront()) + ++count; + length *= count; + } } } @property auto front() @@ -398,11 +457,10 @@ if (ranges.length >= 2 && r.popFront(); if (!r.empty) break; - static if (i == 0) - empty = true; - else + static if (i != 0) r = ranges[i].save; // rollover } + length--; } @property Result save() return scope { @@ -414,6 +472,10 @@ if (ranges.length >= 2 && } return copy; } + @property bool empty() return scope + { + return length == 0; + } } static assert(isForwardRange!Result, Result.stringof ~ " must be a forward" ~ " range"); @@ -428,6 +490,7 @@ if (ranges.length >= 2 && int[] a, b, c, d, e; auto cprod = cartesianProduct(a,b,c,d,e); assert(cprod.empty); + assert(cprod.length == 0); foreach (_; cprod) {} // should not crash // Test case where only one of the ranges is empty: the result should still @@ -435,6 +498,7 @@ if (ranges.length >= 2 && int[] p=[1], q=[]; auto cprod2 = cartesianProduct(p,p,p,q,p); assert(cprod2.empty); + assert(cprod2.length == 0); foreach (_; cprod2) {} // should not crash } @@ -443,6 +507,7 @@ if (ranges.length >= 2 && // .init value of cartesianProduct should be empty auto cprod = cartesianProduct([0,0], [1,1], [2,2]); assert(!cprod.empty); + assert(cprod.length == 8); assert(cprod.init.empty); } @@ -519,6 +584,7 @@ if (!allSatisfy!(isForwardRange, R1, R2, RR) || auto C = [ "x", "y", "z" ]; auto ABC = cartesianProduct(A, B, C); + assert(ABC.length == 27); assert(ABC.equal([ tuple(1, 'a', "x"), tuple(1, 'a', "y"), tuple(1, 'a', "z"), tuple(1, 'b', "x"), tuple(1, 'b', "y"), tuple(1, 'b', "z"), @@ -584,8 +650,9 @@ pure @safe nothrow @nogc unittest } } - assert(SystemRange([1, 2]).cartesianProduct(SystemRange([3, 4])) - .equal([tuple(1, 3), tuple(1, 4), tuple(2, 3), tuple(2, 4)])); + auto cprod = SystemRange([1, 2]).cartesianProduct(SystemRange([3, 4])); + assert(cprod.length == 4); + assert(cprod.equal([tuple(1, 3), tuple(1, 4), tuple(2, 3), tuple(2, 4)])); } // largestPartialIntersection