[med-svn] [proalign] 06/10: New upstream version 0.603

Andreas Tille tille at debian.org
Thu Nov 16 13:29:47 UTC 2017


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository proalign.

commit 6f89e7ca2729a6e53a096c3ca0889465cb751b68
Author: Andreas Tille <tille at debian.org>
Date:   Thu Nov 16 14:24:55 2017 +0100

    New upstream version 0.603
---
 AlignmentLoop.java        |  355 ++++++++++++
 AlignmentNode.java        | 1026 +++++++++++++++++++++++++++++++++
 CheckSequence.java        |   81 +++
 CheckTrailing.java        |   55 ++
 CheckTreeAndData.java     |   46 ++
 EstimateFrequencies.java  |   61 ++
 InFile.java               |   16 +
 MinimumProbWindow.java    |   87 +++
 NeighborJoining.java      |  178 ++++++
 NicePrint.java            |   50 ++
 OpenDialog.java           |   53 ++
 OpenFileChooser.java      |   55 ++
 OpenFolderChooser.java    |   52 ++
 OutFile.java              |   20 +
 OutOfMemoryException.java |   19 +
 PAFileFilter.java         |   46 ++
 ParameterEstimates.java   |  195 +++++++
 PixelRule.java            |  102 ++++
 PrintCurve.java           |   76 +++
 PrintData.java            |  382 +++++++++++++
 PrintMinimumCurve.java    |   73 +++
 PrintNames.java           |  250 +++++++++
 PrintTree.java            |  194 +++++++
 ProAlign.java             |  231 ++++++++
 PwAlignment.java          |  345 ++++++++++++
 PwAlignmentLoop.java      |  273 +++++++++
 PwSubstitutionMatrix.java |  143 +++++
 QualityWindow.java        |   96 ++++
 ReadArguments.java        |  232 ++++++++
 ResultWindow.java         | 1374 +++++++++++++++++++++++++++++++++++++++++++++
 Rule.java                 |  117 ++++
 RunClustalw.java          |  186 ++++++
 RunCommandLine.java       |  209 +++++++
 RunMultiple.java          |  103 ++++
 SampleAlignments.java     |  446 +++++++++++++++
 SampleLoop.java           |  146 +++++
 SaveData.java             |  854 ++++++++++++++++++++++++++++
 SequenceReader.java       |  641 +++++++++++++++++++++
 SetParameters.java        |  881 +++++++++++++++++++++++++++++
 SubstitutionModel.java    |  511 +++++++++++++++++
 TraceBackPath.java        |  518 +++++++++++++++++
 TransformLog.java         |   36 ++
 TreeNode.java             |  317 +++++++++++
 TreeReader.java           |  226 ++++++++
 UserSettings.java         |   85 +++
 Viterbi.java              |  354 ++++++++++++
 WinClustalw.java          |   55 ++
 debian/changelog          |   12 -
 debian/compat             |    1 -
 debian/control            |   31 -
 debian/copyright          |   35 --
 debian/manifest.txt       |    3 -
 debian/proalign.1         |   79 ---
 debian/proalign.install   |    3 -
 debian/proalign.manpages  |    1 -
 debian/rules              |   17 -
 debian/scripts/proalign   |   21 -
 debian/source/format      |    1 -
 debian/upstream/metadata  |   12 -
 debian/watch              |    4 -
 60 files changed, 11851 insertions(+), 220 deletions(-)

diff --git a/AlignmentLoop.java b/AlignmentLoop.java
new file mode 100644
index 0000000..42cb609
--- /dev/null
+++ b/AlignmentLoop.java
@@ -0,0 +1,355 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+class AlignmentLoop {
+
+/*
+  Do the viterbi path, and mark the track.
+  Do forward & backward algorithm, calculate posterior probs
+*/
+
+    ProAlign pa;
+    AlignmentLoop al;
+    AlignmentNode an;
+
+    double[][][] price, priceX, priceY;
+    double[][] vitM, vitX, vitY;
+
+    double[][][] pathM;
+    double[][] pathX, pathY;
+
+    double[][] fwdM, fwdX, fwdY;
+    double[][] bwdM, bwdX, bwdY;
+
+    double vitEnd, fwdEnd;
+    double[] pathEnd;
+
+    double[][] seq1, seq2;
+    float dist1, dist2;
+
+    int aSize;
+
+    int BWIDTH;
+    int MIDDLE;
+
+    int endPoint = 0;
+
+    AlignmentLoop(ProAlign pa,AlignmentNode an) {
+
+	this.pa = pa;
+	this.an = an;
+	al = this;
+
+	BWIDTH = ProAlign.bandWidth;
+	MIDDLE = BWIDTH/2+1;
+    }
+    
+    void align(double[][] s1, double[][] s2, float d1, float d2) throws Exception {
+
+	seq1 = s1;
+	seq2 = s2;
+	dist1 = d1;
+	dist2 = d2;
+
+	al.pa.sm.setBranchLength(dist1,dist2);
+
+	aSize = seq1[0].length; // aSize = alphabet size; 
+
+	ProAlign.log("AlignmentLoop");
+	ProAlign.log(" seq1.length = "+seq1.length+", seq2.length = "+seq2.length);
+	
+	try {
+	    al.initialiseMatrices();
+	} catch(Error e) {
+	    if(ProAlign.isResultWindow) {
+		String text = "\n        Out Of Memory Error!\n  Please, increase JVM memory.\n";
+		OpenDialog od = new OpenDialog(pa.rw);
+		od.showDialog("Error!", text);
+		System.exit(0);
+	    } else {
+		ProAlign.log.println("AlignmentLoop: Out Of Memory Error. Increase JVM memory.");
+		System.out.println("Out Of Memory Error.\nPlease, increase JVM memory.");
+		if(ProAlign.exitInError) {
+		    System.exit(0);
+		} else {
+//		    System.out.println("AlignmentLoop: throws an exception");
+		    throw new OutOfMemoryException("Out Of Memory Error. Increase JVM memory");
+		}
+	    }
+	}
+
+	Viterbi v = new Viterbi(al);
+
+	// do viterbi loop through the matrix;
+	// at the same, do forward and mark the path
+	// for the traceback
+
+	for(int i=1; i<vitM.length; i++) { // go through seq1
+	    for(int j=0; j<BWIDTH; j++) {  // go through seq2
+
+		if(j-MIDDLE+i < 1) { continue; }     // upper border of real matrix
+
+		if(j-MIDDLE+i > seq2.length+1) { continue; }  // lower border of real matrix
+
+		if(i==1 && j==MIDDLE) { continue; }  // starting point
+
+		// exception if seq2 starts later; only X-gaps possible
+
+		if(j-MIDDLE+i==1) {   
+
+		    sumSubstPriceX(i,j);
+
+		    pathM[i][j][0] = -1d;
+		    pathM[i][j][1] = -1d;  
+		    vitX[i][j] = v.getViterbiX(i,j);
+		    pathY[i][j] = -1d;
+		    fwdX[i][j] = v.getForwardX(i,j);
+
+		    endPoint = j;
+		} 
+
+		// exception if seq1 starts later; only Y-gaps possible
+		else if(i==1) {   
+
+		    sumSubstPriceY(i,j);
+
+		    pathM[i][j][0] = -1d;
+		    pathM[i][j][1] = -1d;
+		    pathX[i][j] = -1d;
+		    vitY[i][j] = v.getViterbiY(i,j);
+		    fwdY[i][j] = v.getForwardY(i,j);
+
+		    endPoint = j;
+		}
+		
+		else {
+		    
+		    // go through alphabet, get probability for each character 
+		    sumSubstPrice(i,j);
+
+		    vitM[i][j] = v.getViterbiM(i,j); // for 'Match'
+		    vitX[i][j] = v.getViterbiX(i,j); // for 'X indel'
+		    vitY[i][j] = v.getViterbiY(i,j); // for 'Y indel'
+		    fwdM[i][j] = v.getForwardM(i,j);
+		    fwdX[i][j] = v.getForwardX(i,j);
+		    fwdY[i][j] = v.getForwardY(i,j);
+
+		    endPoint = j;
+		}
+	    }
+	}
+	vitEnd = v.getViterbiEnd(endPoint);
+	
+	try {
+	    al.initialiseBwdMatrices(endPoint);
+	} catch(Error e) {
+	    if(ProAlign.isResultWindow) {
+		String text = "\n        Out Of Memory Error!\n  Please, increase JVM memory.\n";
+		OpenDialog od = new OpenDialog(pa.rw);
+		od.showDialog("Error!", text);
+		System.exit(0);
+	    } else {
+		ProAlign.log.println("AlignmentLoop: Out Of Memory Error. Increase JVM memory.");
+		System.out.println("Out Of Memory Error.\nPlease, increase JVM memory.");
+		if(ProAlign.exitInError) {
+		    System.exit(0);
+		} else {
+//		    System.out.println("AlignmentLoop: throws an exception");
+		    throw new OutOfMemoryException("Out Of Memory Error. Increase JVM memory");
+		}
+	    }
+	}
+
+	// do backward loop through the matrix;
+	for(int i=vitM.length-1; i>0; i--) {            // go through seq1
+	    for(int j=BWIDTH-1; j>=0; j--) {            // go through seq2
+
+		if(j-MIDDLE+i < 1) { continue; }        // upper border of real matrix
+
+		else if(j-MIDDLE+i > seq2.length+1) { continue; } // lower border of real matrix
+
+		else if(i==vitM.length-1 && j==endPoint) { continue; }  // starting point
+
+		else {
+		    bwdM[i][j] = v.getBackwardM(i,j);
+		    bwdX[i][j] = v.getBackwardX(i,j);
+		    bwdY[i][j] = v.getBackwardY(i,j);
+		}
+	    }
+	}
+	if(ProAlign.DEBUG) { // ---DEBUG---
+
+	    TransformLog tl = new TransformLog();
+	    double bwdEnd = tl.sumLogs(bwdM[1][MIDDLE],tl.sumLogs(bwdX[1][MIDDLE],
+								  bwdY[1][MIDDLE]));
+	    double diff = (double) bwdEnd-fwdEnd;
+	    ProAlign.log.println("AlignmentLoop: vEnd: "+vitEnd+" fEnd: "+fwdEnd+
+			" bEnd: "+bwdEnd+" fwd-bwd: "+diff);
+	} // ---DEBUG---
+    }
+
+    // initialise all matrices before starting
+    void initialiseMatrices() {
+
+	// [seq1 length][band width][alphabet]
+	price = new double[seq1.length+2][BWIDTH][aSize+1];
+	priceX = new double[seq1.length+2][BWIDTH][aSize+1];
+	priceY = new double[seq1.length+3][BWIDTH][aSize+1];
+
+	vitM = new double[seq1.length+2][BWIDTH];
+	vitX = new double[seq1.length+2][BWIDTH];
+	vitY = new double[seq1.length+2][BWIDTH];
+
+	pathM = new double[seq1.length+2][BWIDTH][2];
+	pathX = new double[seq1.length+2][BWIDTH];
+	pathY = new double[seq1.length+2][BWIDTH];
+
+	fwdM = new double[seq1.length+2][BWIDTH];
+	fwdX = new double[seq1.length+2][BWIDTH];
+	fwdY = new double[seq1.length+2][BWIDTH];
+
+	bwdM = new double[seq1.length+3][BWIDTH];
+	bwdX = new double[seq1.length+3][BWIDTH];
+	bwdY = new double[seq1.length+3][BWIDTH];
+
+	pathEnd = new double[2];
+
+	for(int j=0; j<vitM[0].length; j++) {
+	    for(int i=0; i<vitM.length; i++) {
+		vitM[i][j] = Double.NEGATIVE_INFINITY;
+		vitX[i][j] = Double.NEGATIVE_INFINITY;
+		vitY[i][j] = Double.NEGATIVE_INFINITY;
+		fwdM[i][j] = Double.NEGATIVE_INFINITY;
+		fwdX[i][j] = Double.NEGATIVE_INFINITY;
+		fwdY[i][j] = Double.NEGATIVE_INFINITY;
+	    }
+	    
+	    for(int i=0; i<bwdM.length; i++) {
+		bwdM[i][j] = Double.NEGATIVE_INFINITY;
+		bwdX[i][j] = Double.NEGATIVE_INFINITY;
+		bwdY[i][j] = Double.NEGATIVE_INFINITY;
+	    }
+	}
+
+	if(an.hasTrailers) {
+
+	    if(an.start0>an.start1) {
+		vitM[1][MIDDLE] = 0d;
+		vitX[1][MIDDLE] = 0d;
+		vitY[1][MIDDLE] = Double.NEGATIVE_INFINITY;
+		
+		fwdM[1][MIDDLE] = 0d;
+		fwdX[1][MIDDLE] = 0d;
+		fwdY[1][MIDDLE] = Double.NEGATIVE_INFINITY;		
+	    } else {
+		vitM[1][MIDDLE] = 0d;
+		vitX[1][MIDDLE] = Double.NEGATIVE_INFINITY;
+		vitY[1][MIDDLE] = 0d;
+		
+		fwdM[1][MIDDLE] = 0d;
+		fwdX[1][MIDDLE] = Double.NEGATIVE_INFINITY;
+		fwdY[1][MIDDLE] = 0d;
+	    }
+
+	} else {
+	    //  ([0] means actually [-1]) 
+	    vitM[1][MIDDLE] = 0d;
+	    vitX[1][MIDDLE] = 0d;
+	    vitY[1][MIDDLE] = 0d;
+	    
+	    fwdM[1][MIDDLE] = 0d;
+	    fwdX[1][MIDDLE] = 0d;
+	    fwdY[1][MIDDLE] = 0d;
+	}
+    }
+
+    void initialiseBwdMatrices(int endPoint) {
+
+	bwdM[bwdM.length-2][endPoint] = 0d;
+	bwdX[bwdX.length-2][endPoint] = 0d;
+	bwdY[bwdY.length-2][endPoint] = 0d;
+    }
+    
+
+
+    // calculate the sum of substitution prices for both branches separately;
+    // iterate over all possible characters at the node, child 1 and child 2;
+    // characters at [i] get values 'l'; at [j] values 'm'; node gets characters 'k' 
+
+    void sumSubstPrice(int i, int j){
+
+	// character at node
+	int js = i+j-MIDDLE;  // convert from "band" value to real value
+
+	double[][] sum = new double[2][aSize];
+	
+	for(int k=0; k<aSize; k++) {
+	    sum[0][k] = 0d;
+	    sum[1][k] = 0d;
+	    for(int l = 0; l<aSize; l++) {
+		// match
+		// characters at child 1; correction [i-2] for "positive" matrix
+		sum[0][k] += seq1[i-2][l]*al.pa.sm.substProb1[k][l];
+		// characters at child 2; correction [j-2] for "positive" matrix
+		sum[1][k] += seq2[js-2][l]*al.pa.sm.substProb2[k][l];
+		
+		// x-gap
+		priceX[i][j][k] += al.seq1[i-2][l]*al.pa.sm.substProb1[k][l];
+		
+		// y-gap
+		priceY[i][j][k] += al.seq2[js-2][l]*al.pa.sm.substProb2[k][l];
+	    }
+	    
+	    price[i][j][k] = sum[0][k]*sum[1][k]*al.pa.sm.charFreqs[k];
+	    // because the other child is "gap" for sure
+	    priceX[i][j][k] = priceX[i][j][k]*1.0d*al.pa.sm.substProb2[k][aSize-1]*al.pa.sm.charFreqs[k];
+	    priceY[i][j][k] = priceY[i][j][k]*1.0d*al.pa.sm.substProb1[k][aSize-1]*al.pa.sm.charFreqs[k];
+	}
+	
+    	return;
+    }
+
+    void sumSubstPriceX(int i, int j){
+
+	for(int k=0; k<aSize; k++) {
+	    for(int l = 0; l<aSize; l++) { 
+		// x-gap
+		priceX[i][j][k] += al.seq1[i-2][l]*al.pa.sm.substProb1[k][l];
+	    }   
+	    
+	    priceX[i][j][k] = priceX[i][j][k]*1.0d*al.pa.sm.substProb2[k][aSize-1]*al.pa.sm.charFreqs[k];
+	}	    
+	return; 
+    }
+
+    void sumSubstPriceY(int i, int j){
+
+	int js = i+j-MIDDLE;  // convert from "band" value to real value
+	
+	for(int k=0; k<aSize; k++) { 
+	    for(int l = 0; l<aSize; l++) { 
+		// y-gap
+		priceY[i][j][k] += al.seq2[js-2][l]*al.pa.sm.substProb2[k][l];
+	    }   
+	    
+	    priceY[i][j][k] = priceY[i][j][k]*1.0d*al.pa.sm.substProb1[k][aSize-1]*al.pa.sm.charFreqs[k];
+	}
+	return; 
+    }
+
+
+}
+
+
+
+
+
diff --git a/AlignmentNode.java b/AlignmentNode.java
new file mode 100644
index 0000000..9be47b3
--- /dev/null
+++ b/AlignmentNode.java
@@ -0,0 +1,1026 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.io.IOException;
+import java.lang.ClassNotFoundException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+public class AlignmentNode implements Serializable {
+    
+    String sequence;
+    String alphabet;
+    String equateAlphabet;
+    String name;
+    String tree;
+    float distance;
+    int nodeNumber;
+
+    String minimumProbNode = new String();
+    int minimumProbNumber;
+
+    int treeX;
+    int treeY;
+
+    boolean isUnique = true;
+    int sampleTimes = 0;
+
+    boolean isBandWarning = false;
+
+    boolean hasTrailers = false;
+    int start0=0;
+    int start1=0;
+    int end0=0;
+    int end1=0;
+
+    double viterbiEnd;
+    double forwardEnd;
+
+    double[][] charProb; 
+    boolean isTerminal;
+    int numChild;
+
+    double[] postProb;
+    int[][] cellPath;
+
+    AlignmentNode[] child;
+    AlignmentNode parent;
+    ProAlign pa;
+
+    AlignmentNode(ProAlign pa, String tree, float distance) {
+
+	this.pa = pa;
+	this.tree = tree;
+	this.distance = Math.abs(distance);
+	parent = this;
+
+	sequence = new String();
+
+	// If this is internal node..
+	//
+	if(tree.indexOf(",")>0) { 
+
+	    nodeNumber = pa.getNodeNumber(); // set unique number 
+	    child = new AlignmentNode[2];
+	    
+	    String[] trees;
+	    float[] distances;
+	    TreeReader tr = new TreeReader();
+
+	    if(nodeNumber==0) { // root is trifurcating: correction.
+
+		ProAlign.log("AlignmentNode: "+tree);
+
+		name = "root";
+		
+		trees = tr.divideTree(tree);
+		distances = tr.distance;
+
+	    } else {            // other internal nodes bifurcating
+
+		ProAlign.log("AlignmentNode: "+tree);
+
+		name = "node"+nodeNumber;
+		trees = tr.divideTree(tree);
+		distances = tr.distance;
+	    }
+
+	    child[0] = new AlignmentNode(pa, trees[0], distances[0]);
+	    child[1] = new AlignmentNode(pa, trees[1], distances[1]);
+
+	    numChild = getNumChild();
+	    
+	}
+	
+	// ..else this is terminal node.
+	//
+	else { 
+	    
+	    name = tree;
+	    alphabet = pa.sm.alphabet;
+	    equateAlphabet = pa.sm.equateAlphabet;
+	    sequence = (String) pa.seqs.get(name);
+	    isTerminal = true;
+	    numChild = 0;
+
+	    ProAlign.log("AlignmentNode: "+name+":"+distance+", "+sequence.length());
+	
+	    // Observed charcter has probability of 1, others 0.
+	    //
+	    charProb = new double[sequence.length()][alphabet.length()];
+	    
+	    for(int i=0; i<sequence.length();i++) {
+		if(ProAlign.isDna) {
+		    if(equateAlphabet.indexOf(sequence.charAt(i)) < 4) {
+			charProb[i][equateAlphabet.indexOf(sequence.charAt(i))] = 1d;
+		    } else { // uses equate characters 
+			if(sequence.charAt(i)=='R') {
+			    charProb[i][0] = 0.5d;
+			    charProb[i][2] = 0.5d;
+			} else if(sequence.charAt(i)=='Y') {
+			    charProb[i][1] = 0.5d;
+			    charProb[i][3] = 0.5d;
+			} else if(sequence.charAt(i)=='M') {
+			    charProb[i][0] = 0.5d;
+			    charProb[i][1] = 0.5d;
+			} else if(sequence.charAt(i)=='K') {
+			    charProb[i][2] = 0.5d;
+			    charProb[i][3] = 0.5d;
+			} else if(sequence.charAt(i)=='S') {
+			    charProb[i][1] = 0.5d;
+			    charProb[i][2] = 0.5d;
+			} else if(sequence.charAt(i)=='W') {
+			    charProb[i][0] = 0.5d;
+			    charProb[i][3] = 0.5d;
+			} else if(sequence.charAt(i)=='H') {
+			    charProb[i][0] = (double)(1d/3d);
+			    charProb[i][1] = (double)(1d/3d);
+			    charProb[i][3] = (double)(1d/3d);
+			} else if(sequence.charAt(i)=='B') {
+			    charProb[i][1] = (double)(1d/3d);
+			    charProb[i][2] = (double)(1d/3d);
+			    charProb[i][3] = (double)(1d/3d);
+			} else if(sequence.charAt(i)=='V') {
+			    charProb[i][0] = (double)(1d/3d);
+			    charProb[i][1] = (double)(1d/3d);
+			    charProb[i][2] = (double)(1d/3d);
+			} else if(sequence.charAt(i)=='D') {
+			    charProb[i][0] = (double)(1d/3d);
+			    charProb[i][2] = (double)(1d/3d);
+			    charProb[i][3] = (double)(1d/3d);
+			} else if(sequence.charAt(i)=='N') {
+			    charProb[i][0] = 0.25d;
+			    charProb[i][1] = 0.25d;
+			    charProb[i][2] = 0.25d;
+			    charProb[i][3] = 0.25d;
+			} else if(sequence.charAt(i)=='U') {
+			    charProb[i][3] = 1d;
+			}
+		    }
+		} else {
+		    if(equateAlphabet.indexOf(sequence.charAt(i)) < 20) {
+			charProb[i][equateAlphabet.indexOf(sequence.charAt(i))] = 1d;
+		    } else { // uses equate characters 
+			for(int j=0; j<20; j++) {
+			    charProb[i][j] = pa.sm.aaFreqs[j];
+			}
+		    }
+		}
+	    }	
+	    
+	}
+    }
+
+    void align() throws TraceBackException, Exception {
+
+	// If there are childs, align them first.
+	//
+	try {
+	    if(child[0].getNumChild()>1) {
+		child[0].align();
+	    }
+
+	    if(child[1].getNumChild()>1) {
+		child[1].align();
+	    }
+	} catch(TraceBackException tbe) { // throw exception if close to the edge
+	    throw tbe;
+	}
+
+	// NEW TRAILING >>
+
+	int[] trail = new int[2];
+	double[][] midseq0 = new double[0][0];
+	double[][] midseq1 = new double[0][0];
+
+	if(ProAlign.removeTrailing) {
+	    
+	    CheckTrailing ct = new CheckTrailing(pa);
+	    trail = ct.trailing(child[0].sequence.trim(),child[1].sequence.trim());
+
+	    if(Math.abs(trail[0])>ProAlign.offset || Math.abs(trail[1])>ProAlign.offset) {
+
+		if(trail[0]>ProAlign.offset) {
+		    start0=trail[0]-ProAlign.offset;
+		}
+		if(trail[1]>ProAlign.offset) {
+		    end0 = trail[1]-ProAlign.offset;
+		}
+
+		midseq0 = new double[child[0].charProb.length-end0-start0][child[0].charProb[0].length];
+
+		int k=0;
+		for(int i=start0; i<child[0].charProb.length-end0;i++) {
+		    int l=0;
+		    for(int j=0; j<child[0].charProb[i].length; j++) {
+			midseq0[k][l++] = child[0].charProb[i][j];	
+		    }
+		    k++;
+		}
+
+	
+		if(-1*trail[0]>ProAlign.offset) {
+		    start1=Math.abs(trail[0])-ProAlign.offset;
+		}
+		if(-1*trail[1]>ProAlign.offset) {
+		    end1 = Math.abs(trail[1])-ProAlign.offset;
+		}
+
+		midseq1 = new double[child[1].charProb.length-end1-start1][child[1].charProb[0].length];
+
+	        k=0;
+		for(int i=start1; i<child[1].charProb.length-end1;i++) {
+		    int l=0;
+		    for(int j=0; j<midseq1[0].length; j++) {
+			midseq1[k][l++] = child[1].charProb[i][j];	
+		    }
+		    k++;
+		}
+		hasTrailers=true;
+		
+	    }
+	}
+
+	// << NEW TRAILING
+
+	// Child's nodes are aligned, align childs.
+	//
+	AlignmentLoop loop = new AlignmentLoop(parent.pa,AlignmentNode.this);
+
+	ProAlign.log(" "+name+ ": aligning "+child[0].name+" and "+child[1].name);
+	if(ProAlign.isResultWindow) {
+	    ResultWindow.updateInfo(" Creating multiple alignment: aligning '"+name+"'.");
+	}
+
+	try {
+	    if(hasTrailers) {
+		loop.align(midseq0,midseq1,child[0].distance,child[1].distance);
+	    } else {
+		loop.align(child[0].charProb,child[1].charProb,child[0].distance,child[1].distance);
+	    }
+	} catch(Exception e) { 
+//	    System.out.println("AlignmentNode: throws an exception");
+	    throw e; 
+	}
+
+	viterbiEnd = loop.vitEnd;
+	forwardEnd = loop.fwdEnd;
+
+	TraceBackPath path = new TraceBackPath(parent,loop);
+	try {
+	    charProb = path.getNode(ProAlign.trackBest); // get best or sample.
+	    postProb = path.postProb;
+	    cellPath = path.cellPath;
+
+	    if(hasTrailers) {
+		viterbiEnd += path.trailProb;
+		forwardEnd += path.trailProb;
+	    }
+
+	} catch(TraceBackException tbe) {
+	    String error = tbe.getMessage();
+	    throw new TraceBackException(" Aligning "+name+":\n  "+error);
+	}
+
+/*
+	System.out.print("\n0: ");
+	for(int x=0; x<cellPath.length; x++) {
+	    System.out.print(cellPath[x][0]+",");
+	}
+	System.out.print("\n1: ");
+	for(int x=0; x<cellPath.length; x++) {
+	    System.out.print(cellPath[x][1]+",");
+	}
+//*/
+	isUnique = path.isUnique;
+	sampleTimes = path.sampleTimes;
+	isBandWarning = path.isBandWarning;
+
+	createSequence();
+    }
+
+    // Create most likely sequence to be used for pw alignment
+    //
+    void createSequence() {
+
+	alphabet = pa.sm.alphabet;
+	sequence = new String();
+	for(int i=0; i<charProb.length; i++) {
+	    double best = 0d;
+	    char site = ' ';
+	    for(int j=0; j<charProb[0].length-1; j++) {
+		if(charProb[i][j]>best) {
+		    best = charProb[i][j];
+		    site = alphabet.charAt(j);
+		}
+	    }
+	    sequence += site;
+	}
+    }
+
+    // Get maximum depth (#internal nodes) of the tree.
+    //
+    int getTreeDepth() { 
+	if(isTerminal) {
+	    return 0;
+	}
+	if(child[0].getTreeDepth()>child[1].getTreeDepth()){
+	    return child[0].getTreeDepth()+1;
+	}else {
+	    return child[1].getTreeDepth()+1; 
+	}
+    }
+
+    // Get #terminal nodes below this node.
+    //
+    int getNumChild() {
+	if(isTerminal) {
+	    return 1;
+	}
+	return (child[0].getNumChild() + child[1].getNumChild());
+    }
+
+    // Get #times the path was sampled.
+    //
+    int getSampleTimes() {
+	int times = parent.sampleTimes;
+	if(!child[0].isTerminal) {
+	    times += child[0].getSampleTimes();
+	}
+	if(!child[1].isTerminal) {
+	    times += child[1].getSampleTimes();
+	}
+	return times;
+    }
+
+    // Was the path sampled.
+    //
+    boolean isUnique() {
+	boolean unique0 = true;
+	boolean unique1 = true;
+	if(!child[0].isTerminal) {
+	    unique0 = child[0].isUnique();
+	}
+	if(!child[1].isTerminal) {
+	    unique1 = child[1].isUnique();
+	}
+	if(parent.isUnique && unique0 && unique1) {
+	    return true;
+	} else {
+	    return false;
+	}
+    }
+
+    
+    // Was the path close to the edge.
+    //
+    boolean isBandWarning() {
+	boolean warning0 = false;
+	boolean warning1 = false;
+	if(!child[0].isTerminal) {
+	    warning0 = child[0].isBandWarning();
+	}
+	if(!child[1].isTerminal) {
+	    warning1 = child[1].isBandWarning();
+	}
+	if(parent.isBandWarning || warning0 || warning1) {
+	    return true;
+	} else {
+	    return false;
+	}
+    }
+
+    // Get alphabet.
+    //
+    String getAlphabet() {
+	if(isTerminal) {
+	    return alphabet;
+	}
+	return child[0].getAlphabet();
+    }
+
+    // Get real site number at node 'str'.
+    //
+    int getSiteAt(int site, String str) {
+
+	if(isTerminal) {
+	    if(site > sequence.length()-1){ // correct for values outside of range.
+		return -1; 
+	    } else if(site<0) {
+		return -1;
+	    } else if(name.equals(str)) {   // match - return!
+		return site;
+	    } else {
+		return -1;
+	    }
+
+	} else {
+	    if(site > cellPath.length-1){
+		return -1;
+	    } else if(site<0) {
+		return -1;
+	    } else if(name.equals(str)) {
+		return site;
+	    } else if(child[0].getSiteAt(cellPath[site][0]-2, str)>=0) { // correct
+		return child[0].getSiteAt(cellPath[site][0]-2, str);     // for the
+	    } else if(child[1].getSiteAt(cellPath[site][1]-2, str)>=0) { // difference
+		return child[1].getSiteAt(cellPath[site][1]-2, str);     // on numbers.
+	    } else {
+		return -1;
+	    }
+	}
+    }
+
+    // Get characters at 'site'; returns an alignment column.
+    //
+    char[] getCharacterAt(int site) {
+	
+	char[] chars;
+
+	// Terminal node returns a character or a gap
+	//
+	if(isTerminal) {
+	    chars = new char[1];
+	    if(site<0) {
+		chars[0] = '-';		
+	    } else {
+		chars[0] = sequence.charAt(site);
+	    }
+	    return chars;
+	}
+
+	// Internal node returns a vector of..
+	//
+	chars = new char[numChild];
+	if(site<0) {                       // ..gaps..
+	    for(int i=0; i<numChild; i++) {
+		chars[i] = '-';
+	    }
+	} else {                           // ..or characters on nodes below.
+	    
+	    char[] chars0 = child[0].getCharacterAt(cellPath[site][0]-2);
+	    char[] chars1 = child[1].getCharacterAt(cellPath[site][1]-2);
+
+	    int c=0;
+	    for(int i=0; i<chars0.length; i++) {
+		chars[c++] = chars0[i];
+	    }
+	    for(int i=0; i<chars1.length; i++) {
+		chars[c++] = chars1[i];
+	    }	    
+	}
+	return chars;
+    }
+
+    // Get sum of viterbiEnds.
+    //
+    double sumViterbiEnd() {
+	if(isTerminal) {
+	    return 0d;
+	} else {
+	    double sum = child[0].sumViterbiEnd() + child[1].sumViterbiEnd() + viterbiEnd;
+	    return sum;
+	}
+    }
+
+    // Get sum of forwardEnds.
+    //
+    double sumForwardEnd() {
+	if(isTerminal) {
+	    return 0d;
+	} else {
+	    double sum = child[0].sumForwardEnd() + child[1].sumForwardEnd() + forwardEnd;
+	    return sum;
+	}
+    }
+
+    // Get posterior probability at 'site' for full column. This uses log's!
+    //
+    double[] getPostProbAt(int site) {
+	
+	double[] post;
+
+	// Terminal node returns 0 (log(0) = 1) or -oo (log(-oo) = 0)
+	//
+	if(isTerminal) {
+	    post = new double[1];
+	    if(site<0) {
+		post[0] = Double.NEGATIVE_INFINITY;		
+	    } else {
+		post[0] = 0d;
+	    }
+	    return post;
+	}
+	
+	// Note: #all nodes = 2*#terminal nodes-1.
+	//
+	post = new double[2*numChild-1];
+
+	// Outside of range.
+	//
+	if(site<0) {
+	    for(int i=0; i<post.length; i++) {
+		post[i] = Double.NEGATIVE_INFINITY;
+	    }
+	}
+
+	// Order: child[0], parent, child[1]
+	//
+	else {
+
+	    double[] post0 = child[0].getPostProbAt(cellPath[site][0]-2);
+	    double[] post1 = child[1].getPostProbAt(cellPath[site][1]-2);
+
+	    int c=0;
+	    for(int i=0; i<post0.length; i++) {
+		post[c++] = post0[i];
+	    }
+	    post[c++] = postProb[site];
+	    for(int i=0; i<post1.length; i++) {
+		post[c++] = post1[i];
+	    }	    
+	}
+	return post;
+	    
+    }
+
+    // Get posterior probability at 'site' for int. nodes. This uses log's!
+    //
+    double[] getInternalPostProbAt(int site) {
+	
+	double[] post = new double[numChild-1];
+	
+	// Outside of range.
+	//
+	if(site<0) {
+	    for(int i=0; i<post.length; i++) {
+		post[i] = Double.NEGATIVE_INFINITY;
+	    }
+	}
+
+	// Order: child[0], parent, child[1]
+	//
+	else {
+	    double[] post0 = new double[0];
+	    double[] post1 = new double[0];
+
+	    if(!child[0].isTerminal) {
+		post0 = child[0].getInternalPostProbAt(cellPath[site][0]-2);
+	    }
+	    if(!child[1].isTerminal) {
+		post1 = child[1].getInternalPostProbAt(cellPath[site][1]-2);
+	    }
+
+	    int c=0;
+	    for(int i=0; i<post0.length; i++) {
+		post[c++] = post0[i];
+	    }
+	    post[c++] = postProb[site];
+	    for(int i=0; i<post1.length; i++) {
+		post[c++] = post1[i];
+	    }	    
+	}
+	return post;
+	    
+    }
+
+    // Get minimum posterior probability at 'site' for int. nodes. This uses log's!
+    //
+    double getMinimumInternalPostProbAt(int site) {
+	
+	minimumProbNode = name;
+	minimumProbNumber = nodeNumber;
+
+	double minPost;
+	// Outside of range.
+	//
+	if(site<0) {
+
+	    minPost = 0d; 
+
+	} else {
+
+	    minPost = postProb[site];
+	    double tmp = 0d;
+
+	    if(!child[0].isTerminal) {
+		tmp = child[0].getMinimumInternalPostProbAt(cellPath[site][0]-2);
+	    }
+	    if(tmp < minPost) {
+		minPost = tmp;
+		minimumProbNode = child[0].getMinimumInternalPostProbNode();
+		minimumProbNumber = child[0].getMinimumInternalPostProbNumber();
+	    } else if(tmp == minPost) {
+		minimumProbNode += ", "+child[0].getMinimumInternalPostProbNode();
+		minimumProbNumber = child[0].getMinimumInternalPostProbNumber();
+	    }
+	    if(!child[1].isTerminal) {
+		tmp = child[1].getMinimumInternalPostProbAt(cellPath[site][1]-2);
+	    }
+	    if(tmp < minPost) {
+		minPost = tmp;
+		minimumProbNode = child[1].getMinimumInternalPostProbNode();
+		minimumProbNumber = child[1].getMinimumInternalPostProbNumber();
+	    } else if(tmp == minPost) {
+		minimumProbNode += ", "+child[1].getMinimumInternalPostProbNode();
+		minimumProbNumber = child[1].getMinimumInternalPostProbNumber();
+	    }
+
+	}
+	return minPost;
+	    
+    }
+
+    // Get minimum posterior probability at 'site' for int. nodes. This uses log's!
+    //
+    String getMinimumInternalPostProbNode() {
+	return minimumProbNode;
+    }
+    int getMinimumInternalPostProbNumber() {
+	return minimumProbNumber;
+    }
+
+    // Get posterior probability at 'site' for sequence 'seq'. This uses log's!
+    //
+    double getOnePostProbAt(int site, String seq) {
+
+	// Outside of range.
+	//
+	if(site<0) {
+	    return Double.NEGATIVE_INFINITY; 
+	}
+	if(name.equals(seq)) {
+	    return postProb[site];
+	}
+
+	if(!child[0].isTerminal) {
+	    double post = child[0].getOnePostProbAt(cellPath[site][0]-2,seq);
+	    if(post!=Double.NEGATIVE_INFINITY) {
+		return post;
+	    }	
+	}
+	if(!child[1].isTerminal) {
+	    double post = child[1].getOnePostProbAt(cellPath[site][1]-2,seq);
+	    if(post!=Double.NEGATIVE_INFINITY) {
+		return post;
+	    }  
+	}
+	
+	return Double.NEGATIVE_INFINITY;
+    }
+
+
+    // Get viterbiEnd for int. nodes. This uses log's!
+    //
+    double[] getInternalViterbiEnd() {
+	
+	double[] vit = new double[numChild-1];
+	
+	// Order: child[0], parent, child[1]
+	//
+	double[] vit0 = new double[0];
+	double[] vit1 = new double[0];
+	
+	if(!child[0].isTerminal) {
+	    vit0 = child[0].getInternalViterbiEnd();
+	}
+	if(!child[1].isTerminal) {
+	    vit1 = child[1].getInternalViterbiEnd();
+	}
+	
+	int c=0;
+	for(int i=0; i<vit0.length; i++) {
+	    vit[c++] = vit0[i];
+	}
+	vit[c++] = viterbiEnd;
+	for(int i=0; i<vit1.length; i++) {
+	    vit[c++] = vit1[i];
+	}	    
+	
+	return vit;
+	    
+    }
+
+
+    // Get terminal sequences.
+    //
+    String[] getTerminalSequences() {
+	
+	String[] sequences;
+
+	if(isTerminal) {
+	    sequences = new String[1];
+	    sequences[0] = sequence;
+	} else {
+	    sequences = new String[numChild]; 
+	    String[] seqs0 = child[0].getTerminalSequences();
+	    String[] seqs1 = child[1].getTerminalSequences();
+
+	    int c=0;
+	    for(int i=0; i<seqs0.length; i++) {
+		sequences[c++] = seqs0[i];
+	    }
+	    for(int i=0; i<seqs1.length; i++) {
+		sequences[c++] = seqs1[i];
+	    }	    
+	}
+	return sequences;
+    }
+
+    // Get terminal names only.
+    //
+    String[] getTerminalNames() {
+	
+	String[] names;
+
+	if(isTerminal) {
+	    names = new String[1];
+	    names[0] = name;
+	} else {
+	    names = new String[numChild]; 
+	    String[] names0 = child[0].getTerminalNames();
+	    String[] names1 = child[1].getTerminalNames();
+
+	    int c=0;
+	    for(int i=0; i<names0.length; i++) {
+		names[c++] = names0[i];
+	    }
+	    for(int i=0; i<names1.length; i++) {
+		names[c++] = names1[i];
+	    }	    
+	}
+	return names;
+    }
+
+    // Get terminal pair names only.
+    //
+    String[][] getTerminalPairNames() {
+	
+	String[][] names;
+
+	if(child[0].isTerminal && child[1].isTerminal) {
+	     names = new String[1][2];
+	     names[0][0] = child[0].name;
+	     names[0][1] = child[1].name;
+	} else if(child[0].isTerminal) {
+	    names = child[1].getTerminalPairNames();
+	} else if(child[1].isTerminal) {
+	    names = child[0].getTerminalPairNames();
+	} else {
+	    String[][] names0 = child[0].getTerminalPairNames();
+	    String[][] names1 = child[1].getTerminalPairNames();
+	    names = new String[names0.length+names1.length][2]; 
+	    int c=0;
+	    for(int i=0; i<names0.length; i++) {
+		names[c][0] = names0[i][0];
+		names[c++][1] = names0[i][1];		
+	    }
+	    for(int i=0; i<names1.length; i++) {
+		names[c][0] = names1[i][0];
+		names[c++][1] = names1[i][1];
+	    }	    
+	}
+	return names;
+    }
+
+    // Get internal names only.
+    //
+    String[] getInternalNames() {
+	
+	String[] names = new String[numChild-1];
+	if(numChild<=2) { // no internal nodes below.
+	    names[0] = name;
+	}
+	
+	else {
+
+	    String[] names0 = new String[0];
+	    String[] names1 = new String[0];
+
+	    if(!child[0].isTerminal) {
+		names0 = child[0].getInternalNames();
+	    }
+	    if(!child[1].isTerminal) {
+		names1 = child[1].getInternalNames();
+	    }
+    
+	    int c=0;
+	    for(int i=0; i<names0.length; i++) {
+		names[c++] = names0[i];
+	    }
+	    names[c++] = name;
+	    for(int i=0; i<names1.length; i++) {
+		names[c++] = names1[i];
+	    }	    
+	}
+	return names;
+    }
+
+    // Get all names
+    //
+    String[] getAllNames() {
+	
+	String[] names;
+
+	if(isTerminal) {
+	    names = new String[1];
+	    names[0] = name;	    
+	} 
+
+	// Order: child[0], parent, child[1]
+	//
+	else {
+
+	    names = new String[2*numChild-1]; 
+	    String[] names0 = child[0].getAllNames();
+	    String[] names1 = child[1].getAllNames();
+
+	    int c=0;
+	    for(int i=0; i<names0.length; i++) {
+		names[c++] = names0[i];
+	    }
+	    names[c++] = name;
+	    for(int i=0; i<names1.length; i++) {
+		names[c++] = names1[i];  
+	    }	    
+	}
+	return names;
+    }
+    
+    // Needed for graphics (used by getNodeNameAtXY).
+    //
+    void setTreeXY(int x, int y) {
+	treeX = x;
+	treeY = y;
+    }
+
+    // Get node that was clicked.
+    //
+    String getNodeNameAtXY(int x, int y) {
+	int prec = 4; // precision
+	if(Math.abs(treeX-x)<prec && Math.abs(treeY-y)<prec) {
+	    return parent.name;
+	}
+	if(!child[1].isTerminal) {
+	    String nodeName = child[1].getNodeNameAtXY(x,y);
+	    if(!nodeName.equals("")){
+		return nodeName;
+	    }
+	}
+	if(!child[0].isTerminal) {
+	    String nodeName = child[0].getNodeNameAtXY(x,y);
+	    if(!nodeName.equals("")){
+		return nodeName;
+	    }
+	}
+	return new String();
+    }
+
+    // Get node by name.
+    //
+    AlignmentNode getNodeNamed(String node) {
+	if(name.equals(node)) {
+	    return parent;
+	}
+	if(!child[0].isTerminal) {
+	    AlignmentNode kid = child[0].getNodeNamed(node);
+	    if(kid.name.equals(node)){
+		return kid;
+	    }
+	}
+	if(!child[1].isTerminal) {
+	    AlignmentNode kid = child[1].getNodeNamed(node);
+	    if(kid.name.equals(node)){
+		return kid;
+	    }
+	}
+	return parent;
+    }
+    
+    private void writeObject(ObjectOutputStream s)
+	throws IOException {
+
+	s.writeObject("0.2");
+	s.writeBoolean(ProAlign.isDna);
+	s.writeBoolean(isTerminal);
+	s.writeObject(name);
+	s.writeObject(tree);
+	s.writeObject(parent);
+	s.writeInt(numChild);
+	
+	s.writeInt(charProb.length);
+	s.writeInt(charProb[0].length);
+	for(int i=0; i<charProb.length; i++) {
+	    for(int j=0; j<charProb[0].length; j++) {
+		s.writeDouble(charProb[i][j]);
+	    }	
+	}
+
+	if(isTerminal) {
+	    s.writeObject(sequence);
+	    s.writeObject(alphabet);
+
+	} else {
+	    s.writeInt(nodeNumber);
+	    s.writeInt(treeX);
+	    s.writeInt(treeY);
+	    s.writeObject(child[0]);
+	    s.writeObject(child[1]);
+
+	    s.writeDouble(viterbiEnd);
+	    s.writeDouble(forwardEnd);
+
+	    s.writeInt(postProb.length);
+	    for(int i=0; i<postProb.length; i++) {
+		s.writeDouble(postProb[i]);
+	    }
+	 
+	    s.writeInt(cellPath.length);
+	    s.writeInt(cellPath[0].length);
+	    for(int i=0; i<cellPath.length; i++) {
+		for(int j=0; j<cellPath[0].length; j++) {
+		    s.writeInt(cellPath[i][j]);
+		}	
+	    }
+	}
+    }
+
+     private void readObject(ObjectInputStream s)
+	 throws IOException, ClassNotFoundException  {
+
+	 // version number can be used for backward compatibility! 
+	 String version = (String) s.readObject(); 
+	 ProAlign.isDna = s.readBoolean();
+	 isTerminal = s.readBoolean();
+	 name = (String) s.readObject();
+	 tree = (String) s.readObject();
+	 parent = (AlignmentNode) s.readObject();
+	 numChild = s.readInt();
+	
+	 int k = s.readInt();
+	 int l = s.readInt();
+	 charProb = new double[k][l];
+	 for(int i=0; i<charProb.length; i++) {
+	     for(int j=0; j<charProb[0].length; j++) {
+		 charProb[i][j] = s.readDouble();
+	     }	
+	 }
+
+	if(isTerminal) {
+	    sequence = (String) s.readObject();
+	    alphabet = (String) s.readObject();
+
+	} else {
+	    nodeNumber = s.readInt();
+	    treeX = s.readInt();
+	    treeY = s.readInt();
+	    child = new AlignmentNode[2];
+	    child[0] = (AlignmentNode) s.readObject();
+	    child[1] = (AlignmentNode) s.readObject();
+
+	    viterbiEnd = s.readDouble();
+	    forwardEnd = s.readDouble();
+
+	    k = s.readInt(); 
+	    postProb = new double[k];
+	    for(int i=0; i<postProb.length; i++) {
+		postProb[i] = s.readDouble();
+	    }
+	 
+	    k = s.readInt();
+	    l = s.readInt();
+	    cellPath = new int[k][l];
+	    for(int i=0; i<cellPath.length; i++) {
+		for(int j=0; j<cellPath[0].length; j++) {
+		    cellPath[i][j] = s.readInt();
+		}	
+	    }
+	}
+     }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CheckSequence.java b/CheckSequence.java
new file mode 100644
index 0000000..b5f0935
--- /dev/null
+++ b/CheckSequence.java
@@ -0,0 +1,81 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class CheckSequence {
+
+    String error = new String("");
+    String alphabet;
+    CheckSequence() { 
+	ProAlign.log("CheckSequence");
+    }
+
+    boolean isDna(HashMap seqs) {
+	
+	Iterator seqKeys = seqs.keySet().iterator();	
+	String name, seq;
+	int acgt = 0, total = 0;
+	
+	while(seqKeys.hasNext()) {
+	    name = (String) seqKeys.next();
+	    seq = (String) seqs.get(name);
+	    
+	    for(int i=0; i<seq.length(); i++) {
+		if(seq.charAt(i)=='A') {
+		    acgt++;
+		} else if(seq.charAt(i)=='C') {
+		    acgt++;
+		} else if(seq.charAt(i)=='G') {
+		    acgt++;
+		} else if(seq.charAt(i)=='T') {
+		    acgt++;
+		} else if(seq.charAt(i)=='U') {
+		    acgt++;
+		}
+		total++;
+	    }
+	}
+	if(((double) acgt/(double) total)>0.9d) {
+	    return true;
+	}
+	return false;
+    }
+
+    boolean isFromAlphabet(HashMap seqs,String alphabet) {
+
+	boolean isFine = true;
+	Iterator seqKeys = seqs.keySet().iterator();	
+	String name, seq;
+
+	while(seqKeys.hasNext()) {
+	    name = (String) seqKeys.next();
+	    seq = (String) seqs.get(name);
+	    
+	    for(int i=0; i<seq.length(); i++) {
+		if(alphabet.indexOf(seq.charAt(i))<0) {
+		    isFine = false;
+		    error += "\n "+name+": '"+seq.charAt(i)+
+			"' is not from alphabet '"+alphabet+"'!";
+		    ProAlign.log.print("\n "+name+": '"+seq.charAt(i)+
+				       "' is not from alphabet '"+alphabet+"'!");	
+		} 
+	    }
+	}
+	return isFine;
+    }
+
+    String getError() {
+	return error;
+    }
+}
diff --git a/CheckTrailing.java b/CheckTrailing.java
new file mode 100644
index 0000000..d40316f
--- /dev/null
+++ b/CheckTrailing.java
@@ -0,0 +1,55 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+class CheckTrailing {
+
+    PwAlignment pwa;
+
+    CheckTrailing(ProAlign pa) { 
+
+	ProAlign.log("CheckTrailing");
+
+	PwSubstitutionMatrix psm = new PwSubstitutionMatrix();
+	String pwAlphabet;
+	int[][] pwSubst;
+	int gOpen, gExt;
+	
+	if(ProAlign.isDna){
+	    pwAlphabet = psm.dnaAlphabet;
+	    pwSubst = psm.swdna;
+	    gOpen = -1*pa.pwDnaOpen;
+	    gExt = -1*pa.pwDnaExt;
+	    
+	} else {
+	    
+	    pwAlphabet = psm.protAlphabet;
+	    if(pa.pwProtMatrix.equals("pam60")) {
+		pwSubst = psm.pam60;
+	    } else if(pa.pwProtMatrix.equals("pam160")) {
+		pwSubst = psm.pam160;
+	    } else if(pa.pwProtMatrix.equals("pam250")) {
+		    pwSubst = psm.pam250;
+	    } else {
+		pwSubst = psm.pam120;
+	    }
+	    gOpen = -1*pa.pwProtOpen;
+	    gExt = -1*pa.pwProtExt;
+	}
+	
+	pwa = new PwAlignment(pwSubst,gOpen,gExt,pwAlphabet,ProAlign.isDna);
+    }
+
+    int[] trailing(String s1, String s2) {	
+	return pwa.trailing(s1,s2);
+    }
+
+}
diff --git a/CheckTreeAndData.java b/CheckTreeAndData.java
new file mode 100644
index 0000000..bb8779d
--- /dev/null
+++ b/CheckTreeAndData.java
@@ -0,0 +1,46 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.util.HashMap;
+
+class CheckTreeAndData {
+
+    boolean isFine = true;
+    boolean isDone = false;
+    String error = new String("");
+
+    CheckTreeAndData(String[] treeNodes, HashMap seqs) {
+	
+	ProAlign.log("CheckTreeAndData");
+
+	for(int i=0; i<treeNodes.length; i++) {
+	    isDone = true;
+	    if(!seqs.containsKey(treeNodes[i])) {
+		isFine = false;
+		ProAlign.log.println("CheckTreeAndData: "+treeNodes[i]+": no sequence found!");
+		error = ""+treeNodes[i]+": no sequence found!";
+	    } 
+	}
+    }
+    
+    boolean nodesAreSame() {
+	if(isDone) {
+	    return isFine;
+	} else {
+	    return false;
+	}
+    }
+    
+    String getError() {
+	return error;
+    }
+}
diff --git a/EstimateFrequencies.java b/EstimateFrequencies.java
new file mode 100644
index 0000000..1576e19
--- /dev/null
+++ b/EstimateFrequencies.java
@@ -0,0 +1,61 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+class EstimateFrequencies {
+
+    double[] charFreqs;
+    
+    EstimateFrequencies(String[] dataArray, String alphabet, String equateAlphabet) {
+
+	ProAlign.log("EstimateFrequencies");
+
+	equateAlphabet = equateAlphabet+"-";
+
+	int[] eqCounts = new int[equateAlphabet.length()];
+
+	int sum = 0;
+	for(int i=0; i<dataArray.length; i++) {
+	    for(int j=0; j<dataArray[i].length(); j++) {  
+		eqCounts[equateAlphabet.indexOf(dataArray[i].charAt(j))]++;
+		sum++;
+	    }
+	}
+
+	float[] counts = new float[alphabet.length()];
+	counts[0] = (float)eqCounts[0]+(float)eqCounts[4]/2f+(float)eqCounts[6]/2f+
+	    (float)eqCounts[9]/2f+(float)eqCounts[10]/3f+(float)eqCounts[12]/3f+
+	    +(float)eqCounts[13]/3f+(float)eqCounts[14]/4f;
+	counts[1] = (float)eqCounts[1]+(float)eqCounts[5]/2f+(float)eqCounts[6]/2f+
+	    (float)eqCounts[8]/2f+(float)eqCounts[10]/3f+(float)eqCounts[11]/3f+
+	    +(float)eqCounts[12]/3f+(float)eqCounts[14]/4f;
+	counts[2] = (float)eqCounts[2]+(float)eqCounts[4]/2f+(float)eqCounts[7]/2f+
+	    (float)eqCounts[8]/2f+(float)eqCounts[11]/3f+(float)eqCounts[12]/3f+
+	    +(float)eqCounts[13]/3f+(float)eqCounts[14]/4f;
+	counts[3] = (float)eqCounts[3]+(float)eqCounts[5]/2f+(float)eqCounts[7]/2f+
+	    (float)eqCounts[9]/2f+(float)eqCounts[10]/3f+(float)eqCounts[11]/3f+
+	    +(float)eqCounts[13]/3f+(float)eqCounts[14]/4f+eqCounts[15];
+	counts[4] = (float)eqCounts[16];
+
+	charFreqs = new double[alphabet.length()];
+	for(int i=0; i<alphabet.length(); i++) {
+	    charFreqs[i] = (double) counts[i]/sum; 
+	}
+
+	for(int i=0; i<alphabet.length(); i++) {
+	    ProAlign.log(" "+alphabet.charAt(i)+":"+charFreqs[i]);
+	}
+    }
+
+    double[] getCharacterFrequencies() {
+	return charFreqs;
+    }
+}
diff --git a/InFile.java b/InFile.java
new file mode 100644
index 0000000..c9265a2
--- /dev/null
+++ b/InFile.java
@@ -0,0 +1,16 @@
+package proalign;
+
+import java.io.File;
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+
+public class InFile extends DataInputStream {
+    public InFile(String filename) throws FileNotFoundException {
+	super(new BufferedInputStream(new FileInputStream(filename)));
+    }
+    public InFile(File file) throws FileNotFoundException {
+	this(file.getPath());
+    }
+}
diff --git a/MinimumProbWindow.java b/MinimumProbWindow.java
new file mode 100644
index 0000000..5f15ba1
--- /dev/null
+++ b/MinimumProbWindow.java
@@ -0,0 +1,87 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  sequence alignment comparison program<p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JViewport;
+import javax.swing.JScrollPane;
+import javax.swing.JScrollBar;
+import javax.swing.BorderFactory;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Container;
+import java.awt.Point;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowAdapter;
+
+public class MinimumProbWindow extends JFrame {
+
+    JScrollPane sPane;
+    int height;
+    double[] minProb;
+
+    PixelRule pixRule;
+    PrintMinimumCurve curve;
+    ResultWindow rw;
+    String[] nodes;
+
+    MinimumProbWindow(double[] minProb, String[] nodes, ResultWindow rw) {
+
+	this.rw = rw;
+	this.nodes = nodes;
+	this.minProb = minProb;
+
+	height = this.getHeight();
+
+	setTitle("Minimum posterior probability");
+
+	curve = new PrintMinimumCurve(MinimumProbWindow.this, minProb, nodes);
+
+	// pixelruler.
+	pixRule = new PixelRule();
+	pixRule.setPreferredWidth((int) curve.getPreferredSize().getWidth());
+	pixRule.setIncrement(curve.xScale);
+	pixRule.setBackground(Color.white);
+
+
+	sPane = new JScrollPane(curve,
+				 JScrollPane.VERTICAL_SCROLLBAR_NEVER,
+				 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+
+
+	sPane.setViewportBorder(BorderFactory.createLineBorder(Color.black));
+	sPane.setColumnHeaderView(pixRule);
+	sPane.setBackground(Color.white);
+
+	Container cp = getContentPane();
+	cp.add(sPane);
+
+	addWindowListener(new WindowAdapter() {
+		public void windowClosing(WindowEvent e){
+		    PrintTree.numOpenWindows--;
+		    dispose();
+		}
+	    });
+    }
+
+    int getVisibleHeight() {
+	return sPane.getViewport().getHeight();
+    }
+}
+
+
+
+
+
+
+
diff --git a/NeighborJoining.java b/NeighborJoining.java
new file mode 100644
index 0000000..6186268
--- /dev/null
+++ b/NeighborJoining.java
@@ -0,0 +1,178 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author       Ari Loytynoja
+ * @version      1.0
+ */
+package proalign;
+
+// As Implemented by Studier and Keppler (1988) in Li (1997), p. 111.
+//
+class NeighborJoining {
+
+    String[] newOtus;
+    String tree = new String();
+    String tree3 = new String();
+
+    NeighborJoining(double[][] distance, String[] otus) {
+
+	ProAlign.log("NeighborJoining");
+
+	while(distance.length>2) {
+	    distance = joinNeighbors(distance, otus);
+	    otus = newOtus;
+	}
+
+	double dist = Math.abs((distance[0][0]+distance[0][1])/2d);
+
+
+	tree = "("+otus[0]+":"+dist+","+otus[1]+":"+dist+");";
+
+	if(otus[0].startsWith("(")&&otus[0].endsWith(")")) {
+	    tree3 = otus[0].substring(0,otus[0].length()-2)+","+
+		otus[1]+":"+dist*2d+");";
+	} else {
+	    tree3 = "("+otus[0]+":"+dist*2d+","+otus[1].substring(1)+";";;
+	}
+    }
+
+    String getTree() {
+	return tree;
+    }
+    String getTree3() {
+	return tree3;
+    }
+
+    double[][] joinNeighbors(double[][] distance, String[] otus) {
+
+	
+	int no = distance.length;
+	int otu1=0, otu2=0;
+	double minM = Double.POSITIVE_INFINITY;
+
+	double[] rDist = new double[no]; // sum of distances d_i,j
+
+	for(int i=0; i<no; i++) {
+	    for(int j=0; j<no; j++) {
+		rDist[i] += distance[i][j];
+	    }
+	}
+	for(int i=0; i<no; i++) {
+	    for(int j=0; j<no; j++) {
+		if(j==i) {
+		    continue;
+		}
+		double mDist = distance[i][j]-(rDist[i]+rDist[j])/(no-2);
+		if(mDist<minM) {
+		    minM = mDist;
+		    otu1 = Math.min(i,j);
+		    otu2 = Math.max(i,j);
+		}
+	    }
+	}
+
+	double brl1 = distance[otu1][otu2]/2+
+	    (rDist[otu1]-rDist[otu2])/(2*(no-2));
+	double brl2 = distance[otu1][otu2]-brl1;
+
+	    
+	double[][] newDistance = new double[no-1][no-1];
+	newOtus = new String[no-1];
+	int ci=0;
+	int cj=0;
+
+	for(int i=0; i<no; i++) {
+	    for(int j=0; j<no; j++) {
+		if(i==j) {
+		    continue;
+		}
+
+		if(j<otu2) {
+		    cj=j;
+		} else if(j==otu2) {
+		    continue;
+		} else {
+		   cj=j-1;
+		} 
+
+		if(i<otu2) {
+		    ci=i;
+		} else if(i==otu2) {
+		    continue;
+		} else { 
+		    ci=i-1;
+		}
+
+		if(i==otu1) {
+		    newDistance[ci][cj] = (distance[otu1][j]+distance[otu2][j]-
+						 distance[otu1][otu2])/2;
+		} else if(j==otu1) {  
+		    newDistance[ci][cj] = (distance[otu1][i]+distance[otu2][i]-
+					   distance[otu1][otu2])/2;
+		} else {
+		    newDistance[ci][cj] = distance[i][j];
+		}
+	    }
+	}
+	
+	for(int i=0; i<no; i++) {
+	    if(i==otu1) {
+		newOtus[i] = "("+otus[otu1]+":"+Math.abs(brl1)+","+
+		    otus[otu2]+":"+Math.abs(brl2)+")";
+		continue;
+	    } else if(i<otu2) {
+		ci = i;
+	    } else if(i==otu2) {
+		continue;
+	    } else {
+		ci=i-1;
+	    } 
+	    newOtus[ci] = otus[i];
+	}
+
+	return newDistance;
+    }
+
+    public static void main(String[] args) {
+
+	// Reads PHYLIP distance tables
+
+	double[][] distance = new double [0][0];
+	String[] otus = new String[0];
+
+	try {
+
+            InFile in = new InFile(args[0]);
+	    String row = in.readLine();
+	    int no = new Integer(row.trim()).intValue();
+
+	    otus = new String[no];
+	    distance = new double[no][no];
+	    
+	    int i=0;
+            while((row = in.readLine()) != null) {
+		otus[i] = row.substring(0,10).trim();
+		row = row.substring(11).trim();
+		for(int j=0; j<no-1; j++) {
+		    String value = row.substring(0,row.indexOf(" ")).trim();
+		    distance[i][j] = new Double(value).doubleValue();
+		    row = row.substring(row.indexOf(" ")).trim();
+		}		
+		distance[i][no-1] = new Double(row.trim()).doubleValue();
+		i++;
+	    }
+	 }  catch(Exception e) { 
+	     System.out.println("Problems with "+args[0]);
+	     e.printStackTrace();
+	     System.exit(0);
+	 }
+
+	NeighborJoining nj = new NeighborJoining(distance,otus);
+	System.out.println(nj.getTree());
+	System.out.println(nj.getTree3());
+    }
+}
diff --git a/NicePrint.java b/NicePrint.java
new file mode 100644
index 0000000..2c19831
--- /dev/null
+++ b/NicePrint.java
@@ -0,0 +1,50 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+class NicePrint {
+    NicePrint() { };
+    String dbl(double number, int length) {
+	return ((""+number+"          ").substring(0,length+1));
+    }
+
+    String rdbl(double val, int prec) {
+        String full = ""+val;
+        if(full.indexOf('.')>-1) {
+            String begin = full.substring(0,full.indexOf('.'));
+            String end = full.substring(full.indexOf('.')+1);
+            if(end.length()>prec) {
+		String zeros = new String();
+		while(end.startsWith("0")) {
+		    end = end.substring(1);
+		    zeros += "0";
+		}
+                char num = end.charAt(prec);
+                if(num=='0'||num=='1'||num=='2'||num=='3'||num=='4') {
+                    full = begin+"."+zeros+end.substring(0,prec);
+                } else {
+                    full = begin+"."+zeros+(new Integer(end.substring(0,prec)).intValue()+1);
+                }
+            }
+        }
+        return full;
+    }
+    
+    void pdbl(double number, int length) {
+	System.out.print((""+number+"          ").substring(0,length+1));
+    }
+    void str(String str) {
+	System.out.print(str);
+    }
+    void strl(String str) {
+	System.out.println(str);
+    }
+}
diff --git a/OpenDialog.java b/OpenDialog.java
new file mode 100644
index 0000000..1cd0e2a
--- /dev/null
+++ b/OpenDialog.java
@@ -0,0 +1,53 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.awt.Component;
+import java.awt.Font;
+import java.io.File;
+import java.io.IOException;
+import javax.swing.JTextArea;
+import javax.swing.BorderFactory;
+import javax.swing.border.Border;
+import javax.swing.JOptionPane;
+import javax.swing.JDialog;
+import java.awt.Color;
+
+public class OpenDialog extends JDialog {
+
+    Font font = new Font(ProAlign.paFontName, Font.PLAIN, ProAlign.paFontSize);
+    Component parent;
+    
+    OpenDialog(Component parent) {
+	this.parent = parent;
+    }
+
+    void showDialog(String title, String text) {
+
+	JTextArea ta = new JTextArea(text);
+	ta.setFont(font);
+	ta.setEditable(false);
+	ta.setBackground(Color.white);
+		
+	Border loweredbevel = BorderFactory.createLoweredBevelBorder();
+	ta.setBorder(loweredbevel);
+		
+	JOptionPane op = new JOptionPane();
+	op.setMessage(ta);
+	op.setMessageType(JOptionPane.PLAIN_MESSAGE);
+		
+	JDialog dialog = op.createDialog(parent,title);
+	dialog.setLocation(150,150);
+	dialog.show();
+    }
+}
+
+
diff --git a/OpenFileChooser.java b/OpenFileChooser.java
new file mode 100644
index 0000000..c2951c0
--- /dev/null
+++ b/OpenFileChooser.java
@@ -0,0 +1,55 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JFileChooser;
+import java.awt.Component;
+import java.awt.Font;
+import java.io.File;
+import java.io.IOException;
+
+public class OpenFileChooser extends JFileChooser {
+
+    Component parent; 
+    String txt;
+    boolean useFilter;
+
+    OpenFileChooser(Component parent, String txt, boolean useFilter) { 
+	this.parent = parent;
+	this.txt = txt;
+	this.useFilter = useFilter;
+    }
+
+    String openFile() {
+	
+	String filepath = new String("");
+	JFileChooser jfc = new JFileChooser(ProAlign.folderPath);
+	if(useFilter) {
+	    jfc.addChoosableFileFilter(new PAFileFilter());
+	}
+	int returnValue = jfc.showDialog(parent, txt);
+
+	if(returnValue == JFileChooser.APPROVE_OPTION) {
+	    File path;
+	    if (jfc.getCurrentDirectory().toString().endsWith(File.separator)) {
+		path = new File(jfc.getCurrentDirectory().toString()
+				+ jfc.getSelectedFile().getName());
+	    } else {
+		path = new File(jfc.getCurrentDirectory().toString()
+				+ File.separator + jfc.getSelectedFile().getName());
+	    }
+	    filepath = path.toString();
+	}
+	return filepath;
+
+    }
+}
+
diff --git a/OpenFolderChooser.java b/OpenFolderChooser.java
new file mode 100644
index 0000000..7102a5d
--- /dev/null
+++ b/OpenFolderChooser.java
@@ -0,0 +1,52 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JFileChooser;
+import java.awt.Component;
+import java.awt.Font;
+import java.io.File;
+import java.io.IOException;
+
+public class OpenFolderChooser extends JFileChooser {
+
+    Component parent; 
+    String txt;
+
+    OpenFolderChooser(Component parent, String txt) { 
+	this.parent = parent;
+	this.txt = txt;
+    }
+
+    String openFile() {
+	
+	String filepath = new String("");
+	JFileChooser jfc = new JFileChooser(ProAlign.folderPath);
+	jfc.setFileSelectionMode(1);
+
+	int returnValue = jfc.showDialog(parent, txt);
+
+	if(returnValue == JFileChooser.APPROVE_OPTION) {
+	    File path;
+	    if (jfc.getCurrentDirectory().toString().endsWith(File.separator)) {
+		path = new File(jfc.getCurrentDirectory().toString()
+				+ jfc.getSelectedFile().getName());
+	    } else {
+		path = new File(jfc.getCurrentDirectory().toString()
+				+ File.separator + jfc.getSelectedFile().getName());
+	    }
+	    filepath = path.toString();
+	}
+	return filepath;
+
+    }
+}
+
diff --git a/OutFile.java b/OutFile.java
new file mode 100644
index 0000000..3c47616
--- /dev/null
+++ b/OutFile.java
@@ -0,0 +1,20 @@
+package proalign;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+
+public class OutFile extends PrintStream {
+  public OutFile(String filename)
+    throws IOException {
+    super(
+      new BufferedOutputStream(
+        new FileOutputStream(filename)));
+  }
+  public OutFile(File file)
+    throws IOException {
+    this(file.getPath());
+  }
+}
diff --git a/OutOfMemoryException.java b/OutOfMemoryException.java
new file mode 100644
index 0000000..784262c
--- /dev/null
+++ b/OutOfMemoryException.java
@@ -0,0 +1,19 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+public class OutOfMemoryException extends Exception {
+
+    public OutOfMemoryException() {}
+    public OutOfMemoryException(String msg) {
+	super(msg);
+    }
+}
diff --git a/PAFileFilter.java b/PAFileFilter.java
new file mode 100644
index 0000000..e4a330a
--- /dev/null
+++ b/PAFileFilter.java
@@ -0,0 +1,46 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.io.File;
+import javax.swing.filechooser.FileFilter;
+
+public class PAFileFilter extends FileFilter {
+
+    // Accept all directories and files ending with ProAlign.fileExt . 
+    public boolean accept(File f) {
+        if (f.isDirectory()) {
+            return true;
+        }
+	
+	String s = f.getName();
+	int i = s.lastIndexOf('.');
+	String ext = new String();
+        if (i > 0 &&  i < s.length() - 1) {
+            ext = s.substring(i+1).toLowerCase();
+        }
+	
+	if (ext != null) {
+            if (ext.equals(ProAlign.fileExt)) {
+		return true;
+            } else {
+                return false;
+            }
+	}
+	
+        return false;
+    }
+    
+    // The description of this filter
+    public String getDescription() {
+        return "ProAlign Object-file";
+    }
+}
diff --git a/ParameterEstimates.java b/ParameterEstimates.java
new file mode 100644
index 0000000..42abbd0
--- /dev/null
+++ b/ParameterEstimates.java
@@ -0,0 +1,195 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author       Ari Loytynoja
+ * @version      1.0
+ */
+package proalign;
+
+import java.util.HashMap;
+
+public class ParameterEstimates {
+
+    String[][] pairs;
+    HashMap seqs;
+    PwAlignment pwa;
+
+    ParameterEstimates(ResultWindow rw,SetParameters sp) { 
+
+	this.seqs = rw.seqs;
+
+	PwSubstitutionMatrix psm = new PwSubstitutionMatrix();
+	String pwAlphabet;
+	int[][] pwSubst;
+	int gOpen, gExt;
+
+	if(ProAlign.isDna){
+	    pwAlphabet = psm.dnaAlphabet;
+	    pwSubst = psm.swdna;
+	    gOpen = -1*rw.pa.pwDnaOpen;
+	    gExt = -1*rw.pa.pwDnaExt;
+
+	} else {
+
+	    pwAlphabet = psm.protAlphabet;
+	    if(rw.pa.pwProtMatrix.equals("pam60")) {
+		pwSubst = psm.pam60;
+	    } else if(rw.pa.pwProtMatrix.equals("pam160")) {
+		pwSubst = psm.pam160;
+	    } else if(rw.pa.pwProtMatrix.equals("pam250")) {
+		pwSubst = psm.pam250;
+	    } else {
+		pwSubst = psm.pam120;
+	    }
+	    gOpen = -1*rw.pa.pwProtOpen;
+	    gExt = -1*rw.pa.pwProtExt;
+	}
+	
+	pwa = new PwAlignment(pwSubst,gOpen,gExt,pwAlphabet,ProAlign.isDna);
+
+	pairs = rw.root.getTerminalPairNames();
+
+	this.estimate();
+
+	sp.textDelta.setText((""+ProAlign.modelDelta+"      ").substring(0,5));
+	sp.textEpsil.setText((""+ProAlign.modelEpsilon+"      ").substring(0,5));
+	
+    }
+
+    ParameterEstimates(RunCommandLine rcl) {
+
+	ProAlign.log("ParameterEstimates");
+
+	AlignmentNode root = rcl.root;
+
+	seqs = rcl.pa.seqs;
+	pwa = rcl.pwa;
+
+	pairs = root.getTerminalPairNames();
+
+	this.estimate();
+    }
+
+    void estimate() {
+
+	double gapFreq = 0d;
+	double sumDelta = 0d;
+	double sumEpsilon = 0d;
+
+	for(int i=0; i<pairs.length; i++) {
+
+	    String s0 = (String)seqs.get(pairs[i][0]);
+	    String s1 = (String)seqs.get(pairs[i][1]);
+
+	    String[] revSeq =  pwa.revAligned(s0,s1);
+	    
+	    int all = 0;
+	    int ms = 0; // match sum
+	    int mn = 1; // match numebr
+	    int ml = 0; // match length
+	    int gs = 0;
+	    int gn = 1;
+	    int gl = 0;
+	    double mm = 5d; // match mean
+	    double mg = 5d;
+	    boolean isM = false;
+	    
+	    for(int k=0; k<revSeq[0].length(); k++) {
+		if(k==0) {
+		    if(revSeq[0].charAt(k)=='-' || revSeq[1].charAt(k)=='-') {
+			gn++;
+		    } else {
+			mn++;
+		    }
+		}
+		
+		if(revSeq[0].charAt(k)=='-' || revSeq[1].charAt(k)=='-') {
+		    gs++;
+		    gl++;
+		    if(isM) {
+			if(k>0) {
+			    if(mn>1) {
+				mm = ((double)mm*(mn-1)+ml)/mn;
+			    } else {
+				mm = (mm+ml)/2;
+			    }
+			    ml=0;
+			    gn++;
+			}
+			isM = false;				
+		    }
+		} else {
+		    ms++;
+		    ml++;
+		    if(!isM) {		    
+			if(k>0) {
+			    if(gn>1) {
+				mg = ((double)mg*(gn-1)+gl)/gn;
+			    } else {
+				mg = (mg+gl)/2;
+			    }
+			    gl=0;
+			    mn++;
+			}
+			isM = true;				
+		    }
+		}
+		
+		if(k+1==revSeq[0].length()) {
+		    if(isM) {
+			if(mn>1) {
+			    mm = ((double)mm*(mn-1)+ml)/mn;
+			} else {
+			    mm = ml;
+			}
+		    } else {
+			if(gn>1) {
+			    mg = ((double)mg*(gn-1)+gl)/gn;
+			} else {
+			    mg = gl;
+			}
+		    }
+		}
+		all++;
+	    }
+	    gapFreq += (double)gs/(2*all);
+	    sumDelta += mm;
+	    sumEpsilon += mg;
+	}
+
+	gapFreq /= pairs.length;
+	sumDelta /= pairs.length;
+	sumEpsilon /= pairs.length;
+
+	if(ProAlign.estimateDelta) {
+	    ProAlign.modelDelta = 0.5d/(sumDelta+1);
+	    ProAlign.log.println(" HMM delta estimate: "+ProAlign.modelDelta);
+//	    System.out.println("modelDelta: "+ProAlign.modelDelta);	    
+	}
+	if(ProAlign.estimateEpsilon) {
+	    ProAlign.modelEpsilon = (1d-1d/(sumEpsilon+1));
+	    ProAlign.log.println(" HMM epsilon estimate: "+ProAlign.modelEpsilon);
+//	    System.out.println("modelEpsilon: "+ProAlign.modelEpsilon);
+	}
+	if(ProAlign.estimateGapFreq) {
+	    ProAlign.gapFreq = gapFreq;
+	    ProAlign.log.println(" gap frequency estimate: "+ProAlign.gapFreq);
+//	    System.out.println("gapFreq: "+ProAlign.gapFreq);
+	}
+	if(ProAlign.estimateGapProb) {
+	    if(ProAlign.isDna) {
+		ProAlign.gapProb = gapFreq/4d;
+	    } else {
+		ProAlign.gapProb = gapFreq/20d;
+	    }
+	    ProAlign.log.println(" gap probability estimate: "+ProAlign.gapProb);
+//	    System.out.println("gapProb: "+ProAlign.gapProb);
+	}
+
+    }	
+
+}
diff --git a/PixelRule.java b/PixelRule.java
new file mode 100644
index 0000000..9769a77
--- /dev/null
+++ b/PixelRule.java
@@ -0,0 +1,102 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  sequence alignment comparison program<p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.awt.Toolkit;
+import java.awt.Graphics;
+import java.awt.Dimension;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Rectangle;
+import javax.swing.JComponent;
+
+// A nice ruler for the quality window.
+//
+public class PixelRule extends JComponent {
+  int rulerHeight = 15;
+
+  private int increment;
+  private int startPosition = 0;
+  private int units;
+
+  public PixelRule() {  }
+
+  public void setIncrement(int xScale) {
+    increment = xScale;
+    units = 25*xScale;
+  }
+  
+  public void setPreferredWidth(int pw) {
+    setPreferredSize(new Dimension(pw,rulerHeight));
+  }
+
+  public void paintComponent(Graphics g) {
+    Rectangle drawHere = g.getClipBounds();
+
+    // Fill clipping area 
+    g.setColor(Color.white);
+    g.fillRect(drawHere.x, drawHere.y, drawHere.width, drawHere.height);
+
+    // Do the ruler labels in a small font 
+    g.setFont(new Font(ProAlign.paFontName, Font.PLAIN, ProAlign.paFontSize-2)); 
+    g.setColor(Color.black);
+    
+    // Some vars we need.
+    int tickLength = 0;
+    String text = null;
+    
+    // Use clipping bounds to calculate first tick and last tick location.
+    int start = (drawHere.x / increment) * increment;
+    int end = (((drawHere.x + drawHere.width) / increment) + 1) * increment;
+    
+    // Make a special case of 0 to display the number
+    // within the rule and draw a units label.
+    if (start == 0) {
+      text = Integer.toString(0);
+      tickLength = 3;
+      g.drawLine(0, rulerHeight-1, 0, rulerHeight-tickLength-1);
+      g.drawString("0", 0, 10);
+      start = increment;
+    }
+
+    // ticks and labels
+    for (int i = start; i < end; i += increment) {
+	if (i % units == 0)  {
+	  tickLength = 3;
+	  text = Integer.toString(i/increment);
+	} else if(i % 10 == 0)  {
+	    tickLength = 1;
+	    text = null;
+	} else {
+	    tickLength = 0;
+	    text = null;
+	}
+      
+	if (tickLength != 0) { 
+	    g.drawLine(i, rulerHeight-1, i, rulerHeight-tickLength-1);
+	    if (text != null)
+		g.drawString(text, i-10, 10);
+	}
+    }
+  }
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PrintCurve.java b/PrintCurve.java
new file mode 100644
index 0000000..47ae598
--- /dev/null
+++ b/PrintCurve.java
@@ -0,0 +1,76 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  sequence alignment comparison program<p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JPanel;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Dimension;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseListener;
+ 
+//  Print a curve on a JPanel.
+// 
+public class PrintCurve extends JPanel {
+
+    double[] postProb;
+    int height = 100;
+    int xScale = 2; // scale: pixels per column.
+    PrintCurve pc;
+    QualityWindow qw;
+
+    PrintCurve(QualityWindow qw, double[] postProb) {
+	pc = this;
+	this.qw = qw;
+	this.postProb = postProb;
+
+	setBackground(Color.white);
+	setForeground(Color.black);
+
+	setPreferredSize(new Dimension(xScale*postProb.length, height));
+	addMouseListener(new MiceListener());
+    }
+
+    // Update data when changing node. 
+    //
+    void upDateData(double[] postProb) {
+	this.postProb = postProb;
+	pc.updateUI();
+    }
+
+    public void paintComponent(Graphics g) {
+	Graphics2D  g2 = (Graphics2D) g;
+	super.paintComponent(g2);      //clears the background
+	height = pc.qw.getVisibleHeight();
+	pc.setPreferredSize(new Dimension(xScale*postProb.length, height));
+	for(int i=0; i<postProb.length-1; i++) {
+	    int y1 = (int) (Math.exp(postProb[i])*height);
+	    int y2 = (int) (Math.exp(postProb[i+1])*height);
+	    if(y1>0 && y2>0) {
+		g2.drawLine(i*xScale,height-y1,(i+1)*xScale,height-y2);
+	    }
+	}
+    }
+
+    // Listens mouse click: focus alignment, update messageText. 
+    //
+    class MiceListener extends MouseAdapter {
+	public void mouseClicked(MouseEvent e) {
+	    int x = e.getX();
+	    int y = e.getY();
+	    qw.rw.focusAlignment(x/xScale);
+	    qw.rw.convertNodeInfoX(x/xScale,qw.name);
+	}
+    }
+}
+
diff --git a/PrintData.java b/PrintData.java
new file mode 100644
index 0000000..84b0ace
--- /dev/null
+++ b/PrintData.java
@@ -0,0 +1,382 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  sequence alignment comparison program<p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.RenderingHints;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseListener;
+
+
+//  Prints alignment characters with nice colors, shows quality as color.
+//
+public class PrintData extends JPanel {
+
+    final Color bg = Color.white;
+    final Color fg = Color.black;
+    final Color purple = new Color(250,90,240);
+    final Color lblue = new Color(90,210,250);
+    final Color brown = new Color(200,160,30);
+    final Color blue = new Color(205,205,255);
+    
+    int verticalCharSpace;
+    int horizontalCharSpace;
+    
+    Font printFont;
+    FontMetrics currentMetrics;
+    
+    int columnWidth, rowHeight; 
+    int totalWidth, totalHeight;
+    int charWidth, charHeight;
+    int maxLength;
+    
+    int startSite = -1;
+
+    boolean isDataGiven = false;
+    boolean isDataAligned = false;
+    boolean hasMouseListener = false;
+
+    boolean[] taxaRemoved = new boolean[0];
+    boolean[] sitesRemoved = new boolean[0];
+    
+    String textArray[];
+    double[][] postProb;
+
+    PrintData pd;
+    ResultWindow parent;
+    JScrollPane sPane = null;
+    
+    MiceListener mListener;
+
+    public PrintData() {
+	this.setParameters();
+    }
+
+    public PrintData(String[] textArray, int maxLength, ResultWindow rw) {
+
+	pd = this;
+	parent = rw;
+	this.maxLength = maxLength;
+	this.textArray = textArray;
+	
+	verticalCharSpace = ResultWindow.verticalCharSpace;
+	horizontalCharSpace = ResultWindow.horizontalCharSpace;
+
+	this.setParameters();
+
+	isDataGiven = true;
+	isDataAligned = false;
+
+	totalWidth = maxLength*columnWidth+horizontalCharSpace;
+	totalHeight = textArray.length*rowHeight+verticalCharSpace;
+	setPreferredSize(new Dimension(totalWidth,totalHeight));
+	
+	if(hasMouseListener) {
+	    removeMouseListener(mListener);
+	    hasMouseListener = false;
+	}
+    }
+
+    public PrintData(String[] textArray, double[][] postProb, ResultWindow rw) {
+
+	pd = this;
+	parent = rw;
+
+	this.textArray = textArray;
+	this.postProb =  postProb;
+
+	verticalCharSpace = ResultWindow.verticalCharSpace;
+	horizontalCharSpace = ResultWindow.horizontalCharSpace;
+
+	this.setParameters();
+
+	isDataGiven = true;
+	isDataAligned = true;
+
+	sitesRemoved = new boolean[textArray[0].length()];
+	maxLength = textArray[0].length();
+
+	totalWidth = maxLength*columnWidth+horizontalCharSpace;
+	totalHeight = textArray.length*rowHeight+verticalCharSpace;
+	setPreferredSize(new Dimension(totalWidth,totalHeight));
+	
+	mListener = new MiceListener();
+	addMouseListener(mListener);
+	hasMouseListener = true;
+	
+    }
+    
+    // Some parameters need to be set even without data.
+    //
+    void setParameters() {
+
+	printFont = new Font("monospaced", Font.BOLD, ResultWindow.rwFontSize);
+	
+	setBackground(bg);
+	setForeground(fg);
+	
+	currentMetrics = this.getFontMetrics(printFont);
+	charWidth = currentMetrics.charWidth(' ');
+	charHeight = currentMetrics.getHeight();
+
+	columnWidth = charWidth + horizontalCharSpace;      
+	rowHeight = charHeight + verticalCharSpace;
+    }
+
+
+    public void paint(Graphics g) {
+
+	if(printFont.getSize()!=ResultWindow.rwFontSize || 
+	   horizontalCharSpace!=ResultWindow.horizontalCharSpace ||
+	   verticalCharSpace!=ResultWindow.verticalCharSpace){
+
+	    printFont = new Font("monospaced", Font.BOLD, ResultWindow.rwFontSize);
+	    horizontalCharSpace=ResultWindow.horizontalCharSpace;
+	    verticalCharSpace=ResultWindow.verticalCharSpace;
+
+	    this.setParameters();
+	}
+	
+	if(isDataGiven) { // if no data, no update.
+	    totalWidth = Math.max(maxLength*columnWidth+
+				  horizontalCharSpace,sPane.getWidth());
+	    totalHeight = Math.max(textArray.length*rowHeight+
+				   verticalCharSpace,sPane.getHeight());
+	    setPreferredSize(new Dimension(totalWidth,totalHeight));
+	    
+	    int startX = sPane.getHorizontalScrollBar().getValue();
+	    int endX = startX + sPane.getWidth();
+	    int startY = sPane.getVerticalScrollBar().getValue();
+	    int endY = startY + sPane.getHeight();
+	    
+	    int startColumn = (int) (startX/columnWidth);
+	    int endColumn = Math.min((int)(endX/columnWidth)+1,maxLength);
+	    int startRow = (int) (startY/rowHeight);
+	    int endRow = Math.min((int)(endY/rowHeight)+1,textArray.length);
+	    
+	    int charX = startColumn * columnWidth;
+	    int charY = rowHeight + startRow * rowHeight;
+	    
+	    Graphics2D  g2 = (Graphics2D) g;
+	    g2.setColor(bg);
+	    g2.setFont(printFont);
+	    g2.fillRect(Math.max(startX-10,0),Math.max(startY-10,0),
+			Math.min(endX+10,totalWidth),Math.min(endY+10,totalHeight));
+	    
+	    for (int i = startRow; i < endRow; i++) {
+		String str = textArray[i];
+		for (int j = startColumn; j < endColumn; j++) {
+
+		    if(j>=str.length()) { break; } // unaligned seqs can be of different length.
+
+		    if(isDataAligned) { // if aligned, draw "selected/not-selected".
+			// Do only once...
+			if (i == startRow) {
+			    if (j < sitesRemoved.length) {
+				if (sitesRemoved[j]) {
+				    g2.setColor(blue);
+				    g2.fillRect(charX,0,(charX+columnWidth),totalHeight);
+				    
+				} else {
+				    g2.setColor(bg);
+				    g2.fillRect(charX,0,(charX+columnWidth),totalHeight);
+				}
+			    }
+			}	  
+			// ..until here.
+		    }
+
+		    g2.setColor(getColor(str.charAt(j)));
+		    g2.drawString(""+str.charAt(j), charX, charY);
+		    charX += columnWidth;
+		}
+
+		if(isDataAligned) { // if aligned, draw color box.
+		    if(i<postProb[0].length) {
+			charX = startColumn * columnWidth ;
+			for (int j = startColumn; j < endColumn; j++) {
+			    g2.setColor(getProbColor(postProb[j][i]));
+			    g2.fillRect(charX+1,charY+1,columnWidth-2,verticalCharSpace-2);
+			    g2.setColor(bg);
+			    charX += columnWidth;
+			}
+		    } 
+		}
+
+		charY += rowHeight;
+		charX = startColumn * columnWidth ;
+	    }
+	}
+    }
+    
+    // A click on "quality box" updates messageText on the scree.
+    //
+    class MiceListener extends MouseAdapter {
+	public void mouseClicked(MouseEvent e) {
+	    int x = e.getX();
+	    int y = e.getY();	    
+	    int col = (int) x/columnWidth;
+	    y = y - rowHeight/2;
+	    if(y<0) { y = 0; }
+	    int row = (int) y/rowHeight;
+	    if(col >= postProb.length) { col=postProb.length-1; }
+	    if(row >= postProb[0].length) { row = postProb[0].length-1; }
+	    
+	    parent.writeNodeInfo(col,row);
+
+	}
+    } 
+
+    // Called from Rule.
+    //
+    void updateStableSites(int x, int y, boolean shiftDown) {
+	if(pd.isDataAligned) {
+
+	    if(shiftDown) {  // shift down -> range selection
+		if(startSite > 0) {
+		    int site = (int) x/columnWidth;
+		    int start, end;
+		    if(startSite>site) {
+			start = site;
+			end = startSite;
+		    } else {
+			start = startSite+1;
+			end = site+1;
+		    }
+		    for(int sx=start; sx<end; sx++) {
+			if (sitesRemoved[sx])
+			    sitesRemoved[sx] = false;
+			else 
+			    sitesRemoved[sx] = true;
+		    }
+		    pd.repaint();
+
+		    startSite = -1;
+		} else {
+		    int site = (int) x/columnWidth;
+		
+		    if (sitesRemoved[site])
+			sitesRemoved[site] = false;
+		    else 
+			sitesRemoved[site] = true;
+		    pd.repaint();
+
+		    startSite = site;
+		}
+
+	    } else {  // not shift down -> single selection
+		int site = (int) x/columnWidth;
+		
+		if (sitesRemoved[site])
+		    sitesRemoved[site] = false;
+		else 
+		    sitesRemoved[site] = true;
+		pd.repaint();
+
+		startSite = -1;
+	    }
+	}
+    }
+
+    public boolean[] getRemovedSites() {
+	return sitesRemoved;
+    }  
+
+    Color getColor(char x) {
+	if(ProAlign.isDna) {
+	    if (x == 'a' || x == 'A')
+		return Color.red;
+	    if (x == 'c' || x == 'C')
+		return Color.blue;
+	    if (x == 'g' || x == 'G')
+		return Color.green;
+	    if (x == 't' || x == 'T' || x == 'u' || x == 'U')
+		return brown;
+	    else
+		return Color.gray;
+	} else {
+	    if (x == 'g' || x == 'G' || x == 'a' || x == 'A' || x == 't' || x == 'T')
+		return purple;
+	    if (x == 'p' || x == 'P' || x == 's' || x == 'S')
+		return purple;
+	    if (x == 'l' || x == 'L' || x == 'i' || x == 'I')
+		return Color.green;
+	    if (x == 'v' || x == 'V' || x == 'm' || x == 'M')
+		return Color.green;
+	    if (x == 'k' || x == 'K' || x == 'r' || x == 'R' || x == 'h' || x == 'H')
+		return lblue;
+	    if (x == 'f' || x == 'F' || x == 'w' || x == 'W' || x == 'y' || x == 'Y')
+		return Color.blue;
+	    if (x == 'e' || x == 'E' || x == 'q' || x == 'Q')
+		return Color.black;
+	    if (x == 'd' || x == 'D' || x == 'n' || x == 'N')
+		return Color.black;
+	    if (x == 'c' || x == 'C')
+		return Color.red;
+	    else
+		return Color.gray;
+	}
+    }
+    
+    Color getProbColor(double x) {
+	x = Math.exp(x);
+
+ 	if(x<=0d) {
+	    return Color.white;
+	} else if(x>0d && x<=0.1d) {
+	    return new Color(255,0,0);
+	} else if(x>0.1d && x<=0.2d) {
+	    return new Color(255,26,0);
+	} else if(x>0.2d && x<=0.3d) {
+	    return new Color(255,51,0);
+	} else if(x>0.3d && x<=0.4d) {
+	    return new Color(255,78,0);
+	} else if(x>0.4d && x<=0.5d) {
+	    return new Color(255,102,0);
+	} else if(x>0.5d && x<=0.6d) {
+	    return new Color(255,128,0);
+	} else if(x>0.6d && x<=0.7d) {
+	    return new Color(255,153,0);
+	} else if(x>0.7d && x<=0.8d) {
+	    return new Color(255,179,0);
+	} else if(x>0.8d && x<=0.9d) {
+	    return new Color(255,204,0);
+	} else if(x>0.9d) {
+	    return new Color(255,230,0);
+	} else {
+	    return Color.gray;
+	}
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PrintMinimumCurve.java b/PrintMinimumCurve.java
new file mode 100644
index 0000000..0664666
--- /dev/null
+++ b/PrintMinimumCurve.java
@@ -0,0 +1,73 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  sequence alignment comparison program<p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JPanel;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Dimension;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseListener;
+ 
+//  Print a curve on a JPanel.
+// 
+public class PrintMinimumCurve extends JPanel {
+
+    double[] minProb;
+    String[] nodes;
+    int height = 100;
+    int xScale = 2; // scale: pixels per column.
+    PrintMinimumCurve pc;
+    MinimumProbWindow qw;
+
+    PrintMinimumCurve(MinimumProbWindow qw, double[] minProb, String[] nodes) {
+	pc = this;
+	this.qw = qw;
+	this.minProb = minProb;
+	this.nodes = nodes;
+
+	setBackground(Color.white);
+	setForeground(Color.black);
+
+	setPreferredSize(new Dimension(xScale*minProb.length, height));
+	addMouseListener(new MiceListener());
+    }
+
+    public void paintComponent(Graphics g) {
+	Graphics2D  g2 = (Graphics2D) g;
+	super.paintComponent(g2);      //clears the background
+	height = pc.qw.getVisibleHeight();
+	pc.setPreferredSize(new Dimension(xScale*minProb.length, height));
+	for(int i=0; i<minProb.length-1; i++) {
+	    int y1 = (int) (Math.exp(minProb[i])*height);
+	    int y2 = (int) (Math.exp(minProb[i+1])*height);
+	    if(y1>0 && y2>0) {
+		g2.drawLine(i*xScale,height-y1,(i+1)*xScale,height-y2);
+	    }
+	}
+    }
+
+    // Listens mouse click: focus alignment, update messageText. 
+    //
+    class MiceListener extends MouseAdapter {
+	public void mouseClicked(MouseEvent e) {
+	    int x = e.getX();
+	    int y = e.getY();
+	    qw.rw.focusAlignment(x/xScale);
+	    qw.rw.messageText.setText(" Minimum posterior probability "+
+				      (""+Math.exp(minProb[x/xScale])+"     ").substring(0,5)+
+				      " at "+nodes[x/xScale]+".");
+	}
+    }
+}
+
diff --git a/PrintNames.java b/PrintNames.java
new file mode 100644
index 0000000..53ee414
--- /dev/null
+++ b/PrintNames.java
@@ -0,0 +1,250 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  sequence alignment comparison program<p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.RenderingHints;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseListener;
+
+
+//  Prints taxa names, holds the info of sequences to remove.  
+//
+public class PrintNames extends JPanel {
+
+    final Color bg = Color.white;
+    final Color fg = Color.black;
+    final Color blue = new Color(205,205,255);
+    
+    int verticalCharSpace;
+    int horizontalCharSpace = 1;
+    
+    Font printFont;
+    FontMetrics currentMetrics;
+    
+    int columnWidth, rowHeight; 
+    int totalWidth, totalHeight;
+    int charWidth, charHeight;
+    
+    int startSite = -1;
+
+    boolean isNamesGiven = false;
+
+    boolean[] taxaRemoved = new boolean[0];
+    
+    String textArray[];
+
+    PrintNames pn;
+    JScrollPane sPane = null;
+    
+    public PrintNames() { 
+	
+	setBackground(bg);
+	setForeground(fg);
+	
+    }
+    
+    public PrintNames(String[] textArray, boolean isAligned) {
+
+	pn = this;
+	this.textArray = textArray;
+
+	verticalCharSpace = ResultWindow.verticalCharSpace;
+
+	isNamesGiven = true;
+
+	taxaRemoved = new boolean[textArray.length];
+
+	this.setParameters();
+
+	totalWidth = textArray[0].length()*columnWidth+horizontalCharSpace;
+	totalHeight = textArray.length*rowHeight+verticalCharSpace;
+	setPreferredSize(new Dimension(totalWidth,totalHeight));
+
+	if(isAligned) {
+	    addMouseListener(new MiceListener());
+	}
+
+    }
+
+    void setParameters() {
+
+	printFont = new Font("monospaced", Font.PLAIN, ResultWindow.rwFontSize);
+	
+	setBackground(bg);
+	setForeground(fg);
+	
+	currentMetrics = pn.getFontMetrics(printFont);
+	charWidth = currentMetrics.charWidth(' ');
+	charHeight = currentMetrics.getHeight();
+
+	columnWidth = charWidth + horizontalCharSpace;      
+	rowHeight = charHeight + verticalCharSpace;
+
+    }
+    
+    public void paint(Graphics g) {
+	
+	if(isNamesGiven) {
+
+	    if(printFont.getSize()!=ResultWindow.rwFontSize || 
+	       verticalCharSpace!=ResultWindow.verticalCharSpace){
+		
+		printFont = new Font("monospaced", Font.BOLD, ResultWindow.rwFontSize);
+		verticalCharSpace=ResultWindow.verticalCharSpace;
+		
+		this.setParameters();
+	    }
+
+	    totalWidth = Math.max(textArray[0].length()*columnWidth+
+				  horizontalCharSpace,sPane.getWidth());
+	    totalHeight = Math.max(textArray.length*rowHeight+
+				   verticalCharSpace,sPane.getHeight());
+	    setPreferredSize(new Dimension(totalWidth,totalHeight));
+	    
+	    int startX = sPane.getHorizontalScrollBar().getValue();
+	    int endX = startX + sPane.getWidth();
+	    int startY = sPane.getVerticalScrollBar().getValue();
+	    int endY = startY + sPane.getHeight();
+	    
+	    int startColumn = (int) (startX/columnWidth);
+	    int endColumn = Math.min((int)(endX/columnWidth)+1,textArray[0].length());
+	    int startRow = (int) (startY/rowHeight);
+	    int endRow = Math.min((int)(endY/rowHeight)+1,textArray.length);
+	    
+	    int charX = startColumn*columnWidth;
+	    int charY = rowHeight+startRow*rowHeight;
+	    
+	    Graphics2D  g2 = (Graphics2D) g;
+	    g2.setColor(bg);
+	    g2.setFont(printFont);
+	    g2.fillRect(Math.max(startX-10,0),Math.max(startY-10,0),
+			Math.min(endX+10,totalWidth),
+			Math.min(endY+10,totalHeight));
+	    
+	    for (int i = startRow; i < endRow; i++) {
+		String str = textArray[i];
+		for (int j = startColumn; j < endColumn; j++) {
+		    // Do only once...
+		    if (j == startColumn) {
+			if (i < taxaRemoved.length) {
+			    if (taxaRemoved[i]) {
+				g2.setColor(blue);
+				g2.fillRect(0,(charY-rowHeight+3),totalWidth,charY+3);
+			    } else {
+				g2.setColor(bg);
+				g2.fillRect(0,(charY-rowHeight+3),totalWidth,charY+3);
+			    }
+			    g2.setColor(fg);
+			}
+		    }
+		    // ..until here.
+		    g2.drawString(""+str.charAt(j), charX, charY);
+		    charX += columnWidth;
+		}
+		charX = startColumn * columnWidth ;
+		charY += rowHeight;
+	    }
+	}
+    }
+
+    // Click on a name excludes/includes the taxon.
+    //
+    class MiceListener extends MouseAdapter {
+	public void mouseClicked(MouseEvent e) {
+	    int x = e.getX();
+	    int y = e.getY();
+	    if(e.isShiftDown()) {
+		pn.updateTaxonList(x,y,true);
+	    } else {
+		pn.updateTaxonList(x,y,false);
+	    }
+	}
+    } 
+
+    // Taxa list kept up-to-date.
+    //
+    void updateTaxonList(int x, int y, boolean shiftDown) {
+
+	if(shiftDown) {
+	    if(startSite > 0) {
+		int site = (int) y/rowHeight; 
+		int start, end;
+		if(startSite>site) {
+		    start = site;
+		    end = startSite;
+		} else {
+		    start = startSite+1;
+		    end = site+1;
+		}
+		end = Math.min(end,taxaRemoved.length);
+		for(int sx=start; sx<end; sx++) {
+		    if (taxaRemoved[sx])
+			taxaRemoved[sx] = false;
+		    else 
+			taxaRemoved[sx] = true;
+		}
+		pn.repaint();
+
+		startSite = -1;
+	    } else {
+		int site = (int) y/rowHeight;
+		
+		    if (taxaRemoved[site])
+			taxaRemoved[site] = false;
+		    else 
+			taxaRemoved[site] = true;
+		    pn.repaint();
+
+		    startSite = site;
+		}
+
+
+	} else {
+	    int taxon = (int) y/rowHeight;
+	    
+	    taxon = Math.min(taxon,taxaRemoved.length-1);
+	    if (taxaRemoved[taxon])
+		taxaRemoved[taxon] = false;
+	    else 
+		taxaRemoved[taxon] = true;
+	    pn.repaint();
+
+	    startSite = -1;
+	}
+    }
+
+    public boolean[] getRemovedTaxa() {
+	return taxaRemoved;
+    }  
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PrintTree.java b/PrintTree.java
new file mode 100644
index 0000000..ca62d5f
--- /dev/null
+++ b/PrintTree.java
@@ -0,0 +1,194 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  sequence alignment comparison program<p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JPanel;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Dimension;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseListener;
+
+//  Print a tree on a JPanel.
+// 
+public class PrintTree extends JPanel {
+
+    int xBase, yBase, xMove, yMax, xMax, rowHeight;
+    int cs = 3; // circleSize
+    static int numOpenWindows = 0;
+    
+    boolean isTreeGiven = false;
+
+    AlignmentNode root;
+    ResultWindow rw;
+    PrintTree pt; 
+    QualityWindow currentQw;
+
+    PrintTree() {    
+
+	setBackground(Color.white);
+	setForeground(Color.black);
+
+    }
+
+    PrintTree(AlignmentNode root, ResultWindow rw, boolean isAligned) {
+
+	this.root = root;
+	this.rw = rw;
+	pt = this;
+
+	isTreeGiven = true;
+	
+	this.setParameters();
+	    
+	setPreferredSize(new Dimension(rw.splitWidth, yMax));
+	
+
+	if(isAligned) {
+	    addMouseListener(new MiceListener());
+	}
+
+    }    
+
+    void setParameters() {
+
+	setBackground(Color.white);
+	setForeground(Color.black);
+
+	rowHeight = rw.seqname.rowHeight;
+	yMax = 4+root.getNumChild()*rowHeight;
+	xMax = rw.splitWidth-10;
+	yBase = 2+root.child[0].getNumChild()*rowHeight;
+	xBase = 5;
+	xMove = xMax/(root.getTreeDepth());	
+
+    }
+
+    public void paintComponent(Graphics g) {
+
+	Graphics2D  g2 = (Graphics2D) g;
+	super.paintComponent(g2);      //clears the background
+
+	if(isTreeGiven) {
+
+	    if(rowHeight!=rw.seqname.rowHeight) {
+		this.setParameters();
+	    }
+
+	    setPreferredSize(new Dimension(rw.sPane1.getWidth(), yMax));
+	    xMax = rw.sPane1.getWidth()-10;
+	    xMove = xMax/(root.getTreeDepth());
+	    
+	    paintBranch(g2,root,yBase,xBase);
+	    g2.drawLine(xBase-2,yBase,xBase,yBase);
+	}
+    }
+    
+    // Called from 'paintComponent'. Calls recursively itself.
+    //
+    public void paintBranch(Graphics2D g2, AlignmentNode node, int y, int x) {
+
+	int nc0 = 0; int nc1 = 0; // number of child nodes
+	if(node.child[0].getNumChild() > 1) {
+	    nc0 = node.child[0].child[1].getNumChild();
+	} 
+	if(node.child[1].getNumChild()> 1) {
+	    nc1 = node.child[1].child[0].getNumChild();
+	}
+
+	g2.fillOval(x-cs,y-cs,cs*2,cs*2);
+	node.setTreeXY(x,y);
+	g2.drawString(""+node.nodeNumber,x+cs*2,y+4);
+
+	if(nc0>0) {
+	    g2.drawLine(x,y,x,y-nc0*rowHeight);
+	    g2.drawLine(x,y-nc0*rowHeight,x+xMove,y-nc0*rowHeight);
+	    paintBranch(g2,node.child[0],y-nc0*rowHeight,x+xMove);	    
+	} else {
+	    g2.drawLine(x,y,x,y-rowHeight/2);
+	    g2.drawLine(x,y-rowHeight/2,xMax,y-rowHeight/2);
+	}
+	if(nc1>0) {
+	    g2.drawLine(x,y,x,y+nc1*rowHeight);
+	    g2.drawLine(x,y+nc1*rowHeight,x+xMove,y+nc1*rowHeight);
+	    paintBranch(g2,node.child[1],y+nc1*rowHeight,x+xMove);
+	} else {
+	    g2.drawLine(x,y,x,y+rowHeight/2);
+	    g2.drawLine(x,y+rowHeight/2,xMax,y+rowHeight/2);
+	}
+    }
+
+    // A click on a node opens an alignment quality window.
+    //
+    public void openQualityWindow(String name, boolean newWindow) {
+
+	// ifShiftDown, open a new window.
+	//
+	if(!newWindow) {
+	    if(numOpenWindows==0) {
+		QualityWindow qw = new QualityWindow(root, name,rw);
+		qw.setSize(600,100);
+		qw.setLocation(200,300);
+		qw.setTitle("Posterior probability of "+name);
+		qw.setVisible(true);
+		currentQw = qw;
+		numOpenWindows++;
+	    } else {
+		currentQw.upDateData(root, name);
+		currentQw.setTitle("Posterior probability of "+name);
+	    }
+	} else {
+	    QualityWindow qw = new QualityWindow(root, name,rw);
+	    qw.setSize(600,100);
+	    qw.setLocation(220,320);
+	    qw.setTitle("Posterior probability of "+name);
+	    qw.setVisible(true);
+	    currentQw = qw;
+	    numOpenWindows++;
+	}
+    }
+
+    
+    class MiceListener extends MouseAdapter {
+	public void mouseClicked(MouseEvent e) {
+	    int x = e.getX();
+	    int y = e.getY();
+ 	    String name = root.getNodeNameAtXY(x,y);
+	    if(!name.equals("")) {
+		AlignmentNode node = root.getNodeNamed(name);
+		if(node.name.equals(name)) {
+		    if(e.isShiftDown()) {
+			pt.openQualityWindow(name,true);
+		    } else {
+			pt.openQualityWindow(name,false);
+		    }
+		}
+		rw.messageText.setText(" viterbi:"+node.viterbiEnd+
+				       "; forward: "+node.forwardEnd);
+	    }
+	}
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ProAlign.java b/ProAlign.java
new file mode 100644
index 0000000..1eab7d1
--- /dev/null
+++ b/ProAlign.java
@@ -0,0 +1,231 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author       Ari Loytynoja
+ * @version      1.0
+ */
+package proalign;
+
+import javax.swing.LookAndFeel;
+import javax.swing.UIManager;
+import java.util.HashMap;
+import java.util.Iterator; 
+import java.io.File;
+import java.util.Calendar;
+import java.util.Date;
+
+/** 
+ * The main class of ProAlign, the probabilistic alignment program.
+ */
+public class ProAlign {
+
+    public static double modelDelta = 0.1d;   // HMM
+    public static double modelEpsilon = 0.75d; // HMM
+
+    public static double distScale = 1d;      // distance scale factor
+
+    public static double gapFreq = 0.05d;     // prot model gap freq
+    public static double gapProb = 0.001d;    // prot model gap subst prob
+
+    public int pwDnaOpen = 1500;       // pw alignment
+    public int pwDnaExt = 700;         // pw alignment
+
+    public int pwProtOpen = 1000;      // pw alignment
+    public int pwProtExt = 100;        // pw alignment
+    public String pwProtMatrix = "pam120";  // pw alignment
+
+    public String treefile = new String();
+    public String seqfile;
+    public String outfile = new String("proalign.out");
+
+    public boolean nogui = false;
+    public boolean doTree = false;
+    boolean isAligned = false;
+
+    public int outformat = 2;
+    int nodeNumber = -1;
+    int defBandWidth;
+    int defOffset;
+
+    HashMap seqs;
+
+    public static boolean isDna = true;
+    static boolean isResultWindow = false;
+    public static boolean trackBest = true; // traceback best
+    static boolean DEBUG = true;     // write log
+    public static boolean removeTrailing = true; 
+    public static boolean penalizeTerminal = true; 
+    public static boolean correctMultiple = true; 
+    public static boolean writeMin = true;
+    public static boolean writeMean = false;
+    public static boolean writeAll = false;
+    public static boolean writeRoot = false;
+    public static boolean estimateGapFreq = false;
+    public static boolean estimateGapProb = false;
+    public static boolean estimateDelta = false;
+    public static boolean estimateEpsilon = false;
+
+    public static boolean exitInError = true;
+    public static boolean  estimateParameters = false;
+ 
+    public  static int bandWidth = 101;      // diagonal alignment band width
+    public static int offset = 15;
+
+    static String fileExt = new String("paa"); // PA-file extension
+    static String version = "0.5 alpha 0";
+
+    public static String protModel = "wag";
+
+    static int paFontSize = 11;
+    static String paFontName = "sansserif";
+
+    static String clustalwPath;
+    static String folderPath;
+    static String tempFolder;
+
+    static int wWidth = 800; // window size
+    static int wHeight = 300;
+
+    static OutFile log; 
+
+    public SubstitutionModel sm;
+    AlignmentNode root;
+    ResultWindow rw;
+    
+    public ProAlign() {
+//	System.out.println("ProAlign");
+    }
+
+    public ProAlign(String[] args){
+
+	defBandWidth = bandWidth;
+	defOffset = offset;
+
+	startNewLog("proalign.log");
+	setUserData();
+
+	if(args.length>0) {
+	    ReadArguments ra = new ReadArguments(ProAlign.this, args);
+	}
+	
+	sm = new SubstitutionModel(ProAlign.this);
+	if(ProAlign.isDna){
+	    sm.jcDnaModel();
+	} else {
+	    if(ProAlign.protModel.equals("dayhoff")) {
+		sm.dayhoffProteinModel();
+	    } else if(ProAlign.protModel.equals("jtt")) {
+		sm.jttProteinModel();
+	    } else {
+		sm.wagProteinModel();
+	    }
+	}
+
+	if(nogui) {
+	    try {
+		RunCommandLine rcl = new RunCommandLine(ProAlign.this);
+	    } catch(Exception e) { }
+	} else {
+
+	    rw = new ResultWindow(ProAlign.this);
+	    rw.setSize(wWidth,wHeight);
+	    isResultWindow = true;
+	    rw.setVisible(true);	    
+
+	}
+    }
+
+    /**
+      * Keeps count on the new nodes that are created. Gives a unique number.
+     */
+    int getNodeNumber() {
+	nodeNumber++;
+	return nodeNumber;
+    }
+
+    
+    /**
+      * Resets the counter.
+     */
+    void setNodeNumber(int i) {
+	nodeNumber = i;	
+    }
+
+
+    /**
+      * Keep log.
+     */
+    public void startNewLog(String file) {
+
+	try {                    
+	    log = new OutFile(file);
+	} catch(Exception e) { }
+	
+	String date = Calendar.getInstance().getTime().toString();
+	log.println("\n------------------------------------");
+	log.println("ProAlign - starting a new event log.");
+	log.println(" "+date);
+	log.println("------------------------------------\n");
+	log.flush();
+    }
+
+
+    /**
+     * Get userdata from file
+     */
+    void setUserData() {
+
+	UserSettings user = new UserSettings(); 
+        String[] userdata = user.readSettings();
+	try {
+            if(new File(userdata[0]).isDirectory())
+                folderPath = new File(userdata[0]).getAbsolutePath();
+        } catch (Exception e) {
+            folderPath = new String(".");
+        }
+        try {
+            if(new File(userdata[1]).isFile())
+                clustalwPath = new File(userdata[1]).getAbsolutePath();
+        } catch (Exception e) {
+            clustalwPath = new String("clustalw");
+        }
+	try {
+            if(new File(userdata[2]).isDirectory())
+                folderPath = new File(userdata[0]).getAbsolutePath();
+        } catch (Exception e) {
+	    if(System.getProperty("os.name").startsWith("Windows")){
+		tempFolder = "C:\\TEMP";
+	    } else {
+		tempFolder = "/tmp";
+	    }
+	}
+    }
+
+    static void log(String txt) {
+	if(DEBUG) {
+	    log.println(txt);
+	    log.flush();
+	}
+    }
+
+    /**
+     * Main class. Takes command line arguments to run with scripts.
+     */
+    public static void main(String[] args) {
+
+	try {
+            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+        } catch (Exception e) { }
+	
+	ProAlign pa = new ProAlign(args);
+    }
+}
+
+
+
+
+
diff --git a/PwAlignment.java b/PwAlignment.java
new file mode 100644
index 0000000..b3e48c5
--- /dev/null
+++ b/PwAlignment.java
@@ -0,0 +1,345 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+class PwAlignment {
+
+    int len1,len2;
+    int[][] matM,matX,matY,pointM,pointX,pointY;
+    String[] rev = new String[2];
+    String seq1, seq2;
+    
+    int[][] subst;
+    int gOpen;
+    int gExt;
+    String alphabet;
+    boolean isDna;
+    
+    PwAlignment(int[][] substTable, int gapOpen, int gapExt, String alpha, boolean isD) {  
+
+	ProAlign.log("PwAlignment");
+
+	subst = substTable;
+	gOpen = gapOpen;
+	gExt = gapExt;
+	alphabet = alpha;
+	isDna = isD;
+
+    }
+
+    String[] revAligned(String s1, String s2) {
+
+	ProAlign.log("PwAlignment");
+		    
+	len1 = s1.length();
+	len2 = s2.length();
+	seq1 = " "+s1;
+	seq2 = " "+s2;
+
+	initializeMatrices();
+
+	pwAlignment();
+
+	return rev;
+    }
+
+    double align(String s1, String s2) {
+
+	len1 = s1.length();
+	len2 = s2.length();
+	seq1 = " "+s1;
+	seq2 = " "+s2;
+
+	initializeMatrices();
+
+	pwAlignment();
+
+
+	// Look for terminal gaps
+	//
+	int first = 0; int last = 0;
+	for(int i=0; i<rev[0].length(); i++) {
+	    if(rev[0].charAt(i)=='-' || rev[1].charAt(i)=='-') {
+		continue;
+	    } else {
+		first = i;
+		break;
+	    }
+	}
+	for(int i=rev[0].length()-1; i>=0; i--) {
+	    if(rev[0].charAt(i)=='-' || rev[1].charAt(i)=='-') {
+		continue;
+	    } else {
+		last = i;
+		break;
+	    }
+	}
+	
+	// Count identities
+	//
+	int all = 0;
+	int same = 0;
+	for(int i=first; i<=last; i++) {
+	    if(rev[0].charAt(i)==rev[1].charAt(i)) {
+		same++;
+	    }
+	    all++;
+	} 
+	
+	// Print the alignment & path
+	//
+	boolean printAlignment = false;
+	if(printAlignment) {
+	    for(int i=rev[0].length()-1; i>=0; i--) {
+		System.out.print(rev[0].charAt(i));
+	    }
+	    System.out.println();
+	    for(int i=rev[1].length()-1; i>=0; i--) {
+		System.out.print(rev[1].charAt(i));
+	    }
+	    System.out.println();
+	    System.out.println("same "+same+", all "+all);
+	}
+
+	// Return the distance
+	//
+	if(isDna) {
+	    double p = 1d-(double)same/(double)all; 
+	    double jcK;
+	    if(p>0.75d) {
+		jcK=5d;
+	    } else if(ProAlign.correctMultiple) {
+		jcK = -0.75d*Math.log(1d-4d/3d*p);
+	    } else {
+		jcK = p;
+	    }
+	    if(jcK>5d) {
+		jcK=5d;
+	    }
+	    return jcK;
+	} else {
+	    double p = 1d-(double)same/(double)all; 
+	    double kD;
+	    if(p>0.85d) {
+		kD=5d;
+	    } else if(ProAlign.correctMultiple) {
+		kD = -1d*Math.log(1-p-0.2d*p*p);
+	    } else {
+		kD = p;
+	    }
+	     if(kD>5d) {
+		kD=5d;
+	    }
+	    return kD;
+	}
+    }
+
+    int[] trailing(String s1, String s2) {
+
+	len1 = s1.length();
+	len2 = s2.length();
+	seq1 = " "+s1;
+	seq2 = " "+s2;
+
+	initializeMatrices();
+
+	pwAlignment();
+
+	// Look for terminal gaps
+	//
+	int[] trail = new int[2];
+	int c1=0,c2=0;
+
+	for(int i=0; i<rev[0].length(); i++) {
+	    if(rev[0].charAt(i)!='-') {
+		c1++;
+	    }
+	    if(rev[1].charAt(i)!='-') {
+		c2++;
+	    }
+
+	    if(rev[0].charAt(i)=='-' || rev[1].charAt(i)=='-') {
+		continue;
+	    } else {
+		trail[1] = c1-c2;
+		break;
+	    }
+	}
+	c1=0;
+	c2=0;
+	for(int i=rev[0].length()-1; i>=0; i--) {
+	    if(rev[0].charAt(i)!='-') {
+		c1++;
+	    }
+	    if(rev[1].charAt(i)!='-') {
+		c2++;
+	    }
+	    if(rev[0].charAt(i)=='-' || rev[1].charAt(i)=='-') {
+		continue;
+	    } else {
+		trail[0] = c1-c2;
+		break;
+	    }
+	}
+
+	return trail;
+    }
+
+    void pwAlignment() {
+
+	// Fill the alignment tables
+	//
+	for(int i=0; i<=len1; i++) {
+	    for(int j=0; j<=len2; j++) {
+
+		if(i==0 && j==0 ) {
+		    continue;
+		}
+
+		if(i>0 && j>0) {
+		    int match = subst[alphabet.indexOf(seq1.charAt(i))][alphabet.indexOf(seq2.charAt(j))];
+		    if(matM[i-1][j-1] >= matX[i-1][j-1] && matM[i-1][j-1] >= matY[i-1][j-1]) {
+			matM[i][j] = matM[i-1][j-1]+match;
+			pointM[i][j] = 0;
+		    } else if(matX[i-1][j-1] >= matY[i-1][j-1]) {
+			matM[i][j] = matX[i-1][j-1]+match;
+			pointM[i][j] = 1;
+		    } else {
+			matM[i][j] = matY[i-1][j-1]+match;
+			pointM[i][j] = 2;
+		    }
+		}
+
+		if(j==0 && i>0 && !ProAlign.penalizeTerminal) {
+		    matX[i][j] = matX[i-1][j];
+		    pointX[i][j] = 1;
+		} else if(j==len2 && i>0 && !ProAlign.penalizeTerminal) {
+		    if(matM[i-1][j] >= matX[i-1][j]) {
+			matX[i][j] = matM[i-1][j];
+			pointX[i][j] = 0;
+		    } else { 
+			matX[i][j] = matX[i-1][j];
+			pointX[i][j] = 1;
+		    }
+		} else if(i>0) {
+		    if(matM[i-1][j]+gOpen >= matX[i-1][j]+gExt) {
+			matX[i][j] = matM[i-1][j]+gOpen;
+			pointX[i][j] = 0;
+		    } else {
+			matX[i][j] = matX[i-1][j]+gExt;
+			pointX[i][j] = 1;
+		    }
+		}
+
+		if(i==0 && j>0 && !ProAlign.penalizeTerminal) {
+		    matY[i][j] = matY[i][j-1];
+		    pointY[i][j] = 2;
+		} else if(i==len1 && j>0 && !ProAlign.penalizeTerminal) {
+		    if(matM[i][j-1] >= matY[i][j-1]) {
+			matY[i][j] = matM[i][j-1];
+			pointY[i][j] = 0;
+		    } else {
+			matY[i][j] = matY[i][j-1];
+			pointY[i][j] = 2;
+		    }
+		} else if(j>0) {
+		    if(matM[i][j-1]+gOpen >= matY[i][j-1]+gExt) {
+			matY[i][j] = matM[i][j-1]+gOpen;
+			pointY[i][j] = 0;
+		    } else {
+			matY[i][j] = matY[i][j-1]+gExt;
+			pointY[i][j] = 2;
+		    }
+		}
+	    }
+	}
+
+
+	// Look for best end path & score
+	//
+	int end = 0; int point = 0;
+	    
+	if(matM[len1][len2] >= matX[len1][len2] && matM[len1][len2] >= matY[len1][len2]) {
+	    end = matM[len1][len2];
+	    point = 0;
+	} else if(matX[len1][len2] >= matY[len1][len2]) {
+	    end = matX[len1][len2];
+	    point = 1;
+	} else {
+	    end = matY[len1][len2];
+	    point = 2;
+	}
+
+	// Trace back the path
+	//
+	rev[0] = "";
+	rev[1] = "";
+
+	int i=len1; int j=len2;
+
+	while(i>0 || j>0) {
+
+	    if(point==0) {
+		rev[0]+=seq1.charAt(i);
+		rev[1]+=seq2.charAt(j);
+		point = pointM[i][j];
+		i--;j--;
+		continue;
+
+	    } else if(point==1) {
+		rev[0]+=seq1.charAt(i);
+		rev[1]+="-";
+		point = pointX[i][j];
+		i--;
+		continue;
+
+	    } else if(point==2) {
+		rev[0]+="-";
+		rev[1]+=seq2.charAt(j);
+		point = pointY[i][j];
+		j--;
+		continue;
+
+	    } else {
+		System.out.println("wrong pointer!");
+		break;
+	    }
+	}
+    }
+
+
+    // Initialize matrices for score and pointers
+    //
+    void initializeMatrices() {
+	
+	matM = new int[len1+1][len2+1];
+	matX = new int[len1+1][len2+1];
+	matY = new int[len1+1][len2+1];
+	pointM = new int[len1+1][len2+1];
+	pointX = new int[len1+1][len2+1];
+	pointY = new int[len1+1][len2+1];
+	
+	int small = -100000;
+
+	for(int j=1; j<matM.length; j++) {
+	    matM[j][0] = small;
+	    matX[j][0] = small;
+	    matY[j][0] = small;
+	}
+	for(int i=1; i<matM[0].length; i++) {
+	    matM[0][i] = small;
+	    matX[0][i] = small;
+	    matY[0][i] = small;
+	}
+    }
+}
+
+
diff --git a/PwAlignmentLoop.java b/PwAlignmentLoop.java
new file mode 100644
index 0000000..36a89aa
--- /dev/null
+++ b/PwAlignmentLoop.java
@@ -0,0 +1,273 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.util.HashMap;
+import java.util.Iterator; 
+
+class PwAlignmentLoop extends Thread {
+
+    
+    PwAlignment pa;
+    ResultWindow rw;
+    PwAlignmentLoop pal;
+
+    double[][] distance;
+    String[] names;
+    String tree;
+
+    PwAlignmentLoop(PwAlignment pa,HashMap seqs) {
+	this.pa = pa;
+
+	ProAlign.log("PwAlignmentLoop");
+
+	align(seqs);
+
+	if(ProAlign.DEBUG) {
+	    for(int i=0; i<names.length; i++) {
+		ProAlign.log.print(names[i]+"      ");
+		for(int j=0; j<names.length; j++) {
+		    ProAlign.log.print((""+distance[i][j]+"    ").substring(0,5)+"  ");
+		}
+		ProAlign.log("");
+	    }
+	}
+
+	NeighborJoining nj = new NeighborJoining(distance,names);
+
+	tree = nj.getTree();
+	ProAlign.log("PwAlignmentLoop unrooted tree:\n"+tree);
+
+	TreeNode tn = new TreeNode(tree);
+	tree = tn.findMiddlePoint();
+	
+    }
+
+    PwAlignmentLoop(ResultWindow rw,PwAlignment pa) {
+	this.rw = rw;
+	this.pa = pa;
+	pal = this;
+
+	start();
+    }
+
+    public void run(){
+
+	ProAlign.log("PwAlignmentLoop");
+
+	if(rw.isAligned) {
+	    pal.computeDistances();
+	} else {
+	    pal.align(rw.seqs);
+	}
+
+	if(ProAlign.DEBUG) {
+	    for(int i=0; i<names.length; i++) {
+		ProAlign.log.print(names[i]+"      ");
+		for(int j=0; j<names.length; j++) {
+		    ProAlign.log.print((""+distance[i][j]+"    ").substring(0,5)+"  ");
+		}
+		ProAlign.log("");
+	    }
+	}
+
+	NeighborJoining nj = new NeighborJoining(distance,names);
+
+	tree = nj.getTree();
+	ProAlign.log("PwAlignmentLoop unrooted tree:\n"+tree);
+
+	TreeNode tn = new TreeNode(tree);
+	tree = tn.findMiddlePoint();
+
+	if(rw.isAligned) {
+	    rw.removeGaps(tree);
+	} else {
+	    rw.setRawDataAndTree(tree);
+	}
+	rw.setScrollPanes();
+
+    }
+    
+    void computeDistances() {
+
+	names = rw.root.getTerminalNames();
+	String[] seqData = new String[rw.root.getNumChild()];
+
+	for(int i=0; i<seqData.length; i++) { seqData[i] = "";}
+	for(int i=0; i<rw.root.cellPath.length; i++) {
+	    
+	    int h = 0;
+	    char[] c0 = rw.root.child[0].getCharacterAt(rw.root.cellPath[i][0]-2);
+	    char[] c1 = rw.root.child[1].getCharacterAt(rw.root.cellPath[i][1]-2);
+	    
+	    for(int j=0; j<c0.length; j++) {
+		seqData[h] += c0[j];
+		h++;
+	    }
+	    for(int j=0; j<c1.length; j++) {
+		seqData[h] += c1[j];
+		h++;
+	    } 
+	}
+
+	int ns = names.length;
+	distance = new double[ns][ns];
+
+	for(int i=0; i<ns; i++) {
+	    for(int j=i+1; j<ns; j++) { 
+
+
+		// Remove terminal gaps
+		//
+		int k=0;
+		int first = 0; int last = 0;
+		for(k=0; k<seqData[i].length(); k++) {
+		    if(seqData[i].charAt(k)=='-' || seqData[j].charAt(k)=='-') {
+			continue;
+		    } else {
+			first = k;
+			break;
+		    }
+		}
+		for(k=seqData[i].length()-1; k>=0; k--) {
+		    if(seqData[i].charAt(k)=='-' || seqData[j].charAt(k)=='-') {
+			continue;
+		    } else {
+			last = k;
+			break;
+		    }
+		}
+	
+
+		int all = 0;
+		int same = 0;
+		for(k=first; k<=last; k++) {
+		    if(seqData[i].charAt(k)==seqData[j].charAt(k)) {
+			same++;
+		    }
+		    all++;
+		}
+ 
+		if(ProAlign.isDna) {
+		    double p = 1d-(double)same/(double)all; 
+		    double jcK;
+		    if(ProAlign.correctMultiple) {
+			jcK= -0.75d*Math.log(1d-4d/3d*p);
+		    } else {
+			jcK = p;
+		    }
+		    if(jcK>5d) {
+			jcK=5d;
+		    }
+		    
+		    distance[i][j] = distance[j][i] = jcK;
+		    
+		} else {
+		    double p = 1d-(double)same/(double)all; 
+		    double kD;
+		    if(ProAlign.correctMultiple) {
+			kD = -1d*Math.log(1-p-0.2d*p*p);
+		    } else {
+			kD = p;
+		    }
+		    if(kD>5d) {
+			kD=5d;
+		    }
+		    distance[i][j] = distance[j][i] = kD;
+		    
+		}
+	    }
+	}
+    }
+
+    void align(HashMap seqs) {
+
+
+	Iterator seqKeys = seqs.keySet().iterator();
+	int ns = seqs.size();
+
+	names = new String[ns];
+	int h=0;
+	while(seqKeys.hasNext()) {
+	    names[h++] = (String)seqKeys.next();
+	}
+
+	distance = new double[ns][ns];
+
+	for(int i=0; i<ns; i++) {
+	    for(int j=i+1; j<ns; j++) {   
+
+		if(ProAlign.isResultWindow) {
+		    ResultWindow.updateInfo(" Computing distance matrix:"+
+					    " aligning '"+names[i]+"' and '"+names[j]+"'.");
+		} 
+		String seq1 = (String)seqs.get(names[i]);
+		String seq2 = (String)seqs.get(names[j]);
+		distance[i][j] = pa.align(seq1,seq2);
+		distance[j][i] = distance[i][j];
+		distance[i][i] = 0d;
+
+	    }
+	}
+    }
+    double[][] getDistance() {
+	return distance;
+    }
+    String[] getNames() {
+	return names;
+    }
+
+    String getTree() {
+	return tree;
+    }
+
+/*
+    public static void main(String[] args) {
+
+	SequenceReader2 sr = new SequenceReader2();
+	boolean works = sr.fromFile(args[0]);
+	if(!works) {
+	    System.exit(0);
+	}
+	HashMap seqs = sr.getSequences(); 
+
+	String alphabet;
+	int[][] subst;
+	int gOpen;
+	int gExt;
+	boolean isDna;
+
+	PwSubstitutionMatrix psm = new PwSubstitutionMatrix();
+	CheckSequence2 cs = new CheckSequence2();
+	if(cs.isDna(seqs)){
+	    alphabet = psm.dnaAlphabet;
+	    subst = psm.swdna;
+	    gOpen = -15;
+	    gExt = -7;
+	    isDna = true;
+	} else {
+	    alphabet = psm.protAlphabet;
+	    subst = psm.pam60;
+	    gOpen = -10;
+	    gExt = -1;
+	    isDna = false;
+	}
+
+	PwAlignment pa = new PwAlignment(subst,gOpen,gExt,alphabet,isDna);
+
+	PwAlignmentLoop pal = new PwAlignmentLoop(pa);
+	pal.align(seqs);
+
+	NeighborJoining nj = new NeighborJoining(pal.getDistance(),pal.getNames());
+	System.out.println(nj.getTree());
+    }
+*/
+}
diff --git a/PwSubstitutionMatrix.java b/PwSubstitutionMatrix.java
new file mode 100644
index 0000000..073951e
--- /dev/null
+++ b/PwSubstitutionMatrix.java
@@ -0,0 +1,143 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+class PwSubstitutionMatrix {
+    
+    PwSubstitutionMatrix() { }
+
+    String protAlphabet = "ABCDEFGHIKLMNPQRSTVWXYZ";
+    String dnaAlphabet = "ABCDGHKMNRSTUVWXY";
+
+    int[][] pam60 = {
+	{500,-200,-500,-200,-100,-600,0,-500,-300,-500,-400,-300,-200,0,-300,-500,100,100,-100,-1000,-200,-600,-200},
+	{-200,500,-900,500,200,-800,-200,0,-400,-100,-700,-600,500,-400,-100,-500,0,-200,-500,-800,-300,-500,100},
+	{-500,-900,900,-1000,-1000,-900,-700,-600,-400,-1000,-1100,-1000,-700,-600,-1000,-600,-100,-500,-400,-1200,-600,-200,-1000},
+	{-200,500,-1000,700,300,-1100,-200,-200,-500,-200,-900,-700,200,-500,-100,-600,-200,-300,-600,-1100,-300,-800,200},
+	{-100,200,-1000,300,700,-1000,-200,-300,-400,-300,-700,-500,0,-300,200,-600,-200,-400,-400,-1200,-300,-700,500},
+	{-600,-800,-900,-1100,-1000,800,-700,-400,-100,-1000,-100,-200,-600,-700,-900,-700,-500,-600,-500,-300,-500,300,-1000},
+	{0,-200,-700,-200,-200,-700,600,-600,-700,-500,-800,-600,-100,-400,-500,-700,0,-300,-400,-1100,-300,-1000,-300},
+	{-500,0,-600,-200,-300,-400,-600,800,-600,-400,-400,-700,100,-200,200,0,-400,-500,-500,-500,-300,-200,0},
+	{-300,-400,-400,-500,-400,-100,-700,-600,700,-400,0,100,-400,-600,-500,-400,-400,-100,300,-1000,-300,-400,-400},
+	{-500,-100,-1000,-200,-300,-1000,-500,-400,-400,600,-600,0,0,-400,-100,200,-200,-200,-600,-800,-300,-700,-200},
+	{-400,-700,-1100,-900,-700,-100,-800,-400,0,-600,600,200,-500,-500,-300,-600,-600,-500,-100,-400,-400,-500,-500},
+	{-300,-600,-1000,-700,-500,-200,-600,-700,100,0,200,1000,-600,-600,-200,-200,-400,-200,0,-900,-300,-700,-400},
+	{-200,500,-700,200,0,-600,-100,100,-400,0,-500,-600,600,-400,-200,-300,100,-100,-500,-600,-200,-300,-100},
+	{0,-400,-600,-500,-300,-700,-400,-200,-600,-400,-500,-600,-400,700,-100,-200,0,-200,-400,-1000,-300,-1000,-200},
+	{-300,-100,-1000,-100,200,-900,-500,200,-500,-100,-300,-200,-200,-100,700,0,-300,-400,-500,-900,-300,-800,600},
+	{-500,-500,-600,-600,-600,-700,-700,0,-400,200,-600,-200,-300,-200,0,800,-200,-400,-500,0,-400,-800,-200},
+	{100,0,-100,-200,-200,-500,0,-400,-400,-200,-600,-400,100,0,-300,-200,500,100,-400,-400,-200,-500,-300},
+	{100,-200,-500,-300,-400,-600,-300,-500,-100,-200,-500,-200,-100,-200,-400,-400,100,600,-100,-900,-200,-500,-400},
+	{-100,-500,-400,-600,-400,-500,-400,-500,300,-600,-100,0,-500,-400,-500,-500,-400,-100,600,-1100,-300,-500,-500},
+	{-1000,-800,-1200,-1100,-1200,-300,-1100,-500,-1000,-800,-400,-900,-600,-1000,-900,0,-400,-900,-1100,1300,-800,-300,-1100},
+	{-200,-300,-600,-300,-300,-500,-300,-300,-300,-300,-400,-300,-200,-300,-300,-400,-200,-200,-300,-800,-300,-500,-300},
+	{-600,-500,-200,-800,-700,300,-1000,-200,-400,-700,-500,-700,-300,-1000,-800,-800,-500,-500,-500,-300,-500,900,-700},
+	{-200,100,-1000,200,500,-1000,-300,0,-400,-200,-500,-400,-100,-200,600,-200,-300,-400,-500,-1100,-300,-700,500}
+	};
+
+    int[][] pam120 = {
+	{300,0,-300,0,0,-400,100,-300,-100,-200,-300,-200,-100,100,-100,-300,100,100,0,-700,-100,-400,-100},
+	{0,400,-600,400,300,-500,0,100,-300,0,-400,-400,300,-200,0,-200,0,0,-300,-600,-100,-300,200},
+	{-300,-600,900,-700,-700,-600,-400,-400,-300,-700,-700,-600,-500,-400,-700,-400,0,-300,-300,-800,-400,-100,-700},
+	{0,400,-700,500,300,-700,0,0,-300,-100,-500,-400,200,-300,100,-300,0,-100,-300,-800,-200,-500,300},
+	{0,300,-700,300,500,-700,-100,-100,-300,-100,-400,-300,100,-200,200,-300,-100,-200,-300,-800,-100,-500,400},
+	{-400,-500,-600,-700,-700,800,-500,-300,0,-700,0,-100,-400,-500,-600,-500,-300,-400,-300,-100,-300,400,-600},
+	{100,0,-400,0,-100,-500,500,-400,-400,-300,-500,-400,0,-200,-300,-400,100,-100,-200,-800,-200,-600,-200},
+	{-300,100,-400,0,-100,-300,-400,700,-400,-200,-300,-400,200,-100,300,100,-200,-300,-300,-300,-200,-100,100},
+	{-100,-300,-300,-300,-300,0,-400,-400,600,-300,100,100,-200,-300,-300,-200,-200,0,300,-600,-100,-200,-300},
+	    {-200,0,-700,-100,-100,-700,-300,-200,-300,500,-400,0,100,-200,0,200,-100,-100,-400,-500,-200,-500,-100},
+	{-300,-400,-700,-500,-400,0,-500,-300,100,-400,500,300,-400,-300,-200,-400,-400,-300,100,-300,-200,-200,-300},
+	{-200,-400,-600,-400,-300,-100,-400,-400,100,0,300,800,-300,-300,-100,-100,-200,-100,100,-600,-200,-400,-200},
+	{-100,300,-500,200,100,-400,0,200,-200,100,-400,-300,400,-200,0,-100,100,0,-300,-400,-100,-200,0},
+	{100,-200,-400,-300,-200,-500,-200,-100,-300,-200,-300,-300,-200,600,0,-100,100,-100,-200,-700,-200,-600,-100},
+	{-100,0,-700,100,200,-600,-300,300,-300,0,-200,-100,0,0,600,100,-200,-200,-300,-600,-100,-500,400},
+	{-300,-200,-400,-300,-300,-500,-400,100,-200,200,-400,-100,-100,-100,100,600,-100,-200,-300,100,-200,-500,-100},
+	{100,0,0,0,-100,-300,100,-200,-200,-100,-400,-200,100,100,-200,-100,300,200,-200,-200,-100,-300,-100},
+	{100,0,-300,-100,-200,-400,-100,-300,0,-100,-300,-100,0,-100,-200,-200,200,400,0,-600,-100,-300,-200},
+	{0,-300,-300,-300,-300,-300,-200,-300,300,-400,100,100,-300,-200,-300,-300,-200,0,500,-800,-100,-300,-300},
+	{-700,-600,-800,-800,-800,-100,-800,-300,-600,-500,-300,-600,-400,-700,-600,100,-200,-600,-800,1200,-500,-200,-700},
+	{-100,-100,-400,-200,-100,-300,-200,-200,-100,-200,-200,-200,-100,-200,-100,-200,-100,-100,-100,-500,-200,-300,-100},
+	{-400,-300,-100,-500,-500,400,-600,-100,-200,-500,-200,-400,-200,-600,-500,-500,-300,-300,-300,-200,-300,800,-500},
+	{-100,200,-700,300,400,-600,-200,100,-300,-100,-300,-200,0,-100,400,-100,-100,-200,-300,-700,-100,-500,400}
+    };
+
+    int[][] pam160 = {
+	{200,0,-200,0,0,-300,100,-200,-100,-200,-200,-100,0,100,-100,-200,100,100,0,-500,0,-300,0},
+	{0,300,-400,300,200,-400,0,100,-200,0,-400,-300,200,-100,100,-100,0,0,-200,-500,-100,-300,200},
+	{-200,-400,900,-500,-500,-500,-300,-300,-200,-500,-600,-500,-400,-300,-500,-300,0,-200,-200,-700,-300,0,-500},
+	{0,300,-500,400,300,-600,0,0,-300,0,-400,-300,200,-200,100,-200,0,-100,-300,-600,-100,-400,200},
+	{0,200,-500,300,400,-500,0,0,-200,-100,-300,-200,100,-100,200,-200,0,-100,-200,-700,-100,-400,300},
+	{-300,-400,-500,-600,-500,700,-400,-200,0,-500,100,0,-300,-400,-500,-400,-300,-300,-200,-100,-300,500,-500},
+	{100,0,-300,0,0,-400,400,-300,-300,-200,-400,-300,0,-100,-200,-300,100,-100,-200,-700,-100,-500,-100},
+	{-200,100,-300,0,0,-200,-300,600,-300,-100,-200,-300,200,-100,200,100,-100,-200,-200,-300,-100,0,100},
+	{-100,-200,-200,-300,-200,0,-300,-300,500,-200,200,200,-200,-200,-200,-200,-200,0,300,-500,-100,-200,-200},
+	{-200,0,-500,0,-100,-500,-200,-100,-200,400,-300,0,100,-200,0,300,-100,0,-300,-400,-100,-400,0},
+	{-200,-400,-600,-400,-300,100,-400,-200,200,-300,500,300,-300,-300,-200,-300,-300,-200,100,-200,-200,-200,-300},
+	{-100,-300,-500,-300,-200,0,-300,-300,200,0,300,700,-200,-200,-100,-100,-200,-100,100,-400,-100,-300,-200},
+	{0,200,-400,200,100,-300,0,200,-200,100,-300,-200,300,-100,0,-100,100,0,-200,-400,0,-200,100},
+	{100,-100,-300,-200,-100,-400,-100,-100,-200,-200,-300,-200,-100,500,0,-100,100,0,-200,-500,-100,-500,-100},
+	{-100,100,-500,100,200,-500,-200,200,-200,0,-200,-100,0,0,500,100,-100,-100,-200,-500,-100,-400,300},
+	{-200,-100,-300,-200,-200,-400,-300,100,-200,300,-300,-100,-100,-100,100,600,-100,-100,-300,100,-100,-400,0},
+	{100,0,0,0,0,-300,100,-100,-200,-100,-300,-200,100,100,-100,-100,200,100,-100,-200,0,-300,-100},
+	{100,0,-200,-100,-100,-300,-100,-200,0,0,-200,-100,0,0,-100,-100,100,300,0,-500,0,-300,-100},
+	{0,-200,-200,-300,-200,-200,-200,-200,300,-300,100,100,-200,-200,-200,-300,-100,0,400,-600,-100,-300,-200},
+	{-500,-500,-700,-600,-700,-100,-700,-300,-500,-400,-200,-400,-400,-500,-500,100,-200,-500,-600,1200,-400,-100,-600},
+	{0,-100,-300,-100,-100,-300,-100,-100,-100,-100,-200,-100,0,-100,-100,-100,0,0,-100,-400,-100,-300,-100},
+	{-300,-300,0,-400,-400,500,-500,0,-200,-400,-200,-300,-200,-500,-400,-400,-300,-300,-300,-100,-300,800,-400},
+	{0,200,-500,200,300,-500,-100,100,-200,0,-300,-200,100,-100,300,0,-100,-100,-200,-600,-100,-400,300}
+    };
+    
+    int[][] pam250 = {
+	{200,0,-200,0,0,-300,100,-100,-100,-100,-200,-100,0,100,0,-200,100,100,0,-600,0,-300,0},
+	{0,300,-400,300,300,-400,0,100,-200,100,-300,-200,200,-100,100,-100,0,0,-200,-500,-100,-300,200},
+	{-200,-400,1200,-500,-500,-400,-300,-300,-200,-500,-600,-500,-400,-300,-500,-400,0,-200,-200,-800,-300,0,-500},
+	{0,300,-500,400,300,-600,100,100,-200,0,-400,-300,200,-100,200,-100,0,0,-200,-700,-100,-400,300},
+	{0,300,-500,300,400,-500,0,100,-200,0,-300,-200,100,-100,200,-100,0,0,-200,-700,-100,-400,300},
+	{-300,-400,-400,-600,-500,900,-500,-200,100,-500,200,0,-300,-500,-500,-400,-300,-300,-100,0,-200,700,-500},
+	{100,0,-300,100,0,-500,500,-200,-300,-200,-400,-300,0,0,-100,-300,100,0,-100,-700,-100,-500,0},
+	{-100,100,-300,100,100,-200,-200,600,-200,0,-200,-200,200,0,300,200,-100,-100,-200,-300,-100,0,200},
+	{-100,-200,-200,-200,-200,100,-300,-200,500,-200,200,200,-200,-200,-200,-200,-100,0,400,-500,-100,-100,-200},
+	{-100,100,-500,0,0,-500,-200,0,-200,500,-300,0,100,-100,100,300,0,0,-200,-300,-100,-400,0},
+	{-200,-300,-600,-400,-300,200,-400,-200,200,-300,600,400,-300,-300,-200,-300,-300,-200,200,-200,-100,-100,-300},
+	{-100,-200,-500,-300,-200,0,-300,-200,200,0,400,600,-200,-200,-100,0,-200,-100,200,-400,-100,-200,-200},
+	{0,200,-400,200,100,-300,0,200,-200,100,-300,-200,200,0,100,0,100,0,-200,-400,0,-200,100},
+	{100,-100,-300,-100,-100,-500,0,0,-200,-100,-300,-200,0,600,0,0,100,0,-100,-600,-100,-500,0},
+	{0,100,-500,200,200,-500,-100,300,-200,100,-200,-100,100,0,400,100,-100,-100,-200,-500,-100,-400,300},
+	{-200,-100,-400,-100,-100,-400,-300,200,-200,300,-300,0,0,0,100,600,0,-100,-200,200,-100,-400,0},
+	{100,0,0,0,0,-300,100,-100,-100,0,-300,-200,100,100,-100,0,200,100,-100,-200,0,-300,0},
+	{100,0,-200,0,0,-300,0,-100,0,0,-200,-100,0,0,-100,-100,100,300,0,-500,0,-300,-100},
+	{0,-200,-200,-200,-200,-100,-100,-200,400,-200,200,200,-200,-100,-200,-200,-100,0,400,-600,-100,-200,-200},
+	{-600,-500,-800,-700,-700,0,-700,-300,-500,-300,-200,-400,-400,-600,-500,200,-200,-500,-600,1700,-400,0,-600},
+	{0,-100,-300,-100,-100,-200,-100,-100,-100,-100,-100,-100,0,-100,-100,-100,0,0,-100,-400,-100,-200,-100},
+	{-300,-300,0,-400,-400,700,-500,0,-100,-400,-100,-200,-200,-500,-400,-400,-300,-300,-200,0,-200,1000,-400},
+	{0,200,-500,300,300,-500,0,200,-200,0,-300,-200,100,0,300,0,0,-100,-200,-600,-100,-400,300}
+    };
+    
+    int[][] swdna = {
+	{1000,-900,-900,1000,-900,1000,-900,1000,1000,1000,-900,-900,-900,1000,1000,1000,-900},
+	{-900,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000},
+	{-900,1000,1000,-900,-900,1000,-900,1000,1000,-900,1000,-900,-900,1000,-900,1000,1000},
+	{1000,1000,-900,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000},
+	{-900,1000,-900,1000,1000,-900,1000,-900,1000,1000,1000,-900,-900,1000,-900,1000,-900},
+	{1000,1000,1000,1000,-900,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000},
+	{-900,1000,-900,1000,1000,1000,1000,-900,1000,1000,1000,1000,1000,1000,1000,1000,1000},
+	{1000,1000,1000,1000,-900,1000,-900,1000,1000,1000,1000,-900,-900,1000,1000,1000,1000},
+	{1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000},
+	{1000,1000,-900,1000,1000,1000,1000,1000,1000,1000,1000,-900,-900,1000,1000,1000,-900},
+	{-900,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,-900,-900,1000,-900,1000,1000},
+	{-900,1000,-900,1000,-900,1000,1000,-900,1000,-900,-900,1000,1000,-900,1000,1000,1000},
+	{-900,1000,-900,1000,-900,1000,1000,-900,1000,-900,-900,1000,1000,-900,1000,1000,1000},
+	{1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,-900,-900,1000,1000,1000,1000},
+	{1000,1000,-900,1000,-900,1000,1000,1000,1000,1000,-900,1000,1000,1000,1000,1000,1000},
+	{1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000},
+	{-900,1000,1000,1000,-900,1000,1000,1000,1000,-900,1000,1000,1000,1000,1000,1000,1000}
+    };      
+}
diff --git a/QualityWindow.java b/QualityWindow.java
new file mode 100644
index 0000000..445b540
--- /dev/null
+++ b/QualityWindow.java
@@ -0,0 +1,96 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JViewport;
+import javax.swing.JScrollPane;
+import javax.swing.JScrollBar;
+import javax.swing.BorderFactory;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Container;
+import java.awt.Point;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowAdapter;
+
+public class QualityWindow extends JFrame {
+
+    JScrollPane sPane;
+    int height;
+    double[] postProb;
+
+    PixelRule pixRule;
+    PrintCurve curve;
+    ResultWindow rw;
+    String name;
+
+    QualityWindow(AlignmentNode root, String name, ResultWindow rw) {
+
+	this.rw = rw;
+	this.name = name;
+
+	postProb = new double[root.cellPath.length];
+	for(int i=0; i<root.cellPath.length; i++) {
+	    postProb[i] = root.getOnePostProbAt(i, name);
+	}
+	height = this.getHeight();
+
+	curve = new PrintCurve(QualityWindow.this, postProb);
+
+	// pixelruler.
+	pixRule = new PixelRule();
+	pixRule.setPreferredWidth((int) curve.getPreferredSize().getWidth());
+	pixRule.setIncrement(curve.xScale);
+	pixRule.setBackground(Color.white);
+
+
+	sPane = new JScrollPane(curve,
+				 JScrollPane.VERTICAL_SCROLLBAR_NEVER,
+				 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+
+
+	sPane.setViewportBorder(BorderFactory.createLineBorder(Color.black));
+	sPane.setColumnHeaderView(pixRule);
+	sPane.setBackground(Color.white);
+
+	Container cp = getContentPane();
+	cp.add(sPane);
+
+	addWindowListener(new WindowAdapter() {
+		public void windowClosing(WindowEvent e){
+		    PrintTree.numOpenWindows--;
+		    dispose();
+		}
+	    });
+    }
+
+    int getVisibleHeight() {
+	return sPane.getViewport().getHeight();
+    }
+
+    void upDateData(AlignmentNode root, String name){
+	postProb = new double[root.cellPath.length];
+	for(int i=0; i<root.cellPath.length; i++) {
+	    postProb[i] = root.getOnePostProbAt(i, name);
+	}
+	curve.upDateData(postProb);
+    }
+}
+
+
+
+
+
+
+
diff --git a/ReadArguments.java b/ReadArguments.java
new file mode 100644
index 0000000..a7ab0c1
--- /dev/null
+++ b/ReadArguments.java
@@ -0,0 +1,232 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+public class ReadArguments {
+    
+    ReadArguments(ProAlign pa, String[] args) {
+
+	ProAlign.log("ReadArguments");
+	
+	for(int i=0; i<args.length; i++) {
+
+	    // sequence file
+	    if(args[i].startsWith("-seqfile=")) {
+		pa.seqfile = args[i].substring(args[i].indexOf("=")+1);
+		ProAlign.log.println(" sequence file: "+pa.seqfile);
+
+	    // guide tree file
+	    } else if(args[i].startsWith("-treefile=")) {
+		pa.treefile = args[i].substring(args[i].indexOf("=")+1);
+		ProAlign.log.println(" tree file: "+pa.treefile);
+
+	    // no GUI		
+	    } else if(args[i].startsWith("-nogui")) {
+		pa.nogui = true;
+		ProAlign.log.println(" no GUI: true");
+
+	    // do tree
+	    } else if(args[i].startsWith("-newtree")) {
+		pa.doTree = true;
+		ProAlign.log.println(" do tree: true");
+
+	    // sample from post. probabilities 		
+	    } else if(args[i].startsWith("-sample")) {
+		pa.trackBest = false;
+		ProAlign.log.println(" sample backtrace path: true");
+
+	    // 	gap frequency
+	    } else if(args[i].startsWith("-gapfreq=")) {
+		if(args[i].endsWith("=estimate")) {
+		    pa.estimateGapFreq = true;
+		    pa.estimateParameters = true ;
+		    ProAlign.log.println(" gap frequency: estimate");
+		} else {
+		    double tmp = new Double(
+			args[i].substring(args[i].indexOf("=")+1)).doubleValue();
+		    if(tmp>0d && tmp<0.5d) {
+			pa.gapFreq = tmp;
+			ProAlign.log.println(" gap frequency: "+tmp);
+		    } else {
+			ProAlign.log.println(" bad value: gap frequency "+tmp); 
+		    }
+		}
+
+	    // 	gap probability
+	    } else if(args[i].startsWith("-gapprob=")) {
+		if(args[i].endsWith("=estimate")) {
+		    pa.estimateGapProb = true;
+		    pa.estimateParameters = true ;
+		    ProAlign.log.println(" gap probability: estimate");
+		} else {
+		    double tmp = new Double(
+			args[i].substring(args[i].indexOf("=")+1)).doubleValue();
+		    if(tmp>0d && tmp<0.5d) {
+			pa.gapProb = tmp;
+			ProAlign.log.println(" gap probability: "+tmp);
+		    } else {
+			ProAlign.log.println(" bad value: gap probability "+tmp); 
+		    }
+		}
+
+	    // 	delta for HMM model		
+	    } else if(args[i].startsWith("-delta=")) {
+		if(args[i].endsWith("=estimate")) {
+		    pa.estimateDelta = true;
+		    pa.estimateParameters = true ;
+		    ProAlign.log.println(" HMM delta: estimate");
+		} else {
+		    double tmp = new Double(
+			args[i].substring(args[i].indexOf("=")+1)).doubleValue();
+		    if(tmp>0d && tmp<0.5d) {
+			pa.modelDelta = tmp;
+			ProAlign.log.println(" HMM delta: "+tmp);
+		    } else {
+			ProAlign.log.println(" bad value: HMM delta "+tmp); 
+		    }
+		}
+
+	    // 	epsilon for HMM model		
+	    } else if(args[i].startsWith("-epsilon=")) {
+		if(args[i].endsWith("=estimate")) {
+		    pa.estimateEpsilon = true;
+		    pa.estimateParameters = true ;
+		    ProAlign.log.println(" HMM epsilon: estimate");
+		} else {		
+		    double tmp = new Double(
+			args[i].substring(args[i].indexOf("=")+1)).doubleValue();
+		    if(tmp>0d && tmp<1.0d) {
+			pa.modelEpsilon = tmp;
+			ProAlign.log.println(" HMM epsilon: "+tmp);
+		    } else {
+			ProAlign.log.println(" bad value: HMM epsilon "+tmp); 
+		    }
+		}
+
+	    // 	band width		
+	    } else if(args[i].startsWith("-bwidth=")) {
+		int tmp = new Integer(
+		    args[i].substring(args[i].indexOf("=")+1)).intValue();
+		if(tmp>5) {
+		    pa.bandWidth = tmp;
+		    ProAlign.log.println(" band width: "+tmp);
+		} else {
+		    ProAlign.log.println(" bad value: band width "+tmp); 
+		}
+
+	    // 	distance scale
+	    } else if(args[i].startsWith("-distscale=")) {
+		double tmp = new Double(
+		    args[i].substring(args[i].indexOf("=")+1)).doubleValue();
+		if(tmp>0d && tmp<2.0d) {
+		    pa.distScale = tmp;
+		    ProAlign.log.println(" distance scale: "+tmp);
+		} else {
+		    ProAlign.log.println(" bad value:  distance scale "+tmp); 
+		}
+	    // multiple hits 
+	    } else if(args[i].startsWith("-nocorrection")) {
+		pa.correctMultiple = false ;
+		ProAlign.log.println(" correctMultiple: "+pa.correctMultiple);
+
+	    // terminal gaps
+	    } else if(args[i].startsWith("-penalize")) {
+		if(args[i].endsWith("=true")) {
+		    pa.penalizeTerminal = true ;
+		} else if(args[i].endsWith("=false")) {		
+		    pa.penalizeTerminal = false ;
+		}
+		ProAlign.log.println(" penalizeTerminal: "+pa.penalizeTerminal);
+
+	    // mean post prob.
+	    } else if(args[i].startsWith("-writemean")) {
+		pa.writeMean = true ;
+		ProAlign.log.println(" output mean: "+pa.writeMean);
+
+	    // all post prob.
+	    } else if(args[i].startsWith("-writeall")) {
+		pa.writeAll = true ;
+		ProAlign.log.println(" output all: "+pa.writeAll);
+
+	    // root characters
+	    } else if(args[i].startsWith("-writeroot")) {
+		pa.writeRoot = true ;
+		ProAlign.log.println(" output root: "+pa.writeRoot);
+
+	    // protein model
+	    } else if(args[i].startsWith("-wag")) {
+		ProAlign.protModel = "wag";
+		ProAlign.isDna = false;
+		ProAlign.log.println(" protein model: "+ProAlign.protModel);
+
+	    } else if(args[i].startsWith("-dayhoff")) {
+		ProAlign.protModel = "dayhoff";
+		ProAlign.isDna = false;
+		ProAlign.log.println(" protein model: "+ProAlign.protModel);
+
+	    } else if(args[i].startsWith("-jtt")) {
+		ProAlign.protModel = "jtt";
+		ProAlign.isDna = false;
+		ProAlign.log.println(" protein model: "+ProAlign.protModel);
+
+	    // trailing 
+	    } else if(args[i].startsWith("-notrailing")) {
+		pa.removeTrailing = false ;
+		ProAlign.log.println(" removeTrailing: "+pa.removeTrailing);
+
+
+	    // 	allowed trailing 
+	    } else if(args[i].startsWith("-trailing=")) {
+		int tmp = new Integer(
+		    args[i].substring(args[i].indexOf("=")+1)).intValue();
+		if(tmp>5 && tmp < pa.bandWidth/2) {
+		    pa.offset = tmp;
+		    ProAlign.log.println(" trailing: "+tmp);
+		} else {
+		    ProAlign.log.println(" bad value: trailing "+tmp); 
+		}
+
+	    // output file
+	    } else if(args[i].startsWith("-outfile=")) {
+		pa.outfile = args[i].substring(args[i].indexOf("=")+1);
+		ProAlign.log.println(" output file: "+pa.outfile);
+
+	    // output format
+	    } else if(args[i].startsWith("-outformat=")) {
+		if(args[i].substring(args[i].indexOf("=")+1).equalsIgnoreCase("fasta")){
+		    pa.outformat = 2;
+		    ProAlign.log.println(" outformat: PIR");
+		} else if(args[i].substring(args[i].indexOf("=")+1).equalsIgnoreCase("PIR")){
+		    pa.outformat = 2;
+		    ProAlign.log.println(" outformat: PIR");
+		} else if(args[i].substring(args[i].indexOf("=")+1).equalsIgnoreCase("nexus")){
+		    pa.outformat = 1;
+		    ProAlign.log.println(" outformat: nexus");
+		} else if(args[i].substring(args[i].indexOf("=")+1).equalsIgnoreCase("phylip")){
+		    pa.outformat = 3;
+		    ProAlign.log.println(" outformat: phylip");
+		} else if(args[i].substring(args[i].indexOf("=")+1).equalsIgnoreCase("msf")){
+		    pa.outformat = 4;
+		    ProAlign.log.println(" outformat: msf");
+		}
+		
+	    // 	stay quiet, no log 
+	    } else if(args[i].startsWith("-quiet")) {
+		pa.DEBUG = false;
+		ProAlign.log.println(" quiet...");
+		ProAlign.log.flush();
+	    } else {
+		ProAlign.log.println("Unrecognized paramter: "+args[i]);
+		ProAlign.log.flush();
+	    }		
+	}
+    }
+}
diff --git a/ResultWindow.java b/ResultWindow.java
new file mode 100644
index 0000000..637699b
--- /dev/null
+++ b/ResultWindow.java
@@ -0,0 +1,1374 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTextField;
+import javax.swing.JScrollBar;
+import javax.swing.BorderFactory;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JMenu;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Container;
+import java.awt.Point;
+import java.awt.Graphics;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowAdapter;
+import java.beans.PropertyChangeEvent ;
+import java.beans.PropertyChangeListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionAdapter;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.io.File;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+public class ResultWindow extends JFrame {
+
+    JMenuBar mb = new JMenuBar();
+    JMenu 
+        fm = new JMenu("File   "),
+	dm = new JMenu("Import"),
+	em = new JMenu("Export"),
+	am = new JMenu("Align  "),
+	tm = new JMenu("Font   "),
+	tm1 = new JMenu("Font size"),
+	tm2 = new JMenu("Horizontal space"),
+	tm3 = new JMenu("Vertical space"),
+	pm = new JMenu("Result "),
+	lm = new JMenu("Filter sites");
+
+    JMenuItem[] file = {
+	new JMenuItem("About ProAlign"),
+	new JMenuItem("Open alignment"),
+	new JMenuItem("Save alignment"),
+	new JMenuItem("Save guide tree"),
+	new JMenuItem("Exit")
+	    };
+    JMenuItem[] data = {
+	new JMenuItem("data"),
+	new JMenuItem("guide tree")
+	    };   
+    JMenuItem[] export = {
+	new JMenuItem("PIR"),
+	new JMenuItem("Phylip"),
+	new JMenuItem("MSF"),
+	new JMenuItem("Nexus")
+	    };
+    JMenuItem align[] = {
+	new JMenuItem("Do ClustalW guide tree"),
+	new JMenuItem("Do ProAlign guide tree"),
+	new JMenuItem("Do multiple alignment"),
+	new JMenuItem("Sample alignments"),
+	new JMenuItem("Remove all gaps"),
+	new JMenuItem("Set parameters"),
+	    };
+
+    JMenuItem proba[] = {
+	new JMenuItem("Plot min. probability"),
+	new JMenuItem("min 90%"),
+	new JMenuItem("min 80%"),
+	new JMenuItem("min 70%"),
+	new JMenuItem("min 60%"),
+	new JMenuItem("min 50%"),
+	new JMenuItem("min 40%"),
+	new JMenuItem("min 30%"),
+	new JMenuItem("min 20%"),
+	new JMenuItem("min 10%"),
+	new JMenuItem("0% (reset)"),
+    };
+    
+    JMenuItem fom1[] = {
+	new JMenuItem("8"),
+	new JMenuItem("9"),
+	new JMenuItem("10"),
+	new JMenuItem("11"),
+	new JMenuItem("12"),
+	new JMenuItem("13"),
+	new JMenuItem("14")
+    };
+
+    JMenuItem fom2[] = {
+	new JMenuItem("2"),
+	new JMenuItem("3"),
+	new JMenuItem("4"),
+	new JMenuItem("5"),
+	new JMenuItem("6"),
+	new JMenuItem("7"),
+	new JMenuItem("8"),
+	new JMenuItem("9")
+    };
+
+    JMenuItem fom3[] = {
+	new JMenuItem("2"),
+	new JMenuItem("3"),
+	new JMenuItem("4"),
+	new JMenuItem("5"),
+	new JMenuItem("6"),
+	new JMenuItem("7"),
+	new JMenuItem("8"),
+	new JMenuItem("9")
+    };
+
+
+    JScrollPane sPane1;
+    JScrollPane sPane2;
+    JScrollPane sPane3;
+
+    JScrollBar sButt1;
+    JScrollBar sButt3;
+
+    //   ScrollListener scro;
+    
+    JSplitPane splitp1;
+    JSplitPane splitp2;
+
+    JTextField messageText;
+    Container cp;
+    
+    Font font = new Font(ProAlign.paFontName,Font.PLAIN, ProAlign.paFontSize);
+    Font font1 = new Font("monospaced", Font.PLAIN, ProAlign.paFontSize);
+    
+    PrintTree guidetree;
+    PrintNames seqname;
+    PrintData seqcont;
+
+    Rule charRule;
+    JPanel columnRule1;
+    JPanel columnRule2;
+    
+    int splitWidth = 100;
+    boolean isAligned = false;
+    boolean hasData = false;
+    boolean hasTree = false;
+
+    String[] nodeNames;
+    double[][] postProb;
+
+    int wHeight;
+    int wWidth;
+    int maxLength;
+
+    static int verticalCharSpace = 5;
+    static int horizontalCharSpace = 5;
+    static int rwFontSize = 11;
+
+    HashMap seqs;
+
+    static ResultWindow rw;
+    AlignmentNode root;
+    ProAlign pa;
+
+    ResultWindow(ProAlign pa) {
+	
+	rw = this;
+	this.pa = pa;
+	setTitle("ProAlign "+ProAlign.version);   
+
+	// Create the JMenuBar - it's needed anyway.
+	//
+	FileMenuListener fle = new FileMenuListener();
+	AlignmentMenuListener alg = new AlignmentMenuListener();
+	ProbabilityMenuListener prb = new ProbabilityMenuListener();
+	FontMenuListener fon = new FontMenuListener();
+
+        file[0].setActionCommand("about");
+        file[0].addActionListener(fle);
+        file[0].setFont(font);
+        file[1].setActionCommand("open");
+        file[1].addActionListener(fle);
+        file[1].setFont(font);
+        file[2].setActionCommand("save");
+        file[2].addActionListener(fle);
+        file[2].setFont(font);
+        file[3].setActionCommand("savepag");
+        file[3].addActionListener(fle);
+        file[3].setFont(font);
+        file[4].setActionCommand("exit");
+        file[4].addActionListener(fle);
+        file[4].setFont(font);
+
+	data[0].setActionCommand("data");
+	data[0].addActionListener(fle);
+	data[0].setFont(font);
+	data[1].setActionCommand("tree");
+	data[1].addActionListener(fle);
+	data[1].setEnabled(false);
+	data[1].setFont(font);
+
+	export[0].setActionCommand("fasta");
+	export[0].addActionListener(fle);
+	export[0].setFont(font);
+	export[1].setActionCommand("phylip");
+	export[1].addActionListener(fle);
+	export[1].setFont(font);
+	export[2].setActionCommand("msf");
+	export[2].addActionListener(fle);
+	export[2].setFont(font);
+	export[3].setActionCommand("nexus");
+	export[3].addActionListener(fle);
+	export[3].setFont(font);
+
+	align[0].setActionCommand("guide");
+	align[0].addActionListener(alg);
+	align[0].setFont(font);
+	align[0].setEnabled(false);
+	align[1].setActionCommand("paguide");
+	align[1].addActionListener(alg);
+	align[1].setFont(font);
+	align[1].setEnabled(false);
+	align[2].setActionCommand("multiple");
+	align[2].addActionListener(alg);
+	align[2].setFont(font);
+	align[2].setEnabled(false);
+	align[3].setActionCommand("sample");
+	align[3].addActionListener(alg);
+	align[3].setFont(font);
+	align[3].setEnabled(false);
+	align[4].setActionCommand("remove");
+	align[4].addActionListener(alg);
+	align[4].setFont(font);
+	align[4].setEnabled(false);
+	align[5].setActionCommand("setting");
+	align[5].addActionListener(alg);
+	align[5].setFont(font);
+	align[5].setEnabled(true);
+
+	for(int i=0; i<proba.length; i++) {
+	    proba[i].addActionListener(prb);
+	    proba[i].setFont(font);
+	}
+	proba[0].setActionCommand("plot");
+	proba[0].setEnabled(false);
+	proba[1].setActionCommand("90percent");
+	proba[2].setActionCommand("80percent");
+	proba[3].setActionCommand("70percent");
+	proba[4].setActionCommand("60percent");
+	proba[5].setActionCommand("50percent");
+	proba[6].setActionCommand("40percent");
+	proba[7].setActionCommand("30percent");
+	proba[8].setActionCommand("20percent");
+	proba[9].setActionCommand("10percent");
+	proba[10].setActionCommand("0percent");
+
+
+	for(int i=0; i<fom1.length; i++) {
+	    fom1[i].addActionListener(fon);
+	    fom1[i].setFont(font);
+	    fom1[i].setActionCommand("size");
+	    tm1.add(fom1[i]);
+	}
+	for(int i=0; i<fom2.length; i++) {
+	    fom2[i].addActionListener(fon);
+	    fom2[i].setFont(font);
+	    fom2[i].setActionCommand("hor");
+	    tm2.add(fom2[i]);
+	}
+	for(int i=0; i<fom3.length; i++) {
+	    fom3[i].addActionListener(fon);
+	    fom3[i].setFont(font);
+	    fom3[i].setActionCommand("ver");
+	    tm3.add(fom3[i]);
+	}
+
+	dm.add(data[0]);
+	dm.add(data[1]);
+	dm.setFont(font);
+	em.add(export[0]);
+	em.add(export[1]);
+	em.add(export[2]);
+	em.add(export[3]);
+	em.setFont(font);
+	em.setEnabled(false);
+        fm.add(file[0]);
+        fm.addSeparator();
+        fm.add(file[1]);
+        fm.add(file[2]);
+	file[2].setEnabled(false);
+        fm.addSeparator();
+        fm.add(dm);
+        fm.add(em);
+	file[3].setEnabled(false);
+        fm.add(file[3]);
+        fm.addSeparator();
+        fm.add(file[4]);
+        fm.setFont(font);
+	am.add(align[0]);
+	am.add(align[1]);
+	am.add(align[2]);
+	am.add(align[3]);
+        am.addSeparator();
+	am.add(align[4]);
+	am.add(align[5]);
+	am.setFont(font);
+
+	for(int i=1; i<proba.length; i++) {
+	    lm.add(proba[i]);
+	}
+	pm.add(proba[0]);
+	pm.add(lm);
+	lm.setFont(font);
+	lm.setEnabled(false);
+	pm.setFont(font);
+	
+        mb.add(fm);
+
+	tm.setFont(font);
+	tm1.setFont(font);
+	tm2.setFont(font);
+	tm3.setFont(font);
+	tm.add(tm1);
+	tm.add(tm2);
+	tm.add(tm3);
+	mb.add(tm);
+
+        mb.add(am);
+	mb.add(pm);
+        setJMenuBar(mb);
+
+
+	// Message field to show some info.
+	//
+	messageText = new JTextField();
+	messageText.setFont(font);
+	messageText.setBackground(Color.white);
+	messageText.setEditable(false);
+
+
+	//Create empty panels and import data later.
+	//
+	seqname = new PrintNames();
+	seqname.setPreferredSize(new Dimension(50,100));
+	seqcont = new PrintData();
+	seqcont.setPreferredSize(new Dimension(500,100));
+	guidetree = new PrintTree();
+	guidetree.setPreferredSize(new Dimension(50,100));
+
+	// Create a split pane for the two scroll panes at time.
+	//
+	splitp1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+	splitp1.setOneTouchExpandable(true);
+	splitp1.setDividerLocation(splitWidth);
+	
+	// Listener needed to update the tree.
+	splitp1.addPropertyChangeListener(new SplitListener());
+
+	// Second splitpane will keep the first as a left-side.
+	//
+	splitp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+	splitp2.setOneTouchExpandable(true);
+	splitp2.setDividerLocation(splitWidth*2);
+
+	// Set scrollPanes.
+	//	
+	setScrollPanes();
+
+
+	// Get the initial size from the parent - needs updating.
+	//
+	wHeight = ProAlign.wHeight;
+	wWidth = ProAlign.wWidth;
+
+	// Do not use layout but set it manually.
+	//
+	cp = getContentPane();
+	cp.setLayout(null);
+	cp.add(splitp2);
+	cp.add(messageText);
+
+	if(System.getProperty("os.name").startsWith("Windows")){
+	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
+	    messageText.setBounds(0,wHeight-75,wWidth-10,20);
+	} else if(System.getProperty("os.name").startsWith("Mac")){
+	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
+	    messageText.setBounds(0,wHeight-75,wWidth-20,25);	    
+	} else {
+	    splitp2.setBounds(0,0, wWidth-10,wHeight-66);
+	    messageText.setBounds(0,wHeight-65,wWidth-10,25);
+	}
+	addWindowListener(new WindowAdapter() {
+		public void windowClosing(WindowEvent e){System.exit(0);}
+	    });
+	addMouseMotionListener(new MiceListener());
+    }
+
+    void setAlignedData(AlignmentNode root) {
+	    
+	// Get the sequence names..
+	//
+	String[] seqNames = root.getTerminalNames();
+	for(int i=0; i<seqNames.length; i++) {
+	    seqNames[i] = (seqNames[i]+"                    ").substring(0,20);
+	}
+	
+	// and get the sequence data.
+	//
+	String[] seqData = new String[root.getNumChild()];
+	for(int i=0; i<seqData.length; i++) { seqData[i] = ""; }
+	for(int i=0; i<root.cellPath.length; i++) {
+	    
+	    int h = 0;
+	    char[] c0 = root.child[0].getCharacterAt(root.cellPath[i][0]-2);
+	    char[] c1 = root.child[1].getCharacterAt(root.cellPath[i][1]-2);
+	    
+	    for(int j=0; j<c0.length; j++) {
+		seqData[h++] += c0[j];
+	    }
+	    for(int j=0; j<c1.length; j++) {
+		seqData[h++] += c1[j];
+	    }	
+	} 
+
+	// Get the posterior probabilities.
+	//
+	nodeNames = root.getInternalNames();
+	postProb = new double[root.cellPath.length][nodeNames.length];
+	for(int i=0; i<root.cellPath.length; i++) {
+	    postProb[i] = root.getInternalPostProbAt(i);
+	}
+	
+	// Update info & create new JPanels.
+	//
+	rw.messageText.setText(" Alignment ready: "+seqData.length+" sequences, "+
+			       seqData[0].length() +" characters. viterbi:"+root.sumViterbiEnd()+
+				       "; forward: "+root.sumForwardEnd());
+	seqname = new PrintNames(seqNames, true);
+	seqcont = new PrintData(seqData, postProb, rw);
+	guidetree = new PrintTree(root, rw, true);
+
+	rw.file[1].setEnabled(true); // open alignment
+	rw.file[2].setEnabled(true); // save alignment
+	rw.file[3].setEnabled(true); // save paguide
+
+	rw.data[1].setEnabled(true); // import guide tree
+
+	rw.align[0].setEnabled(false); // do guide tree
+	rw.align[1].setEnabled(true); // do paguide tree
+	rw.align[2].setEnabled(false); // do multiple 
+	rw.align[3].setEnabled(false); // sample
+	rw.align[4].setEnabled(true); // remove gaps
+
+	rw.dm.setEnabled(true); // import
+	rw.em.setEnabled(true); // export 
+
+	rw.proba[0].setEnabled(true); // plot prob's
+	rw.lm.setEnabled(true); // filter sites
+
+	isAligned = true;
+	hasData = true;
+	hasTree = true;
+
+    }
+
+    void setRawData(HashMap seqs) {
+
+	rw.seqs = seqs;
+
+	// Get the sequence names and the data.
+	//
+	Iterator seqKeys = seqs.keySet().iterator();	
+	String[] seqNames = new String[seqs.keySet().size()];
+	String[] seqData = new String[seqs.keySet().size()];
+
+	int i = 0;
+	maxLength = 0;
+	while(seqKeys.hasNext()) {
+	    String name = (String) seqKeys.next();
+	    seqData[i] = (String) seqs.get(name);
+	    seqNames[i] = (name+"                    ").substring(0,20);
+
+	    if(seqData[i].length()>maxLength) {
+		maxLength = seqData[i].length();
+	    }
+	    i++;
+	}
+	
+	// Update info & create new JPanels.
+	//
+	rw.messageText.setText(" Data ready: "+seqData.length+" sequences. ");
+	seqname = new PrintNames(seqNames, false);
+	seqcont = new PrintData(seqData, maxLength, rw);
+	guidetree = new PrintTree();
+	guidetree.setPreferredSize(new Dimension(50,seqname.getHeight()));
+
+	rw.file[1].setEnabled(true); // open alignment
+	rw.file[2].setEnabled(false); // save alignment
+	rw.file[3].setEnabled(false); // save paguide
+
+	rw.data[1].setEnabled(true); // import guide tree
+
+	rw.align[0].setEnabled(true); // do clustalw guide tree
+	rw.align[1].setEnabled(true); // do proalign guide tree
+	rw.align[2].setEnabled(false); // do multiple
+	rw.align[3].setEnabled(true); // sample
+	rw.align[4].setEnabled(false); // remove gaps
+
+	rw.dm.setEnabled(true); // import
+	rw.em.setEnabled(false); // export 
+
+	rw.proba[0].setEnabled(false); // plot prob's
+	rw.lm.setEnabled(false); // filter sites
+
+	isAligned = false;
+	hasData = true;
+	hasTree = false;
+
+    }
+
+    void setRawDataAndTree(String tree) {
+	
+	rw.root = new AlignmentNode(pa,tree,0f);
+
+	String[] seqNames = root.getTerminalNames();
+	String[] seqData = new String[seqNames.length];
+
+	for(int j=0; j<seqNames.length; j++) {
+	    seqData[j] = (String) pa.seqs.get(seqNames[j]);
+	    seqNames[j] = (seqNames[j]+"                    ").substring(0,20);
+	}
+
+	// Update info & create new JPanels.
+	//
+	rw.messageText.setText(" Data and tree ready: "+seqData.length+" sequences. ");
+	seqname = new PrintNames(seqNames, false);
+	seqcont = new PrintData(seqData, maxLength, rw);
+	guidetree = new PrintTree(root, rw, false);
+
+	rw.file[1].setEnabled(true); // open alignment
+	rw.file[2].setEnabled(false); // save alignment
+	rw.file[3].setEnabled(true); // save paguide
+
+	rw.data[0].setEnabled(true); // import data
+	rw.data[1].setEnabled(true); // import guide tree
+
+	rw.align[0].setEnabled(true); // do guide tree
+	rw.align[1].setEnabled(true); // do paguide tree
+	rw.align[2].setEnabled(true); // do multiple
+	rw.align[3].setEnabled(true); // sample
+	rw.align[4].setEnabled(false); // remove gaps
+	
+	rw.dm.setEnabled(true); // import
+	rw.em.setEnabled(false); // export 
+
+	isAligned = false;
+	hasData = true;
+	hasTree = true;
+    }
+
+    void removeGaps(String tree) {
+
+	pa.setNodeNumber(-1);
+	rw.root = new AlignmentNode(pa,tree,0f);
+	
+	String[] seqNames = rw.root.getTerminalNames();
+	String[] seqData = new String[rw.root.getNumChild()];
+	
+	maxLength = 0;
+	for(int i=0; i<seqNames.length; i++) {
+	    seqData[i] = (String) seqs.get(seqNames[i]);
+	    seqNames[i] = (seqNames[i]+"                    ").substring(0,20);
+
+	    if(seqData[i].length()>maxLength) {
+		maxLength = seqData[i].length();
+	    }
+	}
+
+	// Update info & create new JPanels.
+	//
+	rw.messageText.setText(" Data and tree ready: "+seqData.length+" sequences. ");
+	seqname = new PrintNames(seqNames, false);
+	seqcont = new PrintData(seqData, maxLength, rw);
+	guidetree = new PrintTree(root, rw, false);
+
+	rw.file[1].setEnabled(true); // open alignment
+	rw.file[2].setEnabled(false); // save alignment
+	rw.file[3].setEnabled(true); // save paguide
+
+	rw.data[1].setEnabled(true); // import guide tree
+
+	rw.align[0].setEnabled(true); // do guide tree 
+	rw.align[1].setEnabled(true); // do pa guide tree
+	rw.align[2].setEnabled(true); // do multiple
+	rw.align[3].setEnabled(true); // sample
+	rw.align[4].setEnabled(false); // remove gaps
+
+	rw.dm.setEnabled(true); // import
+	rw.em.setEnabled(false); // export
+
+	rw.proba[0].setEnabled(false); // plot prob's
+	rw.lm.setEnabled(false); // filter sites
+
+	isAligned = false;
+	hasData = true;
+
+    }
+
+    // Update if the frame has been resized.
+    //
+    public void update(Graphics g) {
+	
+	wHeight = rw.getHeight();
+        wWidth = rw.getWidth();
+
+	if(System.getProperty("os.name").startsWith("Windows")){
+	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
+	    messageText.setBounds(0,wHeight-75,wWidth-10,20);
+	} else if(System.getProperty("os.name").startsWith("Mac")){
+	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
+	    messageText.setBounds(0,wHeight-75,wWidth-20,25);	    
+	} else {
+	    splitp2.setBounds(0,0, wWidth-10,wHeight-66);
+	    messageText.setBounds(0,wHeight-65,wWidth-10,25);
+	}
+	splitp2.updateUI();
+	
+	messageText.updateUI();
+
+    }
+    public void macUpdate() { // things don't work on OSX
+
+	if(wHeight!=rw.getHeight() || wWidth!=rw.getWidth()){
+
+	    wHeight = rw.getHeight();
+	    wWidth = rw.getWidth();
+
+	    splitp2.setBounds(0,0, wWidth-10,wHeight-76);	
+	    messageText.setBounds(0,wHeight-75,wWidth-20,25);	    
+
+	    splitp2.updateUI();
+	    
+	    messageText.updateUI();
+	}
+    }
+
+    void setScrollPanes() {
+
+	sPane1 = new JScrollPane(guidetree,
+				 JScrollPane.VERTICAL_SCROLLBAR_NEVER,
+				 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+	sPane2 = new JScrollPane(seqname,
+				 JScrollPane.VERTICAL_SCROLLBAR_NEVER,
+				 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+	sPane3 = new JScrollPane(seqcont,
+				 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+				 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+	
+	sButt1 = new JScrollBar();
+	sButt3 = new JScrollBar();
+
+	rw.setRulers();
+
+	sPane1.setViewportBorder(BorderFactory.createLineBorder(Color.black));
+	sPane1.setColumnHeaderView(columnRule1);
+	sPane1.setBackground(Color.white);
+
+	sPane2.setViewportBorder(BorderFactory.createLineBorder(Color.black));
+	sPane2.setColumnHeaderView(columnRule2);
+	sPane2.setBackground(Color.white);
+	seqname.sPane = sPane2;
+	    
+	sPane3.setViewportBorder(BorderFactory.createLineBorder(Color.black));
+	sPane3.setColumnHeaderView(charRule);
+	sPane3.setBackground(Color.white);
+	seqcont.sPane = sPane3;
+	
+	sButt3.setUnitIncrement(30);
+	sButt3.setBlockIncrement(100);
+	sButt3.addAdjustmentListener(new ScrollListener());
+	sPane3.setVerticalScrollBar(sButt3);
+
+	sPane1.getHorizontalScrollBar().setBackground(Color.white);
+	sPane2.getHorizontalScrollBar().setBackground(Color.white);
+	sPane3.getHorizontalScrollBar().setBackground(Color.white);
+	sPane1.getVerticalScrollBar().setBackground(Color.white);
+	sPane3.getVerticalScrollBar().setBackground(Color.white);
+	sPane3.getHorizontalScrollBar().setUnitIncrement(50);
+	sPane3.getHorizontalScrollBar().setBlockIncrement(200);
+	
+	splitp1.setLeftComponent(sPane1);
+	splitp1.setRightComponent(sPane2);
+	splitp2.setLeftComponent(splitp1);
+	splitp2.setRightComponent(sPane3);
+
+	splitp1.setOneTouchExpandable(true);
+	splitp1.setDividerLocation(splitWidth);
+	splitp2.setOneTouchExpandable(true);
+	splitp2.setDividerLocation(splitWidth*2);
+
+	splitp1.updateUI();
+	splitp2.updateUI();
+
+    }    
+
+    void setRulers() {
+
+	// Create a ruler for sequence data..
+	//
+	charRule = new Rule(rw);
+	charRule.setPreferredWidth(seqcont.totalWidth);
+	charRule.setIncrement(seqcont.columnWidth, seqcont.horizontalCharSpace);
+	charRule.setBackground(Color.white);	
+	// ..and two empty panels to fill the same space in other windows.
+	//
+	columnRule1 = new JPanel();
+	columnRule1.setPreferredSize(new Dimension(guidetree.getWidth(),charRule.rulerHeight));
+	columnRule1.setBackground(Color.white);
+	//
+	columnRule2 = new JPanel();
+	columnRule2.setPreferredSize(new Dimension(seqname.totalWidth,charRule.rulerHeight));
+	columnRule2.setBackground(Color.white);
+
+    }
+    
+    // Focus the alignment view if clicked on the curve.
+    //
+    void focusAlignment(int x) {
+	int y3 = (int) sPane3.getViewport().getViewPosition().getY();
+	int x3 = x*seqcont.columnWidth-sPane3.getWidth()/2;
+	if(x3<0) { 
+	    x3 = 0; 
+	}
+	if(x3>(seqcont.totalWidth-sPane3.getWidth())) { 
+	    x3 = seqcont.totalWidth-sPane3.getWidth();
+	}
+	sPane3.getViewport().setViewPosition(new Point(x3,y3));
+	sPane3.getViewport().updateUI();
+    }
+
+
+    // Write info at messageText field.
+    // 
+    void writeNodeInfo(int col, int row) {
+	String nn = nodeNames[row];
+	AlignmentNode an = root.getNodeNamed(nn);
+	String alphabet = root.getAlphabet();
+	int site = root.getSiteAt(col,nn);
+
+	String txt = "";
+
+	if(site>-1) {
+	    for(int i=0; i<alphabet.length(); i++) {
+		txt += ", ";
+		txt += alphabet.charAt(i)+": ";
+		if(ProAlign.isDna) {
+		    if(an.charProb[site][i]<0.001) {
+			txt += ("0.000"); 
+		    } else {
+			txt += (""+an.charProb[site][i]+"     ").substring(0,5);
+		    }
+		} else {
+		    if(an.charProb[site][i]<0.001) {
+			txt += ("0.00"); 
+		    } else {
+			txt += (""+an.charProb[site][i]+"     ").substring(0,4);
+		    }
+		}
+	    }
+	} else {
+	    txt = ", no info";
+	}
+
+	String pp = (""+Math.exp(postProb[col][row])+"     ").substring(0,5);
+	messageText.setText(col+", "+nn+": "+pp+" (char "+site+txt+")");
+       
+    }
+
+    void convertNodeInfoX(int col, String name) {
+
+	if(col >= postProb.length) { col=postProb.length-1; }
+	int row = -1;
+	for(int i=0; i<nodeNames.length; i++) {
+	    if(nodeNames[i].equals(name)) { row = i; }
+	}
+	if(row > -1) {
+	    rw.writeNodeInfo(col,row);
+	}
+    }
+
+    void writeTempFasta() {
+
+	try {
+	    OutFile out = new OutFile(ProAlign.tempFolder+File.separator+"proalign.seq");
+
+	    Iterator seqKeys = seqs.keySet().iterator();	
+
+	    while(seqKeys.hasNext()) {
+		String name = (String) seqKeys.next();
+		String data = (String) seqs.get(name);
+
+		if(ProAlign.isDna) {
+		    out.println(">DL;" +name);
+		} else {
+		    out.println(">P1;" +name);
+		}
+		out.println();
+		int count = 0;
+		for(int j = 0; j<data.length(); j++) {
+		    count++;
+		    out.print(data.charAt(j));
+		    if(count == 50) {
+			out.println("");
+			count = 0;
+		    }
+		}
+		out.println("*");
+	    }
+	    out.close();
+	} catch (IOException e) {
+	}
+    }
+
+    static void updateInfo(String text) {
+	
+	rw.messageText.setText(text);
+
+    }
+
+    // Vertical scrolling - requires some synchronizing.
+    //
+    class ScrollListener implements AdjustmentListener {
+	public void adjustmentValueChanged(AdjustmentEvent e) {
+
+	    int y3 = (int) sPane3.getViewport().getViewPosition().getY();
+	    int x3 = (int) sPane3.getViewport().getViewPosition().getX();
+	    int x2 =  (int) sPane2.getViewport().getViewPosition().getX();
+
+	    sPane1.getViewport().setViewPosition(new Point(0,y3));
+	    sPane2.getViewport().setViewPosition(new Point(x2,y3));
+	    sPane3.getViewport().setViewPosition(new Point(x3,y3));
+
+	    sPane2.getViewport().updateUI();
+	    sPane3.getViewport().updateUI();
+	    
+	}
+    }   
+
+    // Tree split pane - tree dimesions need updating.
+    //
+    class SplitListener implements PropertyChangeListener {
+	public void propertyChange(PropertyChangeEvent e) {
+
+	    if(guidetree.isTreeGiven) {
+		guidetree.setPreferredSize(new Dimension(sPane1.getWidth(), guidetree.yMax));
+		guidetree.updateUI();
+	    }
+
+	}
+    }
+
+     
+    // ActionListener for 'File' menu
+    //
+    class FileMenuListener implements ActionListener {
+	public void actionPerformed(ActionEvent e) {
+	    
+	    JMenuItem target = (JMenuItem)e.getSource();
+	    String actionCommand = target.getActionCommand();
+	    
+	    if(actionCommand.equals("about")) {
+		
+		String text = new String(
+		    "\n            ProAlign v. "+ProAlign.version+"\n"+
+		    "  a program for probabilistic sequence alignment \n"+
+		    "  (c) 2002 Ari Loytynoja & Michel C. Milinkovitch \n");
+		OpenDialog od = new OpenDialog(ResultWindow.this);
+		od.showDialog("About ProAlign", text);
+		
+	    } else if(actionCommand.equals("open")) {
+
+		OpenFileChooser opf = 
+		    new OpenFileChooser(ResultWindow.this,"Open",true);
+		String filepath = opf.openFile();
+
+		if(!filepath.equals("")) {
+
+		    UserSettings user = new UserSettings(); 
+		    String[] userdata = user.readSettings();
+		    ProAlign.folderPath = new File(filepath).getParent();
+		    userdata[0] = new File(filepath).getParent();
+                    user.writeSettings(userdata);
+
+		    boolean validFile = true;
+
+		    try {
+			ObjectInputStream is = 
+			    new ObjectInputStream(new FileInputStream(filepath));
+			rw.root = (AlignmentNode) is.readObject();
+		    }
+		    catch(InvalidClassException ice) {
+			String text = new String("\n  Error! File doesn't look\n"+
+						 "  like a ProAlign-file!\n");
+			OpenDialog od = new OpenDialog(ResultWindow.this);
+			od.showDialog("Error!", text);
+
+			ProAlign.log.println("Open exception1: "+filepath+"\n"+ice.toString());
+			ProAlign.log.flush();
+			validFile = false;
+		    } 
+		    catch(Exception oe) {
+			ProAlign.log.println("Open exception2: "+filepath+"\n"+oe.toString());
+			ProAlign.log.flush();
+			//oe.printStackTrace();	
+			validFile = false;
+		    }
+
+		    if(validFile) {
+
+			rw.setAlignedData(rw.root);
+			rw.setScrollPanes();
+			PrintTree.numOpenWindows = 0;
+		    }
+
+		    // Set substitution model
+		    if(ProAlign.isDna){
+			rw.pa.sm.jcDnaModel();	    
+		    } else {
+			rw.pa.sm.wagProteinModel();
+		    }
+
+		    // Set ProAlign 'seqs' hashmap
+		    //
+		    String[] names = rw.root.getTerminalNames();
+		    String[] data = new String[rw.root.getNumChild()];
+
+		    for(int i=0; i<data.length; i++) { data[i] = ""; }
+		    for(int i=0; i<rw.root.cellPath.length; i++) {
+			
+			int h = 0;
+			char[] c0 = root.child[0].getCharacterAt(root.cellPath[i][0]-2);
+			char[] c1 = root.child[1].getCharacterAt(root.cellPath[i][1]-2);
+			
+			for(int j=0; j<c0.length; j++) {
+			    if(c0[j]!='-') {
+				data[h] += c0[j];
+			    }
+			    h++;
+			}
+			for(int j=0; j<c1.length; j++) {
+			    if(c1[j]!='-') {
+				data[h] += c1[j];
+			    }	
+			    h++;
+			} 
+		    }
+		    		 
+		    rw.seqs = new HashMap();
+		    for(int i=0; i<names.length; i++) {
+			rw.seqs.put(names[i], data[i]);
+		    }
+		    pa.seqs = seqs;
+		    
+		}
+		
+	    } else if(actionCommand.equals("save")) {
+		
+		if(rw.root != null) {
+		    OpenFileChooser opf = 
+			new OpenFileChooser(ResultWindow.this,"Save",true);
+		    String filepath = opf.openFile();
+		
+		    if(!filepath.equals("")) {
+			if(!filepath.endsWith(ProAlign.fileExt)) {
+			    filepath += "."+ProAlign.fileExt;
+
+			    UserSettings user = new UserSettings(); 
+			    String[] userdata = user.readSettings();
+			    ProAlign.folderPath = new File(filepath).getParent();
+			    userdata[0] = new File(filepath).getParent();
+			    user.writeSettings(userdata);
+			}
+			try {
+			    ObjectOutputStream os = 
+				new ObjectOutputStream(new FileOutputStream(filepath));
+			    os.writeObject((AlignmentNode) rw.root);
+			    os.flush();
+			    os.close();
+			} catch(IOException ioe) {
+			    ProAlign.log.println("Save exception1: "+filepath+"\n"+ioe.toString());
+			    ProAlign.log.flush();
+			    //ioe.printStackTrace();
+			}
+			catch(Exception oe) {
+			    ProAlign.log.println("Save exception2: "+filepath+"\n"+oe.toString());
+			    ProAlign.log.flush();
+			    //oe.printStackTrace();	
+			}
+		    }
+		}
+
+	    } else if(actionCommand.equals("savepag")) {
+		
+		OpenFileChooser opf = 
+		    new OpenFileChooser(ResultWindow.this,"Save guide tree",false);
+		String filepath = opf.openFile();
+		if(!filepath.equals("")) {
+		    try {
+			OutFile paguide = new OutFile(filepath);
+			paguide.println(rw.root.tree);
+			paguide.flush();
+			paguide.close();
+		    } catch(Exception ne) { }
+		}
+		//System.out.println("pag: "+rw.root.tree);
+
+	    } else if(actionCommand.equals("data")) {
+
+		OpenFileChooser opf = 
+		    new OpenFileChooser(ResultWindow.this,"Import",false);
+		String filepath = opf.openFile();
+
+		SequenceReader sr = new SequenceReader();
+		if(!filepath.equals("")) {
+		    if(sr.fromFile(filepath)) {
+			HashMap seqs = sr.getSequences();
+			pa.seqs = seqs;
+
+			CheckSequence cs = new CheckSequence();
+			if(cs.isDna(seqs)){
+			    pa.sm.jcDnaModel();
+			    ProAlign.isDna = true;
+			} else {
+			    pa.sm.wagProteinModel();
+			    ProAlign.isDna = false;
+			}
+
+			if(!cs.isFromAlphabet(seqs,pa.sm.equateAlphabet)) {
+			    OpenDialog od = new OpenDialog(ResultWindow.this);
+			    od.showDialog("Error!", cs.getError());
+			} else {
+			    rw.setRawData(seqs);
+			    rw.setScrollPanes();
+
+			    rw.root = null;
+			    pa.setNodeNumber(-1);
+			    
+			    UserSettings user = new UserSettings(); 
+			    String[] userdata = user.readSettings();
+			    ProAlign.folderPath = new File(filepath).getParent();
+			    userdata[0] = new File(filepath).getParent();
+			    user.writeSettings(userdata);
+			}
+		    } else {
+			OpenDialog od = new OpenDialog(ResultWindow.this);
+			od.showDialog("Error!", sr.getErrors());	
+		    }
+		}
+
+	    } else if(actionCommand.equals("tree")) {
+
+		OpenFileChooser opf = 
+		    new OpenFileChooser(ResultWindow.this,"Import",false);
+		String filepath = opf.openFile();
+
+		if(!filepath.equals("")) {
+
+		    UserSettings user = new UserSettings(); 
+		    String[] userdata = user.readSettings();
+		    ProAlign.folderPath = new File(filepath).getParent();
+		    userdata[0] = new File(filepath).getParent();
+                    user.writeSettings(userdata);
+
+		    TreeReader tr = new TreeReader();
+		    
+		    String[] treeNodes = tr.getAllNodes(filepath);
+		    CheckTreeAndData chk = 
+			new CheckTreeAndData(treeNodes, rw.seqs);
+		    if(chk.nodesAreSame()) {
+			pa.setNodeNumber(-1);
+			String guide = tr.readFile(filepath);
+			if(tr.isUnRooted) {
+			    TreeNode tn = new TreeNode(guide);
+			    guide = tn.findMiddlePoint();
+ 			}
+			if(rw.isAligned) {
+			    rw.removeGaps(guide);
+			} else {
+			    rw.setRawDataAndTree(guide);
+			}
+			rw.setScrollPanes();
+
+		    }
+		}
+	    } else if(actionCommand.equals("fasta")) {
+
+		SaveData sd = new SaveData("Fasta", ResultWindow.this);
+
+	    } else if(actionCommand.equals("phylip")) {
+
+		SaveData sd = new SaveData("Phylip", ResultWindow.this);
+
+	    } else if(actionCommand.equals("msf")) {
+
+		SaveData sd = new SaveData("MSF", ResultWindow.this);
+
+	    } else if(actionCommand.equals("nexus")) {
+
+		SaveData sd = new SaveData("Nexus", ResultWindow.this);
+
+	    } else if(actionCommand.equals("exit")) {
+		dispatchEvent(new WindowEvent(ResultWindow.this,
+					      WindowEvent.WINDOW_CLOSING));
+	    }
+	}
+    }
+
+    // ActionListener for 'Alignment' menu
+    //
+    class AlignmentMenuListener implements ActionListener {
+	public void actionPerformed(ActionEvent e) {
+	    
+	    JMenuItem target = (JMenuItem)e.getSource();
+	    String actionCommand = target.getActionCommand();
+	    
+	    if(actionCommand.equals("guide")) {
+
+		pa.setNodeNumber(-1);
+		rw.writeTempFasta();
+
+		try {
+		    RunClustalw rc = new RunClustalw(ResultWindow.this);
+		    rw.messageText.setText(" Creating ClustalW guide tree.");
+
+		    rw.file[1].setEnabled(false); // open alignment
+		    rw.file[2].setEnabled(false); // save alignment
+		    rw.dm.setEnabled(false); // import
+		    rw.align[0].setEnabled(false); // do guide tree  
+		    rw.align[1].setEnabled(false); // do paguide tree
+		    rw.align[2].setEnabled(false); // do multiple
+		    rw.align[3].setEnabled(false); // sample
+		    rw.align[4].setEnabled(false); // remove gaps
+
+		} catch(Exception ex) {
+
+		    rw.file[1].setEnabled(true); // open alignment
+		    rw.file[2].setEnabled(false); // save alignment
+		    rw.file[3].setEnabled(false); // save paguide
+		    
+		    rw.data[1].setEnabled(true); // import guide tree
+		    
+		    rw.align[0].setEnabled(true); // do clustalw guide tree
+		    rw.align[1].setEnabled(true); // do proalign guide tree
+		    rw.align[2].setEnabled(false); // do multiple
+		    rw.align[3].setEnabled(true); // sample
+		    rw.align[4].setEnabled(false); // remove gaps
+		    
+		    rw.dm.setEnabled(true); // import
+		    rw.em.setEnabled(false); // export 
+		    
+		    rw.proba[0].setEnabled(false); // plot prob's
+		    rw.lm.setEnabled(false); // filter sites
+
+		}	
+
+	    } else if(actionCommand.equals("paguide")) {
+
+		pa.setNodeNumber(-1);
+		PwSubstitutionMatrix psm = new PwSubstitutionMatrix();
+		String pwAlphabet;
+		int[][] pwSubst;
+		int gOpen, gExt;
+
+		if(ProAlign.isDna){
+		    pwAlphabet = psm.dnaAlphabet;
+		    pwSubst = psm.swdna;
+		    gOpen = -1*pa.pwDnaOpen;
+		    gExt = -1*pa.pwDnaExt;
+
+		} else {
+
+		    pwAlphabet = psm.protAlphabet;
+		    if(pa.pwProtMatrix.equals("pam60")) {
+			pwSubst = psm.pam60;
+		    } else if(pa.pwProtMatrix.equals("pam160")) {
+			pwSubst = psm.pam160;
+		    } else if(pa.pwProtMatrix.equals("pam250")) {
+			pwSubst = psm.pam250;
+		    } else {
+			pwSubst = psm.pam120;
+		    }
+		    gOpen = -1*pa.pwProtOpen;
+		    gExt = -1*pa.pwProtExt;
+		}
+
+		PwAlignment pa = 
+		    new PwAlignment(pwSubst,gOpen,gExt,pwAlphabet,ProAlign.isDna);
+		PwAlignmentLoop pal = new PwAlignmentLoop(ResultWindow.this,pa);
+		rw.messageText.setText(" Creating ProAlign guide tree.");
+
+		rw.file[1].setEnabled(false); // open alignment
+		rw.file[2].setEnabled(false); // save alignment
+		rw.dm.setEnabled(false); // import
+		rw.align[0].setEnabled(false); // do guide tree
+		rw.align[1].setEnabled(false); // do paguide tree
+		rw.align[2].setEnabled(false); // do multiple
+		rw.align[3].setEnabled(false); // sample
+		rw.align[4].setEnabled(false); // remove gaps
+
+	    } else if(actionCommand.equals("multiple")) {
+
+		RunMultiple rm = new RunMultiple(ResultWindow.this);
+		rw.messageText.setText(" Creating multiple alignment.");
+
+		rw.file[1].setEnabled(false); // open alignment
+		rw.file[2].setEnabled(false); // save alignment
+		rw.dm.setEnabled(false); // import
+		rw.align[0].setEnabled(false); // do guide tree  
+		rw.align[1].setEnabled(false); // do paguide tree
+		rw.align[2].setEnabled(false); // do multiple
+		rw.align[3].setEnabled(false); // sample
+		rw.align[4].setEnabled(false); // remove gaps
+
+	    } else if(actionCommand.equals("sample")) {
+
+		SampleAlignments sa = new SampleAlignments(ResultWindow.this);
+		sa.setSize(sa.width,sa.height);
+		sa.setVisible(true);
+
+	    } else if(actionCommand.equals("remove")) {
+
+		rw.removeGaps(rw.root.tree);
+		rw.setScrollPanes();
+
+	    } else if(actionCommand.equals("setting")) {
+
+		SetParameters sp = new SetParameters(ResultWindow.this);
+		sp.setSize(sp.width,sp.height);
+		sp.setVisible(true);
+
+	    }
+	}
+    }  
+
+    // ActionListener for 'Probability' menu
+    //
+    class ProbabilityMenuListener implements ActionListener {
+	public void actionPerformed(ActionEvent e) {
+	    
+	    JMenuItem target = (JMenuItem)e.getSource();
+	    String actionCommand = target.getActionCommand();
+	    double limit = -1d;
+
+	    if(actionCommand.equals("plot")) {
+		//
+		double[] minPost = new double[root.cellPath.length];
+		String[] minNode = new String[root.cellPath.length];
+		for(int i=0; i<root.cellPath.length; i++) {
+ 		    minPost[i] = root.getMinimumInternalPostProbAt(i);
+		    minNode[i] = root. getMinimumInternalPostProbNode();
+		}
+		
+		MinimumProbWindow mpw = new MinimumProbWindow(minPost,minNode,rw);
+		mpw.setSize(600,100);
+		mpw.setLocation(220,320);		
+		mpw.setVisible(true);
+
+	    } else if(actionCommand.equals("90percent")) {
+		limit = 0.9d;
+	    } else if(actionCommand.equals("80percent")) {
+		limit = 0.8d;
+	    } else if(actionCommand.equals("70percent")) {
+		limit = 0.7d;
+	    } else if(actionCommand.equals("60percent")) {
+		limit = 0.6d;
+	    } else if(actionCommand.equals("50percent")) {
+		limit = 0.5d;
+	    } else if(actionCommand.equals("40percent")) {
+		limit = 0.4d;
+	    } else if(actionCommand.equals("30percent")) {
+		limit = 0.3d;
+	    } else if(actionCommand.equals("20percent")) {
+		limit = 0.2d;
+	    } else if(actionCommand.equals("10percent")) {
+		limit = 0.1d;
+	    } else if(actionCommand.equals("0percent")) {
+		limit = 0d;
+	    }
+
+	    if(limit >= 0d) {	
+
+		boolean[] sitesRemoved = new boolean[root.cellPath.length];
+		int count = 0;
+		for(int i=0; i<root.cellPath.length; i++) {
+ 		    if(Math.exp(root.getMinimumInternalPostProbAt(i)) < limit) {
+			sitesRemoved[i] = true;
+			count++;
+		    }
+		}
+		rw.seqcont.sitesRemoved = sitesRemoved;
+		rw.messageText.setText(
+		    " Filter: "+count+" of "+root.cellPath.length+" sites have posterior "+
+		    "probability lower than "+(int)(limit*100)+" percent.");
+
+	    }
+	}
+    }
+
+    // ActionListener for 'Probability' menu
+    //
+    class FontMenuListener implements ActionListener {
+	public void actionPerformed(ActionEvent e) {
+	    JMenuItem target = (JMenuItem)e.getSource();
+	    String actionCommand = target.getActionCommand();
+	    // System.out.println(actionCommand+" "+target.getText()+"");
+
+	    if(actionCommand.equals("size")) {
+		rwFontSize = new Integer(target.getText()).intValue();
+	    } else if(actionCommand.equals("hor")) {
+		horizontalCharSpace = new Integer(target.getText()).intValue();
+	    } else if(actionCommand.equals("ver")) {
+		verticalCharSpace = new Integer(target.getText()).intValue();
+	    }
+
+	    FontMetrics currentMetrics = 
+		rw.getFontMetrics(new Font("monospaced", Font.BOLD, ResultWindow.rwFontSize));
+	    int columnWidth = currentMetrics.charWidth(' ')+horizontalCharSpace;
+	    rw.charRule.setIncrement(columnWidth,horizontalCharSpace);
+	    rw.charRule.setPreferredWidth(rw.seqcont.maxLength*columnWidth);
+	}
+    }
+
+    class MiceListener extends MouseMotionAdapter {
+	public void mouseMoved(MouseEvent e) {
+	    if(System.getProperty("os.name").startsWith("Mac")){
+		rw.macUpdate();
+	    }
+	}
+    }
+} 
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Rule.java b/Rule.java
new file mode 100644
index 0000000..4867025
--- /dev/null
+++ b/Rule.java
@@ -0,0 +1,117 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.awt.Toolkit;
+import java.awt.Graphics;
+import java.awt.Dimension;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Rectangle;
+import javax.swing.JComponent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseListener;
+
+public class Rule extends JComponent {
+    int rulerHeight = 15;
+    
+    private int increment;
+    private int startPosition;
+    private int units;
+
+    Rule rule;
+    ResultWindow parent;
+    public Rule(ResultWindow rw) { 
+	
+	rule = this;
+	parent = rw; 
+	
+	addMouseListener(new MiceListener());
+    }
+
+    public void setIncrement(int columnWidth, int startPosition) {
+	increment = columnWidth;
+	this.startPosition = startPosition;
+	units = 10*columnWidth;
+    }
+
+    public void setPreferredWidth(int pw) {
+	setPreferredSize(new Dimension(pw,rulerHeight));
+    }
+
+    public void paintComponent(Graphics g) {
+	Rectangle drawHere = g.getClipBounds();
+	
+	// Fill clipping area 
+	g.setColor(Color.white);
+	g.fillRect(drawHere.x, drawHere.y, drawHere.width, drawHere.height);
+
+	// Do the ruler labels in a small font 
+	g.setFont(new Font(ProAlign.paFontName, Font.PLAIN, ProAlign.paFontSize-2)); 
+	g.setColor(Color.black);
+	
+	// Some vars we need.
+	int tickLength = 0;
+	String text = null;
+	
+	// Use clipping bounds to calculate first tick and last tick location.
+	int start = (drawHere.x / increment) * increment;
+	int end = (((drawHere.x + drawHere.width) / increment) + 1) * increment;
+	
+	// Make a special case of 0 to display the number
+	// within the rule and draw a units label.
+	if (start == 0) {
+	    text = Integer.toString(0);
+	    tickLength = 3;
+	    g.drawLine(0, rulerHeight-1, 0, rulerHeight-tickLength-1);
+	    start = increment;
+	}
+	
+	// ticks and labels
+	for (int i = start; i < end; i += increment) {
+	    if (i % units == 0)  {
+		tickLength = 3;
+		text = Integer.toString(i/increment);
+	    } else {
+		tickLength = 1;
+		text = null;
+	    }
+	    
+	    if (tickLength != 0) { 
+		g.drawLine(i, rulerHeight-1, i, rulerHeight-tickLength-1);
+		if (text != null)
+		    g.drawString(text, i-10, 10);
+	    }
+	}
+    }
+    // A click on a site will include/exclude that character.
+    //
+    class MiceListener extends MouseAdapter {
+	public void mouseClicked(MouseEvent e) {
+	    int x = e.getX();
+	    int y = e.getY();
+	    if(e.isShiftDown()) {
+		parent.seqcont.updateStableSites(x,y,true);
+	    } else {
+		parent.seqcont.updateStableSites(x,y,false);
+	    }	
+	}
+    } 
+
+
+}
+
+
+
+
+
+
diff --git a/RunClustalw.java b/RunClustalw.java
new file mode 100644
index 0000000..d664d09
--- /dev/null
+++ b/RunClustalw.java
@@ -0,0 +1,186 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.io.DataInputStream;
+import java.io.BufferedInputStream;
+
+/**
+ * Running native ClustalW.
+ */
+public class RunClustalw extends Thread {
+
+
+    String tempFolder;
+    String clustalwPath;
+    String cmdParameter;
+
+    ResultWindow rw;
+
+    Process process;
+    DataInputStream clustalwOut;
+
+     /**
+     * Argument list describing the ClustalW loop.
+     */
+    RunClustalw(ResultWindow rw) {
+	
+	this.rw = rw;
+
+	ProAlign.log("RunClustalw");
+
+	clustalwPath = ProAlign.clustalwPath;
+	tempFolder = ProAlign.tempFolder+File.separator;
+
+	if(System.getProperty("os.name").startsWith("Windows")){
+	    File oldFile = new File(tempFolder+"PROALIGN.TRE");
+	    if(oldFile.exists()) {
+		try {
+		    oldFile.delete();
+		} catch (Exception ie) {
+		    ProAlign.log.println(" can not delete old guide tree-file!");
+		}
+	    }
+	} else {
+	    File oldFile = new File(tempFolder+"proalign.tre");
+	    if(oldFile.exists()) {
+		try {
+		    oldFile.delete();
+		} catch (Exception ie) { 
+		    ProAlign.log.println(" can not delete old guide tree-file!");
+		}
+	    }
+	}
+
+	if(System.getProperty("os.name").startsWith("Windows")){
+	    cmdParameter = "clustalw /tree /newtree="+tempFolder+"PROALIGN.TRE /infile="+
+		tempFolder+"proalign.seq";
+	} else {
+	    cmdParameter = " -tree -newtree="+tempFolder+"proalign.tre -infile="+
+		tempFolder+"proalign.seq";
+	}
+	
+	start();
+    }
+
+    public void run(){
+	/** 
+	 *  Clustalw - make guide tree only!
+	 */
+	if(System.getProperty("os.name").startsWith("Windows")) {
+
+
+	    int narg = numArg(cmdParameter,"/");
+	    try {
+		WinClustalw wc = new WinClustalw(narg,cmdParameter);
+		
+		while(wc.running) {
+		    try{
+			Thread.currentThread().sleep(500);
+		    } catch(InterruptedException e) {}
+		}
+	    } catch(Exception ex) {
+		ProAlign.log("WinClustalw error");
+	    }
+
+	} else {
+	    try {
+
+		process = Runtime.getRuntime().exec(clustalwPath+cmdParameter);
+		clustalwOut = 
+		    new DataInputStream(new BufferedInputStream(process.getInputStream()));
+		
+		String str;	
+		while((str = clustalwOut.readLine()) != null) {         
+		    if (str.length() > 0) {
+			ProAlign.log(str);
+		    }
+		}
+	    } catch(IOException e) { }  
+	}
+	if(ProAlign.DEBUG) {
+	    ProAlign.log.flush();
+	}
+	
+	String filepath;
+	if(System.getProperty("os.name").startsWith("Windows")){
+	    filepath = ProAlign.tempFolder+File.separator+"PROALIGN.TRE";
+	} else {
+	    filepath = ProAlign.tempFolder+File.separator+"proalign.tre"; 
+	}
+	
+	File newFile = new File(filepath);
+	if(!newFile.exists()) {  // for some reason ClustalW failed.
+
+	    String text = "\n      ClustalW output not found!\n";
+	    OpenDialog od = new OpenDialog(rw);
+	    od.showDialog("Error!", text);
+	    rw.setRawData(rw.seqs);
+	    rw.setScrollPanes();
+	    
+	} else { // guide tree is fine.
+
+	    TreeReader tr = new TreeReader();
+	    String[] treeNodes = tr.getAllNodes(filepath);
+	    CheckTreeAndData chk = new CheckTreeAndData(treeNodes, rw.seqs);
+
+	    if(chk.nodesAreSame()) {
+		String tree = tr.readFile(filepath);
+		TreeNode tn = new TreeNode(tree);
+		tree = tn.findMiddlePoint();
+
+		rw.setRawDataAndTree(tree);
+		rw.setScrollPanes();
+		
+	    } else {
+
+		String text = new String("\n   Guide tree and sequence \n"+
+					 "    file do not match!\n");
+		OpenDialog od = new OpenDialog(rw);
+		od.showDialog("Warning!", text); 
+
+		ProAlign.log.println("ClustalW: nodes and names do not match!");
+
+	    }
+	}
+    }
+
+    public int numArg(String cmnd,String csep) {
+        int narg = 0;
+        for(int i = 0, j = 0; i < cmnd.length();) {
+            j = cmnd.indexOf(csep, i);
+            if (j >= i){
+                narg++;
+                i = j;
+            }
+            i++;
+        }
+        return narg;
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RunCommandLine.java b/RunCommandLine.java
new file mode 100644
index 0000000..8408a5e
--- /dev/null
+++ b/RunCommandLine.java
@@ -0,0 +1,209 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author       Ari Loytynoja
+ * @version      1.0
+ */
+package proalign;
+
+import java.io.File;
+import java.util.HashMap;
+
+public class RunCommandLine {
+
+    ProAlign pa;
+    PwAlignment pwa;
+    PwAlignmentLoop pwal;
+    AlignmentNode root;
+
+    public RunCommandLine(ProAlign pa) throws Exception {
+
+	ProAlign.log("RunCommandLine");
+
+	this.pa = pa;
+	SequenceReader sr = new SequenceReader();
+
+	if(pa.seqfile != null && new File(pa.seqfile).exists()) {
+	    
+	    // ..and it's a valid file,
+	    if(sr.fromFile(pa.seqfile)) {
+		pa.seqs = sr.getSequences();
+
+		CheckSequence cs = new CheckSequence();
+		if(cs.isDna(pa.seqs)){
+		    pa.sm.jcDnaModel();
+		    ProAlign.isDna = true;
+		} else {
+		    if(ProAlign.protModel.equals("dayhoff")) {
+			pa.sm.dayhoffProteinModel();
+		    } else if(ProAlign.protModel.equals("jtt")) {
+			pa.sm.jttProteinModel();
+		    } else {
+			pa.sm.wagProteinModel();
+		    }
+		    ProAlign.isDna = false;
+		}
+		if(!cs.isFromAlphabet(pa.seqs,pa.sm.equateAlphabet)) {
+		    ProAlign.log.println("Sequence reading error: Illegal characters!");
+		    System.out.println("Sequence reading error: Illegal characters!");
+		    if(ProAlign.exitInError) {
+			System.exit(0);
+		    } else {
+			throw new Exception("Sequence reading error: Illegal characters");
+		    }
+		}
+	    } else {
+		String msg = sr.getErrors();
+		ProAlign.log.println(msg);
+		System.out.println(msg);
+		if(ProAlign.exitInError) {
+		    System.exit(0);
+		} else {
+		    throw new Exception("Sequence reading error");
+		}
+	    }
+	} else {
+	    ProAlign.log.println("Sequence file doesn't exist!");
+	    System.out.println("Sequence file doesn't exist!");
+	    if(ProAlign.exitInError) {
+		System.exit(0);
+	    } else {
+		throw new Exception("Sequence file doesn't exist");
+	    }
+	}
+
+	String tree = new String();
+	TreeReader tr = new TreeReader();
+
+	// do a new tree
+
+	PwSubstitutionMatrix psm = new PwSubstitutionMatrix();
+	String pwAlphabet;
+	int[][] pwSubst;
+	int gOpen, gExt;
+	
+	if(ProAlign.isDna){
+	    pwAlphabet = psm.dnaAlphabet;
+	    pwSubst = psm.swdna;
+	    gOpen = -1*pa.pwDnaOpen;
+	    gExt = -1*pa.pwDnaExt;
+
+	} else {
+	    
+	    pwAlphabet = psm.protAlphabet;
+	    if(pa.pwProtMatrix.equals("pam60")) {
+		pwSubst = psm.pam60;
+	    } else if(pa.pwProtMatrix.equals("pam160")) {
+		pwSubst = psm.pam160;
+	    } else if(pa.pwProtMatrix.equals("pam250")) {
+		pwSubst = psm.pam250;
+	    } else {
+		pwSubst = psm.pam120;
+	    }
+	    gOpen = -1*pa.pwProtOpen;
+	    gExt = -1*pa.pwProtExt;
+	}
+
+	pwa = new PwAlignment(pwSubst,gOpen,gExt,pwAlphabet,ProAlign.isDna);
+
+	if(pa.doTree) {
+
+	    pwal = new PwAlignmentLoop(pwa,pa.seqs);	    
+	    tree = pwal.getTree();
+
+	    if(pa.treefile!=null) {
+		try {
+		    OutFile newtree = new OutFile(pa.treefile);
+		    newtree.println(tree);
+		    newtree.close();
+		} catch(Exception e) { }
+
+	    }
+	    // open an existing tree file
+	} else if(pa.treefile != null && new File(pa.treefile).exists()) {
+
+	    String[] treeNodes = tr.getAllNodes(pa.treefile);
+	    CheckTreeAndData chk = new CheckTreeAndData(treeNodes,pa.seqs);
+	    
+	    // ..and it's a valid file, set the tree.
+	    if(chk.nodesAreSame()) {
+		tree = tr.readFile(pa.treefile);
+		if(tr.isUnRooted) {
+		    TreeNode tn = new TreeNode(tree);
+		    tree = tn.findMiddlePoint();
+		}
+	    } else {
+		ProAlign.log.println("Sequence and tree files don't match!");
+		System.out.println("Sequence and tree files don't match!");
+		if(ProAlign.exitInError) {
+		    System.exit(0);
+		} else {
+		    throw new Exception("Sequence and tree files don't match");
+		}
+	    }
+	} else {
+	    ProAlign.log.println("Tree file doesn't exist!");
+	    System.out.println("Tree file doesn't exist!");
+	    if(ProAlign.exitInError) {
+		System.exit(0);
+	    }else {
+		throw new Exception("Tree file doesn't exist");
+	    }
+	}
+
+	
+	if(tree != null) {
+	    
+	    root = new AlignmentNode(pa,tree,0f);
+
+//
+	    if(ProAlign.estimateParameters) {
+		ParameterEstimates pe = new ParameterEstimates(RunCommandLine.this);
+
+	    }
+//
+
+	    try {
+		root.align();
+	    } catch(TraceBackException tbe) {
+		String msg = tbe.getMessage();
+		ProAlign.log.println(msg);
+		System.out.println(msg);
+		if(ProAlign.exitInError) {
+		    System.exit(0);
+		}else {
+		    throw new Exception(msg);
+		}
+	    } catch(Exception e) {
+//		System.out.println("RunCommandLine: throws an exception");
+		throw e;
+	    }
+
+	    // seems to be fine - save results.
+	    
+	    if(pa.outformat==1) {
+		SaveData sd = new SaveData(pa.outfile,1,root);
+	    } else if(pa.outformat==2){
+		SaveData sd = new SaveData(pa.outfile,2,root);
+	    } else if(pa.outformat==3){
+		SaveData sd = new SaveData(pa.outfile,3,root);	    
+	    } else if(pa.outformat==4){
+		SaveData sd = new SaveData(pa.outfile,4,root);	    
+	    }	   
+
+	} else {
+
+	    ProAlign.log.println("Problems with tree!");
+	    System.out.println("Problems with tree!");
+	    if(ProAlign.exitInError) {
+		System.exit(0);
+	    } else {
+		throw new Exception("Problems with tree");
+	    }
+	}
+    }
+}
diff --git a/RunMultiple.java b/RunMultiple.java
new file mode 100644
index 0000000..8200470
--- /dev/null
+++ b/RunMultiple.java
@@ -0,0 +1,103 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.io.DataInputStream;
+import java.io.BufferedInputStream;
+
+/**
+ * Running alignment in a separate thread
+ */
+public class RunMultiple extends Thread {
+
+    ResultWindow rw;
+
+    RunMultiple(ResultWindow rw) {
+	
+	this.rw = rw;
+
+	ProAlign.log("RunMultiple");
+
+	start();
+    }
+
+    public void run() {
+
+	boolean isError = false;
+	String errorMsg = new String();
+
+	try {
+	    rw.root.align();
+	} catch(TraceBackException tbe) { 
+	    isError = true;
+	    errorMsg = tbe.getMessage();
+	} catch(Exception e) { }
+
+	if(isError) {
+	    
+	    OpenDialog od = new OpenDialog(rw);
+	    od.showDialog("Error!", "\n  "+errorMsg+"\n  Try to increase the band width.\n");
+
+	    rw.file[1].setEnabled(true); // open alignment
+	    rw.file[2].setEnabled(false); // save alignment
+	    rw.data[0].setEnabled(true); // import data
+	    rw.data[1].setEnabled(true); // import guide tree
+	    rw.align[0].setEnabled(true); // do guide tree  NOT YET
+	    rw.align[1].setEnabled(true); // do multiple
+	    rw.align[2].setEnabled(true); // do complete  NOT YET
+	    rw.align[3].setEnabled(true); // sample
+	    rw.align[4].setEnabled(false); // remove gaps
+	    
+	    rw.dm.setEnabled(true); // import
+	    rw.em.setEnabled(false); // export 
+
+	} else {
+	    
+	    rw.setAlignedData(rw.root);
+	    rw.setScrollPanes();
+	    
+	    if(rw.root.isBandWarning()) {
+		String text = new String(
+		    "\n  Traceback path came very close\n  to the band edge."+
+		    " Alignment may\n  not be the correct one!");
+		OpenDialog od = new OpenDialog(rw);
+		od.showDialog("Warning!",text);
+	    }
+	    
+	    if(!rw.root.isUnique()) {
+		String text = new String(
+		    "\n  There exist more than one possible\n  alignment. The backtrace "+
+		    "path was\n  chosen "+rw.root.getSampleTimes()+" times randomly between\n"+
+		    "  two equally good alternatives.\n");
+		OpenDialog od = new OpenDialog(rw);
+		od.showDialog("Notice!",text);	 
+	    }
+	}
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SampleAlignments.java b/SampleAlignments.java
new file mode 100644
index 0000000..1a9f435
--- /dev/null
+++ b/SampleAlignments.java
@@ -0,0 +1,446 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JLabel;
+import javax.swing.JButton;
+import javax.swing.JRadioButton;
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBox;
+import javax.swing.JTextField;
+import javax.swing.JTextArea;
+import javax.swing.JScrollPane;
+import javax.swing.JScrollBar;
+import java.awt.GridLayout;
+import java.awt.BorderLayout;
+import java.awt.Font;
+import java.awt.Container;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.ItemListener;
+import java.awt.event.ItemEvent;
+import java.io.File;
+import java.io.IOException;
+
+public class SampleAlignments extends JFrame {
+
+    JTextArea textOutput;
+    JTextField textNumber;
+    JCheckBox boxResult;
+    JCheckBox boxFolder;
+    JCheckBox boxNexus;
+    JCheckBox boxFasta;
+    JScrollPane sp;
+    JScrollBar sb;
+
+    String resultPath = new String("sample_result.csv");
+    String folderPath = new String("");
+    String nexusPath = new String("");
+    String fastaPath = new String("");
+    int numRun;
+
+    boolean writeResults = true;
+    boolean writeAlignments = false;
+    boolean writeNexus = false;
+    boolean writeFasta = false;
+    boolean isRunning = true;
+
+    OutFile resultOut;
+
+    int height = 300;
+    int width = 600;
+
+    ProAlign pa;
+    ResultWindow rw;
+    SampleAlignments sa;
+
+    SampleAlignments(ResultWindow rw) {
+
+	setTitle("Sample alignments");   
+	this.rw = rw;
+	this.pa = rw.pa;
+	sa = this;
+
+	Font font = new Font(ProAlign.paFontName, Font.PLAIN, ProAlign.paFontSize);
+	try {
+	    resultOut = new OutFile(resultPath);
+	} catch(IOException ioe) { }
+
+	JButton buttonResult = new JButton("Select");
+	buttonResult.setFont(font);
+	buttonResult.setActionCommand("result");
+	buttonResult.addActionListener(new ButtonL());
+	JButton buttonFolder = new JButton("Select");
+	buttonFolder.setFont(font);
+	buttonFolder.setActionCommand("folder");
+	buttonFolder.addActionListener(new ButtonL());
+	JButton buttonNexus = new JButton("Select");
+	buttonNexus.setFont(font);
+	buttonNexus.setActionCommand("nexus");
+	buttonNexus.addActionListener(new ButtonL());
+	JButton buttonFasta = new JButton("Select");
+	buttonFasta.setFont(font);
+	buttonFasta.setActionCommand("fasta");
+	buttonFasta.addActionListener(new ButtonL());
+	JButton buttonStart = new JButton("Start");
+	buttonStart.setFont(font);
+	buttonStart.setActionCommand("start");
+	buttonStart.addActionListener(new ButtonL());
+	JButton buttonStop = new JButton("Stop");
+	buttonStop.setFont(font);
+	buttonStop.setActionCommand("stop");
+	buttonStop.addActionListener(new ButtonL());
+	JButton buttonClose = new JButton("Close");
+	buttonClose.setFont(font);
+	buttonClose.setActionCommand("close");
+	buttonClose.addActionListener(new ButtonL());
+
+	JLabel labelHeader = new JLabel("   Sampling parameters");
+	labelHeader.setFont(font);
+	JLabel labelResult = new JLabel("Change file");
+	labelResult.setFont(font);
+	JLabel labelFolder = new JLabel("Change folder");
+	labelFolder.setFont(font);
+	JLabel labelNexus = new JLabel("Change folder");
+	labelNexus.setFont(font);
+	JLabel labelFasta = new JLabel("Change folder");
+	labelFasta.setFont(font);
+	JLabel labelNumber = new JLabel("Permutations");
+	labelNumber.setFont(font);
+	JLabel labelSample = new JLabel("Traceback route");
+	labelSample.setFont(font);
+
+	textNumber = new JTextField("100");
+	textNumber.setFont(font);
+
+	boxResult = new JCheckBox("Write results");
+	boxResult.setActionCommand("result");
+	boxResult.setFont(font);
+	if(writeResults) {
+	    boxResult.setSelected(true);
+	} else {
+	    boxResult.setSelected(false);
+	}
+
+	boxFolder = new JCheckBox("Save ."+ProAlign.fileExt);
+	boxFolder.setActionCommand("folder");
+	boxFolder.setFont(font);
+	boxFolder.setSelected(false);
+
+	boxNexus = new JCheckBox("Save .nex");
+	boxNexus.setActionCommand("nexus");
+	boxNexus.setFont(font);
+	boxNexus.setSelected(false);
+
+	boxFasta = new JCheckBox("Save .pir");
+	boxFasta.setActionCommand("fasta");
+	boxFasta.setFont(font);
+	boxFasta.setSelected(false);
+
+	JRadioButton traceBest = new JRadioButton("select best");
+	traceBest.setFont(font);
+	traceBest.setActionCommand("best");
+	JRadioButton traceSample = new JRadioButton("sample");
+	traceSample.setFont(font);
+	traceSample.setActionCommand("sample");
+	if(ProAlign.trackBest) {
+	    traceBest.setSelected(true);
+	} else { 
+	   traceSample.setSelected(true);
+	}
+
+	CheckBoxListener chboxL = new CheckBoxListener();
+	boxResult.addItemListener(chboxL);
+	boxFolder.addItemListener(chboxL);
+	boxNexus.addItemListener(chboxL);
+	boxFasta.addItemListener(chboxL);
+
+	JPanel fullPanel = new JPanel();
+	fullPanel.setLayout(new GridLayout(1,2,15,15));
+	JPanel halfPanel = new JPanel();
+	halfPanel.setLayout(new GridLayout(10,1,5,5));
+
+	halfPanel.add(labelHeader);
+
+	JPanel pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(new JLabel());
+	pnl.add(labelNumber);
+	pnl.add(textNumber);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(boxResult);
+	pnl.add(labelResult);
+	pnl.add(buttonResult);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(boxFolder);
+	pnl.add(labelFolder);
+	pnl.add(buttonFolder);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(boxNexus);
+	pnl.add(labelNexus);
+	pnl.add(buttonNexus);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(boxFasta);
+	pnl.add(labelFasta);
+	pnl.add(buttonFasta);
+	halfPanel.add(pnl);
+
+	ButtonGroup traceBack = new ButtonGroup();
+        traceBack.add(traceBest);
+        traceBack.add(traceSample);
+
+	RadioListener radioL = new RadioListener();
+        traceBest.addActionListener(radioL);
+        traceSample.addActionListener(radioL);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,2,5,5));
+	pnl.add(labelSample);
+	pnl.add(traceBest);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,2,5,5));
+	pnl.add(new JPanel());
+	pnl.add(traceSample);
+	halfPanel.add(pnl);
+
+	halfPanel.add(new JPanel());
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(buttonStart);
+	pnl.add(buttonStop);
+	pnl.add(buttonClose);
+	halfPanel.add(pnl);
+
+	fullPanel.add(halfPanel);
+
+
+	textOutput = new JTextArea();
+	textOutput.setFont(font);
+
+	sp = new JScrollPane(textOutput,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+			      JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+	sb = sp.getVerticalScrollBar();
+
+	fullPanel.add(sp);
+
+	Container cp = getContentPane();
+	cp.add(fullPanel);
+
+	addWindowListener(new WindowAdapter() {
+		public void windowClosing(WindowEvent e) {
+		    dispose();
+		}
+	    });
+    }
+     
+    public void write(String str) {
+	if (str != "") {
+	    textOutput.append("  " + str + "  \n");
+	    sb.setValue(sb.getMaximum());
+	}
+    }
+
+    class ButtonL implements ActionListener {
+	public void actionPerformed(ActionEvent e) {
+	    
+	    JButton target = (JButton)e.getSource();
+	    String actionCommand = target.getActionCommand();
+	    
+	    if(actionCommand.equals("result")) {
+		
+		OpenFileChooser opf = 
+		    new OpenFileChooser(SampleAlignments.this,"Save",false);
+		String filepath = opf.openFile();
+	 	if(!filepath.equals("")) {
+		    resultOut.close();
+		    try {
+			resultOut = new OutFile(filepath);
+		    } catch(IOException ioe) { }
+		    sa.write("result file is: "+filepath);
+		    resultPath = filepath;
+		    UserSettings user = new UserSettings(); 
+		    String[] userdata = user.readSettings();
+		    userdata[0] = new File(filepath).getParent();
+                    user.writeSettings(userdata);
+		}
+		
+	    } else if(actionCommand.equals("folder")) {
+
+		OpenFolderChooser opf = 
+		    new OpenFolderChooser(SampleAlignments.this,"Select");
+		String filepath = opf.openFile();
+	 	if(!filepath.equals("")) {
+		    folderPath = filepath;
+		    sa.write("alignment folder is: "+new File(folderPath).getAbsolutePath());
+		    UserSettings user = new UserSettings(); 
+		    String[] userdata = user.readSettings();
+		    userdata[0] = new File(filepath).getParent();
+                    user.writeSettings(userdata);
+		}
+		
+	    } else if(actionCommand.equals("nexus")) {
+
+		OpenFolderChooser opf = 
+		    new OpenFolderChooser(SampleAlignments.this,"Select");
+		String filepath = opf.openFile();
+	 	if(!filepath.equals("")) {
+		    nexusPath = filepath;
+		    sa.write("nexus folder is: "+new File(nexusPath).getAbsolutePath());
+		    UserSettings user = new UserSettings(); 
+		    String[] userdata = user.readSettings();
+		    userdata[0] = new File(filepath).getParent();
+                    user.writeSettings(userdata);
+		}
+
+	    } else if(actionCommand.equals("fasta")) {
+
+		OpenFolderChooser opf = 
+		    new OpenFolderChooser(SampleAlignments.this,"Select");
+		String filepath = opf.openFile();
+	 	if(!filepath.equals("")) {
+		    fastaPath = filepath;
+		    sa.write("fasta folder is: "+new File(fastaPath).getAbsolutePath());
+		    UserSettings user = new UserSettings(); 
+		    String[] userdata = user.readSettings();
+		    userdata[0] = new File(filepath).getParent();
+                    user.writeSettings(userdata);
+		}
+
+	    } else if(actionCommand.equals("start")) {
+
+		while(true) {
+		    try{
+			int maxRun = new Integer(textNumber.getText()).intValue();
+			if(maxRun>0) {
+			    numRun = maxRun;
+			} else {
+			    String text = new String("\n   Illegal value!  \n");
+			    OpenDialog od = new OpenDialog(SampleAlignments.this);
+			    od.showDialog("Error!", text); 
+			    break;
+			}
+			if(ProAlign.DEBUG) {
+			    ProAlign.log("SampleAlignments");
+			    ProAlign.log("resultPath="+resultPath+","+writeResults);
+			    ProAlign.log("folderPath="+folderPath+","+writeAlignments);
+			    ProAlign.log("nexusPath="+nexusPath+","+writeNexus);
+			    ProAlign.log("fastaPath="+fastaPath+","+writeFasta);
+			    ProAlign.log("numRun="+numRun);
+			    ProAlign.log("trackBest="+ProAlign.trackBest);
+			    ProAlign.log.flush();
+			}
+		    } catch(NumberFormatException nfe) {
+			String text = new String("\n   Illegal value!  \n");
+			OpenDialog od = new OpenDialog(SampleAlignments.this);
+			od.showDialog("Error!", text); 
+			break;
+		    }
+		    
+		    SampleLoop sl = new SampleLoop(SampleAlignments.this);
+		    break;
+		}
+
+	    } else if(actionCommand.equals("stop")) {
+
+		isRunning = false;
+
+	    } else if(actionCommand.equals("close")) {
+
+		dispatchEvent(new WindowEvent(SampleAlignments.this,
+					      WindowEvent.WINDOW_CLOSING));	    
+	    }
+	}
+    }
+
+
+    class CheckBoxListener implements ItemListener {
+        public void itemStateChanged(ItemEvent e) {
+	    
+	    JCheckBox target = (JCheckBox)e.getItem();
+	    String actionCommand = target.getActionCommand();
+
+	    if(actionCommand.equals("result")) {
+
+		if (e.getStateChange() == ItemEvent.DESELECTED) {
+		    writeResults = false;
+		    sa.write("writing results stopped!");
+		} else {
+		    writeResults = true;
+		    sa.write("writing results started.");
+		}
+		
+	    } else if(actionCommand.equals("folder")) {
+
+		if (e.getStateChange() == ItemEvent.DESELECTED) {
+		    writeAlignments = false;
+		    sa.write("sampled alignments are not saved!");
+		} else {
+		    writeAlignments = true;
+		    sa.write("all sampled alignments are saved.");
+		}
+
+	    } else if(actionCommand.equals("nexus")) {
+		
+		if (e.getStateChange() == ItemEvent.DESELECTED) {
+		    writeNexus = false;
+		    sa.write("nexus files are not saved!");
+		} else {
+		    writeNexus = true;
+		    sa.write("alignments are saved as nexus files.");
+		}
+
+	    } else if(actionCommand.equals("fasta")) {
+		
+		if (e.getStateChange() == ItemEvent.DESELECTED) {
+		    writeFasta = false;
+		    sa.write("fasta files are not saved!");
+		} else {
+		    writeFasta = true;
+		    sa.write("alignments are saved as fasta files.");
+		}
+	    }
+	}
+    }
+
+    class RadioListener implements ActionListener {
+        public void actionPerformed(ActionEvent e) {
+	    String actionCommand = e.getActionCommand();
+
+	    if(actionCommand.equals("sample")) {
+		
+		ProAlign.trackBest = false;
+		
+	    } else if(actionCommand.equals("best")) {
+
+		ProAlign.trackBest = true;
+	    }
+	}
+    }
+}
diff --git a/SampleLoop.java b/SampleLoop.java
new file mode 100644
index 0000000..adb007c
--- /dev/null
+++ b/SampleLoop.java
@@ -0,0 +1,146 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InvalidClassException;
+import java.io.FileOutputStream;
+import java.io.ObjectOutputStream;
+
+class SampleLoop extends Thread {
+
+    SampleAlignments sl;
+    AlignmentNode best;
+    AlignmentNode current;
+    
+    double bestEnd = 0d;
+
+    int currentRun = 0;
+    int maxRun;
+
+    SampleLoop(SampleAlignments sl) {
+
+	this.sl = sl;
+	current = sl.rw.root;
+	maxRun = sl.numRun;
+
+	ProAlign.log("SampleLoop");
+
+	sl.write("\n Sampling "+maxRun+" alignments: ");
+	if(sl.writeResults) {
+	    String[] names = current.getInternalNames();
+	    String str = "number,length,total";
+	    for(int i=0; i<names.length; i++) {
+		str += ","+names[i];
+	    }
+	    sl.resultOut.println(str);
+	    sl.resultOut.flush();
+	}
+
+	start();
+
+    }
+
+    public void run(){
+	
+	while(sl.isRunning && currentRun < maxRun) {
+
+	    boolean isError = false;
+	    String errorMsg = new String();
+	    try {
+		current.align();
+	    } catch(TraceBackException tbe) {
+		isError = true;
+		errorMsg = tbe.getMessage();
+	    } catch(Exception e) { }
+
+	    // Threw an exception during the alignment
+	    if(isError) {
+		
+		OpenDialog od = new OpenDialog(sl);
+		od.showDialog("Error!", "\n  "+errorMsg+"\n  Try to increase the band width.\n");
+		break;
+
+	    } else {
+		
+		if(current.isBandWarning()) {
+		    sl.write("Traceback path came very close\n  to the band edge."+
+			     " Alignment may\n  not be the correct one!");
+		}
+		
+	    }
+
+	    if(currentRun==0) {
+		bestEnd = current.sumViterbiEnd();
+		sl.rw.root = current;
+		sl.rw.setAlignedData(sl.rw.root);
+		sl.rw.setScrollPanes();
+		sl.rw.messageText.setText(" Sample "+(currentRun+1)+"/"+maxRun+": "+current.sumViterbiEnd());
+
+	    } else if (current.sumViterbiEnd() > bestEnd) {
+		bestEnd = current.sumViterbiEnd();
+		sl.rw.root = current;
+		sl.rw.setAlignedData(sl.rw.root);
+		sl.rw.setScrollPanes();
+		sl.rw.messageText.setText(" Sample "+(currentRun+1)+"/"+maxRun+": "+current.sumViterbiEnd());
+	    }
+	    if(sl.writeAlignments) {
+
+		String path = sl.folderPath+File.separator+(currentRun+1)+"."+ProAlign.fileExt;
+		try {
+		    ObjectOutputStream os =
+			new ObjectOutputStream(new FileOutputStream(path));
+		    os.writeObject((AlignmentNode) current);
+		    os.flush();
+		    os.close();
+		} catch(IOException ioe) {
+		    ProAlign.log.println("Sample exception1: "+path+"\n"+ioe.toString());
+		    ProAlign.log.flush();
+		    //ioe.printStackTrace();
+		}
+		catch(Exception oe) {
+		    ProAlign.log.println("Sample exception2: "+path+"\n"+oe.toString());
+		    ProAlign.log.flush();
+		    //oe.printStackTrace();
+		}
+	    }
+
+	    if(sl.writeNexus) {
+		String path = sl.nexusPath+File.separator+(currentRun+1)+".nex";
+		SaveData sd = new SaveData(path,1,current);
+	    }
+	    if(sl.writeFasta) {
+		String path = sl.fastaPath+File.separator+(currentRun+1)+".pir";
+		SaveData sd = new SaveData(path,2,current);
+	    }
+
+	    sl.write("Sample "+(currentRun+1)+"/"+maxRun+": "+current.sumViterbiEnd()+"; best: "+bestEnd);
+
+	    if(sl.writeResults) {
+
+		double[] vit = current.getInternalViterbiEnd();
+		String str = (currentRun+1)+","+current.cellPath.length+","+current.sumViterbiEnd();
+		for(int i=0; i<vit.length; i++) {
+		    str += ","+vit[i];
+		}
+		sl.resultOut.println(str);
+		sl.resultOut.flush();
+	    }
+	    if(ProAlign.DEBUG) {
+		ProAlign.log.println(" sample number "+currentRun);
+		ProAlign.log.flush();
+	    }
+
+	    currentRun++;
+	}
+    }
+}
diff --git a/SaveData.java b/SaveData.java
new file mode 100644
index 0000000..22ee7c8
--- /dev/null
+++ b/SaveData.java
@@ -0,0 +1,854 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JOptionPane;
+import java.io.File;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+
+public class SaveData {
+
+    boolean[] sitesRemoved;
+    boolean[] taxaRemoved;
+    
+    String filename;  
+    String resultString;
+    
+    String[] sequenceNames;
+    String[] sequenceData;
+
+    String spaceStr = "                         ";
+    int taxaNum;
+    int seqLength;
+    
+    int numTaxa;
+    int numSite;
+
+    AlignmentNode root;
+
+    double[] minProb;
+
+//
+    public SaveData(String file, int type, AlignmentNode root) {
+	
+	ProAlign.log("SaveData");
+
+	this.root = root;
+	resultString = "vitEnd: "+root.sumViterbiEnd()+", fwdEnd: "+root.sumForwardEnd();
+
+	sequenceNames = root.getTerminalNames();
+	for(int i=0; i<sequenceNames.length; i++) {
+	    sequenceNames[i] = (sequenceNames[i]+spaceStr).substring(0,21);
+	}
+
+	sequenceData = new String[root.getNumChild()];
+	for(int i=0; i<sequenceData.length; i++) { sequenceData[i] = ""; }
+	for(int i=0; i<root.cellPath.length; i++) {
+	    
+	    int h = 0;
+	    char[] c0 = root.child[0].getCharacterAt(root.cellPath[i][0]-2);
+	    char[] c1 = root.child[1].getCharacterAt(root.cellPath[i][1]-2);
+	    
+	    for(int j=0; j<c0.length; j++) {
+		sequenceData[h++] += c0[j];
+	    }
+	    for(int j=0; j<c1.length; j++) {
+		sequenceData[h++] += c1[j];
+	    }	
+	} 
+
+	sitesRemoved = new boolean[sequenceData[0].length()];
+	taxaRemoved = new boolean[sequenceNames.length];
+	
+	taxaNum = sequenceNames.length;
+	seqLength = sequenceData[0].length();
+	
+	minProb = new double[root.cellPath.length];
+	for(int i=0; i<root.cellPath.length; i++) {
+	    minProb[i] = Math.exp(root.getMinimumInternalPostProbAt(i));
+	}
+
+	if(type==1) {		
+	    outputNexus(file,1);
+	} else if(type==2) {
+	    outputFasta(file);
+	} else if(type==3) {
+	    outputPhylip(file);
+	} else if(type==4) {
+	    outputMsf(file);
+	}
+
+	if(ProAlign.writeRoot) {
+	    writeRoot(file);
+	}
+    }
+
+//
+    
+    public SaveData(String type, ResultWindow rw) {
+	
+	ProAlign.log("SaveData");
+	
+	this.root = rw.root;
+	resultString = "vitEnd: "+rw.root.sumViterbiEnd()+", fwdEnd: "+rw.root.sumForwardEnd();
+
+	sitesRemoved = rw.seqcont.getRemovedSites();
+	taxaRemoved = rw.seqname.getRemovedTaxa();
+	
+	sequenceData =  rw.seqcont.textArray;
+	sequenceNames = rw.seqname.textArray;
+
+	taxaNum = sequenceNames.length;
+	seqLength = sequenceData[0].length();
+	
+	minProb = new double[rw.root.cellPath.length];
+	for(int i=0; i<rw.root.cellPath.length; i++) {
+	    minProb[i] = Math.exp(rw.root.getMinimumInternalPostProbAt(i));
+	}
+		
+
+	OpenFileChooser opf = 
+	    new OpenFileChooser(rw,"Save "+type,false);
+	String filepath = opf.openFile();
+	if(!filepath.equals("")) {
+
+	    UserSettings user = new UserSettings(); 
+	    String[] userdata = user.readSettings();
+	    ProAlign.folderPath = new File(filepath).getParent();
+	    userdata[0] = new File(filepath).getParent();
+	    user.writeSettings(userdata);
+
+	    if(type.equals("Nexus")) {
+		Object[] options = {"interleaved",
+				    "sequential"};
+		int n = JOptionPane.showOptionDialog(rw,"Do you want your alignment "+
+						     "interleaved or sequential?","Nexus format",
+						     JOptionPane.YES_NO_OPTION,
+						     JOptionPane.PLAIN_MESSAGE,
+						     null,options,options[0]);
+		if(n==JOptionPane.YES_OPTION) {
+		    outputNexus(filepath,1);
+		} else if(n==JOptionPane.NO_OPTION) {
+		    outputNexus(filepath,2);
+		}
+	    } else if(type.equals("Fasta")) {
+		outputFasta(filepath);	
+	    } else if(type.equals("Phylip")) {
+		outputPhylip(filepath);	
+	    } else if(type.equals("MSF")) {
+		outputMsf(filepath);	
+	    }
+
+	    if(ProAlign.writeRoot) {
+		writeRoot(filepath);
+	    }
+
+	}
+    }
+    
+
+    // Write data in nexus format.
+    //
+    public void outputNexus(String file,int type) {
+	
+	try {
+	    
+	  OutFile out = new OutFile(file);
+	  
+	  // Write the intro.
+	  out.println("#NEXUS");
+	  String date = Calendar.getInstance().getTime().toString();
+	  out.println("[ ProAlign - "+date+" ]\n");
+	  out.println("Begin data;");
+	  out.println("     Dimensions ntax=" + taxaNum + 
+		      " nchar=" + seqLength + ";");
+	  if(ProAlign.isDna) {
+	      out.print("     Format datatype=nucleotide");  
+	  } else {
+	      out.print("     Format datatype=protein");  
+	  }
+	  if(type == 1)
+	      out.print(" interleave");
+	  out.println(" missing=-;");
+	  
+	  // Write the matrix.
+	  out.println("     Matrix");
+	  
+	  int k = 0, j = 0, max = 0;
+	  
+	  if (type == 1) { // nexus interleaved
+	      
+	      while(true) {
+		  
+		  for(int i = 0; i < taxaNum; ++i) {
+		      j = k;
+		      String str = "";
+		      str = ((sequenceNames[i]+spaceStr).substring(0,21) + "  ");
+		      int count = 0;
+		      for(int m = 0; m < 100; m++) {
+			  if (j >= (seqLength)) {
+			      break;
+			  }
+			  str += (""+sequenceData[i].charAt(j++));
+			  if(m > 0)
+			      if((m+1) % 20 == 0) {
+				  str += (" ");
+			      }
+		      }
+		      out.println(str);
+		  }
+		  out.println("");
+		  if(j >= (seqLength)) {
+		      break;
+		  }
+		  k += 100;
+	      }
+	      
+	  } else { // nexus sequential
+	      for(int i = 0; i < taxaNum; ++i) {
+		  out.print((sequenceNames[i]+spaceStr).substring(0,21) + "  ");
+		  out.println(sequenceData[i]);
+	      }
+	  }
+	  
+	  out.println("     ;\nEnd;\n");
+	  
+	  // Write the paup block.
+	  out.println("Begin paup;");
+	  
+	  // Delete sequnces
+	  String str = "     Delete";
+	  for (int i = 0; i < taxaNum; i++) {
+	      if(taxaRemoved[i]) {
+		  str += (" "+(sequenceNames[i]+spaceStr).substring(0,21).trim());
+	      }
+	  }
+	  str += ";";
+	  out.println(str);
+	  
+	  // Exclude sites
+	  boolean exc = false;
+	  int prev = 0; int start = 0;
+	  
+	  str = "     Exclude";
+	  for (int n = 0; n < sitesRemoved.length; n++) {
+	      if(sitesRemoved[n]) {
+		  prev = n;
+		  if(!exc) {
+		      // array starts form 0, Nexus matrix from 1!
+		      str += (" " + (n+1));
+		      start = n;
+		      exc = true;
+		  }
+	      } else {
+		  if(exc) {
+		      if (start != prev)
+			  str += ("-" + (prev+1));
+		      exc = false;
+		  }
+	      }
+	  }
+	  if(exc)
+	      if (start != prev)
+		  str += ("-" + (prev+1));  
+	  str += ";";
+	  out.println(str);
+	  
+	  out.println("End;\n");
+	  
+	  // Write the assumptions block.
+	  out.println("Begin assumptions;");
+	  str = "     wtset ProAlignWeights (VECTOR) = \n       ";
+	  for (int i = 0; i < minProb.length; i++) {
+	      str += (roundDoubleToString((double)minProb[i],3)+"     ").substring(0,5)+" ";
+	      if(type==1 && i>0 && (i+1)%15==0)
+		  str += "\n       ";
+	  }
+	  str += ";";
+	  out.println(str);
+	  
+	  out.println("End;\n");
+	  
+	  out.close();
+	} catch (IOException e) { }
+    }
+    
+    
+
+    // Write data in fasta format.
+    //
+    public void outputFasta(String file) {
+	
+	try {
+	    OutFile out = new OutFile(file);
+	    
+	    for(int i = 0; i < taxaNum; i++) {
+		if(!taxaRemoved[i]) {
+		    if(ProAlign.isDna) {
+			out.println(">DL;" + sequenceNames[i].trim()+"\n");
+		    } else {
+			out.println(">P1;" + sequenceNames[i].trim()+"\n");
+		    }
+		    int count = 0;
+		    for(int j = 0; j < seqLength; j++) {
+			if(!sitesRemoved[j]) {
+			    count++;
+			    out.print(sequenceData[i].charAt(j));
+			    if(count == 50) {
+				out.println("");
+				count = 0;
+			    }
+			}
+		    }
+		    out.println("*");
+		}
+	    }
+	    
+	    out.close();
+	} catch (IOException e) {
+	}
+
+	if(ProAlign.writeMin) {
+	    outputWeights(file+".min", false);
+	}
+	if(ProAlign.writeMean) {
+	    outputMean(file+".mean", false);
+	}
+	if(ProAlign.writeAll) {
+	    outputAll(file+".all", false);
+	}
+    }
+    
+    
+    //
+    // Write data table in given file in phylip int. format.
+    //
+    public void outputPhylip(String file) {
+	    
+	numTaxa=0;
+	for(int i = 0; i < taxaNum; i++) {
+	    if(!taxaRemoved[i]) {
+		numTaxa++;
+	    }
+	}
+	numSite=0;
+	for(int j = 0; j < seqLength; j++) {
+	    if(!sitesRemoved[j]) {
+		numSite++;
+	    }
+	}
+
+	try {
+	    OutFile out = new OutFile(file);
+
+	    int k = 0;
+	    int j = 0;
+	    int max = 0;
+	    boolean newline = true;
+
+	    out.println(" "+numTaxa+" "+numSite);
+	    
+	    while(true) {
+		for(int i = 0; i < taxaNum; i++) {
+		    newline = true;
+		    j = k;
+		    if(!taxaRemoved[i]) {
+			if(k == 0)
+			    out.print((sequenceNames[i].trim()+
+				       spaceStr).substring(0,10) + "   ");
+			else
+			    out.print("             ");
+			
+			int count = 0;
+			while(count < 50) {
+			    if (j >= seqLength) {
+				max = j;
+				break;
+			    }
+			    
+			    else if(!sitesRemoved[j]) {
+				out.print(sequenceData[i].charAt(j));
+				count++;
+				if(count % 50 == 0) {
+				    out.print("");
+				} else if(count % 10 == 0)
+				    out.print(" ");
+			    }
+			    j++;
+			}
+			max = j;
+			out.println("");
+		    } else {
+			newline = false;
+		    }
+		}
+		if (newline)
+		    //out.println("");
+		if(max >= seqLength) {
+		    break;
+		}
+		k = max;
+		out.println("");
+	    }
+	    
+	    out.close();
+	} catch (IOException e) {
+	}
+
+	if(ProAlign.writeMin) {
+	    outputWeights(file+".min", false);
+	}
+	if(ProAlign.writeMean) {
+	    outputMean(file+".mean", false);
+	}
+	if(ProAlign.writeAll) {
+	    outputAll(file+".all", false);
+	}
+    }
+
+//
+
+    //
+    // Write data table in given file in phylip int. format.
+    //
+    public void outputMsf(String file) {
+	    
+	numSite=0;
+	for(int j = 0; j < seqLength; j++) {
+	    if(!sitesRemoved[j]) {
+		numSite++;
+	    }
+	}
+
+	try {
+	    OutFile out = new OutFile(file);
+
+	    int k = 0;
+	    int j = 0;
+	    int max = 0;
+	    boolean newline = true;
+
+	    out.println("PileUp\n");
+	    out.print("   MSF: "+numSite);
+	    if(ProAlign.isDna) {
+		out.println("   Type: N\n");
+	    } else {
+		out.println("   Type: P\n");
+	    }
+	    for(int i = 0; i < taxaNum; i++) {
+		if(!taxaRemoved[i]) {
+		    out.println(" Name: "+(sequenceNames[i].trim()
+					+spaceStr).substring(0,21) + "   ");
+		}
+	    }
+	    out.println("\n//\n");
+
+	    while(true) {
+		for(int i = 0; i < taxaNum; i++) {
+		    newline = true;
+		    j = k;
+		    if(!taxaRemoved[i]) {
+			out.print((sequenceNames[i].trim()
+				   +spaceStr).substring(0,21) + "   ");
+			
+			int count = 0;
+			while(count < 50) {
+			    if (j >= seqLength) {
+				max = j;
+				break;
+			    }
+			    
+			    else if(!sitesRemoved[j]) {
+				out.print(sequenceData[i].charAt(j));
+				count++;
+				if(count % 50 == 0) {
+				    out.print("");
+				} else if(count % 10 == 0)
+				    out.print(" ");
+			    }
+			    j++;
+			}
+			max = j;
+			out.println("");
+		    } else {
+			newline = false;
+		    }
+		}
+		if (newline)
+		    //out.println("");
+		if(max >= seqLength) {
+		    break;
+		}
+		k = max;
+		out.println("");
+	    }
+	    
+	    out.close();
+	} catch (IOException e) {
+	}
+
+	if(ProAlign.writeMin) {
+	    outputWeights(file+".min", false);
+	}
+	if(ProAlign.writeMean) {
+	    outputMean(file+".mean", false);
+	}
+	if(ProAlign.writeAll) {
+	    outputAll(file+".all", false);
+	}
+    }	
+
+//
+
+
+    // Write probabilities in plain text format.
+    // 
+    public void outputWeights(String file, boolean all) {
+	if (all) {
+	    double min=0d;
+	    int nc=0;
+	    for (int i = 0; i < minProb.length; i++) {
+		min+=minProb[i];
+		nc++;
+	    }
+	    min=min/nc;
+
+	    double mean=0d;
+	    nc=0;
+	    for (int i=0; i<seqLength; i++) {
+		double[] column = root.getInternalPostProbAt(i);
+		double sum = 0d;
+		int cn=0;
+		for (int j=0; j<column.length; j++) {
+		    if(column[j]!=Double.NEGATIVE_INFINITY) {
+			sum+=Math.exp(column[j]);
+			cn++;
+		    }
+		}
+		sum=sum/cn;
+		mean+=sum;
+		nc++;
+	    }
+	    mean=mean/nc;
+
+	    try {
+	      OutFile out = new OutFile(file);
+	      out.print("# ProAlign: minimum posterior probability.\n");
+	      out.print("# "+resultString+", averMin: "+min+", averAll: "+mean+".\n");
+	      for (int i = 0; i < minProb.length; i++) {
+		  out.print((roundDoubleToString((double)minProb[i],3)+"     ").substring(0,5)+" ");
+		  if(i>0 && (i+1)%20==0)
+		      out.print("\n");
+	      }
+	      out.print("\n");
+	      out.close();
+	    } catch (IOException e) {
+	    }
+	}else {
+	    double min=0d;
+	    int nc=0;
+	    for (int i = 0; i < minProb.length; i++) {
+		if(!sitesRemoved[i]) {
+		    min+=minProb[i];
+		    nc++;
+		}
+	    }
+	    min=min/nc;
+
+	    double mean=0d;
+	    nc=0;
+	    for (int i=0; i<seqLength; i++) {
+		double[] column = root.getInternalPostProbAt(i);
+		double sum = 0d;
+		int cn=0;
+		for (int j=0; j<column.length; j++) {
+		    if(column[j]!=Double.NEGATIVE_INFINITY) {
+			sum+=Math.exp(column[j]);
+			cn++;
+		    }
+		}
+		sum=sum/cn;
+		mean+=sum;
+		nc++;
+	    }
+	    mean=mean/nc;
+
+	    try {
+		OutFile out = new OutFile(file);
+		out.print("# ProAlign: minimum posterior probability.\n");
+		out.print("# "+resultString+", averMin: "+min+", averAll: "+mean+".\n");
+		int count = 1;
+		for (int i = 0; i < minProb.length; i++) {
+		    if(!sitesRemoved[i]) {
+			out.print((roundDoubleToString((double)minProb[i],3)+"     ").substring(0,5)+" ");
+			if((count%20)==0)
+			    out.print("\n");
+			count++;
+		  }
+		}
+		out.print("\n");
+		out.close();
+	    } catch (IOException e) {
+		e.printStackTrace();
+	    }
+	}
+    }
+
+
+    // Write mean probabilities in plain text format.
+    // 
+    public void outputMean(String file, boolean all) {
+	if (all) {
+	    double min=0d;
+	    int nc=0;
+	    for (int i = 0; i < minProb.length; i++) {
+		if(!sitesRemoved[i]) {
+		    min+=minProb[i];
+		    nc++;
+		}
+	    }
+	    min=min/nc;
+
+	    double mean=0d;
+	    nc=0;
+	    for (int i=0; i<seqLength; i++) {
+		double[] column = root.getInternalPostProbAt(i);
+		double sum = 0d;
+		int cn=0;
+		for (int j=0; j<column.length; j++) {
+		    if(column[j]!=Double.NEGATIVE_INFINITY) {
+			sum+=Math.exp(column[j]);
+			cn++;
+		    }
+		}
+		sum=sum/cn;
+		mean+=sum;
+		nc++;
+	    }
+	    mean=mean/nc;
+
+	    try {
+	      OutFile out = new OutFile(file);
+	      out.print("# ProAlign: mean posterior probability.\n");
+	      out.print("# "+resultString+", averMin: "+min+", averAll: "+mean+".\n");
+	      for (int i=0; i<seqLength; i++) {
+		  double[] column = root.getInternalPostProbAt(i);
+		  double sum = 0d;
+		  int cn=0;
+		  for (int j=0; j<column.length; j++) {
+		      if(column[j]!=Double.NEGATIVE_INFINITY) {
+			  sum+=Math.exp(column[j]);
+			  cn++;
+		      }
+		  }
+		  sum=sum/cn;
+		  out.print((roundDoubleToString((double)sum,3)+"     ").substring(0,5)+" ");
+		  if(i>0 && (i+1)%20==0)
+		      out.print("\n");
+	      }
+	      out.print("\n");
+	      out.close();
+	    } catch (IOException e) {
+	    }
+	}else {
+	    double min=0d;
+	    int nc=0;
+	    for (int i = 0; i < minProb.length; i++) {
+		if(!sitesRemoved[i]) {
+		    min+=minProb[i];
+		    nc++;
+		}
+	    }
+	    min=min/nc;
+
+	    double mean=0d;
+	    nc=0;
+	    for (int i=0; i<seqLength; i++) {
+		if(!sitesRemoved[i]) {
+		    double[] column = root.getInternalPostProbAt(i);
+		    double sum = 0d;
+		    int cn=0;
+		    for (int j=0; j<column.length; j++) {
+			if(column[j]!=Double.NEGATIVE_INFINITY) {
+			    sum+=Math.exp(column[j]);
+			    cn++;
+			}
+		    }
+		    sum=sum/cn;
+		    mean+=sum;
+		    nc++;
+		}
+	    }
+	    mean=mean/nc;
+	    try {
+		OutFile out = new OutFile(file);
+		out.print("# ProAlign: mean posterior probability.\n");
+		out.print("# "+resultString+", averMin: "+min+", averAll: "+mean+".\n");
+		int count = 1;
+		for (int i = 0; i < seqLength; i++) {
+		    if(!sitesRemoved[i]) {
+			double[] column = root.getInternalPostProbAt(i);
+			double sum = 0d;
+			int cn=0;
+			for (int j=0; j<column.length; j++) {
+			    if(column[j]!=Double.NEGATIVE_INFINITY) {
+				sum+=Math.exp(column[j]);
+				cn++;
+			    }
+			}
+			sum=sum/cn;
+			out.print((roundDoubleToString((double)sum,3)+"     ").substring(0,5)+" ");
+			if((count%20)==0)
+			    out.print("\n");
+			count++;
+		  }
+		}
+		out.print("\n");
+		out.close();
+	    } catch (IOException e) {
+		e.printStackTrace();
+	    }
+	}
+    }
+
+
+    // Write all probabilities in plain text format.
+    // 
+    public void outputAll(String file, boolean all) {
+	if (all) {
+	    try {
+	      OutFile out = new OutFile(file);
+	      out.print("# ProAlign: all posterior probability.\n");
+	      out.print("# "+resultString+".\n");
+	      String[] nodeNames = root.getInternalNames();
+	      for (int j=0; j<root.getNumChild()-1; j++) {
+		  out.print("# "+nodeNames[j]+".\n");
+		  for (int i=0; i<seqLength; i++) {
+		      double[] column = root.getInternalPostProbAt(i);
+		      out.print((roundDoubleToString((double)Math.exp(column[j]),3)+"     ").substring(0,5)+" ");
+		      if(i>0 && (i+1)%20==0)
+			  out.print("\n");
+		  }
+		  out.print("\n");
+	      }
+	      out.print("\n");
+	      out.close();
+	    } catch (IOException e) {
+	    }
+	}else {
+	    try {
+		OutFile out = new OutFile(file);
+		out.print("# ProAlign: mean posterior probability.\n");
+		out.print("# "+resultString+".\n");
+		String[] nodeNames = root.getInternalNames();
+		for (int j=0; j<root.getNumChild()-1; j++) {
+		    out.print("# "+nodeNames[j]+".\n");
+		    int count = 1;
+		    for (int i = 0; i < seqLength; i++) {
+			if(!sitesRemoved[i]) {
+			    double[] column = root.getInternalPostProbAt(i);			    
+			    out.print((roundDoubleToString((double)Math.exp(column[j]),3)+"     ").substring(0,5)+" ");
+			    if((count%20)==0)
+				out.print("\n");
+			    count++;
+			}
+		    }
+		    out.print("\n");
+		}
+		out.print("\n");
+		out.close();
+	    } catch (IOException e) {
+		e.printStackTrace();
+	    }
+	}
+    }
+
+    // Write root character probabilities
+    // 
+    public void writeRoot(String file) {
+
+	try {
+	    OutFile out = new OutFile(file+".root");
+
+	    for(int i=0; i<root.charProb.length; i++) {
+		out.print(i+1);
+		for(int j=0; j<root.charProb[i].length; j++) {
+		    out.print(" "+root.charProb[i][j]);
+		}
+		out.println();
+	    }
+
+	    out.close();
+	} catch (IOException e) {
+	    e.printStackTrace();
+	}
+    }
+
+    // Round a value to certain precision
+    //
+    public String roundDoubleToString(double val, int prec) {
+	String full = ""+val;
+	if(full.indexOf('.')>-1) {
+	    String begin = full.substring(0,full.indexOf('.'));
+	    String end = full.substring(full.indexOf('.')+1);
+	    if(end.length()>prec) {
+		char num = end.charAt(prec);
+		if(num=='0'||num=='1'||num=='2'||num=='3'||num=='4') {
+		    full = begin+"."+end.substring(0,prec);
+		    
+		    // last one is greater than 4 -> rounded up
+		} else {
+		    char[] digit = new char[prec];
+		    for(int i=0; i<prec; i++) {
+			digit[i] = end.charAt(i);
+		    }
+		    
+		    int add = 1;
+		    for(int i=prec-1; i>=0; i--) {
+			if(digit[i]=='9') {
+			    add = 1;
+			    digit[i]='0';
+			} else {
+			    digit[i]= (char)((int)digit[i]+add);
+			    add = 0;
+			    break;
+			}		       
+		    }
+		    begin = ""+(new Integer(begin).intValue()+add);
+		    end = new String();
+		    for(int i=0; i<prec; i++) {
+			end += digit[i];
+		    }
+		    full = begin+"."+end;
+		}
+	    }
+	}
+	return full;
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SequenceReader.java b/SequenceReader.java
new file mode 100644
index 0000000..ee90cec
--- /dev/null
+++ b/SequenceReader.java
@@ -0,0 +1,641 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+/**
+ * Reads FASTA/PIR format alignment files.
+ * Data read into a hashtable: String 'name', String 'data'
+ */
+public class SequenceReader {
+    String row, str1, str2 = new String();
+    HashMap seqmap;
+    boolean allFine;
+    String alphabet;
+    String errors;
+
+    SequenceReader() { }
+
+
+    /**
+     * Tries to read from a file. Returns TRUE if fine, FALSE if errors.
+     */
+    boolean fromFile(String infile) {
+
+	allFine = true;
+	seqmap = new HashMap();
+	errors = new String("\n");
+
+	ProAlign.log("SequenceReader: "+infile);
+
+	String firstRow = new String();
+	try {
+	    InFile in = new InFile(infile);
+	    while(true) {
+		firstRow = in.readLine().trim();
+		if(firstRow.equals("")) {
+		    continue;
+		}
+		break;
+	    }
+	    
+	} catch(FileNotFoundException e) {
+	    warnError("File not found:" + infile);
+	} catch(IOException e) { }
+
+	if(firstRow.startsWith(">")) {
+	    readFasta(infile);
+	} else if(Character.isDigit(firstRow.charAt(0))) {
+	    readPhylip(infile);
+	} else if(firstRow.equalsIgnoreCase("#nexus")) {
+	    readNexus(infile);
+	} else if(firstRow.equalsIgnoreCase("PileUp")||
+		  firstRow.equalsIgnoreCase("!!AA_MULTIPLE_ALIGNMENT")||
+		  firstRow.equalsIgnoreCase("!!NA_MULTIPLE_ALIGNMENT")) {
+	    readMsf(infile);
+	} else {
+	    warnError("     The file does not look like Fasta-,\n"+
+		      "    PIR-, Phylip-, MSF-, or Nexus-format!");
+	}
+
+	if(seqmap.size()>0 && allFine) {
+	    return true;
+	} else {
+	    return false;
+	}
+    }
+
+
+    void readFasta(String infile) {
+	
+	try {
+	    
+	    // Calls ready-made filereader
+	    InFile in = new InFile(infile);
+	    
+	    int formatType = 0;
+
+	    while(true) { // Read first non-empty line
+		row = in.readLine().trim();
+		if(row.equals("")) {
+		    continue;
+		}
+		break;
+	    }
+
+	    while(row != null) {
+		
+		// Takes sequence name and checks
+		// that same name doesn't exist
+		if(row.startsWith(">")) {
+		    row = row.substring(1);
+		    row = row.trim();
+		    
+		    // Sequence is PIR
+		    if(row.startsWith("DL;")) {
+			formatType = 1;
+			row = row.substring(3);
+			row = row.trim();
+			in.readLine(); // skip one row!
+			
+		    } else if(row.startsWith("P1;")) {
+			formatType = 2;
+			row = row.substring(3);
+			row = row.trim();
+			in.readLine(); // skip one row!
+			
+			//Sequence must be FASTA
+		    } else {
+			formatType = 0;
+		    }
+		    
+		    // Same sequence name given twice!!
+		    if(seqmap.containsKey(row)) {
+			warnError("  Sequence " + row + "\n     is double defined!");
+		    }
+		    
+		    if(formatType > 0) { // If PIR -> has to end with '*'
+			
+			str2 = "";
+			while((str1 = in.readLine())!= null) {
+			    			    
+			    // remove "gap" signs
+			    if(str1.indexOf("-")>-1) {
+				str1 = removeChar(str1,'-');
+			    }
+			    if(str1.indexOf(" ")>-1) {
+				str1 = removeSpace(str1);
+			    }
+			    
+			    if(str1.length() == 0) {  // Skips empty lines.
+				continue;   
+			    }
+			    
+			    str2 += str1;
+			    
+			    if(str1.endsWith("*")){ // Reads until the asterisk (needed!)
+				
+				str1 = str2.substring(0,str2.indexOf("*")); // but removed.
+				str1 = str1.toUpperCase();
+				
+				seqmap.put(row, str1);
+				ProAlign.log(">"+row+"\n"+str1);
+				
+				row = in.readLine(); // New one in!
+				break;
+			    }
+			}
+		    }
+
+		    
+		    
+		    // Append FASTA sequence and save it.
+		    else { // If FASTA -> no '*' in the end
+			
+			str2 = "";
+			while((str1 = in.readLine())!= null) {
+			    
+			    // remove "gap" signs
+			    if(str1.indexOf("-")>-1) {
+				str1 = removeChar(str1,'-');
+			    }
+			    if(str1.indexOf(" ")>-1) {
+				str1 = removeSpace(str1);
+			    }
+			    
+			    if(str1.length() == 0) { // Skips empty lines.
+				continue;
+			    }
+			    
+			    if(str1.startsWith(">")){  // Loop works until a new seqname
+				
+				str2 = str2.toUpperCase();			    
+				
+				seqmap.put(row, str2);
+				ProAlign.log(">"+row+"\n"+str2);
+				
+				row = str1; // Copy new one
+				row = row.trim();
+				break;
+			    }
+			    str2 += str1;
+			}
+			if(str1 == null) { // Take the last seq. End of the file.
+			    
+			    str2 = str2.toUpperCase();			    
+			    
+			    seqmap.put(row, str2);
+			    ProAlign.log(">"+row+"\n"+str2);
+			    
+			    row = str1; // Copy null so first loop stops
+			}
+		    }
+		} else if(row.trim().equals("")){
+		    row = in.readLine();
+		    continue;		    
+		} else {
+		    warnError("  File '" + infile + "'\n"+
+			      "    is supposed to start with '>'!");
+		    break;
+		    //row = in.readLine();
+		}
+	    }
+	    in.close();
+	    
+	    // Something wrong. Give warnings and return.
+	} catch(FileNotFoundException e) {
+	    warnError("File not found:" + infile);
+		
+	} catch(IOException e) { }
+    }
+
+    void readPhylip(String infile) {
+
+	String[] seqNames = new String[0];
+	String[] seqData = new String[0];
+
+	String row = new String();
+	try {
+	    InFile in = new InFile(infile);
+	    while(true) {
+		row = in.readLine().trim();
+		if(row.equals("")) {
+		    continue;
+		}
+		break;
+	    }
+
+	    int numTaxa = new Integer(row.substring(0,row.indexOf(" ")).trim()).intValue();
+	    int seqLength = new Integer(row.substring(row.indexOf(" ")+1).trim()).intValue();
+
+	    seqNames = new String[numTaxa];
+	    seqData = new String[numTaxa];
+
+	    int sn = 0;
+	    int rn = 0;
+	    while((row = in.readLine())!= null) {
+		if(row.trim().equals("")) {
+		    continue;
+		}
+		if(rn<numTaxa) {
+		    seqNames[sn] = row.substring(0,row.indexOf(" ")).trim();
+		    seqData[sn] = row.substring(row.indexOf(" ")+1).trim();
+		    rn++; sn++;
+		} else {
+		    seqData[sn++] += row.trim();
+		}
+		if(sn==numTaxa) {
+		    sn=0;
+		}
+	    }
+	    in.close();
+	    
+	    // Something wrong. Give warnings and return.
+	} catch(FileNotFoundException e) {
+	    warnError("File not found:" + infile);
+		
+	} catch(IOException e) { }
+
+	for(int i=0; i<seqNames.length; i++) {
+
+	    if(seqmap.containsKey(seqNames[i])) {
+		warnError("  Sequence " +seqNames[i]+ "\n     is double defined!");
+	    }
+
+	    String seq = removeSpace(seqData[i].toUpperCase());
+	    seq = removeChar(seq,'-');
+
+	    seqmap.put(seqNames[i],seq);
+	    ProAlign.log(">"+seqNames[i]+"\n"+seq);
+	}
+    }
+
+
+    void readMsf(String infile) {
+
+	String[] seqNames = new String[0];
+	String[] seqData = new String[0];
+	HashMap names = new HashMap();
+
+	String row = new String();
+	try {
+	    InFile in = new InFile(infile);
+	    int s=0;
+	    while(true) {
+		row = in.readLine().trim();
+		if(row.startsWith("//")) {
+		    break;
+		} else if(row.indexOf("Name:")>-1) {
+		    row = row.substring(row.indexOf("Name:")+5).trim();
+		    if(row.indexOf(" ")>-1) {
+			row = row.substring(0,row.indexOf(" ")).trim();
+		    }
+		    names.put(row,new Integer(s));
+		    s++;
+		} else {
+		    continue;		    
+		}
+	    }
+	    
+	    seqNames = new String[names.size()];
+	    seqData = new String[names.size()];
+
+	    Iterator nameKeys = names.keySet().iterator();
+	    while(nameKeys.hasNext()) {
+		String n = (String) nameKeys.next();
+		s = ((Integer)names.get(n)).intValue();
+		seqNames[s] = n;
+		seqData[s] = "";
+	    }
+
+	    while((row = in.readLine())!= null) {
+		if(row.trim().equals("")) {
+		    continue;
+		}
+		
+		String begin = row.substring(0,row.indexOf(" ")).trim();
+
+		if(names.containsKey(begin)) {
+
+		    String end = row.substring(row.indexOf(" ")+1).trim();
+		    s = ((Integer)names.get(begin)).intValue();
+		    seqData[s] += end;
+
+		}
+	    }
+	    in.close();
+	    
+	    // Something wrong. Give warnings and return.
+	} catch(FileNotFoundException e) {
+	    warnError("File not found:" + infile);
+		
+	} catch(IOException e) { }
+
+	for(int i=0; i<seqNames.length; i++) {
+
+	    if(seqmap.containsKey(seqNames[i])) {
+		warnError("  Sequence " +seqNames[i]+ "\n     is double defined!");
+	    }
+
+	    String seq = removeSpace(seqData[i].toUpperCase());
+	    seq = removeChar(seq,'-');
+	    seq = removeChar(seq,'.');
+	    seq = removeChar(seq,'~');
+
+	    seqmap.put(seqNames[i],seq);
+	    ProAlign.log(">"+seqNames[i]+"\n"+seq);
+	}
+//
+	ProAlign.log.print("CORE ");
+	for(int m=0; m<seqData[0].length(); m++) {
+	    if(Character.isUpperCase(seqData[0].charAt(m))) {
+		ProAlign.log.print("1");
+	    } else {
+		ProAlign.log.print("0");	
+	    }    
+	}
+	ProAlign.log.println();
+//
+    }
+
+
+
+    void readNexus(String infile) {
+
+	String[] seqNames = new String[0];
+	String[] seqData = new String[0];
+	String missing = new String("?");
+
+	try {
+	    
+	    // Calls ready-made filereader
+	    InFile in = new InFile(infile);
+	    int order = 0;
+	    
+	    int numChar = 0;
+	    int numTaxa = 0;
+	    
+	    boolean interleave = false;
+	    
+	    String row = in.readLine(); // Read first line
+	    while((row != null)) {
+		
+		row = row.toUpperCase().trim();
+		
+		if(row.startsWith("DIMENSIONS")) {
+		    if(row.indexOf("NTAX")>-1) {
+			String ntax = row.substring(row.indexOf("NTAX"));
+			ntax = ntax.substring(ntax.indexOf("=")+1);
+			if(ntax.indexOf(" ")>0) {
+			    ntax = ntax.substring(0,ntax.indexOf(" "));
+			} else {
+			    ntax = ntax.substring(0,ntax.indexOf(";"));
+			}
+			numTaxa = Integer.valueOf(ntax).intValue();
+		    }
+		    if(row.indexOf("NCHAR")>-1) {
+			String nchar = row.substring(row.indexOf("NCHAR"));
+			nchar = nchar.substring(nchar.indexOf("=")+1);
+			if(nchar.indexOf(" ")>0) {
+			    nchar = nchar.substring(0,nchar.indexOf(" "));
+			} else {
+			    nchar = nchar.substring(0,nchar.indexOf(";"));
+			}
+			numChar = Integer.valueOf(nchar).intValue();
+		    }
+		}
+		
+		if(row.indexOf("MISSING")>-1) {
+		    missing = row.substring(row.indexOf("MISSING"));
+		    missing = missing.substring(missing.indexOf("=")+1);
+		    if(missing.indexOf(" ")>0) {
+			missing = missing.substring(0,missing.indexOf(" "));
+		    } else {
+			missing = missing.substring(0,missing.indexOf(";"));
+		    }
+		} else if(row.indexOf("GAP")>-1) {
+		    missing = row.substring(row.indexOf("GAP"));
+		    missing = missing.substring(missing.indexOf("=")+1);
+		    if(missing.indexOf(" ")>0) {
+			missing = missing.substring(0,missing.indexOf(" "));
+		    } else if(missing.indexOf(";")>0) {
+			missing = missing.substring(0,missing.indexOf(";"));
+		    } else {
+			missing = missing.trim();
+		    }
+		}
+		if(row.indexOf("INTERLEAVE")>0) {
+		    interleave = true;
+		}
+
+		if(row.startsWith("MATRIX")) {
+
+		    // sequence data starts.
+		    seqNames = new String[numTaxa];
+		    seqData = new String[numTaxa];
+		    
+		    row = in.readLine();
+		    while((row != null)) {
+			row = row.trim();
+			if(!interleave) {
+			    for(int i=0; i<numTaxa; i++) {
+				
+				if(row.length() == 0 ) {
+				    row = in.readLine().trim();
+				    continue;
+				}			
+				if(row.indexOf("[")>-1) {
+				    row = removeComment(row);
+				}
+				//System.out.println(i+": "+row);
+				if(row.indexOf(" ")>-1) {
+				    seqNames[i] = row.substring(0,row.indexOf(" "));
+				    row = row.substring(row.indexOf(" ")+1).toUpperCase();
+
+				    seqData[i] = removeSpace(row);
+				    row = in.readLine();
+				    while(seqData[i].length() < numChar) {
+					if(row.length() == 0 ) {
+					    row = in.readLine().trim();
+					    continue;
+					}
+					row = row.toUpperCase();
+					seqData[i] += removeSpace(row);
+					row = in.readLine();
+				    }
+				} else {
+				    seqNames[i] = row.trim();
+				    row = in.readLine().trim();
+				    //System.out.println(i+": "+row);
+				    seqData[i] = removeSpace(row);
+				    row = in.readLine();
+				    while(seqData[i].length() < numChar) {
+					if(row.length() == 0 ) {
+					    row = in.readLine().trim();
+					    continue;
+					}
+					row = row.toUpperCase();
+					seqData[i] += removeSpace(row);
+					row = in.readLine();
+				    }
+				} 
+
+			    }
+			    break;
+			} else {
+			    for(int i=0; i<numTaxa; i++) {
+				if(row.length() == 0 ) {
+				    row = in.readLine().trim();
+				    continue;
+				}
+				seqNames[i] = row.substring(0,row.indexOf(" "));
+				row = row.substring(row.indexOf(" ")+1).toUpperCase();
+				seqData[i] = removeSpace(row);
+				row = in.readLine().trim();
+			    }
+			    while(seqData[0].length() < numChar) {
+				if(row.length() == 0 ) {
+				    row = in.readLine().trim();
+				    continue;
+				}
+				for(int i=0; i<numTaxa; i++) {
+				    row = row.substring(row.indexOf(" ")+1).toUpperCase();
+				    seqData[i] += removeSpace(row);
+				    row = in.readLine().trim();
+				}
+			    }
+			    break;
+			}
+		    }
+		}
+		
+		row = in.readLine();
+	    }
+	    in.close();
+
+	    // Something wrong. Give warnings and return.
+	} catch(FileNotFoundException e) {
+	    warnError("File not found:" + infile);
+	    
+	} catch(IOException e) { }
+	
+	for(int i=0; i<seqNames.length; i++) {
+	    
+	    if(seqmap.containsKey(seqNames[i])) {
+		warnError("  Sequence " +seqNames[i]+ 
+			  "\n     is double defined!");
+	    }
+	    
+	    String seq = seqData[i].toUpperCase();
+	    seq = removeChar(seqData[i],missing.charAt(0));
+
+	    seqmap.put(seqNames[i].trim(),seq);
+	    ProAlign.log(">"+seqNames[i]+"\n"+seq);
+	}
+    }
+
+
+    String removeSpace(String row) {
+	if(row.indexOf(" ")>-1) {
+	    String str = new String("");
+	    for(int j=0; j<row.length(); j++) {
+		if(row.charAt(j)!=' ') {
+		    str += row.charAt(j);
+		}
+	    }
+	    return str;
+	} else {
+	    return row;
+	}
+    }
+
+    String removeChar(String row,char rem) {
+	if(row.indexOf(rem)>-1) {
+	    String str = new String("");
+	    for(int j=0; j<row.length(); j++) {
+		if(row.charAt(j)!=rem) {
+		    str += row.charAt(j);
+		}
+	    }
+	    return str;
+	} else {
+	    return row;
+	}
+    }
+
+    String removeComment(String row) {
+
+	String content = new String();
+	boolean keep = true;
+	for(int i=0; i<row.length(); i++) {
+	    if(row.charAt(i)=='[') {
+		keep=false;
+	    } else if(row.charAt(i)==']') {
+		keep=true;
+		i++;
+	    }
+	    if(keep) {
+		content += row.charAt(i);
+	    }
+	}
+	return content.trim();
+    }
+
+
+    /**
+     * Reading sequences returns just TRUE or FALSE.
+     * Return sequences if reading the file was OK. 
+     */
+    public HashMap getSequences() {
+	return seqmap;
+    }
+    
+    void warnError(String msg) {
+	allFine = false;
+	ProAlign.log.println("SequenceReader: "+msg);
+	errors += msg+"\n";
+    }
+    String getErrors() {
+	return errors;
+    }
+
+/*   
+    public static void main(String[] args) {
+        SequenceReader sr = new SequenceReader();
+        if(sr.fromFile(args[0])) {
+	    
+	    HashMap seqs = sr.getSequences();
+	    Iterator it = seqs.keySet().iterator();
+	    while(it.hasNext()) {
+		String name = (String) it.next();
+		String seq = (String) seqs.get(name);
+		System.out.println(name+"\n"+seq);
+	    }
+	} 
+    }
+//*/
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/SetParameters.java b/SetParameters.java
new file mode 100644
index 0000000..b883fec
--- /dev/null
+++ b/SetParameters.java
@@ -0,0 +1,881 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JLabel;
+import javax.swing.JMenuItem;
+import javax.swing.JComboBox;
+import javax.swing.JButton;
+import javax.swing.JRadioButton;
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBox;
+import javax.swing.JTextField;
+import java.awt.GridLayout;
+import java.awt.BorderLayout;
+import java.awt.Font;
+import java.awt.Container;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.ItemListener;
+import java.awt.event.ItemEvent;
+import java.io.File;
+
+public class SetParameters extends JFrame {
+
+    JTextField textDelta;
+    JTextField textEpsil;
+    JTextField textGapF;
+    JTextField textGapP;
+    JTextField[] textFreq;
+    JTextField textPwOpen;
+    JTextField textPwExt;
+    JTextField textPwMat;
+    JTextField textWidth;
+    JTextField textClustalw;
+    JTextField textTemp;
+    JTextField textOffset;
+    JTextField textScale;
+    JCheckBox boxLog;
+    JCheckBox boxTrail;
+    JCheckBox boxMultiple;
+    JCheckBox boxPenalize;
+    JComboBox pwSubst;
+
+    String[] pwMatrix = {"pam60","pam120","pam160","pam250"};
+
+    int height = 300;
+    int width = 600;
+
+    ProAlign pa;
+    ResultWindow rw;
+
+    SetParameters(ResultWindow rw) {
+
+	setTitle("Set parameters");   
+	this.rw = rw;
+	this.pa = rw.pa;
+
+	Font font = new Font(ProAlign.paFontName, Font.PLAIN, ProAlign.paFontSize);
+
+	ButtonListener but = new ButtonListener(); 
+	JButton buttonEstFreq = new JButton("Estimate");
+	buttonEstFreq.setFont(font);
+	buttonEstFreq.setActionCommand("estimatefreq");
+	buttonEstFreq.addActionListener(but);
+	JButton buttonDefFreq = new JButton("Default");
+	buttonDefFreq.setFont(font);
+	buttonDefFreq.setActionCommand("defaultfreq");
+	buttonDefFreq.addActionListener(but);
+	JButton buttonSetFreq = new JButton("Set");
+	buttonSetFreq.setFont(font);
+	buttonSetFreq.setActionCommand("setfreq");
+	buttonSetFreq.addActionListener(but);
+	JButton buttonLock = new JButton();
+	buttonLock.setFont(font);
+	buttonLock.setActionCommand("lock");
+	buttonLock.addActionListener(but);
+	JButton buttonHmmE = new JButton("Default");
+	buttonHmmE.setFont(font);
+	buttonHmmE.setActionCommand("hmmE");
+	buttonHmmE.addActionListener(but);
+	JButton buttonHmmD = new JButton("Default");
+	buttonHmmD.setFont(font);
+	buttonHmmD.setActionCommand("hmmD");
+	buttonHmmD.addActionListener(but);
+	JButton buttonHmmEst = new JButton("Estimate");
+	buttonHmmEst.setFont(font);
+	buttonHmmEst.setActionCommand("hmmEst");
+	buttonHmmEst.addActionListener(but);
+	if(rw.hasTree) {
+	    buttonHmmEst.setEnabled(true);
+	} else {
+	    buttonHmmEst.setEnabled(false);  
+	}
+	JButton buttonGapF = new JButton("Default");
+	buttonGapF.setFont(font);
+	buttonGapF.setActionCommand("gapF");
+	buttonGapF.addActionListener(but);
+	JButton buttonGapP = new JButton("Default");
+	buttonGapP.setFont(font);
+	buttonGapP.setActionCommand("gapP");
+	buttonGapP.addActionListener(but);
+
+	JButton buttonOffset = new JButton("Default");
+	buttonOffset.setFont(font);
+	buttonOffset.setActionCommand("offset");
+	buttonOffset.addActionListener(but);
+	JButton buttonLog = new JButton("Select");
+	buttonLog.setFont(font);
+	buttonLog.setActionCommand("log");
+	buttonLog.addActionListener(but);
+	JButton buttonWidth = new JButton("Default");
+	buttonWidth.setFont(font);
+	buttonWidth.setActionCommand("defwidth");
+	buttonWidth.addActionListener(but);
+	JButton buttonClustalw = new JButton("Change");
+	buttonClustalw.setFont(font);
+	buttonClustalw.setActionCommand("clustalw");
+	buttonClustalw.addActionListener(but);
+	JButton buttonTemp = new JButton("Change");
+	buttonTemp.setFont(font);
+	buttonTemp.setActionCommand("temp");
+	buttonTemp.addActionListener(but);
+	JButton buttonOK = new JButton("OK");
+	buttonOK.setFont(font);
+	buttonOK.setActionCommand("ok");
+	buttonOK.addActionListener(but);
+	JButton buttonCancel = new JButton("Cancel");
+	buttonCancel.setFont(font);
+	buttonCancel.setActionCommand("cancel");
+	buttonCancel.addActionListener(but);
+
+	JLabel labelHMM = new JLabel("   HMM model");
+	labelHMM.setFont(font);
+	JLabel labelVit = new JLabel("   Viterbi parameters");
+	labelVit.setFont(font);
+	JLabel labelSet = new JLabel("   Other parameters");
+	labelSet.setFont(font);
+	JLabel labelChar = new JLabel("   Character frequencies");
+	labelChar.setFont(font);
+	JLabel labelTrail = new JLabel(" Trailing sequences");
+	labelTrail.setFont(font);
+	JLabel labelPenalize = new JLabel(" Terminal gaps");
+	labelPenalize.setFont(font);
+	JLabel labelOffset = new JLabel("   Max allow");
+	labelOffset.setFont(font);
+	JLabel labelMultiple = new JLabel(" Distances");
+	labelMultiple.setFont(font);
+	JLabel labelScale = new JLabel(" scale");
+	labelScale.setFont(font);
+	
+	JLabel labelSample = new JLabel(" Viterbi traceback");
+	labelSample.setFont(font);
+	JLabel labelClustalw = new JLabel(" ClustalW");
+	labelClustalw.setFont(font);
+	JLabel labelTemp = new JLabel(" Temp folder");
+	labelTemp.setFont(font);
+	JLabel labelLog = new JLabel(" Change file");
+	labelLog.setFont(font);
+	JLabel labelDelta = new JLabel(" Delta");
+	labelDelta.setFont(font);
+	JLabel labelEpsil = new JLabel(" Epsilon");
+	labelEpsil.setFont(font);
+	JLabel labelGapF = new JLabel(" Gap frequency");
+	labelGapF.setFont(font);
+	JLabel labelGapP = new JLabel(" Gap probability");
+	labelGapP.setFont(font);
+	JLabel labelPwPar = new JLabel("   Pairwise alignment");       
+	labelPwPar.setFont(font);
+	JLabel labelPwOpen = new JLabel(" Gap opening");       
+	labelPwOpen.setFont(font);
+	JLabel labelPwExt = new JLabel(" Gap extension");       
+	labelPwExt.setFont(font);
+	JLabel labelWidth = new JLabel(" Band width");
+	labelWidth.setFont(font); 
+	JLabel[] labelFreq = new JLabel[pa.sm.alphabet.length()];
+
+	textDelta = new JTextField("     ");
+	textDelta.setText(""+pa.sm.delta);
+	textDelta.setFont(font);
+	textEpsil = new JTextField("     ");
+	textEpsil.setText(""+pa.sm.epsilon);
+	textEpsil.setFont(font);
+
+	textGapF = new JTextField("     ");
+	textGapF.setText(""+pa.gapFreq);
+	textGapF.setFont(font);
+	textGapP = new JTextField("     ");
+	textGapP.setText(""+pa.gapProb);
+	textGapP.setFont(font);
+
+	textPwOpen = new JTextField("     "); 
+	textPwExt = new JTextField("     "); 
+	textPwMat = new JTextField("     "); 
+	textPwOpen.setFont(font);
+	textPwExt.setFont(font);
+
+	if(ProAlign.isDna) {
+	    textPwOpen.setText(""+(float)pa.pwDnaOpen/100f);
+	    textPwExt.setText(""+(float)pa.pwDnaExt/100f);
+	    textPwMat.setText("  DNA");
+	    textPwMat.setEditable(false);
+	} else {
+	    textPwOpen.setText(""+(float)pa.pwProtOpen/100f);
+	    textPwExt.setText(""+(float)pa.pwProtExt/100f);
+	    textPwMat.setText("  "+pa.pwProtMatrix);
+	    textPwMat.setEditable(false);
+	}
+
+	// Pairwise protein score matrix 
+	pwSubst = new JComboBox();
+	pwSubst.setFont(font);
+	pwSubst.addItemListener(new ComboBoxListener());
+
+	for(int i=0; i<pwMatrix.length; i++) {
+	    pwSubst.addItem(pwMatrix[i]);
+	}
+	pwSubst.setSelectedItem(pa.pwProtMatrix);
+
+	if(ProAlign.isDna) {
+	    textFreq = new JTextField[pa.sm.alphabet.length()];
+	    for(int i=0; i<textFreq.length; i++) {
+		labelFreq[i] = new JLabel();
+		labelFreq[i].setFont(font);
+		textFreq[i] = new JTextField();
+		textFreq[i].setFont(font);
+		labelFreq[i].setText("     "+pa.sm.alphabet.charAt(i));
+		textFreq[i].setText((""+pa.sm.charFreqs[i]+"     ").substring(0,5));
+	    }
+	    buttonLock.setText(""+pa.sm.alphabet.charAt(pa.sm.alphabet.length()-1));
+	    textFreq[pa.sm.alphabet.length()-1].setEditable(false);
+	}
+
+	textWidth = new JTextField("     ");
+	textWidth.setText(""+pa.bandWidth);
+	textWidth.setFont(font);
+	textClustalw = new JTextField("     "); 
+	textClustalw.setText(""+pa.clustalwPath);
+	textClustalw.setFont(font);
+	textClustalw.setEditable(false);
+	textTemp = new JTextField("     "); 
+	textTemp.setText(""+pa.tempFolder);
+	textTemp.setFont(font);
+	textTemp.setEditable(false);
+ 	textOffset = new JTextField("     "); 
+	textOffset.setText(""+ProAlign.offset);
+	textOffset.setFont(font);
+ 	textScale = new JTextField("     "); 
+	textScale.setText(""+ProAlign.distScale);
+	textScale.setFont(font);
+
+	JRadioButton traceBest = new JRadioButton("select best");
+	traceBest.setFont(font);
+	traceBest.setActionCommand("best");
+	JRadioButton traceSample = new JRadioButton("sample");
+	traceSample.setFont(font);
+	traceSample.setActionCommand("sample");
+
+	if(ProAlign.trackBest) {
+	    traceBest.setSelected(true);
+	} else {
+	   traceSample.setSelected(true);
+	}
+
+	boxLog = new JCheckBox("Write log");
+	boxLog.setFont(font);
+	boxLog.setActionCommand("log");
+	boxLog.addActionListener(new CheckBoxListener());
+	if(ProAlign.DEBUG) {
+	    boxLog.setSelected(true);
+	} else {
+	    boxLog.setSelected(false);
+	}
+
+	boxTrail = new JCheckBox("correct");
+	boxTrail.setFont(font);
+	boxTrail.setActionCommand("trailing");
+	boxTrail.addActionListener(new CheckBoxListener());
+	if(ProAlign.removeTrailing) {
+	    boxTrail.setSelected(true);
+	    textOffset.setEditable(true);
+	} else {
+	    boxTrail.setSelected(false);
+	    textOffset.setEditable(false);
+	}
+
+	boxMultiple = new JCheckBox("correct");
+	boxMultiple.setFont(font);
+	boxMultiple.setActionCommand("multiple");
+	boxMultiple.addActionListener(new CheckBoxListener());
+	if(ProAlign.correctMultiple) {
+	    boxMultiple.setSelected(true);
+	} else {
+	    boxMultiple.setSelected(false);
+	}
+
+	boxPenalize = new JCheckBox("penalize");
+	boxPenalize.setFont(font);
+	boxPenalize.setActionCommand("terminal");
+	boxPenalize.addActionListener(new CheckBoxListener());
+	if(ProAlign.penalizeTerminal) {
+	    boxPenalize.setSelected(true);
+	} else {
+	    boxPenalize.setSelected(false);
+	}
+
+	// Construct panels
+	//
+	JPanel fullPanel = new JPanel();
+	fullPanel.setLayout(new GridLayout(1,2,15,15));
+	JPanel halfPanel = new JPanel();
+	halfPanel.setLayout(new GridLayout(11,1,5,5));
+
+//
+	halfPanel.add(labelChar);
+
+	JPanel pnl = new JPanel(); 
+	if(ProAlign.isDna) {
+	    pnl = new JPanel();
+	    pnl.setLayout(new GridLayout(1,textFreq.length,5,5));
+	    for(int i=0; i<textFreq.length-1; i++) {
+		pnl.add(labelFreq[i]);
+	    }
+	    pnl.add(buttonLock);
+	    halfPanel.add(pnl);
+	    
+	    pnl = new JPanel();
+	    pnl.setLayout(new GridLayout(1,textFreq.length,5,5));
+	    for(int i=0; i<textFreq.length; i++) {
+		pnl.add(textFreq[i]);
+	    }
+	    halfPanel.add(pnl);	
+
+	    pnl = new JPanel();
+	    pnl.setLayout(new GridLayout(1,3,5,5));
+	    pnl.add(buttonSetFreq);
+	    pnl.add(buttonDefFreq);
+	    pnl.add(buttonEstFreq);
+	    halfPanel.add(pnl);
+	} else {
+	    pnl = new JPanel();
+	    pnl.setLayout(new GridLayout(1,3,5,5));
+	    pnl.add(labelGapF);
+	    pnl.add(textGapF);
+	    pnl.add(buttonGapF);
+	    halfPanel.add(pnl);
+
+	    pnl = new JPanel();
+	    pnl.setLayout(new GridLayout(1,3,5,5));
+	    pnl.add(labelGapP);
+	    pnl.add(textGapP);
+	    pnl.add(buttonGapP);
+	    halfPanel.add(pnl);
+
+	    halfPanel.add(new JLabel());
+	}
+
+	halfPanel.add(labelPwPar);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	if(ProAlign.isDna) {
+	    pnl.add(new JLabel());
+	} else {
+	    pnl.add(pwSubst);
+	}
+	pnl.add(labelPwOpen);
+	pnl.add(labelPwExt);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(textPwMat);
+	pnl.add(textPwOpen);
+	pnl.add(textPwExt);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,2,5,5));
+	pnl.add(labelPenalize);
+	pnl.add(boxPenalize);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,2,5,5));
+	pnl.add(labelTrail);
+	pnl.add(boxTrail);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(labelOffset);
+	pnl.add(textOffset);
+	pnl.add(buttonOffset);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,4,5,5));
+	pnl.add(labelMultiple);
+	pnl.add(boxMultiple);
+	pnl.add(labelScale);
+	pnl.add(textScale);
+	halfPanel.add(pnl);
+
+	fullPanel.add(halfPanel);
+
+	halfPanel = new JPanel();
+	halfPanel.setLayout(new GridLayout(11,1,5,5));
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(labelHMM);
+	pnl.add(new JLabel());
+	pnl.add(buttonHmmEst);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(labelDelta);
+	pnl.add(textDelta);
+	pnl.add(buttonHmmD);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(labelEpsil);
+	pnl.add(textEpsil);
+	pnl.add(buttonHmmE);
+	halfPanel.add(pnl);
+
+	ButtonGroup traceBack = new ButtonGroup();
+        traceBack.add(traceBest);
+        traceBack.add(traceSample);
+        traceBest.addActionListener(new RadioListener());
+        traceSample.addActionListener(new RadioListener());
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,2,5,5));
+	pnl.add(labelSample);
+	pnl.add(traceBest);
+	halfPanel.add(pnl);
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,2,5,5));
+	pnl.add(new JPanel());
+	pnl.add(traceSample);
+	halfPanel.add(pnl);
+
+//	halfPanel.add(new JLabel());
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(boxLog);
+	pnl.add(labelLog);
+	pnl.add(buttonLog);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(labelWidth);
+	pnl.add(textWidth);
+	pnl.add(buttonWidth);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(labelClustalw);
+	pnl.add(textClustalw);
+	pnl.add(buttonClustalw);
+	halfPanel.add(pnl);
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(labelTemp);
+	pnl.add(textTemp);
+	pnl.add(buttonTemp);
+	halfPanel.add(pnl);
+
+	halfPanel.add(new JLabel());
+
+	pnl = new JPanel();
+	pnl.setLayout(new GridLayout(1,3,5,5));
+	pnl.add(new JLabel());
+	pnl.add(buttonOK);
+	pnl.add(buttonCancel);
+	halfPanel.add(pnl);
+
+	fullPanel.add(halfPanel);
+
+	Container cp = getContentPane();
+	cp.add(fullPanel);
+
+	addWindowListener(new WindowAdapter() {
+		public void windowClosing(WindowEvent e) {
+		    dispose();
+		}
+	    });
+
+    }
+    
+    class ButtonListener implements ActionListener {
+	public void actionPerformed(ActionEvent e) {
+	    
+	    JButton target = (JButton)e.getSource();
+	    String actionCommand = target.getActionCommand();
+
+	    if(actionCommand.equals("estimatefreq")) {
+
+		if(!rw.hasData) {
+
+		    String text = new String("\n   Estimating frequencies needs some data! \n");
+		    OpenDialog od = new OpenDialog(SetParameters.this);
+		    od.showDialog("Error!", text);    
+
+		} else {
+
+		    EstimateFrequencies ef = new EstimateFrequencies(
+			rw.seqcont.textArray,pa.sm.alphabet,pa.sm.equateAlphabet);
+		    double[] freqs = ef.getCharacterFrequencies();
+
+		    NicePrint nn = new NicePrint();
+		    for(int i=0; i<textFreq.length; i++) {
+			textFreq[i].setText(nn.rdbl(freqs[i],3));
+		    }
+		    
+		    if(!rw.isAligned) {
+			String text = new String("\n   Sequences are not aligned. Estimating \n"+
+						 "  gap frequency does not make much sense!\n");
+			OpenDialog od = new OpenDialog(SetParameters.this);
+			od.showDialog("Warning!", text);
+		    }
+		}
+
+	    } else if(actionCommand.equals("defaultfreq")) {
+
+		for(int i=0; i<textFreq.length; i++) {
+		    textFreq[i].setText(""+pa.sm.charFreqsDefault[i]);
+		}
+		
+	    } else if(actionCommand.equals("setfreq")) {
+
+		NicePrint nn = new NicePrint();
+		if(textFreq[textFreq.length-1].isEditable()) {
+		    double[] setFreqs = new double[textFreq.length];
+		    double sum = 0d;
+		    for(int i=0; i<textFreq.length; i++) {
+			setFreqs[i] = new Double(textFreq[i].getText()).doubleValue();
+			sum += setFreqs[i];
+			System.out.println("read: "+setFreqs[i]+", sum: "+sum);
+		    }
+		    for(int i=0; i<textFreq.length; i++) {
+			textFreq[i].setText(nn.rdbl(setFreqs[i]/sum,3));	
+			System.out.println("write: "+setFreqs[i]/sum);
+		    }
+		} else {
+		    double sum = 0d;
+		    for(int i=0; i<textFreq.length-1; i++) {
+			sum += new Double(textFreq[i].getText()).doubleValue();
+		    }
+		    if(sum < 1d) {
+			textFreq[textFreq.length-1].setText(nn.rdbl(1d-sum,3));
+		    }  
+		}
+
+	    } else if(actionCommand.equals("lock")) {
+
+		if(textFreq[textFreq.length-1].isEditable()) {
+		    textFreq[textFreq.length-1].setEditable(false);
+		} else {
+		    textFreq[textFreq.length-1].setEditable(true);
+		}
+
+	    } else if(actionCommand.equals("log")) {
+
+		OpenFileChooser opf = 
+		    new OpenFileChooser(SetParameters.this,"Start log",false);
+		String filepath = opf.openFile();
+	 	if(!filepath.equals("")) {
+		    pa.log.close();
+		    pa.startNewLog(filepath);
+		    UserSettings user = new UserSettings(); 
+		    String[] userdata = user.readSettings();
+		    ProAlign.folderPath = new File(filepath).getParent();
+		    userdata[0] = new File(filepath).getParent();
+                    user.writeSettings(userdata);
+		}
+		
+	    } else if(actionCommand.equals("hmmE")) {
+
+		textEpsil.setText(""+pa.sm.epsilonDefault);
+
+	    } else if(actionCommand.equals("hmmD")) {
+
+		textDelta.setText(""+pa.sm.deltaDefault);
+
+	    } else if(actionCommand.equals("hmmEst")) {
+
+		ProAlign.estimateDelta = true;
+		ProAlign.estimateEpsilon = true;
+		ProAlign.estimateGapFreq = false;
+		ProAlign.estimateGapProb = false;
+
+		ParameterEstimates pe = new ParameterEstimates(rw,SetParameters.this);
+
+	    } else if(actionCommand.equals("gapF")) {
+
+		textGapF.setText(""+pa.sm.gapFreqDefault);
+
+	    } else if(actionCommand.equals("gapP")) {
+
+		textGapP.setText(""+pa.sm.gapProbDefault);
+
+	    } else if(actionCommand.equals("defwidth")) {
+
+		textWidth.setText(""+pa.defBandWidth);
+
+	    } else if(actionCommand.equals("offset")) {
+		
+		textOffset.setText(""+pa.defOffset);
+		textOffset.setEditable(true);
+		boxTrail.setSelected(true);
+
+	    } else if(actionCommand.equals("clustalw")) {
+
+		OpenFileChooser opf = 
+		    new OpenFileChooser(SetParameters.this,"Select",false);
+		String filepath = opf.openFile();
+	 	if(!filepath.equals("")) {
+		    pa.clustalwPath = filepath;
+		    textClustalw.setText(filepath);
+		    UserSettings user = new UserSettings(); 
+		    String[] userdata = user.readSettings();
+		    userdata[1] = filepath;
+                    user.writeSettings(userdata);
+
+		}
+
+	    } else if(actionCommand.equals("temp")) {
+
+		OpenFolderChooser opf = 
+		    new OpenFolderChooser(SetParameters.this,"Select");
+		String filepath = opf.openFile();
+	 	if(!filepath.equals("")) {
+		    pa.tempFolder = filepath;
+		    textTemp.setText(filepath);
+		    UserSettings user = new UserSettings(); 
+		    String[] userdata = user.readSettings();
+		    userdata[2] = filepath;
+                    user.writeSettings(userdata);
+		}
+		
+	    } else if(actionCommand.equals("cancel")) {
+
+		dispatchEvent(new WindowEvent(SetParameters.this,
+					      WindowEvent.WINDOW_CLOSING));
+
+	    } else if(actionCommand.equals("ok")) {
+
+		while(true) {
+		    try{
+
+			if(ProAlign.isDna) {
+			    // check that character freqs are reasonable and update them.
+			    double[] setFreqs = new double[textFreq.length];
+			    double sum = 0d;
+			    for(int i=1; i<textFreq.length; i++) {
+				setFreqs[i] = new Double(textFreq[i].getText()).doubleValue();
+				sum += setFreqs[i];
+			    }
+			    if(sum < 1d) {
+				setFreqs[0] = 1d-sum;
+			    } 
+			    pa.sm.charFreqs = setFreqs;
+			    // or check that gap freq/prob are reasonable
+			} else {
+			    double gapF = new Double(textGapF.getText()).doubleValue();
+			    double gapP = new Double(textGapP.getText()).doubleValue();
+			    if(gapF>0d && gapF<0.5d && gapP>0d && gapP<0.5d) {
+				pa.sm.gapFreq = gapF;
+				pa.sm.gapProb = gapP;
+			    } else {
+				String text = new String("\n  Illegal value for gap frequency/"+
+							 "probability!\n     Value not changed.\n");
+				OpenDialog od = new OpenDialog(SetParameters.this);
+				od.showDialog("Warning!", text); 
+			    }   
+			    
+			}
+			
+			double delta = new Double(textDelta.getText()).doubleValue();
+			if(delta>0d && delta<0.5d) {
+			    pa.sm.delta = delta;
+			} else {
+			    String text = new String("\n  Illegal value for delta!\n"+
+						     "     Value not changed.\n");
+			    OpenDialog od = new OpenDialog(SetParameters.this);
+			    od.showDialog("Warning!", text); 
+			}
+			
+			double epsilon = new Double(textEpsil.getText()).doubleValue();
+			if(epsilon>0d && epsilon<1.0d) {
+			    pa.sm.epsilon = epsilon;
+			} else {
+			    String text = new String("\n  Illegal value for epsilon!\n"+
+						     "     Value not changed.\n");
+			    OpenDialog od = new OpenDialog(SetParameters.this);
+			    od.showDialog("Warning!", text); 
+			}
+			
+			int bandWidth = new Integer(textWidth.getText()).intValue();
+			if(bandWidth>10) {
+			    pa.bandWidth = bandWidth;
+			} else {
+			    String text = new String("\n  Band width too low!\n"+
+						     "  Value not changed.\n");
+			    OpenDialog od = new OpenDialog(SetParameters.this);
+			    od.showDialog("Warning!", text); 
+			}
+			if(bandWidth>200) {
+			    String text = new String("\n  Too great band width may run\n"+
+						     "   your system out of memory!\n");
+			    OpenDialog od = new OpenDialog(SetParameters.this);
+			    od.showDialog("Warning!", text); 
+			}
+			int gapOpen = (int)(new Float(textPwOpen.getText()).floatValue()*100f);
+			if(gapOpen>=0 && gapOpen<5100) {
+			    if(ProAlign.isDna) {
+				pa.pwDnaOpen = gapOpen;
+			    } else {
+				pa.pwProtOpen = gapOpen;
+			    }
+			} else {
+			    String text = new String("\n  Illegal value for gap opening!\n"+
+						     "     Value not changed.\n");
+			    OpenDialog od = new OpenDialog(SetParameters.this);
+			    od.showDialog("Warning!", text); 
+			}
+
+			int gapExt = (int)(new Float(textPwExt.getText()).floatValue()*100f);
+			if(gapExt>=0 && gapExt<2100) {
+			    if(ProAlign.isDna) {
+				pa.pwDnaExt = gapExt;
+			    } else {
+				pa.pwProtExt = gapExt;
+			    }
+			} else {
+			    String text = new String("\n  Illegal value for gap extension!\n"+
+						     "     Value not changed.\n");
+			    OpenDialog od = new OpenDialog(SetParameters.this);
+			    od.showDialog("Warning!", text); 
+			}
+			
+			int offset = new Integer(textOffset.getText()).intValue();
+			if(offset>0 && offset<pa.bandWidth/2) {
+			    pa.offset = offset;
+			} else {
+			    String text = new String("\n  Offset should be less than\n"+
+						     "  a half of the band width!\n"+
+						     "  Value not changed.\n");
+			    OpenDialog od = new OpenDialog(SetParameters.this);
+			    od.showDialog("Warning!", text); 
+			}
+
+			double scale = new Double(textScale.getText()).doubleValue();
+			if(scale>0d && scale<2.0d) {
+			    pa.distScale = scale;
+			} else {
+			    String text = new String("\n  Distance scale should be \n"+
+						     "  between 0.0 and 2.0!\n"+
+						     "  Value not changed.\n");
+			    OpenDialog od = new OpenDialog(SetParameters.this);
+			    od.showDialog("Warning!", text); 
+			}
+
+			if(!ProAlign.isDna) {
+			    pa.pwProtMatrix = textPwMat.getText().trim();
+			}
+
+			if(ProAlign.DEBUG) {
+			    ProAlign.log("SetParameters");
+			    ProAlign.log(" trackBest="+ProAlign.trackBest);
+			    ProAlign.log(" bandWidth="+ProAlign.bandWidth);
+			    ProAlign.log(" clustalwPath="+ProAlign.clustalwPath);
+			    ProAlign.log(" tempFolder="+ProAlign.tempFolder);
+			    ProAlign.log(" offset="+ProAlign.offset);
+			    ProAlign.log(" distScale="+ProAlign.distScale);
+			    ProAlign.log(" removeTrailing="+ProAlign.removeTrailing);
+			    ProAlign.log(" penalizeTerminal="+ProAlign.penalizeTerminal);
+			    if(ProAlign.isDna) {
+				ProAlign.log(" pwGapOpen="+pa.pwDnaOpen);
+				ProAlign.log(" pwGapExt="+pa.pwDnaExt);
+			    } else {
+				ProAlign.log(" pwGapOpen="+pa.pwProtOpen);
+				ProAlign.log(" pwGapExt="+pa.pwProtExt);
+				ProAlign.log(" pwGapMatrix="+pa.pwProtMatrix);
+			    }
+			    ProAlign.log.flush();
+			}
+
+		    } catch(NumberFormatException nfe) {
+			String text = new String("\n   Illegal value!\n");
+			OpenDialog od = new OpenDialog(SetParameters.this);
+			od.showDialog("Error!", text); 
+			break;
+		    }
+		    
+		    dispatchEvent(new WindowEvent(SetParameters.this,
+						  WindowEvent.WINDOW_CLOSING));
+		    break;
+		}
+	    }
+	}
+    }
+
+     class RadioListener implements ActionListener {
+        public void actionPerformed(ActionEvent e) {
+	    String actionCommand = e.getActionCommand();
+
+	    if(actionCommand.equals("sample")) {
+
+		ProAlign.trackBest = false;
+
+	    } else if(actionCommand.equals("best")) {
+
+		ProAlign.trackBest = true;
+	    }
+	}
+    }
+    
+    class ComboBoxListener implements ItemListener {
+	public void itemStateChanged(ItemEvent e) {
+	    if(!ProAlign.isDna) {
+		String item = (String) e.getItem();
+		textPwMat.setText("  "+item);
+	    }
+	}
+    }
+
+    class CheckBoxListener implements ActionListener {
+        public void actionPerformed(ActionEvent e) {
+	    String actionCommand = e.getActionCommand();
+	    if(actionCommand.equals("log")) {
+		if(boxLog.isSelected()) {
+		    ProAlign.DEBUG = true;		
+		} else {
+		    ProAlign.DEBUG = false;
+		}
+	    } else if(actionCommand.equals("trailing")) {
+		if(boxTrail.isSelected()) {
+		    ProAlign.removeTrailing = true;
+		    textOffset.setEditable(true);
+		} else {
+		    ProAlign.removeTrailing = false;
+		    textOffset.setEditable(false);
+		}
+	    } else if(actionCommand.equals("multiple")) {
+		if(boxMultiple.isSelected()) {
+		    ProAlign.correctMultiple = true;		
+		} else {
+		    ProAlign.correctMultiple = false;
+		}
+	    } else if(actionCommand.equals("terminal")) {
+		if(boxPenalize.isSelected()) {
+		    ProAlign.penalizeTerminal = true;		
+		} else {
+		    ProAlign.penalizeTerminal = false;
+		}
+	    }
+	}
+    }
+}
diff --git a/SubstitutionModel.java b/SubstitutionModel.java
new file mode 100644
index 0000000..f124e5e
--- /dev/null
+++ b/SubstitutionModel.java
@@ -0,0 +1,511 @@
+ /**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+public class SubstitutionModel {
+  
+    double delta, epsilon;
+    double deltaDefault, epsilonDefault;
+
+    double[] charFreqs;
+    double[] charFreqsDefault;
+    double[][] substProb1, substProb2;
+
+    String alphabet;
+    String equateAlphabet;
+
+    double[] aaFreqs;
+    double[][] aaPam;
+    double[][] gapPam;
+
+    double gapFreq;
+    double gapProb;
+    double gapFreqDefault, gapProbDefault;
+
+    String modelName;
+
+    /**
+     * Holds a handle to ProAlign-class. 
+     */
+    public SubstitutionModel(ProAlign pa) {  
+
+	delta = pa.modelDelta;     // prob to move to INSERT(X/Y) 
+	epsilon = pa.modelEpsilon; // prob to stay in INSERT(X/Y) 
+
+	gapFreq = pa.gapFreq;
+	gapProb = pa.gapProb;
+
+	// save defaults
+	deltaDefault = delta; 
+	epsilonDefault = epsilon;
+	gapFreqDefault = gapFreq;
+	gapProbDefault = gapProb;
+	
+	ProAlign.log("SubstitutionModel");
+    }
+
+    /**
+     * Simple DNA model. Currently the alphabet is "ACGT-", all the background 
+     * frequencies are 0.2, and substitution probabilities are equal.  
+     */
+    void jcDnaModel() { 
+
+	modelName = "jcDnaModel";
+
+	alphabet = "ACGT-";
+	equateAlphabet = "ACGTRYMKSWHBVDNU";
+
+	charFreqs = new double[5];
+
+	charFreqs[0] = 0.2d;
+	charFreqs[1] = 0.2d;
+	charFreqs[2] = 0.2d;
+	charFreqs[3] = 0.2d;
+	charFreqs[4] = 0.2d;
+
+	printDebug();
+
+	// save defaults
+	charFreqsDefault = charFreqs;
+
+    }
+
+
+    /**
+     * Simple protein model based on WAG.  
+     */
+
+    void wagProteinModel() {
+
+	modelName = "wagProteinModel";
+
+	alphabet = "ARNDCQEGHILKMFPSTWYV-";
+	equateAlphabet = "ARNDCQEGHILKMFPSTWYVX";
+
+	double[] tmp = {0.086628,0.043972,0.039089,0.057045,0.019308,
+			0.036728,0.058059,0.083252,0.024431,0.048466,
+			0.086209,0.062029,0.019503,0.038432,0.045763,
+			0.069518,0.061013,0.014386,0.035274,0.070896};
+	aaFreqs = tmp;
+
+	double[][] tmp2 = {{0.989896,0.000294,0.000272,0.000394,0.000548,
+			   0.000485,0.000844,0.000756,0.000169,0.000103,
+			   0.000212,0.000483,0.000477,0.000112,0.000767,
+			   0.001798,0.001131,6.03e-05,0.000128,0.00107},
+			  {0.000294,0.989794,0.000339,7.86e-05,0.000282,
+			   0.001619,0.000234,0.000312,0.00114,9.97e-05,
+			   0.000265,0.002854,0.000364,5.48e-05,0.000362,
+			   0.000653,0.000296,0.000621,0.000203,0.000134},
+			  {0.000272,0.000339,0.986156,0.002896,0.000141,
+			   0.000823,0.000505,0.0006,0.00211,0.000296,
+			   7.01e-05,0.001606,0.000106,5.13e-05,0.000104,
+			   0.00212,0.001083,3.84e-05,0.000579,0.000105},
+			  {0.000394,7.86e-05,0.002896,0.990311,1.62e-05,
+			   0.000329,0.003293,0.000462,0.000496,2.1e-05,
+			   4.52e-05,0.000256,5.53e-05,2.49e-05,0.000226,
+			   0.000572,0.0002,6.92e-05,0.000174,8.12e-05},
+			  {0.000548,0.000282,0.000141,1.62e-05,0.995607,
+			   5.27e-05,1.14e-05,0.000164,0.000133,9.07e-05,
+			   0.000205,3.95e-05,0.000208,0.000212,5.83e-05,
+			   0.000751,0.000274,0.000382,0.00029,0.000534},
+			  {0.000485,0.001619,0.000823,0.000329,5.27e-05,
+			   0.985928,0.002917,0.000176,0.00229,6.08e-05,
+			   0.000464,0.002077,0.000824,5.33e-05,0.000498,
+			   0.000549,0.000458,0.000115,0.000121,0.000161},
+			  {0.000844,0.000234,0.000505,0.003293,1.14e-05,
+			   0.002917,0.988168,0.000303,0.000304,6.79e-05,
+			   8.23e-05,0.001378,0.000168,4.33e-05,0.000364,
+			   0.000376,0.000439,8.35e-05,0.000105,0.000314},
+			  {0.000756,0.000312,0.0006,0.000462,0.000164,
+			   0.000176,0.000303,0.995427,0.000133,1.62e-05,
+			   3.27e-05,0.000199,9.29e-05,2.66e-05,0.00013,
+			   0.000716,0.00012,0.00018,5.53e-05,9.99e-05},
+			  {0.000169,0.00114,0.00211,0.000496,0.000133,
+			   0.00229,0.000304,0.000133,0.988544,7.37e-05,
+			   0.000266,0.000475,0.000216,0.000362,0.000371,
+			   0.000395,0.000252,0.00014,0.002066,6.31e-05},
+			  {0.000103,9.97e-05,0.000296,2.1e-05,9.07e-05,
+			   6.08e-05,6.79e-05,1.62e-05,7.37e-05,0.988961,
+			   0.001691,0.000173,0.002271,0.000565,5.33e-05,
+			   0.00017,0.000778,0.000113,0.000224,0.004171},
+			  {0.000212,0.000265,7.01e-05,4.52e-05,0.000205,
+			   0.000464,8.23e-05,3.27e-05,0.000266,0.001691,
+			   0.990704,0.000137,0.002589,0.001128,0.000222,
+			   0.000184,0.000174,0.000355,0.000213,0.00096},
+			  {0.000483,0.002854,0.001606,0.000256,3.95e-05,
+			   0.002077,0.001378,0.000199,0.000475,0.000173,
+			   0.000137,0.987915,0.000498,4.74e-05,0.000297,
+			   0.000516,0.00074,7.33e-05,7.11e-05,0.000163},
+			  {0.000477,0.000364,0.000106,5.53e-05,0.000208,
+			   0.000824,0.000168,9.29e-05,0.000216,0.002271,
+			   0.002589,0.000498,0.988732,0.000635,9.14e-05,
+			   0.000263,0.000809,0.000275,0.000228,0.001098},
+			  {0.000112,5.48e-05,5.13e-05,2.49e-05,0.000212,
+			   5.33e-05,4.33e-05,2.66e-05,0.000362,0.000565,
+			   0.001128,4.74e-05,0.000635,0.99161,8.61e-05,
+			   0.000291,9.17e-05,0.000816,0.003442,0.000347},
+			  {0.000767,0.000362,0.000104,0.000226,5.83e-05,
+			   0.000498,0.000364,0.00013,0.000371,5.33e-05,
+			   0.000222,0.000297,9.14e-05,8.61e-05,0.994727,
+			   0.00086,0.000424,7.43e-05,0.000115,0.000168},
+			  {0.001798,0.000653,0.00212,0.000572,0.000751,
+			   0.000549,0.000376,0.000716,0.000395,0.00017,
+			   0.000184,0.000516,0.000263,0.000291,0.00086,
+			   0.986629,0.002335,0.000279,0.00042,0.000124},
+			  {0.001131,0.000296,0.001083,0.0002,0.000274,
+			   0.000458,0.000439,0.00012,0.000252,0.000778,
+			   0.000174,0.00074,0.000809,9.17e-05,0.000424,
+			   0.002335,0.989442,5.91e-05,0.000155,0.00074},
+			  {6.03e-05,0.000621,3.84e-05,6.92e-05,0.000382,
+			   0.000115,8.35e-05,0.00018,0.00014,0.000113,
+			   0.000355,7.33e-05,0.000275,0.000816,7.43e-05,
+			   0.000279,5.91e-05,0.994745,0.001326,0.000195},
+			  {0.000128,0.000203,0.000579,0.000174,0.00029,
+			   0.000121,0.000105,5.53e-05,0.002066,0.000224,
+			   0.000213,7.11e-05,0.000228,0.003442,0.000115,
+			   0.00042,0.000155,0.001326,0.989916,0.000168},
+			  {0.00107,0.000134,0.000105,8.12e-05,0.000534,
+			   0.000161,0.000314,9.99e-05,6.31e-05,0.004171,
+			   0.00096,0.000163,0.001098,0.000347,0.000168,
+			   0.000124,0.00074,0.000195,0.000168,0.989304}};
+	aaPam = tmp2;
+	setGapPam();
+
+	printDebug();
+    }
+
+
+
+    void dayhoffProteinModel() {
+
+	modelName = "dayhoffProteinModel";
+
+	alphabet = "ARNDCQEGHILKMFPSTWYV-";
+	equateAlphabet = "ARNDCQEGHILKMFPSTWYVX";
+
+	double[] tmp = {0.087,0.041,0.04,0.047,0.033,
+			0.038,0.05,0.089,0.034,0.037,
+			0.085,0.08,0.015,0.04,0.051,
+			0.07,0.058,0.01,0.03,0.065};
+	aaFreqs = tmp;
+
+	double[][] tmp2 = {{0.988234,0.000137,0.000509,0.000612,0.000187,
+			    0.000457,0.000994,0.001216,0.000115,0.000333,
+			    0.000209,0.000133,0.000361,9.35e-05,0.001264,
+			    0.002061,0.001901,5.46e-06,0.000125,0.00105},
+			   {0.000137,0.991108,0.000169,8.44e-06,0.00012,
+			    0.001252,7.93e-06,4.46e-05,0.001202,0.000322,
+			    7.93e-05,0.002365,0.000449,6.94e-05,0.000521,
+			    0.000776,0.000137,0.001071,3.97e-05,0.000122},
+			   {0.000509,0.000169,0.983089,0.004602,1.05e-05,
+			    0.000535,0.000764,0.000713,0.002702,0.000396,
+			    0.000177,0.001636,2.71e-05,7.11e-05,0.000215,
+			    0.002509,0.001185,0.000122,0.000488,8.13e-05},
+			   {0.000612,8.44e-06,0.004602,0.985754,3.62e-06,
+			    0.000692,0.00575,0.00063,0.000438,0.000122,
+			    3.68e-06,0.000368,1.65e-05,1.84e-06,6.78e-05,
+			    0.000484,0.00034,4.59e-06,1.15e-05,9.05e-05},
+			   {0.000187,0.00012,1.05e-05,3.62e-06,0.99741,
+			    3.1e-06,2.09e-06,5.54e-05,0.000145,0.000226,
+			    5.54e-06,1.15e-06,2.01e-05,1.23e-05,9.66e-05,
+			    0.000824,8.5e-05,4.93e-05,0.000493,0.00025},
+			   {0.000457,0.001252,0.000535,0.000692,3.1e-06,
+			    0.98687,0.003612,0.000144,0.003058,9.25e-05,
+			    0.000378,0.000786,0.000571,2.56e-06,0.00078,
+			    0.000287,0.000273,1.36e-05,1.43e-05,0.000178},
+			   {0.000994,7.93e-06,0.000764,0.00575,2.09e-06,
+			    0.003612,0.986165,0.000409,0.00022,0.000308,
+			    5.74e-05,0.000423,0.000152,2.81e-06,0.000255,
+			    0.0004,0.000174,1.04e-05,0.000108,0.000185},
+			   {0.001216,4.46e-05,0.000713,0.00063,5.54e-05,
+			    0.000144,0.000409,0.994594,5.37e-05,3.28e-06,
+			    3.65e-05,0.000137,8.53e-05,7.77e-05,0.000176,
+			    0.001175,0.000158,1.83e-05,2.43e-06,0.000273},
+			   {0.000115,0.001202,0.002702,0.000438,0.000145,
+			    0.003058,0.00022,5.37e-05,0.989644,3.88e-05,
+			    0.000225,0.000138,1.78e-05,0.000239,0.000469,
+			    0.000178,0.000115,0.000143,0.000638,0.000221},
+			   {0.000333,0.000322,0.000396,0.000122,0.000226,
+			    9.25e-05,0.000308,3.28e-06,3.88e-05,0.988119,
+			    0.001308,0.000236,0.00167,0.000989,6.03e-05,
+			    0.000126,0.000977,1.4e-05,0.00019,0.004469},
+			   {0.000209,7.93e-05,0.000177,3.68e-06,5.54e-06,
+			    0.000378,5.74e-05,3.65e-05,0.000225,0.001308,
+			    0.992281,9.33e-05,0.00264,0.000799,0.000161,
+			    8.75e-05,0.000172,0.000249,0.000147,0.000892},
+			   {0.000133,0.002365,0.001636,0.000368,1.15e-06,
+			    0.000786,0.000423,0.000137,0.000138,0.000236,
+			    9.33e-05,0.990977,0.00122,9.45e-07,0.000171,
+			    0.000488,0.000701,4.86e-06,6.78e-05,5.32e-05},
+			   {0.000361,0.000449,2.71e-05,1.65e-05,2.01e-05,
+			    0.000571,0.000152,8.53e-05,1.78e-05,0.00167,
+			    0.00264,0.00122,0.990068,0.000461,8.5e-05,
+			    0.00031,0.000523,2.3e-05,1.63e-05,0.001284},
+			   {9.35e-05,6.94e-05,7.11e-05,1.84e-06,1.23e-05,
+			    2.56e-06,2.81e-06,7.77e-05,0.000239,0.000989,
+			    0.000799,9.45e-07,0.000461,0.99283,5.58e-05,
+			    0.000232,7.01e-05,0.000407,0.003523,6.25e-05},
+			   {0.001264,0.000521,0.000215,6.78e-05,9.66e-05,
+			    0.00078,0.000255,0.000176,0.000469,6.03e-05,
+			    0.000161,0.000171,8.5e-05,5.58e-05,0.993738,
+			    0.001225,0.000401,5.08e-06,6.21e-06,0.000245},
+			   {0.002061,0.000776,0.002509,0.000484,0.000824,
+			    0.000287,0.0004,0.001175,0.000178,0.000126,
+			    8.75e-05,0.000488,0.00031,0.000232,0.001225,
+			    0.985332,0.002788,0.000395,0.00017,0.000154},
+			   {0.001901,0.000137,0.001185,0.00034,8.5e-05,
+			    0.000273,0.000174,0.000158,0.000115,0.000977,
+			    0.000172,0.000701,0.000523,7.01e-05,0.000401,
+			    0.002788,0.988977,5.96e-06,0.000215,0.000802},
+			   {5.46e-06,0.001071,0.000122,4.59e-06,4.93e-05,
+			    1.36e-05,1.04e-05,1.83e-05,0.000143,1.4e-05,
+			    0.000249,4.86e-06,2.3e-05,0.000407,5.08e-06,
+			    0.000395,5.96e-06,0.997116,0.000325,1.79e-05},
+			   {0.000125,3.97e-05,0.000488,1.15e-05,0.000493,
+			    1.43e-05,0.000108,2.43e-06,0.000638,0.00019,
+			    0.000147,6.78e-05,1.63e-05,0.003523,6.21e-06,
+			    0.00017,0.000215,0.000325,0.993278,0.000142},
+			   {0.00105,0.000122,8.13e-05,9.05e-05,0.00025,
+			    0.000178,0.000185,0.000273,0.000221,0.004469,
+			    0.000892,5.32e-05,0.001284,6.25e-05,0.000245,
+			    0.000154,0.000802,1.79e-05,0.000142,0.989428}};
+	aaPam = tmp2;
+	setGapPam();
+
+	printDebug();
+    }
+
+
+
+    void jttProteinModel() {
+
+	modelName = "jttProteinModel";
+
+	alphabet = "ARNDCQEGHILKMFPSTWYV-";
+	equateAlphabet = "ARNDCQEGHILKMFPSTWYVX";
+
+	double[] tmp = {0.077,0.051,0.043,0.052,0.02,
+			0.041,0.062,0.074,0.023,0.052,
+			0.091,0.059,0.024,0.04,0.051,
+			0.069,0.059,0.014,0.032,0.066};
+	aaFreqs = tmp;
+
+	double[][] tmp2 = {{0.986454,0.000128,0.000552,0.000645,0.00036,
+			    0.000494,0.000935,0.001705,0.000199,0.000277,
+			    0.000228,0.000211,0.000263,0.000109,0.001474,
+			    0.002439,0.00218,4.55e-06,0.000136,0.001205},
+			   {0.000128,0.991919,0.00013,6.33e-06,0.000165,
+			    0.000963,5.31e-06,4.45e-05,0.001474,0.00019,
+			    6.15e-05,0.00266,0.000233,5.76e-05,0.000432,
+			    0.000653,0.000112,0.000635,3.09e-05,9.97e-05},
+			   {0.000552,0.00013,0.982571,0.003993,1.66e-05,
+			    0.000476,0.000592,0.000823,0.003835,0.00027,
+			    0.000159,0.00213,1.63e-05,6.83e-05,0.000207,
+			    0.002444,0.001118,8.36e-05,0.000439,7.69e-05},
+			   {0.000645,6.33e-06,0.003993,0.987626,5.57e-06,
+			    0.000598,0.004326,0.000707,0.000603,8.07e-05,
+			    3.2e-06,0.000465,9.64e-06,1.71e-06,6.33e-05,
+			    0.000458,0.000312,3.06e-06,1.01e-05,8.31e-05},
+			   {0.00036,0.000165,1.66e-05,5.57e-06,0.995644,
+			    4.89e-06,2.88e-06,0.000113,0.000365,0.000274,
+			    8.82e-06,2.64e-06,2.14e-05,2.1e-05,0.000165,
+			    0.001423,0.000142,5.99e-05,0.000787,0.00042},
+			   {0.000494,0.000963,0.000476,0.000598,4.89e-06,
+			    0.986951,0.002786,0.000166,0.004325,6.3e-05,
+			    0.000337,0.00102,0.000341,2.45e-06,0.000746,
+			    0.000279,0.000257,9.32e-06,1.28e-05,0.000167},
+			   {0.000935,5.31e-06,0.000592,0.004326,2.88e-06,
+			    0.002786,0.988953,0.00041,0.000271,0.000182,
+			    4.46e-05,0.000477,7.9e-05,2.34e-06,0.000212,
+			    0.000337,0.000142,6.16e-06,8.46e-05,0.000152},
+			   {0.001705,4.45e-05,0.000823,0.000707,0.000113,
+			    0.000166,0.00041,0.993253,9.86e-05,2.9e-06,
+			    4.24e-05,0.000231,6.61e-05,9.64e-05,0.000218,
+			    0.001479,0.000192,1.62e-05,2.82e-06,0.000333},
+			   {0.000199,0.001474,0.003835,0.000603,0.000365,
+			    0.004325,0.000271,9.86e-05,0.985237,4.21e-05,
+			    0.000321,0.000284,1.7e-05,0.000365,0.000715,
+			    0.000275,0.000173,0.000156,0.000912,0.000332},
+			   {0.000277,0.00019,0.00027,8.07e-05,0.000274,
+			    6.3e-05,0.000182,2.9e-06,4.21e-05,0.991779,
+			    0.000897,0.000235,0.000767,0.000726,4.43e-05,
+			    9.35e-05,0.000706,7.35e-06,0.000131,0.003232},
+			   {0.000228,6.15e-05,0.000159,3.2e-06,8.82e-06,
+			    0.000337,4.46e-05,4.24e-05,0.000321,0.000897,
+			    0.993861,0.000122,0.001591,0.00077,0.000155,
+			    8.55e-05,0.000163,0.000171,0.000133,0.000847},
+			   {0.000211,0.00266,0.00213,0.000465,2.64e-06,
+			    0.00102,0.000477,0.000231,0.000284,0.000235,
+			    0.000122,0.98903,0.001067,1.32e-06,0.00024,
+			    0.000693,0.000964,4.86e-06,8.89e-05,7.33e-05},
+			   {0.000263,0.000233,1.63e-05,9.64e-06,2.14e-05,
+			    0.000341,7.9e-05,6.61e-05,1.7e-05,0.000767,
+			    0.001591,0.001067,0.993806,0.000297,5.48e-05,
+			    0.000203,0.000332,1.06e-05,9.87e-06,0.000816},
+			   {0.000109,5.76e-05,6.83e-05,1.71e-06,2.1e-05,
+			    2.45e-06,2.34e-06,9.64e-05,0.000365,0.000726,
+			    0.00077,1.32e-06,0.000297,0.993338,5.76e-05,
+			    0.000243,7.11e-05,0.0003,0.003409,6.36e-05},
+			   {0.001474,0.000432,0.000207,6.33e-05,0.000165,
+			    0.000746,0.000212,0.000218,0.000715,4.43e-05,
+			    0.000155,0.00024,5.48e-05,5.76e-05,0.993266,
+			    0.001283,0.000407,3.75e-06,6.01e-06,0.000249},
+			   {0.002439,0.000653,0.002444,0.000458,0.001423,
+			    0.000279,0.000337,0.001479,0.000275,9.35e-05,
+			    8.55e-05,0.000693,0.000203,0.000243,0.001283,
+			    0.984122,0.002869,0.000295,0.000167,0.000158},
+			   {0.00218,0.000112,0.001118,0.000312,0.000142,
+			    0.000257,0.000142,0.000192,0.000173,0.000706,
+			    0.000163,0.000964,0.000332,7.11e-05,0.000407,
+			    0.002869,0.98885,4.32e-06,0.000204,0.000802},
+			   {4.55e-06,0.000635,8.36e-05,3.06e-06,5.99e-05,
+			    9.32e-06,6.16e-06,1.62e-05,0.000156,7.35e-06,
+			    0.000171,4.86e-06,1.06e-05,0.0003,3.75e-06,
+			    0.000295,4.32e-06,0.997991,0.000225,1.3e-05},
+			   {0.000136,3.09e-05,0.000439,1.01e-05,0.000787,
+			    1.28e-05,8.46e-05,2.82e-06,0.000912,0.000131,
+			    0.000133,8.89e-05,9.87e-06,0.003409,6.01e-06,
+			    0.000167,0.000204,0.000225,0.993076,0.000135},
+			   {0.001205,9.97e-05,7.69e-05,8.31e-05,0.00042,
+			    0.000167,0.000152,0.000333,0.000332,0.003232,
+			    0.000847,7.33e-05,0.000816,6.36e-05,0.000249,
+			    0.000158,0.000802,1.3e-05,0.000135,0.990742}}; 
+	aaPam = tmp2;
+	setGapPam();
+
+	printDebug();
+    }
+
+    void setBranchLength(double dist1, double dist2) {
+
+
+	dist1 = ProAlign.distScale*dist1;
+	dist2 = ProAlign.distScale*dist2;
+
+	if(ProAlign.isDna) {
+	    setDnaSubstTable(dist1,1);
+	    setDnaSubstTable(dist2,2);
+	} else {
+	    setProtSubstTable(dist1*100,1);
+	    setProtSubstTable(dist2*100,2);
+   	}
+    }
+
+    void setDnaSubstTable(double dist, int table) {
+
+	// probablility matrix to observe character pairs
+	double[][] sProb = new double[5][5];
+
+	for(int i=0; i<sProb.length; i++) {
+	    for(int j=0; j<sProb.length; j++) {
+		if(i==j) {
+		    sProb[i][j] = (double) 1d/5d+4d/5d*Math.exp(-5d/4d*dist);
+		} else {
+		    sProb[i][j] = (double) 1d/5d-1d/5d*Math.exp(-5d/4d*dist);
+		}
+	    }
+	}
+
+	if(table == 1) {
+	    substProb1 = sProb;
+	} else {
+	    substProb2 = sProb;
+	}
+    }
+
+    void setGapPam() {
+
+	// Compute new character freqs.
+	charFreqs = new double[21];
+	for(int i=0; i<20; i++) {
+	    charFreqs[i] = aaFreqs[i]*(1-gapFreq);
+	}
+	charFreqs[20] = gapFreq;
+
+	// Add gap to PAM1 matrix.
+	gapPam = new double[21][21];
+	for(int i=0; i<20; i++) {
+	    gapPam[i][20] = gapProb;
+	    gapPam[20][i] = gapProb;
+	}
+	gapPam[20][20] = 1-20*gapProb;
+
+	for(int i=0; i<20; i++) {
+	    for(int j=0; j<20; j++) {
+		gapPam[i][j] = aaPam[i][j]*(1-gapPam[20][i]);
+	    }
+	}
+
+	// Scale matrix to PAM1.
+	double sum = 0d;
+	for(int i=0; i<21; i++) {
+	    sum += charFreqs[i]*(1-gapPam[i][i]);	    
+	}	
+	for(int i=0; i<21; i++) {
+	    for(int j=0; j<21; j++) {
+		gapPam[i][j] = gapPam[i][j]*(0.01d/sum);
+	    }
+	}
+
+	for(int i=0; i<21; i++) {
+	    sum = 0d;
+	    for(int j=0; j<21; j++) {
+		if(j!=i) {
+		    sum += gapPam[i][j];
+		}
+	    }
+	    gapPam[i][i] = 1-sum;
+	}
+    }
+
+    void setProtSubstTable(double num, int table) {
+
+	double[][] sProb = gapPam;
+	double[][] newProb;
+
+	for(int n=1; n<num; n++) {
+	    newProb = new double[21][21];
+	    for(int i=0; i<21; i++) {
+		for(int j=0; j<21; j++) {	
+		    double sum = 0d;
+		    for(int k=0; k<21; k++) {
+			 sum += gapPam[k][j]*sProb[i][k];
+		    }
+		    newProb[i][j] = sum;
+		}
+	    }
+	    sProb = newProb;
+	}
+
+	if(table == 1) {
+	    substProb1 = sProb;
+	} else {
+	    substProb2 = sProb;
+	}
+    }
+
+    void printDebug() {
+
+	if(ProAlign.DEBUG) {
+	    ProAlign.log(" preset variables:");
+	    ProAlign.log("  delta:      "+delta);
+	    ProAlign.log("  epsilon:    "+epsilon);
+	    ProAlign.log(" model: "+modelName+"\n");
+	    ProAlign.log(" alphabet:  "+alphabet);
+	    ProAlign.log("  character freqs:");
+	    for(int i=0; i<charFreqs.length; i++) {
+		ProAlign.log.print(" "+charFreqs[i]);
+	    }
+	    ProAlign.log("");
+	}
+    }
+
+}
diff --git a/TraceBackPath.java b/TraceBackPath.java
new file mode 100644
index 0000000..2dbc4c8
--- /dev/null
+++ b/TraceBackPath.java
@@ -0,0 +1,518 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.util.Random;
+
+class TraceBackPath {
+
+    AlignmentLoop al;
+    AlignmentNode an;
+    Random r;
+
+    int warnLimit = 10;
+    int stopLimit = 3;
+    boolean isBandWarning = false;
+
+    int state = 0;
+    int aSize;
+    int[][] cellPath;
+    double[] postProb;
+
+    double siteSum;
+    double trailProb;
+    double logDelta;
+    double logEpsilon;
+    double logMinusEpsilon;
+
+    int sampleTimes = 0;
+    boolean isUnique = true;
+    boolean BEST;
+
+    TraceBackPath(AlignmentNode an, AlignmentLoop al) {
+	
+	this.an = an;
+	this.al = al;
+        r = new Random();
+
+	logDelta = Math.log(al.pa.sm.delta);
+	logEpsilon = Math.log(al.pa.sm.epsilon);
+	logMinusEpsilon = Math.log(1-al.pa.sm.epsilon);
+
+	ProAlign.log("TraceBackPath");
+    }
+
+    double[][] getNode(boolean best) throws TraceBackException {
+
+	this.BEST = best;
+	this.aSize = al.aSize;
+	char path = getEndPath();
+	int i=al.pathM.length-1; // back trace strting point (i,j)
+	int j=al.endPoint;
+
+	double[][] cProb = new double[i+j][aSize]; // char. probs. on the path
+	double[] pProb = new double[i+j];          // post. probs. on the path
+	int[][] cell = new int[i+j][2];
+	int ppc = 0;
+	
+	// go through the matrix;
+	// start from end, stop when at (1,1)
+	while(true) {
+
+	    if(j<stopLimit || j>ProAlign.bandWidth-stopLimit) {
+		throw new TraceBackException("traceback path comes too close to the band edge.");
+
+	    } else if(j<warnLimit || j>ProAlign.bandWidth-warnLimit) {
+		isBandWarning = true;
+	    }
+
+	    if(i==1 && j==al.MIDDLE) { // currently(?) stops at middle
+		break;
+	    } 
+	   
+	    if(state==0) { // -> match
+		
+		path = getPathM(i,j);
+
+		for(int l=0; l<aSize; l++) {
+		    cProb[ppc][l] = al.price[i][j][l];
+		}
+		cell[ppc][0] = i;
+		cell[ppc][1] = j-al.MIDDLE+i;
+		pProb[ppc++] = (al.fwdM[i][j]+al.bwdM[i][j])-al.fwdEnd;
+		i--;
+		
+		if(path=='X') {
+		    state = 1;
+		}else if(path=='Y') {
+		    state = 2;
+		}
+	    }
+
+	    
+	    else if(state==1) { // -> gap X
+
+		path = getPathX(i,j);
+
+		for(int l=0; l<aSize; l++) {
+		    cProb[ppc][l] = al.priceX[i][j][l];
+		}
+		cell[ppc][0] = i;
+		cell[ppc][1] = -1;
+		pProb[ppc++] = (al.fwdX[i][j]+al.bwdX[i][j])-al.fwdEnd;
+
+		i--;j++;
+
+		if(path=='m') {
+		    state = 0;
+		}
+	    }
+
+	    else if(state==2) { // -> gap Y
+		
+		path = getPathY(i,j);
+		for(int l=0; l<aSize; l++) {
+		    cProb[ppc][l] = al.priceY[i][j][l];
+		}
+
+		cell[ppc][0] = -1;
+		cell[ppc][1] = j-al.MIDDLE+i;
+		pProb[ppc++] = (al.fwdY[i][j]+al.bwdY[i][j])-al.fwdEnd;
+
+		j--;
+		if(path=='m') {
+		    state = 0;
+		}
+	    }
+	}
+
+	double[][] charProb;
+
+	if(an.hasTrailers) { // trailing sequence(s) cut out 
+
+	    int start = Math.max(an.start0,an.start1);
+	    int end = Math.max(an.end0,an.end1);
+	    int length = ppc+start+end;
+	    trailProb = 0d;
+	    charProb = new double[length][aSize];
+	    cellPath = new int[length][2];
+	    postProb = new double[length];
+	    if(start>0) {
+		if(an.start0>an.start1) {
+		    for(int k=0; k<start; k++) {
+			charProb[k] = trailerCharProb1(k);
+			cellPath[k][0] = k+2;
+			cellPath[k][1] = -1;
+			postProb[k] = 0d;
+			trailProb += Math.log(siteSum)+logEpsilon;
+		    }
+		    trailProb = trailProb+logMinusEpsilon-logEpsilon;
+
+		    for(int k=0; k<ppc; k++) {
+			double sum = 0d;
+			for(int l=0; l<aSize; l++) {
+			    sum += cProb[ppc-k-1][l];
+			}
+			for(int l=0; l<aSize; l++) {
+			    charProb[k+start][l] =  cProb[ppc-k-1][l]/sum;
+			}
+			if(cell[ppc-k-1][0]>0) {
+			    cellPath[k+start][0] = cell[ppc-k-1][0]+start;
+			} else {
+			    cellPath[k+start][0] = cell[ppc-k-1][0];
+			}
+			cellPath[k+start][1] = cell[ppc-k-1][1];
+			postProb[k+start] = pProb[ppc-k-1];
+		    }
+
+		} else {
+		    for(int k=0; k<start; k++) {
+			charProb[k] = trailerCharProb2(k);
+			cellPath[k][0] = -1;
+			cellPath[k][1] = k+2;
+			postProb[k] = 0d;
+			trailProb += Math.log(siteSum)+logEpsilon;
+		    }
+		    trailProb = trailProb+logMinusEpsilon-logEpsilon;
+
+		    for(int k=0; k<ppc; k++) {
+			double sum = 0d;
+			for(int l=0; l<aSize; l++) {
+			    sum += cProb[ppc-k-1][l];
+			}
+			for(int l=0; l<aSize; l++) {
+			    charProb[k+start][l] =  cProb[ppc-k-1][l]/sum;
+			}
+			cellPath[k+start][0] = cell[ppc-k-1][0];
+			if(cell[ppc-k-1][1]>0) {
+			    cellPath[k+start][1] = cell[ppc-k-1][1]+start;
+			} else {
+			    cellPath[k+start][1] = cell[ppc-k-1][1];
+			}
+			postProb[k+start] = pProb[ppc-k-1];
+		    }
+		}
+	    } else {
+		
+		for(int k=0; k<ppc; k++) {
+		    double sum = 0d;
+		    for(int l=0; l<aSize; l++) {
+			sum += cProb[ppc-k-1][l];
+		    }
+		    for(int l=0; l<aSize; l++) {
+			charProb[k][l] =  cProb[ppc-k-1][l]/sum;
+		}
+		    cellPath[k][0] = cell[ppc-k-1][0];
+		    cellPath[k][1] = cell[ppc-k-1][1];
+		    postProb[k] = pProb[ppc-k-1];
+		}	
+		
+	    }
+	    if(end>0) {
+		if(an.end0>an.end1) {
+
+		    for(int k=0; k<end; k++) {
+			int p = an.child[0].charProb.length-end+k;
+			charProb[ppc+start+k] = trailerCharProb1(p);
+			cellPath[ppc+start+k][0] = p+2;
+			cellPath[ppc+start+k][1] = -1;
+			postProb[ppc+start+k] = 0d;	
+			trailProb += Math.log(siteSum)+logEpsilon;
+		    }
+
+		    if(getEndPath()=='M') {
+			trailProb = trailProb+logDelta-logEpsilon;
+		    }
+			
+		} else {
+
+		    for(int k=0; k<end; k++) {
+			int p = an.child[1].charProb.length-end+k;
+			charProb[ppc+start+k] = trailerCharProb2(p);
+			cellPath[ppc+start+k][0] = -1;
+			cellPath[ppc+start+k][1] = p+2;
+			postProb[ppc+start+k] = 0d;
+			trailProb += Math.log(siteSum)+logEpsilon;
+		    }
+
+		    if(getEndPath()=='M') {
+			trailProb = trailProb+logDelta-logEpsilon;
+		    }
+		}
+	    }
+	    return charProb;
+
+	} else {   // no trailing sequences removed
+
+	    charProb = new double[ppc][aSize];
+
+	    cellPath = new int[ppc][2];
+	    postProb = new double[ppc];
+	
+	    for(int k=0; k<ppc; k++) {
+		double sum = 0d;
+		for(int l=0; l<aSize; l++) {
+		    sum += cProb[ppc-k-1][l];
+		}
+		for(int l=0; l<aSize; l++) {
+		    charProb[k][l] =  cProb[ppc-k-1][l]/sum;
+		}
+		cellPath[k][0] = cell[ppc-k-1][0];
+		cellPath[k][1] = cell[ppc-k-1][1];
+		postProb[k] = pProb[ppc-k-1];
+	    }
+	    return charProb;
+	}
+    }
+
+    // get path to go backwards;
+    // select either best (viterbi) or sample from probabilities
+
+    char getEndPath() {
+
+	char path = ' ';
+	double pm = al.pathEnd[0];
+	double px = al.pathEnd[1];
+	double py = (double) 1d - pm - px;
+
+	// select viterbi path; if equally good, take one randomly
+	if(BEST) { 
+	    if(pm > px && pm > py) {
+		path = 'M';
+	    } else if(pm > py && pm == px) {
+		if(r.nextBoolean()) {
+		    path = 'M';
+		} else {
+		    path = 'x';
+		    state = 1;
+		}
+		isUnique = false;
+		sampleTimes++;
+	    } else if(pm > px && pm == py) {
+		if(r.nextBoolean()) {
+		    path = 'M';
+		} else {
+		    path = 'y';
+		    state = 2;
+		}
+		isUnique = false;
+		sampleTimes++;
+	    } else if(px > py) {
+		path = 'x';
+		state = 1;
+	    } else if(px == py) {
+		if(r.nextBoolean()) {
+		    path = 'x';
+		    state = 1;
+		} else {
+		    path = 'y';
+		    state = 2;
+		}
+		isUnique = false;
+		sampleTimes++;
+	    } else {
+		path = 'y';
+		state = 2;
+	    }
+	    
+	// sample from paths according to their probabilities
+ 	} else {
+	    double rdn = r.nextDouble();
+	    if(rdn < pm) {
+		path = 'M';
+	    } else if(rdn < (pm+px)) {
+		path = 'X';
+		state = 1;
+	    } else {
+		path = 'Y';
+		state = 2;
+	    }
+	}
+
+	return path;
+    }
+
+    char getPathM(int i, int j) {
+
+	char path = ' ';
+	double pm = al.pathM[i][j][0];
+	double px = al.pathM[i][j][1];
+	double py = (double) 1d - pm - px;
+
+	// select viterbi path; if equally good, take one randomly
+	if(BEST) { 
+	    if(pm > px && pm > py) {
+		path = 'M';
+	    } else if(pm > py && pm == px) {
+		if(r.nextBoolean()) {
+		    path = 'M';
+		} else {
+		    path = 'X';
+		}
+		isUnique = false;
+		sampleTimes++;
+	    } else if(pm > px && pm == py) {
+		if(r.nextBoolean()) {
+		    path = 'M';
+		} else {
+		    path = 'Y';
+		}
+		isUnique = false;
+		sampleTimes++;
+	    } else if(px > py) {
+		path = 'X';
+	    } else if(px == py) {
+		if(r.nextBoolean()) {
+		    path = 'X';
+		} else {
+		    path = 'Y';
+		}
+		isUnique = false;
+		sampleTimes++;
+	    } else {
+		path = 'Y';
+	    }
+	    
+	// sample from paths according to their probabilities
+ 	} else {
+	    double rdn = r.nextDouble();
+	    if(rdn < pm) {
+		path = 'M';
+	    } else if(rdn < (pm+px)) {
+		path = 'X';
+	    } else {
+		path = 'Y';
+	    }
+	}
+
+	return path;
+    } 
+
+    char getPathX(int i, int j) {
+
+	char path = ' ';
+	double pm = al.pathX[i][j];
+	double px = (double) 1d - pm;
+
+	if(BEST) {
+	    if(pm > px) {
+		path = 'm'; 
+	    } else if( pm == px) {
+		if(r.nextBoolean()) {
+		    path = 'm';
+		} else {
+		    path = 'x';
+		}
+		isUnique = false;
+		sampleTimes++;
+	    } else {
+		path = 'x';
+	    }
+
+	} else {
+	    double rdn = r.nextDouble();
+	    if(rdn < pm) {
+		path = 'm';
+	    } else {
+		path = 'x';
+	    }
+	}
+
+	return path;
+    } 
+    
+    char getPathY(int i, int j) {
+	
+	char path = ' ';
+	double pm = al.pathY[i][j];
+	double py = (double) 1d - pm;
+
+	if(BEST) {
+	    if(pm > py) {
+		path = 'm'; 
+	    } else if( pm == py) {
+		if(r.nextBoolean()) {
+		    path = 'm';
+		} else {
+		    path = 'y';
+		}
+		isUnique = false;
+		sampleTimes++;
+	    } else {
+		path = 'y';
+	    }
+
+	} else {
+	    double rdn = r.nextDouble();
+	    if(rdn < pm) {
+		path = 'm';
+	    } else {
+		path = 'y';
+	    }
+	}
+
+	return path;
+    }
+
+    double[] trailerCharProb1(int i) {
+	
+	double[] cProb = new double[aSize];
+	siteSum = 0d;
+	for(int k=0; k<aSize; k++) {
+	    for(int l = 0; l<aSize; l++) {
+		cProb[k] += an.child[0].charProb[i][l]*al.pa.sm.substProb1[k][l];
+	    }
+	    cProb[k] = cProb[k]*al.pa.sm.substProb2[k][aSize-1]*al.pa.sm.charFreqs[k];
+	    siteSum += cProb[k];
+	}
+	for(int k=0; k<aSize; k++) {
+	    cProb[k] = cProb[k]/siteSum;
+	}
+	return cProb;
+    }
+
+    double[] trailerCharProb2(int i) {
+	
+	double[] cProb = new double[aSize];
+	siteSum = 0d;
+	for(int k=0; k<aSize; k++) {
+	    for(int l = 0; l<aSize; l++) {
+		cProb[k] += an.child[1].charProb[i][l]*al.pa.sm.substProb2[k][l];
+	    }
+	    cProb[k] = cProb[k]*al.pa.sm.substProb1[k][aSize-1]*al.pa.sm.charFreqs[k];
+	    siteSum += cProb[k];
+	}
+	for(int k=0; k<aSize; k++) {
+	    cProb[k] = cProb[k]/siteSum;
+	}
+	return cProb;
+    }
+}
+
+class TraceBackException extends Exception {
+
+    public TraceBackException() {}
+    public TraceBackException(String msg) {
+	super(msg);
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TransformLog.java b/TransformLog.java
new file mode 100644
index 0000000..cf7adad
--- /dev/null
+++ b/TransformLog.java
@@ -0,0 +1,36 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+class TransformLog {
+
+    TransformLog() { }
+    double sumLogs(double a, double b) {
+	if(Double.isNaN(a) && Double.isNaN(b)) {
+	    return Double.NEGATIVE_INFINITY;
+	}
+	else if(Double.isNaN(a)){
+	    return b;
+	} 
+	else if(Double.isNaN(b)){
+	    return a;
+	} 
+	if(b>a){
+	    double c = a;
+	    a = b; 
+	    b = c;
+	}
+	return (a+Math.log(1+Math.exp(b-a)));
+    }
+}
+
+
+
diff --git a/TreeNode.java b/TreeNode.java
new file mode 100644
index 0000000..52561b9
--- /dev/null
+++ b/TreeNode.java
@@ -0,0 +1,317 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+class TreeNode {
+
+    String tree;
+    String[] subTrees;
+    String[] revTrees;
+    static String mpTree;
+
+    TreeNode parent;
+    TreeNode child0;
+    TreeNode child1;
+
+    static float halfLength;
+    static float maxSpan;
+    static float minDiff;
+    float[] subDistances;
+    float maxLength = 0f;
+    float totalDist = 0f; // weight
+
+
+    boolean isLast = true;
+
+    TreeNode(String t) {
+
+	mpTree = new String();
+	minDiff = 100f;
+
+	ProAlign.log("TreeNode");
+
+	tree = t;
+	TreeReader tr = new TreeReader();
+
+	subTrees = tr.divideTree(tree);
+	subDistances = tr.distance;
+	subDistances[0] = Math.abs(subDistances[0]);
+	subDistances[1] = Math.abs(subDistances[1]);
+
+//	System.out.println(subTrees[0]+" "+subTrees[1]);
+
+	float tot = subDistances[0]+subDistances[1];
+	revTrees = new String[2];
+	revTrees[0] = subTrees[1]+":"+tot;
+	revTrees[1] = subTrees[0]+":"+tot;
+
+	child0 = new TreeNode(tr,subTrees[0],TreeNode.this,0);
+	child1 = new TreeNode(tr,subTrees[1],TreeNode.this,1);
+
+	totalDist = subDistances[0]+subDistances[1]+
+	    child0.totalDist+child1.totalDist;
+
+	float currPair = subDistances[0]+child0.maxLength+
+	    subDistances[1]+child1.maxLength;
+	if(currPair > TreeNode.maxSpan) {
+	    TreeNode.maxSpan = currPair;
+//	    System.out.println("ms0 "+child0.tree+":"+subDistances[0]+" | "+
+//			       child1.tree+":"+subDistances[1]+" "+currPair);
+	}
+    }
+
+    TreeNode(TreeReader tr,String t,TreeNode p,int branch) {
+
+	tree = t;
+	parent = p;
+
+	if(tree.indexOf(",")>0) {
+
+	    isLast = false;
+	    subTrees = tr.divideTree(tree);
+	    subDistances = tr.distance;
+	    subDistances[0] = Math.abs(subDistances[0]);
+	    subDistances[1] = Math.abs(subDistances[1]);
+	    
+	    revTrees = new String[2];
+	    revTrees[0] = "("+parent.revTrees[branch]+","+
+		subTrees[1]+":"+subDistances[1]+"):"+subDistances[0];
+	    revTrees[1] = "("+parent.revTrees[branch]+","+
+		subTrees[0]+":"+subDistances[0]+"):"+subDistances[1];
+
+	    child0 = new TreeNode(tr,subTrees[0],TreeNode.this,0);
+	    child1 = new TreeNode(tr,subTrees[1],TreeNode.this,1);
+
+	    totalDist = subDistances[0]+subDistances[1]+
+		child0.totalDist+child1.totalDist;
+
+	    float currPair = subDistances[0]+child0.maxLength+
+		subDistances[1]+child1.maxLength;
+	    if(currPair > TreeNode.maxSpan) {
+		TreeNode.maxSpan = currPair;
+//		System.out.println("ms1 "+child0.tree+":"+subDistances[0]+" | "+
+//				   child1.tree+":"+subDistances[1]+" "+currPair);
+	    }
+	    if(subDistances[0]+child0.maxLength > subDistances[1]+child1.maxLength) {
+		maxLength = subDistances[0]+child0.maxLength;
+	    } else {
+		maxLength = subDistances[1]+child1.maxLength;
+	    }
+//	    System.out.println("ml "+maxLength);
+	}
+    }
+
+    String findMiddlePoint() {
+
+	TreeNode.halfLength = TreeNode.maxSpan/2f;	
+
+	if(TreeNode.halfLength > child0.maxLength && 
+	   TreeNode.halfLength < child0.maxLength+subDistances[0]+subDistances[1]) {
+
+	    float b0 = TreeNode.halfLength-child0.maxLength;
+	    float b1 = subDistances[0]+subDistances[1]-b0;
+
+	    TreeNode.mpTree = "("+child0.tree+": "+b0+","+child1.tree+":"+b1+");";		    
+	}
+
+	child0.findMiddle(1);
+	child1.findMiddle(0);
+	ProAlign.log("mprooted: "+TreeNode.mpTree);
+	return TreeNode.mpTree;
+    }
+
+    void findMiddle(int branch) {
+	if(!isLast) {
+
+	    if(branch==0) {
+
+		if(TreeNode.halfLength > child0.maxLength && 
+		   TreeNode.halfLength < child0.maxLength+subDistances[0]) {
+
+		    float b0 = TreeNode.halfLength-child0.maxLength;
+		    float b1 = subDistances[0]-b0;
+			
+		    TreeNode.mpTree = "("+child0.tree+": "+b0+",("+parent.revTrees[1]+","+
+			subTrees[1]+":"+subDistances[1]+"):"+b1+");";		    
+			
+//		    System.out.println("nt "+branch+": "+child0.tree+" "+child0.maxLength+" "+
+//		     child1.tree+" "+child1.maxLength+" "+TreeNode.halfLength+" "+TreeNode.minDiff);
+		}
+
+		if(TreeNode.halfLength > child1.maxLength && 
+		   TreeNode.halfLength < child1.maxLength+subDistances[1]) {
+
+		    float b0 = TreeNode.halfLength-child1.maxLength;
+		    float b1 = subDistances[1]-b0;
+
+		    TreeNode.mpTree = "("+child1.tree+": "+b0+",("+parent.revTrees[1]+","+
+			subTrees[0]+":"+subDistances[0]+"):"+b1+");";
+
+//		    System.out.println("nt "+branch+": "+child0.tree+" "+child0.maxLength+" "+
+//		      child1.tree+" "+child1.maxLength+" "+TreeNode.halfLength+" "+TreeNode.minDiff);
+
+		}
+		child0.findMiddle(1);
+		child1.findMiddle(0);
+
+	    } else {
+
+		if(TreeNode.halfLength > child0.maxLength && 
+		   TreeNode.halfLength < child0.maxLength+subDistances[0]) {
+
+		    float b0 = TreeNode.halfLength-child0.maxLength;
+		    float b1 = subDistances[0]-b0;
+
+		    TreeNode.mpTree = "("+child0.tree+": "+b0+",("+parent.revTrees[0]+
+			","+subTrees[1]+":"+subDistances[1]+"):"+b1+");";
+
+//		    System.out.println("nt "+branch+": "+child0.tree+" "+child0.maxLength+" "+
+//		      child1.tree+" "+child1.maxLength+" "+TreeNode.halfLength+" "+TreeNode.minDiff);
+
+		}
+
+
+		if(TreeNode.halfLength > child1.maxLength && 
+		   TreeNode.halfLength < child1.maxLength+subDistances[1]) {
+
+		    float b0 = TreeNode.halfLength-child1.maxLength;
+		    float b1 = subDistances[1]-b0;
+
+		    TreeNode.mpTree = "("+child1.tree+": "+b0+",("+parent.revTrees[0]+
+			","+subTrees[0]+":"+subDistances[0]+"):"+b1+");";
+
+//		    System.out.println("nt "+branch+": "+child0.tree+" "+child0.maxLength+" "+
+//		      child1.tree+" "+child1.maxLength+" "+TreeNode.halfLength+" "+TreeNode.minDiff);
+
+		}
+		child0.findMiddle(1);
+		child1.findMiddle(0);
+	    }
+	}
+    }
+
+
+/*
+This was the first attempt for the tree rooting. Not used anymore.
+*/
+
+    String findMiddleWeightPoint() {
+	TreeNode.halfLength = totalDist/2f;
+	child0.findMiddle(1);
+	child1.findMiddle(0);
+	ProAlign.log("mprooted: "+TreeNode.mpTree);
+	return TreeNode.mpTree;
+    }
+
+    void findMiddleWeight(int branch) {
+	if(!isLast) {
+
+	    if(branch==0) {
+		if(Math.abs(TreeNode.halfLength-child0.totalDist)<TreeNode.minDiff) {
+		    
+		    TreeNode.minDiff = Math.abs(TreeNode.halfLength-child0.totalDist);
+		    float b0,b1;
+		    if(TreeNode.halfLength>child0.totalDist) {
+			b0 = TreeNode.halfLength-child0.totalDist;
+			b1 = subDistances[0]-b0;
+		    } else if(subDistances[0]>0.001f) {
+			b0 = 0.001f;
+			b1 = subDistances[0]-b0;
+		    } else {
+			b0 = 0f;
+			b1 = subDistances[0];
+		    }
+		    TreeNode.mpTree = "("+child0.tree+": "+b0+",("+parent.revTrees[1]+","+
+			subTrees[1]+":"+subDistances[1]+"):"+b1+");";		    
+		}
+
+		if(Math.abs(TreeNode.halfLength-child1.totalDist)<TreeNode.minDiff) {
+		    
+		    TreeNode.minDiff = Math.abs(TreeNode.halfLength-child1.totalDist);
+		    float b0,b1;
+		    if(TreeNode.halfLength>child1.totalDist) {
+			b0 = TreeNode.halfLength-child1.totalDist;
+			b1 = subDistances[1]-b0;
+		    } else if(subDistances[1]>0.001f) {
+			b0 = 0.001f;
+			b1 = subDistances[1]-b0;
+		    } else {
+			b0 = 0f;
+			b1 = subDistances[1];
+		    }
+		    TreeNode.mpTree = "("+child1.tree+": "+b0+",("+parent.revTrees[1]+","+
+			subTrees[0]+":"+subDistances[0]+"):"+b1+");";
+		}
+		child0.findMiddle(1);
+		child1.findMiddle(0);
+
+	    } else {
+		if(Math.abs(TreeNode.halfLength-child0.totalDist)<TreeNode.minDiff) {
+
+		    TreeNode.minDiff = Math.abs(TreeNode.halfLength-child0.totalDist);
+		    float b0,b1;
+		    if(TreeNode.halfLength>child0.totalDist) {
+			b0 = TreeNode.halfLength-child0.totalDist;
+			b1 = subDistances[0]-b0;
+		    } else if(subDistances[0]>0.001f) {
+			b0 = 0.001f;
+			b1 = subDistances[0]-b0;
+		    } else {
+			b0 = 0f;
+			b1 = subDistances[0];	
+		    }
+		    TreeNode.mpTree = "("+child0.tree+": "+b0+",("+parent.revTrees[0]+
+			","+subTrees[1]+":"+subDistances[1]+"):"+b1+");";
+		}
+
+		if(Math.abs(TreeNode.halfLength-child1.totalDist)<TreeNode.minDiff) {
+		    
+		    TreeNode.minDiff = Math.abs(TreeNode.halfLength-child1.totalDist);
+		    float b0,b1;
+		    if(TreeNode.halfLength>child1.totalDist) {
+			b0 = TreeNode.halfLength-child1.totalDist;
+			b1 = subDistances[1]-b0;
+		    } else if(subDistances[1]>0.001f) {
+			b0 = 0.001f;
+			b1 = subDistances[1]-b0;
+		    } else {
+			b0 = 0f;
+			b1 = subDistances[1];
+		    }
+		    TreeNode.mpTree = "("+child1.tree+": "+b0+",("+parent.revTrees[0]+
+			","+subTrees[0]+":"+subDistances[0]+"):"+b1+")";
+		}
+		child0.findMiddle(1);
+		child1.findMiddle(0);
+	    }
+	}
+    }
+
+    void printNames() {
+	if(isLast) {
+	    System.out.println(tree);
+	} else {
+	    child0.printNames();
+	    child1.printNames();
+	    System.out.println("node: "+totalDist);
+	}
+    }
+    /*   
+    public static void main(String[] args) {
+
+	TreeReader2 tr = new TreeReader2();
+	String tree = tr.readFile(args[0]);
+	TreeNode root = new TreeNode(tree);
+	TreeNode.halfLength = root.totalDist/2f;
+	System.out.println(root.findMiddlePoint());
+    }
+    //*/
+}
diff --git a/TreeReader.java b/TreeReader.java
new file mode 100644
index 0000000..3f0eb82
--- /dev/null
+++ b/TreeReader.java
@@ -0,0 +1,226 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.io.File; 
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+class TreeReader {
+
+    float[] distance;
+    boolean isUnRooted = false;
+
+    TreeReader(){ 
+	
+    }
+
+    // Read a tree from a file and remove branch lengths
+    //
+    String readFile(String filename) {
+
+	String tree = new String();
+	
+	ProAlign.log("TreeReader: "+filename);
+
+	try {
+	    InFile in = new InFile(filename);
+
+	    String row =  new String();
+	    while((row = in.readLine()) != null) {
+		tree += row;
+	    }
+	} catch (Exception e) {}
+
+	return tree;
+    }
+
+    // Take a tree and divide it into two subtrees.
+    // Notice that clustalw root is trifurcating
+    //
+    String[] divideTree(String tree) {
+
+//	System.out.println("tree "+tree+"\n");
+
+	String[] trees = new String[2];
+	distance = new float[2];
+	trees[0] = "";
+	
+	if(tree.endsWith(";")) {
+	    tree = tree.substring(0,tree.length()-1); // remove last ';'
+	}
+	tree = tree.substring(1,tree.length()-1);     // remove first & last '('
+
+
+	if(tree.charAt(0)!='(') { // only one taxon
+
+	    String tmp = tree.substring(0,tree.indexOf(","));
+	    trees[0] = tmp.substring(0,tmp.indexOf(":"));
+	    distance[0] = new Float(tmp.substring(tmp.indexOf(":")+1)).floatValue();
+
+	    boolean trifurc = false;
+	    int open = 0;
+	    for(int j = tree.indexOf(",")+1; j<tree.length(); j++) {
+		if(tree.charAt(j)=='(') { open++; }
+		else if(tree.charAt(j)==')') { open--; }
+		if(open==0 && tree.substring(j).indexOf(",")>0) {
+		    trifurc = true;
+		}
+	    }
+
+	    // correction for trifurcating root
+	    if(trifurc) {
+
+		isUnRooted = true;
+		trees[1] = "("+tree.substring(tree.indexOf(",")+1)+")";
+		distance[0] = distance[0]/2f;
+		distance[1] = distance[0];
+
+	    } else {
+
+		trees[1] = tree.substring(tree.indexOf(",")+1,tree.lastIndexOf(":"));
+		tmp = tree.substring(tree.lastIndexOf(":")+1);
+		distance[1] = new Float(tmp).floatValue();
+	    }
+			    
+//	    System.out.println("1o:  "+trees[0]+" "+distance[0]+"\n");
+//	    System.out.println("2o:  "+trees[1]+" "+distance[1]+"\n");
+
+	} else {
+
+	    int open = 0;
+
+	    for(int i=0; i<tree.length(); i++) {
+
+		// count parentheses that are "open"
+		if(tree.charAt(i)=='(') { open++; }
+		else if(tree.charAt(i)==')') { open--; }
+		trees[0] += ""+tree.charAt(i);
+
+		if(open<=0) {
+		    String tmp = tree.substring(i+2,tree.indexOf(",",i+2));
+		    distance[0] = new Float(tmp).floatValue();
+
+		    boolean trifurc = false;
+		    open = 0;
+		    for(int j = tree.indexOf(",",i+2)+1; j<tree.length(); j++) {
+			if(tree.charAt(j)=='(') { open++; }
+			else if(tree.charAt(j)==')') { open--; }
+			if(open==0 && tree.substring(j).indexOf(",")>0) {
+			    trifurc = true;
+			}
+		    }
+
+		    // correction for trifurcating root
+		    if(trifurc) {
+
+			isUnRooted = true;
+			trees[1] = "("+tree.substring(tree.indexOf(",",i+2)+1)+")";
+			distance[0] = distance[0]/2f;
+			distance[1] = distance[0];
+			
+//			System.out.println("1:  "+trees[0]+" "+distance[0]+"\n");
+//			System.out.println("2a: "+trees[1]+" "+distance[1]+"\n");
+			
+		    } else {
+
+			tmp = tree.substring(tree.indexOf(",",i+2)+1);
+			trees[1] = tmp.substring(0,tmp.lastIndexOf(":"));
+
+			tmp = tmp.substring(tmp.lastIndexOf(":")+1);
+			distance[1] = new Float(tmp).floatValue();
+
+//			System.out.println("1:  "+trees[0]+" "+distance[0]+"\n");
+//			System.out.println("2b: "+trees[1]+" "+distance[1]+"\n");
+
+		    }
+
+		    break; 
+		}
+	    }
+	    
+	}
+           
+	//System.out.println("0: "+trees[0]+" ["+distance[0]+"]");
+	//System.out.println("1: "+trees[1]+" ["+distance[1]+"]");
+
+	return trees;
+    }
+
+    String[] getAllNodes(String filepath) {
+	ArrayList nl = new ArrayList();
+	TreeReader tr = this;
+	String tree = this.readFile(filepath);
+//	if(ProAlign.DEBUG) {
+//	    ProAlign.log.println(" reading a list of all the nodes.");
+//	}
+
+	//System.out.println(clustalw);
+	String[] trees = tr.divideTree(tree);
+
+	tr.loopThroughNodes(trees[0],tr,nl);
+	tr.loopThroughNodes(trees[1],tr,nl);
+
+	String[] nodes = new String[nl.size()];
+	for(int i=0; i<nl.size(); i++) {
+	    nodes[i] = (String) nl.get(i); 
+	}
+
+	return nodes;
+    }
+
+    void loopThroughNodes(String tree, TreeReader tr, ArrayList nl) {
+	if(tree.indexOf(",")>0) {
+	    String[] trees = tr.divideTree(tree);
+	    tr.loopThroughNodes(trees[0],tr,nl);
+	    tr.loopThroughNodes(trees[1],tr,nl);
+	} else {
+	    nl.add(tree);
+	    //System.out.println(nl.size()+" "+tree);
+	}
+    }
+
+
+    // ---DEBUGGING ONLY -->
+    void loopTreeTest(String tree, TreeReader tr) {
+	if(tree.indexOf(",")>0) {
+	    String[] trees = tr.divideTree(tree);
+	    tr.loopTreeTest(trees[0],tr);
+	    tr.loopTreeTest(trees[1],tr);
+	} else {
+	    //System.out.println("l "+tree);
+	}
+    }
+
+    public static void main(String[] args) {
+
+	TreeReader tr = new TreeReader();
+	String tree = tr.readFile(args[0]);
+	String[] trees = tr.divideTree(tree);
+	tr.loopTreeTest(trees[0],tr);
+	tr.loopTreeTest(trees[1],tr);
+	System.out.println("OK");
+	String[] nodes = tr.getAllNodes(args[0]);
+	for(int i=0; i<nodes.length; i++) {
+	    System.out.println(nodes[i]); 
+	}
+    }
+    // <--DEBUGGING ONLY ---
+
+}
+
+
+
+
+
+
+
diff --git a/UserSettings.java b/UserSettings.java
new file mode 100644
index 0000000..5dd01f5
--- /dev/null
+++ b/UserSettings.java
@@ -0,0 +1,85 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+import java.io.File;
+import java.io.IOException;
+
+/**   
+ *  Reads and writes a file for user settings
+ */
+public class UserSettings {
+
+    String filepath;
+    File file;
+
+    UserSettings() {
+
+	ProAlign.log("UserSettings");
+
+	if(System.getProperty("os.name").startsWith("Windows")){
+	    filepath = "proalign.ini";
+	} else {
+	    filepath = ".proalignrc";
+	}
+	file = new File(filepath);
+    }
+
+    // Read existing settings
+    String[] readSettings() {
+	String[] userdata = new String[3];
+	String str = new String();
+	if(file.exists()) {
+	    try {
+		InFile dataIn = new InFile(filepath);
+		if((str = dataIn.readLine())!=null) {
+		    if(new File(str).isDirectory()){
+			userdata[0] = str; 
+		    }
+		}
+		if((str = dataIn.readLine())!=null) {
+		    if(new File(str).isFile()){
+			userdata[1] = str; 
+		    }
+		}
+		if((str = dataIn.readLine())!=null) {
+		    if(new File(str).isDirectory()){
+			userdata[2] = str; 
+		    }
+		}
+		dataIn.close();
+	    } catch (IOException ie) { }  
+	}
+	return userdata;
+    }
+    
+    // Write current settings
+    void writeSettings(String[] userdata) {
+	if(file.exists()) {
+	    try {
+		file.delete();
+	    } catch (Exception ie) { }  
+	}
+	try {  
+	    OutFile dataOut = new OutFile(filepath);
+	    dataOut.println(userdata[0]); // ProAlign data
+	    dataOut.println(userdata[1]); // ProAlign ClustalW
+	    dataOut.println(userdata[2]); // temp
+	    dataOut.close();
+	} catch (IOException ie) { }  
+    }
+}
+
+
+
+
+
+
diff --git a/Viterbi.java b/Viterbi.java
new file mode 100644
index 0000000..aa8cf4a
--- /dev/null
+++ b/Viterbi.java
@@ -0,0 +1,354 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+class Viterbi {
+
+    AlignmentLoop al;
+
+    double[] charFreqs;
+    String alphabet;
+
+    double delta;
+    double epsilon;
+    double logDelta;
+    double logEpsilon;
+    double logMinus2Delta;
+    double logMinusEpsilon;
+    
+    double constantM;
+    double constantX;
+    double constantY;
+
+    TransformLog tl;
+
+    int aSize;
+
+    Viterbi(AlignmentLoop al) {
+	this.al = al;	
+
+	this.charFreqs = al.pa.sm.charFreqs;
+	this.alphabet = al.pa.sm.alphabet;
+
+	delta = al.pa.sm.delta;
+	epsilon = al.pa.sm.epsilon;
+	logDelta = Math.log(al.pa.sm.delta);
+	logEpsilon = Math.log(al.pa.sm.epsilon);
+	logMinus2Delta = Math.log(1-2*al.pa.sm.delta);
+	logMinusEpsilon = Math.log(1-al.pa.sm.epsilon);
+
+	aSize = al.seq1[0].length; // aSize = alphabet size; 
+
+	tl = new TransformLog();
+    }
+
+    // get viterbi value for M-state; set probability of each path to this cell;
+    // pathM[i][j][0] = P(match to M)
+    // pathM[i][j][1] = P(match to X)
+    // 1 - pathM[i][j][0] - pathM[i][j][1] = P(match to Y)
+
+    double getViterbiM(int i, int j) {
+
+	constantM = Math.log(sumOverChars(i,j));
+
+	double max = maxOfThree(logMinus2Delta+al.vitM[i-1][j],
+				logMinusEpsilon+al.vitX[i-1][j],
+				logMinusEpsilon+al.vitY[i-1][j]);
+
+	double pm = logMinus2Delta+al.vitM[i-1][j];
+	double px = logMinusEpsilon+al.vitX[i-1][j];
+	double py = logMinusEpsilon+al.vitY[i-1][j];
+	double sum = tl.sumLogs(pm,tl.sumLogs(px,py));
+
+	al.pathM[i][j][0] = (double) Math.exp(pm-sum);
+	al.pathM[i][j][1] = (double) Math.exp(px-sum);
+
+	return (constantM+max);
+    }
+
+    // get viterbi value for X-state; set probability of each path to this cell;
+    // pathX[i][j] = P(match to M)
+    // 1 - pathX[i][j] = P(match to X)
+
+    double getViterbiX(int i, int j) {
+	    
+	if(j==al.BWIDTH-1) {  // lower border of band, an X-gap impossible
+	    return Double.NEGATIVE_INFINITY;
+	} 
+
+	constantX = Math.log(sumOverCharsX(i,j));
+
+	double max = maxOfTwoX(logDelta+al.vitM[i-1][j+1],
+			       logEpsilon+al.vitX[i-1][j+1]);
+	
+	double pm = logDelta+al.vitM[i-1][j+1];
+	double px = logEpsilon+al.vitX[i-1][j+1];
+	double sum = tl.sumLogs(px,pm);
+	
+	al.pathX[i][j] = (double) Math.exp(pm-sum);
+    
+	return constantX+max;
+    }
+
+    // get viterbi value for X-state; set probability of each path to this cell;
+    // pathY[i][j] = P(match to M)
+    // 1 - pathY[i][j] = P(match to Y)
+
+    double getViterbiY(int i, int j) {
+
+	if(j==0) {   // upper border of band, a Y-gap impossible
+	    return Double.NEGATIVE_INFINITY;
+	}
+
+	constantY = Math.log(sumOverCharsY(i,j));
+
+	double max = maxOfTwoY(logDelta+al.vitM[i][j-1],
+			       logEpsilon+al.vitY[i][j-1]);
+
+	double pm = logDelta+al.vitM[i][j-1];
+	double py = logEpsilon+al.vitY[i][j-1];
+	double sum = tl.sumLogs(py,pm);
+
+	al.pathY[i][j] = (double) Math.exp(pm-sum);
+
+	return constantY+max;
+    }
+
+    // set vierbi end, set forward end,
+    // set probability of each path ending.
+    // pathEnd[0] = P(match to M), pathEnd[1] = P(gap to x)
+    // 1 - pathEnd[0] - pathEnd[1] = P(gap to y)
+    // endPoint is not necessarily MIDDLE!
+    
+    double getViterbiEnd(int endPoint) {
+	
+	if(al.an.hasTrailers) {
+
+	    double pm = al.vitM[al.vitM.length-1][endPoint];
+	    double px = al.vitX[al.vitX.length-1][endPoint];
+	    double py = al.vitY[al.vitY.length-1][endPoint];
+	    double sum;
+
+	    if(al.an.end0>al.an.end1) {
+		sum =  tl.sumLogs(pm,px);
+		al.pathEnd[0] = (double) Math.exp(pm-sum);
+		al.pathEnd[1] = (double) Math.exp(px-sum);
+
+		al.fwdEnd = (double) tl.sumLogs(al.fwdM[al.fwdM.length-1][endPoint],
+						al.fwdX[al.fwdX.length-1][endPoint]);
+	    } else {
+		sum =  tl.sumLogs(pm,py);
+		al.pathEnd[0] = (double) Math.exp(pm-sum);
+		al.pathEnd[1] = Double.NEGATIVE_INFINITY;
+
+		al.fwdEnd = (double) tl.sumLogs(al.fwdM[al.fwdM.length-1][endPoint],
+						al.fwdY[al.fwdY.length-1][endPoint]);
+	    }
+
+	} else {
+
+	    double pm = al.vitM[al.vitM.length-1][endPoint];
+	    double px = al.vitX[al.vitX.length-1][endPoint];
+	    double py = al.vitY[al.vitY.length-1][endPoint];
+	    double sum =  tl.sumLogs(pm,tl.sumLogs(px,py));
+	    
+	    al.pathEnd[0] = (double) Math.exp(pm-sum);
+	    al.pathEnd[1] = (double) Math.exp(px-sum);
+		    
+	    al.fwdEnd = (double) tl.sumLogs(al.fwdM[al.fwdM.length-1][endPoint],
+					    tl.sumLogs(al.fwdX[al.fwdX.length-1][endPoint],
+						       al.fwdY[al.fwdY.length-1][endPoint]));
+	}    
+
+	return (double) maxOfThree(al.vitM[al.vitM.length-1][endPoint],
+				   al.vitX[al.vitX.length-1][endPoint],
+				   al.vitY[al.vitY.length-1][endPoint]);
+    }
+
+    double getForwardM(int i, int j) {
+
+	return constantM+tl.sumLogs(logMinus2Delta+al.fwdM[i-1][j],
+				    logMinusEpsilon+tl.sumLogs(al.fwdX[i-1][j],al.fwdY[i-1][j]));
+    }
+
+    double getForwardX(int i, int j) {
+
+	if(j==al.BWIDTH-1) { // lower border of band, an X-gap impossible
+	    return Double.NEGATIVE_INFINITY;
+	}
+
+	if(j-al.MIDDLE+i==1) { // only X-gaps possible, constant's not set
+	    constantX = Math.log(sumOverCharsX(i,j));
+	}
+			   
+	return constantX+tl.sumLogs(logDelta+al.fwdM[i-1][j+1],logEpsilon+al.fwdX[i-1][j+1]);
+    }
+
+    double getForwardY(int i, int j) {
+
+	if(j==0) { // upper border of band, a Y-gap impossible
+	    return Double.NEGATIVE_INFINITY;
+	}
+
+	if(i==1) { // only Y-gaps possible, constant's not set
+	    constantY = Math.log(sumOverCharsY(i,j));
+	}
+
+	return constantY+tl.sumLogs(logDelta+al.fwdM[i][j-1],logEpsilon+al.fwdY[i][j-1]);
+    }
+
+    double getBackwardM(int i, int j) {
+
+	if(i==al.vitM.length-1) { // left border of matrix
+
+	    constantM = Double.NEGATIVE_INFINITY;
+	    constantX = Double.NEGATIVE_INFINITY;
+	    constantY = Math.log(sumOverCharsY(i,j+1));
+
+	}else if(j-al.MIDDLE+i==al.seq2.length+1) { // lower border of real matrix
+
+	    constantM = Double.NEGATIVE_INFINITY;	    
+	    constantX = Math.log(sumOverCharsX(i+1,j-1));
+	    constantY = Double.NEGATIVE_INFINITY;
+
+	}else if(j==0) {
+
+	    constantM = Math.log(sumOverChars(i+1,j));
+	    constantX = Double.NEGATIVE_INFINITY;
+	    constantY = Math.log(sumOverCharsY(i,j+1));
+
+	}else if(j==al.BWIDTH-1) {
+
+	    constantM = Math.log(sumOverChars(i+1,j));
+	    constantX = Math.log(sumOverCharsX(i+1,j-1));
+	    constantY = Double.NEGATIVE_INFINITY;
+
+	} else {
+
+	    constantM = Math.log(sumOverChars(i+1,j));
+	    constantX = Math.log(sumOverCharsX(i+1,j-1));
+	    constantY = Math.log(sumOverCharsY(i,j+1));
+
+	}
+
+	if(j==0) { // upper border of the band; an X-gap impossible
+	    return tl.sumLogs(logMinus2Delta+constantM+al.bwdM[i+1][j],
+			      logDelta+constantY+al.bwdY[i][j+1]);
+	}
+
+	if(j==al.BWIDTH-1) { // lower border of the band; a Y-gap impossible 
+	    return tl.sumLogs(logMinus2Delta+constantM+al.bwdM[i+1][j],
+			      logDelta+constantX+al.bwdX[i+1][j-1]);
+	}
+ 
+	return  tl.sumLogs(logMinus2Delta+constantM+al.bwdM[i+1][j],
+			   logDelta+tl.sumLogs(constantX+al.bwdX[i+1][j-1],
+					       constantY+al.bwdY[i][j+1]));
+			       
+    }
+
+    double getBackwardX(int i, int j) {
+
+	if(j==0) { // upper border of the band; an X-gap impossible
+	    return logMinusEpsilon+constantM+al.bwdM[i+1][j];   
+	}
+	
+	return tl.sumLogs(logMinusEpsilon+constantM+al.bwdM[i+1][j],
+			   logEpsilon+constantX+al.bwdX[i+1][j-1]);
+    }
+
+    double getBackwardY(int i, int j) {
+
+	if(j==al.BWIDTH-1) { // lower border of the band; a Y-gap impossible
+	    return logMinusEpsilon+constantM+al.bwdM[i+1][j];  
+	}
+
+	return  tl.sumLogs(logMinusEpsilon+constantM+al.bwdM[i+1][j],
+			   logEpsilon+constantY+al.bwdY[i][j+1]);
+    }
+
+ 
+   // sum over all possible characters [k] at the node [i][j]
+
+    double sumOverChars(int i, int j){
+
+	double sum = 0d;
+	for(int k = 0; k<aSize; k++) {
+	    sum += al.price[i][j][k];
+	}
+	return sum;
+    }
+
+    double sumOverCharsX(int i, int j){
+
+	double sum = 0d;
+	for(int k = 0; k<aSize; k++) {
+	    sum += al.priceX[i][j][k];
+	}
+	return sum;
+    }
+
+    double sumOverCharsY(int i, int j){
+    
+	double sum = 0d;
+	for(int k = 0; k<aSize; k++) {
+	    sum += al.priceY[i][j][k];
+	}
+	return sum;
+    }
+
+
+    double maxOfTwoX(double m, double x) {
+	double max = 0d;
+        if(m > x) {
+	    max = m;
+	} else {
+	    max = x;
+	}
+	
+	return max;
+    }
+
+    double maxOfTwoY(double m, double y) {
+	double max = 0d;
+	if(m > y) {
+	    max = m;
+	} else {
+	    max = y;
+	}
+	return max;
+    }
+
+    double maxOfThree(double m, double x, double y) {
+
+	double max = 0d;
+	if(m > x && m > y) {
+	    max = m;
+	} else if(x > y){
+	    max = x;
+	} else {
+	    max = y;
+	}
+
+	return max;
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WinClustalw.java b/WinClustalw.java
new file mode 100644
index 0000000..e6dafae
--- /dev/null
+++ b/WinClustalw.java
@@ -0,0 +1,55 @@
+/**
+ * Title:        ProAlign<p>
+ * Description:  <p>
+ * Copyright:    Copyright (c) Ari Loytynoja<p>
+ * License:      GNU GENERAL PUBLIC LICENSE<p>
+ * @see          http://www.gnu.org/copyleft/gpl.html
+ * Company:      ULB<p>
+ * @author Ari Loytynoja
+ * @version 1.0
+ */
+package proalign;
+
+public class WinClustalw extends Thread {
+
+  String command;
+  int num;
+  boolean running = true;
+
+  WinClustalw(int num, String command) { 
+
+      this.num = num;
+      this.command = command;
+      start();
+  }
+
+  public void run(){
+    runClustalw(num,command);
+    running = false;
+  }
+    
+  /**
+   * Run native ClustalW
+   */
+  public native void runClustalw(int num, String cmnd);
+  static {
+      Runtime.getRuntime().load(ProAlign.clustalwPath);
+  }
+  
+  /**
+   * Get output and write it in the log.
+   */
+  private void writeout(String txt) {
+      //System.out.println(txt);
+      ProAlign.log(txt);
+    try {
+	sleep(20);
+    } catch(InterruptedException e) {}
+  }
+}
+
+
+
+
+
+
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index e7e0715..0000000
--- a/debian/changelog
+++ /dev/null
@@ -1,12 +0,0 @@
-proalign (0.603-2) unstable; urgency=medium
-
-  * Moved debian/upstream to debian/upstream/metadata
-  * cme fix dpkg-control
-
- -- Andreas Tille <tille at debian.org>  Wed, 13 Jan 2016 12:38:14 +0100
-
-proalign (0.603-1) unstable; urgency=low
-
-  * Initial release (Closes: #378290)
-
- -- Thorsten Alteholz <debian at alteholz.de>  Tue, 01 May 2013 18:00:00 +0200
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index ec63514..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-9
diff --git a/debian/control b/debian/control
deleted file mode 100644
index 764994e..0000000
--- a/debian/control
+++ /dev/null
@@ -1,31 +0,0 @@
-Source: proalign
-Maintainer: Debian Med Packaging Team <debian-med-packaging at lists.alioth.debian.org>
-Uploaders: Andreas Tille <tille at debian.org>,
-           Thorsten Alteholz <debian at alteholz.de>
-Section: science
-Priority: optional
-Build-Depends: debhelper (>= 9),
-               default-jdk
-Standards-Version: 3.9.6
-Vcs-Browser: http://anonscm.debian.org/viewvc/debian-med/trunk/packages/proalign/trunk/
-Vcs-Svn: svn://anonscm.debian.org/debian-med/trunk/packages/proalign/trunk/
-Homepage: http://ueg.ulb.ac.be/ProAlign/
-
-Package: proalign
-Architecture: any
-Depends: ${shlibs:Depends},
-         ${misc:Depends},
-         default-jre
-Description: Probabilistic multiple alignment program
- ProAlign performs probabilistic sequence alignments using hidden Markov
- models (HMM). It includes a graphical interface (GUI) allowing to (i)
- perform alignments of nucleotide or amino-acid sequences, (ii) view the
- quality of solutions, (iii) filter the unreliable alignment regions and
- (iv) export alignments to other software.
- .
- ProAlign uses a progressive method, such that multiple alignment is
- created stepwise by performing pairwise alignments in the nodes of a
- guide tree. Sequences are described with vectors of character
- probabilities, and each pairwise alignment reconstructs the ancestral
- (parent) sequence by computing the probabilities of different
- characters according to an evolutionary model.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 5f26d57..0000000
--- a/debian/copyright
+++ /dev/null
@@ -1,35 +0,0 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Name: ProAlign
-Upstream-Contact: Ari Löytynoja & Michel C. Milinkovitch
-Source: http://ueg.ulb.ac.be/ProAlign/
-
-Files: *
-Copyright: © 2002-2003 Ari Löytynoja and Michel C. Milinkovitch.
-License: GPL-2+
-
-Files: debian/*
-Copyright: © 2012 Andreas Tille <tille at debian.org>
-License: GPL-2+
-
-License: GPL-2+
- This program is free software; you can redistribute it
- and/or modify it under the terms of the GNU General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later
- version.
- .
- This program is distributed in the hope that it will be
- useful, but WITHOUT ANY WARRANTY; without even the implied
- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- PURPOSE.  See the GNU General Public License for more
- details.
- .
- You should have received a copy of the GNU General Public
- License along with this package; if not, write to the Free
- Software Foundation, Inc., 51 Franklin St, Fifth Floor,
- Boston, MA  02110-1301 USA
- .
- On Debian systems, the full text of the GNU General Public
- License version 2 can be found in the file
- `/usr/share/common-licenses/GPL-2'.
-
diff --git a/debian/manifest.txt b/debian/manifest.txt
deleted file mode 100644
index 3497d16..0000000
--- a/debian/manifest.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-version: 1.0
-Main-Class: proalign.ProAlign
-
diff --git a/debian/proalign.1 b/debian/proalign.1
deleted file mode 100644
index 03304e2..0000000
--- a/debian/proalign.1
+++ /dev/null
@@ -1,79 +0,0 @@
-.TH PROALIGN "1" "May 2013" "ProAlign" "User Commands"
-.SH NAME
-proalign \- a probabilistic multiple alignment program
-.SH SYNOPSIS
-.B proalign [OPTIONS]
-.SH DESCRIPTION
-Only limited usage of the program is possible from command line. 
-Better use version with GUI (start without OPTIONS).
-.SH OPTIONS
-.TP
-\fB\-nogui\fR 
-force command line
-.TP
-\fB-seqfile=<sequence file>\fR
-.TP
-\fB-treefile=<tree file>\fR
-.TP
-\fB-newtree \fR
-compute a new guide tree
-.TP
-\fB-sample \fR
-sample traceback path; if not given, Viterbi is chosen
-.TP
-\fB-delta=<HMM delta> or \-delta=estimate \fR
-if not given, default is used
-.TP
-\fB-epsilon=<HMM epsilon> or \-epsilon=estimate \fR
-if not given, default is used
-.TP
-\fB-gapfreq=<gap frequency> \fR
-if not given, default is used
-.TP
-\fB-gapprob=<gap substitution probability> \fR
-if not given, default is used
-.TP
-\fB-bwidth=<search band width> \fR
-if not given, default is used
-.TP
-\fB-distscale=<distance scale factor> \fR
-for branch lengths
-.TP
-\fB-nocorrection \fR
-no correction for pairwise distances on guide tree computation
-.TP
-\fB-notrailing \fR
-no trailing sequence corrcection
-.TP
-\fB-trailing=<trailing sequence correction length> \fR
-for missing ends
-.TP
-\fB-penalize=true, or =false \fR
-penalize end gaps on pairwise alignments for guide tree
-.TP
-\fB-writemean \fR
-write mean posterior probability of sites
-.TP
-\fB-writeall \fR
-write posterior probability of each node
-.TP
-\fB-writeroot \fR
-write root node character probabilities
-.TP
-\fB-wag \fR
-use WAG probability table
-.TP
-\fB-dayhoff \fR
-use Dayhoff probability table
-.TP
-\fB-jtt \fR
-use JTT probability table
-.TP
-\fB-outfile=<alignment file>\fR
-.TP
-\fB\-outformat=pir, \-outformat=msf, \-outformat=phylip, or \-outformat=nexus \fR
-output format
-.TP
-\fB-quiet \fR
-no log
-
diff --git a/debian/proalign.install b/debian/proalign.install
deleted file mode 100644
index 32cb5c6..0000000
--- a/debian/proalign.install
+++ /dev/null
@@ -1,3 +0,0 @@
-debian/scripts/proalign usr/bin
-build/ProAlign.jar usr/share/proalign
-
diff --git a/debian/proalign.manpages b/debian/proalign.manpages
deleted file mode 100644
index 2958a32..0000000
--- a/debian/proalign.manpages
+++ /dev/null
@@ -1 +0,0 @@
-debian/proalign.1
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 71d903a..0000000
--- a/debian/rules
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/make -f
-
-# DH_VERBOSE := 1
-
-%:
-	dh $@
-
-override_dh_auto_build:
-	mkdir -p build
-	javac -d ./build *.java
-	(cd build; jar cmvf ../debian/manifest.txt ProAlign.jar *)
-	#(cd build; jar cmvf ../debian/manifest.txt ProAlign.jar proalign/ProAlign.class proalign/OutFile.class)
-
-get-orig-source:
-	mkdir -p ../tarballs
-	uscan --verbose --force-download --destdir=../tarballs
-
diff --git a/debian/scripts/proalign b/debian/scripts/proalign
deleted file mode 100644
index 3024912..0000000
--- a/debian/scripts/proalign
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-
-PARA=$@
-
-if [ -z $JAVA_HOME ]; then
-        java=java
-else
-        java=$JAVA_HOME/bin/java
-fi
-
-if [ -x /usr/share/proalign ]; then
-        if [ -r /usr/share/proalign/ProAlign.jar  ]; then 
-                #cd /usr/share/proalign && $java -jar ProAlign.jar
-                $java -jar /usr/share/proalign/ProAlign.jar $PARA
-        else 
-                echo "Could not find /usr/share/proalign/ProAlign.jar"
-        fi
-else 
-        echo "Could not find /usr/share/proalign directory."
-fi
-
diff --git a/debian/source/format b/debian/source/format
deleted file mode 100644
index 163aaf8..0000000
--- a/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/debian/upstream/metadata b/debian/upstream/metadata
deleted file mode 100644
index 9321bcd..0000000
--- a/debian/upstream/metadata
+++ /dev/null
@@ -1,12 +0,0 @@
-Reference:
-  Author: Ari Löytynoja and Michel C Milinkovitch
-  Title: A hidden Markov model for progressive multiple alignment
-  Journal: Bioinformatics
-  Year: 2003
-  Volume: 19
-  Number: 12
-  Pages: 1505-13
-  DOI: 10.1093/bioinformatics/btg193
-  PMID: 12912831
-  URL: http://bioinformatics.oxfordjournals.org/content/19/12/1505
-  eprint: http://bioinformatics.oxfordjournals.org/content/19/12/1505.full.pdf+html
diff --git a/debian/watch b/debian/watch
deleted file mode 100644
index a743fa0..0000000
--- a/debian/watch
+++ /dev/null
@@ -1,4 +0,0 @@
-version=3
-
-opts=uversionmangle=s/0/0\./ \
-  http://ueg.ulb.ac.be/ProAlign/download/src/proalign_(.+)\.tgz

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/proalign.git



More information about the debian-med-commit mailing list