/* Lecture code 16.2
 *
 * Code to generate a function's derivative using functors.  This
 * example illustrates using templates to pass functors as parameters,
 * as well as type inference for functions.
 */

#include <iostream>
#include <cmath> // For sqrt
using namespace std;

const int NUM_STEPS = 20;

/* The TabulateValues function prints out twenty steps of the function between
 * the start and stop point.  Note that the function parameter is templatized over
 * some type UnaryFunction.  The name UnaryFunction is meaningless in this context
 * and serves only to indicate to callers what type of objects to pass in.  Passing
 * an invalid argument will result in a compile-time error.
 */
template <typename UnaryFunction>
void TabulateValues(double start, double stop, UnaryFunction function)
{
	const double step = (stop - start) / (NUM_STEPS - 1);
	for(int i = 0; i < NUM_STEPS; ++i)
	{
		const double value = start + step * i;
		cout << "f(" << value << ") = " << function(value) << endl;
	}
}

const double EPSILON = 0.00001;  // Epsilon for derivatives

/* Template class that wraps a unary function and produces a new unary
 * function that is the derivative of the source function, to a certain
 * precision.
 */
template <typename UnaryFunction>
class Derivative
{
public:
	/* Constructor stores the function of which to take the derivative. */
	explicit Derivative(UnaryFunction fn): function(fn)
	{
	}

	/* Functor operation returns the derivative at a point using the identity
	 *                   f(x + h) - f(x - h)
	 * f'(x)     =       -------------------
	 *        lim (h->0)          2h
	 *
	 * Here, we substitute the value EPSILON for the limit, which gives reasonable
	 * accuracy.
	 */
	double operator() (double input) const
	{
		return (function(input + EPSILON) - function(input - EPSILON)) / (2.0*EPSILON);
	}
private:
	UnaryFunction function;
};

/* Because we have to explicitly provide template arguments to classes, it can be
 * tricky to use the Derivative class.  This helper function takes in a unary function
 * and uses the template mechanism's type inference to construct and return a
 * Derivative object of the appropriate type.
 */
template <typename UnaryFunction>
Derivative<UnaryFunction> takeDerivative(UnaryFunction fn)
{
	return Derivative<UnaryFunction>(fn);
}

int main()
{
	/* Print out several values of the second derivative of the square root
	 * function.  Feel free to experiment with this and see what you can
	 * come up with!
	 */
	TabulateValues(1.0, 2.0, takeDerivative(takeDerivative(sqrt)));
	return 0;
}