// Written in the D programming language. /** This is a submodule of $(LINK2 std_algorithm.html, std.algorithm). It contains generic _mutation algorithms. $(BOOKTABLE Cheat Sheet, $(TR $(TH Function Name) $(TH Description)) $(T2 bringToFront, If $(D a = [1, 2, 3]) and $(D b = [4, 5, 6, 7]), $(D bringToFront(a, b)) leaves $(D a = [4, 5, 6]) and $(D b = [7, 1, 2, 3]).) $(T2 copy, Copies a range to another. If $(D a = [1, 2, 3]) and $(D b = new int[5]), then $(D copy(a, b)) leaves $(D b = [1, 2, 3, 0, 0]) and returns $(D b[3 .. $]).) $(T2 fill, Fills a range with a pattern, e.g., if $(D a = new int[3]), then $(D fill(a, 4)) leaves $(D a = [4, 4, 4]) and $(D fill(a, [3, 4])) leaves $(D a = [3, 4, 3]).) $(T2 initializeAll, If $(D a = [1.2, 3.4]), then $(D initializeAll(a)) leaves $(D a = [double.init, double.init]).) $(T2 move, $(D move(a, b)) moves $(D a) into $(D b). $(D move(a)) reads $(D a) destructively.) $(T2 moveAll, Moves all elements from one range to another.) $(T2 moveSome, Moves as many elements as possible from one range to another.) $(T2 remove, Removes elements from a range in-place, and returns the shortened range.) $(T2 reverse, If $(D a = [1, 2, 3]), $(D reverse(a)) changes it to $(D [3, 2, 1]).) $(T2 strip, Strips all leading and trailing elements equal to a value, or that satisfy a predicate. If $(D a = [1, 1, 0, 1, 1]), then $(D strip(a, 1)) and $(D strip!(e => e == 1)(a)) returns $(D [0]).) $(T2 stripLeft, Strips all leading elements equal to a value, or that satisfy a predicate. If $(D a = [1, 1, 0, 1, 1]), then $(D stripLeft(a, 1)) and $(D stripLeft!(e => e == 1)(a)) returns $(D [0, 1, 1]).) $(T2 stripRight, Strips all trailing elements equal to a value, or that satisfy a predicate. If $(D a = [1, 1, 0, 1, 1]), then $(D stripRight(a, 1)) and $(D stripRight!(e => e == 1)(a)) returns $(D [1, 1, 0]).) $(T2 swap, Swaps two values.) $(T2 swapRanges, Swaps all elements of two ranges.) $(T2 uninitializedFill, Fills a range (assumed uninitialized) with a value.) ) Copyright: Andrei Alexandrescu 2008-. License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(WEB erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/algorithm/_mutation.d) Macros: T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) */ module std.algorithm.mutation; import std.range.primitives; import std.traits : isBlitAssignable, isNarrowString; // FIXME import std.typecons; // : tuple, Tuple; // FIXME: somehow deleting this breaks the bringToFront() unittests. import std.range; // bringToFront /** The $(D bringToFront) function has considerable flexibility and usefulness. It can rotate elements in one buffer left or right, swap buffers of equal length, and even move elements across disjoint buffers of different types and different lengths. $(D bringToFront) takes two ranges $(D front) and $(D back), which may be of different types. Considering the concatenation of $(D front) and $(D back) one unified range, $(D bringToFront) rotates that unified range such that all elements in $(D back) are brought to the beginning of the unified range. The relative ordering of elements in $(D front) and $(D back), respectively, remains unchanged. Performs $(BIGOH max(front.length, back.length)) evaluations of $(D swap). Preconditions: Either $(D front) and $(D back) are disjoint, or $(D back) is reachable from $(D front) and $(D front) is not reachable from $(D back). Returns: The number of elements brought to the front, i.e., the length of $(D back). See_Also: $(WEB sgi.com/tech/stl/_rotate.html, STL's rotate) */ size_t bringToFront(Range1, Range2)(Range1 front, Range2 back) if (isInputRange!Range1 && isForwardRange!Range2) { import std.range: Take, take; enum bool sameHeadExists = is(typeof(front.sameHead(back))); size_t result; for (bool semidone; !front.empty && !back.empty; ) { static if (sameHeadExists) { if (front.sameHead(back)) break; // shortcut } // Swap elements until front and/or back ends. auto back0 = back.save; size_t nswaps; do { static if (sameHeadExists) { // Detect the stepping-over condition. if (front.sameHead(back0)) back0 = back.save; } swapFront(front, back); ++nswaps; front.popFront(); back.popFront(); } while (!front.empty && !back.empty); if (!semidone) result += nswaps; // Now deal with the remaining elements. if (back.empty) { if (front.empty) break; // Right side was shorter, which means that we've brought // all the back elements to the front. semidone = true; // Next pass: bringToFront(front, back0) to adjust the rest. back = back0; } else { assert(front.empty); // Left side was shorter. Let's step into the back. static if (is(Range1 == Take!Range2)) { front = take(back0, nswaps); } else { immutable subresult = bringToFront(take(back0, nswaps), back); if (!semidone) result += subresult; break; // done } } } return result; } /** The simplest use of $(D bringToFront) is for rotating elements in a buffer. For example: */ @safe unittest { auto arr = [4, 5, 6, 7, 1, 2, 3]; auto p = bringToFront(arr[0 .. 4], arr[4 .. $]); assert(p == arr.length - 4); assert(arr == [ 1, 2, 3, 4, 5, 6, 7 ]); } /** The $(D front) range may actually "step over" the $(D back) range. This is very useful with forward ranges that cannot compute comfortably right-bounded subranges like $(D arr[0 .. 4]) above. In the example below, $(D r2) is a right subrange of $(D r1). */ @safe unittest { import std.algorithm.comparison : equal; import std.container : SList; auto list = SList!(int)(4, 5, 6, 7, 1, 2, 3); auto r1 = list[]; auto r2 = list[]; popFrontN(r2, 4); assert(equal(r2, [ 1, 2, 3 ])); bringToFront(r1, r2); assert(equal(list[], [ 1, 2, 3, 4, 5, 6, 7 ])); } /** Elements can be swapped across ranges of different types: */ @safe unittest { import std.algorithm.comparison : equal; import std.container : SList; auto list = SList!(int)(4, 5, 6, 7); auto vec = [ 1, 2, 3 ]; bringToFront(list[], vec); assert(equal(list[], [ 1, 2, 3, 4 ])); assert(equal(vec, [ 5, 6, 7 ])); } @safe unittest { import std.algorithm.comparison : equal; import std.conv : text; import std.random : Random, unpredictableSeed, uniform; debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); // a more elaborate test { auto rnd = Random(unpredictableSeed); int[] a = new int[uniform(100, 200, rnd)]; int[] b = new int[uniform(100, 200, rnd)]; foreach (ref e; a) e = uniform(-100, 100, rnd); foreach (ref e; b) e = uniform(-100, 100, rnd); int[] c = a ~ b; // writeln("a= ", a); // writeln("b= ", b); auto n = bringToFront(c[0 .. a.length], c[a.length .. $]); //writeln("c= ", c); assert(n == b.length); assert(c == b ~ a, text(c, "\n", a, "\n", b)); } // different types, moveFront, no sameHead { static struct R(T) { T[] data; size_t i; @property { R save() { return this; } bool empty() { return i >= data.length; } T front() { return data[i]; } T front(real e) { return data[i] = cast(T) e; } } void popFront() { ++i; } } auto a = R!int([1, 2, 3, 4, 5]); auto b = R!real([6, 7, 8, 9]); auto n = bringToFront(a, b); assert(n == 4); assert(a.data == [6, 7, 8, 9, 1]); assert(b.data == [2, 3, 4, 5]); } // front steps over back { int[] arr, r1, r2; // back is shorter arr = [4, 5, 6, 7, 1, 2, 3]; r1 = arr; r2 = arr[4 .. $]; bringToFront(r1, r2) == 3 || assert(0); assert(equal(arr, [1, 2, 3, 4, 5, 6, 7])); // front is shorter arr = [5, 6, 7, 1, 2, 3, 4]; r1 = arr; r2 = arr[3 .. $]; bringToFront(r1, r2) == 4 || assert(0); assert(equal(arr, [1, 2, 3, 4, 5, 6, 7])); } } // copy /** Copies the content of $(D source) into $(D target) and returns the remaining (unfilled) part of $(D target). Preconditions: $(D target) shall have enough room to accomodate the entirety of $(D source). See_Also: $(WEB sgi.com/tech/stl/_copy.html, STL's _copy) */ TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) if (isInputRange!SourceRange && isOutputRange!(TargetRange, ElementType!SourceRange)) { static TargetRange genericImpl(SourceRange source, TargetRange target) { // Specialize for 2 random access ranges. // Typically 2 random access ranges are faster iterated by common // index then by x.popFront(), y.popFront() pair static if (isRandomAccessRange!SourceRange && hasLength!SourceRange && hasSlicing!TargetRange && isRandomAccessRange!TargetRange && hasLength!TargetRange) { assert(target.length >= source.length, "Cannot copy a source range into a smaller target range."); auto len = source.length; foreach (idx; 0 .. len) target[idx] = source[idx]; return target[len .. target.length]; } else { put(target, source); return target; } } import std.traits : isArray; static if (isArray!SourceRange && isArray!TargetRange && is(Unqual!(typeof(source[0])) == Unqual!(typeof(target[0])))) { immutable overlaps = () @trusted { return source.ptr < target.ptr + target.length && target.ptr < source.ptr + source.length; }(); if (overlaps) { return genericImpl(source, target); } else { // Array specialization. This uses optimized memory copying // routines under the hood and is about 10-20x faster than the // generic implementation. assert(target.length >= source.length, "Cannot copy a source array into a smaller target array."); target[0 .. source.length] = source[]; return target[source.length .. $]; } } else { return genericImpl(source, target); } } /// @safe unittest { int[] a = [ 1, 5 ]; int[] b = [ 9, 8 ]; int[] buf = new int[](a.length + b.length + 10); auto rem = a.copy(buf); // copy a into buf rem = b.copy(rem); // copy b into remainder of buf assert(buf[0 .. a.length + b.length] == [1, 5, 9, 8]); assert(rem.length == 10); // unused slots in buf } /** As long as the target range elements support assignment from source range elements, different types of ranges are accepted: */ @safe unittest { float[] src = [ 1.0f, 5 ]; double[] dest = new double[src.length]; src.copy(dest); } /** To _copy at most $(D n) elements from a range, you may want to use $(XREF range, take): */ @safe unittest { import std.range; int[] src = [ 1, 5, 8, 9, 10 ]; auto dest = new int[](3); src.take(dest.length).copy(dest); assert(dest == [ 1, 5, 8 ]); } /** To _copy just those elements from a range that satisfy a predicate, use $(LREF filter): */ @safe unittest { import std.algorithm.iteration : filter; int[] src = [ 1, 5, 8, 9, 10, 1, 2, 0 ]; auto dest = new int[src.length]; auto rem = src .filter!(a => (a & 1) == 1) .copy(dest); assert(dest[0 .. $ - rem.length] == [ 1, 5, 9, 1 ]); } /** $(XREF range, retro) can be used to achieve behavior similar to $(WEB sgi.com/tech/stl/copy_backward.html, STL's copy_backward'): */ @safe unittest { import std.algorithm, std.range; int[] src = [1, 2, 4]; int[] dest = [0, 0, 0, 0, 0]; src.retro.copy(dest.retro); assert(dest == [0, 0, 1, 2, 4]); } @safe unittest { import std.algorithm.iteration : filter; debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); { int[] a = [ 1, 5 ]; int[] b = [ 9, 8 ]; auto e = copy(filter!("a > 1")(a), b); assert(b[0] == 5 && e.length == 1); } { int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; copy(a[5..10], a[4..9]); assert(a[4..9] == [6, 7, 8, 9, 10]); } { // Test for bug 7898 enum v = { import std.algorithm; int[] arr1 = [10, 20, 30, 40, 50]; int[] arr2 = arr1.dup; copy(arr1, arr2); return 35; }(); } } /** Assigns $(D value) to each element of input range $(D range). Params: range = An $(XREF2 range, isInputRange, input range) that exposes references to its elements and has assignable elements value = Assigned to each element of range See_Also: $(LREF uninitializedFill) $(LREF initializeAll) */ void fill(Range, Value)(Range range, Value value) if (isInputRange!Range && is(typeof(range.front = value))) { alias T = ElementType!Range; static if (is(typeof(range[] = value))) { range[] = value; } else static if (is(typeof(range[] = T(value)))) { range[] = T(value); } else { for ( ; !range.empty; range.popFront() ) { range.front = value; } } } /// @safe unittest { int[] a = [ 1, 2, 3, 4 ]; fill(a, 5); assert(a == [ 5, 5, 5, 5 ]); } @safe unittest { import std.conv : text; import std.internal.test.dummyrange; debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 1, 2, 3 ]; fill(a, 6); assert(a == [ 6, 6, 6 ], text(a)); void fun0() { foreach (i; 0 .. 1000) { foreach (ref e; a) e = 6; } } void fun1() { foreach (i; 0 .. 1000) fill(a, 6); } //void fun2() { foreach (i; 0 .. 1000) fill2(a, 6); } //writeln(benchmark!(fun0, fun1, fun2)(10000)); // fill should accept InputRange alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input); enum filler = uint.max; InputRange range; fill(range, filler); foreach (value; range.arr) assert(value == filler); } @safe unittest { //ER8638_1 IS_NOT self assignable static struct ER8638_1 { void opAssign(int){} } //ER8638_1 IS self assignable static struct ER8638_2 { void opAssign(ER8638_2){} void opAssign(int){} } auto er8638_1 = new ER8638_1[](10); auto er8638_2 = new ER8638_2[](10); er8638_1.fill(5); //generic case er8638_2.fill(5); //opSlice(T.init) case } @safe unittest { { int[] a = [1, 2, 3]; immutable(int) b = 0; static assert(__traits(compiles, a.fill(b))); } { double[] a = [1, 2, 3]; immutable(int) b = 0; static assert(__traits(compiles, a.fill(b))); } } /** Fills $(D range) with a pattern copied from $(D filler). The length of $(D range) does not have to be a multiple of the length of $(D filler). If $(D filler) is empty, an exception is thrown. Params: range = An $(XREF2 range, isInputRange, input range) that exposes references to its elements and has assignable elements. filler = The $(XREF2 range, isForwardRange, forward range) representing the _fill pattern. */ void fill(Range1, Range2)(Range1 range, Range2 filler) if (isInputRange!Range1 && (isForwardRange!Range2 || (isInputRange!Range2 && isInfinite!Range2)) && is(typeof(Range1.init.front = Range2.init.front))) { static if (isInfinite!Range2) { //Range2 is infinite, no need for bounds checking or saving static if (hasSlicing!Range2 && hasLength!Range1 && is(typeof(filler[0 .. range.length]))) { copy(filler[0 .. range.length], range); } else { //manual feed for ( ; !range.empty; range.popFront(), filler.popFront()) { range.front = filler.front; } } } else { import std.exception : enforce; enforce(!filler.empty, "Cannot fill range with an empty filler"); static if (hasLength!Range1 && hasLength!Range2 && is(typeof(range.length > filler.length))) { //Case we have access to length auto len = filler.length; //Start by bulk copies while (range.length > len) { range = copy(filler.save, range); } //and finally fill the partial range. No need to save here. static if (hasSlicing!Range2 && is(typeof(filler[0 .. range.length]))) { //use a quick copy auto len2 = range.length; range = copy(filler[0 .. len2], range); } else { //iterate. No need to check filler, it's length is longer than range's for (; !range.empty; range.popFront(), filler.popFront()) { range.front = filler.front; } } } else { //Most basic case. auto bck = filler.save; for (; !range.empty; range.popFront(), filler.popFront()) { if (filler.empty) filler = bck.save; range.front = filler.front; } } } } /// @safe unittest { int[] a = [ 1, 2, 3, 4, 5 ]; int[] b = [ 8, 9 ]; fill(a, b); assert(a == [ 8, 9, 8, 9, 8 ]); } @safe unittest { import std.exception : assertThrown; import std.internal.test.dummyrange; debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 1, 2, 3, 4, 5 ]; int[] b = [1, 2]; fill(a, b); assert(a == [ 1, 2, 1, 2, 1 ]); // fill should accept InputRange alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input); InputRange range; fill(range,[1,2]); foreach (i,value;range.arr) assert(value == (i%2==0?1:2)); //test with a input being a "reference forward" range fill(a, new ReferenceForwardRange!int([8, 9])); assert(a == [8, 9, 8, 9, 8]); //test with a input being an "infinite input" range fill(a, new ReferenceInfiniteInputRange!int()); assert(a == [0, 1, 2, 3, 4]); //empty filler test assertThrown(fill(a, a[$..$])); } /** Initializes all elements of $(D range) with their $(D .init) value. Assumes that the elements of the range are uninitialized. Params: range = An $(XREF2 range, isInputRange, input range) that exposes references to its elements and has assignable elements See_Also: $(LREF fill) $(LREF uninitializeFill) Example: ---- struct S { ... } S[] s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5]; initializeAll(s); assert(s == [ 0, 0, 0, 0, 0 ]); ---- */ void initializeAll(Range)(Range range) if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range) { import core.stdc.string : memset, memcpy; import std.traits : hasElaborateAssign, isDynamicArray; alias T = ElementType!Range; static if (hasElaborateAssign!T) { 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).init().ptr; if (p) for ( ; !range.empty ; range.popFront() ) memcpy(addressOf(range.front), p, 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 fill(range, T.init); } // ditto void initializeAll(Range)(Range range) if (is(Range == char[]) || is(Range == wchar[])) { alias T = ElementEncodingType!Range; range[] = T.init; } unittest { import std.algorithm.iteration : filter; import std.traits : hasElaborateAssign; import std.typetuple : TypeTuple; debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); //Test strings: //Must work on narrow strings. //Must reject const char[3] a = void; a[].initializeAll(); assert(a[] == [char.init, char.init, char.init]); string s; assert(!__traits(compiles, s.initializeAll())); //Note: Cannot call uninitializedFill on narrow strings enum e {e1, e2} e[3] b1 = void; b1[].initializeAll(); assert(b1[] == [e.e1, e.e1, e.e1]); e[3] b2 = void; b2[].uninitializedFill(e.e2); assert(b2[] == [e.e2, e.e2, e.e2]); static struct S1 { int i; } static struct S2 { int i = 1; } static struct S3 { int i; this(this){} } static struct S4 { int i = 1; this(this){} } static assert (!hasElaborateAssign!S1); static assert (!hasElaborateAssign!S2); static assert ( hasElaborateAssign!S3); static assert ( hasElaborateAssign!S4); assert (!typeid(S1).init().ptr); assert ( typeid(S2).init().ptr); assert (!typeid(S3).init().ptr); assert ( typeid(S4).init().ptr); foreach(S; TypeTuple!(S1, S2, S3, S4)) { //initializeAll { //Array S[3] ss1 = void; ss1[].initializeAll(); assert(ss1[] == [S.init, S.init, S.init]); //Not array S[3] ss2 = void; auto sf = ss2[].filter!"true"(); sf.initializeAll(); assert(ss2[] == [S.init, S.init, S.init]); } //uninitializedFill { //Array S[3] ss1 = void; ss1[].uninitializedFill(S(2)); assert(ss1[] == [S(2), S(2), S(2)]); //Not array S[3] ss2 = void; auto sf = ss2[].filter!"true"(); sf.uninitializedFill(S(2)); assert(ss2[] == [S(2), S(2), S(2)]); } } } // move /** Moves $(D source) into $(D target) via a destructive copy. Params: source = Data to copy. If a destructor or postblit is defined, it is reset to its $(D .init) value after it is moved into target. Note that data with internal pointers that point to itself cannot be moved, and will trigger an assertion failure. target = Where to copy into. The destructor, if any, is invoked before the copy is performed. */ void move(T)(ref T source, ref T target) { import core.stdc.string : memcpy, memset; import std.traits : hasAliasing, hasElaborateAssign, hasElaborateCopyConstructor, hasElaborateDestructor, isAssignable; static if (!is( T == class) && hasAliasing!T) if (!__ctfe) { import std.exception : doesPointTo; assert(!doesPointTo(source, source), "Cannot move object with internal pointer."); } static if (is(T == struct)) { if (&source == &target) return; // Most complicated case. Destroy whatever target had in it // and bitblast source over it static if (hasElaborateDestructor!T) typeid(T).destroy(&target); static if (hasElaborateAssign!T || !isAssignable!T) memcpy(&target, &source, T.sizeof); else target = source; // If the source defines a destructor or a postblit hook, we must obliterate the // object in order to avoid double freeing and undue aliasing static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) { // If T is nested struct, keep original context pointer static if (__traits(isNested, T)) enum sz = T.sizeof - (void*).sizeof; else enum sz = T.sizeof; auto init = typeid(T).init(); if (init.ptr is null) // null ptr means initialize to 0s memset(&source, 0, sz); else memcpy(&source, init.ptr, sz); } } else { // Primitive data (including pointers and arrays) or class - // assignment works great target = source; } } /// unittest { Object obj1 = new Object; Object obj2 = obj1; Object obj3; move(obj2, obj3); assert(obj3 is obj1); } /// unittest { // Structs without destructors are simply copied struct S1 { int a = 1; int b = 2; } S1 s11 = { 10, 11 }; S1 s12; move(s11, s12); assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); // But structs with destructors or postblits are reset to their .init value // after copying to the target. struct S2 { int a = 1; int b = 2; ~this() { } } S2 s21 = { 3, 4 }; S2 s22; move(s21, s22); assert(s21.a == 1 && s21.b == 2 && s22.a == 3 && s22.b == 4); } unittest { import std.traits; debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); import std.exception : assertCTFEable; assertCTFEable!((){ Object obj1 = new Object; Object obj2 = obj1; Object obj3; move(obj2, obj3); assert(obj3 is obj1); static struct S1 { int a = 1, b = 2; } S1 s11 = { 10, 11 }; S1 s12; move(s11, s12); assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); static struct S2 { int a = 1; int * b; } S2 s21 = { 10, null }; s21.b = new int; S2 s22; move(s21, s22); assert(s21 == s22); }); // Issue 5661 test(1) static struct S3 { static struct X { int n = 0; ~this(){n = 0;} } X x; } static assert(hasElaborateDestructor!S3); S3 s31, s32; s31.x.n = 1; move(s31, s32); assert(s31.x.n == 0); assert(s32.x.n == 1); // Issue 5661 test(2) static struct S4 { static struct X { int n = 0; this(this){n = 0;} } X x; } static assert(hasElaborateCopyConstructor!S4); S4 s41, s42; s41.x.n = 1; move(s41, s42); assert(s41.x.n == 0); assert(s42.x.n == 1); // Issue 13990 test class S5; S5 s51; static assert(__traits(compiles, move(s51, s51)), "issue 13990, cannot move opaque class reference"); } /// Ditto T move(T)(ref T source) { import core.stdc.string : memcpy, memset; import std.traits : hasAliasing, hasElaborateAssign, hasElaborateCopyConstructor, hasElaborateDestructor, isAssignable; static if (!is(T == class) && hasAliasing!T) if (!__ctfe) { import std.exception : doesPointTo; assert(!doesPointTo(source, source), "Cannot move object with internal pointer."); } T result = void; static if (is(T == struct)) { // Can avoid destructing result. static if (hasElaborateAssign!T || !isAssignable!T) memcpy(&result, &source, T.sizeof); else result = source; // If the source defines a destructor or a postblit hook, we must obliterate the // object in order to avoid double freeing and undue aliasing static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) { // If T is nested struct, keep original context pointer static if (__traits(isNested, T)) enum sz = T.sizeof - (void*).sizeof; else enum sz = T.sizeof; auto init = typeid(T).init(); if (init.ptr is null) // null ptr means initialize to 0s memset(&source, 0, sz); else memcpy(&source, init.ptr, sz); } } else { // Primitive data (including pointers and arrays) or class - // assignment works great result = source; } return result; } unittest { import std.traits; debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); import std.exception : assertCTFEable; assertCTFEable!((){ Object obj1 = new Object; Object obj2 = obj1; Object obj3 = move(obj2); assert(obj3 is obj1); static struct S1 { int a = 1, b = 2; } S1 s11 = { 10, 11 }; S1 s12 = move(s11); assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); static struct S2 { int a = 1; int * b; } S2 s21 = { 10, null }; s21.b = new int; S2 s22 = move(s21); assert(s21 == s22); }); // Issue 5661 test(1) static struct S3 { static struct X { int n = 0; ~this(){n = 0;} } X x; } static assert(hasElaborateDestructor!S3); S3 s31; s31.x.n = 1; S3 s32 = move(s31); assert(s31.x.n == 0); assert(s32.x.n == 1); // Issue 5661 test(2) static struct S4 { static struct X { int n = 0; this(this){n = 0;} } X x; } static assert(hasElaborateCopyConstructor!S4); S4 s41; s41.x.n = 1; S4 s42 = move(s41); assert(s41.x.n == 0); assert(s42.x.n == 1); // Issue 13990 test class S5; S5 s51; static assert(__traits(compiles, s51 = move(s51)), "issue 13990, cannot move opaque class reference"); } unittest//Issue 6217 { import std.algorithm.iteration : map; auto x = map!"a"([1,2,3]); x = move(x); } unittest// Issue 8055 { static struct S { int x; ~this() { assert(x == 0); } } S foo(S s) { return move(s); } S a; a.x = 0; auto b = foo(a); assert(b.x == 0); } unittest// Issue 8057 { int n = 10; struct S { int x; ~this() { // Access to enclosing scope assert(n == 10); } } S foo(S s) { // Move nested struct return move(s); } S a; a.x = 1; auto b = foo(a); assert(b.x == 1); // Regression 8171 static struct Array(T) { // nested struct has no member struct Payload { ~this() {} } } Array!int.Payload x = void; static assert(__traits(compiles, move(x) )); static assert(__traits(compiles, move(x, x) )); } // moveAll /** For each element $(D a) in $(D src) and each element $(D b) in $(D tgt) in lockstep in increasing order, calls $(D move(a, b)). Preconditions: $(D walkLength(src) <= walkLength(tgt)). An exception will be thrown if this condition does not hold, i.e., there is not enough room in $(D tgt) to accommodate all of $(D src). Params: src = An $(XREF2 range, isInputRange, input range) with movable elements. tgt = An $(XREF2 range, isInputRange, input range) with elements that elements from $(D src) can be moved into. Returns: The leftover portion of $(D tgt) after all elements from $(D src) have been moved. */ Range2 moveAll(Range1, Range2)(Range1 src, Range2 tgt) if (isInputRange!Range1 && isInputRange!Range2 && is(typeof(move(src.front, tgt.front)))) { import std.exception : enforce; static if (isRandomAccessRange!Range1 && hasLength!Range1 && hasLength!Range2 && hasSlicing!Range2 && isRandomAccessRange!Range2) { auto toMove = src.length; enforce(toMove <= tgt.length); // shouldn't this be an assert? foreach (idx; 0 .. toMove) move(src[idx], tgt[idx]); return tgt[toMove .. tgt.length]; } else { for (; !src.empty; src.popFront(), tgt.popFront()) { enforce(!tgt.empty); //ditto? move(src.front, tgt.front); } return tgt; } } /// unittest { int[] a = [ 1, 2, 3 ]; int[] b = new int[5]; assert(moveAll(a, b) is b[3 .. $]); assert(a == b[0 .. 3]); assert(a == [ 1, 2, 3 ]); } // moveSome /** For each element $(D a) in $(D src) and each element $(D b) in $(D tgt) in lockstep in increasing order, calls $(D move(a, b)). Stops when either $(D src) or $(D tgt) have been exhausted. Params: src = An $(XREF2 range, isInputRange, input range) with movable elements. tgt = An $(XREF2 range, isInputRange, input range) with elements that elements from $(D src) can be moved into. Returns: The leftover portions of the two ranges after one or the other of the ranges have been exhausted. */ Tuple!(Range1, Range2) moveSome(Range1, Range2)(Range1 src, Range2 tgt) if (isInputRange!Range1 && isInputRange!Range2 && is(typeof(move(src.front, tgt.front)))) { import std.exception : enforce; for (; !src.empty && !tgt.empty; src.popFront(), tgt.popFront()) { enforce(!tgt.empty); move(src.front, tgt.front); } return tuple(src, tgt); } /// unittest { int[] a = [ 1, 2, 3, 4, 5 ]; int[] b = new int[3]; assert(moveSome(a, b)[0] is a[3 .. $]); assert(a[0 .. 3] == b); assert(a == [ 1, 2, 3, 4, 5 ]); } // SwapStrategy /** Defines the swapping strategy for algorithms that need to swap elements in a range (such as partition and sort). The strategy concerns the swapping of elements that are not the core concern of the algorithm. For example, consider an algorithm that sorts $(D [ "abc", "b", "aBc" ]) according to $(D toUpper(a) < toUpper(b)). That algorithm might choose to swap the two equivalent strings $(D "abc") and $(D "aBc"). That does not affect the sorting since both $(D [ "abc", "aBc", "b" ]) and $(D [ "aBc", "abc", "b" ]) are valid outcomes. Some situations require that the algorithm must NOT ever change the relative ordering of equivalent elements (in the example above, only $(D [ "abc", "aBc", "b" ]) would be the correct result). Such algorithms are called $(B stable). If the ordering algorithm may swap equivalent elements discretionarily, the ordering is called $(B unstable). Yet another class of algorithms may choose an intermediate tradeoff by being stable only on a well-defined subrange of the range. There is no established terminology for such behavior; this library calls it $(B semistable). Generally, the $(D stable) ordering strategy may be more costly in time and/or space than the other two because it imposes additional constraints. Similarly, $(D semistable) may be costlier than $(D unstable). As (semi-)stability is not needed very often, the ordering algorithms in this module parameterized by $(D SwapStrategy) all choose $(D SwapStrategy.unstable) as the default. */ enum SwapStrategy { /** Allows freely swapping of elements as long as the output satisfies the algorithm's requirements. */ unstable, /** In algorithms partitioning ranges in two, preserve relative ordering of elements only to the left of the partition point. */ semistable, /** Preserve the relative ordering of elements to the largest extent allowed by the algorithm's requirements. */ stable, } /** Eliminates elements at given offsets from $(D range) and returns the shortened range. In the simplest call, one element is removed. ---- int[] a = [ 3, 5, 7, 8 ]; assert(remove(a, 1) == [ 3, 7, 8 ]); assert(a == [ 3, 7, 8, 8 ]); ---- In the case above the element at offset $(D 1) is removed and $(D remove) returns the range smaller by one element. The original array has remained of the same length because all functions in $(D std.algorithm) only change $(I content), not $(I topology). The value $(D 8) is repeated because $(XREF algorithm, move) was invoked to move elements around and on integers $(D move) simply copies the source to the destination. To replace $(D a) with the effect of the removal, simply assign $(D a = remove(a, 1)). The slice will be rebound to the shorter array and the operation completes with maximal efficiency. Multiple indices can be passed into $(D remove). In that case, elements at the respective indices are all removed. The indices must be passed in increasing order, otherwise an exception occurs. ---- int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove(a, 1, 3, 5) == [ 0, 2, 4, 6, 7, 8, 9, 10 ]); ---- (Note how all indices refer to slots in the $(I original) array, not in the array as it is being progressively shortened.) Finally, any combination of integral offsets and tuples composed of two integral offsets can be passed in. ---- int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove(a, 1, tuple(3, 5), 9) == [ 0, 2, 6, 7, 8, 10 ]); ---- In this case, the slots at positions 1, 3, 4, and 9 are removed from the array. The tuple passes in a range closed to the left and open to the right (consistent with built-in slices), e.g. $(D tuple(3, 5)) means indices $(D 3) and $(D 4) but not $(D 5). If the need is to remove some elements in the range but the order of the remaining elements does not have to be preserved, you may want to pass $(D SwapStrategy.unstable) to $(D remove). ---- int[] a = [ 0, 1, 2, 3 ]; assert(remove!(SwapStrategy.unstable)(a, 1) == [ 0, 3, 2 ]); ---- In the case above, the element at slot $(D 1) is removed, but replaced with the last element of the range. Taking advantage of the relaxation of the stability requirement, $(D remove) moved elements from the end of the array over the slots to be removed. This way there is less data movement to be done which improves the execution time of the function. The function $(D remove) works on any forward range. The moving strategy is (listed from fastest to slowest): $(UL $(LI If $(D s == SwapStrategy.unstable && isRandomAccessRange!Range && hasLength!Range && hasLvalueElements!Range), then elements are moved from the end of the range into the slots to be filled. In this case, the absolute minimum of moves is performed.) $(LI Otherwise, if $(D s == SwapStrategy.unstable && isBidirectionalRange!Range && hasLength!Range && hasLvalueElements!Range), then elements are still moved from the end of the range, but time is spent on advancing between slots by repeated calls to $(D range.popFront).) $(LI Otherwise, elements are moved incrementally towards the front of $(D range); a given element is never moved several times, but more elements are moved than in the previous cases.)) */ Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if (s != SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1) { import std.algorithm.comparison : min; Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; foreach (i, v; offset) { 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"); } } size_t left = 0, right = offset.length - 1; auto tgt = range.save; size_t steps = 0; while (left <= right) { // 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) { --right; continue; } else break; } // Advance to next blackout on the left assert(blackouts[left].pos >= steps); tgt.popFrontExactly(blackouts[left].pos - steps); steps = blackouts[left].pos; auto toMove = min( blackouts[left].len, range.length - (blackouts[right].pos + blackouts[right].len)); foreach (i; 0 .. toMove) { move(range.back, tgt.front); range.popBack(); tgt.popFront(); } steps += toMove; if (toMove == blackouts[left].len) { // Filled the entire left hole ++left; continue; } } 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) { 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"); for (; pos < from; ++pos, src.popFront(), tgt.popFront()) { move(src.front, tgt.front); } } 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; } @safe unittest { import std.exception : assertThrown; import std.range; // http://d.puremagic.com/issues/show_bug.cgi?id=10173 int[] test = iota(0, 10).array(); assertThrown(remove!(SwapStrategy.stable)(test, tuple(2, 4), tuple(1, 3))); assertThrown(remove!(SwapStrategy.unstable)(test, tuple(2, 4), tuple(1, 3))); assertThrown(remove!(SwapStrategy.stable)(test, 2, 4, 1, 3)); assertThrown(remove!(SwapStrategy.unstable)(test, 2, 4, 1, 3)); } @safe unittest { import std.range; debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; //writeln(remove!(SwapStrategy.stable)(a, 1)); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.unstable)(a, 0, 10) == [ 9, 1, 2, 3, 4, 5, 6, 7, 8 ]); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.unstable)(a, 0, tuple(9, 11)) == [ 8, 1, 2, 3, 4, 5, 6, 7 ]); // http://d.puremagic.com/issues/show_bug.cgi?id=5224 a = [ 1, 2, 3, 4 ]; assert(remove!(SwapStrategy.unstable)(a, 2) == [ 1, 2, 4 ]); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; //writeln(remove!(SwapStrategy.stable)(a, 1, 5)); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.stable)(a, 1, 5) == [ 0, 2, 3, 4, 6, 7, 8, 9, 10 ]); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; //writeln(remove!(SwapStrategy.stable)(a, 1, 3, 5)); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.stable)(a, 1, 3, 5) == [ 0, 2, 4, 6, 7, 8, 9, 10]); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; //writeln(remove!(SwapStrategy.stable)(a, 1, tuple(3, 5))); a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 5)) == [ 0, 2, 5, 6, 7, 8, 9, 10]); a = iota(0, 10).array(); assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4), tuple(6, 7)) == [0, 9, 8, 7, 4, 5]); } @safe unittest { // Issue 11576 auto arr = [1,2,3]; arr = arr.remove!(SwapStrategy.unstable)(2); assert(arr == [1,2]); } @safe unittest { import std.range; // Bug# 12889 int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]]; auto orig = arr.dup; foreach (i; iota(arr.length)) { assert(orig == arr.remove!(SwapStrategy.unstable)(tuple(i,i))); assert(orig == arr.remove!(SwapStrategy.stable)(tuple(i,i))); } } /** Reduces the length of the bidirectional range $(D range) by removing elements that satisfy $(D pred). If $(D s = SwapStrategy.unstable), elements are moved from the right end of the range over the elements to eliminate. If $(D s = SwapStrategy.stable) (the default), elements are moved progressively to front such that their relative order is preserved. Returns the filtered range. */ Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range) (Range range) if (isBidirectionalRange!Range && hasLvalueElements!Range) { import std.functional : unaryFun; auto result = range; static if (s != SwapStrategy.stable) { for (;!range.empty;) { if (!unaryFun!pred(range.front)) { range.popFront(); continue; } move(range.back, range.front); range.popBack(); result.popBack(); } } else { auto tgt = range; for (; !range.empty; range.popFront()) { if (unaryFun!(pred)(range.front)) { // yank this guy result.popBack(); continue; } // keep this guy move(range.front, tgt.front); tgt.popFront(); } } return result; } /// @safe unittest { static immutable base = [1, 2, 3, 2, 4, 2, 5, 2]; int[] arr = base[].dup; // using a string-based predicate assert(remove!("a == 2")(arr) == [ 1, 3, 4, 5 ]); // The original array contents have been modified, // so we need to reset it to its original state. // The length is unmodified however. arr[] = base[]; // using a lambda predicate assert(remove!(a => a == 2)(arr) == [ 1, 3, 4, 5 ]); } @safe unittest { debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] a = [ 1, 2, 3, 2, 3, 4, 5, 2, 5, 6 ]; assert(remove!("a == 2", SwapStrategy.unstable)(a) == [ 1, 6, 3, 5, 3, 4, 5 ]); a = [ 1, 2, 3, 2, 3, 4, 5, 2, 5, 6 ]; //writeln(remove!("a != 2", SwapStrategy.stable)(a)); assert(remove!("a == 2", SwapStrategy.stable)(a) == [ 1, 3, 3, 4, 5, 5, 6 ]); } // reverse /** Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D swap). See_Also: $(WEB sgi.com/tech/stl/_reverse.html, STL's _reverse) */ void reverse(Range)(Range r) if (isBidirectionalRange!Range && !isRandomAccessRange!Range && hasSwappableElements!Range) { while (!r.empty) { swap(r.front, r.back); r.popFront(); if (r.empty) break; r.popBack(); } } /// @safe unittest { int[] arr = [ 1, 2, 3 ]; reverse(arr); assert(arr == [ 3, 2, 1 ]); } ///ditto void reverse(Range)(Range r) if (isRandomAccessRange!Range && hasLength!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++) { swapAt(r, i, last-i); } } @safe unittest { debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); int[] range = null; reverse(range); range = [ 1 ]; reverse(range); assert(range == [1]); range = [1, 2]; reverse(range); assert(range == [2, 1]); range = [1, 2, 3]; reverse(range); assert(range == [3, 2, 1]); } /** Reverses $(D r) in-place, where $(D r) is a narrow string (having elements of type $(D char) or $(D wchar)). UTF sequences consisting of multiple code units are preserved properly. */ void reverse(Char)(Char[] s) if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable)) { import std.string : representation; import std.utf : stride; auto r = representation(s); for (size_t i = 0; i < s.length; ) { immutable step = std.utf.stride(s, i); if (step > 1) { .reverse(r[i .. i + step]); i += step; } else { ++i; } } 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) { auto c = a.dup; reverse(c); assert(c == b, c ~ " != " ~ b); } test("a", "a"); test(" ", " "); test("\u2029", "\u2029"); test("\u0100", "\u0100"); test("\u0430", "\u0430"); test("\U00010143", "\U00010143"); test("abcdefcdef", "fedcfedcba"); test("hello\U00010143\u0100\U00010143", "\U00010143\u0100\U00010143olleh"); } //private void swapAt(R)(R r, size_t i1, size_t i2) { static if (is(typeof(&r[i1]))) { swap(r[i1], r[i2]); } else { if (i1 == i2) return; auto t1 = moveAt(r, i1); auto t2 = moveAt(r, i2); r[i2] = t1; r[i1] = t2; } } /** The strip group of functions allow stripping of either leading, trailing, or both leading and trailing elements. The $(D stripLeft) function will strip the $(D front) of the range, the $(D stripRight) function will strip the $(D back) of the range, while the $(D strip) function will strip both the $(D front) and $(D back) of the range. Note that the $(D strip) and $(D stripRight) functions require the range to be a $(LREF BidirectionalRange) range. All of these functions come in two varieties: one takes a target element, where the range will be stripped as long as this element can be found. The other takes a lambda predicate, where the range will be stripped as long as the predicate returns true. */ Range strip(Range, E)(Range range, E element) if (isBidirectionalRange!Range && is(typeof(range.front == element) : bool)) { return range.stripLeft(element).stripRight(element); } /// ditto Range strip(alias pred, Range)(Range range) if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) { return range.stripLeft!pred().stripRight!pred(); } /// ditto Range stripLeft(Range, E)(Range range, E element) if (isInputRange!Range && is(typeof(range.front == element) : bool)) { import std.algorithm.searching : find; return find!((auto ref a) => a != element)(range); } /// ditto Range stripLeft(alias pred, Range)(Range range) if (isInputRange!Range && is(typeof(pred(range.front)) : bool)) { import std.algorithm.searching : find; import std.functional : not; return find!(not!pred)(range); } /// ditto Range stripRight(Range, E)(Range range, E element) if (isBidirectionalRange!Range && is(typeof(range.back == element) : bool)) { for (; !range.empty; range.popBack()) { if (range.back != element) break; } return range; } /// ditto Range stripRight(alias pred, Range)(Range range) if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) { for (; !range.empty; range.popBack()) { if (!pred(range.back)) break; } return range; } /// Strip leading and trailing elements equal to the target element. @safe pure unittest { assert(" foobar ".strip(' ') == "foobar"); assert("00223.444500".strip('0') == "223.4445"); assert("ëëêéüŗōpéêëë".strip('ë') == "êéüŗōpéê"); assert([1, 1, 0, 1, 1].strip(1) == [0]); assert([0.0, 0.01, 0.01, 0.0].strip(0).length == 2); } /// Strip leading and trailing elements while the predicate returns true. @safe pure unittest { assert(" foobar ".strip!(a => a == ' ')() == "foobar"); assert("00223.444500".strip!(a => a == '0')() == "223.4445"); assert("ëëêéüŗōpéêëë".strip!(a => a == 'ë')() == "êéüŗōpéê"); assert([1, 1, 0, 1, 1].strip!(a => a == 1)() == [0]); assert([0.0, 0.01, 0.5, 0.6, 0.01, 0.0].strip!(a => a < 0.4)().length == 2); } /// Strip leading elements equal to the target element. @safe pure unittest { assert(" foobar ".stripLeft(' ') == "foobar "); assert("00223.444500".stripLeft('0') == "223.444500"); assert("ůůűniçodêéé".stripLeft('ů') == "űniçodêéé"); assert([1, 1, 0, 1, 1].stripLeft(1) == [0, 1, 1]); assert([0.0, 0.01, 0.01, 0.0].stripLeft(0).length == 3); } /// Strip leading elements while the predicate returns true. @safe pure unittest { assert(" foobar ".stripLeft!(a => a == ' ')() == "foobar "); assert("00223.444500".stripLeft!(a => a == '0')() == "223.444500"); assert("ůůűniçodêéé".stripLeft!(a => a == 'ů')() == "űniçodêéé"); assert([1, 1, 0, 1, 1].stripLeft!(a => a == 1)() == [0, 1, 1]); assert([0.0, 0.01, 0.10, 0.5, 0.6].stripLeft!(a => a < 0.4)().length == 2); } /// Strip trailing elements equal to the target element. @safe pure unittest { assert(" foobar ".stripRight(' ') == " foobar"); assert("00223.444500".stripRight('0') == "00223.4445"); assert("ùniçodêéé".stripRight('é') == "ùniçodê"); assert([1, 1, 0, 1, 1].stripRight(1) == [1, 1, 0]); assert([0.0, 0.01, 0.01, 0.0].stripRight(0).length == 3); } /// Strip trailing elements while the predicate returns true. @safe pure unittest { assert(" foobar ".stripRight!(a => a == ' ')() == " foobar"); assert("00223.444500".stripRight!(a => a == '0')() == "00223.4445"); assert("ùniçodêéé".stripRight!(a => a == 'é')() == "ùniçodê"); assert([1, 1, 0, 1, 1].stripRight!(a => a == 1)() == [1, 1, 0]); assert([0.0, 0.01, 0.10, 0.5, 0.6].stripRight!(a => a > 0.4)().length == 3); } // swap /** Swaps $(D lhs) and $(D rhs). The instances $(D lhs) and $(D rhs) are moved in memory, without ever calling $(D opAssign), nor any other function. $(D T) need not be assignable at all to be swapped. If $(D lhs) and $(D rhs) reference the same instance, then nothing is done. $(D lhs) and $(D rhs) must be mutable. If $(D T) is a struct or union, then its fields must also all be (recursively) mutable. Params: lhs = Data to be swapped with $(D rhs). rhs = Data to be swapped with $(D lhs). */ void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) { import std.traits : hasAliasing, hasElaborateAssign, isAssignable, isStaticArray; static if (hasAliasing!T) if (!__ctfe) { import std.exception : doesPointTo; assert(!doesPointTo(lhs, lhs), "Swap: lhs internal pointer."); assert(!doesPointTo(rhs, rhs), "Swap: rhs internal pointer."); assert(!doesPointTo(lhs, rhs), "Swap: lhs points to rhs."); assert(!doesPointTo(rhs, lhs), "Swap: rhs points to lhs."); } static if (hasElaborateAssign!T || !isAssignable!T) { if (&lhs != &rhs) { // For structs with non-trivial assignment, move memory directly ubyte[T.sizeof] t = void; auto a = (cast(ubyte*) &lhs)[0 .. T.sizeof]; auto b = (cast(ubyte*) &rhs)[0 .. T.sizeof]; t[] = a[]; a[] = b[]; b[] = t[]; } } else { //Avoid assigning overlapping arrays. Dynamic arrays are fine, because //it's their ptr and length properties which get assigned rather //than their elements when assigning them, but static arrays are value //types and therefore all of their elements get copied as part of //assigning them, which would be assigning overlapping arrays if lhs //and rhs were the same array. static if (isStaticArray!T) { if (lhs.ptr == rhs.ptr) return; } // For non-struct types, suffice to do the classic swap auto tmp = lhs; lhs = rhs; rhs = tmp; } } /// @safe unittest { // Swapping POD (plain old data) types: int a = 42, b = 34; swap(a, b); assert(a == 34 && b == 42); // Swapping structs with indirection: static struct S { int x; char c; int[] y; } S s1 = { 0, 'z', [ 1, 2 ] }; S s2 = { 42, 'a', [ 4, 6 ] }; swap(s1, s2); assert(s1.x == 42); assert(s1.c == 'a'); assert(s1.y == [ 4, 6 ]); assert(s2.x == 0); assert(s2.c == 'z'); assert(s2.y == [ 1, 2 ]); // Immutables cannot be swapped: immutable int imm1, imm2; static assert(!__traits(compiles, swap(imm1, imm2))); } /// @safe unittest { // Non-copyable types can still be swapped. static struct NoCopy { this(this) { assert(0); } int n; string s; } NoCopy nc1, nc2; nc1.n = 127; nc1.s = "abc"; nc2.n = 513; nc2.s = "uvwxyz"; swap(nc1, nc2); assert(nc1.n == 513 && nc1.s == "uvwxyz"); assert(nc2.n == 127 && nc2.s == "abc"); swap(nc1, nc1); swap(nc2, nc2); assert(nc1.n == 513 && nc1.s == "uvwxyz"); assert(nc2.n == 127 && nc2.s == "abc"); // Types containing non-copyable fields can also be swapped. static struct NoCopyHolder { NoCopy noCopy; } NoCopyHolder h1, h2; h1.noCopy.n = 31; h1.noCopy.s = "abc"; h2.noCopy.n = 65; h2.noCopy.s = null; swap(h1, h2); assert(h1.noCopy.n == 65 && h1.noCopy.s == null); assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc"); swap(h1, h1); swap(h2, h2); assert(h1.noCopy.n == 65 && h1.noCopy.s == null); assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc"); // Const types cannot be swapped. const NoCopy const1, const2; static assert(!__traits(compiles, swap(const1, const2))); } @safe unittest { //Bug# 4789 int[1] s = [1]; swap(s, s); } @safe unittest { static struct NoAssign { int i; void opAssign(NoAssign) @disable; } auto s1 = NoAssign(1); auto s2 = NoAssign(2); swap(s1, s2); assert(s1.i == 2); assert(s2.i == 1); } @safe unittest { struct S { const int i; } S s; static assert(!__traits(compiles, swap(s, s))); } @safe unittest { //11853 import std.traits : isAssignable; alias T = Tuple!(int, double); static assert(isAssignable!T); } @safe unittest { // 12024 import std.datetime; SysTime a, b; } unittest // 9975 { import std.exception : doesPointTo, mayPointTo; static struct S2 { union { size_t sz; string s; } } S2 a , b; a.sz = -1; assert(!doesPointTo(a, b)); assert( mayPointTo(a, b)); swap(a, b); //Note: we can catch an error here, because there is no RAII in this test import std.exception : assertThrown; void* p, pp; p = &p; assertThrown!Error(move(p)); assertThrown!Error(move(p, pp)); assertThrown!Error(swap(p, pp)); } unittest { static struct A { int* x; this(this) { x = new int; } } A a1, a2; swap(a1, a2); static struct B { int* x; void opAssign(B) { x = new int; } } B b1, b2; swap(b1, b2); } // Not yet documented void swap(T)(ref T lhs, ref T rhs) if (is(typeof(lhs.proxySwap(rhs)))) { lhs.proxySwap(rhs); } void swapFront(R1, R2)(R1 r1, R2 r2) if (isInputRange!R1 && isInputRange!R2) { static if (is(typeof(swap(r1.front, r2.front)))) { swap(r1.front, r2.front); } else { auto t1 = moveFront(r1), t2 = moveFront(r2); r1.front = move(t2); r2.front = move(t1); } } // swapRanges /** Swaps all elements of $(D r1) with successive elements in $(D r2). Returns a tuple containing the remainder portions of $(D r1) and $(D r2) that were not swapped (one of them will be empty). The ranges may be of different types but must have the same element type and support swapping. */ Tuple!(Range1, Range2) swapRanges(Range1, Range2)(Range1 r1, Range2 r2) if (isInputRange!(Range1) && isInputRange!(Range2) && hasSwappableElements!(Range1) && hasSwappableElements!(Range2) && is(ElementType!(Range1) == ElementType!(Range2))) { for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront()) { swap(r1.front, r2.front); } return tuple(r1, r2); } /// @safe unittest { int[] a = [ 100, 101, 102, 103 ]; int[] b = [ 0, 1, 2, 3 ]; auto c = swapRanges(a[1 .. 3], b[2 .. 4]); assert(c[0].empty && c[1].empty); assert(a == [ 100, 2, 3, 103 ]); assert(b == [ 0, 1, 101, 102 ]); } /** Initializes each element of $(D range) with $(D value). Assumes that the elements of the range are uninitialized. This is of interest for structs that define copy constructors (for all other types, $(LREF fill) and uninitializedFill are equivalent). Params: range = An $(XREF2 range, isInputRange, input range) that exposes references to its elements and has assignable elements value = Assigned to each element of range See_Also: $(LREF fill) $(LREF initializeAll) Example: ---- struct S { ... } S[] s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5]; uninitializedFill(s, 42); assert(s == [ 42, 42, 42, 42, 42 ]); ---- */ void uninitializedFill(Range, Value)(Range range, Value value) if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = value))) { import std.traits : hasElaborateAssign; alias T = ElementType!Range; static if (hasElaborateAssign!T) { import std.conv : emplaceRef; // Must construct stuff by the book for (; !range.empty; range.popFront()) emplaceRef!T(range.front, value); } else // Doesn't matter whether fill is initialized or not return fill(range, value); }