| // 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. | |
| `if (v.among(1, 2, 3)) // `v` is 1, 2 or 3`) | |
| $(T2 castSwitch, | |
| `(new A()).castSwitch((A a)=>1,(B b)=>2)` returns `1`.) | |
| $(T2 clamp, | |
| `clamp(1, 3, 6)` returns `3`. `clamp(4, 3, 6)` returns `4`.) | |
| $(T2 cmp, | |
| `cmp("abc", "abcd")` is `-1`, `cmp("abc", "aba")` is `1`, | |
| and `cmp("abc", "abc")` is `0`.) | |
| $(T2 either, | |
| Return first parameter `p` that passes an `if (p)` test, e.g. | |
| `either(0, 42, 43)` returns `42`.) | |
| $(T2 equal, | |
| Compares ranges for element-by-element equality, e.g. | |
| `equal([1, 2, 3], [1.0, 2.0, 3.0])` returns `true`.) | |
| $(T2 isPermutation, | |
| `isPermutation([1, 2], [2, 1])` returns `true`.) | |
| $(T2 isSameLength, | |
| `isSameLength([1, 2, 3], [4, 5, 6])` returns `true`.) | |
| $(T2 levenshteinDistance, | |
| `levenshteinDistance("kitten", "sitting")` returns `3` by using | |
| the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, | |
| Levenshtein distance algorithm).) | |
| $(T2 levenshteinDistanceAndPath, | |
| `levenshteinDistanceAndPath("kitten", "sitting")` returns | |
| `tuple(3, "snnnsni")` by using the | |
| $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, | |
| Levenshtein distance algorithm).) | |
| $(T2 max, | |
| `max(3, 4, 2)` returns `4`.) | |
| $(T2 min, | |
| `min(3, 4, 2)` returns `2`.) | |
| $(T2 mismatch, | |
| `mismatch("oh hi", "ohayo")` returns `tuple(" hi", "ayo")`.) | |
| $(T2 predSwitch, | |
| `2.predSwitch(1, "one", 2, "two", 3, "three")` returns `"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; | |
| import std.functional : unaryFun, binaryFun, lessThan, greaterThan; | |
| import std.range.primitives; | |
| import std.traits; | |
| import std.meta : allSatisfy, anySatisfy; | |
| import std.typecons : tuple, Tuple, Flag, Yes; | |
| import std.internal.attributes : betterC; | |
| /** | |
| Find `value` _among `values`, returning the 1-based index | |
| of the first matching value in `values`, or `0` if `value` | |
| is not _among `values`. The predicate `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: | |
| $(UL | |
| $(LI $(REF_SHORT find, std,algorithm,searching) and | |
| $(REF_SHORT canFind, std,algorithm,searching) for finding a value in a range.) | |
| $(LI $(REF_ALTTEXT `value in iota(start, end)`, iota, std,range) to find a value in | |
| a particular interval.) | |
| ) | |
| */ | |
| 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 @nogc @betterC 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, `values` can be passed at compile-time, allowing for a more | |
| efficient search, but one that only supports matching on equality: | |
| */ | |
| @safe @nogc @betterC 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 one of a series of handlers based on the dynamic type of a class instance. | |
| $(P | |
| `switchObject`'s dynamic type is checked for compatibility with the argument type of each | |
| choice in turn until a match is found. When there's a match, the choice will be called with | |
| `switchObject` cast to that type. If the choice returns a value, `castSwitch` will return it. | |
| ) | |
| - If one choice's return type is void and one can return a value, `SwitchError` | |
| will be thrown if the void handler was matched and completed execution without throwing. | |
| - A void handler is allowed to return when all the choices' return types convert to void - | |
| in that case, `castSwitch` itself will return void. | |
| - A choice's return type can be `noreturn` (e.g. when a handler always throws an exception). | |
| Throws: If none of the choices match, a `SwitchError` will be thrown. $(D | |
| SwitchError) will also be thrown if one choice can return a value but a void | |
| choice was matched and completed execution. | |
| Params: | |
| choices = A sequence of function and/or delegate | |
| handlers that each accept one argument. There can also be one choice that | |
| accepts zero arguments, which will be invoked if the $(D | |
| switchObject) is null. | |
| switchObject = the object against which the tests are being made. | |
| Returns: | |
| The return value of the selected choice. | |
| */ | |
| 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); // void or noreturn | |
| } | |
| 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 only have a class or interface argument type."); | |
| // 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; } | |
| } | |
| 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 noreturn/void handlers: | |
| @system unittest | |
| { | |
| import core.exception : SwitchError; | |
| import std.exception : assertThrown; | |
| class A { } | |
| class B { } | |
| // B's handler never returns, so `i` does not need a value | |
| int i; | |
| assertThrown!Exception( | |
| i = new B().castSwitch!( | |
| (A a) => 1, | |
| (B b) { throw new Exception("B is not allowed!"); } | |
| )() | |
| ); | |
| // Void handler call will throw if another handler returns a value | |
| assertThrown!SwitchError( | |
| i = new B().castSwitch!( | |
| (A a) => 1, | |
| (B b) {} | |
| )() | |
| ); | |
| // Void handlers are allowed if all the handlers convert to 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"); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=22384 | |
| @system unittest | |
| { | |
| // Use explicit methods to enforce return types | |
| static void objectSkip(Object) {} | |
| static void defaultSkip() {} | |
| static noreturn objectError(Object) { assert(false); } | |
| static noreturn defaultError() { assert(false); } | |
| { | |
| alias test = castSwitch!(objectSkip, defaultError); | |
| static assert(is(ReturnType!test == void)); | |
| }{ | |
| alias test = castSwitch!(objectError, defaultSkip); | |
| static assert(is(ReturnType!test == void)); | |
| }{ | |
| alias test = castSwitch!(objectError, defaultError); | |
| static assert(is(ReturnType!test == noreturn)); | |
| } | |
| // Also works with non-void handlers | |
| static int objectValue(Object) { return 1;} | |
| static int defaultValue() { return 2; } | |
| { | |
| alias test = castSwitch!(objectValue, defaultError); | |
| static assert(is(ReturnType!test == int)); | |
| }{ | |
| alias test = castSwitch!(objectError, defaultValue); | |
| static assert(is(ReturnType!test == int)); | |
| } | |
| // No confusion w.r.t. void callbacks | |
| alias FP = void function(); | |
| static FP objectFunc(Object) { return &defaultSkip; } | |
| static FP defaultFunc() { return &defaultSkip; } | |
| { | |
| alias test = castSwitch!(objectFunc, defaultError); | |
| static assert(is(ReturnType!test == FP)); | |
| }{ | |
| alias test = castSwitch!(objectError, defaultFunc); | |
| static assert(is(ReturnType!test == FP)); | |
| } | |
| } | |
| /** Clamps `val` into the given bounds. Result has the same type as `val`. | |
| Params: | |
| val = The value to _clamp. | |
| lower = The _lower bound of the _clamp. | |
| upper = The _upper bound of the _clamp. | |
| Returns: | |
| `lower` if `val` is less than `lower`, `upper` if `val` is greater than | |
| `upper`, and `val` in all other cases. Comparisons are made | |
| correctly (using $(REF lessThan, std,functional) and the return value | |
| is converted to the return type using the standard integer coversion rules | |
| $(REF greaterThan, std,functional)) even if the signedness of `T1`, `T2`, | |
| and `T3` are different. | |
| */ | |
| T1 clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper) | |
| { | |
| static assert(is(T2 : T1), "T2 of type '", T2.stringof | |
| , "' must be implicitly convertible to type of T1 '" | |
| , T1.stringof, "'"); | |
| static assert(is(T3 : T1), "T3 of type '", T3.stringof | |
| , "' must be implicitly convertible to type of T1 '" | |
| , T1.stringof, "'"); | |
| assert(!lower.greaterThan(upper), "Lower can't be greater than upper."); | |
| // `if (is(typeof(val.lessThan(lower) ? lower : val.greaterThan(upper) ? upper : val) : T1)) | |
| // because of https://issues.dlang.org/show_bug.cgi?id=16235. | |
| // Once that is fixed, we can simply use the ternary in both the template constraint | |
| // and the template body | |
| if (val.lessThan(lower)) | |
| return lower; | |
| else if (val.greaterThan(upper)) | |
| return upper; | |
| return val; | |
| } | |
| /// | |
| @safe @nogc @betterC 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); | |
| auto x = clamp(42, uint.max, uint.max); | |
| static assert(is(typeof(x) == int)); | |
| assert(x == -1); | |
| } | |
| @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)) == uint)); | |
| 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)); | |
| // Stability | |
| struct A { | |
| int x, y; | |
| int opCmp(ref const A rhs) const { return (x > rhs.x) - (x < rhs.x); } | |
| } | |
| A x, lo, hi; | |
| x.y = 42; | |
| assert(x.clamp(lo, hi).y == 42); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=23268 | |
| @safe pure nothrow @nogc unittest | |
| { | |
| static assert(__traits(compiles, clamp(short.init, short.init, cast(const) short.init))); | |
| } | |
| // cmp | |
| /********************************** | |
| Performs a lexicographical comparison on two | |
| $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives). | |
| Iterating `r1` and `r2` in lockstep, `cmp` compares each element | |
| `e1` of `r1` with the corresponding element `e2` in `r2`. If one | |
| of the ranges has been finished, `cmp` returns a negative value | |
| if `r1` has fewer elements than `r2`, a positive value if `r1` | |
| has more elements than `r2`, and `0` if the ranges have the same | |
| number of elements. | |
| If the ranges are strings, `cmp` performs UTF decoding | |
| appropriately and compares the ranges one code point at a time. | |
| A custom predicate may be specified, in which case `cmp` performs | |
| a three-way lexicographical comparison using `pred`. Otherwise | |
| the elements are compared using `opCmp`. | |
| Params: | |
| pred = Predicate used for comparison. Without a predicate | |
| specified the ordering implied by `opCmp` is used. | |
| r1 = The first range. | |
| r2 = The second range. | |
| Returns: | |
| `0` if the ranges compare equal. A negative value if `r1` is a prefix of `r2` or | |
| the first differing element of `r1` is less than the corresponding element of `r2` | |
| according to `pred`. A positive value if `r2` is a prefix of `r1` or the first | |
| differing element of `r2` is less than the corresponding element of `r1` | |
| according to `pred`. | |
| Note: | |
| An earlier version of the documentation incorrectly stated that `-1` is the | |
| only negative value returned and `1` is the only positive value returned. | |
| Whether that is true depends on the types being compared. | |
| */ | |
| auto cmp(R1, R2)(R1 r1, R2 r2) | |
| if (isInputRange!R1 && isInputRange!R2) | |
| { | |
| alias E1 = ElementEncodingType!R1; | |
| alias E2 = ElementEncodingType!R2; | |
| static if (isDynamicArray!R1 && isDynamicArray!R2 | |
| && __traits(isUnsigned, E1) && __traits(isUnsigned, E2) | |
| && E1.sizeof == 1 && E2.sizeof == 1 | |
| // Both or neither must auto-decode. | |
| && (is(immutable E1 == immutable char) == is(immutable E2 == immutable char))) | |
| { | |
| // dstrcmp algorithm is correct for both ubyte[] and for char[]. | |
| import core.internal.string : dstrcmp; | |
| return dstrcmp(cast(const char[]) r1, cast(const char[]) r2); | |
| } | |
| else static if (!(isSomeString!R1 && isSomeString!R2)) | |
| { | |
| for (;; r1.popFront(), r2.popFront()) | |
| { | |
| static if (is(typeof(r1.front.opCmp(r2.front)) R)) | |
| alias Result = R; | |
| else | |
| alias Result = int; | |
| if (r2.empty) return Result(!r1.empty); | |
| if (r1.empty) return Result(-1); | |
| static if (is(typeof(r1.front.opCmp(r2.front)))) | |
| { | |
| auto c = r1.front.opCmp(r2.front); | |
| if (c != 0) return c; | |
| } | |
| else | |
| { | |
| auto a = r1.front, b = r2.front; | |
| if (auto result = (b < a) - (a < b)) return result; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof) | |
| { | |
| return () @trusted | |
| { | |
| auto p1 = r1.ptr, p2 = r2.ptr, | |
| pEnd = p1 + min(r1.length, r2.length); | |
| for (; p1 != pEnd; ++p1, ++p2) | |
| { | |
| if (*p1 != *p2) return cast(int) *p1 - cast(int) *p2; | |
| } | |
| static if (typeof(r1[0]).sizeof >= 2 && size_t.sizeof <= uint.sizeof) | |
| return cast(int) r1.length - cast(int) r2.length; | |
| else | |
| return int(r1.length > r2.length) - int(r1.length < r2.length); | |
| }(); | |
| } | |
| else | |
| { | |
| import std.utf : decode; | |
| for (size_t i1, i2;;) | |
| { | |
| if (i1 == r1.length) return -int(i2 < r2.length); | |
| if (i2 == r2.length) return int(1); | |
| immutable c1 = decode(r1, i1), | |
| c2 = decode(r2, i2); | |
| if (c1 != c2) return cast(int) c1 - cast(int) c2; | |
| } | |
| } | |
| } | |
| } | |
| /// ditto | |
| int cmp(alias pred, R1, R2)(R1 r1, R2 r2) | |
| if (isInputRange!R1 && isInputRange!R2) | |
| { | |
| static if (!(isSomeString!R1 && isSomeString!R2)) | |
| { | |
| for (;; r1.popFront(), r2.popFront()) | |
| { | |
| if (r2.empty) return !r1.empty; | |
| if (r1.empty) return -1; | |
| auto a = r1.front, b = r2.front; | |
| if (binaryFun!pred(a, b)) return -1; | |
| if (binaryFun!pred(b, a)) return 1; | |
| } | |
| } | |
| else | |
| { | |
| import std.utf : decode; | |
| for (size_t i1, i2;;) | |
| { | |
| if (i1 == r1.length) return -int(i2 < r2.length); | |
| if (i2 == r2.length) return 1; | |
| immutable c1 = decode(r1, i1), | |
| c2 = decode(r2, i2); | |
| if (c1 != c2) | |
| { | |
| if (binaryFun!pred(c2, c1)) return 1; | |
| if (binaryFun!pred(c1, c2)) return -1; | |
| } | |
| } | |
| } | |
| } | |
| /// | |
| pure @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("aaa"d, "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); | |
| } | |
| /// Example predicate that compares individual elements in reverse lexical order | |
| pure @safe unittest | |
| { | |
| int result; | |
| result = cmp!"a > b"("abc", "abc"); | |
| assert(result == 0); | |
| result = cmp!"a > b"("", ""); | |
| assert(result == 0); | |
| result = cmp!"a > b"("abc", "abcd"); | |
| assert(result < 0); | |
| result = cmp!"a > b"("abcd", "abc"); | |
| assert(result > 0); | |
| result = cmp!"a > b"("abc"d, "abd"); | |
| assert(result > 0); | |
| result = cmp!"a > b"("bbc", "abc"w); | |
| assert(result < 0); | |
| result = cmp!"a > b"("aaa", "aaaa"d); | |
| assert(result < 0); | |
| result = cmp!"a > b"("aaaa", "aaa"d); | |
| assert(result > 0); | |
| result = cmp!"a > b"("aaa", "aaa"d); | |
| assert(result == 0); | |
| result = cmp("aaa"d, "aaa"d); | |
| assert(result == 0); | |
| result = cmp!"a > b"(cast(int[])[], cast(int[])[]); | |
| assert(result == 0); | |
| result = cmp!"a > b"([1, 2, 3], [1, 2, 3]); | |
| assert(result == 0); | |
| result = cmp!"a > b"([1, 3, 2], [1, 2, 3]); | |
| assert(result < 0); | |
| result = cmp!"a > b"([1, 2, 3], [1L, 2, 3, 4]); | |
| assert(result < 0); | |
| result = cmp!"a > b"([1L, 2, 3], [1, 2]); | |
| assert(result > 0); | |
| } | |
| // cmp for string with custom predicate fails if distinct chars can compare equal | |
| // https://issues.dlang.org/show_bug.cgi?id=18286 | |
| @nogc nothrow pure @safe unittest | |
| { | |
| static bool ltCi(dchar a, dchar b)// less than, case insensitive | |
| { | |
| import std.ascii : toUpper; | |
| return toUpper(a) < toUpper(b); | |
| } | |
| static assert(cmp!ltCi("apple2", "APPLE1") > 0); | |
| static assert(cmp!ltCi("apple1", "APPLE2") < 0); | |
| static assert(cmp!ltCi("apple", "APPLE1") < 0); | |
| static assert(cmp!ltCi("APPLE", "apple1") < 0); | |
| static assert(cmp!ltCi("apple", "APPLE") == 0); | |
| } | |
| // for non-string ranges check that opCmp is evaluated only once per pair. | |
| // https://issues.dlang.org/show_bug.cgi?id=18280 | |
| @nogc nothrow @safe unittest | |
| { | |
| static int ctr = 0; | |
| struct S | |
| { | |
| int opCmp(ref const S rhs) const | |
| { | |
| ++ctr; | |
| return 0; | |
| } | |
| bool opEquals(T)(T o) const { return false; } | |
| size_t toHash() const { return 0; } | |
| } | |
| immutable S[4] a; | |
| immutable S[4] b; | |
| immutable result = cmp(a[], b[]); | |
| assert(result == 0, "neither should compare greater than the other!"); | |
| assert(ctr == a.length, "opCmp should be called exactly once per pair of items!"); | |
| } | |
| nothrow pure @safe @nogc unittest | |
| { | |
| import std.array : staticArray; | |
| // Test cmp when opCmp returns float. | |
| struct F | |
| { | |
| float value; | |
| float opCmp(const ref F rhs) const | |
| { | |
| return value - rhs.value; | |
| } | |
| bool opEquals(T)(T o) const { return false; } | |
| size_t toHash() const { return 0; } | |
| } | |
| auto result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2), F(3)].staticArray[]); | |
| assert(result == 0); | |
| assert(is(typeof(result) == float)); | |
| result = cmp([F(1), F(3), F(2)].staticArray[], [F(1), F(2), F(3)].staticArray[]); | |
| assert(result > 0); | |
| result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2), F(3), F(4)].staticArray[]); | |
| assert(result < 0); | |
| result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2)].staticArray[]); | |
| assert(result > 0); | |
| } | |
| nothrow pure @safe unittest | |
| { | |
| // Parallelism (was broken by inferred return type "immutable int") | |
| import std.parallelism : task; | |
| auto t = task!cmp("foo", "bar"); | |
| } | |
| // equal | |
| /** | |
| Compares two or more ranges for equality, as defined by predicate `pred` | |
| (which is `==` by default). | |
| */ | |
| template equal(alias pred = "a == b") | |
| { | |
| /++ | |
| Compares two or more ranges for equality. The ranges may have | |
| different element types, as long as all are comparable by means of | |
| the `pred`. | |
| Performs $(BIGOH min(rs[0].length, rs[1].length, ...)) evaluations of `pred`. However, if | |
| `equal` is invoked with the default predicate, the implementation may take the liberty | |
| to use faster implementations that have the theoretical worst-case | |
| $(BIGOH max(rs[0].length, rs[1].length, ...)). | |
| At least one of the ranges must be finite. If one range involved is infinite, the result is | |
| (statically known to be) `false`. | |
| If the ranges have different kinds of UTF code unit (`char`, `wchar`, or | |
| `dchar`), then they are compared using UTF decoding to avoid | |
| accidentally integer-promoting units. | |
| Params: | |
| rs = The ranges to be compared. | |
| Returns: | |
| `true` if and only if all ranges compare _equal element | |
| for element, according to binary predicate `pred`. | |
| +/ | |
| bool equal(Ranges...)(Ranges rs) | |
| if (rs.length > 1 | |
| && allSatisfy!(isInputRange, Ranges) | |
| && !allSatisfy!(isInfinite, Ranges) | |
| && is(typeof(binaryFun!pred(rs[0].front, rs[1].front))) | |
| && (rs.length == 2 || is(typeof(equal!pred(rs[1 .. $])) == bool)) | |
| ) | |
| { | |
| alias ElementEncodingTypes = staticMap!(ElementEncodingType, Ranges); | |
| enum differentSize(T) = T.sizeof != ElementEncodingTypes[0].sizeof; | |
| enum useCodePoint = allSatisfy!(isSomeChar, ElementEncodingTypes) && | |
| anySatisfy!(differentSize, ElementEncodingTypes); | |
| enum bool comparableWithEq(alias r) = is(typeof(rs[0] == r)); | |
| static if (anySatisfy!(isInfinite, Ranges)) | |
| { | |
| return false; | |
| } | |
| else static if (useCodePoint) | |
| { | |
| import std.utf : byDchar; | |
| static bool allByDchar(size_t done, Ranges...)(auto ref Ranges rs) | |
| { | |
| static if (done == rs.length) | |
| return equalLoop(rs); | |
| else | |
| return allByDchar!(done + 1)(rs[0 .. done], rs[done].byDchar, rs[done + 1 .. $]); | |
| } | |
| return allByDchar!0(rs); | |
| } | |
| else static if (is(typeof(pred) == string) && pred == "a == b" && | |
| allSatisfy!(isArray, Ranges) && allSatisfy!(comparableWithEq, rs)) | |
| { | |
| static foreach (r; rs[1 .. $]) | |
| if (rs[0] != r) | |
| return false; | |
| return true; | |
| } | |
| // 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 | |
| // TODO: generalize this | |
| else static if (rs.length == 2 && is(typeof(pred) == string) && pred == "a == b" && | |
| isAutodecodableString!(Ranges[0]) != isAutodecodableString!(Ranges[1]) && | |
| is(immutable ElementEncodingType!(Ranges[0]) == immutable ElementEncodingType!(Ranges[1]))) | |
| { | |
| import std.utf : byCodeUnit; | |
| static if (isAutodecodableString!(Ranges[0])) | |
| return equal(rs[0].byCodeUnit, rs[1]); | |
| else | |
| return equal(rs[1].byCodeUnit, rs[0]); | |
| } | |
| else | |
| { | |
| static foreach (i, R; Ranges) | |
| { | |
| static if (hasLength!R) | |
| { | |
| static if (!is(typeof(firstLength))) | |
| { | |
| // Found the first range that has length | |
| auto firstLength = rs[i].length; | |
| } | |
| else | |
| { | |
| // Compare the length of the current range against the first with length | |
| if (firstLength != rs[i].length) | |
| return false; | |
| } | |
| } | |
| } | |
| return equalLoop(rs); | |
| } | |
| } | |
| private bool equalLoop(Rs...)(ref Rs rs) | |
| { | |
| for (; !rs[0].empty; rs[0].popFront) | |
| static foreach (r; rs[1 .. $]) | |
| if (r.empty || !binaryFun!pred(rs[0].front, r.front)) | |
| return false; | |
| else | |
| r.popFront; | |
| static foreach (r; rs[1 .. $]) | |
| if (!r.empty) | |
| return false; | |
| return true; | |
| } | |
| } | |
| /// | |
| @safe @nogc unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.math.operations : isClose; | |
| int[4] 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[4] b = [ 1.0, 2, 4, 3]; | |
| assert(!equal(a[], b[1..$])); | |
| assert(equal(a[], b[])); | |
| // predicated: ensure that two vectors are approximately equal | |
| double[4] c = [ 1.0000000005, 2, 4, 3]; | |
| assert(equal!isClose(b[], c[])); | |
| } | |
| @safe @nogc unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.math.operations : isClose; | |
| auto s1 = "abc", s2 = "abc"w; | |
| assert(equal(s1, s2, s2)); | |
| assert(equal(s1, s2, s2, s1)); | |
| assert(!equal(s1, s2, s2[1 .. $])); | |
| int[4] a = [ 1, 2, 4, 3 ]; | |
| assert(!equal(a[], a[1..$], a[])); | |
| assert(equal(a[], a[], a[])); | |
| assert(equal!((a, b) => a == b)(a[], a[], a[])); | |
| // different types | |
| double[4] b = [ 1.0, 2, 4, 3]; | |
| assert(!equal(a[], b[1..$], b[])); | |
| assert(equal(a[], b[], a[], b[])); | |
| // predicated: ensure that two vectors are approximately equal | |
| double[4] c = [ 1.0000000005, 2, 4, 3]; | |
| assert(equal!isClose(b[], c[], b[])); | |
| } | |
| /++ | |
| Tip: `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, `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.operations : isClose; | |
| // 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.0000000005, 2, 4, 3]; | |
| assert(equal!isClose(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!isClose(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 @nogc pure unittest | |
| { | |
| import std.utf : byChar, byDchar, byWchar; | |
| assert(equal("æøå".byChar, "æøå")); | |
| assert(equal("æøå".byChar, "æøå"w)); | |
| assert(equal("æøå".byChar, "æøå"d)); | |
| assert(equal("æøå", "æøå".byChar)); | |
| assert(equal("æøå"w, "æøå".byChar)); | |
| assert(equal("æøå"d, "æøå".byChar)); | |
| assert(equal("æøå".byWchar, "æøå")); | |
| assert(equal("æøå".byWchar, "æøå"w)); | |
| assert(equal("æøå".byWchar, "æøå"d)); | |
| assert(equal("æøå", "æøå".byWchar)); | |
| assert(equal("æøå"w, "æøå".byWchar)); | |
| assert(equal("æøå"d, "æøå".byWchar)); | |
| assert(equal("æøå".byDchar, "æøå")); | |
| assert(equal("æøå".byDchar, "æøå"w)); | |
| assert(equal("æøå".byDchar, "æøå"d)); | |
| assert(equal("æøå", "æøå".byDchar)); | |
| assert(equal("æøå"w, "æøå".byDchar)); | |
| assert(equal("æøå"d, "æøå".byDchar)); | |
| } | |
| @safe @nogc 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())); | |
| } | |
| // levenshteinDistance | |
| /** | |
| Encodes $(HTTP realityinteractive.com/rgrzywinski/archives/000249.html, | |
| edit operations) necessary to transform one sequence into | |
| another. Given sequences `s` (source) and `t` (target), a | |
| sequence of `EditOp` encodes the steps that need to be taken to | |
| convert `s` into `t`. For example, if `s = "cat"` and $(D | |
| "cars"), the minimal sequence that transforms `s` into `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); | |
| assert(!overflow, "Overflow during multiplication to determine number " | |
| ~ " of matrix elements"); | |
| 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); | |
| assert(!overflow, "Overflow during multiplication to determine " | |
| ~ " number of bytes of matrix"); | |
| 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 `s` and `t`. The Levenshtein distance computes | |
| the minimal amount of edit operations necessary to transform `s` | |
| into `t`. Performs $(BIGOH s.length * t.length) evaluations of $(D | |
| equals) and occupies $(BIGOH min(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 `s` and | |
| `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 returns the maximum value. | |
| Params: | |
| args = The values to select the maximum from. At least two arguments must | |
| be passed, and they must be comparable with `<`. | |
| Returns: | |
| The maximum of the passed-in values. The type of the returned value is | |
| the type among the passed arguments that is able to store the largest value. | |
| If at least one of the arguments is NaN, the result is an unspecified value. | |
| See $(REF maxElement, std,algorithm,searching) for examples on how to cope | |
| with NaNs. | |
| See_Also: | |
| $(REF maxElement, std,algorithm,searching) | |
| */ | |
| auto max(T...)(T args) | |
| if (T.length >= 2 && !is(CommonType!T == void)) | |
| { | |
| // Get left-hand side of the comparison. | |
| static if (T.length == 2) | |
| alias a = args[0]; | |
| else | |
| auto a = max(args[0 .. ($ + 1) / 2]); | |
| alias T0 = typeof(a); | |
| // Get right-hand side. | |
| static if (T.length <= 3) | |
| alias b = args[$ - 1]; | |
| else | |
| auto b = max(args[($ + 1) / 2 .. $]); | |
| alias T1 = typeof(b); | |
| static assert(is(typeof(a < b)), | |
| "Invalid arguments: Cannot compare types " ~ T0.stringof ~ | |
| " and " ~ T1.stringof ~ " for ordering."); | |
| // Compute the returned type. | |
| static if (is(typeof(mostNegative!T0 < mostNegative!T1))) | |
| // Both are numeric (or character or Boolean), so we choose the one with the highest maximum. | |
| // (We use mostNegative for num/bool/char testing purposes even if it's not used otherwise.) | |
| alias Result = Select!(T1.max > T0.max, T1, T0); | |
| else | |
| // At least one is non-numeric, so just go with the common type. | |
| alias Result = CommonType!(T0, T1); | |
| // Perform the computation. | |
| import std.functional : lessThan; | |
| immutable chooseB = lessThan!(T0, T1)(a, b); | |
| return cast(Result) (chooseB ? b : a); | |
| } | |
| /// ditto | |
| T max(T, U)(T a, U b) | |
| if (is(T == U) && is(typeof(a < b))) | |
| { | |
| /* Handle the common case without all the template expansions | |
| * of the general case | |
| */ | |
| return a < b ? b : a; | |
| } | |
| /// | |
| @safe @betterC @nogc 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 // not @nogc due to `Date` | |
| { | |
| 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); | |
| } | |
| // 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. The type of the returned value is | |
| the type among the passed arguments that is able to store the smallest value. | |
| If at least one of the arguments is NaN, the result is an unspecified value. | |
| See $(REF minElement, std,algorithm,searching) for examples on how to cope | |
| with NaNs. | |
| See_Also: | |
| $(REF minElement, std,algorithm,searching) | |
| */ | |
| auto min(T...)(T args) | |
| if (T.length >= 2 && !is(CommonType!T == void)) | |
| { | |
| // Get the left-hand side of the comparison. | |
| static if (T.length <= 2) | |
| alias a = args[0]; | |
| else | |
| auto a = min(args[0 .. ($ + 1) / 2]); | |
| alias T0 = typeof(a); | |
| // Get the right-hand side. | |
| static if (T.length <= 3) | |
| alias b = args[$ - 1]; | |
| else | |
| auto b = min(args[($ + 1) / 2 .. $]); | |
| alias T1 = typeof(b); | |
| static assert(is(typeof(a < b)), | |
| "Invalid arguments: Cannot compare types " ~ T0.stringof ~ | |
| " and " ~ T1.stringof ~ " for ordering."); | |
| // Compute the returned type. | |
| static if (is(typeof(mostNegative!T0 < mostNegative!T1))) | |
| // Both are numeric (or character or Boolean), so we choose the one with the lowest minimum. | |
| // If they have the same minimum, choose the one with the smallest size. | |
| // If both mostNegative and sizeof are equal, go for stability: pick the type of the first one. | |
| alias Result = Select!(mostNegative!T1 < mostNegative!T0 || | |
| mostNegative!T1 == mostNegative!T0 && T1.sizeof < T0.sizeof, | |
| T1, T0); | |
| else | |
| // At least one is non-numeric, so just go with the common type. | |
| alias Result = CommonType!(T0, T1); | |
| // Engage! | |
| import std.functional : lessThan; | |
| immutable chooseB = lessThan!(T1, T0)(b, a); | |
| return cast(Result) (chooseB ? b : a); | |
| } | |
| /// ditto | |
| T min(T, U)(T a, U b) | |
| if (is(T == U) && is(typeof(a < b))) | |
| { | |
| /* Handle the common case without all the template expansions | |
| * of the general case | |
| */ | |
| return b < a ? b : a; | |
| } | |
| /// | |
| @safe @nogc @betterC 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); | |
| ulong f = 0xffff_ffff_ffff; | |
| const uint g = min(f, 0xffff_0000); | |
| assert(g == 0xffff_0000); | |
| dchar h = 100; | |
| uint i = 101; | |
| static assert(is(typeof(min(h, i)) == dchar)); | |
| static assert(is(typeof(min(i, h)) == uint)); | |
| assert(min(h, i) == 100); | |
| } | |
| /** | |
| With arguments of mixed signedness, the return type is the one that can | |
| store the lowest values. | |
| */ | |
| @safe @nogc @betterC unittest | |
| { | |
| int 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. | |
| @safe unittest // not @nogc due to `Date` | |
| { | |
| 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); | |
| } | |
| // min must be stable: when in doubt, return the first argument. | |
| @safe unittest | |
| { | |
| assert(min(1.0, double.nan) == 1.0); | |
| assert(min(double.nan, 1.0) is double.nan); | |
| static struct A { | |
| int x; | |
| string y; | |
| int opCmp(const A a) const { return int(x > a.x) - int(x < a.x); } | |
| } | |
| assert(min(A(1, "first"), A(1, "second")) == A(1, "first")); | |
| } | |
| // mismatch | |
| /** | |
| Sequentially compares elements in `rs` in lockstep, and | |
| stops at the first mismatch (according to `pred`, by default | |
| equality). Returns a tuple with the reduced ranges that start with the | |
| two mismatched values. Performs $(BIGOH min(r[0].length, r[1].length, ...)) | |
| evaluations of `pred`. | |
| */ | |
| Tuple!(Ranges) | |
| mismatch(alias pred = (a, b) => a == b, Ranges...)(Ranges rs) | |
| if (rs.length >= 2 && allSatisfy!(isInputRange, Ranges)) | |
| { | |
| loop: for (; !rs[0].empty; rs[0].popFront) | |
| { | |
| static foreach (r; rs[1 .. $]) | |
| { | |
| if (r.empty || !binaryFun!pred(rs[0].front, r.front)) | |
| break loop; | |
| r.popFront; | |
| } | |
| } | |
| return tuple(rs); | |
| } | |
| /// | |
| @safe @nogc unittest | |
| { | |
| int[6] x = [ 1, 5, 2, 7, 4, 3 ]; | |
| double[6] y = [ 1.0, 5, 2, 7.3, 4, 8 ]; | |
| auto m = mismatch(x[], y[]); | |
| assert(m[0] == x[3 .. $]); | |
| assert(m[1] == y[3 .. $]); | |
| auto m2 = mismatch(x[], y[], x[], y[]); | |
| assert(m2[0] == x[3 .. $]); | |
| assert(m2[1] == y[3 .. $]); | |
| assert(m2[2] == x[3 .. $]); | |
| assert(m2[3] == y[3 .. $]); | |
| } | |
| @safe @nogc unittest | |
| { | |
| import std.range : only; | |
| int[3] a = [ 1, 2, 3 ]; | |
| int[4] b = [ 1, 2, 4, 5 ]; | |
| auto mm = mismatch(a[], b[]); | |
| assert(equal(mm[0], only(3))); | |
| assert(equal(mm[1], only(4, 5))); | |
| } | |
| /** | |
| Returns one of a collection of expressions based on the value of the switch | |
| expression. | |
| `choices` needs to be composed of pairs of test expressions and return | |
| expressions. Each test-expression is compared with `switchExpression` using | |
| `pred`(`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 - `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 two or more ranges have the same number of elements. This function is | |
| optimized to always take advantage of the `length` member of either range | |
| if it exists. | |
| If all ranges have a `length` member or at least one is infinite, | |
| `_isSameLength`'s complexity is $(BIGOH 1). Otherwise, complexity is | |
| $(BIGOH n), where `n` is the smallest of the lengths of ranges with unknown | |
| length. | |
| Infinite ranges are considered of the same length. An infinite range has never | |
| the same length as a finite range. | |
| Params: | |
| rs = two or more $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) | |
| Returns: | |
| `true` if both ranges have the same length, `false` otherwise. | |
| */ | |
| bool isSameLength(Ranges...)(Ranges rs) | |
| if (allSatisfy!(isInputRange, Ranges)) | |
| { | |
| static if (anySatisfy!(isInfinite, Ranges)) | |
| { | |
| return allSatisfy!(isInfinite, Ranges); | |
| } | |
| else static if (anySatisfy!(hasLength, Ranges)) | |
| { | |
| // Compute the O(1) length | |
| auto baselineLength = size_t.max; | |
| static foreach (i, R; Ranges) | |
| { | |
| static if (hasLength!R) | |
| { | |
| if (baselineLength == size_t.max) | |
| baselineLength = rs[i].length; | |
| else if (rs[i].length != baselineLength) | |
| return false; | |
| } | |
| } | |
| // Iterate all ranges without known length | |
| foreach (_; 0 .. baselineLength) | |
| static foreach (i, R; Ranges) | |
| { | |
| static if (!hasLength!R) | |
| { | |
| // All must be non-empty | |
| if (rs[i].empty) | |
| return false; | |
| rs[i].popFront; | |
| } | |
| } | |
| static foreach (i, R; Ranges) | |
| { | |
| static if (!hasLength!R) | |
| { | |
| // All must be now empty | |
| if (!rs[i].empty) | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| else | |
| { | |
| // All have unknown length, iterate in lockstep | |
| for (;;) | |
| static foreach (i, r; rs) | |
| { | |
| if (r.empty) | |
| { | |
| // One is empty, so all must be empty | |
| static if (i != 0) | |
| { | |
| return false; | |
| } | |
| else | |
| { | |
| static foreach (j, r1; rs[1 .. $]) | |
| if (!r1.empty) | |
| return false; | |
| return true; | |
| } | |
| } | |
| r.popFront; | |
| } | |
| } | |
| } | |
| /// | |
| @safe nothrow pure unittest | |
| { | |
| assert(isSameLength([1, 2, 3], [4, 5, 6])); | |
| assert(isSameLength([1, 2, 3], [4, 5, 6], [7, 8, 9])); | |
| assert(isSameLength([0.3, 90.4, 23.7, 119.2], [42.6, 23.6, 95.5, 6.3])); | |
| assert(isSameLength("abc", "xyz")); | |
| assert(isSameLength("abc", "xyz", [1, 2, 3])); | |
| int[] a; | |
| int[] b; | |
| assert(isSameLength(a, b)); | |
| assert(isSameLength(a, b, a, a, b, b, b)); | |
| assert(!isSameLength([1, 2, 3], [4, 5])); | |
| assert(!isSameLength([1, 2, 3], [4, 5, 6], [7, 8])); | |
| assert(!isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3])); | |
| assert(!isSameLength("abcd", "xyz")); | |
| assert(!isSameLength("abcd", "xyz", "123")); | |
| assert(!isSameLength("abcd", "xyz", "1234")); | |
| } | |
| // Test CTFE | |
| @safe @nogc pure @betterC unittest | |
| { | |
| static assert(isSameLength([1, 2, 3], [4, 5, 6])); | |
| static assert(isSameLength([1, 2, 3], [4, 5, 6], [7, 8, 9])); | |
| static assert(!isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3])); | |
| static assert(!isSameLength([1], [0.3, 90.4], [42])); | |
| } | |
| @safe @nogc pure unittest | |
| { | |
| import std.range : only; | |
| assert(isSameLength(only(1, 2, 3), only(4, 5, 6))); | |
| assert(isSameLength(only(1, 2, 3), only(4, 5, 6), only(7, 8, 9))); | |
| assert(isSameLength(only(0.3, 90.4, 23.7, 119.2), only(42.6, 23.6, 95.5, 6.3))); | |
| assert(!isSameLength(only(1, 3, 3), only(4, 5))); | |
| assert(!isSameLength(only(1, 3, 3), only(1, 3, 3), only(4, 5))); | |
| assert(!isSameLength(only(1, 3, 3), only(4, 5), only(1, 3, 3))); | |
| } | |
| @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)); | |
| import std.algorithm.iteration : filter; | |
| assert(isSameLength(filter!"a >= 1"([1, 2, 3]), [4, 5, 6])); | |
| assert(!isSameLength(filter!"a > 1"([1, 2, 3]), [4, 5, 6])); | |
| assert(isSameLength(filter!"a > 1"([1, 2, 3]), filter!"a > 4"([4, 5, 6]))); | |
| assert(isSameLength(filter!"a > 1"([1, 2, 3]), | |
| filter!"a > 4"([4, 5, 6]), filter!"a >= 5"([4, 5, 6]))); | |
| } | |
| // Still functional but not documented anymore. | |
| alias AllocateGC = Flag!"allocateGC"; | |
| /** | |
| Checks if both ranges are permutations of each other. | |
| This function can allocate if the `Yes.allocateGC` flag is passed. This has | |
| the benefit of have better complexity than the `Yes.allocateGC` option. However, | |
| this option is only available for ranges whose equality can be determined via each | |
| element's `toHash` method. If customized equality is needed, then the `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 `pred`. | |
| Non-allocating forward range option: $(BIGOH n^2) | |
| Non-allocating forward range option with custom `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 | |
| allocateGC = `Yes.allocateGC`/`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: | |
| `true` if all of the elements in `r1` appear the same number of times in `r2`. | |
| Otherwise, returns `false`. | |
| */ | |
| bool isPermutation(Flag!"allocateGC" allocateGC, Range1, Range2) | |
| (Range1 r1, Range2 r2) | |
| if (allocateGC == 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 @betterC 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); | |
| } | |
| /// | |
| @safe pure unittest | |
| { | |
| 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") == ""); | |
| } | |
| /// | |
| @safe pure unittest | |
| { | |
| 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"))); | |
| } |