Samer Adeeb

Introduction to Numerical Analysis: Numerical Differentiation

Introduction

Let f:\mathbb{R}\rightarrow\mathbb{R} be a smooth (differentiable) function, then the derivative of f at x is defined as the limit:

    \[ f'(x)=\lim_{\Delta x\rightarrow 0}\frac{f(x+\Delta x)-f(x)}{\Delta x} \]

When f is an explicit function of x, one can often find an expression for the derivative of f. For example, assuming f is a polynomial function such that f(x)=a_0+a_1x+a_2x^2+\cdots+a_nx^n, then the derivative of f is given by:

    \[ f'(x)=a_1+2a_2x+3a_3x^2+\cdots+na_nx^{n-1} \]

From a geometric perspective, the derivative of the function at a point x gives the slope of the tangent to the function at that point. The following tool shows the slope (\frac{\Delta y}{\Delta x}) of the tangent to the function f(x)=x^2+5x. You can move the slider to see how the slope changes. You can also download the code below and edit it to calculate the derivatives of any function you wish. This tool was created based on the Wolfram Demonstration Project of the derivative.

View Mathematica Code
Clear[x, f, x0]
f[x_] := x^2 + 5 x;
xstart = -10;
xstop = 10;
Deltax = xstart - xstop;
Manipulate[
 kchangesides = xstart/2 + xstop/2;
 koffsetleft = 0.2 Deltax;
 koffsetright = 0.15 Deltax;
 ystop = Maximize[{f[x], xstart <= x <= xstop}, x][[1]];
 ystart = Minimize[{f[x], xstart <= x <= xstop}, x][[1]];
 Deltay = (ystop - ystart)*0.5;
 ystop = ystop + Deltay;
 ystart = ystart - Deltay;
 dx = (xstop - xstart)/10;
 funccolor = RGBColor[0.378912, 0.742199, 0.570321];
 Plot[{f[x]}, {x, xstart, xstop}, 
  PlotRange -> {{xstart, xstop}, {ystart, ystop}}, ImageSize -> 600, 
  AxesLabel -> {Style["x", 14, Italic], Style["f[x]=" f[x], 15]}, 
  Background -> RGBColor[0.972549, 0.937255, 0.694118], 
  PlotStyle -> {{funccolor, Thickness[.005]}}, 
  Epilog -> {Black, PointSize[.015], Arrowheads[.025], Arrow[{{xstop - .02, 0}, {xstop, 0}}], Arrow[{{0, ystop - .02}, {0, ystop}}], Line[{{x0, f[x0]}, {x0 + 1, f[x0]}}], Thickness[.005], Blue, Line[{{x0 - dx, f[x0] - dx f'[x0]}, {x0 + dx,  f[x0] + dx f'[x0]}}], Red, Point[{x0, f[x0]}], Line[{{x0 + 1, f[x0]}, {x0 + 1, f'[x0] + f[x0]}}], Black,  Point[{x0 + 1, f'[x0] + f[x0]}], Point[{x0 + 1, f[x0]}], Red,  Style[Inset[Row[{"f'[", x0, "] = ", ToString[NumberForm[N@f'[x0], {4, 3}]]}], If[x0 < kchangesides, {x0 - koffsetleft, f[x0]}, {x0 + koffsetright, f[x0]}]], 14]}], 
{x0, xstart, xstop}]

The derivative of a function has many practical applications. Perhaps the most ubiquitous example is that of trying to calculate the velocity and the acceleration of a moving body if its position with respect to time is known. For example, one of our research projects in sports Biomechanics has focused on comparing the velocity and acceleration of squash players during tournament matches. To do so, the position of the player's feet can be tracked on the court and then, using numerical differentiation, the velocity and acceleration can be quantified. The following video shows the tracked feet of a few rallies of one of the analyzed games:

In this case, and in many similar cases, there is no explicit function to be differentiated, and therefore, one has to deal with numerically differentiating the existing data. In other cases, an explicit formula for calculating the derivative might not exist and thus, if the derivative is sought, a numerical tool would be necessary.

Another similar interesting application is the estimation of the possible 100m time of Usain Bolt during the 2008 Beijing Olympics had he not celebrated in the last 20m during his race. Using some techniques described here along with some statistical analyses the authors of the article cited provide a good estimate of the time that Usain could have achieved.

Basic Numerical Differentiation Formulas

The numerical differentiation formulas presented in the Taylor Series section will be repeated here.

Forward Finite Difference

Let f:[a,b]\rightarrow\mathbb{R} be differentiable and let a\leq x_i<x_{i+1}\leq b, then, using Taylor theorem:

    \[ f(x_{i+1})=f(x_i)+f'(x_i)h+\mathcal O (h^2) \]

where h=x_{i+1}-x_i. In this case, the forward finite-difference can be used to approximate f'(x_{i}) as follows:

(1)   \begin{equation*}  f'(x_{i})=\frac{f(x_{i+1})-f(x_i)}{x_{i+1}-x_i}+\mathcal O (h) \end{equation*}

Where \mathcal O (h) indicates that the error term is directly proportional to the chosen step size h.

Backward Finite Difference

Let f:[a,b]\rightarrow\mathbb{R} be differentiable and let a\leq x_{i-1}<x_{i}\leq b, then, using Taylor theorem:

    \[ f(x_{i-1})=f(x_i)-f'(x_i)h+\mathcal O (h^2) \]

where h=x_i-x_{i-1}. In this case, the backward finite-difference can be used to approximate f'(x_{i}) as follows:

(2)   \begin{equation*}  f'(x_{i})=\frac{f(x_{i-1})-f(x_i)}{x_{i-1}-x_i}+\mathcal O (h) \end{equation*}

Where \mathcal O (h) indicates that the error term is directly proportional to the chosen step size h.

Centred Finite Difference

The centred finite difference can provide a better estimate for the derivative of a function at a particular point. If the values of a function f are known at the points x_{i-1}<x_i<x_{i+1} and x_{i+1}-x_i=x_i-x_{i-1}=h, then, we can use the Taylor series to find a good approximation for the derivative as follows:

    \[\begin{split} f(x_{i+1})&=f(x_i)+f'(x_i) h+\frac{f''(x_i)}{2!}h^2+\mathcal O (h^3)\\ f(x_{i-1})&=f(x_i)+f'(x_i) (-h)+\frac{f''(x_i)}{2!}h^2+\mathcal O (h^3) \end{split} \]

Subtracting the above two equations and dividing by h gives the following:

    \[ f'(x_i)=\frac{f(x_{i+1})-f(x_{i-1})}{2h}+\mathcal O (h^2) \]

Where \mathcal O (h^2) indicates that the error term is directly proportional to the square of the chosen step size h. I.e., the centred finite difference provides a better estimate for the derivative when the step size h is reduced compared to the forward and backward finite differences. Notice that when x_{i+1}-x_i=x_i-x_{i-1}, the centred finite difference is the average of the forward and backward finite difference!

The following tool illustrates the difference between forward, backward, and centred finite difference when calculating the numerical derivative of the function f(x)=\cos(x) at a point x=x_0 with a step h. Move the slider to vary the value of x_0 and h to see the effect on the calculated derivative. The centred finite difference in most cases provides a closer estimate to the true value of the derivative.

Basic Numerical Differentiation Formulas for Higher Derivatives

The formulas presented in the previous section can be extended naturally to higher-order derivatives as follows.

Forward Finite Difference

Let f:[a,b]\rightarrow\mathbb{R} be differentiable and let a\leq x_i<x_{i+1}<x_{i+2}\leq b, with h=x_{i+1}-x_i=x_i-x_{i-1}, then, using the basic forward finite difference formula for the second derivative, we have:

(3)   \begin{equation*}\begin{split} f''(x_i)&= \frac{f'(x_{i+1})-f'(x_i)}{h} +\mathcal O (h)\\ &=\frac{\frac{f(x_{i+2})-f(x_{i+1})}{h}-\frac{f(x_{i+1})-f(x_i)}{h}}{h}+\mathcal O (h)\\ &=\frac{f(x_{i+2})-2f(x_{i+1})+f(x_i)}{h^2}+\mathcal O (h) \end{split} \end{equation*}

Notice that in order to calculate the second derivative at a point x_i using forward finite difference, the value of the function at two additional points x_{i+2} and x_{i+1} is needed.

Similarly, for the third derivative, the value of the function at another point x_{i+3} with x_{i+2}<x_{i+3}\leq b is required (with the same spacing h). Then, the third derivative can be calculated as follows:

    \[\begin{split} f'''(x_i) & = \frac{f''(x_{i+1})-f''(x_i)}{h}+\mathcal O (h)\\ &=\frac{\frac{f(x_{i+3})-2f(x_{i+2})+f(x_{i+1})}{h^2}-\frac{f(x_{i+2})-2f(x_{i+1})+f(x_i)}{h^2}}{h}+\mathcal O (h)\\ &=\frac{f(x_{i+3})-3f(x_{i+2})+3f(x_{i+1})-f(x_i)}{h^3}+\mathcal O (h) \end{split} \]

Similarly, for the fourth derivative, the value of the function at another point x_{i+4} with x_{i+3}<x_{i+4}\leq b is required (with the same spacing h). Then, the fourth derivative can be calculated as follows:

    \[\begin{split} f''''(x_i) & = \frac{f'''(x_{i+1})-f'''(x_i)}{h}+\mathcal O (h)\\ &=\frac{\frac{f(x_{i+4})-3f(x_{i+3})+3f(x_{i+2})-f(x_{i+1})}{h^3}-\frac{f(x_{i+3})-3f(x_{i+2})+3f(x_{i+1})-f(x_i)}{h^3}}{h} +\mathcal O (h)\\ &=\frac{f(x_{i+4})-4f(x_{i+3})+6f(x_{i+2})-4f(x_{i+1})+f(x_i)}{h^4}+\mathcal O (h) \end{split} \]

Backward Finite Difference

Let f:[a,b]\rightarrow\mathbb{R} be differentiable and let a\leq x_{i-2}<x_{i-1}<x_{i}\leq b, with h=x_{i}-x_{i-1}=x_{i-1}-x_{i-2}, then, using the basic backward finite difference formula for the second derivative, we have:

(4)   \begin{equation*}\begin{split} f''(x_i)&= \frac{f'(x_{i})-f'(x_{i-1})}{h}+\mathcal O (h)\\ &=\frac{\frac{f(x_{i})-f(x_{i-1})}{h}-\frac{f(x_{i-1})-f(x_{i-2})}{h}}{h}+\mathcal O (h)\\ &=\frac{f(x_{i})-2f(x_{i-1})+f(x_{i-2})}{h^2}+\mathcal O (h) \end{split} \end{equation*}

Notice that in order to calculate the second derivative at a point x_i using backward finite difference, the value of the function at two additional points x_{i-2} and x_{i-1} is needed.

Similarly, for the third derivative, the value of the function at another point x_{i-3} with a\leq x_{i-3}<x_{i-2} is required (with the same spacing h). Then, the third derivative can be calculated as follows:

    \[\begin{split} f'''(x_i) & = \frac{f''(x_{i})-f''(x_{i-1})}{h}+\mathcal O (h)\\ &=\frac{\frac{f(x_{i})-2f(x_{i-1})+f(x_{i-2})}{h^2}-\frac{f(x_{i-1})-2f(x_{i-2})+f(x_{i-3})}{h^2}}{h}+\mathcal O (h)\\ &=\frac{f(x_{i})-3f(x_{i-1})+3f(x_{i-2})-f(x_{i-3})}{h^3}+\mathcal O (h) \end{split} \]

Similarly, for the fourth derivative, the value of the function at another point x_{i-4} with a\leq x_{i-4}<x_{i-3} is required (with the same spacing h). Then, the fourth derivative can be calculated as follows:

    \[\begin{split} f''''(x_i) & = \frac{f'''(x_{i})-f'''(x_{i-1})}{h}+\mathcal O (h)\\ &=\frac{\frac{f(x_{i})-3f(x_{i-1})+3f(x_{i-2})-f(x_{i-3})}{h^3}-\frac{f(x_{i-1})-3f(x_{i-2})+3f(x_{i-3})-f(x_{i-4})}{h^3}}{h}+\mathcal O (h)\\ &=\frac{f(x_{i})-4f(x_{i-1})+6f(x_{i-2})-4f(x_{i-3})+f(x_{i-4})}{h^4}+\mathcal O (h) \end{split} \]

Centred Finite Difference

Let f:[a,b]\rightarrow\mathbb{R} be differentiable and let a\leq x_{i-1}<x_{i}<x_{i+1}\leq b, with a constant spacing h then, we can use the Taylor theorem for f(x_{i+1}) and f(x_{i-1}) as follows:

    \[\begin{split} f(x_{i+1})&=f(x_i)+f'(x_i) h+\frac{f''(x_i)}{2!}h^2+\frac{f'''(x_i)}{3!}h^3+\mathcal O (h^4)\\ f(x_{i-1})&=f(x_i)+f'(x_i) (-h)+\frac{f''(x_i)}{2!}h^2-\frac{f'''(x_i)}{3!}h^3+\mathcal O (h^4) \end{split} \]

Adding the above two equations and dividing by h^2 gives the following:

(5)   \begin{equation*}  f''(x_i)=\frac{f(x_{i+1})-2f(x_i)+f(x_{i-1})}{h^2}+\mathcal O (h^2) \end{equation*}

which provides a better approximation for the second derivative than that provided by the forward or backward finite difference as the error is directly proportional to the square of the step size.
For the third derivative, the value of the function is required at the points x_{i-2} and x_{i+2}. Assuming all the points to be equidistant with a spacing h, then, the third derivative can be calculated using Equation 5 as follows:

    \[ f'''(x_{i})= \frac{f'(x_{i+1})-2f'(x_i)+f'(x_{i-1})}{h^2}+\mathcal O (h^2) \]

Replacing the first derivatives with the centred finite difference value for those:

(6)   \begin{equation*}\begin{split} f'''(x_{i})&= \frac{\frac{f(x_{i+2})-f(x_{i})}{2h}-2\frac{f(x_{i+1})-f(x_{i-1})}{2h}+\frac{f(x_{i})-f(x_{i-2})}{2h}}{h^2}+\mathcal O (h^2)\\ &=\frac{f(x_{i+2})-2f(x_{i+1})+2f(x_{i-1})-f(x_{i-2})}{2h^3}+\mathcal O (h^2) \end{split} \end{equation*}

For the fourth derivative, the value of the function at the points x_{i+2} and x_{i-2} is required. Assuming all the points to be equidistant with a spacing h, then, the fourth derivative can be calculated using Equation 5 as follows:

    \[ f''''(x_i)=\frac{f''(x_{i+1})-2f''(x_i)+f''(x_{i-1})}{h^2}+\mathcal O (h^2) \]

Using the centred finite difference for the second derivatives (Equation 5) yields:

    \[\begin{split} f''''(x_i) & = \frac{\frac{f(x_{i+2})-2f(x_{i+1})+f(x_{i})}{h^2}-2\frac{f(x_{i+1})-2f(x_i)+f(x_{i-1})}{h^2}+\frac{f(x_{i})-2f(x_{i-1})+f(x_{i-2})}{h^2}}{h^2} +\mathcal O (h^2)\\ &=\frac{f(x_{i+2})-4f(x_{i+1})+6f(x_{i})-4f(x_{i-1})+f(x_{i-2})}{h^4}+\mathcal O (h^2) \end{split} \]

High-Accuracy Numerical Differentiation Formulas

The formulas presented in the previous sections for the forward and backward finite difference have an error term of \mathcal O (h) while those for the centred finite difference scheme have an error term of \mathcal O (h^2). It is possible to provide formulas with less error by utilizing more terms in the Taylor approximation. In essence, by increasing the number of terms in the Taylor series approximation, we assume a higher-order polynomial for the approximation which increases the accuracy of the derivatives. In the following presentation, the function f is assumed to be smooth and the points are equidistant with a step size of h.

Forward Finite Difference

According to Taylor theorem, we have:

    \[ f(x_{i+1})=f(x_i)+f'(x_i) h+\frac{f''(x_i)}{2!}h^2+\mathcal O (h^3)\\ \]

Using the forward finite difference equation (Equation 3) for f''(x_i) yields:

    \[ f'(x_i) h=f(x_{i+1})-f(x_i)-\frac{f(x_{i+2})-2f(x_{i+1})+f(x_i)}{2}+\mathcal O (h^3)\\ \]

Therefore:

    \[ f'(x_i)=\frac{-f(x_{i+2})+4f(x_{i+1})-3f(x_{i})}{2h}+\mathcal O (h^2)\\ \]

In comparison with Equation 1, this equation provides an error term that is directly proportional to the square of the step size indicating higher accuracy.

Using the same procedure, the following equations can be obtained for the second, third, and fourth derivatives:

    \[\begin{split} f''(x_{i})&=\frac{-f(x_{i+3})+4f(x_{i+2})-5f(x_{i+1})+2f(x_{i})}{h^2}+\mathcal O (h^2)\\ f'''(x_{i})&=\frac{-3f(x_{i+4})+14f(x_{i+3})-24f(x_{i+2})+18f(x_{i+1})-5f(x_{i})}{2h^3}+\mathcal O (h^2)\\ f''''(x_{i})&=\frac{-2f(x_{i+5})+11f(x_{i+4})-24f(x_{i+3})+26f(x_{i+2})-14f(x_{i+1})+3f(x_{i})}{h^4}+\mathcal O (h^2) \end{split} \]

Backward Finite Difference

According to Taylor theorem, we have:

    \[ f(x_{i-1})=f(x_i)-f'(x_i) h+\frac{f''(x_i)}{2!}h^2+\mathcal O (h^3)\\ \]

Using the backward finite difference equation (Equation 4) for f''(x_i) yields:

    \[ f'(x_i) h=-f(x_{i-1})+f(x_i)+\frac{f(x_{i})-2f(x_{i-1})+f(x_{i-2})}{2}+\mathcal O (h^3)\\ \]

Therefore:

    \[ f'(x_i)=\frac{3f(x_{i})-4f(x_{i-1})+f(x_{i-2})}{2h}+\mathcal O (h^2)\\ \]

In comparison with Equation 2, this equation provides an error term that is directly proportional to the square of the step size indicating higher accuracy.
Using the same procedure, the following equations can be obtained for the second, third, and fourth derivatives:

    \[\begin{split} f''(x_{i})&=\frac{2f(x_{i})-5f(x_{i-1})+4f(x_{i-2})-f(x_{i-3})}{h^2}+\mathcal O (h^2)\\ f'''(x_{i})&=\frac{5f(x_{i})-18f(x_{i-1})+24f(x_{i-2})-14f(x_{i-3})+3f(x_{i-4})}{2h^3}+\mathcal O (h^2)\\ f''''(x_{i})&=\frac{3f(x_{i})-14f(x_{i-1})+26f(x_{i-2})-24f(x_{i-3})+11f(x_{i-4})-2f(x_{i-5})}{h^4}+\mathcal O (h^2) \end{split} \]

Centred Finite Difference

According to Taylor theorem, we have:

    \[\begin{split} f(x_{i+1})&=f(x_i)+f'(x_i) h+\frac{f''(x_i)}{2!}h^2+\frac{f'''(x_i)}{3!}h^3+\frac{f''''(x_i)}{4!}h^4+\mathcal O (h^5)\\ f(x_{i-1})&=f(x_i)-f'(x_i) h+\frac{f''(x_i)}{2!}h^2-\frac{f'''(x_i)}{3!}h^3+\frac{f''''(x_i)}{4!}h^4+\mathcal O (h^5) \end{split} \]

Subtracting the above two equations and using the centred finite difference equation (Equation 6) for f'''(x_i) yields:

    \[ f'(x_i) 2h=f(x_{i+1})-f(x_{i-1})-\frac{f(x_{i+2})-2f(x_{i+1})+2f(x_{i-1})-f(x_{i-2})}{6}+\mathcal O (h^5)\\ \]

Therefore:

    \[ f'(x_i) =\frac{-f(x_{i+2})+8f(x_{i+1})-8f(x_{i-1})+f(x_{i-2})}{12h}+\mathcal O (h^4)\\ \]

Using the same procedure, the following equations can be obtained for the second, third, and fourth derivatives:

    \[\begin{split} f''(x_{i})&=\frac{-f(x_{i+2})+16f(x_{i+1})-30f(x_{i})+16f(x_{i-1})-f(x_{i-2})}{12h^2}+\mathcal O (h^4)\\ f'''(x_{i})&=\frac{-f(x_{i+3})+8f(x_{i+2})-13f(x_{i+1})+13f(x_{i-1})-8f(x_{i-2})+f(x_{i-3})}{8h^3}+\mathcal O (h^4)\\ f''''(x_{i})&=\frac{-f(x_{i+3})+12f(x_{i+2})-39f(x_{i+1})+56f(x_{i})-39f(x_{i-1})+12f(x_{i-2})-f(x_{i-3})}{6h^4}+\mathcal O (h^4) \end{split} \]

Comparison

For the sake of comparison, data points generated by the Runge function y=\frac{1}{1+25x^2} in the range x\in[-1,1] are used to compare the different formulas obtained above. The exact first, second, third, and fourth derivatives of the function are plotted against the first, second, third, and fourth derivatives obtained using the formulas above. To differentiate between the results, the terms FFD, BFD, and CFD are used for forward, backward, and centred finite difference respectively. FFD1h1,FFD2h1, FFD3h1, and FFD4h1 indicate the forward finite difference first, second, third, and fourth derivatives respectively obtained using the basic formulas whose error terms are \mathcol{O} (h). The same applies for the basic formulas of the backward finite difference. However, CFD1h2, CFD2h2, CFD3h2, and CFD4h2 indicate the centred finite difference first, second, third, and fourth derivatives respectively obtained using the basic formulas whose error terms are \mathcal{O}(h^2). For higher accuracy formulas of the forward and backward finite difference, h1 is replaced by h2, while for higher accuracy formulas of the centred finite difference, h2 is replaced by h4. To generate these plots, the following Mathematica code was utilized. Given a table of data of two dimensions (x_i,y_i), the procedures in the code below provides the tables of the derivatives listed above. Note that the code below generates the data using the Runge function with a specific h, but does not apply the procedure to the data.
View Mathematica Code

h=0.1;
n = 2/h + 1;
y = 1/(1 + 25 x^2);
yp = D[y, x];
ypp = D[yp, x];
yppp = D[ypp, x];
ypppp = D[yppp, x];
Data = Table[{-1 + h*(i - 1), (y /. x -> -1 + h*(i - 1))}, {i, 1, n}];
FFD1h1[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]]; Table[{Data[[i, 1]], (f[[i + 1]] - f[[i]])/h}, {i, 1, n - 1}]);
FFD1h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]]; Table[{Data[[i, 1]], (-f[[i + 2]] + 4 f[[i + 1]] - 3 f[[i]])/2/h}, {i, 1, n - 2}]);
FFD2h1[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i, 1]], (f[[i + 2]] - 2 f[[i + 1]] + f[[i]])/h^2}, {i, 1, n - 2}]);
FFD2h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]]; Table[{Data[[i,1]], (-f[[i + 3]] + 4 f[[i + 2]] - 5 f[[i + 1]] + 2 f[[i]])/ h^2}, {i, 1, n - 3}]);
FFD3h1[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]];  f = Data[[All, 2]]; Table[{Data[[i,  1]], (f[[i + 3]] - 3 f[[i + 2]] + 3 f[[i + 1]] - f[[i]])/ h^3}, {i, 1, n - 3}]);
FFD3h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]]; Table[{Data[[i,  1]], (-3 f[[i + 4]] + 14 f[[i + 3]] - 24 f[[i + 2]] + 18 f[[i + 1]] - 5 f[[i]])/2/h^3}, {i, 1, n - 4}]);
FFD4h1[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]]; Table[{Data[[i, 1]], (f[[i + 4]] - 4 f[[i + 3]] + 6 f[[i + 2]] - 4 f[[i + 1]] +  f[[i]])/h^4}, {i, 1, n - 4}]);
FFD4h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]];  f = Data[[All, 2]]; Table[{Data[[i,1]], (-2 f[[i + 5]] + 11 f[[i + 4]] - 24 f[[i + 3]] +  26 f[[i + 2]] - 14 f[[i + 1]] + 3 f[[i]])/h^4}, {i, 1,  n - 5}]);
BFD1h1[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i, 1]], (f[[i]] - f[[i - 1]])/h}, {i, 2, n}]);
BFD1h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i, 1]], (3 f[[i]] - 4 f[[i - 1]] + f[[i - 2]])/2/h}, {i, 3, n}]);
BFD2h1[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i, 1]], (f[[i]] - 2 f[[i - 1]] + f[[i - 2]])/h^2}, {i, 3, n}]);
BFD2h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]];  f = Data[[All, 2]];  Table[{Data[[i,  1]], (2 f[[i]] - 5 f[[i - 1]] + 4 f[[i - 2]] - f[[i - 3]])/ h^2}, {i, 4, n}]);
BFD3h1[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i,  1]], (f[[i]] - 3 f[[i - 1]] + 3 f[[i - 2]] - f[[i - 3]])/ h^3}, {i, 4, n}]);
BFD3h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i, 1]], (5 f[[i]] - 18 f[[i - 1]] + 24 f[[i - 2]] - 14 f[[i - 3]] + 3 f[[i - 4]])/2/h^3}, {i, 5, n}]);
BFD4h1[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i,   1]], (f[[i]] - 4 f[[i - 1]] + 6 f[[i - 2]] - 4 f[[i - 3]] +  f[[i - 4]])/h^4}, {i, 5, n}]);
BFD4h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]];  f = Data[[All, 2]];  Table[{Data[[i,  1]], (3 f[[i]] - 14 f[[i - 1]] + 26 f[[i - 2]] -   24 f[[i - 3]] + 11 f[[i - 4]] - 2 f[[i - 5]])/h^4}, {i, 6,  n}]);
CFD1h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]];  f = Data[[All, 2]];  Table[{Data[[i, 1]], (f[[i + 1]] - f[[i - 1]])/2/h}, {i, 2,  n - 1}]);
CFD1h4[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]];  f = Data[[All, 2]];  Table[{Data[[i,  1]], (-f[[i + 2]] + 8 f[[i + 1]] - 8 f[[i - 1]] + f[[i - 2]])/12/h}, {i, 3, n - 2}]);
CFD2h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i, 1]], (f[[i + 1]] - 2 f[[i]] + f[[i - 1]])/h^2}, {i,2, n - 1}]);
CFD2h4[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i, 1]], (-f[[i + 2]] + 16 f[[i + 1]] - 30 f[[i]] + 16 f[[i - 1]] -  f[[i - 2]])/12/h^2}, {i, 3, n - 2}]);
CFD3h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i,  1]], (f[[i + 2]] - 2 f[[i + 1]] + 2 f[[i - 1]] - f[[i - 2]])/2/ h^3}, {i, 3, n - 2}]);
CFD3h4[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]];  Table[{Data[[i,  1]], (-f[[i + 3]] + 8 f[[i + 2]] - 13 f[[i + 1]] + 13 f[[i - 1]] - 8 f[[i - 2]] + f[[i - 3]])/8/h^3}, {i, 4, n - 3}]);
CFD4h2[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]]; Table[{Data[[i,  1]], (f[[i + 2]] - 4 f[[i + 1]] + 6 f[[i]] - 4 f[[i - 1]] +   f[[i - 2]])/h^4}, {i, 3, n - 2}]);
CFD4h4[Data_] := (n = Length[Data]; h = Data[[2, 1]] - Data[[1, 1]]; f = Data[[All, 2]]; Table[{Data[[i,   1]], (-f[[i + 3]] + 12 f[[i + 2]] - 39 f[[i + 1]] +   56 f[[i]] - 39 f[[i - 1]] + 12 f[[i - 2]] - f[[i - 3]])/6/ h^4}, {i, 4, n - 3}]);

Basic Formulas

The following tool draws the plots of the exact first, second, third, and fourth derivatives of the Runge function overlaid with the data points of the first, second, third, and fourth derivatives obtained using the basic formulas for the forward, backward, and centred finite difference. Choose h=0.05 and notice the following: in general, the centred finite difference scheme provides more accurate results, especially for the second and third derivatives. For all the formulas, accuracy is lost with the higher derivatives especially in the neighbourhood of x=0 where the function and its derivatives have large variations within a small region. The forward finite difference uses the function information on the right of the point to calculate the derivative and hence the calculated derivatives appear to be slightly shifted to the left in comparison with the exact derivatives. This is evident when looking at any of the derivatives. Similarly, the backward finite difference uses the function information on the left of the point to calculate the derivative and hence the calculated derivatives appear to be slightly shifted to the right in comparison with the exact derivatives. The centred finite difference scheme does not exhibit the same behaviour because it uses the information from both sides. When choosing h\leq 0.01, the formulas provide very good approximation with the exact derivatives almost exactly overlaid with the finite difference calculations. However, for h\geq 0.1, the results of the finite difference calculations for the higher derivatives are far from accurate in the neighbourhood of x=0 for the third and fourth derivatives.

Choose h=0.1 and notice that for the first derivative using forward finite difference, the derivative for the last point x=1 is unavailable (why?). Similarly, using the backward finite difference, the derivative for the first point x=-1 is unavailable. The derivatives for the first and last points are unavailable if the centred finite difference scheme is used. This carries on for the higher derivatives with the second, third, and fourth derivatives being unavailable for the last two, three, and four points when the forward finite difference scheme is used. The situation is reversed for the backward finite difference. The centred finite difference scheme provides more balanced results. The first and second derivatives at the first and last points are unavailable while the third and fourth derivatives at the first two points and the last two points are unavailable.

Higher Accuracy Formulas

The observations from the basic formulas carry over to the higher accuracy formulas except that the results are unavailable at a larger number of points on the edges of the domain. Choose h=0.1 and notice that the first derivative is unavailable at the last two points because the forward finite difference higher accuracy formulas use the information at x_{i+2} to calculate the derivative at x_i. Similarly, the first derivative is unavailable at the first two points in the backward finite difference higher accuracy formulas because the scheme uses the information at x_{i-2} to calculate the derivative at x_i. For higher derivatives, more results are unavailable at a larger number of points close to the edge of the domain. Similar to the basic formulas scheme, the centred finite difference provides more balanced results with the results being unavailable at the points on both edges of the domain.

Forward Finite Difference

Both the basic formulas, and higher accuracy formulas for the forward finite difference scheme provide similar results. For h>0.05, both schemes provide very good approximations for the first derivative, and for the flat parts of the higher derivatives. As explained above, the derivatives calculated using the formulas appear to be shifted to the left in comparison to the actual derivatives. For h<0.01, both schemes provide relatively accurate predictions.

Backward Finite Difference

Both the basic formulas, and higher accuracy formulas for the backward finite difference scheme provide similar results. For h>0.05, both schemes provides very good approximations for the first derivative, and for the flat parts of the higher derivatives. As explained above, the derivatives calculated using the formulas appear to be shifted to the right in comparison to the actual derivatives. For h<0.01, both schemes provide relatively accurate predictions.

Centred Finite Difference

When h<0.2, except for the fourth derivative at x=0 both centred finite difference schemes provide predictions that are very close to the exact results. It is obvious from these figures that when h is relatively large, a centred finite difference scheme is perhaps more appropriate as it is more balanced and provides better accuracy. For smaller values of h, all the schemes provide relatively acceptable predictions.

Derivatives Using Interpolation Functions

The basic and high-accuracy formulas derived above were based on equally spaced data, i.e., the distance between the points x_{i-2}, x_{i-1}, x_i, x_{i+1}, x_{i+2} is constant. It is possible to derive formulas for unequally spaced data such that the derivatives would be a function of the spacings close to x_i. Another method is to fit a piecewise interpolation function of a particular order to the data and then calculate its derivative as described previously. This technique in fact provides better estimates for higher values of h as shown in the following tools. However, the disadvantage is that depending on the order of the interpolation function, higher derivatives are not available.

First Order Interpolation

The first order interpolation provides results for the first derivatives that are exactly similar to the results using forward finite difference. However, a first order interpolation function predicts zero second and higher derivatives. In the following tool, h defines the interval at which the Runge function is sampled. The first derivative is calculated similar to the forward finite difference method. The higher derivatives are zero. The tool overlays the actual derivative with that predicted using the interpolation function. The black dots provide the values of the derivatives at the sampled points.

Second Order Interpolation

The second order interpolation provides much better results for h\leq 0.1 compared with the finite difference formulas. However, the third and fourth derivatives are equal to zero. Change the value of h to see its effect on the results.

Third Order Interpolation

The third order interpolation (cubic splines) provides much better results for h\leq 0.2 compared with the finite difference formulas. However, the fourth derivative is equal to zero. Change the value of h to see its effect on the results.

The following Mathematica code can be used to generate the tools above.
View Mathematica Code

h = 0.2;
n = 2/h + 1;
y = 1/(1 + 25 x^2);
yp = D[y, x];
ypp = D[yp, x];
yppp = D[ypp, x];
ypppp = D[yppp, x];
Data = Table[{-1 + h*(i - 1), (y /. x -> -1 + h*(i - 1))}, {i, 1, n}];
y2 = Interpolation[Data, Method -> "Spline", InterpolationOrder -> 1];
ypInter = D[y2[x], x];
yppInter = D[y2[x], {x, 2}];
ypppInter = D[y2[x], {x, 3}];
yppppInter = D[y2[x], {x, 4}];
Data1 = Table[{Data[[i, 1]], (ypInter /. x -> Data[[i, 1]])}, {i, 1,     n}];
Data2 = Table[{Data[[i, 1]], (yppInter /. x -> Data[[i, 1]])}, {i, 1,    n}];
Data3 = Table[{Data[[i, 1]], (ypppInter /. x -> Data[[i, 1]])}, {i, 1,    n}];
Data4 = Table[{Data[[i, 1]], (yppppInter /. x -> Data[[i, 1]])}, {i,    1, n}];
a0 = Plot[{y, y2[x]}, {x, -1, 1},   Epilog -> {PointSize[Large], Point[Data]}, AxesLabel -> {"x", "y"},  BaseStyle -> Bold, ImageSize -> Medium, PlotRange -> All,  PlotLegends -> {"y actual", "y spline"},    PlotLabel -> "First order splines"];
a1 = Plot[{yp, ypInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data1]},    AxesLabel -> {"x", "y'"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y'", "y'spline"},    PlotLabel -> "First derivative"];
a2 = Plot[{ypp, yppInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data2]},    AxesLabel -> {"x", "y''"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y''", "y''spline"},    PlotLabel -> "Second derivative"];
a3 = Plot[{yppp, ypppInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data3]},    AxesLabel -> {"x", "y''"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y'''", "y'''spline"},    PlotLabel -> "Third derivative"];
a4 = Plot[{ypppp, yppppInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data4]},    AxesLabel -> {"x", "y''"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y''''", "y''''spline"},    PlotLabel -> "Fourth derivative"];
Grid[{{a0}, {a1}, {a2}, {a3}, {a4}}]

Effect of Data Errors

One of the major problems when numerically differentiating empirical data is the existence of random (measurement) errors within the data itself. These errors cause the experimental data to have some natural fluctuations. If the experimental data is supposed to follow a particular model, then the best way to deal with such data is to find the best fit and use the derivatives of the best fit as described in the curve fitting section. However, if such a model does not exist, for example when measuring the position function of a moving subject, then care has to be taken when dealing with higher derivatives of the data. In particular, the fluctuations are magnified when higher derivatives are calculated.

The following figures were generated by first sampling the Runge function on the domain [-1,1] at an interval of 0.1, then a random number between -0.1 and 0.1 is added to the data points. A cubic spline is then fit into the data. The small fluctuations in the data lead to very high fluctuations in all the derivatives. In particular, the second and third derivatives do not provide any useful information.
FD1

View Mathematica Code
Clear[x]
h = 0.1;
n = 2/h + 1;
y = 1/(1 + 25 x^2);
yp = D[y, x];
ypp = D[yp, x];
yppp = D[ypp, x];
ypppp = D[yppp, x];
Data = Table[{-1 + h*(i - 1), (y /. x -> -1 + h*(i - 1)) +RandomReal[{-0.1, 0.1}]}, {i, 1, n}];
y2 = Interpolation[Data, Method -> "Spline", InterpolationOrder -> 3];
ypInter = D[y2[x], x];
yppInter = D[y2[x], {x, 2}];
ypppInter = D[y2[x], {x, 3}];
yppppInter = D[y2[x], {x, 4}];
Data1 = Table[{Data[[i, 1]], (ypInter /. x -> Data[[i, 1]])}, {i, 1, n}];
Data2 = Table[{Data[[i, 1]], (yppInter /. x -> Data[[i, 1]])}, {i, 1,n}];
Data3 = Table[{Data[[i, 1]], (ypppInter /. x -> Data[[i, 1]])}, {i, 1, n}];
Data4 = Table[{Data[[i, 1]], (yppppInter /. x -> Data[[i, 1]])}, {i, 1, n}];
a0 = Plot[{y, y2[x]}, {x, -1, 1}, Epilog -> {PointSize[Large], Point[Data]}, AxesLabel -> {"x", "y"}, BaseStyle -> Bold, ImageSize -> Medium, PlotRange -> All, PlotLegends -> {"y actual", "y spline"},  PlotLabel -> "Third order splines"];
a1 = Plot[{yp, ypInter}, {x, -1, 1}, Epilog -> {PointSize[Large], Point[Data1]}, AxesLabel -> {"x", "y'"}, BaseStyle -> Bold, ImageSize -> Medium, PlotRange -> All, PlotLegends -> {"y'", "y'spline"},  PlotLabel -> "First derivative"];
a2 = Plot[{ypp, yppInter}, {x, -1, 1}, Epilog -> {PointSize[Large], Point[Data2]}, AxesLabel -> {"x", "y''"}, BaseStyle -> Bold, ImageSize -> Medium,  PlotRange -> All, PlotLegends -> {"y''", "y''spline"},   PlotLabel -> "Second derivative"];
a3 = Plot[{yppp, ypppInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data3]},    AxesLabel -> {"x", "y'''"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y'''", "y'''spline"},    PlotLabel -> "Third derivative"];
a4 = Plot[{ypppp, yppppInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data4]},    AxesLabel -> {"x", "y''''"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y''''", "y''''spline"},    PlotLabel -> "Fourth derivative"];
Grid[{{a0,a1}, {a2,a3}}]

Similarly, the shown figures show the results when random numbers between -0.05 and 0.05 are added to the sampled data of the Runge function. When the centred finite difference scheme is used, the errors lead to very large variations in the second and third derivatives. The fourth derivative does not provide any useful information.
random2
random3

One possible solution to reduce noise in the data is to use a "Filter" to smooth the data. In the following figure, the built-in Gaussian Filter in Mathematica is used to reduce the generated noise. The fluctuations in the data are reduced and smoother derivatives are obtained.
FD2
View Mathematica Code

Clear[x]
h = 0.1;
n = 2/h + 1;
y = 1/(1 + 25 x^2);
yp = D[y, x];
ypp = D[yp, x];
yppp = D[ypp, x];
ypppp = D[yppp, x];
Data = Table[{-1 + h*(i - 1), (y /. x -> -1 + h*(i - 1)) +    RandomReal[{-0.1, 0.1}]}, {i, 1, n}];
Dataa = Data;
f = Data[[All, 2]];
f = GaussianFilter[f, 3];
Data = Table[{Data[[i, 1]], f[[i]]}, {i, 1, n}];
yun = Interpolation[Dataa, Method -> "Spline",   InterpolationOrder -> 3];
y2 = Interpolation[Data, Method -> "Spline", InterpolationOrder -> 3];
ypInter = D[y2[x], x];
yppInter = D[y2[x], {x, 2}];
ypppInter = D[y2[x], {x, 3}];
yppppInter = D[y2[x], {x, 4}];
Data1 = Table[{Data[[i, 1]], (ypInter /. x -> Data[[i, 1]])}, {i, 1,  n}];
Data2 = Table[{Data[[i, 1]], (yppInter /. x -> Data[[i, 1]])}, {i, 1, n}];
Data3 = Table[{Data[[i, 1]], (ypppInter /. x -> Data[[i, 1]])}, {i, 1, n}];
Data4 = Table[{Data[[i, 1]], (yppppInter /. x -> Data[[i, 1]])}, {i, 1, n}];
aun = Plot[{y, yun[x]}, {x, -1, 1}, Epilog -> {PointSize[Large], Point[Dataa]}, AxesLabel -> {"x", "y"}, BaseStyle -> Bold, ImageSize -> Medium, PlotRange -> All, PlotLegends -> {"y actual", "y spline"}, PlotLabel -> "Third order splines"];
a0 = Plot[{y, y2[x]}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data]}, AxesLabel -> {"x", "y"},    BaseStyle -> Bold, ImageSize -> Medium, PlotRange -> All,    PlotLegends -> {"y actual", "y spline"},    PlotLabel -> "Third order splines Filtered"];
a1 = Plot[{yp, ypInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data1]},    AxesLabel -> {"x", "y'"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y'", "y'spline"},    PlotLabel -> "First derivative"];
a2 = Plot[{ypp, yppInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data2]},    AxesLabel -> {"x", "y''"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y''", "y''spline"},    PlotLabel -> "Second derivative"];
a3 = Plot[{yppp, ypppInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data3]},    AxesLabel -> {"x", "y'''"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y'''", "y'''spline"},    PlotLabel -> "Third derivative"];
a4 = Plot[{ypppp, yppppInter}, {x, -1, 1},    Epilog -> {PointSize[Large], Point[Data4]},    AxesLabel -> {"x", "y''''"}, BaseStyle -> Bold, ImageSize -> Medium,    PlotRange -> All, PlotLegends -> {"y''''", "y''''spline"},    PlotLabel -> "Fourth derivative"];
Grid[{{aun, SpanFromLeft}, {a0, a1}, {a2, a3}}]

Problems

  1. Compare the basic formulas and the high-accuracy formulas to calculate the first and second derivatives of e^{x} at x=2 with h=0.1. Clearly indicate the order of the error term in the approximation used. Calculate the relative error E_r in each case.
  2. A plane is being tracked by radar, and data are taken every two seconds in polar coordinates \theta and r.
    radar

    Time t (sec.) 200 202 204 206 208 210
    \theta (rad) 0.75 0.72 0.70 0.68 0.67 0.66
    r (m.) 5120 5370 5560 5800 6030 6240

    The vector expression in polar coordinates for the velocity v and the acceleration a are given by:

        \[\begin{split} v&=\frac{\mathrm{d}r}{\mathrm {d}t}e_r+r\frac{\mathrm{d}\theta}{\mathrm {d}t}e_{\theta} \\ a&=\left(\frac{\mathrm{d}^2r}{\mathrm {d}t^2}-r\left(\frac{\mathrm{d}\theta}{\mathrm {d}t}\right)^2\right)e_r+\left(r\frac{\mathrm{d}^2\theta}{\mathrm {d}t^2}+2\frac{\mathrm{d}r}{\mathrm {d}t}\frac{\mathrm{d}\theta}{\mathrm {d}t}\right)e_{\theta}  \end{split} \]

    Use the centred finite difference basic formulas to find the velocity and acceleration vectors at t=206 sec. as a function of the unit vectors e_r and e_{\theta}. Then, using the norm of the velocity and acceleration vectors describe the plane speed and the magnitude of its acceleration.

  3. Fick's first diffusion law states that:

        \[ \mbox{Mass Flux}=-D\frac{\mathrm{d}C}{\mathrm{d}x} \]

    where \mbox{Mass Flux} is the mass transported per unit area and per unit time with units g/m^2/s, D is a constant called the diffusion coefficient given in m^2/sec., C is the concentration given in g/m^3 and x is the distance in m. The law states that the mass transported per unit area and per unit time is directly proportional to the gradient of the concentration (with a negative constant of proportionality). Stated differently, it means that the molecules tend to move towards the area of less concentration. If the concentration of a pollutant in the pore waters of sediments underlying the lake is constant throughout the year and is measured to be:

    x (m) 0 1 3
    C, 10^{-6} (g/m^3) 0.06 0.32 0.6

    where x=0 is the interface between the lake and the sediment. By fitting a parabola to the data, estimate \frac{\mathrm{d}C}{\mathrm{d}x} at x=0. If the area of interface between the lake and the sediment is 3.6\times 10^6 m^2, and if D=1.52\times 10^{-6}m^2/sec, how much pollutant in g would be transported into the lake over a period of one year?

  4. The positions in m. of the left and right feet of two squash players (Ramy Ashour and Cameron Pilley) during an 8 second segment of their match in the North American Open in February 2013 are given in the excel sheet provided here. Using a centred finite difference scheme, compare the average and top speed and acceleration between the two players during that segment of the match. Similarly, using a parabolic spline interpolation function, compare the average and top speed and acceleration between the two players. Finally, compare with the average speed of walking for humans.

Page Comments

  1. Richard says:

    under 13.12.4 the fourth derivative for centered finite difference is missing the error term accuracy

  2. Gabriel Valdeleon says:

    Hello, I don’t know if it is a deliberate typo or not, but Section 13.12.5 is titled “Comparision”. Is this supposed to be “Comparison”?

    1. Samer Adeeb says:

      I corrected that and added a bonus mark for you!

Leave a Reply

Your email address will not be published. Required fields are marked *