import numpy as np
import matplotlib.pyplot as plt

def f(x):
    return 1 / (5 + x**2)

def divided_differences(x, y):
    n = len(y) - 1
    matrix = np.zeros((n+1, n+1))
    coef = np.copy(y)
    for i in range(0, n + 1):
        matrix[i, 0] = coef[i]
    for j in range(1, n + 1):
        for i in range(j, n + 1):
            matrix[i, j] = (matrix[i, j - 1] - matrix[i - 1, j - 1]) / (x[i] - x[i - j])
    for i in range(0, n + 1):
        coef[i] =  matrix[i, i]
    return coef

def newton_interpolation(x, coef, xp):
    n = len(coef) - 1
    p = coef[n]
    for k in range(n - 1, -1, -1):
        p = coef[k] + (xp - x[k]) * p
    return p

def newton_interpolation_function(n, xp):
    x_k = np.linspace(-1, 1, n + 1)
    y_k = f(x_k)
    coef = divided_differences(x_k, y_k)
    t = newton_interpolation(x_k, coef, xp)
    return t

n_values = [1, 5, 10, 15, 20, 25]
x_plot = np.linspace(-1, 1, 500)
y_true = f(x_plot)

plt.figure(figsize=(10, 6))
plt.plot(x_plot, y_true, label="f(x) = 1 / (5 + x^2)", color='black')

for n in n_values:
    y_interp = newton_interpolation_function(n, x_plot)
    plt.plot(x_plot, y_interp, label=f'Newton Interpolation n={n}')

plt.legend()
plt.title("Comparison of Newton Interpolations for different n")
plt.xlabel("x")
plt.ylabel("f(x)")
plt.grid(True)
plt.show()