package sequenzenanalyse;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 *
 * @author 
 */
public class SequenzenAnalyse {

    public static void main(String[] args) {
        
        /**
         * Die aktuelle Sequenz.
         */
        String actSequenz;
        
        /**
         * Alle enthaltenen Buchstaben der aktuellen Sequenz.
         */
        char[] actIncLetters;
        
        /**
         * Schleifenzähler
         */
        int i0 = -1;
        
        /**
         * Minimale Bausteinlänge
         */
        int minBlockLength = 2;
        
        /**
         * Maximale Bausteinlänge
         */
        int maxBlockLength = 30;
        
        /**
         * Ordner, in dem die Testsequenzen liegen
         */
        String sequenzDir = "C:/Users/ICH/Documents/NetBeansProjects/SequenzenAnalyse/testsequenzen/";
        
         /**
         * Ab welcher Datei soll die Analyse starten?
         */
        int startAtFile = 1;
        
        /**
         * Häufigkeit von EEE
         */
        int occurences_EEE;
        
        /**
         * Array, das alle Bausteine speichert, die 128x vorkommen.
         */
        String[] block128;
        
        /**
         * Array, das alle Bausteine einer Sequenz enthält.
         */
        String[] allBlocks;
        
        /**
         * Enthält das aktuelle Analyse-Objekt
         */
        Analyse actA;
        
        /**
         * Enthält alle gefundenen Bausteine
         */
        List<String> knownBlocks = new ArrayList<>();
        
        /**
         * Enthält die vollständige Dateiliste
         */
        String[] fileList = getFileListFromDir(sequenzDir);
        
        /**
         * 
         */
        int numberOfSequenzes = fileList.length;
        
        //Eine neue Instanz von Sequenz erstellen
        Sequenz S = new Sequenz(fileList, startAtFile);
        
        Analyse[] arr_Analyse = new Analyse[numberOfSequenzes];
        
        do {
            i0++;
            //Lese die nächste Sequenz ein
            actSequenz = S.readNextSequenz();
            
            if(actSequenz == null) {
                System.out.println("Beim Dateiladen ist ein Fehler aufgetreten.");
                break;
            }
            
            Analyse A = new Analyse(actSequenz);
            
            System.out.println(S.getActFileName());
//            System.out.println(S.getActSequenz());
            
            //Aufgabe 1 - Buchstaben ermitteln
            actIncLetters = A.includedChars(true, true);
            System.out.println("Alle enthaltenden Zeichen in der Sequenz sind: " + String.copyValueOf(actIncLetters));
            
            arr_Analyse[i0] = A;
            
        } while(S.existNextSequenz());
        
        System.out.println("-------------------------------------------------------------");
        
        actA = arr_Analyse[0];
        
        //Aufgabe 2
        if(actA.sameOccurencesInAllSequenzes("EEE", arr_Analyse)) {
            occurences_EEE = actA.countBlock("EEE");
            System.out.println("Der Block [EEE] kommt " + occurences_EEE + "x mal in allen Sequenzen vor.");
        } else {
            System.out.println("Der Block [EEE] kommt nicht in allen Sequenzen gleich häufig vor.");
        }
        
        
        //Aufgabe 3;
        block128 = actA.searchBlockForAllSequenzes(128, arr_Analyse[0].buildBlocks(minBlockLength, maxBlockLength), true, arr_Analyse);
        System.out.println("Alle folgenden Blöcke, kommen in allen Sequenzen 128x vor: ");
        System.out.println(Arrays.toString(block128));
        
        
        //Aufgabe 4+5
        //knownBlocks.add("EEE");
        //knownBlocks.addAll(block128);
        String[] allBlocksWithSameOccurences = actA.searchAllBlocksWithSameOccurences(minBlockLength, maxBlockLength, arr_Analyse, knownBlocks);
        
        System.out.println("-------------------------------------------------------------");
        System.out.println("Alle folgenden Blöcke kommen in allen Sequenzen gleich gäufig vor.");
        for(String b: allBlocksWithSameOccurences) {
            int occurrence = actA.countBlock(b);
            System.out.println("[" + b + "] - " + occurrence + "/" + occurrence*numberOfSequenzes);
        }
        
        knownBlocks.clear();
        knownBlocks.addAll(Arrays.asList(allBlocksWithSameOccurences));
        
        System.out.println("-------------------------------------------------------------");
        System.out.println("Folgende Teilsequenzen bleiben nun über:");
        
        String pattern = "";
        for(String block: knownBlocks) {
            pattern += "(" + block + ")|";
        }
        pattern = pattern.substring(0, pattern.length()-1);
        
        for(int i = 0; i < arr_Analyse.length ; i++) {
            String tmp_seq = arr_Analyse[i].getSequenz();
            
            tmp_seq = tmp_seq.replaceAll(pattern, " ");
            tmp_seq = tmp_seq.replaceAll(" +", " ");
            
            System.out.println(fileList[i] + " - " + tmp_seq.trim());
        }
        
        
        System.out.println("-------------------------------------------------------------");
        System.out.println("Nun werden alle Bausteine einer jeden Sequenz ermittelt:");
        
        
        List<String> tmp = new ArrayList<>();
        for(int i = 0; i < arr_Analyse.length ; i++) {
            Analyse A = arr_Analyse[i];
            
            allBlocks = A.searchAllBlocks(minBlockLength, maxBlockLength, arr_Analyse, knownBlocks);
            for(String newBlock: allBlocks) {
                if(!knownBlocks.contains(newBlock)) {
                    tmp.add(newBlock);
                }
            }
            knownBlocks.addAll(tmp);
            tmp.clear();
            
            System.out.println("---");
            System.out.println(fileList[i]);
            int occurencesSum = 0;
            int occurrence;
            for(String b: knownBlocks) {
                occurrence = A.countBlock(b);
                occurencesSum += occurrence;
                System.out.println(b + " - " + occurrence);
            }
            System.out.println("In der Sequenz wurden insgesamt " + occurencesSum + " Bausteine identifiziert.");
        }
        
        System.out.println("-------------------------------------------------------------");
        System.out.println("Alle gefundenen Bausteine sind:");
        System.out.println(knownBlocks);
        
        //Aufgabe 6
        int minLengthForBlocks = knownBlocks.get(0).length();
        int maxLengthForBlocks = minLengthForBlocks;
        for(String b: knownBlocks) {
            if(b.length() < minLengthForBlocks) {
                minLengthForBlocks = b.length();
            }
            if(b.length() > maxLengthForBlocks) {
                maxLengthForBlocks = b.length();
            }
        }
        
        System.out.println("-------------------------------------------------------------");
        System.out.println("Ein Baustein ist mindestens " + minLengthForBlocks + " Zeichen und maximal " + maxLengthForBlocks + " Zeichen lang.");
        
        //Aufgabe 7
        List<String> genderBlocks = new ArrayList<>();
        for(Analyse A: arr_Analyse) {
            tmp = Arrays.asList(A.searchBlock(1, knownBlocks, true));
            for(String new_block: tmp) {
                if(!genderBlocks.contains(new_block)) {
                    genderBlocks.add(new_block);
                }
            }
        }
        System.out.println("-------------------------------------------------------------");
        System.out.println("Die beiden Bausteine, die die Geschlechter identifizieren sind: ");
        System.out.println(genderBlocks);
        
        
        //Aufgabe 8
        System.out.println("-------------------------------------------------------------");
        System.out.println("Die Bausteine, deren Häufigkeit voneinander abhängt sind:");
        //Alle gefundenen Bausteine könnten gesucht sein
        List<String> connectedBlocks = knownBlocks;
        //Entferne alle Bausteine, die immer gleich häufig vorkommen
        connectedBlocks.removeAll(Arrays.asList(allBlocksWithSameOccurences));
        //Entferne die geschlechtsspezifischen Bausteine
        connectedBlocks.removeAll(genderBlocks);
        
        System.out.println(connectedBlocks);
        System.out.println("---");
        //Beziehung der Bausteine anhand der ersten Sequenz bestimmen
//        actA = arr_Analyse[0];
        int cb_occurencesSum = 0;
        for(String cb: connectedBlocks) {
            cb_occurencesSum += actA.countBlock(cb);
        }
        
        System.out.println("Die Bausteine kommen zusammen genau " + cb_occurencesSum + " mal pro Sequenz vor.");
        
        if(connectedBlocks.size() == 2) {
            int minOccurencesOfTheFirstBlock = actA.countBlock(connectedBlocks.get(0));
            int maxOccurencesOfTheFirstBlock = minOccurencesOfTheFirstBlock;
            for(Analyse analyse: arr_Analyse) {
                int actCount = analyse.countBlock(connectedBlocks.get(0));
                if(actCount < minOccurencesOfTheFirstBlock) {
                    minOccurencesOfTheFirstBlock = actCount;
                }
                if(actCount > maxOccurencesOfTheFirstBlock) {
                    maxOccurencesOfTheFirstBlock = actCount;
                }
            }
            
            System.out.println("Die Beziehung der beiden Bausteine lässt sich wie folgt darstellen: ");
            System.out.println("n(" + connectedBlocks.get(1) + ") = 300 - n(" + connectedBlocks.get(0) + ") mit " + minOccurencesOfTheFirstBlock + " <= n(" + connectedBlocks.get(0) + ") >= " + maxOccurencesOfTheFirstBlock);
        }
        
        System.out.println("-------------------------------------------------------------");
        System.out.println("Hiermit ist die Analyse beendet.");
        
    }
    
    
    /**
     * Gibt eine Liste aller Dateien in einem Verzeichnis zurück.
     * 
     * @param dir
     * @return 
     */
    private static String[] getFileListFromDir(String dir) {
        File file = new File(dir);
//        System.out.println(file.getName());
        List<String> fileList = new ArrayList<>();
        
        File[] files = file.listFiles();
        if (files != null) { // Erforderliche Berechtigungen etc. sind vorhanden
            for (int i = 0; i < files.length; i++) {
//                System.out.print(files[i].getAbsolutePath());
                if (files[i].isDirectory()) {
//                    System.out.print(" (Ordner)\n");
                }
                else {
//                    System.out.print(" (Datei)\n");
                    fileList.add(files[i].getAbsolutePath());
//                    System.out.println(files[i].getAbsolutePath());
                }
            }
        }
        //Collections.sort(fileList);
        
        String[] finalList = new String[fileList.size()];
        finalList = fileList.toArray(finalList);
        return finalList;
    }
    
    /**
     * Gibts number Daten aus dem Verzeichnis dir zurück.
     * 
     * @param dir
     * @param number
     * @return 
     */
    private static String[] getFileListFromDir(String dir, int number) {
        String[] list = getFileListFromDir(dir);
        List<String> newList = new ArrayList<>();
        int i = 0;
        
        for(String s: list) {
            if(i == number) {
                break;
            }
            newList.add(s);
            i++;
        }
        
        String[] finalList = new String[newList.size()];
        finalList = newList.toArray(finalList);
        return finalList;
    }
}

----

package sequenzenanalyse;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;



/**
 * Liest Sequenzen aus den Dateien ein und gibt diese zurück.
 * 
 * @author 
 */
public class Sequenz {
    
    //Variablendeklaration
    /**
     * 
     */
    private String fileName;
    
    /**
     * 
     */
    public String sequenz;
    
    /**
     * 
     */
    String[] fileList;
    
    /**
     * 
     */
    int fileCounter;
    
    /**
     * 
     */
    int fileCounterStart;
    
    
    /**
     * Konstruktor
     * 
     * @param fileList
     * @param fileCounterStart 
     */
    public Sequenz(String[] fileList, int fileCounterStart) {
        this.fileList = fileList;
        this.sequenz = null;

        if(fileCounterStart+1 > fileList.length) {
            this.fileCounterStart = fileList.length-1;
        } else {
            this.fileCounterStart = fileCounterStart-1;
        }
        this.fileCounter = this.fileCounterStart;
    }
    
    /**
     * Liefert den aktuellen Dateinamen zurück.
     * 
     * @return 
     */
    public String getActFileName() {
        return this.fileName;
    }
    
    /**
     * Gibt die zuletzt eingelesene Sequenz zurück.
     * 
     * @return 
     */
    public String getActSequenz() {
        return this.sequenz;
    }
    
    /**
     * Gibt den aktuellen Counterstand zurück.
     * 
     * @return 
     */
    public int getActCounter() {
        return this.fileCounter;
    }
    
    /**
     * Gibt true zurück, wenn eine weitere Sequenz existiert.
     * 
     * @return 
     */
    public boolean existNextSequenz() {
        if (this.fileList.length <= this.fileCounter) {
            return false;
        }
        
        return true;
    }
    
    /**
     * Liest die Sequenz aus der Datei fileName aus.
     * 
     * @param filename
     * @return 
     */
    public String readSequenz(String fileName) {
        String file = null;
//        System.out.println(fileName);
        
        FileReader fr;
        BufferedReader br;
        try {
            fr = new FileReader(fileName);
            br = new BufferedReader(fr);
           
            // Textzeilen der Datei einlesen:
            String zeile;
            zeile = br.readLine();
//            System.out.println("In der Datei steht: ");
            while (zeile != null) {
                if (file == null) {
                    file = zeile;
                } else {
                    file += zeile;
                }
                zeile = br.readLine();
            }
//            System.out.println(file);
            
            fr.close();
        }
        catch (IOException e){
            System.out.println("Fehler beim Lesen der Datei " + fileName);
            System.out.println(e.toString());
        }
        
        if (file != null) {
            this.sequenz = file;
        } else {
            System.out.println("Die Datei ist leer!");
        }
        return file;
    }
    
    /**
     * Liest die nächste Sequenz ein.
     * 
     * @return 
     */
    public String readNextSequenz() {
//        System.out.println(fileCounter);
//        System.out.println(fileList[fileCounter]);
        if (this.fileList[this.fileCounter] == null) {
            System.out.println("Dateiliste enthält keine weiteren Einträge!");
            return null;
        } else {
            String actSequenz = this.readSequenz(this.fileList[this.fileCounter]);
            this.fileName = this.fileList[this.fileCounter];
            this.fileCounter++;
            return actSequenz;
        }
    }
    
}

---


package sequenzenanalyse;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 *
 * @author 
 */
public class Analyse {
    
    /**
     * Enthält die aktuelle Sequenz
     */
    private String sequenz;
    
    /**
     * Enthält alle Zeichen der aktuellen Sequenz
     */
    private char[] incChars;
    
    /**
     *
     */
    public Analyse() {}
    
    /**
     *
     * @param seq
     */
    public Analyse(String seq) {
        this.sequenz = seq;
    }
    
    /**
     * Liefert die aktuell gespeicherte Sequenz zurück.
     * 
     * @return 
     */
    public String getSequenz() {
        return this.sequenz;
    }
    
    
    /**************************************************************************
     * Ermittle alle enthaltenen Buchstaben in der aktuellen Sequenz.         *
     **************************************************************************/
    
    /**
     * Ermittle, welche Buchstaben in der Sequenz enthalten sind.
     * 
     * @return 
     */
    public char[] includedLetters() {
        return includedChars(true, false);
    }
    
    /**
     * Ermittelt alle enthaltenen Groß- und Kleinbuchstaben in der Sequenz.
     * 
     * @param caseSensetive
     * @return 
     */
    public char[] includedLetters(boolean caseSensetive) {
        return includedChars(caseSensetive, false);
    }
    
    /**
     * Ermittelt alle enthaltenen Groß-, Kleinbuchstaben und Ziffern in der
     * Sequenz.
     * 
     * @param caseSensetive
     * @param withDigits
     * @return 
     */
    public char[] includedChars(boolean caseSensetive, boolean withDigits) {
        String chars = "";

        if(this.sequenz != null && !"".equals(this.sequenz)) {
            
            for(char c = 'A'; c <= 'Z'; c++) {
//                System.out.println(Character.toString(c));
                if(caseSensetive) {
                    if(this.sequenz.contains(Character.toString(c))) {
                        chars += c;
                    }
                    if(this.sequenz.contains(Character.toString(c).toLowerCase())) {
                        chars += c;
                    }
                } else {
                    if(this.sequenz.toUpperCase().contains(Character.toString(c))) {
                        chars += c;
                    }
                }
            }
            
            if(withDigits) {
                for(char c = '0'; c <= '9'; c++) {
        //                System.out.println(Character.toString(c));

                    if(this.sequenz.contains(Character.toString(c))) {
                        chars += c;
                    }
                }
            }
//            System.out.println("Alle enthaltenden Buchstaben in der Sequenz sind: " + chars);
            
            this.incChars = chars.toCharArray();
            return chars.toCharArray();
        }
        return null;
    }
    
    
    /**************************************************************************
     * Bausteine überprüfen                                                   *
     **************************************************************************/
    
    /**
     * Überprüft, ob ein Baustein nur aus vorhandenen Zeichen besteht.
     * 
     * @param block
     * @return 
     */
    public boolean isValidBlock(String block) {
        if(this.incChars.length == 0) {
            this.includedChars(true, true);
        }
        
        boolean found;
        
        for(char c: block.toCharArray()) {
            found = false;
            for(char incChar: incChars) {
                if(c == incChar) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                return false;
            }
        }
//        System.out.println("Alle Buchstaben des Blocks sind in der Sequenz vorhanden.");
        return true;
    }
    
    
    /**************************************************************************
     * Überprüfen, ob ein/mehrere Baustein/e in ein/mehreren/allen Sequenzen  *
     * vorkommen.                                                             *
     **************************************************************************/
    
    /**
     * Überprüfe, ob der Baustein in der Sequenz enthalten ist.
     * 
     * @param block
     * @return 
     */
    public boolean existBlock(String block) {
        if(this.sequenz.contains(block)) {
            return true;
        }
        return false;
    }
    
    /**
     * Sucht einen Baustein in allen Sequenzen und gibt aus, ob dieser überall
     * vorhanden ist.
     * 
     * @param block
     * @param allSequenzes
     * @return 
     */
    public static boolean existBlockInAllSequenzes(String block, Analyse[] allSequenzes) {
        for(Analyse A: allSequenzes) {
            if(!A.existBlock(block)) {
                return false;
            }
        }
        return true;
    }

    
    /**************************************************************************
     * Bausteine erstellen                                                    *
     **************************************************************************/
    
    /**
     * Erstellt Bausteine aus allen enthaltenen Zeichen der Sequenz.
     * Gibt nur Bausteine mit einer bestimmten Länge sowie mindestens 
     * einmaligem Vorkommen, in der aktuellen Sequenz, zurück.
     * 
     * @param minChars
     * @param maxChars
     * @return 
     */
    public String[] buildBlocks(int minChars, int maxChars) {
        List<String> blocksFound = new ArrayList<>();
        List<String> actBlocks = new ArrayList<>();
        String[] actBlocks2;

        String actBlock = "";

        //Alle Bausteine beginnen mit einem Buchstaben
        for(char c: this.incChars) {
            actBlocks.add(String.valueOf(c));
        }

//        System.out.println("Mögliche Blöcke sind: ");
        do {
            //Bausteinliste zwischenspeichern
            actBlocks2 = new String[actBlocks.size()];
            actBlocks2 = actBlocks.toArray(actBlocks2);
            actBlocks.clear();
            
            
            for(String s: actBlocks2) {
                for(char c: this.incChars) {
                    //An bisherigen Baustein einen Buchstaben anhängen
                    actBlock = s + c;
                    
                    //Existiert der Baustein in der Sequenz?
                    //Bausteine, die nicht vorkommen sofort entfernen/ignorieren
                    if(this.existBlock(actBlock)) {
                        
                        //Baustein entspricht der geforderten Länge
                        if(actBlock.length() >= minChars & actBlock.length() <= maxChars) {
                            blocksFound.add(actBlock);
//                            System.out.println(actBlock + " ist ein Block.");
                        }
                        //Aktuelle Bausteine zwischenspeichern, um weitere
                        //Buchstaben anzuhängen
                        actBlocks.add(actBlock);
                    }
                }
            }

        } while(actBlock.length() < maxChars);
        
        //Typecast
        String[] resultBlocks = new String[blocksFound.size()];
        resultBlocks = blocksFound.toArray(resultBlocks);
        
        return resultBlocks;
    }
    
    
    /**************************************************************************
     * Zählen des Vorkommens eines/mehrerer Baustein/e in einer/mehreren/     *
     * allen Sequenz/en.                                                      *
     **************************************************************************/
    
    /**
     * Zähle die Anzahl der Vorkommen des Bausteins in der Sequenz.
     * 
     * @param block
     * @return 
     */
    public int countBlock(String block) {
        int occurrence = 0;
        
        //Ist der Block valide?
        if(!this.isValidBlock(block)) {
            return 0;
        }
        if(block.length() == 0) {
            return 0;
        }
        if(!this.existBlock(block)) {
            return 0;
        }
        
        //false = Kein Überlappen der Bausteine
        boolean eagerMatching = false;
        
        if (0 != block.length()) {
            for (int index = this.sequenz.indexOf(block, 0);
                 index != -1;
                 index = this.sequenz.indexOf(block, eagerMatching ? index + 1 : index + block.length())) {
                occurrence++;
            }
        }
        
//        System.out.println("Der Block " + '"' + block + '"' + " kommt " + occurrence + " mal in der Sequenz vor.");
        return occurrence;
    }
    
    /**
     * Kommt der Block in allen Dateien gleich oft vor?
     * 
     * @param block
     * @param other_sequenzes
     * @return 
     */
    public boolean sameOccurencesInAllSequenzes(String block, Analyse[] other_sequenzes) {
        int occurence;
        
        occurence = this.countBlock(block);
        for(Analyse A: other_sequenzes) {
            if(A.countBlock(block) != occurence) {
                return false;
            }
        }
//        System.out.println("Der Baustein " + block + " kommt " + occurence + "x in allen Sequenzen vor.");
        return true;
    }
    
    
    /**************************************************************************
     * Suche Bausteine, die genau x mal in einer/allen Sequenzen vorkommen    *
     **************************************************************************/
    
    /**
     * Suche Bauchsteine (mit buildBlocks erstellt), die genau occurrence mal in
     * der Sequenz vorkommen.
     * 
     * @param occurrence
     * @param minChars
     * @param maxChars
     * @param trim 
     * @return 
     */
    public String[] searchBlock(int occurrence, int minChars, int maxChars, boolean trim) {
        //Erstelle mögliche Bausteine
        String[] possibleBlocks = this.buildBlocks(minChars, maxChars);
        return this.searchBlock(occurrence, possibleBlocks, trim);
    }
    
    /**
     * Suche einen Bauchstein, der genau occurrence mal in
     * der Sequenz vorkommt.
     * 
     * @param occurrence
     * @param possibleBlocks
     * @param trim
     * @return 
     */
    public String[] searchBlock(int occurrence, List<String> possibleBlocks, boolean trim) {
        String[] pBlocks = new String[possibleBlocks.size()];
        pBlocks = possibleBlocks.toArray(pBlocks);
        return this.searchBlock(occurrence, pBlocks, trim);
    }
    
    /**
     * Suche einen Bauchstein, der genau occurrence mal in
     * der Sequenz vorkommt.
     * 
     * @param occurrence
     * @param possibleBlocks
     * @param trim
     * @return 
     */
    public String[] searchBlock(int occurrence, String[] possibleBlocks, boolean trim) {
        
        List<String> blocks = new ArrayList<>();
        List<String> foundBlocks = new ArrayList<>();
        int maxLength = this.sequenz.length() / occurrence;
        
        for(String b: possibleBlocks) {
            if(this.isValidBlock(b)) {
                blocks.add(b);
            }
        }
        
        String[] isValidBlocks = new String[blocks.size()];
        isValidBlocks = blocks.toArray(isValidBlocks);
        
//        System.out.println("Es wurden folgende Blocks mit der Häufigkeit " + occurrence + " gefunden.");
        
        for(String block: isValidBlocks) {
            //Kommt der Block occutences mal in der Sequenz vor?
            if(block.length() <= maxLength) {
                if(this.countBlock(block) == occurrence) {
    //                System.out.println(block);
                    foundBlocks.add(block);
                }
            }
        }
        
        if(trim) {
            //Doppelte Bausteine entfernen
            //Es werden alle Bausteine aus der Liste entfernt, die Bestandteil eines
            //größeren Bausteins sind.
            for(int i = foundBlocks.size()-1; i >= 0; i--) {
                for(int j = i-1; j >= 0; j--) {
                    try {
                        //Enthält Baustein i den Baustein j ?
                        if(foundBlocks.get(i).contains(foundBlocks.get(j))) {
                            //Entferne Baustein j
                            foundBlocks.remove(foundBlocks.get(j));
                            //Indexgrenze verschieben
                            i--;
                        } else {
                            //Enthält Baustein j den Baustein i?
                            if(foundBlocks.get(j).contains(foundBlocks.get(i))) {
                                //Entferne Baustein i
                                foundBlocks.remove(foundBlocks.get(i));
                                //Indexgrenze verschieben
                                j--;
                            }
                        }
                    } catch(IndexOutOfBoundsException e) {
                        System.out.println(e.toString());
                    }
                }
            }
        }
        
        //Typecast
        String[] resultBlocks = new String[foundBlocks.size()];
        resultBlocks = foundBlocks.toArray(resultBlocks);
        
        return resultBlocks;
    }
    
    /**
     * Sucht einen Baustein, der genau occurrence mal in allen Sequenzen vorkommt.
     * 
     * @param occurrence
     * @param possibleBlocks
     * @param trim
     * @param otherSequenzes
     * @return
     */
    public String[] searchBlockForAllSequenzes(int occurrence, String[] possibleBlocks, boolean trim, Analyse[] otherSequenzes) {
        String[] foundBlocks = this.searchBlock(occurrence, possibleBlocks, true);
        List<String> realResultBlocks;
        realResultBlocks = Arrays.asList(foundBlocks);
        
        for(String rrb: realResultBlocks) {
            if(!this.sameOccurencesInAllSequenzes(rrb, otherSequenzes)) {
                System.out.println("Der Block " + '"' + rrb + '"' + " kommt in der Sequenz NICHT " + occurrence + "x vor.");
                realResultBlocks.remove(realResultBlocks.indexOf(rrb));
            }
        }
        
        return realResultBlocks.toArray(foundBlocks);
    }
    
    /**
     * Sucht alle Bausteine, die genau gleich häufig in allen Sequenzen vorkommen.
     * 
     * @param minChars
     * @param maxChars
     * @param other_sequenzes
     * @param knownBlocks
     * @return 
     */
    public String[] searchAllBlocksWithSameOccurences(int minChars, int maxChars, Analyse[] other_sequenzes, List<String> knownBlocks) {
        //Variablen
        int seqLength;
        seqLength = this.sequenz.length();
        
        String tmp_seq;
        tmp_seq = this.sequenz;
        
        String[] possibleBlocks;
        
        List<String> removeBlocks;
        
        List<String> blocksInAllSequenzes;
        blocksInAllSequenzes = new ArrayList<>();
        
        //Übergabewerte überprüfen
        if(minChars <= 0) {
            System.out.println("Alle Bausteine sollten mindestens einen Buchstaben enthalten.");
            minChars = 1;
        }
        if(maxChars >= seqLength) {
            System.out.println("Ein Baustein muss Teil einer Sequenz sein.");
            maxChars = seqLength/2;
        }
        
        //Bausteine erstellen
        possibleBlocks = this.buildBlocks(minChars, maxChars);
//        System.out.println("Es gibt " + possibleBlocks.length+ " mögliche Bausteine.");
        
        //Ersten Bausteine aussortieren
//        for(String b: possibleBlocks) {
//            if(existBlockInAllSequenzes(b, other_sequenzes)) {
//                blocksInAllSequenzes.add(b);
//            }
//        }
        
        //Hier wird in anderen Dateien gesucht.
        for(String tkb: possibleBlocks) {
            if(this.sameOccurencesInAllSequenzes(tkb, other_sequenzes)) {
                blocksInAllSequenzes.add(tkb);
            }
        }
        
//        System.out.println("Es gibt " + blocksInAllSequenzes.size() + " mögliche Bausteine, die in allen Sequenzen gleich häufig vorkommen.");
//        System.out.println("Diese Bausteine sind:");
//        System.out.println(blocksInAllSequenzes);
        
        //Bausteine entfernen, die eindeutig Bestandteil eines anderen Bausteins sind.
        removeBlocks = new ArrayList<>();
        for(String b: blocksInAllSequenzes) {
            for(String b_: blocksInAllSequenzes) {
                if(!b.equals(b_)) {
                    if(this.countBlock(b_) == this.countBlock(b)) {
                        if(b.length() < b_.length()) {
                            if(b_.contains(b)) {
                                removeBlocks.add(b);
                            }
                        } else if(b.length() > b_.length()) {
                            if(b.contains(b_)) {
                                removeBlocks.add(b_);
                            }
                        }
                    }
                }
            }
        }
        
        blocksInAllSequenzes.removeAll(removeBlocks);
        //removeBlocks.clear();
        
//        System.out.println("Entfernt man nun kleinere Bausteine, die bereits in anderen Bausteinen gleich häufig vorkommen, erhaält man: ");
//        System.out.println(blocksInAllSequenzes);
        
        List<String> splitedSequenzes;
        
        String falseBlock = findFalseBlock(minChars, this.sequenz, blocksInAllSequenzes);
        if(falseBlock != null && !"".equals(falseBlock)) {
            System.out.println("Es wurde ein Baustein hinzugefügt, der kein echter Baustein ist. - Der Block ist " + falseBlock);
            blocksInAllSequenzes.remove(falseBlock);
            //splitedSequenzes = this.splitSequenz(this.sequenz, blocksInAllSequenzes);
        } else if("".equals(falseBlock)) {
            System.out.println("Es wurde ein Baustein hinzugefügt, der kein echter Baustein ist. - Der Block konnte nicht identifiziert werden.");
            System.out.println("Die Ausgabe läuft somit falsch weiter.");
        }
        
        String[] result = new String[blocksInAllSequenzes.size()];
        result = blocksInAllSequenzes.toArray(result);
        
        return result;
    }
    
    
    /**************************************************************************
     * Sequenz anhand der bekannten Bausteine in Teilsequenzen aufteilen      *
     **************************************************************************/
    
    /**
     * 
     * @param theSequenz
     * @param blocks
     * @return 
     */
    public List<String> splitSequenz(String theSequenz, List<String> blocks) {
        String[] splitedSequenz;
        List<String> subSequenzes = new ArrayList<>();
        
        String pattern = "";
        for(String block: blocks) {
            pattern += "(" + block + ")|";
        }
        pattern = pattern.substring(0, pattern.length()-1);
        
//        System.out.println("Pattern: " + pattern);
        
        splitedSequenz = theSequenz.split(pattern);
        
        for(String tmp: splitedSequenz) {
            if(!"".equals(tmp) && !subSequenzes.contains(tmp) ) {
                subSequenzes.add(tmp);
            }
        }
        
        return subSequenzes;
    }
    
    
    /**************************************************************************
     * Falsche Bausteine aussortieren                                         *
     **************************************************************************/
        
    /**
     * Falls ein Baustein hinzugefügt wurde, der die Sequenz in eine unzulässige
     * Teilsequenz zerlegt, wird dieser zurückgegeben.
     * 
     * @param minChars
     * @param theSequenz
     * @param blocks
     * @return 
     */
    public String findFalseBlock(int minChars, String theSequenz, List<String> blocks) {
        List<String> splitedSequenzes = this.splitSequenz(theSequenz, blocks);
        boolean seq_part_to_short = false;
        List<String> tmp_splitedSequenzes;
        
            
        //Überprüfen, ob die Teilsequenzen mindestens minChars lang sind.
        for (Iterator<String> it = splitedSequenzes.iterator(); it.hasNext();) {
            String seq_part = it.next();
            if(seq_part.length() < minChars && !seq_part.isEmpty()) {
                System.out.println("Es wurde ein Baustein hinzugefügt, der kein echter Baustein ist.");
                seq_part_to_short = true;
                break;
            }
        }
        //Ggf. alten Baustein entfernen.
        if(seq_part_to_short) {
//            boolean foundFailure = false;
            seq_part_to_short = false;
            List<String> tmp_foundBlocks = new ArrayList<>();
            tmp_foundBlocks.addAll(blocks);

//                for(int j = 0; j < foundBlocks.size(); j++) {

                for (Iterator<String> it_fB = blocks.iterator(); it_fB.hasNext();) {
                    String removeBlock = it_fB.next();
                    tmp_foundBlocks.remove(removeBlock);
                    tmp_splitedSequenzes = this.splitSequenz(theSequenz, tmp_foundBlocks);

                    for (Iterator<String> it = tmp_splitedSequenzes.iterator(); it.hasNext();) {
                        String seq_part = it.next();
                        if(seq_part.length() < minChars && !seq_part.isEmpty()) {
                            seq_part_to_short = true;
                            break;
                        }
                    }
                    if(!seq_part_to_short) {
                        return removeBlock;
//                        System.out.println("Der Baustein, der kein richtiger Baustein ist, wurde gefunden. - Der Baustein war [" + removeBlock + "]");
                    } else {
                        tmp_foundBlocks.add(removeBlock);
                    }
                }

                return "";
//                }
        }
        
        return null;
    }
    
    
    /**************************************************************************
     * Vollständige Bausteinsuche                                             *
     **************************************************************************/
    
    /**
     * ...
     * 
     * @param minChars
     * @param maxChars
     * @param other_sequenzes
     * @param knownBlocks
     * @return 
     */
    public String[] searchAllBlocks(int minChars, int maxChars, Analyse[] other_sequenzes, List<String> knownBlocks) {
        //Variablen
        int seqLength = this.sequenz.length();
        
        List<String> tmp_blocks = new ArrayList<>();
        
        String tmp_seq;
        
        List<String> foundBlocks = new ArrayList<>();
        
        List<String> removeBlocks = new ArrayList<>();
        
        //Andere Liste, da diese anosonsten in einer Schleife nicht bearbeitet werden kann.
        List<String> splitedSequenzes = new CopyOnWriteArrayList<>();
        
        List<String> tmp_knownBlocks = new ArrayList<>();
        foundBlocks.addAll(knownBlocks);
        
        //Übergabewerte überprüfen
        if(minChars <= 0) {
//            System.out.println("Alle Bausteine sollten mindestens einen Buchstaben enthalten.");
            minChars = 1;
        }
        if(maxChars >= seqLength) {
//            System.out.println("Ein Baustein muss Teil einer Sequenz sein.");
            maxChars = seqLength/2;
        }
        
        //knownBlocks = Arrays.asList(this.searchAllBlocksWithSameOccurences(minChars, maxChars, other_sequenzes, knownBlocks));
        
        splitedSequenzes.add(this.sequenz);
        
        //Maximal 10 Durchläufe sollten ausreichen, um alle Bausteine zu ermitteln.
        for(int i = 0; i < 10; i++) {
            if(splitedSequenzes.isEmpty()) {
//                System.out.println("Es sind keine Teilsequenzen mehr vorhanden");
                break;
            }
            
            splitedSequenzes = this.splitSequenz(this.sequenz, foundBlocks);
            
            //Falls EIN falscher Baustein hinzugefügt wurde, kann man ihn hiermit entfernen lassen
//            String falseBlock = findFalseBlock(minChars, this.sequenz, foundBlocks);
//            if(falseBlock != null) {
//                System.out.println("Es wurde ein Baustein hinzugefügt, der kein echter Baustein ist. - Der Block ist " + falseBlock);
//                foundBlocks.remove(falseBlock);
//                splitedSequenzes = this.splitSequenz(this.sequenz, foundBlocks);
//            } else if("".equals(falseBlock)) {
//                System.out.println("Es wurde ein Baustein hinzugefügt, der kein echter Baustein ist. - Der Block konnte nicht identifiziert werden.");
//            } 
            
            
//            System.out.println("Es gibt nun folgende Teilsequenzen:");
//            System.out.println(splitedSequenzes);

            //Ermittelte Teilsequenzen nach Bausteinen durchsuchen
            tmp_knownBlocks.addAll(foundBlocks);
            boolean found;
            int oc = 0;
            
            Iterator<String> iterator_s = splitedSequenzes.iterator();
            while(iterator_s.hasNext()) {
                String seq = iterator_s.next();
                
                if(seq.length() <= maxChars & seq.length() > 0) {
                    found = false;
                    for(String kb: tmp_knownBlocks) {
                        if(countMultiple(kb, seq) == 0) {
                            if((seq.contains(kb) || kb.contains(seq))) {
                                //if(kb.contains(seq + seq))
                                found = true;
                                break;
                            }
                        } else {
                            oc = countMultiple(kb, seq);
                            break;
                        }
                    }
                    if(!found) {
                        tmp_seq = extendString(seq, oc);
                        if(tmp_knownBlocks.contains(tmp_seq)) {
                            tmp_knownBlocks.remove(tmp_seq);
                        }
                        tmp_knownBlocks.add(seq);
                    }
                }
            }
            
//            knownBlocks.clear();
            foundBlocks.clear();
            foundBlocks.addAll(tmp_knownBlocks);
            tmp_knownBlocks.clear();
            
            //Bausteine ggf. unterteilen.
            for (Iterator<String> it = foundBlocks.iterator(); it.hasNext();) {
                String b = it.next();
                
                String multiBlock = this.findMultiple(b, minChars);
                if(multiBlock != null) {
//                    System.out.println("Entferne den Baustein " + b + " und füge " + multiBlock + " hinzu.");
                    tmp_blocks.add(multiBlock);
                    removeBlocks.add(b);
                }
            }
            foundBlocks.addAll(tmp_blocks);
            foundBlocks.removeAll(removeBlocks);
            
            //Entfernt Bausteine, die aus anderen zusammengesetzt sind.
            tmp_blocks = this.findCombinedBlocks(foundBlocks);
            if(!tmp_blocks.isEmpty()) {
                foundBlocks.removeAll(tmp_blocks);
            }
            
//            System.out.println("Alle bisher bekannten Bausteine sind.");
//            System.out.println(foundBlocks);
        }
        
        //Bausteine ggf. in kleinere Bausteine aufteilen.
        
        //foundBlocks = knownBlocks;
        tmp_seq = this.sequenz;
        //Überprüfen, ob alle Blocks gefunden wurden.
        
        String pattern = "";
        for(String block: foundBlocks) {
            pattern += "(" + block + ")|";
        }
        pattern = pattern.substring(0, pattern.length()-1);
        tmp_seq = tmp_seq.replaceAll(pattern, " ");
        tmp_seq = tmp_seq.replaceAll(" +", " ");
        
//        System.out.println("Alle gefunden Bausteine sind:");
//        System.out.println(foundBlocks);
//        
        if(tmp_seq.trim().length() == 0) {
            System.out.println("Es wurden alle Bausteine dieser Sequenz gefunden.");
        } else {
            System.out.println("Es wurden mehrere Bausteine nicht gefunden. Diese sind in den folgenden Teilsequenz enthalten:");
            System.out.println(tmp_seq);
        }
        
        //... mit allen anderen Dateien vergleichen
        String[] result = new String[foundBlocks.size()];
        result = foundBlocks.toArray(result);
        
        return result;
    }
    
    
    /**************************************************************************
     * Finde alle Bausteine, die aus kleineren Bausteinen bestehen, zurück    *
     **************************************************************************/
    
    /**
     * Gibt eine Liste der vermeintlichen Bausteine zurück, die aus anderen
     * Bausteinen bestehen.
     * 
     * @param blocks
     * @return 
     */
    public List<String> findCombinedBlocks(List<String> blocks) {
        List<String> combinedBlocks = new ArrayList<>();
        String tmp;
        String tmp_b;
        
        Iterator<String> it;
        
        it = blocks.iterator();
        while(it.hasNext()) {
            tmp = it.next();
            
            
            Iterator<String> itb = blocks.iterator();
            while(itb.hasNext()) {
                tmp_b = itb.next();

                if(tmp.equals(tmp_b)) {
                    continue;
                } else {
                    if(tmp_b.contains(tmp) && !combinedBlocks.contains(tmp_b)) {
                        combinedBlocks.add(tmp_b);
                    }
                }

            }
            
        }
        
        if(!combinedBlocks.isEmpty()) {
//            System.out.println("Entferne folgende Bausteine: ");
//            System.out.println(combinedBlocks);
        }
        return combinedBlocks;
    }
    
    
    /**************************************************************************
     * Funktionen zum Überprüfen von Strings                                  *
     **************************************************************************/
    
    /**
     * Überprüft einen String, ob dieser vollständig durch Teilstring
     * gebildet wird. Falls ja, wird die Häufigkeit der Wiederholung des
     * Teilstrings zurückgegeben.
     * 
     * @param str
     * @param substr
     * @return 
     */
    public int countMultiple(String str, String substr) {
        int str_length = str.length();
        int substr_length = substr.length();
        if(str_length < substr_length) {
            return 0;
        }
        if(str_length == substr_length) {
            if(str.equals(substr)) {
                return 1;
            }
            return 0;
        }
        if(str_length > substr_length) {
            if(str_length % substr_length == 0) {
                int oc = 0;
                for(int i = 0; i <= str_length - substr_length; i += substr_length) {
                    if(!substr.equals(str.subSequence(i, i+substr_length))) {
                        return 0;
                    }
                    oc++;
                }
                return oc;
            }
            return 0;
        }
        return 0;
    }
    
    /**
     * Gibt einen String zurück, der den substr times mal enthält.
     * 
     * @param substr
     * @param times
     * @return 
     */
    public String extendString(String substr, int times) {
        String str = "";
        for(int i = 0; i < times; i++) {
            str += substr;
        }
        return str;
    }
    
    /**
     * Falls ein String aus einer sich wiederholenden Zeichenkette besteht, 
     * wird diese Zeichenkette zurückgegeben.
     * 
     * @param str
     * @return 
     */
    public String findMultiple(String str) {
        return this.findMultiple(str, 2);
    }
    
    /**
     * Falls ein String aus einer sich wiederholenden Zeichenkette besteht, 
     * wird diese Zeichenkette zurückgegeben.
     * Die Zeichenkette ist mindestens minChars lang.
     * 
     * @param str
     * @param minChars
     * @return 
     */
    public String findMultiple(String str, int minChars) {
        int strLength = str.length();
        String multipleWord;
        
        for(int i = minChars; i <= strLength/2 - minChars; i++) {
            for(int pos = 0; pos < (strLength/2 - i); pos++) {
                multipleWord = str.substring(pos, pos+i);
                if(this.countMultiple(str, multipleWord) > 0) {
                    return multipleWord;
                }
            }
        }
        
        return null;
    }
    
    /**
     * Bestimmt, ob der übergebene String ein Vielfaches eines kleineren Strings
     * ist.
     * 
     * @param str
     * @param minChars
     * @return 
     */
    public boolean isMultiple(String str, int minChars) {
        if(null != this.findMultiple(str, minChars)) {
            return true;
        } else {
            return false;
        }
    }
}