import csv
import numpy as np

"""
  Effect of h for numerial differentiation
"""

# define function to be differentiated
def func(x):
    return x**3

# define analytical deviation of f(x)
def dfdx(x):
    return 3.0 * x * x

# numerical differentiation by two-point forward difference method
def diff2forward(func, x, h):
    return (func(x+h) - func(x)) / h;

#===================
# parameters
#===================
outfile = 'diff_h.csv'
h = [1.0, 0.1, 0.01, 0.001, 1.0e-6]
x0     = 0.0
xstep  = 0.1
nx     = 21

#===================
# main routine
#===================
print("Numerical differentiation using two-point forward difference method")
print("Write to [{}]".format(outfile))

# open outfile to write a csv file
f = open(outfile, 'w')
fout = csv.writer(f, lineterminator='\n')
fout.writerow([
    'x', 'f(x)', 'df/dx', 
    'Df/Dx(h={})'.format(h[0]),
    'Df/Dx(h={})'.format(h[1]),
    'Df/Dx(h={})'.format(h[2]),
    'Df/Dx(h={})'.format(h[3]),
    'Df/Dx(h={})'.format(h[4])
    ])

print("")
print("{:^5}:\t{:^12}\t{:^12}\t"
    "{:^12}\t{:^12}\t{:^12}\t{:^12}\t{:^12}"
    .format(
    'x', 'f(x)', 'df/dx', 
    'h={}'.format(h[0]), 
    'h={}'.format(h[1]), 
    'h={}'.format(h[2]), 
    'h={}'.format(h[3]), 
    'h={}'.format(h[4])) 
    )

for ix in range(nx):
    x = x0 + ix * xstep
    fx = func(x)
    f1x = dfdx(x)
    print("{:^5.3f}:\t{:^10.6f}\t{:^10.6f}\t".format(x, fx, f1x), end = '')
    diff = []
    for i in range(len(h)):
        diff.append(diff2forward(func, x, h[i]))
        print("{:^12.8f}\t".format(diff[i]), end = '')
    fout.writerow([x, fx, f1x] + diff)
    print("")

f.close()
