def runge_kutta_o4(f, a, b, N, y_0):

    """To approximate the solution of an initial-value problem

    Approximates the solution of the initial-value problem
            y' = f(t, y), a ≤ t ≤ b, y(a) = α,
    at (N + 1) equally spaced numbers in the interval [a, b].

    Args:
        f: A function to be evaluated (can be a lambda or def).
        a: The lower bound in the given range
        b: The upper bound in the given range
        N: The number of iterations to be perfomed
        y_0: The initial condition

    Returns: Has two results. The first one is an approximation w to y at the (N + 1) values of t.
        The second one is a lists of lists for every solution after N + 1 iterations.
        For example:
        This would be the result after 3 iterations
            [[0, 0.5], [0.2, 0.8292933333333334], [0.4, 1.2140762106666667]]
        if the function f was
            f = lambda t, y: y - t ** 2 + 1
        with a = 0, b = 2, N = 10, y_0 = 0.5
    Raises:
        None
    """

    # To hold the results
    p = a
    result = []

    h = (b - a) / N
    t = a
    w = y_0
    result.append([t, w])
    for i in range(1, N + 1):
        K1 = h * f(t, w)
        K2 = h * f(t + h / 2, w + K1 / 2)
        K3 = h * f(t + h / 2, w + K2 / 2)
        K4 = h * f(t + h, w + K3)
        w = w + (K1 + 2 * K2 + 2 * K3 + K4) / 6  # (Compute wi.)
        t = a + i * h  # (Compute ti.)
        p = w
        result.append([t, w])

    return p, result