/*
Copyright 2012-2014 Samuel Gesche

This file is part of the Greek Reuse Toolkit.

The Greek Reuse Toolkit is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

The Greek Reuse Toolkit is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with the Greek Reuse Toolkit.  If not, see <http://www.gnu.org/licenses/>.
*/

package fr.cnrs.liris.drim.grt.proc.recherche;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.text.JTextComponent;
import fr.cnrs.liris.drim.grt.modele.Citation;
import fr.cnrs.liris.drim.grt.modele.OrdreStrict;
import fr.cnrs.liris.drim.grt.modele.Passage;
import fr.cnrs.liris.drim.grt.modele.Terme;
import fr.cnrs.liris.drim.grt.modele.exceptions.ListeStatistiqueNonInitialiseeException;
import fr.cnrs.liris.drim.grt.modele.lemmatiseurs.BibleWorks;
import fr.cnrs.liris.drim.grt.modele.lemmatiseurs.Perseus;
import fr.cnrs.liris.drim.grt.modele.lemmatiseurs.SourcesChretiennes;
import fr.cnrs.liris.drim.grt.modele.listes.Lemmatiseurs;
import fr.cnrs.liris.drim.grt.modele.listes.OrdreAlphabetiqueGrec;
import fr.cnrs.liris.drim.grt.proc.ComparateurCitations;
import fr.cnrs.liris.drim.grt.proc.Texte;
import fr.cnrs.liris.drim.grt.proc.parsers.PassageType;
import fr.cnrs.liris.drim.grt.proc.parsers.References;
import fr.cnrs.liris.drim.grt.proc.parsers.Temps;
import fr.cnrs.liris.drim.grt.proc.similarite.AnalyseurSpectral;
import fr.cnrs.liris.drim.grt.proc.similarite.AuditAnalyseurSpectral;
import fr.cnrs.liris.drim.grt.proc.similarite.DistanceStatisticoSemantique;

/**
 *
 * @author Sam
 */
public class Experiences {
    private RechercheNLexes rechercheLitteraleNew;
    private final Timer timerRechercheLitteraleNew;
    
    private RechercheParVoisinage rechercheParVoisinageNew;
    private final Timer timerRechercheParVoisinageNew;
    
    private AuditAnalyseurSpectral audit;
    private final Timer timerAuditAnalyseurSpectral;
    private int etapeAudit;
    
    private long debut = System.currentTimeMillis();
    
    private final JTextComponent cRapport;
    private final JLabel cStatut;
    private final String sortieHTML;
    
    private PassageType source;
    private PassageType citeur;
    private Citation[] temoin;
    private Citation[] temoinSA;
    
    private AnalyseurSpectral analyseur;
    
    private ArrayList<Citation[]> resultatsExperiences;
    private ArrayList<Map<Integer, Integer>> resultatsAuditsClusters;
    private ArrayList<Map<Integer, Integer>> resultatsAuditsSimilarites;
    private ArrayList<String> legendesAudits;
    
    public Experiences(JTextComponent rapport, JLabel statut, String nomFichierSortieHTML, PassageType source, PassageType citeur, Citation[] temoin, Citation[] temoinSA, AnalyseurSpectral analyseur) {
        cRapport = rapport;
        cStatut = statut;
        sortieHTML = nomFichierSortieHTML;
        
        this.source = source;
        this.citeur = citeur;
        this.temoin = temoin;
        this.temoinSA = temoinSA;
        this.analyseur = analyseur;
        this.audit = new AuditAnalyseurSpectral(analyseur);
        
        resultatsExperiences = new ArrayList<>();
        legendesAudits = new ArrayList<>();
        resultatsAuditsClusters = new ArrayList<>();
        resultatsAuditsSimilarites = new ArrayList<>();
        
        timerRechercheLitteraleNew = new Timer(200, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                cStatut.setText("[" + Temps.getTexte(System.currentTimeMillis()-debut) + "] " + 
                        rechercheLitteraleNew.getStatut() + " (" +
                        ((int)(rechercheLitteraleNew.getAvancement()*10000))/100.0 + "%).");
            }
        });
        timerRechercheParVoisinageNew = new Timer(200, new ActionListener() {
        @Override
            public void actionPerformed(ActionEvent ae) {
                cStatut.setText("[" + Temps.getTexte(System.currentTimeMillis()-debut) + "] " + 
                        rechercheParVoisinageNew.getStatut() + " (" +
                        ((int)(rechercheParVoisinageNew.getAvancement()*10000))/100.0 + "%).");
            }
        });
        timerAuditAnalyseurSpectral = new Timer(200, new ActionListener() {
        @Override
            public void actionPerformed(ActionEvent ae) {
                cStatut.setText("[" + Temps.getTexte(System.currentTimeMillis()-debut) + "] " + 
                        audit.getStatut() + " (" +
                        ((((int)(audit.getAvancement()*10000/4))/100.0) + (etapeAudit - 1) / 4) + "%).");
            }
        });
    }
    
    private final static int AO =  Recherche.L_ORDRE_DES_MOTS_COMPTE;
    private final static int SO =  Recherche.L_ORDRE_DES_MOTS_NE_COMPTE_PAS;
    private final static int AMV = Recherche.TEXTE_AVEC_MOTS_VIDES;
    private final static int SMV = Recherche.TEXTE_SANS_MOTS_VIDES;
    private final static int SMSV = Recherche.TEXTE_SANS_MOTS_STATISTIQUEMENT_VIDES;
    private final static int AEV = Recherche.FILTRAGE_AVEC_LEMMES_ET_EXPRESSIONS_VIDES;
    private final static int SEV = Recherche.FILTRAGE_SANS_LEMMES_NI_EXPRESSIONS_VIDES;
    private final static int SESV = Recherche.FILTRAGE_SANS_LEMMES_STATISTIQUEMENT_VIDES;
    private final static int B =   Recherche.TERMES_BRUTS;
    private final static int N =   Recherche.TERMES_NORMALISES;
    private final static int L =   Recherche.TERMES_LEMMATISES;
    private final static int STR = Recherche.CORRESPONDANCE_STRICTE;
    private final static int ABS = Recherche.CORRESPONDANCE_SEUILLEE_EN_VALEUR;
    private final static int REL = Recherche.CORRESPONDANCE_SEUILLEE_EN_POURCENTAGE;
    private final static int AMC = Recherche.CITATIONS_VERS_PLUSIEURS_SOURCES;
    private final static int SMC = Recherche.CITATIONS_VERS_UNE_SEULE_SOURCE;
    private final static int SEX = Recherche.SIMILARITE_EXACTE;
    private final static int SCO = Recherche.SIMILARITE_PAR_CO_OCCURRENCES;
    private final static int LSA = Recherche.SIMILARITE_LSA;
    private final static int T =   AnalyseurSpectral.TERMES_TOUS_SAUFS_VIDES;
    private final static int R =   AnalyseurSpectral.TERMES_LEMMATISES_RECURRENTS_UNIQUEMENT;
    private final static int DE =  DistanceStatisticoSemantique.DISTANCE_EUCLIDIENNE;
    private final static int DM =  DistanceStatisticoSemantique.DISTANCE_MANHATTAN;
    
    private int noXP = 0;
    public void run() throws IOException {
        ecritureAnalyseCitations();
        
        noXP = 0;
        xpN(AO, AMV, AEV, B, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, AMV, SEV, B, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, SMV, AEV, B, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, SMV, SEV, B, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, AMV, AEV, N, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, AMV, SEV, N, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, SMV, AEV, N, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, SMV, SEV, N, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, AMV, AEV, L, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, AMV, SEV, L, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, SMV, AEV, L, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(AO, SMV, SEV, L, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        
        noXP = 12;
        xpN(SO, AMV, AEV, B, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, AMV, SEV, B, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, SMV, AEV, B, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, SMV, SEV, B, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, AMV, AEV, N, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, AMV, SEV, N, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, SMV, AEV, N, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, SMV, SEV, N, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, AMV, AEV, L, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, AMV, SEV, L, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, SMV, AEV, L, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        xpN(SO, SMV, SEV, L, SEX, 0, 0, STR, 0.00, 5, 3, 500);
        
        noXP = 24;
        xpN(AO, AMV, AEV, B, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, AMV, SEV, B, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, SMV, AEV, B, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, SMV, SEV, B, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, AMV, AEV, N, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, AMV, SEV, N, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, SMV, AEV, N, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, SMV, SEV, N, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, AMV, AEV, L, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, AMV, SEV, L, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, SMV, AEV, L, SEX, 0, 0, ABS, 7, 5, 3, 500);
        xpN(AO, SMV, SEV, L, SEX, 0, 0, ABS, 7, 5, 3, 500);
        
        noXP = 36;
        xpN(AO, AMV, AEV, B, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, AMV, SEV, B, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, SMV, AEV, B, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, SMV, SEV, B, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, AMV, AEV, N, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, AMV, SEV, N, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, SMV, AEV, N, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, SMV, SEV, N, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, AMV, AEV, L, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, AMV, SEV, L, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, SMV, AEV, L, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        xpN(AO, SMV, SEV, L, SEX, 0, 0, REL, 0.70, 5, 3, 500);
        
        noXP = 48;
        
        if(resultatsExperiences.size()>0) {
            ecritureResultatsExperiences();
            ecritureAnalyseLemmatisation();
        }
        if(legendesAudits.size()>0) {
            ecritureResultatsAudits();
        }
    }
    
    
    private void as(int nbFrequents, int traitement, int termesUtilises, double seuil, int distance) throws IOException {
        noXP++;
        debut = System.currentTimeMillis();
        String nomXP = nbFrequents + "_" +
                (traitement==B?"B":(traitement==N?"N":"L")) + "_" + 
                (termesUtilises==T?"T":"R") + "_" +
                seuil + "_" +
                (distance==DE?"DE":"DM");
        File fSortie = new File(sortieHTML + (noXP<100 ? "0":"") + (noXP<10 ? "0":"") + noXP + "_" + nomXP + ".html");
        File fSortie2 = new File(sortieHTML + (noXP<100 ? "0":"") + (noXP<10 ? "0":"") + noXP + "_" + nomXP + ".csv");
        while(fSortie.exists() || fSortie2.exists()) {
            noXP++;
            fSortie = new File(sortieHTML + (noXP<100 ? "0":"") + (noXP<10 ? "0":"") + noXP + "_" + nomXP + ".html");
            fSortie2 = new File(sortieHTML + (noXP<100 ? "0":"") + (noXP<10 ? "0":"") + noXP + "_" + nomXP + ".csv");
        }
        BufferedWriter sortie = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        BufferedWriter sortie2 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        sortie.write("<html>"); sortie.newLine();
        sortie.write("  <head>"); sortie.newLine();
        sortie.write("    <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />"); sortie.newLine();
        sortie.write("    <title>Expérience "+noXP+"</title>"); sortie.newLine();
        sortie.write("    "+getCSS()); sortie.newLine();
        sortie.write("  </head>"); sortie.newLine();
        sortie.write("  <body>"); sortie.newLine();
        sortie.flush();
        cStatut.setText("Audit de l'analyseur en cours (0%)");
        final long tps0 = System.currentTimeMillis();
        String legende = "[" + noXP + "] Audit d'analyseur spectral : taille de spectre "+
                nbFrequents + ", " +
                (traitement==B?"termes bruts":(traitement==N?"termes normalisés":"termes lemmatisés")) + ", " +
                (termesUtilises==T?"tous les termes non vides":"lemmes récurrents uniquement") + ", " +
                "seuil de similarité " + seuil + ", " +
                (distance==DE?"distance euclidienne":"distance de Manhattan");
        cRapport.setText(cRapport.getText()+ legende + "\n");
        timerAuditAnalyseurSpectral.start();
        legendesAudits.add(legende);
        
        etapeAudit = 1;
        Map<Integer, Integer> clusters = audit.getRepartitionClusters(nbFrequents, traitement, termesUtilises, seuil, distance);
        resultatsAuditsClusters.add(clusters);
        
        etapeAudit = 2;
        Map<Integer, Integer> termesSimilaires = audit.getNombreDeLemmesSimilaires(temoin, nbFrequents, traitement, termesUtilises, seuil, distance);
        resultatsAuditsSimilarites.add(termesSimilaires);
        
        etapeAudit = 3;
        Map<Terme, Terme[]> listeClusters = audit.getClusters(nbFrequents, traitement, termesUtilises, seuil, distance);
        Terme[] termes = new Terme[listeClusters.keySet().size()];
        listeClusters.keySet().toArray(termes);
        Arrays.sort(termes, new OrdreAlphabetiqueGrec());
        for(Terme t: termes) {
            Terme[] lies = listeClusters.get(t);
            ArrayList<Terme> liesRnd = new ArrayList<>(Arrays.asList(lies));
            Terme[] lies2 = new Terme[Math.min(lies.length, 50)];
            for(int i=0; i<lies2.length; i++) {
                int a = (int)(1.0 * Math.random() * liesRnd.size());
                lies2[i] = liesRnd.get(a);
                liesRnd.remove(a);
            }
            Arrays.sort(lies2, new OrdreAlphabetiqueGrec());
            if(lies2.length > 1) {
                sortie.write("    <p><em>" + t.getExpression() + "</em> : " + Arrays.toString(lies2) + 
                        ((lies2.length < lies.length)?(" ... (" + (lies.length-lies2.length) + " autres)"):"") + 
                        "</p>"); sortie.newLine();
            }
        }
        
        etapeAudit = 4;
        Map<Terme[], Double> paires = audit.getAllPairesSimilaires(nbFrequents, traitement, termesUtilises, seuil, distance);
        for(Terme[] paire: paires.keySet()) {
            sortie2.write(paire[0] + "\t" + paire[1] + "\t" + paires.get(paire));
            sortie2.newLine();
        }
        sortie2.flush();
        
        long tps = System.currentTimeMillis() - tps0;
        timerAuditAnalyseurSpectral.stop();
        sortie.write("  </body>"); sortie.newLine();
        sortie.write(" </html>"); sortie.newLine();
        sortie.flush();
        cStatut.setText("Audit terminé.");
        cRapport.setText(cRapport.getText()+"L'audit a pris " + Temps.getTexte(tps) + ".\n");
    }
    
    private void xpN(int importanceOrdre, int modeFiltrage, int modeFiltrage2, int traitement,
            int modeSimilarite, int parametreSimilarite, double seuilEgalite, 
            int tolerance, double seuilTolerance, int seuilFusion, int tailleNLexes, int tailleMaxCitation) throws IOException {
        noXP++;
        debut = System.currentTimeMillis();
        String nomXP = (importanceOrdre==AO?"AO":"SO") + "_" +
                (modeFiltrage==AMV?"AMV":(modeFiltrage==SMV?"SMV":"SMSV")) + "_" +
                (modeFiltrage2==AEV?"AEV":(modeFiltrage2==SEV?"SEV":"SESV")) + "_" +
                (traitement==B?"B":(traitement==N?"N":"L")) + "_" + 
                (modeSimilarite==SEX?"SEX":("SCO_"+parametreSimilarite+"_"+seuilEgalite)) + "_" +
                (tolerance==STR?"STR":(tolerance==REL?"REL":"ABS")+"_"+seuilTolerance) +
                "_" + seuilFusion + "_" + tailleMaxCitation + "_" + tailleNLexes;
        File fSortie = new File(sortieHTML + (noXP<100 ? "0":"") + (noXP<10 ? "0":"") + noXP + "_" + nomXP + ".html");
        while(fSortie.exists()) {
            noXP++;
            fSortie = new File(sortieHTML + (noXP<100 ? "0":"") + (noXP<10 ? "0":"") + noXP + "_" + nomXP + ".html");
        }
        BufferedWriter sortie = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        sortie.write("<html>"); sortie.newLine();
        sortie.write("  <head>"); sortie.newLine();
        sortie.write("    <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />"); sortie.newLine();
        sortie.write("    <title>Expérience "+noXP+"</title>"); sortie.newLine();
        sortie.write("    "+getCSS()); sortie.newLine();
        sortie.write("  </head>"); sortie.newLine();
        sortie.write("  <body>"); sortie.newLine();
        sortie.flush();
        cStatut.setText("Recherche de citations en cours (0%)");
        final long tps0 = System.currentTimeMillis();
        rechercheLitteraleNew = new RechercheNLexes(source, citeur, OrdreStrict.getInstance(OrdreStrict.VERSETS), OrdreStrict.getInstance(OrdreStrict.MOTS));
        rechercheLitteraleNew.setImportanceOrdreMots(importanceOrdre);
        rechercheLitteraleNew.setFiltrageMotsVides(modeFiltrage);
        rechercheLitteraleNew.setFiltrageExpressionsVides(modeFiltrage2);
        rechercheLitteraleNew.setTraitementTermes(traitement);
        rechercheLitteraleNew.setToleranceCorrespondance(tolerance, seuilTolerance);
        rechercheLitteraleNew.setSeuilFusion(seuilFusion);
        rechercheLitteraleNew.setSimilarite(modeSimilarite, parametreSimilarite, seuilEgalite);
        rechercheLitteraleNew.setAnalyseur(analyseur);
        rechercheLitteraleNew.setNbMotsMaxParCitation(tailleMaxCitation);

        sortie.write("    <h1>Expérience "+noXP+"</h1>"); sortie.newLine();
        sortie.write("    <h2>Description</h2>"); sortie.newLine();
        sortie.write("    <p>Nous allons rechercher des citations d'un texte dans "
                + "un autre par la méthode des n-lexes.</p>"); sortie.newLine();
        sortie.write("    <p>Cela signifie que nous allons commencer par chercher "
                + "toutes les suites de n mots similaires dans les deux textes. "
                + "Ensuite, pour chaque correspondance trouvée, nous allons étendre "
                + "l'analyse aux mots précédents et suivants pour voir jusqu'où les "
                + "textes sont similaires. Cela nous donnera le texte des citations, "
                + "dont nous récupèrerons les références.</p>"); sortie.newLine();
        sortie.write("    <p>Les paramètres choisis sont les suivants :</p>"); sortie.newLine();
        sortie.write("    <ul>"); sortie.newLine();
        sortie.write("      <li><em>n = " + tailleNLexes + "</em> (nous prenons "
                + "toutes les suites de " + tailleNLexes + " mots pour les "
                + "comparer).</li>"); sortie.newLine();
        sortie.write("      <li><em>" + (importanceOrdre==AO?"avec ordre":"sans ordre") + 
                "</em> (nous cherchons la similarité entre les mots dans " + 
                (importanceOrdre==AO?"l'ordre uniquement":"l'ordre ou dans le désordre") + 
                ").</li>"); sortie.newLine();
        sortie.write("      <li><em>" + (modeFiltrage==AMV?"avec les mots vides":
                (modeFiltrage==SMV?"sans les mots vides":"sans les mots statistiquement vides")) + 
                "</em> (les mots vides, ceux qui se retrouvent tout le temps partout "
                + "- articles, prépositions etc. - " + (modeFiltrage==AMV?"sont conservés "
                + "pour l'analyse":"sont enlevés du texte avant l'analyse") +
                ").</li>"); sortie.newLine();
        sortie.write("      <li><em>" + (modeFiltrage2==AEV?"avec les expressions vides":
                (modeFiltrage2==SEV?"sans les expressions vides":"sans les expressions statistiquement vides")) + 
                "</em> (les résultats qui s'appuient uniquement sur des expressions vides ou récurrentes, "
                + "ou qui ne sont constitués que de lemmes vides ou usuels, " + 
                (modeFiltrage2==AEV?"sont conservés":"sont abandonnés") +
                ").</li>"); sortie.newLine();
        sortie.write("      <li><em>" + (traitement==B?"aucun traitement des "
                + "termes":(traitement==N?"normalisation des termes":"lemmatisation "
                + "des termes")) + "</em> (" + (traitement==B?"les termes seront "
                + "utilisés tels quels, avec flexions et altérations":
                (traitement==N?"les altérations des termes seront supprimées - "
                + "esprits, accents, majuscules et ponctuation":"les termes seront "
                + "réduits à leur lemme - la forme de base du terme, sans flexion")) + 
                ").</li>"); sortie.newLine();
        sortie.write("      <li><em>" + (modeSimilarite==SEX?"similarité exacte</em> "
                + "(deux termes sont identiques ou différents)":("similarité par "
                + "co-occurrence (avec les "+parametreSimilarite+" termes les plus "
                + "fréquents, seuil de "+seuilEgalite+" </em> (les termes qui se "
                + "retrouvent souvent avec les mêmes termes fréquents seront "
                + "considérés comme similaires, et s'ils le sont suffisamment, "
                + "ils seront considérés comme identiques)"))); sortie.newLine();
        sortie.write("      <li><em>" + (tolerance==STR?"correspondance stricte":
                ("correspondance tolérante ("+(tolerance==REL?(seuilTolerance*100)+"%":seuilTolerance)+")")) + 
                "</em> (les suites de mots seront considérées comme similaires si " + 
                (tolerance==STR?"elles sont composées strictement des mêmes termes":
                ("chacune comporte au maximum "+(tolerance==REL?(seuilTolerance*100)+"% de":seuilTolerance)+
                " termes qui ne sont pas dans l'autre")) + ").</li>"); sortie.newLine();
        sortie.write("      <li><em>seuil de fusion : " + seuilFusion + "</em> "
                + "(si deux citations ont au maximum " + seuilFusion + " mots "
                + "entre elles dans chacun des textes, elles seront fusionnées)."
                + "</li>"); sortie.newLine();
        sortie.write("      <li><em>taille max = " + tailleMaxCitation + "</em> "
                + "(une citation trouvée comportera au maximum "+tailleMaxCitation+" "
                + "mots, pour ne pas trop générer de calculs ; pragmatiquement, "
                + "on considère qu'une citation plus grande est une erreur).</li>"); sortie.newLine();
        sortie.write("    </ul>"); sortie.newLine();
        sortie.flush();
        cRapport.setText(cRapport.getText()+"[" + noXP + "] Recherche par n-lexes : "+
                (importanceOrdre==AO?"avec ordre":"sans ordre") + ", " +
                (modeFiltrage==AMV?"avec les mots vides":(modeFiltrage==SMV?"sans les mots vides":"sans les mots statistiquement vides")) + ", " +
                (modeFiltrage2==AEV?"avec les expressions vides":(modeFiltrage2==SEV?"sans les expressions vides":"sans les expressions statistiquement vides")) + ", " +
                (traitement==B?"termes bruts":(traitement==N?"termes normalisés":"termes lemmatisés")) + ", " +
                (modeSimilarite==SEX?"similarité exacte":("similarité par co-occurrences ("+parametreSimilarite+" termes, seuil "+seuilEgalite + ")")) + ", " +
                (tolerance==STR?"correspondance stricte":("correspondance tolérante ("+(tolerance==REL?(seuilTolerance*100)+"%":seuilTolerance)+")")) + ", " +
                seuilFusion +" mots max pour fusion, "+ tailleMaxCitation+" mots max par citation, " + tailleNLexes+"-lexes" + 
                "\n");
        
        timerRechercheLitteraleNew.start();
        try {
            rechercheLitteraleNew.setTailleFenetre(tailleNLexes);
            Citation[] resultat = rechercheLitteraleNew.chercheCitations();
            resultatsExperiences.add(resultat);
            timerRechercheLitteraleNew.stop();
            long tps = System.currentTimeMillis() - tps0;
            cRapport.setText(cRapport.getText()+rechercheLitteraleNew.getLogExecution() + 
                    "\n"+"La recherche a pris " + Temps.getTexte(tps) + " et a ramené "+resultat.length+" références.\n");
            ecritureAnalyse(sortie, resultat, tps, traitement);
            System.gc();
            cStatut.setText("Recherche terminée.");
        } catch(ListeStatistiqueNonInitialiseeException lnie) {
            sortie.write("    <P>La liste des mots statistiquement vides est nécessaire et n'a pas été faite.</P>"); sortie.newLine();
        }
        sortie.write("  </BODY>"); sortie.newLine();
        sortie.write("</HTML>"); sortie.newLine();
        sortie.flush();
    }
    
    private void xpV(int modeFiltrage, int traitement, int multicitation, int tailleFenetre) throws IOException {
        noXP++;
        debut = System.currentTimeMillis();
        String nomXP = (modeFiltrage==AMV?"AMV":(modeFiltrage==SMV?"SMV":"SMSV")) + "_" +
                (traitement==B?"B":(traitement==N?"N":"L")) + "_" + 
                (multicitation==AMC?"AMC":"SMC") + "_" + tailleFenetre;
        File fSortie = new File(sortieHTML + (noXP<100 ? "0":"") + (noXP<10 ? "0":"") + noXP + "_" + nomXP + ".html");
        while(fSortie.exists()) {
            noXP++;
            fSortie = new File(sortieHTML + (noXP<100 ? "0":"") + (noXP<10 ? "0":"") + noXP + "_" + nomXP + ".html");
        }
        BufferedWriter sortie = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        sortie.write("<html>"); sortie.newLine();
        sortie.write("  <head>"); sortie.newLine();
        sortie.write("    <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />"); sortie.newLine();
        sortie.write("    <title>Expérience "+noXP+"</title>"); sortie.newLine();
        sortie.write("    "+getCSS()); sortie.newLine();
        sortie.write("  </head>"); sortie.newLine();
        sortie.write("  <body>"); sortie.newLine();
        sortie.flush();
        cStatut.setText("Recherche de citations en cours (0%)");
        // Dans la recherche par voisinage, le type de référence n'est pas géré pour l'instant
        rechercheParVoisinageNew = new RechercheParVoisinage(source.getPassage(), citeur.getPassage());
        rechercheParVoisinageNew.setTraitementTermes(traitement);
        rechercheParVoisinageNew.setFiltrageMotsVides(modeFiltrage);
        rechercheParVoisinageNew.setMultiCitation(multicitation);
        
        sortie.write("    <h1>Expérience "+noXP+"</h1>"); sortie.newLine();
        sortie.write("    <h2>Description</h2>"); sortie.newLine();
        sortie.write("    <p>Nous allons rechercher des citations d'un texte dans "
                + "un autre par la méthode des fenêtres chevauchantes.</p>"); sortie.newLine();
        sortie.write("    <p>Cela signifie que nous allons chercher dans les textes "
                + "des suites de mots (les 'fenêtres') ayant au moins deux mots en commun. "
                + "Ensuite, nous allons fusionner les fenêtres qui se chevauchent.</p>"); sortie.newLine();
        sortie.write("    <p>Les paramètres choisis sont les suivants :</p>"); sortie.newLine();
        sortie.write("    <ul>"); sortie.newLine();
        sortie.write("      <li><em>" + (modeFiltrage==AMV?"avec les mots vides":"sans les mots vides") + 
                "</em> (les mots vides, ceux qui se retrouvent tout le temps partout "
                + "- articles, prépositions etc. - " + (modeFiltrage==AMV?"sont conservés "
                + "pour l'analyse":"sont enlevés du texte avant l'analyse") +
                ").</li>"); sortie.newLine();
        sortie.write("      <li><em>" + (traitement==B?"aucun traitement des "
                + "termes":(traitement==N?"normalisation des termes":"lemmatisation "
                + "des termes")) + "</em> (" + (traitement==B?"les termes seront "
                + "utilisés tels quels, avec flexions et altérations":
                (traitement==N?"les altérations des termes seront supprimées - "
                + "esprits, accents, majuscules et ponctuation":"les termes seront "
                + "réduits à leur lemme - la forme de base du terme, sans flexion")) + 
                ").</li>"); sortie.newLine();
        sortie.write("      <li><em>" + (multicitation==AMC?"avec multi-citation":"sans multi-citation") + 
                "</em> (quand plusieurs textes source sont trouvés pour un texte citant " + 
                (multicitation==AMC?"nous les conserverons tous":
                "nous ne garderons que le plus similaire"+").</li>")); sortie.newLine();
        sortie.write("      <li><em>n = " + tailleFenetre + "</em> (nous chercherons des suites de " + 
                tailleFenetre + " mots avec au moins deux mots identiques).</li>"); sortie.newLine();
        sortie.write("    </ul>"); sortie.newLine();
        sortie.flush();
        cRapport.setText(cRapport.getText()+"[" + noXP + "] Recherche par voisinage : "+
                (modeFiltrage==AMV?"avec les mots vides":"sans les mots vides") + ", " +
                (traitement==B?"termes bruts":(traitement==N?"termes normalisés":"termes lemmatisés")) + ", " +
                (multicitation==AMC?"citations multiples":"citations uniques") + ", " +
                "fenêtre de taille "+tailleFenetre +
                "\n");
        long tps0 = System.currentTimeMillis();
        timerRechercheParVoisinageNew.start();
        Citation[] resultat = rechercheParVoisinageNew.chercheCitations(tailleFenetre);
        resultatsExperiences.add(resultat);
        timerRechercheParVoisinageNew.stop();
        long tps = System.currentTimeMillis() - tps0;
        cStatut.setText("Analyse des résultats en cours");
        ecritureAnalyse(sortie, resultat, tps, traitement);
        System.gc();
        cStatut.setText("Recherche terminée.");
    }
    
    private String getCSS() {
        return "<style type=\"text/css\">"
                + "body {"
                + "background-color: #FFFFFF;"
                + "color: #202020;"
                + "font-family: Calibri, Verdana, Arial, Sans-Serif;"
                + "font-size: 12px;"
                + "text-align: justify;"
                + "}"
                + "h1 {"
                + "color: #A00000;"
                + "font-size: 1.5em;"
                + "text-decoration: underline;"
                + "}"
                + "h2 {"
                + "color: #00A000;"
                + "font-size: 1.5em;"
                + "text-decoration: underline;"
                + "}"
                + "h3 {"
                + "color: #000080;"
                + "font-size: 1.5em;"
                + "text-decoration: underline;"
                + "}"
                + "h4 {"
                + "color: #A0A0A0;"
                + "font-size: 1.5em;"
                + "text-decoration: underline;"
                + "}"
                + "h5 {"
                + "color: #A0A0A0;"
                + "font-size: 1em;"
                + "text-decoration: underline;"
                + "}"
                + "em {"
                + "font-style: italic;"
                + "text-decoration: underline;"
                + "}"
                + "p.citation {"
                + "color: #0000FF;"
                + "padding-left: .5em;"
                + "padding-top: .5em;"
                + "padding-bottom: .5em;"
                + "padding-right: .5em;"
                + "border: 1px solid #202020;"
                + "font-style: italic;"
                + "letter-spacing: .1em;"
                + "}"
                + "p.reference {"
                + "marging-left: 1em;"
                + "font-variant: small-caps;"
                + "}"
                + "table {"
                + "table-layout: fixed;"
                + "}"
                + "td {"
                + "border: 1px dotted #202020;"
                + "text-align: center;"
                + "}"
                + "td.spectre {"
                + "font-size: 1em;"
                + "font-style: normal;"
                + "font-weight: bold;"
                + "}"
                + "td.liste {"
                + "font-size: .6em;"
                + "font-style: normal;"
                + "font-weight: normal;"
                + "}"
                + "a:link, a:visited {"
                + "color: #A0A000;"
                + "text-decoration: none;"
                + "}"
                + "span.citation0 {"
                + "}"
                + "span.citation1 {"
                + "background-color: #D0D0A0;"
                + "font-weight: bold;"
                + "}"
                + "span.citation2 {"
                + "background-color: #A0A0D0;"
                + "font-weight: bold;"
                + "}"
                + "span.citation3 {"
                + "background-color: #A0D0A0;"
                + "background-image: repeating-linear-gradient(to right, #D0D0A0, #A0A0D0);"
                + "background-size: .5em 2em;"
                + "font-weight: bold;"
                + "}"
                + "[data-tip] {"
                + "position: relative;"
                + "}"
                + "[data-tip]:hover:after {"
                + "content: span(data-tip);"
                + "position: absolute;"
                + "top: 1.5em; left: 0;"
                + "white-space: nowrap;"
                + "padding: 5px 10px;"
                + "background: #A0A0A0;"
                + "color: #FFFFFF;"
                + "}"
                + "</style>";
    }
    
    private void ecritureAnalyse(BufferedWriter sortie, Citation[] resultat, long tps, int traitement) throws IOException {
        cStatut.setText("Analyse des résultats en cours (0/10)");
        sortie.write("    <h2>Résultats statistiques</h2>"); sortie.newLine();
        sortie.write("    <p>La recherche a pris " + Temps.getTexte(tps) + ".</p>"); sortie.newLine();
        sortie.write("    <p>" + resultat.length + " citations ont été trouvées.</p>"); sortie.newLine();
        tps = System.currentTimeMillis();
        sortie.write("    <h3>Résultats bruts</h3>"); sortie.newLine();
        sortie.write("    <ul>"); sortie.newLine();
        int preIA = ((int)(100000.0*ComparateurCitations.getPrecision(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_BRUTES))/1000);
	int nbPreIA = ComparateurCitations.getBonnesReponses(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_BRUTES);
        sortie.write("      <li>" + preIA + "% de ces citations ("+nbPreIA+") ont au moins un mot en commun, dans chacun des textes, avec une référence à trouver.</li>"); sortie.newLine();
        sortie.flush();
        cRapport.setText(cRapport.getText()+"Précision brute :  " + preIA + "% (" + nbPreIA + "/" + resultat.length + ")" + "\n");
        cStatut.setText("Analyse des résultats en cours (1/10)");
        int rapIA = ((int)(100000.0*ComparateurCitations.getRappel(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_BRUTES))/1000);
	int nbRapIA = ComparateurCitations.getBienTrouvees(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_BRUTES);
        sortie.write("      <li>" + rapIA + "% des références à trouver ("+nbRapIA+") ont au moins un mot en commun, dans chacun des textes, avec une citation trouvée.</li>"); sortie.newLine();
        sortie.flush();
        cRapport.setText(cRapport.getText()+"Rappel brut :  " + rapIA + "% (" + nbRapIA + "/" + temoin.length + ")" + "\n");
        cStatut.setText("Analyse des résultats en cours (2/10)");
        /*int preIS = ((int)(100000.0*ComparateurCitations.getPrecision(resultat, temoinSA, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT))/1000);
	//int nbPreIS = ComparateurCitations.getBonnesReponses(resultat, temoinSA, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT);
        //sortie.write("      <li>" + preIS + "% des citations trouvées ("+nbPreIS+") ont au moins un mot en commun, dans chacun des textes, avec une citation à trouver (hors allusions donc).</li>"); sortie.newLine();
        //sortie.flush();
        cStatut.setText("Analyse des résultats en cours (3/15)");
        //int rapIS = ((int)(100000.0*ComparateurCitations.getRappel(resultat, temoinSA, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT))/1000);
	//int nbRapIS = ComparateurCitations.getBienTrouvees(resultat, temoinSA, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT);
        //sortie.write("      <li>" + rapIS + "% des citations à trouver ("+nbRapIS+") ont au moins un mot en commun, dans chacun des textes, avec une citation trouvée (toujours hors allusions).</li>"); sortie.newLine();
        //sortie.flush();
        cStatut.setText("Analyse des résultats en cours (4/15)");
        //int preEA = ((int)(100000.0*ComparateurCitations.getPrecision(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_SUPERPOSITION))/1000);
	//int nbPreEA = ComparateurCitations.getBonnesReponses(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_SUPERPOSITION);
        //sortie.write("      <li>" + preEA + "% des citations trouvées ("+nbPreEA+") sont strictement identiques à une référence à trouver.</li>"); sortie.newLine();
        //sortie.flush();
        cStatut.setText("Analyse des résultats en cours (5/15)");
        //int rapEA = ((int)(100000.0*ComparateurCitations.getRappel(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_SUPERPOSITION))/1000);
	//int nbRapEA = ComparateurCitations.getBienTrouvees(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_SUPERPOSITION);
        //sortie.write("      <li>" + rapEA + "% des citations à trouver ("+nbRapEA+") sont strictement identiques à une citation trouvée.</li>"); sortie.newLine();
        //sortie.flush();
        cStatut.setText("Analyse des résultats en cours (6/15)");
        int nbIneIA = ComparateurCitations.getCitationsInedites(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT).length;
        int nbSurIA = ComparateurCitations.getCitationsSurnumeraires(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT).length;
        sortie.write("      <li>" + nbSurIA + " des citations qui n'étaient pas à trouver sont sur des passages concurrents d'une citation à trouver.</li>"); sortie.newLine();
        sortie.write("      <li>" + nbIneIA + " des citations qui n'étaient pas à trouver n'ont pas de lien avec les citations à trouver.</li>"); sortie.newLine();*/
        sortie.write("    </ul>"); sortie.newLine();
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (3/10)");
        boolean[] trouvees = ComparateurCitations.getSpectre(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_BRUTES);
        sortie.write("    <h3>Spectre brut des citations trouvées : </h3>"); sortie.newLine();
        sortie.write("    <table>"); sortie.newLine();
        sortie.write("      <th>"); sortie.newLine(); sortie.write("        ");
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (4/10)");
        for(int i=1; i<=temoin.length; i++) {
            sortie.write("<td class=\"liste\">"+(i<100 ? "0":"") + (i<10 ? "0":"") + i +"</td> ");
        }
        sortie.newLine();
        sortie.write("      </th>"); sortie.newLine();
        sortie.write("      <tr>"); sortie.newLine(); sortie.write("        ");
        sortie.write("<td class=\"spectre\"></td> "); // Première cellule = titre de ligne
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (5/10)");
        int nb = 0;
        for(boolean trouvee: trouvees) {
            nb++;
            if(trouvee) {
                sortie.write("<td class=\"spectre\">"+nb+"</td> ");
            } else {
                sortie.write("<td class=\"spectre\"></td> ");
            }
        }
        sortie.write("    </table>"); sortie.newLine();
        sortie.newLine();
        
        
        sortie.write("    <h3>Résultats épurés</h3>"); sortie.newLine();
        sortie.write("    <ul>"); sortie.newLine();
        preIA = ((int)(100000.0*ComparateurCitations.getPrecision(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_EPUREES))/1000);
	nbPreIA = ComparateurCitations.getBonnesReponses(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_EPUREES);
        sortie.write("      <li>" + preIA + "% de ces citations ("+nbPreIA+") ont au moins un mot en commun, dans chacun des textes, avec une référence à trouver.</li>"); sortie.newLine();
        sortie.flush();
        cRapport.setText(cRapport.getText()+"Précision épurée :  " + preIA + "% (" + nbPreIA + "/" + resultat.length + ")" + "\n");
        cStatut.setText("Analyse des résultats en cours (6/10)");
        rapIA = ((int)(100000.0*ComparateurCitations.getRappel(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_EPUREES))/1000);
	nbRapIA = ComparateurCitations.getBienTrouvees(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_EPUREES);
        sortie.write("      <li>" + rapIA + "% des références à trouver ("+nbRapIA+") ont au moins un mot en commun, dans chacun des textes, avec une citation trouvée.</li>"); sortie.newLine();
        sortie.flush();
        cRapport.setText(cRapport.getText()+"Rappel épuré :  " + rapIA + "% (" + nbRapIA + "/" + temoin.length + ")" + "\n");
        cStatut.setText("Analyse des résultats en cours (7/10)");
        sortie.write("    </ul>"); sortie.newLine();
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (8/10)");
        trouvees = ComparateurCitations.getSpectre(resultat, temoin, 
                ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_EPUREES);
        sortie.write("    <h3>Spectre épuré des citations trouvées : </h3>"); sortie.newLine();
        sortie.write("    <table>"); sortie.newLine();
        sortie.write("      <th>"); sortie.newLine(); sortie.write("        ");
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (9/10)");
        for(int i=1; i<=temoin.length; i++) {
            sortie.write("<td class=\"liste\">"+(i<100 ? "0":"") + (i<10 ? "0":"") + i +"</td> ");
        }
        sortie.newLine();
        sortie.write("      </th>"); sortie.newLine();
        sortie.write("      <tr>"); sortie.newLine(); sortie.write("        ");
        sortie.write("<td class=\"spectre\"></td> "); // Première cellule = titre de ligne
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (10/10)");
        nb = 0;
        for(boolean trouvee: trouvees) {
            nb++;
            if(trouvee) {
                sortie.write("<td class=\"spectre\">"+nb+"</td> ");
            } else {
                sortie.write("<td class=\"spectre\"></td> ");
            }
        }
        sortie.write("    </table>"); sortie.newLine();
        sortie.newLine();
        
        /*sortie.write("      </tr>"); sortie.newLine();
        sortie.write("    </table>"); sortie.newLine();
        sortie.write("    <a name=\"restexte\"><h2>Résultats dans le texte</h2></a>"); sortie.newLine();
        sortie.write("    <a href=\"#restexte\">Résultats dans le texte</a> / <a href=\"#trouvees\">Citations trouvées</a> / <a href=\"#nontrouvees\">non trouvées</a> / <a href=\"#surnumeraires\">surnuméraires</a> / <a href=\"#inedites\">inédites</a>"); sortie.newLine();
        ecritureTexte(sortie, resultat, traitement);
        sortie.write("    <a name=\"brut\"><h2>Résultats bruts</h2></a>"); sortie.newLine();
        sortie.write("    <a href=\"#restexte\">Résultats dans le texte</a> / <a href=\"#trouvees\">Citations trouvées</a> / <a href=\"#nontrouvees\">non trouvées</a> / <a href=\"#surnumeraires\">surnuméraires</a> / <a href=\"#inedites\">inédites</a>"); sortie.newLine();
        sortie.write("    <a name=\"trouvees\"><h3>Liste des "+trouvees.keySet().size()+" citations trouvées (les bornes ne sont pas forcément correctes)</h3></a>"); sortie.newLine();
        sortie.write("    <a href=\"#brut\">Retour</a>"); sortie.newLine();
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (10/15)");
        nb = 0;
        for(Citation aTrouver: temoin) {
            nb++;
            ArrayList<Citation> trouvee = trouvees.get(aTrouver);
            if(trouvee != null) {
                sortie.write("    <h4>Citation " + nb + "</h4>"); sortie.newLine();
                sortie.write("    <h5>Citation à trouver</h5>"); sortie.newLine();
                sortie.write("    <p>Passage</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(aTrouver.getCite().getNumerosCompletsSousPassages(), source.getType()) +
                        " (" + aTrouver.getCite().getReferenceInitialeDans(source.getPassage()) + ")" + "</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(aTrouver.getCite()).getTexte()+"</p>"); sortie.newLine();
                sortie.write("    <p>Cité par</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(aTrouver.getCiteur().getNumerosCompletsSousPassages(), citeur.getType()) +
                        " (" + aTrouver.getCiteur().getReferenceInitialeDans(citeur.getPassage()) + ")" +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(aTrouver.getCiteur()).getTexte()+"</p>"); sortie.newLine();
                for(int i=0; i<trouvee.size(); i++) {
                    Citation c = trouvee.get(i);
                    sortie.write("    <h5>Citation trouvée "+(i+1)+"/"+trouvee.size()+"</h5>"); sortie.newLine();
                    sortie.write("    <p>Passage</p>"); sortie.newLine();
                    sortie.write("    <p class=\"reference\">"+References.minimiseRefs(c.getCite().getNumerosCompletsSousPassages(), source.getType()) +
                            " (" + c.getCite().getReferenceInitialeDans(source.getPassage()) + ")" +"</p>"); sortie.newLine();
                    sortie.write("    <p class=\"citation\">"+new Texte(c.getCite()).getTexte()+"</p>"); sortie.newLine();
                    sortie.write("    <p>Cité par</p>"); sortie.newLine();
                    sortie.write("    <p class=\"reference\">"+References.minimiseRefs(c.getCiteur().getNumerosCompletsSousPassages(), citeur.getType()) +
                            " (" + c.getCiteur().getReferenceInitialeDans(citeur.getPassage()) + ")" +"</p>"); sortie.newLine();
                    sortie.write("    <p class=\"citation\">"+new Texte(c.getCiteur()).getTexte()+"</p>"); sortie.newLine();
                }
            }
        }
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (11/15)");
        ArrayList<Citation> pasTrouvees = new ArrayList<>();
        Citation[] cc = ComparateurCitations.getCitationsNonTrouvees(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT);
        pasTrouvees.addAll(Arrays.asList(cc));
        sortie.write("    <a name=\"nontrouvees\"><h3>Liste des "+pasTrouvees.size()+" citations qui n'ont pas été trouvées</h3></a>"); sortie.newLine();
        sortie.write("    <a href=\"#brut\">Retour</a>"); sortie.newLine();
        nb = 0;
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (12/15)");
        for(Citation aTrouver: temoin) {
            nb++;
            if(pasTrouvees.contains(aTrouver)) {
                sortie.write("    <h4>Citation " + nb + "</h4>"); sortie.newLine();
                sortie.write("    <p>Passage</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(
                        aTrouver.getCite().getNumerosCompletsSousPassages(), source.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(aTrouver.getCite()).getTexte()+"</p>"); sortie.newLine();
                sortie.write("    <p>Cité dans</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(
                        aTrouver.getCiteur().getNumerosCompletsSousPassages(), citeur.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(aTrouver.getCiteur()).getTexte()+"</p>"); sortie.newLine();
            }
        }
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (13/15)");
        Citation[] surnumeraires = ComparateurCitations.getCitationsSurnumeraires(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT);
        if(surnumeraires.length<=100) {
            sortie.write("    <a name=\"surnumeraires\"><h3>Liste des "+surnumeraires.length+" citations trouvées qui concurrencent une citation de la liste à trouver</h3></a>"); sortie.newLine();
            sortie.write("    <a href=\"#brut\">Retour</a>"); sortie.newLine();
            nb = 0;
            for(Citation fausse: surnumeraires) {
                nb++;
                sortie.write("    <h4>Citation " + nb + "</h4>"); sortie.newLine();
                sortie.write("    <p>Passage</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(fausse.getCite().getNumerosCompletsSousPassages(), source.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(fausse.getCite()).getTexte()+"</p>"); sortie.newLine();
                sortie.write("    <p>Cité dans</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(fausse.getCiteur().getNumerosCompletsSousPassages(), citeur.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(fausse.getCiteur()).getTexte()+"</p>"); sortie.newLine();
            }
        } else {
            sortie.write("    <h3>Liste de 100 citations trouvées qui concurrencent une citation de la liste à trouver</h3>"); sortie.newLine();
            sortie.write("    <a href=\"#brut\">Retour</a>"); sortie.newLine();
            for(nb=0; nb<100; nb++) {
                int i = (int)(Math.random()*surnumeraires.length);
                Citation fausse = surnumeraires[i];
                sortie.write("    <h4>Citation " + i + "</h4>"); sortie.newLine();
                sortie.write("    <p>Passage</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(fausse.getCite().getNumerosCompletsSousPassages(), source.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(fausse.getCite()).getTexte()+"</p>"); sortie.newLine();
                sortie.write("    <p>Cité dans</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(fausse.getCiteur().getNumerosCompletsSousPassages(), citeur.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(fausse.getCiteur()).getTexte()+"</p>"); sortie.newLine();
            }
        }
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (14/15)");
        Citation[] inedites = ComparateurCitations.getCitationsInedites(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT);
        if(inedites.length<=100) {
            sortie.write("    <a name=\"surnumeraires\"><h3>Liste des "+inedites.length+" citations trouvées qui n'ont rien à voir avec des citations à trouver</h3></a>"); sortie.newLine();
            sortie.write("    <a href=\"#brut\">Retour</a>"); sortie.newLine();
            nb = 0;
            for(Citation fausse: inedites) {
                nb++;
                sortie.write("    <h4>Citation " + nb + "</h4>"); sortie.newLine();
                sortie.write("    <p>Passage</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(fausse.getCite().getNumerosCompletsSousPassages(), source.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(fausse.getCite()).getTexte()+"</p>"); sortie.newLine();
                sortie.write("    <p>Cité dans</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(fausse.getCiteur().getNumerosCompletsSousPassages(), citeur.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(fausse.getCiteur()).getTexte()+"</p>"); sortie.newLine();
            }
        } else {
            sortie.write("    <h3>Liste de 100 citations trouvées qui n'ont rien à voir avec des citations à trouver</h3>"); sortie.newLine();
            sortie.write("    <a href=\"#brut\">Retour</a>"); sortie.newLine();
            for(nb=0; nb<100; nb++) {
                int i = (int)(Math.random()*inedites.length);
                Citation fausse = inedites[i];
                sortie.write("    <h4>Citation " + i + "</h4>"); sortie.newLine();
                sortie.write("    <p>Passage</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(fausse.getCite().getNumerosCompletsSousPassages(), source.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(fausse.getCite()).getTexte()+"</p>"); sortie.newLine();
                sortie.write("    <p>Cité dans</p>"); sortie.newLine();
                sortie.write("    <p class=\"reference\">"+References.minimiseRefs(fausse.getCiteur().getNumerosCompletsSousPassages(), citeur.getType()) +"</p>"); sortie.newLine();
                sortie.write("    <p class=\"citation\">"+new Texte(fausse.getCiteur()).getTexte()+"</p>"); sortie.newLine();
            }
        }*/
        tps = System.currentTimeMillis() - tps;
        sortie.flush();
        cStatut.setText("Analyse des résultats en cours (10/10)");
        cRapport.setText(cRapport.getText()+"L'analyse a pris " + Temps.getTexte(tps) + ".\n\n");
    }
    
    private void ecritureTexte(BufferedWriter sortie, Citation[] resultat, int traitement) throws IOException {
        // texte citant : citeur (Passage)
        // citations témoin : temoin (Citation[])
        // citations trouvees : resultat (Citation[])
        
        ecritureTexteRecursif(sortie, citeur, resultat, true, traitement);
        sortie.flush();
    }
    
    private void ecritureTexteRecursif(BufferedWriter sortie, PassageType aEcrire, Citation[] resultat, boolean initialisation, int traitement) throws IOException {
        if(aEcrire.getPassage().estUnMot()) {
            Citation[] temoins = getCitants(aEcrire.getPassage(), temoin);
            Citation[] trouves = getCitants(aEcrire.getPassage(), resultat);
            String infoBulle = "";
            int nbCitants = 0;
            if(temoins.length>0) {
                nbCitants++;
                infoBulle += "A trouver : ";
                for(int i=0; i<temoins.length; i++) {
                    Citation c = temoins[i];
                    infoBulle += References.minimiseRefs(c.getCite().getNumerosCompletsSousPassages(), source.getType());
                    if(i < temoins.length - 1) {
                        infoBulle += ", ";
                    }
                }
            }
            if(trouves.length>0) {
                if(temoins.length>0) {
                    infoBulle += ". ";
                }
                nbCitants+=2;
                infoBulle += "Trouvé : ";
                for(int i=0; i<trouves.length; i++) {
                    Citation c = trouves[i];
                    infoBulle += References.minimiseRefs(c.getCite().getNumerosCompletsSousPassages(), source.getType());
                    if(i < trouves.length - 1) {
                        infoBulle += ", ";
                    }
                }
            }
            Terme terme = aEcrire.getPassage().getContenu();
            if(traitement == N) {
                terme = terme.getFormeNormale();
            } else if(traitement == L) {
                terme = terme.getLemmePrincipal();
            }
            sortie.write("    <span class=\"citation" + nbCitants + "\""+" title=\""+infoBulle+"\">" + terme.getExpression() + " </span>");
        } else {
            //System.err.println(aEcrire.getPassage().getNumerosCompletsSousPassages());
            if(initialisation) {
                sortie.write("    <p class=\"titre\">" + aEcrire.getPassage().getTitre()); sortie.newLine();
            } else if(aEcrire.getPassage().getNbSousPassages() == 0) {
                sortie.newLine();
                sortie.write("    </p>"); sortie.newLine();
                sortie.write("    <p class=\"ligne\"></p>"); sortie.newLine();
            } else if(aEcrire.getPassage().getSousCoordonneeMinimale().getSysteme().equals(OrdreStrict.getInstance(OrdreStrict.VERSETS))) {
                sortie.newLine();
                sortie.write("    </p>"); sortie.newLine();
                sortie.write("    <p class=\"titre\">" + aEcrire.getPassage().getReferenceInitialeDans(citeur.getPassage())); sortie.newLine();
            } else if(aEcrire.getPassage().getSousCoordonneeMinimale().getSysteme().equals(OrdreStrict.getInstance(OrdreStrict.LIGNES))) {
                sortie.newLine();
                sortie.write("    </p>"); sortie.newLine();
                sortie.write("    <p class=\"titre\">" + aEcrire.getPassage().getReferenceInitialeDans(citeur.getPassage())); sortie.newLine();
            } else if(aEcrire.getPassage().getSousCoordonneeMinimale().getSysteme().equals(OrdreStrict.getInstance(OrdreStrict.MOTS))) {
                sortie.newLine();
                sortie.write("    </p>"); sortie.newLine();
                sortie.write("    <p class=\"ligne\">"); sortie.newLine();
            }
            for(Passage sousPassage : aEcrire.getPassage().getSousPassages()) {
                ecritureTexteRecursif(sortie, new PassageType(sousPassage, aEcrire.getType()), resultat, false, traitement);
            }
        }
        sortie.flush();
    }
    
    private Citation[] getCitants(Passage mot, Citation[] listePotentielle) {
        ArrayList<Citation> resultat = new ArrayList<>();
        for(Citation potentielle : listePotentielle) {
            if(potentielle.getCiteur().contientMot(mot)) {
                resultat.add(potentielle);
            }
        }
        Citation[] res = new Citation[resultat.size()];
        resultat.toArray(res);
        return res;
    }
    
    private void ecritureResultatsExperiences() throws IOException {
        long tps = System.currentTimeMillis();
        
        cStatut.setText("Rédaction des fichiers d'expérience (0%)");
        
        // 1. Ecriture des références trouvées et non trouvées
        File fSortie = new File(sortieHTML + "_ATrouver.txt");
        BufferedWriter sortie = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        
        // 1.1. Ligne de titre
        sortie.write("référence citante\tréférence citée\t"
                + "texte citant\ttexte cité\t"
                + "texte citant étendu\ttexte cité étendu\t"
                + "termes communs\tnb\ttermes normalisés communs\tnb\tlemmes communs\tnb\t"
                + "plus grande suite de termes commune\tnb\tplus grande suite de termes normalisés commune\tnb\tplus grande suite de lemmes commune\tnb\t");
        for(int i=0; i<resultatsExperiences.size(); i++) {
            sortie.write("xp " + (i+1));
            sortie.write("\t");
        }
        for(int i=0; i<resultatsExperiences.size(); i++) {
            sortie.write("texte citant " + (i+1));
            sortie.write("\t");
        }
        for(int i=0; i<resultatsExperiences.size(); i++) {
            sortie.write("texte cité " + (i+1));
            if(i<resultatsExperiences.size()-1) {
                sortie.write("\t");
            } else {
                sortie.newLine();
            }
        }
        
        // 1.2. Récupération des citations trouvées par expérience
        ArrayList<boolean[]> spectres = new ArrayList<>();
        ArrayList<Passage[]> trouveSrc = new ArrayList<>();
        ArrayList<Passage[]> trouveCit = new ArrayList<>();
        for(Citation[] resultat: resultatsExperiences) {
            spectres.add(ComparateurCitations.getSpectre(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT, ComparateurCitations.METRIQUES_EPUREES));
            trouveSrc.add(ComparateurCitations.getSources(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT));
            trouveCit.add(ComparateurCitations.getCitants(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT));
        }
        
        // 1.3. Ecriture
        for(int i=0; i<temoin.length; i++) {
            cStatut.setText("Rédaction des fichiers d'expérience ("+(0+((int)(1500*i/temoin.length))/100.0)+"%)");
            Citation aTrouver = temoin[i];
            
            // 1.3.1. Calcul des références
            String txtRefSourceAT = "";
            Passage src = aTrouver.getCite();
            if(src.getNbSousPassages() > 0) {
                Passage src0 = src.getSousPassages()[0];
                Passage src1 = src.getSousPassages()[src.getNbSousPassages()-1];
                txtRefSourceAT = References.toReference(
                        src0.getReferenceInitialeDans(source.getPassage()), 
                        src1.getReferenceInitialeDans(source.getPassage()), 
                        source.getType());
            }
            Passage[] motsSrc = new Passage[src.getAllMots().length+20];
            System.arraycopy(src.getAllMots(), 0, motsSrc, 10, src.getAllMots().length);
            for(int m=9; m>=0; m--) {
                motsSrc[m] = source.getPassage().getMotPrecedent(motsSrc[m+1]);
            }
            for(int m=motsSrc.length-10; m<motsSrc.length; m++) {
                motsSrc[m] = source.getPassage().getMotSuivant(motsSrc[m-1]);
            }
            for(int m=0; m<motsSrc.length; m++) {
                if(motsSrc[m] == null) {
                    motsSrc[m] = Passage.creeMot(new Passage[]{}, Terme.cree(""));
                }
            }
            //System.err.println(Arrays.toString(motsSrc));
            Passage srcEtendu = Passage.creeCollectionDePassages(motsSrc);
            
            String txtRefCiteurAT = "";
            Passage cit = aTrouver.getCiteur();
            if(cit.getNbSousPassages() > 0) {
                //System.err.println(cit.getNbSousPassages() + " : " + cit.getTexteCompletAvecRefs());
                Passage cit0 = cit.getSousPassages()[0];
                Passage cit1 = cit.getSousPassages()[cit.getNbSousPassages()-1];
                txtRefCiteurAT = References.toReference(
                        cit0.getReferenceInitialeDans(citeur.getPassage()), 
                        cit1.getReferenceInitialeDans(citeur.getPassage()), 
                        citeur.getType());
            }
            Passage[] motsCit = new Passage[cit.getAllMots().length+20];
            System.arraycopy(cit.getAllMots(), 0, motsCit, 10, cit.getAllMots().length);
            for(int m=9; m>=0; m--) {
                motsCit[m] = citeur.getPassage().getMotPrecedent(motsCit[m+1]);
            }
            for(int m=motsCit.length-10; m<motsCit.length; m++) {
                motsCit[m] = citeur.getPassage().getMotSuivant(motsCit[m-1]);
            }
            for(int m=0; m<motsCit.length; m++) {
                if(motsCit[m] == null) {
                    motsCit[m] = Passage.creeMot(new Passage[]{}, Terme.cree(""));
                }
            }
            //System.err.println(Arrays.toString(motsCit));
            Passage citEtendu = Passage.creeCollectionDePassages(motsCit);
            
            // 1.3.2. Ecriture
            String txt = "";
            txt += txtRefCiteurAT+"\t";
            txt += txtRefSourceAT+"\t";
            txt += new Texte(aTrouver.getCiteur()).getTexte()+"\t";
            txt += new Texte(aTrouver.getCite()).getTexte()+"\t";
            //System.err.println(srcEtendu.toString());
            txt += new Texte(srcEtendu).getTexte()+"\t";
            //System.err.println(citEtendu.toString());
            txt += new Texte(citEtendu).getTexte()+"\t";
            txt += liste(aTrouver.getTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS))+"\t";
            txt += aTrouver.getNbTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS)+"\t";
            txt += liste(aTrouver.getTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS))+"\t";
            txt += aTrouver.getNbTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS)+"\t";
            txt += liste(aTrouver.getTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS))+"\t";
            txt += aTrouver.getNbTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS)+"\t";
            txt += liste(aTrouver.getPlusGrandeChaineDeMotsCommune(Citation.MODE_NORMAL, false))+"\t";
            txt += aTrouver.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMAL, false)+"\t";
            txt += liste(aTrouver.getPlusGrandeChaineDeMotsCommune(Citation.MODE_NORMALISE, false))+"\t";
            txt += aTrouver.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMALISE, false)+"\t";
            txt += liste(aTrouver.getPlusGrandeChaineDeMotsCommune(Citation.MODE_LEMMATISE, false))+"\t";
            txt += aTrouver.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_LEMMATISE, false)+"\t";
            
            sortie.write(txt);
            for(int j=0; j<spectres.size(); j++) {
                boolean trouvee = spectres.get(j)[i];
                sortie.write(trouvee?"X":"");
                sortie.write("\t");
            }
            for(int j=0; j<trouveCit.size(); j++) {
                boolean trouvee = spectres.get(j)[i];
                if(trouvee) {
                    sortie.write(new Texte(trouveCit.get(j)[i]).getTexte());
                }
                sortie.write("\t");
            }
            for(int j=0; j<trouveSrc.size(); j++) {
                boolean trouvee = spectres.get(j)[i];
                if(trouvee) {
                    sortie.write(new Texte(trouveSrc.get(j)[i]).getTexte());
                }
                if(j<trouveSrc.size()-1) {
                    sortie.write("\t");
                } else {
                    sortie.newLine();
                }
            }
            sortie.flush();
        }
        
        cStatut.setText("Rédaction des fichiers d'expérience (15%)");
        
        
        // 2. Ecriture des références surnuméraires
        fSortie = new File(sortieHTML + "_Surnuméraires.txt");
        sortie = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        
        
        // 2.1. Ligne de titre
        sortie.write("référence citante\tréférence citée\tréférence citée attendue\t"
                + "texte citant\ttexte cité\ttexte cité attendu\t");
        for(int i=0; i<resultatsExperiences.size(); i++) {
            sortie.write("xp " + (i+1));
            if(i<resultatsExperiences.size()-1) {
                sortie.write("\t");
            } else {
                sortie.newLine();
            }
        }
        
        // 2.2. Récupération des références surnuméraires par expérience
        // 2.2.1. Récupération
        ArrayList<Map<Citation,Citation>> surnumeraires = new ArrayList<>();
        for(Citation[] resultat: resultatsExperiences) {
            surnumeraires.add(ComparateurCitations.getCitationsSurnumerairesEtAttendues(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT));
        }
        Map<String, Citation> indexSurnumeraires = new HashMap<>();
        Map<String, boolean[]> indexSurnumeraires2 = new HashMap<>();
        Map<String, Citation> indexSurnumeraires3 = new HashMap<>();
        
        // 2.2.2. Tri
        for(int i=0; i<surnumeraires.size(); i++) {
            cStatut.setText("Rédaction des fichiers d'expérience ("+(15+((int)(1000*i/surnumeraires.size()))/100.0)+"%)");
            ArrayList<Citation> s = new ArrayList<>(surnumeraires.get(i).keySet());
            for(int j=0; j<s.size(); j++) {
                Citation surnumeraire = s.get(j);

                // 2.2.2.1. Calcul des références
                Passage src = surnumeraire.getCite();
                Passage src0 = src.getSousPassages()[0];
                Passage src1 = src.getSousPassages()[src.getNbSousPassages()-1];
                String txtRefSource = References.toReference(
                        src0.getReferenceInitialeDans(source.getPassage()), 
                        src1.getReferenceInitialeDans(source.getPassage()),
                        source.getType());

                Passage cit = surnumeraire.getCiteur();
                Passage cit0 = cit.getSousPassages()[0];
                Passage cit1 = cit.getSousPassages()[cit.getNbSousPassages()-1];
                String txtRefCiteur = References.toReference(
                        cit0.getReferenceInitialeDans(citeur.getPassage()), 
                        cit1.getReferenceInitialeDans(citeur.getPassage()),
                        citeur.getType());
                
                // 2.2.2.2. Indexation
                String cle = txtRefSource+"¤"+txtRefCiteur;
                if(!indexSurnumeraires.containsKey(cle)) {
                    indexSurnumeraires.put(cle, surnumeraire);
                    boolean[] b = new boolean[surnumeraires.size()];
                    for(int a=0; a<b.length; a++) {
                        b[a] = false;
                    }
                    indexSurnumeraires2.put(cle, b);
                    indexSurnumeraires3.put(cle, surnumeraires.get(i).get(surnumeraire));
                }
                indexSurnumeraires2.get(cle)[i] = true;
            }
        }
        
        // 2.3. Ecriture
        ArrayList<String> is = new ArrayList<>(indexSurnumeraires.keySet());
        for(int n=0; n<is.size(); n++) {
            cStatut.setText("Rédaction des fichiers d'expérience ("+(25+((int)(2500*n/is.size()))/100.0)+"%)");
            String surnumeraire = is.get(n);
            
            //System.err.println("Demande : "+surnumeraire);
            // 2.3.1. Calcul des références
            String txtRefSource = surnumeraire.split("¤")[0];
            String txtRefCiteur = surnumeraire.split("¤")[1];
            Passage att = indexSurnumeraires3.get(surnumeraire).getCite();
            Passage att0 = att.getSousPassages()[0];
            Passage att1 = att.getSousPassages()[att.getNbSousPassages()-1];
            String txtRefAttendue = References.toReference(
                    att0.getReferenceInitialeDans(source.getPassage()), 
                    att1.getReferenceInitialeDans(source.getPassage()),
                    source.getType());
            
            // 2.3.2. Ecriture
            sortie.write(txtRefCiteur+"\t"+txtRefSource+"\t"+txtRefAttendue+"\t"
                    +new Texte(indexSurnumeraires.get(surnumeraire).getCiteur()).getTexte()+"\t"
                    +new Texte(indexSurnumeraires.get(surnumeraire).getCite()).getTexte()+"\t"
                    +new Texte(indexSurnumeraires3.get(surnumeraire).getCite()).getTexte()+"\t"
                    );
            for(int j=0; j<indexSurnumeraires2.get(surnumeraire).length; j++) {
                boolean trouvee = indexSurnumeraires2.get(surnumeraire)[j];
                sortie.write(trouvee?"X":"");
                if(j<indexSurnumeraires2.get(surnumeraire).length-1) {
                    sortie.write("\t");
                } else {
                    sortie.newLine();
                }
            }
            sortie.flush();
        }
        
        
        // 3. Ecriture des références inédites
        fSortie = new File(sortieHTML + "Inédites.txt");
        sortie = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        
        // 3.1. Ligne de titre
        sortie.write("référence citante\tréférence citée\t"
                + "texte citant\ttexte cité\t");
        for(int i=0; i<resultatsExperiences.size(); i++) {
            sortie.write("xp " + (i+1));
            if(i<resultatsExperiences.size()-1) {
                sortie.write("\t");
            } else {
                sortie.newLine();
            }
        }
        
        // 3.2. Récupération des références surnuméraires par expérience
        // 3.2.1. Récupération
        ArrayList<Citation[]> inedites = new ArrayList<>();
        for(Citation[] resultat: resultatsExperiences) {
            inedites.add(ComparateurCitations.getCitationsInedites(resultat, temoin, ComparateurCitations.IDENTIQUE_SI_CHEVAUCHEMENT));
        }
        Map<String, Citation> indexInedites = new HashMap<>();
        Map<String, boolean[]> indexInedites2 = new HashMap<>();
        
        // 3.2.2. Tri
        for(int i=0; i<inedites.size(); i++) {
            cStatut.setText("Rédaction des fichiers d'expérience ("+(50+((int)(1500*i/inedites.size()))/100.0)+"%)");
            for(int j=0; j<inedites.get(i).length; j++) {
                Citation aTrouver = inedites.get(i)[j];

                // 3.2.2.1. Calcul des références
                Passage src = aTrouver.getCite();
                Passage src0 = src.getSousPassages()[0];
                Passage src1 = src.getSousPassages()[src.getNbSousPassages()-1];
                String txtRefSource = References.toReference(
                        src0.getReferenceInitialeDans(source.getPassage()), 
                        src1.getReferenceInitialeDans(source.getPassage()),
                        source.getType());

                Passage cit = aTrouver.getCiteur();
                Passage cit0 = cit.getSousPassages()[0];
                Passage cit1 = cit.getSousPassages()[cit.getNbSousPassages()-1];
                String txtRefCiteur = References.toReference(
                        cit0.getReferenceInitialeDans(citeur.getPassage()), 
                        cit1.getReferenceInitialeDans(citeur.getPassage()),
                        citeur.getType());
                
                // 3.2.2.2. Indexation
                String cle = txtRefSource+"¤"+txtRefCiteur;
                if(!indexInedites.containsKey(cle)) {
                    indexInedites.put(cle, aTrouver);
                    boolean[] b = new boolean[inedites.size()];
                    for(int a=0; a<b.length; a++) {
                        b[a] = false;
                    }
                    indexInedites2.put(cle, b);
                }
                indexInedites2.get(cle)[i] = true;
            }
        }
        
        // 3.3. Ecriture
        ArrayList<String> ii = new ArrayList<>(indexInedites.keySet());
        for(int n=0; n<ii.size(); n++) {
            cStatut.setText("Rédaction des fichiers d'expérience ("+(65+((int)(3500*n/is.size()))/100.0)+"%)");
            String inedite = ii.get(n);
            
            // 3.3.1. Calcul des références
            String txtRefSource = inedite.split("¤")[0];
            String txtRefCiteur = inedite.split("¤")[1];
            
            // 3.3.2. Ecriture
            sortie.write(txtRefCiteur+"\t"+txtRefSource+"\t"
                    +new Texte(indexInedites.get(inedite).getCiteur()).getTexte()+"\t"
                    +new Texte(indexInedites.get(inedite).getCite()).getTexte()+"\t"
                    );
            for(int j=0; j<indexInedites2.get(inedite).length; j++) {
                boolean trouvee = indexInedites2.get(inedite)[j];
                sortie.write(trouvee?"X":"");
                if(j<indexInedites2.get(inedite).length-1) {
                    sortie.write("\t");
                } else {
                    sortie.newLine();
                }
            }
            sortie.flush();
        }
        
        tps = System.currentTimeMillis() - tps;
        cRapport.setText(cRapport.getText()+"La rédaction des tableaux de résultat a pris " + Temps.getTexte(tps) + ".\n\n");
        
        cStatut.setText("Terminé.");
    }
    
    private void ecritureAnalyseLemmatisation() throws IOException {
        
        cStatut.setText("Rédaction des statistiques de lemmatisation");
        
        int sc = Lemmatiseurs.listeFormesTrouveesPar(Lemmatiseurs.SOURCESCHRETIENNES).length;
        int bw = Lemmatiseurs.listeFormesTrouveesPar(Lemmatiseurs.BIBLEWORKS).length;
        int p = Lemmatiseurs.listeFormesTrouveesPar(Lemmatiseurs.PERSEUS).length;
        int scu = Lemmatiseurs.listeFormesSpecifiquesA(Lemmatiseurs.SOURCESCHRETIENNES).length;
        int bwu = Lemmatiseurs.listeFormesSpecifiquesA(Lemmatiseurs.BIBLEWORKS).length;
        int pu = Lemmatiseurs.listeFormesSpecifiquesA(Lemmatiseurs.PERSEUS).length;
        int c = Lemmatiseurs.listeFormesTrouveesParTous().length;
        int t = Lemmatiseurs.listeFormesDemandeesAvecLemme().length;
        int nl = Lemmatiseurs.listeFormesDemandeesSansLemme().length;
        int tt = Lemmatiseurs.listeFormesDemandees().length;
        cRapport.setText(cRapport.getText() + 
                sc + " formes lemmatisées par Sources Chrétiennes\n" +
                bw + " formes lemmatisées par Bibleworks\n" +
                p + " formes lemmatisées par Perseus\n" +
                scu + " formes connues de Sources Chrétiennes uniquement\n" +
                bwu + " formes connues de BibleWorks uniquement\n" +
                pu + " formes connues de Perseus uniquement\n" +
                c + " formes connues de tous\n" +
                t + " formes lemmatisées au total\n" +
                nl + " formes non lemmatisées faute de lemme\n" +
                tt + " formes demandées au total\n" +
                "Le taux de lemmatisation est de " + 
                (int)(100.0 * t / tt) + "% sur l'ensemble du corpus");
        
        File fSortie = new File(sortieHTML + "_Formes.txt");
        BufferedWriter sortie = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        
        for(String s: Lemmatiseurs.listeFormesDemandees()) {
            cStatut.setText("Rédaction des statistiques de lemmatisation");
            sortie.write(s + "\t" + ((Perseus.getInstance().getFormesLemmatisees(s).length > 0)?"1":"") + "\t" + 
                    ((BibleWorks.getInstance().getFormesLemmatisees(s).length > 0)?"1":"") + "\t" + 
                    ((SourcesChretiennes.getInstance().getFormesLemmatisees(s).length > 0)?"1":"") + "\n");
            sortie.flush();
        }
        cStatut.setText("Terminé.");
    }
    
    private String liste(Terme[] termes) {
        String res = "";
        for(Terme t: termes) {
            res += t.getExpression()+" ";
        }
        return res.trim();
    }
    
    private void ecritureResultatsAudits() throws IOException {
        long tps = System.currentTimeMillis();
        
        cStatut.setText("Rédaction des fichiers d'audit (0%)");
        
        File fSortie = new File(sortieHTML + "_Legendes.txt");
        BufferedWriter sortie = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        for(int i=0; i<legendesAudits.size(); i++) {
            sortie.write(legendesAudits.get(i) + "\n");
        }
        sortie.flush();
        sortie.close();

        fSortie = new File(sortieHTML + "_NbSimilaritesParTerme.txt");
        sortie = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fSortie), "UTF-8"));
        
        int tailleMaxCluster = 0;
        for(Map<Integer, Integer> a: resultatsAuditsClusters) {
            for(Integer taille: a.keySet()) {
                if(taille > tailleMaxCluster) {
                    tailleMaxCluster = taille;
                }
            }
        }
        
        cStatut.setText("Rédaction des fichiers d'audit (1%)");
        
        sortie.write("Nombre de termes similaires");
        for(int i=0; i<resultatsAuditsClusters.size(); i++) {
            sortie.write("\taudit " + (i+1));
        }
        sortie.write("\n");
        sortie.flush();
        
        for(int t=1; t<tailleMaxCluster; t++) {
            cStatut.setText("Rédaction des fichiers d'audit ("+((int)(100.0*t/tailleMaxCluster))+"%)");
            sortie.write(""+t);
            for(int i=0; i<resultatsAuditsClusters.size(); i++) {
                Integer nb = resultatsAuditsClusters.get(i).get(t);
                if(nb == null) {
                    nb = 0;
                }
                sortie.write("\t" + nb);
            }
            sortie.write("\n");
        }
        sortie.flush();
        sortie.close();
        
        tps = System.currentTimeMillis() - tps;
        cRapport.setText(cRapport.getText()+"La rédaction des tableaux d'audit a pris " + 
                Temps.getTexte(tps) + ".\n\n");
        cStatut.setText("Terminé.");
    }
    
    private void ecritureAnalyseCitations() throws IOException {
        long tps = System.currentTimeMillis();
        
        cStatut.setText("Analyse des citations-témoin");
        
        cRapport.setText(cRapport.getText()+"Répartition des citations par quantités en commun\n\n");
        
        cRapport.setText(cRapport.getText()+"Termes vides, usuels, récurrents ou significatifs en commun\n");
        cRapport.setText(cRapport.getText()+"(termes bruts, termes normalisés puis lemmes)\n\n");
        int[] cb_vurs = new int[10];
        int[] cn_vurs = new int[10];
        int[] cl_vurs = new int[10];
        for(Citation c: temoin) {
            int b = c.getNbTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS);
            for(int i=0; i<cb_vurs.length && i<=b; i++) {
                cb_vurs[i]++;
            }
            int n = c.getNbTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS);
            for(int i=0; i<cn_vurs.length && i<=n; i++) {
                cn_vurs[i]++;
            }
            int l = c.getNbTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS);
            for(int i=0; i<cl_vurs.length && i<=l; i++) {
                cl_vurs[i]++;
            }
        }
        for(int i=0; i<10; i++) {
            cRapport.setText(cRapport.getText() + i + "\t" + cb_vurs[i] + "\t" + cn_vurs[i] + "\t" + cl_vurs[i] + "\n");
        }
        
        cRapport.setText(cRapport.getText()+"\nTermes usuels, récurrents ou significatifs en commun\n");
        cRapport.setText(cRapport.getText()+"(termes bruts, termes normalisés puis lemmes)\n\n");
        int[] cb_urs = new int[10];
        int[] cn_urs = new int[10];
        int[] cl_urs = new int[10];
        for(Citation c: temoin) {
            int b = c.getNbTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS);
            for(int i=0; i<cb_urs.length && i<=b; i++) {
                cb_urs[i]++;
            }
            int n = c.getNbTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS);
            for(int i=0; i<cn_urs.length && i<=n; i++) {
                cn_urs[i]++;
            }
            int l = c.getNbTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS);
            for(int i=0; i<cl_urs.length && i<=l; i++) {
                cl_urs[i]++;
            }
        }
        for(int i=0; i<10; i++) {
            cRapport.setText(cRapport.getText() + i + "\t" + cb_urs[i] + "\t" + cn_urs[i] + "\t" + cl_urs[i] + "\n");
        }
        
        cRapport.setText(cRapport.getText()+"\nTermes récurrents ou significatifs en commun\n");
        cRapport.setText(cRapport.getText()+"(termes bruts, termes normalisés puis lemmes)\n\n");
        int[] cb_rs = new int[10];
        int[] cn_rs = new int[10];
        int[] cl_rs = new int[10];
        for(Citation c: temoin) {
            int b = c.getNbTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_RECURRENTS_SIGNIFICATIFS);
            for(int i=0; i<cb_rs.length && i<=b; i++) {
                cb_rs[i]++;
            }
            int n = c.getNbTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_RECURRENTS_SIGNIFICATIFS);
            for(int i=0; i<cn_rs.length && i<=n; i++) {
                cn_rs[i]++;
            }
            int l = c.getNbTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_RECURRENTS_SIGNIFICATIFS);
            for(int i=0; i<cl_rs.length && i<=l; i++) {
                cl_rs[i]++;
            }
        }
        for(int i=0; i<10; i++) {
            cRapport.setText(cRapport.getText() + i + "\t" + cb_rs[i] + "\t" + cn_rs[i] + "\t" + cl_rs[i] + "\n");
        }
        
        cRapport.setText(cRapport.getText()+"\nTermes significatifs en commun\n");
        cRapport.setText(cRapport.getText()+"(termes bruts, termes normalisés puis lemmes)\n\n");
        int[] cb_s = new int[10];
        int[] cn_s = new int[10];
        int[] cl_s = new int[10];
        for(Citation c: temoin) {
            int b = c.getNbTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_SIGNIFICATIFS);
            for(int i=0; i<cb_s.length && i<=b; i++) {
                cb_s[i]++;
            }
            int n = c.getNbTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_SIGNIFICATIFS);
            for(int i=0; i<cn_s.length && i<=n; i++) {
                cn_s[i]++;
            }
            int l = c.getNbTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_SIGNIFICATIFS);
            for(int i=0; i<cl_s.length && i<=l; i++) {
                cl_s[i]++;
            }
        }
        for(int i=0; i<10; i++) {
            cRapport.setText(cRapport.getText() + i + "\t" + cb_s[i] + "\t" + cn_s[i] + "\t" + cl_s[i] + "\n");
        }
        
        int[] ccb = new int[10];
        int[] ccn = new int[10];
        int[] ccl = new int[10];
        for(Citation c: temoin) {
            int b = c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMAL, false);
            for(int i=0; i<ccb.length && i<=b; i++) {
                ccb[i]++;
            }
            int n = c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMALISE, false);
            for(int i=0; i<ccn.length && i<=n; i++) {
                ccn[i]++;
            }
            int l = c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_LEMMATISE, false);
            for(int i=0; i<ccl.length && i<=l; i++) {
                ccl[i]++;
            }
        }
        
        
        cRapport.setText(cRapport.getText()+"\n\nDétail des citations par quantités en commun\n");
        cRapport.setText(cRapport.getText()+"(termes, chaîne max de termes) * (brut, norm, lem) * (vurs, urs, rs, s)\n\n");
        
        StringBuilder s = new StringBuilder();
        
        for(int i=0; i<temoin.length; i++) {
            Citation c = temoin[i];
            s.append(i+1).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_NORMAL, Citation.TOLERANCE_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_NORMALISE, Citation.TOLERANCE_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getNbTermesCommuns(Citation.MODE_LEMMATISE, Citation.TOLERANCE_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMAL, Citation.TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMAL, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMAL, Citation.TOLERANCE_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMAL, Citation.TOLERANCE_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMALISE, Citation.TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMALISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMALISE, Citation.TOLERANCE_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_NORMALISE, Citation.TOLERANCE_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_LEMMATISE, Citation.TOLERANCE_VIDES_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_LEMMATISE, Citation.TOLERANCE_USUELS_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_LEMMATISE, Citation.TOLERANCE_RECURRENTS_SIGNIFICATIFS)).append("\t");
            s.append(c.getTaillePlusGrandeChaineDeMotsCommune(Citation.MODE_LEMMATISE, Citation.TOLERANCE_SIGNIFICATIFS)).append("\n");
        }
        cRapport.setText(cRapport.getText() + s);
        
        tps = System.currentTimeMillis() - tps;
        cRapport.setText(cRapport.getText()+"\n\nLa rédaction des statistiques de citation a pris " + 
                Temps.getTexte(tps) + ".\n\n");
        cStatut.setText("Terminé.");
    }
}
