//#####################################################################
// Copyright 2021.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################

#include <Core/Log/LOG.h>
#include <map>
#include <set>
#include <tuple>
#include <vector>

using namespace PhysBAM;

/*
  A "memoized" function is a function that caches its own results.  This is
  useful for expensive functions that will often be called repeatedly with the
  same inputs.  This can be used as a way of doing dynamic programming.  In this
  exercise, you will write a function "memoize" that turns a regular function
  "f" into a memoized function "g".  Your function takes "f" as its only
  argument and returns "g".  The type of "g" is entirely up to you, but it needs
  to be callable in exactly the same way that "f" is.  You are only allowed to
  modify this file between the comments below.
 */

// YOU MAY NOT CHANGE THIS FILE ABOVE THIS LINE

template<class FUNC>
auto memoize(FUNC f){return f;}

// YOU MAY NOT CHANGE THIS FILE BELOW THIS LINE

#define P LOG::printf("%P\n",__FUNCTION__)

long f_(long n){P;return n*n+3;}
long g_(long n){P;return 7-n;}
long h_(long n){P;return n*43;}

auto f=memoize(f_);
auto g=memoize(g_);
auto h=memoize(h_);

long fib_(long n);
auto fib=memoize(fib_);

long fib_(long n)
{
    P;
    if(n<2) return n;
    return fib(n-1)+fib(n-2);
}

std::string j_(long l, bool b, const std::string& s, const std::vector<long>& v,
    const std::pair<std::string,char>& p, const std::map<int,std::set<int> >& m)
{
    P;
    return LOG::sprintf("%P %P %P %P %P %P %P\n",l,b,s,v.size(),p.first,p.second,m.size());
}
auto j=memoize(j_);

long k_(int a0, std::string a1, int a2, int a3, int a4, int a5, int a6, int a7,
    long a8, int a9, int a10, std::string a11, int a12, int a13, int a14, int a15,
    long a16, int a17, int a18, int a19, int a20, std::string a21, int a22, int a23,
    long a24, int a25, int a26, int a27, int a28, int a29, int a30, std::string a31,
    long a32, int a33, int a34, int a35, int a36, int a37, int a38, int a39,
    long a40, std::string a41, int a42, int a43, int a44, int a45, int a46, int a47,
    long a48, int a49, int a50, std::string a51, int a52, int a53, int a54, int a55,
    long a56, int a57, int a58, int a59, int a60, std::string a61, int a62, int a63){P;return a0+a63;}

auto k=memoize(k_);

int main(int argc, char* argv[])
{
    LOG::printf("%P\n",f(3)); // f_ 12
    LOG::printf("%P\n",g(3)); // g_ 4
    LOG::printf("%P\n",h(3)); // h_ 129
    LOG::printf("%P\n",f(3)); // 12
    LOG::printf("%P\n",g(3)); // 4
    LOG::printf("%P\n",h(3)); // 129
    LOG::printf("%P\n",f(4)); // f_ 19
    LOG::printf("%P\n",fib(4)); // fib_ fib_ fib_ fib_ fib_ 3
    LOG::printf("%P\n",fib(3)); // 2

    // this should complete in a reasonable amount of time and print a bunch of fib_'s
    // and then 259695496911122585
    LOG::printf("%P\n",fib(85));

    LOG::printf("%P\n",j(3,true,"cat",{1,2,3},{"hat",'c'},{{4,{2,3}}})); // prints j_
    LOG::printf("%P\n",j(3,true,"cat",{4,2,3},{"hat",'c'},{{4,{2,3}}})); // prints j_
    LOG::printf("%P\n",j(3,true,"cat",{1,2,3},{"the",'c'},{{4,{2,3}}})); // prints j_
    LOG::printf("%P\n",j(3,true,"cat",{1,2,3},{"hat",'q'},{{4,{2,3}}})); // prints j_
    LOG::printf("%P\n",j(3,true,"cat",{1,2,3},{"hat",'c'},{{4,{2,3}}})); // does not print j_
    LOG::printf("%P\n",j(3,true,"cat",{1,2,3},{"hat",'c'},{{4,{7,3}}})); // prints j_
    LOG::printf("%P\n",j(2,true,"cat",{1,2,3},{"hat",'c'},{{4,{2,3}}})); // prints j_
    LOG::printf("%P\n",j(3,false,"cat",{1,2,3},{"hat",'c'},{{4,{2,3}}})); // prints j_
    LOG::printf("%P\n",j(3,true,"cut",{1,2,3},{"hat",'c'},{{4,{2,3}}})); // prints j_
    LOG::printf("%P\n",j(3,false,"cat",{1,2,3},{"hat",'c'},{{4,{2,3}}})); // does not print j_

    LOG::printf("%P\n",k(4,"x",4,4,4,4,4,4,3,4,4,"x",4,4,4,4,3,4,4,4,4,"x",
            4,4,3,4,4,4,4,4,4,"x",3,4,4,4,4,4,4,4,3,"x",4,4,4,4,4,4,3,4,4,
            "x",4,4,4,4,3,4,4,4,4,"x",4,4)); // k_ 8

    return 0;
}

