Main.java 32,7 ko
Newer Older
package fr.univ.dblp;

import fr.univ.dblp.analysis.*;
import fr.univ.dblp.export.ResultsPrinter;
import fr.univ.dblp.generators.CopyGenerator;
import fr.univ.dblp.generators.NetworkGenerator;
import fr.univ.dblp.loader.GraphLoader;
import fr.univ.dblp.simulation.*;
import org.graphstream.graph.Graph;

import java.io.IOException;
import java.util.*;

/**
 * Point d'entrée principal pour l'analyse du réseau DBLP.
 *
 * Cette classe gère le menu interactif et orchestre l'exécution
 * des différentes analyses du réseau de collaboration scientifique DBLP.
 *
 * @author Hamadou BA
 * @see <a href="https://www-apps.univ-lehavre.fr/forge/bh243413/tp2-ri-mesures-de-reseaux-interaction.git">Dépôt Git</a>
 */
public class Main {

    private static final String DBLP_FILE_PATH = "src/main/resources/snap/com-dblp.ungraph.txt";
    private static Graph dblpGraph = null;
    private static Graph baGraph = null;

    public static void main(String[] args) {
        ResultsPrinter.printHeader("TP: ANALYSE DU RÉSEAU DBLP");
        ResultsPrinter.printInfo("Mesures de réseaux d'interaction - Collaboration scientifique");
        ResultsPrinter.printInfo("Données: DBLP (Digital Bibliography & Library Project)");

        System.out.println();

        // Chargement initial du graphe DBLP
        runQuestion1();

        if (dblpGraph == null) {
            ResultsPrinter.printError("Impossible de charger le graphe DBLP!");
            ResultsPrinter.printError("Vérifiez que le fichier existe: " + DBLP_FILE_PATH);
            return;
        }

        // Affichage du menu interactif
        Scanner scanner = new Scanner(System.in);
        boolean continueRunning = true;

        while (continueRunning) {
            displayMenu();
            System.out.print("\nVotre choix: ");

            String choice = scanner.nextLine().trim();

            switch (choice) {
                case "1":
                    runQuestion1();
                    break;
                case "2":
                    runQuestion2();
                    break;
                case "3":
                    runQuestion3();
                    break;
                case "4":
                    runQuestion4();
                    break;
                case "5":
                    runQuestion5();
                    break;
                case "6":
                    runQuestion6();
                    break;
                case "7":
                    runQuestion7();
                    break;
                case "8":
                    runAllQuestions();
                    break;
                case "9":
                    runPartie2Question1();
                    break;
                case "10":
                    runPartie2Question3();
                    break;
                case "11":
                    runPartie2Question5();
                    break;
                case "12":
                    runPartie2Question6();
                    break;
                case "13":
                    runAllPartie2();
                    break;
                case "0":
                    continueRunning = false;
                    ResultsPrinter.printInfo("Au revoir!");
                    break;
                default:
                    ResultsPrinter.printWarning("Choix invalide! Essayez encore.");
            }

            if (continueRunning && !choice.equals("8")) {
                System.out.println("\nAppuyez sur Entrée pour continuer...");
                scanner.nextLine();
            }
        }

        scanner.close();
    }

    /**
     * Affiche le menu interactif principal.
     *
     * Ce menu permet à l'utilisateur de choisir quelle analyse exécuter
     * parmi les 7 questions du TP ou d'exécuter toutes les analyses en séquence.
     */
    private static void displayMenu() {
        System.out.println("\n" + "=".repeat(80));
        System.out.println("MENU PRINCIPAL");
        System.out.println("=".repeat(80));
        System.out.println("PARTIE 1 - Analyse du réseau:");
        System.out.println("  1. Question 1  - Chargement des données");
        System.out.println("  2. Question 2  - Mesures de base");
        System.out.println("  3. Question 3  - Analyse de connexité");
        System.out.println("  4. Question 4  - Distribution des degrés");
        System.out.println("  5. Question 5  - Distance moyenne");
        System.out.println("  6. Question 6  - Comparaison avec générateurs");
        System.out.println("  7. Question 7  - Générateur par copie (BONUS)");
        System.out.println("  8. TOUT EXÉCUTER (Partie 1: Questions 1-7)");
        System.out.println("\nPARTIE 2 - Propagation virale:");
        System.out.println("  9. Seuils épidémiques (Questions 1-2)");
        System.out.println(" 10. Simulation 3 scénarios (Questions 3-4)");
        System.out.println(" 11. Analyse immunisation (Question 5)");
        System.out.println(" 12. Comparaison réseaux (Question 6)");
        System.out.println(" 13. TOUT EXÉCUTER (Partie 2: Questions 1-6)");
        System.out.println("\n  0. Quitter");
        System.out.println("=".repeat(80));
    }

    /**
     * Question 1: Charge les données du réseau DBLP.
     *
     * Cette méthode charge le graphe de collaboration DBLP depuis le fichier
     * de données et affiche les informations de base sur le graphe chargé.
     */
    private static void runQuestion1() {
        ResultsPrinter.printHeader("QUESTION 1: Chargement des données");

        dblpGraph = GraphLoader.loadDBLP(DBLP_FILE_PATH);

        if (dblpGraph != null) {
            ResultsPrinter.printSuccess("Graphe DBLP chargé avec succès!");
            GraphLoader.displayGraphInfo(dblpGraph);

            ResultsPrinter.printInfo("\nNote: La visualisation n'est pas recommandée pour ce graphe");
            ResultsPrinter.printInfo("      (trop de nœuds: visualisation très lente et peu informative)");
        }
    }

    /**
     * Question 2: Calcule et affiche les mesures de base du réseau.
     *
     * Cette méthode calcule le nombre de nœuds, d'arêtes, le degré moyen
     * et le coefficient de clustering du réseau DBLP.
     */
    private static void runQuestion2() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        BasicMetrics.analyze(dblpGraph, "DBLP");
    }

    /**
     * Question 3: Analyse la connexité du réseau.
     *
     * Cette méthode vérifie si le réseau est connexe, compte le nombre de
     * composantes connexes et détermine le degré critique pour la connexité.
     */
    private static void runQuestion3() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        ConnectivityAnalyzer.analyze(dblpGraph);
    }

    /**
     * Question 4: Analyse la distribution des degrés du réseau.
     *
     * Cette méthode calcule la distribution des degrés, l'exporte pour
     * visualisation avec gnuplot et vérifie si elle suit une loi de puissance.
     */
    private static void runQuestion4() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        DegreeAnalyzer.analyze(dblpGraph, "DBLP", "dblp");

        ResultsPrinter.printInfo("\nPour générer les graphiques avec gnuplot:");
        ResultsPrinter.printInfo("  cd gnuplot");
        ResultsPrinter.printInfo("  gnuplot plot_dd_linear.gnu");
        ResultsPrinter.printInfo("  gnuplot plot_dd_loglog.gnu");
        ResultsPrinter.printInfo("  gnuplot plot_dd_powerlaw.gnu");
    }

    /**
     * Question 5: Calcule la distance moyenne du réseau.
     *
     * Cette méthode effectue un échantillonnage de 1000 nœuds et calcule
     * la distance moyenne par parcours BFS. Attention: opération longue (15-25 min).
     */
    private static void runQuestion5() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        ResultsPrinter.printWarning("ATTENTION: Cette analyse peut prendre 15-25 minutes!");
        ResultsPrinter.printInfo("(1000 parcours BFS sur un graphe de 317k nœuds)");

        System.out.print("\nContinuer? (o/n): ");
        Scanner scanner = new Scanner(System.in);
        String response = scanner.nextLine().trim().toLowerCase();

        if (response.equals("o") || response.equals("oui") || response.equals("y") || response.equals("yes")) {
            DistanceAnalyzer.analyze(dblpGraph, "DBLP", "dblp");

            ResultsPrinter.printInfo("\nPour générer le graphique avec gnuplot:");
            ResultsPrinter.printInfo("  cd gnuplot && gnuplot plot_distances.gnu");
        } else {
            ResultsPrinter.printInfo("Analyse annulée.");
        }
    }

    /**
     * Question 6: Compare le réseau DBLP avec des générateurs de réseaux.
     *
     * Cette méthode génère un réseau aléatoire (Erdős-Rényi) et un réseau
     * Barabási-Albert, puis compare leurs propriétés avec DBLP. Durée: 30-40 min.
     */
    private static void runQuestion6() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        ResultsPrinter.printWarning("ATTENTION: Cette analyse peut prendre 30-40 minutes!");
        ResultsPrinter.printInfo("(Génération et analyse de 2 grands réseaux)");

        System.out.print("\nContinuer? (o/n): ");
        Scanner scanner = new Scanner(System.in);
        String response = scanner.nextLine().trim().toLowerCase();

        if (response.equals("o") || response.equals("oui") || response.equals("y") || response.equals("yes")) {
            NetworkGenerator.analyze(dblpGraph);

            ResultsPrinter.printInfo("\nPour générer le graphique comparatif:");
            ResultsPrinter.printInfo("  cd gnuplot && gnuplot plot_comparison_networks.gnu");
        } else {
            ResultsPrinter.printInfo("Analyse annulée.");
        }
    }

    /**
     * Question 7 (BONUS): Teste le générateur par copie.
     *
     * Cette méthode génère un réseau avec le mécanisme de copie et compare
     * son clustering avec DBLP et Barabási-Albert. Durée: 15-20 min.
     */
    private static void runQuestion7() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        // Génération du graphe BA si nécessaire pour la comparaison
        if (baGraph == null) {
            ResultsPrinter.printInfo("Génération du graphe Barabási-Albert pour comparaison...");
            int n = Math.min(dblpGraph.getNodeCount(), 50000);
            double avgDegree = BasicMetrics.getAverageDegree(dblpGraph);
            int edgesPerNode = (int) Math.round(avgDegree / 2.0);
            baGraph = NetworkGenerator.generateBarabasiAlbert(n, Math.max(1, edgesPerNode));
        }

        ResultsPrinter.printWarning("ATTENTION: Cette analyse peut prendre 15-20 minutes!");

        System.out.print("\nContinuer? (o/n): ");
        Scanner scanner = new Scanner(System.in);
        String response = scanner.nextLine().trim().toLowerCase();

        if (response.equals("o") || response.equals("oui") || response.equals("y") || response.equals("yes")) {
            CopyGenerator.analyze(dblpGraph, baGraph);
        } else {
            ResultsPrinter.printInfo("Analyse annulée.");
        }
    }

    /**
     * Exécute toutes les questions en séquence.
     *
     * Cette méthode lance l'exécution automatique de toutes les analyses
     * (Questions 1 à 7) sans interruption. Durée totale: environ 1h30.
     */
    private static void runAllQuestions() {
        ResultsPrinter.printHeader("EXÉCUTION COMPLÈTE - TOUTES LES QUESTIONS");
        ResultsPrinter.printWarning("ATTENTION: L'exécution complète peut prendre 1h30 à 2h!");
        ResultsPrinter.printInfo("Les questions longues (Q5, Q6, Q7) seront exécutées automatiquement.");

        System.out.print("\nVoulez-vous vraiment tout exécuter? (o/n): ");
        Scanner scanner = new Scanner(System.in);
        String response = scanner.nextLine().trim().toLowerCase();

        if (!response.equals("o") && !response.equals("oui") && !response.equals("y") && !response.equals("yes")) {
            ResultsPrinter.printInfo("Exécution annulée.");
            return;
        }

        long globalStart = System.currentTimeMillis();

        // Question 1: Chargement
        if (dblpGraph == null) {
            runQuestion1();
        }

        // Question 2: Mesures de base
        if (dblpGraph != null) {
            runQuestion2();
        }

        // Question 3: Connexité
        if (dblpGraph != null) {
            runQuestion3();
        }

        // Question 4: Distribution des degrés
        if (dblpGraph != null) {
            runQuestion4();
        }

        // Question 5: Distance moyenne
        if (dblpGraph != null) {
            ResultsPrinter.printInfo("\n>>> Démarrage Question 5 (Distance moyenne)...");
            DistanceAnalyzer.analyze(dblpGraph, "DBLP", "dblp");
        }

        // Question 6: Comparaison avec générateurs
        if (dblpGraph != null) {
            ResultsPrinter.printInfo("\n>>> Démarrage Question 6 (Générateurs)...");
            NetworkGenerator.analyze(dblpGraph);
        }

        // Question 7: Générateur par copie (BONUS)
        if (dblpGraph != null) {
            ResultsPrinter.printInfo("\n>>> Démarrage Question 7 (Générateur copie - BONUS)...");
            // Génération du graphe BA si nécessaire
            if (baGraph == null) {
                int n = Math.min(dblpGraph.getNodeCount(), 50000);
                double avgDegree = BasicMetrics.getAverageDegree(dblpGraph);
                int edgesPerNode = (int) Math.round(avgDegree / 2.0);
                baGraph = NetworkGenerator.generateBarabasiAlbert(n, Math.max(1, edgesPerNode));
            }
            CopyGenerator.analyze(dblpGraph, baGraph);
        }

        ResultsPrinter.printHeader("TOUTES LES ANALYSES TERMINÉES!");
        ResultsPrinter.printElapsedTime(globalStart);

        ResultsPrinter.printInfo("\nPROCHAINES ÉTAPES:");
        ResultsPrinter.printInfo("  1. Générez les graphiques avec gnuplot (voir gnuplot/*.gnu)");
        ResultsPrinter.printInfo("  2. Consultez les fichiers de données dans output/data/");
        ResultsPrinter.printInfo("  3. Rédigez le rapport README.md avec les résultats et graphiques");
    }

    // ============================================================================
    // PARTIE 2 - PROPAGATION VIRALE
    // ============================================================================

    /**
     * Partie 2 - Questions 1-2: Calcul des seuils épidémiques
     */
    private static void runPartie2Question1() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        ResultsPrinter.printHeader("PARTIE 2 - QUESTIONS 1-2: Seuils épidémiques");

        long startTime = System.currentTimeMillis();

        // Paramètres du modèle
        double beta = ViralSimulator.DEFAULT_TRANSMISSION_RATE;
        double gamma = ViralSimulator.DEFAULT_RECOVERY_RATE;
        double r0 = EpidemicAnalyzer.calculateR0(beta, gamma);

        ResultsPrinter.printInfo("PARAMÈTRES DU MODÈLE:");
        ResultsPrinter.printMetric("β (transmission)", String.format("%.4f par jour (1 mail/semaine)", beta));
        ResultsPrinter.printMetric("γ (récupération)", String.format("%.4f par jour (2 màj/mois)", gamma));
        ResultsPrinter.printMetric("R0 (taux reproduction)", String.format("%.4f", r0));

        // Seuil épidémique du réseau DBLP
        double threshold = EpidemicAnalyzer.calculateEpidemicThreshold(dblpGraph);
        ResultsPrinter.printInfo("\nSEUIL ÉPIDÉMIQUE DU RÉSEAU DBLP:");
        ResultsPrinter.printMetric("τ (seuil épidémique)", String.format("%.4f", threshold));

        boolean epidemic = EpidemicAnalyzer.isEpidemicPossible(r0, threshold);
        ResultsPrinter.printMetric("Épidémie possible?", epidemic ? "OUI (R0 > τ)" : "NON (R0 ≤ τ)");

        // Seuil théorique pour un réseau aléatoire
        double avgDegree = BasicMetrics.getAverageDegree(dblpGraph);
        double theoreticalThreshold = EpidemicAnalyzer.theoreticalRandomThreshold(avgDegree);

        ResultsPrinter.printInfo("\nCOMPARAISON AVEC RÉSEAU ALÉATOIRE:");
        ResultsPrinter.printMetric("Degré moyen <k>", String.format("%.2f", avgDegree));
        ResultsPrinter.printMetric("τ théorique (aléatoire)", String.format("%.4f", theoreticalThreshold));
        ResultsPrinter.printMetric("τ réel (DBLP)", String.format("%.4f", threshold));
        ResultsPrinter.printMetric("Ratio (DBLP/aléatoire)", String.format("%.2fx", threshold / theoreticalThreshold));

        // Variance des degrés
        double variance = EpidemicAnalyzer.getDegreeVariance(dblpGraph);
        ResultsPrinter.printInfo("\nHÉTÉROGÉNÉITÉ DU RÉSEAU:");
        ResultsPrinter.printMetric("Variance des degrés", String.format("%.2f", variance));
        ResultsPrinter.printMetric("Écart-type", String.format("%.2f", Math.sqrt(variance)));

        ResultsPrinter.printInfo("\nINTERPRÉTATION:");
        if (threshold > theoreticalThreshold) {
            ResultsPrinter.printInfo("  Le seuil épidémique est PLUS ÉLEVÉ que celui d'un réseau aléatoire");
            ResultsPrinter.printInfo("  → Structure hétérogène avec hubs (loi de puissance)");
            ResultsPrinter.printInfo("  → Plus difficile de déclencher une épidémie");
        } else {
            ResultsPrinter.printInfo("  Le seuil épidémique est PLUS BAS que celui d'un réseau aléatoire");
        }

        if (epidemic) {
            ResultsPrinter.printWarning("\n⚠ ATTENTION: Avec R0=" + String.format("%.2f", r0) +
                " > τ=" + String.format("%.2f", threshold) + ", l'épidémie VA SE PROPAGER!");
        }

        ResultsPrinter.printElapsedTime(startTime);
    }

    /**
     * Partie 2 - Questions 3-4: Simulation des 3 scénarios
     */
    private static void runPartie2Question3() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        ResultsPrinter.printHeader("PARTIE 2 - QUESTIONS 3-4: Simulation 3 scénarios");
        ResultsPrinter.printWarning("Cette simulation peut prendre 5-10 minutes");
        ResultsPrinter.printInfo("(10 runs × 3 scénarios × 90 jours sur graphe de 317k nœuds)");

        System.out.print("\nContinuer? (o/n): ");
        Scanner scanner = new Scanner(System.in);
        String response = scanner.nextLine().trim().toLowerCase();

        if (!response.equals("o") && !response.equals("oui") && !response.equals("y") && !response.equals("yes")) {
            ResultsPrinter.printInfo("Simulation annulée.");
            return;
        }

        long startTime = System.currentTimeMillis();

        int numRuns = 10;
        int simulationDays = 90;
        List<SimulationResult> results = new ArrayList<>();

        // Scénario 0: Aucune intervention
        ResultsPrinter.printInfo("\n>>> Scénario 0: Aucune intervention");
        Set<String> noImmune = ImmunizationStrategy.noImmunization(dblpGraph);
        ViralSimulator sim0 = new ViralSimulator(dblpGraph, noImmune);
        SimulationResult result0 = sim0.simulateMultipleRuns(numRuns, simulationDays, "Scenario_0_Aucune_intervention");
        results.add(result0);
        ResultsPrinter.printMetric("Pic d'infection", result0.getPeakInfection() + " nœuds (jour " + result0.getPeakDay() + ")");

        // Scénario 1: 50% immunisation aléatoire
        ResultsPrinter.printInfo("\n>>> Scénario 1: 50% immunisation aléatoire");
        Set<String> randomImmune = ImmunizationStrategy.randomImmunization(dblpGraph, 0.5);
        ViralSimulator sim1 = new ViralSimulator(dblpGraph, randomImmune);
        SimulationResult result1 = sim1.simulateMultipleRuns(numRuns, simulationDays, "Scenario_1_Immunisation_aleatoire");
        results.add(result1);
        ResultsPrinter.printMetric("Nœuds immunisés", randomImmune.size());
        ResultsPrinter.printMetric("Pic d'infection", result1.getPeakInfection() + " nœuds (jour " + result1.getPeakDay() + ")");

        // Scénario 2: 50% immunisation sélective (acquaintance)
        ResultsPrinter.printInfo("\n>>> Scénario 2: 50% immunisation sélective (acquaintance)");
        Set<String> acquaintanceImmune = ImmunizationStrategy.acquaintanceImmunization(dblpGraph, 0.5);
        ViralSimulator sim2 = new ViralSimulator(dblpGraph, acquaintanceImmune);
        SimulationResult result2 = sim2.simulateMultipleRuns(numRuns, simulationDays, "Scenario_2_Immunisation_selective");
        results.add(result2);
        ResultsPrinter.printMetric("Nœuds immunisés", acquaintanceImmune.size());
        ResultsPrinter.printMetric("Pic d'infection", result2.getPeakInfection() + " nœuds (jour " + result2.getPeakDay() + ")");

        // Export des résultats
        ResultsPrinter.printInfo("\n>>> Export des données");
        try {
            for (SimulationResult result : results) {
                String filename = "output/data/partie2_" + result.getScenario() + ".dat";
                SimulationExporter.exportSimulationResult(result, filename);
                ResultsPrinter.printSuccess("Exporté: " + filename);
            }

            SimulationExporter.exportScenarioComparison(results, "output/data/partie2_scenarios_comparison.dat");
            ResultsPrinter.printSuccess("Exporté: output/data/partie2_scenarios_comparison.dat");

            SimulationExporter.exportSummary(results, "output/data/partie2_scenarios_summary.dat");
            ResultsPrinter.printSuccess("Exporté: output/data/partie2_scenarios_summary.dat");

        } catch (IOException e) {
            ResultsPrinter.printError("Erreur lors de l'export: " + e.getMessage());
        }

        // Comparaison des résultats
        ResultsPrinter.printInfo("\nCOMPARAISON DES SCÉNARIOS:");
        ResultsPrinter.printInfo(String.format("%-30s %15s %15s", "Scénario", "Pic infection", "Jour pic"));
        ResultsPrinter.printInfo("-".repeat(60));
        for (SimulationResult result : results) {
            ResultsPrinter.printInfo(String.format("%-30s %15d %15d",
                result.getScenario(), result.getPeakInfection(), result.getPeakDay()));
        }

        ResultsPrinter.printElapsedTime(startTime);
        ResultsPrinter.printInfo("\nPour visualiser les résultats:");
        ResultsPrinter.printInfo("  cd gnuplot && gnuplot plot_epidemic_scenarios.gnu");
    }

    /**
     * Partie 2 - Question 5: Analyse de l'immunisation
     */
    private static void runPartie2Question5() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        ResultsPrinter.printHeader("PARTIE 2 - QUESTION 5: Analyse immunisation");

        long startTime = System.currentTimeMillis();

        // Immunisation aléatoire 50%
        Set<String> randomImmune = ImmunizationStrategy.randomImmunization(dblpGraph, 0.5);
        Set<String> randomNonImmune = new HashSet<>();
        dblpGraph.nodes().forEach(node -> {
            if (!randomImmune.contains(node.getId())) {
                randomNonImmune.add(node.getId());
            }
        });

        // Immunisation sélective 50%
        Set<String> acquaintanceImmune = ImmunizationStrategy.acquaintanceImmunization(dblpGraph, 0.5);
        Set<String> acquaintanceNonImmune = new HashSet<>();
        dblpGraph.nodes().forEach(node -> {
            if (!acquaintanceImmune.contains(node.getId())) {
                acquaintanceNonImmune.add(node.getId());
            }
        });

        // Calcul des degrés moyens
        double randomNonImmuneDegree = ImmunizationStrategy.getAverageDegree(dblpGraph, randomNonImmune);
        double acquaintanceNonImmuneDegree = ImmunizationStrategy.getAverageDegree(dblpGraph, acquaintanceNonImmune);
        double randomImmuneDegree = ImmunizationStrategy.getAverageDegree(dblpGraph, randomImmune);
        double acquaintanceImmuneDegree = ImmunizationStrategy.getAverageDegree(dblpGraph, acquaintanceImmune);

        ResultsPrinter.printInfo("DEGRÉ MOYEN DES GROUPES:");
        ResultsPrinter.printInfo("\nImmunisation aléatoire:");
        ResultsPrinter.printMetric("Degré moyen immunisés", String.format("%.2f", randomImmuneDegree));
        ResultsPrinter.printMetric("Degré moyen non-immunisés", String.format("%.2f", randomNonImmuneDegree));

        ResultsPrinter.printInfo("\nImmunisation sélective (acquaintance):");
        ResultsPrinter.printMetric("Degré moyen immunisés", String.format("%.2f", acquaintanceImmuneDegree));
        ResultsPrinter.printMetric("Degré moyen non-immunisés", String.format("%.2f", acquaintanceNonImmuneDegree));

        ResultsPrinter.printInfo("\nCOMPARAISON:");
        ResultsPrinter.printMetric("Ratio (selective/random) immunisés",
            String.format("%.2fx", acquaintanceImmuneDegree / randomImmuneDegree));
        ResultsPrinter.printMetric("Ratio (selective/random) non-immunisés",
            String.format("%.2fx", acquaintanceNonImmuneDegree / randomNonImmuneDegree));

        // Seuils épidémiques après immunisation
        double thresholdOriginal = EpidemicAnalyzer.calculateEpidemicThreshold(dblpGraph);
        double thresholdRandom = EpidemicAnalyzer.calculateThresholdAfterImmunization(dblpGraph, randomImmune);
        double thresholdAcquaintance = EpidemicAnalyzer.calculateThresholdAfterImmunization(dblpGraph, acquaintanceImmune);

        ResultsPrinter.printInfo("\nSEUILS ÉPIDÉMIQUES:");
        ResultsPrinter.printMetric("τ original", String.format("%.4f", thresholdOriginal));
        ResultsPrinter.printMetric("τ après immunisation aléatoire", String.format("%.4f", thresholdRandom));
        ResultsPrinter.printMetric("τ après immunisation sélective", String.format("%.4f", thresholdAcquaintance));

        ResultsPrinter.printInfo("\nINTERPRÉTATION:");
        ResultsPrinter.printInfo("  L'immunisation sélective cible préférentiellement les HUBS");
        ResultsPrinter.printInfo("  → Les nœuds immunisés ont un degré PLUS ÉLEVÉ");
        ResultsPrinter.printInfo("  → Les nœuds non-immunisés ont un degré PLUS BAS");
        ResultsPrinter.printInfo("  → Le seuil épidémique AUGMENTE davantage");
        ResultsPrinter.printInfo("  → Stratégie PLUS EFFICACE pour bloquer l'épidémie");

        ResultsPrinter.printElapsedTime(startTime);
    }

    /**
     * Partie 2 - Question 6: Comparaison avec différents réseaux
     */
    private static void runPartie2Question6() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        ResultsPrinter.printHeader("PARTIE 2 - QUESTION 6: Comparaison réseaux");
        ResultsPrinter.printWarning("Cette simulation peut prendre 15-20 minutes");
        ResultsPrinter.printInfo("(Génération réseaux + simulations × 3 réseaux × 3 scénarios)");

        System.out.print("\nContinuer? (o/n): ");
        Scanner scanner = new Scanner(System.in);
        String response = scanner.nextLine().trim().toLowerCase();

        if (!response.equals("o") && !response.equals("oui") && !response.equals("y") && !response.equals("yes")) {
            ResultsPrinter.printInfo("Simulation annulée.");
            return;
        }

        long startTime = System.currentTimeMillis();

        // Paramètres pour les réseaux générés
        int n = Math.min(50000, dblpGraph.getNodeCount()); // Limiter pour performance
        double avgDegree = BasicMetrics.getAverageDegree(dblpGraph);
        int edgesPerNode = Math.max(1, (int) Math.round(avgDegree / 2.0));

        // Génération réseau aléatoire
        ResultsPrinter.printInfo("\n>>> Génération réseau aléatoire");
        Graph randomGraph = NetworkGenerator.generateRandomGraph(n, avgDegree);
        ResultsPrinter.printSuccess("Réseau aléatoire généré: " + n + " nœuds, degré moyen " +
            String.format("%.2f", BasicMetrics.getAverageDegree(randomGraph)));

        // Génération réseau Barabási-Albert
        ResultsPrinter.printInfo("\n>>> Génération réseau Barabási-Albert");
        Graph baGraph = NetworkGenerator.generateBarabasiAlbert(n, edgesPerNode);
        ResultsPrinter.printSuccess("Réseau BA généré: " + n + " nœuds, degré moyen " +
            String.format("%.2f", BasicMetrics.getAverageDegree(baGraph)));

        // Simulations sur chaque réseau (scénario 0 uniquement pour comparaison)
        int numRuns = 5;
        int simulationDays = 90;

        ResultsPrinter.printInfo("\n>>> Simulation sur réseau DBLP (échantillon)");
        // Utiliser un sous-graphe de DBLP pour comparaison équitable
        Graph dblpSample = dblpGraph; // TODO: échantillonner si nécessaire
        ViralSimulator simDBLP = new ViralSimulator(dblpSample);
        SimulationResult resultDBLP = simDBLP.simulateMultipleRuns(numRuns, simulationDays, "DBLP");

        ResultsPrinter.printInfo("\n>>> Simulation sur réseau aléatoire");
        ViralSimulator simRandom = new ViralSimulator(randomGraph);
        SimulationResult resultRandom = simRandom.simulateMultipleRuns(numRuns, simulationDays, "Random");

        ResultsPrinter.printInfo("\n>>> Simulation sur réseau Barabási-Albert");
        ViralSimulator simBA = new ViralSimulator(baGraph);
        SimulationResult resultBA = simBA.simulateMultipleRuns(numRuns, simulationDays, "Barabasi_Albert");

        // Export
        ResultsPrinter.printInfo("\n>>> Export des données");
        try {
            List<String> networkNames = Arrays.asList("DBLP", "Random", "Barabasi_Albert");
            List<SimulationResult> results = Arrays.asList(resultDBLP, resultRandom, resultBA);

            SimulationExporter.exportNetworkComparison(networkNames, results,
                "output/data/partie2_networks_comparison.dat");
            ResultsPrinter.printSuccess("Exporté: output/data/partie2_networks_comparison.dat");
        } catch (IOException e) {
            ResultsPrinter.printError("Erreur lors de l'export: " + e.getMessage());
        }

        // Comparaison
        ResultsPrinter.printInfo("\nCOMPARAISON DES RÉSEAUX:");
        ResultsPrinter.printInfo(String.format("%-20s %15s %15s %15s",
            "Réseau", "Clustering", "Pic infection", "Jour pic"));
        ResultsPrinter.printInfo("-".repeat(70));

        ResultsPrinter.printInfo(String.format("%-20s %15.4f %15d %15d",
            "DBLP", BasicMetrics.getClusteringCoefficient(dblpSample),
            resultDBLP.getPeakInfection(), resultDBLP.getPeakDay()));
        ResultsPrinter.printInfo(String.format("%-20s %15.4f %15d %15d",
            "Aléatoire", BasicMetrics.getClusteringCoefficient(randomGraph),
            resultRandom.getPeakInfection(), resultRandom.getPeakDay()));
        ResultsPrinter.printInfo(String.format("%-20s %15.4f %15d %15d",
            "Barabási-Albert", BasicMetrics.getClusteringCoefficient(baGraph),
            resultBA.getPeakInfection(), resultBA.getPeakDay()));

        ResultsPrinter.printElapsedTime(startTime);
        ResultsPrinter.printInfo("\nPour visualiser les résultats:");
        ResultsPrinter.printInfo("  cd gnuplot && gnuplot plot_epidemic_networks.gnu");
    }

    /**
     * Exécute toute la partie 2 en séquence
     */
    private static void runAllPartie2() {
        if (dblpGraph == null) {
            ResultsPrinter.printError("Chargez d'abord le graphe (Question 1)!");
            return;
        }

        ResultsPrinter.printHeader("EXÉCUTION COMPLÈTE - PARTIE 2");
        ResultsPrinter.printWarning("ATTENTION: L'exécution complète peut prendre 30-45 minutes!");

        System.out.print("\nVoulez-vous vraiment tout exécuter? (o/n): ");
        Scanner scanner = new Scanner(System.in);
        String response = scanner.nextLine().trim().toLowerCase();

        if (!response.equals("o") && !response.equals("oui") && !response.equals("y") && !response.equals("yes")) {
            ResultsPrinter.printInfo("Exécution annulée.");
            return;
        }

        long globalStart = System.currentTimeMillis();

        runPartie2Question1();
        System.out.println("\nAppuyez sur Entrée pour continuer...");
        scanner.nextLine();

        runPartie2Question3();
        System.out.println("\nAppuyez sur Entrée pour continuer...");
        scanner.nextLine();

        runPartie2Question5();
        System.out.println("\nAppuyez sur Entrée pour continuer...");
        scanner.nextLine();

        runPartie2Question6();

        ResultsPrinter.printHeader("PARTIE 2 TERMINÉE!");
        ResultsPrinter.printElapsedTime(globalStart);
    }