/*
 * $Id: LuceneClassifier.java,v 1.41 2006/10/27 08:50:50 lawern Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: LuceneClassifier.java,v $
 * Revision 1.41  2006/10/27 08:50:50  lawern
 * WRITABLE-Views eingefügt
 *
 * Revision 1.40  2006/10/04 11:31:49  lawern
 * Keine Indexoptimierung während der Optimierung der Klassifikationsparameter.
 *
 * Revision 1.39  2006/07/17 13:04:01  lawern
 * Neuer typografiegewichteter Index und k-NN-Klassifikation aus meiner Diss.
 *
 * Revision 1.38  2006/06/08 07:29:10  lawern
 * Formatierung nach den Java Code Conventions
 *
 * Revision 1.37  2006/05/27 18:34:47  marbobo
 * + Neueste Lucene-Version eingebaut
 *
 * Revision 1.36  2006/03/23 11:40:56  marbobo
 * + Mögliche NPE abgefangen
 *
 * Revision 1.35  2005/08/23 12:41:13  lawern
 * Optimieren des Index nur noch, wenn das OPTIMIZATION_INTERVAL abgelaufen ist.
 *
 * Revision 1.34  2005/06/14 10:37:49  marbobo
 * + MAX_FIELD_LENGTH auf maximum gesetzt
 * + refactoring
 *
 * Revision 1.33  2005/04/11 14:57:40  jonyp
 * + Einige Exception abgefangen
 *
 * Revision 1.32  2005/04/05 22:13:05  marbobo
 * + "Suchindex neu aufbauen" korrigiert
 *
 * Revision 1.31  2004/11/10 15:02:53  koppy
 * Projektweite Überarbeitung von Error-Logs, Stacktraces,..
 *
 * Revision 1.30  2004/10/08 13:56:33  koppy
 * +classifyByCategoryKeywords
 *
 * Revision 1.29  2004/06/22 13:20:20  lawern
 * Classify auf das Interface Classifier umgestellt.
 *
 * Revision 1.27  2004/03/17 13:18:38  lawern
 * searchCategory nicht mehr synchronized
 *
 * Revision 1.26  2004/03/15 12:02:03  lawern
 * getAllCategories zu getAllCategoryIds umgebaut.
 *
 * Revision 1.25  2004/03/13 20:46:16  bigmac
 * Weitere Refactorings ...
 *
 * Revision 1.24  2004/03/07 13:45:47  bigmac
 * Anpassungen an VKCDocumentTypeSerializer, VKCDocumentClassSerializer, VKCDocumentStatusSerializer
 *
 * Revision 1.23  2004/02/24 08:54:41  bigmac
 * zusätzliches Feld "keywords" in createDocument() eingefügt
 *
 * Revision 1.22  2004/02/21 11:56:10  bigmac
 * Parameter zur Begrenzung der Anzahl von Klassifikations-Hits eingefügt
 *
 * Revision 1.21  2003/12/24 10:12:13  bigmac
 * Projektweite Import-Optimierung
 *
 * Revision 1.20  2003/09/19 10:48:08  lawern
 * Genauigkeit verbessert
 *
 * Revision 1.19  2003/09/18 14:04:11  lawern
 * überarbeitete Version
 *
 * Revision 1.18  2003/09/17 11:36:23  lawern
 * Debug-Ausgaben hinzugefügt.
 *
 * Revision 1.17  2003/08/05 20:24:24  bigmac
 * *** empty log message ***
 *
 * Revision 1.16  2003/08/02 17:00:04  bigmac
 * *** empty log message ***
 *
 * Revision 1.15  2003/07/31 08:04:03  lawern
 * internal-Flag der VKCKeywords wird jetzt verwendet und gesetzt
 *
 * Revision 1.14  2003/07/30 12:00:27  jonyp
 * *** empty log message ***
 *
 * Revision 1.13  2003/06/12 15:53:45  koppy
 * Klassifizierung ab hier
 *
 * Revision 1.12  2003/06/10 14:51:33  lawern
 * Debug-Ausgaben entfernt
 *
 * Revision 1.11  2003/06/10 13:44:54  lawern
 * classify zeigt jetzt nur noch die erlaubten Kategorien an
 *
 * Revision 1.10  2003/06/04 12:40:28  koppy
 * Parameterliste von classify(...)  um VKCUser erweitert
 *
 * Revision 1.9  2003/06/03 14:20:26  lawern
 * Anpassung an neue getKeywords(boolean)-Methode
 *
 * Revision 1.8  2003/05/27 07:51:22  lawern
 * überarbeitete Version
 *
 * Revision 1.7  2003/04/23 14:55:33  lawern
 * Ergänzungen für Volltextverarbeitung
 *
 * Revision 1.6  2003/04/16 13:01:44  koppy
 * bugfix
 *
 * Revision 1.5  2003/04/16 11:36:30  lawern
 * Bugs behoben
 *
 * Revision 1.4  2003/04/16 11:19:17  lawern
 * an den VKCLogManager werden jetzt Strings übergeben
 *
 * Revision 1.3  2003/04/15 07:26:38  lawern
 * Schnittstelle von Seach.search hat sich geändert.
 *
 * Revision 1.2  2003/04/09 14:45:52  lawern
 * der classify-Methode müssen jetzt die Keywords vom Anylyzer übergeben werden
 *
 * Revision 1.1  2003/04/09 14:15:27  lawern
 * initiale Version
 *
 */
package vkc.search;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.store.FSDirectory;

import vkc.analyse.SummaryEntry;
import vkc.analyse.TdfAnalyzer;
import vkc.analyse.VectorAnalyzer;

import vkc.db.entities.VKCCategory;
import vkc.db.entities.VKCDocument;
import vkc.db.entities.VKCKeyword;
import vkc.db.entities.VKCUser;

import vkc.db.serializer.VKCCategorySerializer;

import vkc.exception.DBException;

import vkc.misc.VKCLogManager;

import vkc.util.CategoryUtils;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


/**
 * Klasse für den Aufbau und Pflege des Klassifikationsindex von Lucene
 *
 * @author lawern
 */
public class LuceneClassifier implements Classifier {
    private static final boolean debug = false;

    // Verzeichnis, in dem der Suchindex liegt
    private static File indexDir = null;

    /** Lucene-Objekt zur Suche über alle Dokumente */
    private static IndexSearcher searcher = null;
    private static long lastOptmized = System.currentTimeMillis();

    // schützt den Index vor nebenläufigen Lese\-Schreiboperationen
    private static Object semaphor = new Object();

    // Lucene-Parameter
    public static int MERGE_FACTOR = 5;
    public static int MAX_FIELD_LENGTH = Integer.MAX_VALUE;
    public static int OPTIMIZATION_INTERVAL = 3600000; //1h

    /** Maximale Anzahl von Termen eines Kategorie-Vektors */
    private static final int maxKeysPerCategory = 200;

    /** Maximale Anzahl von Termen eines Kategorie-Vektors */
    private static final int maxKeysPerDocument = 50;

    // Analyzer anlegen, die für Lucene die Dokumente parsen
    private static Analyzer globalAnalyzer = new TdfAnalyzer();
    private static VectorAnalyzer analyzer = new VectorAnalyzer();

    /**
     * Konstruktor
     */
    public LuceneClassifier() {
    }

    /**
     * Initialisiert das Classify-Modul
     *
     * @param classifyIndexDir - Wurzelverzeichnis der VKC-Struktur
     * @param create - falls true, wird der Index neu aufgebaut
     */
    public void init(String classifyIndexDir, boolean create) {
        indexDir = new File(classifyIndexDir);

        try {
            File vkcIndex = new File(indexDir, "segments");

            if (!vkcIndex.exists() || create) {
                // Falls kein Index existiert, wird er automatisch
                // aufgebaut
                rebuildIndex();
            } else {
                // Index optimieren
                IndexWriter writer = new IndexWriter(indexDir, analyzer, false);
                writer.optimize();
                writer.close();
                lastOptmized = System.currentTimeMillis();
            }
        } catch (Exception e) {
            VKCLogManager.errorLog(this, "", e.getMessage(), e);
        }
    }

    /**
     * Methode baut den Klassifikationsindex von Lucene neu auf
     */
    public void rebuildIndex() throws IOException {
        if (searcher != null) {
            try {
                searcher.close();
            } catch (IOException e) {
                searcher = null;
            }
        }

        IndexWriter writer = null;

        try {
            if (indexDir.exists()) {
                try {
                    FSDirectory.getDirectory(indexDir, true);
                } catch (IOException e) {
                }
            }

            VKCLogManager.infoLog("vkc.search.Classify",
                "creating new classify-index in directory '" + indexDir + "'");

            writer = new IndexWriter(indexDir, analyzer, true);
            writer.setMergeFactor(MERGE_FACTOR);
            writer.setMaxFieldLength(MAX_FIELD_LENGTH);
        } finally {
            if (writer != null) {
                if ((lastOptmized + OPTIMIZATION_INTERVAL) < System.currentTimeMillis()) {
                    writer.optimize();
                    lastOptmized = System.currentTimeMillis();
                }

                writer.close();
            }
        }

        try {
            // alle in der Datenbank vorhandenen Kategorien einfügen
            Collection catIds = VKCCategorySerializer.getAllCategoryIds();
            addCategories(catIds);
        } catch (Exception ex) {
            VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
        }
    }

    /**
     * Fügt neue Kategorien in den Suchindex ein. Es werden keine neuen
     * Kategorievektoren erzeugt. Diese müssen bereits in der Datenbank
     * existieren.
     *
     * @param cats Collection mit Objekten vom Typ VKCCategory, die zum Index
     *        hinzugefügt werden sollen
     */
    final static private void addCategories(Collection catIds) {
        synchronized (semaphor) {
            //IndexWriter anlegen
            IndexWriter writer = null;

            try {
                writer = new IndexWriter(indexDir, analyzer, false);

                //Dokumente dem IndexWriter hinzufügen
                Integer actCatId;
                VKCCategory actCat;
                Document doc;

                for (Iterator it = catIds.iterator(); it.hasNext();) {
                    actCatId = (Integer) it.next();
                    actCat = VKCCategorySerializer.retrieveCategory(actCatId);
                    doc = createDocument(actCat);
                    writer.addDocument(doc);
                }
            } catch (Exception e) {
                VKCLogManager.errorLog(LuceneClassifier.class, "",
                    e.getMessage(), e);
            } finally {
                try {
                    if (writer != null) {
                        writer.optimize();
                        lastOptmized = System.currentTimeMillis();

                        writer.close();
                    }
                } catch (Exception ex) {
                }
            }

            // Searcher auf null setzen, weil sich der Index geändert hat
            searcher = null;
        }
    }

    /**
     * Einfügemethode für einzelne VKCCategory-Objekte. Nach dem Aufruf muss
     * das übergebene Objekt in der Datenbank aktualisiert werden, da die
     * Keywords ergänzt werden.
     *
     * @param data VKCCategory-Objekt, welches dem Index hinzugefügt werden
     *        soll
     */
    public void addCategory(VKCCategory data) throws IOException {
        if (debug) {
            VKCLogManager.infoLog(this, "addCategory: " + data.getCategoryId());
        }

        //Kategorievektor berechnen
        VKCKeyword[] catKeys = createCatVector(data, maxKeysPerCategory);

        if (catKeys != null) {
            //Keywords aus dem Kategorievektor dem VKCCategory-Objekt hinzufügen
            for (int i = 0; i < catKeys.length; i++) {
                data.addKeyword(catKeys[i]);
            }
        }

        synchronized (semaphor) {
            IndexWriter writer = null;

            try {
                Document doc = createDocument(data);

                writer = new IndexWriter(indexDir, analyzer, false);
                writer.addDocument(doc);
            } catch (Exception e) {
                VKCLogManager.errorLog(this, "", e.getMessage(), e);
            } finally {
                try {
                    if (writer != null) {
                        if ((lastOptmized + OPTIMIZATION_INTERVAL) < System.currentTimeMillis()) {
                            writer.optimize();
                            lastOptmized = System.currentTimeMillis();
                        }

                        writer.close();
                    }
                } catch (Exception ex) {
                }
            }

            //Searcher auf null setzen, weil sich der Index geändert hat
            searcher = null;
        }
    }

    /**
     * Sollte immer dann aufgerufen werden, wenn einer Kategorie ein neues
     * Dokument hinzugefügt wird. Nach dem Aufruf muss das übergebene Objekt
     * in der Datenbank aktualisiert werden, da die Keywords ergänzt werden.
     *
     * @param data VKCCategory-Objekt, welches im Index aktualisiert werden
     *        soll
     */
    public void updateCategory(VKCCategory data) throws IOException {
        if (debug) {
            VKCLogManager.infoLog("vkc.search.Classify",
                "updateCategory: " + data.getCategoryId());
        }

        //automatisch erzeugte Keywords löschen
        Set keys;

        keys = data.getKeywords();

        VKCKeyword tmpKey;

        for (Iterator it = keys.iterator(); it.hasNext();) {
            tmpKey = (VKCKeyword) it.next();

            if (tmpKey.isInternal()) {
                it.remove();
            }
        }

        data.setKeywords(keys);

        //Kategorie aus dem Index löschen
        removeCategory(data.getCategoryId());

        //Kategorie wieder in den Index einfügen
        addCategory(data);
    }

    /**
     * Methode zur Entfernung eines Dokuments aus dem Suchindex
     *
     * @param documentId Eindeutiger Bezeichner für einen Eintrag
     */
    public void removeCategory(Integer categoryId) throws IOException {
        IndexReader reader = null;

        if (debug) {
            VKCLogManager.infoLog("vkc.search.Classify",
                "Classify.removeCategory: " + categoryId);
        }

        synchronized (semaphor) {
            try {
                reader = IndexReader.open(indexDir);

                // Anhand der documentId das Dokument eliminieren
                reader.deleteDocuments(new Term("categoryId", categoryId.toString()));
            } finally {
                if (reader != null) {
                    reader.close();
                }
            }
        }
    }

    /**
     * Diese Methode erzeugt aus dem übergebenen VKCCategory-Objekt ein
     * Lucene-Document-Objekt. DynaAttributes werden noch nicht indiziert.
     *
     * @param vkcdoc VKCCategory-Objekt
     *
     * @return Lucene-Document-Objekt
     */
    private final static Document createDocument(VKCCategory vkcCat) {
        // Keywords-String aufbauen
        StringBuffer keywords = new StringBuffer();

        for (Iterator it = vkcCat.getKeywords().iterator(); it.hasNext();) {
            VKCKeyword keyword = (VKCKeyword) it.next();

            keywords.append(((int) (keyword.getWeight() * 10000)));
            keywords.append(':');
            keywords.append(keyword.getTermDescriptor());
            keywords.append('\n');
        }

        // Dokument für Index erstellen
        Document doc = new Document();
        doc.add(new Field("categoryId", vkcCat.getCategoryId().toString(),
                Field.Store.YES, Field.Index.UN_TOKENIZED));
        doc.add(new Field("name", vkcCat.getName(), Field.Store.YES,
                Field.Index.NO));

        if (keywords.length() > 0) {
            doc.add(new Field("contents",
                    new StringReader(keywords.toString().trim())));
            doc.add(new Field("keywords", vkcCat.getExternalKeywordsString(),
                    Field.Store.YES, Field.Index.TOKENIZED));
        }

        String description = vkcCat.getDescription();

        if ((description != null) && (description.length() > 0)) {
            doc.add(new Field("description", description, Field.Store.YES,
                    Field.Index.NO));
        }

        return doc;
    }

    /**
     * Methode zum Suchen über die VKC-Kategorien
     *
     * @param query Suchanfrage
     * @param maxHits - maximal Anzahl zurückgelieferter Treffer
     * @param dateRangeMode gibt das Alter an das die Dokumente haben dürfen,
     *        die gesucht werden sollen
     *
     * @return Liste mit Objekten vom Typ ResultEntry der gefundenen Dokumente
     *         oder <code>null</code> falls etwas schief gegangen ist
     */
    private static final Collection searchCategory(String query,
        int dateRangeMode, VKCUser user, VKCCategory category, int maxHits,
        String field) {
        ArrayList result = new ArrayList();

        if (searcher == null) {
            //Suchobjekt initialisieren
            try {
                searcher = new IndexSearcher(indexDir.getCanonicalPath());
            } catch (IOException e) {
                VKCLogManager.errorLog("vkc.search.Classify", "7",
                    "Can't create IndexSearcher: " + query, e);
            }
        }

        //Filter für die Suche initialisieren
        Set validDocuments;

        try {
            //Kategorien holen in denen der User publizieren darf
            if (category != null) {
                validDocuments = CategoryUtils.getWriteableCategoryIds(category.getCategoryId(),
                        user);
            } else {
                validDocuments = CategoryUtils.getWritableCategoryIds(user);
            }

            ClassifyResultFilter filter = new ClassifyResultFilter(validDocuments,
                    null);

            Query userQuery = null;

            //Suchstring parsen
            QueryParser parser = new QueryParser(field, globalAnalyzer);
            userQuery = parser.parse(query.toLowerCase());

            if (debug) {
                System.out.println("Classify-Query: " + userQuery);
            }

            //Suche durchführen
            Hits hits = searcher.search(userQuery, filter);

            //Ergebnisliste aufbauen
            int hitsLength = Math.min(hits.length(), maxHits);

            for (int i = 0; i < hitsLength; i++) {
                result.add(new ClassifyResultEntry(hits.doc(i).get("categoryId"),
                        hits.doc(i).get("name"), hits.doc(i).get("keywords"),
                        hits.doc(i).get("description"), hits.score(i)));
            }
        } catch (ParseException e) {
            VKCLogManager.errorLog("vkc.search.Classify", "1",
                "Can't parse search query: " + query, e);
        } catch (IOException e1) {
            VKCLogManager.errorLog("vkc.search.Classify", "2",
                "Error in search call!", e1);
        } catch (DBException e2) {
            VKCLogManager.errorLog("vkc.search.Classify", "6",
                "Database Exception", e2);
        }

        return result;
    }

    /**
     * Diese Methode liefert zu dem übergebenen Schlagwörtern eines Dokuments
     * einer Liste aller ähnlichen Kategorien.
     *
     * @param keywords Keywords die von der Methode
     *        vkc.analyse.Analyser.getKeywords(...) erzeugt wurden
     * @param dateRangeMode gibt das Alter an das die Dokumente haben dürfen,
     *        die gesucht werden sollen
     * @param user
     * @param category falls !=null, dann soll ab dieser Kategorie
     *        klassifiziert werden
     *
     * @return Liste mit Objekten vom Typ ClassifyResultEntry der ähnlichen
     *         Kategorien oder <code>null</code> falls etwas schief gegangen
     *         ist
     */
    public Collection classify(SummaryEntry[] keywords, int currDocLangId, int dateRangeMode,
        VKCUser user, VKCCategory category, int maxHits) {
        double weightSum = 0;
        StringBuffer query = new StringBuffer();

        int keys = Math.min(maxKeysPerDocument, keywords.length);

        SummaryEntry keyword;
        String term;

        //Summe der Gewichte im Vektor berechnen
        for (int i = keys - 1; i >= 0; i--) {
            weightSum += keywords[i].getWeight();
        }

        //Normierten Anfrage-Vektor erzeugen
        for (int i = keys - 1; i >= 0; i--) {
            keyword = keywords[i];

            double weight = keyword.getWeight() / weightSum;

            term = keyword.getText();
            term.replace('"', '\'');
            query.append("\"" + term + "\"^" + weight + " ");
        }

        return searchCategory(query.toString(), dateRangeMode, user, category,
            maxHits, "contents");
    }

    /**
     *
     * @param keywords Keywords die von der Methode
     *        vkc.analyse.Analyser.getKeywords(...) erzeugt wurden
     * @param dateRangeMode gibt das Alter an das die Dokumente haben dürfen,
     *        die gesucht werden sollen
     * @param user
     * @param category falls !=null, dann soll ab dieser Kategorie
     *        klassifiziert werden
     *
     * @return Liste mit Objekten vom Typ ClassifyResultEntry der ähnlichen
     *         Kategorien oder <code>null</code> falls etwas schief gegangen
     *         ist
     */
    public Collection classifyByCategoryKeywords(SummaryEntry[] keywords,
        int dateRangeMode, VKCUser user, VKCCategory category, int maxHits) {
        double weightSum = 0;
        StringBuffer query = new StringBuffer();

        int keys = Math.min(maxKeysPerDocument, keywords.length);

        SummaryEntry keyword;
        String term;

        //Summe der Gewichte im Vektor berechnen
        for (int i = keys - 1; i >= 0; i--) {
            weightSum += keywords[i].getWeight();
        }

        //Normierten Anfrage-Vektor erzeugen
        for (int i = keys - 1; i >= 0; i--) {
            keyword = keywords[i];

            term = keyword.getText();
            term.replace('"', '\'');

            query.append(term);
            query.append("; ");
        }

        return searchCategory(query.toString(), dateRangeMode, user, category,
            maxHits, "keywords");
    }

    /**
     * Diese Methode liefert zu der Kategorie mit der übergebenen ID den
     * Kategorie-Vektor in Form einer Collection von VKCKeyword für die
     * whatsRelated-Funktion. Es wird die Zentroid-Methode verwendet.
     *
     * @param category VKCCategory-Objekt der Kategorie
     * @param size Größe des Arrays das zurückgeliefert wird. Wenn alle
     *        verfügbaren VKCKeywords zurückgeliefert werden sollen ist hier
     *        -1 zu setzen.
     *
     * @return Liste mit Objekten vom Typ VKCKeyword oder <code>null</code>
     *         falls etwas schief gegangen ist
     */
    private static final VKCKeyword[] createCatVector(VKCCategory category,
        int size) {
        int docs;
        double weightSum = 0;

        HashMap catVector = new HashMap();
        VKCKeyword[] resultVector = null;

        //Liste der Kategorie-Dokumente holen
        List documents = category.getDocuments();
        Set docKeys;
        VKCKeyword keyword;
        VKCKeyword tmpKey;
        docs = documents.size();

        for (int i = docs - 1; i >= 0; i--) {
            docKeys = ((VKCDocument) documents.get(i)).getKeywords();

            //docKeys in den catVector einfügen
            for (Iterator it = docKeys.iterator(); it.hasNext();) {
                keyword = (VKCKeyword) it.next();

                //                if (debug) {
                //                    System.out.println("Term: " + keyword.getTermDescriptor() +
                //                        " : " + keyword.getWeight());
                //                }
                if (keyword.isInternal()) {
                    //nur die automatisch erzeugten Keywords einfügen
                    if (catVector.containsKey(keyword)) {
                        //Keyword ist schon im catVector
                        tmpKey = (VKCKeyword) catVector.get(keyword);
                        tmpKey.setWeight(tmpKey.getWeight() +
                            keyword.getWeight());
                    } else {
                        //Keyword ist noch nicht im catVector
                        catVector.put(keyword, keyword);
                    }
                }
            }
        }

        //Ergebnis-Array aufbauen
        resultVector = new VKCKeyword[catVector.size()];
        resultVector = (VKCKeyword[]) catVector.values().toArray(resultVector);

        //Ergebnis-Array nach Gewicht sortieren
        Arrays.sort(resultVector);

        //Ergebnis-Array gegebenenfalls verkleinern
        if (size < resultVector.length) {
            VKCKeyword[] tmpVector = new VKCKeyword[size];
            System.arraycopy(resultVector, 0, tmpVector, 0, size);
            resultVector = tmpVector;
        }

        //Summe der Gewichte im Ergebnis-Vektor berechnen
        for (int i = resultVector.length - 1; i >= 0; i--) {
            weightSum += resultVector[i].getWeight();
        }

        //Ergebnis-Array normieren
        for (int i = resultVector.length - 1; i >= 0; i--) {
            //Auf die Anzahl der Dokumente normieren
            //resultVector[i].setWeight(resultVector[i].getWeight() / docs);
            //Auf die Summe aller Gewichte normieren
            resultVector[i].setWeight(resultVector[i].getWeight() / weightSum);
        }

        return resultVector;
    }

    /**
     * Main-Methode für Tests
     */
    public static void main(java.lang.String[] args) {
        VKCLogManager.init("c:/temp/test_", ".log");
        VKCLogManager.setEnableLogging(true);
        VKCLogManager.setLogToConsole(true);

        Classifier classifier = new LuceneClassifier();
        classifier.init("E:\\work\\VKC3\\defaultroot\\classifyindex", false);

        //search("data", -1, -1);
        //whatsRelated(createDocVector("46", -1), -1, -1);
        //int[] test = { 1024, 888, 666, 333, 220 };
        //System.out.println("ggT: " + ggT(test));
    }

    public void saveCatBase() {
    }

    public boolean optimizationIsRunning() {
        return false;
    }
}
