diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d index fe47834..d39b0c5 100644 --- a/source/tanya/range/primitive.d +++ b/source/tanya/range/primitive.d @@ -14,6 +14,7 @@ */ module tanya.range.primitive; +import tanya.algorithm.mutation; import tanya.math; import tanya.meta.trait; import tanya.meta.transform; @@ -1355,3 +1356,501 @@ if (isBidirectionalRange!R) assert(range.empty); } } + +/** + * Moves the front element of an input range. + * + * The front element is left in a valid but inspecified state. + * $(D_PSYMBOL moveFront) doesn't advances the range, so `popFront` should be + * probably called after this function. + * + * Params: + * R = Type of the range. + * range = Input range. + * + * Returns: The front element of the $(D_PSYMBOL range). + * + * See_Also: $(D_PSYMBOL move). + */ +ElementType!R moveFront(R)(R range) +if (isInputRange!R) +{ + static if (is(typeof(R.moveFront))) + { + return range.moveFront(); + } + else static if (!hasElaborateCopyConstructor!(ElementType!R)) + { + return range.front; + } + else static if (is(typeof(((ref ElementType!R e) => e)(range.front)))) + { + return move(range.front); + } + else + { + static assert(false, "Front element cannot be moved"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + // Defines moveFront. + static struct R1 + { + enum bool empty = false; + + int moveFront() @nogc nothrow pure @safe + { + return 5; + } + alias front = moveFront; + + void popFront() @nogc nothrow pure @safe + { + } + } + assert(moveFront(R1()) == 5); + + // Has elements without a postblit constructor. + static struct R2 + { + enum bool empty = false; + + int front() const @nogc nothrow pure @safe + { + return 5; + } + + void popFront() @nogc nothrow pure @safe + { + } + } + assert(moveFront(R2()) == 5); + + static struct Element + { + this(this) + { + assert(false); + } + } + // Returns its elements by reference. + static struct R3 + { + Element element; + enum bool empty = false; + + ref Element front() @nogc nothrow pure @safe + { + return element; + } + + void popFront() @nogc nothrow pure @safe + { + } + } + static assert(is(typeof(moveFront(R3())))); + + // Returns elements with a postblit constructor by value. moveFront fails. + static struct R4 + { + enum bool empty = false; + + Element front() @nogc nothrow pure @safe + { + return Element(); + } + + void popFront() @nogc nothrow pure @safe + { + } + } + static assert(!is(typeof(moveFront(R4())))); +} + +/** + * Moves the back element of a bidirectional range. + * + * The back element is left in a valid but inspecified state. + * $(D_PSYMBOL moveBack) doesn't advances the range, so `popBack` should be + * probably called after this function. + * + * Params: + * R = Type of the range. + * range = Bidirectional range. + * + * Returns: The back element of the $(D_PSYMBOL range). + * + * See_Also: $(D_PSYMBOL move). + */ +ElementType!R moveBack(R)(R range) +if (isBidirectionalRange!R) +{ + static if (is(typeof(R.moveBack))) + { + return range.moveBack(); + } + else static if (!hasElaborateCopyConstructor!(ElementType!R)) + { + return range.back; + } + else static if (is(typeof(((ref ElementType!R e) => e)(range.back)))) + { + return move(range.back); + } + else + { + static assert(false, "Back element cannot be moved"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + // Defines moveBack. + static struct R1 + { + enum bool empty = false; + + int moveBack() @nogc nothrow pure @safe + { + return 5; + } + alias back = moveBack; + alias front = moveBack; + + void popBack() @nogc nothrow pure @safe + { + } + alias popFront = popBack; + + R1 save() @nogc nothrow pure @safe + { + return this; + } + } + assert(moveBack(R1()) == 5); + + // Has elements without a postblit constructor. + static struct R2 + { + enum bool empty = false; + + int back() const @nogc nothrow pure @safe + { + return 5; + } + alias front = back; + + void popBack() @nogc nothrow pure @safe + { + } + alias popFront = popBack; + + R2 save() @nogc nothrow pure @safe + { + return this; + } + } + assert(moveBack(R2()) == 5); + + static struct Element + { + this(this) + { + assert(false); + } + } + // Returns its elements by reference. + static struct R3 + { + Element element; + enum bool empty = false; + + ref Element back() @nogc nothrow pure @safe + { + return element; + } + alias front = back; + + void popBack() @nogc nothrow pure @safe + { + } + alias popFront = popBack; + + R3 save() @nogc nothrow pure @safe + { + return this; + } + } + static assert(is(typeof(moveBack(R3())))); + + // Returns elements with a postblit constructor by value. moveBack fails. + static struct R4 + { + enum bool empty = false; + + Element back() @nogc nothrow pure @safe + { + return Element(); + } + alias front = back; + + void popBack() @nogc nothrow pure @safe + { + } + alias popFront = popBack; + + R4 save() @nogc nothrow pure @safe + { + return this; + } + } + static assert(!is(typeof(moveBack(R4())))); +} + +/** + * Moves the element at the position $(D_PARAM n) out of the range. + * + * The moved element is left in a valid but inspecified state. + * + * Params: + * R = Type of the range. + * range = Random-access range. + * n = Element position. + * + * Returns: The element at the position $(D_PARAM n). + * + * See_Also: $(D_PSYMBOL move). + */ +ElementType!R moveAt(R)(R range, size_t n) +if (isRandomAccessRange!R) +{ + static if (is(typeof(R.moveAt))) + { + return range.moveAt(n); + } + else static if (!hasElaborateCopyConstructor!(ElementType!R)) + { + return range[n]; + } + else static if (is(typeof(((ref ElementType!R e) => e)(range[0])))) + { + return move(range[n]); + } + else + { + static assert(false, "Back element cannot be moved"); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + // Defines moveAt. + static struct R1 + { + enum bool empty = false; + + int front() @nogc nothrow pure @safe + { + return 5; + } + + void popFront() @nogc nothrow pure @safe + { + } + + int moveAt(size_t) @nogc nothrow pure @safe + { + return 5; + } + alias opIndex = moveAt; + } + assert(moveAt(R1(), 0) == 5); + + // Has elements without a postblit constructor. + static struct R2 + { + enum bool empty = false; + + int front() const @nogc nothrow pure @safe + { + return 5; + } + + void popFront() @nogc nothrow pure @safe + { + } + + int opIndex(size_t) const @nogc nothrow pure @safe + { + return 5; + } + } + assert(moveAt(R2(), 0) == 5); + + static struct Element + { + this(this) + { + assert(false); + } + } + // Returns its elements by reference. + static struct R3 + { + Element element; + enum bool empty = false; + + ref Element front() @nogc nothrow pure @safe + { + return element; + } + + void popFront() @nogc nothrow pure @safe + { + } + + ref Element opIndex(size_t) + { + return element; + } + } + static assert(is(typeof(moveAt(R3(), 0)))); + + // Returns elements with a postblit constructor by value. moveAt fails. + static struct R4 + { + enum bool empty = false; + + Element front() @nogc nothrow pure @safe + { + return Element(); + } + + void popFront() @nogc nothrow pure @safe + { + } + + Element opIndex() @nogc nothrow pure @safe + { + return Element(); + } + } + static assert(!is(typeof(moveAt(R4(), 0)))); +} + +/** + * Determines whether $(D_PSYMBOL R) is a range containing mobile elements, + * i.e. elements that can be moved out of the range. + * + * Having mobile elements means for an input range to support + * $(D_PSYMBOL moveFront), for a bidirectional range - both, + * $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveBack), for a random-access - + * $(D_PSYMBOL moveFront) and $(D_PSYMBOL moveAt). + * + * Params: + * R = Range type. + * + * Returns: $(D_KEYWORD true) if $(D_PSYMBOL R) has mobile elements, + * $(D_KEYWORD false) otherwise. + * + * See_Also: $(D_PSYMBOL moveFront), $(D_PSYMBOL moveBack), + * $(D_PSYMBOL moveAt). + */ +template hasMobileElements(R) +{ + static if (isRandomAccessRange!R) + { + enum bool hasMobileElements = is(typeof((R r) => moveFront(r))) + && is(typeof((R r) => moveAt(r, 0))); + } + else static if (isBidirectionalRange!R) + { + enum bool hasMobileElements = is(typeof((R r) => moveFront(r))) + && is(typeof((R r) => moveBack(r))); + } + else + { + enum bool hasMobileElements = is(typeof((R r) => moveFront(r))); + } +} + +/// +@nogc nothrow pure @safe unittest +{ + // Input range with elements without a postblit constructor. + // These can be moved. + static struct InputRange + { + enum bool empty = false; + + int front() @nogc nothrow pure @safe + { + return 5; + } + + void popFront() @nogc nothrow pure @safe + { + } + } + static assert(hasMobileElements!InputRange); + + static struct Element + { + this(this) + { + } + } + // Bidirectional range, whose elements cannot be moved. The range defines + // only moveFront, but not moveBack. So it doesn't have mobile elements. + static struct BidirectionalRange + { + enum bool empty = false; + + Element front() @nogc nothrow pure @safe + { + return Element(); + } + alias back = front; + alias moveFront = front; + + void popFront() @nogc nothrow pure @safe + { + } + alias popBack = popFront; + + BidirectionalRange save() @nogc nothrow pure @safe + { + return this; + } + } + static assert(!hasMobileElements!BidirectionalRange); + + // Access-random range, whose elements cannot be moved, but the range + // defines both, moveFront and moveAt. + static struct RandomAccessRange + { + enum bool empty = false; + + Element front() @nogc nothrow pure @safe + { + return Element(); + } + alias moveFront = front; + + void popFront() @nogc nothrow pure @safe + { + } + + Element opIndex(size_t) @nogc nothrow pure @safe + { + return Element(); + } + alias moveAt = opIndex; + } + static assert(hasMobileElements!RandomAccessRange); +}