/*****************************************************************************
 * File: Derivative.hh
 * Author: Keith Schwarz (htiek@cs.stanford.edu)
 *
 * A function that produces a function object containing an approximation of
 * the derivative of a real-valued function.  Internally, the function works
 * by handing back a function object that uses a secant-line approximation of
 * the derivative, as shown here:
 *
 *            f(x + e) - f(x - e)
 * f'(x) ~=   -------------------
 *                     2e
 *
 * This can be shown to produce the derivative with a very good approximation
 * for small e.
 */

#ifndef Derivative_Included
#define Derivative_Included

/* Forward declarations to make TakeDerivative make sense. */
namespace derivative_detail {
  template <typename UnaryFunction> class Derivative;
}

/**
 * Function: TakeDerivative(UnaryFunction function, double epsilon = 0.000001);
 * Usage: transform(v.begin(), v.end(), v.begin(), TakeDerivative(sinc));
 * --------------------------------------------------------------------------
 * Given a unary function (and optionally an epsilon), returns a function object
 * containing an approximation of the first derivative of that function.
 */

template <typename UnaryFunction>
derivative_detail::Derivative<UnaryFunction>
TakeDerivative(UnaryFunction function, double epsilon = 0.000001);

/* * * * * Implementation Below This Point * * * * */
namespace derivative_detail {
  /* Functor class responsible for approximating a derivative. */
  template <typename UnaryFunction> class Derivative {
  public:
    /* Constructor stores the function and epsilon to use. */
    Derivative(UnaryFunction function, double epsilon) 
      : mFunction(function), mEpsilon(epsilon) {
      // Handled in initializer list.
    }

    /* Functor operator produces a derivative approximation. */
    double operator() (double x) const {
      return (mFunction(x + mEpsilon) - mFunction(x - mEpsilon)) / (2.0 * mEpsilon);
    }

  private:
    /* The function to call and the obligatory epsilon. */
    UnaryFunction mFunction;
    double mEpsilon;
  };
}

/* Actual implementation of TakeDerivative. */
template <typename UnaryFunction>
derivative_detail::Derivative<UnaryFunction>
TakeDerivative(UnaryFunction function, double epsilon) {
  /* Construct a Derivative of the correct type, then hand it back. */
  return derivative_detail::Derivative<UnaryFunction>(function, epsilon);
}

#endif