/*
    Example of Functor caching values of a function.
*/

#include <iostream>
#include <cmath>
#include <map>

using namespace std;

// Imagine this function takes an hour to calculate..
double func(double x)
{
    double y=0.0;
    for (int i=1;i<500;i++)
        for (int j=1;j<500;j++)
            for (int k=1;k<500;k++)
                y+=pow(sin(i*x+j/x+0.314159265*k),j+2*i+3*k)/(1.1*i+3.3*j+7.7*k);

    return y;
}



// define class with a functor interface   double operator(double)
class Functor
{
    map<double,double> storage;       // Store previous calls
    int cachemisses;                  // How many times have we called the function
    int cachehits;                    // How many uses of stored values.

public:
    Functor() : cachemisses(0), cachehits(0)  {   }

    double operator()(double x)
    {
        auto it=storage.find(x);
        if (it == storage.end())        // x is not stored.
        {
            cachemisses++;              // increment cachemisses
            double y=func(x);           // calculate value
            storage[x]=y;               // store it
            return y;                   // return y
        }
        else
        {
            cachehits++;                // increment cachehits
            return it->second;          // return stored value.
        }
    }

    friend ostream& operator<<(ostream&, Functor& );
};

ostream& operator<<(ostream& os, Functor& F)
{
    os << "Stored values: " << F.storage.size() << endl;
    os << "Cachemisses: " << F.cachemisses << endl;
    os << "Cachehits: " << F.cachehits << endl;
    return os;
}


int main()
{
    // Definition of Functor F with operator()
    Functor F;
    cout << "Functor(1)=" << F(1.0) << endl;
    cout << "Functor(1)=" << F(1.0) << endl;
    cout << "Functor(1)=" << F(1.0) << endl;
    cout << "Functor(1)=" << F(1.0) << endl;
    cout << "Functor(2)=" << F(2.0) << endl;
    cout << "Functor(2)=" << F(2.0) << endl;
    cout << "Functor(2)=" << F(2.0) << endl;
    cout << "Functor(2)=" << F(2.0) << endl;
    cout << "Functor(3)=" << F(3.0) << endl;
    cout << "Functor(3)=" << F(3.0) << endl;
    cout << "Functor(3)=" << F(3.0) << endl;
    cout << "Functor(3)=" << F(3.0) << endl;

    cout << F << endl;
}


