aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/algorithm/mutation.d
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
commitb4c522fabd0df7be08882d2207df8b2765026110 (patch)
treeb5ffc312b0a441c1ba24323152aec463fdbe5e9f /libphobos/src/std/algorithm/mutation.d
parent01ce9e31a02c8039d88e90f983735104417bf034 (diff)
downloadgcc-b4c522fabd0df7be08882d2207df8b2765026110.zip
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.gz
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.bz2
Add D front-end, libphobos library, and D2 testsuite.
ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
Diffstat (limited to 'libphobos/src/std/algorithm/mutation.d')
-rw-r--r--libphobos/src/std/algorithm/mutation.d2909
1 files changed, 2909 insertions, 0 deletions
diff --git a/libphobos/src/std/algorithm/mutation.d b/libphobos/src/std/algorithm/mutation.d
new file mode 100644
index 0000000..2b708ad
--- /dev/null
+++ b/libphobos/src/std/algorithm/mutation.d
@@ -0,0 +1,2909 @@
+// Written in the D programming language.
+/**
+This is a submodule of $(MREF std, algorithm).
+It contains generic _mutation algorithms.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(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 when necessary.)
+$(T2 moveEmplace,
+ Similar to $(D move) but assumes `target` is uninitialized.)
+$(T2 moveAll,
+ Moves all elements from one range to another.)
+$(T2 moveEmplaceAll,
+ Similar to $(D moveAll) but assumes all elements in `target` are uninitialized.)
+$(T2 moveSome,
+ Moves as many elements as possible from one range to another.)
+$(T2 moveEmplaceSome,
+ Similar to $(D moveSome) but assumes all elements in `target` are uninitialized.)
+$(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 swapAt,
+ Swaps two values by indices.)
+$(T2 swapRanges,
+ Swaps all elements of two ranges.)
+$(T2 uninitializedFill,
+ Fills a range (assumed uninitialized) with a value.)
+)
+
+Copyright: Andrei Alexandrescu 2008-.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP 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 : isArray, isBlitAssignable, isNarrowString, Unqual, isSomeChar;
+// FIXME
+import std.typecons; // : tuple, Tuple;
+
+// 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.
+
+The $(D bringToFront) function treats strings at the code unit
+level and it is not concerned with Unicode character integrity.
+$(D bringToFront) is designed as a function for moving elements
+in ranges, not as a string function.
+
+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).
+
+Params:
+ front = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ back = a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+
+Returns:
+ The number of elements brought to the front, i.e., the length of $(D back).
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/_rotate.html, STL's rotate)
+*/
+size_t bringToFront(InputRange, ForwardRange)(InputRange front, ForwardRange back)
+if (isInputRange!InputRange && isForwardRange!ForwardRange)
+{
+ import std.string : representation;
+
+ static if (isNarrowString!InputRange)
+ {
+ auto frontW = representation(front);
+ }
+ else
+ {
+ alias frontW = front;
+ }
+ static if (isNarrowString!ForwardRange)
+ {
+ auto backW = representation(back);
+ }
+ else
+ {
+ alias backW = back;
+ }
+
+ return bringToFrontImpl(frontW, backW);
+}
+
+private size_t bringToFrontImpl(InputRange, ForwardRange)(InputRange front, ForwardRange back)
+if (isInputRange!InputRange && isForwardRange!ForwardRange)
+{
+ import std.array : sameHead;
+ 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(InputRange == Take!ForwardRange))
+ {
+ 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;
+ import std.range.primitives : popFrontN;
+
+ 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 ]));
+}
+
+/**
+Unicode integrity is not preserved:
+*/
+@safe unittest
+{
+ import std.string : representation;
+ auto ar = representation("a".dup);
+ auto br = representation("ç".dup);
+
+ bringToFront(ar, br);
+
+ auto a = cast(char[]) ar;
+ auto b = cast(char[]) br;
+
+ // Illegal UTF-8
+ assert(a == "\303");
+ // Illegal UTF-8
+ assert(b == "\247a");
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : text;
+ import std.random : Random, unpredictableSeed, uniform;
+
+ // 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]));
+ }
+
+ // Bugzilla 16959
+ 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']);
+}
+
+// Tests if types are arrays and support slice assign.
+private enum bool areCopyCompatibleArrays(T1, T2) =
+ isArray!T1 && isArray!T2 && is(typeof(T2.init[] = T1.init[]));
+
+// 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 accommodate
+the entirety of $(D source).
+
+Params:
+ source = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ target = an output range
+
+Returns:
+ The unfilled part of target
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/_copy.html, STL's _copy)
+ */
+TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
+if (areCopyCompatibleArrays!(SourceRange, TargetRange))
+{
+ const tlen = target.length;
+ const slen = source.length;
+ assert(tlen >= slen,
+ "Cannot copy a source range into a smaller target range.");
+
+ immutable overlaps = __ctfe || () @trusted {
+ return source.ptr < target.ptr + tlen &&
+ target.ptr < source.ptr + slen; }();
+
+ if (overlaps)
+ {
+ foreach (idx; 0 .. slen)
+ target[idx] = source[idx];
+ return target[slen .. tlen];
+ }
+ else
+ {
+ // Array specialization. This uses optimized memory copying
+ // routines under the hood and is about 10-20x faster than the
+ // generic implementation.
+ target[0 .. slen] = source[];
+ return target[slen .. $];
+ }
+}
+
+/// ditto
+TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
+if (!areCopyCompatibleArrays!(SourceRange, TargetRange) &&
+ isInputRange!SourceRange &&
+ isOutputRange!(TargetRange, ElementType!SourceRange))
+{
+ // Specialize for 2 random access ranges.
+ // Typically 2 random access ranges are faster iterated by common
+ // index than by x.popFront(), y.popFront() pair
+ static if (isRandomAccessRange!SourceRange &&
+ hasLength!SourceRange &&
+ hasSlicing!TargetRange &&
+ isRandomAccessRange!TargetRange &&
+ hasLength!TargetRange)
+ {
+ auto len = source.length;
+ foreach (idx; 0 .. len)
+ target[idx] = source[idx];
+ return target[len .. target.length];
+ }
+ else
+ {
+ put(target, source);
+ return 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
+$(REF take, std,range):
+*/
+@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 ]);
+}
+
+/**
+$(REF retro, std,range) can be used to achieve behavior similar to
+$(HTTP 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]);
+}
+
+// Test CTFE copy.
+@safe unittest
+{
+ enum c = copy([1,2,3], [4,5,6,7]);
+ assert(c == [7]);
+}
+
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+
+ {
+ 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;
+ }();
+ assert(v == 35);
+ }
+}
+
+@safe unittest
+{
+ // Issue 13650
+ import std.meta : AliasSeq;
+ foreach (Char; AliasSeq!(char, wchar, dchar))
+ {
+ Char[3] a1 = "123";
+ Char[6] a2 = "456789";
+ assert(copy(a1[], a2[]) is a2[3..$]);
+ assert(a1[] == "123");
+ assert(a2[] == "123789");
+ }
+}
+
+/**
+Assigns $(D value) to each element of input _range $(D range).
+
+Params:
+ range = An
+ $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ 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)(auto ref Range range, auto ref Value value)
+if ((isInputRange!Range && is(typeof(range.front = value)) ||
+ isSomeChar!Value && is(typeof(range[] = 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 ]);
+}
+
+// issue 16342, test fallback on mutable narrow strings
+@safe unittest
+{
+ char[] chars = ['a', 'b'];
+ fill(chars, 'c');
+ assert(chars == "cc");
+
+ char[2] chars2 = ['a', 'b'];
+ fill(chars2, 'c');
+ assert(chars2 == "cc");
+
+ wchar[] wchars = ['a', 'b'];
+ fill(wchars, wchar('c'));
+ assert(wchars == "cc"w);
+
+ dchar[] dchars = ['a', 'b'];
+ fill(dchars, dchar('c'));
+ assert(dchars == "cc"d);
+}
+
+@nogc @safe unittest
+{
+ const(char)[] chars;
+ assert(chars.length == 0);
+ static assert(!__traits(compiles, fill(chars, 'c')));
+ wstring wchars;
+ assert(wchars.length == 0);
+ static assert(!__traits(compiles, fill(wchars, wchar('c'))));
+}
+
+@nogc @safe unittest
+{
+ char[] chars;
+ fill(chars, 'c');
+ assert(chars == ""c);
+}
+
+@safe unittest
+{
+ shared(char)[] chrs = ['r'];
+ fill(chrs, 'c');
+ assert(chrs == [shared(char)('c')]);
+}
+
+@nogc @safe unittest
+{
+ struct Str(size_t len)
+ {
+ private char[len] _data;
+ void opIndexAssign(char value) @safe @nogc
+ {_data[] = value;}
+ }
+ Str!2 str;
+ str.fill(':');
+ assert(str._data == "::");
+}
+
+@safe unittest
+{
+ char[] chars = ['a','b','c','d'];
+ chars[1 .. 3].fill(':');
+ assert(chars == "a::d");
+}
+// end issue 16342
+
+@safe unittest
+{
+ import std.conv : text;
+ import std.internal.test.dummyrange;
+
+ 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); }
+
+ // 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;
+ a.fill(b);
+ assert(a == [0, 0, 0]);
+ }
+ {
+ double[] a = [1, 2, 3];
+ immutable(int) b = 0;
+ a.fill(b);
+ assert(a == [0, 0, 0]);
+ }
+}
+
+/**
+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 $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ that exposes references to its elements and has assignable elements.
+ filler = The
+ $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives)
+ representing the _fill pattern.
+ */
+void fill(InputRange, ForwardRange)(InputRange range, ForwardRange filler)
+if (isInputRange!InputRange
+ && (isForwardRange!ForwardRange
+ || (isInputRange!ForwardRange && isInfinite!ForwardRange))
+ && is(typeof(InputRange.init.front = ForwardRange.init.front)))
+{
+ static if (isInfinite!ForwardRange)
+ {
+ //ForwardRange is infinite, no need for bounds checking or saving
+ static if (hasSlicing!ForwardRange && hasLength!InputRange
+ && 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!InputRange && hasLength!ForwardRange
+ && is(typeof(range.length > filler.length)))
+ {
+ //Case we have access to length
+ immutable 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!ForwardRange && 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;
+
+ 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
+ $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ that exposes references to its elements and has assignable
+ elements
+
+See_Also:
+ $(LREF fill)
+ $(LREF uninitializeFill)
+ */
+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).initializer();
+ if (p.ptr)
+ {
+ for ( ; !range.empty ; range.popFront() )
+ {
+ static if (__traits(isStaticArray, T))
+ {
+ // static array initializer only contains initialization
+ // for one element of the static array.
+ auto elemp = cast(void *) addressOf(range.front);
+ auto endp = elemp + T.sizeof;
+ while (elemp < endp)
+ {
+ memcpy(elemp, p.ptr, p.length);
+ elemp += p.length;
+ }
+ }
+ else
+ {
+ memcpy(addressOf(range.front), p.ptr, 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;
+}
+
+///
+@system unittest
+{
+ import core.stdc.stdlib : malloc, free;
+
+ struct S
+ {
+ int a = 10;
+ }
+
+ auto s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5];
+ initializeAll(s);
+ assert(s == [S(10), S(10), S(10), S(10), S(10)]);
+
+ scope(exit) free(s.ptr);
+}
+
+@system unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.meta : AliasSeq;
+ import std.traits : hasElaborateAssign;
+
+ //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()));
+ assert(!__traits(compiles, s.initializeAll()));
+ assert(s.empty);
+
+ //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).initializer().ptr);
+ assert( typeid(S2).initializer().ptr);
+ assert(!typeid(S3).initializer().ptr);
+ assert( typeid(S4).initializer().ptr);
+
+ foreach (S; AliasSeq!(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)]);
+ }
+ }
+}
+
+// test that initializeAll works for arrays of static arrays of structs with
+// elaborate assigns.
+@system unittest
+{
+ struct Int {
+ ~this() {}
+ int x = 3;
+ }
+ Int[2] xs = [Int(1), Int(2)];
+ struct R {
+ bool done;
+ bool empty() { return done; }
+ ref Int[2] front() { return xs; }
+ void popFront() { done = true; }
+ }
+ initializeAll(R());
+ assert(xs[0].x == 3);
+ assert(xs[1].x == 3);
+}
+
+// move
+/**
+Moves `source` into `target`, via a destructive copy when necessary.
+
+If `T` is a struct with a destructor or postblit defined, source is reset
+to its `.init` value after it is moved into target, otherwise it is
+left unchanged.
+
+Preconditions:
+If source has internal pointers that point to itself, it cannot be moved, and
+will trigger an assertion failure.
+
+Params:
+ source = Data to copy.
+ 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)
+{
+ // test @safe destructible
+ static if (__traits(compiles, (T t) @safe {}))
+ trustedMoveImpl(source, target);
+ else
+ moveImpl(source, target);
+}
+
+/// For non-struct types, `move` just performs `target = source`:
+@safe unittest
+{
+ Object obj1 = new Object;
+ Object obj2 = obj1;
+ Object obj3;
+
+ move(obj2, obj3);
+ assert(obj3 is obj1);
+ // obj2 unchanged
+ assert(obj2 is obj1);
+}
+
+///
+pure nothrow @safe @nogc 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(s12 == S1(10, 11));
+ assert(s11 == s12);
+
+ // 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() pure nothrow @safe @nogc { }
+ }
+ S2 s21 = { 3, 4 };
+ S2 s22;
+
+ move(s21, s22);
+
+ assert(s21 == S2(1, 2));
+ assert(s22 == S2(3, 4));
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+ import std.traits;
+
+ 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;
+ S5 s52 = s51;
+ S5 s53;
+ move(s52, s53);
+ assert(s53 is s51);
+}
+
+/// Ditto
+T move(T)(ref T source)
+{
+ // test @safe destructible
+ static if (__traits(compiles, (T t) @safe {}))
+ return trustedMoveImpl(source);
+ else
+ return moveImpl(source);
+}
+
+/// Non-copyable structs can still be moved:
+pure nothrow @safe @nogc unittest
+{
+ struct S
+ {
+ int a = 1;
+ @disable this(this);
+ ~this() pure nothrow @safe @nogc {}
+ }
+ S s1;
+ s1.a = 2;
+ S s2 = move(s1);
+ assert(s1.a == 1);
+ assert(s2.a == 2);
+}
+
+private void trustedMoveImpl(T)(ref T source, ref T target) @trusted
+{
+ moveImpl(source, target);
+}
+
+private void moveImpl(T)(ref T source, ref T target)
+{
+ import std.traits : hasElaborateDestructor;
+
+ static if (is(T == struct))
+ {
+ if (&source == &target) return;
+ // Destroy target before overwriting it
+ static if (hasElaborateDestructor!T) target.__xdtor();
+ }
+ // move and emplace source into target
+ moveEmplace(source, target);
+}
+
+private T trustedMoveImpl(T)(ref T source) @trusted
+{
+ return moveImpl(source);
+}
+
+private T moveImpl(T)(ref T source)
+{
+ T result = void;
+ moveEmplace(source, result);
+ return result;
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+ import std.traits;
+
+ 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;
+ S5 s52 = s51;
+ S5 s53;
+ s53 = move(s52);
+ assert(s53 is s51);
+}
+
+@system unittest
+{
+ static struct S { int n = 0; ~this() @system { n = 0; } }
+ S a, b;
+ static assert(!__traits(compiles, () @safe { move(a, b); }));
+ static assert(!__traits(compiles, () @safe { move(a); }));
+ a.n = 1;
+ () @trusted { move(a, b); }();
+ assert(a.n == 0);
+ a.n = 1;
+ () @trusted { move(a); }();
+ assert(a.n == 0);
+}
+
+@safe unittest//Issue 6217
+{
+ import std.algorithm.iteration : map;
+ auto x = map!"a"([1,2,3]);
+ x = move(x);
+}
+
+@safe 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);
+}
+
+@system 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;
+ move(x);
+ move(x, x);
+}
+
+/**
+ * Similar to $(LREF move) but assumes `target` is uninitialized. This
+ * is more efficient because `source` can be blitted over `target`
+ * without destroying or initializing it first.
+ *
+ * Params:
+ * source = value to be moved into target
+ * target = uninitialized value to be filled by source
+ */
+void moveEmplace(T)(ref T source, ref T target) @system
+{
+ 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))
+ {
+ assert(&source !is &target, "source and target must not be identical");
+
+ 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).initializer();
+ 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;
+ }
+}
+
+///
+pure nothrow @nogc @system unittest
+{
+ static struct Foo
+ {
+ pure nothrow @nogc:
+ this(int* ptr) { _ptr = ptr; }
+ ~this() { if (_ptr) ++*_ptr; }
+ int* _ptr;
+ }
+
+ int val;
+ Foo foo1 = void; // uninitialized
+ auto foo2 = Foo(&val); // initialized
+ assert(foo2._ptr is &val);
+
+ // Using `move(foo2, foo1)` would have an undefined effect because it would destroy
+ // the uninitialized foo1.
+ // moveEmplace directly overwrites foo1 without destroying or initializing it first.
+ moveEmplace(foo2, foo1);
+ assert(foo1._ptr is &val);
+ assert(foo2._ptr is null);
+ assert(val == 0);
+}
+
+// moveAll
+/**
+Calls `move(a, b)` for each element `a` in `src` and the corresponding
+element `b` in `tgt`, in increasing order.
+
+Preconditions:
+`walkLength(src) <= walkLength(tgt)`.
+This precondition will be asserted. If you cannot ensure there is enough room in
+`tgt` to accommodate all of `src` use $(LREF moveSome) instead.
+
+Params:
+ src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with
+ movable elements.
+ tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 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.
+ */
+InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt)
+if (isInputRange!InputRange1 && isInputRange!InputRange2
+ && is(typeof(move(src.front, tgt.front))))
+{
+ return moveAllImpl!move(src, tgt);
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ int[3] a = [ 1, 2, 3 ];
+ int[5] b;
+ assert(moveAll(a[], b[]) is b[3 .. $]);
+ assert(a[] == b[0 .. 3]);
+ int[3] cmp = [ 1, 2, 3 ];
+ assert(a[] == cmp[]);
+}
+
+/**
+ * Similar to $(LREF moveAll) but assumes all elements in `tgt` are
+ * uninitialized. Uses $(LREF moveEmplace) to move elements from
+ * `src` over elements from `tgt`.
+ */
+InputRange2 moveEmplaceAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) @system
+if (isInputRange!InputRange1 && isInputRange!InputRange2
+ && is(typeof(moveEmplace(src.front, tgt.front))))
+{
+ return moveAllImpl!moveEmplace(src, tgt);
+}
+
+///
+pure nothrow @nogc @system unittest
+{
+ static struct Foo
+ {
+ ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; }
+ int* _ptr;
+ }
+ int[3] refs = [0, 1, 2];
+ Foo[3] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2])];
+ Foo[5] dst = void;
+
+ auto tail = moveEmplaceAll(src[], dst[]); // move 3 value from src over dst
+ assert(tail.length == 2); // returns remaining uninitialized values
+ initializeAll(tail);
+
+ import std.algorithm.searching : all;
+ assert(src[].all!(e => e._ptr is null));
+ assert(dst[0 .. 3].all!(e => e._ptr !is null));
+}
+
+@system unittest
+{
+ struct InputRange
+ {
+ ref int front() { return data[0]; }
+ void popFront() { data.popFront; }
+ bool empty() { return data.empty; }
+ int[] data;
+ }
+ auto a = InputRange([ 1, 2, 3 ]);
+ auto b = InputRange(new int[5]);
+ moveAll(a, b);
+ assert(a.data == b.data[0 .. 3]);
+ assert(a.data == [ 1, 2, 3 ]);
+}
+
+private InputRange2 moveAllImpl(alias moveOp, InputRange1, InputRange2)(
+ ref InputRange1 src, ref InputRange2 tgt)
+{
+ import std.exception : enforce;
+
+ static if (isRandomAccessRange!InputRange1 && hasLength!InputRange1 && hasLength!InputRange2
+ && hasSlicing!InputRange2 && isRandomAccessRange!InputRange2)
+ {
+ auto toMove = src.length;
+ assert(toMove <= tgt.length);
+ foreach (idx; 0 .. toMove)
+ moveOp(src[idx], tgt[idx]);
+ return tgt[toMove .. tgt.length];
+ }
+ else
+ {
+ for (; !src.empty; src.popFront(), tgt.popFront())
+ {
+ assert(!tgt.empty);
+ moveOp(src.front, tgt.front);
+ }
+ return tgt;
+ }
+}
+
+// moveSome
+/**
+Calls `move(a, b)` for each element `a` in `src` and the corresponding
+element `b` in `tgt`, in increasing order, stopping when either range has been
+exhausted.
+
+Params:
+ src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with
+ movable elements.
+ tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 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!(InputRange1, InputRange2) moveSome(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt)
+if (isInputRange!InputRange1 && isInputRange!InputRange2
+ && is(typeof(move(src.front, tgt.front))))
+{
+ return moveSomeImpl!move(src, tgt);
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ int[5] a = [ 1, 2, 3, 4, 5 ];
+ int[3] b;
+ assert(moveSome(a[], b[])[0] is a[3 .. $]);
+ assert(a[0 .. 3] == b);
+ assert(a == [ 1, 2, 3, 4, 5 ]);
+}
+
+/**
+ * Same as $(LREF moveSome) but assumes all elements in `tgt` are
+ * uninitialized. Uses $(LREF moveEmplace) to move elements from
+ * `src` over elements from `tgt`.
+ */
+Tuple!(InputRange1, InputRange2) moveEmplaceSome(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) @system
+if (isInputRange!InputRange1 && isInputRange!InputRange2
+ && is(typeof(move(src.front, tgt.front))))
+{
+ return moveSomeImpl!moveEmplace(src, tgt);
+}
+
+///
+pure nothrow @nogc @system unittest
+{
+ static struct Foo
+ {
+ ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; }
+ int* _ptr;
+ }
+ int[4] refs = [0, 1, 2, 3];
+ Foo[4] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2]), Foo(&refs[3])];
+ Foo[3] dst = void;
+
+ auto res = moveEmplaceSome(src[], dst[]);
+ assert(res.length == 2);
+
+ import std.algorithm.searching : all;
+ assert(src[0 .. 3].all!(e => e._ptr is null));
+ assert(src[3]._ptr !is null);
+ assert(dst[].all!(e => e._ptr !is null));
+}
+
+private Tuple!(InputRange1, InputRange2) moveSomeImpl(alias moveOp, InputRange1, InputRange2)(
+ ref InputRange1 src, ref InputRange2 tgt)
+{
+ for (; !src.empty && !tgt.empty; src.popFront(), tgt.popFront())
+ moveOp(src.front, tgt.front);
+ return tuple(src, tgt);
+ }
+
+
+// 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,
+}
+
+///
+@safe unittest
+{
+ import std.stdio;
+ import std.algorithm.sorting : partition;
+ int[] a = [0, 1, 2, 3];
+ assert(remove!(SwapStrategy.stable)(a, 1) == [0, 2, 3]);
+ a = [0, 1, 2, 3];
+ assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 3, 2]);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.sorting : partition;
+
+ // Put stuff greater than 3 on the left
+ auto arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ assert(partition!(a => a > 3, SwapStrategy.stable)(arr) == [1, 2, 3]);
+ assert(arr == [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]);
+
+ arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ assert(partition!(a => a > 3, SwapStrategy.semistable)(arr) == [2, 3, 1]);
+ assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1]);
+
+ arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ assert(partition!(a => a > 3, SwapStrategy.unstable)(arr) == [3, 2, 1]);
+ assert(arr == [10, 9, 8, 4, 5, 6, 7, 3, 2, 1]);
+}
+
+/**
+Eliminates elements at given offsets from `range` and returns the shortened
+range.
+
+For example, here is how to _remove a single element from an array:
+
+----
+string[] a = [ "a", "b", "c", "d" ];
+a = a.remove(1); // remove element at offset 1
+assert(a == [ "a", "c", "d"]);
+----
+
+Note that `remove` does not change the length of the original _range directly;
+instead, it returns the shortened _range. If its return value is not assigned to
+the original _range, the original _range will retain its original length, though
+its contents will have changed:
+
+----
+int[] a = [ 3, 5, 7, 8 ];
+assert(remove(a, 1) == [ 3, 7, 8 ]);
+assert(a == [ 3, 7, 8, 8 ]);
+----
+
+The element at _offset `1` has been removed and the rest of the elements have
+shifted up to fill its place, however, the original array remains of the same
+length. This is because all functions in `std.algorithm` only change $(I
+content), not $(I topology). The value `8` is repeated because $(LREF move) was
+invoked to rearrange elements, and on integers `move` simply copies the source
+to the destination. To replace `a` with the effect of the removal, simply
+assign the slice returned by `remove` to it, as shown in the first example.
+
+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 that 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, 5, 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 bidirectional ranges that have assignable
+lvalue elements. 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.))
+
+Params:
+ s = a SwapStrategy to determine if the original order needs to be preserved
+ range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,_range,primitives)
+ with a length member
+ offset = which element(s) to remove
+
+Returns:
+ a range containing all of the elements of range with offset removed
+ */
+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)
+{
+ 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 tgtPos = 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 >= tgtPos);
+ tgt.popFrontExactly(blackouts[left].pos - tgtPos);
+ tgtPos = blackouts[left].pos;
+
+ // Number of elements to the right of blackouts[right]
+ immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len);
+ size_t toMove = void;
+ if (tailLen < blackouts[left].len)
+ {
+ toMove = tailLen;
+ blackouts[left].pos += toMove;
+ blackouts[left].len -= toMove;
+ }
+ else
+ {
+ toMove = blackouts[left].len;
+ ++left;
+ }
+ tgtPos += toMove;
+ foreach (i; 0 .. toMove)
+ {
+ move(range.back, tgt.front);
+ range.popBack();
+ tgt.popFront();
+ }
+ }
+
+ 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 pure unittest
+{
+ import std.typecons : tuple;
+
+ auto a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5 ]);
+ a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.stable)(a, 1, 3) == [ 0, 2, 4, 5] );
+ a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 6)) == [ 0, 2 ]);
+
+ a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 5, 2, 3, 4]);
+ a = [ 0, 1, 2, 3, 4, 5 ];
+ assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4)) == [0, 5, 4]);
+}
+
+@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;
+ int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ 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 ];
+ 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 ];
+ 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 ];
+ 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
+$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,_range,primitives) $(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.
+
+Params:
+ range = a bidirectional ranges with lvalue elements
+
+Returns:
+ the range with all of the elements where $(D pred) is $(D true)
+ removed
+*/
+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
+{
+ 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 ];
+ assert(remove!("a == 2", SwapStrategy.stable)(a) ==
+ [ 1, 3, 3, 4, 5, 5, 6 ]);
+}
+
+@nogc @system unittest
+{
+ // @nogc test
+ int[10] arr = [0,1,2,3,4,5,6,7,8,9];
+ alias pred = e => e < 5;
+
+ auto r = arr[].remove!(SwapStrategy.unstable)(0);
+ r = r.remove!(SwapStrategy.stable)(0);
+ r = r.remove!(pred, SwapStrategy.unstable);
+ r = r.remove!(pred, SwapStrategy.stable);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : min;
+ import std.algorithm.searching : all, any;
+ import std.algorithm.sorting : isStrictlyMonotonic;
+ import std.array : array;
+ import std.meta : AliasSeq;
+ import std.range : iota, only;
+ import std.typecons : Tuple;
+ alias S = Tuple!(int[2]);
+ S[] soffsets;
+ foreach (start; 0 .. 5)
+ foreach (end; min(start+1,5) .. 5)
+ soffsets ~= S([start,end]);
+ alias D = Tuple!(int[2],int[2]);
+ D[] doffsets;
+ foreach (start1; 0 .. 10)
+ foreach (end1; min(start1+1,10) .. 10)
+ foreach (start2; end1 .. 10)
+ foreach (end2; min(start2+1,10) .. 10)
+ doffsets ~= D([start1,end1],[start2,end2]);
+ alias T = Tuple!(int[2],int[2],int[2]);
+ T[] toffsets;
+ foreach (start1; 0 .. 15)
+ foreach (end1; min(start1+1,15) .. 15)
+ foreach (start2; end1 .. 15)
+ foreach (end2; min(start2+1,15) .. 15)
+ foreach (start3; end2 .. 15)
+ foreach (end3; min(start3+1,15) .. 15)
+ toffsets ~= T([start1,end1],[start2,end2],[start3,end3]);
+
+ static void verify(O...)(int[] r, int len, int removed, bool stable, O offsets)
+ {
+ assert(r.length == len - removed);
+ assert(!stable || r.isStrictlyMonotonic);
+ assert(r.all!(e => all!(o => e < o[0] || e >= o[1])(offsets.only)));
+ }
+
+ foreach (offsets; AliasSeq!(soffsets,doffsets,toffsets))
+ foreach (os; offsets)
+ {
+ int len = 5*os.length;
+ auto w = iota(0, len).array;
+ auto x = w.dup;
+ auto y = w.dup;
+ auto z = w.dup;
+ alias pred = e => any!(o => o[0] <= e && e < o[1])(only(os.expand));
+ w = w.remove!(SwapStrategy.unstable)(os.expand);
+ x = x.remove!(SwapStrategy.stable)(os.expand);
+ y = y.remove!(pred, SwapStrategy.unstable);
+ z = z.remove!(pred, SwapStrategy.stable);
+ int removed;
+ foreach (o; os)
+ removed += o[1] - o[0];
+ verify(w, len, removed, false, os[]);
+ verify(x, len, removed, true, os[]);
+ verify(y, len, removed, false, os[]);
+ verify(z, len, removed, true, os[]);
+ assert(w == y);
+ assert(x == z);
+ }
+}
+
+// reverse
+/**
+Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D
+swap).
+Params:
+ r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
+ with swappable elements or a random access range with a length member
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view
+*/
+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++)
+ {
+ r.swapAt(i, last-i);
+ }
+}
+
+@safe unittest
+{
+ 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.
+
+Params:
+ s = a narrow string
+
+Bugs:
+ When passing a sting with unicode modifiers on characters, such as $(D \u0301),
+ this function will not properly keep the position of the modifier. For example,
+ reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("d́ab") instead of
+ $(D da\u0301b) ("dáb").
+*/
+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 = 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");
+}
+
+/**
+ 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.
+
+ Params:
+ range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
+ or $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ element = the elements to remove
+
+ Returns:
+ a Range with all of range except element at the start and end
+*/
+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 = 1, imm2 = 2;
+ static assert(!__traits(compiles, swap(imm1, imm2)));
+
+ int c = imm1 + 0;
+ int d = imm2 + 0;
+ swap(c, d);
+ assert(c == 2);
+ assert(d == 1);
+}
+
+///
+@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;
+ assert(const1.n == 0 && const2.n == 0);
+ static assert(!__traits(compiles, swap(const1, const2)));
+}
+
+@safe unittest
+{
+ //Bug# 4789
+ int[1] s = [1];
+ swap(s, s);
+
+ int[3] a = [1, 2, 3];
+ swap(a[1], a[2]);
+ assert(a == [1, 3, 2]);
+}
+
+@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;
+ int i2 = 2;
+ int i3 = 3;
+ }
+ S s;
+ static assert(!__traits(compiles, swap(s, s)));
+ swap(s.i2, s.i3);
+ assert(s.i2 == 3);
+ assert(s.i3 == 2);
+}
+
+@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;
+ swap(a, b);
+}
+
+@system 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));
+}
+
+@system 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);
+}
+
+/// ditto
+void swap(T)(ref T lhs, ref T rhs)
+if (is(typeof(lhs.proxySwap(rhs))))
+{
+ lhs.proxySwap(rhs);
+}
+
+/**
+Swaps two elements in-place of a range `r`,
+specified by their indices `i1` and `i2`.
+
+Params:
+ r = a range with swappable elements
+ i1 = first index
+ i2 = second index
+*/
+void swapAt(R)(auto ref R r, size_t i1, size_t i2)
+{
+ static if (is(typeof(&r.swapAt)))
+ {
+ r.swapAt(i1, i2);
+ }
+ else static if (is(typeof(&r[i1])))
+ {
+ swap(r[i1], r[i2]);
+ }
+ else
+ {
+ if (i1 == i2) return;
+ auto t1 = r.moveAt(i1);
+ auto t2 = r.moveAt(i2);
+ r[i2] = t1;
+ r[i1] = t2;
+ }
+}
+
+///
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = [1, 2, 3];
+ a.swapAt(1, 2);
+ assert(a.equal([1, 3, 2]));
+}
+
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = [4, 5, 6];
+ a.swapAt(1, 1);
+ assert(a.equal([4, 5, 6]));
+}
+
+pure @safe nothrow unittest
+{
+ // test non random access ranges
+ import std.algorithm.comparison : equal;
+ import std.array : array;
+
+ char[] b = ['a', 'b', 'c'];
+ b.swapAt(1, 2);
+ assert(b.equal(['a', 'c', 'b']));
+
+ int[3] c = [1, 2, 3];
+ c.swapAt(1, 2);
+ assert(c.array.equal([1, 3, 2]));
+
+ // opIndex returns lvalue
+ struct RandomIndexType(T)
+ {
+ T payload;
+
+ @property ref auto opIndex(size_t i)
+ {
+ return payload[i];
+ }
+
+ }
+ auto d = RandomIndexType!(int[])([4, 5, 6]);
+ d.swapAt(1, 2);
+ assert(d.payload.equal([4, 6, 5]));
+
+ // custom moveAt and opIndexAssign
+ struct RandomMoveAtType(T)
+ {
+ T payload;
+
+ ElementType!T moveAt(size_t i)
+ {
+ return payload.moveAt(i);
+ }
+
+ void opIndexAssign(ElementType!T val, size_t idx)
+ {
+ payload[idx] = val;
+ }
+ }
+ auto e = RandomMoveAtType!(int[])([7, 8, 9]);
+ e.swapAt(1, 2);
+ assert(e.payload.equal([7, 9, 8]));
+
+
+ // custom swapAt
+ struct RandomSwapAtType(T)
+ {
+ T payload;
+
+ void swapAt(size_t i)
+ {
+ return payload.swapAt(i);
+ }
+ }
+ auto f = RandomMoveAtType!(int[])([10, 11, 12]);
+ swapAt(f, 1, 2);
+ assert(f.payload.equal([10, 12, 11]));
+}
+
+private 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.
+
+Params:
+ r1 = an $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ with swappable elements
+ r2 = an $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ with swappable elements
+
+Returns:
+ Tuple containing the remainder portions of r1 and r2 that were not swapped
+*/
+Tuple!(InputRange1, InputRange2)
+swapRanges(InputRange1, InputRange2)(InputRange1 r1, InputRange2 r2)
+if (hasSwappableElements!InputRange1 && hasSwappableElements!InputRange2
+ && is(ElementType!InputRange1 == ElementType!InputRange2))
+{
+ for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront())
+ {
+ swap(r1.front, r2.front);
+ }
+ return tuple(r1, r2);
+}
+
+///
+@safe unittest
+{
+ import std.range : empty;
+ 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
+ $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ that exposes references to its elements and has assignable
+ elements
+ value = Assigned to each element of range
+
+See_Also:
+ $(LREF fill)
+ $(LREF initializeAll)
+ */
+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);
+}
+
+///
+nothrow @system unittest
+{
+ import core.stdc.stdlib : malloc, free;
+
+ auto s = (cast(int*) malloc(5 * int.sizeof))[0 .. 5];
+ uninitializedFill(s, 42);
+ assert(s == [ 42, 42, 42, 42, 42 ]);
+
+ scope(exit) free(s.ptr);
+}