# %% Imports & Constants
import numpy as np
import matplotlib.pyplot as plt


## Integration par la méthode des rectangles - Version 3 (vectorisée avec NumPy)
def integration_rectangle_vecteur(f, a, b, N):
    """
    Intègre numériquement une fonction f sur l'intervalle [a, b] en utilisant la méthode des rectangles.

    Paramètres:
    f : fonction
        La fonction à intégrer.
    a : float
        La borne inférieure de l'intervalle d'intégration.
    b : float
        La borne supérieure de l'intervalle d'intégration.
    N : int
        Le nombre de sous-intervalles (rectangles).

    Retourne:
    float
        La valeur approchée de l'intégrale de f de a à b.
    """
    # Largeur de chaque sous-intervalle
    h = (b - a) / N

    # Points d'évaluation (gauche des rectangles)
    x = np.linspace(a, b - h, N)

    # Somme des aires des rectangles
    integral = np.sum(f(x) * h)

    return integral


## Integration par la méthode des trapèzes - Version 3 (vectorisée avec NumPy))
def integration_trapeze_vecteur(f, a, b, N):
    """
    Intègre numériquement une fonction f sur l'intervalle [a, b] en utilisant la méthode des trapèzes
    avec l'aide de Numpy pour vectoriser les opérations.

    Paramètres:
    f : fonction
        La fonction à intégrer.
    a : float
        La borne inférieure de l'intervalle d'intégration.
    b : float
        La borne supérieure de l'intervalle d'intégration.
    N : int
        Le nombre de sous-intervalles (trapèzes).

    Retourne:
    float
        La valeur approchée de l'intégrale de f de a à b.
    """
    # Largeur de chaque sous-intervalle
    h = (b - a) / N

    # Points d'évaluation (gauche des rectangles)
    x = np.linspace(a, b - h, N)

    # Calcul de l'intégrale en utilisant la méthode des trapèzes
    integral = np.sum(0.5 * (f(x) + f(x + h)) * h)

    return integral


# %% Comparaison de la méthode des trapèzes et des rectangles

if __name__ == "__main__":

    f = lambda x: np.log(x / 4) / x + x**2
    F = lambda x: np.log(x / 4) ** 2 / 2 + x**3 / 3
    a = 1e-1
    b = 5
    nPoints = [10, 50, 100, 1000, 5000]

    I = F(b) - F(a)
    print(f"Valeur exacte de l'intégrale : I = {I:.6f}")

    Erreur_Absolue_Methode_Trapeze, Erreur_Absolue_Methode_Rectangle = [], []
    for N in nPoints:
        # à compléter : Calculer l'erreur absolue pour chaque méthode
        # et stocker les résultats dans les listes définies ci-dessus.
        # Vous pouvez utiliser la fonction np.abs(u)
        # pour calculer la valeur absolue de u


    plt.figure()
    plt.loglog(nPoints, Erreur_Absolue_Methode_Trapeze, "o-", label="Méthode des trapèzes")
    plt.loglog(nPoints, Erreur_Absolue_Methode_Rectangle, "s-", label="Méthode des rectangles")
    plt.xlabel(r"Nombre de points d'integration : $N$", fontsize=13)
    plt.ylabel(r"Erreur absolue : $\left\Vert I - I_{approx}\right\Vert$", fontsize=13)
    plt.grid()
    plt.legend(fontsize=12)
    plt.title("Comparaison des méthodes des trapèzes et des rectangles", fontsize=13)
# %%
