#include <map>
#include <tuple>
#include <cstdio>
#include <functional>

template <typename ReturnType, typename... Args>
std::function<ReturnType (Args...)> memoize(std::function<ReturnType (Args...)> func)
{
    std::map<std::tuple<Args...>, ReturnType> cache;
    return ([=](Args... args) mutable  {
            std::tuple<Args...> t(args...);

            if (cache.find(t) == cache.end())
            {
                ReturnType r = func(args...);
                cache[t] = r;
            }
            return cache[t];
    });
}


int sum(int x, int y)
{
    printf("sum(%d, %d)\n", x, y);
    return x+y;
}

float times(float x, float y)
{
    printf("times(%f, %f)\n", x, y);
    return x*y;
}

int main()
{
    // testing with a couple of functions
    auto f = memoize(std::function<int (int, int)>(sum));   
    auto g = memoize(std::function<float (float, float)>(times));   
    printf("%d\n", f(3,5));
    printf("%d\n", f(2,4));
    printf("%d\n", f(3,5));
    printf("%d\n", f(4,6));
    printf("%d\n", f(2,4));
    printf("%d\n", f(3,5));
    printf("%f\n", g(3,5));
    printf("%f\n", g(2,4));
    printf("%f\n", g(3,5));
    printf("%f\n", g(4,6));
    printf("%f\n", g(2,4));
    printf("%f\n", g(3,5));
}


