// std.array @property bool empty(T)(in T[] a) { return !a.length; } @property ref T front(T)(T[] a) { return a[0]; } void popFront(T)(ref T[] a) { a = a[1 .. $]; } // std.typecons struct Tuple(T...) { T field; alias field this; } Tuple!T tuple(T...)(T args) { return typeof(return)(args); } // std.range template ElementType(R) { static if (is(typeof(R.init.front.init) T)) alias T ElementType; else alias void ElementType; } struct Repeat(T) { private T _value; enum bool empty = false; @property inout(T) front() inout { return _value; } void popFront() {} } Repeat!T repeat(T)(T value) { return Repeat!T(value); } struct Zip(R...) { //alias Tuple!(staticMap!(.ElementType, R)) ElementType; static if (R.length == 3) alias Tuple!(int, int, int) ElementType; static if (R.length == 2) alias Tuple!(int, int) ElementType; R ranges; this(R rs) { foreach (i, Unused; R) { ranges[i] = rs[i]; } } @property bool empty() { foreach (i, Unused; R) { if (ranges[i].empty) return true; } return false; } @property ElementType front() { ElementType result; return result; } void popFront() { foreach (i, Unused; R) { ranges[i].popFront(); } } ElementType opIndex(size_t n) { ElementType result; return result; } } auto zip(Rs...)(Rs ranges) { return Zip!Rs(ranges); } // std.algorithm template map(fun...) { auto map(Range)(Range r) { return MapResult!(fun, Range)(r); } } private struct MapResult(alias fun, R) { R _input; this(R input) { _input = input; } @property bool empty() { return _input.empty; } @property auto ref front() { return fun(_input.front); } void popFront() { _input.popFront(); } } auto cartesianProduct(R1, R2)(R1 range1, R2 range2) { return range2.map!((ElementType!R2 a) => zip(range1, repeat(a))); } auto cartesianProduct(R1, R2, RR...)(R1 range1, R2 range2, RR otherRanges) { return map!(a => tuple(a[0], a[1][0], a[1][1]))( cartesianProduct(range1, cartesianProduct(range2, otherRanges)) ); } // test void main() { foreach (i, j, k; cartesianProduct([1], [1], [1])) {} }