// Written in the D programming language.

/**
Functions that manipulate other functions.

This module provides functions for compile time function composition. These
functions are helpful when constructing predicates for the algorithms in
$(MREF std, algorithm) or $(MREF std, range).

$(SCRIPT inhibitQuickIndex = 1;)
$(DIVC quickindex,
$(BOOKTABLE ,
$(TR $(TH Function Name) $(TH Description)
)
    $(TR $(TD $(LREF adjoin))
        $(TD Joins a couple of functions into one that executes the original
        functions independently and returns a tuple with all the results.
    ))
    $(TR $(TD $(LREF compose), $(LREF pipe))
        $(TD Join a couple of functions into one that executes the original
        functions one after the other, using one function's result for the next
        function's argument.
    ))
    $(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo))
        $(TD Ready-made predicate functions to compare two values.
    ))
    $(TR $(TD $(LREF memoize))
        $(TD Creates a function that caches its result for fast re-evaluation.
    ))
    $(TR $(TD $(LREF not))
        $(TD Creates a function that negates another.
    ))
    $(TR $(TD $(LREF partial))
        $(TD Creates a function that binds the first argument of a given function
        to a given value.
    ))
    $(TR $(TD $(LREF curry))
        $(TD Converts a multi-argument function into a series of single-argument
        functions.  f(x, y) == curry(f)(x)(y)
    ))
    $(TR $(TD $(LREF reverseArgs))
        $(TD Predicate that reverses the order of its arguments.
    ))
    $(TR $(TD $(LREF toDelegate))
        $(TD Converts a callable to a delegate.
    ))
    $(TR $(TD $(LREF unaryFun), $(LREF binaryFun))
        $(TD Create a unary or binary function from a string. Most often
        used when defining algorithms on ranges.
    ))
))

Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors:   $(HTTP erdani.org, Andrei Alexandrescu)
Source:    $(PHOBOSSRC std/functional.d)
*/
/*
         Copyright Andrei Alexandrescu 2008 - 2009.
Distributed under the Boost Software License, Version 1.0.
   (See accompanying file LICENSE_1_0.txt or copy at
         http://www.boost.org/LICENSE_1_0.txt)
*/
module std.functional;

import std.meta : AliasSeq, Reverse;
import std.traits : isCallable, Parameters;

import std.internal.attributes : betterC;

public import core.lifetime : forward;

private template needOpCallAlias(alias fun)
{
    /* Determine whether or not unaryFun and binaryFun need to alias to fun or
     * fun.opCall. Basically, fun is a function object if fun(...) compiles. We
     * want is(unaryFun!fun) (resp., is(binaryFun!fun)) to be true if fun is
     * any function object. There are 4 possible cases:
     *
     *  1) fun is the type of a function object with static opCall;
     *  2) fun is an instance of a function object with static opCall;
     *  3) fun is the type of a function object with non-static opCall;
     *  4) fun is an instance of a function object with non-static opCall.
     *
     * In case (1), is(unaryFun!fun) should compile, but does not if unaryFun
     * aliases itself to fun, because typeof(fun) is an error when fun itself
     * is a type. So it must be aliased to fun.opCall instead. All other cases
     * should be aliased to fun directly.
     */
    static if (is(typeof(fun.opCall) == function))
    {
        enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () {
            return fun(Parameters!fun.init);
        });
    }
    else
        enum needOpCallAlias = false;
}

/**
Transforms a `string` representing an expression into a unary
function. The `string` must either use symbol name `a` as
the parameter or provide the symbol via the `parmName` argument.

Params:
    fun = a `string` or a callable
    parmName = the name of the parameter if `fun` is a string. Defaults
    to `"a"`.
Returns:
    If `fun` is a `string`, a new single parameter function

    If `fun` is not a `string`, an alias to `fun`.
*/
template unaryFun(alias fun, string parmName = "a")
{
    static if (is(typeof(fun) : string))
    {
        static if (!fun._ctfeMatchUnary(parmName))
        {
            import std.algorithm, std.conv, std.exception, std.math, std.range, std.string;
            import std.meta, std.traits, std.typecons;
        }
        auto unaryFun(ElementType)(auto ref ElementType __a)
        {
            mixin("alias " ~ parmName ~ " = __a ;");
            return mixin(fun);
        }
    }
    else static if (needOpCallAlias!fun)
    {
        // https://issues.dlang.org/show_bug.cgi?id=9906
        alias unaryFun = fun.opCall;
    }
    else
    {
        alias unaryFun = fun;
    }
}

///
@safe unittest
{
    // Strings are compiled into functions:
    alias isEven = unaryFun!("(a & 1) == 0");
    assert(isEven(2) && !isEven(1));
}

@safe unittest
{
    static int f1(int a) { return a + 1; }
    static assert(is(typeof(unaryFun!(f1)(1)) == int));
    assert(unaryFun!(f1)(41) == 42);
    int f2(int a) { return a + 1; }
    static assert(is(typeof(unaryFun!(f2)(1)) == int));
    assert(unaryFun!(f2)(41) == 42);
    assert(unaryFun!("a + 1")(41) == 42);
    //assert(unaryFun!("return a + 1;")(41) == 42);

    int num = 41;
    assert(unaryFun!"a + 1"(num) == 42);

    // https://issues.dlang.org/show_bug.cgi?id=9906
    struct Seen
    {
        static bool opCall(int n) { return true; }
    }
    static assert(needOpCallAlias!Seen);
    static assert(is(typeof(unaryFun!Seen(1))));
    assert(unaryFun!Seen(1));

    Seen s;
    static assert(!needOpCallAlias!s);
    static assert(is(typeof(unaryFun!s(1))));
    assert(unaryFun!s(1));

    struct FuncObj
    {
        bool opCall(int n) { return true; }
    }
    FuncObj fo;
    static assert(!needOpCallAlias!fo);
    static assert(is(typeof(unaryFun!fo)));
    assert(unaryFun!fo(1));

    // Function object with non-static opCall can only be called with an
    // instance, not with merely the type.
    static assert(!is(typeof(unaryFun!FuncObj)));
}

/**
Transforms a `string` representing an expression into a binary function. The
`string` must either use symbol names `a` and `b` as the parameters or
provide the symbols via the `parm1Name` and `parm2Name` arguments.

Params:
    fun = a `string` or a callable
    parm1Name = the name of the first parameter if `fun` is a string.
    Defaults to `"a"`.
    parm2Name = the name of the second parameter if `fun` is a string.
    Defaults to `"b"`.
Returns:
    If `fun` is not a string, `binaryFun` aliases itself away to
    `fun`.
*/
template binaryFun(alias fun, string parm1Name = "a",
        string parm2Name = "b")
{
    static if (is(typeof(fun) : string))
    {
        static if (!fun._ctfeMatchBinary(parm1Name, parm2Name))
        {
            import std.algorithm, std.conv, std.exception, std.math, std.range, std.string;
            import std.meta, std.traits, std.typecons;
        }
        auto binaryFun(ElementType1, ElementType2)
            (auto ref ElementType1 __a, auto ref ElementType2 __b)
        {
            mixin("alias "~parm1Name~" = __a ;");
            mixin("alias "~parm2Name~" = __b ;");
            return mixin(fun);
        }
    }
    else static if (needOpCallAlias!fun)
    {
        // https://issues.dlang.org/show_bug.cgi?id=9906
        alias binaryFun = fun.opCall;
    }
    else
    {
        alias binaryFun = fun;
    }
}

///
@safe unittest
{
    alias less = binaryFun!("a < b");
    assert(less(1, 2) && !less(2, 1));
    alias greater = binaryFun!("a > b");
    assert(!greater("1", "2") && greater("2", "1"));
}

@safe unittest
{
    static int f1(int a, string b) { return a + 1; }
    static assert(is(typeof(binaryFun!(f1)(1, "2")) == int));
    assert(binaryFun!(f1)(41, "a") == 42);
    string f2(int a, string b) { return b ~ "2"; }
    static assert(is(typeof(binaryFun!(f2)(1, "1")) == string));
    assert(binaryFun!(f2)(1, "4") == "42");
    assert(binaryFun!("a + b")(41, 1) == 42);
    //@@BUG
    //assert(binaryFun!("return a + b;")(41, 1) == 42);

    // https://issues.dlang.org/show_bug.cgi?id=9906
    struct Seen
    {
        static bool opCall(int x, int y) { return true; }
    }
    static assert(is(typeof(binaryFun!Seen)));
    assert(binaryFun!Seen(1,1));

    struct FuncObj
    {
        bool opCall(int x, int y) { return true; }
    }
    FuncObj fo;
    static assert(!needOpCallAlias!fo);
    static assert(is(typeof(binaryFun!fo)));
    assert(unaryFun!fo(1,1));

    // Function object with non-static opCall can only be called with an
    // instance, not with merely the type.
    static assert(!is(typeof(binaryFun!FuncObj)));
}

// skip all ASCII chars except a .. z, A .. Z, 0 .. 9, '_' and '.'.
private uint _ctfeSkipOp(ref string op)
{
    if (!__ctfe) assert(false);
    import std.ascii : isASCII, isAlphaNum;
    immutable oldLength = op.length;
    while (op.length)
    {
        immutable front = op[0];
        if (front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.'))
            op = op[1..$];
        else
            break;
    }
    return oldLength != op.length;
}

// skip all digits
private uint _ctfeSkipInteger(ref string op)
{
    if (!__ctfe) assert(false);
    import std.ascii : isDigit;
    immutable oldLength = op.length;
    while (op.length)
    {
        immutable front = op[0];
        if (front.isDigit())
            op = op[1..$];
        else
            break;
    }
    return oldLength != op.length;
}

// skip name
private uint _ctfeSkipName(ref string op, string name)
{
    if (!__ctfe) assert(false);
    if (op.length >= name.length && op[0 .. name.length] == name)
    {
        op = op[name.length..$];
        return 1;
    }
    return 0;
}

// returns 1 if `fun` is trivial unary function
private uint _ctfeMatchUnary(string fun, string name)
{
    if (!__ctfe) assert(false);
    fun._ctfeSkipOp();
    for (;;)
    {
        immutable h = fun._ctfeSkipName(name) + fun._ctfeSkipInteger();
        if (h == 0)
        {
            fun._ctfeSkipOp();
            break;
        }
        else if (h == 1)
        {
            if (!fun._ctfeSkipOp())
                break;
        }
        else
            return 0;
    }
    return fun.length == 0;
}

@safe unittest
{
    static assert(!_ctfeMatchUnary("sqrt(ё)", "ё"));
    static assert(!_ctfeMatchUnary("ё.sqrt", "ё"));
    static assert(!_ctfeMatchUnary(".ё+ё", "ё"));
    static assert(!_ctfeMatchUnary("_ё+ё", "ё"));
    static assert(!_ctfeMatchUnary("ёё", "ё"));
    static assert(_ctfeMatchUnary("a+a", "a"));
    static assert(_ctfeMatchUnary("a + 10", "a"));
    static assert(_ctfeMatchUnary("4 == a", "a"));
    static assert(_ctfeMatchUnary("2 == a", "a"));
    static assert(_ctfeMatchUnary("1 != a", "a"));
    static assert(_ctfeMatchUnary("a != 4", "a"));
    static assert(_ctfeMatchUnary("a< 1", "a"));
    static assert(_ctfeMatchUnary("434 < a", "a"));
    static assert(_ctfeMatchUnary("132 > a", "a"));
    static assert(_ctfeMatchUnary("123 >a", "a"));
    static assert(_ctfeMatchUnary("a>82", "a"));
    static assert(_ctfeMatchUnary("ё>82", "ё"));
    static assert(_ctfeMatchUnary("ё[ё(ё)]", "ё"));
    static assert(_ctfeMatchUnary("ё[21]", "ё"));
}

// returns 1 if `fun` is trivial binary function
private uint _ctfeMatchBinary(string fun, string name1, string name2)
{
    if (!__ctfe) assert(false);
    fun._ctfeSkipOp();
    for (;;)
    {
        immutable h = fun._ctfeSkipName(name1) + fun._ctfeSkipName(name2) + fun._ctfeSkipInteger();
        if (h == 0)
        {
            fun._ctfeSkipOp();
            break;
        }
        else if (h == 1)
        {
            if (!fun._ctfeSkipOp())
                break;
        }
        else
            return 0;
    }
    return fun.length == 0;
}

@safe unittest
{

    static assert(!_ctfeMatchBinary("sqrt(ё)", "ё", "b"));
    static assert(!_ctfeMatchBinary("ё.sqrt", "ё", "b"));
    static assert(!_ctfeMatchBinary(".ё+ё", "ё", "b"));
    static assert(!_ctfeMatchBinary("_ё+ё", "ё", "b"));
    static assert(!_ctfeMatchBinary("ёё", "ё", "b"));
    static assert(_ctfeMatchBinary("a+a", "a", "b"));
    static assert(_ctfeMatchBinary("a + 10", "a", "b"));
    static assert(_ctfeMatchBinary("4 == a", "a", "b"));
    static assert(_ctfeMatchBinary("2 == a", "a", "b"));
    static assert(_ctfeMatchBinary("1 != a", "a", "b"));
    static assert(_ctfeMatchBinary("a != 4", "a", "b"));
    static assert(_ctfeMatchBinary("a< 1", "a", "b"));
    static assert(_ctfeMatchBinary("434 < a", "a", "b"));
    static assert(_ctfeMatchBinary("132 > a", "a", "b"));
    static assert(_ctfeMatchBinary("123 >a", "a", "b"));
    static assert(_ctfeMatchBinary("a>82", "a", "b"));
    static assert(_ctfeMatchBinary("ё>82", "ё", "q"));
    static assert(_ctfeMatchBinary("ё[ё(10)]", "ё", "q"));
    static assert(_ctfeMatchBinary("ё[21]", "ё", "q"));

    static assert(!_ctfeMatchBinary("sqrt(ё)+b", "b", "ё"));
    static assert(!_ctfeMatchBinary("ё.sqrt-b", "b", "ё"));
    static assert(!_ctfeMatchBinary(".ё+b", "b", "ё"));
    static assert(!_ctfeMatchBinary("_b+ё", "b", "ё"));
    static assert(!_ctfeMatchBinary("ba", "b", "a"));
    static assert(_ctfeMatchBinary("a+b", "b", "a"));
    static assert(_ctfeMatchBinary("a + b", "b", "a"));
    static assert(_ctfeMatchBinary("b == a", "b", "a"));
    static assert(_ctfeMatchBinary("b == a", "b", "a"));
    static assert(_ctfeMatchBinary("b != a", "b", "a"));
    static assert(_ctfeMatchBinary("a != b", "b", "a"));
    static assert(_ctfeMatchBinary("a< b", "b", "a"));
    static assert(_ctfeMatchBinary("b < a", "b", "a"));
    static assert(_ctfeMatchBinary("b > a", "b", "a"));
    static assert(_ctfeMatchBinary("b >a", "b", "a"));
    static assert(_ctfeMatchBinary("a>b", "b", "a"));
    static assert(_ctfeMatchBinary("ё>b", "b", "ё"));
    static assert(_ctfeMatchBinary("b[ё(-1)]", "b", "ё"));
    static assert(_ctfeMatchBinary("ё[-21]", "b", "ё"));
}

//undocumented
template safeOp(string S)
if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!=")
{
    import std.traits : isIntegral;
    private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure
        if (isIntegral!ElementType1 && isIntegral!ElementType2)
    {
        import std.traits : CommonType;
        alias T = CommonType!(ElementType1, ElementType2);
        return mixin("cast(T)a "~S~" cast(T) b");
    }

    bool safeOp(T0, T1)(auto ref T0 a, auto ref T1 b)
    {
        import std.traits : mostNegative;
        static if (isIntegral!T0 && isIntegral!T1 &&
                   (mostNegative!T0 < 0) != (mostNegative!T1 < 0))
        {
            static if (S == "<=" || S == "<")
            {
                static if (mostNegative!T0 < 0)
                    immutable result = a < 0 || unsafeOp(a, b);
                else
                    immutable result = b >= 0 && unsafeOp(a, b);
            }
            else
            {
                static if (mostNegative!T0 < 0)
                    immutable result = a >= 0 && unsafeOp(a, b);
                else
                    immutable result = b < 0 || unsafeOp(a, b);
            }
        }
        else
        {
            static assert(is(typeof(mixin("a "~S~" b"))),
                "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ ".");

            immutable result = mixin("a "~S~" b");
        }
        return result;
    }
}

@safe unittest //check user defined types
{
    import std.algorithm.comparison : equal;
    struct Foo
    {
        int a;
        auto opEquals(Foo foo)
        {
            return a == foo.a;
        }
    }
    assert(safeOp!"!="(Foo(1), Foo(2)));
}

/**
   Predicate that returns $(D_PARAM a < b).
   Correctly compares signed and unsigned integers, ie. -1 < 2U.
*/
alias lessThan = safeOp!"<";

///
pure @safe @nogc nothrow unittest
{
    assert(lessThan(2, 3));
    assert(lessThan(2U, 3U));
    assert(lessThan(2, 3.0));
    assert(lessThan(-2, 3U));
    assert(lessThan(2, 3U));
    assert(!lessThan(3U, -2));
    assert(!lessThan(3U, 2));
    assert(!lessThan(0, 0));
    assert(!lessThan(0U, 0));
    assert(!lessThan(0, 0U));
}

/**
   Predicate that returns $(D_PARAM a > b).
   Correctly compares signed and unsigned integers, ie. 2U > -1.
*/
alias greaterThan = safeOp!">";

///
@safe unittest
{
    assert(!greaterThan(2, 3));
    assert(!greaterThan(2U, 3U));
    assert(!greaterThan(2, 3.0));
    assert(!greaterThan(-2, 3U));
    assert(!greaterThan(2, 3U));
    assert(greaterThan(3U, -2));
    assert(greaterThan(3U, 2));
    assert(!greaterThan(0, 0));
    assert(!greaterThan(0U, 0));
    assert(!greaterThan(0, 0U));
}

/**
   Predicate that returns $(D_PARAM a == b).
   Correctly compares signed and unsigned integers, ie. !(-1 == ~0U).
*/
alias equalTo = safeOp!"==";

///
@safe unittest
{
    assert(equalTo(0U, 0));
    assert(equalTo(0, 0U));
    assert(!equalTo(-1, ~0U));
}
/**
N-ary predicate that reverses the order of arguments, e.g., given
$(D pred(a, b, c)), returns $(D pred(c, b, a)).

Params:
    pred = A callable
Returns:
    A function which calls `pred` after reversing the given parameters
*/
template reverseArgs(alias pred)
{
    auto reverseArgs(Args...)(auto ref Args args)
    if (is(typeof(pred(Reverse!args))))
    {
        return pred(Reverse!args);
    }
}

///
@safe unittest
{
    alias gt = reverseArgs!(binaryFun!("a < b"));
    assert(gt(2, 1) && !gt(1, 1));
}

///
@safe unittest
{
    int x = 42;
    bool xyz(int a, int b) { return a * x < b / x; }
    auto foo = &xyz;
    foo(4, 5);
    alias zyx = reverseArgs!(foo);
    assert(zyx(5, 4) == foo(4, 5));
}

///
@safe unittest
{
    alias gt = reverseArgs!(binaryFun!("a < b"));
    assert(gt(2, 1) && !gt(1, 1));
    int x = 42;
    bool xyz(int a, int b) { return a * x < b / x; }
    auto foo = &xyz;
    foo(4, 5);
    alias zyx = reverseArgs!(foo);
    assert(zyx(5, 4) == foo(4, 5));
}

///
@safe unittest
{
    int abc(int a, int b, int c) { return a * b + c; }
    alias cba = reverseArgs!abc;
    assert(abc(91, 17, 32) == cba(32, 17, 91));
}

///
@safe unittest
{
    int a(int a) { return a * 2; }
    alias _a = reverseArgs!a;
    assert(a(2) == _a(2));
}

///
@safe unittest
{
    int b() { return 4; }
    alias _b = reverseArgs!b;
    assert(b() == _b());
}

/**
Negates predicate `pred`.

Params:
    pred = A string or a callable
Returns:
    A function which calls `pred` and returns the logical negation of its
    return value.
 */
template not(alias pred)
{
    auto not(T...)(auto ref T args)
    {
        static if (is(typeof(!pred(args))))
            return !pred(args);
        else static if (T.length == 1)
            return !unaryFun!pred(args);
        else static if (T.length == 2)
            return !binaryFun!pred(args);
        else
            static assert(0);
    }
}

///
@safe unittest
{
    import std.algorithm.searching : find;
    import std.uni : isWhite;
    string a = "   Hello, world!";
    assert(find!(not!isWhite)(a) == "Hello, world!");
}

@safe unittest
{
    assert(not!"a != 5"(5));
    assert(not!"a != b"(5, 5));

    assert(not!(() => false)());
    assert(not!(a => a != 5)(5));
    assert(not!((a, b) => a != b)(5, 5));
    assert(not!((a, b, c) => a * b * c != 125 )(5, 5, 5));
}

/**
$(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially
applies) $(D_PARAM fun) by tying its first argument to $(D_PARAM arg).

Params:
    fun = A callable
    arg = The first argument to apply to `fun`
Returns:
    A new function which calls `fun` with `arg` plus the passed parameters.
 */
template partial(alias fun, alias arg)
{
    import std.traits : isCallable;
    // Check whether fun is a user defined type which implements opCall or a template.
    // As opCall itself can be templated, std.traits.isCallable does not work here.
    enum isSomeFunctor = (is(typeof(fun) == struct) || is(typeof(fun) == class)) && __traits(hasMember, fun, "opCall");
    static if (isSomeFunctor || __traits(isTemplate, fun))
    {
        auto partial(Ts...)(Ts args2)
        {
            static if (is(typeof(fun(arg, args2))))
            {
                return fun(arg, args2);
            }
            else
            {
                static string errormsg()
                {
                    string msg = "Cannot call '" ~ fun.stringof ~ "' with arguments " ~
                        "(" ~ arg.stringof;
                    foreach (T; Ts)
                        msg ~= ", " ~ T.stringof;
                    msg ~= ").";
                    return msg;
                }
                static assert(0, errormsg());
            }
        }
    }
    else static if (!isCallable!fun)
    {
        static assert(false, "Cannot apply partial to a non-callable '" ~ fun.stringof ~ "'.");
    }
    else
    {
        import std.meta : Filter;

        static if (__traits(compiles, __traits(getOverloads,
            __traits(parent, fun), __traits(identifier, fun))))
            alias overloads = __traits(getOverloads, __traits(parent, fun),
                __traits(identifier, fun));
        else
            alias overloads = AliasSeq!(fun);

        enum isCallableWithArg(alias fun) = Parameters!fun.length > 0 &&
            is(typeof(arg) : Parameters!fun[0]);
        alias candidates = Filter!(isCallableWithArg, overloads);

        static if (overloads.length == 1 && Parameters!fun.length == 0)
        {
            static assert(0, "Cannot partially apply '" ~ fun.stringof ~ "'." ~
                "'" ~ fun.stringof ~ "' has 0 arguments.");
        }
        else static if (candidates.length == 0)
        {
            import std.meta : NoDuplicates, staticMap;

            enum hasParameters(alias fun) = Parameters!fun.length > 0;
            alias firstParameter(alias fun) = Parameters!fun[0];
            alias firstParameters = NoDuplicates!(
                staticMap!(firstParameter, Filter!(hasParameters, overloads)));

            string errorMsg()
            {
                string msg = "Argument mismatch for '" ~ fun.stringof ~
                    "': expected " ~ firstParameters[0].stringof;
                static foreach (firstParam; firstParameters[1 .. $])
                    msg ~= " or " ~ firstParam.stringof;
                msg ~= ", but got " ~ typeof(arg).stringof ~ ".";

                return msg;
            }
            static assert(0, errorMsg());
        }
        else
        {
            import std.traits : ReturnType;
            static foreach (candidate; candidates)
                ReturnType!candidate partial(Parameters!candidate[1..$] args2)
                {
                    return candidate(arg, args2);
                }
        }
    }
}

///
@safe unittest
{
    int fun(int a, int b) { return a + b; }
    alias fun5 = partial!(fun, 5);
    assert(fun5(6) == 11);
    // Note that in most cases you'd use an alias instead of a value
    // assignment. Using an alias allows you to partially evaluate template
    // functions without committing to a particular type of the function.
}

// https://issues.dlang.org/show_bug.cgi?id=21457
///
@safe unittest
{
    // Overloads are resolved when the partially applied function is called
    // with the remaining arguments.
    struct S
    {
        static char fun(int i, string s) { return s[i]; }
        static int fun(int a, int b) { return a * b; }
    }
    alias fun3 = partial!(S.fun, 3);
    assert(fun3("hello") == 'l');
    assert(fun3(10) == 30);
}

// tests for partially evaluating callables
@safe unittest
{
    static int f1(int a, int b) { return a + b; }
    assert(partial!(f1, 5)(6) == 11);

    int f2(int a, int b) { return a + b; }
    int x = 5;
    assert(partial!(f2, x)(6) == 11);
    x = 7;
    assert(partial!(f2, x)(6) == 13);
    static assert(partial!(f2, 5)(6) == 11);

    auto dg = &f2;
    auto f3 = &partial!(dg, x);
    assert(f3(6) == 13);

    static int funOneArg(int a) { return a; }
    assert(partial!(funOneArg, 1)() == 1);

    static int funThreeArgs(int a, int b, int c) { return a + b + c; }
    alias funThreeArgs1 = partial!(funThreeArgs, 1);
    assert(funThreeArgs1(2, 3) == 6);
    static assert(!is(typeof(funThreeArgs1(2))));

    enum xe = 5;
    alias fe = partial!(f2, xe);
    static assert(fe(6) == 11);
}

// tests for partially evaluating templated/overloaded callables
@safe unittest
{
    static auto add(A, B)(A x, B y)
    {
        return x + y;
    }

    alias add5 = partial!(add, 5);
    assert(add5(6) == 11);
    static assert(!is(typeof(add5())));
    static assert(!is(typeof(add5(6, 7))));

    // taking address of templated partial evaluation needs explicit type
    auto dg = &add5!(int);
    assert(dg(6) == 11);

    int x = 5;
    alias addX = partial!(add, x);
    assert(addX(6) == 11);

    static struct Callable
    {
        static string opCall(string a, string b) { return a ~ b; }
        int opCall(int a, int b) { return a * b; }
        double opCall(double a, double b) { return a + b; }
    }
    Callable callable;
    assert(partial!(Callable, "5")("6") == "56");
    assert(partial!(callable, 5)(6) == 30);
    assert(partial!(callable, 7.0)(3.0) == 7.0 + 3.0);

    static struct TCallable
    {
        auto opCall(A, B)(A a, B b)
        {
            return a + b;
        }
    }
    TCallable tcallable;
    assert(partial!(tcallable, 5)(6) == 11);
    static assert(!is(typeof(partial!(tcallable, "5")(6))));

    static struct NonCallable{}
    static assert(!__traits(compiles, partial!(NonCallable, 5)), "Partial should not work on non-callable structs.");
    static assert(!__traits(compiles, partial!(NonCallable.init, 5)),
        "Partial should not work on instances of non-callable structs.");

    static A funOneArg(A)(A a) { return a; }
    alias funOneArg1 = partial!(funOneArg, 1);
    assert(funOneArg1() == 1);

    static auto funThreeArgs(A, B, C)(A a, B b, C c) { return a + b + c; }
    alias funThreeArgs1 = partial!(funThreeArgs, 1);
    assert(funThreeArgs1(2, 3) == 6);
    static assert(!is(typeof(funThreeArgs1(1))));

    auto dg2 = &funOneArg1!();
    assert(dg2() == 1);
}

// Fix https://issues.dlang.org/show_bug.cgi?id=15732
@safe unittest
{
    // Test whether it works with functions.
    auto partialFunction(){
        auto fullFunction = (float a, float b, float c) => a + b / c;
        alias apply1 = partial!(fullFunction, 1);
        return &apply1;
    }
    auto result = partialFunction()(2, 4);
    assert(result == 1.5f);

    // And with delegates.
    auto partialDelegate(float c){
        auto fullDelegate = (float a, float b) => a + b / c;
        alias apply1 = partial!(fullDelegate, 1);
        return &apply1;
    }
    auto result2 = partialDelegate(4)(2);
    assert(result2 == 1.5f);
}

/**
Takes a function of (potentially) many arguments, and returns a function taking
one argument and returns a callable taking the rest.  f(x, y) == curry(f)(x)(y)

Params:
    F = a function taking at least one argument
    t = a callable object whose opCall takes at least 1 object
Returns:
    A single parameter callable object
*/
template curry(alias F)
if (isCallable!F && Parameters!F.length)
{
    //inspired from the implementation from Artur Skawina here:
    //https://forum.dlang.org/post/mailman.1626.1340110492.24740.digitalmars-d@puremagic.com
    //This implementation stores a copy of all filled in arguments with each curried result
    //this way, the curried functions are independent and don't share any references
    //eg: auto fc = curry!f;  auto fc1 = fc(1); auto fc2 = fc(2); fc1(3) != fc2(3)
    struct CurryImpl(size_t N)
    {
        alias FParams = Parameters!F;
        FParams[0 .. N] storedArguments;
        static if (N > 0)
        {
            this(U : FParams[N - 1])(ref CurryImpl!(N - 1) prev, ref U arg)
            {
                storedArguments[0 .. N - 1] = prev.storedArguments[];
                storedArguments[N-1] = arg;
            }
        }

        auto opCall(U : FParams[N])(auto ref U arg) return scope
        {
            static if (N == FParams.length - 1)
            {
                return F(storedArguments, arg);
            }
            else
            {
                return CurryImpl!(N + 1)(this, arg);
            }
        }
    }

    auto curry()
    {
        CurryImpl!0 res;
        return res; // return CurryImpl!0.init segfaults for delegates on Windows
    }
}

///
pure @safe @nogc nothrow unittest
{
    int f(int x, int y, int z)
    {
        return x + y + z;
    }
    auto cf = curry!f;
    auto cf1 = cf(1);
    auto cf2 = cf(2);

    assert(cf1(2)(3) == f(1, 2, 3));
    assert(cf2(2)(3) == f(2, 2, 3));
}

///ditto
auto curry(T)(T t)
if (isCallable!T && Parameters!T.length)
{
    static auto fun(ref T inst, ref Parameters!T args)
    {
        return inst(args);
    }

    return curry!fun()(t);
}

///
pure @safe @nogc nothrow unittest
{
    //works with callable structs too
    struct S
    {
        int w;
        int opCall(int x, int y, int z)
        {
            return w + x + y + z;
        }
    }

    S s;
    s.w = 5;

    auto cs = curry(s);
    auto cs1 = cs(1);
    auto cs2 = cs(2);

    assert(cs1(2)(3) == s(1, 2, 3));
    assert(cs1(2)(3) == (1 + 2 + 3 + 5));
    assert(cs2(2)(3) ==s(2, 2, 3));
}


@safe pure @nogc nothrow unittest
{
    //currying a single argument function does nothing
    int pork(int a){ return a*2;}
    auto curryPork = curry!pork;
    assert(curryPork(0) == pork(0));
    assert(curryPork(1) == pork(1));
    assert(curryPork(-1) == pork(-1));
    assert(curryPork(1000) == pork(1000));

    //test 2 argument function
    double mixedVeggies(double a, int b, bool)
    {
        return a + b;
    }

    auto mixedCurry = curry!mixedVeggies;
    assert(mixedCurry(10)(20)(false) == mixedVeggies(10, 20, false));
    assert(mixedCurry(100)(200)(true) == mixedVeggies(100, 200, true));

    // struct with opCall
    struct S
    {
        double opCall(int x, double y, short z) const pure nothrow @nogc
        {
            return x*y*z;
        }
    }

    S s;
    auto curriedStruct = curry(s);
    assert(curriedStruct(1)(2)(short(3)) == s(1, 2, short(3)));
    assert(curriedStruct(300)(20)(short(10)) == s(300, 20, short(10)));
}

pure @safe nothrow unittest
{
    auto cfl = curry!((double a, int b)  => a + b);
    assert(cfl(13)(2) == 15);

    int c = 42;
    auto cdg = curry!((double a, int b)  => a + b + c);
    assert(cdg(13)(2) == 57);

    static class C
    {
        int opCall(int mult, int add) pure @safe nothrow @nogc scope
        {
            return  mult * 42 + add;
        }
    }

    scope C ci = new C();
    scope cc = curry(ci);
    assert(cc(2)(4) == ci(2, 4));
}

// Disallows callables without parameters
pure @safe @nogc nothrow unittest
{
    static void noargs() {}
    static assert(!__traits(compiles, curry!noargs()));

    static struct NoArgs
    {
        void opCall() {}
    }

    static assert(!__traits(compiles, curry(NoArgs.init)));
}

private template Iota(size_t n)
{
    static if (n == 0)
        alias Iota = AliasSeq!();
    else
        alias Iota = AliasSeq!(Iota!(n - 1), n - 1);
}

/**
Takes multiple functions and adjoins them together.

Params:
    F = the call-able(s) to adjoin
Returns:
    A new function which returns a $(REF Tuple, std,typecons). Each of the
    elements of the tuple will be the return values of `F`.

Note: In the special case where only a single function is provided
($(D F.length == 1)), adjoin simply aliases to the single passed function
(`F[0]`).
*/
template adjoin(F...)
if (F.length >= 1)
{
    static if (F.length == 1)
        alias adjoin = F[0];
    else
        auto adjoin(V...)(auto ref V a)
        {
            import std.typecons : tuple;
            import std.meta : staticMap;

            auto resultElement(size_t i)()
            {
                return F[i](a);
            }

            return tuple(staticMap!(resultElement, Iota!(F.length)));
        }
}

///
@safe unittest
{
    import std.typecons : Tuple;
    static bool f1(int a) { return a != 0; }
    static int f2(int a) { return a / 2; }
    auto x = adjoin!(f1, f2)(5);
    assert(is(typeof(x) == Tuple!(bool, int)));
    assert(x[0] == true && x[1] == 2);
}

@safe unittest
{
    import std.typecons : Tuple;
    static bool F1(int a) { return a != 0; }
    auto x1 = adjoin!(F1)(5);
    static int F2(int a) { return a / 2; }
    auto x2 = adjoin!(F1, F2)(5);
    assert(is(typeof(x2) == Tuple!(bool, int)));
    assert(x2[0] && x2[1] == 2);
    auto x3 = adjoin!(F1, F2, F2)(5);
    assert(is(typeof(x3) == Tuple!(bool, int, int)));
    assert(x3[0] && x3[1] == 2 && x3[2] == 2);

    bool F4(int a) { return a != x1; }
    alias eff4 = adjoin!(F4);
    static struct S
    {
        bool delegate(int) @safe store;
        int fun() { return 42 + store(5); }
    }
    S s;
    s.store = (int a) { return eff4(a); };
    auto x4 = s.fun();
    assert(x4 == 43);
}

@safe unittest
{
    import std.meta : staticMap;
    import std.typecons : Tuple, tuple;
    alias funs = staticMap!(unaryFun, "a", "a * 2", "a * 3", "a * a", "-a");
    alias afun = adjoin!funs;
    assert(afun(5) == tuple(5, 10, 15, 25, -5));

    static class C{}
    alias IC = immutable(C);
    IC foo(){return typeof(return).init;}
    Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)();

    static struct S{int* p;}
    alias IS = immutable(S);
    IS bar(){return typeof(return).init;}
    enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)();
}

// https://issues.dlang.org/show_bug.cgi?id=21347
@safe @betterC unittest
{
    alias f = (int n) => n + 1;
    alias g = (int n) => n + 2;
    alias h = (int n) => n + 3;
    alias i = (int n) => n + 4;

    auto result = adjoin!(f, g, h, i)(0);

    assert(result[0] == 1);
    assert(result[1] == 2);
    assert(result[2] == 3);
    assert(result[3] == 4);
}

/**
   Composes passed-in functions $(D fun[0], fun[1], ...).

   Params:
        fun = the call-able(s) or `string`(s) to compose into one function
    Returns:
        A new function `f(x)` that in turn returns `fun[0](fun[1](...(x)))...`.

   See_Also: $(LREF pipe)
*/
template compose(fun...)
if (fun.length > 0)
{
    static if (fun.length == 1)
    {
        alias compose = unaryFun!(fun[0]);
    }
    else
    {
        alias fun0 = unaryFun!(fun[0]);
        alias rest = compose!(fun[1 .. $]);

        auto compose(Args...)(Args args)
        {
            return fun0(rest(args));
        }
    }
}

///
@safe unittest
{
    import std.algorithm.comparison : equal;
    import std.algorithm.iteration : map;
    import std.array : split;
    import std.conv : to;

    // First split a string in whitespace-separated tokens and then
    // convert each token into an integer
    assert(compose!(map!(to!(int)), split)("1 2 3").equal([1, 2, 3]));
}

// https://issues.dlang.org/show_bug.cgi?id=6484
@safe unittest
{
    int f(int a) { return a; }
    int g(int a) { return a; }
    int h(int a,int b,int c) { return a * b * c; }

    alias F = compose!(f,g,h);
    assert(F(1,2,3) == f(g(h(1,2,3))));
}

/**
   Pipes functions in sequence. Offers the same functionality as $(D
   compose), but with functions specified in reverse order. This may
   lead to more readable code in some situation because the order of
   execution is the same as lexical order.

   Params:
        fun = the call-able(s) or `string`(s) to compose into one function
    Returns:
        A new function `f(x)` that in turn returns `fun[$-1](...fun[1](fun[0](x)))...`.

   Example:

----
// Read an entire text file, split the resulting string in
// whitespace-separated tokens, and then convert each token into an
// integer
int[] a = pipe!(readText, split, map!(to!(int)))("file.txt");
----

   See_Also: $(LREF compose)
 */
alias pipe(fun...) = compose!(Reverse!(fun));

///
@safe unittest
{
    import std.conv : to;
    string foo(int a) { return to!(string)(a); }
    int bar(string a) { return to!(int)(a) + 1; }
    double baz(int a) { return a + 0.5; }
    assert(compose!(baz, bar, foo)(1) == 2.5);
    assert(pipe!(foo, bar, baz)(1) == 2.5);

    assert(compose!(baz, `to!(int)(a) + 1`, foo)(1) == 2.5);
    assert(compose!(baz, bar)("1"[]) == 2.5);

    assert(compose!(baz, bar)("1") == 2.5);

    assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5);
}

/**
 * $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as
 * to avoid repeated computation. The memoization structure is a hash table keyed by a
 * tuple of the function's arguments. There is a speed gain if the
 * function is repeatedly called with the same arguments and is more
 * expensive than a hash table lookup. For more information on memoization, refer to $(HTTP docs.google.com/viewer?url=http%3A%2F%2Fhop.perl.plover.com%2Fbook%2Fpdf%2F03CachingAndMemoization.pdf, this book chapter).

Example:
----
double transmogrify(int a, string b)
{
   ... expensive computation ...
}
alias fastTransmogrify = memoize!transmogrify;
unittest
{
    auto slow = transmogrify(2, "hello");
    auto fast = fastTransmogrify(2, "hello");
    assert(slow == fast);
}
----

Params:
    fun = the call-able to memozie
    maxSize = The maximum size of the GC buffer to hold the return values
Returns:
    A new function which calls `fun` and caches its return values.

Note:
    Technically the memoized function should be pure because `memoize` assumes it will
    always return the same result for a given tuple of arguments. However, `memoize` does not
    enforce that because sometimes it is useful to memoize an impure function, too.
*/
template memoize(alias fun)
{
    import std.traits : ReturnType;
     // https://issues.dlang.org/show_bug.cgi?id=13580
    // alias Args = Parameters!fun;

    ReturnType!fun memoize(Parameters!fun args)
    {
        alias Args = Parameters!fun;
        import std.typecons : Tuple;
        import std.traits : Unqual;

        static Unqual!(ReturnType!fun)[Tuple!Args] memo;
        auto t = Tuple!Args(args);
        if (auto p = t in memo)
            return *p;
        auto r = fun(args);
        memo[t] = r;
        return r;
    }
}

/// ditto
template memoize(alias fun, uint maxSize)
{
    import std.traits : ReturnType;
     // https://issues.dlang.org/show_bug.cgi?id=13580
    // alias Args = Parameters!fun;
    ReturnType!fun memoize(Parameters!fun args)
    {
        import std.meta : staticMap;
        import std.traits : hasIndirections, Unqual;
        import std.typecons : tuple;
        static struct Value { staticMap!(Unqual, Parameters!fun) args; Unqual!(ReturnType!fun) res; }
        static Value[] memo;
        static size_t[] initialized;

        if (!memo.length)
        {
            import core.memory : GC;

            // Ensure no allocation overflows
            static assert(maxSize < size_t.max / Value.sizeof);
            static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1));

            enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN);
            memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize];
            enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof);
            initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords];
        }

        import core.bitop : bt, bts;
        import core.lifetime : emplace;

        size_t hash;
        foreach (ref arg; args)
            hash = hashOf(arg, hash);
        // cuckoo hashing
        immutable idx1 = hash % maxSize;
        if (!bt(initialized.ptr, idx1))
        {
            emplace(&memo[idx1], args, fun(args));
            // only set to initialized after setting args and value
            // https://issues.dlang.org/show_bug.cgi?id=14025
            bts(initialized.ptr, idx1);
            return memo[idx1].res;
        }
        else if (memo[idx1].args == args)
            return memo[idx1].res;
        // FNV prime
        immutable idx2 = (hash * 16_777_619) % maxSize;
        if (!bt(initialized.ptr, idx2))
        {
            emplace(&memo[idx2], memo[idx1]);
            bts(initialized.ptr, idx2);
        }
        else if (memo[idx2].args == args)
            return memo[idx2].res;
        else if (idx1 != idx2)
            memo[idx2] = memo[idx1];

        memo[idx1] = Value(args, fun(args));
        return memo[idx1].res;
    }
}

/**
 * To _memoize a recursive function, simply insert the memoized call in lieu of the plain recursive call.
 * For example, to transform the exponential-time Fibonacci implementation into a linear-time computation:
 */
@safe nothrow
unittest
{
    ulong fib(ulong n) @safe nothrow
    {
        return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1);
    }
    assert(fib(10) == 55);
}

/**
 * To improve the speed of the factorial function,
 */
@safe unittest
{
    ulong fact(ulong n) @safe
    {
        return n < 2 ? 1 : n * memoize!fact(n - 1);
    }
    assert(fact(10) == 3628800);
}

/**
 * This memoizes all values of `fact` up to the largest argument. To only cache the final
 * result, move `memoize` outside the function as shown below.
 */
@safe unittest
{
    ulong factImpl(ulong n) @safe
    {
        return n < 2 ? 1 : n * factImpl(n - 1);
    }
    alias fact = memoize!factImpl;
    assert(fact(10) == 3628800);
}

/**
 * When the `maxSize` parameter is specified, memoize will used
 * a fixed size hash table to limit the number of cached entries.
 */
@system unittest // not @safe due to memoize
{
    ulong fact(ulong n)
    {
        // Memoize no more than 8 values
        return n < 2 ? 1 : n * memoize!(fact, 8)(n - 1);
    }
    assert(fact(8) == 40320);
    // using more entries than maxSize will overwrite existing entries
    assert(fact(10) == 3628800);
}

@system unittest // not @safe due to memoize
{
    import core.math : sqrt;
    alias msqrt = memoize!(function double(double x) { return sqrt(x); });
    auto y = msqrt(2.0);
    assert(y == msqrt(2.0));
    y = msqrt(4.0);
    assert(y == sqrt(4.0));

    // alias mrgb2cmyk = memoize!rgb2cmyk;
    // auto z = mrgb2cmyk([43, 56, 76]);
    // assert(z == mrgb2cmyk([43, 56, 76]));

    //alias mfib = memoize!fib;

    static ulong fib(ulong n) @safe
    {
        alias mfib = memoize!fib;
        return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1);
    }

    auto z = fib(10);
    assert(z == 89);

    static ulong fact(ulong n) @safe
    {
        alias mfact = memoize!fact;
        return n < 2 ? 1 : n * mfact(n - 1);
    }
    assert(fact(10) == 3628800);

    // https://issues.dlang.org/show_bug.cgi?id=12568
    static uint len2(const string s) { // Error
    alias mLen2 = memoize!len2;
    if (s.length == 0)
        return 0;
    else
        return 1 + mLen2(s[1 .. $]);
    }

    int _func(int x) @safe { return 1; }
    alias func = memoize!(_func, 10);
    assert(func(int.init) == 1);
    assert(func(int.init) == 1);
}

// https://issues.dlang.org/show_bug.cgi?id=16079
// memoize should work with arrays
@system unittest // not @safe with -dip1000 due to memoize
{
    int executed = 0;
    T median(T)(const T[] nums) {
        import std.algorithm.sorting : sort;
        executed++;
        auto arr = nums.dup;
        arr.sort();
        if (arr.length % 2)
            return arr[$ / 2];
        else
            return (arr[$ / 2 - 1]
                + arr[$ / 2]) / 2;
    }

    alias fastMedian = memoize!(median!int);

    assert(fastMedian([7, 5, 3]) == 5);
    assert(fastMedian([7, 5, 3]) == 5);

    assert(executed == 1);
}

// https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with structs
@safe unittest
{
    int executed = 0;
    T pickFirst(T)(T first)
    {
        executed++;
        return first;
    }

    struct Foo { int k; }
    Foo A = Foo(3);

    alias first = memoize!(pickFirst!Foo);
    assert(first(Foo(3)) == A);
    assert(first(Foo(3)) == A);
    assert(executed == 1);
}

// https://issues.dlang.org/show_bug.cgi?id=20439 memoize should work with void opAssign
@safe unittest
{
    static struct S
    {
        void opAssign(S) {}
    }

    assert(memoize!(() => S()) == S());
}

// https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with classes
@system unittest // not @safe with -dip1000 due to memoize
{
    int executed = 0;
    T pickFirst(T)(T first)
    {
        executed++;
        return first;
    }

    class Bar
    {
        size_t k;
        this(size_t k)
        {
            this.k = k;
        }
        override size_t toHash()
        {
            return k;
        }
        override bool opEquals(Object o)
        {
            auto b = cast(Bar) o;
            return b && k == b.k;
        }
    }

    alias firstClass = memoize!(pickFirst!Bar);
    assert(firstClass(new Bar(3)).k == 3);
    assert(firstClass(new Bar(3)).k == 3);
    assert(executed == 1);
}

// https://issues.dlang.org/show_bug.cgi?id=20302
@system unittest
{
    version (none) // TODO change `none` to `all` and fix remaining limitations
        struct S { const int len; }
    else
        struct S { int len; }

    static       string  fun000(      string str,       S s) { return str[0 .. s.len] ~ "123"; }
    static       string  fun001(      string str, const S s) { return str[0 .. s.len] ~ "123"; }
    static       string  fun010(const string str,       S s) { return str[0 .. s.len] ~ "123"; }
    static       string  fun011(const string str, const S s) { return str[0 .. s.len] ~ "123"; }
    static const(string) fun100(      string str,       S s) { return str[0 .. s.len] ~ "123"; }
    static const(string) fun101(      string str, const S s) { return str[0 .. s.len] ~ "123"; }
    static const(string) fun110(const string str,       S s) { return str[0 .. s.len] ~ "123"; }
    static const(string) fun111(const string str, const S s) { return str[0 .. s.len] ~ "123"; }

    static foreach (fun; AliasSeq!(fun000, fun001, fun010, fun011, fun100, fun101, fun110, fun111))
    {{
        alias mfun = memoize!fun;
        assert(mfun("abcdefgh", S(3)) == "abc123");

        alias mfun2 = memoize!(fun, 42);
        assert(mfun2("asd", S(3)) == "asd123");
    }}
}

private struct DelegateFaker(F)
{
    import std.typecons : FuncInfo, MemberFunctionGenerator;

    // for @safe
    static F castToF(THIS)(THIS x) @trusted
    {
        return cast(F) x;
    }

    /*
     * What all the stuff below does is this:
     *--------------------
     * struct DelegateFaker(F) {
     *     extern(linkage)
     *     [ref] ReturnType!F doIt(Parameters!F args) [@attributes]
     *     {
     *         auto fp = cast(F) &this;
     *         return fp(args);
     *     }
     * }
     *--------------------
     */

    // We will use MemberFunctionGenerator in std.typecons.  This is a policy
    // configuration for generating the doIt().
    template GeneratingPolicy()
    {
        // Inform the genereator that we only have type information.
        enum WITHOUT_SYMBOL = true;

        // Generate the function body of doIt().
        template generateFunctionBody(unused...)
        {
            enum generateFunctionBody =
            // [ref] ReturnType doIt(Parameters args) @attributes
            q{
                // When this function gets called, the this pointer isn't
                // really a this pointer (no instance even really exists), but
                // a function pointer that points to the function to be called.
                // Cast it to the correct type and call it.

                auto fp = castToF(&this);
                return fp(args);
            };
        }
    }
    // Type information used by the generated code.
    alias FuncInfo_doIt = FuncInfo!(F);

    // Generate the member function doIt().
    mixin( MemberFunctionGenerator!(GeneratingPolicy!())
            .generateFunction!("FuncInfo_doIt", "doIt", F) );
}

/**
 * Convert a callable to a delegate with the same parameter list and
 * return type, avoiding heap allocations and use of auxiliary storage.
 *
 * Params:
 *     fp = a function pointer or an aggregate type with `opCall` defined.
 * Returns:
 *     A delegate with the context pointer pointing to nothing.
 *
 * Example:
 * ----
 * void doStuff() {
 *     writeln("Hello, world.");
 * }
 *
 * void runDelegate(void delegate() myDelegate) {
 *     myDelegate();
 * }
 *
 * auto delegateToPass = toDelegate(&doStuff);
 * runDelegate(delegateToPass);  // Calls doStuff, prints "Hello, world."
 * ----
 *
 * BUGS:
 * $(UL
 *   $(LI Does not work with `@safe` functions.)
 *   $(LI Ignores C-style / D-style variadic arguments.)
 * )
 */
auto toDelegate(F)(auto ref F fp)
if (isCallable!(F))
{
    static if (is(F == delegate))
    {
        return fp;
    }
    else static if (is(typeof(&F.opCall) == delegate)
                || (is(typeof(&F.opCall) V : V*) && is(V == function)))
    {
        return toDelegate(&fp.opCall);
    }
    else
    {
        alias DelType = typeof(&(new DelegateFaker!(F)).doIt);

        static struct DelegateFields {
            union {
                DelType del;
                //pragma(msg, typeof(del));

                struct {
                    void* contextPtr;
                    void* funcPtr;
                }
            }
        }

        // fp is stored in the returned delegate's context pointer.
        // The returned delegate's function pointer points to
        // DelegateFaker.doIt.
        DelegateFields df;

        df.contextPtr = cast(void*) fp;

        DelegateFaker!(F) dummy;
        auto dummyDel = &dummy.doIt;
        df.funcPtr = dummyDel.funcptr;

        return df.del;
    }
}

///
@system unittest
{
    static int inc(ref uint num) {
        num++;
        return 8675309;
    }

    uint myNum = 0;
    auto incMyNumDel = toDelegate(&inc);
    auto returnVal = incMyNumDel(myNum);
    assert(myNum == 1);
}

@system unittest // not @safe due to toDelegate
{
    static int inc(ref uint num) {
        num++;
        return 8675309;
    }

    uint myNum = 0;
    auto incMyNumDel = toDelegate(&inc);
    int delegate(ref uint) dg = incMyNumDel;
    auto returnVal = incMyNumDel(myNum);
    assert(myNum == 1);

    interface I { int opCall(); }
    class C: I { int opCall() { inc(myNum); return myNum;} }
    auto c = new C;
    auto i = cast(I) c;

    auto getvalc = toDelegate(c);
    assert(getvalc() == 2);

    auto getvali = toDelegate(i);
    assert(getvali() == 3);

    struct S1 { int opCall() { inc(myNum); return myNum; } }
    static assert(!is(typeof(&s1.opCall) == delegate));
    S1 s1;
    auto getvals1 = toDelegate(s1);
    assert(getvals1() == 4);

    struct S2 { static int opCall() { return 123456; } }
    static assert(!is(typeof(&S2.opCall) == delegate));
    S2 s2;
    auto getvals2 =&S2.opCall;
    assert(getvals2() == 123456);

    /* test for attributes */
    {
        static int refvar = 0xDeadFace;

        static ref int func_ref() { return refvar; }
        static int func_pure() pure { return 1; }
        static int func_nothrow() nothrow { return 2; }
        static int func_property() @property { return 3; }
        static int func_safe() @safe { return 4; }
        static int func_trusted() @trusted { return 5; }
        static int func_system() @system { return 6; }
        static int func_pure_nothrow() pure nothrow { return 7; }
        static int func_pure_nothrow_safe() pure nothrow @safe { return 8; }

        auto dg_ref = toDelegate(&func_ref);
        int delegate() pure dg_pure = toDelegate(&func_pure);
        int delegate() nothrow dg_nothrow = toDelegate(&func_nothrow);
        int delegate() @property dg_property = toDelegate(&func_property);
        int delegate() @safe dg_safe = toDelegate(&func_safe);
        int delegate() @trusted dg_trusted = toDelegate(&func_trusted);
        int delegate() @system dg_system = toDelegate(&func_system);
        int delegate() pure nothrow dg_pure_nothrow = toDelegate(&func_pure_nothrow);
        int delegate() @safe pure nothrow dg_pure_nothrow_safe = toDelegate(&func_pure_nothrow_safe);

        //static assert(is(typeof(dg_ref) == ref int delegate())); // [BUG@DMD]

        assert(dg_ref() == refvar);
        assert(dg_pure() == 1);
        assert(dg_nothrow() == 2);
        assert(dg_property() == 3);
        assert(dg_safe() == 4);
        assert(dg_trusted() == 5);
        assert(dg_system() == 6);
        assert(dg_pure_nothrow() == 7);
        assert(dg_pure_nothrow_safe() == 8);
    }
    /* test for linkage */
    {
        struct S
        {
            extern(C) static void xtrnC() {}
            extern(D) static void xtrnD() {}
        }
        auto dg_xtrnC = toDelegate(&S.xtrnC);
        auto dg_xtrnD = toDelegate(&S.xtrnD);
        static assert(! is(typeof(dg_xtrnC) == typeof(dg_xtrnD)));
    }
}
