# L-33 MCS 507 Mon 7 Nov 2011 : funwithdiff.py

# The class FunWithDiff defines an interface to compute first and second
# order derivatives of callable objects.  If the user does not override the
# methods for the derivatives, then numerical approximations are computed.
# The main program below causes the exception.

class FunWithDiff:
   """
   Defines an interface as superclass
   for numerical first and second order
   derivatives with central differences.
   """
   def __init__(self,h=1.0E-5):
      """
      Superclass constructor is never
      called but overridden.
      """
      self.h = h # step size for diff

   def __call__(self,x):
      """
      We work with callable objects.
      """
      raise NotImplementedError \
      ('__call__ missing in class %s' \
      % self.__class__.__name__)

   def df(self,x):
      """
      Approximates the first derivative.
      """
      h = self.h
      fpls = self(x+h)
      fmin = self(x-h)
      return (fpls - fmin)/(2.0*h)

   def ddf(self,x):
      """
      Approximates the second derivative.
      """
      h = self.h
      fp = self(x+h)
      fx = self(x)
      fm = self(x-h)
      return (fp - 2*fx + fm)/(float(h**2))

def main():
   """
   Test on exception handler.
   """
   f = FunWithDiff()
   print f(1)

if __name__=="__main__": main()
