package geneFinder;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.Vector;

import types.*;


/**
 * extracts high-coverage clusters as potential genes, includes alternative splicing
 * Copyright (c) 2013,
 * Franziska Zickmann, 
 * ZickmannF@rki.de, Robert Koch-Institute, Berlin, Germany
 * Distributed under the GNU Lesser General Public License, version 3.0
 */

public class ExtractGeneCandidates {
	

    // variables for log file
	
	public static int numMergedClusters;
	public static int numFoundNoStart_firstTime;
	public static int numFoundNoStop_firstTime;
	public static int numNoFrameFound;
	
	/*
	 * method to analyze the mapping -> extract clusters of high coverage and assign start and stop codons 
	 */
	
	public int initializeClusterSearch(String nameRefFile){

		File refFile;
		if(GeneFinder.useTopHat){
			refFile = new File(nameRefFile+".fa");
		}else{
			refFile = new File(nameRefFile+".fasta");
		}

		int id = 1;

		for(String contigName : GeneFinder.mappedContigs.keySet()){
			Contig thisContig = GeneFinder.mappedContigs.get(contigName);
			StringBuffer contigSeq = new StringBuffer();

			try{ 
				BufferedReader br = new BufferedReader(new FileReader(refFile));
				String line;

				while((line = br.readLine()) != null){
					if(line.startsWith(">")){
						// test if correct contig
						if(line.substring(1).startsWith(contigName)){
							if(!((line.substring(1).startsWith(contigName+" ")) || (line.substring(1).length() == contigName.length()))){
								continue;			// as an additional check to avoid picking the wrong contig because of name sub-similarities 											
							}
							// found right one, now extract sequence
							while(((line = br.readLine()) != null) && (line.length() != 0) &&  (!(line.startsWith(">")))){
								String line2 = "";
								if(Character.isLowerCase(line.charAt(0))){
									for(int i = 0;i<line.length();i++){
										char letter = line.charAt(i);
										letter = Character.toUpperCase(letter);
										line2 += letter;
									}
								}else{
									line2 = line;
								}
								contigSeq.append(line2);
							}
							break;
						}
					}
				}

				if(contigSeq.length() == 0){
					// oops, did not found contig
					System.out.println("Error, could not find contig " + contigName);
					System.exit(0);
				}
		
				// now that we have the sequence, search for areas with high coverage
				Runtime r = Runtime.getRuntime();

				id = searchClusters(thisContig, id, contigSeq);

				double memBef_2 = (r.totalMemory()-r.freeMemory());

				thisContig.positionTOmappingRnas.clear();
				thisContig.positionTOmappingRnas = new TreeMap<Integer,Vector<Rna>>();
				thisContig.splicePositions.clear();
				thisContig.splicePositions =  new TreeMap<Integer,Integer>();
				thisContig.positionTOdiff.clear();
				thisContig.positionTOdiff = new TreeMap<Integer,Integer>();
				contigSeq = null;
				r.gc();
				r.gc();

				double memAft_2 = (r.totalMemory()-r.freeMemory());
				if(!GeneFinder.secondPart){
					System.out.println("Memory freed = " + (((memBef_2-memAft_2)/1000.0)/1000.0) + "MB");
					System.out.println();
				}

			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}  catch (IOException e) {
				e.printStackTrace();
			}
		}

		return id;

	}
	
	/*
	 * searches potential genes on forward and reverse strand, extracts the specific regions respecting reading frame (if possible)
	 */
	
	public int searchClusters(Contig thisContig, int id, StringBuffer contigSeq){

		if(GeneFinder.noAmbiOpti){
			HelperFunctions_GeneSearch.removeAmbiHits(thisContig);
		}
		
		Iterator<Integer> positionIt = thisContig.positionTOmappingRnas.keySet().iterator();
		
		if(GeneFinder.iteration == 2 && !GeneFinder.secondPart){
			System.out.print("Iteration 2. ");
			WriteOutput.writeToLogFile("Iteration 2. ");
		}
		if(!GeneFinder.secondPart){
			System.out.println("Contig: = " + thisContig.contigName);
			WriteOutput.writeToLogFile("Contig: = " + thisContig.contigName + "\n\n");
		}

		int spliceKey = -1;                             // this is important to always know the next spliceSite without checking all rnas each time
		if(!thisContig.splicePositions.isEmpty()){
			spliceKey = thisContig.splicePositions.firstKey();
		}
		
		int startPos = 0;
		int currentPos = 0;
		int nextPos = 0;
		
		// variables for log file
		
		numMergedClusters = 0;
		numFoundNoStart_firstTime = 0;
		numFoundNoStop_firstTime = 0;
		numNoFrameFound = 0;

		if(positionIt.hasNext()){
			currentPos = positionIt.next();
			while((spliceKey != -1) && (currentPos > spliceKey) && (thisContig.splicePositions.higherKey(spliceKey) != null)){
				spliceKey = thisContig.splicePositions.higherKey(spliceKey);   //update the key until it appears AFTER the current position
			}
		}
		
		// initialize the coverage handling:
		Object[] coverageVecAndPos = new Object[3];
		Vector<Integer> coverageVec = new Vector<Integer>();   // vector is preferred over array because more flexible
		// first the coverage is zero at all positions:
		for(int arrPos=0;arrPos<GeneFinder.readLength;++arrPos){
			coverageVec.add(0);
		}
		
		coverageVecAndPos[0] = coverageVec;
		coverageVecAndPos[1] = currentPos;
		coverageVecAndPos[2] = -1;
		
		if(GeneFinder.spliceLim == -1){
			GeneFinder.spliceLim = GeneFinder.minCoverage;
		}
		if(GeneFinder.endCoverage == -1){
		
			GeneFinder.endCoverage = (1.0/3.0)*GeneFinder.minCoverage - 0.001; 
			
			if(!GeneFinder.secondPart){
				System.out.println("End coverage estimated from required minimum coverage: " + GeneFinder.endCoverage);
				WriteOutput.writeToLogFile("End coverage estimated from required minimum coverage: " + GeneFinder.endCoverage + "\n\n");
			}
			
		}
		
		if(GeneFinder.maxCov == -1){
			GeneFinder.maxCov = Double.MAX_VALUE;  // so we always accept the coverage
		}
	
		boolean noMoreCluster = false;
		boolean startedNewCluster = false;   // this boolean ensures that also the last cluster is completed once it has been started due to sufficient coverage (otherwise, if map is empty, cluster is not extracted)
		boolean doNotCountTwice = false;	 // if true we do not perform the coverageVec update for the first current position (when starting a new cluster) because this has already been done with "nextPos"
		
		int numIdentifiedClusters = 0;       // if = 1, this ends the while loop
		
		Vector<Gene> temporaryGenes = new Vector<Gene>();   // contains all candidate genes that have been derived by regarding isoforms completely spanned by introns
		
		Object[] proceedFromIsoStartStuff = new Object[2];  // 1: int , 2: reads
		proceedFromIsoStartStuff[0] = -1;
		proceedFromIsoStartStuff[1] = new Vector<Rna>();
		
		do{

			Gene cluster = new Gene();
			coverageVecAndPos[2] = -1;
			
			do{
				
				startedNewCluster = false;
			
				cluster.possibleIntrons.clear();
				
				int currentCompete = -1;  // stores the current alternative interval, is only -1,-1 if no alternatives exist for current position
				int currentCompeteStart = -1;  // defines the first split determining the currentCompete, necessary to not extract exons, that overlap with currentCompete region
				
				Vector<Rna> rnasThatDoNotSupportAnySplit = new Vector<Rna>();  // contains all rnas that do not support any splits
				int localExonEnd = -1;
				int localSpliceSite = -1;
				int searchTranscriptStart = -1;  // indicates whether a transcript starts within an intron
				int inTranscriptMode = -1;		// indicates if we currently also respect a newly opened transcript
				int inNormalTranscript = -1;	// indicates whether we closed a new transcript within an intron or with an overlap to the normal transcript
				int endToPassForclosedGenes = -1;  // when we have to close a gene within a currentCompete interval, it is important to grab the last included position as the end
				boolean chooseAlternativeEndInstead = false;
						
				int acceptReads = 0;
				Vector<Rna> isoformRnas = new Vector<Rna>();
				boolean goToIni = false;
				
				TreeMap<Integer,Vector<Integer>> posiCovMap = new TreeMap<Integer,Vector<Integer>>();  // for each intron, the coverage add after begin and end ist stored
				
				int diff = 0;
				int considerSpliceSiteForCovUpdate = -1;	// with alternative splicing, all positions after splice site would could twice, so take it into account
				
				if(!doNotCountTwice){
				
					if(!(spliceKey < 0) && ((currentPos+GeneFinder.readLength)-spliceKey >= 0) ){  // note,old: && (thisContig.splicePositions.get(spliceKey) >= GeneFinder.spliceLim)
					
						// find splits associated with this site, only take introns into account that are supported by a sufficient number of reads		
	
						double limit = 1; // formerly geneFinder.spliceLim					
						Object[] returnObject = IntronExonSearch.findIntrons_RespectAlternative(cluster, thisContig, spliceKey, currentPos, currentCompete, posiCovMap,limit);
						
						currentCompete = (Integer) returnObject[0];
						if(currentCompete != -1){
							if(spliceKey < currentCompete){
								currentCompeteStart = spliceKey;
							}	
						}else{
							currentCompeteStart = -1;
						}
						
						posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnObject[1];
						rnasThatDoNotSupportAnySplit.addAll((Vector<Rna>) returnObject[2]);
						
						if(thisContig.splicePositions.get(spliceKey) >= GeneFinder.spliceLim){
							considerSpliceSiteForCovUpdate = spliceKey;
						}else{
							Vector<Integer> otherSpliceSites = ((Vector<Integer>) returnObject[3]);
							if(!otherSpliceSites.isEmpty()){
								int minSite = Integer.MAX_VALUE;
								for(int site : otherSpliceSites){
									if(site < minSite){
										minSite = site;
									}
								}
								considerSpliceSiteForCovUpdate = minSite;
							}
						}

					}

					int covPlus = thisContig.positionTOmappingRnas.get(currentPos).size();
					
					Object[] returnValues = updateCoverageInterval_respectAlternatives(thisContig,covPlus,currentPos,coverageVecAndPos,posiCovMap,considerSpliceSiteForCovUpdate);
					
					coverageVecAndPos = (Object[]) returnValues[0];
					posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnValues[1];
					
					if(thisContig.positionTOdiff.keySet().contains(currentPos)){
						diff = thisContig.positionTOdiff.get(currentPos); // if there occurred insertions or deletions before this positions add/subtract the difference
					}
					
				}else{
					if(!(spliceKey < 0) && ((currentPos+GeneFinder.readLength)-spliceKey >= 0)){ // note: old :  && (thisContig.splicePositions.get(spliceKey) >= GeneFinder.spliceLim)
					
						// we have a "valid" splice site within the next covered interval, so regard this in the update of the coverage vector			
						double limit = 1;
						Object[] returnObject = IntronExonSearch.findIntrons_RespectAlternative(cluster, thisContig, spliceKey, currentPos, currentCompete, posiCovMap,limit);
						
						currentCompete = (Integer) returnObject[0];
						
						if(currentCompete != -1){
							if(spliceKey < currentCompete){
								currentCompeteStart = spliceKey;
							}								
						}else{
							currentCompeteStart = -1;
						}
						posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnObject[1];
						rnasThatDoNotSupportAnySplit.addAll((Vector<Rna>) returnObject[2]);
						
						if(thisContig.splicePositions.get(spliceKey) >= GeneFinder.spliceLim){
							considerSpliceSiteForCovUpdate = spliceKey;
						}else{
							Vector<Integer> otherSpliceSites = ((Vector<Integer>) returnObject[3]);
							if(!otherSpliceSites.isEmpty()){
								int minSite = Integer.MAX_VALUE;
								for(int site : otherSpliceSites){
									if(site < minSite){
										minSite = site;
									}
								}
								considerSpliceSiteForCovUpdate = minSite;
							}
						}
					
					}
				}
				
				int[] normalExonAndDifferenceToIt = {-1,-1,-1};   // if an alternative transcript start merges with the normal exon, make sure that the difference between alternative and transcript is < readLength, so remember diff here

				boolean startWithGene = false;
				if(((Integer)proceedFromIsoStartStuff[0]).intValue() != -1){
					startWithGene = true;
					goToIni = false;
				}else if((((Vector<Integer>) coverageVecAndPos[0]).get(0) + diff >= GeneFinder.minCoverage) && (((Vector<Integer>) coverageVecAndPos[0]).get(0) + diff < GeneFinder.maxCov)){
					startWithGene = true;
				}
											
				if(startWithGene){
					
					double averageExonCov = 0; // is updated with every new read start and reflects the average coverage for the current exon
					int currentExonLength = 0; // length from start of the currentExon until current position
					int numReadsExon = 0; 	   // reads mapping this exon (intron read-ending are handled differently - average of posiCovMap added) 
										
					if(((Integer)proceedFromIsoStartStuff[0]).intValue() != -1){
						startPos = ((Integer)proceedFromIsoStartStuff[0]).intValue();
						HelperFunctions_GeneSearch.addRnasFromVector(cluster,(Vector<Rna>)proceedFromIsoStartStuff[1]);
						
						numReadsExon += ((Vector<Rna>)proceedFromIsoStartStuff[1]).size();
						
						proceedFromIsoStartStuff[0] = -1;
						((Vector<Rna>)proceedFromIsoStartStuff[1]).clear();
					}else{
						startPos = (Integer)coverageVecAndPos[2]; // potential start of a new cluster begins at currentPos - bases covered by already present rnas	
						if((Integer)coverageVecAndPos[2] == -1){
							startPos = currentPos;
						}

						int pos_temp = (Integer)coverageVecAndPos[2];

						if((Integer)coverageVecAndPos[2] != -1){
							do{							
								HelperFunctions_GeneSearch.addRnasFromVector(cluster,thisContig.positionTOmappingRnas.get(pos_temp));
								numReadsExon += thisContig.positionTOmappingRnas.get(pos_temp).size();
								
								if(thisContig.positionTOmappingRnas.higherKey(pos_temp) != null){
									pos_temp = thisContig.positionTOmappingRnas.higherKey(pos_temp);
								}else{
									pos_temp = currentPos;
								}
							}while((pos_temp != currentPos) && !(pos_temp > currentPos));
						}		
					}
							
					int posStartExon = startPos;
					currentExonLength = (currentPos - startPos) + GeneFinder.readLength;
					
					HelperFunctions_GeneSearch.addRnasFromVector(cluster,thisContig.positionTOmappingRnas.get(currentPos));
					numReadsExon += thisContig.positionTOmappingRnas.get(currentPos).size();
					
					averageExonCov = (double)((double)(numReadsExon * GeneFinder.readLength)/(double)currentExonLength);
					
					startedNewCluster = true;    
		
					Gene fakeGene = new Gene(); // this is the pseudo gene used to account for different isoforms
					
					while(positionIt.hasNext()){ 

						nextPos = positionIt.next();
						acceptReads = -1;

						while(!(spliceKey < 0) && (nextPos > spliceKey)){
							
							if((localExonEnd != -1) && (localSpliceSite != -1)){
								// update fussy exon for last local splice site, take the split as the end
								if(cluster.possibleIntrons.containsKey(localSpliceSite)){
									cluster.possibleIntrons.get(localSpliceSite)[2] = spliceKey;
									cluster.possibleIntrons.get(localSpliceSite)[3] = 0;
									localExonEnd = -1;
								}else{
									System.err.println("Splice site not included!");
								}
							}
							
							
							double limit = GeneFinder.spliceLim;   // according to average exon coverage specify a threshold that has to be exceeded
							int rivalNum = 0;   // number of competitors at this site (they "share" the overall coverage)
							
							if(cluster.possibleFussyExons.containsKey(spliceKey)){
								rivalNum++;
							}
							if((cluster.possibleIntrons.containsKey(spliceKey)) && (cluster.possibleIntrons.get(spliceKey)[0] != null)){
								int intronNum = ((Vector<int[]>) cluster.possibleIntrons.get(spliceKey)[0]).size();
								rivalNum += intronNum;
							}
							
							if(rivalNum > 0){
								limit = ((double)(averageExonCov/(double)rivalNum))*0.1;
							}
							
							Object[] returnCheck = IntronExonSearch.checkIntronsAfterSpliceSiteSwitch(cluster,spliceKey,currentCompete, posiCovMap,nextPos,limit);
							
							boolean spliceSiteNotContained = (Boolean) returnCheck[3];
							if(!spliceSiteNotContained){
								currentCompete = (Integer) returnCheck[0];  // if splice site not contained, this can mean that it was simply not above the limit or that it has been included in fake gene
							}
							posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnCheck[1];
							boolean switchedFromFussyToExon = (Boolean) returnCheck[2];
							
							if(currentCompete != -1){
								if(!switchedFromFussyToExon && spliceKey < currentCompete && (currentPos > currentCompeteStart) && (thisContig.splicePositions.get(spliceKey) >= GeneFinder.spliceLim)){
									currentCompeteStart = spliceKey;
								}	
							}else{
								currentCompeteStart = -1;
							}
							
							if(!switchedFromFussyToExon && (rnasThatDoNotSupportAnySplit.size() != 0)){
								Object[] returnVal = checkIfNonSplitRnasAreSufficient(cluster, rnasThatDoNotSupportAnySplit, spliceKey, currentPos,posiCovMap,limit);	
								localExonEnd = (Integer) returnVal[0];
								posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnVal[1];
								localSpliceSite = (Integer) returnVal[2];  // is only set to local splice site if the non split rnas are kept
							}else{  
								localSpliceSite = -1;
								localExonEnd = -1;
							}
						
							rnasThatDoNotSupportAnySplit.clear();
							
							if((thisContig.splicePositions.higherKey(spliceKey) == null)){
								spliceKey = -1;
							}else{
								spliceKey = thisContig.splicePositions.higherKey(spliceKey);
							}

							considerSpliceSiteForCovUpdate = -1;
							if(inNormalTranscript != -1){
								int[] intronEndFound = HelperFunctions_GeneSearch.checkIfNextPosMergesWithPreviousIsoform(cluster,nextPos,inNormalTranscript);
								if((intronEndFound[0] != -1) && (intronEndFound[1] == 1)){
									inNormalTranscript = -1;
								}
							}
						}

						if(!(spliceKey < 0 ) && ((nextPos+GeneFinder.readLength)-spliceKey > 0)){ 
						
							acceptReads = 1;  // indicates that we deal with a split, so make sure that all rnas are included in normal candidate if we proceed
							if((inTranscriptMode != -1) || (searchTranscriptStart != -1) || (inNormalTranscript != -1)){
								// test in general if we merged with normal transcript, if not, break
								
								int[] intronEndFound = HelperFunctions_GeneSearch.checkIfNextPosMergesWithPreviousIsoform(cluster,nextPos,inTranscriptMode);
							
								if(intronEndFound[0] == -1){
									
									// check if split is leading to a normal exon
									
									Vector<Integer> theseEnds = IntronExonSearch.findIntronEnds(spliceKey,nextPos,thisContig);
									
								    acceptReads = -1; // if -1 we do not accept
								    
									for(int thisEnd : theseEnds){
										int[] intronEndMerges = HelperFunctions_GeneSearch.checkIfIntronEndsInNormalTranscript(cluster,thisEnd,inTranscriptMode);
										
										if(intronEndMerges[0] != -1){
											acceptReads = 1;
											break;
										}
									}
									
									
									if(acceptReads == -1){
										
										if(inTranscriptMode != -1){
											
											// make sure that the fake gene gets a chance in next extraction
											
											proceedFromIsoStartStuff[0] = inTranscriptMode;
											((Vector<Rna>)proceedFromIsoStartStuff[1]).addAll(isoformRnas);
											fakeGene = new Gene();
											isoformRnas.clear();
										}
									
										// we do not want to proceed with this candidate, but rather close it at endToPassForClosedGenes
										
										if(endToPassForclosedGenes != -1){
											chooseAlternativeEndInstead = true;
										}
										
										goToIni = true;
									}
									
								}
							}
							
							if(!goToIni){
								// find splits associated with this site, only take introns into account that are supported by a sufficient number of reads		
								
								Object[] returnObject = new Object[4];
								
								double limit = 1;
								returnObject = IntronExonSearch.findIntrons_RespectAlternative(cluster, thisContig, spliceKey, nextPos, currentCompete, posiCovMap,limit);
								currentCompete = (Integer) returnObject[0];
								rnasThatDoNotSupportAnySplit.addAll((Vector<Rna>) returnObject[2]);
								
								if(currentCompete != -1){
									if(spliceKey < currentCompete && (currentPos > currentCompeteStart)){
										currentCompeteStart = spliceKey;
									}							
								}else{
									currentCompeteStart = -1;
								}
								
								posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnObject[1];
								
								if(thisContig.splicePositions.get(spliceKey) >= GeneFinder.spliceLim){
									considerSpliceSiteForCovUpdate = spliceKey;
								}else{
									Vector<Integer> otherSpliceSites = ((Vector<Integer>) returnObject[3]);
									if(!otherSpliceSites.isEmpty()){
										int minSite = Integer.MAX_VALUE;
										for(int site : otherSpliceSites){
											if(site < minSite){
												minSite = site;
											}
										}
										considerSpliceSiteForCovUpdate = minSite;
									}
								}
							}
								
						}

						int covPlusNext = thisContig.positionTOmappingRnas.get(nextPos).size();
						
						Object[] returnValues = updateCoverageInterval_respectAlternatives(thisContig,covPlusNext,nextPos,coverageVecAndPos,posiCovMap,considerSpliceSiteForCovUpdate);
						
						coverageVecAndPos = (Object[]) returnValues[0];
						posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnValues[1];
				
						if(thisContig.positionTOdiff.keySet().contains(nextPos)){
							diff = thisContig.positionTOdiff.get(nextPos); // if there occurred insertions or deletions before this positions add/subtract the difference
						}else{
							diff = 0;
						}

						if(!goToIni && ((nextPos - currentPos) <= (GeneFinder.readLength)) && (((Vector<Integer>) coverageVecAndPos[0]).get(0) + diff > GeneFinder.endCoverage) && (((Vector<Integer>) coverageVecAndPos[0]).get(0) + diff < GeneFinder.maxCov)){

							if((inTranscriptMode != -1) || (searchTranscriptStart != -1)){
								int[] intronEndFound = HelperFunctions_GeneSearch.checkIfNextPosExceedsIntronEnd(cluster,nextPos,inTranscriptMode);

								if(searchTranscriptStart != -1){
									if((intronEndFound[0] == -1) && (((Vector<Integer>) coverageVecAndPos[0]).get(0) + diff >= GeneFinder.minCoverage)){
										// start new isoform now


										inTranscriptMode = searchTranscriptStart;

										fakeGene = new Gene();								
										fakeGene.startPos = searchTranscriptStart;
										if(inNormalTranscript != -1){
											if(searchTranscriptStart > inNormalTranscript){
												inNormalTranscript = searchTranscriptStart;
											}
										}else{
											inNormalTranscript = searchTranscriptStart;
										}

										searchTranscriptStart = -1;

									}else if(intronEndFound[0]  != -1){
										// already passed the "normal" transcript, so forget about alternative one
										searchTranscriptStart = -1;
										isoformRnas.clear();
										fakeGene = new Gene();
										if(intronEndFound[1] == 1){
											inNormalTranscript = -1;
										}
									}
								}
								if((inTranscriptMode != -1) && (intronEndFound[0]  != -1)){

									if((normalExonAndDifferenceToIt[0] == intronEndFound[0]) && (normalExonAndDifferenceToIt[1] == -1)){
										
										int[] transcriptStarts = FrameSearch.lookForStartOfIsoform(inTranscriptMode,contigSeq);
										cluster.alternativeTranscripts.add(new Object[] {inTranscriptMode,inTranscriptMode, transcriptStarts[0],transcriptStarts[1]});
										HelperFunctions_GeneSearch.addRnasFromVector(cluster,isoformRnas);
										
										isoformRnas.clear();
										fakeGene = new Gene();
										
										IntronExonSearch.replaceEntryInAltTransWithUpdatedOne_endTranscript(cluster, inTranscriptMode, currentPos, normalExonAndDifferenceToIt[2]);
										normalExonAndDifferenceToIt[0] = -1;
										normalExonAndDifferenceToIt[1] = -1;
										normalExonAndDifferenceToIt[2] = -1;
									}else{
										
										// this time we do not need to extract a fake gene
										int[] transcriptStarts = FrameSearch.lookForStartOfIsoform(inTranscriptMode,contigSeq);
										cluster.alternativeTranscripts.add(new Object[] {inTranscriptMode,inTranscriptMode, transcriptStarts[0],transcriptStarts[1]});
										HelperFunctions_GeneSearch.addRnasFromVector(cluster,isoformRnas);
										
										isoformRnas.clear();
										fakeGene = new Gene();
										
										IntronExonSearch.replaceEntryInAltTransWithUpdatedOne(cluster, inTranscriptMode, currentPos, intronEndFound[0],nextPos);
									}

									inTranscriptMode = -1;
									searchTranscriptStart = -1;		
									if(intronEndFound[1] == 1){
										inNormalTranscript = -1;
									}
								}

							}
	
							if(inNormalTranscript != -1){
								int[] intronEndFound = HelperFunctions_GeneSearch.checkIfNextPosMergesWithPreviousIsoform(cluster,nextPos,inNormalTranscript);
								if((intronEndFound[0] != -1) && (intronEndFound[1] == 1)){
									inNormalTranscript = -1;
									isoformRnas.clear();
									fakeGene = new Gene();
								}
							}
							
							if((searchTranscriptStart == -1) && (inTranscriptMode == -1) && (inNormalTranscript == -1)){
								endToPassForclosedGenes = nextPos;
							}
							
							currentPos = nextPos;
							
							if(currentPos >= currentCompete){
								currentCompete = -1;
								currentCompeteStart = -1;
							}
						
							if(((searchTranscriptStart != -1) || (inTranscriptMode != -1) || (inNormalTranscript != -1)) && (acceptReads != 1)){
								
								isoformRnas.addAll(thisContig.positionTOmappingRnas.get(currentPos));
							}else{
								HelperFunctions_GeneSearch.addRnasFromVector(cluster,thisContig.positionTOmappingRnas.get(currentPos));
							}					

							currentExonLength = (currentPos - posStartExon) + GeneFinder.readLength;
							
							numReadsExon += thisContig.positionTOmappingRnas.get(currentPos).size();
							
							averageExonCov = (double)((double)(numReadsExon * GeneFinder.readLength)/(double)currentExonLength);
							
							
							if(localExonEnd != -1){
								localExonEnd = currentPos + GeneFinder.readLength;
							}
							
						}else{
							boolean closeGene = false;
							
							if(goToIni){
								currentCompete = -1;
								currentCompeteStart = -1;
								if(endToPassForclosedGenes != -1){
									chooseAlternativeEndInstead = true;
								}

								closeGene = true;
							}else{

								int basesToAddForOverlap = 0;
								int positionPosiCovMap = HelperFunctions_GeneSearch.findIntronNearNextPos(cluster,nextPos);

								if(posiCovMap.containsKey(positionPosiCovMap)){
									basesToAddForOverlap = posiCovMap.get(positionPosiCovMap).size();
								}

								if(inTranscriptMode != -1){
									int[] intronEndFound  = HelperFunctions_GeneSearch.checkIfNextPosExceedsIntronEnd(cluster,nextPos,inTranscriptMode);
									if(intronEndFound[0] != -1){
										if(((nextPos - currentPos) <= (GeneFinder.readLength))){
											// put isoform to normal gene candidate
											int[] transcriptStarts = FrameSearch.lookForStartOfIsoform(inTranscriptMode,contigSeq);
											cluster.alternativeTranscripts.add(new Object[] {inTranscriptMode,inTranscriptMode, transcriptStarts[0],transcriptStarts[1]});
											HelperFunctions_GeneSearch.addRnasFromVector(cluster,isoformRnas);

											isoformRnas.clear();
											fakeGene = new Gene();

											IntronExonSearch.replaceEntryInAltTransWithUpdatedOne(cluster, inTranscriptMode, currentPos, intronEndFound[0],nextPos);
										}else{
											// put isoform as fake gene aside
											fakeGene.startPos = inTranscriptMode;
											HelperFunctions_GeneSearch.addRnasFromVector(fakeGene,isoformRnas);
											fakeGene.geneID = id++;
											fakeGene.stopPos = currentPos + GeneFinder.readLength;
											temporaryGenes.add(fakeGene);

											fakeGene = new Gene();
											isoformRnas.clear();										

										}

										inTranscriptMode = -1;
										searchTranscriptStart = -1;

										if(intronEndFound[1] == 1){
											inNormalTranscript = -1;
										}
									}else if(positionPosiCovMap != -1){
										normalExonAndDifferenceToIt[0] = positionPosiCovMap;
										if(((nextPos - currentPos) <= (GeneFinder.readLength))){
											normalExonAndDifferenceToIt[1] = 0;
										}else{
											normalExonAndDifferenceToIt[1] = -1;
											normalExonAndDifferenceToIt[2] = currentPos; // indicates the end that we want
										}

									}
								}

								if((currentCompeteStart != -1 && ((currentCompeteStart - currentPos) < GeneFinder.readLength)) && ((currentCompete + basesToAddForOverlap + 1) >= nextPos) && (((Vector<Integer>) coverageVecAndPos[0]).get(0) + diff < GeneFinder.maxCov)){   // old: ((currentCompete+GeneFinder.readLength+1) >= nextPos)
									// +1 because currentCompete defines the intron end
									// have to go on because we are still within the cluster, so just decide if this part shall be included or not								

									int goOn = 0;
									int spliceStartAfterCurrentPos = -1;
									if((nextPos - currentPos) > (GeneFinder.readLength)){

										int[] returnValuesCheckGoOn = new int[3];

										if((inTranscriptMode != -1) || (searchTranscriptStart != -1) || (inNormalTranscript != -1)){
											returnValuesCheckGoOn = IntronExonSearch.checkIfIntronSupported_fakeGene(cluster, currentPos, nextPos, posiCovMap, inTranscriptMode,searchTranscriptStart,endToPassForclosedGenes,inNormalTranscript);
											goOn = returnValuesCheckGoOn[0];

											if(goOn == -1){
												// check for isos that should be closed or not
												goOn = 0;
											}
										}else{
											returnValuesCheckGoOn = IntronExonSearch.checkIfIntronSupported(cluster, currentPos, nextPos, posiCovMap, inTranscriptMode,searchTranscriptStart,endToPassForclosedGenes,inNormalTranscript);
											goOn = returnValuesCheckGoOn[0];
											if(goOn == 2){
												// alternative transcript erased, adapt modi
												goOn = 0;
												searchTranscriptStart = -1;										
												inTranscriptMode = -1;										
												inNormalTranscript = -1;

											}
											if(goOn == 3){
												goOn = -1;
											}

										}

										endToPassForclosedGenes = returnValuesCheckGoOn[1];
										spliceStartAfterCurrentPos = returnValuesCheckGoOn[2];
								
									}

									if(inNormalTranscript != -1){  // TODO: should not be necessary any longer!  wrong here? because we already check with inTranscriptMode?
										int[] intronEndFound = HelperFunctions_GeneSearch.checkIfNextPosExceedsIntronEnd(cluster,nextPos,inNormalTranscript);
										if((intronEndFound[0] != -1) && (intronEndFound[1] == 1)){

											if((nextPos - currentPos) > (GeneFinder.readLength)){
											
												fakeGene = new Gene();
												isoformRnas.clear();

												inNormalTranscript = -1;
											}else{
												int[] transcriptStarts = FrameSearch.lookForStartOfIsoform(inTranscriptMode,contigSeq);
												cluster.alternativeTranscripts.add(new Object[] {inTranscriptMode,inTranscriptMode, transcriptStarts[0],transcriptStarts[1]});
												HelperFunctions_GeneSearch.addRnasFromVector(cluster,isoformRnas);

												isoformRnas.clear();
												fakeGene = new Gene();

												IntronExonSearch.replaceEntryInAltTransWithUpdatedOne(cluster, inTranscriptMode, currentPos, intronEndFound[0],nextPos);
												inNormalTranscript = -1;
											}

										}
									}
									
									if(positionPosiCovMap == -1 && ((nextPos - currentPos) > (GeneFinder.readLength)) && (nextPos < currentCompete)){
										// indicates a potential transcript start

										if(searchTranscriptStart != -1){
											isoformRnas.clear();
											fakeGene = new Gene();
										}
										if(!((inTranscriptMode != -1) && ((nextPos - currentPos) <= (2*GeneFinder.readLength)))){
											// for alternatives, we do accept greater differences due to coverage inconsistencies

											if(inTranscriptMode != -1){
												// extract fake gene as a new potential candidate
												fakeGene.startPos = inTranscriptMode;
												HelperFunctions_GeneSearch.addRnasFromVector(fakeGene,isoformRnas);
												fakeGene.geneID = id++;
												fakeGene.stopPos = currentPos + GeneFinder.readLength;
												temporaryGenes.add(fakeGene);

												fakeGene = new Gene();
												isoformRnas.clear();
											}

											searchTranscriptStart = nextPos;
											inTranscriptMode = -1;

										}							
									}

									if(goOn != -1){ 
										
										if((nextPos - currentPos) > (GeneFinder.readLength)){
											
											int intronEnd = HelperFunctions_GeneSearch.findIntronNearNextPos(cluster, nextPos);
											
											if(intronEnd == -1){
												if((nextPos - currentPos) > (2*GeneFinder.readLength)){
													posStartExon = nextPos;
													numReadsExon = 0;
													averageExonCov = 0;
												}
											}else{
												
												if(intronEnd > nextPos){
													intronEnd = nextPos;
												}
												
												posStartExon = intronEnd;
												numReadsExon = 0;
												averageExonCov = 0;
											}							
										
										}
										
										if(goOn == 0){
											if(((searchTranscriptStart != -1) || (inTranscriptMode != -1) || (inNormalTranscript != -1))  && (acceptReads != 1)){
												isoformRnas.addAll(thisContig.positionTOmappingRnas.get(currentPos));
											}else{
												HelperFunctions_GeneSearch.addRnasFromVector(cluster,thisContig.positionTOmappingRnas.get(nextPos));
											}

											currentExonLength = (nextPos - posStartExon) + GeneFinder.readLength;

											numReadsExon += thisContig.positionTOmappingRnas.get(nextPos).size();

											averageExonCov = (double)((double)(numReadsExon * GeneFinder.readLength)/(double)currentExonLength);

										}									

										if(((nextPos - currentPos) > (GeneFinder.readLength)) || ((((Vector<Integer>) coverageVecAndPos[0]).get(0) + diff < GeneFinder.endCoverage))){
										
											if(rnasThatDoNotSupportAnySplit.size() > 0){	
												double limit = GeneFinder.spliceLim;
												Object[] returnVal = checkIfNonSplitRnasAreSufficient(cluster, rnasThatDoNotSupportAnySplit, spliceKey, currentPos, posiCovMap, limit);
												posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnVal[1];
												rnasThatDoNotSupportAnySplit.clear();
											}		

										}

										if(((nextPos - currentPos) > (GeneFinder.readLength)) && localExonEnd != -1 && localSpliceSite != -1 && (cluster.possibleIntrons.get(localSpliceSite)[0] != null)){										 
											cluster.possibleIntrons.get(localSpliceSite)[2] = currentPos + GeneFinder.readLength;
											if(spliceStartAfterCurrentPos == -1){											
												cluster.possibleIntrons.get(localSpliceSite)[3] = -2;
											}										 
											localExonEnd = -1;
											localSpliceSite = -1;
										}

										if((nextPos - currentPos) > (GeneFinder.readLength)){
											for(int position : posiCovMap.keySet()){
												if(currentPos < (position-GeneFinder.readLength) && nextPos > (position+GeneFinder.readLength)){						
													// had no chance to look at it, so create fake intron to also regard this exon and close
													IntronExonSearch.searchCompleteIntronForEndingAndMakeNewIsoform(cluster, position,posiCovMap);
												}
											}
										}

										currentPos = nextPos;							

										if(currentPos >= currentCompete){
											currentCompete = -1;
											currentCompeteStart = -1;
										}
									}else{

										currentCompete = -1;
										currentCompeteStart = -1;
										if(endToPassForclosedGenes != -1){
											chooseAlternativeEndInstead = true;
										}
										
										closeGene = true;
									}

								}else{
									closeGene = true;
								}
							}
							
							if(closeGene){

								int basesToAdd =  GeneFinder.readLength;
								
								if((localExonEnd != -1) && (localSpliceSite != -1)){
									// update fussy exon for last local splice site, take the split as the end
									if(cluster.possibleIntrons.containsKey(localSpliceSite)){
										cluster.possibleIntrons.get(localSpliceSite)[2] = (currentPos+basesToAdd);										
										cluster.possibleIntrons.get(localSpliceSite)[3] = -2;										
									}else{
										System.err.println("Splice site not included!");
									}
								}
					
								if(chooseAlternativeEndInstead){
									currentPos = endToPassForclosedGenes;
								} 
								
								if(((currentCompeteStart != -1 && ((currentCompeteStart - currentPos) < GeneFinder.readLength))) && currentCompete > currentPos){
									
									if((nextPos - currentPos) > (GeneFinder.readLength)){
										for(int position : posiCovMap.keySet()){
											if(currentPos < (position-GeneFinder.readLength) && nextPos > (position+GeneFinder.readLength) && (position != currentCompete)){						
												// had no chance to look at it, so create fake intron to also regard this exon and close
												IntronExonSearch.searchCompleteIntronForEndingAndMakeNewIsoform(cluster, position,posiCovMap);
											}
										}
									}
									
									if((currentCompeteStart != -1) && ((currentCompeteStart - currentPos) < GeneFinder.readLength) && !((currentCompete + basesToAdd) >= nextPos)){
										IntronExonSearch.checkIfIntronSupported_forGeneIni(cluster, currentPos, nextPos, posiCovMap, inTranscriptMode,searchTranscriptStart,endToPassForclosedGenes);												
									}
									
									currentPos = currentCompete + 1;   // nextPos is bigger than split end, but we have to consider the split anyway
									
									// look new number up in posiCovMap
									
									if(posiCovMap.containsKey(currentCompete)){
										basesToAdd = posiCovMap.get(currentCompete).size();
									}
								}
								
								
								
								// extract this cluster, interval [startPos,currentPos+readLength]							

								id = clustIni(cluster, thisContig, contigSeq, startPos, id, currentPos,basesToAdd);
								
								numIdentifiedClusters++;
								currentPos = nextPos;  // now nextPos is potential new start
							
								doNotCountTwice = true;
								break;
							}
						}							
					}

				}else{
					if(positionIt.hasNext()){
						currentPos = positionIt.next();
						doNotCountTwice = false;
						
						cluster.idTOassociatedRnas.clear();
						cluster.possibleIntrons.clear();
						
						while(!(spliceKey < 0) && (currentPos > spliceKey)){
						
							if((thisContig.splicePositions.higherKey(spliceKey) == null)){
								spliceKey = -1;
							}else{
								spliceKey = thisContig.splicePositions.higherKey(spliceKey);
							}
						}
						
					}else{
						break;
					}
				}

				if(!positionIt.hasNext() && (numIdentifiedClusters < 1) && startedNewCluster){  // to grab very last cluster
										
					int basesToAdd =  GeneFinder.readLength;
					
					if((localExonEnd != -1) && (localSpliceSite != -1)){
						// update fussy exon for last local splice site, take the split as the end
						if(cluster.possibleIntrons.containsKey(localSpliceSite)){
							cluster.possibleIntrons.get(localSpliceSite)[2] = (currentPos+basesToAdd);
							cluster.possibleIntrons.get(localSpliceSite)[3] = -2;	
						}else{
							System.err.println("Splice site not included!");
						}
					}
					
					if(chooseAlternativeEndInstead){
						currentPos = endToPassForclosedGenes;
					} 
					
					if(((currentCompeteStart != -1 && ((currentCompeteStart - currentPos) < GeneFinder.readLength))) && currentCompete > currentPos){
						
						if((nextPos - currentPos) > (GeneFinder.readLength)){
							for(int position : posiCovMap.keySet()){
								if(currentPos < (position-GeneFinder.readLength) && nextPos > (position+GeneFinder.readLength) && (position != currentCompete)){						
									// had no chance to look at it, so create fake intron to also regard this exon and close
									IntronExonSearch.searchCompleteIntronForEndingAndMakeNewIsoform(cluster, position,posiCovMap);
								}
							}
						}
						
						if((currentCompeteStart != -1) && ((currentCompeteStart - currentPos) < GeneFinder.readLength) && !((currentCompete + basesToAdd) >= nextPos)){
							IntronExonSearch.checkIfIntronSupported_forGeneIni(cluster, currentPos, nextPos, posiCovMap, inTranscriptMode,searchTranscriptStart,endToPassForclosedGenes);												
						}
						
						currentPos = currentCompete + 1;   // nextPos is bigger than split end, but we have to consider the split anyway
						
						// look new number up in posiCovMap
						
						if(posiCovMap.containsKey(currentCompete)){
							basesToAdd = posiCovMap.get(currentCompete).size();
						}
					}
					
					id = clustIni(cluster, thisContig, contigSeq, startPos, id, currentPos,basesToAdd);
	
					numIdentifiedClusters++;
				}

			}while(positionIt.hasNext() && (numIdentifiedClusters < 1));

			if(!positionIt.hasNext() && (numIdentifiedClusters < 1)){
				// reached end of reference sequence, no cluster has been extracted this time, so stop
				noMoreCluster = true;
				break;
			}

			// now that we found the high-coverage area, search for start and stop codons
			
			int possibleStart_FO = FrameSearch.findPossibleStarts_Forward(cluster, contigSeq, 0, (int) (cluster.startPos+3));  // now the start positions are directly the right ones
			int possibleStart_RE = FrameSearch.findPossibleStarts_Reverse(cluster,contigSeq,0,(int) (cluster.startPos+3));
			
			boolean foundNoStart = false;
			boolean useClusterBef = false;
			boolean doWithoutStart = false;

			Gene clusterBef = null;
			if(thisContig.allGenes.size() != 0){
				clusterBef = thisContig.allGenes.get(thisContig.allGenes.size()-1);
			}

			if(((possibleStart_FO == -1) && (possibleStart_RE == -1)) || (clusterBef != null && !clusterBef.hasStop_temp) || (clusterBef != null && clusterBef.twinNode != null && !clusterBef.twinNode.hasStop_temp)){
				if((possibleStart_FO == -1) && (possibleStart_RE == -1)){
					numFoundNoStart_firstTime++;
				}
				if(thisContig.allGenes.size() == 0){
					doWithoutStart = true;
				}else{
					boolean overlaps = false;
					boolean overlapsTwin = false;
					
					int tolerance = GeneFinder.interval;
					if(tolerance == -1){
						tolerance = GeneFinder.readLength; 
					}
					
					if((cluster.startPos <= clusterBef.stopPos+1) || (cluster.startPos - clusterBef.stopPos+1 <= tolerance)){
						overlaps = true;					
					}
					if(clusterBef.twinNode != null){
						if((cluster.startPos <= clusterBef.twinNode.stopPos+1) || (cluster.startPos - clusterBef.twinNode.stopPos+1 <= tolerance)){
							overlapsTwin = true;
						}
					}
					
					// combine current cluster with the one before
					if(overlaps || overlapsTwin){
						useClusterBef = true;
					}else if((clusterBef.hasStop_temp) || (clusterBef.twinNode != null && clusterBef.twinNode.hasStop_temp)){
						doWithoutStart = true;
					}else{

						clusterBef.hasStop_temp = true;
						
						if(GeneFinder.inprogeaCall){
							clusterBef.sequence = contigSeq.substring(clusterBef.startPos,Math.min(clusterBef.stopPos+1,contigSeq.length()));
						}					
						clusterBef.realDirectionNotKnown = true;
						if(clusterBef.twinNode != null){
							clusterBef.twinNode.hasStop_temp = true;
							if(GeneFinder.inprogeaCall){
								clusterBef.twinNode.sequence = contigSeq.substring(clusterBef.twinNode.startPos,Math.min(clusterBef.twinNode.stopPos+1,contigSeq.length()));
							}
							
							clusterBef.twinNode.realDirectionNotKnown = true;
						}

						if((possibleStart_FO == -1) && (possibleStart_RE == -1)){
							doWithoutStart = true;
						}	

					}
				}

			}

			int possibleStop_FO = -1;
			int possibleStop_RE = -1;
			
			possibleStop_FO = FrameSearch.findPossibleStops_Forward(cluster,contigSeq,0,(int) cluster.stopPos-2);
			possibleStop_RE = FrameSearch.findPossibleStops_Reverse(cluster,contigSeq,0,(int) cluster.stopPos-2);

			if(!foundNoStart){

				// first have a look, if already forward or reverse direction is excluded due to missing start or stop

				if(useClusterBef){
					
					numMergedClusters++;
					boolean overlaps = false;
					boolean overlapsTwin = false;
					clusterBef = thisContig.allGenes.get(thisContig.allGenes.size()-1);
					thisContig.allGenes.remove(thisContig.allGenes.size()-1);
					
					int[] intronBef = new int[2];
					intronBef[0] = clusterBef.stopPos+1;
					intronBef[1] = cluster.startPos;
					
					int tolerance = GeneFinder.interval;
					if(tolerance == -1){
						tolerance = GeneFinder.readLength; 
					}
					if((cluster.startPos <= clusterBef.stopPos+1) || (cluster.startPos - clusterBef.stopPos+1 <= tolerance)){
						overlaps = true;					
					}

					if(clusterBef.twinNode == null){

						// easy case, we simply take direction of this cluster as our new direction

						MergeClusters.updateCluster_AfterMerging(cluster,clusterBef,cluster.stopPos-2,contigSeq);
						handleFrameSearchWithoutTwin(cluster,possibleStop_FO,possibleStop_RE,contigSeq,overlaps);

						if(!overlaps){
							IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef);
						}


					}else{
						// clusterBef has a twin, now it gets a bit more complicated: first check if we can exclude one direction because we do not find the adequate stop/start

						int[] intronBef_twin = new int[2];
						intronBef_twin[0] = clusterBef.twinNode.stopPos+1;
						intronBef_twin[1] = cluster.startPos;
						if((cluster.startPos <= clusterBef.twinNode.stopPos+1) || (cluster.startPos - clusterBef.twinNode.stopPos+1 <= tolerance)){
							overlapsTwin = true;
						}


						if(possibleStop_RE == -1 && possibleStop_FO == -1){   // useClustBef, no start found

							// ooops, found nothing at all, so merge with both?

							if((!clusterBef.freeToResolve && !clusterBef.twinNode.freeToResolve) || (overlaps != overlapsTwin)){
								MergeClusters.findLeadingTwinAndMerge(cluster,clusterBef,false,overlaps,overlapsTwin,intronBef,intronBef_twin,contigSeq);

							}else{

								MergeClusters.mergeClustWithBothBefs(cluster,clusterBef,contigSeq,false);

								if(!overlapsTwin){
									IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster.twinNode,intronBef_twin);;
								}
								if(!overlaps){
									IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef);
								}

							}	

							numFoundNoStop_firstTime++;
							cluster.realDirectionNotKnown = true;
							cluster.twinNode.realDirectionNotKnown = true;

						} else if(possibleStop_FO != -1 && possibleStop_RE != -1){  // useClustBef, both starts found

							if((!clusterBef.freeToResolve && !clusterBef.twinNode.freeToResolve) || (overlaps != overlapsTwin)){

								MergeClusters.findLeadingTwinAndMerge(cluster,clusterBef,true,overlaps,overlapsTwin,intronBef,intronBef_twin,contigSeq);
								
								if(cluster.possibleIntrons.keySet().isEmpty()){
									if(!cluster.onRevStrand){
										int[] pair_FO = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
										if(pair_FO == null){

											cluster.stopPos = possibleStop_FO + 2;

											checkIfAdequateAndRefine(cluster, false, cluster.possibleStarts_Forward, contigSeq);

											numNoFrameFound++;

										}else{
											cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
											cluster.realDirectionNotKnown = false;
										}
									}else{
										int[] pair_RE = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
										if(pair_RE == null){

											cluster.stopPos = possibleStop_RE + 2;

											checkIfAdequateAndRefine(cluster, true, cluster.possibleStarts_Reverse, contigSeq);

											numNoFrameFound++;
										}else{
											cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
											cluster.realDirectionNotKnown = false;
										}
									}
								}else{
									int[] pair_FO = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
									int[] pair_RE = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
									
									
									if(pair_FO == null && pair_RE == null){
										
										// if possible, refer to XS tag		
										
										if(cluster.direcCounter[0] > cluster.direcCounter[1]){
											cluster.onRevStrand = false;
										}else if(cluster.direcCounter[0] < cluster.direcCounter[1]){
											cluster.onRevStrand = true;
										}
										
										if(!cluster.onRevStrand){
											cluster.stopPos = possibleStop_FO + 2;
											checkIfAdequateAndRefine(cluster, false, cluster.possibleStarts_Forward, contigSeq);
										}else{
											cluster.stopPos = possibleStop_RE + 2;
											checkIfAdequateAndRefine(cluster, true, cluster.possibleStarts_Reverse, contigSeq);										
										}
										
									}else{
										// choose smallest interval possible
										if(pair_FO == null && pair_RE != null){
											cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
											cluster.realDirectionNotKnown = false;
										}else if(pair_FO != null && pair_RE == null){
											cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
											cluster.realDirectionNotKnown = false;
										}else{
											
											// if possible, refer to XS tag		
											
											if(cluster.direcCounter[0] > cluster.direcCounter[1]){
												cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
											}else if(cluster.direcCounter[0] < cluster.direcCounter[1]){
												cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
											}else{
												if(pair_FO[2] <= pair_RE[2]){   // extract smallest possible interval
													cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
												}else{
													cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
												}	
											}
											
										}
	
									}
								}
								

							}else{

								// merge with both

								MergeClusters.mergeClustWithBothBefs(cluster,clusterBef,contigSeq,true);

								if(!overlapsTwin){
									IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster.twinNode,intronBef_twin);
								}
								if(!overlaps){
									IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef);
								}

								// if possible, refer to XS tag		
								
								if((cluster.possibleIntrons.keySet().size() > 0) && cluster.direcCounter[0] > cluster.direcCounter[1]){
									cluster.onRevStrand = false;
								}else if((cluster.possibleIntrons.keySet().size() > 0) && cluster.direcCounter[0] < cluster.direcCounter[1]){
									cluster.onRevStrand = true;
								}

								if(!cluster.onRevStrand){

									int[] pair_FO = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
									int[] pair_RE_Twin = FrameSearch.checkAndChooseReadingFrame(cluster.twinNode.possibleStarts_Reverse,cluster.twinNode.possibleStops_Reverse);

									if(pair_FO == null){

										cluster.stopPos = possibleStop_FO + 2;

										checkIfAdequateAndRefine(cluster, false, cluster.possibleStarts_Forward, contigSeq);

										numNoFrameFound++;
									}else{
										cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
										cluster.realDirectionNotKnown = false;
									}

									if(pair_RE_Twin == null){

										cluster.twinNode.stopPos = possibleStop_RE + 2;

										checkIfAdequateAndRefine(cluster.twinNode, true, cluster.twinNode.possibleStarts_Reverse, contigSeq);

										numNoFrameFound++;
									}else{
										cluster.twinNode = refineExtractedCluster(cluster.twinNode, contigSeq, pair_RE_Twin[0], pair_RE_Twin[1]+2,true);
										cluster.twinNode.realDirectionNotKnown = false;
									}
								}else{

									int[] pair_RE = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
									int[] pair_FO_Twin = FrameSearch.checkAndChooseReadingFrame(cluster.twinNode.possibleStarts_Forward,cluster.twinNode.possibleStops_Forward);

									if(pair_RE == null){

										cluster.stopPos = possibleStop_RE + 2;

										checkIfAdequateAndRefine(cluster, true, cluster.possibleStarts_Reverse, contigSeq);

										numNoFrameFound++;

									}else{
										cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
										cluster.realDirectionNotKnown = false;
									}

									if(pair_FO_Twin == null){

										cluster.twinNode.stopPos = possibleStop_FO + 2;

										checkIfAdequateAndRefine(cluster.twinNode, false, cluster.twinNode.possibleStarts_Forward, contigSeq);

										numNoFrameFound++;

									}else{
										cluster.twinNode = refineExtractedCluster(cluster.twinNode, contigSeq, pair_FO_Twin[0], pair_FO_Twin[1]+2,false);
										cluster.twinNode.realDirectionNotKnown = false;
									}
								}						
							}	

						}else if(possibleStop_FO != -1 && possibleStop_RE == -1){  // useClustBef, forward start found

							// take forward twin
							if((!clusterBef.freeToResolve && !clusterBef.twinNode.freeToResolve) || (overlaps != overlapsTwin)){

								boolean changeDirections = false;
								boolean changeOverlap = false;   // only necessary to give the right argument to changeDirections function

								if(!clusterBef.freeToResolve && !clusterBef.twinNode.freeToResolve){

									if(clusterBef.isMergedTwin){

										if(clusterBef.onRevStrand || clusterBef.realDirectionNotKnown){
											// make this twin a forward one, other twin becomes a reverse one instead
											changeDirections = true;
										}

										MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef,contigSeq,false,true,true);

										changeOverlap = overlaps;

										if(!overlaps){
											IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef);
										}

									}else{
										if(clusterBef.twinNode.onRevStrand || clusterBef.twinNode.realDirectionNotKnown){
											// make this twin a forward one, other twin becomes a reverse one instead
											changeDirections = true;
										}

										MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef.twinNode,contigSeq,false,true,true);

										changeOverlap = overlapsTwin;

										if(!overlapsTwin){
											IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef_twin);
										}

									}
								}else{
									if(overlaps){
										if(clusterBef.onRevStrand || clusterBef.realDirectionNotKnown){
											// make this twin a forward one, other twin becomes a reverse one instead
											changeDirections = true;
										}
										MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef,contigSeq,false,true,true);
										changeOverlap = overlaps;
									}else{
										if(clusterBef.twinNode.onRevStrand || clusterBef.twinNode.realDirectionNotKnown){
											// make this twin a forward one, other twin becomes a reverse one instead
											changeDirections = true;
										}
										MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef.twinNode,contigSeq,false,true,true);
										changeOverlap = overlapsTwin;
									}
								}								

								int[] pair_FO = null;
								if(cluster.possibleIntrons.keySet().isEmpty()){
									pair_FO = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
								}else{
									pair_FO = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
								}
							
								if(pair_FO == null){

									cluster.stopPos = possibleStop_FO + 2;

									checkIfAdequateAndRefine(cluster, false, cluster.possibleStarts_Forward, contigSeq);

									numNoFrameFound++;

								}else{

									if(changeDirections){										
										boolean haveChanged = changeDirectionsIfPossible(cluster,contigSeq,changeOverlap,true);									
										if(!haveChanged){
											cluster.stopPos = possibleStop_FO + 2;
											declareClusterAsNotCompleted(cluster,cluster.startPos,contigSeq);
											numNoFrameFound++;
										}
									}else{								
										cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
										cluster.realDirectionNotKnown = false;
									}
								}

							}else{

								int[] pair_FO = null;
								int[] pair_FO_twin = null;
								
								if(cluster.possibleIntrons.keySet().isEmpty() && clusterBef.possibleIntrons.keySet().isEmpty()){
									pair_FO = FrameSearch.checkAndChooseReadingFrame(clusterBef.possibleStarts_Forward,cluster.possibleStops_Forward);
								}else{
									pair_FO = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(clusterBef.possibleStarts_Forward,cluster.possibleStops_Forward);
								}
								
								if(cluster.possibleIntrons.keySet().isEmpty() && clusterBef.twinNode.possibleIntrons.keySet().isEmpty()){
									pair_FO_twin = FrameSearch.checkAndChooseReadingFrame(clusterBef.twinNode.possibleStarts_Forward,cluster.possibleStops_Forward);
								}else{
									pair_FO_twin = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(clusterBef.twinNode.possibleStarts_Forward,cluster.possibleStops_Forward);
								}

								if(pair_FO != null && pair_FO_twin == null){									
									MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef,contigSeq,false,true,true);		
									if(!overlaps){
										IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef);
									}
									cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
									cluster.realDirectionNotKnown = false;
								} else if(pair_FO == null && pair_FO_twin != null){
									MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef.twinNode,contigSeq,false,true,true);
									if(!overlapsTwin){
										IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef_twin);
									}
									cluster = refineExtractedCluster(cluster, contigSeq, pair_FO_twin[0], pair_FO_twin[1]+2,false);
									cluster.realDirectionNotKnown = false;
								} else if((pair_FO != null && pair_FO_twin != null) || (pair_FO == null && pair_FO_twin == null)){

									// merge with both
									cluster.stopPos = possibleStop_FO + 2;
									MergeClusters.mergeClustWithBothBefs(cluster,clusterBef,contigSeq,true);

									if(!overlapsTwin){
										IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster.twinNode,intronBef_twin);
									}

									if(!overlaps){
										IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef);
									}

									if(pair_FO == null && pair_FO_twin == null){

										numNoFrameFound++;

										cluster.hasStop_temp = false;
										cluster.twinNode.hasStop_temp = false;
										cluster.realDirectionNotKnown = true;
										cluster.twinNode.realDirectionNotKnown = true;

									}else{

										// merge with present forward, set other to noStop

										if(!cluster.onRevStrand){										
											cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
											cluster.realDirectionNotKnown = false;

											cluster.twinNode.stopPos = possibleStop_FO + 2;
											declareClusterAsNotCompleted(cluster.twinNode,cluster.twinNode.startPos,contigSeq);
											numNoFrameFound++;

										}else{
											cluster.twinNode = refineExtractedCluster(cluster.twinNode, contigSeq,pair_FO_twin[0], pair_FO_twin[1]+2,false);
											cluster.twinNode.realDirectionNotKnown = false;

											cluster.stopPos = possibleStop_FO + 2;
											declareClusterAsNotCompleted(cluster,cluster.startPos,contigSeq);
											numNoFrameFound++;
										}
									}

								}
							}

						}else if(possibleStop_FO == -1 && possibleStop_RE != -1){  // useClustBef, reverse start found

							// take reverse twin

							if((!clusterBef.freeToResolve && !clusterBef.twinNode.freeToResolve) || (overlaps != overlapsTwin)){

								boolean changeDirections = false;
								boolean changeOverlap = false;   // only necessary to give the right argument to changeDirections function

								if(!clusterBef.freeToResolve && !clusterBef.twinNode.freeToResolve){
									if(clusterBef.isMergedTwin){

										if(!clusterBef.onRevStrand || clusterBef.realDirectionNotKnown){
											// make this twin a forward one, other twin becomes a reverse one instead
											changeDirections = true;
										}

										MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef,contigSeq,false,true,true);

										changeOverlap = overlaps;

										if(!overlaps){
											IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef);
										}								

									}else{

										if(!clusterBef.twinNode.onRevStrand || clusterBef.twinNode.realDirectionNotKnown){
											// make this twin a forward one, other twin becomes a reverse one instead
											changeDirections = true;
										}

										MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef.twinNode,contigSeq,false,true,true);

										changeOverlap = overlapsTwin;

										if(!overlapsTwin){
											IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef_twin);
										}
									}
								}else{
									if(overlaps){
										if(!clusterBef.onRevStrand || clusterBef.realDirectionNotKnown){
											// make this twin a forward one, other twin becomes a reverse one instead
											changeDirections = true;
										}
										MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef,contigSeq,false,true,true);
										changeOverlap = overlaps;
									}else{
										if(!clusterBef.twinNode.onRevStrand || clusterBef.twinNode.realDirectionNotKnown){
											// make this twin a forward one, other twin becomes a reverse one instead
											changeDirections = true;
										}
										MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef.twinNode,contigSeq,false,true,true);
										changeOverlap = overlapsTwin;
									}
								}
								
								int[] pair_RE = null;
								
								if(cluster.possibleIntrons.keySet().isEmpty()){
									pair_RE = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
								}else{
									pair_RE = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
								}
								
								if(pair_RE == null){

									cluster.stopPos = possibleStop_RE + 2;

									checkIfAdequateAndRefine(cluster, true, cluster.possibleStarts_Reverse, contigSeq);

									numNoFrameFound++;
								}else{

									if(changeDirections){										
										boolean haveChanged = changeDirectionsIfPossible(cluster,contigSeq,changeOverlap,false);									
										if(!haveChanged){
											cluster.stopPos = possibleStop_RE + 2;
											declareClusterAsNotCompleted(cluster,cluster.startPos,contigSeq);
											numNoFrameFound++;
										}
									}else{								
										cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
										cluster.realDirectionNotKnown = false;
									}
								}

							}else{
								int[] pair_RE = null;
								int[] pair_RE_twin = null;
								
								if(cluster.possibleIntrons.keySet().isEmpty() && clusterBef.possibleIntrons.keySet().isEmpty()){
									pair_RE = FrameSearch.checkAndChooseReadingFrame(clusterBef.possibleStarts_Reverse,cluster.possibleStarts_Reverse);
								}else{
									pair_RE = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(clusterBef.possibleStarts_Reverse,cluster.possibleStarts_Reverse);
								}
								
								if(cluster.possibleIntrons.keySet().isEmpty() && clusterBef.twinNode.possibleIntrons.keySet().isEmpty()){
									pair_RE_twin = FrameSearch.checkAndChooseReadingFrame(clusterBef.twinNode.possibleStarts_Reverse,cluster.possibleStarts_Reverse);
								}else{
									pair_RE_twin = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(clusterBef.twinNode.possibleStarts_Reverse,cluster.possibleStarts_Reverse);
								}
								
								if(pair_RE != null && pair_RE_twin == null){									
									MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef,contigSeq,false,true,true);		
									if(!overlaps){
										IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef);
									}
									cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
									cluster.realDirectionNotKnown = false;
								} else if(pair_RE == null && pair_RE_twin != null){
									MergeClusters.mergeWithOneIncludeTwin(cluster,clusterBef.twinNode,contigSeq,false,true,true);
									if(!overlapsTwin){
										IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef_twin);
									}
									cluster = refineExtractedCluster(cluster, contigSeq, pair_RE_twin[0], pair_RE_twin[1]+2,true);
									cluster.realDirectionNotKnown = false;
								} else if((pair_RE != null && pair_RE_twin != null) || (pair_RE == null && pair_RE_twin == null)){

									// merge with both
									cluster.stopPos = possibleStop_RE + 2;
									MergeClusters.mergeClustWithBothBefs(cluster,clusterBef,contigSeq,true);

									if(!overlapsTwin){
										IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster.twinNode,intronBef_twin);
									}

									if(!overlaps){
										IntronExonSearch.checkIfIntronIsIncluded_AndAdd(cluster,intronBef);
									}

									if(pair_RE == null && pair_RE_twin == null){

										numNoFrameFound++;										

										cluster.hasStop_temp = false;
										cluster.twinNode.hasStop_temp = false;
										cluster.realDirectionNotKnown = true;
										cluster.twinNode.realDirectionNotKnown = true;

									}else{

										// merge with present reverse, set other to noStop

										if(cluster.onRevStrand){										
											cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
											cluster.realDirectionNotKnown = false;

											cluster.twinNode.stopPos = possibleStop_RE + 2;
											declareClusterAsNotCompleted(cluster.twinNode,cluster.twinNode.startPos,contigSeq);
											numNoFrameFound++;

										}else{
											cluster.twinNode = refineExtractedCluster(cluster.twinNode, contigSeq,pair_RE_twin[0], pair_RE_twin[1]+2,true);
											cluster.twinNode.realDirectionNotKnown = false;

											cluster.stopPos = possibleStop_RE +2;
											declareClusterAsNotCompleted(cluster,cluster.startPos,contigSeq);
											numNoFrameFound++;
										}
									}

								}
							}

						}


					}

				}else if(possibleStart_FO == -1 && possibleStart_RE != -1){   // reverse start found
					
					// if it is an ORF, a reverse one is more likely, so search only for a possible start position of an reverse gene
				
					if(possibleStop_RE != -1){
						
						int[] pair = null;
						
						if(cluster.possibleIntrons.keySet().isEmpty()){
							pair = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
						}else{
							pair = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
						}			
						
						if(pair != null){
							cluster = refineExtractedCluster(cluster, contigSeq, pair[0], pair[1]+2,true);
							cluster.realDirectionNotKnown = false;
						}else{
							
							cluster.startPos = possibleStart_RE;
							cluster.stopPos = possibleStop_RE + 2;
							
							checkIfAdequateAndRefine(cluster, true, cluster.possibleStarts_Reverse, contigSeq);
							
							numNoFrameFound++;
						}

					}else{
						cluster.onRevStrand = true;
						declareClusterAsNotCompleted(cluster,possibleStart_RE,contigSeq);
						numFoundNoStop_firstTime++;
					}

				}else if(possibleStart_RE == -1 && possibleStart_FO != -1){   // forward start found

					// if it is an ORF, a forward one is more likely, so search only for possible stop positions of a forward gene

					if(possibleStop_FO != -1){		
						
						int[] pair = null;
						
						if(cluster.possibleIntrons.keySet().isEmpty()){
							pair = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
						}else{
							pair = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
						}
						
						if(pair != null){
							cluster = refineExtractedCluster(cluster, contigSeq, pair[0], pair[1]+2,false);
							cluster.realDirectionNotKnown = false;
						}else{
														
							cluster.startPos = possibleStart_FO;													
							cluster.stopPos = possibleStop_FO + 2;
							
							checkIfAdequateAndRefine(cluster, false, cluster.possibleStarts_Forward, contigSeq);
							
							numNoFrameFound++;
						}						

					}else{
						declareClusterAsNotCompleted(cluster,possibleStart_FO,contigSeq);
						numFoundNoStop_firstTime++;
					}

				}else if(possibleStart_RE != -1 && possibleStart_FO != -1){    // both starts found

					// at the moment both directions are equally likely, so first search for the right frames

					if((possibleStop_FO == -1) && (possibleStop_RE == -1)){
						declareClusterAsNotCompleted(cluster,Math.max(possibleStart_FO,possibleStart_RE),contigSeq); // use the smallest possible interval!
						numFoundNoStop_firstTime++;

					}else if((possibleStop_FO != -1) && (possibleStop_RE == -1)){
						
						int[] pair = null;
						
						if(cluster.possibleIntrons.keySet().isEmpty()){
							pair = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
						}else{
							pair = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
						}
						
						if(pair != null){
							cluster = refineExtractedCluster(cluster, contigSeq, pair[0], pair[1]+2,false);
							cluster.realDirectionNotKnown = false;
						}else{
							
							cluster.startPos = possibleStart_FO;													
							cluster.stopPos = possibleStop_FO + 2;
							checkIfAdequateAndRefine(cluster, false, cluster.possibleStarts_Forward, contigSeq);
							
							numNoFrameFound++;
						}
						
					}else if((possibleStop_FO == -1) && (possibleStop_RE != -1)){
						
						int[] pair = null;
						
						if(cluster.possibleIntrons.keySet().isEmpty()){
							pair = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
						}else{
							pair = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
						}		
						
						if(pair != null){
							cluster = refineExtractedCluster(cluster, contigSeq, pair[0], pair[1]+2,true);
							cluster.realDirectionNotKnown = false;
						}else{
							
							cluster.startPos = possibleStart_RE;													
							cluster.stopPos = possibleStop_RE + 2;
							checkIfAdequateAndRefine(cluster, true, cluster.possibleStarts_Reverse, contigSeq);

							numNoFrameFound++;
						}
						
					}else{
						// both are still equally likely, so check both of them
						
						int[] pair_FO = null;
						int[] pair_RE = null;
						
						if(cluster.possibleIntrons.keySet().isEmpty()){
							pair_FO = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
							pair_RE = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
						}else{
				
							pair_FO = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
							pair_RE = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
						}
	
						if(pair_FO == null && pair_RE == null){
							
							// if possible, refer to XS tag		
							
							if(!(cluster.possibleIntrons.keySet().isEmpty()) && cluster.direcCounter[0] > cluster.direcCounter[1]){
								cluster.startPos = possibleStart_FO;
								cluster.stopPos = possibleStop_FO + 2;
								cluster.onRevStrand = false;
							}else if(!(cluster.possibleIntrons.keySet().isEmpty()) && cluster.direcCounter[0] < cluster.direcCounter[1]){
								cluster.startPos = possibleStart_RE;
								cluster.stopPos = possibleStop_RE + 2;
								cluster.onRevStrand = true;
							}else{
								if((possibleStop_FO - possibleStart_FO) <= (possibleStop_RE - possibleStart_RE)){
									cluster.startPos = possibleStart_FO;
									cluster.stopPos = possibleStop_FO + 2;
									cluster.onRevStrand = false;
								}else{
									cluster.startPos = possibleStart_RE;
									cluster.stopPos = possibleStop_RE + 2;
									cluster.onRevStrand = true;
								}
							}
											
							declareClusterAsNotCompleted(cluster,cluster.startPos,contigSeq);
							numNoFrameFound++;
						} else if(pair_FO != null && pair_RE == null){
							cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
							cluster.realDirectionNotKnown = false;
						} else if(pair_FO == null && pair_RE != null){
							cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
							cluster.realDirectionNotKnown = false;
						}else if(pair_FO != null && pair_RE != null){
							// if possible, refer to XS tag	
							if(!(cluster.possibleIntrons.keySet().isEmpty()) && cluster.direcCounter[0] > cluster.direcCounter[1]){
								cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
								cluster.realDirectionNotKnown = false;
							}else if(!(cluster.possibleIntrons.keySet().isEmpty()) && cluster.direcCounter[0] < cluster.direcCounter[1]){
								cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
								cluster.realDirectionNotKnown = false;
							}else{
								// now is the time to create a twin, note: TODO: first a twin was always created -> change in results?
								
								Gene clusterTwin = new Gene();
								
								HelperFunctions_GeneSearch.declareAsShared(cluster);

								cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
								clusterTwin = refineExtractedCluster(clusterTwin, contigSeq, pair_RE[0], pair_RE[1]+2,true);					

								HelperFunctions_GeneSearch.addAssociatedRnas(clusterTwin, cluster.idTOassociatedRnas);
								
								clusterTwin.geneID = id++;
								HelperFunctions_GeneSearch.declareAsShared(clusterTwin);

								twinIni(cluster,clusterTwin,thisContig);
							}
							
						}

					}

				}
					
					
				if(doWithoutStart){
					// we simply use the identified start as a starting point and do not have any forward start codon or reverse stop codon

					possibleStart_FO = cluster.startPos;
					possibleStart_RE = cluster.startPos;

					if((possibleStop_FO == -1) && (possibleStop_RE == -1)){
						
						declareClusterAsNotCompleted(cluster,cluster.startPos,contigSeq);
						numFoundNoStop_firstTime++;

					}else if((possibleStop_FO != -1) && (possibleStop_RE == -1)){
						cluster = refineExtractedCluster(cluster, contigSeq,cluster.startPos, possibleStop_FO+2,false);
						cluster.realDirectionNotKnown = true;
					}else if((possibleStop_FO == -1) && (possibleStop_RE != -1)){
						cluster = refineExtractedCluster(cluster, contigSeq,cluster.startPos, possibleStop_RE+2,true);
						cluster.realDirectionNotKnown = true;
					}else{			
						
						// if possible, refer to XS tag	
						if(!(cluster.possibleIntrons.keySet().isEmpty()) && cluster.direcCounter[0] > cluster.direcCounter[1]){
							cluster = refineExtractedCluster(cluster, contigSeq, cluster.startPos,possibleStop_FO+2,false);
							cluster.realDirectionNotKnown = false;
						}else if(!(cluster.possibleIntrons.keySet().isEmpty()) && cluster.direcCounter[0] < cluster.direcCounter[1]){
							cluster = refineExtractedCluster(cluster, contigSeq, cluster.startPos, possibleStop_RE+2,true);
							cluster.realDirectionNotKnown = false;
						}else{
							if(possibleStop_FO <= possibleStop_RE){
								cluster = refineExtractedCluster(cluster, contigSeq, cluster.startPos, possibleStop_FO+2,false);
							}else{
								cluster = refineExtractedCluster(cluster, contigSeq, cluster.startPos, possibleStop_RE+2,true);
							}
							
							cluster.realDirectionNotKnown = true;
						}
						
						
					}
				}

				// now test for overlap merging:

				if(thisContig.allGenes.size() > 0){

					Gene clusterBeforeInVec = thisContig.allGenes.get(thisContig.allGenes.size()-1);

					if(MergeClusters.checkIfNeedMerge(cluster, clusterBeforeInVec,thisContig,contigSeq)){
						numMergedClusters++;
					}else{
						thisContig.allGenes.add(cluster);
					}
									
				}else{
					thisContig.allGenes.add(cluster);
				}
			}

			if(thisContig.allGenes.size() > 1){
				// test previously added gene, if it has a twin and if yes, if it can be resolved
				Gene clustBef = thisContig.allGenes.get(thisContig.allGenes.size()-2);
				if((clustBef.twinNode != null) && (clustBef.freeToResolve)){
					thisContig.allGenes.setElementAt(LocalTwinResolve.resolveTwins(clustBef),thisContig.allGenes.size()-2);
					clustBef = thisContig.allGenes.get(thisContig.allGenes.size()-2);
				}
				FrameSearch.useDirecInfo(clustBef,contigSeq);
				if((clustBef.coreSeq <= (2*GeneFinder.readLength)) && ((clustBef.stopPos-clustBef.startPos +1) <= (clustBef.coreSeq + (2*GeneFinder.readLength)))){
					if(!checkIfOnlyMultiRnas(clustBef)){
						thisContig.allGenes.removeElement(clustBef);
						removeGenesRnas(clustBef,thisContig);
					}
					
				}
			}
		
			if(!positionIt.hasNext()){
				noMoreCluster = true;			
				break;
			}

			numIdentifiedClusters--;

		}while(!noMoreCluster);

		// check for the last extracted cluster if it has a proper stop or not and further check for twins
		
		if(thisContig.allGenes.size() != 0 && !thisContig.allGenes.get(thisContig.allGenes.size()-1).hasStop_temp){
		
			thisContig.allGenes.get(thisContig.allGenes.size()-1).hasStop_temp = true;
			
			Gene clustBef = thisContig.allGenes.get(thisContig.allGenes.size()-1);
			if((clustBef.twinNode != null) && (clustBef.freeToResolve)){
				thisContig.allGenes.setElementAt(LocalTwinResolve.resolveTwins(clustBef),thisContig.allGenes.size()-1);
				clustBef = thisContig.allGenes.get(thisContig.allGenes.size()-1);
			}
			
			FrameSearch.useDirecInfo(clustBef,contigSeq);
			
			if((clustBef.coreSeq <= (2*GeneFinder.readLength)) && ((clustBef.stopPos-clustBef.startPos +1) <= (clustBef.coreSeq + (2*GeneFinder.readLength)))){
				if(!checkIfOnlyMultiRnas(clustBef)){
					thisContig.allGenes.removeElement(clustBef);
					removeGenesRnas(clustBef,thisContig);
				}
				
			}
			
		}
		
		if(!temporaryGenes.isEmpty()){
			// check also the temporarily extracted candidates, add the coreSeq, check if they are multi-read artifacts and assign a proper start and stop and direction
			for(int pos = temporaryGenes.size()-1;pos >= 0; pos--){
				Gene tempGene = temporaryGenes.get(pos);
				
				if(tempGene.startPos == 0){
					System.err.println("Fake gene " + tempGene.geneID + " has zero start!");
					WriteOutput.writeToLogFile("Fake gene " + tempGene.geneID + " has zero start!\n");
				}
				tempGene.coreSeq = (tempGene.stopPos - tempGene.startPos);
				
				if(GeneFinder.inprogeaCall){
					tempGene.sequence = contigSeq.substring(tempGene.startPos,tempGene.stopPos+1);
				}
				
				if((tempGene.coreSeq > (2*GeneFinder.readLength)) && (checkIfOnlyMultiRnas(tempGene))){ // TODO: test if this stricter version yields better results

					//tempGene.sequence = contigSeq.substring(tempGene.startPos,tempGene.stopPos+1);
					tempGene.onRevStrand = false;
					FrameSearch.findFrameAndCheckWithNeighbors(tempGene, thisContig, contigSeq);
					FrameSearch.useDirecInfo(tempGene,contigSeq);
					thisContig.allGenes.add(tempGene);

				}else{
					removeGenesRnas(tempGene,thisContig);
					temporaryGenes.removeElementAt(pos);
				}
				
			}
		}
		
		if(!GeneFinder.secondPart){
			String s = "";
			s += "No more clusters can be found \n";
			s += "Total identified clusters: " + thisContig.allGenes.size() + "\n";
			s += "Number mergings: " + numMergedClusters + "\n";
			s += "no start at first trial: " + numFoundNoStart_firstTime + "\n";
			s += "no stop at first trial: " + numFoundNoStop_firstTime + "\n";
			s += "no frame determined at first trial: " + numNoFrameFound + "\n";
			
			WriteOutput.writeToLogFile(s + "\n");
			System.out.println(s);
		}

		return id;
	}
	
	/*
	 * check if only multiRnaSupport
	 */
	
	public static boolean checkIfOnlyMultiRnas(Gene gene){
		
		for(String rnaString : gene.idTOassociatedRnas.keySet()){
			Rna rna = ((Rna)gene.idTOassociatedRnas.get(rnaString)[0]);
			if(rna.isMulti == 0){
				return true;
			}
		}
		return false;
	}
	
	/*
	 * for all rnas mapping to this gene, remove the gene alignment
	 */
	
	public static void removeGenesRnas(Gene gene, Contig thisContig){
		
		for(String rnaID : gene.idTOassociatedRnas.keySet()){
			Rna rna = (Rna) gene.idTOassociatedRnas.get(rnaID)[0];
			
			for(Object[] info : rna.contigsMappedOn){
				
				int alignPos = ((Integer) info[1]).intValue();
				
				if((((Contig) info[0]).equals(thisContig)) && (alignPos > gene.startPos) && (alignPos < (gene.stopPos-GeneFinder.readLength))){				
					rna.contigsMappedOn.removeElement(info);
					rna.assignedNum = rna.assignedNum -1;
				
					break;				
				}
			}
			
		}
	}
	
	
	/*
	 * initiate twinNodes, set freeToresolve, alternative Splicing, hadTwinBefore, isMergedTwin etc...
	 */
	
	public static void twinIni(Gene cluster, Gene clusterTwin, Contig thisContig){
		
		cluster.twinNode = clusterTwin;
		clusterTwin.twinNode = cluster;
		cluster.freeToResolve = true;
		cluster.twinNode.freeToResolve = true;

		cluster.hadTwinBefore = true;
		cluster.twinNode.hadTwinBefore = true;
		
		cluster.isMergedTwin = false;
		cluster.twinNode.isMergedTwin = false;
		
		cluster.twinNode.possibleStarts_Forward = cluster.possibleStarts_Forward;
		cluster.twinNode.possibleStarts_Reverse = cluster.possibleStarts_Reverse;
		cluster.twinNode.possibleStops_Forward = cluster.possibleStops_Forward;
		cluster.twinNode.possibleStops_Reverse = cluster.possibleStops_Reverse;
		
		cluster.realDirectionNotKnown = false;
		cluster.twinNode.realDirectionNotKnown = false;
		
		cluster.twinNode.possibleIntrons.putAll(cluster.possibleIntrons);
		cluster.twinNode.possibleFussyExons.putAll(cluster.possibleFussyExons);
		
		cluster.twinNode.alternativeTranscripts.addAll(cluster.alternativeTranscripts);
		cluster.twinNode.intronEndsThatAreNotContinued.addAll(cluster.intronEndsThatAreNotContinued);
		
		cluster.twinNode.coreSeq = cluster.coreSeq;
		
	}
	
	
	/*
	 * if no stop was found, mark the cluster 
	 */
	
	public static void declareClusterAsNotCompleted(Gene cluster, int possibleStart, StringBuffer contigSeq){
		
		cluster.startPos = possibleStart;
		cluster.realDirectionNotKnown = true;
		if(GeneFinder.inprogeaCall){
			cluster.sequence = contigSeq.substring(cluster.startPos,cluster.stopPos+1);
		}
		cluster.hasStop_temp = false;
		
	}
	
	
	/*
	 * applied to twin nodes: we try to switch their directions and find suitable reading frames
	 */
	
	public static boolean changeDirectionsIfPossible(Gene cluster, StringBuffer contigSeq, boolean overlap, boolean wantForward){
		
		boolean haveChanged = false;
		
		if(wantForward){
			int[] pair_FO = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
			int[] pair_RE_twin = FrameSearch.checkAndChooseReadingFrame(cluster.twinNode.possibleStarts_Reverse,cluster.twinNode.possibleStops_Reverse);
			
			if(pair_FO != null && pair_RE_twin != null){
				haveChanged = true;
				cluster.onRevStrand = false;
				cluster.startPos = pair_FO[0];
				cluster.stopPos =  pair_FO[1]+2;
				
				cluster.twinNode.onRevStrand = true;
				cluster.twinNode.startPos = pair_RE_twin[0];
				cluster.twinNode.stopPos = pair_RE_twin[1]+2;
				
				if(GeneFinder.inprogeaCall){
					cluster.sequence = contigSeq.substring(cluster.startPos,cluster.stopPos+1);
					cluster.twinNode.sequence = contigSeq.substring(cluster.twinNode.startPos,cluster.twinNode.stopPos+1);
				}
				cluster.realDirectionNotKnown = false;
				cluster.twinNode.realDirectionNotKnown = false;
				
				cluster.hasStop_temp = true;
				cluster.twinNode.hasStop_temp = true;
			}else if(pair_RE_twin != null && cluster.possibleStarts_Forward[0] >= 0 && !overlap){
				
				// we can change directions, because start and stop are at least in same direction and twin is ok
				haveChanged = true;
				cluster.onRevStrand = false;
				cluster.startPos = cluster.possibleStarts_Forward[0];
				cluster.stopPos =  cluster.possibleStops_Forward[0]+2;
				
				cluster.twinNode.onRevStrand = true;
				cluster.twinNode.startPos = pair_RE_twin[0];
				cluster.twinNode.stopPos = pair_RE_twin[1]+2;
				
				if(GeneFinder.inprogeaCall){
					cluster.sequence = contigSeq.substring(cluster.startPos,cluster.stopPos+1);
					cluster.twinNode.sequence = contigSeq.substring(cluster.twinNode.startPos,cluster.twinNode.stopPos+1);
				}
				
				cluster.realDirectionNotKnown = true;
				cluster.twinNode.realDirectionNotKnown = false;
				
				cluster.hasStop_temp = true;
				cluster.twinNode.hasStop_temp = true;
			}
			
		}else{
			int[] pair_RE = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
			int[] pair_FO_twin = FrameSearch.checkAndChooseReadingFrame(cluster.twinNode.possibleStarts_Forward,cluster.twinNode.possibleStops_Forward);
			
			if(pair_RE != null && pair_FO_twin != null){
				haveChanged = true;
				cluster.onRevStrand = true;
				cluster.startPos = pair_RE[0];
				cluster.stopPos =  pair_RE[1]+2;
				
				cluster.twinNode.onRevStrand = false;
				cluster.twinNode.startPos = pair_FO_twin[0];
				cluster.twinNode.stopPos = pair_FO_twin[1]+2;
				if(GeneFinder.inprogeaCall){
					cluster.sequence = contigSeq.substring(cluster.startPos,cluster.stopPos+1);
					cluster.twinNode.sequence = contigSeq.substring(cluster.twinNode.startPos,cluster.twinNode.stopPos+1);
				}
				cluster.realDirectionNotKnown = false;
				cluster.twinNode.realDirectionNotKnown = false;
				
				cluster.hasStop_temp = true;
				cluster.twinNode.hasStop_temp = true;
			}else if(pair_FO_twin != null && cluster.possibleStarts_Reverse[0] >= 0 && !overlap){
				
				// we can change directions, because start and stop are at least in same direction and twin is ok
				haveChanged = true;
				cluster.onRevStrand = true;
				cluster.startPos = cluster.possibleStarts_Reverse[0];
				cluster.stopPos =  cluster.possibleStops_Reverse[0]+2;
				
				cluster.twinNode.onRevStrand = false;
				cluster.twinNode.startPos = pair_FO_twin[0];
				cluster.twinNode.stopPos = pair_FO_twin[1]+2;
				if(GeneFinder.inprogeaCall){
					cluster.sequence = contigSeq.substring(cluster.startPos,cluster.stopPos+1);
					cluster.twinNode.sequence = contigSeq.substring(cluster.twinNode.startPos,cluster.twinNode.stopPos+1);
				}
				cluster.realDirectionNotKnown = true;
				cluster.twinNode.realDirectionNotKnown = false;
				
				cluster.hasStop_temp = true;
				cluster.twinNode.hasStop_temp = true;
			}
		}
		
		return haveChanged;
	}
	
	
	
	/*
	 * updates the coverage entries in the vector representing the current covered interval
	 */
	
	public static Object[] updateCoverageInterval_respectAlternatives(Contig thisContig, int covPlus, int currentPos, Object[] coverageVecAndPos, TreeMap<Integer,Vector<Integer>> posiCovMap,int considerSpliceSite){
		
		boolean addedValues = false;
		
		int splitDiff = 0;    // necessary to consider splice site, do not update with covplus if we exceed splice site	
		if(considerSpliceSite != -1){
			splitDiff = (considerSpliceSite-currentPos);
		}
		
		Vector<Integer> covVecClone = new Vector<Integer>();    // stores all coverage add values derived by posiCovMap
		Object[] returnObject = HelperFunctions_GeneSearch.lookIntoPosiCovMap(posiCovMap, currentPos);

		addedValues = (Boolean) returnObject[0];
		covVecClone = (Vector<Integer>) returnObject[1];
		posiCovMap = (TreeMap<Integer,Vector<Integer>>) returnObject[2];
		
		if(currentPos - (Integer) coverageVecAndPos[1] > GeneFinder.readLength){

			Vector<Integer> coverageVecNew = new Vector<Integer>();
			// initialize:
			for(int arrPos=0;arrPos<GeneFinder.readLength;++arrPos){
				if((considerSpliceSite != -1) && (arrPos >= splitDiff)){
					coverageVecNew.add(0);
				}else{
					coverageVecNew.add(covPlus);
				}
			}
			
			coverageVecAndPos[0] = coverageVecNew;
			coverageVecAndPos[2] = -1;
			

		}else{
			
			Vector<Integer> covVec = (Vector<Integer>) coverageVecAndPos[0];
			for(int pos = 0;pos<(currentPos- (Integer) coverageVecAndPos[1]);++pos){
				covVec.remove(covVec.firstElement());
				covVec.add(0);
			}
			for(int vecPos=0;vecPos < covVec.size();++vecPos){
				if(!((considerSpliceSite != -1) && (vecPos >= splitDiff))){
					covVec.set(vecPos,(covVec.get(vecPos) + covPlus));    // correct?
				}
				
			}
			
			if((((Integer)coverageVecAndPos[2] == -1)) || (currentPos-((Integer) coverageVecAndPos[2]) > (GeneFinder.readLength))){
				// update overlap-position
				boolean foundPos = false;
				int pos_temp = (Integer) coverageVecAndPos[2];
				if(pos_temp == -1){
					coverageVecAndPos[2] = (Integer) coverageVecAndPos[1];
				}else{
					if(!addedValues){  // if we have introns, then stay with the current start position
						do{	
							if(thisContig.positionTOmappingRnas.lastKey() != pos_temp){
								pos_temp = thisContig.positionTOmappingRnas.higherKey(pos_temp);
							}else{
								pos_temp = (Integer) coverageVecAndPos[1];
							}
							if((currentPos-(pos_temp) <= (GeneFinder.readLength))){
								coverageVecAndPos[2] = pos_temp;
								foundPos = true;
							}
						}while(!foundPos || !(pos_temp >= (Integer) coverageVecAndPos[1]));
					}
				}
			}
		}
		
		coverageVecAndPos[1] = currentPos;
		
		if(addedValues){
			for(int posVec = 0; posVec < GeneFinder.readLength; ++posVec){
				((Vector<Integer>) coverageVecAndPos[0]).setElementAt((((Vector<Integer>)coverageVecAndPos[0]).get(posVec) + covVecClone.get(posVec)),posVec);
			}
		}
		
		Object[] toReturn = {coverageVecAndPos,posiCovMap,addedValues};
		return toReturn;
	}
	
	
	
	
	/*
	 * set start and stop of the cluster + extract new sequence interval
	 * be careful: the position stop refers to the exact stop, for sequence extraction +1 has to be added
	 * stop = possibleStop + 2
	 */
	
	public static Gene refineExtractedCluster(Gene cluster, StringBuffer contigSeq, int start, int stop, boolean isReverse){
		
		if(GeneFinder.inprogeaCall){
			cluster.sequence = contigSeq.substring(start,stop+1);
		}
		cluster.startPos= start;
		cluster.stopPos = stop;
		
		cluster.hasStop_temp = true;

		cluster.onRevStrand = isReverse;
		
		return cluster;
	}
	
	
	/*
	 * first extraction of each cluster, simply regard the high coverage interval
	 */
	
	public static int clustIni(Gene cluster, Contig thisContig, StringBuffer contigSeq, int startPos, int id, int currentPos, int basesToAdd){
		
		if(GeneFinder.inprogeaCall){
			cluster.sequence = contigSeq.substring(startPos,currentPos+basesToAdd);
		}

		cluster.geneID = id++;
		cluster.onRevStrand = false;
		cluster.startPos = startPos;		
		cluster.stopPos = currentPos+basesToAdd-1; // note: without "-1" stopPos would be the position necessary for seq-extraction = actual stop + 1 
		
		cluster.coreSeq = (cluster.stopPos-cluster.startPos);
		
		cluster.realDirectionNotKnown = true;	
		
		DefineAlternativeTranscripts.finalIni_alternativeStartsCheck(cluster);
		IntronExonSearch.finalIni_IntronSupportCheck(cluster,thisContig);
		
		return id;
	}
	
	
	/*
	 * check which of the possible start positions is the smaller one
	 * return value shall be understood as "isReverse"
	 */
	
	public static boolean checkWhichStart(Gene cluster){
		
		if(cluster.possibleStarts_Reverse[0] >= 0 && cluster.possibleStarts_Forward[0] >= 0){
			
			if(cluster.possibleStarts_Reverse[0] > cluster.possibleStarts_Forward[0]){
				// take reverse start
				cluster.startPos = cluster.possibleStarts_Reverse[0];
				return true;
			}else{
				// take forward start
				cluster.startPos = cluster.possibleStarts_Forward[0];
				return false;
			}
			
		}else if(cluster.possibleStarts_Reverse[0] >= 0 && cluster.possibleStarts_Forward[0] < 0){
			// take reverse start
			cluster.startPos = cluster.possibleStarts_Reverse[0];
			return true;
		}else if(cluster.possibleStarts_Reverse[0] < 0 && cluster.possibleStarts_Forward[0] >= 0){
			// take forward start
			cluster.startPos = cluster.possibleStarts_Forward[0];
			return false;
		}
		
		return true;  // we only call this method if we will never reach here, so whether true or false stands here makes no difference
	}
	
	/*
	 * if clusterBef had no twin, then perform this frame search after the merge
	 */
	
	public static void handleFrameSearchWithoutTwin(Gene cluster, int possibleStop_FO, int possibleStop_RE, StringBuffer contigSeq, boolean overlaps){
		
		// check both directions
		
		int[] pair_FO = null;
		int[] pair_RE = null;
		
		if(cluster.possibleIntrons.keySet().isEmpty()){
			pair_FO = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
			pair_RE = FrameSearch.checkAndChooseReadingFrame(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
		}else{
			pair_FO = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Forward,cluster.possibleStops_Forward);
			pair_RE = FrameSearch.checkAndChooseReadingFrame_SplicingVariant(cluster.possibleStarts_Reverse,cluster.possibleStops_Reverse);
		}

		// stop and clusterBef start should be in frame

		if(pair_FO == null && pair_RE == null){
			
			boolean foundAdequateStartStop_FO = false;
			boolean foundAdequateStartStop_RE = false;
			
			if(possibleStop_FO == -1 && possibleStop_RE == -1){
				numFoundNoStop_firstTime++;
				
				// if possible, refer to XS tag		
				
				if((cluster.possibleIntrons.keySet().size() > 0) && cluster.direcCounter[0] > cluster.direcCounter[1]){
					cluster.onRevStrand = false;
				}else if((cluster.possibleIntrons.keySet().size() > 0) && cluster.direcCounter[0] < cluster.direcCounter[1]){
					cluster.onRevStrand = true;
				}
				
			}else{
				if(possibleStop_FO != -1 && possibleStop_RE == -1){
					cluster.stopPos = possibleStop_FO + 2;
					
					if(cluster.possibleStarts_Forward[0] >= 0){
						cluster.startPos = cluster.possibleStarts_Forward[0];
						
						if(!overlaps || (cluster.possibleIntrons.keySet().size() > 0)){
							foundAdequateStartStop_FO = true;
						}

					}
					
				}else if(possibleStop_FO == -1 && possibleStop_RE != -1){
					cluster.stopPos = possibleStop_RE + 2;
					
					if(cluster.possibleStarts_Reverse[0] >= 0){
						cluster.startPos = cluster.possibleStarts_Reverse[0];
						
						if(!overlaps || (cluster.possibleIntrons.keySet().size() > 0)){
							foundAdequateStartStop_RE = true;
						}

					}
				}else{
					
					// if possible, refer to XS tag			
					
					boolean isReverse;
						
					if(cluster.possibleStarts_Reverse[0] < 0 && cluster.possibleStarts_Forward[0] < 0){
						cluster.stopPos = Math.min(possibleStop_FO,possibleStop_RE) + 2;
					}else{
						if((cluster.possibleIntrons.keySet().size() > 0) && cluster.direcCounter[0] > cluster.direcCounter[1]){
							isReverse = false;
						}else if( (cluster.possibleIntrons.keySet().size() > 0) && cluster.direcCounter[0] < cluster.direcCounter[1]){
							isReverse = true;
						}else{
							isReverse = checkWhichStart(cluster);
						}
						 
						if(isReverse){
							cluster.stopPos = possibleStop_RE + 2;
							if(!overlaps || (cluster.possibleIntrons.keySet().size() > 0)){
								foundAdequateStartStop_RE = true;
							}
						}else{
							cluster.stopPos = possibleStop_FO + 2;
							if(!overlaps || (cluster.possibleIntrons.keySet().size() > 0)){
								foundAdequateStartStop_FO = true;
							}
						}
					}
				}
				numNoFrameFound++;
			}
			
			if(foundAdequateStartStop_FO){
				cluster = refineExtractedCluster(cluster, contigSeq, cluster.startPos, cluster.stopPos,false);
				cluster.realDirectionNotKnown = true;
			}else if(foundAdequateStartStop_RE){
				cluster = refineExtractedCluster(cluster, contigSeq, cluster.startPos, cluster.stopPos,true);
				cluster.realDirectionNotKnown = true;
			}else{
				declareClusterAsNotCompleted(cluster,cluster.startPos,contigSeq);
			}
			
		} else if(pair_FO != null && pair_RE == null){
			cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
			cluster.realDirectionNotKnown = false;
		} else if(pair_FO == null && pair_RE != null){
			cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
			cluster.realDirectionNotKnown = false;
		}else if(pair_FO != null && pair_RE != null){
			
			// if possible, refer to XS tag		
			
			if((cluster.possibleIntrons.keySet().size() > 0) && cluster.direcCounter[0] > cluster.direcCounter[1]){
				cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
			}else if((cluster.possibleIntrons.keySet().size() > 0) && cluster.direcCounter[0] < cluster.direcCounter[1]){
				cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
			}else{
				if(pair_FO[2] <= pair_RE[2]){   // extract smallest possible interval
					cluster = refineExtractedCluster(cluster, contigSeq, pair_FO[0], pair_FO[1]+2,false);
				}else{
					cluster = refineExtractedCluster(cluster, contigSeq, pair_RE[0], pair_RE[1]+2,true);
				}																	
				cluster.realDirectionNotKnown = true;
			}
			
			
		}
		
	}
	
	
	/*
	 * method to test whether we find at least start and stop on same strand, though they are not in frame
	 */
	
	public static void checkIfAdequateAndRefine(Gene cluster, boolean wantReverse, int[] possibleStarts, StringBuffer contigSeq){
		
		boolean foundAdequateStartStop = false;  
		
		cluster.onRevStrand = wantReverse;
		
		if(possibleStarts[0] >= 0){
			cluster.startPos = possibleStarts[0];
			if(cluster.possibleIntrons.keySet().size() > 0){
				foundAdequateStartStop = true;
			}
		}
		
		if(foundAdequateStartStop){
			cluster = refineExtractedCluster(cluster, contigSeq, cluster.startPos, cluster.stopPos,wantReverse);
			cluster.realDirectionNotKnown = true;
		}else{
			declareClusterAsNotCompleted(cluster,cluster.startPos,contigSeq);
		}
		
	}
	
	
	/*
	 * when we have passed a split start, then check if the number of rnas not supporting a split is sufficient 
	 */
	
	public static Object[] checkIfNonSplitRnasAreSufficient(Gene cluster, Vector<Rna> nonSplitRnas, int splitStart, int currentPos, TreeMap<Integer,Vector<Integer>> posiCovMap, double limit){
		
		int exonEnd = -1;
		int kept = splitStart;
		
		if(nonSplitRnas.size() < limit){
			// erase
			kept = -1;
			for(Rna rna : nonSplitRnas){
				
				if(cluster.moreThanOneHitRnas.contains(rna.rnaID)){
					// if it is contained here, this serves as one "back-up life" that is now destroyed
					cluster.moreThanOneHitRnas.remove(rna.rnaID);
				}else{
					cluster.idTOassociatedRnas.remove(rna.rnaID);
				}
				
				for(Object[] info : rna.contigsMappedOn){
					if(info[4] == null && ((splitStart - (Integer) info[1]) < GeneFinder.readLength) && ((splitStart > (Integer) info[1]))){															
							int diffToSplitStart = (splitStart - (Integer) info[1]);							
							posiCovMap = HelperFunctions_GeneSearch.updatePosiCovMap_AfterSpliceSwitch(posiCovMap,splitStart,(GeneFinder.readLength-diffToSplitStart));							
							rna.contigsMappedOn.removeElement(info);	
							break;  // have to be removed soon!
					}
				}
				
			}
			
			if(cluster.possibleIntrons.containsKey(splitStart)){
				cluster.possibleIntrons.get(splitStart)[2] = -1;
			}
					
			if(cluster.possibleFussyExons.containsKey(splitStart)){
				cluster.possibleFussyExons.remove(splitStart);
			}
		}else{
			// keep this alternative
			exonEnd = currentPos + GeneFinder.readLength;
			if(cluster.possibleIntrons.containsKey(splitStart)){
				cluster.possibleIntrons.get(splitStart)[2] = exonEnd;
			}else{
				System.err.println("Splice site not included!");
			}
			
			for(Rna rna : nonSplitRnas){   // do this update here to save time later
				cluster.idTOassociatedRnas.get(rna.rnaID)[2] = splitStart;
			}
		}
		
		return new Object[] {exonEnd,posiCovMap,kept};
	}
	

}


