aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/algorithm
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
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')
-rw-r--r--libphobos/src/std/algorithm/comparison.d2159
-rw-r--r--libphobos/src/std/algorithm/internal.d77
-rw-r--r--libphobos/src/std/algorithm/iteration.d5187
-rw-r--r--libphobos/src/std/algorithm/mutation.d2909
-rw-r--r--libphobos/src/std/algorithm/package.d198
-rw-r--r--libphobos/src/std/algorithm/searching.d4600
-rw-r--r--libphobos/src/std/algorithm/setops.d1521
-rw-r--r--libphobos/src/std/algorithm/sorting.d4468
8 files changed, 21119 insertions, 0 deletions
diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d
new file mode 100644
index 0000000..faa4d44
--- /dev/null
+++ b/libphobos/src/std/algorithm/comparison.d
@@ -0,0 +1,2159 @@
+// Written in the D programming language.
+/**
+This is a submodule of $(MREF std, algorithm).
+It contains generic _comparison algorithms.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE Cheat Sheet,
+$(TR $(TH Function Name) $(TH Description))
+$(T2 among,
+ Checks if a value is among a set of values, e.g.
+ $(D if (v.among(1, 2, 3)) // `v` is 1, 2 or 3))
+$(T2 castSwitch,
+ $(D (new A()).castSwitch((A a)=>1,(B b)=>2)) returns $(D 1).)
+$(T2 clamp,
+ $(D clamp(1, 3, 6)) returns $(D 3). $(D clamp(4, 3, 6)) returns $(D 4).)
+$(T2 cmp,
+ $(D cmp("abc", "abcd")) is $(D -1), $(D cmp("abc", "aba")) is $(D 1),
+ and $(D cmp("abc", "abc")) is $(D 0).)
+$(T2 either,
+ Return first parameter $(D p) that passes an $(D if (p)) test, e.g.
+ $(D either(0, 42, 43)) returns $(D 42).)
+$(T2 equal,
+ Compares ranges for element-by-element equality, e.g.
+ $(D equal([1, 2, 3], [1.0, 2.0, 3.0])) returns $(D true).)
+$(T2 isPermutation,
+ $(D isPermutation([1, 2], [2, 1])) returns $(D true).)
+$(T2 isSameLength,
+ $(D isSameLength([1, 2, 3], [4, 5, 6])) returns $(D true).)
+$(T2 levenshteinDistance,
+ $(D levenshteinDistance("kitten", "sitting")) returns $(D 3) by using
+ the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance,
+ Levenshtein distance _algorithm).)
+$(T2 levenshteinDistanceAndPath,
+ $(D levenshteinDistanceAndPath("kitten", "sitting")) returns
+ $(D tuple(3, "snnnsni")) by using the
+ $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance,
+ Levenshtein distance _algorithm).)
+$(T2 max,
+ $(D max(3, 4, 2)) returns $(D 4).)
+$(T2 min,
+ $(D min(3, 4, 2)) returns $(D 2).)
+$(T2 mismatch,
+ $(D mismatch("oh hi", "ohayo")) returns $(D tuple(" hi", "ayo")).)
+$(T2 predSwitch,
+ $(D 2.predSwitch(1, "one", 2, "two", 3, "three")) returns $(D "two").)
+)
+
+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/_comparison.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.comparison;
+
+// FIXME
+import std.functional; // : unaryFun, binaryFun;
+import std.range.primitives;
+import std.traits;
+// FIXME
+import std.meta : allSatisfy;
+import std.typecons; // : tuple, Tuple, Flag, Yes;
+
+/**
+Find $(D value) _among $(D values), returning the 1-based index
+of the first matching value in $(D values), or $(D 0) if $(D value)
+is not _among $(D values). The predicate $(D pred) is used to
+compare values, and uses equality by default.
+
+Params:
+ pred = The predicate used to compare the values.
+ value = The value to search for.
+ values = The values to compare the value to.
+
+Returns:
+ 0 if value was not found among the values, otherwise the index of the
+ found value plus one is returned.
+
+See_Also:
+$(REF_ALTTEXT find, find, std,algorithm,searching) and $(REF_ALTTEXT canFind, canFind, std,algorithm,searching) for finding a value in a
+range.
+*/
+uint among(alias pred = (a, b) => a == b, Value, Values...)
+ (Value value, Values values)
+if (Values.length != 0)
+{
+ foreach (uint i, ref v; values)
+ {
+ import std.functional : binaryFun;
+ if (binaryFun!pred(value, v)) return i + 1;
+ }
+ return 0;
+}
+
+/// Ditto
+template among(values...)
+if (isExpressionTuple!values)
+{
+ uint among(Value)(Value value)
+ if (!is(CommonType!(Value, values) == void))
+ {
+ switch (value)
+ {
+ foreach (uint i, v; values)
+ case v:
+ return i + 1;
+ default:
+ return 0;
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ assert(3.among(1, 42, 24, 3, 2));
+
+ if (auto pos = "bar".among("foo", "bar", "baz"))
+ assert(pos == 2);
+ else
+ assert(false);
+
+ // 42 is larger than 24
+ assert(42.among!((lhs, rhs) => lhs > rhs)(43, 24, 100) == 2);
+}
+
+/**
+Alternatively, $(D values) can be passed at compile-time, allowing for a more
+efficient search, but one that only supports matching on equality:
+*/
+@safe unittest
+{
+ assert(3.among!(2, 3, 4));
+ assert("bar".among!("foo", "bar", "baz") == 2);
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+
+ if (auto pos = 3.among(1, 2, 3))
+ assert(pos == 3);
+ else
+ assert(false);
+ assert(!4.among(1, 2, 3));
+
+ auto position = "hello".among("hello", "world");
+ assert(position);
+ assert(position == 1);
+
+ alias values = AliasSeq!("foo", "bar", "baz");
+ auto arr = [values];
+ assert(arr[0 .. "foo".among(values)] == ["foo"]);
+ assert(arr[0 .. "bar".among(values)] == ["foo", "bar"]);
+ assert(arr[0 .. "baz".among(values)] == arr);
+ assert("foobar".among(values) == 0);
+
+ if (auto pos = 3.among!(1, 2, 3))
+ assert(pos == 3);
+ else
+ assert(false);
+ assert(!4.among!(1, 2, 3));
+
+ position = "hello".among!("hello", "world");
+ assert(position);
+ assert(position == 1);
+
+ static assert(!__traits(compiles, "a".among!("a", 42)));
+ static assert(!__traits(compiles, (Object.init).among!(42, "a")));
+}
+
+// Used in castSwitch to find the first choice that overshadows the last choice
+// in a tuple.
+private template indexOfFirstOvershadowingChoiceOnLast(choices...)
+{
+ alias firstParameterTypes = Parameters!(choices[0]);
+ alias lastParameterTypes = Parameters!(choices[$ - 1]);
+
+ static if (lastParameterTypes.length == 0)
+ {
+ // If the last is null-typed choice, check if the first is null-typed.
+ enum isOvershadowing = firstParameterTypes.length == 0;
+ }
+ else static if (firstParameterTypes.length == 1)
+ {
+ // If the both first and last are not null-typed, check for overshadowing.
+ enum isOvershadowing =
+ is(firstParameterTypes[0] == Object) // Object overshadows all other classes!(this is needed for interfaces)
+ || is(lastParameterTypes[0] : firstParameterTypes[0]);
+ }
+ else
+ {
+ // If the first is null typed and the last is not - the is no overshadowing.
+ enum isOvershadowing = false;
+ }
+
+ static if (isOvershadowing)
+ {
+ enum indexOfFirstOvershadowingChoiceOnLast = 0;
+ }
+ else
+ {
+ enum indexOfFirstOvershadowingChoiceOnLast =
+ 1 + indexOfFirstOvershadowingChoiceOnLast!(choices[1..$]);
+ }
+}
+
+/**
+Executes and returns one of a collection of handlers based on the type of the
+switch object.
+
+The first choice that $(D switchObject) can be casted to the type
+of argument it accepts will be called with $(D switchObject) casted to that
+type, and the value it'll return will be returned by $(D castSwitch).
+
+If a choice's return type is void, the choice must throw an exception, unless
+all the choices are void. In that case, castSwitch itself will return void.
+
+Throws: If none of the choice matches, a $(D SwitchError) will be thrown. $(D
+SwitchError) will also be thrown if not all the choices are void and a void
+choice was executed without throwing anything.
+
+Params:
+ choices = The $(D choices) needs to be composed of function or delegate
+ handlers that accept one argument. There can also be a choice that
+ accepts zero arguments. That choice will be invoked if the $(D
+ switchObject) is null.
+ switchObject = the object against which the tests are being made.
+
+Returns:
+ The value of the selected choice.
+
+Note: $(D castSwitch) can only be used with object types.
+*/
+auto castSwitch(choices...)(Object switchObject)
+{
+ import core.exception : SwitchError;
+ import std.format : format;
+
+ // Check to see if all handlers return void.
+ enum areAllHandlersVoidResult = {
+ bool result = true;
+ foreach (index, choice; choices)
+ {
+ result &= is(ReturnType!choice == void);
+ }
+ return result;
+ }();
+
+ if (switchObject !is null)
+ {
+
+ // Checking for exact matches:
+ const classInfo = typeid(switchObject);
+ foreach (index, choice; choices)
+ {
+ static assert(isCallable!choice,
+ "A choice handler must be callable");
+
+ alias choiceParameterTypes = Parameters!choice;
+ static assert(choiceParameterTypes.length <= 1,
+ "A choice handler can not have more than one argument.");
+
+ static if (choiceParameterTypes.length == 1)
+ {
+ alias CastClass = choiceParameterTypes[0];
+ static assert(is(CastClass == class) || is(CastClass == interface),
+ "A choice handler can have only class or interface typed argument.");
+
+ // Check for overshadowing:
+ immutable indexOfOvershadowingChoice =
+ indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]);
+ static assert(indexOfOvershadowingChoice == index,
+ "choice number %d(type %s) is overshadowed by choice number %d(type %s)".format(
+ index + 1, CastClass.stringof, indexOfOvershadowingChoice + 1,
+ Parameters!(choices[indexOfOvershadowingChoice])[0].stringof));
+
+ if (classInfo == typeid(CastClass))
+ {
+ static if (is(ReturnType!(choice) == void))
+ {
+ choice(cast(CastClass) switchObject);
+ static if (areAllHandlersVoidResult)
+ {
+ return;
+ }
+ else
+ {
+ throw new SwitchError("Handlers that return void should throw");
+ }
+ }
+ else
+ {
+ return choice(cast(CastClass) switchObject);
+ }
+ }
+ }
+ }
+
+ // Checking for derived matches:
+ foreach (choice; choices)
+ {
+ alias choiceParameterTypes = Parameters!choice;
+ static if (choiceParameterTypes.length == 1)
+ {
+ if (auto castedObject = cast(choiceParameterTypes[0]) switchObject)
+ {
+ static if (is(ReturnType!(choice) == void))
+ {
+ choice(castedObject);
+ static if (areAllHandlersVoidResult)
+ {
+ return;
+ }
+ else
+ {
+ throw new SwitchError("Handlers that return void should throw");
+ }
+ }
+ else
+ {
+ return choice(castedObject);
+ }
+ }
+ }
+ }
+ }
+ else // If switchObject is null:
+ {
+ // Checking for null matches:
+ foreach (index, choice; choices)
+ {
+ static if (Parameters!(choice).length == 0)
+ {
+ immutable indexOfOvershadowingChoice =
+ indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]);
+
+ // Check for overshadowing:
+ static assert(indexOfOvershadowingChoice == index,
+ "choice number %d(null reference) is overshadowed by choice number %d(null reference)".format(
+ index + 1, indexOfOvershadowingChoice + 1));
+
+ if (switchObject is null)
+ {
+ static if (is(ReturnType!(choice) == void))
+ {
+ choice();
+ static if (areAllHandlersVoidResult)
+ {
+ return;
+ }
+ else
+ {
+ throw new SwitchError("Handlers that return void should throw");
+ }
+ }
+ else
+ {
+ return choice();
+ }
+ }
+ }
+ }
+ }
+
+ // In case nothing matched:
+ throw new SwitchError("Input not matched by any choice");
+}
+
+///
+@system unittest
+{
+ import std.algorithm.iteration : map;
+ import std.format : format;
+
+ class A
+ {
+ int a;
+ this(int a) {this.a = a;}
+ @property int i() { return a; }
+ }
+ interface I { }
+ class B : I { }
+
+ Object[] arr = [new A(1), new B(), null];
+
+ auto results = arr.map!(castSwitch!(
+ (A a) => "A with a value of %d".format(a.a),
+ (I i) => "derived from I",
+ () => "null reference",
+ ))();
+
+ // A is handled directly:
+ assert(results[0] == "A with a value of 1");
+ // B has no handler - it is handled by the handler of I:
+ assert(results[1] == "derived from I");
+ // null is handled by the null handler:
+ assert(results[2] == "null reference");
+}
+
+/// Using with void handlers:
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ class A { }
+ class B { }
+ // Void handlers are allowed if they throw:
+ assertThrown!Exception(
+ new B().castSwitch!(
+ (A a) => 1,
+ (B d) { throw new Exception("B is not allowed!"); }
+ )()
+ );
+
+ // Void handlers are also allowed if all the handlers are void:
+ new A().castSwitch!(
+ (A a) { },
+ (B b) { assert(false); },
+ )();
+}
+
+@system unittest
+{
+ import core.exception : SwitchError;
+ import std.exception : assertThrown;
+
+ interface I { }
+ class A : I { }
+ class B { }
+
+ // Nothing matches:
+ assertThrown!SwitchError((new A()).castSwitch!(
+ (B b) => 1,
+ () => 2,
+ )());
+
+ // Choices with multiple arguments are not allowed:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ (A a, B b) => 0,
+ )()));
+
+ // Only callable handlers allowed:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ 1234,
+ )()));
+
+ // Only object arguments allowed:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ (int x) => 0,
+ )()));
+
+ // Object overshadows regular classes:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ (Object o) => 0,
+ (A a) => 1,
+ )()));
+
+ // Object overshadows interfaces:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ (Object o) => 0,
+ (I i) => 1,
+ )()));
+
+ // No multiple null handlers allowed:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ () => 0,
+ () => 1,
+ )()));
+
+ // No non-throwing void handlers allowed(when there are non-void handlers):
+ assertThrown!SwitchError((new A()).castSwitch!(
+ (A a) {},
+ (B b) => 2,
+ )());
+
+ // All-void handlers work for the null case:
+ null.castSwitch!(
+ (Object o) { assert(false); },
+ () { },
+ )();
+
+ // Throwing void handlers work for the null case:
+ assertThrown!Exception(null.castSwitch!(
+ (Object o) => 1,
+ () { throw new Exception("null"); },
+ )());
+}
+
+@system unittest
+{
+ interface I { }
+ class B : I { }
+ class C : I { }
+
+ assert((new B()).castSwitch!(
+ (B b) => "class B",
+ (I i) => "derived from I",
+ ) == "class B");
+
+ assert((new C()).castSwitch!(
+ (B b) => "class B",
+ (I i) => "derived from I",
+ ) == "derived from I");
+}
+
+/** Clamps a value into the given bounds.
+
+This functions is equivalent to $(D max(lower, min(upper,val))).
+
+Params:
+ val = The value to _clamp.
+ lower = The _lower bound of the _clamp.
+ upper = The _upper bound of the _clamp.
+
+Returns:
+ Returns $(D val), if it is between $(D lower) and $(D upper).
+ Otherwise returns the nearest of the two.
+
+*/
+auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper)
+in
+{
+ import std.functional : greaterThan;
+ assert(!lower.greaterThan(upper));
+}
+body
+{
+ return max(lower, min(upper, val));
+}
+
+///
+@safe unittest
+{
+ assert(clamp(2, 1, 3) == 2);
+ assert(clamp(0, 1, 3) == 1);
+ assert(clamp(4, 1, 3) == 3);
+
+ assert(clamp(1, 1, 1) == 1);
+
+ assert(clamp(5, -1, 2u) == 2);
+}
+
+@safe unittest
+{
+ int a = 1;
+ short b = 6;
+ double c = 2;
+ static assert(is(typeof(clamp(c,a,b)) == double));
+ assert(clamp(c, a, b) == c);
+ assert(clamp(a-c, a, b) == a);
+ assert(clamp(b+c, a, b) == b);
+ // mixed sign
+ a = -5;
+ uint f = 5;
+ static assert(is(typeof(clamp(f, a, b)) == int));
+ assert(clamp(f, a, b) == f);
+ // similar type deduction for (u)long
+ static assert(is(typeof(clamp(-1L, -2L, 2UL)) == long));
+
+ // user-defined types
+ import std.datetime : Date;
+ assert(clamp(Date(1982, 1, 4), Date(1012, 12, 21), Date(2012, 12, 21)) == Date(1982, 1, 4));
+ assert(clamp(Date(1982, 1, 4), Date.min, Date.max) == Date(1982, 1, 4));
+ // UFCS style
+ assert(Date(1982, 1, 4).clamp(Date.min, Date.max) == Date(1982, 1, 4));
+
+}
+
+// cmp
+/**********************************
+Performs three-way lexicographical comparison on two
+$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives)
+according to predicate $(D pred). Iterating $(D r1) and $(D r2) in
+lockstep, $(D cmp) compares each element $(D e1) of $(D r1) with the
+corresponding element $(D e2) in $(D r2). If one of the ranges has been
+finished, $(D cmp) returns a negative value if $(D r1) has fewer
+elements than $(D r2), a positive value if $(D r1) has more elements
+than $(D r2), and $(D 0) if the ranges have the same number of
+elements.
+
+If the ranges are strings, $(D cmp) performs UTF decoding
+appropriately and compares the ranges one code point at a time.
+
+Params:
+ pred = The predicate used for comparison.
+ r1 = The first range.
+ r2 = The second range.
+
+Returns:
+ 0 if both ranges compare equal. -1 if the first differing element of $(D
+ r1) is less than the corresponding element of $(D r2) according to $(D
+ pred). 1 if the first differing element of $(D r2) is less than the
+ corresponding element of $(D r1) according to $(D pred).
+
+*/
+int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2)
+if (isInputRange!R1 && isInputRange!R2 && !(isSomeString!R1 && isSomeString!R2))
+{
+ for (;; r1.popFront(), r2.popFront())
+ {
+ if (r1.empty) return -cast(int)!r2.empty;
+ if (r2.empty) return !r1.empty;
+ auto a = r1.front, b = r2.front;
+ if (binaryFun!pred(a, b)) return -1;
+ if (binaryFun!pred(b, a)) return 1;
+ }
+}
+
+/// ditto
+int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2)
+if (isSomeString!R1 && isSomeString!R2)
+{
+ import core.stdc.string : memcmp;
+ import std.utf : decode;
+
+ static if (is(typeof(pred) : string))
+ enum isLessThan = pred == "a < b";
+ else
+ enum isLessThan = false;
+
+ // For speed only
+ static int threeWay(size_t a, size_t b)
+ {
+ static if (size_t.sizeof == int.sizeof && isLessThan)
+ return a - b;
+ else
+ return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0;
+ }
+ // For speed only
+ // @@@BUG@@@ overloading should be allowed for nested functions
+ static int threeWayInt(int a, int b)
+ {
+ static if (isLessThan)
+ return a - b;
+ else
+ return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0;
+ }
+
+ static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof && isLessThan)
+ {
+ static if (typeof(r1[0]).sizeof == 1)
+ {
+ immutable len = min(r1.length, r2.length);
+ immutable result = __ctfe ?
+ {
+ foreach (i; 0 .. len)
+ {
+ if (r1[i] != r2[i])
+ return threeWayInt(r1[i], r2[i]);
+ }
+ return 0;
+ }()
+ : () @trusted { return memcmp(r1.ptr, r2.ptr, len); }();
+ if (result) return result;
+ }
+ else
+ {
+ auto p1 = r1.ptr, p2 = r2.ptr,
+ pEnd = p1 + min(r1.length, r2.length);
+ for (; p1 != pEnd; ++p1, ++p2)
+ {
+ if (*p1 != *p2) return threeWayInt(cast(int) *p1, cast(int) *p2);
+ }
+ }
+ return threeWay(r1.length, r2.length);
+ }
+ else
+ {
+ for (size_t i1, i2;;)
+ {
+ if (i1 == r1.length) return threeWay(i2, r2.length);
+ if (i2 == r2.length) return threeWay(r1.length, i1);
+ immutable c1 = decode(r1, i1),
+ c2 = decode(r2, i2);
+ if (c1 != c2) return threeWayInt(cast(int) c1, cast(int) c2);
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ int result;
+
+ result = cmp("abc", "abc");
+ assert(result == 0);
+ result = cmp("", "");
+ assert(result == 0);
+ result = cmp("abc", "abcd");
+ assert(result < 0);
+ result = cmp("abcd", "abc");
+ assert(result > 0);
+ result = cmp("abc"d, "abd");
+ assert(result < 0);
+ result = cmp("bbc", "abc"w);
+ assert(result > 0);
+ result = cmp("aaa", "aaaa"d);
+ assert(result < 0);
+ result = cmp("aaaa", "aaa"d);
+ assert(result > 0);
+ result = cmp("aaa", "aaa"d);
+ assert(result == 0);
+ result = cmp(cast(int[])[], cast(int[])[]);
+ assert(result == 0);
+ result = cmp([1, 2, 3], [1, 2, 3]);
+ assert(result == 0);
+ result = cmp([1, 3, 2], [1, 2, 3]);
+ assert(result > 0);
+ result = cmp([1, 2, 3], [1L, 2, 3, 4]);
+ assert(result < 0);
+ result = cmp([1L, 2, 3], [1, 2]);
+ assert(result > 0);
+}
+
+// equal
+/**
+Compares two ranges for equality, as defined by predicate $(D pred)
+(which is $(D ==) by default).
+*/
+template equal(alias pred = "a == b")
+{
+ enum isEmptyRange(R) =
+ isInputRange!R && __traits(compiles, {static assert(R.empty);});
+
+ enum hasFixedLength(T) = hasLength!T || isNarrowString!T;
+
+ /++
+ Compares two ranges for equality. The ranges may have
+ different element types, as long as $(D pred(r1.front, r2.front))
+ evaluates to $(D bool).
+ Performs $(BIGOH min(r1.length, r2.length)) evaluations of $(D pred).
+
+ Params:
+ r1 = The first range to be compared.
+ r2 = The second range to be compared.
+
+ Returns:
+ $(D true) if and only if the two ranges compare _equal element
+ for element, according to binary predicate $(D pred).
+
+ See_Also:
+ $(HTTP sgi.com/tech/stl/_equal.html, STL's _equal)
+ +/
+ bool equal(Range1, Range2)(Range1 r1, Range2 r2)
+ if (isInputRange!Range1 && isInputRange!Range2 &&
+ is(typeof(binaryFun!pred(r1.front, r2.front))))
+ {
+ static assert(!(isInfinite!Range1 && isInfinite!Range2),
+ "Both ranges are known to be infinite");
+
+ //No pred calls necessary
+ static if (isEmptyRange!Range1 || isEmptyRange!Range2)
+ {
+ return r1.empty && r2.empty;
+ }
+ else static if ((isInfinite!Range1 && hasFixedLength!Range2) ||
+ (hasFixedLength!Range1 && isInfinite!Range2))
+ {
+ return false;
+ }
+ //Detect default pred and compatible dynamic array
+ else static if (is(typeof(pred) == string) && pred == "a == b" &&
+ isArray!Range1 && isArray!Range2 && is(typeof(r1 == r2)))
+ {
+ return r1 == r2;
+ }
+ // if one of the arguments is a string and the other isn't, then auto-decoding
+ // can be avoided if they have the same ElementEncodingType
+ else static if (is(typeof(pred) == string) && pred == "a == b" &&
+ isAutodecodableString!Range1 != isAutodecodableString!Range2 &&
+ is(ElementEncodingType!Range1 == ElementEncodingType!Range2))
+ {
+ import std.utf : byCodeUnit;
+
+ static if (isAutodecodableString!Range1)
+ {
+ return equal(r1.byCodeUnit, r2);
+ }
+ else
+ {
+ return equal(r2.byCodeUnit, r1);
+ }
+ }
+ //Try a fast implementation when the ranges have comparable lengths
+ else static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length)))
+ {
+ immutable len1 = r1.length;
+ immutable len2 = r2.length;
+ if (len1 != len2) return false; //Short circuit return
+
+ //Lengths are the same, so we need to do an actual comparison
+ //Good news is we can squeeze out a bit of performance by not checking if r2 is empty
+ for (; !r1.empty; r1.popFront(), r2.popFront())
+ {
+ if (!binaryFun!(pred)(r1.front, r2.front)) return false;
+ }
+ return true;
+ }
+ else
+ {
+ //Generic case, we have to walk both ranges making sure neither is empty
+ for (; !r1.empty; r1.popFront(), r2.popFront())
+ {
+ if (r2.empty) return false;
+ if (!binaryFun!(pred)(r1.front, r2.front)) return false;
+ }
+ static if (!isInfinite!Range1)
+ return r2.empty;
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.math : approxEqual;
+
+ int[] a = [ 1, 2, 4, 3 ];
+ assert(!equal(a, a[1..$]));
+ assert(equal(a, a));
+ assert(equal!((a, b) => a == b)(a, a));
+
+ // different types
+ double[] b = [ 1.0, 2, 4, 3];
+ assert(!equal(a, b[1..$]));
+ assert(equal(a, b));
+
+ // predicated: ensure that two vectors are approximately equal
+ double[] c = [ 1.005, 2, 4, 3];
+ assert(equal!approxEqual(b, c));
+}
+
+/++
+Tip: $(D equal) can itself be used as a predicate to other functions.
+This can be very useful when the element type of a range is itself a
+range. In particular, $(D equal) can be its own predicate, allowing
+range of range (of range...) comparisons.
+ +/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota, chunks;
+ assert(equal!(equal!equal)(
+ [[[0, 1], [2, 3]], [[4, 5], [6, 7]]],
+ iota(0, 8).chunks(2).chunks(2)
+ ));
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.internal.test.dummyrange : ReferenceForwardRange,
+ ReferenceInputRange, ReferenceInfiniteForwardRange;
+ import std.math : approxEqual;
+
+ // various strings
+ assert(equal("æøå", "æøå")); //UTF8 vs UTF8
+ assert(!equal("???", "æøå")); //UTF8 vs UTF8
+ assert(equal("æøå"w, "æøå"d)); //UTF16 vs UTF32
+ assert(!equal("???"w, "æøå"d));//UTF16 vs UTF32
+ assert(equal("æøå"d, "æøå"d)); //UTF32 vs UTF32
+ assert(!equal("???"d, "æøå"d));//UTF32 vs UTF32
+ assert(!equal("hello", "world"));
+
+ // same strings, but "explicit non default" comparison (to test the non optimized array comparison)
+ assert( equal!("a == b")("æøå", "æøå")); //UTF8 vs UTF8
+ assert(!equal!("a == b")("???", "æøå")); //UTF8 vs UTF8
+ assert( equal!("a == b")("æøå"w, "æøå"d)); //UTF16 vs UTF32
+ assert(!equal!("a == b")("???"w, "æøå"d));//UTF16 vs UTF32
+ assert( equal!("a == b")("æøå"d, "æøå"d)); //UTF32 vs UTF32
+ assert(!equal!("a == b")("???"d, "æøå"d));//UTF32 vs UTF32
+ assert(!equal!("a == b")("hello", "world"));
+
+ //Array of string
+ assert(equal(["hello", "world"], ["hello", "world"]));
+ assert(!equal(["hello", "world"], ["hello"]));
+ assert(!equal(["hello", "world"], ["hello", "Bob!"]));
+
+ //Should not compile, because "string == dstring" is illegal
+ static assert(!is(typeof(equal(["hello", "world"], ["hello"d, "world"d]))));
+ //However, arrays of non-matching string can be compared using equal!equal. Neat-o!
+ equal!equal(["hello", "world"], ["hello"d, "world"d]);
+
+ //Tests, with more fancy map ranges
+ int[] a = [ 1, 2, 4, 3 ];
+ assert(equal([2, 4, 8, 6], map!"a*2"(a)));
+ double[] b = [ 1.0, 2, 4, 3];
+ double[] c = [ 1.005, 2, 4, 3];
+ assert(equal!approxEqual(map!"a*2"(b), map!"a*2"(c)));
+ assert(!equal([2, 4, 1, 3], map!"a*2"(a)));
+ assert(!equal([2, 4, 1], map!"a*2"(a)));
+ assert(!equal!approxEqual(map!"a*3"(b), map!"a*2"(c)));
+
+ //Tests with some fancy reference ranges.
+ ReferenceInputRange!int cir = new ReferenceInputRange!int([1, 2, 4, 3]);
+ ReferenceForwardRange!int cfr = new ReferenceForwardRange!int([1, 2, 4, 3]);
+ assert(equal(cir, a));
+ cir = new ReferenceInputRange!int([1, 2, 4, 3]);
+ assert(equal(cir, cfr.save));
+ assert(equal(cfr.save, cfr.save));
+ cir = new ReferenceInputRange!int([1, 2, 8, 1]);
+ assert(!equal(cir, cfr));
+
+ //Test with an infinite range
+ auto ifr = new ReferenceInfiniteForwardRange!int;
+ assert(!equal(a, ifr));
+ assert(!equal(ifr, a));
+ //Test InputRange without length
+ assert(!equal(ifr, cir));
+ assert(!equal(cir, ifr));
+}
+
+@safe pure unittest
+{
+ import std.utf : byChar, byWchar, byDchar;
+
+ assert(equal("æøå".byChar, "æøå"));
+ assert(equal("æøå", "æøå".byChar));
+ assert(equal("æøå".byWchar, "æøå"w));
+ assert(equal("æøå"w, "æøå".byWchar));
+ assert(equal("æøå".byDchar, "æøå"d));
+ assert(equal("æøå"d, "æøå".byDchar));
+}
+
+@safe pure unittest
+{
+ struct R(bool _empty) {
+ enum empty = _empty;
+ @property char front(){assert(0);}
+ void popFront(){assert(0);}
+ }
+ alias I = R!false;
+ static assert(!__traits(compiles, I().equal(I())));
+ // strings have fixed length so don't need to compare elements
+ assert(!I().equal("foo"));
+ assert(!"bar".equal(I()));
+
+ alias E = R!true;
+ assert(E().equal(E()));
+ assert(E().equal(""));
+ assert("".equal(E()));
+ assert(!E().equal("foo"));
+ assert(!"bar".equal(E()));
+}
+
+// MaxType
+private template MaxType(T...)
+if (T.length >= 1)
+{
+ static if (T.length == 1)
+ {
+ alias MaxType = T[0];
+ }
+ else static if (T.length == 2)
+ {
+ static if (!is(typeof(T[0].min)))
+ alias MaxType = CommonType!T;
+ else static if (T[1].max > T[0].max)
+ alias MaxType = T[1];
+ else
+ alias MaxType = T[0];
+ }
+ else
+ {
+ alias MaxType = MaxType!(MaxType!(T[0 .. ($+1)/2]), MaxType!(T[($+1)/2 .. $]));
+ }
+}
+
+// levenshteinDistance
+/**
+Encodes $(HTTP realityinteractive.com/rgrzywinski/archives/000249.html,
+edit operations) necessary to transform one sequence into
+another. Given sequences $(D s) (source) and $(D t) (target), a
+sequence of $(D EditOp) encodes the steps that need to be taken to
+convert $(D s) into $(D t). For example, if $(D s = "cat") and $(D
+"cars"), the minimal sequence that transforms $(D s) into $(D t) is:
+skip two characters, replace 't' with 'r', and insert an 's'. Working
+with edit operations is useful in applications such as spell-checkers
+(to find the closest word to a given misspelled word), approximate
+searches, diff-style programs that compute the difference between
+files, efficient encoding of patches, DNA sequence analysis, and
+plagiarism detection.
+*/
+
+enum EditOp : char
+{
+ /** Current items are equal; no editing is necessary. */
+ none = 'n',
+ /** Substitute current item in target with current item in source. */
+ substitute = 's',
+ /** Insert current item from the source into the target. */
+ insert = 'i',
+ /** Remove current item from the target. */
+ remove = 'r'
+}
+
+///
+@safe unittest
+{
+ with(EditOp)
+ {
+ assert(levenshteinDistanceAndPath("foo", "foobar")[1] == [none, none, none, insert, insert, insert]);
+ assert(levenshteinDistanceAndPath("banana", "fazan")[1] == [substitute, none, substitute, none, none, remove]);
+ }
+}
+
+private struct Levenshtein(Range, alias equals, CostType = size_t)
+{
+ EditOp[] path()
+ {
+ import std.algorithm.mutation : reverse;
+
+ EditOp[] result;
+ size_t i = rows - 1, j = cols - 1;
+ // restore the path
+ while (i || j)
+ {
+ auto cIns = j == 0 ? CostType.max : matrix(i,j - 1);
+ auto cDel = i == 0 ? CostType.max : matrix(i - 1,j);
+ auto cSub = i == 0 || j == 0
+ ? CostType.max
+ : matrix(i - 1,j - 1);
+ switch (min_index(cSub, cIns, cDel))
+ {
+ case 0:
+ result ~= matrix(i - 1,j - 1) == matrix(i,j)
+ ? EditOp.none
+ : EditOp.substitute;
+ --i;
+ --j;
+ break;
+ case 1:
+ result ~= EditOp.insert;
+ --j;
+ break;
+ default:
+ result ~= EditOp.remove;
+ --i;
+ break;
+ }
+ }
+ reverse(result);
+ return result;
+ }
+
+ ~this() {
+ FreeMatrix();
+ }
+
+private:
+ CostType _deletionIncrement = 1,
+ _insertionIncrement = 1,
+ _substitutionIncrement = 1;
+ CostType[] _matrix;
+ size_t rows, cols;
+
+ // Treat _matrix as a rectangular array
+ ref CostType matrix(size_t row, size_t col) { return _matrix[row * cols + col]; }
+
+ void AllocMatrix(size_t r, size_t c) @trusted {
+ import core.checkedint : mulu;
+ bool overflow;
+ const rc = mulu(r, c, overflow);
+ if (overflow) assert(0);
+ rows = r;
+ cols = c;
+ if (_matrix.length < rc)
+ {
+ import core.exception : onOutOfMemoryError;
+ import core.stdc.stdlib : realloc;
+ const nbytes = mulu(rc, _matrix[0].sizeof, overflow);
+ if (overflow) assert(0);
+ auto m = cast(CostType *) realloc(_matrix.ptr, nbytes);
+ if (!m)
+ onOutOfMemoryError();
+ _matrix = m[0 .. r * c];
+ InitMatrix();
+ }
+ }
+
+ void FreeMatrix() @trusted {
+ import core.stdc.stdlib : free;
+
+ free(_matrix.ptr);
+ _matrix = null;
+ }
+
+ void InitMatrix() {
+ foreach (r; 0 .. rows)
+ matrix(r,0) = r * _deletionIncrement;
+ foreach (c; 0 .. cols)
+ matrix(0,c) = c * _insertionIncrement;
+ }
+
+ static uint min_index(CostType i0, CostType i1, CostType i2)
+ {
+ if (i0 <= i1)
+ {
+ return i0 <= i2 ? 0 : 2;
+ }
+ else
+ {
+ return i1 <= i2 ? 1 : 2;
+ }
+ }
+
+ CostType distanceWithPath(Range s, Range t)
+ {
+ auto slen = walkLength(s.save), tlen = walkLength(t.save);
+ AllocMatrix(slen + 1, tlen + 1);
+ foreach (i; 1 .. rows)
+ {
+ auto sfront = s.front;
+ auto tt = t.save;
+ foreach (j; 1 .. cols)
+ {
+ auto cSub = matrix(i - 1,j - 1)
+ + (equals(sfront, tt.front) ? 0 : _substitutionIncrement);
+ tt.popFront();
+ auto cIns = matrix(i,j - 1) + _insertionIncrement;
+ auto cDel = matrix(i - 1,j) + _deletionIncrement;
+ switch (min_index(cSub, cIns, cDel))
+ {
+ case 0:
+ matrix(i,j) = cSub;
+ break;
+ case 1:
+ matrix(i,j) = cIns;
+ break;
+ default:
+ matrix(i,j) = cDel;
+ break;
+ }
+ }
+ s.popFront();
+ }
+ return matrix(slen,tlen);
+ }
+
+ CostType distanceLowMem(Range s, Range t, CostType slen, CostType tlen)
+ {
+ CostType lastdiag, olddiag;
+ AllocMatrix(slen + 1, 1);
+ foreach (y; 1 .. slen + 1)
+ {
+ _matrix[y] = y;
+ }
+ foreach (x; 1 .. tlen + 1)
+ {
+ auto tfront = t.front;
+ auto ss = s.save;
+ _matrix[0] = x;
+ lastdiag = x - 1;
+ foreach (y; 1 .. rows)
+ {
+ olddiag = _matrix[y];
+ auto cSub = lastdiag + (equals(ss.front, tfront) ? 0 : _substitutionIncrement);
+ ss.popFront();
+ auto cIns = _matrix[y - 1] + _insertionIncrement;
+ auto cDel = _matrix[y] + _deletionIncrement;
+ switch (min_index(cSub, cIns, cDel))
+ {
+ case 0:
+ _matrix[y] = cSub;
+ break;
+ case 1:
+ _matrix[y] = cIns;
+ break;
+ default:
+ _matrix[y] = cDel;
+ break;
+ }
+ lastdiag = olddiag;
+ }
+ t.popFront();
+ }
+ return _matrix[slen];
+ }
+}
+
+/**
+Returns the $(HTTP wikipedia.org/wiki/Levenshtein_distance, Levenshtein
+distance) between $(D s) and $(D t). The Levenshtein distance computes
+the minimal amount of edit operations necessary to transform $(D s)
+into $(D t). Performs $(BIGOH s.length * t.length) evaluations of $(D
+equals) and occupies $(BIGOH s.length * t.length) storage.
+
+Params:
+ equals = The binary predicate to compare the elements of the two ranges.
+ s = The original range.
+ t = The transformation target
+
+Returns:
+ The minimal number of edits to transform s into t.
+
+Does not allocate GC memory.
+*/
+size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2)
+ (Range1 s, Range2 t)
+if (isForwardRange!(Range1) && isForwardRange!(Range2))
+{
+ alias eq = binaryFun!(equals);
+
+ for (;;)
+ {
+ if (s.empty) return t.walkLength;
+ if (t.empty) return s.walkLength;
+ if (eq(s.front, t.front))
+ {
+ s.popFront();
+ t.popFront();
+ continue;
+ }
+ static if (isBidirectionalRange!(Range1) && isBidirectionalRange!(Range2))
+ {
+ if (eq(s.back, t.back))
+ {
+ s.popBack();
+ t.popBack();
+ continue;
+ }
+ }
+ break;
+ }
+
+ auto slen = walkLength(s.save);
+ auto tlen = walkLength(t.save);
+
+ if (slen == 1 && tlen == 1)
+ {
+ return eq(s.front, t.front) ? 0 : 1;
+ }
+
+ if (slen > tlen)
+ {
+ Levenshtein!(Range1, eq, size_t) lev;
+ return lev.distanceLowMem(s, t, slen, tlen);
+ }
+ else
+ {
+ Levenshtein!(Range2, eq, size_t) lev;
+ return lev.distanceLowMem(t, s, tlen, slen);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.uni : toUpper;
+
+ assert(levenshteinDistance("cat", "rat") == 1);
+ assert(levenshteinDistance("parks", "spark") == 2);
+ assert(levenshteinDistance("abcde", "abcde") == 0);
+ assert(levenshteinDistance("abcde", "abCde") == 1);
+ assert(levenshteinDistance("kitten", "sitting") == 3);
+ assert(levenshteinDistance!((a, b) => toUpper(a) == toUpper(b))
+ ("parks", "SPARK") == 2);
+ assert(levenshteinDistance("parks".filter!"true", "spark".filter!"true") == 2);
+ assert(levenshteinDistance("ID", "I♥D") == 1);
+}
+
+@safe @nogc nothrow unittest
+{
+ assert(levenshteinDistance("cat"d, "rat"d) == 1);
+}
+
+/// ditto
+size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2)
+ (auto ref Range1 s, auto ref Range2 t)
+if (isConvertibleToString!Range1 || isConvertibleToString!Range2)
+{
+ import std.meta : staticMap;
+ alias Types = staticMap!(convertToString, Range1, Range2);
+ return levenshteinDistance!(equals, Types)(s, t);
+}
+
+@safe unittest
+{
+ static struct S { string s; alias s this; }
+ assert(levenshteinDistance(S("cat"), S("rat")) == 1);
+ assert(levenshteinDistance("cat", S("rat")) == 1);
+ assert(levenshteinDistance(S("cat"), "rat") == 1);
+}
+
+@safe @nogc nothrow unittest
+{
+ static struct S { dstring s; alias s this; }
+ assert(levenshteinDistance(S("cat"d), S("rat"d)) == 1);
+ assert(levenshteinDistance("cat"d, S("rat"d)) == 1);
+ assert(levenshteinDistance(S("cat"d), "rat"d) == 1);
+}
+
+/**
+Returns the Levenshtein distance and the edit path between $(D s) and
+$(D t).
+
+Params:
+ equals = The binary predicate to compare the elements of the two ranges.
+ s = The original range.
+ t = The transformation target
+
+Returns:
+ Tuple with the first element being the minimal amount of edits to transform s into t and
+ the second element being the sequence of edits to effect this transformation.
+
+Allocates GC memory for the returned EditOp[] array.
+*/
+Tuple!(size_t, EditOp[])
+levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2)
+ (Range1 s, Range2 t)
+if (isForwardRange!(Range1) && isForwardRange!(Range2))
+{
+ Levenshtein!(Range1, binaryFun!(equals)) lev;
+ auto d = lev.distanceWithPath(s, t);
+ return tuple(d, lev.path());
+}
+
+///
+@safe unittest
+{
+ string a = "Saturday", b = "Sundays";
+ auto p = levenshteinDistanceAndPath(a, b);
+ assert(p[0] == 4);
+ assert(equal(p[1], "nrrnsnnni"));
+}
+
+@safe unittest
+{
+ assert(levenshteinDistance("a", "a") == 0);
+ assert(levenshteinDistance("a", "b") == 1);
+ assert(levenshteinDistance("aa", "ab") == 1);
+ assert(levenshteinDistance("aa", "abc") == 2);
+ assert(levenshteinDistance("Saturday", "Sunday") == 3);
+ assert(levenshteinDistance("kitten", "sitting") == 3);
+}
+
+/// ditto
+Tuple!(size_t, EditOp[])
+levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2)
+ (auto ref Range1 s, auto ref Range2 t)
+if (isConvertibleToString!Range1 || isConvertibleToString!Range2)
+{
+ import std.meta : staticMap;
+ alias Types = staticMap!(convertToString, Range1, Range2);
+ return levenshteinDistanceAndPath!(equals, Types)(s, t);
+}
+
+@safe unittest
+{
+ static struct S { string s; alias s this; }
+ assert(levenshteinDistanceAndPath(S("cat"), S("rat"))[0] == 1);
+ assert(levenshteinDistanceAndPath("cat", S("rat"))[0] == 1);
+ assert(levenshteinDistanceAndPath(S("cat"), "rat")[0] == 1);
+}
+
+// max
+/**
+Iterates the passed arguments and return the maximum value.
+
+Params:
+ args = The values to select the maximum from. At least two arguments must
+ be passed.
+
+Returns:
+ The maximum of the passed-in args. The type of the returned value is
+ the type among the passed arguments that is able to store the largest value.
+
+See_Also:
+ $(REF maxElement, std,algorithm,searching)
+*/
+MaxType!T max(T...)(T args)
+if (T.length >= 2)
+{
+ //Get "a"
+ static if (T.length <= 2)
+ alias a = args[0];
+ else
+ auto a = max(args[0 .. ($+1)/2]);
+ alias T0 = typeof(a);
+
+ //Get "b"
+ static if (T.length <= 3)
+ alias b = args[$-1];
+ else
+ auto b = max(args[($+1)/2 .. $]);
+ alias T1 = typeof(b);
+
+ import std.algorithm.internal : algoFormat;
+ static assert(is(typeof(a < b)),
+ algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof));
+
+ //Do the "max" proper with a and b
+ import std.functional : lessThan;
+ immutable chooseB = lessThan!(T0, T1)(a, b);
+ return cast(typeof(return)) (chooseB ? b : a);
+}
+
+///
+@safe unittest
+{
+ int a = 5;
+ short b = 6;
+ double c = 2;
+ auto d = max(a, b);
+ assert(is(typeof(d) == int));
+ assert(d == 6);
+ auto e = min(a, b, c);
+ assert(is(typeof(e) == double));
+ assert(e == 2);
+}
+
+@safe unittest
+{
+ int a = 5;
+ short b = 6;
+ double c = 2;
+ auto d = max(a, b);
+ static assert(is(typeof(d) == int));
+ assert(d == 6);
+ auto e = max(a, b, c);
+ static assert(is(typeof(e) == double));
+ assert(e == 6);
+ // mixed sign
+ a = -5;
+ uint f = 5;
+ static assert(is(typeof(max(a, f)) == uint));
+ assert(max(a, f) == 5);
+
+ //Test user-defined types
+ import std.datetime : Date;
+ assert(max(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(2012, 12, 21));
+ assert(max(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(2012, 12, 21));
+ assert(max(Date(1982, 1, 4), Date.min) == Date(1982, 1, 4));
+ assert(max(Date.min, Date(1982, 1, 4)) == Date(1982, 1, 4));
+ assert(max(Date(1982, 1, 4), Date.max) == Date.max);
+ assert(max(Date.max, Date(1982, 1, 4)) == Date.max);
+ assert(max(Date.min, Date.max) == Date.max);
+ assert(max(Date.max, Date.min) == Date.max);
+}
+
+// MinType
+private template MinType(T...)
+if (T.length >= 1)
+{
+ static if (T.length == 1)
+ {
+ alias MinType = T[0];
+ }
+ else static if (T.length == 2)
+ {
+ static if (!is(typeof(T[0].min)))
+ alias MinType = CommonType!T;
+ else
+ {
+ enum hasMostNegative = is(typeof(mostNegative!(T[0]))) &&
+ is(typeof(mostNegative!(T[1])));
+ static if (hasMostNegative && mostNegative!(T[1]) < mostNegative!(T[0]))
+ alias MinType = T[1];
+ else static if (hasMostNegative && mostNegative!(T[1]) > mostNegative!(T[0]))
+ alias MinType = T[0];
+ else static if (T[1].max < T[0].max)
+ alias MinType = T[1];
+ else
+ alias MinType = T[0];
+ }
+ }
+ else
+ {
+ alias MinType = MinType!(MinType!(T[0 .. ($+1)/2]), MinType!(T[($+1)/2 .. $]));
+ }
+}
+
+// min
+/**
+Iterates the passed arguments and returns the minimum value.
+
+Params: args = The values to select the minimum from. At least two arguments
+ must be passed, and they must be comparable with `<`.
+Returns: The minimum of the passed-in values.
+See_Also:
+ $(REF minElement, std,algorithm,searching)
+*/
+MinType!T min(T...)(T args)
+if (T.length >= 2)
+{
+ //Get "a"
+ static if (T.length <= 2)
+ alias a = args[0];
+ else
+ auto a = min(args[0 .. ($+1)/2]);
+ alias T0 = typeof(a);
+
+ //Get "b"
+ static if (T.length <= 3)
+ alias b = args[$-1];
+ else
+ auto b = min(args[($+1)/2 .. $]);
+ alias T1 = typeof(b);
+
+ import std.algorithm.internal : algoFormat;
+ static assert(is(typeof(a < b)),
+ algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof));
+
+ //Do the "min" proper with a and b
+ import std.functional : lessThan;
+ immutable chooseA = lessThan!(T0, T1)(a, b);
+ return cast(typeof(return)) (chooseA ? a : b);
+}
+
+///
+@safe unittest
+{
+ int a = 5;
+ short b = 6;
+ double c = 2;
+ auto d = min(a, b);
+ static assert(is(typeof(d) == int));
+ assert(d == 5);
+ auto e = min(a, b, c);
+ static assert(is(typeof(e) == double));
+ assert(e == 2);
+
+ // With arguments of mixed signedness, the return type is the one that can
+ // store the lowest values.
+ a = -10;
+ uint f = 10;
+ static assert(is(typeof(min(a, f)) == int));
+ assert(min(a, f) == -10);
+
+ // User-defined types that support comparison with < are supported.
+ import std.datetime;
+ assert(min(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(1982, 1, 4));
+ assert(min(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(1982, 1, 4));
+ assert(min(Date(1982, 1, 4), Date.min) == Date.min);
+ assert(min(Date.min, Date(1982, 1, 4)) == Date.min);
+ assert(min(Date(1982, 1, 4), Date.max) == Date(1982, 1, 4));
+ assert(min(Date.max, Date(1982, 1, 4)) == Date(1982, 1, 4));
+ assert(min(Date.min, Date.max) == Date.min);
+ assert(min(Date.max, Date.min) == Date.min);
+}
+
+// mismatch
+/**
+Sequentially compares elements in $(D r1) and $(D r2) in lockstep, and
+stops at the first mismatch (according to $(D pred), by default
+equality). Returns a tuple with the reduced ranges that start with the
+two mismatched values. Performs $(BIGOH min(r1.length, r2.length))
+evaluations of $(D pred).
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/_mismatch.html, STL's _mismatch)
+*/
+Tuple!(Range1, Range2)
+mismatch(alias pred = "a == b", Range1, Range2)(Range1 r1, Range2 r2)
+if (isInputRange!(Range1) && isInputRange!(Range2))
+{
+ for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront())
+ {
+ if (!binaryFun!(pred)(r1.front, r2.front)) break;
+ }
+ return tuple(r1, r2);
+}
+
+///
+@safe unittest
+{
+ int[] x = [ 1, 5, 2, 7, 4, 3 ];
+ double[] y = [ 1.0, 5, 2, 7.3, 4, 8 ];
+ auto m = mismatch(x, y);
+ assert(m[0] == x[3 .. $]);
+ assert(m[1] == y[3 .. $]);
+}
+
+@safe unittest
+{
+ int[] a = [ 1, 2, 3 ];
+ int[] b = [ 1, 2, 4, 5 ];
+ auto mm = mismatch(a, b);
+ assert(mm[0] == [3]);
+ assert(mm[1] == [4, 5]);
+}
+
+/**
+Returns one of a collection of expressions based on the value of the switch
+expression.
+
+$(D choices) needs to be composed of pairs of test expressions and return
+expressions. Each test-expression is compared with $(D switchExpression) using
+$(D pred)($(D switchExpression) is the first argument) and if that yields true
+- the return expression is returned.
+
+Both the test and the return expressions are lazily evaluated.
+
+Params:
+
+switchExpression = The first argument for the predicate.
+
+choices = Pairs of test expressions and return expressions. The test
+expressions will be the second argument for the predicate, and the return
+expression will be returned if the predicate yields true with $(D
+switchExpression) and the test expression as arguments. May also have a
+default return expression, that needs to be the last expression without a test
+expression before it. A return expression may be of void type only if it
+always throws.
+
+Returns: The return expression associated with the first test expression that
+made the predicate yield true, or the default return expression if no test
+expression matched.
+
+Throws: If there is no default return expression and the predicate does not
+yield true with any test expression - $(D SwitchError) is thrown. $(D
+SwitchError) is also thrown if a void return expression was executed without
+throwing anything.
+*/
+auto predSwitch(alias pred = "a == b", T, R ...)(T switchExpression, lazy R choices)
+{
+ import core.exception : SwitchError;
+ alias predicate = binaryFun!(pred);
+
+ foreach (index, ChoiceType; R)
+ {
+ //The even places in `choices` are for the predicate.
+ static if (index % 2 == 1)
+ {
+ if (predicate(switchExpression, choices[index - 1]()))
+ {
+ static if (is(typeof(choices[index]()) == void))
+ {
+ choices[index]();
+ throw new SwitchError("Choices that return void should throw");
+ }
+ else
+ {
+ return choices[index]();
+ }
+ }
+ }
+ }
+
+ //In case nothing matched:
+ static if (R.length % 2 == 1) //If there is a default return expression:
+ {
+ static if (is(typeof(choices[$ - 1]()) == void))
+ {
+ choices[$ - 1]();
+ throw new SwitchError("Choices that return void should throw");
+ }
+ else
+ {
+ return choices[$ - 1]();
+ }
+ }
+ else //If there is no default return expression:
+ {
+ throw new SwitchError("Input not matched by any pattern");
+ }
+}
+
+///
+@safe unittest
+{
+ string res = 2.predSwitch!"a < b"(
+ 1, "less than 1",
+ 5, "less than 5",
+ 10, "less than 10",
+ "greater or equal to 10");
+
+ assert(res == "less than 5");
+
+ //The arguments are lazy, which allows us to use predSwitch to create
+ //recursive functions:
+ int factorial(int n)
+ {
+ return n.predSwitch!"a <= b"(
+ -1, {throw new Exception("Can not calculate n! for n < 0");}(),
+ 0, 1, // 0! = 1
+ n * factorial(n - 1) // n! = n * (n - 1)! for n >= 0
+ );
+ }
+ assert(factorial(3) == 6);
+
+ //Void return expressions are allowed if they always throw:
+ import std.exception : assertThrown;
+ assertThrown!Exception(factorial(-9));
+}
+
+@system unittest
+{
+ import core.exception : SwitchError;
+ import std.exception : assertThrown;
+
+ //Nothing matches - with default return expression:
+ assert(20.predSwitch!"a < b"(
+ 1, "less than 1",
+ 5, "less than 5",
+ 10, "less than 10",
+ "greater or equal to 10") == "greater or equal to 10");
+
+ //Nothing matches - without default return expression:
+ assertThrown!SwitchError(20.predSwitch!"a < b"(
+ 1, "less than 1",
+ 5, "less than 5",
+ 10, "less than 10",
+ ));
+
+ //Using the default predicate:
+ assert(2.predSwitch(
+ 1, "one",
+ 2, "two",
+ 3, "three",
+ ) == "two");
+
+ //Void return expressions must always throw:
+ assertThrown!SwitchError(1.predSwitch(
+ 0, "zero",
+ 1, {}(), //A void return expression that doesn't throw
+ 2, "two",
+ ));
+}
+
+/**
+Checks if the two ranges have the same number of elements. This function is
+optimized to always take advantage of the $(D length) member of either range
+if it exists.
+
+If both ranges have a length member, this function is $(BIGOH 1). Otherwise,
+this function is $(BIGOH min(r1.length, r2.length)).
+
+Params:
+ r1 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ r2 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+
+Returns:
+ $(D true) if both ranges have the same length, $(D false) otherwise.
+*/
+bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2)
+if (isInputRange!Range1 &&
+ isInputRange!Range2 &&
+ !isInfinite!Range1 &&
+ !isInfinite!Range2)
+{
+ static if (hasLength!(Range1) && hasLength!(Range2))
+ {
+ return r1.length == r2.length;
+ }
+ else static if (hasLength!(Range1) && !hasLength!(Range2))
+ {
+ size_t length;
+
+ while (!r2.empty)
+ {
+ r2.popFront;
+
+ if (++length > r1.length)
+ {
+ return false;
+ }
+ }
+
+ return !(length < r1.length);
+ }
+ else static if (!hasLength!(Range1) && hasLength!(Range2))
+ {
+ size_t length;
+
+ while (!r1.empty)
+ {
+ r1.popFront;
+
+ if (++length > r2.length)
+ {
+ return false;
+ }
+ }
+
+ return !(length < r2.length);
+ }
+ else
+ {
+ while (!r1.empty)
+ {
+ if (r2.empty)
+ {
+ return false;
+ }
+
+ r1.popFront;
+ r2.popFront;
+ }
+
+ return r2.empty;
+ }
+}
+
+///
+@safe nothrow pure unittest
+{
+ assert(isSameLength([1, 2, 3], [4, 5, 6]));
+ assert(isSameLength([0.3, 90.4, 23.7, 119.2], [42.6, 23.6, 95.5, 6.3]));
+ assert(isSameLength("abc", "xyz"));
+
+ int[] a;
+ int[] b;
+ assert(isSameLength(a, b));
+
+ assert(!isSameLength([1, 2, 3], [4, 5]));
+ assert(!isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3]));
+ assert(!isSameLength("abcd", "xyz"));
+}
+
+// Test CTFE
+@safe pure unittest
+{
+ enum result1 = isSameLength([1, 2, 3], [4, 5, 6]);
+ static assert(result1);
+
+ enum result2 = isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3]);
+ static assert(!result2);
+}
+
+@safe nothrow pure unittest
+{
+ import std.internal.test.dummyrange;
+
+ auto r1 = new ReferenceInputRange!int([1, 2, 3]);
+ auto r2 = new ReferenceInputRange!int([4, 5, 6]);
+ assert(isSameLength(r1, r2));
+
+ auto r3 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r4;
+ assert(isSameLength(r3, r4));
+
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r5;
+ auto r6 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+ assert(isSameLength(r5, r6));
+
+ auto r7 = new ReferenceInputRange!int([1, 2]);
+ auto r8 = new ReferenceInputRange!int([4, 5, 6]);
+ assert(!isSameLength(r7, r8));
+
+ auto r9 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]);
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r10;
+ assert(!isSameLength(r9, r10));
+
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r11;
+ auto r12 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]);
+ assert(!isSameLength(r11, r12));
+}
+
+/// For convenience
+alias AllocateGC = Flag!"allocateGC";
+
+/**
+Checks if both ranges are permutations of each other.
+
+This function can allocate if the $(D Yes.allocateGC) flag is passed. This has
+the benefit of have better complexity than the $(D Yes.allocateGC) option. However,
+this option is only available for ranges whose equality can be determined via each
+element's $(D toHash) method. If customized equality is needed, then the $(D pred)
+template parameter can be passed, and the function will automatically switch to
+the non-allocating algorithm. See $(REF binaryFun, std,functional) for more details on
+how to define $(D pred).
+
+Non-allocating forward range option: $(BIGOH n^2)
+Non-allocating forward range option with custom $(D pred): $(BIGOH n^2)
+Allocating forward range option: amortized $(BIGOH r1.length) + $(BIGOH r2.length)
+
+Params:
+ pred = an optional parameter to change how equality is defined
+ allocate_gc = $(D Yes.allocateGC)/$(D No.allocateGC)
+ r1 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ r2 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+
+Returns:
+ $(D true) if all of the elements in $(D r1) appear the same number of times in $(D r2).
+ Otherwise, returns $(D false).
+*/
+
+bool isPermutation(AllocateGC allocate_gc, Range1, Range2)
+(Range1 r1, Range2 r2)
+if (allocate_gc == Yes.allocateGC &&
+ isForwardRange!Range1 &&
+ isForwardRange!Range2 &&
+ !isInfinite!Range1 &&
+ !isInfinite!Range2)
+{
+ alias E1 = Unqual!(ElementType!Range1);
+ alias E2 = Unqual!(ElementType!Range2);
+
+ if (!isSameLength(r1.save, r2.save))
+ {
+ return false;
+ }
+
+ // Skip the elements at the beginning where r1.front == r2.front,
+ // they are in the same order and don't need to be counted.
+ while (!r1.empty && !r2.empty && r1.front == r2.front)
+ {
+ r1.popFront();
+ r2.popFront();
+ }
+
+ if (r1.empty && r2.empty)
+ {
+ return true;
+ }
+
+ int[CommonType!(E1, E2)] counts;
+
+ foreach (item; r1)
+ {
+ ++counts[item];
+ }
+
+ foreach (item; r2)
+ {
+ if (--counts[item] < 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/// ditto
+bool isPermutation(alias pred = "a == b", Range1, Range2)
+(Range1 r1, Range2 r2)
+if (is(typeof(binaryFun!(pred))) &&
+ isForwardRange!Range1 &&
+ isForwardRange!Range2 &&
+ !isInfinite!Range1 &&
+ !isInfinite!Range2)
+{
+ import std.algorithm.searching : count;
+
+ alias predEquals = binaryFun!(pred);
+ alias E1 = Unqual!(ElementType!Range1);
+ alias E2 = Unqual!(ElementType!Range2);
+
+ if (!isSameLength(r1.save, r2.save))
+ {
+ return false;
+ }
+
+ // Skip the elements at the beginning where r1.front == r2.front,
+ // they are in the same order and don't need to be counted.
+ while (!r1.empty && !r2.empty && predEquals(r1.front, r2.front))
+ {
+ r1.popFront();
+ r2.popFront();
+ }
+
+ if (r1.empty && r2.empty)
+ {
+ return true;
+ }
+
+ size_t r1_count;
+ size_t r2_count;
+
+ // At each element item, when computing the count of item, scan it while
+ // also keeping track of the scanning index. If the first occurrence
+ // of item in the scanning loop has an index smaller than the current index,
+ // then you know that the element has been seen before
+ size_t index;
+ outloop: for (auto r1s1 = r1.save; !r1s1.empty; r1s1.popFront, index++)
+ {
+ auto item = r1s1.front;
+ r1_count = 0;
+ r2_count = 0;
+
+ size_t i;
+ for (auto r1s2 = r1.save; !r1s2.empty; r1s2.popFront, i++)
+ {
+ auto e = r1s2.front;
+ if (predEquals(e, item) && i < index)
+ {
+ continue outloop;
+ }
+ else if (predEquals(e, item))
+ {
+ ++r1_count;
+ }
+ }
+
+ r2_count = r2.save.count!pred(item);
+
+ if (r1_count != r2_count)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+///
+@safe pure unittest
+{
+ import std.typecons : Yes;
+
+ assert(isPermutation([1, 2, 3], [3, 2, 1]));
+ assert(isPermutation([1.1, 2.3, 3.5], [2.3, 3.5, 1.1]));
+ assert(isPermutation("abc", "bca"));
+
+ assert(!isPermutation([1, 2], [3, 4]));
+ assert(!isPermutation([1, 1, 2, 3], [1, 2, 2, 3]));
+ assert(!isPermutation([1, 1], [1, 1, 1]));
+
+ // Faster, but allocates GC handled memory
+ assert(isPermutation!(Yes.allocateGC)([1.1, 2.3, 3.5], [2.3, 3.5, 1.1]));
+ assert(!isPermutation!(Yes.allocateGC)([1, 2], [3, 4]));
+}
+
+// Test @nogc inference
+@safe @nogc pure unittest
+{
+ static immutable arr1 = [1, 2, 3];
+ static immutable arr2 = [3, 2, 1];
+ assert(isPermutation(arr1, arr2));
+
+ static immutable arr3 = [1, 1, 2, 3];
+ static immutable arr4 = [1, 2, 2, 3];
+ assert(!isPermutation(arr3, arr4));
+}
+
+@safe pure unittest
+{
+ import std.internal.test.dummyrange;
+
+ auto r1 = new ReferenceForwardRange!int([1, 2, 3, 4]);
+ auto r2 = new ReferenceForwardRange!int([1, 2, 4, 3]);
+ assert(isPermutation(r1, r2));
+
+ auto r3 = new ReferenceForwardRange!int([1, 2, 3, 4]);
+ auto r4 = new ReferenceForwardRange!int([4, 2, 1, 3]);
+ assert(isPermutation!(Yes.allocateGC)(r3, r4));
+
+ auto r5 = new ReferenceForwardRange!int([1, 2, 3]);
+ auto r6 = new ReferenceForwardRange!int([4, 2, 1, 3]);
+ assert(!isPermutation(r5, r6));
+
+ auto r7 = new ReferenceForwardRange!int([4, 2, 1, 3]);
+ auto r8 = new ReferenceForwardRange!int([1, 2, 3]);
+ assert(!isPermutation!(Yes.allocateGC)(r7, r8));
+
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r9;
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r10;
+ assert(isPermutation(r9, r10));
+
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r11;
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r12;
+ assert(isPermutation!(Yes.allocateGC)(r11, r12));
+
+ alias mytuple = Tuple!(int, int);
+
+ assert(isPermutation!"a[0] == b[0]"(
+ [mytuple(1, 4), mytuple(2, 5)],
+ [mytuple(2, 3), mytuple(1, 2)]
+ ));
+}
+
+/**
+Get the _first argument `a` that passes an `if (unaryFun!pred(a))` test. If
+no argument passes the test, return the last argument.
+
+Similar to behaviour of the `or` operator in dynamic languages such as Lisp's
+`(or ...)` and Python's `a or b or ...` except that the last argument is
+returned upon no match.
+
+Simplifies logic, for instance, in parsing rules where a set of alternative
+matchers are tried. The _first one that matches returns it match result,
+typically as an abstract syntax tree (AST).
+
+Bugs:
+Lazy parameters are currently, too restrictively, inferred by DMD to
+always throw even though they don't need to be. This makes it impossible to
+currently mark `either` as `nothrow`. See issue at $(BUGZILLA 12647).
+
+Returns:
+ The _first argument that passes the test `pred`.
+*/
+CommonType!(T, Ts) either(alias pred = a => a, T, Ts...)(T first, lazy Ts alternatives)
+if (alternatives.length >= 1 &&
+ !is(CommonType!(T, Ts) == void) &&
+ allSatisfy!(ifTestable, T, Ts))
+{
+ alias predFun = unaryFun!pred;
+
+ if (predFun(first)) return first;
+
+ foreach (e; alternatives[0 .. $ - 1])
+ if (predFun(e)) return e;
+
+ return alternatives[$ - 1];
+}
+
+///
+@safe pure unittest
+{
+ const a = 1;
+ const b = 2;
+ auto ab = either(a, b);
+ static assert(is(typeof(ab) == const(int)));
+ assert(ab == a);
+
+ auto c = 2;
+ const d = 3;
+ auto cd = either!(a => a == 3)(c, d); // use predicate
+ static assert(is(typeof(cd) == int));
+ assert(cd == d);
+
+ auto e = 0;
+ const f = 2;
+ auto ef = either(e, f);
+ static assert(is(typeof(ef) == int));
+ assert(ef == f);
+
+ immutable p = 1;
+ immutable q = 2;
+ auto pq = either(p, q);
+ static assert(is(typeof(pq) == immutable(int)));
+ assert(pq == p);
+
+ assert(either(3, 4) == 3);
+ assert(either(0, 4) == 4);
+ assert(either(0, 0) == 0);
+ assert(either("", "a") == "");
+
+ string r = null;
+ assert(either(r, "a") == "a");
+ assert(either("a", "") == "a");
+
+ immutable s = [1, 2];
+ assert(either(s, s) == s);
+
+ assert(either([0, 1], [1, 2]) == [0, 1]);
+ assert(either([0, 1], [1]) == [0, 1]);
+ assert(either("a", "b") == "a");
+
+ static assert(!__traits(compiles, either(1, "a")));
+ static assert(!__traits(compiles, either(1.0, "a")));
+ static assert(!__traits(compiles, either('a', "a")));
+}
diff --git a/libphobos/src/std/algorithm/internal.d b/libphobos/src/std/algorithm/internal.d
new file mode 100644
index 0000000..ca03fd7
--- /dev/null
+++ b/libphobos/src/std/algorithm/internal.d
@@ -0,0 +1,77 @@
+// Written in the D programming language.
+
+/// Helper functions for std.algorithm package.
+module std.algorithm.internal;
+
+
+// Same as std.string.format, but "self-importing".
+// Helps reduce code and imports, particularly in static asserts.
+// Also helps with missing imports errors.
+package template algoFormat()
+{
+ import std.format : format;
+ alias algoFormat = format;
+}
+
+// Internal random array generators
+version (unittest)
+{
+ package enum size_t maxArraySize = 50;
+ package enum size_t minArraySize = maxArraySize - 1;
+
+ package string[] rndstuff(T : string)()
+ {
+ import std.random : Random, unpredictableSeed, uniform;
+
+ static Random rnd;
+ static bool first = true;
+ if (first)
+ {
+ rnd = Random(unpredictableSeed);
+ first = false;
+ }
+ string[] result =
+ new string[uniform(minArraySize, maxArraySize, rnd)];
+ string alpha = "abcdefghijABCDEFGHIJ";
+ foreach (ref s; result)
+ {
+ foreach (i; 0 .. uniform(0u, 20u, rnd))
+ {
+ auto j = uniform(0, alpha.length - 1, rnd);
+ s ~= alpha[j];
+ }
+ }
+ return result;
+ }
+
+ package int[] rndstuff(T : int)()
+ {
+ import std.random : Random, unpredictableSeed, uniform;
+
+ static Random rnd;
+ static bool first = true;
+ if (first)
+ {
+ rnd = Random(unpredictableSeed);
+ first = false;
+ }
+ int[] result = new int[uniform(minArraySize, maxArraySize, rnd)];
+ foreach (ref i; result)
+ {
+ i = uniform(-100, 100, rnd);
+ }
+ return result;
+ }
+
+ package double[] rndstuff(T : double)()
+ {
+ double[] result;
+ foreach (i; rndstuff!(int)())
+ {
+ result ~= i / 50.0;
+ }
+ return result;
+ }
+}
+
+package(std) T* addressOf(T)(ref T val) { return &val; }
diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d
new file mode 100644
index 0000000..7e57824
--- /dev/null
+++ b/libphobos/src/std/algorithm/iteration.d
@@ -0,0 +1,5187 @@
+// Written in the D programming language.
+/**
+This is a submodule of $(MREF std, algorithm).
+It contains generic _iteration algorithms.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE Cheat Sheet,
+$(TR $(TH Function Name) $(TH Description))
+$(T2 cache,
+ Eagerly evaluates and caches another range's $(D front).)
+$(T2 cacheBidirectional,
+ As above, but also provides $(D back) and $(D popBack).)
+$(T2 chunkBy,
+ $(D chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]]))
+ returns a range containing 3 subranges: the first with just
+ $(D [1, 1]); the second with the elements $(D [1, 2]) and $(D [2, 2]);
+ and the third with just $(D [2, 1]).)
+$(T2 cumulativeFold,
+ $(D cumulativeFold!((a, b) => a + b)([1, 2, 3, 4])) returns a
+ lazily-evaluated range containing the successive reduced values `1`,
+ `3`, `6`, `10`.)
+$(T2 each,
+ $(D each!writeln([1, 2, 3])) eagerly prints the numbers $(D 1), $(D 2)
+ and $(D 3) on their own lines.)
+$(T2 filter,
+ $(D filter!(a => a > 0)([1, -1, 2, 0, -3])) iterates over elements $(D 1)
+ and $(D 2).)
+$(T2 filterBidirectional,
+ Similar to $(D filter), but also provides $(D back) and $(D popBack) at
+ a small increase in cost.)
+$(T2 fold,
+ $(D fold!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10).)
+$(T2 group,
+ $(D group([5, 2, 2, 3, 3])) returns a range containing the tuples
+ $(D tuple(5, 1)), $(D tuple(2, 2)), and $(D tuple(3, 2)).)
+$(T2 joiner,
+ $(D joiner(["hello", "world!"], "; ")) returns a range that iterates
+ over the characters $(D "hello; world!"). No new string is created -
+ the existing inputs are iterated.)
+$(T2 map,
+ $(D map!(a => a * 2)([1, 2, 3])) lazily returns a range with the numbers
+ $(D 2), $(D 4), $(D 6).)
+$(T2 permutations,
+ Lazily computes all permutations using Heap's algorithm.)
+$(T2 reduce,
+ $(D reduce!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10).
+ This is the old implementation of `fold`.)
+$(T2 splitter,
+ Lazily splits a range by a separator.)
+$(T2 sum,
+ Same as $(D fold), but specialized for accurate summation.)
+$(T2 uniq,
+ Iterates over the unique elements in a range, which is assumed sorted.)
+)
+
+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/_iteration.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.iteration;
+
+// FIXME
+import std.functional; // : unaryFun, binaryFun;
+import std.range.primitives;
+import std.traits;
+
+private template aggregate(fun...)
+if (fun.length >= 1)
+{
+ /* --Intentionally not ddoc--
+ * Aggregates elements in each subrange of the given range of ranges using
+ * the given aggregating function(s).
+ * Params:
+ * fun = One or more aggregating functions (binary functions that return a
+ * single _aggregate value of their arguments).
+ * ror = A range of ranges to be aggregated.
+ *
+ * Returns:
+ * A range representing the aggregated value(s) of each subrange
+ * of the original range. If only one aggregating function is specified,
+ * each element will be the aggregated value itself; if multiple functions
+ * are specified, each element will be a tuple of the aggregated values of
+ * each respective function.
+ */
+ auto aggregate(RoR)(RoR ror)
+ if (isInputRange!RoR && isIterable!(ElementType!RoR))
+ {
+ return ror.map!(reduce!fun);
+ }
+
+ @safe unittest
+ {
+ import std.algorithm.comparison : equal, max, min;
+
+ auto data = [[4, 2, 1, 3], [4, 9, -1, 3, 2], [3]];
+
+ // Single aggregating function
+ auto agg1 = data.aggregate!max;
+ assert(agg1.equal([4, 9, 3]));
+
+ // Multiple aggregating functions
+ import std.typecons : tuple;
+ auto agg2 = data.aggregate!(max, min);
+ assert(agg2.equal([
+ tuple(4, 1),
+ tuple(9, -1),
+ tuple(3, 3)
+ ]));
+ }
+}
+
+/++
+$(D cache) eagerly evaluates $(D front) of $(D range)
+on each construction or call to $(D popFront),
+to store the result in a cache.
+The result is then directly returned when $(D front) is called,
+rather than re-evaluated.
+
+This can be a useful function to place in a chain, after functions
+that have expensive evaluation, as a lazy alternative to $(REF array, std,array).
+In particular, it can be placed after a call to $(D map), or before a call
+to $(D filter).
+
+$(D cache) may provide
+$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
+iteration if needed, but since this comes at an increased cost, it must be explicitly requested via the
+call to $(D cacheBidirectional). Furthermore, a bidirectional cache will
+evaluate the "center" element twice, when there is only one element left in
+the range.
+
+$(D cache) does not provide random access primitives,
+as $(D cache) would be unable to cache the random accesses.
+If $(D Range) provides slicing primitives,
+then $(D cache) will provide the same slicing primitives,
+but $(D hasSlicing!Cache) will not yield true (as the $(REF hasSlicing, std,_range,primitives)
+trait also checks for random access).
+
+Params:
+ range = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+
+Returns:
+ an input range with the cached values of range
++/
+auto cache(Range)(Range range)
+if (isInputRange!Range)
+{
+ return _Cache!(Range, false)(range);
+}
+
+/// ditto
+auto cacheBidirectional(Range)(Range range)
+if (isBidirectionalRange!Range)
+{
+ return _Cache!(Range, true)(range);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range, std.stdio;
+ import std.typecons : tuple;
+
+ ulong counter = 0;
+ double fun(int x)
+ {
+ ++counter;
+ // http://en.wikipedia.org/wiki/Quartic_function
+ return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5;
+ }
+ // Without cache, with array (greedy)
+ auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
+ .filter!(a => a[1] < 0)()
+ .map!(a => a[0])()
+ .array();
+
+ // the values of x that have a negative y are:
+ assert(equal(result1, [-3, -2, 2]));
+
+ // Check how many times fun was evaluated.
+ // As many times as the number of items in both source and result.
+ assert(counter == iota(-4, 5).length + result1.length);
+
+ counter = 0;
+ // Without array, with cache (lazy)
+ auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
+ .cache()
+ .filter!(a => a[1] < 0)()
+ .map!(a => a[0])();
+
+ // the values of x that have a negative y are:
+ assert(equal(result2, [-3, -2, 2]));
+
+ // Check how many times fun was evaluated.
+ // Only as many times as the number of items in source.
+ assert(counter == iota(-4, 5).length);
+}
+
+/++
+Tip: $(D cache) is eager when evaluating elements. If calling front on the
+underlying _range has a side effect, it will be observable before calling
+front on the actual cached _range.
+
+Furthermore, care should be taken composing $(D cache) with $(REF take, std,_range).
+By placing $(D take) before $(D cache), then $(D cache) will be "aware"
+of when the _range ends, and correctly stop caching elements when needed.
+If calling front has no side effect though, placing $(D take) after $(D cache)
+may yield a faster _range.
+
+Either way, the resulting ranges will be equivalent, but maybe not at the
+same cost or side effects.
++/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ int i = 0;
+
+ auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop);
+ auto r1 = r.take(3).cache();
+ auto r2 = r.cache().take(3);
+
+ assert(equal(r1, [0, 1, 2]));
+ assert(i == 2); //The last "seen" element was 2. The data in cache has been cleared.
+
+ assert(equal(r2, [0, 1, 2]));
+ assert(i == 3); //cache has accessed 3. It is still stored internally by cache.
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ auto a = [1, 2, 3, 4];
+ assert(equal(a.map!(a => (a - 1) * a)().cache(), [ 0, 2, 6, 12]));
+ assert(equal(a.map!(a => (a - 1) * a)().cacheBidirectional().retro(), [12, 6, 2, 0]));
+ auto r1 = [1, 2, 3, 4].cache() [1 .. $];
+ auto r2 = [1, 2, 3, 4].cacheBidirectional()[1 .. $];
+ assert(equal(r1, [2, 3, 4]));
+ assert(equal(r2, [2, 3, 4]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //immutable test
+ static struct S
+ {
+ int i;
+ this(int i)
+ {
+ //this.i = i;
+ }
+ }
+ immutable(S)[] s = [S(1), S(2), S(3)];
+ assert(equal(s.cache(), s));
+ assert(equal(s.cacheBidirectional(), s));
+}
+
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //safety etc
+ auto a = [1, 2, 3, 4];
+ assert(equal(a.cache(), a));
+ assert(equal(a.cacheBidirectional(), a));
+}
+
+@safe unittest
+{
+ char[][] stringbufs = ["hello".dup, "world".dup];
+ auto strings = stringbufs.map!((a)=>a.idup)().cache();
+ assert(strings.front is strings.front);
+}
+
+@safe unittest
+{
+ import std.range : cycle;
+ import std.algorithm.comparison : equal;
+
+ auto c = [1, 2, 3].cycle().cache();
+ c = c[1 .. $];
+ auto d = c[0 .. 1];
+ assert(d.equal([2]));
+}
+
+@safe unittest
+{
+ static struct Range
+ {
+ bool initialized = false;
+ bool front() @property {return initialized = true;}
+ void popFront() {initialized = false;}
+ enum empty = false;
+ }
+ auto r = Range().cache();
+ assert(r.source.initialized == true);
+}
+
+private struct _Cache(R, bool bidir)
+{
+ import core.exception : RangeError;
+
+ private
+ {
+ import std.algorithm.internal : algoFormat;
+ import std.meta : AliasSeq;
+
+ alias E = ElementType!R;
+ alias UE = Unqual!E;
+
+ R source;
+
+ static if (bidir) alias CacheTypes = AliasSeq!(UE, UE);
+ else alias CacheTypes = AliasSeq!UE;
+ CacheTypes caches;
+
+ static assert(isAssignable!(UE, E) && is(UE : E),
+ algoFormat(
+ "Cannot instantiate range with %s because %s elements are not assignable to %s.",
+ R.stringof,
+ E.stringof,
+ UE.stringof
+ )
+ );
+ }
+
+ this(R range)
+ {
+ source = range;
+ if (!range.empty)
+ {
+ caches[0] = source.front;
+ static if (bidir)
+ caches[1] = source.back;
+ }
+ }
+
+ static if (isInfinite!R)
+ enum empty = false;
+ else
+ bool empty() @property
+ {
+ return source.empty;
+ }
+
+ static if (hasLength!R) auto length() @property
+ {
+ return source.length;
+ }
+
+ E front() @property
+ {
+ version (assert) if (empty) throw new RangeError();
+ return caches[0];
+ }
+ static if (bidir) E back() @property
+ {
+ version (assert) if (empty) throw new RangeError();
+ return caches[1];
+ }
+
+ void popFront()
+ {
+ version (assert) if (empty) throw new RangeError();
+ source.popFront();
+ if (!source.empty)
+ caches[0] = source.front;
+ else
+ caches = CacheTypes.init;
+ }
+ static if (bidir) void popBack()
+ {
+ version (assert) if (empty) throw new RangeError();
+ source.popBack();
+ if (!source.empty)
+ caches[1] = source.back;
+ else
+ caches = CacheTypes.init;
+ }
+
+ static if (isForwardRange!R)
+ {
+ private this(R source, ref CacheTypes caches)
+ {
+ this.source = source;
+ this.caches = caches;
+ }
+ typeof(this) save() @property
+ {
+ return typeof(this)(source.save, caches);
+ }
+ }
+
+ static if (hasSlicing!R)
+ {
+ enum hasEndSlicing = is(typeof(source[size_t.max .. $]));
+
+ static if (hasEndSlicing)
+ {
+ private static struct DollarToken{}
+ enum opDollar = DollarToken.init;
+
+ auto opSlice(size_t low, DollarToken)
+ {
+ return typeof(this)(source[low .. $]);
+ }
+ }
+
+ static if (!isInfinite!R)
+ {
+ typeof(this) opSlice(size_t low, size_t high)
+ {
+ return typeof(this)(source[low .. high]);
+ }
+ }
+ else static if (hasEndSlicing)
+ {
+ auto opSlice(size_t low, size_t high)
+ in
+ {
+ assert(low <= high, "Bounds error when slicing cache.");
+ }
+ body
+ {
+ import std.range : takeExactly;
+ return this[low .. $].takeExactly(high - low);
+ }
+ }
+ }
+}
+
+/**
+$(D auto map(Range)(Range r) if (isInputRange!(Unqual!Range));)
+
+Implements the homonym function (also known as $(D transform)) present
+in many languages of functional flavor. The call $(D map!(fun)(range))
+returns a range of which elements are obtained by applying $(D fun(a))
+left to right for all elements $(D a) in $(D range). The original ranges are
+not changed. Evaluation is done lazily.
+
+Params:
+ fun = one or more transformation functions
+ r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+
+Returns:
+ a range with each fun applied to all the elements. If there is more than one
+ fun, the element type will be $(D Tuple) containing one element for each fun.
+
+See_Also:
+ $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
+*/
+template map(fun...)
+if (fun.length >= 1)
+{
+ auto map(Range)(Range r) if (isInputRange!(Unqual!Range))
+ {
+ import std.meta : AliasSeq, staticMap;
+
+ alias RE = ElementType!(Range);
+ static if (fun.length > 1)
+ {
+ import std.functional : adjoin;
+ import std.meta : staticIndexOf;
+
+ alias _funs = staticMap!(unaryFun, fun);
+ alias _fun = adjoin!_funs;
+
+ // Once DMD issue #5710 is fixed, this validation loop can be moved into a template.
+ foreach (f; _funs)
+ {
+ static assert(!is(typeof(f(RE.init)) == void),
+ "Mapping function(s) must not return void: " ~ _funs.stringof);
+ }
+ }
+ else
+ {
+ alias _fun = unaryFun!fun;
+ alias _funs = AliasSeq!(_fun);
+
+ // Do the validation separately for single parameters due to DMD issue #15777.
+ static assert(!is(typeof(_fun(RE.init)) == void),
+ "Mapping function(s) must not return void: " ~ _funs.stringof);
+ }
+
+ return MapResult!(_fun, Range)(r);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : chain;
+ int[] arr1 = [ 1, 2, 3, 4 ];
+ int[] arr2 = [ 5, 6 ];
+ auto squares = map!(a => a * a)(chain(arr1, arr2));
+ assert(equal(squares, [ 1, 4, 9, 16, 25, 36 ]));
+}
+
+/**
+Multiple functions can be passed to $(D map). In that case, the
+element type of $(D map) is a tuple containing one element for each
+function.
+*/
+@safe unittest
+{
+ auto sums = [2, 4, 6, 8];
+ auto products = [1, 4, 9, 16];
+
+ size_t i = 0;
+ foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a"))
+ {
+ assert(result[0] == sums[i]);
+ assert(result[1] == products[i]);
+ ++i;
+ }
+}
+
+/**
+You may alias $(D map) with some function(s) to a symbol and use
+it separately:
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : to;
+
+ alias stringize = map!(to!string);
+ assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
+}
+
+@safe unittest
+{
+ // Verify workaround for DMD #15777
+
+ import std.algorithm.mutation, std.string;
+ auto foo(string[] args)
+ {
+ return args.map!strip;
+ }
+}
+
+private struct MapResult(alias fun, Range)
+{
+ alias R = Unqual!Range;
+ R _input;
+
+ static if (isBidirectionalRange!R)
+ {
+ @property auto ref back()()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty map.");
+ return fun(_input.back);
+ }
+
+ void popBack()()
+ {
+ assert(!empty, "Attempting to popBack an empty map.");
+ _input.popBack();
+ }
+ }
+
+ this(R input)
+ {
+ _input = input;
+ }
+
+ static if (isInfinite!R)
+ {
+ // Propagate infinite-ness.
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _input.empty;
+ }
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty map.");
+ _input.popFront();
+ }
+
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty map.");
+ return fun(_input.front);
+ }
+
+ static if (isRandomAccessRange!R)
+ {
+ static if (is(typeof(_input[ulong.max])))
+ private alias opIndex_t = ulong;
+ else
+ private alias opIndex_t = uint;
+
+ auto ref opIndex(opIndex_t index)
+ {
+ return fun(_input[index]);
+ }
+ }
+
+ static if (hasLength!R)
+ {
+ @property auto length()
+ {
+ return _input.length;
+ }
+
+ alias opDollar = length;
+ }
+
+ static if (hasSlicing!R)
+ {
+ static if (is(typeof(_input[ulong.max .. ulong.max])))
+ private alias opSlice_t = ulong;
+ else
+ private alias opSlice_t = uint;
+
+ static if (hasLength!R)
+ {
+ auto opSlice(opSlice_t low, opSlice_t high)
+ {
+ return typeof(this)(_input[low .. high]);
+ }
+ }
+ else static if (is(typeof(_input[opSlice_t.max .. $])))
+ {
+ struct DollarToken{}
+ enum opDollar = DollarToken.init;
+ auto opSlice(opSlice_t low, DollarToken)
+ {
+ return typeof(this)(_input[low .. $]);
+ }
+
+ auto opSlice(opSlice_t low, opSlice_t high)
+ {
+ import std.range : takeExactly;
+ return this[low .. $].takeExactly(high - low);
+ }
+ }
+ }
+
+ static if (isForwardRange!R)
+ {
+ @property auto save()
+ {
+ return typeof(this)(_input.save);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : to;
+ import std.functional : adjoin;
+
+ alias stringize = map!(to!string);
+ assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
+
+ uint counter;
+ alias count = map!((a) { return counter++; });
+ assert(equal(count([ 10, 2, 30, 4 ]), [ 0, 1, 2, 3 ]));
+
+ counter = 0;
+ adjoin!((a) { return counter++; }, (a) { return counter++; })(1);
+ alias countAndSquare = map!((a) { return counter++; }, (a) { return counter++; });
+ //assert(equal(countAndSquare([ 10, 2 ]), [ tuple(0u, 100), tuple(1u, 4) ]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.ascii : toUpper;
+ import std.internal.test.dummyrange;
+ import std.range;
+ import std.typecons : tuple;
+ import std.random : unpredictableSeed, uniform, Random;
+
+ int[] arr1 = [ 1, 2, 3, 4 ];
+ const int[] arr1Const = arr1;
+ int[] arr2 = [ 5, 6 ];
+ auto squares = map!("a * a")(arr1Const);
+ assert(squares[$ - 1] == 16);
+ assert(equal(squares, [ 1, 4, 9, 16 ][]));
+ assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][]));
+
+ // Test the caching stuff.
+ assert(squares.back == 16);
+ auto squares2 = squares.save;
+ assert(squares2.back == 16);
+
+ assert(squares2.front == 1);
+ squares2.popFront();
+ assert(squares2.front == 4);
+ squares2.popBack();
+ assert(squares2.front == 4);
+ assert(squares2.back == 9);
+
+ assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][]));
+
+ uint i;
+ foreach (e; map!("a", "a * a")(arr1))
+ {
+ assert(e[0] == ++i);
+ assert(e[1] == i * i);
+ }
+
+ // Test length.
+ assert(squares.length == 4);
+ assert(map!"a * a"(chain(arr1, arr2)).length == 6);
+
+ // Test indexing.
+ assert(squares[0] == 1);
+ assert(squares[1] == 4);
+ assert(squares[2] == 9);
+ assert(squares[3] == 16);
+
+ // Test slicing.
+ auto squareSlice = squares[1 .. squares.length - 1];
+ assert(equal(squareSlice, [4, 9][]));
+ assert(squareSlice.back == 9);
+ assert(squareSlice[1] == 9);
+
+ // Test on a forward range to make sure it compiles when all the fancy
+ // stuff is disabled.
+ auto fibsSquares = map!"a * a"(recurrence!("a[n-1] + a[n-2]")(1, 1));
+ assert(fibsSquares.front == 1);
+ fibsSquares.popFront();
+ fibsSquares.popFront();
+ assert(fibsSquares.front == 4);
+ fibsSquares.popFront();
+ assert(fibsSquares.front == 9);
+
+ auto repeatMap = map!"a"(repeat(1));
+ auto gen = Random(unpredictableSeed);
+ auto index = uniform(0, 1024, gen);
+ static assert(isInfinite!(typeof(repeatMap)));
+ assert(repeatMap[index] == 1);
+
+ auto intRange = map!"a"([1,2,3]);
+ static assert(isRandomAccessRange!(typeof(intRange)));
+ assert(equal(intRange, [1, 2, 3]));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ auto m = map!"a * a"(d);
+
+ static assert(propagatesRangeType!(typeof(m), DummyType));
+ assert(equal(m, [1,4,9,16,25,36,49,64,81,100]));
+ }
+
+ //Test string access
+ string s1 = "hello world!";
+ dstring s2 = "日本語";
+ dstring s3 = "hello world!"d;
+ auto ms1 = map!(std.ascii.toUpper)(s1);
+ auto ms2 = map!(std.ascii.toUpper)(s2);
+ auto ms3 = map!(std.ascii.toUpper)(s3);
+ static assert(!is(ms1[0])); //narrow strings can't be indexed
+ assert(ms2[0] == '日');
+ assert(ms3[0] == 'H');
+ static assert(!is(ms1[0 .. 1])); //narrow strings can't be sliced
+ assert(equal(ms2[0 .. 2], "日本"w));
+ assert(equal(ms3[0 .. 2], "HE"));
+
+ // Issue 5753
+ static void voidFun(int) {}
+ static int nonvoidFun(int) { return 0; }
+ static assert(!__traits(compiles, map!voidFun([1])));
+ static assert(!__traits(compiles, map!(voidFun, voidFun)([1])));
+ static assert(!__traits(compiles, map!(nonvoidFun, voidFun)([1])));
+ static assert(!__traits(compiles, map!(voidFun, nonvoidFun)([1])));
+ static assert(!__traits(compiles, map!(a => voidFun(a))([1])));
+
+ // Phobos issue #15480
+ auto dd = map!(z => z * z, c => c * c * c)([ 1, 2, 3, 4 ]);
+ assert(dd[0] == tuple(1, 1));
+ assert(dd[1] == tuple(4, 8));
+ assert(dd[2] == tuple(9, 27));
+ assert(dd[3] == tuple(16, 64));
+ assert(dd.length == 4);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ auto LL = iota(1L, 4L);
+ auto m = map!"a*a"(LL);
+ assert(equal(m, [1L, 4L, 9L]));
+}
+
+@safe unittest
+{
+ import std.range : iota;
+
+ // Issue #10130 - map of iota with const step.
+ const step = 2;
+ assert(map!(i => i)(iota(0, 10, step)).walkLength == 5);
+
+ // Need these to all by const to repro the float case, due to the
+ // CommonType template used in the float specialization of iota.
+ const floatBegin = 0.0;
+ const floatEnd = 1.0;
+ const floatStep = 0.02;
+ assert(map!(i => i)(iota(floatBegin, floatEnd, floatStep)).walkLength == 50);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ //slicing infinites
+ auto rr = iota(0, 5).cycle().map!"a * a"();
+ alias RR = typeof(rr);
+ static assert(hasSlicing!RR);
+ rr = rr[6 .. $]; //Advances 1 cycle and 1 unit
+ assert(equal(rr[0 .. 5], [1, 4, 9, 16, 0]));
+}
+
+@safe unittest
+{
+ import std.range;
+ struct S {int* p;}
+ auto m = immutable(S).init.repeat().map!"a".save;
+ assert(m.front == immutable(S)(null));
+}
+
+// each
+/**
+Eagerly iterates over $(D r) and calls $(D pred) over _each element.
+
+If no predicate is specified, $(D each) will default to doing nothing
+but consuming the entire range. $(D .front) will be evaluated, but this
+can be avoided by explicitly specifying a predicate lambda with a
+$(D lazy) parameter.
+
+$(D each) also supports $(D opApply)-based iterators, so it will work
+with e.g. $(REF parallel, std,parallelism).
+
+Params:
+ pred = predicate to apply to each element of the range
+ r = range or iterable over which each iterates
+
+See_Also: $(REF tee, std,range)
+
+ */
+template each(alias pred = "a")
+{
+ import std.meta : AliasSeq;
+ import std.traits : Parameters;
+
+private:
+ alias BinaryArgs = AliasSeq!(pred, "i", "a");
+
+ enum isRangeUnaryIterable(R) =
+ is(typeof(unaryFun!pred(R.init.front)));
+
+ enum isRangeBinaryIterable(R) =
+ is(typeof(binaryFun!BinaryArgs(0, R.init.front)));
+
+ enum isRangeIterable(R) =
+ isInputRange!R &&
+ (isRangeUnaryIterable!R || isRangeBinaryIterable!R);
+
+ enum isForeachUnaryIterable(R) =
+ is(typeof((R r) {
+ foreach (ref a; r)
+ cast(void) unaryFun!pred(a);
+ }));
+
+ enum isForeachBinaryIterable(R) =
+ is(typeof((R r) {
+ foreach (ref i, ref a; r)
+ cast(void) binaryFun!BinaryArgs(i, a);
+ }));
+
+ enum isForeachIterable(R) =
+ (!isForwardRange!R || isDynamicArray!R) &&
+ (isForeachUnaryIterable!R || isForeachBinaryIterable!R);
+
+public:
+ void each(Range)(Range r)
+ if (!isForeachIterable!Range && (
+ isRangeIterable!Range ||
+ __traits(compiles, typeof(r.front).length)))
+ {
+ static if (isRangeIterable!Range)
+ {
+ debug(each) pragma(msg, "Using while for ", Range.stringof);
+ static if (isRangeUnaryIterable!Range)
+ {
+ while (!r.empty)
+ {
+ cast(void) unaryFun!pred(r.front);
+ r.popFront();
+ }
+ }
+ else // if (isRangeBinaryIterable!Range)
+ {
+ size_t i = 0;
+ while (!r.empty)
+ {
+ cast(void) binaryFun!BinaryArgs(i, r.front);
+ r.popFront();
+ i++;
+ }
+ }
+ }
+ else
+ {
+ // range interface with >2 parameters.
+ for (auto range = r; !range.empty; range.popFront())
+ pred(range.front.expand);
+ }
+ }
+
+ void each(Iterable)(auto ref Iterable r)
+ if (isForeachIterable!Iterable ||
+ __traits(compiles, Parameters!(Parameters!(r.opApply))))
+ {
+ static if (isForeachIterable!Iterable)
+ {
+ debug(each) pragma(msg, "Using foreach for ", Iterable.stringof);
+ static if (isForeachUnaryIterable!Iterable)
+ {
+ foreach (ref e; r)
+ cast(void) unaryFun!pred(e);
+ }
+ else // if (isForeachBinaryIterable!Iterable)
+ {
+ foreach (ref i, ref e; r)
+ cast(void) binaryFun!BinaryArgs(i, e);
+ }
+ }
+ else
+ {
+ // opApply with >2 parameters. count the delegate args.
+ // only works if it is not templated (otherwise we cannot count the args)
+ auto dg(Parameters!(Parameters!(r.opApply)) params) {
+ pred(params);
+ return 0; // tells opApply to continue iteration
+ }
+ r.opApply(&dg);
+ }
+ }
+}
+
+///
+@system unittest
+{
+ import std.range : iota;
+
+ long[] arr;
+ iota(5).each!(n => arr ~= n);
+ assert(arr == [0, 1, 2, 3, 4]);
+
+ // If the range supports it, the value can be mutated in place
+ arr.each!((ref n) => n++);
+ assert(arr == [1, 2, 3, 4, 5]);
+
+ arr.each!"a++";
+ assert(arr == [2, 3, 4, 5, 6]);
+
+ // by-ref lambdas are not allowed for non-ref ranges
+ static assert(!is(typeof(arr.map!(n => n).each!((ref n) => n++))));
+
+ // The default predicate consumes the range
+ auto m = arr.map!(n => n);
+ (&m).each();
+ assert(m.empty);
+
+ // Indexes are also available for in-place mutations
+ arr[] = 0;
+ arr.each!"a=i"();
+ assert(arr == [0, 1, 2, 3, 4]);
+
+ // opApply iterators work as well
+ static class S
+ {
+ int x;
+ int opApply(scope int delegate(ref int _x) dg) { return dg(x); }
+ }
+
+ auto s = new S;
+ s.each!"a++";
+ assert(s.x == 1);
+}
+
+// binary foreach with two ref args
+@system unittest
+{
+ import std.range : lockstep;
+
+ auto a = [ 1, 2, 3 ];
+ auto b = [ 2, 3, 4 ];
+
+ a.lockstep(b).each!((ref x, ref y) { ++x; ++y; });
+
+ assert(a == [ 2, 3, 4 ]);
+ assert(b == [ 3, 4, 5 ]);
+}
+
+// #15358: application of `each` with >2 args (opApply)
+@system unittest
+{
+ import std.range : lockstep;
+ auto a = [0,1,2];
+ auto b = [3,4,5];
+ auto c = [6,7,8];
+
+ lockstep(a, b, c).each!((ref x, ref y, ref z) { ++x; ++y; ++z; });
+
+ assert(a == [1,2,3]);
+ assert(b == [4,5,6]);
+ assert(c == [7,8,9]);
+}
+
+// #15358: application of `each` with >2 args (range interface)
+@safe unittest
+{
+ import std.range : zip;
+ auto a = [0,1,2];
+ auto b = [3,4,5];
+ auto c = [6,7,8];
+
+ int[] res;
+
+ zip(a, b, c).each!((x, y, z) { res ~= x + y + z; });
+
+ assert(res == [9, 12, 15]);
+}
+
+// #16255: `each` on opApply doesn't support ref
+@safe unittest
+{
+ int[] dynamicArray = [1, 2, 3, 4, 5];
+ int[5] staticArray = [1, 2, 3, 4, 5];
+
+ dynamicArray.each!((ref x) => x++);
+ assert(dynamicArray == [2, 3, 4, 5, 6]);
+
+ staticArray.each!((ref x) => x++);
+ assert(staticArray == [2, 3, 4, 5, 6]);
+
+ staticArray[].each!((ref x) => x++);
+ assert(staticArray == [3, 4, 5, 6, 7]);
+}
+
+// #16255: `each` on opApply doesn't support ref
+@system unittest
+{
+ struct S
+ {
+ int x;
+ int opApply(int delegate(ref int _x) dg) { return dg(x); }
+ }
+
+ S s;
+ foreach (ref a; s) ++a;
+ assert(s.x == 1);
+ s.each!"++a";
+ assert(s.x == 2);
+}
+
+// filter
+/**
+$(D auto filter(Range)(Range rs) if (isInputRange!(Unqual!Range));)
+
+Implements the higher order _filter function. The predicate is passed to
+$(REF unaryFun, std,functional), and can either accept a string, or any callable
+that can be executed via $(D pred(element)).
+
+Params:
+ predicate = Function to apply to each element of range
+ range = Input range of elements
+
+Returns:
+ $(D filter!(predicate)(range)) returns a new range containing only elements $(D x) in $(D range) for
+ which $(D predicate(x)) returns $(D true).
+
+See_Also:
+ $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
+ */
+template filter(alias predicate)
+if (is(typeof(unaryFun!predicate)))
+{
+ auto filter(Range)(Range range) if (isInputRange!(Unqual!Range))
+ {
+ return FilterResult!(unaryFun!predicate, Range)(range);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.math : approxEqual;
+ import std.range;
+
+ int[] arr = [ 1, 2, 3, 4, 5 ];
+
+ // Sum all elements
+ auto small = filter!(a => a < 3)(arr);
+ assert(equal(small, [ 1, 2 ]));
+
+ // Sum again, but with Uniform Function Call Syntax (UFCS)
+ auto sum = arr.filter!(a => a < 3);
+ assert(equal(sum, [ 1, 2 ]));
+
+ // In combination with chain() to span multiple ranges
+ int[] a = [ 3, -2, 400 ];
+ int[] b = [ 100, -101, 102 ];
+ auto r = chain(a, b).filter!(a => a > 0);
+ assert(equal(r, [ 3, 400, 100, 102 ]));
+
+ // Mixing convertible types is fair game, too
+ double[] c = [ 2.5, 3.0 ];
+ auto r1 = chain(c, a, b).filter!(a => cast(int) a != a);
+ assert(approxEqual(r1, [ 2.5 ]));
+}
+
+private struct FilterResult(alias pred, Range)
+{
+ alias R = Unqual!Range;
+ R _input;
+ private bool _primed;
+
+ private void prime()
+ {
+ if (_primed) return;
+ while (!_input.empty && !pred(_input.front))
+ {
+ _input.popFront();
+ }
+ _primed = true;
+ }
+
+ this(R r)
+ {
+ _input = r;
+ }
+
+ private this(R r, bool primed)
+ {
+ _input = r;
+ _primed = primed;
+ }
+
+ auto opSlice() { return this; }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty() { prime; return _input.empty; }
+ }
+
+ void popFront()
+ {
+ do
+ {
+ _input.popFront();
+ } while (!_input.empty && !pred(_input.front));
+ _primed = true;
+ }
+
+ @property auto ref front()
+ {
+ prime;
+ assert(!empty, "Attempting to fetch the front of an empty filter.");
+ return _input.front;
+ }
+
+ static if (isForwardRange!R)
+ {
+ @property auto save()
+ {
+ return typeof(this)(_input.save, _primed);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+ import std.range;
+
+ auto shouldNotLoop4ever = repeat(1).filter!(x => x % 2 == 0);
+ static assert(isInfinite!(typeof(shouldNotLoop4ever)));
+ assert(!shouldNotLoop4ever.empty);
+
+ int[] a = [ 3, 4, 2 ];
+ auto r = filter!("a > 3")(a);
+ static assert(isForwardRange!(typeof(r)));
+ assert(equal(r, [ 4 ][]));
+
+ a = [ 1, 22, 3, 42, 5 ];
+ auto under10 = filter!("a < 10")(a);
+ assert(equal(under10, [1, 3, 5][]));
+ static assert(isForwardRange!(typeof(under10)));
+ under10.front = 4;
+ assert(equal(under10, [4, 3, 5][]));
+ under10.front = 40;
+ assert(equal(under10, [40, 3, 5][]));
+ under10.front = 1;
+
+ auto infinite = filter!"a > 2"(repeat(3));
+ static assert(isInfinite!(typeof(infinite)));
+ static assert(isForwardRange!(typeof(infinite)));
+ assert(infinite.front == 3);
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ auto f = filter!"a & 1"(d);
+ assert(equal(f, [1,3,5,7,9]));
+
+ static if (isForwardRange!DummyType)
+ {
+ static assert(isForwardRange!(typeof(f)));
+ }
+ }
+
+ // With delegates
+ int x = 10;
+ int overX(int a) { return a > x; }
+ typeof(filter!overX(a)) getFilter()
+ {
+ return filter!overX(a);
+ }
+ auto r1 = getFilter();
+ assert(equal(r1, [22, 42]));
+
+ // With chain
+ auto nums = [0,1,2,3,4];
+ assert(equal(filter!overX(chain(a, nums)), [22, 42]));
+
+ // With copying of inner struct Filter to Map
+ auto arr = [1,2,3,4,5];
+ auto m = map!"a + 1"(filter!"a < 4"(arr));
+ assert(equal(m, [2, 3, 4]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int[] a = [ 3, 4 ];
+ const aConst = a;
+ auto r = filter!("a > 3")(aConst);
+ assert(equal(r, [ 4 ][]));
+
+ a = [ 1, 22, 3, 42, 5 ];
+ auto under10 = filter!("a < 10")(a);
+ assert(equal(under10, [1, 3, 5][]));
+ assert(equal(under10.save, [1, 3, 5][]));
+ assert(equal(under10.save, under10));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.functional : compose, pipe;
+
+ assert(equal(compose!(map!"2 * a", filter!"a & 1")([1,2,3,4,5]),
+ [2,6,10]));
+ assert(equal(pipe!(filter!"a & 1", map!"2 * a")([1,2,3,4,5]),
+ [2,6,10]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int x = 10;
+ int underX(int a) { return a < x; }
+ const(int)[] list = [ 1, 2, 10, 11, 3, 4 ];
+ assert(equal(filter!underX(list), [ 1, 2, 3, 4 ]));
+}
+
+/**
+ * $(D auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range));)
+ *
+ * Similar to $(D filter), except it defines a
+ * $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives).
+ * There is a speed disadvantage - the constructor spends time
+ * finding the last element in the range that satisfies the filtering
+ * condition (in addition to finding the first one). The advantage is
+ * that the filtered range can be spanned from both directions. Also,
+ * $(REF retro, std,range) can be applied against the filtered range.
+ *
+ * The predicate is passed to $(REF unaryFun, std,functional), and can either
+ * accept a string, or any callable that can be executed via $(D pred(element)).
+ *
+ * Params:
+ * pred = Function to apply to each element of range
+ * r = Bidirectional range of elements
+ *
+ * Returns:
+ * a new range containing only the elements in r for which pred returns $(D true).
+ */
+template filterBidirectional(alias pred)
+{
+ auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range))
+ {
+ return FilterBidiResult!(unaryFun!pred, Range)(r);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+
+ int[] arr = [ 1, 2, 3, 4, 5 ];
+ auto small = filterBidirectional!("a < 3")(arr);
+ static assert(isBidirectionalRange!(typeof(small)));
+ assert(small.back == 2);
+ assert(equal(small, [ 1, 2 ]));
+ assert(equal(retro(small), [ 2, 1 ]));
+ // In combination with chain() to span multiple ranges
+ int[] a = [ 3, -2, 400 ];
+ int[] b = [ 100, -101, 102 ];
+ auto r = filterBidirectional!("a > 0")(chain(a, b));
+ assert(r.back == 102);
+}
+
+private struct FilterBidiResult(alias pred, Range)
+{
+ alias R = Unqual!Range;
+ R _input;
+
+ this(R r)
+ {
+ _input = r;
+ while (!_input.empty && !pred(_input.front)) _input.popFront();
+ while (!_input.empty && !pred(_input.back)) _input.popBack();
+ }
+
+ @property bool empty() { return _input.empty; }
+
+ void popFront()
+ {
+ do
+ {
+ _input.popFront();
+ } while (!_input.empty && !pred(_input.front));
+ }
+
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty filterBidirectional.");
+ return _input.front;
+ }
+
+ void popBack()
+ {
+ do
+ {
+ _input.popBack();
+ } while (!_input.empty && !pred(_input.back));
+ }
+
+ @property auto ref back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty filterBidirectional.");
+ return _input.back;
+ }
+
+ @property auto save()
+ {
+ return typeof(this)(_input.save);
+ }
+}
+
+/**
+Groups consecutively equivalent elements into a single tuple of the element and
+the number of its repetitions.
+
+Similarly to $(D uniq), $(D group) produces a range that iterates over unique
+consecutive elements of the given range. Each element of this range is a tuple
+of the element and the number of times it is repeated in the original range.
+Equivalence of elements is assessed by using the predicate $(D pred), which
+defaults to $(D "a == b"). The predicate is passed to $(REF binaryFun, std,functional),
+and can either accept a string, or any callable that can be executed via
+$(D pred(element, element)).
+
+Params:
+ pred = Binary predicate for determining equivalence of two elements.
+ r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
+ iterate over.
+
+Returns: A range of elements of type $(D Tuple!(ElementType!R, uint)),
+representing each consecutively unique element and its respective number of
+occurrences in that run. This will be an input range if $(D R) is an input
+range, and a forward range in all other cases.
+
+See_Also: $(LREF chunkBy), which chunks an input range into subranges
+ of equivalent adjacent elements.
+*/
+Group!(pred, Range) group(alias pred = "a == b", Range)(Range r)
+{
+ return typeof(return)(r);
+}
+
+/// ditto
+struct Group(alias pred, R)
+if (isInputRange!R)
+{
+ import std.typecons : Rebindable, tuple, Tuple;
+
+ private alias comp = binaryFun!pred;
+
+ private alias E = ElementType!R;
+ static if ((is(E == class) || is(E == interface)) &&
+ (is(E == const) || is(E == immutable)))
+ {
+ private alias MutableE = Rebindable!E;
+ }
+ else static if (is(E : Unqual!E))
+ {
+ private alias MutableE = Unqual!E;
+ }
+ else
+ {
+ private alias MutableE = E;
+ }
+
+ private R _input;
+ private Tuple!(MutableE, uint) _current;
+
+ ///
+ this(R input)
+ {
+ _input = input;
+ if (!_input.empty) popFront();
+ }
+
+ ///
+ void popFront()
+ {
+ if (_input.empty)
+ {
+ _current[1] = 0;
+ }
+ else
+ {
+ _current = tuple(_input.front, 1u);
+ _input.popFront();
+ while (!_input.empty && comp(_current[0], _input.front))
+ {
+ ++_current[1];
+ _input.popFront();
+ }
+ }
+ }
+
+ static if (isInfinite!R)
+ {
+ ///
+ enum bool empty = false; // Propagate infiniteness.
+ }
+ else
+ {
+ ///
+ @property bool empty()
+ {
+ return _current[1] == 0;
+ }
+ }
+
+ ///
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty Group.");
+ return _current;
+ }
+
+ static if (isForwardRange!R)
+ {
+ ///
+ @property typeof(this) save() {
+ typeof(this) ret = this;
+ ret._input = this._input.save;
+ ret._current = this._current;
+ return ret;
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple, Tuple;
+
+ int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
+ assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u),
+ tuple(4, 3u), tuple(5, 1u) ][]));
+}
+
+/**
+ * Using group, an associative array can be easily generated with the count of each
+ * unique element in the range.
+ */
+@safe unittest
+{
+ import std.algorithm.sorting : sort;
+ import std.array : assocArray;
+
+ uint[string] result;
+ auto range = ["a", "b", "a", "c", "b", "c", "c", "d", "e"];
+ result = range.sort!((a, b) => a < b)
+ .group
+ .assocArray;
+
+ assert(result == ["a": 2U, "b": 2U, "c": 3U, "d": 1U, "e": 1U]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+ import std.typecons : tuple, Tuple;
+
+ int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
+ assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u),
+ tuple(4, 3u), tuple(5, 1u) ][]));
+ static assert(isForwardRange!(typeof(group(arr))));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ auto g = group(d);
+
+ static assert(d.rt == RangeType.Input || isForwardRange!(typeof(g)));
+
+ assert(equal(g, [tuple(1, 1u), tuple(2, 1u), tuple(3, 1u), tuple(4, 1u),
+ tuple(5, 1u), tuple(6, 1u), tuple(7, 1u), tuple(8, 1u),
+ tuple(9, 1u), tuple(10, 1u)]));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ // Issue 13857
+ immutable(int)[] a1 = [1,1,2,2,2,3,4,4,5,6,6,7,8,9,9,9];
+ auto g1 = group(a1);
+ assert(equal(g1, [ tuple(1, 2u), tuple(2, 3u), tuple(3, 1u),
+ tuple(4, 2u), tuple(5, 1u), tuple(6, 2u),
+ tuple(7, 1u), tuple(8, 1u), tuple(9, 3u)
+ ]));
+
+ // Issue 13162
+ immutable(ubyte)[] a2 = [1, 1, 1, 0, 0, 0];
+ auto g2 = a2.group;
+ assert(equal(g2, [ tuple(1, 3u), tuple(0, 3u) ]));
+
+ // Issue 10104
+ const a3 = [1, 1, 2, 2];
+ auto g3 = a3.group;
+ assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ]));
+
+ interface I {}
+ class C : I {}
+ const C[] a4 = [new const C()];
+ auto g4 = a4.group!"a is b";
+ assert(g4.front[1] == 1);
+
+ immutable I[] a5 = [new immutable C()];
+ auto g5 = a5.group!"a is b";
+ assert(g5.front[1] == 1);
+
+ const(int[][]) a6 = [[1], [1]];
+ auto g6 = a6.group;
+ assert(equal(g6.front[0], [1]));
+}
+
+// Used by implementation of chunkBy for non-forward input ranges.
+private struct ChunkByChunkImpl(alias pred, Range)
+if (isInputRange!Range && !isForwardRange!Range)
+{
+ alias fun = binaryFun!pred;
+
+ private Range r;
+ private ElementType!Range prev;
+
+ this(Range range, ElementType!Range _prev)
+ {
+ r = range;
+ prev = _prev;
+ }
+
+ @property bool empty()
+ {
+ return r.empty || !fun(prev, r.front);
+ }
+
+ @property ElementType!Range front() { return r.front; }
+ void popFront() { r.popFront(); }
+}
+
+private template ChunkByImplIsUnary(alias pred, Range)
+{
+ static if (is(typeof(binaryFun!pred(ElementType!Range.init,
+ ElementType!Range.init)) : bool))
+ enum ChunkByImplIsUnary = false;
+ else static if (is(typeof(
+ unaryFun!pred(ElementType!Range.init) ==
+ unaryFun!pred(ElementType!Range.init))))
+ enum ChunkByImplIsUnary = true;
+ else
+ static assert(0, "chunkBy expects either a binary predicate or "~
+ "a unary predicate on range elements of type: "~
+ ElementType!Range.stringof);
+}
+
+// Implementation of chunkBy for non-forward input ranges.
+private struct ChunkByImpl(alias pred, Range)
+if (isInputRange!Range && !isForwardRange!Range)
+{
+ enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
+
+ static if (isUnary)
+ alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
+ else
+ alias eq = binaryFun!pred;
+
+ private Range r;
+ private ElementType!Range _prev;
+
+ this(Range _r)
+ {
+ r = _r;
+ if (!empty)
+ {
+ // Check reflexivity if predicate is claimed to be an equivalence
+ // relation.
+ assert(eq(r.front, r.front),
+ "predicate is not reflexive");
+
+ // _prev's type may be a nested struct, so must be initialized
+ // directly in the constructor (cannot call savePred()).
+ _prev = r.front;
+ }
+ else
+ {
+ // We won't use _prev, but must be initialized.
+ _prev = typeof(_prev).init;
+ }
+ }
+ @property bool empty() { return r.empty; }
+
+ @property auto front()
+ {
+ static if (isUnary)
+ {
+ import std.typecons : tuple;
+ return tuple(unaryFun!pred(_prev),
+ ChunkByChunkImpl!(eq, Range)(r, _prev));
+ }
+ else
+ {
+ return ChunkByChunkImpl!(eq, Range)(r, _prev);
+ }
+ }
+
+ void popFront()
+ {
+ while (!r.empty)
+ {
+ if (!eq(_prev, r.front))
+ {
+ _prev = r.front;
+ break;
+ }
+ r.popFront();
+ }
+ }
+}
+
+// Single-pass implementation of chunkBy for forward ranges.
+private struct ChunkByImpl(alias pred, Range)
+if (isForwardRange!Range)
+{
+ import std.typecons : RefCounted;
+
+ enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
+
+ static if (isUnary)
+ alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
+ else
+ alias eq = binaryFun!pred;
+
+ // Outer range
+ static struct Impl
+ {
+ size_t groupNum;
+ Range current;
+ Range next;
+ }
+
+ // Inner range
+ static struct Group
+ {
+ private size_t groupNum;
+ private Range start;
+ private Range current;
+
+ private RefCounted!Impl mothership;
+
+ this(RefCounted!Impl origin)
+ {
+ groupNum = origin.groupNum;
+
+ start = origin.current.save;
+ current = origin.current.save;
+ assert(!start.empty);
+
+ mothership = origin;
+
+ // Note: this requires reflexivity.
+ assert(eq(start.front, current.front),
+ "predicate is not reflexive");
+ }
+
+ @property bool empty() { return groupNum == size_t.max; }
+ @property auto ref front() { return current.front; }
+
+ void popFront()
+ {
+ current.popFront();
+
+ // Note: this requires transitivity.
+ if (current.empty || !eq(start.front, current.front))
+ {
+ if (groupNum == mothership.groupNum)
+ {
+ // If parent range hasn't moved on yet, help it along by
+ // saving location of start of next Group.
+ mothership.next = current.save;
+ }
+
+ groupNum = size_t.max;
+ }
+ }
+
+ @property auto save()
+ {
+ auto copy = this;
+ copy.current = current.save;
+ return copy;
+ }
+ }
+ static assert(isForwardRange!Group);
+
+ private RefCounted!Impl impl;
+
+ this(Range r)
+ {
+ impl = RefCounted!Impl(0, r, r.save);
+ }
+
+ @property bool empty() { return impl.current.empty; }
+
+ @property auto front()
+ {
+ static if (isUnary)
+ {
+ import std.typecons : tuple;
+ return tuple(unaryFun!pred(impl.current.front), Group(impl));
+ }
+ else
+ {
+ return Group(impl);
+ }
+ }
+
+ void popFront()
+ {
+ // Scan for next group. If we're lucky, one of our Groups would have
+ // already set .next to the start of the next group, in which case the
+ // loop is skipped.
+ while (!impl.next.empty && eq(impl.current.front, impl.next.front))
+ {
+ impl.next.popFront();
+ }
+
+ impl.current = impl.next.save;
+
+ // Indicate to any remaining Groups that we have moved on.
+ impl.groupNum++;
+ }
+
+ @property auto save()
+ {
+ // Note: the new copy of the range will be detached from any existing
+ // satellite Groups, and will not benefit from the .next acceleration.
+ return typeof(this)(impl.current.save);
+ }
+
+ static assert(isForwardRange!(typeof(this)));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ size_t popCount = 0;
+ class RefFwdRange
+ {
+ int[] impl;
+
+ @safe nothrow:
+
+ this(int[] data) { impl = data; }
+ @property bool empty() { return impl.empty; }
+ @property auto ref front() { return impl.front; }
+ void popFront()
+ {
+ impl.popFront();
+ popCount++;
+ }
+ @property auto save() { return new RefFwdRange(impl); }
+ }
+ static assert(isForwardRange!RefFwdRange);
+
+ auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9]);
+ auto groups = testdata.chunkBy!((a,b) => (a % 2) == (b % 2));
+ auto outerSave1 = groups.save;
+
+ // Sanity test
+ assert(groups.equal!equal([[1, 3, 5], [2, 4], [7], [6, 8], [9]]));
+ assert(groups.empty);
+
+ // Performance test for single-traversal use case: popFront should not have
+ // been called more times than there are elements if we traversed the
+ // segmented range exactly once.
+ assert(popCount == 9);
+
+ // Outer range .save test
+ groups = outerSave1.save;
+ assert(!groups.empty);
+
+ // Inner range .save test
+ auto grp1 = groups.front.save;
+ auto grp1b = grp1.save;
+ assert(grp1b.equal([1, 3, 5]));
+ assert(grp1.save.equal([1, 3, 5]));
+
+ // Inner range should remain consistent after outer range has moved on.
+ groups.popFront();
+ assert(grp1.save.equal([1, 3, 5]));
+
+ // Inner range should not be affected by subsequent inner ranges.
+ assert(groups.front.equal([2, 4]));
+ assert(grp1.save.equal([1, 3, 5]));
+}
+
+/**
+ * Chunks an input range into subranges of equivalent adjacent elements.
+ * In other languages this is often called `partitionBy`, `groupBy`
+ * or `sliceWhen`.
+ *
+ * Equivalence is defined by the predicate $(D pred), which can be either
+ * binary, which is passed to $(REF binaryFun, std,functional), or unary, which is
+ * passed to $(REF unaryFun, std,functional). In the binary form, two _range elements
+ * $(D a) and $(D b) are considered equivalent if $(D pred(a,b)) is true. In
+ * unary form, two elements are considered equivalent if $(D pred(a) == pred(b))
+ * is true.
+ *
+ * This predicate must be an equivalence relation, that is, it must be
+ * reflexive ($(D pred(x,x)) is always true), symmetric
+ * ($(D pred(x,y) == pred(y,x))), and transitive ($(D pred(x,y) && pred(y,z))
+ * implies $(D pred(x,z))). If this is not the case, the range returned by
+ * chunkBy may assert at runtime or behave erratically.
+ *
+ * Params:
+ * pred = Predicate for determining equivalence.
+ * r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be chunked.
+ *
+ * Returns: With a binary predicate, a range of ranges is returned in which
+ * all elements in a given subrange are equivalent under the given predicate.
+ * With a unary predicate, a range of tuples is returned, with the tuple
+ * consisting of the result of the unary predicate for each subrange, and the
+ * subrange itself.
+ *
+ * Notes:
+ *
+ * Equivalent elements separated by an intervening non-equivalent element will
+ * appear in separate subranges; this function only considers adjacent
+ * equivalence. Elements in the subranges will always appear in the same order
+ * they appear in the original range.
+ *
+ * See_also:
+ * $(LREF group), which collapses adjacent equivalent elements into a single
+ * element.
+ */
+auto chunkBy(alias pred, Range)(Range r)
+if (isInputRange!Range)
+{
+ return ChunkByImpl!(pred, Range)(r);
+}
+
+/// Showing usage with binary predicate:
+/*FIXME: @safe*/ @system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // Grouping by particular attribute of each element:
+ auto data = [
+ [1, 1],
+ [1, 2],
+ [2, 2],
+ [2, 3]
+ ];
+
+ auto r1 = data.chunkBy!((a,b) => a[0] == b[0]);
+ assert(r1.equal!equal([
+ [[1, 1], [1, 2]],
+ [[2, 2], [2, 3]]
+ ]));
+
+ auto r2 = data.chunkBy!((a,b) => a[1] == b[1]);
+ assert(r2.equal!equal([
+ [[1, 1]],
+ [[1, 2], [2, 2]],
+ [[2, 3]]
+ ]));
+}
+
+version (none) // this example requires support for non-equivalence relations
+@safe unittest
+{
+ // Grouping by maximum adjacent difference:
+ import std.math : abs;
+ auto r3 = [1, 3, 2, 5, 4, 9, 10].chunkBy!((a, b) => abs(a-b) < 3);
+ assert(r3.equal!equal([
+ [1, 3, 2],
+ [5, 4],
+ [9, 10]
+ ]));
+
+}
+
+/// Showing usage with unary predicate:
+/* FIXME: pure @safe nothrow*/ @system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives;
+ import std.typecons : tuple;
+
+ // Grouping by particular attribute of each element:
+ auto range =
+ [
+ [1, 1],
+ [1, 1],
+ [1, 2],
+ [2, 2],
+ [2, 3],
+ [2, 3],
+ [3, 3]
+ ];
+
+ auto byX = chunkBy!(a => a[0])(range);
+ auto expected1 =
+ [
+ tuple(1, [[1, 1], [1, 1], [1, 2]]),
+ tuple(2, [[2, 2], [2, 3], [2, 3]]),
+ tuple(3, [[3, 3]])
+ ];
+ foreach (e; byX)
+ {
+ assert(!expected1.empty);
+ assert(e[0] == expected1.front[0]);
+ assert(e[1].equal(expected1.front[1]));
+ expected1.popFront();
+ }
+
+ auto byY = chunkBy!(a => a[1])(range);
+ auto expected2 =
+ [
+ tuple(1, [[1, 1], [1, 1]]),
+ tuple(2, [[1, 2], [2, 2]]),
+ tuple(3, [[2, 3], [2, 3], [3, 3]])
+ ];
+ foreach (e; byY)
+ {
+ assert(!expected2.empty);
+ assert(e[0] == expected2.front[0]);
+ assert(e[1].equal(expected2.front[1]));
+ expected2.popFront();
+ }
+}
+
+/*FIXME: pure @safe nothrow*/ @system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ struct Item { int x, y; }
+
+ // Force R to have only an input range API with reference semantics, so
+ // that we're not unknowingly making use of array semantics outside of the
+ // range API.
+ class RefInputRange(R)
+ {
+ R data;
+ this(R _data) pure @safe nothrow { data = _data; }
+ @property bool empty() pure @safe nothrow { return data.empty; }
+ @property auto front() pure @safe nothrow { return data.front; }
+ void popFront() pure @safe nothrow { data.popFront(); }
+ }
+ auto refInputRange(R)(R range) { return new RefInputRange!R(range); }
+
+ {
+ auto arr = [ Item(1,2), Item(1,3), Item(2,3) ];
+ static assert(isForwardRange!(typeof(arr)));
+
+ auto byX = chunkBy!(a => a.x)(arr);
+ static assert(isForwardRange!(typeof(byX)));
+
+ auto byX_subrange1 = byX.front[1].save;
+ auto byX_subrange2 = byX.front[1].save;
+ static assert(isForwardRange!(typeof(byX_subrange1)));
+ static assert(isForwardRange!(typeof(byX_subrange2)));
+
+ byX.popFront();
+ assert(byX_subrange1.equal([ Item(1,2), Item(1,3) ]));
+ byX_subrange1.popFront();
+ assert(byX_subrange1.equal([ Item(1,3) ]));
+ assert(byX_subrange2.equal([ Item(1,2), Item(1,3) ]));
+
+ auto byY = chunkBy!(a => a.y)(arr);
+ static assert(isForwardRange!(typeof(byY)));
+
+ auto byY2 = byY.save;
+ static assert(is(typeof(byY) == typeof(byY2)));
+ byY.popFront();
+ assert(byY.front[0] == 3);
+ assert(byY.front[1].equal([ Item(1,3), Item(2,3) ]));
+ assert(byY2.front[0] == 2);
+ assert(byY2.front[1].equal([ Item(1,2) ]));
+ }
+
+ // Test non-forward input ranges.
+ {
+ auto range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
+ auto byX = chunkBy!(a => a.x)(range);
+ assert(byX.front[0] == 1);
+ assert(byX.front[1].equal([ Item(1,1), Item(1,2) ]));
+ byX.popFront();
+ assert(byX.front[0] == 2);
+ assert(byX.front[1].equal([ Item(2,2) ]));
+ byX.popFront();
+ assert(byX.empty);
+ assert(range.empty);
+
+ range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
+ auto byY = chunkBy!(a => a.y)(range);
+ assert(byY.front[0] == 1);
+ assert(byY.front[1].equal([ Item(1,1) ]));
+ byY.popFront();
+ assert(byY.front[0] == 2);
+ assert(byY.front[1].equal([ Item(1,2), Item(2,2) ]));
+ byY.popFront();
+ assert(byY.empty);
+ assert(range.empty);
+ }
+}
+
+// Issue 13595
+version (none) // This requires support for non-equivalence relations
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto r = [1, 2, 3, 4, 5, 6, 7, 8, 9].chunkBy!((x, y) => ((x*y) % 3) == 0);
+ assert(r.equal!equal([
+ [1],
+ [2, 3, 4],
+ [5, 6, 7],
+ [8, 9]
+ ]));
+}
+
+// Issue 13805
+@system unittest
+{
+ [""].map!((s) => s).chunkBy!((x, y) => true);
+}
+
+// joiner
+/**
+Lazily joins a range of ranges with a separator. The separator itself
+is a range. If a separator is not provided, then the ranges are
+joined directly without anything in between them (often called `flatten`
+in other languages).
+
+Params:
+ r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of input
+ ranges to be joined.
+ sep = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of
+ element(s) to serve as separators in the joined range.
+
+Returns:
+A range of elements in the joined range. This will be a forward range if
+both outer and inner ranges of $(D RoR) are forward ranges; otherwise it will
+be only an input range.
+
+See_also:
+$(REF chain, std,range), which chains a sequence of ranges with compatible elements
+into a single range.
+ */
+auto joiner(RoR, Separator)(RoR r, Separator sep)
+if (isInputRange!RoR && isInputRange!(ElementType!RoR)
+ && isForwardRange!Separator
+ && is(ElementType!Separator : ElementType!(ElementType!RoR)))
+{
+ static struct Result
+ {
+ private RoR _items;
+ private ElementType!RoR _current;
+ private Separator _sep, _currentSep;
+
+ // This is a mixin instead of a function for the following reason (as
+ // explained by Kenji Hara): "This is necessary from 2.061. If a
+ // struct has a nested struct member, it must be directly initialized
+ // in its constructor to avoid leaving undefined state. If you change
+ // setItem to a function, the initialization of _current field is
+ // wrapped into private member function, then compiler could not detect
+ // that is correctly initialized while constructing. To avoid the
+ // compiler error check, string mixin is used."
+ private enum setItem =
+ q{
+ if (!_items.empty)
+ {
+ // If we're exporting .save, we must not consume any of the
+ // subranges, since RoR.save does not guarantee that the states
+ // of the subranges are also saved.
+ static if (isForwardRange!RoR &&
+ isForwardRange!(ElementType!RoR))
+ _current = _items.front.save;
+ else
+ _current = _items.front;
+ }
+ };
+
+ private void useSeparator()
+ {
+ // Separator must always come after an item.
+ assert(_currentSep.empty && !_items.empty,
+ "joiner: internal error");
+ _items.popFront();
+
+ // If there are no more items, we're done, since separators are not
+ // terminators.
+ if (_items.empty) return;
+
+ if (_sep.empty)
+ {
+ // Advance to the next range in the
+ // input
+ while (_items.front.empty)
+ {
+ _items.popFront();
+ if (_items.empty) return;
+ }
+ mixin(setItem);
+ }
+ else
+ {
+ _currentSep = _sep.save;
+ assert(!_currentSep.empty);
+ }
+ }
+
+ private enum useItem =
+ q{
+ // FIXME: this will crash if either _currentSep or _current are
+ // class objects, because .init is null when the ctor invokes this
+ // mixin.
+ //assert(_currentSep.empty && _current.empty,
+ // "joiner: internal error");
+
+ // Use the input
+ if (_items.empty) return;
+ mixin(setItem);
+ if (_current.empty)
+ {
+ // No data in the current item - toggle to use the separator
+ useSeparator();
+ }
+ };
+
+ this(RoR items, Separator sep)
+ {
+ _items = items;
+ _sep = sep;
+
+ //mixin(useItem); // _current should be initialized in place
+ if (_items.empty)
+ _current = _current.init; // set invalid state
+ else
+ {
+ // If we're exporting .save, we must not consume any of the
+ // subranges, since RoR.save does not guarantee that the states
+ // of the subranges are also saved.
+ static if (isForwardRange!RoR &&
+ isForwardRange!(ElementType!RoR))
+ _current = _items.front.save;
+ else
+ _current = _items.front;
+
+ if (_current.empty)
+ {
+ // No data in the current item - toggle to use the separator
+ useSeparator();
+ }
+ }
+ }
+
+ @property auto empty()
+ {
+ return _items.empty;
+ }
+
+ @property ElementType!(ElementType!RoR) front()
+ {
+ if (!_currentSep.empty) return _currentSep.front;
+ assert(!_current.empty, "Attempting to fetch the front of an empty joiner.");
+ return _current.front;
+ }
+
+ void popFront()
+ {
+ assert(!_items.empty, "Attempting to popFront an empty joiner.");
+ // Using separator?
+ if (!_currentSep.empty)
+ {
+ _currentSep.popFront();
+ if (!_currentSep.empty) return;
+ mixin(useItem);
+ }
+ else
+ {
+ // we're using the range
+ _current.popFront();
+ if (!_current.empty) return;
+ useSeparator();
+ }
+ }
+
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ {
+ @property auto save()
+ {
+ Result copy = this;
+ copy._items = _items.save;
+ copy._current = _current.save;
+ copy._sep = _sep.save;
+ copy._currentSep = _currentSep.save;
+ return copy;
+ }
+ }
+ }
+ return Result(r, sep);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : text;
+
+ assert(["abc", "def"].joiner.equal("abcdef"));
+ assert(["Mary", "has", "a", "little", "lamb"]
+ .joiner("...")
+ .equal("Mary...has...a...little...lamb"));
+ assert(["", "abc"].joiner("xyz").equal("xyzabc"));
+ assert([""].joiner("xyz").equal(""));
+ assert(["", ""].joiner("xyz").equal("xyz"));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.interfaces;
+ import std.range.primitives;
+ // joiner() should work for non-forward ranges too.
+ auto r = inputRangeObject(["abc", "def"]);
+ assert(equal(joiner(r, "xyz"), "abcxyzdef"));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+
+ // Related to issue 8061
+ auto r = joiner([
+ inputRangeObject("abc"),
+ inputRangeObject("def"),
+ ], "-*-");
+
+ assert(equal(r, "abc-*-def"));
+
+ // Test case where separator is specified but is empty.
+ auto s = joiner([
+ inputRangeObject("abc"),
+ inputRangeObject("def"),
+ ], "");
+
+ assert(equal(s, "abcdef"));
+
+ // Test empty separator with some empty elements
+ auto t = joiner([
+ inputRangeObject("abc"),
+ inputRangeObject(""),
+ inputRangeObject("def"),
+ inputRangeObject(""),
+ ], "");
+
+ assert(equal(t, "abcdef"));
+
+ // Test empty elements with non-empty separator
+ auto u = joiner([
+ inputRangeObject(""),
+ inputRangeObject("abc"),
+ inputRangeObject(""),
+ inputRangeObject("def"),
+ inputRangeObject(""),
+ ], "+-");
+
+ assert(equal(u, "+-abc+-+-def+-"));
+
+ // Issue 13441: only(x) as separator
+ string[][] lines = [null];
+ lines
+ .joiner(only("b"))
+ .array();
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // Transience correctness test
+ struct TransientRange
+ {
+ @safe:
+ int[][] src;
+ int[] buf;
+
+ this(int[][] _src)
+ {
+ src = _src;
+ buf.length = 100;
+ }
+ @property bool empty() { return src.empty; }
+ @property int[] front()
+ {
+ assert(src.front.length <= buf.length);
+ buf[0 .. src.front.length] = src.front[0..$];
+ return buf[0 .. src.front.length];
+ }
+ void popFront() { src.popFront(); }
+ }
+
+ // Test embedded empty elements
+ auto tr1 = TransientRange([[], [1,2,3], [], [4]]);
+ assert(equal(joiner(tr1, [0]), [0,1,2,3,0,0,4]));
+
+ // Test trailing empty elements
+ auto tr2 = TransientRange([[], [1,2,3], []]);
+ assert(equal(joiner(tr2, [0]), [0,1,2,3,0]));
+
+ // Test no empty elements
+ auto tr3 = TransientRange([[1,2], [3,4]]);
+ assert(equal(joiner(tr3, [0,1]), [1,2,0,1,3,4]));
+
+ // Test consecutive empty elements
+ auto tr4 = TransientRange([[1,2], [], [], [], [3,4]]);
+ assert(equal(joiner(tr4, [0,1]), [1,2,0,1,0,1,0,1,0,1,3,4]));
+
+ // Test consecutive trailing empty elements
+ auto tr5 = TransientRange([[1,2], [3,4], [], []]);
+ assert(equal(joiner(tr5, [0,1]), [1,2,0,1,3,4,0,1,0,1]));
+}
+
+@safe unittest
+{
+ static assert(isInputRange!(typeof(joiner([""], ""))));
+ static assert(isForwardRange!(typeof(joiner([""], ""))));
+}
+
+/// Ditto
+auto joiner(RoR)(RoR r)
+if (isInputRange!RoR && isInputRange!(ElementType!RoR))
+{
+ static struct Result
+ {
+ private:
+ RoR _items;
+ ElementType!RoR _current;
+ enum prepare =
+ q{
+ // Skip over empty subranges.
+ if (_items.empty) return;
+ while (_items.front.empty)
+ {
+ _items.popFront();
+ if (_items.empty) return;
+ }
+ // We cannot export .save method unless we ensure subranges are not
+ // consumed when a .save'd copy of ourselves is iterated over. So
+ // we need to .save each subrange we traverse.
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ _current = _items.front.save;
+ else
+ _current = _items.front;
+ };
+ this(RoR items, ElementType!RoR current)
+ {
+ _items = items;
+ _current = current;
+ }
+ public:
+ this(RoR r)
+ {
+ _items = r;
+ //mixin(prepare); // _current should be initialized in place
+
+ // Skip over empty subranges.
+ while (!_items.empty && _items.front.empty)
+ _items.popFront();
+
+ if (_items.empty)
+ _current = _current.init; // set invalid state
+ else
+ {
+ // We cannot export .save method unless we ensure subranges are not
+ // consumed when a .save'd copy of ourselves is iterated over. So
+ // we need to .save each subrange we traverse.
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ _current = _items.front.save;
+ else
+ _current = _items.front;
+ }
+ }
+ static if (isInfinite!RoR)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property auto empty()
+ {
+ return _items.empty;
+ }
+ }
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty joiner.");
+ return _current.front;
+ }
+ void popFront()
+ {
+ assert(!_current.empty, "Attempting to popFront an empty joiner.");
+ _current.popFront();
+ if (_current.empty)
+ {
+ assert(!_items.empty);
+ _items.popFront();
+ mixin(prepare);
+ }
+ }
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ {
+ @property auto save()
+ {
+ return Result(_items.save, _current.save);
+ }
+ }
+
+ static if (hasAssignableElements!(ElementType!RoR))
+ {
+ @property void front(ElementType!(ElementType!RoR) element)
+ {
+ assert(!empty, "Attempting to assign to front of an empty joiner.");
+ _current.front = element;
+ }
+
+ @property void front(ref ElementType!(ElementType!RoR) element)
+ {
+ assert(!empty, "Attempting to assign to front of an empty joiner.");
+ _current.front = element;
+ }
+ }
+ }
+ return Result(r);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.interfaces : inputRangeObject;
+ import std.range : repeat;
+
+ static assert(isInputRange!(typeof(joiner([""]))));
+ static assert(isForwardRange!(typeof(joiner([""]))));
+ assert(equal(joiner([""]), ""));
+ assert(equal(joiner(["", ""]), ""));
+ assert(equal(joiner(["", "abc"]), "abc"));
+ assert(equal(joiner(["abc", ""]), "abc"));
+ assert(equal(joiner(["abc", "def"]), "abcdef"));
+ assert(equal(joiner(["Mary", "has", "a", "little", "lamb"]),
+ "Maryhasalittlelamb"));
+ assert(equal(joiner(repeat("abc", 3)), "abcabcabc"));
+
+ // joiner allows in-place mutation!
+ auto a = [ [1, 2, 3], [42, 43] ];
+ auto j = joiner(a);
+ j.front = 44;
+ assert(a == [ [44, 2, 3], [42, 43] ]);
+ assert(equal(j, [44, 2, 3, 42, 43]));
+}
+
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.interfaces : inputRangeObject;
+
+ // bugzilla 8240
+ assert(equal(joiner([inputRangeObject("")]), ""));
+
+ // issue 8792
+ auto b = [[1], [2], [3]];
+ auto jb = joiner(b);
+ auto js = jb.save;
+ assert(equal(jb, js));
+
+ auto js2 = jb.save;
+ jb.popFront();
+ assert(!equal(jb, js));
+ assert(equal(js2, js));
+ js.popFront();
+ assert(equal(jb, js));
+ assert(!equal(js2, js));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ struct TransientRange
+ {
+ @safe:
+ int[] _buf;
+ int[][] _values;
+ this(int[][] values)
+ {
+ _values = values;
+ _buf = new int[128];
+ }
+ @property bool empty()
+ {
+ return _values.length == 0;
+ }
+ @property auto front()
+ {
+ foreach (i; 0 .. _values.front.length)
+ {
+ _buf[i] = _values[0][i];
+ }
+ return _buf[0 .. _values.front.length];
+ }
+ void popFront()
+ {
+ _values = _values[1 .. $];
+ }
+ }
+
+ auto rr = TransientRange([[1,2], [3,4,5], [], [6,7]]);
+
+ // Can't use array() or equal() directly because they fail with transient
+ // .front.
+ int[] result;
+ foreach (c; rr.joiner())
+ {
+ result ~= c;
+ }
+
+ assert(equal(result, [1,2,3,4,5,6,7]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : algoFormat;
+
+ struct TransientRange
+ {
+ @safe:
+ dchar[] _buf;
+ dstring[] _values;
+ this(dstring[] values)
+ {
+ _buf.length = 128;
+ _values = values;
+ }
+ @property bool empty()
+ {
+ return _values.length == 0;
+ }
+ @property auto front()
+ {
+ foreach (i; 0 .. _values.front.length)
+ {
+ _buf[i] = _values[0][i];
+ }
+ return _buf[0 .. _values.front.length];
+ }
+ void popFront()
+ {
+ _values = _values[1 .. $];
+ }
+ }
+
+ auto rr = TransientRange(["abc"d, "12"d, "def"d, "34"d]);
+
+ // Can't use array() or equal() directly because they fail with transient
+ // .front.
+ dchar[] result;
+ foreach (c; rr.joiner())
+ {
+ result ~= c;
+ }
+
+ import std.conv : to;
+ assert(equal(result, "abc12def34"d),
+ //Convert to string for assert's message
+ to!string("Unexpected result: '%s'"d.algoFormat(result)));
+}
+
+// Issue 8061
+@system unittest
+{
+ import std.conv : to;
+ import std.range.interfaces;
+
+ auto r = joiner([inputRangeObject("ab"), inputRangeObject("cd")]);
+ assert(isForwardRange!(typeof(r)));
+
+ auto str = to!string(r);
+ assert(str == "abcd");
+}
+
+@safe unittest
+{
+ import std.range : repeat;
+
+ class AssignableRange
+ {
+ @safe:
+ int element;
+ @property int front()
+ {
+ return element;
+ }
+
+ enum empty = false;
+
+ void popFront()
+ {
+ }
+
+ @property void front(int newValue)
+ {
+ element = newValue;
+ }
+ }
+
+ static assert(isInputRange!AssignableRange);
+ static assert(is(ElementType!AssignableRange == int));
+ static assert(hasAssignableElements!AssignableRange);
+ static assert(!hasLvalueElements!AssignableRange);
+
+ auto range = new AssignableRange();
+ assert(range.element == 0);
+
+ auto joined = joiner(repeat(range));
+ joined.front = 5;
+ assert(range.element == 5);
+ assert(joined.front == 5);
+
+ joined.popFront;
+ int byRef = 7;
+ joined.front = byRef;
+ assert(range.element == byRef);
+ assert(joined.front == byRef);
+}
+
+/++
+Implements the homonym function (also known as $(D accumulate), $(D
+compress), $(D inject), or $(D foldl)) present in various programming
+languages of functional flavor. There is also $(LREF fold) which does
+the same thing but with the opposite parameter order.
+The call $(D reduce!(fun)(seed, range)) first assigns $(D seed) to
+an internal variable $(D result), also called the accumulator.
+Then, for each element $(D x) in $(D range), $(D result = fun(result, x))
+gets evaluated. Finally, $(D result) is returned.
+The one-argument version $(D reduce!(fun)(range))
+works similarly, but it uses the first element of the range as the
+seed (the range must be non-empty).
+
+Returns:
+ the accumulated $(D result)
+
+Params:
+ fun = one or more functions
+
+See_Also:
+ $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
+
+ $(LREF fold) is functionally equivalent to $(LREF reduce) with the argument order reversed,
+ and without the need to use $(LREF tuple) for multiple seeds. This makes it easier
+ to use in UFCS chains.
+
+ $(LREF sum) is similar to $(D reduce!((a, b) => a + b)) that offers
+ pairwise summing of floating point numbers.
++/
+template reduce(fun...)
+if (fun.length >= 1)
+{
+ import std.meta : staticMap;
+
+ alias binfuns = staticMap!(binaryFun, fun);
+ static if (fun.length > 1)
+ import std.typecons : tuple, isTuple;
+
+ /++
+ No-seed version. The first element of $(D r) is used as the seed's value.
+
+ For each function $(D f) in $(D fun), the corresponding
+ seed type $(D S) is $(D Unqual!(typeof(f(e, e)))), where $(D e) is an
+ element of $(D r): $(D ElementType!R) for ranges,
+ and $(D ForeachType!R) otherwise.
+
+ Once S has been determined, then $(D S s = e;) and $(D s = f(s, e);)
+ must both be legal.
+
+ If $(D r) is empty, an $(D Exception) is thrown.
+
+ Params:
+ r = an iterable value as defined by $(D isIterable)
+
+ Returns:
+ the final result of the accumulator applied to the iterable
+ +/
+ auto reduce(R)(R r)
+ if (isIterable!R)
+ {
+ import std.exception : enforce;
+ alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R);
+ alias Args = staticMap!(ReduceSeedType!E, binfuns);
+
+ static if (isInputRange!R)
+ {
+ enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value.");
+ Args result = r.front;
+ r.popFront();
+ return reduceImpl!false(r, result);
+ }
+ else
+ {
+ auto result = Args.init;
+ return reduceImpl!true(r, result);
+ }
+ }
+
+ /++
+ Seed version. The seed should be a single value if $(D fun) is a
+ single function. If $(D fun) is multiple functions, then $(D seed)
+ should be a $(REF Tuple, std,typecons), with one field per function in $(D f).
+
+ For convenience, if the seed is const, or has qualified fields, then
+ $(D reduce) will operate on an unqualified copy. If this happens
+ then the returned type will not perfectly match $(D S).
+
+ Use $(D fold) instead of $(D reduce) to use the seed version in a UFCS chain.
+
+ Params:
+ seed = the initial value of the accumulator
+ r = an iterable value as defined by $(D isIterable)
+
+ Returns:
+ the final result of the accumulator applied to the iterable
+ +/
+ auto reduce(S, R)(S seed, R r)
+ if (isIterable!R)
+ {
+ static if (fun.length == 1)
+ return reducePreImpl(r, seed);
+ else
+ {
+ import std.algorithm.internal : algoFormat;
+ static assert(isTuple!S, algoFormat("Seed %s should be a Tuple", S.stringof));
+ return reducePreImpl(r, seed.expand);
+ }
+ }
+
+ private auto reducePreImpl(R, Args...)(R r, ref Args args)
+ {
+ alias Result = staticMap!(Unqual, Args);
+ static if (is(Result == Args))
+ alias result = args;
+ else
+ Result result = args;
+ return reduceImpl!false(r, result);
+ }
+
+ private auto reduceImpl(bool mustInitialize, R, Args...)(R r, ref Args args)
+ if (isIterable!R)
+ {
+ import std.algorithm.internal : algoFormat;
+ static assert(Args.length == fun.length,
+ algoFormat("Seed %s does not have the correct amount of fields (should be %s)", Args.stringof, fun.length));
+ alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R);
+
+ static if (mustInitialize) bool initialized = false;
+ foreach (/+auto ref+/ E e; r) // @@@4707@@@
+ {
+ foreach (i, f; binfuns)
+ {
+ static assert(!is(typeof(f(args[i], e))) || is(typeof(args[i] = f(args[i], e))),
+ algoFormat(
+ "Incompatible function/seed/element: %s/%s/%s",
+ fullyQualifiedName!f,
+ Args[i].stringof,
+ E.stringof
+ )
+ );
+ }
+
+ static if (mustInitialize) if (initialized == false)
+ {
+ import std.conv : emplaceRef;
+ foreach (i, f; binfuns)
+ emplaceRef!(Args[i])(args[i], e);
+ initialized = true;
+ continue;
+ }
+
+ foreach (i, f; binfuns)
+ args[i] = f(args[i], e);
+ }
+ static if (mustInitialize)
+ if (!initialized)
+ throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value.");
+
+ static if (Args.length == 1)
+ return args[0];
+ else
+ return tuple(args);
+ }
+}
+
+/**
+Many aggregate range operations turn out to be solved with $(D reduce)
+quickly and easily. The example below illustrates $(D reduce)'s
+remarkable power and flexibility.
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.math : approxEqual;
+ import std.range;
+
+ int[] arr = [ 1, 2, 3, 4, 5 ];
+ // Sum all elements
+ auto sum = reduce!((a,b) => a + b)(0, arr);
+ assert(sum == 15);
+
+ // Sum again, using a string predicate with "a" and "b"
+ sum = reduce!"a + b"(0, arr);
+ assert(sum == 15);
+
+ // Compute the maximum of all elements
+ auto largest = reduce!(max)(arr);
+ assert(largest == 5);
+
+ // Max again, but with Uniform Function Call Syntax (UFCS)
+ largest = arr.reduce!(max);
+ assert(largest == 5);
+
+ // Compute the number of odd elements
+ auto odds = reduce!((a,b) => a + (b & 1))(0, arr);
+ assert(odds == 3);
+
+ // Compute the sum of squares
+ auto ssquares = reduce!((a,b) => a + b * b)(0, arr);
+ assert(ssquares == 55);
+
+ // Chain multiple ranges into seed
+ int[] a = [ 3, 4 ];
+ int[] b = [ 100 ];
+ auto r = reduce!("a + b")(chain(a, b));
+ assert(r == 107);
+
+ // Mixing convertible types is fair game, too
+ double[] c = [ 2.5, 3.0 ];
+ auto r1 = reduce!("a + b")(chain(a, b, c));
+ assert(approxEqual(r1, 112.5));
+
+ // To minimize nesting of parentheses, Uniform Function Call Syntax can be used
+ auto r2 = chain(a, b, c).reduce!("a + b");
+ assert(approxEqual(r2, 112.5));
+}
+
+/**
+Sometimes it is very useful to compute multiple aggregates in one pass.
+One advantage is that the computation is faster because the looping overhead
+is shared. That's why $(D reduce) accepts multiple functions.
+If two or more functions are passed, $(D reduce) returns a
+$(REF Tuple, std,typecons) object with one member per passed-in function.
+The number of seeds must be correspondingly increased.
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.math : approxEqual, sqrt;
+ import std.typecons : tuple, Tuple;
+
+ double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ];
+ // Compute minimum and maximum in one pass
+ auto r = reduce!(min, max)(a);
+ // The type of r is Tuple!(int, int)
+ assert(approxEqual(r[0], 2)); // minimum
+ assert(approxEqual(r[1], 11)); // maximum
+
+ // Compute sum and sum of squares in one pass
+ r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a);
+ assert(approxEqual(r[0], 35)); // sum
+ assert(approxEqual(r[1], 233)); // sum of squares
+ // Compute average and standard deviation from the above
+ auto avg = r[0] / a.length;
+ assert(avg == 5);
+ auto stdev = sqrt(r[1] / a.length - avg * avg);
+ assert(cast(int) stdev == 2);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.range : chain;
+ import std.typecons : tuple, Tuple;
+
+ double[] a = [ 3, 4 ];
+ auto r = reduce!("a + b")(0.0, a);
+ assert(r == 7);
+ r = reduce!("a + b")(a);
+ assert(r == 7);
+ r = reduce!(min)(a);
+ assert(r == 3);
+ double[] b = [ 100 ];
+ auto r1 = reduce!("a + b")(chain(a, b));
+ assert(r1 == 107);
+
+ // two funs
+ auto r2 = reduce!("a + b", "a - b")(tuple(0.0, 0.0), a);
+ assert(r2[0] == 7 && r2[1] == -7);
+ auto r3 = reduce!("a + b", "a - b")(a);
+ assert(r3[0] == 7 && r3[1] == -1);
+
+ a = [ 1, 2, 3, 4, 5 ];
+ // Stringize with commas
+ string rep = reduce!("a ~ `, ` ~ to!(string)(b)")("", a);
+ assert(rep[2 .. $] == "1, 2, 3, 4, 5", "["~rep[2 .. $]~"]");
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.exception : assertThrown;
+ import std.range : iota;
+ import std.typecons : tuple, Tuple;
+
+ // Test the opApply case.
+ static struct OpApply
+ {
+ bool actEmpty;
+
+ int opApply(scope int delegate(ref int) dg)
+ {
+ int res;
+ if (actEmpty) return res;
+
+ foreach (i; 0 .. 100)
+ {
+ res = dg(i);
+ if (res) break;
+ }
+ return res;
+ }
+ }
+
+ OpApply oa;
+ auto hundredSum = reduce!"a + b"(iota(100));
+ assert(reduce!"a + b"(5, oa) == hundredSum + 5);
+ assert(reduce!"a + b"(oa) == hundredSum);
+ assert(reduce!("a + b", max)(oa) == tuple(hundredSum, 99));
+ assert(reduce!("a + b", max)(tuple(5, 0), oa) == tuple(hundredSum + 5, 99));
+
+ // Test for throwing on empty range plus no seed.
+ assertThrown(reduce!"a + b"([1, 2][0 .. 0]));
+
+ oa.actEmpty = true;
+ assertThrown(reduce!"a + b"(oa));
+}
+
+@safe unittest
+{
+ const float a = 0.0;
+ const float[] b = [ 1.2, 3, 3.3 ];
+ float[] c = [ 1.2, 3, 3.3 ];
+ auto r = reduce!"a + b"(a, b);
+ r = reduce!"a + b"(a, c);
+ assert(r == 7.5);
+}
+
+@safe unittest
+{
+ // Issue #10408 - Two-function reduce of a const array.
+ import std.algorithm.comparison : max, min;
+ import std.typecons : tuple, Tuple;
+
+ const numbers = [10, 30, 20];
+ immutable m = reduce!(min)(numbers);
+ assert(m == 10);
+ immutable minmax = reduce!(min, max)(numbers);
+ assert(minmax == tuple(10, 30));
+}
+
+@safe unittest
+{
+ //10709
+ import std.typecons : tuple, Tuple;
+
+ enum foo = "a + 0.5 * b";
+ auto r = [0, 1, 2, 3];
+ auto r1 = reduce!foo(r);
+ auto r2 = reduce!(foo, foo)(r);
+ assert(r1 == 3);
+ assert(r2 == tuple(3, 3));
+}
+
+@system unittest
+{
+ static struct OpApply
+ {
+ int opApply(int delegate(ref int) dg)
+ {
+ int[] a = [1, 2, 3];
+
+ int res = 0;
+ foreach (ref e; a)
+ {
+ res = dg(e);
+ if (res) break;
+ }
+ return res;
+ }
+ }
+ //test CTFE and functions with context
+ int fun(int a, int b) @safe {return a + b + 1;}
+ auto foo()
+ {
+ import std.algorithm.comparison : max;
+ import std.typecons : tuple, Tuple;
+
+ auto a = reduce!(fun)([1, 2, 3]);
+ auto b = reduce!(fun, fun)([1, 2, 3]);
+ auto c = reduce!(fun)(0, [1, 2, 3]);
+ auto d = reduce!(fun, fun)(tuple(0, 0), [1, 2, 3]);
+ auto e = reduce!(fun)(0, OpApply());
+ auto f = reduce!(fun, fun)(tuple(0, 0), OpApply());
+
+ return max(a, b.expand, c, d.expand, e, f.expand);
+ }
+ auto a = foo();
+ assert(a == 9);
+ enum b = foo();
+ assert(b == 9);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.typecons : tuple, Tuple;
+
+ //http://forum.dlang.org/post/oghtttkopzjshsuflelk@forum.dlang.org
+ //Seed is tuple of const.
+ static auto minmaxElement(alias F = min, alias G = max, R)(in R range)
+ @safe pure nothrow
+ if (isInputRange!R)
+ {
+ return reduce!(F, G)(tuple(ElementType!R.max,
+ ElementType!R.min), range);
+ }
+ assert(minmaxElement([1, 2, 3]) == tuple(1, 3));
+}
+
+@safe unittest //12569
+{
+ import std.algorithm.comparison : max, min;
+ import std.typecons : tuple;
+ dchar c = 'a';
+ reduce!(min, max)(tuple(c, c), "hello"); // OK
+ static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello"))));
+ static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello"))));
+
+
+ //"Seed dchar should be a Tuple"
+ static assert(!is(typeof(reduce!(min, max)(c, "hello"))));
+ //"Seed (dchar) does not have the correct amount of fields (should be 2)"
+ static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello"))));
+ //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)"
+ static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello"))));
+ //"Incompatable function/seed/element: all(alias pred = "a")/int/dchar"
+ static assert(!is(typeof(reduce!all(1, "hello"))));
+ static assert(!is(typeof(reduce!(all, all)(tuple(1, 1), "hello"))));
+}
+
+@safe unittest //13304
+{
+ int[] data;
+ static assert(is(typeof(reduce!((a, b) => a + b)(data))));
+ assert(data.length == 0);
+}
+
+//Helper for Reduce
+private template ReduceSeedType(E)
+{
+ static template ReduceSeedType(alias fun)
+ {
+ import std.algorithm.internal : algoFormat;
+
+ alias ReduceSeedType = Unqual!(typeof(fun(lvalueOf!E, lvalueOf!E)));
+
+ //Check the Seed type is useable.
+ ReduceSeedType s = ReduceSeedType.init;
+ static assert(is(typeof({ReduceSeedType s = lvalueOf!E;})) &&
+ is(typeof(lvalueOf!ReduceSeedType = fun(lvalueOf!ReduceSeedType, lvalueOf!E))),
+ algoFormat(
+ "Unable to deduce an acceptable seed type for %s with element type %s.",
+ fullyQualifiedName!fun,
+ E.stringof
+ )
+ );
+ }
+}
+
+
+/++
+Implements the homonym function (also known as $(D accumulate), $(D
+compress), $(D inject), or $(D foldl)) present in various programming
+languages of functional flavor. The call $(D fold!(fun)(range, seed))
+first assigns $(D seed) to an internal variable $(D result),
+also called the accumulator. Then, for each element $(D x) in $(D
+range), $(D result = fun(result, x)) gets evaluated. Finally, $(D
+result) is returned. The one-argument version $(D fold!(fun)(range))
+works similarly, but it uses the first element of the range as the
+seed (the range must be non-empty).
+
+Returns:
+ the accumulated $(D result)
+
+See_Also:
+ $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
+
+ $(LREF sum) is similar to $(D fold!((a, b) => a + b)) that offers
+ precise summing of floating point numbers.
+
+ This is functionally equivalent to $(LREF reduce) with the argument order reversed,
+ and without the need to use $(LREF tuple) for multiple seeds.
++/
+template fold(fun...)
+if (fun.length >= 1)
+{
+ auto fold(R, S...)(R r, S seed)
+ {
+ static if (S.length < 2)
+ {
+ return reduce!fun(seed, r);
+ }
+ else
+ {
+ import std.typecons : tuple;
+ return reduce!fun(tuple(seed), r);
+ }
+ }
+}
+
+///
+@safe pure unittest
+{
+ immutable arr = [1, 2, 3, 4, 5];
+
+ // Sum all elements
+ assert(arr.fold!((a, b) => a + b) == 15);
+
+ // Sum all elements with explicit seed
+ assert(arr.fold!((a, b) => a + b)(6) == 21);
+
+ import std.algorithm.comparison : min, max;
+ import std.typecons : tuple;
+
+ // Compute minimum and maximum at the same time
+ assert(arr.fold!(min, max) == tuple(1, 5));
+
+ // Compute minimum and maximum at the same time with seeds
+ assert(arr.fold!(min, max)(0, 7) == tuple(0, 7));
+
+ // Can be used in a UFCS chain
+ assert(arr.map!(a => a + 1).fold!((a, b) => a + b) == 20);
+
+ // Return the last element of any range
+ assert(arr.fold!((a, b) => b) == 5);
+}
+
+@safe @nogc pure nothrow unittest
+{
+ int[1] arr;
+ static assert(!is(typeof(arr.fold!())));
+ static assert(!is(typeof(arr.fold!(a => a))));
+ static assert(is(typeof(arr.fold!((a, b) => a))));
+ static assert(is(typeof(arr.fold!((a, b) => a)(1))));
+ assert(arr.length == 1);
+}
+
+/++
+Similar to `fold`, but returns a range containing the successive reduced values.
+The call $(D cumulativeFold!(fun)(range, seed)) first assigns `seed` to an
+internal variable `result`, also called the accumulator.
+The returned range contains the values $(D result = fun(result, x)) lazily
+evaluated for each element `x` in `range`. Finally, the last element has the
+same value as $(D fold!(fun)(seed, range)).
+The one-argument version $(D cumulativeFold!(fun)(range)) works similarly, but
+it returns the first element unchanged and uses it as seed for the next
+elements.
+This function is also known as
+ $(HTTP en.cppreference.com/w/cpp/algorithm/partial_sum, partial_sum),
+ $(HTTP docs.python.org/3/library/itertools.html#itertools.accumulate, accumulate),
+ $(HTTP hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#v:scanl, scan),
+ $(HTTP mathworld.wolfram.com/CumulativeSum.html, Cumulative Sum).
+
+Params:
+ fun = one or more functions to use as fold operation
+
+Returns:
+ The function returns a range containing the consecutive reduced values. If
+ there is more than one `fun`, the element type will be $(REF Tuple,
+ std,typecons) containing one element for each `fun`.
+
+See_Also:
+ $(HTTP en.wikipedia.org/wiki/Prefix_sum, Prefix Sum)
++/
+template cumulativeFold(fun...)
+if (fun.length >= 1)
+{
+ import std.meta : staticMap;
+ private alias binfuns = staticMap!(binaryFun, fun);
+
+ /++
+ No-seed version. The first element of `r` is used as the seed's value.
+ For each function `f` in `fun`, the corresponding seed type `S` is
+ $(D Unqual!(typeof(f(e, e)))), where `e` is an element of `r`:
+ `ElementType!R`.
+ Once `S` has been determined, then $(D S s = e;) and $(D s = f(s, e);) must
+ both be legal.
+
+ Params:
+ range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ Returns:
+ a range containing the consecutive reduced values.
+ +/
+ auto cumulativeFold(R)(R range)
+ if (isInputRange!(Unqual!R))
+ {
+ return cumulativeFoldImpl(range);
+ }
+
+ /++
+ Seed version. The seed should be a single value if `fun` is a single
+ function. If `fun` is multiple functions, then `seed` should be a
+ $(REF Tuple, std,typecons), with one field per function in `f`.
+ For convenience, if the seed is `const`, or has qualified fields, then
+ `cumulativeFold` will operate on an unqualified copy. If this happens
+ then the returned type will not perfectly match `S`.
+
+ Params:
+ range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ seed = the initial value of the accumulator
+ Returns:
+ a range containing the consecutive reduced values.
+ +/
+ auto cumulativeFold(R, S)(R range, S seed)
+ if (isInputRange!(Unqual!R))
+ {
+ static if (fun.length == 1)
+ return cumulativeFoldImpl(range, seed);
+ else
+ return cumulativeFoldImpl(range, seed.expand);
+ }
+
+ private auto cumulativeFoldImpl(R, Args...)(R range, ref Args args)
+ {
+ import std.algorithm.internal : algoFormat;
+
+ static assert(Args.length == 0 || Args.length == fun.length,
+ algoFormat("Seed %s does not have the correct amount of fields (should be %s)",
+ Args.stringof, fun.length));
+
+ static if (args.length)
+ alias State = staticMap!(Unqual, Args);
+ else
+ alias State = staticMap!(ReduceSeedType!(ElementType!R), binfuns);
+
+ foreach (i, f; binfuns)
+ {
+ static assert(!__traits(compiles, f(args[i], e)) || __traits(compiles,
+ { args[i] = f(args[i], e); }()),
+ algoFormat("Incompatible function/seed/element: %s/%s/%s",
+ fullyQualifiedName!f, Args[i].stringof, E.stringof));
+ }
+
+ static struct Result
+ {
+ private:
+ R source;
+ State state;
+
+ this(R range, ref Args args)
+ {
+ source = range;
+ if (source.empty)
+ return;
+
+ foreach (i, f; binfuns)
+ {
+ static if (args.length)
+ state[i] = f(args[i], source.front);
+ else
+ state[i] = source.front;
+ }
+ }
+
+ public:
+ @property bool empty()
+ {
+ return source.empty;
+ }
+
+ @property auto front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty cumulativeFold.");
+ static if (fun.length > 1)
+ {
+ import std.typecons : tuple;
+ return tuple(state);
+ }
+ else
+ {
+ return state[0];
+ }
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty cumulativeFold.");
+ source.popFront;
+
+ if (source.empty)
+ return;
+
+ foreach (i, f; binfuns)
+ state[i] = f(state[i], source.front);
+ }
+
+ static if (isForwardRange!R)
+ {
+ @property auto save()
+ {
+ auto result = this;
+ result.source = source.save;
+ return result;
+ }
+ }
+
+ static if (hasLength!R)
+ {
+ @property size_t length()
+ {
+ return source.length;
+ }
+ }
+ }
+
+ return Result(range, args);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.array : array;
+ import std.math : approxEqual;
+ import std.range : chain;
+
+ int[] arr = [1, 2, 3, 4, 5];
+ // Partial sum of all elements
+ auto sum = cumulativeFold!((a, b) => a + b)(arr, 0);
+ assert(sum.array == [1, 3, 6, 10, 15]);
+
+ // Partial sum again, using a string predicate with "a" and "b"
+ auto sum2 = cumulativeFold!"a + b"(arr, 0);
+ assert(sum2.array == [1, 3, 6, 10, 15]);
+
+ // Compute the partial maximum of all elements
+ auto largest = cumulativeFold!max(arr);
+ assert(largest.array == [1, 2, 3, 4, 5]);
+
+ // Partial max again, but with Uniform Function Call Syntax (UFCS)
+ largest = arr.cumulativeFold!max;
+ assert(largest.array == [1, 2, 3, 4, 5]);
+
+ // Partial count of odd elements
+ auto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0);
+ assert(odds.array == [1, 1, 2, 2, 3]);
+
+ // Compute the partial sum of squares
+ auto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0);
+ assert(ssquares.array == [1, 5, 14, 30, 55]);
+
+ // Chain multiple ranges into seed
+ int[] a = [3, 4];
+ int[] b = [100];
+ auto r = cumulativeFold!"a + b"(chain(a, b));
+ assert(r.array == [3, 7, 107]);
+
+ // Mixing convertible types is fair game, too
+ double[] c = [2.5, 3.0];
+ auto r1 = cumulativeFold!"a + b"(chain(a, b, c));
+ assert(approxEqual(r1, [3, 7, 107, 109.5, 112.5]));
+
+ // To minimize nesting of parentheses, Uniform Function Call Syntax can be used
+ auto r2 = chain(a, b, c).cumulativeFold!"a + b";
+ assert(approxEqual(r2, [3, 7, 107, 109.5, 112.5]));
+}
+
+/**
+Sometimes it is very useful to compute multiple aggregates in one pass.
+One advantage is that the computation is faster because the looping overhead
+is shared. That's why `cumulativeFold` accepts multiple functions.
+If two or more functions are passed, `cumulativeFold` returns a $(REF Tuple,
+std,typecons) object with one member per passed-in function.
+The number of seeds must be correspondingly increased.
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.algorithm.iteration : map;
+ import std.math : approxEqual;
+ import std.typecons : tuple;
+
+ double[] a = [3.0, 4, 7, 11, 3, 2, 5];
+ // Compute minimum and maximum in one pass
+ auto r = a.cumulativeFold!(min, max);
+ // The type of r is Tuple!(int, int)
+ assert(approxEqual(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2])); // minimum
+ assert(approxEqual(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum
+
+ // Compute sum and sum of squares in one pass
+ auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0));
+ assert(approxEqual(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35])); // sum
+ assert(approxEqual(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal, max, min;
+ import std.conv : to;
+ import std.range : chain;
+ import std.typecons : tuple;
+
+ double[] a = [3, 4];
+ auto r = a.cumulativeFold!("a + b")(0.0);
+ assert(r.equal([3, 7]));
+ auto r2 = cumulativeFold!("a + b")(a);
+ assert(r2.equal([3, 7]));
+ auto r3 = cumulativeFold!(min)(a);
+ assert(r3.equal([3, 3]));
+ double[] b = [100];
+ auto r4 = cumulativeFold!("a + b")(chain(a, b));
+ assert(r4.equal([3, 7, 107]));
+
+ // two funs
+ auto r5 = cumulativeFold!("a + b", "a - b")(a, tuple(0.0, 0.0));
+ assert(r5.equal([tuple(3, -3), tuple(7, -7)]));
+ auto r6 = cumulativeFold!("a + b", "a - b")(a);
+ assert(r6.equal([tuple(3, 3), tuple(7, -1)]));
+
+ a = [1, 2, 3, 4, 5];
+ // Stringize with commas
+ auto rep = cumulativeFold!("a ~ `, ` ~ to!string(b)")(a, "");
+ assert(rep.map!"a[2 .. $]".equal(["1", "1, 2", "1, 2, 3", "1, 2, 3, 4", "1, 2, 3, 4, 5"]));
+
+ // Test for empty range
+ a = [];
+ assert(a.cumulativeFold!"a + b".empty);
+ assert(a.cumulativeFold!"a + b"(2.0).empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.array : array;
+ import std.math : approxEqual;
+ import std.typecons : tuple;
+
+ const float a = 0.0;
+ const float[] b = [1.2, 3, 3.3];
+ float[] c = [1.2, 3, 3.3];
+
+ auto r = cumulativeFold!"a + b"(b, a);
+ assert(approxEqual(r, [1.2, 4.2, 7.5]));
+
+ auto r2 = cumulativeFold!"a + b"(c, a);
+ assert(approxEqual(r2, [1.2, 4.2, 7.5]));
+
+ const numbers = [10, 30, 20];
+ enum m = numbers.cumulativeFold!(min).array;
+ assert(m == [10, 10, 10]);
+ enum minmax = numbers.cumulativeFold!(min, max).array;
+ assert(minmax == [tuple(10, 10), tuple(10, 30), tuple(10, 30)]);
+}
+
+@safe unittest
+{
+ import std.math : approxEqual;
+ import std.typecons : tuple;
+
+ enum foo = "a + 0.5 * b";
+ auto r = [0, 1, 2, 3];
+ auto r1 = r.cumulativeFold!foo;
+ auto r2 = r.cumulativeFold!(foo, foo);
+ assert(approxEqual(r1, [0, 0.5, 1.5, 3]));
+ assert(approxEqual(r2.map!"a[0]", [0, 0.5, 1.5, 3]));
+ assert(approxEqual(r2.map!"a[1]", [0, 0.5, 1.5, 3]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal, max, min;
+ import std.array : array;
+ import std.typecons : tuple;
+
+ //Seed is tuple of const.
+ static auto minmaxElement(alias F = min, alias G = max, R)(in R range)
+ @safe pure nothrow
+ if (isInputRange!R)
+ {
+ return range.cumulativeFold!(F, G)(tuple(ElementType!R.max, ElementType!R.min));
+ }
+
+ assert(minmaxElement([1, 2, 3]).equal([tuple(1, 1), tuple(1, 2), tuple(1, 3)]));
+}
+
+@safe unittest //12569
+{
+ import std.algorithm.comparison : equal, max, min;
+ import std.typecons : tuple;
+
+ dchar c = 'a';
+
+ assert(cumulativeFold!(min, max)("hello", tuple(c, c)).equal([tuple('a', 'h'),
+ tuple('a', 'h'), tuple('a', 'l'), tuple('a', 'l'), tuple('a', 'o')]));
+ static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c))));
+ static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c))));
+
+ //"Seed dchar should be a Tuple"
+ static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", c)));
+ //"Seed (dchar) does not have the correct amount of fields (should be 2)"
+ static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c))));
+ //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)"
+ static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c))));
+ //"Incompatable function/seed/element: all(alias pred = "a")/int/dchar"
+ static assert(!__traits(compiles, cumulativeFold!all("hello", 1)));
+ static assert(!__traits(compiles, cumulativeFold!(all, all)("hello", tuple(1, 1))));
+}
+
+@safe unittest //13304
+{
+ int[] data;
+ assert(data.cumulativeFold!((a, b) => a + b).empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : AllDummyRanges, propagatesLength,
+ propagatesRangeType, RangeType;
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ auto m = d.cumulativeFold!"a * b";
+
+ static assert(propagatesLength!(typeof(m), DummyType));
+ static if (DummyType.rt <= RangeType.Forward)
+ static assert(propagatesRangeType!(typeof(m), DummyType));
+
+ assert(m.equal([1, 2, 6, 24, 120, 720, 5040, 40_320, 362_880, 3_628_800]));
+ }
+}
+
+// splitter
+/**
+Lazily splits a range using an element as a separator. This can be used with
+any narrow string type or sliceable range type, but is most popular with string
+types.
+
+Two adjacent separators are considered to surround an empty element in
+the split range. Use $(D filter!(a => !a.empty)) on the result to compress
+empty elements.
+
+The predicate is passed to $(REF binaryFun, std,functional), and can either accept
+a string, or any callable that can be executed via $(D pred(element, s)).
+
+If the empty range is given, the result is a range with one empty
+element. If a range with one separator is given, the result is a range
+with two empty elements.
+
+If splitting a string on whitespace and token compression is desired,
+consider using $(D splitter) without specifying a separator (see fourth overload
+below).
+
+Params:
+ pred = The predicate for comparing each element with the separator,
+ defaulting to $(D "a == b").
+ r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be
+ split. Must support slicing and $(D .length).
+ s = The element to be treated as the separator between range segments to be
+ split.
+
+Constraints:
+ The predicate $(D pred) needs to accept an element of $(D r) and the
+ separator $(D s).
+
+Returns:
+ An input range of the subranges of elements between separators. If $(D r)
+ is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
+ the returned range will be likewise.
+
+See_Also:
+ $(REF _splitter, std,regex) for a version that splits using a regular
+expression defined separator.
+*/
+auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
+if (is(typeof(binaryFun!pred(r.front, s)) : bool)
+ && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range))
+{
+ import std.algorithm.searching : find;
+ import std.conv : unsigned;
+
+ static struct Result
+ {
+ private:
+ Range _input;
+ Separator _separator;
+ // Do we need hasLength!Range? popFront uses _input.length...
+ enum size_t _unComputed = size_t.max - 1, _atEnd = size_t.max;
+ size_t _frontLength = _unComputed;
+ size_t _backLength = _unComputed;
+
+ static if (isNarrowString!Range)
+ {
+ size_t _separatorLength;
+ }
+ else
+ {
+ enum _separatorLength = 1;
+ }
+
+ static if (isBidirectionalRange!Range)
+ {
+ static size_t lastIndexOf(Range haystack, Separator needle)
+ {
+ import std.range : retro;
+ auto r = haystack.retro().find!pred(needle);
+ return r.retro().length - 1;
+ }
+ }
+
+ public:
+ this(Range input, Separator separator)
+ {
+ _input = input;
+ _separator = separator;
+
+ static if (isNarrowString!Range)
+ {
+ import std.utf : codeLength;
+
+ _separatorLength = codeLength!(ElementEncodingType!Range)(separator);
+ }
+ if (_input.empty)
+ _frontLength = _atEnd;
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _frontLength == _atEnd;
+ }
+ }
+
+ @property Range front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty splitter.");
+ if (_frontLength == _unComputed)
+ {
+ auto r = _input.find!pred(_separator);
+ _frontLength = _input.length - r.length;
+ }
+ return _input[0 .. _frontLength];
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty splitter.");
+ if (_frontLength == _unComputed)
+ {
+ front;
+ }
+ assert(_frontLength <= _input.length);
+ if (_frontLength == _input.length)
+ {
+ // no more input and need to fetch => done
+ _frontLength = _atEnd;
+
+ // Probably don't need this, but just for consistency:
+ _backLength = _atEnd;
+ }
+ else
+ {
+ _input = _input[_frontLength + _separatorLength .. _input.length];
+ _frontLength = _unComputed;
+ }
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ return ret;
+ }
+ }
+
+ static if (isBidirectionalRange!Range)
+ {
+ @property Range back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty splitter.");
+ if (_backLength == _unComputed)
+ {
+ immutable lastIndex = lastIndexOf(_input, _separator);
+ if (lastIndex == -1)
+ {
+ _backLength = _input.length;
+ }
+ else
+ {
+ _backLength = _input.length - lastIndex - 1;
+ }
+ }
+ return _input[_input.length - _backLength .. _input.length];
+ }
+
+ void popBack()
+ {
+ assert(!empty, "Attempting to popBack an empty splitter.");
+ if (_backLength == _unComputed)
+ {
+ // evaluate back to make sure it's computed
+ back;
+ }
+ assert(_backLength <= _input.length);
+ if (_backLength == _input.length)
+ {
+ // no more input and need to fetch => done
+ _frontLength = _atEnd;
+ _backLength = _atEnd;
+ }
+ else
+ {
+ _input = _input[0 .. _input.length - _backLength - _separatorLength];
+ _backLength = _unComputed;
+ }
+ }
+ }
+ }
+
+ return Result(r, s);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ]));
+ int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+ int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
+ assert(equal(splitter(a, 0), w));
+ a = [ 0 ];
+ assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ]));
+ a = [ 0, 1 ];
+ assert(equal(splitter(a, 0), [ [], [1] ]));
+ w = [ [0], [1], [2] ];
+ assert(equal(splitter!"a.front == b"(w, 1), [ [[0]], [[2]] ]));
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ import std.array : array;
+ import std.internal.test.dummyrange;
+ import std.range : retro;
+
+ assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ]));
+ assert(equal(splitter("žlutoučkýřkůň", 'ř'), [ "žlutoučký", "kůň" ]));
+ int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+ int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
+ static assert(isForwardRange!(typeof(splitter(a, 0))));
+
+ assert(equal(splitter(a, 0), w));
+ a = null;
+ assert(equal(splitter(a, 0), (int[][]).init));
+ a = [ 0 ];
+ assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ][]));
+ a = [ 0, 1 ];
+ assert(equal(splitter(a, 0), [ [], [1] ][]));
+
+ // Thoroughly exercise the bidirectional stuff.
+ auto str = "abc abcd abcde ab abcdefg abcdefghij ab ac ar an at ada";
+ assert(equal(
+ retro(splitter(str, 'a')),
+ retro(array(splitter(str, 'a')))
+ ));
+
+ // Test interleaving front and back.
+ auto split = splitter(str, 'a');
+ assert(split.front == "");
+ assert(split.back == "");
+ split.popBack();
+ assert(split.back == "d");
+ split.popFront();
+ assert(split.front == "bc ");
+ assert(split.back == "d");
+ split.popFront();
+ split.popBack();
+ assert(split.back == "t ");
+ split.popBack();
+ split.popBack();
+ split.popFront();
+ split.popFront();
+ assert(split.front == "b ");
+ assert(split.back == "r ");
+
+ foreach (DummyType; AllDummyRanges) { // Bug 4408
+ static if (isRandomAccessRange!DummyType)
+ {
+ static assert(isBidirectionalRange!DummyType);
+ DummyType d;
+ auto s = splitter(d, 5);
+ assert(equal(s.front, [1,2,3,4]));
+ assert(equal(s.back, [6,7,8,9,10]));
+
+ auto s2 = splitter(d, [4, 5]);
+ assert(equal(s2.front, [1,2,3]));
+ }
+ }
+}
+@safe unittest
+{
+ import std.algorithm;
+ import std.range;
+ auto L = retro(iota(1L, 10L));
+ auto s = splitter(L, 5L);
+ assert(equal(s.front, [9L, 8L, 7L, 6L]));
+ s.popFront();
+ assert(equal(s.front, [4L, 3L, 2L, 1L]));
+ s.popFront();
+ assert(s.empty);
+}
+
+/**
+Similar to the previous overload of $(D splitter), except this one uses another
+range as a separator. This can be used with any narrow string type or sliceable
+range type, but is most popular with string types. The predicate is passed to
+$(REF binaryFun, std,functional), and can either accept a string, or any callable
+that can be executed via $(D pred(r.front, s.front)).
+
+Two adjacent separators are considered to surround an empty element in
+the split range. Use $(D filter!(a => !a.empty)) on the result to compress
+empty elements.
+
+Params:
+ pred = The predicate for comparing each element with the separator,
+ defaulting to $(D "a == b").
+ r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be
+ split.
+ s = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to
+ be treated as the separator between segments of $(D r) to be split.
+
+Constraints:
+ The predicate $(D pred) needs to accept an element of $(D r) and an
+ element of $(D s).
+
+Returns:
+ An input range of the subranges of elements between separators. If $(D r)
+ is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
+ the returned range will be likewise.
+
+See_Also: $(REF _splitter, std,regex) for a version that splits using a regular
+expression defined separator.
+ */
+auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s)
+if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
+ && (hasSlicing!Range || isNarrowString!Range)
+ && isForwardRange!Separator
+ && (hasLength!Separator || isNarrowString!Separator))
+{
+ import std.algorithm.searching : find;
+ import std.conv : unsigned;
+
+ static struct Result
+ {
+ private:
+ Range _input;
+ Separator _separator;
+ // _frontLength == size_t.max means empty
+ size_t _frontLength = size_t.max;
+ static if (isBidirectionalRange!Range)
+ size_t _backLength = size_t.max;
+
+ @property auto separatorLength() { return _separator.length; }
+
+ void ensureFrontLength()
+ {
+ if (_frontLength != _frontLength.max) return;
+ assert(!_input.empty);
+ // compute front length
+ _frontLength = (_separator.empty) ? 1 :
+ _input.length - find!pred(_input, _separator).length;
+ static if (isBidirectionalRange!Range)
+ if (_frontLength == _input.length) _backLength = _frontLength;
+ }
+
+ void ensureBackLength()
+ {
+ static if (isBidirectionalRange!Range)
+ if (_backLength != _backLength.max) return;
+ assert(!_input.empty);
+ // compute back length
+ static if (isBidirectionalRange!Range && isBidirectionalRange!Separator)
+ {
+ import std.range : retro;
+ _backLength = _input.length -
+ find!pred(retro(_input), retro(_separator)).source.length;
+ }
+ }
+
+ public:
+ this(Range input, Separator separator)
+ {
+ _input = input;
+ _separator = separator;
+ }
+
+ @property Range front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty splitter.");
+ ensureFrontLength();
+ return _input[0 .. _frontLength];
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false; // Propagate infiniteness
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _frontLength == size_t.max && _input.empty;
+ }
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty splitter.");
+ ensureFrontLength();
+ if (_frontLength == _input.length)
+ {
+ // done, there's no separator in sight
+ _input = _input[_frontLength .. _frontLength];
+ _frontLength = _frontLength.max;
+ static if (isBidirectionalRange!Range)
+ _backLength = _backLength.max;
+ return;
+ }
+ if (_frontLength + separatorLength == _input.length)
+ {
+ // Special case: popping the first-to-last item; there is
+ // an empty item right after this.
+ _input = _input[_input.length .. _input.length];
+ _frontLength = 0;
+ static if (isBidirectionalRange!Range)
+ _backLength = 0;
+ return;
+ }
+ // Normal case, pop one item and the separator, get ready for
+ // reading the next item
+ _input = _input[_frontLength + separatorLength .. _input.length];
+ // mark _frontLength as uninitialized
+ _frontLength = _frontLength.max;
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ return ret;
+ }
+ }
+ }
+
+ return Result(r, s);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(equal(splitter("hello world", " "), [ "hello", "world" ]));
+ int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+ int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
+ assert(equal(splitter(a, [0, 0]), w));
+ a = [ 0, 0 ];
+ assert(equal(splitter(a, [0, 0]), [ (int[]).init, (int[]).init ]));
+ a = [ 0, 0, 1 ];
+ assert(equal(splitter(a, [0, 0]), [ [], [1] ]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : Tuple;
+
+ alias C = Tuple!(int, "x", int, "y");
+ auto a = [C(1,0), C(2,0), C(3,1), C(4,0)];
+ assert(equal(splitter!"a.x == b"(a, [2, 3]), [ [C(1,0)], [C(4,0)] ]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.array : split;
+ import std.conv : text;
+
+ auto s = ",abc, de, fg,hi,";
+ auto sp0 = splitter(s, ',');
+ assert(equal(sp0, ["", "abc", " de", " fg", "hi", ""][]));
+
+ auto s1 = ", abc, de, fg, hi, ";
+ auto sp1 = splitter(s1, ", ");
+ assert(equal(sp1, ["", "abc", "de", " fg", "hi", ""][]));
+ static assert(isForwardRange!(typeof(sp1)));
+
+ int[] a = [ 1, 2, 0, 3, 0, 4, 5, 0 ];
+ int[][] w = [ [1, 2], [3], [4, 5], [] ];
+ uint i;
+ foreach (e; splitter(a, 0))
+ {
+ assert(i < w.length);
+ assert(e == w[i++]);
+ }
+ assert(i == w.length);
+ // // Now go back
+ // auto s2 = splitter(a, 0);
+
+ // foreach (e; retro(s2))
+ // {
+ // assert(i > 0);
+ // assert(equal(e, w[--i]), text(e));
+ // }
+ // assert(i == 0);
+
+ wstring names = ",peter,paul,jerry,";
+ auto words = split(names, ",");
+ assert(walkLength(words) == 5, text(walkLength(words)));
+}
+
+@safe unittest
+{
+ int[][] a = [ [1], [2], [0], [3], [0], [4], [5], [0] ];
+ int[][][] w = [ [[1], [2]], [[3]], [[4], [5]], [] ];
+ uint i;
+ foreach (e; splitter!"a.front == 0"(a, 0))
+ {
+ assert(i < w.length);
+ assert(e == w[i++]);
+ }
+ assert(i == w.length);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ auto s6 = ",";
+ auto sp6 = splitter(s6, ',');
+ foreach (e; sp6) {}
+ assert(equal(sp6, ["", ""][]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // Issue 10773
+ auto s = splitter("abc", "");
+ assert(s.equal(["a", "b", "c"]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // Test by-reference separator
+ class RefSep {
+ @safe:
+ string _impl;
+ this(string s) { _impl = s; }
+ @property empty() { return _impl.empty; }
+ @property auto front() { return _impl.front; }
+ void popFront() { _impl = _impl[1..$]; }
+ @property RefSep save() { return new RefSep(_impl); }
+ @property auto length() { return _impl.length; }
+ }
+ auto sep = new RefSep("->");
+ auto data = "i->am->pointing";
+ auto words = splitter(data, sep);
+ assert(words.equal([ "i", "am", "pointing" ]));
+}
+
+/**
+
+Similar to the previous overload of $(D splitter), except this one does not use a separator.
+Instead, the predicate is an unary function on the input range's element type.
+The $(D isTerminator) predicate is passed to $(REF unaryFun, std,functional) and can
+either accept a string, or any callable that can be executed via $(D pred(element, s)).
+
+Two adjacent separators are considered to surround an empty element in
+the split range. Use $(D filter!(a => !a.empty)) on the result to compress
+empty elements.
+
+Params:
+ isTerminator = The predicate for deciding where to split the range.
+ input = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
+ be split.
+
+Constraints:
+ The predicate $(D isTerminator) needs to accept an element of $(D input).
+
+Returns:
+ An input range of the subranges of elements between separators. If $(D input)
+ is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
+ the returned range will be likewise.
+
+See_Also: $(REF _splitter, std,regex) for a version that splits using a regular
+expression defined separator.
+ */
+auto splitter(alias isTerminator, Range)(Range input)
+if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(input.front))))
+{
+ return SplitterResult!(unaryFun!isTerminator, Range)(input);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : front;
+
+ assert(equal(splitter!(a => a == ' ')("hello world"), [ "hello", "", "world" ]));
+ int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+ int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
+ assert(equal(splitter!(a => a == 0)(a), w));
+ a = [ 0 ];
+ assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ]));
+ a = [ 0, 1 ];
+ assert(equal(splitter!(a => a == 0)(a), [ [], [1] ]));
+ w = [ [0], [1], [2] ];
+ assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));
+}
+
+private struct SplitterResult(alias isTerminator, Range)
+{
+ import std.algorithm.searching : find;
+ enum fullSlicing = (hasLength!Range && hasSlicing!Range) || isSomeString!Range;
+
+ private Range _input;
+ private size_t _end = 0;
+ static if (!fullSlicing)
+ private Range _next;
+
+ private void findTerminator()
+ {
+ static if (fullSlicing)
+ {
+ auto r = find!isTerminator(_input.save);
+ _end = _input.length - r.length;
+ }
+ else
+ for ( _end = 0; !_next.empty ; _next.popFront)
+ {
+ if (isTerminator(_next.front))
+ break;
+ ++_end;
+ }
+ }
+
+ this(Range input)
+ {
+ _input = input;
+ static if (!fullSlicing)
+ _next = _input.save;
+
+ if (!_input.empty)
+ findTerminator();
+ else
+ _end = size_t.max;
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false; // Propagate infiniteness.
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _end == size_t.max;
+ }
+ }
+
+ @property auto front()
+ {
+ version (assert)
+ {
+ import core.exception : RangeError;
+ if (empty)
+ throw new RangeError();
+ }
+ static if (fullSlicing)
+ return _input[0 .. _end];
+ else
+ {
+ import std.range : takeExactly;
+ return _input.takeExactly(_end);
+ }
+ }
+
+ void popFront()
+ {
+ version (assert)
+ {
+ import core.exception : RangeError;
+ if (empty)
+ throw new RangeError();
+ }
+
+ static if (fullSlicing)
+ {
+ _input = _input[_end .. _input.length];
+ if (_input.empty)
+ {
+ _end = size_t.max;
+ return;
+ }
+ _input.popFront();
+ }
+ else
+ {
+ if (_next.empty)
+ {
+ _input = _next;
+ _end = size_t.max;
+ return;
+ }
+ _next.popFront();
+ _input = _next.save;
+ }
+ findTerminator();
+ }
+
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ static if (!fullSlicing)
+ ret._next = _next.save;
+ return ret;
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ auto L = iota(1L, 10L);
+ auto s = splitter(L, [5L, 6L]);
+ assert(equal(s.front, [1L, 2L, 3L, 4L]));
+ s.popFront();
+ assert(equal(s.front, [7L, 8L, 9L]));
+ s.popFront();
+ assert(s.empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : algoFormat;
+ import std.internal.test.dummyrange;
+
+ void compare(string sentence, string[] witness)
+ {
+ auto r = splitter!"a == ' '"(sentence);
+ assert(equal(r.save, witness), algoFormat("got: %(%s, %) expected: %(%s, %)", r, witness));
+ }
+
+ compare(" Mary has a little lamb. ",
+ ["", "Mary", "", "has", "a", "little", "lamb.", "", "", ""]);
+ compare("Mary has a little lamb. ",
+ ["Mary", "", "has", "a", "little", "lamb.", "", "", ""]);
+ compare("Mary has a little lamb.",
+ ["Mary", "", "has", "a", "little", "lamb."]);
+ compare("", (string[]).init);
+ compare(" ", ["", ""]);
+
+ static assert(isForwardRange!(typeof(splitter!"a == ' '"("ABC"))));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ static if (isRandomAccessRange!DummyType)
+ {
+ auto rangeSplit = splitter!"a == 5"(DummyType.init);
+ assert(equal(rangeSplit.front, [1,2,3,4]));
+ rangeSplit.popFront();
+ assert(equal(rangeSplit.front, [6,7,8,9,10]));
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : algoFormat;
+ import std.range;
+
+ struct Entry
+ {
+ int low;
+ int high;
+ int[][] result;
+ }
+ Entry[] entries = [
+ Entry(0, 0, []),
+ Entry(0, 1, [[0]]),
+ Entry(1, 2, [[], []]),
+ Entry(2, 7, [[2], [4], [6]]),
+ Entry(1, 8, [[], [2], [4], [6], []]),
+ ];
+ foreach ( entry ; entries )
+ {
+ auto a = iota(entry.low, entry.high).filter!"true"();
+ auto b = splitter!"a%2"(a);
+ assert(equal!equal(b.save, entry.result), algoFormat("got: %(%s, %) expected: %(%s, %)", b, entry.result));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.uni : isWhite;
+
+ //@@@6791@@@
+ assert(equal(
+ splitter("là dove terminava quella valle"),
+ ["là", "dove", "terminava", "quella", "valle"]
+ ));
+ assert(equal(
+ splitter!(std.uni.isWhite)("là dove terminava quella valle"),
+ ["là", "dove", "terminava", "quella", "valle"]
+ ));
+ assert(equal(splitter!"a=='本'"("日本語"), ["日", "語"]));
+}
+
+/++
+Lazily splits the string $(D s) into words, using whitespace as the delimiter.
+
+This function is string specific and, contrary to
+$(D splitter!(std.uni.isWhite)), runs of whitespace will be merged together
+(no empty tokens will be produced).
+
+Params:
+ s = The string to be split.
+
+Returns:
+ An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of slices of
+ the original string split by whitespace.
+ +/
+auto splitter(C)(C[] s)
+if (isSomeChar!C)
+{
+ import std.algorithm.searching : find;
+ static struct Result
+ {
+ private:
+ import core.exception : RangeError;
+ C[] _s;
+ size_t _frontLength;
+
+ void getFirst() pure @safe
+ {
+ import std.uni : isWhite;
+
+ auto r = find!(isWhite)(_s);
+ _frontLength = _s.length - r.length;
+ }
+
+ public:
+ this(C[] s) pure @safe
+ {
+ import std.string : strip;
+ _s = s.strip();
+ getFirst();
+ }
+
+ @property C[] front() pure @safe
+ {
+ version (assert) if (empty) throw new RangeError();
+ return _s[0 .. _frontLength];
+ }
+
+ void popFront() pure @safe
+ {
+ import std.string : stripLeft;
+ version (assert) if (empty) throw new RangeError();
+ _s = _s[_frontLength .. $].stripLeft();
+ getFirst();
+ }
+
+ @property bool empty() const @safe pure nothrow
+ {
+ return _s.empty;
+ }
+
+ @property inout(Result) save() inout @safe pure nothrow
+ {
+ return this;
+ }
+ }
+ return Result(s);
+}
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = " a bcd ef gh ";
+ assert(equal(splitter(a), ["a", "bcd", "ef", "gh"][]));
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.meta : AliasSeq;
+ foreach (S; AliasSeq!(string, wstring, dstring))
+ {
+ import std.conv : to;
+ S a = " a bcd ef gh ";
+ assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")]));
+ a = "";
+ assert(splitter(a).empty);
+ }
+
+ immutable string s = " a bcd ef gh ";
+ assert(equal(splitter(s), ["a", "bcd", "ef", "gh"][]));
+}
+
+@safe unittest
+{
+ import std.conv : to;
+ import std.string : strip;
+
+ // TDPL example, page 8
+ uint[string] dictionary;
+ char[][3] lines;
+ lines[0] = "line one".dup;
+ lines[1] = "line \ttwo".dup;
+ lines[2] = "yah last line\ryah".dup;
+ foreach (line; lines)
+ {
+ foreach (word; splitter(strip(line)))
+ {
+ if (word in dictionary) continue; // Nothing to do
+ auto newID = dictionary.length;
+ dictionary[to!string(word)] = cast(uint) newID;
+ }
+ }
+ assert(dictionary.length == 5);
+ assert(dictionary["line"]== 0);
+ assert(dictionary["one"]== 1);
+ assert(dictionary["two"]== 2);
+ assert(dictionary["yah"]== 3);
+ assert(dictionary["last"]== 4);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : algoFormat;
+ import std.array : split;
+ import std.conv : text;
+
+ // Check consistency:
+ // All flavors of split should produce the same results
+ foreach (input; [(int[]).init,
+ [0],
+ [0, 1, 0],
+ [1, 1, 0, 0, 1, 1],
+ ])
+ {
+ foreach (s; [0, 1])
+ {
+ auto result = split(input, s);
+
+ assert(equal(result, split(input, [s])), algoFormat(`"[%(%s,%)]"`, split(input, [s])));
+ //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented
+ assert(equal(result, split!((a) => a == s)(input)), text(split!((a) => a == s)(input)));
+
+ //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented
+ //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented
+ //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
+ assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"())));
+
+ assert(equal(result, splitter(input, s)));
+ assert(equal(result, splitter(input, [s])));
+ //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented
+ assert(equal(result, splitter!((a) => a == s)(input)));
+
+ //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented
+ //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented
+ //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
+ assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"())));
+ }
+ }
+ foreach (input; [string.init,
+ " ",
+ " hello ",
+ "hello hello",
+ " hello what heck this ? "
+ ])
+ {
+ foreach (s; [' ', 'h'])
+ {
+ auto result = split(input, s);
+
+ assert(equal(result, split(input, [s])));
+ //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented
+ assert(equal(result, split!((a) => a == s)(input)));
+
+ //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented
+ //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented
+ //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
+ assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"())));
+
+ assert(equal(result, splitter(input, s)));
+ assert(equal(result, splitter(input, [s])));
+ //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented
+ assert(equal(result, splitter!((a) => a == s)(input)));
+
+ //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented
+ //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented
+ //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
+ assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"())));
+ }
+ }
+}
+
+// sum
+/**
+Sums elements of $(D r), which must be a finite
+$(REF_ALTTEXT input range, isInputRange, std,range,primitives). Although
+conceptually $(D sum(r)) is equivalent to $(LREF fold)!((a, b) => a +
+b)(r, 0), $(D sum) uses specialized algorithms to maximize accuracy,
+as follows.
+
+$(UL
+$(LI If $(D $(REF ElementType, std,range,primitives)!R) is a floating-point
+type and $(D R) is a
+$(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) with
+length and slicing, then $(D sum) uses the
+$(HTTP en.wikipedia.org/wiki/Pairwise_summation, pairwise summation)
+algorithm.)
+$(LI If $(D ElementType!R) is a floating-point type and $(D R) is a
+finite input range (but not a random-access range with slicing), then
+$(D sum) uses the $(HTTP en.wikipedia.org/wiki/Kahan_summation,
+Kahan summation) algorithm.)
+$(LI In all other cases, a simple element by element addition is done.)
+)
+
+For floating point inputs, calculations are made in
+$(DDLINK spec/type, Types, $(D real))
+precision for $(D real) inputs and in $(D double) precision otherwise
+(Note this is a special case that deviates from $(D fold)'s behavior,
+which would have kept $(D float) precision for a $(D float) range).
+For all other types, the calculations are done in the same type obtained
+from from adding two elements of the range, which may be a different
+type from the elements themselves (for example, in case of
+$(DDSUBLINK spec/type,integer-promotions, integral promotion)).
+
+A seed may be passed to $(D sum). Not only will this seed be used as an initial
+value, but its type will override all the above, and determine the algorithm
+and precision used for summation.
+
+Note that these specialized summing algorithms execute more primitive operations
+than vanilla summation. Therefore, if in certain cases maximum speed is required
+at expense of precision, one can use $(D fold!((a, b) => a + b)(r, 0)), which
+is not specialized for summation.
+
+Params:
+ seed = the initial value of the summation
+ r = a finite input range
+
+Returns:
+ The sum of all the elements in the range r.
+ */
+auto sum(R)(R r)
+if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front)))
+{
+ alias E = Unqual!(ElementType!R);
+ static if (isFloatingPoint!E)
+ alias Seed = typeof(E.init + 0.0); //biggest of double/real
+ else
+ alias Seed = typeof(r.front + r.front);
+ return sum(r, Unqual!Seed(0));
+}
+/// ditto
+auto sum(R, E)(R r, E seed)
+if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front)))
+{
+ static if (isFloatingPoint!E)
+ {
+ static if (hasLength!R && hasSlicing!R)
+ {
+ if (r.empty) return seed;
+ return seed + sumPairwise!E(r);
+ }
+ else
+ return sumKahan!E(seed, r);
+ }
+ else
+ {
+ return reduce!"a + b"(seed, r);
+ }
+}
+
+// Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation
+private auto sumPairwise(F, R)(R data)
+if (isInputRange!R && !isInfinite!R)
+{
+ import core.bitop : bsf;
+ // Works for r with at least length < 2^^(64 + log2(16)), in keeping with the use of size_t
+ // elsewhere in std.algorithm and std.range on 64 bit platforms. The 16 in log2(16) comes
+ // from the manual unrolling in sumPairWise16
+ F[64] store = void;
+ size_t idx = 0;
+
+ void collapseStore(T)(T k)
+ {
+ auto lastToKeep = idx - cast(uint) bsf(k+1);
+ while (idx > lastToKeep)
+ {
+ store[idx - 1] += store[idx];
+ --idx;
+ }
+ }
+
+ static if (hasLength!R)
+ {
+ foreach (k; 0 .. data.length / 16)
+ {
+ static if (isRandomAccessRange!R && hasSlicing!R)
+ {
+ store[idx] = sumPairwise16!F(data);
+ data = data[16 .. data.length];
+ }
+ else store[idx] = sumPairwiseN!(16, false, F)(data);
+
+ collapseStore(k);
+ ++idx;
+ }
+
+ size_t i = 0;
+ foreach (el; data)
+ {
+ store[idx] = el;
+ collapseStore(i);
+ ++idx;
+ ++i;
+ }
+ }
+ else
+ {
+ size_t k = 0;
+ while (!data.empty)
+ {
+ store[idx] = sumPairwiseN!(16, true, F)(data);
+ collapseStore(k);
+ ++idx;
+ ++k;
+ }
+ }
+
+ F s = store[idx - 1];
+ foreach_reverse (j; 0 .. idx - 1)
+ s += store[j];
+
+ return s;
+}
+
+private auto sumPairwise16(F, R)(R r)
+if (isRandomAccessRange!R)
+{
+ return (((cast(F) r[ 0] + r[ 1]) + (cast(F) r[ 2] + r[ 3]))
+ + ((cast(F) r[ 4] + r[ 5]) + (cast(F) r[ 6] + r[ 7])))
+ + (((cast(F) r[ 8] + r[ 9]) + (cast(F) r[10] + r[11]))
+ + ((cast(F) r[12] + r[13]) + (cast(F) r[14] + r[15])));
+}
+
+private auto sumPair(bool needEmptyChecks, F, R)(ref R r)
+if (isForwardRange!R && !isRandomAccessRange!R)
+{
+ static if (needEmptyChecks) if (r.empty) return F(0);
+ F s0 = r.front;
+ r.popFront();
+ static if (needEmptyChecks) if (r.empty) return s0;
+ s0 += r.front;
+ r.popFront();
+ return s0;
+}
+
+private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r)
+if (isForwardRange!R && !isRandomAccessRange!R)
+{
+ import std.math : isPowerOf2;
+ static assert(isPowerOf2(N));
+ static if (N == 2) return sumPair!(needEmptyChecks, F)(r);
+ else return sumPairwiseN!(N/2, needEmptyChecks, F)(r)
+ + sumPairwiseN!(N/2, needEmptyChecks, F)(r);
+}
+
+// Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm
+private auto sumKahan(Result, R)(Result result, R r)
+{
+ static assert(isFloatingPoint!Result && isMutable!Result);
+ Result c = 0;
+ for (; !r.empty; r.popFront())
+ {
+ immutable y = r.front - c;
+ immutable t = result + y;
+ c = (t - result) - y;
+ result = t;
+ }
+ return result;
+}
+
+/// Ditto
+@safe pure nothrow unittest
+{
+ import std.range;
+
+ //simple integral sumation
+ assert(sum([ 1, 2, 3, 4]) == 10);
+
+ //with integral promotion
+ assert(sum([false, true, true, false, true]) == 3);
+ assert(sum(ubyte.max.repeat(100)) == 25500);
+
+ //The result may overflow
+ assert(uint.max.repeat(3).sum() == 4294967293U );
+ //But a seed can be used to change the sumation primitive
+ assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL);
+
+ //Floating point sumation
+ assert(sum([1.0, 2.0, 3.0, 4.0]) == 10);
+
+ //Floating point operations have double precision minimum
+ static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double));
+ assert(sum([1F, 2, 3, 4]) == 10);
+
+ //Force pair-wise floating point sumation on large integers
+ import std.math : approxEqual;
+ assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0)
+ .approxEqual((ulong.max / 2) * 4096.0 + 4096^^2 / 2));
+}
+
+@safe pure nothrow unittest
+{
+ static assert(is(typeof(sum([cast( byte) 1])) == int));
+ static assert(is(typeof(sum([cast(ubyte) 1])) == int));
+ static assert(is(typeof(sum([ 1, 2, 3, 4])) == int));
+ static assert(is(typeof(sum([ 1U, 2U, 3U, 4U])) == uint));
+ static assert(is(typeof(sum([ 1L, 2L, 3L, 4L])) == long));
+ static assert(is(typeof(sum([1UL, 2UL, 3UL, 4UL])) == ulong));
+
+ int[] empty;
+ assert(sum(empty) == 0);
+ assert(sum([42]) == 42);
+ assert(sum([42, 43]) == 42 + 43);
+ assert(sum([42, 43, 44]) == 42 + 43 + 44);
+ assert(sum([42, 43, 44, 45]) == 42 + 43 + 44 + 45);
+}
+
+@safe pure nothrow unittest
+{
+ static assert(is(typeof(sum([1.0, 2.0, 3.0, 4.0])) == double));
+ static assert(is(typeof(sum([ 1F, 2F, 3F, 4F])) == double));
+ const(float[]) a = [1F, 2F, 3F, 4F];
+ assert(sum(a) == 10F);
+ static assert(is(typeof(sum(a)) == double));
+
+ double[] empty;
+ assert(sum(empty) == 0);
+ assert(sum([42.]) == 42);
+ assert(sum([42., 43.]) == 42 + 43);
+ assert(sum([42., 43., 44.]) == 42 + 43 + 44);
+ assert(sum([42., 43., 44., 45.5]) == 42 + 43 + 44 + 45.5);
+}
+
+@safe pure nothrow unittest
+{
+ import std.container;
+ static assert(is(typeof(sum(SList!float()[])) == double));
+ static assert(is(typeof(sum(SList!double()[])) == double));
+ static assert(is(typeof(sum(SList!real()[])) == real));
+
+ assert(sum(SList!double()[]) == 0);
+ assert(sum(SList!double(1)[]) == 1);
+ assert(sum(SList!double(1, 2)[]) == 1 + 2);
+ assert(sum(SList!double(1, 2, 3)[]) == 1 + 2 + 3);
+ assert(sum(SList!double(1, 2, 3, 4)[]) == 10);
+}
+
+@safe pure nothrow unittest // 12434
+{
+ immutable a = [10, 20];
+ auto s1 = sum(a);
+ assert(s1 == 30);
+ auto s2 = a.map!(x => x).sum;
+ assert(s2 == 30);
+}
+
+@system unittest
+{
+ import std.bigint;
+ import std.range;
+
+ immutable BigInt[] a = BigInt("1_000_000_000_000_000_000").repeat(10).array();
+ immutable ulong[] b = (ulong.max/2).repeat(10).array();
+ auto sa = a.sum();
+ auto sb = b.sum(BigInt(0)); //reduce ulongs into bigint
+ assert(sa == BigInt("10_000_000_000_000_000_000"));
+ assert(sb == (BigInt(ulong.max/2) * 10));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.range;
+ foreach (n; iota(50))
+ assert(repeat(1.0, n).sum == n);
+}
+
+// uniq
+/**
+Lazily iterates unique consecutive elements of the given range (functionality
+akin to the $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system
+utility). Equivalence of elements is assessed by using the predicate
+$(D pred), by default $(D "a == b"). The predicate is passed to
+$(REF binaryFun, std,functional), and can either accept a string, or any callable
+that can be executed via $(D pred(element, element)). If the given range is
+bidirectional, $(D uniq) also yields a
+$(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives).
+
+Params:
+ pred = Predicate for determining equivalence between range elements.
+ r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
+ elements to filter.
+
+Returns:
+ An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
+ consecutively unique elements in the original range. If $(D r) is also a
+ forward range or bidirectional range, the returned range will be likewise.
+*/
+auto uniq(alias pred = "a == b", Range)(Range r)
+if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool))
+{
+ return UniqResult!(binaryFun!pred, Range)(r);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.mutation : copy;
+
+ int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
+ assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][]));
+
+ // Filter duplicates in-place using copy
+ arr.length -= arr.uniq().copy(arr).length;
+ assert(arr == [ 1, 2, 3, 4, 5 ]);
+
+ // Note that uniqueness is only determined consecutively; duplicated
+ // elements separated by an intervening different element will not be
+ // eliminated:
+ assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
+}
+
+private struct UniqResult(alias pred, Range)
+{
+ Range _input;
+
+ this(Range input)
+ {
+ _input = input;
+ }
+
+ auto opSlice()
+ {
+ return this;
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty uniq.");
+ auto last = _input.front;
+ do
+ {
+ _input.popFront();
+ }
+ while (!_input.empty && pred(last, _input.front));
+ }
+
+ @property ElementType!Range front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty uniq.");
+ return _input.front;
+ }
+
+ static if (isBidirectionalRange!Range)
+ {
+ void popBack()
+ {
+ assert(!empty, "Attempting to popBack an empty uniq.");
+ auto last = _input.back;
+ do
+ {
+ _input.popBack();
+ }
+ while (!_input.empty && pred(last, _input.back));
+ }
+
+ @property ElementType!Range back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty uniq.");
+ return _input.back;
+ }
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false; // Propagate infiniteness.
+ }
+ else
+ {
+ @property bool empty() { return _input.empty; }
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property typeof(this) save() {
+ return typeof(this)(_input.save);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+ import std.range;
+
+ int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
+ auto r = uniq(arr);
+ static assert(isForwardRange!(typeof(r)));
+
+ assert(equal(r, [ 1, 2, 3, 4, 5 ][]));
+ assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][])));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ auto u = uniq(d);
+ assert(equal(u, [1,2,3,4,5,6,7,8,9,10]));
+
+ static assert(d.rt == RangeType.Input || isForwardRange!(typeof(u)));
+
+ static if (d.rt >= RangeType.Bidirectional)
+ {
+ assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1]));
+ }
+ }
+}
+
+@safe unittest // https://issues.dlang.org/show_bug.cgi?id=17264
+{
+ import std.algorithm.comparison : equal;
+
+ const(int)[] var = [0, 1, 1, 2];
+ assert(var.uniq.equal([0, 1, 2]));
+}
+
+/**
+Lazily computes all _permutations of $(D r) using $(HTTP
+en.wikipedia.org/wiki/Heap%27s_algorithm, Heap's algorithm).
+
+Returns:
+A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+the elements of which are an $(REF indexed, std,range) view into $(D r).
+
+See_Also:
+$(REF nextPermutation, std,algorithm,sorting).
+*/
+Permutations!Range permutations(Range)(Range r)
+if (isRandomAccessRange!Range && hasLength!Range)
+{
+ return typeof(return)(r);
+}
+
+/// ditto
+struct Permutations(Range)
+if (isRandomAccessRange!Range && hasLength!Range)
+{
+ private size_t[] _indices, _state;
+ private Range _r;
+ private bool _empty;
+
+ ///
+ this(Range r)
+ {
+ import std.array : array;
+ import std.range : iota;
+
+ this._r = r;
+ _state = r.length ? new size_t[r.length-1] : null;
+ _indices = iota(size_t(r.length)).array;
+ _empty = r.length == 0;
+ }
+
+ ///
+ @property bool empty() const pure nothrow @safe @nogc
+ {
+ return _empty;
+ }
+
+ ///
+ @property auto front()
+ {
+ import std.range : indexed;
+ return _r.indexed(_indices);
+ }
+
+ ///
+ void popFront()
+ {
+ void next(int n)
+ {
+ import std.algorithm.mutation : swap;
+
+ if (n > _indices.length)
+ {
+ _empty = true;
+ return;
+ }
+
+ if (n % 2 == 1)
+ swap(_indices[0], _indices[n-1]);
+ else
+ swap(_indices[_state[n-2]], _indices[n-1]);
+
+ if (++_state[n-2] == n)
+ {
+ _state[n-2] = 0;
+ next(n+1);
+ }
+ }
+
+ next(2);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+ assert(equal!equal(iota(3).permutations,
+ [[0, 1, 2],
+ [1, 0, 2],
+ [2, 0, 1],
+ [0, 2, 1],
+ [1, 2, 0],
+ [2, 1, 0]]));
+}
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);
+}
diff --git a/libphobos/src/std/algorithm/package.d b/libphobos/src/std/algorithm/package.d
new file mode 100644
index 0000000..4c9a72f
--- /dev/null
+++ b/libphobos/src/std/algorithm/package.d
@@ -0,0 +1,198 @@
+// Written in the D programming language.
+
+/**
+This package implements generic algorithms oriented towards the processing of
+sequences. Sequences processed by these functions define range-based
+interfaces. See also $(MREF_ALTTEXT Reference on ranges, std, range) and
+$(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges).
+
+$(SCRIPT inhibitQuickIndex = 1;)
+
+Algorithms are categorized into the following submodules:
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Submodule) $(TH Functions)
+)
+$(TR
+ $(TDNW $(SUBMODULE Searching, searching))
+ $(TD
+ $(SUBREF searching, all)
+ $(SUBREF searching, any)
+ $(SUBREF searching, balancedParens)
+ $(SUBREF searching, boyerMooreFinder)
+ $(SUBREF searching, canFind)
+ $(SUBREF searching, commonPrefix)
+ $(SUBREF searching, count)
+ $(SUBREF searching, countUntil)
+ $(SUBREF searching, endsWith)
+ $(SUBREF searching, find)
+ $(SUBREF searching, findAdjacent)
+ $(SUBREF searching, findAmong)
+ $(SUBREF searching, findSkip)
+ $(SUBREF searching, findSplit)
+ $(SUBREF searching, findSplitAfter)
+ $(SUBREF searching, findSplitBefore)
+ $(SUBREF searching, minCount)
+ $(SUBREF searching, maxCount)
+ $(SUBREF searching, minElement)
+ $(SUBREF searching, maxElement)
+ $(SUBREF searching, minIndex)
+ $(SUBREF searching, maxIndex)
+ $(SUBREF searching, minPos)
+ $(SUBREF searching, maxPos)
+ $(SUBREF searching, skipOver)
+ $(SUBREF searching, startsWith)
+ $(SUBREF searching, until)
+ )
+)
+$(TR
+ $(TDNW $(SUBMODULE Comparison, comparison))
+ $(TD
+ $(SUBREF comparison, among)
+ $(SUBREF comparison, castSwitch)
+ $(SUBREF comparison, clamp)
+ $(SUBREF comparison, cmp)
+ $(SUBREF comparison, either)
+ $(SUBREF comparison, equal)
+ $(SUBREF comparison, isPermutation)
+ $(SUBREF comparison, isSameLength)
+ $(SUBREF comparison, levenshteinDistance)
+ $(SUBREF comparison, levenshteinDistanceAndPath)
+ $(SUBREF comparison, max)
+ $(SUBREF comparison, min)
+ $(SUBREF comparison, mismatch)
+ $(SUBREF comparison, predSwitch)
+ )
+)
+$(TR
+ $(TDNW $(SUBMODULE Iteration, iteration))
+ $(TD
+ $(SUBREF iteration, cache)
+ $(SUBREF iteration, cacheBidirectional)
+ $(SUBREF iteration, chunkBy)
+ $(SUBREF iteration, cumulativeFold)
+ $(SUBREF iteration, each)
+ $(SUBREF iteration, filter)
+ $(SUBREF iteration, filterBidirectional)
+ $(SUBREF iteration, fold)
+ $(SUBREF iteration, group)
+ $(SUBREF iteration, joiner)
+ $(SUBREF iteration, map)
+ $(SUBREF iteration, permutations)
+ $(SUBREF iteration, reduce)
+ $(SUBREF iteration, splitter)
+ $(SUBREF iteration, sum)
+ $(SUBREF iteration, uniq)
+ )
+)
+$(TR
+ $(TDNW $(SUBMODULE Sorting, sorting))
+ $(TD
+ $(SUBREF sorting, completeSort)
+ $(SUBREF sorting, isPartitioned)
+ $(SUBREF sorting, isSorted)
+ $(SUBREF sorting, isStrictlyMonotonic)
+ $(SUBREF sorting, ordered)
+ $(SUBREF sorting, strictlyOrdered)
+ $(SUBREF sorting, makeIndex)
+ $(SUBREF sorting, merge)
+ $(SUBREF sorting, multiSort)
+ $(SUBREF sorting, nextEvenPermutation)
+ $(SUBREF sorting, nextPermutation)
+ $(SUBREF sorting, partialSort)
+ $(SUBREF sorting, partition)
+ $(SUBREF sorting, partition3)
+ $(SUBREF sorting, schwartzSort)
+ $(SUBREF sorting, sort)
+ $(SUBREF sorting, topN)
+ $(SUBREF sorting, topNCopy)
+ $(SUBREF sorting, topNIndex)
+ )
+)
+$(TR
+ $(TDNW Set operations $(BR)($(SUBMODULE setops, setops)))
+ $(TD
+ $(SUBREF setops, cartesianProduct)
+ $(SUBREF setops, largestPartialIntersection)
+ $(SUBREF setops, largestPartialIntersectionWeighted)
+ $(SUBREF setops, multiwayMerge)
+ $(SUBREF setops, multiwayUnion)
+ $(SUBREF setops, setDifference)
+ $(SUBREF setops, setIntersection)
+ $(SUBREF setops, setSymmetricDifference)
+ )
+)
+$(TR
+ $(TDNW $(SUBMODULE Mutation, mutation))
+ $(TD
+ $(SUBREF mutation, bringToFront)
+ $(SUBREF mutation, copy)
+ $(SUBREF mutation, fill)
+ $(SUBREF mutation, initializeAll)
+ $(SUBREF mutation, move)
+ $(SUBREF mutation, moveAll)
+ $(SUBREF mutation, moveSome)
+ $(SUBREF mutation, moveEmplace)
+ $(SUBREF mutation, moveEmplaceAll)
+ $(SUBREF mutation, moveEmplaceSome)
+ $(SUBREF mutation, remove)
+ $(SUBREF mutation, reverse)
+ $(SUBREF mutation, strip)
+ $(SUBREF mutation, stripLeft)
+ $(SUBREF mutation, stripRight)
+ $(SUBREF mutation, swap)
+ $(SUBREF mutation, swapRanges)
+ $(SUBREF mutation, uninitializedFill)
+ )
+)
+))
+
+Many functions in this package are parameterized with a $(GLOSSARY predicate).
+The predicate may be any suitable callable type
+(a function, a delegate, a $(GLOSSARY functor), or a lambda), or a
+compile-time string. The string may consist of $(B any) legal D
+expression that uses the symbol $(D a) (for unary functions) or the
+symbols $(D a) and $(D b) (for binary functions). These names will NOT
+interfere with other homonym symbols in user code because they are
+evaluated in a different context. The default for all binary
+comparison predicates is $(D "a == b") for unordered operations and
+$(D "a < b") for ordered operations.
+
+Example:
+
+----
+int[] a = ...;
+static bool greater(int a, int b)
+{
+ return a > b;
+}
+sort!greater(a); // predicate as alias
+sort!((a, b) => a > b)(a); // predicate as a lambda.
+sort!"a > b"(a); // predicate as string
+ // (no ambiguity with array name)
+sort(a); // no predicate, "a < b" is implicit
+----
+
+Macros:
+SUBMODULE = $(MREF_ALTTEXT $1, std, algorithm, $2)
+SUBREF = $(REF_ALTTEXT $(TT $2), $2, std, algorithm, $1)$(NBSP)
+
+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/package.d)
+ */
+module std.algorithm;
+
+public import std.algorithm.comparison;
+public import std.algorithm.iteration;
+public import std.algorithm.mutation;
+public import std.algorithm.searching;
+public import std.algorithm.setops;
+public import std.algorithm.sorting;
+
+static import std.functional;
diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d
new file mode 100644
index 0000000..6468a87
--- /dev/null
+++ b/libphobos/src/std/algorithm/searching.d
@@ -0,0 +1,4600 @@
+// Written in the D programming language.
+/**
+This is a submodule of $(MREF std, algorithm).
+It contains generic _searching algorithms.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE Cheat Sheet,
+$(TR $(TH Function Name) $(TH Description))
+$(T2 all,
+ $(D all!"a > 0"([1, 2, 3, 4])) returns $(D true) because all elements
+ are positive)
+$(T2 any,
+ $(D any!"a > 0"([1, 2, -3, -4])) returns $(D true) because at least one
+ element is positive)
+$(T2 balancedParens,
+ $(D balancedParens("((1 + 1) / 2)")) returns $(D true) because the
+ string has balanced parentheses.)
+$(T2 boyerMooreFinder,
+ $(D find("hello world", boyerMooreFinder("or"))) returns $(D "orld")
+ using the $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm,
+ Boyer-Moore _algorithm).)
+$(T2 canFind,
+ $(D canFind("hello world", "or")) returns $(D true).)
+$(T2 count,
+ Counts elements that are equal to a specified value or satisfy a
+ predicate. $(D count([1, 2, 1], 1)) returns $(D 2) and
+ $(D count!"a < 0"([1, -3, 0])) returns $(D 1).)
+$(T2 countUntil,
+ $(D countUntil(a, b)) returns the number of steps taken in $(D a) to
+ reach $(D b); for example, $(D countUntil("hello!", "o")) returns
+ $(D 4).)
+$(T2 commonPrefix,
+ $(D commonPrefix("parakeet", "parachute")) returns $(D "para").)
+$(T2 endsWith,
+ $(D endsWith("rocks", "ks")) returns $(D true).)
+$(T2 find,
+ $(D find("hello world", "or")) returns $(D "orld") using linear search.
+ (For binary search refer to $(REF sortedRange, std,range).))
+$(T2 findAdjacent,
+ $(D findAdjacent([1, 2, 3, 3, 4])) returns the subrange starting with
+ two equal adjacent elements, i.e. $(D [3, 3, 4]).)
+$(T2 findAmong,
+ $(D findAmong("abcd", "qcx")) returns $(D "cd") because $(D 'c') is
+ among $(D "qcx").)
+$(T2 findSkip,
+ If $(D a = "abcde"), then $(D findSkip(a, "x")) returns $(D false) and
+ leaves $(D a) unchanged, whereas $(D findSkip(a, "c")) advances $(D a)
+ to $(D "de") and returns $(D true).)
+$(T2 findSplit,
+ $(D findSplit("abcdefg", "de")) returns the three ranges $(D "abc"),
+ $(D "de"), and $(D "fg").)
+$(T2 findSplitAfter,
+ $(D findSplitAfter("abcdefg", "de")) returns the two ranges
+ $(D "abcde") and $(D "fg").)
+$(T2 findSplitBefore,
+ $(D findSplitBefore("abcdefg", "de")) returns the two ranges $(D "abc")
+ and $(D "defg").)
+$(T2 minCount,
+ $(D minCount([2, 1, 1, 4, 1])) returns $(D tuple(1, 3)).)
+$(T2 maxCount,
+ $(D maxCount([2, 4, 1, 4, 1])) returns $(D tuple(4, 2)).)
+$(T2 minElement,
+ Selects the minimal element of a range.
+ `minElement([3, 4, 1, 2])` returns `1`.)
+$(T2 maxElement,
+ Selects the maximal element of a range.
+ `maxElement([3, 4, 1, 2])` returns `4`.)
+$(T2 minIndex,
+ Index of the minimal element of a range.
+ `minElement([3, 4, 1, 2])` returns `2`.)
+$(T2 maxIndex,
+ Index of the maximal element of a range.
+ `maxElement([3, 4, 1, 2])` returns `1`.)
+$(T2 minPos,
+ $(D minPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [1, 3, 4, 1]),
+ i.e., positions the range at the first occurrence of its minimal
+ element.)
+$(T2 maxPos,
+ $(D maxPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [4, 1]),
+ i.e., positions the range at the first occurrence of its maximal
+ element.)
+$(T2 mismatch,
+ $(D mismatch("parakeet", "parachute")) returns the two ranges
+ $(D "keet") and $(D "chute").)
+$(T2 skipOver,
+ Assume $(D a = "blah"). Then $(D skipOver(a, "bi")) leaves $(D a)
+ unchanged and returns $(D false), whereas $(D skipOver(a, "bl"))
+ advances $(D a) to refer to $(D "ah") and returns $(D true).)
+$(T2 startsWith,
+ $(D startsWith("hello, world", "hello")) returns $(D true).)
+$(T2 until,
+ Lazily iterates a range until a specific value is found.)
+)
+
+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/_searching.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.searching;
+
+// FIXME
+import std.functional; // : unaryFun, binaryFun;
+import std.range.primitives;
+import std.traits;
+// FIXME
+import std.typecons; // : Tuple, Flag, Yes, No;
+
+/++
+Checks if $(I _all) of the elements verify $(D pred).
+ +/
+template all(alias pred = "a")
+{
+ /++
+ Returns $(D true) if and only if $(I _all) values $(D v) found in the
+ input _range $(D range) satisfy the predicate $(D pred).
+ Performs (at most) $(BIGOH range.length) evaluations of $(D pred).
+ +/
+ bool all(Range)(Range range)
+ if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))))
+ {
+ import std.functional : not;
+
+ return find!(not!(unaryFun!pred))(range).empty;
+ }
+}
+
+///
+@safe unittest
+{
+ assert( all!"a & 1"([1, 3, 5, 7, 9]));
+ assert(!all!"a & 1"([1, 2, 3, 5, 7, 9]));
+}
+
+/++
+$(D all) can also be used without a predicate, if its items can be
+evaluated to true or false in a conditional statement. This can be a
+convenient way to quickly evaluate that $(I _all) of the elements of a range
+are true.
+ +/
+@safe unittest
+{
+ int[3] vals = [5, 3, 18];
+ assert( all(vals[]));
+}
+
+@safe unittest
+{
+ int x = 1;
+ assert(all!(a => a > x)([2, 3]));
+}
+
+/++
+Checks if $(I _any) of the elements verifies $(D pred).
+$(D !any) can be used to verify that $(I none) of the elements verify
+$(D pred).
+This is sometimes called `exists` in other languages.
+ +/
+template any(alias pred = "a")
+{
+ /++
+ Returns $(D true) if and only if $(I _any) value $(D v) found in the
+ input _range $(D range) satisfies the predicate $(D pred).
+ Performs (at most) $(BIGOH range.length) evaluations of $(D pred).
+ +/
+ bool any(Range)(Range range)
+ if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))))
+ {
+ return !find!pred(range).empty;
+ }
+}
+
+///
+@safe unittest
+{
+ import std.ascii : isWhite;
+ assert( all!(any!isWhite)(["a a", "b b"]));
+ assert(!any!(all!isWhite)(["a a", "b b"]));
+}
+
+/++
+$(D any) can also be used without a predicate, if its items can be
+evaluated to true or false in a conditional statement. $(D !any) can be a
+convenient way to quickly test that $(I none) of the elements of a range
+evaluate to true.
+ +/
+@safe unittest
+{
+ int[3] vals1 = [0, 0, 0];
+ assert(!any(vals1[])); //none of vals1 evaluate to true
+
+ int[3] vals2 = [2, 0, 2];
+ assert( any(vals2[]));
+ assert(!all(vals2[]));
+
+ int[3] vals3 = [3, 3, 3];
+ assert( any(vals3[]));
+ assert( all(vals3[]));
+}
+
+@safe unittest
+{
+ auto a = [ 1, 2, 0, 4 ];
+ assert(any!"a == 2"(a));
+}
+
+// balancedParens
+/**
+Checks whether $(D r) has "balanced parentheses", i.e. all instances
+of $(D lPar) are closed by corresponding instances of $(D rPar). The
+parameter $(D maxNestingLevel) controls the nesting level allowed. The
+most common uses are the default or $(D 0). In the latter case, no
+nesting is allowed.
+
+Params:
+ r = The range to check.
+ lPar = The element corresponding with a left (opening) parenthesis.
+ rPar = The element corresponding with a right (closing) parenthesis.
+ maxNestingLevel = The maximum allowed nesting level.
+
+Returns:
+ true if the given range has balanced parenthesis within the given maximum
+ nesting level; false otherwise.
+*/
+bool balancedParens(Range, E)(Range r, E lPar, E rPar,
+ size_t maxNestingLevel = size_t.max)
+if (isInputRange!(Range) && is(typeof(r.front == lPar)))
+{
+ size_t count;
+ for (; !r.empty; r.popFront())
+ {
+ if (r.front == lPar)
+ {
+ if (count > maxNestingLevel) return false;
+ ++count;
+ }
+ else if (r.front == rPar)
+ {
+ if (!count) return false;
+ --count;
+ }
+ }
+ return count == 0;
+}
+
+///
+@safe unittest
+{
+ auto s = "1 + (2 * (3 + 1 / 2)";
+ assert(!balancedParens(s, '(', ')'));
+ s = "1 + (2 * (3 + 1) / 2)";
+ assert(balancedParens(s, '(', ')'));
+ s = "1 + (2 * (3 + 1) / 2)";
+ assert(!balancedParens(s, '(', ')', 0));
+ s = "1 + (2 * 3 + 1) / (2 - 5)";
+ assert(balancedParens(s, '(', ')', 0));
+}
+
+/**
+ * Sets up Boyer-Moore matching for use with $(D find) below.
+ * By default, elements are compared for equality.
+ *
+ * $(D BoyerMooreFinder) allocates GC memory.
+ *
+ * Params:
+ * pred = Predicate used to compare elements.
+ * needle = A random-access range with length and slicing.
+ *
+ * Returns:
+ * An instance of $(D BoyerMooreFinder) that can be used with $(D find()) to
+ * invoke the Boyer-Moore matching algorithm for finding of $(D needle) in a
+ * given haystack.
+ */
+struct BoyerMooreFinder(alias pred, Range)
+{
+private:
+ size_t[] skip; // GC allocated
+ ptrdiff_t[ElementType!(Range)] occ; // GC allocated
+ Range needle;
+
+ ptrdiff_t occurrence(ElementType!(Range) c)
+ {
+ auto p = c in occ;
+ return p ? *p : -1;
+ }
+
+/*
+This helper function checks whether the last "portion" bytes of
+"needle" (which is "nlen" bytes long) exist within the "needle" at
+offset "offset" (counted from the end of the string), and whether the
+character preceding "offset" is not a match. Notice that the range
+being checked may reach beyond the beginning of the string. Such range
+is ignored.
+ */
+ static bool needlematch(R)(R needle,
+ size_t portion, size_t offset)
+ {
+ import std.algorithm.comparison : equal;
+ ptrdiff_t virtual_begin = needle.length - offset - portion;
+ ptrdiff_t ignore = 0;
+ if (virtual_begin < 0)
+ {
+ ignore = -virtual_begin;
+ virtual_begin = 0;
+ }
+ if (virtual_begin > 0
+ && needle[virtual_begin - 1] == needle[$ - portion - 1])
+ return 0;
+
+ immutable delta = portion - ignore;
+ return equal(needle[needle.length - delta .. needle.length],
+ needle[virtual_begin .. virtual_begin + delta]);
+ }
+
+public:
+ ///
+ this(Range needle)
+ {
+ if (!needle.length) return;
+ this.needle = needle;
+ /* Populate table with the analysis of the needle */
+ /* But ignoring the last letter */
+ foreach (i, n ; needle[0 .. $ - 1])
+ {
+ this.occ[n] = i;
+ }
+ /* Preprocess #2: init skip[] */
+ /* Note: This step could be made a lot faster.
+ * A simple implementation is shown here. */
+ this.skip = new size_t[needle.length];
+ foreach (a; 0 .. needle.length)
+ {
+ size_t value = 0;
+ while (value < needle.length
+ && !needlematch(needle, a, value))
+ {
+ ++value;
+ }
+ this.skip[needle.length - a - 1] = value;
+ }
+ }
+
+ ///
+ Range beFound(Range haystack)
+ {
+ import std.algorithm.comparison : max;
+
+ if (!needle.length) return haystack;
+ if (needle.length > haystack.length) return haystack[$ .. $];
+ /* Search: */
+ immutable limit = haystack.length - needle.length;
+ for (size_t hpos = 0; hpos <= limit; )
+ {
+ size_t npos = needle.length - 1;
+ while (pred(needle[npos], haystack[npos+hpos]))
+ {
+ if (npos == 0) return haystack[hpos .. $];
+ --npos;
+ }
+ hpos += max(skip[npos], cast(sizediff_t) npos - occurrence(haystack[npos+hpos]));
+ }
+ return haystack[$ .. $];
+ }
+
+ ///
+ @property size_t length()
+ {
+ return needle.length;
+ }
+
+ ///
+ alias opDollar = length;
+}
+
+/// Ditto
+BoyerMooreFinder!(binaryFun!(pred), Range) boyerMooreFinder
+(alias pred = "a == b", Range)
+(Range needle)
+if ((isRandomAccessRange!(Range) && hasSlicing!Range) || isSomeString!Range)
+{
+ return typeof(return)(needle);
+}
+
+///
+@safe pure nothrow unittest
+{
+ auto bmFinder = boyerMooreFinder("TG");
+
+ string r = "TAGTGCCTGA";
+ // search for the first match in the haystack r
+ r = bmFinder.beFound(r);
+ assert(r == "TGCCTGA");
+
+ // continue search in haystack
+ r = bmFinder.beFound(r[2 .. $]);
+ assert(r == "TGA");
+}
+
+/**
+Returns the common prefix of two ranges.
+
+Params:
+ pred = The predicate to use in comparing elements for commonality. Defaults
+ to equality $(D "a == b").
+
+ r1 = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of
+ elements.
+
+ r2 = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
+ elements.
+
+Returns:
+A slice of $(D r1) which contains the characters that both ranges start with,
+if the first argument is a string; otherwise, the same as the result of
+$(D takeExactly(r1, n)), where $(D n) is the number of elements in the common
+prefix of both ranges.
+
+See_Also:
+ $(REF takeExactly, std,range)
+ */
+auto commonPrefix(alias pred = "a == b", R1, R2)(R1 r1, R2 r2)
+if (isForwardRange!R1 && isInputRange!R2 &&
+ !isNarrowString!R1 &&
+ is(typeof(binaryFun!pred(r1.front, r2.front))))
+{
+ import std.algorithm.comparison : min;
+ static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 &&
+ hasLength!R1 && hasLength!R2 &&
+ hasSlicing!R1)
+ {
+ immutable limit = min(r1.length, r2.length);
+ foreach (i; 0 .. limit)
+ {
+ if (!binaryFun!pred(r1[i], r2[i]))
+ {
+ return r1[0 .. i];
+ }
+ }
+ return r1[0 .. limit];
+ }
+ else
+ {
+ import std.range : takeExactly;
+ auto result = r1.save;
+ size_t i = 0;
+ for (;
+ !r1.empty && !r2.empty && binaryFun!pred(r1.front, r2.front);
+ ++i, r1.popFront(), r2.popFront())
+ {}
+ return takeExactly(result, i);
+ }
+}
+
+///
+@safe unittest
+{
+ assert(commonPrefix("hello, world", "hello, there") == "hello, ");
+}
+
+/// ditto
+auto commonPrefix(alias pred, R1, R2)(R1 r1, R2 r2)
+if (isNarrowString!R1 && isInputRange!R2 &&
+ is(typeof(binaryFun!pred(r1.front, r2.front))))
+{
+ import std.utf : decode;
+
+ auto result = r1.save;
+ immutable len = r1.length;
+ size_t i = 0;
+
+ for (size_t j = 0; i < len && !r2.empty; r2.popFront(), i = j)
+ {
+ immutable f = decode(r1, j);
+ if (!binaryFun!pred(f, r2.front))
+ break;
+ }
+
+ return result[0 .. i];
+}
+
+/// ditto
+auto commonPrefix(R1, R2)(R1 r1, R2 r2)
+if (isNarrowString!R1 && isInputRange!R2 && !isNarrowString!R2 &&
+ is(typeof(r1.front == r2.front)))
+{
+ return commonPrefix!"a == b"(r1, r2);
+}
+
+/// ditto
+auto commonPrefix(R1, R2)(R1 r1, R2 r2)
+if (isNarrowString!R1 && isNarrowString!R2)
+{
+ import std.algorithm.comparison : min;
+
+ static if (ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof)
+ {
+ import std.utf : stride, UTFException;
+
+ immutable limit = min(r1.length, r2.length);
+ for (size_t i = 0; i < limit;)
+ {
+ immutable codeLen = stride(r1, i);
+ size_t j = 0;
+
+ for (; j < codeLen && i < limit; ++i, ++j)
+ {
+ if (r1[i] != r2[i])
+ return r1[0 .. i - j];
+ }
+
+ if (i == limit && j < codeLen)
+ throw new UTFException("Invalid UTF-8 sequence", i);
+ }
+ return r1[0 .. limit];
+ }
+ else
+ return commonPrefix!"a == b"(r1, r2);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+ import std.conv : to;
+ import std.exception : assertThrown;
+ import std.meta : AliasSeq;
+ import std.range;
+ import std.utf : UTFException;
+
+ assert(commonPrefix([1, 2, 3], [1, 2, 3, 4, 5]) == [1, 2, 3]);
+ assert(commonPrefix([1, 2, 3, 4, 5], [1, 2, 3]) == [1, 2, 3]);
+ assert(commonPrefix([1, 2, 3, 4], [1, 2, 3, 4]) == [1, 2, 3, 4]);
+ assert(commonPrefix([1, 2, 3], [7, 2, 3, 4, 5]).empty);
+ assert(commonPrefix([7, 2, 3, 4, 5], [1, 2, 3]).empty);
+ assert(commonPrefix([1, 2, 3], cast(int[]) null).empty);
+ assert(commonPrefix(cast(int[]) null, [1, 2, 3]).empty);
+ assert(commonPrefix(cast(int[]) null, cast(int[]) null).empty);
+
+ foreach (S; AliasSeq!(char[], const(char)[], string,
+ wchar[], const(wchar)[], wstring,
+ dchar[], const(dchar)[], dstring))
+ {
+ foreach (T; AliasSeq!(string, wstring, dstring))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ assert(commonPrefix(to!S(""), to!T("")).empty);
+ assert(commonPrefix(to!S(""), to!T("hello")).empty);
+ assert(commonPrefix(to!S("hello"), to!T("")).empty);
+ assert(commonPrefix(to!S("hello, world"), to!T("hello, there")) == to!S("hello, "));
+ assert(commonPrefix(to!S("hello, there"), to!T("hello, world")) == to!S("hello, "));
+ assert(commonPrefix(to!S("hello, "), to!T("hello, world")) == to!S("hello, "));
+ assert(commonPrefix(to!S("hello, world"), to!T("hello, ")) == to!S("hello, "));
+ assert(commonPrefix(to!S("hello, world"), to!T("hello, world")) == to!S("hello, world"));
+
+ //Bug# 8890
+ assert(commonPrefix(to!S("Пиво"), to!T("Пони"))== to!S("П"));
+ assert(commonPrefix(to!S("Пони"), to!T("Пиво"))== to!S("П"));
+ assert(commonPrefix(to!S("Пиво"), to!T("Пиво"))== to!S("Пиво"));
+ assert(commonPrefix(to!S("\U0010FFFF\U0010FFFB\U0010FFFE"),
+ to!T("\U0010FFFF\U0010FFFB\U0010FFFC")) == to!S("\U0010FFFF\U0010FFFB"));
+ assert(commonPrefix(to!S("\U0010FFFF\U0010FFFB\U0010FFFC"),
+ to!T("\U0010FFFF\U0010FFFB\U0010FFFE")) == to!S("\U0010FFFF\U0010FFFB"));
+ assert(commonPrefix!"a != b"(to!S("Пиво"), to!T("онво")) == to!S("Пи"));
+ assert(commonPrefix!"a != b"(to!S("онво"), to!T("Пиво")) == to!S("он"));
+ }();
+
+ static assert(is(typeof(commonPrefix(to!S("Пиво"), filter!"true"("Пони"))) == S));
+ assert(equal(commonPrefix(to!S("Пиво"), filter!"true"("Пони")), to!S("П")));
+
+ static assert(is(typeof(commonPrefix(filter!"true"("Пиво"), to!S("Пони"))) ==
+ typeof(takeExactly(filter!"true"("П"), 1))));
+ assert(equal(commonPrefix(filter!"true"("Пиво"), to!S("Пони")), takeExactly(filter!"true"("П"), 1)));
+ }
+
+ assertThrown!UTFException(commonPrefix("\U0010FFFF\U0010FFFB", "\U0010FFFF\U0010FFFB"[0 .. $ - 1]));
+
+ assert(commonPrefix("12345"d, [49, 50, 51, 60, 60]) == "123"d);
+ assert(commonPrefix([49, 50, 51, 60, 60], "12345" ) == [49, 50, 51]);
+ assert(commonPrefix([49, 50, 51, 60, 60], "12345"d) == [49, 50, 51]);
+
+ assert(commonPrefix!"a == ('0' + b)"("12345" , [1, 2, 3, 9, 9]) == "123");
+ assert(commonPrefix!"a == ('0' + b)"("12345"d, [1, 2, 3, 9, 9]) == "123"d);
+ assert(commonPrefix!"('0' + a) == b"([1, 2, 3, 9, 9], "12345" ) == [1, 2, 3]);
+ assert(commonPrefix!"('0' + a) == b"([1, 2, 3, 9, 9], "12345"d) == [1, 2, 3]);
+}
+
+// count
+/**
+The first version counts the number of elements $(D x) in $(D r) for
+which $(D pred(x, value)) is $(D true). $(D pred) defaults to
+equality. Performs $(BIGOH haystack.length) evaluations of $(D pred).
+
+The second version returns the number of times $(D needle) occurs in
+$(D haystack). Throws an exception if $(D needle.empty), as the _count
+of the empty range in any range would be infinite. Overlapped counts
+are not considered, for example $(D count("aaa", "aa")) is $(D 1), not
+$(D 2).
+
+The third version counts the elements for which $(D pred(x)) is $(D
+true). Performs $(BIGOH haystack.length) evaluations of $(D pred).
+
+The fourth version counts the number of elements in a range. It is
+an optimization for the third version: if the given range has the
+`length` property the count is returned right away, otherwise
+performs $(BIGOH haystack.length) to walk the range.
+
+Note: Regardless of the overload, $(D count) will not accept
+infinite ranges for $(D haystack).
+
+Params:
+ pred = The predicate to evaluate.
+ haystack = The range to _count.
+ needle = The element or sub-range to _count in the `haystack`.
+
+Returns:
+ The number of positions in the `haystack` for which `pred` returned true.
+*/
+size_t count(alias pred = "a == b", Range, E)(Range haystack, E needle)
+if (isInputRange!Range && !isInfinite!Range &&
+ is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
+{
+ bool pred2(ElementType!Range a) { return binaryFun!pred(a, needle); }
+ return count!pred2(haystack);
+}
+
+///
+@safe unittest
+{
+ import std.uni : toLower;
+
+ // count elements in range
+ int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
+ assert(count(a) == 9);
+ assert(count(a, 2) == 3);
+ assert(count!("a > b")(a, 2) == 5);
+ // count range in range
+ assert(count("abcadfabf", "ab") == 2);
+ assert(count("ababab", "abab") == 1);
+ assert(count("ababab", "abx") == 0);
+ // fuzzy count range in range
+ assert(count!((a, b) => toLower(a) == toLower(b))("AbcAdFaBf", "ab") == 2);
+ // count predicate in range
+ assert(count!("a > 1")(a) == 8);
+}
+
+@safe unittest
+{
+ import std.conv : text;
+
+ int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
+ assert(count(a, 2) == 3, text(count(a, 2)));
+ assert(count!("a > b")(a, 2) == 5, text(count!("a > b")(a, 2)));
+
+ // check strings
+ assert(count("日本語") == 3);
+ assert(count("日本語"w) == 3);
+ assert(count("日本語"d) == 3);
+
+ assert(count!("a == '日'")("日本語") == 1);
+ assert(count!("a == '本'")("日本語"w) == 1);
+ assert(count!("a == '語'")("日本語"d) == 1);
+}
+
+@safe unittest
+{
+ string s = "This is a fofofof list";
+ string sub = "fof";
+ assert(count(s, sub) == 2);
+}
+
+/// Ditto
+size_t count(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
+if (isForwardRange!R1 && !isInfinite!R1 &&
+ isForwardRange!R2 &&
+ is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
+{
+ assert(!needle.empty, "Cannot count occurrences of an empty range");
+
+ static if (isInfinite!R2)
+ {
+ //Note: This is the special case of looking for an infinite inside a finite...
+ //"How many instances of the Fibonacci sequence can you count in [1, 2, 3]?" - "None."
+ return 0;
+ }
+ else
+ {
+ size_t result;
+ //Note: haystack is not saved, because findskip is designed to modify it
+ for ( ; findSkip!pred(haystack, needle.save) ; ++result)
+ {}
+ return result;
+ }
+}
+
+/// Ditto
+size_t count(alias pred, R)(R haystack)
+if (isInputRange!R && !isInfinite!R &&
+ is(typeof(unaryFun!pred(haystack.front)) : bool))
+{
+ size_t result;
+ alias T = ElementType!R; //For narrow strings forces dchar iteration
+ foreach (T elem; haystack)
+ if (unaryFun!pred(elem)) ++result;
+ return result;
+}
+
+/// Ditto
+size_t count(R)(R haystack)
+if (isInputRange!R && !isInfinite!R)
+{
+ return walkLength(haystack);
+}
+
+@safe unittest
+{
+ int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
+ assert(count!("a == 3")(a) == 2);
+ assert(count("日本語") == 3);
+}
+
+// Issue 11253
+@safe nothrow unittest
+{
+ assert([1, 2, 3].count([2, 3]) == 1);
+}
+
+/++
+ Counts elements in the given
+ $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ until the given predicate is true for one of the given $(D needles).
+
+ Params:
+ pred = The predicate for determining when to stop counting.
+ haystack = The
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be
+ counted.
+ needles = Either a single element, or a
+ $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ of elements, to be evaluated in turn against each
+ element in $(D haystack) under the given predicate.
+
+ Returns: The number of elements which must be popped from the front of
+ $(D haystack) before reaching an element for which
+ $(D startsWith!pred(haystack, needles)) is $(D true). If
+ $(D startsWith!pred(haystack, needles)) is not $(D true) for any element in
+ $(D haystack), then $(D -1) is returned.
+
+ See_Also: $(REF indexOf, std,string)
+ +/
+ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles)
+if (isForwardRange!R
+ && Rs.length > 0
+ && isForwardRange!(Rs[0]) == isInputRange!(Rs[0])
+ && is(typeof(startsWith!pred(haystack, needles[0])))
+ && (Rs.length == 1
+ || is(typeof(countUntil!pred(haystack, needles[1 .. $])))))
+{
+ typeof(return) result;
+
+ static if (needles.length == 1)
+ {
+ static if (hasLength!R) //Note: Narrow strings don't have length.
+ {
+ //We delegate to find because find is very efficient.
+ //We store the length of the haystack so we don't have to save it.
+ auto len = haystack.length;
+ auto r2 = find!pred(haystack, needles[0]);
+ if (!r2.empty)
+ return cast(typeof(return)) (len - r2.length);
+ }
+ else
+ {
+ import std.range : dropOne;
+
+ if (needles[0].empty)
+ return 0;
+
+ //Default case, slower route doing startsWith iteration
+ for ( ; !haystack.empty ; ++result )
+ {
+ //We compare the first elements of the ranges here before
+ //forwarding to startsWith. This avoids making useless saves to
+ //haystack/needle if they aren't even going to be mutated anyways.
+ //It also cuts down on the amount of pops on haystack.
+ if (binaryFun!pred(haystack.front, needles[0].front))
+ {
+ //Here, we need to save the needle before popping it.
+ //haystack we pop in all paths, so we do that, and then save.
+ haystack.popFront();
+ if (startsWith!pred(haystack.save, needles[0].save.dropOne()))
+ return result;
+ }
+ else
+ haystack.popFront();
+ }
+ }
+ }
+ else
+ {
+ foreach (i, Ri; Rs)
+ {
+ static if (isForwardRange!Ri)
+ {
+ if (needles[i].empty)
+ return 0;
+ }
+ }
+ Tuple!Rs t;
+ foreach (i, Ri; Rs)
+ {
+ static if (!isForwardRange!Ri)
+ {
+ t[i] = needles[i];
+ }
+ }
+ for (; !haystack.empty ; ++result, haystack.popFront())
+ {
+ foreach (i, Ri; Rs)
+ {
+ static if (isForwardRange!Ri)
+ {
+ t[i] = needles[i].save;
+ }
+ }
+ if (startsWith!pred(haystack.save, t.expand))
+ {
+ return result;
+ }
+ }
+ }
+
+ //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement"
+ static if (isInfinite!R) assert(0);
+ else return -1;
+}
+
+/// ditto
+ptrdiff_t countUntil(alias pred = "a == b", R, N)(R haystack, N needle)
+if (isInputRange!R &&
+ is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
+{
+ bool pred2(ElementType!R a) { return binaryFun!pred(a, needle); }
+ return countUntil!pred2(haystack);
+}
+
+///
+@safe unittest
+{
+ assert(countUntil("hello world", "world") == 6);
+ assert(countUntil("hello world", 'r') == 8);
+ assert(countUntil("hello world", "programming") == -1);
+ assert(countUntil("日本語", "本語") == 1);
+ assert(countUntil("日本語", '語') == 2);
+ assert(countUntil("日本語", "五") == -1);
+ assert(countUntil("日本語", '五') == -1);
+ assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2);
+ assert(countUntil([0, 7, 12, 22, 9], 9) == 4);
+ assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3);
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.internal.test.dummyrange;
+
+ assert(countUntil("日本語", "") == 0);
+ assert(countUntil("日本語"d, "") == 0);
+
+ assert(countUntil("", "") == 0);
+ assert(countUntil("".filter!"true"(), "") == 0);
+
+ auto rf = [0, 20, 12, 22, 9].filter!"true"();
+ assert(rf.countUntil!"a > b"((int[]).init) == 0);
+ assert(rf.countUntil!"a > b"(20) == 3);
+ assert(rf.countUntil!"a > b"([20, 8]) == 3);
+ assert(rf.countUntil!"a > b"([20, 10]) == -1);
+ assert(rf.countUntil!"a > b"([20, 8, 0]) == -1);
+
+ auto r = new ReferenceForwardRange!int([0, 1, 2, 3, 4, 5, 6]);
+ auto r2 = new ReferenceForwardRange!int([3, 4]);
+ auto r3 = new ReferenceForwardRange!int([3, 5]);
+ assert(r.save.countUntil(3) == 3);
+ assert(r.save.countUntil(r2) == 3);
+ assert(r.save.countUntil(7) == -1);
+ assert(r.save.countUntil(r3) == -1);
+}
+
+@safe unittest
+{
+ assert(countUntil("hello world", "world", "asd") == 6);
+ assert(countUntil("hello world", "world", "ello") == 1);
+ assert(countUntil("hello world", "world", "") == 0);
+ assert(countUntil("hello world", "world", 'l') == 2);
+}
+
+/++
+ Similar to the previous overload of $(D countUntil), except that this one
+ evaluates only the predicate $(D pred).
+
+ Params:
+ pred = Predicate to when to stop counting.
+ haystack = An
+ $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
+ elements to be counted.
+ Returns: The number of elements which must be popped from $(D haystack)
+ before $(D pred(haystack.front)) is $(D true).
+ +/
+ptrdiff_t countUntil(alias pred, R)(R haystack)
+if (isInputRange!R &&
+ is(typeof(unaryFun!pred(haystack.front)) : bool))
+{
+ typeof(return) i;
+ static if (isRandomAccessRange!R)
+ {
+ //Optimized RA implementation. Since we want to count *and* iterate at
+ //the same time, it is more efficient this way.
+ static if (hasLength!R)
+ {
+ immutable len = cast(typeof(return)) haystack.length;
+ for ( ; i < len ; ++i )
+ if (unaryFun!pred(haystack[i])) return i;
+ }
+ else //if (isInfinite!R)
+ {
+ for ( ; ; ++i )
+ if (unaryFun!pred(haystack[i])) return i;
+ }
+ }
+ else static if (hasLength!R)
+ {
+ //For those odd ranges that have a length, but aren't RA.
+ //It is faster to quick find, and then compare the lengths
+ auto r2 = find!pred(haystack.save);
+ if (!r2.empty) return cast(typeof(return)) (haystack.length - r2.length);
+ }
+ else //Everything else
+ {
+ alias T = ElementType!R; //For narrow strings forces dchar iteration
+ foreach (T elem; haystack)
+ {
+ if (unaryFun!pred(elem)) return i;
+ ++i;
+ }
+ }
+
+ //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement"
+ static if (isInfinite!R) assert(0);
+ else return -1;
+}
+
+///
+@safe unittest
+{
+ import std.ascii : isDigit;
+ import std.uni : isWhite;
+
+ assert(countUntil!(std.uni.isWhite)("hello world") == 5);
+ assert(countUntil!(std.ascii.isDigit)("hello world") == -1);
+ assert(countUntil!"a > 20"([0, 7, 12, 22, 9]) == 3);
+}
+
+@safe unittest
+{
+ import std.internal.test.dummyrange;
+
+ // References
+ {
+ // input
+ ReferenceInputRange!int r;
+ r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]);
+ assert(r.countUntil(3) == 3);
+ r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]);
+ assert(r.countUntil(7) == -1);
+ }
+ {
+ // forward
+ auto r = new ReferenceForwardRange!int([0, 1, 2, 3, 4, 5, 6]);
+ assert(r.save.countUntil([3, 4]) == 3);
+ assert(r.save.countUntil(3) == 3);
+ assert(r.save.countUntil([3, 7]) == -1);
+ assert(r.save.countUntil(7) == -1);
+ }
+ {
+ // infinite forward
+ auto r = new ReferenceInfiniteForwardRange!int(0);
+ assert(r.save.countUntil([3, 4]) == 3);
+ assert(r.save.countUntil(3) == 3);
+ }
+}
+
+/**
+Checks if the given range ends with (one of) the given needle(s).
+The reciprocal of $(D startsWith).
+
+Params:
+ pred = The predicate to use for comparing elements between the range and
+ the needle(s).
+
+ doesThisEnd = The
+ $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
+ to check.
+
+ withOneOfThese = The needles to check against, which may be single
+ elements, or bidirectional ranges of elements.
+
+ withThis = The single element to check.
+
+Returns:
+0 if the needle(s) do not occur at the end of the given range;
+otherwise the position of the matching needle, that is, 1 if the range ends
+with $(D withOneOfThese[0]), 2 if it ends with $(D withOneOfThese[1]), and so
+on.
+
+In the case when no needle parameters are given, return $(D true) iff back of
+$(D doesThisStart) fulfils predicate $(D pred).
+*/
+uint endsWith(alias pred = "a == b", Range, Needles...)(Range doesThisEnd, Needles withOneOfThese)
+if (isBidirectionalRange!Range && Needles.length > 1 &&
+ is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[0])) : bool) &&
+ is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[1 .. $])) : uint))
+{
+ alias haystack = doesThisEnd;
+ alias needles = withOneOfThese;
+
+ // Make one pass looking for empty ranges in needles
+ foreach (i, Unused; Needles)
+ {
+ // Empty range matches everything
+ static if (!is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool))
+ {
+ if (needles[i].empty) return i + 1;
+ }
+ }
+
+ for (; !haystack.empty; haystack.popBack())
+ {
+ foreach (i, Unused; Needles)
+ {
+ static if (is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool))
+ {
+ // Single-element
+ if (binaryFun!pred(haystack.back, needles[i]))
+ {
+ // found, but continue to account for one-element
+ // range matches (consider endsWith("ab", "b",
+ // 'b') should return 1, not 2).
+ continue;
+ }
+ }
+ else
+ {
+ if (binaryFun!pred(haystack.back, needles[i].back))
+ continue;
+ }
+
+ // This code executed on failure to match
+ // Out with this guy, check for the others
+ uint result = endsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]);
+ if (result > i) ++result;
+ return result;
+ }
+
+ // If execution reaches this point, then the back matches for all
+ // needles ranges. What we need to do now is to lop off the back of
+ // all ranges involved and recurse.
+ foreach (i, Unused; Needles)
+ {
+ static if (is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool))
+ {
+ // Test has passed in the previous loop
+ return i + 1;
+ }
+ else
+ {
+ needles[i].popBack();
+ if (needles[i].empty) return i + 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/// Ditto
+bool endsWith(alias pred = "a == b", R1, R2)(R1 doesThisEnd, R2 withThis)
+if (isBidirectionalRange!R1 &&
+ isBidirectionalRange!R2 &&
+ is(typeof(binaryFun!pred(doesThisEnd.back, withThis.back)) : bool))
+{
+ alias haystack = doesThisEnd;
+ alias needle = withThis;
+
+ static if (is(typeof(pred) : string))
+ enum isDefaultPred = pred == "a == b";
+ else
+ enum isDefaultPred = false;
+
+ static if (isDefaultPred && isArray!R1 && isArray!R2 &&
+ is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
+ {
+ if (haystack.length < needle.length) return false;
+
+ return haystack[$ - needle.length .. $] == needle;
+ }
+ else
+ {
+ import std.range : retro;
+ return startsWith!pred(retro(doesThisEnd), retro(withThis));
+ }
+}
+
+/// Ditto
+bool endsWith(alias pred = "a == b", R, E)(R doesThisEnd, E withThis)
+if (isBidirectionalRange!R &&
+ is(typeof(binaryFun!pred(doesThisEnd.back, withThis)) : bool))
+{
+ if (doesThisEnd.empty)
+ return false;
+
+ alias predFunc = binaryFun!pred;
+
+ // auto-decoding special case
+ static if (isNarrowString!R)
+ {
+ // specialize for ASCII as to not change previous behavior
+ if (withThis <= 0x7F)
+ return predFunc(doesThisEnd[$ - 1], withThis);
+ else
+ return predFunc(doesThisEnd.back, withThis);
+ }
+ else
+ {
+ return predFunc(doesThisEnd.back, withThis);
+ }
+}
+
+/// Ditto
+bool endsWith(alias pred, R)(R doesThisEnd)
+if (isInputRange!R &&
+ ifTestable!(typeof(doesThisEnd.front), unaryFun!pred))
+{
+ return !doesThisEnd.empty && unaryFun!pred(doesThisEnd.back);
+}
+
+///
+@safe unittest
+{
+ import std.ascii : isAlpha;
+ assert("abc".endsWith!(a => a.isAlpha));
+ assert("abc".endsWith!isAlpha);
+
+ assert(!"ab1".endsWith!(a => a.isAlpha));
+
+ assert(!"ab1".endsWith!isAlpha);
+ assert(!"".endsWith!(a => a.isAlpha));
+
+ import std.algorithm.comparison : among;
+ assert("abc".endsWith!(a => a.among('c', 'd') != 0));
+ assert(!"abc".endsWith!(a => a.among('a', 'b') != 0));
+
+ assert(endsWith("abc", ""));
+ assert(!endsWith("abc", "b"));
+ assert(endsWith("abc", "a", 'c') == 2);
+ assert(endsWith("abc", "c", "a") == 1);
+ assert(endsWith("abc", "c", "c") == 1);
+ assert(endsWith("abc", "bc", "c") == 2);
+ assert(endsWith("abc", "x", "c", "b") == 2);
+ assert(endsWith("abc", "x", "aa", "bc") == 3);
+ assert(endsWith("abc", "x", "aaa", "sab") == 0);
+ assert(endsWith("abc", "x", "aaa", 'c', "sab") == 3);
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filterBidirectional;
+ import std.conv : to;
+ import std.meta : AliasSeq;
+
+ foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
+ assert(!endsWith(to!S("abc"), 'a'));
+ assert(endsWith(to!S("abc"), 'a', 'c') == 2);
+ assert(!endsWith(to!S("abc"), 'x', 'n', 'b'));
+ assert(endsWith(to!S("abc"), 'x', 'n', 'c') == 3);
+ assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2);
+
+ foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ //Lots of strings
+ assert(endsWith(to!S("abc"), to!T("")));
+ assert(!endsWith(to!S("abc"), to!T("a")));
+ assert(!endsWith(to!S("abc"), to!T("b")));
+ assert(endsWith(to!S("abc"), to!T("bc"), 'c') == 2);
+ assert(endsWith(to!S("abc"), to!T("a"), "c") == 2);
+ assert(endsWith(to!S("abc"), to!T("c"), "a") == 1);
+ assert(endsWith(to!S("abc"), to!T("c"), "c") == 1);
+ assert(endsWith(to!S("abc"), to!T("x"), 'c', "b") == 2);
+ assert(endsWith(to!S("abc"), 'x', to!T("aa"), "bc") == 3);
+ assert(endsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0);
+ assert(endsWith(to!S("abc"), to!T("x"), "aaa", "c", "sab") == 3);
+ assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co")));
+ assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("lo"), to!T("l\uFF4co")) == 2);
+
+ //Unicode
+ assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co")));
+ assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("lo"), to!T("l\uFF4co")) == 2);
+ assert(endsWith(to!S("日本語"), to!T("本語")));
+ assert(endsWith(to!S("日本語"), to!T("日本語")));
+ assert(!endsWith(to!S("本語"), to!T("日本語")));
+
+ //Empty
+ assert(endsWith(to!S(""), T.init));
+ assert(!endsWith(to!S(""), 'a'));
+ assert(endsWith(to!S("a"), T.init));
+ assert(endsWith(to!S("a"), T.init, "") == 1);
+ assert(endsWith(to!S("a"), T.init, 'a') == 1);
+ assert(endsWith(to!S("a"), 'a', T.init) == 2);
+ }();
+ }
+
+ foreach (T; AliasSeq!(int, short))
+ {
+ immutable arr = cast(T[])[0, 1, 2, 3, 4, 5];
+
+ //RA range
+ assert(endsWith(arr, cast(int[]) null));
+ assert(!endsWith(arr, 0));
+ assert(!endsWith(arr, 4));
+ assert(endsWith(arr, 5));
+ assert(endsWith(arr, 0, 4, 5) == 3);
+ assert(endsWith(arr, [5]));
+ assert(endsWith(arr, [4, 5]));
+ assert(endsWith(arr, [4, 5], 7) == 1);
+ assert(!endsWith(arr, [2, 4, 5]));
+ assert(endsWith(arr, [2, 4, 5], [3, 4, 5]) == 2);
+
+ //Normal input range
+ assert(!endsWith(filterBidirectional!"true"(arr), 4));
+ assert(endsWith(filterBidirectional!"true"(arr), 5));
+ assert(endsWith(filterBidirectional!"true"(arr), [5]));
+ assert(endsWith(filterBidirectional!"true"(arr), [4, 5]));
+ assert(endsWith(filterBidirectional!"true"(arr), [4, 5], 7) == 1);
+ assert(!endsWith(filterBidirectional!"true"(arr), [2, 4, 5]));
+ assert(endsWith(filterBidirectional!"true"(arr), [2, 4, 5], [3, 4, 5]) == 2);
+ assert(endsWith(arr, filterBidirectional!"true"([4, 5])));
+ assert(endsWith(arr, filterBidirectional!"true"([4, 5]), 7) == 1);
+ assert(!endsWith(arr, filterBidirectional!"true"([2, 4, 5])));
+ assert(endsWith(arr, [2, 4, 5], filterBidirectional!"true"([3, 4, 5])) == 2);
+
+ //Non-default pred
+ assert(endsWith!("a%10 == b%10")(arr, [14, 15]));
+ assert(!endsWith!("a%10 == b%10")(arr, [15, 14]));
+ }
+}
+
+/**
+Iterates the passed range and selects the extreme element with `less`.
+If the extreme element occurs multiple time, the first occurrence will be
+returned.
+
+Params:
+ map = custom accessor for the comparison key
+ selector = custom mapping for the extrema selection
+ seed = custom seed to use as initial element
+ r = Range from which the extreme value will be selected
+
+Returns:
+ The extreme value according to `map` and `selector` of the passed-in values.
+*/
+private auto extremum(alias map, alias selector = "a < b", Range)(Range r)
+if (isInputRange!Range && !isInfinite!Range &&
+ is(typeof(unaryFun!map(ElementType!(Range).init))))
+in
+{
+ assert(!r.empty, "r is an empty range");
+}
+body
+{
+ alias Element = ElementType!Range;
+ Unqual!Element seed = r.front;
+ r.popFront();
+ return extremum!(map, selector)(r, seed);
+}
+
+private auto extremum(alias map, alias selector = "a < b", Range,
+ RangeElementType = ElementType!Range)
+ (Range r, RangeElementType seedElement)
+if (isInputRange!Range && !isInfinite!Range &&
+ !is(CommonType!(ElementType!Range, RangeElementType) == void) &&
+ is(typeof(unaryFun!map(ElementType!(Range).init))))
+{
+ alias mapFun = unaryFun!map;
+ alias selectorFun = binaryFun!selector;
+
+ alias Element = ElementType!Range;
+ alias CommonElement = CommonType!(Element, RangeElementType);
+ Unqual!CommonElement extremeElement = seedElement;
+
+ alias MapType = Unqual!(typeof(mapFun(CommonElement.init)));
+ MapType extremeElementMapped = mapFun(extremeElement);
+
+ // direct access via a random access range is faster
+ static if (isRandomAccessRange!Range)
+ {
+ foreach (const i; 0 .. r.length)
+ {
+ MapType mapElement = mapFun(r[i]);
+ if (selectorFun(mapElement, extremeElementMapped))
+ {
+ extremeElement = r[i];
+ extremeElementMapped = mapElement;
+ }
+ }
+ }
+ else
+ {
+ while (!r.empty)
+ {
+ MapType mapElement = mapFun(r.front);
+ if (selectorFun(mapElement, extremeElementMapped))
+ {
+ extremeElement = r.front;
+ extremeElementMapped = mapElement;
+ }
+ r.popFront();
+ }
+ }
+ return extremeElement;
+}
+
+private auto extremum(alias selector = "a < b", Range)(Range r)
+ if (isInputRange!Range && !isInfinite!Range &&
+ !is(typeof(unaryFun!selector(ElementType!(Range).init))))
+{
+ alias Element = ElementType!Range;
+ Unqual!Element seed = r.front;
+ r.popFront();
+ return extremum!selector(r, seed);
+}
+
+// if we only have one statement in the loop it can be optimized a lot better
+private auto extremum(alias selector = "a < b", Range,
+ RangeElementType = ElementType!Range)
+ (Range r, RangeElementType seedElement)
+ if (isInputRange!Range && !isInfinite!Range &&
+ !is(CommonType!(ElementType!Range, RangeElementType) == void) &&
+ !is(typeof(unaryFun!selector(ElementType!(Range).init))))
+{
+ alias Element = ElementType!Range;
+ alias CommonElement = CommonType!(Element, RangeElementType);
+ Unqual!CommonElement extremeElement = seedElement;
+ alias selectorFun = binaryFun!selector;
+
+ // direct access via a random access range is faster
+ static if (isRandomAccessRange!Range)
+ {
+ foreach (const i; 0 .. r.length)
+ {
+ if (selectorFun(r[i], extremeElement))
+ {
+ extremeElement = r[i];
+ }
+ }
+ }
+ else
+ {
+ while (!r.empty)
+ {
+ if (selectorFun(r.front, extremeElement))
+ {
+ extremeElement = r.front;
+ }
+ r.popFront();
+ }
+ }
+ return extremeElement;
+}
+
+@safe pure unittest
+{
+ // allows a custom map to select the extremum
+ assert([[0, 4], [1, 2]].extremum!"a[0]" == [0, 4]);
+ assert([[0, 4], [1, 2]].extremum!"a[1]" == [1, 2]);
+
+ // allows a custom selector for comparison
+ assert([[0, 4], [1, 2]].extremum!("a[0]", "a > b") == [1, 2]);
+ assert([[0, 4], [1, 2]].extremum!("a[1]", "a > b") == [0, 4]);
+
+ // use a custom comparator
+ import std.math : cmp;
+ assert([-2., 0, 5].extremum!cmp == 5.0);
+ assert([-2., 0, 2].extremum!`cmp(a, b) < 0` == -2.0);
+
+ // combine with map
+ import std.range : enumerate;
+ assert([-3., 0, 5].enumerate.extremum!(`a.value`, cmp) == tuple(2, 5.0));
+ assert([-2., 0, 2].enumerate.extremum!(`a.value`, `cmp(a, b) < 0`) == tuple(0, -2.0));
+
+ // seed with a custom value
+ int[] arr;
+ assert(arr.extremum(1) == 1);
+}
+
+@safe pure nothrow unittest
+{
+ // 2d seeds
+ int[][] arr2d;
+ assert(arr2d.extremum([1]) == [1]);
+
+ // allow seeds of different types (implicit casting)
+ assert(extremum([2, 3, 4], 1.5) == 1.5);
+}
+
+@safe pure unittest
+{
+ import std.range : enumerate, iota;
+
+ // forward ranges
+ assert(iota(1, 5).extremum() == 1);
+ assert(iota(2, 5).enumerate.extremum!"a.value" == tuple(0, 2));
+
+ // should work with const
+ const(int)[] immArr = [2, 1, 3];
+ assert(immArr.extremum == 1);
+
+ // should work with immutable
+ immutable(int)[] immArr2 = [2, 1, 3];
+ assert(immArr2.extremum == 1);
+
+ // with strings
+ assert(["b", "a", "c"].extremum == "a");
+
+ // with all dummy ranges
+ import std.internal.test.dummyrange;
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ assert(d.extremum == 1);
+ assert(d.extremum!(a => a) == 1);
+ assert(d.extremum!`a > b` == 10);
+ assert(d.extremum!(a => a, `a > b`) == 10);
+ }
+}
+
+@nogc @safe nothrow pure unittest
+{
+ static immutable arr = [7, 3, 4, 2, 1, 8];
+ assert(arr.extremum == 1);
+
+ static immutable arr2d = [[1, 9], [3, 1], [4, 2]];
+ assert(arr2d.extremum!"a[1]" == arr2d[1]);
+}
+
+// find
+/**
+Finds an individual element in an input range. Elements of $(D
+haystack) are compared with $(D needle) by using predicate $(D
+pred). Performs $(BIGOH walkLength(haystack)) evaluations of $(D
+pred).
+
+To _find the last occurrence of $(D needle) in $(D haystack), call $(D
+find(retro(haystack), needle)). See $(REF retro, std,range).
+
+Params:
+
+pred = The predicate for comparing each element with the needle, defaulting to
+$(D "a == b").
+The negated predicate $(D "a != b") can be used to search instead for the first
+element $(I not) matching the needle.
+
+haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+searched in.
+
+needle = The element searched for.
+
+Constraints:
+
+$(D isInputRange!InputRange && is(typeof(binaryFun!pred(haystack.front, needle)
+: bool)))
+
+Returns:
+
+$(D haystack) advanced such that the front element is the one searched for;
+that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no
+such position exists, returns an empty $(D haystack).
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/_find.html, STL's _find)
+ */
+InputRange find(alias pred = "a == b", InputRange, Element)(InputRange haystack, scope Element needle)
+if (isInputRange!InputRange &&
+ is (typeof(binaryFun!pred(haystack.front, needle)) : bool))
+{
+ alias R = InputRange;
+ alias E = Element;
+ alias predFun = binaryFun!pred;
+ static if (is(typeof(pred == "a == b")))
+ enum isDefaultPred = pred == "a == b";
+ else
+ enum isDefaultPred = false;
+ enum isIntegralNeedle = isSomeChar!E || isIntegral!E || isBoolean!E;
+
+ alias EType = ElementType!R;
+
+ // If the haystack is a SortedRange we can use binary search to find the needle.
+ // Works only for the default find predicate and any SortedRange predicate.
+ // 8829 enhancement
+ import std.range : SortedRange;
+ static if (is(InputRange : SortedRange!TT, TT) && isDefaultPred)
+ {
+ auto lb = haystack.lowerBound(needle);
+ if (lb.length == haystack.length || haystack[lb.length] != needle)
+ return haystack[$ .. $];
+
+ return haystack[lb.length .. $];
+ }
+ else static if (isNarrowString!R)
+ {
+ alias EEType = ElementEncodingType!R;
+ alias UEEType = Unqual!EEType;
+
+ //These are two special cases which can search without decoding the UTF stream.
+ static if (isDefaultPred && isIntegralNeedle)
+ {
+ import std.utf : canSearchInCodeUnits;
+
+ //This special case deals with UTF8 search, when the needle
+ //is represented by a single code point.
+ //Note: "needle <= 0x7F" properly handles sign via unsigned promotion
+ static if (is(UEEType == char))
+ {
+ if (!__ctfe && canSearchInCodeUnits!char(needle))
+ {
+ static R trustedMemchr(ref R haystack, ref E needle) @trusted nothrow pure
+ {
+ import core.stdc.string : memchr;
+ auto ptr = memchr(haystack.ptr, needle, haystack.length);
+ return ptr ?
+ haystack[cast(char*) ptr - haystack.ptr .. $] :
+ haystack[$ .. $];
+ }
+ return trustedMemchr(haystack, needle);
+ }
+ }
+
+ //Ditto, but for UTF16
+ static if (is(UEEType == wchar))
+ {
+ if (canSearchInCodeUnits!wchar(needle))
+ {
+ foreach (i, ref EEType e; haystack)
+ {
+ if (e == needle)
+ return haystack[i .. $];
+ }
+ return haystack[$ .. $];
+ }
+ }
+ }
+
+ //Previous conditonal optimizations did not succeed. Fallback to
+ //unconditional implementations
+ static if (isDefaultPred)
+ {
+ import std.utf : encode;
+
+ //In case of default pred, it is faster to do string/string search.
+ UEEType[is(UEEType == char) ? 4 : 2] buf;
+
+ size_t len = encode(buf, needle);
+ return find(haystack, buf[0 .. len]);
+ }
+ else
+ {
+ import std.utf : decode;
+
+ //Explicit pred: we must test each character by the book.
+ //We choose a manual decoding approach, because it is faster than
+ //the built-in foreach, or doing a front/popFront for-loop.
+ immutable len = haystack.length;
+ size_t i = 0, next = 0;
+ while (next < len)
+ {
+ if (predFun(decode(haystack, next), needle))
+ return haystack[i .. $];
+ i = next;
+ }
+ return haystack[$ .. $];
+ }
+ }
+ else static if (isArray!R)
+ {
+ //10403 optimization
+ static if (isDefaultPred && isIntegral!EType && EType.sizeof == 1 && isIntegralNeedle)
+ {
+ import std.algorithm.comparison : max, min;
+
+ R findHelper(ref R haystack, ref E needle) @trusted nothrow pure
+ {
+ import core.stdc.string : memchr;
+
+ EType* ptr = null;
+ //Note: we use "min/max" to handle sign mismatch.
+ if (min(EType.min, needle) == EType.min &&
+ max(EType.max, needle) == EType.max)
+ {
+ ptr = cast(EType*) memchr(haystack.ptr, needle,
+ haystack.length);
+ }
+
+ return ptr ?
+ haystack[ptr - haystack.ptr .. $] :
+ haystack[$ .. $];
+ }
+
+ if (!__ctfe)
+ return findHelper(haystack, needle);
+ }
+
+ //Default implementation.
+ foreach (i, ref e; haystack)
+ if (predFun(e, needle))
+ return haystack[i .. $];
+ return haystack[$ .. $];
+ }
+ else
+ {
+ //Everything else. Walk.
+ for ( ; !haystack.empty; haystack.popFront() )
+ {
+ if (predFun(haystack.front, needle))
+ break;
+ }
+ return haystack;
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container : SList;
+ import std.range;
+ import std.range.primitives : empty;
+
+ auto arr = assumeSorted!"a < b"([1, 2, 4, 4, 4, 4, 5, 6, 9]);
+ assert(find(arr, 4) == assumeSorted!"a < b"([4, 4, 4, 4, 5, 6, 9]));
+ assert(find(arr, 1) == arr);
+ assert(find(arr, 9) == assumeSorted!"a < b"([9]));
+ assert(find!"a > b"(arr, 4) == assumeSorted!"a < b"([5, 6, 9]));
+ assert(find!"a < b"(arr, 4) == arr);
+ assert(find(arr, 0).empty());
+ assert(find(arr, 10).empty());
+ assert(find(arr, 8).empty());
+
+ auto r = assumeSorted!"a > b"([10, 7, 3, 1, 0, 0]);
+ assert(find(r, 3) == assumeSorted!"a > b"([3, 1, 0, 0]));
+ assert(find!"a > b"(r, 8) == r);
+ assert(find!"a < b"(r, 5) == assumeSorted!"a > b"([3, 1, 0, 0]));
+
+ assert(find("hello, world", ',') == ", world");
+ assert(find([1, 2, 3, 5], 4) == []);
+ assert(equal(find(SList!int(1, 2, 3, 4, 5)[], 4), SList!int(4, 5)[]));
+ assert(find!"a > b"([1, 2, 3, 5], 2) == [3, 5]);
+
+ auto a = [ 1, 2, 3 ];
+ assert(find(a, 5).empty); // not found
+ assert(!find(a, 2).empty); // found
+
+ // Case-insensitive find of a string
+ string[] s = [ "Hello", "world", "!" ];
+ assert(!find!("toLower(a) == b")(s, "hello").empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container : SList;
+
+ auto lst = SList!int(1, 2, 5, 7, 3);
+ assert(lst.front == 1);
+ auto r = find(lst[], 5);
+ assert(equal(r, SList!int(5, 7, 3)[]));
+ assert(find([1, 2, 3, 5], 4).empty);
+ assert(equal(find!"a > b"("hello", 'k'), "llo"));
+}
+
+@safe pure nothrow unittest
+{
+ assert(!find ([1, 2, 3], 2).empty);
+ assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty);
+ assert(!find ([1, 2, 3], 2).empty);
+ assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty);
+}
+
+@safe pure unittest
+{
+ import std.meta : AliasSeq;
+ foreach (R; AliasSeq!(string, wstring, dstring))
+ {
+ foreach (E; AliasSeq!(char, wchar, dchar))
+ {
+ assert(find ("hello world", 'w') == "world");
+ assert(find!((a,b)=>a == b)("hello world", 'w') == "world");
+ assert(find ("日c語", 'c') == "c語");
+ assert(find!((a,b)=>a == b)("日c語", 'c') == "c語");
+ assert(find ("0123456789", 'A').empty);
+ static if (E.sizeof >= 2)
+ {
+ assert(find ("日本語", '本') == "本語");
+ assert(find!((a,b)=>a == b)("日本語", '本') == "本語");
+ }
+ }
+ }
+}
+
+@safe unittest
+{
+ //CTFE
+ static assert(find("abc", 'b') == "bc");
+ static assert(find("日b語", 'b') == "b語");
+ static assert(find("日本語", '本') == "本語");
+ static assert(find([1, 2, 3], 2) == [2, 3]);
+
+ static assert(find ([1, 2, 3], 2));
+ static assert(find!((a,b)=>a == b)([1, 2, 3], 2));
+ static assert(find ([1, 2, 3], 2));
+ static assert(find!((a,b)=>a == b)([1, 2, 3], 2));
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+ import std.meta : AliasSeq;
+
+ void dg() @safe pure nothrow
+ {
+ byte[] sarr = [1, 2, 3, 4];
+ ubyte[] uarr = [1, 2, 3, 4];
+ foreach (arr; AliasSeq!(sarr, uarr))
+ {
+ foreach (T; AliasSeq!(byte, ubyte, int, uint))
+ {
+ assert(find(arr, cast(T) 3) == arr[2 .. $]);
+ assert(find(arr, cast(T) 9) == arr[$ .. $]);
+ }
+ assert(find(arr, 256) == arr[$ .. $]);
+ }
+ }
+ dg();
+ assertCTFEable!dg;
+}
+
+@safe unittest
+{
+ // Bugzilla 11603
+ enum Foo : ubyte { A }
+ assert([Foo.A].find(Foo.A).empty == false);
+
+ ubyte x = 0;
+ assert([x].find(x).empty == false);
+}
+
+/**
+Advances the input range $(D haystack) by calling $(D haystack.popFront)
+until either $(D pred(haystack.front)), or $(D
+haystack.empty). Performs $(BIGOH haystack.length) evaluations of $(D
+pred).
+
+To _find the last element of a
+$(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives) $(D haystack) satisfying
+$(D pred), call $(D find!(pred)(retro(haystack))). See $(REF retro, std,range).
+
+`find` behaves similar to `dropWhile` in other languages.
+
+Params:
+
+pred = The predicate for determining if a given element is the one being
+searched for.
+
+haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
+search in.
+
+Returns:
+
+$(D haystack) advanced such that the front element is the one searched for;
+that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no
+such position exists, returns an empty $(D haystack).
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/find_if.html, STL's find_if)
+*/
+InputRange find(alias pred, InputRange)(InputRange haystack)
+if (isInputRange!InputRange)
+{
+ alias R = InputRange;
+ alias predFun = unaryFun!pred;
+ static if (isNarrowString!R)
+ {
+ import std.utf : decode;
+
+ immutable len = haystack.length;
+ size_t i = 0, next = 0;
+ while (next < len)
+ {
+ if (predFun(decode(haystack, next)))
+ return haystack[i .. $];
+ i = next;
+ }
+ return haystack[$ .. $];
+ }
+ else
+ {
+ //standard range
+ for ( ; !haystack.empty; haystack.popFront() )
+ {
+ if (predFun(haystack.front))
+ break;
+ }
+ return haystack;
+ }
+}
+
+///
+@safe unittest
+{
+ auto arr = [ 1, 2, 3, 4, 1 ];
+ assert(find!("a > 2")(arr) == [ 3, 4, 1 ]);
+
+ // with predicate alias
+ bool pred(int x) { return x + 1 > 1.5; }
+ assert(find!(pred)(arr) == arr);
+}
+
+@safe pure unittest
+{
+ int[] r = [ 1, 2, 3 ];
+ assert(find!(a=>a > 2)(r) == [3]);
+ bool pred(int x) { return x + 1 > 1.5; }
+ assert(find!(pred)(r) == r);
+
+ assert(find!(a=>a > 'v')("hello world") == "world");
+ assert(find!(a=>a%4 == 0)("日本語") == "本語");
+}
+
+/**
+Finds the first occurrence of a forward range in another forward range.
+
+Performs $(BIGOH walkLength(haystack) * walkLength(needle)) comparisons in the
+worst case. There are specializations that improve performance by taking
+advantage of $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
+or random access in the given ranges (where possible), depending on the statistics
+of the two ranges' content.
+
+Params:
+
+pred = The predicate to use for comparing respective elements from the haystack
+and the needle. Defaults to simple equality $(D "a == b").
+
+haystack = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+searched in.
+
+needle = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+searched for.
+
+Returns:
+
+$(D haystack) advanced such that $(D needle) is a prefix of it (if no
+such position exists, returns $(D haystack) advanced to termination).
+ */
+R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle)
+if (isForwardRange!R1 && isForwardRange!R2
+ && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
+{
+ static if (!isRandomAccessRange!R1)
+ {
+ static if (is(typeof(pred == "a == b")) && pred == "a == b" && isSomeString!R1 && isSomeString!R2
+ && haystack[0].sizeof == needle[0].sizeof)
+ {
+ // return cast(R1) find(representation(haystack), representation(needle));
+ // Specialization for simple string search
+ alias Representation =
+ Select!(haystack[0].sizeof == 1, ubyte[],
+ Select!(haystack[0].sizeof == 2, ushort[], uint[]));
+ // Will use the array specialization
+ static TO force(TO, T)(T r) @trusted { return cast(TO) r; }
+ return force!R1(.find!(pred, Representation, Representation)
+ (force!Representation(haystack), force!Representation(needle)));
+ }
+ else
+ {
+ return simpleMindedFind!pred(haystack, needle);
+ }
+ }
+ else static if (!isBidirectionalRange!R2 || !hasSlicing!R1)
+ {
+ static if (!is(ElementType!R1 == ElementType!R2))
+ {
+ return simpleMindedFind!pred(haystack, needle);
+ }
+ else
+ {
+ // Prepare the search with needle's first element
+ if (needle.empty)
+ return haystack;
+
+ haystack = .find!pred(haystack, needle.front);
+
+ static if (hasLength!R1 && hasLength!R2 && is(typeof(takeNone(haystack)) == R1))
+ {
+ if (needle.length > haystack.length)
+ return takeNone(haystack);
+ }
+ else
+ {
+ if (haystack.empty)
+ return haystack;
+ }
+
+ needle.popFront();
+ size_t matchLen = 1;
+
+ // Loop invariant: haystack[0 .. matchLen] matches everything in
+ // the initial needle that was popped out of needle.
+ for (;;)
+ {
+ // Extend matchLength as much as possible
+ for (;;)
+ {
+ import std.range : takeNone;
+
+ if (needle.empty || haystack.empty)
+ return haystack;
+
+ static if (hasLength!R1 && is(typeof(takeNone(haystack)) == R1))
+ {
+ if (matchLen == haystack.length)
+ return takeNone(haystack);
+ }
+
+ if (!binaryFun!pred(haystack[matchLen], needle.front))
+ break;
+
+ ++matchLen;
+ needle.popFront();
+ }
+
+ auto bestMatch = haystack[0 .. matchLen];
+ haystack.popFront();
+ haystack = .find!pred(haystack, bestMatch);
+ }
+ }
+ }
+ else // static if (hasSlicing!R1 && isBidirectionalRange!R2)
+ {
+ if (needle.empty) return haystack;
+
+ static if (hasLength!R2)
+ {
+ immutable needleLength = needle.length;
+ }
+ else
+ {
+ immutable needleLength = walkLength(needle.save);
+ }
+ if (needleLength > haystack.length)
+ {
+ return haystack[haystack.length .. haystack.length];
+ }
+ // Optimization in case the ranges are both SortedRanges.
+ // Binary search can be used to find the first occurence
+ // of the first element of the needle in haystack.
+ // When it is found O(walklength(needle)) steps are performed.
+ // 8829 enhancement
+ import std.algorithm.comparison : mismatch;
+ import std.range : SortedRange;
+ static if (is(R1 == R2)
+ && is(R1 : SortedRange!TT, TT)
+ && pred == "a == b")
+ {
+ auto needleFirstElem = needle[0];
+ auto partitions = haystack.trisect(needleFirstElem);
+ auto firstElemLen = partitions[1].length;
+ size_t count = 0;
+
+ if (firstElemLen == 0)
+ return haystack[$ .. $];
+
+ while (needle.front() == needleFirstElem)
+ {
+ needle.popFront();
+ ++count;
+
+ if (count > firstElemLen)
+ return haystack[$ .. $];
+ }
+
+ auto m = mismatch(partitions[2], needle);
+
+ if (m[1].empty)
+ return haystack[partitions[0].length + partitions[1].length - count .. $];
+ }
+ else static if (isRandomAccessRange!R2)
+ {
+ immutable lastIndex = needleLength - 1;
+ auto last = needle[lastIndex];
+ size_t j = lastIndex, skip = 0;
+ for (; j < haystack.length;)
+ {
+ if (!binaryFun!pred(haystack[j], last))
+ {
+ ++j;
+ continue;
+ }
+ immutable k = j - lastIndex;
+ // last elements match
+ for (size_t i = 0;; ++i)
+ {
+ if (i == lastIndex)
+ return haystack[k .. haystack.length];
+ if (!binaryFun!pred(haystack[k + i], needle[i]))
+ break;
+ }
+ if (skip == 0)
+ {
+ skip = 1;
+ while (skip < needleLength && needle[needleLength - 1 - skip] != needle[needleLength - 1])
+ {
+ ++skip;
+ }
+ }
+ j += skip;
+ }
+ }
+ else
+ {
+ // @@@BUG@@@
+ // auto needleBack = moveBack(needle);
+ // Stage 1: find the step
+ size_t step = 1;
+ auto needleBack = needle.back;
+ needle.popBack();
+ for (auto i = needle.save; !i.empty && i.back != needleBack;
+ i.popBack(), ++step)
+ {
+ }
+ // Stage 2: linear find
+ size_t scout = needleLength - 1;
+ for (;;)
+ {
+ if (scout >= haystack.length)
+ break;
+ if (!binaryFun!pred(haystack[scout], needleBack))
+ {
+ ++scout;
+ continue;
+ }
+ // Found a match with the last element in the needle
+ auto cand = haystack[scout + 1 - needleLength .. haystack.length];
+ if (startsWith!pred(cand, needle))
+ {
+ // found
+ return cand;
+ }
+ scout += step;
+ }
+ }
+ return haystack[haystack.length .. haystack.length];
+ }
+}
+
+///
+@safe unittest
+{
+ import std.container : SList;
+ import std.range.primitives : empty;
+ import std.typecons : Tuple;
+
+ assert(find("hello, world", "World").empty);
+ assert(find("hello, world", "wo") == "world");
+ assert([1, 2, 3, 4].find(SList!int(2, 3)[]) == [2, 3, 4]);
+ alias C = Tuple!(int, "x", int, "y");
+ auto a = [C(1,0), C(2,0), C(3,1), C(4,0)];
+ assert(a.find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]);
+ assert(a[1 .. $].find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]);
+}
+
+@safe unittest
+{
+ import std.container : SList;
+ alias C = Tuple!(int, "x", int, "y");
+ assert([C(1,0), C(2,0), C(3,1), C(4,0)].find!"a.x == b"(SList!int(2, 3)[]) == [C(2,0), C(3,1), C(4,0)]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container : SList;
+
+ auto lst = SList!int(1, 2, 5, 7, 3);
+ static assert(isForwardRange!(int[]));
+ static assert(isForwardRange!(typeof(lst[])));
+ auto r = find(lst[], [2, 5]);
+ assert(equal(r, SList!int(2, 5, 7, 3)[]));
+}
+
+@safe unittest
+{
+ import std.range;
+ import std.stdio;
+
+ auto r1 = assumeSorted([1, 2, 3, 3, 3, 4, 5, 6, 7, 8, 8, 8, 10]);
+ auto r2 = assumeSorted([3, 3, 4, 5, 6, 7, 8, 8]);
+ auto r3 = assumeSorted([3, 4, 5, 6, 7, 8]);
+ auto r4 = assumeSorted([4, 5, 6]);
+ auto r5 = assumeSorted([12, 13]);
+ auto r6 = assumeSorted([8, 8, 10, 11]);
+ auto r7 = assumeSorted([3, 3, 3, 3, 3, 3, 3]);
+
+ assert(find(r1, r2) == assumeSorted([3, 3, 4, 5, 6, 7, 8, 8, 8, 10]));
+ assert(find(r1, r3) == assumeSorted([3, 4, 5, 6, 7, 8, 8, 8, 10]));
+ assert(find(r1, r4) == assumeSorted([4, 5, 6, 7, 8, 8, 8, 10]));
+ assert(find(r1, r5).empty());
+ assert(find(r1, r6).empty());
+ assert(find(r1, r7).empty());
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ // @@@BUG@@@ removing static below makes unittest fail
+ static struct BiRange
+ {
+ int[] payload;
+ @property bool empty() { return payload.empty; }
+ @property BiRange save() { return this; }
+ @property ref int front() { return payload[0]; }
+ @property ref int back() { return payload[$ - 1]; }
+ void popFront() { return payload.popFront(); }
+ void popBack() { return payload.popBack(); }
+ }
+ auto r = BiRange([1, 2, 3, 10, 11, 4]);
+ assert(equal(find(r, [10, 11]), [10, 11, 4]));
+}
+
+@safe unittest
+{
+ import std.container : SList;
+
+ assert(find([ 1, 2, 3 ], SList!int(2, 3)[]) == [ 2, 3 ]);
+ assert(find([ 1, 2, 1, 2, 3, 3 ], SList!int(2, 3)[]) == [ 2, 3, 3 ]);
+}
+
+//Bug# 8334
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.range;
+
+ auto haystack = [1, 2, 3, 4, 1, 9, 12, 42];
+ auto needle = [12, 42, 27];
+
+ //different overload of find, but it's the base case.
+ assert(find(haystack, needle).empty);
+
+ assert(find(haystack, takeExactly(filter!"true"(needle), 3)).empty);
+ assert(find(haystack, filter!"true"(needle)).empty);
+}
+
+// Internally used by some find() overloads above
+private R1 simpleMindedFind(alias pred, R1, R2)(R1 haystack, scope R2 needle)
+{
+ enum estimateNeedleLength = hasLength!R1 && !hasLength!R2;
+
+ static if (hasLength!R1)
+ {
+ static if (!hasLength!R2)
+ size_t estimatedNeedleLength = 0;
+ else
+ immutable size_t estimatedNeedleLength = needle.length;
+ }
+
+ bool haystackTooShort()
+ {
+ static if (estimateNeedleLength)
+ {
+ return haystack.length < estimatedNeedleLength;
+ }
+ else
+ {
+ return haystack.empty;
+ }
+ }
+
+ searching:
+ for (;; haystack.popFront())
+ {
+ if (haystackTooShort())
+ {
+ // Failed search
+ static if (hasLength!R1)
+ {
+ static if (is(typeof(haystack[haystack.length ..
+ haystack.length]) : R1))
+ return haystack[haystack.length .. haystack.length];
+ else
+ return R1.init;
+ }
+ else
+ {
+ assert(haystack.empty);
+ return haystack;
+ }
+ }
+ static if (estimateNeedleLength)
+ size_t matchLength = 0;
+ for (auto h = haystack.save, n = needle.save;
+ !n.empty;
+ h.popFront(), n.popFront())
+ {
+ if (h.empty || !binaryFun!pred(h.front, n.front))
+ {
+ // Failed searching n in h
+ static if (estimateNeedleLength)
+ {
+ if (estimatedNeedleLength < matchLength)
+ estimatedNeedleLength = matchLength;
+ }
+ continue searching;
+ }
+ static if (estimateNeedleLength)
+ ++matchLength;
+ }
+ break;
+ }
+ return haystack;
+}
+
+@safe unittest
+{
+ // Test simpleMindedFind for the case where both haystack and needle have
+ // length.
+ struct CustomString
+ {
+ @safe:
+ string _impl;
+
+ // This is what triggers issue 7992.
+ @property size_t length() const { return _impl.length; }
+ @property void length(size_t len) { _impl.length = len; }
+
+ // This is for conformance to the forward range API (we deliberately
+ // make it non-random access so that we will end up in
+ // simpleMindedFind).
+ @property bool empty() const { return _impl.empty; }
+ @property dchar front() const { return _impl.front; }
+ void popFront() { _impl.popFront(); }
+ @property CustomString save() { return this; }
+ }
+
+ // If issue 7992 occurs, this will throw an exception from calling
+ // popFront() on an empty range.
+ auto r = find(CustomString("a"), CustomString("b"));
+ assert(r.empty);
+}
+
+/**
+Finds two or more $(D needles) into a $(D haystack). The predicate $(D
+pred) is used throughout to compare elements. By default, elements are
+compared for equality.
+
+Params:
+
+pred = The predicate to use for comparing elements.
+
+haystack = The target of the search. Must be an input range.
+If any of $(D needles) is a range with elements comparable to
+elements in $(D haystack), then $(D haystack) must be a
+$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+such that the search can backtrack.
+
+needles = One or more items to search for. Each of $(D needles) must
+be either comparable to one element in $(D haystack), or be itself a
+forward range with elements comparable with elements in
+$(D haystack).
+
+Returns:
+
+A tuple containing $(D haystack) positioned to match one of the
+needles and also the 1-based index of the matching element in $(D
+needles) (0 if none of $(D needles) matched, 1 if $(D needles[0])
+matched, 2 if $(D needles[1]) matched...). The first needle to be found
+will be the one that matches. If multiple needles are found at the
+same spot in the range, then the shortest one is the one which matches
+(if multiple needles of the same length are found at the same spot (e.g
+$(D "a") and $(D 'a')), then the left-most of them in the argument list
+matches).
+
+The relationship between $(D haystack) and $(D needles) simply means
+that one can e.g. search for individual $(D int)s or arrays of $(D
+int)s in an array of $(D int)s. In addition, if elements are
+individually comparable, searches of heterogeneous types are allowed
+as well: a $(D double[]) can be searched for an $(D int) or a $(D
+short[]), and conversely a $(D long) can be searched for a $(D float)
+or a $(D double[]). This makes for efficient searches without the need
+to coerce one side of the comparison into the other's side type.
+
+The complexity of the search is $(BIGOH haystack.length *
+max(needles.length)). (For needles that are individual items, length
+is considered to be 1.) The strategy used in searching several
+subranges at once maximizes cache usage by moving in $(D haystack) as
+few times as possible.
+ */
+Tuple!(Range, size_t) find(alias pred = "a == b", Range, Ranges...)
+(Range haystack, Ranges needles)
+if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles))))
+{
+ for (;; haystack.popFront())
+ {
+ size_t r = startsWith!pred(haystack, needles);
+ if (r || haystack.empty)
+ {
+ return tuple(haystack, r);
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ import std.typecons : tuple;
+ int[] a = [ 1, 4, 2, 3 ];
+ assert(find(a, 4) == [ 4, 2, 3 ]);
+ assert(find(a, [ 1, 4 ]) == [ 1, 4, 2, 3 ]);
+ assert(find(a, [ 1, 3 ], 4) == tuple([ 4, 2, 3 ], 2));
+ // Mixed types allowed if comparable
+ assert(find(a, 5, [ 1.2, 3.5 ], 2.0) == tuple([ 2, 3 ], 3));
+}
+
+@safe unittest
+{
+ auto s1 = "Mary has a little lamb";
+ assert(find(s1, "has a", "has an") == tuple("has a little lamb", 1));
+ assert(find(s1, 't', "has a", "has an") == tuple("has a little lamb", 2));
+ assert(find(s1, 't', "has a", 'y', "has an") == tuple("y has a little lamb", 3));
+ assert(find("abc", "bc").length == 2);
+}
+
+@safe unittest
+{
+ import std.algorithm.internal : rndstuff;
+ import std.meta : AliasSeq;
+ import std.uni : toUpper;
+
+ int[] a = [ 1, 2, 3 ];
+ assert(find(a, 5).empty);
+ assert(find(a, 2) == [2, 3]);
+
+ foreach (T; AliasSeq!(int, double))
+ {
+ auto b = rndstuff!(T)();
+ if (!b.length) continue;
+ b[$ / 2] = 200;
+ b[$ / 4] = 200;
+ assert(find(b, 200).length == b.length - b.length / 4);
+ }
+
+ // Case-insensitive find of a string
+ string[] s = [ "Hello", "world", "!" ];
+ assert(find!("toUpper(a) == toUpper(b)")(s, "hello").length == 3);
+
+ static bool f(string a, string b) { return toUpper(a) == toUpper(b); }
+ assert(find!(f)(s, "hello").length == 3);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : rndstuff;
+ import std.meta : AliasSeq;
+ import std.range : retro;
+
+ int[] a = [ 1, 2, 3, 2, 6 ];
+ assert(find(retro(a), 5).empty);
+ assert(equal(find(retro(a), 2), [ 2, 3, 2, 1 ][]));
+
+ foreach (T; AliasSeq!(int, double))
+ {
+ auto b = rndstuff!(T)();
+ if (!b.length) continue;
+ b[$ / 2] = 200;
+ b[$ / 4] = 200;
+ assert(find(retro(b), 200).length ==
+ b.length - (b.length - 1) / 2);
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+
+ int[] a = [ -1, 0, 1, 2, 3, 4, 5 ];
+ int[] b = [ 1, 2, 3 ];
+ assert(find(a, b) == [ 1, 2, 3, 4, 5 ]);
+ assert(find(b, a).empty);
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ auto findRes = find(d, 5);
+ assert(equal(findRes, [5,6,7,8,9,10]));
+ }
+}
+
+/**
+ * Finds $(D needle) in $(D haystack) efficiently using the
+ * $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm,
+ * Boyer-Moore) method.
+ *
+ * Params:
+ * haystack = A random-access range with length and slicing.
+ * needle = A $(LREF BoyerMooreFinder).
+ *
+ * Returns:
+ * $(D haystack) advanced such that $(D needle) is a prefix of it (if no
+ * such position exists, returns $(D haystack) advanced to termination).
+ */
+RandomAccessRange find(RandomAccessRange, alias pred, InputRange)(
+ RandomAccessRange haystack, scope BoyerMooreFinder!(pred, InputRange) needle)
+{
+ return needle.beFound(haystack);
+}
+
+@safe unittest
+{
+ string h = "/homes/aalexand/d/dmd/bin/../lib/libphobos.a(dmain2.o)"~
+ "(.gnu.linkonce.tmain+0x74): In function `main' undefined reference"~
+ " to `_Dmain':";
+ string[] ns = ["libphobos", "function", " undefined", "`", ":"];
+ foreach (n ; ns)
+ {
+ auto p = find(h, boyerMooreFinder(n));
+ assert(!p.empty);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.range.primitives : empty;
+ int[] a = [ -1, 0, 1, 2, 3, 4, 5 ];
+ int[] b = [ 1, 2, 3 ];
+
+ assert(find(a, boyerMooreFinder(b)) == [ 1, 2, 3, 4, 5 ]);
+ assert(find(b, boyerMooreFinder(a)).empty);
+}
+
+@safe unittest
+{
+ auto bm = boyerMooreFinder("for");
+ auto match = find("Moor", bm);
+ assert(match.empty);
+}
+
+// canFind
+/++
+Convenience function. Like find, but only returns whether or not the search
+was successful.
+
+See_Also:
+$(LREF among) for checking a value against multiple possibilities.
+ +/
+template canFind(alias pred="a == b")
+{
+ import std.meta : allSatisfy;
+
+ /++
+ Returns $(D true) if and only if any value $(D v) found in the
+ input range $(D range) satisfies the predicate $(D pred).
+ Performs (at most) $(BIGOH haystack.length) evaluations of $(D pred).
+ +/
+ bool canFind(Range)(Range haystack)
+ if (is(typeof(find!pred(haystack))))
+ {
+ return any!pred(haystack);
+ }
+
+ /++
+ Returns $(D true) if and only if $(D needle) can be found in $(D
+ range). Performs $(BIGOH haystack.length) evaluations of $(D pred).
+ +/
+ bool canFind(Range, Element)(Range haystack, scope Element needle)
+ if (is(typeof(find!pred(haystack, needle))))
+ {
+ return !find!pred(haystack, needle).empty;
+ }
+
+ /++
+ Returns the 1-based index of the first needle found in $(D haystack). If no
+ needle is found, then $(D 0) is returned.
+
+ So, if used directly in the condition of an if statement or loop, the result
+ will be $(D true) if one of the needles is found and $(D false) if none are
+ found, whereas if the result is used elsewhere, it can either be cast to
+ $(D bool) for the same effect or used to get which needle was found first
+ without having to deal with the tuple that $(D LREF find) returns for the
+ same operation.
+ +/
+ size_t canFind(Range, Ranges...)(Range haystack, scope Ranges needles)
+ if (Ranges.length > 1 &&
+ allSatisfy!(isForwardRange, Ranges) &&
+ is(typeof(find!pred(haystack, needles))))
+ {
+ return find!pred(haystack, needles)[1];
+ }
+}
+
+///
+@safe unittest
+{
+ assert(canFind([0, 1, 2, 3], 2) == true);
+ assert(canFind([0, 1, 2, 3], [1, 2], [2, 3]));
+ assert(canFind([0, 1, 2, 3], [1, 2], [2, 3]) == 1);
+ assert(canFind([0, 1, 2, 3], [1, 7], [2, 3]));
+ assert(canFind([0, 1, 2, 3], [1, 7], [2, 3]) == 2);
+
+ assert(canFind([0, 1, 2, 3], 4) == false);
+ assert(!canFind([0, 1, 2, 3], [1, 3], [2, 4]));
+ assert(canFind([0, 1, 2, 3], [1, 3], [2, 4]) == 0);
+}
+
+/**
+ * Example using a custom predicate.
+ * Note that the needle appears as the second argument of the predicate.
+ */
+@safe unittest
+{
+ auto words = [
+ "apple",
+ "beeswax",
+ "cardboard"
+ ];
+ assert(!canFind(words, "bees"));
+ assert( canFind!((string a, string b) => a.startsWith(b))(words, "bees"));
+}
+
+@safe unittest
+{
+ import std.algorithm.internal : rndstuff;
+
+ auto a = rndstuff!(int)();
+ if (a.length)
+ {
+ auto b = a[a.length / 2];
+ assert(canFind(a, b));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ assert(equal!(canFind!"a < b")([[1, 2, 3], [7, 8, 9]], [2, 8]));
+}
+
+// findAdjacent
+/**
+Advances $(D r) until it finds the first two adjacent elements $(D a),
+$(D b) that satisfy $(D pred(a, b)). Performs $(BIGOH r.length)
+evaluations of $(D pred).
+
+Params:
+ pred = The predicate to satisfy.
+ r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to
+ search in.
+
+Returns:
+$(D r) advanced to the first occurrence of two adjacent elements that satisfy
+the given predicate. If there are no such two elements, returns $(D r) advanced
+until empty.
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/adjacent_find.html, STL's adjacent_find)
+*/
+Range findAdjacent(alias pred = "a == b", Range)(Range r)
+if (isForwardRange!(Range))
+{
+ auto ahead = r.save;
+ if (!ahead.empty)
+ {
+ for (ahead.popFront(); !ahead.empty; r.popFront(), ahead.popFront())
+ {
+ if (binaryFun!(pred)(r.front, ahead.front)) return r;
+ }
+ }
+ static if (!isInfinite!Range)
+ return ahead;
+}
+
+///
+@safe unittest
+{
+ int[] a = [ 11, 10, 10, 9, 8, 8, 7, 8, 9 ];
+ auto r = findAdjacent(a);
+ assert(r == [ 10, 10, 9, 8, 8, 7, 8, 9 ]);
+ auto p = findAdjacent!("a < b")(a);
+ assert(p == [ 7, 8, 9 ]);
+
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+ import std.range;
+
+ int[] a = [ 11, 10, 10, 9, 8, 8, 7, 8, 9 ];
+ auto p = findAdjacent(a);
+ assert(p == [10, 10, 9, 8, 8, 7, 8, 9 ]);
+ p = findAdjacent!("a < b")(a);
+ assert(p == [7, 8, 9]);
+ // empty
+ a = [];
+ p = findAdjacent(a);
+ assert(p.empty);
+ // not found
+ a = [ 1, 2, 3, 4, 5 ];
+ p = findAdjacent(a);
+ assert(p.empty);
+ p = findAdjacent!"a > b"(a);
+ assert(p.empty);
+ ReferenceForwardRange!int rfr = new ReferenceForwardRange!int([1, 2, 3, 2, 2, 3]);
+ assert(equal(findAdjacent(rfr), [2, 2, 3]));
+
+ // Issue 9350
+ assert(!repeat(1).findAdjacent().empty);
+}
+
+// findAmong
+/**
+Searches the given range for an element that matches one of the given choices.
+
+Advances $(D seq) by calling $(D seq.popFront) until either
+$(D find!(pred)(choices, seq.front)) is $(D true), or $(D seq) becomes empty.
+Performs $(BIGOH seq.length * choices.length) evaluations of $(D pred).
+
+Params:
+ pred = The predicate to use for determining a match.
+ seq = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
+ search.
+ choices = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+ of possible choices.
+
+Returns:
+$(D seq) advanced to the first matching element, or until empty if there are no
+matching elements.
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/find_first_of.html, STL's find_first_of)
+*/
+InputRange findAmong(alias pred = "a == b", InputRange, ForwardRange)(
+ InputRange seq, ForwardRange choices)
+if (isInputRange!InputRange && isForwardRange!ForwardRange)
+{
+ for (; !seq.empty && find!pred(choices, seq.front).empty; seq.popFront())
+ {
+ }
+ return seq;
+}
+
+///
+@safe unittest
+{
+ int[] a = [ -1, 0, 1, 2, 3, 4, 5 ];
+ int[] b = [ 3, 1, 2 ];
+ assert(findAmong(a, b) == a[2 .. $]);
+}
+
+@safe unittest
+{
+ int[] a = [ -1, 0, 2, 1, 2, 3, 4, 5 ];
+ int[] b = [ 1, 2, 3 ];
+ assert(findAmong(a, b) == [2, 1, 2, 3, 4, 5 ]);
+ assert(findAmong(b, [ 4, 6, 7 ][]).empty);
+ assert(findAmong!("a == b")(a, b).length == a.length - 2);
+ assert(findAmong!("a == b")(b, [ 4, 6, 7 ][]).empty);
+}
+
+// findSkip
+/**
+ * Finds $(D needle) in $(D haystack) and positions $(D haystack)
+ * right after the first occurrence of $(D needle).
+ *
+ * Params:
+ * haystack = The
+ * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search
+ * in.
+ * needle = The
+ * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search
+ * for.
+ *
+ * Returns: $(D true) if the needle was found, in which case $(D haystack) is
+ * positioned after the end of the first occurrence of $(D needle); otherwise
+ * $(D false), leaving $(D haystack) untouched.
+ */
+bool findSkip(alias pred = "a == b", R1, R2)(ref R1 haystack, R2 needle)
+if (isForwardRange!R1 && isForwardRange!R2
+ && is(typeof(binaryFun!pred(haystack.front, needle.front))))
+{
+ auto parts = findSplit!pred(haystack, needle);
+ if (parts[1].empty) return false;
+ // found
+ haystack = parts[2];
+ return true;
+}
+
+///
+@safe unittest
+{
+ import std.range.primitives : empty;
+ // Needle is found; s is replaced by the substring following the first
+ // occurrence of the needle.
+ string s = "abcdef";
+ assert(findSkip(s, "cd") && s == "ef");
+
+ // Needle is not found; s is left untouched.
+ s = "abcdef";
+ assert(!findSkip(s, "cxd") && s == "abcdef");
+
+ // If the needle occurs at the end of the range, the range is left empty.
+ s = "abcdef";
+ assert(findSkip(s, "def") && s.empty);
+}
+
+/**
+These functions find the first occurrence of `needle` in `haystack` and then
+split `haystack` as follows.
+
+`findSplit` returns a tuple `result` containing $(I three) ranges. `result[0]`
+is the portion of `haystack` before `needle`, `result[1]` is the portion of
+`haystack` that matches `needle`, and `result[2]` is the portion of `haystack`
+after the match. If `needle` was not found, `result[0]` comprehends `haystack`
+entirely and `result[1]` and `result[2]` are empty.
+
+`findSplitBefore` returns a tuple `result` containing two ranges. `result[0]` is
+the portion of `haystack` before `needle`, and `result[1]` is the balance of
+`haystack` starting with the match. If `needle` was not found, `result[0]`
+comprehends `haystack` entirely and `result[1]` is empty.
+
+`findSplitAfter` returns a tuple `result` containing two ranges.
+`result[0]` is the portion of `haystack` up to and including the
+match, and `result[1]` is the balance of `haystack` starting
+after the match. If `needle` was not found, `result[0]` is empty
+and `result[1]` is `haystack`.
+
+In all cases, the concatenation of the returned ranges spans the
+entire `haystack`.
+
+If `haystack` is a random-access range, all three components of the tuple have
+the same type as `haystack`. Otherwise, `haystack` must be a
+$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and
+the type of `result[0]` and `result[1]` is the same as $(REF takeExactly,
+std,range).
+
+Params:
+ pred = Predicate to use for comparing needle against haystack.
+ haystack = The range to search.
+ needle = What to look for.
+
+Returns:
+
+A sub-type of `Tuple!()` of the split portions of `haystack` (see above for
+details). This sub-type of `Tuple!()` has `opCast` defined for `bool`. This
+`opCast` returns `true` when the separating `needle` was found
+(`!result[1].empty`) and `false` otherwise. This enables the convenient idiom
+shown in the following example.
+
+Example:
+---
+if (const split = haystack.findSplit(needle))
+{
+ doSomethingWithSplit(split);
+}
+---
+ */
+auto findSplit(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
+if (isForwardRange!R1 && isForwardRange!R2)
+{
+ static struct Result(S1, S2) if (isForwardRange!S1 &&
+ isForwardRange!S2)
+ {
+ this(S1 pre, S1 separator, S2 post)
+ {
+ asTuple = typeof(asTuple)(pre, separator, post);
+ }
+ void opAssign(typeof(asTuple) rhs)
+ {
+ asTuple = rhs;
+ }
+ Tuple!(S1, S1, S2) asTuple;
+ bool opCast(T : bool)()
+ {
+ return !asTuple[1].empty;
+ }
+ alias asTuple this;
+ }
+
+ static if (isSomeString!R1 && isSomeString!R2
+ || (isRandomAccessRange!R1 && hasSlicing!R1 && hasLength!R1 && hasLength!R2))
+ {
+ auto balance = find!pred(haystack, needle);
+ immutable pos1 = haystack.length - balance.length;
+ immutable pos2 = balance.empty ? pos1 : pos1 + needle.length;
+ return Result!(typeof(haystack[0 .. pos1]),
+ typeof(haystack[pos2 .. haystack.length]))(haystack[0 .. pos1],
+ haystack[pos1 .. pos2],
+ haystack[pos2 .. haystack.length]);
+ }
+ else
+ {
+ import std.range : takeExactly;
+ auto original = haystack.save;
+ auto h = haystack.save;
+ auto n = needle.save;
+ size_t pos1, pos2;
+ while (!n.empty && !h.empty)
+ {
+ if (binaryFun!pred(h.front, n.front))
+ {
+ h.popFront();
+ n.popFront();
+ ++pos2;
+ }
+ else
+ {
+ haystack.popFront();
+ n = needle.save;
+ h = haystack.save;
+ pos2 = ++pos1;
+ }
+ }
+ return Result!(typeof(takeExactly(original, pos1)),
+ typeof(h))(takeExactly(original, pos1),
+ takeExactly(haystack, pos2 - pos1),
+ h);
+ }
+}
+
+/// Ditto
+auto findSplitBefore(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
+if (isForwardRange!R1 && isForwardRange!R2)
+{
+ static struct Result(S1, S2) if (isForwardRange!S1 &&
+ isForwardRange!S2)
+ {
+ this(S1 pre, S2 post)
+ {
+ asTuple = typeof(asTuple)(pre, post);
+ }
+ void opAssign(typeof(asTuple) rhs)
+ {
+ asTuple = rhs;
+ }
+ Tuple!(S1, S2) asTuple;
+ bool opCast(T : bool)()
+ {
+ return !asTuple[0].empty;
+ }
+ alias asTuple this;
+ }
+
+ static if (isSomeString!R1 && isSomeString!R2
+ || (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2))
+ {
+ auto balance = find!pred(haystack, needle);
+ immutable pos = haystack.length - balance.length;
+ return Result!(typeof(haystack[0 .. pos]),
+ typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos],
+ haystack[pos .. haystack.length]);
+ }
+ else
+ {
+ import std.range : takeExactly;
+ auto original = haystack.save;
+ auto h = haystack.save;
+ auto n = needle.save;
+ size_t pos;
+ while (!n.empty && !h.empty)
+ {
+ if (binaryFun!pred(h.front, n.front))
+ {
+ h.popFront();
+ n.popFront();
+ }
+ else
+ {
+ haystack.popFront();
+ n = needle.save;
+ h = haystack.save;
+ ++pos;
+ }
+ }
+ return Result!(typeof(takeExactly(original, pos)),
+ typeof(haystack))(takeExactly(original, pos),
+ haystack);
+ }
+}
+
+/// Ditto
+auto findSplitAfter(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
+if (isForwardRange!R1 && isForwardRange!R2)
+{
+ static struct Result(S1, S2) if (isForwardRange!S1 &&
+ isForwardRange!S2)
+ {
+ this(S1 pre, S2 post)
+ {
+ asTuple = typeof(asTuple)(pre, post);
+ }
+ void opAssign(typeof(asTuple) rhs)
+ {
+ asTuple = rhs;
+ }
+ Tuple!(S1, S2) asTuple;
+ bool opCast(T : bool)()
+ {
+ return !asTuple[1].empty;
+ }
+ alias asTuple this;
+ }
+
+ static if (isSomeString!R1 && isSomeString!R2
+ || isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2)
+ {
+ auto balance = find!pred(haystack, needle);
+ immutable pos = balance.empty ? 0 : haystack.length - balance.length + needle.length;
+ return Result!(typeof(haystack[0 .. pos]),
+ typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos],
+ haystack[pos .. haystack.length]);
+ }
+ else
+ {
+ import std.range : takeExactly;
+ auto original = haystack.save;
+ auto h = haystack.save;
+ auto n = needle.save;
+ size_t pos1, pos2;
+ while (!n.empty)
+ {
+ if (h.empty)
+ {
+ // Failed search
+ return Result!(typeof(takeExactly(original, 0)),
+ typeof(original))(takeExactly(original, 0),
+ original);
+ }
+ if (binaryFun!pred(h.front, n.front))
+ {
+ h.popFront();
+ n.popFront();
+ ++pos2;
+ }
+ else
+ {
+ haystack.popFront();
+ n = needle.save;
+ h = haystack.save;
+ pos2 = ++pos1;
+ }
+ }
+ return Result!(typeof(takeExactly(original, pos2)),
+ typeof(h))(takeExactly(original, pos2),
+ h);
+ }
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.range.primitives : empty;
+
+ auto a = "Carl Sagan Memorial Station";
+ auto r = findSplit(a, "Velikovsky");
+ import std.typecons : isTuple;
+ static assert(isTuple!(typeof(r.asTuple)));
+ static assert(isTuple!(typeof(r)));
+ assert(!r);
+ assert(r[0] == a);
+ assert(r[1].empty);
+ assert(r[2].empty);
+ r = findSplit(a, " ");
+ assert(r[0] == "Carl");
+ assert(r[1] == " ");
+ assert(r[2] == "Sagan Memorial Station");
+ auto r1 = findSplitBefore(a, "Sagan");
+ assert(r1);
+ assert(r1[0] == "Carl ");
+ assert(r1[1] == "Sagan Memorial Station");
+ auto r2 = findSplitAfter(a, "Sagan");
+ assert(r2);
+ assert(r2[0] == "Carl Sagan");
+ assert(r2[1] == " Memorial Station");
+}
+
+/// Use $(REF only, std,range) to find single elements:
+@safe pure nothrow unittest
+{
+ import std.range : only;
+ assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]);
+}
+
+@safe pure nothrow unittest
+{
+ import std.range.primitives : empty;
+
+ auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
+ auto r = findSplit(a, [9, 1]);
+ assert(!r);
+ assert(r[0] == a);
+ assert(r[1].empty);
+ assert(r[2].empty);
+ r = findSplit(a, [3]);
+ assert(r);
+ assert(r[0] == a[0 .. 2]);
+ assert(r[1] == a[2 .. 3]);
+ assert(r[2] == a[3 .. $]);
+
+ auto r1 = findSplitBefore(a, [9, 1]);
+ assert(r1);
+ assert(r1[0] == a);
+ assert(r1[1].empty);
+ r1 = findSplitBefore(a, [3, 4]);
+ assert(r1);
+ assert(r1[0] == a[0 .. 2]);
+ assert(r1[1] == a[2 .. $]);
+
+ auto r2 = findSplitAfter(a, [9, 1]);
+ assert(r2);
+ assert(r2[0].empty);
+ assert(r2[1] == a);
+ r2 = findSplitAfter(a, [3, 4]);
+ assert(r2);
+ assert(r2[0] == a[0 .. 4]);
+ assert(r2[1] == a[4 .. $]);
+}
+
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+
+ auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ];
+ auto fwd = filter!"a > 0"(a);
+ auto r = findSplit(fwd, [9, 1]);
+ assert(!r);
+ assert(equal(r[0], a));
+ assert(r[1].empty);
+ assert(r[2].empty);
+ r = findSplit(fwd, [3]);
+ assert(r);
+ assert(equal(r[0], a[0 .. 2]));
+ assert(equal(r[1], a[2 .. 3]));
+ assert(equal(r[2], a[3 .. $]));
+
+ auto r1 = findSplitBefore(fwd, [9, 1]);
+ assert(r1);
+ assert(equal(r1[0], a));
+ assert(r1[1].empty);
+ r1 = findSplitBefore(fwd, [3, 4]);
+ assert(r1);
+ assert(equal(r1[0], a[0 .. 2]));
+ assert(equal(r1[1], a[2 .. $]));
+
+ auto r2 = findSplitAfter(fwd, [9, 1]);
+ assert(r2);
+ assert(r2[0].empty);
+ assert(equal(r2[1], a));
+ r2 = findSplitAfter(fwd, [3, 4]);
+ assert(r2);
+ assert(equal(r2[0], a[0 .. 4]));
+ assert(equal(r2[1], a[4 .. $]));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ auto str = "sep,one,sep,two";
+
+ auto split = str.findSplitAfter(",");
+ assert(split[0] == "sep,");
+
+ split = split[1].findSplitAfter(",");
+ assert(split[0] == "one,");
+
+ split = split[1].findSplitBefore(",");
+ assert(split[0] == "sep");
+}
+
+@safe pure nothrow @nogc unittest
+{
+ auto str = "sep,one,sep,two";
+
+ auto split = str.findSplitBefore(",two");
+ assert(split[0] == "sep,one,sep");
+ assert(split[1] == ",two");
+
+ split = split[0].findSplitBefore(",sep");
+ assert(split[0] == "sep,one");
+ assert(split[1] == ",sep");
+
+ split = split[0].findSplitAfter(",");
+ assert(split[0] == "sep,");
+ assert(split[1] == "one");
+}
+
+// minCount
+/**
+
+Computes the minimum (respectively maximum) of `range` along with its number of
+occurrences. Formally, the minimum is a value `x` in `range` such that $(D
+pred(a, x)) is `false` for all values `a` in `range`. Conversely, the maximum is
+a value `x` in `range` such that $(D pred(x, a)) is `false` for all values `a`
+in `range` (note the swapped arguments to `pred`).
+
+These functions may be used for computing arbitrary extrema by choosing `pred`
+appropriately. For corrrect functioning, `pred` must be a strict partial order,
+i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and
+irreflexive ($(D pred(a, a)) is `false`). The $(LUCKY trichotomy property of
+inequality) is not required: these algoritms consider elements `a` and `b` equal
+(for the purpose of counting) if `pred` puts them in the same equivalence class,
+i.e. $(D !pred(a, b) && !pred(b, a)).
+
+Params:
+ pred = The ordering predicate to use to determine the extremum (minimum
+ or maximum).
+ range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to count.
+
+Returns: The minimum, respectively maximum element of a range together with the
+number it occurs in the range.
+
+Throws: `Exception` if `range.empty`.
+ */
+Tuple!(ElementType!Range, size_t)
+minCount(alias pred = "a < b", Range)(Range range)
+if (isInputRange!Range && !isInfinite!Range &&
+ is(typeof(binaryFun!pred(range.front, range.front))))
+{
+ import std.algorithm.internal : algoFormat;
+ import std.exception : enforce;
+
+ alias T = ElementType!Range;
+ alias UT = Unqual!T;
+ alias RetType = Tuple!(T, size_t);
+
+ static assert(is(typeof(RetType(range.front, 1))),
+ algoFormat("Error: Cannot call minCount on a %s, because it is not possible "~
+ "to copy the result value (a %s) into a Tuple.", Range.stringof, T.stringof));
+
+ enforce(!range.empty, "Can't count elements from an empty range");
+ size_t occurrences = 1;
+
+ static if (isForwardRange!Range)
+ {
+ Range least = range.save;
+ for (range.popFront(); !range.empty; range.popFront())
+ {
+ if (binaryFun!pred(least.front, range.front))
+ {
+ assert(!binaryFun!pred(range.front, least.front),
+ "min/maxPos: predicate must be a strict partial order.");
+ continue;
+ }
+ if (binaryFun!pred(range.front, least.front))
+ {
+ // change the min
+ least = range.save;
+ occurrences = 1;
+ }
+ else
+ ++occurrences;
+ }
+ return RetType(least.front, occurrences);
+ }
+ else static if (isAssignable!(UT, T) || (!hasElaborateAssign!UT && isAssignable!UT))
+ {
+ UT v = UT.init;
+ static if (isAssignable!(UT, T)) v = range.front;
+ else v = cast(UT) range.front;
+
+ for (range.popFront(); !range.empty; range.popFront())
+ {
+ if (binaryFun!pred(*cast(T*)&v, range.front)) continue;
+ if (binaryFun!pred(range.front, *cast(T*)&v))
+ {
+ // change the min
+ static if (isAssignable!(UT, T)) v = range.front;
+ else v = cast(UT) range.front; //Safe because !hasElaborateAssign!UT
+ occurrences = 1;
+ }
+ else
+ ++occurrences;
+ }
+ return RetType(*cast(T*)&v, occurrences);
+ }
+ else static if (hasLvalueElements!Range)
+ {
+ import std.algorithm.internal : addressOf;
+ T* p = addressOf(range.front);
+ for (range.popFront(); !range.empty; range.popFront())
+ {
+ if (binaryFun!pred(*p, range.front)) continue;
+ if (binaryFun!pred(range.front, *p))
+ {
+ // change the min
+ p = addressOf(range.front);
+ occurrences = 1;
+ }
+ else
+ ++occurrences;
+ }
+ return RetType(*p, occurrences);
+ }
+ else
+ static assert(false,
+ algoFormat("Sorry, can't find the minCount of a %s: Don't know how "~
+ "to keep track of the smallest %s element.", Range.stringof, T.stringof));
+}
+
+/// Ditto
+Tuple!(ElementType!Range, size_t)
+maxCount(alias pred = "a < b", Range)(Range range)
+if (isInputRange!Range && !isInfinite!Range &&
+ is(typeof(binaryFun!pred(range.front, range.front))))
+{
+ return range.minCount!((a, b) => binaryFun!pred(b, a));
+}
+
+///
+@safe unittest
+{
+ import std.conv : text;
+ import std.typecons : tuple;
+
+ int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+ // Minimum is 1 and occurs 3 times
+ assert(a.minCount == tuple(1, 3));
+ // Maximum is 4 and occurs 2 times
+ assert(a.maxCount == tuple(4, 2));
+}
+
+@system unittest
+{
+ import std.conv : text;
+ import std.exception : assertThrown;
+ import std.internal.test.dummyrange;
+
+ int[][] b = [ [4], [2, 4], [4], [4] ];
+ auto c = minCount!("a[0] < b[0]")(b);
+ assert(c == tuple([2, 4], 1), text(c[0]));
+
+ //Test empty range
+ assertThrown(minCount(b[$..$]));
+
+ //test with reference ranges. Test both input and forward.
+ assert(minCount(new ReferenceInputRange!int([1, 2, 1, 0, 2, 0])) == tuple(0, 2));
+ assert(minCount(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])) == tuple(0, 2));
+}
+
+@system unittest
+{
+ import std.conv : text;
+ import std.meta : AliasSeq;
+
+ static struct R(T) //input range
+ {
+ T[] arr;
+ alias arr this;
+ }
+
+ immutable a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+ R!(immutable int) b = R!(immutable int)(a);
+
+ assert(minCount(a) == tuple(1, 3));
+ assert(minCount(b) == tuple(1, 3));
+ assert(minCount!((ref immutable int a, ref immutable int b) => (a > b))(a) == tuple(4, 2));
+ assert(minCount!((ref immutable int a, ref immutable int b) => (a > b))(b) == tuple(4, 2));
+
+ immutable(int[])[] c = [ [4], [2, 4], [4], [4] ];
+ assert(minCount!("a[0] < b[0]")(c) == tuple([2, 4], 1), text(c[0]));
+
+ static struct S1
+ {
+ int i;
+ }
+ alias IS1 = immutable(S1);
+ static assert( isAssignable!S1);
+ static assert( isAssignable!(S1, IS1));
+
+ static struct S2
+ {
+ int* p;
+ this(ref immutable int i) immutable {p = &i;}
+ this(ref int i) {p = &i;}
+ @property ref inout(int) i() inout {return *p;}
+ bool opEquals(const S2 other) const {return i == other.i;}
+ }
+ alias IS2 = immutable(S2);
+ static assert( isAssignable!S2);
+ static assert(!isAssignable!(S2, IS2));
+ static assert(!hasElaborateAssign!S2);
+
+ static struct S3
+ {
+ int i;
+ void opAssign(ref S3 other) @disable;
+ }
+ static assert(!isAssignable!S3);
+
+ foreach (Type; AliasSeq!(S1, IS1, S2, IS2, S3))
+ {
+ static if (is(Type == immutable)) alias V = immutable int;
+ else alias V = int;
+ V one = 1, two = 2;
+ auto r1 = [Type(two), Type(one), Type(one)];
+ auto r2 = R!Type(r1);
+ assert(minCount!"a.i < b.i"(r1) == tuple(Type(one), 2));
+ assert(minCount!"a.i < b.i"(r2) == tuple(Type(one), 2));
+ assert(one == 1 && two == 2);
+ }
+}
+
+/**
+Iterates the passed range and returns the minimal element.
+A custom mapping function can be passed to `map`.
+In other languages this is sometimes called `argmin`.
+
+Complexity: O(n)
+ Exactly `n - 1` comparisons are needed.
+
+Params:
+ map = custom accessor for the comparison key
+ r = range from which the minimal element will be selected
+ seed = custom seed to use as initial element
+
+Returns: The minimal element of the passed-in range.
+
+See_Also:
+ $(REF min, std,algorithm,comparison)
+*/
+auto minElement(alias map, Range)(Range r)
+if (isInputRange!Range && !isInfinite!Range)
+{
+ return extremum!map(r);
+}
+
+/// ditto
+auto minElement(Range)(Range r)
+ if (isInputRange!Range && !isInfinite!Range)
+{
+ return extremum(r);
+}
+
+/// ditto
+auto minElement(alias map, Range, RangeElementType = ElementType!Range)
+ (Range r, RangeElementType seed)
+if (isInputRange!Range && !isInfinite!Range &&
+ !is(CommonType!(ElementType!Range, RangeElementType) == void))
+{
+ return extremum!map(r, seed);
+}
+
+/// ditto
+auto minElement(Range, RangeElementType = ElementType!Range)
+ (Range r, RangeElementType seed)
+ if (isInputRange!Range && !isInfinite!Range &&
+ !is(CommonType!(ElementType!Range, RangeElementType) == void))
+{
+ return extremum(r, seed);
+}
+
+///
+@safe pure unittest
+{
+ import std.range : enumerate;
+ import std.typecons : tuple;
+
+ assert([2, 1, 4, 3].minElement == 1);
+
+ // allows to get the index of an element too
+ assert([5, 3, 7, 9].enumerate.minElement!"a.value" == tuple(1, 3));
+
+ // any custom accessor can be passed
+ assert([[0, 4], [1, 2]].minElement!"a[1]" == [1, 2]);
+
+ // can be seeded
+ int[] arr;
+ assert(arr.minElement(1) == 1);
+}
+
+@safe pure unittest
+{
+ import std.range : enumerate, iota;
+ // supports mapping
+ assert([3, 4, 5, 1, 2].enumerate.minElement!"a.value" == tuple(3, 1));
+ assert([5, 2, 4].enumerate.minElement!"a.value" == tuple(1, 2));
+
+ // forward ranges
+ assert(iota(1, 5).minElement() == 1);
+ assert(iota(2, 5).enumerate.minElement!"a.value" == tuple(0, 2));
+
+ // should work with const
+ const(int)[] immArr = [2, 1, 3];
+ assert(immArr.minElement == 1);
+
+ // should work with immutable
+ immutable(int)[] immArr2 = [2, 1, 3];
+ assert(immArr2.minElement == 1);
+
+ // with strings
+ assert(["b", "a", "c"].minElement == "a");
+
+ // with all dummy ranges
+ import std.internal.test.dummyrange;
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ assert(d.minElement == 1);
+ assert(d.minElement!(a => a) == 1);
+ }
+
+ // with empty, but seeded ranges
+ int[] arr;
+ assert(arr.minElement(42) == 42);
+ assert(arr.minElement!(a => a)(42) == 42);
+}
+
+@nogc @safe nothrow pure unittest
+{
+ static immutable arr = [7, 3, 4, 2, 1, 8];
+ assert(arr.minElement == 1);
+
+ static immutable arr2d = [[1, 9], [3, 1], [4, 2]];
+ assert(arr2d.minElement!"a[1]" == arr2d[1]);
+}
+
+/**
+Iterates the passed range and returns the maximal element.
+A custom mapping function can be passed to `map`.
+In other languages this is sometimes called `argmax`.
+
+Complexity:
+ Exactly `n - 1` comparisons are needed.
+
+Params:
+ map = custom accessor for the comparison key
+ r = range from which the maximum will be selected
+ seed = custom seed to use as initial element
+
+Returns: The maximal element of the passed-in range.
+
+See_Also:
+ $(REF max, std,algorithm,comparison)
+*/
+auto maxElement(alias map, Range)(Range r)
+if (isInputRange!Range && !isInfinite!Range)
+{
+ return extremum!(map, "a > b")(r);
+}
+
+/// ditto
+auto maxElement(Range)(Range r)
+if (isInputRange!Range && !isInfinite!Range)
+{
+ return extremum!`a > b`(r);
+}
+
+/// ditto
+auto maxElement(alias map, Range, RangeElementType = ElementType!Range)
+ (Range r, RangeElementType seed)
+if (isInputRange!Range && !isInfinite!Range &&
+ !is(CommonType!(ElementType!Range, RangeElementType) == void))
+{
+ return extremum!(map, "a > b")(r, seed);
+}
+
+/// ditto
+auto maxElement(Range, RangeElementType = ElementType!Range)
+ (Range r, RangeElementType seed)
+if (isInputRange!Range && !isInfinite!Range &&
+ !is(CommonType!(ElementType!Range, RangeElementType) == void))
+{
+ return extremum!`a > b`(r, seed);
+}
+
+///
+@safe pure unittest
+{
+ import std.range : enumerate;
+ import std.typecons : tuple;
+ assert([2, 1, 4, 3].maxElement == 4);
+
+ // allows to get the index of an element too
+ assert([2, 1, 4, 3].enumerate.maxElement!"a.value" == tuple(2, 4));
+
+ // any custom accessor can be passed
+ assert([[0, 4], [1, 2]].maxElement!"a[1]" == [0, 4]);
+
+ // can be seeded
+ int[] arr;
+ assert(arr.minElement(1) == 1);
+}
+
+@safe pure unittest
+{
+ import std.range : enumerate, iota;
+
+ // supports mapping
+ assert([3, 4, 5, 1, 2].enumerate.maxElement!"a.value" == tuple(2, 5));
+ assert([5, 2, 4].enumerate.maxElement!"a.value" == tuple(0, 5));
+
+ // forward ranges
+ assert(iota(1, 5).maxElement() == 4);
+ assert(iota(2, 5).enumerate.maxElement!"a.value" == tuple(2, 4));
+ assert(iota(4, 14).enumerate.maxElement!"a.value" == tuple(9, 13));
+
+ // should work with const
+ const(int)[] immArr = [2, 3, 1];
+ assert(immArr.maxElement == 3);
+
+ // should work with immutable
+ immutable(int)[] immArr2 = [2, 3, 1];
+ assert(immArr2.maxElement == 3);
+
+ // with strings
+ assert(["a", "c", "b"].maxElement == "c");
+
+ // with all dummy ranges
+ import std.internal.test.dummyrange;
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ assert(d.maxElement == 10);
+ assert(d.maxElement!(a => a) == 10);
+ }
+
+ // with empty, but seeded ranges
+ int[] arr;
+ assert(arr.maxElement(42) == 42);
+ assert(arr.maxElement!(a => a)(42) == 42);
+
+}
+
+@nogc @safe nothrow pure unittest
+{
+ static immutable arr = [7, 3, 8, 2, 1, 4];
+ assert(arr.maxElement == 8);
+
+ static immutable arr2d = [[1, 3], [3, 9], [4, 2]];
+ assert(arr2d.maxElement!"a[1]" == arr2d[1]);
+}
+
+// minPos
+/**
+Computes a subrange of `range` starting at the first occurrence of `range`'s
+minimum (respectively maximum) and with the same ending as `range`, or the
+empty range if `range` itself is empty.
+
+Formally, the minimum is a value `x` in `range` such that $(D pred(a, x)) is
+`false` for all values `a` in `range`. Conversely, the maximum is a value `x` in
+`range` such that $(D pred(x, a)) is `false` for all values `a` in `range` (note
+the swapped arguments to `pred`).
+
+These functions may be used for computing arbitrary extrema by choosing `pred`
+appropriately. For corrrect functioning, `pred` must be a strict partial order,
+i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and
+irreflexive ($(D pred(a, a)) is `false`).
+
+Params:
+ pred = The ordering predicate to use to determine the extremum (minimum or
+ maximum) element.
+ range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to search.
+
+Returns: The position of the minimum (respectively maximum) element of forward
+range `range`, i.e. a subrange of `range` starting at the position of its
+smallest (respectively largest) element and with the same ending as `range`.
+
+*/
+Range minPos(alias pred = "a < b", Range)(Range range)
+if (isForwardRange!Range && !isInfinite!Range &&
+ is(typeof(binaryFun!pred(range.front, range.front))))
+{
+ static if (hasSlicing!Range && isRandomAccessRange!Range && hasLength!Range)
+ {
+ // Prefer index-based access
+ size_t pos = 0;
+ foreach (i; 1 .. range.length)
+ {
+ if (binaryFun!pred(range[i], range[pos]))
+ {
+ pos = i;
+ }
+ }
+ return range[pos .. range.length];
+ }
+ else
+ {
+ auto result = range.save;
+ if (range.empty) return result;
+ for (range.popFront(); !range.empty; range.popFront())
+ {
+ // Note: Unlike minCount, we do not care to find equivalence, so a
+ // single pred call is enough.
+ if (binaryFun!pred(range.front, result.front))
+ {
+ // change the min
+ result = range.save;
+ }
+ }
+ return result;
+ }
+}
+
+/// Ditto
+Range maxPos(alias pred = "a < b", Range)(Range range)
+if (isForwardRange!Range && !isInfinite!Range &&
+ is(typeof(binaryFun!pred(range.front, range.front))))
+{
+ return range.minPos!((a, b) => binaryFun!pred(b, a));
+}
+
+///
+@safe unittest
+{
+ int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+ // Minimum is 1 and first occurs in position 3
+ assert(a.minPos == [ 1, 2, 4, 1, 1, 2 ]);
+ // Maximum is 4 and first occurs in position 2
+ assert(a.maxPos == [ 4, 1, 2, 4, 1, 1, 2 ]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+
+ int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+ //Test that an empty range works
+ int[] b = a[$..$];
+ assert(equal(minPos(b), b));
+
+ //test with reference range.
+ assert( equal( minPos(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])), [0, 2, 0] ) );
+}
+
+@system unittest
+{
+ //Rvalue range
+ import std.algorithm.comparison : equal;
+ import std.container : Array;
+
+ assert(Array!int(2, 3, 4, 1, 2, 4, 1, 1, 2)
+ []
+ .minPos()
+ .equal([ 1, 2, 4, 1, 1, 2 ]));
+}
+
+@safe unittest
+{
+ //BUG 9299
+ immutable a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+ // Minimum is 1 and first occurs in position 3
+ assert(minPos(a) == [ 1, 2, 4, 1, 1, 2 ]);
+ // Maximum is 4 and first occurs in position 5
+ assert(minPos!("a > b")(a) == [ 4, 1, 2, 4, 1, 1, 2 ]);
+
+ immutable(int[])[] b = [ [4], [2, 4], [4], [4] ];
+ assert(minPos!("a[0] < b[0]")(b) == [ [2, 4], [4], [4] ]);
+}
+
+/**
+Computes the index of the first occurrence of `range`'s minimum element.
+
+Params:
+ pred = The ordering predicate to use to determine the minimum element.
+ range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ to search.
+
+Complexity: O(n)
+ Exactly `n - 1` comparisons are needed.
+
+Returns:
+ The index of the first encounter of the minimum element in `range`. If the
+ `range` is empty, -1 is returned.
+
+See_Also:
+ $(REF min, std,algorithm,comparison), $(LREF minCount), $(LREF minElement), $(LREF minPos)
+ */
+sizediff_t minIndex(alias pred = "a < b", Range)(Range range)
+if (isForwardRange!Range && !isInfinite!Range &&
+ is(typeof(binaryFun!pred(range.front, range.front))))
+{
+ if (range.empty) return -1;
+
+ sizediff_t minPos = 0;
+
+ static if (isRandomAccessRange!Range && hasLength!Range)
+ {
+ foreach (i; 1 .. range.length)
+ {
+ if (binaryFun!pred(range[i], range[minPos]))
+ {
+ minPos = i;
+ }
+ }
+ }
+ else
+ {
+ sizediff_t curPos = 0;
+ Unqual!(typeof(range.front)) min = range.front;
+ for (range.popFront(); !range.empty; range.popFront())
+ {
+ ++curPos;
+ if (binaryFun!pred(range.front, min))
+ {
+ min = range.front;
+ minPos = curPos;
+ }
+ }
+ }
+ return minPos;
+}
+
+///
+@safe pure nothrow unittest
+{
+ int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2];
+
+ // Minimum is 1 and first occurs in position 3
+ assert(a.minIndex == 3);
+ // Get maximum index with minIndex
+ assert(a.minIndex!"a > b" == 2);
+
+ // Range is empty, so return value is -1
+ int[] b;
+ assert(b.minIndex == -1);
+
+ // Works with more custom types
+ struct Dog { int age; }
+ Dog[] dogs = [Dog(10), Dog(5), Dog(15)];
+ assert(dogs.minIndex!"a.age < b.age" == 1);
+}
+
+@safe pure unittest
+{
+ // should work with const
+ const(int)[] immArr = [2, 1, 3];
+ assert(immArr.minIndex == 1);
+
+ // Works for const ranges too
+ const int[] c = [2, 5, 4, 1, 2, 3];
+ assert(c.minIndex == 3);
+
+ // should work with immutable
+ immutable(int)[] immArr2 = [2, 1, 3];
+ assert(immArr2.minIndex == 1);
+
+ // with strings
+ assert(["b", "a", "c"].minIndex == 1);
+
+ // infinite range
+ import std.range : cycle;
+ static assert(!__traits(compiles, cycle([1]).minIndex));
+
+ // with all dummy ranges
+ import std.internal.test.dummyrange : AllDummyRanges;
+ foreach (DummyType; AllDummyRanges)
+ {
+ static if (isForwardRange!DummyType && !isInfinite!DummyType)
+ {
+ DummyType d;
+ d.arr = [5, 3, 7, 2, 1, 4];
+ assert(d.minIndex == 4);
+
+ d.arr = [];
+ assert(d.minIndex == -1);
+ }
+ }
+}
+
+@nogc @safe nothrow pure unittest
+{
+ static immutable arr = [7, 3, 8, 2, 1, 4];
+ assert(arr.minIndex == 4);
+
+ static immutable arr2d = [[1, 3], [3, 9], [4, 2]];
+ assert(arr2d.minIndex!"a[1] < b[1]" == 2);
+}
+
+/**
+Computes the index of the first occurrence of `range`'s maximum element.
+
+Complexity: O(n)
+ Exactly `n - 1` comparisons are needed.
+
+Params:
+ pred = The ordering predicate to use to determine the maximum element.
+ range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to search.
+
+Returns:
+ The index of the first encounter of the maximum in `range`. If the
+ `range` is empty, -1 is returned.
+
+See_Also:
+ $(REF max, std,algorithm,comparison), $(LREF maxCount), $(LREF maxElement), $(LREF maxPos)
+ */
+sizediff_t maxIndex(alias pred = "a < b", Range)(Range range)
+if (isInputRange!Range && !isInfinite!Range &&
+ is(typeof(binaryFun!pred(range.front, range.front))))
+{
+ return range.minIndex!((a, b) => binaryFun!pred(b, a));
+}
+
+///
+@safe pure nothrow unittest
+{
+ // Maximum is 4 and first occurs in position 2
+ int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2];
+ assert(a.maxIndex == 2);
+
+ // Empty range
+ int[] b;
+ assert(b.maxIndex == -1);
+
+ // Works with more custom types
+ struct Dog { int age; }
+ Dog[] dogs = [Dog(10), Dog(15), Dog(5)];
+ assert(dogs.maxIndex!"a.age < b.age" == 1);
+}
+
+@safe pure unittest
+{
+ // should work with const
+ const(int)[] immArr = [5, 1, 3];
+ assert(immArr.maxIndex == 0);
+
+ // Works for const ranges too
+ const int[] c = [2, 5, 4, 1, 2, 3];
+ assert(c.maxIndex == 1);
+
+
+ // should work with immutable
+ immutable(int)[] immArr2 = [2, 1, 3];
+ assert(immArr2.maxIndex == 2);
+
+ // with strings
+ assert(["b", "a", "c"].maxIndex == 2);
+
+ // infinite range
+ import std.range : cycle;
+ static assert(!__traits(compiles, cycle([1]).maxIndex));
+
+ // with all dummy ranges
+ import std.internal.test.dummyrange : AllDummyRanges;
+ foreach (DummyType; AllDummyRanges)
+ {
+ static if (isForwardRange!DummyType && !isInfinite!DummyType)
+ {
+ DummyType d;
+
+ d.arr = [5, 3, 7, 2, 1, 4];
+ assert(d.maxIndex == 2);
+
+ d.arr = [];
+ assert(d.maxIndex == -1);
+ }
+ }
+}
+
+@nogc @safe nothrow pure unittest
+{
+ static immutable arr = [7, 3, 8, 2, 1, 4];
+ assert(arr.maxIndex == 2);
+
+ static immutable arr2d = [[1, 3], [3, 9], [4, 2]];
+ assert(arr2d.maxIndex!"a[1] < b[1]" == 1);
+}
+
+/**
+Skip over the initial portion of the first given range that matches the second
+range, or if no second range is given skip over the elements that fullfil pred.
+Do nothing if there is no match.
+
+Params:
+ pred = The predicate that determines whether elements from each respective
+ range match. Defaults to equality $(D "a == b").
+ r1 = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to
+ move forward.
+ r2 = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ representing the initial segment of $(D r1) to skip over.
+
+Returns:
+true if the initial segment of $(D r1) matches $(D r2) or $(D pred) evaluates to true,
+and $(D r1) has been advanced to the point past this segment; otherwise false, and
+$(D r1) is left in its original position.
+ */
+bool skipOver(R1, R2)(ref R1 r1, R2 r2)
+if (isForwardRange!R1 && isInputRange!R2
+ && is(typeof(r1.front == r2.front)))
+{
+ static if (is(typeof(r1[0 .. $] == r2) : bool)
+ && is(typeof(r2.length > r1.length) : bool)
+ && is(typeof(r1 = r1[r2.length .. $])))
+ {
+ if (r2.length > r1.length || r1[0 .. r2.length] != r2)
+ {
+ return false;
+ }
+ r1 = r1[r2.length .. $];
+ return true;
+ }
+ else
+ {
+ return skipOver!((a, b) => a == b)(r1, r2);
+ }
+}
+
+/// Ditto
+bool skipOver(alias pred, R1, R2)(ref R1 r1, R2 r2)
+if (is(typeof(binaryFun!pred(r1.front, r2.front))) &&
+ isForwardRange!R1 &&
+ isInputRange!R2)
+{
+ static if (hasLength!R1 && hasLength!R2)
+ {
+ // Shortcut opportunity!
+ if (r2.length > r1.length)
+ return false;
+ }
+ auto r = r1.save;
+ while (!r2.empty && !r.empty && binaryFun!pred(r.front, r2.front))
+ {
+ r.popFront();
+ r2.popFront();
+ }
+ if (r2.empty)
+ r1 = r;
+ return r2.empty;
+}
+
+/// Ditto
+bool skipOver(alias pred, R)(ref R r1)
+if (isForwardRange!R &&
+ ifTestable!(typeof(r1.front), unaryFun!pred))
+{
+ if (r1.empty || !unaryFun!pred(r1.front))
+ return false;
+
+ do
+ r1.popFront();
+ while (!r1.empty && unaryFun!pred(r1.front));
+ return true;
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto s1 = "Hello world";
+ assert(!skipOver(s1, "Ha"));
+ assert(s1 == "Hello world");
+ assert(skipOver(s1, "Hell") && s1 == "o world");
+
+ string[] r1 = ["abc", "def", "hij"];
+ dstring[] r2 = ["abc"d];
+ assert(!skipOver!((a, b) => a.equal(b))(r1, ["def"d]));
+ assert(r1 == ["abc", "def", "hij"]);
+ assert(skipOver!((a, b) => a.equal(b))(r1, r2));
+ assert(r1 == ["def", "hij"]);
+
+ import std.ascii : isWhite;
+ import std.range.primitives : empty;
+
+ auto s2 = "\t\tvalue";
+ auto s3 = "";
+ auto s4 = "\t\t\t";
+ assert(s2.skipOver!isWhite && s2 == "value");
+ assert(!s3.skipOver!isWhite);
+ assert(s4.skipOver!isWhite && s3.empty);
+}
+
+/**
+Skip over the first element of the given range if it matches the given element,
+otherwise do nothing.
+
+Params:
+ pred = The predicate that determines whether an element from the range
+ matches the given element.
+
+ r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to skip
+ over.
+
+ e = The element to match.
+
+Returns:
+true if the first element matches the given element according to the given
+predicate, and the range has been advanced by one element; otherwise false, and
+the range is left untouched.
+ */
+bool skipOver(R, E)(ref R r, E e)
+if (isInputRange!R && is(typeof(r.front == e) : bool))
+{
+ return skipOver!((a, b) => a == b)(r, e);
+}
+
+/// Ditto
+bool skipOver(alias pred, R, E)(ref R r, E e)
+if (is(typeof(binaryFun!pred(r.front, e))) && isInputRange!R)
+{
+ if (r.empty || !binaryFun!pred(r.front, e))
+ return false;
+ r.popFront();
+ return true;
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto s1 = "Hello world";
+ assert(!skipOver(s1, 'a'));
+ assert(s1 == "Hello world");
+ assert(skipOver(s1, 'H') && s1 == "ello world");
+
+ string[] r = ["abc", "def", "hij"];
+ dstring e = "abc"d;
+ assert(!skipOver!((a, b) => a.equal(b))(r, "def"d));
+ assert(r == ["abc", "def", "hij"]);
+ assert(skipOver!((a, b) => a.equal(b))(r, e));
+ assert(r == ["def", "hij"]);
+
+ auto s2 = "";
+ assert(!s2.skipOver('a'));
+}
+
+/**
+Checks whether the given
+$(REF_ALTTEXT input range, isInputRange, std,range,primitives) starts with (one
+of) the given needle(s) or, if no needles are given,
+if its front element fulfils predicate $(D pred).
+
+Params:
+
+ pred = Predicate to use in comparing the elements of the haystack and the
+ needle(s). Mandatory if no needles are given.
+
+ doesThisStart = The input range to check.
+
+ withOneOfThese = The needles against which the range is to be checked,
+ which may be individual elements or input ranges of elements.
+
+ withThis = The single needle to check, which may be either a single element
+ or an input range of elements.
+
+Returns:
+
+0 if the needle(s) do not occur at the beginning of the given range;
+otherwise the position of the matching needle, that is, 1 if the range starts
+with $(D withOneOfThese[0]), 2 if it starts with $(D withOneOfThese[1]), and so
+on.
+
+In the case where $(D doesThisStart) starts with multiple of the ranges or
+elements in $(D withOneOfThese), then the shortest one matches (if there are
+two which match which are of the same length (e.g. $(D "a") and $(D 'a')), then
+the left-most of them in the argument
+list matches).
+
+In the case when no needle parameters are given, return $(D true) iff front of
+$(D doesThisStart) fulfils predicate $(D pred).
+ */
+uint startsWith(alias pred = "a == b", Range, Needles...)(Range doesThisStart, Needles withOneOfThese)
+if (isInputRange!Range && Needles.length > 1 &&
+ is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[0])) : bool ) &&
+ is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[1 .. $])) : uint))
+{
+ alias haystack = doesThisStart;
+ alias needles = withOneOfThese;
+
+ // Make one pass looking for empty ranges in needles
+ foreach (i, Unused; Needles)
+ {
+ // Empty range matches everything
+ static if (!is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool))
+ {
+ if (needles[i].empty) return i + 1;
+ }
+ }
+
+ for (; !haystack.empty; haystack.popFront())
+ {
+ foreach (i, Unused; Needles)
+ {
+ static if (is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool))
+ {
+ // Single-element
+ if (binaryFun!pred(haystack.front, needles[i]))
+ {
+ // found, but instead of returning, we just stop searching.
+ // This is to account for one-element
+ // range matches (consider startsWith("ab", "a",
+ // 'a') should return 1, not 2).
+ break;
+ }
+ }
+ else
+ {
+ if (binaryFun!pred(haystack.front, needles[i].front))
+ {
+ continue;
+ }
+ }
+
+ // This code executed on failure to match
+ // Out with this guy, check for the others
+ uint result = startsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]);
+ if (result > i) ++result;
+ return result;
+ }
+
+ // If execution reaches this point, then the front matches for all
+ // needle ranges, or a needle element has been matched.
+ // What we need to do now is iterate, lopping off the front of
+ // the range and checking if the result is empty, or finding an
+ // element needle and returning.
+ // If neither happens, we drop to the end and loop.
+ foreach (i, Unused; Needles)
+ {
+ static if (is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool))
+ {
+ // Test has passed in the previous loop
+ return i + 1;
+ }
+ else
+ {
+ needles[i].popFront();
+ if (needles[i].empty) return i + 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/// Ditto
+bool startsWith(alias pred = "a == b", R1, R2)(R1 doesThisStart, R2 withThis)
+if (isInputRange!R1 &&
+ isInputRange!R2 &&
+ is(typeof(binaryFun!pred(doesThisStart.front, withThis.front)) : bool))
+{
+ alias haystack = doesThisStart;
+ alias needle = withThis;
+
+ static if (is(typeof(pred) : string))
+ enum isDefaultPred = pred == "a == b";
+ else
+ enum isDefaultPred = false;
+
+ //Note: While narrow strings don't have a "true" length, for a narrow string to start with another
+ //narrow string *of the same type*, it must have *at least* as many code units.
+ static if ((hasLength!R1 && hasLength!R2) ||
+ (isNarrowString!R1 && isNarrowString!R2 && ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof))
+ {
+ if (haystack.length < needle.length)
+ return false;
+ }
+
+ static if (isDefaultPred && isArray!R1 && isArray!R2 &&
+ is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
+ {
+ //Array slice comparison mode
+ return haystack[0 .. needle.length] == needle;
+ }
+ else static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && hasLength!R2)
+ {
+ //RA dual indexing mode
+ foreach (j; 0 .. needle.length)
+ {
+ if (!binaryFun!pred(haystack[j], needle[j]))
+ // not found
+ return false;
+ }
+ // found!
+ return true;
+ }
+ else
+ {
+ //Standard input range mode
+ if (needle.empty) return true;
+ static if (hasLength!R1 && hasLength!R2)
+ {
+ //We have previously checked that haystack.length > needle.length,
+ //So no need to check haystack.empty during iteration
+ for ( ; ; haystack.popFront() )
+ {
+ if (!binaryFun!pred(haystack.front, needle.front)) break;
+ needle.popFront();
+ if (needle.empty) return true;
+ }
+ }
+ else
+ {
+ for ( ; !haystack.empty ; haystack.popFront() )
+ {
+ if (!binaryFun!pred(haystack.front, needle.front)) break;
+ needle.popFront();
+ if (needle.empty) return true;
+ }
+ }
+ return false;
+ }
+}
+
+/// Ditto
+bool startsWith(alias pred = "a == b", R, E)(R doesThisStart, E withThis)
+if (isInputRange!R &&
+ is(typeof(binaryFun!pred(doesThisStart.front, withThis)) : bool))
+{
+ if (doesThisStart.empty)
+ return false;
+
+ alias predFunc = binaryFun!pred;
+
+ // auto-decoding special case
+ static if (isNarrowString!R)
+ {
+ // specialize for ASCII as to not change previous behavior
+ if (withThis <= 0x7F)
+ return predFunc(doesThisStart[0], withThis);
+ else
+ return predFunc(doesThisStart.front, withThis);
+ }
+ else
+ {
+ return predFunc(doesThisStart.front, withThis);
+ }
+}
+
+/// Ditto
+bool startsWith(alias pred, R)(R doesThisStart)
+if (isInputRange!R &&
+ ifTestable!(typeof(doesThisStart.front), unaryFun!pred))
+{
+ return !doesThisStart.empty && unaryFun!pred(doesThisStart.front);
+}
+
+///
+@safe unittest
+{
+ import std.ascii : isAlpha;
+
+ assert("abc".startsWith!(a => a.isAlpha));
+ assert("abc".startsWith!isAlpha);
+ assert(!"1ab".startsWith!(a => a.isAlpha));
+ assert(!"".startsWith!(a => a.isAlpha));
+
+ import std.algorithm.comparison : among;
+ assert("abc".startsWith!(a => a.among('a', 'b') != 0));
+ assert(!"abc".startsWith!(a => a.among('b', 'c') != 0));
+
+ assert(startsWith("abc", ""));
+ assert(startsWith("abc", "a"));
+ assert(!startsWith("abc", "b"));
+ assert(startsWith("abc", 'a', "b") == 1);
+ assert(startsWith("abc", "b", "a") == 2);
+ assert(startsWith("abc", "a", "a") == 1);
+ assert(startsWith("abc", "ab", "a") == 2);
+ assert(startsWith("abc", "x", "a", "b") == 2);
+ assert(startsWith("abc", "x", "aa", "ab") == 3);
+ assert(startsWith("abc", "x", "aaa", "sab") == 0);
+ assert(startsWith("abc", "x", "aaa", "a", "sab") == 3);
+
+ import std.typecons : Tuple;
+ alias C = Tuple!(int, "x", int, "y");
+ assert(startsWith!"a.x == b"([ C(1,1), C(1,2), C(2,2) ], [1, 1]));
+ assert(startsWith!"a.x == b"([ C(1,1), C(2,1), C(2,2) ], [1, 1], [1, 2], [1, 3]) == 2);
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ import std.range;
+
+ foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
+ assert(!startsWith(to!S("abc"), 'c'));
+ assert(startsWith(to!S("abc"), 'a', 'c') == 1);
+ assert(!startsWith(to!S("abc"), 'x', 'n', 'b'));
+ assert(startsWith(to!S("abc"), 'x', 'n', 'a') == 3);
+ assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2);
+
+ foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ //Lots of strings
+ assert(startsWith(to!S("abc"), to!T("")));
+ assert(startsWith(to!S("ab"), to!T("a")));
+ assert(startsWith(to!S("abc"), to!T("a")));
+ assert(!startsWith(to!S("abc"), to!T("b")));
+ assert(!startsWith(to!S("abc"), to!T("b"), "bc", "abcd", "xyz"));
+ assert(startsWith(to!S("abc"), to!T("ab"), 'a') == 2);
+ assert(startsWith(to!S("abc"), to!T("a"), "b") == 1);
+ assert(startsWith(to!S("abc"), to!T("b"), "a") == 2);
+ assert(startsWith(to!S("abc"), to!T("a"), 'a') == 1);
+ assert(startsWith(to!S("abc"), 'a', to!T("a")) == 1);
+ assert(startsWith(to!S("abc"), to!T("x"), "a", "b") == 2);
+ assert(startsWith(to!S("abc"), to!T("x"), "aa", "ab") == 3);
+ assert(startsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0);
+ assert(startsWith(to!S("abc"), 'a'));
+ assert(!startsWith(to!S("abc"), to!T("sab")));
+ assert(startsWith(to!S("abc"), 'x', to!T("aaa"), 'a', "sab") == 3);
+
+ //Unicode
+ assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("\uFF28el")));
+ assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("Hel"), to!T("\uFF28el")) == 2);
+ assert(startsWith(to!S("日本語"), to!T("日本")));
+ assert(startsWith(to!S("日本語"), to!T("日本語")));
+ assert(!startsWith(to!S("日本"), to!T("日本語")));
+
+ //Empty
+ assert(startsWith(to!S(""), T.init));
+ assert(!startsWith(to!S(""), 'a'));
+ assert(startsWith(to!S("a"), T.init));
+ assert(startsWith(to!S("a"), T.init, "") == 1);
+ assert(startsWith(to!S("a"), T.init, 'a') == 1);
+ assert(startsWith(to!S("a"), 'a', T.init) == 2);
+ }();
+ }
+
+ //Length but no RA
+ assert(!startsWith("abc".takeExactly(3), "abcd".takeExactly(4)));
+ assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(3)));
+ assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(1)));
+
+ foreach (T; AliasSeq!(int, short))
+ {
+ immutable arr = cast(T[])[0, 1, 2, 3, 4, 5];
+
+ //RA range
+ assert(startsWith(arr, cast(int[]) null));
+ assert(!startsWith(arr, 5));
+ assert(!startsWith(arr, 1));
+ assert(startsWith(arr, 0));
+ assert(startsWith(arr, 5, 0, 1) == 2);
+ assert(startsWith(arr, [0]));
+ assert(startsWith(arr, [0, 1]));
+ assert(startsWith(arr, [0, 1], 7) == 1);
+ assert(!startsWith(arr, [0, 1, 7]));
+ assert(startsWith(arr, [0, 1, 7], [0, 1, 2]) == 2);
+
+ //Normal input range
+ assert(!startsWith(filter!"true"(arr), 1));
+ assert(startsWith(filter!"true"(arr), 0));
+ assert(startsWith(filter!"true"(arr), [0]));
+ assert(startsWith(filter!"true"(arr), [0, 1]));
+ assert(startsWith(filter!"true"(arr), [0, 1], 7) == 1);
+ assert(!startsWith(filter!"true"(arr), [0, 1, 7]));
+ assert(startsWith(filter!"true"(arr), [0, 1, 7], [0, 1, 2]) == 2);
+ assert(startsWith(arr, filter!"true"([0, 1])));
+ assert(startsWith(arr, filter!"true"([0, 1]), 7) == 1);
+ assert(!startsWith(arr, filter!"true"([0, 1, 7])));
+ assert(startsWith(arr, [0, 1, 7], filter!"true"([0, 1, 2])) == 2);
+
+ //Non-default pred
+ assert(startsWith!("a%10 == b%10")(arr, [10, 11]));
+ assert(!startsWith!("a%10 == b%10")(arr, [10, 12]));
+ }
+}
+
+/* (Not yet documented.)
+Consume all elements from $(D r) that are equal to one of the elements
+$(D es).
+ */
+private void skipAll(alias pred = "a == b", R, Es...)(ref R r, Es es)
+//if (is(typeof(binaryFun!pred(r1.front, es[0]))))
+{
+ loop:
+ for (; !r.empty; r.popFront())
+ {
+ foreach (i, E; Es)
+ {
+ if (binaryFun!pred(r.front, es[i]))
+ {
+ continue loop;
+ }
+ }
+ break;
+ }
+}
+
+@safe unittest
+{
+ auto s1 = "Hello world";
+ skipAll(s1, 'H', 'e');
+ assert(s1 == "llo world");
+}
+
+/**
+Interval option specifier for `until` (below) and others.
+
+If set to $(D OpenRight.yes), then the interval is open to the right
+(last element is not included).
+
+Otherwise if set to $(D OpenRight.no), then the interval is closed to the right
+(last element included).
+ */
+alias OpenRight = Flag!"openRight";
+
+/**
+Lazily iterates $(D range) _until the element $(D e) for which
+$(D pred(e, sentinel)) is true.
+
+This is similar to `takeWhile` in other languages.
+
+Params:
+ pred = Predicate to determine when to stop.
+ range = The $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives)
+ to iterate over.
+ sentinel = The element to stop at.
+ openRight = Determines whether the element for which the given predicate is
+ true should be included in the resulting range ($(D No.openRight)), or
+ not ($(D Yes.openRight)).
+
+Returns:
+ An $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) that
+ iterates over the original range's elements, but ends when the specified
+ predicate becomes true. If the original range is a
+ $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives) or
+ higher, this range will be a forward range.
+ */
+Until!(pred, Range, Sentinel)
+until(alias pred = "a == b", Range, Sentinel)
+(Range range, Sentinel sentinel, OpenRight openRight = Yes.openRight)
+if (!is(Sentinel == OpenRight))
+{
+ return typeof(return)(range, sentinel, openRight);
+}
+
+/// Ditto
+Until!(pred, Range, void)
+until(alias pred, Range)
+(Range range, OpenRight openRight = Yes.openRight)
+{
+ return typeof(return)(range, openRight);
+}
+
+/// ditto
+struct Until(alias pred, Range, Sentinel)
+if (isInputRange!Range)
+{
+ private Range _input;
+ static if (!is(Sentinel == void))
+ private Sentinel _sentinel;
+ private OpenRight _openRight;
+ private bool _done;
+
+ static if (!is(Sentinel == void))
+ ///
+ this(Range input, Sentinel sentinel,
+ OpenRight openRight = Yes.openRight)
+ {
+ _input = input;
+ _sentinel = sentinel;
+ _openRight = openRight;
+ _done = _input.empty || openRight && predSatisfied();
+ }
+ else
+ ///
+ this(Range input, OpenRight openRight = Yes.openRight)
+ {
+ _input = input;
+ _openRight = openRight;
+ _done = _input.empty || openRight && predSatisfied();
+ }
+
+ ///
+ @property bool empty()
+ {
+ return _done;
+ }
+
+ ///
+ @property auto ref front()
+ {
+ assert(!empty);
+ return _input.front;
+ }
+
+ private bool predSatisfied()
+ {
+ static if (is(Sentinel == void))
+ return cast(bool) unaryFun!pred(_input.front);
+ else
+ return cast(bool) startsWith!pred(_input, _sentinel);
+ }
+
+ ///
+ void popFront()
+ {
+ assert(!empty);
+ if (!_openRight)
+ {
+ _done = predSatisfied();
+ _input.popFront();
+ _done = _done || _input.empty;
+ }
+ else
+ {
+ _input.popFront();
+ _done = _input.empty || predSatisfied();
+ }
+ }
+
+ static if (isForwardRange!Range)
+ {
+ static if (!is(Sentinel == void))
+ ///
+ @property Until save()
+ {
+ Until result = this;
+ result._input = _input.save;
+ result._sentinel = _sentinel;
+ result._openRight = _openRight;
+ result._done = _done;
+ return result;
+ }
+ else
+ ///
+ @property Until save()
+ {
+ Until result = this;
+ result._input = _input.save;
+ result._openRight = _openRight;
+ result._done = _done;
+ return result;
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : No;
+ int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];
+ assert(equal(a.until(7), [1, 2, 4]));
+ assert(equal(a.until(7, No.openRight), [1, 2, 4, 7]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];
+
+ static assert(isForwardRange!(typeof(a.until(7))));
+ static assert(isForwardRange!(typeof(until!"a == 2"(a, No.openRight))));
+
+ assert(equal(a.until(7), [1, 2, 4]));
+ assert(equal(a.until([7, 2]), [1, 2, 4, 7]));
+ assert(equal(a.until(7, No.openRight), [1, 2, 4, 7]));
+ assert(equal(until!"a == 2"(a, No.openRight), [1, 2]));
+}
+
+@system unittest // bugzilla 13171
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ auto a = [1, 2, 3, 4];
+ assert(equal(refRange(&a).until(3, No.openRight), [1, 2, 3]));
+ assert(a == [4]);
+}
+
+@safe unittest // Issue 10460
+{
+ import std.algorithm.comparison : equal;
+ auto a = [1, 2, 3, 4];
+ foreach (ref e; a.until(3))
+ e = 0;
+ assert(equal(a, [0, 0, 3, 4]));
+}
+
+@safe unittest // Issue 13124
+{
+ import std.algorithm.comparison : among, equal;
+ auto s = "hello how\nare you";
+ assert(equal(s.until!(c => c.among!('\n', '\r')), "hello how"));
+}
diff --git a/libphobos/src/std/algorithm/setops.d b/libphobos/src/std/algorithm/setops.d
new file mode 100644
index 0000000..05a6e7e
--- /dev/null
+++ b/libphobos/src/std/algorithm/setops.d
@@ -0,0 +1,1521 @@
+// Written in the D programming language.
+/**
+This is a submodule of $(MREF std, algorithm).
+It contains generic algorithms that implement set operations.
+
+The functions $(LREF multiwayMerge), $(LREF multiwayUnion), $(LREF setDifference),
+$(LREF setIntersection), $(LREF setSymmetricDifference) expect a range of sorted
+ranges as input.
+
+All algorithms are generalized to accept as input not only sets but also
+$(HTTP https://en.wikipedia.org/wiki/Multiset, multisets). Each algorithm
+documents behaviour in the presence of duplicated inputs.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE Cheat Sheet,
+$(TR $(TH Function Name) $(TH Description))
+$(T2 cartesianProduct,
+ Computes Cartesian product of two ranges.)
+$(T2 largestPartialIntersection,
+ Copies out the values that occur most frequently in a range of ranges.)
+$(T2 largestPartialIntersectionWeighted,
+ Copies out the values that occur most frequently (multiplied by
+ per-value weights) in a range of ranges.)
+$(T2 multiwayMerge,
+ Merges a range of sorted ranges.)
+$(T2 multiwayUnion,
+ Computes the union of a range of sorted ranges.)
+$(T2 setDifference,
+ Lazily computes the set difference of two or more sorted ranges.)
+$(T2 setIntersection,
+ Lazily computes the intersection of two or more sorted ranges.)
+$(T2 setSymmetricDifference,
+ Lazily computes the symmetric set difference of two or more sorted
+ ranges.)
+)
+
+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/_setops.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.setops;
+
+import std.range.primitives;
+
+// FIXME
+import std.functional; // : unaryFun, binaryFun;
+import std.traits;
+// FIXME
+import std.meta; // : AliasSeq, staticMap, allSatisfy, anySatisfy;
+
+import std.algorithm.sorting; // : Merge;
+import std.typecons : No;
+
+// cartesianProduct
+/**
+Lazily computes the Cartesian product of two or more ranges. The product is a
+_range of tuples of elements from each respective range.
+
+The conditions for the two-range case are as follows:
+
+If both ranges are finite, then one must be (at least) a
+$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and the
+other an $(REF_ALTTEXT input range, isInputRange, std,range,primitives).
+
+If one _range is infinite and the other finite, then the finite _range must
+be a forward _range, and the infinite range can be an input _range.
+
+If both ranges are infinite, then both must be forward ranges.
+
+When there are more than two ranges, the above conditions apply to each
+adjacent pair of ranges.
+
+Params:
+ range1 = The first range
+ range2 = The second range
+ ranges = Two or more non-infinite forward ranges
+ otherRanges = Zero or more non-infinite forward ranges
+
+Returns:
+ A forward range of $(REF Tuple, std,typecons) representing elements of the
+ cartesian product of the given ranges.
+*/
+auto cartesianProduct(R1, R2)(R1 range1, R2 range2)
+if (!allSatisfy!(isForwardRange, R1, R2) ||
+ anySatisfy!(isInfinite, R1, R2))
+{
+ import std.algorithm.iteration : map, joiner;
+
+ static if (isInfinite!R1 && isInfinite!R2)
+ {
+ static if (isForwardRange!R1 && isForwardRange!R2)
+ {
+ import std.range : zip, repeat, take, chain, sequence;
+
+ // This algorithm traverses the cartesian product by alternately
+ // covering the right and bottom edges of an increasing square area
+ // over the infinite table of combinations. This schedule allows us
+ // to require only forward ranges.
+ return zip(sequence!"n"(cast(size_t) 0), range1.save, range2.save,
+ repeat(range1), repeat(range2))
+ .map!(function(a) => chain(
+ zip(repeat(a[1]), take(a[4].save, a[0])),
+ zip(take(a[3].save, a[0]+1), repeat(a[2]))
+ ))()
+ .joiner();
+ }
+ else static assert(0, "cartesianProduct of infinite ranges requires "~
+ "forward ranges");
+ }
+ else static if (isInputRange!R1 && isForwardRange!R2 && !isInfinite!R2)
+ {
+ import std.range : zip, repeat;
+ return joiner(map!((ElementType!R1 a) => zip(repeat(a), range2.save))
+ (range1));
+ }
+ else static if (isInputRange!R2 && isForwardRange!R1 && !isInfinite!R1)
+ {
+ import std.range : zip, repeat;
+ return joiner(map!((ElementType!R2 a) => zip(range1.save, repeat(a)))
+ (range2));
+ }
+ else static assert(0, "cartesianProduct involving finite ranges must "~
+ "have at least one finite forward range");
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple;
+
+ auto N = sequence!"n"(0); // the range of natural numbers
+ auto N2 = cartesianProduct(N, N); // the range of all pairs of natural numbers
+
+ // Various arbitrary number pairs can be found in the range in finite time.
+ assert(canFind(N2, tuple(0, 0)));
+ assert(canFind(N2, tuple(123, 321)));
+ assert(canFind(N2, tuple(11, 35)));
+ assert(canFind(N2, tuple(279, 172)));
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.typecons : tuple;
+
+ auto B = [ 1, 2, 3 ];
+ auto C = [ 4, 5, 6 ];
+ auto BC = cartesianProduct(B, C);
+
+ foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6],
+ [2, 6], [3, 6]])
+ {
+ assert(canFind(BC, tuple(n[0], n[1])));
+ }
+}
+
+@safe unittest
+{
+ // Test cartesian product of two infinite ranges
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple;
+
+ auto Even = sequence!"2*n"(0);
+ auto Odd = sequence!"2*n+1"(0);
+ auto EvenOdd = cartesianProduct(Even, Odd);
+
+ foreach (pair; [[0, 1], [2, 1], [0, 3], [2, 3], [4, 1], [4, 3], [0, 5],
+ [2, 5], [4, 5], [6, 1], [6, 3], [6, 5]])
+ {
+ assert(canFind(EvenOdd, tuple(pair[0], pair[1])));
+ }
+
+ // This should terminate in finite time
+ assert(canFind(EvenOdd, tuple(124, 73)));
+ assert(canFind(EvenOdd, tuple(0, 97)));
+ assert(canFind(EvenOdd, tuple(42, 1)));
+}
+
+@safe unittest
+{
+ // Test cartesian product of an infinite input range and a finite forward
+ // range.
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple;
+
+ auto N = sequence!"n"(0);
+ auto M = [100, 200, 300];
+ auto NM = cartesianProduct(N,M);
+
+ foreach (pair; [[0, 100], [0, 200], [0, 300], [1, 100], [1, 200], [1, 300],
+ [2, 100], [2, 200], [2, 300], [3, 100], [3, 200],
+ [3, 300]])
+ {
+ assert(canFind(NM, tuple(pair[0], pair[1])));
+ }
+
+ // We can't solve the halting problem, so we can only check a finite
+ // initial segment here.
+ assert(!canFind(NM.take(100), tuple(100, 0)));
+ assert(!canFind(NM.take(100), tuple(1, 1)));
+ assert(!canFind(NM.take(100), tuple(100, 200)));
+
+ auto MN = cartesianProduct(M,N);
+ foreach (pair; [[100, 0], [200, 0], [300, 0], [100, 1], [200, 1], [300, 1],
+ [100, 2], [200, 2], [300, 2], [100, 3], [200, 3],
+ [300, 3]])
+ {
+ assert(canFind(MN, tuple(pair[0], pair[1])));
+ }
+
+ // We can't solve the halting problem, so we can only check a finite
+ // initial segment here.
+ assert(!canFind(MN.take(100), tuple(0, 100)));
+ assert(!canFind(MN.take(100), tuple(0, 1)));
+ assert(!canFind(MN.take(100), tuple(100, 200)));
+}
+
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.typecons : tuple;
+
+ // Test cartesian product of two finite ranges.
+ auto X = [1, 2, 3];
+ auto Y = [4, 5, 6];
+ auto XY = cartesianProduct(X, Y);
+ auto Expected = [[1, 4], [1, 5], [1, 6], [2, 4], [2, 5], [2, 6], [3, 4],
+ [3, 5], [3, 6]];
+
+ // Verify Expected ⊆ XY
+ foreach (pair; Expected)
+ {
+ assert(canFind(XY, tuple(pair[0], pair[1])));
+ }
+
+ // Verify XY ⊆ Expected
+ foreach (pair; XY)
+ {
+ assert(canFind(Expected, [pair[0], pair[1]]));
+ }
+
+ // And therefore, by set comprehension, XY == Expected
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ import std.algorithm.searching : canFind;
+ import std.typecons : tuple;
+
+ import std.range;
+ auto N = sequence!"n"(0);
+
+ // To force the template to fall to the second case, we wrap N in a struct
+ // that doesn't allow bidirectional access.
+ struct FwdRangeWrapper(R)
+ {
+ R impl;
+
+ // Input range API
+ @property auto front() { return impl.front; }
+ void popFront() { impl.popFront(); }
+ static if (isInfinite!R)
+ enum empty = false;
+ else
+ @property bool empty() { return impl.empty; }
+
+ // Forward range API
+ @property auto save() { return typeof(this)(impl.save); }
+ }
+ auto fwdWrap(R)(R range) { return FwdRangeWrapper!R(range); }
+
+ // General test: two infinite bidirectional ranges
+ auto N2 = cartesianProduct(N, N);
+
+ assert(canFind(N2, tuple(0, 0)));
+ assert(canFind(N2, tuple(123, 321)));
+ assert(canFind(N2, tuple(11, 35)));
+ assert(canFind(N2, tuple(279, 172)));
+
+ // Test first case: forward range with bidirectional range
+ auto fwdN = fwdWrap(N);
+ auto N2_a = cartesianProduct(fwdN, N);
+
+ assert(canFind(N2_a, tuple(0, 0)));
+ assert(canFind(N2_a, tuple(123, 321)));
+ assert(canFind(N2_a, tuple(11, 35)));
+ assert(canFind(N2_a, tuple(279, 172)));
+
+ // Test second case: bidirectional range with forward range
+ auto N2_b = cartesianProduct(N, fwdN);
+
+ assert(canFind(N2_b, tuple(0, 0)));
+ assert(canFind(N2_b, tuple(123, 321)));
+ assert(canFind(N2_b, tuple(11, 35)));
+ assert(canFind(N2_b, tuple(279, 172)));
+
+ // Test third case: finite forward range with (infinite) input range
+ static struct InpRangeWrapper(R)
+ {
+ R impl;
+
+ // Input range API
+ @property auto front() { return impl.front; }
+ void popFront() { impl.popFront(); }
+ static if (isInfinite!R)
+ enum empty = false;
+ else
+ @property bool empty() { return impl.empty; }
+ }
+ auto inpWrap(R)(R r) { return InpRangeWrapper!R(r); }
+
+ auto inpN = inpWrap(N);
+ auto B = [ 1, 2, 3 ];
+ auto fwdB = fwdWrap(B);
+ auto BN = cartesianProduct(fwdB, inpN);
+
+ assert(equal(map!"[a[0],a[1]]"(BN.take(10)), [[1, 0], [2, 0], [3, 0],
+ [1, 1], [2, 1], [3, 1], [1, 2], [2, 2], [3, 2], [1, 3]]));
+
+ // Test fourth case: (infinite) input range with finite forward range
+ auto NB = cartesianProduct(inpN, fwdB);
+
+ assert(equal(map!"[a[0],a[1]]"(NB.take(10)), [[0, 1], [0, 2], [0, 3],
+ [1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1]]));
+
+ // General finite range case
+ auto C = [ 4, 5, 6 ];
+ auto BC = cartesianProduct(B, C);
+
+ foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6],
+ [2, 6], [3, 6]])
+ {
+ assert(canFind(BC, tuple(n[0], n[1])));
+ }
+}
+
+// Issue 13091
+pure nothrow @safe @nogc unittest
+{
+ int[1] a = [1];
+ foreach (t; cartesianProduct(a[], a[])) {}
+}
+
+/// ditto
+auto cartesianProduct(RR...)(RR ranges)
+if (ranges.length >= 2 &&
+ allSatisfy!(isForwardRange, RR) &&
+ !anySatisfy!(isInfinite, RR))
+{
+ // This overload uses a much less template-heavy implementation when
+ // all ranges are finite forward ranges, which is the most common use
+ // case, so that we don't run out of resources too quickly.
+ //
+ // For infinite ranges or non-forward ranges, we fall back to the old
+ // implementation which expands an exponential number of templates.
+ import std.typecons : tuple;
+
+ static struct Result
+ {
+ RR ranges;
+ RR current;
+ bool empty = true;
+
+ this(RR _ranges)
+ {
+ ranges = _ranges;
+ empty = false;
+ foreach (i, r; ranges)
+ {
+ current[i] = r.save;
+ if (current[i].empty)
+ empty = true;
+ }
+ }
+ @property auto front()
+ {
+ import std.algorithm.internal : algoFormat;
+ import std.range : iota;
+ return mixin(algoFormat("tuple(%(current[%d].front%|,%))",
+ iota(0, current.length)));
+ }
+ void popFront()
+ {
+ foreach_reverse (i, ref r; current)
+ {
+ r.popFront();
+ if (!r.empty) break;
+
+ static if (i == 0)
+ empty = true;
+ else
+ r = ranges[i].save; // rollover
+ }
+ }
+ @property Result save()
+ {
+ Result copy = this;
+ foreach (i, r; ranges)
+ {
+ copy.ranges[i] = r.save;
+ copy.current[i] = current[i].save;
+ }
+ return copy;
+ }
+ }
+ static assert(isForwardRange!Result);
+
+ return Result(ranges);
+}
+
+@safe unittest
+{
+ // Issue 10693: cartesian product of empty ranges should be empty.
+ int[] a, b, c, d, e;
+ auto cprod = cartesianProduct(a,b,c,d,e);
+ assert(cprod.empty);
+ foreach (_; cprod) {} // should not crash
+
+ // Test case where only one of the ranges is empty: the result should still
+ // be empty.
+ int[] p=[1], q=[];
+ auto cprod2 = cartesianProduct(p,p,p,q,p);
+ assert(cprod2.empty);
+ foreach (_; cprod2) {} // should not crash
+}
+
+@safe unittest
+{
+ // .init value of cartesianProduct should be empty
+ auto cprod = cartesianProduct([0,0], [1,1], [2,2]);
+ assert(!cprod.empty);
+ assert(cprod.init.empty);
+}
+
+@safe unittest
+{
+ // Issue 13393
+ assert(!cartesianProduct([0],[0],[0]).save.empty);
+}
+
+/// ditto
+auto cartesianProduct(R1, R2, RR...)(R1 range1, R2 range2, RR otherRanges)
+if (!allSatisfy!(isForwardRange, R1, R2, RR) ||
+ anySatisfy!(isInfinite, R1, R2, RR))
+{
+ /* We implement the n-ary cartesian product by recursively invoking the
+ * binary cartesian product. To make the resulting range nicer, we denest
+ * one level of tuples so that a ternary cartesian product, for example,
+ * returns 3-element tuples instead of nested 2-element tuples.
+ */
+ import std.algorithm.internal : algoFormat;
+ import std.algorithm.iteration : map;
+ import std.range : iota;
+
+ enum string denest = algoFormat("tuple(a[0], %(a[1][%d]%|,%))",
+ iota(0, otherRanges.length+1));
+ return map!denest(
+ cartesianProduct(range1, cartesianProduct(range2, otherRanges))
+ );
+}
+
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple, Tuple;
+
+ auto N = sequence!"n"(0);
+ auto N3 = cartesianProduct(N, N, N);
+
+ // Check that tuples are properly denested
+ assert(is(ElementType!(typeof(N3)) == Tuple!(size_t,size_t,size_t)));
+
+ assert(canFind(N3, tuple(0, 27, 7)));
+ assert(canFind(N3, tuple(50, 23, 71)));
+ assert(canFind(N3, tuple(9, 3, 0)));
+}
+
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple, Tuple;
+
+ auto N = sequence!"n"(0);
+ auto N4 = cartesianProduct(N, N, N, N);
+
+ // Check that tuples are properly denested
+ assert(is(ElementType!(typeof(N4)) == Tuple!(size_t,size_t,size_t,size_t)));
+
+ assert(canFind(N4, tuple(1, 2, 3, 4)));
+ assert(canFind(N4, tuple(4, 3, 2, 1)));
+ assert(canFind(N4, tuple(10, 31, 7, 12)));
+}
+
+// Issue 9878
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ auto A = [ 1, 2, 3 ];
+ auto B = [ 'a', 'b', 'c' ];
+ auto C = [ "x", "y", "z" ];
+ auto ABC = cartesianProduct(A, B, C);
+
+ assert(ABC.equal([
+ tuple(1, 'a', "x"), tuple(1, 'a', "y"), tuple(1, 'a', "z"),
+ tuple(1, 'b', "x"), tuple(1, 'b', "y"), tuple(1, 'b', "z"),
+ tuple(1, 'c', "x"), tuple(1, 'c', "y"), tuple(1, 'c', "z"),
+ tuple(2, 'a', "x"), tuple(2, 'a', "y"), tuple(2, 'a', "z"),
+ tuple(2, 'b', "x"), tuple(2, 'b', "y"), tuple(2, 'b', "z"),
+ tuple(2, 'c', "x"), tuple(2, 'c', "y"), tuple(2, 'c', "z"),
+ tuple(3, 'a', "x"), tuple(3, 'a', "y"), tuple(3, 'a', "z"),
+ tuple(3, 'b', "x"), tuple(3, 'b', "y"), tuple(3, 'b', "z"),
+ tuple(3, 'c', "x"), tuple(3, 'c', "y"), tuple(3, 'c', "z")
+ ]));
+}
+
+pure @safe nothrow @nogc unittest
+{
+ import std.range.primitives : isForwardRange;
+ int[2] A = [1,2];
+ auto C = cartesianProduct(A[], A[], A[]);
+ assert(isForwardRange!(typeof(C)));
+
+ C.popFront();
+ auto front1 = C.front;
+ auto D = C.save;
+ C.popFront();
+ assert(D.front == front1);
+}
+
+// Issue 13935
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ auto seq = [1, 2].map!(x => x);
+ foreach (pair; cartesianProduct(seq, seq)) {}
+}
+
+// largestPartialIntersection
+/**
+Given a range of sorted $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives)
+$(D ror), copies to $(D tgt) the elements that are common to most ranges, along with their number
+of occurrences. All ranges in $(D ror) are assumed to be sorted by $(D
+less). Only the most frequent $(D tgt.length) elements are returned.
+
+Params:
+ less = The predicate the ranges are sorted by.
+ ror = A range of forward ranges sorted by `less`.
+ tgt = The target range to copy common elements to.
+ sorted = Whether the elements copied should be in sorted order.
+
+The function $(D largestPartialIntersection) is useful for
+e.g. searching an $(LINK2 https://en.wikipedia.org/wiki/Inverted_index,
+inverted index) for the documents most
+likely to contain some terms of interest. The complexity of the search
+is $(BIGOH n * log(tgt.length)), where $(D n) is the sum of lengths of
+all input ranges. This approach is faster than keeping an associative
+array of the occurrences and then selecting its top items, and also
+requires less memory ($(D largestPartialIntersection) builds its
+result directly in $(D tgt) and requires no extra memory).
+
+If at least one of the ranges is a multiset, then all occurences
+of a duplicate element are taken into account. The result is
+equivalent to merging all ranges and picking the most frequent
+$(D tgt.length) elements.
+
+Warning: Because $(D largestPartialIntersection) does not allocate
+extra memory, it will leave $(D ror) modified. Namely, $(D
+largestPartialIntersection) assumes ownership of $(D ror) and
+discretionarily swaps and advances elements of it. If you want $(D
+ror) to preserve its contents after the call, you may want to pass a
+duplicate to $(D largestPartialIntersection) (and perhaps cache the
+duplicate in between calls).
+ */
+void largestPartialIntersection
+(alias less = "a < b", RangeOfRanges, Range)
+(RangeOfRanges ror, Range tgt, SortOutput sorted = No.sortOutput)
+{
+ struct UnitWeights
+ {
+ static int opIndex(ElementType!(ElementType!RangeOfRanges)) { return 1; }
+ }
+ return largestPartialIntersectionWeighted!less(ror, tgt, UnitWeights(),
+ sorted);
+}
+
+///
+@system unittest
+{
+ import std.typecons : tuple, Tuple;
+
+ // Figure which number can be found in most arrays of the set of
+ // arrays below.
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto b = new Tuple!(double, uint)[1];
+ // it will modify the input range, hence we need to create a duplicate
+ largestPartialIntersection(a.dup, b);
+ // First member is the item, second is the occurrence count
+ assert(b[0] == tuple(7.0, 4u));
+ // 7.0 occurs in 4 out of 5 inputs, more than any other number
+
+ // If more of the top-frequent numbers are needed, just create a larger
+ // tgt range
+ auto c = new Tuple!(double, uint)[2];
+ largestPartialIntersection(a, c);
+ assert(c[0] == tuple(1.0, 3u));
+ // 1.0 occurs in 3 inputs
+
+ // multiset
+ double[][] x =
+ [
+ [1, 1, 1, 1, 4, 7, 8],
+ [1, 7],
+ [1, 7, 8],
+ [4, 7],
+ [7]
+ ];
+ auto y = new Tuple!(double, uint)[2];
+ largestPartialIntersection(x.dup, y);
+ // 7.0 occurs 5 times
+ assert(y[0] == tuple(7.0, 5u));
+ // 1.0 occurs 6 times
+ assert(y[1] == tuple(1.0, 6u));
+}
+
+import std.algorithm.sorting : SortOutput; // FIXME
+
+// largestPartialIntersectionWeighted
+/**
+Similar to $(D largestPartialIntersection), but associates a weight
+with each distinct element in the intersection.
+
+If at least one of the ranges is a multiset, then all occurences
+of a duplicate element are taken into account. The result
+is equivalent to merging all input ranges and picking the highest
+$(D tgt.length), weight-based ranking elements.
+
+Params:
+ less = The predicate the ranges are sorted by.
+ ror = A range of $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives)
+ sorted by `less`.
+ tgt = The target range to copy common elements to.
+ weights = An associative array mapping elements to weights.
+ sorted = Whether the elements copied should be in sorted order.
+
+*/
+void largestPartialIntersectionWeighted
+(alias less = "a < b", RangeOfRanges, Range, WeightsAA)
+(RangeOfRanges ror, Range tgt, WeightsAA weights, SortOutput sorted = No.sortOutput)
+{
+ import std.algorithm.iteration : group;
+ import std.algorithm.sorting : topNCopy;
+
+ if (tgt.empty) return;
+ alias InfoType = ElementType!Range;
+ bool heapComp(InfoType a, InfoType b)
+ {
+ return weights[a[0]] * a[1] > weights[b[0]] * b[1];
+ }
+ topNCopy!heapComp(group(multiwayMerge!less(ror)), tgt, sorted);
+}
+
+///
+@system unittest
+{
+ import std.typecons : tuple, Tuple;
+
+ // Figure which number can be found in most arrays of the set of
+ // arrays below, with specific per-element weights
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto b = new Tuple!(double, uint)[1];
+ double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ];
+ largestPartialIntersectionWeighted(a, b, weights);
+ // First member is the item, second is the occurrence count
+ assert(b[0] == tuple(4.0, 2u));
+ // 4.0 occurs 2 times -> 4.6 (2 * 2.3)
+ // 7.0 occurs 3 times -> 4.4 (3 * 1.1)
+
+ // multiset
+ double[][] x =
+ [
+ [ 1, 1, 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto y = new Tuple!(double, uint)[1];
+ largestPartialIntersectionWeighted(x, y, weights);
+ assert(y[0] == tuple(1.0, 5u));
+ // 1.0 occurs 5 times -> 1.2 * 5 = 6
+}
+
+@system unittest
+{
+ import std.conv : text;
+ import std.typecons : tuple, Tuple, Yes;
+
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto b = new Tuple!(double, uint)[2];
+ largestPartialIntersection(a, b, Yes.sortOutput);
+ assert(b == [ tuple(7.0, 4u), tuple(1.0, 3u) ][], text(b));
+ assert(a[0].empty);
+}
+
+@system unittest
+{
+ import std.conv : text;
+ import std.typecons : tuple, Tuple, Yes;
+
+ string[][] a =
+ [
+ [ "1", "4", "7", "8" ],
+ [ "1", "7" ],
+ [ "1", "7", "8"],
+ [ "4" ],
+ [ "7" ],
+ ];
+ auto b = new Tuple!(string, uint)[2];
+ largestPartialIntersection(a, b, Yes.sortOutput);
+ assert(b == [ tuple("7", 4u), tuple("1", 3u) ][], text(b));
+}
+
+@system unittest
+{
+ import std.typecons : tuple, Tuple;
+
+ // Figure which number can be found in most arrays of the set of
+ // arrays below, with specific per-element weights
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto b = new Tuple!(double, uint)[1];
+ double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ];
+ largestPartialIntersectionWeighted(a, b, weights);
+ // First member is the item, second is the occurrence count
+ assert(b[0] == tuple(4.0, 2u));
+}
+
+@system unittest
+{
+ import std.container : Array;
+ import std.typecons : Tuple;
+
+ alias T = Tuple!(uint, uint);
+ const Array!T arrayOne = Array!T( [ T(1,2), T(3,4) ] );
+ const Array!T arrayTwo = Array!T([ T(1,2), T(3,4) ] );
+
+ assert(arrayOne == arrayTwo);
+}
+
+// MultiwayMerge
+/**
+Merges multiple sets. The input sets are passed as a
+range of ranges and each is assumed to be sorted by $(D
+less). Computation is done lazily, one union element at a time. The
+complexity of one $(D popFront) operation is $(BIGOH
+log(ror.length)). However, the length of $(D ror) decreases as ranges
+in it are exhausted, so the complexity of a full pass through $(D
+MultiwayMerge) is dependent on the distribution of the lengths of ranges
+contained within $(D ror). If all ranges have the same length $(D n)
+(worst case scenario), the complexity of a full pass through $(D
+MultiwayMerge) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D
+log(ror.length)) times worse than just spanning all ranges in
+turn. The output comes sorted (unstably) by $(D less).
+
+The length of the resulting range is the sum of all lengths of
+the ranges passed as input. This means that all elements (duplicates
+included) are transferred to the resulting range.
+
+For backward compatibility, `multiwayMerge` is available under
+the name `nWayUnion` and `MultiwayMerge` under the name of `NWayUnion` .
+Future code should use `multiwayMerge` and `MultiwayMerge` as `nWayUnion`
+and `NWayUnion` will be deprecated.
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ ror = A range of ranges sorted by `less` to compute the union for.
+
+Returns:
+ A range of the union of the ranges in `ror`.
+
+Warning: Because $(D MultiwayMerge) does not allocate extra memory, it
+will leave $(D ror) modified. Namely, $(D MultiwayMerge) assumes ownership
+of $(D ror) and discretionarily swaps and advances elements of it. If
+you want $(D ror) to preserve its contents after the call, you may
+want to pass a duplicate to $(D MultiwayMerge) (and perhaps cache the
+duplicate in between calls).
+ */
+struct MultiwayMerge(alias less, RangeOfRanges)
+{
+ import std.container : BinaryHeap;
+
+ private alias ElementType = .ElementType!(.ElementType!RangeOfRanges);
+ private alias comp = binaryFun!less;
+ private RangeOfRanges _ror;
+
+ ///
+ static bool compFront(.ElementType!RangeOfRanges a,
+ .ElementType!RangeOfRanges b)
+ {
+ // revert comparison order so we get the smallest elements first
+ return comp(b.front, a.front);
+ }
+ private BinaryHeap!(RangeOfRanges, compFront) _heap;
+
+ ///
+ this(RangeOfRanges ror)
+ {
+ import std.algorithm.mutation : remove, SwapStrategy;
+
+ // Preemptively get rid of all empty ranges in the input
+ // No need for stability either
+ _ror = remove!("a.empty", SwapStrategy.unstable)(ror);
+ //Build the heap across the range
+ _heap.acquire(_ror);
+ }
+
+ ///
+ @property bool empty() { return _ror.empty; }
+
+ ///
+ @property auto ref front()
+ {
+ return _heap.front.front;
+ }
+
+ ///
+ void popFront()
+ {
+ _heap.removeFront();
+ // let's look at the guy just popped
+ _ror.back.popFront();
+ if (_ror.back.empty)
+ {
+ _ror.popBack();
+ // nothing else to do: the empty range is not in the
+ // heap and not in _ror
+ return;
+ }
+ // Put the popped range back in the heap
+ _heap.conditionalInsert(_ror.back) || assert(false);
+ }
+}
+
+/// Ditto
+MultiwayMerge!(less, RangeOfRanges) multiwayMerge
+(alias less = "a < b", RangeOfRanges)
+(RangeOfRanges ror)
+{
+ return typeof(return)(ror);
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto witness = [
+ 1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8
+ ];
+ assert(equal(multiwayMerge(a), witness));
+
+ double[][] b =
+ [
+ // range with duplicates
+ [ 1, 1, 4, 7, 8 ],
+ [ 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ // duplicates are propagated to the resulting range
+ assert(equal(multiwayMerge(b), witness));
+}
+
+alias nWayUnion = multiwayMerge;
+alias NWayUnion = MultiwayMerge;
+
+/**
+Computes the union of multiple ranges. The input ranges are passed
+as a range of ranges and each is assumed to be sorted by $(D
+less). Computation is done lazily, one union element at a time.
+`multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`.
+
+"The output of multiwayUnion has no duplicates even when its inputs contain duplicates."
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ ror = A range of ranges sorted by `less` to compute the intersection for.
+
+Returns:
+ A range of the union of the ranges in `ror`.
+
+See also: $(LREF multiwayMerge)
+ */
+auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror)
+{
+ import std.algorithm.iteration : uniq;
+ return ror.multiwayMerge.uniq;
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // sets
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+
+ auto witness = [1, 4, 7, 8];
+ assert(equal(multiwayUnion(a), witness));
+
+ // multisets
+ double[][] b =
+ [
+ [ 1, 1, 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ assert(equal(multiwayUnion(b), witness));
+}
+
+/**
+Lazily computes the difference of $(D r1) and $(D r2). The two ranges
+are assumed to be sorted by $(D less). The element types of the two
+ranges must have a common type.
+
+
+In the case of multisets, considering that element `a` appears `x`
+times in $(D r1) and `y` times and $(D r2), the number of occurences
+of `a` in the resulting range is going to be `x-y` if x > y or 0 othwerise.
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ r1 = The first range.
+ r2 = The range to subtract from `r1`.
+
+Returns:
+ A range of the difference of `r1` and `r2`.
+
+See_also: $(LREF setSymmetricDifference)
+ */
+struct SetDifference(alias less = "a < b", R1, R2)
+if (isInputRange!(R1) && isInputRange!(R2))
+{
+private:
+ R1 r1;
+ R2 r2;
+ alias comp = binaryFun!(less);
+
+ void adjustPosition()
+ {
+ while (!r1.empty)
+ {
+ if (r2.empty || comp(r1.front, r2.front)) break;
+ if (comp(r2.front, r1.front))
+ {
+ r2.popFront();
+ }
+ else
+ {
+ // both are equal
+ r1.popFront();
+ r2.popFront();
+ }
+ }
+ }
+
+public:
+ ///
+ this(R1 r1, R2 r2)
+ {
+ this.r1 = r1;
+ this.r2 = r2;
+ // position to the first element
+ adjustPosition();
+ }
+
+ ///
+ void popFront()
+ {
+ r1.popFront();
+ adjustPosition();
+ }
+
+ ///
+ @property auto ref front()
+ {
+ assert(!empty);
+ return r1.front;
+ }
+
+ static if (isForwardRange!R1 && isForwardRange!R2)
+ {
+ ///
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret.r1 = r1.save;
+ ret.r2 = r2.save;
+ return ret;
+ }
+ }
+
+ ///
+ @property bool empty() { return r1.empty; }
+}
+
+/// Ditto
+SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2)
+(R1 r1, R2 r2)
+{
+ return typeof(return)(r1, r2);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : isForwardRange;
+
+ //sets
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ assert(equal(setDifference(a, b), [5, 9]));
+ static assert(isForwardRange!(typeof(setDifference(a, b))));
+
+ // multisets
+ int[] x = [1, 1, 1, 2, 3];
+ int[] y = [1, 1, 2, 4, 5];
+ auto r = setDifference(x, y);
+ assert(equal(r, [1, 3]));
+ assert(setDifference(r, x).empty);
+}
+
+@safe unittest // Issue 10460
+{
+ import std.algorithm.comparison : equal;
+
+ int[] a = [1, 2, 3, 4, 5];
+ int[] b = [2, 4];
+ foreach (ref e; setDifference(a, b))
+ e = 0;
+ assert(equal(a, [0, 2, 0, 4, 0]));
+}
+
+/**
+Lazily computes the intersection of two or more input ranges $(D
+ranges). The ranges are assumed to be sorted by $(D less). The element
+types of the ranges must have a common type.
+
+In the case of multisets, the range with the minimum number of
+occurences of a given element, propagates the number of
+occurences of this element to the resulting range.
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ ranges = The ranges to compute the intersection for.
+
+Returns:
+ A range containing the intersection of the given ranges.
+ */
+struct SetIntersection(alias less = "a < b", Rs...)
+if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) &&
+ !is(CommonType!(staticMap!(ElementType, Rs)) == void))
+{
+private:
+ Rs _input;
+ alias comp = binaryFun!less;
+ alias ElementType = CommonType!(staticMap!(.ElementType, Rs));
+
+ // Positions to the first elements that are all equal
+ void adjustPosition()
+ {
+ if (empty) return;
+
+ size_t done = Rs.length;
+ static if (Rs.length > 1) while (true)
+ {
+ foreach (i, ref r; _input)
+ {
+ alias next = _input[(i + 1) % Rs.length];
+
+ if (comp(next.front, r.front))
+ {
+ do
+ {
+ next.popFront();
+ if (next.empty) return;
+ } while (comp(next.front, r.front));
+ done = Rs.length;
+ }
+ if (--done == 0) return;
+ }
+ }
+ }
+
+public:
+ ///
+ this(Rs input)
+ {
+ this._input = input;
+ // position to the first element
+ adjustPosition();
+ }
+
+ ///
+ @property bool empty()
+ {
+ foreach (ref r; _input)
+ {
+ if (r.empty) return true;
+ }
+ return false;
+ }
+
+ ///
+ void popFront()
+ {
+ assert(!empty);
+ static if (Rs.length > 1) foreach (i, ref r; _input)
+ {
+ alias next = _input[(i + 1) % Rs.length];
+ assert(!comp(r.front, next.front));
+ }
+
+ foreach (ref r; _input)
+ {
+ r.popFront();
+ }
+ adjustPosition();
+ }
+
+ ///
+ @property ElementType front()
+ {
+ assert(!empty);
+ return _input[0].front;
+ }
+
+ static if (allSatisfy!(isForwardRange, Rs))
+ {
+ ///
+ @property SetIntersection save()
+ {
+ auto ret = this;
+ foreach (i, ref r; _input)
+ {
+ ret._input[i] = r.save;
+ }
+ return ret;
+ }
+ }
+}
+
+/// Ditto
+SetIntersection!(less, Rs) setIntersection(alias less = "a < b", Rs...)(Rs ranges)
+if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) &&
+ !is(CommonType!(staticMap!(ElementType, Rs)) == void))
+{
+ return typeof(return)(ranges);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // sets
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ int[] c = [ 0, 1, 4, 5, 7, 8 ];
+ assert(equal(setIntersection(a, a), a));
+ assert(equal(setIntersection(a, b), [1, 2, 4, 7]));
+ assert(equal(setIntersection(a, b, c), [1, 4, 7]));
+
+ // multisets
+ int[] d = [ 1, 1, 2, 2, 7, 7 ];
+ int[] e = [ 1, 1, 1, 7];
+ assert(equal(setIntersection(a, d), [1, 2, 7]));
+ assert(equal(setIntersection(d, e), [1, 1, 7]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ int[] c = [ 0, 1, 4, 5, 7, 8 ];
+ int[] d = [ 1, 3, 4 ];
+ int[] e = [ 4, 5 ];
+
+ assert(equal(setIntersection(a, a), a));
+ assert(equal(setIntersection(a, a, a), a));
+ assert(equal(setIntersection(a, b), [1, 2, 4, 7]));
+ assert(equal(setIntersection(a, b, c), [1, 4, 7]));
+ assert(equal(setIntersection(a, b, c, d), [1, 4]));
+ assert(equal(setIntersection(a, b, c, d, e), [4]));
+
+ auto inpA = a.filter!(_ => true), inpB = b.filter!(_ => true);
+ auto inpC = c.filter!(_ => true), inpD = d.filter!(_ => true);
+ assert(equal(setIntersection(inpA, inpB, inpC, inpD), [1, 4]));
+
+ assert(equal(setIntersection(a, b, b, a), [1, 2, 4, 7]));
+ assert(equal(setIntersection(a, c, b), [1, 4, 7]));
+ assert(equal(setIntersection(b, a, c), [1, 4, 7]));
+ assert(equal(setIntersection(b, c, a), [1, 4, 7]));
+ assert(equal(setIntersection(c, a, b), [1, 4, 7]));
+ assert(equal(setIntersection(c, b, a), [1, 4, 7]));
+}
+
+/**
+Lazily computes the symmetric difference of $(D r1) and $(D r2),
+i.e. the elements that are present in exactly one of $(D r1) and $(D
+r2). The two ranges are assumed to be sorted by $(D less), and the
+output is also sorted by $(D less). The element types of the two
+ranges must have a common type.
+
+If both ranges are sets (without duplicated elements), the resulting
+range is going to be a set. If at least one of the ranges is a multiset,
+the number of occurences of an element `x` in the resulting range is `abs(a-b)`
+where `a` is the number of occurences of `x` in $(D r1), `b` is the number of
+occurences of `x` in $(D r2), and `abs` is the absolute value.
+
+If both arguments are ranges of L-values of the same type then
+$(D SetSymmetricDifference) will also be a range of L-values of
+that type.
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ r1 = The first range.
+ r2 = The second range.
+
+Returns:
+ A range of the symmetric difference between `r1` and `r2`.
+
+See_also: $(LREF setDifference)
+ */
+struct SetSymmetricDifference(alias less = "a < b", R1, R2)
+if (isInputRange!(R1) && isInputRange!(R2))
+{
+private:
+ R1 r1;
+ R2 r2;
+ //bool usingR2;
+ alias comp = binaryFun!(less);
+
+ void adjustPosition()
+ {
+ while (!r1.empty && !r2.empty)
+ {
+ if (comp(r1.front, r2.front) || comp(r2.front, r1.front))
+ {
+ break;
+ }
+ // equal, pop both
+ r1.popFront();
+ r2.popFront();
+ }
+ }
+
+public:
+ ///
+ this(R1 r1, R2 r2)
+ {
+ this.r1 = r1;
+ this.r2 = r2;
+ // position to the first element
+ adjustPosition();
+ }
+
+ ///
+ void popFront()
+ {
+ assert(!empty);
+ if (r1.empty) r2.popFront();
+ else if (r2.empty) r1.popFront();
+ else
+ {
+ // neither is empty
+ if (comp(r1.front, r2.front))
+ {
+ r1.popFront();
+ }
+ else
+ {
+ assert(comp(r2.front, r1.front));
+ r2.popFront();
+ }
+ }
+ adjustPosition();
+ }
+
+ ///
+ @property auto ref front()
+ {
+ assert(!empty);
+ immutable chooseR1 = r2.empty || !r1.empty && comp(r1.front, r2.front);
+ assert(chooseR1 || r1.empty || comp(r2.front, r1.front));
+ return chooseR1 ? r1.front : r2.front;
+ }
+
+ static if (isForwardRange!R1 && isForwardRange!R2)
+ {
+ ///
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret.r1 = r1.save;
+ ret.r2 = r2.save;
+ return ret;
+ }
+ }
+
+ ///
+ ref auto opSlice() { return this; }
+
+ ///
+ @property bool empty() { return r1.empty && r2.empty; }
+}
+
+/// Ditto
+SetSymmetricDifference!(less, R1, R2)
+setSymmetricDifference(alias less = "a < b", R1, R2)
+(R1 r1, R2 r2)
+{
+ return typeof(return)(r1, r2);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : isForwardRange;
+
+ // sets
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ assert(equal(setSymmetricDifference(a, b), [0, 5, 8, 9][]));
+ static assert(isForwardRange!(typeof(setSymmetricDifference(a, b))));
+
+ //mutisets
+ int[] c = [1, 1, 1, 1, 2, 2, 2, 4, 5, 6];
+ int[] d = [1, 1, 2, 2, 2, 2, 4, 7, 9];
+ assert(equal(setSymmetricDifference(c, d), setSymmetricDifference(d, c)));
+ assert(equal(setSymmetricDifference(c, d), [1, 1, 2, 5, 6, 7, 9]));
+}
+
+@safe unittest // Issue 10460
+{
+ import std.algorithm.comparison : equal;
+
+ int[] a = [1, 2];
+ double[] b = [2.0, 3.0];
+ int[] c = [2, 3];
+
+ alias R1 = typeof(setSymmetricDifference(a, b));
+ static assert(is(ElementType!R1 == double));
+ static assert(!hasLvalueElements!R1);
+
+ alias R2 = typeof(setSymmetricDifference(a, c));
+ static assert(is(ElementType!R2 == int));
+ static assert(hasLvalueElements!R2);
+
+ assert(equal(setSymmetricDifference(a, b), [1.0, 3.0]));
+ assert(equal(setSymmetricDifference(a, c), [1, 3]));
+}
+
+/++
+TODO: once SetUnion got deprecated we can provide the usual definition
+(= merge + filter after uniqs)
+See: https://github.com/dlang/phobos/pull/4249
+/**
+Lazily computes the union of two or more ranges $(D rs). The ranges
+are assumed to be sorted by $(D less). Elements in the output are
+unique. The element types of all ranges must have a common type.
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ rs = The ranges to compute the union for.
+
+Returns:
+ A range containing the unique union of the given ranges.
+
+See_Also:
+ $(REF merge, std,algorithm,sorting)
+ */
+auto setUnion(alias less = "a < b", Rs...)
+(Rs rs)
+{
+ import std.algorithm.iteration : uniq;
+ import std.algorithm.sorting : merge;
+ return merge!(less, Rs)(rs).uniq;
+}
+
+///
+@safe pure nothrow unittest
+ ///
+{
+ import std.algorithm.comparison : equal;
+
+ int[] a = [1, 3, 5];
+ int[] b = [2, 3, 4];
+ assert(a.setUnion(b).equal([1, 2, 3, 4, 5]));
+}
+
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ double[] c = [ 10.5 ];
+
+ assert(equal(setUnion(a, b), [0, 1, 2, 4, 5, 7, 8, 9][]));
+ assert(equal(setUnion(a, c, b),
+ [0, 1, 2, 4, 5, 7, 8, 9, 10.5][]));
+}
+
+@safe unittest
+{
+ // save
+ import std.range : dropOne;
+ int[] a = [0, 1, 2];
+ int[] b = [0, 3];
+ auto arr = a.setUnion(b);
+ assert(arr.front == 0);
+ assert(arr.save.dropOne.front == 1);
+ assert(arr.front == 0);
+}
+
+@nogc @safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ static immutable a = [1, 3, 5];
+ static immutable b = [2, 4];
+ static immutable r = [1, 2, 3, 4, 5];
+ assert(a.setUnion(b).equal(r));
+}
+
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+ import std.range : iota;
+
+ auto dummyResult1 = [1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10];
+ auto dummyResult2 = iota(1, 11);
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ assert(d.setUnion([1, 1.5, 5.5]).equal(dummyResult1));
+ assert(d.setUnion(d).equal(dummyResult2));
+ }
+}
+++/
diff --git a/libphobos/src/std/algorithm/sorting.d b/libphobos/src/std/algorithm/sorting.d
new file mode 100644
index 0000000..2400bca
--- /dev/null
+++ b/libphobos/src/std/algorithm/sorting.d
@@ -0,0 +1,4468 @@
+// Written in the D programming language.
+/**
+This is a submodule of $(MREF std, algorithm).
+It contains generic _sorting algorithms.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE Cheat Sheet,
+$(TR $(TH Function Name) $(TH Description))
+$(T2 completeSort,
+ If $(D a = [10, 20, 30]) and $(D b = [40, 6, 15]), then
+ $(D completeSort(a, b)) leaves $(D a = [6, 10, 15]) and $(D b = [20,
+ 30, 40]).
+ The range $(D a) must be sorted prior to the call, and as a result the
+ combination $(D $(REF chain, std,range)(a, b)) is sorted.)
+$(T2 isPartitioned,
+ $(D isPartitioned!"a < 0"([-1, -2, 1, 0, 2])) returns $(D true) because
+ the predicate is $(D true) for a portion of the range and $(D false)
+ afterwards.)
+$(T2 isSorted,
+ $(D isSorted([1, 1, 2, 3])) returns $(D true).)
+$(T2 isStrictlyMonotonic,
+ $(D isStrictlyMonotonic([1, 1, 2, 3])) returns $(D false).)
+$(T2 ordered,
+ $(D ordered(1, 1, 2, 3)) returns $(D true).)
+$(T2 strictlyOrdered,
+ $(D strictlyOrdered(1, 1, 2, 3)) returns $(D false).)
+$(T2 makeIndex,
+ Creates a separate index for a range.)
+$(T2 merge,
+ Lazily merges two or more sorted ranges.)
+$(T2 multiSort,
+ Sorts by multiple keys.)
+$(T2 nextEvenPermutation,
+ Computes the next lexicographically greater even permutation of a range
+ in-place.)
+$(T2 nextPermutation,
+ Computes the next lexicographically greater permutation of a range
+ in-place.)
+$(T2 partialSort,
+ If $(D a = [5, 4, 3, 2, 1]), then $(D partialSort(a, 3)) leaves
+ $(D a[0 .. 3] = [1, 2, 3]).
+ The other elements of $(D a) are left in an unspecified order.)
+$(T2 partition,
+ Partitions a range according to a unary predicate.)
+$(T2 partition3,
+ Partitions a range according to a binary predicate in three parts (less
+ than, equal, greater than the given pivot). Pivot is not given as an
+ index, but instead as an element independent from the range's content.)
+$(T2 pivotPartition,
+ Partitions a range according to a binary predicate in two parts: less
+ than or equal, and greater than or equal to the given pivot, passed as
+ an index in the range.)
+$(T2 schwartzSort,
+ Sorts with the help of the $(LINK2 https://en.wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform).)
+$(T2 sort,
+ Sorts.)
+$(T2 topN,
+ Separates the top elements in a range.)
+$(T2 topNCopy,
+ Copies out the top elements of a range.)
+$(T2 topNIndex,
+ Builds an index of the top elements of a range.)
+)
+
+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/_sorting.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.sorting;
+
+import std.algorithm.mutation : SwapStrategy;
+import std.functional; // : unaryFun, binaryFun;
+import std.range.primitives;
+import std.typecons : Flag;
+// FIXME
+import std.meta; // : allSatisfy;
+import std.range; // : SortedRange;
+import std.traits;
+
+/**
+Specifies whether the output of certain algorithm is desired in sorted
+format.
+
+If set to $(D SortOutput.no), the output should not be sorted.
+
+Otherwise if set to $(D SortOutput.yes), the output should be sorted.
+ */
+alias SortOutput = Flag!"sortOutput";
+
+// completeSort
+/**
+Sorts the random-access range $(D chain(lhs, rhs)) according to
+predicate $(D less). The left-hand side of the range $(D lhs) is
+assumed to be already sorted; $(D rhs) is assumed to be unsorted. The
+exact strategy chosen depends on the relative sizes of $(D lhs) and
+$(D rhs). Performs $(BIGOH lhs.length + rhs.length * log(rhs.length))
+(best case) to $(BIGOH (lhs.length + rhs.length) * log(lhs.length +
+rhs.length)) (worst-case) evaluations of $(D swap).
+
+Params:
+ less = The predicate to sort by.
+ ss = The swapping strategy to use.
+ lhs = The sorted, left-hand side of the random access range to be sorted.
+ rhs = The unsorted, right-hand side of the random access range to be
+ sorted.
+*/
+void completeSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
+ RandomAccessRange1, RandomAccessRange2)(SortedRange!(RandomAccessRange1, less) lhs, RandomAccessRange2 rhs)
+if (hasLength!(RandomAccessRange2) && hasSlicing!(RandomAccessRange2))
+{
+ import std.algorithm.mutation : bringToFront;
+ import std.range : chain, assumeSorted;
+ // Probably this algorithm can be optimized by using in-place
+ // merge
+ auto lhsOriginal = lhs.release();
+ foreach (i; 0 .. rhs.length)
+ {
+ auto sortedSoFar = chain(lhsOriginal, rhs[0 .. i]);
+ auto ub = assumeSorted!less(sortedSoFar).upperBound(rhs[i]);
+ if (!ub.length) continue;
+ bringToFront(ub.release(), rhs[i .. i + 1]);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.range : assumeSorted;
+ int[] a = [ 1, 2, 3 ];
+ int[] b = [ 4, 0, 6, 5 ];
+ completeSort(assumeSorted(a), b);
+ assert(a == [ 0, 1, 2 ]);
+ assert(b == [ 3, 4, 5, 6 ]);
+}
+
+// isSorted
+/**
+Checks whether a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+is sorted according to the comparison operation $(D less). Performs $(BIGOH r.length)
+evaluations of $(D less).
+
+Unlike `isSorted`, `isStrictlyMonotonic` does not allow for equal values,
+i.e. values for which both `less(a, b)` and `less(b, a)` are false.
+
+With either function, the predicate must be a strict ordering just like with
+`isSorted`. For example, using `"a <= b"` instead of `"a < b"` is
+incorrect and will cause failed assertions.
+
+Params:
+ less = Predicate the range should be sorted by.
+ r = Forward range to check for sortedness.
+
+Returns:
+ `true` if the range is sorted, false otherwise. `isSorted` allows
+ duplicates, `isStrictlyMonotonic` not.
+*/
+bool isSorted(alias less = "a < b", Range)(Range r)
+if (isForwardRange!(Range))
+{
+ if (r.empty) return true;
+
+ static if (isRandomAccessRange!Range && hasLength!Range)
+ {
+ immutable limit = r.length - 1;
+ foreach (i; 0 .. limit)
+ {
+ if (!binaryFun!less(r[i + 1], r[i])) continue;
+ assert(
+ !binaryFun!less(r[i], r[i + 1]),
+ "Predicate for isSorted is not antisymmetric. Both" ~
+ " pred(a, b) and pred(b, a) are true for certain values.");
+ return false;
+ }
+ }
+ else
+ {
+ auto ahead = r.save;
+ ahead.popFront();
+ size_t i;
+
+ for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i)
+ {
+ if (!binaryFun!less(ahead.front, r.front)) continue;
+ // Check for antisymmetric predicate
+ assert(
+ !binaryFun!less(r.front, ahead.front),
+ "Predicate for isSorted is not antisymmetric. Both" ~
+ " pred(a, b) and pred(b, a) are true for certain values.");
+ return false;
+ }
+ }
+ return true;
+}
+
+///
+@safe unittest
+{
+ assert([1, 1, 2].isSorted);
+ // strictly monotonic doesn't allow duplicates
+ assert(![1, 1, 2].isStrictlyMonotonic);
+
+ int[] arr = [4, 3, 2, 1];
+ assert(!isSorted(arr));
+ assert(!isStrictlyMonotonic(arr));
+
+ assert(isSorted!"a > b"(arr));
+ assert(isStrictlyMonotonic!"a > b"(arr));
+
+ sort(arr);
+ assert(isSorted(arr));
+ assert(isStrictlyMonotonic(arr));
+}
+
+@safe unittest
+{
+ import std.conv : to;
+
+ // Issue 9457
+ auto x = "abcd";
+ assert(isSorted(x));
+ auto y = "acbd";
+ assert(!isSorted(y));
+
+ int[] a = [1, 2, 3];
+ assert(isSorted(a));
+ int[] b = [1, 3, 2];
+ assert(!isSorted(b));
+
+ // ignores duplicates
+ int[] c = [1, 1, 2];
+ assert(isSorted(c));
+
+ dchar[] ds = "コーヒーが好きです"d.dup;
+ sort(ds);
+ string s = to!string(ds);
+ assert(isSorted(ds)); // random-access
+ assert(isSorted(s)); // bidirectional
+}
+
+@nogc @safe nothrow pure unittest
+{
+ static immutable a = [1, 2, 3];
+ assert(a.isSorted);
+}
+
+/// ditto
+bool isStrictlyMonotonic(alias less = "a < b", Range)(Range r)
+if (isForwardRange!Range)
+{
+ import std.algorithm.searching : findAdjacent;
+ return findAdjacent!((a,b) => !binaryFun!less(a,b))(r).empty;
+}
+
+@safe unittest
+{
+ import std.conv : to;
+
+ assert("abcd".isStrictlyMonotonic);
+ assert(!"aacd".isStrictlyMonotonic);
+ assert(!"acb".isStrictlyMonotonic);
+
+ assert([1, 2, 3].isStrictlyMonotonic);
+ assert(![1, 3, 2].isStrictlyMonotonic);
+ assert(![1, 1, 2].isStrictlyMonotonic);
+
+ // ー occurs twice -> can't be strict
+ dchar[] ds = "コーヒーが好きです"d.dup;
+ sort(ds);
+ string s = to!string(ds);
+ assert(!isStrictlyMonotonic(ds)); // random-access
+ assert(!isStrictlyMonotonic(s)); // bidirectional
+
+ dchar[] ds2 = "コーヒが好きです"d.dup;
+ sort(ds2);
+ string s2 = to!string(ds2);
+ assert(isStrictlyMonotonic(ds2)); // random-access
+ assert(isStrictlyMonotonic(s2)); // bidirectional
+}
+
+@nogc @safe nothrow pure unittest
+{
+ static immutable a = [1, 2, 3];
+ assert(a.isStrictlyMonotonic);
+}
+
+/**
+Like $(D isSorted), returns $(D true) if the given $(D values) are ordered
+according to the comparison operation $(D less). Unlike $(D isSorted), takes values
+directly instead of structured in a range.
+
+$(D ordered) allows repeated values, e.g. $(D ordered(1, 1, 2)) is $(D true). To verify
+that the values are ordered strictly monotonically, use $(D strictlyOrdered);
+$(D strictlyOrdered(1, 1, 2)) is $(D false).
+
+With either function, the predicate must be a strict ordering. For example,
+using $(D "a <= b") instead of $(D "a < b") is incorrect and will cause failed
+assertions.
+
+Params:
+ values = The tested value
+ less = The comparison predicate
+
+Returns:
+ $(D true) if the values are ordered; $(D ordered) allows for duplicates,
+ $(D strictlyOrdered) does not.
+*/
+
+bool ordered(alias less = "a < b", T...)(T values)
+if ((T.length == 2 && is(typeof(binaryFun!less(values[1], values[0])) : bool))
+ ||
+ (T.length > 2 && is(typeof(ordered!less(values[0 .. 1 + $ / 2])))
+ && is(typeof(ordered!less(values[$ / 2 .. $]))))
+ )
+{
+ foreach (i, _; T[0 .. $ - 1])
+ {
+ if (binaryFun!less(values[i + 1], values[i]))
+ {
+ assert(!binaryFun!less(values[i], values[i + 1]),
+ __FUNCTION__ ~ ": incorrect non-strict predicate.");
+ return false;
+ }
+ }
+ return true;
+}
+
+/// ditto
+bool strictlyOrdered(alias less = "a < b", T...)(T values)
+if (is(typeof(ordered!less(values))))
+{
+ foreach (i, _; T[0 .. $ - 1])
+ {
+ if (!binaryFun!less(values[i], values[i + 1]))
+ {
+ return false;
+ }
+ assert(!binaryFun!less(values[i + 1], values[i]),
+ __FUNCTION__ ~ ": incorrect non-strict predicate.");
+ }
+ return true;
+}
+
+///
+@safe unittest
+{
+ assert(ordered(42, 42, 43));
+ assert(!strictlyOrdered(43, 42, 45));
+ assert(ordered(42, 42, 43));
+ assert(!strictlyOrdered(42, 42, 43));
+ assert(!ordered(43, 42, 45));
+ // Ordered lexicographically
+ assert(ordered("Jane", "Jim", "Joe"));
+ assert(strictlyOrdered("Jane", "Jim", "Joe"));
+ // Incidentally also ordered by length decreasing
+ assert(ordered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe"));
+ // ... but not strictly so: "Jim" and "Joe" have the same length
+ assert(!strictlyOrdered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe"));
+}
+
+// partition
+/**
+Partitions a range in two using the given $(D predicate).
+Specifically, reorders the range $(D r = [left, right$(RPAREN)) using $(D swap)
+such that all elements $(D i) for which $(D predicate(i)) is $(D true) come
+before all elements $(D j) for which $(D predicate(j)) returns $(D false).
+
+Performs $(BIGOH r.length) (if unstable or semistable) or $(BIGOH
+r.length * log(r.length)) (if stable) evaluations of $(D less) and $(D
+swap). The unstable version computes the minimum possible evaluations
+of $(D swap) (roughly half of those performed by the semistable
+version).
+
+Params:
+ predicate = The predicate to partition by.
+ ss = The swapping strategy to employ.
+ r = The random-access range to partition.
+
+Returns:
+
+The right part of $(D r) after partitioning.
+
+If $(D ss == SwapStrategy.stable), $(D partition) preserves the relative
+ordering of all elements $(D a), $(D b) in $(D r) for which $(D predicate(a) ==
+predicate(b)). If $(D ss == SwapStrategy.semistable), $(D partition) preserves
+the relative ordering of all elements $(D a), $(D b) in the left part of $(D r)
+for which $(D predicate(a) == predicate(b)).
+
+See_Also:
+ STL's $(HTTP sgi.com/tech/stl/_partition.html, _partition)$(BR)
+ STL's $(HTTP sgi.com/tech/stl/stable_partition.html, stable_partition)
+*/
+Range partition(alias predicate, SwapStrategy ss, Range)(Range r)
+if (ss == SwapStrategy.stable && isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range)
+{
+ import std.algorithm.mutation : bringToFront;
+
+ alias pred = unaryFun!(predicate);
+ if (r.empty) return r;
+
+ if (r.length == 1)
+ {
+ if (pred(r.front)) r.popFront();
+ return r;
+ }
+ const middle = r.length / 2;
+ alias recurse = .partition!(pred, ss, Range);
+ auto lower = recurse(r[0 .. middle]);
+ auto upper = recurse(r[middle .. r.length]);
+ bringToFront(lower, r[middle .. r.length - upper.length]);
+ return r[r.length - lower.length - upper.length .. r.length];
+}
+
+///ditto
+Range partition(alias predicate, SwapStrategy ss = SwapStrategy.unstable, Range)(Range r)
+if (ss != SwapStrategy.stable && isInputRange!Range && hasSwappableElements!Range)
+{
+ import std.algorithm.mutation : swap;
+ alias pred = unaryFun!(predicate);
+
+ static if (ss == SwapStrategy.semistable)
+ {
+ if (r.empty) return r;
+
+ for (; !r.empty; r.popFront())
+ {
+ // skip the initial portion of "correct" elements
+ if (pred(r.front)) continue;
+ // hit the first "bad" element
+ auto result = r;
+ for (r.popFront(); !r.empty; r.popFront())
+ {
+ if (!pred(r.front)) continue;
+ swap(result.front, r.front);
+ result.popFront();
+ }
+ return result;
+ }
+
+ return r;
+ }
+ else
+ {
+ // Inspired from www.stepanovpapers.com/PAM3-partition_notes.pdf,
+ // section "Bidirectional Partition Algorithm (Hoare)"
+ static if (isDynamicArray!Range)
+ {
+ import std.algorithm.mutation : swapAt;
+ // For dynamic arrays prefer index-based manipulation
+ if (!r.length) return r;
+ size_t lo = 0, hi = r.length - 1;
+ for (;;)
+ {
+ for (;;)
+ {
+ if (lo > hi) return r[lo .. r.length];
+ if (!pred(r[lo])) break;
+ ++lo;
+ }
+ // found the left bound
+ assert(lo <= hi);
+ for (;;)
+ {
+ if (lo == hi) return r[lo .. r.length];
+ if (pred(r[hi])) break;
+ --hi;
+ }
+ // found the right bound, swap & make progress
+ r.swapAt(lo++, hi--);
+ }
+ }
+ else
+ {
+ import std.algorithm.mutation : swap;
+ auto result = r;
+ for (;;)
+ {
+ for (;;)
+ {
+ if (r.empty) return result;
+ if (!pred(r.front)) break;
+ r.popFront();
+ result.popFront();
+ }
+ // found the left bound
+ assert(!r.empty);
+ for (;;)
+ {
+ if (pred(r.back)) break;
+ r.popBack();
+ if (r.empty) return result;
+ }
+ // found the right bound, swap & make progress
+ static if (is(typeof(swap(r.front, r.back))))
+ {
+ swap(r.front, r.back);
+ }
+ else
+ {
+ auto t1 = r.moveFront(), t2 = r.moveBack();
+ r.front = t2;
+ r.back = t1;
+ }
+ r.popFront();
+ result.popFront();
+ r.popBack();
+ }
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.mutation : SwapStrategy;
+ import std.algorithm.searching : count, find;
+ import std.conv : text;
+ import std.range.primitives : empty;
+
+ auto Arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ auto arr = Arr.dup;
+ static bool even(int a) { return (a & 1) == 0; }
+ // Partition arr such that even numbers come first
+ auto r = partition!(even)(arr);
+ // Now arr is separated in evens and odds.
+ // Numbers may have become shuffled due to instability
+ assert(r == arr[5 .. $]);
+ assert(count!(even)(arr[0 .. 5]) == 5);
+ assert(find!(even)(r).empty);
+
+ // Can also specify the predicate as a string.
+ // Use 'a' as the predicate argument name
+ arr[] = Arr[];
+ r = partition!(q{(a & 1) == 0})(arr);
+ assert(r == arr[5 .. $]);
+
+ // Now for a stable partition:
+ arr[] = Arr[];
+ r = partition!(q{(a & 1) == 0}, SwapStrategy.stable)(arr);
+ // Now arr is [2 4 6 8 10 1 3 5 7 9], and r points to 1
+ assert(arr == [2, 4, 6, 8, 10, 1, 3, 5, 7, 9] && r == arr[5 .. $]);
+
+ // In case the predicate needs to hold its own state, use a delegate:
+ arr[] = Arr[];
+ int x = 3;
+ // Put stuff greater than 3 on the left
+ bool fun(int a) { return a > x; }
+ r = partition!(fun, SwapStrategy.semistable)(arr);
+ // Now arr is [4 5 6 7 8 9 10 2 3 1] and r points to 2
+ assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1] && r == arr[7 .. $]);
+}
+
+@safe unittest
+{
+ import std.algorithm.internal : rndstuff;
+ static bool even(int a) { return (a & 1) == 0; }
+
+ // test with random data
+ auto a = rndstuff!int();
+ partition!even(a);
+ assert(isPartitioned!even(a));
+ auto b = rndstuff!string();
+ partition!`a.length < 5`(b);
+ assert(isPartitioned!`a.length < 5`(b));
+}
+
+// pivotPartition
+/**
+
+Partitions `r` around `pivot` using comparison function `less`, algorithm akin
+to $(LINK2 https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme,
+Hoare partition). Specifically, permutes elements of `r` and returns
+an index $(D k < r.length) such that:
+
+$(UL
+
+$(LI `r[pivot]` is swapped to `r[k]`)
+
+$(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(r[k], e))
+(i.e. `r[k]` is greater than or equal to each element to its left according to
+predicate `less`))
+
+$(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(e,
+r[k])) (i.e. `r[k]` is less than or equal to each element to its right
+according to predicate `less`)))
+
+If `r` contains equivalent elements, multiple permutations of `r` satisfy these
+constraints. In such cases, `pivotPartition` attempts to distribute equivalent
+elements fairly to the left and right of `k` such that `k` stays close to $(D
+r.length / 2).
+
+Params:
+less = The predicate used for comparison, modeled as a
+ $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings,
+ strict weak ordering) (irreflexive, antisymmetric, transitive, and implying a transitive
+ equivalence)
+r = The range being partitioned
+pivot = The index of the pivot for partitioning, must be less than `r.length` or
+`0` is `r.length` is `0`
+
+Returns:
+The new position of the pivot
+
+See_Also:
+$(HTTP jgrcs.info/index.php/jgrcs/article/view/142, Engineering of a Quicksort
+Partitioning Algorithm), D. Abhyankar, Journal of Global Research in Computer
+Science, February 2011. $(HTTPS youtube.com/watch?v=AxnotgLql0k, ACCU 2016
+Keynote), Andrei Alexandrescu.
+*/
+size_t pivotPartition(alias less = "a < b", Range)
+(Range r, size_t pivot)
+if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range)
+{
+ assert(pivot < r.length || r.length == 0 && pivot == 0);
+ if (r.length <= 1) return 0;
+ import std.algorithm.mutation : swapAt, move;
+ alias lt = binaryFun!less;
+
+ // Pivot at the front
+ r.swapAt(pivot, 0);
+
+ // Fork implementation depending on nothrow copy, assignment, and
+ // comparison. If all of these are nothrow, use the specialized
+ // implementation discussed at https://youtube.com/watch?v=AxnotgLql0k.
+ static if (is(typeof(
+ () nothrow { auto x = r.front; x = r.front; return lt(x, x); }
+ )))
+ {
+ auto p = r[0];
+ // Plant the pivot in the end as well as a sentinel
+ size_t lo = 0, hi = r.length - 1;
+ auto save = move(r[hi]);
+ r[hi] = p; // Vacancy is in r[$ - 1] now
+ // Start process
+ for (;;)
+ {
+ // Loop invariant
+ version (unittest)
+ {
+ import std.algorithm.searching : all;
+ assert(r[0 .. lo].all!(x => !lt(p, x)));
+ assert(r[hi + 1 .. r.length].all!(x => !lt(x, p)));
+ }
+ do ++lo; while (lt(r[lo], p));
+ r[hi] = r[lo];
+ // Vacancy is now in r[lo]
+ do --hi; while (lt(p, r[hi]));
+ if (lo >= hi) break;
+ r[lo] = r[hi];
+ // Vacancy is not in r[hi]
+ }
+ // Fixup
+ assert(lo - hi <= 2);
+ assert(!lt(p, r[hi]));
+ if (lo == hi + 2)
+ {
+ assert(!lt(r[hi + 1], p));
+ r[lo] = r[hi + 1];
+ --lo;
+ }
+ r[lo] = save;
+ if (lt(p, save)) --lo;
+ assert(!lt(p, r[lo]));
+ }
+ else
+ {
+ size_t lo = 1, hi = r.length - 1;
+ loop: for (;; lo++, hi--)
+ {
+ for (;; ++lo)
+ {
+ if (lo > hi) break loop;
+ if (!lt(r[lo], r[0])) break;
+ }
+ // found the left bound: r[lo] >= r[0]
+ assert(lo <= hi);
+ for (;; --hi)
+ {
+ if (lo >= hi) break loop;
+ if (!lt(r[0], r[hi])) break;
+ }
+ // found the right bound: r[hi] <= r[0], swap & make progress
+ assert(!lt(r[lo], r[hi]));
+ r.swapAt(lo, hi);
+ }
+ --lo;
+ }
+ r.swapAt(lo, 0);
+ return lo;
+}
+
+///
+@safe nothrow unittest
+{
+ int[] a = [5, 3, 2, 6, 4, 1, 3, 7];
+ size_t pivot = pivotPartition(a, a.length / 2);
+ import std.algorithm.searching : all;
+ assert(a[0 .. pivot].all!(x => x <= a[pivot]));
+ assert(a[pivot .. $].all!(x => x >= a[pivot]));
+}
+
+@safe unittest
+{
+ void test(alias less)()
+ {
+ int[] a;
+ size_t pivot;
+
+ a = [-9, -4, -2, -2, 9];
+ pivot = pivotPartition!less(a, a.length / 2);
+ import std.algorithm.searching : all;
+ assert(a[0 .. pivot].all!(x => x <= a[pivot]));
+ assert(a[pivot .. $].all!(x => x >= a[pivot]));
+
+ a = [9, 2, 8, -5, 5, 4, -8, -4, 9];
+ pivot = pivotPartition!less(a, a.length / 2);
+ assert(a[0 .. pivot].all!(x => x <= a[pivot]));
+ assert(a[pivot .. $].all!(x => x >= a[pivot]));
+
+ a = [ 42 ];
+ pivot = pivotPartition!less(a, a.length / 2);
+ assert(pivot == 0);
+ assert(a == [ 42 ]);
+
+ a = [ 43, 42 ];
+ pivot = pivotPartition!less(a, 0);
+ assert(pivot == 1);
+ assert(a == [ 42, 43 ]);
+
+ a = [ 43, 42 ];
+ pivot = pivotPartition!less(a, 1);
+ assert(pivot == 0);
+ assert(a == [ 42, 43 ]);
+
+ a = [ 42, 42 ];
+ pivot = pivotPartition!less(a, 0);
+ assert(pivot == 0 || pivot == 1);
+ assert(a == [ 42, 42 ]);
+ pivot = pivotPartition!less(a, 1);
+ assert(pivot == 0 || pivot == 1);
+ assert(a == [ 42, 42 ]);
+
+ import std.algorithm.iteration : map;
+ import std.random;
+ import std.stdio;
+ auto s = unpredictableSeed;
+ auto g = Random(s);
+ a = iota(0, uniform(1, 1000, g))
+ .map!(_ => uniform(-1000, 1000, g))
+ .array;
+ scope(failure) writeln("RNG seed was ", s);
+ pivot = pivotPartition!less(a, a.length / 2);
+ assert(a[0 .. pivot].all!(x => x <= a[pivot]));
+ assert(a[pivot .. $].all!(x => x >= a[pivot]));
+ }
+ test!"a < b";
+ static bool myLess(int a, int b)
+ {
+ static bool bogus;
+ if (bogus) throw new Exception(""); // just to make it no-nothrow
+ return a < b;
+ }
+ test!myLess;
+}
+
+/**
+Params:
+ pred = The predicate that the range should be partitioned by.
+ r = The range to check.
+Returns: $(D true) if $(D r) is partitioned according to predicate $(D pred).
+ */
+bool isPartitioned(alias pred, Range)(Range r)
+if (isForwardRange!(Range))
+{
+ for (; !r.empty; r.popFront())
+ {
+ if (unaryFun!(pred)(r.front)) continue;
+ for (r.popFront(); !r.empty; r.popFront())
+ {
+ if (unaryFun!(pred)(r.front)) return false;
+ }
+ break;
+ }
+ return true;
+}
+
+///
+@safe unittest
+{
+ int[] r = [ 1, 3, 5, 7, 8, 2, 4, ];
+ assert(isPartitioned!"a & 1"(r));
+}
+
+// partition3
+/**
+Rearranges elements in $(D r) in three adjacent ranges and returns
+them. The first and leftmost range only contains elements in $(D r)
+less than $(D pivot). The second and middle range only contains
+elements in $(D r) that are equal to $(D pivot). Finally, the third
+and rightmost range only contains elements in $(D r) that are greater
+than $(D pivot). The less-than test is defined by the binary function
+$(D less).
+
+Params:
+ less = The predicate to use for the rearrangement.
+ ss = The swapping strategy to use.
+ r = The random-access range to rearrange.
+ pivot = The pivot element.
+
+Returns:
+ A $(REF Tuple, std,typecons) of the three resulting ranges. These ranges are
+ slices of the original range.
+
+BUGS: stable $(D partition3) has not been implemented yet.
+ */
+auto partition3(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, E)
+(Range r, E pivot)
+if (ss == SwapStrategy.unstable && isRandomAccessRange!Range
+ && hasSwappableElements!Range && hasLength!Range && hasSlicing!Range
+ && is(typeof(binaryFun!less(r.front, pivot)) == bool)
+ && is(typeof(binaryFun!less(pivot, r.front)) == bool)
+ && is(typeof(binaryFun!less(r.front, r.front)) == bool))
+{
+ // The algorithm is described in "Engineering a sort function" by
+ // Jon Bentley et al, pp 1257.
+
+ import std.algorithm.comparison : min;
+ import std.algorithm.mutation : swap, swapAt, swapRanges;
+ import std.typecons : tuple;
+
+ alias lessFun = binaryFun!less;
+ size_t i, j, k = r.length, l = k;
+
+ bigloop:
+ for (;;)
+ {
+ for (;; ++j)
+ {
+ if (j == k) break bigloop;
+ assert(j < r.length);
+ if (lessFun(r[j], pivot)) continue;
+ if (lessFun(pivot, r[j])) break;
+ r.swapAt(i++, j);
+ }
+ assert(j < k);
+ for (;;)
+ {
+ assert(k > 0);
+ if (!lessFun(pivot, r[--k]))
+ {
+ if (lessFun(r[k], pivot)) break;
+ r.swapAt(k, --l);
+ }
+ if (j == k) break bigloop;
+ }
+ // Here we know r[j] > pivot && r[k] < pivot
+ r.swapAt(j++, k);
+ }
+
+ // Swap the equal ranges from the extremes into the middle
+ auto strictlyLess = j - i, strictlyGreater = l - k;
+ auto swapLen = min(i, strictlyLess);
+ swapRanges(r[0 .. swapLen], r[j - swapLen .. j]);
+ swapLen = min(r.length - l, strictlyGreater);
+ swapRanges(r[k .. k + swapLen], r[r.length - swapLen .. r.length]);
+ return tuple(r[0 .. strictlyLess],
+ r[strictlyLess .. r.length - strictlyGreater],
+ r[r.length - strictlyGreater .. r.length]);
+}
+
+///
+@safe unittest
+{
+ auto a = [ 8, 3, 4, 1, 4, 7, 4 ];
+ auto pieces = partition3(a, 4);
+ assert(pieces[0] == [ 1, 3 ]);
+ assert(pieces[1] == [ 4, 4, 4 ]);
+ assert(pieces[2] == [ 8, 7 ]);
+}
+
+@safe unittest
+{
+ import std.random : Random, uniform, unpredictableSeed;
+
+ immutable uint[] seeds = [3923355730, 1927035882, unpredictableSeed];
+ foreach (s; seeds)
+ {
+ auto r = Random(s);
+ auto a = new int[](uniform(0, 100, r));
+ foreach (ref e; a)
+ {
+ e = uniform(0, 50, r);
+ }
+ auto pieces = partition3(a, 25);
+ assert(pieces[0].length + pieces[1].length + pieces[2].length == a.length);
+ foreach (e; pieces[0])
+ {
+ assert(e < 25);
+ }
+ foreach (e; pieces[1])
+ {
+ assert(e == 25);
+ }
+ foreach (e; pieces[2])
+ {
+ assert(e > 25);
+ }
+ }
+}
+
+// makeIndex
+/**
+Computes an index for $(D r) based on the comparison $(D less). The
+index is a sorted array of pointers or indices into the original
+range. This technique is similar to sorting, but it is more flexible
+because (1) it allows "sorting" of immutable collections, (2) allows
+binary search even if the original collection does not offer random
+access, (3) allows multiple indexes, each on a different predicate,
+and (4) may be faster when dealing with large objects. However, using
+an index may also be slower under certain circumstances due to the
+extra indirection, and is always larger than a sorting-based solution
+because it needs space for the index in addition to the original
+collection. The complexity is the same as $(D sort)'s.
+
+The first overload of $(D makeIndex) writes to a range containing
+pointers, and the second writes to a range containing offsets. The
+first overload requires $(D Range) to be a
+$(REF_ALTTEXT forward range, isForwardRange, std,range,primitives), and the
+latter requires it to be a random-access range.
+
+$(D makeIndex) overwrites its second argument with the result, but
+never reallocates it.
+
+Params:
+ less = The comparison to use.
+ ss = The swapping strategy.
+ r = The range to index.
+ index = The resulting index.
+
+Returns: The pointer-based version returns a $(D SortedRange) wrapper
+over index, of type $(D SortedRange!(RangeIndex, (a, b) =>
+binaryFun!less(*a, *b))) thus reflecting the ordering of the
+index. The index-based version returns $(D void) because the ordering
+relation involves not only $(D index) but also $(D r).
+
+Throws: If the second argument's length is less than that of the range
+indexed, an exception is thrown.
+*/
+SortedRange!(RangeIndex, (a, b) => binaryFun!less(*a, *b))
+makeIndex(
+ alias less = "a < b",
+ SwapStrategy ss = SwapStrategy.unstable,
+ Range,
+ RangeIndex)
+(Range r, RangeIndex index)
+if (isForwardRange!(Range) && isRandomAccessRange!(RangeIndex)
+ && is(ElementType!(RangeIndex) : ElementType!(Range)*))
+{
+ import std.algorithm.internal : addressOf;
+ import std.exception : enforce;
+
+ // assume collection already ordered
+ size_t i;
+ for (; !r.empty; r.popFront(), ++i)
+ index[i] = addressOf(r.front);
+ enforce(index.length == i);
+ // sort the index
+ sort!((a, b) => binaryFun!less(*a, *b), ss)(index);
+ return typeof(return)(index);
+}
+
+/// Ditto
+void makeIndex(
+ alias less = "a < b",
+ SwapStrategy ss = SwapStrategy.unstable,
+ Range,
+ RangeIndex)
+(Range r, RangeIndex index)
+if (isRandomAccessRange!Range && !isInfinite!Range &&
+ isRandomAccessRange!RangeIndex && !isInfinite!RangeIndex &&
+ isIntegral!(ElementType!RangeIndex))
+{
+ import std.conv : to;
+ import std.exception : enforce;
+
+ alias IndexType = Unqual!(ElementType!RangeIndex);
+ enforce(r.length == index.length,
+ "r and index must be same length for makeIndex.");
+ static if (IndexType.sizeof < size_t.sizeof)
+ {
+ enforce(r.length <= size_t(1) + IndexType.max, "Cannot create an index with " ~
+ "element type " ~ IndexType.stringof ~ " with length " ~
+ to!string(r.length) ~ ".");
+ }
+
+ // Use size_t as loop index to avoid overflow on ++i,
+ // e.g. when squeezing 256 elements into a ubyte index.
+ foreach (size_t i; 0 .. r.length)
+ index[i] = cast(IndexType) i;
+
+ // sort the index
+ sort!((a, b) => binaryFun!less(r[cast(size_t) a], r[cast(size_t) b]), ss)
+ (index);
+}
+
+///
+@system unittest
+{
+ immutable(int[]) arr = [ 2, 3, 1, 5, 0 ];
+ // index using pointers
+ auto index1 = new immutable(int)*[arr.length];
+ makeIndex!("a < b")(arr, index1);
+ assert(isSorted!("*a < *b")(index1));
+ // index using offsets
+ auto index2 = new size_t[arr.length];
+ makeIndex!("a < b")(arr, index2);
+ assert(isSorted!
+ ((size_t a, size_t b){ return arr[a] < arr[b];})
+ (index2));
+}
+
+@system unittest
+{
+ immutable(int)[] arr = [ 2, 3, 1, 5, 0 ];
+ // index using pointers
+ auto index1 = new immutable(int)*[arr.length];
+ alias ImmRange = typeof(arr);
+ alias ImmIndex = typeof(index1);
+ static assert(isForwardRange!(ImmRange));
+ static assert(isRandomAccessRange!(ImmIndex));
+ static assert(!isIntegral!(ElementType!(ImmIndex)));
+ static assert(is(ElementType!(ImmIndex) : ElementType!(ImmRange)*));
+ makeIndex!("a < b")(arr, index1);
+ assert(isSorted!("*a < *b")(index1));
+
+ // index using offsets
+ auto index2 = new long[arr.length];
+ makeIndex(arr, index2);
+ assert(isSorted!
+ ((long a, long b){
+ return arr[cast(size_t) a] < arr[cast(size_t) b];
+ })(index2));
+
+ // index strings using offsets
+ string[] arr1 = ["I", "have", "no", "chocolate"];
+ auto index3 = new byte[arr1.length];
+ makeIndex(arr1, index3);
+ assert(isSorted!
+ ((byte a, byte b){ return arr1[a] < arr1[b];})
+ (index3));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ ubyte[256] index = void;
+ iota(256).makeIndex(index[]);
+ assert(index[].equal(iota(256)));
+ byte[128] sindex = void;
+ iota(128).makeIndex(sindex[]);
+ assert(sindex[].equal(iota(128)));
+
+ auto index2 = new uint[10];
+ 10.iota.makeIndex(index2);
+ assert(index2.equal(10.iota));
+}
+
+struct Merge(alias less = "a < b", Rs...)
+if (Rs.length >= 2 &&
+ allSatisfy!(isInputRange, Rs) &&
+ !is(CommonType!(staticMap!(ElementType, Rs)) == void))
+{
+ public Rs source;
+ private size_t _lastFrontIndex = size_t.max;
+ static if (isBidirectional)
+ {
+ private size_t _lastBackIndex = size_t.max; // `size_t.max` means uninitialized,
+ }
+
+ import std.functional : binaryFun;
+ import std.traits : isCopyable;
+ import std.typetuple : anySatisfy;
+
+ private alias comp = binaryFun!less;
+ private alias ElementType = CommonType!(staticMap!(.ElementType, Rs));
+ private enum isBidirectional = allSatisfy!(isBidirectionalRange, staticMap!(Unqual, Rs));
+
+ debug private enum canCheckSortedness = isCopyable!ElementType && !hasAliasing!ElementType;
+
+ this(Rs source)
+ {
+ this.source = source;
+ this._lastFrontIndex = frontIndex;
+ }
+
+ static if (anySatisfy!(isInfinite, Rs))
+ {
+ enum bool empty = false; // propagate infiniteness
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _lastFrontIndex == size_t.max;
+ }
+ }
+
+ @property auto ref front()
+ {
+ final switch (_lastFrontIndex)
+ {
+ foreach (i, _; Rs)
+ {
+ case i:
+ assert(!source[i].empty);
+ return source[i].front;
+ }
+ }
+ }
+
+ private size_t frontIndex()
+ {
+ size_t bestIndex = size_t.max; // indicate undefined
+ Unqual!ElementType bestElement;
+ foreach (i, _; Rs)
+ {
+ if (source[i].empty) continue;
+ if (bestIndex == size_t.max || // either this is the first or
+ comp(source[i].front, bestElement))
+ {
+ bestIndex = i;
+ bestElement = source[i].front;
+ }
+ }
+ return bestIndex;
+ }
+
+ void popFront()
+ {
+ sw: final switch (_lastFrontIndex)
+ {
+ foreach (i, R; Rs)
+ {
+ case i:
+ debug static if (canCheckSortedness)
+ {
+ ElementType previousFront = source[i].front();
+ }
+ source[i].popFront();
+ debug static if (canCheckSortedness)
+ {
+ if (!source[i].empty)
+ {
+ assert(previousFront == source[i].front ||
+ comp(previousFront, source[i].front),
+ "Input " ~ i.stringof ~ " is unsorted"); // @nogc
+ }
+ }
+ break sw;
+ }
+ }
+ _lastFrontIndex = frontIndex;
+ }
+
+ static if (isBidirectional)
+ {
+ @property auto ref back()
+ {
+ if (_lastBackIndex == size_t.max)
+ {
+ this._lastBackIndex = backIndex; // lazy initialization
+ }
+ final switch (_lastBackIndex)
+ {
+ foreach (i, _; Rs)
+ {
+ case i:
+ assert(!source[i].empty);
+ return source[i].back;
+ }
+ }
+ }
+
+ private size_t backIndex()
+ {
+ size_t bestIndex = size_t.max; // indicate undefined
+ Unqual!ElementType bestElement;
+ foreach (i, _; Rs)
+ {
+ if (source[i].empty) continue;
+ if (bestIndex == size_t.max || // either this is the first or
+ comp(bestElement, source[i].back))
+ {
+ bestIndex = i;
+ bestElement = source[i].back;
+ }
+ }
+ return bestIndex;
+ }
+
+ void popBack()
+ {
+ if (_lastBackIndex == size_t.max)
+ {
+ this._lastBackIndex = backIndex; // lazy initialization
+ }
+ sw: final switch (_lastBackIndex)
+ {
+ foreach (i, R; Rs)
+ {
+ case i:
+ debug static if (canCheckSortedness)
+ {
+ ElementType previousBack = source[i].back();
+ }
+ source[i].popBack();
+ debug static if (canCheckSortedness)
+ {
+ if (!source[i].empty)
+ {
+ assert(previousBack == source[i].back ||
+ comp(source[i].back, previousBack),
+ "Input " ~ i.stringof ~ " is unsorted"); // @nogc
+ }
+ }
+ break sw;
+ }
+ }
+ _lastBackIndex = backIndex;
+ if (_lastBackIndex == size_t.max) // if emptied
+ {
+ _lastFrontIndex = size_t.max;
+ }
+ }
+ }
+
+ static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs)))
+ {
+ @property auto save()
+ {
+ auto result = this;
+ foreach (i, _; Rs)
+ {
+ result.source[i] = result.source[i].save;
+ }
+ return result;
+ }
+ }
+
+ static if (allSatisfy!(hasLength, Rs))
+ {
+ @property size_t length()
+ {
+ size_t result;
+ foreach (i, _; Rs)
+ {
+ result += source[i].length;
+ }
+ return result;
+ }
+
+ alias opDollar = length;
+ }
+}
+
+/**
+ Merge multiple sorted ranges `rs` with less-than predicate function `pred`
+ into one single sorted output range containing the sorted union of the
+ elements of inputs. Duplicates are not eliminated, meaning that the total
+ number of elements in the output is the sum of all elements in the ranges
+ passed to it; the `length` member is offered if all inputs also have
+ `length`. The element types of all the inputs must have a common type
+ `CommonType`.
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ rs = The ranges to compute the union for.
+
+Returns:
+ A range containing the union of the given ranges.
+
+Details:
+
+All of its inputs are assumed to be sorted. This can mean that inputs are
+ instances of $(REF SortedRange, std,range). Use the result of $(REF sort,
+ std,algorithm,sorting), or $(REF assumeSorted, std,range) to merge ranges
+ known to be sorted (show in the example below). Note that there is currently
+ no way of ensuring that two or more instances of $(REF SortedRange,
+ std,range) are sorted using a specific comparison function `pred`. Therefore
+ no checking is done here to assure that all inputs `rs` are instances of
+ $(REF SortedRange, std,range).
+
+ This algorithm is lazy, doing work progressively as elements are pulled off
+ the result.
+
+ Time complexity is proportional to the sum of element counts over all inputs.
+
+ If all inputs have the same element type and offer it by `ref`, output
+ becomes a range with mutable `front` (and `back` where appropriate) that
+ reflects in the original inputs.
+
+ If any of the inputs `rs` is infinite so is the result (`empty` being always
+ `false`).
+*/
+Merge!(less, Rs) merge(alias less = "a < b", Rs...)(Rs rs)
+if (Rs.length >= 2 &&
+ allSatisfy!(isInputRange, Rs) &&
+ !is(CommonType!(staticMap!(ElementType, Rs)) == void))
+{
+ return typeof(return)(rs);
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+
+ int[] a = [1, 3, 5];
+ int[] b = [2, 3, 4];
+
+ assert(a.merge(b).equal([1, 2, 3, 3, 4, 5]));
+ assert(a.merge(b).retro.equal([5, 4, 3, 3, 2, 1]));
+}
+
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ double[] c = [ 10.5 ];
+
+ assert(merge(a, b).length == a.length + b.length);
+ assert(equal(merge(a, b), [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][]));
+ assert(equal(merge(a, c, b),
+ [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9, 10.5][]));
+ auto u = merge(a, b);
+ u.front--;
+ assert(equal(u, [-1, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][]));
+}
+
+@safe pure nothrow unittest
+{
+ // save
+ import std.range : dropOne;
+ int[] a = [1, 2];
+ int[] b = [0, 3];
+ auto arr = a.merge(b);
+ assert(arr.front == 0);
+ assert(arr.save.dropOne.front == 1);
+ assert(arr.front == 0);
+}
+
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+
+ auto dummyResult1 = [1, 1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10];
+ auto dummyResult2 = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
+ 6, 6, 7, 7, 8, 8, 9, 9, 10, 10];
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ assert(d.merge([1, 1.5, 5.5]).equal(dummyResult1));
+ assert(d.merge(d).equal(dummyResult2));
+ }
+}
+
+@nogc @safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ static immutable a = [1, 3, 5];
+ static immutable b = [2, 3, 4];
+ static immutable r = [1, 2, 3, 3, 4, 5];
+ assert(a.merge(b).equal(r));
+}
+
+/// test bi-directional access and common type
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+ import std.traits : CommonType;
+
+ alias S = short;
+ alias I = int;
+ alias D = double;
+
+ S[] a = [1, 2, 3];
+ I[] b = [50, 60];
+ D[] c = [10, 20, 30, 40];
+
+ auto m = merge(a, b, c);
+
+ static assert(is(typeof(m.front) == CommonType!(S, I, D)));
+
+ assert(equal(m, [1, 2, 3, 10, 20, 30, 40, 50, 60]));
+ assert(equal(m.retro, [60, 50, 40, 30, 20, 10, 3, 2, 1]));
+
+ m.popFront();
+ assert(equal(m, [2, 3, 10, 20, 30, 40, 50, 60]));
+ m.popBack();
+ assert(equal(m, [2, 3, 10, 20, 30, 40, 50]));
+ m.popFront();
+ assert(equal(m, [3, 10, 20, 30, 40, 50]));
+ m.popBack();
+ assert(equal(m, [3, 10, 20, 30, 40]));
+ m.popFront();
+ assert(equal(m, [10, 20, 30, 40]));
+ m.popBack();
+ assert(equal(m, [10, 20, 30]));
+ m.popFront();
+ assert(equal(m, [20, 30]));
+ m.popBack();
+ assert(equal(m, [20]));
+ m.popFront();
+ assert(m.empty);
+}
+
+private template validPredicates(E, less...)
+{
+ static if (less.length == 0)
+ enum validPredicates = true;
+ else static if (less.length == 1 && is(typeof(less[0]) == SwapStrategy))
+ enum validPredicates = true;
+ else
+ enum validPredicates =
+ is(typeof((E a, E b){ bool r = binaryFun!(less[0])(a, b); }))
+ && validPredicates!(E, less[1 .. $]);
+}
+
+/**
+$(D auto multiSort(Range)(Range r)
+ if (validPredicates!(ElementType!Range, less));)
+
+Sorts a range by multiple keys. The call $(D multiSort!("a.id < b.id",
+"a.date > b.date")(r)) sorts the range $(D r) by $(D id) ascending,
+and sorts elements that have the same $(D id) by $(D date)
+descending. Such a call is equivalent to $(D sort!"a.id != b.id ? a.id
+< b.id : a.date > b.date"(r)), but $(D multiSort) is faster because it
+does fewer comparisons (in addition to being more convenient).
+
+Returns:
+ The initial range wrapped as a $(D SortedRange) with its predicates
+ converted to an equivalent single predicate.
+ */
+template multiSort(less...) //if (less.length > 1)
+{
+ auto multiSort(Range)(Range r)
+ if (validPredicates!(ElementType!Range, less))
+ {
+ import std.meta : AliasSeq;
+ import std.range : assumeSorted;
+ static if (is(typeof(less[$ - 1]) == SwapStrategy))
+ {
+ enum ss = less[$ - 1];
+ alias funs = less[0 .. $ - 1];
+ }
+ else
+ {
+ enum ss = SwapStrategy.unstable;
+ alias funs = less;
+ }
+
+ static if (funs.length == 0)
+ static assert(false, "No sorting predicate provided for multiSort");
+ else
+ static if (funs.length == 1)
+ return sort!(funs[0], ss, Range)(r);
+ else
+ {
+ multiSortImpl!(Range, ss, funs)(r);
+ return assumeSorted!(multiSortPredFun!(Range, funs))(r);
+ }
+ }
+}
+
+private bool multiSortPredFun(Range, funs...)(ElementType!Range a, ElementType!Range b)
+{
+ foreach (f; funs)
+ {
+ alias lessFun = binaryFun!(f);
+ if (lessFun(a, b)) return true;
+ if (lessFun(b, a)) return false;
+ }
+ return false;
+}
+
+private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r)
+{
+ alias lessFun = binaryFun!(funs[0]);
+
+ static if (funs.length > 1)
+ {
+ while (r.length > 1)
+ {
+ auto p = getPivot!lessFun(r);
+ auto t = partition3!(funs[0], ss)(r, r[p]);
+ if (t[0].length <= t[2].length)
+ {
+ multiSortImpl!(Range, ss, funs)(t[0]);
+ multiSortImpl!(Range, ss, funs[1 .. $])(t[1]);
+ r = t[2];
+ }
+ else
+ {
+ multiSortImpl!(Range, ss, funs[1 .. $])(t[1]);
+ multiSortImpl!(Range, ss, funs)(t[2]);
+ r = t[0];
+ }
+ }
+ }
+ else
+ {
+ sort!(lessFun, ss)(r);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.mutation : SwapStrategy;
+ static struct Point { int x, y; }
+ auto pts1 = [ Point(0, 0), Point(5, 5), Point(0, 1), Point(0, 2) ];
+ auto pts2 = [ Point(0, 0), Point(0, 1), Point(0, 2), Point(5, 5) ];
+ multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1);
+ assert(pts1 == pts2);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+
+ static struct Point { int x, y; }
+ auto pts1 = [ Point(5, 6), Point(1, 0), Point(5, 7), Point(1, 1), Point(1, 2), Point(0, 1) ];
+ auto pts2 = [ Point(0, 1), Point(1, 0), Point(1, 1), Point(1, 2), Point(5, 6), Point(5, 7) ];
+ static assert(validPredicates!(Point, "a.x < b.x", "a.y < b.y"));
+ multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1);
+ assert(pts1 == pts2);
+
+ auto pts3 = indexed(pts1, iota(pts1.length));
+ assert(pts3.multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable).release.equal(pts2));
+
+ auto pts4 = iota(10).array;
+ assert(pts4.multiSort!("a > b").release.equal(iota(10).retro));
+}
+
+@safe unittest //issue 9160 (L-value only comparators)
+{
+ static struct A
+ {
+ int x;
+ int y;
+ }
+
+ static bool byX(const ref A lhs, const ref A rhs)
+ {
+ return lhs.x < rhs.x;
+ }
+
+ static bool byY(const ref A lhs, const ref A rhs)
+ {
+ return lhs.y < rhs.y;
+ }
+
+ auto points = [ A(4, 1), A(2, 4)];
+ multiSort!(byX, byY)(points);
+ assert(points[0] == A(2, 4));
+ assert(points[1] == A(4, 1));
+}
+
+@safe unittest // issue 16179 (cannot access frame of function)
+{
+ auto arr = [[1, 2], [2, 0], [1, 0], [1, 1]];
+ int c = 3;
+
+ arr.multiSort!(
+ (a, b) => a[0] < b[0],
+ (a, b) => c*a[1] < c*b[1]
+ );
+ assert(arr == [[1, 0], [1, 1], [1, 2], [2, 0]]);
+}
+
+@safe unittest //Issue 16413 - @system comparison function
+{
+ bool lt(int a, int b) { return a < b; } static @system
+ auto a = [2, 1];
+ a.multiSort!(lt, lt);
+ assert(a == [1, 2]);
+}
+
+private size_t getPivot(alias less, Range)(Range r)
+{
+ auto mid = r.length / 2;
+ if (r.length < 512)
+ {
+ if (r.length >= 32)
+ medianOf!less(r, size_t(0), mid, r.length - 1);
+ return mid;
+ }
+
+ // The plan here is to take the median of five by taking five elements in
+ // the array, segregate around their median, and return the position of the
+ // third. We choose first, mid, last, and two more in between those.
+
+ auto quarter = r.length / 4;
+ medianOf!less(r,
+ size_t(0), mid - quarter, mid, mid + quarter, r.length - 1);
+ return mid;
+}
+
+/*
+Sorting routine that is optimized for short ranges. Note: uses insertion sort
+going downward. Benchmarked a similar routine that goes upward, for some reason
+it's slower.
+*/
+private void shortSort(alias less, Range)(Range r)
+{
+ import std.algorithm.mutation : swapAt;
+ alias pred = binaryFun!(less);
+
+ switch (r.length)
+ {
+ case 0: case 1:
+ return;
+ case 2:
+ if (pred(r[1], r[0])) r.swapAt(0, 1);
+ return;
+ case 3:
+ if (pred(r[2], r[0]))
+ {
+ if (pred(r[0], r[1]))
+ {
+ r.swapAt(0, 1);
+ r.swapAt(0, 2);
+ }
+ else
+ {
+ r.swapAt(0, 2);
+ if (pred(r[1], r[0])) r.swapAt(0, 1);
+ }
+ }
+ else
+ {
+ if (pred(r[1], r[0]))
+ {
+ r.swapAt(0, 1);
+ }
+ else
+ {
+ if (pred(r[2], r[1])) r.swapAt(1, 2);
+ }
+ }
+ return;
+ case 4:
+ if (pred(r[1], r[0])) r.swapAt(0, 1);
+ if (pred(r[3], r[2])) r.swapAt(2, 3);
+ if (pred(r[2], r[0])) r.swapAt(0, 2);
+ if (pred(r[3], r[1])) r.swapAt(1, 3);
+ if (pred(r[2], r[1])) r.swapAt(1, 2);
+ return;
+ default:
+ sort5!pred(r[r.length - 5 .. r.length]);
+ if (r.length == 5) return;
+ break;
+ }
+
+ assert(r.length >= 6);
+ /* The last 5 elements of the range are sorted. Proceed with expanding the
+ sorted portion downward. */
+ immutable maxJ = r.length - 2;
+ for (size_t i = r.length - 6; ; --i)
+ {
+ static if (is(typeof(() nothrow
+ {
+ auto t = r[0]; if (pred(t, r[0])) r[0] = r[0];
+ }))) // Can we afford to temporarily invalidate the array?
+ {
+ size_t j = i + 1;
+ auto temp = r[i];
+ if (pred(r[j], temp))
+ {
+ do
+ {
+ r[j - 1] = r[j];
+ ++j;
+ }
+ while (j < r.length && pred(r[j], temp));
+ r[j - 1] = temp;
+ }
+ }
+ else
+ {
+ size_t j = i;
+ while (pred(r[j + 1], r[j]))
+ {
+ r.swapAt(j, j + 1);
+ if (j == maxJ) break;
+ ++j;
+ }
+ }
+ if (i == 0) break;
+ }
+}
+
+@safe unittest
+{
+ import std.random : Random, uniform;
+
+ auto rnd = Random(1);
+ auto a = new int[uniform(100, 200, rnd)];
+ foreach (ref e; a)
+ {
+ e = uniform(-100, 100, rnd);
+ }
+
+ shortSort!(binaryFun!("a < b"), int[])(a);
+ assert(isSorted(a));
+}
+
+/*
+Sorts the first 5 elements exactly of range r.
+*/
+private void sort5(alias lt, Range)(Range r)
+{
+ assert(r.length >= 5);
+
+ import std.algorithm.mutation : swapAt;
+
+ // 1. Sort first two pairs
+ if (lt(r[1], r[0])) r.swapAt(0, 1);
+ if (lt(r[3], r[2])) r.swapAt(2, 3);
+
+ // 2. Arrange first two pairs by the largest element
+ if (lt(r[3], r[1]))
+ {
+ r.swapAt(0, 2);
+ r.swapAt(1, 3);
+ }
+ assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[3], r[2]));
+
+ // 3. Insert 4 into [0, 1, 3]
+ if (lt(r[4], r[1]))
+ {
+ r.swapAt(3, 4);
+ r.swapAt(1, 3);
+ if (lt(r[1], r[0]))
+ {
+ r.swapAt(0, 1);
+ }
+ }
+ else if (lt(r[4], r[3]))
+ {
+ r.swapAt(3, 4);
+ }
+ assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[4], r[3]));
+
+ // 4. Insert 2 into [0, 1, 3, 4] (note: we already know the last is greater)
+ assert(!lt(r[4], r[2]));
+ if (lt(r[2], r[1]))
+ {
+ r.swapAt(1, 2);
+ if (lt(r[1], r[0]))
+ {
+ r.swapAt(0, 1);
+ }
+ }
+ else if (lt(r[3], r[2]))
+ {
+ r.swapAt(2, 3);
+ }
+ // 7 comparisons, 0-9 swaps
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : permutations;
+ import std.algorithm.mutation : copy;
+
+ int[5] buf;
+ foreach (per; iota(5).permutations)
+ {
+ per.copy(buf[]);
+ sort5!((a, b) => a < b)(buf[]);
+ assert(buf[].isSorted);
+ }
+}
+
+// sort
+/**
+Sorts a random-access range according to the predicate $(D less). Performs
+$(BIGOH r.length * log(r.length)) evaluations of $(D less). If `less` involves
+expensive computations on the _sort key, it may be worthwhile to use
+$(LREF schwartzSort) instead.
+
+Stable sorting requires $(D hasAssignableElements!Range) to be true.
+
+$(D sort) returns a $(REF SortedRange, std,range) over the original range,
+allowing functions that can take advantage of sorted data to know that the
+range is sorted and adjust accordingly. The $(REF SortedRange, std,range) is a
+wrapper around the original range, so both it and the original range are sorted.
+Other functions can't know that the original range has been sorted, but
+they $(I can) know that $(REF SortedRange, std,range) has been sorted.
+
+Preconditions:
+
+The predicate is expected to satisfy certain rules in order for $(D sort) to
+behave as expected - otherwise, the program may fail on certain inputs (but not
+others) when not compiled in release mode, due to the cursory $(D assumeSorted)
+check. Specifically, $(D sort) expects $(D less(a,b) && less(b,c)) to imply
+$(D less(a,c)) (transitivity), and, conversely, $(D !less(a,b) && !less(b,c)) to
+imply $(D !less(a,c)). Note that the default predicate ($(D "a < b")) does not
+always satisfy these conditions for floating point types, because the expression
+will always be $(D false) when either $(D a) or $(D b) is NaN.
+Use $(REF cmp, std,math) instead.
+
+Params:
+ less = The predicate to sort by.
+ ss = The swapping strategy to use.
+ r = The range to sort.
+
+Returns: The initial range wrapped as a $(D SortedRange) with the predicate
+$(D binaryFun!less).
+
+Algorithms: $(HTTP en.wikipedia.org/wiki/Introsort, Introsort) is used for unstable sorting and
+$(HTTP en.wikipedia.org/wiki/Timsort, Timsort) is used for stable sorting.
+Each algorithm has benefits beyond stability. Introsort is generally faster but
+Timsort may achieve greater speeds on data with low entropy or if predicate calls
+are expensive. Introsort performs no allocations whereas Timsort will perform one
+or more allocations per call. Both algorithms have $(BIGOH n log n) worst-case
+time complexity.
+
+See_Also:
+ $(REF assumeSorted, std,range)$(BR)
+ $(REF SortedRange, std,range)$(BR)
+ $(REF SwapStrategy, std,algorithm,mutation)$(BR)
+ $(REF binaryFun, std,functional)
+*/
+SortedRange!(Range, less)
+sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
+ Range)(Range r)
+if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range ||
+ hasAssignableElements!Range)) ||
+ (ss != SwapStrategy.unstable && hasAssignableElements!Range)) &&
+ isRandomAccessRange!Range &&
+ hasSlicing!Range &&
+ hasLength!Range)
+ /+ Unstable sorting uses the quicksort algorithm, which uses swapAt,
+ which either uses swap(...), requiring swappable elements, or just
+ swaps using assignment.
+ Stable sorting uses TimSort, which needs to copy elements into a buffer,
+ requiring assignable elements. +/
+{
+ import std.range : assumeSorted;
+ alias lessFun = binaryFun!(less);
+ alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun
+ static if (is(LessRet == bool))
+ {
+ static if (ss == SwapStrategy.unstable)
+ quickSortImpl!(lessFun)(r, r.length);
+ else //use Tim Sort for semistable & stable
+ TimSortImpl!(lessFun, Range).sort(r, null);
+
+ assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof);
+ }
+ else
+ {
+ static assert(false, "Invalid predicate passed to sort: " ~ less.stringof);
+ }
+ return assumeSorted!less(r);
+}
+
+///
+@safe pure nothrow unittest
+{
+ int[] array = [ 1, 2, 3, 4 ];
+
+ // sort in descending order
+ array.sort!("a > b");
+ assert(array == [ 4, 3, 2, 1 ]);
+
+ // sort in ascending order
+ array.sort();
+ assert(array == [ 1, 2, 3, 4 ]);
+
+ // sort with reusable comparator and chain
+ alias myComp = (x, y) => x > y;
+ assert(array.sort!(myComp).release == [ 4, 3, 2, 1 ]);
+}
+
+///
+@safe unittest
+{
+ // Showcase stable sorting
+ import std.algorithm.mutation : SwapStrategy;
+ string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ];
+ sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable)(words);
+ assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);
+}
+
+///
+@safe unittest
+{
+ // Sorting floating-point numbers in presence of NaN
+ double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan];
+
+ import std.algorithm.comparison : equal;
+ import std.math : cmp, isIdentical;
+
+ sort!((a, b) => cmp(a, b) < 0)(numbers);
+
+ double[] sorted = [-double.nan, -2.0, -0.0, 0.0, 3.0, double.nan];
+ assert(numbers.equal!isIdentical(sorted));
+}
+
+@safe unittest
+{
+ // Simple regression benchmark
+ import std.algorithm.iteration, std.algorithm.mutation, std.random;
+ Random rng;
+ int[] a = iota(20148).map!(_ => uniform(-1000, 1000, rng)).array;
+ static uint comps;
+ static bool less(int a, int b) { ++comps; return a < b; }
+ sort!less(a); // random numbers
+ sort!less(a); // sorted ascending
+ a.reverse();
+ sort!less(a); // sorted descending
+ a[] = 0;
+ sort!less(a); // all equal
+
+ // This should get smaller with time. On occasion it may go larger, but only
+ // if there's thorough justification.
+ debug enum uint watermark = 1676280;
+ else enum uint watermark = 1676220;
+
+ import std.conv;
+ assert(comps <= watermark, text("You seem to have pessimized sort! ",
+ watermark, " < ", comps));
+ assert(comps >= watermark, text("You seem to have improved sort!",
+ " Please update watermark from ", watermark, " to ", comps));
+}
+
+@safe unittest
+{
+ import std.algorithm.internal : rndstuff;
+ import std.algorithm.mutation : swapRanges;
+ import std.random : Random, unpredictableSeed, uniform;
+ import std.uni : toUpper;
+
+ // sort using delegate
+ auto a = new int[100];
+ auto rnd = Random(unpredictableSeed);
+ foreach (ref e; a)
+ {
+ e = uniform(-100, 100, rnd);
+ }
+
+ int i = 0;
+ bool greater2(int a, int b) @safe { return a + i > b + i; }
+ auto greater = &greater2;
+ sort!(greater)(a);
+ assert(isSorted!(greater)(a));
+
+ // sort using string
+ sort!("a < b")(a);
+ assert(isSorted!("a < b")(a));
+
+ // sort using function; all elements equal
+ foreach (ref e; a)
+ {
+ e = 5;
+ }
+ static bool less(int a, int b) { return a < b; }
+ sort!(less)(a);
+ assert(isSorted!(less)(a));
+
+ string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ];
+ bool lessi(string a, string b) { return toUpper(a) < toUpper(b); }
+ sort!(lessi, SwapStrategy.stable)(words);
+ assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);
+
+ // sort using ternary predicate
+ //sort!("b - a")(a);
+ //assert(isSorted!(less)(a));
+
+ a = rndstuff!(int)();
+ sort(a);
+ assert(isSorted(a));
+ auto b = rndstuff!(string)();
+ sort!("toLower(a) < toLower(b)")(b);
+ assert(isSorted!("toUpper(a) < toUpper(b)")(b));
+
+ {
+ // Issue 10317
+ enum E_10317 { a, b }
+ auto a_10317 = new E_10317[10];
+ sort(a_10317);
+ }
+
+ {
+ // Issue 7767
+ // Unstable sort should complete without an excessive number of predicate calls
+ // This would suggest it's running in quadratic time
+
+ // Compilation error if predicate is not static, i.e. a nested function
+ static uint comp;
+ static bool pred(size_t a, size_t b)
+ {
+ ++comp;
+ return a < b;
+ }
+
+ size_t[] arr;
+ arr.length = 1024;
+
+ foreach (k; 0 .. arr.length) arr[k] = k;
+ swapRanges(arr[0..$/2], arr[$/2..$]);
+
+ sort!(pred, SwapStrategy.unstable)(arr);
+ assert(comp < 25_000);
+ }
+
+ {
+ import std.algorithm.mutation : swap;
+
+ bool proxySwapCalled;
+ struct S
+ {
+ int i;
+ alias i this;
+ void proxySwap(ref S other) { swap(i, other.i); proxySwapCalled = true; }
+ @disable void opAssign(S value);
+ }
+
+ alias R = S[];
+ R r = [S(3), S(2), S(1)];
+ static assert(hasSwappableElements!R);
+ static assert(!hasAssignableElements!R);
+ r.sort();
+ assert(proxySwapCalled);
+ }
+}
+
+private void quickSortImpl(alias less, Range)(Range r, size_t depth)
+{
+ import std.algorithm.comparison : min, max;
+ import std.algorithm.mutation : swap, swapAt;
+
+ alias Elem = ElementType!(Range);
+ enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof);
+ static assert(shortSortGetsBetter >= 1);
+
+ // partition
+ while (r.length > shortSortGetsBetter)
+ {
+ if (depth == 0)
+ {
+ HeapOps!(less, Range).heapSort(r);
+ return;
+ }
+ depth = depth >= depth.max / 2 ? (depth / 3) * 2 : (depth * 2) / 3;
+
+ const pivotIdx = getPivot!(less)(r);
+ auto pivot = r[pivotIdx];
+
+ // partition
+ r.swapAt(pivotIdx, r.length - 1);
+ size_t lessI = size_t.max, greaterI = r.length - 1;
+
+ outer: for (;;)
+ {
+ alias pred = binaryFun!less;
+ while (pred(r[++lessI], pivot)) {}
+ assert(lessI <= greaterI, "sort: invalid comparison function.");
+ for (;;)
+ {
+ if (greaterI == lessI) break outer;
+ if (!pred(pivot, r[--greaterI])) break;
+ }
+ assert(lessI <= greaterI, "sort: invalid comparison function.");
+ if (lessI == greaterI) break;
+ r.swapAt(lessI, greaterI);
+ }
+
+ r.swapAt(r.length - 1, lessI);
+ auto left = r[0 .. lessI], right = r[lessI + 1 .. r.length];
+ if (right.length > left.length)
+ {
+ swap(left, right);
+ }
+ .quickSortImpl!(less, Range)(right, depth);
+ r = left;
+ }
+ // residual sort
+ static if (shortSortGetsBetter > 1)
+ {
+ shortSort!(less, Range)(r);
+ }
+}
+
+// Heap operations for random-access ranges
+package(std) template HeapOps(alias less, Range)
+{
+ import std.algorithm.mutation : swapAt;
+
+ static assert(isRandomAccessRange!Range);
+ static assert(hasLength!Range);
+ static assert(hasSwappableElements!Range || hasAssignableElements!Range);
+
+ alias lessFun = binaryFun!less;
+
+ //template because of @@@12410@@@
+ void heapSort()(Range r)
+ {
+ // If true, there is nothing to do
+ if (r.length < 2) return;
+ // Build Heap
+ buildHeap(r);
+ // Sort
+ for (size_t i = r.length - 1; i > 0; --i)
+ {
+ r.swapAt(0, i);
+ percolate(r, 0, i);
+ }
+ }
+
+ //template because of @@@12410@@@
+ void buildHeap()(Range r)
+ {
+ immutable n = r.length;
+ for (size_t i = n / 2; i-- > 0; )
+ {
+ siftDown(r, i, n);
+ }
+ assert(isHeap(r));
+ }
+
+ bool isHeap()(Range r)
+ {
+ size_t parent = 0;
+ foreach (child; 1 .. r.length)
+ {
+ if (lessFun(r[parent], r[child])) return false;
+ // Increment parent every other pass
+ parent += !(child & 1);
+ }
+ return true;
+ }
+
+ // Sifts down r[parent] (which is initially assumed to be messed up) so the
+ // heap property is restored for r[parent .. end].
+ // template because of @@@12410@@@
+ void siftDown()(Range r, size_t parent, immutable size_t end)
+ {
+ for (;;)
+ {
+ auto child = (parent + 1) * 2;
+ if (child >= end)
+ {
+ // Leftover left child?
+ if (child == end && lessFun(r[parent], r[--child]))
+ r.swapAt(parent, child);
+ break;
+ }
+ auto leftChild = child - 1;
+ if (lessFun(r[child], r[leftChild])) child = leftChild;
+ if (!lessFun(r[parent], r[child])) break;
+ r.swapAt(parent, child);
+ parent = child;
+ }
+ }
+
+ // Alternate version of siftDown that performs fewer comparisons, see
+ // https://en.wikipedia.org/wiki/Heapsort#Bottom-up_heapsort. The percolate
+ // process first sifts the parent all the way down (without comparing it
+ // against the leaves), and then a bit up until the heap property is
+ // restored. So there are more swaps but fewer comparisons. Gains are made
+ // when the final position is likely to end toward the bottom of the heap,
+ // so not a lot of sifts back are performed.
+ //template because of @@@12410@@@
+ void percolate()(Range r, size_t parent, immutable size_t end)
+ {
+ immutable root = parent;
+
+ // Sift down
+ for (;;)
+ {
+ auto child = (parent + 1) * 2;
+
+ if (child >= end)
+ {
+ if (child == end)
+ {
+ // Leftover left node.
+ --child;
+ r.swapAt(parent, child);
+ parent = child;
+ }
+ break;
+ }
+
+ auto leftChild = child - 1;
+ if (lessFun(r[child], r[leftChild])) child = leftChild;
+ r.swapAt(parent, child);
+ parent = child;
+ }
+
+ // Sift up
+ for (auto child = parent; child > root; child = parent)
+ {
+ parent = (child - 1) / 2;
+ if (!lessFun(r[parent], r[child])) break;
+ r.swapAt(parent, child);
+ }
+ }
+}
+
+// Tim Sort implementation
+private template TimSortImpl(alias pred, R)
+{
+ import core.bitop : bsr;
+ import std.array : uninitializedArray;
+
+ static assert(isRandomAccessRange!R);
+ static assert(hasLength!R);
+ static assert(hasSlicing!R);
+ static assert(hasAssignableElements!R);
+
+ alias T = ElementType!R;
+
+ alias less = binaryFun!pred;
+ alias greater = (a, b) => less(b, a);
+ alias greaterEqual = (a, b) => !less(a, b);
+ alias lessEqual = (a, b) => !less(b, a);
+
+ enum minimalMerge = 128;
+ enum minimalGallop = 7;
+ enum minimalStorage = 256;
+ enum stackSize = 40;
+
+ struct Slice{ size_t base, length; }
+
+ // Entry point for tim sort
+ void sort()(R range, T[] temp)
+ {
+ import std.algorithm.comparison : min;
+
+ // Do insertion sort on small range
+ if (range.length <= minimalMerge)
+ {
+ binaryInsertionSort(range);
+ return;
+ }
+
+ immutable minRun = minRunLength(range.length);
+ immutable minTemp = min(range.length / 2, minimalStorage);
+ size_t minGallop = minimalGallop;
+ Slice[stackSize] stack = void;
+ size_t stackLen = 0;
+
+ // Allocate temporary memory if not provided by user
+ if (temp.length < minTemp) temp = () @trusted { return uninitializedArray!(T[])(minTemp); }();
+
+ for (size_t i = 0; i < range.length; )
+ {
+ // Find length of first run in list
+ size_t runLen = firstRun(range[i .. range.length]);
+
+ // If run has less than minRun elements, extend using insertion sort
+ if (runLen < minRun)
+ {
+ // Do not run farther than the length of the range
+ immutable force = range.length - i > minRun ? minRun : range.length - i;
+ binaryInsertionSort(range[i .. i + force], runLen);
+ runLen = force;
+ }
+
+ // Push run onto stack
+ stack[stackLen++] = Slice(i, runLen);
+ i += runLen;
+
+ // Collapse stack so that (e1 > e2 + e3 && e2 > e3)
+ // STACK is | ... e1 e2 e3 >
+ while (stackLen > 1)
+ {
+ immutable run4 = stackLen - 1;
+ immutable run3 = stackLen - 2;
+ immutable run2 = stackLen - 3;
+ immutable run1 = stackLen - 4;
+
+ if ( (stackLen > 2 && stack[run2].length <= stack[run3].length + stack[run4].length) ||
+ (stackLen > 3 && stack[run1].length <= stack[run3].length + stack[run2].length) )
+ {
+ immutable at = stack[run2].length < stack[run4].length ? run2 : run3;
+ mergeAt(range, stack[0 .. stackLen], at, minGallop, temp);
+ }
+ else if (stack[run3].length > stack[run4].length) break;
+ else mergeAt(range, stack[0 .. stackLen], run3, minGallop, temp);
+
+ stackLen -= 1;
+ }
+
+ // Assert that the code above established the invariant correctly
+ version (assert)
+ {
+ if (stackLen == 2) assert(stack[0].length > stack[1].length);
+ else if (stackLen > 2)
+ {
+ foreach (k; 2 .. stackLen)
+ {
+ assert(stack[k - 2].length > stack[k - 1].length + stack[k].length);
+ assert(stack[k - 1].length > stack[k].length);
+ }
+ }
+ }
+ }
+
+ // Force collapse stack until there is only one run left
+ while (stackLen > 1)
+ {
+ immutable run3 = stackLen - 1;
+ immutable run2 = stackLen - 2;
+ immutable run1 = stackLen - 3;
+ immutable at = stackLen >= 3 && stack[run1].length <= stack[run3].length
+ ? run1 : run2;
+ mergeAt(range, stack[0 .. stackLen], at, minGallop, temp);
+ --stackLen;
+ }
+ }
+
+ // Calculates optimal value for minRun:
+ // take first 6 bits of n and add 1 if any lower bits are set
+ size_t minRunLength()(size_t n)
+ {
+ immutable shift = bsr(n)-5;
+ auto result = (n >> shift) + !!(n & ~((1 << shift)-1));
+ return result;
+ }
+
+ // Returns length of first run in range
+ size_t firstRun()(R range)
+ out(ret)
+ {
+ assert(ret <= range.length);
+ }
+ body
+ {
+ import std.algorithm.mutation : reverse;
+
+ if (range.length < 2) return range.length;
+
+ size_t i = 2;
+ if (lessEqual(range[0], range[1]))
+ {
+ while (i < range.length && lessEqual(range[i-1], range[i])) ++i;
+ }
+ else
+ {
+ while (i < range.length && greater(range[i-1], range[i])) ++i;
+ reverse(range[0 .. i]);
+ }
+ return i;
+ }
+
+ // A binary insertion sort for building runs up to minRun length
+ void binaryInsertionSort()(R range, size_t sortedLen = 1)
+ out
+ {
+ if (!__ctfe) assert(isSorted!pred(range));
+ }
+ body
+ {
+ import std.algorithm.mutation : move;
+
+ for (; sortedLen < range.length; ++sortedLen)
+ {
+ T item = range.moveAt(sortedLen);
+ size_t lower = 0;
+ size_t upper = sortedLen;
+ while (upper != lower)
+ {
+ size_t center = (lower + upper) / 2;
+ if (less(item, range[center])) upper = center;
+ else lower = center + 1;
+ }
+ //Currently (DMD 2.061) moveAll+retro is slightly less
+ //efficient then stright 'for' loop
+ //11 instructions vs 7 in the innermost loop [checked on Win32]
+ //moveAll(retro(range[lower .. sortedLen]),
+ // retro(range[lower+1 .. sortedLen+1]));
+ for (upper=sortedLen; upper > lower; upper--)
+ range[upper] = range.moveAt(upper - 1);
+ range[lower] = move(item);
+ }
+ }
+
+ // Merge two runs in stack (at, at + 1)
+ void mergeAt()(R range, Slice[] stack, immutable size_t at, ref size_t minGallop, ref T[] temp)
+ in
+ {
+ assert(stack.length >= 2);
+ assert(stack.length - at == 2 || stack.length - at == 3);
+ }
+ body
+ {
+ immutable base = stack[at].base;
+ immutable mid = stack[at].length;
+ immutable len = stack[at + 1].length + mid;
+
+ // Pop run from stack
+ stack[at] = Slice(base, len);
+ if (stack.length - at == 3) stack[$ - 2] = stack[$ - 1];
+
+ // Merge runs (at, at + 1)
+ return merge(range[base .. base + len], mid, minGallop, temp);
+ }
+
+ // Merge two runs in a range. Mid is the starting index of the second run.
+ // minGallop and temp are references; The calling function must receive the updated values.
+ void merge()(R range, size_t mid, ref size_t minGallop, ref T[] temp)
+ in
+ {
+ if (!__ctfe)
+ {
+ assert(isSorted!pred(range[0 .. mid]));
+ assert(isSorted!pred(range[mid .. range.length]));
+ }
+ }
+ body
+ {
+ assert(mid < range.length);
+
+ // Reduce range of elements
+ immutable firstElement = gallopForwardUpper(range[0 .. mid], range[mid]);
+ immutable lastElement = gallopReverseLower(range[mid .. range.length], range[mid - 1]) + mid;
+ range = range[firstElement .. lastElement];
+ mid -= firstElement;
+
+ if (mid == 0 || mid == range.length) return;
+
+ // Call function which will copy smaller run into temporary memory
+ if (mid <= range.length / 2)
+ {
+ temp = ensureCapacity(mid, temp);
+ minGallop = mergeLo(range, mid, minGallop, temp);
+ }
+ else
+ {
+ temp = ensureCapacity(range.length - mid, temp);
+ minGallop = mergeHi(range, mid, minGallop, temp);
+ }
+ }
+
+ // Enlarge size of temporary memory if needed
+ T[] ensureCapacity()(size_t minCapacity, T[] temp)
+ out(ret)
+ {
+ assert(ret.length >= minCapacity);
+ }
+ body
+ {
+ if (temp.length < minCapacity)
+ {
+ size_t newSize = 1<<(bsr(minCapacity)+1);
+ //Test for overflow
+ if (newSize < minCapacity) newSize = minCapacity;
+
+ if (__ctfe) temp.length = newSize;
+ else temp = () @trusted { return uninitializedArray!(T[])(newSize); }();
+ }
+ return temp;
+ }
+
+ // Merge front to back. Returns new value of minGallop.
+ // temp must be large enough to store range[0 .. mid]
+ size_t mergeLo()(R range, immutable size_t mid, size_t minGallop, T[] temp)
+ out
+ {
+ if (!__ctfe) assert(isSorted!pred(range));
+ }
+ body
+ {
+ import std.algorithm.mutation : copy;
+
+ assert(mid <= range.length);
+ assert(temp.length >= mid);
+
+ // Copy run into temporary memory
+ temp = temp[0 .. mid];
+ copy(range[0 .. mid], temp);
+
+ // Move first element into place
+ range[0] = range[mid];
+
+ size_t i = 1, lef = 0, rig = mid + 1;
+ size_t count_lef, count_rig;
+ immutable lef_end = temp.length - 1;
+
+ if (lef < lef_end && rig < range.length)
+ outer: while (true)
+ {
+ count_lef = 0;
+ count_rig = 0;
+
+ // Linear merge
+ while ((count_lef | count_rig) < minGallop)
+ {
+ if (lessEqual(temp[lef], range[rig]))
+ {
+ range[i++] = temp[lef++];
+ if (lef >= lef_end) break outer;
+ ++count_lef;
+ count_rig = 0;
+ }
+ else
+ {
+ range[i++] = range[rig++];
+ if (rig >= range.length) break outer;
+ count_lef = 0;
+ ++count_rig;
+ }
+ }
+
+ // Gallop merge
+ do
+ {
+ count_lef = gallopForwardUpper(temp[lef .. $], range[rig]);
+ foreach (j; 0 .. count_lef) range[i++] = temp[lef++];
+ if (lef >= temp.length) break outer;
+
+ count_rig = gallopForwardLower(range[rig .. range.length], temp[lef]);
+ foreach (j; 0 .. count_rig) range[i++] = range[rig++];
+ if (rig >= range.length) while (true)
+ {
+ range[i++] = temp[lef++];
+ if (lef >= temp.length) break outer;
+ }
+
+ if (minGallop > 0) --minGallop;
+ }
+ while (count_lef >= minimalGallop || count_rig >= minimalGallop);
+
+ minGallop += 2;
+ }
+
+ // Move remaining elements from right
+ while (rig < range.length)
+ range[i++] = range[rig++];
+
+ // Move remaining elements from left
+ while (lef < temp.length)
+ range[i++] = temp[lef++];
+
+ return minGallop > 0 ? minGallop : 1;
+ }
+
+ // Merge back to front. Returns new value of minGallop.
+ // temp must be large enough to store range[mid .. range.length]
+ size_t mergeHi()(R range, immutable size_t mid, size_t minGallop, T[] temp)
+ out
+ {
+ if (!__ctfe) assert(isSorted!pred(range));
+ }
+ body
+ {
+ import std.algorithm.mutation : copy;
+
+ assert(mid <= range.length);
+ assert(temp.length >= range.length - mid);
+
+ // Copy run into temporary memory
+ temp = temp[0 .. range.length - mid];
+ copy(range[mid .. range.length], temp);
+
+ // Move first element into place
+ range[range.length - 1] = range[mid - 1];
+
+ size_t i = range.length - 2, lef = mid - 2, rig = temp.length - 1;
+ size_t count_lef, count_rig;
+
+ outer:
+ while (true)
+ {
+ count_lef = 0;
+ count_rig = 0;
+
+ // Linear merge
+ while ((count_lef | count_rig) < minGallop)
+ {
+ if (greaterEqual(temp[rig], range[lef]))
+ {
+ range[i--] = temp[rig];
+ if (rig == 1)
+ {
+ // Move remaining elements from left
+ while (true)
+ {
+ range[i--] = range[lef];
+ if (lef == 0) break;
+ --lef;
+ }
+
+ // Move last element into place
+ range[i] = temp[0];
+
+ break outer;
+ }
+ --rig;
+ count_lef = 0;
+ ++count_rig;
+ }
+ else
+ {
+ range[i--] = range[lef];
+ if (lef == 0) while (true)
+ {
+ range[i--] = temp[rig];
+ if (rig == 0) break outer;
+ --rig;
+ }
+ --lef;
+ ++count_lef;
+ count_rig = 0;
+ }
+ }
+
+ // Gallop merge
+ do
+ {
+ count_rig = rig - gallopReverseLower(temp[0 .. rig], range[lef]);
+ foreach (j; 0 .. count_rig)
+ {
+ range[i--] = temp[rig];
+ if (rig == 0) break outer;
+ --rig;
+ }
+
+ count_lef = lef - gallopReverseUpper(range[0 .. lef], temp[rig]);
+ foreach (j; 0 .. count_lef)
+ {
+ range[i--] = range[lef];
+ if (lef == 0) while (true)
+ {
+ range[i--] = temp[rig];
+ if (rig == 0) break outer;
+ --rig;
+ }
+ --lef;
+ }
+
+ if (minGallop > 0) --minGallop;
+ }
+ while (count_lef >= minimalGallop || count_rig >= minimalGallop);
+
+ minGallop += 2;
+ }
+
+ return minGallop > 0 ? minGallop : 1;
+ }
+
+ // false = forward / lower, true = reverse / upper
+ template gallopSearch(bool forwardReverse, bool lowerUpper)
+ {
+ // Gallop search on range according to attributes forwardReverse and lowerUpper
+ size_t gallopSearch(R)(R range, T value)
+ out(ret)
+ {
+ assert(ret <= range.length);
+ }
+ body
+ {
+ size_t lower = 0, center = 1, upper = range.length;
+ alias gap = center;
+
+ static if (forwardReverse)
+ {
+ static if (!lowerUpper) alias comp = lessEqual; // reverse lower
+ static if (lowerUpper) alias comp = less; // reverse upper
+
+ // Gallop Search Reverse
+ while (gap <= upper)
+ {
+ if (comp(value, range[upper - gap]))
+ {
+ upper -= gap;
+ gap *= 2;
+ }
+ else
+ {
+ lower = upper - gap;
+ break;
+ }
+ }
+
+ // Binary Search Reverse
+ while (upper != lower)
+ {
+ center = lower + (upper - lower) / 2;
+ if (comp(value, range[center])) upper = center;
+ else lower = center + 1;
+ }
+ }
+ else
+ {
+ static if (!lowerUpper) alias comp = greater; // forward lower
+ static if (lowerUpper) alias comp = greaterEqual; // forward upper
+
+ // Gallop Search Forward
+ while (lower + gap < upper)
+ {
+ if (comp(value, range[lower + gap]))
+ {
+ lower += gap;
+ gap *= 2;
+ }
+ else
+ {
+ upper = lower + gap;
+ break;
+ }
+ }
+
+ // Binary Search Forward
+ while (lower != upper)
+ {
+ center = lower + (upper - lower) / 2;
+ if (comp(value, range[center])) lower = center + 1;
+ else upper = center;
+ }
+ }
+
+ return lower;
+ }
+ }
+
+ alias gallopForwardLower = gallopSearch!(false, false);
+ alias gallopForwardUpper = gallopSearch!(false, true);
+ alias gallopReverseLower = gallopSearch!( true, false);
+ alias gallopReverseUpper = gallopSearch!( true, true);
+}
+
+@safe unittest
+{
+ import std.random : Random, uniform, randomShuffle;
+
+ // Element type with two fields
+ static struct E
+ {
+ size_t value, index;
+ }
+
+ // Generates data especially for testing sorting with Timsort
+ static E[] genSampleData(uint seed) @safe
+ {
+ import std.algorithm.mutation : swap, swapRanges;
+
+ auto rnd = Random(seed);
+
+ E[] arr;
+ arr.length = 64 * 64;
+
+ // We want duplicate values for testing stability
+ foreach (i, ref v; arr) v.value = i / 64;
+
+ // Swap ranges at random middle point (test large merge operation)
+ immutable mid = uniform(arr.length / 4, arr.length / 4 * 3, rnd);
+ swapRanges(arr[0 .. mid], arr[mid .. $]);
+
+ // Shuffle last 1/8 of the array (test insertion sort and linear merge)
+ randomShuffle(arr[$ / 8 * 7 .. $], rnd);
+
+ // Swap few random elements (test galloping mode)
+ foreach (i; 0 .. arr.length / 64)
+ {
+ immutable a = uniform(0, arr.length, rnd), b = uniform(0, arr.length, rnd);
+ swap(arr[a], arr[b]);
+ }
+
+ // Now that our test array is prepped, store original index value
+ // This will allow us to confirm the array was sorted stably
+ foreach (i, ref v; arr) v.index = i;
+
+ return arr;
+ }
+
+ // Tests the Timsort function for correctness and stability
+ static bool testSort(uint seed)
+ {
+ auto arr = genSampleData(seed);
+
+ // Now sort the array!
+ static bool comp(E a, E b)
+ {
+ return a.value < b.value;
+ }
+
+ sort!(comp, SwapStrategy.stable)(arr);
+
+ // Test that the array was sorted correctly
+ assert(isSorted!comp(arr));
+
+ // Test that the array was sorted stably
+ foreach (i; 0 .. arr.length - 1)
+ {
+ if (arr[i].value == arr[i + 1].value) assert(arr[i].index < arr[i + 1].index);
+ }
+
+ return true;
+ }
+
+ enum seed = 310614065;
+ testSort(seed);
+
+ enum result = testSort(seed);
+ assert(result == true);
+}
+
+@safe unittest
+{//bugzilla 4584
+ assert(isSorted!"a < b"(sort!("a < b", SwapStrategy.stable)(
+ [83, 42, 85, 86, 87, 22, 89, 30, 91, 46, 93, 94, 95, 6,
+ 97, 14, 33, 10, 101, 102, 103, 26, 105, 106, 107, 6]
+ )));
+
+}
+
+@safe unittest
+{
+ //test stable sort + zip
+ import std.range;
+ auto x = [10, 50, 60, 60, 20];
+ dchar[] y = "abcde"d.dup;
+
+ sort!("a[0] < b[0]", SwapStrategy.stable)(zip(x, y));
+ assert(x == [10, 20, 50, 60, 60]);
+ assert(y == "aebcd"d);
+}
+
+@safe unittest
+{
+ // Issue 14223
+ import std.array, std.range;
+ auto arr = chain(iota(0, 384), iota(0, 256), iota(0, 80), iota(0, 64), iota(0, 96)).array;
+ sort!("a < b", SwapStrategy.stable)(arr);
+}
+
+// schwartzSort
+/**
+Alternative sorting method that should be used when comparing keys involves an
+expensive computation. Instead of using `less(a, b)` for comparing elements,
+`schwartzSort` uses `less(transform(a), transform(b))`. The values of the
+`transform` function are precomputed in a temporary array, thus saving on
+repeatedly computing it. Conversely, if the cost of `transform` is small
+compared to the cost of allocating and filling the precomputed array, `sort`
+may be faster and therefore preferable.
+
+This approach to sorting is akin to the $(HTTP
+wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform), also known as
+the decorate-sort-undecorate pattern in Python and Lisp. The complexity is the
+same as that of the corresponding `sort`, but `schwartzSort` evaluates
+`transform` only `r.length` times (less than half when compared to regular
+sorting). The usage can be best illustrated with an example.
+
+Example:
+----
+uint hashFun(string) { ... expensive computation ... }
+string[] array = ...;
+// Sort strings by hash, slow
+sort!((a, b) => hashFun(a) < hashFun(b))(array);
+// Sort strings by hash, fast (only computes arr.length hashes):
+schwartzSort!(hashFun, "a < b")(array);
+----
+
+The $(D schwartzSort) function might require less temporary data and
+be faster than the Perl idiom or the decorate-sort-undecorate idiom
+present in Python and Lisp. This is because sorting is done in-place
+and only minimal extra data (one array of transformed elements) is
+created.
+
+To check whether an array was sorted and benefit of the speedup of
+Schwartz sorting, a function $(D schwartzIsSorted) is not provided
+because the effect can be achieved by calling $(D
+isSorted!less(map!transform(r))).
+
+Params:
+ transform = The transformation to apply.
+ less = The predicate to sort by.
+ ss = The swapping strategy to use.
+ r = The range to sort.
+
+Returns: The initial range wrapped as a $(D SortedRange) with the
+predicate $(D (a, b) => binaryFun!less(transform(a),
+transform(b))).
+ */
+SortedRange!(R, ((a, b) => binaryFun!less(unaryFun!transform(a),
+ unaryFun!transform(b))))
+schwartzSort(alias transform, alias less = "a < b",
+ SwapStrategy ss = SwapStrategy.unstable, R)(R r)
+if (isRandomAccessRange!R && hasLength!R)
+{
+ import std.conv : emplace;
+ import std.range : zip, SortedRange;
+ import std.string : representation;
+
+ alias T = typeof(unaryFun!transform(r.front));
+ static trustedMalloc(size_t len) @trusted
+ {
+ import core.checkedint : mulu;
+ import core.stdc.stdlib : malloc;
+ bool overflow;
+ const nbytes = mulu(len, T.sizeof, overflow);
+ if (overflow) assert(0);
+ return (cast(T*) malloc(nbytes))[0 .. len];
+ }
+ auto xform1 = trustedMalloc(r.length);
+
+ size_t length;
+ scope(exit)
+ {
+ static if (hasElaborateDestructor!T)
+ {
+ foreach (i; 0 .. length) collectException(destroy(xform1[i]));
+ }
+ static void trustedFree(T[] p) @trusted
+ {
+ import core.stdc.stdlib : free;
+ free(p.ptr);
+ }
+ trustedFree(xform1);
+ }
+ for (; length != r.length; ++length)
+ {
+ emplace(&xform1[length], unaryFun!transform(r[length]));
+ }
+ // Make sure we use ubyte[] and ushort[], not char[] and wchar[]
+ // for the intermediate array, lest zip gets confused.
+ static if (isNarrowString!(typeof(xform1)))
+ {
+ auto xform = xform1.representation();
+ }
+ else
+ {
+ alias xform = xform1;
+ }
+ zip(xform, r).sort!((a, b) => binaryFun!less(a[0], b[0]), ss)();
+ return typeof(return)(r);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.numeric : entropy;
+
+ auto lowEnt = [ 1.0, 0, 0 ],
+ midEnt = [ 0.1, 0.1, 0.8 ],
+ highEnt = [ 0.31, 0.29, 0.4 ];
+ auto arr = new double[][3];
+ arr[0] = midEnt;
+ arr[1] = lowEnt;
+ arr[2] = highEnt;
+
+ schwartzSort!(entropy, "a > b")(arr);
+
+ assert(arr[0] == highEnt);
+ assert(arr[1] == midEnt);
+ assert(arr[2] == lowEnt);
+ assert(isSorted!("a > b")(map!(entropy)(arr)));
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.numeric : entropy;
+
+ auto lowEnt = [ 1.0, 0, 0 ],
+ midEnt = [ 0.1, 0.1, 0.8 ],
+ highEnt = [ 0.31, 0.29, 0.4 ];
+ auto arr = new double[][3];
+ arr[0] = midEnt;
+ arr[1] = lowEnt;
+ arr[2] = highEnt;
+
+ schwartzSort!(entropy, "a < b")(arr);
+
+ assert(arr[0] == lowEnt);
+ assert(arr[1] == midEnt);
+ assert(arr[2] == highEnt);
+ assert(isSorted!("a < b")(map!(entropy)(arr)));
+}
+
+@safe unittest
+{
+ // issue 4909
+ import std.typecons : Tuple;
+ Tuple!(char)[] chars;
+ schwartzSort!"a[0]"(chars);
+}
+
+@safe unittest
+{
+ // issue 5924
+ import std.typecons : Tuple;
+ Tuple!(char)[] chars;
+ schwartzSort!((Tuple!(char) c){ return c[0]; })(chars);
+}
+
+// partialSort
+/**
+Reorders the random-access range $(D r) such that the range $(D r[0
+.. mid]) is the same as if the entire $(D r) were sorted, and leaves
+the range $(D r[mid .. r.length]) in no particular order. Performs
+$(BIGOH r.length * log(mid)) evaluations of $(D pred). The
+implementation simply calls $(D topN!(less, ss)(r, n)) and then $(D
+sort!(less, ss)(r[0 .. n])).
+
+Params:
+ less = The predicate to sort by.
+ ss = The swapping strategy to use.
+ r = The random-access range to reorder.
+ n = The length of the initial segment of `r` to sort.
+*/
+void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
+ Range)(Range r, size_t n)
+if (isRandomAccessRange!(Range) && hasLength!(Range) && hasSlicing!(Range))
+{
+ partialSort!(less, ss)(r[0 .. n], r[n .. $]);
+}
+
+///
+@system unittest
+{
+ int[] a = [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ];
+ partialSort(a, 5);
+ assert(a[0 .. 5] == [ 0, 1, 2, 3, 4 ]);
+}
+
+/**
+Stores the smallest elements of the two ranges in the left-hand range in sorted order.
+
+Params:
+ less = The predicate to sort by.
+ ss = The swapping strategy to use.
+ r1 = The first range.
+ r2 = The second range.
+ */
+
+void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
+ Range1, Range2)(Range1 r1, Range2 r2)
+if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
+ isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) &&
+ hasLvalueElements!Range1 && hasLvalueElements!Range2)
+{
+ topN!(less, ss)(r1, r2);
+ sort!(less, ss)(r1);
+}
+///
+@system unittest
+{
+ int[] a = [5, 7, 2, 6, 7];
+ int[] b = [2, 1, 5, 6, 7, 3, 0];
+
+ partialSort(a, b);
+ assert(a == [0, 1, 2, 2, 3]);
+}
+
+// topN
+/**
+Reorders the range $(D r) using $(D swap) such that $(D r[nth]) refers
+to the element that would fall there if the range were fully
+sorted. In addition, it also partitions $(D r) such that all elements
+$(D e1) from $(D r[0]) to $(D r[nth]) satisfy $(D !less(r[nth], e1)),
+and all elements $(D e2) from $(D r[nth]) to $(D r[r.length]) satisfy
+$(D !less(e2, r[nth])). Effectively, it finds the nth smallest
+(according to $(D less)) elements in $(D r). Performs an expected
+$(BIGOH r.length) (if unstable) or $(BIGOH r.length * log(r.length))
+(if stable) evaluations of $(D less) and $(D swap).
+
+If $(D n >= r.length), the algorithm has no effect and returns
+`r[0 .. r.length]`.
+
+Params:
+ less = The predicate to sort by.
+ ss = The swapping strategy to use.
+ r = The random-access range to reorder.
+ nth = The index of the element that should be in sorted position after the
+ function is done.
+
+See_Also:
+ $(LREF topNIndex),
+ $(HTTP sgi.com/tech/stl/nth_element.html, STL's nth_element)
+
+BUGS:
+
+Stable topN has not been implemented yet.
+*/
+auto topN(alias less = "a < b",
+ SwapStrategy ss = SwapStrategy.unstable,
+ Range)(Range r, size_t nth)
+if (isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range)
+{
+ static assert(ss == SwapStrategy.unstable,
+ "Stable topN not yet implemented");
+ if (nth >= r.length) return r[0 .. r.length];
+ auto ret = r[0 .. nth];
+ if (false)
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=16528
+ // Safety checks: enumerate all potentially unsafe generic primitives
+ // then use a @trusted implementation.
+ binaryFun!less(r[0], r[r.length - 1]);
+ import std.algorithm.mutation : swapAt;
+ r.swapAt(size_t(0), size_t(0));
+ static assert(is(typeof(r.length) == size_t));
+ pivotPartition!less(r, 0);
+ }
+ bool useSampling = true;
+ topNImpl!(binaryFun!less)(r, nth, useSampling);
+ return ret;
+}
+
+private @trusted
+void topNImpl(alias less, R)(R r, size_t n, ref bool useSampling)
+{
+ for (;;)
+ {
+ import std.algorithm.mutation : swapAt;
+ assert(n < r.length);
+ size_t pivot = void;
+
+ // Decide strategy for partitioning
+ if (n == 0)
+ {
+ pivot = 0;
+ foreach (i; 1 .. r.length)
+ if (less(r[i], r[pivot])) pivot = i;
+ r.swapAt(n, pivot);
+ return;
+ }
+ if (n + 1 == r.length)
+ {
+ pivot = 0;
+ foreach (i; 1 .. r.length)
+ if (less(r[pivot], r[i])) pivot = i;
+ r.swapAt(n, pivot);
+ return;
+ }
+ if (r.length <= 12)
+ {
+ pivot = pivotPartition!less(r, r.length / 2);
+ }
+ else if (n * 16 <= (r.length - 1) * 7)
+ {
+ pivot = topNPartitionOffMedian!(less, No.leanRight)
+ (r, n, useSampling);
+ // Quality check
+ if (useSampling)
+ {
+ if (pivot < n)
+ {
+ if (pivot * 4 < r.length)
+ {
+ useSampling = false;
+ }
+ }
+ else if ((r.length - pivot) * 8 < r.length * 3)
+ {
+ useSampling = false;
+ }
+ }
+ }
+ else if (n * 16 >= (r.length - 1) * 9)
+ {
+ pivot = topNPartitionOffMedian!(less, Yes.leanRight)
+ (r, n, useSampling);
+ // Quality check
+ if (useSampling)
+ {
+ if (pivot < n)
+ {
+ if (pivot * 8 < r.length * 3)
+ {
+ useSampling = false;
+ }
+ }
+ else if ((r.length - pivot) * 4 < r.length)
+ {
+ useSampling = false;
+ }
+ }
+ }
+ else
+ {
+ pivot = topNPartition!less(r, n, useSampling);
+ // Quality check
+ if (useSampling &&
+ (pivot * 9 < r.length * 2 || pivot * 9 > r.length * 7))
+ {
+ // Failed - abort sampling going forward
+ useSampling = false;
+ }
+ }
+
+ assert(pivot != size_t.max);
+ // See how the pivot fares
+ if (pivot == n)
+ {
+ return;
+ }
+ if (pivot > n)
+ {
+ r = r[0 .. pivot];
+ }
+ else
+ {
+ n -= pivot + 1;
+ r = r[pivot + 1 .. r.length];
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ int[] v = [ 25, 7, 9, 2, 0, 5, 21 ];
+ topN!"a < b"(v, 100);
+ assert(v == [ 25, 7, 9, 2, 0, 5, 21 ]);
+ auto n = 4;
+ topN!"a < b"(v, n);
+ assert(v[n] == 9);
+}
+
+private size_t topNPartition(alias lp, R)(R r, size_t n, bool useSampling)
+{
+ assert(r.length >= 9 && n < r.length);
+ immutable ninth = r.length / 9;
+ auto pivot = ninth / 2;
+ // Position subrange r[lo .. hi] to have length equal to ninth and its upper
+ // median r[lo .. hi][$ / 2] in exactly the same place as the upper median
+ // of the entire range r[$ / 2]. This is to improve behavior for searching
+ // the median in already sorted ranges.
+ immutable lo = r.length / 2 - pivot, hi = lo + ninth;
+ // We have either one straggler on the left, one on the right, or none.
+ assert(lo - (r.length - hi) <= 1 || (r.length - hi) - lo <= 1);
+ assert(lo >= ninth * 4);
+ assert(r.length - hi >= ninth * 4);
+
+ // Partition in groups of 3, and the mid tertile again in groups of 3
+ if (!useSampling)
+ p3!lp(r, lo - ninth, hi + ninth);
+ p3!lp(r, lo, hi);
+
+ // Get the median of medians of medians
+ // Map the full interval of n to the full interval of the ninth
+ pivot = (n * (ninth - 1)) / (r.length - 1);
+ topNImpl!lp(r[lo .. hi], pivot, useSampling);
+ return expandPartition!lp(r, lo, pivot + lo, hi);
+}
+
+private void p3(alias less, Range)(Range r, size_t lo, immutable size_t hi)
+{
+ assert(lo <= hi && hi < r.length);
+ immutable ln = hi - lo;
+ for (; lo < hi; ++lo)
+ {
+ assert(lo >= ln);
+ assert(lo + ln < r.length);
+ medianOf!less(r, lo - ln, lo, lo + ln);
+ }
+}
+
+private void p4(alias less, Flag!"leanRight" f, Range)
+ (Range r, size_t lo, immutable size_t hi)
+{
+ assert(lo <= hi && hi < r.length);
+ immutable ln = hi - lo, _2ln = ln * 2;
+ for (; lo < hi; ++lo)
+ {
+ assert(lo >= ln);
+ assert(lo + ln < r.length);
+ static if (f == Yes.leanRight)
+ medianOf!(less, f)(r, lo - _2ln, lo - ln, lo, lo + ln);
+ else
+ medianOf!(less, f)(r, lo - ln, lo, lo + ln, lo + _2ln);
+ }
+}
+
+private size_t topNPartitionOffMedian(alias lp, Flag!"leanRight" f, R)
+ (R r, size_t n, bool useSampling)
+{
+ assert(r.length >= 12);
+ assert(n < r.length);
+ immutable _4 = r.length / 4;
+ static if (f == Yes.leanRight)
+ immutable leftLimit = 2 * _4;
+ else
+ immutable leftLimit = _4;
+ // Partition in groups of 4, and the left quartile again in groups of 3
+ if (!useSampling)
+ {
+ p4!(lp, f)(r, leftLimit, leftLimit + _4);
+ }
+ immutable _12 = _4 / 3;
+ immutable lo = leftLimit + _12, hi = lo + _12;
+ p3!lp(r, lo, hi);
+
+ // Get the median of medians of medians
+ // Map the full interval of n to the full interval of the ninth
+ immutable pivot = (n * (_12 - 1)) / (r.length - 1);
+ topNImpl!lp(r[lo .. hi], pivot, useSampling);
+ return expandPartition!lp(r, lo, pivot + lo, hi);
+}
+
+/*
+Params:
+less = predicate
+r = range to partition
+pivot = pivot to partition around
+lo = value such that r[lo .. pivot] already less than r[pivot]
+hi = value such that r[pivot .. hi] already greater than r[pivot]
+
+Returns: new position of pivot
+*/
+private
+size_t expandPartition(alias lp, R)(R r, size_t lo, size_t pivot, size_t hi)
+in
+{
+ import std.algorithm.searching : all;
+ assert(lo <= pivot);
+ assert(pivot < hi);
+ assert(hi <= r.length);
+ assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)));
+ assert(r[pivot + 1 .. hi].all!(x => !lp(x, r[pivot])));
+ }
+out
+{
+ import std.algorithm.searching : all;
+ assert(r[0 .. pivot + 1].all!(x => !lp(r[pivot], x)));
+ assert(r[pivot + 1 .. r.length].all!(x => !lp(x, r[pivot])));
+}
+body
+{
+ import std.algorithm.mutation : swapAt;
+ import std.algorithm.searching : all;
+ // We work with closed intervals!
+ --hi;
+
+ size_t left = 0, rite = r.length - 1;
+ loop: for (;; ++left, --rite)
+ {
+ for (;; ++left)
+ {
+ if (left == lo) break loop;
+ if (!lp(r[left], r[pivot])) break;
+ }
+ for (;; --rite)
+ {
+ if (rite == hi) break loop;
+ if (!lp(r[pivot], r[rite])) break;
+ }
+ r.swapAt(left, rite);
+ }
+
+ assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x)));
+ assert(r[pivot + 1 .. hi + 1].all!(x => !lp(x, r[pivot])));
+ assert(r[0 .. left].all!(x => !lp(r[pivot], x)));
+ assert(r[rite + 1 .. r.length].all!(x => !lp(x, r[pivot])));
+
+ immutable oldPivot = pivot;
+
+ if (left < lo)
+ {
+ // First loop: spend r[lo .. pivot]
+ for (; lo < pivot; ++left)
+ {
+ if (left == lo) goto done;
+ if (!lp(r[oldPivot], r[left])) continue;
+ --pivot;
+ assert(!lp(r[oldPivot], r[pivot]));
+ r.swapAt(left, pivot);
+ }
+ // Second loop: make left and pivot meet
+ for (;; ++left)
+ {
+ if (left == pivot) goto done;
+ if (!lp(r[oldPivot], r[left])) continue;
+ for (;;)
+ {
+ if (left == pivot) goto done;
+ --pivot;
+ if (lp(r[pivot], r[oldPivot]))
+ {
+ r.swapAt(left, pivot);
+ break;
+ }
+ }
+ }
+ }
+
+ // First loop: spend r[lo .. pivot]
+ for (; hi != pivot; --rite)
+ {
+ if (rite == hi) goto done;
+ if (!lp(r[rite], r[oldPivot])) continue;
+ ++pivot;
+ assert(!lp(r[pivot], r[oldPivot]));
+ r.swapAt(rite, pivot);
+ }
+ // Second loop: make left and pivot meet
+ for (; rite > pivot; --rite)
+ {
+ if (!lp(r[rite], r[oldPivot])) continue;
+ while (rite > pivot)
+ {
+ ++pivot;
+ if (lp(r[oldPivot], r[pivot]))
+ {
+ r.swapAt(rite, pivot);
+ break;
+ }
+ }
+ }
+
+done:
+ r.swapAt(oldPivot, pivot);
+ return pivot;
+}
+
+@safe unittest
+{
+ auto a = [ 10, 5, 3, 4, 8, 11, 13, 3, 9, 4, 10 ];
+ assert(expandPartition!((a, b) => a < b)(a, 4, 5, 6) == 9);
+ a = randomArray;
+ if (a.length == 0) return;
+ expandPartition!((a, b) => a < b)(a, a.length / 2, a.length / 2,
+ a.length / 2 + 1);
+}
+
+version (unittest)
+private T[] randomArray(Flag!"exactSize" flag = No.exactSize, T = int)(
+ size_t maxSize = 1000,
+ T minValue = 0, T maxValue = 255)
+{
+ import std.algorithm.iteration : map;
+ import std.random : unpredictableSeed, Random, uniform;
+ auto size = flag == Yes.exactSize ? maxSize : uniform(1, maxSize);
+ return iota(0, size).map!(_ => uniform(minValue, maxValue)).array;
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.algorithm.iteration : reduce;
+
+ int[] v = [ 7, 6, 5, 4, 3, 2, 1, 0 ];
+ ptrdiff_t n = 3;
+ topN!("a < b")(v, n);
+ assert(reduce!max(v[0 .. n]) <= v[n]);
+ assert(reduce!min(v[n + 1 .. $]) >= v[n]);
+ //
+ v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
+ n = 3;
+ topN(v, n);
+ assert(reduce!max(v[0 .. n]) <= v[n]);
+ assert(reduce!min(v[n + 1 .. $]) >= v[n]);
+ //
+ v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
+ n = 1;
+ topN(v, n);
+ assert(reduce!max(v[0 .. n]) <= v[n]);
+ assert(reduce!min(v[n + 1 .. $]) >= v[n]);
+ //
+ v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
+ n = v.length - 1;
+ topN(v, n);
+ assert(v[n] == 7);
+ //
+ v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
+ n = 0;
+ topN(v, n);
+ assert(v[n] == 1);
+
+ double[][] v1 = [[-10, -5], [-10, -3], [-10, -5], [-10, -4],
+ [-10, -5], [-9, -5], [-9, -3], [-9, -5],];
+
+ // double[][] v1 = [ [-10, -5], [-10, -4], [-9, -5], [-9, -5],
+ // [-10, -5], [-10, -3], [-10, -5], [-9, -3],];
+ double[]*[] idx = [ &v1[0], &v1[1], &v1[2], &v1[3], &v1[4], &v1[5], &v1[6],
+ &v1[7], ];
+
+ auto mid = v1.length / 2;
+ topN!((a, b){ return (*a)[1] < (*b)[1]; })(idx, mid);
+ foreach (e; idx[0 .. mid]) assert((*e)[1] <= (*idx[mid])[1]);
+ foreach (e; idx[mid .. $]) assert((*e)[1] >= (*idx[mid])[1]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.algorithm.iteration : reduce;
+ import std.random : Random, uniform, unpredictableSeed;
+
+ immutable uint[] seeds = [90027751, 2709791795, 1374631933, 995751648, 3541495258, 984840953, unpredictableSeed];
+ foreach (s; seeds)
+ {
+ auto r = Random(s);
+
+ int[] a = new int[uniform(1, 10000, r)];
+ foreach (ref e; a) e = uniform(-1000, 1000, r);
+
+ auto k = uniform(0, a.length, r);
+ topN(a, k);
+ if (k > 0)
+ {
+ auto left = reduce!max(a[0 .. k]);
+ assert(left <= a[k]);
+ }
+ if (k + 1 < a.length)
+ {
+ auto right = reduce!min(a[k + 1 .. $]);
+ assert(right >= a[k]);
+ }
+ }
+}
+
+// bug 12987
+@safe unittest
+{
+ int[] a = [ 25, 7, 9, 2, 0, 5, 21 ];
+ auto n = 4;
+ auto t = topN(a, n);
+ sort(t);
+ assert(t == [0, 2, 5, 7]);
+}
+
+/**
+Stores the smallest elements of the two ranges in the left-hand range.
+
+Params:
+ less = The predicate to sort by.
+ ss = The swapping strategy to use.
+ r1 = The first range.
+ r2 = The second range.
+ */
+auto topN(alias less = "a < b",
+ SwapStrategy ss = SwapStrategy.unstable,
+ Range1, Range2)(Range1 r1, Range2 r2)
+if (isRandomAccessRange!(Range1) && hasLength!Range1 &&
+ isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) &&
+ hasLvalueElements!Range1 && hasLvalueElements!Range2)
+{
+ import std.container : BinaryHeap;
+
+ static assert(ss == SwapStrategy.unstable,
+ "Stable topN not yet implemented");
+
+ auto heap = BinaryHeap!(Range1, less)(r1);
+ foreach (ref e; r2)
+ {
+ heap.conditionalSwap(e);
+ }
+
+ return r1;
+}
+
+///
+@system unittest
+{
+ int[] a = [ 5, 7, 2, 6, 7 ];
+ int[] b = [ 2, 1, 5, 6, 7, 3, 0 ];
+ topN(a, b);
+ sort(a);
+ assert(a == [0, 1, 2, 2, 3]);
+}
+
+// bug 15421
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+ import std.meta : AliasSeq;
+
+ alias RandomRanges = AliasSeq!(
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random)
+ );
+
+ alias ReferenceRanges = AliasSeq!(
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward),
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional),
+ DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random),
+ DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward),
+ DummyRange!(ReturnBy.Reference, Length.No, RangeType.Bidirectional));
+
+ foreach (T1; RandomRanges)
+ {
+ foreach (T2; ReferenceRanges)
+ {
+ import std.array;
+
+ T1 A;
+ T2 B;
+
+ A.reinit();
+ B.reinit();
+
+ topN(A, B);
+
+ // BUG(?): sort doesn't accept DummyRanges (needs Slicing and Length)
+ auto a = array(A);
+ auto b = array(B);
+ sort(a);
+ sort(b);
+
+ assert(equal(a, [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ]));
+ assert(equal(b, [ 6, 6, 7, 7, 8, 8, 9, 9, 10, 10 ]));
+ }
+ }
+}
+
+// bug 15421
+@system unittest
+{
+ auto a = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ];
+ auto b = [ 9, 8, 0, 3, 5, 25, 43, 4, 2, 0, 7 ];
+
+ topN(a, 4);
+ topN(b[0 .. 4], b[4 .. $]);
+
+ sort(a[0 .. 4]);
+ sort(a[4 .. $]);
+ sort(b[0 .. 4]);
+ sort(b[4 .. $]);
+
+ assert(a[0 .. 4] == b[0 .. 4]);
+ assert(a[4 .. $] == b[4 .. $]);
+ assert(a == b);
+}
+
+// bug 12987
+@system unittest
+{
+ int[] a = [ 5, 7, 2, 6, 7 ];
+ int[] b = [ 2, 1, 5, 6, 7, 3, 0 ];
+ auto t = topN(a, b);
+ sort(t);
+ assert(t == [ 0, 1, 2, 2, 3 ]);
+}
+
+// bug 15420
+@system unittest
+{
+ int[] a = [ 5, 7, 2, 6, 7 ];
+ int[] b = [ 2, 1, 5, 6, 7, 3, 0 ];
+ topN!"a > b"(a, b);
+ sort!"a > b"(a);
+ assert(a == [ 7, 7, 7, 6, 6 ]);
+}
+
+/**
+Copies the top $(D n) elements of the
+$(REF_ALTTEXT input range, isInputRange, std,range,primitives) $(D source) into the
+random-access range $(D target), where $(D n =
+target.length). Elements of $(D source) are not touched. If $(D
+sorted) is $(D true), the target is sorted. Otherwise, the target
+respects the $(HTTP en.wikipedia.org/wiki/Binary_heap, heap property).
+
+Params:
+ less = The predicate to sort by.
+ source = The source range.
+ target = The target range.
+ sorted = Whether to sort the elements copied into `target`.
+
+Returns: The slice of `target` containing the copied elements.
+ */
+TRange topNCopy(alias less = "a < b", SRange, TRange)
+ (SRange source, TRange target, SortOutput sorted = No.sortOutput)
+if (isInputRange!(SRange) && isRandomAccessRange!(TRange)
+ && hasLength!(TRange) && hasSlicing!(TRange))
+{
+ import std.container : BinaryHeap;
+
+ if (target.empty) return target;
+ auto heap = BinaryHeap!(TRange, less)(target, 0);
+ foreach (e; source) heap.conditionalInsert(e);
+ auto result = target[0 .. heap.length];
+ if (sorted == Yes.sortOutput)
+ {
+ while (!heap.empty) heap.removeFront();
+ }
+ return result;
+}
+
+///
+@system unittest
+{
+ import std.typecons : Yes;
+
+ int[] a = [ 10, 16, 2, 3, 1, 5, 0 ];
+ int[] b = new int[3];
+ topNCopy(a, b, Yes.sortOutput);
+ assert(b == [ 0, 1, 2 ]);
+}
+
+@system unittest
+{
+ import std.random : Random, unpredictableSeed, uniform, randomShuffle;
+ import std.typecons : Yes;
+
+ auto r = Random(unpredictableSeed);
+ ptrdiff_t[] a = new ptrdiff_t[uniform(1, 1000, r)];
+ foreach (i, ref e; a) e = i;
+ randomShuffle(a, r);
+ auto n = uniform(0, a.length, r);
+ ptrdiff_t[] b = new ptrdiff_t[n];
+ topNCopy!(binaryFun!("a < b"))(a, b, Yes.sortOutput);
+ assert(isSorted!(binaryFun!("a < b"))(b));
+}
+
+/**
+Given a range of elements, constructs an index of its top $(I n) elements
+(i.e., the first $(I n) elements if the range were sorted).
+
+Similar to $(LREF topN), except that the range is not modified.
+
+Params:
+ less = A binary predicate that defines the ordering of range elements.
+ Defaults to $(D a < b).
+ ss = $(RED (Not implemented yet.)) Specify the swapping strategy.
+ r = A
+ $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives)
+ of elements to make an index for.
+ index = A
+ $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives)
+ with assignable elements to build the index in. The length of this range
+ determines how many top elements to index in $(D r).
+
+ This index range can either have integral elements, in which case the
+ constructed index will consist of zero-based numerical indices into
+ $(D r); or it can have pointers to the element type of $(D r), in which
+ case the constructed index will be pointers to the top elements in
+ $(D r).
+ sorted = Determines whether to sort the index by the elements they refer
+ to.
+
+See_also: $(LREF topN), $(LREF topNCopy).
+
+BUGS:
+The swapping strategy parameter is not implemented yet; currently it is
+ignored.
+*/
+void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
+ Range, RangeIndex)
+ (Range r, RangeIndex index, SortOutput sorted = No.sortOutput)
+if (isRandomAccessRange!Range &&
+ isRandomAccessRange!RangeIndex &&
+ hasAssignableElements!RangeIndex)
+{
+ static assert(ss == SwapStrategy.unstable,
+ "Stable swap strategy not implemented yet.");
+
+ import std.container.binaryheap : BinaryHeap;
+ if (index.empty) return;
+
+ static if (isIntegral!(ElementType!(RangeIndex)))
+ {
+ import std.exception : enforce;
+
+ enforce(ElementType!(RangeIndex).max >= index.length,
+ "Index type too small");
+ bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b)
+ {
+ return binaryFun!(less)(r[a], r[b]);
+ }
+ auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
+ foreach (i; 0 .. r.length)
+ {
+ heap.conditionalInsert(cast(ElementType!RangeIndex) i);
+ }
+
+ }
+ else static if (is(ElementType!(RangeIndex) == ElementType!(Range)*))
+ {
+ static bool indirectLess(const ElementType!(RangeIndex) a,
+ const ElementType!(RangeIndex) b)
+ {
+ return binaryFun!less(*a, *b);
+ }
+ auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
+ foreach (i; 0 .. r.length)
+ {
+ heap.conditionalInsert(&r[i]);
+ }
+ }
+ else static assert(0, "Invalid ElementType");
+
+ if (sorted == Yes.sortOutput)
+ {
+ while (!heap.empty) heap.removeFront();
+ }
+}
+
+///
+@system unittest
+{
+ import std.typecons : Yes;
+
+ // Construct index to top 3 elements using numerical indices:
+ int[] a = [ 10, 2, 7, 5, 8, 1 ];
+ int[] index = new int[3];
+ topNIndex(a, index, Yes.sortOutput);
+ assert(index == [5, 1, 3]); // because a[5]==1, a[1]==2, a[3]==5
+
+ // Construct index to top 3 elements using pointer indices:
+ int*[] ptrIndex = new int*[3];
+ topNIndex(a, ptrIndex, Yes.sortOutput);
+ assert(ptrIndex == [ &a[5], &a[1], &a[3] ]);
+}
+
+@system unittest
+{
+ import std.conv : text;
+
+ {
+ int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ];
+ int*[] b = new int*[5];
+ topNIndex!("a > b")(a, b, Yes.sortOutput);
+ assert(b == [ &a[0], &a[2], &a[1], &a[6], &a[5]]);
+ }
+ {
+ int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ];
+ auto b = new ubyte[5];
+ topNIndex!("a > b")(a, b, Yes.sortOutput);
+ assert(b == [ cast(ubyte) 0, cast(ubyte) 2, cast(ubyte) 1, cast(ubyte) 6, cast(ubyte) 5], text(b));
+ }
+}
+
+// medianOf
+/*
+Private for the time being.
+
+Computes the median of 2 to 5 arbitrary indexes in random-access range `r`
+using hand-written specialized algorithms. The indexes must be distinct (if not,
+behavior is implementation-defined). The function also partitions the elements
+involved around the median, e.g. $(D medianOf(r, a, b, c)) not only fills `r[b]`
+with the median of `r[a]`, `r[b]`, and `r[c]`, but also puts the minimum in
+`r[a]` and the maximum in `r[c]`.
+
+Params:
+less = The comparison predicate used, modeled as a
+ $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, strict weak ordering)
+ (irreflexive, antisymmetric, transitive, and implying a transitive equivalence).
+flag = Used only for even values of `T.length`. If `No.leanRight`, the median
+"leans left", meaning $(D medianOf(r, a, b, c, d)) puts the lower median of the
+four in `r[b]`, the minimum in `r[a]`, and the two others in `r[c]` and `r[d]`.
+Conversely, $(D median!("a < b", Yes.leanRight)(r, a, b, c, d)) puts the upper
+median of the four in `r[c]`, the maximum in `r[d]`, and the two others in
+`r[a]` and `r[b]`.
+r = The range containing the indexes.
+i = Two to five indexes inside `r`.
+*/
+private void medianOf(
+ alias less = "a < b",
+ Flag!"leanRight" flag = No.leanRight,
+ Range,
+ Indexes...)
+ (Range r, Indexes i)
+if (isRandomAccessRange!Range && hasLength!Range &&
+ Indexes.length >= 2 && Indexes.length <= 5 &&
+ allSatisfy!(isUnsigned, Indexes))
+{
+ assert(r.length >= Indexes.length);
+ import std.functional : binaryFun;
+ alias lt = binaryFun!less;
+ enum k = Indexes.length;
+ import std.algorithm.mutation : swapAt;
+
+ alias a = i[0];
+ static assert(is(typeof(a) == size_t));
+ static if (k >= 2)
+ {
+ alias b = i[1];
+ static assert(is(typeof(b) == size_t));
+ assert(a != b);
+ }
+ static if (k >= 3)
+ {
+ alias c = i[2];
+ static assert(is(typeof(c) == size_t));
+ assert(a != c && b != c);
+ }
+ static if (k >= 4)
+ {
+ alias d = i[3];
+ static assert(is(typeof(d) == size_t));
+ assert(a != d && b != d && c != d);
+ }
+ static if (k >= 5)
+ {
+ alias e = i[4];
+ static assert(is(typeof(e) == size_t));
+ assert(a != e && b != e && c != e && d != e);
+ }
+
+ static if (k == 2)
+ {
+ if (lt(r[b], r[a])) r.swapAt(a, b);
+ }
+ else static if (k == 3)
+ {
+ if (lt(r[c], r[a])) // c < a
+ {
+ if (lt(r[a], r[b])) // c < a < b
+ {
+ r.swapAt(a, b);
+ r.swapAt(a, c);
+ }
+ else // c < a, b <= a
+ {
+ r.swapAt(a, c);
+ if (lt(r[b], r[a])) r.swapAt(a, b);
+ }
+ }
+ else // a <= c
+ {
+ if (lt(r[b], r[a])) // b < a <= c
+ {
+ r.swapAt(a, b);
+ }
+ else // a <= c, a <= b
+ {
+ if (lt(r[c], r[b])) r.swapAt(b, c);
+ }
+ }
+ assert(!lt(r[b], r[a]));
+ assert(!lt(r[c], r[b]));
+ }
+ else static if (k == 4)
+ {
+ static if (flag == No.leanRight)
+ {
+ // Eliminate the rightmost from the competition
+ if (lt(r[d], r[c])) r.swapAt(c, d); // c <= d
+ if (lt(r[d], r[b])) r.swapAt(b, d); // b <= d
+ medianOf!lt(r, a, b, c);
+ }
+ else
+ {
+ // Eliminate the leftmost from the competition
+ if (lt(r[b], r[a])) r.swapAt(a, b); // a <= b
+ if (lt(r[c], r[a])) r.swapAt(a, c); // a <= c
+ medianOf!lt(r, b, c, d);
+ }
+ }
+ else static if (k == 5)
+ {
+ // Credit: Teppo Niinimäki
+ version (unittest) scope(success)
+ {
+ assert(!lt(r[c], r[a]));
+ assert(!lt(r[c], r[b]));
+ assert(!lt(r[d], r[c]));
+ assert(!lt(r[e], r[c]));
+ }
+
+ if (lt(r[c], r[a])) r.swapAt(a, c);
+ if (lt(r[d], r[b])) r.swapAt(b, d);
+ if (lt(r[d], r[c]))
+ {
+ r.swapAt(c, d);
+ r.swapAt(a, b);
+ }
+ if (lt(r[e], r[b])) r.swapAt(b, e);
+ if (lt(r[e], r[c]))
+ {
+ r.swapAt(c, e);
+ if (lt(r[c], r[a])) r.swapAt(a, c);
+ }
+ else
+ {
+ if (lt(r[c], r[b])) r.swapAt(b, c);
+ }
+ }
+}
+
+@safe unittest
+{
+ // Verify medianOf for all permutations of [1, 2, 2, 3, 4].
+ int[5] data = [1, 2, 2, 3, 4];
+ do
+ {
+ int[5] a = data;
+ medianOf(a[], size_t(0), size_t(1));
+ assert(a[0] <= a[1]);
+
+ a[] = data[];
+ medianOf(a[], size_t(0), size_t(1), size_t(2));
+ assert(ordered(a[0], a[1], a[2]));
+
+ a[] = data[];
+ medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3));
+ assert(a[0] <= a[1] && a[1] <= a[2] && a[1] <= a[3]);
+
+ a[] = data[];
+ medianOf!("a < b", Yes.leanRight)(a[], size_t(0), size_t(1),
+ size_t(2), size_t(3));
+ assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3]);
+
+ a[] = data[];
+ medianOf(a[], size_t(0), size_t(1), size_t(2), size_t(3), size_t(4));
+ assert(a[0] <= a[2] && a[1] <= a[2] && a[2] <= a[3] && a[2] <= a[4]);
+ }
+ while (nextPermutation(data[]));
+}
+
+// nextPermutation
+/**
+ * Permutes $(D range) in-place to the next lexicographically greater
+ * permutation.
+ *
+ * The predicate $(D less) defines the lexicographical ordering to be used on
+ * the range.
+ *
+ * If the range is currently the lexicographically greatest permutation, it is
+ * permuted back to the least permutation and false is returned. Otherwise,
+ * true is returned. One can thus generate all permutations of a range by
+ * sorting it according to $(D less), which produces the lexicographically
+ * least permutation, and then calling nextPermutation until it returns false.
+ * This is guaranteed to generate all distinct permutations of the range
+ * exactly once. If there are $(I N) elements in the range and all of them are
+ * unique, then $(I N)! permutations will be generated. Otherwise, if there are
+ * some duplicated elements, fewer permutations will be produced.
+----
+// Enumerate all permutations
+int[] a = [1,2,3,4,5];
+do
+{
+ // use the current permutation and
+ // proceed to the next permutation of the array.
+} while (nextPermutation(a));
+----
+ * Params:
+ * less = The ordering to be used to determine lexicographical ordering of the
+ * permutations.
+ * range = The range to permute.
+ *
+ * Returns: false if the range was lexicographically the greatest, in which
+ * case the range is reversed back to the lexicographically smallest
+ * permutation; otherwise returns true.
+ * See_Also:
+ * $(REF permutations, std,algorithm,iteration).
+ */
+bool nextPermutation(alias less="a < b", BidirectionalRange)
+ (BidirectionalRange range)
+if (isBidirectionalRange!BidirectionalRange &&
+ hasSwappableElements!BidirectionalRange)
+{
+ import std.algorithm.mutation : reverse, swap;
+ import std.algorithm.searching : find;
+ import std.range : retro, takeExactly;
+ // Ranges of 0 or 1 element have no distinct permutations.
+ if (range.empty) return false;
+
+ auto i = retro(range);
+ auto last = i.save;
+
+ // Find last occurring increasing pair of elements
+ size_t n = 1;
+ for (i.popFront(); !i.empty; i.popFront(), last.popFront(), n++)
+ {
+ if (binaryFun!less(i.front, last.front))
+ break;
+ }
+
+ if (i.empty)
+ {
+ // Entire range is decreasing: it's lexicographically the greatest. So
+ // wrap it around.
+ range.reverse();
+ return false;
+ }
+
+ // Find last element greater than i.front.
+ auto j = find!((a) => binaryFun!less(i.front, a))(
+ takeExactly(retro(range), n));
+
+ assert(!j.empty); // shouldn't happen since i.front < last.front
+ swap(i.front, j.front);
+ reverse(takeExactly(retro(range), n));
+
+ return true;
+}
+
+///
+@safe unittest
+{
+ // Step through all permutations of a sorted array in lexicographic order
+ int[] a = [1,2,3];
+ assert(nextPermutation(a) == true);
+ assert(a == [1,3,2]);
+ assert(nextPermutation(a) == true);
+ assert(a == [2,1,3]);
+ assert(nextPermutation(a) == true);
+ assert(a == [2,3,1]);
+ assert(nextPermutation(a) == true);
+ assert(a == [3,1,2]);
+ assert(nextPermutation(a) == true);
+ assert(a == [3,2,1]);
+ assert(nextPermutation(a) == false);
+ assert(a == [1,2,3]);
+}
+
+///
+@safe unittest
+{
+ // Step through permutations of an array containing duplicate elements:
+ int[] a = [1,1,2];
+ assert(nextPermutation(a) == true);
+ assert(a == [1,2,1]);
+ assert(nextPermutation(a) == true);
+ assert(a == [2,1,1]);
+ assert(nextPermutation(a) == false);
+ assert(a == [1,1,2]);
+}
+
+@safe unittest
+{
+ // Boundary cases: arrays of 0 or 1 element.
+ int[] a1 = [];
+ assert(!nextPermutation(a1));
+ assert(a1 == []);
+
+ int[] a2 = [1];
+ assert(!nextPermutation(a2));
+ assert(a2 == [1]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto a1 = [1, 2, 3, 4];
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 2, 4, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 3, 2, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 3, 4, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 4, 2, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 4, 3, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 1, 3, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 1, 4, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 3, 1, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 3, 4, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 4, 1, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 4, 3, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 1, 2, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 1, 4, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 2, 1, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 2, 4, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 4, 1, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 4, 2, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 1, 2, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 1, 3, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 2, 1, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 2, 3, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 3, 1, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 3, 2, 1]));
+
+ assert(!nextPermutation(a1));
+ assert(equal(a1, [1, 2, 3, 4]));
+}
+
+@safe unittest
+{
+ // Test with non-default sorting order
+ int[] a = [3,2,1];
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [3,1,2]);
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [2,3,1]);
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [2,1,3]);
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [1,3,2]);
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [1,2,3]);
+ assert(nextPermutation!"a > b"(a) == false);
+ assert(a == [3,2,1]);
+}
+
+// Issue 13594
+@safe unittest
+{
+ int[3] a = [1,2,3];
+ assert(nextPermutation(a[]));
+ assert(a == [1,3,2]);
+}
+
+// nextEvenPermutation
+/**
+ * Permutes $(D range) in-place to the next lexicographically greater $(I even)
+ * permutation.
+ *
+ * The predicate $(D less) defines the lexicographical ordering to be used on
+ * the range.
+ *
+ * An even permutation is one which is produced by swapping an even number of
+ * pairs of elements in the original range. The set of $(I even) permutations
+ * is distinct from the set of $(I all) permutations only when there are no
+ * duplicate elements in the range. If the range has $(I N) unique elements,
+ * then there are exactly $(I N)!/2 even permutations.
+ *
+ * If the range is already the lexicographically greatest even permutation, it
+ * is permuted back to the least even permutation and false is returned.
+ * Otherwise, true is returned, and the range is modified in-place to be the
+ * lexicographically next even permutation.
+ *
+ * One can thus generate the even permutations of a range with unique elements
+ * by starting with the lexicographically smallest permutation, and repeatedly
+ * calling nextEvenPermutation until it returns false.
+----
+// Enumerate even permutations
+int[] a = [1,2,3,4,5];
+do
+{
+ // use the current permutation and
+ // proceed to the next even permutation of the array.
+} while (nextEvenPermutation(a));
+----
+ * One can also generate the $(I odd) permutations of a range by noting that
+ * permutations obey the rule that even + even = even, and odd + even = odd.
+ * Thus, by swapping the last two elements of a lexicographically least range,
+ * it is turned into the first odd permutation. Then calling
+ * nextEvenPermutation on this first odd permutation will generate the next
+ * even permutation relative to this odd permutation, which is actually the
+ * next odd permutation of the original range. Thus, by repeatedly calling
+ * nextEvenPermutation until it returns false, one enumerates the odd
+ * permutations of the original range.
+----
+// Enumerate odd permutations
+int[] a = [1,2,3,4,5];
+swap(a[$-2], a[$-1]); // a is now the first odd permutation of [1,2,3,4,5]
+do
+{
+ // use the current permutation and
+ // proceed to the next odd permutation of the original array
+ // (which is an even permutation of the first odd permutation).
+} while (nextEvenPermutation(a));
+----
+ *
+ * Warning: Since even permutations are only distinct from all permutations
+ * when the range elements are unique, this function assumes that there are no
+ * duplicate elements under the specified ordering. If this is not _true, some
+ * permutations may fail to be generated. When the range has non-unique
+ * elements, you should use $(MYREF nextPermutation) instead.
+ *
+ * Params:
+ * less = The ordering to be used to determine lexicographical ordering of the
+ * permutations.
+ * range = The range to permute.
+ *
+ * Returns: false if the range was lexicographically the greatest, in which
+ * case the range is reversed back to the lexicographically smallest
+ * permutation; otherwise returns true.
+ */
+bool nextEvenPermutation(alias less="a < b", BidirectionalRange)
+ (BidirectionalRange range)
+if (isBidirectionalRange!BidirectionalRange &&
+ hasSwappableElements!BidirectionalRange)
+{
+ import std.algorithm.mutation : reverse, swap;
+ import std.algorithm.searching : find;
+ import std.range : retro, takeExactly;
+ // Ranges of 0 or 1 element have no distinct permutations.
+ if (range.empty) return false;
+
+ bool oddParity = false;
+ bool ret = true;
+ do
+ {
+ auto i = retro(range);
+ auto last = i.save;
+
+ // Find last occurring increasing pair of elements
+ size_t n = 1;
+ for (i.popFront(); !i.empty;
+ i.popFront(), last.popFront(), n++)
+ {
+ if (binaryFun!less(i.front, last.front))
+ break;
+ }
+
+ if (!i.empty)
+ {
+ // Find last element greater than i.front.
+ auto j = find!((a) => binaryFun!less(i.front, a))(
+ takeExactly(retro(range), n));
+
+ // shouldn't happen since i.front < last.front
+ assert(!j.empty);
+
+ swap(i.front, j.front);
+ oddParity = !oddParity;
+ }
+ else
+ {
+ // Entire range is decreasing: it's lexicographically
+ // the greatest.
+ ret = false;
+ }
+
+ reverse(takeExactly(retro(range), n));
+ if ((n / 2) % 2 == 1)
+ oddParity = !oddParity;
+ } while (oddParity);
+
+ return ret;
+}
+
+///
+@safe unittest
+{
+ // Step through even permutations of a sorted array in lexicographic order
+ int[] a = [1,2,3];
+ assert(nextEvenPermutation(a) == true);
+ assert(a == [2,3,1]);
+ assert(nextEvenPermutation(a) == true);
+ assert(a == [3,1,2]);
+ assert(nextEvenPermutation(a) == false);
+ assert(a == [1,2,3]);
+}
+
+@safe unittest
+{
+ auto a3 = [ 1, 2, 3, 4 ];
+ int count = 1;
+ while (nextEvenPermutation(a3)) count++;
+ assert(count == 12);
+}
+
+@safe unittest
+{
+ // Test with non-default sorting order
+ auto a = [ 3, 2, 1 ];
+
+ assert(nextEvenPermutation!"a > b"(a) == true);
+ assert(a == [ 2, 1, 3 ]);
+ assert(nextEvenPermutation!"a > b"(a) == true);
+ assert(a == [ 1, 3, 2 ]);
+ assert(nextEvenPermutation!"a > b"(a) == false);
+ assert(a == [ 3, 2, 1 ]);
+}
+
+@safe unittest
+{
+ // Test various cases of rollover
+ auto a = [ 3, 1, 2 ];
+ assert(nextEvenPermutation(a) == false);
+ assert(a == [ 1, 2, 3 ]);
+
+ auto b = [ 3, 2, 1 ];
+ assert(nextEvenPermutation(b) == false);
+ assert(b == [ 1, 3, 2 ]);
+}
+
+@safe unittest
+{
+ // Issue 13594
+ int[3] a = [1,2,3];
+ assert(nextEvenPermutation(a[]));
+ assert(a == [2,3,1]);
+}
+
+/**
+Even permutations are useful for generating coordinates of certain geometric
+shapes. Here's a non-trivial example:
+*/
+@safe unittest
+{
+ import std.math : sqrt;
+
+ // Print the 60 vertices of a uniform truncated icosahedron (soccer ball)
+ enum real Phi = (1.0 + sqrt(5.0)) / 2.0; // Golden ratio
+ real[][] seeds = [
+ [0.0, 1.0, 3.0*Phi],
+ [1.0, 2.0+Phi, 2.0*Phi],
+ [Phi, 2.0, Phi^^3]
+ ];
+ size_t n;
+ foreach (seed; seeds)
+ {
+ // Loop over even permutations of each seed
+ do
+ {
+ // Loop over all sign changes of each permutation
+ size_t i;
+ do
+ {
+ // Generate all possible sign changes
+ for (i=0; i < seed.length; i++)
+ {
+ if (seed[i] != 0.0)
+ {
+ seed[i] = -seed[i];
+ if (seed[i] < 0.0)
+ break;
+ }
+ }
+ n++;
+ } while (i < seed.length);
+ } while (nextEvenPermutation(seed));
+ }
+ assert(n == 60);
+}