diff options
author | Iain Buclaw <ibuclaw@gcc.gnu.org> | 2018-10-28 19:51:47 +0000 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gcc.gnu.org> | 2018-10-28 19:51:47 +0000 |
commit | b4c522fabd0df7be08882d2207df8b2765026110 (patch) | |
tree | b5ffc312b0a441c1ba24323152aec463fdbe5e9f /libphobos/src/std/algorithm | |
parent | 01ce9e31a02c8039d88e90f983735104417bf034 (diff) | |
download | gcc-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.d | 2159 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/internal.d | 77 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/iteration.d | 5187 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/mutation.d | 2909 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/package.d | 198 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/searching.d | 4600 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/setops.d | 1521 | ||||
-rw-r--r-- | libphobos/src/std/algorithm/sorting.d | 4468 |
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); +} |