/** Intégrateur Runge Kutta d'ordre 4. Implémente les méthodes de
 * l'interface Integrateur pour pouvoir facilement être substitué à un
 * autre Integrateur. */
public class RungeKutta4 implements Integrateur {
    private final Integrable systeme;
    private final double h;
    private final double[] etat;
    private final double [] etatTest, k1, k2, k3, k4;
    private final int nVar;

    /** L'ntegrateur doit être initialisé en fournissant le système
     * à intégrer, et le pas de temps désiré. L'intégration se fera
     * par la suite toujours sur ce système et avec ce pas de
     * temps. */
    public RungeKutta4(Integrable systeme, double pasDeTemps) {
        this.systeme = systeme;
        h = pasDeTemps;
        etat = systeme.getState();
        // combien de variables d'état ?
        nVar = etat.length;
        // allouer des tableaux pour les calculs intermédiaires
        etatTest = new double[nVar];
        k1 = new double[nVar];
        k2 = new double[nVar];
        k3 = new double[nVar];
        k4 = new double[nVar];
    }

    public void integre() {// on nous demande d'intégrer d'un pas de temps
        int i;
        // k1 = f(etat)
        systeme.calcDeriv(etat, k1);

        // k2 = f(etat+0.5*dt*k1)
        for (i=0; i<nVar; i++)
            etatTest[i] = etat[i] + 0.5*h*k1[i];
        systeme.calcDeriv(etatTest, k2);

        // k3 = f(etat+0.5*dt*k2)
        for (i=0; i<nVar; i++)
            etatTest[i] = etat[i] + 0.5*h*k2[i];
        systeme.calcDeriv(etatTest, k3);

        // k4 = f(etat+dt*k3)
        for (i=0; i<nVar; i++)
            etatTest[i] = etat[i] + h*k3[i];
        systeme.calcDeriv(etatTest, k4);

        // nouvel etat = ancien etat + dt/6 * (k1 + 2*k2 + 2*k3 + k4)
        for (i=0; i<nVar; i++)
            etat[i] = etat[i] + h/6*(k1[i] + 2*k2[i] + 2*k3[i] + k4[i]);
    }
}