# TestComplexStepMethod.py
#
# The complex step method for differentiation.
# 
# This code was ported from C++
#
# https://pdfs.semanticscholar.org/3de7/e8ae217a4214507b9abdac66503f057aaae9.pdf
#
# http://mdolab.engin.umich.edu/sites/default/files/Martins2003CSD.pdf
#
# (C) Datasim Education BV 2019
#

import numpy, math, cmath, random

j = cmath.sqrt(-1)

def Derivative(f, x, h):
# df/dx at x using tbe Complex Step Method

    z = x + h*j
    fz = f(z)
    return fz.imag/h;

def DerivativeZ(f, z):
# df/dx at z using tbe Complex Step Method

    #z = x + h*j
    fz = f(z)
    return fz.imag/h;

def DerivativeComposite(f, g, x, h):
# df(g)/dx at x  = df/dg X dg/dx using the Complex Step Method
# i.e. function composition

    z = x + h*j
    dgz = DerivativeZ(g,z)
    
    w = g(z)
    dfw = f(w)
   
    return (dfw.imag)/h + dgz.imag/h;

def SquireTrapp(t):
	n1 = cmath.exp(t);
	d1 = cmath.sin(t);
	d2 = cmath.cos(t);

	return n1 / (d1*d1*d1 + d2*d2*d2);

def func(t):
    return cmath.exp(t);

def func2(t):
    return cmath.cos(t);

def f(t):
    return cmath.exp(t);

def g(t):
    return cmath.exp(t);

x = 1.5
h = 0.001

ans = Derivative(SquireTrapp, x, h)
print(ans)

x = 5.0
ans = Derivative(func, x, h)
print(ans)

x = 1# math.sqrt(5)
ans2 = DerivativeComposite(g, g, x, h)
print(ans2)

b = math.exp(1)* math.exp(math.exp(1))
print(b)