// 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:
$(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 @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 and returns one of a collection of handlers based on the type of the
switch object.

The first choice that `switchObject` can be casted to the type
of argument it accepts will be called with `switchObject` casted to that
type, and the value it'll return will be returned by `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 `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 `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: `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); // 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 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");
}

// 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")));
}
