import numpy as np


def table6_4():
    # Integrate f(x) over [a, b] using hermite
    #
    # Inputs:
    #       a = left endpoint of interval
    #       b = right endpoint of interval
    #       f = function
    #      df = derivative of function

    a = 0
    b = 1
    exact = (np.exp(3) - 1) / 3

    # interv = number of subinternals
    interv = np.array([10, 20, 40, 80, 160])
    nk = len(interv)
    H = np.zeros(nk)
    errH = np.zeros(nk)
    for k in range(nk):
        n = int(interv[k])
        H[k] = herm(a, b, n)
        errH[k] = abs(exact - H[k])

    print("\n n  \t\t I_H \t \t Error")
    for k in range(nk):
        print(f" {interv[k]:d} \t {H[k]:19.15f} \t {errH[k]:5.1e}")
        # print('\\, %i  \\, & ...')
    print()


def f(x):
    y = np.exp(3 * x)
    return y


def df(x):
    y = 3 * np.exp(3 * x)
    return y


### Hermite: n = number of subintervals
def herm(a, b, n):
    xd = np.linspace(a, b, n + 1)
    h = xd[1] - xd[0]
    s = 0.5 * f(xd[0])
    for j in range(1, n):
        s = s + f(xd[j])
    df0 = df(a)
    df1 = df(b)
    s = h * (s + 0.5 * f(xd[n])) + h ** 2 * (df0 - df1) / 12
    return s


if __name__ == "__main__":
    table6_4()
